From e906b9f7002c7ce7205a1f68733e71f9ccfa33f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Oct 2026 23:59:14 +0200 Subject: [PATCH] CoreTiming: Catch the ME up when the main CPU reads the boot page The ME runs a slice behind the main CPU, so a main CPU polling a mailbox in the boot page saw the ME's progress up to a slice late. Reading the boot page now runs the ME up to where the main CPU is in its slice first, and the end of the slice runs only the rest. me/audiofifo, which times the ME's playing from the main CPU, read 43900Hz for 44100. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/CoreTiming.cpp | 16 +++++++++++++++- Core/CoreTiming.h | 3 +++ Core/HW/MMIO.cpp | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 8b06f48617..b64b877c84 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -58,6 +58,8 @@ static Event *eventPool = 0; // Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block // as we can already reach that structure through a register. int slicelength; +// How much of the current slice the other CPUs have already run (CatchUpOtherCPUs). +static int otherCPUsRan = 0; alignas(16) static s64 globalTimer; static s64 idledCycles; @@ -218,6 +220,7 @@ void UnregisterAllEvents() { void Init(MIPSState *mips) { mips->downcount = INITIAL_SLICE_LENGTH; slicelength = INITIAL_SLICE_LENGTH; + otherCPUsRan = 0; globalTimer = 0; idledCycles = 0; lastGlobalTimeTicks = 0; @@ -417,7 +420,16 @@ void ProcessEvents() { // The Media Engine catches up with the main CPU. static void RunOtherCPUs(int cycles) { if (MeCore::g_meState) { - MeCore::RunFor(cycles); + MeCore::RunFor(cycles - otherCPUsRan); + } + otherCPUsRan = 0; +} + +void CatchUpOtherCPUs(MIPSState *mips) { + const int ran = slicelength - mips->downcount; + if (MeCore::g_meState && ran > otherCPUsRan) { + MeCore::RunFor(ran - otherCPUsRan); + otherCPUsRan = ran; } } @@ -553,6 +565,8 @@ void DoState(PointerWrap &p) { auto s = p.Section("CoreTiming", 1, 3); if (!s) return; + // States are saved between slices. + otherCPUsRan = 0; int n = (int)event_types.size(); int current = n; diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index bc52e6cc38..0a4cf2870c 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -127,6 +127,9 @@ namespace CoreTiming { bool IsScheduled(int event_type); void Advance(MIPSState *mips); void ForceCheck(MIPSState *mips); + // Runs the other CPUs (the Media Engine) up to where this one is in the slice, as they otherwise + // run at its end: for when this one looks at what they share, such as the boot page. + void CatchUpOtherCPUs(MIPSState *mips); // Pretend that the main CPU has executed enough cycles to reach the next event. void Idle(MIPSState *mips, int maxIdle = 0); diff --git a/Core/HW/MMIO.cpp b/Core/HW/MMIO.cpp index 0aeb93e564..5e3dfa451d 100644 --- a/Core/HW/MMIO.cpp +++ b/Core/HW/MMIO.cpp @@ -21,6 +21,7 @@ #include "Common/Log.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" +#include "Core/CoreTiming.h" #include "Core/HW/GpioMMIO.h" #include "Core/HW/MMIO.h" #include "Core/HW/MeMMIO.h" @@ -37,6 +38,9 @@ public: bool Read(MIPSState *mips, u32 offset, int size, u32 *value) override { if (offset + size > Memory::BOOT_PAGE_SIZE) return false; + // What the ME has written by now: a main CPU polling a mailbox here sees it as on a PSP. + if (mips == currentMIPS) + CoreTiming::CatchUpOtherCPUs(mips); const u8 *ptr = Memory::GetBootPage() + offset; switch (size) { case 1: *value = *ptr; break;