psxinterpreter: a bit more accurate Cause reg handling
[pcsx_rearmed.git] / libpcsxcore / psxinterpreter.c
index 08b656f..ff49491 100644 (file)
@@ -25,6 +25,7 @@
 #include "r3000a.h"
 #include "gte.h"
 #include "psxhle.h"
+#include "debug.h"
 
 static int branch = 0;
 static int branch2 = 0;
@@ -38,14 +39,14 @@ static u32 branchPC;
 #define debugI()
 #endif
 
-inline void execI();
+void execI();
 
 // Subsets
 void (*psxBSC[64])();
 void (*psxSPC[64])();
 void (*psxREG[32])();
 void (*psxCP0[32])();
-void (*psxCP2[64])();
+void (*psxCP2[64])(struct psxCP2Regs *regs);
 void (*psxCP2BSC[32])();
 
 static void delayRead(int reg, u32 bpc) {
@@ -59,13 +60,13 @@ static void delayRead(int reg, u32 bpc) {
 
        psxRegs.pc = bpc;
 
-       psxBranchTest();
+       branch = 0;
 
        psxRegs.GPR.r[reg] = rold;
        execI(); // first branch opcode
        psxRegs.GPR.r[reg] = rnew;
 
-       branch = 0;
+       psxBranchTest();
 }
 
 static void delayWrite(int reg, u32 bpc) {
@@ -153,8 +154,12 @@ int psxTestLoadDelay(int reg, u32 tmp) {
 
                case 0x01: // REGIMM
                        switch (_tRt_) {
-                               case 0x00: case 0x02:
-                               case 0x10: case 0x12: // BLTZ/BGEZ...
+                               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;
                        }
@@ -166,10 +171,18 @@ int psxTestLoadDelay(int reg, u32 tmp) {
                        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
+                       // Xenogears - lbu v0 / beq v0
+                       // - no load delay (fixes battle loading)
+                       break;
+
                        if (_tRs_ == reg) return 2;
                        break;
 
@@ -273,13 +286,139 @@ void psxDelayTest(int reg, u32 bpc) {
        psxBranchTest();
 }
 
-__inline void doBranch(u32 tar) {
+static u32 psxBranchNoDelay(void) {
+       u32 *code;
+       u32 temp;
+
+       code = (u32 *)PSXM(psxRegs.pc);
+       psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));
+       switch (_Op_) {
+               case 0x00: // SPECIAL
+                       switch (_Funct_) {
+                               case 0x08: // JR
+                                       return _u32(_rRs_);
+                               case 0x09: // JALR
+                                       temp = _u32(_rRs_);
+                                       if (_Rd_) { _SetLink(_Rd_); }
+                                       return temp;
+                       }
+                       break;
+               case 0x01: // REGIMM
+                       switch (_Rt_) {
+                               case 0x00: // BLTZ
+                                       if (_i32(_rRs_) < 0)
+                                               return _BranchTarget_;
+                                       break;
+                               case 0x01: // BGEZ
+                                       if (_i32(_rRs_) >= 0)
+                                               return _BranchTarget_;
+                                       break;
+                               case 0x08: // BLTZAL
+                                       if (_i32(_rRs_) < 0) {
+                                               _SetLink(31);
+                                               return _BranchTarget_;
+                                       }
+                                       break;
+                               case 0x09: // BGEZAL
+                                       if (_i32(_rRs_) >= 0) {
+                                               _SetLink(31);
+                                               return _BranchTarget_;
+                                       }
+                                       break;
+                       }
+                       break;
+               case 0x02: // J
+                       return _JumpTarget_;
+               case 0x03: // JAL
+                       _SetLink(31);
+                       return _JumpTarget_;
+               case 0x04: // BEQ
+                       if (_i32(_rRs_) == _i32(_rRt_))
+                               return _BranchTarget_;
+                       break;
+               case 0x05: // BNE
+                       if (_i32(_rRs_) != _i32(_rRt_))
+                               return _BranchTarget_;
+                       break;
+               case 0x06: // BLEZ
+                       if (_i32(_rRs_) <= 0)
+                               return _BranchTarget_;
+                       break;
+               case 0x07: // BGTZ
+                       if (_i32(_rRs_) > 0)
+                               return _BranchTarget_;
+                       break;
+       }
+
+       return (u32)-1;
+}
+
+static int psxDelayBranchExec(u32 tar) {
+       execI();
+
+       branch = 0;
+       psxRegs.pc = tar;
+       psxRegs.cycle += BIAS;
+       psxBranchTest();
+       return 1;
+}
+
+static int psxDelayBranchTest(u32 tar1) {
+       u32 tar2, tmp1, tmp2;
+
+       tar2 = psxBranchNoDelay();
+       if (tar2 == (u32)-1)
+               return 0;
+
+       debugI();
+
+       /*
+        * Branch in delay slot:
+        * - execute 1 instruction at tar1
+        * - jump to tar2 (target of branch in delay slot; this branch
+        *   has no normal delay slot, instruction at tar1 was fetched instead)
+        */
+       psxRegs.pc = tar1;
+       tmp1 = psxBranchNoDelay();
+       if (tmp1 == (u32)-1) {
+               return psxDelayBranchExec(tar2);
+       }
+       debugI();
+       psxRegs.cycle += BIAS;
+
+       /*
+        * Got a branch at tar1:
+        * - execute 1 instruction at tar2
+        * - jump to target of that branch (tmp1)
+        */
+       psxRegs.pc = tar2;
+       tmp2 = psxBranchNoDelay();
+       if (tmp2 == (u32)-1) {
+               return psxDelayBranchExec(tmp1);
+       }
+       debugI();
+       psxRegs.cycle += BIAS;
+
+       /*
+        * Got a branch at tar2:
+        * - execute 1 instruction at tmp1
+        * - jump to target of that branch (tmp2)
+        */
+       psxRegs.pc = tmp1;
+       return psxDelayBranchExec(tmp2);
+}
+
+static void doBranch(u32 tar) {
        u32 *code;
        u32 tmp;
 
        branch2 = branch = 1;
        branchPC = tar;
 
+       // check for branch in delay slot
+       if (psxDelayBranchTest(tar))
+               return;
+
        code = (u32 *)PSXM(psxRegs.pc);
        psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));
 
@@ -366,6 +505,10 @@ void psxDIV() {
                _i32(_rLo_) = _i32(_rRs_) / _i32(_rRt_);
                _i32(_rHi_) = _i32(_rRs_) % _i32(_rRt_);
        }
+       else {
+               _i32(_rLo_) = _i32(_rRs_) >= 0 ? 0xffffffff : 1;
+               _i32(_rHi_) = _i32(_rRs_);
+       }
 }
 
 void psxDIVU() {
@@ -373,6 +516,10 @@ void psxDIVU() {
                _rLo_ = _rRs_ / _rRt_;
                _rHi_ = _rRs_ % _rRt_;
        }
+       else {
+               _i32(_rLo_) = 0xffffffff;
+               _i32(_rHi_) = _i32(_rRs_);
+       }
 }
 
 void psxMULT() {
@@ -633,15 +780,14 @@ void psxMFC0() { if (!_Rt_) return; _i32(_rRt_) = (int)_rFs_; }
 void psxCFC0() { if (!_Rt_) return; _i32(_rRt_) = (int)_rFs_; }
 
 void psxTestSWInts() {
-       // the next code is untested, if u know please
-       // tell me if it works ok or not (linuzappz)
        if (psxRegs.CP0.n.Cause & psxRegs.CP0.n.Status & 0x0300 &&
-               psxRegs.CP0.n.Status & 0x1) {
+          psxRegs.CP0.n.Status & 0x1) {
+               psxRegs.CP0.n.Cause &= ~0x7c;
                psxException(psxRegs.CP0.n.Cause, branch);
        }
 }
 
-__inline void MTC0(int reg, u32 val) {
+void MTC0(int reg, u32 val) {
 //     SysPrintf("MTC0 %d: %x\n", reg, val);
        switch (reg) {
                case 12: // Status
@@ -650,7 +796,8 @@ __inline void MTC0(int reg, u32 val) {
                        break;
 
                case 13: // Cause
-                       psxRegs.CP0.n.Cause = val & ~(0xfc00);
+                       psxRegs.CP0.n.Cause &= ~0x0300;
+                       psxRegs.CP0.n.Cause |= val & 0x0300;
                        psxTestSWInts();
                        break;
 
@@ -686,10 +833,10 @@ void psxCOP0() {
 }
 
 void psxCOP2() {
-       psxCP2[_Funct_]();
+       psxCP2[_Funct_]((struct psxCP2Regs *)&psxRegs.CP2D);
 }
 
-void psxBASIC() {
+void psxBASIC(struct psxCP2Regs *regs) {
        psxCP2BSC[_Rs_]();
 }
 
@@ -735,7 +882,7 @@ void (*psxCP0[32])() = {
        psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL
 };
 
-void (*psxCP2[64])() = {
+void (*psxCP2[64])(struct psxCP2Regs *regs) = {
        psxBASIC, gteRTPS , psxNULL , psxNULL, psxNULL, psxNULL , gteNCLIP, psxNULL, // 00
        psxNULL , psxNULL , psxNULL , psxNULL, gteOP  , psxNULL , psxNULL , psxNULL, // 08
        gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , psxNULL , gteNCDT , psxNULL, // 10
@@ -781,7 +928,7 @@ static void intShutdown() {
 }
 
 // interpreter execution
-inline void execI() { 
+void execI() {
        u32 *code = (u32 *)PSXM(psxRegs.pc);
        psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));