gpulib: don't corrupt memory on garbage commands
[pcsx_rearmed.git] / libpcsxcore / psxbios.c
index 678377e..0f8beb0 100644 (file)
@@ -344,6 +344,11 @@ static void mips_return(u32 val)
        pc0 = ra;
 }
 
+static void mips_return_void(void)
+{
+       pc0 = ra;
+}
+
 static void use_cycles(u32 cycle)
 {
        psxRegs.cycle += cycle * 2;
@@ -414,6 +419,7 @@ static void CloseEvent(u32 ev);
        PSXBIOS_LOG("read %d: %x,%x (%s)\n", FDesc[1 + mcd].mcfile, FDesc[1 + mcd].offset, a2, Mcd##mcd##Data + 128 * FDesc[1 + mcd].mcfile + 0xa); \
        ptr = Mcd##mcd##Data + 8192 * FDesc[1 + mcd].mcfile + FDesc[1 + mcd].offset; \
        memcpy(Ra1, ptr, length); \
+       psxCpu->Clear(a1, (length + 3) / 4); \
        if (FDesc[1 + mcd].mode & 0x8000) { \
        DeliverEvent(0xf0000011, 0x0004); \
        DeliverEvent(0xf4000001, 0x0004); \
@@ -904,33 +910,69 @@ void psxBios_tolower() { // 0x26
        pc0 = ra;
 }
 
-void psxBios_bcopy() { // 0x27
-       char *p1 = (char *)Ra1, *p2 = (char *)Ra0;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
+static void do_memset(u32 dst, u32 v, s32 len)
+{
+       u32 d = dst;
+       s32 l = len;
+       while (l-- > 0) {
+               u8 *db = PSXM(d);
+               if (db != INVALID_PTR)
+                       *db = v;
+               d++;
+       }
+       psxCpu->Clear(dst, (len + 3) / 4);
+}
+
+static void do_memcpy(u32 dst, u32 src, s32 len)
+{
+       u32 d = dst, s = src;
+       s32 l = len;
+       while (l-- > 0) {
+               const u8 *sb = PSXM(s);
+               u8 *db = PSXM(d);
+               if (db != INVALID_PTR && sb != INVALID_PTR)
+                       *db = *sb;
+               d++;
+               s++;
+       }
+       psxCpu->Clear(dst, (len + 3) / 4);
+}
+
+static void psxBios_memcpy();
+
+static void psxBios_bcopy() { // 0x27 - memcpy with args swapped
+       //PSXBIOS_LOG("psxBios_%s %x %x %x\n", biosA0n[0x27], a0, a1, a2);
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0) // ...but it checks src this time
        {
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       while ((s32)a2-- > 0) *p1++ = *p2++;
-       a2 = 0;
-       pc0 = ra;
+       v1 = a0;
+       if ((s32)a2 > 0) {
+               do_memcpy(a1, a0, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
+       }
+       mips_return_c(ret, cycles + 5);
 }
 
 static void psxBios_bzero() { // 0x28
        /* Same as memset here (See memset below) */
-       u32 ret = a0;
+       u32 ret = a0, cycles;
        if (a0 == 0 || (s32)a1 <= 0)
        {
                mips_return_c(0, 6);
                return;
        }
-       while ((s32)a1-- > 0) {
-               storeRam8(a0++, 0);
-               use_cycles(4);
-       }
+       do_memset(a0, 0, a1);
+       cycles = a1 * 4;
+       a0 += a1;
+       a1 = 0;
        // todo: many more cycles due to uncached bios mem
-       mips_return_c(ret, 5);
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_bcmp() { // 0x29
@@ -949,53 +991,70 @@ void psxBios_bcmp() { // 0x29
        v0 = 0; pc0 = ra;
 }
 
-void psxBios_memcpy() { // 0x2a
-       char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
+static void psxBios_memcpy() { // 0x2a
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0)
        {
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       while ((s32)a2-- > 0) {
-               *p1++ = *p2++;
+       v1 = a0;
+       if ((s32)a2 > 0) {
+               do_memcpy(a0, a1, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
        }
-       a2 = 0;
-       pc0 = ra;
+       mips_return_c(ret, cycles + 5);
 }
 
 static void psxBios_memset() { // 0x2b
-       u32 ret = a0;
+       u32 ret = a0, cycles;
        if (a0 == 0 || (s32)a2 <= 0)
        {
                mips_return_c(0, 6);
                return;
        }
-       while ((s32)a2-- > 0) {
-               storeRam8(a0++, a1);
-               use_cycles(4);
-       }
+       do_memset(a0, a1, a2);
+       cycles = a2 * 4;
+       a0 += a2;
+       a2 = 0;
        // todo: many more cycles due to uncached bios mem
-       mips_return_c(ret, 5);
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_memmove() { // 0x2c
-       char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0)
        {
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       if (p2 <= p1 && p2 + a2 > p1) {
-               a2++; // BUG: copy one more byte here
-               p1 += a2;
-               p2 += a2;
-               while ((s32)a2-- > 0) *--p1 = *--p2;
-       } else {
-               while ((s32)a2-- > 0) *p1++ = *p2++;
+       v1 = a0;
+       if ((s32)a2 > 0 && a0 > a1 && a0 < a1 + a2) {
+               u32 dst = a0, len = a2 + 1;
+               a0 += a2;
+               a1 += a2;
+               while ((s32)a2 >= 0) { // BUG: copies one more byte here
+                       const u8 *sb = PSXM(a1);
+                       u8 *db = PSXM(a0);
+                       if (db != INVALID_PTR && sb != INVALID_PTR)
+                               *db = *sb;
+                       a0--;
+                       a1--;
+                       a2--;
+               }
+               psxCpu->Clear(dst, (len + 3) / 4);
+               cycles = 10 + len * 8;
+       } else if ((s32)a2 > 0) {
+               do_memcpy(a0, a1, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
        }
-       pc0 = ra;
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_memcmp() { // 0x2d
@@ -1416,6 +1475,14 @@ static void psxBios_SystemErrorUnresolvedException() {
        mips_return_void_c(1000);
 }
 
+static void FlushCache() {
+       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
+       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
+       k0 = 0xbfc0193c;
+       // runs from uncached mem so tons of cycles
+       use_cycles(500);
+}
+
 /*
  *     long Load(char *name, struct EXEC *header);
  */
@@ -1425,8 +1492,10 @@ void psxBios_Load() { // 0x42
        void *pa1;
 
        pa1 = Ra1;
-       if (pa1 && LoadCdromFile(Ra0, &eheader) == 0) {
+       if (pa1 != INVALID_PTR && LoadCdromFile(Ra0, &eheader) == 0) {
                memcpy(pa1, ((char*)&eheader)+16, sizeof(EXEC));
+               psxCpu->Clear(a1, sizeof(EXEC) / 4);
+               FlushCache();
                v0 = 1;
        } else v0 = 0;
        PSXBIOS_LOG("psxBios_%s: %s, %d -> %d\n", biosA0n[0x42], Ra0, a1, v0);
@@ -1473,13 +1542,10 @@ void psxBios_Exec() { // 43
        pc0 = SWAP32(header->_pc0);
 }
 
-void psxBios_FlushCache() { // 44
-#ifdef PSXBIOS_LOG
+static void psxBios_FlushCache() { // 44
        PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x44]);
-#endif
-       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
-       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
-       pc0 = ra;
+       FlushCache();
+       mips_return_void();
 }
 
 void psxBios_GPU_dw() { // 0x46
@@ -2615,11 +2681,12 @@ void psxBios_InitCARD() { // 4a
        u32 *ram32 = (u32 *)psxM;
        PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x4a], a0);
        write_chain(ram32 + A_PADCRD_CHN_E/4, 0, 0x49bc, 0x4a4c);
-       // (maybe) todo: early_card_irq, FlushCache etc
+       // (maybe) todo: early_card_irq, etc
 
        ram32[A_PAD_IRQR_ENA/4] = SWAP32(a0);
 
-       mips_return_c(0, 300);
+       psxBios_FlushCache();
+       mips_return_c(0, 34+13+15+6);
 }
 
 void psxBios_StartCARD() { // 4b
@@ -3031,8 +3098,8 @@ static void write_chain(u32 *d, u32 next, u32 handler1, u32 handler2)
        d[2] = SWAPu32(handler2);
 
        // install the hle traps
-       PSXMu32ref(handler1) = HLEOP(chain_hle_op(handler1));
-       PSXMu32ref(handler2) = HLEOP(chain_hle_op(handler2));
+       if (handler1) PSXMu32ref(handler1) = HLEOP(chain_hle_op(handler1));
+       if (handler2) PSXMu32ref(handler2) = HLEOP(chain_hle_op(handler2));
 }
 
 static void setup_tt(u32 tcb_cnt, u32 evcb_cnt)
@@ -3520,6 +3587,13 @@ void psxBiosInit() {
        len = 0x80000 - 0x69d68;
        uncompress((Bytef *)(psxR + 0x69d68), &len, font_889f, sizeof(font_889f));
 
+       // trap attempts to call bios directly
+       rom32[0x00000/4] = HLEOP(hleop_dummy);
+       rom32[0x00180/4] = HLEOP(hleop_dummy);
+       rom32[0x3fffc/4] = HLEOP(hleop_dummy);
+       rom32[0x65ffc/4] = HLEOP(hleop_dummy);
+       rom32[0x7ff2c/4] = HLEOP(hleop_dummy);
+
        /*      Some games like R-Types, CTR, Fade to Black read from adress 0x00000000 due to uninitialized pointers.
                See Garbage Area at Address 00000000h in Nocash PSX Specfications for more information.
                Here are some examples of games not working with this fix in place :