453 lines
14 KiB
Rust
453 lines
14 KiB
Rust
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<u8> 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<u8> for Register {
|
|
type Error = RegisterParseError;
|
|
|
|
fn try_from(idx: u8) -> Result<Self, Self::Error> {
|
|
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<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),
|
|
_ => 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>(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",
|
|
}
|
|
}
|
|
|
|
/// 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<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),
|
|
_ => Err(InstructionDecodeError::InvalidOpcode(opcode)),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod args;
|
|
mod encode;
|
|
pub mod errors;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|