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
|
// TODO: organize APU structs
|
||||||
|
|
||||||
const FRAME_COUNTER_STEPS: [usize; 5] = [3728, 7456, 11185, 14914, 18640];
|
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] = [
|
const LENGTH_COUNTER_TABLE: [u8; 32] = [
|
||||||
10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14,
|
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,
|
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) {
|
pub fn write_reg(&mut self, address: usize, value: u8) {
|
||||||
|
// println!("writing 0b{:08b} to 0x{:X}", value, address);
|
||||||
match address {
|
match address {
|
||||||
0x4000 => self.square1.write_duty(value),
|
0x4000 => self.square1.write_duty(value),
|
||||||
0x4001 => self.square1.write_sweep(value),
|
0x4001 => self.square1.write_sweep(value),
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl Square {
|
||||||
}
|
}
|
||||||
// Update volume for this channel
|
// Update volume for this channel
|
||||||
// The mixer receives the current envelope volume except when
|
// 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.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.length_counter == 0 // the length counter is zero, or
|
||||||
|| self.timer < 8 { // the timer has a value less than eight.
|
|| self.timer < 8 { // the timer has a value less than eight.
|
||||||
|
@ -83,7 +83,6 @@ impl Square {
|
||||||
} else {
|
} else {
|
||||||
self.decay_counter
|
self.decay_counter
|
||||||
};
|
};
|
||||||
println!("{}", self.sample);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clock_envelope(&mut self) {
|
pub fn clock_envelope(&mut self) {
|
||||||
|
@ -101,7 +100,7 @@ impl Square {
|
||||||
pub fn clock_length_counter(&mut self) {
|
pub fn clock_length_counter(&mut self) {
|
||||||
if !(self.length_counter == 0 || self.length_counter_halt) {
|
if !(self.length_counter == 0 || self.length_counter_halt) {
|
||||||
self.length_counter -= 1;
|
self.length_counter -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_envelope_divider(&mut self) {
|
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 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) {
|
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 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 {
|
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
|
// $4000/$4004
|
||||||
pub fn write_duty(&mut self, value: u8) {
|
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.duty_cycle = DUTY_CYCLE_SEQUENCES[(value >> 6) as usize];
|
||||||
self.length_counter_halt = value & (1<<5) != 0;
|
self.length_counter_halt = value & (1<<5) != 0;
|
||||||
self.constant_volume_flag = value & (1<<4) != 0;
|
self.constant_volume_flag = value & (1<<4) != 0;
|
||||||
|
@ -175,8 +170,8 @@ impl Square {
|
||||||
|
|
||||||
// $4002/$4006
|
// $4002/$4006
|
||||||
pub fn write_timer_low(&mut self, value: u8) {
|
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_period &= 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 |= value as u16; // apply low 8 bits
|
||||||
}
|
}
|
||||||
|
|
||||||
// $4003/$4007
|
// $4003/$4007
|
||||||
|
@ -186,8 +181,8 @@ impl Square {
|
||||||
self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3];
|
self.length_counter = super::LENGTH_COUNTER_TABLE[value as usize >> 3];
|
||||||
}
|
}
|
||||||
let timer_high = value as u16 & 0b0000_0111;
|
let timer_high = value as u16 & 0b0000_0111;
|
||||||
self.timer &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer
|
self.timer_period &= 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 |= 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.
|
// 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.duty_counter = 0;
|
||||||
self.start = true;
|
self.start = true;
|
||||||
|
|
24
src/audio.rs
24
src/audio.rs
|
@ -2,28 +2,6 @@ extern crate sdl2;
|
||||||
|
|
||||||
use sdl2::audio::{AudioCallback, AudioSpecDesired};
|
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> {
|
pub fn initialize(context: &sdl2::Sdl) -> Result<sdl2::audio::AudioQueue<f32>, String> {
|
||||||
let audio_subsystem = context.audio()?;
|
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)
|
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
|
// Set up audio
|
||||||
let mut audio_device = audio::initialize(&sdl_context).expect("Could not create audio device");
|
let mut audio_device = audio::initialize(&sdl_context).expect("Could not create audio device");
|
||||||
let mut half_cycle = false;
|
let mut half_cycle = false;
|
||||||
let mut audio_buffer = Vec::<f32>::new();
|
|
||||||
audio_device.resume();
|
audio_device.resume();
|
||||||
|
|
||||||
// Initialize hardware components
|
// Initialize hardware components
|
||||||
|
@ -70,19 +69,11 @@ fn main() -> Result<(), String> {
|
||||||
match cpu.apu.clock() {
|
match cpu.apu.clock() {
|
||||||
Some(sample) => {
|
Some(sample) => {
|
||||||
sps += 1;
|
sps += 1;
|
||||||
// if sample != 0.0 {
|
audio_device.queue(&vec![sample]);
|
||||||
// println!("sample {}", sample);
|
|
||||||
// }
|
|
||||||
audio_buffer.push(sample)
|
|
||||||
},
|
},
|
||||||
None => (),
|
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
|
// clock PPU three times for every CPU cycle
|
||||||
for _ in 0..cpu_cycles * 3 {
|
for _ in 0..cpu_cycles * 3 {
|
||||||
let (pixel, end_of_frame) = cpu.ppu.clock();
|
let (pixel, end_of_frame) = cpu.ppu.clock();
|
||||||
|
@ -130,5 +121,13 @@ fn main() -> Result<(), String> {
|
||||||
Ok(())
|
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