From 8f3ab6e751fcc6f380e46e625ce6351dce8da071 Mon Sep 17 00:00:00 2001 From: Theron Spiegl Date: Tue, 31 Dec 2019 17:22:44 -0600 Subject: [PATCH] troubleshooting, experimenting --- src/apu/mod.rs | 2 -- src/apu/square.rs | 12 ++++++++++-- src/main.rs | 8 +++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/apu/mod.rs b/src/apu/mod.rs index c2ab8e2..c87eab9 100644 --- a/src/apu/mod.rs +++ b/src/apu/mod.rs @@ -60,8 +60,6 @@ impl Apu { pub fn new() -> Self { let square_table = (0..31).map(|x| 95.52/((8128.0 / x as f32) + 100.0)).collect(); let tnd_table = (0..203).map(|x| 163.67/((24329.0 / x as f32) + 100.0)).collect(); - println!("square_table: {:?}", square_table); - println!("tnd_table: {:?}", tnd_table); Apu { square1: Square::new(false), square2: Square::new(true), diff --git a/src/apu/square.rs b/src/apu/square.rs index 0b359cd..21569fe 100644 --- a/src/apu/square.rs +++ b/src/apu/square.rs @@ -80,7 +80,7 @@ impl Square { // Update volume for this channel // The mixer receives the current envelope volume except when self.sample = if self.duty_cycle[self.duty_counter] == 0 // the sequencer output is zero, or - || self.target_period > 0x7FF // overflow from the sweep unit's adder is silencing the channel, + || self.timer_period > 0x7FF // overflow from the sweep unit's adder is silencing the channel, || self.length_counter == 0 // the length counter is zero, or || self.timer_period < 8 { // the timer has a value less than eight. 0 @@ -126,9 +126,10 @@ impl Square { } pub fn clock_sweep(&mut self) { + self.calculate_target_period(); // When the frame counter sends a half-frame clock (at 120 or 96 Hz), two things happen. // If the divider's counter is zero, the sweep is enabled, and the sweep unit is not muting the channel: The pulse's period is adjusted. - if self.sweep_counter == 0 && self.sweep_enabled && !(self.timer_period < 8 || self.target_period > 0x7FF) { + if self.sweep_counter == 0 && self.sweep_enabled && !(self.timer_period < 8 || self.timer_period > 0x7FF) { self.timer_period = self.target_period; println!("timer period adjusted to {}", self.timer_period); } @@ -139,7 +140,10 @@ impl Square { } else { self.sweep_counter -= 1; } + } + // Whenever the current period changes for any reason, whether by $400x writes or by sweep, the target period also changes. + pub fn calculate_target_period(&mut self) { // The sweep unit continuously calculates each channel's target period in this way: // A barrel shifter shifts the channel's 11-bit raw timer period right by the shift count, producing the change amount. let change = self.timer_period >> self.shift_count; @@ -185,17 +189,21 @@ impl Square { pub fn write_timer_low(&mut self, value: u8) { self.timer_period &= 0b00000111_00000000; // mask off everything but high 3 bits of 11-bit timer self.timer_period |= value as u16; // apply low 8 bits + self.calculate_target_period(); } // $4003/$4007 pub fn write_timer_high(&mut self, value: u8) { // LLLL.Lttt Pulse channel 1 length counter load and timer (write) + // TODO: thought the below meant that the length counter was only set if the channel was enabled, but apparently not as not having it fixes start game noise in DK. + // When the enabled bit is cleared (via $4015), the length counter is forced to 0 and cannot be changed until enabled is set again (the length counter's previous value is lost). if self.enabled { self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3]; } let timer_high = value as u16 & 0b0000_0111; self.timer_period &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer self.timer_period |= timer_high << 8; // apply high timer bits in their place + self.calculate_target_period(); // The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset. self.duty_counter = 0; self.start = true; diff --git a/src/main.rs b/src/main.rs index 342f6a5..fcaa57d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,7 +69,7 @@ fn main() -> Result<(), String> { match cpu.apu.clock() { Some(sample) => { sps += 1; - audio_device.queue(&vec![sample]); + if sps < 44100 {audio_device.queue(&vec![sample]);} }, None => (), }; @@ -126,8 +126,14 @@ TODO: - remaining APU channels - common mappers - battery-backed RAM solution +- fix mysterious Mario pipe non-locations - GUI? drag and drop ROMs? - reset function - save/load/pause functionality + +Timing notes: +The PPU is throttled to 60Hz by sleeping in the main loop. This locks the CPU to roughly its intended speed, 1.789773MHz NTSC. The APU runs at half that. +The SDL audio device samples/outputs at 44,100Hz, so as long as the APU queues up 44,100 samples per second, it works. + */