progress: new assembler works, instruction set fully implemented with call/ret and push/pop, bf.dsa works which means the dsa assembly language works reliably. still a few bugs to fix. might be able to squeeze out a tiny bit more performance
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
use crate::{instructions::Instruction, register::Register};
|
||||
|
||||
impl Instruction {
|
||||
#[inline]
|
||||
pub fn opcode(self) -> u8 {
|
||||
((self.0 >> 26) & 0x3F) as u8
|
||||
}
|
||||
|
||||
// Src shared between R type and I type
|
||||
#[inline]
|
||||
pub unsafe fn src1_uc(self) -> Register {
|
||||
unsafe { Register::from_u8_unchecked(((self.0 >> 21) & 0x1F) as u8) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn src1_checked(self) -> Register {
|
||||
Register::from_u8(((self.0 >> 21) & 0x1F) as u8).unwrap_or(Register::Null)
|
||||
}
|
||||
|
||||
// Dest shared between R type and I type
|
||||
#[inline]
|
||||
pub unsafe fn dest_uc(self) -> Register {
|
||||
unsafe { Register::from_u8_unchecked(((self.0 >> 16) & 0x1F) as u8) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dest_checked(self) -> Register {
|
||||
Register::from_u8(((self.0 >> 16) & 0x1F) as u8).unwrap_or(Register::Null)
|
||||
}
|
||||
|
||||
// Secondary Src for R type only
|
||||
#[inline]
|
||||
pub unsafe fn src2_uc(self) -> Register {
|
||||
unsafe { Register::from_u8_unchecked(((self.0 >> 11) & 0x1F) as u8) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn src2_checked(self) -> Register {
|
||||
Register::from_u8(((self.0 >> 11) & 0x1F) as u8).unwrap_or(Register::Null)
|
||||
}
|
||||
|
||||
// Misc/Conditional reg for R type only
|
||||
#[inline]
|
||||
pub unsafe fn misc_uc(self) -> Register {
|
||||
unsafe { Register::from_u8_unchecked(((self.0 >> 6) & 0x1F) as u8) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn misc_checked(self) -> Register {
|
||||
Register::from_u8(((self.0 >> 6) & 0x1F) as u8).unwrap_or(Register::Null)
|
||||
}
|
||||
|
||||
// Shift amount / misc data for R type only
|
||||
#[inline]
|
||||
pub fn shamt(self) -> u8 {
|
||||
(self.0 & 0x3F) as u8
|
||||
}
|
||||
|
||||
// 16 Bit immediate for I type only
|
||||
#[inline]
|
||||
pub fn imm16(self) -> u16 {
|
||||
self.0 as u16
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
instructions::{Instruction, Opcode},
|
||||
register::Register,
|
||||
};
|
||||
|
||||
impl From<u32> for Instruction {
|
||||
#[inline]
|
||||
fn from(word: u32) -> Self {
|
||||
Self(word)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Instruction {
|
||||
type Target = u32;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Instruction {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
// ---- R Type Builders ----
|
||||
|
||||
#[inline]
|
||||
fn build_r(
|
||||
opcode: Opcode,
|
||||
src1: Register,
|
||||
dest: Register,
|
||||
src2: Register,
|
||||
misc: Register,
|
||||
shamt: u8,
|
||||
) -> Self {
|
||||
Self(
|
||||
((opcode as u32) << 26)
|
||||
| ((src1 as u32) << 21)
|
||||
| ((dest as u32) << 16)
|
||||
| ((src2 as u32) << 11)
|
||||
| ((misc as u32) << 6)
|
||||
| ((shamt as u32) & 0x3F),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn build_i(opcode: Opcode, src: Register, dest: Register, imm: u16) -> Self {
|
||||
Self(((opcode as u32) << 26) | ((src as u32) << 21) | ((dest as u32) << 16) | (imm as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn build_noarg(opcode: Opcode) -> Self {
|
||||
Self((opcode as u32) << 26)
|
||||
}
|
||||
|
||||
/// No operation.
|
||||
pub fn nop() -> Self {
|
||||
Self::build_noarg(Opcode::Nop)
|
||||
}
|
||||
|
||||
// ---- Move ----
|
||||
|
||||
/// Move a value from `src` to `dest`.
|
||||
pub fn mov(src: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Mov, src, dest, Register::Zero, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Conditional move: if `cmp` is true then move `src` to `dest`.
|
||||
pub fn cmov(src: Register, dest: Register, cmp: Register) -> Self {
|
||||
Self::build_r(Opcode::CMov, src, dest, cmp, Register::Zero, 0)
|
||||
}
|
||||
|
||||
// ---- Load ----
|
||||
|
||||
/// Load byte from memory at `src + offset` into `dest`.
|
||||
pub fn ldb(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Ldb, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Load signed byte (sign‑extended) from memory at `src + offset` into `dest`.
|
||||
pub fn ldbs(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Ldbs, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Load halfword from memory at `src + offset` into `dest`.
|
||||
pub fn ldh(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Ldh, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Load signed halfword (sign‑extended) from memory at `src + offset` into `dest`.
|
||||
pub fn ldhs(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Ldhs, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Load word from memory at `src + offset` into `dest`.
|
||||
pub fn ldw(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Ldw, src, dest, offset)
|
||||
}
|
||||
|
||||
// ---- Store ----
|
||||
|
||||
/// Store byte from `src` to memory at `dest + offset`.
|
||||
pub fn stb(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Stb, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Store halfword from `src` to memory at `dest + offset`.
|
||||
pub fn sth(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Sth, src, dest, offset)
|
||||
}
|
||||
|
||||
/// Store word from `src` to memory at `dest + offset`.
|
||||
pub fn stw(src: Register, dest: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Stw, src, dest, offset)
|
||||
}
|
||||
|
||||
// ---- Load Immediate ----
|
||||
|
||||
/// Load lower 16 bits of an immediate into `dest`.
|
||||
pub fn lli(dest: Register, imm: u16) -> Self {
|
||||
Self::build_i(Opcode::Lli, Register::Zero, dest, imm)
|
||||
}
|
||||
|
||||
/// Load upper 16 bits of an immediate into `dest`.
|
||||
pub fn lui(dest: Register, imm: u16) -> Self {
|
||||
Self::build_i(Opcode::Lui, Register::Zero, dest, imm)
|
||||
}
|
||||
|
||||
/// Load a full 32‑bit immediate using an `lli` + `lui` pair.
|
||||
pub fn load_imm32(dest: Register, imm: u32) -> [Self; 2] {
|
||||
[
|
||||
Self::lli(dest, imm as u16),
|
||||
Self::lui(dest, (imm >> 16) as u16),
|
||||
]
|
||||
}
|
||||
|
||||
// ---- Comparison ----
|
||||
|
||||
/// Set `dest` to 1 if `sr1 == sr2`, else 0.
|
||||
pub fn ieq(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Ieq, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Set `dest` to 1 if `sr1 != sr2`, else 0.
|
||||
pub fn ine(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Ine, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Set `dest` to 1 if `sr1 < sr2`, else 0.
|
||||
pub fn ilt(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Ilt, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Set `dest` to 1 if `sr1 <= sr2`, else 0.
|
||||
pub fn ile(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Ile, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Set `dest` to 1 if `sr1 > sr2`, else 0.
|
||||
pub fn igt(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Igt, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Set `dest` to 1 if `sr1 >= sr2`, else 0.
|
||||
pub fn ige(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Ige, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
// ---- Jump ----
|
||||
|
||||
/// Unconditional jump to address in `addr` with offset.
|
||||
pub fn jmp(addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Jmp, Register::Zero, addr, offset)
|
||||
}
|
||||
|
||||
/// Jump if zero flag set (`jez`).
|
||||
pub fn jez(cmp: Register, addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Jez, cmp, addr, offset)
|
||||
}
|
||||
|
||||
/// Jump if zero flag clear (`jnz`).
|
||||
pub fn jnz(cmp: Register, addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Jnz, cmp, addr, offset)
|
||||
}
|
||||
|
||||
/// Jump if carry set (`jic`).
|
||||
pub fn jic(addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Jic, Register::Zero, addr, offset)
|
||||
}
|
||||
|
||||
/// Jump if carry clear (`jnc`).
|
||||
pub fn jnc(addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Jnc, Register::Zero, addr, offset)
|
||||
}
|
||||
|
||||
// ---- Bitwise ----
|
||||
|
||||
/// Bitwise AND of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn and(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::And, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise NAND of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn nand(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Nand, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise OR of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn or(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Or, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise NOR of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn nor(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Nor, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise XOR of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn xor(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Xor, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise XNOR of `sr1` and `sr2`, result in `dest`.
|
||||
pub fn xnor(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Xnor, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Bitwise NOT of `src`, result in `dest`.
|
||||
pub fn not(src: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Not, src, dest, Register::Zero, Register::Zero, 0)
|
||||
}
|
||||
|
||||
// ---- Arithmetic ----
|
||||
|
||||
/// Add `sr1` and `sr2`, store result in `dest`.
|
||||
pub fn add(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Add, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Subtract `sr2` from `sr1`, store result in `dest`.
|
||||
pub fn sub(sr1: Register, sr2: Register, dest: Register) -> Self {
|
||||
Self::build_r(Opcode::Sub, sr1, dest, sr2, Register::Zero, 0)
|
||||
}
|
||||
|
||||
/// Shift left logical: shift `src` by (`rshamt` + `ishamt`) bits into `dest`.
|
||||
pub fn shl(src: Register, rshamt: Register, dest: Register, ishamt: u8) -> Self {
|
||||
debug_assert!(ishamt < 64);
|
||||
Self::build_r(Opcode::Shl, src, dest, rshamt, Register::Zero, ishamt)
|
||||
}
|
||||
|
||||
/// Shift right logical: shift `src` by (`rshamt` + `ishamt`) bits into `dest`.
|
||||
pub fn shr(src: Register, rshamt: Register, dest: Register, ishamt: u8) -> Self {
|
||||
debug_assert!(ishamt < 64);
|
||||
Self::build_r(Opcode::Shr, src, dest, rshamt, Register::Zero, ishamt)
|
||||
}
|
||||
|
||||
/// Add immediate `imm` to `sr1`, store result in `dest`.
|
||||
pub fn addi(sr1: Register, dest: Register, imm: u16) -> Self {
|
||||
Self::build_i(Opcode::Addi, sr1, dest, imm)
|
||||
}
|
||||
|
||||
/// Subtract immediate `imm` from `sr1`, store result in `dest`.
|
||||
pub fn subi(sr1: Register, dest: Register, imm: u16) -> Self {
|
||||
Self::build_i(Opcode::Subi, sr1, dest, imm)
|
||||
}
|
||||
|
||||
// ---- Util -----
|
||||
pub fn call(addr: Register, offset: u16) -> Self {
|
||||
Self::build_i(Opcode::Call, Register::Zero, addr, offset)
|
||||
}
|
||||
|
||||
pub fn ret() -> Self {
|
||||
Self::build_noarg(Opcode::Ret)
|
||||
}
|
||||
|
||||
pub fn push(reg: Register) -> Self {
|
||||
Self::build_r(
|
||||
Opcode::Push,
|
||||
reg,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pop(reg: Register) -> Self {
|
||||
Self::build_r(
|
||||
Opcode::Pop,
|
||||
Register::Zero,
|
||||
reg,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
// ---- System ----
|
||||
|
||||
/// Trigger an interrupt with the given 6‑bit code.
|
||||
pub fn int(code: u8) -> Self {
|
||||
debug_assert!(code < 64);
|
||||
Self::build_i(Opcode::Int, Register::Zero, Register::Zero, code as u16)
|
||||
}
|
||||
|
||||
/// Return from interrupt.
|
||||
pub fn irt() -> Self {
|
||||
Self::build_noarg(Opcode::IRet)
|
||||
}
|
||||
|
||||
/// Halt execution.
|
||||
pub fn hlt() -> Self {
|
||||
Self::build_r(
|
||||
Opcode::Hlt,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
Register::Zero,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Raw data (not an instruction)
|
||||
pub fn data(value: u32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
pub mod decode;
|
||||
pub mod encode;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Instruction(pub u32);
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Opcode {
|
||||
Nop = 0,
|
||||
|
||||
// move
|
||||
Mov = 1,
|
||||
CMov = 2,
|
||||
|
||||
// load
|
||||
Ldb = 3,
|
||||
Ldbs = 4,
|
||||
Ldh = 5,
|
||||
Ldhs = 6,
|
||||
Ldw = 7,
|
||||
|
||||
// store
|
||||
Stb = 8,
|
||||
Sth = 9,
|
||||
Stw = 10,
|
||||
|
||||
// load immediate
|
||||
Lli = 11,
|
||||
Lui = 12,
|
||||
|
||||
// comparison
|
||||
Ieq = 13,
|
||||
Ine = 14,
|
||||
Ilt = 15,
|
||||
Ile = 16,
|
||||
Igt = 17,
|
||||
Ige = 18,
|
||||
|
||||
// jump
|
||||
Jmp = 19,
|
||||
Jez = 20,
|
||||
Jnz = 21,
|
||||
Jic = 22,
|
||||
Jnc = 23,
|
||||
|
||||
// bitwise
|
||||
And = 24,
|
||||
Nand = 25,
|
||||
Or = 26,
|
||||
Nor = 27,
|
||||
Xor = 28,
|
||||
Xnor = 29,
|
||||
Not = 30,
|
||||
|
||||
// arithmetic
|
||||
Add = 31,
|
||||
Sub = 32,
|
||||
Shl = 33,
|
||||
Shr = 34,
|
||||
Addi = 35,
|
||||
Subi = 36,
|
||||
|
||||
// utility
|
||||
Push = 37,
|
||||
Pop = 38,
|
||||
Call = 39,
|
||||
Ret = 40,
|
||||
|
||||
// system
|
||||
Int = 41,
|
||||
IRet = 42,
|
||||
Hlt = 43,
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
#[inline]
|
||||
pub fn from_u8(val: u8) -> Option<Self> {
|
||||
if val <= Self::Hlt as u8 {
|
||||
Some(unsafe { std::mem::transmute(val) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Debug for Instruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let opcode = Opcode::from_u8(self.opcode());
|
||||
|
||||
match opcode {
|
||||
None => write!(f, "Instruction(INVALID OPCODE {:#04x})", self.opcode()),
|
||||
|
||||
Some(op @ (Opcode::Nop | Opcode::Hlt | Opcode::Ret | Opcode::IRet)) => {
|
||||
write!(f, "{op:?}")
|
||||
}
|
||||
|
||||
Some(op @ Opcode::Push) => {
|
||||
write!(f, "{op:?} {src1:?}", op = op, src1 = self.src1_checked())
|
||||
}
|
||||
|
||||
Some(op @ Opcode::Pop) => {
|
||||
write!(f, "{op:?} {dest:?}", op = op, dest = self.dest_checked())
|
||||
}
|
||||
|
||||
// I-type: single reg + immediate
|
||||
Some(op @ (Opcode::Lli | Opcode::Lui)) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {dest:?}, {imm:#06x}",
|
||||
op = op,
|
||||
dest = self.dest_checked(),
|
||||
imm = self.imm16()
|
||||
)
|
||||
}
|
||||
|
||||
// I-type: no reg, immediate only
|
||||
Some(op @ Opcode::Int) => {
|
||||
write!(f, "{op:?} {imm:#06x}", op = op, imm = self.imm16())
|
||||
}
|
||||
|
||||
// I-type: src + dest + immediate
|
||||
Some(
|
||||
op @ (Opcode::Ldb
|
||||
| Opcode::Ldbs
|
||||
| Opcode::Ldh
|
||||
| Opcode::Ldhs
|
||||
| Opcode::Ldw
|
||||
| Opcode::Stb
|
||||
| Opcode::Sth
|
||||
| Opcode::Stw
|
||||
| Opcode::Addi
|
||||
| Opcode::Subi),
|
||||
) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {src:?}, {dest:?}, {imm:#06x}",
|
||||
op = op,
|
||||
src = self.src1_checked(),
|
||||
dest = self.dest_checked(),
|
||||
imm = self.imm16()
|
||||
)
|
||||
}
|
||||
|
||||
// I-type: cmp + addr + immediate (conditional jumps)
|
||||
Some(op @ (Opcode::Jez | Opcode::Jnz)) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {cmp:?}, {addr:?}, {imm:#06x}",
|
||||
op = op,
|
||||
cmp = self.src1_checked(),
|
||||
addr = self.dest_checked(),
|
||||
imm = self.imm16()
|
||||
)
|
||||
}
|
||||
|
||||
// I-type: addr + immediate only (unconditional/carry jumps)
|
||||
Some(op @ (Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc)) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {addr:?}, {imm:#06x}",
|
||||
op = op,
|
||||
addr = self.dest_checked(),
|
||||
imm = self.imm16()
|
||||
)
|
||||
}
|
||||
|
||||
// R-type: src + dest (two reg)
|
||||
Some(op @ (Opcode::Mov | Opcode::Not)) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {src:?}, {dest:?}",
|
||||
op = op,
|
||||
src = self.src1_checked(),
|
||||
dest = self.dest_checked()
|
||||
)
|
||||
}
|
||||
|
||||
// R-type: src + dest + cmp (cmov)
|
||||
Some(op @ Opcode::CMov) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {src:?}, {dest:?}, {cmp:?}",
|
||||
op = op,
|
||||
src = self.src1_checked(),
|
||||
dest = self.dest_checked(),
|
||||
cmp = self.src2_checked()
|
||||
)
|
||||
}
|
||||
|
||||
// R-type: sr1 + sr2 + dest (arithmetic/bitwise/comparison)
|
||||
Some(
|
||||
op @ (Opcode::Add
|
||||
| Opcode::Sub
|
||||
| Opcode::And
|
||||
| Opcode::Nand
|
||||
| Opcode::Or
|
||||
| Opcode::Nor
|
||||
| Opcode::Xor
|
||||
| Opcode::Xnor
|
||||
| Opcode::Ieq
|
||||
| Opcode::Ine
|
||||
| Opcode::Ilt
|
||||
| Opcode::Ile
|
||||
| Opcode::Igt
|
||||
| Opcode::Ige),
|
||||
) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {sr1:?}, {sr2:?}, {dest:?}",
|
||||
op = op,
|
||||
sr1 = self.src1_checked(),
|
||||
sr2 = self.src2_checked(),
|
||||
dest = self.dest_checked()
|
||||
)
|
||||
}
|
||||
|
||||
// R-type: src + rshamt + dest + ishamt
|
||||
Some(op @ (Opcode::Shl | Opcode::Shr)) => {
|
||||
write!(
|
||||
f,
|
||||
"{op:?} {src:?}, {dest:?}, r:{rshamt:?} + i:{ishamt}",
|
||||
op = op,
|
||||
src = self.src1_checked(),
|
||||
dest = self.dest_checked(),
|
||||
rshamt = self.src2_checked(),
|
||||
ishamt = self.shamt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
pub mod instructions;
|
||||
pub mod register;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::instructions::Instruction;
|
||||
pub use crate::register::Register;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
pub enum Register {
|
||||
// general purpose
|
||||
Rg0 = 0,
|
||||
Rg1 = 1,
|
||||
Rg2 = 2,
|
||||
Rg3 = 3,
|
||||
Rg4 = 4,
|
||||
Rg5 = 5,
|
||||
Rg6 = 6,
|
||||
Rg7 = 7,
|
||||
Rg8 = 8,
|
||||
Rg9 = 9,
|
||||
Rga = 10,
|
||||
Rgb = 11,
|
||||
Rgc = 12,
|
||||
Rgd = 13,
|
||||
Rge = 14,
|
||||
Rgf = 15,
|
||||
|
||||
// special purpose
|
||||
Zero = 16,
|
||||
Acc = 17,
|
||||
Spr = 18,
|
||||
Bpr = 19,
|
||||
Ret = 20,
|
||||
Idr = 21,
|
||||
Mmr = 22,
|
||||
|
||||
// system - read only
|
||||
Mar = 23,
|
||||
Mdr = 24,
|
||||
Sts = 25,
|
||||
Cir = 26,
|
||||
Pcx = 27,
|
||||
|
||||
#[default]
|
||||
Null = 28,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub unsafe fn from_u8_unchecked(idx: u8) -> Self {
|
||||
debug_assert!(idx <= Self::Null as u8);
|
||||
unsafe { std::mem::transmute(idx) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_u8(idx: u8) -> Result<Self, ()> {
|
||||
if idx > 28 {
|
||||
return Err(());
|
||||
}
|
||||
Ok(unsafe { Self::from_u8_unchecked(idx) })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Register {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
// general purpose
|
||||
Self::Rg0 => "Rg0",
|
||||
Self::Rg1 => "Rg1",
|
||||
Self::Rg2 => "Rg2",
|
||||
Self::Rg3 => "Rg3",
|
||||
Self::Rg4 => "Rg4",
|
||||
Self::Rg5 => "Rg5",
|
||||
Self::Rg6 => "Rg6",
|
||||
Self::Rg7 => "Rg7",
|
||||
Self::Rg8 => "Rg8",
|
||||
Self::Rg9 => "Rg9",
|
||||
Self::Rga => "Rga",
|
||||
Self::Rgb => "Rgb",
|
||||
Self::Rgc => "Rgc",
|
||||
Self::Rgd => "Rgd",
|
||||
Self::Rge => "Rge",
|
||||
Self::Rgf => "Rgf",
|
||||
|
||||
// special purpose
|
||||
Self::Zero => "Zero",
|
||||
Self::Acc => "Acc",
|
||||
Self::Spr => "Spr",
|
||||
Self::Bpr => "Bpr",
|
||||
Self::Ret => "Ret",
|
||||
Self::Idr => "Idr",
|
||||
Self::Mmr => "Mmr",
|
||||
|
||||
// system - read only
|
||||
Self::Mar => "Mar",
|
||||
Self::Mdr => "Mdr",
|
||||
Self::Sts => "Sts",
|
||||
Self::Cir => "Cir",
|
||||
Self::Pcx => "Pcx",
|
||||
|
||||
Self::Null => "Null",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Register {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
// general purpose
|
||||
"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),
|
||||
|
||||
// special purpose
|
||||
"zero" => Ok(Self::Zero),
|
||||
"acc" => Ok(Self::Acc),
|
||||
"spr" => Ok(Self::Spr),
|
||||
"bpr" => Ok(Self::Bpr),
|
||||
// "ret" => Ok(Self::Ret),
|
||||
"idr" => Ok(Self::Idr),
|
||||
"mmr" => Ok(Self::Mmr),
|
||||
|
||||
// system - read only
|
||||
"mar" => Ok(Self::Mar),
|
||||
"mdr" => Ok(Self::Mdr),
|
||||
"sts" => Ok(Self::Sts),
|
||||
"cir" => Ok(Self::Cir),
|
||||
"pcx" => Ok(Self::Pcx),
|
||||
|
||||
"null" => Ok(Self::Null),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user