+static void (INT_ATTR *psxBSC[64])(psxRegisters *regs_, u32 code);
+static void (INT_ATTR *psxSPC[64])(psxRegisters *regs_, u32 code);
+
+// 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];
+ 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);
+}
+
+/*
+Formula One 2001 :
+Use old CPU cache code when the RAM location is updated with new code (affects in-game racing)
+*/
+static struct cache_entry {
+ u32 tag;
+ u32 data[4];
+} ICache[256];
+
+static u32 INT_ATTR fetchICache(psxRegisters *regs, u8 **memRLUT, u32 pc)
+{
+ // cached?
+ if (pc < 0xa0000000)
+ {
+ // this is not how the hardware works but whatever
+ struct cache_entry *entry = &ICache[(pc & 0xff0) >> 4];
+
+ if (((entry->tag ^ pc) & 0xfffffff0) != 0 || pc < entry->tag)
+ {
+ const u8 *base = memRLUT[pc >> 16];
+ const 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 & 0xfff0));
+
+ entry->tag = pc;
+ // treat as 4 words, although other configurations are said to be possible
+ switch (pc & 0x0c)
+ {
+ case 0x00: entry->data[0] = SWAP32(code[0]);
+ case 0x04: entry->data[1] = SWAP32(code[1]);
+ case 0x08: entry->data[2] = SWAP32(code[2]);
+ case 0x0c: entry->data[3] = SWAP32(code[3]);
+ }
+ }
+ return entry->data[(pc & 0x0f) >> 2];
+ }
+
+ return fetchNoCache(regs, memRLUT, pc);
+}
+
+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.
+// FIXME: count cache misses, memory latencies, stalls to get rid of this
+static inline void addCycle(void)
+{
+ assert(psxRegs.subCycleStep >= 0x10000);
+ psxRegs.subCycle += psxRegs.subCycleStep;
+ psxRegs.cycle += psxRegs.subCycle >> 16;
+ psxRegs.subCycle &= 0xffff;
+}