aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorlameguy64 <lameguy64@gmail.com>2019-05-23 22:24:56 +0800
committerlameguy64 <lameguy64@gmail.com>2019-05-23 22:24:56 +0800
commit3ffebff2aad2ca438cf76db51fb3459c5639cd67 (patch)
treea234fc6158d3c09904c4c1fb2feee09afb479a4c /examples
parente70cd149f41ea71f9ca9ee86c03d1e59005dad2a (diff)
downloadpsn00bsdk-3ffebff2aad2ca438cf76db51fb3459c5639cd67.tar.gz
Added BIOS Controller, BIOS CD, 2 new examples and custom exit handler in the works
Diffstat (limited to 'examples')
-rw-r--r--examples/cartrom/bios.inc67
-rw-r--r--examples/cartrom/cop0.inc11
-rw-r--r--examples/cartrom/makefile14
-rw-r--r--examples/cartrom/parse.inc113
-rw-r--r--examples/cartrom/readme.txt74
-rw-r--r--examples/cartrom/rom.ld11
-rw-r--r--examples/cartrom/rom.s399
-rw-r--r--examples/rgb24/bunpattern.timbin0 -> 921620 bytes
-rw-r--r--examples/rgb24/main.c52
-rw-r--r--examples/rgb24/makefile41
-rw-r--r--examples/rgb24/tim.s7
11 files changed, 789 insertions, 0 deletions
diff --git a/examples/cartrom/bios.inc b/examples/cartrom/bios.inc
new file mode 100644
index 0000000..1c0201d
--- /dev/null
+++ b/examples/cartrom/bios.inc
@@ -0,0 +1,67 @@
+printf:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x3F
+
+atoi:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x10
+
+toupper:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x25
+
+open:
+ addiu $t2, $0, 0xB0
+ jr $t2
+ addiu $t1, $0, 0x32
+
+read:
+ addiu $t2, $0, 0xB0
+ jr $t2
+ addiu $t1, $0, 0x34
+
+close:
+ addiu $t2, $0, 0xB0
+ jr $t2
+ addiu $t1, $0, 0x36
+
+_96_init:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x71
+
+LoadExe:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x42
+
+DoExec:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x43
+
+SetConf:
+ addiu $t2, $0, 0xA0
+ jr $t2
+ addiu $t1, $0, 0x9C
+
+SetDefaultExitFromException:
+ addiu $t2, $0, 0xB0
+ jr $t2
+ addiu $t1, $0, 0x18
+
+EnterCriticalSection:
+ addiu $a0, $0, 1
+ syscall 0
+ jr $ra
+ nop
+
+ExitCriticalSection:
+ addiu $a0, $0, 2
+ syscall 0
+ jr $ra
+ nop
+ \ No newline at end of file
diff --git a/examples/cartrom/cop0.inc b/examples/cartrom/cop0.inc
new file mode 100644
index 0000000..620fa44
--- /dev/null
+++ b/examples/cartrom/cop0.inc
@@ -0,0 +1,11 @@
+.set BPC, $3
+.set BDA, $5
+.set JUMPDEST, $6
+.set DCIC, $7
+.set BADVADDR, $8
+.set BDAM, $9
+.set BPCM, $11
+.set SR, $12
+.set CAUSE, $13
+.set EPC, $14
+.set PRID, $15
diff --git a/examples/cartrom/makefile b/examples/cartrom/makefile
new file mode 100644
index 0000000..2434685
--- /dev/null
+++ b/examples/cartrom/makefile
@@ -0,0 +1,14 @@
+PREFIX = mipsel-unknown-elf-
+
+CC = $(PREFIX)gcc
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: rom.o
+ $(LD) --oformat binary -T rom.ld -o cartrom.rom rom.o
+
+%.o: %.s
+ $(AS) -msoft-float --warn $< -o $@
+
+clean:
+ rm -f rom.o cartrom.rom \ No newline at end of file
diff --git a/examples/cartrom/parse.inc b/examples/cartrom/parse.inc
new file mode 100644
index 0000000..b1190f7
--- /dev/null
+++ b/examples/cartrom/parse.inc
@@ -0,0 +1,113 @@
+# Skips spaces and equal signs (used by CNF parser)
+skipspace:
+ # a0 - Input string
+ # Return: v0 - Address to first non-space character.
+ lbu $v1, 0($a0)
+ nop
+ beq $v1, ' ', .skip
+ nop
+ beq $v1, '=', .skip
+ nop
+ jr $ra
+ move $v0, $a0
+.skip:
+ b skipspace
+ addiu $a0, 1
+
+
+# Copies a string until a CR/LF or space is encountered
+getline:
+ # a0 - Output address
+ # a1 - String to copy from
+ lbu $v0, 0($a1)
+ addiu $a1, 1
+ beqz $v0, .end
+ nop
+ beq $v0, 0x0D, .end
+ nop
+ beq $v0, 0x0A, .end
+ nop
+ beq $v0, ' ', .end
+ nop
+ sb $v0, 0($a0)
+ b getline
+ addiu $a0, 1
+.end:
+ jr $ra
+ sb $0, 0($a0)
+
+
+# strcasestr implementation
+strcasestr:
+ # a0 - String to search
+ # a1 - String to find
+ addiu $sp, -24
+ sw $ra, 0($sp)
+ sw $s0, 4($sp)
+ sw $s1, 8($sp)
+ sw $a1, 16($sp)
+
+.scan_start:
+
+ sw $a0, 12($sp)
+
+.comp_loop:
+
+ lbu $s0, 0($a0) # Load character from A and B
+ lbu $s1, 0($a1)
+
+ beqz $s0, .end_strcasestr
+ nop
+ beqz $s1, .found
+ nop
+
+ sw $a0, 20($sp) # Save a0 parameter
+
+ jal toupper # tolower character A
+ move $a0, $s0
+ move $s0, $v0
+
+ jal toupper # tolower character B
+ move $a0, $s1
+ move $s1, $v0
+
+ lw $a0, 20($sp) # Restore a0 parameter
+
+ addiu $a1, 1
+ addiu $a0, 1
+
+ beq $s0, $s1, .comp_loop # If value matches continue compare
+ nop
+
+.end_strcasestr:
+
+ lw $a0, 12($sp) # Rescan from next character of string A
+ lw $a1, 16($sp)
+ addiu $a0, 1
+
+ lbu $v0, 0($a0)
+ nop
+ beqz $v0, .not_found # If terminator is reached, string is not found
+ nop
+
+ b .scan_start
+ nop
+
+.not_found:
+
+ b .quit
+ move $v0, $0
+
+.found:
+
+ lw $v0, 12($sp) # Return address of string match
+
+.quit:
+
+ lw $ra, 0($sp)
+ lw $s0, 4($sp)
+ lw $s1, 8($sp)
+ jr $ra
+ addiu $sp, 24
+
+
diff --git a/examples/cartrom/readme.txt b/examples/cartrom/readme.txt
new file mode 100644
index 0000000..8d11a7f
--- /dev/null
+++ b/examples/cartrom/readme.txt
@@ -0,0 +1,74 @@
+LibPSn00b Example Programs
+Part of the PSn00bSDK Poject
+
+TurboBoot Example by Lameguy64
+
+
+Explanation:
+
+This example demonstrates how to create a ROM program to be used on a cheat
+cartridge such as an Action Replay or Xplorer using GNU assembler. The program
+stored in ROM is executed pretty much instantly on power-on.
+
+The PS1 BIOS can execute code from a ROM chip connected to the expansion port
+(or Parallel I/O port) such as a cheat cartridge provided the valid headers
+are present. However, the boot vectors are somewhat limited as it only
+provides a preboot vector and a postboot vector.
+
+The preboot vector is executed at the earliest point of the BIOS start-up
+sequence which means the RAM is almost completely blank and you can't execute
+PS-EXEs from this point whearas the postboot vector is only executed between
+the PS logo and game execution. To get around this limitation, the preboot
+vector is used to execute code that places a breakpoint to a desired point
+in memory and returns execution to the BIOS. This will effectively intercept
+execution once it reaches the breakpoint allowing to run code from ROM with
+an initialized kernel allowing for bootstrapping PS-EXEs from ROM, CD or
+from a comms interface such as serial.
+
+The Xplorer and later Pro Action Replay cheat devices use this so called
+'midboot' trick in their firmwares.
+
+In this TurboBoot example, the ROM program attempts to boot a PS-EXE from
+CD using BIOS CD-ROM and file functions. It will also parse through the
+SYSTEM.CNF to retrieve the file name of executable as well as some boot
+parameters such as stack addres and number of TCBs and EvCBs. The ROM
+program can fallback to attempting to load a PS-EXE named PSX.EXE if the
+SYSTEM.CNF file does not exist like in the official BIOS.
+
+This example can also be used as a turbo boot utility as it boots straight
+to the game, skipping the start-up animation altogether. It cannot be used
+as a way to bypass the authentication check of an unmodified console however
+(though it would help with bypassing the license data check on older Japanese
+consoles) as the CD-ROM would only read data discs correctly IF the elusive
+wobble groove containing the SCE string of the disc is present or when a
+modchip is installed. It is possible to circumvent this by issuing special
+'CD Unlock' commands (see nocash's psx-spx document) to the CD-ROM controller
+which would effectively turn this into a chipless modchip solution that plugs
+to the expansion port. However, the unlock commands only works on US and EU
+consoles.
+
+It is possible and pretty trivial to boot a PS-EXE straight from ROM by
+simply copying the program text from ROM to its desired location in RAM and
+transferring execution to it. Such an example may be created in the future.
+
+This ROM program example had to be written entirely in assembly as C cannot
+be used to write a ROM program even though it should be possible with some
+assembly code to take care of the bootstrapping. GNU ld would complain about
+'relocation truncation of R_MIPS_GPREL16' when you map program text to ROM
+starting at 0x1f000000 (EXP ROM segment) and program data and bss sections
+to RAM. Unknown how to get around this as all methods I've tried so far
+either don't work or it just produces a massive binary file.
+
+
+Building:
+
+To build this example, simply run make and a file named cartrom.rom should
+be created. Run the ROM in no$psx (make sure a ISO image has been opened
+first) or burn it to an Xplorer/PAR EEPROM (I recommend modding a PAR with
+a DIP32 socket if you wish to program the chip externally which in many
+cases would be easier).
+
+
+Changelog:
+
+May 23, 2019 - Initial version.
diff --git a/examples/cartrom/rom.ld b/examples/cartrom/rom.ld
new file mode 100644
index 0000000..f3f9cd3
--- /dev/null
+++ b/examples/cartrom/rom.ld
@@ -0,0 +1,11 @@
+MEMORY {
+ ROM : ORIGIN = 0x1f000000, LENGTH = 256K
+}
+
+ENTRY (entry)
+
+SECTIONS {
+ .text :{
+ *(.text)
+ } >ROM
+}
diff --git a/examples/cartrom/rom.s b/examples/cartrom/rom.s
new file mode 100644
index 0000000..14c1167
--- /dev/null
+++ b/examples/cartrom/rom.s
@@ -0,0 +1,399 @@
+# LibPSn00b Example Programs
+# Part of the PSn00bSDk project
+#
+# TurboBoot Example by Lameguy64
+#
+
+
+# Uncomment either PAR or XPLORER depending on the cartridge
+# you're going to use (makes disabling turbo boot via switch to work)
+
+#.set PAR, 0
+#.set XPLORER, 1
+
+
+.set noreorder
+
+.include "cop0.inc" # Contains definitions for cop0 registers
+
+.set SP_base, 0x801ffff0
+.set BREAK_ADDR, 0xa0000040 # cop0 breakpoint vector address
+
+
+.set RAM_buff, 2048
+.set RAM_handle, 2052
+.set RAM_tcb, 2056
+.set RAM_evcb, 2060
+.set RAM_stack, 2064
+.set RAM_psexe, 2068
+
+
+.set EXE_pc0, 0 # PS-EXE header offsets
+.set EXE_gp0, 4
+.set EXE_taddr, 8
+.set EXE_tsize, 12
+.set EXE_daddr, 16
+.set EXE_dsize, 20
+.set EXE_baddr, 24
+.set EXE_bsize, 28
+.set EXE_spaddr, 32
+.set EXE_sp_size, 36
+.set EXE_sp, 40
+.set EXE_fp, 44
+.set EXE_gp, 48
+.set EXE_ret, 52
+.set EXE_base, 56
+.set EXE_datapos, 60
+
+
+.section .text
+
+
+# ROM header
+#
+# The Licensed by... strings are essential otherwise the BIOS will not
+# execute the boot vectors. Always make sure the tty message fields (string
+# after Licensed by) must be no more than 80 bytes long and must have a null
+# terminating byte.
+#
+# Postboot vector isn't particularly practical as its only executed in between
+# the PS boot logo and the point where game execution occurs.
+#
+header:
+ # Postboot vector
+ .word 0
+ .ascii "Licensed by Sony Computer Entertainment Inc."
+ .ascii "Not officially licensed or endorsed by Sony Computer Entertainment Inc."
+
+ .balign 0x80 # This keeps things in the header aligned
+
+ # Preboot vector
+ .word preboot
+ .ascii "Licensed by Sony Computer Entertainment Inc."
+ .ascii "Cart ROM example for PSn00bSDK https://github.com/lameguy64/psn00bsdk"
+
+ .balign 0x80 # This keeps things in the header aligned
+
+
+# Preboot vector
+#
+# All it does is it initializes a breakpoint vector at 0x40
+# and sets a cop0 breakpoint at 0x80030000 to perform a midboot
+# exploit as preboot doesn't have the kernel area initialized.
+#
+preboot:
+
+ li $v0, 1
+
+.ifdef XPLORER
+ lui $a0, 0x1f06 # Read switch status for Xplorer
+ lbu $v0, 0($a0)
+.endif
+
+.ifdef PAR
+ lui $a0, 0x1f02 # Read switch status for PAR/GS devices
+ lbu $v0, 0x18($a0)
+.endif
+
+ nop
+ andi $v0, 0x1
+ beqz $v0, .no_rom # If switch is off don't install hook
+ nop # and effectively disables the cartridge
+
+ li $v0, BREAK_ADDR # Patch a jump at cop0 breakpoint vector
+
+ li $a0, 0x3c1a1f00 # lui $k0, $1f00
+ sw $a0, 0($v0)
+ la $a1, entry # ori $k0, < address to code entrypoint >
+ andi $a1, 0xffff
+ lui $a0, 0x375a
+ or $a0, $a1
+ sw $a0, 4($v0)
+ li $a0, 0x03400008 # jr $k0
+ sw $a0, 8($v0)
+ sw $0 , 12($v0) # nop
+
+ lui $v0, 0xffff # Set BPCM and BDAM masks
+ ori $v0, 0xffff
+ mtc0 $v0, BDAM
+ mtc0 $v0, BPCM
+
+
+ li $v0, 0x80030000 # Set break on PC and data-write address
+
+ mtc0 $v0, BDA # BPC break is for compatibility with no$psx
+ mtc0 $v0, BPC # as it does not emulate break on BDA
+
+ lui $v0, 0xeb80 # Enable break on data-write and and break
+ mtc0 $v0, DCIC # on PC to DCIC control register
+
+.no_rom:
+
+ jr $ra # Return to BIOS
+ nop
+
+
+# Actual ROM entrypoint
+.global entry
+entry:
+
+ mtc0 $0 , DCIC # Clear DCIC register
+
+ la $sp, SP_base # Set stack base
+ la $gp, 0x8000c000 # Set GP address as RAM base addr in this case
+
+ jal SetDefaultExitFromException # Set default exit handler just in case
+ nop
+ jal ExitCriticalSection # Exit out of critical section (brings back interrupts)
+ nop
+
+ # Beyond this point, the PS1 is in full control to the ROM
+
+ la $a0, m_banner # Print out program banner
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ la $a0, m_cdinit
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ jal _96_init # Initialize the CD
+ nop
+
+ la $a0, m_ok # Print OK message if init didn't crash
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ la $a0, m_readfile
+ la $a1, s_systemcnf
+ jal printf
+ addiu $sp, -8
+ addiu $sp, 8
+
+ la $a0, s_systemcnf # Attempt to open the SYSTEM.CNF file on CD
+ li $a1, 1
+ jal open
+ addiu $sp, -8
+ addiu $sp, 8
+
+ bltz $v0, .no_systemcnf # Fallback to loading PSX.EXE if not found
+ nop
+
+ sw $v0, RAM_handle($gp) # Save file handle
+
+ move $a0, $v0 # Read file contents of SYSTEM.CNF
+ move $a1, $gp
+ li $a2, 0x0800
+ jal read
+ addiu $sp, -12
+ addiu $sp, 12
+
+ lw $a0, RAM_handle($gp) # Close file
+ jal close
+ addiu $sp, -4
+ addiu $sp, 4
+
+ la $a0, m_ok # Output ok message
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ # Parse CNF file
+
+ la $a0, m_parsecnf
+ jal printf
+ nop
+
+ la $a1, s_tcb # Get TCB number
+ jal strcasestr
+ move $a0, $gp
+ jal skipspace # Skip spaces
+ addiu $a0, $v0, 3
+ addiu $a0, $v0, -2 # Step two charactters back and inject '0x'
+ li $v0, '0'
+ sb $v0, 0($a0)
+ li $v0, 'x'
+ sb $v0, 1($a0)
+ jal atoi
+ addiu $sp, -4
+ addiu $sp, 4
+ move $s1, $v0
+
+ la $a1, s_evcb # Get EVCB number
+ jal strcasestr
+ move $a0, $gp
+ jal skipspace
+ addiu $a0, $v0, 5
+ addiu $a0, $v0, -2
+ li $v0, '0'
+ sb $v0, 0($a0)
+ li $v0, 'x'
+ sb $v0, 1($a0)
+ jal atoi
+ addiu $sp, -4
+ addiu $sp, 4
+ move $s0, $v0
+
+ la $a1, s_stack # Get STACK address
+ jal strcasestr
+ move $a0, $gp
+ jal skipspace
+ addiu $a0, $v0, 5
+ addiu $a0, $v0, -2
+ li $v0, '0'
+ sb $v0, 0($a0)
+ li $v0, 'x'
+ sb $v0, 1($a0)
+ jal atoi
+ addiu $sp, -4
+ addiu $sp, 4
+ move $s2, $v0
+
+ la $a1, s_boot # Get the PS-EXE file name
+ jal strcasestr
+ move $a0, $gp
+
+ jal skipspace # Skip spaces
+ addiu $a0, $v0, 4
+
+ addiu $a0, $gp, RAM_psexe # Extract the line
+ jal getline
+ move $a1, $v0
+
+ la $a0, m_ok # Print successful parsing
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ la $a0, m_readfile
+ addiu $a1, $gp, RAM_psexe
+ jal printf
+ addiu $sp, -8
+ addiu $sp, 8
+
+ b .do_load # Proceed loading PS-EXE
+ addiu $a0, $gp, RAM_psexe
+
+.no_systemcnf: # Load fallback
+
+ la $a0, m_notfound
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ la $a0, m_fallback
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ li $s0, 0x10 # Default EvCBs and TCBs
+ li $s1, 0x04
+ li $s2, SP_base # Default stack
+ la $a0, s_psxexe # Attempt loading PSX.EXE
+
+.do_load:
+
+ jal LoadExe # Load PS-EXE
+ move $a1, $gp
+
+ beqz $v0, load_fail
+ nop
+
+ la $a0, m_ok
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ sw $s2, EXE_sp($gp) # Patch the header
+ sw $s2, EXE_spaddr($gp)
+
+ la $a0, m_boot
+ jal printf
+ addiu $sp, -4
+ addiu $sp, 4
+
+ jal EnterCriticalSection # Disable interrupt handling
+ nop
+
+ move $a0, $s0 # Set configuration (EvCBs and TCBs)
+ move $a1, $s1
+ move $a2, $s2
+ jal SetConf
+ addiu $sp, -12
+ addiu $sp, 12
+
+ move $a0, $gp # Transfer execution
+ move $a1, $0
+ move $a2, $0
+ jal DoExec
+ addiu $sp, -12
+ addiu $sp, 12
+
+
+load_fail: # Fail state
+ la $a0, m_loadfail
+ jal printf
+ nop
+.fail_loop:
+ b .fail_loop
+ nop
+
+
+.include "parse.inc"
+.include "bios.inc"
+
+
+# Strings
+
+s_boot:
+ .asciz "BOOT"
+ .balign 4
+s_tcb:
+ .asciz "TCB"
+ .balign 4
+s_evcb:
+ .asciz "EVENT"
+ .balign 4
+s_stack:
+ .asciz "STACK"
+ .balign 4
+s_systemcnf:
+ .asciz "cdrom:SYSTEM.CNF;1"
+ .balign 4
+s_psxexe:
+ .asciz "cdrom:PSX.EXE;1"
+ .balign 4
+
+
+# Message strings
+
+m_banner:
+ .asciz "\nCARTROM Bootstrap Example by Lameguy64\nPart of the PSn00bSDK Project\n\n"
+ .balign 4
+m_cdinit:
+ .asciz "Initializing CD-ROM (BIOS)... "
+ .balign 4
+m_readfile:
+ .asciz "Attempting to read %s... "
+ .balign 4
+m_parsecnf:
+ .asciz "Parsing CNF file... "
+ .balign 4
+m_fallback:
+ .asciz "Falling back to loading PSX.EXE... "
+ .balign 4
+m_notfound:
+ .asciiz "Not found.\n"
+ .balign 4
+m_ok:
+ .asciz "Ok.\n"
+ .balign 4
+m_boot:
+ .asciz "Boot!\n"
+ .balign 4
+m_loadfail:
+ .asciz "Failed to load PS-EXE file.\n"
+ .balign 4
diff --git a/examples/rgb24/bunpattern.tim b/examples/rgb24/bunpattern.tim
new file mode 100644
index 0000000..f233453
--- /dev/null
+++ b/examples/rgb24/bunpattern.tim
Binary files differ
diff --git a/examples/rgb24/main.c b/examples/rgb24/main.c
new file mode 100644
index 0000000..178ece2
--- /dev/null
+++ b/examples/rgb24/main.c
@@ -0,0 +1,52 @@
+/* LibPSn00b Example Programs
+ * Part of the PSn00bSDK Project
+ *
+ * RGB24 Example by Lameguy64
+ *
+ *
+ * This example demonstrates the 24-bit color mode of the PS1. This mode is
+ * not practical for gameplay as the GPU can only draw graphics primitives
+ * in 16-bit color depth so this feature would normally be used only for
+ * fullscreen graphic illustrations or FMV sequences.
+ *
+ *
+ * Changelog:
+ *
+ * 05-03-2019 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgte.h>
+#include <psxgpu.h>
+
+// So data from tim.s can be accessed
+extern unsigned int tim_image[];
+
+int main() {
+
+ DISPENV disp;
+ TIM_IMAGE tim;
+
+ // Reset GPU
+ ResetGraph(0);
+
+ // Setup 640x480 24-bit video mode
+ SetDefDispEnv(&disp, 0, 0, 640, 480);
+ disp.isrgb24 = 1;
+ disp.isinter = 1;
+
+ // Apply and enable display
+ PutDispEnv(&disp);
+ SetDispMask(1);
+
+ // Upload image to VRAM
+ GetTimInfo(tim_image, &tim);
+ LoadImage(tim.prect, tim.paddr);
+ DrawSync();
+
+ while(1) {
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/examples/rgb24/makefile b/examples/rgb24/makefile
new file mode 100644
index 0000000..ada9fbb
--- /dev/null
+++ b/examples/rgb24/makefile
@@ -0,0 +1,41 @@
+include ../sdk-common.mk
+
+TARGET = rgb24.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lc -lpsxgpu -lpsxapi -lgcc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.tim
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/rgb24/tim.s b/examples/rgb24/tim.s
new file mode 100644
index 0000000..a4432d9
--- /dev/null
+++ b/examples/rgb24/tim.s
@@ -0,0 +1,7 @@
+.section .data
+
+.global tim_image
+.type tim_image, @object
+tim_image:
+ .incbin "bunpattern.tim"
+ \ No newline at end of file