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::{get_register, EmulatorState, Register};
/// Sets the condition code flags according to `result`.
/// Does not set the carry flag and will always set the Z, S and P flags.
#[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.s = result & 0x80 > 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)
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 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 (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;
}
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);
}
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 (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 {
($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);
}
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;
state.cc.c = false;
set_cc(state, result);
@ -57,18 +58,18 @@ bitwise_op!(and, and_reg, &);
bitwise_op!(or, or_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);
}
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);
state.cc.c = carry;
set_cc(state, result);
}
/// 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 (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
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);
set_cc(state, result);
set_register(register, result, state);
}
/// 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);
set_cc(state, result);
set_register(register, result, state);
}
/// 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);
set_register_pair(register, result, state);
}
/// 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);
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);
state.a = result;
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.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 result = state.a << 1;
state.a = result | new_carry as u8;
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 result = state.a >> 1;
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::{get_register_pair, EmulatorState, Register};
/// 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;
}
/// 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 {
jump(address, state);
}
}
/// 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);
jump(address, state);
}
// 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 {
call(address, state);
}
}
// 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);
}
// 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 {
ret(state)
}
@ -41,12 +42,12 @@ pub fn ret_cond(cond: bool, state: &mut EmulatorState) {
/// "Restart" at (call) a specific interrupt vector
///
/// 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");
call((vector * 8) as u16, state);
}
/// 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);
}

View file

