fixed timer_period bug, cleaned up. square channels kind of working. need to figure out how to sample properly.
This commit is contained in:
parent
bf2317dc28
commit
686d45f8d3
|
@ -30,7 +30,8 @@ use dmc::DMC;
|
|||
// TODO: organize APU structs
|
||||
|
||||
const FRAME_COUNTER_STEPS: [usize; 5] = [3728, 7456, 11185, 14914, 18640];
|
||||
const CYCLES_PER_SAMPLE: f32 = 894_886.5/44_100.0; // APU frequency over sample frequency. May need to turn this down slightly as it's outputting less than 44_100Hz.
|
||||
// const CYCLES_PER_SAMPLE: f32 = 894_886.5/44_100.0; // APU frequency over sample frequency. May need to turn this down slightly as it's outputting less than 44_100Hz.
|
||||
const CYCLES_PER_SAMPLE: f32 = 19.65;
|
||||
const LENGTH_COUNTER_TABLE: [u8; 32] = [
|
||||
10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14,
|
||||
12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30,
|
||||
|
@ -111,6 +112,7 @@ impl Apu {
|
|||
}
|
||||
|
||||
pub fn write_reg(&mut self, address: usize, value: u8) {
|
||||
// println!("writing 0b{:08b} to 0x{:X}", value, address);
|
||||
match address {
|
||||
0x4000 => self.square1.write_duty(value),
|
||||
0x4001 => self.square1.write_sweep(value),
|
||||
|
|
|
@ -75,7 +75,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.sample = if self.duty_cycle[self.duty_counter] == 0 // the sequencer output is zero, or
|
||||
|| self.sweep_divider > 0x7FF // overflow from the sweep unit's adder is silencing the channel,
|
||||
|| self.length_counter == 0 // the length counter is zero, or
|
||||
|| self.timer < 8 { // the timer has a value less than eight.
|
||||
|
@ -83,7 +83,6 @@ impl Square {
|
|||
} else {
|
||||
self.decay_counter
|
||||
};
|
||||
println!("{}", self.sample);
|
||||
}
|
||||
|
||||
pub fn clock_envelope(&mut self) {
|
||||
|
@ -101,7 +100,7 @@ impl Square {
|
|||
pub fn clock_length_counter(&mut self) {
|
||||
if !(self.length_counter == 0 || self.length_counter_halt) {
|
||||
self.length_counter -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clock_envelope_divider(&mut self) {
|
||||
|
@ -136,7 +135,7 @@ impl Square {
|
|||
}
|
||||
// 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 < 8 || self.sweep_divider > 0x7FF) {
|
||||
self.adjust_sweep();
|
||||
self.timer_period = self.sweep_divider;
|
||||
}
|
||||
// If the divider's counter is zero or the reload flag is true: The counter is set to P and the reload flag is cleared. Otherwise, the counter is decremented.
|
||||
if self.sweep_counter == 0 || self.sweep_reload {
|
||||
|
@ -147,13 +146,9 @@ impl Square {
|
|||
}
|
||||
}
|
||||
|
||||
fn adjust_sweep(&mut self) {
|
||||
self.timer_period = self.sweep_divider;
|
||||
}
|
||||
|
||||
// $4000/$4004
|
||||
pub fn write_duty(&mut self, value: u8) {
|
||||
// TODO: The duty cycle is changed (see table below), but the sequencer's current position isn't affected.
|
||||
// The duty cycle is changed (see table below), but the sequencer's current position isn't affected.
|
||||
self.duty_cycle = DUTY_CYCLE_SEQUENCES[(value >> 6) as usize];
|
||||
self.length_counter_halt = value & (1<<5) != 0;
|
||||
self.constant_volume_flag = value & (1<<4) != 0;
|
||||
|
@ -175,8 +170,8 @@ impl Square {
|
|||
|
||||
// $4002/$4006
|
||||
pub fn write_timer_low(&mut self, value: u8) {
|
||||
self.timer &= 0b00000111_00000000; // mask off everything but high 3 bits of 11-bit timer
|
||||
self.timer |= value as u16; // apply low 8 bits
|
||||
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
|
||||
}
|
||||
|
||||
// $4003/$4007
|
||||
|
@ -186,8 +181,8 @@ impl Square {
|
|||
self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3];
|
||||
}
|
||||
let timer_high = value as u16 & 0b0000_0111;
|
||||
self.timer &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer
|
||||
self.timer |= timer_high << 8; // apply high timer bits in their place
|
||||
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
|
||||
// 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;
|
||||
|
|
24
src/audio.rs
24
src/audio.rs
|
@ -2,28 +2,6 @@ extern crate sdl2;
|
|||
|
||||
use sdl2::audio::{AudioCallback, AudioSpecDesired};
|
||||
|
||||
// pub struct Speaker {
|
||||
// buffer: [f32; 4096*4],
|
||||
// head: usize,
|
||||
// }
|
||||
|
||||
// impl AudioCallback for Speaker {
|
||||
// type Channel = f32;
|
||||
// fn callback(&mut self, out: &mut [f32]) {
|
||||
// for (i, x) in out.iter_mut().enumerate() {
|
||||
// *x = self.buffer[i+self.head]; // get data from apu
|
||||
// }
|
||||
// self.head = (self.head + 4096) % (4096*4)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Speaker {
|
||||
// pub fn append(&mut self, sample: f32) {
|
||||
// self.buffer[self.head] = sample;
|
||||
// self.head = (self.head + 1) % (4096*4);
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn initialize(context: &sdl2::Sdl) -> Result<sdl2::audio::AudioQueue<f32>, String> {
|
||||
let audio_subsystem = context.audio()?;
|
||||
|
||||
|
@ -35,5 +13,3 @@ pub fn initialize(context: &sdl2::Sdl) -> Result<sdl2::audio::AudioQueue<f32>, S
|
|||
|
||||
audio_subsystem.open_queue(None, &desired_spec)
|
||||
}
|
||||
|
||||
// problem is: how to get data into callback from outside? can't change its signature. can't sneak it in through struct as struct is consumed by the audio device
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -37,7 +37,6 @@ fn main() -> Result<(), String> {
|
|||
// Set up audio
|
||||
let mut audio_device = audio::initialize(&sdl_context).expect("Could not create audio device");
|
||||
let mut half_cycle = false;
|
||||
let mut audio_buffer = Vec::<f32>::new();
|
||||
audio_device.resume();
|
||||
|
||||
// Initialize hardware components
|
||||
|
@ -70,19 +69,11 @@ fn main() -> Result<(), String> {
|
|||
match cpu.apu.clock() {
|
||||
Some(sample) => {
|
||||
sps += 1;
|
||||
// if sample != 0.0 {
|
||||
// println!("sample {}", sample);
|
||||
// }
|
||||
audio_buffer.push(sample)
|
||||
audio_device.queue(&vec![sample]);
|
||||
},
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
if audio_buffer.len() == 44_100 {
|
||||
// println!("queueing: {:?}", &audio_buffer[..32]);
|
||||
audio_device.queue(&audio_buffer);
|
||||
audio_buffer = vec![];
|
||||
}
|
||||
// clock PPU three times for every CPU cycle
|
||||
for _ in 0..cpu_cycles * 3 {
|
||||
let (pixel, end_of_frame) = cpu.ppu.clock();
|
||||
|
@ -130,5 +121,13 @@ fn main() -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: reset function?
|
||||
// TODO: save/load functionality
|
||||
/*
|
||||
TODO:
|
||||
- remaining APU channels
|
||||
- common mappers
|
||||
- battery-backed RAM solution
|
||||
- GUI? drag and drop ROMs?
|
||||
- reset function
|
||||
- save/load/pause functionality
|
||||
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue