CPU/Recompiler: Implement mfc0/mtc0/rfe
This commit is contained in:
parent
f5d65448d6
commit
4e7d420a06
|
@ -109,6 +109,10 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi)
|
||||||
result = Compile_lui(cbi);
|
result = Compile_lui(cbi);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InstructionOp::cop0:
|
||||||
|
result = Compile_cop0(cbi);
|
||||||
|
break;
|
||||||
|
|
||||||
case InstructionOp::funct:
|
case InstructionOp::funct:
|
||||||
{
|
{
|
||||||
switch (cbi.instruction.r.funct)
|
switch (cbi.instruction.r.funct)
|
||||||
|
@ -1358,4 +1362,142 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
||||||
|
{
|
||||||
|
if (cbi.instruction.cop.IsCommonInstruction())
|
||||||
|
{
|
||||||
|
switch (cbi.instruction.cop.CommonOp())
|
||||||
|
{
|
||||||
|
case CopCommonInstruction::mfcn:
|
||||||
|
case CopCommonInstruction::mtcn:
|
||||||
|
{
|
||||||
|
u32 offset;
|
||||||
|
u32 write_mask = UINT32_C(0xFFFFFFFF);
|
||||||
|
switch (static_cast<Cop0Reg>(cbi.instruction.r.rd.GetValue()))
|
||||||
|
{
|
||||||
|
case Cop0Reg::BPC:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.BPC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::BPCM:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.BPCM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::BDA:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.BDA);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::BDAM:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.BDAM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::DCIC:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.dcic.bits);
|
||||||
|
write_mask = Cop0Registers::DCIC::WRITE_MASK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::JUMPDEST:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.TAR);
|
||||||
|
write_mask = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::BadVaddr:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.BadVaddr);
|
||||||
|
write_mask = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::SR:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.sr.bits);
|
||||||
|
write_mask = Cop0Registers::SR::WRITE_MASK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::CAUSE:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.cause.bits);
|
||||||
|
write_mask = Cop0Registers::CAUSE::WRITE_MASK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::EPC:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.EPC);
|
||||||
|
write_mask = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Cop0Reg::PRID:
|
||||||
|
offset = offsetof(Core, m_cop0_regs.PRID);
|
||||||
|
write_mask = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Compile_Fallback(cbi);
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionPrologue(cbi, 1);
|
||||||
|
|
||||||
|
if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mfcn)
|
||||||
|
{
|
||||||
|
// coprocessor loads are load-delayed
|
||||||
|
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
EmitLoadCPUStructField(value.host_reg, value.size, offset);
|
||||||
|
m_register_cache.WriteGuestRegisterDelayed(cbi.instruction.r.rt, std::move(value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// some registers are not writable, so ignore those
|
||||||
|
if (write_mask != 0)
|
||||||
|
{
|
||||||
|
Value value = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt);
|
||||||
|
if (write_mask != UINT32_C(0xFFFFFFFF))
|
||||||
|
{
|
||||||
|
// need to adjust the mask
|
||||||
|
Value scratch = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
EmitCopyValue(scratch.host_reg, value);
|
||||||
|
EmitAnd(scratch.host_reg, Value::FromConstantU32(write_mask));
|
||||||
|
value = std::move(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitStoreCPUStructField(offset, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionEpilogue(cbi);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only mfc/mtc for cop0
|
||||||
|
default:
|
||||||
|
return Compile_Fallback(cbi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (cbi.instruction.cop.Cop0Op())
|
||||||
|
{
|
||||||
|
case Cop0Instruction::rfe:
|
||||||
|
{
|
||||||
|
InstructionPrologue(cbi, 1);
|
||||||
|
|
||||||
|
// shift mode bits right two, preserving upper bits
|
||||||
|
static constexpr u32 mode_bits_mask = UINT32_C(0b1111);
|
||||||
|
Value sr = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
EmitLoadCPUStructField(sr.host_reg, RegSize_32, offsetof(Core, m_cop0_regs.sr.bits));
|
||||||
|
{
|
||||||
|
Value new_mode_bits = m_register_cache.AllocateScratch(RegSize_32);
|
||||||
|
EmitCopyValue(new_mode_bits.host_reg, sr);
|
||||||
|
EmitShr(new_mode_bits.host_reg, new_mode_bits.size, Value::FromConstantU32(2));
|
||||||
|
EmitAnd(new_mode_bits.host_reg, Value::FromConstantU32(mode_bits_mask));
|
||||||
|
EmitAnd(sr.host_reg, Value::FromConstantU32(~mode_bits_mask));
|
||||||
|
EmitOr(sr.host_reg, new_mode_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitStoreCPUStructField(offsetof(Core, m_cop0_regs.sr.bits), sr);
|
||||||
|
|
||||||
|
InstructionEpilogue(cbi);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Compile_Fallback(cbi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CPU::Recompiler
|
} // namespace CPU::Recompiler
|
||||||
|
|
|
@ -191,6 +191,7 @@ private:
|
||||||
bool Compile_SetLess(const CodeBlockInstruction& cbi);
|
bool Compile_SetLess(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_cop0(const CodeBlockInstruction& cbi);
|
||||||
|
|
||||||
Core* m_cpu;
|
Core* m_cpu;
|
||||||
JitCodeBuffer* m_code_buffer;
|
JitCodeBuffer* m_code_buffer;
|
||||||
|
|
Loading…
Reference in New Issue