PatchEngine: Handle MSR more cleanly

Instead of fiddling with the MSR value, just reschedule and try again
after the game fixes it itself.
This commit is contained in:
EmptyChaos 2016-09-24 15:14:51 +00:00
parent 83407263e5
commit c3cef54910
6 changed files with 46 additions and 33 deletions

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "Common/Assert.h"
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
@ -178,7 +179,8 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader)
PatchEngine::LoadPatches(); PatchEngine::LoadPatches();
// If we have any patches that need to be applied very early, here's a good place // 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; return true;
} }

View File

@ -157,25 +157,11 @@ static Installation InstallCodeHandlerLocked()
return Installation::Installed; return Installation::Installed;
} }
void RunCodeHandler(u32 msr_reg) void RunCodeHandler()
{ {
if (!SConfig::GetInstance().bEnableCheats) if (!SConfig::GetInstance().bEnableCheats)
return; 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<std::mutex> codes_lock(s_active_codes_lock); std::lock_guard<std::mutex> codes_lock(s_active_codes_lock);
// Don't spam retry if the install failed. The corrupt / missing disk file is not likely to be // 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. // fixed within 1 frame of the last error.

View File

@ -52,6 +52,6 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD; constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
void SetActiveCodes(const std::vector<GeckoCode>& gcodes); void SetActiveCodes(const std::vector<GeckoCode>& gcodes);
void RunCodeHandler(u32 msr_reg); void RunCodeHandler();
} // namespace Gecko } // namespace Gecko

View File

@ -166,11 +166,29 @@ s64 GetLocalTimeRTCOffset()
return s_localtime_rtc_offset; 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 // We have 2 periods, a 1000 cycle error period and the VI period.
PatchEngine::ApplyFramePatches(); // We have to carefully combine these together so that we stay on the VI period without drifting.
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField() - cyclesLate, et_PatchEngine); 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) static void ThrottleCallback(u64 last_time, s64 cyclesLate)

View File

@ -203,22 +203,29 @@ static void ApplyPatches(const std::vector<Patch>& patches)
} }
} }
void ApplyFramePatches() bool ApplyFramePatches()
{ {
// TODO: Messing with MSR this way is really, really, evil; we should // Because we're using the VI Interrupt to time this instead of patching the game with a
// probably be using some sort of Gecko OS-style hooking mechanism // callback hook we can end up catching the game in an exception vector.
// so the emulated CPU is in a predictable state when we process cheats. // We deal with this by returning false so that SystemTimers will reschedule us in a few cycles
u32 oldMSR = MSR; // where we can try again after the CPU hopefully returns back to the normal instruction flow.
UReg_MSR newMSR = oldMSR; UReg_MSR msr = MSR;
newMSR.IR = 1; if (!msr.DR || !msr.IR)
newMSR.DR = 1; {
MSR = newMSR.Hex; INFO_LOG(
ACTIONREPLAY,
"Need to retry later. CPU configuration is currently incorrect. PC = 0x%08X, MSR = 0x%08X",
PC, MSR);
return false;
}
ApplyPatches(onFrame); ApplyPatches(onFrame);
// Run the Gecko code handler // Run the Gecko code handler
Gecko::RunCodeHandler(oldMSR); Gecko::RunCodeHandler();
ActionReplay::RunAllActive(); ActionReplay::RunAllActive();
MSR = oldMSR;
return true;
} }
void Shutdown() void Shutdown()

View File

@ -43,7 +43,7 @@ int GetSpeedhackCycles(const u32 addr);
void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni, void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni,
IniFile& localIni); IniFile& localIni);
void LoadPatches(); void LoadPatches();
void ApplyFramePatches(); bool ApplyFramePatches();
void Shutdown(); void Shutdown();
inline int GetPatchTypeCharLength(PatchType type) inline int GetPatchTypeCharLength(PatchType type)