From c3cef5491014540386f3656a98cb4a27fb43b482 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Sat, 24 Sep 2016 15:14:51 +0000 Subject: [PATCH] PatchEngine: Handle MSR more cleanly Instead of fiddling with the MSR value, just reschedule and try again after the game fixes it itself. --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 4 +++- Source/Core/Core/GeckoCode.cpp | 16 +-------------- Source/Core/Core/GeckoCode.h | 2 +- Source/Core/Core/HW/SystemTimers.cpp | 26 ++++++++++++++++++++---- Source/Core/Core/PatchEngine.cpp | 29 +++++++++++++++++---------- Source/Core/Core/PatchEngine.h | 2 +- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 68a975476b..2f73cd7c68 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -178,7 +179,8 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) PatchEngine::LoadPatches(); // If we have any patches that need to be applied very early, here's a good place - PatchEngine::ApplyFramePatches(); + bool patched = PatchEngine::ApplyFramePatches(); + _assert_(patched); return true; } diff --git a/Source/Core/Core/GeckoCode.cpp b/Source/Core/Core/GeckoCode.cpp index 1d4efa9b9b..c0185ff3af 100644 --- a/Source/Core/Core/GeckoCode.cpp +++ b/Source/Core/Core/GeckoCode.cpp @@ -157,25 +157,11 @@ static Installation InstallCodeHandlerLocked() return Installation::Installed; } -void RunCodeHandler(u32 msr_reg) +void RunCodeHandler() { if (!SConfig::GetInstance().bEnableCheats) return; - // Dolphin's hook mechanism is less 'precise' than Gecko OS' which detects particular - // instruction sequences (uses configuration, not automatic) that only run once per frame - // which includes a BLR as one of the instructions. It then overwrites the BLR with a - // "B 0x800018A8" to establish the hook. Dolphin uses its own internal VI interrupt which - // means the PC is non-deterministic and could be anywhere. - UReg_MSR msr = msr_reg; - if (!msr.DR || !msr.IR) - { - WARN_LOG(ACTIONREPLAY, "GeckoCode: Skipping frame update. MSR.IR/DR is currently disabled. " - "PC = 0x%08X, MSR = 0x%08X", - PC, msr_reg); - return; - } - std::lock_guard codes_lock(s_active_codes_lock); // Don't spam retry if the install failed. The corrupt / missing disk file is not likely to be // fixed within 1 frame of the last error. diff --git a/Source/Core/Core/GeckoCode.h b/Source/Core/Core/GeckoCode.h index 3e1cd2710a..4fa17c8c5e 100644 --- a/Source/Core/Core/GeckoCode.h +++ b/Source/Core/Core/GeckoCode.h @@ -52,6 +52,6 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4; constexpr u32 MAGIC_GAMEID = 0xD01F1BAD; void SetActiveCodes(const std::vector& gcodes); -void RunCodeHandler(u32 msr_reg); +void RunCodeHandler(); } // namespace Gecko diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp index 3e8b96381f..e2f8e3e9f4 100644 --- a/Source/Core/Core/HW/SystemTimers.cpp +++ b/Source/Core/Core/HW/SystemTimers.cpp @@ -166,11 +166,29 @@ s64 GetLocalTimeRTCOffset() return s_localtime_rtc_offset; } -static void PatchEngineCallback(u64 userdata, s64 cyclesLate) +static void PatchEngineCallback(u64 userdata, s64 cycles_late) { - // Patch mem and run the Action Replay - PatchEngine::ApplyFramePatches(); - CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField() - cyclesLate, et_PatchEngine); + // We have 2 periods, a 1000 cycle error period and the VI period. + // We have to carefully combine these together so that we stay on the VI period without drifting. + u32 vi_interval = VideoInterface::GetTicksPerField(); + s64 cycles_pruned = (userdata + cycles_late) % vi_interval; + s64 next_schedule = 0; + + // Try to patch mem and run the Action Replay + if (PatchEngine::ApplyFramePatches()) + { + next_schedule = vi_interval - cycles_pruned; + cycles_pruned = 0; + } + else + { + // The patch failed, usually because the CPU is in an inappropriate state (interrupt handler). + // We'll try again after 1000 cycles. + next_schedule = 1000; + cycles_pruned += next_schedule; + } + + CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned); } static void ThrottleCallback(u64 last_time, s64 cyclesLate) diff --git a/Source/Core/Core/PatchEngine.cpp b/Source/Core/Core/PatchEngine.cpp index 6ee403b9b7..e17a1d7720 100644 --- a/Source/Core/Core/PatchEngine.cpp +++ b/Source/Core/Core/PatchEngine.cpp @@ -203,22 +203,29 @@ static void ApplyPatches(const std::vector& patches) } } -void ApplyFramePatches() +bool ApplyFramePatches() { - // TODO: Messing with MSR this way is really, really, evil; we should - // probably be using some sort of Gecko OS-style hooking mechanism - // so the emulated CPU is in a predictable state when we process cheats. - u32 oldMSR = MSR; - UReg_MSR newMSR = oldMSR; - newMSR.IR = 1; - newMSR.DR = 1; - MSR = newMSR.Hex; + // Because we're using the VI Interrupt to time this instead of patching the game with a + // callback hook we can end up catching the game in an exception vector. + // We deal with this by returning false so that SystemTimers will reschedule us in a few cycles + // where we can try again after the CPU hopefully returns back to the normal instruction flow. + UReg_MSR msr = MSR; + if (!msr.DR || !msr.IR) + { + INFO_LOG( + ACTIONREPLAY, + "Need to retry later. CPU configuration is currently incorrect. PC = 0x%08X, MSR = 0x%08X", + PC, MSR); + return false; + } + ApplyPatches(onFrame); // Run the Gecko code handler - Gecko::RunCodeHandler(oldMSR); + Gecko::RunCodeHandler(); ActionReplay::RunAllActive(); - MSR = oldMSR; + + return true; } void Shutdown() diff --git a/Source/Core/Core/PatchEngine.h b/Source/Core/Core/PatchEngine.h index 61975a8d82..c7d3c9b838 100644 --- a/Source/Core/Core/PatchEngine.h +++ b/Source/Core/Core/PatchEngine.h @@ -43,7 +43,7 @@ int GetSpeedhackCycles(const u32 addr); void LoadPatchSection(const std::string& section, std::vector& patches, IniFile& globalIni, IniFile& localIni); void LoadPatches(); -void ApplyFramePatches(); +bool ApplyFramePatches(); void Shutdown(); inline int GetPatchTypeCharLength(PatchType type)