From d75705689125df2134b29accdbd7028c76cf994e Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 5 Dec 2020 15:59:47 +1000 Subject: [PATCH] GPU: Implement dot clock timer (Timer 0) Fixes softlock in Evil Dead - Hail to the King. --- src/core/gpu.cpp | 27 ++++++++++++++++++++++----- src/core/gpu.h | 2 ++ src/core/save_state_version.h | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 65d5406d5..229549792 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -91,6 +91,7 @@ void GPU::SoftReset() m_crtc_state.regs.horizontal_display_range = 0xC60260; m_crtc_state.regs.vertical_display_range = 0x3FC10; m_crtc_state.fractional_ticks = 0; + m_crtc_state.fractional_dot_ticks = 0; m_crtc_state.current_tick_in_scanline = 0; m_crtc_state.current_scanline = 0; m_crtc_state.in_hblank = false; @@ -172,6 +173,7 @@ bool GPU::DoState(StateWrapper& sw, bool update_display) sw.Do(&m_crtc_state.fractional_ticks); sw.Do(&m_crtc_state.current_tick_in_scanline); sw.Do(&m_crtc_state.current_scanline); + sw.DoEx(&m_crtc_state.fractional_dot_ticks, 46, 0); sw.Do(&m_crtc_state.in_hblank); sw.Do(&m_crtc_state.in_vblank); sw.Do(&m_crtc_state.interlaced_field); @@ -679,15 +681,21 @@ TickCount GPU::GetPendingCommandTicks() const void GPU::UpdateCRTCTickEvent() { // figure out how many GPU ticks until the next vblank or event - const TickCount lines_until_vblank = + TickCount lines_until_event = (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 lines_until_event = g_timers.IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ? - std::min(g_timers.GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) : - lines_until_vblank; - const TickCount ticks_until_event = + if (g_timers.IsExternalIRQEnabled(HBLANK_TIMER_INDEX)) + lines_until_event = std::min(lines_until_event, g_timers.GetTicksUntilIRQ(HBLANK_TIMER_INDEX)); + + TickCount ticks_until_event = lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline; + if (g_timers.IsExternalIRQEnabled(DOT_TIMER_INDEX)) + { + const TickCount dots_until_irq = g_timers.GetTicksUntilIRQ(DOT_TIMER_INDEX); + const TickCount ticks_until_irq = (dots_until_irq * m_crtc_state.dot_clock_divider) - m_crtc_state.fractional_dot_ticks; + ticks_until_event = std::min(ticks_until_event, std::max(ticks_until_irq, 0)); + } #if 0 const TickCount ticks_until_hblank = @@ -715,6 +723,15 @@ void GPU::CRTCTickEvent(TickCount ticks) { const TickCount gpu_ticks = SystemTicksToCRTCTicks(ticks, &m_crtc_state.fractional_ticks); m_crtc_state.current_tick_in_scanline += gpu_ticks; + + if (g_timers.IsUsingExternalClock(DOT_TIMER_INDEX)) + { + m_crtc_state.fractional_dot_ticks += gpu_ticks; + const TickCount dots = m_crtc_state.fractional_dot_ticks / m_crtc_state.dot_clock_divider; + m_crtc_state.fractional_dot_ticks = m_crtc_state.fractional_dot_ticks % m_crtc_state.dot_clock_divider; + if (dots > 0) + g_timers.AddTicks(DOT_TIMER_INDEX, dots); + } } if (m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_total) diff --git a/src/core/gpu.h b/src/core/gpu.h index 4c8ab4acd..bc2dd43a5 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -501,6 +501,8 @@ protected: TickCount current_tick_in_scanline; u32 current_scanline; + TickCount fractional_dot_ticks; // only used when timer0 is enabled + bool in_hblank; bool in_vblank; diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index d12cfa696..b31e12a80 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,7 +2,7 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 45; +static constexpr u32 SAVE_STATE_VERSION = 46; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);