use crate::{instructions::encode::Encode, prelude::*}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Interrupt { Software(u8), } pub type Address = u32; impl Interrupt { const fn as_u8(self) -> u8 { match self { Self::Software(code) => code, } } } // TODO: This should be TryFrom. impl From for Interrupt { #[allow(unreachable_code)] fn from(_code: u8) -> Self { todo!("Implement this once a hardware interrupt convention is established."); // Self::Software(_code) } } /// Whether an [`Instruction`] is an I-type or R-type instruction. #[non_exhaustive] pub enum InstructionType { Register, Immediate, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Register { // general purpose registers Rg0, Rg1, Rg2, Rg3, Rg4, Rg5, Rg6, Rg7, Rg8, Rg9, Rga, Rgb, Rgc, Rgd, Rge, Rgf, // special purpose registers Acc, Spr, Bpr, Ret, Idr, Mmr, Zero, NoReg, // system registers - can't be written to by instructions. Mar, Mdr, Sts, Cir, Pcx, } impl Default for Register { fn default() -> Self { Self::NoReg } } impl TryFrom for Register { type Error = RegisterParseError; fn try_from(idx: u8) -> Result { if idx > 0x18 { return Err(RegisterParseError::InvalidIndex(idx)); } Ok(match idx { // System registers are not indexable in the reg file so they cannot be modified by instructions. 0x0 => Self::Rg0, 0x1 => Self::Rg1, 0x2 => Self::Rg2, 0x3 => Self::Rg3, 0x4 => Self::Rg4, 0x5 => Self::Rg5, 0x6 => Self::Rg6, 0x7 => Self::Rg7, 0x8 => Self::Rg8, 0x9 => Self::Rg9, 0xA => Self::Rga, 0xB => Self::Rgb, 0xC => Self::Rgc, 0xD => Self::Rgd, 0xE => Self::Rge, 0xF => Self::Rgf, 0x10 => Self::Acc, 0x11 => Self::Spr, 0x12 => Self::Bpr, 0x13 => Self::Ret, 0x14 => Self::Idr, 0x15 => Self::Mmr, 0x16 => Self::Zero, 0x17 => Self::NoReg, _ => unreachable!("This is already checked for in top `if` branch."), }) } } impl TryFrom<&str> for Register { type Error = RegisterParseError; fn try_from(value: &str) -> Result { match value.to_lowercase().as_str() { "rg0" => Ok(Self::Rg0), "rg1" => Ok(Self::Rg1), "rg2" => Ok(Self::Rg2), "rg3" => Ok(Self::Rg3), "rg4" => Ok(Self::Rg4), "rg5" => Ok(Self::Rg5), "rg6" => Ok(Self::Rg6), "rg7" => Ok(Self::Rg7), "rg8" => Ok(Self::Rg8), "rg9" => Ok(Self::Rg9), "rga" => Ok(Self::Rga), "rgb" => Ok(Self::Rgb), "rgc" => Ok(Self::Rgc), "rgd" => Ok(Self::Rgd), "rge" => Ok(Self::Rge), "rgf" => Ok(Self::Rgf), "acc" => Ok(Self::Acc), "spr" => Ok(Self::Spr), "bpr" => Ok(Self::Bpr), "ret" => Ok(Self::Ret), "idr" => Ok(Self::Idr), "mmr" => Ok(Self::Mmr), "zero" => Ok(Self::Zero), "null" => Ok(Self::NoReg), _ => Err(RegisterParseError::InvalidName(value.to_string())), } } } 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, "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"), } } } #[derive(Debug, Clone, Copy, Eq)] #[repr(u8)] #[non_exhaustive] /// A list of all current instructions in the DSA. /// /// # Note /// /// This is subject to change and is therefore marked non exhaustive. pub enum Instruction { // No-op Nop = 0x0, // Data transfer instructions Mov(args::RTypeArgs) = 0x1, MovSigned(args::RTypeArgs) = 0x2, LoadByte(args::ITypeArgs) = 0x3, LoadByteSigned(args::ITypeArgs) = 0x4, LoadHalfword(args::ITypeArgs) = 0x5, LoadHalfwordSigned(args::ITypeArgs) = 0x6, LoadWord(args::ITypeArgs) = 0x7, StoreByte(args::ITypeArgs) = 0x8, StoreHalfword(args::ITypeArgs) = 0x9, StoreWord(args::ITypeArgs) = 0xA, LoadLowerImmediate(args::ITypeArgs) = 0xB, LoadUpperImmediate(args::ITypeArgs) = 0xC, // Jump Instructions Jump(args::ITypeArgs) = 0xD, JumpEq(args::ITypeArgs) = 0xE, JumpNeq(args::ITypeArgs) = 0xF, JumpGt(args::ITypeArgs) = 0x10, JumpGe(args::ITypeArgs) = 0x11, JumpLt(args::ITypeArgs) = 0x12, JumpLe(args::ITypeArgs) = 0x13, // Comparison Compare(args::RTypeArgs) = 0x14, // Arithmetic Add(args::RTypeArgs) = 0x19, Sub(args::RTypeArgs) = 0x1A, Increment(args::RTypeArgs) = 0x15, Decrement(args::RTypeArgs) = 0x16, ShiftLeft(args::RTypeArgs) = 0x17, ShiftRight(args::RTypeArgs) = 0x18, // Logical And(args::RTypeArgs) = 0x1B, Or(args::RTypeArgs) = 0x1C, Not(args::RTypeArgs) = 0x1D, Xor(args::RTypeArgs) = 0x1E, Nand(args::RTypeArgs) = 0x1F, Nor(args::RTypeArgs) = 0x20, Xnor(args::RTypeArgs) = 0x21, // Misc Interrupt(Interrupt) = 0x22, IntReturn = 0x23, Halt = 0x24, } impl PartialEq for Instruction { fn eq(&self, other: &Self) -> bool { self.encode() == other.encode() } } impl Instruction { /// Returns the opcode of an instruction. /// /// # Notes /// /// The top two bits shall be 0, opcodes are 6-bits long. #[must_use] pub const fn opcode(&self) -> u8 { unsafe { *std::ptr::from_ref::(self).cast::() } } /// Encodes an [`Instruction`] into a word. #[must_use] pub fn encode(&self) -> u32 { Encode::encode(*self, self.opcode()) } /// Decodes an [`Instruction`] from a word `data`. pub fn decode(data: u32) -> Result { data.try_into() } /// Returns the mnemonic for a given [`Instruction`]. #[must_use] pub const fn mnemonic(self) -> &'static str { match self { Self::Add(_) => "add", Self::Sub(_) => "sub", Self::Increment(_) => "inc", Self::Decrement(_) => "dec", Self::Compare(_) => "cmp", Self::Halt => "hlt", Self::And(_) => "and", Self::IntReturn => "intr", Self::Interrupt(_) => "int", Self::Jump(_) => "jmp", Self::JumpEq(_) => "jeq", Self::JumpNeq(_) => "jneq", Self::JumpGt(_) => "jgt", Self::JumpGe(_) => "jge", Self::JumpLt(_) => "jlt", Self::JumpLe(_) => "jle", Self::Mov(_) => "mov", Self::MovSigned(_) => "movs", Self::LoadByte(_) => "ldb", Self::LoadByteSigned(_) => "ldbs", Self::LoadHalfword(_) => "ldh", Self::LoadHalfwordSigned(_) => "ldhs", Self::LoadWord(_) => "ldw", Self::StoreByte(_) => "stb", Self::StoreHalfword(_) => "sth", Self::StoreWord(_) => "stw", Self::LoadLowerImmediate(_) => "lli", Self::LoadUpperImmediate(_) => "lui", Self::ShiftLeft(_) => "shl", Self::ShiftRight(_) => "shr", Self::Or(_) => "or", Self::Not(_) => "not", Self::Nop => "nop", Self::Xor(_) => "xor", Self::Nand(_) => "nand", Self::Nor(_) => "nor", Self::Xnor(_) => "xnor", } } /// Returns the [`InstructionType`] for the given [`Instruction`]. #[must_use] pub const fn instruction_type(self) -> InstructionType { Self::instruction_type_from_opcode(self.opcode()) } /// Returns the [`InstructionType`] for the given `opcode`. #[must_use] pub const fn instruction_type_from_opcode(opcode: u8) -> InstructionType { match opcode { 0x3..=0x13 => InstructionType::Immediate, _ => InstructionType::Register, } } } // Instruction decoding logic goes here. impl std::fmt::Display for Instruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.mnemonic())?; match self { Self::Mov(args) | Self::MovSigned(args) => { write!(f, " {}, {}", args.sr1, args.dr) } Self::LoadByte(args) | Self::LoadByteSigned(args) | Self::LoadHalfword(args) | Self::LoadHalfwordSigned(args) | Self::LoadWord(args) | Self::StoreByte(args) | Self::StoreHalfword(args) | Self::StoreWord(args) => { write!(f, " {:x}({}), {}", args.immediate, args.r1, args.r2) } Self::Jump(args) | Self::JumpEq(args) | Self::JumpNeq(args) | Self::JumpGt(args) | Self::JumpGe(args) | Self::JumpLt(args) | Self::JumpLe(args) => { write!(f, " ({:x}){}", args.immediate, args.r1) } Self::LoadLowerImmediate(args) | Self::LoadUpperImmediate(args) => { write!(f, " {}, {}, {}", args.immediate, args.r1, args.r2) } Self::Compare(args) | Self::Not(args) => { write!(f, " {}, {}", args.sr1, args.sr2) } Self::Add(args) | Self::Sub(args) | Self::Xor(args) | Self::Nand(args) | Self::Nor(args) | Self::Xnor(args) | Self::ShiftLeft(args) | Self::ShiftRight(args) | Self::And(args) | Self::Or(args) => { write!(f, " {}, {}, {}", args.sr1, args.sr2, args.dr) } Self::Increment(a) | Self::Decrement(a) => write!(f, " {}", a.dr), Self::Interrupt(a) => write!(f, " {}", a.as_u8()), _ => Ok(()), } } } impl TryFrom for Instruction { type Error = InstructionDecodeError; /// 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 = ((data >> 26) & 0x3F) as u8; match opcode { 0x0 => Ok(Self::Nop), 0x1 => Ok(Self::Mov(RTypeArgs::try_from(data)?)), 0x2 => Ok(Self::MovSigned(RTypeArgs::try_from(data)?)), 0x3 => Ok(Self::LoadByte(ITypeArgs::try_from(data)?)), 0x4 => Ok(Self::LoadByteSigned(ITypeArgs::try_from(data)?)), 0x5 => Ok(Self::LoadHalfword(ITypeArgs::try_from(data)?)), 0x6 => Ok(Self::LoadHalfwordSigned(ITypeArgs::try_from(data)?)), 0x7 => Ok(Self::LoadWord(ITypeArgs::try_from(data)?)), 0x8 => Ok(Self::StoreByte(ITypeArgs::try_from(data)?)), 0x9 => Ok(Self::StoreHalfword(ITypeArgs::try_from(data)?)), 0xA => Ok(Self::StoreWord(ITypeArgs::try_from(data)?)), 0xB => Ok(Self::LoadLowerImmediate(ITypeArgs::try_from(data)?)), 0xC => Ok(Self::LoadUpperImmediate(ITypeArgs::try_from(data)?)), 0xD => Ok(Self::Jump(ITypeArgs::try_from(data)?)), 0xE => Ok(Self::JumpEq(ITypeArgs::try_from(data)?)), 0xF => Ok(Self::JumpNeq(ITypeArgs::try_from(data)?)), 0x10 => Ok(Self::JumpGt(ITypeArgs::try_from(data)?)), 0x11 => Ok(Self::JumpGe(ITypeArgs::try_from(data)?)), 0x12 => Ok(Self::JumpLt(ITypeArgs::try_from(data)?)), 0x13 => Ok(Self::JumpLe(ITypeArgs::try_from(data)?)), 0x14 => Ok(Self::Compare(RTypeArgs::try_from(data)?)), 0x15 => Ok(Self::Increment(RTypeArgs::try_from(data)?)), 0x16 => Ok(Self::Decrement(RTypeArgs::try_from(data)?)), 0x17 => Ok(Self::ShiftLeft(RTypeArgs::try_from(data)?)), 0x18 => Ok(Self::ShiftRight(RTypeArgs::try_from(data)?)), 0x19 => Ok(Self::Add(RTypeArgs::try_from(data)?)), 0x1A => Ok(Self::Sub(RTypeArgs::try_from(data)?)), 0x1B => Ok(Self::And(RTypeArgs::try_from(data)?)), 0x1C => Ok(Self::Or(RTypeArgs::try_from(data)?)), 0x1D => Ok(Self::Not(RTypeArgs::try_from(data)?)), 0x1E => Ok(Self::Xor(RTypeArgs::try_from(data)?)), 0x1F => Ok(Self::Nand(RTypeArgs::try_from(data)?)), 0x20 => Ok(Self::Nor(RTypeArgs::try_from(data)?)), 0x21 => Ok(Self::Xnor(RTypeArgs::try_from(data)?)), 0x22 => Ok(Self::Interrupt(Interrupt::from((data & 0xFF) as u8))), 0x23 => Ok(Self::IntReturn), 0x24 => Ok(Self::Halt), _ => Err(InstructionDecodeError::InvalidOpcode(opcode)), } } } pub mod args; mod encode; pub mod errors; #[cfg(test)] mod tests;