drc: botched msb bit check
[pcsx_rearmed.git] / libpcsxcore / psxinterpreter.c
index 02e00a9..f7898e9 100644 (file)
@@ -26,6 +26,8 @@
 #include "gte.h"
 #include "psxhle.h"
 #include "debug.h"
+#include "psxinterpreter.h"
+#include <assert.h>
 
 static int branch = 0;
 static int branch2 = 0;
@@ -39,8 +41,6 @@ static u32 branchPC;
 #define debugI()
 #endif
 
-void execI();
-
 // Subsets
 void (*psxBSC[64])();
 void (*psxSPC[64])();
@@ -49,64 +49,52 @@ void (*psxCP0[32])();
 void (*psxCP2[64])(struct psxCP2Regs *regs);
 void (*psxCP2BSC[32])();
 
-#ifdef ICACHE_EMULATION
+static u32 fetchNoCache(u32 pc)
+{
+       u32 *code = (u32 *)PSXM(pc);
+       return ((code == NULL) ? 0 : SWAP32(*code));
+}
+
 /*
 Formula One 2001 :
 Use old CPU cache code when the RAM location is updated with new code (affects in-game racing)
 */
-static u8* ICache_Addr;
-static u8* ICache_Code;
-uint32_t *Read_ICache(uint32_t pc)
-{
-       uint32_t pc_bank, pc_offset, pc_cache;
-       uint8_t *IAddr, *ICode;
+static struct cache_entry {
+       u32 tag;
+       u32 data[4];
+} ICache[256];
 
-       pc_bank = pc >> 24;
-       pc_offset = pc & 0xffffff;
-       pc_cache = pc & 0xfff;
-
-       IAddr = ICache_Addr;
-       ICode = ICache_Code;
-
-       // cached - RAM
-       if (pc_bank == 0x80 || pc_bank == 0x00)
+static u32 fetchICache(u32 pc)
+{
+       // cached?
+       if (pc < 0xa0000000)
        {
-               if (SWAP32(*(uint32_t *)(IAddr + pc_cache)) == pc_offset)
-               {
-                       // Cache hit - return last opcode used
-                       return (uint32_t *)(ICode + pc_cache);
-               }
-               else
+               // 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)
                {
-                       // Cache miss - addresses don't match
-                       // - default: 0xffffffff (not init)
-
-                       // cache line is 4 bytes wide
-                       pc_offset &= ~0xf;
-                       pc_cache &= ~0xf;
-
-                       // address line
-                       *(uint32_t *)(IAddr + pc_cache + 0x0) = SWAP32(pc_offset + 0x0);
-                       *(uint32_t *)(IAddr + pc_cache + 0x4) = SWAP32(pc_offset + 0x4);
-                       *(uint32_t *)(IAddr + pc_cache + 0x8) = SWAP32(pc_offset + 0x8);
-                       *(uint32_t *)(IAddr + pc_cache + 0xc) = SWAP32(pc_offset + 0xc);
-
-                       // opcode line
-                       pc_offset = pc & ~0xf;
-                       *(uint32_t *)(ICode + pc_cache + 0x0) = psxMu32ref(pc_offset + 0x0);
-                       *(uint32_t *)(ICode + pc_cache + 0x4) = psxMu32ref(pc_offset + 0x4);
-                       *(uint32_t *)(ICode + pc_cache + 0x8) = psxMu32ref(pc_offset + 0x8);
-                       *(uint32_t *)(ICode + pc_cache + 0xc) = psxMu32ref(pc_offset + 0xc);
+                       u32 *code = (u32 *)PSXM(pc & ~0x0f);
+                       if (!code)
+                               return 0;
+
+                       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];
        }
 
-       /*
-       TODO: Probably should add cached BIOS
-       */
-       // default
-       return (uint32_t *)PSXM(pc);
+       return fetchNoCache(pc);
 }
-#endif
+
+u32 (*fetch)(u32 pc) = fetchNoCache;
 
 static void delayRead(int reg, u32 bpc) {
        u32 rold, rnew;
@@ -322,21 +310,7 @@ int psxTestLoadDelay(int reg, u32 tmp) {
 }
 
 void psxDelayTest(int reg, u32 bpc) {
-       u32 *code;
-       u32 tmp;
-
-       #ifdef ICACHE_EMULATION
-       if (Config.icache_emulation)
-       {
-               code = Read_ICache(psxRegs.pc);
-       }
-       else
-       #endif
-       {
-               code = (u32 *)PSXM(psxRegs.pc);
-       }
-
-       tmp = ((code == NULL) ? 0 : SWAP32(*code));
+       u32 tmp = fetch(psxRegs.pc);
        branch = 1;
 
        switch (psxTestLoadDelay(reg, tmp)) {
@@ -356,20 +330,9 @@ void psxDelayTest(int reg, u32 bpc) {
 }
 
 static u32 psxBranchNoDelay(void) {
-       u32 *code;
        u32 temp;
 
-       #ifdef ICACHE_EMULATION
-       if (Config.icache_emulation)
-       {
-               code = Read_ICache(psxRegs.pc);
-       }
-       else
-       #endif
-       {
-               code = (u32 *)PSXM(psxRegs.pc);
-       }
-       psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));
+       psxRegs.code = fetch(psxRegs.pc);
        switch (_Op_) {
                case 0x00: // SPECIAL
                        switch (_Funct_) {
@@ -487,7 +450,6 @@ static int psxDelayBranchTest(u32 tar1) {
 }
 
 static void doBranch(u32 tar) {
-       u32 *code;
        u32 tmp;
 
        branch2 = branch = 1;
@@ -497,17 +459,7 @@ static void doBranch(u32 tar) {
        if (psxDelayBranchTest(tar))
                return;
 
-       #ifdef ICACHE_EMULATION
-       if (Config.icache_emulation)
-       {
-               code = Read_ICache(psxRegs.pc);
-       }
-       else
-       #endif
-       {
-               code = (u32 *)PSXM(psxRegs.pc);
-       }
-       psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));
+       psxRegs.code = fetch(psxRegs.pc);
 
        debugI();
 
@@ -611,6 +563,11 @@ void psxDIV() {
     }
 }
 
+void psxDIV_stall() {
+       psxRegs.muldivBusyCycle = psxRegs.cycle + 37;
+       psxDIV();
+}
+
 void psxDIVU() {
        if (_rRt_ != 0) {
                _rLo_ = _rRs_ / _rRt_;
@@ -622,6 +579,11 @@ void psxDIVU() {
        }
 }
 
+void psxDIVU_stall() {
+       psxRegs.muldivBusyCycle = psxRegs.cycle + 37;
+       psxDIVU();
+}
+
 void psxMULT() {
        u64 res = (s64)((s64)_i32(_rRs_) * (s64)_i32(_rRt_));
 
@@ -629,6 +591,15 @@ void psxMULT() {
        psxRegs.GPR.n.hi = (u32)((res >> 32) & 0xffffffff);
 }
 
+void psxMULT_stall() {
+       // approximate, but maybe good enough
+       u32 rs = _rRs_;
+       u32 lz = __builtin_clz(((rs ^ ((s32)rs >> 21)) | 1));
+       u32 c = 7 + (2 - (lz / 11)) * 4;
+       psxRegs.muldivBusyCycle = psxRegs.cycle + c;
+       psxMULT();
+}
+
 void psxMULTU() {
        u64 res = (u64)((u64)_u32(_rRs_) * (u64)_u32(_rRt_));
 
@@ -636,6 +607,14 @@ void psxMULTU() {
        psxRegs.GPR.n.hi = (u32)((res >> 32) & 0xffffffff);
 }
 
+void psxMULTU_stall() {
+       // approximate, but maybe good enough
+       u32 lz = __builtin_clz(_rRs_ | 1);
+       u32 c = 7 + (2 - (lz / 11)) * 4;
+       psxRegs.muldivBusyCycle = psxRegs.cycle + c;
+       psxMULTU();
+}
+
 /*********************************************************
 * Register branch logic                                  *
 * Format:  OP rs, offset                                 *
@@ -679,6 +658,18 @@ void psxLUI() { if (!_Rt_) return; _u32(_rRt_) = psxRegs.code << 16; } // Upper
 void psxMFHI() { if (!_Rd_) return; _rRd_ = _rHi_; } // Rd = Hi
 void psxMFLO() { if (!_Rd_) return; _rRd_ = _rLo_; } // Rd = Lo
 
+static void mflohiCheckStall(void)
+{
+       u32 left = psxRegs.muldivBusyCycle - psxRegs.cycle;
+       if (left <= 37) {
+               //printf("muldiv stall %u\n", left);
+               psxRegs.cycle = psxRegs.muldivBusyCycle;
+       }
+}
+
+void psxMFHI_stall() { mflohiCheckStall(); psxMFHI(); }
+void psxMFLO_stall() { mflohiCheckStall(); psxMFLO(); }
+
 /*********************************************************
 * Move to GPR to HI/LO & Register jump                   *
 * Format:  OP rs                                         *
@@ -938,6 +929,12 @@ void psxCOP2() {
        psxCP2[_Funct_]((struct psxCP2Regs *)&psxRegs.CP2D);
 }
 
+void psxCOP2_stall() {
+       u32 f = _Funct_;
+       gteCheckStall(f);
+       psxCP2[f]((struct psxCP2Regs *)&psxRegs.CP2D);
+}
+
 void psxBASIC(struct psxCP2Regs *regs) {
        psxCP2BSC[_Rs_]();
 }
@@ -1012,38 +1009,11 @@ void (*psxCP2BSC[32])() = {
 ///////////////////////////////////////////
 
 static int intInit() {
-       #ifdef ICACHE_EMULATION
-       /* We have to allocate the icache memory even if 
-        * the user has not enabled it as otherwise it can cause issues.
-        */
-       if (!ICache_Addr)
-       {
-               ICache_Addr = malloc(0x1000);
-               if (!ICache_Addr)
-               {
-                       return -1;
-               }
-       }
-
-       if (!ICache_Code)
-       {
-               ICache_Code = malloc(0x1000);
-               if (!ICache_Code)
-               {
-                       return -1;
-               }
-       }
-       memset(ICache_Addr, 0xff, 0x1000);
-       memset(ICache_Code, 0xff, 0x1000);
-       #endif
        return 0;
 }
 
 static void intReset() {
-       #ifdef ICACHE_EMULATION
-       memset(ICache_Addr, 0xff, 0x1000);
-       memset(ICache_Code, 0xff, 0x1000);
-       #endif
+       memset(&ICache, 0xff, sizeof(ICache));
 }
 
 void intExecute() {
@@ -1061,46 +1031,60 @@ static void intClear(u32 Addr, u32 Size) {
 }
 
 void intNotify (int note, void *data) {
-       #ifdef ICACHE_EMULATION
        /* Gameblabla - Only clear the icache if it's isolated */
        if (note == R3000ACPU_NOTIFY_CACHE_ISOLATED)
        {
-               memset(ICache_Addr, 0xff, 0x1000);
-               memset(ICache_Code, 0xff, 0x1000);
+               memset(&ICache, 0xff, sizeof(ICache));
        }
-       #endif
 }
 
-static void intShutdown() {
-       #ifdef ICACHE_EMULATION
-       if (ICache_Addr)
-       {
-               free(ICache_Addr);
-               ICache_Addr = NULL;
+void intApplyConfig() {
+       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);
+       assert(psxSPC[18] == psxMFLO  || psxSPC[18] == psxMFLO_stall);
+       assert(psxSPC[24] == psxMULT  || psxSPC[24] == psxMULT_stall);
+       assert(psxSPC[25] == psxMULTU || psxSPC[25] == psxMULTU_stall);
+       assert(psxSPC[26] == psxDIV   || psxSPC[26] == psxDIV_stall);
+       assert(psxSPC[27] == psxDIVU  || psxSPC[27] == psxDIVU_stall);
+
+       if (Config.DisableStalls) {
+               psxBSC[18] = psxCOP2;
+               psxBSC[50] = gteLWC2;
+               psxBSC[58] = gteSWC2;
+               psxSPC[16] = psxMFHI;
+               psxSPC[18] = psxMFLO;
+               psxSPC[24] = psxMULT;
+               psxSPC[25] = psxMULTU;
+               psxSPC[26] = psxDIV;
+               psxSPC[27] = psxDIVU;
+       } else {
+               psxBSC[18] = psxCOP2_stall;
+               psxBSC[50] = gteLWC2_stall;
+               psxBSC[58] = gteSWC2_stall;
+               psxSPC[16] = psxMFHI_stall;
+               psxSPC[18] = psxMFLO_stall;
+               psxSPC[24] = psxMULT_stall;
+               psxSPC[25] = psxMULTU_stall;
+               psxSPC[26] = psxDIV_stall;
+               psxSPC[27] = psxDIVU_stall;
        }
 
-       if (ICache_Code)
-       {
-               free(ICache_Code);
-               ICache_Code = NULL;
-       }
-       #endif
+       // 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)
+       if (!Config.icache_emulation || psxCpu != &psxInt)
+               fetch = fetchNoCache;
+       else
+               fetch = fetchICache;
+}
+
+static void intShutdown() {
 }
 
 // interpreter execution
 void execI() {
-       u32 *code;
-       #ifdef ICACHE_EMULATION
-       if (Config.icache_emulation)
-       {
-               code = Read_ICache(psxRegs.pc);
-       }
-       else
-       #endif
-       {
-               code = (u32 *)PSXM(psxRegs.pc);
-       }
-       psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code));
+       psxRegs.code = fetch(psxRegs.pc);
 
        debugI();
 
@@ -1118,8 +1102,7 @@ R3000Acpu psxInt = {
        intExecute,
        intExecuteBlock,
        intClear,
-#ifdef ICACHE_EMULATION
        intNotify,
-#endif
+       intApplyConfig,
        intShutdown
 };