core: dma: Delay DMA by 3 cycles

Former-commit-id: 06426b01a3e9e9084c97cd8f3a3de3b3c2b207e6
Former-commit-id: 2dee7bc2a2a3a3e69c7afa878f2d03208f330752
This commit is contained in:
Michel Heily 2020-10-10 10:57:00 -07:00 committed by MishMish
parent 0de8a60006
commit 97101d7bc1
3 changed files with 22 additions and 12 deletions

View file

@ -1,6 +1,7 @@
use super::cartridge::BackupMedia;
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
use super::sched::{EventType, Scheduler, SharedScheduler};
use super::sysbus::SysBus;
use super::Bus;
@ -20,8 +21,6 @@ pub struct DmaChannel {
internal: DmaInternalRegs,
running: bool,
cycles: usize,
start_cycles: usize,
fifo_mode: bool,
irq: Interrupt,
interrupt_flags: SharedInterruptFlags,
@ -47,8 +46,7 @@ impl DmaChannel {
dst: 0,
wc: 0,
ctrl: DmaChannelCtrl(0),
cycles: 0,
start_cycles: 0,
fifo_mode: false,
internal: Default::default(),
interrupt_flags,
@ -98,7 +96,6 @@ impl DmaChannel {
self.dst,
self.wc
);
self.start_cycles = self.cycles;
self.running = true;
start_immediately = timing == 0;
self.internal.src_addr = self.src;
@ -177,7 +174,6 @@ impl DmaChannel {
interrupt::signal_irq(&self.interrupt_flags, self.irq);
}
if self.ctrl.repeat() {
self.start_cycles = self.cycles;
/* reload */
if 3 == self.ctrl.dst_adj() {
self.internal.dst_addr = self.dst;
@ -193,7 +189,9 @@ impl DmaChannel {
pub struct DmaController {
pub channels: [DmaChannel; 4],
pending_set: u8,
cycles: usize,
#[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
}
impl InterruptConnect for DmaController {
@ -205,7 +203,7 @@ impl InterruptConnect for DmaController {
}
impl DmaController {
pub fn new(interrupt_flags: SharedInterruptFlags) -> DmaController {
pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> DmaController {
DmaController {
channels: [
DmaChannel::new(0, interrupt_flags.clone()),
@ -214,7 +212,7 @@ impl DmaController {
DmaChannel::new(3, interrupt_flags.clone()),
],
pending_set: 0,
cycles: 0,
scheduler: scheduler,
}
}
@ -240,9 +238,11 @@ impl DmaController {
8 => self.channels[channel_id].write_word_count(value),
10 => {
if self.channels[channel_id].write_dma_ctrl(value) {
self.pending_set |= 1 << channel_id;
// DMA actually starts after 3 cycles
self.scheduler
.schedule(EventType::DmaActivateChannel(channel_id), 3);
} else {
self.pending_set &= !(1 << channel_id);
self.deactivate_channel(channel_id);
}
}
_ => panic!("Invalid dma offset {:x}", ofs),
@ -268,6 +268,14 @@ impl DmaController {
}
}
}
pub fn activate_channel(&mut self, channel_id: usize) {
self.pending_set |= 1 << channel_id;
}
pub fn deactivate_channel(&mut self, channel_id: usize) {
self.pending_set &= !(1 << channel_id);
}
}
pub const TIMING_VBLANK: u16 = 1;

View file

@ -77,7 +77,7 @@ impl GameBoyAdvance {
let intc = InterruptController::new(interrupt_flags.clone());
let gpu = Box::new(Gpu::new(scheduler.clone(), interrupt_flags.clone()));
let dmac = DmaController::new(interrupt_flags.clone());
let dmac = DmaController::new(interrupt_flags.clone(), scheduler.clone());
let timers = Timers::new(interrupt_flags.clone());
let sound_controller = Box::new(SoundController::new(
scheduler.clone(),
@ -329,6 +329,7 @@ impl EventHandler for GameBoyAdvance {
&mut (*ptr).io as &mut IoDevices
};
match event {
EventType::DmaActivateChannel(channel_id) => io.dmac.activate_channel(channel_id),
EventType::Gpu(event) => io.gpu.on_event(
event,
extra_cycles,

View file

@ -29,6 +29,7 @@ pub enum ApuEvent {
pub enum EventType {
Gpu(GpuEvent),
Apu(ApuEvent),
DmaActivateChannel(usize),
}
#[derive(Serialize, Deserialize, Debug, Clone)]