From 49568a582b6cd1b0e6836ab6bf6c4116f79dcb0c Mon Sep 17 00:00:00 2001 From: "se.cherkasov" Date: Fri, 13 Mar 2026 16:07:57 +0300 Subject: [PATCH] feat(apu): clock pulse/triangle/noise timers and sequencers --- src/native_core/apu/api.rs | 7 +++++ src/native_core/apu/timing.rs | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/native_core/apu/api.rs b/src/native_core/apu/api.rs index 8cfd571..5250745 100644 --- a/src/native_core/apu/api.rs +++ b/src/native_core/apu/api.rs @@ -67,6 +67,8 @@ impl Apu { 0x4003 => { self.reload_length_counter(0, value >> 3); self.envelope_start_flags |= 1 << 0; + self.pulse_duty_step[0] = 0; + self.pulse_timer_counter[0] = self.pulse_timer_period(0x02); } 0x4001 => { self.sweep_reload_flags |= 1 << 0; @@ -74,6 +76,8 @@ impl Apu { 0x4007 => { self.reload_length_counter(1, value >> 3); self.envelope_start_flags |= 1 << 1; + self.pulse_duty_step[1] = 0; + self.pulse_timer_counter[1] = self.pulse_timer_period(0x06); } 0x4005 => { self.sweep_reload_flags |= 1 << 1; @@ -160,6 +164,9 @@ impl Apu { self.clock_frame_counter(); } self.clock_dmc(); + self.clock_pulse_timers(); + self.clock_triangle_timer(); + self.clock_noise_timer(); self.cpu_cycle_parity = !self.cpu_cycle_parity; } diff --git a/src/native_core/apu/timing.rs b/src/native_core/apu/timing.rs index 5401373..a0f9644 100644 --- a/src/native_core/apu/timing.rs +++ b/src/native_core/apu/timing.rs @@ -215,6 +215,62 @@ impl Apu { let hi = (self.io[timer_lo_idx + 1] as u16 & 0x07) << 8; hi | lo } + pub(crate) fn triangle_timer_period(&self) -> u16 { + let lo = self.io[0x0A] as u16; + let hi = (self.io[0x0B] as u16 & 0x07) << 8; + hi | lo + } + + pub(crate) fn noise_timer_period(&self) -> u16 { + const NOISE_PERIOD_TABLE: [u16; 16] = [ + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068, + ]; + let idx = (self.io[0x0E] & 0x0F) as usize; + NOISE_PERIOD_TABLE[idx] + } + + pub(crate) fn clock_pulse_timers(&mut self) { + if self.cpu_cycle_parity { + return; + } + for ch in 0..2usize { + if self.pulse_timer_counter[ch] == 0 { + let reg_offset = ch * 4; + let period = self.pulse_timer_period(reg_offset + 2); + self.pulse_timer_counter[ch] = period; + self.pulse_duty_step[ch] = (self.pulse_duty_step[ch] + 1) & 0x07; + } else { + self.pulse_timer_counter[ch] -= 1; + } + } + } + + pub(crate) fn clock_triangle_timer(&mut self) { + if self.triangle_timer_counter == 0 { + self.triangle_timer_counter = self.triangle_timer_period(); + if self.length_counters[2] > 0 && self.triangle_linear_counter > 0 { + self.triangle_step = (self.triangle_step + 1) & 0x1F; + } + } else { + self.triangle_timer_counter -= 1; + } + } + + pub(crate) fn clock_noise_timer(&mut self) { + if self.cpu_cycle_parity { + return; + } + if self.noise_timer_counter == 0 { + self.noise_timer_counter = self.noise_timer_period(); + let mode_flag = (self.io[0x0E] & 0x80) != 0; + let feedback_bit = if mode_flag { 6 } else { 1 }; + let feedback = (self.noise_lfsr & 1) ^ ((self.noise_lfsr >> feedback_bit) & 1); + self.noise_lfsr = (self.noise_lfsr >> 1) | (feedback << 14); + } else { + self.noise_timer_counter -= 1; + } + } + pub(crate) fn set_pulse_timer_period(&mut self, channel: usize, period: u16) { let (timer_lo_idx, timer_hi_idx) = if channel == 0 { (0x02, 0x03)