From d373b5d3a9ed59ed6174339d5957fdfa441dfb24 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 8 Oct 2019 18:21:15 +1000 Subject: [PATCH] Timers: Fix oneshot/toggle modes --- src/core/system.cpp | 2 +- src/core/timers.cpp | 128 +++++++++++++++++++++++++++++++++----------- src/core/timers.h | 12 +++-- 3 files changed, 104 insertions(+), 38 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index d61b1b9f8..9d2447d1b 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -284,7 +284,7 @@ void System::Synchronize() m_global_tick_counter += static_cast(pending_ticks); m_gpu->Execute(pending_ticks); - m_timers->AddSystemTicks(pending_ticks); + m_timers->Execute(pending_ticks); m_cdrom->Execute(pending_ticks); m_pad->Execute(pending_ticks); m_dma->Execute(pending_ticks); diff --git a/src/core/timers.cpp b/src/core/timers.cpp index 9045c4fff..86429edce 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -26,7 +26,10 @@ void Timers::Reset() cs.gate = false; cs.external_counting_enabled = false; cs.counting_enabled = true; + cs.irq_done = false; } + + m_sysclk_div_8_carry = 0; } bool Timers::DoState(StateWrapper& sw) @@ -40,8 +43,10 @@ bool Timers::DoState(StateWrapper& sw) sw.Do(&cs.use_external_clock); sw.Do(&cs.external_counting_enabled); sw.Do(&cs.counting_enabled); + sw.Do(&cs.irq_done); } + sw.Do(&m_sysclk_div_8_carry); return !sw.HasError(); } @@ -74,46 +79,71 @@ void Timers::SetGate(u32 timer, bool state) } } -void Timers::AddTicks(u32 timer, u32 count) +void Timers::AddTicks(u32 timer, TickCount count) { CounterState& cs = m_states[timer]; - cs.counter += count; + const u32 old_counter = cs.counter; + cs.counter += static_cast(count); - const u32 reset_value = cs.mode.reset_at_target ? cs.target : u32(0xFFFF); - if (cs.counter < reset_value) - return; - - const bool old_intr = cs.mode.interrupt_request; - - if (cs.counter >= cs.target) - cs.mode.reached_target = true; - if (cs.counter >= u32(0xFFFF)) - cs.mode.reached_overflow = true; - - // TODO: Non-repeat mode. - const bool target_intr = cs.mode.reached_target & cs.mode.irq_at_target; - const bool overflow_intr = cs.mode.reached_overflow & cs.mode.irq_on_overflow; - const bool new_intr = target_intr | overflow_intr; - if (!old_intr && new_intr) + bool interrupt_request = false; + if (cs.counter >= cs.target && old_counter < cs.target) { - m_interrupt_controller->InterruptRequest( - static_cast(static_cast(InterruptController::IRQ::TMR0) + timer)); + interrupt_request = true; + cs.mode.reached_target = true; + } + if (cs.counter >= 0xFFFF) + { + interrupt_request = true; + cs.mode.reached_overflow = true; } - if (reset_value > 0) - cs.counter = cs.counter % reset_value; + if (interrupt_request) + { + if (!cs.mode.irq_pulse_n) + { + // this is actually low for a few cycles + cs.mode.interrupt_request_n = false; + UpdateIRQ(timer); + cs.mode.interrupt_request_n = true; + } + else + { + cs.mode.interrupt_request_n ^= true; + UpdateIRQ(timer); + } + } + + if (cs.mode.reset_at_target) + { + if (cs.target > 0) + cs.counter %= cs.target; + else + cs.counter = 0; + } else - cs.counter = 0; + { + cs.counter %= 0xFFFF; + } } -void Timers::AddSystemTicks(u32 ticks) +void Timers::Execute(TickCount sysclk_ticks) { if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled) - AddTicks(0, ticks); + AddTicks(0, sysclk_ticks); if (!m_states[1].external_counting_enabled && m_states[1].counting_enabled) - AddTicks(1, ticks); - if (m_states[2].counting_enabled) - AddTicks(2, m_states[2].external_counting_enabled ? (ticks / 8) : (ticks)); + AddTicks(1, sysclk_ticks); + if (m_states[2].external_counting_enabled) + { + TickCount sysclk_div_8_ticks = (sysclk_ticks + m_sysclk_div_8_carry) / 8; + m_sysclk_div_8_carry = (sysclk_ticks + m_sysclk_div_8_carry) % 8; + AddTicks(2, sysclk_div_8_ticks); + } + else if (m_states[2].counting_enabled) + { + AddTicks(2, m_states[2].external_counting_enabled ? sysclk_ticks / 8 : sysclk_ticks); + } + + UpdateDowncount(); } u32 Timers::ReadRegister(u32 offset) @@ -174,7 +204,12 @@ void Timers::WriteRegister(u32 offset, u32 value) cs.mode.bits = value & u32(0x1FFF); cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0; cs.counter = 0; + cs.irq_done = false; + if (cs.mode.irq_pulse_n) + cs.mode.interrupt_request_n = true; + UpdateCountingEnabled(cs); + UpdateIRQ(timer_index); } break; @@ -220,9 +255,38 @@ void Timers::UpdateCountingEnabled(CounterState& cs) cs.external_counting_enabled = cs.use_external_clock && cs.counting_enabled; } -void Timers::UpdateDowncount() {} - -u32 Timers::GetSystemTicksForTimerTicks(u32 timer) const +void Timers::UpdateIRQ(u32 index) { - return 1; + CounterState& cs = m_states[index]; + if (cs.mode.interrupt_request_n || (!cs.mode.irq_repeat && cs.irq_done)) + return; + + Log_DebugPrintf("Raising timer %u IRQ", index); + cs.irq_done = true; + m_interrupt_controller->InterruptRequest( + static_cast(static_cast(InterruptController::IRQ::TMR0) + index)); +} + +void Timers::UpdateDowncount() +{ + TickCount min_ticks = std::numeric_limits::max(); + for (u32 i = 0; i < NUM_TIMERS; i++) + { + CounterState& cs = m_states[i]; + if (!cs.counting_enabled || (i < 2 && cs.external_counting_enabled)) + continue; + + TickCount min_ticks_for_this_timer = min_ticks; + if (cs.mode.irq_at_target && cs.counter < cs.target) + min_ticks_for_this_timer = static_cast(cs.target - cs.counter); + if (cs.mode.irq_on_overflow && cs.counter < cs.target) + min_ticks_for_this_timer = std::min(min_ticks_for_this_timer, static_cast(0xFFFF - cs.counter)); + + if (cs.external_counting_enabled) // sysclk/8 for timer 2 + min_ticks_for_this_timer = std::max(1, min_ticks_for_this_timer / 8); + + min_ticks = std::min(min_ticks, min_ticks_for_this_timer); + } + + m_system->SetDowncount(min_ticks); } diff --git a/src/core/timers.h b/src/core/timers.h index 7cfa61eed..dbce67cde 100644 --- a/src/core/timers.h +++ b/src/core/timers.h @@ -22,8 +22,8 @@ public: // dot clock/hblank/sysclk div 8 bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; } - void AddTicks(u32 timer, u32 ticks); - void AddSystemTicks(u32 ticks); + void AddTicks(u32 timer, TickCount ticks); + void Execute(TickCount sysclk_ticks); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); @@ -49,9 +49,9 @@ private: BitField irq_at_target; BitField irq_on_overflow; BitField irq_repeat; - BitField irq_pulse; + BitField irq_pulse_n; BitField clock_source; - BitField interrupt_request; + BitField interrupt_request_n; BitField reached_target; BitField reached_overflow; }; @@ -65,15 +65,17 @@ private: bool use_external_clock; bool external_counting_enabled; bool counting_enabled; + bool irq_done; }; void UpdateCountingEnabled(CounterState& cs); + void UpdateIRQ(u32 index); void UpdateDowncount(); - u32 GetSystemTicksForTimerTicks(u32 timer) const; System* m_system = nullptr; InterruptController* m_interrupt_controller = nullptr; std::array m_states{}; + u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8 };