From de03d2f67286d6080c0f798f33b4fc5dca68e3cf Mon Sep 17 00:00:00 2001 From: Ty Date: Sat, 10 Jan 2026 15:39:18 -0500 Subject: [PATCH] Debugger: Properly reset the breakpoint skip first when skipped --- pcsx2/DebugTools/Breakpoints.cpp | 29 ++++++++++++++++++++++++++--- pcsx2/DebugTools/Breakpoints.h | 7 ++++--- pcsx2/Interpreter.cpp | 5 +++++ pcsx2/R3000AInterpreter.cpp | 5 +++++ pcsx2/x86/iR3000A.cpp | 23 +++++++++++++++-------- pcsx2/x86/ix86-32/iR5900.cpp | 19 ++++++++++++++----- 6 files changed, 69 insertions(+), 19 deletions(-) diff --git a/pcsx2/DebugTools/Breakpoints.cpp b/pcsx2/DebugTools/Breakpoints.cpp index 02f4e6005c..c6b7a5e627 100644 --- a/pcsx2/DebugTools/Breakpoints.cpp +++ b/pcsx2/DebugTools/Breakpoints.cpp @@ -11,6 +11,8 @@ std::vector CBreakPoints::breakPoints_; u32 CBreakPoints::breakSkipFirstAtEE_ = 0; u32 CBreakPoints::breakSkipFirstAtIop_ = 0; +bool CBreakPoints::pendingClearSkipFirstAtEE_ = false; +bool CBreakPoints::pendingClearSkipFirstAtIop_ = false; std::vector CBreakPoints::memChecks_; std::vector CBreakPoints::cleanupMemChecks_; bool CBreakPoints::breakpointTriggered_ = false; @@ -391,10 +393,12 @@ void CBreakPoints::SetSkipFirst(BreakPointCpu cpu, u32 pc) if (cpu == BREAKPOINT_EE) { breakSkipFirstAtEE_ = standardizeBreakpointAddress(pc); + pendingClearSkipFirstAtEE_ = false; } else if (cpu == BREAKPOINT_IOP) { breakSkipFirstAtIop_ = pc; + pendingClearSkipFirstAtIop_ = false; } } @@ -407,10 +411,29 @@ u32 CBreakPoints::CheckSkipFirst(BreakPointCpu cpu, u32 cmpPc) return 0; } -void CBreakPoints::ClearSkipFirst() +void CBreakPoints::ClearSkipFirst(BreakPointCpu cpu) { - breakSkipFirstAtEE_ = 0; - breakSkipFirstAtIop_ = 0; + if((cpu & BREAKPOINT_EE) != 0) + pendingClearSkipFirstAtEE_ = true; + else if ((cpu & BREAKPOINT_IOP) != 0) + pendingClearSkipFirstAtIop_ = true; + + if(cpu == BREAKPOINT_IOP_AND_EE) + CommitClearSkipFirst(BREAKPOINT_IOP_AND_EE); +} + +void CBreakPoints::CommitClearSkipFirst(BreakPointCpu cpu) +{ + if((cpu & BREAKPOINT_EE) != 0 && pendingClearSkipFirstAtEE_) + { + pendingClearSkipFirstAtEE_ = false; + breakSkipFirstAtEE_ = 0; + } + else if ((cpu & BREAKPOINT_IOP) != 0 && pendingClearSkipFirstAtIop_) + { + pendingClearSkipFirstAtIop_ = true; + breakSkipFirstAtIop_ = 0; + } } const std::vector CBreakPoints::GetMemCheckRanges() diff --git a/pcsx2/DebugTools/Breakpoints.h b/pcsx2/DebugTools/Breakpoints.h index b3acdc0e36..b65506c88f 100644 --- a/pcsx2/DebugTools/Breakpoints.h +++ b/pcsx2/DebugTools/Breakpoints.h @@ -135,7 +135,8 @@ public: static void SetSkipFirst(BreakPointCpu cpu, u32 pc); static u32 CheckSkipFirst(BreakPointCpu cpu, u32 pc); - static void ClearSkipFirst(); + static void ClearSkipFirst(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE); + static void CommitClearSkipFirst(BreakPointCpu cpu); // Includes uncached addresses. static const std::vector GetMemCheckRanges(); @@ -169,9 +170,9 @@ private: static std::vector breakPoints_; static u32 breakSkipFirstAtEE_; - static u64 breakSkipFirstTicksEE_; + static bool pendingClearSkipFirstAtEE_; static u32 breakSkipFirstAtIop_; - static u64 breakSkipFirstTicksIop_; + static bool pendingClearSkipFirstAtIop_; static bool breakpointTriggered_; static BreakPointCpu breakpointTriggeredCpu_; diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index 16f848a6be..f538cf7cf5 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -67,7 +67,10 @@ void intBreakpoint(bool memcheck) { const u32 pc = cpuRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_EE); return; + } if (!memcheck) { @@ -161,6 +164,8 @@ static void execI() intBreakpoint(false); intCheckMemcheck(); + + CBreakPoints::CommitClearSkipFirst(BREAKPOINT_EE); #endif const u32 pc = cpuRegs.pc; diff --git a/pcsx2/R3000AInterpreter.cpp b/pcsx2/R3000AInterpreter.cpp index 67cb011533..a17ad3c59b 100644 --- a/pcsx2/R3000AInterpreter.cpp +++ b/pcsx2/R3000AInterpreter.cpp @@ -120,7 +120,10 @@ void psxBreakpoint(bool memcheck) { u32 pc = psxRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) != 0) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP); return; + } if (!memcheck) { @@ -208,6 +211,8 @@ static __fi void execI() psxBreakpoint(false); psxCheckMemcheck(); + + CBreakPoints::CommitClearSkipFirst(BREAKPOINT_IOP); #endif // Inject IRX hack diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index afc5b87e4a..ea19f42fb5 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -1259,7 +1259,10 @@ static bool psxDynarecCheckBreakpoint() { u32 pc = psxRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP); return false; + } int bpFlags = psxIsBreakpointNeeded(pc); bool hit = false; @@ -1299,8 +1302,10 @@ static bool psxDynarecMemcheck(size_t i) auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_IOP)[i]; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP); return false; - + } if (mc.hasCond) { if (!mc.cond.Evaluate()) @@ -1373,7 +1378,7 @@ static void psxRecMemcheck(u32 op, u32 bits, bool store) } } -static void psxEncodeBreakpoint() +static bool psxEncodeBreakpoint() { if (psxIsBreakpointNeeded(psxpc) != 0) { @@ -1381,14 +1386,17 @@ static void psxEncodeBreakpoint() xFastCall((void*)psxDynarecCheckBreakpoint); xTEST(al, al); xJNZ(iopExitRecompiledCode); + return true; } + + return false; } -static void psxEncodeMemcheck() +static bool psxEncodeMemcheck() { int needed = psxIsMemcheckNeeded(psxpc); if (needed == 0) - return; + return false; u32 op = iopMemRead32(needed == 2 ? psxpc + 4 : psxpc); const R5900::OPCODE& opcode = R5900::GetInstruction(op); @@ -1409,6 +1417,7 @@ static void psxEncodeMemcheck() psxRecMemcheck(op, 64, store); break; } + return true; } void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot) @@ -1437,12 +1446,10 @@ void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot) EEINST* old_inst_info = g_pCurInstInfo; s_recompilingDelaySlot = delayslot; - // add breakpoint if (!delayslot) { - // Broken on x64 - psxEncodeBreakpoint(); - psxEncodeMemcheck(); + if(psxEncodeBreakpoint() || psxEncodeMemcheck()) + xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_IOP); } else { diff --git a/pcsx2/x86/ix86-32/iR5900.cpp b/pcsx2/x86/ix86-32/iR5900.cpp index 82b71dc4d6..06f957fdda 100644 --- a/pcsx2/x86/ix86-32/iR5900.cpp +++ b/pcsx2/x86/ix86-32/iR5900.cpp @@ -1498,7 +1498,10 @@ void dynarecCheckBreakpoint() { u32 pc = cpuRegs.pc; if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_EE); return; + } const int bpFlags = isBreakpointNeeded(pc); bool hit = false; @@ -1532,7 +1535,10 @@ void dynarecMemcheck(size_t i) const u32 op = memRead32(cpuRegs.pc); const OPCODE& opcode = GetInstruction(op); if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0) + { + CBreakPoints::ClearSkipFirst(BREAKPOINT_EE); return; + } auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_EE)[i]; @@ -1606,20 +1612,22 @@ void recMemcheck(u32 op, u32 bits, bool store) } } -void encodeBreakpoint() +bool encodeBreakpoint() { if (isBreakpointNeeded(pc) != 0) { iFlushCall(FLUSH_EVERYTHING | FLUSH_PC); xFastCall((void*)dynarecCheckBreakpoint); + return true; } + return false; } -void encodeMemcheck() +bool encodeMemcheck() { const int needed = isMemcheckNeeded(pc); if (needed == 0) - return; + return false; const u32 op = memRead32(needed == 2 ? pc + 4 : pc); const OPCODE& opcode = GetInstruction(op); @@ -1643,6 +1651,7 @@ void encodeMemcheck() recMemcheck(op, 128, store); break; } + return true; } void recompileNextInstruction(bool delayslot, bool swapped_delay_slot) @@ -1653,8 +1662,8 @@ void recompileNextInstruction(bool delayslot, bool swapped_delay_slot) // add breakpoint if (!delayslot) { - encodeBreakpoint(); - encodeMemcheck(); + if(encodeBreakpoint() || encodeMemcheck()) + xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_EE); } else {