//! Various types of arguments that instructions can take, alongside encoding and decoding logic. use crate::common::instructions::{RegisterParseError, encode::Encode}; use super::Register; /// A list of errors that can be returned when decoding instruction arguments. #[derive(Debug)] pub enum ArgsDecodeError { /// The register was not valid. InvalidRegister(u8), } impl From for ArgsDecodeError { fn from(value: RegisterParseError) -> Self { match value { RegisterParseError::InvalidIndex(idx) => Self::InvalidRegister(idx), } } } impl std::fmt::Display for ArgsDecodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InvalidRegister(idx) => { write!(f, "invalid register index, got {idx:x}")?; } } Ok(()) } } impl std::error::Error for ArgsDecodeError {} #[derive(Debug, Clone, Copy)] /// Used by instructions with 2 registers and an immediate argument. pub struct ITypeArgs { pub immediate: u16, pub r1: Register, /// May not actually be used by some instructions taking an immediate e.g. LUI. This is solved by making the constructor take Options. pub r2: Register, } impl ITypeArgs { #[must_use] /// Creates a new [`ITypeArgs`]. If r1 or r2 is unset, they will be replaced with [`Register::NoReg`]. pub fn new(immediate: u16, r1: Option, r2: Option) -> Self { let r1 = r1.unwrap_or_default(); let r2 = r2.unwrap_or_default(); Self { immediate, r1, r2 } } } 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 } } 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 immediate = data as u16; Ok(Self { immediate, r1, r2 }) } } /// Used by instructions not using immediates (besides 5 bit shift values). #[derive(Debug, Clone, Copy)] pub struct RTypeArgs { pub sr1: Register, pub sr2: Register, pub dr: Register, /// 5 bit shift amount. pub shamt: u8, } impl RTypeArgs { #[must_use] /// Creates a new [`RTypeArgs`]. If any registers are unset, they will be replaced with [`Register::NoReg`]. If `shamt` is unset, it will be set to 0. pub fn new( sr1: Option, sr2: Option, dr: Option, shamt: Option, ) -> Self { let sr1 = sr1.unwrap_or_default(); let shamt = shamt.unwrap_or_default(); let sr2 = sr2.unwrap_or_default(); let dr = dr.unwrap_or_default(); Self { sr1, sr2, dr, shamt, } } } 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) } } 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_reg = sr1.try_into()?; let sr2_reg = sr2.try_into()?; let dr_reg = dr.try_into()?; Ok(Self { sr1: sr1_reg, sr2: sr2_reg, dr: dr_reg, shamt, }) } }