2020-12-24 01:39:37 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{self, Read};
|
|
|
|
use std::path::Path;
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
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)]
|
2020-12-24 01:39:37 +00:00
|
|
|
pub struct Cartridge {
|
|
|
|
memory: Vec<u8>,
|
2021-03-21 01:22:31 +00:00
|
|
|
mbc: Box<dyn MemoryBankController>,
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
})
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
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,
|
2021-03-17 03:51:41 +00:00
|
|
|
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 {
|
2021-03-21 01:22:31 +00:00
|
|
|
let id = memory[RAM_SIZE_ADDRESS];
|
2021-01-20 07:39:24 +00:00
|
|
|
id.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_bank_count(memory: &[u8]) -> BankCount {
|
2021-03-21 01:22:31 +00:00
|
|
|
let id = memory[ROM_SIZE_ADDRESS];
|
2021-01-20 07:39:24 +00:00
|
|
|
id.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_mbc(memory: &[u8]) -> MBCKind {
|
2021-03-21 01:22:31 +00:00
|
|
|
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 {
|
2020-12-24 01:39:37 +00:00
|
|
|
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],
|
2021-03-21 01:22:31 +00:00
|
|
|
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);
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2021-03-21 06:52:29 +00:00
|
|
|
rom_bank: u8, // 5-bit Number
|
|
|
|
ram_bank: u8, // 2-bit number
|
2021-01-20 07:39:24 +00:00
|
|
|
mode: bool,
|
2021-03-16 06:05:13 +00:00
|
|
|
ram_size: RamSize,
|
2021-03-17 03:51:41 +00:00
|
|
|
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 {
|
2021-03-21 06:52:29 +00:00
|
|
|
use BankCount::*;
|
|
|
|
|
2021-01-20 07:39:24 +00:00
|
|
|
match self.bank_count {
|
2021-03-21 06:52:29 +00:00
|
|
|
ThirtyTwo | Sixteen | Eight | Four => 0,
|
|
|
|
SixtyFour => (self.ram_bank & 0x01) << 5,
|
|
|
|
OneHundredTwentyEight => self.ram_bank << 5,
|
|
|
|
_ => unreachable!("{:#?} is not a valid MBC1 BankCount", self.bank_count),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_high_bank_number(&self) -> u8 {
|
2021-03-21 06:52:29 +00:00
|
|
|
use BankCount::*;
|
|
|
|
|
2021-01-20 07:39:24 +00:00
|
|
|
match self.bank_count {
|
2021-03-21 06:52:29 +00:00
|
|
|
None | Four | Eight | Sixteen | ThirtyTwo => self.apply_rom_size_bitmask(self.rom_bank),
|
|
|
|
SixtyFour => {
|
|
|
|
let mut num = self.apply_rom_size_bitmask(self.rom_bank);
|
2021-01-20 07:39:24 +00:00
|
|
|
num &= !(0x01 << 5);
|
2021-03-21 06:52:29 +00:00
|
|
|
num | ((self.ram_bank & 0x01) << 5)
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
2021-03-21 06:52:29 +00:00
|
|
|
OneHundredTwentyEight => {
|
|
|
|
let mut num = self.apply_rom_size_bitmask(self.rom_bank);
|
2021-01-20 07:39:24 +00:00
|
|
|
num &= !(0x03 << 5);
|
2021-03-21 06:52:29 +00:00
|
|
|
num | ((self.ram_bank) << 5)
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
2021-03-21 06:52:29 +00:00
|
|
|
_ => unreachable!("{:#?} is not a valid MBC1 BankCount", self.bank_count),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-03-21 06:52:29 +00:00
|
|
|
_ => unreachable!("{:#?} does not have a bitmask in MBC1", self.bank_count),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_ram_address(&self, addr: u16) -> u16 {
|
|
|
|
match self.ram_size {
|
|
|
|
RamSize::_2KB | RamSize::_8KB => {
|
|
|
|
let ram_size = self.ram_size.to_byte_count() as u16;
|
|
|
|
(addr - 0xA000) % ram_size
|
|
|
|
}
|
|
|
|
RamSize::_32KB => {
|
|
|
|
if self.mode {
|
|
|
|
0x2000 * self.ram_bank as u16 + (addr - 0xA000)
|
|
|
|
} else {
|
|
|
|
addr - 0xA000
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => unreachable!("RAM size can not be greater than 32KB on MBC1"),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
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 {
|
2021-03-21 06:52:29 +00:00
|
|
|
let zero_bank = self.calc_zero_bank_number() as u16;
|
|
|
|
Address(0x4000 * zero_bank + 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 => {
|
2021-03-21 06:52:29 +00:00
|
|
|
let high_bank = self.calc_high_bank_number() as u16;
|
|
|
|
Address(0x4000 * high_bank + (addr - 0x4000))
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
0xA000..=0xBFFF => {
|
|
|
|
if self.ram_enabled {
|
2021-03-21 06:52:29 +00:00
|
|
|
let ram_addr = self.calc_ram_address(addr);
|
2021-03-21 01:22:31 +00:00
|
|
|
Value(self.ram[ram_addr as usize])
|
2021-01-20 07:39:24 +00:00
|
|
|
} else {
|
2021-03-21 06:52:29 +00:00
|
|
|
Value(0xFF)
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-23 02:52:28 +00:00
|
|
|
_ => unreachable!("A read from {:#06X} should not be handled by MBC1", addr),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_write(&mut self, addr: u16, byte: u8) {
|
|
|
|
match addr {
|
|
|
|
0x0000..=0x1FFF => self.ram_enabled = (byte & 0x0F) == 0x0A,
|
|
|
|
0x2000..=0x3FFF => {
|
2021-03-21 06:52:29 +00:00
|
|
|
self.rom_bank = self.apply_rom_size_bitmask(byte);
|
2021-01-20 07:39:24 +00:00
|
|
|
|
2021-03-21 06:52:29 +00:00
|
|
|
if self.rom_bank == 0 {
|
|
|
|
self.rom_bank = 1;
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-21 06:52:29 +00:00
|
|
|
0x4000..=0x5FFF => self.ram_bank = byte & 0b11,
|
|
|
|
0x6000..=0x7FFF => self.mode = (byte & 0x01) == 0x01,
|
2021-01-20 07:39:24 +00:00
|
|
|
0xA000..=0xBFFF => {
|
|
|
|
if self.ram_enabled {
|
2021-03-21 06:52:29 +00:00
|
|
|
let ram_addr = self.calc_ram_address(addr);
|
2021-01-20 07:39:24 +00:00
|
|
|
self.ram[ram_addr as usize] = byte;
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 02:52:28 +00:00
|
|
|
_ => unreachable!("A write to {:#06X} should not be handled by MBC1", addr),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-03-16 00:08:19 +00:00
|
|
|
trait CloneMBC {
|
2021-03-21 01:22:31 +00:00
|
|
|
fn clone_mbc(&self) -> Box<dyn MemoryBankController>;
|
2021-03-16 00:08:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> CloneMBC for T
|
|
|
|
where
|
2021-03-21 01:22:31 +00:00
|
|
|
T: MemoryBankController + Clone + 'static,
|
2021-03-16 00:08:19 +00:00
|
|
|
{
|
2021-03-21 01:22:31 +00:00
|
|
|
fn clone_mbc<'a>(&self) -> Box<dyn MemoryBankController> {
|
2021-03-16 00:08:19 +00:00
|
|
|
Box::new(self.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 07:39:24 +00:00
|
|
|
enum MBCResult {
|
|
|
|
Address(u16),
|
2021-03-21 01:22:31 +00:00
|
|
|
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-03-21 06:52:29 +00:00
|
|
|
None = 0x00,
|
|
|
|
_2KB = 0x01,
|
|
|
|
_8KB = 0x02,
|
|
|
|
_32KB = 0x03, // Split into 4 RAM banks
|
|
|
|
_128KB = 0x04, // Split into 16 RAM banks
|
|
|
|
_64KB = 0x05, // Split into 8 RAm Banks
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
|
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,
|
2021-03-21 06:52:29 +00:00
|
|
|
0x04 => _128KB,
|
|
|
|
0x05 => _64KB,
|
2021-01-20 07:39:24 +00:00
|
|
|
_ => unreachable!("{:#04X} is an invalid value for RAMSize"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum BankCount {
|
2021-03-21 06:52:29 +00:00
|
|
|
None = 0x00, // 32KB
|
|
|
|
Four = 0x01, // 64KB
|
|
|
|
Eight = 0x02, // 128KB
|
|
|
|
Sixteen = 0x03, // 256KB
|
|
|
|
ThirtyTwo = 0x04, // 512KB
|
|
|
|
SixtyFour = 0x05, // 1MB
|
|
|
|
OneHundredTwentyEight = 0x06, // 2MB
|
|
|
|
TwoHundredFiftySix = 0x07, // 4MB
|
|
|
|
FiveHundredTwelve = 0x08, // 8MB
|
|
|
|
SeventyTwo = 0x52, // 1.1MB
|
|
|
|
Eighty = 0x53, // 1.2MB
|
|
|
|
NinetySix = 0x54, // 1.5MB
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for BankCount {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BankCount {
|
|
|
|
// https://hacktix.github.io/GBEDG/mbcs/#rom-size
|
2021-03-21 06:52:29 +00:00
|
|
|
pub fn to_rom_size(&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-03-21 06:52:29 +00:00
|
|
|
_ => unreachable!("{:#04X} is an invalid value for BankCount", byte),
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
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 {
|
2021-03-16 00:08:19 +00:00
|
|
|
todo!("Implement Debug for Box<dyn MBC> Trait Object");
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
impl std::clone::Clone for Box<dyn MemoryBankController> {
|
2021-01-20 07:39:24 +00:00
|
|
|
fn clone(&self) -> Self {
|
2021-03-16 00:08:19 +00:00
|
|
|
self.clone_mbc()
|
2021-01-20 07:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-21 01:22:31 +00:00
|
|
|
impl std::default::Default for Box<dyn MemoryBankController> {
|
2021-01-20 07:39:24 +00:00
|
|
|
fn default() -> Self {
|
2021-03-16 00:08:19 +00:00
|
|
|
Box::new(MBC1::default())
|
2020-12-24 01:39:37 +00:00
|
|
|
}
|
|
|
|
}
|