Improve DMA code

Former-commit-id: 8fdb6195ceb323aebd8a26da98fe286d89ef8363
This commit is contained in:
Michel Heily 2019-12-27 12:37:32 +02:00
parent b026ad4ed3
commit c2ac3c5a10
3 changed files with 32 additions and 22 deletions

View file

@ -25,6 +25,7 @@ zip = "0.5.3"
ctrlc = "3.1.3" ctrlc = "3.1.3"
cpal="0.10.0" cpal="0.10.0"
spin_sleep="0.3.7" spin_sleep="0.3.7"
bit-set = "0.5.1"
[[bin]] [[bin]]
name = "rba-sdl2" name = "rba-sdl2"

View file

@ -1,4 +1,6 @@
use std::collections::VecDeque; extern crate bit_set;
use bit_set::BitSet;
use super::arm7tdmi::{Addr, Bus}; use super::arm7tdmi::{Addr, Bus};
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B}; use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
@ -105,6 +107,9 @@ impl DmaChannel {
&& (self.id == 1 || self.id == 2) && (self.id == 1 || self.id == 2)
&& (self.dst == REG_FIFO_A || self.dst == REG_FIFO_B); && (self.dst == REG_FIFO_A || self.dst == REG_FIFO_B);
} }
if !ctrl.is_enabled() {
self.running = false;
}
self.ctrl = ctrl; self.ctrl = ctrl;
return start_immediately; return start_immediately;
} }
@ -138,20 +143,20 @@ impl DmaChannel {
if fifo_mode { if fifo_mode {
for _ in 0..4 { for _ in 0..4 {
let v = sb.read_32(self.internal.src_addr); let v = sb.read_32(self.internal.src_addr & !3);
sb.write_32(self.internal.dst_addr, v); sb.write_32(self.internal.dst_addr & !3, v);
self.internal.src_addr += 4; self.internal.src_addr += 4;
} }
} else if word_size == 4 { } else if word_size == 4 {
for _ in 0..count { for _ in 0..count {
let w = sb.read_32(self.internal.src_addr); let w = sb.read_32(self.internal.src_addr & !3);
sb.write_32(self.internal.dst_addr, w); sb.write_32(self.internal.dst_addr & !3, w);
self.xfer_adj_addrs(word_size); self.xfer_adj_addrs(word_size);
} }
} else { } else {
for _ in 0..count { for _ in 0..count {
let hw = sb.read_16(self.internal.src_addr); let hw = sb.read_16(self.internal.src_addr & !1);
sb.write_16(self.internal.dst_addr, hw); sb.write_16(self.internal.dst_addr & !1, hw);
self.xfer_adj_addrs(word_size) self.xfer_adj_addrs(word_size)
} }
} }
@ -174,7 +179,7 @@ impl DmaChannel {
#[derive(Debug)] #[derive(Debug)]
pub struct DmaController { pub struct DmaController {
pub channels: [DmaChannel; 4], pub channels: [DmaChannel; 4],
xfers_queue: VecDeque<usize>, pending_bittset: BitSet,
cycles: usize, cycles: usize,
} }
@ -187,20 +192,20 @@ impl DmaController {
DmaChannel::new(2), DmaChannel::new(2),
DmaChannel::new(3), DmaChannel::new(3),
], ],
xfers_queue: VecDeque::new(), pending_bittset: BitSet::with_capacity(4),
cycles: 0, cycles: 0,
} }
} }
pub fn perform_work(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) -> bool { pub fn has_work(&self) -> bool {
if self.xfers_queue.is_empty() { !self.pending_bittset.is_empty()
false
} else {
while let Some(id) = self.xfers_queue.pop_front() {
self.channels[id].xfer(sb, irqs)
} }
true
pub fn perform_work(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) {
for id in self.pending_bittset.iter() {
self.channels[id].xfer(sb, irqs);
} }
self.pending_bittset.clear();
} }
pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) { pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) {
@ -212,7 +217,9 @@ impl DmaController {
8 => self.channels[channel_id].write_word_count(value), 8 => self.channels[channel_id].write_word_count(value),
10 => { 10 => {
if self.channels[channel_id].write_dma_ctrl(value) { if self.channels[channel_id].write_dma_ctrl(value) {
self.xfers_queue.push_back(channel_id) self.pending_bittset.insert(channel_id);
} else {
self.pending_bittset.remove(channel_id);
} }
} }
_ => panic!("Invalid dma offset"), _ => panic!("Invalid dma offset"),
@ -222,7 +229,7 @@ impl DmaController {
pub fn notify_vblank(&mut self) { pub fn notify_vblank(&mut self) {
for i in 0..4 { for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 1 { if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 1 {
self.xfers_queue.push_back(i); self.pending_bittset.insert(i);
} }
} }
} }
@ -230,7 +237,7 @@ impl DmaController {
pub fn notify_hblank(&mut self) { pub fn notify_hblank(&mut self) {
for i in 0..4 { for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 2 { if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 2 {
self.xfers_queue.push_back(i); self.pending_bittset.insert(i);
} }
} }
} }
@ -238,10 +245,11 @@ impl DmaController {
pub fn notify_sound_fifo(&mut self, fifo_addr: u32) { pub fn notify_sound_fifo(&mut self, fifo_addr: u32) {
for i in 1..=2 { for i in 1..=2 {
if self.channels[i].ctrl.is_enabled() if self.channels[i].ctrl.is_enabled()
&& self.channels[i].running
&& self.channels[i].ctrl.timing() == 3 && self.channels[i].ctrl.timing() == 3
&& self.channels[i].dst == fifo_addr && self.channels[i].dst == fifo_addr
{ {
self.xfers_queue.push_back(i); self.pending_bittset.insert(i);
} }
} }
} }

View file

@ -89,7 +89,7 @@ impl GameBoyAdvance {
&mut (*ptr).io as &mut IoDevices &mut (*ptr).io as &mut IoDevices
}; };
let cycles = if !io.dmac.perform_work(&mut self.sysbus, &mut irqs) { let cycles = if !io.dmac.has_work() {
if io.intc.irq_pending() if io.intc.irq_pending()
&& self.cpu.last_executed.is_some() && self.cpu.last_executed.is_some()
&& !self.cpu.did_pipeline_flush() && !self.cpu.did_pipeline_flush()
@ -105,6 +105,7 @@ impl GameBoyAdvance {
1 1
} }
} else { } else {
io.dmac.perform_work(&mut self.sysbus, &mut irqs);
0 0
}; };