diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 355940c92..c5d917617 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -65,8 +65,6 @@ void GPU::SoftReset() m_draw_mode.SetTextureWindow(0); UpdateDMARequest(); UpdateCRTCConfig(); - - m_tick_event->Deactivate(); UpdateSliceTicks(); } @@ -291,8 +289,13 @@ void GPU::DMAWrite(const u32* words, u32 word_count) { Assert(m_blitter_ticks == 0); m_blitter_ticks = GetPendingGPUTicks() + word_count; + + // reschedule GPU tick event + const TickCount sysclk_ticks = GPUTicksToSystemTicks(word_count); + if (m_tick_event->GetTicksUntilNextExecution() > sysclk_ticks) + m_tick_event->Schedule(sysclk_ticks); + UpdateDMARequest(); - UpdateSliceTicks(); } } break; @@ -434,12 +437,6 @@ void GPU::UpdateCRTCConfig() UpdateSliceTicks(); } -static TickCount GPUTicksToSystemTicks(TickCount gpu_ticks) -{ - // convert to master clock, rounding up as we want to overshoot not undershoot - return static_cast((static_cast(gpu_ticks) * 7u + 10u) / 11u); -} - TickCount GPU::GetPendingGPUTicks() const { const TickCount pending_sysclk_ticks = m_tick_event->GetTicksSinceLastExecution(); @@ -462,7 +459,6 @@ void GPU::UpdateSliceTicks() m_tick_event->Schedule( GPUTicksToSystemTicks((m_blitter_ticks > 0) ? std::min(m_blitter_ticks, ticks_until_vblank) : ticks_until_vblank)); - m_tick_event->SetPeriod(GPUTicksToSystemTicks(ticks_until_hblank)); } bool GPU::IsRasterScanlinePending() const @@ -479,14 +475,14 @@ void GPU::Execute(TickCount ticks) m_crtc_state.fractional_ticks = ticks_mul_11 % 7; m_crtc_state.current_tick_in_scanline += gpu_ticks; - if (m_blitter_ticks > 0) + // handle blits + TickCount blit_ticks_remaining = gpu_ticks; + while (m_blitter_ticks > 0 && blit_ticks_remaining > 0) { - m_blitter_ticks -= gpu_ticks; - if (m_blitter_ticks <= 0) - { - m_blitter_ticks = 0; - UpdateDMARequest(); - } + const TickCount slice = std::min(blit_ticks_remaining, m_blitter_ticks); + m_blitter_ticks -= slice; + blit_ticks_remaining -= slice; + UpdateDMARequest(); } } @@ -636,6 +632,7 @@ void GPU::WriteGP1(u32 value) case 0x00: // Reset GPU { Log_DebugPrintf("GP1 reset GPU"); + Synchronize(); SoftReset(); } break; @@ -647,6 +644,7 @@ void GPU::WriteGP1(u32 value) m_command_total_words = 0; m_vram_transfer = {}; m_GP0_buffer.clear(); + m_blitter_ticks = 0; UpdateDMARequest(); } break; @@ -662,7 +660,7 @@ void GPU::WriteGP1(u32 value) { const bool disable = ConvertToBoolUnchecked(value & 0x01); Log_DebugPrintf("Display %s", disable ? "disabled" : "enabled"); - m_tick_event->InvokeEarly(); + Synchronize(); m_GPUSTAT.display_disable = disable; } break; @@ -690,7 +688,7 @@ void GPU::WriteGP1(u32 value) if (m_crtc_state.regs.horizontal_display_range != new_value) { - m_tick_event->InvokeEarly(true); + Synchronize(); m_crtc_state.regs.horizontal_display_range = new_value; UpdateCRTCConfig(); } @@ -704,7 +702,7 @@ void GPU::WriteGP1(u32 value) if (m_crtc_state.regs.vertical_display_range != new_value) { - m_tick_event->InvokeEarly(true); + Synchronize(); m_crtc_state.regs.vertical_display_range = new_value; UpdateCRTCConfig(); } @@ -739,7 +737,7 @@ void GPU::WriteGP1(u32 value) if (m_GPUSTAT.bits != new_GPUSTAT.bits) { - m_tick_event->InvokeEarly(true); + Synchronize(); m_GPUSTAT.bits = new_GPUSTAT.bits; UpdateCRTCConfig(); } diff --git a/src/core/gpu.h b/src/core/gpu.h index eab1de2aa..bc8319fac 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -133,6 +133,12 @@ public: void DMARead(u32* words, u32 word_count); void DMAWrite(const u32* words, u32 word_count); + /// Returns the number of pending GPU ticks. + TickCount GetPendingGPUTicks() const; + + /// Returns true if enough ticks have passed for the raster to be on the next line. + bool IsRasterScanlinePending() const; + // Synchronizes the CRTC, updating the hblank timer. void Synchronize(); @@ -152,6 +158,12 @@ public: static std::unique_ptr CreateSoftwareRenderer(); protected: + static TickCount GPUTicksToSystemTicks(TickCount gpu_ticks) + { + // convert to master clock, rounding up as we want to overshoot not undershoot + return static_cast((static_cast(gpu_ticks) * 7u + 10u) / 11u); + } + // Helper/format conversion functions. static constexpr u8 Convert5To8(u8 x5) { return (x5 << 3) | (x5 & 7); } static constexpr u8 Convert8To5(u8 x8) { return (x8 >> 3); } @@ -311,12 +323,6 @@ protected: // Ticks for hblank/vblank. void Execute(TickCount ticks); - /// Returns the number of pending GPU ticks. - TickCount GetPendingGPUTicks() const; - - /// Returns true if enough ticks have passed for the raster to be on the next line. - bool IsRasterScanlinePending() const; - /// Returns true if scanout should be interlaced. bool IsDisplayInterlaced() const { return !m_force_progressive_scan && m_GPUSTAT.In480iMode(); } diff --git a/src/core/timers.cpp b/src/core/timers.cpp index 4704c405c..124895220 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -166,10 +166,10 @@ u32 Timers::ReadRegister(u32 offset) { case 0x00: { - if (timer_index < 2) + if (timer_index < 2 && cs.external_counting_enabled) { // timers 0/1 depend on the GPU - if (cs.external_counting_enabled) + if (timer_index == 0 || m_gpu->IsRasterScanlinePending()) m_gpu->Synchronize(); } @@ -180,10 +180,10 @@ u32 Timers::ReadRegister(u32 offset) case 0x04: { - if (timer_index < 2) + if (timer_index < 2 && cs.external_counting_enabled) { // timers 0/1 depend on the GPU - if (cs.external_counting_enabled) + if (timer_index == 0 || m_gpu->IsRasterScanlinePending()) m_gpu->Synchronize(); } @@ -211,8 +211,13 @@ void Timers::WriteRegister(u32 offset, u32 value) CounterState& cs = m_states[timer_index]; - if (timer_index < 2) - m_gpu->Synchronize(); + if (timer_index < 2 && cs.external_counting_enabled) + { + // timers 0/1 depend on the GPU + if (timer_index == 0 || m_gpu->IsRasterScanlinePending()) + m_gpu->Synchronize(); + } + m_sysclk_event->InvokeEarly(); switch (port_offset) diff --git a/src/core/timing_event.cpp b/src/core/timing_event.cpp index ed3000b3b..94903d260 100644 --- a/src/core/timing_event.cpp +++ b/src/core/timing_event.cpp @@ -74,7 +74,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */) if (!m_active) return; - const TickCount pending_ticks = m_system->m_running_events ? 0 : m_system->m_cpu->GetPendingTicks(); + const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks(); const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks; if (!force && ticks_to_execute < m_period) return; @@ -93,7 +93,7 @@ void TimingEvent::Activate() return; // leave the downcount intact - const TickCount pending_ticks = m_system->m_running_events ? 0 : m_system->m_cpu->GetPendingTicks(); + const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks(); m_downcount += pending_ticks; m_time_since_last_run -= pending_ticks; @@ -106,7 +106,7 @@ void TimingEvent::Deactivate() if (!m_active) return; - const TickCount pending_ticks = m_system->m_running_events ? 0 : m_system->m_cpu->GetPendingTicks(); + const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks(); m_downcount -= pending_ticks; m_time_since_last_run += pending_ticks;