This commit is contained in:
Theron 2019-12-18 23:35:04 -06:00
parent 6fb17d77c6
commit 359459f5ad
4 changed files with 60 additions and 21 deletions

View File

@ -24,6 +24,9 @@ use dmc::DMC;
// No. Don't have to be concerned about the audio device, that's solved by the buffer, and the 44100 samples get fed in batches of 4096 from the large buffer, // No. Don't have to be concerned about the audio device, that's solved by the buffer, and the 44100 samples get fed in batches of 4096 from the large buffer,
// when the device needs them, which is accomplished just by calling .resume() before the main loop starts. So a large buffer really should allow for the 60Hz sleep lock. // when the device needs them, which is accomplished just by calling .resume() before the main loop starts. So a large buffer really should allow for the 60Hz sleep lock.
// We need to take a sample 44100 times per second. The CPU clocks (not steps) at 1.789773 MHz. Meaning the APU, going half as fast,
// clocks 894,866.5 times per second. 894,866.5/44,100=20.29 APU clocks per audio sample.
pub struct Apu { pub struct Apu {
square1: Square, square1: Square,
square2: Square, square2: Square,
@ -39,6 +42,8 @@ pub struct Apu {
mode: u8, mode: u8,
interrupt_inhibit: u8, interrupt_inhibit: u8,
frame_interrupt: bool, frame_interrupt: bool,
cycle: usize,
remainder: f64, // keep sample at 44100Hz
} }
struct Envelope { struct Envelope {
@ -47,9 +52,7 @@ struct Envelope {
delay_level_counter: usize, delay_level_counter: usize,
} }
struct FrameCounter { const FRAME_COUNTER_STEPS: [usize; 5] = [3728, 7456, 11185, 14914, 18640];
}
impl Apu { impl Apu {
pub fn new() -> Self { pub fn new() -> Self {
@ -70,11 +73,29 @@ impl Apu {
mode: 0, mode: 0,
interrupt_inhibit: 0, interrupt_inhibit: 0,
frame_interrupt: false, frame_interrupt: false,
cycle: 0,
remainder: 0,
} }
} }
pub fn step(&mut self) { pub fn step(&mut self) {
if (self.frame_counter == 4 && FRAME_COUNTER_STEPS[..4].contains(&self.cycle))
|| (self.frame_counter == 5 && FRAME_COUNTER_STEPS.contains(&self.cycle)) {
self.clock_frame_counter();
}
// push sample to buffer
if self.remainder > 894_866.5/44_100 { // APU frequency over sample frequency
self.sample_audio();
self.remainder -= 894_866.5/44_100;
}
self.remainder += 1;
self.cycle += 1;
if (self.frame_counter == 4 && self.cycle == 14915)
|| (self.frame_counter == 5 && self.cycle == 18641) {
self.cycle = 0;
}
} }
pub fn write_reg(&mut self, address: usize, value: u8) { pub fn write_reg(&mut self, address: usize, value: u8) {
@ -134,24 +155,30 @@ impl Apu {
} }
fn step_frame_counter(&mut self) { fn clock_frame_counter(&mut self) {
match self.frame_counter { if !(self.frame_counter == 5 && self.current_frame == 4) {
4 => { // step envelopes
self.square1.clock_envelope(); self.square1.clock_envelope();
self.square2.clock_envelope(); self.square2.clock_envelope();
self.triangle.clock_linear_counter(); self.triangle.clock_linear_counter();
self.noise.clock_envelope(); self.noise.clock_envelope();
if self.current_frame == 1 || self.current_frame == 3 { }
if (self.current_frame == 1)
} || (self.frame_counter == 4 && self.current_frame == 3)
if self.current_frame == 3 { || (self.frame_counter == 5 && self.current_frame == 4) {
self.issue_irq(); // step length counters and sweep units
} self.square1.clock_length_counter();
}, self.square2.clock_length_counter();
5 => { self.triangle.clock_length_counter();
self.noise.clock_length_counter();
}, }
_ => panic!("invalid frame counter value"), if self.frame_counter == 4 && self.current_frame == 3 {
self.issue_irq();
}
// advance counter
self.current_frame += 1;
if self.current_frame == self.frame_counter {
self.current_frame = 0;
} }
} }

View File

@ -43,6 +43,10 @@ impl Noise {
} }
pub fn clock_length_counter(&mut self) {
}
pub fn loop_noise(&mut self, value: u8) { pub fn loop_noise(&mut self, value: u8) {
} }

View File

@ -59,6 +59,10 @@ impl Square {
self.divider = self.envelope; // and the divider's period is immediately reloaded self.divider = self.envelope; // and the divider's period is immediately reloaded
} }
} }
pub fn clock_length_counter(&mut self) {
}
fn clock_divider(&mut self) { fn clock_divider(&mut self) {
// When the divider is clocked while at 0, it is loaded with V and clocks the decay level counter. // When the divider is clocked while at 0, it is loaded with V and clocks the decay level counter.

View File

@ -35,4 +35,8 @@ impl Triangle {
pub fn clock_linear_counter(&mut self) { pub fn clock_linear_counter(&mut self) {
} }
pub fn clock_length_counter(&mut self) {
}
} }