diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 6244944c7..af089e054 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -1545,7 +1545,9 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi) { u32 offset; u32 write_mask = UINT32_C(0xFFFFFFFF); - switch (static_cast(cbi.instruction.r.rd.GetValue())) + + const Cop0Reg reg = static_cast(cbi.instruction.r.rd.GetValue()); + switch (reg) { case Cop0Reg::BPC: offset = offsetof(Core, m_cop0_regs.BPC); @@ -1627,6 +1629,37 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi) } } + if (cbi.instruction.cop.CommonOp() == CopCommonInstruction::mtcn && + (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(Core, m_cop0_regs.sr.bits)); + EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(Core, m_cop0_regs.cause.bits)); + EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt); + EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value); + EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00)); + sr_value.ReleaseAndClear(); + cause_value.ReleaseAndClear(); + EmitConditionalBranch(Condition::Zero, false, &no_interrupt); + + EmitBranch(GetCurrentFarCodePointer()); + SwitchToFarCode(); + + // we want to flush pc here + m_register_cache.PushState(); + m_register_cache.FlushAllGuestRegisters(false, true); + EmitExceptionExit(); + m_register_cache.PopState(); + + SwitchToNearCode(); + EmitBindLabel(&no_interrupt); + } + InstructionEpilogue(cbi); return true; }