CPU: Implement cop0 execution and data breakpoints

This commit is contained in:
Connor McLaughlin 2021-04-14 19:06:44 +10:00
parent 22fdd80cc3
commit 442b801d39
4 changed files with 228 additions and 68 deletions

View file

@ -23,7 +23,6 @@ static void SetPC(u32 new_pc);
static void UpdateLoadDelay(); static void UpdateLoadDelay();
static void Branch(u32 target); static void Branch(u32 target);
static void FlushPipeline(); static void FlushPipeline();
static void UpdateDebugDispatcherFlag();
State g_state; State g_state;
bool g_using_interpreter = false; bool g_using_interpreter = false;
@ -227,34 +226,13 @@ ALWAYS_INLINE_RELEASE void Branch(u32 target)
g_state.branch_was_taken = true; g_state.branch_was_taken = true;
} }
ALWAYS_INLINE static u32 GetExceptionVector(Exception excode) ALWAYS_INLINE static u32 GetExceptionVector(bool debug_exception = false)
{ {
const u32 base = g_state.cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000); const u32 base = g_state.cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000);
return base | (debug_exception ? UINT32_C(0x00000040) : UINT32_C(0x00000080));
#if 0
// apparently this isn't correct...
switch (excode)
{
case Exception::BP:
return base | UINT32_C(0x00000040);
default:
return base | UINT32_C(0x00000080);
}
#else
return base | UINT32_C(0x00000080);
#endif
} }
void RaiseException(Exception excode) ALWAYS_INLINE_RELEASE static void RaiseException(u32 CAUSE_bits, u32 EPC, u32 vector)
{
RaiseException(Cop0Registers::CAUSE::MakeValueForException(excode, g_state.current_instruction_in_branch_delay_slot,
g_state.current_instruction_was_branch_taken,
g_state.current_instruction.cop.cop_n),
g_state.current_instruction_pc);
}
void RaiseException(u32 CAUSE_bits, u32 EPC)
{ {
g_state.cop0_regs.EPC = EPC; g_state.cop0_regs.EPC = EPC;
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK) | g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK) |
@ -264,10 +242,10 @@ void RaiseException(u32 CAUSE_bits, u32 EPC)
if (g_state.cop0_regs.cause.Excode != Exception::INT && g_state.cop0_regs.cause.Excode != Exception::Syscall && if (g_state.cop0_regs.cause.Excode != Exception::INT && g_state.cop0_regs.cause.Excode != Exception::Syscall &&
g_state.cop0_regs.cause.Excode != Exception::BP) g_state.cop0_regs.cause.Excode != Exception::BP)
{ {
Log_DebugPrintf("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)", Log_DevPrintf("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)",
static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()), g_state.current_instruction_pc, static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()), g_state.current_instruction_pc,
g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false", g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false",
g_state.cop0_regs.cause.CE.GetValue()); g_state.cop0_regs.cause.CE.GetValue());
DisassembleAndPrint(g_state.current_instruction_pc, 4, 0); DisassembleAndPrint(g_state.current_instruction_pc, 4, 0);
if (s_trace_to_log) if (s_trace_to_log)
{ {
@ -291,11 +269,36 @@ void RaiseException(u32 CAUSE_bits, u32 EPC)
g_state.cop0_regs.sr.mode_bits <<= 2; g_state.cop0_regs.sr.mode_bits <<= 2;
// flush the pipeline - we don't want to execute the previously fetched instruction // flush the pipeline - we don't want to execute the previously fetched instruction
g_state.regs.npc = GetExceptionVector(g_state.cop0_regs.cause.Excode); g_state.regs.npc = vector;
g_state.exception_raised = true; g_state.exception_raised = true;
FlushPipeline(); FlushPipeline();
} }
ALWAYS_INLINE_RELEASE static void DispatchCop0Breakpoint()
{
// When a breakpoint address match occurs the PSX jumps to 80000040h (ie. unlike normal exceptions, not to 80000080h).
// The Excode value in the CAUSE register is set to 09h (same as BREAK opcode), and EPC contains the return address,
// as usually. One of the first things to be done in the exception handler is to disable breakpoints (eg. if the
// any-jump break is enabled, then it must be disabled BEFORE jumping from 80000040h to the actual exception handler).
RaiseException(Cop0Registers::CAUSE::MakeValueForException(
Exception::BP, g_state.current_instruction_in_branch_delay_slot,
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
g_state.current_instruction_pc, GetExceptionVector(true));
}
void RaiseException(u32 CAUSE_bits, u32 EPC)
{
RaiseException(CAUSE_bits, EPC, GetExceptionVector());
}
void RaiseException(Exception excode)
{
RaiseException(Cop0Registers::CAUSE::MakeValueForException(excode, g_state.current_instruction_in_branch_delay_slot,
g_state.current_instruction_was_branch_taken,
g_state.current_instruction.cop.cop_n),
g_state.current_instruction_pc, GetExceptionVector());
}
void SetExternalInterrupt(u8 bit) void SetExternalInterrupt(u8 bit)
{ {
g_state.cop0_regs.cause.Ip |= static_cast<u8>(1u << bit); g_state.cop0_regs.cause.Ip |= static_cast<u8>(1u << bit);
@ -466,6 +469,7 @@ ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
g_state.cop0_regs.dcic.bits = g_state.cop0_regs.dcic.bits =
(g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK); (g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, g_state.cop0_regs.dcic.bits); Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, g_state.cop0_regs.dcic.bits);
UpdateDebugDispatcherFlag();
} }
break; break;
@ -488,12 +492,83 @@ ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
break; break;
default: default:
Log_DevPrintf("Unknown COP0 reg %u", ZeroExtend32(static_cast<u8>(reg))); Log_WarningPrintf("Unknown COP0 reg write %u (%08X)", ZeroExtend32(static_cast<u8>(reg)), value);
break; break;
} }
} }
static void PrintInstruction(u32 bits, u32 pc, Registers* regs) ALWAYS_INLINE_RELEASE void Cop0ExecutionBreakpointCheck()
{
if (!g_state.cop0_regs.dcic.ExecutionBreakpointsEnabled())
return;
const u32 pc = g_state.current_instruction_pc;
const u32 bpc = g_state.cop0_regs.BPC;
const u32 bpcm = g_state.cop0_regs.BPCM;
// Break condition is "((PC XOR BPC) AND BPCM)=0".
if (bpcm == 0 || ((pc ^ bpc) & bpcm) != 0u)
return;
Log_DevPrintf("Cop0 execution breakpoint at %08X", pc);
g_state.cop0_regs.dcic.status_any_break = true;
g_state.cop0_regs.dcic.status_bpc_code_break = true;
DispatchCop0Breakpoint();
}
template<MemoryAccessType type>
ALWAYS_INLINE_RELEASE void Cop0DataBreakpointCheck(VirtualMemoryAddress address)
{
if constexpr (type == MemoryAccessType::Read)
{
if (!g_state.cop0_regs.dcic.DataReadBreakpointsEnabled())
return;
}
else
{
if (!g_state.cop0_regs.dcic.DataWriteBreakpointsEnabled())
return;
}
// Break condition is "((addr XOR BDA) AND BDAM)=0".
const u32 bda = g_state.cop0_regs.BDA;
const u32 bdam = g_state.cop0_regs.BDAM;
if (bdam == 0 || ((address ^ bda) & bdam) != 0u)
return;
Log_DevPrintf("Cop0 data breakpoint for %08X at %08X", address, g_state.current_instruction_pc);
g_state.cop0_regs.dcic.status_any_break = true;
g_state.cop0_regs.dcic.status_bda_data_break = true;
if constexpr (type == MemoryAccessType::Read)
g_state.cop0_regs.dcic.status_bda_data_read_break = true;
else
g_state.cop0_regs.dcic.status_bda_data_write_break = true;
DispatchCop0Breakpoint();
}
static void TracePrintInstruction()
{
const u32 pc = g_state.current_instruction_pc;
const u32 bits = g_state.current_instruction.bits;
TinyString instr;
TinyString comment;
DisassembleInstruction(&instr, pc, bits);
DisassembleInstructionComment(&comment, pc, bits, &g_state.regs);
if (!comment.IsEmpty())
{
for (u32 i = instr.GetLength(); i < 30; i++)
instr.AppendCharacter(' ');
instr.AppendString("; ");
instr.AppendString(comment);
}
std::printf("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
}
static void PrintInstruction(u32 bits, u32 pc, Registers* regs, const char* prefix)
{ {
TinyString instr; TinyString instr;
TinyString comment; TinyString comment;
@ -507,7 +582,7 @@ static void PrintInstruction(u32 bits, u32 pc, Registers* regs)
instr.AppendString(comment); instr.AppendString(comment);
} }
std::printf("%08x: %08x %s\n", pc, bits, instr.GetCharArray()); Log_DevPrintf("%s%08x: %08x %s", prefix, pc, bits, instr.GetCharArray());
} }
static void LogInstruction(u32 bits, u32 pc, Registers* regs) static void LogInstruction(u32 bits, u32 pc, Registers* regs)
@ -537,11 +612,11 @@ ALWAYS_INLINE static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u3
return (((new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0; return (((new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0;
} }
void DisassembleAndPrint(u32 addr) void DisassembleAndPrint(u32 addr, const char* prefix)
{ {
u32 bits = 0; u32 bits = 0;
SafeReadMemoryWord(addr, &bits); SafeReadMemoryWord(addr, &bits);
PrintInstruction(bits, addr, &g_state.regs); PrintInstruction(bits, addr, &g_state.regs, prefix);
} }
void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instructions_after /* = 0 */) void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instructions_after /* = 0 */)
@ -549,21 +624,19 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru
u32 disasm_addr = addr - (instructions_before * sizeof(u32)); u32 disasm_addr = addr - (instructions_before * sizeof(u32));
for (u32 i = 0; i < instructions_before; i++) for (u32 i = 0; i < instructions_before; i++)
{ {
DisassembleAndPrint(disasm_addr); DisassembleAndPrint(disasm_addr, "");
disasm_addr += sizeof(u32); disasm_addr += sizeof(u32);
} }
std::printf("----> ");
// <= to include the instruction itself // <= to include the instruction itself
for (u32 i = 0; i <= instructions_after; i++) for (u32 i = 0; i <= instructions_after; i++)
{ {
DisassembleAndPrint(disasm_addr); DisassembleAndPrint(disasm_addr, (i == 0) ? "---->" : "");
disasm_addr += sizeof(u32); disasm_addr += sizeof(u32);
} }
} }
template<PGXPMode pgxp_mode> template<PGXPMode pgxp_mode, bool debug>
ALWAYS_INLINE_RELEASE static void ExecuteInstruction() ALWAYS_INLINE_RELEASE static void ExecuteInstruction()
{ {
restart_instruction: restart_instruction:
@ -579,7 +652,7 @@ restart_instruction:
#ifdef _DEBUG #ifdef _DEBUG
if (TRACE_EXECUTION) if (TRACE_EXECUTION)
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs); TracePrintInstruction();
#endif #endif
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter. // Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
@ -1059,6 +1132,9 @@ restart_instruction:
case InstructionOp::lb: case InstructionOp::lb:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u8 value; u8 value;
if (!ReadMemoryByte(addr, &value)) if (!ReadMemoryByte(addr, &value))
return; return;
@ -1075,6 +1151,9 @@ restart_instruction:
case InstructionOp::lh: case InstructionOp::lh:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u16 value; u16 value;
if (!ReadMemoryHalfWord(addr, &value)) if (!ReadMemoryHalfWord(addr, &value))
return; return;
@ -1090,6 +1169,9 @@ restart_instruction:
case InstructionOp::lw: case InstructionOp::lw:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u32 value; u32 value;
if (!ReadMemoryWord(addr, &value)) if (!ReadMemoryWord(addr, &value))
return; return;
@ -1104,6 +1186,9 @@ restart_instruction:
case InstructionOp::lbu: case InstructionOp::lbu:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u8 value; u8 value;
if (!ReadMemoryByte(addr, &value)) if (!ReadMemoryByte(addr, &value))
return; return;
@ -1119,6 +1204,9 @@ restart_instruction:
case InstructionOp::lhu: case InstructionOp::lhu:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u16 value; u16 value;
if (!ReadMemoryHalfWord(addr, &value)) if (!ReadMemoryHalfWord(addr, &value))
return; return;
@ -1136,6 +1224,9 @@ restart_instruction:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
u32 aligned_value; u32 aligned_value;
if (!ReadMemoryWord(aligned_addr, &aligned_value)) if (!ReadMemoryWord(aligned_addr, &aligned_value))
return; return;
@ -1165,6 +1256,9 @@ restart_instruction:
case InstructionOp::sb: case InstructionOp::sb:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
const u8 value = Truncate8(ReadReg(inst.i.rt)); const u8 value = Truncate8(ReadReg(inst.i.rt));
WriteMemoryByte(addr, value); WriteMemoryByte(addr, value);
@ -1176,6 +1270,9 @@ restart_instruction:
case InstructionOp::sh: case InstructionOp::sh:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
const u16 value = Truncate16(ReadReg(inst.i.rt)); const u16 value = Truncate16(ReadReg(inst.i.rt));
WriteMemoryHalfWord(addr, value); WriteMemoryHalfWord(addr, value);
@ -1187,6 +1284,9 @@ restart_instruction:
case InstructionOp::sw: case InstructionOp::sw:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Write>(addr);
const u32 value = ReadReg(inst.i.rt); const u32 value = ReadReg(inst.i.rt);
WriteMemoryWord(addr, value); WriteMemoryWord(addr, value);
@ -1200,6 +1300,9 @@ restart_instruction:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
if constexpr (debug)
Cop0DataBreakpointCheck<MemoryAccessType::Write>(aligned_addr);
const u32 reg_value = ReadReg(inst.i.rt); const u32 reg_value = ReadReg(inst.i.rt);
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
u32 mem_value; u32 mem_value;
@ -1508,16 +1611,20 @@ void DispatchInterrupt()
g_state.regs.pc); g_state.regs.pc);
} }
static void UpdateDebugDispatcherFlag() void UpdateDebugDispatcherFlag()
{ {
const bool has_any_breakpoints = !s_breakpoints.empty(); const bool has_any_breakpoints = !s_breakpoints.empty();
// TODO: cop0 breakpoints // TODO: cop0 breakpoints
const auto& dcic = g_state.cop0_regs.dcic;
const bool has_cop0_breakpoints =
dcic.super_master_enable_1 && dcic.super_master_enable_2 && dcic.execution_breakpoint_enable;
const bool use_debug_dispatcher = has_any_breakpoints || s_trace_to_log; const bool use_debug_dispatcher = has_any_breakpoints || has_cop0_breakpoints || s_trace_to_log;
if (use_debug_dispatcher == g_state.use_debug_dispatcher) if (use_debug_dispatcher == g_state.use_debug_dispatcher)
return; return;
Log_DevPrintf("%s debug dispatcher", use_debug_dispatcher ? "Now using" : "No longer using");
g_state.use_debug_dispatcher = use_debug_dispatcher; g_state.use_debug_dispatcher = use_debug_dispatcher;
ForceDispatcherExit(); ForceDispatcherExit();
} }
@ -1694,7 +1801,7 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search)
return false; return false;
} }
static ALWAYS_INLINE_RELEASE bool BreakpointCheck() ALWAYS_INLINE_RELEASE static bool BreakpointCheck()
{ {
const u32 pc = g_state.regs.pc; const u32 pc = g_state.regs.pc;
@ -1779,6 +1886,8 @@ static void ExecuteImpl()
if constexpr (debug) if constexpr (debug)
{ {
Cop0ExecutionBreakpointCheck();
if (BreakpointCheck()) if (BreakpointCheck())
{ {
// continue is measurably faster than break on msvc for some reason // continue is measurably faster than break on msvc for some reason
@ -1818,7 +1927,7 @@ static void ExecuteImpl()
#endif #endif
// execute the instruction we previously fetched // execute the instruction we previously fetched
ExecuteInstruction<pgxp_mode>(); ExecuteInstruction<pgxp_mode, debug>();
// next load delay // next load delay
UpdateLoadDelay(); UpdateLoadDelay();
@ -1891,7 +2000,7 @@ void InterpretCachedBlock(const CodeBlock& block)
g_state.regs.npc += 4; g_state.regs.npc += 4;
// execute the instruction we previously fetched // execute the instruction we previously fetched
ExecuteInstruction<pgxp_mode>(); ExecuteInstruction<pgxp_mode, false>();
// next load delay // next load delay
UpdateLoadDelay(); UpdateLoadDelay();
@ -1944,7 +2053,7 @@ void InterpretUncachedBlock()
} }
// execute the instruction we previously fetched // execute the instruction we previously fetched
ExecuteInstruction<pgxp_mode>(); ExecuteInstruction<pgxp_mode, false>();
// next load delay // next load delay
UpdateLoadDelay(); UpdateLoadDelay();
@ -1969,13 +2078,13 @@ namespace Recompiler::Thunks {
bool InterpretInstruction() bool InterpretInstruction()
{ {
ExecuteInstruction<PGXPMode::Disabled>(); ExecuteInstruction<PGXPMode::Disabled, false>();
return g_state.exception_raised; return g_state.exception_raised;
} }
bool InterpretInstructionPGXP() bool InterpretInstructionPGXP()
{ {
ExecuteInstruction<PGXPMode::Memory>(); ExecuteInstruction<PGXPMode::Memory, false>();
return g_state.exception_raised; return g_state.exception_raised;
} }

View file

@ -21,6 +21,7 @@ ALWAYS_INLINE void CheckForPendingInterrupt()
} }
void DispatchInterrupt(); void DispatchInterrupt();
void UpdateDebugDispatcherFlag();
// icache stuff // icache stuff
ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address) ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address)

View file

@ -2494,25 +2494,55 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
} }
} }
if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mtcn && if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mtcn)
(reg == Cop0Reg::CAUSE || reg == Cop0Reg::SR))
{ {
// Emit an interrupt check on load of CAUSE/SR. if (reg == Cop0Reg::CAUSE || reg == Cop0Reg::SR)
Value sr_value = m_register_cache.AllocateScratch(RegSize_32); {
Value cause_value = m_register_cache.AllocateScratch(RegSize_32); // Emit an interrupt check on load of CAUSE/SR.
Value sr_value = m_register_cache.AllocateScratch(RegSize_32);
Value cause_value = m_register_cache.AllocateScratch(RegSize_32);
// m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0) // m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0)
LabelType no_interrupt; LabelType no_interrupt;
EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(State, cop0_regs.sr.bits)); EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(State, cop0_regs.sr.bits));
EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(State, cop0_regs.cause.bits)); EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(State, cop0_regs.cause.bits));
EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt); EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
m_register_cache.InhibitAllocation(); m_register_cache.InhibitAllocation();
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value); EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00)); EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
EmitConditionalBranch(Condition::Zero, false, &no_interrupt); EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0)); EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
EmitBindLabel(&no_interrupt); EmitBindLabel(&no_interrupt);
m_register_cache.UninhibitAllocation(); m_register_cache.UninhibitAllocation();
}
else if (reg == Cop0Reg::DCIC)
{
Value dcic_value = m_register_cache.AllocateScratch(RegSize_32);
m_register_cache.InhibitAllocation();
// if ((dcic & master_enable_bits) != master_enable_bits) goto not_enabled;
LabelType not_enabled;
EmitLoadCPUStructField(dcic_value.host_reg, dcic_value.size, offsetof(State, cop0_regs.dcic.bits));
EmitAnd(dcic_value.host_reg, dcic_value.host_reg,
Value::FromConstantU32(Cop0Registers::DCIC::MASTER_ENABLE_BITS));
EmitConditionalBranch(Condition::NotEqual, false, dcic_value.host_reg,
Value::FromConstantU32(Cop0Registers::DCIC::MASTER_ENABLE_BITS), &not_enabled);
// if ((dcic & breakpoint_bits) == 0) goto not_enabled;
EmitLoadCPUStructField(dcic_value.host_reg, dcic_value.size, offsetof(State, cop0_regs.dcic.bits));
EmitTest(dcic_value.host_reg, Value::FromConstantU32(Cop0Registers::DCIC::ANY_BREAKPOINTS_ENABLED_BITS));
EmitConditionalBranch(Condition::Zero, false, &not_enabled);
m_register_cache.UninhibitAllocation();
// exit block early if enabled
m_register_cache.PushState();
EmitFunctionCall(nullptr, &CPU::UpdateDebugDispatcherFlag);
EmitExceptionExit();
m_register_cache.PopState();
EmitBindLabel(&not_enabled);
}
} }
InstructionEpilogue(cbi); InstructionEpilogue(cbi);

