diff --git a/src/bin/chirp-minifb.rs b/src/bin/chirp-minifb.rs index e31f17b..b1212fb 100644 --- a/src/bin/chirp-minifb.rs +++ b/src/bin/chirp-minifb.rs @@ -6,6 +6,7 @@ use chirp::{error::Result, prelude::*}; use gumdrop::*; +use owo_colors::OwoColorize; use std::fs::read; use std::{ path::PathBuf, @@ -23,11 +24,12 @@ struct Arguments { #[options(help = "Enable pause mode at startup.")] pub pause: bool, - #[options(help = "Set the instructions-per-frame rate.")] + #[options(help = "Set the instructions-per-delay rate, or use realtime.")] pub speed: Option, - #[options(help = "Run the emulator as fast as possible for `step` instructions.")] + #[options(help = "Set the instructions-per-frame rate.")] pub step: Option, - + #[options(help = "Enable performance benchmarking on stderr (requires -S)")] + pub perf: bool, #[options( short = "z", help = "Disable setting vF to 0 after a bitwise operation." @@ -77,6 +79,7 @@ struct State { pub speed: usize, pub step: Option, pub rate: u64, + pub perf: bool, pub ch8: Chip8, pub ui: UI, pub ft: Instant, @@ -88,6 +91,7 @@ impl State { speed: options.speed.unwrap_or(8), step: options.step, rate: options.frame_rate, + perf: options.perf, ch8: Chip8 { bus: bus! { // Load the charset into ROM @@ -140,16 +144,15 @@ impl State { Some(ticks) => { let time = Instant::now(); self.ch8.cpu.multistep(&mut self.ch8.bus, ticks)?; - let time = time.elapsed(); - let nspt = time.as_secs_f64() / ticks as f64; - eprintln!( - "{ticks},\t{time:.05?},\t{:.4}nspt,\t{}ipf", - nspt * 1_000_000_000.0, - ((1.0 / 60.0f64) / nspt).trunc(), - ); - // Pause the CPU and clear step - //self.ch8.cpu.flags.pause = true; - //self.step = None; + if self.perf { + let time = time.elapsed(); + let nspt = time.as_secs_f64() / ticks as f64; + eprintln!( + "{ticks},\t{time:.05?},\t{:.4}nspt,\t{}ipf", + nspt * 1_000_000_000.0, + ((1.0 / 60.0f64) / nspt).trunc(), + ); + } } None => { self.ch8.cpu.multistep(&mut self.ch8.bus, rate)?; @@ -166,21 +169,33 @@ impl State { } impl Iterator for State { - type Item = (); + type Item = Result<()>; + /// Pretty heavily abusing iterators here, in an annoying way fn next(&mut self) -> Option { self.wait_for_next_frame(); - self.keys().unwrap_or_else(|_| None)?; - self.tick_cpu().ok()?; - self.frame(); - Some(()) + match self.keys() { + Ok(opt) => opt?, + Err(e) => return Some(Err(e)), // summary + } + self.keys().unwrap_or(None)?; + if let Err(e) = self.tick_cpu() { + return Some(Err(e)); + } + self.frame()?; + Some(Ok(())) } } fn main() -> Result<()> { let options = Arguments::parse_args_default_or_exit(); let state = State::new(options)?; - Ok(for _ in state {}) + Ok(for result in state { + if let Err(e) = result { + eprintln!("{}", e.bold().red()); + break; + } + }) } /// Parses a hexadecimal string into a u16 diff --git a/src/cpu.rs b/src/cpu.rs index 353d8d4..2362f8c 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -580,7 +580,6 @@ impl CPU { }; // Print opcode disassembly: - if self.flags.debug { println!("{:?}", self.timers.insn.elapsed().bright_black()); self.timers.insn = Instant::now(); diff --git a/src/error.rs b/src/error.rs index ce26b4c..c8c6967 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,7 +15,7 @@ pub type Result = std::result::Result; #[derive(Debug, Error)] pub enum Error { /// Represents an unimplemented operation - #[error("Unrecognized opcode {word}")] + #[error("Unrecognized opcode: {word:04x}")] UnimplementedInstruction { /// The offending word word: u16, @@ -27,7 +27,7 @@ pub enum Error { region: Region, }, /// Tried to fetch [Range] from bus, received nothing - #[error("Invalid range {range:?} for bus")] + #[error("Invalid range {range:04x?} for bus")] InvalidBusRange { /// The offending [Range] range: Range, diff --git a/src/io.rs b/src/io.rs index 7ebf326..dd36191 100644 --- a/src/io.rs +++ b/src/io.rs @@ -151,6 +151,9 @@ impl UI { (1.0 / self.time.elapsed().as_secs_f64()).trunc() )); } + if !self.window.is_open() { + std::process::exit(0); + } self.time = Instant::now(); // update framebuffer self.fb.render(&mut self.window, &mut ch8.bus);