CPU/Intepreter: Raise #RI on invalid COP0 move
This commit is contained in:
parent
62414b0c4c
commit
c6746e76f1
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue