Skip to content

Commit

Permalink
Memory bank controller 3
Browse files Browse the repository at this point in the history
  • Loading branch information
joajfreitas committed Jul 26, 2024
1 parent 3d40bc5 commit 1b27aad
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
37 changes: 37 additions & 0 deletions fpt/src/memory/cartridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ pub fn get_cartridge_type(cartridge_rom_data: &[u8]) -> u8 {
cartridge_rom_data[map::CARTRIDGE_TYPE]
}

pub fn get_rom_size(tape: &[u8]) -> u8 {
tape[map::ROM_SIZE]
}

pub fn get_ram_size(cartridge_rom_data: &[u8]) -> u8 {
cartridge_rom_data[map::RAM_SIZE]
}

pub fn convert_rom_size(rom_size: u8) -> usize {
match rom_size {
0x00 => 2,
0x01 => 4,
0x02 => 8,
0x03 => 16,
0x04 => 32,
0x05 => 64,
0x06 => 128,
0x07 => 256,
0x08 => 512,
0x52 => 72,
0x53 => 80,
0x54 => 96,
_ => panic!(),
}
}

pub fn convert_ram_size(ram_size: u8) -> usize {
match ram_size {
0x00 => 0,
0x02 => 1,
0x03 => 4,
0x04 => 16,
0x05 => 8,
_ => panic!(),
}
}

pub trait Cartridge {
fn read(&self, address: Address) -> u8;

Expand Down
84 changes: 84 additions & 0 deletions fpt/src/memory/mbc3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use super::cartridge::{convert_ram_size, convert_rom_size, get_ram_size, get_rom_size};
use super::{map, Address, Cartridge, MemoryRange};

pub struct Mbc3Cartridge {
memory: Vec<u8>,
rom_banks: Vec<[u8; 0x4000]>,
ram_banks: Vec<[u8; 0x2000]>,
ext_ram_enabled: bool,
rom_bank_number: usize,
ram_bank_number: usize,
}

impl Mbc3Cartridge {
pub fn new(cartridge: &[u8]) -> Mbc3Cartridge {
let rom_size = convert_rom_size(get_rom_size(cartridge));
let ram_size = convert_ram_size(get_ram_size(cartridge));
let mut rom_banks = vec![[0; 0x4000]; rom_size as usize];
let mut ram_banks = vec![[0; 0x2000]; ram_size as usize];

// TODO: wtf is this initialization
for i in 0..rom_size {
for j in 0..0x4000 {
rom_banks[i][j] = cartridge[0x4000 * i + j];
}
}

for i in 0..ram_size {
for j in 0..0x2000 {
ram_banks[i][j] = cartridge[0x2000 * i + j];
}
}

Mbc3Cartridge {
memory: cartridge.to_vec(),
rom_banks,
ram_banks,
ext_ram_enabled: false,
rom_bank_number: 0,
ram_bank_number: 0,
}
}
}

impl Cartridge for Mbc3Cartridge {
fn read(&self, address: Address) -> u8 {
if map::EXT_WRAM.contains(&address) && !self.ext_ram_enabled {
0 // TODO: check that disabled ram reads 0
} else if map::EXT_WRAM.contains(&address) && self.ext_ram_enabled {
self.ram_banks[self.ram_bank_number][address - map::EXT_WRAM.start]
} else if map::ROM_BANK0.contains(&address) {
self.rom_banks[0][address - map::ROM_BANK0.start]
} else if map::ROM_BANK1.contains(&address) {
self.rom_banks[self.rom_bank_number][address - map::ROM_BANK1.start]
} else {
self.memory[address]
}
}
fn write(&mut self, address: Address, value: u8) {
if (0x0000..0x2000).contains(&address) {
self.ext_ram_enabled = value & 0xF == 0xA;
} else if (0x2000..0x4000).contains(&address) {
// TODO: rom bank number upper bits
let rom_bank_number = value & 0x1F;
if rom_bank_number == 0 {
self.rom_bank_number = 1;
} else {
self.rom_bank_number = rom_bank_number as usize; // TODO: needs to be masked to log2(#banks)
}
} else if (0x4000..0x6000).contains(&address) {
let ram_bank_number = value & 0x3;
self.ram_bank_number = ram_bank_number as usize; // TODO: needs to be checked for number of ram
// banks
} else if map::EXT_WRAM.contains(&address) && self.ext_ram_enabled {
self.memory[address] = value;
}
}

fn read_range(&self, memory_range: MemoryRange) -> Vec<u8> {
memory_range
.into_iter()
.map(|address| self.read(address))
.collect()
}
}
4 changes: 3 additions & 1 deletion fpt/src/memory/mbc_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use std::cell::RefCell;

use super::cartridge::{get_cartridge_type, EmptyCartridge};
use super::mbc_none::NoMbcCartridge;
use super::mbc3::Mbc3Cartridge;
use super::Cartridge;

pub fn create_mbc(cartridge_data: &[u8]) -> Option<Box<RefCell<dyn Cartridge>>> {
// https://gbdev.io/pandocs/The_Cartridge_Header.html#0147--cartridge-type
match get_cartridge_type(cartridge_data) {
match dbg!(get_cartridge_type(cartridge_data)) {
0x00 => Some(Box::new(RefCell::new(NoMbcCartridge::new(cartridge_data)))), // rom only
0x0F..=0x13 => Some(Box::new(RefCell::new(Mbc3Cartridge::new(cartridge_data)))),
_ => None,
}
}
Expand Down
2 changes: 2 additions & 0 deletions fpt/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ mod lib;
pub mod map;
mod mbc_builder;
mod mbc_none;
mod mbc3;

pub use cartridge::Cartridge;
pub use lib::{Address, Bus, Buttons, MemoryRange};
pub use mbc_builder::{create_empty_mbc, create_mbc};
pub use mbc_none::NoMbcCartridge;
pub use mbc3::Mbc3Cartridge;

0 comments on commit 1b27aad

Please sign in to comment.