From 45466a57334942de336eca83be66e4bf89a1dd19 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Mon, 22 Mar 2021 21:41:22 -0500 Subject: [PATCH] feat: implement clap for cli and improve error messages --- Cargo.lock | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/bus.rs | 20 +++++++++------ src/cpu.rs | 12 ++++----- src/main.rs | 55 ++++++++++++++++++++++++++++++--------- 5 files changed, 135 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0cc1f8..8bcb2de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,15 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anyhow" version = "1.0.39" @@ -40,6 +49,17 @@ dependencies = [ "libloading 0.6.7", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -128,6 +148,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -278,7 +313,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.9.3", "syn", ] @@ -478,6 +513,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bitfield", + "clap", "pixels", "winit", "winit_input_helper", @@ -629,6 +665,15 @@ dependencies = [ "slab", ] +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1234,6 +1279,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.9.3" @@ -1251,6 +1302,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.24" @@ -1327,12 +1387,24 @@ dependencies = [ "wide", ] +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index e7647c1..df6ddb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] anyhow = "^1.0" bitfield = "^0.13" +clap = "^2.33" pixels = "^0.2" winit = "^0.24" winit_input_helper = "^0.9" diff --git a/src/bus.rs b/src/bus.rs index 6b3b030..51d8a63 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -8,6 +8,7 @@ use super::serial::Serial; use super::sound::Sound; use super::timer::Timer; use super::work_ram::{VariableWorkRam, WorkRam}; +use anyhow::anyhow; use std::{convert::TryInto, fs::File, io::Read}; const BOOT_ROM_SIZE: usize = 256; @@ -46,21 +47,24 @@ impl Default for Bus { } impl Bus { - pub fn with_boot(path: &str) -> Self { - let mut file = File::open(path).unwrap(); + pub fn with_boot(path: &str) -> anyhow::Result { + let mut file = File::open(path)?; let mut buf = Vec::with_capacity(BOOT_ROM_SIZE); - file.read_to_end(&mut buf).unwrap(); + file.read_to_end(&mut buf)?; - let boot_rom: [u8; BOOT_ROM_SIZE] = buf.try_into().unwrap(); + let boot_rom: [u8; BOOT_ROM_SIZE] = buf + .try_into() + .map_err(|_| anyhow!("{} was not {} bytes in size", path, BOOT_ROM_SIZE))?; - Self { + Ok(Self { boot: Some(boot_rom), ..Default::default() - } + }) } - pub fn load_cartridge(&mut self, path: &str) { - self.cartridge = Some(Cartridge::new(path).unwrap()); + pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> { + self.cartridge = Some(Cartridge::new(path)?); + Ok(()) } pub fn step(&mut self, cycles: Cycles) { diff --git a/src/cpu.rs b/src/cpu.rs index bc781d9..7e562f6 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -33,11 +33,11 @@ impl Cpu { } } - pub fn boot_new(path: &str) -> Self { - Self { - bus: Bus::with_boot(path), + pub fn boot_new(path: &str) -> anyhow::Result { + Ok(Self { + bus: Bus::with_boot(path)?, ..Default::default() - } + }) } pub fn ime(&self) -> bool { @@ -52,8 +52,8 @@ impl Cpu { self.reg.pc += 1; } - pub fn load_cartridge(&mut self, path: &str) { - self.bus.load_cartridge(path); + pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> { + Ok(self.bus.load_cartridge(path)?) } } diff --git a/src/main.rs b/src/main.rs index 26ad229..19ff515 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ use anyhow::{anyhow, Result}; +use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use gb::LR35902_CLOCK_SPEED; use gb::{Cycles, LR35902}; use pixels::{Pixels, SurfaceTexture}; -use std::env::args; use std::time::{Duration, Instant}; use winit::dpi::LogicalSize; use winit::event::{Event, VirtualKeyCode}; @@ -19,20 +19,51 @@ const LR35902_CYCLE_TIME: f64 = 1.0f64 / LR35902_CLOCK_SPEED as f64; const CYCLES_IN_FRAME: Cycles = Cycles::new(70224); fn main() -> Result<()> { + let app = App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()); + + let m = app + .arg( + Arg::with_name("rom") + .value_name("ROM_FILE") + .takes_value(true) + .required(true) + .index(1) + .help("The ROM that the emulator will run"), + ) + .arg( + Arg::with_name("boot") + .short("b") + .long("boot") + .value_name("FILE") + .takes_value(true) + .help("Path to Boot ROM"), + ) + .get_matches(); + + let mut game_boy = match m.value_of("boot") { + Some(path) => LR35902::boot_new(path).expect("Failed to load boot ROM"), + None => LR35902::new(), + }; + + // This is a required value so if we program gets here, + // a string **will** have been provided to the rom argument + let rom_path = m + .value_of("rom") + .expect("ROM Path not provided despite it being a required argument"); + + game_boy + .load_cartridge(rom_path) + .expect("Failed to load ROM"); + + // Initialize GUI let event_loop = EventLoop::new(); let mut input = WinitInputHelper::new(); let window = create_window(&event_loop)?; let mut pixels = create_pixels(&window)?; - let mut game_boy = match args().nth(1) { - Some(boot_path) => LR35902::boot_new(&boot_path), - None => LR35902::new(), - }; - - // game_boy.load_cartridge("bin/instr_timing.gb"); - game_boy.load_cartridge("bin/cpu_instrs.gb"); - // game_boy.load_cartridge("bin/tetris.gb"); - let mut now = Instant::now(); let mut cycles_in_frame = Cycles::default(); event_loop.run(move |event, _, control_flow| { @@ -83,7 +114,7 @@ fn main() -> Result<()> { }); } -pub fn create_window(event_loop: &EventLoop<()>) -> Result { +fn create_window(event_loop: &EventLoop<()>) -> Result { let size = LogicalSize::new((GB_WIDTH as f64) * SCALE, (GB_HEIGHT as f64) * SCALE); Ok(WindowBuilder::new() .with_title("DMG-1 Emulator") @@ -92,7 +123,7 @@ pub fn create_window(event_loop: &EventLoop<()>) -> Result { .build(&event_loop)?) } -pub fn create_pixels(window: &Window) -> Result> { +fn create_pixels(window: &Window) -> Result> { let window_size = window.inner_size(); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window); Ok(Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?)