diff --git a/dcb-tools/src/decompiler/main.rs b/dcb-tools/src/decompiler/main.rs index 9eba15a..887315b 100644 --- a/dcb-tools/src/decompiler/main.rs +++ b/dcb-tools/src/decompiler/main.rs @@ -65,6 +65,8 @@ #![allow(clippy::large_digit_groups)] // We don't put the final `else` if it's empty #![allow(clippy::else_if_without_else)] +// We're usually fine with missing future variants +#![allow(clippy::wildcard_enum_match_arm)] // Modules mod cli; @@ -150,9 +152,9 @@ fn main() -> Result<(), anyhow::Error> { // Get all function jumps let funcs_pos: HashMap = instructions .iter() - .filter_map(|(_, instruction)| match instruction { - Instruction::Simple(SimpleInstruction::Jal { target }) | - Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) => Some(Pos(*target)), + .filter_map(|(_, instruction)| match *instruction { + Instruction::Simple(SimpleInstruction::Jal { target }) => Some(target), + Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) => Some(Pos(target)), _ => None, }) .filter(|target| (Instruction::CODE_START..Instruction::CODE_END).contains(target) && offsets.contains(target)) @@ -163,7 +165,7 @@ fn main() -> Result<(), anyhow::Error> { // Get all local jumps let locals_pos: HashMap = instructions .iter() - .filter_map(|(_, instruction)| match instruction { + .filter_map(|(_, instruction)| match *instruction { Instruction::Simple( SimpleInstruction::J { target } | SimpleInstruction::Beq { target, .. } | @@ -177,7 +179,7 @@ fn main() -> Result<(), anyhow::Error> { ) | Instruction::Pseudo( PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. } | PseudoInstruction::B { target }, - ) => Some(Pos(*target)), + ) => Some(target), _ => None, }) .filter(|target| (Instruction::CODE_START..Instruction::CODE_END).contains(target) && offsets.contains(target)) @@ -270,10 +272,10 @@ fn main() -> Result<(), anyhow::Error> { SimpleInstruction::Bgezal { target, .. }, ) => { print!(" #"); - if let Some(func_idx) = funcs_pos.get(Pos::ref_cast(target)) { + if let Some(func_idx) = funcs_pos.get(target) { print!(" func_{func_idx}"); } - if let Some(local_idx) = locals_pos.get(Pos::ref_cast(target)) { + if let Some(local_idx) = locals_pos.get(target) { print!(" .{local_idx}"); } }, diff --git a/dcb/Cargo.toml b/dcb/Cargo.toml index c32f693..5245897 100644 --- a/dcb/Cargo.toml +++ b/dcb/Cargo.toml @@ -18,6 +18,7 @@ ascii = { version = "1.0", features = ["serde"] } arrayref = "0.3" int-conv = "0.1" indoc = "1.0" +bitmatch = "0.1" # Serde serde = { version = "1.0", features = ["derive"] } diff --git a/dcb/src/game/exe/instruction/directive.rs b/dcb/src/game/exe/instruction/directive.rs index e55b2b9..464a900 100644 --- a/dcb/src/game/exe/instruction/directive.rs +++ b/dcb/src/game/exe/instruction/directive.rs @@ -53,7 +53,6 @@ impl FromRawIter for Directive { let raw = iter.next()?; // Try to get an ascii string from the raw and check for nulls - #[allow(clippy::wildcard_enum_match_arm)] // Option won't get more variants match AsciiString::from_ascii(raw.repr.to_ne_bytes()).map(check_nulls) { // If we got a string with at least 1 non-null, but // at least 1 null and uniformly null, return just it diff --git a/dcb/src/game/exe/instruction/pseudo.rs b/dcb/src/game/exe/instruction/pseudo.rs index 5c7e2b2..5c09f0f 100644 --- a/dcb/src/game/exe/instruction/pseudo.rs +++ b/dcb/src/game/exe/instruction/pseudo.rs @@ -223,24 +223,23 @@ pub enum PseudoInstruction { /// Branch if equal to zero /// Alias for `beq $rx, $zr, target` #[display(fmt = "beqz {rx}, {target:#x}")] - Beqz { rx: Register, target: u32 }, + 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: u32 }, + Bnez { rx: Register, target: Pos }, /// Jump relative /// Alias for `beq $zr, $zr, target` #[display(fmt = "b {target:#x}")] - B { target: u32 }, + B { target: Pos }, // TODO: Push / Pop } impl FromRawIter for PseudoInstruction { type Decoded = Option<(Pos, Self)>; - #[allow(clippy::wildcard_enum_match_arm)] // New entries won't affect this function, it can only act on entries it knows. #[allow(clippy::similar_names)] // With register names, this happens too much #[allow(clippy::too_many_lines, clippy::clippy::cognitive_complexity)] // We can't separate this into several functions, it's just 1 big match fn decode + Clone>(iter: &mut I) -> Self::Decoded { diff --git a/dcb/src/game/exe/instruction/simple.rs b/dcb/src/game/exe/instruction/simple.rs index acdd965..12db2c1 100644 --- a/dcb/src/game/exe/instruction/simple.rs +++ b/dcb/src/game/exe/instruction/simple.rs @@ -3,847 +3,404 @@ // Lints // #[allow(clippy::similar_names)] -// Modules -pub mod repr; - -// Exports -pub use repr::RawRepr; - // Imports use super::{FromRawIter, Raw, Register}; use crate::{game::exe::Pos, util::SignedHex}; -use int_conv::{SignExtended, Signed}; +use bitmatch::bitmatch; +use int_conv::{SignExtended, Signed, Truncate, Truncated}; -/// Macro to declare all instructions -macro_rules! decl_instructions { - ( - $( - $( #[doc = $doc:literal] )* - #[display(fmt = $fmt:literal $( , $fmt_args:expr )* $(,)?)] - $split:pat $( if $cond:expr )? => $variant:ident { - $( $field_name:ident : $field_type:ty $( = $field_expr: expr )? ),* $(,)? - } - ),+ $(,)? - ) => { - /// A raw instruction - #[derive(PartialEq, Eq, Clone, Copy, Debug)] - #[derive(derive_more::Display)] - pub enum SimpleInstruction { - $( - $( #[doc = $doc] )* - #[display(fmt = $fmt, $( $fmt_args, )*)] - $variant { - $( - $field_name: $field_type, - )* - }, - )+ - } - - impl FromRawIter for SimpleInstruction { - type Decoded = Option<(Pos, Self)>; - - #[allow(clippy::redundant_field_names)] // For uniform initialization - fn decode + Clone>(iter: &mut I) -> Self::Decoded { - let raw = iter.next()?; - let split = RawRepr::new(raw); - - let instruction = match split { - $( - $split $( if $cond )? => Some( Self::$variant { - $( - $field_name $( : $field_expr )?, - )* - } ), - )* - - _ => None, - }; - - instruction.map(|instruction| (raw.pos, instruction)) - } - } - } -} - -decl_instructions! { +/// All simple instructions +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(derive_more::Display)] +#[allow(clippy::missing_docs_in_private_items)] // They're mostly register and immediate names. +pub enum SimpleInstruction { /// Store byte #[display(fmt = "sb {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x28, - rs, rt, imm16, - .. - } => Sb { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Sb { rt: Register, rs: Register, offset: u16 }, /// Store half-word #[display(fmt = "sh {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x29, - rs, rt, imm16, - .. - } => Sh { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Sh { rt: Register, rs: Register, offset: u16 }, /// Store left word #[display(fmt = "swl {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x2a, - rs, rt, imm16, - .. - } => Swl { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Swl { rt: Register, rs: Register, offset: u16 }, /// Store word #[display(fmt = "sw {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x2b, - rs, rt, imm16, - .. - } => Sw { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Sw { rt: Register, rs: Register, offset: u16 }, /// Store right word #[display(fmt = "swr {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x2e, - rs, rt, imm16, - .. - } => Swr { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, - - + Swr { rt: Register, rs: Register, offset: u16 }, /// Load byte #[display(fmt = "lb {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x20, - rs, rt, imm16, - .. - } => Lb { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lb { rt: Register, rs: Register, offset: u16 }, /// Load byte unsigned #[display(fmt = "lbu {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x24, - rs, rt, imm16, - .. - } => Lbu { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lbu { rt: Register, rs: Register, offset: u16 }, /// Load half-word #[display(fmt = "lh {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x21, - rs, rt, imm16, - .. - } => Lh { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lh { rt: Register, rs: Register, offset: u16 }, /// Load half-word unsigned #[display(fmt = "lhu {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x25, - rs, rt, imm16, - .. - } => Lhu { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lhu { rt: Register, rs: Register, offset: u16 }, /// Load left word #[display(fmt = "lwl {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x22, - rs, rt, imm16, - .. - } => Lwl { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lwl { rt: Register, rs: Register, offset: u16 }, /// Load word #[display(fmt = "lw {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x23, - rs, rt, imm16, - .. - } => Lw { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lw { rt: Register, rs: Register, offset: u16 }, /// Load right word #[display(fmt = "lwr {rt}, {offset:#x}({rs})")] - RawRepr { - op: 0x26, - rs, rt, imm16, - .. - } => Lwr { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - offset: u16 = imm16, - }, + Lwr { rt: Register, rs: Register, offset: u16 }, /// Add #[display(fmt = "add {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x20, - rd, rs, rt, - .. - } => Add { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Add { rd: Register, rs: Register, rt: Register }, /// Add unsigned #[display(fmt = "addu {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x21, - rd, rs, rt, - .. - } => Addu { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Addu { rd: Register, rs: Register, rt: Register }, /// Sub #[display(fmt = "sub {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x22, - rd, rs, rt, - .. - } => Sub { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Sub { rd: Register, rs: Register, rt: Register }, /// Sub unsigned #[display(fmt = "subu {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x23, - rd, rs, rt, - .. - } => Subu { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Subu { rd: Register, rs: Register, rt: Register }, /// Add immediate #[display(fmt = "addi {rt}, {rs}, {:#x}", "SignedHex(imm)")] - RawRepr { - op: 0x08, - rt, rs, imm16, - .. - } => Addi { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: i16 = imm16.as_signed(), - }, + Addi { rt: Register, rs: Register, imm: i16 }, /// Add immediate sign-extended /// Note: _NOT_ Unsigned. #[display(fmt = "addiu {rt}, {rs}, {:#x}", "SignedHex(imm)")] - RawRepr { - op: 0x09, - rt, rs, imm16, - .. - } => Addiu { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: i16 = imm16.as_signed(), - }, + Addiu { rt: Register, rs: Register, imm: i16 }, /// Set less than #[display(fmt = "slt {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x2a, - rd, rs, rt, - .. - } => Slt { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Slt { rd: Register, rs: Register, rt: Register }, /// Set less than unsigned #[display(fmt = "sltu {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x2b, - rd, rs, rt, - .. - } => Sltu { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Sltu { rd: Register, rs: Register, rt: Register }, /// Set less than immediate #[display(fmt = "slti {rt}, {rs}, {:#x}", "SignedHex(imm)")] - RawRepr { - op: 0x0a, - rt, rs, imm16, - .. - } => Slti { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: i16 = imm16.as_signed(), - }, + Slti { rt: Register, rs: Register, imm: i16 }, /// Set less than immediate unsigned #[display(fmt = "sltiu {rt}, {rs}, {imm:#x}")] - RawRepr { - op: 0x0b, - rt, rs, imm16, - .. - } => Sltiu { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: u16 = imm16, - }, + Sltiu { rt: Register, rs: Register, imm: u16 }, /// And #[display(fmt = "and {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x24, - rd, rs, rt, - .. - } => And { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + And { rd: Register, rs: Register, rt: Register }, /// Or #[display(fmt = "or {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x25, - rd, rs, rt, - .. - } => Or { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Or { rd: Register, rs: Register, rt: Register }, /// Xor #[display(fmt = "xor {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x26, - rd, rs, rt, - .. - } => Xor { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Xor { rd: Register, rs: Register, rt: Register }, /// Nor #[display(fmt = "nor {rd}, {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x27, - rd, rs, rt, - .. - } => Nor { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Nor { rd: Register, rs: Register, rt: Register }, /// And immediate #[display(fmt = "andi {rt}, {rs}, {imm:#x}")] - RawRepr { - op: 0x0c, - rt, rs, imm16, - .. - } => Andi { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: u16 = imm16, - }, + Andi { rt: Register, rs: Register, imm: u16 }, /// Or immediate #[display(fmt = "ori {rt}, {rs}, {imm:#x}")] - RawRepr { - op: 0x0d, - rt, rs, imm16, - .. - } => Ori { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: u16 = imm16, - }, + Ori { rt: Register, rs: Register, imm: u16 }, /// Xor immediate #[display(fmt = "xori {rt}, {rs}, {imm:#x}")] - RawRepr { - op: 0x0e, - rt, rs, imm16, - .. - } => Xori { - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - imm: u16 = imm16, - }, + Xori { rt: Register, rs: Register, imm: u16 }, /// Shift left logical variable #[display(fmt = "sllv {rd}, {rt}, {rs}")] - RawRepr { - op: 0x00, op2: 0x04, - rd, rs, rt, - .. - } => Sllv { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - }, + Sllv { rd: Register, rt: Register, rs: Register }, /// Shift right logical variable #[display(fmt = "srlv {rd}, {rt}, {rs}")] - RawRepr { - op: 0x00, op2: 0x06, - rd, rs, rt, - .. - } => Srlv { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - }, + Srlv { rd: Register, rt: Register, rs: Register }, /// Shift right arithmetic variable #[display(fmt = "srav {rd}, {rt}, {rs}")] - RawRepr { - op: 0x00, op2: 0x07, - rd, rs, rt, - .. - } => Srav { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - rs: Register = Register::new(rs)?, - }, + Srav { rd: Register, rt: Register, rs: Register }, /// Shift left logical #[display(fmt = "sll {rd}, {rt}, {imm:#x}")] - RawRepr { - op: 0x00, op2: 0x00, - rd, rt, imm5, - .. - } => Sll { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - imm: u8 = imm5, - }, + Sll { rd: Register, rt: Register, imm: u8 }, /// Shift right logical #[display(fmt = "srl {rd}, {rt}, {imm:#x}")] - RawRepr { - op: 0x00, op2: 0x02, - rd, rt, imm5, - .. - } => Srl { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - imm: u8 = imm5, - }, + Srl { rd: Register, rt: Register, imm: u8 }, /// Shift right arithmetic #[display(fmt = "sra {rd}, {rt}, {imm:#x}")] - RawRepr { - op: 0x00, op2: 0x03, - rd, rt, imm5, - .. - } => Sra { - rd: Register = Register::new(rd)?, - rt: Register = Register::new(rt)?, - imm: u8 = imm5, - }, + Sra { rd: Register, rt: Register, imm: u8 }, /// Load upper immediate #[display(fmt = "lui {rt}, {imm:#x}")] - RawRepr { - op: 0x0f, - rt, imm16, - .. - } => Lui { - rt: Register = Register::new(rt)?, - imm: u16 = imm16, - }, + Lui { rt: Register, imm: u16 }, /// Multiply #[display(fmt = "mult {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x18, - rs, rt, - .. - } => Mult { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Mult { rs: Register, rt: Register }, /// Multiply unsigned #[display(fmt = "multu {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x19, - rs, rt, - .. - } => Multu { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Multu { rs: Register, rt: Register }, /// Divide #[display(fmt = "div {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x1a, - rs, rt, - .. - } => Div { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Div { rs: Register, rt: Register }, /// Multiply unsigned #[display(fmt = "divu {rs}, {rt}")] - RawRepr { - op: 0x00, op2: 0x1b, - rs, rt, - .. - } => Divu { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - }, + Divu { rs: Register, rt: Register }, /// Move from hi #[display(fmt = "mfhi {rd}")] - RawRepr { - op: 0x00, op2: 0x10, - rd, - .. - } => Mfhi { - rd: Register = Register::new(rd)?, - }, + Mfhi { rd: Register }, /// Move from lo #[display(fmt = "mflo {rd}")] - RawRepr { - op: 0x00, op2: 0x11, - rd, - .. - } => Mflo { - rd: Register = Register::new(rd)?, - }, + Mflo { rd: Register }, /// Move to hi - #[display(fmt = "mthi {rd}")] - RawRepr { - op: 0x00, op2: 0x12, - rd, - .. - } => Mthi { - rd: Register = Register::new(rd)?, - }, + #[display(fmt = "mthi {rs}")] + Mthi { rs: Register }, /// Move to lo - #[display(fmt = "mtlo {rd}")] - RawRepr { - op: 0x00, op2: 0x13, - rd, - .. - } => Mtlo { - rd: Register = Register::new(rd)?, - }, + #[display(fmt = "mtlo {rs}")] + Mtlo { rs: Register }, /// Jump #[display(fmt = "j {target:#x}")] - RawRepr { - op: 0x02, - imm26, pos, - .. - } => J { - target: u32 = i32::as_unsigned(u32::as_signed(pos & 0xF000_0000) + imm26.as_signed() * 4), - }, + J { target: Pos }, /// Jump and link #[display(fmt = "jal {target:#x}")] - RawRepr { - op: 0x03, - imm26, pos, - .. - } => Jal { - target: u32 = i32::as_unsigned(u32::as_signed(pos & 0xF000_0000) + imm26.as_signed() * 4), - }, + Jal { target: Pos }, /// Jump register #[display(fmt = "jr {rs}")] - RawRepr { - op: 0x00, op2: 0x08, - rs, - .. - } => Jr { - rs: Register = Register::new(rs)?, - }, + Jr { rs: Register }, /// Jump and link register #[display(fmt = "jalr {rd}, {rs}")] - RawRepr { - op: 0x00, op2: 0x09, - rd, rs, - .. - } => Jalr { - rd: Register = Register::new(rd)?, - rs: Register = Register::new(rs)?, - }, + Jalr { rd: Register, rs: Register }, /// Branch if equal #[display(fmt = "beq {rs}, {rt}, {target:#x}")] - RawRepr { - op: 0x04, - rs, rt, imm16, pos, - .. - } => Beq { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4), - }, + Beq { rs: Register, rt: Register, target: Pos }, /// Branch if not equal #[display(fmt = "bne {rs}, {rt}, {target:#x}")] - RawRepr { - op: 0x05, - rs, rt, imm16, pos, - .. - } => Bne { - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4), - }, + Bne { rs: Register, rt: Register, target: Pos }, /// Branch if less than zero #[display(fmt = "bltz {rs}, {target:#x}")] - RawRepr { - op: 0x01, rt: 0x00, - rs, imm16, pos, - .. - } => Bltz { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4) - }, + Bltz { rs: Register, target: Pos }, /// Branch if greater or equal to zero #[display(fmt = "bgez {rs}, {target:#x}")] - RawRepr { - op: 0x01, rt: 0x01, - rs, imm16, pos, - .. - } => Bgez { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4) - }, + Bgez { rs: Register, target: Pos }, /// Branch if greater than zero #[display(fmt = "bgtz {rs}, {target:#x}")] - RawRepr { - op: 0x07, - rs, imm16, pos, - .. - } => Bgtz { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4) - }, + Bgtz { rs: Register, target: Pos }, /// Branch if less or equal to zero #[display(fmt = "blez {rs}, {target:#x}")] - RawRepr { - op: 0x06, - rs, imm16, pos, - .. - } => Blez { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + 4 + imm16.as_signed().sign_extended::() * 4) - }, + Blez { rs: Register, target: Pos }, /// Branch if less than zero and link #[display(fmt = "bltzal {rs}, {target:#x}")] - RawRepr { - op: 0x01, rt: 0x10, - rs, imm16, pos, - .. - } => Bltzal { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + imm16.as_signed().sign_extended::() * 4) - }, + Bltzal { rs: Register, target: Pos }, /// Branch if greater or equal to zero and link #[display(fmt = "bgezal {rs}, {target:#x}")] - RawRepr { - op: 0x01, rt: 0x11, - rs, imm16, pos, - .. - } => Bgezal { - rs: Register = Register::new(rs)?, - target: u32 = i32::as_unsigned(pos.as_signed() + imm16.as_signed().sign_extended::() * 4) - }, + Bgezal { rs: Register, target: Pos }, /// Save co-processor data registers #[display(fmt = "mfc{n} {rt}, {rd}")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b0000, - co_n, rt, rd, - .. - } => MfcN { - n : u8 = co_n, - rt: Register = Register::new(rt)?, - rd: Register = Register::new(rd)?, - }, + MfcN { n: u8, rt: Register, rd: Register }, /// Save co-processor control registers #[display(fmt = "cfc{n} {rt}, {rd}")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b0010, - co_n, rt, rd, - .. - } => CfcN { - n : u8 = co_n, - rt: Register = Register::new(rt)?, - rd: Register = Register::new(rd)?, - }, + CfcN { n: u8, rt: Register, rd: Register }, /// Load co-processor data registers #[display(fmt = "mtc{n} {rt}, {rd}")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b0100, - co_n, rt, rd, - .. - } => MtcN { - n : u8 = co_n, - rt: Register = Register::new(rt)?, - rd: Register = Register::new(rd)?, - }, + MtcN { n: u8, rt: Register, rd: Register }, /// Load co-processor control registers #[display(fmt = "ctc{n} {rt}, {rd}")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b0110, - co_n, rt, rd, - .. - } => CtcN { - n : u8 = co_n, - rt: Register = Register::new(rt)?, - rd: Register = Register::new(rd)?, - }, + 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")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b1000, rt: 0b00000, - co_n, imm16, - .. - } => BcNf { - n: u8 = co_n, - target: u16 = imm16, - }, + BcNf { n: u8, target: u16 }, /// Branch co-processor if true #[display(fmt = "bc{n}t {target:#x} # Raw target")] - RawRepr { - co_op: 0b0100, co_rs0: 0, co_rs1: 0b1000, rt: 0b00001, - co_n, imm16, - .. - } => BcNt { - n: u8 = co_n, - target: u16 = imm16, - }, + BcNt { n: u8, target: u16 }, /// Exec immediate co-processor #[display(fmt = "cop{n} {imm:#x}")] - RawRepr { - co_op: 0b0100, co_rs0: 1, - co_n, imm25, - .. - } => CopN { - n: u8 = co_n, - imm: u32 = imm25, - }, + CopN { n: u8, imm: u32 }, /// Load word co-processor #[display(fmt = "lwc{n} {rt}, {imm:#x}({rs})")] - RawRepr { - co_op: 0b1100, - co_n, rs, rt, imm16, - .. - } => LwcN { - n: u8 = co_n, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - imm: u16 = imm16, - }, + LwcN { n: u8, rs: Register, rt: Register, imm: u16 }, /// Store word co-processor #[display(fmt = "swc{n} {rt}, {imm:#x}({rs})")] - RawRepr { - co_op: 0b1110, - co_n, rs, rt, imm16, - .. - } => SwcN { - n: u8 = co_n, - rs: Register = Register::new(rs)?, - rt: Register = Register::new(rt)?, - imm: u16 = imm16, - }, + SwcN { n: u8, rs: Register, rt: Register, imm: u16 }, /// Syscall #[display(fmt = "sys {imm:#x}")] - RawRepr { - op: 0x00, op2: 0x0c, - sys_imm, - .. - } => Syscall { - imm: u32 = sys_imm, - }, + Syscall { imm: u32 }, /// Break #[display(fmt = "break {imm:#x}")] - RawRepr { - op: 0x00, op2: 0x0d, - sys_imm, - .. - } => Break { - imm: u32 = sys_imm, - }, + Break { imm: u32 }, +} + +impl SimpleInstruction { + /// Decodes an instruction from it's raw representation + #[bitmatch] + #[allow(clippy::cognitive_complexity)] // It's just a big match, not much we can do about it. + fn decode_repr(Raw { repr, pos }: Raw) -> Option { + #[allow(clippy::enum_glob_use)] // It's local to this function and REALLY reduces on the noise + use SimpleInstruction::*; + + /// Alias for `Register::new` + fn reg(idx: u32) -> Option { + Register::new(idx.truncated()) + } + + #[rustfmt::skip] + let instruction = #[bitmatch] + match repr { + "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::().as_signed().sign_extended::() + 1) * 4 }, + "000001_sssss_?????_iiiii_iiiii_iiiiii" => Bgez { rs: reg(s)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 1) * 4 }, + "000001_sssss_?????_iiiii_iiiii_iiiiii" => Bltzal { rs: reg(s)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 1) * 4 }, + "000001_sssss_?????_iiiii_iiiii_iiiiii" => Bgezal { rs: reg(s)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 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 + 1) * 4 }, + "000101_sssss_ttttt_iiiii_iiiii_iiiiii" => Bne { rs: reg(s)?, rt: reg(t)?, target: pos + (i + 1) * 4 }, + + "000110_sssss_?????_iiiii_iiiii_iiiiii" => Blez { rs: reg(s)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 1) * 4 }, + "000111_sssss_?????_iiiii_iiiii_iiiiii" => Bgtz { rs: reg(s)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 1) * 4 }, + + "001000_sssss_ttttt_iiiii_iiiii_iiiiii" => Addi { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::().as_signed() }, + "001001_sssss_ttttt_iiiii_iiiii_iiiiii" => Addiu { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::().as_signed() }, + "001010_sssss_ttttt_iiiii_iiiii_iiiiii" => Slti { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::().as_signed() }, + "001011_sssss_ttttt_iiiii_iiiii_iiiiii" => Sltiu { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() }, + "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() }, + "100001_sssss_ttttt_iiiii_iiiii_iiiiii" => Lh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "100010_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "100011_sssss_ttttt_iiiii_iiiii_iiiiii" => Lw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "100100_sssss_ttttt_iiiii_iiiii_iiiiii" => Lbu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "100101_sssss_ttttt_iiiii_iiiii_iiiiii" => Lhu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "100110_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + + "101000_sssss_ttttt_iiiii_iiiii_iiiiii" => Sb { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "101001_sssss_ttttt_iiiii_iiiii_iiiiii" => Sh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "101010_sssss_ttttt_iiiii_iiiii_iiiiii" => Swl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "101011_sssss_ttttt_iiiii_iiiii_iiiiii" => Sw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + "101110_sssss_ttttt_iiiii_iiiii_iiiiii" => Swr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated() }, + + "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) + } +} + +impl FromRawIter for SimpleInstruction { + type Decoded = Option<(Pos, Self)>; + + fn decode + Clone>(iter: &mut I) -> Self::Decoded { + let raw = iter.next()?; + let instruction = Self::decode_repr(raw)?; + Some((raw.pos, instruction)) + } } diff --git a/dcb/src/game/exe/instruction/simple/repr.rs b/dcb/src/game/exe/instruction/simple/repr.rs deleted file mode 100644 index 81d9bde..0000000 --- a/dcb/src/game/exe/instruction/simple/repr.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Raw instruction representation - -// Imports -use super::Raw; -use int_conv::Truncate; - -/// An instruction's raw representation, including -/// it's current address. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[allow(clippy::missing_docs_in_private_items)] -pub struct RawRepr { - pub op: u8, - pub rs: u8, - pub rt: u8, - pub rd: u8, - pub imm5: u8, - pub op2: u8, - pub imm16: u16, - pub imm25: u32, - pub imm26: u32, - - /// Syscall / Break immediate - pub sys_imm: u32, - - /// Co-processor opcode - pub co_op: u8, - - /// Co-processor number - pub co_n: u8, - - /// Co-processor highest `rs` bit. - pub co_rs0: u8, - - /// Co-processor lowest `rs` bits. - pub co_rs1: u8, - - /// Position of the instruction - pub pos: u32, -} - -#[allow(clippy::inconsistent_digit_grouping)] // We're grouping 6-5-5-5-5-6 as per docs. -impl RawRepr { - /// Creates a new split instruction - #[must_use] - #[rustfmt::skip] - pub fn new(Raw {repr, pos}: Raw) -> Self { - Self { - op : ((repr & 0b111111_00000_00000_00000_00000_000000) >> 26).truncate(), - rs : ((repr & 0b000000_11111_00000_00000_00000_000000) >> 21).truncate(), - rt : ((repr & 0b000000_00000_11111_00000_00000_000000) >> 16).truncate(), - rd : ((repr & 0b000000_00000_00000_11111_00000_000000) >> 11).truncate(), - imm5 : ((repr & 0b000000_00000_00000_00000_11111_000000) >> 6 ).truncate(), - op2 : ((repr & 0b000000_00000_00000_00000_00000_111111) >> 0 ).truncate(), - imm16 : ((repr & 0b000000_00000_00000_11111_11111_111111) >> 0 ).truncate(), - imm25 : ((repr & 0b000000_01111_11111_11111_11111_111111) >> 0 ), - imm26 : ((repr & 0b000000_11111_11111_11111_11111_111111) >> 0 ), - sys_imm: ((repr & 0b000000_11111_11111_11111_11111_000000) >> 0 ), - co_op : ((repr & 0b111100_00000_00000_00000_00000_000000) >> 28).truncate(), - co_rs0 : ((repr & 0b000000_10000_00000_00000_00000_000000) >> 25).truncate(), - co_rs1 : ((repr & 0b000000_01111_00000_00000_00000_000000) >> 21).truncate(), - co_n : ((repr & 0b000011_00000_00000_00000_00000_000000) >> 26).truncate(), - pos: pos.0, - } - } -} diff --git a/dcb/src/game/exe/pos.rs b/dcb/src/game/exe/pos.rs index 8bb84b4..8e5fd64 100644 --- a/dcb/src/game/exe/pos.rs +++ b/dcb/src/game/exe/pos.rs @@ -2,6 +2,7 @@ // TODO: More implementations for `Pos` // Imports +use int_conv::Signed; use std::{fmt, ops}; /// An instruction position @@ -26,6 +27,30 @@ impl ops::Sub for Pos { } } +impl ops::Add for Pos { + type Output = Self; + + fn add(self, rhs: u32) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl ops::Add for Pos { + type Output = Self; + + fn add(self, rhs: i32) -> Self::Output { + Self((self.0.as_signed() + rhs).as_unsigned()) + } +} + +impl ops::BitAnd for Pos { + type Output = Self; + + fn bitand(self, rhs: u32) -> Self::Output { + Self(self.0 & rhs) + } +} + impl<'a, T> ops::Sub for &'_ Pos where Pos: ops::Sub, @@ -36,3 +61,14 @@ where >::sub(Pos(self.0), rhs) } } + +impl<'a, T> ops::Add for &'_ Pos +where + Pos: ops::Add, +{ + type Output = Pos; + + fn add(self, rhs: T) -> Self::Output { + >::add(Pos(self.0), rhs) + } +} diff --git a/dcb/src/lib.rs b/dcb/src/lib.rs index 5b78d77..f850562 100644 --- a/dcb/src/lib.rs +++ b/dcb/src/lib.rs @@ -102,6 +102,8 @@ #![allow(clippy::panic_in_result_fn)] // A `match Option / Result / Bool` can sometimes look cleaner than a `if let / else` #![allow(clippy::single_match_else, clippy::match_bool)] +// We're usually fine with missing future variants +#![allow(clippy::wildcard_enum_match_arm)] // Modules