220 changes: 98 additions & 122 deletions Source/Core/Core/HW/CPU.cpp

Large diffs are not rendered by default.

166 changes: 108 additions & 58 deletions Source/Core/Core/HW/CPU.h
Expand Up @@ -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
{
Expand All @@ -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
4 changes: 2 additions & 2 deletions Source/Core/Core/HW/HW.cpp
Expand Up @@ -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)
Expand All @@ -71,7 +71,7 @@ void Shutdown()
IOS::Shutdown();

SystemTimers::Shutdown();
CPU::Shutdown();
system.GetCPU().Shutdown();
DVDInterface::Shutdown();
DSP::Shutdown();
MemoryInterface::Shutdown();
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/HW/VideoInterface.cpp
Expand Up @@ -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!

Expand Down
9 changes: 6 additions & 3 deletions Source/Core/Core/Movie.cpp
Expand Up @@ -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);
}
});
Expand Down Expand Up @@ -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;
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
13 changes: 8 additions & 5 deletions Source/Core/Core/PowerPC/GDBStub.cpp
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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();
Expand All @@ -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':
Expand Down
14 changes: 8 additions & 6 deletions Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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",
Expand All @@ -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()
Expand Down
12 changes: 7 additions & 5 deletions Source/Core/Core/PowerPC/Jit64/Jit.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 |
Expand Down Expand Up @@ -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();
Expand All @@ -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);

Expand Down
7 changes: 4 additions & 3 deletions Source/Core/Core/PowerPC/Jit64/JitAsm.cpp
Expand Up @@ -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);
}
Expand All @@ -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.
Expand Down Expand Up @@ -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);

Expand Down
14 changes: 9 additions & 5 deletions Source/Core/Core/PowerPC/JitArm64/Jit.cpp
Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down
8 changes: 5 additions & 3 deletions Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp
Expand Up @@ -81,19 +81,21 @@ 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);
}

dispatcher_no_check = GetCodePtr();

bool assembly_dispatcher = true;

auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();

if (assembly_dispatcher)
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/Core/PowerPC/JitCommon/JitBase.cpp
Expand Up @@ -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++)
Expand Down
76 changes: 41 additions & 35 deletions Source/Core/Core/PowerPC/MMU.cpp
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
}
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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();
Expand All @@ -602,35 +607,35 @@ 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;
}

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;
}

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;
}

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;
}

Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 8 additions & 4 deletions Source/Core/Core/PowerPC/PowerPC.cpp
Expand Up @@ -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
{
Expand Down Expand Up @@ -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();
}
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/Core/System.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions Source/Core/Core/System.h
Expand Up @@ -15,6 +15,10 @@ namespace AudioInterface
{
class AudioInterfaceState;
};
namespace CPU
{
class CPUManager;
}
namespace CommandProcessor
{
class CommandProcessorManager;
Expand Down Expand Up @@ -119,6 +123,7 @@ class System
void SetAudioDumpStarted(bool started);

AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const;
CPU::CPUManager& GetCPU() const;
CoreTiming::CoreTimingManager& GetCoreTiming() const;
CommandProcessor::CommandProcessorManager& GetCommandProcessor() const;
DSP::DSPState& GetDSPState() const;
Expand Down
19 changes: 14 additions & 5 deletions Source/Core/DolphinQt/Debugger/CodeWidget.cpp
Expand Up @@ -435,15 +435,18 @@ 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;

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);
Expand All @@ -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 = [] {
Expand All @@ -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
Expand All @@ -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
Expand Down