summaryrefslogtreecommitdiff
path: root/libpsx/src
diff options
context:
space:
mode:
authorXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
committerXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
commit7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch)
treec28d0748652ad4b4222309e46e6cfc82c0906220 /libpsx/src
parenta2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff)
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'libpsx/src')
-rw-r--r--libpsx/src/atexit.c23
-rw-r--r--libpsx/src/cdrom.c240
-rw-r--r--libpsx/src/cdromh.s109
-rw-r--r--libpsx/src/cop.c111
-rw-r--r--libpsx/src/costbl.h26
-rw-r--r--libpsx/src/exc1.s96
-rw-r--r--libpsx/src/exception.c178
-rw-r--r--libpsx/src/exception.h14
-rw-r--r--libpsx/src/font.h266
-rw-r--r--libpsx/src/gpu.c1507
-rw-r--r--libpsx/src/libc.c705
-rw-r--r--libpsx/src/libc/error.c19
-rw-r--r--libpsx/src/libc/misc.c106
-rw-r--r--libpsx/src/libc/printf.c899
-rw-r--r--libpsx/src/libc/qsort.c65
-rw-r--r--libpsx/src/libc/scanf.c425
-rw-r--r--libpsx/src/libc/stat.c31
-rw-r--r--libpsx/src/libc/string.c706
-rw-r--r--libpsx/src/libc/strings.c41
-rw-r--r--libpsx/src/libc/unistd.c17
-rw-r--r--libpsx/src/memcard.c87
-rw-r--r--libpsx/src/memory.c215
-rw-r--r--libpsx/src/memory.h6
-rw-r--r--libpsx/src/pad.c163
-rw-r--r--libpsx/src/psxsdk.c542
-rw-r--r--libpsx/src/runexe/runexe.c76
-rw-r--r--libpsx/src/runexe/stage2.s40
-rw-r--r--libpsx/src/setup.c50
-rw-r--r--libpsx/src/sio.c64
-rw-r--r--libpsx/src/spu.c269
-rwxr-xr-xlibpsx/src/start/start.s322
-rw-r--r--libpsx/src/syscalls.s344
-rw-r--r--libpsx/src/util.c42
33 files changed, 7804 insertions, 0 deletions
diff --git a/libpsx/src/atexit.c b/libpsx/src/atexit.c
new file mode 100644
index 0000000..835aae3
--- /dev/null
+++ b/libpsx/src/atexit.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+static void *f_ptr[32];
+static int f_ptr_pos = 0;
+
+int atexit(void (*function)(void))
+{
+ if(f_ptr_pos >= 32)
+ return -1;
+
+ f_ptr[f_ptr_pos++] = function;
+
+ return 0;
+}
+
+void call_atexit_callbacks(void)
+{
+ int i;
+ void (*f)(void);
+
+ for(i = (f_ptr_pos - 1); i >= 0; i--)
+ (f = f_ptr[i])();
+}
diff --git a/libpsx/src/cdrom.c b/libpsx/src/cdrom.c
new file mode 100644
index 0000000..ca14fd4
--- /dev/null
+++ b/libpsx/src/cdrom.c
@@ -0,0 +1,240 @@
+/*
+ * Low-level CDROM library
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <psx.h>
+
+#define CDREG(x) *((unsigned char*)(0x1f801800+x))
+
+void _internal_cdrom_handler();
+void (*cdrom_handler_callback)();
+volatile int cdrom_command_direct = 0;
+volatile int cdrom_command_done = 0;
+volatile int cdrom_direct_cmd;
+volatile int cdrom_command_dpos = 0;
+volatile unsigned char cdrom_last_command;
+volatile unsigned char cdrom_command_stat[2];
+
+unsigned int cdrom_queue_buf[4] = {0x0, /* Will contain next interrupt handler in queue */
+ 0x0, /* func1 */
+ (unsigned int)_internal_cdrom_handler, /* func2 */
+ 0x0, /* pad */
+ };
+
+static const unsigned char cdrom_command_type[0x1F] = // 0 = single int, 1 = double int, 2,3,... = others
+{
+ 0, // Sync 0
+ 0, // GetStat/Nop 1
+ 0, // Setloc 2
+ 0, // Play 3
+ 0, // Forward 4
+ 0, // Backward 5
+ 0, // ReadN 6
+ 0, // Standby 7
+ 0, // Stop 8
+ 1, // Pause 9
+ 1, // Init A
+ 0, // Mute B
+ 0, // Demute C
+ 0, // Setfilter D
+ 0, // Setmode E
+ 0, // Getmode F
+ 0, // GetlocL 10
+ 0, // GetlocP 11
+ 0xFF, // ??? 12
+ 0, // GetTN 13
+ 0, // GetTD 14
+ 1, // SeekL 15
+ 1, // SeekP 16
+ 0xFF, // ??? 17
+ 0xFF, // ??? 18
+ 0, // Test 19
+ 1, // ID 1A
+ 0, // ReadS 1B
+ 0, // Reset 1C
+ 0xFF, // ??? 1D
+ 1, // ReadToc 1E
+};
+
+void CdSendCommand(int cmd, int num, ...)
+{
+ int x;
+ va_list ap;
+ va_start(ap, num);
+
+// Wait for command execution to end
+// while(CDREG(0) & 128);
+
+// Flush old interrupts
+// If this is not done, some events (like the opening of the shell) will hang the CD controller.
+
+ CDREG(0) = 1;
+ CDREG(3) = 7;
+
+// Send parameters
+
+ CDREG(0) = 0;
+
+ while(num)
+ {
+ CDREG(2) = (unsigned char)va_arg(ap, unsigned int);
+ num--;
+ }
+
+// Send command
+
+ CDREG(0) = 0;
+ CDREG(1) = cmd;
+
+// Depending on the number of INTs we expect for a command,
+// we wait for an INT to occur, we store the response data returned,
+// and we flush the INT.
+ for(x = 0; x < (cdrom_command_type[cmd] + 1); x++)
+ {
+ CDREG(0) = 1;
+ while((CDREG(3) & 7) == 0);
+
+ cdrom_command_stat[x] = CDREG(1);
+
+ CDREG(0) = 1;
+ CDREG(3) = 7;
+ }
+
+// Store ID number of last executed command (this)
+ cdrom_last_command = cmd;
+
+ va_end(ap);
+}
+
+int CdReadResults(unsigned char *out, int max)
+{
+ int x;
+ unsigned char *outo = out;
+ unsigned char b;
+
+ for(x = 0; x < (cdrom_command_type[cdrom_last_command] + 1); x++)
+ {
+ if(max > 0)
+ {
+ *(out++) = cdrom_command_stat[x];
+ max--;
+ }
+ }
+
+ CDREG(0) = 1;
+
+ while(CDREG(0) & 0x20)
+ {
+ b = CDREG(1);
+ if(max>0)
+ {
+ *(out++) = b;
+ max--;
+ }
+ }
+
+ return (out-outo);
+}
+
+void _internal_cdromlib_callback()
+{
+ // 0 = ACKNOWLEDGE (0x*2)
+ // 1 = ACKNOWLEDGE (0x*2), COMPLETE (0x*3)
+
+/* int x;
+
+
+
+ unsigned char i;
+
+
+ if(cdrom_command_done)
+ return;
+
+ for(x = 0; x < 100; x++); // Waste time
+
+ CDREG(0) = 1;
+ i=CDREG(3);
+
+ if((i&0xf)==5) // Error
+ cdrom_command_done = 1;
+
+ //printf("i&0xf = %x\n", i&0xf);
+// printf("cdrom_direct_cmd = %x\n", cdrom_direct_cmd);
+
+ switch(kind[cdrom_direct_cmd])
+ {
+ case 0:
+ if(((i&0xf)==3) || ((i&0xf) == 2))
+ cdrom_command_done = 1;
+ break;
+
+ case 1:
+ if((i&0xf)==2)
+ cdrom_command_done = 1;
+ break;
+
+ case 0xFF: // Unknown command!
+ cdrom_command_done = 1;
+ return;
+ break;
+ }
+
+ cdrom_command_stat[0] = i;
+
+ for(x = 0; x < 100; x++); // Waste time
+
+ CDREG(0) = 1;
+ CDREG(3) = 7;
+ i = CDREG(1);
+ cdrom_command_stat[1] = i;
+
+ //printf("cdrom_command_done = %d\n", cdrom_command_done);*/
+}
+
+void _internal_cdromlib_init()
+{
+ printf("Starting CDROMlib...\n");
+
+ EnterCriticalSection(); // Disable IRQs
+
+ SysEnqIntRP(0, cdrom_queue_buf);
+
+ IMASK|=4;
+
+ cdrom_handler_callback = _internal_cdromlib_callback;
+
+ ExitCriticalSection(); // Enable IRQs
+}
+
+int CdGetStatus()
+{
+ unsigned char out;
+
+ CdSendCommand(CdlGetstat, 0);
+ CdReadResults(&out, 1);
+
+ return out;
+}
+
+int CdPlayTrack(unsigned int track)
+{
+ while(CdGetStatus() & CDSTATUS_SEEK);
+ CdSendCommand(CdlSetmode, 1, 0x20);
+ CdSendCommand(CdlPlay, 1, ((track/10)<<4)|(track%10));
+
+ return 1;
+}
+
+unsigned char CdRamRead(unsigned short addr)
+{
+ unsigned char b;
+ addr &= 0x3ff;
+
+ CdSendCommand(0x19, 0x60, addr&0xff, addr >> 8);
+ CdReadResults(&b, 1);
+
+ return b;
+}
diff --git a/libpsx/src/cdromh.s b/libpsx/src/cdromh.s
new file mode 100644
index 0000000..b2968c1
--- /dev/null
+++ b/libpsx/src/cdromh.s
@@ -0,0 +1,109 @@
+.global _internal_cdrom_handler
+.set noat
+
+
+_internal_cdrom_handler:
+ addi $sp, -112
+ sw $at, 0($sp)
+ sw $v0, 4($sp)
+ sw $v1, 8($sp)
+ sw $a0, 12($sp)
+ sw $a1, 16($sp)
+ sw $a2, 20($sp)
+ sw $a3, 24($sp)
+ sw $t0, 28($sp)
+ sw $t1, 32($sp)
+ sw $t2, 36($sp)
+ sw $t3, 40($sp)
+ sw $t4, 44($sp)
+ sw $t5, 48($sp)
+ sw $t6, 52($sp)
+ sw $t7, 56($sp)
+ sw $s0, 60($sp)
+ sw $s1, 64($sp)
+ sw $s2, 68($sp)
+ sw $s3, 72($sp)
+ sw $s4, 76($sp)
+ sw $s5, 80($sp)
+ sw $s6, 84($sp)
+ sw $s7, 88($sp)
+ sw $t8, 92($sp)
+ sw $t9, 96($sp)
+ sw $gp, 100($sp)
+ sw $s8, 104($sp)
+ sw $ra, 108($sp)
+
+# Do not run code if cdrom interrupt is not enabled
+
+ li $t0, 0x1f801074
+ lw $t1, 0($t0)
+ andi $t1, $t1, 4
+ beq $t1, $zero, cdrom_handler_end
+ nop
+
+# Do not run code if cdrom interrupt is not pending
+
+ li $t0, 0x1f801070
+ lw $t1, 0($t0)
+ andi $t1, $t1, 4
+ beq $t1, $zero, cdrom_handler_end
+ nop
+
+# If the CDROM command isn't direct
+# (direct = sent by us and not by the BIOS' ISO9660 routines)
+# exit and let the BIOS do its work.
+
+cdrom_check_direct_cmd:
+ la $t0, cdrom_command_direct
+ lw $t1, 0($t0)
+ beq $t1, $zero, cdrom_handler_end
+ nop
+
+cdrom_fire_user_handler:
+ la $t0, cdrom_handler_callback
+ lw $t1, 0($t0)
+
+ jalr $t1
+ nop
+
+# Remove bit for CDROM interrupt (bit 2) from pending interrupts mask.
+
+cdrom_handler_remove_pending:
+ li $t0, 0x1f801070
+ lw $t1, 0($t0)
+ xori $t1, $t1, 4
+ sw $t1, 0($t0)
+
+cdrom_handler_end:
+
+ lw $at, 0($sp)
+ lw $v0, 4($sp)
+ lw $v1, 8($sp)
+ lw $a0, 12($sp)
+ lw $a1, 16($sp)
+ lw $a2, 20($sp)
+ lw $a3, 24($sp)
+ lw $t0, 28($sp)
+ lw $t1, 32($sp)
+ lw $t2, 36($sp)
+ lw $t3, 40($sp)
+ lw $t4, 44($sp)
+ lw $t5, 48($sp)
+ lw $t6, 52($sp)
+ lw $t7, 56($sp)
+ lw $s0, 60($sp)
+ lw $s1, 64($sp)
+ lw $s2, 68($sp)
+ lw $s3, 72($sp)
+ lw $s4, 76($sp)
+ lw $s5, 80($sp)
+ lw $s6, 84($sp)
+ lw $s7, 88($sp)
+ lw $t8, 92($sp)
+ lw $t9, 96($sp)
+ lw $gp, 100($sp)
+ lw $s8, 104($sp)
+ lw $ra, 108($sp)
+ addi $sp, 112
+ jr $ra
+ nop
diff --git a/libpsx/src/cop.c b/libpsx/src/cop.c
new file mode 100644
index 0000000..04b49f5
--- /dev/null
+++ b/libpsx/src/cop.c
@@ -0,0 +1,111 @@
+#include <psx.h>
+
+unsigned int get_cop_register(unsigned char cop_num,
+ unsigned char register_num)
+{
+// Workaround for MIPS' simplicistic instruction set...
+
+ unsigned int instr[] =
+ {0x40020000, // cfc $v0, 0
+ 0x03E00008, // jr $ra
+ 0x00000000, // nop
+ 0x00000000}; // nop
+
+ int (*rawFunc)() = (void*)instr;
+
+// Write coprocessor register number inside instruction
+ instr[0] |= ( (cop_num & 3) << 26 ) | ( (register_num & 31) << 11 );
+
+// Execute modified instruction
+ return rawFunc();
+}
+
+unsigned int get_cop_ctrl_register(unsigned char cop_num,
+ unsigned char register_num)
+{
+// Workaround for MIPS' simplicistic instruction set...
+ unsigned int instr[] =
+ {0x40420000, // mfc $v0, 0
+ 0x03E00008, // jr $ra
+ 0x00000000, // nop
+ 0x00000000}; // nop
+
+ int (*rawFunc)() = (void*)instr;
+
+// Write coprocessor register number inside instruction
+ instr[0] |= ( (cop_num & 3) << 26 ) | ( (register_num & 31) << 11 );
+
+// Execute modified instruction
+ return rawFunc();
+}
+
+unsigned int get_cop0_register(unsigned char register_num)
+{
+ return get_cop_register(0, register_num);
+}
+
+void set_cop_register(unsigned char cop_num,
+ unsigned char register_num,
+ unsigned int value)
+{
+// Workaround for MIPS' simplicistic instruction set...
+ unsigned int instr[] =
+ {0x40840000, // mtc $a0, 0
+ 0x03E00008, // jr $ra
+ 0x00000000, // nop
+ 0x00000000}; // nop
+
+ void (*rawFunc)(int value) = (void*)instr;
+
+// Write coprocessor register number inside instruction
+ instr[0] |= ( (cop_num & 3) << 26 ) | ( (register_num & 31) << 11 );
+
+// Execute modified instruction
+ rawFunc(value);
+}
+
+void set_cop_ctrl_register(unsigned char cop_num,
+ unsigned char register_num,
+ unsigned int value)
+{
+// Workaround for MIPS' simplicistic instruction set...
+ unsigned int instr[] =
+ {0x40C40000, // ctc $a0, 0
+ 0x03E00008, // jr $ra
+ 0x00000000, // nop
+ 0x00000000}; // nop
+
+ void (*rawFunc)(int value) = (void*)instr;
+
+// Write coprocessor register number inside instruction
+ instr[0] |= ( (cop_num & 3) << 26 ) | ( (register_num & 31) << 11 );
+
+// Execute modified instruction
+ rawFunc(value);
+}
+
+void set_cop0_register(unsigned char register_num,
+ unsigned int value)
+{
+ set_cop_register(0, register_num, value);
+}
+
+void run_cop_instruction(unsigned char cop_num,
+ unsigned int operation)
+{
+// Workaround for MIPS' simplicistic instruction set...
+ unsigned int instr[] =
+ {0x42000000, // cop 0
+ 0x03E00008, // jr $ra
+ 0x00000000, // nop
+ 0x00000000}; // nop
+
+ void (*rawFunc)(void) = (void*)instr;
+
+// Write coprocessor register number inside instruction
+ instr[0] |= ( (cop_num & 3) << 26 ) | (operation & 0x1ffffff);
+
+// Execute modified instruction
+ rawFunc();
+}
+
diff --git a/libpsx/src/costbl.h b/libpsx/src/costbl.h
new file mode 100644
index 0000000..2af73a6
--- /dev/null
+++ b/libpsx/src/costbl.h
@@ -0,0 +1,26 @@
+// Cosine / sine table used for rotations, 1 degree precision
+// While not as good, much faster than using libm cos() and sin() and does not require
+// to link libm with libpsx..
+
+const double gs_rot_cos_tbl[]=
+{
+ 1.000000,0.999848,0.999391,0.998630,0.997564,
+ 0.996195,0.994522,0.992546,0.990268,0.987688,
+ 0.984808,0.981627,0.978148,0.974370,0.970296,
+ 0.965926,0.961262,0.956305,0.951057,0.945519,
+ 0.939693,0.933580,0.927184,0.920505,0.913545,
+ 0.906308,0.898794,0.891007,0.882948,0.874620,
+ 0.866025,0.857167,0.848048,0.838671,0.829038,
+ 0.819152,0.809017,0.798636,0.788011,0.777146,
+ 0.766044,0.754710,0.743145,0.731354,0.719340,
+ 0.707107,0.694658,0.681998,0.669131,0.656059,
+ 0.642788,0.629320,0.615661,0.601815,0.587785,
+ 0.573576,0.559193,0.544639,0.529919,0.515038,
+ 0.500000,0.484810,0.469472,0.453990,0.438371,
+ 0.422618,0.406737,0.390731,0.374607,0.358368,
+ 0.342020,0.325568,0.309017,0.292372,0.275637,
+ 0.258819,0.241922,0.224951,0.207912,0.190809,
+ 0.173648,0.156434,0.139173,0.121869,0.104528,
+ 0.087156,0.069756,0.052336,0.034899,0.017452,
+ 0.000000,
+};
diff --git a/libpsx/src/exc1.s b/libpsx/src/exc1.s
new file mode 100644
index 0000000..379ed6d
--- /dev/null
+++ b/libpsx/src/exc1.s
@@ -0,0 +1,96 @@
+
+.text
+.global __psxsdk_exception_manager
+
+__psxsdk_exception_manager:
+
+# Save registers on stack
+
+.set noat
+ addi $sp, -120
+.set noat
+ sw $at, 0($sp)
+ mfhi $at
+ sw $at, 112($sp)
+ mflo $at
+ sw $at, 116($sp)
+.set at
+ sw $v0, 4($sp)
+ sw $v1, 8($sp)
+ sw $a0, 12($sp)
+ sw $a1, 16($sp)
+ sw $a2, 20($sp)
+ sw $a3, 24($sp)
+ sw $t0, 28($sp)
+ sw $t1, 32($sp)
+ sw $t2, 36($sp)
+ sw $t3, 40($sp)
+ sw $t4, 44($sp)
+ sw $t5, 48($sp)
+ sw $t6, 52($sp)
+ sw $t7, 56($sp)
+ sw $s0, 60($sp)
+ sw $s1, 64($sp)
+ sw $s2, 68($sp)
+ sw $s3, 72($sp)
+ sw $s4, 76($sp)
+ sw $s5, 80($sp)
+ sw $s6, 84($sp)
+ sw $s7, 88($sp)
+ sw $t8, 92($sp)
+ sw $t9, 96($sp)
+ sw $gp, 100($sp)
+ sw $fp, 104($sp)
+ sw $ra, 108($sp)
+
+# Execute real exception handler
+ jal __psxsdk_real_exception_handler
+ nop
+
+# Load registers from stack
+
+.set noat
+ lw $at, 112($sp)
+ nop
+ mthi $at
+ lw $at, 116($sp)
+ nop
+ mtlo $at
+ lw $at, 0($sp)
+.set at
+ lw $v0, 4($sp)
+ lw $v1, 8($sp)
+ lw $a0, 12($sp)
+ lw $a1, 16($sp)
+ lw $a2, 20($sp)
+ lw $a3, 24($sp)
+ lw $t0, 28($sp)
+ lw $t1, 32($sp)
+ lw $t2, 36($sp)
+ lw $t3, 40($sp)
+ lw $t4, 44($sp)
+ lw $t5, 48($sp)
+ lw $t6, 52($sp)
+ lw $t7, 56($sp)
+ lw $s0, 60($sp)
+ lw $s1, 64($sp)
+ lw $s2, 68($sp)
+ lw $s3, 72($sp)
+ lw $s4, 76($sp)
+ lw $s5, 80($sp)
+ lw $s6, 84($sp)
+ lw $s7, 88($sp)
+ lw $t8, 92($sp)
+ lw $t9, 96($sp)
+ lw $gp, 100($sp)
+ lw $fp, 104($sp)
+ lw $ra, 108($sp)
+ addiu $sp, 120
+
+# Get exception return address..
+ mfc0 $k0, $14
+# Exit from exception handler
+.set noreorder # Do not let the assembler fill the delay slot!
+ jr $k0
+ rfe
+.set reorder # The assembler can fill the delay slot again.
diff --git a/libpsx/src/exception.c b/libpsx/src/exception.c
new file mode 100644
index 0000000..6ecabb2
--- /dev/null
+++ b/libpsx/src/exception.c
@@ -0,0 +1,178 @@
+/**
+ * exception.c
+ *
+ * Exception handling code (part 2)
+ */
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <psx.h>
+
+#define DICR *((unsigned int*)0x1f8010f4)
+#define IPENDING *((volatile unsigned int*)0x1f801070)
+#define IMASK *((volatile unsigned int*)0x1f801074)
+
+void __psxsdk_exception_manager();
+
+void (*_EXC_vblank_handler)();
+void (*_EXC_cdrom_handler)();
+void (*_EXC_sio_handler)(unsigned char *data);
+void (*_EXC_dma_handler)();
+unsigned int _EXC_vblank_handler_set;
+unsigned int _EXC_cdrom_handler_set;
+unsigned int _EXC_sio_handler_set;
+unsigned int _EXC_dma_handler_set;
+
+volatile int __psxsdk_gpu_dma_finished;
+
+void __psxsdk_real_exception_handler()
+{
+ unsigned int Cause = get_cop0_register(COP0_CAUSE);
+ unsigned int Cause_excCode = (Cause >> 2) & 31;
+ unsigned int Cause_IP = (Cause >> 8) & 255;
+ unsigned int SR = get_cop0_register(COP0_SR);
+ unsigned int SR_IM = (SR >> 8) & 255;
+ int i;
+/* unsigned int oldSR = SR;
+ unsigned int psxIP = IPENDING;
+ unsigned int psxIM = IMASK;
+ unsigned int sio_data;*/
+
+ if(Cause_excCode == 0) // interrupt generated the exception
+ {
+ /*for(i = 0; i < 8; i++)
+ {
+ if((Cause_IP & (1<<i)) && (SR_IM & (1<<i)))
+ {
+ SR ^= 1<<(i+8);
+ set_cop0_register(COP0_SR, SR);
+
+ switch(i)
+ {
+ case 2: // PSX Interrupt controller
+ // program_vblank_handler();
+ break;
+ }
+ }
+ }*/
+
+ if((Cause_IP & (1<<2)) && (SR_IM & (1<<2)))
+ {
+ Cause ^= 1<<10;
+ set_cop0_register(COP0_CAUSE, Cause);
+ //SR ^= 1<<10;
+ //set_cop0_register(COP0_SR, SR);
+
+ //while(IPENDING != 0)
+ {
+ for(i = 0; i < 11; i++)
+ {
+ if(IPENDING & (1<<i))
+ {
+ // printf("IP = %x\n", IPENDING);
+// Very interesting, when reading joypad status the PCSXR emulator sets in IPENDING that IRQ7 is pending even
+// if it is not enabled in IMASK! That is insane, but it can be easily worked around.
+// So we are going to disable pending bits for IRQs in IPENDING in any case, even if the in IRQ at hand was
+// not enabled in IMASK.
+
+ IPENDING ^= 1 << i;
+
+ if(IMASK & (1<<i))
+ {
+ // printf("IM = %x\n", IMASK);
+
+ switch(i)
+ {
+ case 0: // VBLANK
+ // if(!(GPU_CONTROL_PORT & (1<<0x1c)))
+ // GPU_CONTROL_PORT = 0x02000000;
+
+ // Execute the user-supplied VBlank handler.
+ if(_EXC_vblank_handler_set)
+ _EXC_vblank_handler();
+ break;
+ case 2:
+ /* if(_EXC_cdrom_handler_set)
+ _EXC_cdrom_handler();*/
+ break;
+ case 3: // DMA
+ // Execute the user-supplied DMA handler.
+ if(_EXC_dma_handler_set)
+ _EXC_dma_handler();
+ break;
+ case 8:
+ //while(!(SIO_STAT & 2))
+ //sio_data = SIO_RX_DATA;
+
+ //_EXC_sio_handler((unsigned char*)&sio_data);
+
+ // SIO_CTRL |= SIOCTRL_ACK;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void __psxsdk_dma_handler()
+{
+ unsigned int s_dicr = DICR;
+ unsigned char irq = (s_dicr >> 24) & 127;
+
+ if(irq & (1<<2)) // GPU
+ __psxsdk_gpu_dma_finished = 1;
+
+ // Acknowledge
+ DICR = s_dicr;
+
+ // Waste some cycles, so that the acknowledgement is reported
+// int x;
+
+// for(x = 0; x < 1000; x++);
+}
+
+extern void _internal_cdromlib_callback();
+
+ void __PSX_Init_NoBios()
+ {
+ _EXC_vblank_handler = NULL;
+ _EXC_cdrom_handler = _internal_cdromlib_callback;
+ _EXC_dma_handler = __psxsdk_dma_handler;
+ _EXC_sio_handler = NULL;
+
+ _EXC_vblank_handler_set = 0;
+ _EXC_cdrom_handler_set = 0;
+ _EXC_dma_handler_set = 1;
+ _EXC_sio_handler_set = 0;
+
+ IMASK = 0; // Clear Mask
+ IPENDING = 0; // Clear pending interrupts
+
+// Disable interrupts
+
+ set_cop0_register(COP0_SR, 0);
+
+// Change exception vector to point to our exception manager
+
+ *((unsigned int*)0x80000080) = 0x08000000 | ((((unsigned int)__psxsdk_exception_manager)>>2) & 0x3FFFFFF);
+ *((unsigned int*)0x80000084) = 0;
+
+
+// Enable interrupt generation, and interrupt 2 (PlayStation Interrupt Controller)
+ set_cop0_register(COP0_SR, (1<<10) | 1);
+
+// Enable VBlank, CDROM and DMA IRQs (on PlayStation Interrupt Controller)
+ IMASK = 1 | /* CDROM */ /*4 |*/ 8;
+
+// Set DMA channel priority
+ DPCR = 0x07654321;
+
+// Enable DMA IRQ master, and IRQ generation for DMA channel 2 (GPU)
+ DICR = (1<<23) | (1<<(16+2));
+
+// Setup variables
+ __psxsdk_gpu_dma_finished = 1;
+}
diff --git a/libpsx/src/exception.h b/libpsx/src/exception.h
new file mode 100644
index 0000000..1c72798
--- /dev/null
+++ b/libpsx/src/exception.h
@@ -0,0 +1,14 @@
+#ifndef _PSXSDK_EXCEPTION_H
+#define _PSXSDK_EXCEPTION_H
+
+extern void (*_EXC_vblank_handler)();
+extern void (*_EXC_sio_handler)(unsigned char *data);
+extern void (*_EXC_dma_handler)();
+extern unsigned int _EXC_vblank_handler_set;
+extern unsigned int _EXC_sio_handler_set;
+extern unsigned int _EXC_dma_handler_set;
+
+
+void __PSX_Init_NoBios();
+
+#endif
diff --git a/libpsx/src/font.h b/libpsx/src/font.h
new file mode 100644
index 0000000..b1c71cb
--- /dev/null
+++ b/libpsx/src/font.h
@@ -0,0 +1,266 @@
+// 8x8 ASCII font
+// 64x128
+// 4-bit format
+
+// The font was created by John Reeves Hall
+
+unsigned char psxsdk_font_data[] =
+{
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 16, 16, 0, 0, 16, 16, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 16, 1, 0, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 16, 16, 0, 0, 16, 16, 0,
+0, 16, 17, 1, 0, 17, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 16, 17, 17, 17,
+0, 1, 1, 0, 0, 17, 16, 0, 0, 16, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 16, 16, 0,
+0, 16, 17, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 16, 17, 17, 17,
+0, 0, 1, 1, 0, 16, 16, 1, 16, 0, 16, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 0,
+0, 17, 17, 0, 0, 1, 16, 1, 16, 0, 16, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 16, 16, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 17, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+0, 0, 1, 0, 0, 0, 1, 0, 16, 0, 1, 16, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 1, 1, 1, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 16, 17, 0, 16, 17, 17, 17,
+0, 0, 0, 0, 16, 17, 17, 17, 0, 0, 0, 0, 0, 0, 1, 0,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 1, 1, 1, 0, 0, 1, 0,
+0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 16, 0, 1, 16, 0, 0, 1, 0,
+0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 1, 0, 0,
+0, 0, 16, 0, 0, 16, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
+0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 17, 0, 16, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 16, 17, 0, 0, 0, 1, 0, 0, 16, 17, 0, 0, 16, 17, 0,
+0, 0, 17, 0, 0, 17, 17, 1, 0, 16, 17, 0, 0, 17, 17, 1,
+0, 1, 0, 1, 0, 16, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+0, 16, 16, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1,
+0, 1, 16, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 16, 0,
+0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 17, 0,
+0, 17, 17, 1, 0, 17, 17, 0, 0, 17, 17, 0, 0, 0, 1, 0,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+0, 0, 16, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 16, 0, 0,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 16, 0, 0, 0, 1, 0, 1,
+0, 0, 16, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 16, 0, 0,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 17, 17, 1, 0, 16, 17, 0,
+0, 0, 17, 1, 0, 16, 17, 0, 0, 16, 17, 0, 0, 16, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 16, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 17, 0,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 17, 0, 0, 0, 17, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 17, 0, 0, 0, 17, 0,
+0, 16, 0, 0, 16, 17, 17, 17, 0, 0, 1, 0, 0, 0, 0, 1,
+0, 16, 17, 0, 0, 16, 17, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0,
+0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 17, 0, 0, 0, 17, 0,
+0, 16, 0, 0, 16, 17, 17, 17, 0, 0, 1, 0, 0, 0, 1, 0,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 17, 0, 0, 0, 17, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+0, 0, 16, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 17, 17, 0, 0, 16, 17, 0,
+0, 17, 17, 0, 0, 17, 17, 1, 0, 17, 17, 1, 0, 16, 17, 0,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1,
+0, 1, 17, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
+0, 1, 1, 1, 0, 17, 17, 1, 0, 17, 17, 0, 0, 1, 0, 0,
+0, 1, 0, 1, 0, 17, 17, 0, 0, 17, 17, 1, 0, 1, 17, 1,
+0, 1, 17, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1,
+0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1,
+0, 16, 17, 0, 0, 1, 0, 1, 0, 17, 17, 0, 0, 16, 17, 0,
+0, 17, 17, 0, 0, 17, 17, 1, 0, 1, 0, 0, 0, 16, 17, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 16, 17, 0, 0, 0, 17, 1, 0, 1, 0, 1,
+0, 16, 0, 0, 16, 0, 0, 16, 0, 1, 0, 1, 0, 16, 17, 0,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 16, 0, 0, 1, 0, 1,
+0, 16, 0, 0, 16, 1, 0, 17, 0, 17, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 16, 0, 0, 1, 16, 0,
+0, 16, 0, 0, 16, 16, 16, 16, 0, 1, 1, 1, 0, 1, 0, 1,
+0, 17, 17, 1, 0, 0, 1, 0, 0, 0, 16, 0, 0, 17, 1, 0,
+0, 16, 0, 0, 16, 0, 1, 16, 0, 1, 1, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 16, 0, 0, 1, 16, 0,
+0, 16, 0, 0, 16, 0, 0, 16, 0, 1, 16, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 16, 0, 0, 1, 0, 1,
+0, 16, 0, 0, 16, 0, 0, 16, 0, 1, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 16, 17, 0, 0, 16, 1, 0, 0, 1, 0, 1,
+0, 16, 17, 1, 16, 0, 0, 16, 0, 1, 0, 1, 0, 16, 17, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 17, 17, 0, 0, 16, 17, 0,
+0, 17, 17, 1, 0, 1, 0, 1, 0, 1, 0, 1, 16, 0, 0, 16,
+0, 16, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 16, 0, 0, 16,
+0, 16, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 16, 0, 0, 16,
+0, 16, 17, 0, 0, 1, 0, 1, 0, 17, 17, 0, 0, 16, 17, 0,
+0, 0, 1, 0, 0, 1, 0, 1, 0, 16, 16, 0, 0, 1, 1, 1,
+0, 16, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
+0, 0, 1, 0, 0, 1, 0, 1, 0, 16, 16, 0, 0, 1, 1, 1,
+0, 16, 0, 0, 0, 1, 0, 1, 0, 1, 16, 0, 0, 1, 0, 1,
+0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 16, 16, 0,
+0, 16, 0, 0, 0, 16, 17, 0, 0, 1, 0, 1, 0, 16, 17, 0,
+0, 0, 1, 0, 0, 16, 17, 0, 0, 0, 1, 0, 0, 16, 16, 0,
+0, 0, 0, 0, 0, 0, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 17, 17, 1, 0, 16, 17, 0,
+16, 0, 0, 0, 0, 16, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 16, 0, 0,
+0, 1, 0, 0, 0, 0, 16, 0, 0, 16, 16, 0, 0, 0, 0, 0,
+0, 16, 16, 0, 0, 16, 16, 0, 0, 0, 16, 0, 0, 16, 0, 0,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 1, 0, 1, 0, 0, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 16, 16, 0, 0, 0, 1, 0, 0, 16, 0, 0, 0, 16, 0, 0,
+0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 16, 0, 0,
+0, 0, 0, 1, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 17, 17, 1, 0, 16, 17, 0,
+0, 0, 0, 16, 0, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 17, 17, 17,
+0, 16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0,
+0, 0, 1, 0, 0, 16, 17, 0, 0, 16, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 0, 0, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 1, 0, 16, 17, 0, 0, 16, 17, 0,
+0, 0, 17, 1, 0, 16, 17, 0, 0, 16, 0, 0, 0, 16, 17, 16,
+0, 0, 0, 0, 0, 16, 17, 1, 0, 16, 0, 1, 0, 1, 0, 0,
+0, 16, 0, 1, 0, 1, 0, 1, 0, 17, 1, 0, 0, 1, 0, 1,
+0, 0, 0, 0, 0, 1, 0, 1, 0, 16, 0, 1, 0, 1, 0, 0,
+0, 16, 0, 1, 0, 17, 17, 1, 0, 16, 0, 0, 0, 1, 0, 1,
+0, 0, 0, 0, 0, 1, 0, 1, 0, 16, 0, 1, 0, 1, 0, 0,
+0, 16, 0, 1, 0, 1, 0, 0, 0, 16, 0, 0, 0, 16, 17, 1,
+0, 0, 0, 0, 0, 16, 17, 16, 0, 1, 17, 0, 0, 16, 17, 0,
+0, 0, 17, 16, 0, 16, 17, 0, 0, 16, 0, 0, 0, 0, 0, 1,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 17, 0,
+0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+0, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 0,
+0, 0, 1, 0, 1, 17, 16, 1, 0, 1, 17, 0, 0, 16, 17, 0,
+0, 17, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0,
+0, 0, 1, 0, 16, 0, 1, 16, 0, 16, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 17, 0, 0,
+0, 0, 1, 0, 16, 0, 1, 16, 0, 16, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0,
+0, 0, 1, 0, 16, 0, 0, 16, 0, 16, 0, 1, 0, 1, 0, 1,
+0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 16, 0,
+0, 0, 1, 0, 16, 0, 0, 16, 0, 16, 0, 1, 0, 16, 17, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 17, 0, 0, 16, 1, 1, 0, 1, 17, 0, 0, 16, 17, 0,
+0, 16, 17, 0, 0, 1, 16, 0, 0, 1, 0, 1, 16, 0, 0, 16,
+0, 16, 0, 1, 0, 1, 16, 0, 0, 17, 0, 0, 0, 1, 0, 0,
+0, 0, 1, 0, 0, 1, 16, 0, 0, 1, 0, 1, 16, 0, 0, 16,
+0, 16, 0, 1, 0, 1, 16, 0, 0, 1, 0, 0, 0, 16, 1, 0,
+0, 0, 1, 0, 0, 1, 16, 0, 0, 1, 0, 1, 16, 0, 1, 16,
+0, 16, 17, 0, 0, 16, 17, 0, 0, 1, 0, 0, 0, 0, 16, 0,
+0, 0, 1, 0, 0, 1, 16, 0, 0, 16, 16, 0, 16, 16, 16, 16,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 1, 0, 0, 0, 17, 1, 0,
+0, 0, 1, 0, 0, 16, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
+0, 16, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0,
+0, 0, 1, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 16, 0, 1, 0, 17, 17, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 0, 17, 0, 0, 0, 0, 0, 0,
+0, 16, 16, 0, 0, 16, 0, 1, 0, 0, 16, 0, 0, 1, 0, 0,
+0, 0, 1, 0, 0, 0, 16, 0, 16, 0, 1, 16, 0, 0, 0, 0,
+0, 0, 1, 0, 0, 16, 0, 1, 0, 0, 1, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 16, 1, 0, 0, 0, 0,
+0, 16, 16, 0, 0, 0, 17, 1, 0, 16, 0, 0, 0, 16, 0, 0,
+0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 0, 1, 0, 0, 0, 1, 0, 17, 17, 0, 0, 0, 17, 0,
+0, 0, 1, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
diff --git a/libpsx/src/gpu.c b/libpsx/src/gpu.c
new file mode 100644
index 0000000..7e097c6
--- /dev/null
+++ b/libpsx/src/gpu.c
@@ -0,0 +1,1507 @@
+// PSXSDK Graphics Processing Unit (GPU) / Graphics Synthesizer (GS)
+// Routines
+
+#include <psx.h>
+#include <stdio.h>
+#include <strings.h>
+#include "font.h"
+#include "costbl.h"
+
+extern volatile int __psxsdk_gpu_dma_finished;
+
+static unsigned int *linked_list;
+static unsigned int linked_list_pos;
+
+int fb_font_x, fb_font_y, fb_font_cx, fb_font_cy;
+
+static unsigned int prfont_flags = 0;
+static int prfont_scale_x = 0;
+static int prfont_scale_y = 0;
+static unsigned char prfont_rl = NORMAL_LUMINANCE;
+static unsigned char prfont_gl = NORMAL_LUMINANCE;
+static unsigned char prfont_bl = NORMAL_LUMINANCE;
+
+unsigned short GsScreenW;
+unsigned short GsScreenH;
+unsigned char GsScreenM;
+
+unsigned short GsCurDrawEnvW;
+unsigned short GsCurDrawEnvH;
+
+double gs_vbuf[4][3];
+
+static int __gs_autowait = 0;
+
+unsigned int PRFONT_SCALEX(int i)
+{
+ prfont_scale_x = i;
+ return PRFONT_SCALE;
+}
+
+unsigned int PRFONT_SCALEY(int i)
+{
+ prfont_scale_y = i;
+ return PRFONT_SCALE;
+}
+
+unsigned int PRFONT_RL(unsigned char f)
+{
+ prfont_rl = f;
+ return PRFONT_COLOR;
+}
+
+unsigned int PRFONT_GL(unsigned char f)
+{
+ prfont_gl = f;
+ return PRFONT_COLOR;
+}
+
+unsigned int PRFONT_BL(unsigned char f)
+{
+ prfont_bl = f;
+ return PRFONT_COLOR;
+}
+
+unsigned int draw_mode_packet;
+
+unsigned int setup_attribs(unsigned char tpage, unsigned int attribute, unsigned char *packet);
+static void gs_internal_vector_rotate(int x_a, int y_a, int z_a, double *v, double *n);
+
+static char gpu_stringbuf[512];
+
+int gs_calculate_scaled_size(int size, int scale)
+{
+ if(scale > 8)
+ return (size * scale) / SCALE_ONE;
+ else if(scale == 0)
+ return size;
+ else if(scale > 0)
+ return size * scale;
+ else if(scale > -8)
+ return size / (scale * -1);
+
+ return (size * SCALE_ONE) / -scale;
+}
+
+void GsSetList(unsigned int *listptr)
+{
+ linked_list = listptr;
+ linked_list_pos = 0;
+}
+
+void GsDrawList()
+{
+ if(PSX_GetInitFlags() & PSX_INIT_NOBIOS)
+ {
+// DMA is unreliable right now, use PIO.
+ GsDrawListPIO();
+ return;
+ }
+
+ //int x = 0;
+
+ /* Put a terminator, so the link listed ends. */
+ linked_list[linked_list_pos] = 0x00ffffff;
+
+// do{printf("linked_list[%d] = %08x\n", x, linked_list[x]);}while(linked_list[x++]!=0xffffff);
+
+ //#warning "Let's hope this works well."
+
+ while(!(GPU_CONTROL_PORT & (1<<0x1a))); /* Wait for the GPU to finish
+ * drawing primitives. */
+ while(!(GPU_CONTROL_PORT & (1<<0x1c))); /* Wait for the GPU to be free */
+
+ gpu_ctrl(4, 2); // DMA CPU->GPU mode
+ D2_MADR = (unsigned int)linked_list;
+ D2_BCR = 0;
+ D2_CHCR = (1<<0xa)|1|(1<<0x18);
+
+ linked_list_pos = 0;
+
+ //if(PSX_GetInitFlags() & PSX_INIT_NOBIOS)
+ // __psxsdk_gpu_dma_finished = 0;
+
+ if(__gs_autowait)
+ while(GsIsDrawing());
+}
+
+void GsDrawListPIO()
+{
+ //linked_list[linked_list_pos] = 0x00ffffff;
+ int pos = 0;
+ int sz = 0;
+ int x;
+
+ while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+ // Disable DMA
+ GPU_CONTROL_PORT = 0x04000000;
+
+
+ while(pos < linked_list_pos)
+ {
+ while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+
+ GPU_DATA_PORT = 0x01000000; // Reset data port
+
+ sz = linked_list[pos++] >> 24;
+
+ for(x = 0; x < sz; x++)
+ GPU_DATA_PORT = linked_list[pos++];
+ }
+
+ linked_list_pos = 0;
+// GPU_DATA_PORT = 0xE6000000; // Disable masking stuff
+// gpu_data_ctrl(2, ((b&0xff)<<16)|((g&0xff)<<8)|r);
+// GPU_DATA_PORT = (y<<16)|x;
+// GPU_DATA_PORT = (h<<16)|w;
+ if(__gs_autowait)
+ while(GsIsDrawing());
+}
+
+void GsSortPoly3(GsPoly3 *poly3)
+{
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x20;
+ unsigned int md;
+
+ md = setup_attribs(0, poly3->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x05000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(poly3->b<<16)|(poly3->g<<8)|(poly3->r);
+
+ for(x = 0; x < 3; x++)
+ linked_list[linked_list_pos++] = ((poly3->y[x]&0x7ff)<<16)|(poly3->x[x]&0x7ff);
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortPoly4(GsPoly4 *poly4)
+{
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x28;
+ unsigned int md;
+
+ md = setup_attribs(0, poly4->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x06000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(poly4->b<<16)|(poly4->g<<8)|(poly4->r);
+
+ for(x = 0; x < 4; x++)
+ linked_list[linked_list_pos++] = ((poly4->y[x]&0x7ff)<<16)|(poly4->x[x]&0x7ff);
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortGPoly3(GsGPoly3 *poly3)
+{
+ // PKT 0x30
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x30;
+ unsigned int md;
+
+ md = setup_attribs(0, poly3->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x07000000;
+ linked_list[linked_list_pos++] = md;
+
+ for(x = 0; x < 3; x++)
+ {
+ linked_list[linked_list_pos++] = (poly3->b[x]<<16)|(poly3->g[x]<<8)|(poly3->r[x]) | ((x == 0)?(pkt<<24):0);
+ linked_list[linked_list_pos++] = ((poly3->y[x]&0x7ff)<<16)|(poly3->x[x]&0x7ff);
+ }
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortGPoly4(GsGPoly4 *poly4)
+{
+ // PKT 0x38
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x38;
+ unsigned int md;
+
+ md = setup_attribs(0, poly4->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x09000000;
+ linked_list[linked_list_pos++] = md;
+
+ for(x = 0; x < 4; x++)
+ {
+ linked_list[linked_list_pos++] = (poly4->b[x]<<16)|(poly4->g[x]<<8)|(poly4->r[x]) | ((x == 0)?(pkt<<24):0);
+ linked_list[linked_list_pos++] = ((poly4->y[x]&0x7ff)<<16)|(poly4->x[x]&0x7ff);
+ }
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortLine(GsLine *line)
+{
+ // PKT 0x40
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x40;
+ unsigned int md;
+
+ md = setup_attribs(0, line->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x04000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(line->b<<16)|(line->g<<8)|(line->r);
+
+ for(x = 0; x < 2; x++)
+ linked_list[linked_list_pos++] = ((line->y[x]&0x7ff)<<16)|(line->x[x]&0x7ff);
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortGLine(GsGLine *line)
+{
+ // PKT 0x50
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x50;
+ unsigned int md;
+
+ md = setup_attribs(0, line->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x05000000;
+ linked_list[linked_list_pos++] = md;
+
+ for(x=0;x<2;x++)
+ {
+ linked_list[linked_list_pos++] = (line->b[x]<<16)|(line->g[x]<<8)|(line->r[x])|((x == 0)?(pkt<<24):0);
+ linked_list[linked_list_pos++] = ((line->y[x]&0x7ff)<<16)|(line->x[x] & 0x7ff);
+ }
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortDot(GsDot *dot)
+{
+ // PKT 0x68
+
+ int orig_pos = linked_list_pos;
+ unsigned char pkt = 0x68;
+ unsigned int md;
+
+ md = setup_attribs(0, dot->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x03000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(dot->b<<16)|(dot->g<<8)|(dot->r);
+ linked_list[linked_list_pos++] = ((dot->y&0x7ff)<<16)|(dot->x&0x7ff);
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortSprite(GsSprite *sprite)
+{
+ GsTPoly4 tpoly4;
+ int x, y;
+ int sx = sprite->x & 0x7ff;
+ int sy = sprite->y & 0x7ff;
+ int mcx, mcy;
+
+ /*if(sprite->w > 256)
+ sprite->w = 256;
+
+ if(sprite->h > 256)
+ sprite->h = 256;*/
+
+ // If "sprite" has no flipping and no scaling use sprite primitive
+ // otherwise manipulate a 4 point textured polygon primitive
+
+ if(sprite->rotate != 0)
+ {
+ tpoly4.u[0] = sprite->u;
+ tpoly4.v[0] = sprite->v;
+
+ tpoly4.u[1] = sprite->u;
+ tpoly4.v[1] = sprite->v + sprite->h;
+
+ tpoly4.u[2] = sprite->u + sprite->w;
+ tpoly4.v[2] = sprite->v;
+
+ tpoly4.u[3] = sprite->u + sprite->w;
+ tpoly4.v[3] = sprite->v + sprite->h;
+
+ gs_vbuf[0][2] = gs_vbuf[1][2] = gs_vbuf[2][2] = gs_vbuf[3][2] = 0;
+
+ mcx = sprite->mx + sprite->x;
+ mcy = sprite->my + sprite->y;
+
+ gs_vbuf[0][0] = -(mcx - sprite->x);
+ gs_vbuf[0][1] = (mcy - sprite->y);
+
+ gs_vbuf[1][0] = -(mcx - sprite->x);
+ gs_vbuf[1][1] = (mcy - (sprite->y + gs_calculate_scaled_size(sprite->h, sprite->scaley)));
+
+ gs_vbuf[2][0] = -(mcx - (sprite->x + gs_calculate_scaled_size(sprite->w, sprite->scalex)));
+ gs_vbuf[2][1] = (mcy - sprite->y);
+
+ gs_vbuf[3][0] = -(mcx - (sprite->x + gs_calculate_scaled_size(sprite->w, sprite->scalex)));
+ gs_vbuf[3][1] = (mcy - (sprite->y + gs_calculate_scaled_size(sprite->h, sprite->scaley)));
+
+ for(x = 0; x < 4; x++)
+ {
+ gs_internal_vector_rotate(0, 0, sprite->rotate, gs_vbuf[x], gs_vbuf[x]);
+ tpoly4.x[x] = mcx + gs_vbuf[x][0];
+ tpoly4.y[x] = mcy + gs_vbuf[x][1];
+ }
+
+ tpoly4.r = sprite->r;
+ tpoly4.g = sprite->g;
+ tpoly4.b = sprite->b;
+ tpoly4.attribute = sprite->attribute;
+ tpoly4.tpage = sprite->tpage;
+ tpoly4.cx = sprite->cx;
+ tpoly4.cy = sprite->cy;
+
+ GsSortTPoly4(&tpoly4);
+ }
+ else if((sprite->attribute & (H_FLIP|V_FLIP)) ||
+ sprite->scalex != 0 || sprite->scaley != 0)
+ {
+ x = sprite->w;
+ if(x>256)x=256;
+
+ y = sprite->h;
+ if(y>256)y=256;
+
+ if(sprite->scalex > 8)
+ {
+ x *= sprite->scalex;
+ x /= 4096;
+ }
+ else
+ {
+ if(sprite->scalex >= 2)
+ x*=sprite->scalex;
+ else if(sprite->scalex <= -2)
+ x/=-sprite->scalex;
+ }
+
+ if(sprite->scaley > 8)
+ {
+ y *= sprite->scaley;
+ y /= 4096;
+ }
+ else
+ {
+ if(sprite->scaley >= 2)
+ y*=sprite->scaley;
+ else if(sprite->scaley <= -2)
+ y/=-sprite->scaley;
+ }
+
+ tpoly4.x[0] = tpoly4.x[1] = sx;
+ tpoly4.x[2] = tpoly4.x[3] = (sx + x);
+ tpoly4.y[0] = tpoly4.y[2] = sy;
+ tpoly4.y[1] = tpoly4.y[3] = (sy + y);
+
+ if(sprite->attribute & H_FLIP)
+ {
+ tpoly4.u[0] = tpoly4.u[1] = (sprite->u + sprite->w) - 1;
+ tpoly4.u[2] = tpoly4.u[3] = sprite->u;
+ }
+ else
+ {
+ tpoly4.u[0] = tpoly4.u[1] = sprite->u;
+ tpoly4.u[2] = tpoly4.u[3] = (sprite->u + sprite->w);
+ }
+
+ if(sprite->attribute & V_FLIP)
+ {
+ tpoly4.v[0] = tpoly4.v[2] = (sprite->v + sprite->h) - 1;
+ tpoly4.v[1] = tpoly4.v[3] = sprite->v;
+ }
+ else
+ {
+ tpoly4.v[0] = tpoly4.v[2] = sprite->v;
+ tpoly4.v[1] = tpoly4.v[3] = (sprite->v + sprite->h);
+ }
+
+ tpoly4.r = sprite->r;
+ tpoly4.g = sprite->g;
+ tpoly4.b = sprite->b;
+ tpoly4.attribute = sprite->attribute;
+ tpoly4.tpage = sprite->tpage;
+ tpoly4.cx = sprite->cx;
+ tpoly4.cy = sprite->cy;
+
+ GsSortTPoly4(&tpoly4);
+ }
+ else
+ {
+ GsSortSimpleSprite(sprite);
+ }
+}
+
+void GsSortSimpleSprite(GsSprite *sprite)
+{
+ unsigned int orig_pos = linked_list_pos;
+ unsigned char pkt = 0x64;
+ unsigned int md;
+
+ md = setup_attribs(sprite->tpage, sprite->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x05000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(sprite->b<<16)|(sprite->g<<8)|sprite->r;
+ linked_list[linked_list_pos++] = ((sprite->y&0x7ff)<<16)|(sprite->x&0x7ff);
+ linked_list[linked_list_pos++] = (get_clutid(sprite->cx,sprite->cy)<<16)|(sprite->v<<8)|sprite->u;
+ linked_list[linked_list_pos++] = (sprite->h<<16)|sprite->w;
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortRectangle(GsRectangle *rectangle)
+{
+ unsigned int orig_pos = linked_list_pos;
+ unsigned char pkt = 0x60;
+ unsigned int md;
+
+ md = setup_attribs(0, rectangle->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x04000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(rectangle->b<<16)|(rectangle->g<<8)|(rectangle->r);
+ linked_list[linked_list_pos++] = ((rectangle->y&0x7ff)<<16)|(rectangle->x&0x7ff);
+ linked_list[linked_list_pos++] = (rectangle->h<<16)|rectangle->w;
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortTPoly4(GsTPoly4 *tpoly4)
+{
+ unsigned int orig_pos = linked_list_pos;
+ unsigned char pkt = 0x2c;
+ unsigned int md;
+
+ /*md = setup_attribs(tpoly4->tpage, tpoly4->attribute, &pkt);*/
+
+ //printf("tpoly4->tpage = %d\n", tpoly4->tpage);
+
+ md = setup_attribs(tpoly4->tpage, tpoly4->attribute, &pkt);
+
+ //printf("pkt = %x\n", pkt);
+
+ linked_list[linked_list_pos++] = 0x09000000;
+ //linked_list[linked_list_pos++] = md;
+ //linked_list[linked_list_pos++] = 0xe0000000;
+ //linked_list[linked_list_pos++] = 0xe1000105;
+
+ //printf("tpoly4 md: %08x\n", md);
+ linked_list[linked_list_pos++] = (pkt<<24)|(tpoly4->b<<16)|(tpoly4->g<<8)|(tpoly4->r);
+ linked_list[linked_list_pos++] = ((tpoly4->y[0]&0x7ff)<<16)|(tpoly4->x[0]&0x7ff);
+ linked_list[linked_list_pos++] = (get_clutid(tpoly4->cx, tpoly4->cy)<<16)|(tpoly4->v[0]<<8)|tpoly4->u[0];
+ linked_list[linked_list_pos++] = ((tpoly4->y[1]&0x7ff)<<16)|(tpoly4->x[1]&0x7ff);
+ linked_list[linked_list_pos++] = (md << 16)|(tpoly4->v[1]<<8)|tpoly4->u[1];
+ linked_list[linked_list_pos++] = ((tpoly4->y[2]&0x7ff)<<16)|(tpoly4->x[2]&0x7ff);
+ linked_list[linked_list_pos++] = (tpoly4->v[2]<<8)|tpoly4->u[2];
+ linked_list[linked_list_pos++] = ((tpoly4->y[3]&0x7ff)<<16)|(tpoly4->x[3]&0x7ff);
+ linked_list[linked_list_pos++] = (tpoly4->v[3]<<8)|tpoly4->u[3];
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortTPoly3(GsTPoly3 *tpoly3)
+{
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x24;
+ unsigned int md;
+
+ md = setup_attribs(tpoly3->tpage, tpoly3->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x07000000;
+ linked_list[linked_list_pos++] =
+ (pkt<<24)|(tpoly3->b<<16)|(tpoly3->g<<8)|(tpoly3->r);
+
+ for(x = 0; x < 3; x++)
+ {
+ linked_list[linked_list_pos++] = ((tpoly3->y[x]&0x7ff)<<16)|(tpoly3->x[x]&0x7ff);
+ linked_list[linked_list_pos] = (tpoly3->u[x]<<8)|tpoly3->v[x];
+
+ switch(x)
+ {
+ case 0:
+ linked_list[linked_list_pos++] |=
+ get_clutid(tpoly3->cx, tpoly3->cy) << 16;
+ break;
+ case 1:
+ linked_list[linked_list_pos++] |=
+ md << 16;
+ break;
+ default:
+ linked_list_pos++;
+ break;
+ }
+ }
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void MoveImage(int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ /*
+ * This seems more like "CopyImage"...
+ */
+
+ while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+
+ GPU_CONTROL_PORT = 0x04000000;
+ GPU_DATA_PORT = 0x01000000; // Reset command buffer
+ GPU_DATA_PORT = 0xE6000000;
+ GPU_DATA_PORT = 0x80000000;
+ GPU_DATA_PORT = (src_y<<16)|src_x;
+ GPU_DATA_PORT = (dst_y<<16)|dst_x;
+ GPU_DATA_PORT = (h<<16)|w;
+}
+
+/*
+ * Add a method to add arbitrary data to the packet list
+ */
+
+void LoadImage(void *img, int x, int y, int w, int h)
+{
+ unsigned short *image = (unsigned short*)img;
+ int a, l;
+
+ //printf("LoadImage: %d, %d, %d, %d\n", x, y, w, h);
+
+ while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+
+ GPU_CONTROL_PORT = 0x04000000; // Disable DMA
+
+// Reset should be on data port ! otherwise we won't be able
+// to write CLUTs for some time after they've been used!
+// (why??)
+
+ GPU_DATA_PORT = 0x01000000;
+ GPU_DATA_PORT = 0xE6000000; // disable masking stuff !!
+ GPU_DATA_PORT = 0xA0000000;
+ GPU_DATA_PORT = (y<<16)|x;
+ GPU_DATA_PORT = (h<<16)|w;
+
+ l = w*h;
+ if(l&1)l++;
+
+ for(a = 0; a < l; a+=2)
+ GPU_DATA_PORT = image[a]|(image[a+1]<<16);
+
+ GPU_CONTROL_PORT = 0x01000000;
+// while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+}
+
+/*void LoadImage(void *img, int x, int y, int w, int h)
+{
+ GPU_dw(x, y, w, h, img);
+
+ int l;
+
+ printf("LoadImage: %d, %d, %d, %d\n", x, y, w, h);
+
+ l = w*h;
+ if(l&1)l++;
+ l/=2;
+
+ while(!(GPU_CONTROL_PORT & (1<<0x1c))); // Wait for the GPU to be free
+
+ gpu_ctrl(4, 2); // DMA CPU->GPU mode
+ D2_MADR = (unsigned int)img;
+ D2_BCR = (l << 16) | 1;
+ D2_CHCR = 0x01000201;
+
+ // Wait for DMA to finish
+
+ while(D2_CHCR & (1<<0x18));
+//}*/
+
+void GsSetDrawEnv(GsDrawEnv *drawenv)
+{
+ int end_y, end_x;
+ int mf;
+
+ /*
+ * Store the 0xe1 packet - we need it because we have to
+ * modify drawing environment for sprites
+ */
+
+ draw_mode_packet = (0xe1<<24)|(drawenv->draw_on_display>=1)<<10|
+ (drawenv->dither>=1)<<9;
+
+ gpu_data_ctrl(0xe1, draw_mode_packet);
+ gpu_data_ctrl(0xe2, 0);
+ gpu_data_ctrl(0xe3, (drawenv->y<<10)|drawenv->x);
+
+ end_x = (drawenv->x + drawenv->w)-1;
+ end_y = (drawenv->y + drawenv->h)-1;
+
+ gpu_data_ctrl(0xe4, (end_y<<10)|end_x);
+
+ //#warning "Check drawing offset better."
+ gpu_data_ctrl(0xe5, (drawenv->y<<11)|drawenv->x);
+ //gpu_data_ctrl(0xe5, 0);
+
+
+ mf = 0;
+ if(drawenv->set_mask) mf|=MASK_SET;
+ if(drawenv->ignore_mask) mf|=MASK_IGNORE;
+
+ GsSetMasking(mf);
+
+ GsCurDrawEnvW = drawenv->w;
+ GsCurDrawEnvH = drawenv->h;
+}
+
+void GsSetDispEnv(GsDispEnv *dispenv)
+{
+ gpu_ctrl(5, (dispenv->y<<10)|dispenv->x); // Display offset
+}
+
+void gpu_ctrl(unsigned int command, unsigned int param)
+{
+ unsigned int doubleword = (command << 0x18) | param;
+
+ GPU_CONTROL_PORT = 0x01000000;
+ GPU_CONTROL_PORT = doubleword;
+}
+
+void gpu_data(unsigned int data)
+{
+ GPU_DATA_PORT = data;
+}
+
+void gpu_data_ctrl(unsigned int command, unsigned int param)
+{
+ unsigned int doubleword = (command << 0x18) | param;
+
+ GPU_CONTROL_PORT = 0x01000000;
+ GPU_DATA_PORT = doubleword;
+}
+
+unsigned int setup_attribs(unsigned char tpage, unsigned int attribute, unsigned char *packet)
+{
+ unsigned int sprite_mode_packet;
+
+ //printf("tpage = %d, attribute = %x, packet = %x\n", tpage, attribute, packet);
+ //while(1);*/
+
+/*
+ * First, setup draw mode setting.
+ */
+
+ sprite_mode_packet = draw_mode_packet;
+ sprite_mode_packet|= tpage & 0x1f; /* Texture page */
+ sprite_mode_packet|= (attribute & 3) << 7; /* Color mode */
+ sprite_mode_packet|= ((attribute>>2)&3) << 5; /* Translucency mode */
+
+/*
+ * Check for STP bit flag in attribute, and modify packet byte accordingly
+ */
+ if(attribute & 16)
+ *packet|=2;
+
+ //printf("sprite_mode_packet = %08x\n", sprite_mode_packet);
+
+ return sprite_mode_packet;
+}
+
+unsigned int GsListPos()
+{
+ return linked_list_pos;
+}
+
+void GsEnableDisplay(int enable)
+{
+ gpu_ctrl(3, enable ? 0 : 1);
+}
+
+void GsReset()
+{
+ gpu_ctrl(0, 0); // Reset GPU
+}
+
+void GsInitEx(unsigned int flags)
+{
+ //gpu_ctrl(0, 0); // Reset GPU
+ GsReset(); // Reset GPU
+
+ DPCR |= (1<<0xb); // Enable dma channel 2
+ gpu_ctrl(4, 2); // DMA CPU->GPU mode
+
+ //gpu_ctrl(3, 1); // Disable display
+ GsEnableDisplay(0); // Disable display
+
+ GPU_DATA_PORT = 0x01000000; // Reset data port
+
+ /*gpu_ctrl(6, 0xc40240); // Horizontal start end
+ gpu_ctrl(7, 0x049025); // Vertical start end*/
+ //DrawFBRect(0, 0, 1023, 511, 0, 0, 0);
+}
+
+void GsInit()
+{
+ GsInitEx(0);
+}
+
+/*void SetVBlankHandler2(void *(callback)())
+{
+ unsigned int eventid;
+
+ EnterCriticalSection();
+
+ IMASK|=8; // Enable VBLANK interrupt
+ eventid = openevent(0xf0000001, 2, 0x1000, vblank_handler);
+
+ if(eventid == -1)
+ {
+ printf("SetVBlankHandler: Failed to open event!\n");
+ return;
+ }
+ else
+ printf("SetVBlankHandler: Event opened successfully!\n");
+
+ if(enableevent(eventid) == 0)
+ {
+ printf("SetVBlankHandler: Failed to enable event!\n");
+ // Shouldn't we close the event as well?
+ return;
+ }
+ else
+ printf("SetVBlankHandler: Event enabled successfully!\n");
+
+ ExitCriticalSection();
+}*/
+
+int GsSetVideoMode(int width, int height, int video_mode)
+{
+ // Just a quick wrapper for GsSetVideoModeEx
+ return GsSetVideoModeEx(width, height, video_mode, 0, 0, 0);
+}
+
+int GsSetVideoModeEx(int width, int height, int video_mode, int rgb24,
+ int inter, int reverse)
+{
+ unsigned char mode = 0;
+
+ GsEnableDisplay(0);
+
+ if(video_mode == VMODE_NTSC)
+ {
+ gpu_ctrl(6, 0xC4E24E); // Horizontal screen range
+ gpu_ctrl(7, 0x040010); // Vertical screen range
+ }
+ else
+ {
+ gpu_ctrl(6, 0xC62262); // Horizontal screen range
+ gpu_ctrl(7, 0x04B42D); // Vertical screen range
+ }
+
+ switch(height)
+ {
+ case 240:
+ break;
+ case 480:
+ mode|=4;
+ break;
+ default:
+ printf("%s: error, unknown width %d!\n", __FUNCTION__, width);
+ return 0;
+ }
+
+ switch(width)
+ {
+ case 256:
+ break;
+ case 320:
+ mode|=1;
+ break;
+ case 512:
+ mode|=2;
+ break;
+ case 640:
+ mode|=3;
+ break;
+ case 384:
+ mode|=64;
+ break;
+ default:
+ printf("%s: error, unknown height %d!\n", __FUNCTION__, height);
+ return 0;
+ }
+
+ if(video_mode)mode|=8; // Set PAL
+ if(rgb24)mode|=16; // Set unaccellerated 24-bit mode
+ if(inter)mode|=32; // Set interlaced video mode
+ if(reverse)mode|=128; // Set reverse flag (?)
+
+ gpu_ctrl(8, mode);
+ GsEnableDisplay(1);
+
+ GsScreenW = width;
+ GsScreenH = height;
+ GsScreenM = video_mode;
+
+ return 1;
+}
+
+void DrawFBRect(int x, int y, int w, int h, int r, int g, int b)
+{
+ while(!(GPU_CONTROL_PORT & (1<<0x1c)));
+
+ // Disable DMA
+ GPU_CONTROL_PORT = 0x04000000;
+
+ GPU_DATA_PORT = 0x01000000; // Reset data port
+ GPU_DATA_PORT = 0xE6000000; // Disable masking stuff
+ gpu_data_ctrl(2, ((b&0xff)<<16)|((g&0xff)<<8)|r);
+ GPU_DATA_PORT = (y<<16)|x;
+ GPU_DATA_PORT = (h<<16)|w;
+}
+
+void GsClearMem()
+{
+ // "Clears" the entire video memory by using DrawFBRect
+ // and waits that it has finished drawing...
+
+ DrawFBRect(0,0,1023,511,0,0,0);
+ while(GsIsDrawing());
+ DrawFBRect(0,511,1023,1,0,0,0);
+ while(GsIsDrawing());
+ DrawFBRect(1023,511,1,1,0,0,0);
+ while(GsIsDrawing());
+}
+
+int GsImageFromTim(GsImage *image, void *timdata)
+{
+ unsigned int *timdata_i = (unsigned int*)timdata;
+ unsigned short *timdata_s = (unsigned short*)timdata;
+ unsigned int pdata_pos;
+ unsigned int pdata_pos_s;
+
+ //printf("timdata_i[0] = %08x\n", timdata_i[0]);
+
+ if(timdata_i[0] != 0x10)
+ {
+ //printf("timdata_i[0] = %08x\n", timdata_i[0]);
+ return 0; // Unknown version or ID
+ }
+
+ image->pmode = timdata_i[1] & 7;
+
+ //printf("image->pmode = %d\n", image->pmode);
+
+ image->has_clut = (timdata_i[1] & 8) ? 1 : 0;
+
+ if(!image->has_clut)
+ pdata_pos = 8;
+ else
+ {
+ pdata_pos = 8 + timdata_i[2];
+ image->clut_x = timdata_s[6];
+ image->clut_y = timdata_s[7];
+ image->clut_w = timdata_s[8];
+ image->clut_h = timdata_s[9];
+ image->clut_data = &timdata_s[10];
+
+ /*printf("image->clut_y = %d\n", image->clut_y);
+ printf("image->clut_x = %d\n", image->clut_x);
+ printf("image->clut_h = %d\n", image->clut_h);
+ printf("image->clut_w = %d\n", image->clut_w);*/
+ }
+
+ pdata_pos_s = pdata_pos / 2;
+
+ image->x = timdata_s[pdata_pos_s + 2];
+ image->y = timdata_s[pdata_pos_s + 3];
+ image->w = timdata_s[pdata_pos_s + 4];
+ image->h = timdata_s[pdata_pos_s + 5];
+ image->data = &timdata_s[pdata_pos_s + 6];
+
+ /*printf("image->y = %d\n", image->y);
+ printf("image->x = %d\n", image->x);
+ printf("image->h = %d\n", image->h);
+ printf("image->w = %d\n", image->w);*/
+
+ return 1;
+}
+
+void GsUploadImage(GsImage *image)
+{
+ if(image->has_clut)
+ LoadImage(image->clut_data, image->clut_x, image->clut_y,
+ image->clut_w, image->clut_h);
+
+ LoadImage(image->data, image->x, image->y, image->w, image->h);
+}
+
+int GsSpriteFromImage(GsSprite *sprite, GsImage *image, int do_upload)
+{
+ if(do_upload)
+ GsUploadImage(image);
+
+ bzero(sprite, sizeof(GsSprite));
+
+ sprite->tpage = (image->x / 64) + ((image->y/256)*16);
+ sprite->u = image->x & 0x3f;
+ sprite->v = image->y & 0xff;
+
+ sprite->cx = image->clut_x;
+ sprite->cy = image->clut_y;
+
+ if(image->pmode == 0) // 4-bit pixel mode
+ sprite->u*=4;
+ else if(image->pmode == 1) // 8-bit pixel mode
+ sprite->u*=2;
+
+ switch(image->pmode)
+ {
+ case 0:
+ sprite->w = image->w * 4;
+ break;
+ case 1:
+ sprite->w = image->w * 2;
+ break;
+ case 2:
+ sprite->w = image->w;
+ break;
+ case 3:
+ sprite->w = image->w + (image->w / 2);
+ break;
+ }
+
+ sprite->h = image->h;
+ sprite->attribute = COLORMODE(image->pmode);
+ sprite->r = sprite->g = sprite->b = NORMAL_LUMINANCE;
+
+ return 1;
+}
+
+void GsSetMasking(unsigned char flag)
+{
+ gpu_data_ctrl(0xe6, flag);
+}
+
+int GsIsDrawing()
+{
+ /*int x;
+
+ if(PSX_GetInitFlags() & PSX_INIT_NOBIOS)
+ {
+ int r = (!(GPU_CONTROL_PORT & (1<<0x1a))) || (!__psxsdk_gpu_dma_finished);
+
+ for(x = 0; x < 1000; x++);
+
+ return r;
+ }*/
+
+ return !(GPU_CONTROL_PORT & (1<<0x1a)) ;
+}
+
+
+
+// Functions which use default values to use when you do not
+// really need to fiddle with all the fields of the structure
+
+void GsSetDrawEnvSimple(int x, int y, int w, int h)
+{
+ GsDrawEnv env;
+
+ env.dither = 0;
+ env.draw_on_display = 1;
+ env.x = x;
+ env.y = y;
+ env.w = w;
+ env.h = h;
+ env.ignore_mask = 0;
+ env.set_mask = 0;
+
+ GsSetDrawEnv(&env);
+}
+
+void GsSetDispEnvSimple(int x, int y)
+{
+ GsDispEnv env;
+
+ env.x = x;
+ env.y = y;
+
+ GsSetDispEnv(&env);
+}
+
+// Built-in font functions.
+
+void GsLoadFont(int fb_x, int fb_y, int cx, int cy)
+{
+ unsigned short pal[2] = {0x0, 0x7fff};
+
+ LoadImage(psxsdk_font_data, fb_x, fb_y, 16, 128);
+ while(GsIsDrawing());
+
+ if(cx != -1 && cy != -1)
+ {
+ LoadImage(pal, cx, cy, 16, 1);
+
+ fb_font_cx = cx;
+ fb_font_cy = cy;
+
+ while(GsIsDrawing());
+ }
+
+ fb_font_x = fb_x;
+ fb_font_y = fb_y;
+}
+
+unsigned int GsPrintFont_Draw(int x, int y, int scalex, int scaley)
+{
+ //int r;
+ GsSprite spr;
+ char *string;
+ int fw, fh;
+
+ /*va_list ap;
+
+ va_start(ap, fmt);*/
+
+// r = vsnprintf(gpu_stringbuf, 512, fmt, ap);
+
+// va_end(ap);
+ fw = gs_calculate_scaled_size(8, scalex);//(8*scalex)/4096;
+ fh = gs_calculate_scaled_size(8, scaley);//(8*scaley)/4096;
+
+ spr.x = x;
+ spr.y = y;
+ spr.r = prfont_rl;
+ spr.g = prfont_gl;
+ spr.b = prfont_bl;
+ spr.attribute = 0;
+ spr.cx = fb_font_cx;
+ spr.cy = fb_font_cy;
+ spr.tpage = (fb_font_x / 64) + ((fb_font_y / 256)*16);
+ spr.w = 8;
+ spr.h = 8;
+ spr.scalex = scalex;
+ spr.scaley = scaley;
+
+ string = gpu_stringbuf;
+
+ while(*string)
+ {
+ if(prfont_flags & PRFONT_WRAP)
+ {
+ if(spr.x >= GsScreenW)
+ {
+ spr.x = spr.x - GsScreenW;
+ spr.y += fh;
+ }
+ }
+
+ if(*string >= ' ' && *string <= '~')
+ {
+ spr.u = ((fb_font_x & 0x3f)*4)+((*string & 7) << 3);
+ spr.v = (fb_font_y & 0xff)+(*string & 0xf8);
+
+ if((spr.x < GsCurDrawEnvW && (spr.x+fw)>=0) &&
+ (spr.y < GsCurDrawEnvH && (spr.y+fh)>=0))
+ {
+
+ if((scalex == 0 || scalex == 1) && (scaley == 0 || scaley == 1))
+ GsSortSimpleSprite(&spr);
+ else
+ GsSortSprite(&spr);
+ }
+
+ spr.x += fw;
+ }
+
+ if(*string == '\r')
+ spr.x = 0;
+
+ if(*string == '\n')
+ {
+ spr.x = (prfont_flags & PRFONT_UNIXLF)? 0 : x;
+ spr.y += fh;
+ }
+
+ if(*string == '\t')
+ spr.x += fw * 8;
+
+ string++;
+ }
+
+ return (spr.y << 16) | spr.x;
+}
+
+unsigned int GsVPrintFont(int x, int y, const char *fmt, va_list ap)
+{
+ int r;
+ //GsSprite spr;
+ //char *string;
+ int fw = gs_calculate_scaled_size(8, prfont_scale_x);
+
+ r = vsnprintf(gpu_stringbuf, 512, fmt, ap);
+
+ if(prfont_flags & PRFONT_WRAP)
+ r = GsPrintFont_Draw(x, y, prfont_scale_x, prfont_scale_y);
+ else if(prfont_flags & PRFONT_CENTER)
+ r = GsPrintFont_Draw(x - ((r * fw)/2), y, prfont_scale_x, prfont_scale_y);
+ else if(prfont_flags & PRFONT_RIGHT)
+ r = GsPrintFont_Draw(x - (r * fw), y, prfont_scale_x, prfont_scale_y);
+ else
+ r = GsPrintFont_Draw(x, y, prfont_scale_x, prfont_scale_y);
+
+ return r;
+}
+
+unsigned int GsPrintFont(int x, int y, const char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = GsVPrintFont(x, y, fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
+
+void GsSetFont(int fb_x, int fb_y, int cx, int cy)
+{
+ if(fb_x != -1)
+ fb_font_x = fb_x;
+
+ if(fb_y != -1)
+ fb_font_y = fb_y;
+
+ if(fb_font_cx != -1)
+ fb_font_cx = cx;
+
+ if(fb_font_cy != -1)
+ fb_font_cy = cy;
+}
+
+void GsSetFontAttrib(unsigned int flags)
+{
+ prfont_flags = flags;
+
+ if(prfont_flags == 0)
+ {
+ PRFONT_SCALEX(0);
+ PRFONT_SCALEY(0);
+
+ PRFONT_RL(NORMAL_LUMINANCE);
+ PRFONT_GL(NORMAL_LUMINANCE);
+ PRFONT_BL(NORMAL_LUMINANCE);
+ }
+}
+
+static double gs_internal_cos(int a)
+{
+ int a_a = (a>>12)-(((a>>12)/360)*360);
+
+ if(a_a>=0 && a_a<=90)
+ return gs_rot_cos_tbl[a_a];
+ else if(a_a>90 && a_a<=180)
+ return -gs_rot_cos_tbl[180 - a_a];
+ else if(a_a>180 && a_a<=270)
+ return -gs_rot_cos_tbl[a_a - 180];
+ else if(a_a>270 && a_a<=359)
+ return gs_rot_cos_tbl[360 - a_a];
+
+ return 0;
+}
+
+static double gs_internal_sin(int a)
+{
+ int a_a = (a>>12)-(((a>>12)/360)*360);
+
+ if(a_a>=0 && a_a<=90)
+ return gs_rot_cos_tbl[90-a_a];
+ else if(a_a>90 && a_a<=180)
+ return gs_rot_cos_tbl[a_a-90];
+ else if(a_a>180 && a_a<=270)
+ return -gs_rot_cos_tbl[270-a_a];
+ else if(a_a>270 && a_a<=359)
+ return -gs_rot_cos_tbl[a_a-270];
+
+ return 0;
+}
+
+static void gs_internal_vector_rotate(int x_a, int y_a, int z_a, double *v, double *n)
+{
+ double axis_m[3][3];
+ double b[3];
+ double k[3], s[3];
+ int x;
+
+ k[0] = gs_internal_cos(x_a);
+ k[1] = gs_internal_cos(y_a);
+ k[2] = gs_internal_cos(z_a);
+
+ s[0] = gs_internal_sin(x_a);
+ s[1] = gs_internal_sin(y_a);
+ s[2] = gs_internal_sin(z_a);
+
+ axis_m[0][0] = k[1] * k[2];
+ axis_m[0][1] = (k[0] * s[2]) + (s[0]*s[1]*k[2]);
+ axis_m[0][2] = (s[0]*s[2]) - (k[0]*s[1]*k[2]);
+ axis_m[1][0] = -(k[1] * s[2]);
+ axis_m[1][1] = (k[0]*k[2]) - (s[0]*s[1]*s[2]);
+ axis_m[1][2] = (s[0]*k[2]) + (k[0]*s[1]*s[2]);
+ axis_m[2][0] = s[1];
+ axis_m[2][1] = -(s[0]*k[1]);
+ axis_m[2][2] = k[0]*k[1];
+
+ for(x=0;x<3;x++)
+ b[x] = (axis_m[x][0] * v[0]) + (axis_m[x][1] * v[1]) + (axis_m[x][2] * v[2]);
+
+ b[1]=-b[1];
+
+ for(x=0;x<3;x++)
+ n[x]=b[x];
+}
+
+int GsIsWorking()
+{
+ return GsIsDrawing();
+}
+
+void GsSortCls(int r, int g, int b)
+{
+ GsRectangle rect;
+
+ rect.r = r;
+ rect.g = g;
+ rect.b = b;
+ rect.x = 0;
+ rect.y = 0;
+ rect.attribute = 0;
+ rect.w = GsCurDrawEnvW;
+ rect.h = GsCurDrawEnvH;
+
+ GsSortRectangle(&rect);
+}
+
+void GsSetAutoWait()
+{
+ __gs_autowait = 1;
+}
+
+void GsRotateVector(int x_a, int y_a, int z_a, double *v, double *n)
+{
+ gs_internal_vector_rotate(x_a, y_a, z_a, v, n);
+}
+
+/*void GsSortSimpleMap(GsMap *map)
+{
+ unsigned int orig_pos = linked_list_pos;
+ //unsigned int
+ unsigned char pkt = 0x64;
+ unsigned int md;
+ unsigned char curCount = 0;
+ unsigned int remaining;
+ unsigned int tn;
+ unsigned short tu;
+ unsigned short tv;
+ int x, y;
+
+ md = setup_attribs(map->tpage, map->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x01000000;
+ linked_list[linked_list_pos++] = md;
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+
+ orig_pos = linked_list_pos;
+ linked_list[linked_list_pos++] = 0x00000000;
+
+ remaining = map->w * map->h;
+
+ for(y = 0; y < map->h; y++)
+ {
+ for(x = 0; x < map->w; x++)
+ {
+ switch(map->tsize)
+ {
+ case 1:
+ tn = ((unsigned char*)map->data)[(y * map->l) + x];
+ break;
+ case 2:
+ tn = ((unsigned short*)map->data)[(y * map->l) + x];
+ break;
+ case 4:
+ tn = ((unsigned int*)map->data)[(y * map->l) + x];
+ break;
+ }
+
+ tn &= ~map->tmask;
+
+ tu = (tn * map->tw) % map->tmw;
+ tv = ((tn * map->tw) / map->tmw) * map->th;
+
+ linked_list[linked_list_pos++] = (pkt<<24)|(map->b<<16)|(map->g<<8)|map->r;
+ linked_list[linked_list_pos++] = (((map->y+(y*map->th))&0x7ff)<<16)|((map->x+(x*map->tw))&0x7ff);
+ linked_list[linked_list_pos++] = (get_clutid(map->cx,map->cy)<<16)|((tv+map->v)<<8)|
+ (tu+map->u);
+ linked_list[linked_list_pos++] = (map->th<<16)|map->tw;
+
+ curCount++;
+
+ if(curCount == 252)
+ {
+ linked_list[orig_pos] = (252 << 24) | (((unsigned int)&linked_list[linked_list_pos]) & 0xffffff);
+ orig_pos = linked_list_pos;
+
+ remaining -= curCount;
+
+ if(remaining > 0)
+ linked_list_pos++;
+
+ curCount = 0;
+ }
+ }
+ }
+
+ if(curCount > 0)
+ linked_list[orig_pos] = (curCount << 24) | (((unsigned int)&linked_list[linked_list_pos]) & 0xffffff);
+}*/
+
+void GsSetListEx(unsigned int *listptr, unsigned int listpos)
+{
+ linked_list = listptr;
+ linked_list_pos = listpos;
+}
+
+void GsSortPolyLine(GsPolyLine *line)
+{
+ // PKT 0x48
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x48;
+ unsigned int md;
+
+ md = setup_attribs(0, line->attribute, &pkt);
+
+ linked_list_pos++; // skip this word, we will replace it later
+ linked_list[linked_list_pos++] = md;
+ linked_list[linked_list_pos++] = (pkt<<24)|(line->b<<16)|(line->g<<8)|(line->r);
+
+ for(x = 0; x < line->npoints; x++)
+ linked_list[linked_list_pos++] = ((line->y[x]&0x7ff)<<16)|(line->x[x]&0x7ff);
+
+ linked_list[linked_list_pos++] = 0x55555555; // termination code
+
+ linked_list[orig_pos] = ((line->npoints+3) << 24) | (((unsigned int)&linked_list[linked_list_pos]) & 0xffffff);
+}
+
+void GsSortGPolyLine(GsGPolyLine *line)
+{
+ // PKT 0x58
+
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x58;
+ unsigned int md;
+
+ md = setup_attribs(0, line->attribute, &pkt);
+
+ linked_list_pos++; // skip this word, we will replace it later
+ linked_list[linked_list_pos++] = md;
+
+ for(x=0; x < line->npoints;x++)
+ {
+ linked_list[linked_list_pos++] = (line->b[x]<<16)|(line->g[x]<<8)|(line->r[x])|((x == 0)?(pkt<<24):0);
+ linked_list[linked_list_pos++] = ((line->y[x]&0x7ff)<<16)|(line->x[x] & 0x7ff);
+ }
+
+ linked_list[linked_list_pos++] = 0x55555555; // termination code
+
+ linked_list[orig_pos] = (((line->npoints*2)+2) << 24) | (((unsigned int)&linked_list[linked_list_pos]) & 0xffffff);
+}
+
+void GsSortGTPoly4(GsGTPoly4 *tpoly4)
+{
+ unsigned int orig_pos = linked_list_pos;
+ unsigned char pkt = 0x3c;
+ unsigned int md;
+
+ /*md = setup_attribs(tpoly4->tpage, tpoly4->attribute, &pkt);*/
+
+ //printf("tpoly4->tpage = %d\n", tpoly4->tpage);
+
+ md = setup_attribs(tpoly4->tpage, tpoly4->attribute, &pkt);
+
+ //printf("pkt = %x\n", pkt);
+
+ linked_list[linked_list_pos++] = 0x0C000000;
+ //linked_list[linked_list_pos++] = md;
+ //linked_list[linked_list_pos++] = 0xe0000000;
+ //linked_list[linked_list_pos++] = 0xe1000105;
+
+ //printf("tpoly4 md: %08x\n", md);
+ linked_list[linked_list_pos++] = (pkt<<24)|(tpoly4->b[0]<<16)|(tpoly4->g[0]<<8)|(tpoly4->r[0]);
+ linked_list[linked_list_pos++] = ((tpoly4->y[0]&0x7ff)<<16)|(tpoly4->x[0]&0x7ff);
+ linked_list[linked_list_pos++] = (get_clutid(tpoly4->cx, tpoly4->cy)<<16)|(tpoly4->v[0]<<8)|tpoly4->u[0];
+ linked_list[linked_list_pos++] = (tpoly4->b[1]<<16)|(tpoly4->g[1]<<8)|tpoly4->r[1];
+ linked_list[linked_list_pos++] = ((tpoly4->y[1]&0x7ff)<<16)|(tpoly4->x[1]&0x7ff);
+ linked_list[linked_list_pos++] = (md << 16)|(tpoly4->v[1]<<8)|tpoly4->u[1];
+ linked_list[linked_list_pos++] = (tpoly4->b[1]<<16)|(tpoly4->g[1]<<8)|tpoly4->r[1];
+ linked_list[linked_list_pos++] = ((tpoly4->y[2]&0x7ff)<<16)|(tpoly4->x[2]&0x7ff);
+ linked_list[linked_list_pos++] = (tpoly4->v[2]<<8)|tpoly4->u[2];
+ linked_list[linked_list_pos++] = (tpoly4->b[2]<<16)|(tpoly4->g[2]<<8)|tpoly4->r[2];
+ linked_list[linked_list_pos++] = ((tpoly4->y[3]&0x7ff)<<16)|(tpoly4->x[3]&0x7ff);
+ linked_list[linked_list_pos++] = (tpoly4->v[3]<<8)|tpoly4->u[3];
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
+
+void GsSortGTPoly3(GsGTPoly3 *tpoly3)
+{
+ int orig_pos = linked_list_pos;
+ int x;
+ unsigned char pkt = 0x34;
+ unsigned int md;
+
+ md = setup_attribs(tpoly3->tpage, tpoly3->attribute, &pkt);
+
+ linked_list[linked_list_pos++] = 0x09000000;
+
+ for(x = 0; x < 3; x++)
+ {
+ linked_list[linked_list_pos++] =
+ ((x==0)?(pkt<<24):0)|(tpoly3->b[x]<<16)|(tpoly3->g[x]<<8)|(tpoly3->r[x]);
+ linked_list[linked_list_pos++] = ((tpoly3->y[x]&0x7ff)<<16)|(tpoly3->x[x]&0x7ff);
+ linked_list[linked_list_pos] = (tpoly3->u[x]<<8)|tpoly3->v[x];
+
+ switch(x)
+ {
+ case 0:
+ linked_list[linked_list_pos++] |=
+ get_clutid(tpoly3->cx, tpoly3->cy) << 16;
+ break;
+ case 1:
+ linked_list[linked_list_pos++] |=
+ md << 16;
+ break;
+ default:
+ linked_list_pos++;
+ }
+ }
+
+ linked_list[orig_pos] |= ((unsigned int)&linked_list[linked_list_pos]) & 0xffffff;
+}
diff --git a/libpsx/src/libc.c b/libpsx/src/libc.c
new file mode 100644
index 0000000..8ad519b
--- /dev/null
+++ b/libpsx/src/libc.c
@@ -0,0 +1,705 @@
+/*
+ * small libc functions for the PSXSDK
+ *
+ * In this file, C functions either not implemented by the BIOS
+ * or very broken in the BIOS are implemented independently
+ *
+ * Some functions, such as printf, have both a BIOS implementation
+ * and an implementation here. The implementation here is prefixed
+ * with libc_ to not make confusion.
+ */
+
+#include <psx.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+char onesec_buf[2048];
+int errno;
+int __stdio_direction = STDIO_DIRECTION_BIOS;
+static unsigned int __sio_cr_mapped = 0;
+
+#define NUM_OF_FILE_STRUCTS 259
+
+FILE file_structs[NUM_OF_FILE_STRUCTS] =
+{
+ [0] = // stdin
+ {
+ .fildes = 0,
+ .pos = 0,
+ .mode = O_RDONLY,
+ .dev = FDEV_CONSOLE,
+ .size = 0,
+ .used = 1,
+ .eof = 0,
+ .error = 0
+ },
+ [1] = // stdout
+ {
+ .fildes = 1,
+ .pos = 0,
+ .mode = O_WRONLY,
+ .dev = FDEV_CONSOLE,
+ .size = 0,
+ .used = 1,
+ .eof = 0,
+ .error = 0
+ },
+ [2] = // stderr
+ {
+ .fildes = 2,
+ .pos = 0,
+ .mode = O_WRONLY,
+ .dev = FDEV_CONSOLE,
+ .size = 0,
+ .used = 1,
+ .eof = 0,
+ .error = 0
+ },
+};
+
+FILE *stdin = &file_structs[0];
+FILE *stdout = &file_structs[1];
+FILE *stderr = &file_structs[2];
+
+#define IS_CONS_IN(f) (f->fildes == 0)
+#define IS_CONS_OUT(f) (f->fildes == 1 || f->fildes == 2)
+
+//unsigned char file_state[NUM_OF_FILE_STRUCTS];
+
+int libc_get_transtbl_fname(const char *tofind, char *outstr, int outl);
+
+unsigned int fmode_to_desmode(const char *fmode)
+{
+ char rmode[16];
+ int x, y;
+
+ y = 0;
+
+ for(x=0;x<15;x++)
+ {
+ if(fmode[x] == 0)
+ break;
+ else
+ {
+ if(fmode[x] != 'b' && fmode[x] != 'f')
+ rmode[y++] = fmode[x];
+ }
+ }
+
+ rmode[y] = 0;
+
+ if(strcmp(rmode, "r") == 0)
+ {
+ dprintf("Open for reading.\n");
+ return O_RDONLY;
+ }
+ else if(strcmp(rmode, "r+") == 0)
+ {
+ dprintf("Open for reading and writing.\n");
+ return O_RDWR;
+ }
+ else if(strcmp(rmode, "w") == 0)
+ {
+ dprintf("Open for writing.\n");
+ return O_WRONLY | O_CREAT | O_TRUNC;
+ }
+ else if(strcmp(rmode, "w+") == 0)
+ {
+ dprintf("Open for writing. Truncate to zero or create file.\n");
+ return O_RDWR | O_CREAT | O_TRUNC;
+ }
+ else if(strcmp(rmode, "a") == 0)
+ {
+ dprintf("Append; open for writing. Create file if it doesn't exist.\n");
+ return O_WRONLY | O_APPEND;
+ }
+ else if(strcmp(rmode, "a+") == 0)
+ {
+ dprintf("Append; open for reading and writing. Create file if it doesn't exist.\n");
+ return O_RDWR | O_APPEND | O_CREAT;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+FILE *fdopen(int fildes, const char *mode)
+{
+// Adjust for malloc
+ int x;
+
+// Find a free file structure
+ for(x = 0; x < NUM_OF_FILE_STRUCTS; x++)
+ {
+ if(file_structs[x].used == 0)
+ {
+ bzero(&file_structs[x], sizeof(FILE));
+ file_structs[x].used = 1;
+ break;
+ }
+ }
+
+// If we found no free file structure, return NULL pointer
+
+ if(x == NUM_OF_FILE_STRUCTS)
+ return NULL;
+
+ file_structs[x].fildes = fildes;
+ file_structs[x].pos = lseek(fildes, 0, SEEK_CUR);
+ file_structs[x].mode = fmode_to_desmode(mode);
+
+ return &file_structs[x];
+}
+
+static FILE *fopen_internal(const char *path, const char *mode, FILE *f)
+{
+ int fd;
+ char *s = NULL;
+
+ if(strncmp(path, "cdromL:", 7) == 0)
+ {
+ s = malloc(1024);
+
+ if(libc_get_transtbl_fname(path+7, s, 1024) == 0)
+ return NULL;
+
+ fd = open(s, fmode_to_desmode(mode));
+ }
+ else
+ fd = open(path, fmode_to_desmode(mode));
+
+ if(fd == -1)
+ {
+ if(s!=NULL)free(s);
+ return NULL;
+ }
+
+ if(f == NULL)
+ f = fdopen(fd, mode);
+ else
+ {
+ f->fildes = fd;
+ f->pos = lseek(fd, 0, SEEK_CUR);
+ f->mode = fmode_to_desmode(mode);
+ }
+
+ if(f == NULL)
+ {
+ if(s!=NULL)free(s);
+ return NULL;
+ }
+
+ f->dev = FDEV_UNKNOWN;
+
+ if(strncmp(path, "cdrom", 5) == 0 || strncmp(path, "cdromL", 6) == 0)
+ f->dev = FDEV_CDROM;
+ else if(strncmp(path, "bu", 2) == 0)
+ f->dev = FDEV_MEMCARD;
+
+// nocash bios freezes at get_real_file_size(), due
+// to problems with firstfile()
+
+ if(s!=NULL)
+ {
+ f->size = get_real_file_size(s);
+ free(s);
+ }
+ else
+ f->size = get_real_file_size(path);
+
+ return f;
+}
+
+FILE *fopen(const char *path, const char *mode)
+{
+ return fopen_internal(path, mode, NULL);
+}
+
+int fclose(FILE *stream)
+{
+ stream->used = 0;
+ close(stream->fildes);
+ return 0;
+}
+
+/*
+ * fread doesn't require reads to be carried in block unit
+ * Notice that however seeks on the CD drive will be very slow - so avoid using non block units
+ *
+ * This is done to make programming and porting easier
+ */
+
+int fread(void *ptr, int size, int nmemb, FILE *f)
+{
+ int rsize = size * nmemb;
+ int csize = rsize;
+ int max;
+ int nsect = (f->pos + rsize) >> 11;
+ nsect -= f->pos >> 11;
+ nsect++;
+
+ //printf("f->dev = %d, f->pos = %d, rsize = %d\n", f->dev, f->pos, rsize);
+
+ if(f->dev == FDEV_CDROM)
+ {
+ // First sector
+ lseek(f->fildes, f->pos & (~0x7ff), SEEK_SET);
+ read(f->fildes, onesec_buf, 2048);
+
+ max = 2048 - (f->pos & 2047);
+
+ //printf("ptr(FIRST) = %d, %x\n", ptr, ptr);
+ printf("rsize = %d\n", rsize);
+
+ memcpy(ptr, onesec_buf + (f->pos & 2047), (rsize > max) ? max : rsize);
+
+ // Middle sector
+ ptr += max;
+
+ //printf("ptr(MIDDLEsex) = %d, %x\n", ptr, ptr);
+ nsect--;
+ csize -= max;
+
+ if(nsect > 1)
+ {
+ //lseek(f->fildes, (f->pos & (~0x7ff)) + 2048, SEEK_SET);
+
+//#warning "Check correctness of this calculation."
+
+ /*if(rsize & 2047)
+ sect_num = (rsize|2047)+1;
+ else
+ sect_num = rsize;
+
+ sect_num -= 4096;*/
+
+ //printf("read_middle=%d, sect_num = %d\n", read(f->fildes, ptr, sect_num), sect_num);
+
+ read(f->fildes, ptr, (nsect - 1) * 2048);
+
+ ptr += (nsect - 1) * 2048;
+ csize -= (nsect - 1) * 2048;
+ nsect = 1;
+ }
+
+ //printf("ptr(LAST) = %d, %x\n", ptr, ptr);
+
+ if(nsect == 1)
+ {
+ // Last sector
+ read(f->fildes, onesec_buf, 2048);
+
+ memcpy(ptr, onesec_buf, csize);
+ }
+ }
+ else if(f->dev == FDEV_CONSOLE)
+ return 0;
+
+
+ if(f->dev != FDEV_CONSOLE)
+ {
+ f->pos+= rsize;
+ f->eof = (f->pos > f->size);
+
+ if(f->eof)
+ f->pos = f->size;
+ }
+
+ return rsize;
+}
+
+int fgetc(FILE *f)
+{
+ unsigned char c;
+
+ if(f->pos >= f->size)
+ return EOF;
+
+ fread(&c, sizeof(char), 1, f);
+
+ return (int)c;
+}
+
+int ftell(FILE *f)
+{
+ return f->pos;
+}
+
+int fseek(FILE *f, int offset, int whence)
+{
+ switch(whence)
+ {
+ case SEEK_SET:
+ f->pos = offset;
+ break;
+ case SEEK_CUR:
+ f->pos+= offset;
+ break;
+ case SEEK_END:
+ f->pos = f->size + offset;
+ break;
+ default:
+ f->pos = whence + offset;
+ break;
+ }
+
+ return 0;
+}
+
+int toupper(int c)
+{
+ if(c >= 'a' && c <= 'z')
+ return (c-'a')+'A';
+
+ return c;
+}
+
+int tolower(int c)
+{
+ if(c >= 'A' && c <= 'Z')
+ return (c-'A')+'a';
+
+ return c;
+}
+
+int libc_get_transtbl_fname(const char *tofind, char *outstr, int outl)
+{
+ FILE *f;
+ int s;
+ int x;
+ int type;
+ int y;
+ int l = strlen(tofind);
+ int filename_found = 0;
+ int exit_loop = 0;
+ int otfp = 0;
+ int tfp = 0;
+ char transtbl[0x4000];
+ char orgname[16];
+ char newname[256];
+ char rootpath[256];
+
+ bzero(transtbl, 0x4000);
+ strcpy(rootpath, "cdrom:\\");
+
+ f = fopen("cdrom:\\TRANS.TBL;1", "rb");
+
+ if(f == NULL)
+ return 0;
+
+ fseek(f, 0, SEEK_END);
+ s = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ fread(transtbl, 1, s, f);
+ fclose(f);
+
+ outstr[0] = 0;
+
+ x = 0;
+
+ exit_loop = 0;
+ filename_found = 0;
+
+ for(tfp = 0; tofind[tfp] == '\\' || tofind[tfp] == '/'; tfp++);
+
+ otfp = tfp;
+
+ for(y = otfp; y < l; y++)
+ {
+ if(tofind[y] == '\0' || tofind[y] == '\\' || tofind[y] == '/')
+ break;
+ }
+
+ tfp = y;
+
+ while((x < s) && !exit_loop)
+ {
+ while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
+ x++;
+
+ if(transtbl[x] == 'F')
+ type = 0;
+ else if(transtbl[x] == 'D')
+ type = 1;
+
+ x++;
+
+ while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
+ x++;
+
+ y = 0;
+
+ while(!(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r'
+ || transtbl[x] == 0))
+ orgname[y++] = transtbl[x++];
+
+ orgname[y] = 0;
+ //printf("orgname = %s\n", orgname);
+
+ while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
+ x++;
+
+ y = 0;
+
+ while(!(transtbl[x] == '\n' || transtbl[x] == '\r' || transtbl[x] == 0))
+ newname[y++] = transtbl[x++];
+
+ newname[y] = 0;
+
+ while(transtbl[x] == ' ' || transtbl[x] == '\t' || transtbl[x] == '\n' || transtbl[x] == '\r')
+ x++;
+
+ //printf("newname = %s\n", newname);
+
+ if(strncasecmp(&tofind[otfp], newname, tfp-otfp) == 0)
+ {
+ if(type == 0)
+ {
+ dprintf("Filename found: %s -> %s%s\n", tofind, rootpath, orgname);
+ filename_found = 1;
+ exit_loop = 1;
+
+ strncpy(outstr, rootpath, outl);
+ strncat(outstr, orgname, outl-strlen(rootpath));
+ }
+ else
+ {
+ //printf("Found directory! %s\n", newname);
+
+ //printf("tfp = %d\n", tfp);
+
+ if(tfp == l || tofind[l-1] == '/'
+ || tofind[l-1] == '\\')
+ {
+ dprintf("File not found. A directory was specified.\n");
+ exit_loop = 1;
+ continue;
+ }
+
+ //tfp++;
+ for(; tofind[tfp] == '\\' || tofind[tfp] == '/'; tfp++);
+
+ otfp = tfp;
+
+ for(y = otfp; y < l; y++)
+ {
+ if(tofind[y] == '\0' || tofind[y] == '\\' || tofind[y] == '/')
+ break;
+ }
+
+ tfp = y;
+
+ strcat(rootpath, orgname);
+ strcat(rootpath, "\\");
+
+ y = strlen(rootpath);
+ strcat(rootpath, "TRANS.TBL;1");
+
+ bzero(transtbl, 0x4000);
+
+ f = fopen(rootpath, "rb");
+
+ if(f == NULL)
+ {
+ dprintf("Couldn't find %s\n", rootpath);
+ exit_loop = 1;
+ continue;
+ }
+
+ rootpath[y] = 0;
+
+ fseek(f, 0, SEEK_END);
+ s = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ fread(transtbl, 1, s, f);
+ fclose(f);
+
+ x = 0;
+ }
+ }
+ }
+
+ return filename_found;
+}
+
+int isupper(int c)
+{
+ return (c >= 'A' && c <= 'Z');
+}
+
+int islower(int c)
+{
+ return (c >= 'a' && c <= 'z');
+}
+
+int isdigit(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+int isxdigit(int c)
+{
+ return ((c >= '0' && c <= '9') || (c >= 'A' && c<='F') || (c >= 'a' && c<='f'));
+}
+
+int isalpha(int c)
+{
+ return ((c>='a' && c<='z') || (c>='A' && c<='Z'));
+}
+
+int isalnum(int c)
+{
+ return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'));
+}
+
+int isspace(int c)
+{
+ return ((c == ' ') || (c == '\f') || (c == '\n') || (c == '\r') || (c == '\t') || (c == '\v'));
+}
+
+int isprint(int c)
+{
+ return (c >= 0x20) && (c <= 0x7E);
+}
+
+int isgraph(int c)
+{
+ return (c > 0x20) && (c <= 0x7E);
+}
+
+int iscntrl(int c)
+{
+ return (c < 0x20);
+}
+
+int isblank(int c)
+{
+ return ((c == ' ') || (c == '\t'));
+}
+
+void redirect_stdio_to_sio(void)
+{
+ __stdio_direction = STDIO_DIRECTION_SIO;
+}
+
+void sio_stdio_mapcr(unsigned int setting)
+{
+ __sio_cr_mapped = setting;
+}
+
+int sio_putchar(int c)
+{
+ if(c == '\n' && __sio_cr_mapped)
+ sio_putchar('\r');
+
+ while(!SIOCheckOutBuffer());
+
+ SIOSendByte(c);
+
+ return c;
+}
+
+int sio_puts(const char *str)
+{
+ while(*str)
+ sio_putchar(*(str++));
+
+ sio_putchar('\n');
+
+ return 1;
+}
+
+extern int bios_putchar(int c);
+extern int bios_puts(const char *str);
+
+int putchar(int c)
+{
+ switch(__stdio_direction)
+ {
+ case STDIO_DIRECTION_BIOS:
+ return bios_putchar(c);
+ break;
+ case STDIO_DIRECTION_SIO:
+ return sio_putchar(c);
+ break;
+ }
+
+ return EOF;
+}
+
+int puts(const char *str)
+{
+ switch(__stdio_direction)
+ {
+ case STDIO_DIRECTION_BIOS:
+ return bios_puts(str);
+ break;
+ case STDIO_DIRECTION_SIO:
+ return sio_puts(str);
+ break;
+ }
+
+ return EOF;
+}
+
+int fwrite(void *ptr, int size, int nmemb, FILE *f)
+{
+ if(IS_CONS_OUT(f)) // stdout or stderr
+ {
+ char *c = ptr;
+ int i;
+
+ for(i = 0; i < size; i++)
+ putchar(c[i]);
+
+ return size;
+ }
+
+ return 0;
+}
+
+int fputs(const char *str, FILE *stream)
+{
+ if(IS_CONS_OUT(stream))
+ return puts(str);
+
+ return EOF;
+}
+
+FILE *freopen(const char *path, const char *mode, FILE *stream)
+{
+ if(stream == NULL)
+ return NULL;
+
+ if(stream->used)
+ fclose(stream);
+
+ return fopen_internal(path, mode, stream);
+}
+
+void clearerr(FILE *stream)
+{
+ stream->eof = 0;
+ stream->error = 0;
+}
+
+int feof(FILE *stream)
+{
+ return stream->eof;
+}
+
+int ferror(FILE *stream)
+{
+ return stream->error;
+}
+
+int fileno(FILE *stream)
+{
+ return stream->fildes;
+}
diff --git a/libpsx/src/libc/error.c b/libpsx/src/libc/error.c
new file mode 100644
index 0000000..6863b2f
--- /dev/null
+++ b/libpsx/src/libc/error.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <string.h>
+
+static char strerror_not_implemented[64];
+
+char *strerror(int errnum)
+{
+ strerror_r(errnum, strerror_not_implemented, 64);
+
+ return strerror_not_implemented;
+}
+
+int strerror_r(int errnum, char *strerrbuf, size_t buflen)
+{
+ snprintf(strerrbuf, buflen,
+ "strerror(%d)", errnum);
+
+ return 0;
+}
diff --git a/libpsx/src/libc/misc.c b/libpsx/src/libc/misc.c
new file mode 100644
index 0000000..f3a1326
--- /dev/null
+++ b/libpsx/src/libc/misc.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+static unsigned int rand_seed = 0;
+static unsigned int rand_next = 0;
+
+int abs(int x)
+{
+ if(x<0)return -x;
+
+ return x;
+}
+
+void srand(unsigned int seed)
+{
+ rand_seed = seed;
+}
+
+int rand()
+{
+ rand_next = (rand_next ^ 0x98765432)*0x1357;
+
+ return rand_next % RAND_MAX;
+}
+
+static char *__ulltoa_internal__(unsigned long long value, char *str, int base, int minus_sign,
+ unsigned long long maxp )
+{
+ unsigned long long p;
+ unsigned long long p3;
+ int c;
+ int a;
+
+ p = 1;
+
+ do
+ {
+ p3 = p;
+ p *= base;
+
+ if(maxp && p > maxp)
+ break;
+ }while((p >= p3) && !(p % p3));
+
+ if(minus_sign)
+ *(str++) = '-';
+
+ for(a = 0;p3 > 0;p3/=base)
+ {
+ c = value / p3;
+ value %= p3;
+
+ if(c)
+ a = 1;
+
+ if(a)
+ {
+ if(c <= 9)
+ c += '0';
+ else
+ c = (c - 10) + 'A';
+
+ *(str++) = c;
+ }
+ }
+
+ *str = '\0';
+
+ return str;
+}
+
+char *ulltoa(unsigned long long value, char *str, int base)
+{
+ return __ulltoa_internal__(value, str, base, 0, 0);
+}
+
+char *ultoa(unsigned long value, char *str, int base)
+{
+ return __ulltoa_internal__(value, str, base, 0, (sizeof(long)==8)?0:0xFFFFFFFF);
+}
+
+char *utoa(unsigned int value, char *str, int base)
+{
+ return __ulltoa_internal__(value, str, base, 0, 0xFFFFFFFF);
+}
+
+char *lltoa(long long value, char *str, int base)
+{
+ return __ulltoa_internal__((value<0)?-value:value, str, base, value<0, 0);
+}
+
+char *ltoa(long value, char *str, int base)
+{
+ return __ulltoa_internal__((value<0)?-value:value, str, base, value<0, (sizeof(long)==8)?0:0xFFFFFFFF);
+}
+
+char *itoa(int value, char *str, int base)
+{
+ return __ulltoa_internal__((value<0)?-value:value, str, base, value<0, 0xFFFFFFFF);
+}
+
+void abort(void)
+{
+ printf("abort(): Abnormal program termination\n");
+ exit(1);
+}
diff --git a/libpsx/src/libc/printf.c b/libpsx/src/libc/printf.c
new file mode 100644
index 0000000..ce8ffcd
--- /dev/null
+++ b/libpsx/src/libc/printf.c
@@ -0,0 +1,899 @@
+/*
+ * printf.c
+ *
+ * Part of the PSXSDK C library
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SPRINTF_ALT_FLAG (1<<0)
+#define SPRINTF_ZERO_FLAG (1<<1)
+#define SPRINTF_NEGFIELD_FLAG (1<<2)
+#define SPRINTF_SPACE_FLAG (1<<3)
+#define SPRINTF_SIGN_FLAG (1<<4)
+
+// sprintf() macros to calculate the real padding and to write it
+// these were made to not repeat the code in the function
+// they can only be used in sprintf()
+
+// sprintf macros START
+
+#define calculate_real_padding() \
+ y = 1; \
+ \
+ for(x=0;x<=19;x++) \
+ { \
+ if(x == 0) \
+ pad_quantity--; \
+ else \
+ { \
+ if(arg / y) \
+ pad_quantity--; \
+ } \
+ \
+ y *= 10; \
+ } \
+ \
+ if(pad_quantity < 0) pad_quantity = 0;
+
+/*#define calculate_real_padding_hex() \
+ for (x = 0; x < 8; x++) \
+ { \
+ if(x == 0) \
+ pad_quantity--; \
+ else \
+ { \
+ if((arg >> (x * 4)) & 0xf) \
+ pad_quantity--; \
+ } \
+ }*/
+
+#define calculate_real_padding_hex() \
+ last = 0; \
+ for (x = 0; x < 16; x++) \
+ if((arg >> (x * 4)) & 0xf) \
+ last = x; \
+ \
+ pad_quantity = (pad_quantity - 1) - last; \
+ if(pad_quantity < 0) pad_quantity = 0;
+
+#define write_padding() \
+ if(!(flags & SPRINTF_NEGFIELD_FLAG)) \
+ for(x = 0; x < pad_quantity; x++) \
+ { \
+ if(flags & SPRINTF_ZERO_FLAG) \
+ put_in_string(string, ssz, '0', string_pos++); \
+ else \
+ put_in_string(string, ssz, ' ', string_pos++); \
+ }
+
+#define write_neg_padding() \
+ if(flags & SPRINTF_NEGFIELD_FLAG) \
+ { \
+ for(x = 0; x < pad_quantity; x++) \
+ put_in_string(string, ssz, ' ', string_pos++);\
+ }
+
+// sprintf macros END
+
+enum
+{
+ SPRINTF_SIZE_CHAR,
+ SPRINTF_SIZE_SHORT,
+ SPRINTF_SIZE_INT,
+ SPRINTF_SIZE_LONG,
+ SPRINTF_SIZE_LONG_LONG,
+};
+
+static unsigned int get_arg_in_size(int size, unsigned long long *arg, unsigned int check_sign)
+{
+ int s = 0;
+
+ switch(size)
+ {
+ case SPRINTF_SIZE_CHAR:
+ *arg &= 0xff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<7))
+ {
+ *arg |= 0xffffff00;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+ case SPRINTF_SIZE_SHORT:
+ *arg &= 0xffff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<15))
+ {
+ *arg |= 0xffff0000;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+
+// sizeof(long) == sizeof(int) on 32bit, so this will suffice for the psx
+
+ case SPRINTF_SIZE_INT:
+ case SPRINTF_SIZE_LONG:
+ *arg &= 0xffffffff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<31))
+ {
+ *arg |= (long long)0xffffffff00000000;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+
+ case SPRINTF_SIZE_LONG_LONG:
+ if(check_sign)
+ {
+ if(*arg & ((long long)1<<63))
+ {
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+ }
+
+ return s;
+}
+
+static int libc_ulltoa(unsigned long long i, char *dst, int n, int nopad)
+{
+ int x, y;
+ unsigned long long a, b;
+ int empty_digit = 1;
+ int sp=0;
+ int n2=0;
+
+ if(n<=0)
+ return 0;
+
+ for(x=18;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+ b = (i/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0 || nopad == 1)
+ {
+ i -= b*a;
+
+ //put_in_string(string, ssz, b + '0', string_pos++);
+ if(n2!=(n-1))
+ {
+ //printf("n2=%d\n",n2);
+ dst[sp++] = b + '0';
+ n2++;
+ }
+ }
+ }
+
+ dst[sp] = 0;
+
+ return n2;
+}
+
+/*static void libc_float_to_string(float fl, char *dst, int n)
+{
+ unsigned int *p = (unsigned int*)&fl;
+ unsigned long long i = 0;
+ unsigned long long f = 0;
+ int e, m, s;
+ int x, y;
+ unsigned long long z;
+
+ s = *p >> 31;
+
+ e = (*p >> 23) & 0xff;
+
+ m = *p & 0x7fffff;
+
+ if(e == 255 && m == 0) // Infinity
+ {
+ if(s) strncpy(dst, "-inf", n);
+ else strncpy(dst, "inf", n);
+ }else if(e == 255 && m != 0) // NaN
+ {
+ strncpy(dst, "nan", n);
+ }
+ else
+ {
+ e -= 127;
+ m |= 1<<23;
+
+
+
+ for(x = 23; x >= 0; x--)
+ {
+ if(m & (1<<x))
+ {
+ if(e >= 0)
+ {
+ z = 1;
+ for(y=0;y<e;y++)
+ z*=2;
+
+ i+=z;
+ }
+ else
+ {
+ z = 5000000000000000000;
+ for(y = 1; y < -e; y++)
+ z /= 2;
+
+ f+=z;
+ }
+ }
+ e--;
+ }
+
+ if(s && (n>0))
+ {
+ *(dst++) = '-';
+ n--;
+ }
+
+ x = libc_ulltoa(i, dst, n, 0);
+ n-=x+1;
+ dst+=x;
+
+ if(n>0)
+ {
+ *(dst++) = '.';
+ n--;
+ if(n>0)
+ {
+ x = libc_ulltoa(f, dst, n<6?n:6, 1);
+ n-=x;
+ dst+=x;
+
+ if(n>0)
+ *dst=0;
+ }
+ }
+ }
+}*/
+
+static void libc_double_to_string(double fl, char *dst, int n, int prec)
+{
+ unsigned long long *p = (unsigned long long *)&fl;
+ unsigned long long i = 0;
+ unsigned long long f = 0;
+ unsigned long long m, s;
+ long long e;
+ int x;
+ unsigned long long z;
+
+ s = *p >> 63;
+
+ e = (*p >> 52) & 0x7ff;
+ //printf("%d\n", e);
+
+ m = *p & 0xfffffffffffff;
+
+ for(x=0;x<52;x++)
+ if(m&((unsigned long long)1<<(52-x))) putchar('1'); else putchar('0');
+
+ if(e == 255 && m == 0) // Infinity
+ {
+ if(s) strncpy(dst, "-inf", n);
+ else strncpy(dst, "inf", n);
+ }else if(e == 255 && m != 0) // NaN
+ {
+ strncpy(dst, "nan", n);
+ }
+ else
+ {
+ e -= 1023;
+ m |= (unsigned long long)1<<52;
+
+ for(x = 52; x >= 0; x--)
+ {
+ if(m & ((unsigned long long)1<<x))
+ {
+ if(e >= 0)
+ {
+ z = (long long)1<<e;
+
+ i+=z;
+ }
+ else
+ {
+ z = 5000000000000000000;
+ z >>= -(e + 1);
+
+ f+=z;
+ }
+ }
+ e--;
+ }
+
+ if(s && (n>0))
+ {
+ *(dst++) = '-';
+ n--;
+ }
+
+ x = libc_ulltoa(i, dst, n, 0);
+ n-=x+1;
+ dst+=x;
+
+ dprintf("N = %d\n", n);
+
+ if(n>0)
+ {
+ *(dst++) = '.';
+
+ if(n>0)
+ libc_ulltoa(f, dst, (n<(prec+1))?n:(prec+1), 1);
+ }
+ }
+}
+
+static char libc_sprintf_floatbuf[64];
+
+static int __vsnprintf_internal(char *string, size_t size, const char *fmt, va_list ap, int (put_in_string(char *string, unsigned int sz, char c, int pos)))
+{
+ int string_pos,fmt_pos;
+ int l;
+ unsigned long long arg;
+ char *argcp;
+ char *argcp_tmp;
+ int directive_coming = 0;
+ int flags = 0;
+ int argsize = 2; // int
+ int x, y;
+ unsigned long long a, b;
+ int empty_digit;
+ int ssz = size - 1;
+ int zero_flag_imp = 0;
+ int pad_quantity = 0;
+ int pad_quantity_f = -1;
+ int last;
+
+ if(size == 0)
+ ssz = 0;
+
+ l = strlen(fmt);
+
+ string_pos = 0;
+
+ for(fmt_pos=0;fmt_pos<l;fmt_pos++)
+ {
+ if(directive_coming)
+ {
+ switch(fmt[fmt_pos])
+ {
+ case '%':
+ put_in_string(string, ssz, '%', string_pos++);
+ directive_coming = 0;
+ break;
+ case ' ':
+ flags |= SPRINTF_SPACE_FLAG;
+ break;
+ case '#': // Specify alternate form
+ flags |= SPRINTF_ALT_FLAG;
+ break;
+ case '+': // Specify sign in signed conversions
+ flags |= SPRINTF_SIGN_FLAG;
+ break;
+ case '0': // Padding with zeros...
+ if(zero_flag_imp == 0)
+ {
+ flags |= SPRINTF_ZERO_FLAG;
+ zero_flag_imp = 1;
+ //printf("Zero padding enabled!\n");
+ }
+ else
+ {
+ pad_quantity *= 10;
+ //printf("pad_quantity = %d\n", pad_quantity);
+ }
+ break;
+ case '1' ... '9': // '...' cases are a GNU extension,
+ // but they simplify a lot
+
+ pad_quantity *= 10;
+ pad_quantity += fmt[fmt_pos] - '0';
+ zero_flag_imp = 1;
+
+ //printf("pad_quantity = %d\n", pad_quantity);
+ break;
+ case '-': // Negative field flag
+ if(flags & SPRINTF_ZERO_FLAG)
+ flags &= ~SPRINTF_ZERO_FLAG;
+
+ flags |= SPRINTF_NEGFIELD_FLAG;
+ break;
+ case '.': // Floating point precision
+ pad_quantity_f = pad_quantity;
+ pad_quantity = 0;
+ break;
+ case 'h': // Half argument size
+ if(argsize) argsize--;
+ break;
+ case 'l': // Double argument size
+ if(argsize < 2) argsize = 2;
+ else if(argsize < SPRINTF_SIZE_LONG_LONG) argsize++;
+ break;
+
+// 'j', 't', 'z', 'q' added 2013-10-26 by nextvolume
+
+ case 'j': // Maximum integer size
+ argsize = SPRINTF_SIZE_LONG_LONG;
+ break;
+
+ case 't': // Size of ptrdiff_t (i.e. long on 32-bit, long long on 64-bit)
+ argsize = (sizeof(void*)==8)?
+ SPRINTF_SIZE_LONG_LONG:SPRINTF_SIZE_LONG;
+ break;
+
+ case 'z': // Size of size_t (int)
+ argsize = SPRINTF_SIZE_INT;
+ break;
+
+ case 'q': // Size of quad_t
+ argsize = SPRINTF_SIZE_LONG_LONG;
+ break;
+
+ case 'd': // signed decimal
+ case 'i':
+ empty_digit = 1;
+
+ //printf("argsize = %d\n", argsize);
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ if(flags & SPRINTF_SPACE_FLAG)
+ put_in_string(string, ssz, ' ', string_pos++);
+
+ if(get_arg_in_size(argsize, &arg, 1))
+ {
+ put_in_string(string, ssz, '-', string_pos++);
+ pad_quantity--;
+ }
+ else
+ {
+ if(flags & SPRINTF_SIGN_FLAG)
+ {
+ put_in_string(string, ssz, '+', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ /* Calculate how much padding we have to write */
+
+ /*y = 1;
+
+ for(x=0;x<=9;x++)
+ {
+ if(x == 0)
+ pad_quantity--;
+ else
+ {
+ if(arg / y)
+ pad_quantity--;
+ }
+
+ y *= 10;
+ }
+ if(pad_quantity < 0) pad_quantity = 0;*/
+
+ calculate_real_padding();
+
+ //printf("Actual pad quantity = %d\n", pad_quantity);
+
+
+
+ /*if(!(flags & SPRINTF_NEGFIELD_FLAG))
+ {
+ for(x = 0; x < pad_quantity; x++)
+ {
+ if(flags & SPRINTF_ZERO_FLAG)
+ put_in_string(string, ssz, '0', string_pos++);
+ else
+ put_in_string(string, ssz, ' ', string_pos++);
+ }
+ }*/
+
+ write_padding();
+
+ for(x=19;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+ b = (arg/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ arg -= b*a;
+
+ put_in_string(string, ssz, b + '0', string_pos++);
+ }
+ }
+
+ /*if(flags & SPRINTF_NEGFIELD_FLAG)
+ {
+ for(x = 0; x < pad_quantity; x++)
+ put_in_string(string, ssz, ' ', string_pos++);
+ }*/
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'u': // unsigned decimal
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ get_arg_in_size(argsize, &arg, 0);
+
+ calculate_real_padding();
+ write_padding();
+
+ for(x=19;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+
+
+ b = (arg/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ arg -= b*a;
+
+ put_in_string(string, ssz, b + '0', string_pos++);
+ }
+ }
+
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'x': // Hexadecimal
+ case 'X': // Hexadecimal with big letters
+ case 'p': // Hexadecimal with small letters with '0x' prefix
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long int);
+
+ get_arg_in_size(argsize, &arg, 0);
+
+ if(fmt_pos == 'p')
+ flags |= SPRINTF_ALT_FLAG;
+
+ if(flags & SPRINTF_ALT_FLAG)
+ {
+ put_in_string(string, ssz, '0', string_pos++);
+
+ if(fmt[fmt_pos] == 'X')
+ put_in_string(string, ssz, 'X', string_pos++);
+ else
+ put_in_string(string, ssz, 'x', string_pos++);
+ }
+
+ calculate_real_padding_hex();
+ write_padding();
+
+ for(x=15;x>=0;x--)
+ {
+ y = arg >> (x << 2);
+ y &= 0xf;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ if(y>=0 && y<=9)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ else if(y>=0xA && y<=0xF)
+ {
+ if(fmt[fmt_pos] == 'X')
+ put_in_string(string, ssz, (y - 0xa) + 'A', string_pos++);
+ else
+ put_in_string(string, ssz, (y - 0xa) + 'a', string_pos++);
+ }
+ }
+ }
+
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'c': // character
+ arg = va_arg(ap, int);
+
+ put_in_string(string, ssz, arg & 0xff, string_pos++);
+
+ directive_coming = 0;
+ break;
+ case 's': // string
+ argcp = va_arg(ap, char *);
+ argcp_tmp = argcp;
+
+ if(argcp == NULL)
+ {
+ // Non standard extension, but supported by Linux and the BSDs.
+
+ put_in_string(string, ssz, '(', string_pos++);
+ put_in_string(string, ssz, 'n', string_pos++);
+ put_in_string(string, ssz, 'u', string_pos++);
+ put_in_string(string, ssz, 'l', string_pos++);
+ put_in_string(string, ssz, 'l', string_pos++);
+ put_in_string(string, ssz, ')', string_pos++);
+
+ directive_coming = 0;
+ break;
+ }
+
+ while(*argcp_tmp)
+ {
+ if(pad_quantity > 0) pad_quantity--;
+ argcp_tmp++;
+ }
+
+ if(!(flags & SPRINTF_NEGFIELD_FLAG))
+ {
+ while(pad_quantity > 0)
+ {
+ put_in_string(string,ssz, ' ', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ while(*argcp)
+ {
+ put_in_string(string, ssz, *argcp, string_pos++);
+
+ argcp++;
+ }
+
+ if(flags & SPRINTF_NEGFIELD_FLAG)
+ {
+ while(pad_quantity > 0)
+ {
+ put_in_string(string,ssz, ' ', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ directive_coming = 0;
+ break;
+ case 'o': // Octal
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ for(x=21;x>=0;x--)
+ {
+ y = arg >> (x * 3);
+ y &= 0x7;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ }
+
+ directive_coming = 0;
+ break;
+ case '@': // Binary
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ for(x=63;x>=0;x--)
+ {
+ y = (arg >> x);
+ y &= 1;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ }
+
+ directive_coming = 0;
+ break;
+
+ case 'f':
+ if(pad_quantity_f == -1)
+ pad_quantity_f = 6;
+ else
+ {
+ x = pad_quantity_f;
+ pad_quantity_f = pad_quantity;
+ pad_quantity = x;
+ }
+
+ dprintf("PRECISION = %d\n", pad_quantity_f);
+
+ libc_double_to_string(va_arg(ap, double), libc_sprintf_floatbuf, 64, pad_quantity_f);
+
+ // calculate padding
+ pad_quantity -= strlen(libc_sprintf_floatbuf);
+
+ write_padding();
+
+ for(x=0;libc_sprintf_floatbuf[x]!=0;x++)
+ put_in_string(string, ssz, libc_sprintf_floatbuf[x], string_pos++);
+
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'n': // Number of characters written
+ *(va_arg(ap,unsigned int*)) = string_pos;
+
+ directive_coming = 0;
+ break;
+
+ default:
+ put_in_string(string, ssz, fmt[fmt_pos], string_pos++);
+ directive_coming = 0;
+ }
+ }
+ else
+ {
+ if(fmt[fmt_pos] == '%')
+ {
+ directive_coming = 1;
+ flags = 0;
+ argsize = 2;
+ pad_quantity = 0;
+ pad_quantity_f = -1;
+ zero_flag_imp = 0;
+ }
+ else
+ {
+ put_in_string(string, ssz, fmt[fmt_pos], string_pos++);
+ }
+ }
+ }
+
+ /*if(((size-1) < string_pos) && (size>0))
+ string[size - 1] = 0;
+ else
+ string[string_pos] = 0;*/
+ put_in_string(string, ssz, '\0', string_pos);
+
+ return string_pos;
+}
+
+static int vsnprintf_put_in_string(char *string, unsigned int sz, char c, int pos)
+{
+ if(pos>=sz)
+ return 0;
+ else
+ string[pos] = c;
+
+ return 1;
+}
+
+int vsnprintf(char *string, size_t size, const char *fmt, va_list ap)
+{
+ return __vsnprintf_internal(string, size, fmt, ap, vsnprintf_put_in_string);
+}
+
+static int sio_put_in_string(char *string, unsigned int sz, char c, int pos)
+{
+ sio_putchar(c);
+
+ return 1;
+}
+
+int sio_vprintf(const char *fmt, va_list ap)
+{
+ return __vsnprintf_internal(NULL, -1, fmt, ap, sio_put_in_string);
+}
+
+static int out_put_in_string(char *string, unsigned int sz, char c, int pos)
+{
+ putchar(c);
+
+ return 1;
+}
+
+int vprintf(char *fmt, va_list ap)
+{
+ return __vsnprintf_internal(NULL, -1, fmt, ap, out_put_in_string);
+}
+
+int vsprintf(char *string, const char *fmt, va_list ap)
+{
+ return vsnprintf(string, 0xffffffff, fmt, ap);
+}
+
+int sprintf(char *string, const char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = vsprintf(string, fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
+
+int snprintf(char *string, size_t size, const char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = vsnprintf(string, size, fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
+
+int sio_printf(const char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = sio_vprintf(fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
diff --git a/libpsx/src/libc/qsort.c b/libpsx/src/libc/qsort.c
new file mode 100644
index 0000000..0cb1d24
--- /dev/null
+++ b/libpsx/src/libc/qsort.c
@@ -0,0 +1,65 @@
+// quickSort
+//
+// This public-domain C implementation by Darel Rex Finley.
+//
+// Modified by Giuseppe Gatta
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+ #define QSORT_MAX_LEVELS 300
+
+
+ int beg[QSORT_MAX_LEVELS], end[QSORT_MAX_LEVELS], i=0, L, R, swap ;
+ char piv[size];
+
+ beg[0]=0; end[0]=nmemb;
+
+ while (i>=0)
+ {
+ L=beg[i]; R=end[i]-1;
+
+ if (L<R)
+ {
+ memcpy(piv, base + (size * L), size);
+
+ while (L<R)
+ {
+ while(compar(base + (R*size), piv) > 0 && L<R) R--;
+
+ if (L<R)
+ {
+ memcpy(base + (size *L), base + (size * R), size);
+ L++;
+ }
+
+ while(compar(base + (L*size), piv) <= 0 && L<R) L++;
+
+ if (L<R)
+ {
+ memcpy(base + (size *R), base + (size * L), size);
+ R--;
+ }
+ }
+
+ memcpy(base + (size*L), piv, size);
+
+ beg[i+1]=L+1;
+ end[i+1]=end[i];
+ end[i++]=L;
+
+ if (end[i]-beg[i]>end[i-1]-beg[i-1])
+ {
+ swap=beg[i]; beg[i]=beg[i-1]; beg[i-1]=swap;
+ swap=end[i]; end[i]=end[i-1]; end[i-1]=swap;
+ }
+ }
+ else
+ {
+ i--;
+ }
+ }
+}
diff --git a/libpsx/src/libc/scanf.c b/libpsx/src/libc/scanf.c
new file mode 100644
index 0000000..d419030
--- /dev/null
+++ b/libpsx/src/libc/scanf.c
@@ -0,0 +1,425 @@
+// vsscanf
+// Programmed by Giuseppe Gatta, 2011
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+char libc_vsscanf_buf[512];
+char libc_vsscanf_allow[256];
+
+enum
+{
+ elem_skip_space = 1,
+};
+
+int libc_vsscanf_get_element(char *dst, const char *src, int flag, int s)
+{
+ int i;
+ const char *osrc = src;
+
+ if(flag & elem_skip_space)
+ {
+ while(*src == ' ')
+ src++;
+ }
+
+ for(i=0;i<s;i++)
+ {
+ if((flag & elem_skip_space) && *src == ' ')
+ break;
+
+ if(*src != 0)
+ *(dst++) = *(src++);
+ else
+ break;
+ }
+
+ *dst = 0;
+
+ return src - osrc;
+}
+
+enum
+{
+ scanf_s_char, scanf_s_short, scanf_s_int,
+ scanf_s_long,
+ scanf_s_long_long
+};
+
+int vsscanf(const char *str, const char *format, va_list ap)
+{
+ int fp = 0;
+ int sp = 0;
+ int conv = 0;
+ int sz = scanf_s_int; // size for numbers defaults to 32-bit
+ int i,x,y,z;// h;
+ int suppress = 0;
+// int neg = 0;
+ int fsz = 512;
+ int def_fsz = 1;
+ int exspace = 0;
+// int alt = 0;
+ int r = 0;
+ int exit_loop=0;
+ char *ep;
+ long long buf;
+ double fbuf;
+
+
+ while(format[fp] && str[sp] && !exit_loop)
+ {
+ if(conv)
+ {
+ switch(format[fp])
+ {
+ case '%': // Percent, assignment does not occur
+ conv = 0;
+ break;
+
+ case 'h': // Halve size
+ sz--;
+ break;
+
+ case 'l': // Double size
+ sz++;
+ break;
+
+ case '*': // Suppress
+ suppress = 1;
+ break;
+
+ case ' ': // Explicit space
+ exspace = 1;
+ break;
+
+ case '#': // Alternate format
+ //alt = 1;
+ break;
+
+ case '0' ... '9': // '0' ... '9' is a GNU C extension!
+ if(def_fsz)
+ {
+ def_fsz = 0;
+ fsz = 0;
+ }
+
+ fsz *= 10;
+ fsz+=format[fp]-'0';
+
+ if(fsz > 512)
+ fsz = 512; // 512 is the maximum.
+ break;
+
+ case '@': // Binary. Non-standard extension
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 2);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf;break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'D':
+ sz++;
+ case 'd': // Decimal
+ case 'u':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 10);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf;break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 's': // String
+ sp += libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+
+ if(!suppress)
+ {
+ strcpy(va_arg(ap, char*), libc_vsscanf_buf);
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'c':
+ if(def_fsz)
+ fsz = 1;
+
+ sp += (i = libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], (exspace ? elem_skip_space : 0), fsz));
+ if(!suppress)
+ {
+ memcpy(va_arg(ap, char*), libc_vsscanf_buf, (fsz>i)?i:fsz);
+ r++;
+ }
+ break;
+
+ case 'n':
+ if(!suppress)
+ {
+ *(va_arg(ap, int*)) = sp;
+ r++;
+ }
+ break;
+
+ case 'p':
+ case 'x':
+ case 'X':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 16);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, unsigned char*)) = (unsigned char)buf; break;
+ case scanf_s_short: *(va_arg(ap, unsigned short*)) = (unsigned short)buf; break;
+ case scanf_s_int: *(va_arg(ap, unsigned int*)) = (unsigned int)buf; break;
+ case scanf_s_long: *(va_arg(ap, unsigned long*)) = (unsigned long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, unsigned long long*)) = (unsigned long long)buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'O':
+ sz++;
+ case 'o': // Octal integer
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 8);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, unsigned char*)) = (unsigned char)buf;break;
+ case scanf_s_short: *(va_arg(ap, unsigned short*)) = (unsigned short)buf; break;
+ case scanf_s_int: *(va_arg(ap, unsigned int*)) = (unsigned int)buf;break;
+ case scanf_s_long: *(va_arg(ap, unsigned long*)) = (unsigned long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, unsigned long long*)) = (unsigned long long)buf;break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'i':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+
+ if(libc_vsscanf_buf[0] == '0')
+ {
+ if(libc_vsscanf_buf[1] == 'x' || libc_vsscanf_buf[1] == 'X')
+ i = 16;
+ else
+ i = 8;
+ }
+ else
+ i = 10;
+
+ buf = strtoll(libc_vsscanf_buf, &ep, i);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf; break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = (long long)buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case '[':
+ i=0;
+ x=0; // Exclusion?
+ //h=0; // Hyphen?
+
+ fp++;
+ i++;
+
+ while(format[fp])
+ {
+ if(format[fp] == '^' && i==1)
+ {
+ memset(libc_vsscanf_allow, 1, 256);
+ x = 1;
+ fp++; i++; continue;
+ }
+
+ if(x)
+ {
+ if(format[fp] == ']' && i>=3)
+ break;
+ }
+ else
+ {
+ if(format[fp] == ']' && i>=2)
+ break;
+ }
+
+ if(format[fp] == '-')
+ {
+ if(format[fp+1] != ']')
+ y = 1;
+ else
+ libc_vsscanf_allow['-'] = x^1;
+ }
+ else
+ {
+ if(y == 1)
+ {
+ if(format[fp] < format[fp-2])
+ libc_vsscanf_allow[(unsigned char)format[fp]] = x^1;
+ else
+ for(z = format[fp-2]; z <= format[fp]; z++)
+ libc_vsscanf_allow[z] = x^1;
+
+ y = 0;
+
+ //printf("%s all chars from %c to %c\n", x?"Excluding":"Including",format[fp-2], format[fp]);
+ }
+ else
+ libc_vsscanf_allow[(unsigned char)format[fp]] = x^1;
+ }
+
+ fp++;
+ i++;
+ }
+
+// Now as we know what our character set is, let's get data from the string
+ /* puts("Character set:");
+
+ for(y=0;y<16;y++)
+ {
+ for(x=0;x<16;x++)
+ if(libc_vsscanf_allow[(y*16) + x])
+ putchar((y*16)+x);
+ else
+ putchar('*');
+
+ putchar('\n');
+ }
+ */
+ i = 0;
+
+ while(libc_vsscanf_allow[(unsigned char)str[sp]] && i<512)
+ libc_vsscanf_buf[i++] = str[sp++];
+
+ libc_vsscanf_buf[i] = 0;
+
+ if(!suppress)
+ {
+ strcpy(va_arg(ap, char*), libc_vsscanf_buf);
+ r++;
+ }
+ break;
+
+ case 'f': // Floating point number
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ fbuf = strtod(libc_vsscanf_buf, &ep);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char:
+ case scanf_s_short:
+ case scanf_s_int:
+ *(va_arg(ap, float*)) = (float)fbuf;
+ break;
+
+ case scanf_s_long:
+ case scanf_s_long_long:
+ *(va_arg(ap, double*)) = fbuf;
+ break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ }
+ }
+ else
+ {
+ if(format[fp] == '%')
+ {
+ // conv = 1;
+ // neg = 0;
+ suppress = 0;
+ sz = scanf_s_int;
+ fsz = 512;
+ def_fsz = 1;
+ exspace = 0;
+ // alt = 0;
+ bzero(libc_vsscanf_allow, 256);
+ //chset = 0;
+ }
+ else if(format[fp] != ' ')
+ {
+ if(format[fp] != str[sp])
+ exit_loop=1;
+
+ sp++;
+ }
+
+ }
+
+ fp++;
+ }
+
+ return r;
+}
+
+int sscanf(const char *str, const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start(ap, fmt);
+ r = vsscanf(str, fmt, ap);
+
+ va_end(ap);
+ return r;
+}
diff --git a/libpsx/src/libc/stat.c b/libpsx/src/libc/stat.c
new file mode 100644
index 0000000..1c43b92
--- /dev/null
+++ b/libpsx/src/libc/stat.c
@@ -0,0 +1,31 @@
+#include <psx.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+int stat(const char *path, struct stat *sb)
+{
+ struct DIRENTRY dir_e;
+
+ if(firstfile((char*)path, &dir_e) == NULL)
+ return -1;
+
+ sb->st_size = dir_e.size;
+
+ if(strncmp(path, "cdrom:", 6) == 0)
+ sb->st_blksize = 2048;
+ else if(strncmp(path, "bu00:", 5) == 0 ||
+ strncmp(path, "bu10:", 5) == 0)
+ sb->st_blksize = 128;
+ else
+// not a real blocksize, will be there just as a placeholder
+ sb->st_blksize = 1024;
+
+ sb->st_blocks =
+ sb->st_size / sb->st_blksize;
+
+ if(sb->st_size % sb->st_blksize)
+ sb->st_blocks++;
+
+ return 0;
+}
diff --git a/libpsx/src/libc/string.c b/libpsx/src/libc/string.c
new file mode 100644
index 0000000..ea59b91
--- /dev/null
+++ b/libpsx/src/libc/string.c
@@ -0,0 +1,706 @@
+/*
+ * string.c
+ *
+ * Part of the PSXSDK C library
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+void *memcpy(void *dst, const void *src, size_t len)
+{
+ void *dst2 = dst;
+
+ while(len--)
+ *(((unsigned char*)dst++)) = *(((unsigned char*)src++));
+
+ return dst2;
+}
+
+void *memccpy(void *dst, const void *src, int c, size_t len)
+{
+ unsigned char c2;
+
+ while(len--)
+ {
+ *(((unsigned char*)dst++)) = ( c2 = *(((unsigned char*)src++)) );
+
+ if(c2 == c)
+ return (void*)src;
+ }
+
+ return NULL;
+}
+
+void *memset(void *dst , char c , size_t n)
+{
+ unsigned char *dstc = (unsigned char*)dst;
+ int x;
+
+ for(x = 0; x < n; x++)
+ dstc[x] = c;
+
+ return dst;
+}
+
+int memcmp(const void *b1, const void *b2, size_t n)
+{
+ int x;
+ unsigned char *bp1 = (unsigned char*)b1;
+ unsigned char *bp2 = (unsigned char*)b2;
+
+ for(x = 0; x < n; x++)
+ if(bp1[x] != bp2[x])
+ return (bp1[x] - bp2[x]);
+
+ return 0;
+}
+
+void *memmove(void *dst, const void *src, size_t len)
+{
+ void *dst2 = dst;
+
+ dst+=len-1;
+ src+=len-1;
+
+ while(len--)
+ *(((unsigned char*)dst--)) = *(((unsigned char*)src--));
+
+ return dst2;
+}
+
+void *memchr(void *s , int c , size_t n)
+{
+ while(n--)
+ {
+ if(*((unsigned char*)s) == (unsigned char)c)
+ return s;
+
+ s++;
+ }
+
+ return NULL;
+}
+
+char *strncpy(char *dst, const char *src, size_t len)
+{
+ char *odst=dst;
+
+ while(*src && len)
+ {
+ *(dst++) = *(src++);
+ len--;
+ }
+
+ if(len)*dst = 0;
+
+ return odst;
+}
+
+char *strcpy(char *dst, const char *src)
+{
+ char *odst = dst;
+
+ while((*(dst++) = *(src++)));
+ return odst;
+}
+
+int strlen(const char *str)
+{
+ int i = 0;
+ while(*(str++))i++;
+ return i;
+}
+
+char *strchr(const char *s, int c)
+{
+ int x;
+ int l = strlen(s);
+
+ for(x = 0; x <= l; x++)
+ if(s[x] == c) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strrchr(const char *s, int c)
+{
+ int x;
+ int l = strlen(s);
+
+ for(x = l; x>=0; x--)
+ if(s[x] == c) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strpbrk(const char *s, const char *charset)
+{
+ int x,y;
+
+ for(x = 0; s[x] != 0; x++)
+ for(y = 0; charset[y] != 0; y++)
+ if(s[x] == charset[y]) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strstr(const char *big, const char *little)
+{
+ int ls = strlen(little);
+ int bs = strlen(big);
+ int x;
+
+ if(ls == 0)
+ return (char*)big;
+
+ if(ls > bs)
+ return NULL;
+
+ for(x = 0; x <= bs-ls; x++)
+ if(memcmp(little, &big[x], ls) == 0)
+ return (char*)&big[x];
+
+ return NULL;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+ while(*s1 && *s2 && (*s1 == *s2))
+ {
+ s1++;
+ s2++;
+ }
+
+ return *s1-*s2;
+}
+
+int strncmp(const char *s1, const char *s2, size_t len)
+{
+ int p = 0;
+
+ while(*s1 && *s2 && (*s1 == *s2) && p<len)
+ {
+ p++;
+
+ if(p<len)
+ {
+ s1++;
+ s2++;
+ }
+ }
+
+ return *s1-*s2;
+}
+
+int strcoll(const char *s1, const char *s2)
+{
+ return strcmp(s1, s2);
+}
+
+char *strdup(const char *str)
+{
+ char *ns = (void*)malloc(strlen(str) + 1);
+
+ if(ns == NULL)
+ return NULL;
+
+ strcpy(ns, str);
+ return ns;
+}
+
+char *strndup(const char *str, size_t len)
+{
+ int n=strlen(str);
+ char *ns = (void*)malloc((n+1)>len?len:(n+1));
+
+ if(ns == NULL)
+ return NULL;
+
+ strncpy(ns, str, (n+1)>len?len:(n+1));
+ return ns;
+}
+
+long long strtoll(const char *nptr, char **endptr, int base)
+{
+ int r = 0;
+ int t = 0;
+ int n = 0;
+
+ while(*nptr && isspace(*nptr))
+ nptr++;
+
+ if(*nptr == '-')
+ {
+ nptr++;
+ n = 1;
+ }
+
+ if(base == 0)
+ {
+ if(*nptr == '0')
+ base = 8;
+ else
+ base = 10;
+ }
+
+ if(!(base >= 2 && base <= 36))
+ return 0;
+
+ if(base == 16 && *nptr == '0')
+ {
+ if(*(nptr+1) == 'x' || *(nptr+1) == 'X')
+ nptr+=2;
+ }
+
+ while(*nptr)
+ {
+ switch(*nptr)
+ {
+ case '0'...'9':
+ t = *nptr - '0';
+ break;
+ case 'a' ... 'z':
+ t = (*nptr - 'a') + 10;
+ break;
+ case 'A' ... 'Z':
+ t = (*nptr - 'A') + 10;
+ break;
+ default:
+ t = 1000;
+ break;
+ }
+
+ if(t>=base)
+ break;
+
+ r*=base;
+ r+=t;
+ nptr++;
+ }
+
+ if(endptr)*endptr = (char*)nptr;
+ return n?-r:r;
+}
+
+long strtol(const char *nptr, char **endptr, int base)
+{
+ return (long)strtoll(nptr, endptr, base);
+}
+
+double strtod(const char *nptr, char **endptr)
+{
+ char strbuf[64];
+ int x = 0;
+ int y;
+ double i=0, d=0;
+ int s=1;
+
+ if(*nptr == '-')
+ {
+ nptr++;
+ s=-1;
+ }
+
+ while(*nptr >= '0' && *nptr <= '9' && x < 18)
+ strbuf[x++] = *(nptr++);
+
+ strbuf[x] = 0;
+
+ i = (double)strtoll(strbuf, NULL, 10);
+
+ if(*nptr == '.')
+ {
+ nptr++;
+ x = 0;
+
+ while(*nptr >= '0' && *nptr <= '9' && x < 7)
+ strbuf[x++] = *(nptr++);
+
+ strbuf[x] = 0;
+
+ if(endptr != NULL) *endptr = (char*)nptr;
+
+ y=1;
+
+ for(x=0;strbuf[x]!=0;x++)
+ y*=10;
+
+ d = (double)strtoll(strbuf, NULL, 10);
+ d /= y;
+ }
+ else
+ {
+ if(endptr != NULL)
+ *endptr = (char*)nptr;
+ }
+
+ return (i + d)*s;
+}
+
+long double strtold(const char *nptr, char **endptr)
+{
+ return (long double)strtod(nptr, endptr);
+}
+
+float strtof(const char *nptr, char **endptr)
+{
+ return (float)strtod(nptr, endptr);
+}
+
+char *strcat(char *s, const char *append)
+{
+ strcpy(&s[strlen(s)], append);
+
+ return s;
+}
+
+char *strncat(char *s, const char *append, size_t count)
+{
+ strncpy(&s[strlen(s)], append, count);
+
+ return s;
+}
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ while(tolower(*s1) && tolower(*s2) && (tolower(*s1) == tolower(*s2)))
+ {
+ s1++;
+ s2++;
+ }
+
+ return tolower(*s1)-tolower(*s2);
+}
+
+int strncasecmp(const char *s1, const char *s2, size_t len)
+{
+ int p = 0;
+
+ while(tolower(*s1) && tolower(*s2) && (tolower(*s1) == tolower(*s2)) && p<len)
+ {
+ p++;
+
+ if(p<len)
+ {
+ s1++;
+ s2++;
+ }
+ }
+
+ return tolower(*s1)-tolower(*s2);
+}
+
+int stricmp(const char *s1, const char *s2)
+{
+ return strcasecmp(s1,s2);
+}
+
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+ return strncasecmp(s1, s2, len);
+}
+
+//static char *strtok_string;
+
+char *strsep(char **stringp, const char *delim)
+{
+ //int x,y;
+ char *old = *stringp;
+ const char *s;
+ int ok = 0;
+
+ while(**stringp && !ok)
+ {
+ s = delim;
+
+ while(*delim)
+ {
+ if(**stringp == *delim)
+ {
+ **stringp = 0;
+ ok = 1;
+ break;
+ }
+
+ delim++;
+ }
+
+ delim = s;
+
+ *stringp+=1;
+ }
+
+ if(!ok)*stringp = NULL;
+
+ return old;
+}
+
+char *strtok(char *str, const char *sep)
+{
+ int x, y;
+ static char *strtok_string;
+ static int strtok_len;
+ static int strtok_pos;
+ //int strtok_oldpos = 0;
+
+ if(str != NULL)
+ {
+ strtok_string = str;
+ strtok_len = strlen(str);
+
+ for(x = 0; x < strtok_len; x++)
+ {
+ for(y = 0; sep[y] != 0; y++)
+ {
+ if(strtok_string[x] == sep[y])
+ {
+ strtok_string[x] = 0;
+ break;
+ }
+ }
+ }
+
+ strtok_pos = 0;
+
+ while(strtok_pos < strtok_len)
+ {
+ if(strtok_string[strtok_pos])
+ return &strtok_string[strtok_pos];
+
+ strtok_pos++;
+ }
+ }
+
+ while(strtok_pos < strtok_len)
+ {
+ if(!strtok_string[strtok_pos])
+ {
+ strtok_pos++;
+ break;
+ }
+
+ strtok_pos++;
+ }
+
+ while(strtok_pos < strtok_len)
+ {
+
+ if(strtok_string[strtok_pos])
+ return &strtok_string[strtok_pos];
+
+ strtok_pos++;
+ }
+
+ return NULL;
+}
+
+int strspn(const char *s, const char *charset)
+{
+ int x, y;
+ int appears;
+
+ for(x = 0; s[x] != 0; x++)
+ {
+ appears = 0;
+
+ for(y = 0; charset[y] != 0; y++)
+ {
+ if(s[x] == charset[y])
+ {
+ appears = 1;
+ break;
+ }
+ }
+
+ if(!appears)break;
+ }
+
+ return x;
+}
+
+int strcspn(const char *s, const char *charset)
+{
+ int x, y;
+ int appears;
+
+ for(x = 0; s[x] != 0; x++)
+ {
+ appears = 0;
+
+ for(y = 0; charset[y] != 0; y++)
+ {
+ if(s[x] == charset[y])
+ {
+ appears = 1;
+ break;
+ }
+ }
+
+ if(appears)break;
+ }
+
+ return x;
+}
+
+char *strlwr(char *string)
+{
+ char *old = string;
+
+ while(*string)
+ {
+ *string = tolower(*string);
+ string++;
+ }
+
+ return old;
+}
+
+char *strupr(char *string)
+{
+ char *old = string;
+
+ while(*string)
+ {
+ *string = toupper(*string);
+ string++;
+ }
+
+ return old;
+}
+
+int atoi(const char *string)
+{
+ return (int)strtol(string, NULL, 10);
+}
+
+long atol(const char *string)
+{
+ return strtol(string, NULL, 10);
+}
+
+int strnlen(const char *s, size_t maxlen)
+{
+ int l=0;
+
+ while(*(s++) && l<maxlen)
+ l++;
+
+ return l;
+}
+
+void *memrchr(void *b, int c, size_t len)
+{
+ int i = len - 1;
+ unsigned char *p = b;
+
+ for(i = len - 1; p[i] != (unsigned char)c && i >= 0;i--);
+
+ return (i>=0)?&p[i]:NULL;
+}
+
+char *stpcpy(char *dst, const char *src)
+{
+ do
+ {
+ *(dst++) = *src;
+ }while(*(src++));
+
+ return dst-1;
+}
+
+char *stpncpy(char *dst, const char *src, int len)
+{
+ int c = 0;
+
+ do
+ {
+ if(c < len)
+ *(dst++) = *src;
+
+ c++;
+ }while(*(src++) && c < len);
+
+ return dst-1;
+}
+
+char *strcasestr(const char *big, const char *little)
+{
+ while(*big)
+ {
+ const char *pbig = big;
+ const char *plittle = little;
+ int ok = 1;
+
+ while(*pbig)
+ {
+ if(tolower(*pbig) != tolower(*plittle))
+ {
+ ok = 0;
+ break;
+ }
+
+ pbig++;
+ plittle++;
+ }
+
+ if(ok)
+ return (char*)big;
+
+ big++;
+ }
+
+ return NULL;
+}
+
+int strlcpy(char *dst, const char *src, size_t size)
+{
+ char *src_end = memchr((void*)src, '\0', size);
+
+ if(src_end == NULL)
+ return 0;
+
+ memcpy(dst, src, src_end - src);
+
+ return (src_end - src);
+}
+
+int strlcat(char *dst, const char *src, size_t size)
+{
+ int dstl = strlen(dst);
+ char *q = dst + dstl;
+ int real_size = size;
+
+ if(memchr((void*)src, '\0', size))
+ real_size = strlen(src);
+
+ memcpy(q, src, real_size-dstl-1);
+ if(real_size != size) q[real_size-dstl-1] = '\0';
+
+ return size-dstl;
+}
+
+void *memmem(const void *big, size_t big_len, const void *little,
+ size_t little_len)
+{
+ int i, j, l;
+ unsigned char *bigp = (unsigned char*)big;
+ unsigned char *littlep = (unsigned char*)little;
+
+ for(i = 0, l = (int)(big_len - little_len); i <= l; i++, bigp++)
+ {
+ for(j = 0; j < little_len; j++)
+ {
+ if(littlep[j] != bigp[j])
+ break;
+ }
+
+ if(j == little_len)
+ return bigp;
+ }
+
+ return NULL;
+}
diff --git a/libpsx/src/libc/strings.c b/libpsx/src/libc/strings.c
new file mode 100644
index 0000000..76a5f9f
--- /dev/null
+++ b/libpsx/src/libc/strings.c
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <strings.h>
+#include <stdint.h>
+
+#define ffs_func(func_name, type) \
+\
+int func_name(type value) \
+{ \
+ int i; \
+ int nbits = sizeof(type) * 8;\
+ \
+ for(i = 0; i < nbits && !(value & ((type)1 << i) ); i++);\
+ \
+ return (i == nbits) ? 0 : (i + 1);\
+}
+
+ffs_func(ffs, int);
+ffs_func(ffsl, long);
+ffs_func(ffsll, long long);
+
+#define popcount_func(func_name, type) \
+\
+unsigned int func_name(type value) \
+{ \
+ int i, bitcnt; \
+ int nbits = sizeof(type) * 8; \
+ \
+ for(i = 0, bitcnt = 0; i < nbits; i++) \
+ {\
+ if( value & ((type)1 << i) )\
+ bitcnt++;\
+ }\
+ \
+ return bitcnt;\
+}
+
+popcount_func(popcount, unsigned int);
+popcount_func(popcountl, unsigned long);
+popcount_func(popcountll, unsigned long long);
+popcount_func(popcount32, uint32_t);
+popcount_func(popcount64, uint64_t);
diff --git a/libpsx/src/libc/unistd.c b/libpsx/src/libc/unistd.c
new file mode 100644
index 0000000..f64096b
--- /dev/null
+++ b/libpsx/src/libc/unistd.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+#include <unistd.h>
+
+void swab(const void *src, void *dst, ssize_t len)
+{
+ ssize_t x;
+ const unsigned char *srcp = src;
+ unsigned char *dstp = dst;
+
+ for(x = 0; x < len; x+=2)
+ {
+ dstp[x] = srcp[x + 1];
+
+ if( (x+1) < len )
+ dstp[x+1] = srcp[x];
+ }
+}
diff --git a/libpsx/src/memcard.c b/libpsx/src/memcard.c
new file mode 100644
index 0000000..16debf9
--- /dev/null
+++ b/libpsx/src/memcard.c
@@ -0,0 +1,87 @@
+/*
+ * PSXSDK Memory Card Helper Functions
+ *
+ * These functions help to manage memory card loading/saving
+ *
+ * Normal file functions can be used to do this, but it will be very tedious...
+ */
+
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <fcntl.h>
+ #include <memcard.h>
+ #include <string.h>
+ #include <psx.h>
+
+static unsigned char card_cmd[140];
+static unsigned char arr[140];
+
+unsigned char McReadSector(int card_slot, int sector, unsigned char *buffer)
+{
+ memset(&card_cmd[0], 0, 140);
+
+ card_cmd[0] = 0x81; /*MC access*/
+ card_cmd[1] = 0x52; /*Read command*/
+
+ /*Copy frame number to command*/
+ card_cmd[4] = sector >> 8; /*Frame MSB*/
+ card_cmd[5] = sector & 0xFF; /*Frame LSB*/
+
+ QueryPAD(card_slot, card_cmd, arr, sizeof(card_cmd));
+
+ /*Copy received frame data*/
+ memcpy(buffer, &arr[10], 128);
+
+ /*Return RW status*/
+ return arr[139];
+}
+
+unsigned char McWriteSector(int card_slot, int sector, unsigned char *buffer)
+{
+ int i;
+
+ memset(&card_cmd[0], 0, 140);
+
+ card_cmd[0] = 0x81; /*MC access*/
+ card_cmd[1] = 0x57; /*Write command*/
+
+ /*Copy frame number to command*/
+ card_cmd[4] = sector >> 8; /*Frame MSB*/
+ card_cmd[5] = sector & 0xFF; /*Frame LSB*/
+
+ memcpy(&card_cmd[6], buffer, 128);
+
+ /* Compute checksum */
+ for(i = 4, card_cmd[134] = 0; i < 134; i++)
+ card_cmd[134] ^= card_cmd[i];
+
+ QueryPAD(card_slot, card_cmd, arr, sizeof(card_cmd));
+
+ /*Return RW status*/
+ return arr[137];
+}
+
+unsigned int McGetStatus(int card_slot)
+{
+ unsigned int status = 0;
+
+ memset(&card_cmd[0], 0, 140);
+
+ card_cmd[0] = 0x81; /*MC access*/
+ card_cmd[1] = 0x52; /*Read command*/
+
+ /*Copy frame number to command*/
+ card_cmd[4] = 0;//sector >> 8; /*Frame MSB*/
+ card_cmd[5] = 0;//sector & 0xFF; /*Frame LSB*/
+
+ QueryPAD(card_slot, card_cmd, arr, sizeof(card_cmd));
+
+ if(arr[2] == 0x5a && arr[3] == 0x5d)
+ status |= MEMCARD_CONNECTED;
+
+ if(arr[6] == 'M' && arr[7] == 'C')
+ status |= MEMCARD_FORMATTED;
+
+ return status;
+}
diff --git a/libpsx/src/memory.c b/libpsx/src/memory.c
new file mode 100644
index 0000000..e6bb806
--- /dev/null
+++ b/libpsx/src/memory.c
@@ -0,0 +1,215 @@
+/*
+ * memory.c
+ *
+ * PSXSDK malloc() family functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern int __bss_start[];
+extern int __bss_end[];
+
+unsigned int first_free_page;
+
+// 1K granularity and "pages", so more allocations which are small can be done
+
+unsigned char busy_pages[2048];
+unsigned int alloc_size[2048];
+
+// RAM memory map on the PSX
+// 0x80000000 - 0x8000FFFF RAM used by the BIOS
+// 0x80010000 - 0x801FFFFF Program memory
+
+void malloc_setup()
+{
+ int x;
+
+ dprintf("malloc setup.\n");
+
+ first_free_page = (unsigned int) __bss_end;
+ first_free_page-= 0x80000000;
+
+ if(first_free_page & 0x3ff)
+ first_free_page = (first_free_page | 0x3ff) + 1;
+
+ first_free_page>>=10;
+
+ //printf("First free page: %d, bss_end: %x\n", first_free_page, __bss_end);
+
+ for(x = 0; x < first_free_page; x++)
+ {
+ busy_pages[x] = 1; // RAM occupied by program, data and BIOS looks always allocated
+ alloc_size[x] = 1; // Fake that 1K was required
+ }
+
+ for(x = first_free_page; x < 2048; x++)
+ {
+ busy_pages[x] = 0;
+ alloc_size[x] = 0;
+ }
+}
+
+void *malloc(size_t size)
+{
+ dprintf("malloc(%d)\n", size);
+
+ int x, y;
+
+// Round size
+
+ if(size & 0x3ff)
+ size = (size | 0x3ff) + 1;
+
+// Divide it by 1024
+ size >>= 10;
+ //printf("Allocating %dKb\n", size);
+
+// Find a free page
+ for(x = 0; x < 2048; x++)
+ {
+ if(busy_pages[x] == 0)
+ {
+ // If we find a free page, check how many free pages follow it.
+ // If it's enough for the memory we want to allocate, then return
+ // the pointer to the free page we found, otherwise keep finding
+
+ // printf("Page found at %dKb\n", x);
+
+ for(y = 0; y < size; y++)
+ if(busy_pages[x+y] == 1) goto malloc_keep_finding;
+
+ // We found the memory we wanted, now make it busy
+
+ for(y = 0; y < size; y++)
+ busy_pages[x+y] = 1;
+
+ // Store allocation size, it is essential for free()
+
+ alloc_size[x] = size;
+ // printf("malloc(): alloc_size[%d] = %d\n", x, size);
+
+ return (void*)((unsigned int)0x80000000 + (x<<10));
+ }
+malloc_keep_finding:
+ ; // Useless statement to make GCC not bail out...
+ }
+
+// We couldn't find anything, return NULL
+ return NULL;
+}
+
+void *calloc(size_t number, size_t size)
+{
+ void *ptr = malloc(number * size);
+ unsigned char *cptr = (unsigned char*)ptr;
+ int x;
+
+ if(ptr == NULL)
+ ptr = NULL;
+
+ for(x = 0; x < (number * size); x++)
+ cptr[x] = 0;
+
+ return ptr;
+}
+
+void free(void *ptr)
+{
+ dprintf("free(%x)\n", (unsigned int)ptr);
+
+ unsigned int ptri = (unsigned int)ptr;
+ ptri -= 0x80000000;
+ int x;
+
+ if((ptri & 0x3ff) || (busy_pages[ptri>>10] == 0) || (alloc_size[ptri>>10] == 0))
+ {
+ // If the pointer address is not a multiplier of 1K, or the page
+ // is free, it means that memory not allocated by malloc() was passed to free.
+ // Print a warning message and return.
+
+ printf("** free() ** : tried to free memory with invalid pointer at %x\n",
+ ptri + 0x80000000);
+
+ return;
+ }
+
+// Divide ptri by 1024, getting initial page
+
+ ptri>>=10;
+
+// printf("Freeing page at %dKb\n", ptri);
+
+// Free pages
+
+// printf("alloc_size[%d] = %d\n", ptri, alloc_size[ptri]);
+
+ for(x = 0; x < alloc_size[ptri]; x++)
+ {
+ dprintf("ptri + x = %d\n", ptri + x);
+ busy_pages[ptri + x] = 0;
+ }
+
+// Set allocation size to 0, finally freeing initial page
+
+ alloc_size[ptri] = 0;
+
+ /*for(x=150;x<170;x++)
+ printf("%d: %d, %d\n", x, busy_pages[x], alloc_size[x]);*/
+}
+
+void *realloc(void *ptr, size_t size)
+{
+ unsigned int ptri = (unsigned int)ptr;
+ int x;
+ void *newptr;
+
+ if(ptr == NULL)
+ return malloc(size);
+
+ ptri -= 0x80000000;
+
+ size |= 0x3ff;
+ size++;
+
+ size>>=10;
+
+ if((ptri & 0x3ff) || (busy_pages[ptri>>10] == 0) || (alloc_size[ptri>>10] == 0))
+ {
+ // If the pointer address is not a multiplier of 1K, or the page
+ // is free, it means that memory not allocated by malloc() was passed to realloc.
+ // Print a warning message and return.
+
+ printf("** realloc() ** : tried to reallocate memory with invalid pointer at %x\n",
+ ptri + 0x80000000);
+
+ return NULL;
+ }
+
+// Get page
+
+ ptri>>=10;
+
+ if(size < alloc_size[ptri]) // New size smaller than old size
+ {
+ for(x = size; x < alloc_size[ptri]; x++)
+ busy_pages[ptri + x] = 0;
+
+ alloc_size[ptri] = size;
+ }
+ else if(size > alloc_size[ptri]) // New size bigger than old size
+ {
+ newptr = malloc(size * 1024);
+
+ if(newptr == NULL)
+ return NULL;
+
+ memcpy(newptr, ptr, alloc_size[ptri]);
+ free(ptr);
+
+ ptr = newptr;
+ }
+
+ return ptr;
+}
diff --git a/libpsx/src/memory.h b/libpsx/src/memory.h
new file mode 100644
index 0000000..39985b2
--- /dev/null
+++ b/libpsx/src/memory.h
@@ -0,0 +1,6 @@
+#ifndef _PSXSDK_MEMORY_H
+#define _PSXSDK_MEMORY_H
+
+void malloc_setup();
+
+#endif \ No newline at end of file
diff --git a/libpsx/src/pad.c b/libpsx/src/pad.c
new file mode 100644
index 0000000..ea3e20e
--- /dev/null
+++ b/libpsx/src/pad.c
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <psx.h>
+
+/*
+ * Flags taken from PCSX
+ */
+
+// Status Flags
+#define TX_RDY 0x0001
+#define RX_RDY 0x0002
+#define TX_EMPTY 0x0004
+#define PARITY_ERR 0x0008
+#define RX_OVERRUN 0x0010
+#define FRAMING_ERR 0x0020
+#define SYNC_DETECT 0x0040
+#define DSR 0x0080
+#define CTS 0x0100
+#define IRQ 0x0200
+
+// Control Flags
+#define TX_PERM 0x0001
+#define DTR 0x0002
+#define RX_PERM 0x0004
+#define BREAK 0x0008
+#define RESET_ERR 0x0010
+#define RTS 0x0020
+#define PADSIO_RESET 0x0040
+
+/*
+ from BlackBag/Nagra PSX
+
+ 0x1f801040 - unsigned char data;
+ 0x1f801044 - unsigned short status;
+ 0x1f80104a - unsigned short cntl;
+ 0x1f80104e - unsigned short baud;
+*/
+
+#define PADSIO_DATA(x) *((volatile unsigned char*)(0x1f801040 + (x<<4)))
+#define PADSIO_STATUS(x) *((volatile unsigned short*)(0x1f801044 + (x<<4)))
+#define PADSIO_MODE(x) *((volatile unsigned short*)(0x1f801048 + (x<<4)))
+#define PADSIO_CTRL(x) *((volatile unsigned short*)(0x1f80104a + (x<<4)))
+#define PADSIO_BAUD(x) *((volatile unsigned short*)(0x1f80104e + (x<<4)))
+
+unsigned char readpad_vibrations[4][2];
+int querypad_rxrdy = 1;
+
+void QueryPAD(int pad_n, unsigned char *in, unsigned char *out, int len)
+{
+ int x;
+ volatile int y; // specified as volatile to not make busy loops get optimized out
+ int i;
+ unsigned char TempData;
+ int EmuFlag = 0;
+
+ PADSIO_MODE(0) = 0xD;
+ PADSIO_BAUD(0) = 0x88;
+
+ if(pad_n == 1) PADSIO_CTRL(0) = 0x3003; else PADSIO_CTRL(0) = 0x1003;
+
+ /*Get the initial command (usually 0x01 or 0x81)*/
+ TempData = *in;
+
+ for(y=0;y<400;y++); /*Slight delay before first transmission*/
+
+ for(x = 0; x < len; x++)
+ {
+ /*Must use timeouts or else program hangs on emulators*/
+ if(!EmuFlag)
+ {
+ for(y=0;y<1000;y++)
+ {
+ /*Wait for TX ready*/
+ if(PADSIO_STATUS(0) & 4)break;
+ }
+ }
+
+ PADSIO_DATA(0) = *in;
+ in++;
+
+ if(!EmuFlag)
+ {
+ /*Check ACK only for Memory Cards*/
+ if(TempData == 0x81 || x == 0)
+ {
+ for(y=0;y<2000;y++)
+ {
+ /*Check for ACK signal*/
+ if(PADSIO_STATUS(0) & 128)break;
+ }
+ }
+
+ for(i=0;i<100;i++)
+ {
+ /*Read RX status flag, required for Xebra*/
+ if(PADSIO_STATUS(0) & 2)break;
+ }
+ }
+
+ *out = PADSIO_DATA(0);
+
+ /*This is emulator, valid data was received without ACK, ePSXe and PCSX*/
+ if(x == 0 && y > 1900 && *out != 0xFF)EmuFlag = 1;
+
+ out++;
+ }
+
+ PADSIO_CTRL(0) = 0;
+}
+
+void pad_read_raw(int pad_n, unsigned char *arr)
+{
+ // arr must be at least 16 bytes long...
+
+ unsigned char pad_cmd[PAD_READ_RAW_SIZE] = {1,0x42,0,0,0};
+
+ pad_cmd[3] = readpad_vibrations[pad_n][0];
+ pad_cmd[4] = readpad_vibrations[pad_n][1];
+
+ QueryPAD(pad_n, pad_cmd, arr, sizeof(pad_cmd));
+}
+
+void pad_escape_mode(int pad_n, int enable)
+{
+ unsigned char pad_cmd[] = {1,0x43,0,enable?1:0,0};
+
+ QueryPAD(pad_n, pad_cmd, NULL, sizeof(pad_cmd));
+}
+
+void pad_enable_vibration(int pad_n)
+{
+ unsigned char pad_cmd[] = {1, 0x4d, 0, 0, 1, 0xff, 0xff, 0xff, 0xff};
+
+ pad_escape_mode(pad_n, 1); // Enter escape / configuration mode
+ QueryPAD(pad_n, pad_cmd, NULL, sizeof(pad_cmd));
+ pad_escape_mode(pad_n, 0); // Exit escape / configuration mode
+}
+
+void pad_set_vibration(int pad_n, unsigned char small, unsigned char big)
+{
+ if(pad_n >= 0 && pad_n <= 3)
+ {
+ readpad_vibrations[pad_n][0] = small;
+ readpad_vibrations[pad_n][1] = big;
+ }
+}
+
+/*
+ // Sony pads make this interface unpractical!
+void pad_set_analog(int pad_n, int lock)
+{
+ unsigned char pad_cmd[9] =
+ {1, 0x44, 0, 1, lock?3:0, 0, 0, 0, 0};
+ unsigned char pad_cmd2[9] =
+ {1, 0x4f, 0, 0xff, 0xff, 3, 0, 0, 0};
+
+ pad_escape_mode(pad_n, 1);
+ QueryPAD(pad_n, pad_cmd, NULL, sizeof(pad_cmd));
+ QueryPAD(pad_n, pad_cmd2, NULL, sizeof(pad_cmd));
+ pad_escape_mode(pad_n, 0);
+}*/
+
diff --git a/libpsx/src/psxsdk.c b/libpsx/src/psxsdk.c
new file mode 100644
index 0000000..a595bdb
--- /dev/null
+++ b/libpsx/src/psxsdk.c
@@ -0,0 +1,542 @@
+/*
+ * PSXSDK Library
+ *
+ * Free and open source library to develop for the Sony PlayStation
+ */
+
+#include <psx.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "exception.h"
+
+static const char *sysromver_unavail = "System ROM Version Unavailable";
+
+void (*vblank_handler_callback)();
+
+extern int *vblank_handler();
+
+void (*rcnt_handler_callback)();
+
+extern int *rcnt_handler();
+
+/*static unsigned int vblank_queue_buf[4] = {0x0, // Will contain next interrupt handler in queue
+ 0x0, // func1
+ (unsigned int)vblank_handler, // func2
+ 0x0, // pad
+ };*/
+
+static int vblank_handler_set = 0;
+static unsigned int vblank_handler_event_id = 0;
+
+static int rcnt_handler_set = 0;
+static unsigned int rcnt_handler_event_id = 0;
+unsigned int rcnt_handler_evfield;
+
+void _internal_cdromlib_init(void);
+
+static unsigned int psxSdkFlags = 0;
+
+static unsigned char *psxBiosState;
+
+extern void _96_remove(void);
+extern void _96_init(void);
+extern void InitCARD(void);
+extern void StartCARD(void);
+extern void StopCARD(void);
+extern void _bu_init(void);
+extern void BIOSWarmReboot(void);
+
+void PSX_InitEx(unsigned int flags)
+{
+ if(flags & PSX_INIT_NOBIOS)
+ {
+ printf("Entering No BIOS mode...\n");
+
+ __PSX_Init_NoBios();
+ goto _initex_end;
+ }
+
+ if(flags & PSX_INIT_SAVESTATE)
+ {
+// Save BIOS state
+// This simply copies the entire section of RAM used by the BIOS
+// in a buffer.
+ EnterCriticalSection();
+ psxBiosState = malloc(0x10000);
+ memcpy(psxBiosState, (void*)0x80000000, 0x10000);
+ ExitCriticalSection();
+ }
+
+ /* Reinitialize ISO 9660 filesystem driver */
+
+ if(flags & PSX_INIT_CD)
+ {
+ EnterCriticalSection();
+ _96_remove();
+ ExitCriticalSection();
+
+ _96_init();
+ }
+
+
+ /*This is needed, otherwise PSX will crash when VBlank handler is set*/
+ /*InitCARD(1);
+ StartCARD();
+ StopCARD();*/
+
+ if(flags & PSX_INIT_CD)
+ _internal_cdromlib_init();
+
+ printf("PSXSDK testing version !!!\n");
+
+ vblank_handler_set = 0;
+_initex_end:
+ psxSdkFlags = flags;
+}
+
+void PSX_Init(void)
+{
+ PSX_InitEx(PSX_INIT_CD);
+}
+
+void PSX_DeInit(void)
+{
+ if(psxSdkFlags & PSX_INIT_CD)
+ {
+ EnterCriticalSection();
+ _96_remove();
+ ExitCriticalSection();
+ }
+
+ RemoveVBlankHandler();
+
+ if(psxSdkFlags & PSX_INIT_SAVESTATE)// This must always be the last to be called!
+ PSX_RestoreBiosState();
+}
+
+void PSX_ReadPad(unsigned short *padbuf, unsigned short *padbuf2)
+{
+ int x;
+ unsigned char arr[PAD_READ_RAW_SIZE];
+ unsigned short *padbuf_a[2];
+
+// Now uses low level pad routines...
+ padbuf_a[0] = padbuf;
+ padbuf_a[1] = padbuf2;
+
+ for(x = 0; x < 2; x++)
+ {
+ pad_read_raw(x, arr);
+
+ if(arr[2] == 0x5a)
+ {
+ *padbuf_a[x] = (arr[3]<<8)|arr[4];
+ *padbuf_a[x] = ~*padbuf_a[x];
+ }
+ else
+ *padbuf_a[x] = 0;
+ }
+}
+
+unsigned char psxsdkPadArr[PAD_READ_RAW_SIZE];
+
+void PSX_PollPad(int pad_num, psx_pad_state *pad_state)
+{
+// int x;
+ /*unsigned short *padbuf_a[2];
+
+// Now uses low level pad routines...
+ padbuf_a[0] = padbuf;
+ padbuf_a[1] = padbuf2;
+
+ for(x = 0; x < 2; x++)
+ {
+ pad_read_raw(x, arr);
+
+ if(arr[2] == 0x5a)
+ {
+ *padbuf_a[x] = (arr[3]<<8)|arr[4];
+ *padbuf_a[x] = ~*padbuf_a[x];
+ }
+ else
+ *padbuf_a[x] = 0;
+ }*/
+
+ unsigned char *arr = psxsdkPadArr;
+
+ pad_read_raw(pad_num, arr);
+
+ pad_state->status = arr[0];
+ pad_state->id = arr[1];
+
+ pad_state->buttons = (arr[3]<<8)|arr[4];
+ pad_state->buttons = ~pad_state->buttons;
+
+ switch(pad_state->id)
+ {
+ case 0xFF:
+ pad_state->type = PADTYPE_NONE;
+ break;
+ case 0x41:
+ pad_state->type = PADTYPE_NORMALPAD;
+ break;
+ case 0x53:
+ pad_state->type = PADTYPE_ANALOGJOY;
+ pad_state->extra.analogJoy.x[0] = arr[5]-128;
+ pad_state->extra.analogJoy.y[0] = arr[6]-128;
+ pad_state->extra.analogJoy.x[1] = arr[7]-128;
+ pad_state->extra.analogJoy.y[1] = arr[8]-128;
+ break;
+ case 0x73:
+ pad_state->type = PADTYPE_ANALOGPAD;
+ pad_state->extra.analogPad.x[0] = arr[5]-128;
+ pad_state->extra.analogPad.y[0] = arr[6]-128;
+ pad_state->extra.analogPad.x[1] = arr[7]-128;
+ pad_state->extra.analogPad.y[1] = arr[8]-128;
+ break;
+ case 0x23:
+ pad_state->type = PADTYPE_NEGCON;
+ pad_state->extra.negCon.steering = arr[5]-128;
+ pad_state->extra.negCon.one = arr[6];
+ pad_state->extra.negCon.two = arr[7];
+ pad_state->extra.negCon.shoulder = arr[8];
+ break;
+ case 0x31:
+ pad_state->type = PADTYPE_KONAMIGUN;
+ break;
+ default:
+ pad_state->type = PADTYPE_UNKNOWN;
+ }
+}
+
+/*int PSX_GetPadType(unsigned int pad_num)
+//{
+ //#warning "Function does not currently work!"
+// unsigned char arr[16];
+
+ pad_read_raw(pad_num, arr);
+
+ switch(arr[1])
+ {
+ case 0xFF:
+ return PADTYPE_NONE;
+ break;
+ case 0x41:
+ return PADTYPE_NORMALPAD;
+ break;
+ case 0x53:
+ return PADTYPE_ANALOGJOY;
+ break;
+ case 0x73:
+ return PADTYPE_ANALOGPAD;
+ break;
+ }
+
+ return PADTYPE_UNKNOWN;
+}*/
+
+
+void PSX_GetSysInfo(struct psx_info *info)
+{
+ unsigned long i,i2;
+
+ info->kernel.version = GetKernelRomVersion();
+
+ i = GetKernelDate();
+
+/*
+ * Convert year from BCD to decimal
+ */
+
+ i2 = i >> 16;
+
+ info->kernel.year = i2 & 0xf;
+ info->kernel.year+= ((i2>>4)&0xf)*10;
+ info->kernel.year+= ((i2>>8)&0xf)*100;
+ info->kernel.year+= ((i2>>12)&0xf)*1000;
+
+/*
+ * Convert month from BCD to decimal
+ */
+ i2 = (i >> 8) & 0xff;
+
+ info->kernel.month = i2 & 0xf;
+ info->kernel.month+= (i2>>4) * 10;
+
+/*
+ * Convert day from BCD to decimal
+ */
+ i2 = i & 0xff;
+
+ info->kernel.day = i2 & 0xf;
+ info->kernel.day+= (i2>>4) * 10;
+
+/*
+ * Unless we receive something in the range >= 1 && <= 16,
+ * RAM size will be reported as 2 Megabytes
+ */
+
+ i = GetRamSize();
+
+ if(i == 0 || i > 16)
+ info->system.memory = 2<<20; /* 2 Megabytes */
+ else
+ info->system.memory <<= 20;
+}
+
+
+
+int get_real_file_size(const char *name)
+{
+ struct DIRENTRY dirent_buf;
+
+ if(firstfile(name, &dirent_buf) == &dirent_buf)
+ return dirent_buf.size;
+ else
+ return 0;
+}
+
+int get_file_size(const char *name)
+{
+ int i = get_real_file_size(name);
+
+ if(strncmp(name, "cdrom:", 6) == 0)
+ {
+ if(i & 0x7ff)
+ {
+ i += 0x800;
+ i &= ~0x7ff;
+ }
+ }else if(strncmp(name, "bu", 2) == 0)
+ {
+ if(i & 0x7f)
+ {
+ i += 0x80;
+ i &= ~0x7f;
+ }
+ }
+
+ return i;
+}
+
+int SetRCnt(int spec, unsigned short target, unsigned int mode)
+{
+ spec &= 0xf;
+
+ if(spec >= 3)
+ return 0;
+
+ RCNT_MODE(spec)=0;
+ RCNT_TARGET(spec)=target;
+ RCNT_MODE(spec)=mode;
+
+ return 1;
+}
+
+int GetRCnt(int spec)
+{
+ spec &= 0xf;
+
+ if(spec >= 4)
+ return -1;
+
+ return (RCNT_COUNT(spec) & 0xffff);
+}
+
+int StartRCnt(int spec)
+{
+ spec &= 0xf;
+
+ if(spec >= 3)
+ return 0;
+
+ IMASK |= 1 << (spec + 4);
+
+ return 1;
+}
+
+int StopRCnt(int spec)
+{
+ spec &= 0xf;
+
+ if(spec >= 3)
+ return 0;
+
+ IMASK ^= 1 << (spec + 4);
+
+ return 1;
+}
+
+void SetVBlankHandler(void (*callback)())
+{
+ if(psxSdkFlags & PSX_INIT_NOBIOS)
+ {
+ _EXC_vblank_handler_set = 0;
+ _EXC_vblank_handler = callback;
+ _EXC_vblank_handler_set = 1;
+ return;
+ }
+
+ if(vblank_handler_set == 1)
+ {
+ EnterCriticalSection();
+
+ vblank_handler_callback = callback;
+
+ ExitCriticalSection();
+
+ return;
+ }
+
+// Enter critical section
+
+ EnterCriticalSection();
+
+ IMASK|=1;
+
+ vblank_handler_event_id = OpenEvent(RCntCNT3, 2, 0x1000, vblank_handler);
+ EnableEvent(vblank_handler_event_id);
+
+ vblank_handler_callback = callback;
+ vblank_handler_set = 1;
+
+// Exit critical section
+
+ ExitCriticalSection();
+}
+
+void RemoveVBlankHandler(void)
+{
+ if(psxSdkFlags & PSX_INIT_NOBIOS)
+ {
+ _EXC_vblank_handler_set = 0;
+ _EXC_vblank_handler = NULL;
+ return;
+ }
+
+ if(vblank_handler_set)
+ {
+ EnterCriticalSection();
+
+ DisableEvent(vblank_handler_event_id);
+ CloseEvent(vblank_handler_event_id);
+
+ //IMASK^=1;
+ // ^ commented because masking out vblank could give problems to other bios functions
+
+ vblank_handler_set = 0;
+
+ ExitCriticalSection();
+ }
+}
+
+void SetRCntHandler(void (*callback)(), int spec, unsigned short target)
+{
+ if(psxSdkFlags & PSX_INIT_NOBIOS)
+ return; // Not yet supported in No-Bios Mode
+
+ if(rcnt_handler_set)
+ {
+ EnterCriticalSection();
+
+ rcnt_handler_callback = callback;
+
+ ExitCriticalSection();
+
+ return;
+ }
+
+// Enter critical section
+
+ SetRCnt(spec, target, RCntIntr | 0x08 | 0x10 | 0x40);
+ StartRCnt(spec);
+
+ EnterCriticalSection();
+ rcnt_handler_event_id = OpenEvent(spec, 2, 0x1000, rcnt_handler);
+ EnableEvent(rcnt_handler_event_id);
+
+ rcnt_handler_callback = callback;
+ rcnt_handler_set = spec;
+
+ switch(spec)
+ {
+ case RCntCNT0: rcnt_handler_evfield = 1 << 4; break;
+ case RCntCNT1: rcnt_handler_evfield = 1 << 5; break;
+ case RCntCNT2: rcnt_handler_evfield = 1 << 6; break;
+ case RCntCNT3: rcnt_handler_evfield = 1; break;
+ }
+
+// Exit critical section
+
+ ExitCriticalSection();
+}
+
+void RemoveRCntHandler(int spec)
+{
+ if(psxSdkFlags & PSX_INIT_NOBIOS)
+ return; // Not yet supported in No-Bios Mode
+
+ if(rcnt_handler_set)
+ {
+ EnterCriticalSection();
+
+ DisableEvent(rcnt_handler_event_id);
+ CloseEvent(rcnt_handler_event_id);
+
+ rcnt_handler_set = 0;
+
+ ExitCriticalSection();
+ }
+}
+
+const char *GetSystemRomVersion(void)
+{
+// Get pointer to zero-terminated string containing System ROM Version which is embedded in
+// most PlayStation BIOSes.
+
+// If getting the pointer is not possible, a pointer to a string saying "System ROM Unavailable" is returned.
+
+ int x;
+
+ for(x = 0x7ffee; x >= 0; x--)
+ if(memcmp("System ROM Version", (void*)(0xbfc00000 + x), 18) == 0)
+ return (char*)(0xbfc00000 + x);
+
+ return sysromver_unavail;
+}
+
+int PSX_RestoreBiosState(void)
+{
+ if(!(psxSdkFlags & PSX_INIT_SAVESTATE))
+ return 0; // can't restore BIOS state if it was not saved previously
+
+ EnterCriticalSection();
+ memcpy((void*)0x80000000, psxBiosState, 0x10000);
+ ExitCriticalSection();
+
+ return 1;
+}
+
+unsigned int PSX_GetInitFlags(void)
+{
+ return psxSdkFlags;
+}
+
+void PSX_WarmReboot(void)
+{
+ if(psxSdkFlags & PSX_INIT_NOBIOS)
+ {
+psx_warmreboot_nobios:
+ PSX_DeInit();
+ __asm__("j _start");
+ __asm__("nop");
+ }
+ else
+ {
+ if(!(psxSdkFlags & PSX_INIT_CD))
+ goto psx_warmreboot_nobios;
+
+ BIOSWarmReboot();
+ }
+}
diff --git a/libpsx/src/runexe/runexe.c b/libpsx/src/runexe/runexe.c
new file mode 100644
index 0000000..5fe22b0
--- /dev/null
+++ b/libpsx/src/runexe/runexe.c
@@ -0,0 +1,76 @@
+/**
+ * PSXSDK
+ *
+ * RunEXE functionality
+ */
+
+#include <psx.h>
+#include <stdio.h>
+#include <string.h>
+#include <runexe.h>
+
+// Include runexe stage2 position independent code
+
+extern void PSX_RunExeStage2();
+extern void PSX_RunExeStage2_END();
+
+static void *runExeBuffer;
+
+int PSX_RunExe(void *exeBuffer)
+{
+ //unsigned char *exe = exeBuffer;
+ unsigned int *exe = exeBuffer;
+// Size of position indipendent code
+ int sz = PSX_RunExeStage2_END -
+ PSX_RunExeStage2;
+
+ if(!(PSX_GetInitFlags() & PSX_INIT_SAVESTATE))
+ {
+ printf("RunExe error: State was not saved!\n");
+ return 0;
+ }
+
+ if(((unsigned int)exeBuffer & 3))
+ {
+ printf("RunExe: Alignment violation.\n");
+ printf("Specified buffer must have an address which is a multiplier of 4.\n");
+ return 0;
+ }
+
+ // Check magic string
+ if(strcmp(exeBuffer, "PS-X EXE") != 0)
+ return 0; // Return failure if magic string couldn't be found
+
+ runExeBuffer = exeBuffer;
+
+ printf("PSXSDK RunExe Debug Information\n");
+ printf("t_addr = %08x\n", exe[6]);
+ printf("t_size = %08x\n", exe[7]);
+ printf("d_addr = %08x\n", exe[8]);
+ printf("d_size = %08x\n", exe[9]);
+ printf("b_addr = %08x\n", exe[10]);
+ printf("b_size = %08x\n", exe[11]);
+ printf("s_addr = %08x\n", exe[12]);
+ printf("s_size = %08x\n", exe[13]);
+
+ printf("OK..! Farewell.\n");
+
+// Deinitialize the PSXSDK library
+ PSX_DeInit();
+
+// Copy the position independent stage2 code in high memory
+ memcpy((void*)0x801fef00, PSX_RunExeStage2, sz);
+
+// Stack is unusable now! Do not do anything else except calling the stage2 code.
+
+// Jump to the stage2 code without using the stack..
+
+ __asm__("la $a0, runExeBuffer");
+ __asm__("nop");
+ __asm__("lw $a0, 0($a0)");
+ __asm__("j 0x801fef00");
+ __asm__("nop");
+
+// This is never reached but keep the compiler happy
+ return 0;
+}
diff --git a/libpsx/src/runexe/stage2.s b/libpsx/src/runexe/stage2.s
new file mode 100644
index 0000000..e60e109
--- /dev/null
+++ b/libpsx/src/runexe/stage2.s
@@ -0,0 +1,40 @@
+.global PSX_RunExeStage2
+.global PSX_RunExeStage2_END
+
+PSX_RunExeStage2:
+# a0 = address of PSX-EXE buffer
+
+# Load initial program counter in t0
+ lw $t0, 0x10($a0)
+# Load text section address in t1
+ lw $t1, 0x18($a0)
+# Load text section size in t2
+ lw $t2, 0x1c($a0)
+
+# Load initial sp register value in sp
+ lw $sp, 0x30($a0)
+
+# t3 = current source address
+ addiu $t3, $a0, 0x800
+
+# t4 = current destination address
+ addu $t4, $t1, $zero
+
+copyExeLoop:
+ beq $t2, $zero, copyExeLoop_End
+ nop
+copyExeLoop_2:
+ lb $t5, 0($t3)
+ nop
+ sb $t5, 0($t4)
+ addiu $t3, $t3, 1
+ addiu $t4, $t4, 1
+ addiu $t2, $t2, -1
+ beq $zero, $zero, copyExeLoop
+
+copyExeLoop_End:
+# Jump to the program we loaded
+ jr $t0
+ nop
+
+PSX_RunExeStage2_END:
diff --git a/libpsx/src/setup.c b/libpsx/src/setup.c
new file mode 100644
index 0000000..d77f062
--- /dev/null
+++ b/libpsx/src/setup.c
@@ -0,0 +1,50 @@
+#include <psx.h>
+#include <stdio.h>
+#include "memory.h"
+
+extern int __bss_start[];
+extern int __bss_end[];
+
+extern void *__ctor_list;
+extern void *__ctor_end;
+
+
+// Function to call static constructors (for C++, etc.)
+static void call_ctors(void)
+{
+ dprintf("Calling static constructors...\n");
+
+ void **p = &__ctor_end - 1;
+
+ for(--p; *p != NULL && (int)*p != -1 && p > &__ctor_list; p--)
+ {
+ dprintf("Constructor address = %x\n", (unsigned int)*p);
+ (*(void (**)())p)();
+ }
+
+ dprintf("Finished calling static constructors\n");
+}
+
+void psxsdk_setup()
+{
+ unsigned int x;
+
+ printf("Initializing PSXSDK... \n");
+
+ dprintf("Clearing BSS space...\n");
+
+// Clear BSS space
+ for(x = (unsigned int)__bss_start; x < (unsigned int)__bss_end; x++)
+ *((unsigned char*)x) = 0;
+
+ dprintf("BSS space cleared.\n");
+
+// Setup memory allocation functions
+ malloc_setup();
+
+ dprintf("Finished setting up memory allocation functions.\n");
+
+// Call static constructors
+ call_ctors();
+
+}
diff --git a/libpsx/src/sio.c b/libpsx/src/sio.c
new file mode 100644
index 0000000..5d399d3
--- /dev/null
+++ b/libpsx/src/sio.c
@@ -0,0 +1,64 @@
+/*
+ * SIO communication library for PSXSDK.
+ * Originally written by Shendo.
+ * Thanks to Martin Korth of the NO$PSX for documentation.
+ *
+ * This library is accessing SIO registers directly, no BIOS routines are used.
+ */
+
+#include <psx.h>
+#include <psxsio.h>
+#include <stdio.h>
+
+void SIOStart(int bitrate)
+{
+ /*Set to 8N1 mode with desired bitrate*/
+ SIOStartEx(bitrate, SIO_DATA_LEN_8, SIO_PARITY_NONE, SIO_STOP_BIT_1);
+}
+
+void SIOStartEx(int bitrate, int datalength, int parity, int stopbit)
+{
+ /*Set SIO_MODE register, bitrate reload factor set to MUL16 by default*/
+ SIO_MODE = SIO_REL_MUL16 | (datalength << 2) | (parity << 4) | (stopbit << 6);
+
+ /*Reset SIO_CTRL register.*/
+ SIO_CTRL = 0;
+
+ /*Set TX and RT to enabled, no handshaking signals.*/
+ SIO_CTRL = 1 | (1 << 2);
+
+ /*Calculate bitrate reload value based on the given bitrate
+ * Reload = SystemClock (33 Mhz) / (Factor (MULI16) * bitrate)*/
+ SIO_BPSV = 0x204CC00 / (16 * bitrate);
+}
+
+void SIOStop()
+{
+ /*Set all SIO related registers to zero*/
+ SIO_MODE = 0;
+ SIO_CTRL = 0;
+ SIO_BPSV = 0;
+}
+
+unsigned char SIOReadByte()
+{
+ return (unsigned char)SIO_TX_RX;
+}
+
+void SIOSendByte(unsigned char data)
+{
+ SIO_TX_RX = data;
+}
+
+int SIOCheckInBuffer()
+{
+ /*Return status of RX FIFO*/
+ return (SIO_STAT & 0x2)>0;
+}
+
+int SIOCheckOutBuffer()
+{
+ /*Return status of TX Ready flag*/
+ return (SIO_STAT & 0x4)>0;
+}
+ \ No newline at end of file
diff --git a/libpsx/src/spu.c b/libpsx/src/spu.c
new file mode 100644
index 0000000..441acb6
--- /dev/null
+++ b/libpsx/src/spu.c
@@ -0,0 +1,269 @@
+/**
+ * PSXSDK
+ *
+ * Sound Processing Unit Functions
+ * Based on code from James Higgs's PSX lib and on code by bitmaster
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <psx.h>
+
+static unsigned int ss_vag_addr;
+
+void SsVoiceVol(int voice, unsigned short left, unsigned short right)
+{
+ unsigned short *a = (unsigned short*)SPU_VOICE_BASE_ADDR(voice);
+
+ a[0] = left;
+ a[1] = right;
+}
+
+void SsVoicePitch(int voice, unsigned short pitch)
+{
+ unsigned short *a = (unsigned short*)SPU_VOICE_BASE_ADDR(voice);
+
+ a[2] = pitch;
+}
+
+void SsVoiceStartAddr(int voice, unsigned int addr)
+{
+// address given is real address, then it is divided by eight when written to the register
+// example: SSVoiceStartAddr(0, 0x1008) , writes 0x201 on the register which means 0x1008
+
+ unsigned short *a = (unsigned short*)SPU_VOICE_BASE_ADDR(voice);
+
+ a[3] = (addr >> 3);
+}
+
+void SsVoiceADSRRaw(int voice, unsigned short level, unsigned short rate)
+{
+ unsigned short *a = (unsigned short*)SPU_VOICE_BASE_ADDR(voice);
+
+ a[4] = level;
+ a[5] = rate;
+}
+
+void SsVoiceRepeatAddr(int voice, unsigned int addr)
+{
+// only valid after KeyOn
+// the explanation for SSVoiceStartAddr() is valid for this function as well
+
+ unsigned short *a = (unsigned short*)SPU_VOICE_BASE_ADDR(voice);
+
+ a[7] = (addr >> 3);
+}
+
+void SsKeyOn(int voice)
+{
+ unsigned int i = 1 << voice;
+
+ SPU_KEY_ON1 = i & 0xffff;
+ SPU_KEY_ON2 = i >> 16;
+
+/* while(SPU_KEY_ON1 != (i & 0xffff));
+ while(SPU_KEY_ON2 != (i >> 16));
+*/
+}
+
+void SsKeyOff(int voice)
+{
+ unsigned int i = 1 << voice;
+
+ SPU_KEY_OFF1 = i & 0xffff;
+ SPU_KEY_OFF2 = i >> 16;
+}
+
+
+
+void SsKeyOnMask(int mask)
+{
+ SPU_KEY_ON1 = mask & 0xffff;
+ SPU_KEY_ON2 = mask >> 16;
+}
+
+void SsKeyOffMask(int mask)
+{
+ SPU_KEY_OFF1 = mask & 0xffff;
+ SPU_KEY_OFF2 = mask >> 16;
+}
+
+void SsWait()
+{
+ while(SPU_STATUS2 & 0x7ff);
+}
+
+void SsInit()
+{
+ int x;
+
+ printf("Initializing SPU (Sound Synthesizer)...\n");
+
+ DPCR |= 0xB0000;
+
+ SPU_MVOL_L = 0x3fff;
+ SPU_MVOL_R = 0x3fff;
+
+ SPU_CONTROL = 0x0;
+ SsWait();
+
+ SPU_STATUS = 0x4; // Must be done, but not totally understood
+
+ while(SPU_STATUS2 & 0x7ff);
+
+ SPU_REVERB_L = 0x0;
+ SPU_REVERB_R = 0x0;
+
+ // All keys off
+
+ SPU_KEY_OFF1 = 0xFFFF;
+ SPU_KEY_OFF2 = 0xFFFF;
+
+ // Switch FM, reverb and noise off
+ SPU_KEY_FM_MODE1 = 0x0;
+ SPU_KEY_FM_MODE2 = 0x0;
+ SPU_KEY_NOISE_MODE1 = 0x0;
+ SPU_KEY_NOISE_MODE2 = 0x0;
+ SPU_KEY_REVERB_MODE1 = 0x0;
+ SPU_KEY_REVERB_MODE2 = 0x0;
+
+ // set CD master volume to 0 (mute it)
+ SPU_CD_MVOL_L = 0x0;
+ SPU_CD_MVOL_R = 0x0;
+
+ // set external input volume to 0 (mute it)
+ SPU_EXT_VOL_L = 0x0;
+ SPU_EXT_VOL_R = 0x0;
+
+ // set volume of all voices to 0 and adsr to 0,0
+
+ for(x = 0; x < 24; x++)
+ {
+ SsVoiceVol(x, 0, 0);
+ SsVoiceADSRRaw(x, 0, 0);
+ }
+
+ SsWait();
+
+ SPU_CONTROL = 0xC000; // SPU is on
+ SPU_REVERB_WORK_ADDR = 0xFFFE; // Reverb work address in SPU memory, 0x1fff * 8 = 0xFFF8
+
+ ss_vag_addr = SPU_DATA_BASE_ADDR;
+
+ printf("SPU/SS Initialized.\n");
+}
+
+// This implementation of SsUpload() was contributed by Shendo
+// It waits either for a period of time or for the status flags to be raised, whichever comes first.
+// This makes it work also on ePSXe, which never raises the status flags.
+
+void SsUpload(void *addr, int size, int spu_addr)
+{
+ unsigned short *ptr = addr;
+ int i;
+
+ while(size > 0)
+ {
+ SPU_STATUS = 4; // Sound RAM Data Transfer Control
+ SPU_CONTROL = SPU_CONTROL & ~0x30; // SPUCNT.transfer_mode = 0 (STOP)
+
+
+ for(i = 0; i < 100; i++)
+ if(((SPU_STATUS2 >> 4) & 3) == 0)break; // wait until SPUSTAT.transfer is 0 (STOP)
+
+ SPU_ADDR = spu_addr >> 3;
+
+ for(i = 0; i < 32; i++)
+ SPU_DATA = ptr[i];
+
+ SPU_CONTROL = (SPU_CONTROL & ~0x30) | 16; // SPUCNT.transfer_mode = 1 (MANUAL)
+
+ for(i = 0; i < 100; i++)
+ if(((SPU_STATUS2 >> 4) & 3) == 1)break; // wait until SPUSTAT.transfer is 1 (MANUAL)
+
+ while(SPU_STATUS2 & 0x400); // wait for transfer busy bit to be cleared
+
+ spu_addr += 64;
+ ptr += 32;
+ size-=64;
+ }
+}
+
+unsigned short SsFreqToPitch(int hz)
+{
+// Converts a normal samples per second frequency value in Hz
+// in a pitch value
+
+// i.e. 44100 -> 0x1000, 22050 -> 0x800
+
+ return (hz << 12) / 44100;
+}
+
+int SsReadVag(SsVag *vag, void *data)
+{
+ unsigned char *i = data;
+
+ if(strncmp(data, "VAGp", 4) != 0)
+ return 0;
+
+ vag->version = (i[4]<<24)|(i[5]<<16)|(i[6]<<8)|i[7];
+ vag->data_size = (i[12]<<24)|(i[13]<<16)|(i[14]<<8)|i[15];
+ vag->sample_rate = (i[16]<<24)|(i[17]<<16)|(i[18]<<8)|i[19];
+ memcpy(vag->name, &i[32], 16);
+ vag->data = &i[48];
+
+ return 1;
+}
+
+void SsUploadVagEx(SsVag *vag, int spu_addr)
+{
+ vag->spu_addr = spu_addr;
+ SsUpload(vag->data, vag->data_size, vag->spu_addr);
+ //spu_addr += vag->data_size;
+}
+
+void SsUploadVag(SsVag *vag)
+{
+ vag->spu_addr = ss_vag_addr;
+ SsUploadVagEx(vag, ss_vag_addr);
+ ss_vag_addr += vag->data_size;
+}
+
+void SsPlayVag(SsVag *vag, unsigned char voice, unsigned short vl,
+ unsigned short vr)
+{
+ SsVoicePitch(voice, SsFreqToPitch(vag->sample_rate));
+ SsVoiceStartAddr(voice, vag->spu_addr);
+ SsVoiceVol(voice, vl, vr);
+ SsKeyOn(voice);
+
+ vag->cur_voice = voice;
+}
+
+void SsStopVag(SsVag *vag)
+{
+ SsKeyOff(vag->cur_voice);
+ vag->cur_voice = -1;
+}
+
+void SsResetVagAddr()
+{
+ ss_vag_addr = SPU_DATA_BASE_ADDR;
+}
+
+void SsEnableCd()
+{
+ SPU_CONTROL |= 1;
+ CdSendCommand(CdlDemute, 0);
+}
+
+void SsEnableExt()
+{
+ SPU_CONTROL |= 2;
+}
+
+void SsCdVol(unsigned short left, unsigned short right)
+{
+ SPU_CD_MVOL_L = left;
+ SPU_CD_MVOL_R = right;
+}
diff --git a/libpsx/src/start/start.s b/libpsx/src/start/start.s
new file mode 100755
index 0000000..5c72f79
--- /dev/null
+++ b/libpsx/src/start/start.s
@@ -0,0 +1,322 @@
+# This is the start code for the PSXSDK.
+# It sets needed things up, and calls the setup and the main function.
+
+# This has to be linked in as the first object when using ld, so that it
+# appears at the start of the .text section.
+# If a ldscript is being used, it is sufficient to specify this as the first
+# startup object.
+
+# nextvolume (2014-03-17):
+# fixed possible stack corruption.
+
+ .align 16
+ .text
+.global _start
+#.global exit
+.extern call_atexit_callbacks
+#.global vblank_handler
+.extern vblank_handler
+.extern vblank_handler_callback
+
+.extern rcnt_handler
+.extern rcnt_handler_evfield
+.extern rcnt_handler_callback
+
+.extern run_bios
+.extern is_load_delay_ok
+.extern exit
+
+.extern get_cop0_status
+.extern set_cop0_status
+.extern get_cop0_epc
+#.global run_bios
+#.global is_load_delay_ok
+
+_start:
+ li $29, 0x801fff00 # Load stack pointer
+ li $k1, 0x1f800000 # set to hardware base
+
+ addiu $sp, $sp, -24
+ jal ResetEntryInt
+ nop
+
+ jal psxsdk_setup
+ nop
+
+ jal main
+ nop
+
+_real_exit:
+ la $a0, progtermfmt
+ move $a1, $v0
+
+ jal printf
+ nop
+
+ jal call_atexit_callbacks
+ nop
+
+inf_loop:
+ j inf_loop
+ nop
+
+# VBlank handler
+
+vblank_handler:
+ addi $sp, -120
+.set noat
+ sw $at, 0($sp)
+ mfhi $at
+ sw $at, 112($sp)
+ mflo $at
+ sw $at, 116($sp)
+.set at
+ sw $v0, 4($sp)
+ sw $v1, 8($sp)
+ sw $a0, 12($sp)
+ sw $a1, 16($sp)
+ sw $a2, 20($sp)
+ sw $a3, 24($sp)
+ sw $t0, 28($sp)
+ sw $t1, 32($sp)
+ sw $t2, 36($sp)
+ sw $t3, 40($sp)
+ sw $t4, 44($sp)
+ sw $t5, 48($sp)
+ sw $t6, 52($sp)
+ sw $t7, 56($sp)
+ sw $s0, 60($sp)
+ sw $s1, 64($sp)
+ sw $s2, 68($sp)
+ sw $s3, 72($sp)
+ sw $s4, 76($sp)
+ sw $s5, 80($sp)
+ sw $s6, 84($sp)
+ sw $s7, 88($sp)
+ sw $t8, 92($sp)
+ sw $t9, 96($sp)
+ sw $gp, 100($sp)
+ sw $s8, 104($sp)
+ sw $ra, 108($sp)
+
+vblank_fire_user_handler:
+
+ la $t0, vblank_handler_callback
+ lw $t1, 0($t0)
+
+ addiu $sp, $sp, -24
+ jalr $t1
+ nop
+ addiu $sp, $sp, 24
+
+vblank_acknowledge_irq:
+ li $t0, 0x1f801070 # IPENDING
+
+ lw $t1, 0($t0)
+ nop
+ nop
+ xori $t1, $t1, 1 # Acknowledge VBlank IRQ
+ sw $t1, 0($t0)
+
+vblank_handler_end:
+.set noat
+ lw $at, 112($sp)
+ nop
+ mthi $at
+ lw $at, 116($sp)
+ nop
+ mtlo $at
+ lw $at, 0($sp)
+.set at
+ lw $v0, 4($sp)
+ lw $v1, 8($sp)
+ lw $a0, 12($sp)
+ lw $a1, 16($sp)
+ lw $a2, 20($sp)
+ lw $a3, 24($sp)
+ lw $t0, 28($sp)
+ lw $t1, 32($sp)
+ lw $t2, 36($sp)
+ lw $t3, 40($sp)
+ lw $t4, 44($sp)
+ lw $t5, 48($sp)
+ lw $t6, 52($sp)
+ lw $t7, 56($sp)
+ lw $s0, 60($sp)
+ lw $s1, 64($sp)
+ lw $s2, 68($sp)
+ lw $s3, 72($sp)
+ lw $s4, 76($sp)
+ lw $s5, 80($sp)
+ lw $s6, 84($sp)
+ lw $s7, 88($sp)
+ lw $t8, 92($sp)
+ lw $t9, 96($sp)
+ lw $gp, 100($sp)
+ lw $s8, 104($sp)
+ lw $ra, 108($sp)
+ addi $sp, 120
+ jr $ra
+ nop
+
+# Root counter handler
+
+rcnt_handler:
+ addi $sp, -120
+.set noat
+ sw $at, 0($sp)
+ mfhi $at
+ sw $at, 112($sp)
+ mflo $at
+ sw $at, 116($sp)
+.set at
+ sw $v0, 4($sp)
+ sw $v1, 8($sp)
+ sw $a0, 12($sp)
+ sw $a1, 16($sp)
+ sw $a2, 20($sp)
+ sw $a3, 24($sp)
+ sw $t0, 28($sp)
+ sw $t1, 32($sp)
+ sw $t2, 36($sp)
+ sw $t3, 40($sp)
+ sw $t4, 44($sp)
+ sw $t5, 48($sp)
+ sw $t6, 52($sp)
+ sw $t7, 56($sp)
+ sw $s0, 60($sp)
+ sw $s1, 64($sp)
+ sw $s2, 68($sp)
+ sw $s3, 72($sp)
+ sw $s4, 76($sp)
+ sw $s5, 80($sp)
+ sw $s6, 84($sp)
+ sw $s7, 88($sp)
+ sw $t8, 92($sp)
+ sw $t9, 96($sp)
+ sw $gp, 100($sp)
+ sw $s8, 104($sp)
+ sw $ra, 108($sp)
+
+rcnt_fire_user_handler:
+ la $t0, rcnt_handler_callback
+ lw $t1, 0($t0)
+
+ addiu $sp, $sp, -24
+ jalr $t1
+ nop
+ addiu $sp, $sp, 24
+
+rcnt_acknowledge_irq:
+ li $t0, 0x1f801070 # IPENDING
+ la $t2, rcnt_handler_evfield
+
+ lw $t1, 0($t0)
+ nop
+ nop
+ xor $t1, $t1, $t2 # Acknowledge Root Counter IRQ
+ sw $t1, 0($t0)
+
+rcnt_handler_end:
+.set noat
+ lw $at, 112($sp)
+ nop
+ mthi $at
+ lw $at, 116($sp)
+ nop
+ mtlo $at
+ lw $at, 0($sp)
+.set at
+ lw $v0, 4($sp)
+ lw $v1, 8($sp)
+ lw $a0, 12($sp)
+ lw $a1, 16($sp)
+ lw $a2, 20($sp)
+ lw $a3, 24($sp)
+ lw $t0, 28($sp)
+ lw $t1, 32($sp)
+ lw $t2, 36($sp)
+ lw $t3, 40($sp)
+ lw $t4, 44($sp)
+ lw $t5, 48($sp)
+ lw $t6, 52($sp)
+ lw $t7, 56($sp)
+ lw $s0, 60($sp)
+ lw $s1, 64($sp)
+ lw $s2, 68($sp)
+ lw $s3, 72($sp)
+ lw $s4, 76($sp)
+ lw $s5, 80($sp)
+ lw $s6, 84($sp)
+ lw $s7, 88($sp)
+ lw $t8, 92($sp)
+ lw $t9, 96($sp)
+ lw $gp, 100($sp)
+ lw $s8, 104($sp)
+ lw $ra, 108($sp)
+ addi $sp, 120
+ jr $ra
+ nop
+
+get_cop0_status:
+ mfc0 $v0, $12
+ jr $ra
+ nop
+
+set_cop0_status:
+ mtc0 $a0, $12
+ jr $ra
+ nop
+
+get_cop0_epc:
+ mfc0 $2, $14
+ jr $ra
+ nop
+
+run_bios:
+ j 0xbfc00000
+ nop
+
+is_load_delay_ok:
+ li $t2, 0
+ li $t0, 0x0adecade
+ la $t1, isldo_data
+ .word 0x8d2a0000 #lw t2,0(t1)
+ beq $t0, $t2, load_delay_not_ok
+ nop
+load_delay_ok:
+ li $v0, 1
+ jr $ra
+ nop
+load_delay_not_ok:
+ li $v0, 0
+ jr $ra
+ nop
+
+exit:
+ move $s0, $a0
+
+ la $a0, called_exit
+ move $a1, $s0
+
+ addiu $sp, $sp, -24
+ jal printf
+ nop
+ addiu $sp, $sp, 24
+
+ move $v0, $s0
+
+ j _real_exit
+ nop
+
+ .align 16
+ .data
+
+called_exit:
+ .string "Called exit(%d)\n"
+
+progtermfmt:
+ .string "Program terminated with return value %d\n"
+
+isldo_data:
+ .word 0x0adecade
diff --git a/libpsx/src/syscalls.s b/libpsx/src/syscalls.s
new file mode 100644
index 0000000..305d03e
--- /dev/null
+++ b/libpsx/src/syscalls.s
@@ -0,0 +1,344 @@
+# PSX SDK System calls file
+
+.text
+
+# Console functions
+
+.global bios_putchar
+.global bios_puts
+.global printf
+.extern __stdio_direction
+.extern sio_printf
+
+bios_putchar:
+ li $9, 0x3c
+ j 0xa0
+ nop
+
+bios_puts:
+ li $9, 0x3e
+ j 0xa0
+ nop
+
+printf:
+ la $9, __stdio_direction
+ lw $10, 0($9)
+ beq $10, $0, use_bios_printf
+ nop
+ j sio_printf
+ nop
+use_bios_printf:
+ li $9, 0x3f
+ j 0xa0
+ nop
+
+# Memory functions
+# These are not used by PSXSDK, and are here just for completeness.
+
+.global InitHeap
+.global FlushCache
+
+InitHeap:
+ li $9, 0x39
+ j 0xa0
+ nop
+
+FlushCache:
+ li $9, 0x44
+ j 0xa0
+ nop
+
+# GPU functions
+.global GPU_dw
+.global mem2vram
+.global SendGPU
+.global GPU_cw
+.global GPU_cwb
+.global SendPrimitive
+.global GetGPUStatus
+
+GPU_dw:
+ li $9, 0x46
+ j 0xa0
+ nop
+
+mem2vram:
+ li $9, 0x47
+ j 0xa0
+ nop
+
+.global ResetEntryInt
+
+ResetEntryInt:
+ li $9, 0x18
+ j 0xb0
+ nop
+
+# ???
+.global GetKernelDate
+.global GetKernelRomVersion
+.global GetRamSize
+
+GetKernelDate:
+ li $9, 0xb4
+ li $4, 0
+ j 0xa0
+ nop
+
+GetKernelRomVersion:
+ li $9, 0xb4
+ li $4, 2
+ j 0xa0
+ nop
+
+GetRamSize:
+ li $9, 0xb4
+ li $4, 5
+ j 0xa0
+ nop
+
+# Event functions
+.global OpenEvent
+.global EnableEvent
+.global CloseEvent
+.global DisableEvent
+.global WaitEvent
+.global TestEvent
+.global DeliverEvent
+
+OpenEvent:
+ li $9, 0x08
+ j 0xb0
+ nop
+
+EnableEvent:
+ li $9, 0x0c
+ j 0xb0
+ nop
+
+CloseEvent:
+ li $9, 0x09
+ j 0xb0
+ nop
+
+DisableEvent:
+ li $9, 0x0d
+ j 0xb0
+ nop
+
+DeliverEvent:
+ li $9, 0x07
+ j 0xb0
+ nop
+
+WaitEvent:
+ li $9, 0x0a
+ j 0xb0
+ nop
+
+TestEvent:
+ li $9, 0x0b
+ j 0xb0
+ nop
+
+# File I/O functions
+.global open
+.global lseek
+.global read
+.global write
+.global close
+.global cd
+.global firstfile
+.global nextfile
+.global rename
+.global remove
+
+open:
+ li $9, 0x32
+ j 0xb0
+ nop
+
+lseek:
+ li $9, 0x33
+ j 0xb0
+ nop
+
+read:
+ li $9, 0x34
+ j 0xb0
+ nop
+
+write:
+ li $9, 0x35
+ j 0xb0
+ nop
+
+close:
+ li $9, 0x36
+ j 0xb0
+ nop
+
+cd:
+ li $9, 0x40
+ j 0xb0
+ nop
+
+firstfile:
+ li $9, 0x42
+ j 0xb0
+ nop
+
+nextfile:
+ li $9, 0x43
+ j 0xb0
+ nop
+
+rename:
+ li $9, 0x44
+ j 0xb0
+ nop
+
+remove:
+ li $9, 0x45
+ j 0xb0
+ nop
+
+# Exception / Interrupt functions
+
+.global EnterCriticalSection
+.global ExitCriticalSection
+.global SysEnqIntRP
+.global SysDeqIntRP
+
+EnterCriticalSection:
+ li $a0, 1
+ syscall
+ nop
+ jr $ra
+ nop
+
+ExitCriticalSection:
+ li $a0, 2
+ syscall
+ nop
+ jr $ra
+ nop
+
+SysEnqIntRP:
+ li $9, 0x02
+ j 0xc0
+ nop
+
+SysDeqIntRP:
+ li $9, 0x03
+ j 0xc0
+ nop
+
+# Filesystem functions
+
+.global _96_init
+.global _96_remove
+.global _bu_init
+
+_96_init:
+ li $9, 0x71
+ j 0xa0
+ nop
+
+_96_remove:
+ li $9, 0x72
+ j 0xa0
+ nop
+
+_bu_init:
+ li $9, 0x70
+ j 0xa0
+ nop
+
+# Executable loading functions
+
+.global LoadExec
+
+LoadExec:
+ li $9, 0x51
+ j 0xa0
+ nop
+
+# Memory card routines
+
+.global InitCARD
+.global StartCARD
+.global StopCARD
+.global _card_info
+.global _card_load
+.global _card_auto
+.global _card_write
+.global _card_read
+.global _card_status
+.global _new_card
+
+InitCARD:
+ li $9, 0x4a
+ j 0xb0
+ nop
+
+StartCARD:
+ li $9, 0x4b
+ j 0xb0
+ nop
+
+StopCARD:
+ li $9, 0x4c
+ j 0xb0
+ nop
+
+_card_info:
+ li $9, 0xab
+ j 0xa0
+ nop
+
+_card_load:
+ li $9, 0xac
+ j 0xa0
+ nop
+
+_card_auto:
+ li $9, 0xad
+ j 0xa0
+ nop
+
+_card_write:
+ li $9, 0x4e
+ j 0xb0
+ nop
+
+_card_read:
+ li $9, 0x4f
+ j 0xb0
+ nop
+
+_new_card:
+ li $9, 0x50
+ j 0xb0
+ nop
+
+_card_status:
+ li $9, 0x5c
+ j 0xb0
+ nop
+
+# Device functions
+
+.global PrintInstalledDevices
+
+PrintInstalledDevices:
+ li $9, 0x49
+ j 0xb0
+ nop
+
+.global BIOSWarmReboot
+
+BIOSWarmReboot:
+ li $9, 0xa0
+ nop
+ j 0xa0
diff --git a/libpsx/src/util.c b/libpsx/src/util.c
new file mode 100644
index 0000000..a9c7921
--- /dev/null
+++ b/libpsx/src/util.c
@@ -0,0 +1,42 @@
+// util.c
+// PSXSDK utility functions
+
+// This is not a core part of the PSXSDK
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <psxutil.h>
+
+const char *psxsdk_btn_names[] =
+ { "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross",
+ "Square", "Select", "Res1", "Res2", "Start",
+ "Up", "Right", "Down", "Left"};
+
+char *PSX_GetButtonName(unsigned short button, char *out, unsigned int out_len)
+{
+ int x;
+
+ if(out_len)out[0] = 0;
+
+ for(x = 0; x < 16; x++)
+ {
+ if(button & (1<<x))
+ {
+ strncat(out, psxsdk_btn_names[x], out_len);
+ out_len -= strlen(out);
+ strncat(out, "&", out_len);
+ out_len--;
+ }
+ }
+
+ if(strlen(out))
+ {
+ if(out[strlen(out) - 1] == '&')
+ out[strlen(out) - 1] = 0;
+ }
+ else
+ strncpy(out, "None", out_len);
+
+ return out;
+}