Refactor run.rs to reduce repetition and use proper Error types

jsmoo
Nick Krichevsky 2022-04-08 19:42:37 -04:00
parent af38d1edf4
commit 75235ad5e0
1 changed files with 121 additions and 65 deletions

View File

@ -4,10 +4,23 @@ use crate::{
memory::{self, Memory},
register::{self, Registers},
};
use thiserror::Error;
mod instructions;
mod parse;
/// `Error` represents a run error. Typically these will be fatal and panicking on their emission
/// is likely the best course of action
#[derive(Error, Debug)]
enum Error {
/// An invalid address was provided to an instruction that required an immediate address
#[error("invalid immediate address: {0:X}")]
InvalidImmediateAddress(usize),
/// An invalid address was stored in the given register and attempted to be loaded to/from
#[error("invalid address stored in register ({0}): {1:X}")]
InvalidRegisterAddress(register::Combined, usize),
}
#[derive(Debug, Default, Clone)]
pub struct Processor {
num_cycles: u64,
@ -15,6 +28,13 @@ pub struct Processor {
memory: Memory,
}
macro_rules! assert_ok {
($result: expr) => {
let res: Result<_, _> = $result;
assert!(res.is_ok(), "{}", res.unwrap_err());
};
}
impl Processor {
fn run(&mut self, instruction: &RunnableInstruction) {
match instruction.instruction {
@ -28,55 +48,29 @@ impl Processor {
}
Instruction::LDFromRegisterAddress { src, dst } => {
let src_address = self.registers.get_combined_register(src);
let memory_value = self
.memory
.get(src_address.into())
.unwrap_or_else(|| panic!("invalid value stored in ({src}): {src_address:X}"));
self.registers.set_single_register(dst, memory_value);
let load_res = self.load_from_register_address_to_register(dst, src);
assert_ok!(load_res);
}
Instruction::LDFromImmediateAddress { src_address, dst } => {
let memory_value = self
.memory
.get(src_address.into())
.unwrap_or_else(|| panic!("invalid address immediate: {src_address}"));
self.registers.set_single_register(dst, memory_value);
let load_res = self.load_from_address_to_register(dst, src_address.into());
assert_ok!(load_res);
}
Instruction::LDToRegisterAddress { src, dst } => {
let value = self.registers.get_single_register(src);
let dst_address = self.registers.get_combined_register(dst);
let memory_value = self.memory.set(dst_address.into(), value);
assert!(
memory_value.is_some(),
"invalid address stored in ({src}): {dst_address:X}"
);
let load_res = self.load_from_register_to_register_address(dst, src);
assert_ok!(load_res);
}
Instruction::LDToImmediateAddress { src, dst_address } => {
let register_value = self.registers.get_single_register(src);
let memory_value = self.memory.set(dst_address.into(), register_value);
assert!(
memory_value.is_some(),
"invalid address immediate: {dst_address:X}"
);
let load_res = self.load_from_register_to_address(dst_address.into(), src);
assert_ok!(load_res);
}
Instruction::LDnToHLAddress { value } => {
let dst_address = self.registers.get_combined_register(register::Combined::HL);
let memory_value = self.memory.set(dst_address.into(), value);
assert!(
memory_value.is_some(),
"invalid address stored in (hl): {dst_address:X}"
);
let dest_address = self.registers.get_combined_register(register::Combined::HL);
let load_res = self.load_8bit_immediate_to_address(dest_address.into(), value);
assert_ok!(load_res);
}
Instruction::LDFromMemoryRelativeToIORegisterStart {
@ -86,11 +80,8 @@ impl Processor {
let src_address_offset = self.registers.get_single_register(offset_register);
let src_address =
memory::IO_REGISTER_START_ADDRESS + usize::from(src_address_offset);
let memory_value = self.memory.get(src_address).unwrap_or_else(|| {
panic!("invalid offset stored in src register ({offset_register})")
});
self.registers.set_single_register(dst, memory_value);
let load_res = self.load_from_address_to_register(dst, src_address);
assert_ok!(load_res);
}
Instruction::LDToMemoryRelativeToIORegisterStart {
src,
@ -99,37 +90,20 @@ impl Processor {
let dst_address_offset = self.registers.get_single_register(offset_register);
let dst_address =
memory::IO_REGISTER_START_ADDRESS + usize::from(dst_address_offset);
let value = self.registers.get_single_register(src);
let memory_value = self.memory.set(dst_address, value);
assert!(
memory_value.is_some(),
"invalid offset stored in dst register ({offset_register})"
);
let load_res = self.load_from_register_to_address(dst_address, src);
assert_ok!(load_res);
}
Instruction::LDToRegisterAddressThenDec { dst, src } => {
let value = self.registers.get_single_register(src);
let dst_address = self.registers.get_combined_register(dst);
let memory_value = self.memory.set(dst_address.into(), value);
assert!(
memory_value.is_some(),
"invalid address stored in ({src}): {dst_address:X}"
);
let load_res = self.load_from_register_to_address(dst_address.into(), src);
assert_ok!(load_res);
self.registers.set_combined_register(dst, dst_address - 1);
}
Instruction::LDToRegisterAddressThenInc { dst, src } => {
let value = self.registers.get_single_register(src);
let dst_address = self.registers.get_combined_register(dst);
let memory_value = self.memory.set(dst_address.into(), value);
assert!(
memory_value.is_some(),
"invalid address stored in ({src}): {dst_address:X}"
);
let load_res = self.load_from_register_to_address(dst_address.into(), src);
assert_ok!(load_res);
self.registers.set_combined_register(dst, dst_address + 1);
}
@ -137,6 +111,88 @@ impl Processor {
self.num_cycles += u64::from(instruction.cycles);
}
fn load_from_register_to_register_address(
&mut self,
register_with_dst_address: register::Combined,
src: register::Single,
) -> Result<(), Error> {
let dst_address = self
.registers
.get_combined_register(register_with_dst_address);
let load_res = self.load_from_register_to_address(dst_address.into(), src);
// We didn't truly load to an immediate address - we're just using that method for convenience.
// map the error as such.
if let Err(Error::InvalidImmediateAddress(addr)) = load_res {
Err(Error::InvalidRegisterAddress(
register_with_dst_address,
addr,
))
} else {
load_res
}
}
fn load_from_register_to_address(
&mut self,
dst_address: usize,
src: register::Single,
) -> Result<(), Error> {
let register_value = self.registers.get_single_register(src);
self.load_8bit_immediate_to_address(dst_address, register_value)
}
fn load_8bit_immediate_to_address(
&mut self,
dst_address: usize,
value: u8,
) -> Result<(), Error> {
self.memory
.set(dst_address, value)
.map(|_| ())
.ok_or(Error::InvalidImmediateAddress(dst_address))
}
fn load_from_address_to_register(
&mut self,
dst: register::Single,
src_address: usize,
) -> Result<(), Error> {
let memory_value = self.memory.get(src_address);
match memory_value {
None => Err(Error::InvalidImmediateAddress(src_address)),
Some(value) => {
// technically this could be done with map but I think this is more readable
self.registers.set_single_register(dst, value);
Ok(())
}
}
}
fn load_from_register_address_to_register(
&mut self,
dst: register::Single,
register_with_src_address: register::Combined,
) -> Result<(), Error> {
let src_address = self
.registers
.get_combined_register(register_with_src_address);
let load_res = self.load_from_address_to_register(dst, src_address.into());
// We didn't truly load to an immediate address - we're just using that method for convenience.
// map the error as such.
if let Err(Error::InvalidImmediateAddress(addr)) = load_res {
Err(Error::InvalidRegisterAddress(
register_with_src_address,
addr,
))
} else {
load_res
}
}
}
#[cfg(test)]