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? } } }