diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 9cdc4888d9140e5bfed4dd4966c4e8cff73e63bc..0e4239265e96e25ea2d3a777bffd9a2909f5710e 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -6,10 +6,11 @@ pub mod graphics; use audio::Audio; use boytacean::{ - devices::printer::PrinterDevice, + devices::{printer::PrinterDevice, stdout::StdoutDevice}, gb::{AudioProvider, GameBoy, GameBoyMode}, pad::PadKey, ppu::{PaletteInfo, PpuMode, DISPLAY_HEIGHT, DISPLAY_WIDTH}, + serial::{NullDevice, SerialDevice}, }; use chrono::Utc; use clap::Parser; @@ -461,6 +462,9 @@ struct Args { #[arg(short, long, default_value_t = String::from("cgb"))] mode: String, + #[arg(short, long, default_value_t = String::from("printer"))] + device: String, + #[arg(short, long, default_value_t = String::from("../../res/roms.prop/tetris_dx.gbc"))] rom_path: String, } @@ -469,24 +473,13 @@ fn main() { // parses the provided command line arguments and uses them to // obtain structured values let args = Args::parse(); - let mode = GameBoyMode::from_str(&args.mode); + let mode: GameBoyMode = GameBoyMode::from_str(&args.mode); // creates a new Game Boy instance and loads both the boot ROM // and the initial game ROM to "start the engine" let mut game_boy = GameBoy::new(mode); - let mut printer = Box::<PrinterDevice>::default(); - printer.set_callback(|image_buffer| { - let file_name = format!("printer-{}.png", Utc::now().format("%Y%m%d-%H%M%S")); - image::save_buffer( - Path::new(&file_name), - image_buffer, - 160, - (image_buffer.len() / 4 / 160) as u32, - ColorType::Rgba8, - ) - .unwrap(); - }); - game_boy.attach_serial(printer); + let device = build_device(&args.device); + game_boy.attach_serial(device); game_boy.load(true); // prints the current version of the emulator (informational message) @@ -503,6 +496,29 @@ fn main() { emulator.run(); } +fn build_device(device: &str) -> Box<dyn SerialDevice> { + match device { + "null" => Box::<NullDevice>::default(), + "stdout" => Box::<StdoutDevice>::default(), + "printer" => { + let mut printer = Box::<PrinterDevice>::default(); + printer.set_callback(|image_buffer| { + let file_name = format!("printer-{}.png", Utc::now().format("%Y%m%d-%H%M%S")); + image::save_buffer( + Path::new(&file_name), + image_buffer, + 160, + (image_buffer.len() / 4 / 160) as u32, + ColorType::Rgba8, + ) + .unwrap(); + }); + printer + } + _ => panic!("Unsupported device: {}", device), + } +} + fn key_to_pad(keycode: Keycode) -> Option<PadKey> { match keycode { Keycode::Up => Some(PadKey::Up), diff --git a/src/cpu.rs b/src/cpu.rs index 2f765e4b60836098cbcbcaededc5ec56adee0b74..1fb00036410f7fe2bb16dc2ee2c9af1d75014577 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -340,16 +340,31 @@ impl Cpu { self.mmu().pad() } + #[inline(always)] + pub fn pad_i(&self) -> &Pad { + self.mmu_i().pad_i() + } + #[inline(always)] pub fn timer(&mut self) -> &mut Timer { self.mmu().timer() } + #[inline(always)] + pub fn timer_i(&self) -> &Timer { + self.mmu_i().timer_i() + } + #[inline(always)] pub fn serial(&mut self) -> &mut Serial { self.mmu().serial() } + #[inline(always)] + pub fn serial_i(&self) -> &Serial { + self.mmu_i().serial_i() + } + #[inline(always)] pub fn halted(&self) -> bool { self.halted diff --git a/src/devices/printer.rs b/src/devices/printer.rs index 23c46b4e0c8b2d03d85826217f634289934d03f1..cf0161a8c90b319f39c6aa0fbe30e3210333b50d 100644 --- a/src/devices/printer.rs +++ b/src/devices/printer.rs @@ -312,6 +312,10 @@ impl SerialDevice for PrinterDevice { fn allow_slave(&self) -> bool { false } + + fn description(&self) -> String { + format!("Printer [{}]", self.command) + } } impl Default for PrinterDevice { diff --git a/src/devices/stdout.rs b/src/devices/stdout.rs index 048a83f7a78e61df5245d8aa03cd04524d8c8ba0..cdaefb2c9096c5dbe4cc766929588114222f4e6b 100644 --- a/src/devices/stdout.rs +++ b/src/devices/stdout.rs @@ -1,4 +1,7 @@ -use std::io::{stdout, Write}; +use std::{ + fmt::{self, Display, Formatter}, + io::{stdout, Write}, +}; use crate::serial::SerialDevice; @@ -37,6 +40,10 @@ impl SerialDevice for StdoutDevice { fn allow_slave(&self) -> bool { false } + + fn description(&self) -> String { + String::from("Stdout") + } } impl Default for StdoutDevice { @@ -44,3 +51,9 @@ impl Default for StdoutDevice { Self::new(true) } } + +impl Display for StdoutDevice { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Stdout") + } +} diff --git a/src/gb.rs b/src/gb.rs index 74b8dc271a50d8dffefb1464e8cb948652c2c06a..5088e6f1fc4e59fb72afef3483c7eeb9cb5adcd2 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -610,7 +610,7 @@ impl GameBoy { pub fn description(&self, column_length: usize) -> String { format!( - "{} {}\n{} {}\n{} {}\n{} {}", + "{} {}\n{} {}\n{} {}\n{} {}\n{} {}", format!("{:width$}", "Version", width = column_length), VERSION, format!("{:width$}", "Mode", width = column_length), @@ -619,6 +619,8 @@ impl GameBoy { self.ram_size(), format!("{:width$}", "VRAM Size", width = column_length), self.vram_size(), + format!("{:width$}", "Serial", width = column_length), + self.serial_i().device().description(), ) } } @@ -662,14 +664,26 @@ impl GameBoy { self.cpu.pad() } + pub fn pad_i(&self) -> &Pad { + self.cpu.pad_i() + } + pub fn timer(&mut self) -> &mut Timer { self.cpu.timer() } + pub fn timer_i(&self) -> &Timer { + self.cpu.timer_i() + } + pub fn serial(&mut self) -> &mut Serial { self.cpu.serial() } + pub fn serial_i(&self) -> &Serial { + self.cpu.serial_i() + } + pub fn frame_buffer(&mut self) -> &[u8; FRAME_BUFFER_SIZE] { &(self.ppu().frame_buffer) } diff --git a/src/mmu.rs b/src/mmu.rs index 301d32988283477eda33813766feec825850199a..983dcbdd3c48242c169b0c35d8fc175e868be658 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -144,14 +144,26 @@ impl Mmu { &mut self.pad } + pub fn pad_i(&self) -> &Pad { + &self.pad + } + pub fn timer(&mut self) -> &mut Timer { &mut self.timer } + pub fn timer_i(&self) -> &Timer { + &self.timer + } + pub fn serial(&mut self) -> &mut Serial { &mut self.serial } + pub fn serial_i(&self) -> &Serial { + &self.serial + } + pub fn boot_active(&self) -> bool { self.boot_active } diff --git a/src/rom.rs b/src/rom.rs index 8d18fab57e8852b83ab9a2c7db04313057ce604e..c9acccf5538fa5e1264da0fe17532f121218dd07 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -1,6 +1,5 @@ use core::fmt; use std::{ - any::Any, cmp::max, fmt::{Display, Formatter}, }; diff --git a/src/serial.rs b/src/serial.rs index dd6388a3c65daddda4800dabbb2ead2851184322..551a375b0500d966d61a789b3a3560ae3f7f3997 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -8,6 +8,9 @@ pub trait SerialDevice { /// simulating an external clock source. Or if instead the /// clock should always be generated by the running device. fn allow_slave(&self) -> bool; + + /// Returns a short description of the serial device. + fn description(&self) -> String; } pub struct Serial { @@ -202,6 +205,10 @@ impl SerialDevice for NullDevice { fn allow_slave(&self) -> bool { false } + + fn description(&self) -> String { + String::from("Null") + } } impl Default for NullDevice {