diff --git a/src/emulator/system/model.rs b/src/emulator/system/model.rs index b68e09c..b6859db 100644 --- a/src/emulator/system/model.rs +++ b/src/emulator/system/model.rs @@ -19,7 +19,7 @@ pub enum Command { Write(Address, Vec), } -#[derive(Default, Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct RegFile { // General Purpose Registers rg0: u32, diff --git a/src/emulator/system/processor.rs b/src/emulator/system/processor.rs index 8dcbfe0..dfa2258 100644 --- a/src/emulator/system/processor.rs +++ b/src/emulator/system/processor.rs @@ -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; diff --git a/src/emulator/system/processor/tests.rs b/src/emulator/system/processor/tests.rs new file mode 100644 index 0000000..e6a71f1 --- /dev/null +++ b/src/emulator/system/processor/tests.rs @@ -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); +}