diff --git a/Core/Core.cpp b/Core/Core.cpp index bee40b41f8..4f43b73bd8 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -275,6 +275,7 @@ const char *CoreStateToString(CoreState state) { switch (state) { case CORE_RUNNING_CPU: return "RUNNING_CPU"; case CORE_NEXTFRAME: return "NEXTFRAME"; + case CORE_REENTER_DISPATCH: return "REENTER_DISPATCH"; case CORE_STEPPING_CPU: return "STEPPING_CPU"; case CORE_POWERDOWN: return "POWERDOWN"; case CORE_RUNTIME_ERROR: return "RUNTIME_ERROR"; @@ -727,7 +728,16 @@ bool Core_NextFrame() { _dbg_assert_(coreState != CORE_STEPPING_GE && coreState != CORE_RUNNING_GE); - if (coreState == CORE_RUNNING_CPU) { + if (coreState == CORE_RUNNING_CPU || coreState == CORE_REENTER_DISPATCH) { + // REENTER_DISPATCH is just a request to bounce out of the dispatcher and come back in, + // so that pending jit cache invalidations get applied. Switching to NEXTFRAME instead + // bounces out just as well, and MIPSState::RunLoopUntil() calls ProcessPendingInvalidates() + // on every entry regardless of state, so they're still applied when we resume. + // + // This happens when a CoreTiming event invalidates the icache (a cheat writing code, an + // async io notify, ...) and the vblank event follows it in the same ProcessEvents() batch: + // event callbacks run back to back without returning to the dispatcher in between, so the + // state is still REENTER_DISPATCH when __DisplayFlip gets here. ::coreState = CORE_NEXTFRAME; return true; } else if (coreState == CORE_STEPPING_CPU) {