Add GameRom struct with MBC1 support and integrate with MemoryBus
Introduced `GameRom` struct and implemented support for MBC1 cartridges, including ROM and RAM banking. Modified `MemoryBus` and `CPU` to use `GameRom` for cartridge interactions, replacing raw game ROM handling. Added logic to load, read, and write cartridge memory based on type and address.
This commit is contained in:
131
src/main.rs
131
src/main.rs
@@ -1,8 +1,118 @@
|
||||
mod registers;
|
||||
|
||||
use std::ops::Index;
|
||||
use crate::registers::FlagsRegister;
|
||||
use crate::registers::Registers;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GameRom {
|
||||
data: Vec<u8>,
|
||||
title: String,
|
||||
cgb_flag: u8,
|
||||
licensee: [u8; 2],
|
||||
sgb_flag: u8,
|
||||
cartridge_type: CartridgeType,
|
||||
rom_size: u16,
|
||||
rom_bank_low: u8,
|
||||
rom_bank_high: u8,
|
||||
ram_size: u8,
|
||||
ram_enable: bool,
|
||||
banking_mode: bool,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum CartridgeType{
|
||||
RomOnly,
|
||||
MBC1,
|
||||
MBC2,
|
||||
MMM01,
|
||||
MBC3,
|
||||
MBC5,
|
||||
MBC6,
|
||||
MBC7,
|
||||
PocketCamera,
|
||||
BandaiTama5,
|
||||
HuC3,
|
||||
HuC1RamBattery,
|
||||
}
|
||||
|
||||
impl GameRom {
|
||||
fn load(filename: &str) -> GameRom {
|
||||
|
||||
let data = std::fs::read(filename).unwrap();
|
||||
let title_end = data[0x134..0x143].iter().position(|b| *b == 0x0).unwrap();
|
||||
let title = data[0x134..0x134 + title_end].to_vec().escape_ascii().to_string();
|
||||
let cgb_flag = data[0x143];
|
||||
if cgb_flag == 0xC0 {
|
||||
panic!("CGB not supported");
|
||||
}
|
||||
let licensee = [data[0x144], data[0x145]];
|
||||
let sgb_flag = data[0x146];
|
||||
println!("{:x}", data[0x147]);
|
||||
|
||||
let cartridge_type = match data[0x147] {
|
||||
0x00 => CartridgeType::RomOnly,
|
||||
0x01 => CartridgeType::MBC1,
|
||||
_ => {panic!("not a supported cartridge")}
|
||||
};
|
||||
let rom_size = match data[0x148] {
|
||||
0x00 => 2, // 0b1
|
||||
0x01 => 4, // 0b11
|
||||
0x02 => 8, // 0b111
|
||||
0x03 => 16, // 0b1111
|
||||
0x04 => 32, // 0b1111 1
|
||||
0x05 => 64, // 0b1111 11
|
||||
0x06 => 128,// 0b1111 111
|
||||
0x07 => 256,// 0b1111 1111
|
||||
0x08 => 512,// 0b1111 1111 1
|
||||
_ => { panic!("not a supported rom size"); }
|
||||
};
|
||||
let ram_size = data[0x149];
|
||||
println!("{rom_size}");
|
||||
GameRom{data, title, cgb_flag, licensee, sgb_flag, cartridge_type, rom_size, ram_size, rom_bank_low: 0, rom_bank_high: 0, ram_enable: false, banking_mode: false, }
|
||||
}
|
||||
fn read_byte(&self, addr: usize) -> u8 {
|
||||
match self.cartridge_type {
|
||||
CartridgeType::RomOnly => {self.data[addr]}
|
||||
CartridgeType::MBC1 => {
|
||||
match addr {
|
||||
0x0000..=0x3FFF => self.data[addr],
|
||||
0x4000..=0x7FFF => {
|
||||
let bank: u8;
|
||||
if self.rom_size > 32 {
|
||||
bank = self.rom_bank_low & 0b11111 | self.rom_bank_high << 5
|
||||
} else {
|
||||
bank = self.rom_bank_low & 0b11111;
|
||||
}
|
||||
let offset = (0x4000 * bank as usize) + (addr - 0x4000);
|
||||
self.data[offset]
|
||||
},
|
||||
_ => {panic!("unsupported memory access")}
|
||||
}
|
||||
}
|
||||
_ => {panic!("not a supported cartridge")}
|
||||
}
|
||||
}
|
||||
fn write_byte(&mut self, addr: usize, data: u8) {
|
||||
match self.cartridge_type {
|
||||
CartridgeType::RomOnly => {}
|
||||
CartridgeType::MBC1 => {
|
||||
match addr {
|
||||
0x0000..=0x1FFF => { self.ram_enable = data & 0xF == 0x0A;}
|
||||
0x2000..=0x3FFF => { self.rom_bank_low = data }
|
||||
0x4000..=0x5FFF => { self.rom_bank_high = data }
|
||||
0x6000..=0x7FFF => { self.banking_mode = data &0x1 == 0x1 }
|
||||
0xA000..=0xBFFF => { if self.ram_enable {
|
||||
self.data[addr] = data;
|
||||
} }
|
||||
_ => {panic!("unsupported Cartridge access")}
|
||||
}
|
||||
}
|
||||
_ => {panic!("unsupported Cartridge type")}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct CPU {
|
||||
registers: Registers,
|
||||
@@ -10,10 +120,11 @@ struct CPU {
|
||||
bus: MemoryBus,
|
||||
sp: u16,
|
||||
boot_rom: Vec<u8>,
|
||||
game_rom: Vec<u8>,
|
||||
}
|
||||
const VRAM_BEGIN: usize = 0x8000;
|
||||
const VRAM_END: usize = 0x9FFF;
|
||||
const CART_BEGIN: usize = 0x0000;
|
||||
const CART_END: usize = 0x7FFF;
|
||||
struct GPU { vram: Vec<u8>, tile_set: [[[TilePixelValue; 8]; 8]; 384] }
|
||||
#[derive(Copy, Clone)]
|
||||
enum TilePixelValue { Three, Two, One, Zero }
|
||||
@@ -87,6 +198,7 @@ impl GPU {
|
||||
struct MemoryBus {
|
||||
memory: [u8; 0xFFFF],
|
||||
gpu: GPU,
|
||||
rom: GameRom,
|
||||
}
|
||||
impl MemoryBus {
|
||||
fn read_byte(&self, address: u16) -> u8 {
|
||||
@@ -95,6 +207,7 @@ impl MemoryBus {
|
||||
VRAM_BEGIN ..= VRAM_END => {
|
||||
self.gpu.read_vram(address - VRAM_BEGIN)
|
||||
}
|
||||
CART_BEGIN ..= CART_END => {self.rom.read_byte(address)}
|
||||
_ => self.memory[address as usize]
|
||||
}
|
||||
}
|
||||
@@ -226,11 +339,6 @@ impl CPU {
|
||||
self.bus.write_byte(address as u16, *byte);
|
||||
}
|
||||
// println!("Game ROM: {:02X?}", self.game_rom);
|
||||
for (address, byte) in self.game_rom.iter().enumerate() {
|
||||
if address < 0x100 { continue; }
|
||||
if address >= 0x4000 { break; }
|
||||
self.bus.write_byte(address as u16, *byte);
|
||||
}
|
||||
|
||||
}
|
||||
fn check_condition(&self, condition: Condition) -> bool {
|
||||
@@ -1285,15 +1393,20 @@ impl CPU {
|
||||
|
||||
fn main() {
|
||||
let boot_rom = std::fs::read("boot/dmg.bin").unwrap();
|
||||
let game_rom = std::fs::read("cpu_instrs/cpu_instrs.gb").unwrap();
|
||||
let game_rom= GameRom::load("cpu_instrs/cpu_instrs.gb");
|
||||
|
||||
// let game_rom = std::fs::read("cpu_instrs/cpu_instrs.gb").unwrap();
|
||||
|
||||
let mut gameboy = CPU{
|
||||
registers:Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0},
|
||||
pc:0,
|
||||
bus:MemoryBus{ memory: [0xFFu8; 0xFFFF], gpu:GPU{ vram: Vec::with_capacity(VRAM_END-VRAM_BEGIN), tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] }},
|
||||
bus:MemoryBus{
|
||||
memory: [0xFFu8; 0xFFFF],
|
||||
gpu:GPU{ vram: Vec::with_capacity(VRAM_END-VRAM_BEGIN), tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] },
|
||||
rom: game_rom,
|
||||
},
|
||||
sp: 0,
|
||||
boot_rom,
|
||||
game_rom,
|
||||
};
|
||||
gameboy.init();
|
||||
let sp = gameboy.sp;
|
||||
|
||||
Reference in New Issue
Block a user