mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 13:55: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);
|
||||
break;
|
||||
|
||||
case InstructionOp::addi:
|
||||
case InstructionOp::addiu:
|
||||
result = Compile_addiu(cbi);
|
||||
result = Compile_Add(cbi);
|
||||
break;
|
||||
|
||||
case InstructionOp::funct:
|
||||
|
@ -132,6 +133,11 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
|
|||
result = Compile_MoveHiLo(cbi);
|
||||
break;
|
||||
|
||||
case InstructionFunct::add:
|
||||
case InstructionFunct::addu:
|
||||
result = Compile_Add(cbi);
|
||||
break;
|
||||
|
||||
case InstructionFunct::mult:
|
||||
case InstructionFunct::multu:
|
||||
result = Compile_Multiply(cbi);
|
||||
|
@ -261,10 +267,10 @@ void CodeGenerator::ConvertValueSizeInPlace(Value* value, RegSize size, bool sig
|
|||
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);
|
||||
if (lhs.IsConstant() && rhs.IsConstant())
|
||||
if (lhs.IsConstant() && rhs.IsConstant() && !set_flags)
|
||||
{
|
||||
// compile-time
|
||||
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);
|
||||
if (lhs.HasConstantValue(0))
|
||||
if (lhs.HasConstantValue(0) && !set_flags)
|
||||
{
|
||||
EmitCopyValue(res.host_reg, rhs);
|
||||
return res;
|
||||
}
|
||||
else if (rhs.HasConstantValue(0))
|
||||
else if (rhs.HasConstantValue(0) && !set_flags)
|
||||
{
|
||||
EmitCopyValue(res.host_reg, lhs);
|
||||
return res;
|
||||
|
@ -301,7 +307,7 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs)
|
|||
else
|
||||
{
|
||||
EmitCopyValue(res.host_reg, lhs);
|
||||
EmitAdd(res.host_reg, rhs);
|
||||
EmitAdd(res.host_reg, rhs, set_flags);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -903,7 +909,7 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi)
|
|||
// rt <- mem[rs + sext(imm)]
|
||||
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
||||
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
|
||||
Value address = AddValues(base, offset);
|
||||
Value address = AddValues(base, offset, false);
|
||||
|
||||
Value result;
|
||||
switch (cbi.instruction.op)
|
||||
|
@ -942,7 +948,7 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi)
|
|||
// mem[rs + sext(imm)] <- rt
|
||||
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
|
||||
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);
|
||||
|
||||
switch (cbi.instruction.op)
|
||||
|
@ -999,6 +1005,52 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
|
|||
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)
|
||||
{
|
||||
InstructionPrologue(cbi, 1);
|
||||
|
@ -1052,7 +1104,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
|||
{
|
||||
// npc = pc + (sext(imm) << 2)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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 bool bgez = ConvertToBoolUnchecked(rt & u8(1));
|
||||
|
@ -1118,18 +1170,4 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
|
|||
InstructionEpilogue(cbi);
|
||||
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
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
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 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 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);
|
||||
|
@ -76,6 +76,9 @@ public:
|
|||
// Branching, generates two paths.
|
||||
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();
|
||||
void RestoreStackAfterCall(u32 adjust_size);
|
||||
|
||||
|
@ -130,7 +133,7 @@ public:
|
|||
#endif
|
||||
|
||||
// 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);
|
||||
Value ShlValues(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_Store(const CodeBlockInstruction& cbi);
|
||||
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
|
||||
bool Compile_Add(const CodeBlockInstruction& cbi);
|
||||
bool Compile_Multiply(const CodeBlockInstruction& cbi);
|
||||
bool Compile_Branch(const CodeBlockInstruction& cbi);
|
||||
bool Compile_lui(const CodeBlockInstruction& cbi);
|
||||
bool Compile_addiu(const CodeBlockInstruction& cbi);
|
||||
|
||||
Core* m_cpu;
|
||||
JitCodeBuffer* m_code_buffer;
|
||||
|
|
|
@ -44,6 +44,10 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool relative, c
|
|||
{
|
||||
Panic("Not implemented");
|
||||
}
|
||||
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
|
||||
{
|
||||
Panic("Not implemented");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // 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
|
||||
// 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_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());
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1712,6 +1713,28 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool always_link
|
|||
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
|
||||
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);
|
||||
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
|
||||
// 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);
|
||||
|
||||
if (clear)
|
||||
{
|
||||
m_load_delay_register = Reg::count;
|
||||
m_load_delay_value.ReleaseAndClear();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty)
|
||||
|
|
|
@ -252,7 +252,7 @@ public:
|
|||
void WriteLoadDelayToCPU(bool clear);
|
||||
|
||||
/// 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 InvalidateGuestRegister(Reg guest_reg);
|
||||
|
|
|
@ -78,6 +78,11 @@ void Thunks::UpdateLoadDelay(Core* cpu)
|
|||
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)
|
||||
{
|
||||
cpu->m_cop0_regs.BadVaddr = address;
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
static bool WriteMemoryWord(Core* cpu, u32 address, u32 value);
|
||||
static bool InterpretInstruction(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);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue