added error handling to emulator
This commit is contained in:
@@ -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 },
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user