RAM for cgb

This commit is contained in:
Franco Colmenarez 2021-12-21 20:14:49 -05:00
parent d6ef80afe4
commit c109346dcf
4 changed files with 107 additions and 8 deletions

View File

@ -1,6 +1,7 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::utils::join_bytes; use crate::utils::join_bytes;
use crate::rom::{ROM, load_rom}; use crate::rom::{ROM, load_rom};
use crate::ram::{RAM, DMGRAM, CGBRAM, WRAM_BANK_SELECT_ADDRESS};
use crate::ppu::{ use crate::ppu::{
PPU, PPU,
DMA_ADDRESS, DMA_ADDRESS,
@ -29,6 +30,7 @@ pub const HIGH_RAM: RangeInclusive<u16> = 0xFF80..=0xFFFE;
pub struct Bus { pub struct Bus {
data: [u8; 0x10000], data: [u8; 0x10000],
pub rom: Box<dyn ROM>, pub rom: Box<dyn ROM>,
pub ram: Box<dyn RAM>,
pub ppu: PPU, pub ppu: PPU,
pub joypad: Joypad, pub joypad: Joypad,
pub timer: Timer, pub timer: Timer,
@ -51,9 +53,15 @@ impl Bus {
std::process::exit(1); std::process::exit(1);
}, },
}; };
let info = rom.info().clone();
let cgb_mode = info.cgb_features() || info.cgb_only();
let mut bus = Self { let mut bus = Self {
data: [0x00; 0x10000], data: [0x00; 0x10000],
rom, rom,
ram: match cgb_mode {
true => Box::new(CGBRAM::new()),
false => Box::new(DMGRAM::new()),
},
ppu: PPU::new(), ppu: PPU::new(),
joypad: Joypad::new(), joypad: Joypad::new(),
timer: Timer::new(), timer: Timer::new(),
@ -90,6 +98,10 @@ impl Bus {
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) { if BANK_ZERO.contains(&address) || BANK_SWITCHABLE.contains(&address) || EXTERNAL_RAM.contains(&address) {
return self.rom.read(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 { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
return self.interrupts.read(address); return self.interrupts.read(address);
} else if VIDEO_RAM.contains(&address) { } else if VIDEO_RAM.contains(&address) {
@ -121,17 +133,12 @@ impl Bus {
self.rom.write(address, data); self.rom.write(address, data);
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS { } else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
self.interrupts.write(address, data); self.interrupts.write(address, data);
} else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) { } else if WORK_RAM_1.contains(&address) || WORK_RAM_2.contains(&address) || address == WRAM_BANK_SELECT_ADDRESS {
self.data[address as usize] = data; self.ram.write(address, 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 EXTERNAL_RAM.contains(&address) { } else if EXTERNAL_RAM.contains(&address) {
self.rom.write(address, data); self.rom.write(address, data);
} else if ECHO_RAM.contains(&address) { } else if ECHO_RAM.contains(&address) {
self.data[address as usize] = data; self.ram.write(WORK_RAM_1.min().unwrap() + ((address - ECHO_RAM.min().unwrap()) & 0x1FFF), data);
self.data[(WORK_RAM_1.min().unwrap() + (address - ECHO_RAM.min().unwrap())) as usize] = data; // Copy to the working RAM
} else if Timer::is_io_register(address) { } else if Timer::is_io_register(address) {
self.timer.set_register(address, data); self.timer.set_register(address, data);
} else if Sound::is_io_register(address) { } else if Sound::is_io_register(address) {

View File

@ -4,6 +4,7 @@ pub mod ppu;
pub mod timer; pub mod timer;
pub mod sound; pub mod sound;
pub mod rom; pub mod rom;
pub mod ram;
pub mod bus; pub mod bus;
pub mod interrupts; pub mod interrupts;
pub mod joypad; pub mod joypad;

80
src/ram.rs Normal file
View File

@ -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;
}
}

View File

@ -41,6 +41,7 @@ pub fn load_rom(_filename: &str) -> std::io::Result<Box<dyn ROM>> {
filename: "".to_string(), filename: "".to_string(),
publisher: "".to_string(), publisher: "".to_string(),
title: "".to_string(), title: "".to_string(),
cgb_features: false,
cgb_only: false, cgb_only: false,
sgb_features: false, sgb_features: false,
has_ram: false, has_ram: false,
@ -150,6 +151,7 @@ pub struct ROMInfo {
filename: String, filename: String,
publisher: String, publisher: String,
title: String, title: String,
cgb_features: bool,
cgb_only: bool, cgb_only: bool,
sgb_features: bool, sgb_features: bool,
has_ram: bool, has_ram: bool,
@ -161,6 +163,14 @@ pub struct ROMInfo {
} }
impl 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) { pub fn set_filename(&mut self, filename: String) {
self.filename = filename; self.filename = filename;
} }
@ -205,6 +215,7 @@ impl ROMInfo {
}, },
publisher: "".to_string(), // TODO: Extract publisher publisher: "".to_string(), // TODO: Extract publisher
title: "".to_string(), // TODO: Extract the game title 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, cgb_only: bytes[CGB_FLAG_ADDRESS as usize] == 0xC0,
sgb_features: bytes[SGB_FLAG_ADDRESS as usize] == 0x03, sgb_features: bytes[SGB_FLAG_ADDRESS as usize] == 0x03,
has_ram: match rom_type { has_ram: match rom_type {