From 1ed1d641a614eb79154dad33d02914ee328c913b Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 11 May 2021 20:07:38 +1000 Subject: [PATCH] CPU/Recompiler: Don't use intepreter icache when falling back Fixes broken rendering in TOCA 2. It has self-modifying code every frame, which gets falled back to the interpreter, and using the interpreter's icache, which resulted in stale code executing. --- src/core/bus.cpp | 44 +++++++++++++++++++++++++++++++++---- src/core/cpu_core.cpp | 4 ++-- src/core/cpu_core_private.h | 1 + 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 2055e1a14..e210389d4 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -410,7 +410,8 @@ void UpdateFastmemViews(CPUFastmemMode mode) auto view = m_memory_arena.CreateReservedView(end_address_inclusive - start_address + 1, map_address); if (!view) { - Log_ErrorPrintf("Failed to map reserved region %p (size 0x%08X)", map_address, end_address_inclusive - start_address + 1); + Log_ErrorPrintf("Failed to map reserved region %p (size 0x%08X)", map_address, + end_address_inclusive - start_address + 1); return; } @@ -1664,9 +1665,7 @@ bool FetchInstruction() { DebugAssert(Common::IsAlignedPow2(g_state.regs.npc, 4)); - using namespace Bus; - - PhysicalMemoryAddress address = g_state.regs.npc; + const PhysicalMemoryAddress address = g_state.regs.npc; switch (address >> 29) { case 0x00: // KUSEG 0M-512M @@ -1710,6 +1709,43 @@ bool FetchInstruction() return true; } +bool FetchInstructionForInterpreterFallback() +{ + DebugAssert(Common::IsAlignedPow2(g_state.regs.npc, 4)); + + const PhysicalMemoryAddress address = g_state.regs.npc; + switch (address >> 29) + { + case 0x00: // KUSEG 0M-512M + case 0x04: // KSEG0 - physical memory cached + case 0x05: // KSEG1 - physical memory uncached + { + // We don't use the icache when doing interpreter fallbacks, because it's probably stale. + if (!DoInstructionRead(address, &g_state.next_instruction.bits)) + return false; + } + break; + + case 0x01: // KUSEG 512M-1024M + case 0x02: // KUSEG 1024M-1536M + case 0x03: // KUSEG 1536M-2048M + case 0x06: // KSEG2 + case 0x07: // KSEG2 + default: + { + CPU::RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::IBE, + g_state.current_instruction_in_branch_delay_slot, + g_state.current_instruction_was_branch_taken, 0), + address); + return false; + } + } + + g_state.regs.pc = g_state.regs.npc; + g_state.regs.npc += sizeof(g_state.next_instruction.bits); + return true; +} + bool SafeReadInstruction(VirtualMemoryAddress addr, u32* value) { switch (addr >> 29) diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 0615dfc03..0f42d60ab 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -2009,7 +2009,7 @@ template void InterpretUncachedBlock() { g_state.regs.npc = g_state.regs.pc; - if (!FetchInstruction()) + if (!FetchInstructionForInterpreterFallback()) return; // At this point, pc contains the last address executed (in the previous block). The instruction has not been fetched @@ -2032,7 +2032,7 @@ void InterpretUncachedBlock() const bool branch = IsBranchInstruction(g_state.current_instruction); if (!g_state.current_instruction_in_branch_delay_slot || branch) { - if (!FetchInstruction()) + if (!FetchInstructionForInterpreterFallback()) break; } else diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index c2492ca8e..fac106b0d 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -100,6 +100,7 @@ ALWAYS_INLINE VirtualMemoryAddress PhysicalAddressToVirtual(PhysicalMemoryAddres // defined in bus.cpp - memory access functions which return false if an exception was thrown. bool FetchInstruction(); +bool FetchInstructionForInterpreterFallback(); bool SafeReadInstruction(VirtualMemoryAddress addr, u32* value); bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value); bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);