CPU/Recompiler: Implement lwl/lwr/swl/swr

This commit is contained in:
Connor McLaughlin 2020-10-18 14:44:32 +10:00
parent 7566c45f64
commit 51eff82eb6
3 changed files with 110 additions and 0 deletions

View File

@ -88,6 +88,16 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
result = Compile_Load(cbi); result = Compile_Load(cbi);
break; break;
case InstructionOp::lwl:
case InstructionOp::lwr:
result = Compile_LoadLeftRight(cbi);
break;
case InstructionOp::swl:
case InstructionOp::swr:
result = Compile_StoreLeftRight(cbi);
break;
case InstructionOp::sb: case InstructionOp::sb:
case InstructionOp::sh: case InstructionOp::sh:
case InstructionOp::sw: case InstructionOp::sw:
@ -1263,6 +1273,100 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi)
return true; return true;
} }
bool CodeGenerator::Compile_LoadLeftRight(const CodeBlockInstruction& cbi)
{
InstructionPrologue(cbi, 1);
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
Value address = AddValues(base, offset, false);
base.ReleaseAndClear();
Value shift = ShlValues(AndValues(address, Value::FromConstantU32(3)), Value::FromConstantU32(3)); // * 8
address = AndValues(address, Value::FromConstantU32(~u32(3)));
Value mem = EmitLoadGuestMemory(cbi, address, RegSize_32);
// hack to bypass load delays
Value value;
if (cbi.instruction.i.rt == m_register_cache.GetLoadDelayRegister())
{
const Value& ld_value = m_register_cache.GetLoadDelayValue();
if (ld_value.IsInHostRegister())
value.SetHostReg(&m_register_cache, ld_value.GetHostRegister(), ld_value.size);
else
value = ld_value;
}
else
{
value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt, true, true);
}
if (cbi.instruction.op == InstructionOp::lwl)
{
Value lhs = AndValues(value, ShrValues(Value::FromConstantU32(0x00FFFFFF), shift));
mem = ShlValues(mem, SubValues(Value::FromConstantU32(24), shift, false));
EmitOr(mem.GetHostRegister(), mem.GetHostRegister(), lhs);
}
else
{
Value lhs = AndValues(
value, ShlValues(Value::FromConstantU32(0xFFFFFF00), SubValues(Value::FromConstantU32(24), shift, false)));
EmitShr(mem.GetHostRegister(), mem.GetHostRegister(), RegSize_32, shift);
EmitOr(mem.GetHostRegister(), mem.GetHostRegister(), lhs);
}
shift.ReleaseAndClear();
if (g_settings.gpu_pgxp_enable)
EmitFunctionCall(nullptr, PGXP::CPU_LW, Value::FromConstantU32(cbi.instruction.bits), mem, address);
m_register_cache.WriteGuestRegisterDelayed(cbi.instruction.i.rt, std::move(mem));
InstructionEpilogue(cbi);
return true;
}
bool CodeGenerator::Compile_StoreLeftRight(const CodeBlockInstruction& cbi)
{
InstructionPrologue(cbi, 1);
Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs);
Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32());
Value address = AddValues(base, offset, false);
base.ReleaseAndClear();
Value shift = ShlValues(AndValues(address, Value::FromConstantU32(3)), Value::FromConstantU32(3)); // * 8
address = AndValues(address, Value::FromConstantU32(~u32(3)));
Value mem = EmitLoadGuestMemory(cbi, address, RegSize_32);
Value reg = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
if (cbi.instruction.op == InstructionOp::swl)
{
Value lhs = ShrValues(reg, SubValues(Value::FromConstantU32(24), shift, false));
EmitAnd(mem.GetHostRegister(), mem.GetHostRegister(), ShlValues(Value::FromConstantU32(0xFFFFFF00), shift));
EmitOr(mem.GetHostRegister(), mem.GetHostRegister(), lhs);
}
else
{
Value lhs = ShlValues(reg, shift);
mem = AndValues(mem,
ShrValues(Value::FromConstantU32(0x00FFFFFF), SubValues(Value::FromConstantU32(24), shift, false)));
EmitOr(mem.GetHostRegister(), mem.GetHostRegister(), lhs);
}
shift.ReleaseAndClear();
EmitStoreGuestMemory(cbi, address, mem);
if (g_settings.gpu_pgxp_enable)
EmitFunctionCall(nullptr, PGXP::CPU_SW, Value::FromConstantU32(cbi.instruction.bits), mem, address);
InstructionEpilogue(cbi);
return true;
}
bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi) bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi)
{ {
InstructionPrologue(cbi, 1); InstructionPrologue(cbi, 1);

View File

@ -201,6 +201,8 @@ private:
bool Compile_Shift(const CodeBlockInstruction& cbi); bool Compile_Shift(const CodeBlockInstruction& cbi);
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_LoadLeftRight(const CodeBlockInstruction& cbi);
bool Compile_StoreLeftRight(const CodeBlockInstruction& cbi);
bool Compile_MoveHiLo(const CodeBlockInstruction& cbi); bool Compile_MoveHiLo(const CodeBlockInstruction& cbi);
bool Compile_Add(const CodeBlockInstruction& cbi); bool Compile_Add(const CodeBlockInstruction& cbi);
bool Compile_Subtract(const CodeBlockInstruction& cbi); bool Compile_Subtract(const CodeBlockInstruction& cbi);

View File

@ -308,6 +308,10 @@ public:
/// Stores the specified value to the guest register after the next instruction (load delay). /// Stores the specified value to the guest register after the next instruction (load delay).
void WriteGuestRegisterDelayed(Reg guest_reg, Value&& value); void WriteGuestRegisterDelayed(Reg guest_reg, Value&& value);
/// Returns the current target for a load delay, or Reg::count.
Reg GetLoadDelayRegister() const { return m_state.load_delay_register; }
const Value& GetLoadDelayValue() const { return m_state.load_delay_value; }
/// Moves load delay to the next load delay, and writes any previous load delay to the destination register. /// Moves load delay to the next load delay, and writes any previous load delay to the destination register.
void UpdateLoadDelay(); void UpdateLoadDelay();