Renamed some instructions' register names for consistency.

Made `BasicInst::Lui` it's own type.
Started working on `PseudoInst`.
Removed `dcb::game::exe` and `decompiler` binary temporarily.
Fixed the game file not being read / written correctly.
This commit is contained in:
Filipe Rodrigues 2020-11-05 15:59:59 +00:00
parent b974e89d74
commit 94bb2d780d
22 changed files with 788 additions and 736 deletions

View File

@ -12,9 +12,9 @@ path = "src/extractor/main.rs"
name = "patcher"
path = "src/patcher/main.rs"
[[bin]]
name = "decompiler"
path = "src/decompiler/main.rs"
# [[bin]]
# name = "decompiler"
# path = "src/decompiler/main.rs"
[dependencies]
# Dcb

View File

@ -82,7 +82,7 @@ use dcb::{
data::DataTable,
func::FuncTable,
instruction::{
BasicInstruction, Directive,
Directive,
PseudoInstruction::{self, Nop},
Raw,
},
@ -225,18 +225,18 @@ fn main() -> Result<(), anyhow::Error> {
None => print!("{cur_pos:#010x}:\t"),
}
match instruction {
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::Basic(
BasicInst::J { target } |
BasicInst::Jal { target } |
BasicInst::Beq { target, .. } |
BasicInst::Bne { target, .. } |
BasicInst::Bltz { target, .. } |
BasicInst::Bgez { target, .. } |
BasicInst::Bgtz { target, .. } |
BasicInst::Blez { target, .. } |
BasicInst::Bltzal { target, .. } |
BasicInst::Bgezal { target, .. },
) | */
Instruction::Pseudo(
PseudoInstruction::B { target } | PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. },
) => match functions

View File

@ -14,11 +14,11 @@
// Modules
pub mod card;
pub mod deck;
pub mod exe;
//pub mod exe;
pub mod validation;
// Exports
pub use card::{Digimon, Digivolve, Item, Table as CardTable};
pub use deck::{Deck, Table as DeckTable};
pub use exe::{Exe, Header as ExeHeader, Pos as ExePos};
//pub use exe::{Exe, Header as ExeHeader, Pos as ExePos};
pub use validation::{Validatable, Validation};

View File

@ -19,17 +19,10 @@ pub use iter::WithInstructionsIter;
// Imports
use super::Func;
use crate::{
game::exe::{
instruction::{BasicInstruction, Directive, PseudoInstruction, Register},
Instruction, Pos,
},
game::exe::{Instruction, Pos},
util::discarding_sorted_merge_iter::DiscardingSortedMergeIter,
};
use std::{
collections::{BTreeSet, HashMap},
fs::File,
iter::FromIterator,
};
use std::{collections::BTreeSet, fs::File, iter::FromIterator};
/// Function table
///
@ -83,11 +76,8 @@ impl FuncTable {
#[must_use]
#[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 BasicInstruction::*;
use Instruction::{Basic, Pseudo};
use PseudoInstruction::*;
pub fn from_instructions<'a>(_instructions: &(impl Iterator<Item = (Pos, &'a Instruction)> + Clone)) -> Self {
/*
// Get all returns
let returns: BTreeSet<Pos> = instructions
.clone()
@ -481,5 +471,7 @@ impl FuncTable {
}
})
.collect()
*/
todo!()
}
}

View File

@ -8,7 +8,7 @@ pub mod raw;
pub mod reg;
// Exports
pub use basic::BasicInstruction;
pub use basic::BasicInst;
pub use directive::Directive;
pub use pseudo::PseudoInstruction;
pub use raw::{FromRawIter, Raw};
@ -22,7 +22,7 @@ use crate::game::exe::Pos;
#[derive(derive_more::Display)]
pub enum Instruction {
/// A basic instruction
Basic(BasicInstruction),
Basic(BasicInst),
/// A pseudo instruction
Pseudo(PseudoInstruction),
@ -112,9 +112,12 @@ impl<I: Iterator<Item = Raw> + Clone> Iterator for Iter<I> {
}
// Else try to decode it as a pseudo, basic or directive, in that order.
todo!()
/*
self.try_next_from(Instruction::Pseudo)
.or_else(|| self.try_next_from(Instruction::Basic))
.or_else(|| self.try_next_from(Instruction::Directive))
*/
}
}

View File

