diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 727f449d8..f6bf291f2 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -132,18 +132,17 @@ static void ExecuteImpl() g_state.frame_done = false; while (!g_state.frame_done) { + if (HasPendingInterrupt()) + { + SafeReadInstruction(g_state.regs.pc, &g_state.next_instruction.bits); + DispatchInterrupt(); + } + TimingEvents::UpdateCPUDowncount(); next_block_key = GetNextBlockKey(); while (g_state.pending_ticks < g_state.downcount) { - if (HasPendingInterrupt()) - { - SafeReadInstruction(g_state.regs.pc, &g_state.next_instruction.bits); - DispatchInterrupt(); - next_block_key = GetNextBlockKey(); - } - CodeBlock* block = LookupBlock(next_block_key); if (!block) { @@ -153,6 +152,7 @@ static void ExecuteImpl() } reexecute_block: + Assert(!(HasPendingInterrupt())); #if 0 const u32 tick = TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks(); @@ -171,7 +171,7 @@ static void ExecuteImpl() if (g_state.pending_ticks >= g_state.downcount) break; - else if (HasPendingInterrupt() || !USE_BLOCK_LINKING) + else if (!USE_BLOCK_LINKING) continue; next_block_key = GetNextBlockKey(); @@ -243,16 +243,16 @@ void ExecuteRecompiler() g_state.frame_done = false; while (!g_state.frame_done) { + if (HasPendingInterrupt()) + { + SafeReadInstruction(g_state.regs.pc, &g_state.next_instruction.bits); + DispatchInterrupt(); + } + TimingEvents::UpdateCPUDowncount(); while (g_state.pending_ticks < g_state.downcount) { - if (HasPendingInterrupt()) - { - SafeReadInstruction(g_state.regs.pc, &g_state.next_instruction.bits); - DispatchInterrupt(); - } - const u32 pc = g_state.regs.pc; g_state.current_instruction_pc = pc; const u32 fast_map_index = GetFastMapIndex(pc); diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 1f48ceb06..7bf72a92d 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -229,7 +229,16 @@ void RaiseException(u32 CAUSE_bits, u32 EPC) void SetExternalInterrupt(u8 bit) { g_state.cop0_regs.cause.Ip |= static_cast(1u << bit); - g_state.interrupt_delay = 1; + + if (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter) + { + g_state.interrupt_delay = 1; + } + else + { + g_state.interrupt_delay = 0; + CheckForPendingInterrupt(); + } } void ClearExternalInterrupt(u8 bit) @@ -395,6 +404,7 @@ ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value) g_state.cop0_regs.sr.bits = (g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK); Log_DebugPrintf("COP0 SR <- %08X (now %08X)", value, g_state.cop0_regs.sr.bits); + CheckForPendingInterrupt(); } break; @@ -403,6 +413,7 @@ ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value) g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK); Log_DebugPrintf("COP0 CAUSE <- %08X (now %08X)", value, g_state.cop0_regs.cause.bits); + CheckForPendingInterrupt(); } break; @@ -1216,6 +1227,7 @@ restart_instruction: // restore mode g_state.cop0_regs.sr.mode_bits = (g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2); + CheckForPendingInterrupt(); } break; @@ -1365,6 +1377,20 @@ restart_instruction: } } +void DispatchInterrupt() +{ + // If the instruction we're about to execute is a GTE instruction, delay dispatching the interrupt until the next + // instruction. For some reason, if we don't do this, we end up with incorrectly sorted polygons and flickering.. + if (g_state.next_instruction.op == InstructionOp::cop2 && !g_state.next_instruction.cop.IsCommonInstruction()) + GTE::ExecuteInstruction(g_state.next_instruction.bits); + + // Interrupt raising occurs before the start of the instruction. + RaiseException( + Cop0Registers::CAUSE::MakeValueForException(Exception::INT, g_state.next_instruction_is_branch_delay_slot, + g_state.branch_was_taken, g_state.next_instruction.cop.cop_n), + g_state.regs.pc); +} + template static void ExecuteImpl() { @@ -1375,9 +1401,10 @@ static void ExecuteImpl() while (g_state.pending_ticks < g_state.downcount) { - if (HasPendingInterrupt()) + if (HasPendingInterrupt() && !g_state.interrupt_delay) DispatchInterrupt(); + g_state.interrupt_delay = false; g_state.pending_ticks++; // now executing the instruction we previously fetched diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index 05ee62f5f..caff0de60 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -8,33 +8,20 @@ namespace CPU { void RaiseException(Exception excode); void RaiseException(u32 CAUSE_bits, u32 EPC); -ALWAYS_INLINE static bool HasPendingInterrupt() +ALWAYS_INLINE bool HasPendingInterrupt() { - // const bool do_interrupt = g_state.m_cop0_regs.sr.IEc && ((g_state.m_cop0_regs.cause.Ip & g_state.m_cop0_regs.sr.Im) - // != 0); - const bool do_interrupt = g_state.cop0_regs.sr.IEc && + return g_state.cop0_regs.sr.IEc && (((g_state.cop0_regs.cause.bits & g_state.cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0); - - const bool interrupt_delay = g_state.interrupt_delay; - g_state.interrupt_delay = false; - - return do_interrupt && !interrupt_delay; } -ALWAYS_INLINE static void DispatchInterrupt() +ALWAYS_INLINE void CheckForPendingInterrupt() { - // If the instruction we're about to execute is a GTE instruction, delay dispatching the interrupt until the next - // instruction. For some reason, if we don't do this, we end up with incorrectly sorted polygons and flickering.. - if (g_state.next_instruction.IsCop2Instruction()) - return; - - // Interrupt raising occurs before the start of the instruction. - RaiseException( - Cop0Registers::CAUSE::MakeValueForException(Exception::INT, g_state.next_instruction_is_branch_delay_slot, - g_state.branch_was_taken, g_state.next_instruction.cop.cop_n), - g_state.regs.pc); + if (HasPendingInterrupt()) + g_state.downcount = 0; } +void DispatchInterrupt(); + // icache stuff ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address) { diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 7f7a81bba..55ce19a52 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -1930,21 +1930,8 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi) 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); - WriteNewPC(CalculatePC(), false); - EmitExceptionExit(); - m_register_cache.PopState(); - - SwitchToNearCode(); + EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0)); EmitBindLabel(&no_interrupt); } @@ -1979,6 +1966,16 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi) EmitStoreCPUStructField(offsetof(State, cop0_regs.sr.bits), sr); + Value cause_value = m_register_cache.AllocateScratch(RegSize_32); + EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(State, cop0_regs.cause.bits)); + + LabelType no_interrupt; + EmitAnd(sr.host_reg, sr.host_reg, cause_value); + EmitTest(sr.host_reg, Value::FromConstantU32(0xFF00)); + EmitConditionalBranch(Condition::Zero, false, &no_interrupt); + EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0)); + EmitBindLabel(&no_interrupt); + InstructionEpilogue(cbi); return true; } diff --git a/src/core/timing_event.cpp b/src/core/timing_event.cpp index 8197eff80..2c8de39ec 100644 --- a/src/core/timing_event.cpp +++ b/src/core/timing_event.cpp @@ -3,6 +3,7 @@ #include "common/log.h" #include "common/state_wrapper.h" #include "cpu_core.h" +#include "cpu_core_private.h" #include "system.h" Log_SetChannel(TimingEvents); @@ -49,8 +50,11 @@ std::unique_ptr CreateTimingEvent(std::string name, TickCount perio void UpdateCPUDowncount() { - if (!CPU::g_state.frame_done) + if (!CPU::g_state.frame_done && + (!CPU::HasPendingInterrupt() || g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter)) + { CPU::g_state.downcount = s_active_events_head->GetDowncount(); + } } static void SortEvent(TimingEvent* event)