From 5a1b00825deef23c5289b9b038b595e9efa50793 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 28 Apr 2020 20:30:44 +1000 Subject: [PATCH] GPU: Fix timer 1 IRQs sometimes triggering late Should hopefully fix missing voices in Akuji the Heartless. --- src/core/gpu.cpp | 18 ++++++++++++------ src/core/timers.cpp | 15 +++++++++++++++ src/core/timers.h | 12 +++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index b8bd9c5cd..5051f6b38 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -568,20 +568,26 @@ TickCount GPU::GetPendingGPUTicks() const void GPU::UpdateSliceTicks() { - // figure out how many GPU ticks until the next vblank + // figure out how many GPU ticks until the next vblank or event const TickCount lines_until_vblank = (m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end ? (m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) : (m_crtc_state.vertical_display_end - m_crtc_state.current_scanline)); - const TickCount ticks_until_vblank = - lines_until_vblank * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline; + const TickCount lines_until_event = m_timers->IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ? + std::min(m_timers->GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) : + lines_until_vblank; + const TickCount ticks_until_event = + lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline; + +#if 0 const TickCount ticks_until_hblank = (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end) ? - (m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline + m_crtc_state.horizontal_display_end) : - (m_crtc_state.horizontal_display_end - m_crtc_state.current_tick_in_scanline); + (m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline + m_crtc_state.horizontal_display_end) : + (m_crtc_state.horizontal_display_end - m_crtc_state.current_tick_in_scanline); +#endif m_tick_event->Schedule( - GPUTicksToSystemTicks((m_command_ticks > 0) ? std::min(m_command_ticks, ticks_until_vblank) : ticks_until_vblank)); + GPUTicksToSystemTicks((m_command_ticks > 0) ? std::min(m_command_ticks, ticks_until_event) : ticks_until_event)); } bool GPU::IsRasterScanlinePending() const diff --git a/src/core/timers.cpp b/src/core/timers.cpp index 9022d6ed2..0078ededc 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -89,6 +89,21 @@ void Timers::SetGate(u32 timer, bool state) } } +TickCount Timers::GetTicksUntilIRQ(u32 timer) const +{ + const CounterState& cs = m_states[timer]; + if (!cs.counting_enabled) + return std::numeric_limits::max(); + + TickCount ticks_until_irq = std::numeric_limits::max(); + if (cs.mode.irq_at_target) + ticks_until_irq = static_cast(cs.target - cs.counter); + if (cs.mode.irq_on_overflow) + ticks_until_irq = std::min(ticks_until_irq, static_cast(0xFFFFu - cs.counter)); + + return ticks_until_irq; +} + void Timers::AddTicks(u32 timer, TickCount count) { CounterState& cs = m_states[timer]; diff --git a/src/core/timers.h b/src/core/timers.h index 2d05af747..488877d8e 100644 --- a/src/core/timers.h +++ b/src/core/timers.h @@ -27,6 +27,16 @@ public: // dot clock/hblank/sysclk div 8 bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; } + + // queries for GPU + bool IsExternalIRQEnabled(u32 timer) const + { + const CounterState& cs = m_states[timer]; + return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0); + } + + TickCount GetTicksUntilIRQ(u32 timer) const; + void AddTicks(u32 timer, TickCount ticks); u32 ReadRegister(u32 offset); @@ -89,5 +99,5 @@ private: std::unique_ptr m_sysclk_event; std::array m_states{}; - u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8 + u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8 };