+// load delay
+static void doLoad(psxRegisters *regs, u32 r, u32 val)
+{
+#ifdef HANDLE_LOAD_DELAY
+ int sel = regs->dloadSel ^ 1;
+ assert(regs->dloadReg[sel] == 0);
+ regs->dloadReg[sel] = r;
+ regs->dloadVal[sel] = r ? val : 0;
+ if (regs->dloadReg[sel ^ 1] == r)
+ regs->dloadVal[sel ^ 1] = regs->dloadReg[sel ^ 1] = 0;
+#else
+ regs->GPR.r[r] = r ? val : 0;
+#endif
+}
+
+static void dloadRt(psxRegisters *regs, u32 r, u32 val)
+{
+#ifdef HANDLE_LOAD_DELAY
+ int sel = regs->dloadSel;
+ if (unlikely(regs->dloadReg[sel] == r))
+ regs->dloadVal[sel] = regs->dloadReg[sel] = 0;
+#endif
+ regs->GPR.r[r] = r ? val : 0;
+}
+
+static void dloadStep(psxRegisters *regs)
+{
+#ifdef HANDLE_LOAD_DELAY
+ int sel = regs->dloadSel;
+ regs->GPR.r[regs->dloadReg[sel]] = regs->dloadVal[sel];
+ regs->dloadVal[sel] = regs->dloadReg[sel] = 0;
+ regs->dloadSel ^= 1;
+ assert(regs->GPR.r[0] == 0);
+#endif
+}
+
+static void dloadFlush(psxRegisters *regs)
+{
+#ifdef HANDLE_LOAD_DELAY
+ regs->GPR.r[regs->dloadReg[0]] = regs->dloadVal[0];
+ regs->GPR.r[regs->dloadReg[1]] = regs->dloadVal[1];
+ regs->dloadVal[0] = regs->dloadVal[1] = 0;
+ regs->dloadReg[0] = regs->dloadReg[1] = 0;
+ assert(regs->GPR.r[0] == 0);
+#endif
+}
+
+static void dloadClear(psxRegisters *regs)
+{
+#ifdef HANDLE_LOAD_DELAY
+ regs->dloadVal[0] = regs->dloadVal[1] = 0;
+ regs->dloadReg[0] = regs->dloadReg[1] = 0;
+ regs->dloadSel = 0;
+#endif
+}
+
+static void intException(psxRegisters *regs, u32 pc, u32 cause)
+{
+ if (cause != 0x20) {
+ //FILE *f = fopen("/tmp/psx_ram.bin", "wb");
+ //fwrite(psxM, 1, 0x200000, f); fclose(f);
+ log_unhandled("exception %08x @%08x ra=%08x\n",
+ cause, pc, regs->GPR.n.ra);
+ }
+ dloadFlush(regs);
+ regs->pc = pc;
+ psxException(cause, regs->branching, ®s->CP0);
+ regs->branching = R3000A_BRANCH_NONE_OR_EXCEPTION;
+}
+
+// exception caused by current instruction (excluding unkasking)
+static void intExceptionInsn(psxRegisters *regs, u32 cause)
+{
+ cause |= (regs->code & 0x0c000000) << 2;
+ intException(regs, regs->pc - 4, cause);
+}
+
+static noinline void intExceptionReservedInsn(psxRegisters *regs)
+{
+#ifdef DO_EXCEPTION_RESERVEDI
+ static u32 ppc_ = ~0u;
+ if (regs->pc != ppc_) {
+ SysPrintf("reserved instruction %08x @%08x ra=%08x\n",
+ regs->code, regs->pc - 4, regs->GPR.n.ra);
+ ppc_ = regs->pc;
+ }
+ intExceptionInsn(regs, R3000E_RI << 2);
+#endif
+}
+
+// 29 Enable for 80000000-ffffffff
+// 30 Enable for 00000000-7fffffff
+// 31 Enable exception
+#define DBR_ABIT(dc, a) ((dc) & (1u << (29+(((a)>>31)^1))))
+#define DBR_EN_EXEC(dc, a) (((dc) & 0x01800000) == 0x01800000 && DBR_ABIT(dc, a))
+#define DBR_EN_LD(dc, a) (((dc) & 0x06800000) == 0x06800000 && DBR_ABIT(dc, a))
+#define DBR_EN_ST(dc, a) (((dc) & 0x0a800000) == 0x0a800000 && DBR_ABIT(dc, a))
+static void intExceptionDebugBp(psxRegisters *regs, u32 pc)
+{
+ psxCP0Regs *cp0 = ®s->CP0;
+ dloadFlush(regs);
+ cp0->n.Cause &= 0x300;
+ cp0->n.Cause |= (regs->branching << 30) | (R3000E_Bp << 2);
+ cp0->n.SR = (cp0->n.SR & ~0x3f) | ((cp0->n.SR & 0x0f) << 2);
+ cp0->n.EPC = regs->branching ? pc - 4 : pc;
+ psxRegs.pc = 0x80000040;
+}
+
+static int execBreakCheck(psxRegisters *regs, u32 pc)
+{
+ if (unlikely(DBR_EN_EXEC(regs->CP0.n.DCIC, pc) &&
+ ((pc ^ regs->CP0.n.BPC) & regs->CP0.n.BPCM) == 0))
+ {
+ regs->CP0.n.DCIC |= 0x03;
+ if (regs->CP0.n.DCIC & (1u << 31)) {
+ intExceptionDebugBp(regs, pc);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// 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)