CPU/Intepreter: Raise #RI on invalid COP0 move

This commit is contained in:
Stenzek 2024-11-28 00:44:31 +10:00
parent 62414b0c4c
commit c6746e76f1
No known key found for this signature in database
1 changed files with 132 additions and 125 deletions

View File

@ -48,9 +48,6 @@ static u32 ReadReg(Reg rs);
static void WriteReg(Reg rd, u32 value); static void WriteReg(Reg rd, u32 value);
static void WriteRegDelayed(Reg rd, u32 value); static void WriteRegDelayed(Reg rd, u32 value);
static u32 ReadCop0Reg(Cop0Reg reg);
static void WriteCop0Reg(Cop0Reg reg, u32 value);
static void DispatchCop0Breakpoint(); static void DispatchCop0Breakpoint();
static bool IsCop0ExecutionBreakpointUnmasked(); static bool IsCop0ExecutionBreakpointUnmasked();
static void Cop0ExecutionBreakpointCheck(); static void Cop0ExecutionBreakpointCheck();
@ -494,123 +491,6 @@ ALWAYS_INLINE_RELEASE void CPU::WriteRegDelayed(Reg rd, u32 value)
g_state.next_load_delay_value = value; g_state.next_load_delay_value = value;
} }
ALWAYS_INLINE_RELEASE u32 CPU::ReadCop0Reg(Cop0Reg reg)
{
switch (reg)
{
case Cop0Reg::BPC:
return g_state.cop0_regs.BPC;
case Cop0Reg::BPCM:
return g_state.cop0_regs.BPCM;
case Cop0Reg::BDA:
return g_state.cop0_regs.BDA;
case Cop0Reg::BDAM:
return g_state.cop0_regs.BDAM;
case Cop0Reg::DCIC:
return g_state.cop0_regs.dcic.bits;
case Cop0Reg::JUMPDEST:
return g_state.cop0_regs.TAR;
case Cop0Reg::BadVaddr:
return g_state.cop0_regs.BadVaddr;
case Cop0Reg::SR:
return g_state.cop0_regs.sr.bits;
case Cop0Reg::CAUSE:
return g_state.cop0_regs.cause.bits;
case Cop0Reg::EPC:
return g_state.cop0_regs.EPC;
case Cop0Reg::PRID:
return g_state.cop0_regs.PRID;
default:
return 0;
}
}
ALWAYS_INLINE_RELEASE void CPU::WriteCop0Reg(Cop0Reg reg, u32 value)
{
switch (reg)
{
case Cop0Reg::BPC:
{
g_state.cop0_regs.BPC = value;
DEV_LOG("COP0 BPC <- {:08X}", value);
}
break;
case Cop0Reg::BPCM:
{
g_state.cop0_regs.BPCM = value;
DEV_LOG("COP0 BPCM <- {:08X}", value);
if (UpdateDebugDispatcherFlag())
ExitExecution();
}
break;
case Cop0Reg::BDA:
{
g_state.cop0_regs.BDA = value;
DEV_LOG("COP0 BDA <- {:08X}", value);
}
break;
case Cop0Reg::BDAM:
{
g_state.cop0_regs.BDAM = value;
DEV_LOG("COP0 BDAM <- {:08X}", value);
}
break;
case Cop0Reg::JUMPDEST:
{
WARNING_LOG("Ignoring write to Cop0 JUMPDEST");
}
break;
case Cop0Reg::DCIC:
{
g_state.cop0_regs.dcic.bits =
(g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
DEV_LOG("COP0 DCIC <- {:08X} (now {:08X})", value, g_state.cop0_regs.dcic.bits);
if (UpdateDebugDispatcherFlag())
ExitExecution();
}
break;
case Cop0Reg::SR:
{
g_state.cop0_regs.sr.bits =
(g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK);
DEBUG_LOG("COP0 SR <- {:08X} (now {:08X})", value, g_state.cop0_regs.sr.bits);
UpdateMemoryPointers();
CheckForPendingInterrupt();
}
break;
case Cop0Reg::CAUSE:
{
g_state.cop0_regs.cause.bits =
(g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK);
DEBUG_LOG("COP0 CAUSE <- {:08X} (now {:08X})", value, g_state.cop0_regs.cause.bits);
CheckForPendingInterrupt();
}
break;
[[unlikely]] default:
DEV_LOG("Unknown COP0 reg write {} ({:08X})", static_cast<u8>(reg), value);
break;
}
}
ALWAYS_INLINE_RELEASE bool CPU::IsCop0ExecutionBreakpointUnmasked() ALWAYS_INLINE_RELEASE bool CPU::IsCop0ExecutionBreakpointUnmasked()
{ {
static constexpr const u32 code_address_ranges[][2] = { static constexpr const u32 code_address_ranges[][2] = {
@ -1776,7 +1656,59 @@ restart_instruction:
{ {
case CopCommonInstruction::mfcn: case CopCommonInstruction::mfcn:
{ {
const u32 value = ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue())); u32 value;
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
{
case Cop0Reg::BPC:
value = g_state.cop0_regs.BPC;
break;
case Cop0Reg::BPCM:
value = g_state.cop0_regs.BPCM;
break;
case Cop0Reg::BDA:
value = g_state.cop0_regs.BDA;
break;
case Cop0Reg::BDAM:
value = g_state.cop0_regs.BDAM;
break;
case Cop0Reg::DCIC:
value = g_state.cop0_regs.dcic.bits;
break;
case Cop0Reg::JUMPDEST:
value = g_state.cop0_regs.TAR;
break;
case Cop0Reg::BadVaddr:
value = g_state.cop0_regs.BadVaddr;
break;
case Cop0Reg::SR:
value = g_state.cop0_regs.sr.bits;
break;
case Cop0Reg::CAUSE:
value = g_state.cop0_regs.cause.bits;
break;
case Cop0Reg::EPC:
value = g_state.cop0_regs.EPC;
break;
case Cop0Reg::PRID:
value = g_state.cop0_regs.PRID;
break;
default:
RaiseException(Exception::RI);
return;
}
WriteRegDelayed(inst.r.rt, value); WriteRegDelayed(inst.r.rt, value);
if constexpr (pgxp_mode == PGXPMode::CPU) if constexpr (pgxp_mode == PGXPMode::CPU)
@ -1786,11 +1718,86 @@ restart_instruction:
case CopCommonInstruction::mtcn: case CopCommonInstruction::mtcn:
{ {
const u32 rtVal = ReadReg(inst.r.rt); u32 value = ReadReg(inst.r.rt);
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), rtVal); [[maybe_unused]] const u32 orig_value = value;
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
{
case Cop0Reg::BPC:
{
g_state.cop0_regs.BPC = value;
DEV_LOG("COP0 BPC <- {:08X}", value);
}
break;
case Cop0Reg::BPCM:
{
g_state.cop0_regs.BPCM = value;
DEV_LOG("COP0 BPCM <- {:08X}", value);
if (UpdateDebugDispatcherFlag())
ExitExecution();
}
break;
case Cop0Reg::BDA:
{
g_state.cop0_regs.BDA = value;
DEV_LOG("COP0 BDA <- {:08X}", value);
}
break;
case Cop0Reg::BDAM:
{
g_state.cop0_regs.BDAM = value;
DEV_LOG("COP0 BDAM <- {:08X}", value);
}
break;
case Cop0Reg::JUMPDEST:
{
WARNING_LOG("Ignoring write to Cop0 JUMPDEST");
}
break;
case Cop0Reg::DCIC:
{
g_state.cop0_regs.dcic.bits = (g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) |
(value & Cop0Registers::DCIC::WRITE_MASK);
DEV_LOG("COP0 DCIC <- {:08X} (now {:08X})", value, g_state.cop0_regs.dcic.bits);
value = g_state.cop0_regs.dcic.bits;
if (UpdateDebugDispatcherFlag())
ExitExecution();
}
break;
case Cop0Reg::SR:
{
g_state.cop0_regs.sr.bits = (g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) |
(value & Cop0Registers::SR::WRITE_MASK);
DEBUG_LOG("COP0 SR <- {:08X} (now {:08X})", value, g_state.cop0_regs.sr.bits);
value = g_state.cop0_regs.sr.bits;
UpdateMemoryPointers();
CheckForPendingInterrupt();
}
break;
case Cop0Reg::CAUSE:
{
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) |
(value & Cop0Registers::CAUSE::WRITE_MASK);
DEBUG_LOG("COP0 CAUSE <- {:08X} (now {:08X})", value, g_state.cop0_regs.cause.bits);
value = g_state.cop0_regs.cause.bits;
CheckForPendingInterrupt();
}
break;
[[unlikely]] default:
RaiseException(Exception::RI);
return;
}
if constexpr (pgxp_mode == PGXPMode::CPU) if constexpr (pgxp_mode == PGXPMode::CPU)
PGXP::CPU_MTC0(inst, ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue())), rtVal); PGXP::CPU_MTC0(inst, value, orig_value);
} }
break; break;
@ -1818,7 +1825,7 @@ restart_instruction:
case Cop0Instruction::tlbwr: case Cop0Instruction::tlbwr:
case Cop0Instruction::tlbp: case Cop0Instruction::tlbp:
RaiseException(Exception::RI); RaiseException(Exception::RI);
break; return;
default: default:
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, [[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc,