Newer
Older
//! Cartridge (ROM) related functions and structures.
use std::{
cmp::max,
fmt::{Display, Formatter},
cheats::{genie::GameGenie, shark::GameShark},
debugln,
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
pub const ROM_BANK_SIZE: usize = 16384;
pub const RAM_BANK_SIZE: usize = 8192;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum MbcType {
NoMbc = 0x00,
Mbc1 = 0x01,
Mbc2 = 0x02,
Mbc3 = 0x03,
Mbc5 = 0x04,
Mbc6 = 0x05,
Mbc7 = 0x06,
Unknown = 0x07,
}
impl MbcType {
pub fn ram_bank_mask(&self) -> u8 {
match self {
MbcType::NoMbc => 0x00,
MbcType::Mbc1 => 0x03,
MbcType::Mbc2 => unimplemented!("MBC2 is not supported"),
MbcType::Mbc3 => 0x03,
MbcType::Mbc5 => 0x0f,
MbcType::Mbc6 => unimplemented!("MBC6 is not supported"),
MbcType::Mbc7 => unimplemented!("MBC7 is not supported"),
MbcType::Unknown => unimplemented!(),
}
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RomType {
RomOnly = 0x00,
Mbc1 = 0x01,
Mbc1Ram = 0x02,
Mbc1RamBattery = 0x03,
Mbc2 = 0x05,
Mbc2Battery = 0x06,
RomRam = 0x08,
RomRamBattery = 0x09,
Mmm01 = 0x0b,
Mmm01Ram = 0x0c,
Mmm01RamBattery = 0x0d,
Mbc3TimerBattery = 0x0f,
Mbc3TimerRamBattery = 0x10,
Mbc3 = 0x11,
Mbc3Ram = 0x12,
Mbc3RamBattery = 0x13,
Mbc5 = 0x19,
Mbc5Ram = 0x1a,
Mbc5RamBattery = 0x1b,
Mbc5Rumble = 0x1c,
Mbc5RumbleRam = 0x1d,
Mbc5RumbleRamBattery = 0x1e,
Mbc6 = 0x20,
Mbc7SensorRumbleRamBattery = 0x22,
PocketCamera = 0xfc,
BandaiTama5 = 0xfd,
HuC3 = 0xfe,
HuC1RamBattery = 0xff,
Unknown = 0xef,
impl RomType {
pub fn description(&self) -> &'static str {
match self {
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
RomType::Mbc1 => "MBC1",
RomType::Mbc1Ram => "MBC1 + RAM",
RomType::Mbc1RamBattery => "MBC1 + RAM + Battery",
RomType::Mbc2 => "MBC2",
RomType::Mbc2Battery => "MBC2 + RAM",
RomType::RomRam => "ROM + RAM",
RomType::RomRamBattery => "ROM + RAM + BATTERY",
RomType::Mmm01 => "MMM01",
RomType::Mmm01Ram => "MMM01 + RAM",
RomType::Mmm01RamBattery => "MMM01 + RAM + BATTERY",
RomType::Mbc3TimerBattery => "MBC3 + TIMER + BATTERY",
RomType::Mbc3TimerRamBattery => "MBC3 + TIMER + RAM + BATTERY",
RomType::Mbc3 => "MBC3",
RomType::Mbc3Ram => "MBC3 + RAM",
RomType::Mbc3RamBattery => "MBC3 + RAM + BATTERY",
RomType::Mbc5 => "MBC5",
RomType::Mbc5Ram => "MBC5 + RAM",
RomType::Mbc5RamBattery => "MBC5 + RAM + BATTERY",
RomType::Mbc5Rumble => "MBC5 + RUMBLE",
RomType::Mbc5RumbleRam => "MBC5 + RUMBLE + RAM",
RomType::Mbc5RumbleRamBattery => "MBC5 + RUMBLE + RAM + BATTERY",
RomType::Mbc6 => "MBC6",
RomType::Mbc7SensorRumbleRamBattery => "MBC6 + SENSOR + RUMBLE + RAM + BATTERY",
RomType::PocketCamera => "POCKET CAMERA",
RomType::BandaiTama5 => "BANDAI TAMA5",
RomType::HuC3 => "HuC3",
RomType::HuC1RamBattery => "HuC1 + RAM + BATTERY",
pub fn mbc_type(&self) -> MbcType {
match self {
RomType::RomOnly => MbcType::NoMbc,
RomType::Mbc1 | RomType::Mbc1Ram | RomType::Mbc1RamBattery => MbcType::Mbc1,
RomType::Mbc2 | RomType::Mbc2Battery => MbcType::Mbc2,
RomType::Mbc3
| RomType::Mbc3Ram
| RomType::Mbc3RamBattery
| RomType::Mbc3TimerBattery
| RomType::Mbc3TimerRamBattery => MbcType::Mbc3,
RomType::Mbc5
| RomType::Mbc5Ram
| RomType::Mbc5RamBattery
| RomType::Mbc5Rumble
| RomType::Mbc5RumbleRam
| RomType::Mbc5RumbleRamBattery => MbcType::Mbc5,
RomType::Mbc6 => MbcType::Mbc6,
RomType::Mbc7SensorRumbleRamBattery => MbcType::Mbc7,
_ => MbcType::Unknown,
}
}
}
impl Display for RomType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
Size32K,
Size64K,
Size128K,
pub fn description(&self) -> &'static str {
match self {
RomSize::Size32K => "32 KB",
RomSize::Size64K => "64 KB",
RomSize::Size128K => "128 KB",
RomSize::Size256K => "256 KB",
RomSize::Size512K => "512 KB",
RomSize::Size1M => "1 MB",
RomSize::Size2M => "2 MB",
RomSize::Size4M => "4 MB",
RomSize::Size8M => "8 MB",
RomSize::SizeUnknown => "Unknown",
}
}
pub fn rom_banks(&self) -> u16 {
match self {
RomSize::Size32K => 2,
RomSize::Size64K => 4,
RomSize::Size128K => 8,
RomSize::Size256K => 16,
RomSize::Size512K => 32,
RomSize::Size1M => 64,
RomSize::Size2M => 128,
RomSize::Size4M => 256,
RomSize::Size8M => 512,
RomSize::SizeUnknown => 0,
}
}
}
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RamSize {
NoRam,
Unused,
Size8K,
Size32K,
Size64K,
Size128K,
SizeUnknown,
}
pub fn description(&self) -> &'static str {
match self {
RamSize::NoRam => "No RAM",
RamSize::Unused => "Unused",
RamSize::Size8K => "8 KB",
RamSize::Size32K => "32 KB",
RamSize::Size128K => "128 KB",
RamSize::Size64K => "64 KB",
RamSize::SizeUnknown => "Unknown",
}
}
pub fn ram_banks(&self) -> u16 {
match self {
RamSize::NoRam => 0,
RamSize::Unused => 0,
RamSize::Size8K => 1,
RamSize::Size32K => 4,
RamSize::Size64K => 8,
RamSize::Size128K => 16,
RamSize::SizeUnknown => 0,
}
}
}
impl Display for RamSize {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
pub enum Region {
World,
Japan,
USA,
Europe,
Spain,
Italy,
France,
Germany,
Korean,
Australia,
Unknown,
}
impl Region {
pub fn description(&self) -> &'static str {
match self {
Region::World => "World",
Region::Japan => "Japan",
Region::USA => "USA",
Region::Europe => "Europe",
Region::Spain => "Spain",
Region::Italy => "Italy",
Region::France => "France",
Region::Germany => "Germany",
Region::Korean => "Korea",
Region::Australia => "Australia",
Region::Unknown => "Unknown",
}
}
}
impl Display for Region {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CgbMode {
NoCgb = 0x00,
CgbCompatible = 0x80,
CgbOnly = 0xc0,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SgbMode {
NoSgb = 0x00,
SgbFunctions = 0x03,
}
impl CgbMode {
pub fn description(&self) -> &'static str {
match self {
CgbMode::NoCgb => "No CGB support",
CgbMode::CgbCompatible => "CGB backwards compatible",
CgbMode::CgbOnly => "CGB only",
}
}
}
impl Display for CgbMode {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.description())
}
}
/// Structure that defines the ROM and ROM contents
/// of a Game Boy cartridge. Should correctly address
/// the specifics of all the major MBCs (Memory Bank
/// Controllers).
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone)]
pub struct Cartridge {
/// The complete data of the ROM cartridge, should
/// include the complete set o ROM banks.
/// The base RAM that is going to be used to store
/// temporary data for basic cartridges.
/// The MBC (Memory Bank Controller) to be used for
/// RAM and ROM access on the current cartridge.
mbc: &'static Mbc,
/// The current memory handler in charge of handling the
/// memory access for the current cartridge.
/// Typically this is the same as the MBC, but to allow
/// memory patching (ex: Game Genie) we may need another
/// level of indirection.
handler: &'static Mbc,
/// The number of ROM banks (of 8KB) that are available
/// to the current cartridge, this is a computed value
/// to allow improved performance.
rom_bank_count: u16,
/// The number of RAM banks (of 8KB) that are available
/// to the current cartridge, this is a computed value
/// to allow improved performance.
ram_bank_count: u16,
/// The offset address to the ROM bank (#1) that is
/// currently in use by the ROM cartridge.
rom_offset: usize,
/// The offset address to the ERAM bank that is
/// currently in use by the ROM cartridge.
ram_offset: usize,
/// If the RAM access ia enabled, this flag allows
/// control of memory access to avoid corruption.
ram_enabled: bool,
/// The final offset of the last character of the title
/// that is considered to be non zero (0x0) so that a
/// proper safe conversion to UTF-8 string can be done.
/// The current rumble state of the cartridge, this
/// boolean value controls if vibration is currently active.
rumble_active: bool,
/// Callback function to be called whenever there's a new
/// rumble vibration triggered or when it's disabled.
rumble_cb: fn(active: bool),
/// Optional reference to the Game Genie instance that
/// would be used for the "cheating" by patching the
/// current ROM's cartridge data.
game_genie: Option<GameGenie>,
/// Optional reference to the GameShark instance that
/// would be used for the "cheating" by patching the
/// current ROM's cartridge data.
game_shark: Option<GameShark>,
}
impl Cartridge {
pub fn new() -> Self {
pub fn from_data(data: &[u8]) -> Result<Self, Error> {
cartridge.set_data(data)?;
Ok(cartridge)
pub fn from_file(path: &str) -> Result<Self, Error> {
Loading
Loading full blame...