From c109346dcf27c8aad0b49a5ef925baeccde70b89 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Tue, 21 Dec 2021 20:14:49 -0500 Subject: [PATCH] RAM for cgb --- src/bus.rs | 23 ++++++++++------ src/lib.rs | 1 + src/ram.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rom.rs | 11 ++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 src/ram.rs diff --git a/src/bus.rs b/src/bus.rs index 8e0baac..caa1766 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,6 +1,7 @@ use std::ops::RangeInclusive; use crate::utils::join_bytes; use crate::rom::{ROM, load_rom}; +use crate::ram::{RAM, DMGRAM, CGBRAM, WRAM_BANK_SELECT_ADDRESS}; use crate::ppu::{ PPU, DMA_ADDRESS, @@ -29,6 +30,7 @@ pub const HIGH_RAM: RangeInclusive = 0xFF80..=0xFFFE; pub struct Bus { data: [u8; 0x10000], pub rom: Box, + pub ram: Box, pub ppu: PPU, pub joypad: Joypad, pub timer: Timer, @@ -51,9 +53,15 @@ impl Bus { std::process::exit(1); }, }; + let info = rom.info().clone(); + let cgb_mode = info.cgb_features() || info.cgb_only(); let mut bus = Self { data: [0x00; 0x10000], rom, + ram: match cgb_mode { + true => Box::new(CGBRAM::new()), + false => Box::new(DMGRAM::new()), + }, ppu: PPU::new(), joypad: Joypad::new(), timer: Timer::new(), @@ -90,6 +98,10 @@ impl Bus { pub fn read(&self, address: u16) -> u8 { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) { return self.rom.read(address); + } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS { + return self.ram.read(address); + } else if ECHO_RAM.contains(&address) { + return self.ram.read(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF)); } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS { return self.interrupts.read(address); } else if VIDEO_RAM.contains(&address) { @@ -121,17 +133,12 @@ impl Bus { self.rom.write(address, data); } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS { self.interrupts.write(address, data); - } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) { - self.data[address as usize] = data; - // Copy to the ECHO RAM - if address <= 0xDDFF { - self.data[(ECHO_RAM.min().unwrap() + (address - WORK_RAM_1.min().unwrap())) as usize] = data; - } + } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS { + self.ram.write(address, data); } else if EXTERNAL_RAM.contains(&address) { self.rom.write(address, data); } else if ECHO_RAM.contains(&address) { - self.data[address as usize] = data; - self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM + self.ram.write(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF), data); } else if Timer::is_io_register(address) { self.timer.set_register(address, data); } else if Sound::is_io_register(address) { diff --git a/src/lib.rs b/src/lib.rs index 665aede..80e13d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ pub mod ppu; pub mod timer; pub mod sound; pub mod rom; +pub mod ram; pub mod bus; pub mod interrupts; pub mod joypad; diff --git a/src/ram.rs b/src/ram.rs new file mode 100644 index 0000000..41bc367 --- /dev/null +++ b/src/ram.rs @@ -0,0 +1,80 @@ +pub const WRAM_BANK_SELECT_ADDRESS: u16 = 0xFF70; + +pub trait RAM { + fn read(&self, address: u16) -> u8; + fn write(&mut self, address: u16, value: u8); +} + +pub struct DMGRAM { + data: [u8; 4096 * 2], +} + +impl DMGRAM { + pub fn new() -> Self { + Self { + data: [0; 4096 * 2], + } + } +} + +impl RAM for DMGRAM { + fn read(&self, address: u16) -> u8 { + if address == WRAM_BANK_SELECT_ADDRESS { + return 0xFF; + } + self.data[(address - 0xC000) as usize] + } + + fn write(&mut self, address: u16, value: u8) { + if address == WRAM_BANK_SELECT_ADDRESS { + return; + } + self.data[(address - 0xC000) as usize] = value; + } +} + + +pub struct CGBRAM { + data: [u8; 4096 * 8], + bank: u8, + +} + +impl CGBRAM { + pub fn new() -> Self { + Self { + data: [0; 4096 * 8], + bank: 1, + } + } + + fn switch_bank(&mut self, bank: u8) { + self.bank = bank; + if self.bank > 7 { + self.bank = 7; + } else if bank == 0 { + self.bank = 0; + } + } +} + +impl RAM for CGBRAM { + fn read(&self, address: u16) -> u8 { + if address == WRAM_BANK_SELECT_ADDRESS { + return self.bank; + } + if address <= 0xCFFF { + return self.data[(address - 0xC000) as usize]; + } + self.data[((address - 0xC000) as usize) * (self.bank as usize)] + } + + fn write(&mut self, address: u16, value: u8) { + if address == WRAM_BANK_SELECT_ADDRESS { + return self.switch_bank(value); + } else if address <= 0xCFFF { + return self.data[(address - 0xC000) as usize] = value; + } + self.data[((address - 0xC000) as usize) * (self.bank as usize)] = value; + } +} diff --git a/src/rom.rs b/src/rom.rs index 509345d..6247b37 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -41,6 +41,7 @@ pub fn load_rom(_filename: &str) -> std::io::Result> { filename: "".to_string(), publisher: "".to_string(), title: "".to_string(), + cgb_features: false, cgb_only: false, sgb_features: false, has_ram: false, @@ -150,6 +151,7 @@ pub struct ROMInfo { filename: String, publisher: String, title: String, + cgb_features: bool, cgb_only: bool, sgb_features: bool, has_ram: bool, @@ -161,6 +163,14 @@ pub struct ROMInfo { } impl ROMInfo { + pub fn cgb_features(&self) -> bool { + self.cgb_features + } + + pub fn cgb_only(&self) -> bool { + self.cgb_only + } + pub fn set_filename(&mut self, filename: String) { self.filename = filename; } @@ -205,6 +215,7 @@ impl ROMInfo { }, publisher: "".to_string(), // TODO: Extract publisher title: "".to_string(), // TODO: Extract the game title + cgb_features: bytes[CGB_FLAG_ADDRESS as usize] == 0x80, cgb_only: bytes[CGB_FLAG_ADDRESS as usize] == 0xC0, sgb_features: bytes[SGB_FLAG_ADDRESS as usize] == 0x03, has_ram: match rom_type {