From e8e6c41dbe5b05a5cb12cdcc2022b52559999a0f Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 6 Jun 2021 23:57:54 -0500 Subject: [PATCH] fix(dma): initial version of dma transfer now works --- src/bus.rs | 112 +++++++++++++++++++++++++++++++------------------ src/cpu.rs | 4 +- src/ppu.rs | 2 +- src/ppu/dma.rs | 79 ++++++++++------------------------ 4 files changed, 97 insertions(+), 100 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index 6481c90..63ed2d3 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -79,8 +79,8 @@ impl Bus { for _ in 0..pending_cycles { if let Some((src_addr, dest_addr)) = self.ppu.dma.clock() { - let byte = self.read_byte(src_addr); - self.write_byte(dest_addr, byte); + let byte = self.oam_read_byte(src_addr); + self.oam_write_byte(dest_addr, byte); } } } @@ -90,11 +90,12 @@ impl Bus { } } -impl BusIo for Bus { - fn read_byte(&self, addr: u16) -> u8 { +impl Bus { + pub fn oam_read_byte(&self, addr: u16) -> u8 { match addr { - 0x0000..=0x3FFF => { - // 16KB ROM bank 00 + 0x0000..=0x7FFF => { + // 16KB ROM bank 00 (ends at 0x3FFF) + // and 16KB ROM Bank 01 -> NN (switchable via MB) if addr < 0x100 { if let Some(boot) = self.boot { return boot[addr as usize]; @@ -106,11 +107,56 @@ impl BusIo for Bus { None => panic!("Tried to read from a non-existent cartridge"), } } - 0x4000..=0x7FFF => match self.cartridge.as_ref() { - // 16KB ROM Bank 01 -> NN (switchable via MB) + 0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM + 0xA000..=0xBFFF => match self.cartridge.as_ref() { + // 8KB External RAM Some(cart) => cart.read_byte(addr), None => panic!("Tried to read from a non-existent cartridge"), }, + 0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0 + 0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N + 0xE000..=0xFDFF => { + // Mirror of 0xC000 to 0xDDFF (ECHO RAM) + match addr & 0x1FFF { + // 0xE000 ..= 0xEFFF + 0x0000..=0x0FFF => { + // 4KB Work RAM Bank 0 + self.work_ram.read_byte(addr) + } + // 0xF000 ..= 0xFDFF + 0x1000..=0x1DFF => { + // 4KB Work RAM Bank 1 -> N + self.var_ram.read_byte(addr) + } + _ => unreachable!("{:#06X} was incorrectly handled by ECHO RAM", addr), + } + } + _ => panic!("OAM Transfer abnormally tried reading from {:#06X}", addr), + } + } + + pub fn oam_write_byte(&mut self, addr: u16, byte: u8) { + self.ppu.oam.write_byte(addr, byte); + } +} + +impl BusIo for Bus { + fn read_byte(&self, addr: u16) -> u8 { + match addr { + 0x0000..=0x7FFF => { + // 16KB ROM bank 00 (ends at 0x3FFF) + // and 16KB ROM Bank 01 -> NN (switchable via MB) + if addr < 0x100 { + if let Some(boot) = self.boot { + return boot[addr as usize]; + } + } + + match self.cartridge.as_ref() { + Some(cart) => cart.read_byte(addr), + None => panic!("Tried to read from a non-existent cartridge"), + } + } 0x8000..=0x9FFF => { // 8KB Video RAM match self.ppu.stat.mode() { @@ -123,14 +169,8 @@ impl BusIo for Bus { Some(cart) => cart.read_byte(addr), None => panic!("Tried to read from a non-existent cartridge"), }, - 0xC000..=0xCFFF => { - // 4KB Work RAM Bank 0 - self.work_ram.read_byte(addr) - } - 0xD000..=0xDFFF => { - // 4KB Work RAM Bank 1 -> N - self.var_ram.read_byte(addr) - } + 0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0 + 0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N 0xE000..=0xFDFF => { // Mirror of 0xC000 to 0xDDFF (ECHO RAM) match addr & 0x1FFF { @@ -152,7 +192,7 @@ impl BusIo for Bus { use PpuMode::{HBlank, VBlank}; match self.ppu.stat.mode() { - HBlank | VBlank if !self.ppu.dma.is_active() => self.ppu.oam.read_byte(addr), + HBlank | VBlank => self.ppu.oam.read_byte(addr), _ => 0xFF, } } @@ -214,15 +254,9 @@ impl BusIo for Bus { fn write_byte(&mut self, addr: u16, byte: u8) { match addr { - 0x0000..=0x3FFF => { - // 16KB ROM bank 00 - match self.cartridge.as_mut() { - Some(cart) => cart.write_byte(addr, byte), - None => panic!("Tried to write into non-existent cartridge"), - } - } - 0x4000..=0x7FFF => { - // 16KB ROM Bank 01 -> NN (switchable via MB) + 0x0000..=0x7FFF => { + // 16KB ROM bank 00 (ends at 0x3FFF) + // and 16KB ROM Bank 01 -> NN (switchable via MB) match self.cartridge.as_mut() { Some(cart) => cart.write_byte(addr, byte), None => panic!("Tried to write into non-existent cartridge"), @@ -242,14 +276,8 @@ impl BusIo for Bus { None => panic!("Tried to write into non-existent cartridge"), } } - 0xC000..=0xCFFF => { - // 4KB Work RAM Bank 0 - self.work_ram.write_byte(addr, byte); - } - 0xD000..=0xDFFF => { - // 4KB Work RAM Bank 1 -> N - self.var_ram.write_byte(addr, byte); - } + 0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0 + 0xD000..=0xDFFF => self.var_ram.write_byte(addr, byte), // 4KB Work RAM Bank 1 -> N 0xE000..=0xFDFF => { // Mirror of 0xC000 to 0xDDFF (ECHO RAM) match addr & 0x1FFF { @@ -268,14 +296,16 @@ impl BusIo for Bus { } 0xFE00..=0xFE9F => { // Sprite Attribute Table - use PpuMode::{HBlank, VBlank}; + // use PpuMode::{HBlank, VBlank}; - match self.ppu.stat.mode() { - HBlank | VBlank if !self.ppu.dma.is_active() => { - self.ppu.oam.write_byte(addr, byte) - } - _ => {} - } + // FIXME: There is most definitely something wrong with the + // PPU Timing + // + // match self.ppu.stat.mode() { + // HBlank | VBlank => self.ppu.oam.write_byte(addr, byte), + // _ => {} + // } + self.ppu.oam.write_byte(addr, byte) } 0xFEA0..=0xFEFF => {} // TODO: As far as I know, writes to here do nothing. 0xFF00..=0xFF7F => { diff --git a/src/cpu.rs b/src/cpu.rs index 5c200d4..b3bb54b 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -114,8 +114,6 @@ impl Cpu { // self.log_state(handle).unwrap(); // } - self.handle_interrupts(); - let cycles = match self.halted() { Some(state) => { use HaltState::*; @@ -141,6 +139,8 @@ impl Cpu { self.bus.step(cycles); self.bus.step_dma(cycles); + self.handle_interrupts(); + cycles } } diff --git a/src/ppu.rs b/src/ppu.rs index 1e63ed3..8520d41 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -189,7 +189,7 @@ impl Ppu { } fn scan_oam(&mut self) { - if self.scan_state.mode() == OamScanMode::Scan && self.dma.is_active() { + if self.scan_state.mode() == OamScanMode::Scan { if !self.window_stat.coincidence() && self.scan_state.count() == 0 { // Determine whether we should draw the window next frame self.window_stat diff --git a/src/ppu/dma.rs b/src/ppu/dma.rs index 3eabbb4..355bdb8 100644 --- a/src/ppu/dma.rs +++ b/src/ppu/dma.rs @@ -1,5 +1,4 @@ use crate::instruction::Cycle; -use std::ops::Range; #[derive(Debug, Default, Clone)] pub(crate) struct DmaProcess { @@ -10,8 +9,6 @@ pub(crate) struct DmaProcess { impl DmaProcess { pub(crate) fn clock(&mut self) -> Option<(u16, u16)> { - self.cycle += 1; - match self.state { DmaState::Pending => { self.cycle += 1; @@ -26,20 +23,28 @@ impl DmaProcess { None } DmaState::Transferring => { - if (self.cycle - 4) % 4 == 0 { - let i = u32::from((self.cycle - 4) / 4) as usize; - let dest = &mut self.ctrl.dest; + self.cycle += 1; - match self.ctrl.src.as_mut() { - Some(src_range) => src_range.nth(i).zip(dest.nth(i)), - None => { - self.reset(); - None - } - } + let src_addr = self + .ctrl + .src_addr + .as_mut() + .expect("DMA Transfer Attempted without a known source address"); + + 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, } @@ -52,8 +57,7 @@ impl DmaProcess { fn reset(&mut self) { self.cycle = Cycle::new(0); self.state = DmaState::Disabled; - self.ctrl.src = None; - self.ctrl.repr = 0; + self.ctrl.src_addr = None; } } @@ -73,61 +77,24 @@ impl Default for DmaState { #[derive(Debug, Clone)] pub(crate) struct DmaControl { pub(crate) repr: u8, - src: Option>, - dest: Range, + src_addr: Option, } impl Default for DmaControl { fn default() -> Self { Self { repr: 0, - src: None, - dest: 0xFE00..0xFE9F, + src_addr: None, } } } impl DmaControl { pub(crate) fn update(&mut self, byte: u8, state: &mut DmaState) { - let left = (byte as u16) << 8; - let right = (byte as u16) << 8 | 0x009F; + let start = (byte as u16) << 8; self.repr = byte; - self.src = Some(left..right); + self.src_addr = Some(start); *state = DmaState::Pending; } } - -#[cfg(test)] -mod tests { - use super::{DmaControl, DmaProcess, DmaState}; - - #[derive(Debug, Default, Clone)] - struct MockBus { - dma: DmaProcess, - } - - #[test] - fn dma_control_works() { - let mut dma_ctrl: DmaControl = Default::default(); - let mut state = DmaState::Disabled; - - assert_eq!(dma_ctrl.src, None); - assert_eq!(dma_ctrl.dest, 0xFE00..0xFE9F); - - dma_ctrl.update(0xAB, &mut state); - assert_eq!(dma_ctrl.src, Some(0xAB00..0xAB9F)); - assert_eq!(dma_ctrl.dest, 0xFE00..0xFE9F); - } - - #[test] - fn ctrl_update_vs_borrow_checker() { - let mut bus: MockBus = Default::default(); - assert_eq!(bus.dma.state, DmaState::Disabled); - - bus.dma.ctrl.update(0xAB, &mut bus.dma.state); - - assert_eq!(bus.dma.ctrl.src, Some(0xAB00..0xAB9F)); - assert_eq!(bus.dma.state, DmaState::Pending); - } -}