diff --git a/dcb-tools/src/decompiler/main.rs b/dcb-tools/src/decompiler/main.rs index 7621932..9eba15a 100644 --- a/dcb-tools/src/decompiler/main.rs +++ b/dcb-tools/src/decompiler/main.rs @@ -80,11 +80,11 @@ use byteorder::{ByteOrder, LittleEndian}; use dcb::{ game::exe::{ instruction::{ - Directive, Pos, + Directive, PseudoInstruction::{self, Nop}, Raw, Register, SimpleInstruction, }, - Instruction, + Instruction, Pos, }, GameFile, }; diff --git a/dcb/Cargo.toml b/dcb/Cargo.toml index 1c7ebc8..c32f693 100644 --- a/dcb/Cargo.toml +++ b/dcb/Cargo.toml @@ -17,6 +17,7 @@ byteorder = "1.3" ascii = { version = "1.0", features = ["serde"] } arrayref = "0.3" int-conv = "0.1" +indoc = "1.0" # Serde serde = { version = "1.0", features = ["derive"] } diff --git a/dcb/src/game/exe.rs b/dcb/src/game/exe.rs index d83c51b..9537353 100644 --- a/dcb/src/game/exe.rs +++ b/dcb/src/game/exe.rs @@ -5,13 +5,17 @@ // Modules pub mod error; +pub mod func; pub mod header; pub mod instruction; +pub mod pos; // Exports pub use error::DeserializeError; +pub use func::Func; pub use header::Header; pub use instruction::Instruction; +pub use pos::Pos; // Imports use crate::{io::address::Data, GameFile}; diff --git a/dcb/src/game/exe/func.rs b/dcb/src/game/exe/func.rs new file mode 100644 index 0000000..5847e1d --- /dev/null +++ b/dcb/src/game/exe/func.rs @@ -0,0 +1,64 @@ +//! Executable functions + +// Imports +use crate::game::exe::Pos; +use indoc::indoc; +use std::borrow::Cow; + +/// A function within the executable +#[derive(PartialEq, Eq, Clone, Hash, Debug)] +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Func { + /// Function signature + signature: Cow<'static, str>, + + /// Description + desc: Cow<'static, str>, + + /// Start position + start_pos: Pos, + + /// End position + end_pos: Pos, +} + +impl Func { + /// All currently known functions + pub const FUNCTIONS: &'static [Self] = &[ + Self { + signature: Cow::Borrowed("void InitHeap(int* addr, unsigned int size)"), + desc: Cow::Borrowed("Calls A(0x39)"), + start_pos: Pos(0x8006a734), + end_pos: Pos(0x8006a744), + }, + Self { + signature: Cow::Borrowed("void start(void)"), + desc: Cow::Borrowed(indoc! {" + Executable start. + Zeroes out 0x80077a08..0x801ddf38. + Initializes the stack, frame and global pointer. + Calls InitHeap(0x8007f988, ???). + Calls func_1025(0x8007f98c). + Calls func_1026(string_0, string_0). + "}), + start_pos: Pos(0x80056270), + end_pos: Pos(0x80056328), + }, + Self { + signature: Cow::Borrowed("void func_1025(int*)"), + desc: Cow::Borrowed(indoc! {" + At the end, calls func_446 indefinitely. + "}), + start_pos: Pos(0x80013e4c), + end_pos: Pos(0x80013f00), + }, + Self { + signature: Cow::Borrowed("int func_446(int)"), + desc: Cow::Borrowed(indoc! {" + + "}), + start_pos: Pos(0x80069124), + end_pos: Pos(0x80069150), + }, + ]; +} diff --git a/dcb/src/game/exe/instruction.rs b/dcb/src/game/exe/instruction.rs index e02fbf0..959e688 100644 --- a/dcb/src/game/exe/instruction.rs +++ b/dcb/src/game/exe/instruction.rs @@ -2,7 +2,6 @@ // Modules pub mod directive; -pub mod pos; pub mod pseudo; pub mod raw; pub mod reg; @@ -10,12 +9,14 @@ pub mod simple; // Exports pub use directive::Directive; -pub use pos::Pos; pub use pseudo::PseudoInstruction; pub use raw::{FromRawIter, Raw}; pub use reg::Register; pub use simple::SimpleInstruction; +// Imports +use crate::game::exe::Pos; + /// An assembler instruction #[derive(PartialEq, Eq, Clone, Debug)] #[derive(derive_more::Display)] diff --git a/dcb/src/game/exe/instruction/directive.rs b/dcb/src/game/exe/instruction/directive.rs index b873759..e55b2b9 100644 --- a/dcb/src/game/exe/instruction/directive.rs +++ b/dcb/src/game/exe/instruction/directive.rs @@ -1,7 +1,8 @@ //! Directives // Imports -use super::{FromRawIter, Pos, Raw}; +use super::{FromRawIter, Raw}; +use crate::game::exe::Pos; use ascii::{AsciiChar, AsciiStr, AsciiString}; use AsciiChar::Null; diff --git a/dcb/src/game/exe/instruction/pseudo.rs b/dcb/src/game/exe/instruction/pseudo.rs index 54551ff..5c7e2b2 100644 --- a/dcb/src/game/exe/instruction/pseudo.rs +++ b/dcb/src/game/exe/instruction/pseudo.rs @@ -1,8 +1,8 @@ //! Pseudo instructions // Imports -use super::{FromRawIter, Pos, Raw, Register, SimpleInstruction}; -use crate::util::SignedHex; +use super::{FromRawIter, Raw, Register, SimpleInstruction}; +use crate::{game::exe::Pos, util::SignedHex}; use int_conv::{Join, SignExtended, Signed, ZeroExtended}; /// A pseudo instruction @@ -91,7 +91,7 @@ pub enum PseudoInstruction { Li32 { rx: Register, imm: u32 }, /// Load immediate 16-bit - /// Alias for `ori $rx, $zr, imm` + /// Alias for `{ori,addiu} $rx, $zr, imm`, with imm > 0 for `addiu` #[display(fmt = "li {rx}, {imm:#x}")] Li16 { rx: Register, imm: u16 }, @@ -105,6 +105,11 @@ pub enum PseudoInstruction { #[display(fmt = "li {rx}, {:#x}", "imm.zero_extended::() << 16")] LiUpper16 { rx: Register, imm: u16 }, + /// Add immediate assign + /// Alias for `addi $rx, $rx, imm` + #[display(fmt = "addi {rx}, {:#x}", "SignedHex(imm)")] + AddiAssign { rx: Register, imm: i16 }, + /// Add assign /// Alias for `add $rx, $rx, $rt` #[display(fmt = "add {rx}, {rt}")] @@ -196,24 +201,24 @@ pub enum PseudoInstruction { JalrRa { rx: Register }, /// Subtract immediate - /// Alias for `addi $rt, $rs, imm` for negative `imm`s - #[display(fmt = "subi {rt}, {rs}, {:#x}", "SignedHex(imm)")] - Subi { rt: Register, rs: Register, imm: i16 }, + /// 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` for negative `imm`s - #[display(fmt = "subiu {rt}, {rs}, {:#x}", "SignedHex(imm)")] - Subiu { rt: Register, rs: Register, imm: i16 }, + /// 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}, {:#x}", "SignedHex(imm)")] - SubiAssign { rx: Register, imm: i16 }, + #[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}, {:#x}", "SignedHex(imm)")] - SubiuAssign { rx: Register, imm: i16 }, + #[display(fmt = "subiu {rx}, {imm:#x}")] + SubiuAssign { rx: Register, imm: u32 }, /// Branch if equal to zero /// Alias for `beq $rx, $zr, target` @@ -245,6 +250,16 @@ impl FromRawIter for PseudoInstruction { Srlv, Sub, Subu, Sw, Swl, Swr, Xor, Xori, }; + // TODO: Deal with code such as: + // li $rx, 0xXXXX_0000 + // l* $ry, 0xZZZZ_ZZZZ + // ori $rx, 0xYYYY + // + // And transform it into + // l* $ry, 0xZZZZ_ZZZZ + // ori $rx 0xXXXX_YYYY + // Assuming the `l*` doesn't use $rx + // Get the first instruction let (pos, instruction) = SimpleInstruction::decode(iter)?; let pseudo = match instruction { @@ -322,7 +337,12 @@ impl FromRawIter for PseudoInstruction { Addu { rd, rs, rt: Zr } | Addiu { rt: rd, rs, imm: 0 } | Or { rd, rs, rt: Zr } => Some(Self::MovReg { rd, rs }), - Ori { rt, rs: Zr, imm } => Some(Self::Li16 { rx: rt, imm }), + Addiu { + rt: rx, + rs: Zr, + imm: imm @ 1..i16::MAX, + } => Some(Self::Li16 { rx, imm: imm.as_unsigned() }), + Ori { rt: rx, rs: Zr, imm } => Some(Self::Li16 { rx, imm }), Add { rd, rs, rt } if rd == rs => Some(Self::AddAssign { rx: rd, rt }), Addu { rd, rs, rt } if rd == rs => Some(Self::AdduAssign { rx: rd, rt }), @@ -353,8 +373,15 @@ impl FromRawIter for PseudoInstruction { rs, imm: imm @ i16::MIN..0, } => match rt == rs { - true => Some(Self::SubiAssign { rx: rt, imm }), - false => Some(Self::Subi { rt, rs, imm }), + true => Some(Self::SubiAssign { + rx: rt, + imm: imm.sign_extended::().abs().as_unsigned(), + }), + false => Some(Self::Subi { + rt, + rs, + imm: imm.sign_extended::().abs().as_unsigned(), + }), }, Addiu { @@ -368,10 +395,19 @@ impl FromRawIter for PseudoInstruction { rs, imm: imm @ i16::MIN..0, } => match rt == rs { - true => Some(Self::SubiuAssign { rx: rt, imm }), - false => Some(Self::Subiu { rt, rs, imm }), + true => Some(Self::SubiuAssign { + rx: rt, + imm: imm.sign_extended::().abs().as_unsigned(), + }), + false => Some(Self::Subiu { + rt, + rs, + imm: imm.sign_extended::().abs().as_unsigned(), + }), }, + Addi { rt, rs, imm } if rt == rs => Some(Self::AddiAssign { rx: rt, imm }), + Beq { rs: Zr, rt: Zr, target } => Some(Self::B { target }), Beq { rs: rx, rt: Zr, target } => Some(Self::Beqz { rx, target }), Bne { rs: rx, rt: Zr, target } => Some(Self::Bnez { rx, target }), diff --git a/dcb/src/game/exe/instruction/raw.rs b/dcb/src/game/exe/instruction/raw.rs index 1e04478..70de899 100644 --- a/dcb/src/game/exe/instruction/raw.rs +++ b/dcb/src/game/exe/instruction/raw.rs @@ -1,7 +1,7 @@ //! Raw instructions // Imports -use super::Pos; +use crate::game::exe::Pos; /// A raw instruction #[derive(PartialEq, Eq, Clone, Copy, Debug)] diff --git a/dcb/src/game/exe/instruction/simple.rs b/dcb/src/game/exe/instruction/simple.rs index e264af0..acdd965 100644 --- a/dcb/src/game/exe/instruction/simple.rs +++ b/dcb/src/game/exe/instruction/simple.rs @@ -10,8 +10,8 @@ pub mod repr; pub use repr::RawRepr; // Imports -use super::{FromRawIter, Pos, Raw, Register}; -use crate::util::SignedHex; +use super::{FromRawIter, Raw, Register}; +use crate::{game::exe::Pos, util::SignedHex}; use int_conv::{SignExtended, Signed}; /// Macro to declare all instructions @@ -826,4 +826,24 @@ decl_instructions! { rt: Register = Register::new(rt)?, imm: u16 = imm16, }, + + /// Syscall + #[display(fmt = "sys {imm:#x}")] + RawRepr { + op: 0x00, op2: 0x0c, + sys_imm, + .. + } => Syscall { + imm: u32 = sys_imm, + }, + + /// Break + #[display(fmt = "break {imm:#x}")] + RawRepr { + op: 0x00, op2: 0x0d, + sys_imm, + .. + } => Break { + imm: u32 = sys_imm, + }, } diff --git a/dcb/src/game/exe/instruction/simple/repr.rs b/dcb/src/game/exe/instruction/simple/repr.rs index daf48b3..81d9bde 100644 --- a/dcb/src/game/exe/instruction/simple/repr.rs +++ b/dcb/src/game/exe/instruction/simple/repr.rs @@ -19,6 +19,9 @@ pub struct RawRepr { pub imm25: u32, pub imm26: u32, + /// Syscall / Break immediate + pub sys_imm: u32, + /// Co-processor opcode pub co_op: u8, @@ -42,19 +45,20 @@ impl RawRepr { #[rustfmt::skip] pub fn new(Raw {repr, pos}: Raw) -> Self { Self { - op : ((repr & 0b111111_00000_00000_00000_00000_000000) >> 26).truncate(), - rs : ((repr & 0b000000_11111_00000_00000_00000_000000) >> 21).truncate(), - rt : ((repr & 0b000000_00000_11111_00000_00000_000000) >> 16).truncate(), - rd : ((repr & 0b000000_00000_00000_11111_00000_000000) >> 11).truncate(), - imm5 : ((repr & 0b000000_00000_00000_00000_11111_000000) >> 6 ).truncate(), - op2 : ((repr & 0b000000_00000_00000_00000_00000_111111) >> 0 ).truncate(), - imm16 : ((repr & 0b000000_00000_00000_11111_11111_111111) >> 0 ).truncate(), - imm25 : ((repr & 0b000000_01111_11111_11111_11111_111111) >> 0 ), - imm26 : ((repr & 0b000000_11111_11111_11111_11111_111111) >> 0 ), - co_op : ((repr & 0b111100_00000_00000_00000_00000_000000) >> 28).truncate(), - co_rs0: ((repr & 0b000000_10000_00000_00000_00000_000000) >> 25).truncate(), - co_rs1: ((repr & 0b000000_01111_00000_00000_00000_000000) >> 21).truncate(), - co_n : ((repr & 0b000011_00000_00000_00000_00000_000000) >> 26).truncate(), + 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/instruction/pos.rs b/dcb/src/game/exe/pos.rs similarity index 91% rename from dcb/src/game/exe/instruction/pos.rs rename to dcb/src/game/exe/pos.rs index 394d082..8bb84b4 100644 --- a/dcb/src/game/exe/instruction/pos.rs +++ b/dcb/src/game/exe/pos.rs @@ -6,7 +6,9 @@ use std::{fmt, ops}; /// An instruction position #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)] +#[derive(serde::Serialize, serde::Deserialize)] #[derive(ref_cast::RefCast)] +#[serde(transparent)] #[repr(transparent)] pub struct Pos(pub u32); diff --git a/rustfmt.toml b/rustfmt.toml index f0c05cf..33f5b69 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -54,4 +54,4 @@ inline_attribute_width = 0 use_try_shorthand = true force_explicit_abi = true error_on_line_overflow = true -error_on_unformatted = true +error_on_unformatted = false