mirror of https://github.com/PCSX2/pcsx2.git
R3000A/R5900: Refactor interpreter/recompiler exits
Now, IOP breakpoints work nice and reliably in both interpreter and recompiler, exiting as soon as possible, without leaving the event state indeterminate.
This commit is contained in:
parent
25e15a16b1
commit
3801825793
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue