aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-05-22 14:07:19 +0200
committerAstatin <[email protected]>2025-05-22 14:07:19 +0200
commit0c7f945407561f7c4531b2780e908bb2098551d8 (patch)
tree082b5cef5430a787ca524b0f846be999e5633334
parent4fce95c86e12f91e127605d440118e1b6a64208b (diff)
Add load/save parameters to the CLI & remove errors
-rw-r--r--.gitignore1
-rw-r--r--src/desktop/load_save.rs131
-rw-r--r--src/display.rs18
-rw-r--r--src/interrupts_timers.rs7
-rw-r--r--src/io.rs22
-rw-r--r--src/main.rs29
-rw-r--r--src/mmio.rs8
-rw-r--r--src/opcodes.rs328
-rw-r--r--src/state.rs63
9 files changed, 348 insertions, 259 deletions
diff --git a/.gitignore b/.gitignore
index b301fc1..a7af57c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ target/
debug.txt
assets/cgb_boot.bin
assets/dmg_boot.bin
+assets/dmg_boot.bin.bak
*.record
diff --git a/src/desktop/load_save.rs b/src/desktop/load_save.rs
index 5c3a123..a7eadfc 100644
--- a/src/desktop/load_save.rs
+++ b/src/desktop/load_save.rs
@@ -76,45 +76,122 @@ 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();
+ fn dump_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>) -> Result<(), std::io::Error> {
+ {
+ let mut vram_dump_file = File::create(format!("{}.vram.dump", self.rom_file))?;
+
+ for addr in 0x8000..0xa000 {
+ vram_dump_file.write_all(format!("{:02X} ", state.mem.r(addr)).as_bytes())?;
+ }
+ }
+
+ {
+ let mut wram_dump_file = File::create(format!("{}.wram.dump", self.rom_file))?;
- for addr in 0x8000..0xa000 {
- vram_dump_file
- .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
- }
+ for addr in 0xc000..0xe000 {
+ wram_dump_file.write_all(format!("{:02X} ", state.mem.r(addr)).as_bytes())?;
}
+ }
- {
- let mut wram_dump_file = File::create(format!("{}.wram", state_file)).unwrap();
+ {
+ let mut io_dump_file = File::create(format!("{}.io.dump", self.rom_file))?;
- for addr in 0xc000..0xe000 {
- wram_dump_file
- .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
- }
+ for addr in 0xff00..0xff80 {
+ io_dump_file.write_all(format!("{:02X} ", state.mem.r(addr)).as_bytes())?;
}
+ }
+
+ {
+ let mut hram_dump_file = File::create(format!("{}.hram.dump", self.rom_file))?;
- {
- let mut io_dump_file = File::create(format!("{}.io", state_file)).unwrap();
+ for addr in 0xff80..=0xffff {
+ hram_dump_file.write_all(format!("{:02X} ", state.mem.r(addr)).as_bytes())?;
+ }
+ }
+
+ {
+ let mut cpu_dump_file = File::create(format!("{}.cpu.dump", self.rom_file))?;
+
+ for i in 0..8 {
+ cpu_dump_file.write_all(format!("{:02X} ", state.cpu.r[i]).as_bytes())?;
+ }
+
+ cpu_dump_file.write_all(format!("{:04X} ", state.cpu.pc).as_bytes())?;
+
+ cpu_dump_file.write_all(format!("{:04X} ", state.cpu.sp).as_bytes())?;
+ }
+
+ Ok(())
+ }
- for addr in 0xff00..0xff80 {
- io_dump_file
- .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
- }
+ fn save_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>) -> Result<(), std::io::Error> {
+ if let Some(state_file) = &self.state_file {
+ let mut state_file = File::create(state_file)?;
+ for addr in 0x8000..0xa000 {
+ state_file.write_all(&[state.mem.r(addr)])?;
}
- {
- let mut hram_dump_file = File::create(format!("{}.hram", state_file)).unwrap();
+ state_file.write_all(state.mem.wram_00.as_ref())?;
+ state_file.write_all(state.mem.wram_01.as_ref())?;
- for addr in 0xff80..=0xffff {
- hram_dump_file
- .write_all(format!("{:02X} ", state.mem.r(addr).unwrap()).as_bytes());
- }
+ for addr in 0xff00..0xff80 {
+ state_file.write_all(&[state.mem.r(addr)])?;
}
+
+ state_file.write_all(state.mem.hram.as_ref())?;
+ state_file.write_all(&[state.mem.interrupts_register])?;
+
+ state_file.write_all(&state.cpu.r)?;
+ state_file.write_all(&state.cpu.pc.to_le_bytes())?;
+ state_file.write_all(&state.cpu.sp.to_le_bytes())?;
+ state_file.write_all(&[state.mem.boot_rom_on.into(), state.mem.ime.into()])?;
} else {
- panic!("{:?}", self)
+ eprintln!("Tried to save state without state_file specified");
}
+ Ok(())
+ }
+
+ fn load_state<S: Serial, A: Audio>(
+ &self,
+ state: &mut GBState<S, A>,
+ ) -> Result<(), std::io::Error> {
+ if let Some(state_file) = &self.state_file {
+ let mut state_file = File::open(state_file)?;
+
+ let mut vram = Box::new([0; 0x2000]);
+ state_file.read_exact(vram.as_mut())?;
+ for i in 0x0000..0x2000 {
+ state.mem.w(0x8000 + i, vram[i as usize]);
+ }
+
+ state_file.read_exact(state.mem.wram_00.as_mut())?;
+ state_file.read_exact(state.mem.wram_01.as_mut())?;
+
+ let mut io = [0; 0x80];
+ state_file.read_exact(io.as_mut())?;
+ for i in 0x00..0x80 {
+ state.mem.w(0xff00 + i, io[i as usize]);
+ }
+
+ state_file.read_exact(state.mem.hram.as_mut())?;
+
+ let mut reg8 = [0; 1];
+ state_file.read_exact(reg8.as_mut())?;
+ state.mem.interrupts_register = reg8[0];
+
+ state_file.read_exact(&mut state.cpu.r)?;
+
+ let mut reg16 = [0; 2];
+ state_file.read_exact(&mut reg16)?;
+ state.cpu.pc = u16::from_le_bytes(reg16);
+ state_file.read_exact(&mut reg16)?;
+ state.cpu.sp = u16::from_le_bytes(reg16);
+
+ state_file.read_exact(reg8.as_mut())?;
+ state.mem.boot_rom_on = reg8[0] != 0;
+ state_file.read_exact(reg8.as_mut())?;
+ state.mem.ime = reg8[0] != 0;
+ }
+ Ok(())
}
}
diff --git a/src/display.rs b/src/display.rs
index 917f330..0417a00 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -1,7 +1,6 @@
// Very readable, much clean wow.
use crate::consts::DISPLAY_UPDATE_SLEEP_TIME_MICROS;
-use crate::state::MemError;
use std::time::SystemTime;
const COLORS: [u32; 4] = [0x00e0f8d0, 0x0088c070, 0x346856, 0x00081820];
@@ -145,7 +144,7 @@ impl Display {
}
}
- pub fn w(&mut self, addr: u16, value: u8) -> Result<(), MemError> {
+ pub fn w(&mut self, addr: u16, value: u8) {
if self.vram_bank == 0 {
if addr < 0x1800 {
self.tiledata[addr as usize] = value;
@@ -161,25 +160,24 @@ impl Display {
self.bg_map_attr[addr as usize - 0x1800] = value;
}
}
- Ok(())
}
- pub fn r(&self, addr: u16) -> Result<u8, MemError> {
+ pub fn r(&self, addr: u16) -> u8 {
if self.vram_bank == 0 {
if addr < 0x1800 {
- Ok(self.tiledata[addr as usize])
+ self.tiledata[addr as usize]
} else if addr >= 0x7e00 {
- Ok(self.oam[addr as usize - 0x7e00])
+ self.oam[addr as usize - 0x7e00]
} else {
- Ok(self.tilemaps[addr as usize - 0x1800])
+ self.tilemaps[addr as usize - 0x1800]
}
} else {
if addr < 0x1800 {
- Ok(self.tiledata[addr as usize + 0x1800])
+ self.tiledata[addr as usize + 0x1800]
} else if addr < 0x1c00 {
- Ok(self.bg_map_attr[addr as usize - 0x1800])
+ self.bg_map_attr[addr as usize - 0x1800]
} else {
- Ok(0)
+ 0
}
}
}
diff --git a/src/interrupts_timers.rs b/src/interrupts_timers.rs
index b776777..f9ae84b 100644
--- a/src/interrupts_timers.rs
+++ b/src/interrupts_timers.rs
@@ -1,16 +1,16 @@
use crate::display::DisplayInterrupt;
use crate::io::{Audio, Serial};
-use crate::state::{GBState, MemError};
+use crate::state::GBState;
const TIMA_TIMER_SPEEDS: [u64; 4] = [1024, 16, 64, 256];
impl<S: Serial, A: Audio> GBState<S, A> {
- pub fn check_interrupts(&mut self) -> Result<(), MemError> {
+ pub fn check_interrupts(&mut self) {
if self.mem.ime {
let interrupts = self.mem.io[0x0f] & self.mem.interrupts_register & 0b11111;
for i in 0..5 {
if interrupts & (1 << i) != 0 {
- self.push(self.cpu.pc)?;
+ self.push(self.cpu.pc);
self.mem.ime = false;
self.cpu.pc = 0x40 + (i << 3);
@@ -21,7 +21,6 @@ impl<S: Serial, A: Audio> GBState<S, A> {
}
}
}
- Ok(())
}
pub fn tima_timer(&mut self, c: u64) {
diff --git a/src/io.rs b/src/io.rs
index b26533a..89f3a3b 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -59,7 +59,10 @@ where
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>);
+ fn dump_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>) -> Result<(), Self::Error>;
+ fn save_state<S: Serial, A: Audio>(&self, state: &GBState<S, A>) -> Result<(), Self::Error>;
+ fn load_state<S: Serial, A: Audio>(&self, state: &mut GBState<S, A>)
+ -> Result<(), Self::Error>;
}
pub struct Gameboy<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> {
@@ -81,6 +84,15 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
}
}
+ pub fn load_state(&mut self) -> Result<(), LS::Error> {
+ self.load_save.load_state(&mut self.state)?;
+ Ok(())
+ }
+
+ pub fn debug(&mut self) {
+ self.state.is_debug = true;
+ }
+
pub fn start(self) {
let Self {
mut window,
@@ -115,7 +127,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
}
was_previously_halted = state.mem.halt;
let c = if !state.mem.halt {
- state.exec_opcode().unwrap()
+ state.exec_opcode()
} else {
halt_time += 4;
4
@@ -127,7 +139,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
state.div_timer(c);
state.tima_timer(c);
state.update_display_interrupts(c);
- state.check_interrupts().unwrap();
+ state.check_interrupts();
state.mem.update_serial();
nanos_sleep += c as f64 * (consts::CPU_CYCLE_LENGTH_NANOS as f64 / speed) as f64;
@@ -144,7 +156,9 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A,
);
if save_state {
- load_save.save_state(&state);
+ if let Err(err) = load_save.save_state(&state) {
+ eprintln!("FAILED SAVE STATE: {:?}", err);
+ }
}
if state.mem.joypad_is_action
diff --git a/src/main.rs b/src/main.rs
index 2e5c80c..9a48983 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -35,6 +35,12 @@ struct Cli {
#[arg(long)]
replay_input: Option<String>,
+ #[arg(long)]
+ state_file: Option<String>,
+
+ #[arg(short, long, default_value_t = false)]
+ load_state: bool,
+
#[arg(short, long, default_value_t = false)]
keyboard: bool,
@@ -65,13 +71,26 @@ fn main() {
gamepad = Box::new(GamepadRecorder::new(gamepad, record_file));
};
- io::Gameboy::<_, _, _, desktop::audio::RodioAudio, _>::new(
+ let mut fs_load_save = FSLoadSave::new(&cli.rom, format!("{}.sav", &cli.rom));
+ if let Some(state_file) = &cli.state_file {
+ fs_load_save = fs_load_save.state_file(state_file);
+ }
+
+ let mut gameboy = io::Gameboy::<_, _, _, desktop::audio::RodioAudio, _>::new(
gamepad,
window,
serial,
- FSLoadSave::new(&cli.rom, format!("{}.sav", &cli.rom))
- .state_file(format!("{}.dump", &cli.rom)),
+ fs_load_save,
cli.speed as f64,
- )
- .start();
+ );
+
+ if cli.debug {
+ gameboy.debug();
+ }
+
+ if cli.load_state {
+ gameboy.load_state().unwrap();
+ }
+
+ gameboy.start();
}
diff --git a/src/mmio.rs b/src/mmio.rs
index aa2f61d..366c94b 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::state::Memory;
impl<S: Serial, A: Audio> Memory<S, A> {
pub fn r_io(&self, addr: u8) -> u8 {
@@ -61,7 +61,7 @@ impl<S: Serial, A: Audio> Memory<S, A> {
}
}
- pub fn w_io(&mut self, addr: u8, value: u8) -> Result<(), MemError> {
+ pub fn w_io(&mut self, addr: u8, value: u8) {
match addr {
0x00 => {
self.joypad_is_action = !value & 0b00100000 != 0;
@@ -213,7 +213,7 @@ impl<S: Serial, A: Audio> Memory<S, A> {
let addr = (value as u16) << 8;
for i in 0..0xa0 {
- self.w(0xfe00 | i, self.r(addr | i)?)?;
+ self.w(0xfe00 | i, self.r(addr | i))
}
}
}
@@ -262,7 +262,5 @@ impl<S: Serial, A: Audio> Memory<S, A> {
self.audio.ch3.wave_pattern[i * 2] = value >> 4;
self.audio.ch3.wave_pattern[i * 2 + 1] = value & 0xf;
}
-
- Ok(())
}
}
diff --git a/src/opcodes.rs b/src/opcodes.rs
index 3779af0..a8af25f 100644
--- a/src/opcodes.rs
+++ b/src/opcodes.rs
@@ -1,35 +1,35 @@
use crate::io::{Audio, Serial};
-use crate::state::{flag, reg, GBState, MemError};
+use crate::state::{flag, reg, GBState};
// The opcodes functions are returning the number of cycles used.
impl<S: Serial, A: Audio> GBState<S, A> {
- fn r_16b_from_pc(&mut self) -> Result<u16, MemError> {
- let p: u16 = self.mem.r(self.cpu.pc)? as u16 | ((self.mem.r(self.cpu.pc + 1)? as u16) << 8);
+ fn r_16b_from_pc(&mut self) -> u16 {
+ let p: u16 = self.mem.r(self.cpu.pc) as u16 | ((self.mem.r(self.cpu.pc + 1) as u16) << 8);
self.cpu.pc += 2;
- Ok(p)
+ p
}
- fn r_8b_from_pc(&mut self) -> Result<u8, MemError> {
- let p = self.mem.r(self.cpu.pc)?;
+ fn r_8b_from_pc(&mut self) -> u8 {
+ let p = self.mem.r(self.cpu.pc);
self.cpu.pc += 1;
- Ok(p)
+ p
}
- fn ldrr(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
+ fn ldrr(&mut self, n1: u8, n2: u8) -> () {
// Load a register into another register
// LD r, r
- self.w_reg(n1, self.r_reg(n2)?)
+ self.w_reg(n1, self.r_reg(n2))
}
- fn ldr8(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn ldr8(&mut self, n1: u8) -> u64 {
// Load an raw 8b value into a register
- let p = self.r_8b_from_pc()?;
+ let p = self.r_8b_from_pc();
- self.w_reg(n1, p)?;
- Ok(8)
+ self.w_reg(n1, p);
+ 8
}
fn ldrr16(&mut self, rr: u8, x: u16) {
@@ -37,13 +37,13 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.w16(rr, x);
}
- fn ldnnsp(&mut self) -> Result<u64, MemError> {
+ fn ldnnsp(&mut self) -> u64 {
// Load SP into an arbitrary position in memory
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
- self.mem.w(p, (self.cpu.sp & 0xff) as u8)?;
- self.mem.w(p + 1, (self.cpu.sp >> 8) as u8)?;
- Ok(20)
+ self.mem.w(p, (self.cpu.sp & 0xff) as u8);
+ self.mem.w(p + 1, (self.cpu.sp >> 8) as u8);
+ 20
}
fn ldsphl(&mut self) -> u64 {
@@ -51,48 +51,48 @@ impl<S: Serial, A: Audio> GBState<S, A> {
8
}
- fn ldnna(&mut self, nn: u16) -> Result<(), MemError> {
+ fn ldnna(&mut self, nn: u16) -> () {
// Load A into an arbitrary position in memory
- self.mem.w(nn, self.cpu.r[reg::A as usize])?;
- Ok(())
+ self.mem.w(nn, self.cpu.r[reg::A as usize]);
+ ()
}
- fn ldann(&mut self, nn: u16) -> Result<(), MemError> {
+ fn ldann(&mut self, nn: u16) -> () {
// Load A from an arbitrary position in memory
- self.cpu.r[reg::A as usize] = self.mem.r(nn)?;
- Ok(())
+ self.cpu.r[reg::A as usize] = self.mem.r(nn);
+ ()
}
- pub fn push(&mut self, x: u16) -> Result<(), MemError> {
+ pub fn push(&mut self, x: u16) -> () {
self.cpu.sp -= 2;
- self.mem.w(self.cpu.sp, (x & 0xff) as u8)?;
+ self.mem.w(self.cpu.sp, (x & 0xff) as u8);
- self.mem.w(self.cpu.sp + 1, (x >> 8) as u8)?;
+ self.mem.w(self.cpu.sp + 1, (x >> 8) as u8);
- Ok(())
+ ()
}
- fn pop(&mut self) -> Result<u16, MemError> {
- let res = self.mem.r(self.cpu.sp)? as u16 | ((self.mem.r(self.cpu.sp + 1)? as u16) << 8);
+ fn pop(&mut self) -> u16 {
+ let res = self.mem.r(self.cpu.sp) as u16 | ((self.mem.r(self.cpu.sp + 1) as u16) << 8);
self.cpu.sp += 2;
- Ok(res)
+ res
}
- fn jr8(&mut self) -> Result<u64, MemError> {
+ fn jr8(&mut self) -> u64 {
// Unconditional relative jump
- let p = self.r_8b_from_pc()?;
+ let p = self.r_8b_from_pc();
self.cpu.pc = (self.cpu.pc as i16 + p as i8 as i16) as u16;
- Ok(12)
+ 12
}
- fn jrcc8(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn jrcc8(&mut self, n1: u8) -> u64 {
// Conditional relative jump
- let p = self.r_8b_from_pc()?;
+ let p = self.r_8b_from_pc();
let mut cycles = 8;
if self.cpu.check_flag(n1 & 0b11) {
@@ -100,16 +100,16 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.pc = (self.cpu.pc as i16 + p as i8 as i16) as u16;
}
- Ok(cycles)
+ cycles
}
- fn jp16(&mut self) -> Result<u64, MemError> {
+ fn jp16(&mut self) -> u64 {
// Unconditional absolute jump
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
self.cpu.pc = p;
- Ok(16)
+ 16
}
fn jphl(&mut self) -> u64 {
@@ -119,9 +119,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
4
}
- fn jpcc16(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn jpcc16(&mut self, n1: u8) -> u64 {
// Conditional absolute jump
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
let mut cycles = 8;
if self.cpu.check_flag(n1 & 0b11) {
@@ -129,35 +129,35 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.pc = p;
}
- Ok(cycles)
+ cycles
}
- fn call(&mut self) -> Result<u64, MemError> {
+ fn call(&mut self) -> u64 {
// Unconditional function call
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
- self.push(self.cpu.pc)?;
+ self.push(self.cpu.pc);
self.cpu.pc = p;
- Ok(24)
+ 24
}
- fn callcc(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn callcc(&mut self, n1: u8) -> u64 {
// Conditional function call
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
let mut cycles = 12;
if self.cpu.check_flag(n1 & 0b11) {
cycles += 12;
- self.push(self.cpu.pc)?;
+ self.push(self.cpu.pc);
self.cpu.pc = p;
}
- Ok(cycles)
+ cycles
}
- fn ret(&mut self) -> Result<u64, MemError> {
- let res = self.pop()?;
+ fn ret(&mut self) -> u64 {
+ let res = self.pop();
if res == 0 {
println!("DEBUG: {:?}", self.cpu);
@@ -166,20 +166,20 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.pc = res;
- Ok(16)
+ 16
}
- fn retcc(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn retcc(&mut self, n1: u8) -> u64 {
let mut cycles = 8;
if self.cpu.check_flag(n1 & 0b11) {
cycles += 12;
- self.cpu.pc = self.pop()?;
+ self.cpu.pc = self.pop();
}
- Ok(cycles)
+ cycles
}
- fn ld00a(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn ld00a(&mut self, n1: u8) -> u64 {
// Load register A into or from memory pointed by rr (BC, DE or HL(+/-))
// LD (rr), A
// LD A, (rr)
@@ -190,10 +190,10 @@ impl<S: Serial, A: Audio> GBState<S, A> {
};
if n1 & 0b001 == 1 {
- self.cpu.r[reg::A as usize] = self.mem.r(self.cpu.r16(ptr_reg))?;
+ self.cpu.r[reg::A as usize] = self.mem.r(self.cpu.r16(ptr_reg));
} else {
self.mem
- .w(self.cpu.r16(ptr_reg), self.cpu.r[reg::A as usize])?;
+ .w(self.cpu.r16(ptr_reg), self.cpu.r[reg::A as usize]);
}
if n1 & 0b110 == 0b100 {
@@ -204,39 +204,39 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.w16(reg::HL, self.cpu.r16(reg::HL) - 1); // (HL-)
}
- Ok(8)
+ 8
}
- fn inc8(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn inc8(&mut self, n1: u8) -> u64 {
// Increment 8 bit register
- self.w_reg(n1, self.r_reg(n1)? + 1)?;
+ self.w_reg(n1, self.r_reg(n1) + 1);
self.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF | flag::H);
- if self.r_reg(n1)? == 0 {
+ if self.r_reg(n1) == 0 {
self.cpu.r[reg::F as usize] |= flag::ZF;
}
- if self.r_reg(n1)? & 0xf == 0x0 {
+ if self.r_reg(n1) & 0xf == 0x0 {
self.cpu.r[reg::F as usize] |= flag::H;
}
- Ok(4)
+ 4
}
- fn dec8(&mut self, n1: u8) -> Result<u64, MemError> {
+ fn dec8(&mut self, n1: u8) -> u64 {
// Decrement 8 bit register
- self.w_reg(n1, self.r_reg(n1)? - 1)?;
+ self.w_reg(n1, self.r_reg(n1) - 1);
self.cpu.r[reg::F as usize] |= flag::N;
self.cpu.r[reg::F as usize] &= !(flag::ZF | flag::H);
- if self.r_reg(n1)? == 0 {
+ if self.r_reg(n1) == 0 {
self.cpu.r[reg::F as usize] |= flag::ZF;
}
- if self.r_reg(n1)? & 0xf == 0xf {
+ if self.r_reg(n1) & 0xf == 0xf {
self.cpu.r[reg::F as usize] |= flag::H;
}
- Ok(4)
+ 4
}
fn inc16(&mut self, rr: u8) -> u64 {
@@ -301,8 +301,8 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.cpu.r[reg::A as usize] ^= 0xff;
}
- fn addsp8(&mut self) -> Result<u64, MemError> {
- let n = self.r_8b_from_pc()? as i8;
+ fn addsp8(&mut self) -> u64 {
+ let n = self.r_8b_from_pc() as i8;
self.cpu.sp = (self.cpu.sp as i32 + n as i32) as u16;
@@ -315,7 +315,7 @@ impl<S: Serial, A: Audio> GBState<S, A> {
if (self.cpu.sp as i32 + n as i32) & !0xffff != 0 {
self.cpu.r[reg::F as usize] |= flag::CY;
}
- Ok(16)
+ 16
}
fn add(&mut self, x: u8) {
@@ -473,9 +473,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
}
}
- fn rlc(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn rlc(&mut self, r_i: u8) -> () {
// ROTATE LEFT the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
self.cpu.r[reg::F as usize] |= (n >> 7) << 4;
n <<= 1;
@@ -483,9 +483,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn rrc(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn rrc(&mut self, r_i: u8) -> () {
// ROTATE RIGHT the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
self.cpu.r[reg::F as usize] |= (n & 1) << 4;
n >>= 1;
@@ -493,10 +493,10 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn rl(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn rl(&mut self, r_i: u8) -> () {
// ROTATE LEFT THROUGH CARRY the input register
// (RLC IS ROTATE AND RL IS ROTATE THROUGH CARRY ! IT DOESN'T MAKE ANY SENSE !!)
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
@@ -506,9 +506,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn rr(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn rr(&mut self, r_i: u8) -> () {
// ROTATE RIGHT THROUGH CARRY the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
let carry = (self.cpu.r[reg::F as usize] & flag::CY) >> 4;
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
@@ -518,9 +518,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn sla(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn sla(&mut self, r_i: u8) -> () {
// Shift left Arithmetic (b0=0) the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
self.cpu.r[reg::F as usize] |= (n >> 7) << 4;
@@ -533,9 +533,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn sra(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn sra(&mut self, r_i: u8) -> () {
// Shift right Arithmetic (b7=b7) the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
self.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
@@ -550,9 +550,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn swap(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn swap(&mut self, r_i: u8) -> () {
// Swap the high nibble and low nibble
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
let nibble_low = n & 0b1111;
let nibble_high = n >> 4;
@@ -568,9 +568,9 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn srl(&mut self, r_i: u8) -> Result<(), MemError> {
+ fn srl(&mut self, r_i: u8) -> () {
// Shift right Logical (b7=0) the input register
- let mut n = self.r_reg(r_i)?;
+ let mut n = self.r_reg(r_i);
self.cpu.r[reg::F as usize] &= !(flag::H | flag::N | flag::ZF | flag::CY);
self.cpu.r[reg::F as usize] |= (n & 0b1) << 4;
@@ -583,48 +583,48 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.w_reg(r_i, n)
}
- fn bit(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
- let z = (((self.r_reg(n2)? >> n1) & 1) ^ 1) << 7;
+ fn bit(&mut self, n1: u8, n2: u8) -> () {
+ let z = (((self.r_reg(n2) >> n1) & 1) ^ 1) << 7;
self.cpu.r[reg::F as usize] &= !(flag::N | flag::ZF);
self.cpu.r[reg::F as usize] |= flag::H | z;
- Ok(())
+ ()
}
- fn set(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
- self.w_reg(n2, self.r_reg(n2)? | (1 << n1))
+ fn set(&mut self, n1: u8, n2: u8) -> () {
+ self.w_reg(n2, self.r_reg(n2) | (1 << n1))
}
- fn res(&mut self, n1: u8, n2: u8) -> Result<(), MemError> {
- self.w_reg(n2, self.r_reg(n2)? & !(1 << n1))
+ fn res(&mut self, n1: u8, n2: u8) -> () {
+ self.w_reg(n2, self.r_reg(n2) & !(1 << n1))
}
// I don't remember why I separated op00, op01, op10 and op11 AND I'M NOT GOING TO CHANGE IT
// BECAUSE I LOVE CHAOS
- fn op00(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ fn op00(&mut self, n1: u8, n2: u8) -> u64 {
// Dispatcher for the instructions starting with 0b00 based on their 3 LSB
match n2 {
0b000 => match n1 {
- 0b000 => Ok(4),
+ 0b000 => 4,
0b001 => self.ldnnsp(),
0b010 => todo!("STOP"),
0b011 => self.jr8(),
_ => self.jrcc8(n1),
},
0b001 => match n1 {
- 0b001 | 0b011 | 0b101 | 0b111 => Ok(self.addhlrr(n1 >> 1)),
+ 0b001 | 0b011 | 0b101 | 0b111 => self.addhlrr(n1 >> 1),
0b000 | 0b010 | 0b100 | 0b110 => {
- let p = self.r_16b_from_pc()?;
+ let p = self.r_16b_from_pc();
self.ldrr16(n1 >> 1, p);
- Ok(12)
+ 12
}
_ => panic!(),
},
0b010 => self.ld00a(n1),
0b011 => match n1 {
- 0b001 | 0b011 | 0b101 | 0b111 => Ok(self.dec16(n1 >> 1)),
- 0b000 | 0b010 | 0b100 | 0b110 => Ok(self.inc16(n1 >> 1)),
+ 0b001 | 0b011 | 0b101 | 0b111 => self.dec16(n1 >> 1),
+ 0b000 | 0b010 | 0b100 | 0b110 => self.inc16(n1 >> 1),
_ => panic!(),
},
0b100 => self.inc8(n1),
@@ -632,77 +632,77 @@ impl<S: Serial, A: Audio> GBState<S, A> {
0b110 => self.ldr8(n1),
0b111 => {
match n1 {
- 0b000 => self.rlc(7)?,
- 0b001 => self.rrc(7)?,
- 0b010 => self.rl(7)?,
- 0b011 => self.rr(7)?,
+ 0b000 => self.rlc(7),
+ 0b001 => self.rrc(7),
+ 0b010 => self.rl(7),
+ 0b011 => self.rr(7),
0b100 => self.daa(),
0b101 => self.cpl(),
0b110 => self.scf(),
0b111 => self.ccf(),
_ => panic!(),
};
- Ok(4)
+ 4
}
_ => panic!(),
}
}
- fn op01(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ fn op01(&mut self, n1: u8, n2: u8) -> u64 {
// Dispatcher for the instructions starting with 0b01 (LD r,r and HALT)
if n1 == 0b110 && n2 == 0b110 {
self.mem.halt = true;
- Ok(4)
+ 4
} else {
- self.ldrr(n1, n2)?;
+ self.ldrr(n1, n2);
if n1 == 0b110 || n2 == 0b110 {
- Ok(8)
+ 8
} else {
- Ok(4)
+ 4
}
}
}
- fn op10(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ fn op10(&mut self, n1: u8, n2: u8) -> u64 {
// Dispatcher for the instructions starting with 0b10 (Arithmetic)
match n1 {
- 0b000 => self.add(self.r_reg(n2)?),
- 0b001 => self.adc(self.r_reg(n2)?),
- 0b010 => self.sub(self.r_reg(n2)?),
- 0b011 => self.sbc(self.r_reg(n2)?),
- 0b100 => self.and(self.r_reg(n2)?),
- 0b101 => self.xor(self.r_reg(n2)?),
- 0b110 => self.or(self.r_reg(n2)?),
- 0b111 => self.cp(self.r_reg(n2)?),
+ 0b000 => self.add(self.r_reg(n2)),
+ 0b001 => self.adc(self.r_reg(n2)),
+ 0b010 => self.sub(self.r_reg(n2)),
+ 0b011 => self.sbc(self.r_reg(n2)),
+ 0b100 => self.and(self.r_reg(n2)),
+ 0b101 => self.xor(self.r_reg(n2)),
+ 0b110 => self.or(self.r_reg(n2)),
+ 0b111 => self.cp(self.r_reg(n2)),
_ => panic!(),
}
if n2 == 0b110 {
- Ok(8)
+ 8
} else {
- Ok(4)
+ 4
}
}
- fn op11(&mut self, n1: u8, n2: u8) -> Result<u64, MemError> {
+ fn op11(&mut self, n1: u8, n2: u8) -> u64 {
match n2 {
0b000 => match n1 {
0b100 => {
- let n = self.r_8b_from_pc()?;
- self.ldnna(n as u16 | 0xff00)?;
- Ok(12)
+ let n = self.r_8b_from_pc();
+ self.ldnna(n as u16 | 0xff00);
+ 12
}
0b101 => self.addsp8(),
0b110 => {
- let n = self.r_8b_from_pc()?;
- self.ldann(n as u16 | 0xff00)?;
- Ok(12)
+ let n = self.r_8b_from_pc();
+ self.ldann(n as u16 | 0xff00);
+ 12
}
0b111 => {
- let n = self.r_8b_from_pc()?;
+ let n = self.r_8b_from_pc();
self.ldrr16(reg::HL, n as u16 + self.cpu.sp);
- Ok(12)
+ 12
}
_ => self.retcc(n1 & 0b11),
},
@@ -713,33 +713,33 @@ impl<S: Serial, A: Audio> GBState<S, A> {
self.ret()
}
- 0b101 => Ok(self.jphl()),
- 0b111 => Ok(self.ldsphl()),
+ 0b101 => self.jphl(),
+ 0b111 => self.ldsphl(),
_ => {
- let p = self.pop()?;
+ let p = self.pop();
self.cpu.r[(n1 >> 1) as usize * 2 + 1] = (p & 0xff) as u8;
self.cpu.r[(n1 >> 1) as usize * 2] = (p >> 8) as u8;
- Ok(12)
+ 12
}
},
0b010 => match n1 {
0b100 => {
- self.ldnna(self.cpu.r[reg::C as usize] as u16 | 0xff00)?;
- Ok(8)
+ self.ldnna(self.cpu.r[reg::C as usize] as u16 | 0xff00);
+ 8
}
0b101 => {
- let nn = self.r_16b_from_pc()?;
- self.ldnna(nn)?;
- Ok(16)
+ let nn = self.r_16b_from_pc();
+ self.ldnna(nn);
+ 16
}
0b110 => {
- self.ldann(self.cpu.r[reg::C as usize] as u16 | 0xff00)?;
- Ok(8)
+ self.ldann(self.cpu.r[reg::C as usize] as u16 | 0xff00);
+ 8
}
0b111 => {
- let nn = self.r_16b_from_pc()?;
- self.ldann(nn)?;
- Ok(16)
+ let nn = self.r_16b_from_pc();
+ self.ldann(nn);
+ 16
}
_ => self.jpcc16(n1 & 0b11),
},
@@ -749,15 +749,15 @@ impl<S: Serial, A: Audio> GBState<S, A> {
0b011 | 0b100 | 0b101 => unimplemented!(),
0b010 => {
self.cpu.print_debug();
- Ok(4)
+ 4
}
0b110 => {
self.mem.ime = false;
- Ok(4)
+ 4
}
0b111 => {
self.mem.ime = true;
- Ok(4)
+ 4
}
_ => panic!(),
},
@@ -768,12 +768,12 @@ impl<S: Serial, A: Audio> GBState<S, A> {
_ => {
let value = self.cpu.r[(n1 >> 1) as usize * 2 + 1] as u16
| ((self.cpu.r[(n1 >> 1) as usize * 2] as u16) << 8);
- self.push(value)?;
- Ok(16)
+ self.push(value);
+ 16
}
},
0b110 => {
- let p = self.r_8b_from_pc()?;
+ let p = self.r_8b_from_pc();
match n1 {
0b000 => self.add(p),
@@ -786,21 +786,21 @@ impl<S: Serial, A: Audio> GBState<S, A> {
0b111 => self.cp(p),
_ => panic!(),
}
- Ok(8)
+ 8
}
0b111 => {
let p = n1 << 3;
- self.push(self.cpu.pc)?;
+ self.push(self.cpu.pc);
self.cpu.pc = p as u16;
- Ok(16)
+ 16
} // RST
_ => panic!(),
}
}
- fn op_bitwise(&mut self) -> Result<u64, MemError> {
- let p = self.r_8b_from_pc()?;
+ fn op_bitwise(&mut self) -> u64 {
+ let p = self.r_8b_from_pc();
let opcode = p >> 6;
let n1 = p >> 3 & 0b111;
let n2 = p & 0b111;
@@ -821,16 +821,16 @@ impl<S: Serial, A: Audio> GBState<S, A> {
0b10 => self.res(n1, n2),
0b11 => self.set(n1, n2),
_ => panic!(),
- }?;
+ };
if n2 == 0b110 {
- Ok(16)
+ 16
} else {
- Ok(8)
+ 8
}
}
- pub fn exec_opcode(&mut self) -> Result<u64, MemError> {
- let opcode = self.mem.r(self.cpu.pc)?;
+ pub fn exec_opcode(&mut self) -> u64 {
+ let opcode = self.mem.r(self.cpu.pc);
if self.is_debug {
println!(
diff --git a/src/state.rs b/src/state.rs
index 67da18e..530296e 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -128,10 +128,10 @@ pub struct Memory<S: Serial, A: Audio> {
pub rom: Box<[u8; 0x200000]>,
// 4 KiB Work RAM 00
- wram_00: Box<[u8; 0x1000]>,
+ pub wram_00: Box<[u8; 0x1000]>,
// 4 KiB Work RAM 00
- wram_01: Box<[u8; 0x1000]>,
+ pub wram_01: Box<[u8; 0x1000]>,
// External RAM
pub external_ram: Box<[u8; 0x8000]>,
@@ -142,7 +142,7 @@ pub struct Memory<S: Serial, A: Audio> {
pub io: Box<[u8; 0x80]>,
// High RAM
- hram: Box<[u8; 0x7f]>,
+ pub hram: Box<[u8; 0x7f]>,
pub audio: Channels<A>,
@@ -173,13 +173,6 @@ pub struct Memory<S: Serial, A: Audio> {
pub serial_control: u8,
}
-#[derive(Debug)]
-pub enum MemError {
- WritingToROM,
- Unimplemented,
- NotUsable,
-}
-
mod serial_control_flags {
pub const CLOCK_SELECT: u8 = 0b1;
pub const TRANSFER_ENABLE: u8 = 0b10000000;
@@ -249,79 +242,70 @@ impl<S: Serial, A: Audio> Memory<S, A> {
}
}
- pub fn r(&self, addr: u16) -> Result<u8, MemError> {
+ pub fn r(&self, addr: u16) -> u8 {
if (addr < 0x100 || (addr >= 0x200 && addr < 0x900)) && self.boot_rom_on {
- Ok(self.boot_rom[addr as usize])
+ self.boot_rom[addr as usize]
} else if addr < 0x4000 {
- Ok(self.rom[addr as usize])
+ self.rom[addr as usize]
} else if addr < 0x8000 {
- Ok(self.rom[self.rom_bank as usize * 0x4000 + addr as usize - 0x4000 as usize])
+ self.rom[self.rom_bank as usize * 0x4000 + addr as usize - 0x4000 as usize]
} else if addr >= 0xa000 && addr < 0xc000 {
if self.ram_bank_enabled {
- Ok(self.external_ram[self.ram_bank as usize * 0x2000 + addr as usize - 0xa000])
+ self.external_ram[self.ram_bank as usize * 0x2000 + addr as usize - 0xa000]
} else {
- Ok(0xff)
+ 0xff
}
} else if addr >= 0xc000 && addr < 0xd000 {
- Ok(self.wram_00[addr as usize - 0xc000])
+ self.wram_00[addr as usize - 0xc000]
} else if addr >= 0xd000 && addr < 0xe000 {
- Ok(self.wram_01[addr as usize - 0xd000])
+ self.wram_01[addr as usize - 0xd000]
} else if (addr >= 0x8000 && addr < 0xa000) || (addr >= 0xfe00 && addr < 0xfea0) {
self.display.r(addr & !0x8000)
} else if addr >= 0xff00 && addr < 0xff80 {
- Ok(self.r_io((addr & 0xff) as u8))
+ self.r_io((addr & 0xff) as u8)
} else if addr >= 0xff80 && addr < 0xffff {
- Ok(self.hram[addr as usize - 0xff80])
+ self.hram[addr as usize - 0xff80]
} else if addr == 0xffff {
- Ok(self.interrupts_register)
+ self.interrupts_register
} else {
println!(
"Trying to read at address 0x{:04x} which is unimplemented",
addr
);
- Ok(0) //Err(MemError::Unimplemented)
+ 0
}
}
- pub fn w(&mut self, addr: u16, value: u8) -> Result<(), MemError> {
+ pub fn w(&mut self, addr: u16, value: u8) {
if addr < 0x2000 {
self.ram_bank_enabled = value == 0x0a;
- Ok(())
} else if addr >= 0x2000 && addr < 0x4000 {
if value == 0 {
self.rom_bank = 1
} else {
self.rom_bank = value & 0b1111111;
}
- Ok(())
} else if addr >= 0x4000 && addr < 0x6000 {
self.ram_bank = value & 0b11;
- Ok(())
} else if addr >= 0xa000 && addr < 0xc000 {
self.external_ram[self.ram_bank as usize * 0x2000 + addr as usize - 0xa000] = value;
- Ok(())
} else if addr >= 0xc000 && addr < 0xd000 {
self.wram_00[addr as usize - 0xc000] = value;
- Ok(())
} else if addr >= 0xd000 && addr < 0xe000 {
self.wram_01[addr as usize - 0xd000] = value;
- Ok(())
} else if (addr >= 0x8000 && addr < 0xa000) || (addr >= 0xfe00 && addr < 0xfea0) {
- self.display.w(addr & !0x8000, value)
+ self.display.w(addr & !0x8000, value);
} else if addr >= 0xff00 && addr < 0xff80 {
- Ok(self.w_io((addr & 0xff) as u8, value)?)
+ self.w_io((addr & 0xff) as u8, value);
} else if addr >= 0xff80 && addr < 0xffff {
self.hram[addr as usize - 0xff80] = value;
- Ok(())
} else if addr == 0xffff {
self.interrupts_register = value;
- Ok(())
} else {
println!(
"Trying to write at address 0x{:04x} which is unimplemented (value: {:02x})",
addr, value
);
- Ok(()) //Err(MemError::Unimplemented)
}
}
}
@@ -349,11 +333,11 @@ impl<S: Serial, A: Audio> GBState<S, A> {
}
}
- pub fn r_reg(&self, r_i: u8) -> Result<u8, MemError> {
+ pub fn r_reg(&self, r_i: u8) -> u8 {
if r_i < 6 {
- Ok(self.cpu.r[r_i as usize])
+ self.cpu.r[r_i as usize]
} else if r_i == 7 {
- Ok(self.cpu.r[6])
+ self.cpu.r[6]
} else if r_i == 6 {
self.mem.r(self.cpu.r16(reg::HL))
} else {
@@ -361,17 +345,16 @@ impl<S: Serial, A: Audio> GBState<S, A> {
}
}
- pub fn w_reg(&mut self, r_i: u8, value: u8) -> Result<(), MemError> {
+ pub fn w_reg(&mut self, r_i: u8, value: u8) {
if r_i < 6 {
self.cpu.r[r_i as usize] = value;
} else if r_i == 7 {
self.cpu.r[6] = value;
} else if r_i == 6 {
- self.mem.w(self.cpu.r16(reg::HL), value)?;
+ self.mem.w(self.cpu.r16(reg::HL), value);
} else {
panic!("r_i must be a 3 bits register input number")
}
- Ok(())
}
pub fn debug(&self, s: &str) -> () {