basic HDMA implementation

This commit is contained in:
Franco Colmenarez 2022-01-02 21:09:48 -05:00
parent 7925dd75c9
commit 2ca1813939
2 changed files with 76 additions and 10 deletions

View File

@ -5,6 +5,7 @@ use crate::ram::{RAM, DMGRAM, CGBRAM, WRAM_BANK_SELECT_ADDRESS};
use crate::ppu::{ use crate::ppu::{
PPU, PPU,
DMA_ADDRESS, DMA_ADDRESS,
HDMA5_ADDRESS,
}; };
use crate::timer::Timer; use crate::timer::Timer;
use crate::joypad::{Joypad, JOYPAD_ADDRESS}; use crate::joypad::{Joypad, JOYPAD_ADDRESS};
@ -153,14 +154,11 @@ impl Bus {
} else if SPRITE_ATTRIBUTE_TABLE.contains(&address) { } else if SPRITE_ATTRIBUTE_TABLE.contains(&address) {
return self.ppu.write_oam(address, data); return self.ppu.write_oam(address, data);
} else if address == DMA_ADDRESS { } else if address == DMA_ADDRESS {
self.data[address as usize] = data; self.ppu.set_register(address, data);
let source = (data as u16) * 0x100; self.dma_transfer(data);
let mut count: u16 = 0; } else if address == HDMA5_ADDRESS {
let oam_addr = SPRITE_ATTRIBUTE_TABLE.min().unwrap(); self.ppu.set_register(address, data);
while count < 160 { self.hdma_transfer(data);
self.ppu.write_oam(oam_addr + count, self.read(source + count));
count += 1;
}
} else if PPU::is_io_register(address) { } else if PPU::is_io_register(address) {
self.ppu.set_register(address, data); self.ppu.set_register(address, data);
} else { } else {
@ -173,4 +171,27 @@ impl Bus {
self.write(address, bytes[0]); self.write(address, bytes[0]);
self.write(address.wrapping_add(1), bytes[1]); 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);
}
} }

View File

@ -29,6 +29,12 @@ pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B; pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
pub const VRAM_BANK_SELECT_ADDRESS: u16 = 0xFF4F; 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 BCPS_BGPI_ADDRESS: u16 = 0xFF68;
pub const BCPD_BGPD_ADDRESS: u16 = 0xFF69; pub const BCPD_BGPD_ADDRESS: u16 = 0xFF69;
pub const OCPS_OBPI_ADDRESS: u16 = 0xFF6A; pub const OCPS_OBPI_ADDRESS: u16 = 0xFF6A;
@ -296,6 +302,9 @@ pub struct PPU {
obj_cram: [u8; 64], obj_cram: [u8; 64],
oam: [u8; 0xA0], oam: [u8; 0xA0],
vram_bank: u8, vram_bank: u8,
hdma_source: u16,
hdma_destination: u16,
hdma_start: u8,
cgb_mode: bool, cgb_mode: bool,
} }
@ -329,6 +338,9 @@ impl PPU {
obj_cram: [0; 64], obj_cram: [0; 64],
oam: [0; 0xA0], oam: [0; 0xA0],
vram_bank: 0, vram_bank: 0,
hdma_source: 0,
hdma_destination: 0,
hdma_start: 0,
cgb_mode, cgb_mode,
} }
} }
@ -343,7 +355,16 @@ impl PPU {
self.vram_bank | 0xFE 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 { pub fn is_io_register(address: u16) -> bool {
(address >= 0xFF51 && address <= 0xFF55) ||
(address >= 0xFF68 && address <= 0xFF6B) || (address >= 0xFF68 && address <= 0xFF6B) ||
(address >= 0xFF40 && address <= 0xFF4F) (address >= 0xFF40 && address <= 0xFF4F)
} }
@ -369,7 +390,19 @@ impl PPU {
} }
pub fn get_register(&self, address: u16) -> u8 { 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]; return self.cram_registers[(address as usize) - 0xFF68];
} else if address == VRAM_BANK_SELECT_ADDRESS { } else if address == VRAM_BANK_SELECT_ADDRESS {
return self.get_vram_bank(); return self.get_vram_bank();
@ -382,7 +415,19 @@ impl PPU {
} }
pub fn set_register(&mut self, address: u16, data: u8) { 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; self.cram_registers[(address as usize) - 0xFF68] = data;
if address == BCPD_BGPD_ADDRESS { if address == BCPD_BGPD_ADDRESS {