diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 7938f0dbd3d0646ee92c873385b03c626873b8f1..66a89f1be880771b41fe6e4882d245a93699ec7e 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -719,7 +719,7 @@ struct Args { )] cycles: u64, - #[arg(short, long, default_value_t = String::from("C:/Users/joamag/Desktop/gb-test-roms/dmg_sound/rom_singles/01-registers.gb"), help = "Path to the ROM file to be loaded")] + #[arg(short, long, default_value_t = String::from("C:/Users/joamag/Desktop/gb-test-roms/dmg_sound/rom_singles/02-len ctr.gb"), help = "Path to the ROM file to be loaded")] rom_path: String, } diff --git a/src/apu.rs b/src/apu.rs index 1b86fa5ef43c942338d1ab2f746b2c53d5093eff..6a9b9a0af832ce32ee503787ecb5b08c54d43391 100644 --- a/src/apu.rs +++ b/src/apu.rs @@ -25,6 +25,7 @@ pub struct Apu { ch1_envelope_enabled: bool, ch1_sweep_sequence: u8, ch1_output: u8, + ch1_dac: bool, ch1_sweep_slope: u8, ch1_sweep_increase: bool, ch1_sweep_pace: u8, @@ -34,7 +35,7 @@ pub struct Apu { ch1_direction: u8, ch1_volume: u8, ch1_wave_length: u16, - ch1_length_stop: bool, + ch1_length_enabled: bool, ch1_enabled: bool, ch2_timer: i16, @@ -42,13 +43,14 @@ pub struct Apu { ch2_envelope_sequence: u8, ch2_envelope_enabled: bool, ch2_output: u8, + ch2_dac: bool, ch2_length_timer: u8, ch2_wave_duty: u8, ch2_pace: u8, ch2_direction: u8, ch2_volume: u8, ch2_wave_length: u16, - ch2_length_stop: bool, + ch2_length_enabled: bool, ch2_enabled: bool, ch3_timer: i16, @@ -58,13 +60,14 @@ pub struct Apu { ch3_length_timer: u8, ch3_output_level: u8, ch3_wave_length: u16, - ch3_length_stop: bool, + ch3_length_enabled: bool, ch3_enabled: bool, ch4_timer: i32, ch4_envelope_sequence: u8, ch4_envelope_enabled: bool, ch4_output: u8, + ch4_dac: bool, ch4_length_timer: u8, ch4_pace: u8, ch4_direction: u8, @@ -73,9 +76,10 @@ pub struct Apu { ch4_width_mode: bool, ch4_clock_shift: u8, ch4_lfsr: u16, - ch4_length_stop: bool, + ch4_length_enabled: bool, ch4_enabled: bool, + master: u8, glob_panning: u8, right_enabled: bool, @@ -108,8 +112,9 @@ impl Apu { ch1_envelope_enabled: false, ch1_sweep_sequence: 0, ch1_output: 0, + ch1_dac: false, ch1_sweep_slope: 0x0, - ch1_sweep_increase: false, + ch1_sweep_increase: true, ch1_sweep_pace: 0x0, ch1_length_timer: 0x0, ch1_wave_duty: 0x0, @@ -117,7 +122,7 @@ impl Apu { ch1_direction: 0x0, ch1_volume: 0x0, ch1_wave_length: 0x0, - ch1_length_stop: false, + ch1_length_enabled: false, ch1_enabled: false, ch2_timer: 0, @@ -125,13 +130,14 @@ impl Apu { ch2_envelope_sequence: 0, ch2_envelope_enabled: false, ch2_output: 0, + ch2_dac: false, ch2_length_timer: 0x0, ch2_wave_duty: 0x0, ch2_pace: 0x0, ch2_direction: 0x0, ch2_volume: 0x0, ch2_wave_length: 0x0, - ch2_length_stop: false, + ch2_length_enabled: false, ch2_enabled: false, ch3_timer: 0, @@ -141,13 +147,14 @@ impl Apu { ch3_length_timer: 0x0, ch3_output_level: 0x0, ch3_wave_length: 0x0, - ch3_length_stop: false, + ch3_length_enabled: false, ch3_enabled: false, ch4_timer: 0, ch4_envelope_sequence: 0, ch4_envelope_enabled: false, ch4_output: 0, + ch4_dac: false, ch4_length_timer: 0x0, ch4_pace: 0x0, ch4_direction: 0x0, @@ -156,9 +163,10 @@ impl Apu { ch4_width_mode: false, ch4_clock_shift: 0x0, ch4_lfsr: 0x0, - ch4_length_stop: false, + ch4_length_enabled: false, ch4_enabled: false, + master: 0x0, glob_panning: 0x0, left_enabled: true, @@ -200,8 +208,9 @@ impl Apu { self.ch1_envelope_enabled = false; self.ch1_sweep_sequence = 0; self.ch1_output = 0; + self.ch1_dac = false; self.ch1_sweep_slope = 0x0; - self.ch1_sweep_increase = false; + self.ch1_sweep_increase = true; self.ch1_sweep_pace = 0x0; self.ch1_length_timer = 0x0; self.ch1_wave_duty = 0x0; @@ -209,7 +218,7 @@ impl Apu { self.ch1_direction = 0x0; self.ch1_volume = 0x0; self.ch1_wave_length = 0x0; - self.ch1_length_stop = false; + self.ch1_length_enabled = false; self.ch1_enabled = false; self.ch2_timer = 0; @@ -217,13 +226,14 @@ impl Apu { self.ch2_envelope_sequence = 0; self.ch2_envelope_enabled = false; self.ch2_output = 0; + self.ch2_dac = false; self.ch2_length_timer = 0x0; self.ch2_wave_duty = 0x0; self.ch2_pace = 0x0; self.ch2_direction = 0x0; self.ch2_volume = 0x0; self.ch2_wave_length = 0x0; - self.ch2_length_stop = false; + self.ch2_length_enabled = false; self.ch2_enabled = false; self.ch3_timer = 0; @@ -233,13 +243,14 @@ impl Apu { self.ch3_length_timer = 0x0; self.ch3_output_level = 0x0; self.ch3_wave_length = 0x0; - self.ch3_length_stop = false; + self.ch3_length_enabled = false; self.ch3_enabled = false; self.ch4_timer = 0; self.ch4_envelope_sequence = 0; self.ch4_envelope_enabled = false; self.ch4_output = 0; + self.ch4_dac = false; self.ch4_length_timer = 0x0; self.ch4_pace = 0x0; self.ch4_direction = 0x0; @@ -248,9 +259,10 @@ impl Apu { self.ch4_width_mode = false; self.ch4_clock_shift = 0x0; self.ch4_lfsr = 0x0; - self.ch4_length_stop = false; + self.ch4_length_enabled = false; self.ch4_enabled = false; + self.master = 0x0; self.glob_panning = 0x0; self.left_enabled = true; @@ -348,7 +360,7 @@ impl Apu { // 0xFF13 — NR13: Channel 1 wavelength low 0xff13 => 0xff, // 0xFF14 — NR14: Channel 1 wavelength high & control - 0xff14 => (if self.ch1_length_stop { 0x40 } else { 0x00 }) | 0xbf, + 0xff14 => (if self.ch1_length_enabled { 0x40 } else { 0x00 }) | 0xbf, // 0xFF15 — Not used 0xff15 => 0xff, @@ -360,46 +372,67 @@ impl Apu { | ((self.ch2_direction & 0x01) << 3) | ((self.ch2_volume & 0x0f) << 4) } + // 0xFF18 — NR23: Channel 2 wavelength low + 0xff18 => 0xff, + // 0xFF19 — NR24: Channel 2 wavelength high & control + 0xff19 => (if self.ch2_length_enabled { 0x40 } else { 0x00 }) | 0xbf, // 0xFF1A — NR30: Channel 3 DAC enable - 0xff1a => { - if self.ch3_dac { - 0x80 - } else { - 0x00 - } - } + 0xff1a => (if self.ch3_dac { 0x80 } else { 0x00 }) | 0x7f, // 0xFF1B — NR31: Channel 3 length timer - 0xff1b => 0x00, + 0xff1b => 0xff, // 0xFF1C — NR32: Channel 3 output level - 0xff1c => (self.ch3_output_level & 0x03) << 5, + 0xff1c => ((self.ch3_output_level & 0x03) << 5) | 0x9f, + // 0xFF1D — NR33: Channel 3 wavelength low + 0xff1d => 0xff, + // 0xFF1E — NR34: Channel 3 wavelength high & control + 0xff1e => (if self.ch3_length_enabled { 0x40 } else { 0x00 }) | 0xbf, // 0xFF1F — Not used 0xff1f => 0xff, // 0xFF20 — NR41: Channel 4 length timer - 0xff20 => 0x00, + 0xff20 => 0xff, // 0xFF21 — NR42: Channel 4 volume & envelope 0xff21 => { (self.ch4_pace & 0x07) | ((self.ch4_direction & 0x01) << 3) | ((self.ch4_volume & 0x0f) << 4) } + // 0xFF22 — NR43: Channel 4 frequency & randomness + 0xff22 => { + (self.ch4_divisor & 0x07) + | if self.ch4_width_mode { 0x08 } else { 0x00 } + | ((self.ch4_clock_shift & 0x0f) << 4) + } + // 0xFF23 — NR44: Channel 4 control + 0xff23 => (if self.ch4_length_enabled { 0x40 } else { 0x00 }) | 0xbf, + // 0xFF24 — NR50: Master volume & VIN panning + 0xff24 => self.master, // 0xFF25 — NR51: Sound panning 0xff25 => self.glob_panning, // 0xFF26 — NR52: Sound on/off 0xff26 => { #[allow(clippy::bool_to_int_with_if)] - (if self.ch1_enabled { 0x01 } else { 0x00 } - | if self.ch2_enabled { 0x02 } else { 0x00 } - | if self.ch3_enabled && self.ch3_dac { - 0x04 - } else { - 0x00 - } - | if self.ch4_enabled { 0x08 } else { 0x00 } - | if self.sound_enabled { 0x80 } else { 0x00 }) + ((if self.ch1_enabled && self.ch1_dac { + 0x01 + } else { + 0x00 + } | if self.ch2_enabled && self.ch2_dac { + 0x02 + } else { + 0x00 + } | if self.ch3_enabled && self.ch3_dac { + 0x04 + } else { + 0x00 + } | if self.ch4_enabled && self.ch4_dac { + 0x08 + } else { + 0x00 + } | if self.sound_enabled { 0x80 } else { 0x00 }) + | 0x70) } // 0xFF30-0xFF3F — Wave pattern RAM @@ -411,13 +444,20 @@ impl Apu { } }; - println!("APU read: {:04x} = {:02x}", addr, value); + println!("APU read: 0x{:04X} = 0x{:02X}", addr, value); value } pub fn write(&mut self, addr: u16, value: u8) { - println!("APU write: {:04x} = {:02x}", addr, value); + println!("APU write: 0x{:04X} = 0x{:02X}", addr, value); + + // in case the sound is disabled then ignores writes + // to any register aside from the sound on/off + if !self.sound_enabled && addr != 0xff26 { + return; + } + match addr { // 0xFF10 — NR10: Channel 1 sweep 0xff10 => { @@ -438,6 +478,10 @@ impl Apu { self.ch1_volume = (value & 0xf0) >> 4; self.ch1_envelope_enabled = self.ch1_pace > 0; self.ch1_envelope_sequence = 0; + self.ch1_dac = value & 0xf8 != 0x00; + if !self.ch1_dac { + self.ch1_enabled = false; + } } // 0xFF13 — NR13: Channel 1 wavelength low 0xff13 => { @@ -449,12 +493,12 @@ impl Apu { let trigger = value & 0x80 == 0x80; self.ch1_wave_length = (self.ch1_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); - self.ch1_length_stop = value & 0x40 == 0x40; + self.ch1_length_enabled = value & 0x40 == 0x40; self.ch1_enabled |= value & 0x80 == 0x80; if trigger { self.trigger_ch1(); } - if length_trigger && self.ch1_length_timer == 0 { + if length_trigger && self.ch1_length_timer >= 64 { self.ch1_enabled = false; } } @@ -473,6 +517,10 @@ impl Apu { self.ch2_volume = (value & 0xf0) >> 4; self.ch2_envelope_enabled = self.ch2_pace > 0; self.ch2_envelope_sequence = 0; + self.ch2_dac = value & 0xf8 != 0x00; + if !self.ch2_dac { + self.ch2_enabled = false; + } } // 0xFF18 — NR23: Channel 2 wavelength low 0xff18 => { @@ -484,12 +532,12 @@ impl Apu { let trigger = value & 0x80 == 0x80; self.ch2_wave_length = (self.ch2_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); - self.ch2_length_stop = length_trigger; + self.ch2_length_enabled = length_trigger; self.ch2_enabled |= trigger; if trigger { self.trigger_ch2(); } - if length_trigger && self.ch2_length_timer == 0 { + if length_trigger && self.ch2_length_timer >= 64 { self.ch2_enabled = false; } } @@ -497,6 +545,9 @@ impl Apu { // 0xFF1A — NR30: Channel 3 DAC enable 0xff1a => { self.ch3_dac = value & 0x80 == 0x80; + if !self.ch3_dac { + self.ch3_enabled = false; + } } // 0xFF1B — NR31: Channel 3 length timer 0xff1b => { @@ -506,7 +557,7 @@ impl Apu { 0xff1c => { self.ch3_output_level = (value & 0x60) >> 5; } - // 0xFF1D — NR33: Channel 3 wavelength low [write-only] + // 0xFF1D — NR33: Channel 3 wavelength low 0xff1d => { self.ch3_wave_length = (self.ch3_wave_length & 0xff00) | value as u16; } @@ -516,12 +567,12 @@ impl Apu { let trigger = value & 0x80 == 0x80; self.ch3_wave_length = (self.ch3_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); - self.ch3_length_stop = length_trigger; + self.ch3_length_enabled = length_trigger; self.ch3_enabled |= trigger; if trigger { self.trigger_ch3(); } - if length_trigger && self.ch3_length_timer == 0 { + if length_trigger && self.ch3_length_timer >= 64 { self.ch3_enabled = false; } } @@ -539,6 +590,10 @@ impl Apu { self.ch4_volume = (value & 0xf0) >> 4; self.ch4_envelope_enabled = self.ch4_pace > 0; self.ch4_envelope_sequence = 0; + self.ch4_dac = value & 0xf8 != 0x00; + if !self.ch4_dac { + self.ch4_enabled = false; + } } // 0xFF22 — NR43: Channel 4 frequency & randomness 0xff22 => { @@ -550,19 +605,19 @@ impl Apu { 0xff23 => { let length_trigger = value & 0x40 == 0x40; let trigger = value & 0x80 == 0x80; - self.ch4_length_stop = length_trigger; + self.ch4_length_enabled = length_trigger; self.ch4_enabled |= trigger; if trigger { self.trigger_ch4(); } - if length_trigger && self.ch4_length_timer == 0 { + if length_trigger && self.ch4_length_timer >= 64 { self.ch4_enabled = false; } } // 0xFF24 — NR50: Master volume & VIN panning 0xff24 => { - //@TODO: Implement master volume & VIN panning + self.master = value; } // 0xFF25 — NR51: Sound panning 0xff25 => { @@ -691,33 +746,42 @@ impl Apu { fn tick_length(&mut self, channel: Channel) { match channel { Channel::Ch1 => { - if !self.ch1_enabled { + if !self.ch1_length_enabled { return; } self.ch1_length_timer = self.ch1_length_timer.saturating_add(1); if self.ch1_length_timer >= 64 { - self.ch1_enabled = !self.ch1_length_stop; + self.ch1_enabled = false; self.ch1_length_timer = 0; } } Channel::Ch2 => { + if !self.ch2_length_enabled { + return; + } self.ch2_length_timer = self.ch2_length_timer.saturating_add(1); if self.ch2_length_timer >= 64 { - self.ch2_enabled = !self.ch2_length_stop; + self.ch2_enabled = false; self.ch2_length_timer = 0; } } Channel::Ch3 => { + if !self.ch3_length_enabled { + return; + } self.ch3_length_timer = self.ch3_length_timer.saturating_add(1); if self.ch3_length_timer >= 64 { - self.ch3_enabled = !self.ch3_length_stop; + self.ch3_enabled = false; self.ch3_length_timer = 0; } } Channel::Ch4 => { + if !self.ch4_length_enabled { + return; + } self.ch4_length_timer = self.ch4_length_timer.saturating_add(1); if self.ch4_length_timer >= 64 { - self.ch4_enabled = !self.ch4_length_stop; + self.ch4_enabled = false; self.ch4_length_timer = 0; } }