mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-08 11:28:44 +00:00
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:
parent
b974e89d74
commit
94bb2d780d
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
dcb/src/game/exe/instruction/basic/iter.rs
Normal file
63
dcb/src/game/exe/instruction/basic/iter.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
47
dcb/src/game/exe/instruction/basic/lui.rs
Normal file
47
dcb/src/game/exe/instruction/basic/lui.rs
Normal 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>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
41
dcb/src/game/exe/instruction/pseudo/load.rs
Normal file
41
dcb/src/game/exe/instruction/pseudo/load.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
109
dcb/src/game/exe/instruction/pseudo/load_imm.rs
Normal file
109
dcb/src/game/exe/instruction/pseudo/load_imm.rs
Normal 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),
|
||||
*/
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
74
dcb/src/game/exe/instruction/pseudo/move_reg.rs
Normal file
74
dcb/src/game/exe/instruction/pseudo/move_reg.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
41
dcb/src/game/exe/instruction/pseudo/store.rs
Normal file
41
dcb/src/game/exe/instruction/pseudo/store.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()`
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user