From 2ca181393943af26b59b5f1f5dac16634f29a738 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sun, 2 Jan 2022 21:09:48 -0500 Subject: [PATCH] basic HDMA implementation --- src/bus.rs | 37 +++++++++++++++++++++++++++++-------- src/ppu.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index ca4d23c..8f15e19 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -5,6 +5,7 @@ use crate::ram::{RAM, DMGRAM, CGBRAM, WRAM_BANK_SELECT_ADDRESS}; use crate::ppu::{ PPU, DMA_ADDRESS, + HDMA5_ADDRESS, }; use crate::timer::Timer; use crate::joypad::{Joypad, JOYPAD_ADDRESS}; @@ -153,14 +154,11 @@ impl Bus { } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) { return self.ppu.write_oam(address, data); } else if address == DMA_ADDRESS { - self.data[address as usize] = data; - let source = (data as u16) * 0x100; - let mut count: u16 = 0; - let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); - while count < 160 { - self.ppu.write_oam(oam_addr + count, self.read(source + count)); - count += 1; - } + self.ppu.set_register(address, data); + self.dma_transfer(data); + } else if address == HDMA5_ADDRESS { + self.ppu.set_register(address, data); + self.hdma_transfer(data); } else if PPU::is_io_register(address) { self.ppu.set_register(address, data); } else { @@ -173,4 +171,27 @@ impl Bus { self.write(address, bytes[0]); self.write(address.wrapping_add(1), bytes[1]); } + + fn dma_transfer(&mut self, data: u8) { + let source = (data as u16) * 0x100; + let mut count: u16 = 0; + let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); + while count < 160 { + self.ppu.write_oam(oam_addr + count, self.read(source + count)); + count += 1; + } + } + + fn hdma_transfer(&mut self, data: u8) { + let source = self.ppu.hdma_source() & 0xFFF0; + let destination = (self.ppu.hdma_destination() & 0xFF0) + 0x8000; + let length = (((data & 0x7F) as u16) + 1) * 0x10; + let mut count: u16 = 0; + while count < length { + let byte = self.read(source + count); + self.ppu.write_vram_external(destination + count, byte); + count += 1; + } + self.ppu.set_register(HDMA5_ADDRESS, 0xFF); + } } diff --git a/src/ppu.rs b/src/ppu.rs index aaba5d7..8976b2f 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -29,6 +29,12 @@ pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A; pub const WINDOW_X_ADDRESS: u16 = 0xFF4B; pub const VRAM_BANK_SELECT_ADDRESS: u16 = 0xFF4F; +pub const HDMA1_ADDRESS: u16 = 0xFF51; +pub const HDMA2_ADDRESS: u16 = 0xFF52; +pub const HDMA3_ADDRESS: u16 = 0xFF53; +pub const HDMA4_ADDRESS: u16 = 0xFF54; +pub const HDMA5_ADDRESS: u16 = 0xFF55; + pub const BCPS_BGPI_ADDRESS: u16 = 0xFF68; pub const BCPD_BGPD_ADDRESS: u16 = 0xFF69; pub const OCPS_OBPI_ADDRESS: u16 = 0xFF6A; @@ -296,6 +302,9 @@ pub struct PPU { obj_cram: [u8; 64], oam: [u8; 0xA0], vram_bank: u8, + hdma_source: u16, + hdma_destination: u16, + hdma_start: u8, cgb_mode: bool, } @@ -329,6 +338,9 @@ impl PPU { obj_cram: [0; 64], oam: [0; 0xA0], vram_bank: 0, + hdma_source: 0, + hdma_destination: 0, + hdma_start: 0, cgb_mode, } } @@ -343,7 +355,16 @@ impl PPU { self.vram_bank | 0xFE } + pub fn hdma_source(&self) -> u16 { + self.hdma_source + } + + pub fn hdma_destination(&self) -> u16 { + self.hdma_destination + } + pub fn is_io_register(address: u16) -> bool { + (address >= 0xFF51 && address <= 0xFF55) || (address >= 0xFF68 && address <= 0xFF6B) || (address >= 0xFF40 && address <= 0xFF4F) } @@ -369,7 +390,19 @@ impl PPU { } pub fn get_register(&self, address: u16) -> u8 { - if address >= 0xFF68 && address <= 0xFF6B { + if address >= 0xFF51 && address <= 0xFF55 { + if address == HDMA1_ADDRESS { + return self.hdma_source.to_be_bytes()[0]; + } else if address == HDMA2_ADDRESS { + return self.hdma_source.to_be_bytes()[1]; + } else if address == HDMA3_ADDRESS { + return self.hdma_destination.to_be_bytes()[0]; + } else if address == HDMA4_ADDRESS { + return self.hdma_destination.to_be_bytes()[1]; + } else if address == HDMA5_ADDRESS { + return self.hdma_start; + } + } else if address >= 0xFF68 && address <= 0xFF6B { return self.cram_registers[(address as usize) - 0xFF68]; } else if address == VRAM_BANK_SELECT_ADDRESS { return self.get_vram_bank(); @@ -382,7 +415,19 @@ impl PPU { } pub fn set_register(&mut self, address: u16, data: u8) { - if address >= 0xFF68 && address <= 0xFF6B { + if address >= 0xFF51 && address <= 0xFF55 { + if address == HDMA1_ADDRESS { + self.hdma_source = (self.hdma_source & 0xFF) | ((data as u16) << 8); + } else if address == HDMA2_ADDRESS { + self.hdma_source = (self.hdma_source & 0xFF00) | (data as u16); + } else if address == HDMA3_ADDRESS { + self.hdma_destination = (self.hdma_destination & 0xFF) | ((data as u16) << 8); + } else if address == HDMA4_ADDRESS { + self.hdma_destination = (self.hdma_destination & 0xFF00) | (data as u16); + } else if address == HDMA5_ADDRESS { + self.hdma_start = data; + } + } else if address >= 0xFF68 && address <= 0xFF6B { self.cram_registers[(address as usize) - 0xFF68] = data; if address == BCPD_BGPD_ADDRESS {