feat(apu): clock pulse/triangle/noise timers and sequencers

This commit is contained in:
2026-03-13 16:07:57 +03:00
parent cd0a99a813
commit 49568a582b
2 changed files with 63 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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)