processor: fix Flags to be bit flags and add test module
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user