Compare commits

..

No commits in common. "de0d1476857365bee7c565b81817a6f05cec9303" and "32b597a328f66e9b42456805ce579f50a51c651c" have entirely different histories.

1 changed files with 60 additions and 72 deletions

View File

@ -7,13 +7,12 @@ use crate::bus::BusIo;
const RAM_SIZE_ADDRESS: usize = 0x0149; const RAM_SIZE_ADDRESS: usize = 0x0149;
const ROM_SIZE_ADDRESS: usize = 0x0148; const ROM_SIZE_ADDRESS: usize = 0x0148;
const MBC_TYPE_ADDRESS: usize = 0x0147; const MBC_TYPE_ADDRESS: usize = 0x0147;
const ROM_TITLE_RANGE: std::ops::RangeInclusive<usize> = 0x0134..=0x0143;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Cartridge { pub(crate) struct Cartridge {
memory: Vec<u8>, memory: Vec<u8>,
title: Option<String>, title: Option<String>,
mbc: Box<dyn MBCIo>, mbc: Box<dyn MemoryBankController>,
} }
impl Cartridge { impl Cartridge {
@ -32,7 +31,7 @@ impl Cartridge {
}) })
} }
fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> { fn detect_mbc(memory: &[u8]) -> Box<dyn MemoryBankController> {
let ram_size = Self::find_ram_size(memory); let ram_size = Self::find_ram_size(memory);
let bank_count = Self::find_bank_count(memory); let bank_count = Self::find_bank_count(memory);
let mbc_kind = Self::find_mbc(memory); let mbc_kind = Self::find_mbc(memory);
@ -43,9 +42,9 @@ impl Cartridge {
eprintln!("MBC Type: {:?}", mbc_kind); eprintln!("MBC Type: {:?}", mbc_kind);
match mbc_kind { match mbc_kind {
MBCKind::None => Box::new(NoMBC {}), MbcKind::None => Box::new(NoMbc {}),
MBCKind::MBC1 => { MbcKind::Mbc1 => {
let mbc = MBC1 { let mbc = Mbc1 {
ram_size, ram_size,
ram: vec![0; ram_byte_count as usize], ram: vec![0; ram_byte_count as usize],
bank_count, bank_count,
@ -54,9 +53,9 @@ impl Cartridge {
Box::new(mbc) Box::new(mbc)
} }
MBCKind::MBC1WithBattery => { MbcKind::Mbc1WithBattery => {
// TODO: Implement Saving // TODO: Implement Saving
let mbc = MBC1 { let mbc = Mbc1 {
ram_size, ram_size,
ram: vec![0; ram_byte_count as usize], ram: vec![0; ram_byte_count as usize],
bank_count, bank_count,
@ -65,7 +64,7 @@ impl Cartridge {
Box::new(mbc) Box::new(mbc)
} }
MBCKind::MBC3WithBattery => { MbcKind::Mbc3WithBattery => {
// TODO: Implement Saving // TODO: Implement Saving
let mbc = MBC3 { let mbc = MBC3 {
ram_size, ram_size,
@ -75,7 +74,7 @@ impl Cartridge {
Box::new(mbc) Box::new(mbc)
} }
MBCKind::MBC3 => { MbcKind::Mbc3 => {
let mbc = MBC3 { let mbc = MBC3 {
ram_size, ram_size,
ram: vec![0; ram_byte_count as usize], ram: vec![0; ram_byte_count as usize],
@ -84,19 +83,19 @@ impl Cartridge {
Box::new(mbc) Box::new(mbc)
} }
MBCKind::MBC5 => todo!("Implement MBC5"), MbcKind::Mbc5 => todo!("Implement MBC5"),
} }
} }
fn find_title(memory: &[u8]) -> Option<String> { fn find_title(memory: &[u8]) -> Option<String> {
let slice = &memory[ROM_TITLE_RANGE]; // FIXME: Get rid of magic values and handle cases
let with_nulls = std::str::from_utf8(slice).ok(); // where 0x134..0x143 reads past the length of the
let trimmed = with_nulls.map(|s| s.trim_matches('\0')); // string
match trimmed { let slice = &memory[0x134..0x143];
Some("") | None => None,
Some(_) => trimmed.map(String::from), let str_with_nulls = std::str::from_utf8(slice).ok();
} str_with_nulls.map(|s| s.trim_matches('\0').to_string())
} }
pub(crate) fn title(&self) -> Option<&str> { pub(crate) fn title(&self) -> Option<&str> {
@ -113,18 +112,18 @@ impl Cartridge {
id.into() id.into()
} }
fn find_mbc(memory: &[u8]) -> MBCKind { fn find_mbc(memory: &[u8]) -> MbcKind {
let id = memory[MBC_TYPE_ADDRESS]; let id = memory[MBC_TYPE_ADDRESS];
// TODO: Refactor this to match the other enums in this module // TODO: Refactor this to match the other enums in this module
match id { match id {
0x00 => MBCKind::None, 0x00 => MbcKind::None,
0x01 => MBCKind::MBC1, 0x01 => MbcKind::Mbc1,
0x02 => MBCKind::MBC1, 0x02 => MbcKind::Mbc1,
0x03 => MBCKind::MBC1WithBattery, 0x03 => MbcKind::Mbc1WithBattery,
0x19 => MBCKind::MBC5, 0x19 => MbcKind::Mbc5,
0x13 => MBCKind::MBC3WithBattery, 0x13 => MbcKind::Mbc3WithBattery,
0x11 => MBCKind::MBC3, 0x11 => MbcKind::Mbc3,
_ => unimplemented!("id {:#04X} is an unsupported MBC", id), _ => unimplemented!("id {:#04X} is an unsupported MBC", id),
} }
} }
@ -132,7 +131,7 @@ impl Cartridge {
impl BusIo for Cartridge { impl BusIo for Cartridge {
fn read_byte(&self, addr: u16) -> u8 { fn read_byte(&self, addr: u16) -> u8 {
use MBCResult::*; use MbcResult::*;
match self.mbc.handle_read(addr) { match self.mbc.handle_read(addr) {
Address(addr) => self.memory[addr], Address(addr) => self.memory[addr],
@ -146,7 +145,7 @@ impl BusIo for Cartridge {
} }
#[derive(Debug)] #[derive(Debug)]
struct MBC1 { struct Mbc1 {
/// 5-bit number /// 5-bit number
rom_bank: u8, rom_bank: u8,
/// 2-bit number /// 2-bit number
@ -158,7 +157,7 @@ struct MBC1 {
ram_enabled: bool, ram_enabled: bool,
} }
impl Default for MBC1 { impl Default for Mbc1 {
fn default() -> Self { fn default() -> Self {
Self { Self {
rom_bank: 0x01, rom_bank: 0x01,
@ -172,7 +171,7 @@ impl Default for MBC1 {
} }
} }
impl MBC1 { impl Mbc1 {
fn zero_bank(&self) -> u8 { fn zero_bank(&self) -> u8 {
use BankCount::*; use BankCount::*;
@ -238,9 +237,9 @@ impl MBC1 {
} }
} }
impl MBCIo for MBC1 { impl MemoryBankController for Mbc1 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MbcResult {
use MBCResult::*; use MbcResult::*;
match addr { match addr {
0x0000..=0x3FFF => { 0x0000..=0x3FFF => {
@ -305,14 +304,11 @@ struct MBC3 {
currently_mapped: Option<MBC3Device>, currently_mapped: Option<MBC3Device>,
ram_size: RamSize, ram_size: RamSize,
ram: Vec<u8>, ram: Vec<u8>,
// RTC Data Latch Previous Write
prev_latch_write: Option<u8>,
} }
impl MBCIo for MBC3 { impl MemoryBankController for MBC3 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MbcResult {
use MBCResult::*; use MbcResult::*;
let res = match addr { let res = match addr {
0x0000..=0x3FFF => Address(addr as usize), 0x0000..=0x3FFF => Address(addr as usize),
@ -322,7 +318,7 @@ impl MBCIo for MBC3 {
Value(self.ram[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)]) Value(self.ram[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)])
} }
Some(MBC3Device::RealTimeClock) if self.devices_enabled => { Some(MBC3Device::RealTimeClock) if self.devices_enabled => {
todo!("Return Latched value of register") unimplemented!("Reading from MBC3 RTC is currently unsupported")
} }
_ => Value(0xFF), _ => Value(0xFF),
}, },
@ -335,12 +331,10 @@ impl MBCIo for MBC3 {
fn handle_write(&mut self, addr: u16, byte: u8) { fn handle_write(&mut self, addr: u16, byte: u8) {
match addr { match addr {
0x000..=0x1FFF => self.devices_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one 0x000..=0x1FFF => self.devices_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one
0x2000..=0x3FFF => { 0x2000..=0x3FFF => match byte {
self.rom_bank = match byte { 0x00 => self.rom_bank = 0x01,
0x00 => 0x01, byte => self.rom_bank = byte & 0x7F,
byte => byte & 0x7F, },
}
}
0x4000..=0x5FFF => match byte { 0x4000..=0x5FFF => match byte {
0x00 | 0x01 | 0x02 | 0x03 => { 0x00 | 0x01 | 0x02 | 0x03 => {
self.ram_bank = byte & 0x03; self.ram_bank = byte & 0x03;
@ -348,23 +342,17 @@ impl MBCIo for MBC3 {
} }
0x08 | 0x09 | 0x0A | 0x0B | 0x0C => { 0x08 | 0x09 | 0x0A | 0x0B | 0x0C => {
self.currently_mapped = Some(MBC3Device::RealTimeClock); self.currently_mapped = Some(MBC3Device::RealTimeClock);
unimplemented!("RTC in MBC3 is currently unimplemented")
} }
_ => {} _ => {}
}, },
0x6000..=0x7FFF => { 0x6000..=0x7FFF => unimplemented!("RTC Data Latch is currently unimplemented"),
if let Some(0x00) = self.prev_latch_write {
if byte == 0x01 {
todo!("Perform Data Latch")
}
}
self.prev_latch_write = Some(byte);
}
0xA000..=0xBFFF => match self.currently_mapped { 0xA000..=0xBFFF => match self.currently_mapped {
Some(MBC3Device::ExternalRam) if self.devices_enabled => { Some(MBC3Device::ExternalRam) if self.devices_enabled => {
self.ram[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte self.ram[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte
} }
Some(MBC3Device::RealTimeClock) if self.devices_enabled => { Some(MBC3Device::RealTimeClock) if self.devices_enabled => {
todo!("Write to RTC") unimplemented!("Writing to MBC3 RTC is currently unsupported")
} }
_ => {} _ => {}
}, },
@ -374,11 +362,11 @@ impl MBCIo for MBC3 {
} }
#[derive(Debug)] #[derive(Debug)]
struct NoMBC {} struct NoMbc {}
impl MBCIo for NoMBC { impl MemoryBankController for NoMbc {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MbcResult {
MBCResult::Address(addr as usize) MbcResult::Address(addr as usize)
} }
fn handle_write(&mut self, _addr: u16, _byte: u8) { fn handle_write(&mut self, _addr: u16, _byte: u8) {
@ -386,28 +374,28 @@ impl MBCIo for NoMBC {
} }
} }
trait MBCIo { trait MemoryBankController {
fn handle_read(&self, addr: u16) -> MBCResult; fn handle_read(&self, addr: u16) -> MbcResult;
fn handle_write(&mut self, addr: u16, byte: u8); fn handle_write(&mut self, addr: u16, byte: u8);
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum MBCResult { enum MbcResult {
Address(usize), Address(usize),
Value(u8), Value(u8),
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum MBCKind { enum MbcKind {
None, None,
MBC1, Mbc1,
MBC1WithBattery, Mbc1WithBattery,
MBC5, Mbc5,
MBC3WithBattery, Mbc3WithBattery,
MBC3, Mbc3,
} }
impl Default for MBCKind { impl Default for MbcKind {
fn default() -> Self { fn default() -> Self {
Self::None Self::None
} }
@ -526,14 +514,14 @@ impl From<u8> for BankCount {
} }
} }
impl std::fmt::Debug for Box<dyn MBCIo> { impl std::fmt::Debug for Box<dyn MemoryBankController> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!("Implement Debug for Box<dyn MBC> Trait Object"); todo!("Implement Debug for Box<dyn MBC> Trait Object");
} }
} }
impl Default for Box<dyn MBCIo> { impl Default for Box<dyn MemoryBankController> {
fn default() -> Self { fn default() -> Self {
Box::new(MBC1::default()) Box::new(Mbc1::default())
} }
} }