diff --git a/src/processor/memory/mmio/apu.rs b/src/processor/memory/mmio/apu.rs index f3c1a7a..1a06cc5 100644 --- a/src/processor/memory/mmio/apu.rs +++ b/src/processor/memory/mmio/apu.rs @@ -3,6 +3,10 @@ use crate::{ util::{get_bit, set_or_clear_bit}, }; +use self::channels::{EnvelopeMode, NoiseChannel, PwmChannel, WaveChannel}; + +mod channels; + const MEM_START: usize = 0xFF10; const MEM_SIZE: usize = 0xFF40 - MEM_START; @@ -10,36 +14,20 @@ const fn reg(a: Address) -> usize { (a as usize) - MEM_START } -struct Channel { - enabled: bool, - pan_left: bool, - pan_right: bool, -} - -impl Channel { - fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { - Self { - enabled, - pan_left, - pan_right, - } - } -} - struct Channels { - one: Channel, - two: Channel, - three: Channel, - four: Channel, + one: PwmChannel, + two: PwmChannel, + three: WaveChannel, + four: NoiseChannel, } impl Default for Channels { fn default() -> Self { Self { - one: Channel::new(true, true, true), - two: Channel::new(false, true, true), - three: Channel::new(false, true, false), - four: Channel::new(false, true, false), + one: PwmChannel::new(true, true, true), + two: PwmChannel::new(false, true, true), + three: WaveChannel::new(false, true, false), + four: NoiseChannel::new(false, true, false), } } } @@ -89,6 +77,13 @@ impl Apu { fn make_register(&self, addr: Address) -> u8 { match addr { + 0xFF10 => self.channels.one.get_sweep_register(), + 0xFF11 => self.channels.one.get_length_timer_and_duty_cycle(), + 0xFF12 => self.channels.one.get_volume_and_envelope(), + 0xFF14 => self.channels.one.get_control(), + 0xFF16 => self.channels.two.get_length_timer_and_duty_cycle(), + 0xFF17 => self.channels.two.get_volume_and_envelope(), + 0xFF19 => self.channels.two.get_control(), 0xFF24 => { // NR50 - Master volume + VIN panning let mut v = ((self.vol_left << 4) & 0b1110000) | (self.vol_right & 0b111); @@ -129,7 +124,24 @@ impl Apu { pub fn mmio_write(&mut self, addr: Address, data: u8) { match addr { - 0xFF10 => self.masked_io(reg(addr), data, 0b01111111), + 0xFF10 => { + let pace = (data & 0b1110000) >> 4; + let mode = if get_bit(data, 3) { + EnvelopeMode::Decrease + } else { + EnvelopeMode::Increase + }; + let slope = data & 0b111; + self.channels.one.update_sweep(pace, mode, slope); + } + 0xFF11 => self.channels.one.update_length_timer_and_duty_cycle(data), + 0xFF12 => self.channels.one.update_volume_and_envelope(data), + 0xFF13 => self.channels.one.update_wavelength_low(data), + 0xFF14 => self.channels.one.update_wavelength_high_and_control(data), + 0xFF16 => self.channels.two.update_length_timer_and_duty_cycle(data), + 0xFF17 => self.channels.two.update_volume_and_envelope(data), + 0xFF18 => self.channels.two.update_wavelength_low(data), + 0xFF19 => self.channels.two.update_wavelength_high_and_control(data), 0xFF1A => self.masked_io(reg(addr), data, 0b10000000), 0xFF1C => self.masked_io(reg(addr), data, 0b1100000), 0xFF20 => self.masked_io(reg(addr), data, 0b111111), diff --git a/src/processor/memory/mmio/apu/channels.rs b/src/processor/memory/mmio/apu/channels.rs new file mode 100644 index 0000000..ae5848c --- /dev/null +++ b/src/processor/memory/mmio/apu/channels.rs @@ -0,0 +1,167 @@ +use crate::util::{get_bit, set_or_clear_bit}; + +#[derive(Clone, Copy, PartialEq)] +pub(super) enum EnvelopeMode { + Increase, + Decrease, +} + +struct Sweep { + pace: u8, + mode: EnvelopeMode, + slope: u8, +} + +impl Default for Sweep { + fn default() -> Self { + Self { + pace: 0, + mode: EnvelopeMode::Increase, + slope: 0, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +struct Envelope { + initial_volume: u8, + mode: EnvelopeMode, + rate: u8, +} + +impl Default for Envelope { + fn default() -> Self { + Self { + initial_volume: 0xF, + mode: EnvelopeMode::Decrease, + rate: 0x3, + } + } +} + +pub(super) struct PwmChannel { + pub(super) enabled: bool, + pub(super) pan_left: bool, + pub(super) pan_right: bool, + sweep: Sweep, + duty_cycle: u8, + length_enable: bool, + length_timer: u8, + envelope: Envelope, + queued_envelope: Envelope, + wavelength: u16, +} + +impl PwmChannel { + pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { + Self { + enabled, + pan_left, + pan_right, + sweep: Sweep::default(), + duty_cycle: 2, + length_enable: false, + length_timer: 63, + envelope: Envelope::default(), + queued_envelope: Envelope::default(), + wavelength: 0x700, + } + } + + fn trigger(&mut self) { + self.enabled = true; + self.envelope = self.queued_envelope; + } + + pub(super) fn update_sweep(&mut self, pace: u8, mode: EnvelopeMode, slope: u8) { + self.sweep.pace = pace; + self.sweep.mode = mode; + self.sweep.slope = slope; + } + + pub(super) fn get_sweep_register(&self) -> u8 { + set_or_clear_bit( + ((self.sweep.pace & 0b1110000) << 4) | (self.sweep.slope & 0b111), + 3, + self.sweep.mode == EnvelopeMode::Decrease, + ) + } + + pub(super) fn update_length_timer_and_duty_cycle(&mut self, data: u8) { + self.length_timer = (data & 0b11000000) >> 6; + self.duty_cycle = data & 0b11111; + } + + pub(super) fn get_length_timer_and_duty_cycle(&self) -> u8 { + ((self.duty_cycle & 0b11) << 6) | 0b11111 + } + + pub(super) fn update_volume_and_envelope(&mut self, data: u8) { + self.queued_envelope = Envelope { + initial_volume: (data & 0b11110000) >> 4, + mode: if (data & 0b1000) == 0b1000 { + EnvelopeMode::Increase + } else { + EnvelopeMode::Decrease + }, + rate: data & 0b111, + }; + } + + pub(super) fn get_volume_and_envelope(&self) -> u8 { + set_or_clear_bit( + (self.queued_envelope.rate & 0b111) + | ((self.queued_envelope.initial_volume & 0b1111) << 4), + 3, + self.queued_envelope.mode == EnvelopeMode::Decrease, + ) + } + + pub(super) fn update_wavelength_low(&mut self, data: u8) { + self.wavelength = (self.wavelength & 0xFF00) | (data as u16); + } + + pub(super) fn update_wavelength_high_and_control(&mut self, data: u8) { + self.length_enable = get_bit(data, 6); + self.wavelength = (self.wavelength & 0xFF) | (((data & 0b111) as u16) << 8); + if get_bit(data, 7) { + self.trigger(); + } + } + + pub(super) fn get_control(&self) -> u8 { + set_or_clear_bit(0, 6, self.length_enable) + } +} + +pub(super) struct WaveChannel { + pub(super) enabled: bool, + pub(super) pan_left: bool, + pub(super) pan_right: bool, +} + +impl WaveChannel { + pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { + Self { + enabled, + pan_left, + pan_right, + } + } +} + +pub(super) struct NoiseChannel { + pub(super) enabled: bool, + pub(super) pan_left: bool, + pub(super) pan_right: bool, +} + +impl NoiseChannel { + pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { + Self { + enabled, + pan_left, + pan_right, + } + } +}