Start working on DMA sound

Former-commit-id: 066210a86a7836b6ae1dfd5ce229d050cbe00ca4
This commit is contained in:
Michel Heily 2019-12-20 19:18:16 +02:00
parent 38e504515b
commit 70cb99161d
5 changed files with 225 additions and 57 deletions

View file

@ -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,36 +90,23 @@ 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;
}
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 {
0 => match self.id {
3 => 0x1_0000,
_ => 0x0_4000,
},
_ => self.internal.count,
};
for _ in 0..count {
if word_size == 4 {
let w = sb.read_32(self.internal.src_addr);
sb.write_32(self.internal.dst_addr, w)
} else {
let hw = sb.read_16(self.internal.src_addr);
sb.write_16(self.internal.dst_addr, hw)
}
#[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,
@ -131,6 +120,38 @@ impl DmaChannel {
_ => 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 {
0 => match self.id {
3 => 0x1_0000,
_ => 0x0_4000,
},
_ => self.internal.count,
};
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);
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);
self.xfer_adj_addrs(word_size)
}
}
if self.ctrl.is_triggering_irq() {
irqs.add_irq(self.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! {

View file

@ -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 => {

51
src/core/sound/fifo.rs Normal file
View file

@ -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;
}
}

View file

@ -1,11 +1,41 @@
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<i8>,
}
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?
@ -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
REG_SOUNDCNT_H => {
DMG_RATIOS
.iter()
.position(|&f| f == self.dmg_volume_ratio)
.expect("bad dmg_volume_ratio!") as u16,
.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);
}
}
}

View file

@ -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<TimerEvent> {
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 => {}
_ => {
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
}
}