memory mapping

This commit is contained in:
Martin Löffler 2023-01-28 23:43:16 +01:00
parent 465aadd54e
commit a97824b26e
Signed by: FatalErrorCoded
GPG key ID: FFEF368AC076566A
6 changed files with 182 additions and 112 deletions

View file

@ -1,22 +1,23 @@
use crate::mapper::MemoryMapper;
use crate::structs::{get_register_pair, set_register, set_register_pair}; use crate::structs::{get_register_pair, set_register, set_register_pair};
use crate::{get_register, EmulatorState, Register}; use crate::{get_register, EmulatorState, Register};
/// Sets the condition code flags according to `result`. /// Sets the condition code flags according to `result`.
/// Does not set the carry flag and will always set the Z, S and P flags. /// Does not set the carry flag and will always set the Z, S and P flags.
#[inline(always)] #[inline(always)]
fn set_cc(state: &mut EmulatorState, result: u8) { fn set_cc<M: MemoryMapper>(state: &mut EmulatorState<M>, result: u8) {
state.cc.z = result == 0; state.cc.z = result == 0;
state.cc.s = result & 0x80 > 0; state.cc.s = result & 0x80 > 0;
state.cc.p = result.count_ones() % 2 == 0; state.cc.p = result.count_ones() % 2 == 0;
} }
/// Add values of `register` and `A`, add +1 if carry arg is set (either false or state.cc.c) /// Add values of `register` and `A`, add +1 if carry arg is set (either false or state.cc.c)
pub fn add_reg(register: Register, carry: bool, state: &mut EmulatorState) { pub fn add_reg<M: MemoryMapper>(register: Register, carry: bool, state: &mut EmulatorState<M>) {
add(get_register(register, state), carry, state); add(get_register(register, state), carry, state);
} }
/// Add values of input byte and `A`, add +1 if carry arg is set (either false or state.cc.c) /// Add values of input byte and `A`, add +1 if carry arg is set (either false or state.cc.c)
pub fn add(byte: u8, carry: bool, state: &mut EmulatorState) { pub fn add<M: MemoryMapper>(byte: u8, carry: bool, state: &mut EmulatorState<M>) {
let (a, first) = state.a.overflowing_add(byte); let (a, first) = state.a.overflowing_add(byte);
let (result, second) = a.overflowing_add(carry as u8); let (result, second) = a.overflowing_add(carry as u8);
@ -25,11 +26,11 @@ pub fn add(byte: u8, carry: bool, state: &mut EmulatorState) {
state.a = result; state.a = result;
} }
pub fn sub_reg(register: Register, borrow: bool, state: &mut EmulatorState) { pub fn sub_reg<M: MemoryMapper>(register: Register, borrow: bool, state: &mut EmulatorState<M>) {
sub(get_register(register, state), borrow, state); sub(get_register(register, state), borrow, state);
} }
pub fn sub(byte: u8, borrow: bool, state: &mut EmulatorState) { pub fn sub<M: MemoryMapper>(byte: u8, borrow: bool, state: &mut EmulatorState<M>) {
let (a, first) = state.a.overflowing_sub(byte); let (a, first) = state.a.overflowing_sub(byte);
let (result, second) = a.overflowing_sub(borrow as u8); let (result, second) = a.overflowing_sub(borrow as u8);
@ -40,11 +41,11 @@ pub fn sub(byte: u8, borrow: bool, state: &mut EmulatorState) {
macro_rules! bitwise_op { macro_rules! bitwise_op {
($name:ident, $reg_name:ident, $op:tt) => { ($name:ident, $reg_name:ident, $op:tt) => {
pub fn $reg_name(register: Register, state: &mut EmulatorState) { pub fn $reg_name<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
$name(get_register(register, state), state); $name(get_register(register, state), state);
} }
pub fn $name(byte: u8, state: &mut EmulatorState) { pub fn $name<M: MemoryMapper>(byte: u8, state: &mut EmulatorState<M>) {
let result = state.a $op byte; let result = state.a $op byte;
state.cc.c = false; state.cc.c = false;
set_cc(state, result); set_cc(state, result);
@ -57,18 +58,18 @@ bitwise_op!(and, and_reg, &);
bitwise_op!(or, or_reg, |); bitwise_op!(or, or_reg, |);
bitwise_op!(xor, xor_reg, ^); bitwise_op!(xor, xor_reg, ^);
pub fn cmp_reg(register: Register, state: &mut EmulatorState) { pub fn cmp_reg<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
cmp(get_register(register, state), state); cmp(get_register(register, state), state);
} }
pub fn cmp(byte: u8, state: &mut EmulatorState) { pub fn cmp<M: MemoryMapper>(byte: u8, state: &mut EmulatorState<M>) {
let (result, carry) = state.a.overflowing_sub(byte); let (result, carry) = state.a.overflowing_sub(byte);
state.cc.c = carry; state.cc.c = carry;
set_cc(state, result); set_cc(state, result);
} }
/// Double precision add - Add B&C, D&E or H&L to H&L /// Double precision add - Add B&C, D&E or H&L to H&L
pub fn dad(register: Register, state: &mut EmulatorState) { pub fn dad<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let num = get_register_pair(register, state); let num = get_register_pair(register, state);
let (result, overflow) = num.overflowing_add(u16::from_le_bytes([state.l, state.h])); let (result, overflow) = num.overflowing_add(u16::from_le_bytes([state.l, state.h]));
@ -81,50 +82,50 @@ pub fn dad(register: Register, state: &mut EmulatorState) {
} }
/// Increase register /// Increase register
pub fn inr(register: Register, state: &mut EmulatorState) { pub fn inr<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let (result, _) = get_register(register, state).overflowing_add(1); let (result, _) = get_register(register, state).overflowing_add(1);
set_cc(state, result); set_cc(state, result);
set_register(register, result, state); set_register(register, result, state);
} }
/// Decrease register /// Decrease register
pub fn dcr(register: Register, state: &mut EmulatorState) { pub fn dcr<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let (result, _) = get_register(register, state).overflowing_sub(1); let (result, _) = get_register(register, state).overflowing_sub(1);
set_cc(state, result); set_cc(state, result);
set_register(register, result, state); set_register(register, result, state);
} }
/// Increase register pair /// Increase register pair
pub fn inx(register: Register, state: &mut EmulatorState) { pub fn inx<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let (result, _) = get_register_pair(register, state).overflowing_add(1); let (result, _) = get_register_pair(register, state).overflowing_add(1);
set_register_pair(register, result, state); set_register_pair(register, result, state);
} }
/// Decrease register pair /// Decrease register pair
pub fn dcx(register: Register, state: &mut EmulatorState) { pub fn dcx<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let (result, _) = get_register_pair(register, state).overflowing_sub(1); let (result, _) = get_register_pair(register, state).overflowing_sub(1);
set_register_pair(register, result, state); set_register_pair(register, result, state);
} }
pub fn rlc(state: &mut EmulatorState) { pub fn rlc<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let result = state.a.rotate_left(1); let result = state.a.rotate_left(1);
state.a = result; state.a = result;
state.cc.c = result & 0x01 > 0; state.cc.c = result & 0x01 > 0;
} }
pub fn rrc(state: &mut EmulatorState) { pub fn rrc<M: MemoryMapper>(state: &mut EmulatorState<M>) {
state.cc.c = state.a & 0x01 > 0; state.cc.c = state.a & 0x01 > 0;
state.a = state.a.rotate_right(1); state.a = state.a.rotate_right(1);
} }
pub fn ral(state: &mut EmulatorState) { pub fn ral<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let new_carry = state.a & 0x80 > 0; let new_carry = state.a & 0x80 > 0;
let result = state.a << 1; let result = state.a << 1;
state.a = result | new_carry as u8; state.a = result | new_carry as u8;
state.cc.c = new_carry; state.cc.c = new_carry;
} }
pub fn rar(state: &mut EmulatorState) { pub fn rar<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let new_carry = state.a & 0x01 > 0; let new_carry = state.a & 0x01 > 0;
let result = state.a >> 1; let result = state.a >> 1;
state.a = result | (new_carry as u8) << 7; state.a = result | (new_carry as u8) << 7;

View file

@ -1,38 +1,39 @@
use crate::mapper::MemoryMapper;
use crate::transfer::{pop, push}; use crate::transfer::{pop, push};
use crate::{get_register_pair, EmulatorState, Register}; use crate::{get_register_pair, EmulatorState, Register};
/// Jump (set PC) to specified address /// Jump (set PC) to specified address
pub fn jump(address: u16, state: &mut EmulatorState) { pub fn jump<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.pc = address; state.pc = address;
} }
/// Jump to a specified address only if `cond` is true /// Jump to a specified address only if `cond` is true
pub fn jump_cond(address: u16, cond: bool, state: &mut EmulatorState) { pub fn jump_cond<M: MemoryMapper>(address: u16, cond: bool, state: &mut EmulatorState<M>) {
if cond { if cond {
jump(address, state); jump(address, state);
} }
} }
/// Push the current PC to the stack and jump to the specified address /// Push the current PC to the stack and jump to the specified address
pub fn call(address: u16, state: &mut EmulatorState) { pub fn call<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
push(state.pc, state); push(state.pc, state);
jump(address, state); jump(address, state);
} }
// Push the current PC to the stack and jump to the specified address if `cond` is true // Push the current PC to the stack and jump to the specified address if `cond` is true
pub fn call_cond(address: u16, cond: bool, state: &mut EmulatorState) { pub fn call_cond<M: MemoryMapper>(address: u16, cond: bool, state: &mut EmulatorState<M>) {
if cond { if cond {
call(address, state); call(address, state);
} }
} }
// Pop a value from the stack and jump to it // Pop a value from the stack and jump to it
pub fn ret(state: &mut EmulatorState) { pub fn ret<M: MemoryMapper>(state: &mut EmulatorState<M>) {
jump(pop(state), state); jump(pop(state), state);
} }
// Pop a value from the stack and jump to it if `cond` is true // Pop a value from the stack and jump to it if `cond` is true
pub fn ret_cond(cond: bool, state: &mut EmulatorState) { pub fn ret_cond<M: MemoryMapper>(cond: bool, state: &mut EmulatorState<M>) {
if cond { if cond {
ret(state) ret(state)
} }
@ -41,12 +42,12 @@ pub fn ret_cond(cond: bool, state: &mut EmulatorState) {
/// "Restart" at (call) a specific interrupt vector /// "Restart" at (call) a specific interrupt vector
/// ///
/// Panics if `vector` is 8 or above /// Panics if `vector` is 8 or above
pub fn restart(vector: u8, state: &mut EmulatorState) { pub fn restart<M: MemoryMapper>(vector: u8, state: &mut EmulatorState<M>) {
assert!(vector < 8, "Illegal restart vector"); assert!(vector < 8, "Illegal restart vector");
call((vector * 8) as u16, state); call((vector * 8) as u16, state);
} }
/// Load a new program counter from the HL register pair /// Load a new program counter from the HL register pair
pub fn pchl(state: &mut EmulatorState) { pub fn pchl<M: MemoryMapper>(state: &mut EmulatorState<M>) {
state.pc = get_register_pair(Register::H, state); state.pc = get_register_pair(Register::H, state);
} }

View file

@ -1,44 +1,55 @@
use crate::mapper::MemoryMapper;
use crate::{ use crate::{
get_register, get_register_pair, set_register, set_register_pair, EmulatorState, Register, get_register, get_register_pair, set_register, set_register_pair, EmulatorState, Register,
}; };
/// Move (copy) value from source to destination register /// Move (copy) value from source to destination register
pub fn mov(src: Register, dest: Register, state: &mut EmulatorState) { pub fn mov<M: MemoryMapper>(src: Register, dest: Register, state: &mut EmulatorState<M>) {
let data = get_register(src, state); let data = get_register(src, state);
set_register(dest, data, state); set_register(dest, data, state);
} }
/// Move immediate into destination register /// Move immediate into destination register
pub fn mvi(byte: u8, dest: Register, state: &mut EmulatorState) { pub fn mvi<M: MemoryMapper>(byte: u8, dest: Register, state: &mut EmulatorState<M>) {
set_register(dest, byte, state); set_register(dest, byte, state);
} }
// Store accumulator to the speficied address
pub fn sta<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.write_byte(address, state.a);
}
// Load accumulator from the specified address
pub fn lda<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.a = state.read_byte(address);
}
/// Store accumulator using the BC or DE register pair /// Store accumulator using the BC or DE register pair
pub fn stax(register: Register, state: &mut EmulatorState) { pub fn stax<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let address = get_register_pair(register, state); let address = get_register_pair(register, state);
state.memory[address as usize] = state.a; state.write_byte(address, state.a);
} }
/// Load accumulator using the BC or DE register pair /// Load accumulator using the BC or DE register pair
pub fn ldax(register: Register, state: &mut EmulatorState) { pub fn ldax<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
let address = get_register_pair(register, state); let address = get_register_pair(register, state);
state.a = state.memory[address as usize]; state.a = state.read_byte(address);
} }
/// Store a 16-bit word from H and L to the specified address /// Store a 16-bit word from H and L to the specified address
pub fn shld(address: u16, state: &mut EmulatorState) { pub fn shld<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.memory[address as usize] = state.l; state.write_byte(address, state.l);
state.memory[address as usize + 1] = state.h; state.write_byte(address.overflowing_add(1).0, state.h)
} }
/// Load a 16-bit word into H and L from the specified address /// Load a 16-bit word into H and L from the specified address
pub fn lhld(address: u16, state: &mut EmulatorState) { pub fn lhld<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.l = state.memory[address as usize]; state.l = state.read_byte(address);
state.h = state.memory[address as usize + 1]; state.h = state.read_byte(address.overflowing_add(1).0);
} }
/// Exchange the HL register pair with the value at the top of the stack /// Exchange the HL register pair with the value at the top of the stack
pub fn xthl(state: &mut EmulatorState) { pub fn xthl<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let at_hl = get_register_pair(Register::H, state); let at_hl = get_register_pair(Register::H, state);
let at_stack = state.read_word(state.sp); let at_stack = state.read_word(state.sp);
// Set HL to the 16-bit value currently on top of the stack // Set HL to the 16-bit value currently on top of the stack
@ -48,12 +59,12 @@ pub fn xthl(state: &mut EmulatorState) {
} }
/// Load the stack pointer from the HL register pair /// Load the stack pointer from the HL register pair
pub fn sphl(state: &mut EmulatorState) { pub fn sphl<M: MemoryMapper>(state: &mut EmulatorState<M>) {
state.sp = get_register_pair(Register::H, state); state.sp = get_register_pair(Register::H, state);
} }
/// Exchange the HL and DE register pairs /// Exchange the HL and DE register pairs
pub fn xchg(state: &mut EmulatorState) { pub fn xchg<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let at_hl = get_register_pair(Register::H, state); let at_hl = get_register_pair(Register::H, state);
let at_de = get_register_pair(Register::D, state); let at_de = get_register_pair(Register::D, state);
// Set HL to DE // Set HL to DE
@ -64,23 +75,23 @@ pub fn xchg(state: &mut EmulatorState) {
/* STACK */ /* STACK */
/// Push a 16-bit value from a register pair onto the stack /// Push a 16-bit value from a register pair onto the stack
pub fn push_reg(register: Register, state: &mut EmulatorState) { pub fn push_reg<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
push(get_register_pair(register, state), state); push(get_register_pair(register, state), state);
} }
/// Push a 16-bit value onto the stack /// Push a 16-bit value onto the stack
pub fn push(value: u16, state: &mut EmulatorState) { pub fn push<M: MemoryMapper>(value: u16, state: &mut EmulatorState<M>) {
(state.sp, ..) = state.sp.overflowing_sub(2); (state.sp, ..) = state.sp.overflowing_sub(2);
state.write_word(state.sp, value); state.write_word(state.sp, value);
} }
/// Pop a 16-bit value from the stack into a register pair /// Pop a 16-bit value from the stack into a register pair
pub fn pop_reg(register: Register, state: &mut EmulatorState) { pub fn pop_reg<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) {
set_register_pair(register, pop(state), state); set_register_pair(register, pop(state), state);
} }
/// Pop a 16-bit value from the stack /// Pop a 16-bit value from the stack
pub fn pop(state: &mut EmulatorState) -> u16 { pub fn pop<M: MemoryMapper>(state: &mut EmulatorState<M>) -> u16 {
let value = state.read_word(state.sp); let value = state.read_word(state.sp);
(state.sp, ..) = state.sp.overflowing_add(2); (state.sp, ..) = state.sp.overflowing_add(2);
value value
@ -88,6 +99,7 @@ pub fn pop(state: &mut EmulatorState) -> u16 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::mapper::TestMapper;
use crate::EmulatorState; use crate::EmulatorState;
#[test] #[test]
@ -100,14 +112,14 @@ mod tests {
// memory address two less than the contents of the // memory address two less than the contents of the
// stack pointer. // stack pointer.
// (3) The stack pointer is automatically decremented by two. // (3) The stack pointer is automatically decremented by two.
let mut state = EmulatorState { let mut state: EmulatorState<TestMapper> = EmulatorState {
sp: 0x12, sp: 0x12,
..Default::default() ..Default::default()
}; };
super::push(0x1234, &mut state); super::push(0x1234, &mut state);
assert_eq!(state.sp, 0x10); assert_eq!(state.sp, 0x10);
assert_eq!(state.memory[0x10..=0x11], [0x34, 0x12]); assert_eq!(state.mapper.0[0x10..=0x11], [0x34, 0x12]);
} }
#[test] #[test]
@ -121,13 +133,13 @@ mod tests {
// memory address one greater than the address held in // memory address one greater than the address held in
// the stack pointer. // the stack pointer.
// (3) The stack pointer is automatically incremented by two. // (3) The stack pointer is automatically incremented by two.
let mut state = EmulatorState { let mut state: EmulatorState<TestMapper> = EmulatorState {
sp: 0x10, sp: 0x10,
..Default::default() ..Default::default()
}; };
state.memory[0x10] = 0x34; state.mapper.0[0x10] = 0x34;
state.memory[0x11] = 0x12; state.mapper.0[0x11] = 0x12;
assert_eq!(super::pop(&mut state), 0x1234); assert_eq!(super::pop(&mut state), 0x1234);
} }
} }

