common: instruction encoding via macro and trait ugly hack works

This commit is contained in:
2025-06-15 13:56:45 +01:00
parent 4e1d4d784f
commit 977a621d5f
2 changed files with 127 additions and 66 deletions
+65 -66
View File
@@ -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<u8> 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;
+62
View File
@@ -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!()
]
)
}
}