Added instruction decoding to the executable.

This commit is contained in:
Filipe Rodrigues 2020-10-25 19:11:41 +00:00
parent 46e794c1c5
commit 7d1ebd5fa3
10 changed files with 1743 additions and 1 deletions

View File

@ -6,10 +6,12 @@
// Modules
pub mod error;
pub mod header;
pub mod instruction;
// Exports
pub use error::DeserializeError;
pub use header::Header;
pub use instruction::Instruction;
// Imports
use crate::{io::address::Data, GameFile};

View File

@ -0,0 +1,125 @@
//! Psx cpu instructions
// Modules
pub mod directive;
pub mod pos;
pub mod pseudo;
pub mod raw;
pub mod reg;
pub mod simple;
// Exports
pub use directive::Directive;
pub use pos::Pos;
pub use pseudo::PseudoInstruction;
pub use raw::{FromRawIter, Raw};
pub use reg::Register;
pub use simple::SimpleInstruction;
/// An assembler instruction
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(derive_more::Display)]
pub enum Instruction {
/// A simple instruction
Simple(SimpleInstruction),
/// A pseudo instruction
Pseudo(PseudoInstruction),
/// A directive
Directive(Directive),
}
impl Instruction {
/// End of the code itself in the executable.
pub const CODE_END: Pos = Pos(0x8006dd3c);
/// Start of the code itself in the executable.
pub const CODE_START: Pos = Pos(0x80013e4c);
}
/// Iterator adaptor for converting [`RawInstruction`]s into [`Instruction`]s.
pub struct Iter<I: Iterator<Item = Raw> + Clone> {
/// Underlying iterator
iter: I,
/// Remaining items from last iterator
remaining: Option<Box<dyn Iterator<Item = (Pos, Instruction)>>>,
}
impl<I: Iterator<Item = Raw> + Clone> Iter<I> {
/// Helper function to try to decode without consuming the iterator
fn try_decode<T: FromRawIter>(iter: &I) -> (I, T::Decoded) {
let mut cloned_iter = iter.clone();
let decoded = T::decode(&mut cloned_iter);
(cloned_iter, decoded)
}
/// Helper function to try to get instructions from `T`.
fn try_next_from<T: FromRawIter + 'static>(&mut self, to_instruction: fn(T) -> Instruction) -> Option<(Pos, Instruction)> {
// Try to decode it and get all instructions
let (iter, instructions) = Self::try_decode::<T>(&self.iter);
// Map the instructions to be an iterator over `Instruction` and peekable
let mut instructions = instructions
.into_iter()
.map(move |(pos, decoded)| (pos, to_instruction(decoded)))
.peekable();
// Then check if we got any from the decode
match instructions.next() {
// If we did, set our iter, set any remaining instructions and return the instruction
Some(instruction) => {
self.iter = iter;
// If there are any instructions left, set remaining, else just leave it
if instructions.peek().is_some() {
self.remaining = Some(Box::new(instructions));
}
Some(instruction)
},
// Else we didn't get anything, don't update the iterator.
None => None,
}
}
/// Returns the current position of the iterator
fn cur_pos(&self) -> Option<Pos> {
self.iter.clone().next().map(|raw| raw.pos)
}
}
impl<I: Iterator<Item = Raw> + Clone> Iterator for Iter<I> {
type Item = (Pos, Instruction);
fn next(&mut self) -> Option<Self::Item> {
// If we have remaining instruction, supply them
if let Some(remaining) = self.remaining.as_mut() {
if let Some(instruction) = remaining.next() {
return Some(instruction);
} else {
// Note: We set it to none in case `next` is expensive to check.
self.remaining = None;
}
}
// Else get the current position
let cur_pos = self.cur_pos()?;
// If we're before the code start, just read directives
if cur_pos < Instruction::CODE_START || cur_pos >= Instruction::CODE_END {
return self.try_next_from(Instruction::Directive);
}
// Else try to decode it as a pseudo, simple or directive, in that order.
self.try_next_from(Instruction::Pseudo)
.or_else(|| self.try_next_from(Instruction::Simple))
.or_else(|| self.try_next_from(Instruction::Directive))
}
}
impl Instruction {
/// Adapts an iterator over raw words to an instruction iterator
pub fn new_iter<I: Iterator<Item = Raw> + Clone>(iter: I) -> Iter<I> {
Iter { iter, remaining: None }
}
}

View File

