diff --git a/src/common/instructions.rs b/src/common/instructions.rs index 18144b8..b66a650 100644 --- a/src/common/instructions.rs +++ b/src/common/instructions.rs @@ -154,69 +154,121 @@ impl std::fmt::Display for Register { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] +#[repr(u8)] pub enum Instruction { // No-op - Nop, + Nop = 0x0, // Data transfer instructions - Mov(Register, Register), - MovSigned(Register, Register), + Mov(Register, Register) = 0x1, + MovSigned(Register, Register) = 0x2, - LoadByte(Register, Offset, Register), - LoadByteSigned(Register, Offset, Register), - LoadHalfword(Register, Offset, Register), - LoadHalfwordSigned(Register, Offset, Register), - LoadWord(Register, Offset, Register), + LoadByte(Register, Offset, Register) = 0x3, + LoadByteSigned(Register, Offset, Register) = 0x4, + LoadHalfword(Register, Offset, Register) = 0x5, + LoadHalfwordSigned(Register, Offset, Register) = 0x6, + LoadWord(Register, Offset, Register) = 0x7, - StoreByte(Register, Offset, Register), - StoreHalfword(Register, Offset, Register), - StoreWord(Register, Offset, Register), + StoreByte(Register, Offset, Register) = 0x8, + StoreHalfword(Register, Offset, Register) = 0x9, + StoreWord(Register, Offset, Register) = 0xA, - LoadLowerImmediate(Register, Immediate), - LoadUpperImmediate(Register, Immediate), + LoadLowerImmediate(Register, Immediate) = 0xB, + LoadUpperImmediate(Register, Immediate) = 0xC, // Jump Instructions - Jump(Register, Offset), - JumpEq(Register, Offset), - JumpNeq(Register, Offset), - JumpGt(Register, Offset), - JumpGe(Register, Offset), - JumpLt(Register, Offset), - JumpLe(Register, Offset), + Jump(Register, Offset) = 0xD, + JumpEq(Register, Offset) = 0xE, + JumpNeq(Register, Offset) = 0xF, + JumpGt(Register, Offset) = 0x10, + JumpGe(Register, Offset) = 0x11, + JumpLt(Register, Offset) = 0x12, + JumpLe(Register, Offset) = 0x13, // Comparison - Compare(Register, Register), + Compare(Register, Register) = 0x14, // Arithmetic - Add(Register, Register, Register), - Sub(Register, Register, Register), - Increment(Register), - Decrement(Register), - ShiftLeft(Register, Register, Immediate), - ShiftRight(Register, Register, Immediate), + Add(Register, Register, Register) = 0x19, + Sub(Register, Register, Register) = 0x1A, + Increment(Register) = 0x15, + Decrement(Register) = 0x16, + ShiftLeft(Register, Register, Immediate) = 0x17, + ShiftRight(Register, Register, Immediate) = 0x18, // Logical - And(Register, Register, Register), - Or(Register, Register, Register), - Not(Register, Register), - Xor(Register, Register, Register), - Nand(Register, Register, Register), - Nor(Register, Register, Register), - Xnor(Register, Register, Register), + And(Register, Register, Register) = 0x1B, + Or(Register, Register, Register) = 0x1C, + Not(Register, Register) = 0x1D, + Xor(Register, Register, Register) = 0x1E, + Nand(Register, Register, Register) = 0x1F, + Nor(Register, Register, Register) = 0x20, + Xnor(Register, Register, Register) = 0x21, // Misc - Interrupt(Interrupt), - IntReturn, - Halt, + Interrupt(Interrupt) = 0x22, + IntReturn = 0x23, + Halt = 0x24, } impl Instruction { + /// Returns the opcode of an instruction. + /// + /// # Notes + /// + /// The top two bits shall be 0, opcodes are 6-bits long. #[must_use] - pub fn encode(&self) -> u32 { - todo!("imlement instruction encoding") + pub const fn opcode(&self) -> u8 { + unsafe { *std::ptr::from_ref::(self).cast::() } } + #[must_use] + #[allow(unused)] + pub fn encode(&self) -> u32 { + match self { + Self::Nop => todo!(), + Self::Mov(src, dst) => todo!(), + Self::MovSigned(register, register1) => todo!(), + Self::LoadByte(register, _, register1) => todo!(), + Self::LoadByteSigned(register, _, register1) => todo!(), + Self::LoadHalfword(register, _, register1) => todo!(), + Self::LoadHalfwordSigned(register, _, register1) => todo!(), + Self::LoadWord(register, _, register1) => todo!(), + Self::StoreByte(register, _, register1) => todo!(), + Self::StoreHalfword(register, _, register1) => todo!(), + Self::StoreWord(register, _, register1) => todo!(), + Self::LoadLowerImmediate(register, _) => todo!(), + Self::LoadUpperImmediate(register, _) => todo!(), + Self::Jump(register, _) => todo!(), + Self::JumpEq(register, _) => todo!(), + Self::JumpNeq(register, _) => todo!(), + Self::JumpGt(register, _) => todo!(), + Self::JumpGe(register, _) => todo!(), + Self::JumpLt(register, _) => todo!(), + Self::JumpLe(register, _) => todo!(), + Self::Compare(register, register1) => todo!(), + Self::Add(register, register1, register2) => todo!(), + Self::Sub(register, register1, register2) => todo!(), + Self::Increment(register) => todo!(), + Self::Decrement(register) => todo!(), + Self::ShiftLeft(register, register1, _) => todo!(), + Self::ShiftRight(register, register1, _) => todo!(), + Self::And(register, register1, register2) => todo!(), + Self::Or(register, register1, register2) => todo!(), + Self::Not(register, register1) => todo!(), + Self::Xor(register, register1, register2) => todo!(), + Self::Nand(register, register1, register2) => todo!(), + Self::Nor(register, register1, register2) => todo!(), + Self::Xnor(register, register1, register2) => todo!(), + Self::Interrupt(interrupt) => todo!(), + Self::IntReturn => todo!(), + Self::Halt => todo!(), + } + } + + const fn _encode_mov() {} + #[must_use] pub const fn decode(_data: u32) -> Self { // TODO: this needs to actually decode something diff --git a/src/common/mod.rs b/src/common/mod.rs index 4f17719..7173b3f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1 +1,4 @@ pub mod instructions; + +#[cfg(test)] +mod tests; diff --git a/src/common/tests.rs b/src/common/tests.rs new file mode 100644 index 0000000..cdc5a14 --- /dev/null +++ b/src/common/tests.rs @@ -0,0 +1,175 @@ +use crate::common::instructions::*; + +#[test] +fn test_opcode_basic_instructions() { + assert_eq!(Instruction::Nop.opcode(), 0x0); + assert_eq!(Instruction::Mov(Register::Rg0, Register::Rg1).opcode(), 0x1); + assert_eq!( + Instruction::MovSigned(Register::Rg0, Register::Rg1).opcode(), + 0x2 + ); + assert_eq!(Instruction::Halt.opcode(), 0x24); +} + +#[test] +fn test_opcode_data_transfer_instructions() { + assert_eq!( + Instruction::LoadByte(Register::Rg0, 0, Register::Rg1).opcode(), + 0x3 + ); + assert_eq!( + Instruction::LoadByteSigned(Register::Rg0, 0, Register::Rg1).opcode(), + 0x4 + ); + assert_eq!( + Instruction::LoadHalfword(Register::Rg0, 0, Register::Rg1).opcode(), + 0x5 + ); + assert_eq!( + Instruction::LoadHalfwordSigned(Register::Rg0, 0, Register::Rg1).opcode(), + 0x6 + ); + assert_eq!( + Instruction::LoadWord(Register::Rg0, 0, Register::Rg1).opcode(), + 0x7 + ); + assert_eq!( + Instruction::StoreByte(Register::Rg0, 0, Register::Rg1).opcode(), + 0x8 + ); + assert_eq!( + Instruction::StoreHalfword(Register::Rg0, 0, Register::Rg1).opcode(), + 0x9 + ); + assert_eq!( + Instruction::StoreWord(Register::Rg0, 0, Register::Rg1).opcode(), + 0xA + ); +} + +#[test] +fn test_opcode_immediate_instructions() { + assert_eq!( + Instruction::LoadLowerImmediate(Register::Rg0, 0).opcode(), + 0xB + ); + assert_eq!( + Instruction::LoadUpperImmediate(Register::Rg0, 0).opcode(), + 0xC + ); +} + +#[test] +fn test_opcode_jump_instructions() { + assert_eq!(Instruction::Jump(Register::Rg0, 0).opcode(), 0xD); + assert_eq!(Instruction::JumpEq(Register::Rg0, 0).opcode(), 0xE); + assert_eq!(Instruction::JumpNeq(Register::Rg0, 0).opcode(), 0xF); + assert_eq!(Instruction::JumpGt(Register::Rg0, 0).opcode(), 0x10); + assert_eq!(Instruction::JumpGe(Register::Rg0, 0).opcode(), 0x11); + assert_eq!(Instruction::JumpLt(Register::Rg0, 0).opcode(), 0x12); + assert_eq!(Instruction::JumpLe(Register::Rg0, 0).opcode(), 0x13); +} + +#[test] +fn test_opcode_arithmetic_instructions() { + assert_eq!( + Instruction::Compare(Register::Rg0, Register::Rg1).opcode(), + 0x14 + ); + assert_eq!(Instruction::Increment(Register::Rg0).opcode(), 0x15); + assert_eq!(Instruction::Decrement(Register::Rg0).opcode(), 0x16); + assert_eq!( + Instruction::ShiftLeft(Register::Rg0, Register::Rg1, 0).opcode(), + 0x17 + ); + assert_eq!( + Instruction::ShiftRight(Register::Rg0, Register::Rg1, 0).opcode(), + 0x18 + ); + assert_eq!( + Instruction::Add(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x19 + ); + assert_eq!( + Instruction::Sub(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x1A + ); +} + +#[test] +fn test_opcode_logical_instructions() { + assert_eq!( + Instruction::And(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x1B + ); + assert_eq!( + Instruction::Or(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x1C + ); + assert_eq!( + Instruction::Not(Register::Rg0, Register::Rg1).opcode(), + 0x1D + ); + assert_eq!( + Instruction::Xor(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x1E + ); + assert_eq!( + Instruction::Nand(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x1F + ); + assert_eq!( + Instruction::Nor(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x20 + ); + assert_eq!( + Instruction::Xnor(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + 0x21 + ); +} + +#[test] +fn test_opcode_system_instructions() { + assert_eq!( + Instruction::Interrupt(Interrupt::Software(0)).opcode(), + 0x22 + ); + assert_eq!(Instruction::IntReturn.opcode(), 0x23); +} + +#[test] +fn test_opcode_top_bits_are_zero() { + // Test that opcodes have top 2 bits as 0 (6-bit opcodes) + let instructions = [ + Instruction::Nop, + Instruction::Mov(Register::Rg0, Register::Rg1), + Instruction::Add(Register::Rg0, Register::Rg1, Register::Rg2), + Instruction::Halt, + ]; + + for instruction in instructions { + let opcode = instruction.opcode(); + assert_eq!( + opcode & 0xC0, + 0, + "Top 2 bits should be 0 for opcode {opcode:#02x}" + ); + } +} + +#[test] +fn test_opcode_same_instruction_different_params() { + // Same instruction type with different parameters should have same opcode + assert_eq!( + Instruction::Mov(Register::Rg0, Register::Rg1).opcode(), + Instruction::Mov(Register::Acc, Register::Spr).opcode() + ); + assert_eq!( + Instruction::Add(Register::Rg0, Register::Rg1, Register::Rg2).opcode(), + Instruction::Add(Register::Acc, Register::Spr, Register::Bpr).opcode() + ); + assert_eq!( + Instruction::LoadWord(Register::Rg0, 100, Register::Rg1).opcode(), + Instruction::LoadWord(Register::Acc, 500, Register::Spr).opcode() + ); +} diff --git a/src/emulator/system/processor.rs b/src/emulator/system/processor.rs index 51af413..e64dbc6 100644 --- a/src/emulator/system/processor.rs +++ b/src/emulator/system/processor.rs @@ -48,7 +48,7 @@ impl Processor { // decode and execute the instruction let instruction = Instruction::decode(val); - instruction.clone().execute(self); + instruction.execute(self); instruction }