summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorMai <mai.iam2048@gmail.com>2023-03-08 11:52:30 -0500
committerGitHub <noreply@github.com>2023-03-08 11:52:30 -0500
commitc592c94395c8ef271eef331ba27d2afaa694c5e9 (patch)
tree626a968236d3964fc343e7bd5c38b0e18ddd9917 /Source/Core
parentf1e4b6a1418c87b3968b35ae78d82d7e9be70636 (diff)
parent3b364c5c16d519902bdc3e1e07434a9f8b916ac6 (diff)
Merge pull request #11630 from AdmiralCurtiss/globals-cpu
HW/CPU: Refactor to class, move to System.
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/Core.cpp48
-rw-r--r--Source/Core/Core/Core.h4
-rw-r--r--Source/Core/Core/FifoPlayer/FifoPlayer.cpp20
-rw-r--r--Source/Core/Core/HLE/HLE_Misc.cpp3
-rw-r--r--Source/Core/Core/HW/CPU.cpp220
-rw-r--r--Source/Core/Core/HW/CPU.h166
-rw-r--r--Source/Core/Core/HW/HW.cpp4
-rw-r--r--Source/Core/Core/HW/VideoInterface.cpp2
-rw-r--r--Source/Core/Core/Movie.cpp9
-rw-r--r--Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp8
-rw-r--r--Source/Core/Core/PowerPC/GDBStub.cpp13
-rw-r--r--Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp14
-rw-r--r--Source/Core/Core/PowerPC/Jit64/Jit.cpp12
-rw-r--r--Source/Core/Core/PowerPC/Jit64/JitAsm.cpp7
-rw-r--r--Source/Core/Core/PowerPC/JitArm64/Jit.cpp14
-rw-r--r--Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp8
-rw-r--r--Source/Core/Core/PowerPC/JitCommon/JitBase.cpp3
-rw-r--r--Source/Core/Core/PowerPC/MMU.cpp76
-rw-r--r--Source/Core/Core/PowerPC/PowerPC.cpp12
-rw-r--r--Source/Core/Core/System.cpp7
-rw-r--r--Source/Core/Core/System.h5
-rw-r--r--Source/Core/DolphinQt/Debugger/CodeWidget.cpp19
22 files changed, 383 insertions, 291 deletions
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<std::string>& 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<std::string>& 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<BootParameters> 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<void()> 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<void()> function, bool wait_for_completion)
@@ -826,26 +830,28 @@ void RunOnCPUThread(std::function<void()> 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;
-
-// 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<std::function<void()>> s_pending_jobs;
-
-void Init(PowerPC::CPUCore cpu_core)
+CPUManager::CPUManager() = default;
+CPUManager::~CPUManager() = default;
+
+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<std::mutex>& state_lock)
+void CPUManager::ExecutePendingJobs(std::unique_lock<std::mutex>& 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();
-
- s_state_cpu_step_instruction = true;
- s_state_cpu_step_instruction_sync = &gdb_step_sync_event;
+ if (m_state_cpu_step_instruction_sync &&
+ m_state_cpu_step_instruction_sync != &gdb_step_sync_event)
+ {
+ m_state_cpu_step_instruction_sync->Set();
+ }
+
+ 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<void()> function)
+void CPUManager::AddCPUThreadJob(std::function<void()> 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 <condition_variable>
#include <functional>
+#include <mutex>
+#include <queue>
namespace Common
{
@@ -23,62 +27,108 @@ enum class State
PowerDown = 3
};
-// Init
-void Init(PowerPC::CPUCore cpu_core);
-
-// Shutdown
-void Shutdown();
-
-// Starts the CPU
-// To be called by the CPU Thread.
-void Run();
-
-// Causes shutdown
-// Once started, State::PowerDown cannot be stopped.
-// Synchronizes with the CPU Thread (waits for CPU::Run to exit).
-void Stop();
-
-// Reset [NOT IMPLEMENTED]
-void Reset();
-
-// StepOpcode (Steps one Opcode)
-void StepOpcode(Common::Event* event = nullptr);
-
-// Enable or Disable Stepping. [Will deadlock if called from a system thread]
-void EnableStepping(bool stepping);
-
-// 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();
-
-// This should only be called from the CPU thread
-void Continue();
-
-// Shorthand for GetState() == State::Stepping.
-// WARNING: State::PowerDown will return false, not just State::Running.
-bool IsStepping();
-
-// Get current CPU Thread State
-State GetState();
-
-// Direct State Access (Raw pointer for embedding into JIT Blocks)
-// Strictly read-only. A lock is required to change the value.
-const State* GetStatePtr();
-
-// 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<void()> function);
+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();
+
+ // Init
+ void Init(PowerPC::CPUCore cpu_core);
+
+ // Shutdown
+ void Shutdown();
+
+ // Starts the CPU
+ // To be called by the CPU Thread.
+ void Run();
+
+ // Causes shutdown
+ // Once started, State::PowerDown cannot be stopped.
+ // Synchronizes with the CPU Thread (waits for CPU::Run to exit).
+ void Stop();
+
+ // Reset [NOT IMPLEMENTED]
+ void Reset();
+
+ // StepOpcode (Steps one Opcode)
+ void StepOpcode(Common::Event* event = nullptr);
+
+ // Enable or Disable Stepping. [Will deadlock if called from a system thread]
+ void EnableStepping(bool stepping);
+
+ // 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();
+
+ // This should only be called from the CPU thread
+ void Continue();
+
+ // Shorthand for GetState() == State::Stepping.
+ // WARNING: State::PowerDown will return false, not just State::Running.
+ bool IsStepping() const;
+
+ // Get current CPU Thread State
+ State GetState() 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() 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);
+
+ // 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<void()> function);
+
+private:
+ void FlushStepSyncEventLocked();
+ void ExecutePendingJobs(std::unique_lock<std::mutex>& 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<std::function<void()>> 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 <XCheckTLBFlag flag, typename T, bool never_translate = false>
-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<flag, u8, never_translate>(memory, em_address + i);
+ {
+ var =
+ (var << 8) | ReadFromHardware<flag, u8, never_translate>(system, memory, em_address + i);
+ }
return static_cast<T>(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<XCheckTLBFlag::OpcodeNoException, u32>(memory, address);
+ return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(system, memory, address);
}
std::optional<ReadResult<u32>> HostTryReadInstruction(const Core::CPUThreadGuard& guard,
@@ -540,20 +543,22 @@ std::optional<ReadResult<u32>> HostTryReadInstruction(const Core::CPUThreadGuard
{
case RequestedAddressSpace::Effective:
{
- const u32 value = ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(memory, address);
+ const u32 value =
+ ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(system, memory, address);
return ReadResult<u32>(!!PowerPC::ppcState.msr.DR, value);
}
case RequestedAddressSpace::Physical:
{
const u32 value =
- ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32, true>(memory, address);
+ ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32, true>(system, memory, address);
return ReadResult<u32>(false, value);
}
case RequestedAddressSpace::Virtual:
{
if (!PowerPC::ppcState.msr.DR)
return std::nullopt;
- const u32 value = ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(memory, address);
+ const u32 value =
+ ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(system, memory, address);
return ReadResult<u32>(true, value);
}
}
@@ -562,7 +567,7 @@ std::optional<ReadResult<u32>> 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<XCheckTLBFlag::Read, u8>(memory, address);
- Memcheck(address, var, false, 1);
+ u8 var = ReadFromHardware<XCheckTLBFlag::Read, u8>(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<XCheckTLBFlag::Read, u16>(memory, address);
- Memcheck(address, var, false, 2);
+ u16 var = ReadFromHardware<XCheckTLBFlag::Read, u16>(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<XCheckTLBFlag::Read, u32>(memory, address);
- Memcheck(address, var, false, 4);
+ u32 var = ReadFromHardware<XCheckTLBFlag::Read, u32>(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<XCheckTLBFlag::Read, u64>(memory, address);
- Memcheck(address, var, false, 8);
+ u64 var = ReadFromHardware<XCheckTLBFlag::Read, u64>(system, memory, address);
+ Memcheck(system, address, var, false, 8);
return var;
}
@@ -662,19 +667,19 @@ static std::optional<ReadResult<T>> HostTryReadUX(const Core::CPUThreadGuard& gu
{
case RequestedAddressSpace::Effective:
{
- T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(memory, address);
+ T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(system, memory, address);
return ReadResult<T>(!!PowerPC::ppcState.msr.DR, std::move(value));
}
case RequestedAddressSpace::Physical:
{
- T value = ReadFromHardware<XCheckTLBFlag::NoException, T, true>(memory, address);
+ T value = ReadFromHardware<XCheckTLBFlag::NoException, T, true>(system, memory, address);
return ReadResult<T>(false, std::move(value));
}
case RequestedAddressSpace::Virtual:
{
if (!PowerPC::ppcState.msr.DR)
return std::nullopt;
- T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(memory, address);
+ T value = ReadFromHardware<XCheckTLBFlag::NoException, T>(system, memory, address);
return ReadResult<T>(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<XCheckTLBFlag::Write>(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<XCheckTLBFlag::Write>(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<XCheckTLBFlag::Write>(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<XCheckTLBFlag::Write>(system, memory, address, static_cast<u32>(var >> 32), 4);
WriteToHardware<XCheckTLBFlag::Write>(system, memory, address + sizeof(u32),
static_cast<u32>(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<XCheckTLBFlag::NoException, u8>(memory, address);
+ return ReadFromHardware<XCheckTLBFlag::NoException, u8>(system, memory, address);
}
u16 HostRead_U16(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
- return ReadFromHardware<XCheckTLBFlag::NoException, u16>(memory, address);
+ return ReadFromHardware<XCheckTLBFlag::NoException, u16>(system, memory, address);
}
u32 HostRead_U32(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
- return ReadFromHardware<XCheckTLBFlag::NoException, u32>(memory, address);
+ return ReadFromHardware<XCheckTLBFlag::NoException, u32>(system, memory, address);
}
u64 HostRead_U64(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
- return ReadFromHardware<XCheckTLBFlag::NoException, u64>(memory, address);
+ return ReadFromHardware<XCheckTLBFlag::NoException, u64>(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