* 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 <nicolasnoble@users.noreply.github.com>
* 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 <nicolasnoble@users.noreply.github.com>
* 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 <nicolasnoble@users.noreply.github.com>
* [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 <nicolasnoble@users.noreply.github.com>
need_xlib="no"
need_libpicofe="yes"
need_warm="no"
+enable_icache_emu="yes"
CFLAGS_GLES=""
LDLIBS_GLES=""
# these are for known platforms
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"
drc_cache_base="yes"
optimize_arm926ej="yes"
need_warm="yes"
+ enable_icache_emu="no"
;;
libretro)
sound_drivers="libretro"
;;
--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
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
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64"
fi
+if [ "$enable_icache_emu" = "yes" ]; then
+ CFLAGS="$CFLAGS -DICACHE_EMULATION"
+fi
+
cat > $TMPC <<EOF
void test(void *f, void *d) { fread(d, 1, 1, f); }
EOF
{
// try to set sane config on which most games work
Config.Xa = Config.Cdda = Config.Sio =
- Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
+ Config.icache_emulation = Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
Config.PsxAuto = 1;
pl_rearmed_cbs.gpu_neon.allow_interlace = 2; // auto
CE_CONFIG_VAL(SpuIrq),
CE_CONFIG_VAL(RCntFix),
CE_CONFIG_VAL(VSyncWA),
+ CE_CONFIG_VAL(icache_emulation),
CE_CONFIG_VAL(Cpu),
CE_INTVAL(region),
CE_INTVAL_V(g_scaler, 3),
"Might be useful to overcome some dynarec bugs";
static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
"must reload game for any change to take effect";
-
+static const char h_cfg_icache[] = "Allows you to play the F1 games.\n"
+ "Note: This breaks the PAL version of Spyro 2.";
+
static menu_entry e_menu_adv_options[] =
{
mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
//mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
+#ifdef ICACHE_EMULATION
+ mee_onoff_h ("ICache emulation", 0, Config.icache_emulation, 1, h_cfg_icache),
+#endif
//mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
invalidate_block(start);
}
+#ifdef ICACHE_EMULATION
+static void ari64_notify(int note, void *data) {
+ /*
+ Should be fixed when ARM dynarec has proper icache emulation.
+ switch (note)
+ {
+ case R3000ACPU_NOTIFY_CACHE_UNISOLATED:
+ break;
+ case R3000ACPU_NOTIFY_CACHE_ISOLATED:
+ Sent from psxDma3().
+ case R3000ACPU_NOTIFY_DMA3_EXE_LOAD:
+ default:
+ break;
+ }
+ */
+}
+#endif
+
static void ari64_shutdown()
{
new_dynarec_cleanup();
intExecuteBlockT,
#endif
ari64_clear,
+#ifdef ICACHE_EMULATION
+ ari64_notify,
+#endif
ari64_shutdown
};
#ifdef PSXBIOS_LOG
PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x44]);
#endif
-
+#ifdef ICACHE_EMULATION
+ psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
+ psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
+#endif
pc0 = ra;
}
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
u8 psxHwRead8(u32 add) {
unsigned char hard;
- switch (add) {
+ switch (add & 0x1fffffff) {
case 0x1f801040: hard = sioRead8();break; \r
#ifdef ENABLE_SIO1API
case 0x1f801050: hard = SIO1_readData8(); break;\r
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);
u32 psxHwRead32(u32 add) {
u32 hard;
- switch (add) {
+ switch (add & 0x1fffffff) {
case 0x1f801040:
hard = sioRead8();
hard |= sioRead8() << 8;
}
void psxHwWrite8(u32 add, u8 value) {
- switch (add) {
+ switch (add & 0x1fffffff) {
case 0x1f801040: sioWrite8(value); break;\r
#ifdef ENABLE_SIO1API
case 0x1f801050: SIO1_writeData8(value); break;\r
}
void psxHwWrite16(u32 add, u16 value) {
- switch (add) {
+ switch (add & 0x1fffffff) {
case 0x1f801040:
sioWrite8((unsigned char)value);
sioWrite8((unsigned char)(value>>8));
}
void psxHwWrite32(u32 add, u32 value) {
- switch (add) {
+ switch (add & 0x1fffffff) {
case 0x1f801040:
sioWrite8((unsigned char)value);
sioWrite8((unsigned char)((value&0xff) >> 8));
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;
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;
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
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();
* 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
* 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 *
* Format: OP *
*********************************************************/
void psxBREAK() {
- // Break exception - psx rom doens't handles this
+ psxRegs.pc -= 4;
+ psxException(0x24, branch);
}
void psxSYSCALL() {
// SysPrintf("psxRFE\n");
psxRegs.CP0.n.Status = (psxRegs.CP0.n.Status & 0xfffffff0) |
((psxRegs.CP0.n.Status & 0x3c) >> 2);
+ psxTestSWInts();
}
/*********************************************************
* 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);
}
/*********************************************************
///////////////////////////////////////////
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() {
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();
intExecute,
intExecuteBlock,
intClear,
+#ifdef ICACHE_EMULATION
+ intNotify,
+#endif
intShutdown
};
#ifdef PSXMEM_LOG
PSXMEM_LOG("err lb %8.8lx\n", mem);
#endif
- return 0;
+ return 0xFF;
}
}
}
#ifdef PSXMEM_LOG
PSXMEM_LOG("err lh %8.8lx\n", mem);
#endif
- return 0;
+ return 0xFFFF;
}
}
}
#ifdef PSXMEM_LOG
if (writeok) { PSXMEM_LOG("err lw %8.8lx\n", mem); }
#endif
- return 0;
+ return 0xFFFFFFFF;
}
}
}
}
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)
#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
#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;