Compare commits
No commits in common. "de0d1476857365bee7c565b81817a6f05cec9303" and "32b597a328f66e9b42456805ce579f50a51c651c" have entirely different histories.
de0d147685
...
32b597a328
132
src/cartridge.rs
132
src/cartridge.rs
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue