mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
CPU/Recompiler: Implement add/addu/addi
This commit is contained in:
parent
641e68db95
commit
f14ad1d3c4
|
@ -105,8 +105,9 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
|
||||||
result = Compile_lui(cbi);
|
result = Compile_lui(cbi);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InstructionOp::addi:
|
||||||
case InstructionOp::addiu:
|
case InstructionOp::addiu:
|
||||||
result = Compile_addiu(cbi);
|
result = Compile_Add(cbi);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstructionOp::funct:
|
case InstructionOp::funct:
|
||||||
|
@ -132,6 +133,11 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
|
||||||
result = Compile_MoveHiLo(cbi);
|
result = Compile_MoveHiLo(cbi);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InstructionFunct::add:
|
||||||
|
case InstructionFunct::addu:
|
||||||
|
result = Compile_Add(cbi);
|
||||||
|
break;
|
||||||
|
|
||||||
case InstructionFunct::mult:
|
case InstructionFunct::mult:
|
||||||
case InstructionFunct::multu:
|
case InstructionFunct::multu:
|
||||||
result = Compile_Multiply(cbi);
|
result = Compile_Multiply(cbi);
|
||||||
|
@ -261,10 +267,10 @@ void CodeGenerator::ConvertValueSizeInPlace(Value* value, RegSize size, bool sig
|
||||||
value->size = size;
|
value->size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
|
Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs, bool set_flags)
|
||||||
{
|
{
|
||||||
DebugAssert(lhs.size == rhs.size);
|
DebugAssert(lhs.size == rhs.size);
|
||||||
if (lhs.IsConstant() && rhs.IsConstant())
|
if (lhs.IsConstant() && rhs.IsConstant() && !set_flags)
|
||||||
{
|
{
|
||||||
// compile-time
|
// compile-time
|
||||||
u64 new_cv = lhs.constant_value + rhs.constant_value;
|
u64 new_cv = lhs.constant_value + rhs.constant_value;
|
||||||
|
@ -288,12 +294,12 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
Value res = m_register_cache.AllocateScratch(lhs.size);
|
Value res = m_register_cache.AllocateScratch(lhs.size);
|
||||||
if (lhs.HasConstantValue(0))
|
if (lhs.HasConstantValue(0) && !set_flags)
|
||||||
{
|
{
|
||||||
EmitCopyValue(res.host_reg, rhs);
|
EmitCopyValue(res.host_reg, rhs);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
else if (rhs.HasConstantValue(0))
|
else if (rhs.HasConstantValue(0) && !set_flags)
|
||||||
{
|
{
|
||||||
EmitCopyValue(res.host_reg, lhs);
|
EmitCopyValue(res.host_reg, lhs);
|
||||||
return res;
|
return res;
|
||||||
|
@ -301,7 +307,7 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitCopyValue(res.host_reg, lhs);
|
EmitCopyValue(res.host_reg, lhs);
|
||||||
EmitAdd(res.host_reg, rhs);
|
EmitAdd(res.host_reg, rhs, set_flags);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -903,7 +909,7 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi)
|
||||||
// rt <- mem[rs + sext(imm)]
|
// rt <- mem[rs + sext(imm)]
|
||||||
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
||||||
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
||||||
Value address = AddValues(base, offset);
|
Value address = AddValues(base, offset, false);
|
||||||
|
|
||||||
Value result;
|
Value result;
|
||||||
switch (cbi.instruction.op)
|
switch (cbi.instruction.op)
|
||||||
|
@ -942,7 +948,7 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi)
|
||||||
// mem[rs + sext(imm)] <- rt
|
// mem[rs + sext(imm)] <- rt
|
||||||
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
||||||
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
||||||
Value address = AddValues(base, offset);
|
Value address = AddValues(base, offset, false);
|
||||||
Value value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt);
|
Value value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt);
|
||||||
|
|
||||||
switch (cbi.instruction.op)
|
switch (cbi.instruction.op)
|
||||||
|
@ -999,6 +1005,52 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CodeGenerator::Compile_Add(const CodeBlockInstruction& cbi)
|
||||||
|
{
|
||||||
|
InstructionPrologue(cbi, 1);
|
||||||
|
|
||||||
|
const bool check_overflow =
|
||||||
|
(cbi.instruction.op == InstructionOp::addi ||
|
||||||
|
(cbi.instruction.op == InstructionOp::funct && cbi.instruction.r.funct == InstructionFunct::add));
|
||||||
|
|
||||||
|
Value lhs, rhs;
|
||||||
|
Reg dest;
|
||||||
|
switch (cbi.instruction.op)
|
||||||
|
{
|
||||||
|
case InstructionOp::addi:
|
||||||
|
case InstructionOp::addiu:
|
||||||
|
{
|
||||||
|
// rt <- rs + sext(imm)
|
||||||
|
dest = cbi.instruction.i.rt;
|
||||||
|
lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
||||||
|
rhs = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstructionOp::funct:
|
||||||
|
{
|
||||||
|
Assert(cbi.instruction.r.funct == InstructionFunct::add || cbi.instruction.r.funct == InstructionFunct::addu);
|
||||||
|
dest = cbi.instruction.r.rd;
|
||||||
|
lhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs);
|
||||||
|
rhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value result = AddValues(lhs, rhs, check_overflow);
|
||||||
|
if (check_overflow)
|
||||||
|
EmitRaiseException(Exception::Ov, Condition::Overflow);
|
||||||
|
|
||||||
|
m_register_cache.WriteGuestRegister(dest, std::move(result));
|
||||||
|
|
||||||
|
InstructionEpilogue(cbi);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi)
|
bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi)
|
||||||
{
|
{
|
||||||
InstructionPrologue(cbi, 1);
|
InstructionPrologue(cbi, 1);
|
||||||
|
@ -1052,7 +1104,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
||||||
{
|
{
|
||||||
// npc = pc + (sext(imm) << 2)
|
// npc = pc + (sext(imm) << 2)
|
||||||
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
||||||
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2));
|
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
|
||||||
|
|
||||||
// branch <- rs op rt
|
// branch <- rs op rt
|
||||||
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
|
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
|
||||||
|
@ -1069,7 +1121,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
||||||
{
|
{
|
||||||
// npc = pc + (sext(imm) << 2)
|
// npc = pc + (sext(imm) << 2)
|
||||||
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
||||||
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2));
|
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
|
||||||
|
|
||||||
// branch <- rs op 0
|
// branch <- rs op 0
|
||||||
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
|
Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true);
|
||||||
|
@ -1085,7 +1137,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
||||||
{
|
{
|
||||||
// npc = pc + (sext(imm) << 2)
|
// npc = pc + (sext(imm) << 2)
|
||||||
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false),
|
||||||
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2));
|
Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false);
|
||||||
|
|
||||||
const u8 rt = static_cast<u8>(cbi.instruction.i.rt.GetValue());
|
const u8 rt = static_cast<u8>(cbi.instruction.i.rt.GetValue());
|
||||||
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
|
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
|
||||||
|
@ -1118,18 +1170,4 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
|
||||||
InstructionEpilogue(cbi);
|
InstructionEpilogue(cbi);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CodeGenerator::Compile_addiu(const CodeBlockInstruction& cbi)
|
|
||||||
{
|
|
||||||
InstructionPrologue(cbi, 1);
|
|
||||||
|
|
||||||
// rt <- rs + sext(imm)
|
|
||||||
m_register_cache.WriteGuestRegister(cbi.instruction.i.rt,
|
|
||||||
AddValues(m_register_cache.ReadGuestRegister(cbi.instruction.i.rs),
|
|
||||||
Value::FromConstantU32(cbi.instruction.i.imm_sext32())));
|
|
||||||
|
|
||||||
InstructionEpilogue(cbi);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace CPU::Recompiler
|
} // namespace CPU::Recompiler
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
|
void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
|
||||||
void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
|
void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size);
|
||||||
void EmitCopyValue(HostReg to_reg, const Value& value);
|
void EmitCopyValue(HostReg to_reg, const Value& value);
|
||||||
void EmitAdd(HostReg to_reg, const Value& value);
|
void EmitAdd(HostReg to_reg, const Value& value, bool set_flags);
|
||||||
void EmitSub(HostReg to_reg, const Value& value);
|
void EmitSub(HostReg to_reg, const Value& value);
|
||||||
void EmitCmp(HostReg to_reg, const Value& value);
|
void EmitCmp(HostReg to_reg, const Value& value);
|
||||||
void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply);
|
void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply);
|
||||||
|
@ -76,6 +76,9 @@ public:
|
||||||
// Branching, generates two paths.
|
// Branching, generates two paths.
|
||||||
void EmitBranch(Condition condition, Reg lr_reg, bool always_link, Value&& branch_target);
|
void EmitBranch(Condition condition, Reg lr_reg, bool always_link, Value&& branch_target);
|
||||||
|
|
||||||
|
// Raising exception if condition is true.
|
||||||
|
void EmitRaiseException(Exception excode, Condition condition = Condition::Always);
|
||||||
|
|
||||||
u32 PrepareStackForCall();
|
u32 PrepareStackForCall();
|
||||||
void RestoreStackAfterCall(u32 adjust_size);
|
void RestoreStackAfterCall(u32 adjust_size);
|
||||||
|
|
||||||
|
@ -130,7 +133,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Value ops
|
// Value ops
|
||||||
Value AddValues(const Value& lhs, const Value& rhs);
|
Value AddValues(const Value& lhs, const Value& rhs, bool set_flags);
|
||||||
std::pair<Value, Value> MulValues(const Value& lhs, const Value& rhs, bool signed_multiply);
|
std::pair<Value, Value> MulValues(const Value& lhs, const Value& rhs, bool signed_multiply);
|
||||||
Value ShlValues(const Value& lhs, const Value& rhs);
|
Value ShlValues(const Value& lhs, const Value& rhs);
|
||||||
Value ShrValues(const Value& lhs, const Value& rhs);
|
Value ShrValues(const Value& lhs, const Value& rhs);
|
||||||
|
@ -175,10 +178,10 @@ private:
|
||||||
bool Compile_Load(const CodeBlockInstruction& cbi);
|
bool Compile_Load(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_Store(const CodeBlockInstruction& cbi);
|
bool Compile_Store(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
|
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
|
||||||
|
bool Compile_Add(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_Multiply(const CodeBlockInstruction& cbi);
|
bool Compile_Multiply(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_Branch(const CodeBlockInstruction& cbi);
|
bool Compile_Branch(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_lui(const CodeBlockInstruction& cbi);
|
bool Compile_lui(const CodeBlockInstruction& cbi);
|
||||||
bool Compile_addiu(const CodeBlockInstruction& cbi);
|
|
||||||
|
|
||||||
Core* m_cpu;
|
Core* m_cpu;
|
||||||
JitCodeBuffer* m_code_buffer;
|
JitCodeBuffer* m_code_buffer;
|
||||||
|
|
|
@ -44,6 +44,10 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool relative, c
|
||||||
{
|
{
|
||||||
Panic("Not implemented");
|
Panic("Not implemented");
|
||||||
}
|
}
|
||||||
|
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
|
||||||
|
{
|
||||||
|
Panic("Not implemented");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace CPU::Recompiler
|
} // namespace CPU::Recompiler
|
|
@ -186,7 +186,7 @@ void CodeGenerator::EmitExceptionExit()
|
||||||
|
|
||||||
// the interpreter load delay might have its own value, but we'll overwrite it here anyway
|
// the interpreter load delay might have its own value, but we'll overwrite it here anyway
|
||||||
// technically RaiseException() and FlushPipeline() have already been called, but that should be okay
|
// technically RaiseException() and FlushPipeline() have already been called, but that should be okay
|
||||||
m_register_cache.FlushLoadDelayForException();
|
m_register_cache.FlushLoadDelay(false);
|
||||||
|
|
||||||
m_register_cache.PopCalleeSavedRegisters(false);
|
m_register_cache.PopCalleeSavedRegisters(false);
|
||||||
m_emit->ret();
|
m_emit->ret();
|
||||||
|
@ -339,7 +339,7 @@ void CodeGenerator::EmitCopyValue(HostReg to_reg, const Value& value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value)
|
void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags)
|
||||||
{
|
{
|
||||||
DebugAssert(value.IsConstant() || value.IsInHostRegister());
|
DebugAssert(value.IsConstant() || value.IsInHostRegister());
|
||||||
|
|
||||||
|
@ -1601,7 +1601,8 @@ void CodeGenerator::EmitDelaySlotUpdate(bool skip_check_for_delay, bool skip_che
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const Xbyak::Label& label)
|
template<typename T>
|
||||||
|
static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const T& label)
|
||||||
{
|
{
|
||||||
switch (condition)
|
switch (condition)
|
||||||
{
|
{
|
||||||
|
@ -1712,6 +1713,28 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool always_link
|
||||||
m_emit->L(skip_branch);
|
m_emit->L(skip_branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
|
||||||
|
{
|
||||||
|
if (condition == Condition::Always)
|
||||||
|
{
|
||||||
|
// no need to use far code if we're always raising the exception
|
||||||
|
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(),
|
||||||
|
Value::FromConstantU8(static_cast<u8>(excode)));
|
||||||
|
m_register_cache.FlushAllGuestRegisters(true, true);
|
||||||
|
m_register_cache.FlushLoadDelay(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* far_code_ptr = GetCurrentFarCodePointer();
|
||||||
|
EmitConditionalJump(condition, false, m_emit, far_code_ptr);
|
||||||
|
|
||||||
|
SwitchToFarCode();
|
||||||
|
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(),
|
||||||
|
Value::FromConstantU8(static_cast<u8>(excode)));
|
||||||
|
EmitExceptionExit();
|
||||||
|
SwitchToNearCode();
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
class ThunkGenerator
|
class ThunkGenerator
|
||||||
{
|
{
|
||||||
|
|
|
@ -570,7 +570,7 @@ void RegisterCache::WriteLoadDelayToCPU(bool clear)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterCache::FlushLoadDelayForException()
|
void RegisterCache::FlushLoadDelay(bool clear)
|
||||||
{
|
{
|
||||||
Assert(m_next_load_delay_register == Reg::count);
|
Assert(m_next_load_delay_register == Reg::count);
|
||||||
if (m_load_delay_register == Reg::count)
|
if (m_load_delay_register == Reg::count)
|
||||||
|
@ -579,6 +579,12 @@ void RegisterCache::FlushLoadDelayForException()
|
||||||
// if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next
|
// if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next
|
||||||
// non-exception-raised path. TODO: push/pop whole state would avoid this issue
|
// non-exception-raised path. TODO: push/pop whole state would avoid this issue
|
||||||
m_code_generator.EmitStoreGuestRegister(m_load_delay_register, m_load_delay_value);
|
m_code_generator.EmitStoreGuestRegister(m_load_delay_register, m_load_delay_value);
|
||||||
|
|
||||||
|
if (clear)
|
||||||
|
{
|
||||||
|
m_load_delay_register = Reg::count;
|
||||||
|
m_load_delay_value.ReleaseAndClear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)
|
void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)
|
||||||
|
|
|
@ -252,7 +252,7 @@ public:
|
||||||
void WriteLoadDelayToCPU(bool clear);
|
void WriteLoadDelayToCPU(bool clear);
|
||||||
|
|
||||||
/// Flushes the load delay, i.e. writes it to the destination register.
|
/// Flushes the load delay, i.e. writes it to the destination register.
|
||||||
void FlushLoadDelayForException();
|
void FlushLoadDelay(bool clear);
|
||||||
|
|
||||||
void FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty);
|
void FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty);
|
||||||
void InvalidateGuestRegister(Reg guest_reg);
|
void InvalidateGuestRegister(Reg guest_reg);
|
||||||
|
|
|
@ -78,6 +78,11 @@ void Thunks::UpdateLoadDelay(Core* cpu)
|
||||||
cpu->UpdateLoadDelay();
|
cpu->UpdateLoadDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thunks::RaiseException(Core* cpu, u8 excode)
|
||||||
|
{
|
||||||
|
cpu->RaiseException(static_cast<Exception>(excode));
|
||||||
|
}
|
||||||
|
|
||||||
void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch)
|
void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch)
|
||||||
{
|
{
|
||||||
cpu->m_cop0_regs.BadVaddr = address;
|
cpu->m_cop0_regs.BadVaddr = address;
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
static bool WriteMemoryWord(Core* cpu, u32 address, u32 value);
|
static bool WriteMemoryWord(Core* cpu, u32 address, u32 value);
|
||||||
static bool InterpretInstruction(Core* cpu);
|
static bool InterpretInstruction(Core* cpu);
|
||||||
static void UpdateLoadDelay(Core* cpu);
|
static void UpdateLoadDelay(Core* cpu);
|
||||||
|
static void RaiseException(Core* cpu, u8 excode);
|
||||||
static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch);
|
static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue