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<TickCount>::max();
+
+  TickCount ticks_until_irq = std::numeric_limits<TickCount>::max();
+  if (cs.mode.irq_at_target)
+    ticks_until_irq = static_cast<TickCount>(cs.target - cs.counter);
+  if (cs.mode.irq_on_overflow)
+    ticks_until_irq = std::min(ticks_until_irq, static_cast<TickCount>(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<TimingEvent> m_sysclk_event;
 
   std::array<CounterState, NUM_TIMERS> 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
 };