diff --git a/src/common/instructions.rs b/src/common/instructions.rs index 5343d91..598599a 100644 --- a/src/common/instructions.rs +++ b/src/common/instructions.rs @@ -152,7 +152,7 @@ impl std::fmt::Display for Register { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq)] #[repr(u8)] #[non_exhaustive] /// A list of all current instructions in the DSA. @@ -216,6 +216,12 @@ pub enum Instruction { Halt = 0x24, } +impl PartialEq for Instruction { + fn eq(&self, other: &Self) -> bool { + self.encode() == other.encode() + } +} + impl Instruction { /// Returns the opcode of an instruction. /// @@ -355,7 +361,7 @@ impl TryFrom for Instruction { /// Instruction decoding can be using using [`Instruction::try_from`] fn try_from(data: u32) -> Result { // Pull the opcode out so we can parse it correctly. - let opcode: u8 = ((data & 0xfc << 26) >> 26) as u8; + let opcode = ((data >> 26) & 0x3F) as u8; match opcode { 0x0 => Ok(Self::Nop), diff --git a/src/common/instructions/args.rs b/src/common/instructions/args.rs index 33cee92..c796958 100644 --- a/src/common/instructions/args.rs +++ b/src/common/instructions/args.rs @@ -32,7 +32,7 @@ impl std::fmt::Display for ArgsDecodeError { impl std::error::Error for ArgsDecodeError {} -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] /// Used by instructions with 2 registers and an immediate argument. pub struct ITypeArgs { pub immediate: u16, @@ -69,16 +69,19 @@ impl TryFrom for ITypeArgs { type Error = ArgsDecodeError; fn try_from(data: u32) -> Result { - let r1 = ((data >> 21) as u8).try_into()?; - let r2 = ((data >> 16) as u8).try_into()?; + let r1 = ((data >> 21) & 0x1F) as u8; + let r2 = ((data >> 16) & 0x1F) as u8; let immediate = data as u16; + let r1 = r1.try_into()?; + let r2 = r2.try_into()?; + Ok(Self { immediate, r1, r2 }) } } /// Used by instructions not using immediates (besides 5 bit shift values). -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RTypeArgs { pub sr1: Register, pub sr2: Register, @@ -132,10 +135,10 @@ impl TryFrom for RTypeArgs { type Error = ArgsDecodeError; fn try_from(data: u32) -> Result { - let sr1 = (data >> 21) as u8; - let sr2 = (data >> 16) as u8; - let dr = (data >> 11) as u8; - let shamt = (data >> 6) as u8; + let sr1 = ((data >> 21) & 0x1F) as u8; + let sr2 = ((data >> 16) & 0x1F) as u8; + let dr = ((data >> 11) & 0x1F) as u8; + let shamt = ((data >> 6) & 0x1F) as u8; let sr1_reg = sr1.try_into()?; let sr2_reg = sr2.try_into()?; diff --git a/src/common/instructions/encode.rs b/src/common/instructions/encode.rs index 2ad0345..0d9a7cb 100644 --- a/src/common/instructions/encode.rs +++ b/src/common/instructions/encode.rs @@ -58,3 +58,6 @@ impl Encode for Instruction { ) } } + +#[cfg(test)] +mod tests; diff --git a/src/common/instructions/encode/tests.rs b/src/common/instructions/encode/tests.rs new file mode 100644 index 0000000..afd7eeb --- /dev/null +++ b/src/common/instructions/encode/tests.rs @@ -0,0 +1,95 @@ +use crate::common::prelude::*; + +#[test] +fn test_encode_nop() { + let no_reg = Register::NoReg as u32; + let no_op = u32::from(Instruction::Nop.opcode()); + + let expected = no_op << 26 | no_reg << 21 | no_reg << 16 | no_reg << 11; + let got = Instruction::Nop.encode(); + + assert_eq!(expected, got); +} + +#[test] +fn test_encode_mov() { + let rg0 = Register::Rg0 as u32; + let rg1 = Register::Rg1 as u32; + let no_reg = Register::NoReg as u32; + + let instruction = Instruction::Mov(RTypeArgs::new( + Some(Register::Rg0), + None, + Some(Register::Rg1), + None, + )); + let mov = u32::from(instruction.opcode()); + + let expected = mov << 26 | rg0 << 21 | no_reg << 16 | rg1 << 11; + let got = instruction.encode(); + + assert_eq!(expected, got); +} + +#[test] +fn test_encode_load_byte() { + let rg0 = Register::Rg0 as u32; + let rg1 = Register::Rg1 as u32; + let immediate = 100; + + let instruction = Instruction::LoadByte(ITypeArgs::new( + immediate, + Some(Register::Rg0), + Some(Register::Rg1), + )); + let load_byte = u32::from(instruction.opcode()); + + let expected = load_byte << 26 | rg0 << 21 | rg1 << 16 | u32::from(immediate); + let got = instruction.encode(); + + assert_eq!(expected, got); +} + +#[test] +fn test_encode_shift_left_shamt() { + let rg0 = Register::Rg0 as u32; + let no_reg = Register::NoReg as u32; + + let shift_amount = 5; + + let instruction = Instruction::ShiftLeft(RTypeArgs::new( + Some(Register::Rg0), + None, + None, + Some(shift_amount), + )); + let shift_left = u32::from(instruction.opcode()); + + let expected = + shift_left << 26 | rg0 << 21 | no_reg << 16 | no_reg << 11 | u32::from(shift_amount) << 6; + + let got = instruction.encode(); + + assert_eq!(expected, got); +} + +#[test] +fn test_encode_shift_left_reg() { + let rg0 = Register::Rg0 as u32; + let rg1 = Register::Rg1 as u32; + let no_reg = Register::NoReg as u32; + + let instruction = Instruction::ShiftLeft(RTypeArgs::new( + Some(Register::Rg0), + Some(Register::Rg1), + None, + None, + )); + let shift_left = u32::from(instruction.opcode()); + + let expected = shift_left << 26 | rg0 << 21 | rg1 << 16 | no_reg << 11; + + let got = instruction.encode(); + + assert_eq!(expected, got); +} diff --git a/src/common/instructions/tests.rs b/src/common/instructions/tests.rs index 267ff19..fe2f22f 100644 --- a/src/common/instructions/tests.rs +++ b/src/common/instructions/tests.rs @@ -1,3 +1,4 @@ +#![allow(clippy::unwrap_used)] use crate::common::prelude::*; #[test] @@ -105,95 +106,106 @@ fn test_opcode_boundary_values() { } #[test] -fn test_encode_nop() { - let no_reg = Register::NoReg as u32; - let no_op = u32::from(Instruction::Nop.opcode()); - - let expected = no_op << 26 | no_reg << 21 | no_reg << 16 | no_reg << 11; - let got = Instruction::Nop.encode(); - - assert_eq!(expected, got); +fn test_instruction_decode_nop() { + let instr = Instruction::Nop; + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); } #[test] -fn test_encode_mov() { - let rg0 = Register::Rg0 as u32; - let rg1 = Register::Rg1 as u32; - let no_reg = Register::NoReg as u32; - - let instruction = Instruction::Mov(RTypeArgs::new( - Some(Register::Rg0), - None, - Some(Register::Rg1), - None, - )); - let mov = u32::from(instruction.opcode()); - - let expected = mov << 26 | rg0 << 21 | no_reg << 16 | rg1 << 11; - let got = instruction.encode(); - - assert_eq!(expected, got); -} - -#[test] -fn test_encode_load_byte() { - let rg0 = Register::Rg0 as u32; - let rg1 = Register::Rg1 as u32; - let immediate = 100; - - let instruction = Instruction::LoadByte(ITypeArgs::new( - immediate, +fn test_instruction_decode_data_transfer() { + let args = RTypeArgs::new( Some(Register::Rg0), Some(Register::Rg1), - )); - let load_byte = u32::from(instruction.opcode()); + Some(Register::Rg2), + Some(5), + ); + let instr = Instruction::Mov(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); - let expected = load_byte << 26 | rg0 << 21 | rg1 << 16 | u32::from(immediate); - let got = instruction.encode(); - - assert_eq!(expected, got); + let iargs = ITypeArgs::new(100, Some(Register::Rg3), Some(Register::Rg4)); + let instr = Instruction::LoadWord(iargs); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); } #[test] -fn test_encode_shift_left_shamt() { - let rg0 = Register::Rg0 as u32; - let no_reg = Register::NoReg as u32; +fn test_instruction_decode_jump() { + let args = ITypeArgs::new(200, Some(Register::Acc), Some(Register::Spr)); + let instr = Instruction::Jump(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); - let shift_amount = 5; - - let instruction = Instruction::ShiftLeft(RTypeArgs::new( - Some(Register::Rg0), - None, - None, - Some(shift_amount), - )); - let shift_left = u32::from(instruction.opcode()); - - let expected = - shift_left << 26 | rg0 << 21 | no_reg << 16 | no_reg << 11 | u32::from(shift_amount) << 6; - - let got = instruction.encode(); - - assert_eq!(expected, got); + let instr = Instruction::JumpEq(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); } #[test] -fn test_encode_shift_left_reg() { - let rg0 = Register::Rg0 as u32; - let rg1 = Register::Rg1 as u32; - let no_reg = Register::NoReg as u32; +fn test_instruction_decode_arithmetic() { + let args = RTypeArgs::new( + Some(Register::Bpr), + Some(Register::Rg7), + Some(Register::Rgf), + Some(31), + ); + let instr = Instruction::Add(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); - let instruction = Instruction::ShiftLeft(RTypeArgs::new( - Some(Register::Rg0), - Some(Register::Rg1), - None, - None, - )); - let shift_left = u32::from(instruction.opcode()); - - let expected = shift_left << 26 | rg0 << 21 | rg1 << 16 | no_reg << 11; - - let got = instruction.encode(); - - assert_eq!(expected, got); + let instr = Instruction::Compare(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); } + +#[test] +fn test_instruction_decode_logical() { + let args = RTypeArgs::new( + Some(Register::Rg8), + Some(Register::Rg9), + Some(Register::Rga), + Some(15), + ); + let instr = Instruction::And(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); + + let instr = Instruction::Xor(args); + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); +} + +#[test] +fn test_instruction_decode_misc() { + let instr = Instruction::Halt; + let encoded = instr.encode(); + let decoded = Instruction::decode(encoded).unwrap(); + assert_eq!(instr, decoded); +} + +#[test] +fn test_instruction_decode_invalid() { + // Test with invalid opcode. + let invalid_encoded = 0xFF00_0000; + assert!(Instruction::decode(invalid_encoded).is_err()); +} + +// TODO: Get interrupts working. +// #[test] +// fn test_instruction_decode_interrupt() { +// let interrupt = Interrupt::Software(10); +// let instr = Instruction::Interrupt(interrupt); +// let encoded = instr.encode(); +// let decoded = Instruction::decode(encoded).unwrap(); +// assert_eq!(instr, decoded); +// }