processor: fix Flags to be bit flags and add test module

This commit is contained in:
2025-06-15 12:58:08 +01:00
parent 300c455efd
commit 3aa5d33f68
3 changed files with 506 additions and 10 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ pub enum Command {
Write(Address, Vec<u8>),
}
#[derive(Default, Debug, Clone, Copy)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegFile {
// General Purpose Registers
rg0: u32,
+12 -9
View File
@@ -134,15 +134,15 @@ impl Processor {
#[derive(Debug)]
#[expect(dead_code)]
enum Flag {
Equal = 0,
GreaterThan = 1,
LessThan = 2,
Zero = 3,
Positive = 4,
Negative = 5,
Carry = 6,
UserMode = 7,
InterruptsEnabled = 8,
Equal = 1,
GreaterThan = 2,
LessThan = 4,
Zero = 8,
Positive = 16,
Negative = 32,
Carry = 64,
UserMode = 128,
InterruptsEnabled = 256,
}
trait Executable {
@@ -419,3 +419,6 @@ const fn sign_extend(val: u32) -> u32 {
val
}
}
#[cfg(test)]
mod tests;
+493
View File
@@ -0,0 +1,493 @@
use super::*;
use crate::{common::instructions::*, emulator::system::memory::*};
fn create_test_processor() -> Processor {
let memory = Box::new(MainStore::new());
Processor::new(memory)
}
#[test]
fn test_nop_instruction() {
let mut cpu = create_test_processor();
let initial_state = cpu.registers;
Instruction::Nop.execute(&mut cpu);
assert_eq!(
cpu.registers.get(Register::Rg0),
initial_state.get(Register::Rg0)
);
assert_eq!(
cpu.registers.get(Register::Acc),
initial_state.get(Register::Acc)
);
}
#[test]
fn test_mov_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0x1234_5678;
let mov_instr = Instruction::Mov(RTypeArgs::new(
Some(Register::Rg1),
None,
Some(Register::Rg2),
None,
));
mov_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0x1234_5678);
}
#[test]
fn test_mov_signed_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0x0000_00FF;
let mov_signed_instr = Instruction::MovSigned(RTypeArgs::new(
Some(Register::Rg1),
None,
Some(Register::Rg2),
None,
));
mov_signed_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF);
}
#[test]
fn test_load_byte_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
cpu.memory.write_byte(addr, 0xAB);
*cpu.reg(Register::Rg1) = addr - 4;
let load_byte_instr =
Instruction::LoadByte(ITypeArgs::new(4, Some(Register::Rg1), Some(Register::Rg2)));
load_byte_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0x0000_00AB);
}
#[test]
fn test_load_byte_signed_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
cpu.memory.write_byte(addr, 0xFF);
*cpu.reg(Register::Rg1) = addr;
let load_byte_signed_instr =
Instruction::LoadByteSigned(ITypeArgs::new(0, Some(Register::Rg1), Some(Register::Rg2)));
load_byte_signed_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0xFFFF_FFFF);
}
#[test]
fn test_load_halfword_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
cpu.memory.write_word(addr, 0x1234_5678);
*cpu.reg(Register::Rg1) = addr;
let load_halfword_instr =
Instruction::LoadHalfword(ITypeArgs::new(0, Some(Register::Rg1), Some(Register::Rg2)));
load_halfword_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0x0000_1234);
}
#[test]
fn test_load_word_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
cpu.memory.write_word(addr, 0x1234_5678);
*cpu.reg(Register::Rg1) = addr;
let load_word_instr =
Instruction::LoadWord(ITypeArgs::new(0, Some(Register::Rg1), Some(Register::Rg2)));
load_word_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0x1234_5678);
}
#[test]
fn test_store_byte_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
*cpu.reg(Register::Rg1) = addr;
*cpu.reg(Register::Rg2) = 0xAB;
let store_byte_instr =
Instruction::StoreByte(ITypeArgs::new(0, Some(Register::Rg1), Some(Register::Rg2)));
store_byte_instr.execute(&mut cpu);
assert_eq!(cpu.memory.read_byte(addr), 0xAB);
}
#[test]
fn test_store_word_instruction() {
let mut cpu = create_test_processor();
let addr = 0x100;
*cpu.reg(Register::Rg1) = addr;
*cpu.reg(Register::Rg2) = 0x1234_5678;
let store_word_instr =
Instruction::StoreWord(ITypeArgs::new(0, Some(Register::Rg1), Some(Register::Rg2)));
store_word_instr.execute(&mut cpu);
assert_eq!(cpu.memory.read_word(addr), 0x1234_5678);
}
#[test]
fn test_add_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 15;
*cpu.reg(Register::Rg2) = 25;
let add_instr = Instruction::Add(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
add_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), 40);
}
#[test]
fn test_sub_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 50;
*cpu.reg(Register::Rg2) = 20;
let sub_instr = Instruction::Sub(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
sub_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), 30);
}
#[test]
fn test_and_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let and_instr = Instruction::And(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
and_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), 0b1000);
}
#[test]
fn test_or_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let or_instr = Instruction::Or(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
or_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), 0b1110);
}
#[test]
fn test_xor_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let xor_instr = Instruction::Xor(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
xor_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), 0b0110);
}
#[test]
fn test_not_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0x0F0F_0F0F;
let not_instr = Instruction::Not(RTypeArgs::new(
Some(Register::Rg1),
None,
Some(Register::Rg2),
None,
));
not_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg2), 0xF0F0_F0F0);
}
#[test]
fn test_compare_equal() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 42;
*cpu.reg(Register::Rg2) = 42;
let cmp_instr = Instruction::Compare(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
None,
None,
));
cmp_instr.execute(&mut cpu);
assert!(cpu.get_flag(Flag::Equal));
assert!(!cpu.get_flag(Flag::GreaterThan));
assert!(!cpu.get_flag(Flag::LessThan));
}
#[test]
fn test_compare_greater_than() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 50;
*cpu.reg(Register::Rg2) = 30;
let cmp_instr = Instruction::Compare(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
None,
None,
));
cmp_instr.execute(&mut cpu);
assert!(!cpu.get_flag(Flag::Equal));
assert!(cpu.get_flag(Flag::GreaterThan));
assert!(!cpu.get_flag(Flag::LessThan));
}
#[test]
fn test_compare_less_than() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 20;
*cpu.reg(Register::Rg2) = 30;
let cmp_instr = Instruction::Compare(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
None,
None,
));
cmp_instr.execute(&mut cpu);
assert!(!cpu.get_flag(Flag::Equal));
assert!(!cpu.get_flag(Flag::GreaterThan));
assert!(cpu.get_flag(Flag::LessThan));
}
#[test]
fn test_increment_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 42;
let inc_instr = Instruction::Increment(RTypeArgs::new(Some(Register::Rg1), None, None, None));
inc_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 43);
}
#[test]
fn test_decrement_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 42;
let dec_instr = Instruction::Decrement(RTypeArgs::new(Some(Register::Rg1), None, None, None));
dec_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 41);
}
#[test]
fn test_shift_left_with_shamt() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1010;
let shl_instr = Instruction::ShiftLeft(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Zero),
None,
Some(2),
));
shl_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 0b10_1000);
}
#[test]
fn test_shift_right_with_shamt() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b10_1000;
let shr_instr = Instruction::ShiftRight(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Zero),
None,
Some(2),
));
shr_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 0b1010);
}
#[test]
fn test_shift_left_with_register() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1010;
*cpu.reg(Register::Rg2) = 3;
let shl_instr = Instruction::ShiftLeft(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
None,
None,
));
shl_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 0b101_0000);
}
#[test]
fn test_load_lower_immediate() {
let mut cpu = create_test_processor();
let lli_instr =
Instruction::LoadLowerImmediate(ITypeArgs::new(0x1234, Some(Register::Rg1), None));
lli_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 0x0000_1234);
}
#[test]
fn test_load_upper_immediate() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0x0000_5678;
let lui_instr =
Instruction::LoadUpperImmediate(ITypeArgs::new(0x1234, Some(Register::Rg1), None));
lui_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg1), 0x1234_5678);
}
#[test]
fn test_jump_unconditional() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0x1000;
let initial_pc = cpu.get(Register::Pcx);
let jump_instr = Instruction::Jump(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Pcx), 0x1100);
assert_ne!(cpu.get(Register::Pcx), initial_pc);
}
#[test]
fn test_jump_equal_when_flag_set() {
let mut cpu = create_test_processor();
cpu.set_flag(Flag::Equal, true);
*cpu.reg(Register::Rg1) = 0x1000;
let jump_eq_instr = Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_eq_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Pcx), 0x1100);
}
#[test]
fn test_jump_equal_when_flag_not_set() {
let mut cpu = create_test_processor();
cpu.set_flag(Flag::Equal, false);
*cpu.reg(Register::Rg1) = 0x1000;
let initial_pc = cpu.get(Register::Pcx);
let jump_eq_instr = Instruction::JumpEq(ITypeArgs::new(0x100, Some(Register::Rg1), None));
jump_eq_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Pcx), initial_pc);
}
#[test]
fn test_halt_instruction() {
let mut cpu = create_test_processor();
assert!(!cpu.halted);
Instruction::Halt.execute(&mut cpu);
assert!(cpu.halted);
}
#[test]
fn test_nand_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let nand_instr = Instruction::Nand(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
nand_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), !0b1000);
}
#[test]
fn test_nor_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let nor_instr = Instruction::Nor(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
nor_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), !0b1110);
}
#[test]
fn test_xnor_instruction() {
let mut cpu = create_test_processor();
*cpu.reg(Register::Rg1) = 0b1100;
*cpu.reg(Register::Rg2) = 0b1010;
let xnor_instr = Instruction::Xnor(RTypeArgs::new(
Some(Register::Rg1),
Some(Register::Rg2),
Some(Register::Rg3),
None,
));
xnor_instr.execute(&mut cpu);
assert_eq!(cpu.get(Register::Rg3), !0b0110);
}