aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-06-06 12:29:15 +0200
committerAstatin <[email protected]>2025-06-06 12:29:15 +0200
commitaa5ebbc872b3f9b93add3d2060fa47a82ab0525f (patch)
tree59773a87797447573f52a7f5676a9a41f3ac1f2e
parent0c7f945407561f7c4531b2780e908bb2098551d8 (diff)
Add skip bootrom & dumps on stop
-rw-r--r--src/io.rs50
-rw-r--r--src/main.rs29
-rw-r--r--src/opcodes.rs5
-rw-r--r--src/state.rs2
4 files changed, 63 insertions, 23 deletions
diff --git a/src/io.rs b/src/io.rs
index 89f3a3b..bd36b3a 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -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,