@ -0,0 +1,137 @@
//! Directives
// Imports
use super::{FromRawIter, Pos, Raw};
use ascii::{AsciiChar, AsciiStr, AsciiString};
use AsciiChar::Null;
/// A directive
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(derive_more::Display)]
pub enum Directive {
/// Write word
#[display(fmt = "dw {_0:#x}")]
Dw(u32),
/// Repeated words
#[display(fmt = "dw {value:#x}, {len}")]
DwRepeated {
/// Value being repeated
value: u32,
/// Times the value was repeated
len: usize,
},
/// Ascii string
#[display(fmt = ".ascii {_0:?}")]
Ascii(AsciiString),
}
/// Helper function to check if a string has null and if everything after the first
/// null is also null (or if there were no nulls).
fn check_nulls<S: AsRef<AsciiStr>>(s: S) -> (S, usize, bool) {
let null_idx = s
.as_ref()
.as_slice()
.iter()
.position(|&ch| ch == Null)
.unwrap_or_else(|| s.as_ref().len());
#[allow(clippy::indexing_slicing)] // `null_idx <= len`
let uniform_null = s.as_ref()[null_idx..].chars().all(|ch| ch == Null);
(s, null_idx, uniform_null)
}
impl FromRawIter for Directive {
type Decoded = Option<(Pos, Self)>;
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
// Get the first raw
let raw = iter.next()?;
// Try to get an ascii string from the raw and check for nulls
#[allow(clippy::wildcard_enum_match_arm)] // Option won't get more variants
match AsciiString::from_ascii(raw.repr.to_ne_bytes()).map(check_nulls) {
// If we got a string with at least 1 non-null, but
// at least 1 null and uniformly null, return just it
Ok((mut ascii_string, null_idx @ 1..=3, true)) => {
ascii_string.truncate(null_idx);
Some((raw.pos, Self::Ascii(ascii_string)))
},
// If we got a string without any nulls, keep
// filling the string until we find one.
Ok((mut ascii_string, 4, true)) => {
let ascii_string = loop {
let mut cur_iter = iter.clone();
match cur_iter.next() {
// If we don't have a next character, return the string as-is
// Note: No need to update the iterator, it returned `None`.
None => break ascii_string,
// Else try to get it as a string and check for nulls
Some(next_raw) => match AsciiStr::from_ascii(&next_raw.repr.to_ne_bytes()).map(check_nulls) {
// If we got it and it wasn't null, update the iterator, add it and continue
Ok((new_ascii_str, 4, _)) => {
*iter = cur_iter;
ascii_string.push_str(new_ascii_str);
},
// If we got it, but there was a uniform null, update the iterator,
// add the non-null parts and return.
#[allow(clippy::indexing_slicing)] // `null_idx < len`
Ok((new_ascii_str, null_idx, true)) => {
*iter = cur_iter;
ascii_string.push_str(&new_ascii_str[..null_idx]);
break ascii_string;
},
// If we didn't get it or it was a non-uniform null, return the string we have so far
// Note: We don't update the iterator, as we want to leave
// the next value to `dw`.
Err(_) | Ok((_, _, false)) => break ascii_string,
},
}
};
Some((raw.pos, Self::Ascii(ascii_string)))
},
// Else if it was full null, non-uniformly null or non-ascii,
// try to get a dw table
_ => {
let mut times_repeated = 0;
// Keep getting values until either eof or a different one
loop {
let mut cur_iter = iter.clone();
match cur_iter.next().map(|next_raw| next_raw.repr == raw.repr) {
// If we got a different value, keep fetching values until they're different
Some(true) => {
*iter = cur_iter;
times_repeated += 1;
},
// If we didn't get it or we got a different value, exit
// Note: No need t update the iterator, as it either returned `None` or
// a different raw.
None | Some(false) => match times_repeated {
// If the value didn't repeat, use a single `dw`
0 => break Some((raw.pos, Self::Dw(raw.repr))),
// Else return the table
_ => {
break Some((raw.pos, Self::DwRepeated {
value: raw.repr,
len: times_repeated + 1,
}))
},
},
}
}
},
}
}
}

View File

@ -0,0 +1,36 @@
//! Instruction position
// TODO: More implementations for `Pos`
// Imports
use std::{fmt, ops};
/// An instruction position
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
#[derive(ref_cast::RefCast)]
#[repr(transparent)]
pub struct Pos(pub u32);
impl fmt::LowerHex for Pos {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<u32 as fmt::LowerHex>::fmt(&self.0, f)
}
}
impl ops::Sub<u32> for Pos {
type Output = Self;
fn sub(self, rhs: u32) -> Self::Output {
Self(self.0 - rhs)
}
}
impl<'a, T> ops::Sub<T> for &'_ Pos
where
Pos: ops::Sub<T, Output = Pos>,
{
type Output = Pos;
fn sub(self, rhs: T) -> Self::Output {
<Pos as ops::Sub<T>>::sub(Pos(self.0), rhs)
}
}

View File