View file

@ -1,15 +1,18 @@
use instructions::{arithmetic, branch, transfer}; use instructions::{arithmetic, branch, transfer};
use mapper::MemoryMapper;
use std::{env, fs}; use std::{env, fs};
use crate::structs::*; use crate::structs::*;
mod instructions; mod instructions;
mod mapper;
mod structs; mod structs;
pub const MEMORY_SIZE: usize = 8192; pub const MEMORY_SIZE: usize = 8192;
fn main() { fn main() {
let mut state = EmulatorState::default(); use crate::mapper::TestMapper;
let mut state = EmulatorState::<TestMapper>::default();
// Load the ROM into memory // Load the ROM into memory
let mut args = env::args(); let mut args = env::args();
@ -18,10 +21,10 @@ fn main() {
.expect("Provide a path to a ROM file to emulate as an argument"); .expect("Provide a path to a ROM file to emulate as an argument");
let file = fs::read(filename).expect("where file"); let file = fs::read(filename).expect("where file");
let to_copy = MEMORY_SIZE.min(file.len()); let to_copy = state.mapper.0.len().min(file.len());
state.memory[..to_copy].copy_from_slice(&file[..to_copy]); state.mapper.0[..to_copy].copy_from_slice(&file[..to_copy]);
while state.pc < MEMORY_SIZE as u16 { while state.pc < u16::MAX {
tick(&mut state); tick(&mut state);
} }
@ -29,7 +32,7 @@ fn main() {
print_state(&state); print_state(&state);
} }
fn tick(state: &mut EmulatorState) { fn tick<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let instruction = state.next_byte(); let instruction = state.next_byte();
match instruction { match instruction {
@ -69,8 +72,8 @@ fn tick(state: &mut EmulatorState) {
0x0a => transfer::ldax(Register::B, state), // LDAX B 0x0a => transfer::ldax(Register::B, state), // LDAX B
0x12 => transfer::stax(Register::D, state), // STAX D 0x12 => transfer::stax(Register::D, state), // STAX D
0x1a => transfer::ldax(Register::D, state), // LDAX D 0x1a => transfer::ldax(Register::D, state), // LDAX D
0x32 => state.memory[state.next_word() as usize] = state.a, // STA 0x32 => transfer::sta(state.next_word(), state), // STA
0x3a => state.a = state.memory[state.next_word() as usize], // LDA 0x3a => transfer::lda(state.next_word(), state), // LDA
// 16-bit transfer instructions // 16-bit transfer instructions
0x01 => set_register_pair(Register::B, state.next_word(), state), // LXI B 0x01 => set_register_pair(Register::B, state.next_word(), state), // LXI B
@ -217,8 +220,8 @@ fn tick(state: &mut EmulatorState) {
state.pc += 1; state.pc += 1;
} }
fn not_implemented(state: &EmulatorState) { fn not_implemented<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let instruction = state.memory[state.pc as usize]; let instruction = state.read_byte(state.pc);
panic!( panic!(
"Unimplemented instruction {:#02X} at {:#04X}", "Unimplemented instruction {:#02X} at {:#04X}",
instruction, state.pc instruction, state.pc

View file

@ -0,0 +1,25 @@
pub trait MemoryMapper {
fn read(&mut self, address: u16) -> u8;
fn write(&mut self, address: u16, value: u8);
}
//#[cfg(test)]
pub struct TestMapper(pub [u8; u16::MAX as usize + 1]);
//#[cfg(test)]
impl MemoryMapper for TestMapper {
fn read(&mut self, address: u16) -> u8 {
self.0[address as usize]
}
fn write(&mut self, address: u16, value: u8) {
self.0[address as usize] = value;
}
}
//#[cfg(test)]
impl Default for TestMapper {
fn default() -> Self {
Self([0; u16::MAX as usize + 1])
}
}

View file

@ -1,7 +1,7 @@
use crate::MEMORY_SIZE; use crate::mapper::MemoryMapper;
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct EmulatorState { pub struct EmulatorState<M: MemoryMapper> {
pub a: u8, pub a: u8,
pub b: u8, pub b: u8,
pub c: u8, pub c: u8,
@ -18,43 +18,13 @@ pub struct EmulatorState {
pub pc: u16, pub pc: u16,
/// Enable interrupts /// Enable interrupts
pub ei: bool, pub ei: bool,
/// Memory map
pub memory: [u8; MEMORY_SIZE], /// Memory mapper
pub mapper: M,
} }
impl EmulatorState { impl<M: MemoryMapper> EmulatorState<M> {
// Read the next byte from memory pointed at by PC pub fn new(mapper: M) -> Self {
pub fn next_byte(&mut self) -> u8 {
let value = self.memory[self.pc as usize];
(self.pc, ..) = self.pc.overflowing_add(1);
value
}
// Read the next 16-bit word from memory pointed at by PC, in little endian order
pub fn next_word(&mut self) -> u16 {
let value = self.read_word(self.pc);
(self.pc, ..) = self.pc.overflowing_add(2);
value
}
// Read a 16-bit word at a specific address
pub fn read_word(&mut self, address: u16) -> u16 {
u16::from_le_bytes([
self.memory[address as usize],
self.memory[address.overflowing_add(1).0 as usize],
])
}
// Write a 16-bit word at a specific address
pub fn write_word(&mut self, address: u16, value: u16) {
let [low, high] = u16::to_le_bytes(value);
self.memory[address as usize] = low;
self.memory[address.overflowing_add(1).0 as usize] = high;
}
}
impl Default for EmulatorState {
fn default() -> Self {
Self { Self {
a: 0, a: 0,
b: 0, b: 0,
@ -72,9 +42,60 @@ impl Default for EmulatorState {
}, },
pc: 0, pc: 0,
ei: false, ei: false,
memory: [0; MEMORY_SIZE], mapper,
} }
} }
/// Read a byte at a specific address
#[inline]
pub fn read_byte(&mut self, address: u16) -> u8 {
self.mapper.read(address)
}
/// Write a byte at a specific address
#[inline]
pub fn write_byte(&mut self, address: u16, value: u8) {
self.mapper.write(address, value);
}
/// Read the next byte from memory pointed at by PC
#[inline]
pub fn next_byte(&mut self) -> u8 {
let value = self.read_byte(self.pc);
(self.pc, ..) = self.pc.overflowing_add(1);
value
}
/// Read a 16-bit word at a specific address
#[inline]
pub fn read_word(&mut self, address: u16) -> u16 {
u16::from_le_bytes([
self.mapper.read(address),
self.mapper.read(address.overflowing_add(1).0),
])
}
/// Write a 16-bit word at a specific address
#[inline]
pub fn write_word(&mut self, address: u16, value: u16) {
let [low, high] = u16::to_le_bytes(value);
self.mapper.write(address, low);
self.mapper.write(address.overflowing_add(1).0, high);
}
/// Read the next 16-bit word from memory pointed at by PC, in little endian order
#[inline]
pub fn next_word(&mut self) -> u16 {
let value = self.read_word(self.pc);
(self.pc, ..) = self.pc.overflowing_add(2);
value
}
}
impl<M: MemoryMapper + Default> Default for EmulatorState<M> {
fn default() -> Self {
Self::new(M::default())
}
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@ -121,7 +142,7 @@ pub fn register_from_num(b: u8) -> Register {
} }
} }
pub fn get_register(register: Register, state: &EmulatorState) -> u8 { pub fn get_register<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) -> u8 {
match register { match register {
Register::B => state.b, Register::B => state.b,
Register::C => state.c, Register::C => state.c,
@ -130,12 +151,12 @@ pub fn get_register(register: Register, state: &EmulatorState) -> u8 {
Register::H => state.h, Register::H => state.h,
Register::L => state.l, Register::L => state.l,
Register::A => state.a, Register::A => state.a,
Register::M => state.memory[u16::from_le_bytes([state.l, state.h]) as usize], Register::M => state.mapper.read(u16::from_le_bytes([state.l, state.h])),
Register::SP => unreachable!(), Register::SP => unreachable!(),
} }
} }
pub fn set_register(register: Register, value: u8, state: &mut EmulatorState) { pub fn set_register<M: MemoryMapper>(register: Register, value: u8, state: &mut EmulatorState<M>) {
match register { match register {
Register::B => state.b = value, Register::B => state.b = value,
Register::C => state.c = value, Register::C => state.c = value,
@ -144,12 +165,14 @@ pub fn set_register(register: Register, value: u8, state: &mut EmulatorState) {
Register::H => state.h = value, Register::H => state.h = value,
Register::L => state.l = value, Register::L => state.l = value,
Register::A => state.a = value, Register::A => state.a = value,
Register::M => state.memory[u16::from_le_bytes([state.l, state.h]) as usize] = value, Register::M => state
.mapper
.write(u16::from_le_bytes([state.l, state.h]), value),
Register::SP => panic!("Cannot set 'SP' through set_register()"), Register::SP => panic!("Cannot set 'SP' through set_register()"),
}; };
} }
pub fn get_register_pair(register: Register, state: &mut EmulatorState) -> u16 { pub fn get_register_pair<M: MemoryMapper>(register: Register, state: &mut EmulatorState<M>) -> u16 {
match register { match register {
Register::B => u16::from_le_bytes([state.c, state.b]), Register::B => u16::from_le_bytes([state.c, state.b]),
Register::D => u16::from_le_bytes([state.e, state.d]), Register::D => u16::from_le_bytes([state.e, state.d]),
@ -170,7 +193,11 @@ pub fn get_register_pair(register: Register, state: &mut EmulatorState) -> u16 {
} }
} }
pub fn set_register_pair(register: Register, value: u16, state: &mut EmulatorState) { pub fn set_register_pair<M: MemoryMapper>(
register: Register,
value: u16,
state: &mut EmulatorState<M>,
) {
let arr = value.to_le_bytes(); let arr = value.to_le_bytes();
let high = arr[1]; let high = arr[1];
let low = arr[0]; let low = arr[0];
@ -205,7 +232,7 @@ pub fn set_register_pair(register: Register, value: u16, state: &mut EmulatorSta
} }
/// Print values of registers and flags to stdout /// Print values of registers and flags to stdout
pub fn print_state(state: &EmulatorState) { pub fn print_state<M: MemoryMapper>(state: &EmulatorState<M>) {
// State // State
println!("\nsp\tpc\tei"); println!("\nsp\tpc\tei");
println!("{:#06x}\t{:#06x}\t{}", state.sp, state.pc, state.ei); println!("{:#06x}\t{:#06x}\t{}", state.sp, state.pc, state.ei);
@ -228,27 +255,28 @@ pub fn print_state(state: &EmulatorState) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mapper::TestMapper;
#[test] #[test]
fn read_word() { fn read_word() {
let mut state = EmulatorState::default(); let mut state = EmulatorState::<TestMapper>::default();
state.memory[0x10] = 0x34; state.mapper.0[0x10] = 0x34;
state.memory[0x11] = 0x12; state.mapper.0[0x11] = 0x12;
assert_eq!(state.read_word(0x10), 0x1234); assert_eq!(state.read_word(0x10), 0x1234);
} }
#[test] #[test]
fn write_word() { fn write_word() {
let mut state = EmulatorState::default(); let mut state = EmulatorState::<TestMapper>::default();
state.write_word(0x10, 0x1234); state.write_word(0x10, 0x1234);
assert_eq!(state.memory[0x10..=0x11], [0x34, 0x12]); assert_eq!(state.mapper.0[0x10..=0x11], [0x34, 0x12]);
} }
#[test] #[test]
fn next_word_at_pc() { fn next_word_at_pc() {
let mut state = EmulatorState::default(); let mut state = EmulatorState::<TestMapper>::default();
state.memory[0x10] = 0x34; state.mapper.0[0x10] = 0x34;
state.memory[0x11] = 0x12; state.mapper.0[0x11] = 0x12;
state.pc = 0x10; state.pc = 0x10;
assert_eq!(state.next_word(), 0x1234); assert_eq!(state.next_word(), 0x1234);
} }