From 943a507a4156b8f5b00e4431152e41eeb4dc6f3d Mon Sep 17 00:00:00 2001 From: gameblabla Date: Sat, 2 Oct 2021 13:41:42 +0000 Subject: [PATCH] Icache emulation from PCSX Redux + Senquack changes from PCSX4ALL (#198) * Merge Icache emulation from PCSX Redux See (Redux) : https://github.com/grumpycoders/pcsx-redux/commit/1923ce54ef585beba3a948d50f8c30161102312c See original icache implementation (mirror of PCSX Reloaded): https://github.com/gameblabla/pcsxr Without icache emulation, F1 2001 will greatly misbehave : if you accelerate, the car will go around like crazy. With icache emulation, it works as intended. Our code is slightly different from theirs as i found out that having the icache arrays in psxregs would cause crashes so instead what i'm doing is to taking them out of there and only allocating them on the heap (due to their great size). Co-authored-by: Nicolas Noble * Fix issues with BREAK and some interpreter commands. Fixes F1 2000. Note that the game is very sensible to timing issues when it comes to the CDROM controller. That will be for a separate commit however. * Culling off cache bits from the hardware addresses. Based on those PRs from PCSX-Redux : https://github.com/grumpycoders/pcsx-redux/commit/0cd940100e96b95eea87dbb47381596f7f8dbe72#diff-009cbf66734b5de152bf170b80f8c7e03bebaa08a191f6ad7a06c7420f24b69c https://github.com/grumpycoders/pcsx-redux/commit/03d2ba3f278868cdd7ee3a44edef7ee87e6a1589#diff-009cbf66734b5de152bf170b80f8c7e03bebaa08a191f6ad7a06c7420f24b69c Co-authored-by: Nicolas Noble * Slightly better "open bus" behavior OG commit is here from PCSX Redux : https://github.com/grumpycoders/pcsx-redux/commit/128ba97f9680ab8dcd2f840f72ae998507325730#diff-8552772bc73559e3448880c9b8126252b49b95a89cfac254148d27127cbec719 Co-authored-by: Nicolas Noble * [Interpreter] Link even if branch is not taken in BGEZAL/BLTZAL Source : grumpycoders/pcsx-redux@c1a0569 Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Co-authored-by: Nicolas Noble --- configure | 13 +++ frontend/main.c | 2 +- frontend/menu.c | 8 +- libpcsxcore/new_dynarec/emu_if.c | 21 ++++ libpcsxcore/psxbios.c | 5 +- libpcsxcore/psxcommon.h | 1 + libpcsxcore/psxhw.c | 12 +-- libpcsxcore/psxinterpreter.c | 176 +++++++++++++++++++++++++++++-- libpcsxcore/psxmem.c | 6 +- libpcsxcore/r3000a.c | 17 ++- libpcsxcore/r3000a.h | 12 +++ 11 files changed, 248 insertions(+), 25 deletions(-) diff --git a/configure b/configure index c3ff68fa..5caf0f49 100755 --- a/configure +++ b/configure @@ -59,6 +59,7 @@ need_sdl="no" need_xlib="no" need_libpicofe="yes" need_warm="no" +enable_icache_emu="yes" CFLAGS_GLES="" LDLIBS_GLES="" # these are for known platforms @@ -93,12 +94,14 @@ set_platform() optimize_cortexa8="yes" have_arm_neon="yes" need_xlib="yes" + enable_icache_emu="no" ;; maemo) ram_fixed="yes" drc_cache_base="yes" optimize_cortexa8="yes" have_arm_neon="yes" + enable_icache_emu="no" ;; caanoo) sound_drivers="oss" @@ -106,6 +109,7 @@ set_platform() drc_cache_base="yes" optimize_arm926ej="yes" need_warm="yes" + enable_icache_emu="no" ;; libretro) sound_drivers="libretro" @@ -134,6 +138,10 @@ for opt do ;; --disable-dynarec) enable_dynarec="no" ;; + --disable-icache-emu) enable_icache_emu="no" + ;; + --enable-icache-emu) enable_icache_emu="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -152,6 +160,7 @@ if [ "$show_help" = "yes" ]; then echo " --disable-neon enable/disable ARM NEON optimizations [guessed]" echo " --disable-dynarec disable dynamic recompiler" echo " (dynarec is only available and enabled on ARM)" + echo " --disable-icache-emu Disables the instruction cache emulation" echo "influential environment variables:" echo " CROSS_COMPILE CC CXX AS AR CFLAGS ASFLAGS LDFLAGS LDLIBS" exit 1 @@ -492,6 +501,10 @@ if [ "x$sizeof_long" = "x4" ]; then CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64" fi +if [ "$enable_icache_emu" = "yes" ]; then + CFLAGS="$CFLAGS -DICACHE_EMULATION" +fi + cat > $TMPC <Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL); + psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL); +#endif pc0 = ra; } diff --git a/libpcsxcore/psxcommon.h b/libpcsxcore/psxcommon.h index 8ef794b1..c9d300aa 100644 --- a/libpcsxcore/psxcommon.h +++ b/libpcsxcore/psxcommon.h @@ -132,6 +132,7 @@ typedef struct { boolean RCntFix; boolean UseNet; boolean VSyncWA; + boolean icache_emulation; u8 Cpu; // CPU_DYNAREC or CPU_INTERPRETER u8 PsxType; // PSX_TYPE_NTSC or PSX_TYPE_PAL #ifdef _WIN32 diff --git a/libpcsxcore/psxhw.c b/libpcsxcore/psxhw.c index c90f8c73..84ce2f73 100644 --- a/libpcsxcore/psxhw.c +++ b/libpcsxcore/psxhw.c @@ -44,7 +44,7 @@ void psxHwReset() { u8 psxHwRead8(u32 add) { unsigned char hard; - switch (add) { + switch (add & 0x1fffffff) { case 0x1f801040: hard = sioRead8();break; #ifdef ENABLE_SIO1API case 0x1f801050: hard = SIO1_readData8(); break; @@ -70,7 +70,7 @@ u8 psxHwRead8(u32 add) { u16 psxHwRead16(u32 add) { unsigned short hard; - switch (add) { + switch (add & 0x1fffffff) { #ifdef PSXHW_LOG case 0x1f801070: PSXHW_LOG("IREG 16bit read %x\n", psxHu16(0x1070)); return psxHu16(0x1070); @@ -204,7 +204,7 @@ u16 psxHwRead16(u32 add) { u32 psxHwRead32(u32 add) { u32 hard; - switch (add) { + switch (add & 0x1fffffff) { case 0x1f801040: hard = sioRead8(); hard |= sioRead8() << 8; @@ -355,7 +355,7 @@ u32 psxHwRead32(u32 add) { } void psxHwWrite8(u32 add, u8 value) { - switch (add) { + switch (add & 0x1fffffff) { case 0x1f801040: sioWrite8(value); break; #ifdef ENABLE_SIO1API case 0x1f801050: SIO1_writeData8(value); break; @@ -379,7 +379,7 @@ void psxHwWrite8(u32 add, u8 value) { } void psxHwWrite16(u32 add, u16 value) { - switch (add) { + switch (add & 0x1fffffff) { case 0x1f801040: sioWrite8((unsigned char)value); sioWrite8((unsigned char)(value>>8)); @@ -518,7 +518,7 @@ void psxHwWrite16(u32 add, u16 value) { } void psxHwWrite32(u32 add, u32 value) { - switch (add) { + switch (add & 0x1fffffff) { case 0x1f801040: sioWrite8((unsigned char)value); sioWrite8((unsigned char)((value&0xff) >> 8)); diff --git a/libpcsxcore/psxinterpreter.c b/libpcsxcore/psxinterpreter.c index e59f93d8..02e00a9f 100644 --- a/libpcsxcore/psxinterpreter.c +++ b/libpcsxcore/psxinterpreter.c @@ -49,6 +49,65 @@ void (*psxCP0[32])(); void (*psxCP2[64])(struct psxCP2Regs *regs); void (*psxCP2BSC[32])(); +#ifdef ICACHE_EMULATION +/* +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; + + 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 (uint32_t *)PSXM(pc); +} +#endif + static void delayRead(int reg, u32 bpc) { u32 rold, rnew; @@ -266,7 +325,17 @@ void psxDelayTest(int reg, u32 bpc) { u32 *code; u32 tmp; - code = (u32 *)PSXM(bpc); + #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)); branch = 1; @@ -290,7 +359,16 @@ static u32 psxBranchNoDelay(void) { u32 *code; u32 temp; - code = (u32 *)PSXM(psxRegs.pc); + #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)); switch (_Op_) { case 0x00: // SPECIAL @@ -419,7 +497,16 @@ static void doBranch(u32 tar) { if (psxDelayBranchTest(tar)) return; - code = (u32 *)PSXM(psxRegs.pc); + #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)); debugI(); @@ -554,7 +641,7 @@ void psxMULTU() { * 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 @@ -575,9 +662,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 * @@ -604,7 +691,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() { @@ -616,6 +704,7 @@ void psxRFE() { // SysPrintf("psxRFE\n"); psxRegs.CP0.n.Status = (psxRegs.CP0.n.Status & 0xfffffff0) | ((psxRegs.CP0.n.Status & 0x3c) >> 2); + psxTestSWInts(); } /********************************************************* @@ -639,14 +728,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); } /********************************************************* @@ -923,10 +1012,38 @@ 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 } void intExecute() { @@ -943,12 +1060,46 @@ void intExecuteBlock() { 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); + } + #endif +} + static void intShutdown() { + #ifdef ICACHE_EMULATION + if (ICache_Addr) + { + free(ICache_Addr); + ICache_Addr = NULL; + } + + if (ICache_Code) + { + free(ICache_Code); + ICache_Code = NULL; + } + #endif } // interpreter execution void execI() { - u32 *code = (u32 *)PSXM(psxRegs.pc); + 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)); debugI(); @@ -967,5 +1118,8 @@ R3000Acpu psxInt = { intExecute, intExecuteBlock, intClear, +#ifdef ICACHE_EMULATION + intNotify, +#endif intShutdown }; diff --git a/libpcsxcore/psxmem.c b/libpcsxcore/psxmem.c index 61b14c65..171104cb 100644 --- a/libpcsxcore/psxmem.c +++ b/libpcsxcore/psxmem.c @@ -240,7 +240,7 @@ u8 psxMemRead8(u32 mem) { #ifdef PSXMEM_LOG PSXMEM_LOG("err lb %8.8lx\n", mem); #endif - return 0; + return 0xFF; } } } @@ -265,7 +265,7 @@ u16 psxMemRead16(u32 mem) { #ifdef PSXMEM_LOG PSXMEM_LOG("err lh %8.8lx\n", mem); #endif - return 0; + return 0xFFFF; } } } @@ -290,7 +290,7 @@ u32 psxMemRead32(u32 mem) { #ifdef PSXMEM_LOG if (writeok) { PSXMEM_LOG("err lw %8.8lx\n", mem); } #endif - return 0; + return 0xFFFFFFFF; } } } diff --git a/libpcsxcore/r3000a.c b/libpcsxcore/r3000a.c index 85b77cb4..e21d4883 100644 --- a/libpcsxcore/r3000a.c +++ b/libpcsxcore/r3000a.c @@ -81,7 +81,21 @@ void psxShutdown() { } void psxException(u32 code, u32 bd) { - if (!Config.HLE && ((((psxRegs.code = PSXMu32(psxRegs.pc)) >> 24) & 0xfe) == 0x4a)) { + #ifdef ICACHE_EMULATION + /* Dynarecs may use this codepath and crash as a result. + * This should only be used for the interpreter. - Gameblabla + * */ + if (Config.icache_emulation && Config.Cpu == CPU_INTERPRETER) + { + psxRegs.code = SWAPu32(*Read_ICache(psxRegs.pc)); + } + else + #endif + { + psxRegs.code = PSXMu32(psxRegs.pc); + } + + if (!Config.HLE && ((((psxRegs.code) >> 24) & 0xfe) == 0x4a)) { // "hokuto no ken" / "Crash Bandicot 2" ... // BIOS does not allow to return to GTE instructions // (just skips it, supposedly because it's scheduled already) @@ -98,7 +112,6 @@ void psxException(u32 code, u32 bd) { #ifdef PSXCPU_LOG PSXCPU_LOG("bd set!!!\n"); #endif - SysPrintf("bd set!!!\n"); psxRegs.CP0.n.Cause |= 0x80000000; psxRegs.CP0.n.EPC = (psxRegs.pc - 4); } else diff --git a/libpcsxcore/r3000a.h b/libpcsxcore/r3000a.h index 399f9b63..4b1ec9e0 100644 --- a/libpcsxcore/r3000a.h +++ b/libpcsxcore/r3000a.h @@ -29,12 +29,24 @@ extern "C" { #include "psxcounters.h" #include "psxbios.h" +#ifdef ICACHE_EMULATION +enum { + R3000ACPU_NOTIFY_CACHE_ISOLATED = 0, + R3000ACPU_NOTIFY_CACHE_UNISOLATED = 1, + R3000ACPU_NOTIFY_DMA3_EXE_LOAD = 2 +}; +extern uint32_t *Read_ICache(uint32_t pc); +#endif + typedef struct { int (*Init)(); void (*Reset)(); void (*Execute)(); /* executes up to a break */ void (*ExecuteBlock)(); /* executes up to a jump */ void (*Clear)(u32 Addr, u32 Size); +#ifdef ICACHE_EMULATION + void (*Notify)(int note, void *data); +#endif void (*Shutdown)(); } R3000Acpu; -- 2.39.2