mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-27 03:31:31 +00:00
Basic MBC1 implementation (WIP)
This commit is contained in:
parent
62071d7310
commit
6da1b022d8
10
src/bus.rs
10
src/bus.rs
@ -57,10 +57,10 @@ pub struct Bus {
|
||||
|
||||
impl Bus {
|
||||
pub fn new() -> Self {
|
||||
let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("ignore/m3_scy_change.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.gb".to_string()) {
|
||||
let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) {
|
||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) {
|
||||
@ -107,9 +107,8 @@ impl Bus {
|
||||
}
|
||||
|
||||
pub fn read(&self, address: u16) -> u8 {
|
||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) || EXTERNAL_RAM.in_range(address) {
|
||||
return self.game_rom.read(address);
|
||||
|
||||
} else if address == INTERRUPT_ENABLE_ADDRESS || address == INTERRUPT_FLAG_ADDRESS {
|
||||
return 0b11100000 | self.data[address as usize];
|
||||
} else if address == JOYPAD_ADDRESS {
|
||||
@ -127,7 +126,8 @@ impl Bus {
|
||||
// print!("{}", data as char);
|
||||
}
|
||||
|
||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) || EXTERNAL_RAM.in_range(address) {
|
||||
self.game_rom.write(address, data);
|
||||
// println!("WRITING TO ROM");
|
||||
} else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) {
|
||||
self.data[address as usize] = data;
|
||||
|
@ -266,7 +266,7 @@ impl PPU {
|
||||
// The gameboy only supports 10 sprites per line,
|
||||
// but since we are on an emulator we can avoud that limitation
|
||||
if self.sprite_buffer.len() >= 10 {
|
||||
todo!("Make a setting for the 10 sprites per scanline");
|
||||
// todo!("Make a setting for the 10 sprites per scanline");
|
||||
// break;
|
||||
}
|
||||
let y = bus.read(addr);
|
||||
|
205
src/rom.rs
205
src/rom.rs
@ -1,24 +1,215 @@
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::bus::{
|
||||
BANK_SWITCHABLE,
|
||||
EXTERNAL_RAM,
|
||||
};
|
||||
|
||||
pub const CARTRIDGE_TYPE_ADDRESS: u16 = 0x0147;
|
||||
pub const CGB_FLAG_ADDRESS: u16 = 0x0143;
|
||||
pub const SGB_FLAG_ADDRESS: u16 = 0x0146;
|
||||
pub const RAM_SIZE_ADDRESS: u16 = 0x0149;
|
||||
pub const ROM_SIZE_ADDRESS: u16 = 0x0148;
|
||||
pub const DESTINATION_CODE_ADDRESS: u16 = 0x014A;
|
||||
|
||||
enum Region {
|
||||
Japanese,
|
||||
NonJapanese,
|
||||
}
|
||||
|
||||
enum MBC {
|
||||
NoMBC,
|
||||
MBC1,
|
||||
MBC2,
|
||||
MBC3,
|
||||
MBC4,
|
||||
MBC5,
|
||||
MBC6,
|
||||
MBC7,
|
||||
HuC1,
|
||||
HuC3,
|
||||
MMM01,
|
||||
MBC1M,
|
||||
PocketCamera,
|
||||
BandaiTIMA5,
|
||||
}
|
||||
|
||||
enum BankingMode {
|
||||
Simple,
|
||||
Advanced,
|
||||
}
|
||||
|
||||
pub struct ROMInfo {
|
||||
mbc: MBC,
|
||||
publisher: String,
|
||||
title: String,
|
||||
cgb_only: bool,
|
||||
sgb_features: bool,
|
||||
has_ram: bool,
|
||||
has_battery: bool,
|
||||
has_timer: bool,
|
||||
ram_banks: u8,
|
||||
rom_banks: u16,
|
||||
region: Region,
|
||||
}
|
||||
|
||||
impl ROMInfo {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Self {
|
||||
let rom_type = bytes[CARTRIDGE_TYPE_ADDRESS as usize];
|
||||
Self {
|
||||
mbc: match rom_type {
|
||||
0x00 => MBC::NoMBC,
|
||||
0x01 => MBC::MBC1,
|
||||
0x02 => MBC::MBC1,
|
||||
0x03 => MBC::MBC1,
|
||||
0x05 => MBC::MBC2,
|
||||
0x06 => MBC::MBC2,
|
||||
0x08 => MBC::NoMBC,
|
||||
0x09 => MBC::NoMBC,
|
||||
0x0B => MBC::MMM01,
|
||||
0x0C => MBC::MMM01,
|
||||
0x0F => MBC::MBC3,
|
||||
0x10 => MBC::MBC3,
|
||||
0x11 => MBC::MBC3,
|
||||
0x12 => MBC::MBC3,
|
||||
0x13 => MBC::MBC3,
|
||||
0x19 => MBC::MBC5,
|
||||
0x1A => MBC::MBC5,
|
||||
0x1B => MBC::MBC5,
|
||||
0x1C => MBC::MBC5,
|
||||
0x1D => MBC::MBC5,
|
||||
0x1E => MBC::MBC5,
|
||||
0x20 => MBC::MBC6,
|
||||
0x22 => MBC::MBC7,
|
||||
0xFC => MBC::PocketCamera,
|
||||
0xFD => MBC::BandaiTIMA5,
|
||||
0xFE => MBC::HuC3,
|
||||
0xFF => MBC::HuC1,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
region: match bytes[DESTINATION_CODE_ADDRESS as usize] {
|
||||
0x00 => Region::Japanese,
|
||||
_ => Region::NonJapanese,
|
||||
},
|
||||
publisher: "".to_string(), // TODO: Extract publisher
|
||||
title: "".to_string(), // TODO: Extract the game title
|
||||
cgb_only: bytes[CGB_FLAG_ADDRESS as usize] == 0xC0,
|
||||
sgb_features: bytes[SGB_FLAG_ADDRESS as usize] == 0x03,
|
||||
has_ram: match rom_type {
|
||||
0x02 | 0x03 | 0x08 | 0x09 | 0x0C | 0x0D | 0x10 | 0x12 |
|
||||
0x13 | 0x1A | 0x1B | 0x1D | 0x1E | 0x22 | 0xFF => true,
|
||||
_ => false,
|
||||
},
|
||||
has_battery: match rom_type {
|
||||
0x03 | 0x06 | 0x09 | 0x0D | 0x0F | 0x10 |
|
||||
0x13 | 0x1B | 0x1E | 0x22 | 0xFF => true,
|
||||
_ => false,
|
||||
},
|
||||
has_timer: match rom_type {
|
||||
0x0F | 0x10 => true,
|
||||
_ => false,
|
||||
},
|
||||
ram_banks: match bytes[RAM_SIZE_ADDRESS as usize] {
|
||||
0x00 | 0x01 => 0,
|
||||
0x02 => 1,
|
||||
0x03 => 4,
|
||||
0x04 => 16,
|
||||
0x05 => 8,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
rom_banks: match bytes[ROM_SIZE_ADDRESS as usize] {
|
||||
0x00 => 2,
|
||||
0x01 => 4,
|
||||
0x02 => 8,
|
||||
0x03 => 16,
|
||||
0x04 => 32,
|
||||
0x05 => 64,
|
||||
0x06 => 128,
|
||||
0x07 => 256,
|
||||
0x08 => 512,
|
||||
0x52 => 72,
|
||||
0x53 => 80,
|
||||
0x54 => 96,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ram_size(&self) -> u16 {
|
||||
0x4000 * self.ram_banks as u16
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ROM {
|
||||
bytes: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
info: ROMInfo,
|
||||
ram: Vec<u8>,
|
||||
rom_bank: u16,
|
||||
ram_bank: u8,
|
||||
ram_enable: bool,
|
||||
banking_mode: BankingMode,
|
||||
}
|
||||
|
||||
impl ROM {
|
||||
pub fn load_file(filename: String) -> std::io::Result<Self> {
|
||||
let mut file = File::open(filename)?;
|
||||
let mut bytes = vec![];
|
||||
file.read_to_end(&mut bytes)?;
|
||||
let mut data = vec![];
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
let info = ROMInfo::from_bytes(&data);
|
||||
let ram = Vec::with_capacity(info.ram_size() as usize);
|
||||
|
||||
Ok(Self {
|
||||
bytes,
|
||||
data,
|
||||
info,
|
||||
ram,
|
||||
rom_bank: 1,
|
||||
ram_bank: 0,
|
||||
ram_enable: false,
|
||||
banking_mode: BankingMode::Simple,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read(&self, address: u16) -> u8 {
|
||||
match self.bytes.get(address as usize) {
|
||||
Some(val) => *val,
|
||||
None => 0xFF,
|
||||
match self.info.mbc {
|
||||
MBC::MBC1 => {
|
||||
if BANK_SWITCHABLE.in_range(address) {
|
||||
return self.data[(address + (BANK_SWITCHABLE.begin() * (self.rom_bank - 1))) as usize]
|
||||
} else if EXTERNAL_RAM.in_range(address) {
|
||||
return self.ram[(address - EXTERNAL_RAM.begin() + (EXTERNAL_RAM.begin() * self.ram_bank as u16)) as usize]
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
self.data[address as usize]
|
||||
}
|
||||
|
||||
pub fn write(&mut self, address: u16, data: u8) {
|
||||
match self.info.mbc {
|
||||
MBC::MBC1 => {
|
||||
|
||||
if address >= 0x0000 || address <= 0x1FFF { // RAM enable register
|
||||
self.ram_enable = match data & 0x0F {
|
||||
0x0A => true,
|
||||
_ => false,
|
||||
};
|
||||
return;
|
||||
} else if address >= 0x2000 || address <= 0x3FFF { // ROM bank number register
|
||||
self.rom_bank = data as u16 & 0b00011111;
|
||||
if self.rom_bank > self.info.rom_banks.saturating_sub(1) {
|
||||
self.rom_bank = self.info.rom_banks.saturating_sub(1);
|
||||
}
|
||||
} else if address >= 0x4000 || address <= 0x5FFF { // ROM and RAM bank number register
|
||||
self.ram_bank = data & 0b11;
|
||||
} else if address >= 0x6000 || address <= 0x7FFF { // Banking mode select
|
||||
} else if EXTERNAL_RAM.in_range(address) {
|
||||
if !self.ram_enable {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user