diff --git a/dcb-tools/Cargo.toml b/dcb-tools/Cargo.toml index 4732068..271d588 100644 --- a/dcb-tools/Cargo.toml +++ b/dcb-tools/Cargo.toml @@ -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 diff --git a/dcb-tools/src/decompiler/main.rs b/dcb-tools/src/decompiler/main.rs index b3a9951..eccba50 100644 --- a/dcb-tools/src/decompiler/main.rs +++ b/dcb-tools/src/decompiler/main.rs @@ -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 diff --git a/dcb/src/game.rs b/dcb/src/game.rs index 660327a..5d5f3b3 100644 --- a/dcb/src/game.rs +++ b/dcb/src/game.rs @@ -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}; diff --git a/dcb/src/game/exe/func/table.rs b/dcb/src/game/exe/func/table.rs index 131ae7a..3443c1e 100644 --- a/dcb/src/game/exe/func/table.rs +++ b/dcb/src/game/exe/func/table.rs @@ -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 + Clone)) -> Self { - use BasicInstruction::*; - use Instruction::{Basic, Pseudo}; - use PseudoInstruction::*; - + pub fn from_instructions<'a>(_instructions: &(impl Iterator + Clone)) -> Self { + /* // Get all returns let returns: BTreeSet = instructions .clone() @@ -481,5 +471,7 @@ impl FuncTable { } }) .collect() + */ + todo!() } } diff --git a/dcb/src/game/exe/instruction.rs b/dcb/src/game/exe/instruction.rs index 0562c23..f97b8b9 100644 --- a/dcb/src/game/exe/instruction.rs +++ b/dcb/src/game/exe/instruction.rs @@ -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 + Clone> Iterator for Iter { } // 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)) + */ } } diff --git a/dcb/src/game/exe/instruction/basic.rs b/dcb/src/game/exe/instruction/basic.rs index 0d5862f..e96e5d8 100644 --- a/dcb/src/game/exe/instruction/basic.rs +++ b/dcb/src/game/exe/instruction/basic.rs @@ -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::(), - }, + "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::(); - 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::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::().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.truncated::().as_signed().sign_extended::() + 1) * 4 }, - "000101_sssss_ttttt_iiiii_iiiii_iiiiii" => Bne { rs: reg(s)?, rt: reg(t)?, target: pos + (i.truncated::().as_signed().sign_extended::() + 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::().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::().as_signed() }, - "100001_sssss_ttttt_iiiii_iiiii_iiiiii" => Lh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "100010_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "100011_sssss_ttttt_iiiii_iiiii_iiiiii" => Lw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "100100_sssss_ttttt_iiiii_iiiii_iiiiii" => Lbu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "100101_sssss_ttttt_iiiii_iiiii_iiiiii" => Lhu { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "100110_sssss_ttttt_iiiii_iiiii_iiiiii" => Lwr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - - "101000_sssss_ttttt_iiiii_iiiii_iiiiii" => Sb { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "101001_sssss_ttttt_iiiii_iiiii_iiiiii" => Sh { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "101010_sssss_ttttt_iiiii_iiiii_iiiiii" => Swl { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "101011_sssss_ttttt_iiiii_iiiii_iiiiii" => Sw { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().as_signed() }, - "101110_sssss_ttttt_iiiii_iiiii_iiiiii" => Swr { rt: reg(t)?, rs: reg(s)?, offset: i.truncated::().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 + 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)) - } -} diff --git a/dcb/src/game/exe/instruction/basic/alu_imm.rs b/dcb/src/game/exe/instruction/basic/alu_imm.rs index a6d2eec..f5655ba 100644 --- a/dcb/src/game/exe/instruction/basic/alu_imm.rs +++ b/dcb/src/game/exe/instruction/basic/alu_imm.rs @@ -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 { - let op = AluImmOp::try_from(raw.p.truncated::()).ok()?; + let kind = AluImmKind::try_from(raw.p.truncated::()).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::(); + let p = u8::from(self.kind).zero_extended::(); 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}"), } } } diff --git a/dcb/src/game/exe/instruction/basic/iter.rs b/dcb/src/game/exe/instruction/basic/iter.rs new file mode 100644 index 0000000..2076a1d --- /dev/null +++ b/dcb/src/game/exe/instruction/basic/iter.rs @@ -0,0 +1,63 @@ +//! Iterator over instructions + +// Imports +use super::BasicInst; + +/// Iterator over instructions +#[derive(PartialEq, Eq, Debug)] +pub struct InstIter<'a, I: Iterator + Clone> { + /// Underlying iterator + iter: &'a mut I, +} + +impl<'a, I: Iterator + 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 + Clone> { + /// Original iterator to update if consumed + iter: InstIter<'a, I>, + + /// Current iterator + cur_iter: I, +} + +impl<'a, I: Iterator + 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 + Clone> Iterator for InstPeeker<'a, I> { + type Item = Option; + + fn next(&mut self) -> Option { + // Consume our current iterator + self.cur_iter.next() + } +} diff --git a/dcb/src/game/exe/instruction/basic/load.rs b/dcb/src/game/exe/instruction/basic/load.rs index e7f5bc3..f9a0423 100644 --- a/dcb/src/game/exe/instruction/basic/load.rs +++ b/dcb/src/game/exe/instruction/basic/load.rs @@ -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 { - let op = LoadOpcode::try_from(raw.p.truncated::()).ok()?; + let op = LoadKind::try_from(raw.p.truncated::()).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::().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::(); - let p = u8::from(self.op).zero_extended::(); + let p = u8::from(self.kind).zero_extended::(); 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)) - } -} diff --git a/dcb/src/game/exe/instruction/basic/lui.rs b/dcb/src/game/exe/instruction/basic/lui.rs new file mode 100644 index 0000000..b748b1b --- /dev/null +++ b/dcb/src/game/exe/instruction/basic/lui.rs @@ -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 { + Some(Self { + dst: Register::new(raw.t)?, + value: raw.i.truncated::(), + }) + } + + /// Encodes this instruction + #[must_use] + pub fn encode(self) -> LuiRaw { + LuiRaw { + t: self.dst.idx(), + i: self.value.zero_extended::(), + } + } +} diff --git a/dcb/src/game/exe/instruction/basic/special/alu_reg.rs b/dcb/src/game/exe/instruction/basic/special/alu_reg.rs index a6710ac..495f792 100644 --- a/dcb/src/game/exe/instruction/basic/special/alu_reg.rs +++ b/dcb/src/game/exe/instruction/basic/special/alu_reg.rs @@ -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 { - let func = AluRegFunc::try_from(raw.f.truncated::()).ok()?; + let kind = AluRegKind::try_from(raw.f.truncated::()).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::(); + let f = u8::from(self.kind).zero_extended::(); 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}") } } diff --git a/dcb/src/game/exe/instruction/basic/special/mult.rs b/dcb/src/game/exe/instruction/basic/special/mult.rs index 40f2449..792797a 100644 --- a/dcb/src/game/exe/instruction/basic/special/mult.rs +++ b/dcb/src/game/exe/instruction/basic/special/mult.rs @@ -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 { #[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}"), }, } } diff --git a/dcb/src/game/exe/instruction/basic/special/shift/imm.rs b/dcb/src/game/exe/instruction/basic/special/shift/imm.rs index 3649b46..5ebcd02 100644 --- a/dcb/src/game/exe/instruction/basic/special/shift/imm.rs +++ b/dcb/src/game/exe/instruction/basic/special/shift/imm.rs @@ -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::().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::(); let f = u8::from(self.func).zero_extended::(); @@ -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}") } } diff --git a/dcb/src/game/exe/instruction/basic/special/shift/reg.rs b/dcb/src/game/exe/instruction/basic/special/shift/reg.rs index d6a08a0..5290630 100644 --- a/dcb/src/game/exe/instruction/basic/special/shift/reg.rs +++ b/dcb/src/game/exe/instruction/basic/special/shift/reg.rs @@ -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::()).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::(); @@ -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}") } } diff --git a/dcb/src/game/exe/instruction/basic/store.rs b/dcb/src/game/exe/instruction/basic/store.rs index 5a743d5..9395136 100644 --- a/dcb/src/game/exe/instruction/basic/store.rs +++ b/dcb/src/game/exe/instruction/basic/store.rs @@ -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 { - let kind = StoreOpcode::try_from(raw.p.truncated::()).ok()?; + let kind = StoreKind::try_from(raw.p.truncated::()).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::().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::(); - let p = u8::from(self.op).zero_extended::(); + let p = u8::from(self.kind).zero_extended::(); 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)) - } -} diff --git a/dcb/src/game/exe/instruction/pseudo.rs b/dcb/src/game/exe/instruction/pseudo.rs index 7528fee..5d15c40 100644 --- a/dcb/src/game/exe/instruction/pseudo.rs +++ b/dcb/src/game/exe/instruction/pseudo.rs @@ -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 + Clone>(_iter: &mut I) -> Option { + 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 + Clone>(iter: &mut I) -> Self::Decoded { - use BasicInstruction::*; - use Register::*; - + fn decode + 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::(); - let hi = u32::join(0, hi).as_signed(); - - (hi + lo).as_unsigned() -} diff --git a/dcb/src/game/exe/instruction/pseudo/load.rs b/dcb/src/game/exe/instruction/pseudo/load.rs new file mode 100644 index 0000000..1e7af43 --- /dev/null +++ b/dcb/src/game/exe/instruction/pseudo/load.rs @@ -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::()).as_unsigned(), + kind: load.kind, + } + } +} diff --git a/dcb/src/game/exe/instruction/pseudo/load_imm.rs b/dcb/src/game/exe/instruction/pseudo/load_imm.rs new file mode 100644 index 0000000..9b51f05 --- /dev/null +++ b/dcb/src/game/exe/instruction/pseudo/load_imm.rs @@ -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 + Clone>) -> Option { + 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), + */ + } + */ + } +} diff --git a/dcb/src/game/exe/instruction/pseudo/move_reg.rs b/dcb/src/game/exe/instruction/pseudo/move_reg.rs new file mode 100644 index 0000000..7b341b3 --- /dev/null +++ b/dcb/src/game/exe/instruction/pseudo/move_reg.rs @@ -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 { + 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, + } + } +} diff --git a/dcb/src/game/exe/instruction/pseudo/store.rs b/dcb/src/game/exe/instruction/pseudo/store.rs new file mode 100644 index 0000000..a5af8cc --- /dev/null +++ b/dcb/src/game/exe/instruction/pseudo/store.rs @@ -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::()).as_unsigned(), + kind: store.kind, + } + } +} diff --git a/dcb/src/io/game_file.rs b/dcb/src/io/game_file.rs index f177ce6..0f25992 100644 --- a/dcb/src/io/game_file.rs +++ b/dcb/src/io/game_file.rs @@ -104,10 +104,8 @@ impl Read for GameFile { // 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 Read for GameFile { 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 Write for GameFile { // 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 Write for GameFile { 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()` diff --git a/dcb/src/util/discarding_sorted_merge_iter.rs b/dcb/src/util/discarding_sorted_merge_iter.rs index 0888bd8..5cf62b3 100644 --- a/dcb/src/util/discarding_sorted_merge_iter.rs +++ b/dcb/src/util/discarding_sorted_merge_iter.rs @@ -20,6 +20,7 @@ pub struct DiscardingSortedMergeIter, Ri: Iterato impl, Ri: Iterator> DiscardingSortedMergeIter { /// Creates a new merging iterator + #[allow(dead_code)] // TODO: Remove pub fn new(lhs: Li, rhs: Ri) -> Self { Self { lhs, rhs, last: None } }