fix(cart): support cartridge headers w/ manufactor codes

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-11-25 00:12:35 -04:00
parent c10816c048
commit dda0257655
3 changed files with 87 additions and 11 deletions

View File

@ -9,6 +9,7 @@ const ROM_SIZE_ADDRESS: usize = 0x0148;
const MBC_TYPE_ADDRESS: usize = 0x0147;
const ROM_TITLE_START: usize = 0x134;
const ROM_TITLE_MAX_SIZE: usize = 16;
const ROM_MANUFACTURER_START: usize = 0x13F;
#[derive(Debug, Default)]
pub(crate) struct Cartridge {
@ -19,7 +20,10 @@ pub(crate) struct Cartridge {
impl Cartridge {
pub(crate) fn new(memory: Vec<u8>) -> Self {
let title = Self::find_title(&memory);
let title_mem: &[u8; 16] = memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE)]
.try_into()
.expect("coerce slice containing cartridge title from ROM to [u8; 16]");
let title = Self::detect_title(title_mem);
tracing::info!("Title: {:?}", title);
Self {
@ -65,18 +69,22 @@ impl Cartridge {
}
}
fn find_title(memory: &[u8]) -> Option<String> {
let title_bytes = &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE)];
fn detect_title(title_mem: &[u8; ROM_TITLE_MAX_SIZE]) -> Option<String> {
const ALT_TITLE_LEN: usize = ROM_MANUFACTURER_START - ROM_TITLE_START;
// ASCII Byte array purposely does not have null terminator
let ascii = match title_bytes.iter().position(|byte| *byte == 0x00) {
Some(end) => &memory[ROM_TITLE_START..(ROM_TITLE_START + end)],
None => &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE - 1)],
// byte slice we have here is purposely not null terminated
let ascii = match title_mem.iter().position(|b| *b == 0x00) {
Some(end) => &title_mem[0..end],
None => &title_mem[0..ROM_TITLE_MAX_SIZE],
};
match std::str::from_utf8(ascii).ok() {
Some("") | None => None,
Some(title) => Some(String::from(title)),
None => match std::str::from_utf8(&title_mem[0..ALT_TITLE_LEN]).ok() {
Some("") | None => None,
Some(title) => Some(String::from(title.trim())),
},
Some("") => None,
Some(title) => Some(String::from(title.trim())),
}
}
@ -912,3 +920,70 @@ trait Savable {
fn ext_ram(&self) -> Option<&[u8]>;
fn write_ext_ram(&mut self, memory: Vec<u8>);
}
#[cfg(test)]
mod tests {
use super::Cartridge;
#[test]
fn empty_rom_title() {
let title = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
assert_eq!(None, Cartridge::detect_title(&title));
}
#[test]
fn normal_rom_title() {
let title = [
0x50, 0x4F, 0x4B, 0x45, 0x4D, 0x4F, 0x4E, 0x20, 0x42, 0x4C, 0x55, 0x45, 0x00, 0x00,
0x00, 0x00,
];
assert_eq!(
Some(String::from("POKEMON BLUE")),
Cartridge::detect_title(&title)
);
}
#[test]
fn extra_spaces_title() {
let title = [
0x54, 0x4f, 0x4b, 0x49, 0x4d, 0x45, 0x4b, 0x49, 0x20, 0x43, 0x55, 0x4c, 0x20, 0x20, 0,
0,
];
assert_eq!(
Some(String::from("TOKIMEKI CUL")),
Cartridge::detect_title(&title)
)
}
#[test]
fn long_title() {
let title = [
0x54, 0x4f, 0x4b, 0x49, 0x4d, 0x45, 0x4b, 0x49, 0x20, 0x43, 0x55, 0x4c, 0x54, 0x55,
0x52, 0x45,
];
assert_eq!(
Some(String::from("TOKIMEKI CULTURE")),
Cartridge::detect_title(&title),
);
}
#[test]
fn publisher_code_and_title() {
let title: [u8; 16] = [
0x47, 0x52, 0x41, 0x4E, 0x44, 0x20, 0x54, 0x48, 0x45, 0x46, 0x54, 0x41, 0x4F, 0x41,
0x45, 0x80,
];
assert_eq!(
Some(String::from("GRAND THEFT")),
Cartridge::detect_title(&title)
);
}
}

View File

@ -16,7 +16,7 @@ use winit::event_loop::ControlFlow;
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
const DEFAULT_TITLE: &str = "DMG-01 Emulator";
const DEFAULT_TITLE: &str = "Game Boy Screen";
pub fn run_frame(cpu: &mut Cpu, gamepad: &mut Gilrs, key: KeyboardInput) -> Cycle {
let mut elapsed = 0;

View File

@ -15,6 +15,7 @@ use crate::{GB_HEIGHT, GB_WIDTH};
const EGUI_DIMENSIONS: (usize, usize) = (1280, 720);
const FILTER_MODE: FilterMode = FilterMode::Nearest;
const WINDOW_TITLE: &str = "DMG-01 Emulator";
const SCALE: f32 = 3.0;
@ -57,7 +58,7 @@ pub fn build_window<T>(event_loop: &EventLoop<T>) -> Result<Window, OsError> {
.with_decorations(true)
.with_resizable(true)
.with_transparent(false)
.with_title("DMG-01 Emulator")
.with_title(WINDOW_TITLE)
.with_inner_size(PhysicalSize {
width: EGUI_DIMENSIONS.0 as f32,
height: EGUI_DIMENSIONS.1 as f32,