added error handling to emulator

This commit is contained in:
2025-06-23 21:28:38 +01:00
committed by J. Hinchliffe
parent 00a28e7711
commit 2c44f48232
5 changed files with 276 additions and 110 deletions
+37 -10
View File
@@ -30,7 +30,11 @@ pub fn run_emulator(
let mut history = Vec::<(u32, Instruction)>::new(); let mut history = Vec::<(u32, Instruction)>::new();
let size = 256; let size = 256;
let memory_view = processor.memory.read_range(addr, size); let memory_view = processor
.memory
.read_range(addr, size)
.expect("Failed to read initial memory state!");
let initial_state = state(&mut processor, running, 0, memory_view, &mut history); let initial_state = state(&mut processor, running, 0, memory_view, &mut history);
let _ = state_tx.send(initial_state); let _ = state_tx.send(initial_state);
@@ -114,14 +118,25 @@ pub fn run_emulator(
addr = new; addr = new;
} }
Command::Write(offset, data) => { Command::Write(offset, data) => {
processor.memory.write_range(offset, data); processor
.memory
.write_range(offset, data)
.unwrap_or_else(|_| {
eprintln!("Failed to write memory range!");
processor.begin_interrupt(Interrupt::HardFault);
});
} }
Command::Interrupt(_interrupt) => { Command::Interrupt(_interrupt) => {
todo!("implement interrupts") todo!("implement interrupts")
} }
} }
let memory_view = processor.memory.read_range(addr, size); let memory_view =
processor.memory.read_range(addr, size).unwrap_or_else(|_| {
eprintln!("Failed to read memory range!");
processor.begin_interrupt(Interrupt::HardFault);
Vec::new()
});
let state = state( let state = state(
&mut processor, &mut processor,
running, running,
@@ -130,8 +145,6 @@ pub fn run_emulator(
&mut history, &mut history,
); );
println!("state");
let _ = state_tx.send(state); let _ = state_tx.send(state);
} }
@@ -166,7 +179,15 @@ pub fn run_emulator(
} }
if update { if update {
let memory_view = processor.memory.read_range(addr, size); let memory_view =
processor
.memory
.read_range(addr, size)
.unwrap_or_else(|why| {
eprintln!("Failed to read memory range! Reason: {why}");
processor.begin_interrupt(Interrupt::HardFault);
Vec::new()
});
let state = state( let state = state(
&mut processor, &mut processor,
running, running,
@@ -185,7 +206,7 @@ pub fn run_emulator(
} }
fn state( fn state(
cpu_lock: &mut Processor, processor: &mut Processor,
running: Running, running: Running,
instruction_count: usize, instruction_count: usize,
memory_view: Vec<u8>, memory_view: Vec<u8>,
@@ -196,12 +217,18 @@ fn state(
State { State {
// TODO: Replace with actual register access from your CPU. // TODO: Replace with actual register access from your CPU.
reg_file: cpu_lock.registers, reg_file: processor.registers,
running, running,
instructions: instruction_count, instructions: instruction_count,
stack_view: cpu_lock.get_stack(32), stack_view: processor.get_stack(32).unwrap_or_else(|_| {
processor.begin_interrupt(Interrupt::HardFault);
Vec::new()
}),
memory_view, memory_view,
display_view: cpu_lock.display(), display_view: processor.display().unwrap_or_else(|_| {
processor.begin_interrupt(Interrupt::HardFault);
Vec::new()
}),
error: None, error: None,
persistent: PersistentState { history: hsclone }, persistent: PersistentState { history: hsclone },
} }
+71 -23
View File
@@ -1,13 +1,43 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::emulator::system::model::ProcessorError;
pub trait MemoryUnit: Send + Sync { pub trait MemoryUnit: Send + Sync {
fn reset(&mut self); fn reset(&mut self);
fn read_byte(&mut self, addr: u32) -> u8; fn read_byte(&mut self, addr: u32) -> Result<u8, ProcessorError>;
fn write_byte(&mut self, addr: u32, value: u8); fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), ProcessorError>;
fn read_word(&mut self, addr: u32) -> u32; fn read_word(&mut self, addr: u32) -> Result<u32, ProcessorError>;
fn write_word(&mut self, addr: u32, value: u32); fn write_word(&mut self, addr: u32, value: u32) -> Result<(), ProcessorError>;
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8>;
fn write_range(&mut self, addr: u32, value: Vec<u8>); fn read_range(&mut self, addr: u32, size: u32) -> Result<Vec<u8>, ProcessorError> {
let mut data = Vec::with_capacity(size as usize);
for i in 0..size {
data.push(self.read_byte(addr + i)?);
}
Ok(data)
}
fn write_range(&mut self, addr: u32, value: Vec<u8>) -> Result<(), ProcessorError> {
for (i, byte) in value.into_iter().enumerate() {
self.write_byte(addr + i as u32, byte)?;
}
Ok(())
}
fn read_block(&mut self, addr: u32) -> Result<[u8; 256], ProcessorError> {
let mut data = [0; 256];
for i in 0..256 {
data[i] = self.read_byte(addr + i as u32)?;
}
Ok(data)
}
fn write_block(&mut self, addr: u32, data: [u8; 256]) -> Result<(), ProcessorError> {
for i in 0..256 {
self.write_byte(addr + i as u32, data[i])?;
}
Ok(())
}
} }
pub struct MainStore { pub struct MainStore {
@@ -64,59 +94,77 @@ impl MemoryUnit for MainStore {
self.data.clear(); self.data.clear();
} }
fn read_byte(&mut self, addr: u32) -> u8 { fn read_byte(&mut self, addr: u32) -> Result<u8, ProcessorError> {
let (block_addr, offset) = Self::segment_addr(addr); let (block_addr, offset) = Self::segment_addr(addr);
let block = self.block(block_addr); let block = self.block(block_addr);
block.data[offset as usize] Ok(block.data[offset as usize])
} }
fn read_word(&mut self, addr: u32) -> u32 { fn read_word(&mut self, addr: u32) -> Result<u32, ProcessorError> {
if addr % 4 != 0 {
return Err(ProcessorError::BadMemoryAccess(addr));
}
let (block_addr, offset) = Self::segment_addr(addr); let (block_addr, offset) = Self::segment_addr(addr);
println!("reading word from {block_addr:x?} + {offset}");
let block = self.mut_block(block_addr); let block = self.mut_block(block_addr);
let mut bytes = [0; 4]; let mut bytes = [0; 4];
bytes[0] = block.data[offset as usize]; bytes[0] = block.data[offset as usize];
bytes[1] = block.data[(offset + 1) as usize]; bytes[1] = block.data[(offset + 1) as usize];
bytes[2] = block.data[(offset + 2) as usize]; bytes[2] = block.data[(offset + 2) as usize];
bytes[3] = block.data[(offset + 3) as usize]; bytes[3] = block.data[(offset + 3) as usize];
u32::from_be_bytes(bytes) Ok(u32::from_be_bytes(bytes))
} }
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> { fn read_range(&mut self, addr: u32, size: u32) -> Result<Vec<u8>, ProcessorError> {
let mut data = Vec::with_capacity(size as usize); let mut data = Vec::with_capacity(size as usize);
for i in 0..size { for i in 0..size {
data.push(self.read_byte(addr + i)); data.push(self.read_byte(addr + i)?);
} }
// println!("reading {data:?} from {addr:x?}"); Ok(data)
data
} }
fn write_byte(&mut self, addr: u32, value: u8) { fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), ProcessorError> {
let (block_addr, offset) = Self::segment_addr(addr); let (block_addr, offset) = Self::segment_addr(addr);
let block = self.mut_block(block_addr); let block = self.mut_block(block_addr);
block.data[offset as usize] = value; block.data[offset as usize] = value;
Ok(())
} }
fn write_word(&mut self, addr: u32, value: u32) { fn write_word(&mut self, addr: u32, value: u32) -> Result<(), ProcessorError> {
if addr % 4 != 0 {
return Err(ProcessorError::BadMemoryAccess(addr));
}
let (block_addr, offset) = Self::segment_addr(addr); let (block_addr, offset) = Self::segment_addr(addr);
let block = self.mut_block(block_addr); let block = self.mut_block(block_addr);
block.data[offset as usize] = (value >> 24) as u8; block.data[offset as usize] = (value >> 24) as u8;
block.data[(offset + 1) as usize] = (value >> 16) as u8; block.data[(offset + 1) as usize] = (value >> 16) as u8;
block.data[(offset + 2) as usize] = (value >> 8) as u8; block.data[(offset + 2) as usize] = (value >> 8) as u8;
block.data[(offset + 3) as usize] = value as u8; block.data[(offset + 3) as usize] = value as u8;
Ok(())
} }
fn write_range(&mut self, addr: u32, value: Vec<u8>) { fn write_range(&mut self, addr: u32, value: Vec<u8>) -> Result<(), ProcessorError> {
// println!("writing {value:?} to {addr:x?}");
for (i, byte) in value.into_iter().enumerate() { for (i, byte) in value.into_iter().enumerate() {
let (block_addr, offset) = Self::segment_addr(addr + i as u32); let (block_addr, offset) = Self::segment_addr(addr + i as u32);
let block = self.mut_block(block_addr); let block = self.mut_block(block_addr);
block.data[offset as usize] = byte; block.data[offset as usize] = byte;
} }
Ok(())
}
fn read_block(&mut self, addr: u32) -> Result<[u8; 256], ProcessorError> {
let (block_addr, _) = Self::segment_addr(addr);
let block = self.block(block_addr);
Ok(block.data)
}
fn write_block(&mut self, addr: u32, data: [u8; 256]) -> Result<(), ProcessorError> {
let (block_addr, _) = Self::segment_addr(addr);
let block = self.mut_block(block_addr);
block.data = data;
Ok(())
} }
} }
+25
View File
@@ -27,6 +27,31 @@ pub enum Command {
Write(Address, Vec<u8>), Write(Address, Vec<u8>),
} }
#[derive(Debug)]
pub enum ProcessorError {
InvalidInstruction(u32),
InvalidRegister(u8),
BadMemoryAccess(u32),
}
impl std::error::Error for ProcessorError {}
impl std::fmt::Display for ProcessorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidInstruction(instruction) => {
write!(f, "Invalid instruction: {instruction}")
}
Self::InvalidRegister(register) => {
write!(f, "Invalid register: {register}")
}
Self::BadMemoryAccess(address) => {
write!(f, "Bad memory access: {address}")
}
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegFile { pub struct RegFile {
// General Purpose Registers // General Purpose Registers
+33 -39
View File
@@ -5,12 +5,10 @@ use std::{
use crate::emulator::system::{ use crate::emulator::system::{
memory::MemoryUnit, memory::MemoryUnit,
model::{IODevice, RegFile}, model::{IODevice, ProcessorError, RegFile},
}; };
use common::instructions::{ use common::instructions::{Instruction, Interrupt, Register};
Instruction, Interrupt, Register, errors::InstructionDecodeError,
};
pub struct Processor { pub struct Processor {
pub memory: Box<dyn MemoryUnit>, pub memory: Box<dyn MemoryUnit>,
@@ -25,7 +23,6 @@ fn log(message: &str) {
println!("\x1b[32mINFO:\x1b[0m {message}"); println!("\x1b[32mINFO:\x1b[0m {message}");
} }
#[allow(clippy::needless_pass_by_ref_mut)]
impl Processor { impl Processor {
#[must_use] #[must_use]
pub fn new(memory: Box<dyn MemoryUnit>, io_devices: Vec<Arc<dyn IODevice>>) -> Self { pub fn new(memory: Box<dyn MemoryUnit>, io_devices: Vec<Arc<dyn IODevice>>) -> Self {
@@ -48,7 +45,7 @@ impl Processor {
self.memory.reset(); self.memory.reset();
} }
pub fn cycle(&mut self) -> Result<(u32, Instruction), InstructionDecodeError> { pub fn cycle(&mut self) -> Result<(u32, Instruction), ProcessorError> {
self.halted = false; self.halted = false;
// Get value from PCX. // Get value from PCX.
@@ -58,17 +55,18 @@ impl Processor {
// Set MAR to the previous value of PCX. // Set MAR to the previous value of PCX.
*self.reg(Register::Mar) = addr; *self.reg(Register::Mar) = addr;
let val = self.memory.read_word(addr); let val = self.memory.read_word(addr)?;
// Set CIR to the value of RAM[MAR]. // Set CIR to the value of RAM[MAR].
*self.reg(Register::Mar) = val; *self.reg(Register::Mar) = val;
// Decode and execute the instruction. // Decode and execute the instruction.
let instruction = Instruction::decode(val)?; let instruction = Instruction::decode(val)
.map_err(|_| ProcessorError::InvalidInstruction(val))?;
log(&instruction.to_string()); log(&instruction.to_string());
instruction.execute(self); instruction.execute(self)?;
Ok((addr, instruction)) Ok((addr, instruction))
} }
@@ -89,7 +87,7 @@ impl Processor {
} }
} }
pub fn display(&mut self) -> Vec<u8> { pub fn display(&mut self) -> Result<Vec<u8>, ProcessorError> {
self.memory.read_range(0x20000, 2000) self.memory.read_range(0x20000, 2000)
} }
@@ -99,21 +97,7 @@ impl Processor {
self.set_flag(Flag::LessThan, a < b); self.set_flag(Flag::LessThan, a < b);
} }
// stack operations
pub fn push(&mut self, value: u32) {
let stack_ptr = self.get(Register::Spr);
*self.reg(Register::Spr) += 4;
self.memory.write_word(stack_ptr, value);
}
pub fn pop(&mut self) -> u32 {
*self.reg(Register::Spr) -= 4;
self.memory.read_word(self.get(Register::Spr))
}
// functions to set new state // functions to set new state
fn set_flag(&mut self, flag: Flag, value: bool) { fn set_flag(&mut self, flag: Flag, value: bool) {
if value { if value {
*self.reg(Register::Sts) |= flag as u32; *self.reg(Register::Sts) |= flag as u32;
@@ -135,16 +119,18 @@ impl Processor {
*self.reg(Register::Pcx) = self.get(reg) + u32::from(offset); *self.reg(Register::Pcx) = self.get(reg) + u32::from(offset);
} }
fn begin_interrupt(&mut self, _int: Interrupt) { pub fn begin_interrupt(&mut self, _int: Interrupt) {
// first we get the address of the interrupt descriptor table. // first we get the address of the interrupt descriptor table.
todo!(); todo!();
} }
// TODO: remove this once implemented
#[allow(clippy::needless_pass_by_ref_mut)]
fn end_interrupt(&mut self) { fn end_interrupt(&mut self) {
todo!(); todo!();
} }
pub fn get_stack(&mut self, n: u32) -> Vec<u8> { pub fn get_stack(&mut self, n: u32) -> Result<Vec<u8>, ProcessorError> {
let addr = self.get(Register::Spr); let addr = self.get(Register::Spr);
let size = n * 4; let size = n * 4;
// returns the stack // returns the stack
@@ -170,12 +156,12 @@ enum Flag {
} }
trait Executable { trait Executable {
fn execute(self, cpu: &mut Processor); fn execute(self, cpu: &mut Processor) -> Result<(), ProcessorError>;
} }
impl Executable for Instruction { impl Executable for Instruction {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn execute(self, cpu: &mut Processor) { fn execute(self, cpu: &mut Processor) -> Result<(), ProcessorError> {
match self { match self {
// No operation - a blank line. // No operation - a blank line.
// Copies from SrcReg to a.drReg. // Copies from SrcReg to a.drReg.
@@ -193,7 +179,8 @@ impl Executable for Instruction {
// effective address must be byte-aligned. // effective address must be byte-aligned.
Self::LoadByte(a) => { Self::LoadByte(a) => {
*cpu.reg(a.r2) = u32::from( *cpu.reg(a.r2) = u32::from(
cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)), cpu.memory
.read_byte(cpu.get(a.r1) + u32::from(a.immediate))?,
); );
} }
@@ -201,7 +188,8 @@ impl Executable for Instruction {
// a.drReg. The effective address must be byte-aligned. // a.drReg. The effective address must be byte-aligned.
Self::LoadByteSigned(a) => { Self::LoadByteSigned(a) => {
*cpu.reg(a.r2) = sign_extend(u32::from( *cpu.reg(a.r2) = sign_extend(u32::from(
cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)), cpu.memory
.read_byte(cpu.get(a.r1) + u32::from(a.immediate))?,
)); ));
} }
@@ -210,23 +198,28 @@ impl Executable for Instruction {
Self::LoadHalfword(a) => { Self::LoadHalfword(a) => {
// we read an entire word, then right shift so we only get the first half // we read an entire word, then right shift so we only get the first half
// of the word // of the word
*cpu.reg(a.r2) = *cpu.reg(a.r2) = cpu
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16; .memory
.read_word(cpu.get(a.r1) + u32::from(a.immediate))?
>> 16;
} }
// Loads a sign-extended half-word from memory address (base + offset) into // Loads a sign-extended half-word from memory address (base + offset) into
// a.drReg. The effective address must be 2-byte-aligned. // a.drReg. The effective address must be 2-byte-aligned.
Self::LoadHalfwordSigned(a) => { Self::LoadHalfwordSigned(a) => {
*cpu.reg(a.r2) = sign_extend( *cpu.reg(a.r2) = sign_extend(
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16, cpu.memory
.read_word(cpu.get(a.r1) + u32::from(a.immediate))?
>> 16,
); );
} }
// Loads a word from memory address (base + offset) into a.drReg. The // Loads a word from memory address (base + offset) into a.drReg. The
// effective address must be 4-byte-aligned. // effective address must be 4-byte-aligned.
Self::LoadWord(a) => { Self::LoadWord(a) => {
*cpu.reg(a.r2) = *cpu.reg(a.r2) = cpu
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)); .memory
.read_word(cpu.get(a.r1) + u32::from(a.immediate))?;
} }
// Stores a byte from SrcReg in memory address (base + offset) The effective // Stores a byte from SrcReg in memory address (base + offset) The effective
@@ -235,7 +228,7 @@ impl Executable for Instruction {
cpu.memory.write_byte( cpu.memory.write_byte(
cpu.get(a.r2) + u32::from(a.immediate), cpu.get(a.r2) + u32::from(a.immediate),
cpu.get(a.r1) as u8, cpu.get(a.r1) as u8,
); )?;
} }
// Stores a half-word from SrcReg in memory address (base + offset) The // Stores a half-word from SrcReg in memory address (base + offset) The
@@ -244,16 +237,16 @@ impl Executable for Instruction {
// split the value into bytes and then write two bytes // split the value into bytes and then write two bytes
let bytes = (cpu.get(a.r1) as u16).to_le_bytes(); let bytes = (cpu.get(a.r1) as u16).to_le_bytes();
cpu.memory cpu.memory
.write_byte(cpu.get(a.r2) + u32::from(a.immediate), bytes[0]); .write_byte(cpu.get(a.r2) + u32::from(a.immediate), bytes[0])?;
cpu.memory cpu.memory
.write_byte(cpu.get(a.r2) + u32::from(a.immediate) + 1, bytes[1]); .write_byte(cpu.get(a.r2) + u32::from(a.immediate) + 1, bytes[1])?;
} }
// Stores a word from SrcReg in memory address (base + offset) The effective // Stores a word from SrcReg in memory address (base + offset) The effective
// address must be 4-byte-aligned. // address must be 4-byte-aligned.
Self::StoreWord(a) => { Self::StoreWord(a) => {
cpu.memory cpu.memory
.write_word(cpu.get(a.r2) + u32::from(a.immediate), cpu.get(a.r1)); .write_word(cpu.get(a.r2) + u32::from(a.immediate), cpu.get(a.r1))?;
} }
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the // Loads a 16-bit literal value into reg, setting the bottom 16 bits of the
@@ -411,6 +404,7 @@ impl Executable for Instruction {
todo!() todo!()
} }
} }
Ok(())
} }
} }
+110 -38
View File
@@ -13,7 +13,9 @@ fn test_nop_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
let initial_state = cpu.registers; let initial_state = cpu.registers;
Instruction::Nop.execute(&mut cpu); Instruction::Nop.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!( assert_eq!(
cpu.registers.get(Register::Rg0), cpu.registers.get(Register::Rg0),
@@ -37,7 +39,9 @@ fn test_mov_instruction() {
None, None,
)); ));
mov_instr.execute(&mut cpu); mov_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0x1234_5678); assert_eq!(cpu.get(Register::Rg2), 0x1234_5678);
} }
@@ -53,7 +57,9 @@ fn test_mov_signed_instruction() {
None, None,
)); ));
mov_signed_instr.execute(&mut cpu); mov_signed_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF); assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF);
} }
@@ -61,7 +67,9 @@ fn test_mov_signed_instruction() {
fn test_load_byte_instruction() { fn test_load_byte_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
let addr = 0x100; let addr = 0x100;
cpu.memory.write_byte(addr, 0xAB); cpu.memory
.write_byte(addr, 0xAB)
.expect("Failed to write byte to memory");
*cpu.reg(Register::Rg1) = addr - 4; *cpu.reg(Register::Rg1) = addr - 4;
let load_byte_instr = Instruction::LoadByte(ITypeArgs::new( let load_byte_instr = Instruction::LoadByte(ITypeArgs::new(
@@ -70,7 +78,9 @@ fn test_load_byte_instruction() {
Some(Register::Rg2), Some(Register::Rg2),
)); ));
load_byte_instr.execute(&mut cpu); load_byte_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0x0000_00AB); assert_eq!(cpu.get(Register::Rg2), 0x0000_00AB);
} }
@@ -78,7 +88,9 @@ fn test_load_byte_instruction() {
fn test_load_byte_signed_instruction() { fn test_load_byte_signed_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
let addr = 0x100; let addr = 0x100;
cpu.memory.write_byte(addr, 0xFF); cpu.memory
.write_byte(addr, 0xFF)
.expect("Failed to write byte to memory");
*cpu.reg(Register::Rg1) = addr; *cpu.reg(Register::Rg1) = addr;
let load_byte_signed_instr = Instruction::LoadByteSigned(ITypeArgs::new( let load_byte_signed_instr = Instruction::LoadByteSigned(ITypeArgs::new(
@@ -87,7 +99,9 @@ fn test_load_byte_signed_instruction() {
Some(Register::Rg2), Some(Register::Rg2),
)); ));
load_byte_signed_instr.execute(&mut cpu); load_byte_signed_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF); assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF);
} }
@@ -95,7 +109,9 @@ fn test_load_byte_signed_instruction() {
fn test_load_halfword_instruction() { fn test_load_halfword_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
let addr = 0x100; let addr = 0x100;
cpu.memory.write_word(addr, 0x1234_5678); cpu.memory
.write_word(addr, 0x1234_5678)
.expect("Failed to write word to memory");
*cpu.reg(Register::Rg1) = addr; *cpu.reg(Register::Rg1) = addr;
let load_halfword_instr = Instruction::LoadHalfword(ITypeArgs::new( let load_halfword_instr = Instruction::LoadHalfword(ITypeArgs::new(
@@ -104,7 +120,9 @@ fn test_load_halfword_instruction() {
Some(Register::Rg2), Some(Register::Rg2),
)); ));
load_halfword_instr.execute(&mut cpu); load_halfword_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0x0000_1234); assert_eq!(cpu.get(Register::Rg2), 0x0000_1234);
} }
@@ -112,7 +130,9 @@ fn test_load_halfword_instruction() {
fn test_load_word_instruction() { fn test_load_word_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
let addr = 0x100; let addr = 0x100;
cpu.memory.write_word(addr, 0x1234_5678); cpu.memory
.write_word(addr, 0x1234_5678)
.expect("Failed to write word to memory");
*cpu.reg(Register::Rg1) = addr; *cpu.reg(Register::Rg1) = addr;
let load_word_instr = Instruction::LoadWord(ITypeArgs::new( let load_word_instr = Instruction::LoadWord(ITypeArgs::new(
@@ -121,7 +141,9 @@ fn test_load_word_instruction() {
Some(Register::Rg2), Some(Register::Rg2),
)); ));
load_word_instr.execute(&mut cpu); load_word_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0x1234_5678); assert_eq!(cpu.get(Register::Rg2), 0x1234_5678);
} }
@@ -138,8 +160,10 @@ fn test_store_byte_instruction() {
Some(Register::Rg1), Some(Register::Rg1),
)); ));
store_byte_instr.execute(&mut cpu); store_byte_instr.execute(&mut cpu).expect(
assert_eq!(cpu.memory.read_byte(addr), 0xAB); "Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.memory.read_byte(addr).expect("Emulator was slain by losing the game while attempting to execute instruction"), 0xAB);
} }
#[test] #[test]
@@ -155,8 +179,10 @@ fn test_store_word_instruction() {
Some(Register::Rg1), Some(Register::Rg1),
)); ));
store_word_instr.execute(&mut cpu); store_word_instr.execute(&mut cpu).expect(
assert_eq!(cpu.memory.read_word(addr), 0x1234_5678); "Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.memory.read_word(addr).expect("Emulator was slain by losing the game while attempting to execute instruction"), 0x1234_5678);
} }
#[test] #[test]
@@ -172,7 +198,9 @@ fn test_add_instruction() {
None, None,
)); ));
add_instr.execute(&mut cpu); add_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), 40); assert_eq!(cpu.get(Register::Rg3), 40);
} }
@@ -189,7 +217,9 @@ fn test_sub_instruction() {
None, None,
)); ));
sub_instr.execute(&mut cpu); sub_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), 30); assert_eq!(cpu.get(Register::Rg3), 30);
} }
@@ -206,7 +236,9 @@ fn test_and_instruction() {
None, None,
)); ));
and_instr.execute(&mut cpu); and_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), 0b1000); assert_eq!(cpu.get(Register::Rg3), 0b1000);
} }
@@ -223,7 +255,9 @@ fn test_or_instruction() {
None, None,
)); ));
or_instr.execute(&mut cpu); or_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), 0b1110); assert_eq!(cpu.get(Register::Rg3), 0b1110);
} }
@@ -240,7 +274,9 @@ fn test_xor_instruction() {
None, None,
)); ));
xor_instr.execute(&mut cpu); xor_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), 0b0110); assert_eq!(cpu.get(Register::Rg3), 0b0110);
} }
@@ -256,7 +292,9 @@ fn test_not_instruction() {
None, None,
)); ));
not_instr.execute(&mut cpu); not_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg2), 0xF0F0_F0F0); assert_eq!(cpu.get(Register::Rg2), 0xF0F0_F0F0);
} }
@@ -273,7 +311,9 @@ fn test_compare_equal() {
None, None,
)); ));
cmp_instr.execute(&mut cpu); cmp_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert!(cpu.get_flag(Flag::Equal)); assert!(cpu.get_flag(Flag::Equal));
assert!(!cpu.get_flag(Flag::GreaterThan)); assert!(!cpu.get_flag(Flag::GreaterThan));
@@ -293,7 +333,9 @@ fn test_compare_greater_than() {
None, None,
)); ));
cmp_instr.execute(&mut cpu); cmp_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert!(!cpu.get_flag(Flag::Equal)); assert!(!cpu.get_flag(Flag::Equal));
assert!(cpu.get_flag(Flag::GreaterThan)); assert!(cpu.get_flag(Flag::GreaterThan));
@@ -313,7 +355,9 @@ fn test_compare_less_than() {
None, None,
)); ));
cmp_instr.execute(&mut cpu); cmp_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert!(!cpu.get_flag(Flag::Equal)); assert!(!cpu.get_flag(Flag::Equal));
assert!(!cpu.get_flag(Flag::GreaterThan)); assert!(!cpu.get_flag(Flag::GreaterThan));
@@ -328,7 +372,9 @@ fn test_increment_instruction() {
let inc_instr = let inc_instr =
Instruction::Increment(RTypeArgs::new(Some(Register::Rg1), None, None, None)); Instruction::Increment(RTypeArgs::new(Some(Register::Rg1), None, None, None));
inc_instr.execute(&mut cpu); inc_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 43); assert_eq!(cpu.get(Register::Rg1), 43);
} }
@@ -340,7 +386,9 @@ fn test_decrement_instruction() {
let dec_instr = let dec_instr =
Instruction::Decrement(RTypeArgs::new(Some(Register::Rg1), None, None, None)); Instruction::Decrement(RTypeArgs::new(Some(Register::Rg1), None, None, None));
dec_instr.execute(&mut cpu); dec_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 41); assert_eq!(cpu.get(Register::Rg1), 41);
} }
@@ -356,7 +404,9 @@ fn test_shift_left_with_shamt() {
Some(2), Some(2),
)); ));
shl_instr.execute(&mut cpu); shl_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 0b10_1000); assert_eq!(cpu.get(Register::Rg1), 0b10_1000);
} }
@@ -372,7 +422,9 @@ fn test_shift_right_with_shamt() {
Some(2), Some(2),
)); ));
shr_instr.execute(&mut cpu); shr_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 0b1010); assert_eq!(cpu.get(Register::Rg1), 0b1010);
} }
@@ -389,7 +441,9 @@ fn test_shift_left_with_register() {
None, None,
)); ));
shl_instr.execute(&mut cpu); shl_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 0b101_0000); assert_eq!(cpu.get(Register::Rg1), 0b101_0000);
} }
@@ -403,7 +457,9 @@ fn test_load_lower_immediate() {
None, None,
)); ));
lli_instr.execute(&mut cpu); lli_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 0x0000_1234); assert_eq!(cpu.get(Register::Rg1), 0x0000_1234);
} }
@@ -418,7 +474,9 @@ fn test_load_upper_immediate() {
None, None,
)); ));
lui_instr.execute(&mut cpu); lui_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg1), 0x1234_5678); assert_eq!(cpu.get(Register::Rg1), 0x1234_5678);
} }
@@ -430,7 +488,9 @@ fn test_jump_unconditional() {
let jump_instr = Instruction::Jump(ITypeArgs::new(0x100, Some(Register::Rg1), None)); let jump_instr = Instruction::Jump(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_instr.execute(&mut cpu); jump_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Pcx), 0x1100); assert_eq!(cpu.get(Register::Pcx), 0x1100);
assert_ne!(cpu.get(Register::Pcx), initial_pc); assert_ne!(cpu.get(Register::Pcx), initial_pc);
} }
@@ -444,7 +504,9 @@ fn test_jump_equal_when_flag_set() {
let jump_eq_instr = let jump_eq_instr =
Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None)); Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_eq_instr.execute(&mut cpu); jump_eq_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Pcx), 0x1100); assert_eq!(cpu.get(Register::Pcx), 0x1100);
} }
@@ -458,7 +520,9 @@ fn test_jump_equal_when_flag_not_set() {
let jump_eq_instr = let jump_eq_instr =
Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None)); Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_eq_instr.execute(&mut cpu); jump_eq_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Pcx), initial_pc); assert_eq!(cpu.get(Register::Pcx), initial_pc);
} }
@@ -467,7 +531,9 @@ fn test_halt_instruction() {
let mut cpu = create_test_processor(); let mut cpu = create_test_processor();
assert!(!cpu.halted); assert!(!cpu.halted);
Instruction::Halt.execute(&mut cpu); Instruction::Halt.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert!(cpu.halted); assert!(cpu.halted);
} }
@@ -484,7 +550,9 @@ fn test_nand_instruction() {
None, None,
)); ));
nand_instr.execute(&mut cpu); nand_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), !0b1000); assert_eq!(cpu.get(Register::Rg3), !0b1000);
} }
@@ -501,7 +569,9 @@ fn test_nor_instruction() {
None, None,
)); ));
nor_instr.execute(&mut cpu); nor_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), !0b1110); assert_eq!(cpu.get(Register::Rg3), !0b1110);
} }
@@ -518,6 +588,8 @@ fn test_xnor_instruction() {
None, None,
)); ));
xnor_instr.execute(&mut cpu); xnor_instr.execute(&mut cpu).expect(
"Emulator was slain by losing the game while attempting to execute instruction",
);
assert_eq!(cpu.get(Register::Rg3), !0b0110); assert_eq!(cpu.get(Register::Rg3), !0b0110);
} }