diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index f4459e9..3f477e6 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -7,7 +7,7 @@ use self::{ apu::ApuSaveState, gpu::{Colour, GpuSaveState}, serial::SerialSaveState, - Apu, Gpu, Joypad, OamDma, Serial, Timer, + Apu, Gpu, Joypad, OamDma, Serial, Timer, VramDma, }, rom::RomSaveState, }; @@ -94,7 +94,8 @@ where pub(super) ime: bool, pub(super) ime_scheduled: u8, pub(super) user_mode: bool, - dma: OamDma, + oam_dma: OamDma, + vram_dma: Option, joypad: Joypad, gpu: Gpu, apu: Apu, @@ -119,7 +120,8 @@ where pub(super) ime: bool, pub(super) ime_scheduled: u8, pub(super) user_mode: bool, - dma: OamDma, + oam_dma: OamDma, + vram_dma: Option, joypad: Joypad, gpu: GpuSaveState, apu: ApuSaveState, @@ -143,7 +145,8 @@ where ime: memory.ime, ime_scheduled: memory.ime_scheduled, user_mode: memory.user_mode, - dma: memory.dma, + oam_dma: memory.oam_dma, + vram_dma: memory.vram_dma, joypad: memory.joypad, gpu: GpuSaveState::create(&memory.gpu), apu: ApuSaveState::create(&memory.apu), @@ -213,7 +216,8 @@ where ime: false, ime_scheduled: 0x0, user_mode: false, - dma: OamDma::default(), + oam_dma: OamDma::default(), + vram_dma: if cgb { Some(VramDma::default()) } else { None }, joypad: Joypad::default(), gpu: Gpu::new(cgb, output.window, output.tile_window), apu: Apu::new(output.audio), @@ -229,7 +233,7 @@ where T: Into
, { let address: Address = address.into(); - if self.dma.is_active() && self.user_mode { + if self.oam_dma.is_active() && self.user_mode { if let Address::Hram(_) = address { } else if let Address::Io(IoAddress::Video(v)) = address { if v.inner() == 0xFF46 { @@ -276,7 +280,7 @@ where T: Into
, { let address: Address = address.into(); - if self.dma.is_active() && self.user_mode { + if self.oam_dma.is_active() && self.user_mode { if let Address::Hram(_) = address { } else if let Address::Io(IoAddress::Video(v)) = address { if v.inner() == 0xFF46 { @@ -340,7 +344,7 @@ where 0xFF43 => self.gpu.get_scx(), 0xFF44 => self.gpu.get_ly(), 0xFF45 => self.gpu.get_lyc(), - 0xFF46 => self.dma.get_register(), + 0xFF46 => self.oam_dma.get_register(), 0xFF47 => self.gpu.get_bg_palette(), 0xFF48 => self.gpu.get_obj_palette_0(), 0xFF49 => self.gpu.get_obj_palette_1(), @@ -349,11 +353,11 @@ where 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), }, IoAddress::Cgb(address) => { - if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks { + if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks && let Some(vram_dma) = &self.vram_dma { match address { CgbIoAddress::PrepareSpeed => todo!(), CgbIoAddress::VramBank => self.gpu.vram.get_vram_bank(), - CgbIoAddress::VramDma(_) => todo!(), + CgbIoAddress::VramDma(address) => vram_dma.get_register(address), CgbIoAddress::Infrared => todo!(), CgbIoAddress::Palette(address) => self.gpu.get_cgb_palette(address), CgbIoAddress::ObjPriority => self.gpu.get_obj_priority(), @@ -399,9 +403,7 @@ where 0xFF43 => self.gpu.update_scx(data), 0xFF44 => {} 0xFF45 => self.gpu.update_lyc(data), - 0xFF46 => { - self.dma.set_register(data); - } + 0xFF46 => self.oam_dma.set_register(data), 0xFF47 => self.gpu.update_bg_palette(data), 0xFF48 => self.gpu.update_obj_palette_0(data), 0xFF49 => self.gpu.update_obj_palette_1(data), @@ -410,11 +412,11 @@ where 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), }, IoAddress::Cgb(address) => { - if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks { + if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks && let Some(vram_dma) = &mut self.vram_dma { match address { CgbIoAddress::PrepareSpeed => todo!(), CgbIoAddress::VramBank => self.gpu.vram.set_vram_bank(data), - CgbIoAddress::VramDma(_) => todo!(), + CgbIoAddress::VramDma(address) => vram_dma.set_register(address, data), CgbIoAddress::Infrared => todo!(), CgbIoAddress::Palette(address) => self.gpu.set_cgb_palette(address, data), CgbIoAddress::ObjPriority => self.gpu.set_obj_priority(data), @@ -422,8 +424,7 @@ where CgbIoAddress::Pcm12 => todo!(), CgbIoAddress::Pcm34 => todo!(), CgbIoAddress::Unused(v) => { - println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}"); - todo!() + println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}") } } } @@ -491,7 +492,8 @@ where ime: state.ime, ime_scheduled: state.ime_scheduled, user_mode: state.user_mode, - dma: state.dma, + oam_dma: state.oam_dma, + vram_dma: state.vram_dma, joypad: state.joypad, gpu: Gpu::from_save_state(state.gpu, window, None), apu: Apu::from_save_state(state.apu, output), @@ -513,7 +515,7 @@ where let steps = (machine_cycles as usize) * 4; self.cycle_count += steps; - self.memory.oam_tick(steps); + self.memory.oam_dma_tick(steps); self.memory.camera.lock().unwrap().tick(steps); diff --git a/lib/src/processor/memory/mmio/gpu.rs b/lib/src/processor/memory/mmio/gpu.rs index eea823a..24ef072 100644 --- a/lib/src/processor/memory/mmio/gpu.rs +++ b/lib/src/processor/memory/mmio/gpu.rs @@ -1,11 +1,12 @@ use std::marker::PhantomData; +pub(crate) use self::types::DrawMode; use self::{ cgb::CgbData, tile_window::TileWindow, types::{ - DrawMode, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, - Stat, TiledataArea, TilemapArea, Vram, + GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, Stat, + TiledataArea, TilemapArea, Vram, }, }; use crate::{ @@ -197,6 +198,10 @@ where } } + pub(crate) fn get_mode(&self) -> DrawMode { + self.stat.mode + } + pub fn tick(&mut self, steps: usize) -> GpuInterrupts { let mut interrupts = GpuInterrupts::default(); if self.lcdc.enable { diff --git a/lib/src/processor/memory/mmio/gpu/types.rs b/lib/src/processor/memory/mmio/gpu/types.rs index 9f743bb..cfca622 100644 --- a/lib/src/processor/memory/mmio/gpu/types.rs +++ b/lib/src/processor/memory/mmio/gpu/types.rs @@ -6,7 +6,7 @@ use crate::{ }; #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] -pub(super) enum DrawMode { +pub(crate) enum DrawMode { HBlank, VBlank, Mode2, diff --git a/lib/src/processor/memory/mmio/mod.rs b/lib/src/processor/memory/mmio/mod.rs index 122238e..d4500bd 100644 --- a/lib/src/processor/memory/mmio/mod.rs +++ b/lib/src/processor/memory/mmio/mod.rs @@ -4,9 +4,11 @@ pub(crate) mod joypad; mod oam_dma; pub(crate) mod serial; mod timer; +mod vram_dma; pub use apu::Apu; pub use gpu::Gpu; pub use joypad::Joypad; pub use oam_dma::OamDma; pub use serial::Serial; pub use timer::Timer; +pub use vram_dma::VramDma; diff --git a/lib/src/processor/memory/mmio/oam_dma.rs b/lib/src/processor/memory/mmio/oam_dma.rs index 3dfa538..61797d1 100644 --- a/lib/src/processor/memory/mmio/oam_dma.rs +++ b/lib/src/processor/memory/mmio/oam_dma.rs @@ -41,13 +41,13 @@ where R: Renderer, C: PocketCamera + Send + 'static, { - pub(crate) fn oam_tick(&mut self, steps: usize) { + pub(crate) fn oam_dma_tick(&mut self, steps: usize) { for _ in 0..steps { - self.dma.progress = if let Some(mut progress) = self.dma.progress { + self.oam_dma.progress = if let Some(mut progress) = self.oam_dma.progress { let mut addr: u16 = 0x0; - addr.set_high(self.dma.addr); + addr.set_high(self.oam_dma.addr); addr.set_low(progress); - let val = if self.dma.addr > 0xDF { + let val = if self.oam_dma.addr > 0xDF { 0xFF } else { self.get(addr) diff --git a/lib/src/processor/memory/mmio/vram_dma.rs b/lib/src/processor/memory/mmio/vram_dma.rs new file mode 100644 index 0000000..b46a1e2 --- /dev/null +++ b/lib/src/processor/memory/mmio/vram_dma.rs @@ -0,0 +1,125 @@ +use super::gpu::{Colour, DrawMode}; +use crate::{ + connect::{PocketCamera, Renderer}, + processor::{ + memory::{ + addresses::{AddressMarker, VramDmaAddress}, + Memory, + }, + SplitRegister, + }, + util::get_bit, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Default, Serialize, Deserialize, Clone, Copy)] +pub struct VramDma { + source: u16, + destination: u16, + mode: DmaMode, +} + +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug)] +enum DmaMode { + Halt(u8), + Hblank(u8, u16), + Waiting, +} + +impl DmaMode { + fn get_byte(&self) -> u8 { + match self { + DmaMode::Halt(_) => unreachable!(), + DmaMode::Hblank(v, _) => *v, + DmaMode::Waiting => 0xFF, + } + } +} + +impl Default for DmaMode { + fn default() -> Self { + Self::Waiting + } +} + +impl VramDma { + pub(crate) fn get_register(&self, address: VramDmaAddress) -> u8 { + match address.inner() { + 0xFF51 => self.source.get_high(), + 0xFF52 => self.source.get_low(), + 0xFF53 => self.destination.get_high(), + 0xFF54 => self.destination.get_low(), + 0xFF55 => self.mode.get_byte(), + _ => unreachable!(), + } + } + + pub(crate) fn set_register(&mut self, address: VramDmaAddress, data: u8) { + match address.inner() { + 0xFF51 => self.source.set_high(data), + 0xFF52 => self.source.set_low(data & 0xF0), + 0xFF53 => self.destination.set_high((data & 0b11111) | (0b1 << 7)), + 0xFF54 => self.destination.set_low(data & 0xF0), + 0xFF55 => { + let num = data & !(0b1 << 7); + self.mode = if get_bit(data, 7) { + DmaMode::Hblank(num, 0) + } else if self.mode == DmaMode::Waiting { + DmaMode::Halt(num) + } else { + DmaMode::Waiting + }; + println!("vram dma - entered mode {:?}", self.mode); + println!( + " for transfer from 0x{:0>4X} to 0x{:0>4X}", + self.source, self.destination + ); + println!(" num bytes/lines: 0x{num:X}"); + } + _ => unreachable!(), + } + } +} + +impl Memory +where + ColourFormat: From + Clone, + R: Renderer, + C: PocketCamera + Send + 'static, +{ + pub(crate) fn vram_dma_tick(&mut self) -> usize { + let mut copy = None; + let returning = if let Some(vram_dma) = &mut self.vram_dma { + match vram_dma.mode { + DmaMode::Halt(l) => { + copy = Some((vram_dma.destination, vram_dma.source, 16 * ((l as u16) + 1))); + vram_dma.mode = DmaMode::Waiting; + ((l as usize) + 1) * 8 + } + DmaMode::Hblank(l, ref mut progress) => { + if self.gpu.get_mode() == DrawMode::HBlank { + let offset = *progress * 16; + copy = Some((vram_dma.destination + offset, vram_dma.source + offset, 16)); + *progress += 1; + if *progress > (l as u16) { + vram_dma.mode = DmaMode::Waiting + } + 8 + } else { + 0 + } + } + DmaMode::Waiting => 0, + } + } else { + 0 + }; + if let Some((source, dest, length)) = copy { + println!("copying 0x{length:X} bytes from {source:0>4X} to {dest:0>4X}"); + for i in 0..length { + self.set(dest + i, self.get(source + i)); + } + } + returning + } +} diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index ef45b1d..e5d0321 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -97,6 +97,20 @@ where return; } + // double this if in double speed mode + let mut vram_dma_cycles = self.memory.vram_dma_tick(); + while vram_dma_cycles > 0 { + let cycles = if vram_dma_cycles > 0xFF { + 0xFF + } else { + vram_dma_cycles as u8 + }; + vram_dma_cycles -= cycles as usize; + self.increment_timers(cycles); + let interrupt_cycles = self.handle_interrupts(); + self.increment_timers(interrupt_cycles); + } + if self.memory.ime_scheduled > 0 { self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1); if self.memory.ime_scheduled == 0 {