View file

@ -283,8 +283,7 @@ struct Registers
}; };
}; };
std::optional<VirtualMemoryAddress> GetLoadStoreEffectiveAddress(const Instruction& instruction, std::optional<VirtualMemoryAddress> GetLoadStoreEffectiveAddress(const Instruction& instruction, const Registers* regs);
const Registers* regs);
enum class Cop0Reg : u8 enum class Cop0Reg : u8
{ {
@ -402,6 +401,27 @@ struct Cop0Registers
BitField<u32, bool, 31, 1> super_master_enable_2; BitField<u32, bool, 31, 1> super_master_enable_2;
static constexpr u32 WRITE_MASK = 0b1111'1111'1000'0000'1111'0000'0011'1111; static constexpr u32 WRITE_MASK = 0b1111'1111'1000'0000'1111'0000'0011'1111;
static constexpr u32 ANY_BREAKPOINTS_ENABLED_BITS = (1u << 24) | (1u << 26) | (1u << 27) | (1u << 28);
static constexpr u32 MASTER_ENABLE_BITS = (1u << 23) | (1u << 31);
constexpr bool ExecutionBreakpointsEnabled() const
{
const u32 mask = (1u << 23) | (1u << 24) | (1u << 31);
return ((bits & mask) == mask);
}
constexpr bool DataReadBreakpointsEnabled() const
{
const u32 mask = (1u << 23) | (1u << 25) | (1u << 26) | (1u << 31);
return ((bits & mask) == mask);
}
constexpr bool DataWriteBreakpointsEnabled() const
{
const u32 mask = (1u << 23) | (1u << 25) | (1u << 27) | (1u << 31);
return ((bits & mask) == mask);
}
} dcic; } dcic;
}; };