feat(snd): implement channel 3
This commit is contained in:
parent
e45c13f719
commit
909972d36e
15
src/bus.rs
15
src/bus.rs
|
@ -227,9 +227,14 @@ impl BusIo for Bus {
|
||||||
0x16 => self.sound.ch2.duty.into(),
|
0x16 => self.sound.ch2.duty.into(),
|
||||||
0x17 => self.sound.ch2.envelope.into(),
|
0x17 => self.sound.ch2.envelope.into(),
|
||||||
0x19 => self.sound.ch2.freq_hi.into(),
|
0x19 => self.sound.ch2.freq_hi.into(),
|
||||||
|
0x1A => self.sound.ch3.enabled(),
|
||||||
|
0x1B => self.sound.ch3.len,
|
||||||
|
0x1C => self.sound.ch3.volume(),
|
||||||
|
0x1E => self.sound.ch3.freq_hi.into(),
|
||||||
0x24 => self.sound.ctrl.channel.into(),
|
0x24 => self.sound.ctrl.channel.into(),
|
||||||
0x25 => self.sound.ctrl.output.into(),
|
0x25 => self.sound.ctrl.output.into(),
|
||||||
0x26 => self.sound.ctrl.status.into(),
|
0x26 => self.sound.ctrl.status.into(),
|
||||||
|
0x30..=0x3F => self.sound.ch3.ram[addr as usize - 0xFF30],
|
||||||
0x40 => self.ppu.ctrl.into(),
|
0x40 => self.ppu.ctrl.into(),
|
||||||
0x41 => self.ppu.stat.into(),
|
0x41 => self.ppu.stat.into(),
|
||||||
0x42 => self.ppu.pos.scroll_y,
|
0x42 => self.ppu.pos.scroll_y,
|
||||||
|
@ -328,15 +333,21 @@ impl BusIo for Bus {
|
||||||
0x10 => self.sound.ch1.sweep = byte.into(),
|
0x10 => self.sound.ch1.sweep = byte.into(),
|
||||||
0x11 => self.sound.ch1.duty = byte.into(),
|
0x11 => self.sound.ch1.duty = byte.into(),
|
||||||
0x12 => self.sound.ch1.envelope = byte.into(),
|
0x12 => self.sound.ch1.envelope = byte.into(),
|
||||||
0x13 => self.sound.ch1.freq_lo = byte.into(),
|
0x13 => self.sound.ch1.freq_lo = byte,
|
||||||
0x14 => self.sound.ch1.freq_hi = byte.into(),
|
0x14 => self.sound.ch1.freq_hi = byte.into(),
|
||||||
0x16 => self.sound.ch2.duty = byte.into(),
|
0x16 => self.sound.ch2.duty = byte.into(),
|
||||||
0x17 => self.sound.ch2.envelope = byte.into(),
|
0x17 => self.sound.ch2.envelope = byte.into(),
|
||||||
0x18 => self.sound.ch2.freq_lo = byte.into(),
|
0x18 => self.sound.ch2.freq_lo = byte,
|
||||||
0x19 => self.sound.ch2.freq_hi = byte.into(),
|
0x19 => self.sound.ch2.freq_hi = byte.into(),
|
||||||
|
0x1A => self.sound.ch3.set_enabled(byte),
|
||||||
|
0x1B => self.sound.ch3.len = byte,
|
||||||
|
0x1C => self.sound.ch3.set_volume(byte),
|
||||||
|
0x1D => self.sound.ch3.freq_lo = byte,
|
||||||
|
0x1E => self.sound.ch3.freq_hi = byte.into(),
|
||||||
0x24 => self.sound.ctrl.channel = byte.into(),
|
0x24 => self.sound.ctrl.channel = byte.into(),
|
||||||
0x25 => self.sound.ctrl.output = byte.into(),
|
0x25 => self.sound.ctrl.output = byte.into(),
|
||||||
0x26 => self.sound.ctrl.status = byte.into(), // FIXME: Should we control which bytes are written to here?
|
0x26 => self.sound.ctrl.status = byte.into(), // FIXME: Should we control which bytes are written to here?
|
||||||
|
0x30..=0x3F => self.sound.ch3.ram[addr as usize - 0xFF30] = byte,
|
||||||
0x40 => self.ppu.ctrl = byte.into(),
|
0x40 => self.ppu.ctrl = byte.into(),
|
||||||
0x41 => self.ppu.stat.update(byte),
|
0x41 => self.ppu.stat.update(byte),
|
||||||
0x42 => self.ppu.pos.scroll_y = byte,
|
0x42 => self.ppu.pos.scroll_y = byte,
|
||||||
|
|
92
src/sound.rs
92
src/sound.rs
|
@ -1,9 +1,17 @@
|
||||||
use bitfield::bitfield;
|
use bitfield::bitfield;
|
||||||
|
|
||||||
|
const WAVE_PATTERN_RAM_LEN: usize = 0x10;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub(crate) struct Sound {
|
pub(crate) struct Sound {
|
||||||
pub(crate) ctrl: SoundControl,
|
pub(crate) ctrl: SoundControl,
|
||||||
|
/// Tone & Sweep
|
||||||
pub(crate) ch1: Channel1,
|
pub(crate) ch1: Channel1,
|
||||||
|
/// Tone
|
||||||
pub(crate) ch2: Channel2,
|
pub(crate) ch2: Channel2,
|
||||||
|
/// Wave
|
||||||
|
pub(crate) ch3: Channel3,
|
||||||
|
// pub(crate) ch4: Channel4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sound {
|
impl Sound {
|
||||||
|
@ -57,31 +65,6 @@ impl From<FrequencyHigh> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
pub struct FrequencyLow(u8);
|
|
||||||
impl Debug;
|
|
||||||
pub _, set_freq_bits: 7, 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Copy for FrequencyLow {}
|
|
||||||
impl Clone for FrequencyLow {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FrequencyLow {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for FrequencyLow {
|
|
||||||
fn from(byte: u8) -> Self {
|
|
||||||
Self(byte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum FrequencyType {
|
enum FrequencyType {
|
||||||
Counter = 0,
|
Counter = 0,
|
||||||
|
@ -148,7 +131,7 @@ pub(crate) struct Channel1 {
|
||||||
/// 0xFF12 | NR12 - Channel 1 Volume Envelope
|
/// 0xFF12 | NR12 - Channel 1 Volume Envelope
|
||||||
pub(crate) envelope: VolumeEnvelope,
|
pub(crate) envelope: VolumeEnvelope,
|
||||||
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
||||||
pub(crate) freq_lo: FrequencyLow,
|
pub(crate) freq_lo: u8,
|
||||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||||
pub(crate) freq_hi: FrequencyHigh,
|
pub(crate) freq_hi: FrequencyHigh,
|
||||||
}
|
}
|
||||||
|
@ -215,7 +198,7 @@ pub(crate) struct Channel2 {
|
||||||
/// 0xFF17 | NR22 - Channel 2 Volume ENvelope
|
/// 0xFF17 | NR22 - Channel 2 Volume ENvelope
|
||||||
pub(crate) envelope: VolumeEnvelope,
|
pub(crate) envelope: VolumeEnvelope,
|
||||||
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
||||||
pub(crate) freq_lo: FrequencyLow,
|
pub(crate) freq_lo: u8,
|
||||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||||
pub(crate) freq_hi: FrequencyHigh,
|
pub(crate) freq_hi: FrequencyHigh,
|
||||||
}
|
}
|
||||||
|
@ -339,6 +322,61 @@ impl From<u8> for WavePattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub(crate) struct Channel3 {
|
||||||
|
/// 0xFF1A | NR30 - Channel 3 Sound on/off
|
||||||
|
enabled: bool,
|
||||||
|
/// 0xFF1B | NR31 - Sound Length
|
||||||
|
pub(crate) len: u8,
|
||||||
|
/// 0xFF1C | NR32 - Channel 3 Volume
|
||||||
|
volume: Channel3Volume,
|
||||||
|
/// 0xFF1D | NR33 - Channel 3 Frequency low (lower 8 bits)
|
||||||
|
pub(crate) freq_lo: u8,
|
||||||
|
/// 0xFF1E | NR34 - Channel 3 Frequency high
|
||||||
|
pub(crate) freq_hi: FrequencyHigh,
|
||||||
|
pub(crate) ram: [u8; WAVE_PATTERN_RAM_LEN],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Channel3 {
|
||||||
|
pub fn enabled(&self) -> u8 {
|
||||||
|
self.enabled as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_enabled(&mut self, byte: u8) {
|
||||||
|
self.enabled = (byte >> 7) & 0x01 == 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn volume(&self) -> u8 {
|
||||||
|
(self.volume as u8) << 5
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_volume(&mut self, byte: u8) {
|
||||||
|
use Channel3Volume::*;
|
||||||
|
|
||||||
|
self.volume = match (byte >> 5) & 0x03 {
|
||||||
|
0b00 => Mute,
|
||||||
|
0b01 => Full,
|
||||||
|
0b10 => Half,
|
||||||
|
0b11 => Quarter,
|
||||||
|
_ => unreachable!("{:#04X} is not a valid value for Channel3Volume", byte),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Channel3Volume {
|
||||||
|
Mute = 0,
|
||||||
|
Full = 1,
|
||||||
|
Half = 2,
|
||||||
|
Quarter = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Channel3Volume {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Mute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
pub struct SoundOutput(u8);
|
pub struct SoundOutput(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
|
|
Loading…
Reference in New Issue