X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=pcsx_rearmed.git;a=blobdiff_plain;f=libpcsxcore%2Fpsxinterpreter.c;h=d2225c417b5d46ed122dffce9ed5f908057db12a;hp=f1712050bb2068982f7bf70172032d847738528b;hb=61ad2a6193b343bed12af5400746254583339304;hpb=abbc1ca1f763c1f4aa46b60f3ea7dfb4bed3fb6f diff --git a/libpcsxcore/psxinterpreter.c b/libpcsxcore/psxinterpreter.c index f1712050..d2225c41 100644 --- a/libpcsxcore/psxinterpreter.c +++ b/libpcsxcore/psxinterpreter.c @@ -25,6 +25,9 @@ #include "r3000a.h" #include "gte.h" #include "psxhle.h" +#include "debug.h" +#include "psxinterpreter.h" +#include static int branch = 0; static int branch2 = 0; @@ -38,16 +41,79 @@ static u32 branchPC; #define debugI() #endif -inline void execI(); - // Subsets void (*psxBSC[64])(); void (*psxSPC[64])(); void (*psxREG[32])(); void (*psxCP0[32])(); -void (*psxCP2[64])(); +void (*psxCP2[64])(struct psxCP2Regs *regs); void (*psxCP2BSC[32])(); +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; +static u32 fetchICache(u32 pc) +{ + uint32_t pc_bank, pc_offset, pc_cache; + uint8_t *IAddr, *ICode; + + 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) + { + if (SWAP32(*(uint32_t *)(IAddr + pc_cache)) == pc_offset) + { + // Cache hit - return last opcode used + return *(uint32_t *)(ICode + pc_cache); + } + else + { + // 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); + } + } + + /* + TODO: Probably should add cached BIOS + */ + // default + return fetchNoCache(pc); +} + +u32 (*fetch)(u32 pc) = fetchNoCache; + static void delayRead(int reg, u32 bpc) { u32 rold, rnew; @@ -262,11 +328,7 @@ int psxTestLoadDelay(int reg, u32 tmp) { } void psxDelayTest(int reg, u32 bpc) { - u32 *code; - u32 tmp; - - code = (u32 *)PSXM(bpc); - tmp = ((code == NULL) ? 0 : SWAP32(*code)); + u32 tmp = fetch(psxRegs.pc); branch = 1; switch (psxTestLoadDelay(reg, tmp)) { @@ -286,11 +348,9 @@ void psxDelayTest(int reg, u32 bpc) { } static u32 psxBranchNoDelay(void) { - u32 *code; u32 temp; - code = (u32 *)PSXM(psxRegs.pc); - psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code)); + psxRegs.code = fetch(psxRegs.pc); switch (_Op_) { case 0x00: // SPECIAL switch (_Funct_) { @@ -407,8 +467,7 @@ static int psxDelayBranchTest(u32 tar1) { return psxDelayBranchExec(tmp2); } -__inline void doBranch(u32 tar) { - u32 *code; +static void doBranch(u32 tar) { u32 tmp; branch2 = branch = 1; @@ -418,8 +477,7 @@ __inline void doBranch(u32 tar) { if (psxDelayBranchTest(tar)) return; - code = (u32 *)PSXM(psxRegs.pc); - psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code)); + psxRegs.code = fetch(psxRegs.pc); debugI(); @@ -500,10 +558,32 @@ void psxSLTU() { if (!_Rd_) return; _rRd_ = _u32(_rRs_) < _u32(_rRt_); } // Rd * Format: OP rs, rt * *********************************************************/ void psxDIV() { - if (_i32(_rRt_) != 0) { - _i32(_rLo_) = _i32(_rRs_) / _i32(_rRt_); - _i32(_rHi_) = _i32(_rRs_) % _i32(_rRt_); - } + if (!_i32(_rRt_)) { + _i32(_rHi_) = _i32(_rRs_); + if (_i32(_rRs_) & 0x80000000) { + _i32(_rLo_) = 1; + } else { + _i32(_rLo_) = 0xFFFFFFFF; + } +/* + * Notaz said that this was "not needed" for ARM platforms and could slow it down so let's disable for ARM. + * This fixes a crash issue that can happen when running Amidog's CPU test. + * (It still stays stuck to a black screen but at least it doesn't crash anymore) + */ +#if !defined(__arm__) && !defined(__aarch64__) + } else if (_i32(_rRs_) == 0x80000000 && _i32(_rRt_) == 0xFFFFFFFF) { + _i32(_rLo_) = 0x80000000; + _i32(_rHi_) = 0; +#endif + } else { + _i32(_rLo_) = _i32(_rRs_) / _i32(_rRt_); + _i32(_rHi_) = _i32(_rRs_) % _i32(_rRt_); + } +} + +void psxDIV_stall() { + psxRegs.muldivBusyCycle = psxRegs.cycle + 37; + psxDIV(); } void psxDIVU() { @@ -511,6 +591,15 @@ void psxDIVU() { _rLo_ = _rRs_ / _rRt_; _rHi_ = _rRs_ % _rRt_; } + else { + _i32(_rLo_) = 0xffffffff; + _i32(_rHi_) = _i32(_rRs_); + } +} + +void psxDIVU_stall() { + psxRegs.muldivBusyCycle = psxRegs.cycle + 37; + psxDIVU(); } void psxMULT() { @@ -520,6 +609,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_)); @@ -527,12 +625,20 @@ 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 * *********************************************************/ #define RepZBranchi32(op) if(_i32(_rRs_) op 0) doBranch(_BranchTarget_); -#define RepZBranchLinki32(op) if(_i32(_rRs_) op 0) { _SetLink(31); doBranch(_BranchTarget_); } +#define RepZBranchLinki32(op) { _SetLink(31); if(_i32(_rRs_) op 0) { doBranch(_BranchTarget_); } } void psxBGEZ() { RepZBranchi32(>=) } // Branch if Rs >= 0 void psxBGEZAL() { RepZBranchLinki32(>=) } // Branch if Rs >= 0 and link @@ -553,9 +659,9 @@ void psxSRL() { if (!_Rd_) return; _u32(_rRd_) = _u32(_rRt_) >> _Sa_; } // Rd = * Shift arithmetic with variant register shift * * Format: OP rd, rt, rs * *********************************************************/ -void psxSLLV() { if (!_Rd_) return; _u32(_rRd_) = _u32(_rRt_) << _u32(_rRs_); } // Rd = Rt << rs -void psxSRAV() { if (!_Rd_) return; _i32(_rRd_) = _i32(_rRt_) >> _u32(_rRs_); } // Rd = Rt >> rs (arithmetic) -void psxSRLV() { if (!_Rd_) return; _u32(_rRd_) = _u32(_rRt_) >> _u32(_rRs_); } // Rd = Rt >> rs (logical) +void psxSLLV() { if (!_Rd_) return; _u32(_rRd_) = _u32(_rRt_) << (_u32(_rRs_) & 0x1F); } // Rd = Rt << rs +void psxSRAV() { if (!_Rd_) return; _i32(_rRd_) = _i32(_rRt_) >> (_u32(_rRs_) & 0x1F); } // Rd = Rt >> rs (arithmetic) +void psxSRLV() { if (!_Rd_) return; _u32(_rRd_) = _u32(_rRt_) >> (_u32(_rRs_) & 0x1F); } // Rd = Rt >> rs (logical) /********************************************************* * Load higher 16 bits of the first word in GPR with imm * @@ -570,6 +676,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 * @@ -582,7 +700,8 @@ void psxMTLO() { _rLo_ = _rRs_; } // Lo = Rs * Format: OP * *********************************************************/ void psxBREAK() { - // Break exception - psx rom doens't handles this + psxRegs.pc -= 4; + psxException(0x24, branch); } void psxSYSCALL() { @@ -594,6 +713,7 @@ void psxRFE() { // SysPrintf("psxRFE\n"); psxRegs.CP0.n.Status = (psxRegs.CP0.n.Status & 0xfffffff0) | ((psxRegs.CP0.n.Status & 0x3c) >> 2); + psxTestSWInts(); } /********************************************************* @@ -617,14 +737,14 @@ void psxJAL() { _SetLink(31); doBranch(_JumpTarget_); } * Format: OP rs, rd * *********************************************************/ void psxJR() { - doBranch(_u32(_rRs_)); + doBranch(_rRs_ & ~3); psxJumpTest(); } void psxJALR() { u32 temp = _u32(_rRs_); if (_Rd_) { _SetLink(_Rd_); } - doBranch(temp); + doBranch(temp & ~3); } /********************************************************* @@ -718,9 +838,9 @@ void psxLWR() { */ } -void psxSB() { psxMemWrite8 (_oB_, _u8 (_rRt_)); } -void psxSH() { psxMemWrite16(_oB_, _u16(_rRt_)); } -void psxSW() { psxMemWrite32(_oB_, _u32(_rRt_)); } +void psxSB() { psxMemWrite8 (_oB_, _rRt_ & 0xff); } +void psxSH() { psxMemWrite16(_oB_, _rRt_ & 0xffff); } +void psxSW() { psxMemWrite32(_oB_, _rRt_); } u32 SWL_MASK[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0 }; u32 SWL_SHIFT[4] = { 24, 16, 8, 0 }; @@ -771,15 +891,14 @@ void psxMFC0() { if (!_Rt_) return; _i32(_rRt_) = (int)_rFs_; } void psxCFC0() { if (!_Rt_) return; _i32(_rRt_) = (int)_rFs_; } void psxTestSWInts() { - // the next code is untested, if u know please - // tell me if it works ok or not (linuzappz) if (psxRegs.CP0.n.Cause & psxRegs.CP0.n.Status & 0x0300 && - psxRegs.CP0.n.Status & 0x1) { + psxRegs.CP0.n.Status & 0x1) { + psxRegs.CP0.n.Cause &= ~0x7c; psxException(psxRegs.CP0.n.Cause, branch); } } -__inline void MTC0(int reg, u32 val) { +void MTC0(int reg, u32 val) { // SysPrintf("MTC0 %d: %x\n", reg, val); switch (reg) { case 12: // Status @@ -788,7 +907,8 @@ __inline void MTC0(int reg, u32 val) { break; case 13: // Cause - psxRegs.CP0.n.Cause = val & ~(0xfc00); + psxRegs.CP0.n.Cause &= ~0x0300; + psxRegs.CP0.n.Cause |= val & 0x0300; psxTestSWInts(); break; @@ -824,16 +944,28 @@ void psxCOP0() { } void psxCOP2() { - psxCP2[_Funct_](); + psxCP2[_Funct_]((struct psxCP2Regs *)&psxRegs.CP2D); +} + +void psxCOP2_stall() { + u32 f = _Funct_; + gteCheckStall(f); + psxCP2[f]((struct psxCP2Regs *)&psxRegs.CP2D); } -void psxBASIC() { +void psxBASIC(struct psxCP2Regs *regs) { psxCP2BSC[_Rs_](); } void psxHLE() { // psxHLEt[psxRegs.code & 0xffff](); - psxHLEt[psxRegs.code & 0x07](); // HDHOSHY experimental patch +// psxHLEt[psxRegs.code & 0x07](); // HDHOSHY experimental patch + uint32_t hleCode = psxRegs.code & 0x03ffffff; + if (hleCode >= (sizeof(psxHLEt) / sizeof(psxHLEt[0]))) { + psxNULL(); + } else { + psxHLEt[hleCode](); + } } void (*psxBSC[64])() = { @@ -873,7 +1005,7 @@ void (*psxCP0[32])() = { psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL }; -void (*psxCP2[64])() = { +void (*psxCP2[64])(struct psxCP2Regs *regs) = { psxBASIC, gteRTPS , psxNULL , psxNULL, psxNULL, psxNULL , gteNCLIP, psxNULL, // 00 psxNULL , psxNULL , psxNULL , psxNULL, gteOP , psxNULL , psxNULL , psxNULL, // 08 gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , psxNULL , gteNCDT , psxNULL, // 10 @@ -895,10 +1027,34 @@ void (*psxCP2BSC[32])() = { /////////////////////////////////////////// static int intInit() { + /* 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); return 0; } static void intReset() { + memset(ICache_Addr, 0xff, 0x1000); + memset(ICache_Code, 0xff, 0x1000); } void intExecute() { @@ -915,13 +1071,73 @@ void intExecuteBlock() { static void intClear(u32 Addr, u32 Size) { } +void intNotify (int note, void *data) { + /* 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); + } +} + +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; + } + + // 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() { + if (ICache_Addr) + { + free(ICache_Addr); + ICache_Addr = NULL; + } + + if (ICache_Code) + { + free(ICache_Code); + ICache_Code = NULL; + } } // interpreter execution -inline void execI() { - u32 *code = (u32 *)PSXM(psxRegs.pc); - psxRegs.code = ((code == NULL) ? 0 : SWAP32(*code)); +void execI() { + psxRegs.code = fetch(psxRegs.pc); debugI(); @@ -939,5 +1155,7 @@ R3000Acpu psxInt = { intExecute, intExecuteBlock, intClear, + intNotify, + intApplyConfig, intShutdown };