psxinterpreter: tons of new exceptions
authornotaz <notasas@gmail.com>
Sun, 16 Jul 2023 22:18:05 +0000 (01:18 +0300)
committernotaz <notasas@gmail.com>
Thu, 20 Jul 2023 23:49:31 +0000 (02:49 +0300)
does anything need any of this though?

include/compiler_features.h [new file with mode: 0644]
libpcsxcore/psxinterpreter.c
libpcsxcore/psxinterpreter.h
libpcsxcore/r3000a.c
libpcsxcore/r3000a.h

diff --git a/include/compiler_features.h b/include/compiler_features.h
new file mode 100644 (file)
index 0000000..0c1119d
--- /dev/null
@@ -0,0 +1,13 @@
+
+#ifdef __GNUC__
+# define likely(x)       __builtin_expect((x),1)
+# define unlikely(x)     __builtin_expect((x),0)
+#else
+# define likely(x)       (x)
+# define unlikely(x)     (x)
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
index 76a6a3d..bd732b9 100644 (file)
 #include "psxinterpreter.h"
 #include <stddef.h>
 #include <assert.h>
-//#include "debug.h"
-#define ProcessDebug()
+#include "../include/compiler_features.h"
+
+// these may cause issues: because of poor timing we may step
+// on instructions that real hardware would never reach
+#define DO_EXCEPTION_RESERVEDI
+#define DO_EXCEPTION_ADDR_ERR
 
 static int branch = 0;
 static int branch2 = 0;
 static u32 branchPC;
 
-// These macros are used to assemble the repassembler functions
-
-#ifdef PSXCPU_LOG
-#define debugI() PSXCPU_LOG("%s\n", disR3000AF(psxRegs.code, psxRegs.pc)); 
-#else
-#define debugI()
-#endif
-
 #ifdef __i386__
 #define INT_ATTR __attribute__((regparm(2)))
 #else
@@ -56,12 +52,29 @@ static u32 branchPC;
 static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code);
 static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code);
 
-static u32 INT_ATTR fetchNoCache(u8 **memRLUT, u32 pc)
+// get an opcode without triggering exceptions or affecting cache
+u32 intFakeFetch(u32 pc)
+{
+       u8 *base = psxMemRLUT[pc >> 16];
+       u32 *code;
+       if (unlikely(base == INVALID_PTR))
+               return 0; // nop
+       code = (u32 *)(base + (pc & 0xfffc));
+       return SWAP32(*code);
+
+}
+
+static u32 INT_ATTR fetchNoCache(psxRegisters *regs, u8 **memRLUT, u32 pc)
 {
        u8 *base = memRLUT[pc >> 16];
-       if (base == INVALID_PTR)
-               return 0;
-       u32 *code = (u32 *)(base + (pc & 0xfffc));
+       u32 *code;
+       if (unlikely(base == INVALID_PTR)) {
+               SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra);
+               regs->pc = pc;
+               psxException(R3000E_IBE << 2, branch, &regs->CP0);
+               return 0; // execute as nop
+       }
+       code = (u32 *)(base + (pc & 0xfffc));
        return SWAP32(*code);
 }
 
@@ -74,7 +87,7 @@ static struct cache_entry {
        u32 data[4];
 } ICache[256];
 
-static u32 INT_ATTR fetchICache(u8 **memRLUT, u32 pc)
+static u32 INT_ATTR fetchICache(psxRegisters *regs, u8 **memRLUT, u32 pc)
 {
        // cached?
        if (pc < 0xa0000000)
@@ -86,8 +99,12 @@ static u32 INT_ATTR fetchICache(u8 **memRLUT, u32 pc)
                {
                        const u8 *base = memRLUT[pc >> 16];
                        const u32 *code;
-                       if (base == INVALID_PTR)
-                               return 0;
+                       if (unlikely(base == INVALID_PTR)) {
+                               SysPrintf("game crash @%08x, ra=%08x\n", pc, regs->GPR.n.ra);
+                               regs->pc = pc;
+                               psxException(R3000E_IBE << 2, branch, &regs->CP0);
+                               return 0; // execute as nop
+                       }
                        code = (u32 *)(base + (pc & 0xfff0));
 
                        entry->tag = pc;
@@ -103,10 +120,10 @@ static u32 INT_ATTR fetchICache(u8 **memRLUT, u32 pc)
                return entry->data[(pc & 0x0f) >> 2];
        }
 
-       return fetchNoCache(memRLUT, pc);
+       return fetchNoCache(regs, memRLUT, pc);
 }
 
