macro_rules! lut { ($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => { const $name: [$ty; $len] = { let mut table = [0; $len]; let mut $n = 0; while $n < $len { table[$n] = $expr; $n += 1; } table }; }; } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq)] pub struct LengthTimerHigh(u8); impl Debug; length, set_length: 7, 3; u16; timer_high, set_timer_high: 2, 0; } #[derive(Debug, Clone)] struct LengthCounter { current: u16, silenced: bool, } impl LengthCounter { pub fn new() -> Self { Self { current: 0, silenced: true, } } pub fn clock(&mut self) { if self.current == 0 { self.silenced = true; } else { self.current -= 1; } } pub fn write(&mut self, length: u8) { // self.reg.0 = val; self.silenced = false; const LENGTH_LUT: [u16; 0x20] = [ 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, 3, ]; self.current = LENGTH_LUT[length as usize] + 1; // I think? } pub fn silenced(&self) -> bool { self.silenced } } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq)] pub struct DutyVol(u8); impl Debug; duty, set_duty: 7, 6; length_counter_halt, set_length_counter_halt: 5; const_vol, set_const_vol: 4; volume, set_volume: 3, 0; } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq)] pub struct Sweep(u8); impl Debug; enable, set_enable: 7; period, set_period: 6, 4; negate, set_negate: 3; shift, set_shift: 2, 0; } #[derive(Debug, Clone)] struct PulseChannel { enabled: bool, duty_vol: DutyVol, sweep: Sweep, sweep_reload: bool, sweep_counter: u8, counter: LengthCounter, period: u16, period_timer: u16, cur: u8, sample: u8, envelope_start: bool, envelope_counter: u8, envelope_divider: u8, extra: u16, } impl PulseChannel { pub fn new(extra: u16) -> Self { Self { enabled: false, duty_vol: DutyVol(0), sweep: Sweep(0), sweep_reload: false, sweep_counter: 0, counter: LengthCounter::new(), period: 0, period_timer: 0, cur: 0, sample: 0, envelope_start: false, envelope_counter: 0, envelope_divider: 0, extra, } } pub fn write(&mut self, offset: u16, val: u8) { match offset { 0x00 => { self.duty_vol.0 = val; self.envelope_start = true; } 0x01 => { self.sweep.0 = val; self.sweep_reload = true; } 0x02 => self.period = self.period & 0x700 | (val as u16), 0x03 => { let reg = LengthTimerHigh(val); self.counter.write(reg.length()); self.period = (self.period & 0xFF) | (reg.timer_high() << 8); self.period_timer = self.period; self.cur = 0; } _ => unreachable!(), } } fn volume(&self) -> u8 { if self.duty_vol.const_vol() { self.duty_vol.volume() } else { self.envelope_counter } } pub fn clock(&mut self) { if !self.enabled { self.sample = 0; return; } if self.period_timer == 0 { self.period_timer = self.period; self.cur = (self.cur + 1) % 8; const DUTY: [[bool; 8]; 4] = [ [false, true, false, false, false, false, false, false], [false, true, true, false, false, false, false, false], [false, true, true, true, true, false, false, false], [true, false, false, true, true, true, true, true], ]; self.sample = if DUTY[self.duty_vol.duty() as usize][self.cur as usize] { self.volume() } else { 0x00 }; } else { self.period_timer -= 1; } // TODO } pub fn cur_sample(&self) -> u8 { if self.enabled && !self.counter.silenced() && !self.sweep_silenced() { self.sample } else { 0 } } pub fn q_frame_clock(&mut self) { if !self.enabled { return; } if !self.duty_vol.length_counter_halt() { self.counter.clock(); } else { self.counter.silenced = false; } if !self.duty_vol.const_vol() { if self.envelope_start || self.envelope_counter == 0 { self.envelope_counter = 0xF; self.envelope_divider = self.duty_vol.volume(); self.envelope_start = false; } else if self.envelope_divider == 0 { self.envelope_divider = self.duty_vol.volume(); self.envelope_counter -= 1; } else { self.envelope_divider -= 1; } } } fn target_period(&self) -> u16 { let amt = self.period >> self.sweep.shift(); if self.sweep.negate() { self.period.saturating_sub(amt + self.extra) } else { self.period + amt } } fn sweep_silenced(&self) -> bool { self.period < 8 || self.target_period() > 0x7FF } pub fn h_frame_clock(&mut self) { self.q_frame_clock(); if self.sweep.enable() { if self.sweep_reload { self.sweep_counter = self.sweep.period(); self.sweep_reload = false; } else if self.sweep_counter == 0 { self.sweep_counter = self.sweep.period(); if self.period < 8 || self.target_period() < 0x7FF { self.period = self.target_period(); } } else { self.sweep_counter -= 1; } } } pub fn view(&self) -> String { format!( "Square Channel Evelope Volume: {0:>4} ${0:02X} Constant Volume: {1} Length Counter - Halted: {2} Duty: {3:>4} ${3:02X} Sweep - Shift: {4:>4} ${4:02X} Sweep - Negate: {5} Sweep - Period: {6:>4} ${6:02X} Sweep - Enabled: {7} Period: {8:>4} ${8:04X} Length Counter - Reload Value: {9:>4} ${9:04X} Enabled: {10} Timer: {11:>4} ${11:04X} Duty Position: {12:>4} ${12:02X} Length Counter - Counter: {13:>4} ${13:02X} Envelope - Counter: {14:>4} ${14:02X} Envelope - Divider: {15:>4} ${15:02X} Output: {16:>4} ${16:02X} ", self.duty_vol.volume(), self.duty_vol.const_vol(), self.duty_vol.length_counter_halt(), self.duty_vol.duty(), self.sweep.shift(), self.sweep.negate(), self.sweep.period(), self.sweep.enable(), self.period, self.counter.current, self.enabled, self.period_timer, self.cur, // ? self.counter.current, self.envelope_counter, self.envelope_divider, self.cur_sample(), ) } } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct LengthCounterReg(u8); impl Debug; halt, set_halt: 7; value, set_value: 6, 0; } #[derive(Debug, Clone)] struct TriangleChannel { enabled: bool, length: LengthCounterReg, reload: bool, counter: LengthCounter, length_counter: u16, cur: u8, period: u16, period_timer: u16, sample: u8, } impl TriangleChannel { pub fn new() -> Self { Self { length: LengthCounterReg(0), counter: LengthCounter::new(), reload: false, enabled: false, sample: 0, period: 0, period_timer: 0, cur: 0, length_counter: 0, } } pub fn write(&mut self, offset: u16, val: u8) { match offset { 0x00 => { self.length.0 = val; } 0x01 => (), 0x02 => self.period = self.period & 0x700 | (val as u16), 0x03 => { // self.length_load.0 = val; // self.reload = true; let reg = LengthTimerHigh(val); self.counter.write(reg.length()); self.period = (self.period & 0xFF) | (reg.timer_high() << 8); self.period_timer = self.period; self.cur = 0; } _ => unreachable!(), } } pub fn cur_sample(&self) -> u8 { self.sample } pub fn clock(&mut self) { if self.length_counter > 0 && self.period > 0 { if self.period_timer == 0 { self.period_timer = self.period; const SAMPLES: [u8; 32] = [ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; self.cur = (self.cur + 1) % SAMPLES.len() as u8; self.sample = SAMPLES[self.cur as usize]; } else { self.period_timer -= 1; } } } pub fn q_frame_clock(&mut self) { if self.reload { self.length_counter = self.length.value() as u16; self.reload = self.length.halt(); } else if self.length_counter == 0 { } else { self.length_counter -= 1; } } pub fn h_frame_clock(&mut self) { self.q_frame_clock(); } pub fn view(&self) -> String { format!( "Triangle Channel Linear Counter - Reload: {0:>3} ${0:02X} Linear Counter - Halted: {1} Period: {2:>3} ${2:04X} Length Counter - Reload Value: {3:>3} ${3:04X} Enabled: {4} Timer: {5:>3} ${5:02X} Frequency: ??? Sequence Position: {6:>3} ${6:02X} Length Counter - Counter: {7:>3} ${7:02X} Linear Counter - Counter: {8:>3} ${8:02X} Linear Counter - Reload Flag: {9} Output: {10:>3} ${10:02X} ", self.length.value(), self.length.halt(), self.period, 0, self.enabled, self.period_timer, self.cur, 0, self.length_counter, self.reload, self.cur_sample(), ) } } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct NoiseEnvelope(u8); impl Debug; length_counter_halt, set_length_counter_halt: 5; constant_volume, set_constant_volume: 4; volume, set_volume: 3, 0; } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct ModePeriod(u8); impl Debug; mode, set_mode: 7; period, set_period: 3, 0; } bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct LengthCounterLoad(u8); impl Debug; length, set_length: 7, 3; } #[derive(Debug, Clone)] #[allow(dead_code)] struct NoiseChannel { enabled: bool, evelope: NoiseEnvelope, mode_period: ModePeriod, length_counter_load: LengthCounterLoad, shr: u16, count: u16, length_counter: u8, } impl NoiseChannel { pub fn new() -> Self { Self { enabled: false, evelope: NoiseEnvelope(0), mode_period: ModePeriod(0), length_counter_load: LengthCounterLoad(0), shr: 1, count: 0, length_counter: 0, } } pub fn write(&mut self, offset: u16, _val: u8) { match offset { 0x00 => (), 0x01 => (), 0x02 => (), 0x03 => (), _ => unreachable!(), } } pub fn cur_sample(&self) -> u8 { if self.length_counter == 0 || self.shr & 1 == 1 { 0 } else { self.evelope.volume() } } pub fn clock(&mut self) { const PERIODS: &[u16] = &[ 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4096, ]; if self.count == PERIODS[self.mode_period.period() as usize] / 2 { self.count = 0; let bit_0 = (self.shr & 0b1) << 14; let other = if self.mode_period.mode() { (self.shr & 0b100_0000) << 8 } else { (self.shr & 0b10) << 13 }; self.shr = (self.shr >> 1) | (bit_0 ^ other); } else { self.count += 1; } } pub fn q_frame_clock(&mut self) {} pub fn h_frame_clock(&mut self) { self.q_frame_clock(); } pub fn view(&self) -> String { format!("") } } #[derive(Debug, Clone)] struct DeltaChannel { enabled: bool, sample: u8, } impl DeltaChannel { pub fn new() -> Self { Self { enabled: false, sample: 0, } } pub fn write(&mut self, offset: u16, _val: u8) { match offset { 0x00 => (), 0x01 => (), 0x02 => (), 0x03 => (), _ => unreachable!(), } } pub fn cur_sample(&self) -> u8 { self.sample } pub fn clock(&mut self) {} pub fn q_frame_clock(&mut self) {} pub fn h_frame_clock(&mut self) {} pub fn int(&self) -> bool { false } pub fn view(&self) -> String { format!("") } } #[derive(Debug, Clone)] struct FrameCounter { count: usize, mode_5_step: bool, interrupt_enabled: bool, irq: bool, } #[derive(Clone)] pub struct APU { pulse_1: PulseChannel, pulse_2: PulseChannel, triangle: TriangleChannel, noise: NoiseChannel, dmc: DeltaChannel, frame_counter: FrameCounter, samples: Vec, } impl std::fmt::Debug for APU { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "APU {:#?} {:#?} {:#?} {:#?} {:#?} ", self.pulse_1, self.pulse_2, self.triangle, self.noise, self.dmc, ) } } impl APU { pub fn init() -> Self { Self { pulse_1: PulseChannel::new(1), pulse_2: PulseChannel::new(0), triangle: TriangleChannel::new(), noise: NoiseChannel::new(), dmc: DeltaChannel::new(), frame_counter: FrameCounter { mode_5_step: false, interrupt_enabled: true, count: 0, irq: false, }, samples: Vec::with_capacity(10000), } } pub fn reset(&mut self) { *self = Self::init(); } pub fn read_reg(&mut self, offset: u16) -> u8 { // println!("APU read: {offset:02X}"); match offset { 0x15 => { let val = (u8::from(self.dmc.int()) << 7) | (u8::from(self.frame_counter.irq) << 6) | (u8::from(self.dmc.enabled) << 4) | (u8::from(self.noise.enabled) << 3) | (u8::from(self.triangle.enabled) << 2) | (u8::from(self.pulse_2.enabled) << 1) | (u8::from(self.pulse_1.enabled) << 0); self.frame_counter.irq = false; val } _ => panic!("No register at {:X}", offset), } } pub fn write_reg(&mut self, offset: u16, val: u8) { // println!("APU write: {offset:02X} <= {val:02X}"); match offset { 0x00..0x04 => self.pulse_1.write(offset - 0x00, val), 0x04..0x08 => self.pulse_2.write(offset - 0x04, val), 0x08..0x0C => self.triangle.write(offset - 0x08, val), 0x0C..0x10 => self.noise.write(offset - 0x0C, val), 0x10..0x14 => self.dmc.write(offset - 0x10, val), 0x14 => (), 0x15 => { self.dmc.enabled = val & 0b0001_0000 != 0; self.noise.enabled = val & 0b0000_1000 != 0; self.triangle.enabled = val & 0b0000_0100 != 0; self.pulse_2.enabled = val & 0b0000_0010 != 0; self.pulse_1.enabled = val & 0b0000_0001 != 0; } 0x17 => { self.frame_counter.mode_5_step = val & 0b1000_0000 != 0; self.frame_counter.interrupt_enabled = val & 0b0100_0000 == 0; if !self.frame_counter.interrupt_enabled { self.frame_counter.irq = false; } } // _ => (), _ => panic!("No register at {:X}", offset), } } fn q_frame_clock(&mut self) { self.pulse_1.q_frame_clock(); self.pulse_2.q_frame_clock(); self.triangle.q_frame_clock(); self.noise.q_frame_clock(); self.dmc.q_frame_clock(); } fn h_frame_clock(&mut self) { self.pulse_1.h_frame_clock(); self.pulse_2.h_frame_clock(); self.triangle.h_frame_clock(); self.noise.h_frame_clock(); self.dmc.h_frame_clock(); } fn gen_sample(&mut self) { lut!(P_LUT: [u8; 32] = |n| (95.52 / (8128.0 / n as f64 + 100.0) * 255.0) as u8); let pulse_out = P_LUT[(self.pulse_1.cur_sample() + self.pulse_2.cur_sample()) as usize]; lut!(TND_LUT: [u8; 204] = |n| (163.67 / (24329.0 / n as f64 + 100.0) * 255.0) as u8); let tnd_out = TND_LUT[3 * self.triangle.cur_sample() as usize + 2 * self.noise.cur_sample() as usize + self.dmc.cur_sample() as usize]; self.samples.push(pulse_out + tnd_out); } pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool { if ppu_cycle % 6 == 1 { // APU Frame Counter clock cycle self.frame_counter.count += 1; if self.frame_counter.count == 3728 { self.q_frame_clock(); } else if self.frame_counter.count == 7456 { self.h_frame_clock(); } else if self.frame_counter.count == 11185 { self.q_frame_clock(); } else if self.frame_counter.count == 14914 && !self.frame_counter.mode_5_step { self.frame_counter.irq = self.frame_counter.interrupt_enabled; self.h_frame_clock(); } else if self.frame_counter.count == 14915 && !self.frame_counter.mode_5_step { self.frame_counter.count = 0; self.frame_counter.irq = self.frame_counter.interrupt_enabled; } else if self.frame_counter.count == 18640 { self.h_frame_clock(); self.frame_counter.count = 0; } self.pulse_1.clock(); self.pulse_2.clock(); self.noise.clock(); self.dmc.clock(); } if ppu_cycle % 3 == 1 { self.triangle.clock(); } if ppu_cycle % (6 * 4) == 1 { self.gen_sample(); } false } pub fn get_frame_samples(&self) -> &[u8] { // println!("'Frame' of samples: {}", self.samples.len()); &self.samples } pub fn reset_frame_samples(&mut self) { self.samples.clear(); } pub fn peek_nmi(&self) -> bool { false } pub fn nmi_waiting(&mut self) -> bool { false } pub fn peek_irq(&self) -> bool { self.frame_counter.irq } pub fn irq_waiting(&mut self) -> bool { self.frame_counter.irq } } #[cfg(feature = "iced")] mod apu_iced { use super::*; use iced::{ Element, Font, widget::{column, text}, }; impl APU { pub fn view<'s, T: 's>(&'s self) -> Element<'s, T> { column![ text(self.pulse_1.view()).font(Font::MONOSPACE), text(self.pulse_2.view()).font(Font::MONOSPACE), text(self.triangle.view()).font(Font::MONOSPACE), text(self.noise.view()).font(Font::MONOSPACE), text(self.dmc.view()).font(Font::MONOSPACE), ] .into() } } } #[cfg(test)] mod tests;