mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-08 11:28:44 +00:00
Added instruction decoding to the executable.
This commit is contained in:
parent
46e794c1c5
commit
7d1ebd5fa3
@ -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};
|
||||
|
||||
125
dcb/src/game/exe/instruction.rs
Normal file
125
dcb/src/game/exe/instruction.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
137
dcb/src/game/exe/instruction/directive.rs
Normal file
137
dcb/src/game/exe/instruction/directive.rs
Normal 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,
|
||||
}))
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
36
dcb/src/game/exe/instruction/pos.rs
Normal file
36
dcb/src/game/exe/instruction/pos.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
385
dcb/src/game/exe/instruction/pseudo.rs
Normal file
385
dcb/src/game/exe/instruction/pseudo.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
27
dcb/src/game/exe/instruction/raw.rs
Normal file
27
dcb/src/game/exe/instruction/raw.rs
Normal 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;
|
||||
}
|
||||
135
dcb/src/game/exe/instruction/reg.rs
Normal file
135
dcb/src/game/exe/instruction/reg.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
829
dcb/src/game/exe/instruction/simple.rs
Normal file
829
dcb/src/game/exe/instruction/simple.rs
Normal 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,
|
||||
},
|
||||
}
|
||||
61
dcb/src/game/exe/instruction/simple/repr.rs
Normal file
61
dcb/src/game/exe/instruction/simple/repr.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user