mostly intact mbc1 impl

This commit is contained in:
Alex Janka 2023-02-11 21:43:36 +11:00
parent c442363cd4
commit d6276a1478
4 changed files with 119 additions and 24 deletions

View file

@ -1,4 +1,4 @@
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern, let_chains)]
mod processor; mod processor;

View file

@ -140,7 +140,7 @@ impl Memory {
} }
0xA000..0xC000 => { 0xA000..0xC000 => {
// cart ram // cart ram
0xFF self.rom.get_ram(address)
} }
0xC000..0xE000 => { 0xC000..0xE000 => {
return self.ram[(address - 0xC000) as usize]; return self.ram[(address - 0xC000) as usize];
@ -178,7 +178,7 @@ impl Memory {
self.vram[(address - 0x8000) as usize] = data; self.vram[(address - 0x8000) as usize] = data;
} }
0xA000..0xC000 => { 0xA000..0xC000 => {
// self.switchable_ram[(address - 0xA000) as usize] = data; self.rom.set_ram(address, data);
} }
0xC000..0xE000 => { 0xC000..0xE000 => {
self.ram[(address - 0xC000) as usize] = data; self.ram[(address - 0xC000) as usize] = data;

View file

@ -23,10 +23,15 @@ impl ROM {
let _gbc_flag = data[0x143]; let _gbc_flag = data[0x143];
let _sgb_flag = data[0x146]; let _sgb_flag = data[0x146];
let rom_size = data[0x148]; let rom_size = data[0x148];
let _ram_size = data[0x149]; let ram_size = data[0x149];
let mbc: Box<dyn MBC> = match data[0x147] { let mbc: Box<dyn MBC> = match data[0x147] {
0x00 => Box::new(NONE::init(data)), 0x00 => Box::new(NONE::init(data)),
0x01 => Box::new(MBC1::init(data, rom_size)), 0x01 => Box::new(MBC1::init(data, rom_size, 0, None)),
0x02 => Box::new(MBC1::init(data, rom_size, ram_size, None)),
0x03 => {
println!("MBC1 w/battery - battery not implemented!");
Box::new(MBC1::init(data, rom_size, ram_size, None))
}
_ => panic!("unimplemented mbc: {:#X}", data[0x147]), _ => panic!("unimplemented mbc: {:#X}", data[0x147]),
}; };
Self { title, mbc } Self { title, mbc }
@ -40,10 +45,18 @@ impl ROM {
self.mbc.get(address) self.mbc.get(address)
} }
pub(super) fn get_ram(&self, address: Address) -> u8 {
self.mbc.get_ram(address)
}
pub(super) fn set(&mut self, address: Address, data: u8) { pub(super) fn set(&mut self, address: Address, data: u8) {
self.mbc.set(address, data); self.mbc.set(address, data);
} }
pub(super) fn set_ram(&mut self, address: Address, data: u8) {
self.mbc.set_ram(address, data);
}
pub fn mbc_type(&self) -> &str { pub fn mbc_type(&self) -> &str {
self.mbc.mbc_type() self.mbc.mbc_type()
} }

View file

@ -2,7 +2,9 @@ use crate::processor::memory::Address;
pub(super) trait MBC { pub(super) trait MBC {
fn get(&self, address: Address) -> u8; fn get(&self, address: Address) -> u8;
fn get_ram(&self, address: Address) -> u8;
fn set(&mut self, address: Address, data: u8); fn set(&mut self, address: Address, data: u8);
fn set_ram(&mut self, address: Address, data: u8);
fn mbc_type(&self) -> &str; fn mbc_type(&self) -> &str;
} }
@ -21,6 +23,12 @@ impl MBC for NONE {
self.data[address as usize] self.data[address as usize]
} }
fn get_ram(&self, _address: Address) -> u8 {
0xFF
}
fn set_ram(&mut self, _address: Address, _data: u8) {}
fn set(&mut self, _address: Address, _data: u8) { fn set(&mut self, _address: Address, _data: u8) {
return; return;
} }
@ -32,21 +40,33 @@ impl MBC for NONE {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum BankingMode { enum BankingMode {
RomBanking, Simple,
RamBanking, Advanced,
} }
pub(super) struct MBC1 { pub(super) struct MBC1 {
pub(super) data: Vec<u8>, pub(super) data: Vec<u8>,
num_banks: usize, rom_len: usize,
bank_mode: BankingMode,
ram_enabled: bool,
rom_bank: u8, rom_bank: u8,
ram_enabled: bool,
ram: Option<Vec<u8>>,
ram_bank: u8,
upper_banks: u8,
bank_mode: BankingMode,
} }
const KB: usize = 1024;
const ROM_BANK_SIZE: usize = 16 * KB;
const RAM_BANK_SIZE: usize = 8 * KB;
impl MBC1 { impl MBC1 {
pub(super) fn init(data: Vec<u8>, rom_size: u8) -> Self { pub(super) fn init(
let num_banks = match rom_size { data: Vec<u8>,
rom_size: u8,
ram_size: u8,
_save_file: Option<Vec<u8>>,
) -> Self {
let rom_len = match rom_size {
0x00 => 2, 0x00 => 2,
0x01 => 4, 0x01 => 4,
0x02 => 8, 0x02 => 8,
@ -60,23 +80,48 @@ impl MBC1 {
0x53 => 80, 0x53 => 80,
0x54 => 96, 0x54 => 96,
_ => panic!("unacceptable rom size"), _ => panic!("unacceptable rom size"),
} * ROM_BANK_SIZE;
// in kb
let ram = match ram_size {
0x00 => None,
0x01 => Some(vec![0; 2 * KB]),
0x02 => Some(vec![0; 8 * KB]),
0x03 => Some(vec![0; 32 * KB]),
0x04 => Some(vec![0; 128 * KB]),
0x05 => Some(vec![0; 64 * KB]),
_ => panic!("unacceptable ram size"),
}; };
Self { Self {
data, data,
num_banks, rom_len,
bank_mode: BankingMode::RomBanking,
ram_enabled: false,
rom_bank: 0x1, rom_bank: 0x1,
ram_enabled: false,
ram,
ram_bank: 0,
upper_banks: 0,
bank_mode: BankingMode::Simple,
} }
} }
} }
impl MBC for MBC1 { impl MBC for MBC1 {
fn get(&self, address: Address) -> u8 { fn get(&self, address: Address) -> u8 {
match address { self.data[self.get_rom_addr(address)]
0x0..0x4000 => self.data[address as usize], }
0x4000..0x8000 => self.data[address as usize - (self.rom_bank as usize * 0x4000)],
_ => panic!("address too big for rom!"), fn get_ram(&self, address: Address) -> u8 {
if self.ram_enabled && let Some(ram) = &self.ram {
let addr = self.get_ram_addr(address)%ram.len();
return ram[addr];
}
0xFF
}
fn set_ram(&mut self, address: Address, data: u8) {
let mut addr = self.get_ram_addr(address);
if self.ram_enabled && let Some(ram) = &mut self.ram {
addr = addr % ram.len();
ram[addr] = data;
} }
} }
@ -92,25 +137,24 @@ impl MBC for MBC1 {
if set_data == 0 { if set_data == 0 {
set_data = 1; set_data = 1;
} }
self.rom_bank = (self.rom_bank & 0b11100000) | set_data; self.rom_bank = set_data;
} }
0x4000..0x6000 => { 0x4000..0x6000 => {
// ram bank OR upper bits 5 & 6 of rom bank // ram bank OR upper bits 5 & 6 of rom bank
self.rom_bank = (self.rom_bank & 0b00011111) | ((data & 0b00000011) << 5); self.upper_banks = data & 0b11;
} }
0x6000..0x8000 => { 0x6000..0x8000 => {
// mode select // mode select
self.bank_mode = if data == 0x1 { self.bank_mode = if data == 0x1 {
BankingMode::RamBanking BankingMode::Advanced
} else if data == 0x0 { } else if data == 0x0 {
BankingMode::RomBanking BankingMode::Simple
} else { } else {
self.bank_mode self.bank_mode
}; };
} }
_ => {} _ => {}
} }
self.rom_bank = (self.rom_bank as usize % self.num_banks) as u8;
return; return;
} }
@ -118,3 +162,41 @@ impl MBC for MBC1 {
"MBC1" "MBC1"
} }
} }
impl MBC1 {
fn get_rom_addr(&self, address: Address) -> usize {
(match address {
0x0..0x4000 => match self.bank_mode {
BankingMode::Simple => address as usize,
BankingMode::Advanced => {
(address as usize) + (self.upper_banks as usize * 512 * KB)
}
},
0x4000..0x8000 => {
(address - 0x4000) as usize
+ (ROM_BANK_SIZE * self.rom_bank as usize)
+ (self.upper_banks as usize * 512 * KB)
}
0xA000..0xC000 => panic!("passed ram address to rom address function"),
_ => panic!("address {address} incompatible with MBC1"),
} % self.rom_len)
}
fn get_ram_addr(&self, address: Address) -> usize {
match address {
0x0..0x8000 => panic!("passed rom address to ram address function"),
0xA000..0xC000 => match self.bank_mode {
BankingMode::Simple => {
(address - 0xA000) as usize + (RAM_BANK_SIZE * self.ram_bank as usize)
}
BankingMode::Advanced => {
(address - 0xA000) as usize
+ (RAM_BANK_SIZE * self.ram_bank as usize)
+ (self.upper_banks as usize * 16 * KB)
}
},
_ => panic!("address {address} incompatible with MBC1"),
}
}
}