diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index a95655eda9..a5af1d2867 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -298,7 +298,7 @@ void Stop() // - Hammertime! // Stop the CPU INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU")); - CPU::Stop(); + system.GetCPU().Stop(); if (system.IsDualCoreMode()) { @@ -410,7 +410,8 @@ static void CpuThread(const std::optional& savestate_path, bool del } // Enter CPU run loop. When we leave it - we are done. - CPU::Run(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Run(); #ifdef USE_MEMORYWATCHER s_memory_watcher.reset(); @@ -446,7 +447,8 @@ static void FifoPlayerThread(const std::optional& savestate_path, s_is_started = true; CPUSetInitialExecutionState(); - CPU::Run(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Run(); s_is_started = false; PowerPC::InjectExternalCPUCore(nullptr); @@ -583,7 +585,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi s_is_booting.Clear(); // Set execution state to known values (CPU/FIFO/Audio Paused) - CPU::Break(); + system.GetCPU().Break(); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::CoreMode::Interpreter); @@ -674,18 +676,19 @@ void SetState(State state) if (!IsRunningAndStarted()) return; + auto& system = Core::System::GetInstance(); switch (state) { case State::Paused: // NOTE: GetState() will return State::Paused immediately, even before anything has // stopped (including the CPU). - CPU::EnableStepping(true); // Break + system.GetCPU().EnableStepping(true); // Break Wiimote::Pause(); ResetRumble(); break; case State::Running: { - CPU::EnableStepping(false); + system.GetCPU().EnableStepping(false); Wiimote::Resume(); break; } @@ -704,7 +707,8 @@ State GetState() if (s_hardware_initialized) { - if (CPU::IsStepping() || s_frame_step) + auto& system = Core::System::GetInstance(); + if (system.GetCPU().IsStepping() || s_frame_step) return State::Paused; return State::Running; @@ -763,7 +767,7 @@ void SaveScreenShot(std::string_view name) }); } -static bool PauseAndLock(bool do_lock, bool unpause_on_unlock) +static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock) { // WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread if (!IsRunningAndStarted()) @@ -775,7 +779,7 @@ static bool PauseAndLock(bool do_lock, bool unpause_on_unlock) // first pause the CPU // This acquires a wrapper mutex and converts the current thread into // a temporary replacement CPU Thread. - was_unpaused = CPU::PauseAndLock(true); + was_unpaused = system.GetCPU().PauseAndLock(true); } ExpansionInterface::PauseAndLock(do_lock, false); @@ -785,7 +789,6 @@ static bool PauseAndLock(bool do_lock, bool unpause_on_unlock) // video has to come after CPU, because CPU thread can wait for video thread // (s_efbAccessRequested). - auto& system = Core::System::GetInstance(); system.GetFifo().PauseAndLock(system, do_lock, false); ResetRumble(); @@ -798,7 +801,7 @@ static bool PauseAndLock(bool do_lock, bool unpause_on_unlock) // mechanism to unpause them. If we unpaused the systems above when releasing // the locks then they could call CPU::Break which would require detecting it // and re-pausing with CPU::EnableStepping. - was_unpaused = CPU::PauseAndLock(false, unpause_on_unlock, true); + was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true); } return was_unpaused; @@ -806,15 +809,16 @@ static bool PauseAndLock(bool do_lock, bool unpause_on_unlock) void RunAsCPUThread(std::function function) { + auto& system = Core::System::GetInstance(); const bool is_cpu_thread = IsCPUThread(); bool was_unpaused = false; if (!is_cpu_thread) - was_unpaused = PauseAndLock(true, true); + was_unpaused = PauseAndLock(system, true, true); function(); if (!is_cpu_thread) - PauseAndLock(false, was_unpaused); + PauseAndLock(system, false, was_unpaused); } void RunOnCPUThread(std::function function, bool wait_for_completion) @@ -826,26 +830,28 @@ void RunOnCPUThread(std::function function, bool wait_for_completion) return; } + auto& system = Core::System::GetInstance(); + // Pause the CPU (set it to stepping mode). - const bool was_running = PauseAndLock(true, true); + const bool was_running = PauseAndLock(system, true, true); // Queue the job function. if (wait_for_completion) { // Trigger the event after executing the function. s_cpu_thread_job_finished.Reset(); - CPU::AddCPUThreadJob([&function]() { + system.GetCPU().AddCPUThreadJob([&function]() { function(); s_cpu_thread_job_finished.Set(); }); } else { - CPU::AddCPUThreadJob(std::move(function)); + system.GetCPU().AddCPUThreadJob(std::move(function)); } // Release the CPU thread, and let it execute the callback. - PauseAndLock(false, was_running); + PauseAndLock(system, false, was_running); // If we're waiting for completion, block until the event fires. if (wait_for_completion) @@ -869,7 +875,7 @@ void Callback_FramePresented(double actual_emulation_speed) } // Called from VideoInterface::Update (CPU thread) at emulated field boundaries -void Callback_NewField() +void Callback_NewField(Core::System& system) { if (s_frame_step) { @@ -883,7 +889,7 @@ void Callback_NewField() if (s_stop_frame_step.load()) { s_frame_step = false; - CPU::Break(); + system.GetCPU().Break(); CallOnStateChangedCallbacks(Core::GetState()); } } @@ -1055,13 +1061,13 @@ void UpdateInputGate(bool require_focus, bool require_full_focus) CPUThreadGuard::CPUThreadGuard() : m_was_cpu_thread(IsCPUThread()) { if (!m_was_cpu_thread) - m_was_unpaused = PauseAndLock(true, true); + m_was_unpaused = PauseAndLock(Core::System::GetInstance(), true, true); } CPUThreadGuard::~CPUThreadGuard() { if (!m_was_cpu_thread) - PauseAndLock(false, m_was_unpaused); + PauseAndLock(Core::System::GetInstance(), false, m_was_unpaused); } } // namespace Core diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 26abd24d97..2308848750 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -21,6 +21,8 @@ struct WindowSystemInfo; namespace Core { +class System; + bool GetIsThrottlerTempDisabled(); void SetIsThrottlerTempDisabled(bool disable); @@ -28,7 +30,7 @@ void SetIsThrottlerTempDisabled(bool disable); double GetActualEmulationSpeed(); void Callback_FramePresented(double actual_emulation_speed = 1.0); -void Callback_NewField(); +void Callback_NewField(Core::System& system); enum class State { diff --git a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp index 96a8ca9a4c..648ff82901 100644 --- a/Source/Core/Core/FifoPlayer/FifoPlayer.cpp +++ b/Source/Core/Core/FifoPlayer/FifoPlayer.cpp @@ -232,7 +232,8 @@ public: IsPlayingBackFifologWithBrokenEFBCopies = m_parent->m_File->HasBrokenEFBCopies(); // Without this call, we deadlock in initialization in dual core, as the FIFO is disabled and // thus ClearEfb()'s call to WaitForGPUInactive() never returns - CPU::EnableStepping(false); + auto& system = Core::System::GetInstance(); + system.GetCPU().EnableStepping(false); m_parent->m_CurrentFrame = m_parent->m_FrameRangeStart; m_parent->LoadMemory(); @@ -254,17 +255,19 @@ public: const char* GetName() const override { return "FifoPlayer"; } void Run() override { - while (CPU::GetState() == CPU::State::Running) + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + while (cpu.GetState() == CPU::State::Running) { switch (m_parent->AdvanceFrame()) { case CPU::State::PowerDown: - CPU::Break(); + cpu.Break(); Host_Message(HostMessageID::WMUserStop); break; case CPU::State::Stepping: - CPU::Break(); + cpu.Break(); Host_UpdateMainFrame(); break; @@ -519,6 +522,7 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end) u32 lastBurstEnd = end - 1; auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); auto& core_timing = system.GetCoreTiming(); auto& gpfifo = system.GetGPFifo(); auto& ppc_state = system.GetPPCState(); @@ -528,7 +532,7 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end) { while (IsHighWatermarkSet()) { - if (CPU::GetState() != CPU::State::Running) + if (cpu.GetState() != CPU::State::Running) break; core_timing.Idle(); core_timing.Advance(); @@ -733,10 +737,12 @@ void FifoPlayer::FlushWGP() void FifoPlayer::WaitForGPUInactive() { - auto& core_timing = Core::System::GetInstance().GetCoreTiming(); + auto& system = Core::System::GetInstance(); + auto& core_timing = system.GetCoreTiming(); + auto& cpu = system.GetCPU(); // Sleep while the GPU is active - while (!IsIdleSet() && CPU::GetState() != CPU::State::PowerDown) + while (!IsIdleSet() && cpu.GetState() != CPU::State::PowerDown) { core_timing.Idle(); core_timing.Advance(); diff --git a/Source/Core/Core/HLE/HLE_Misc.cpp b/Source/Core/Core/HLE/HLE_Misc.cpp index 8ae8c0f747..bcc1bec512 100644 --- a/Source/Core/Core/HLE/HLE_Misc.cpp +++ b/Source/Core/Core/HLE/HLE_Misc.cpp @@ -26,7 +26,8 @@ void UnimplementedFunction(const Core::CPUThreadGuard&) void HBReload(const Core::CPUThreadGuard&) { // There isn't much we can do. Just stop cleanly. - CPU::Break(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Break(); Host_Message(HostMessageID::WMUserStop); } diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index 3650f023f4..14031ef5d0 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -19,74 +19,48 @@ namespace CPU { -// CPU Thread execution state. -// Requires s_state_change_lock to modify the value. -// Read access is unsynchronized. -static State s_state = State::PowerDown; +CPUManager::CPUManager() = default; +CPUManager::~CPUManager() = default; -// Synchronizes EnableStepping and PauseAndLock so only one instance can be -// active at a time. Simplifies code by eliminating several edge cases where -// the EnableStepping(true)/PauseAndLock(true) case must release the state lock -// and wait for the CPU Thread which would otherwise require additional flags. -// NOTE: When using the stepping lock, it must always be acquired first. If -// the lock is acquired after the state lock then that is guaranteed to -// deadlock because of the order inversion. (A -> X,Y; B -> Y,X; A waits for -// B, B waits for A) -static std::mutex s_stepping_lock; - -// Primary lock. Protects changing s_state, requesting instruction stepping and -// pause-and-locking. -static std::mutex s_state_change_lock; -// When s_state_cpu_thread_active changes to false -static std::condition_variable s_state_cpu_idle_cvar; -// When s_state changes / s_state_paused_and_locked becomes false (for CPU Thread only) -static std::condition_variable s_state_cpu_cvar; -static bool s_state_cpu_thread_active = false; -static bool s_state_paused_and_locked = false; -static bool s_state_system_request_stepping = false; -static bool s_state_cpu_step_instruction = false; -static Common::Event* s_state_cpu_step_instruction_sync = nullptr; -static std::queue> s_pending_jobs; - -void Init(PowerPC::CPUCore cpu_core) +void CPUManager::Init(PowerPC::CPUCore cpu_core) { PowerPC::Init(cpu_core); - s_state = State::Stepping; + m_state = State::Stepping; } -void Shutdown() +void CPUManager::Shutdown() { Stop(); PowerPC::Shutdown(); } -// Requires holding s_state_change_lock -static void FlushStepSyncEventLocked() +// Requires holding m_state_change_lock +void CPUManager::FlushStepSyncEventLocked() { - if (!s_state_cpu_step_instruction) + if (!m_state_cpu_step_instruction) return; - if (s_state_cpu_step_instruction_sync) + if (m_state_cpu_step_instruction_sync) { - s_state_cpu_step_instruction_sync->Set(); - s_state_cpu_step_instruction_sync = nullptr; + m_state_cpu_step_instruction_sync->Set(); + m_state_cpu_step_instruction_sync = nullptr; } - s_state_cpu_step_instruction = false; + m_state_cpu_step_instruction = false; } -static void ExecutePendingJobs(std::unique_lock& state_lock) +void CPUManager::ExecutePendingJobs(std::unique_lock& state_lock) { - while (!s_pending_jobs.empty()) + while (!m_pending_jobs.empty()) { - auto callback = s_pending_jobs.front(); - s_pending_jobs.pop(); + auto callback = m_pending_jobs.front(); + m_pending_jobs.pop(); state_lock.unlock(); callback(); state_lock.lock(); } } -void Run() +void CPUManager::Run() { auto& system = Core::System::GetInstance(); @@ -94,17 +68,17 @@ void Run() // We can't rely on PowerPC::Init doing it, since it's called from EmuThread. PowerPC::RoundingModeUpdated(); - std::unique_lock state_lock(s_state_change_lock); - while (s_state != State::PowerDown) + std::unique_lock state_lock(m_state_change_lock); + while (m_state != State::PowerDown) { - s_state_cpu_cvar.wait(state_lock, [] { return !s_state_paused_and_locked; }); + m_state_cpu_cvar.wait(state_lock, [this] { return !m_state_paused_and_locked; }); ExecutePendingJobs(state_lock); Common::Event gdb_step_sync_event; - switch (s_state) + switch (m_state) { case State::Running: - s_state_cpu_thread_active = true; + m_state_cpu_thread_active = true; state_lock.unlock(); // Adjust PC for JIT when debugging @@ -116,12 +90,12 @@ void Run() if (PowerPC::breakpoints.IsAddressBreakPoint(system.GetPPCState().pc) || PowerPC::memchecks.HasAny()) { - s_state = State::Stepping; + m_state = State::Stepping; PowerPC::CoreMode old_mode = PowerPC::GetMode(); PowerPC::SetMode(PowerPC::CoreMode::Interpreter); PowerPC::SingleStep(); PowerPC::SetMode(old_mode); - s_state = State::Running; + m_state = State::Running; } } @@ -129,13 +103,13 @@ void Run() PowerPC::RunLoop(); state_lock.lock(); - s_state_cpu_thread_active = false; - s_state_cpu_idle_cvar.notify_all(); + m_state_cpu_thread_active = false; + m_state_cpu_idle_cvar.notify_all(); break; case State::Stepping: // Wait for step command. - s_state_cpu_cvar.wait(state_lock, [&state_lock, &gdb_step_sync_event] { + m_state_cpu_cvar.wait(state_lock, [this, &state_lock, &gdb_step_sync_event] { ExecutePendingJobs(state_lock); state_lock.unlock(); if (GDBStub::IsActive() && GDBStub::HasControl()) @@ -147,16 +121,18 @@ void Run() if (GDBStub::HasControl()) { // Make sure the previous step by gdb was serviced - if (s_state_cpu_step_instruction_sync && - s_state_cpu_step_instruction_sync != &gdb_step_sync_event) - s_state_cpu_step_instruction_sync->Set(); + if (m_state_cpu_step_instruction_sync && + m_state_cpu_step_instruction_sync != &gdb_step_sync_event) + { + m_state_cpu_step_instruction_sync->Set(); + } - s_state_cpu_step_instruction = true; - s_state_cpu_step_instruction_sync = &gdb_step_sync_event; + m_state_cpu_step_instruction = true; + m_state_cpu_step_instruction_sync = &gdb_step_sync_event; } } state_lock.lock(); - return s_state_cpu_step_instruction || !IsStepping(); + return m_state_cpu_step_instruction || !IsStepping(); }); if (!IsStepping()) { @@ -164,18 +140,18 @@ void Run() FlushStepSyncEventLocked(); continue; } - if (s_state_paused_and_locked) + if (m_state_paused_and_locked) continue; // Do step - s_state_cpu_thread_active = true; + m_state_cpu_thread_active = true; state_lock.unlock(); PowerPC::SingleStep(); state_lock.lock(); - s_state_cpu_thread_active = false; - s_state_cpu_idle_cvar.notify_all(); + m_state_cpu_thread_active = false; + m_state_cpu_idle_cvar.notify_all(); // Update disasm dialog FlushStepSyncEventLocked(); @@ -190,57 +166,57 @@ void Run() Host_UpdateDisasmDialog(); } -// Requires holding s_state_change_lock -static void RunAdjacentSystems(bool running) +// Requires holding m_state_change_lock +void CPUManager::RunAdjacentSystems(bool running) { // NOTE: We're assuming these will not try to call Break or EnableStepping. auto& system = Core::System::GetInstance(); system.GetFifo().EmulatorState(running); // Core is responsible for shutting down the sound stream. - if (s_state != State::PowerDown) + if (m_state != State::PowerDown) AudioCommon::SetSoundStreamRunning(Core::System::GetInstance(), running); } -void Stop() +void CPUManager::Stop() { // Change state and wait for it to be acknowledged. // We don't need the stepping lock because State::PowerDown is a priority state which // will stick permanently. - std::unique_lock state_lock(s_state_change_lock); - s_state = State::PowerDown; - s_state_cpu_cvar.notify_one(); + std::unique_lock state_lock(m_state_change_lock); + m_state = State::PowerDown; + m_state_cpu_cvar.notify_one(); - while (s_state_cpu_thread_active) + while (m_state_cpu_thread_active) { - s_state_cpu_idle_cvar.wait(state_lock); + m_state_cpu_idle_cvar.wait(state_lock); } RunAdjacentSystems(false); FlushStepSyncEventLocked(); } -bool IsStepping() +bool CPUManager::IsStepping() const { - return s_state == State::Stepping; + return m_state == State::Stepping; } -State GetState() +State CPUManager::GetState() const { - return s_state; + return m_state; } -const State* GetStatePtr() +const State* CPUManager::GetStatePtr() const { - return &s_state; + return &m_state; } -void Reset() +void CPUManager::Reset() { } -void StepOpcode(Common::Event* event) +void CPUManager::StepOpcode(Common::Event* event) { - std::lock_guard state_lock(s_state_change_lock); + std::lock_guard state_lock(m_state_change_lock); // If we're not stepping then this is pointless if (!IsStepping()) { @@ -250,55 +226,55 @@ void StepOpcode(Common::Event* event) } // Potential race where the previous step has not been serviced yet. - if (s_state_cpu_step_instruction_sync && s_state_cpu_step_instruction_sync != event) - s_state_cpu_step_instruction_sync->Set(); + if (m_state_cpu_step_instruction_sync && m_state_cpu_step_instruction_sync != event) + m_state_cpu_step_instruction_sync->Set(); - s_state_cpu_step_instruction = true; - s_state_cpu_step_instruction_sync = event; - s_state_cpu_cvar.notify_one(); + m_state_cpu_step_instruction = true; + m_state_cpu_step_instruction_sync = event; + m_state_cpu_cvar.notify_one(); } -// Requires s_state_change_lock -static bool SetStateLocked(State s) +// Requires m_state_change_lock +bool CPUManager::SetStateLocked(State s) { - if (s_state == State::PowerDown) + if (m_state == State::PowerDown) return false; - s_state = s; + m_state = s; return true; } -void EnableStepping(bool stepping) +void CPUManager::EnableStepping(bool stepping) { - std::lock_guard stepping_lock(s_stepping_lock); - std::unique_lock state_lock(s_state_change_lock); + std::lock_guard stepping_lock(m_stepping_lock); + std::unique_lock state_lock(m_state_change_lock); if (stepping) { SetStateLocked(State::Stepping); - while (s_state_cpu_thread_active) + while (m_state_cpu_thread_active) { - s_state_cpu_idle_cvar.wait(state_lock); + m_state_cpu_idle_cvar.wait(state_lock); } RunAdjacentSystems(false); } else if (SetStateLocked(State::Running)) { - s_state_cpu_cvar.notify_one(); + m_state_cpu_cvar.notify_one(); RunAdjacentSystems(true); } } -void Break() +void CPUManager::Break() { - std::lock_guard state_lock(s_state_change_lock); + std::lock_guard state_lock(m_state_change_lock); // If another thread is trying to PauseAndLock then we need to remember this // for later to ignore the unpause_on_unlock. - if (s_state_paused_and_locked) + if (m_state_paused_and_locked) { - s_state_system_request_stepping = true; + m_state_system_request_stepping = true; return; } @@ -308,31 +284,31 @@ void Break() RunAdjacentSystems(false); } -void Continue() +void CPUManager::Continue() { - CPU::EnableStepping(false); + EnableStepping(false); Core::CallOnStateChangedCallbacks(Core::State::Running); } -bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent) +bool CPUManager::PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent) { - // NOTE: This is protected by s_stepping_lock. + // NOTE: This is protected by m_stepping_lock. static bool s_have_fake_cpu_thread = false; bool was_unpaused = false; if (do_lock) { - s_stepping_lock.lock(); + m_stepping_lock.lock(); - std::unique_lock state_lock(s_state_change_lock); - s_state_paused_and_locked = true; + std::unique_lock state_lock(m_state_change_lock); + m_state_paused_and_locked = true; - was_unpaused = s_state == State::Running; + was_unpaused = m_state == State::Running; SetStateLocked(State::Stepping); - while (s_state_cpu_thread_active) + while (m_state_cpu_thread_active) { - s_state_cpu_idle_cvar.wait(state_lock); + m_state_cpu_idle_cvar.wait(state_lock); } if (control_adjacent) @@ -357,30 +333,30 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock, bool control_adjacent) } { - std::lock_guard state_lock(s_state_change_lock); - if (s_state_system_request_stepping) + std::lock_guard state_lock(m_state_change_lock); + if (m_state_system_request_stepping) { - s_state_system_request_stepping = false; + m_state_system_request_stepping = false; } else if (unpause_on_unlock && SetStateLocked(State::Running)) { was_unpaused = true; } - s_state_paused_and_locked = false; - s_state_cpu_cvar.notify_one(); + m_state_paused_and_locked = false; + m_state_cpu_cvar.notify_one(); if (control_adjacent) - RunAdjacentSystems(s_state == State::Running); + RunAdjacentSystems(m_state == State::Running); } - s_stepping_lock.unlock(); + m_stepping_lock.unlock(); } return was_unpaused; } -void AddCPUThreadJob(std::function function) +void CPUManager::AddCPUThreadJob(std::function function) { - std::unique_lock state_lock(s_state_change_lock); - s_pending_jobs.push(std::move(function)); + std::unique_lock state_lock(m_state_change_lock); + m_pending_jobs.push(std::move(function)); } } // namespace CPU diff --git a/Source/Core/Core/HW/CPU.h b/Source/Core/Core/HW/CPU.h index 95539ec2ef..85f8885026 100644 --- a/Source/Core/Core/HW/CPU.h +++ b/Source/Core/Core/HW/CPU.h @@ -2,7 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + +#include #include +#include +#include namespace Common { @@ -23,62 +27,108 @@ enum class State PowerDown = 3 }; -// Init -void Init(PowerPC::CPUCore cpu_core); +class CPUManager +{ +public: + CPUManager(); + CPUManager(const CPUManager& other) = delete; + CPUManager(CPUManager&& other) = delete; + CPUManager& operator=(const CPUManager& other) = delete; + CPUManager& operator=(CPUManager&& other) = delete; + ~CPUManager(); -// Shutdown -void Shutdown(); + // Init + void Init(PowerPC::CPUCore cpu_core); -// Starts the CPU -// To be called by the CPU Thread. -void Run(); + // Shutdown + void Shutdown(); -// Causes shutdown -// Once started, State::PowerDown cannot be stopped. -// Synchronizes with the CPU Thread (waits for CPU::Run to exit). -void Stop(); + // Starts the CPU + // To be called by the CPU Thread. + void Run(); -// Reset [NOT IMPLEMENTED] -void Reset(); + // Causes shutdown + // Once started, State::PowerDown cannot be stopped. + // Synchronizes with the CPU Thread (waits for CPU::Run to exit). + void Stop(); -// StepOpcode (Steps one Opcode) -void StepOpcode(Common::Event* event = nullptr); + // Reset [NOT IMPLEMENTED] + void Reset(); -// Enable or Disable Stepping. [Will deadlock if called from a system thread] -void EnableStepping(bool stepping); + // StepOpcode (Steps one Opcode) + void StepOpcode(Common::Event* event = nullptr); -// Breakpoint activation for system threads. Similar to EnableStepping(true). -// NOTE: Unlike EnableStepping, this does NOT synchronize with the CPU Thread -// which enables it to avoid deadlocks but also makes it less safe so it -// should not be used by the Host. -void Break(); + // Enable or Disable Stepping. [Will deadlock if called from a system thread] + void EnableStepping(bool stepping); -// This should only be called from the CPU thread -void Continue(); + // Breakpoint activation for system threads. Similar to EnableStepping(true). + // NOTE: Unlike EnableStepping, this does NOT synchronize with the CPU Thread + // which enables it to avoid deadlocks but also makes it less safe so it + // should not be used by the Host. + void Break(); -// Shorthand for GetState() == State::Stepping. -// WARNING: State::PowerDown will return false, not just State::Running. -bool IsStepping(); + // This should only be called from the CPU thread + void Continue(); -// Get current CPU Thread State -State GetState(); + // Shorthand for GetState() == State::Stepping. + // WARNING: State::PowerDown will return false, not just State::Running. + bool IsStepping() const; -// Direct State Access (Raw pointer for embedding into JIT Blocks) -// Strictly read-only. A lock is required to change the value. -const State* GetStatePtr(); + // Get current CPU Thread State + State GetState() const; -// Locks the CPU Thread (waiting for it to become idle). -// While this lock is held, the CPU Thread will not perform any action so it is safe to access -// PowerPC, CoreTiming, etc. without racing the CPU Thread. -// Cannot be used recursively. Must be paired as PauseAndLock(true)/PauseAndLock(false). -// 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* State::Running or not. -// 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 -// state of the Audio and FIFO subsystems as well. -bool PauseAndLock(bool do_lock, bool unpause_on_unlock = true, bool control_adjacent = false); + // Direct State Access (Raw pointer for embedding into JIT Blocks) + // Strictly read-only. A lock is required to change the value. + const State* GetStatePtr() const; -// Adds a job to be executed during on the CPU thread. This should be combined with PauseAndLock(), -// as while the CPU is in the run loop, it won't execute the function. -void AddCPUThreadJob(std::function function); + // Locks the CPU Thread (waiting for it to become idle). + // While this lock is held, the CPU Thread will not perform any action so it is safe to access + // PowerPC, CoreTiming, etc. without racing the CPU Thread. + // Cannot be used recursively. Must be paired as PauseAndLock(true)/PauseAndLock(false). + // 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* State::Running or not. + // 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 + // state of the Audio and FIFO subsystems as well. + bool PauseAndLock(bool do_lock, bool unpause_on_unlock = true, bool control_adjacent = false); + + // Adds a job to be executed during on the CPU thread. This should be combined with + // PauseAndLock(), as while the CPU is in the run loop, it won't execute the function. + void AddCPUThreadJob(std::function function); + +private: + void FlushStepSyncEventLocked(); + void ExecutePendingJobs(std::unique_lock& state_lock); + void RunAdjacentSystems(bool running); + bool SetStateLocked(State s); + + // CPU Thread execution state. + // Requires m_state_change_lock to modify the value. + // Read access is unsynchronized. + State m_state = State::PowerDown; + + // Synchronizes EnableStepping and PauseAndLock so only one instance can be + // active at a time. Simplifies code by eliminating several edge cases where + // the EnableStepping(true)/PauseAndLock(true) case must release the state lock + // and wait for the CPU Thread which would otherwise require additional flags. + // NOTE: When using the stepping lock, it must always be acquired first. If + // the lock is acquired after the state lock then that is guaranteed to + // deadlock because of the order inversion. (A -> X,Y; B -> Y,X; A waits for + // B, B waits for A) + std::mutex m_stepping_lock; + + // Primary lock. Protects changing m_state, requesting instruction stepping and + // pause-and-locking. + std::mutex m_state_change_lock; + // When m_state_cpu_thread_active changes to false + std::condition_variable m_state_cpu_idle_cvar; + // When m_state changes / m_state_paused_and_locked becomes false (for CPU Thread only) + std::condition_variable m_state_cpu_cvar; + bool m_state_cpu_thread_active = false; + bool m_state_paused_and_locked = false; + bool m_state_system_request_stepping = false; + bool m_state_cpu_step_instruction = false; + Common::Event* m_state_cpu_step_instruction_sync = nullptr; + std::queue> m_pending_jobs; +}; } // namespace CPU diff --git a/Source/Core/Core/HW/HW.cpp b/Source/Core/Core/HW/HW.cpp index 64de70b7a5..f7f76facb2 100644 --- a/Source/Core/Core/HW/HW.cpp +++ b/Source/Core/Core/HW/HW.cpp @@ -52,7 +52,7 @@ void Init(const Sram* override_sram) DSP::Init(Config::Get(Config::MAIN_DSP_HLE)); DVDInterface::Init(); system.GetGPFifo().Init(); - CPU::Init(Config::Get(Config::MAIN_CPU_CORE)); + system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE)); SystemTimers::Init(); if (SConfig::GetInstance().bWii) @@ -71,7 +71,7 @@ void Shutdown() IOS::Shutdown(); SystemTimers::Shutdown(); - CPU::Shutdown(); + system.GetCPU().Shutdown(); DVDInterface::Shutdown(); DSP::Shutdown(); MemoryInterface::Shutdown(); diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index d7d986427b..006dfabd2e 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -916,7 +916,7 @@ void Update(u64 ticks) // dealing with SI polls, but after potentially sending a swap request to the GPU thread if (state.half_line_count == 0 || state.half_line_count == GetHalfLinesPerEvenField()) - Core::Callback_NewField(); + Core::Callback_NewField(system); // If an SI poll is scheduled to happen on this half-line, do it! diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 16dc03579e..4a1f962bf0 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -1275,7 +1275,8 @@ void PlayController(GCPadStatus* PadStatus, int controllerID) Core::RunAsCPUThread([] { if (!DVDInterface::AutoChangeDisc()) { - CPU::Break(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Break(); PanicAlertFmtT("Change the disc to {0}", s_discChange); } }); @@ -1355,9 +1356,11 @@ void EndPlayInput(bool cont) else if (s_playMode != PlayMode::None) { // We can be called by EmuThread during boot (CPU::State::PowerDown) - bool was_running = Core::IsRunningAndStarted() && !CPU::IsStepping(); + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + bool was_running = Core::IsRunningAndStarted() && !cpu.IsStepping(); if (was_running && Config::Get(Config::MAIN_MOVIE_PAUSE_MOVIE)) - CPU::Break(); + cpu.Break(); s_rerecords = 0; s_currentByte = 0; s_playMode = PlayMode::None; diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp index aa6693f466..52a6239485 100644 --- a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp @@ -112,9 +112,10 @@ void CachedInterpreter::Run() { auto& system = Core::System::GetInstance(); auto& core_timing = system.GetCoreTiming(); + auto& cpu = system.GetCPU(); - const CPU::State* state_ptr = CPU::GetStatePtr(); - while (CPU::GetState() == CPU::State::Running) + const CPU::State* state_ptr = cpu.GetStatePtr(); + while (cpu.GetState() == CPU::State::Running) { // Start new timing slice // NOTE: Exceptions may change PC @@ -199,7 +200,8 @@ static bool CheckProgramException(u32 data) static bool CheckBreakpoint(u32 data) { PowerPC::CheckBreakPoints(); - if (CPU::GetState() != CPU::State::Running) + auto& system = Core::System::GetInstance(); + if (system.GetCPU().GetState() != CPU::State::Running) { PowerPC::ppcState.downcount -= data; return true; diff --git a/Source/Core/Core/PowerPC/GDBStub.cpp b/Source/Core/Core/PowerPC/GDBStub.cpp index 782fc32158..b07c1aaf71 100644 --- a/Source/Core/Core/PowerPC/GDBStub.cpp +++ b/Source/Core/Core/PowerPC/GDBStub.cpp @@ -210,7 +210,8 @@ static void ReadCommand() } else if (c == 0x03) { - CPU::Break(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Break(); SendSignal(Signal::Sigtrap); s_has_control = true; INFO_LOG_FMT(GDB_STUB, "gdb: CPU::Break due to break command"); @@ -859,7 +860,8 @@ static void WriteMemory(const Core::CPUThreadGuard& guard) static void Step() { - CPU::EnableStepping(true); + auto& system = Core::System::GetInstance(); + system.GetCPU().EnableStepping(true); Core::CallOnStateChangedCallbacks(Core::State::Paused); } @@ -939,9 +941,11 @@ static void HandleRemoveBreakpoint() void ProcessCommands(bool loop_until_continue) { s_just_connected = false; + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); while (IsActive()) { - if (CPU::GetState() == CPU::State::PowerDown) + if (cpu.GetState() == CPU::State::PowerDown) { Deinit(); INFO_LOG_FMT(GDB_STUB, "killed by power down"); @@ -1004,7 +1008,6 @@ void ProcessCommands(bool loop_until_continue) Core::CPUThreadGuard guard; WriteMemory(guard); - auto& system = Core::System::GetInstance(); auto& ppc_state = system.GetPPCState(); ppc_state.iCache.Reset(); Host_UpdateDisasmDialog(); @@ -1015,7 +1018,7 @@ void ProcessCommands(bool loop_until_continue) return; case 'C': case 'c': - CPU::Continue(); + cpu.Continue(); s_has_control = false; return; case 'z': diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp index f952733d96..aba7c31ab5 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp @@ -253,7 +253,8 @@ void Interpreter::Run() { auto& system = Core::System::GetInstance(); auto& core_timing = system.GetCoreTiming(); - while (CPU::GetState() == CPU::State::Running) + auto& cpu = system.GetCPU(); + while (cpu.GetState() == CPU::State::Running) { // 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 @@ -306,7 +307,7 @@ void Interpreter::Run() } #endif INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", PowerPC::ppcState.pc); - CPU::Break(); + cpu.Break(); if (GDBStub::IsActive()) GDBStub::TakeControl(); if (PowerPC::breakpoints.IsTempBreakPoint(PowerPC::ppcState.pc)) @@ -341,13 +342,14 @@ void Interpreter::Run() void Interpreter::unknown_instruction(UGeckoInstruction inst) { ASSERT(Core::IsCPUThread()); + auto& system = Core::System::GetInstance(); Core::CPUThreadGuard guard; const u32 opcode = PowerPC::HostRead_U32(guard, last_pc); const std::string disasm = Common::GekkoDisassembler::Disassemble(opcode, last_pc); NOTICE_LOG_FMT(POWERPC, "Last PC = {:08x} : {}", last_pc, disasm); - Dolphin_Debugger::PrintCallstack(Core::System::GetInstance(), guard, - Common::Log::LogType::POWERPC, Common::Log::LogLevel::LNOTICE); + Dolphin_Debugger::PrintCallstack(system, guard, Common::Log::LogType::POWERPC, + Common::Log::LogLevel::LNOTICE); NOTICE_LOG_FMT( POWERPC, "\nIntCPU: Unknown instruction {:08x} at PC = {:08x} last_PC = {:08x} LR = {:08x}\n", @@ -361,8 +363,8 @@ void Interpreter::unknown_instruction(UGeckoInstruction inst) ASSERT_MSG(POWERPC, 0, "\nIntCPU: Unknown instruction {:08x} at PC = {:08x} last_PC = {:08x} LR = {:08x}\n", inst.hex, PowerPC::ppcState.pc, last_pc, LR(PowerPC::ppcState)); - if (Core::System::GetInstance().IsPauseOnPanicMode()) - CPU::Break(); + if (system.IsPauseOnPanicMode()) + system.GetCPU().Break(); } void Interpreter::ClearCache() diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index e5c32a5c12..45a5b9a07e 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -720,7 +720,7 @@ void Jit64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure) if (!jo.profile_blocks) { - if (CPU::IsStepping()) + if (Core::System::GetInstance().GetCPU().IsStepping()) { block_size = 1; @@ -822,6 +822,8 @@ bool Jit64::SetEmitterStateToFreeCodeRegion() bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) { + auto& system = Core::System::GetInstance(); + js.firstFPInstructionFound = false; js.isLastInstruction = false; js.blockStart = em_address; @@ -942,7 +944,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) js.mustCheckFifo = false; BitSet32 registersInUse = CallerSavedRegistersInUse(); ABI_PushRegistersAndAdjustStack(registersInUse, 0); - ABI_CallFunctionP(GPFifo::FastCheckGatherPipe, &Core::System::GetInstance().GetGPFifo()); + ABI_CallFunctionP(GPFifo::FastCheckGatherPipe, &system.GetGPFifo()); ABI_PopRegistersAndAdjustStack(registersInUse, 0); gatherPipeIntCheck = true; } @@ -959,7 +961,6 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) SetJumpTarget(extException); TEST(32, PPCSTATE(msr), Imm32(0x0008000)); FixupBranch noExtIntEnable = J_CC(CC_Z, true); - auto& system = Core::System::GetInstance(); MOV(64, R(RSCRATCH), ImmPtr(&system.GetProcessorInterface().m_interrupt_cause)); TEST(32, MatR(RSCRATCH), Imm32(ProcessorInterface::INT_CAUSE_CP | ProcessorInterface::INT_CAUSE_PE_TOKEN | @@ -1012,7 +1013,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) js.firstFPInstructionFound = true; } - if (m_enable_debugging && breakpoints.IsAddressBreakPoint(op.address) && !CPU::IsStepping()) + auto& cpu = system.GetCPU(); + if (m_enable_debugging && breakpoints.IsAddressBreakPoint(op.address) && !cpu.IsStepping()) { gpr.Flush(); fpr.Flush(); @@ -1021,7 +1023,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) ABI_PushRegistersAndAdjustStack({}, 0); ABI_CallFunction(PowerPC::CheckBreakPoints); ABI_PopRegistersAndAdjustStack({}, 0); - MOV(64, R(RSCRATCH), ImmPtr(CPU::GetStatePtr())); + MOV(64, R(RSCRATCH), ImmPtr(cpu.GetStatePtr())); TEST(32, MatR(RSCRATCH), Imm32(0xFFFFFFFF)); FixupBranch noBreakpoint = J_CC(CC_Z); diff --git a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp index 61f6f43d7b..2cf698f6a8 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp @@ -81,10 +81,12 @@ void Jit64AsmRoutineManager::Generate() dispatcher_no_timing_check = GetCodePtr(); + auto& system = Core::System::GetInstance(); + FixupBranch dbg_exit; if (enable_debugging) { - MOV(64, R(RSCRATCH), ImmPtr(CPU::GetStatePtr())); + MOV(64, R(RSCRATCH), ImmPtr(system.GetCPU().GetStatePtr())); TEST(32, MatR(RSCRATCH), Imm32(0xFFFFFFFF)); dbg_exit = J_CC(CC_NZ, true); } @@ -93,7 +95,6 @@ void Jit64AsmRoutineManager::Generate() dispatcher_no_check = GetCodePtr(); - auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); // The following is a translation of JitBaseBlockCache::Dispatch into assembly. @@ -190,7 +191,7 @@ void Jit64AsmRoutineManager::Generate() // Check the state pointer to see if we are exiting // Gets checked on at the end of every slice - MOV(64, R(RSCRATCH), ImmPtr(CPU::GetStatePtr())); + MOV(64, R(RSCRATCH), ImmPtr(system.GetCPU().GetStatePtr())); TEST(32, MatR(RSCRATCH), Imm32(0xFFFFFFFF)); J_CC(CC_Z, outerLoop); diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index 08d137631c..b8ddf1b5d6 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -714,6 +714,9 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure) std::size_t block_size = m_code_buffer.size(); + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + if (m_enable_debugging) { // We can link blocks as long as we are not single stepping @@ -722,7 +725,7 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure) if (!jo.profile_blocks) { - if (CPU::IsStepping()) + if (cpu.IsStepping()) { block_size = 1; @@ -820,6 +823,9 @@ bool JitArm64::SetEmitterStateToFreeCodeRegion() bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) { + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + js.isLastInstruction = false; js.firstFPInstructionFound = false; js.assumeNoPairedQuantize = false; @@ -944,7 +950,6 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) SetJumpTarget(exception); LDR(IndexType::Unsigned, ARM64Reg::W30, PPC_REG, PPCSTATE_OFF(msr)); TBZ(ARM64Reg::W30, 15, done_here); // MSR.EE - auto& system = Core::System::GetInstance(); LDR(IndexType::Unsigned, ARM64Reg::W30, ARM64Reg::X30, MOVPage2R(ARM64Reg::X30, &system.GetProcessorInterface().m_interrupt_cause)); constexpr u32 cause_mask = ProcessorInterface::INT_CAUSE_CP | @@ -981,7 +986,6 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) SetJumpTarget(exception); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr)); TBZ(WA, 15, done_here); // MSR.EE - auto& system = Core::System::GetInstance(); LDR(IndexType::Unsigned, WA, XA, MOVPage2R(XA, &system.GetProcessorInterface().m_interrupt_cause)); constexpr u32 cause_mask = ProcessorInterface::INT_CAUSE_CP | @@ -1035,7 +1039,7 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) } if (m_enable_debugging && PowerPC::breakpoints.IsAddressBreakPoint(op.address) && - !CPU::IsStepping()) + !cpu.IsStepping()) { FlushCarry(); gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); @@ -1050,7 +1054,7 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) BLR(ARM64Reg::X0); LDR(IndexType::Unsigned, ARM64Reg::W0, ARM64Reg::X0, - MOVPage2R(ARM64Reg::X0, CPU::GetStatePtr())); + MOVPage2R(ARM64Reg::X0, cpu.GetStatePtr())); FixupBranch no_breakpoint = CBZ(ARM64Reg::W0); Cleanup(); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp index bfc0de7449..4bfeb1f2ba 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp @@ -81,11 +81,14 @@ void JitArm64::GenerateAsm() dispatcher_no_timing_check = GetCodePtr(); + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + FixupBranch debug_exit; if (enable_debugging) { LDR(IndexType::Unsigned, ARM64Reg::W0, ARM64Reg::X0, - MOVPage2R(ARM64Reg::X0, CPU::GetStatePtr())); + MOVPage2R(ARM64Reg::X0, cpu.GetStatePtr())); debug_exit = CBNZ(ARM64Reg::W0); } @@ -93,7 +96,6 @@ void JitArm64::GenerateAsm() bool assembly_dispatcher = true; - auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); if (assembly_dispatcher) @@ -177,7 +179,7 @@ void JitArm64::GenerateAsm() // Check the state pointer to see if we are exiting // Gets checked on at the end of every slice - LDR(IndexType::Unsigned, ARM64Reg::W0, ARM64Reg::X0, MOVPage2R(ARM64Reg::X0, CPU::GetStatePtr())); + LDR(IndexType::Unsigned, ARM64Reg::W0, ARM64Reg::X0, MOVPage2R(ARM64Reg::X0, cpu.GetStatePtr())); FixupBranch exit = CBNZ(ARM64Reg::W0); SetJumpTarget(to_start_of_timing_slice); diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp index 9800123075..2e319f41ab 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp @@ -213,7 +213,8 @@ void JitBase::CleanUpAfterStackFault() bool JitBase::CanMergeNextInstructions(int count) const { - if (CPU::IsStepping() || js.instructionsLeft < count) + auto& system = Core::System::GetInstance(); + if (system.GetCPU().IsStepping() || js.instructionsLeft < count) return false; // Be careful: a breakpoint kills flags in between instructions for (int i = 1; i <= count; i++) diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index 35cfa5af0e..0a03195138 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -173,7 +173,7 @@ BatTable dbat_table; static void GenerateDSIException(u32 effective_address, bool write); template -static T ReadFromHardware(Memory::MemoryManager& memory, u32 em_address) +static T ReadFromHardware(Core::System& system, Memory::MemoryManager& memory, u32 em_address) { const u32 em_address_start_page = em_address & ~HW_PAGE_MASK; const u32 em_address_end_page = (em_address + sizeof(T) - 1) & ~HW_PAGE_MASK; @@ -185,7 +185,10 @@ static T ReadFromHardware(Memory::MemoryManager& memory, u32 em_address) // Note that "word" means 32-bit, so paired singles or doubles might still be 32-bit aligned! u64 var = 0; for (u32 i = 0; i < sizeof(T); ++i) - var = (var << 8) | ReadFromHardware(memory, em_address + i); + { + var = + (var << 8) | ReadFromHardware(system, memory, em_address + i); + } return static_cast(var); } @@ -271,9 +274,9 @@ static T ReadFromHardware(Memory::MemoryManager& memory, u32 em_address) } PanicAlertFmt("Unable to resolve read address {:x} PC {:x}", em_address, PowerPC::ppcState.pc); - if (Core::System::GetInstance().IsPauseOnPanicMode()) + if (system.IsPauseOnPanicMode()) { - CPU::Break(); + system.GetCPU().Break(); ppcState.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT; } return 0; @@ -460,9 +463,9 @@ static void WriteToHardware(Core::System& system, Memory::MemoryManager& memory, } PanicAlertFmt("Unable to resolve write address {:x} PC {:x}", em_address, PowerPC::ppcState.pc); - if (Core::System::GetInstance().IsPauseOnPanicMode()) + if (system.IsPauseOnPanicMode()) { - CPU::Break(); + system.GetCPU().Break(); ppcState.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT; } } @@ -523,7 +526,7 @@ u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - return ReadFromHardware(memory, address); + return ReadFromHardware(system, memory, address); } std::optional> HostTryReadInstruction(const Core::CPUThreadGuard& guard, @@ -540,20 +543,22 @@ std::optional> HostTryReadInstruction(const Core::CPUThreadGuard { case RequestedAddressSpace::Effective: { - const u32 value = ReadFromHardware(memory, address); + const u32 value = + ReadFromHardware(system, memory, address); return ReadResult(!!PowerPC::ppcState.msr.DR, value); } case RequestedAddressSpace::Physical: { const u32 value = - ReadFromHardware(memory, address); + ReadFromHardware(system, memory, address); return ReadResult(false, value); } case RequestedAddressSpace::Virtual: { if (!PowerPC::ppcState.msr.DR) return std::nullopt; - const u32 value = ReadFromHardware(memory, address); + const u32 value = + ReadFromHardware(system, memory, address); return ReadResult(true, value); } } @@ -562,7 +567,7 @@ std::optional> HostTryReadInstruction(const Core::CPUThreadGuard return std::nullopt; } -static void Memcheck(u32 address, u64 var, bool write, size_t size) +static void Memcheck(Core::System& system, u32 address, u64 var, bool write, size_t size) { if (!memchecks.HasAny()) return; @@ -571,7 +576,7 @@ static void Memcheck(u32 address, u64 var, bool write, size_t size) if (mc == nullptr) return; - if (CPU::IsStepping()) + if (system.GetCPU().IsStepping()) { // Disable when stepping so that resume works. return; @@ -583,7 +588,7 @@ static void Memcheck(u32 address, u64 var, bool write, size_t size) if (!pause) return; - CPU::Break(); + system.GetCPU().Break(); if (GDBStub::IsActive()) GDBStub::TakeControl(); @@ -602,8 +607,8 @@ u8 Read_U8(const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - u8 var = ReadFromHardware(memory, address); - Memcheck(address, var, false, 1); + u8 var = ReadFromHardware(system, memory, address); + Memcheck(system, address, var, false, 1); return var; } @@ -611,8 +616,8 @@ u16 Read_U16(const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - u16 var = ReadFromHardware(memory, address); - Memcheck(address, var, false, 2); + u16 var = ReadFromHardware(system, memory, address); + Memcheck(system, address, var, false, 2); return var; } @@ -620,8 +625,8 @@ u32 Read_U32(const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - u32 var = ReadFromHardware(memory, address); - Memcheck(address, var, false, 4); + u32 var = ReadFromHardware(system, memory, address); + Memcheck(system, address, var, false, 4); return var; } @@ -629,8 +634,8 @@ u64 Read_U64(const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - u64 var = ReadFromHardware(memory, address); - Memcheck(address, var, false, 8); + u64 var = ReadFromHardware(system, memory, address); + Memcheck(system, address, var, false, 8); return var; } @@ -662,19 +667,19 @@ static std::optional> HostTryReadUX(const Core::CPUThreadGuard& gu { case RequestedAddressSpace::Effective: { - T value = ReadFromHardware(memory, address); + T value = ReadFromHardware(system, memory, address); return ReadResult(!!PowerPC::ppcState.msr.DR, std::move(value)); } case RequestedAddressSpace::Physical: { - T value = ReadFromHardware(memory, address); + T value = ReadFromHardware(system, memory, address); return ReadResult(false, std::move(value)); } case RequestedAddressSpace::Virtual: { if (!PowerPC::ppcState.msr.DR) return std::nullopt; - T value = ReadFromHardware(memory, address); + T value = ReadFromHardware(system, memory, address); return ReadResult(true, std::move(value)); } } @@ -737,17 +742,17 @@ u32 Read_U16_ZX(const u32 address) void Write_U8(const u32 var, const u32 address) { - Memcheck(address, var, true, 1); auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); + Memcheck(system, address, var, true, 1); WriteToHardware(system, memory, address, var, 1); } void Write_U16(const u32 var, const u32 address) { - Memcheck(address, var, true, 2); auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); + Memcheck(system, address, var, true, 2); WriteToHardware(system, memory, address, var, 2); } void Write_U16_Swap(const u32 var, const u32 address) @@ -757,9 +762,9 @@ void Write_U16_Swap(const u32 var, const u32 address) void Write_U32(const u32 var, const u32 address) { - Memcheck(address, var, true, 4); auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); + Memcheck(system, address, var, true, 4); WriteToHardware(system, memory, address, var, 4); } void Write_U32_Swap(const u32 var, const u32 address) @@ -769,9 +774,9 @@ void Write_U32_Swap(const u32 var, const u32 address) void Write_U64(const u64 var, const u32 address) { - Memcheck(address, var, true, 8); auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); + Memcheck(system, address, var, true, 8); WriteToHardware(system, memory, address, static_cast(var >> 32), 4); WriteToHardware(system, memory, address + sizeof(u32), static_cast(var), 4); @@ -792,28 +797,28 @@ u8 HostRead_U8(const Core::CPUThreadGuard& guard, const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - return ReadFromHardware(memory, address); + return ReadFromHardware(system, memory, address); } u16 HostRead_U16(const Core::CPUThreadGuard& guard, const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - return ReadFromHardware(memory, address); + return ReadFromHardware(system, memory, address); } u32 HostRead_U32(const Core::CPUThreadGuard& guard, const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - return ReadFromHardware(memory, address); + return ReadFromHardware(system, memory, address); } u64 HostRead_U64(const Core::CPUThreadGuard& guard, const u32 address) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - return ReadFromHardware(memory, address); + return ReadFromHardware(system, memory, address); } float HostRead_F32(const Core::CPUThreadGuard& guard, const u32 address) @@ -1374,13 +1379,14 @@ TranslateResult JitCache_TranslateAddress(u32 address) static void GenerateDSIException(u32 effective_address, bool write) { // DSI exceptions are only supported in MMU mode. - if (!Core::System::GetInstance().IsMMUMode()) + auto& system = Core::System::GetInstance(); + if (!system.IsMMUMode()) { PanicAlertFmt("Invalid {} {:#010x}, PC = {:#010x}", write ? "write to" : "read from", effective_address, PowerPC::ppcState.pc); - if (Core::System::GetInstance().IsPauseOnPanicMode()) + if (system.IsPauseOnPanicMode()) { - CPU::Break(); + system.GetCPU().Break(); ppcState.Exceptions |= EXCEPTION_DSI | EXCEPTION_FAKE_MEMCHECK_HIT; } return; diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp index 228573b8e6..2d8f201b30 100644 --- a/Source/Core/Core/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/PowerPC/PowerPC.cpp @@ -298,10 +298,13 @@ void Reset() void ScheduleInvalidateCacheThreadSafe(u32 address) { - if (CPU::GetState() == CPU::State::Running && !Core::IsCPUThread()) + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + + if (cpu.GetState() == CPU::State::Running && !Core::IsCPUThread()) { - Core::System::GetInstance().GetCoreTiming().ScheduleEvent( - 0, s_invalidate_cache_thread_safe, address, CoreTiming::FromThread::NON_CPU); + system.GetCoreTiming().ScheduleEvent(0, s_invalidate_cache_thread_safe, address, + CoreTiming::FromThread::NON_CPU); } else { @@ -637,7 +640,8 @@ void CheckBreakPoints() if (bp->break_on_hit) { - CPU::Break(); + auto& system = Core::System::GetInstance(); + system.GetCPU().Break(); if (GDBStub::IsActive()) GDBStub::TakeControl(); } diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 58dd46c53f..6bd6694d8f 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -9,6 +9,7 @@ #include "Core/Config/MainSettings.h" #include "Core/CoreTiming.h" #include "Core/HW/AudioInterface.h" +#include "Core/HW/CPU.h" #include "Core/HW/DSP.h" #include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/DVDThread.h" @@ -46,6 +47,7 @@ struct System::Impl AudioInterface::AudioInterfaceState m_audio_interface_state; CoreTiming::CoreTimingManager m_core_timing; CommandProcessor::CommandProcessorManager m_command_processor; + CPU::CPUManager m_cpu; DSP::DSPState m_dsp_state; DVDInterface::DVDInterfaceState m_dvd_interface_state; DVDThread::DVDThreadState m_dvd_thread_state; @@ -115,6 +117,11 @@ AudioInterface::AudioInterfaceState& System::GetAudioInterfaceState() const return m_impl->m_audio_interface_state; } +CPU::CPUManager& System::GetCPU() const +{ + return m_impl->m_cpu; +} + CoreTiming::CoreTimingManager& System::GetCoreTiming() const { return m_impl->m_core_timing; diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 286ceabcaf..0a529a6cbe 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -15,6 +15,10 @@ namespace AudioInterface { class AudioInterfaceState; }; +namespace CPU +{ +class CPUManager; +} namespace CommandProcessor { class CommandProcessorManager; @@ -119,6 +123,7 @@ public: void SetAudioDumpStarted(bool started); AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const; + CPU::CPUManager& GetCPU() const; CoreTiming::CoreTimingManager& GetCoreTiming() const; CommandProcessor::CommandProcessorManager& GetCommandProcessor() const; DSP::DSPState& GetDSPState() const; diff --git a/Source/Core/DolphinQt/Debugger/CodeWidget.cpp b/Source/Core/DolphinQt/Debugger/CodeWidget.cpp index 391152abc9..f5fa095166 100644 --- a/Source/Core/DolphinQt/Debugger/CodeWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/CodeWidget.cpp @@ -435,7 +435,10 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol) void CodeWidget::Step() { - if (!CPU::IsStepping()) + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + + if (!cpu.IsStepping()) return; Common::Event sync_event; @@ -443,7 +446,7 @@ void CodeWidget::Step() PowerPC::CoreMode old_mode = PowerPC::GetMode(); PowerPC::SetMode(PowerPC::CoreMode::Interpreter); PowerPC::breakpoints.ClearAllTemporary(); - CPU::StepOpcode(&sync_event); + cpu.StepOpcode(&sync_event); sync_event.WaitFor(std::chrono::milliseconds(20)); PowerPC::SetMode(old_mode); Core::DisplayMessage(tr("Step successful!").toStdString(), 2000); @@ -452,7 +455,10 @@ void CodeWidget::Step() void CodeWidget::StepOver() { - if (!CPU::IsStepping()) + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + + if (!cpu.IsStepping()) return; const UGeckoInstruction inst = [] { @@ -464,7 +470,7 @@ void CodeWidget::StepOver() { PowerPC::breakpoints.ClearAllTemporary(); PowerPC::breakpoints.Add(PowerPC::ppcState.pc + 4, true); - CPU::EnableStepping(false); + cpu.EnableStepping(false); Core::DisplayMessage(tr("Step over in progress...").toStdString(), 2000); } else @@ -489,7 +495,10 @@ static bool WillInstructionReturn(UGeckoInstruction inst) void CodeWidget::StepOut() { - if (!CPU::IsStepping()) + auto& system = Core::System::GetInstance(); + auto& cpu = system.GetCPU(); + + if (!cpu.IsStepping()) return; // Keep stepping until the next return instruction or timeout after five seconds