X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libpcsxcore%2Fpsxinterpreter.c;h=f7898e9a921f4ff8be4ab2bfff6aa99d42073b0a;hb=c979e8c288de90834ceecfd7a37543a44cdd9402;hp=02e00a9f16906d80f6c6b94aabd98244df642342;hpb=943a507a4156b8f5b00e4431152e41eeb4dc6f3d;p=pcsx_rearmed.git diff --git a/libpcsxcore/psxinterpreter.c b/libpcsxcore/psxinterpreter.c index 02e00a9f..f7898e9a 100644 --- a/libpcsxcore/psxinterpreter.c +++ b/libpcsxcore/psxinterpreter.c @@ -26,6 +26,8 @@ #include "gte.h" #include "psxhle.h" #include "debug.h" +#include "psxinterpreter.h" +#include 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 };