nesfuzz/src/apu/mod.rs

244 lines
8.4 KiB
Rust
Raw Normal View History

2019-11-27 01:11:51 +00:00
mod noise;
mod square;
mod triangle;
mod dmc;
2019-12-17 04:06:00 +00:00
use noise::Noise;
use square::Square;
use triangle::Triangle;
use dmc::DMC;
2019-12-10 06:34:21 +00:00
// APU clock ticks every other CPU cycle.
// Frame counter only ticks every 3728.5 APU ticks, and in audio frames of 4 or 5.
// Length counter controls note durations.
2019-12-12 06:17:07 +00:00
// How to sync clock to audio?
// Measure time slept to see if it will be a problem.
// What if the APU kept a ring buffer of audio data way longer than the audio device's sample size,
// and that was in a struct with some markers, so the audio device can just consume what it needs during PPU's sleep and mark
// where it left off? But wouldn't it catch up and consume buffer? It won't catch up if it's big enough, and the APU can
// change the markers somehow as it needs to? Or audio callback truncates what it consumed and adjusts head? No, audio device doesn't
// need all samples, it needs one from the stream 44100 time per second. So just an if statement, if time has passed grab a sample.
// But then that won't be running during PPU 60Hz sleep... So run audio in its own thread... Except then it won't work on Windows because of SDL...
// So just run the console in its own thread and video/audio in the main thread... But that's annoying.
2019-12-17 05:43:10 +00:00
// 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.
2019-12-12 06:17:07 +00:00
2019-11-27 01:11:51 +00:00
pub struct Apu {
square1: Square,
square2: Square,
triangle: Triangle,
noise: Noise,
dmc: DMC,
2019-11-27 04:23:18 +00:00
square_table: Vec<f32>,
tnd_table: Vec<f32>,
2019-12-10 03:22:53 +00:00
frame_counter: u8,
2019-12-18 03:29:00 +00:00
current_frame: u8,
2019-12-10 03:22:53 +00:00
mode: u8,
interrupt_inhibit: u8,
2019-12-15 00:15:06 +00:00
frame_interrupt: bool,
2019-11-27 01:11:51 +00:00
}
2019-12-07 23:09:28 +00:00
struct Envelope {
start_flag: bool,
divider: usize,
delay_level_counter: usize,
}
2019-12-10 03:22:53 +00:00
struct FrameCounter {
}
2019-11-27 01:11:51 +00:00
impl Apu {
2019-12-10 03:22:53 +00:00
pub fn new() -> Self {
2019-11-27 04:23:18 +00:00
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();
2019-11-27 01:11:51 +00:00
Apu {
square1: Square::new(),
square2: Square::new(),
triangle: Triangle::new(),
noise: Noise::new(),
dmc: DMC::new(),
2019-11-27 04:23:18 +00:00
square_table: square_table,
tnd_table: tnd_table,
2019-12-10 03:22:53 +00:00
frame_counter: 0,
2019-12-18 03:29:00 +00:00
current_frame: 0,
2019-12-10 03:22:53 +00:00
mode: 0,
interrupt_inhibit: 0,
2019-12-15 00:15:06 +00:00
frame_interrupt: false,
2019-11-27 01:11:51 +00:00
}
}
2019-12-15 00:15:06 +00:00
pub fn step(&mut self) {
2019-12-10 06:34:21 +00:00
}
2019-12-15 00:15:06 +00:00
pub fn write_reg(&mut self, address: usize, value: u8) {
2019-11-27 01:11:51 +00:00
match address {
0x4000 => self.square1.duty(value),
0x4001 => self.square1.sweep(value),
0x4002 => self.square1.timer_low(value),
0x4003 => self.square1.timer_high(value),
0x4004 => self.square2.duty(value),
0x4005 => self.square2.sweep(value),
0x4006 => self.square2.timer_low(value),
0x4007 => self.square2.timer_high(value),
0x4008 => self.triangle.counter(value),
2019-12-17 05:43:10 +00:00
0x4009 => (),
2019-11-27 01:11:51 +00:00
0x400A => self.triangle.timer_low(value),
0x400B => self.triangle.timer_high(value),
0x400C => self.noise.envelope(value),
0x400D => (),
0x400E => self.noise.loop_noise(value),
0x400F => self.noise.load_length_counter(value),
0x4010 => self.dmc.control(value),
0x4011 => self.dmc.direct_load(value),
0x4012 => self.dmc.sample_address(value),
0x4013 => self.dmc.sample_length(value),
0x4014 => (),
0x4015 => self.control(value),
0x4016 => (),
2019-12-18 03:29:00 +00:00
0x4017 => self.set_frame_counter(value),
2019-12-05 02:57:05 +00:00
_ => panic!("bad address written: 0x{:X}", address),
2019-11-27 01:11:51 +00:00
}
}
2019-11-27 04:23:18 +00:00
fn mix(&self) -> f32 {
2019-12-05 02:57:05 +00:00
let square_out = self.square_table[(self.square1.sample + self.square2.sample) as usize];
let tnd_out = self.tnd_table[((3*self.triangle.sample)+(2*self.noise.sample) + self.dmc.sample) as usize];
2019-11-27 04:23:18 +00:00
square_out + tnd_out
}
2019-12-10 06:34:21 +00:00
// mode 0: mode 1: function
// --------- ----------- -----------------------------
// - - - f - - - - - IRQ (if bit 6 is clear)
// - l - l - l - - l Length counter and sweep
// e e e e e e e - e Envelope and linear counter
2019-12-18 03:29:00 +00:00
fn set_frame_counter(&mut self, value: u8) {
2019-12-10 03:22:53 +00:00
// 0 selects 4-step sequence, 1 selects 5-step sequence
if value & (1<<7) == 0 {
self.mode = 0;
self.frame_counter = 4;
} else {
self.mode = 1;
self.frame_counter = 5;
}
// If set, the frame interrupt flag is cleared, otherwise it is unaffected.
if value & (1<<6) != 0 {
self.interrupt_inhibit = 0;
}
2019-11-27 04:23:18 +00:00
}
2019-12-05 02:57:05 +00:00
2019-12-18 03:29:00 +00:00
fn step_frame_counter(&mut self) {
match self.frame_counter {
4 => {
self.square1.clock_envelope();
self.square2.clock_envelope();
self.triangle.clock_linear_counter();
self.noise.clock_envelope();
if self.current_frame == 1 || self.current_frame == 3 {
}
if self.current_frame == 3 {
self.issue_irq();
}
},
5 => {
},
_ => panic!("invalid frame counter value"),
}
}
2019-12-05 02:57:05 +00:00
fn control(&mut self, value: u8) {
2019-12-15 00:15:06 +00:00
// Writing to this register clears the DMC interrupt flag.
self.dmc.interrupt = false;
// Writing a zero to any of the channel enable bits will silence that channel and immediately set its length counter to 0.
if value & (1<<0) != 0 {
self.square1.enabled = true;
} else {
self.square1.enabled = false;
self.square1.length_counter = 0;
}
if value & (1<<1) != 0 {
self.square2.enabled = true;
} else {
self.square2.enabled = false;
self.square2.length_counter = 0;
}
if value & (1<<2) != 0 {
self.triangle.enabled = true;
} else {
self.triangle.enabled = false;
self.triangle.length_counter = 0;
}
if value & (1<<3) != 0 {
self.noise.enabled = true;
} else {
self.noise.enabled = false;
self.noise.length_counter = 0;
}
if value & (1<<4) != 0 {
self.dmc.enabled = true;
// If the DMC bit is set, the DMC sample will be restarted only if its bytes remaining is 0.
// If there are bits remaining in the 1-byte sample buffer, these will finish playing before the next sample is fetched.
if self.dmc.bytes_remaining != 0 {
// TODO: how does dmc repeat?
}
} else {
self.dmc.enabled = false;
self.dmc.length_counter = 0;
// If the DMC bit is clear, the DMC bytes remaining will be set to 0 and the DMC will silence when it empties.
self.dmc.bytes_remaining = 0;
}
}
pub fn read_status(&mut self) -> u8 {
// IF-D NT21: DMC interrupt (I), frame interrupt (F), DMC active (D), length counter > 0 (N/T/2/1)
let mut val = 0;
// N/T/2/1 will read as 1 if the corresponding length counter is greater than 0. For the triangle channel, the status of the linear counter is irrelevant.
if self.square1.length_counter != 0 {
val |= 1<<0;
}
if self.square2.length_counter != 0 {
val |= 1<<1;
}
if self.triangle.length_counter != 0 {
val |= 1<<2;
}
if self.noise.length_counter != 0 {
val |= 1<<3;
}
// D will read as 1 if the DMC bytes remaining is more than 0.
if self.dmc.bytes_remaining != 0 {
val |= 1<<4;
}
if self.frame_interrupt {
val |= 1<<6;
}
if self.dmc.interrupt {
val |= 1<<7;
}
2019-12-05 02:57:05 +00:00
2019-12-15 00:15:06 +00:00
// Reading this register clears the frame interrupt flag (but not the DMC interrupt flag).
self.frame_interrupt = false;
// TODO: If an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
val
2019-12-05 02:57:05 +00:00
}
2019-12-18 03:29:00 +00:00
fn issue_irq(&mut self) {
}
2019-11-27 01:11:51 +00:00
}