Start working on DMA sound
Former-commit-id: 066210a86a7836b6ae1dfd5ce229d050cbe00ca4
This commit is contained in:
parent
38e504515b
commit
70cb99161d
|
@ -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! {
|
||||
|
|
|
@ -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
51
src/core/sound/fifo.rs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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<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?
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 => {}
|
||||
_ => {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue