psxbios: Support other syscalls
[pcsx_rearmed.git] / libpcsxcore / psxbios.c
index 4019234..288e33b 100644 (file)
@@ -254,6 +254,7 @@ static EvCB *RcEV; // 0xf2
 static EvCB *UeEV; // 0xf3
 static EvCB *SwEV; // 0xf4
 static EvCB *ThEV; // 0xff
+static u32 heap_size = 0;
 static u32 *heap_addr = NULL;
 static u32 *heap_end = NULL;
 static u32 SysIntRP[8];
@@ -540,13 +541,35 @@ void psxBios_strncat() { // 0x16
 
 void psxBios_strcmp() { // 0x17
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-
+       s32 n=0;
+       if (a0 == 0 && a1 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
+       else if (a0 == 0 && a1 != 0)
+       {
+               v0 = -1;
+               pc0 = ra;
+               return;
+       }
+       else if (a0 != 0 && a1 == 0)
+       {
+               v0 = 1;
+               pc0 = ra;
+               return;
+       }
 #ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s: %s (%x), %s (%x)\n", biosA0n[0x17], Ra0, a0, Ra1, a1);
 #endif
 
        while (*p1 == *p2++) {
+               n++;
                if (*p1++ == '\0') {
+                       v1=n-1;
+                       a0+=n;
+                       a1+=n;
                        v0 = 0;
                        pc0 = ra;
                        return;
@@ -554,13 +577,33 @@ void psxBios_strcmp() { // 0x17
        }
 
        v0 = (*p1 - *--p2);
+       v1 = n;
+       a0+=n;
+       a1+=n;
        pc0 = ra;
 }
 
 void psxBios_strncmp() { // 0x18
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
        s32 n = a2;
-
+       if (a0 == 0 && a1 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
+       else if (a0 == 0 && a1 != 0)
+       {
+               v0 = -1;
+               pc0 = ra;
+               return;
+       }
+       else if (a0 != 0 && a1 == 0)
+       {
+               v0 = 1;
+               pc0 = ra;
+               return;
+       }
 #ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s: %s (%x), %s (%x), %d\n", biosA0n[0x18], Ra0, a0, Ra1, a1, a2);
 #endif
@@ -569,16 +612,30 @@ void psxBios_strncmp() { // 0x18
                if (*p1++ == '\0') {
                        v0 = 0;
                        pc0 = ra;
+                       v1 = a2 - ((a2-n) - 1);
+                       a0 += (a2-n) - 1;
+                       a1 += (a2-n) - 1;
+                       a2 = n;
                        return;
                }
        }
 
        v0 = (n < 0 ? 0 : *p1 - *--p2);
        pc0 = ra;
+       v1 = a2 - ((a2-n) - 1);
+       a0 += (a2-n) - 1;
+       a1 += (a2-n) - 1;
+       a2 = n;
 }
 
 void psxBios_strcpy() { // 0x19
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
+       if (a0 == 0 || a1 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
        while ((*p1++ = *p2++) != '\0');
 
        v0 = a0; pc0 = ra;
@@ -587,7 +644,12 @@ void psxBios_strcpy() { // 0x19
 void psxBios_strncpy() { // 0x1a
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
        s32 n = a2, i;
-
+       if (a0 == 0 || a1 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
        for (i = 0; i < n; i++) {
                if ((*p1++ = *p2++) == '\0') {
                        while (++i < n) {
@@ -604,6 +666,11 @@ void psxBios_strncpy() { // 0x1a
 void psxBios_strlen() { // 0x1b
        char *p = (char *)Ra0;
        v0 = 0;
+       if (a0 == 0)
+       {
+               pc0 = ra;
+               return;
+       }
        while (*p++) v0++;
        pc0 = ra;
 }
@@ -752,7 +819,21 @@ void psxBios_bcopy() { // 0x27
 
 void psxBios_bzero() { // 0x28
        char *p = (char *)Ra0;
+       v0 = a0;
+       /* Same as memset here (See memset below) */
+       if (a1 > 0x7FFFFFFF || a1 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
+       else if (a0 == 0)
+       {
+               pc0 = ra;
+               return;
+       }
        while ((s32)a1-- > 0) *p++ = '\0';
+       a1 = 0;
        pc0 = ra;
 }
 
@@ -774,22 +855,48 @@ void psxBios_bcmp() { // 0x29
 
 void psxBios_memcpy() { // 0x2a
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-       while ((s32)a2-- > 0) *p1++ = *p2++;
-
-       v0 = a0; pc0 = ra;
+       s32 n=0;
+       v0 = a0;
+       if (a0 == 0 || a2 > 0x7FFFFFFF)
+       {
+               pc0 = ra;
+               return;
+       }
+       while ((s32)a2-- > 0) {
+               n++;
+               *p1++ = *p2++;
+       }
+       a2 = 0;
+       pc0 = ra;
 }
 
 void psxBios_memset() { // 0x2b
        char *p = (char *)Ra0;
+       v0 = a0;
+       if (a2 > 0x7FFFFFFF || a2 == 0)
+       {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
+       if (a0 == 0)
+       {
+               pc0 = ra;
+               return;
+       }
        while ((s32)a2-- > 0) *p++ = (char)a1;
-
        a2 = 0;
        v0 = a0; pc0 = ra;
 }
 
 void psxBios_memmove() { // 0x2c
        char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-
+       v0 = a0;
+       if (a0 == 0 || a2 > 0x7FFFFFFF)
+       {
+               pc0 = ra;
+               return;
+       }
        if (p2 <= p1 && p2 + a2 > p1) {
                a2++; // BUG: copy one more byte here
                p1 += a2;
@@ -798,8 +905,7 @@ void psxBios_memmove() { // 0x2c
        } else {
                while ((s32)a2-- > 0) *p1++ = *p2++;
        }
-
-       v0 = a0; pc0 = ra;
+       pc0 = ra;
 }
 
 void psxBios_memcmp() { // 0x2d
@@ -944,6 +1050,11 @@ void psxBios_malloc() { // 0x33
 #ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x33]);
 #endif
+       if (!a0 || (!heap_size || !heap_addr)) {
+               v0 = 0;
+               pc0 = ra;
+               return;
+       }
 
        // scan through heap and combine free chunks of space
        chunk = heap_addr;
@@ -953,6 +1064,15 @@ void psxBios_malloc() { // 0x33
                csize = ((u32)*chunk) & 0xfffffffc;
                cstat = ((u32)*chunk) & 1;
 
+               // most probably broken heap descriptor
+               // this fixes Burning Road
+               if (*chunk == 0) {
+                       newchunk = chunk;
+                       dsize = ((uptr)heap_end - (uptr)chunk) - 4;
+                       colflag = 1;
+                       break;
+               }
+
                // it's a free chunk
                if(cstat == 1) {
                        if(colflag == 0) {
@@ -984,28 +1104,36 @@ void psxBios_malloc() { // 0x33
 
        // exit on uninitialized heap
        if (chunk == NULL) {
-               SysPrintf("malloc %x,%x: Uninitialized Heap!\n", v0, a0);
+               printf("malloc %x,%x: Uninitialized Heap!\n", v0, a0);
                v0 = 0;
                pc0 = ra;
                return;
        }
 
        // search an unused chunk that is big enough until the end of the heap
-       while ((dsize > csize || cstat == 0) && chunk < heap_end ) {
+       while ((dsize > csize || cstat==0) && chunk < heap_end ) {
                chunk = (u32*)((uptr)chunk + csize + 4);
+
+                       // catch out of memory
+                       if(chunk >= heap_end) {
+                               printf("malloc %x,%x: Out of memory error!\n",
+                                       v0, a0);
+                               v0 = 0; pc0 = ra;
+                               return;
+                       }
+
                csize = ((u32)*chunk) & 0xfffffffc;
                cstat = ((u32)*chunk) & 1;
        }
 
-       // catch out of memory
-       if(chunk >= heap_end) { SysPrintf("malloc %x,%x: Out of memory error!\n", v0, a0); v0 = 0; pc0 = ra; return; }
-       
        // allocate memory
        if(dsize == csize) {
                // chunk has same size
                *chunk &= 0xfffffffc;
-       }
-       else {
+       } else if (dsize > csize) {
+               v0 = 0; pc0 = ra;
+               return;
+       } else {
                // split free chunk
                *chunk = SWAP32(dsize);
                newchunk = (u32*)((uptr)chunk + dsize + 4);
@@ -1013,9 +1141,9 @@ void psxBios_malloc() { // 0x33
        }
 
        // return pointer to allocated memory
-       v0 = ((unsigned long)chunk - (unsigned long)psxM) + 4;
+       v0 = ((uptr)chunk - (uptr)psxM) + 4;
        v0|= 0x80000000;
-       SysPrintf ("malloc %x,%x\n", v0, a0);
+       //printf ("malloc %x,%x\n", v0, a0);
        pc0 = ra;
 }
 
@@ -1027,7 +1155,8 @@ void psxBios_free() { // 0x34
 
        SysPrintf("free %x: %x bytes\n", a0, *(u32*)(Ra0-4));
 
-       *(u32*)(Ra0-4) |= 1;    // set chunk to free
+       if (a0)
+               *(u32*)(Ra0-4) |= 1;    // set chunk to free
        pc0 = ra;
 }
 
@@ -2355,6 +2484,13 @@ void psxBios__new_card() { // 0x50
        pc0 = ra;
 }
 
+/* According to a user, this allows Final Fantasy Tactics to save/load properly */
+void psxBios__get_error(void) // 55
+{ 
+       v0 = 0;
+       pc0 = ra;
+}
+
 void psxBios_Krom2RawAdd() { // 0x51
        int i = 0;
 
@@ -2777,7 +2913,7 @@ void psxBiosInit() {
        //biosB0[0x52] = psxBios_sys_b0_52;
        //biosB0[0x53] = psxBios_sys_b0_53;
        //biosB0[0x54] = psxBios__get_errno;
-       //biosB0[0x55] = psxBios__get_error;
+       biosB0[0x55] = psxBios__get_error;
        biosB0[0x56] = psxBios_GetC0Table;
        biosB0[0x57] = psxBios_GetB0Table;
        biosB0[0x58] = psxBios__card_chan;
@@ -2846,6 +2982,7 @@ void psxBiosInit() {
        pad_buf1len = pad_buf2len = 0;
        heap_addr = NULL;
        heap_end = NULL;
+       heap_size = 0;
        CardState = -1;
        CurThread = 0;
        memset(FDesc, 0, sizeof(FDesc));
@@ -3059,6 +3196,9 @@ void psxBiosException() {
                                case 2: // ExitCritical - enable irq's
                                        psxRegs.CP0.n.Status |= 0x404; 
                                        break;
+                               /* Normally this should cover SYS(00h, SYS(04h but they don't do anything relevant so... */
+                               default:
+                                       break;
                        }
                        pc0 = psxRegs.CP0.n.EPC + 4;