diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 1c2a0d6251..366166e05a 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -488,10 +488,13 @@ static __fi void frameLimitUpdateCore() #ifndef PCSX2_CORE GetCoreThread().VsyncInThread(); + if (GetCoreThread().HasPendingStateChangeRequest()) + Cpu->ExitExecution(); #else VMManager::Internal::VSyncOnCPUThread(); + if (VMManager::Internal::IsExecutionInterrupted()) + Cpu->ExitExecution(); #endif - Cpu->CheckExecutionState(); } // Framelimiter - Measures the delta time between calls and stalls until a diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 540f45f987..f616808d24 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -581,23 +581,14 @@ static void intExecute() } while (instruction_was_cancelled); } -static void intCheckExecutionState() +static void intSafeExitExecution() { -#ifndef PCSX2_CORE - const bool interrupted = GetCoreThread().HasPendingStateChangeRequest(); -#else - const bool interrupted = VMManager::Internal::IsExecutionInterrupted(); -#endif - - if (interrupted) - { - // If we're currently processing events, we can't safely jump out of the interpreter here, because we'll - // leave things in an inconsistent state. So instead, we flag it for exiting once cpuEventTest() returns. - if (eeEventTestIsActive) - intExitExecution = true; - else - throw Exception::ExitCpuExecute(); - } + // If we're currently processing events, we can't safely jump out of the interpreter here, because we'll + // leave things in an inconsistent state. So instead, we flag it for exiting once cpuEventTest() returns. + if (eeEventTestIsActive) + intExitExecution = true; + else + throw Exception::ExitCpuExecute(); } static void intStep() @@ -642,7 +633,7 @@ R5900cpu intCpu = intStep, intExecute, - intCheckExecutionState, + intSafeExitExecution, intThrowException, intThrowException, intClear, diff --git a/pcsx2/R3000A.cpp b/pcsx2/R3000A.cpp index 8596ca7761..614f905ee8 100644 --- a/pcsx2/R3000A.cpp +++ b/pcsx2/R3000A.cpp @@ -43,8 +43,6 @@ s32 iopBreak = 0; // control is returned to the EE. s32 iopCycleEE = -1; -bool iopBreakpoint = 0; - // Used to signal to the EE when important actions that need IOP-attention have // happened (hsyncs, vsyncs, IOP exceptions, etc). IOP runs code whenever this // is true, even if it's already running ahead a bit. diff --git a/pcsx2/R3000A.h b/pcsx2/R3000A.h index 9204301a5d..a7dc2a12ce 100644 --- a/pcsx2/R3000A.h +++ b/pcsx2/R3000A.h @@ -120,7 +120,6 @@ alignas(16) extern psxRegisters psxRegs; extern u32 g_iopNextEventCycle; extern s32 iopBreak; // used when the IOP execution is broken and control returned to the EE extern s32 iopCycleEE; // tracks IOP's current sych status with the EE -extern bool iopBreakpoint; #ifndef _PC_ @@ -186,7 +185,6 @@ extern bool iopIsDelaySlot; struct R3000Acpu { void (*Reserve)(); void (*Reset)(); - void (*Execute)(); s32 (*ExecuteBlock)( s32 eeCycles ); // executes the given number of EE cycles. void (*Clear)(u32 Addr, u32 Size); void (*Shutdown)(); diff --git a/pcsx2/R3000AInterpreter.cpp b/pcsx2/R3000AInterpreter.cpp index 28f9529025..f4ada5305b 100644 --- a/pcsx2/R3000AInterpreter.cpp +++ b/pcsx2/R3000AInterpreter.cpp @@ -270,24 +270,29 @@ static void intReset() { intAlloc(); } -static void intExecute() { - for (;;) execI(); -} - static s32 intExecuteBlock( s32 eeCycles ) { iopBreak = 0; iopCycleEE = eeCycles; - while (iopCycleEE > 0){ - if ((psxHu32(HW_ICFG) & 8) && ((psxRegs.pc & 0x1fffffffU) == 0xa0 || (psxRegs.pc & 0x1fffffffU) == 0xb0 || (psxRegs.pc & 0x1fffffffU) == 0xc0)) - psxBiosCall(); + try + { + while (iopCycleEE > 0) { + if ((psxHu32(HW_ICFG) & 8) && ((psxRegs.pc & 0x1fffffffU) == 0xa0 || (psxRegs.pc & 0x1fffffffU) == 0xb0 || (psxRegs.pc & 0x1fffffffU) == 0xc0)) + psxBiosCall(); - branch2 = 0; - while (!branch2) { - execI(); - } + branch2 = 0; + while (!branch2) { + execI(); + } + } } + catch (Exception::ExitCpuExecute&) + { + // Get out of the EE too, regardless of whether it's int or rec. + Cpu->ExitExecution(); + } + return iopBreak + iopCycleEE; } @@ -309,7 +314,6 @@ static uint intGetCacheReserve() R3000Acpu psxInt = { intReserve, intReset, - intExecute, intExecuteBlock, intClear, intShutdown, diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 61cf8ba66b..ea864f053a 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -554,16 +554,18 @@ void __fastcall eeGameStarting() //Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry ); g_GameStarted = true; g_GameLoading = false; -#ifndef PCSX2_CORE - GetCoreThread().GameStartingInThread(); -#else - VMManager::Internal::GameStartingOnCPUThread(); -#endif - // GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and // handle such things here: - Cpu->CheckExecutionState(); +#ifndef PCSX2_CORE + GetCoreThread().GameStartingInThread(); + if (GetCoreThread().HasPendingStateChangeRequest()) + Cpu->ExitExecution(); +#else + VMManager::Internal::GameStartingOnCPUThread(); + if (VMManager::Internal::IsExecutionInterrupted()) + Cpu->ExitExecution(); +#endif } else { diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h index 8e7e1f464c..508d8d58fe 100644 --- a/pcsx2/R5900.h +++ b/pcsx2/R5900.h @@ -347,22 +347,10 @@ struct R5900cpu // void (*Execute)(); - // Checks for execution suspension or cancellation. In pthreads terms this provides - // a "cancellation point." Execution state checks are typically performed at Vsyncs - // by the generic VM event handlers in R5900.cpp/Counters.cpp (applies to both recs - // and ints). - // - // Implementation note: Because of the nuances of recompiled code execution, setjmp - // may be used in place of thread cancellation or C++ exceptions (non-SEH exceptions - // cannot unwind through the recompiled code stackframes, thus longjmp must be used). - // - // Thread Affinity: - // Must be called on the same thread as Execute. - // - // Exception Throws: - // May throw Execution/Pthreads cancellations if the compiler supports SEH. - // - void (*CheckExecutionState)(); + // Immediately exits execution of recompiled code if we are in a state to do so, or + // queues an exit as soon as it is safe. Safe in this case refers to whether we are + // currently executing events or not. + void (*ExitExecution)(); // Safely throws host exceptions from executing code (either recompiled or interpreted). // If this function is called outside the context of the CPU's code execution, then the diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index fc67b3a35f..d0601e309a 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -816,12 +816,6 @@ static void iopClearRecLUT(BASEBLOCK* base, int count) base[i].SetFnptr((uptr)iopJITCompile); } -static void recExecute() -{ - // note: this function is currently never used. - //for (;;) R3000AExecute(); -} - static __noinline s32 recExecuteBlock(s32 eeCycles) { iopBreak = 0; @@ -1094,11 +1088,11 @@ void rpsxBREAK() //if (!psxbranch) psxbranch = 2; } -void psxDynarecCheckBreakpoint() +static bool psxDynarecCheckBreakpoint() { u32 pc = psxRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc) - return; + return false; int bpFlags = psxIsBreakpointNeeded(pc); bool hit = false; @@ -1120,29 +1114,35 @@ void psxDynarecCheckBreakpoint() } if (!hit) - return; + return false; CBreakPoints::SetBreakpointTriggered(true); #ifndef PCSX2_CORE GetCoreThread().PauseSelfDebug(); #endif - iopBreakpoint = true; + + // Exit the EE too. + Cpu->ExitExecution(); + return true; } -void psxDynarecMemcheck() +static bool psxDynarecMemcheck() { u32 pc = psxRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc) - return; + return false; CBreakPoints::SetBreakpointTriggered(true); #ifndef PCSX2_CORE GetCoreThread().PauseSelfDebug(); #endif - iopBreakpoint = true; + + // Exit the EE too. + Cpu->ExitExecution(); + return true; } -void __fastcall psxDynarecMemLogcheck(u32 start, bool store) +static void psxDynarecMemLogcheck(u32 start, bool store) { if (store) DevCon.WriteLn("Hit store breakpoint @0x%x", start); @@ -1150,7 +1150,7 @@ void __fastcall psxDynarecMemLogcheck(u32 start, bool store) DevCon.WriteLn("Hit load breakpoint @0x%x", start); } -void psxRecMemcheck(u32 op, u32 bits, bool store) +static void psxRecMemcheck(u32 op, u32 bits, bool store) { _psxFlushCall(FLUSH_EVERYTHING | FLUSH_PC); @@ -1196,29 +1196,27 @@ void psxRecMemcheck(u32 op, u32 bits, bool store) if (checks[i].result & MEMCHECK_BREAK) { xFastCall((void*)psxDynarecMemcheck); + xTEST(al, al); + xJNZ(iopExitRecompiledCode); } next1.SetTarget(); next2.SetTarget(); } - // get out of here - xCMP(ptr8[&iopBreakpoint], 0); - xJNE(iopExitRecompiledCode); } -void psxEncodeBreakpoint() +static void psxEncodeBreakpoint() { if (psxIsBreakpointNeeded(psxpc) != 0) { _psxFlushCall(FLUSH_EVERYTHING | FLUSH_PC); xFastCall((void*)psxDynarecCheckBreakpoint); - // get out of here - xCMP(ptr8[&iopBreakpoint], 0); - xJNE(iopExitRecompiledCode); + xTEST(al, al); + xJNZ(iopExitRecompiledCode); } } -void psxEncodeMemcheck() +static void psxEncodeMemcheck() { int needed = psxIsMemcheckNeeded(psxpc); if (needed == 0) @@ -1559,7 +1557,6 @@ static uint recGetCacheReserve() R3000Acpu psxRec = { recReserve, recResetIOP, - recExecute, recExecuteBlock, recClearIOP, recShutdown, diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index d42d8ef3b2..01fa5bbae6 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -686,7 +686,9 @@ static void recResetEE() { if (eeCpuExecuting) { + // get outta here as soon as we can eeRecNeedsReset = true; + eeRecExitRequested = true; return; } @@ -711,21 +713,14 @@ static void recExitExecution() fastjmp_jmp(&m_SetJmp_StateCheck, 1); } -static void recCheckExecutionState() +static void recSafeExitExecution() { -#ifndef PCSX2_CORE - if (m_cpuException || m_Exception || eeRecNeedsReset || iopBreakpoint || GetCoreThread().HasPendingStateChangeRequest()) -#else - if (m_cpuException || m_Exception || eeRecNeedsReset || iopBreakpoint || VMManager::Internal::IsExecutionInterrupted()) -#endif - { - // If we're currently processing events, we can't safely jump out of the recompiler here, because we'll - // leave things in an inconsistent state. So instead, we flag it for exiting once cpuEventTest() returns. - if (eeEventTestIsActive) - eeRecExitRequested = true; - else - recExitExecution(); - } + // If we're currently processing events, we can't safely jump out of the recompiler here, because we'll + // leave things in an inconsistent state. So instead, we flag it for exiting once cpuEventTest() returns. + if (eeEventTestIsActive) + eeRecExitRequested = true; + else + recExitExecution(); } static void recExecute() @@ -2399,7 +2394,7 @@ R5900cpu recCpu = recStep, recExecute, - recCheckExecutionState, + recSafeExitExecution, recThrowException, recThrowException, recClear,