From 8d297efe866722094bab3fdd134544204827d706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Thu, 27 Apr 2023 12:15:41 +0100 Subject: [PATCH] chore: much better device initialization --- frontends/sdl/src/main.rs | 46 ++++++++++++++++++++++++++------------- src/cpu.rs | 15 +++++++++++++ src/devices/printer.rs | 4 ++++ src/devices/stdout.rs | 15 ++++++++++++- src/gb.rs | 16 +++++++++++++- src/mmu.rs | 12 ++++++++++ src/rom.rs | 1 - src/serial.rs | 7 ++++++ 8 files changed, 98 insertions(+), 18 deletions(-) diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 9cdc4888..0e423926 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 2f765e4b..1fb00036 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 23c46b4e..cf0161a8 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 048a83f7..cdaefb2c 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 74b8dc27..5088e6f1 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 301d3298..983dcbdd 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 8d18fab5..c9acccf5 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 dd6388a3..551a375b 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 { -- GitLab