From 9c429c7cf6dec0e117ae8bb4434d413c8893cc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Sun, 19 Jun 2022 11:08:39 +0100 Subject: [PATCH] feat: initial support for load and store state --- .gitignore | 1 + examples/sdl/README.md | 2 +- examples/sdl/src/main.rs | 47 +++++++++++++++++++++++-------- src/chip8.rs | 2 ++ src/chip8_classic.rs | 8 +++++- src/chip8_neo.rs | 60 ++++++++++++++++++++++++++++++++++++++++ src/util.rs | 14 ++++++---- 7 files changed, 116 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 28494a9..b141142 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.sv8 *.rs.bk .DS_Store diff --git a/examples/sdl/README.md b/examples/sdl/README.md index 9c609b2..ee6856d 100644 --- a/examples/sdl/README.md +++ b/examples/sdl/README.md @@ -29,7 +29,7 @@ Drag and drop your ROM to play. * `T` Toggles the display of the diagnostics information * `O` Resets the machine * `P` Changes the pixel color of the system -* `M` Takes a RAM and VRAM snapshot and saves it in `out.snp` +* `M` Takes a RAM and VRAM snapshot and saves it in `.sv8` ## ROMs diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs index 82e523e..fee2d73 100644 --- a/examples/sdl/src/main.rs +++ b/examples/sdl/src/main.rs @@ -1,6 +1,6 @@ use chip_ahoyto::{ chip8::Chip8, chip8_classic::Chip8Classic, chip8_classic::SCREEN_PIXEL_HEIGHT, - chip8_classic::SCREEN_PIXEL_WIDTH, chip8_neo::Chip8Neo, util::read_file, + chip8_classic::SCREEN_PIXEL_WIDTH, chip8_neo::Chip8Neo, util::read_file, util::save_snapshot, }; use sdl2::{ audio::AudioCallback, audio::AudioSpecDesired, event::Event, image::LoadSurface, @@ -254,6 +254,16 @@ fn main() { None } + Event::KeyDown { + keycode: Some(Keycode::M), + .. + } => { + if state.rom_loaded { + save_snapshot(format!("{}.sv8", state.rom_name).as_str(), &state.system); + } + None + } + Event::KeyDown { keycode: Some(Keycode::O), .. @@ -282,16 +292,31 @@ fn main() { } Event::DropFile { filename, .. } => { - let rom = read_file(&filename); - let rom_name = Path::new(&filename).file_name().unwrap().to_str().unwrap(); - - state.system.reset_hard(); - state.system.load_rom(&rom); - - state.rom_name = String::from(rom_name); - state.rom_loaded = true; - - state.set_title(&format!("{} [Currently playing: {}]", TITLE, rom_name)); + if filename.ends_with(".sv8") { + let system_state = read_file(&filename); + + state.system.set_state(system_state.as_slice()); + + state.rom_name = String::from( + Path::new(&filename).file_stem().unwrap().to_str().unwrap(), + ); + state.rom_loaded = true; + } else { + let rom = read_file(&filename); + + state.system.reset_hard(); + state.system.load_rom(&rom); + + state.rom_name = String::from( + Path::new(&filename).file_name().unwrap().to_str().unwrap(), + ); + state.rom_loaded = true; + } + + state.set_title(&format!( + "{} [Currently playing: {}]", + TITLE, state.rom_name + )); None } diff --git a/src/chip8.rs b/src/chip8.rs index 258e69c..d805c3b 100644 --- a/src/chip8.rs +++ b/src/chip8.rs @@ -7,6 +7,8 @@ pub trait Chip8 { fn sp(&self) -> u8; fn ram(&self) -> Vec<u8>; fn vram(&self) -> Vec<u8>; + fn get_state(&self) -> Vec<u8>; + fn set_state(&mut self, state: &[u8]); fn load_rom(&mut self, rom: &[u8]); fn clock(&mut self); fn clock_dt(&mut self); diff --git a/src/chip8_classic.rs b/src/chip8_classic.rs index 383ba1f..66f0b6c 100644 --- a/src/chip8_classic.rs +++ b/src/chip8_classic.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{fmt::Display, io::Cursor}; use crate::{chip8::Chip8, util::random}; @@ -112,6 +112,12 @@ impl Chip8 for Chip8Classic { self.vram.to_vec() } + fn get_state(&self) -> Vec<u8> { + Vec::new() + } + + fn set_state(&mut self, state: &[u8]) {} + fn load_rom(&mut self, rom: &[u8]) { self.ram[ROM_START..ROM_START + rom.len()].clone_from_slice(rom); } diff --git a/src/chip8_neo.rs b/src/chip8_neo.rs index d661d5c..9ec1a9a 100644 --- a/src/chip8_neo.rs +++ b/src/chip8_neo.rs @@ -1,3 +1,5 @@ +use std::io::{Cursor, Read}; + use crate::{chip8::Chip8, util::random}; pub const DISPLAY_WIDTH: usize = 64; @@ -243,6 +245,64 @@ impl Chip8 for Chip8Neo { fn vram(&self) -> Vec<u8> { self.vram.to_vec() } + + fn get_state(&self) -> Vec<u8> { + let mut buffer: Vec<u8> = Vec::new(); + buffer.extend(self.ram.iter()); + buffer.extend(self.vram.iter()); + buffer.extend(self.stack.map(|v| v.to_le_bytes()).iter().flatten()); + buffer.extend(self.regs.iter()); + buffer.extend(self.pc.to_le_bytes().iter()); + buffer.extend(self.i.to_le_bytes().iter()); + buffer.extend(self.sp.to_le_bytes().iter()); + buffer.extend(self.dt.to_le_bytes().iter()); + buffer.extend(self.st.to_le_bytes().iter()); + buffer.extend(self.keys.map(|v| v as u8).iter()); + buffer.extend(self.last_key.to_le_bytes().iter()); + buffer + } + + fn set_state(&mut self, state: &[u8]) { + let mut u8_buffer = [0u8; 1]; + let mut u16_buffer = [0u8; 2]; + let mut regs_buffer = [0u8; REGISTERS_SIZE * 2]; + let mut keys_buffer = [0u8; KEYS_SIZE]; + + let mut cursor = Cursor::new(state.to_vec()); + + cursor.read_exact(&mut self.ram).unwrap(); + cursor.read_exact(&mut self.vram).unwrap(); + cursor.read_exact(&mut regs_buffer).unwrap(); + self.stack.clone_from_slice( + regs_buffer + .chunks(2) + .map(|v| { + u16_buffer.clone_from_slice(&v[0..2]); + u16::from_le_bytes(u16_buffer) + }) + .collect::<Vec<u16>>() + .as_slice(), + ); + cursor.read_exact(&mut self.regs).unwrap(); + cursor.read_exact(&mut u16_buffer).unwrap(); + self.pc = u16::from_le_bytes(u16_buffer); + cursor.read_exact(&mut u16_buffer).unwrap(); + self.i = u16::from_le_bytes(u16_buffer); + cursor.read_exact(&mut u8_buffer).unwrap(); + self.sp = u8::from_le_bytes(u8_buffer); + cursor.read_exact(&mut u8_buffer).unwrap(); + self.dt = u8::from_le_bytes(u8_buffer); + cursor.read_exact(&mut u8_buffer).unwrap(); + self.st = u8::from_le_bytes(u8_buffer); + cursor.read_exact(&mut keys_buffer).unwrap(); + self.keys.clone_from_slice( + keys_buffer + .map(|v| if v == 1 { true } else { false }) + .as_slice(), + ); + cursor.read_exact(&mut u8_buffer).unwrap(); + self.last_key = u8::from_le_bytes(u8_buffer); + } } impl Chip8Neo { diff --git a/src/util.rs b/src/util.rs index 2af0970..4563e6e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -22,9 +22,13 @@ pub fn random() -> u8 { /// Saves a snapshot image of the provided machine /// so that it can be latter loaded and RAM restored. -pub fn save_snapshot(chip8: &Box<dyn Chip8>) { - let mut file = File::create("out.snp").unwrap(); - file.write(&chip8.pc().to_le_bytes()).unwrap(); - //file.write(&chip8.ram().tr().).unwrap(); - //@todo need to serialize the rest of the machine +pub fn save_snapshot(name: &str, chip8: &Box<dyn Chip8>) { + let mut file = File::create(name).unwrap(); + let state = chip8.get_state(); + let buffer = state.as_slice(); + file.write(buffer).unwrap(); +} + +pub fn take_snapshot(chip8: &Box<dyn Chip8>) { + save_snapshot("out.sv8", chip8); } -- GitLab