aboutsummaryrefslogtreecommitdiff
path: root/libpcsxcore
diff options
context:
space:
mode:
authornotaz2011-01-23 21:30:52 +0200
committernotaz2011-01-24 00:38:48 +0200
commit990cb018280df6be2114528a0e198200985890c2 (patch)
tree6c17c7dbf4c9cc3555b43b35c06427c5ec2cf03a /libpcsxcore
parent0d16cda294b8f4655dda849a526b336a5b46aa16 (diff)
downloadpcsx_rearmed-990cb018280df6be2114528a0e198200985890c2.tar.gz
pcsx_rearmed-990cb018280df6be2114528a0e198200985890c2.tar.bz2
pcsx_rearmed-990cb018280df6be2114528a0e198200985890c2.zip
interpreter: add handling for a branch in delay slot
Diffstat (limited to 'libpcsxcore')
-rw-r--r--libpcsxcore/psxinterpreter.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/libpcsxcore/psxinterpreter.c b/libpcsxcore/psxinterpreter.c
index 08b656f..3736b23 100644
--- a/libpcsxcore/psxinterpreter.c
+++ b/libpcsxcore/psxinterpreter.c
@@ -273,6 +273,128 @@ void psxDelayTest(int reg, u32 bpc) {
psxBranchTest();
}
+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);
+}
+
__inline void doBranch(u32 tar) {
u32 *code;
u32 tmp;
@@ -280,6 +402,10 @@ __inline void doBranch(u32 tar) {
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));