diff --git a/Common/System/System.h b/Common/System/System.h index fdd7a8187480..365845181689 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -217,6 +217,7 @@ enum class SystemNotification { UI, MEM_VIEW, DISASSEMBLY, + DISASSEMBLY_AFTERSTEP, DEBUG_MODE_CHANGE, BOOT_DONE, // this is sent from EMU thread! Make sure that Host handles it properly! SYMBOL_MAP_UPDATED, diff --git a/Core/Core.cpp b/Core/Core.cpp index e30f5825d884..cf4165ebf16f 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -41,6 +41,7 @@ #include "Core/Debugger/Breakpoints.h" #include "Core/HW/Display.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSAnalyst.h" #include "Core/HLE/sceNetAdhoc.h" #include "GPU/Debugger/Stepping.h" #include "Core/MIPS/MIPSTracer.h" @@ -54,7 +55,7 @@ static std::condition_variable m_StepCond; static std::mutex m_hStepMutex; static std::condition_variable m_InactiveCond; static std::mutex m_hInactiveMutex; -static bool singleStepPending = false; +static int g_singleStepsPending = 0; static int steppingCounter = 0; static const char *steppingReason = ""; static uint32_t steppingAddress = 0; @@ -228,7 +229,7 @@ void Core_RunLoop(GraphicsContext *ctx) { void Core_DoSingleStep() { std::lock_guard guard(m_hStepMutex); - singleStepPending = true; + g_singleStepsPending++; m_StepCond.notify_all(); } @@ -237,24 +238,95 @@ void Core_UpdateSingleStep() { m_StepCond.notify_all(); } -void Core_SingleStep() { - Core_ResetException(); - currentMIPS->SingleStep(); - if (coreState == CORE_STEPPING) - steppingCounter++; +// See comment in header. +u32 Core_PerformStep(DebugInterface *cpu, CPUStepType stepType, int stepSize) { + switch (stepType) { + case CPUStepType::Into: + { + u32 currentPc = cpu->GetPC(); + u32 newAddress = currentPc + stepSize; + // If the current PC is on a breakpoint, the user still wants the step to happen. + CBreakPoints::SetSkipFirst(currentPc); + for (int i = 0; i < (newAddress - currentPc) / 4; i++) { + Core_DoSingleStep(); + } + return cpu->GetPC(); + } + case CPUStepType::Over: + { + u32 currentPc = cpu->GetPC(); + u32 breakpointAddress = currentPc + stepSize; + + CBreakPoints::SetSkipFirst(currentPc); + + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, cpu->GetPC()); + if (info.isBranch) { + if (info.isConditional == false) { + if (info.isLinkedBranch) { // jal, jalr + // it's a function call with a delay slot - skip that too + breakpointAddress += cpu->getInstructionSize(0); + } else { // j, ... + // in case of absolute branches, set the breakpoint at the branch target + breakpointAddress = info.branchTarget; + } + } else { // beq, ... + if (info.conditionMet) { + breakpointAddress = info.branchTarget; + } else { + breakpointAddress = currentPc + 2 * cpu->getInstructionSize(0); + } + } + } + + CBreakPoints::AddBreakPoint(breakpointAddress, true); + Core_Resume(); + return breakpointAddress; + } + case CPUStepType::Out: + { + u32 entry = cpu->GetPC(); + u32 stackTop = 0; + + auto threads = GetThreadsInfo(); + for (size_t i = 0; i < threads.size(); i++) { + if (threads[i].isCurrent) { + entry = threads[i].entrypoint; + stackTop = threads[i].initialStack; + break; + } + } + + auto frames = MIPSStackWalk::Walk(cpu->GetPC(), cpu->GetRegValue(0, 31), cpu->GetRegValue(0, 29), entry, stackTop); + if (frames.size() < 2) { + // Failure. PC not moving. + return cpu->GetPC(); + } + + u32 breakpointAddress = frames[1].pc; + + // If the current PC is on a breakpoint, the user doesn't want to do nothing. + CBreakPoints::SetSkipFirst(currentMIPS->pc); + CBreakPoints::AddBreakPoint(breakpointAddress, true); + Core_Resume(); + return breakpointAddress; + } + default: + // Not yet implemented + return cpu->GetPC(); + } } -static inline bool Core_WaitStepping() { +static inline int Core_WaitStepping() { std::unique_lock guard(m_hStepMutex); // We only wait 16ms so that we can still draw UI or react to events. double sleepStart = time_now_d(); - if (!singleStepPending && coreState == CORE_STEPPING) + if (!g_singleStepsPending && coreState == CORE_STEPPING) m_StepCond.wait_for(guard, std::chrono::milliseconds(16)); double sleepEnd = time_now_d(); DisplayNotifySleep(sleepEnd - sleepStart); - bool result = singleStepPending; - singleStepPending = false; + int result = g_singleStepsPending; + g_singleStepsPending = 0; return result; } @@ -274,19 +346,25 @@ void Core_ProcessStepping() { static int lastSteppingCounter = -1; if (lastSteppingCounter != steppingCounter) { CBreakPoints::ClearTemporaryBreakPoints(); - System_Notify(SystemNotification::DISASSEMBLY); + System_Notify(SystemNotification::DISASSEMBLY_AFTERSTEP); System_Notify(SystemNotification::MEM_VIEW); lastSteppingCounter = steppingCounter; } // Need to check inside the lock to avoid races. - bool doStep = Core_WaitStepping(); + int doSteps = Core_WaitStepping(); // We may still be stepping without singleStepPending to process a save state. - if (doStep && coreState == CORE_STEPPING) { - Core_SingleStep(); + if (doSteps && coreState == CORE_STEPPING) { + Core_ResetException(); + + for (int i = 0; i < doSteps; i++) { + currentMIPS->SingleStep(); + steppingCounter++; + } + // Update disasm dialog. - System_Notify(SystemNotification::DISASSEMBLY); + System_Notify(SystemNotification::DISASSEMBLY_AFTERSTEP); System_Notify(SystemNotification::MEM_VIEW); } } @@ -333,23 +411,24 @@ bool Core_Run(GraphicsContext *ctx) { } } -void Core_EnableStepping(bool step, const char *reason, u32 relatedAddress) { - if (step) { - // Stop the tracer - mipsTracer.stop_tracing(); +void Core_Break(const char *reason, u32 relatedAddress) { + // Stop the tracer + mipsTracer.stop_tracing(); - Core_UpdateState(CORE_STEPPING); - steppingCounter++; - _assert_msg_(reason != nullptr, "No reason specified for break"); - steppingReason = reason; - steppingAddress = relatedAddress; - } else { - // Clear the exception if we resume. - Core_ResetException(); - coreState = CORE_RUNNING; - coreStatePending = false; - m_StepCond.notify_all(); - } + Core_UpdateState(CORE_STEPPING); + steppingCounter++; + _assert_msg_(reason != nullptr, "No reason specified for break"); + steppingReason = reason; + steppingAddress = relatedAddress; + System_Notify(SystemNotification::DEBUG_MODE_CHANGE); +} + +void Core_Resume() { + // Clear the exception if we resume. + Core_ResetException(); + coreState = CORE_RUNNING; + coreStatePending = false; + m_StepCond.notify_all(); System_Notify(SystemNotification::DEBUG_MODE_CHANGE); } @@ -428,7 +507,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy e.accessSize = accessSize; e.stackTrace = stackTrace; e.pc = pc; - Core_EnableStepping(true, "memory.exception", address); + Core_Break("memory.exception", address); } } @@ -456,7 +535,7 @@ void Core_MemoryExceptionInfo(u32 address, u32 accessSize, u32 pc, MemoryExcepti e.accessSize = accessSize; e.stackTrace = stackTrace; e.pc = pc; - Core_EnableStepping(true, "memory.exception", address); + Core_Break("memory.exception", address); } } @@ -475,10 +554,10 @@ void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { e.pc = pc; // This just records the closest value that could be useful as reference. e.ra = currentMIPS->r[MIPS_REG_RA]; - Core_EnableStepping(true, "cpu.exception", address); + Core_Break("cpu.exception", address); } -void Core_Break(u32 pc) { +void Core_BreakException(u32 pc) { ERROR_LOG(Log::CPU, "BREAK!"); MIPSExceptionInfo &e = g_exceptionInfo; @@ -488,7 +567,7 @@ void Core_Break(u32 pc) { e.pc = pc; if (!g_Config.bIgnoreBadMemAccess) { - Core_EnableStepping(true, "cpu.breakInstruction", currentMIPS->pc); + Core_Break("cpu.breakInstruction", currentMIPS->pc); } } diff --git a/Core/Core.h b/Core/Core.h index 9a0ca60903fe..a3cc091b458d 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -24,28 +24,51 @@ #include "Core/CoreParameter.h" class GraphicsContext; +class DebugInterface; // called from emu thread void UpdateRunLoop(GraphicsContext *ctx); +// For platforms that don't call Core_Run +void Core_SetGraphicsContext(GraphicsContext *ctx); + // Returns false when an UI exit state is detected. bool Core_Run(GraphicsContext *ctx); void Core_Stop(); -// For platforms that don't call Core_Run -void Core_SetGraphicsContext(GraphicsContext *ctx); +// X11, sigh. +#ifdef None +#undef None +#endif -// called from gui -void Core_EnableStepping(bool step, const char *reason = nullptr, u32 relatedAddress = 0); +enum class CPUStepType { + None, + Into, + Over, + Out, +}; -bool Core_ShouldRunBehind(); -bool Core_MustRunBehind(); +// Async, called from gui +void Core_Break(const char *reason, u32 relatedAddress = 0); +// void Core_Step(CPUStepType type); // CPUStepType::None not allowed +void Core_Resume(); -bool Core_NextFrame(); +// Handles more advanced step types (used by the debugger). +// stepSize is to support stepping through compound instructions like fused lui+ladd (li). +// Yes, our disassembler does support those. +// Returns the new address. +uint32_t Core_PerformStep(DebugInterface *mips, CPUStepType stepType, int stepSize); + +// Refactor. void Core_DoSingleStep(); void Core_UpdateSingleStep(); void Core_ProcessStepping(); +bool Core_ShouldRunBehind(); +bool Core_MustRunBehind(); + +bool Core_NextFrame(); + // Changes every time we enter stepping. int Core_GetSteppingCounter(); struct SteppingReason { @@ -113,7 +136,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy void Core_MemoryExceptionInfo(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool forceReport); void Core_ExecException(u32 address, u32 pc, ExecExceptionType type); -void Core_Break(u32 pc); +void Core_BreakException(u32 pc); // Call when loading save states, etc. void Core_ResetException(); diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index b357dc24043e..3e4f611b5ea9 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -153,7 +153,7 @@ int RegisterEvent(const char *name, TimedCallback callback) { void AntiCrashCallback(u64 userdata, int cyclesLate) { ERROR_LOG(Log::SaveState, "Savestate broken: an unregistered event was called."); - Core_EnableStepping(true, "savestate.crash", 0); + Core_Break("savestate.crash", 0); } void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback) { diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 0f0d344cffd0..9446fc715f89 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -75,7 +75,7 @@ BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc, const char // Conditions have always already been checked if we get here. Log(addr, write, size, pc, reason); if ((result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) { - Core_EnableStepping(true, "memory.breakpoint", start); + Core_Break("memory.breakpoint", start); } return result; @@ -331,7 +331,7 @@ BreakAction CBreakPoints::ExecBreakPoint(u32 addr) { } } if ((info.result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) { - Core_EnableStepping(true, "cpu.breakpoint", info.addr); + Core_Break("cpu.breakpoint", info.addr); } return info.result; @@ -653,7 +653,7 @@ void CBreakPoints::Update(u32 addr) { if (MIPSComp::jit && addr != -1) { bool resume = false; if (Core_IsStepping() == false) { - Core_EnableStepping(true, "cpu.breakpoint.update", addr); + Core_Break("cpu.breakpoint.update", addr); Core_WaitInactive(200); resume = true; } @@ -665,7 +665,7 @@ void CBreakPoints::Update(u32 addr) { mipsr4k.ClearJitCache(); if (resume) - Core_EnableStepping(false); + Core_Resume(); } if (anyMemChecks_ && addr != -1) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 0e0286b39494..4d32dd82fca9 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -71,7 +71,7 @@ void WebSocketCPUStepping(DebuggerRequest &req) { return req.Fail("CPU not started"); } if (!Core_IsStepping() && Core_IsActive()) { - Core_EnableStepping(true, "cpu.stepping", 0); + Core_Break("cpu.stepping", 0); } } @@ -92,7 +92,7 @@ void WebSocketCPUResume(DebuggerRequest &req) { if (currentMIPS->inDelaySlot) { Core_DoSingleStep(); } - Core_EnableStepping(false); + Core_Resume(); } // Request the current CPU status (cpu.status) diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index 0ec5bdcd702b..524afc023402 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -64,7 +64,7 @@ static AutoDisabledReplacements LockMemoryAndCPU(uint32_t addr, bool keepReplace if (Core_IsStepping()) { result.wasStepping = true; } else { - Core_EnableStepping(true, "memory.access", addr); + Core_Break("memory.access", addr); Core_WaitInactive(); } @@ -99,7 +99,7 @@ AutoDisabledReplacements::~AutoDisabledReplacements() { RestoreSavedReplacements(replacements); } if (!wasStepping) - Core_EnableStepping(false); + Core_Resume(); delete lock; } diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index b07349a26b69..468185ed8c93 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -93,7 +93,7 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) return req.Fail("CPU not started"); if (!Core_IsStepping()) { - Core_EnableStepping(true, "cpu.stepInto", 0); + Core_Break("cpu.stepInto", 0); return; } @@ -119,7 +119,7 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { if (cpuDebug != currentDebugMIPS) { CBreakPoints::AddBreakPoint(breakpointAddress, true); AddThreadCondition(breakpointAddress, threadID); - Core_EnableStepping(false); + Core_Resume(); } } } @@ -173,7 +173,7 @@ void WebSocketSteppingState::Over(DebuggerRequest &req) { CBreakPoints::AddBreakPoint(breakpointAddress, true); if (cpuDebug != currentDebugMIPS) AddThreadCondition(breakpointAddress, threadID); - Core_EnableStepping(false); + Core_Resume(); } } @@ -222,7 +222,7 @@ void WebSocketSteppingState::Out(DebuggerRequest &req) { CBreakPoints::AddBreakPoint(breakpointAddress, true); if (cpuDebug != currentDebugMIPS) AddThreadCondition(breakpointAddress, threadID); - Core_EnableStepping(false); + Core_Resume(); } } @@ -248,7 +248,7 @@ void WebSocketSteppingState::RunUntil(DebuggerRequest &req) { // We may have arrived already if PauseResume() stepped out of a delay slot. if (currentMIPS->pc != address || wasAtAddress) { CBreakPoints::AddBreakPoint(address, true); - Core_EnableStepping(false); + Core_Resume(); } } @@ -264,7 +264,7 @@ void WebSocketSteppingState::HLE(DebuggerRequest &req) { PrepareResume(); hleDebugBreak(); - Core_EnableStepping(false); + Core_Resume(); } uint32_t WebSocketSteppingState::GetNextAddress(DebugInterface *cpuDebug) { diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index b771503a21e9..6fbe816b45de 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -361,7 +361,7 @@ bool hleExecuteDebugBreak(const HLEFunction &func) return false; } - Core_EnableStepping(true, "hle.step", latestSyscallPC); + Core_Break("hle.step", latestSyscallPC); return true; } diff --git a/Core/MIPS/ARM64/Arm64IRCompSystem.cpp b/Core/MIPS/ARM64/Arm64IRCompSystem.cpp index 91a63978f419..92f076760a50 100644 --- a/Core/MIPS/ARM64/Arm64IRCompSystem.cpp +++ b/Core/MIPS/ARM64/Arm64IRCompSystem.cpp @@ -260,7 +260,7 @@ void Arm64JitBackend::CompIR_System(IRInst inst) { RestoreRoundingMode(true); SaveStaticRegisters(); MovFromPC(W0); - QuickCallFunction(SCRATCH2_64, &Core_Break); + QuickCallFunction(SCRATCH2_64, &Core_BreakException); LoadStaticRegisters(); ApplyRoundingMode(true); MovFromPC(SCRATCH1); diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 0d482b485d9a..afd002aa8261 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -1196,7 +1196,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { break; case IROp::Break: - Core_Break(mips->pc); + Core_BreakException(mips->pc); return mips->pc + 4; case IROp::Breakpoint: diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 5a04ff21477c..c80d7ebaaeb4 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -178,7 +178,7 @@ namespace MIPSInt void Int_Break(MIPSOpcode op) { Reporting::ReportMessage("BREAK instruction hit"); - Core_Break(PC); + Core_BreakException(PC); PC += 4; } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index cb3ab2e5a208..d5eaa63ef8a4 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -1004,7 +1004,7 @@ static void RunUntilWithChecks(u64 globalTicks) { if (hasBPs && CBreakPoints::IsAddressBreakPoint(curMips->pc) && CBreakPoints::CheckSkipFirst() != curMips->pc) { auto cond = CBreakPoints::GetBreakPointCondition(currentMIPS->pc); if (!cond || cond->Evaluate()) { - Core_EnableStepping(true, "cpu.breakpoint", curMips->pc); + Core_Break("cpu.breakpoint", curMips->pc); if (CBreakPoints::IsTempBreakPoint(curMips->pc)) CBreakPoints::RemoveBreakPoint(curMips->pc); break; diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index 4bba8a9eceb5..d3cbabf1d514 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -232,7 +232,7 @@ void RiscVJitBackend::CompIR_System(IRInst inst) { RestoreRoundingMode(true); SaveStaticRegisters(); MovFromPC(X10); - QuickCallFunction(&Core_Break, SCRATCH2); + QuickCallFunction(&Core_BreakException, SCRATCH2); LoadStaticRegisters(); ApplyRoundingMode(true); MovFromPC(SCRATCH1); diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 0e58a14ce9c6..ad76f20de9f3 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -109,7 +109,7 @@ static void JitBranchLogMismatch(MIPSOpcode op, u32 pc) char temp[256]; MIPSDisAsm(op, pc, temp, sizeof(temp), true); ERROR_LOG(Log::JIT, "Bad jump: %s - int:%08x jit:%08x", temp, currentMIPS->intBranchExit, currentMIPS->jitBranchExit); - Core_EnableStepping(true, "jit.branchdebug", pc); + Core_Break("jit.branchdebug", pc); } void Jit::BranchLog(MIPSOpcode op) diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp index 353f0166a549..33b32054933a 100644 --- a/Core/MIPS/x86/X64IRCompSystem.cpp +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -255,7 +255,7 @@ void X64JitBackend::CompIR_System(IRInst inst) { RestoreRoundingMode(true); SaveStaticRegisters(); MovFromPC(SCRATCH1); - ABI_CallFunctionR((const void *)&Core_Break, SCRATCH1); + ABI_CallFunctionR((const void *)&Core_BreakException, SCRATCH1); LoadStaticRegisters(); ApplyRoundingMode(true); MovFromPC(SCRATCH1); diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 7877982b1b3b..12a793e2452d 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -554,7 +554,7 @@ static void raintegration_event_handler(const rc_client_raintegration_event_t *e break; case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE: // The toolkit has hit a breakpoint and wants to pause the emulator. Do so. - Core_EnableStepping(true, "ra_breakpoint"); + Core_Break("ra_breakpoint"); break; case RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED: // Hardcore mode has been changed (either directly by the user, or disabled through the use of the tools). diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index bc3950e15c3f..6cc7ea63dd80 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -426,7 +426,7 @@ namespace SaveState { rewindStates.NotifyState(); if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true, "savestate.load", 0); + Core_Break("savestate.load", 0); Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData)); } @@ -434,7 +434,7 @@ namespace SaveState { rewindStates.NotifyState(); if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true, "savestate.save", 0); + Core_Break("savestate.save", 0); Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData)); } @@ -446,7 +446,7 @@ namespace SaveState void Rewind(Callback callback, void *cbUserData) { if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true, "savestate.rewind", 0); + Core_Break("savestate.rewind", 0); Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData)); } diff --git a/SDL/CocoaBarItems.mm b/SDL/CocoaBarItems.mm index 664095c8d5d4..843cfd3dc376 100644 --- a/SDL/CocoaBarItems.mm +++ b/SDL/CocoaBarItems.mm @@ -438,10 +438,10 @@ -(void)breakAction: (NSMenuItem *)item { std::shared_ptr developerUILocalization = GetI18NCategory(I18NCat::DEVELOPER); #define DEVELOPERUI_LOCALIZED(key) @(developerUILocalization->T_cstr(key)) if (Core_IsStepping()) { - Core_EnableStepping(false, "ui.break"); + Core_Resume(); item.title = DESKTOPUI_LOCALIZED("Break"); } else { - Core_EnableStepping(true, "ui.break"); + Core_Break("ui.break", 0); item.title = DEVELOPERUI_LOCALIZED("Resume"); } } @@ -452,7 +452,7 @@ -(void)pauseAction: (NSMenuItem *)item { -(void)resetAction: (NSMenuItem *)item { System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); - Core_EnableStepping(false); + Core_Resume(); } -(void)chatAction: (NSMenuItem *)item { diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index c172f439c1e3..9ae8bd5ae32b 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -843,7 +843,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta if (ctrl && (k == SDLK_w)) { if (Core_IsStepping()) - Core_EnableStepping(false); + Core_Resume(); Core_Stop(); System_PostUIMessage(UIMessage::REQUEST_GAME_STOP); // NOTE: Unlike Windows version, this @@ -854,7 +854,7 @@ static void ProcessSDLEvent(SDL_Window *window, const SDL_Event &event, InputSta if (ctrl && (k == SDLK_b)) { System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); - Core_EnableStepping(false); + Core_Resume(); } } break; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8b30ccf01cb7..b0ff066aa092 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -118,7 +118,7 @@ static void __EmuScreenVblank() if (frameStep_ && lastNumFlips != gpuStats.numFlips) { frameStep_ = false; - Core_EnableStepping(true, "ui.frameAdvance", 0); + Core_Break("ui.frameAdvance", 0); lastNumFlips = gpuStats.numFlips; } #ifndef MOBILE_DEVICE @@ -495,7 +495,7 @@ static void AfterSaveStateAction(SaveState::Status status, std::string_view mess static void AfterStateBoot(SaveState::Status status, std::string_view message, void *ignored) { AfterSaveStateAction(status, message, ignored); - Core_EnableStepping(false); + Core_Resume(); System_Notify(SystemNotification::DISASSEMBLY); } @@ -658,7 +658,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { case VIRTKEY_FASTFORWARD: if (down) { if (coreState == CORE_STEPPING) { - Core_EnableStepping(false); + Core_Resume(); } PSP_CoreParameter().fastForward = true; } else { @@ -1452,10 +1452,10 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { // If game is running, pause emulation immediately. Otherwise, advance a single frame. if (Core_IsStepping()) { frameStep_ = true; - Core_EnableStepping(false); + Core_Resume(); } else if (!frameStep_) { lastNumFlips = gpuStats.numFlips; - Core_EnableStepping(true, "ui.frameAdvance", 0); + Core_Break("ui.frameAdvance", 0); } } } diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 7aad6e246ff5..f8acfc6299b2 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -918,7 +918,7 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, bool showPau fastForward->SetAngle(180.0f); fastForward->OnChange.Add([](UI::EventParams &e) { if (e.a && coreState == CORE_STEPPING) { - Core_EnableStepping(false); + Core_Resume(); } return UI::EVENT_DONE; }); diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 7f9d2a2dbddf..9ce66398f9d2 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -1428,5 +1428,5 @@ u32 CtrlDisAsmView::getInstructionSizeAt(u32 address) { u32 start = manager.getStartAddress(address); u32 next = manager.getNthNextAddress(start,1); - return next-address; + return next - address; } diff --git a/Windows/Debugger/CtrlDisAsmView.h b/Windows/Debugger/CtrlDisAsmView.h index d58b99936043..4bfd1c0fc9f2 100644 --- a/Windows/Debugger/CtrlDisAsmView.h +++ b/Windows/Debugger/CtrlDisAsmView.h @@ -169,7 +169,7 @@ class CtrlDisAsmView void setCurAddress(u32 newAddress, bool extend = false) { newAddress = manager.getStartAddress(newAddress); - u32 after = manager.getNthNextAddress(newAddress,1); + const u32 after = manager.getNthNextAddress(newAddress,1); curAddress = newAddress; selectRangeStart = extend ? std::min(selectRangeStart, newAddress) : newAddress; selectRangeEnd = extend ? std::max(selectRangeEnd, after) : after; diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index 0efdeb468a89..6a6f0a218770 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -427,7 +427,7 @@ void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) { bool active = Core_IsActive(); if (active) - Core_EnableStepping(true, "memory.access", curAddress_); + Core_Break("memory.access", curAddress_); if (asciiSelected_) { Memory::WriteUnchecked_U8((u8)wParam, curAddress_); @@ -452,7 +452,7 @@ void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) { Reporting::NotifyDebugger(); if (active) - Core_EnableStepping(false); + Core_Resume(); } void CtrlMemView::redraw() { diff --git a/Windows/Debugger/DebuggerShared.h b/Windows/Debugger/DebuggerShared.h index 6490a52837e9..05daf87a6c52 100644 --- a/Windows/Debugger/DebuggerShared.h +++ b/Windows/Debugger/DebuggerShared.h @@ -9,7 +9,8 @@ enum { WM_DEB_GOTOWPARAM = WM_USER+2, WM_DEB_SETDEBUGLPARAM, WM_DEB_UPDATE, WM_DEB_SETSTATUSBARTEXT, - WM_DEB_GOTOHEXEDIT + WM_DEB_GOTOHEXEDIT, + WM_DEB_AFTERSTEP, }; bool executeExpressionWindow(HWND hwnd, DebugInterface* cpu, u32& dest); diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index d72e0a90b3b8..67870e90325d 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -92,7 +92,7 @@ static constexpr UINT UPDATE_DELAY = 1000 / 60; CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Dialog((LPCSTR)IDD_DISASM, _hInstance, _hParent) { cpu = _cpu; - lastTicks = PSP_IsInited() ? CoreTiming::GetTicks() : 0; + lastTicks_ = PSP_IsInited() ? CoreTiming::GetTicks() : 0; SetWindowText(m_hDlg, ConvertUTF8ToWString(_cpu->GetName()).c_str()); @@ -198,138 +198,20 @@ CDisasm::~CDisasm() delete moduleList; } -void CDisasm::stepInto() -{ +void CDisasm::step(CPUStepType stepType) { if (!PSP_IsInited() || !Core_IsStepping()) { return; } CtrlDisAsmView *ptr = DisAsmView(); - lastTicks = CoreTiming::GetTicks(); - u32 currentPc = cpu->GetPC(); - - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(currentMIPS->pc); - u32 newAddress = currentPc+ptr->getInstructionSizeAt(currentPc); - - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,currentPc); - if (info.isBranch) - { - ptr->scrollStepping(newAddress); - } else { - bool scroll = true; - if (currentMIPS->inDelaySlot) - { - MIPSAnalyst::MipsOpcodeInfo prevInfo = MIPSAnalyst::GetOpcodeInfo(cpu,currentPc-cpu->getInstructionSize(0)); - if (!prevInfo.isConditional || prevInfo.conditionMet) - scroll = false; - } - - if (scroll) - { - ptr->scrollStepping(newAddress); - } - } - - for (u32 i = 0; i < (newAddress-currentPc)/4; i++) - { - Core_DoSingleStep(); - Sleep(1); - } - - ptr->gotoPC(); - UpdateDialog(); - - threadList->reloadThreads(); - stackTraceView->loadStackTrace(); -} - -void CDisasm::stepOver() -{ - if (!PSP_IsInited() || Core_IsActive()) { - return; - } - - CtrlDisAsmView *ptr = DisAsmView(); - lastTicks = CoreTiming::GetTicks(); - - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(currentMIPS->pc); - u32 currentPc = cpu->GetPC(); - - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu,cpu->GetPC()); ptr->setDontRedraw(true); - u32 breakpointAddress = currentPc+ptr->getInstructionSizeAt(currentPc); - if (info.isBranch) - { - if (info.isConditional == false) - { - if (info.isLinkedBranch) // jal, jalr - { - // it's a function call with a delay slot - skip that too - breakpointAddress += cpu->getInstructionSize(0); - } else { // j, ... - // in case of absolute branches, set the breakpoint at the branch target - breakpointAddress = info.branchTarget; - } - } else { // beq, ... - if (info.conditionMet) - { - breakpointAddress = info.branchTarget; - } else { - breakpointAddress = currentPc+2*cpu->getInstructionSize(0); - ptr->scrollStepping(breakpointAddress); - } - } - } else { - ptr->scrollStepping(breakpointAddress); - } + lastTicks_ = CoreTiming::GetTicks(); - CBreakPoints::AddBreakPoint(breakpointAddress,true); - Core_EnableStepping(false); - Sleep(1); - ptr->gotoAddr(breakpointAddress); - UpdateDialog(); + u32 stepSize = ptr->getInstructionSizeAt(cpu->GetPC()); + Core_PerformStep(cpu, stepType, stepSize); } -void CDisasm::stepOut() { - auto memLock = Memory::Lock(); - if (!PSP_IsInited()) - return; - - auto threads = GetThreadsInfo(); - - u32 entry = cpu->GetPC(), stackTop = 0; - for (size_t i = 0; i < threads.size(); i++) - { - if (threads[i].isCurrent) - { - entry = threads[i].entrypoint; - stackTop = threads[i].initialStack; - break; - } - } - - auto frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop); - if (frames.size() < 2) return; - u32 breakpointAddress = frames[1].pc; - lastTicks = CoreTiming::GetTicks(); - - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(currentMIPS->pc); - - CtrlDisAsmView *ptr = DisAsmView(); - ptr->setDontRedraw(true); - - CBreakPoints::AddBreakPoint(breakpointAddress,true); - Core_EnableStepping(false); - Sleep(1); - ptr->gotoAddr(breakpointAddress); - UpdateDialog(); -} - -void CDisasm::runToLine() -{ +void CDisasm::runToLine() { if (!PSP_IsInited()) { return; } @@ -337,10 +219,10 @@ void CDisasm::runToLine() CtrlDisAsmView *ptr = DisAsmView(); u32 pos = ptr->getSelection(); - lastTicks = CoreTiming::GetTicks(); + lastTicks_ = CoreTiming::GetTicks(); ptr->setDontRedraw(true); CBreakPoints::AddBreakPoint(pos,true); - Core_EnableStepping(false); + Core_Resume(); } BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) @@ -418,7 +300,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) bool isRunning = Core_IsActive(); if (isRunning) { - Core_EnableStepping(true, "cpu.breakpoint.add", 0); + Core_Break("cpu.breakpoint.add", 0); Core_WaitInactive(200); } @@ -426,18 +308,18 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) if (bpw.exec()) bpw.addBreakpoint(); if (isRunning) - Core_EnableStepping(false); + Core_Resume(); view->UnlockPosition(); keepStatusBarText = false; } break; case ID_DEBUG_STEPOVER: - if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepOver(); + if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) step(CPUStepType::Over); break; case ID_DEBUG_STEPINTO: - if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepInto(); + if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) step(CPUStepType::Into); break; case ID_DEBUG_RUNTOLINE: @@ -445,7 +327,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) break; case ID_DEBUG_STEPOUT: - if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) stepOut(); + if (GetFocus() == GetDlgItem(m_hDlg,IDC_DISASMVIEW)) step(CPUStepType::Out); break; case ID_DEBUG_HIDEBOTTOMTABS: @@ -516,47 +398,43 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) if (!PSP_IsInited()) { break; } - if (!Core_IsStepping()) // stop - { + if (!Core_IsStepping()) { // stop ptr->setDontRedraw(false); - Core_EnableStepping(true, "ui.break", 0); - Sleep(1); //let cpu catch up - ptr->gotoPC(); - UpdateDialog(); + Core_Break("ui.break", 0); } else { // go - lastTicks = CoreTiming::GetTicks(); + lastTicks_ = CoreTiming::GetTicks(); // If the current PC is on a breakpoint, the user doesn't want to do nothing. CBreakPoints::SetSkipFirst(currentMIPS->pc); - Core_EnableStepping(false); + Core_Resume(); } } break; case IDC_STEP: - stepInto(); + step(CPUStepType::Into); break; case IDC_STEPOVER: - stepOver(); + step(CPUStepType::Over); break; case IDC_STEPOUT: - stepOut(); + step(CPUStepType::Out); break; case IDC_STEPHLE: { if (Core_IsActive()) break; - lastTicks = CoreTiming::GetTicks(); + lastTicks_ = CoreTiming::GetTicks(); // If the current PC is on a breakpoint, the user doesn't want to do nothing. CBreakPoints::SetSkipFirst(currentMIPS->pc); hleDebugBreak(); - Core_EnableStepping(false); + Core_Resume(); } break; @@ -624,6 +502,23 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) Update(); return TRUE; + case WM_DEB_AFTERSTEP: + { + CtrlDisAsmView *ptr = DisAsmView(); + // At this point, the step should be done, and the new address is just PC. + // Ideally, this part should be done as a reaction to an update message and not directly here. + // That way we could get rid of the sleep. + u32 oldAddress = ptr->getSelection(); + u32 newAddress = cpu->GetPC(); + if (newAddress > oldAddress && newAddress < oldAddress + 12) { + // Heuristic for when to scroll at the edge rather than jump the window. + ptr->scrollStepping(newAddress); + } + ptr->gotoAddr(newAddress); + Update(); + break; + } + case WM_DEB_TABPRESSED: bottomTabs->NextTab(true); SetFocus(bottomTabs->CurrentTabHandle()); @@ -904,7 +799,7 @@ void CDisasm::ProcessUpdateDialog() { // Update Debug Counter if (PSP_IsInited()) { wchar_t tempTicks[24]{}; - _snwprintf(tempTicks, 23, L"%lld", CoreTiming::GetTicks() - lastTicks); + _snwprintf(tempTicks, 23, L"%lld", CoreTiming::GetTicks() - lastTicks_); SetDlgItemText(m_hDlg, IDC_DEBUG_COUNT, tempTicks); } diff --git a/Windows/Debugger/Debugger_Disasm.h b/Windows/Debugger/Debugger_Disasm.h index 9ce5cbf6601b..c6e0212276ea 100644 --- a/Windows/Debugger/Debugger_Disasm.h +++ b/Windows/Debugger/Debugger_Disasm.h @@ -18,7 +18,7 @@ class CDisasm : public Dialog int minWidth; int minHeight; DebugInterface *cpu; - u64 lastTicks; + u64 lastTicks_; HWND statusBarWnd; CtrlBreakpointList* breakpointList; @@ -38,9 +38,7 @@ class CDisasm : public Dialog void UpdateSize(WORD width, WORD height); void SavePosition(); void updateThreadLabel(bool clear); - void stepInto(); - void stepOver(); - void stepOut(); + void step(CPUStepType stepType); void runToLine(); public: diff --git a/Windows/Debugger/DumpMemoryWindow.cpp b/Windows/Debugger/DumpMemoryWindow.cpp index fc438a54189e..c09a3ce53384 100644 --- a/Windows/Debugger/DumpMemoryWindow.cpp +++ b/Windows/Debugger/DumpMemoryWindow.cpp @@ -82,7 +82,7 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, bool priorDumpWasStepping = Core_IsStepping(); if (!priorDumpWasStepping && PSP_IsInited()) { // If emulator isn't paused force paused state, but wait before locking. - Core_EnableStepping(true, "memory.access", bp->start); + Core_Break("memory.access", bp->start); Core_WaitInactive(); } @@ -117,7 +117,7 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, fclose(output); if (!priorDumpWasStepping) { // If emulator wasn't paused before memory dump resume emulation automatically. - Core_EnableStepping(false); + Core_Resume(); } MessageBoxA(hwnd, "Done.", "Information", MB_OK); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index d560bd74da9c..8f51150e5cbf 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -285,7 +285,7 @@ void MainThreadFunc() { if (g_Config.bBrowse) PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0); - Core_EnableStepping(false); + Core_Resume(); if (useEmuThread) { while (emuThreadState != (int)EmuThreadState::DISABLED) { diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index ffffb557f4d0..9c974d35068b 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -841,10 +841,15 @@ namespace MainWindow } if (!noFocusPause && g_Config.bPauseOnLostFocus && GetUIState() == UISTATE_INGAME) { if (pause != Core_IsStepping()) { - if (disasmWindow) + if (disasmWindow) { SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); - else - Core_EnableStepping(pause, "ui.lost_focus", 0); + } else { + if (pause) { + Core_Break("ui.lost_focus", 0); + } else { + Core_Resume(); + } + } } } @@ -1018,7 +1023,7 @@ namespace MainWindow if (DragQueryFile(hdrop, 0, filename, ARRAY_SIZE(filename)) != 0) { const std::string utf8_filename = ReplaceAll(ConvertWStringToUTF8(filename), "\\", "/"); System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, utf8_filename); - Core_EnableStepping(false); + Core_Resume(); } } DragFinish(hdrop); diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index c3237c56da47..b2406f3a0706 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -330,7 +330,7 @@ namespace MainWindow { if (GetUIState() == UISTATE_INGAME) { browsePauseAfter = Core_IsStepping(); if (!browsePauseAfter) - Core_EnableStepping(true, "ui.boot", 0); + Core_Break("ui.boot", 0); } auto mm = GetI18NCategory(I18NCat::MAINMENU); @@ -349,7 +349,7 @@ namespace MainWindow { void BrowseAndBootDone(std::string filename) { if (GetUIState() == UISTATE_INGAME || GetUIState() == UISTATE_EXCEPTION || GetUIState() == UISTATE_PAUSEMENU) { - Core_EnableStepping(false); + Core_Resume(); } filename = ReplaceAll(filename, "\\", "/"); System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, filename); @@ -475,12 +475,12 @@ namespace MainWindow { if (disasmWindow) SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else - Core_EnableStepping(false); + Core_Resume(); } else { if (disasmWindow) SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else - Core_EnableStepping(true, "ui.break", 0); + Core_Break("ui.break", 0); } noFocusPause = !noFocusPause; // If we pause, override pause on lost focus break; @@ -491,7 +491,7 @@ namespace MainWindow { case ID_EMULATION_STOP: if (Core_IsStepping()) - Core_EnableStepping(false); + Core_Resume(); Core_Stop(); System_PostUIMessage(UIMessage::REQUEST_GAME_STOP); @@ -500,7 +500,7 @@ namespace MainWindow { case ID_EMULATION_RESET: System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); - Core_EnableStepping(false); + Core_Resume(); break; case ID_EMULATION_SWITCH_UMD: diff --git a/Windows/main.cpp b/Windows/main.cpp index 01f6b1dfb3da..03f90d34c915 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -436,6 +436,11 @@ void System_Notify(SystemNotification notification) { PostDialogMessage(disasmWindow, WM_DEB_UPDATE); break; + case SystemNotification::DISASSEMBLY_AFTERSTEP: + if (disasmWindow) + PostDialogMessage(disasmWindow, WM_DEB_AFTERSTEP); + break; + case SystemNotification::SYMBOL_MAP_UPDATED: if (g_symbolMap) g_symbolMap->SortSymbols(); // internal locking is performed here