Merge pull request #5180 from lioncash/enum

CPU: Convert state enum to an enum class
This commit is contained in:
Markus Wick 2017-04-02 17:01:25 +02:00 committed by GitHub
commit 40cfa708a8
17 changed files with 68 additions and 56 deletions

View File

@ -352,7 +352,7 @@ static void CpuThread()
if (!s_state_filename.empty()) if (!s_state_filename.empty())
{ {
// Needs to PauseAndLock the Core // Needs to PauseAndLock the Core
// NOTE: EmuThread should have left us in CPU_STEPPING so nothing will happen // NOTE: EmuThread should have left us in State::Stepping so nothing will happen
// until after the job is serviced. // until after the job is serviced.
QueueHostJob([] { QueueHostJob([] {
// Recheck in case Movie cleared it since. // Recheck in case Movie cleared it since.
@ -431,11 +431,11 @@ static void FifoPlayerThread()
// If we did not enter the CPU Run Loop above then run a fake one instead. // If we did not enter the CPU Run Loop above then run a fake one instead.
// We need to be IsRunningAndStarted() for DolphinWX to stop us. // We need to be IsRunningAndStarted() for DolphinWX to stop us.
if (CPU::GetState() != CPU::CPU_POWERDOWN) if (CPU::GetState() != CPU::State::PowerDown)
{ {
s_is_started = true; s_is_started = true;
Host_Message(WM_USER_STOP); Host_Message(WM_USER_STOP);
while (CPU::GetState() != CPU::CPU_POWERDOWN) while (CPU::GetState() != CPU::State::PowerDown)
{ {
if (!_CoreParameter.bCPUThread) if (!_CoreParameter.bCPUThread)
g_video_backend->PeekMessages(); g_video_backend->PeekMessages();
@ -594,7 +594,7 @@ void EmuThread()
// Spawn the CPU+GPU thread // Spawn the CPU+GPU thread
s_cpu_thread = std::thread(cpuThreadFunc); s_cpu_thread = std::thread(cpuThreadFunc);
while (CPU::GetState() != CPU::CPU_POWERDOWN) while (CPU::GetState() != CPU::State::PowerDown)
{ {
g_video_backend->PeekMessages(); g_video_backend->PeekMessages();
Common::SleepCurrentThread(20); Common::SleepCurrentThread(20);

View File

@ -99,19 +99,22 @@ public:
const char* GetName() override { return "FifoPlayer"; } const char* GetName() override { return "FifoPlayer"; }
void Run() override void Run() override
{ {
while (CPU::GetState() == CPU::CPU_RUNNING) while (CPU::GetState() == CPU::State::Running)
{ {
switch (m_parent->AdvanceFrame()) switch (m_parent->AdvanceFrame())
{ {
case CPU::CPU_POWERDOWN: case CPU::State::PowerDown:
CPU::Break(); CPU::Break();
Host_Message(WM_USER_STOP); Host_Message(WM_USER_STOP);
break; break;
case CPU::CPU_STEPPING: case CPU::State::Stepping:
CPU::Break(); CPU::Break();
Host_UpdateMainFrame(); Host_UpdateMainFrame();
break; break;
case CPU::State::Running:
break;
} }
} }
} }
@ -120,15 +123,15 @@ private:
FifoPlayer* m_parent; FifoPlayer* m_parent;
}; };
int FifoPlayer::AdvanceFrame() CPU::State FifoPlayer::AdvanceFrame()
{ {
if (m_CurrentFrame >= m_FrameRangeEnd) if (m_CurrentFrame >= m_FrameRangeEnd)
{ {
if (!m_Loop) if (!m_Loop)
return CPU::CPU_POWERDOWN; return CPU::State::PowerDown;
// If there are zero frames in the range then sleep instead of busy spinning // If there are zero frames in the range then sleep instead of busy spinning
if (m_FrameRangeStart >= m_FrameRangeEnd) if (m_FrameRangeStart >= m_FrameRangeEnd)
return CPU::CPU_STEPPING; return CPU::State::Stepping;
// When looping, reload the contents of all the BP/CP/CF registers. // When looping, reload the contents of all the BP/CP/CF registers.
// This ensures that each time the first frame is played back, the state of the // This ensures that each time the first frame is played back, the state of the
@ -148,7 +151,7 @@ int FifoPlayer::AdvanceFrame()
WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]); WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]);
++m_CurrentFrame; ++m_CurrentFrame;
return CPU::CPU_RUNNING; return CPU::State::Running;
} }
std::unique_ptr<CPUCoreBase> FifoPlayer::GetCPUCore() std::unique_ptr<CPUCoreBase> FifoPlayer::GetCPUCore()

View File

@ -16,6 +16,11 @@ class FifoDataFile;
struct MemoryUpdate; struct MemoryUpdate;
struct AnalyzedFrameInfo; struct AnalyzedFrameInfo;
namespace CPU
{
enum class State;
}
// Story time: // Story time:
// When FifoRecorder was created, efb copies weren't really used or they used efb2tex which ignored // When FifoRecorder was created, efb copies weren't really used or they used efb2tex which ignored
// the underlying memory, so FifoRecorder didn't do anything special about the memory backing efb // the underlying memory, so FifoRecorder didn't do anything special about the memory backing efb
@ -94,7 +99,7 @@ private:
FifoPlayer(); FifoPlayer();
int AdvanceFrame(); CPU::State AdvanceFrame();
void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info); void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info);
void WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame, void WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame,

View File

@ -22,7 +22,7 @@ namespace CPU
// CPU Thread execution state. // CPU Thread execution state.
// Requires s_state_change_lock to modify the value. // Requires s_state_change_lock to modify the value.
// Read access is unsynchronized. // Read access is unsynchronized.
static State s_state = CPU_POWERDOWN; static State s_state = State::PowerDown;
// Synchronizes EnableStepping and PauseAndLock so only one instance can be // Synchronizes EnableStepping and PauseAndLock so only one instance can be
// active at a time. Simplifies code by eliminating several edge cases where // active at a time. Simplifies code by eliminating several edge cases where
@ -50,7 +50,7 @@ static Common::Event* s_state_cpu_step_instruction_sync = nullptr;
void Init(int cpu_core) void Init(int cpu_core)
{ {
PowerPC::Init(cpu_core); PowerPC::Init(cpu_core);
s_state = CPU_STEPPING; s_state = State::Stepping;
} }
void Shutdown() void Shutdown()
@ -73,13 +73,13 @@ static void FlushStepSyncEventLocked()
void Run() void Run()
{ {
std::unique_lock<std::mutex> state_lock(s_state_change_lock); std::unique_lock<std::mutex> state_lock(s_state_change_lock);
while (s_state != CPU_POWERDOWN) while (s_state != State::PowerDown)
{ {
s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; }); s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; });
switch (s_state) switch (s_state)
{ {
case CPU_RUNNING: case State::Running:
s_state_cpu_thread_active = true; s_state_cpu_thread_active = true;
state_lock.unlock(); state_lock.unlock();
@ -91,12 +91,12 @@ void Run()
{ {
if (PowerPC::breakpoints.IsAddressBreakPoint(PC) || PowerPC::memchecks.HasAny()) if (PowerPC::breakpoints.IsAddressBreakPoint(PC) || PowerPC::memchecks.HasAny())
{ {
s_state = CPU_STEPPING; s_state = State::Stepping;
PowerPC::CoreMode old_mode = PowerPC::GetMode(); PowerPC::CoreMode old_mode = PowerPC::GetMode();
PowerPC::SetMode(PowerPC::CoreMode::Interpreter); PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
PowerPC::SingleStep(); PowerPC::SingleStep();
PowerPC::SetMode(old_mode); PowerPC::SetMode(old_mode);
s_state = CPU_RUNNING; s_state = State::Running;
} }
} }
@ -108,11 +108,11 @@ void Run()
s_state_cpu_idle_cvar.notify_all(); s_state_cpu_idle_cvar.notify_all();
break; break;
case CPU_STEPPING: case State::Stepping:
// Wait for step command. // Wait for step command.
s_state_cpu_cvar.wait(state_lock, s_state_cpu_cvar.wait(state_lock,
[] { return s_state_cpu_step_instruction || s_state != CPU_STEPPING; }); [] { return s_state_cpu_step_instruction || !IsStepping(); });
if (s_state != CPU_STEPPING) if (!IsStepping())
{ {
// Signal event if the mode changes. // Signal event if the mode changes.
FlushStepSyncEventLocked(); FlushStepSyncEventLocked();
@ -136,7 +136,7 @@ void Run()
Host_UpdateDisasmDialog(); Host_UpdateDisasmDialog();
break; break;
case CPU_POWERDOWN: case State::PowerDown:
break; break;
} }
} }
@ -155,10 +155,10 @@ static void RunAdjacentSystems(bool running)
void Stop() void Stop()
{ {
// Change state and wait for it to be acknowledged. // Change state and wait for it to be acknowledged.
// We don't need the stepping lock because CPU_POWERDOWN is a priority state which // We don't need the stepping lock because State::PowerDown is a priority state which
// will stick permanently. // will stick permanently.
std::unique_lock<std::mutex> state_lock(s_state_change_lock); std::unique_lock<std::mutex> state_lock(s_state_change_lock);
s_state = CPU_POWERDOWN; s_state = State::PowerDown;
s_state_cpu_cvar.notify_one(); s_state_cpu_cvar.notify_one();
while (s_state_cpu_thread_active) while (s_state_cpu_thread_active)
@ -175,7 +175,7 @@ void Stop()
bool IsStepping() bool IsStepping()
{ {
return s_state == CPU_STEPPING; return s_state == State::Stepping;
} }
State GetState() State GetState()
@ -215,7 +215,7 @@ void StepOpcode(Common::Event* event)
// Requires s_state_change_lock // Requires s_state_change_lock
static bool SetStateLocked(State s) static bool SetStateLocked(State s)
{ {
if (s_state == CPU_POWERDOWN) if (s_state == State::PowerDown)
return false; return false;
s_state = s; s_state = s;
return true; return true;
@ -228,7 +228,7 @@ void EnableStepping(bool stepping)
if (stepping) if (stepping)
{ {
SetStateLocked(CPU_STEPPING); SetStateLocked(State::Stepping);
while (s_state_cpu_thread_active) while (s_state_cpu_thread_active)
{ {
@ -240,7 +240,7 @@ void EnableStepping(bool stepping)
RunAdjacentSystems(false); RunAdjacentSystems(false);
} }
else if (SetStateLocked(CPU_RUNNING)) else if (SetStateLocked(State::Running))
{ {
s_state_cpu_cvar.notify_one(); s_state_cpu_cvar.notify_one();
RunAdjacentSystems(true); RunAdjacentSystems(true);
@ -261,7 +261,7 @@ void Break()
// We'll deadlock if we synchronize, the CPU may block waiting for our caller to // We'll deadlock if we synchronize, the CPU may block waiting for our caller to
// finish resulting in the CPU loop never terminating. // finish resulting in the CPU loop never terminating.
SetStateLocked(CPU_STEPPING); SetStateLocked(State::Stepping);
RunAdjacentSystems(false); RunAdjacentSystems(false);
} }
@ -278,8 +278,8 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent)
std::unique_lock<std::mutex> state_lock(s_state_change_lock); std::unique_lock<std::mutex> state_lock(s_state_change_lock);
s_state_paused_and_locked = true; s_state_paused_and_locked = true;
was_unpaused = s_state == CPU_RUNNING; was_unpaused = s_state == State::Running;
SetStateLocked(CPU_STEPPING); SetStateLocked(State::Stepping);
while (s_state_cpu_thread_active) while (s_state_cpu_thread_active)
{ {
@ -316,7 +316,7 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent)
{ {
s_state_system_request_stepping = false; s_state_system_request_stepping = false;
} }
else if (unpause_on_unlock && SetStateLocked(CPU_RUNNING)) else if (unpause_on_unlock && SetStateLocked(State::Running))
{ {
was_unpaused = true; was_unpaused = true;
} }
@ -324,7 +324,7 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent)
s_state_cpu_cvar.notify_one(); s_state_cpu_cvar.notify_one();
if (control_adjacent) if (control_adjacent)
RunAdjacentSystems(s_state == CPU_RUNNING); RunAdjacentSystems(s_state == State::Running);
} }
s_stepping_lock.unlock(); s_stepping_lock.unlock();
} }

View File

@ -11,11 +11,11 @@ class Event;
namespace CPU namespace CPU
{ {
enum State enum class State
{ {
CPU_RUNNING = 0, Running = 0,
CPU_STEPPING = 2, Stepping = 2,
CPU_POWERDOWN = 3 PowerDown = 3
}; };
// Init // Init
@ -29,7 +29,7 @@ void Shutdown();
void Run(); void Run();
// Causes shutdown // Causes shutdown
// Once started, CPU_POWERDOWN cannot be stopped. // Once started, State::PowerDown cannot be stopped.
// Synchronizes with the CPU Thread (waits for CPU::Run to exit). // Synchronizes with the CPU Thread (waits for CPU::Run to exit).
void Stop(); void Stop();
@ -48,8 +48,8 @@ void EnableStepping(bool stepping);
// should not be used by the Host. // should not be used by the Host.
void Break(); void Break();
// Shorthand for GetState() == CPU_STEPPING. // Shorthand for GetState() == State::Stepping.
// WARNING: CPU_POWERDOWN will return false, not just CPU_RUNNING. // WARNING: State::PowerDown will return false, not just State::Running.
bool IsStepping(); bool IsStepping();
// Get current CPU Thread State // Get current CPU Thread State
@ -63,8 +63,8 @@ const volatile State* GetStatePtr();
// While this lock is held, the CPU Thread will not perform any action so it is safe to access // While this lock is held, the CPU Thread will not perform any action so it is safe to access
// PowerPC::ppcState, CoreTiming, etc. without racing the CPU Thread. // PowerPC::ppcState, CoreTiming, etc. without racing the CPU Thread.
// Cannot be used recursively. Must be paired as PauseAndLock(true)/PauseAndLock(false). // Cannot be used recursively. Must be paired as PauseAndLock(true)/PauseAndLock(false).
// Return value for do_lock == true is whether the state was CPU_RUNNING or not. // Return value for do_lock == true is whether the state was State::Running or not.
// Return value for do_lock == false is whether the state was changed *to* CPU_RUNNING or not. // Return value for do_lock == false is whether the state was changed *to* State::Running or not.
// Cannot be used by System threads as it will deadlock. It is threadsafe otherwise. // Cannot be used by System threads as it will deadlock. It is threadsafe otherwise.
// "control_adjacent" causes PauseAndLock to behave like EnableStepping by modifying the // "control_adjacent" causes PauseAndLock to behave like EnableStepping by modifying the
// state of the Audio and FIFO subsystems as well. // state of the Audio and FIFO subsystems as well.

View File

@ -1344,7 +1344,7 @@ void EndPlayInput(bool cont)
} }
else if (s_playMode != MODE_NONE) else if (s_playMode != MODE_NONE)
{ {
// We can be called by EmuThread during boot (CPU_POWERDOWN) // We can be called by EmuThread during boot (CPU::State::PowerDown)
bool was_running = Core::IsRunningAndStarted() && !CPU::IsStepping(); bool was_running = Core::IsRunningAndStarted() && !CPU::IsStepping();
if (was_running) if (was_running)
CPU::Break(); CPU::Break();

View File

@ -107,7 +107,7 @@ void CachedInterpreter::ExecuteOneBlock()
void CachedInterpreter::Run() void CachedInterpreter::Run()
{ {
while (CPU::GetState() == CPU::CPU_RUNNING) while (CPU::GetState() == CPU::State::Running)
{ {
// Start new timing slice // Start new timing slice
// NOTE: Exceptions may change PC // NOTE: Exceptions may change PC

View File

@ -224,7 +224,7 @@ int ShowSteps = 300;
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared). // FastRun - inspired by GCemu (to imitate the JIT so that they can be compared).
void Interpreter::Run() void Interpreter::Run()
{ {
while (!CPU::GetState()) while (CPU::GetState() == CPU::State::Running)
{ {
// CoreTiming Advance() ends the previous slice and declares the start of the next // CoreTiming Advance() ends the previous slice and declares the start of the next
// one so it must always be called at the start. At boot, we are in slice -1 and must // one so it must always be called at the start. At boot, we are in slice -1 and must

View File

@ -583,7 +583,7 @@ void Jit64::Jit(u32 em_address)
// Comment out the following to disable breakpoints (speed-up) // Comment out the following to disable breakpoints (speed-up)
if (!Profiler::g_ProfileBlocks) if (!Profiler::g_ProfileBlocks)
{ {
if (CPU::GetState() == CPU::CPU_STEPPING) if (CPU::IsStepping())
{ {
blockSize = 1; blockSize = 1;
@ -841,7 +841,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBloc
} }
if (SConfig::GetInstance().bEnableDebugging && if (SConfig::GetInstance().bEnableDebugging &&
breakpoints.IsAddressBreakPoint(ops[i].address) && CPU::GetState() != CPU::CPU_STEPPING) breakpoints.IsAddressBreakPoint(ops[i].address) && !CPU::IsStepping())
{ {
// Turn off block linking if there are breakpoints so that the Step Over command does not // Turn off block linking if there are breakpoints so that the Step Over command does not
// link this block. // link this block.

View File

@ -84,7 +84,7 @@ void Jit64AsmRoutineManager::Generate()
if (SConfig::GetInstance().bEnableDebugging) if (SConfig::GetInstance().bEnableDebugging)
{ {
TEST(32, M(CPU::GetStatePtr()), Imm32(CPU::CPU_STEPPING)); TEST(32, M(CPU::GetStatePtr()), Imm32(static_cast<u32>(CPU::State::Stepping)));
FixupBranch notStepping = J_CC(CC_Z); FixupBranch notStepping = J_CC(CC_Z);
ABI_PushRegistersAndAdjustStack({}, 0); ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunction(PowerPC::CheckBreakPoints); ABI_CallFunction(PowerPC::CheckBreakPoints);

View File

@ -119,7 +119,7 @@ void Jit64::lXXx(UGeckoInstruction inst)
signExtend = true; signExtend = true;
} }
if (CPU::GetState() != CPU::CPU_STEPPING && inst.OPCD == 32 && CanMergeNextInstructions(2) && if (!CPU::IsStepping() && inst.OPCD == 32 && CanMergeNextInstructions(2) &&
(inst.hex & 0xFFFF0000) == 0x800D0000 && (inst.hex & 0xFFFF0000) == 0x800D0000 &&
(js.op[1].inst.hex == 0x28000000 || (js.op[1].inst.hex == 0x28000000 ||
(SConfig::GetInstance().bWii && js.op[1].inst.hex == 0x2C000000)) && (SConfig::GetInstance().bWii && js.op[1].inst.hex == 0x2C000000)) &&

View File

@ -487,7 +487,7 @@ void JitIL::Jit(u32 em_address)
// Comment out the following to disable breakpoints (speed-up) // Comment out the following to disable breakpoints (speed-up)
if (!Profiler::g_ProfileBlocks) if (!Profiler::g_ProfileBlocks)
{ {
if (CPU::GetState() == CPU::CPU_STEPPING) if (CPU::IsStepping())
{ {
blockSize = 1; blockSize = 1;
@ -638,7 +638,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBloc
} }
if (SConfig::GetInstance().bEnableDebugging && if (SConfig::GetInstance().bEnableDebugging &&
breakpoints.IsAddressBreakPoint(ops[i].address) && CPU::GetState() != CPU::CPU_STEPPING) breakpoints.IsAddressBreakPoint(ops[i].address) && !CPU::IsStepping())
{ {
// Turn off block linking if there are breakpoints so that the Step Over command does not // Turn off block linking if there are breakpoints so that the Step Over command does not
// link this block. // link this block.

View File

@ -66,7 +66,7 @@ void JitArm64::GenerateAsm()
// } while (PowerPC::ppcState.downcount > 0); // } while (PowerPC::ppcState.downcount > 0);
// doTiming: // doTiming:
// NPC = PC = DISPATCHER_PC; // NPC = PC = DISPATCHER_PC;
// } while (CPU::GetState() == CPU::CPU_RUNNING); // } while (CPU::GetState() == CPU::State::Running);
AlignCodePage(); AlignCodePage();
dispatcher = GetCodePtr(); dispatcher = GetCodePtr();
WARN_LOG(DYNA_REC, "Dispatcher is %p", dispatcher); WARN_LOG(DYNA_REC, "Dispatcher is %p", dispatcher);

View File

@ -29,7 +29,7 @@ JitBase::~JitBase() = default;
bool JitBase::CanMergeNextInstructions(int count) const bool JitBase::CanMergeNextInstructions(int count) const
{ {
if (CPU::GetState() == CPU::CPU_STEPPING || js.instructionsLeft < count) if (CPU::IsStepping() || js.instructionsLeft < count)
return false; return false;
// Be careful: a breakpoint kills flags in between instructions // Be careful: a breakpoint kills flags in between instructions
for (int i = 1; i <= count; i++) for (int i = 1; i <= count; i++)

View File

@ -57,7 +57,7 @@ void JitILBase::lXz(UGeckoInstruction inst)
// TODO: This really should be done somewhere else. Either lower in the IR // TODO: This really should be done somewhere else. Either lower in the IR
// or higher in PPCAnalyst // or higher in PPCAnalyst
// TODO: We shouldn't use debug reads here. // TODO: We shouldn't use debug reads here.
if (CPU::GetState() != CPU::CPU_STEPPING && inst.OPCD == 32 && // Lwx if (!CPU::IsStepping() && inst.OPCD == 32 && // Lwx
(inst.hex & 0xFFFF0000) == 0x800D0000 && (inst.hex & 0xFFFF0000) == 0x800D0000 &&
(PowerPC::HostRead_U32(js.compilerPC + 4) == 0x28000000 || (PowerPC::HostRead_U32(js.compilerPC + 4) == 0x28000000 ||
(SConfig::GetInstance().bWii && PowerPC::HostRead_U32(js.compilerPC + 4) == 0x2C000000)) && (SConfig::GetInstance().bWii && PowerPC::HostRead_U32(js.compilerPC + 4) == 0x2C000000)) &&

View File

@ -210,11 +210,15 @@ void Reset()
void ScheduleInvalidateCacheThreadSafe(u32 address) void ScheduleInvalidateCacheThreadSafe(u32 address)
{ {
if (CPU::GetState() == CPU::State::CPU_RUNNING) if (CPU::GetState() == CPU::State::Running)
{
CoreTiming::ScheduleEvent(0, s_invalidate_cache_thread_safe, address, CoreTiming::ScheduleEvent(0, s_invalidate_cache_thread_safe, address,
CoreTiming::FromThread::NON_CPU); CoreTiming::FromThread::NON_CPU);
}
else else
{
PowerPC::ppcState.iCache.Invalidate(static_cast<u32>(address)); PowerPC::ppcState.iCache.Invalidate(static_cast<u32>(address));
}
} }
void Shutdown() void Shutdown()

View File

@ -152,7 +152,7 @@ const char* GetCPUName();
// Init() will be called when added and Shutdown() when removed. // Init() will be called when added and Shutdown() when removed.
// [Threadsafety: Same as SetMode(), except it cannot be called from inside the CPU // [Threadsafety: Same as SetMode(), except it cannot be called from inside the CPU
// run loop on the CPU Thread - it doesn't make sense for a CPU to remove itself // run loop on the CPU Thread - it doesn't make sense for a CPU to remove itself
// while it is CPU_RUNNING] // while it is in State::Running]
void InjectExternalCPUCore(CPUCoreBase* core); void InjectExternalCPUCore(CPUCoreBase* core);
// Stepping requires the CPU Execution lock (CPU::PauseAndLock or CPU Thread) // Stepping requires the CPU Execution lock (CPU::PauseAndLock or CPU Thread)