From 977a621d5faa161b72dec3391121cf812719c6f1 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 15 Jun 2025 13:56:45 +0100 Subject: [PATCH] common: instruction encoding via macro and trait ugly hack works --- src/common/instructions.rs | 131 +++++++++++++++--------------- src/common/instructions/encode.rs | 62 ++++++++++++++ 2 files changed, 127 insertions(+), 66 deletions(-) create mode 100644 src/common/instructions/encode.rs diff --git a/src/common/instructions.rs b/src/common/instructions.rs index f88b736..5ac9277 100644 --- a/src/common/instructions.rs +++ b/src/common/instructions.rs @@ -1,3 +1,5 @@ +use crate::common::instructions::encode::Encode; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Interrupt { Software(u8), @@ -124,35 +126,35 @@ impl TryFrom for Register { impl std::fmt::Display for Register { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Rg0 => write!(f, "Rg0"), - Self::Rg1 => write!(f, "Rg1"), - Self::Rg2 => write!(f, "Rg2"), - Self::Rg3 => write!(f, "Rg3"), - Self::Rg4 => write!(f, "Rg4"), - Self::Rg5 => write!(f, "Rg5"), - Self::Rg6 => write!(f, "Rg6"), - Self::Rg7 => write!(f, "Rg7"), - Self::Rg8 => write!(f, "Rg8"), - Self::Rg9 => write!(f, "Rg9"), - Self::Rga => write!(f, "Rga"), - Self::Rgb => write!(f, "Rgb"), - Self::Rgc => write!(f, "Rgc"), - Self::Rgd => write!(f, "Rgd"), - Self::Rge => write!(f, "Rge"), - Self::Rgf => write!(f, "Rgf"), - Self::Acc => write!(f, "Acc"), - Self::Spr => write!(f, "Spr"), - Self::Bpr => write!(f, "Bpr"), - Self::Ret => write!(f, "Ret"), - Self::Idr => write!(f, "Idr"), - Self::Mmr => write!(f, "Mmr"), - Self::Zero => write!(f, "Zero"), - Self::NoReg => write!(f, "None"), - Self::Mar => write!(f, "Mar"), - Self::Mdr => write!(f, "Mdr"), - Self::Sts => write!(f, "Sts"), - Self::Cir => write!(f, "Cir"), - Self::Pcx => write!(f, "Pcx"), + Self::Rg0 => write!(f, "rg0"), + Self::Rg1 => write!(f, "rg1"), + Self::Rg2 => write!(f, "rg2"), + Self::Rg3 => write!(f, "rg3"), + Self::Rg4 => write!(f, "rg4"), + Self::Rg5 => write!(f, "rg5"), + Self::Rg6 => write!(f, "rg6"), + Self::Rg7 => write!(f, "rg7"), + Self::Rg8 => write!(f, "rg8"), + Self::Rg9 => write!(f, "rg9"), + Self::Rga => write!(f, "rga"), + Self::Rgb => write!(f, "rgb"), + Self::Rgc => write!(f, "rgc"), + Self::Rgd => write!(f, "rgd"), + Self::Rge => write!(f, "rge"), + Self::Rgf => write!(f, "rgf"), + Self::Acc => write!(f, "acc"), + Self::Spr => write!(f, "spr"), + Self::Bpr => write!(f, "bpr"), + Self::Ret => write!(f, "ret"), + Self::Idr => write!(f, "idr"), + Self::Mmr => write!(f, "mmr"), + Self::Zero => write!(f, "zero"), + Self::NoReg => write!(f, "noreg"), + Self::Mar => write!(f, "mar"), + Self::Mdr => write!(f, "mdr"), + Self::Sts => write!(f, "sts"), + Self::Cir => write!(f, "cir"), + Self::Pcx => write!(f, "pcx"), } } } @@ -180,6 +182,19 @@ impl ITypeArgs { } } +impl Encode for ITypeArgs { + /// Encodes an I-type instruction from its fields. These must have some unused high-order + /// bits set to 0 else the bit shifting logic gets fucked. + fn encode(self, opcode: u8) -> u32 { + let opcode = u32::from(opcode); + let r1 = self.r1 as u32; + let dr = self.r2 as u32; + let immediate = u32::from(self.immediate); + + (opcode << 26) | (r1 << 21) | (dr << 16) | immediate + } +} + /// Used by instructions not using immediates (besides 5 bit shift values). #[derive(Debug, Clone, Copy)] pub struct RTypeArgs { @@ -213,6 +228,24 @@ impl RTypeArgs { } } +impl Encode for RTypeArgs { + /// Encodes an R-type instruction from its fields. These must have unused high-order + /// bits set to 0 else the bit shifting logic is fucked. + /// + /// # Arguments + /// + /// - `shamt`: The amount to shift value (used only in shift instructions, otherwise 0). + fn encode(self, opcode: u8) -> u32 { + let opcode = u32::from(opcode); + let sr1 = self.sr1 as u32; + let sr2 = self.sr2 as u32; + let dr = self.dr as u32; + let shamt = u32::from(self.shamt); + + (opcode << 26) | (sr1 << 21) | (sr2 << 16) | (dr << 11) | (shamt << 6) + } +} + /// TODO: Turn argument tuples into simple structs like `TwoReg`, `TwoRegAndOff` /// etc just to make code a little cleaner. #[derive(Debug, Clone, Copy)] @@ -273,7 +306,6 @@ pub enum Instruction { Halt = 0x24, } -#[expect(clippy::missing_const_for_fn)] impl Instruction { /// Returns the opcode of an instruction. /// @@ -288,42 +320,7 @@ impl Instruction { #[must_use] #[allow(unused)] pub fn encode(&self) -> u32 { - // match self { - // _ => todo!(), - // } - - // Note to Harry, I will finish this encode/decode shit in a few hours. - - 21 - } - - /// Encodes an R-type instruction from its fields. These must have unused high-order - /// bits set to 0 else the bit shifting logic is fucked. - /// - /// # Arguments - /// - /// - `shamt`: The amount to shift value (used only in shift instructions, otherwise 0). - #[must_use] - pub const fn encode_r_type(opcode: u8, sr1: u8, sr2: u8, dr: u8, shamt: u8) -> u32 { - let opcode = opcode as u32; - let sr1 = sr1 as u32; - let sr2 = sr2 as u32; - let dr = dr as u32; - let shamt = shamt as u32; - - (opcode << 26) | (sr1 << 21) | (sr2 << 16) | (dr << 11) | (shamt << 6) - } - - /// Encodes an I-type instruction from its fields. These must have some unused high-order - /// bits set to 0 else the bit shifting logic gets fucked. - #[must_use] - pub const fn encode_i_type(opcode: u8, sr1: u8, dr: u8, immediate: u16) -> u32 { - let opcode = opcode as u32; - let sr1 = sr1 as u32; - let dr = dr as u32; - let immediate = immediate as u32; - - (opcode << 26) | (sr1 << 21) | (dr << 16) | immediate + Encode::encode(*self, self.opcode()) } #[must_use] @@ -407,3 +404,5 @@ impl std::fmt::Display for Instruction { } } } + +mod encode; diff --git a/src/common/instructions/encode.rs b/src/common/instructions/encode.rs new file mode 100644 index 0000000..fae328c --- /dev/null +++ b/src/common/instructions/encode.rs @@ -0,0 +1,62 @@ +use crate::common::instructions::Register; + +use super::Instruction; + +/// Not to be used directly, just call [`Instruction::encode`]. +pub trait Encode { + fn encode(self, opcode: u8) -> u32; +} + +/// Encodes a zero argument instruction. +fn encode_no_args(opcode: u8) -> u32 { + let opcode = u32::from(opcode); + let sr1 = Register::NoReg as u32; + let sr2 = Register::NoReg as u32; + let dr = Register::NoReg as u32; + let shamt = 0; + + (opcode << 26) | (sr1 << 21) | (sr2 << 16) | (dr << 11) | (shamt << 6) +} + +/// Expands to a match statement that calls encode on instructions that implement [`Encode`]: +/// +/// # Usage +/// +/// ```rs +/// encode_instruction!(self, with_args: [...], no_args: [...], special: [...] ) +/// ``` +macro_rules! encode_instruction { + ($self:expr, with_args: [$($variant:ident),+ $(,)?], no_args: [$($no_arg_variant:ident),* $(,)?] $(, special: [$($special:pat => $body:expr),* $(,)?])?) => { + match $self { + $( + Instruction::$variant(args) => args.encode($self.opcode()), + )+ + $( + Instruction::$no_arg_variant => encode_no_args($self.opcode()), + )* + $($( + $special => $body, + )*)? + } + }; +} + +impl Encode for Instruction { + fn encode(self, _: u8) -> u32 { + encode_instruction!( + self, + with_args: [ + Mov, MovSigned, LoadByte, LoadByteSigned, LoadHalfword, + LoadHalfwordSigned, LoadWord, StoreByte, StoreHalfword, + StoreWord, LoadLowerImmediate, LoadUpperImmediate, Jump, + JumpEq, JumpNeq, JumpGt, JumpGe, JumpLt, JumpLe, Compare, + Add, Sub, Increment, Decrement, ShiftLeft, ShiftRight, + And, Or, Not, Xor, Nand, Nor, Xnor + ], + no_args: [Nop, IntReturn, Halt], + special: [ + Self::Interrupt(_) => todo!() + ] + ) + } +}