feat(apu): clock pulse/triangle/noise timers and sequencers
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user