aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-05-22 00:07:30 +0200
committerAstatin <[email protected]>2025-05-22 00:07:30 +0200
commit4fce95c86e12f91e127605d440118e1b6a64208b (patch)
tree729ff48e04be1c6fef42a45afb17c0d0a2259ec1
parent9a8e4117be8d30109229600346e7d9561c52a3e3 (diff)
Save wram,vram,io,hram with X button + move big arrays to Heap
-rw-r--r--src/audio.rs1
-rw-r--r--src/desktop/audio.rs16
-rw-r--r--src/desktop/input.rs45
-rw-r--r--src/desktop/load_save.rs55
-rw-r--r--src/desktop/mod.rs4
-rw-r--r--src/desktop/window.rs2
-rw-r--r--src/interrupts_timers.rs2
-rw-r--r--src/io.rs45
-rw-r--r--src/main.rs19
-rw-r--r--src/mmio.rs2
-rw-r--r--src/opcodes.rs4
-rw-r--r--src/state.rs32
12 files changed, 168 insertions, 59 deletions
diff --git a/src/audio.rs b/src/audio.rs
index 3e36790..6efd0d8 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -555,7 +555,6 @@ pub struct Channels<A: Audio> {
impl<A: Audio> Channels<A> {
pub fn new() -> Self {
-
let wave_ch1 = Arc::new(Mutex::new(None));
let wave_ch2 = Arc::new(Mutex::new(None));
let wave_ch3 = Arc::new(Mutex::new(None));
diff --git a/src/desktop/audio.rs b/src/desktop/audio.rs
index 60d32df..c32433b 100644
--- a/src/desktop/audio.rs
+++ b/src/desktop/audio.rs
@@ -1,7 +1,7 @@
use rodio::{OutputStream, Sink, Source};
-use crate::io::Audio;
use crate::audio::SAMPLE_RATE;
+use crate::io::Audio;
use std::time::Duration;
pub struct RodioAudio {
@@ -11,7 +11,10 @@ pub struct RodioAudio {
struct RodioWave<W: Iterator + Send + 'static>(W);
-impl<W: Iterator + Send + 'static> Iterator for RodioWave<W> where <W as Iterator>::Item: rodio::Sample {
+impl<W: Iterator + Send + 'static> Iterator for RodioWave<W>
+where
+ <W as Iterator>::Item: rodio::Sample,
+{
type Item = W::Item;
fn next(&mut self) -> Option<Self::Item> {
@@ -19,7 +22,10 @@ impl<W: Iterator + Send + 'static> Iterator for RodioWave<W> where <W as Iterato
}
}
-impl<W: Iterator + Send + 'static> Source for RodioWave<W> where <W as Iterator>::Item: rodio::Sample {
+impl<W: Iterator + Send + 'static> Source for RodioWave<W>
+where
+ <W as Iterator>::Item: rodio::Sample,
+{
fn current_frame_len(&self) -> Option<usize> {
None
}
@@ -37,15 +43,13 @@ impl<W: Iterator + Send + 'static> Source for RodioWave<W> where <W as Iterator>
}
}
-
impl Audio for RodioAudio {
- fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self {
+ fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self {
let (stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
sink.append(RodioWave(wave));
-
RodioAudio {
_stream: stream,
_sink: sink,
diff --git a/src/desktop/input.rs b/src/desktop/input.rs
index 1a2a212..d7c10c8 100644
--- a/src/desktop/input.rs
+++ b/src/desktop/input.rs
@@ -1,14 +1,15 @@
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
-use gilrs::{Button, GamepadId, Gilrs};
use crate::desktop::window::Keys;
use crate::io::Input;
+use gilrs::{Button, GamepadId, Gilrs};
use winit::keyboard::KeyCode;
pub struct Gamepad {
gilrs: Gilrs,
gamepad_id: Option<GamepadId>,
+ last_save_state: bool,
}
impl Gamepad {
@@ -23,7 +24,11 @@ impl Gamepad {
None
};
- Self { gilrs, gamepad_id }
+ Self {
+ gilrs,
+ gamepad_id,
+ last_save_state: false,
+ }
}
pub fn check_special_actions(&self, is_debug: &mut bool) {
@@ -92,6 +97,19 @@ impl Input for Gamepad {
res
}
+
+ fn save_state(&mut self) -> bool {
+ let mut ret = false;
+
+ if let Some(gamepad_id) = self.gamepad_id {
+ if let Some(gamepad) = self.gilrs.connected_gamepad(gamepad_id) {
+ let pressed = gamepad.is_pressed(Button::North);
+ ret = pressed && !self.last_save_state;
+ self.last_save_state = pressed;
+ }
+ }
+ ret
+ }
}
pub struct Keyboard {
@@ -163,6 +181,10 @@ impl Input for Keyboard {
fn get_direction_gamepad_reg(&self) -> u8 {
self.direction_reg
}
+
+ fn save_state(&mut self) -> bool {
+ false
+ }
}
pub struct GamepadRecorder {
@@ -221,6 +243,10 @@ impl Input for GamepadRecorder {
fn get_direction_gamepad_reg(&self) -> u8 {
self.direction_reg
}
+
+ fn save_state(&mut self) -> bool {
+ false
+ }
}
pub struct GamepadReplay {
@@ -228,6 +254,7 @@ pub struct GamepadReplay {
action_reg: u8,
direction_reg: u8,
next_cycle_update: Option<u128>,
+ last_save_state: bool,
}
impl GamepadReplay {
@@ -247,6 +274,7 @@ impl GamepadReplay {
action_reg: 0xff,
direction_reg: 0xff,
next_cycle_update,
+ last_save_state: false,
}
}
}
@@ -284,4 +312,17 @@ impl Input for GamepadReplay {
fn get_direction_gamepad_reg(&self) -> u8 {
self.direction_reg
}
+
+ fn save_state(&mut self) -> bool {
+ if self.last_save_state {
+ return false;
+ }
+ if self.next_cycle_update == None {
+ println!("SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE");
+
+ self.last_save_state = true;
+ return true;
+ }
+ false
+ }
}
diff --git a/src/desktop/load_save.rs b/src/desktop/load_save.rs
index dfe2083..5c3a123 100644
--- a/src/desktop/load_save.rs
+++ b/src/desktop/load_save.rs
@@ -1,10 +1,13 @@
+use crate::io::{Audio, LoadSave, Serial};
+use crate::state::GBState;
use std::fs::File;
-use std::io::{Write, Read};
-use crate::io::LoadSave;
+use std::io::{Read, Write};
+#[derive(Debug)]
pub struct FSLoadSave {
rom_file: String,
save_file: String,
+ state_file: Option<String>,
}
impl FSLoadSave {
@@ -12,8 +15,14 @@ impl FSLoadSave {
Self {
rom_file: rom_file.into(),
save_file: save_file.into(),
+ state_file: None,
}
}
+
+ pub fn state_file(mut self, state_file: impl Into<String>) -> Self {
+ self.state_file = Some(state_file.into());
+ self
+ }
}
impl LoadSave for FSLoadSave {
@@ -66,4 +75,46 @@ impl LoadSave for FSLoadSave {
Ok(())
}
+
+ fn save_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>) {
+ if let Some(state_file) = &self.state_file {
+ {
+ let mut vram_dump_file = File::create(format!("{}.vram", state_file)).unwrap();
+
+ for addr in 0x8000..0xa000 {
+ vram_dump_file
+ .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
+ }
+ }
+
+ {
+ let mut wram_dump_file = File::create(format!("{}.wram", state_file)).unwrap();
+
+ for addr in 0xc000..0xe000 {
+ wram_dump_file
+ .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
+ }
+ }
+
+ {
+ let mut io_dump_file = File::create(format!("{}.io", state_file)).unwrap();
+
+ for addr in 0xff00..0xff80 {
+ io_dump_file
+ .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
+ }
+ }
+
+ {
+ let mut hram_dump_file = File::create(format!("{}.hram", state_file)).unwrap();
+
+ for addr in 0xff80..=0xffff {
+ hram_dump_file
+ .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
+ }
+ }
+ } else {
+ panic!("{:?}", self)
+ }
+ }
}
diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs
index af773b6..3448c0e 100644
--- a/src/desktop/mod.rs
+++ b/src/desktop/mod.rs
@@ -1,5 +1,5 @@
-pub mod window;
-pub mod input;
pub mod audio;
+pub mod input;
pub mod load_save;
pub mod serial;
+pub mod window;
diff --git a/src/desktop/window.rs b/src/desktop/window.rs
index 14ac3a7..78a7c42 100644
--- a/src/desktop/window.rs
+++ b/src/desktop/window.rs
@@ -4,7 +4,7 @@ use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
-use crate::io::{WindowSignal, Window};
+use crate::io::{Window, WindowSignal};
use pixels::{Error, Pixels, SurfaceTexture};
use winit::dpi::LogicalSize;
diff --git a/src/interrupts_timers.rs b/src/interrupts_timers.rs
index f4ac3c2..b776777 100644
--- a/src/interrupts_timers.rs
+++ b/src/interrupts_timers.rs
@@ -1,6 +1,6 @@
use crate::display::DisplayInterrupt;
+use crate::io::{Audio, Serial};
use crate::state::{GBState, MemError};
-use crate::io::{Serial, Audio};
const TIMA_TIMER_SPEEDS: [u64; 4] = [1024, 16, 64, 256];
diff --git a/src/io.rs b/src/io.rs
index 3e7330e..b26533a 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -1,13 +1,14 @@
use std::time::SystemTime;
use std::{thread, time};
-use crate::state::GBState;
use crate::consts;
+use crate::state::GBState;
pub trait Input {
fn update_events(&mut self, cycles: u128) -> Option<u128>;
fn get_action_gamepad_reg(&self) -> u8;
fn get_direction_gamepad_reg(&self) -> u8;
+ fn save_state(&mut self) -> bool;
}
impl<T: Input + ?Sized> Input for Box<T> {
@@ -20,6 +21,9 @@ impl<T: Input + ?Sized> Input for Box<T> {
fn get_direction_gamepad_reg(&self) -> u8 {
(**self).get_direction_gamepad_reg()
}
+ fn save_state(&mut self) -> bool {
+ (**self).save_state()
+ }
}
pub enum WindowSignal {
@@ -45,21 +49,20 @@ pub trait Audio {
fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self;
}
-pub trait LoadSave where Self::Error: std::fmt::Display, Self::Error: std::fmt::Debug {
+pub trait LoadSave
+where
+ Self::Error: std::fmt::Display,
+ Self::Error: std::fmt::Debug,
+{
type Error;
fn load_bootrom(&self, boot_rom: &mut [u8]) -> Result<(), Self::Error>;
fn load_rom(&self, rom: &mut [u8]) -> Result<(), Self::Error>;
fn load_external_ram(&self, external_ram: &mut [u8]) -> Result<(), Self::Error>;
fn save_external_ram(&self, external_ram: &[u8]) -> Result<(), Self::Error>;
+ fn save_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>);
}
-pub struct Gameboy<
- I: Input,
- W: Window,
- S: Serial,
- A: Audio,
- LS: LoadSave,
-> {
+pub struct Gameboy<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> {
input: I,
window: W,
speed: f64,
@@ -78,7 +81,6 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
}
}
-
pub fn start(self) {
let Self {
mut window,
@@ -88,10 +90,10 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
load_save,
} = self;
- load_save.load_bootrom(&mut state.mem.boot_rom).unwrap();
- load_save.load_rom(&mut state.mem.rom).unwrap();
+ 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(&mut state.mem.external_ram) {
+ if let Err(err) = load_save.load_external_ram(state.mem.external_ram.as_mut()) {
println!(
"Loading save failed ({}). Initializing new external ram.",
err
@@ -130,16 +132,24 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
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)) {
+ if nanos_sleep >= 0.0
+ || next_precise_gamepad_update.map_or(false, |c| (c >= total_cycle_counter))
+ {
next_precise_gamepad_update = input.update_events(total_cycle_counter);
- let (action_button_reg, direction_button_reg) = (
+ let (action_button_reg, direction_button_reg, save_state) = (
input.get_action_gamepad_reg(),
input.get_direction_gamepad_reg(),
+ input.save_state(),
);
+ if save_state {
+ load_save.save_state(&state);
+ }
+
if state.mem.joypad_is_action
- && (action_button_reg & (state.mem.joypad_reg >> 4)) != (state.mem.joypad_reg >> 4)
+ && (action_button_reg & (state.mem.joypad_reg >> 4))
+ != (state.mem.joypad_reg >> 4)
|| (!state.mem.joypad_is_action
&& (direction_button_reg & state.mem.joypad_reg & 0b1111)
!= (state.mem.joypad_reg & 0b1111))
@@ -150,7 +160,6 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
state.mem.joypad_reg = direction_button_reg | (action_button_reg << 4);
}
-
if nanos_sleep > 0.0 {
if let Some(fb) = state.mem.display.redraw_request {
if let Some(WindowSignal::Exit) = window.update(&fb) {
@@ -165,7 +174,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
now = SystemTime::now();
if last_ram_bank_enabled && !state.mem.ram_bank_enabled {
- if let Err(err) = load_save.save_external_ram(&state.mem.external_ram) {
+ if let Err(err) = load_save.save_external_ram(state.mem.external_ram.as_ref()) {
println!("Failed to save external RAM ({})", err);
}
}
diff --git a/src/main.rs b/src/main.rs
index 1057634..2e5c80c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,16 +1,16 @@
pub mod audio;
pub mod consts;
+pub mod desktop;
pub mod display;
-pub mod io;
pub mod interrupts_timers;
+pub mod io;
pub mod mmio;
pub mod opcodes;
pub mod state;
-pub mod desktop;
-use crate::io::Input;
use crate::desktop::input::{Gamepad, GamepadRecorder, GamepadReplay, Keyboard};
use crate::desktop::load_save::FSLoadSave;
+use crate::io::Input;
use clap::Parser;
#[derive(Parser)]
@@ -50,7 +50,7 @@ fn main() {
println!("Starting {:?}...", &cli.rom);
- let serial = desktop::serial::UnconnectedSerial{};
+ let serial = desktop::serial::UnconnectedSerial {};
let window = desktop::window::DesktopWindow::new().unwrap();
let mut gamepad: Box<dyn Input> = if let Some(record_file) = cli.replay_input {
@@ -66,7 +66,12 @@ fn main() {
};
io::Gameboy::<_, _, _, desktop::audio::RodioAudio, _>::new(
- gamepad, window, serial,
- FSLoadSave::new(&cli.rom, format!("{}.sav", &cli.rom)),
- cli.speed as f64).start();
+ gamepad,
+ window,
+ serial,
+ FSLoadSave::new(&cli.rom, format!("{}.sav", &cli.rom))
+ .state_file(format!("{}.dump", &cli.rom)),
+ cli.speed as f64,
+ )
+ .start();
}
diff --git a/src/mmio.rs b/src/mmio.rs
index e669ff8..aa2f61d 100644
--- a/src/mmio.rs
+++ b/src/mmio.rs
@@ -1,5 +1,5 @@
+use crate::io::{Audio, Serial};
use crate::state::{MemError, Memory};
-use crate::io::{Serial, Audio};
impl<S: Serial, A: Audio> Memory<S, A> {
pub fn r_io(&self, addr: u8) -> u8 {
diff --git a/src/opcodes.rs b/src/opcodes.rs
index 26e98c5..3779af0 100644
--- a/src/opcodes.rs
+++ b/src/opcodes.rs
@@ -1,5 +1,5 @@
+use crate::io::{Audio, Serial};
use crate::state::{flag, reg, GBState, MemError};
-use crate::io::{Serial, Audio};
// The opcodes functions are returning the number of cycles used.
@@ -750,7 +750,7 @@ impl<S: Serial, A: Audio> GBState<S, A> {
0b010 => {
self.cpu.print_debug();
Ok(4)
- },
+ }
0b110 => {
self.mem.ime = false;
Ok(4)
diff --git a/src/state.rs b/src/state.rs
index 0a18af0..67da18e 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,7 +1,7 @@
+use crate::audio::Channels;
use crate::consts::{PROGRAM_START_ADDRESS, STACK_START_ADDRESS};
use crate::display::Display;
-use crate::audio::Channels;
-use crate::io::{Serial, Audio};
+use crate::io::{Audio, Serial};
pub mod reg {
pub const B: u8 = 0;
@@ -104,7 +104,7 @@ impl CPU {
}
pub struct Memory<S: Serial, A: Audio> {
- pub boot_rom: [u8; 0x900],
+ pub boot_rom: Box<[u8; 0x900]>,
pub cgb_mode: bool,
@@ -125,24 +125,24 @@ pub struct Memory<S: Serial, A: Audio> {
pub ram_bank_enabled: bool,
// 32 KiB ROM bank 00
- pub rom: [u8; 0x200000],
+ pub rom: Box<[u8; 0x200000]>,
// 4 KiB Work RAM 00
- wram_00: [u8; 0x1000],
+ wram_00: Box<[u8; 0x1000]>,
// 4 KiB Work RAM 00
- wram_01: [u8; 0x1000],
+ wram_01: Box<[u8; 0x1000]>,
// External RAM
- pub external_ram: [u8; 0x8000],
+ pub external_ram: Box<[u8; 0x8000]>,
// 8 KiB Video RAM
pub display: Display,
- pub io: [u8; 0x80],
+ pub io: Box<[u8; 0x80]>,
// High RAM
- hram: [u8; 0x7f],
+ hram: Box<[u8; 0x7f]>,
pub audio: Channels<A>,
@@ -192,7 +192,7 @@ impl<S: Serial, A: Audio> Memory<S, A> {
display.cls();
Self {
- boot_rom: [0; 0x900],
+ boot_rom: Box::new([0; 0x900]),
boot_rom_on: true,
cgb_mode: false,
bgcram_pointer: 0,
@@ -202,13 +202,13 @@ impl<S: Serial, A: Audio> Memory<S, A> {
rom_bank: 1,
ram_bank: 0,
ram_bank_enabled: false,
- rom: [0; 0x200000],
- wram_00: [0; 0x1000],
- wram_01: [0; 0x1000],
- external_ram: [0; 0x8000],
+ rom: Box::new([0; 0x200000]),
+ wram_00: Box::new([0; 0x1000]),
+ wram_01: Box::new([0; 0x1000]),
+ external_ram: Box::new([0; 0x8000]),
display,
- io: [0; 0x80],
- hram: [0; 0x7f],
+ io: Box::new([0; 0x80]),
+ hram: Box::new([0; 0x7f]),
audio: Channels::new(),
ime: false,
interrupts_register: 0,