@ -1,44 +1,55 @@
use crate::mapper::MemoryMapper;
use crate::{
get_register, get_register_pair, set_register, set_register_pair, EmulatorState, 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);
set_register(dest, data, state);
}
/// 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);
}
// 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
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);
state.memory[address as usize] = state.a;
state.write_byte(address, state.a);
}
/// 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);
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
pub fn shld(address: u16, state: &mut EmulatorState) {
state.memory[address as usize] = state.l;
state.memory[address as usize + 1] = state.h;
pub fn shld<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.write_byte(address, state.l);
state.write_byte(address.overflowing_add(1).0, state.h)
}
/// Load a 16-bit word into H and L from the specified address
pub fn lhld(address: u16, state: &mut EmulatorState) {
state.l = state.memory[address as usize];
state.h = state.memory[address as usize + 1];
pub fn lhld<M: MemoryMapper>(address: u16, state: &mut EmulatorState<M>) {
state.l = state.read_byte(address);
state.h = state.read_byte(address.overflowing_add(1).0);
}
/// 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_stack = state.read_word(state.sp);
// 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
pub fn sphl(state: &mut EmulatorState) {
pub fn sphl<M: MemoryMapper>(state: &mut EmulatorState<M>) {
state.sp = get_register_pair(Register::H, state);
}
/// 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_de = get_register_pair(Register::D, state);
// Set HL to DE
@ -64,23 +75,23 @@ pub fn xchg(state: &mut EmulatorState) {
/* 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 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.write_word(state.sp, value);
}
/// 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);
}
/// 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);
(state.sp, ..) = state.sp.overflowing_add(2);
value
@ -88,6 +99,7 @@ pub fn pop(state: &mut EmulatorState) -> u16 {
#[cfg(test)]
mod tests {
use crate::mapper::TestMapper;
use crate::EmulatorState;
#[test]
@ -100,14 +112,14 @@ mod tests {
// memory address two less than the contents of the
// stack pointer.
// (3) The stack pointer is automatically decremented by two.
let mut state = EmulatorState {
let mut state: EmulatorState<TestMapper> = EmulatorState {
sp: 0x12,
..Default::default()
};
super::push(0x1234, &mut state);
assert_eq!(state.sp, 0x10);
assert_eq!(state.memory[0x10..=0x11], [0x34, 0x12]);
assert_eq!(state.mapper.0[0x10..=0x11], [0x34, 0x12]);
}
#[test]
@ -121,13 +133,13 @@ mod tests {
// memory address one greater than the address held in
// the stack pointer.
// (3) The stack pointer is automatically incremented by two.
let mut state = EmulatorState {
let mut state: EmulatorState<TestMapper> = EmulatorState {
sp: 0x10,
..Default::default()
};
state.memory[0x10] = 0x34;
state.memory[0x11] = 0x12;
state.mapper.0[0x10] = 0x34;
state.mapper.0[0x11] = 0x12;
assert_eq!(super::pop(&mut state), 0x1234);
}
}

View file

@ -1,15 +1,18 @@
use instructions::{arithmetic, branch, transfer};
use mapper::MemoryMapper;
use std::{env, fs};
use crate::structs::*;
mod instructions;
mod mapper;
mod structs;
pub const MEMORY_SIZE: usize = 8192;
fn main() {
let mut state = EmulatorState::default();
use crate::mapper::TestMapper;
let mut state = EmulatorState::<TestMapper>::default();
// Load the ROM into memory
let mut args = env::args();
@ -18,10 +21,10 @@ fn main() {
.expect("Provide a path to a ROM file to emulate as an argument");
let file = fs::read(filename).expect("where file");
let to_copy = MEMORY_SIZE.min(file.len());
state.memory[..to_copy].copy_from_slice(&file[..to_copy]);
let to_copy = state.mapper.0.len().min(file.len());
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);
}
@ -29,7 +32,7 @@ fn main() {
print_state(&state);
}
fn tick(state: &mut EmulatorState) {
fn tick<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let instruction = state.next_byte();
match instruction {
@ -69,8 +72,8 @@ fn tick(state: &mut EmulatorState) {
0x0a => transfer::ldax(Register::B, state), // LDAX B
0x12 => transfer::stax(Register::D, state), // STAX D
0x1a => transfer::ldax(Register::D, state), // LDAX D
0x32 => state.memory[state.next_word() as usize] = state.a, // STA
0x3a => state.a = state.memory[state.next_word() as usize], // LDA
0x32 => transfer::sta(state.next_word(), state), // STA
0x3a => transfer::lda(state.next_word(), state), // LDA
// 16-bit transfer instructions
0x01 => set_register_pair(Register::B, state.next_word(), state), // LXI B
@ -217,8 +220,8 @@ fn tick(state: &mut EmulatorState) {
state.pc += 1;
}
fn not_implemented(state: &EmulatorState) {
let instruction = state.memory[state.pc as usize];
fn not_implemented<M: MemoryMapper>(state: &mut EmulatorState<M>) {
let instruction = state.read_byte(state.pc);
panic!(
"Unimplemented instruction {:#02X} at {:#04X}",
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)]
pub struct EmulatorState {
pub struct EmulatorState<M: MemoryMapper> {
pub a: u8,
pub b: u8,
pub c: u8,
@ -18,43 +18,13 @@ pub struct EmulatorState {
pub pc: u16,
/// Enable interrupts
pub ei: bool,
/// Memory map
pub memory: [u8; MEMORY_SIZE],
/// Memory mapper
pub mapper: M,
}
impl EmulatorState {
// Read the next byte from memory pointed at by PC
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 {
impl<M: MemoryMapper> EmulatorState<M> {
pub fn new(mapper: M) -> Self {
Self {
a: 0,
b: 0,
@ -72,9 +42,60 @@ impl Default for EmulatorState {
},
pc: 0,
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)]
@ -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 {
Register::B => state.b,
Register::C => state.c,
@ -130,12 +151,12 @@ pub fn get_register(register: Register, state: &EmulatorState) -> u8 {
Register::H => state.h,
Register::L => state.l,
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!(),
}
}
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 {
Register::B => state.b = 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::L => state.l = 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()"),
};
}
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 {
Register::B => u16::from_le_bytes([state.c, state.b]),
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 high = arr[1];
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
pub fn print_state(state: &EmulatorState) {
pub fn print_state<M: MemoryMapper>(state: &EmulatorState<M>) {
// State
println!("\nsp\tpc\tei");
println!("{:#06x}\t{:#06x}\t{}", state.sp, state.pc, state.ei);
@ -228,27 +255,28 @@ pub fn print_state(state: &EmulatorState) {
#[cfg(test)]
mod tests {
use super::*;
use crate::mapper::TestMapper;
#[test]
fn read_word() {
let mut state = EmulatorState::default();
state.memory[0x10] = 0x34;
state.memory[0x11] = 0x12;
let mut state = EmulatorState::<TestMapper>::default();
state.mapper.0[0x10] = 0x34;
state.mapper.0[0x11] = 0x12;
assert_eq!(state.read_word(0x10), 0x1234);
}
#[test]
fn write_word() {
let mut state = EmulatorState::default();
let mut state = EmulatorState::<TestMapper>::default();
state.write_word(0x10, 0x1234);
assert_eq!(state.memory[0x10..=0x11], [0x34, 0x12]);
assert_eq!(state.mapper.0[0x10..=0x11], [0x34, 0x12]);
}
#[test]
fn next_word_at_pc() {
let mut state = EmulatorState::default();
state.memory[0x10] = 0x34;
state.memory[0x11] = 0x12;
let mut state = EmulatorState::<TestMapper>::default();
state.mapper.0[0x10] = 0x34;
state.mapper.0[0x11] = 0x12;
state.pc = 0x10;
assert_eq!(state.next_word(), 0x1234);
}