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:
parent
83407263e5
commit
c3cef54910
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<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
|
||||
// fixed within 1 frame of the last error.
|
||||
|
|
|
@ -52,6 +52,6 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
|
|||
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
||||
|
||||
void SetActiveCodes(const std::vector<GeckoCode>& gcodes);
|
||||
void RunCodeHandler(u32 msr_reg);
|
||||
void RunCodeHandler();
|
||||
|
||||
} // namespace Gecko
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
// 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()
|
||||
|
|
|
@ -43,7 +43,7 @@ int GetSpeedhackCycles(const u32 addr);
|
|||
void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni,
|
||||
IniFile& localIni);
|
||||
void LoadPatches();
|
||||
void ApplyFramePatches();
|
||||
bool ApplyFramePatches();
|
||||
void Shutdown();
|
||||
|
||||
inline int GetPatchTypeCharLength(PatchType type)
|
||||
|
|
Loading…
Reference in New Issue