@ -0,0 +1,385 @@
//! Pseudo instructions
// Imports
use super::{FromRawIter, Pos, Raw, Register, SimpleInstruction};
use crate::util::SignedHex;
use int_conv::{Join, SignExtended, Signed, ZeroExtended};
/// A pseudo instruction
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[allow(clippy::missing_docs_in_private_items)] // Mostly just register names and immediates.
pub enum PseudoInstruction {
/// No-op
/// alias for `sll $zr,$zr,0`
#[display(fmt = "nop")]
Nop,
/// Move register
/// Alias for `{addu|addiu|or} $rd, $rs, $zr`
#[display(fmt = "move {rd}, {rs}")]
MovReg { rd: Register, rs: Register },
/// Load byte immediate
/// Alias for `lui $rx, {offset-hi} / lb $rx, {offset-lo}($rx)`
#[display(fmt = "lb {rx}, {offset:#x}")]
LbImm { rx: Register, offset: u32 },
/// Load byte unsigned immediate
/// Alias for `lui $rx, {offset-hi} / lbu $rx, {offset-lo}($rx)`
#[display(fmt = "lbu {rx}, {offset:#x}")]
LbuImm { rx: Register, offset: u32 },
/// Load half-word immediate
/// Alias for `lui $rx, {offset-hi} / lh $rx, {offset-lo}($rx)`
#[display(fmt = "lh {rx}, {offset:#x}")]
LhImm { rx: Register, offset: u32 },
/// Load half-word unsigned immediate
/// Alias for `lui $rx, {offset-hi} / lhu $rx, {offset-lo}($rx)`
#[display(fmt = "lh {rx}, {offset:#x}")]
LhuImm { rx: Register, offset: u32 },
/// Load left word immediate
/// Alias for `lui $rx, {offset-hi} / lwl $rx, {offset-lo}($rx)`
#[display(fmt = "lwl {rx}, {offset:#x}")]
LwlImm { rx: Register, offset: u32 },
/// Load word immediate
/// Alias for `lui $rx, {offset-hi} / lw $rx, {offset-lo}($rx)`
#[display(fmt = "lw {rx}, {offset:#x}")]
LwImm { rx: Register, offset: u32 },
/// Load right word immediate
/// Alias for `lui $rx, {offset-hi} / lwr $rx, {offset-lo}($rx)`
#[display(fmt = "lwr {rx}, {offset:#x}")]
LwrImm { rx: Register, offset: u32 },
/// Store byte immediate
/// Alias for `lui $at, {offset-hi} / sb $rx, {offset-lo}($at)`
#[display(fmt = "sb {rx}, {offset:#x}")]
SbImm { rx: Register, offset: u32 },
/// Store half-word immediate
/// Alias for `lui $at, {offset-hi} / sh $rx, {offset-lo}($at)`
#[display(fmt = "sh {rx}, {offset:#x}")]
ShImm { rx: Register, offset: u32 },
/// Store left word immediate
/// Alias for `lui $at, {offset-hi} / swl $rx, {offset-lo}($at)`
#[display(fmt = "swl {rx}, {offset:#x}")]
SwlImm { rx: Register, offset: u32 },
/// Store word immediate
/// Alias for `lui $at, {offset-hi} / sw $rx, {offset-lo}($at)`
#[display(fmt = "sw {rx}, {offset:#x}")]
SwImm { rx: Register, offset: u32 },
/// Store right word immediate
/// Alias for `lui $at, {offset-hi} / swr $rx, {offset-lo}($at)`
#[display(fmt = "swr {rx}, {offset:#x}")]
SwrImm { rx: Register, offset: u32 },
/// Load address
/// Alias for `lui $rx, {target-hi} / addiu $rx, $rx, {target-lo}`
#[display(fmt = "la {rx}, {target:#x}")]
La { rx: Register, target: u32 },
/// Load immediate 32-bit
/// Alias for `lui $rx, {imm-hi} / ori $rx, $rx, {imm-lo}`
#[display(fmt = "li {rx}, {imm:#x}")]
Li32 { rx: Register, imm: u32 },
/// Load immediate 16-bit
/// Alias for `ori $rx, $zr, imm`
#[display(fmt = "li {rx}, {imm:#x}")]
Li16 { rx: Register, imm: u16 },
/// Load immediate negative 15-bit
/// Alias for `addiu $rx, $zr, imm`, with imm in `-0x8000 .. -0x1`
#[display(fmt = "li {rx}, {:#x}", "SignedHex(imm)")]
LiNeg15 { rx: Register, imm: i16 },
/// Load immediate upper 16-bits
/// Alias for `lui 0x1000 * imm`
#[display(fmt = "li {rx}, {:#x}", "imm.zero_extended::<u32>() << 16")]
LiUpper16 { rx: Register, imm: u16 },
/// Add assign
/// Alias for `add $rx, $rx, $rt`
#[display(fmt = "add {rx}, {rt}")]
AddAssign { rx: Register, rt: Register },
/// Add unsigned assign
/// Alias for `addu $rx, $rx, $rt`
#[display(fmt = "addu {rx}, {rt}")]
AdduAssign { rx: Register, rt: Register },
/// Sub assign
/// Alias for `sub $rx, $rx, $rt`
#[display(fmt = "sub {rx}, {rt}")]
SubAssign { rx: Register, rt: Register },
/// Sub unsigned assign
/// Alias for `subu $rx, $rx, $rt`
#[display(fmt = "subu {rx}, {rt}")]
SubuAssign { rx: Register, rt: Register },
/// And assign
/// Alias for `and $rx, $rx, $rt`
#[display(fmt = "and {rx}, {rt}")]
AndAssign { rx: Register, rt: Register },
/// Or assign
/// Alias for `or $rx, $rx, $rt`
#[display(fmt = "or {rx}, {rt}")]
OrAssign { rx: Register, rt: Register },
/// Xor assign
/// Alias for `xor $rx, $rx, $rt`
#[display(fmt = "xor {rx}, {rt}")]
XorAssign { rx: Register, rt: Register },
/// Nor assign
/// Alias for `nor $rx, $rx, $rt`
#[display(fmt = "nor {rx}, {rt}")]
NorAssign { rx: Register, rt: Register },
/// And immediate assign
/// Alias for `andi $rx, $rx, imm`
#[display(fmt = "andi {rx}, {imm:#x}")]
AndiAssign { rx: Register, imm: u16 },
/// Or immediate assign
/// Alias for `ori $rx, $rx, imm`
#[display(fmt = "ori {rx}, {imm:#x}")]
OriAssign { rx: Register, imm: u16 },
/// Xor immediate assign
/// Alias for `xori $rx, $rx, imm`
#[display(fmt = "xori {rx}, {imm:#x}")]
XoriAssign { rx: Register, imm: u16 },
/// Shift left logical variable assign
/// Alias for `sllv $rx, $rx, $rs`
#[display(fmt = "sllv {rx} {rs}")]
SllvAssign { rx: Register, rs: Register },
/// Shift right logical variable assign
/// Alias for `srlv $rx, $rx, $rs`
#[display(fmt = "srlv {rx} {rs}")]
SrlvAssign { rx: Register, rs: Register },
/// Shift right arithmetic variable assign
/// Alias for `srav $rx, $rx, $rs`
#[display(fmt = "srav {rx} {rs}")]
SravAssign { rx: Register, rs: Register },
/// Shift left logical assign
/// Alias for `sll $rx, $rx, imm`
#[display(fmt = "sll {rx} {imm:#x}")]
SllAssign { rx: Register, imm: u8 },
/// Shift right logical assign
/// Alias for `srl $rx, $rx, imm`
#[display(fmt = "srl {rx} {imm:#x}")]
SrlAssign { rx: Register, imm: u8 },
/// Shift right arithmetic assign
/// Alias for `sla $rx, $rx, imm`
#[display(fmt = "sra {rx} {imm:#x}")]
SraAssign { rx: Register, imm: u8 },
/// Jump and link with return address
/// Alias for `jalr $ra, $rx`
#[display(fmt = "jalr {rx}")]
JalrRa { rx: Register },
/// Subtract immediate
/// Alias for `addi $rt, $rs, imm` for negative `imm`s
#[display(fmt = "subi {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Subi { rt: Register, rs: Register, imm: i16 },
/// Subtract immediate sign-extended
/// Alias for `addiu $rt, $rs, imm` for negative `imm`s
#[display(fmt = "subiu {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Subiu { rt: Register, rs: Register, imm: i16 },
/// Subtract immediate assign
/// Alias for `subi $rx, $rx, imm`
#[display(fmt = "subi {rx}, {:#x}", "SignedHex(imm)")]
SubiAssign { rx: Register, imm: i16 },
/// Subtract immediate sign-extended assign
/// Alias for `subiu $rx, $rx, imm`
#[display(fmt = "subiu {rx}, {:#x}", "SignedHex(imm)")]
SubiuAssign { rx: Register, imm: i16 },
/// Branch if equal to zero
/// Alias for `beq $rx, $zr, target`
#[display(fmt = "beqz {rx}, {target:#x}")]
Beqz { rx: Register, target: u32 },
/// Branch if different from zero
/// Alias for `bne $rx, $zr, target`
#[display(fmt = "bnez {rx}, {target:#x}")]
Bnez { rx: Register, target: u32 },
/// Jump relative
/// Alias for `beq $zr, $zr, target`
#[display(fmt = "b {target:#x}")]
B { target: u32 },
// TODO: Push / Pop
}
impl FromRawIter for PseudoInstruction {
type Decoded = Option<(Pos, Self)>;
#[allow(clippy::wildcard_enum_match_arm)] // New entries won't affect this function, it can only act on entries it knows.
#[allow(clippy::similar_names)] // With register names, this happens too much
#[allow(clippy::too_many_lines, clippy::clippy::cognitive_complexity)] // We can't separate this into several functions, it's just 1 big match
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
use Register::{At, Ra, Zr};
use SimpleInstruction::{
Add, Addi, Addiu, Addu, And, Andi, Beq, Bne, Jalr, Lb, Lbu, Lh, Lhu, Lui, Lw, Lwl, Lwr, Nor, Or, Ori, Sb, Sh, Sll, Sllv, Sra, Srav, Srl,
Srlv, Sub, Subu, Sw, Swl, Swr, Xor, Xori,
};
// Get the first instruction
let (pos, instruction) = SimpleInstruction::decode(iter)?;
let pseudo = match instruction {
Lui { imm: imm_hi, rt: prev_rt } => {
let iter_before = iter.clone();
match SimpleInstruction::decode(iter)?.1 {
Addiu { imm: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::La {
rx: prev_rt,
// Note: `imm_lo` is signed
target: (u32::join(0, imm_hi).as_signed() + imm_lo.sign_extended::<i32>()).as_unsigned(),
}),
Ori { imm: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::Li32 {
rx: prev_rt,
imm: u32::join(imm_lo, imm_hi),
}),
Lb { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LbImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lbu { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LbuImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lh { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LhImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lhu { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LhuImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lwl { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LwlImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lw { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LwImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Lwr { offset: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Some(Self::LwrImm {
rx: prev_rt,
offset: u32::join(imm_lo, imm_hi),
}),
Sb { offset: imm_lo, rt, rs } if prev_rt == At && rs == At => Some(Self::SbImm {
rx: rt,
offset: u32::join(imm_lo, imm_hi),
}),
Sh { offset: imm_lo, rt, rs } if prev_rt == At && rs == At => Some(Self::ShImm {
rx: rt,
offset: u32::join(imm_lo, imm_hi),
}),
Swl { offset: imm_lo, rt, rs } if prev_rt == At && rs == At => Some(Self::SwlImm {
rx: rt,
offset: u32::join(imm_lo, imm_hi),
}),
Sw { offset: imm_lo, rt, rs } if prev_rt == At && rs == At => Some(Self::SwImm {
rx: rt,
offset: u32::join(imm_lo, imm_hi),
}),
Swr { offset: imm_lo, rt, rs } if prev_rt == At && rs == At => Some(Self::SwrImm {
rx: rt,
offset: u32::join(imm_lo, imm_hi),
}),
// Since we don't use the value, reset the iterator to it's previous value.
_ => {
*iter = iter_before;
Some(Self::LiUpper16 { rx: prev_rt, imm: imm_hi })
},
}
},
Sll { rd: Zr, rt: Zr, imm: 0 } => Some(Self::Nop),
Addu { rd, rs, rt: Zr } | Addiu { rt: rd, rs, imm: 0 } | Or { rd, rs, rt: Zr } => Some(Self::MovReg { rd, rs }),
Ori { rt, rs: Zr, imm } => Some(Self::Li16 { rx: rt, imm }),
Add { rd, rs, rt } if rd == rs => Some(Self::AddAssign { rx: rd, rt }),
Addu { rd, rs, rt } if rd == rs => Some(Self::AdduAssign { rx: rd, rt }),
Sub { rd, rs, rt } if rd == rs => Some(Self::SubAssign { rx: rd, rt }),
Subu { rd, rs, rt } if rd == rs => Some(Self::SubuAssign { rx: rd, rt }),
And { rd, rs, rt } if rd == rs => Some(Self::AndAssign { rx: rd, rt }),
Or { rd, rs, rt } if rd == rs => Some(Self::OrAssign { rx: rd, rt }),
Xor { rd, rs, rt } if rd == rs => Some(Self::XorAssign { rx: rd, rt }),
Nor { rd, rs, rt } if rd == rs => Some(Self::NorAssign { rx: rd, rt }),
Andi { rt, rs, imm } if rt == rs => Some(Self::AndiAssign { rx: rt, imm }),
Ori { rt, rs, imm } if rt == rs => Some(Self::OriAssign { rx: rt, imm }),
Xori { rt, rs, imm } if rt == rs => Some(Self::XoriAssign { rx: rt, imm }),
Sllv { rd, rt, rs } if rd == rt => Some(Self::SllvAssign { rx: rd, rs }),
Srlv { rd, rt, rs } if rd == rt => Some(Self::SrlvAssign { rx: rd, rs }),
Srav { rd, rt, rs } if rd == rt => Some(Self::SravAssign { rx: rd, rs }),
Sll { rd, rt, imm } if rd == rt => Some(Self::SllAssign { rx: rd, imm }),
Srl { rd, rt, imm } if rd == rt => Some(Self::SrlAssign { rx: rd, imm }),
Sra { rd, rt, imm } if rd == rt => Some(Self::SraAssign { rx: rd, imm }),
Jalr { rd: Ra, rs: rx } => Some(Self::JalrRa { rx }),
Addi {
rt,
rs,
imm: imm @ i16::MIN..0,
} => match rt == rs {
true => Some(Self::SubiAssign { rx: rt, imm }),
false => Some(Self::Subi { rt, rs, imm }),
},
Addiu {
rt: rx,
rs: Zr,
imm: imm @ i16::MIN..0,
} => Some(Self::LiNeg15 { rx, imm }),
Addiu {
rt,
rs,
imm: imm @ i16::MIN..0,
} => match rt == rs {
true => Some(Self::SubiuAssign { rx: rt, imm }),
false => Some(Self::Subiu { rt, rs, imm }),
},
Beq { rs: Zr, rt: Zr, target } => Some(Self::B { target }),
Beq { rs: rx, rt: Zr, target } => Some(Self::Beqz { rx, target }),
Bne { rs: rx, rt: Zr, target } => Some(Self::Bnez { rx, target }),
// Note: No need to reset iterator, it returned `None`.
_ => None,
};
pseudo.map(|pseudo_instruction| (pos, pseudo_instruction))
}
}

View File

@ -0,0 +1,27 @@
//! Raw instructions
// Imports
use super::Pos;
/// A raw instruction
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Raw {
/// The raw encoding of the instruction
pub repr: u32,
/// The position of this instruction
pub pos: Pos,
}
/// Raw instruction decoding
///
/// Implementors should be atomic about the consumed and
/// returned iterator, that is, consume the least possible
/// input in order to produce an atomic part of themselves.
pub trait FromRawIter: Sized {
/// Returned iterator from [`decode`]
type Decoded: IntoIterator<Item = (Pos, Self)>;
/// Attempts to decode an instruction from an iterator of raw instructions
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded;
}

View File

@ -0,0 +1,135 @@
//! Cpu registers
// Macro to generate `Register`
macro_rules! generate_register {
(
pub enum Register {
$(
$( #[doc = $doc:literal] )?
#[display(fmt = $fmt:literal)]
$variant:ident = $value:expr
),+ $(,)?
}
) => {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum Register {
$(
$( #[doc = $doc] )?
#[display(fmt = $fmt)]
$variant = $value,
)*
}
impl Register {
/// Array containing all registers
pub const ALL_REGISTERS: [Self; 32] = [
$(
Self::$variant,
)*
];
/// Creates a new register index from a `u8`.
#[must_use]
pub const fn new(idx: u8) -> Option<Self> {
match idx {
$(
$value => Some( Self::$variant ),
)*
_ => None,
}
}
}
impl From<Register> for usize {
fn from(idx: Register) -> Self {
match idx {
$(
Register::$variant => $value,
)*
}
}
}
impl std::str::FromStr for Register {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim() {
$(
$fmt => Ok(Self::$variant),
)*
_ => Err(())
}
}
}
}
}
generate_register! {
pub enum Register {
/// Zero register
#[display(fmt = "$zr")]
Zr = 0,
/// Assembler temporary
#[display(fmt = "$at")]
At = 1,
// Return values
#[display(fmt = "$v0")] V0 = 2,
#[display(fmt = "$v1")] V1 = 3,
// Arguments
#[display(fmt = "$a0")] A0 = 4,
#[display(fmt = "$a1")] A1 = 5,
#[display(fmt = "$a2")] A2 = 6,
#[display(fmt = "$a3")] A3 = 7,
// Temporaries
#[display(fmt = "$t0")] T0 = 8,
#[display(fmt = "$t1")] T1 = 9,
#[display(fmt = "$t2")] T2 = 10,
#[display(fmt = "$t3")] T3 = 11,
#[display(fmt = "$t4")] T4 = 12,
#[display(fmt = "$t5")] T5 = 13,
#[display(fmt = "$t6")] T6 = 14,
#[display(fmt = "$t7")] T7 = 15,
// Static variables
#[display(fmt = "$s0")] S0 = 16,
#[display(fmt = "$s1")] S1 = 17,
#[display(fmt = "$s2")] S2 = 18,
#[display(fmt = "$s3")] S3 = 19,
#[display(fmt = "$s4")] S4 = 20,
#[display(fmt = "$s5")] S5 = 21,
#[display(fmt = "$s6")] S6 = 22,
#[display(fmt = "$s7")] S7 = 23,
// Temporaries
#[display(fmt = "$t8")] T8 = 24,
#[display(fmt = "$t9")] T9 = 25,
// Kernel
#[display(fmt = "$k0")] K0 = 26,
#[display(fmt = "$k1")] K1 = 27,
/// Global pointer
#[display(fmt = "$gp")]
Gp = 28,
/// Stack pointer
#[display(fmt = "$sp")]
Sp = 29,
/// Frame pointer
#[display(fmt = "$fp")]
Fp = 30,
/// Return address
#[display(fmt = "$ra")]
Ra = 31,
}
}

View File

@ -0,0 +1,829 @@
//! Raw instructions
// Lints
// #[allow(clippy::similar_names)]
// Modules
pub mod repr;
// Exports
pub use repr::RawRepr;
// Imports
use super::{FromRawIter, Pos, Raw, Register};
use crate::util::SignedHex;
use int_conv::{SignExtended, Signed};
/// Macro to declare all instructions
macro_rules! decl_instructions {
(
$(
$( #[doc = $doc:literal] )*
#[display(fmt = $fmt:literal $( , $fmt_args:expr )* $(,)?)]
$split:pat $( if $cond:expr )? => $variant:ident {
$( $field_name:ident : $field_type:ty $( = $field_expr: expr )? ),* $(,)?
}
),+ $(,)?
) => {
/// A raw instruction
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum SimpleInstruction {
$(
$( #[doc = $doc] )*
#[display(fmt = $fmt, $( $fmt_args, )*)]
$variant {
$(
$field_name: $field_type,
)*
},
)+
}
impl FromRawIter for SimpleInstruction {
type Decoded = Option<(Pos, Self)>;
#[allow(clippy::redundant_field_names)] // For uniform initialization
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
let raw = iter.next()?;
let split = RawRepr::new(raw);
let instruction = match split {
$(
$split $( if $cond )? => Some( Self::$variant {
$(
$field_name $( : $field_expr )?,
)*
} ),
)*
_ => None,
};
instruction.map(|instruction| (raw.pos, instruction))
}
}
}
}
decl_instructions! {
/// Store byte
#[display(fmt = "sb {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x28,
rs, rt, imm16,
..
} => Sb {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Store half-word
#[display(fmt = "sh {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x29,
rs, rt, imm16,
..
} => Sh {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Store left word
#[display(fmt = "swl {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x2a,
rs, rt, imm16,
..
} => Swl {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Store word
#[display(fmt = "sw {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x2b,
rs, rt, imm16,
..
} => Sw {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Store right word
#[display(fmt = "swr {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x2e,
rs, rt, imm16,
..
} => Swr {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load byte
#[display(fmt = "lb {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x20,
rs, rt, imm16,
..
} => Lb {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load byte unsigned
#[display(fmt = "lbu {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x24,
rs, rt, imm16,
..
} => Lbu {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load half-word
#[display(fmt = "lh {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x21,
rs, rt, imm16,
..
} => Lh {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load half-word unsigned
#[display(fmt = "lhu {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x25,
rs, rt, imm16,
..
} => Lhu {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load left word
#[display(fmt = "lwl {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x22,
rs, rt, imm16,
..
} => Lwl {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load word
#[display(fmt = "lw {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x23,
rs, rt, imm16,
..
} => Lw {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Load right word
#[display(fmt = "lwr {rt}, {offset:#x}({rs})")]
RawRepr {
op: 0x26,
rs, rt, imm16,
..
} => Lwr {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
offset: u16 = imm16,
},
/// Add
#[display(fmt = "add {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x20,
rd, rs, rt,
..
} => Add {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Add unsigned
#[display(fmt = "addu {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x21,
rd, rs, rt,
..
} => Addu {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Sub
#[display(fmt = "sub {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x22,
rd, rs, rt,
..
} => Sub {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Sub unsigned
#[display(fmt = "subu {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x23,
rd, rs, rt,
..
} => Subu {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Add immediate
#[display(fmt = "addi {rt}, {rs}, {:#x}", "SignedHex(imm)")]
RawRepr {
op: 0x08,
rt, rs, imm16,
..
} => Addi {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: i16 = imm16.as_signed(),
},
/// Add immediate sign-extended
/// Note: _NOT_ Unsigned.
#[display(fmt = "addiu {rt}, {rs}, {:#x}", "SignedHex(imm)")]
RawRepr {
op: 0x09,
rt, rs, imm16,
..
} => Addiu {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: i16 = imm16.as_signed(),
},
/// Set less than
#[display(fmt = "slt {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x2a,
rd, rs, rt,
..
} => Slt {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Set less than unsigned
#[display(fmt = "sltu {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x2b,
rd, rs, rt,
..
} => Sltu {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Set less than immediate
#[display(fmt = "slti {rt}, {rs}, {:#x}", "SignedHex(imm)")]
RawRepr {
op: 0x0a,
rt, rs, imm16,
..
} => Slti {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: i16 = imm16.as_signed(),
},
/// Set less than immediate unsigned
#[display(fmt = "sltiu {rt}, {rs}, {imm:#x}")]
RawRepr {
op: 0x0b,
rt, rs, imm16,
..
} => Sltiu {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: u16 = imm16,
},
/// And
#[display(fmt = "and {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x24,
rd, rs, rt,
..
} => And {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Or
#[display(fmt = "or {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x25,
rd, rs, rt,
..
} => Or {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Xor
#[display(fmt = "xor {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x26,
rd, rs, rt,
..
} => Xor {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Nor
#[display(fmt = "nor {rd}, {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x27,
rd, rs, rt,
..
} => Nor {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// And immediate
#[display(fmt = "andi {rt}, {rs}, {imm:#x}")]
RawRepr {
op: 0x0c,
rt, rs, imm16,
..
} => Andi {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: u16 = imm16,
},
/// Or immediate
#[display(fmt = "ori {rt}, {rs}, {imm:#x}")]
RawRepr {
op: 0x0d,
rt, rs, imm16,
..
} => Ori {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: u16 = imm16,
},
/// Xor immediate
#[display(fmt = "xori {rt}, {rs}, {imm:#x}")]
RawRepr {
op: 0x0e,
rt, rs, imm16,
..
} => Xori {
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
imm: u16 = imm16,
},
/// Shift left logical variable
#[display(fmt = "sllv {rd}, {rt}, {rs}")]
RawRepr {
op: 0x00, op2: 0x04,
rd, rs, rt,
..
} => Sllv {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
},
/// Shift right logical variable
#[display(fmt = "srlv {rd}, {rt}, {rs}")]
RawRepr {
op: 0x00, op2: 0x06,
rd, rs, rt,
..
} => Srlv {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
},
/// Shift right arithmetic variable
#[display(fmt = "srav {rd}, {rt}, {rs}")]
RawRepr {
op: 0x00, op2: 0x07,
rd, rs, rt,
..
} => Srav {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
rs: Register = Register::new(rs)?,
},
/// Shift left logical
#[display(fmt = "sll {rd}, {rt}, {imm:#x}")]
RawRepr {
op: 0x00, op2: 0x00,
rd, rt, imm5,
..
} => Sll {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
imm: u8 = imm5,
},
/// Shift right logical
#[display(fmt = "srl {rd}, {rt}, {imm:#x}")]
RawRepr {
op: 0x00, op2: 0x02,
rd, rt, imm5,
..
} => Srl {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
imm: u8 = imm5,
},
/// Shift right arithmetic
#[display(fmt = "sra {rd}, {rt}, {imm:#x}")]
RawRepr {
op: 0x00, op2: 0x03,
rd, rt, imm5,
..
} => Sra {
rd: Register = Register::new(rd)?,
rt: Register = Register::new(rt)?,
imm: u8 = imm5,
},
/// Load upper immediate
#[display(fmt = "lui {rt}, {imm:#x}")]
RawRepr {
op: 0x0f,
rt, imm16,
..
} => Lui {
rt: Register = Register::new(rt)?,
imm: u16 = imm16,
},
/// Multiply
#[display(fmt = "mult {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x18,
rs, rt,
..
} => Mult {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Multiply unsigned
#[display(fmt = "multu {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x19,
rs, rt,
..
} => Multu {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Divide
#[display(fmt = "div {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x1a,
rs, rt,
..
} => Div {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Multiply unsigned
#[display(fmt = "divu {rs}, {rt}")]
RawRepr {
op: 0x00, op2: 0x1b,
rs, rt,
..
} => Divu {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
},
/// Move from hi
#[display(fmt = "mfhi {rd}")]
RawRepr {
op: 0x00, op2: 0x10,
rd,
..
} => Mfhi {
rd: Register = Register::new(rd)?,
},
/// Move from lo
#[display(fmt = "mflo {rd}")]
RawRepr {
op: 0x00, op2: 0x11,
rd,
..
} => Mflo {
rd: Register = Register::new(rd)?,
},
/// Move to hi
#[display(fmt = "mthi {rd}")]
RawRepr {
op: 0x00, op2: 0x12,
rd,
..
} => Mthi {
rd: Register = Register::new(rd)?,
},
/// Move to lo
#[display(fmt = "mtlo {rd}")]
RawRepr {
op: 0x00, op2: 0x13,
rd,
..
} => Mtlo {
rd: Register = Register::new(rd)?,
},
/// Jump
#[display(fmt = "j {target:#x}")]
RawRepr {
op: 0x02,
imm26, pos,
..
} => J {
target: u32 = i32::as_unsigned(u32::as_signed(pos & 0xF000_0000) + imm26.as_signed() * 4),
},
/// Jump and link
#[display(fmt = "jal {target:#x}")]
RawRepr {
op: 0x03,
imm26, pos,
..
} => Jal {
target: u32 = i32::as_unsigned(u32::as_signed(pos & 0xF000_0000) + imm26.as_signed() * 4),
},
/// Jump register
#[display(fmt = "jr {rs}")]
RawRepr {
op: 0x00, op2: 0x08,
rs,
..
} => Jr {
rs: Register = Register::new(rs)?,
},
/// Jump and link register
#[display(fmt = "jalr {rd}, {rs}")]
RawRepr {
op: 0x00, op2: 0x09,
rd, rs,
..
} => Jalr {
rd: Register = Register::new(rd)?,
rs: Register = Register::new(rs)?,
},
/// Branch if equal
#[display(fmt = "beq {rs}, {rt}, {target:#x}")]
RawRepr {
op: 0x04,
rs, rt, imm16, pos,
..
} => Beq {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4),
},
/// Branch if not equal
#[display(fmt = "bne {rs}, {rt}, {target:#x}")]
RawRepr {
op: 0x05,
rs, rt, imm16, pos,
..
} => Bne {
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4),
},
/// Branch if less than zero
#[display(fmt = "bltz {rs}, {target:#x}")]
RawRepr {
op: 0x01, rt: 0x00,
rs, imm16, pos,
..
} => Bltz {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Branch if greater or equal to zero
#[display(fmt = "bgez {rs}, {target:#x}")]
RawRepr {
op: 0x01, rt: 0x01,
rs, imm16, pos,
..
} => Bgez {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Branch if greater than zero
#[display(fmt = "bgtz {rs}, {target:#x}")]
RawRepr {
op: 0x07,
rs, imm16, pos,
..
} => Bgtz {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Branch if less or equal to zero
#[display(fmt = "blez {rs}, {target:#x}")]
RawRepr {
op: 0x06,
rs, imm16, pos,
..
} => Blez {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Branch if less than zero and link
#[display(fmt = "bltzal {rs}, {target:#x}")]
RawRepr {
op: 0x01, rt: 0x10,
rs, imm16, pos,
..
} => Bltzal {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Branch if greater or equal to zero and link
#[display(fmt = "bgezal {rs}, {target:#x}")]
RawRepr {
op: 0x01, rt: 0x11,
rs, imm16, pos,
..
} => Bgezal {
rs: Register = Register::new(rs)?,
target: u32 = i32::as_unsigned(pos.as_signed() + imm16.as_signed().sign_extended::<i32>() * 4)
},
/// Save co-processor data registers
#[display(fmt = "mfc{n} {rt}, {rd}")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b0000,
co_n, rt, rd,
..
} => MfcN {
n : u8 = co_n,
rt: Register = Register::new(rt)?,
rd: Register = Register::new(rd)?,
},
/// Save co-processor control registers
#[display(fmt = "cfc{n} {rt}, {rd}")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b0010,
co_n, rt, rd,
..
} => CfcN {
n : u8 = co_n,
rt: Register = Register::new(rt)?,
rd: Register = Register::new(rd)?,
},
/// Load co-processor data registers
#[display(fmt = "mtc{n} {rt}, {rd}")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b0100,
co_n, rt, rd,
..
} => MtcN {
n : u8 = co_n,
rt: Register = Register::new(rt)?,
rd: Register = Register::new(rd)?,
},
/// Load co-processor control registers
#[display(fmt = "ctc{n} {rt}, {rd}")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b0110,
co_n, rt, rd,
..
} => CtcN {
n : u8 = co_n,
rt: Register = Register::new(rt)?,
rd: Register = Register::new(rd)?,
},
// TODO: Check how to calculate actual targets for these jumps
// Docs say `$+disp`, not sure if a typo or what, no 4
// multiple either, are co-processor instructions 1 byte?
/// Branch co-processor if false
#[display(fmt = "bc{n}f {target:#x} # Raw target")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b1000, rt: 0b00000,
co_n, imm16,
..
} => BcNf {
n: u8 = co_n,
target: u16 = imm16,
},
/// Branch co-processor if true
#[display(fmt = "bc{n}t {target:#x} # Raw target")]
RawRepr {
co_op: 0b0100, co_rs0: 0, co_rs1: 0b1000, rt: 0b00001,
co_n, imm16,
..
} => BcNt {
n: u8 = co_n,
target: u16 = imm16,
},
/// Exec immediate co-processor
#[display(fmt = "cop{n} {imm:#x}")]
RawRepr {
co_op: 0b0100, co_rs0: 1,
co_n, imm25,
..
} => CopN {
n: u8 = co_n,
imm: u32 = imm25,
},
/// Load word co-processor
#[display(fmt = "lwc{n} {rt}, {imm:#x}({rs})")]
RawRepr {
co_op: 0b1100,
co_n, rs, rt, imm16,
..
} => LwcN {
n: u8 = co_n,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
imm: u16 = imm16,
},
/// Store word co-processor
#[display(fmt = "swc{n} {rt}, {imm:#x}({rs})")]
RawRepr {
co_op: 0b1110,
co_n, rs, rt, imm16,
..
} => SwcN {
n: u8 = co_n,
rs: Register = Register::new(rs)?,
rt: Register = Register::new(rt)?,
imm: u16 = imm16,
},
}

View File

@ -0,0 +1,61 @@
//! Raw instruction representation
// Imports
use super::Raw;
use int_conv::Truncate;
/// An instruction's raw representation, including
/// it's current address.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[allow(clippy::missing_docs_in_private_items)]
pub struct RawRepr {
pub op: u8,
pub rs: u8,
pub rt: u8,
pub rd: u8,
pub imm5: u8,
pub op2: u8,
pub imm16: u16,
pub imm25: u32,
pub imm26: u32,
/// Co-processor opcode
pub co_op: u8,
/// Co-processor number
pub co_n: u8,
/// Co-processor highest `rs` bit.
pub co_rs0: u8,
/// Co-processor lowest `rs` bits.
pub co_rs1: u8,
/// Position of the instruction
pub pos: u32,
}
#[allow(clippy::inconsistent_digit_grouping)] // We're grouping 6-5-5-5-5-6 as per docs.
impl RawRepr {
/// Creates a new split instruction
#[must_use]
#[rustfmt::skip]
pub fn new(Raw {repr, pos}: Raw) -> Self {
Self {
op : ((repr & 0b111111_00000_00000_00000_00000_000000) >> 26).truncate(),
rs : ((repr & 0b000000_11111_00000_00000_00000_000000) >> 21).truncate(),
rt : ((repr & 0b000000_00000_11111_00000_00000_000000) >> 16).truncate(),
rd : ((repr & 0b000000_00000_00000_11111_00000_000000) >> 11).truncate(),
imm5 : ((repr & 0b000000_00000_00000_00000_11111_000000) >> 6 ).truncate(),
op2 : ((repr & 0b000000_00000_00000_00000_00000_111111) >> 0 ).truncate(),
imm16 : ((repr & 0b000000_00000_00000_11111_11111_111111) >> 0 ).truncate(),
imm25 : ((repr & 0b000000_01111_11111_11111_11111_111111) >> 0 ),
imm26 : ((repr & 0b000000_11111_11111_11111_11111_111111) >> 0 ),
co_op : ((repr & 0b111100_00000_00000_00000_00000_000000) >> 28).truncate(),
co_rs0: ((repr & 0b000000_10000_00000_00000_00000_000000) >> 25).truncate(),
co_rs1: ((repr & 0b000000_01111_00000_00000_00000_000000) >> 21).truncate(),
co_n : ((repr & 0b000011_00000_00000_00000_00000_000000) >> 26).truncate(),
pos: pos.0,
}
}
}

View File

@ -47,7 +47,9 @@
array_map,
const_mut_refs,
core_intrinsics,
const_assume
const_assume,
bindings_after_at,
array_value_iter
)]
// Lints
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
@ -98,6 +100,9 @@
#![allow(clippy::if_not_else)]
// This lint triggers when using `assert`s and `todo`s, which is unsuitable for this project
#![allow(clippy::panic_in_result_fn)]
// A `match Option / Result / Bool` can sometimes look cleaner than a `if let / else`
#![allow(clippy::single_match_else, clippy::match_bool)]
// Modules
pub mod ascii_str_arr;