core: dma: Delay DMA by 3 cycles
Former-commit-id: 06426b01a3e9e9084c97cd8f3a3de3b3c2b207e6 Former-commit-id: 2dee7bc2a2a3a3e69c7afa878f2d03208f330752
This commit is contained in:
parent
0de8a60006
commit
97101d7bc1
|
@ -1,6 +1,7 @@
|
||||||
use super::cartridge::BackupMedia;
|
use super::cartridge::BackupMedia;
|
||||||
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
|
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
|
||||||
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
|
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
|
||||||
|
use super::sched::{EventType, Scheduler, SharedScheduler};
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
use super::Bus;
|
use super::Bus;
|
||||||
|
|
||||||
|
@ -20,8 +21,6 @@ pub struct DmaChannel {
|
||||||
internal: DmaInternalRegs,
|
internal: DmaInternalRegs,
|
||||||
|
|
||||||
running: bool,
|
running: bool,
|
||||||
cycles: usize,
|
|
||||||
start_cycles: usize,
|
|
||||||
fifo_mode: bool,
|
fifo_mode: bool,
|
||||||
irq: Interrupt,
|
irq: Interrupt,
|
||||||
interrupt_flags: SharedInterruptFlags,
|
interrupt_flags: SharedInterruptFlags,
|
||||||
|
@ -47,8 +46,7 @@ impl DmaChannel {
|
||||||
dst: 0,
|
dst: 0,
|
||||||
wc: 0,
|
wc: 0,
|
||||||
ctrl: DmaChannelCtrl(0),
|
ctrl: DmaChannelCtrl(0),
|
||||||
cycles: 0,
|
|
||||||
start_cycles: 0,
|
|
||||||
fifo_mode: false,
|
fifo_mode: false,
|
||||||
internal: Default::default(),
|
internal: Default::default(),
|
||||||
interrupt_flags,
|
interrupt_flags,
|
||||||
|
@ -98,7 +96,6 @@ impl DmaChannel {
|
||||||
self.dst,
|
self.dst,
|
||||||
self.wc
|
self.wc
|
||||||
);
|
);
|
||||||
self.start_cycles = self.cycles;
|
|
||||||
self.running = true;
|
self.running = true;
|
||||||
start_immediately = timing == 0;
|
start_immediately = timing == 0;
|
||||||
self.internal.src_addr = self.src;
|
self.internal.src_addr = self.src;
|
||||||
|
@ -177,7 +174,6 @@ impl DmaChannel {
|
||||||
interrupt::signal_irq(&self.interrupt_flags, self.irq);
|
interrupt::signal_irq(&self.interrupt_flags, self.irq);
|
||||||
}
|
}
|
||||||
if self.ctrl.repeat() {
|
if self.ctrl.repeat() {
|
||||||
self.start_cycles = self.cycles;
|
|
||||||
/* reload */
|
/* reload */
|
||||||
if 3 == self.ctrl.dst_adj() {
|
if 3 == self.ctrl.dst_adj() {
|
||||||
self.internal.dst_addr = self.dst;
|
self.internal.dst_addr = self.dst;
|
||||||
|
@ -193,7 +189,9 @@ impl DmaChannel {
|
||||||
pub struct DmaController {
|
pub struct DmaController {
|
||||||
pub channels: [DmaChannel; 4],
|
pub channels: [DmaChannel; 4],
|
||||||
pending_set: u8,
|
pending_set: u8,
|
||||||
cycles: usize,
|
#[serde(skip)]
|
||||||
|
#[serde(default = "Scheduler::new_shared")]
|
||||||
|
scheduler: SharedScheduler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptConnect for DmaController {
|
impl InterruptConnect for DmaController {
|
||||||
|
@ -205,7 +203,7 @@ impl InterruptConnect for DmaController {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DmaController {
|
impl DmaController {
|
||||||
pub fn new(interrupt_flags: SharedInterruptFlags) -> DmaController {
|
pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> DmaController {
|
||||||
DmaController {
|
DmaController {
|
||||||
channels: [
|
channels: [
|
||||||
DmaChannel::new(0, interrupt_flags.clone()),
|
DmaChannel::new(0, interrupt_flags.clone()),
|
||||||
|
@ -214,7 +212,7 @@ impl DmaController {
|
||||||
DmaChannel::new(3, interrupt_flags.clone()),
|
DmaChannel::new(3, interrupt_flags.clone()),
|
||||||
],
|
],
|
||||||
pending_set: 0,
|
pending_set: 0,
|
||||||
cycles: 0,
|
scheduler: scheduler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,9 +238,11 @@ 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.pending_set |= 1 << channel_id;
|
// DMA actually starts after 3 cycles
|
||||||
|
self.scheduler
|
||||||
|
.schedule(EventType::DmaActivateChannel(channel_id), 3);
|
||||||
} else {
|
} else {
|
||||||
self.pending_set &= !(1 << channel_id);
|
self.deactivate_channel(channel_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid dma offset {:x}", ofs),
|
_ => 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;
|
pub const TIMING_VBLANK: u16 = 1;
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl GameBoyAdvance {
|
||||||
|
|
||||||
let intc = InterruptController::new(interrupt_flags.clone());
|
let intc = InterruptController::new(interrupt_flags.clone());
|
||||||
let gpu = Box::new(Gpu::new(scheduler.clone(), 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 timers = Timers::new(interrupt_flags.clone());
|
||||||
let sound_controller = Box::new(SoundController::new(
|
let sound_controller = Box::new(SoundController::new(
|
||||||
scheduler.clone(),
|
scheduler.clone(),
|
||||||
|
@ -329,6 +329,7 @@ impl EventHandler for GameBoyAdvance {
|
||||||
&mut (*ptr).io as &mut IoDevices
|
&mut (*ptr).io as &mut IoDevices
|
||||||
};
|
};
|
||||||
match event {
|
match event {
|
||||||
|
EventType::DmaActivateChannel(channel_id) => io.dmac.activate_channel(channel_id),
|
||||||
EventType::Gpu(event) => io.gpu.on_event(
|
EventType::Gpu(event) => io.gpu.on_event(
|
||||||
event,
|
event,
|
||||||
extra_cycles,
|
extra_cycles,
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub enum ApuEvent {
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
Gpu(GpuEvent),
|
Gpu(GpuEvent),
|
||||||
Apu(ApuEvent),
|
Apu(ApuEvent),
|
||||||
|
DmaActivateChannel(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
Reference in a new issue