Files
damn_simple_architecture/common/src/instructions.rs
T

516 lines
16 KiB
Rust

use crate::{instructions::encode::Encode, prelude::*};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Interrupt {
Software(u8),
Breakpoint,
HardFault,
}
pub type Address = u32;
impl Interrupt {
const fn as_u8(self) -> u8 {
match self {
Self::Breakpoint => 0,
Self::HardFault => 1,
Self::Software(code) => code,
}
}
}
// TODO: This should be TryFrom.
impl From<u8> for Interrupt {
#[allow(unreachable_code)]
fn from(code: u8) -> Self {
match code {
0 => Self::Breakpoint,
1 => Self::HardFault,
_ => 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 Register {
// this is here so clippy shuts up about the must_use tag.
#[allow(clippy::must_use_candidate)]
pub fn general() -> Vec<Self> {
vec![
Self::Rg0,
Self::Rg1,
Self::Rg2,
Self::Rg3,
Self::Rg4,
Self::Rg5,
Self::Rg6,
Self::Rg7,
Self::Rg8,
Self::Rg9,
Self::Rga,
Self::Rgb,
Self::Rgc,
Self::Rgd,
Self::Rge,
Self::Rgf,
]
}
}
impl Default for Register {
fn default() -> Self {
Self::NoReg
}
}
impl TryFrom<u8> for Register {
type Error = RegisterParseError;
fn try_from(idx: u8) -> Result<Self, Self::Error> {
if idx > 0x1C {
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,
0x18 => Self::Mar,
0x19 => Self::Mdr,
0x1A => Self::Sts,
0x1B => Self::Cir,
0x1C => Self::Pcx,
_ => unreachable!("This is already checked for in top `if` branch."),
})
}
}
impl TryFrom<&str> for Register {
type Error = RegisterParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
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),
"pcx" => Ok(Self::Pcx),
_ => 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,
// Immediate Arithmetic
AddImmediate(args::ITypeArgs) = 0x25,
SubImmediate(args::ITypeArgs) = 0x26,
// Fake Instructions
Data(u32) = 0x3E,
Segment(u32) = 0x3F,
}
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>(self).cast::<u8>() }
}
/// 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<Self, InstructionDecodeError> {
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",
Self::Data(_) => "data",
Self::AddImmediate(_) => "addi",
Self::SubImmediate(_) => "subi",
Self::Segment(_) => "[SEGMENT]",
}
}
/// 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.r1, args.immediate, args.immediate, 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, " 0x{:x}/{}({})", args.immediate, args.immediate, args.r1)
}
Self::LoadLowerImmediate(args) | Self::LoadUpperImmediate(args) => {
write!(f, " 0x{:x}, {}, {}", 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::AddImmediate(args) | Self::SubImmediate(args) => {
write!(f, " {}, {}, {}", args.r1, args.immediate, args.r2)
}
Self::Increment(a) | Self::Decrement(a) => write!(f, " {}", a.sr1),
Self::Interrupt(a) => write!(f, " {}", a.as_u8()),
Self::Data(a) => write!(f, " {a}"),
Self::Segment(x) => write!(f, " [SEGMENT {x}]"),
_ => Ok(()),
}
}
}
impl TryFrom<u32> for Instruction {
type Error = InstructionDecodeError;
/// Instruction decoding can be using using [`Instruction::try_from`]
fn try_from(data: u32) -> Result<Self, Self::Error> {
// 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),
0x25 => Ok(Self::AddImmediate(ITypeArgs::try_from(data)?)),
0x26 => Ok(Self::SubImmediate(ITypeArgs::try_from(data)?)),
0x3F => Ok(Self::Segment(u32::from(data as u8))),
_ => Err(InstructionDecodeError::InvalidOpcode(opcode)),
}
}
}
pub mod args;
mod encode;
pub mod errors;
#[cfg(test)]
mod tests;