mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-26 23:50:31 +00:00
CPU: Implement cop0 execution and data breakpoints
This commit is contained in:
parent
22fdd80cc3
commit
442b801d39
@ -23,7 +23,6 @@ static void SetPC(u32 new_pc);
|
||||
static void UpdateLoadDelay();
|
||||
static void Branch(u32 target);
|
||||
static void FlushPipeline();
|
||||
static void UpdateDebugDispatcherFlag();
|
||||
|
||||
State g_state;
|
||||
bool g_using_interpreter = false;
|
||||
@ -227,34 +226,13 @@ ALWAYS_INLINE_RELEASE void Branch(u32 target)
|
||||
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);
|
||||
|
||||
#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
|
||||
return base | (debug_exception ? UINT32_C(0x00000040) : UINT32_C(0x00000080));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void RaiseException(u32 CAUSE_bits, u32 EPC)
|
||||
ALWAYS_INLINE_RELEASE static void RaiseException(u32 CAUSE_bits, u32 EPC, u32 vector)
|
||||
{
|
||||
g_state.cop0_regs.EPC = EPC;
|
||||
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 &&
|
||||
g_state.cop0_regs.cause.Excode != Exception::BP)
|
||||
{
|
||||
Log_DebugPrintf("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,
|
||||
g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false",
|
||||
g_state.cop0_regs.cause.CE.GetValue());
|
||||
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,
|
||||
g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false",
|
||||
g_state.cop0_regs.cause.CE.GetValue());
|
||||
DisassembleAndPrint(g_state.current_instruction_pc, 4, 0);
|
||||
if (s_trace_to_log)
|
||||
{
|
||||
@ -291,11 +269,36 @@ void RaiseException(u32 CAUSE_bits, u32 EPC)
|
||||
g_state.cop0_regs.sr.mode_bits <<= 2;
|
||||
|
||||
// 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;
|
||||
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)
|
||||
{
|
||||
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 & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
|
||||
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, g_state.cop0_regs.dcic.bits);
|
||||
UpdateDebugDispatcherFlag();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -488,12 +492,83 @@ ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 comment;
|
||||
@ -507,7 +582,7 @@ static void PrintInstruction(u32 bits, u32 pc, Registers* regs)
|
||||
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)
|
||||
@ -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;
|
||||
}
|
||||
|
||||
void DisassembleAndPrint(u32 addr)
|
||||
void DisassembleAndPrint(u32 addr, const char* prefix)
|
||||
{
|
||||
u32 bits = 0;
|
||||
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 */)
|
||||
@ -549,21 +624,19 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru
|
||||
u32 disasm_addr = addr - (instructions_before * sizeof(u32));
|
||||
for (u32 i = 0; i < instructions_before; i++)
|
||||
{
|
||||
DisassembleAndPrint(disasm_addr);
|
||||
DisassembleAndPrint(disasm_addr, "");
|
||||
disasm_addr += sizeof(u32);
|
||||
}
|
||||
|
||||
std::printf("----> ");
|
||||
|
||||
// <= to include the instruction itself
|
||||
for (u32 i = 0; i <= instructions_after; i++)
|
||||
{
|
||||
DisassembleAndPrint(disasm_addr);
|
||||
DisassembleAndPrint(disasm_addr, (i == 0) ? "---->" : "");
|
||||
disasm_addr += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
template<PGXPMode pgxp_mode>
|
||||
template<PGXPMode pgxp_mode, bool debug>
|
||||
ALWAYS_INLINE_RELEASE static void ExecuteInstruction()
|
||||
{
|
||||
restart_instruction:
|
||||
@ -579,7 +652,7 @@ restart_instruction:
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (TRACE_EXECUTION)
|
||||
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
||||
TracePrintInstruction();
|
||||
#endif
|
||||
|
||||
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
|
||||
@ -1059,6 +1132,9 @@ restart_instruction:
|
||||
case InstructionOp::lb:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u8 value;
|
||||
if (!ReadMemoryByte(addr, &value))
|
||||
return;
|
||||
@ -1075,6 +1151,9 @@ restart_instruction:
|
||||
case InstructionOp::lh:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u16 value;
|
||||
if (!ReadMemoryHalfWord(addr, &value))
|
||||
return;
|
||||
@ -1090,6 +1169,9 @@ restart_instruction:
|
||||
case InstructionOp::lw:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u32 value;
|
||||
if (!ReadMemoryWord(addr, &value))
|
||||
return;
|
||||
@ -1104,6 +1186,9 @@ restart_instruction:
|
||||
case InstructionOp::lbu:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u8 value;
|
||||
if (!ReadMemoryByte(addr, &value))
|
||||
return;
|
||||
@ -1119,6 +1204,9 @@ restart_instruction:
|
||||
case InstructionOp::lhu:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u16 value;
|
||||
if (!ReadMemoryHalfWord(addr, &value))
|
||||
return;
|
||||
@ -1136,6 +1224,9 @@ restart_instruction:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
|
||||
if constexpr (debug)
|
||||
Cop0DataBreakpointCheck<MemoryAccessType::Read>(addr);
|
||||
|
||||
u32 aligned_value;
|
||||
if (!ReadMemoryWord(aligned_addr, &aligned_value))
|
||||
return;
|
||||
@ -1165,6 +1256,9 @@ restart_instruction:
|
||||
case InstructionOp::sb:
|
||||
{
|
||||
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));
|
||||
WriteMemoryByte(addr, value);
|
||||
|
||||
@ -1176,6 +1270,9 @@ restart_instruction:
|
||||
case InstructionOp::sh:
|
||||
{
|
||||
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));
|
||||
WriteMemoryHalfWord(addr, value);
|
||||
|
||||
@ -1187,6 +1284,9 @@ restart_instruction:
|
||||
case InstructionOp::sw:
|
||||
{
|
||||
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);
|
||||
WriteMemoryWord(addr, value);
|
||||
|
||||
@ -1200,6 +1300,9 @@ restart_instruction:
|
||||
{
|
||||
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
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 u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
|
||||
u32 mem_value;
|
||||
@ -1508,16 +1611,20 @@ void DispatchInterrupt()
|
||||
g_state.regs.pc);
|
||||
}
|
||||
|
||||
static void UpdateDebugDispatcherFlag()
|
||||
void UpdateDebugDispatcherFlag()
|
||||
{
|
||||
const bool has_any_breakpoints = !s_breakpoints.empty();
|
||||
|
||||
// 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)
|
||||
return;
|
||||
|
||||
Log_DevPrintf("%s debug dispatcher", use_debug_dispatcher ? "Now using" : "No longer using");
|
||||
g_state.use_debug_dispatcher = use_debug_dispatcher;
|
||||
ForceDispatcherExit();
|
||||
}
|
||||
@ -1694,7 +1801,7 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search)
|
||||
return false;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE_RELEASE bool BreakpointCheck()
|
||||
ALWAYS_INLINE_RELEASE static bool BreakpointCheck()
|
||||
{
|
||||
const u32 pc = g_state.regs.pc;
|
||||
|
||||
@ -1779,6 +1886,8 @@ static void ExecuteImpl()
|
||||
|
||||
if constexpr (debug)
|
||||
{
|
||||
Cop0ExecutionBreakpointCheck();
|
||||
|
||||
if (BreakpointCheck())
|
||||
{
|
||||
// continue is measurably faster than break on msvc for some reason
|
||||
@ -1818,7 +1927,7 @@ static void ExecuteImpl()
|
||||
#endif
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
ExecuteInstruction<pgxp_mode, debug>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
@ -1891,7 +2000,7 @@ void InterpretCachedBlock(const CodeBlock& block)
|
||||
g_state.regs.npc += 4;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
ExecuteInstruction<pgxp_mode, false>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
@ -1944,7 +2053,7 @@ void InterpretUncachedBlock()
|
||||
}
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
ExecuteInstruction<pgxp_mode, false>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
@ -1969,13 +2078,13 @@ namespace Recompiler::Thunks {
|
||||
|
||||
bool InterpretInstruction()
|
||||
{
|
||||
ExecuteInstruction<PGXPMode::Disabled>();
|
||||
ExecuteInstruction<PGXPMode::Disabled, false>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
bool InterpretInstructionPGXP()
|
||||
{
|
||||
ExecuteInstruction<PGXPMode::Memory>();
|
||||
ExecuteInstruction<PGXPMode::Memory, false>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ ALWAYS_INLINE void CheckForPendingInterrupt()
|
||||
}
|
||||
|
||||
void DispatchInterrupt();
|
||||
void UpdateDebugDispatcherFlag();
|
||||
|
||||
// icache stuff
|
||||
ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address)
|
||||
|
@ -2494,25 +2494,55 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
||||
}
|
||||
}
|
||||
|
||||
if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mtcn &&
|
||||
(reg == Cop0Reg::CAUSE || reg == Cop0Reg::SR))
|
||||
if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mtcn)
|
||||
{
|
||||
// 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);
|
||||
if (reg == Cop0Reg::CAUSE || reg == Cop0Reg::SR)
|
||||
{
|
||||
// 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)
|
||||
LabelType no_interrupt;
|
||||
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));
|
||||
EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
|
||||
m_register_cache.InhibitAllocation();
|
||||
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
|
||||
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
|
||||
EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
|
||||
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
|
||||
EmitBindLabel(&no_interrupt);
|
||||
m_register_cache.UninhibitAllocation();
|
||||
// m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0)
|
||||
LabelType no_interrupt;
|
||||
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));
|
||||
EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
|
||||
m_register_cache.InhibitAllocation();
|
||||
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
|
||||
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
|
||||
EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
|
||||
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
|
||||
EmitBindLabel(&no_interrupt);
|
||||
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), ¬_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, ¬_enabled);
|
||||
|
||||
m_register_cache.UninhibitAllocation();
|
||||
|
||||
// exit block early if enabled
|
||||
m_register_cache.PushState();
|
||||
EmitFunctionCall(nullptr, &CPU::UpdateDebugDispatcherFlag);
|
||||
EmitExceptionExit();
|
||||
m_register_cache.PopState();
|
||||
|
||||
EmitBindLabel(¬_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
InstructionEpilogue(cbi);
|
||||
|
@ -283,8 +283,7 @@ struct Registers
|
||||
};
|
||||
};
|
||||
|
||||
std::optional<VirtualMemoryAddress> GetLoadStoreEffectiveAddress(const Instruction& instruction,
|
||||
const Registers* regs);
|
||||
std::optional<VirtualMemoryAddress> GetLoadStoreEffectiveAddress(const Instruction& instruction, const Registers* regs);
|
||||
|
||||
enum class Cop0Reg : u8
|
||||
{
|
||||
@ -402,6 +401,27 @@ struct Cop0Registers
|
||||
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 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;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user