#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
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, ®s->CP0);
+ return 0; // execute as nop
+ }
+ code = (u32 *)(base + (pc & 0xfffc));
return SWAP32(*code);
}
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)
{
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, ®s->CP0);
+ return 0; // execute as nop
+ }
code = (u32 *)(base + (pc & 0xfff0));
entry->tag = 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.
}
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;
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
}
static void psxDelayTest(int reg, u32 bpc) {
- u32 tmp = fetch(psxMemRLUT, bpc);
+ u32 tmp = intFakeFetch(bpc);
branch = 1;
switch (psxTestLoadDelay(reg, tmp)) {
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_) {
if (tar2 == (u32)-1)
return 0;
- debugI();
-
/*
* Branch in delay slot:
* - execute 1 instruction at tar1
if (tmp1 == (u32)-1) {
return psxDelayBranchExec(tar2);
}
- debugI();
addCycle();
/*
if (tmp2 == (u32)-1) {
return psxDelayBranchExec(tmp1);
}
- debugI();
addCycle();
/*
}
static void doBranch(u32 tar) {
- u32 tmp, code;
+ u32 tmp, code, pc;
branch2 = branch = 1;
branchPC = 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
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, ®s->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, ®s->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
* 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
*********************************************************/
OP(psxBREAK) {
regs_->pc -= 4;
- psxException(0x24, branch, ®s_->CP0);
+ psxException(R3000E_Bp << 2, branch, ®s_->CP0);
}
OP(psxSYSCALL) {
regs_->pc -= 4;
- psxException(0x20, branch, ®s_->CP0);
+ psxException(R3000E_Syscall << 2, branch, ®s_->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, ®s_->CP0);
}
// SysPrintf("psxRFE\n");
regs_->CP0.n.Status = (regs_->CP0.n.Status & 0xfffffff0) |
((regs_->CP0.n.Status & 0x3c) >> 2);
- psxTestSWInts(regs_);
+ psxTestSWInts(regs_, 0);
}
/*********************************************************
* 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);
}
/*********************************************************
* 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, ®s_->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:
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, ®s_->CP0);
#endif
}
-OP(psxNULL) { psxNULL_(); }
-void gteNULL(struct psxCP2Regs *regs) { psxNULL_(); }
+void gteNULL(struct psxCP2Regs *regs) {
+ psxNULL_();
+}
OP(psxSPECIAL) {
psxSPC[_Funct_](regs_, code);
}
}
+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, ®s_->CP0);
+#endif
+}
+
OP(psxCOP2) {
psxCP2[_Funct_](®s_->CP2);
}
psxCP2[f](®s_->CP2);
}
+OP(psxCOP2d) {
+#ifdef DO_EXCEPTION_RESERVEDI
+ regs_->pc -= 4;
+ psxException((2<<28) | (R3000E_RI << 2), branch, ®s_->CP0);
+#endif
+}
+
OP(gteMFC2) {
if (!_Rt_) return;
regs_->GPR.r[_Rt_] = MFC2(®s_->CP2, _Rd_);
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, ®s_->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;
}
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) = {
}
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);
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:
}
}
+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);
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)