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