mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #11671 from AdmiralCurtiss/deglobal-interpreter
Deglobalize Interpreter
This commit is contained in:
@@ -19,6 +19,7 @@ struct CachedInterpreter::Instruction
|
||||
{
|
||||
using CommonCallback = void (*)(UGeckoInstruction);
|
||||
using ConditionalCallback = bool (*)(u32);
|
||||
using InterpreterCallback = void (*)(Interpreter&, UGeckoInstruction);
|
||||
|
||||
Instruction() {}
|
||||
Instruction(const CommonCallback c, UGeckoInstruction i)
|
||||
@@ -31,17 +32,24 @@ struct CachedInterpreter::Instruction
|
||||
{
|
||||
}
|
||||
|
||||
Instruction(const InterpreterCallback c, UGeckoInstruction i)
|
||||
: interpreter_callback(c), data(i.hex), type(Type::Interpreter)
|
||||
{
|
||||
}
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Abort,
|
||||
Common,
|
||||
Conditional,
|
||||
Interpreter,
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
const CommonCallback common_callback = nullptr;
|
||||
const ConditionalCallback conditional_callback;
|
||||
const InterpreterCallback interpreter_callback;
|
||||
};
|
||||
|
||||
u32 data = 0;
|
||||
@@ -100,6 +108,11 @@ void CachedInterpreter::ExecuteOneBlock()
|
||||
return;
|
||||
break;
|
||||
|
||||
case Instruction::Type::Interpreter:
|
||||
code->interpreter_callback(Core::System::GetInstance().GetInterpreter(),
|
||||
UGeckoInstruction(code->data));
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG_FMT(POWERPC, "Unknown CachedInterpreter Instruction: {}",
|
||||
static_cast<int>(code->type));
|
||||
|
||||
@@ -27,13 +27,6 @@
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
u32 last_pc;
|
||||
}
|
||||
|
||||
bool Interpreter::m_end_block;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Determines whether or not the given instruction is one where its execution
|
||||
@@ -49,27 +42,34 @@ bool IsPairedSingleInstruction(UGeckoInstruction inst)
|
||||
{
|
||||
return inst.OPCD == 4 || IsPairedSingleQuantizedNonIndexedInstruction(inst);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Checks if a given instruction would be illegal to execute if it's a paired single instruction.
|
||||
//
|
||||
// Paired single instructions are illegal to execute if HID2.PSE is not set.
|
||||
// It's also illegal to execute psq_l, psq_lu, psq_st, and psq_stu if HID2.PSE is enabled,
|
||||
// but HID2.LSQE is not set.
|
||||
bool IsInvalidPairedSingleExecution(UGeckoInstruction inst)
|
||||
bool Interpreter::IsInvalidPairedSingleExecution(UGeckoInstruction inst)
|
||||
{
|
||||
if (!HID2(PowerPC::ppcState).PSE && IsPairedSingleInstruction(inst))
|
||||
if (!HID2(m_ppc_state).PSE && IsPairedSingleInstruction(inst))
|
||||
return true;
|
||||
|
||||
return HID2(PowerPC::ppcState).PSE && !HID2(PowerPC::ppcState).LSQE &&
|
||||
return HID2(m_ppc_state).PSE && !HID2(m_ppc_state).LSQE &&
|
||||
IsPairedSingleQuantizedNonIndexedInstruction(inst);
|
||||
}
|
||||
|
||||
void UpdatePC()
|
||||
void Interpreter::UpdatePC()
|
||||
{
|
||||
last_pc = PowerPC::ppcState.pc;
|
||||
PowerPC::ppcState.pc = PowerPC::ppcState.npc;
|
||||
m_last_pc = m_ppc_state.pc;
|
||||
m_ppc_state.pc = m_ppc_state.npc;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Interpreter::Interpreter(Core::System& system, PowerPC::PowerPCState& ppc_state)
|
||||
: m_system(system), m_ppc_state(ppc_state)
|
||||
{
|
||||
}
|
||||
|
||||
Interpreter::~Interpreter() = default;
|
||||
|
||||
void Interpreter::Init()
|
||||
{
|
||||
@@ -80,44 +80,41 @@ void Interpreter::Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
static bool s_start_trace = false;
|
||||
|
||||
static void Trace(const UGeckoInstruction& inst)
|
||||
void Interpreter::Trace(const UGeckoInstruction& inst)
|
||||
{
|
||||
std::string regs;
|
||||
for (size_t i = 0; i < std::size(PowerPC::ppcState.gpr); i++)
|
||||
for (size_t i = 0; i < std::size(m_ppc_state.gpr); i++)
|
||||
{
|
||||
regs += fmt::format("r{:02d}: {:08x} ", i, PowerPC::ppcState.gpr[i]);
|
||||
regs += fmt::format("r{:02d}: {:08x} ", i, m_ppc_state.gpr[i]);
|
||||
}
|
||||
|
||||
std::string fregs;
|
||||
for (size_t i = 0; i < std::size(PowerPC::ppcState.ps); i++)
|
||||
for (size_t i = 0; i < std::size(m_ppc_state.ps); i++)
|
||||
{
|
||||
const auto& ps = PowerPC::ppcState.ps[i];
|
||||
const auto& ps = m_ppc_state.ps[i];
|
||||
fregs += fmt::format("f{:02d}: {:08x} {:08x} ", i, ps.PS0AsU64(), ps.PS1AsU64());
|
||||
}
|
||||
|
||||
const std::string ppc_inst =
|
||||
Common::GekkoDisassembler::Disassemble(inst.hex, PowerPC::ppcState.pc);
|
||||
const std::string ppc_inst = Common::GekkoDisassembler::Disassemble(inst.hex, m_ppc_state.pc);
|
||||
DEBUG_LOG_FMT(POWERPC,
|
||||
"INTER PC: {:08x} SRR0: {:08x} SRR1: {:08x} CRval: {:016x} "
|
||||
"FPSCR: {:08x} MSR: {:08x} LR: {:08x} {} {:08x} {}",
|
||||
PowerPC::ppcState.pc, SRR0(PowerPC::ppcState), SRR1(PowerPC::ppcState),
|
||||
PowerPC::ppcState.cr.fields[0], PowerPC::ppcState.fpscr.Hex,
|
||||
PowerPC::ppcState.msr.Hex, PowerPC::ppcState.spr[8], regs, inst.hex, ppc_inst);
|
||||
m_ppc_state.pc, SRR0(m_ppc_state), SRR1(m_ppc_state), m_ppc_state.cr.fields[0],
|
||||
m_ppc_state.fpscr.Hex, m_ppc_state.msr.Hex, m_ppc_state.spr[8], regs, inst.hex,
|
||||
ppc_inst);
|
||||
}
|
||||
|
||||
bool Interpreter::HandleFunctionHooking(u32 address)
|
||||
{
|
||||
return HLE::ReplaceFunctionIfPossible(address, [](u32 hook_index, HLE::HookType type) {
|
||||
HLEFunction(hook_index);
|
||||
return HLE::ReplaceFunctionIfPossible(address, [this](u32 hook_index, HLE::HookType type) {
|
||||
HLEFunction(*this, hook_index);
|
||||
return type != HLE::HookType::Start;
|
||||
});
|
||||
}
|
||||
|
||||
int Interpreter::SingleStepInner()
|
||||
{
|
||||
if (HandleFunctionHooking(PowerPC::ppcState.pc))
|
||||
if (HandleFunctionHooking(m_ppc_state.pc))
|
||||
{
|
||||
UpdatePC();
|
||||
// TODO: Does it make sense to use m_prev_inst here?
|
||||
@@ -126,23 +123,23 @@ int Interpreter::SingleStepInner()
|
||||
return PPCTables::GetOpInfo(m_prev_inst)->num_cycles;
|
||||
}
|
||||
|
||||
PowerPC::ppcState.npc = PowerPC::ppcState.pc + sizeof(UGeckoInstruction);
|
||||
m_prev_inst.hex = PowerPC::Read_Opcode(PowerPC::ppcState.pc);
|
||||
m_ppc_state.npc = m_ppc_state.pc + sizeof(UGeckoInstruction);
|
||||
m_prev_inst.hex = PowerPC::Read_Opcode(m_ppc_state.pc);
|
||||
|
||||
const GekkoOPInfo* opinfo = PPCTables::GetOpInfo(m_prev_inst);
|
||||
|
||||
// Uncomment to trace the interpreter
|
||||
// if ((PowerPC::ppcState.pc & 0x00FFFFFF) >= 0x000AB54C &&
|
||||
// (PowerPC::ppcState.pc & 0x00FFFFFF) <= 0x000AB624)
|
||||
// if ((m_ppc_state.pc & 0x00FFFFFF) >= 0x000AB54C &&
|
||||
// (m_ppc_state.pc & 0x00FFFFFF) <= 0x000AB624)
|
||||
// {
|
||||
// s_start_trace = true;
|
||||
// m_start_trace = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// s_start_trace = false;
|
||||
// m_start_trace = false;
|
||||
// }
|
||||
|
||||
if (s_start_trace)
|
||||
if (m_start_trace)
|
||||
{
|
||||
Trace(m_prev_inst);
|
||||
}
|
||||
@@ -154,10 +151,10 @@ int Interpreter::SingleStepInner()
|
||||
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
|
||||
CheckExceptions();
|
||||
}
|
||||
else if (PowerPC::ppcState.msr.FP)
|
||||
else if (m_ppc_state.msr.FP)
|
||||
{
|
||||
RunInterpreterOp(m_prev_inst);
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
RunInterpreterOp(*this, m_prev_inst);
|
||||
if ((m_ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
CheckExceptions();
|
||||
}
|
||||
@@ -167,13 +164,13 @@ int Interpreter::SingleStepInner()
|
||||
// check if we have to generate a FPU unavailable exception or a program exception.
|
||||
if ((opinfo->flags & FL_USE_FPU) != 0)
|
||||
{
|
||||
PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
|
||||
m_ppc_state.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
|
||||
CheckExceptions();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunInterpreterOp(m_prev_inst);
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
RunInterpreterOp(*this, m_prev_inst);
|
||||
if ((m_ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
CheckExceptions();
|
||||
}
|
||||
@@ -189,13 +186,13 @@ int Interpreter::SingleStepInner()
|
||||
UpdatePC();
|
||||
|
||||
PowerPC::UpdatePerformanceMonitor(opinfo->num_cycles, (opinfo->flags & FL_LOADSTORE) != 0,
|
||||
(opinfo->flags & FL_USE_FPU) != 0, PowerPC::ppcState);
|
||||
(opinfo->flags & FL_USE_FPU) != 0, m_ppc_state);
|
||||
return opinfo->num_cycles;
|
||||
}
|
||||
|
||||
void Interpreter::SingleStep()
|
||||
{
|
||||
auto& core_timing = Core::System::GetInstance().GetCoreTiming();
|
||||
auto& core_timing = m_system.GetCoreTiming();
|
||||
auto& core_timing_globals = core_timing.GetGlobals();
|
||||
|
||||
// Declare start of new slice
|
||||
@@ -205,12 +202,12 @@ void Interpreter::SingleStep()
|
||||
|
||||
// The interpreter ignores instruction timing information outside the 'fast runloop'.
|
||||
core_timing_globals.slice_length = 1;
|
||||
PowerPC::ppcState.downcount = 0;
|
||||
m_ppc_state.downcount = 0;
|
||||
|
||||
if (PowerPC::ppcState.Exceptions != 0)
|
||||
if (m_ppc_state.Exceptions != 0)
|
||||
{
|
||||
PowerPC::CheckExceptions();
|
||||
PowerPC::ppcState.pc = PowerPC::ppcState.npc;
|
||||
m_ppc_state.pc = m_ppc_state.npc;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +222,8 @@ constexpr u32 s_show_steps = 300;
|
||||
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared).
|
||||
void Interpreter::Run()
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& core_timing = system.GetCoreTiming();
|
||||
auto& cpu = system.GetCPU();
|
||||
auto& core_timing = m_system.GetCoreTiming();
|
||||
auto& cpu = m_system.GetCPU();
|
||||
while (cpu.GetState() == CPU::State::Running)
|
||||
{
|
||||
// CoreTiming Advance() ends the previous slice and declares the start of the next
|
||||
@@ -239,27 +235,27 @@ void Interpreter::Run()
|
||||
if (Config::Get(Config::MAIN_ENABLE_DEBUGGING))
|
||||
{
|
||||
#ifdef SHOW_HISTORY
|
||||
s_pc_block_vec.push_back(PowerPC::ppcState.pc);
|
||||
s_pc_block_vec.push_back(m_ppc_state.pc);
|
||||
if (s_pc_block_vec.size() > s_show_blocks)
|
||||
s_pc_block_vec.erase(s_pc_block_vec.begin());
|
||||
#endif
|
||||
|
||||
// Debugging friendly version of inner loop. Tries to do the timing as similarly to the
|
||||
// JIT as possible. Does not take into account that some instructions take multiple cycles.
|
||||
while (PowerPC::ppcState.downcount > 0)
|
||||
while (m_ppc_state.downcount > 0)
|
||||
{
|
||||
m_end_block = false;
|
||||
int cycles = 0;
|
||||
while (!m_end_block)
|
||||
{
|
||||
#ifdef SHOW_HISTORY
|
||||
s_pc_vec.push_back(PowerPC::ppcState.pc);
|
||||
s_pc_vec.push_back(m_ppc_state.pc);
|
||||
if (s_pc_vec.size() > s_show_steps)
|
||||
s_pc_vec.erase(s_pc_vec.begin());
|
||||
#endif
|
||||
|
||||
// 2: check for breakpoint
|
||||
if (PowerPC::breakpoints.IsAddressBreakPoint(PowerPC::ppcState.pc))
|
||||
if (PowerPC::breakpoints.IsAddressBreakPoint(m_ppc_state.pc))
|
||||
{
|
||||
#ifdef SHOW_HISTORY
|
||||
NOTICE_LOG_FMT(POWERPC, "----------------------------");
|
||||
@@ -280,25 +276,25 @@ void Interpreter::Run()
|
||||
NOTICE_LOG_FMT(POWERPC, "PC: {:#010x}", s_pc_vec[j]);
|
||||
}
|
||||
#endif
|
||||
INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", PowerPC::ppcState.pc);
|
||||
INFO_LOG_FMT(POWERPC, "Hit Breakpoint - {:08x}", m_ppc_state.pc);
|
||||
cpu.Break();
|
||||
if (GDBStub::IsActive())
|
||||
GDBStub::TakeControl();
|
||||
if (PowerPC::breakpoints.IsTempBreakPoint(PowerPC::ppcState.pc))
|
||||
PowerPC::breakpoints.Remove(PowerPC::ppcState.pc);
|
||||
if (PowerPC::breakpoints.IsTempBreakPoint(m_ppc_state.pc))
|
||||
PowerPC::breakpoints.Remove(m_ppc_state.pc);
|
||||
|
||||
Host_UpdateDisasmDialog();
|
||||
return;
|
||||
}
|
||||
cycles += SingleStepInner();
|
||||
}
|
||||
PowerPC::ppcState.downcount -= cycles;
|
||||
m_ppc_state.downcount -= cycles;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// "fast" version of inner loop. well, it's not so fast.
|
||||
while (PowerPC::ppcState.downcount > 0)
|
||||
while (m_ppc_state.downcount > 0)
|
||||
{
|
||||
m_end_block = false;
|
||||
|
||||
@@ -307,36 +303,39 @@ void Interpreter::Run()
|
||||
{
|
||||
cycles += SingleStepInner();
|
||||
}
|
||||
PowerPC::ppcState.downcount -= cycles;
|
||||
m_ppc_state.downcount -= cycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::unknown_instruction(UGeckoInstruction inst)
|
||||
void Interpreter::unknown_instruction(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
ASSERT(Core::IsCPUThread());
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& system = interpreter.m_system;
|
||||
Core::CPUThreadGuard guard(system);
|
||||
|
||||
const u32 last_pc = interpreter.m_last_pc;
|
||||
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(system, guard, Common::Log::LogType::POWERPC,
|
||||
Common::Log::LogLevel::LNOTICE);
|
||||
|
||||
const auto& ppc_state = interpreter.m_ppc_state;
|
||||
NOTICE_LOG_FMT(
|
||||
POWERPC,
|
||||
"\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, ppc_state.pc, last_pc, LR(ppc_state));
|
||||
for (int i = 0; i < 32; i += 4)
|
||||
{
|
||||
NOTICE_LOG_FMT(POWERPC, "r{}: {:#010x} r{}: {:#010x} r{}: {:#010x} r{}: {:#010x}", i,
|
||||
PowerPC::ppcState.gpr[i], i + 1, PowerPC::ppcState.gpr[i + 1], i + 2,
|
||||
PowerPC::ppcState.gpr[i + 2], i + 3, PowerPC::ppcState.gpr[i + 3]);
|
||||
ppc_state.gpr[i], i + 1, ppc_state.gpr[i + 1], i + 2, ppc_state.gpr[i + 2],
|
||||
i + 3, ppc_state.gpr[i + 3]);
|
||||
}
|
||||
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));
|
||||
inst.hex, ppc_state.pc, last_pc, LR(ppc_state));
|
||||
if (system.IsPauseOnPanicMode())
|
||||
system.GetCPU().Break();
|
||||
}
|
||||
@@ -360,9 +359,3 @@ const char* Interpreter::GetName() const
|
||||
return "Interpreter32";
|
||||
#endif
|
||||
}
|
||||
|
||||
Interpreter* Interpreter::getInstance()
|
||||
{
|
||||
static Interpreter instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,101 +12,110 @@
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
void Interpreter::bx(UGeckoInstruction inst)
|
||||
void Interpreter::bx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
if (inst.LK)
|
||||
LR(PowerPC::ppcState) = PowerPC::ppcState.pc + 4;
|
||||
LR(ppc_state) = ppc_state.pc + 4;
|
||||
|
||||
const auto address = u32(SignExt26(inst.LI << 2));
|
||||
|
||||
if (inst.AA)
|
||||
PowerPC::ppcState.npc = address;
|
||||
ppc_state.npc = address;
|
||||
else
|
||||
PowerPC::ppcState.npc = PowerPC::ppcState.pc + address;
|
||||
ppc_state.npc = ppc_state.pc + address;
|
||||
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
// bcx - ugly, straight from PPC manual equations :)
|
||||
void Interpreter::bcx(UGeckoInstruction inst)
|
||||
void Interpreter::bcx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
CTR(PowerPC::ppcState)--;
|
||||
CTR(ppc_state)--;
|
||||
|
||||
const bool true_false = ((inst.BO >> 3) & 1) != 0;
|
||||
const bool only_counter_check = ((inst.BO >> 4) & 1) != 0;
|
||||
const bool only_condition_check = ((inst.BO >> 2) & 1) != 0;
|
||||
const u32 ctr_check = ((CTR(PowerPC::ppcState) != 0) ^ (inst.BO >> 1)) & 1;
|
||||
const u32 ctr_check = ((CTR(ppc_state) != 0) ^ (inst.BO >> 1)) & 1;
|
||||
const bool counter = only_condition_check || ctr_check != 0;
|
||||
const bool condition =
|
||||
only_counter_check || (PowerPC::ppcState.cr.GetBit(inst.BI) == u32(true_false));
|
||||
const bool condition = only_counter_check || (ppc_state.cr.GetBit(inst.BI) == u32(true_false));
|
||||
|
||||
if (counter && condition)
|
||||
{
|
||||
if (inst.LK)
|
||||
LR(PowerPC::ppcState) = PowerPC::ppcState.pc + 4;
|
||||
LR(ppc_state) = ppc_state.pc + 4;
|
||||
|
||||
const auto address = u32(SignExt16(s16(inst.BD << 2)));
|
||||
|
||||
if (inst.AA)
|
||||
PowerPC::ppcState.npc = address;
|
||||
ppc_state.npc = address;
|
||||
else
|
||||
PowerPC::ppcState.npc = PowerPC::ppcState.pc + address;
|
||||
ppc_state.npc = ppc_state.pc + address;
|
||||
}
|
||||
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
void Interpreter::bcctrx(UGeckoInstruction inst)
|
||||
void Interpreter::bcctrx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
DEBUG_ASSERT_MSG(POWERPC, (inst.BO_2 & BO_DONT_DECREMENT_FLAG) != 0,
|
||||
"bcctrx with decrement and test CTR option is invalid!");
|
||||
|
||||
const u32 condition =
|
||||
((inst.BO_2 >> 4) | (PowerPC::ppcState.cr.GetBit(inst.BI_2) == ((inst.BO_2 >> 3) & 1))) & 1;
|
||||
((inst.BO_2 >> 4) | (ppc_state.cr.GetBit(inst.BI_2) == ((inst.BO_2 >> 3) & 1))) & 1;
|
||||
|
||||
if (condition != 0)
|
||||
{
|
||||
PowerPC::ppcState.npc = CTR(PowerPC::ppcState) & (~3);
|
||||
ppc_state.npc = CTR(ppc_state) & (~3);
|
||||
if (inst.LK_3)
|
||||
LR(PowerPC::ppcState) = PowerPC::ppcState.pc + 4;
|
||||
LR(ppc_state) = ppc_state.pc + 4;
|
||||
}
|
||||
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
void Interpreter::bclrx(UGeckoInstruction inst)
|
||||
void Interpreter::bclrx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if ((inst.BO_2 & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
CTR(PowerPC::ppcState)--;
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
const u32 counter = ((inst.BO_2 >> 2) | ((CTR(PowerPC::ppcState) != 0) ^ (inst.BO_2 >> 1))) & 1;
|
||||
if ((inst.BO_2 & BO_DONT_DECREMENT_FLAG) == 0)
|
||||
CTR(ppc_state)--;
|
||||
|
||||
const u32 counter = ((inst.BO_2 >> 2) | ((CTR(ppc_state) != 0) ^ (inst.BO_2 >> 1))) & 1;
|
||||
const u32 condition =
|
||||
((inst.BO_2 >> 4) | (PowerPC::ppcState.cr.GetBit(inst.BI_2) == ((inst.BO_2 >> 3) & 1))) & 1;
|
||||
((inst.BO_2 >> 4) | (ppc_state.cr.GetBit(inst.BI_2) == ((inst.BO_2 >> 3) & 1))) & 1;
|
||||
|
||||
if ((counter & condition) != 0)
|
||||
{
|
||||
PowerPC::ppcState.npc = LR(PowerPC::ppcState) & (~3);
|
||||
ppc_state.npc = LR(ppc_state) & (~3);
|
||||
if (inst.LK_3)
|
||||
LR(PowerPC::ppcState) = PowerPC::ppcState.pc + 4;
|
||||
LR(ppc_state) = ppc_state.pc + 4;
|
||||
}
|
||||
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
void Interpreter::HLEFunction(UGeckoInstruction inst)
|
||||
void Interpreter::HLEFunction(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
|
||||
ASSERT(Core::IsCPUThread());
|
||||
Core::CPUThreadGuard guard(Core::System::GetInstance());
|
||||
Core::CPUThreadGuard guard(interpreter.m_system);
|
||||
|
||||
HLE::Execute(guard, PowerPC::ppcState.pc, inst.hex);
|
||||
HLE::Execute(guard, interpreter.m_ppc_state.pc, inst.hex);
|
||||
}
|
||||
|
||||
void Interpreter::rfi(UGeckoInstruction inst)
|
||||
void Interpreter::rfi(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if (PowerPC::ppcState.msr.PR)
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
if (ppc_state.msr.PR)
|
||||
{
|
||||
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
|
||||
return;
|
||||
@@ -115,26 +124,27 @@ void Interpreter::rfi(UGeckoInstruction inst)
|
||||
// Restore saved bits from SRR1 to MSR.
|
||||
// Gecko/Broadway can save more bits than explicitly defined in ppc spec
|
||||
const u32 mask = 0x87C0FFFF;
|
||||
PowerPC::ppcState.msr.Hex =
|
||||
(PowerPC::ppcState.msr.Hex & ~mask) | (SRR1(PowerPC::ppcState) & mask);
|
||||
ppc_state.msr.Hex = (ppc_state.msr.Hex & ~mask) | (SRR1(ppc_state) & mask);
|
||||
// MSR[13] is set to 0.
|
||||
PowerPC::ppcState.msr.Hex &= 0xFFFBFFFF;
|
||||
ppc_state.msr.Hex &= 0xFFFBFFFF;
|
||||
// Here we should check if there are pending exceptions, and if their corresponding enable bits
|
||||
// are set
|
||||
// if above is true, we'd do:
|
||||
// PowerPC::CheckExceptions();
|
||||
// else
|
||||
// set NPC to saved offset and resume
|
||||
PowerPC::ppcState.npc = SRR0(PowerPC::ppcState);
|
||||
m_end_block = true;
|
||||
ppc_state.npc = SRR0(ppc_state);
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
// sc isn't really used for anything important in GameCube games (just for a write barrier) so we
|
||||
// really don't have to emulate it.
|
||||
// We do it anyway, though :P
|
||||
void Interpreter::sc(UGeckoInstruction inst)
|
||||
void Interpreter::sc(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
PowerPC::ppcState.Exceptions |= EXCEPTION_SYSCALL;
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
|
||||
ppc_state.Exceptions |= EXCEPTION_SYSCALL;
|
||||
PowerPC::CheckExceptions();
|
||||
m_end_block = true;
|
||||
interpreter.m_end_block = true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -308,104 +308,112 @@ static void Helper_Dequantize(PowerPC::PowerPCState* ppcs, u32 addr, u32 instI,
|
||||
ppcs->ps[instRD].SetBoth(ps0, ps1);
|
||||
}
|
||||
|
||||
void Interpreter::psq_l(UGeckoInstruction inst)
|
||||
void Interpreter::psq_l(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if (HID2(PowerPC::ppcState).LSQE == 0)
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
if (HID2(ppc_state).LSQE == 0)
|
||||
{
|
||||
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 EA = inst.RA ? (PowerPC::ppcState.gpr[inst.RA] + u32(inst.SIMM_12)) : u32(inst.SIMM_12);
|
||||
Helper_Dequantize(&PowerPC::ppcState, EA, inst.I, inst.RD, inst.W);
|
||||
const u32 EA = inst.RA ? (ppc_state.gpr[inst.RA] + u32(inst.SIMM_12)) : u32(inst.SIMM_12);
|
||||
Helper_Dequantize(&ppc_state, EA, inst.I, inst.RD, inst.W);
|
||||
}
|
||||
|
||||
void Interpreter::psq_lu(UGeckoInstruction inst)
|
||||
void Interpreter::psq_lu(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if (HID2(PowerPC::ppcState).LSQE == 0)
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
if (HID2(ppc_state).LSQE == 0)
|
||||
{
|
||||
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 EA = PowerPC::ppcState.gpr[inst.RA] + u32(inst.SIMM_12);
|
||||
Helper_Dequantize(&PowerPC::ppcState, EA, inst.I, inst.RD, inst.W);
|
||||
const u32 EA = ppc_state.gpr[inst.RA] + u32(inst.SIMM_12);
|
||||
Helper_Dequantize(&ppc_state, EA, inst.I, inst.RD, inst.W);
|
||||
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
if ((ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PowerPC::ppcState.gpr[inst.RA] = EA;
|
||||
ppc_state.gpr[inst.RA] = EA;
|
||||
}
|
||||
|
||||
void Interpreter::psq_st(UGeckoInstruction inst)
|
||||
void Interpreter::psq_st(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if (HID2(PowerPC::ppcState).LSQE == 0)
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
if (HID2(ppc_state).LSQE == 0)
|
||||
{
|
||||
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 EA = inst.RA ? (PowerPC::ppcState.gpr[inst.RA] + u32(inst.SIMM_12)) : u32(inst.SIMM_12);
|
||||
Helper_Quantize(&PowerPC::ppcState, EA, inst.I, inst.RS, inst.W);
|
||||
const u32 EA = inst.RA ? (ppc_state.gpr[inst.RA] + u32(inst.SIMM_12)) : u32(inst.SIMM_12);
|
||||
Helper_Quantize(&ppc_state, EA, inst.I, inst.RS, inst.W);
|
||||
}
|
||||
|
||||
void Interpreter::psq_stu(UGeckoInstruction inst)
|
||||
void Interpreter::psq_stu(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
if (HID2(PowerPC::ppcState).LSQE == 0)
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
if (HID2(ppc_state).LSQE == 0)
|
||||
{
|
||||
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 EA = PowerPC::ppcState.gpr[inst.RA] + u32(inst.SIMM_12);
|
||||
Helper_Quantize(&PowerPC::ppcState, EA, inst.I, inst.RS, inst.W);
|
||||
const u32 EA = ppc_state.gpr[inst.RA] + u32(inst.SIMM_12);
|
||||
Helper_Quantize(&ppc_state, EA, inst.I, inst.RS, inst.W);
|
||||
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
if ((ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PowerPC::ppcState.gpr[inst.RA] = EA;
|
||||
ppc_state.gpr[inst.RA] = EA;
|
||||
}
|
||||
|
||||
void Interpreter::psq_lx(UGeckoInstruction inst)
|
||||
void Interpreter::psq_lx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
const u32 EA = inst.RA ? (PowerPC::ppcState.gpr[inst.RA] + PowerPC::ppcState.gpr[inst.RB]) :
|
||||
PowerPC::ppcState.gpr[inst.RB];
|
||||
Helper_Dequantize(&PowerPC::ppcState, EA, inst.Ix, inst.RD, inst.Wx);
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
const u32 EA =
|
||||
inst.RA ? (ppc_state.gpr[inst.RA] + ppc_state.gpr[inst.RB]) : ppc_state.gpr[inst.RB];
|
||||
Helper_Dequantize(&ppc_state, EA, inst.Ix, inst.RD, inst.Wx);
|
||||
}
|
||||
|
||||
void Interpreter::psq_stx(UGeckoInstruction inst)
|
||||
void Interpreter::psq_stx(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
const u32 EA = inst.RA ? (PowerPC::ppcState.gpr[inst.RA] + PowerPC::ppcState.gpr[inst.RB]) :
|
||||
PowerPC::ppcState.gpr[inst.RB];
|
||||
Helper_Quantize(&PowerPC::ppcState, EA, inst.Ix, inst.RS, inst.Wx);
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
const u32 EA =
|
||||
inst.RA ? (ppc_state.gpr[inst.RA] + ppc_state.gpr[inst.RB]) : ppc_state.gpr[inst.RB];
|
||||
Helper_Quantize(&ppc_state, EA, inst.Ix, inst.RS, inst.Wx);
|
||||
}
|
||||
|
||||
void Interpreter::psq_lux(UGeckoInstruction inst)
|
||||
void Interpreter::psq_lux(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
const u32 EA = PowerPC::ppcState.gpr[inst.RA] + PowerPC::ppcState.gpr[inst.RB];
|
||||
Helper_Dequantize(&PowerPC::ppcState, EA, inst.Ix, inst.RD, inst.Wx);
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
const u32 EA = ppc_state.gpr[inst.RA] + ppc_state.gpr[inst.RB];
|
||||
Helper_Dequantize(&ppc_state, EA, inst.Ix, inst.RD, inst.Wx);
|
||||
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
if ((ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PowerPC::ppcState.gpr[inst.RA] = EA;
|
||||
ppc_state.gpr[inst.RA] = EA;
|
||||
}
|
||||
|
||||
void Interpreter::psq_stux(UGeckoInstruction inst)
|
||||
void Interpreter::psq_stux(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
const u32 EA = PowerPC::ppcState.gpr[inst.RA] + PowerPC::ppcState.gpr[inst.RB];
|
||||
Helper_Quantize(&PowerPC::ppcState, EA, inst.Ix, inst.RS, inst.Wx);
|
||||
auto& ppc_state = interpreter.m_ppc_state;
|
||||
const u32 EA = ppc_state.gpr[inst.RA] + ppc_state.gpr[inst.RB];
|
||||
Helper_Quantize(&ppc_state, EA, inst.Ix, inst.RS, inst.Wx);
|
||||
|
||||
if ((PowerPC::ppcState.Exceptions & EXCEPTION_DSI) != 0)
|
||||
if ((ppc_state.Exceptions & EXCEPTION_DSI) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PowerPC::ppcState.gpr[inst.RA] = EA;
|
||||
ppc_state.gpr[inst.RA] = EA;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -468,29 +468,29 @@ Interpreter::Instruction Interpreter::GetInterpreterOp(UGeckoInstruction inst)
|
||||
return result;
|
||||
}
|
||||
|
||||
void Interpreter::RunInterpreterOp(UGeckoInstruction inst)
|
||||
void Interpreter::RunInterpreterOp(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
// Will handle subtables using RunTable4 etc.
|
||||
s_interpreter_op_table[inst.OPCD](inst);
|
||||
s_interpreter_op_table[inst.OPCD](interpreter, inst);
|
||||
}
|
||||
|
||||
void Interpreter::RunTable4(UGeckoInstruction inst)
|
||||
void Interpreter::RunTable4(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
s_interpreter_op_table4[inst.SUBOP10](inst);
|
||||
s_interpreter_op_table4[inst.SUBOP10](interpreter, inst);
|
||||
}
|
||||
void Interpreter::RunTable19(UGeckoInstruction inst)
|
||||
void Interpreter::RunTable19(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
s_interpreter_op_table19[inst.SUBOP10](inst);
|
||||
s_interpreter_op_table19[inst.SUBOP10](interpreter, inst);
|
||||
}
|
||||
void Interpreter::RunTable31(UGeckoInstruction inst)
|
||||
void Interpreter::RunTable31(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
s_interpreter_op_table31[inst.SUBOP10](inst);
|
||||
s_interpreter_op_table31[inst.SUBOP10](interpreter, inst);
|
||||
}
|
||||
void Interpreter::RunTable59(UGeckoInstruction inst)
|
||||
void Interpreter::RunTable59(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
s_interpreter_op_table59[inst.SUBOP5](inst);
|
||||
s_interpreter_op_table59[inst.SUBOP5](interpreter, inst);
|
||||
}
|
||||
void Interpreter::RunTable63(UGeckoInstruction inst)
|
||||
void Interpreter::RunTable63(Interpreter& interpreter, UGeckoInstruction inst)
|
||||
{
|
||||
s_interpreter_op_table63[inst.SUBOP10](inst);
|
||||
s_interpreter_op_table63[inst.SUBOP10](interpreter, inst);
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
|
||||
|
||||
Interpreter::Instruction instr = Interpreter::GetInterpreterOp(inst);
|
||||
ABI_PushRegistersAndAdjustStack({}, 0);
|
||||
ABI_CallFunctionC(instr, inst.hex);
|
||||
ABI_CallFunctionPC(instr, &Core::System::GetInstance().GetInterpreter(), inst.hex);
|
||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||
|
||||
// If the instruction wrote to any registers which were marked as discarded,
|
||||
|
||||
@@ -199,7 +199,8 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
|
||||
|
||||
Interpreter::Instruction instr = Interpreter::GetInterpreterOp(inst);
|
||||
MOVP2R(ARM64Reg::X8, instr);
|
||||
MOVI2R(ARM64Reg::W0, inst.hex);
|
||||
MOVP2R(ARM64Reg::W0, &Core::System::GetInstance().GetInterpreter());
|
||||
MOVI2R(ARM64Reg::W1, inst.hex);
|
||||
BLR(ARM64Reg::X8);
|
||||
|
||||
// If the instruction wrote to any registers which were marked as discarded,
|
||||
|
||||
@@ -40,7 +40,6 @@ PowerPCState ppcState;
|
||||
|
||||
static CPUCoreBase* s_cpu_core_base = nullptr;
|
||||
static bool s_cpu_core_base_is_injected = false;
|
||||
Interpreter* const s_interpreter = Interpreter::getInstance();
|
||||
static CoreMode s_mode = CoreMode::Interpreter;
|
||||
|
||||
BreakPoints breakpoints;
|
||||
@@ -220,12 +219,13 @@ static void InitializeCPUCore(CPUCore cpu_core)
|
||||
{
|
||||
// We initialize the interpreter because
|
||||
// it is used on boot and code window independently.
|
||||
s_interpreter->Init();
|
||||
auto& interpreter = Core::System::GetInstance().GetInterpreter();
|
||||
interpreter.Init();
|
||||
|
||||
switch (cpu_core)
|
||||
{
|
||||
case CPUCore::Interpreter:
|
||||
s_cpu_core_base = s_interpreter;
|
||||
s_cpu_core_base = &interpreter;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -239,7 +239,7 @@ static void InitializeCPUCore(CPUCore cpu_core)
|
||||
break;
|
||||
}
|
||||
|
||||
s_mode = s_cpu_core_base == s_interpreter ? CoreMode::Interpreter : CoreMode::JIT;
|
||||
s_mode = s_cpu_core_base == &interpreter ? CoreMode::Interpreter : CoreMode::JIT;
|
||||
}
|
||||
|
||||
const std::vector<CPUCore>& AvailableCPUCores()
|
||||
@@ -316,7 +316,8 @@ void Shutdown()
|
||||
{
|
||||
InjectExternalCPUCore(nullptr);
|
||||
JitInterface::Shutdown();
|
||||
s_interpreter->Shutdown();
|
||||
auto& interpreter = Core::System::GetInstance().GetInterpreter();
|
||||
interpreter.Shutdown();
|
||||
s_cpu_core_base = nullptr;
|
||||
}
|
||||
|
||||
@@ -327,17 +328,19 @@ CoreMode GetMode()
|
||||
|
||||
static void ApplyMode()
|
||||
{
|
||||
auto& interpreter = Core::System::GetInstance().GetInterpreter();
|
||||
|
||||
switch (s_mode)
|
||||
{
|
||||
case CoreMode::Interpreter: // Switching from JIT to interpreter
|
||||
s_cpu_core_base = s_interpreter;
|
||||
s_cpu_core_base = &interpreter;
|
||||
break;
|
||||
|
||||
case CoreMode::JIT: // Switching from interpreter to JIT.
|
||||
// Don't really need to do much. It'll work, the cache will refill itself.
|
||||
s_cpu_core_base = JitInterface::GetCore();
|
||||
if (!s_cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host
|
||||
s_cpu_core_base = s_interpreter;
|
||||
s_cpu_core_base = &interpreter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/Sram.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "IOS/USB/Emulated/Skylander.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
@@ -39,7 +40,7 @@ struct System::Impl
|
||||
: m_audio_interface(system), m_core_timing(system), m_dsp(system), m_dvd_interface(system),
|
||||
m_dvd_thread(system), m_expansion_interface(system), m_gp_fifo(system), m_memory(system),
|
||||
m_ppc_state(PowerPC::ppcState), m_processor_interface(system), m_serial_interface(system),
|
||||
m_video_interface(system)
|
||||
m_video_interface(system), m_interpreter(system, m_ppc_state)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,6 +71,7 @@ struct System::Impl
|
||||
Sram m_sram;
|
||||
VertexShaderManager m_vertex_shader_manager;
|
||||
VideoInterface::VideoInterfaceManager m_video_interface;
|
||||
Interpreter m_interpreter;
|
||||
};
|
||||
|
||||
System::System() : m_impl{std::make_unique<Impl>(*this)}
|
||||
@@ -175,6 +177,11 @@ HSP::HSPManager& System::GetHSP() const
|
||||
return m_impl->m_hsp;
|
||||
}
|
||||
|
||||
Interpreter& System::GetInterpreter() const
|
||||
{
|
||||
return m_impl->m_interpreter;
|
||||
}
|
||||
|
||||
IOS::HLE::USB::SkylanderPortal& System::GetSkylanderPortal() const
|
||||
{
|
||||
return m_impl->m_skylander_portal;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
|
||||
class GeometryShaderManager;
|
||||
class Interpreter;
|
||||
class PixelShaderManager;
|
||||
class SoundStream;
|
||||
struct Sram;
|
||||
@@ -131,6 +132,7 @@ public:
|
||||
GeometryShaderManager& GetGeometryShaderManager() const;
|
||||
GPFifo::GPFifoManager& GetGPFifo() const;
|
||||
HSP::HSPManager& GetHSP() const;
|
||||
Interpreter& GetInterpreter() const;
|
||||
IOS::HLE::USB::SkylanderPortal& GetSkylanderPortal() const;
|
||||
Memory::MemoryManager& GetMemory() const;
|
||||
MemoryInterface::MemoryInterfaceManager& GetMemoryInterface() const;
|
||||
|
||||
Reference in New Issue
Block a user