diff options
author | Astatin <[email protected]> | 2025-06-06 12:29:15 +0200 |
---|---|---|
committer | Astatin <[email protected]> | 2025-06-06 12:29:15 +0200 |
commit | aa5ebbc872b3f9b93add3d2060fa47a82ab0525f (patch) | |
tree | 59773a87797447573f52a7f5676a9a41f3ac1f2e | |
parent | 0c7f945407561f7c4531b2780e908bb2098551d8 (diff) |
Add skip bootrom & dumps on stop
-rw-r--r-- | src/io.rs | 50 | ||||
-rw-r--r-- | src/main.rs | 29 | ||||
-rw-r--r-- | src/opcodes.rs | 5 | ||||
-rw-r--r-- | src/state.rs | 2 |
4 files changed, 63 insertions, 23 deletions
@@ -75,13 +75,25 @@ pub struct Gameboy<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> { impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, LS> { pub fn new(input: I, window: W, serial: S, load_save: LS, speed: f64) -> Self { - Self { + let mut gb = Self { input, window, speed, state: GBState::<S, A>::new(serial), load_save, + }; + + gb.load_save.load_bootrom(gb.state.mem.boot_rom.as_mut()).unwrap(); + gb.load_save.load_rom(gb.state.mem.rom.as_mut()).unwrap(); + + if let Err(err) = gb.load_save.load_external_ram(gb.state.mem.external_ram.as_mut()) { + println!( + "Loading save failed ({}). Initializing new external ram.", + err + ); } + + gb } pub fn load_state(&mut self) -> Result<(), LS::Error> { @@ -89,28 +101,30 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, Ok(()) } + + pub fn dump_state(&mut self) -> Result<(), LS::Error> { + self.load_save.dump_state(&mut self.state)?; + Ok(()) + } + pub fn debug(&mut self) { self.state.is_debug = true; } - pub fn start(self) { + pub fn skip_bootrom(&mut self) { + self.state.mem.boot_rom_on = false; + self.state.cpu.pc = 0x100; + } + + pub fn start(&mut self) { let Self { - mut window, - mut input, - speed, - mut state, - load_save, + ref mut window, + ref mut input, + ref speed, + ref mut state, + ref load_save, } = self; - load_save.load_bootrom(state.mem.boot_rom.as_mut()).unwrap(); - load_save.load_rom(state.mem.rom.as_mut()).unwrap(); - - if let Err(err) = load_save.load_external_ram(state.mem.external_ram.as_mut()) { - println!( - "Loading save failed ({}). Initializing new external ram.", - err - ); - } let mut total_cycle_counter: u128 = 0; let mut nanos_sleep: f64 = 0.0; let mut halt_time = 0; @@ -120,7 +134,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, let mut now = SystemTime::now(); let mut next_precise_gamepad_update: Option<u128> = None; - loop { + while !state.is_stopped { if was_previously_halted && !state.mem.halt { println!("Halt cycles {}", halt_time); halt_time = 0; @@ -142,7 +156,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, state.check_interrupts(); state.mem.update_serial(); - nanos_sleep += c as f64 * (consts::CPU_CYCLE_LENGTH_NANOS as f64 / speed) as f64; + nanos_sleep += c as f64 * (consts::CPU_CYCLE_LENGTH_NANOS as f64 / *speed) as f64; if nanos_sleep >= 0.0 || next_precise_gamepad_update.map_or(false, |c| (c >= total_cycle_counter)) diff --git a/src/main.rs b/src/main.rs index 9a48983..cee08f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,36 +19,49 @@ struct Cli { /// The gameboy rom file rom: String, - /// Setting uses more battery and set the CPU to 100% but could sometimes solve inconsistent timing. - #[arg(long)] - loop_lock_timing: bool, - + /// Serial communication input from a FIFO file #[arg(long)] fifo_input: Option<String>, + /// Serial communication output from a FIFO file #[arg(long)] fifo_output: Option<String>, + /// Record the inputs into a file when they happen so it can be replayed with --replay-input #[arg(long)] record_input: Option<String>, + /// Replay the inputs from the file recorded by record_input #[arg(long)] replay_input: Option<String>, + /// The file to which the state will be save when using the X (North) button of the controller + /// and loaded from when using the --load-state parameter #[arg(long)] state_file: Option<String>, + /// Start at the state defined by --state-file instead of the start of bootrom with empty mem #[arg(short, long, default_value_t = false)] load_state: bool, + /// Gets inputs from keyboard instead of gamepad #[arg(short, long, default_value_t = false)] keyboard: bool, #[arg(short, long, default_value_t = 1.0)] speed: f32, + /// Will print all of the opcodes executed (WARNING: THERE ARE MANY) #[arg(short, long, default_value_t = false)] debug: bool, + + /// Skip bootrom (will start the execution at 0x100 with all registers empty + #[arg(long, default_value_t = false)] + skip_bootrom: bool, + + /// Dump state to files on stop + #[arg(long, default_value_t = false)] + stop_dump_state: bool, } fn main() { @@ -92,5 +105,13 @@ fn main() { gameboy.load_state().unwrap(); } + if cli.skip_bootrom { + gameboy.skip_bootrom(); + } + gameboy.start(); + + if cli.stop_dump_state { + gameboy.dump_state().unwrap(); + } } diff --git a/src/opcodes.rs b/src/opcodes.rs index a8af25f..606634a 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -608,7 +608,10 @@ impl<S: Serial, A: Audio> GBState<S, A> { 0b000 => match n1 { 0b000 => 4, 0b001 => self.ldnnsp(), - 0b010 => todo!("STOP"), + 0b010 => { + self.is_stopped = true; + 4 + }, 0b011 => self.jr8(), _ => self.jrcc8(n1), }, diff --git a/src/state.rs b/src/state.rs index 530296e..980cbad 100644 --- a/src/state.rs +++ b/src/state.rs @@ -314,6 +314,7 @@ pub struct GBState<S: Serial, A: Audio> { pub cpu: CPU, pub mem: Memory<S, A>, pub is_debug: bool, + pub is_stopped: bool, pub div_cycles: u64, pub tima_cycles: u64, @@ -327,6 +328,7 @@ impl<S: Serial, A: Audio> GBState<S, A> { cpu: CPU::new(), mem, is_debug: false, + is_stopped: false, div_cycles: 0, tima_cycles: 0, |