diff --git a/Core/Core.cpp b/Core/Core.cpp index 5bf6793764..5e32d29af4 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -87,7 +87,6 @@ void Core_NotifyLifecycle(CoreLifecycle stage) { void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); - m_StepCond.notify_all(); } bool Core_IsStepping() { @@ -102,6 +101,14 @@ bool Core_IsInactive() { return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending; } +static inline void Core_StateProcessed() { + if (coreStatePending) { + std::lock_guard guard(m_hInactiveMutex); + coreStatePending = false; + m_InactiveCond.notify_all(); + } +} + void Core_WaitInactive() { while (Core_IsActive()) { std::unique_lock guard(m_hInactiveMutex); @@ -173,6 +180,7 @@ bool UpdateScreenScale(int width, int height) { return false; } +// Note: not used on Android. void UpdateRunLoop() { if (windowHidden && g_Config.bPauseWhenMinimized) { sleep_ms(16); @@ -191,6 +199,8 @@ void KeepScreenAwake() { void Core_RunLoop(GraphicsContext *ctx) { graphicsContext = ctx; while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) { + // In case it was pending, we're not in game anymore. We won't get to Core_Run(). + Core_StateProcessed(); time_update(); double startTime = time_now_d(); UpdateRunLoop(); @@ -227,22 +237,55 @@ void Core_RunLoop(GraphicsContext *ctx) { } void Core_DoSingleStep() { + std::lock_guard guard(m_hStepMutex); singleStepPending = true; m_StepCond.notify_all(); } void Core_UpdateSingleStep() { + std::lock_guard guard(m_hStepMutex); m_StepCond.notify_all(); } void Core_SingleStep() { currentMIPS->SingleStep(); + if (coreState == CORE_STEPPING) + steppingCounter++; } -static inline void CoreStateProcessed() { - if (coreStatePending) { - coreStatePending = false; - m_InactiveCond.notify_all(); +static inline bool Core_WaitStepping() { + std::unique_lock guard(m_hStepMutex); + if (!singleStepPending && coreState == CORE_STEPPING) + m_StepCond.wait(guard); + + bool result = singleStepPending; + singleStepPending = false; + return result; +} + +void Core_ProcessStepping() { + Core_StateProcessed(); + + // Check if there's any pending save state actions. + SaveState::Process(); + if (coreState != CORE_STEPPING) { + return; + } + + // We're not inside jit now, so it's safe to clear the breakpoints. + CBreakPoints::ClearTemporaryBreakPoints(); + host->UpdateDisassembly(); + host->UpdateMemView(); + + // Need to check inside the lock to avoid races. + bool doStep = Core_WaitStepping(); + + // We may still be stepping without singleStepPending to process a save state. + if (doStep && coreState == CORE_STEPPING) { + Core_SingleStep(); + // Update disasm dialog. + host->UpdateDisassembly(); + host->UpdateMemView(); } } @@ -251,9 +294,8 @@ static inline void CoreStateProcessed() { void Core_Run(GraphicsContext *ctx) { host->UpdateDisassembly(); while (true) { -reswitch: if (GetUIState() != UISTATE_INGAME) { - CoreStateProcessed(); + Core_StateProcessed(); if (GetUIState() == UISTATE_EXIT) { UpdateRunLoop(); return; @@ -264,51 +306,20 @@ reswitch: switch (coreState) { case CORE_RUNNING: + case CORE_STEPPING: // enter a fast runloop Core_RunLoop(ctx); - break; - - // We should never get here on Android. - case CORE_STEPPING: - singleStepPending = false; - CoreStateProcessed(); - - // Check if there's any pending savestate actions. - SaveState::Process(); if (coreState == CORE_POWERDOWN) { + Core_StateProcessed(); return; } - - // wait for step command.. - host->UpdateDisassembly(); - host->UpdateMemView(); - host->SendCoreWait(true); - - { - std::unique_lock guard(m_hStepMutex); - m_StepCond.wait(guard); - } - - host->SendCoreWait(false); - // No step pending? Let's go back to the wait. - if (!singleStepPending || coreState != CORE_STEPPING) { - if (coreState == CORE_POWERDOWN) { - return; - } - goto reswitch; - } - - Core_SingleStep(); - // update disasm dialog - host->UpdateDisassembly(); - host->UpdateMemView(); break; case CORE_POWERUP: case CORE_POWERDOWN: case CORE_ERROR: // Exit loop!! - CoreStateProcessed(); + Core_StateProcessed(); return; @@ -320,7 +331,6 @@ reswitch: void Core_EnableStepping(bool step) { if (step) { - sleep_ms(1); host->SetDebugMode(true); Core_UpdateState(CORE_STEPPING); steppingCounter++; diff --git a/Core/Core.h b/Core/Core.h index f01a9d03e8..84fe707792 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -34,6 +34,7 @@ void Core_SetGraphicsContext(GraphicsContext *ctx); void Core_EnableStepping(bool step); void Core_DoSingleStep(); void Core_UpdateSingleStep(); +void Core_ProcessStepping(); // Changes every time we enter stepping. int Core_GetSteppingCounter(); @@ -54,6 +55,7 @@ bool Core_IsStepping(); bool Core_IsActive(); bool Core_IsInactive(); +// Warning: these currently work only on Windows. void Core_WaitInactive(); void Core_WaitInactive(int milliseconds); diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 9f7a27d39f..0223a2f6fc 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -33,11 +33,6 @@ u64 CBreakPoints::breakSkipFirstTicks_ = 0; std::vector CBreakPoints::memChecks_; std::vector CBreakPoints::cleanupMemChecks_; -MemCheck::MemCheck() -{ - numHits = 0; -} - void MemCheck::Log(u32 addr, bool write, int size, u32 pc) { if (result & BREAK_ACTION_LOG) { if (logFormat.empty()) { @@ -493,17 +488,20 @@ u32 CBreakPoints::CheckSkipFirst() return 0; } -const std::vector CBreakPoints::GetMemCheckRanges() -{ +const std::vector CBreakPoints::GetMemCheckRanges(bool write) { std::vector ranges = memChecks_; - for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it) - { - MemCheck check = *it; + for (const auto &check : memChecks_) { + if (!(check.cond & MEMCHECK_READ) && !write) + continue; + if (!(check.cond & MEMCHECK_WRITE) && write) + continue; + + MemCheck copy = check; // Toggle the cached part of the address. - check.start ^= 0x40000000; - if (check.end != 0) - check.end ^= 0x40000000; - ranges.push_back(check); + copy.start ^= 0x40000000; + if (copy.end != 0) + copy.end ^= 0x40000000; + ranges.push_back(copy); } return ranges; diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 934667914a..8cd7acdc4d 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -21,8 +21,7 @@ #include "Core/Debugger/DebugInterface.h" -enum BreakAction -{ +enum BreakAction { BREAK_ACTION_IGNORE = 0x00, BREAK_ACTION_LOG = 0x01, BREAK_ACTION_PAUSE = 0x02, @@ -37,35 +36,27 @@ static inline BreakAction operator | (const BreakAction &lhs, const BreakAction return BreakAction((u32)lhs | (u32)rhs); } -struct BreakPointCond -{ - DebugInterface *debug; +struct BreakPointCond { + DebugInterface *debug = nullptr; PostfixExpression expression; std::string expressionString; - BreakPointCond() : debug(nullptr) - { - } - - u32 Evaluate() - { + u32 Evaluate() { u32 result; - if (debug->parseExpression(expression,result) == false) return 0; + if (debug->parseExpression(expression, result) == false) + return 0; return result; } }; -struct BreakPoint -{ - BreakPoint() : hasCond(false) {} - +struct BreakPoint { u32 addr; bool temporary; - BreakAction result; + BreakAction result = BREAK_ACTION_IGNORE; std::string logFormat; - bool hasCond; + bool hasCond = false; BreakPointCond cond; bool IsEnabled() const { @@ -80,8 +71,7 @@ struct BreakPoint } }; -enum MemCheckCondition -{ +enum MemCheckCondition { MEMCHECK_READ = 0x01, MEMCHECK_WRITE = 0x02, MEMCHECK_WRITE_ONCHANGE = 0x04, @@ -89,21 +79,19 @@ enum MemCheckCondition MEMCHECK_READWRITE = 0x03, }; -struct MemCheck -{ - MemCheck(); +struct MemCheck { u32 start; u32 end; - MemCheckCondition cond; - BreakAction result; + MemCheckCondition cond = MEMCHECK_READ; + BreakAction result = BREAK_ACTION_IGNORE; std::string logFormat; - u32 numHits; + u32 numHits = 0; - u32 lastPC; - u32 lastAddr; - int lastSize; + u32 lastPC = 0; + u32 lastAddr = 0; + int lastSize = 0; BreakAction Action(u32 addr, bool write, int size, u32 pc); void JitBefore(u32 addr, bool write, int size, u32 pc); @@ -168,7 +156,7 @@ public: static u32 CheckSkipFirst(); // Includes uncached addresses. - static const std::vector GetMemCheckRanges(); + static const std::vector GetMemCheckRanges(bool write); static const std::vector GetMemChecks(); static const std::vector GetBreakpoints(); diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 8dd52b2dcd..dec3f24636 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -152,10 +152,10 @@ static int Replace_memcpy() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -194,10 +194,10 @@ static int Replace_memcpy_jak() { currentMIPS->r[MIPS_REG_A2] = 0; currentMIPS->r[MIPS_REG_A3] = destPtr + bytes; RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop.. } @@ -222,10 +222,10 @@ static int Replace_memcpy16() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -260,10 +260,10 @@ static int Replace_memcpy_swizzled() { } RETURN(0); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, pitch * h, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, pitch * h, currentMIPS->pc); -#endif + return 10 + (pitch * h) / 4; // approximation } @@ -288,10 +288,10 @@ static int Replace_memmove() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -310,9 +310,9 @@ static int Replace_memset() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -342,9 +342,8 @@ static int Replace_memset_jak() { currentMIPS->r[MIPS_REG_A3] = -1; RETURN(destPtr); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..) } @@ -591,11 +590,9 @@ static int Replace_dl_write_matrix() { #endif } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(PARAM(2), false, count * sizeof(float), currentMIPS->pc); CBreakPoints::ExecMemCheck(PARAM(0) + 2 * sizeof(u32), true, sizeof(u32), currentMIPS->pc); CBreakPoints::ExecMemCheck(dlStruct[2], true, (count + 1) * sizeof(u32), currentMIPS->pc); -#endif dlStruct[2] += (1 + count) * 4; RETURN(dlStruct[2]); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index ba9d7c1588..29c0cc5c7f 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -654,10 +654,10 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) *dstp++ = *srcp++; } } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc); CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc); -#endif + return dst; } diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index cf82e94e1c..1da8800240 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -766,9 +766,8 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo delete [] imgbuf; } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc); -#endif + return videoImageSize; #endif // USE_FFMPEG return 0; @@ -822,9 +821,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineRGBA(imgbuf, data, width); data += m_desWidth * sizeof(u32); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc); -#endif } break; @@ -834,9 +831,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5650(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -846,9 +841,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5551(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -858,9 +851,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR4444(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -954,9 +945,8 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { if (!m_audioContext->Decode(audioFrame, frameSize, buffer, &outbytes)) { ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType)); } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc); -#endif } return 0x2000; diff --git a/Core/Host.h b/Core/Host.h index c7eed94de1..b06473c4dc 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -50,8 +50,6 @@ public: virtual void SaveSymbolMap() {} virtual void SetWindowTitle(const char *message) {} - virtual void SendCoreWait(bool) {} - // While debugging is active, it's perfectly fine for these to block. virtual bool GPUDebuggingActive() { return false; } virtual void GPUNotifyCommand(u32 pc) {} diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 7e037a7052..3a5bcba1a5 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -238,7 +238,6 @@ void ArmJit::GenerateFixedCode() { B_CC(CC_EQ, outerLoop); SetJumpTarget(badCoreState); - breakpointBailout = GetCodePtr(); SaveDowncount(); RestoreRoundingMode(true); diff --git a/Core/MIPS/ARM/ArmCompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp index 971f8cebdc..f4ec293382 100644 --- a/Core/MIPS/ARM/ArmCompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -93,6 +93,7 @@ extern int logBlocks; void ArmJit::Comp_FPULS(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (s16)(op & 0xFFFF); int ft = _FT; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 2c0baac10b..16a3753fd2 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -112,6 +112,7 @@ namespace MIPSComp void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -120,6 +121,7 @@ namespace MIPSComp if (!js.inDelaySlot) { // Optimisation: Combine to single unaligned load/store bool isLeft = (o == 34 || o == 42); + CheckMemoryBreakpoint(1); MIPSOpcode nextOp = GetOffsetInstruction(1); // Find a matching shift in opposite direction with opposite offset. if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3) @@ -259,6 +261,7 @@ namespace MIPSComp void ArmJit::Comp_ITypeMem(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op&0xFFFF); bool load = false; MIPSGPReg rt = _RT; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 3f275c58be..c171424075 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -226,6 +226,7 @@ namespace MIPSComp void ArmJit::Comp_SV(MIPSOpcode op) { NEON_IF_AVAILABLE(CompNEON_SV); CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (signed short)(op & 0xFFFC); int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); @@ -332,6 +333,7 @@ namespace MIPSComp { NEON_IF_AVAILABLE(CompNEON_SVQ); CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int imm = (signed short)(op&0xFFFC); int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); diff --git a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp index fe49e2e509..9f04436bf4 100644 --- a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp +++ b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp @@ -159,6 +159,7 @@ void ArmJit::CompNEON_VecDo3(MIPSOpcode op) { void ArmJit::CompNEON_SV(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); // Remember to use single lane stores here and not VLDR/VSTR - switching usage // between NEON and VFPU can be expensive on some chips. @@ -276,6 +277,7 @@ inline int MIPS_GET_VQVT(u32 op) { void ArmJit::CompNEON_SVQ(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFC); int vt = MIPS_GET_VQVT(op.encoding); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index ea206a6d26..87bb801087 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -26,6 +26,7 @@ #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" @@ -45,7 +46,7 @@ using namespace ArmJitConstants; -void DisassembleArm(const u8 *data, int size) { +static void DisassembleArm(const u8 *data, int size) { char temp[256]; for (int i = 0; i < size; i += 4) { const u32 *codePtr = (const u32 *)(data + i); @@ -69,6 +70,33 @@ void DisassembleArm(const u8 *data, int size) { } } +static u32 JitBreakpoint() { + // Should we skip this breakpoint? + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc); + if ((result & BREAK_ACTION_PAUSE) == 0) + return 0; + + return 1; +} + +static u32 JitMemCheck(u32 pc) { + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + // Note: pc may be the delay slot. + const auto op = Memory::Read_Instruction(pc, true); + s32 offset = (s16)(op & 0xFFFF); + if (MIPSGetInfo(op) & IS_VFPU) + offset &= 0xFFFC; + u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset; + + CBreakPoints::ExecOpMemCheck(addr, pc); + return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1; +} + namespace MIPSComp { using namespace ArmGen; @@ -86,6 +114,10 @@ ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(m INFO_LOG(JIT, "ARM JIT initialized: %d MB of code space", GetSpaceLeft() / (1024 * 1024)); js.startDefaultPrefix = mips_->HasDefaultPrefix(); + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } ArmJit::~ArmJit() { @@ -104,6 +136,10 @@ void ArmJit::DoState(PointerWrap &p) } else { js.hasSetRounding = 1; } + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } void ArmJit::UpdateFCR31() { @@ -158,13 +194,16 @@ void ArmJit::EatInstruction(MIPSOpcode op) { ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot."); } + CheckJitBreakpoint(GetCompilerPC() + 4, 0); js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); } -void ArmJit::CompileDelaySlot(int flags) -{ +void ArmJit::CompileDelaySlot(int flags) { + // Need to offset the downcount which was already incremented for the branch + delay slot. + CheckJitBreakpoint(GetCompilerPC() + 4, -2); + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. @@ -295,6 +334,9 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b) while (js.compiling) { gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages + // Jit breakpoints are quite fast, so let's do them in release too. + CheckJitBreakpoint(GetCompilerPC(), 0); + MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC()); //MIPSInfo info = MIPSGetInfo(inst); //if (info & IS_VFPU) { @@ -492,7 +534,18 @@ void ArmJit::Comp_ReplacementFunc(MIPSOpcode op) return; } - if (entry->flags & REPFLAG_DISABLED) { + u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC()); + bool disabled = (entry->flags & REPFLAG_DISABLED) != 0; + if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) { + // We don't need to disable hooks, the code will still run. + if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) { + // Any breakpoint at the func entry was already tripped, so we can still run the replacement. + // That's a common case - just to see how often the replacement hits. + disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32)); + } + } + + if (disabled) { MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this); } else if (entry->jitReplaceFunc) { MIPSReplaceFunc repl = entry->jitReplaceFunc; @@ -686,6 +739,60 @@ void ArmJit::WriteSyscallExit() B((const void *)dispatcherCheckCoreState); } +bool ArmJit::CheckJitBreakpoint(u32 addr, int downcountOffset) { + if (CBreakPoints::IsAddressBreakPoint(addr)) { + MRS(R8); + FlushAll(); + MOVI2R(SCRATCHREG1, GetCompilerPC()); + MovToPC(SCRATCHREG1); + RestoreRoundingMode(); + QuickCallFunction(SCRATCHREG1, &JitBreakpoint); + + // If 0, the conditional breakpoint wasn't taken. + CMPI2R(R0, 0, SCRATCHREG2); + FixupBranch skip = B_CC(CC_EQ); + WriteDownCount(downcountOffset); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(true, false, R8); + return true; + } + + return false; +} + +bool ArmJit::CheckMemoryBreakpoint(int instructionOffset) { + if (CBreakPoints::HasMemChecks()) { + int off = instructionOffset + (js.inDelaySlot ? 1 : 0); + + MRS(R8); + FlushAll(); + RestoreRoundingMode(); + MOVI2R(R0, GetCompilerPC()); + MovToPC(R0); + if (off != 0) + ADDI2R(R0, R0, off, SCRATCHREG2); + QuickCallFunction(SCRATCHREG2, &JitMemCheck); + + // If 0, the breakpoint wasn't tripped. + CMPI2R(R0, 0, SCRATCHREG2); + FixupBranch skip = B_CC(CC_EQ); + WriteDownCount(-1 - off); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(true, false, R8); + return true; + } + + return false; +} + void ArmJit::Comp_DoNothing(MIPSOpcode op) { } MIPSOpcode ArmJit::GetOriginalOp(MIPSOpcode op) { diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index af2b6a128d..80a3bc6ae9 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -215,6 +215,8 @@ private: void WriteExit(u32 destination, int exit_num); void WriteExitDestInR(ArmGen::ARMReg Reg); void WriteSyscallExit(); + bool CheckJitBreakpoint(u32 addr, int downcountOffset); + bool CheckMemoryBreakpoint(int instructionOffset = 0); // Utility compilation functions void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely); @@ -311,8 +313,6 @@ public: const u8 *restoreRoundingMode; const u8 *applyRoundingMode; - - const u8 *breakpointBailout; }; } // namespace MIPSComp diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 2390ed9673..1abf148d7c 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -275,7 +275,6 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { B(CC_EQ, outerLoop); SetJumpTarget(badCoreState); - breakpointBailout = GetCodePtr(); SaveStaticRegisters(); RestoreRoundingMode(true); diff --git a/Core/MIPS/ARM64/Arm64CompFPU.cpp b/Core/MIPS/ARM64/Arm64CompFPU.cpp index e608baa6d4..55298ae577 100644 --- a/Core/MIPS/ARM64/Arm64CompFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompFPU.cpp @@ -81,6 +81,7 @@ void Arm64Jit::Comp_FPU3op(MIPSOpcode op) { void Arm64Jit::Comp_FPULS(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); // Surprisingly, these work fine alraedy. @@ -88,7 +89,6 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) int ft = _FT; MIPSGPReg rs = _RS; // u32 addr = R(rs) + offset; - // logBlocks = 1; std::vector skips; switch (op >> 26) { case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1 diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index 4e5f42f053..7b962f0e95 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -111,6 +111,7 @@ namespace MIPSComp { void Arm64Jit::Comp_ITypeMemLR(MIPSOpcode op, bool load) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -119,6 +120,7 @@ namespace MIPSComp { if (!js.inDelaySlot) { // Optimisation: Combine to single unaligned load/store bool isLeft = (o == 34 || o == 42); + CheckMemoryBreakpoint(1); MIPSOpcode nextOp = GetOffsetInstruction(1); // Find a matching shift in opposite direction with opposite offset. if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) { @@ -255,6 +257,7 @@ namespace MIPSComp { void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); bool load = false; diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 0277df28e7..7799dfbfa4 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -202,6 +202,7 @@ namespace MIPSComp { void Arm64Jit::Comp_SV(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (signed short)(op & 0xFFFC); int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); @@ -275,6 +276,7 @@ namespace MIPSComp { void Arm64Jit::Comp_SVQ(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int imm = (signed short)(op&0xFFFC); int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); diff --git a/Core/MIPS/ARM64/Arm64Jit.cpp b/Core/MIPS/ARM64/Arm64Jit.cpp index 46b1a9bf3e..afadca29e0 100644 --- a/Core/MIPS/ARM64/Arm64Jit.cpp +++ b/Core/MIPS/ARM64/Arm64Jit.cpp @@ -28,6 +28,7 @@ #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" @@ -44,7 +45,7 @@ using namespace Arm64JitConstants; -void DisassembleArm64Print(const u8 *data, int size) { +static void DisassembleArm64Print(const u8 *data, int size) { std::vector lines = DisassembleArm64(data, size); for (auto s : lines) { ILOG("%s", s.c_str()); @@ -60,6 +61,33 @@ void DisassembleArm64Print(const u8 *data, int size) { ILOG("===");*/ } +static u32 JitBreakpoint() { + // Should we skip this breakpoint? + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc); + if ((result & BREAK_ACTION_PAUSE) == 0) + return 0; + + return 1; +} + +static u32 JitMemCheck(u32 pc) { + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + // Note: pc may be the delay slot. + const auto op = Memory::Read_Instruction(pc, true); + s32 offset = (s16)(op & 0xFFFF); + if (MIPSGetInfo(op) & IS_VFPU) + offset &= 0xFFFC; + u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset; + + CBreakPoints::ExecOpMemCheck(addr, pc); + return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1; +} + namespace MIPSComp { using namespace Arm64Gen; @@ -80,6 +108,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f GenerateFixedCode(jo); js.startDefaultPrefix = mips_->HasDefaultPrefix(); js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3]; + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } Arm64Jit::~Arm64Jit() { @@ -99,6 +131,10 @@ void Arm64Jit::DoState(PointerWrap &p) { } // Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet. + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } void Arm64Jit::UpdateFCR31() { @@ -151,12 +187,16 @@ void Arm64Jit::EatInstruction(MIPSOpcode op) { ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot."); } + CheckJitBreakpoint(GetCompilerPC() + 4, 0); js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); } void Arm64Jit::CompileDelaySlot(int flags) { + // Need to offset the downcount which was already incremented for the branch + delay slot. + CheckJitBreakpoint(GetCompilerPC() + 4, -2); + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. @@ -285,8 +325,10 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) { js.numInstructions = 0; while (js.compiling) { gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages + // Jit breakpoints are quite fast, so let's do them in release too. + CheckJitBreakpoint(GetCompilerPC(), 0); + MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC()); - js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst, this); @@ -473,7 +515,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op) return; } - if (entry->flags & REPFLAG_DISABLED) { + u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC()); + bool disabled = (entry->flags & REPFLAG_DISABLED) != 0; + if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) { + // We don't need to disable hooks, the code will still run. + if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) { + // Any breakpoint at the func entry was already tripped, so we can still run the replacement. + // That's a common case - just to see how often the replacement hits. + disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32)); + } + } + + if (disabled) { MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this); } else if (entry->jitReplaceFunc) { MIPSReplaceFunc repl = entry->jitReplaceFunc; @@ -646,6 +699,60 @@ void Arm64Jit::WriteSyscallExit() { B((const void *)dispatcherCheckCoreState); } +bool Arm64Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) { + if (CBreakPoints::IsAddressBreakPoint(addr)) { + MRS(FLAGTEMPREG, FIELD_NZCV); + FlushAll(); + MOVI2R(SCRATCH1, GetCompilerPC()); + MovToPC(SCRATCH1); + RestoreRoundingMode(); + QuickCallFunction(SCRATCH1_64, &JitBreakpoint); + + // If 0, the conditional breakpoint wasn't taken. + CMPI2R(W0, 0); + FixupBranch skip = B(CC_EQ); + WriteDownCount(downcountOffset); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(FIELD_NZCV, FLAGTEMPREG); + return true; + } + + return false; +} + +bool Arm64Jit::CheckMemoryBreakpoint(int instructionOffset) { + if (CBreakPoints::HasMemChecks()) { + int off = instructionOffset + (js.inDelaySlot ? 1 : 0); + + MRS(FLAGTEMPREG, FIELD_NZCV); + FlushAll(); + RestoreRoundingMode(); + MOVI2R(W0, GetCompilerPC()); + MovToPC(W0); + if (off != 0) + ADDI2R(W0, W0, off * 4); + QuickCallFunction(SCRATCH2_64, &JitMemCheck); + + // If 0, the breakpoint wasn't tripped. + CMPI2R(W0, 0); + FixupBranch skip = B(CC_EQ); + WriteDownCount(-1 - off); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(FIELD_NZCV, FLAGTEMPREG); + return true; + } + + return false; +} + void Arm64Jit::Comp_DoNothing(MIPSOpcode op) { } MIPSOpcode Arm64Jit::GetOriginalOp(MIPSOpcode op) { diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index 3d84451384..6ad5921d53 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -214,6 +214,8 @@ private: void WriteExit(u32 destination, int exit_num); void WriteExitDestInR(Arm64Gen::ARM64Reg Reg); void WriteSyscallExit(); + bool CheckJitBreakpoint(u32 addr, int downcountOffset); + bool CheckMemoryBreakpoint(int instructionOffset = 0); // Utility compilation functions void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely); @@ -272,8 +274,6 @@ public: const u8 *dispatcher; const u8 *dispatcherNoCheck; - const u8 *breakpointBailout; - const u8 *saveStaticRegisters; const u8 *loadStaticRegisters; diff --git a/Core/MIPS/MIPS/MipsJit.h b/Core/MIPS/MIPS/MipsJit.h index 88c1c2dc6d..23b164b1e7 100644 --- a/Core/MIPS/MIPS/MipsJit.h +++ b/Core/MIPS/MIPS/MipsJit.h @@ -181,8 +181,6 @@ public: const u8 *dispatcherPCInR0; const u8 *dispatcher; const u8 *dispatcherNoCheck; - - const u8 *breakpointBailout; }; typedef void (MipsJit::*MIPSCompileFunc)(MIPSOpcode opcode); diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index a6242801ff..fd1815132d 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -211,11 +211,6 @@ void Jit::GenerateFixedCode(JitOptions &jo) { ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); - breakpointBailout = GetCodePtr(); - RestoreRoundingMode(true); - ABI_PopAllCalleeSavedRegsAndAdjustStack(); - RET(); - // Let's spare the pre-generated code from unprotect-reprotect. endOfPregeneratedCode = AlignCodePage(); EndWrite(); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 1c397005ff..0eefe89c40 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -320,8 +320,6 @@ private: const u8 *dispatcherNoCheck; const u8 *dispatcherInEAXNoCheck; - const u8 *breakpointBailout; - const u8 *restoreRoundingMode; const u8 *applyRoundingMode; diff --git a/Core/MIPS/x86/JitSafeMem.cpp b/Core/MIPS/x86/JitSafeMem.cpp index 56374ed828..104b6579a0 100644 --- a/Core/MIPS/x86/JitSafeMem.cpp +++ b/Core/MIPS/x86/JitSafeMem.cpp @@ -376,17 +376,10 @@ void JitSafeMem::MemCheckImm(MemoryOpType type) void JitSafeMem::MemCheckAsm(MemoryOpType type) { - const auto memchecks = CBreakPoints::GetMemCheckRanges(); - bool possible = false; + const auto memchecks = CBreakPoints::GetMemCheckRanges(type == MEM_WRITE); + bool possible = !memchecks.empty(); for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it) { - if (!(it->cond & MEMCHECK_READ) && type == MEM_READ) - continue; - if (!(it->cond & MEMCHECK_WRITE) && type == MEM_WRITE) - continue; - - possible = true; - FixupBranch skipNext, skipNextRange; if (it->end != 0) { diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index eb5d49ab14..8282aac3db 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -446,9 +446,8 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { for (size_t i = 0; i < _iLength; i++) Write_U8(_iValue, (u32)(_Address + i)); } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc); -#endif } } // namespace diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 1fe06d3ae1..9431db5c0d 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -33,9 +33,7 @@ inline void Memcpy(const u32 to_address, const void *from_data, const u32 len) u8 *to = GetPointer(to_address); if (to) { memcpy(to, from_data, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); -#endif } // if not, GetPointer will log. } @@ -45,9 +43,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len) const u8 *from = GetPointer(from_address); if (from) { memcpy(to_data, from, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(from_address, false, len, currentMIPS->pc); -#endif } // if not, GetPointer will log. } @@ -55,9 +51,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len) inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) { Memcpy(GetPointer(to_address), from_address, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); -#endif } void Memset(const u32 _Address, const u8 _Data, const u32 _iLength); diff --git a/Core/System.cpp b/Core/System.cpp index afe1ac76ea..6355c66dd3 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -430,10 +430,24 @@ void PSP_EndHostFrame() { } } +void PSP_RunLoopWhileState() { + // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. + // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... + int blockTicks = usToCycles(1000000 / 10); + + // Run until CORE_NEXTFRAME + while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) { + PSP_RunLoopFor(blockTicks); + } +} + void PSP_RunLoopUntil(u64 globalticks) { SaveState::Process(); if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) { return; + } else if (coreState == CORE_STEPPING) { + Core_ProcessStepping(); + return; } mipsr4k.RunLoopUntil(globalticks); diff --git a/Core/System.h b/Core/System.h index a2a3ed5c48..32182c3bc2 100644 --- a/Core/System.h +++ b/Core/System.h @@ -70,6 +70,7 @@ void PSP_Shutdown(); void PSP_BeginHostFrame(); void PSP_EndHostFrame(); +void PSP_RunLoopWhileState(); void PSP_RunLoopUntil(u64 globalticks); void PSP_RunLoopFor(int cycles); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1950eab058..dd4c4ad0a0 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2576,10 +2576,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (dstY * dstStride + dstX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index a240840496..29a50f2054 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -344,10 +344,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index b24d286940..9432ba97ef 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -648,10 +648,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8064883ada..35cf6a0e0e 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1193,14 +1193,7 @@ void EmuScreen::render() { PSP_BeginHostFrame(); - // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. - // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... - int blockTicks = usToCycles(1000000 / 10); - - // Run until CORE_NEXTFRAME - while (coreState == CORE_RUNNING) { - PSP_RunLoopFor(blockTicks); - } + PSP_RunLoopWhileState(); // Hopefully coreState is now CORE_NEXTFRAME if (coreState == CORE_NEXTFRAME) { diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 5d187b915f..bf5f9c7995 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -809,7 +809,6 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) if (_bDebug && GetUIState() == UISTATE_INGAME && PSP_IsInited()) { Core_WaitInactive(TEMP_BREAKPOINT_WAIT_MS); - CBreakPoints::ClearTemporaryBreakPoints(); breakpointList->reloadBreakpoints(); threadList->reloadThreads(); stackTraceView->loadStackTrace(); @@ -913,4 +912,4 @@ void CDisasm::UpdateDialog(bool _bComplete) // redraw. all others are updated manually InvalidateRect (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), NULL, TRUE); UpdateWindow (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW)); -} \ No newline at end of file +}