-static u32 (INT_ATTR *fetch)(u8 **memRLUT, u32 pc) = fetchNoCache;
+static u32 (INT_ATTR *fetch)(psxRegisters *regs_, u8 **memRLUT, u32 pc) = fetchNoCache;
 
 // Make the timing events trigger faster as we are currently assuming everything
 // takes one cycle, which is not the case on real hardware.
@@ -269,17 +286,9 @@ static int psxTestLoadDelay(int reg, u32 tmp) {
                        }
                        break;
 
-               case 0x01: // REGIMM
-                       switch (_tRt_) {
-                               case 0x00: case 0x01:
-                               case 0x10: case 0x11: // BLTZ/BGEZ...
-                                       // Xenogears - lbu v0 / beq v0
-                                       // - no load delay (fixes battle loading)
-                                       break;
-
-                                       if (_tRs_ == reg) return 2;
-                                       break;
-                       }
+               case 0x01: // REGIMM - BLTZ/BGEZ...
+                       // Xenogears - lbu v0 / beq v0
+                       // - no load delay (fixes battle loading)
                        break;
 
                // J would be just a break;
@@ -287,22 +296,12 @@ static int psxTestLoadDelay(int reg, u32 tmp) {
                        if (31 == reg) return 3;
                        break;
 
-               case 0x04: case 0x05: // BEQ/BNE
-                       // Xenogears - lbu v0 / beq v0
-                       // - no load delay (fixes battle loading)
-                       break;
-
-                       if (_tRs_ == reg || _tRt_ == reg) return 2;
-                       break;
-
                case 0x06: case 0x07: // BLEZ/BGTZ
+               case 0x04: case 0x05: // BEQ/BNE
                        // Xenogears - lbu v0 / beq v0
                        // - no load delay (fixes battle loading)
                        break;
 
-                       if (_tRs_ == reg) return 2;
-                       break;
-
                case 0x08: case 0x09: case 0x0a: case 0x0b:
                case 0x0c: case 0x0d: case 0x0e: // ADDI/ADDIU...
                        if (_tRt_ == reg && _tRs_ == reg) return 1; else
@@ -380,7 +379,7 @@ static int psxTestLoadDelay(int reg, u32 tmp) {
 }
 
 static void psxDelayTest(int reg, u32 bpc) {
-       u32 tmp = fetch(psxMemRLUT, bpc);
+       u32 tmp = intFakeFetch(bpc);
        branch = 1;
 
        switch (psxTestLoadDelay(reg, tmp)) {
@@ -403,7 +402,7 @@ static void psxDelayTest(int reg, u32 bpc) {
 static u32 psxBranchNoDelay(psxRegisters *regs_) {
        u32 temp, code;
 
-       regs_->code = code = fetch(psxMemRLUT, regs_->pc);
+       regs_->code = code = intFakeFetch(regs_->pc);
        switch (_Op_) {
                case 0x00: // SPECIAL
                        switch (_Funct_) {
@@ -482,8 +481,6 @@ static int psxDelayBranchTest(u32 tar1) {
        if (tar2 == (u32)-1)
                return 0;
 
-       debugI();
-
        /*
         * Branch in delay slot:
         * - execute 1 instruction at tar1
@@ -495,7 +492,6 @@ static int psxDelayBranchTest(u32 tar1) {
        if (tmp1 == (u32)-1) {
                return psxDelayBranchExec(tar2);
        }
-       debugI();
        addCycle();
 
        /*
@@ -508,7 +504,6 @@ static int psxDelayBranchTest(u32 tar1) {
        if (tmp2 == (u32)-1) {
                return psxDelayBranchExec(tmp1);
        }
-       debugI();
        addCycle();
 
        /*
@@ -521,7 +516,7 @@ static int psxDelayBranchTest(u32 tar1) {
 }
 
 static void doBranch(u32 tar) {
-       u32 tmp, code;
+       u32 tmp, code, pc;
 
        branch2 = branch = 1;
        branchPC = tar;
@@ -530,11 +525,10 @@ static void doBranch(u32 tar) {
        if (psxDelayBranchTest(tar))
                return;
 
-       psxRegs.code = code = fetch(psxMemRLUT, psxRegs.pc);
-
-       debugI();
-
+       pc = psxRegs.pc;
        psxRegs.pc += 4;
+       psxRegs.code = code = fetch(&psxRegs, psxMemRLUT, pc);
+
        addCycle();
 
        // check for load delay
@@ -579,11 +573,55 @@ static void doBranch(u32 tar) {
        psxBranchTest();
 }
 
+static void doBranchReg(u32 tar) {
+#ifdef DO_EXCEPTION_ADDR_ERR
+       if (unlikely(tar & 3)) {
+               psxRegs.pc = psxRegs.CP0.n.BadVAddr = tar;
+               psxException(R3000E_AdEL << 2, branch, &psxRegs.CP0);
+               return;
+       }
+#else
+       tar &= ~3;
+#endif
+       doBranch(tar);
+}
+
+#if __has_builtin(__builtin_add_overflow) || (defined(__GNUC__) && __GNUC__ >= 5)
+#define add_overflow(a, b, r) __builtin_add_overflow(a, b, &(r))
+#define sub_overflow(a, b, r) __builtin_sub_overflow(a, b, &(r))
+#else
+#define add_overflow(a, b, r) ({r = (u32)a + (u32)b; (a ^ ~b) & (a ^ r) & (1u<<31);})
+#define sub_overflow(a, b, r) ({r = (u32)a - (u32)b; (a ^  b) & (a ^ r) & (1u<<31);})
+#endif
+
+static void addExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) {
+       s32 r;
+       if (add_overflow(a1, a2, r)) {
+               //printf("ov %08x + %08x = %08x\n", a1, a2, r);
+               regs->pc -= 4;
+               psxException(R3000E_Ov << 2, branch, &regs->CP0);
+               return;
+       }
+       if (rt)
+               regs->GPR.r[rt] = r;
+}
+
+static void subExc(psxRegisters *regs, u32 rt, s32 a1, s32 a2) {
+       s32 r;
+       if (sub_overflow(a1, a2, r)) {
+               regs->pc -= 4;
+               psxException(R3000E_Ov << 2, branch, &regs->CP0);
+               return;
+       }
+       if (rt)
+               regs->GPR.r[rt] = r;
+}
+
 /*********************************************************
 * Arithmetic with immediate operand                      *
 * Format:  OP rt, rs, immediate                          *
 *********************************************************/
-OP(psxADDI)  { if (!_Rt_) return; _rRt_ = _u32(_rRs_) + _Imm_ ; }  // Rt = Rs + Im     (Exception on Integer Overflow)
+OP(psxADDI)  { addExc(regs_, _Rt_, _i32(_rRs_), _Imm_); } // Rt = Rs + Im (Exception on Integer Overflow)
 OP(psxADDIU) { if (!_Rt_) return; _rRt_ = _u32(_rRs_) + _Imm_ ; }  // Rt = Rs + Im
 OP(psxANDI)  { if (!_Rt_) return; _rRt_ = _u32(_rRs_) & _ImmU_; }  // Rt = Rs And Im
 OP(psxORI)   { if (!_Rt_) return; _rRt_ = _u32(_rRs_) | _ImmU_; }  // Rt = Rs Or  Im
@@ -595,9 +633,9 @@ OP(psxSLTIU) { if (!_Rt_) return; _rRt_ = _u32(_rRs_) < ((u32)_Imm_); } // Rt =
 * Register arithmetic                                    *
 * Format:  OP rd, rs, rt                                 *
 *********************************************************/
-OP(psxADD)   { if (!_Rd_) return; _rRd_ = _u32(_rRs_) + _u32(_rRt_); } // Rd = Rs + Rt         (Exception on Integer Overflow)
+OP(psxADD)   { addExc(regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs + Rt (Exception on Integer Overflow)
+OP(psxSUB)   { subExc(regs_, _Rd_, _i32(_rRs_), _i32(_rRt_)); } // Rd = Rs - Rt (Exception on Integer Overflow)
 OP(psxADDU)  { if (!_Rd_) return; _rRd_ = _u32(_rRs_) + _u32(_rRt_); } // Rd = Rs + Rt
-OP(psxSUB)   { if (!_Rd_) return; _rRd_ = _u32(_rRs_) - _u32(_rRt_); } // Rd = Rs - Rt         (Exception on Integer Overflow)
 OP(psxSUBU)  { if (!_Rd_) return; _rRd_ = _u32(_rRs_) - _u32(_rRt_); } // Rd = Rs - Rt
 OP(psxAND)   { if (!_Rd_) return; _rRd_ = _u32(_rRs_) & _u32(_rRt_); } // Rd = Rs And Rt
 OP(psxOR)    { if (!_Rd_) return; _rRd_ = _u32(_rRs_) | _u32(_rRt_); } // Rd = Rs Or  Rt
@@ -758,17 +796,21 @@ OP(psxMTLO) { _rLo_ = _rRs_; } // Lo = Rs
 *********************************************************/
 OP(psxBREAK) {
        regs_->pc -= 4;
-       psxException(0x24, branch, &regs_->CP0);
+       psxException(R3000E_Bp << 2, branch, &regs_->CP0);
 }
 
 OP(psxSYSCALL) {
        regs_->pc -= 4;
-       psxException(0x20, branch, &regs_->CP0);
+       psxException(R3000E_Syscall << 2, branch, &regs_->CP0);
 }
 
-static inline void psxTestSWInts(psxRegisters *regs_) {
+static inline void execI_(u8 **memRLUT, psxRegisters *regs_);
+
+static inline void psxTestSWInts(psxRegisters *regs_, int step) {
        if (regs_->CP0.n.Cause & regs_->CP0.n.Status & 0x0300 &&
           regs_->CP0.n.Status & 0x1) {
+               if (step)
+                       execI_(psxMemRLUT, regs_);
                regs_->CP0.n.Cause &= ~0x7c;
                psxException(regs_->CP0.n.Cause, branch, &regs_->CP0);
        }
@@ -778,7 +820,7 @@ OP(psxRFE) {
 //     SysPrintf("psxRFE\n");
        regs_->CP0.n.Status = (regs_->CP0.n.Status & 0xfffffff0) |
                              ((regs_->CP0.n.Status & 0x3c) >> 2);
-       psxTestSWInts(regs_);
+       psxTestSWInts(regs_, 0);
 }
 
 /*********************************************************
@@ -802,14 +844,14 @@ OP(psxJAL) { _SetLink(31); doBranch(_JumpTarget_); }
 * Format:  OP rs, rd                                     *
 *********************************************************/
 OP(psxJR) {
-       doBranch(_rRs_ & ~3);
+       doBranchReg(_rRs_);
        psxJumpTest();
 }
 
 OP(psxJALR) {
        u32 temp = _u32(_rRs_);
        if (_Rd_) { _SetLink(_Rd_); }
-       doBranch(temp & ~3);
+       doBranchReg(temp);
 }
 
 /*********************************************************
@@ -912,23 +954,38 @@ OP(psxSWR) {
 * Moves between GPR and COPx                             *
 * Format:  OP rt, fs                                     *
 *********************************************************/
-OP(psxMFC0) { if (!_Rt_) return; _rRt_ = _rFs_; }
+OP(psxMFC0) {
+       u32 r = _Rd_;
+#ifdef DO_EXCEPTION_RESERVEDI
+       if (unlikely(r == 0)) {
+               regs_->pc -= 4;
+               psxException(R3000E_RI << 2, branch, &regs_->CP0);
+       }
+#endif
+       if (_Rt_)
+               _rRt_ = regs_->CP0.r[r];
+}
+
 OP(psxCFC0) { if (!_Rt_) return; _rRt_ = _rFs_; }
 
+static void setupCop(u32 sr);
+
 void MTC0(psxRegisters *regs_, int reg, u32 val) {
 //     SysPrintf("MTC0 %d: %x\n", reg, val);
        switch (reg) {
                case 12: // Status
-                       if ((regs_->CP0.n.Status ^ val) & (1 << 16))
+                       if (unlikely((regs_->CP0.n.Status ^ val) & (1 << 16)))
                                psxMemOnIsolate((val >> 16) & 1);
+                       if (unlikely((regs_->CP0.n.Status ^ val) & (7 << 29)))
+                               setupCop(val);
                        regs_->CP0.n.Status = val;
-                       psxTestSWInts(regs_);
+                       psxTestSWInts(regs_, 1);
                        break;
 
                case 13: // Cause
                        regs_->CP0.n.Cause &= ~0x0300;
                        regs_->CP0.n.Cause |= val & 0x0300;
-                       psxTestSWInts(regs_);
+                       psxTestSWInts(regs_, 0);
                        break;
 
                default:
@@ -941,17 +998,24 @@ OP(psxMTC0) { MTC0(regs_, _Rd_, _u32(_rRt_)); }
 OP(psxCTC0) { MTC0(regs_, _Rd_, _u32(_rRt_)); }
 
 /*********************************************************
-* Unknow instruction (would generate an exception)       *
+* Unknown instruction (would generate an exception)      *
 * Format:  ?                                             *
 *********************************************************/
 static inline void psxNULL_(void) {
-#ifdef PSXCPU_LOG
-       PSXCPU_LOG("psx: Unimplemented op %x\n", psxRegs.code);
+       //printf("op %08x @%08x\n", psxRegs.code, psxRegs.pc);
+}
+
+OP(psxNULL) {
+       psxNULL_();
+#ifdef DO_EXCEPTION_RESERVEDI
+       regs_->pc -= 4;
+       psxException(R3000E_RI << 2, branch, &regs_->CP0);
 #endif
 }
 
-OP(psxNULL) { psxNULL_(); }
-void gteNULL(struct psxCP2Regs *regs) { psxNULL_(); }
+void gteNULL(struct psxCP2Regs *regs) {
+       psxNULL_();
+}
 
 OP(psxSPECIAL) {
        psxSPC[_Funct_](regs_, code);
@@ -968,6 +1032,22 @@ OP(psxCOP0) {
        }
 }
 
+OP(psxLWC0) {
+       // MTC0(regs_, _Rt_, psxMemRead32(_oB_)); // ?
+       log_unhandled("LWC0 %08x\n", code);
+}
+
+OP(psxCOP1) {
+       // ??? what actually happens here?
+}
+
+OP(psxCOP1d) {
+#ifdef DO_EXCEPTION_RESERVEDI
+       regs_->pc -= 4;
+       psxException((1<<28) | (R3000E_RI << 2), branch, &regs_->CP0);
+#endif
+}
+
 OP(psxCOP2) {
        psxCP2[_Funct_](&regs_->CP2);
 }
@@ -978,6 +1058,13 @@ OP(psxCOP2_stall) {
        psxCP2[f](&regs_->CP2);
 }
 
+OP(psxCOP2d) {
+#ifdef DO_EXCEPTION_RESERVEDI
+       regs_->pc -= 4;
+       psxException((2<<28) | (R3000E_RI << 2), branch, &regs_->CP0);
+#endif
+}
+
 OP(gteMFC2) {
        if (!_Rt_) return;
        regs_->GPR.r[_Rt_] = MFC2(&regs_->CP2, _Rd_);
@@ -1014,6 +1101,27 @@ OP(gteSWC2_stall) {
        gteSWC2(regs_, code);
 }
 
+OP(psxCOP3) {
+       // ??? what actually happens here?
+}
+
+OP(psxCOP3d) {
+#ifdef DO_EXCEPTION_RESERVEDI
+       regs_->pc -= 4;
+       psxException((3<<28) | (R3000E_RI << 2), branch, &regs_->CP0);
+#endif
+}
+
+OP(psxLWCx) {
+       // does this read memory?
+       log_unhandled("LWCx %08x\n", code);
+}
+
+OP(psxSWCx) {
+       // does this write something to memory?
+       log_unhandled("SWCx %08x\n", code);
+}
+
 static void psxBASIC(struct psxCP2Regs *cp2regs) {
        psxRegisters *regs_ = (void *)((char *)cp2regs - offsetof(psxRegisters, CP2));
        u32 code = regs_->code;
@@ -1041,23 +1149,28 @@ OP(psxREGIMM) {
 }
 
 OP(psxHLE) {
-    uint32_t hleCode = code & 0x03ffffff;
-    if (hleCode >= (sizeof(psxHLEt) / sizeof(psxHLEt[0]))) {
-        psxNULL_();
-    } else {
-        psxHLEt[hleCode]();
-    }
+       u32 hleCode;
+       if (unlikely(!Config.HLE)) {
+               psxSWCx(regs_, code);
+               return;
+       }
+       hleCode = code & 0x03ffffff;
+       if (hleCode >= (sizeof(psxHLEt) / sizeof(psxHLEt[0]))) {
+               psxSWCx(regs_, code);
+               return;
+       }
+       psxHLEt[hleCode]();
 }
 
 static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code) = {
        psxSPECIAL, psxREGIMM, psxJ   , psxJAL  , psxBEQ , psxBNE , psxBLEZ, psxBGTZ,
        psxADDI   , psxADDIU , psxSLTI, psxSLTIU, psxANDI, psxORI , psxXORI, psxLUI ,
-       psxCOP0   , psxNULL  , psxCOP2, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL,
-       psxNULL   , psxNULL  , psxNULL, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL,
-       psxLB     , psxLH    , psxLWL , psxLW   , psxLBU , psxLHU , psxLWR , psxNULL,
-       psxSB     , psxSH    , psxSWL , psxSW   , psxNULL, psxNULL, psxSWR , psxNULL, 
-       psxNULL   , psxNULL  , gteLWC2, psxNULL , psxNULL, psxNULL, psxNULL, psxNULL,
-       psxNULL   , psxNULL  , gteSWC2, psxHLE  , psxNULL, psxNULL, psxNULL, psxNULL 
+       psxCOP0   , psxCOP1d , psxCOP2, psxCOP3d, psxNULL, psxCOP1d,psxCOP2d,psxCOP3d,
+       psxNULL   , psxCOP1d , psxCOP2d,psxCOP3d, psxNULL, psxCOP1d,psxCOP2d,psxCOP3d,
+       psxLB     , psxLH    , psxLWL , psxLW   , psxLBU , psxLHU , psxLWR , psxCOP3d,
+       psxSB     , psxSH    , psxSWL , psxSW   , psxNULL, psxCOP1d,psxSWR , psxCOP3d,
+       psxLWC0   , psxLWCx  , gteLWC2, psxLWCx , psxNULL, psxCOP1d,psxCOP2d,psxCOP3d,
+       psxSWCx   , psxSWCx  , gteSWC2, psxHLE  , psxNULL, psxCOP1d,psxCOP2d,psxCOP3d,
 };
 
 static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code) = {
@@ -1092,13 +1205,10 @@ static void intReset() {
 }
 
 static inline void execI_(u8 **memRLUT, psxRegisters *regs_) {
-       regs_->code = fetch(memRLUT, regs_->pc);
-
-       debugI();
-
-       if (Config.Debug) ProcessDebug();
-
+       u32 pc = regs_->pc;
        regs_->pc += 4;
+       regs_->code = fetch(regs_, memRLUT, pc);
+
        addCycle();
 
        psxBSC[regs_->code >> 26](regs_, regs_->code);
@@ -1127,8 +1237,10 @@ static void intClear(u32 Addr, u32 Size) {
 
 static void intNotify(enum R3000Anote note, void *data) {
        switch (note) {
-       case R3000ACPU_NOTIFY_CACHE_ISOLATED: // Armored Core?
        case R3000ACPU_NOTIFY_AFTER_LOAD:
+               setupCop(psxRegs.CP0.n.Status);
+               // fallthrough
+       case R3000ACPU_NOTIFY_CACHE_ISOLATED: // Armored Core?
                memset(&ICache, 0xff, sizeof(ICache));
                break;
        case R3000ACPU_NOTIFY_CACHE_UNISOLATED:
@@ -1137,10 +1249,25 @@ static void intNotify(enum R3000Anote note, void *data) {
        }
 }
 
+static void setupCop(u32 sr)
+{
+       if (sr & (1u << 29))
+               psxBSC[17] = psxCOP1;
+       else
+               psxBSC[17] = psxCOP1d;
+       if (sr & (1u << 30))
+               psxBSC[18] = Config.DisableStalls ? psxCOP2 : psxCOP2_stall;
+       else
+               psxBSC[18] = psxCOP2d;
+       if (sr & (1u << 31))
+               psxBSC[19] = psxCOP3;
+       else
+               psxBSC[19] = psxCOP3d;
+}
+
 void intApplyConfig() {
        int cycle_mult;
 
-       assert(psxBSC[18] == psxCOP2  || psxBSC[18] == psxCOP2_stall);
        assert(psxBSC[50] == gteLWC2  || psxBSC[50] == gteLWC2_stall);
        assert(psxBSC[58] == gteSWC2  || psxBSC[58] == gteSWC2_stall);
        assert(psxSPC[16] == psxMFHI  || psxSPC[16] == psxMFHI_stall);
@@ -1171,6 +1298,7 @@ void intApplyConfig() {
                psxSPC[26] = psxDIV_stall;
                psxSPC[27] = psxDIVU_stall;
        }
+       setupCop(psxRegs.CP0.n.Status);
 
        // dynarec may occasionally call the interpreter, in such a case the
        // cache won't work (cache only works right if all fetches go through it)
index fe289b0..746c8fe 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef __PSXINTERPRETER_H__
 #define __PSXINTERPRETER_H__
 
+// get an opcode without triggering exceptions or affecting cache
+u32 intFakeFetch(u32 pc);
+
 // called by "new_dynarec"
 void execI();
 void intApplyConfig();
index fbccdea..5374f86 100644 (file)
@@ -26,6 +26,7 @@
 #include "mdec.h"
 #include "gte.h"
 #include "psxinterpreter.h"
+#include "../include/compiler_features.h"
 
 R3000Acpu *psxCpu = NULL;
 #ifdef DRC_DISABLE
@@ -58,8 +59,10 @@ void psxReset() {
 
        psxRegs.pc = 0xbfc00000; // Start in bootstrap
 
-       psxRegs.CP0.r[12] = 0x10900000; // COP0 enabled | BEV = 1 | TS = 1
+       psxRegs.CP0.r[12] = 0x10600000; // COP0 enabled | BEV = 1 | TS = 1
        psxRegs.CP0.r[15] = 0x00000002; // PRevID = Revision ID, same as R3000A
+       if (Config.HLE)
+               psxRegs.CP0.n.Status |= 1u << 30; // COP2 enabled
 
        psxCpu->ApplyConfig();
        psxCpu->Reset();
@@ -90,20 +93,21 @@ void psxShutdown() {
 }
 
 // cp0 is passed separately for lightrec to be less messy
-void psxException(u32 code, u32 bd, psxCP0Regs *cp0) {
-       psxRegs.code = PSXMu32(psxRegs.pc);
+void psxException(u32 cause, u32 bd, psxCP0Regs *cp0) {
+       u32 opcode = intFakeFetch(psxRegs.pc);
        
-       if (!Config.HLE && ((((psxRegs.code) >> 24) & 0xfe) == 0x4a)) {
+       if (unlikely(!Config.HLE && ((((opcode) >> 24) & 0xfe) == 0x4a))) {
                // "hokuto no ken" / "Crash Bandicot 2" ...
                // BIOS does not allow to return to GTE instructions
                // (just skips it, supposedly because it's scheduled already)
                // so we execute it here
                psxCP2Regs *cp2 = (void *)(cp0 + 1);
-               psxCP2[psxRegs.code & 0x3f](cp2);
+               psxRegs.code = opcode;
+               psxCP2[opcode & 0x3f](cp2);
        }
 
        // Set the Cause
-       cp0->n.Cause = (cp0->n.Cause & 0x300) | code;
+       cp0->n.Cause = (cp0->n.Cause & 0x300) | cause;
 
        // Set the EPC & PC
        if (bd) {
index 8d53a18..bdb8d27 100644 (file)
@@ -29,6 +29,19 @@ extern "C" {
 #include "psxcounters.h"
 #include "psxbios.h"
 
+enum R3000Aexception {
+       R3000E_Int = 0,      // Interrupt
+       R3000E_AdEL = 4,     // Address error (on load/I-fetch)
+       R3000E_AdES = 5,     // Address error (on store)
+       R3000E_IBE = 6,      // Bus error (instruction fetch)
+       R3000E_DBE = 7,      // Bus error (data load)
+       R3000E_Syscall = 8,  // syscall instruction
+       R3000E_Bp = 9,       // Breakpoint - a break instruction
+       R3000E_RI = 10,      // reserved instruction
+       R3000E_CpU = 11,     // Co-Processor unusable
+       R3000E_Ov = 12       // arithmetic overflow
+};
+
 enum R3000Anote {
        R3000ACPU_NOTIFY_CACHE_ISOLATED = 0,
        R3000ACPU_NOTIFY_CACHE_UNISOLATED = 1,