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 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 _ = state_tx.send(initial_state);
@@ -114,14 +118,25 @@ pub fn run_emulator(
addr = new;
}
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) => {
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(
&mut processor,
running,
@@ -130,8 +145,6 @@ pub fn run_emulator(
&mut history,
);
println!("state");
let _ = state_tx.send(state);
}
@@ -166,7 +179,15 @@ pub fn run_emulator(
}
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(
&mut processor,
running,
@@ -185,7 +206,7 @@ pub fn run_emulator(
}
fn state(
cpu_lock: &mut Processor,
processor: &mut Processor,
running: Running,
instruction_count: usize,
memory_view: Vec<u8>,
@@ -196,12 +217,18 @@ fn state(
State {
// TODO: Replace with actual register access from your CPU.
reg_file: cpu_lock.registers,
reg_file: processor.registers,
running,
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,
display_view: cpu_lock.display(),
display_view: processor.display().unwrap_or_else(|_| {
processor.begin_interrupt(Interrupt::HardFault);
Vec::new()
}),
error: None,
persistent: PersistentState { history: hsclone },
}
+71 -23
View File
@@ -1,13 +1,43 @@
use std::collections::HashMap;
use crate::emulator::system::model::ProcessorError;
pub trait MemoryUnit: Send + Sync {
fn reset(&mut self);
fn read_byte(&mut self, addr: u32) -> u8;
fn write_byte(&mut self, addr: u32, value: u8);
fn read_word(&mut self, addr: u32) -> u32;
fn write_word(&mut self, addr: u32, value: u32);
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8>;
fn write_range(&mut self, addr: u32, value: Vec<u8>);
fn read_byte(&mut self, addr: u32) -> Result<u8, ProcessorError>;
fn write_byte(&mut self, addr: u32, value: u8) -> Result<(), ProcessorError>;
fn read_word(&mut self, addr: u32) -> Result<u32, ProcessorError>;
fn write_word(&mut self, addr: u32, value: u32) -> Result<(), ProcessorError>;
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 {
@@ -64,59 +94,77 @@ impl MemoryUnit for MainStore {
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 = 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);
println!("reading word from {block_addr:x?} + {offset}");
let block = self.mut_block(block_addr);
let mut bytes = [0; 4];
bytes[0] = block.data[offset as usize];
bytes[1] = block.data[(offset + 1) as usize];
bytes[2] = block.data[(offset + 2) 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);
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?}");
data
Ok(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 = self.mut_block(block_addr);
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 = self.mut_block(block_addr);
block.data[offset as usize] = (value >> 24) 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 + 3) as usize] = value as u8;
Ok(())
}
fn write_range(&mut self, addr: u32, value: Vec<u8>) {
// println!("writing {value:?} to {addr:x?}");
fn write_range(&mut self, addr: u32, value: Vec<u8>) -> Result<(), ProcessorError> {
for (i, byte) in value.into_iter().enumerate() {
let (block_addr, offset) = Self::segment_addr(addr + i as u32);
let block = self.mut_block(block_addr);
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>),
}
#[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)]
pub struct RegFile {
// General Purpose Registers
+33 -39
View File
@@ -5,12 +5,10 @@ use std::{
use crate::emulator::system::{
memory::MemoryUnit,
model::{IODevice, RegFile},
model::{IODevice, ProcessorError, RegFile},
};
use common::instructions::{
Instruction, Interrupt, Register, errors::InstructionDecodeError,
};
use common::instructions::{Instruction, Interrupt, Register};
pub struct Processor {
pub memory: Box<dyn MemoryUnit>,
@@ -25,7 +23,6 @@ fn log(message: &str) {
println!("\x1b[32mINFO:\x1b[0m {message}");
}
#[allow(clippy::needless_pass_by_ref_mut)]
impl Processor {
#[must_use]
pub fn new(memory: Box<dyn MemoryUnit>, io_devices: Vec<Arc<dyn IODevice>>) -> Self {
@@ -48,7 +45,7 @@ impl Processor {
self.memory.reset();
}
pub fn cycle(&mut self) -> Result<(u32, Instruction), InstructionDecodeError> {
pub fn cycle(&mut self) -> Result<(u32, Instruction), ProcessorError> {
self.halted = false;
// Get value from PCX.
@@ -58,17 +55,18 @@ impl Processor {
// Set MAR to the previous value of PCX.
*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].
*self.reg(Register::Mar) = val;
// Decode and execute the instruction.
let instruction = Instruction::decode(val)?;
let instruction = Instruction::decode(val)
.map_err(|_| ProcessorError::InvalidInstruction(val))?;
log(&instruction.to_string());
instruction.execute(self);
instruction.execute(self)?;
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)
}
@@ -99,21 +97,7 @@ impl Processor {
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
fn set_flag(&mut self, flag: Flag, value: bool) {
if value {
*self.reg(Register::Sts) |= flag as u32;
@@ -135,16 +119,18 @@ impl Processor {
*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.
todo!();
}
// TODO: remove this once implemented
#[allow(clippy::needless_pass_by_ref_mut)]
fn end_interrupt(&mut self) {
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 size = n * 4;
// returns the stack
@@ -170,12 +156,12 @@ enum Flag {
}
trait Executable {
fn execute(self, cpu: &mut Processor);
fn execute(self, cpu: &mut Processor) -> Result<(), ProcessorError>;
}
impl Executable for Instruction {
#[allow(clippy::too_many_lines)]
fn execute(self, cpu: &mut Processor) {
fn execute(self, cpu: &mut Processor) -> Result<(), ProcessorError> {
match self {
// No operation - a blank line.
// Copies from SrcReg to a.drReg.
@@ -193,7 +179,8 @@ impl Executable for Instruction {
// effective address must be byte-aligned.
Self::LoadByte(a) => {
*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.
Self::LoadByteSigned(a) => {
*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) => {
// we read an entire word, then right shift so we only get the first half
// of the word
*cpu.reg(a.r2) =
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16;
*cpu.reg(a.r2) = cpu
.memory
.read_word(cpu.get(a.r1) + u32::from(a.immediate))?
>> 16;
}
// Loads a sign-extended half-word from memory address (base + offset) into
// a.drReg. The effective address must be 2-byte-aligned.
Self::LoadHalfwordSigned(a) => {
*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
// effective address must be 4-byte-aligned.
Self::LoadWord(a) => {
*cpu.reg(a.r2) =
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate));
*cpu.reg(a.r2) = cpu
.memory
.read_word(cpu.get(a.r1) + u32::from(a.immediate))?;
}
// 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.get(a.r2) + u32::from(a.immediate),
cpu.get(a.r1) as u8,
);
)?;
}
// 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
let bytes = (cpu.get(a.r1) as u16).to_le_bytes();
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
.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
// address must be 4-byte-aligned.
Self::StoreWord(a) => {
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
@@ -411,6 +404,7 @@ impl Executable for Instruction {
todo!()
}
}
Ok(())
}
}
+110 -38
View File
@@ -13,7 +13,9 @@ fn test_nop_instruction() {
let mut cpu = create_test_processor();
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!(
cpu.registers.get(Register::Rg0),
@@ -37,7 +39,9 @@ fn test_mov_instruction() {
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);
}
@@ -53,7 +57,9 @@ fn test_mov_signed_instruction() {
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);
}
@@ -61,7 +67,9 @@ fn test_mov_signed_instruction() {
fn test_load_byte_instruction() {
let mut cpu = create_test_processor();
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;
let load_byte_instr = Instruction::LoadByte(ITypeArgs::new(
@@ -70,7 +78,9 @@ fn test_load_byte_instruction() {
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);
}
@@ -78,7 +88,9 @@ fn test_load_byte_instruction() {
fn test_load_byte_signed_instruction() {
let mut cpu = create_test_processor();
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;
let load_byte_signed_instr = Instruction::LoadByteSigned(ITypeArgs::new(
@@ -87,7 +99,9 @@ fn test_load_byte_signed_instruction() {
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);
}
@@ -95,7 +109,9 @@ fn test_load_byte_signed_instruction() {
fn test_load_halfword_instruction() {
let mut cpu = create_test_processor();
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;
let load_halfword_instr = Instruction::LoadHalfword(ITypeArgs::new(
@@ -104,7 +120,9 @@ fn test_load_halfword_instruction() {
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);
}
@@ -112,7 +130,9 @@ fn test_load_halfword_instruction() {
fn test_load_word_instruction() {
let mut cpu = create_test_processor();
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;
let load_word_instr = Instruction::LoadWord(ITypeArgs::new(
@@ -121,7 +141,9 @@ fn test_load_word_instruction() {
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);
}
@@ -138,8 +160,10 @@ fn test_store_byte_instruction() {
Some(Register::Rg1),
));
store_byte_instr.execute(&mut cpu);
assert_eq!(cpu.memory.read_byte(addr), 0xAB);
store_byte_instr.execute(&mut cpu).expect(
"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]
@@ -155,8 +179,10 @@ fn test_store_word_instruction() {
Some(Register::Rg1),
));
store_word_instr.execute(&mut cpu);
assert_eq!(cpu.memory.read_word(addr), 0x1234_5678);
store_word_instr.execute(&mut cpu).expect(
"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]
@@ -172,7 +198,9 @@ fn test_add_instruction() {
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);
}
@@ -189,7 +217,9 @@ fn test_sub_instruction() {
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);
}
@@ -206,7 +236,9 @@ fn test_and_instruction() {
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);
}
@@ -223,7 +255,9 @@ fn test_or_instruction() {
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);
}
@@ -240,7 +274,9 @@ fn test_xor_instruction() {
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);
}
@@ -256,7 +292,9 @@ fn test_not_instruction() {
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);
}
@@ -273,7 +311,9 @@ fn test_compare_equal() {
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::GreaterThan));
@@ -293,7 +333,9 @@ fn test_compare_greater_than() {
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::GreaterThan));
@@ -313,7 +355,9 @@ fn test_compare_less_than() {
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::GreaterThan));
@@ -328,7 +372,9 @@ fn test_increment_instruction() {
let inc_instr =
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);
}
@@ -340,7 +386,9 @@ fn test_decrement_instruction() {
let dec_instr =
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);
}
@@ -356,7 +404,9 @@ fn test_shift_left_with_shamt() {
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);
}
@@ -372,7 +422,9 @@ fn test_shift_right_with_shamt() {
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);
}
@@ -389,7 +441,9 @@ fn test_shift_left_with_register() {
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);
}
@@ -403,7 +457,9 @@ fn test_load_lower_immediate() {
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);
}
@@ -418,7 +474,9 @@ fn test_load_upper_immediate() {
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);
}
@@ -430,7 +488,9 @@ fn test_jump_unconditional() {
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_ne!(cpu.get(Register::Pcx), initial_pc);
}
@@ -444,7 +504,9 @@ fn test_jump_equal_when_flag_set() {
let jump_eq_instr =
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);
}
@@ -458,7 +520,9 @@ fn test_jump_equal_when_flag_not_set() {
let jump_eq_instr =
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);
}
@@ -467,7 +531,9 @@ fn test_halt_instruction() {
let mut cpu = create_test_processor();
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);
}
@@ -484,7 +550,9 @@ fn test_nand_instruction() {
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);
}
@@ -501,7 +569,9 @@ fn test_nor_instruction() {
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);
}
@@ -518,6 +588,8 @@ fn test_xnor_instruction() {
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);
}