From 9386638f60b9381d1a21bb4b3332bade3db88909 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sat, 29 Apr 2023 16:41:24 +0100
Subject: [PATCH] chore: initial support for multiple vram banks

---
 src/mmu.rs |  5 ++++-
 src/ppu.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/src/mmu.rs b/src/mmu.rs
index 983dcbdd..54894f76 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -69,6 +69,9 @@ pub struct Mmu {
     /// the 0xD000-0xDFFF memory range (CGB only).
     ram_bank: u8,
 
+    /// The offset to be used in the read and write operation of
+    /// the RAM, this value should be consistent with the RAM bank
+    /// that is currently selected (CGB only).
     ram_offset: u16,
 
     mode: GameBoyMode,
@@ -323,7 +326,7 @@ impl Mmu {
 
                     // 0xFF70 - SVBK: WRAM bank (CGB only)
                     0x70 => {
-                        let mut ram_bank = value & 0x7;
+                        let mut ram_bank = value & 0x07;
                         if ram_bank == 0x0 {
                             ram_bank = 0x1;
                         }
diff --git a/src/ppu.rs b/src/ppu.rs
index fbd91fe1..35c3d2cd 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -9,7 +9,9 @@ use wasm_bindgen::prelude::*;
 
 use crate::warnln;
 
-pub const VRAM_SIZE: usize = 8192;
+pub const VRAM_SIZE_DMG: usize = 8192;
+pub const VRAM_SIZE_CGB: usize = 16384;
+pub const VRAM_SIZE: usize = VRAM_SIZE_CGB;
 pub const HRAM_SIZE: usize = 128;
 pub const OAM_SIZE: usize = 260;
 pub const PALETTE_SIZE: usize = 4;
@@ -19,9 +21,12 @@ pub const TILE_WIDTH: usize = 8;
 pub const TILE_HEIGHT: usize = 8;
 pub const TILE_DOUBLE_HEIGHT: usize = 16;
 
+pub const TILE_COUNT_DMG: usize = 384;
+pub const TILE_COUNT_CGB: usize = 768;
+
 /// The number of tiles that can be store in Game Boy's
 /// VRAM memory according to specifications.
-pub const TILE_COUNT: usize = 384;
+pub const TILE_COUNT: usize = TILE_COUNT_CGB;
 
 /// The number of objects/sprites that can be handled at
 /// the same time by the Game Boy.
@@ -161,6 +166,26 @@ impl Display for ObjectData {
     }
 }
 
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct TileData {
+    palette: u8,
+    vram_bank: u8,
+    xflip: bool,
+    yflip: bool,
+    priority: bool,
+}
+
+impl Display for TileData {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "Palette => {}\nVRAM Bank => {}\nX Flip => {}\nY Flip => {}",
+            self.palette, self.vram_bank, self.xflip, self.yflip
+        )
+    }
+}
+
 pub struct PpuRegisters {
     pub scy: u8,
     pub scx: u8,
@@ -203,6 +228,15 @@ pub struct Ppu {
     /// sprite attributes for each of the 40 sprites of the Game Boy.
     oam: [u8; OAM_SIZE],
 
+    /// The VRAM bank to be used in the read and write operation of
+    /// the 0x8000-0x9FFF memory range (CGB only).
+    vram_bank: u8,
+
+    /// The offset to be used in the read and write operation of
+    /// the VRAM, this value should be consistent with the VRAM bank
+    /// that is currently selected (CGB only).
+    vram_offset: u16,
+
     /// The current set of processed tiles that are store in the
     /// PPU related structures.
     tiles: [Tile; TILE_COUNT],
@@ -366,6 +400,8 @@ impl Ppu {
             vram: [0u8; VRAM_SIZE],
             hram: [0u8; HRAM_SIZE],
             oam: [0u8; OAM_SIZE],
+            vram_bank: 0x0,
+            vram_offset: 0x0000,
             tiles: [Tile { buffer: [0u8; 64] }; TILE_COUNT],
             obj_data: [ObjectData {
                 x: 0,
@@ -420,8 +456,10 @@ impl Ppu {
     pub fn reset(&mut self) {
         self.color_buffer = Box::new([0u8; COLOR_BUFFER_SIZE]);
         self.frame_buffer = Box::new([0u8; FRAME_BUFFER_SIZE]);
-        self.vram = [0u8; VRAM_SIZE];
+        self.vram = [0u8; VRAM_SIZE_CGB];
         self.hram = [0u8; HRAM_SIZE];
+        self.vram_bank = 0x0;
+        self.vram_offset = 0x0000;
         self.tiles = [Tile { buffer: [0u8; 64] }; TILE_COUNT];
         self.palette = [[0u8; RGB_SIZE]; PALETTE_SIZE];
         self.palette_obj_0 = [[0u8; RGB_SIZE]; PALETTE_SIZE];
@@ -544,7 +582,7 @@ impl Ppu {
 
     pub fn read(&mut self, addr: u16) -> u8 {
         match addr {
-            0x8000..=0x9fff => self.vram[(addr & 0x1fff) as usize],
+            0x8000..=0x9fff => self.vram[(self.vram_offset + (addr & 0x1fff)) as usize],
             0xfe00..=0xfe9f => self.oam[(addr & 0x009f) as usize],
             0xff80..=0xfffe => self.hram[(addr & 0x007f) as usize],
             0xff40 =>
@@ -577,7 +615,7 @@ impl Ppu {
             0xff4a => self.wy,
             0xff4b => self.wx,
             // 0xFF4F — VBK (CGB only)
-            0xff4f => 0xff,
+            0xff4f => self.vram_bank | 0xfe,
             // 0xFF68 — BCPS/BGPI (CGB only)
             0xff68 => self.palette_address_bg | if self.auto_increment_bg { 0x80 } else { 0x00 },
             // 0xFF69 — BCPD/BGPD (CGB only)
@@ -596,7 +634,7 @@ impl Ppu {
     pub fn write(&mut self, addr: u16, value: u8) {
         match addr {
             0x8000..=0x9fff => {
-                self.vram[(addr & 0x1fff) as usize] = value;
+                self.vram[(self.vram_offset + (addr & 0x1fff)) as usize] = value;
                 if addr < 0x9800 {
                     self.update_tile(addr, value);
                 }
@@ -653,7 +691,10 @@ impl Ppu {
             0xff4a => self.wy = value,
             0xff4b => self.wx = value,
             // 0xFF4F — VBK (CGB only)
-            0xff4f => (),
+            0xff4f => {
+                self.vram_bank = value & 0x01;
+                self.vram_offset = self.vram_bank as u16 * 0x2000;
+            }
             // 0xFF68 — BCPS/BGPI (CGB only)
             0xff68 => {
                 self.palette_address_bg = value & 0x3f;
@@ -786,7 +827,7 @@ impl Ppu {
     /// just been written to a location on the VRAM associated
     /// with tiles.
     fn update_tile(&mut self, addr: u16, _value: u8) {
-        let addr = (addr & 0x1ffe) as usize;
+        let addr = (self.vram_offset + (addr & 0x1ffe)) as usize;
         let tile_index = (addr >> 4) & 0x01ff;
         let tile = self.tiles[tile_index].borrow_mut();
         let y = (addr >> 1) & 0x0007;
-- 
GitLab