mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-27 08:05:41 +00:00
CPU: Move interrupt check out of inner-most exec loop
This commit is contained in:
parent
0afdc04d88
commit
cb351a7dbd
|
@ -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);
|
||||||
|
|
|
@ -229,8 +229,17 @@ 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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,9 +50,12 @@ 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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue