feat(apu): clock pulse/triangle/noise timers and sequencers
This commit is contained in:
@@ -67,6 +67,8 @@ impl Apu {
|
|||||||
0x4003 => {
|
0x4003 => {
|
||||||
self.reload_length_counter(0, value >> 3);
|
self.reload_length_counter(0, value >> 3);
|
||||||
self.envelope_start_flags |= 1 << 0;
|
self.envelope_start_flags |= 1 << 0;
|
||||||
|
self.pulse_duty_step[0] = 0;
|
||||||
|
self.pulse_timer_counter[0] = self.pulse_timer_period(0x02);
|
||||||
}
|
}
|
||||||
0x4001 => {
|
0x4001 => {
|
||||||
self.sweep_reload_flags |= 1 << 0;
|
self.sweep_reload_flags |= 1 << 0;
|
||||||
@@ -74,6 +76,8 @@ impl Apu {
|
|||||||
0x4007 => {
|
0x4007 => {
|
||||||
self.reload_length_counter(1, value >> 3);
|
self.reload_length_counter(1, value >> 3);
|
||||||
self.envelope_start_flags |= 1 << 1;
|
self.envelope_start_flags |= 1 << 1;
|
||||||
|
self.pulse_duty_step[1] = 0;
|
||||||
|
self.pulse_timer_counter[1] = self.pulse_timer_period(0x06);
|
||||||
}
|
}
|
||||||
0x4005 => {
|
0x4005 => {
|
||||||
self.sweep_reload_flags |= 1 << 1;
|
self.sweep_reload_flags |= 1 << 1;
|
||||||
@@ -160,6 +164,9 @@ impl Apu {
|
|||||||
self.clock_frame_counter();
|
self.clock_frame_counter();
|
||||||
}
|
}
|
||||||
self.clock_dmc();
|
self.clock_dmc();
|
||||||
|
self.clock_pulse_timers();
|
||||||
|
self.clock_triangle_timer();
|
||||||
|
self.clock_noise_timer();
|
||||||
self.cpu_cycle_parity = !self.cpu_cycle_parity;
|
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;
|
let hi = (self.io[timer_lo_idx + 1] as u16 & 0x07) << 8;
|
||||||
hi | lo
|
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) {
|
pub(crate) fn set_pulse_timer_period(&mut self, channel: usize, period: u16) {
|
||||||
let (timer_lo_idx, timer_hi_idx) = if channel == 0 {
|
let (timer_lo_idx, timer_hi_idx) = if channel == 0 {
|
||||||
(0x02, 0x03)
|
(0x02, 0x03)
|
||||||
|
|||||||
Reference in New Issue
Block a user