From 70cb99161d24a1ae7a25deda8b43666a831fb62d Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Fri, 20 Dec 2019 19:18:16 +0200 Subject: [PATCH] Start working on DMA sound Former-commit-id: 066210a86a7836b6ae1dfd5ce229d050cbe00ca4 --- src/core/dma.rs | 70 ++++++++++++++++++------- src/core/gba.rs | 4 +- src/core/sound/fifo.rs | 51 +++++++++++++++++++ src/core/sound/mod.rs | 113 ++++++++++++++++++++++++++++++++++------- src/core/timer.rs | 44 +++++++++------- 5 files changed, 225 insertions(+), 57 deletions(-) create mode 100644 src/core/sound/fifo.rs diff --git a/src/core/dma.rs b/src/core/dma.rs index 6ac5efc..2a1ce36 100644 --- a/src/core/dma.rs +++ b/src/core/dma.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; use super::arm7tdmi::{Addr, Bus}; use super::sysbus::SysBus; -use super::{Interrupt, IrqBitmask, SyncedIoDevice}; +use super::{Interrupt, IrqBitmask}; use num::FromPrimitive; @@ -27,6 +27,7 @@ pub struct DmaChannel { running: bool, cycles: usize, start_cycles: usize, + fifo_mode: bool, irq: Interrupt, } @@ -52,6 +53,7 @@ impl DmaChannel { ctrl: DmaChannelCtrl(0), cycles: 0, start_cycles: 0, + fifo_mode: false, internal: Default::default(), } } @@ -88,19 +90,37 @@ impl DmaChannel { pub fn write_dma_ctrl(&mut self, value: u16) -> bool { let ctrl = DmaChannelCtrl(value); + let timing = ctrl.timing(); let mut start_immediately = false; if ctrl.is_enabled() && !self.ctrl.is_enabled() { self.start_cycles = self.cycles; self.running = true; - start_immediately = ctrl.timing() == 0; + start_immediately = timing == 0; self.internal.src_addr = self.src; self.internal.dst_addr = self.dst; self.internal.count = self.wc; + self.fifo_mode = timing == 3 && (self.id == 0 || self.id == 1); } self.ctrl = ctrl; return start_immediately; } + #[inline] + fn xfer_adj_addrs(&mut self, word_size: u32) { + match self.ctrl.src_adj() { + /* Increment */ 0 => self.internal.src_addr += word_size, + /* Decrement */ 1 => self.internal.src_addr -= word_size, + /* Fixed */ 2 => {} + _ => panic!("forbidden DMA source address adjustment"), + } + match self.ctrl.dst_adj() { + /* Increment[+Reload] */ 0 | 3 => self.internal.dst_addr += word_size, + /* Decrement */ 1 => self.internal.dst_addr -= word_size, + /* Fixed */ 2 => {} + _ => panic!("forbidden DMA dest address adjustment"), + } + } + fn xfer(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) { let word_size = if self.ctrl.is_32bit() { 4 } else { 2 }; let count = match self.internal.count { @@ -110,25 +130,26 @@ impl DmaChannel { }, _ => self.internal.count, }; - for _ in 0..count { - if word_size == 4 { + let fifo_mode = self.fifo_mode; + + if fifo_mode { + println!("FIFO Tranfer"); + for _ in 0..count { + let v = sb.read_16(self.internal.src_addr); + sb.write_16(self.internal.dst_addr, v); + self.internal.src_addr += 2; + } + } else if word_size == 4 { + for _ in 0..count { let w = sb.read_32(self.internal.src_addr); - sb.write_32(self.internal.dst_addr, w) - } else { + sb.write_32(self.internal.dst_addr, w); + self.xfer_adj_addrs(word_size); + } + } else { + for _ in 0..count { let hw = sb.read_16(self.internal.src_addr); - sb.write_16(self.internal.dst_addr, hw) - } - match self.ctrl.src_adj() { - /* Increment */ 0 => self.internal.src_addr += word_size, - /* Decrement */ 1 => self.internal.src_addr -= word_size, - /* Fixed */ 2 => {} - _ => panic!("forbidden DMA source address adjustment"), - } - match self.ctrl.dst_adj() { - /* Increment[+Reload] */ 0 | 3 => self.internal.dst_addr += word_size, - /* Decrement */ 1 => self.internal.dst_addr -= word_size, - /* Fixed */ 2 => {} - _ => panic!("forbidden DMA dest address adjustment"), + sb.write_16(self.internal.dst_addr, hw); + self.xfer_adj_addrs(word_size) } } if self.ctrl.is_triggering_irq() { @@ -210,6 +231,17 @@ impl DmaController { } } } + + pub fn notify_sound_fifo(&mut self, fifo_addr: u32) { + for i in 1..=2 { + if self.channels[i].ctrl.is_enabled() + && self.channels[i].ctrl.timing() == 3 + && self.channels[i].dst == fifo_addr + { + self.xfers_queue.push_back(i); + } + } + } } bitfield! { diff --git a/src/core/gba.rs b/src/core/gba.rs index 7e15737..0ff81cc 100644 --- a/src/core/gba.rs +++ b/src/core/gba.rs @@ -8,6 +8,7 @@ use super::gpu::*; use super::interrupt::*; use super::iodev::*; use super::sysbus::SysBus; +use super::timer::TimerEvent; use super::SyncedIoDevice; @@ -113,7 +114,8 @@ where 0 }; - io.timers.step(cycles, &mut self.sysbus, &mut irqs); + io.timers.tick(cycles, &mut self.sysbus, &mut irqs); + if let Some(new_gpu_state) = io.gpu.step(cycles, &mut self.sysbus, &mut irqs) { match new_gpu_state { GpuState::VBlank => { diff --git a/src/core/sound/fifo.rs b/src/core/sound/fifo.rs new file mode 100644 index 0000000..9fb403d --- /dev/null +++ b/src/core/sound/fifo.rs @@ -0,0 +1,51 @@ +// TODO write tests or replace with a crate + +const SOUND_FIFO_CAPACITY: usize = 32; + +#[derive(Debug)] +pub struct SoundFifo { + wr_pos: usize, + rd_pos: usize, + count: usize, + data: [i8; SOUND_FIFO_CAPACITY], +} + +impl SoundFifo { + pub fn new() -> SoundFifo { + SoundFifo { + wr_pos: 0, + rd_pos: 0, + count: 0, + data: [0; SOUND_FIFO_CAPACITY], + } + } + + pub fn write(&mut self, value: i8) { + if self.count >= SOUND_FIFO_CAPACITY { + return; + } + self.data[self.wr_pos] = value; + self.wr_pos = (self.wr_pos + 1) % SOUND_FIFO_CAPACITY; + self.count += 1; + } + + pub fn read(&mut self) -> i8 { + if self.count == 0 { + return 0; + }; + let value = self.data[self.rd_pos]; + self.rd_pos = (self.rd_pos + 1) % SOUND_FIFO_CAPACITY; + self.count -= 1; + value + } + + pub fn count(&self) -> usize { + self.count + } + + pub fn reset(&mut self) { + self.wr_pos = 0; + self.rd_pos = 0; + self.count = 0; + } +} diff --git a/src/core/sound/mod.rs b/src/core/sound/mod.rs index c3da82a..552c6ad 100644 --- a/src/core/sound/mod.rs +++ b/src/core/sound/mod.rs @@ -1,15 +1,45 @@ use bit::BitIndex; +use super::dma::DmaController; use super::iodev::consts::*; use super::iodev::io_reg_string; +mod fifo; +use fifo::SoundFifo; + const DMG_RATIOS: [f32; 4] = [0.25, 0.5, 1.0, 0.0]; +const DMA_RATIOS: [f32; 2] = [0.5, 1.0]; +const DMA_TIMERS: [usize; 2] = [0, 1]; const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75]; +#[derive(Debug)] +struct NoiseChannel {} + +#[derive(Debug)] +struct DmaSound { + volume: f32, + enable_right: bool, + enable_left: bool, + timer_select: usize, + fifo: SoundFifo, +} + +impl Default for DmaSound { + fn default() -> DmaSound { + DmaSound { + volume: DMA_RATIOS[0], + enable_right: false, + enable_left: false, + timer_select: 0, + fifo: SoundFifo::new(), + } + } +} + #[derive(Debug)] pub struct SoundController { sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample? - last_sample_cycles: usize, // cycles count when we last provided a new sample. + last_sample_cycles: usize, // cycles count when we last provided a new sample. mse: bool, @@ -35,6 +65,9 @@ pub struct SoundController { sqr1_step_increase: bool, sqr1_initial_vol: usize, sqr1_cur_vol: usize, + + sound_a: DmaSound, + sound_b: DmaSound, } impl SoundController { @@ -62,6 +95,8 @@ impl SoundController { sqr1_step_increase: false, sqr1_initial_vol: 0, sqr1_cur_vol: 0, + sound_a: Default::default(), + sound_b: Default::default(), } } @@ -81,10 +116,26 @@ impl SoundController { | cbit(15, self.right_noise) } - REG_SOUNDCNT_H => DMG_RATIOS - .iter() - .position(|&f| f == self.dmg_volume_ratio) - .expect("bad dmg_volume_ratio!") as u16, + REG_SOUNDCNT_H => { + DMG_RATIOS + .iter() + .position(|&f| f == self.dmg_volume_ratio) + .expect("bad dmg_volume_ratio!") as u16 + | DMA_RATIOS + .iter() + .position(|&f| f == self.sound_a.volume) + .unwrap() as u16 + | DMA_RATIOS + .iter() + .position(|&f| f == self.sound_b.volume) + .unwrap() as u16 + | cbit(8, self.sound_a.enable_right) + | cbit(9, self.sound_a.enable_left) + | cbit(10, self.sound_a.timer_select != 0) + | cbit(12, self.sound_b.enable_right) + | cbit(13, self.sound_b.enable_left) + | cbit(14, self.sound_b.timer_select != 0) + } _ => { println!( @@ -105,12 +156,6 @@ impl SoundController { } pub fn handle_write(&mut self, io_addr: u32, value: u16) { - println!( - "Write {} ({:08x}) = {:04x}", - io_reg_string(io_addr), - io_addr, - value - ); if io_addr == REG_SOUNDCNT_X { if value & bit(7) != 0 { if !self.mse { @@ -149,11 +194,20 @@ impl SoundController { REG_SOUNDCNT_H => { self.dmg_volume_ratio = DMG_RATIOS[value.bit_range(0..1) as usize]; - if value.bit_range(2..15) != 0 { - println!( - "unsupported bits in REG_SOUNDCNT_H, {:04x}", - value.bit_range(2..15) - ); + self.sound_a.volume = DMA_RATIOS[value.bit(2) as usize]; + self.sound_b.volume = DMA_RATIOS[value.bit(3) as usize]; + self.sound_a.enable_right = value.bit(8); + self.sound_a.enable_left = value.bit(9); + self.sound_a.timer_select = DMA_TIMERS[value.bit(10) as usize]; + self.sound_b.enable_right = value.bit(12); + self.sound_b.enable_left = value.bit(13); + self.sound_b.timer_select = DMA_TIMERS[value.bit(14) as usize]; + + if value.bit(11) { + self.sound_a.fifo.reset(); + } + if value.bit(15) { + self.sound_b.fifo.reset(); } } @@ -173,6 +227,16 @@ impl SoundController { } } + REG_FIFO_A => { + self.sound_a.fifo.write((value & 0xff00 >> 8) as i8); + self.sound_a.fifo.write((value & 0xff) as i8); + } + + REG_FIFO_B => { + self.sound_b.fifo.write((value & 0xff00 >> 8) as i8); + self.sound_b.fifo.write((value & 0xff) as i8); + } + _ => { println!( "Unimplemented write to {:x} {}", @@ -183,10 +247,23 @@ impl SoundController { } } + pub fn handle_timer_overflow(&mut self, dmac: &mut DmaController, timer_id: usize) { + if !self.mse { + return; + } + // TODO - play sound ? + + if timer_id == self.sound_a.timer_select { + dmac.notify_sound_fifo(REG_FIFO_A); + } + if timer_id == self.sound_b.timer_select { + dmac.notify_sound_fifo(REG_FIFO_B); + } + } + pub fn update(&mut self, cycles: usize) { - if cycles - self.last_sample_cycles >= self.sample_rate_to_cpu_freq { + while cycles - self.last_sample_cycles >= self.sample_rate_to_cpu_freq { self.last_sample_cycles += self.sample_rate_to_cpu_freq; - println!("{:?}", cycles); } } } diff --git a/src/core/timer.rs b/src/core/timer.rs index 0fe4989..c398745 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -19,9 +19,9 @@ pub struct Timer { pub cycles: usize, } -pub enum TimerAction { - Overflow(usize), - Increment, +pub enum TimerEvent { + Overflow(usize, usize), + Increment(usize), } impl Timer { @@ -49,7 +49,7 @@ impl Timer { } } - pub fn add_cycles(&mut self, cycles: usize, irqs: &mut IrqBitmask) -> TimerAction { + pub fn add_cycles(&mut self, cycles: usize, irqs: &mut IrqBitmask) -> TimerEvent { let mut num_overflows = 0; self.cycles += cycles; @@ -66,9 +66,9 @@ impl Timer { } } if num_overflows > 0 { - return TimerAction::Overflow(num_overflows); + return TimerEvent::Overflow(self.timer_id, num_overflows); } else { - return TimerAction::Increment; + return TimerEvent::Increment(self.timer_id); } } } @@ -112,31 +112,37 @@ impl Timers { ); } } -} -impl SyncedIoDevice for Timers { - fn step(&mut self, cycles: usize, _sb: &mut SysBus, irqs: &mut IrqBitmask) { + pub fn tick( + &mut self, + cycles: usize, + sb: &mut SysBus, + irqs: &mut IrqBitmask, + ) -> Option { for i in 0..4 { if self[i].timer_ctl.enabled() && !self[i].timer_ctl.cascade() { - match self[i].add_cycles(cycles, irqs) { - TimerAction::Overflow(num_overflows) => { + let event = self[i].add_cycles(cycles, irqs); + match event { + TimerEvent::Overflow(_, num_overflows) => { if self.trace { println!("TMR{} overflown!", i); } - match i { - 3 => {} - _ => { - let next_i = i + 1; - if self[next_i].timer_ctl.cascade() { - self[next_i].add_cycles(num_overflows, irqs); - } + if i != 3 { + let next_i = i + 1; + if self[next_i].timer_ctl.cascade() { + self[next_i].add_cycles(num_overflows, irqs); } } + if i == 0 || i == 1 { + sb.io.sound.handle_timer_overflow(&mut sb.io.dmac, i); + } } - TimerAction::Increment => {} + _ => {} } + return Some(event); } } + None } }