mbc3 first pass - no rtc

This commit is contained in:
Alex Janka 2023-02-27 09:37:46 +11:00
parent 0536d19242
commit 0417f94518
3 changed files with 113 additions and 1 deletions

View file

@ -1,7 +1,7 @@
use crate::processor::memory::Address; use crate::processor::memory::Address;
use std::str::from_utf8_unchecked; use std::str::from_utf8_unchecked;
use self::mbcs::{Mbc, Mbc1, Mbc5, None}; use self::mbcs::{Mbc, Mbc1, Mbc3, Mbc5, None};
mod mbcs; mod mbcs;
@ -32,6 +32,14 @@ impl Rom {
println!("MBC1 w/battery - battery not implemented!"); println!("MBC1 w/battery - battery not implemented!");
Box::new(Mbc1::init(data, rom_size, ram_size, None)) Box::new(Mbc1::init(data, rom_size, ram_size, None))
} }
0x0F => panic!("MBC3 + RTC"),
0x1F => panic!("MBC3 + RTC + RAM"),
0x11 => Box::new(Mbc3::init(data, rom_size, 0)),
0x12 => Box::new(Mbc3::init(data, rom_size, ram_size)),
0x13 => {
println!("MBC3 w/battery - battery not implemented!");
Box::new(Mbc3::init(data, rom_size, 0))
}
0x19 => Box::new(Mbc5::init(data, rom_size, 0)), 0x19 => Box::new(Mbc5::init(data, rom_size, 0)),
0x1A => Box::new(Mbc5::init(data, rom_size, ram_size)), 0x1A => Box::new(Mbc5::init(data, rom_size, ram_size)),
0x1B => { 0x1B => {

View file

@ -1,9 +1,11 @@
use crate::processor::memory::Address; use crate::processor::memory::Address;
mod mbc1; mod mbc1;
mod mbc3;
mod mbc5; mod mbc5;
mod none; mod none;
pub use mbc1::Mbc1; pub use mbc1::Mbc1;
pub use mbc3::Mbc3;
pub use mbc5::Mbc5; pub use mbc5::Mbc5;
pub use none::None; pub use none::None;

View file

@ -0,0 +1,102 @@
use crate::processor::memory::Address;
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
pub struct Mbc3 {
data: Vec<u8>,
rom_bank: u8,
rom_size: usize,
ram: Option<Vec<u8>>,
ram_bank: u8,
ram_size: usize,
ram_enabled: bool,
}
impl Mbc3 {
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8) -> Self {
let ram = ram_size_kb(ram_size).map(|s| vec![0; s * KB]);
Self {
data,
rom_bank: 1,
rom_size: rom_banks(rom_size) * ROM_BANK_SIZE,
ram,
ram_bank: 0,
ram_size: ram_size_kb(ram_size).map_or(1, |s| s * KB),
ram_enabled: false,
}
}
fn get_rom_addr(&self, address: Address) -> usize {
(match address {
0x0..0x4000 => address as usize,
0x4000..0x8000 => {
let internal_addr = address as usize - 0x4000;
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
}
_ => panic!("address {address} incompatible with MBC5"),
} % self.rom_size)
}
fn get_ram_addr(&self, address: Address) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size
}
}
impl Mbc for Mbc3 {
fn get(&self, address: Address) -> u8 {
self.data[self.get_rom_addr(address)]
}
fn get_ram(&self, address: Address) -> u8 {
if self.ram_enabled && let Some(ram) = &self.ram {
ram[self.get_ram_addr(address)]
} else {
0xFF
}
}
fn set(&mut self, address: Address, data: u8) {
match address {
0x0..0x2000 => {
if data & 0xF == 0xA {
self.ram_enabled = true;
}
}
0x2000..0x4000 => {
self.rom_bank = data & 0b01111111;
if self.rom_bank == 0 {
self.rom_bank = 1;
}
}
0x4000..0x6000 => match data {
0x0..=0x03 => self.ram_bank = data,
0x08..=0x0C => panic!("rtc bank map: {data:#X}"),
_ => panic!("ram/rtc bank error: tried to map {data:#X}"),
},
0x6000..0x8000 => {
// RTC
}
_ => panic!("unsupported addr"),
}
}
fn set_ram(&mut self, address: Address, data: u8) {
let real_addr = self.get_ram_addr(address);
if self.ram_enabled && let Some(ram) = &mut self.ram {
ram[real_addr] = data;
}
}
fn mbc_type(&self) -> String {
if let Some(ram) = &self.ram {
format!(
"{}KB MBC3 with {}KB RAM",
self.rom_size / KB,
ram.len() / KB,
)
} else {
format!("{}KB MBC3", self.rom_size / KB)
}
}
}