diff --git a/src/bus.rs b/src/bus.rs index bac2ed2..8a2f95b 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -99,6 +99,9 @@ impl Bus { match addr { 0xFF07 => self.timer.control.into(), 0xFF0F => self.interrupt.flag.into(), + 0xFF11 => self.sound.ch1.sound_duty.into(), + 0xFF12 => self.sound.ch1.vol_envelope.into(), + 0xFF25 => self.sound.select.into(), 0xFF26 => self.sound.status.into(), _ => unimplemented!("Unable to read {:#06X} in I/O Registers", addr), } @@ -157,6 +160,9 @@ impl Bus { match addr { 0xFF07 => self.timer.control = byte.into(), 0xFF0F => self.interrupt.flag = byte.into(), + 0xFF11 => self.sound.ch1.sound_duty = byte.into(), + 0xFF12 => self.sound.ch1.vol_envelope = byte.into(), + 0xFF25 => self.sound.select = byte.into(), 0xFF26 => self.sound.status = byte.into(), // FIXME: Should we control which bytes are written to here? _ => unimplemented!("Unable to write to {:#06X} in I/O Registers", addr), }; diff --git a/src/sound.rs b/src/sound.rs index 9f2d6d0..4d55a2d 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -1,6 +1,8 @@ #[derive(Debug, Clone, Copy, Default)] pub struct Sound { pub status: SoundStatus, + pub ch1: Channel1, + pub select: SoundOutputSelect, } #[derive(Debug, Clone, Copy, Default)] @@ -33,3 +35,170 @@ impl From for u8 { | (status.sound_1 as u8) << 0 } } + +#[derive(Debug, Clone, Copy, Default)] +pub struct Channel1 { + pub sound_duty: SoundDuty, + pub vol_envelope: VolumeEnvelope, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct VolumeEnvelope { + init_vol: u8, + direction: EnvelopeDirection, + sweep_count: u8, +} + +impl From for VolumeEnvelope { + fn from(byte: u8) -> Self { + Self { + init_vol: byte >> 4, // Bit 7 -> 4 + direction: ((byte >> 3) & 0x01).into(), // Bit 3 + sweep_count: byte & 0x07, // Bits 2 -> 0 + } + } +} + +impl From for u8 { + fn from(envelope: VolumeEnvelope) -> Self { + let dir_bit: u8 = envelope.direction.into(); + let mut byte = envelope.init_vol << 4; + byte |= dir_bit << 3; + byte |= envelope.sweep_count; + byte + } +} + +#[derive(Debug, Clone, Copy)] +enum EnvelopeDirection { + Decrease, + Increase, +} + +impl From for EnvelopeDirection { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::Decrease, + 0b01 => Self::Increase, + _ => unreachable!("{:#04X} is not a possible value for EnvelopeDirection"), + } + } +} + +impl From for u8 { + fn from(envelope: EnvelopeDirection) -> Self { + match envelope { + EnvelopeDirection::Decrease => 0b00, + EnvelopeDirection::Increase => 0b01, + } + } +} + +impl Default for EnvelopeDirection { + fn default() -> Self { + Self::Decrease // Reasoning: Decrease is 0 + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct SoundDuty { + wave_pattern: WaveDuty, + sound_length: u8, // +} + +impl From for SoundDuty { + fn from(byte: u8) -> Self { + let pattern = byte >> 6; // Get bytes 7 and 6 + let sound_length = byte & 0x3F; // Clear bytes 7 and 6 + + SoundDuty { + wave_pattern: pattern.into(), + sound_length, + } + } +} + +impl From for u8 { + fn from(duty: SoundDuty) -> Self { + let mut byte: u8 = duty.wave_pattern.into(); + byte = (byte << 6) | duty.sound_length; + byte + } +} + +#[derive(Debug, Clone, Copy)] +pub enum WaveDuty { + OneEighth, // 12.5% ( _-------_-------_------- ) + OneQuarter, // 25% ( __------__------__------ ) + OneHalf, // 50% ( ____----____----____---- ) (normal) + ThreeQuarters, // 75% ( ______--______--______-- ) +} + +impl Default for WaveDuty { + fn default() -> Self { + Self::OneEighth // Rationale: OneEighth is 0x00 + } +} + +impl From for u8 { + fn from(wave: WaveDuty) -> Self { + match wave { + WaveDuty::OneEighth => 0b00, + WaveDuty::OneQuarter => 0b01, + WaveDuty::OneHalf => 0b10, + WaveDuty::ThreeQuarters => 0b11, + } + } +} + +impl From for WaveDuty { + fn from(byte: u8) -> Self { + match byte { + 0b00 => Self::OneEighth, + 0b01 => Self::OneQuarter, + 0b10 => Self::OneHalf, + 0b11 => Self::ThreeQuarters, + _ => unreachable!("{:#04X} is not a valid value for a Sound Wave", byte), + } + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct SoundOutputSelect { + pub snd4_term2: bool, + pub snd3_term2: bool, + pub snd2_term2: bool, + pub snd1_term2: bool, + pub snd4_term1: bool, + pub snd3_term1: bool, + pub snd2_term1: bool, + pub snd1_term1: bool, +} + +impl From for u8 { + fn from(select: SoundOutputSelect) -> Self { + (select.snd4_term2 as u8) << 7 + | (select.snd3_term2 as u8) << 6 + | (select.snd2_term2 as u8) << 5 + | (select.snd1_term2 as u8) << 4 + | (select.snd4_term1 as u8) << 3 + | (select.snd3_term1 as u8) << 2 + | (select.snd2_term1 as u8) << 1 + | (select.snd1_term1 as u8) + } +} + +impl From for SoundOutputSelect { + fn from(byte: u8) -> Self { + Self { + snd4_term2: byte >> 7 == 0x01, + snd3_term2: (byte >> 6) & 0x01 == 0x01, + snd2_term2: (byte >> 5) & 0x01 == 0x01, + snd1_term2: (byte >> 4) & 0x01 == 0x01, + snd4_term1: (byte >> 3) & 0x01 == 0x01, + snd3_term1: (byte >> 2) & 0x01 == 0x01, + snd2_term1: (byte >> 1) & 0x01 == 0x01, + snd1_term1: byte & 0x01 == 0x01, + } + } +}