diff --git a/src/components/cpu.rs b/src/components/cpu.rs index 03e055b..73b29eb 100644 --- a/src/components/cpu.rs +++ b/src/components/cpu.rs @@ -4,6 +4,7 @@ use std::process; use crate::config::Config; use crate::components::prelude::*; use crate::Framebuffer; +use crate::mbc::header::Header; pub struct CPU { pub reg: Registers, @@ -18,6 +19,7 @@ unsafe impl Send for CPU {} impl CPU { pub fn new(rom: Vec, + header: Header, config: Config, framebuffer: Framebuffer) -> Self { let mut boot_rom: [u8; 0x900] = [0; 0x900]; @@ -41,7 +43,7 @@ impl CPU { Self { reg: Registers::new(config.mode), - mem: MMU::new(rom, config, boot_rom, framebuffer), + mem: MMU::new(rom, header, config, boot_rom, framebuffer), halted: false, ime: false, ime_ask: false diff --git a/src/components/mmu.rs b/src/components/mmu.rs index b442e3d..7050bb1 100644 --- a/src/components/mmu.rs +++ b/src/components/mmu.rs @@ -4,8 +4,8 @@ use crate::mbc::prelude::*; use crate::sound::apu::APU; use crate::Framebuffer; use bitflags::bitflags; -use num_traits::FromPrimitive; use crate::components::prelude::ppu::PPU; +use crate::mbc::header::Header; pub struct MMU { mbc: Box, @@ -37,16 +37,13 @@ bitflags! { impl MMU { pub fn new(rom: Vec, + header: Header, config: Config, boot_rom: [u8; 0x900], framebuffer: Framebuffer) -> Self { - let cart_type: CartTypes = FromPrimitive::from_u8(rom[0x0147]).expect("Failed to get Cart Type!"); - let mbc_mode = match cart_type.get_mbc() { - MBCMode::Unsupported => panic!("Unsupported Cart Type! {:}", cart_type), - v => { - println!("Cart Type: {:}, MBC Type: {:}", cart_type, v); - v - } + let mbc_mode = match header.cart_type.get_mbc() { + MBCMode::Unsupported => panic!("Unsupported Cart Type! {:}", header.cart_type), + v => v }; let mbc: Box = match mbc_mode { diff --git a/src/main.rs b/src/main.rs index a159477..67cf7dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use winit::event_loop::EventLoop; use winit::application::ApplicationHandler; use winit::window::{Window, WindowId}; use crate::components::prelude::ppu::FRAMEBUFFER_SIZE; +use crate::mbc::header::{CGBFlag, Header}; type Framebuffer = Arc>; @@ -42,7 +43,7 @@ struct Args { } struct App { - game_name: String, + header: Header, context: Option>>, config: Config, input_tx: Sender<(JoypadButton, bool)>, @@ -52,7 +53,7 @@ struct App { impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window_attributes = Window::default_attributes() - .with_title(format!("tetsuyu - {:}", self.game_name)) + .with_title(format!("tetsuyu - {:}", self.header.title)) .with_inner_size(winit::dpi::LogicalSize::new( self.config.window_w, self.config.window_h, @@ -152,9 +153,12 @@ fn main() { let mut buffer = Vec::new(); file.read_to_end(&mut buffer).expect("Failed to read ROM!"); - // Get game name - let game_name = String::from_utf8_lossy(&buffer[0x0134..=0x0143]); - println!("Starting \"{}\" in {:?} Mode...", game_name, config.mode); + let header = Header::new(buffer.clone()); + println!("{}", header); + + assert!((header.cgb_flag != CGBFlag::CGBOnly) || (config.mode != GBMode::DMG), "Cannot run CGB only game in DMG Mode!"); + // TODO: DMG-on-CGB Compat Mode + assert!((header.cgb_flag != CGBFlag::DMGOnly) || (config.mode != GBMode::CGB), "Cannot run DMG only game in CGB Mode!"); let panic = std::panic::take_hook(); std::panic::set_hook(Box::new(move |info| { @@ -169,7 +173,7 @@ fn main() { let framebuffer: Framebuffer = Arc::new(RwLock::new([0xFF; FRAMEBUFFER_SIZE])); let mut app = App { - game_name: String::from(game_name), + header: header.clone(), context: None, config: config.clone(), input_tx, @@ -178,7 +182,7 @@ fn main() { // Start CPU thread::spawn(move || { - let mut cpu = CPU::new(buffer, config, framebuffer); + let mut cpu = CPU::new(buffer, header, config, framebuffer); let mut step_cycles = 0; let mut step_zero = Instant::now(); diff --git a/src/mbc/header.rs b/src/mbc/header.rs new file mode 100644 index 0000000..9f79ce9 --- /dev/null +++ b/src/mbc/header.rs @@ -0,0 +1,85 @@ +use std::fmt; +use std::fmt::Formatter; +use num_traits::FromPrimitive; +use crate::mbc::licensee::Licensee; +use crate::mbc::mode::CartTypes; + +#[derive(Clone)] +pub struct Header { + pub cart_type: CartTypes, + pub licensee: Licensee, + pub destination: Destination, + pub rom_size: u8, + pub ram_size: u8, + pub title: String, + pub manufacturer_code: String, + pub cgb_flag: CGBFlag, + pub sgb_flag: bool, +} + +impl Header { + pub fn new(buffer: Vec) -> Self { + let cart_type: CartTypes = FromPrimitive::from_u8(buffer[0x0147]).expect("Failed to get cart type!"); + let licensee = match Licensee::old_licensee(buffer[0x014B]) { + Some(code) => { + code + }, + None => { + let string = String::from_utf8_lossy(&buffer[0x0144..=0x0145]); + let str = string.to_owned(); + let code = Licensee::new_licensee(&str); + code + } + }; + let destination = FromPrimitive::from_u8(buffer[0x014A]).expect("Failed to get cart destination!"); + + let rom_size = buffer[0x0148]; + let ram_size = buffer[0x0149]; + + let title = String::from_utf8_lossy(&buffer[0x0134..=0x013E]).to_string(); + let manufacturer_code = String::from_utf8_lossy(&buffer[0x013F..=0x0142]).to_string(); + + let cgb_flag = match FromPrimitive::from_u8(buffer[0x0143]) { + Some(flag) => { + flag + }, + None => { + CGBFlag::DMGOnly + } + }; + let sgb_flag = buffer[0x0146] == 0x03; + + Self { + cart_type, + licensee, + destination, + rom_size, + ram_size, + title, + manufacturer_code, + cgb_flag, + sgb_flag + } + } +} + +impl fmt::Display for Header { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + writeln!(f, "{} (Licensee: {}, Cart Type: {})", self.title, self.licensee, self.cart_type)?; + writeln!(f, "ROM: {}, RAM: {}, CGB: {:?}, SGB: {}", self.rom_size, self.ram_size, self.cgb_flag, self.sgb_flag)?; + Ok(()) + } +} + +#[derive(Clone, Copy, PartialEq, FromPrimitive, Debug)] +pub enum Destination { + Japan = 0x00, + Oveseas = 0x01 +} + +#[derive(Clone, Copy, PartialEq, FromPrimitive, Debug)] +pub enum CGBFlag { + BackwardsCompatible = 0x80, + CGBOnly = 0xC0, + DMGOnly +} \ No newline at end of file diff --git a/src/mbc/licensee.rs b/src/mbc/licensee.rs index ffda81a..7611ad6 100644 --- a/src/mbc/licensee.rs +++ b/src/mbc/licensee.rs @@ -1,7 +1,9 @@ use std::fmt::{Display, Formatter}; +#[derive(Clone)] pub enum Licensee { None, + Unknown, Capcom, ElectronicArts, HudsonSoft, @@ -134,6 +136,8 @@ pub enum Licensee { IGS, AWave, ExtremeEntertainment, + MTO, + Kodansha } impl Licensee { @@ -201,7 +205,9 @@ impl Licensee { "99" => Licensee::PackInSoft, "9H" => Licensee::BottomUp, "A4" => Licensee::KonamiYuGiOh, - _ => Licensee::None, + "BL" => Licensee::MTO, + "DK" => Licensee::Kodansha, + _ => Licensee::Unknown, } } @@ -355,7 +361,7 @@ impl Licensee { 0xF0 => Some(Licensee::AWave), 0xF3 => Some(Licensee::ExtremeEntertainment), 0xFF => Some(Licensee::LJN), - _ => Some(Licensee::None) + _ => Some(Licensee::Unknown) } } } @@ -364,6 +370,7 @@ impl Display for Licensee { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let name = match self { Licensee::None => "None", + Licensee::Unknown => "Unknown", Licensee::Capcom => "Capcom", Licensee::ElectronicArts => "EA (Electronic Arts)", Licensee::HudsonSoft => "Hudson Soft", @@ -495,10 +502,12 @@ impl Display for Licensee { Licensee::Epic => "Epic", Licensee::IGS => "IGS", Licensee::AWave => "A Wave", - Licensee::ExtremeEntertainment => "Extreme Entertainment" + Licensee::ExtremeEntertainment => "Extreme Entertainment", + Licensee::MTO => "MTO", + Licensee::Kodansha => "Kodansha" }; - writeln!(f, "{}", name)?; + write!(f, "{}", name)?; Ok(()) } } \ No newline at end of file diff --git a/src/mbc/mod.rs b/src/mbc/mod.rs index 4a0f873..13019a0 100644 --- a/src/mbc/mod.rs +++ b/src/mbc/mod.rs @@ -4,7 +4,8 @@ pub mod mbc3; pub mod mbc5; pub mod mode; pub mod rom_only; -mod licensee; +pub mod header; +pub mod licensee; #[allow(unused_imports)] pub mod prelude {