refactor 2 electric boogaloo
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
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 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.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;
|
||||
Reference in New Issue
Block a user