@ -6,25 +6,23 @@
// Modules
pub mod alu_imm;
pub mod cond;
pub mod iter;
pub mod jmp;
pub mod load;
pub mod lui;
pub mod special;
pub mod store;
// Exports
pub use alu_imm::{AluImmInst, AluImmRaw};
pub use cond::{CondInst, CondRaw};
pub use iter::InstIter;
pub use jmp::{JmpInst, JmpRaw};
pub use load::{LoadInst, LoadRaw};
pub use lui::{LuiInst, LuiRaw};
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, ZeroExtended};
/// All basic instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
@ -48,14 +46,7 @@ pub enum BasicInst {
AluImm(AluImmInst),
/// Load upper immediate
#[display(fmt = "lui {dest}, {value:#x}")]
Lui {
/// Destination
dest: Register,
/// Value
value: u16,
},
Lui(LuiInst),
}
impl BasicInst {
@ -73,24 +64,21 @@ impl BasicInst {
"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>(),
},
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Self::Lui(LuiInst::decode(LuiRaw { t, i })?),
"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(),
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
"0100nn_00000_ttttt_ddddd_?????_000000" => MfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00010_ttttt_ddddd_?????_000000" => CfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00100_ttttt_ddddd_?????_000000" => MtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00110_ttttt_ddddd_?????_000000" => CtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => BcNf { n: n.truncate(), target: i.truncate() },
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => BcNt { n: n.truncate(), target: i.truncate() },
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => LwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => SwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
*/
_ => return None,
},
@ -99,521 +87,17 @@ impl BasicInst {
/// Encodes this instruction
#[must_use]
#[bitmatch]
#[bitmatch::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::Lui (inst) => { let LuiRaw { t, i } = inst.encode(); bitpack!("001111_00000_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 BasicInstruction {
/// Store byte
#[display(fmt = "sb {rt}, {:#x}({rs})", "SignedHex(offset)")]
Sb { rt: Register, rs: Register, offset: i16 },
/// Store half-word
#[display(fmt = "sh {rt}, {:#x}({rs})", "SignedHex(offset)")]
Sh { rt: Register, rs: Register, offset: i16 },
/// Store left word
#[display(fmt = "swl {rt}, {:#x}({rs})", "SignedHex(offset)")]
Swl { rt: Register, rs: Register, offset: i16 },
/// Store word
#[display(fmt = "sw {rt}, {:#x}({rs})", "SignedHex(offset)")]
Sw { rt: Register, rs: Register, offset: i16 },
/// Store right word
#[display(fmt = "swr {rt}, {:#x}({rs})", "SignedHex(offset)")]
Swr { rt: Register, rs: Register, offset: i16 },
/// Load byte
#[display(fmt = "lb {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lb { rt: Register, rs: Register, offset: i16 },
/// Load byte unsigned
#[display(fmt = "lbu {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lbu { rt: Register, rs: Register, offset: i16 },
/// Load half-word
#[display(fmt = "lh {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lh { rt: Register, rs: Register, offset: i16 },
/// Load half-word unsigned
#[display(fmt = "lhu {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lhu { rt: Register, rs: Register, offset: i16 },
/// Load left word
#[display(fmt = "lwl {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lwl { rt: Register, rs: Register, offset: i16 },
/// Load word
#[display(fmt = "lw {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lw { rt: Register, rs: Register, offset: i16 },
/// Load right word
#[display(fmt = "lwr {rt}, {:#x}({rs})", "SignedHex(offset)")]
Lwr { rt: Register, rs: Register, offset: i16 },
/// Add
#[display(fmt = "add {rd}, {rs}, {rt}")]
Add { rd: Register, rs: Register, rt: Register },
/// Add unsigned
#[display(fmt = "addu {rd}, {rs}, {rt}")]
Addu { rd: Register, rs: Register, rt: Register },
/// Sub
#[display(fmt = "sub {rd}, {rs}, {rt}")]
Sub { rd: Register, rs: Register, rt: Register },
/// Sub unsigned
#[display(fmt = "subu {rd}, {rs}, {rt}")]
Subu { rd: Register, rs: Register, rt: Register },
/// Add immediate
#[display(fmt = "addi {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Addi { rt: Register, rs: Register, imm: i16 },
/// Add immediate sign-extended
/// Note: _NOT_ Unsigned.
#[display(fmt = "addiu {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Addiu { rt: Register, rs: Register, imm: i16 },
/// Set less than
#[display(fmt = "slt {rd}, {rs}, {rt}")]
Slt { rd: Register, rs: Register, rt: Register },
/// Set less than unsigned
#[display(fmt = "sltu {rd}, {rs}, {rt}")]
Sltu { rd: Register, rs: Register, rt: Register },
/// Set less than immediate
#[display(fmt = "slti {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Slti { rt: Register, rs: Register, imm: i16 },
/// Set less than immediate unsigned
#[display(fmt = "sltiu {rt}, {rs}, {:#x}", "SignedHex(imm)")]
Sltiu { rt: Register, rs: Register, imm: i16 },
/// And
#[display(fmt = "and {rd}, {rs}, {rt}")]
And { rd: Register, rs: Register, rt: Register },
/// Or
#[display(fmt = "or {rd}, {rs}, {rt}")]
Or { rd: Register, rs: Register, rt: Register },
/// Xor
#[display(fmt = "xor {rd}, {rs}, {rt}")]
Xor { rd: Register, rs: Register, rt: Register },
/// Nor
#[display(fmt = "nor {rd}, {rs}, {rt}")]
Nor { rd: Register, rs: Register, rt: Register },
/// And immediate
#[display(fmt = "andi {rt}, {rs}, {imm:#x}")]
Andi { rt: Register, rs: Register, imm: u16 },
/// Or immediate
#[display(fmt = "ori {rt}, {rs}, {imm:#x}")]
Ori { rt: Register, rs: Register, imm: u16 },
/// Xor immediate
#[display(fmt = "xori {rt}, {rs}, {imm:#x}")]
Xori { rt: Register, rs: Register, imm: u16 },
/// Shift left logical variable
#[display(fmt = "sllv {rd}, {rt}, {rs}")]
Sllv { rd: Register, rt: Register, rs: Register },
/// Shift right logical variable
#[display(fmt = "srlv {rd}, {rt}, {rs}")]
Srlv { rd: Register, rt: Register, rs: Register },
/// Shift right arithmetic variable
#[display(fmt = "srav {rd}, {rt}, {rs}")]
Srav { rd: Register, rt: Register, rs: Register },
/// Shift left logical
#[display(fmt = "sll {rd}, {rt}, {imm:#x}")]
Sll { rd: Register, rt: Register, imm: u8 },
/// Shift right logical
#[display(fmt = "srl {rd}, {rt}, {imm:#x}")]
Srl { rd: Register, rt: Register, imm: u8 },
/// Shift right arithmetic
#[display(fmt = "sra {rd}, {rt}, {imm:#x}")]
Sra { rd: Register, rt: Register, imm: u8 },
/// Load upper immediate
#[display(fmt = "lui {rt}, {imm:#x}")]
Lui { rt: Register, imm: u16 },
/// Multiply
#[display(fmt = "mult {rs}, {rt}")]
Mult { rs: Register, rt: Register },
/// Multiply unsigned
#[display(fmt = "multu {rs}, {rt}")]
Multu { rs: Register, rt: Register },
/// Divide
#[display(fmt = "div {rs}, {rt}")]
Div { rs: Register, rt: Register },
/// Multiply unsigned
#[display(fmt = "divu {rs}, {rt}")]
Divu { rs: Register, rt: Register },
/// Move from hi
#[display(fmt = "mfhi {rd}")]
Mfhi { rd: Register },
/// Move from lo
#[display(fmt = "mflo {rd}")]
Mflo { rd: Register },
/// Move to hi
#[display(fmt = "mthi {rs}")]
Mthi { rs: Register },
/// Move to lo
#[display(fmt = "mtlo {rs}")]
Mtlo { rs: Register },
/// Jump
#[display(fmt = "j {target:#x}")]
J { target: Pos },
/// Jump and link
#[display(fmt = "jal {target:#x}")]
Jal { target: Pos },
/// Jump register
#[display(fmt = "jr {rs}")]
Jr { rs: Register },
/// Jump and link register
#[display(fmt = "jalr {rd}, {rs}")]
Jalr { rd: Register, rs: Register },
/// Branch if equal
#[display(fmt = "beq {rs}, {rt}, {target:#x}")]
Beq { rs: Register, rt: Register, target: Pos },
/// Branch if not equal
#[display(fmt = "bne {rs}, {rt}, {target:#x}")]
Bne { rs: Register, rt: Register, target: Pos },
/// Branch if less than zero
#[display(fmt = "bltz {rs}, {target:#x}")]
Bltz { rs: Register, target: Pos },
/// Branch if greater or equal to zero
#[display(fmt = "bgez {rs}, {target:#x}")]
Bgez { rs: Register, target: Pos },
/// Branch if greater than zero
#[display(fmt = "bgtz {rs}, {target:#x}")]
Bgtz { rs: Register, target: Pos },
/// Branch if less or equal to zero
#[display(fmt = "blez {rs}, {target:#x}")]
Blez { rs: Register, target: Pos },
/// Branch if less than zero and link
#[display(fmt = "bltzal {rs}, {target:#x}")]
Bltzal { rs: Register, target: Pos },
/// Branch if greater or equal to zero and link
#[display(fmt = "bgezal {rs}, {target:#x}")]
Bgezal { rs: Register, target: Pos },
/// Save co-processor data registers
#[display(fmt = "mfc{n} {rt}, {rd}")]
MfcN { n: u8, rt: Register, rd: Register },
/// Save co-processor control registers
#[display(fmt = "cfc{n} {rt}, {rd}")]
CfcN { n: u8, rt: Register, rd: Register },
/// Load co-processor data registers
#[display(fmt = "mtc{n} {rt}, {rd}")]
MtcN { n: u8, rt: Register, rd: Register },
/// Load co-processor control registers
#[display(fmt = "ctc{n} {rt}, {rd}")]
CtcN { n: u8, rt: Register, rd: Register },
// 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")]
BcNf { n: u8, target: u16 },
/// Branch co-processor if true
#[display(fmt = "bc{n}t {target:#x} # Raw target")]
BcNt { n: u8, target: u16 },
/// Exec immediate co-processor
#[display(fmt = "cop{n} {imm:#x}")]
CopN { n: u8, imm: u32 },
/// Load word co-processor
#[display(fmt = "lwc{n} {rt}, {imm:#x}({rs})")]
LwcN { n: u8, rs: Register, rt: Register, imm: u16 },
/// Store word co-processor
#[display(fmt = "swc{n} {rt}, {imm:#x}({rs})")]
SwcN { n: u8, rs: Register, rt: Register, imm: u16 },
/// Syscall
#[display(fmt = "sys {imm:#x}")]
Syscall { imm: u32 },
/// Break
#[display(fmt = "break {imm:#x}")]
Break { imm: u32 },
}
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(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 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 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()},
"000000_sssss_ttttt_ddddd_?????_000100" => Sllv { rd: reg(d)?, rt: reg(t)?, rs: reg(s)? },
"000000_sssss_ttttt_ddddd_?????_000110" => Srlv { rd: reg(d)?, rt: reg(t)?, rs: reg(s)? },
"000000_sssss_ttttt_ddddd_?????_000111" => Srav { rd: reg(d)?, rt: reg(t)?, rs: reg(s)? },
"000000_sssss_?????_?????_?????_001000" => Jr { rs: reg(s)? },
"000000_sssss_?????_ddddd_?????_001001" => Jalr { rd: reg(d)?, rs: reg(s)? },
"000000_iiiii_iiiii_iiiii_iiiii_001100" => Syscall { imm: i },
"000000_iiiii_iiiii_iiiii_iiiii_001101" => Break { imm: i },
"000000_?????_?????_ddddd_?????_010000" => Mfhi { rd: reg(d)? },
"000000_sssss_?????_?????_?????_010001" => Mthi { rs: reg(s)? },
"000000_?????_?????_ddddd_?????_010010" => Mflo { rd: reg(d)? },
"000000_sssss_?????_?????_?????_010011" => Mtlo { rs: reg(s)? },
"000000_sssss_ttttt_?????_?????_011000" => Mult { rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_?????_?????_011001" => Multu { rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_?????_?????_011010" => Div { rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_?????_?????_011011" => Divu { rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100000" => Add { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100001" => Addu { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100010" => Sub { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100011" => Subu { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100100" => And { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100101" => Or { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100110" => Xor { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_100111" => Nor { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_101010" => Slt { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000000_sssss_ttttt_ddddd_?????_101011" => Sltu { rd: reg(d)?, rs: reg(s)?, rt: reg(t)? },
"000001_sssss_?????_iiiii_iiiii_iiiiii" => Bltz { rs: reg(s)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000001_sssss_?????_iiiii_iiiii_iiiiii" => Bgez { rs: reg(s)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000001_sssss_?????_iiiii_iiiii_iiiiii" => Bltzal { rs: reg(s)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000001_sssss_?????_iiiii_iiiii_iiiiii" => Bgezal { rs: reg(s)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
"000010_iiiii_iiiii_iiiii_iiiii_iiiiii" => J { target: (pos & 0xf000_0000) + i * 4 },
"000011_iiiii_iiiii_iiiii_iiiii_iiiiii" => Jal { target: (pos & 0xf000_0000) + i * 4 },
"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 },
"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() },
"001010_sssss_ttttt_iiiii_iiiii_iiiiii" => Slti { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
"001011_sssss_ttttt_iiiii_iiiii_iiiiii" => Sltiu { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
"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() },
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
"0100nn_00000_ttttt_ddddd_?????_000000" => MfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00010_ttttt_ddddd_?????_000000" => CfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00100_ttttt_ddddd_?????_000000" => MtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_00110_ttttt_ddddd_?????_000000" => CtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => BcNf { n: n.truncate(), target: i.truncate() },
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => BcNt { n: n.truncate(), target: i.truncate() },
"100000_sssss_ttttt_iiiii_iiiii_iiiiii" => Lb { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100001_sssss_ttttt_iiiii_iiiii_iiiiii" => Lh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100010_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100011_sssss_ttttt_iiiii_iiiii_iiiiii" => Lw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100100_sssss_ttttt_iiiii_iiiii_iiiiii" => Lbu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100101_sssss_ttttt_iiiii_iiiii_iiiiii" => Lhu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"100110_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"101000_sssss_ttttt_iiiii_iiiii_iiiiii" => Sb { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"101001_sssss_ttttt_iiiii_iiiii_iiiiii" => Sh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"101010_sssss_ttttt_iiiii_iiiii_iiiiii" => Swl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"101011_sssss_ttttt_iiiii_iiiii_iiiiii" => Sw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"101110_sssss_ttttt_iiiii_iiiii_iiiiii" => Swr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::<u16>().as_signed() },
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => LwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => SwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
_ => return None,
};
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 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(std::slice::from_ref(&raw.repr), raw.pos)?;
Some((raw.pos, instruction))
}
}

View File

@ -5,11 +5,13 @@ 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)
/// Alu immediate instruction kind
///
/// Each variant's value is equal to the lower 3 bits of the opcode
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum AluImmOp {
pub enum AluImmKind {
/// Add
Add = 0x0,
@ -52,7 +54,7 @@ pub struct AluImmRaw {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluImmInst {
/// Destination register, `rd`
pub dest: Register,
pub dst: Register,
/// Lhs argument, `rs`
pub lhs: Register,
@ -61,29 +63,29 @@ pub struct AluImmInst {
pub rhs: u32,
/// Opcode
pub op: AluImmOp,
pub kind: AluImmKind,
}
impl AluImmInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: AluImmRaw) -> Option<Self> {
let op = AluImmOp::try_from(raw.p.truncated::<u8>()).ok()?;
let kind = AluImmKind::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.t)?,
dst: Register::new(raw.t)?,
lhs: Register::new(raw.s)?,
rhs: raw.i,
op,
kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> AluImmRaw {
let p = u8::from(self.op).zero_extended::<u32>();
let p = u8::from(self.kind).zero_extended::<u32>();
let s = self.lhs.idx();
let t = self.dest.idx();
let t = self.dst.idx();
let i = self.rhs;
AluImmRaw { p, s, t, i }
@ -92,17 +94,17 @@ impl AluImmInst {
impl fmt::Display for AluImmInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { op, dest, lhs, rhs } = self;
let Self { kind, dst, 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}"),
match kind {
AluImmKind::Add => write!(f, "addi {dst}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmKind::AddUnsigned => write!(f, "addiu {dst}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmKind::SetLessThan => write!(f, "slti {dst}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
AluImmKind::SetLessThanUnsigned => write!(f, "sltiu {dst}, {lhs}, {rhs:#x}"),
AluImmKind::And => write!(f, "andi {dst}, {lhs}, {rhs:#x}"),
AluImmKind::Or => write!(f, "ori {dst}, {lhs}, {rhs:#x}"),
AluImmKind::Xor => write!(f, "xori {dst}, {lhs}, {rhs:#x}"),
}
}
}

View File

@ -0,0 +1,63 @@
//! Iterator over instructions
// Imports
use super::BasicInst;
/// Iterator over instructions
#[derive(PartialEq, Eq, Debug)]
pub struct InstIter<'a, I: Iterator<Item = u32> + Clone> {
/// Underlying iterator
iter: &'a mut I,
}
impl<'a, I: Iterator<Item = u32> + Clone> InstIter<'a, I> {
/// Creates a new instruction iterator
pub fn new(iter: &mut I) -> Self {
Self { iter }
}
/// Reborrows this iterator with a smaller lifetime
pub fn reborrow<'b>(&mut self) -> InstIter<'b, I>
where
'a: 'b,
{
InstIter { iter: self.iter }
}
/// Peeks the next element
pub fn peeker<'b>(&mut self) -> InstPeeker<'b, I>
where
'a: 'b,
{
InstPeeker {
cur_iter: self.iter.clone(),
iter: self,
}
}
}
/// Instruction Peeker
#[derive(PartialEq, Eq, Debug)]
pub struct InstPeeker<'a, I: Iterator<Item = u32> + Clone> {
/// Original iterator to update if consumed
iter: InstIter<'a, I>,
/// Current iterator
cur_iter: I,
}
impl<'a, I: Iterator<Item = u32> + Clone> InstPeeker<'a, I> {
/// Updates the iterator this is peeking to consume all
pub fn update(&mut self) {
*self.iter.iter = self.cur_iter;
}
}
impl<'a, I: Iterator<Item = u32> + Clone> Iterator for InstPeeker<'a, I> {
type Item = Option<BasicInst>;
fn next(&mut self) -> Option<Self::Item> {
// Consume our current iterator
self.cur_iter.next()
}
}

View File

@ -3,13 +3,15 @@
// Imports
use crate::{game::exe::instruction::Register, util::SignedHex};
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
use std::convert::TryFrom;
/// Load instruction opcode (lower 3 bits)
/// Load instruction kind
///
/// Each variant's value is equal to the lower 3 bits of the opcode
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum LoadOpcode {
pub enum LoadKind {
/// Byte, `i8`
Byte = 0x0,
@ -32,6 +34,22 @@ pub enum LoadOpcode {
WordRight = 0x6,
}
impl LoadKind {
/// Returns the mnemonic for this load kind
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
Self::Byte => "lb",
Self::HalfWord => "lh",
Self::WordLeft => "lwl",
Self::Word => "lw",
Self::ByteUnsigned => "lbu",
Self::HalfWordUnsigned => "lhu",
Self::WordRight => "lwr",
}
}
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct LoadRaw {
@ -50,60 +68,44 @@ pub struct LoadRaw {
/// Load instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "{} {dst}, {:#x}({src})", "kind.mnemonic()", "SignedHex(offset)")]
pub struct LoadInst {
/// Source register, `rt`
pub source: Register,
pub src: Register,
/// Destination register, `rs`
pub dest: Register,
pub dst: Register,
/// Destination offset.
pub offset: i16,
/// Opcode
pub op: LoadOpcode,
/// Kind
pub kind: LoadKind,
}
impl LoadInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: LoadRaw) -> Option<Self> {
let op = LoadOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
let op = LoadKind::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
source: Register::new(raw.t)?,
dest: Register::new(raw.s)?,
src: Register::new(raw.t)?,
dst: Register::new(raw.s)?,
offset: raw.i.truncated::<u16>().as_signed(),
op,
kind: op,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> LoadRaw {
let t = self.source.idx();
let s = self.dest.idx();
let t = self.src.idx();
let s = self.dst.idx();
let i = self.offset.as_unsigned().zero_extended::<u32>();
let p = u8::from(self.op).zero_extended::<u32>();
let p = u8::from(self.kind).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}, {:#x}({source})", SignedHex(offset))
}
}

View File

@ -0,0 +1,47 @@
//! Lui instruction
// Imports
use crate::game::exe::instruction::Register;
use int_conv::{Truncated, ZeroExtended};
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct LuiRaw {
/// Rt
pub t: u32,
/// Immediate
pub i: u32,
}
/// Load instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "lui {dst}, {value:#x}")]
pub struct LuiInst {
/// Destination register, `rt`
pub dst: Register,
/// Value
pub value: u16,
}
impl LuiInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: LuiRaw) -> Option<Self> {
Some(Self {
dst: Register::new(raw.t)?,
value: raw.i.truncated::<u16>(),
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> LuiRaw {
LuiRaw {
t: self.dst.idx(),
i: self.value.zero_extended::<u32>(),
}
}
}

View File

@ -5,11 +5,13 @@ use crate::game::exe::instruction::Register;
use int_conv::{Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
/// Alu register func
/// Alu register instruction kind
///
/// Each variant's value is equal to the lower 4 bits of the opcode
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum AluRegFunc {
pub enum AluRegKind {
/// Add
Add = 0x20,
@ -61,7 +63,7 @@ pub struct AluRegRaw {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct AluRegInst {
/// Destination register, `rd`
pub dest: Register,
pub dst: Register,
/// Lhs argument, `rs`
pub lhs: Register,
@ -69,31 +71,31 @@ pub struct AluRegInst {
/// Rhs argument, `rt`
pub rhs: Register,
/// Function
pub func: AluRegFunc,
/// Kind
pub kind: AluRegKind,
}
impl AluRegInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: AluRegRaw) -> Option<Self> {
let func = AluRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
let kind = AluRegKind::try_from(raw.f.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.d)?,
dst: Register::new(raw.d)?,
lhs: Register::new(raw.s)?,
rhs: Register::new(raw.t)?,
func,
kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> AluRegRaw {
let d = self.dest.idx();
let d = self.dst.idx();
let s = self.lhs.idx();
let t = self.rhs.idx();
let f = u8::from(self.func).zero_extended::<u32>();
let f = u8::from(self.kind).zero_extended::<u32>();
AluRegRaw { f, t, d, s }
}
@ -101,21 +103,21 @@ impl AluRegInst {
impl fmt::Display for AluRegInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { dest, lhs, rhs, func } = self;
let Self { dst, lhs, rhs, kind } = 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",
let mnemonic = match kind {
AluRegKind::Add => "add",
AluRegKind::AddUnsigned => "addu",
AluRegKind::Sub => "sub",
AluRegKind::SubUnsigned => "subu",
AluRegKind::And => "and",
AluRegKind::Or => "or",
AluRegKind::Xor => "xor",
AluRegKind::Nor => "nor",
AluRegKind::SetLessThan => "slt",
AluRegKind::SetLessThanUnsigned => "sltu",
};
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
write!(f, "{mnemonic} {dst}, {lhs}, {rhs}")
}
}

View File

@ -71,19 +71,19 @@ pub enum MultInst {
/// Move from
MoveFrom {
/// Destination
dest: Register,
dst: Register,
/// Source
source: MultReg,
src: MultReg,
},
/// Move to
MoveTo {
/// Source
source: Register,
dst: Register,
/// Destination
dest: MultReg,
src: MultReg,
},
}
@ -93,11 +93,11 @@ impl MultInst {
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 },
0x10 => Self::MoveFrom { dst: Register::new(raw.d)?, src: MultReg::Hi },
0x12 => Self::MoveFrom { dst: Register::new(raw.d)?, src: MultReg::Lo },
0x11 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Hi },
0x13 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Lo },
0x11 => Self::MoveTo { dst: Register::new(raw.s)?, src: MultReg::Hi },
0x13 => Self::MoveTo { dst: Register::new(raw.s)?, src: 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)? },
@ -123,20 +123,20 @@ impl MultInst {
(MultKind::Div, MultMode::Unsigned) => 0x1b,
},
},
Self::MoveFrom { dest, source } => MultRaw {
Self::MoveFrom { dst, src } => MultRaw {
s: 0,
t: 0,
d: dest.idx(),
f: match source {
d: dst.idx(),
f: match src {
MultReg::Hi => 0x10,
MultReg::Lo => 0x12,
},
},
Self::MoveTo { source, dest } => MultRaw {
s: source.idx(),
Self::MoveTo { dst: src, src: dst } => MultRaw {
s: src.idx(),
t: 0,
d: 0,
f: match dest {
f: match dst {
MultReg::Hi => 0x11,
MultReg::Lo => 0x13,
},
@ -155,13 +155,13 @@ impl fmt::Display for MultInst {
(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::MoveFrom { dst, src } => match src {
MultReg::Hi => write!(f, "mfhi {dst}"),
MultReg::Lo => write!(f, "mflo {dst}"),
},
Self::MoveTo { source, dest } => match dest {
MultReg::Hi => write!(f, "mthi {source}"),
MultReg::Lo => write!(f, "mtlo {source}"),
Self::MoveTo { dst: src, src: dst } => match dst {
MultReg::Hi => write!(f, "mthi {src}"),
MultReg::Lo => write!(f, "mtlo {src}"),
},
}
}

View File

@ -40,7 +40,7 @@ pub struct ShiftImmRaw {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftImmInst {
/// Destination register, `rt`
pub dest: Register,
pub dst: Register,
/// Lhs argument, `rd`
pub lhs: Register,
@ -60,7 +60,7 @@ impl ShiftImmInst {
Some(Self {
lhs: Register::new(raw.t)?,
dest: Register::new(raw.d)?,
dst: Register::new(raw.d)?,
rhs: raw.i.truncated::<u16>().as_signed(),
func,
})
@ -70,7 +70,7 @@ impl ShiftImmInst {
#[must_use]
pub fn encode(self) -> ShiftImmRaw {
let t = self.lhs.idx();
let d = self.dest.idx();
let d = self.dst.idx();
let i = self.rhs.as_unsigned().zero_extended::<u32>();
let f = u8::from(self.func).zero_extended::<u32>();
@ -80,7 +80,7 @@ impl ShiftImmInst {
impl fmt::Display for ShiftImmInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { lhs, dest, rhs, func } = self;
let Self { lhs, dst, rhs, func } = self;
let mnemonic = match func {
ShiftImmFunc::LeftLogical => "sll",
@ -88,6 +88,6 @@ impl fmt::Display for ShiftImmInst {
ShiftImmFunc::RightArithmetic => "sra",
};
write!(f, "{mnemonic} {dest}, {lhs}, {rhs:#x}")
write!(f, "{mnemonic} {dst}, {lhs}, {rhs:#x}")
}
}

View File

@ -40,7 +40,7 @@ pub struct ShiftRegRaw {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ShiftRegInst {
/// Destination register, `rd`
pub dest: Register,
pub dst: Register,
/// Lhs argument, `rt`
pub lhs: Register,
@ -59,7 +59,7 @@ impl ShiftRegInst {
let func = ShiftRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
Some(Self {
dest: Register::new(raw.d)?,
dst: Register::new(raw.d)?,
lhs: Register::new(raw.t)?,
rhs: Register::new(raw.s)?,
func,
@ -69,7 +69,7 @@ impl ShiftRegInst {
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> ShiftRegRaw {
let d = self.dest.idx();
let d = self.dst.idx();
let t = self.lhs.idx();
let s = self.rhs.idx();
let f = u8::from(self.func).zero_extended::<u32>();
@ -80,7 +80,7 @@ impl ShiftRegInst {
impl fmt::Display for ShiftRegInst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { dest, lhs, rhs, func } = self;
let Self { dst, lhs, rhs, func } = self;
let mnemonic = match func {
ShiftRegFunc::LeftLogical => "sllv",
@ -88,6 +88,6 @@ impl fmt::Display for ShiftRegInst {
ShiftRegFunc::RightArithmetic => "srav",
};
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
write!(f, "{mnemonic} {dst}, {lhs}, {rhs}")
}
}

View File

@ -3,13 +3,15 @@
// Imports
use crate::{game::exe::instruction::Register, util::SignedHex};
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryFrom, fmt};
use std::convert::TryFrom;
/// Store instruction opcode (lower 3 bits)
/// Store instruction kind
///
/// Each variant's value is equal to the lower 3 bits of the opcode
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum StoreOpcode {
pub enum StoreKind {
/// Byte, `u8`
Byte = 0x0,
@ -26,6 +28,20 @@ pub enum StoreOpcode {
WordRight = 0x6,
}
impl StoreKind {
/// Returns the mnemonic for this store kind
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
Self::Byte => "sb",
Self::HalfWord => "sh",
Self::WordLeft => "swl",
Self::Word => "sw",
Self::WordRight => "swr",
}
}
}
/// Raw representation
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct StoreRaw {
@ -44,63 +60,44 @@ pub struct StoreRaw {
/// Store instructions
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "{} {dst}, {:#x}({src})", "kind.mnemonic()", "SignedHex(offset)")]
pub struct StoreInst {
/// Source register, `rt`
pub source: Register,
pub src: Register,
/// Destination register, `rs`
pub dest: Register,
pub dst: Register,
/// Destination offset.
pub offset: i16,
/// Opcode
pub op: StoreOpcode,
/// Kind
pub kind: StoreKind,
}
impl StoreInst {
/// Decodes this instruction
#[must_use]
pub fn decode(raw: StoreRaw) -> Option<Self> {
let kind = StoreOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
let kind = StoreKind::try_from(raw.p.truncated::<u8>()).ok()?;
Some(Self {
source: Register::new(raw.t)?,
dest: Register::new(raw.s)?,
src: Register::new(raw.t)?,
dst: Register::new(raw.s)?,
offset: raw.i.truncated::<u16>().as_signed(),
op: kind,
kind,
})
}
/// Encodes this instruction
#[must_use]
pub fn encode(self) -> StoreRaw {
let t = self.source.idx();
let s = self.dest.idx();
let t = self.src.idx();
let s = self.dst.idx();
let i = self.offset.as_unsigned().zero_extended::<u32>();
let p = u8::from(self.op).zero_extended::<u32>();
let p = u8::from(self.kind).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}, {:#x}({source})", SignedHex(offset))
}
}

View File

@ -1,9 +1,215 @@
//! Pseudo instructions
//!
//! This modules defines all the pseudo instructions usually
//! used in mips. They are variable length.
// Modules
pub mod load;
pub mod load_imm;
pub mod move_reg;
pub mod store;
// Exports
pub use load::LoadPseudoInst;
pub use move_reg::MoveRegPseudoInst;
pub use store::StorePseudoInst;
// Imports
use super::{BasicInstruction, FromRawIter, Raw, Register};
use crate::{game::exe::Pos, util::SignedHex};
use int_conv::{Join, SignExtended, Signed, ZeroExtended};
use super::{FromRawIter, Raw, Register};
use crate::{
game::exe::{instruction::BasicInst, Pos},
util::SignedHex,
};
use int_conv::ZeroExtended;
/// A pseudo instruction
// TODO: Maybe partition by start address instead of by type somehow?
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum PseudoInst {
/// Load
Load(LoadPseudoInst),
/// Store
Store(StorePseudoInst),
/// Move register
MoveRegPseudo(MoveRegPseudoInst),
/// No-op
/// Alias for `sll $zr,$zr,0`
#[display(fmt = "nop")]
Nop,
/*
/// 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 },
/// Add immediate assign
/// Alias for `addi $rx, $rx, imm`
#[display(fmt = "addi {rx}, {:#x}", "SignedHex(imm)")]
AddiAssign { rx: Register, imm: i16 },
/// Add immediate sign-extended assign
/// Alias for `addiu $rx, $rx, imm`
#[display(fmt = "addiu {rx}, {:#x}", "SignedHex(imm)")]
AddiuAssign { rx: Register, imm: i16 },
/// 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`
#[display(fmt = "subi {rt}, {rs}, {imm:#x}")]
Subi { rt: Register, rs: Register, imm: u32 },
/// Subtract immediate sign-extended
/// Alias for `addiu $rt, $rs, -imm`
#[display(fmt = "subiu {rt}, {rs}, {imm:#x}")]
Subiu { rt: Register, rs: Register, imm: u32 },
/// Subtract immediate assign
/// Alias for `subi $rx, $rx, imm`
#[display(fmt = "subi {rx}, {imm:#x}")]
SubiAssign { rx: Register, imm: u32 },
/// Subtract immediate sign-extended assign
/// Alias for `subiu $rx, $rx, imm`
#[display(fmt = "subiu {rx}, {imm:#x}")]
SubiuAssign { rx: Register, imm: u32 },
/// Branch if equal to zero
/// Alias for `beq $rx, $zr, target`
#[display(fmt = "beqz {rx}, {target:#x}")]
Beqz { rx: Register, target: Pos },
/// Branch if different from zero
/// Alias for `bne $rx, $zr, target`
#[display(fmt = "bnez {rx}, {target:#x}")]
Bnez { rx: Register, target: Pos },
/// Jump relative
/// Alias for `beq $zr, $zr, target`
#[display(fmt = "b {target:#x}")]
B { target: Pos },
*/
}
impl PseudoInst {
pub fn decode<I: Iterator<Item = BasicInst> + Clone>(_iter: &mut I) -> Option<Self> {
todo!();
/*
let mut cur_iter = iter.clone();
match cur_iter.next()? {
BasicInst::Lui(_) => match cur_iter.next() {
BasicInst::Store(_) => {},
BasicInst::Load(_) => {},
BasicInst::Special(_) => {},
BasicInst::Cond(_) => {},
BasicInst::Jmp(_) => {},
BasicInst::AluImm(_) => {},
Some(_) => {}
None => {}
},
/*
BasicInst::Store(_) => {},
BasicInst::Load(_) => {},
BasicInst::Special(_) => {},
BasicInst::Cond(_) => {},
BasicInst::Jmp(_) => {},
BasicInst::AluImm(_) => {},
*/
_ => None,
}
*/
}
}
/// A pseudo instruction
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -248,17 +454,15 @@ 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 basic instructions and registers
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
use BasicInstruction::*;
use Register::*;
fn decode<I: Iterator<Item = Raw> + Clone>(_iter: &mut I) -> Self::Decoded {
// Get the first instruction
let (pos, instruction) = BasicInstruction::decode(iter)?;
/*
let (pos, instruction) = BasicInst::decode(iter)?;
let pseudo = match instruction {
Lui { imm: imm_hi, rt: prev_rt } => {
let iter_before = iter.clone();
match BasicInstruction::decode(iter)?.1 {
match BasicInst::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),
@ -411,13 +615,7 @@ impl FromRawIter for PseudoInstruction {
};
Some((pos, pseudo))
*/
todo!()
}
}
/// Calculates `hi << 16 + lo`
fn hi_plus_lo(lo: i16, hi: u16) -> u32 {
let lo = lo.sign_extended::<i32>();
let hi = u32::join(0, hi).as_signed();
(hi + lo).as_unsigned()
}

View File

@ -0,0 +1,41 @@
//! Load instructions
// Imports
use crate::game::exe::instruction::{
basic::{load::LoadKind, LoadInst, LuiInst},
Register,
};
use int_conv::{Join, SignExtended, Signed};
/// Load pseudo instructions
///
/// Alias for
/// ```mips
/// lui $rx, {hi}
/// l* $rx, {lo}($rx)
/// ```
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "{} {dst}, {target:#x}", "kind.mnemonic()")]
pub struct LoadPseudoInst {
/// Destination register
pub dst: Register,
/// Target
pub target: u32,
/// Kind
pub kind: LoadKind,
}
impl LoadPseudoInst {
/// Decodes this pseudo instruction
#[must_use]
pub fn decode(lui: LuiInst, load: LoadInst) -> Self {
Self {
dst: load.dst,
target: (u32::join(0, lui.value).as_signed() + load.offset.sign_extended::<i32>()).as_unsigned(),
kind: load.kind,
}
}
}

View File

@ -0,0 +1,109 @@
//! Load immediate
// Imports
use crate::{
game::exe::instruction::{basic::InstIter, Register},
util::SignedHex,
};
use std::fmt;
/// Immediate kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
pub enum LoadImmKind {
/// Address
///
/// Alias for `lui $dst, {hi} / addiu $dst, $dst, {lo}`
Address(u32),
/// Word
///
/// Alias for `lui $dst, {hi} / ori $dst, $dst, {imm-lo}`
Word(u32),
/// Unsigned half-word
///
/// Alias for `ori $dst, $zr, imm`
HalfWordUnsigned(u16),
/// Signed half-word
///
/// Alias for `addiu $dst, $zr, imm`
HalfWordSigned(i16),
}
impl LoadImmKind {
/// Returns the mnemonic for this load kind
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
LoadImmKind::Address(_) => "la",
LoadImmKind::Word(_) | LoadImmKind::HalfWordUnsigned(_) | LoadImmKind::HalfWordSigned(_) => "li",
}
}
/// Returns a displayable with the value of this load kind formatted.
pub fn value_fmt(self) -> impl fmt::Display {
struct FmtValue(Self);
impl fmt::Display for FmtValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[rustfmt::skip]
match self.0 {
LoadImmKind::Address(address) => write!(f, "{address:#x}"),
LoadImmKind::Word(value) => write!(f, "{value:#x}"),
LoadImmKind::HalfWordUnsigned(value) => write!(f, "{value:#x}"),
LoadImmKind::HalfWordSigned(value) => write!(f, "{:#x}", SignedHex(value)),
}
}
}
FmtValue(self)
}
}
/// Load immediate instruction
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "{} {dst}, {}", "kind.mnemonic()", "kind.value_fmt()")]
pub struct LoadImmInst {
/// Destination register
pub dst: Register,
/// Load kind
pub kind: LoadImmKind,
}
impl LoadImmInst {
/// Decodes this pseudo instruction
#[must_use]
pub fn decode(iter: InstIter<'_, impl Iterator<Item = u32> + Clone>) -> Option<Self> {
todo!();
/*
match insts {
[BasicInst::]
/*
/// Alias for `lui $dst, {hi} / addiu $dst, $dst, {lo}`
Address(u32),
/// Word
///
/// Alias for `lui $dst, {hi} / ori $dst, $dst, {imm-lo}`
Word(u32),
/// Unsigned half-word
///
/// Alias for `ori $dst, $zr, imm`
HalfWordUnsigned(u16),
/// Signed half-word
///
/// Alias for `addiu $dst, $zr, imm`
HalfWordSigned(i16),
*/
}
*/
}
}

View File

@ -0,0 +1,74 @@
//! Move register instruction
// Imports
use crate::game::exe::instruction::{
basic::{
alu_imm::AluImmKind,
special::{
alu_reg::AluRegKind,
shift::{ShiftImmInst, ShiftRegInst},
AluRegInst, ShiftInst,
},
AluImmInst, SpecialInst,
},
BasicInst, Register,
};
/// Move register instruction
///
/// Alias for
/// ```mips
/// Alias for `{add|addu|sub|subu|and|or|xor|sllv|srlv|srav} $dst, $src, $zr` or
/// `{addi|addiu|andi|ori|xori|sll|srl|sra} $dst, $src, 0`
/// ```
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "move {dst}, {src}")]
pub struct MoveRegPseudoInst {
/// Destination register
pub dst: Register,
/// Source register
pub src: Register,
}
impl MoveRegPseudoInst {
/// Decodes this pseudo instruction
#[must_use]
pub const fn decode(inst: BasicInst) -> Option<Self> {
match inst {
BasicInst::Special(
SpecialInst::Shift(
ShiftInst::Imm(ShiftImmInst { dst, lhs: src, rhs: 0, .. }) |
ShiftInst::Reg(ShiftRegInst {
dst,
lhs: src,
rhs: Register::Zr,
..
}),
) |
SpecialInst::Alu(AluRegInst {
dst,
lhs: src,
rhs: Register::Zr,
kind:
AluRegKind::Add |
AluRegKind::AddUnsigned |
AluRegKind::Sub |
AluRegKind::SubUnsigned |
AluRegKind::And |
AluRegKind::Or |
AluRegKind::Xor |
AluRegKind::Nor,
}),
) |
BasicInst::AluImm(AluImmInst {
dst,
lhs: src,
rhs: 0,
kind: AluImmKind::Add | AluImmKind::AddUnsigned | AluImmKind::And | AluImmKind::Or | AluImmKind::Xor,
}) => Some(Self { dst, src }),
_ => None,
}
}
}

View File

@ -0,0 +1,41 @@
//! Store instructions
// Imports
use crate::game::exe::instruction::{
basic::{store::StoreKind, LuiInst, StoreInst},
Register,
};
use int_conv::{Join, SignExtended, Signed};
/// Store pseudo instructions
///
/// Alias for
/// ```mips
/// lui $at, {hi}
/// s* $rx, {lo}($at)
/// ```
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::Display)]
#[display(fmt = "{} {dst}, {target:#x}", "kind.mnemonic()")]
pub struct StorePseudoInst {
/// Destination register
pub dst: Register,
/// Target
pub target: u32,
/// Kind
pub kind: StoreKind,
}
impl StorePseudoInst {
/// Decodes this pseudo instruction
#[must_use]
pub fn decode(lui: LuiInst, store: StoreInst) -> Self {
Self {
dst: store.dst,
target: (u32::join(0, lui.value).as_signed() + store.offset.sign_extended::<i32>()).as_unsigned(),
kind: store.kind,
}
}
}

View File

@ -104,10 +104,8 @@ impl<R: Read + Write + Seek> Read for GameFile<R> {
// Get how many bytes we can read, The minimum between the end of the data section and the size of the buffer
// Note: Can't overflow, max is `2048`
// Note: At this point, `cur_address` must be within the data section
let bytes_to_read = cur_address
.try_to_data()
.expect("Address wasn't in data section")
.remaining_bytes()
let bytes_until_section_end = cur_address.try_to_data().expect("Address wasn't in data section").remaining_bytes();
let bytes_to_read = bytes_until_section_end
.min(buf.len().try_into().expect("Unable to convert `usize` to `u64`"))
.try_into()
.expect("Unable to convert number 0..2048 to `i64`");
@ -118,13 +116,13 @@ impl<R: Read + Write + Seek> Read for GameFile<R> {
debug_assert!(bytes_to_read <= buf.len());
let bytes_read = self.reader.read(unsafe { buf.get_unchecked_mut(..bytes_to_read) })?;
// If 0 bytes were read, EOF was reached, so return with however many we've read
// If we reached EOF, return what we read
if bytes_read == 0 {
return Ok(total_buf_len - buf.len());
}
// Else seek into the next data section start
cur_address = RealAddress::from_u64(self.reader.seek(SeekFrom::Start(cur_address.next_sector_data_section_start().as_u64()))?);
// Else update the current address
cur_address += bytes_read.try_into().expect("Unable to convert `usize` to `i64`");
// And discard what we've already read
// Note: This slice can't panic, as `bytes_read` is at most `buf.len()`
@ -168,10 +166,8 @@ impl<R: Read + Write + Seek> Write for GameFile<R> {
// Get how many bytes we can write, The minimum between the end of the data section and the size of the buffer
// Note: Can't overflow, max is `2048`
// Note: At this point, `cur_address` must be within the data section
let bytes_to_write = cur_address
.try_to_data()
.expect("Address wasn't in data section")
.remaining_bytes()
let bytes_until_section_end = cur_address.try_to_data().expect("Address wasn't in data section").remaining_bytes();
let bytes_to_write = bytes_until_section_end
.min(buf.len().try_into().expect("Unable to convert `usize` to `u64`"))
.try_into()
.expect("Unable to convert number 0..2048 to `i64`");
@ -187,8 +183,8 @@ impl<R: Read + Write + Seek> Write for GameFile<R> {
return Ok(total_buf_len - buf.len());
}
// Else seek into the next data section start
cur_address = RealAddress::from_u64(self.reader.seek(SeekFrom::Start(cur_address.next_sector_data_section_start().as_u64()))?);
// Else update the current address
cur_address += bytes_written.try_into().expect("Unable to convert `usize` to `i64`");
// And discard what we've already written
// Note: This slice can't panic, as `bytes_written` is at most `buf.len()`

View File

@ -20,6 +20,7 @@ pub struct DiscardingSortedMergeIter<T: Ord, Li: Iterator<Item = T>, Ri: Iterato
impl<T: Ord, Li: Iterator<Item = T>, Ri: Iterator<Item = T>> DiscardingSortedMergeIter<T, Li, Ri> {
/// Creates a new merging iterator
#[allow(dead_code)] // TODO: Remove
pub fn new(lhs: Li, rhs: Ri) -> Self {
Self { lhs, rhs, last: None }
}