rustyboy/src/cartridge.rs
Kevin Hamacher 6455732043 Add support for MBC1.
TODO: Refactor the cartridge code
2016-05-29 18:36:40 +02:00

227 lines
7.3 KiB
Rust

#[derive(Debug,PartialEq)]
enum MemoryBankControllerType {
None,
MBC1,
MBC2,
MBC3,
HuC1,
}
#[derive(Debug)]
enum RamSize {
None,
Ram2KB,
Ram8KB,
Ram32KB,
}
pub struct Cartridge {
rom: Box<[u8]>,
ram: Box<[u8]>,
bank_no: u8,
mbc_type: MemoryBankControllerType,
ram_rtc_enabled: bool,
ram_bank_no: u8,
ram_size: RamSize,
rom_banks: u16,
bank_mode: u8,
bank_no_high: u8,
}
impl Cartridge {
pub fn new(rom: Box<[u8]>) -> Cartridge {
let mbc: MemoryBankControllerType = match rom[0x0147] {
0x00 | 0x08 ... 0x09 => MemoryBankControllerType::None,
0x01 ... 0x03 => MemoryBankControllerType::MBC1,
0x05 ... 0x06 => MemoryBankControllerType::MBC2,
0x0F ... 0x13 => MemoryBankControllerType::MBC3,
0xFF => MemoryBankControllerType::HuC1,
_ => panic!("Unsupported MBC type: {:02X}", rom[0x0147]),
};
println!("MBC Type: {:?}", &mbc);
let rom_banks: u16 = match rom[0x0148] {
0x00 => 0,
0x01 ... 0x07 => 2u16.pow(rom[0x0148] as u32 + 1),
0x52 => 72,
0x53 => 80,
0x54 => 96,
_ => panic!("Unknown rom size: {:?}", rom[0x148])
};
let ram_size: RamSize = match rom[0x0149] {
0x00 => RamSize::None,
0x01 => RamSize::Ram2KB,
0x02 => RamSize::Ram8KB,
0x03 => RamSize::Ram32KB,
_ => panic!("Unknown ram size: {:?}", rom[0x149])
};
println!("Rom size: {} banks", rom_banks);
println!("Ram size: {:?}", ram_size);
let ram = match ram_size {
RamSize::None => vec![0u8; 0].into_boxed_slice(),
RamSize::Ram2KB => vec![0u8; 2048].into_boxed_slice(),
RamSize::Ram8KB => vec![0u8; 4 * 2048].into_boxed_slice(),
RamSize::Ram32KB => vec![0u8; 16 * 2048].into_boxed_slice(),
};
Cartridge {
rom: rom,
mbc_type: mbc,
bank_no: 1,
ram_rtc_enabled: false,
ram_bank_no: 0,
ram_size: ram_size,
rom_banks: rom_banks,
ram: ram,
bank_mode: 0,
bank_no_high: 0,
}
}
pub fn read_byte_mbc3(&self, addr: u16) -> u8 {
match addr {
0x0000 ... 0x3FFF => self.rom[addr as usize],
0x4000 ... 0x7FFF => {
let addr = addr - 0x4000;
println!("BankNo: {:02X}", self.bank_no);
let abs_addr: usize = addr as usize + self.bank_no as usize * 0x4000;
let val: u8 = self.rom[abs_addr];
val
},
0xA000 ... 0xBFFF => {
// TODO: Safty checks? Or let rust handle this?
let addr = addr - 0xA000;
if self.ram_bank_no < 4 {
self.ram[self.ram_bank_no as usize * 0x2000 + addr as usize]
} else {
println!("Ignoring RTC read");
0
}
}
_ => {
panic!("Cartride: Unable to read from {:04X}", addr);
}
}
}
pub fn read_byte_mbc1(&self, addr: u16) -> u8 {
match addr {
0x0000 ... 0x3FFF => self.rom[addr as usize],
0x4000 ... 0x7FFF => {
let addr = addr - 0x4000;
let mut bank: u8 = self.bank_no;
if self.bank_mode == 0 {
bank |= self.bank_no_high << 5;
}
println!("BankNo: {:02X}", bank);
let abs_addr: usize = addr as usize + bank as usize * 0x4000;
let val: u8 = self.rom[abs_addr];
val
},
0xA000 ... 0xBFFF => {
// TODO: Safty checks? Or let rust handle this?
let addr = addr - 0xA000;
let mut bank: u8 = 0;
if self.bank_mode == 1 {
bank = self.bank_no_high
}
self.ram[bank as usize * 0x2000 + addr as usize]
}
_ => {
panic!("Cartride: Unable to read from {:04X}", addr);
}
}
}
pub fn read_byte(&self, addr: u16) -> u8{
match self.mbc_type {
MemoryBankControllerType::MBC1 => self.read_byte_mbc1(addr),
MemoryBankControllerType::MBC3 | MemoryBankControllerType::None => self.read_byte_mbc3(addr),
_ => panic!("MBC not supported.")
}
}
fn write_byte_none(&mut self, addr: u16, val: u8) {
match addr {
0xA000 ... 0xBFFF => {
// self.ram[addr as usize - 0xA000] = val;
println!("No MBC, ignoring RAM write {:02X} to {:04X}", val, addr);
}
_ => println!("No MBC, unable to write {:02X} to {:04X}", val, addr),
}
}
fn write_byte_mbc1(&mut self, addr: u16, val: u8) {
match addr {
0x2000 ... 0x3FFF => {
// Write low bits of addr.
self.bank_no &= 0xE0;
self.bank_no |= val & 0x1F;
// Can't select 0x00, 0x20, 0x40, 0x60. Adapt it to 0x01, 0x21, 0x41, 0x61
if self.bank_no & 0x1F == 0 {
self.bank_no |= 1;
}
}
0x4000 ... 0x5FFF => {
self.bank_no_high = val & 3;
}
0x6000 ... 0x7FFF => {
self.bank_mode = val & 1;
}
_ => panic!("MBC1 Write {:02X} to {:04X} unsupported", val, addr),
}
}
fn write_byte_mbc3(&mut self, addr: u16, val: u8) {
match addr {
0x0000 ... 0x1FFF => {
match val {
0x0A => self.ram_rtc_enabled = true,
0x00 => self.ram_rtc_enabled = false,
_ => println!("Unknown MBC value {:02X} for {:04X}", val, addr)
}
},
0x2000 ... 0x3FFF => self.bank_no = val & 0x7F,
0x4000 ... 0x5FFF => {
// RAM bank select
match val {
0x00 ... 0x03 => self.ram_bank_no = val,
0x08 ... 0x0C => self.ram_bank_no = val, // RTC clock values, TODO
_ => panic!("Unknown MBC3 RAM BANK NO: {:02X}", val)
}
},
0x6000 ... 0x7FFF => {
// Latch clock data, ignore.
},
0xA000 ... 0xBFFF => {
// TODO: Safty checks? Or let rust handle this?
let addr = addr - 0xA000;
if self.ram_bank_no < 4 {
self.ram[self.ram_bank_no as usize * 0x2000 + addr as usize] = val;
} else {
println!("Ignoring RTC write");
}
}
_ => panic!("MBC3: Writing {:02X} to {:04X} not supported", val, addr),
}
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
match self.mbc_type {
MemoryBankControllerType::None => self.write_byte_none(addr, val),
MemoryBankControllerType::MBC1 => self.write_byte_mbc1(addr, val),
MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val),
_ => panic!("MBC not supported.")
}
}
}