gb/src/ppu/dma.rs

98 lines
2.3 KiB
Rust

use crate::Cycle;
#[derive(Debug, Default)]
pub(crate) struct DirectMemoryAccess {
pub(crate) state: DmaState,
cycle: Cycle,
/// 0xFF46 | DMA - Transfer and Start Address
pub(crate) start: DmaAddress,
}
impl DirectMemoryAccess {
pub(crate) fn tick(&mut self) -> Option<(u16, u16)> {
match self.state {
DmaState::Pending => {
self.cycle += 1;
// Four Cycles pass before we actually start transferring
// files
if self.cycle == 4 {
self.state = DmaState::Transferring;
}
None
}
DmaState::Transferring => {
self.cycle += 1;
let src_addr = self
.start
.0
.as_mut()
.expect("Source Address present during DMA Transfer");
let addresses = if (self.cycle - 4) % 4 == 0 {
*src_addr += 1;
Some((*src_addr, 0xFE00 | (*src_addr & 0x00FF)))
} else {
None
};
if self.cycle == 644 {
self.reset();
return None;
}
addresses
}
DmaState::Disabled => None,
}
}
pub(crate) fn is_active(&self) -> bool {
self.state == DmaState::Transferring
}
fn reset(&mut self) {
self.cycle = 0;
self.state = DmaState::Disabled;
self.start.0 = None;
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum DmaState {
Disabled,
Pending,
Transferring,
}
impl Default for DmaState {
fn default() -> Self {
Self::Disabled
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct DmaAddress(Option<u16>);
impl DmaAddress {
pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) {
let start = (byte as u16) << 8;
self.0 = Some(start);
*state = DmaState::Pending;
}
}
impl From<DmaAddress> for u8 {
fn from(ctrl: DmaAddress) -> Self {
match ctrl.0 {
Some(addr) => (addr >> 8) as u8,
None => 0xFF, // TODO: What garbage value should be here?
}
}
}