From 03a14fc8545e65125387307c755facd545e47c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Tue, 15 Nov 2022 01:43:23 +0000 Subject: [PATCH] feat: initial support for CGB rom banking --- examples/sdl/src/main.rs | 4 +-- src/gb.rs | 21 +++++++++++++ src/mmu.rs | 64 ++++++++++++++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs index b5ea07fc..09ceb462 100644 --- a/examples/sdl/src/main.rs +++ b/examples/sdl/src/main.rs @@ -113,7 +113,7 @@ impl Emulator { }, Event::DropFile { filename, .. } => { self.system.reset(); - self.system.load_boot_default(); + self.system.load_cgb(true); self.load_rom(&filename); } _ => (), @@ -185,7 +185,7 @@ fn main() { // creates a new Game Boy instance and loads both the boot ROM // and the initial game ROM to "start the engine" let mut game_boy = GameBoy::new(); - game_boy.load_boot_default(); + game_boy.load_cgb(true); let mut emulator = Emulator::new(game_boy, SCREEN_SCALE); emulator.load_rom("../../res/roms.prop/super_mario.gb"); diff --git a/src/gb.rs b/src/gb.rs index 13ad5d6a..602c3587 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -109,6 +109,27 @@ impl GameBoy { self.cpu.boot(); } + pub fn load_default(&mut self, boot: bool) { + self.mmu().allocate_default(); + if boot { + self.load_boot_default(); + } + } + + pub fn load_dmg(&mut self, boot: bool) { + self.mmu().allocate_dmg(); + if boot { + self.load_boot_dmg(); + } + } + + pub fn load_cgb(&mut self, boot: bool) { + self.mmu().allocate_cgb(); + if boot { + self.load_boot_cgb(); + } + } + pub fn load_boot(&mut self, data: &[u8]) { self.cpu.mmu().write_boot(0x0000, data); } diff --git a/src/mmu.rs b/src/mmu.rs index 6fe2b26c..00edcbce 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -1,7 +1,10 @@ use crate::{debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer}; -pub const BOOT_SIZE: usize = 2304; -pub const RAM_SIZE: usize = 8192; +pub const BOOT_SIZE_DMG: usize = 256; +pub const BOOT_SIZE_CGB: usize = 2304; + +pub const RAM_SIZE_DMG: usize = 8192; +pub const RAM_SIZE_CGB: usize = 32768; pub struct Mmu { /// Register that controls the interrupts that are considered @@ -38,9 +41,15 @@ pub struct Mmu { /// the bios which is 2308 bytes long is in fact only 2048 bytes /// as the 256 bytes in range 0x100-0x1FF are meant to be /// overwritten byte the cartridge header. - boot: [u8; BOOT_SIZE], + boot: Vec<u8>, + + ram: Vec<u8>, - ram: [u8; RAM_SIZE], + /// The RAM bank to be used in the read and write operation of + /// the 0xD000-0xDFFF memory range. CGB Only + ram_bank: u8, + + ram_offset: u16, } impl Mmu { @@ -51,8 +60,10 @@ impl Mmu { timer, rom: Cartridge::new(), boot_active: true, - boot: [0u8; BOOT_SIZE], - ram: [0u8; RAM_SIZE], + boot: vec![], + ram: vec![], + ram_bank: 0x1, + ram_offset: 0x1000, ie: 0x0, } } @@ -60,11 +71,27 @@ impl Mmu { pub fn reset(&mut self) { self.rom = Cartridge::new(); self.boot_active = true; - self.boot = [0u8; BOOT_SIZE]; - self.ram = [0u8; RAM_SIZE]; + self.boot = vec![]; + self.ram = vec![]; + self.ram_bank = 0x1; + self.ram_offset = 0x1000; self.ie = 0x0; } + pub fn allocate_default(&mut self) { + self.allocate_dmg(); + } + + pub fn allocate_dmg(&mut self) { + self.boot = vec![0x00; BOOT_SIZE_DMG]; + self.ram = vec![0x00; RAM_SIZE_DMG]; + } + + pub fn allocate_cgb(&mut self) { + self.boot = vec![0x00; BOOT_SIZE_CGB]; + self.ram = vec![0x00; RAM_SIZE_CGB]; + } + pub fn ppu(&mut self) -> &mut Ppu { &mut self.ppu } @@ -106,7 +133,7 @@ impl Mmu { // ROM 0 (12 KB/16 KB) 0x1000 | 0x2000 | 0x3000 => self.rom.read(addr), - // ROM 1 (Unbanked) (16 KB) + // ROM 1 (Banked) (16 KB) 0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.read(addr), // Graphics: VRAM (8 KB) @@ -115,8 +142,11 @@ impl Mmu { // External RAM (8 KB) 0xa000 | 0xb000 => self.rom.read(addr), - // Working RAM (8 KB) - 0xc000 | 0xd000 => self.ram[(addr & 0x1fff) as usize], + // Working RAM 0 (4 KB) + 0xc000 => self.ram[(addr & 0x0fff) as usize], + + // Working RAM 1 (Banked) (4KB) + 0xd000 => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize], // Working RAM Shadow 0xe000 => self.ram[(addr & 0x1fff) as usize], @@ -182,7 +212,7 @@ impl Mmu { // ROM 0 (12 KB/16 KB) 0x1000 | 0x2000 | 0x3000 => self.rom.write(addr, value), - // ROM 1 (Unbanked) (16 KB) + // ROM 1 (Banked) (16 KB) 0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.write(addr, value), // Graphics: VRAM (8 KB) @@ -224,6 +254,16 @@ impl Mmu { // 0xFF50 - Boot active flag 0x50 => self.boot_active = false, + // 0xFF70 - SVBK: WRAM bank (CGB Mode only) + 0x70 => { + let mut ram_bank = value & 0x7; + if ram_bank == 0x0 { + ram_bank = 0x1; + } + self.ram_bank = ram_bank; + self.ram_offset = self.ram_bank as u16 * 0x1000; + } + // 0xFF80-0xFFFE - High RAM (HRAM) 0x80..=0xfe => self.ppu.hram[(addr & 0x007f) as usize] = value, -- GitLab