CPU: Move interrupt check out of inner-most exec loop

This commit is contained in:
Connor McLaughlin 2020-10-17 01:28:08 +10:00
parent 0afdc04d88
commit cb351a7dbd
5 changed files with 66 additions and 51 deletions

View file

@ -132,18 +132,17 @@ static void ExecuteImpl()
g_state.frame_done = false; g_state.frame_done = false;
while (!g_state.frame_done) while (!g_state.frame_done)
{ {
if (HasPendingInterrupt())
{
SafeReadInstruction(g_state.regs.pc, &g_state.next_instruction.bits);
DispatchInterrupt();
}
TimingEvents::UpdateCPUDowncount(); TimingEvents::UpdateCPUDowncount();
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
while (g_state.pending_ticks < g_state.downcount) 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); CodeBlock* block = LookupBlock(next_block_key);
if (!block) if (!block)
{ {
@ -153,6 +152,7 @@ static void ExecuteImpl()
} }
reexecute_block: reexecute_block:
Assert(!(HasPendingInterrupt()));
#if 0 #if 0
const u32 tick = TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks(); const u32 tick = TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks();
@ -171,7 +171,7 @@ static void ExecuteImpl()
if (g_state.pending_ticks >= g_state.downcount) if (g_state.pending_ticks >= g_state.downcount)
break; break;
else if (HasPendingInterrupt() || !USE_BLOCK_LINKING) else if (!USE_BLOCK_LINKING)
continue; continue;
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
@ -242,10 +242,6 @@ void ExecuteRecompiler()
{ {
g_state.frame_done = false; g_state.frame_done = false;
while (!g_state.frame_done) while (!g_state.frame_done)
{
TimingEvents::UpdateCPUDowncount();
while (g_state.pending_ticks < g_state.downcount)
{ {
if (HasPendingInterrupt()) if (HasPendingInterrupt())
{ {
@ -253,6 +249,10 @@ void ExecuteRecompiler()
DispatchInterrupt(); DispatchInterrupt();
} }
TimingEvents::UpdateCPUDowncount();
while (g_state.pending_ticks < g_state.downcount)
{
const u32 pc = g_state.regs.pc; const u32 pc = g_state.regs.pc;
g_state.current_instruction_pc = pc; g_state.current_instruction_pc = pc;
const u32 fast_map_index = GetFastMapIndex(pc); const u32 fast_map_index = GetFastMapIndex(pc);

View file

@ -229,7 +229,16 @@ void RaiseException(u32 CAUSE_bits, u32 EPC)
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);
if (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter)
{
g_state.interrupt_delay = 1; g_state.interrupt_delay = 1;
}
else
{
g_state.interrupt_delay = 0;
CheckForPendingInterrupt();
}
} }
void ClearExternalInterrupt(u8 bit) 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 =
(g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK); (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); Log_DebugPrintf("COP0 SR <- %08X (now %08X)", value, g_state.cop0_regs.sr.bits);
CheckForPendingInterrupt();
} }
break; 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 =
(g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK); (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); Log_DebugPrintf("COP0 CAUSE <- %08X (now %08X)", value, g_state.cop0_regs.cause.bits);
CheckForPendingInterrupt();
} }
break; break;
@ -1216,6 +1227,7 @@ restart_instruction:
// restore mode // restore mode
g_state.cop0_regs.sr.mode_bits = 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); (g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
CheckForPendingInterrupt();
} }
break; 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<PGXPMode pgxp_mode> template<PGXPMode pgxp_mode>
static void ExecuteImpl() static void ExecuteImpl()
{ {
@ -1375,9 +1401,10 @@ static void ExecuteImpl()
while (g_state.pending_ticks < g_state.downcount) while (g_state.pending_ticks < g_state.downcount)
{ {
if (HasPendingInterrupt()) if (HasPendingInterrupt() && !g_state.interrupt_delay)
DispatchInterrupt(); DispatchInterrupt();
g_state.interrupt_delay = false;
g_state.pending_ticks++; g_state.pending_ticks++;
// now executing the instruction we previously fetched // now executing the instruction we previously fetched

View file

@ -8,33 +8,20 @@ namespace CPU {
void RaiseException(Exception excode); void RaiseException(Exception excode);
void RaiseException(u32 CAUSE_bits, u32 EPC); 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) return g_state.cop0_regs.sr.IEc &&
// != 0);
const bool do_interrupt = g_state.cop0_regs.sr.IEc &&
(((g_state.cop0_regs.cause.bits & g_state.cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0); (((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 if (HasPendingInterrupt())
// instruction. For some reason, if we don't do this, we end up with incorrectly sorted polygons and flickering.. g_state.downcount = 0;
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);
} }
void DispatchInterrupt();
// icache stuff // icache stuff
ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address) ALWAYS_INLINE bool IsCachedAddress(VirtualMemoryAddress address)
{ {

View file

@ -1930,21 +1930,8 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt); EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
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));
sr_value.ReleaseAndClear();
cause_value.ReleaseAndClear();
EmitConditionalBranch(Condition::Zero, false, &no_interrupt); EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
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();
EmitBindLabel(&no_interrupt); EmitBindLabel(&no_interrupt);
} }
@ -1979,6 +1966,16 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
EmitStoreCPUStructField(offsetof(State, cop0_regs.sr.bits), sr); 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); InstructionEpilogue(cbi);
return true; return true;
} }

View file

@ -3,6 +3,7 @@
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_core_private.h"
#include "system.h" #include "system.h"
Log_SetChannel(TimingEvents); Log_SetChannel(TimingEvents);
@ -49,8 +50,11 @@ std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount perio
void UpdateCPUDowncount() 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(); CPU::g_state.downcount = s_active_events_head->GetDowncount();
}
} }
static void SortEvent(TimingEvent* event) static void SortEvent(TimingEvent* event)