Renamed SimpleInstruction to BasicInstruction.

Added `BasicInst` type to replace `BasicInstruction` eventually.
Replaced `Formatter<'_>` with just `Formatter` where possible.
Added a `Display` impl for `Header`.
`dw` instructions now have their argument replaced instead of just having a comment.
This commit is contained in:
Filipe Rodrigues 2020-11-03 12:05:54 +00:00
parent e62561f228
commit 0e58b3df15
28 changed files with 1730 additions and 89 deletions

View File

@ -82,9 +82,9 @@ use dcb::{
data::DataTable,
func::FuncTable,
instruction::{
Directive,
BasicInstruction, Directive,
PseudoInstruction::{self, Nop},
Raw, SimpleInstruction,
Raw,
},
Func, Instruction, Pos,
},
@ -107,6 +107,8 @@ fn main() -> Result<(), anyhow::Error> {
log::debug!("Deserializing executable");
let exe = dcb::game::Exe::deserialize(&mut game_file).context("Unable to parse game executable")?;
log::info!("Header:\n{}\n", exe.header);
// Get all instructions
log::debug!("Retrieving all instructions");
let instructions: Vec<(Pos, Instruction)> = Instruction::new_iter(
@ -182,8 +184,9 @@ fn main() -> Result<(), anyhow::Error> {
}
// Space out data if it had a name
if let Some(data) = data_pos.get(cur_pos) {
if let Some(data) = data_pos.get(cur_pos - 1) {
if data.end_pos() == cur_pos && !data.name.is_empty() {
println!("####################");
println!();
}
}
@ -207,6 +210,8 @@ fn main() -> Result<(), anyhow::Error> {
}
if let Some(data) = data_pos.get(cur_pos) {
if data.pos == cur_pos {
println!();
println!("####################");
println!("{}:", data.name);
for description in data.desc.lines() {
println!("# {}", description);
@ -215,19 +220,22 @@ fn main() -> Result<(), anyhow::Error> {
}
// Print the instruction and it's location.
print!("{cur_pos:#010x}:\t");
match cur_func {
Some(cur_func) => print!("{:#05x}:\t", cur_pos - cur_func.start_pos),
None => print!("{cur_pos:#010x}:\t"),
}
match instruction {
Instruction::Simple(
SimpleInstruction::J { target } |
SimpleInstruction::Jal { target } |
SimpleInstruction::Beq { target, .. } |
SimpleInstruction::Bne { target, .. } |
SimpleInstruction::Bltz { target, .. } |
SimpleInstruction::Bgez { target, .. } |
SimpleInstruction::Bgtz { target, .. } |
SimpleInstruction::Blez { target, .. } |
SimpleInstruction::Bltzal { target, .. } |
SimpleInstruction::Bgezal { target, .. },
Instruction::Basic(
BasicInstruction::J { target } |
BasicInstruction::Jal { target } |
BasicInstruction::Beq { target, .. } |
BasicInstruction::Bne { target, .. } |
BasicInstruction::Bltz { target, .. } |
BasicInstruction::Bgez { target, .. } |
BasicInstruction::Bgtz { target, .. } |
BasicInstruction::Blez { target, .. } |
BasicInstruction::Bltzal { target, .. } |
BasicInstruction::Bgezal { target, .. },
) |
Instruction::Pseudo(
PseudoInstruction::B { target } | PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. },
@ -256,7 +264,8 @@ fn main() -> Result<(), anyhow::Error> {
PseudoInstruction::SwlImm { offset: target, .. } |
PseudoInstruction::SwImm { offset: target, .. } |
PseudoInstruction::SwrImm { offset: target, .. },
) => match functions
) |
Instruction::Directive(Directive::Dw(target)) => match functions
.get(Pos(*target))
.map(|func| (func.start_pos, &func.name))
.or_else(|| data_pos.get(Pos(*target)).map(|data| (data.pos, &data.name)))
@ -277,22 +286,6 @@ fn main() -> Result<(), anyhow::Error> {
_ => print!("{instruction}"),
}
// Comment any `dw` instructions that are function, data or string pointers
if let Instruction::Directive(Directive::Dw(target)) = instruction {
if let Some(func) = functions.get(Pos(*target)) {
print!(" # {}", func.name);
} else if let Some(data) = data_pos.get(Pos(*target)) {
if data.pos == Pos(*target) {
print!(" # {}", data.name);
} else {
let offset = Pos(*target) - data.pos;
if offset > 0 {
print!(" # {} + {offset:#x}", data.name);
}
}
}
}
// Append any comments in this line
if let Some(cur_func) = cur_func {
if let Some(comment) = cur_func.comments.get(&cur_pos) {

View File

@ -20,6 +20,7 @@ int-conv = "0.1"
bitmatch = "0.1"
either = "1.6"
smallvec = "1.4"
num_enum = "0.5"
# Serde
serde = { version = "1.0", features = ["derive"] }

View File

@ -306,13 +306,13 @@ impl<const N: usize> Default for AsciiStrArr<N> {
}
impl<const N: usize> fmt::Debug for AsciiStrArr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
AsciiStr::fmt(self.as_ascii(), f)
}
}
impl<const N: usize> fmt::Display for AsciiStrArr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
AsciiStr::fmt(self.as_ascii(), f)
}
}

View File

@ -11,9 +11,6 @@
//! All these strings must only contain ascii characters, thus on read and on write, if any
//! strings contain non-ascii characters, an error will occur
// Lints
//#![allow(clippy::missing_docs_in_private_items)] // A lot of our private items are simple digimon types, so they don't need documentation
// Modules
pub mod card;
pub mod deck;

View File

@ -20,7 +20,7 @@ pub use iter::WithInstructionsIter;
use super::Func;
use crate::{
game::exe::{
instruction::{Directive, PseudoInstruction, Register, SimpleInstruction},
instruction::{BasicInstruction, Directive, PseudoInstruction, Register},
Instruction, Pos,
},
util::discarding_sorted_merge_iter::DiscardingSortedMergeIter,
@ -84,15 +84,15 @@ impl FuncTable {
#[allow(clippy::too_many_lines)] // TODO: Refactor?
#[allow(clippy::enum_glob_use)] // It's only for this function
pub fn from_instructions<'a>(instructions: &(impl Iterator<Item = (Pos, &'a Instruction)> + Clone)) -> Self {
use Instruction::{Pseudo, Simple};
use BasicInstruction::*;
use Instruction::{Basic, Pseudo};
use PseudoInstruction::*;
use SimpleInstruction::*;
// Get all returns
let returns: BTreeSet<Pos> = instructions
.clone()
.filter_map(|(pos, instruction)| match instruction {
Simple(Jr { rs: Register::Ra }) => Some(pos),
Basic(Jr { rs: Register::Ra }) => Some(pos),
_ => None,
})
.collect();
@ -101,7 +101,7 @@ impl FuncTable {
let tailcalls: BTreeSet<Pos> = instructions
.clone()
.filter_map(|(pos, instruction)| match instruction {
Simple(J { .. } | Jr { .. }) => Some(pos),
Basic(J { .. } | Jr { .. }) => Some(pos),
_ => None,
})
.collect();
@ -110,7 +110,7 @@ impl FuncTable {
let labels: BTreeSet<Pos> = instructions
.clone()
.filter_map(|(_, instruction)| match instruction {
Simple(
Basic(
J { target } |
Beq { target, .. } |
Bne { target, .. } |
@ -131,7 +131,7 @@ impl FuncTable {
let function_entries: BTreeSet<Pos> = instructions
.clone()
.filter_map(|(_, instruction)| match instruction {
Simple(Jal { target }) => Some(*target),
Basic(Jal { target }) => Some(*target),
Instruction::Directive(Directive::Dw(target)) => Some(Pos(*target)),
_ => None,
})
@ -179,7 +179,7 @@ impl FuncTable {
// TODO: Generalize this in `Instruction` as a method that
// returns all registers used maybe.
match instruction {
Simple(Sb { rt, rs, .. } | Lb { rt, rs, .. } | Lbu { rt, rs, .. }) => {
Basic(Sb { rt, rs, .. } | Lb { rt, rs, .. } | Lbu { rt, rs, .. }) => {
if let Some(idx) = rt.arg_idx() {
if arguments[idx].is_none() {
arguments[idx] = Some("u8");
@ -199,7 +199,7 @@ impl FuncTable {
}
},
Simple(Sh { rt, rs, .. } | Lh { rt, rs, .. } | Lhu { rt, rs, .. }) => {
Basic(Sh { rt, rs, .. } | Lh { rt, rs, .. } | Lhu { rt, rs, .. }) => {
if let Some(idx) = rt.arg_idx() {
if arguments[idx].is_none() {
arguments[idx] = Some("u16");
@ -219,7 +219,7 @@ impl FuncTable {
}
},
Simple(
Basic(
Swl { rt, rs, .. } | Sw { rt, rs, .. } | Swr { rt, rs, .. } | Lwl { rt, rs, .. } | Lw { rt, rs, .. } | Lwr { rt, rs, .. },
) => {
if let Some(idx) = rt.arg_idx() {
@ -244,7 +244,7 @@ impl FuncTable {
}
},
Simple(
Basic(
Addi { rt, rs, .. } |
Addiu { rt, rs, .. } |
Slti { rt, rs, .. } |
@ -274,7 +274,7 @@ impl FuncTable {
}
},
Simple(
Basic(
Add { rd, rs, rt } |
Addu { rd, rs, rt } |
Sub { rd, rs, rt } |
@ -306,7 +306,7 @@ impl FuncTable {
}
},
Simple(
Basic(
Sll { rd, rt, .. } |
Srl { rd, rt, .. } |
Sra { rd, rt, .. } |
@ -327,7 +327,7 @@ impl FuncTable {
}
},
Simple(Jalr { rd, rs }) => {
Basic(Jalr { rd, rs }) => {
if let Some(idx) = rd.arg_idx() {
if arguments[idx].is_none() {
arguments[idx] = Some("u32");
@ -340,7 +340,7 @@ impl FuncTable {
}
},
Simple(Lui { rt, .. }) => {
Basic(Lui { rt, .. }) => {
if let Some(idx) = rt.arg_idx() {
if arguments[idx].is_none() {
arguments[idx] = Some("u32");
@ -348,7 +348,7 @@ impl FuncTable {
}
},
Simple(Mfhi { rd } | Mflo { rd }) => {
Basic(Mfhi { rd } | Mflo { rd }) => {
if let Some(idx) = rd.arg_idx() {
if arguments[idx].is_none() {
arguments[idx] = Some("u32");
@ -356,7 +356,7 @@ impl FuncTable {
}
},
Simple(
Basic(
Bltz { rs, .. } |
Bgez { rs, .. } |
Bgtz { rs, .. } |

View File

@ -4,6 +4,8 @@
pub mod error;
// Exports
use std::fmt;
pub use error::{FromBytesError, ToBytesError};
// Import
@ -49,7 +51,34 @@ pub struct Header {
pub initial_sp_offset: u32,
/// Executable region marker
pub marker: AsciiStrArr<0x7b4>,
pub marker: AsciiStrArr<0x7b3>,
}
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self {
ref initial_pc,
ref initial_gp,
ref dest,
ref size,
ref memfill_start,
ref memfill_size,
ref initial_sp_base,
ref initial_sp_offset,
ref marker,
..
} = self;
write!(
f,
"PC: {initial_pc:#x}
GP: {initial_gp:#x}
Destination: {dest:#x} / size: {size:#x}
Memfill: {memfill_start:#X} / size: {memfill_size:#x}
SP: {initial_sp_base:#x} / offset: {initial_sp_offset:#x}
Marker: {marker:?}"
)
}
}
impl Header {
@ -76,8 +105,8 @@ impl Bytes for Header {
memfill_size : [0x4],
initial_sp_base : [0x4],
initial_sp_offset: [0x4],
_zero2 : [0x13],
marker : [0x7b5],
_zero2 : [0x14],
marker : [0x7b4],
);
// If the magic is wrong, return Err

View File

@ -1,18 +1,18 @@
//! Psx cpu instructions
// Modules
pub mod basic;
pub mod directive;
pub mod pseudo;
pub mod raw;
pub mod reg;
pub mod simple;
// Exports
pub use basic::BasicInstruction;
pub use directive::Directive;
pub use pseudo::PseudoInstruction;
pub use raw::{FromRawIter, Raw};
pub use reg::Register;
pub use simple::SimpleInstruction;
// Imports
use crate::game::exe::Pos;
@ -21,8 +21,8 @@ use crate::game::exe::Pos;
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(derive_more::Display)]
pub enum Instruction {
/// A simple instruction
Simple(SimpleInstruction),
/// A basic instruction
Basic(BasicInstruction),
/// A pseudo instruction
Pseudo(PseudoInstruction),
@ -111,9 +111,9 @@ impl<I: Iterator<Item = Raw> + Clone> Iterator for Iter<I> {
return self.try_next_from(Instruction::Directive);
}
// Else try to decode it as a pseudo, simple or directive, in that order.
// Else try to decode it as a pseudo, basic 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::Basic))
.or_else(|| self.try_next_from(Instruction::Directive))
}
}

View File

@ -1,19 +1,130 @@
//! Raw instructions
//! Basic instructions
//!
//! This modules defines all the basic instructions from the psx.
//! They are all 1 word (`u32`) long.
// Lints
// #[allow(clippy::similar_names)]
// Modules
pub mod alu_imm;
pub mod cond;
pub mod jmp;
pub mod load;
pub mod special;
pub mod store;
// Exports
pub use alu_imm::{AluImmInst, AluImmRaw};
pub use cond::{CondInst, CondRaw};
pub use jmp::{JmpInst, JmpRaw};
pub use load::{LoadInst, LoadRaw};
pub use special::{SpecialInst, SpecialRaw};
pub use store::{StoreInst, StoreRaw};
// Imports
use super::{FromRawIter, Raw, Register};
use crate::{game::exe::Pos, util::SignedHex};
use bitmatch::bitmatch;
use int_conv::{SignExtended, Signed, Truncate, Truncated};
use int_conv::{SignExtended, Signed, Truncate, Truncated, ZeroExtended};
/// All basic instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum BasicInst {
/// Store
Store(StoreInst),
/// Load
Load(LoadInst),
/// Special
Special(SpecialInst),
/// Condition
Cond(CondInst),
/// Jump
Jmp(JmpInst),
/// Alu immediate
AluImm(AluImmInst),
/// Load upper immediate
#[display(fmt = "lui {dest}, {value:#x}")]
Lui {
/// Destination
dest: Register,
/// Value
value: u16,
},
}
impl BasicInst {
// TODO: MAybe extract the strings if the bitmatch macro allows for it.
/// Decodes this instruction
#[must_use]
#[bitmatch::bitmatch]
#[allow(clippy::many_single_char_names)] // `bitmatch` can only output single character names.
pub fn decode(raw: u32) -> Option<Self> {
Some(
#[bitmatch]
#[allow(unused_variables)] // TODO: Remove once `CopInst` is implemented.
match raw {
"000000_sssss_ttttt_ddddd_iiiii_ffffff" => Self::Special(SpecialInst::decode(SpecialRaw { s, t, d, i, f })?),
"00001p_iiiii_iiiii_iiiii_iiiii_iiiiii" => Self::Jmp(JmpInst::decode(JmpRaw { p, i })),
"000ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cond(CondInst::decode(CondRaw { p, s, t, i })?),
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Self::Lui {
dest: Register::new(t)?,
value: i.truncated::<u16>(),
},
"001ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::AluImm(AluImmInst::decode(AluImmRaw { p, s, t, i })?),
"100ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Store(StoreInst::decode(StoreRaw { p, s, t, i })?),
"101ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Load(LoadInst::decode(LoadRaw { p, s, t, i })?),
/*
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => Self::Cop(),
"0100nn_00000_ttttt_ddddd_?????_000000" => Self::Cop(),
"0100nn_00010_ttttt_ddddd_?????_000000" => Self::Cop(),
"0100nn_00100_ttttt_ddddd_?????_000000" => Self::Cop(),
"0100nn_00110_ttttt_ddddd_?????_000000" => Self::Cop(),
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => Self::Cop(),
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => Self::Cop(),
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cop(),
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cop(),
*/
_ => return None,
},
)
}
/// Encodes this instruction
#[must_use]
#[bitmatch]
pub fn encode(self) -> u32 {
#[rustfmt::skip]
match self {
Self::Special(inst) => { let SpecialRaw { s, t, d, i, f } = inst.encode(); bitpack!("000000_sssss_ttttt_ddddd_iiiii_ffffff") },
Self::Jmp (inst) => { let JmpRaw { p, i } = inst.encode(); bitpack!("00001p_iiiii_iiiii_iiiii_iiiii_iiiiii") },
Self::Cond (inst) => { let CondRaw { p, s, t, i } = inst.encode(); bitpack!("000ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
Self::AluImm (inst) => { let AluImmRaw { p, s, t, i } = inst.encode(); bitpack!("001ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
Self::Store (inst) => { let StoreRaw { p, s, t, i } = inst.encode(); bitpack!("100ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
Self::Load (inst) => { let LoadRaw { p, s, t, i } = inst.encode(); bitpack!("101ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
Self::Lui { dest, value } => {
let t = dest.idx();
let i = value.zero_extended::<u32>();
bitpack!("001111_?????_ttttt_iiiii_iiiii_iiiiii")
},
}
}
}
/// All simple instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[allow(clippy::missing_docs_in_private_items)] // They're mostly register and immediate names.
pub enum SimpleInstruction {
pub enum BasicInstruction {
/// Store byte
#[display(fmt = "sb {rt}, {:#x}({rs})", "SignedHex(offset)")]
Sb { rt: Register, rs: Register, offset: i16 },
@ -287,22 +398,27 @@ pub enum SimpleInstruction {
Break { imm: u32 },
}
impl SimpleInstruction {
impl BasicInstruction {
/// Decodes an instruction from it's raw representation
#[bitmatch]
#[allow(clippy::cognitive_complexity)] // It's just a big match, not much we can do about it.
fn decode_repr(Raw { repr, pos }: Raw) -> Option<Self> {
fn decode_repr(raws: &[u32], pos: Pos) -> Option<(Self, &[u32])> {
#[allow(clippy::enum_glob_use)] // It's local to this function and REALLY reduces on the noise
use SimpleInstruction::*;
use BasicInstruction::*;
/// Alias for `Register::new`
fn reg(idx: u32) -> Option<Register> {
Register::new(idx.truncated())
}
let (raw, raws) = match raws {
[raw, raws @ ..] => (raw, raws),
_ => return None,
};
#[rustfmt::skip]
let instruction = #[bitmatch]
match repr {
match raw {
"000000_?????_ttttt_ddddd_iiiii_000000" => Sll { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
"000000_?????_ttttt_ddddd_iiiii_000010" => Srl { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
"000000_?????_ttttt_ddddd_iiiii_000011" => Sra { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
@ -349,8 +465,8 @@ impl SimpleInstruction {
"000100_sssss_ttttt_iiiii_iiiii_iiiiii" => Beq { rs: reg(s)?, rt: reg(t)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000101_sssss_ttttt_iiiii_iiiii_iiiiii" => Bne { rs: reg(s)?, rt: reg(t)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000110_sssss_?????_iiiii_iiiii_iiiiii" => Blez { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000111_sssss_?????_iiiii_iiiii_iiiiii" => Bgtz { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000110_sssss_?????_iiiii_iiiii_iiiiii" => Blez { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000111_sssss_?????_iiiii_iiiii_iiiiii" => Bgtz { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"001000_sssss_ttttt_iiiii_iiiii_iiiiii" => Addi { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
"001001_sssss_ttttt_iiiii_iiiii_iiiiii" => Addiu { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
@ -359,7 +475,7 @@ impl SimpleInstruction {
"001100_sssss_ttttt_iiiii_iiiii_iiiiii" => Andi { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
"001101_sssss_ttttt_iiiii_iiiii_iiiiii" => Ori { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
"001110_sssss_ttttt_iiiii_iiiii_iiiiii" => Xori { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Lui { rt: reg(t)? , imm: i.truncated() },
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Lui { rt: reg(t)? , imm: i.truncated() },
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
@ -390,16 +506,114 @@ impl SimpleInstruction {
_ => return None,
};
Some(instruction)
Some((instruction, raws))
}
/// Encodes an instruction.
#[bitmatch]
#[must_use]
pub fn encode_repr(self, pos: Pos) -> Raw {
#[allow(clippy::enum_glob_use)] // It's local to this function and REALLY reduces on the noise
use BasicInstruction::*;
#[rustfmt::skip]
let repr = match self {
Sll { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000000") },
Srl { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000010") },
Sra { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000011") },
Sllv { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000100") },
Srlv { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000110") },
Srav { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000111") },
Jr { rs } => { let s = rs.idx() ; bitpack!("000000_sssss_00000_00000_00000_001000") },
Jalr { rd, rs } => { let (d, s) = (rd.idx(), rs.idx()); bitpack!("000000_sssss_00000_ddddd_00000_001001") },
Syscall { imm: i } => { bitpack!("000000_iiiii_iiiii_iiiii_iiiii_001100") },
Break { imm: i } => { bitpack!("000000_iiiii_iiiii_iiiii_iiiii_001101") },
Mfhi { rd } => { let d = rd.idx(); bitpack!("000000_00000_00000_ddddd_00000_010000") },
Mthi { rs } => { let s = rs.idx(); bitpack!("000000_sssss_00000_00000_00000_010001") },
Mflo { rd } => { let d = rd.idx(); bitpack!("000000_00000_00000_ddddd_00000_010010") },
Mtlo { rs } => { let s = rs.idx(); bitpack!("000000_sssss_00000_00000_00000_010011") },
Mult { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011000") },
Multu { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011001") },
Div { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011010") },
Divu { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011011") },
Add { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100000") },
Addu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100001") },
Sub { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100010") },
Subu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100011") },
And { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100100") },
Or { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100101") },
Xor { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100110") },
Nor { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100111") },
Slt { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_101010") },
Sltu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_101011") },
Bltz { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
Bgez { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
Bltzal { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
Bgezal { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
J { target } => { let i = (target - (pos & 0x0fff_ffff)) / 4; bitpack!("000010_iiiii_iiiii_iiiii_iiiii_iiiiii") },
Jal { target } => { let i = (target - (pos & 0x0fff_ffff)) / 4; bitpack!("000011_iiiii_iiiii_iiiii_iiiii_iiiiii") },
Beq { rs, rt, target } => { let (s, t) = (rs.idx(), rt.idx()); let i = (target - pos) / 4 - 1; bitpack!("000100_sssss_ttttt_iiiii_iiiii_iiiiii") },
Bne { rs, rt, target } => { let (s, t) = (rs.idx(), rt.idx()); let i = (target - pos) / 4 - 1; bitpack!("000101_sssss_ttttt_iiiii_iiiii_iiiiii") },
Blez { rs , target } => { let s = rs.idx() ; let i = (target - pos) / 4 - 1; bitpack!("000110_sssss_00000_iiiii_iiiii_iiiiii") },
Bgtz { rs , target } => { let s = rs.idx() ; let i = (target - pos) / 4 - 1; bitpack!("000111_sssss_00000_iiiii_iiiii_iiiiii") },
Addi { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001000_sssss_ttttt_iiiii_iiiii_iiiiii") },
Addiu { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001001_sssss_ttttt_iiiii_iiiii_iiiiii") },
Slti { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001010_sssss_ttttt_iiiii_iiiii_iiiiii") },
Sltiu { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001011_sssss_ttttt_iiiii_iiiii_iiiiii") },
Andi { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001100_sssss_ttttt_iiiii_iiiii_iiiiii") },
Ori { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001101_sssss_ttttt_iiiii_iiiii_iiiiii") },
Xori { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001110_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lui { rt, imm: i } => { let t = rt.idx(); bitpack!("001111_00000_ttttt_iiiii_iiiii_iiiiii") },
CopN { n, imm: i } => { bitpack!("0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii") },
MfcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00000_ttttt_ddddd_00000_000000") },
CfcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00010_ttttt_ddddd_00000_000000") },
MtcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00100_ttttt_ddddd_00000_000000") },
CtcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00110_ttttt_ddddd_00000_000000") },
BcNf { n, target: i } => { bitpack!("0100nn_01000_00000_iiiii_iiiii_iiiiii") },
BcNt { n, target: i } => { bitpack!("0100nn_01000_00001_iiiii_iiiii_iiiiii") },
Lb { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100000_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lh { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100001_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lwl { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100010_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lw { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100011_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lbu { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100100_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lhu { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100101_sssss_ttttt_iiiii_iiiii_iiiiii") },
Lwr { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100110_sssss_ttttt_iiiii_iiiii_iiiiii") },
Sb { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101000_sssss_ttttt_iiiii_iiiii_iiiiii") },
Sh { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101001_sssss_ttttt_iiiii_iiiii_iiiiii") },
Swl { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101010_sssss_ttttt_iiiii_iiiii_iiiiii") },
Sw { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101011_sssss_ttttt_iiiii_iiiii_iiiiii") },
Swr { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101110_sssss_ttttt_iiiii_iiiii_iiiiii") },
LwcN { n, rs, rt, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("1100nn_sssss_ttttt_iiiii_iiiii_iiiiii") },
SwcN { n, rs, rt, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("1110nn_sssss_ttttt_iiiii_iiiii_iiiiii") },
};
Raw { repr, pos }
}
}
impl FromRawIter for SimpleInstruction {
impl FromRawIter for BasicInstruction {
type Decoded = Option<(Pos, Self)>;
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
let raw = iter.next()?;
let instruction = Self::decode_repr(raw)?;
let (instruction, _) = Self::decode_repr(std::slice::from_ref(&raw.repr), raw.pos)?;
Some((raw.pos, instruction))
}
}

View File

@ -0,0 +1,108 @@
//! Alu immediate instructions
// Imports
use crate::{game::exe::instruction::Register, util::SignedHex};
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Alu register opcode (lower 3 bits)
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum AluImmOp {
/// Add
Add = 0x0,
/// Add unsigned
AddUnsigned = 0x1,
/// Set less than
SetLessThan = 0x2,
/// Set less than unsigned
SetLessThanUnsigned = 0x3,
/// And
And = 0x4,
/// Or
Or = 0x5,
/// Xor
Xor = 0x6,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluImmRaw {
/// Opcode (lower 3 bits)
pub p: u32,
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Immediate
pub i: u32,
}
/// Alu register instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluImmInst {
/// Destination register, `rd`
pub dest: Register,
/// Lhs argument, `rs`
pub lhs: Register,
/// Rhs immediate argument
pub rhs: u32,
/// Opcode
pub op: AluImmOp,
}
impl AluImmInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: AluImmRaw) -> Option<Self> {
let op = AluImmOp::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.t)?,
lhs: Register::new(raw.s)?,
rhs: raw.i,
op,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> AluImmRaw {
let p = u8::from(self.op).zero_extended::<u32>();
let s = self.lhs.idx();
let t = self.dest.idx();
let i = self.rhs;
AluImmRaw { p, s, t, i }
}
}
impl fmt::Display for AluImmInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { op, dest, lhs, rhs } = self;
#[rustfmt::skip]
match op {
AluImmOp::Add => write!(f, "addi {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmOp::AddUnsigned => write!(f, "addiu {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmOp::SetLessThan => write!(f, "slti {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmOp::SetLessThanUnsigned => write!(f, "sltiu {dest}, {lhs}, {rhs:#x}"),
AluImmOp::And => write!(f, "andi {dest}, {lhs}, {rhs:#x}"),
AluImmOp::Or => write!(f, "ori {dest}, {lhs}, {rhs:#x}"),
AluImmOp::Xor => write!(f, "xori {dest}, {lhs}, {rhs:#x}"),
}
}
}

View File

@ -0,0 +1,131 @@
//! Condition branches
// Imports
use crate::{game::exe::instruction::Register, util::SignedHex};
use int_conv::{Signed, Truncated, ZeroExtended};
use std::fmt;
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct CondRaw {
/// Opcode (lower 3 bits)
pub p: u32,
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Immediate
pub i: u32,
}
/// Condition kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum CondKind {
/// Equal
Equal(Register),
/// Not equal
NotEqual(Register),
/// Less than or zero
LessOrEqualZero,
/// Greater than zero
GreaterThanZero,
/// Less than zero
LessThanZero,
/// Greater than or zero
GreaterOrEqualZero,
/// Less than zero and link
LessThanZeroLink,
/// Greater than or zero and link
GreaterOrEqualZeroLink,
}
/// Condition instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct CondInst {
/// Argument, `rs`
pub arg: Register,
/// Offset
pub offset: i16,
/// Kind
pub kind: CondKind,
}
impl CondInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: CondRaw) -> Option<Self> {
let kind = match raw.p {
0x1 => match raw.t {
0b00000 => CondKind::LessThanZero,
0b00001 => CondKind::GreaterOrEqualZero,
0b10000 => CondKind::LessThanZeroLink,
0b10001 => CondKind::GreaterOrEqualZeroLink,
_ => return None,
},
0x4 => CondKind::Equal(Register::new(raw.t)?),
0x5 => CondKind::NotEqual(Register::new(raw.t)?),
0x6 => CondKind::LessOrEqualZero,
0x7 => CondKind::GreaterThanZero,
_ => return None,
};
Some(Self {
arg: Register::new(raw.s)?,
offset: raw.i.truncated::<u16>().as_signed(),
kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> CondRaw {
#[rustfmt::skip]
let (p, t) = match self.kind {
CondKind::Equal(reg) => (0x4, reg.idx()),
CondKind::NotEqual(reg) => (0x5, reg.idx()),
CondKind::LessOrEqualZero => (0x6, 0),
CondKind::GreaterThanZero => (0x7, 0),
CondKind::LessThanZero => (0x1, 0b00000),
CondKind::GreaterOrEqualZero => (0x1, 0b00001),
CondKind::LessThanZeroLink => (0x1, 0b10000),
CondKind::GreaterOrEqualZeroLink => (0x1, 0b10001),
};
let s = self.arg.idx();
let i = self.offset.as_unsigned().zero_extended();
CondRaw { p, s, t, i }
}
}
// TODO: Fmt given `pc` / label
impl fmt::Display for CondInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { arg, offset, kind } = self;
#[rustfmt::skip]
match kind {
CondKind::Equal(reg) => write!(f, "beq {arg}, {reg}, {:#x}", SignedHex(offset)),
CondKind::NotEqual(reg) => write!(f, "bne {arg}, {reg}, {:#x}", SignedHex(offset)),
CondKind::LessOrEqualZero => write!(f, "blez {arg}, {:#x}" , SignedHex(offset)),
CondKind::GreaterThanZero => write!(f, "bgtz {arg}, {:#x}" , SignedHex(offset)),
CondKind::LessThanZero => write!(f, "bltz {arg}, {:#x}" , SignedHex(offset)),
CondKind::GreaterOrEqualZero => write!(f, "bgez {arg}, {:#x}" , SignedHex(offset)),
CondKind::LessThanZeroLink => write!(f, "bltzal {arg}, {:#x}" , SignedHex(offset)),
CondKind::GreaterOrEqualZeroLink => write!(f, "bgezal {arg}, {:#x}" , SignedHex(offset)),
}
}
}

View File

@ -0,0 +1,74 @@
//! Alu register instructions
// Imports
use std::fmt;
/// Alu register func (bottom bit)
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum JmpKind {
/// Jump
Jump,
/// Jump and link
Link,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct JmpRaw {
/// Opcode (bottom bit)
pub p: u32,
/// Immediate
pub i: u32,
}
/// Alu register instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct JmpInst {
/// Target
pub target: u32,
/// Kind
pub kind: JmpKind,
}
impl JmpInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: JmpRaw) -> Self {
let kind = match raw.p {
0 => JmpKind::Jump,
1 => JmpKind::Link,
_ => unreachable!("Received invalid bit in opcode."),
};
Self { target: raw.i, kind }
}
/// Encodes this instruction
#[must_use]
pub const fn encode(self) -> JmpRaw {
let p = match self.kind {
JmpKind::Jump => 0,
JmpKind::Link => 1,
};
JmpRaw { p, i: self.target }
}
}
// TODO: Format with `pc` / `label`.
impl fmt::Display for JmpInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { target, kind } = self;
let mnemonic = match kind {
JmpKind::Jump => "j",
JmpKind::Link => "jal",
};
write!(f, "{mnemonic} {target}")
}
}

View File

@ -0,0 +1,109 @@
//! Load instructions
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Load instruction opcode (lower 3 bits)
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum LoadOpcode {
/// Byte, `i8`
Byte = 0x0,
/// Half-word, `i16`
HalfWord = 0x1,
/// Word left-bits, `u32`
WordLeft = 0x2,
/// Word, `u32`
Word = 0x3,
/// Byte unsigned, `u8`
ByteUnsigned = 0x4,
/// Half-word unsigned, `u16`
HalfWordUnsigned = 0x5,
/// Word right-bits, `u32`
WordRight = 0x6,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct LoadRaw {
/// Opcode (lower 3 bits)
pub p: u32,
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Immediate
pub i: u32,
}
/// Load instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct LoadInst {
/// Source register, `rt`
pub source: Register,
/// Destination register, `rs`
pub dest: Register,
/// Destination offset.
pub offset: i16,
/// Opcode
pub op: LoadOpcode,
}
impl LoadInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: LoadRaw) -> Option<Self> {
let op = LoadOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
source: Register::new(raw.t)?,
dest: Register::new(raw.s)?,
offset: raw.i.truncated::<u16>().as_signed(),
op,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> LoadRaw {
let t = self.source.idx();
let s = self.dest.idx();
let i = self.offset.as_unsigned().zero_extended::<u32>();
let p = u8::from(self.op).zero_extended::<u32>();
LoadRaw { p, s, t, i }
}
}
impl fmt::Display for LoadInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { source, dest, offset, op } = self;
let mnemonic = match op {
LoadOpcode::Byte => "lb",
LoadOpcode::HalfWord => "lh",
LoadOpcode::WordLeft => "lwl",
LoadOpcode::Word => "lw",
LoadOpcode::ByteUnsigned => "lbu",
LoadOpcode::HalfWordUnsigned => "lhu",
LoadOpcode::WordRight => "lwr",
};
write!(f, "{mnemonic} {dest}, {offset}({source})")
}
}

View File

@ -0,0 +1,143 @@
//! ALU instructions
// Modules
pub mod alu_reg;
pub mod jmp;
pub mod mult;
pub mod shift;
pub mod sys;
// Exports
pub use alu_reg::{AluRegInst, AluRegRaw};
pub use jmp::{JmpInst, JmpRaw};
pub use mult::{MultInst, MultRaw};
pub use shift::{ShiftInst, ShiftRaw};
pub use sys::{SysInst, SysRaw};
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SpecialRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Immediate
pub i: u32,
/// Func
pub f: u32,
}
impl From<SpecialRaw> for ShiftRaw {
fn from(SpecialRaw { t, d, s, i, f }: SpecialRaw) -> Self {
Self { t, d, s, i, f }
}
}
impl From<ShiftRaw> for SpecialRaw {
fn from(ShiftRaw { t, d, s, i, f }: ShiftRaw) -> Self {
Self { t, d, s, i, f }
}
}
impl From<SpecialRaw> for JmpRaw {
fn from(SpecialRaw { d, s, f, .. }: SpecialRaw) -> Self {
Self { d, s, f }
}
}
impl From<JmpRaw> for SpecialRaw {
fn from(JmpRaw { d, s, f }: JmpRaw) -> Self {
Self { t: 0, d, s, i: 0, f }
}
}
impl From<SpecialRaw> for SysRaw {
fn from(SpecialRaw { t, d, s, i, f }: SpecialRaw) -> Self {
Self { t, d, s, i, f }
}
}
impl From<SysRaw> for SpecialRaw {
fn from(SysRaw { t, d, s, i, f }: SysRaw) -> Self {
Self { t, d, s, i, f }
}
}
impl From<SpecialRaw> for MultRaw {
fn from(SpecialRaw { t, d, s, f, .. }: SpecialRaw) -> Self {
Self { t, d, s, f }
}
}
impl From<MultRaw> for SpecialRaw {
fn from(MultRaw { t, d, s, f }: MultRaw) -> Self {
Self { t, d, s, i: 0, f }
}
}
impl From<SpecialRaw> for AluRegRaw {
fn from(SpecialRaw { t, d, s, f, .. }: SpecialRaw) -> Self {
Self { t, d, s, f }
}
}
impl From<AluRegRaw> for SpecialRaw {
fn from(AluRegRaw { t, d, s, f }: AluRegRaw) -> Self {
Self { t, d, s, i: 0, f }
}
}
/// Special instructions for `opcode: 0`
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum SpecialInst {
/// Shift instruction
Shift(ShiftInst),
/// Jump
Jmp(JmpInst),
/// Sys
Sys(SysInst),
/// Mult
Mult(MultInst),
/// Alu
Alu(AluRegInst),
}
impl SpecialInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: SpecialRaw) -> Option<Self> {
Some(match raw.f {
0x00..0x08 => Self::Shift(ShiftInst::decode(raw.into())?),
0x08..0x0c => Self::Jmp(JmpInst::decode(raw.into())?),
0x0c..0x10 => Self::Sys(SysInst::decode(raw.into())?),
0x10..0x20 => Self::Mult(MultInst::decode(raw.into())?),
0x20..0x30 => Self::Alu(AluRegInst::decode(raw.into())?),
0x30..0x40 => return None,
_ => unreachable!("Func was larger than 6 bits."),
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> SpecialRaw {
match self {
Self::Shift(inst) => inst.encode().into(),
Self::Jmp(inst) => inst.encode().into(),
Self::Sys(inst) => inst.encode().into(),
Self::Mult(inst) => inst.encode().into(),
Self::Alu(inst) => inst.encode().into(),
}
}
}

View File

@ -0,0 +1,121 @@
//! Alu register instructions
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Alu register func
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum AluRegFunc {
/// Add
Add = 0x20,
/// Add unsigned
AddUnsigned = 0x21,
/// Sub
Sub = 0x22,
/// Sub unsigned
SubUnsigned = 0x23,
/// And
And = 0x24,
/// Or
Or = 0x25,
/// Xor
Xor = 0x26,
/// Nor
Nor = 0x27,
/// Set less than
SetLessThan = 0x2a,
/// Set less than unsigned
SetLessThanUnsigned = 0x2b,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluRegRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Func
pub f: u32,
}
/// Alu register instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluRegInst {
/// Destination register, `rd`
pub dest: Register,
/// Lhs argument, `rs`
pub lhs: Register,
/// Rhs argument, `rt`
pub rhs: Register,
/// Function
pub func: AluRegFunc,
}
impl AluRegInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: AluRegRaw) -> Option<Self> {
let func = AluRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.d)?,
lhs: Register::new(raw.s)?,
rhs: Register::new(raw.t)?,
func,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> AluRegRaw {
let d = self.dest.idx();
let s = self.lhs.idx();
let t = self.rhs.idx();
let f = u8::from(self.func).zero_extended::<u32>();
AluRegRaw { f, t, d, s }
}
}
impl fmt::Display for AluRegInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { dest, lhs, rhs, func } = self;
let mnemonic = match func {
AluRegFunc::Add => "add",
AluRegFunc::AddUnsigned => "addu",
AluRegFunc::Sub => "sub",
AluRegFunc::SubUnsigned => "subu",
AluRegFunc::And => "and",
AluRegFunc::Or => "or",
AluRegFunc::Xor => "xor",
AluRegFunc::Nor => "nor",
AluRegFunc::SetLessThan => "slt",
AluRegFunc::SetLessThanUnsigned => "sltu",
};
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
}
}

View File

@ -0,0 +1,78 @@
//! Jumps
// Imports
use crate::game::exe::instruction::Register;
use std::fmt;
/// Jump kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum JmpKind {
/// Simple
Simple,
/// With link
Link(Register),
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct JmpRaw {
/// Rs
pub s: u32,
/// Rd
pub d: u32,
/// Func
pub f: u32,
}
/// Jump instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct JmpInst {
/// Target register, `rs`
pub target: Register,
/// Jump kind, `rs`if `jalr`.
pub kind: JmpKind,
}
impl JmpInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: JmpRaw) -> Option<Self> {
let kind = match raw.f {
0x8 => JmpKind::Simple,
0x9 => JmpKind::Link(Register::new(raw.d)?),
_ => return None,
};
Some(Self {
target: Register::new(raw.s)?,
kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> JmpRaw {
let s = self.target.idx();
let (d, f) = match self.kind {
JmpKind::Simple => (0, 0x8),
JmpKind::Link(d) => (d.idx(), 0x9),
};
JmpRaw { s, d, f }
}
}
impl fmt::Display for JmpInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { target, kind } = self;
match kind {
JmpKind::Simple => write!(f, "jr {target}"),
JmpKind::Link(link) => write!(f, "jalr {link}, {target}"),
}
}
}

View File

@ -0,0 +1,168 @@
//! Multiplications
// Imports
use crate::game::exe::instruction::Register;
use std::fmt;
/// Operation func
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum MultKind {
/// Multiplication
Mult,
/// Division
Div,
}
/// Operation mode
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum MultMode {
/// Signed
Signed,
/// Unsigned
Unsigned,
}
/// Multiplication register
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum MultReg {
/// Lo
Lo,
/// Hi
Hi,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct MultRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Func
pub f: u32,
}
/// Multiplication instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum MultInst {
/// Multiplication
Mult {
/// Kind
kind: MultKind,
/// Mode
mode: MultMode,
/// Lhs argument
lhs: Register,
/// Rhs argument
rhs: Register,
},
/// Move from
MoveFrom {
/// Destination
dest: Register,
/// Source
source: MultReg,
},
/// Move to
MoveTo {
/// Source
source: Register,
/// Destination
dest: MultReg,
},
}
impl MultInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: MultRaw) -> Option<Self> {
#[rustfmt::skip]
Some(match raw.f {
0x10 => Self::MoveFrom { dest: Register::new(raw.d)?, source: MultReg::Hi },
0x12 => Self::MoveFrom { dest: Register::new(raw.d)?, source: MultReg::Lo },
0x11 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Hi },
0x13 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Lo },
0x18 => Self::Mult { kind: MultKind::Mult, mode: MultMode:: Signed, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
0x19 => Self::Mult { kind: MultKind::Mult, mode: MultMode::Unsigned, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
0x1a => Self::Mult { kind: MultKind::Div , mode: MultMode:: Signed, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
0x1b => Self::Mult { kind: MultKind::Div , mode: MultMode::Unsigned, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
_ => return None,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> MultRaw {
match self {
Self::Mult { kind, mode, lhs, rhs } => MultRaw {
s: lhs.idx(),
t: rhs.idx(),
d: 0,
f: match (kind, mode) {
(MultKind::Mult, MultMode::Signed) => 0x18,
(MultKind::Mult, MultMode::Unsigned) => 0x19,
(MultKind::Div, MultMode::Signed) => 0x1a,
(MultKind::Div, MultMode::Unsigned) => 0x1b,
},
},
Self::MoveFrom { dest, source } => MultRaw {
s: 0,
t: 0,
d: dest.idx(),
f: match source {
MultReg::Hi => 0x10,
MultReg::Lo => 0x12,
},
},
Self::MoveTo { source, dest } => MultRaw {
s: source.idx(),
t: 0,
d: 0,
f: match dest {
MultReg::Hi => 0x11,
MultReg::Lo => 0x13,
},
},
}
}
}
impl fmt::Display for MultInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
#[rustfmt::skip]
Self::Mult { kind, mode, lhs, rhs } => match (kind, mode) {
(MultKind::Mult, MultMode:: Signed) => write!(f, "mult {lhs}, {rhs}"),
(MultKind::Mult, MultMode::Unsigned) => write!(f, "multu {lhs}, {rhs}"),
(MultKind::Div , MultMode:: Signed) => write!(f, "div {lhs}, {rhs}"),
(MultKind::Div , MultMode::Unsigned) => write!(f, "diu {lhs}, {rhs}"),
},
Self::MoveFrom { dest, source } => match source {
MultReg::Hi => write!(f, "mfhi {dest}"),
MultReg::Lo => write!(f, "mflo {dest}"),
},
Self::MoveTo { source, dest } => match dest {
MultReg::Hi => write!(f, "mthi {source}"),
MultReg::Lo => write!(f, "mtlo {source}"),
},
}
}
}

View File

@ -0,0 +1,84 @@
//! Shift instructions
// Modules
pub mod imm;
pub mod reg;
// Exports
pub use imm::{ShiftImmInst, ShiftImmRaw};
pub use reg::{ShiftRegInst, ShiftRegRaw};
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Immediate
pub i: u32,
/// Func
pub f: u32,
}
impl From<ShiftRaw> for ShiftImmRaw {
fn from(ShiftRaw { t, d, i, f, .. }: ShiftRaw) -> Self {
Self { t, d, i, f }
}
}
impl From<ShiftImmRaw> for ShiftRaw {
fn from(ShiftImmRaw { t, d, i, f }: ShiftImmRaw) -> Self {
Self { t, d, s: 0, i, f }
}
}
impl From<ShiftRaw> for ShiftRegRaw {
fn from(ShiftRaw { t, d, s, f, .. }: ShiftRaw) -> Self {
Self { t, d, s, f }
}
}
impl From<ShiftRegRaw> for ShiftRaw {
fn from(ShiftRegRaw { t, d, s, f }: ShiftRegRaw) -> Self {
Self { t, d, s, i: 0, f }
}
}
/// Shift instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum ShiftInst {
/// Register
Reg(ShiftRegInst),
/// Immediate
Imm(ShiftImmInst),
}
impl ShiftInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: ShiftRaw) -> Option<Self> {
Some(match raw.f {
0x0..0x4 => Self::Imm(ShiftImmInst::decode(raw.into())?),
0x4..0x8 => Self::Reg(ShiftRegInst::decode(raw.into())?),
_ => return None,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> ShiftRaw {
match self {
Self::Reg(inst) => inst.encode().into(),
Self::Imm(inst) => inst.encode().into(),
}
}
}

View File

@ -0,0 +1,93 @@
//! Immediate shifts
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Shift immediate instruction func
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum ShiftImmFunc {
/// Left logical
LeftLogical = 0x0,
/// Right logical
RightLogical = 0x2,
/// Right arithmetic
RightArithmetic = 0x3,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftImmRaw {
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Immediate
pub i: u32,
/// Func
pub f: u32,
}
/// Shift immediate instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftImmInst {
/// Source register, `rd`
pub source: Register,
/// Destination register, `rt`
pub dest: Register,
/// Immediate argument
pub arg: i16,
/// Function
pub func: ShiftImmFunc,
}
impl ShiftImmInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: ShiftImmRaw) -> Option<Self> {
let func = ShiftImmFunc::try_from(raw.f.truncated::<u8>()).ok()?;
Some(Self {
source: Register::new(raw.t)?,
dest: Register::new(raw.d)?,
arg: raw.i.truncated::<u16>().as_signed(),
func,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> ShiftImmRaw {
let t = self.source.idx();
let d = self.dest.idx();
let i = self.arg.as_unsigned().zero_extended::<u32>();
let f = u8::from(self.func).zero_extended::<u32>();
ShiftImmRaw { f, t, d, i }
}
}
impl fmt::Display for ShiftImmInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { source, dest, arg, func } = self;
let mnemonic = match func {
ShiftImmFunc::LeftLogical => "sll",
ShiftImmFunc::RightLogical => "srl",
ShiftImmFunc::RightArithmetic => "sra",
};
write!(f, "{mnemonic} {dest}, {source}, {arg}")
}
}

View File

@ -0,0 +1,93 @@
//! Register shifts
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Shift register instruction func
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum ShiftRegFunc {
/// Left logical
LeftLogical = 0x4,
/// Right logical
RightLogical = 0x6,
/// Right arithmetic
RightArithmetic = 0x7,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftRegRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Func
pub f: u32,
}
/// Shift register instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftRegInst {
/// Destination register, `rd`
pub dest: Register,
/// Lhs argument, `rt`
pub lhs: Register,
/// Rhs argument, `rs`
pub rhs: Register,
/// Function
pub func: ShiftRegFunc,
}
impl ShiftRegInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: ShiftRegRaw) -> Option<Self> {
let func = ShiftRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.d)?,
lhs: Register::new(raw.t)?,
rhs: Register::new(raw.s)?,
func,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> ShiftRegRaw {
let d = self.dest.idx();
let t = self.lhs.idx();
let s = self.rhs.idx();
let f = u8::from(self.func).zero_extended::<u32>();
ShiftRegRaw { f, t, d, s }
}
}
impl fmt::Display for ShiftRegInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { dest, lhs, rhs, func } = self;
let mnemonic = match func {
ShiftRegFunc::LeftLogical => "sllv",
ShiftRegFunc::RightLogical => "srlv",
ShiftRegFunc::RightArithmetic => "srav",
};
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
}
}

View File

@ -0,0 +1,89 @@
//! System calls
// Imports
use int_conv::{Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Sys instruction func
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum SysFunc {
/// Sys
Sys = 0xc,
/// Break
Break = 0xd,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SysRaw {
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Rd
pub d: u32,
/// Immediate
pub i: u32,
/// Func
pub f: u32,
}
/// Syscall instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SysInst {
/// Comment
pub comment: u32,
/// Function
pub func: SysFunc,
}
impl SysInst {
/// Decodes this instruction
#[must_use]
pub fn decode(SysRaw { t, d, s, i, f }: SysRaw) -> Option<Self> {
let s = s.truncated::<u8>();
let t = t.truncated::<u8>();
let d = d.truncated::<u8>();
let i = i.truncated::<u8>();
let comment = u32::from_be_bytes([s, t, d, i]);
let func = SysFunc::try_from(f.truncated::<u8>()).ok()?;
Some(Self { comment, func })
}
/// Encodes this instruction
#[must_use]
#[allow(clippy::many_single_char_names)] // `Raw` has single character names
pub fn encode(self) -> SysRaw {
let [s, t, d, i] = self.comment.to_be_bytes();
let s = s.zero_extended::<u32>();
let t = t.zero_extended::<u32>();
let d = d.zero_extended::<u32>();
let i = i.zero_extended::<u32>();
let f = u8::from(self.func).zero_extended::<u32>();
SysRaw { s, t, d, i, f }
}
}
impl fmt::Display for SysInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { func, comment } = self;
let mnemonic = match func {
SysFunc::Sys => "sys",
SysFunc::Break => "break",
};
write!(f, "{mnemonic} {comment}")
}
}

View File

@ -0,0 +1,106 @@
//! Store instructions
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Store instruction opcode (lower 3 bits)
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum StoreOpcode {
/// Byte, `u8`
Byte = 0x0,
/// Half-word, `u16`
HalfWord = 0x1,
/// Word left-bits, `u32`
WordLeft = 0x2,
/// Word, `u32`
Word = 0x3,
/// Word right-bits, `u32`
WordRight = 0x6,
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct StoreRaw {
/// Opcode (lower 3 bits)
pub p: u32,
/// Rs
pub s: u32,
/// Rt
pub t: u32,
/// Immediate
pub i: u32,
}
/// Store instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct StoreInst {
/// Source register, `rt`
pub source: Register,
/// Destination register, `rs`
pub dest: Register,
/// Destination offset.
pub offset: i16,
/// Opcode
pub op: StoreOpcode,
}
impl StoreInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: StoreRaw) -> Option<Self> {
let kind = StoreOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
source: Register::new(raw.t)?,
dest: Register::new(raw.s)?,
offset: raw.i.truncated::<u16>().as_signed(),
op: kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> StoreRaw {
let t = self.source.idx();
let s = self.dest.idx();
let i = self.offset.as_unsigned().zero_extended::<u32>();
let p = u8::from(self.op).zero_extended::<u32>();
StoreRaw { p, s, t, i }
}
}
impl fmt::Display for StoreInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self {
source,
dest,
offset,
op: kind,
} = self;
let mnemonic = match kind {
StoreOpcode::Byte => "sb",
StoreOpcode::HalfWord => "sh",
StoreOpcode::Word => "sw",
StoreOpcode::WordRight => "swr",
StoreOpcode::WordLeft => "swl",
};
write!(f, "{mnemonic} {dest}, {offset}({source})")
}
}

View File

@ -1,7 +1,7 @@
//! Pseudo instructions
// Imports
use super::{FromRawIter, Raw, Register, SimpleInstruction};
use super::{BasicInstruction, FromRawIter, Raw, Register};
use crate::{game::exe::Pos, util::SignedHex};
use int_conv::{Join, SignExtended, Signed, ZeroExtended};
@ -248,17 +248,17 @@ impl FromRawIter for PseudoInstruction {
#[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
#[allow(clippy::enum_glob_use)] // This reduces the amount of typing for simple instructions and registers
#[allow(clippy::enum_glob_use)] // This reduces the amount of typing for basic instructions and registers
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
use BasicInstruction::*;
use Register::*;
use SimpleInstruction::*;
// Get the first instruction
let (pos, instruction) = SimpleInstruction::decode(iter)?;
let (pos, instruction) = BasicInstruction::decode(iter)?;
let pseudo = match instruction {
Lui { imm: imm_hi, rt: prev_rt } => {
let iter_before = iter.clone();
match SimpleInstruction::decode(iter)?.1 {
match BasicInstruction::decode(iter)?.1 {
Addiu { imm: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Self::La {
rx: prev_rt,
target: self::hi_plus_lo(imm_lo, imm_hi),

View File

@ -19,7 +19,7 @@ pub struct Raw {
/// 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`]
/// Returned iterator from [`decode`].
type Decoded: IntoIterator<Item = (Pos, Self)>;
/// Attempts to decode an instruction from an iterator of raw instructions

View File

@ -31,7 +31,7 @@ macro_rules! generate_register {
/// Creates a new register index from a `u8`.
#[must_use]
pub const fn new(idx: u8) -> Option<Self> {
pub const fn new(idx: u32) -> Option<Self> {
match idx {
$(
$value => Some( Self::$variant ),
@ -40,6 +40,16 @@ macro_rules! generate_register {
_ => None,
}
}
/// Returns the index of this register
#[must_use]
pub const fn idx(self) -> u32 {
match self {
$(
Self::$variant => $value,
)*
}
}
}
impl From<Register> for usize {

View File

@ -128,7 +128,7 @@ impl std::ops::Sub<Data> for Data {
// Display
impl std::fmt::Display for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", u64::from(*self))
}
}

View File

@ -165,7 +165,7 @@ impl std::ops::Sub<Real> for Real {
// Display
impl std::fmt::Display for Real {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.as_u64())
}
}

View File

@ -58,5 +58,5 @@ impl_null_ascii_string!(
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32,
1972
1971
);

View File

@ -17,7 +17,7 @@ impl<'a, T> LowerHex for SignedHex<&'a T>
where
SignedHex<T>: LowerHex,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
<SignedHex<T> as LowerHex>::fmt(SignedHex::<T>::ref_cast(self.0), f)
}
}