gb/src/cartridge.rs

390 lines
11 KiB
Rust
Raw Normal View History

use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
const RAM_SIZE_ADDRESS: usize = 0x0149;
const ROM_SIZE_ADDRESS: usize = 0x0148;
const MBC_TYPE_ADDRESS: usize = 0x0147;
2021-01-20 07:39:24 +00:00
#[derive(Debug, Clone, Default)]
pub struct Cartridge {
memory: Vec<u8>,
mbc: Box<dyn MemoryBankController>,
}
impl Cartridge {
pub fn new<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<Self> {
let mut memory = vec![];
let mut rom = File::open(path)?;
rom.read_to_end(&mut memory)?;
2021-01-20 07:39:24 +00:00
Ok(Self {
mbc: Self::detect_mbc(&memory),
memory,
})
}
fn detect_mbc(memory: &[u8]) -> Box<dyn MemoryBankController> {
2021-01-20 07:39:24 +00:00
let ram_size = Self::find_ram_size(&memory);
let bank_count = Self::find_bank_count(&memory);
let mbc_kind = Self::find_mbc(&memory);
let ram_byte_count = ram_size.to_byte_count();
let mbc = match mbc_kind {
MBCKind::None => todo!("Handle no MBC Situation"),
MBCKind::MBC1 => MBC1 {
ram_size,
ram: vec![0; ram_byte_count as usize],
2021-01-20 07:39:24 +00:00
bank_count,
..Default::default()
},
MBCKind::MBC5 => todo!("Implement MBC5"),
};
Box::new(mbc)
}
2021-03-16 06:05:13 +00:00
fn find_ram_size(memory: &[u8]) -> RamSize {
let id = memory[RAM_SIZE_ADDRESS];
2021-01-20 07:39:24 +00:00
id.into()
}
fn find_bank_count(memory: &[u8]) -> BankCount {
let id = memory[ROM_SIZE_ADDRESS];
2021-01-20 07:39:24 +00:00
id.into()
}
fn find_mbc(memory: &[u8]) -> MBCKind {
let id = memory[MBC_TYPE_ADDRESS];
2021-01-20 07:39:24 +00:00
// TODO: Refactor this to match the other enums in this module
match id {
0x00 => MBCKind::None,
0x01 => MBCKind::MBC1,
0x19 => MBCKind::MBC5,
_ => panic!("Cartridge uses an unknown Memory Bank Controller"),
}
}
}
impl Cartridge {
pub fn read_byte(&self, addr: u16) -> u8 {
2021-01-20 07:39:24 +00:00
match self.mbc.handle_read(addr) {
MBCResult::Address(addr) => self.memory[addr as usize],
MBCResult::Value(byte) => byte,
2021-01-20 07:39:24 +00:00
}
}
pub fn write_byte(&mut self, addr: u16, byte: u8) {
self.mbc.handle_write(addr, byte);
}
pub fn read_word(&self, addr: u16) -> u16 {
2021-01-20 07:39:24 +00:00
(self.read_byte(addr + 1) as u16) << 8 | self.read_byte(addr) as u16
}
pub fn write_word(&mut self, addr: u16, word: u16) {
self.write_byte(addr + 1, (word >> 8) as u8);
self.write_byte(addr, (word & 0x00FF) as u8);
}
}
#[derive(Debug, Clone, Default)]
struct MBC1 {
current_rom_bank: u8, // 5-bit Number
current_ram_bank: u8, // 2-bit number
mode: bool,
2021-03-16 06:05:13 +00:00
ram_size: RamSize,
ram: Vec<u8>,
2021-01-20 07:39:24 +00:00
bank_count: BankCount,
ram_enabled: bool,
}
impl MBC1 {
fn calc_zero_bank_number(&self) -> u8 {
match self.bank_count {
BankCount::ThirtyTwo | BankCount::Sixteen | BankCount::Eight | BankCount::Four => 0,
BankCount::SixtyFour => (self.current_ram_bank & 0x01) << 5,
BankCount::OneHundredTwentyEight => self.current_ram_bank << 5,
_ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
}
}
fn calc_high_bank_number(&self) -> u8 {
match self.bank_count {
BankCount::ThirtyTwo | BankCount::Sixteen | BankCount::Eight | BankCount::Four => {
self.apply_rom_size_bitmask(self.current_rom_bank)
}
BankCount::SixtyFour => {
let mut num = self.apply_rom_size_bitmask(self.current_rom_bank);
num &= !(0x01 << 5);
num | ((self.current_ram_bank & 0x01) << 5)
}
BankCount::OneHundredTwentyEight => {
let mut num = self.apply_rom_size_bitmask(self.current_rom_bank);
num &= !(0x03 << 5);
num | ((self.current_ram_bank) << 5)
}
_ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
}
}
fn apply_rom_size_bitmask(&self, byte: u8) -> u8 {
2021-03-16 00:19:40 +00:00
use BankCount::*;
2021-01-20 07:39:24 +00:00
match self.bank_count {
2021-03-16 00:19:40 +00:00
Four => byte & 0b00000011,
Eight => byte & 0b00000111,
Sixteen => byte & 0b00001111,
ThirtyTwo => byte & 0b00011111,
SixtyFour => byte & 0b00011111,
OneHundredTwentyEight => byte & 0b00011111,
2021-01-20 07:39:24 +00:00
_ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
}
}
}
impl MemoryBankController for MBC1 {
2021-01-20 07:39:24 +00:00
fn handle_read(&self, addr: u16) -> MBCResult {
2021-03-16 00:19:40 +00:00
use MBCResult::*;
2021-01-20 07:39:24 +00:00
match addr {
0x0000..=0x3FFF => {
if self.mode {
let zero_bank_number = self.calc_zero_bank_number() as u16;
2021-03-16 00:19:40 +00:00
Address(0x4000 * zero_bank_number + addr)
2021-01-20 07:39:24 +00:00
} else {
2021-03-16 00:19:40 +00:00
Address(addr)
2021-01-20 07:39:24 +00:00
}
}
0x4000..=0x7FFF => {
let high_bank_number = self.calc_high_bank_number() as u16;
2021-03-16 00:19:40 +00:00
Address(0x4000 * high_bank_number * (addr - 0x4000))
2021-01-20 07:39:24 +00:00
}
0xA000..=0xBFFF => {
if self.ram_enabled {
let ram_addr = match self.ram_size {
2021-03-16 06:05:13 +00:00
RamSize::_2KB | RamSize::_8KB => {
2021-01-20 07:39:24 +00:00
(addr as u32 - 0xA000) % self.ram_size.to_byte_count()
}
2021-03-16 06:05:13 +00:00
RamSize::_32KB => {
2021-01-20 07:39:24 +00:00
if self.mode {
0x2000 * self.current_ram_bank as u32 + (addr as u32 - 0xA000)
} else {
addr as u32 - 0xA000
}
}
_ => unreachable!(""),
};
Value(self.ram[ram_addr as usize])
2021-01-20 07:39:24 +00:00
} else {
2021-03-16 00:19:40 +00:00
Address(0x00FF)
2021-01-20 07:39:24 +00:00
}
}
_ => unimplemented!(),
}
}
fn handle_write(&mut self, addr: u16, byte: u8) {
match addr {
0x0000..=0x1FFF => self.ram_enabled = (byte & 0x0F) == 0x0A,
0x2000..=0x3FFF => {
self.current_rom_bank = self.apply_rom_size_bitmask(byte);
if self.current_rom_bank == 0 {
self.current_rom_bank = 1;
}
}
0x4000..=0x5FFF => self.current_ram_bank = byte & 0x03,
0x6000..=0x7FFF => self.mode = byte >> 7 == 0x01,
0xA000..=0xBFFF => {
if self.ram_enabled {
let ram_addr = match self.ram_size {
2021-03-16 06:05:13 +00:00
RamSize::_2KB | RamSize::_8KB => {
2021-01-20 07:39:24 +00:00
(addr as u32 - 0xA000) % self.ram_size.to_byte_count()
}
2021-03-16 06:05:13 +00:00
RamSize::_32KB => {
2021-01-20 07:39:24 +00:00
if self.mode {
0x2000 * (self.current_ram_bank as u32) + (addr as u32 - 0xA000)
} else {
addr as u32 - 0xA000
}
}
2021-03-16 06:05:13 +00:00
_ => unreachable!("RAM size can not be greater than 32KB on MBC1"),
2021-01-20 07:39:24 +00:00
};
self.ram[ram_addr as usize] = byte;
}
}
_ => unreachable!("{:#06X} should not be handled by MBC1", addr),
}
}
}
trait MemoryBankController: CloneMBC {
2021-01-20 07:39:24 +00:00
fn handle_read(&self, addr: u16) -> MBCResult;
fn handle_write(&mut self, addr: u16, byte: u8);
}
trait CloneMBC {
fn clone_mbc(&self) -> Box<dyn MemoryBankController>;
}
impl<T> CloneMBC for T
where
T: MemoryBankController + Clone + 'static,
{
fn clone_mbc<'a>(&self) -> Box<dyn MemoryBankController> {
Box::new(self.clone())
}
}
2021-01-20 07:39:24 +00:00
enum MBCResult {
Address(u16),
Value(u8),
2021-01-20 07:39:24 +00:00
}
#[derive(Debug, Clone, Copy)]
enum MBCKind {
None,
MBC1,
MBC5,
}
impl Default for MBCKind {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone, Copy)]
2021-03-16 06:05:13 +00:00
enum RamSize {
2021-01-20 07:39:24 +00:00
None = 0,
_2KB = 1,
_8KB = 2,
_32KB = 3, // Split into 4 RAM banks
_128KB = 4, // Split into 16 RAM banks
_64KB = 5, // Split into 8 RAm Banks
}
2021-03-16 06:05:13 +00:00
impl RamSize {
2021-01-20 07:39:24 +00:00
pub fn to_byte_count(&self) -> u32 {
2021-03-16 06:05:13 +00:00
use RamSize::*;
2021-03-16 00:19:40 +00:00
2021-01-20 07:39:24 +00:00
match *self {
2021-03-16 00:19:40 +00:00
None => 0,
_2KB => 2_048,
_8KB => 8_192,
_32KB => 32_768,
_128KB => 131_072,
_64KB => 65_536,
2021-01-20 07:39:24 +00:00
}
}
}
2021-03-16 06:05:13 +00:00
impl Default for RamSize {
2021-01-20 07:39:24 +00:00
fn default() -> Self {
Self::None
}
}
2021-03-16 06:05:13 +00:00
impl From<u8> for RamSize {
2021-01-20 07:39:24 +00:00
fn from(byte: u8) -> Self {
2021-03-16 06:05:13 +00:00
use RamSize::*;
2021-03-16 00:19:40 +00:00
2021-01-20 07:39:24 +00:00
match byte {
2021-03-16 00:19:40 +00:00
0x00 => None,
0x01 => _2KB,
0x02 => _8KB,
0x03 => _32KB,
0x04 => _64KB,
0x05 => _128KB,
2021-01-20 07:39:24 +00:00
_ => unreachable!("{:#04X} is an invalid value for RAMSize"),
}
}
}
#[derive(Debug, Clone, Copy)]
enum BankCount {
None = 0, // 32KB
Four = 1, // 64KB
Eight = 2, // 128KB
Sixteen = 3, // 256KB
ThirtyTwo = 4, // 512KB
SixtyFour = 5, // 1MB
OneHundredTwentyEight = 6, // 2MB
TwoHundredFiftySix = 7, // 4MB
FiveHundredTwelve = 8, // 8MB
SeventyTwo = 0x52, // 1.1MB
Eighty = 0x53, // 1.2MB
NinetySix = 0x54, // 1.5MB
}
impl Default for BankCount {
fn default() -> Self {
Self::None
}
}
impl BankCount {
// https://hacktix.github.io/GBEDG/mbcs/#rom-size
pub fn to_byte_count(&self) -> u32 {
2021-03-16 00:19:40 +00:00
use BankCount::*;
2021-01-20 07:39:24 +00:00
match *self {
2021-03-16 00:19:40 +00:00
None => 32_768,
Four => 65_536,
Eight => 131_072,
Sixteen => 262_144,
ThirtyTwo => 524_288,
SixtyFour => 1_048_576,
OneHundredTwentyEight => 2_097_152,
TwoHundredFiftySix => 4_194_304,
FiveHundredTwelve => 8_388_608,
SeventyTwo => 1_179_648,
Eighty => 1_310_720,
NinetySix => 1_572_864,
2021-01-20 07:39:24 +00:00
}
}
}
impl From<u8> for BankCount {
fn from(byte: u8) -> Self {
2021-03-16 00:19:40 +00:00
use BankCount::*;
2021-01-20 07:39:24 +00:00
match byte {
2021-03-16 00:19:40 +00:00
0x00 => None,
0x01 => Four,
0x02 => Eight,
0x03 => Sixteen,
0x04 => ThirtyTwo,
0x05 => SixtyFour,
0x06 => OneHundredTwentyEight,
0x07 => TwoHundredFiftySix,
0x08 => FiveHundredTwelve,
0x52 => SeventyTwo,
0x53 => Eighty,
0x54 => NinetySix,
2021-01-20 07:39:24 +00:00
_ => unreachable!("{:#04X} is an invalid value for BankCount"),
}
}
}
impl std::fmt::Debug for Box<dyn MemoryBankController> {
2021-03-16 00:19:40 +00:00
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!("Implement Debug for Box<dyn MBC> Trait Object");
2021-01-20 07:39:24 +00:00
}
}
impl std::clone::Clone for Box<dyn MemoryBankController> {
2021-01-20 07:39:24 +00:00
fn clone(&self) -> Self {
self.clone_mbc()
2021-01-20 07:39:24 +00:00
}
}
impl std::default::Default for Box<dyn MemoryBankController> {
2021-01-20 07:39:24 +00:00
fn default() -> Self {
Box::new(MBC1::default())
}
}