diff --git a/frontends/sdl/src/audio.rs b/frontends/sdl/src/audio.rs index 2b5480bcc2498065f5bccac1a2287fbc9e8c1890..d0dabfc1d231d372137cb23aa38a9c3b8269d981 100644 --- a/frontends/sdl/src/audio.rs +++ b/frontends/sdl/src/audio.rs @@ -28,7 +28,7 @@ impl AudioCallback for AudioWave { for x in out.iter_mut() { *x = match self.audio_provider.lock() { Ok(mut provider) => { - let value = provider.tick_apu(self.spec.freq as u32) as f32 / 7.0; + let value = provider.output_clock_apu(1, self.spec.freq as u32) as f32 / 7.0; value } Err(_) => 0.0, @@ -49,7 +49,7 @@ impl Audio { let desired_spec = AudioSpecDesired { freq: Some(44100), channels: Some(1), - samples: None, + samples: Some(2), }; let device = audio_subsystem diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 5844e69a9a8ae556ab0ba6aef5323108132d55a0..5aa26b4969d63cdb059d064c0e4142e19b0f5e44 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -6,7 +6,7 @@ pub mod graphics; use audio::Audio; use boytacean::{ - gb::{AudioProvider, GameBoy}, + gb::GameBoy, pad::PadKey, ppu::{PaletteInfo, PpuMode, DISPLAY_HEIGHT, DISPLAY_WIDTH}, }; @@ -117,10 +117,6 @@ impl Emulator { self.audio = Some(Audio::new(sdl, audio_provider)); } - pub fn tick_audio(&mut self, freq: u32) -> u8 { - self.system.lock().unwrap().tick_apu(freq) - } - pub fn load_rom(&mut self, path: &str) { let mut system = self.system.lock().unwrap(); let rom = system.load_rom_file(path); @@ -288,10 +284,13 @@ impl Emulator { // valid under the current block let mut system = self.system.lock().unwrap(); - // runs the Game Boy clock, this operations should - // include the advance of both the CPU and the PPU + // runs the Game Boy clock, this operation should + // include the advance of both the CPU, PPU, APU + // and any other frequency based component of the system counter_cycles += system.clock() as u32; + // in case a V-Blank state has been reached a new frame is available + // then the frame must be pushed into SDL for display if system.ppu_mode() == PpuMode::VBlank && system.ppu_frame() != last_frame { // obtains the frame buffer of the Game Boy PPU and uses it diff --git a/src/apu.rs b/src/apu.rs index 0e1b92ef824b3f9afccaed3f122b4983c204b655..2c9cd6b01b45576ac9d22cddc53be5781ea9a2f1 100644 --- a/src/apu.rs +++ b/src/apu.rs @@ -34,6 +34,18 @@ pub struct Apu { ch2_wave_length: u16, ch2_sound_length: bool, ch2_enabled: bool, + + ch3_timer: u16, + ch3_sequence: u8, + ch3_output: u8, + ch3_dac: bool, + ch3_length_timer: u8, + ch3_output_level: u8, + ch3_wave_length: u16, + ch3_sound_length: bool, + ch3_enabled: bool, + + wave_ram: [u8; 16], } impl Apu { @@ -65,10 +77,27 @@ impl Apu { ch2_wave_length: 0x0, ch2_sound_length: false, ch2_enabled: false, + + ch3_timer: 0, + ch3_sequence: 0, + ch3_output: 0, + ch3_dac: false, + ch3_length_timer: 0x0, + ch3_output_level: 0x0, + ch3_wave_length: 0x0, + ch3_sound_length: false, + ch3_enabled: false, + + wave_ram: [0u8; 16], } } - pub fn clock(&mut self, cycles: u8, freq: u32) { + pub fn clock(&mut self, cycles: u8) { + self.clock_f(cycles, 4194304); + } + + pub fn clock_f(&mut self, cycles: u8, freq: u32) { + // @todo the performance here requires improvement for _ in 0..cycles { self.cycle(freq); } @@ -76,7 +105,6 @@ impl Apu { pub fn read(&mut self, addr: u16) -> u8 { match addr { - 0xff26 => 1 as u8, // @todo implement this _ => { warnln!("Reading from unknown APU location 0x{:04x}", addr); 0xff @@ -137,9 +165,44 @@ impl Apu { (self.ch2_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); self.ch2_sound_length |= value & 0x40 == 0x40; self.ch2_enabled |= value & 0x80 == 0x80; + if value & 0x80 == 0x80 { + //self.ch2_timer = 0; + //self.ch2_sequence = 0; + //@todo improve this reset operation + } println!("CH2 Enabled {}", self.ch2_enabled); } + // 0xFF1A — NR30: Channel 3 DAC enable + 0xff1a => { + self.ch3_dac = value & 0x80 == 0x80; + } + // 0xFF1B — NR31: Channel 3 length timer + 0xff1b => { + self.ch3_length_timer = value; + } + // 0xFF1C — NR32: Channel 3 output level + 0xff1c => { + self.ch3_output_level = value & 0x60 >> 5; + } + // 0xFF1D — NR33: Channel 3 wavelength low [write-only] + 0xff1d => { + self.ch3_wave_length = (self.ch3_wave_length & 0xff00) | value as u16; + } + // 0xFF1E — NR34: Channel 3 wavelength high & control + 0xff1e => { + self.ch3_wave_length = + (self.ch3_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); + self.ch3_sound_length |= value & 0x40 == 0x40; + self.ch3_enabled |= value & 0x80 == 0x80; + println!("CH3 Enabled {}", self.ch3_enabled); + } + + // 0xFF30-0xFF3F — Wave pattern RAM + 0xff30..=0xff3f => { + self.wave_ram[addr as usize - 0xff30] = value; + } + _ => warnln!("Writing in unknown APU location 0x{:04x}", addr), } } diff --git a/src/cpu.rs b/src/cpu.rs index ac8e52585d83fc1049d316aa18c2fa54b7d32b43..237ef622b82478e4fc6f4433c1eaa051cfd5153c 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -267,6 +267,11 @@ impl Cpu { &mut self.mmu } + #[inline(always)] + pub fn mmu_i(&self) -> &Mmu { + &self.mmu + } + #[inline(always)] pub fn ppu(&mut self) -> &mut Ppu { self.mmu().ppu() @@ -277,6 +282,11 @@ impl Cpu { self.mmu().apu() } + #[inline(always)] + pub fn apu_i(&self) -> &Apu { + self.mmu_i().apu_i() + } + #[inline(always)] pub fn pad(&mut self) -> &mut Pad { self.mmu().pad() diff --git a/src/gb.rs b/src/gb.rs index 125ec06df6e4b7defe6b3f5982b3d8bcb8ee0e40..bb779fe132dc054857b6549f6ebc36609889db9d 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -54,7 +54,8 @@ pub struct Registers { } pub trait AudioProvider { - fn tick_apu(&mut self, freq: u32) -> u8; + fn output_apu(&self) -> u8; + fn output_clock_apu(&mut self, cycles: u8, freq: u32) -> u8; } #[cfg_attr(feature = "wasm", wasm_bindgen)] @@ -99,8 +100,8 @@ impl GameBoy { self.ppu().clock(cycles) } - pub fn apu_clock(&mut self, cycles: u8, freq: u32) { - self.apu().clock(cycles, freq) + pub fn apu_clock(&mut self, cycles: u8) { + self.apu().clock(cycles) } pub fn timer_clock(&mut self, cycles: u8) { @@ -260,6 +261,10 @@ impl GameBoy { self.cpu.apu() } + pub fn apu_i(&self) -> &Apu { + self.cpu.apu_i() + } + pub fn pad(&mut self) -> &mut Pad { self.cpu.pad() } @@ -376,9 +381,13 @@ pub fn hook_impl(info: &PanicInfo) { } impl AudioProvider for GameBoy { - fn tick_apu(&mut self, freq: u32) -> u8 { - self.apu_clock(1, freq); - self.apu().output() + fn output_apu(&self) -> u8 { + self.apu_i().output() + } + + fn output_clock_apu(&mut self, cycles: u8, freq: u32) -> u8 { + self.apu().clock_f(cycles, freq); + self.apu_i().output() } } diff --git a/src/mmu.rs b/src/mmu.rs index a718fa49ddd28003c3fc90f9bdb70b94d47c3fea..d3b389cfb01501c2314d9e1e69459e3a6e107eed 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -79,6 +79,10 @@ impl Mmu { &mut self.apu } + pub fn apu_i(&self) -> &Apu { + &self.apu + } + pub fn pad(&mut self) -> &mut Pad { &mut self.pad }