diff options
| author | iximeow <me@iximeow.net> | 2025-06-27 05:02:32 +0000 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2025-10-19 23:29:05 +0000 | 
| commit | b9b6bb943663a0cd09d039383903d5725fbe034b (patch) | |
| tree | 0022ceb857127718fd8b0d289a97ed45a67d5f5e | |
| parent | 7c46d5cdc9fdfbc3663a3627d522a259683f594c (diff) | |
visitor-oriented fmt
this makes a decode+format benchmark drop from 75s to 14s...
(decode is 5s)
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | src/armv7.rs | 1213 | ||||
| -rw-r--r-- | src/armv7/display.rs | 1438 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | tests/no_std_check/Cargo.toml | 2 | 
5 files changed, 1575 insertions, 1090 deletions
| @@ -21,7 +21,15 @@ bitvec = { version = "1.0.1", default-features = false, features = [] }  "serde_derive" = { version = "1.0", optional = true }  [features] -default = ["std", "use-serde"] +default = ["std", "alloc", "fmt", "use-serde"] + +# fmt-related features that depend on the alloc crate +alloc = [] + +# formatting code (Display, Debug impls, etc) are optional on `fmt`. +# +# some `fmt` code also has `alloc` features (`InstructionDisplayBuffer`) +fmt = []  # opt-in for std-related Error impl - necessary to `?`-unwrap `DecodeError`.  std = ["yaxpeax-arch/std"] diff --git a/src/armv7.rs b/src/armv7.rs index b8f33d3..b1ae2ba 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -9,9 +9,14 @@ use core::fmt::{self, Display, Formatter};  use yaxpeax_arch::{Arch, AddressDiff, Decoder, LengthedInstruction, Reader, ReadError};  #[allow(deprecated)] -use yaxpeax_arch::{Colorize, NoColors, ShowContextual, YaxColors}; +use yaxpeax_arch::{NoColors, ShowContextual};  mod thumb; +// #[cfg(feature = "fmt")] +mod display; + +#[cfg(all(feature="alloc", feature="fmt"))] +pub use display::InstructionTextBuffer;  // opcode, s, w, cond  /// a struct for the combined display of an opcode and possible suffixes. @@ -31,835 +36,6 @@ impl Display for ConditionedOpcode {  /// displaying an instruction the same way its `Display` impl would.  pub struct NoContext; -#[allow(deprecated)] -fn reg_name_colorize<Y: YaxColors>(reg: Reg, colors: &Y) -> impl fmt::Display { -    match reg.number() { -        0 => colors.register("r0"), -        1 => colors.register("r1"), -        2 => colors.register("r2"), -        3 => colors.register("r3"), -        4 => colors.register("r4"), -        5 => colors.register("r5"), -        6 => colors.register("r6"), -        7 => colors.register("r7"), -        8 => colors.register("r8"), -        9 => colors.register("sb"), -        10 => colors.register("r10"), -        11 => colors.register("fp"), -        12 => colors.register("ip"), -        13 => colors.register("sp"), -        14 => colors.register("lr"), -        15 => colors.program_counter("pc"), -        _ => { unreachable!(); } -    } -} - -#[allow(non_snake_case)] -#[allow(deprecated)] -impl <T: fmt::Write, Y: YaxColors> ShowContextual<u32, NoContext, T, Y> for Instruction { -    fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { -        match self.opcode { -            Opcode::IT => { -                if let (Operand::Imm32(cond), Operand::Imm32(mask)) = (&self.operands[0], &self.operands[1]) { -                    let inv = cond & 1 == 1; -                    let condition = ConditionCode::build(*cond as u8); -                    if mask & 0b0001 != 0 { -                        // three flags -                        write!( -                            out, -                            "it{}{}{} {}", -                            if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, -                            if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" }, -                            if inv ^ ((mask & 0b0010) != 0) { "e" } else { "t" }, -                            condition, -                        )?; -                    } else if mask & 0b0010 != 0 { -                        // two flags -                        write!( -                            out, -                            "it{}{} {}", -                            if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, -                            if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" }, -                            condition, -                        )?; -                    } else if mask & 0b0100 != 0 { -                        // one flag -                        write!( -                            out, -                            "it{} {}", -                            if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" }, -                            condition, -                        )?; -                    } else { -                        // no flags -                        write!(out, "it {}", condition)?; -                    } -                    // if the condition is AL, it won't get displayed. append it here. -                    if *cond == 14 { -                        write!(out, "al")?; -                    } -                    return Ok(()); -                } else { -                    panic!("impossible it operand"); -                } -            } -            Opcode::CPS(_) => { -                if let Operand::Imm12(aif) = &self.operands[0] { -                    write!( -                        out, -                        "{} {}{}{}", -                        &self.opcode, -                        if aif & 0b100 != 0 { "a" } else { "" }, -                        if aif & 0b010 != 0 { "i" } else { "" }, -                        if aif & 0b001 != 0 { "f" } else { "" }, -                    )?; -                    if let Operand::Imm12(mode) = &self.operands[1] { -                       write!(out, ", #{:x}", mode)?; -                    } -                    return Ok(()); -                } else { -                    panic!("impossible cps operand"); -                } -            } -            Opcode::SETEND => { -                if let Operand::Imm12(i) = &self.operands[0] { -                    return write!(out, "setend {}", if *i == 0 { "le" } else { "be" }); -                } else { -                    panic!("impossible setend operand"); -                } -            } -            Opcode::LDR => { -                match self.operands { -                    // TODO: should this be PostindexOffset? -                    [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, true, false), Operand::Nothing, Operand::Nothing] => { -                        ConditionedOpcode(Opcode::POP, self.s(), self.w(), self.condition).colorize(colors, out)?; -                        return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); -                    }, -                    _ => {} -                } -            }, -            Opcode::STR => { -                match self.operands { -                    // TODO: should this be PreindexOffset? -                    [Operand::Reg(Rt), Operand::RegDerefPreindexOffset(Reg { bits: 13 }, 4, false, true), Operand::Nothing, Operand::Nothing] => { -                        ConditionedOpcode(Opcode::PUSH, self.s(), self.w(), self.condition).colorize(colors, out)?; -                        return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); -                    }, -                    _ => {} -                } -            }, -            Opcode::LDM(true, false, false, _usermode) => { -                // TODO: what indicates usermode in the ARM syntax? -                match self.operands { -                    [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { -                        ConditionedOpcode(Opcode::POP, self.s(), self.w(), self.condition).colorize(colors, out)?; -                        write!(out, " ")?; -                        return format_reg_list(out, list, colors); -                    } -                    _ => {} -                } -            } -            Opcode::STM(false, true, false, _usermode) => { -                // TODO: what indicates usermode in the ARM syntax? -                match self.operands { -                    [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { -                        ConditionedOpcode(Opcode::PUSH, self.s(), self.w(), self.condition).colorize(colors, out)?; -                        write!(out, " ")?; -                        return format_reg_list(out, list, colors); -                    } -                    _ => {} -                } -            } -            _ => {} -        } - -        match self.opcode { -            // TODO: [add, pre, usermode] -            Opcode::STM(_add, _pre, _wback, _usermode) | -            Opcode::LDM(_add, _pre, _wback, _usermode) => { -                match self.operands { -                    [Operand::RegWBack(Rr, wback), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { -                        ConditionedOpcode(self.opcode, self.s(), self.w(), self.condition).colorize(colors, out)?; -                        write!( -                            out, " {}{}, ", -                            reg_name_colorize(Rr, colors), -                            if wback { "!" } else { "" } -                        )?; -                        return format_reg_list(out, list, colors); -                    }, -                    _ => { unreachable!(); } -                } -            }, -            Opcode::STCL(coproc) => { -                write!(out, "stcl p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::STC(coproc) => { -                write!(out, "stc p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::STC2L(coproc) => { -                write!(out, "stc2l p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::STC2(coproc) => { -                write!(out, "stc2 p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::LDC(coproc) => { -                write!(out, "ldc p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::LDCL(coproc) => { -                write!(out, "ldcl p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::LDC2(coproc) => { -                write!(out, "ldc2 p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::LDC2L(coproc) => { -                write!(out, "ldc2l p{}", coproc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::MRRC2(coproc, opc) => { -                write!(out, "mrrc2 p{}, {}", coproc, opc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::MCRR2(coproc, opc) => { -                write!(out, "mcrr2 p{}, {}", coproc, opc)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -            Opcode::MRC2(coproc, opc1, opc2) => { -                write!(out, "mrc2 p{}, {}", coproc, opc1)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                write!(out, ", {}", opc2)?; - -                Ok(()) -            } -            Opcode::MCR2(coproc, opc1, opc2) => { -                write!(out, "mcr2 p{}, {}", coproc, opc1)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                write!(out, ", {}", opc2)?; - -                Ok(()) -            } -            Opcode::CDP2(coproc, opc1, opc2) => { -                write!(out, "cdp2 p{}, {}", coproc, opc1)?; -                let ops = self.operands.iter(); -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                write!(out, ", {}", opc2)?; - -                Ok(()) -            } -            _ => { -                ConditionedOpcode(self.opcode, self.s(), self.w(), self.condition).colorize(colors, out)?; -                let mut ops = self.operands.iter(); -                if let Some(first_op) = ops.next() { -                    if let Operand::Nothing = first_op { -                        return Ok(()); -                    } -                    write!(out, " ")?; -                    first_op.colorize(colors, out)?; -                } else { -                    return Ok(()); -                } - -                for op in ops { -                    if let Operand::Nothing = op { -                        break; -                    } -                    write!(out, ", ")?; -                    op.colorize(colors, out)?; -                } - -                Ok(()) -            } -        } -    } -} - -#[allow(deprecated)] -impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for ConditionedOpcode { -    fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { -        match self.0 { -            Opcode::UDF | -            Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) }, -            Opcode::TBB | -            Opcode::TBH | -            Opcode::CBZ | -            Opcode::CBNZ | -            Opcode::IT | -            Opcode::B | -            Opcode::BL | -            Opcode::BLX | -            Opcode::BX | -            Opcode::BXJ => { write!(out, "{}", colors.control_flow_op(self)) }, - -            Opcode::AND | -            Opcode::EOR | -            Opcode::ORR | -            Opcode::ORN | -            Opcode::LSL | -            Opcode::LSR | -            Opcode::ROR | -            Opcode::ASR | -            Opcode::RRX | -            Opcode::BIC | - -            Opcode::ADR | -            Opcode::SUB | -            Opcode::RSB | -            Opcode::ADD | -            Opcode::ADC | -            Opcode::SBC | -            Opcode::RSC | - -            Opcode::QADD | -            Opcode::QSUB | -            Opcode::QDADD | -            Opcode::QDSUB | - -            Opcode::SADD16 | -            Opcode::QADD16 | -            Opcode::SHADD16 | -            Opcode::SASX | -            Opcode::QASX | -            Opcode::SHASX | -            Opcode::SSAX | -            Opcode::QSAX | -            Opcode::SHSAX | -            Opcode::SSUB16 | -            Opcode::QSUB16 | -            Opcode::SHSUB16 | -            Opcode::SADD8 | -            Opcode::QADD8 | -            Opcode::SHADD8 | -            Opcode::SSUB8 | -            Opcode::QSUB8 | -            Opcode::SHSUB8 | -            Opcode::UADD16 | -            Opcode::UQADD16 | -            Opcode::UHADD16 | -            Opcode::UASX | -            Opcode::UQASX | -            Opcode::UHASX | -            Opcode::USAX | -            Opcode::UQSAX | -            Opcode::UHSAX | -            Opcode::USUB16 | -            Opcode::UQSUB16 | -            Opcode::UHSUB16 | -            Opcode::UADD8 | -            Opcode::UQADD8 | -            Opcode::UHADD8 | -            Opcode::USUB8 | -            Opcode::UQSUB8 | -            Opcode::UHSUB8 | - -            Opcode::CLZ | - -            Opcode::MUL | -            Opcode::MLA | -            Opcode::UMAAL | -            Opcode::MLS | -            Opcode::UMULL | -            Opcode::UMLAL | -            Opcode::SMLSD | -            Opcode::SMMLA | -            Opcode::SMMLS | -            Opcode::USADA8 | -            Opcode::USAD8 | -            Opcode::SDIV | -            Opcode::UDIV | -            Opcode::SMLALD(_) | -            Opcode::SMLSLD(_) | -            Opcode::SMLAD | -            Opcode::SMUSD | -            Opcode::SMMUL | -            Opcode::SMULW(_) | -            Opcode::SMUAD | -            Opcode::SMULL | -            Opcode::SMUL(_, _) | -            Opcode::SMAL(_, _) | -            Opcode::SMLA(_, _) | -            Opcode::SMLAW(_) | -            Opcode::SMLAL | -            Opcode::SMLAL_halfword(_, _) => { write!(out, "{}", colors.arithmetic_op(self)) }, - -            Opcode::PUSH | -            Opcode::POP => { write!(out, "{}", colors.stack_op(self)) }, - -            Opcode::TST | -            Opcode::TEQ | -            Opcode::CMP | -            Opcode::CMN => { write!(out, "{}", colors.comparison_op(self)) }, - -            Opcode::LDRSH | -            Opcode::LDRSHT | -            Opcode::LDRSB | -            Opcode::LDRSBT | -            Opcode::STRD | -            Opcode::LDRD | -            Opcode::LDREXH | -            Opcode::STREXH | -            Opcode::LDREXB | -            Opcode::STREXB | -            Opcode::LDREXD | -            Opcode::STREXD | -            Opcode::LDREX | -            Opcode::STREX | -            Opcode::LDM(false, false, _, _) | -            Opcode::LDM(false, true, _, _) | -            Opcode::LDM(true, false, _, _) | -            Opcode::LDM(true, true, _, _) | -            Opcode::STM(false, false, _, _) | -            Opcode::STM(false, true, _, _) | -            Opcode::STM(true, false, _, _) | -            Opcode::STM(true, true, _, _) | -            Opcode::LDR | -            Opcode::STR | -            Opcode::LDRH | -            Opcode::STRH | -            Opcode::LDRB | -            Opcode::STRB | -            Opcode::LDRT | -            Opcode::STRT | -            Opcode::LDRHT | -            Opcode::STRHT | -            Opcode::LDRBT | -            Opcode::STRBT | -            Opcode::SWP | -            Opcode::SWPB | -            Opcode::MSR | -            Opcode::MRS | -            Opcode::CLREX | -            Opcode::SXTAB | -            Opcode::SXTAB16 | -            Opcode::SXTAH | -            Opcode::SXTB | -            Opcode::SXTB16 | -            Opcode::SXTH | -            Opcode::UXTAB | -            Opcode::UXTAB16 | -            Opcode::UXTAH | -            Opcode::UXTB | -            Opcode::UXTB16 | -            Opcode::UXTH | -            Opcode::PKHTB | -            Opcode::PKHBT | -            Opcode::REV | -            Opcode::REV16 | -            Opcode::REVSH | -            Opcode::SSAT | -            Opcode::SSAT16 | -            Opcode::SBFX | -            Opcode::USAT | -            Opcode::USAT16 | -            Opcode::UBFX | -            Opcode::BFI | -            Opcode::BFC | -            Opcode::RBIT | -            Opcode::SEL | -            Opcode::MOV | -            Opcode::MOVT | -            Opcode::MVN => { write!(out, "{}", colors.data_op(self)) }, - -            Opcode::HINT | -            Opcode::NOP | -            Opcode::PLD | -            Opcode::PLI | -            Opcode::ISB | -            Opcode::DMB | -            Opcode::DSB | -            Opcode::CSDB | -            Opcode::SRS(_, _) | -            Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) }, - -            Opcode::DBG | -            Opcode::CPS(_) | -            Opcode::CPS_modeonly | -            Opcode::SETEND | -            Opcode::ENTERX | -            Opcode::LEAVEX | -            Opcode::YIELD | -            Opcode::WFE | -            Opcode::WFI | -            Opcode::SEV | -            Opcode::ERET | -            Opcode::RFE(_, _) | -            Opcode::HVC | -            Opcode::SVC | -            Opcode::SMC | -            Opcode::LDC(_) | -            Opcode::LDCL(_) | -            Opcode::LDC2(_) | -            Opcode::LDC2L(_) | -            Opcode::STC(_) | -            Opcode::STCL(_) | -            Opcode::STC2(_) | -            Opcode::STC2L(_) | -            Opcode::MCRR2(_, _) | -            Opcode::MCR2(_, _, _) | -            Opcode::MRRC2(_, _) | -            Opcode::MRC2(_, _, _) | -            Opcode::MCRR(_, _) | -            Opcode::MRRC(_, _) | -            Opcode::CDP2(_, _, _) => { write!(out, "{}", colors.platform_op(self)) }, -        } -    } -} - -impl Display for Opcode { -    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { -        match self { -            Opcode::LDRSH => { write!(f, "ldrsh") }, -            Opcode::LDRSHT => { write!(f, "ldrsht") }, -            Opcode::LDRSB => { write!(f, "ldrsb") }, -            Opcode::LDRSBT => { write!(f, "ldrsbt") }, -            Opcode::STRD => { write!(f, "strd") }, -            Opcode::LDRD => { write!(f, "ldrd") }, -            Opcode::LDC(_) => { write!(f, "ldc") }, -            Opcode::LDCL(_) => { write!(f, "ldcl") }, -            Opcode::LDC2(_) => { write!(f, "ldc2") }, -            Opcode::LDC2L(_) => { write!(f, "ldc2l") }, -            Opcode::STC(_) => { write!(f, "stc") }, -            Opcode::STCL(_) => { write!(f, "stcl") }, -            Opcode::STC2(_) => { write!(f, "stc2") }, -            Opcode::STC2L(_) => { write!(f, "stc2l") }, -            Opcode::MCRR2(_, _) => { write!(f, "mcrr2") }, -            Opcode::MCR2(_, _, _) => { write!(f, "mcr2") }, -            Opcode::MRRC2(_, _) => { write!(f, "mrrc2") }, -            Opcode::MRC2(_, _, _) => { write!(f, "mrc2") }, -            Opcode::MCRR(_, _) => { write!(f, "mcrr") }, -            Opcode::MRRC(_, _) => { write!(f, "mrrc") }, -            Opcode::CDP2(_, _, _) => { write!(f, "cdp2") }, -            Opcode::SRS(p, u) => { write!(f, "srs{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) }, -            Opcode::RFE(p, u) => { write!(f, "rfe{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) }, -            Opcode::ERET => { write!(f, "eret") }, -            Opcode::HVC => { write!(f, "hvc") }, -            Opcode::BKPT => { write!(f, "bkpt") }, -            Opcode::SMC => { write!(f, "smc") }, -            Opcode::MOVT => { write!(f, "movt") }, -            Opcode::QADD => { write!(f, "qadd") }, -            Opcode::QSUB => { write!(f, "qsub") }, -            Opcode::QDADD => { write!(f, "qdadd") }, -            Opcode::QDSUB => { write!(f, "qdsub") }, -            Opcode::Invalid => { write!(f, "invalid") }, -            Opcode::POP => { write!(f, "pop") }, -            Opcode::PUSH => { write!(f, "push") }, -            Opcode::B => { write!(f, "b") }, -            Opcode::BL => { write!(f, "bl") }, -            Opcode::BLX => { write!(f, "blx") }, -            Opcode::BX => { write!(f, "bx") }, -            Opcode::BXJ => { write!(f, "bxj") }, -            Opcode::CLZ => { write!(f, "clz") }, -            Opcode::AND => { write!(f, "and") }, -            Opcode::EOR => { write!(f, "eor") }, -            Opcode::SUB => { write!(f, "sub") }, -            Opcode::RSB => { write!(f, "rsb") }, -            Opcode::ADD => { write!(f, "add") }, -            Opcode::ADC => { write!(f, "adc") }, -            Opcode::SBC => { write!(f, "sbc") }, -            Opcode::RSC => { write!(f, "rsc") }, -            Opcode::TST => { write!(f, "tst") }, -            Opcode::TEQ => { write!(f, "teq") }, -            Opcode::CMP => { write!(f, "cmp") }, -            Opcode::CMN => { write!(f, "cmn") }, -            Opcode::ORR => { write!(f, "orr") }, -            Opcode::MOV => { write!(f, "mov") }, -            Opcode::MSR => { write!(f, "msr") }, -            Opcode::MRS => { write!(f, "mrs") }, -            Opcode::BIC => { write!(f, "bic") }, -            Opcode::MVN => { write!(f, "mvn") }, -            Opcode::LSL => { write!(f, "lsl") }, -            Opcode::LSR => { write!(f, "lsr") }, -            Opcode::ASR => { write!(f, "asr") }, -            Opcode::RRX => { write!(f, "rrx") }, -            Opcode::ROR => { write!(f, "ror") }, -            Opcode::ADR => { write!(f, "adr") }, -            Opcode::LDREXH => { write!(f, "ldrexh") }, -            Opcode::STREXH => { write!(f, "strexh") }, -            Opcode::LDREXB => { write!(f, "ldrexb") }, -            Opcode::STREXB => { write!(f, "strexb") }, -            Opcode::LDREXD => { write!(f, "ldrexd") }, -            Opcode::STREXD => { write!(f, "strexd") }, -            Opcode::LDREX => { write!(f, "ldrex") }, -            Opcode::STREX => { write!(f, "strex") }, -            Opcode::LDM(false, false, _, _) => { write!(f, "ldmda") }, -            Opcode::LDM(false, true, _, _) => { write!(f, "ldmdb") }, -            // TODO: seems like these are backwards -            Opcode::LDM(true, false, _, _) => { write!(f, "ldm") }, -            Opcode::LDM(true, true, _, _) => { write!(f, "ldmia") }, -            Opcode::STM(false, false, _, _) => { write!(f, "stmda") }, -            Opcode::STM(false, true, _, _) => { write!(f, "stmdb") }, -            // TODO: seems like these are backwards -            Opcode::STM(true, false, _, _) => { write!(f, "stm") }, -            Opcode::STM(true, true, _, _) => { write!(f, "stmia") }, -            Opcode::LDR => { write!(f, "ldr") }, -            Opcode::STR => { write!(f, "str") }, -            Opcode::LDRH => { write!(f, "ldrh") }, -            Opcode::STRH => { write!(f, "strh") }, -            Opcode::LDRB => { write!(f, "ldrb") }, -            Opcode::STRB => { write!(f, "strb") }, -            Opcode::LDRT => { write!(f, "ldrt") }, -            Opcode::STRT => { write!(f, "strt") }, -            Opcode::LDRHT => { write!(f, "ldrht") }, -            Opcode::STRHT => { write!(f, "strht") }, -            Opcode::LDRBT => { write!(f, "ldrbt") }, -            Opcode::STRBT => { write!(f, "strbt") }, -            Opcode::SWP => { write!(f, "swp") }, -            Opcode::SWPB => { write!(f, "swpb") }, -            Opcode::SDIV => { write!(f, "sdiv") }, -            Opcode::UDIV => { write!(f, "udiv") }, -            Opcode::MUL => { write!(f, "mul") }, -            Opcode::MLA => { write!(f, "mla") }, -            Opcode::UMAAL => { write!(f, "umaal") }, -            Opcode::MLS => { write!(f, "mls") }, -            Opcode::UMULL => { write!(f, "umull") }, -            Opcode::UMLAL => { write!(f, "umlal") }, -            Opcode::SMULL => { write!(f, "smull") }, -            Opcode::SMLA(first, second) => { -                write!(f, "smla{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) -            } -            Opcode::SMLAL => { write!(f, "smlal") }, -            Opcode::SMLAL_halfword(first, second) => { -                write!(f, "smlal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) -            } -            Opcode::SMUL(first, second) => { -                write!(f, "smul{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) -            }, -            Opcode::SMAL(first, second) => { -                write!(f, "smal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) -            }, -            Opcode::SMLAW(second) => { -                write!(f, "smlaw{}", if *second { "t" } else { "b" }) -            }, -            Opcode::SMULW(second) => { -                write!(f, "smulw{}", if *second { "t" } else { "b" }) -            }, -            Opcode::SMLALD(second) => { -                write!(f, "smlald{}", if *second { "t" } else { "b" }) -            }, -            Opcode::SMLSLD(second) => { -                write!(f, "smlsld{}", if *second { "t" } else { "b" }) -            }, -            Opcode::SMLSD => { write!(f, "smlsd") }, -            Opcode::SMMLA => { write!(f, "smmla") }, -            Opcode::SMMLS => { write!(f, "smmls") }, -            Opcode::USADA8 => { write!(f, "usada8") }, -            Opcode::USAD8 => { write!(f, "usad8") }, -            Opcode::SMLAD => { write!(f, "smlad") }, -            Opcode::SMUSD => { write!(f, "smusd") }, -            Opcode::SMMUL => { write!(f, "smmul") }, -            Opcode::SMUAD => { write!(f, "smuad") }, -            Opcode::TBB => { write!(f, "tbb") }, -            Opcode::TBH => { write!(f, "tbh") }, -            Opcode::UDF => { write!(f, "udf") }, -            Opcode::SVC => { write!(f, "svc") }, -            Opcode::WFE => { write!(f, "wfe") }, -            Opcode::WFI => { write!(f, "wfi") }, -            Opcode::SEV => { write!(f, "sev") }, -            Opcode::CSDB => { write!(f, "csdb") }, -            Opcode::YIELD => { write!(f, "yield") }, -            Opcode::HINT => { write!(f, "hint") }, -            Opcode::NOP => { write!(f, "nop") }, -            Opcode::LEAVEX => { write!(f, "leavex") }, -            Opcode::ENTERX => { write!(f, "enterx") }, -            Opcode::CLREX => { write!(f, "clrex") }, -            Opcode::DSB => { write!(f, "dsb") }, -            Opcode::DMB => { write!(f, "dmb") }, -            Opcode::ISB => { write!(f, "isb") }, -            Opcode::SXTH => { write!(f, "sxth") }, -            Opcode::UXTH => { write!(f, "uxth") }, -            Opcode::SXTB16 => { write!(f, "sxtb16") }, -            Opcode::UXTB16 => { write!(f, "uxtb16") }, -            Opcode::SXTB => { write!(f, "sxtb") }, -            Opcode::UXTB => { write!(f, "uxtb") }, -            Opcode::SXTAH => { write!(f, "sxtah") }, -            Opcode::UXTAH => { write!(f, "uxtah") }, -            Opcode::SXTAB16 => { write!(f, "sxtab16") }, -            Opcode::UXTAB16 => { write!(f, "uxtab16") }, -            Opcode::SXTAB => { write!(f, "sxtab") }, -            Opcode::UXTAB => { write!(f, "uxtab") }, -            Opcode::CBZ => { write!(f, "cbz") }, -            Opcode::CBNZ => { write!(f, "cbnz") }, -            Opcode::SETEND => { write!(f, "setend") }, -            Opcode::CPS(disable) => { write!(f, "cps{}", if *disable { "id" } else { "ie" }) }, -            Opcode::CPS_modeonly => { write!(f, "cps") }, -            Opcode::REV => { write!(f, "rev") }, -            Opcode::REV16 => { write!(f, "rev16") }, -            Opcode::REVSH => { write!(f, "revsh") }, -            Opcode::IT => { write!(f, "it") }, -            Opcode::PKHTB => { write!(f, "pkhtb") }, -            Opcode::PKHBT => { write!(f, "pkhbt") }, -            Opcode::ORN => { write!(f, "orn") }, -            Opcode::SSAT => { write!(f, "ssat") }, -            Opcode::SSAT16 => { write!(f, "ssat16") }, -            Opcode::SBFX => { write!(f, "sbfx") }, -            Opcode::USAT => { write!(f, "usat") }, -            Opcode::USAT16 => { write!(f, "usat16") }, -            Opcode::UBFX => { write!(f, "ubfx") }, -            Opcode::BFI => { write!(f, "bfi") }, -            Opcode::BFC => { write!(f, "bfc") }, -            Opcode::DBG => { write!(f, "dbg") }, -            Opcode::PLD => { write!(f, "pld") }, -            Opcode::PLI => { write!(f, "pli") }, -            Opcode::RBIT => { write!(f, "rbit") }, -            Opcode::SEL => { write!(f, "sel") }, -            Opcode::SADD16 => { write!(f, "sadd16") }, -            Opcode::QADD16 => { write!(f, "qadd16") }, -            Opcode::SHADD16 => { write!(f, "shadd16") }, -            Opcode::SASX => { write!(f, "sasx") }, -            Opcode::QASX => { write!(f, "qasx") }, -            Opcode::SHASX => { write!(f, "shasx") }, -            Opcode::SSAX => { write!(f, "ssax") }, -            Opcode::QSAX => { write!(f, "qsax") }, -            Opcode::SHSAX => { write!(f, "shsax") }, -            Opcode::SSUB16 => { write!(f, "ssub16") }, -            Opcode::QSUB16 => { write!(f, "qsub16") }, -            Opcode::SHSUB16 => { write!(f, "shsub16") }, -            Opcode::SADD8 => { write!(f, "sadd8") }, -            Opcode::QADD8 => { write!(f, "qadd8") }, -            Opcode::SHADD8 => { write!(f, "shadd8") }, -            Opcode::SSUB8 => { write!(f, "ssub8") }, -            Opcode::QSUB8 => { write!(f, "qsub8") }, -            Opcode::SHSUB8 => { write!(f, "shsub8") }, -            Opcode::UADD16 => { write!(f, "uadd16") }, -            Opcode::UQADD16 => { write!(f, "uqadd16") }, -            Opcode::UHADD16 => { write!(f, "uhadd16") }, -            Opcode::UASX => { write!(f, "uasx") }, -            Opcode::UQASX => { write!(f, "uqasx") }, -            Opcode::UHASX => { write!(f, "uhasx") }, -            Opcode::USAX => { write!(f, "usax") }, -            Opcode::UQSAX => { write!(f, "uqsax") }, -            Opcode::UHSAX => { write!(f, "uhsax") }, -            Opcode::USUB16 => { write!(f, "usub16") }, -            Opcode::UQSUB16 => { write!(f, "uqsub16") }, -            Opcode::UHSUB16 => { write!(f, "uhsub16") }, -            Opcode::UADD8 => { write!(f, "uadd8") }, -            Opcode::UQADD8 => { write!(f, "uqadd8") }, -            Opcode::UHADD8 => { write!(f, "uhadd8") }, -            Opcode::USUB8 => { write!(f, "usub8") }, -            Opcode::UQSUB8 => { write!(f, "uqsub8") }, -            Opcode::UHSUB8 => { write!(f, "uhsub8") }, -        } -    } -} -  #[derive(Debug, Copy, Clone, PartialEq, Eq)]  #[allow(non_camel_case_types)]  #[allow(missing_docs)] @@ -1156,12 +332,12 @@ pub enum ShiftStyle {  impl Display for ShiftStyle {      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -        match self { -            ShiftStyle::LSL => write!(f, "lsl"), -            ShiftStyle::LSR => write!(f, "lsr"), -            ShiftStyle::ASR => write!(f, "asr"), -            ShiftStyle::ROR => write!(f, "ror"), -        } +        use core::fmt::Write; +        let name = self.name(); +        f.write_char(name[0] as char)?; +        f.write_char(name[1] as char)?; +        f.write_char(name[2] as char)?; +        Ok(())      }  } @@ -1175,6 +351,15 @@ impl ShiftStyle {              _ => unreachable!("bad ShiftStyle index")          }      } + +    fn name(&self) -> &'static [u8; 3] { +        match self { +            ShiftStyle::LSL => &[b'l', b's', b'l'], +            ShiftStyle::LSR => &[b'l', b's', b'r'], +            ShiftStyle::ASR => &[b'a', b's', b'r'], +            ShiftStyle::ROR => &[b'r', b'o', b'r'], +        } +    }  }  impl RegRegShift { @@ -1338,44 +523,6 @@ impl CReg {      }  } -impl Display for StatusRegMask { -    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -        match self { -            StatusRegMask::CPSR_C => write!(f, "cpsr_c"), -            StatusRegMask::CPSR_X => write!(f, "cpsr_x"), -            StatusRegMask::CPSR_XC => write!(f, "cpsr_xc"), -            StatusRegMask::APSR_G => write!(f, "apsr_g"), -            StatusRegMask::CPSR_SC => write!(f, "cpsr_sc"), -            StatusRegMask::CPSR_SX => write!(f, "cpsr_sx"), -            StatusRegMask::CPSR_SXC => write!(f, "cpsr_sxc"), -            StatusRegMask::APSR_NZCVQ => write!(f, "apsr_nzcvq"), -            StatusRegMask::CPSR_FC => write!(f, "cpsr_fc"), -            StatusRegMask::CPSR_FX => write!(f, "cpsr_fx"), -            StatusRegMask::CPSR_FXC => write!(f, "cpsr_fxc"), -            StatusRegMask::APSR_NZCVQG => write!(f, "apsr_nzcvqg"), -            StatusRegMask::CPSR_FSC => write!(f, "cpsr_fsc"), -            StatusRegMask::CPSR_FSX => write!(f, "cpsr_fsx"), -            StatusRegMask::CPSR_FSXC => write!(f, "cpsr_fsxc"), -            StatusRegMask::SPSR => write!(f, "spsr"), -            StatusRegMask::SPSR_C => write!(f, "spsr_c"), -            StatusRegMask::SPSR_X => write!(f, "spsr_x"), -            StatusRegMask::SPSR_XC => write!(f, "spsr_xc"), -            StatusRegMask::SPSR_S => write!(f, "spsr_s"), -            StatusRegMask::SPSR_SC => write!(f, "spsr_sc"), -            StatusRegMask::SPSR_SX => write!(f, "spsr_sx"), -            StatusRegMask::SPSR_SXC => write!(f, "spsr_sxc"), -            StatusRegMask::SPSR_F => write!(f, "spsr_f"), -            StatusRegMask::SPSR_FC => write!(f, "spsr_fc"), -            StatusRegMask::SPSR_FX => write!(f, "spsr_fx"), -            StatusRegMask::SPSR_FXC => write!(f, "spsr_fxc"), -            StatusRegMask::SPSR_FS => write!(f, "spsr_fs"), -            StatusRegMask::SPSR_FSC => write!(f, "spsr_fsc"), -            StatusRegMask::SPSR_FSX => write!(f, "spsr_fsx"), -            StatusRegMask::SPSR_FSXC => write!(f, "spsr_fsxc"), -        } -    } -} -  #[derive(Clone, Copy, Debug, PartialEq, Eq)]  #[allow(non_camel_case_types)]  #[allow(missing_docs)] @@ -1507,6 +654,11 @@ pub enum Operand {      /// a pc-relative branch, with 32-bit signed offset, left-shifted by 1.      BranchThumbOffset(i32),      /// a coprocessor index. +    #[deprecated( +        since = "0.3.2", +        note = "`Coprocessor` was prematurely added: `CoprocOption` the operand used to indicate \ +                coprocessor selection and has always been the variant used as such" +    )]      Coprocessor(u8),      /// a coprocessor option number.      CoprocOption(u8), @@ -1530,6 +682,77 @@ pub enum Operand {      Nothing,  } +/// a trait describing functions in support of processing operands +/// +/// this is interesting more for future implementation of `yaxpeax-arm`: operands currently are +/// backed by an `Operand` enum, which means that operating on an operand involves potentially more +/// copies and data management than strictly necessary. in the future, operands may be described +/// more granularly, where `OperandVisitor` is the stable interface to the pre-enum constituent +/// parts of operands. +/// +/// see `yaxpeax-x86`'s equivalent trait for examples of that direction. +pub trait OperandVisitor { +    /// the result for successful processing of an operand. for formatting, as an example, this is +    /// likely `()`. +    type Ok; +    /// the result for an error in processing an operand. +    type Error; + +    /// process an operand that is a simple general purpose register. +    fn visit_reg(&mut self, reg: Reg) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a simple general purpose register with writeback. +    fn visit_reg_wback(&mut self, reg: Reg, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a list of registers. +    fn visit_reglist(&mut self, list: u16) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a memory access through a general purpose register. +    fn visit_reg_deref(&mut self, reg: Reg) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a shifted general pupose register. +    fn visit_reg_shift(&mut self, reg_shift: RegShift) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register, afterward incremented by index. +    fn visit_reg_deref_postindex_reg_shift(&mut self, base: Reg, index: RegShift, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register incremented by index. +    fn visit_reg_deref_preindex_reg_shift(&mut self, base: Reg, index: RegShift, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register, afterward incremented by offset. +    fn visit_reg_deref_postindex_offset(&mut self, base: Reg, offset: u16, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register incremented by offset. +    fn visit_reg_deref_preindex_offset(&mut self, base: Reg, offset: u16, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register, afterward incremented by offset. +    fn visit_reg_deref_postindex_reg(&mut self, base: Reg, offset: Reg, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is the dereference of a register incremented by offset. +    fn visit_reg_deref_preindex_reg(&mut self, base: Reg, offset: Reg, add: bool, wback: bool) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a 12-bit immediate. +    fn visit_imm12(&mut self, imm: u16) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a 32-bit immediate. +    fn visit_imm32(&mut self, imm: u32) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a branch with i32 offset. +    fn visit_branch_offset(&mut self, offset: i32) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a branch with i32 offset that also exchanges instruction sets. +    /// +    /// this is typically rendered in the same way as `visit_branch_offset` but is a distinct +    /// helper due to support uses emphasizing the ISA-changing behavior. +    fn visit_blx_offset(&mut self, offset: i32) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a coprocessor option access. not exactly clear what this means, +    /// this may need to get foled into an opcode or redefinition of the operand. +    fn visit_coprocessor_option(&mut self, nr: u8) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a control register. +    fn visit_creg(&mut self, creg: CReg) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a banked register. +    fn visit_banked_reg(&mut self, bank: Bank, reg: u16) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is a banked version of `SPSR`. +    fn visit_banked_spsr(&mut self, bank: Bank) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is some set of bits out of a status register. +    fn visit_status_reg_mask(&mut self, mask: StatusRegMask) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is `APSR`. +    fn visit_apsr(&mut self) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is `SPSR`. +    fn visit_spsr(&mut self) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is `CPSR`. +    fn visit_cpsr(&mut self) -> Result<Self::Ok, Self::Error>; +    /// process an operand that is not defined above. there are no parameters as for an unknown +    /// operand kind there is no appropriate tuple of values to provide. +    fn visit_other(&mut self) -> Result<Self::Ok, Self::Error>; +} +  /// a register bank for a register in `armv7` or below.  #[derive(Debug, Copy, Clone, PartialEq, Eq)]  #[allow(missing_docs)] @@ -1544,108 +767,6 @@ pub enum Bank {      Hyp,  } -impl Display for Bank { -    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -        match self { -            Bank::Usr => write!(f, "usr"), -            Bank::Fiq => write!(f, "fiq"), -            Bank::Irq => write!(f, "irq"), -            Bank::Svc => write!(f, "svc"), -            Bank::Abt => write!(f, "abt"), -            Bank::Und => write!(f, "und"), -            Bank::Mon => write!(f, "mon"), -            Bank::Hyp => write!(f, "hyp"), -        } -    } -} - -#[allow(deprecated)] -impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Operand { -    fn colorize(&self, colors: &Y, f: &mut T) -> fmt::Result { -        match self { -            Operand::RegList(list) => { -                format_reg_list(f, *list, colors) -            } -            Operand::BankedReg(bank, reg) => { -                write!(f, "{}_{}", reg_name_colorize(*reg, colors), bank) -            }, -            Operand::BankedSPSR(bank) => { -                write!(f, "spsr_{}", bank) -            }, -            Operand::Reg(reg) => { -                write!(f, "{}", reg_name_colorize(*reg, colors)) -            } -            Operand::RegDeref(reg) => { -                write!(f, "[{}]", reg_name_colorize(*reg, colors)) -            } -            Operand::RegShift(shift) => { -                format_shift(f, *shift, colors) -            } -            Operand::RegDerefPostindexRegShift(reg, shift, add, wback) => { -                format_reg_shift_mem(f, *reg, *shift, *add, false, *wback, colors) -            } -            Operand::RegDerefPreindexRegShift(reg, shift, add, wback) => { -                format_reg_shift_mem(f, *reg, *shift, *add, true, *wback, colors) -            } -            Operand::RegDerefPostindexOffset(reg, offs, add, wback) => { -                format_reg_imm_mem(f, *reg, *offs, *add, false, *wback, colors) -            } -            Operand::RegDerefPreindexOffset(reg, offs, add, wback) => { -                format_reg_imm_mem(f, *reg, *offs, *add, true, *wback, colors) -            } -            Operand::RegDerefPostindexReg(reg, offsreg, add, wback) => { -                write!(f, "[{}], {}{}{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" }) -            } -            Operand::RegDerefPreindexReg(reg, offsreg, add, wback) => { -                write!(f, "[{}, {}{}]{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" }) -            } -            Operand::Imm12(imm) => { -                write!(f, "{:#x}", imm) -            } -            Operand::Imm32(imm) => { -                write!(f, "{:#x}", imm) -            } -            Operand::BranchOffset(imm) => { -                if *imm < 0 { -                    write!(f, "$-{:#x}", (-*imm) * 4) -                } else { -                    write!(f, "$+{:#x}", *imm * 4) -                } -            } -            Operand::BranchThumbOffset(imm) => { -                if *imm < 0 { -                    write!(f, "$-{:#x}", (-*imm) * 2) -                } else { -                    write!(f, "$+{:#x}", *imm * 2) -                } -            } -            Operand::Coprocessor(num) => { -                write!(f, "p{}", num) -            } -            Operand::CoprocOption(num) => { -                write!(f, "{{{:#x}}}", num) -            } -            Operand::RegWBack(reg, wback) => { -                if *wback { -                    write!(f, "{}!", reg_name_colorize(*reg, colors)) -                } else { -                    write!(f, "{}", reg_name_colorize(*reg, colors)) -                } -            } -            Operand::CReg(creg) => { -                write!(f, "{}", creg) -            } -            Operand::StatusRegMask(mask) => { -                write!(f, "{}", mask) -            } -            Operand::APSR => write!(f, "apsr"), -            Operand::SPSR => write!(f, "spsr"), -            Operand::CPSR => write!(f, "cpsr"), -            Operand::Nothing => { panic!("tried to print Nothing operand") }, -        } -    } -} -  /// a `armv7` or below instruction.  #[derive(Debug, PartialEq, Eq)]  pub struct Instruction { @@ -1776,102 +897,6 @@ impl Instruction {      pub fn thumb(&self) -> bool { self.thumb }  } -#[allow(deprecated)] -fn format_reg_list<T: fmt::Write, Y: YaxColors>(f: &mut T, mut list: u16, colors: &Y) -> Result<(), fmt::Error> { -    write!(f, "{{")?; -    let mut i = 0; -    let mut tail = false; -    while i < 16 { -        let present = (list & 1) == 1; -        if present { -            if tail { -                write!(f, ", ")?; -            } else { -                tail = true; -            } -            write!(f, "{}", reg_name_colorize(Reg::from_u8(i), colors))?; -        } -        i += 1; -        list >>= 1; -    } -    write!(f, "}}") -} - -#[allow(non_snake_case)] -#[allow(deprecated)] -fn format_shift<T: fmt::Write, Y: YaxColors>(f: &mut T, shift: RegShift, colors: &Y) -> Result<(), fmt::Error> { -    match shift.into_shift() { -        RegShiftStyle::RegImm(imm_shift) => { -            if imm_shift.imm() == 0 && imm_shift.stype() == ShiftStyle::LSL { -                write!(f, "{}", reg_name_colorize(imm_shift.shiftee(), colors)) -            } else { -                write!(f, "{}, {} {}", reg_name_colorize(imm_shift.shiftee(), colors), imm_shift.stype(), imm_shift.imm()) -            } -        } -        RegShiftStyle::RegReg(reg_shift) => { -            write!(f, "{}, {} {}", reg_name_colorize(reg_shift.shiftee(), colors), reg_shift.stype(), reg_name_colorize(reg_shift.shifter(), colors)) -        }, -    } -} - -#[allow(non_snake_case)] -#[allow(deprecated)] -fn format_reg_shift_mem<T: fmt::Write, Y: YaxColors>(f: &mut T, Rd: Reg, shift: RegShift, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { -    let op = if add { "" } else { "-" }; - -    match (pre, wback) { -        (true, true) => { -            write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; -            format_shift(f, shift, colors)?; -            write!(f, "]!") -        }, -        (true, false) => { -            write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; -            format_shift(f, shift, colors)?; -            write!(f, "]") -        }, -        (false, true) => { -            unreachable!("preindex with writeback is not an ARM addressing mode. this is a decoder bug."); -        }, -        (false, false) => { -            write!(f, "[{}], {}", reg_name_colorize(Rd, colors), op)?; -            format_shift(f, shift, colors) -        } -    } -} - -#[allow(non_snake_case)] -#[allow(deprecated)] -fn format_reg_imm_mem<T: fmt::Write, Y: YaxColors>(f: &mut T, Rn: Reg, imm: u16, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { -    if imm != 0 { -        let op = if add { "" } else { "-" }; - -        match (pre, wback) { -            (true, true) => { -                write!(f, "[{}, {}{:#x}]!", reg_name_colorize(Rn, colors), op, imm) -            }, -            (true, false) => { -                write!(f, "[{}, {}{:#x}]", reg_name_colorize(Rn, colors), op, imm) -            }, -            (false, _) => { -                write!(f, "[{}], {}{:#x}", reg_name_colorize(Rn, colors), op, imm) -            } -        } -    } else { -        match (pre, wback) { -            (true, true) => { -                write!(f, "[{}]!", reg_name_colorize(Rn, colors)) -            }, -            (true, false) => { -                write!(f, "[{}]", reg_name_colorize(Rn, colors)) -            }, -            (false, _) => { -                write!(f, "[{}]", reg_name_colorize(Rn, colors)) -            } -        } -    } -} -  impl Display for Instruction {      fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {          #[allow(deprecated)] @@ -1915,25 +940,37 @@ pub enum ConditionCode {      AL  } +impl ConditionCode { +    fn name(&self) -> &'static [u8; 2] { +        match self { +            ConditionCode::EQ => &[b'e', b'q'], +            ConditionCode::NE => &[b'n', b'e'], +            ConditionCode::HS => &[b'h', b's'], +            ConditionCode::LO => &[b'l', b'o'], +            ConditionCode::MI => &[b'm', b'i'], +            ConditionCode::PL => &[b'p', b'l'], +            ConditionCode::VS => &[b'v', b's'], +            ConditionCode::VC => &[b'v', b'c'], +            ConditionCode::HI => &[b'h', b'i'], +            ConditionCode::LS => &[b'l', b's'], +            ConditionCode::GE => &[b'g', b'e'], +            ConditionCode::LT => &[b'l', b't'], +            ConditionCode::GT => &[b'g', b't'], +            ConditionCode::LE => &[b'l', b'e'], +            ConditionCode::AL => &[b'a', b'l'], +        } +    } +} +  impl Display for ConditionCode {      fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { -        match self { -            ConditionCode::EQ => write!(f, "eq"), -            ConditionCode::NE => write!(f, "ne"), -            ConditionCode::HS => write!(f, "hs"), -            ConditionCode::LO => write!(f, "lo"), -            ConditionCode::MI => write!(f, "mi"), -            ConditionCode::PL => write!(f, "pl"), -            ConditionCode::VS => write!(f, "vs"), -            ConditionCode::VC => write!(f, "vc"), -            ConditionCode::HI => write!(f, "hi"), -            ConditionCode::LS => write!(f, "ls"), -            ConditionCode::GE => write!(f, "ge"), -            ConditionCode::LT => write!(f, "lt"), -            ConditionCode::GT => write!(f, "gt"), -            ConditionCode::LE => write!(f, "le"), -            ConditionCode::AL => Ok(()) +        if *self != ConditionCode::AL { +            use core::fmt::Write; +            f.write_char(self.name()[0] as char)?; +            f.write_char(self.name()[1] as char)?;          } + +        Ok(())      }  } diff --git a/src/armv7/display.rs b/src/armv7/display.rs new file mode 100644 index 0000000..8eaea26 --- /dev/null +++ b/src/armv7/display.rs @@ -0,0 +1,1438 @@ +use core::fmt; +#[allow(deprecated)] +use crate::yaxpeax_arch::{Colorize, ShowContextual, YaxColors}; +use crate::armv7::OperandVisitor; +use super::{Operand, Opcode, ShiftStyle, RegShift, RegShiftStyle, Reg, Bank, ConditionCode, ConditionedOpcode, Instruction, CReg, StatusRegMask}; +use super::NoContext; +use yaxpeax_arch::display::DisplaySink; + +trait DisplaySinkExt { +    fn write_reg(&mut self, gpr_num: u8) -> Result<(), core::fmt::Error>; +} + +impl<T: DisplaySink> DisplaySinkExt for T { +    #[inline(always)] +    fn write_reg(&mut self, gpr_num: u8) -> Result<(), core::fmt::Error> { +        self.span_start_register(); +        let name = REG_NAMES.get(gpr_num as usize) +            .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); +        unsafe { +            self.write_lt_8(name)?; +        } +        self.span_end_register(); +        Ok(()) +    } +} + +/// an instruction and display settings to configure [`Display](fmt::Display)-formatting of the +/// instruction. +/// +/// ARM instructions do not currently have configurable display behavior, but when they do, this +/// will be the place to configure it. +#[cfg(feature="fmt")] +pub struct InstructionDisplayer<'instr> { +    #[allow(dead_code)] +    pub(crate) instr: &'instr Instruction, +} + +#[cfg(feature="fmt")] +impl<'instr> fmt::Display for InstructionDisplayer<'instr> { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        write!(f, "{}", self.instr) +    } +} + +impl Instruction { +    /// wrap a reference to this instruction to format the instruction later. +    #[cfg(feature="fmt")] +    pub fn display<'instr>(&'instr self) -> InstructionDisplayer<'instr> { +        InstructionDisplayer { +            instr: self +        } +    } + +    fn write_conditioned_opcode<T: DisplaySink>(&self, opc: &Opcode, f: &mut T) -> Result<(), fmt::Error> { +        unsafe { +            f.write_lt_8(opc.name())?; +        } +        if self.s() { +            f.write_char('s')?; +        } +        if self.w() { +            f.write_fixed_size(".w")?; +        } +        if self.condition != ConditionCode::AL { +            let name = self.condition.name(); +            // all condition codes are two characters long +            f.write_char(name[0] as char)?; +            f.write_char(name[1] as char)?; +        } +        Ok(()) +    } +} + +struct DisplayingOperandVisitor<'a, T> { +    f: &'a mut T, +} + +static REG_NAMES: [&'static str; 16] = [ +    "r0", "r1", "r2", "r3", +    "r4", "r5", "r6", "r7", +    "r8", "sb", "r10", "fp", +    "ip", "sp", "lr", "pc", +]; + +impl<T: DisplaySink> DisplayingOperandVisitor<'_, T> { +    fn emit_shift_type(&mut self, stype: ShiftStyle) -> Result<(), core::fmt::Error> { +        // shifter names in armv7 are all three characters. +        let name = stype.name(); +        self.f.write_char(name[0] as char)?; +        self.f.write_char(name[1] as char)?; +        self.f.write_char(name[2] as char)?; + +        Ok(()) +    } + +    fn format_reg_shift(&mut self, shift: RegShift) -> Result<(), core::fmt::Error> { +        match shift.into_shift() { +            RegShiftStyle::RegImm(imm_shift) => { +                self.f.write_reg(imm_shift.shiftee().number())?; +                if imm_shift.imm() != 0 || imm_shift.stype() != ShiftStyle::LSL { +                    self.f.write_fixed_size(", ")?; +                    self.emit_shift_type(imm_shift.stype())?; +                    self.f.write_char(' ')?; +                    let sh = imm_shift.imm(); +                    if sh >= 30 { +                        self.f.write_char('3')?; +                        self.f.write_char((sh - 30 + 0x30) as char)?; +                    } else if sh >= 20 { +                        self.f.write_char('2')?; +                        self.f.write_char((sh - 20 + 0x30) as char)?; +                    } else if sh >= 10 { +                        self.f.write_char('1')?; +                        self.f.write_char((sh - 10 + 0x30) as char)?; +                    } else { +                        self.f.write_char((sh + 0x30) as char)?; +                    } +                } +            } +            RegShiftStyle::RegReg(reg_shift) => { +                self.f.write_reg(reg_shift.shiftee().number())?; +                self.f.write_fixed_size(", ")?; +                self.emit_shift_type(reg_shift.stype())?; +                self.f.write_char(' ')?; +                self.f.write_reg(reg_shift.shifter().number())?; +            }, +        } + +        Ok(()) +    } +} + +impl<T: DisplaySink> crate::armv7::OperandVisitor for DisplayingOperandVisitor<'_, T> { +    type Ok = (); +    type Error = core::fmt::Error; + +    fn visit_reglist(&mut self, mut list: u16) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('{')?; +        let mut i = 0; +        let mut tail = false; +        while i < 16 { +            if (list & 1) == 1 { +                if tail { +                    self.f.write_fixed_size(", ")?; +                } else { +                    tail = true; +                } +                self.f.write_reg(i)?; +            } +            i += 1; +            list = list >> 1; +        } +        self.f.write_char('}')?; +        Ok(()) +    } + +    fn visit_banked_reg(&mut self, bank: Bank, reg: u16) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        unsafe { +            self.f.write_lt_8(REG_NAMES[reg as usize])?; +        } +        self.f.write_char('_')?; +        self.f.write_fixed_size(bank.name())?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_banked_spsr(&mut self, bank: Bank) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_fixed_size("spsr_")?; +        self.f.write_fixed_size(bank.name())?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_reg(&mut self, reg: Reg) -> Result<Self::Ok, Self::Error> { +        self.f.write_reg(reg.number())?; +        Ok(()) +    } + +    fn visit_reg_deref(&mut self, reg: Reg) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(reg.number())?; +        self.f.write_char(']')?; +        Ok(()) +    } + +    fn visit_reg_shift(&mut self, shift: RegShift) -> Result<Self::Ok, Self::Error> { +        self.format_reg_shift(shift)?; +        Ok(()) +    } + +    fn visit_reg_wback(&mut self, reg: Reg, wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_reg(reg.number())?; +        if wback { +            self.f.write_char('!')?; +        } +        Ok(()) +    } + +    fn visit_reg_deref_postindex_reg_shift(&mut self, base: Reg, index: RegShift, add: bool, wback: bool) -> Result<Self::Ok, Self::Error> { +        // TODO: +        assert!(!wback); +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        self.f.write_fixed_size("], ")?; +        if !add { +            self.f.write_char('-')?; +        } +        self.f.write_char(' ')?; +        self.format_reg_shift(index)?; +        Ok(()) +    } + +    fn visit_reg_deref_preindex_reg_shift(&mut self, base: Reg, index: RegShift, add: bool, wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        self.f.write_fixed_size(", ")?; +        if !add { +            self.f.write_char('-')?; +        } +        self.format_reg_shift(index)?; +        self.f.write_char(']')?; +        if wback { +            self.f.write_char('!')?; +        } +        Ok(()) +    } + +    // TODO: post-index with writeback is a bug right..? +    fn visit_reg_deref_postindex_offset(&mut self, base: Reg, offset: u16, add: bool, _wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        self.f.write_char(']')?; + +        if offset != 0 { +            self.f.write_fixed_size(", ")?; +            if !add { +                self.f.write_char('-')?; +            } +            self.f.write_prefixed_u16(offset)?; +        } +        Ok(()) +    } + +    fn visit_reg_deref_preindex_offset(&mut self, base: Reg, offset: u16, add: bool, wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        if offset != 0 { +            self.f.write_fixed_size(", ")?; +            if !add { +                self.f.write_char('-')?; +            } +            self.f.write_prefixed_u16(offset)?; +        } +        self.f.write_char(']')?; +        if wback { +            self.f.write_char('!')?; +        } +        Ok(()) +    } + +    fn visit_reg_deref_postindex_reg(&mut self, base: Reg, offset: Reg, add: bool, wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        self.f.write_fixed_size("], ")?; +        if !add { +            self.f.write_char('-')?; +        } +        self.f.write_reg(offset.number())?; +        if wback { +            self.f.write_char('!')?; +        } +        Ok(()) +    } + +    fn visit_reg_deref_preindex_reg(&mut self, base: Reg, offset: Reg, add: bool, wback: bool) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('[')?; +        self.f.write_reg(base.number())?; +        self.f.write_fixed_size(", ")?; +        if !add { +            self.f.write_char('-')?; +        } +        self.f.write_reg(offset.number())?; +        self.f.write_char(']')?; + +        if wback { +            self.f.write_char('!')?; +        } +        Ok(()) +    } + +    fn visit_imm12(&mut self, imm: u16) -> Result<Self::Ok, Self::Error> { +        self.f.write_prefixed_u16(imm) +    } + +    fn visit_imm32(&mut self, imm: u32) -> Result<Self::Ok, Self::Error> { +        self.f.write_prefixed_u32(imm) +    } + +    fn visit_branch_offset(&mut self, offset: i32) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('$')?; +        if offset >= 0 { +            self.f.write_char('+')?; +        } +        self.f.write_prefixed_i32(offset << 2) +    } + +    fn visit_blx_offset(&mut self, offset: i32) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('$')?; +        if offset >= 0 { +            self.f.write_char('+')?; +        } +        self.f.write_prefixed_i32(offset << 1) +    } + +    fn visit_coprocessor_option(&mut self, nr: u8) -> Result<Self::Ok, Self::Error> { +        self.f.write_char('{')?; +        self.f.write_prefixed_u8(nr)?; +        self.f.write_char('}')?; +        Ok(()) +    } + +    fn visit_creg(&mut self, creg: CReg) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_char('c')?; +        let num = creg.number(); +        if num >= 10 { +            self.f.write_char('1')?; +            // num is at most 16 +            self.f.write_char((num - 10 + 0x30) as char)?; +        } else { +            self.f.write_char((num + 0x30) as char)?; +        } +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_status_reg_mask(&mut self, mask: StatusRegMask) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_fixed_size(mask.name())?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_apsr(&mut self) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_fixed_size("apsr")?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_spsr(&mut self) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_fixed_size("spsr")?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_cpsr(&mut self) -> Result<Self::Ok, Self::Error> { +        self.f.span_start_register(); +        self.f.write_fixed_size("cpsr")?; +        self.f.span_end_register(); +        Ok(()) +    } + +    fn visit_other(&mut self) -> Result<Self::Ok, Self::Error> { +        Ok(()) +    } +} + +#[allow(non_snake_case)] +pub(crate) fn visit_inst<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fmt::Result { +    // handle a few instruction aliasing cases first... +    match instr.opcode { +        Opcode::LDR => { +            match instr.operands { +                // TODO: should this be PostindexOffset? +                [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, true, false), Operand::Nothing, Operand::Nothing] => { +                    out.span_start_opcode(); +                    instr.write_conditioned_opcode(&Opcode::POP, out)?; +                    out.span_end_opcode(); + +                    out.write_char(' ')?; +                    out.write_char('{')?; +                    out.span_start_register(); +                    out.write_reg(Rt.number())?; +                    out.span_end_register(); +                    out.write_char('}')?; +                    return Ok(()); +                }, +                _ => {} +            } +        }, +        Opcode::STR => { +            match instr.operands { +                // TODO: should this be PreindexOffset? +                [Operand::Reg(Rt), Operand::RegDerefPreindexOffset(Reg { bits: 13 }, 4, false, true), Operand::Nothing, Operand::Nothing] => { +                    out.span_start_opcode(); +                    instr.write_conditioned_opcode(&Opcode::PUSH, out)?; +                    out.span_end_opcode(); + +                    out.write_char(' ')?; + +                    out.write_char('{')?; +                    out.span_start_register(); +                    out.write_reg(Rt.number())?; +                    out.span_end_register(); +                    out.write_char('}')?; +                    return Ok(()); +                }, +                _ => {} +            } +        }, +        Opcode::LDM(true, false, false, _usermode) => { +            // TODO: what indicates usermode in the ARM syntax? +            match instr.operands { +                [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { +                    out.span_start_opcode(); +                    instr.write_conditioned_opcode(&Opcode::POP, out)?; +                    out.span_end_opcode(); + +                    out.write_char(' ')?; + +                    let mut visitor = DisplayingOperandVisitor { +                        f: out, +                    }; +                    visitor.visit_reglist(list)?; +                    return Ok(()); +                } +                _ => {} +            } +        } +        Opcode::STM(false, true, false, _usermode) => { +            // TODO: what indicates usermode in the ARM syntax? +            match instr.operands { +                [Operand::RegWBack(Reg { bits: 13 }, true), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { +                    out.span_start_opcode(); +                    instr.write_conditioned_opcode(&Opcode::PUSH, out)?; +                    out.span_end_opcode(); + +                    out.write_char(' ')?; + +                    let mut visitor = DisplayingOperandVisitor { +                        f: out, +                    }; +                    visitor.visit_reglist(list)?; +                    return Ok(()); +                } +                _ => {} +            } +        } +        Opcode::STCL(coproc) | +        Opcode::STC(coproc) | +        Opcode::STC2L(coproc) | +        Opcode::STC2(coproc) | +        Opcode::LDC(coproc) | +        Opcode::LDCL(coproc) | +        Opcode::LDC2(coproc) | +        Opcode::LDC2L(coproc) => { +            out.span_start_opcode(); +            unsafe { +                out.write_lt_8(instr.opcode.name())?; +            } +            out.span_end_opcode(); +            out.write_fixed_size(" p")?; +            if coproc >= 10 { +                out.write_char('1')?; +                out.write_char((coproc - 10 + 0x30) as char)?; +            } else { +                out.write_char((coproc + 0x30) as char)?; +            } + +            let ops = instr.operands.iter(); +            for op in ops { +                if let Operand::Nothing = op { +                    break; +                } +                out.write_fixed_size(", ")?; +                let mut op_visitor = DisplayingOperandVisitor { +                    f: out +                }; +                op.visit(&mut op_visitor)?; +            } + +            return Ok(()); +        } +        Opcode::MRRC2(coproc, opc) | +        Opcode::MCRR2(coproc, opc) => { +            out.span_start_opcode(); +            unsafe { +                out.write_lt_8(instr.opcode.name())?; +            } +            out.span_end_opcode(); +            out.write_fixed_size(" p")?; +            // coproc is a 3-bit field +            out.write_char((coproc + 0x30) as char)?; + +            out.write_fixed_size(", ")?; +            // opc1 is a 3-bit field +            out.write_char((opc + 0x30) as char)?; + +            let ops = instr.operands.iter(); +            for op in ops { +                if let Operand::Nothing = op { +                    break; +                } +                out.write_fixed_size(", ")?; +                let mut op_visitor = DisplayingOperandVisitor { +                    f: out +                }; +                op.visit(&mut op_visitor)?; +            } + +            return Ok(()); +        } +        Opcode::MRC2(coproc, opc1, opc2) | +        Opcode::MCR2(coproc, opc1, opc2) | +        Opcode::CDP2(coproc, opc1, opc2) => { +            out.span_start_opcode(); +            unsafe { +                out.write_lt_8(instr.opcode.name())?; +            } +            out.span_end_opcode(); +            out.write_fixed_size(" p")?; +            // coproc is a 3-bit field +            out.write_char((coproc + 0x30) as char)?; + +            out.write_fixed_size(", ")?; +            // opc1 is a 3-bit field +            out.write_char((opc1 + 0x30) as char)?; + +            let ops = instr.operands.iter(); +            for op in ops { +                if let Operand::Nothing = op { +                    break; +                } +                out.write_fixed_size(", ")?; +                let mut op_visitor = DisplayingOperandVisitor { +                    f: out +                }; +                op.visit(&mut op_visitor)?; +            } + +            out.write_fixed_size(", ")?; +            // opc2 is a 3-bit field +            out.write_char((opc2 + 0x30) as char)?; + +            return Ok(()); +        } +        _ => {} +    } + +    // no aliasing, just print the opcode itself +    out.span_start_opcode(); +    instr.write_conditioned_opcode(&instr.opcode, out)?; +    // don't end the opcode yet because some include a few extra characters after the mnemonic. + +    if instr.opcode == Opcode::IT { +        let (Operand::Imm32(cond), Operand::Imm32(mask)) = (&instr.operands[0], &instr.operands[1]) else { +            panic!("impossible it operand"); +        }; + +        let inv = cond & 1 == 1; +        let condition = ConditionCode::build(*cond as u8); +        if mask & 0b0001 != 0 { +            // three flags +            out.write_char(if inv ^ ((mask & 0b1000) != 0) { 'e' } else { 't' })?; +            out.write_char(if inv ^ ((mask & 0b0100) != 0) { 'e' } else { 't' })?; +            out.write_char(if inv ^ ((mask & 0b0010) != 0) { 'e' } else { 't' })?; +        } else if mask & 0b0010 != 0 { +            // two flags +            out.write_char(if inv ^ ((mask & 0b1000) != 0) { 'e' } else { 't' })?; +            out.write_char(if inv ^ ((mask & 0b0100) != 0) { 'e' } else { 't' })?; +        } else if mask & 0b0100 != 0 { +            // one flag +            out.write_char(if inv ^ ((mask & 0b1000) != 0) { 'e' } else { 't' })?; +        } else { +            // no flags +        } +        out.span_end_opcode(); + +        out.write_char(' ')?; +        let name = condition.name(); +        // all condition codes are two characters long +        out.write_char(name[0] as char)?; +        out.write_char(name[1] as char)?; +        return Ok(()); +    } + +    out.span_end_opcode(); + +    match instr.opcode { +        Opcode::CPS(_) => { +            if let Operand::Imm12(aif) = &instr.operands[0] { +                let mut comma = false; + +                if *aif != 0 { +                    out.write_char(' ')?; +                    if aif & 0b100 != 0 { out.write_char('a')?; } +                    if aif & 0b010 != 0 { out.write_char('i')?; } +                    if aif & 0b001 != 0 { out.write_char('f')?; } +                    // we wrote something for the first operand after all, so include a comma +                    comma = true; +                } +                if let Operand::Imm12(mode) = &instr.operands[1] { +                    if comma { out.write_char(',')?; } +                    out.write_fixed_size(" #")?; +                    out.write_prefixed_u16(*mode)?; +                } +                return Ok(()); +            } else { +                panic!("impossible cps operand"); +            } +        } +        Opcode::SETEND => { +            if let Operand::Imm12(i) = &instr.operands[0] { +                out.write_char(' ')?; +                if *i == 0 { +                    out.write_fixed_size("le")?; +                } else { +                    out.write_fixed_size("be")?; +                } +                return Ok(()); +            } else { +                panic!("impossible setend operand"); +            } +        } + +        _ => {} +    } + +    match instr.opcode { +        // TODO: [add, pre, usermode] +        Opcode::STM(_add, _pre, _wback, _usermode) | +        Opcode::LDM(_add, _pre, _wback, _usermode) => { +            match instr.operands { +                [Operand::RegWBack(Rr, wback), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { +                    out.write_char(' ')?; +                    out.write_reg(Rr.number())?; +                    if wback { out.write_char('!')?; } +                    out.write_fixed_size(", ")?; +                    let mut op_visitor = DisplayingOperandVisitor { +                        f: out, +                    }; +                    op_visitor.visit_reglist(list)?; +                    return Ok(()); +                }, +                _ => { unreachable!(); } +            } +        }, +        _ => {} +    } + +    let mut ops = instr.operands.iter(); +    if let Some(first_op) = ops.next() { +        if let Operand::Nothing = first_op { +            return Ok(()); +        } +        out.write_char(' ')?; +        let mut op_visitor = DisplayingOperandVisitor { +            f: out +        }; +        first_op.visit(&mut op_visitor)?; +    } else { +        return Ok(()); +    } + +    for op in ops { +        if let Operand::Nothing = op { +            break; +        } +        out.write_fixed_size(", ")?; +        let mut op_visitor = DisplayingOperandVisitor { +            f: out +        }; +        op.visit(&mut op_visitor)?; +    } + +    Ok(()) +} + +#[allow(non_snake_case)] +#[allow(deprecated)] +impl <T: fmt::Write, Y: YaxColors> ShowContextual<u32, NoContext, T, Y> for Instruction { +    fn contextualize(&self, _colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +        let mut f = yaxpeax_arch::display::FmtSink::new(out); +        visit_inst(self, &mut f) +    } +} + +#[allow(deprecated)] +impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for ConditionedOpcode { +    fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { +        match self.0 { +            Opcode::UDF | +            Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) }, +            Opcode::TBB | +            Opcode::TBH | +            Opcode::CBZ | +            Opcode::CBNZ | +            Opcode::IT | +            Opcode::B | +            Opcode::BL | +            Opcode::BLX | +            Opcode::BX | +            Opcode::BXJ => { write!(out, "{}", colors.control_flow_op(self)) }, + +            Opcode::AND | +            Opcode::EOR | +            Opcode::ORR | +            Opcode::ORN | +            Opcode::LSL | +            Opcode::LSR | +            Opcode::ROR | +            Opcode::ASR | +            Opcode::RRX | +            Opcode::BIC | + +            Opcode::ADR | +            Opcode::SUB | +            Opcode::RSB | +            Opcode::ADD | +            Opcode::ADC | +            Opcode::SBC | +            Opcode::RSC | + +            Opcode::QADD | +            Opcode::QSUB | +            Opcode::QDADD | +            Opcode::QDSUB | + +            Opcode::SADD16 | +            Opcode::QADD16 | +            Opcode::SHADD16 | +            Opcode::SASX | +            Opcode::QASX | +            Opcode::SHASX | +            Opcode::SSAX | +            Opcode::QSAX | +            Opcode::SHSAX | +            Opcode::SSUB16 | +            Opcode::QSUB16 | +            Opcode::SHSUB16 | +            Opcode::SADD8 | +            Opcode::QADD8 | +            Opcode::SHADD8 | +            Opcode::SSUB8 | +            Opcode::QSUB8 | +            Opcode::SHSUB8 | +            Opcode::UADD16 | +            Opcode::UQADD16 | +            Opcode::UHADD16 | +            Opcode::UASX | +            Opcode::UQASX | +            Opcode::UHASX | +            Opcode::USAX | +            Opcode::UQSAX | +            Opcode::UHSAX | +            Opcode::USUB16 | +            Opcode::UQSUB16 | +            Opcode::UHSUB16 | +            Opcode::UADD8 | +            Opcode::UQADD8 | +            Opcode::UHADD8 | +            Opcode::USUB8 | +            Opcode::UQSUB8 | +            Opcode::UHSUB8 | + +            Opcode::CLZ | + +            Opcode::MUL | +            Opcode::MLA | +            Opcode::UMAAL | +            Opcode::MLS | +            Opcode::UMULL | +            Opcode::UMLAL | +            Opcode::SMLSD | +            Opcode::SMMLA | +            Opcode::SMMLS | +            Opcode::USADA8 | +            Opcode::USAD8 | +            Opcode::SDIV | +            Opcode::UDIV | +            Opcode::SMLALD(_) | +            Opcode::SMLSLD(_) | +            Opcode::SMLAD | +            Opcode::SMUSD | +            Opcode::SMMUL | +            Opcode::SMULW(_) | +            Opcode::SMUAD | +            Opcode::SMULL | +            Opcode::SMUL(_, _) | +            Opcode::SMAL(_, _) | +            Opcode::SMLA(_, _) | +            Opcode::SMLAW(_) | +            Opcode::SMLAL | +            Opcode::SMLAL_halfword(_, _) => { write!(out, "{}", colors.arithmetic_op(self)) }, + +            Opcode::PUSH | +            Opcode::POP => { write!(out, "{}", colors.stack_op(self)) }, + +            Opcode::TST | +            Opcode::TEQ | +            Opcode::CMP | +            Opcode::CMN => { write!(out, "{}", colors.comparison_op(self)) }, + +            Opcode::LDRSH | +            Opcode::LDRSHT | +            Opcode::LDRSB | +            Opcode::LDRSBT | +            Opcode::STRD | +            Opcode::LDRD | +            Opcode::LDREXH | +            Opcode::STREXH | +            Opcode::LDREXB | +            Opcode::STREXB | +            Opcode::LDREXD | +            Opcode::STREXD | +            Opcode::LDREX | +            Opcode::STREX | +            Opcode::LDM(false, false, _, _) | +            Opcode::LDM(false, true, _, _) | +            Opcode::LDM(true, false, _, _) | +            Opcode::LDM(true, true, _, _) | +            Opcode::STM(false, false, _, _) | +            Opcode::STM(false, true, _, _) | +            Opcode::STM(true, false, _, _) | +            Opcode::STM(true, true, _, _) | +            Opcode::LDR | +            Opcode::STR | +            Opcode::LDRH | +            Opcode::STRH | +            Opcode::LDRB | +            Opcode::STRB | +            Opcode::LDRT | +            Opcode::STRT | +            Opcode::LDRHT | +            Opcode::STRHT | +            Opcode::LDRBT | +            Opcode::STRBT | +            Opcode::SWP | +            Opcode::SWPB | +            Opcode::MSR | +            Opcode::MRS | +            Opcode::CLREX | +            Opcode::SXTAB | +            Opcode::SXTAB16 | +            Opcode::SXTAH | +            Opcode::SXTB | +            Opcode::SXTB16 | +            Opcode::SXTH | +            Opcode::UXTAB | +            Opcode::UXTAB16 | +            Opcode::UXTAH | +            Opcode::UXTB | +            Opcode::UXTB16 | +            Opcode::UXTH | +            Opcode::PKHTB | +            Opcode::PKHBT | +            Opcode::REV | +            Opcode::REV16 | +            Opcode::REVSH | +            Opcode::SSAT | +            Opcode::SSAT16 | +            Opcode::SBFX | +            Opcode::USAT | +            Opcode::USAT16 | +            Opcode::UBFX | +            Opcode::BFI | +            Opcode::BFC | +            Opcode::RBIT | +            Opcode::SEL | +            Opcode::MOV | +            Opcode::MOVT | +            Opcode::MVN => { write!(out, "{}", colors.data_op(self)) }, + +            Opcode::HINT | +            Opcode::NOP | +            Opcode::PLD | +            Opcode::PLI | +            Opcode::ISB | +            Opcode::DMB | +            Opcode::DSB | +            Opcode::CSDB | +            Opcode::SRS(_, _) | +            Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) }, + +            Opcode::DBG | +            Opcode::CPS(_) | +            Opcode::CPS_modeonly | +            Opcode::SETEND | +            Opcode::ENTERX | +            Opcode::LEAVEX | +            Opcode::YIELD | +            Opcode::WFE | +            Opcode::WFI | +            Opcode::SEV | +            Opcode::ERET | +            Opcode::RFE(_, _) | +            Opcode::HVC | +            Opcode::SVC | +            Opcode::SMC | +            Opcode::LDC(_) | +            Opcode::LDCL(_) | +            Opcode::LDC2(_) | +            Opcode::LDC2L(_) | +            Opcode::STC(_) | +            Opcode::STCL(_) | +            Opcode::STC2(_) | +            Opcode::STC2L(_) | +            Opcode::MCRR2(_, _) | +            Opcode::MCR2(_, _, _) | +            Opcode::MRRC2(_, _) | +            Opcode::MRC2(_, _, _) | +            Opcode::MCRR(_, _) | +            Opcode::MRRC(_, _) | +            Opcode::CDP2(_, _, _) => { write!(out, "{}", colors.platform_op(self)) }, +        } +    } +} + +impl fmt::Display for Opcode { +    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { +        let mut f = yaxpeax_arch::display::FmtSink::new(f); +        self.write_to_sink(&mut f) +    } +} + +impl Opcode { +    fn write_to_sink<T: DisplaySink>(&self, sink: &mut T) -> Result<(), fmt::Error> { +        sink.write_str(self.name()) +    } + +    fn name(&self) -> &'static str { +        match self { +            Opcode::LDRSH => { "ldrsh" }, +            Opcode::LDRSHT => { "ldrsht" }, +            Opcode::LDRSB => { "ldrsb" }, +            Opcode::LDRSBT => { "ldrsbt" }, +            Opcode::STRD => { "strd" }, +            Opcode::LDRD => { "ldrd" }, +            Opcode::LDC(_) => { "ldc" }, +            Opcode::LDCL(_) => { "ldcl" }, +            Opcode::LDC2(_) => { "ldc2" }, +            Opcode::LDC2L(_) => { "ldc2l" }, +            Opcode::STC(_) => { "stc" }, +            Opcode::STCL(_) => { "stcl" }, +            Opcode::STC2(_) => { "stc2" }, +            Opcode::STC2L(_) => { "stc2l" }, +            Opcode::MCRR2(_, _) => { "mcrr2" }, +            Opcode::MCR2(_, _, _) => { "mcr2" }, +            Opcode::MRRC2(_, _) => { "mrrc2" }, +            Opcode::MRC2(_, _, _) => { "mrc2" }, +            Opcode::MCRR(_, _) => { "mcrr" }, +            Opcode::MRRC(_, _) => { "mrrc" }, +            Opcode::CDP2(_, _, _) => { "cdp2" }, +            Opcode::SRS(true, true) => { "srsib" }, +            Opcode::SRS(false, true) => { "srsia" }, +            Opcode::SRS(true, false) => { "srsdb" }, +            Opcode::SRS(false, false) => { "srsda" }, +            Opcode::RFE(true, true) => { "rfeib" }, +            Opcode::RFE(false, true) => { "rfeia" }, +            Opcode::RFE(true, false) => { "rfedb" }, +            Opcode::RFE(false, false) => { "rfeda" }, +            Opcode::ERET => { "eret" }, +            Opcode::HVC => { "hvc" }, +            Opcode::BKPT => { "bkpt" }, +            Opcode::SMC => { "smc" }, +            Opcode::MOVT => { "movt" }, +            Opcode::QADD => { "qadd" }, +            Opcode::QSUB => { "qsub" }, +            Opcode::QDADD => { "qdadd" }, +            Opcode::QDSUB => { "qdsub" }, +            Opcode::Invalid => { "invalid" }, +            Opcode::POP => { "pop" }, +            Opcode::PUSH => { "push" }, +            Opcode::B => { "b" }, +            Opcode::BL => { "bl" }, +            Opcode::BLX => { "blx" }, +            Opcode::BX => { "bx" }, +            Opcode::BXJ => { "bxj" }, +            Opcode::CLZ => { "clz" }, +            Opcode::AND => { "and" }, +            Opcode::EOR => { "eor" }, +            Opcode::SUB => { "sub" }, +            Opcode::RSB => { "rsb" }, +            Opcode::ADD => { "add" }, +            Opcode::ADC => { "adc" }, +            Opcode::SBC => { "sbc" }, +            Opcode::RSC => { "rsc" }, +            Opcode::TST => { "tst" }, +            Opcode::TEQ => { "teq" }, +            Opcode::CMP => { "cmp" }, +            Opcode::CMN => { "cmn" }, +            Opcode::ORR => { "orr" }, +            Opcode::MOV => { "mov" }, +            Opcode::MSR => { "msr" }, +            Opcode::MRS => { "mrs" }, +            Opcode::BIC => { "bic" }, +            Opcode::MVN => { "mvn" }, +            Opcode::LSL => { "lsl" }, +            Opcode::LSR => { "lsr" }, +            Opcode::ASR => { "asr" }, +            Opcode::RRX => { "rrx" }, +            Opcode::ROR => { "ror" }, +            Opcode::ADR => { "adr" }, +            Opcode::LDREXH => { "ldrexh" }, +            Opcode::STREXH => { "strexh" }, +            Opcode::LDREXB => { "ldrexb" }, +            Opcode::STREXB => { "strexb" }, +            Opcode::LDREXD => { "ldrexd" }, +            Opcode::STREXD => { "strexd" }, +            Opcode::LDREX => { "ldrex" }, +            Opcode::STREX => { "strex" }, +            Opcode::LDM(false, false, _, _) => { "ldmda" }, +            Opcode::LDM(false, true, _, _) => { "ldmdb" }, +            // TODO: seems like these are backwards +            Opcode::LDM(true, false, _, _) => { "ldm" }, +            Opcode::LDM(true, true, _, _) => { "ldmia" }, +            Opcode::STM(false, false, _, _) => { "stmda" }, +            Opcode::STM(false, true, _, _) => { "stmdb" }, +            // TODO: seems like these are backwards +            Opcode::STM(true, false, _, _) => { "stm" }, +            Opcode::STM(true, true, _, _) => { "stmia" }, +            Opcode::LDR => { "ldr" }, +            Opcode::STR => { "str" }, +            Opcode::LDRH => { "ldrh" }, +            Opcode::STRH => { "strh" }, +            Opcode::LDRB => { "ldrb" }, +            Opcode::STRB => { "strb" }, +            Opcode::LDRT => { "ldrt" }, +            Opcode::STRT => { "strt" }, +            Opcode::LDRHT => { "ldrht" }, +            Opcode::STRHT => { "strht" }, +            Opcode::LDRBT => { "ldrbt" }, +            Opcode::STRBT => { "strbt" }, +            Opcode::SWP => { "swp" }, +            Opcode::SWPB => { "swpb" }, +            Opcode::SDIV => { "sdiv" }, +            Opcode::UDIV => { "udiv" }, +            Opcode::MUL => { "mul" }, +            Opcode::MLA => { "mla" }, +            Opcode::UMAAL => { "umaal" }, +            Opcode::MLS => { "mls" }, +            Opcode::UMULL => { "umull" }, +            Opcode::UMLAL => { "umlal" }, +            Opcode::SMULL => { "smull" }, +            Opcode::SMLA(true, true) => { "smlatt" }, +            Opcode::SMLA(true, false) => { "smlatb" }, +            Opcode::SMLA(false, true) => { "smlabt" }, +            Opcode::SMLA(false, false) => { "smlabb" }, +            Opcode::SMLAL => { "smlal" }, +            Opcode::SMLAL_halfword(true, true) => { "smlaltt" }, +            Opcode::SMLAL_halfword(true, false) => { "smlaltb" }, +            Opcode::SMLAL_halfword(false, true) => { "smlalbt" }, +            Opcode::SMLAL_halfword(false, false) => { "smlalbb" }, +            Opcode::SMUL(true, true) => { "smultt" }, +            Opcode::SMUL(true, false) => { "smultb" }, +            Opcode::SMUL(false, true) => { "smulbt" }, +            Opcode::SMUL(false, false) => { "smulbb" }, +            Opcode::SMAL(true, true) => { "smaltt" }, +            Opcode::SMAL(true, false) => { "smaltb" }, +            Opcode::SMAL(false, true) => { "smalbt" }, +            Opcode::SMAL(false, false) => { "smalbb" }, +            Opcode::SMLAW(true) => { "smlawt" }, +            Opcode::SMLAW(false) => { "smlawb" }, +            Opcode::SMULW(true) => { "smulwt" }, +            Opcode::SMULW(false) => { "smulwb" }, +            Opcode::SMLALD(true) => { "smlaldt" }, +            Opcode::SMLALD(false) => { "smlaldb" }, +            Opcode::SMLSLD(true) => { "smlsldt" }, +            Opcode::SMLSLD(false) => { "smlsldb" }, +            Opcode::SMLSD => { "smlsd" }, +            Opcode::SMMLA => { "smmla" }, +            Opcode::SMMLS => { "smmls" }, +            Opcode::USADA8 => { "usada8" }, +            Opcode::USAD8 => { "usad8" }, +            Opcode::SMLAD => { "smlad" }, +            Opcode::SMUSD => { "smusd" }, +            Opcode::SMMUL => { "smmul" }, +            Opcode::SMUAD => { "smuad" }, +            Opcode::TBB => { "tbb" }, +            Opcode::TBH => { "tbh" }, +            Opcode::UDF => { "udf" }, +            Opcode::SVC => { "svc" }, +            Opcode::WFE => { "wfe" }, +            Opcode::WFI => { "wfi" }, +            Opcode::SEV => { "sev" }, +            Opcode::CSDB => { "csdb" }, +            Opcode::YIELD => { "yield" }, +            Opcode::HINT => { "hint" }, +            Opcode::NOP => { "nop" }, +            Opcode::LEAVEX => { "leavex" }, +            Opcode::ENTERX => { "enterx" }, +            Opcode::CLREX => { "clrex" }, +            Opcode::DSB => { "dsb" }, +            Opcode::DMB => { "dmb" }, +            Opcode::ISB => { "isb" }, +            Opcode::SXTH => { "sxth" }, +            Opcode::UXTH => { "uxth" }, +            Opcode::SXTB16 => { "sxtb16" }, +            Opcode::UXTB16 => { "uxtb16" }, +            Opcode::SXTB => { "sxtb" }, +            Opcode::UXTB => { "uxtb" }, +            Opcode::SXTAH => { "sxtah" }, +            Opcode::UXTAH => { "uxtah" }, +            Opcode::SXTAB16 => { "sxtab16" }, +            Opcode::UXTAB16 => { "uxtab16" }, +            Opcode::SXTAB => { "sxtab" }, +            Opcode::UXTAB => { "uxtab" }, +            Opcode::CBZ => { "cbz" }, +            Opcode::CBNZ => { "cbnz" }, +            Opcode::SETEND => { "setend" }, +            Opcode::CPS(true) => { "cpsid" }, +            Opcode::CPS(false) => { "cpsie" }, +            Opcode::CPS_modeonly => { "cps" }, +            Opcode::REV => { "rev" }, +            Opcode::REV16 => { "rev16" }, +            Opcode::REVSH => { "revsh" }, +            Opcode::IT => { "it" }, +            Opcode::PKHTB => { "pkhtb" }, +            Opcode::PKHBT => { "pkhbt" }, +            Opcode::ORN => { "orn" }, +            Opcode::SSAT => { "ssat" }, +            Opcode::SSAT16 => { "ssat16" }, +            Opcode::SBFX => { "sbfx" }, +            Opcode::USAT => { "usat" }, +            Opcode::USAT16 => { "usat16" }, +            Opcode::UBFX => { "ubfx" }, +            Opcode::BFI => { "bfi" }, +            Opcode::BFC => { "bfc" }, +            Opcode::DBG => { "dbg" }, +            Opcode::PLD => { "pld" }, +            Opcode::PLI => { "pli" }, +            Opcode::RBIT => { "rbit" }, +            Opcode::SEL => { "sel" }, +            Opcode::SADD16 => { "sadd16" }, +            Opcode::QADD16 => { "qadd16" }, +            Opcode::SHADD16 => { "shadd16" }, +            Opcode::SASX => { "sasx" }, +            Opcode::QASX => { "qasx" }, +            Opcode::SHASX => { "shasx" }, +            Opcode::SSAX => { "ssax" }, +            Opcode::QSAX => { "qsax" }, +            Opcode::SHSAX => { "shsax" }, +            Opcode::SSUB16 => { "ssub16" }, +            Opcode::QSUB16 => { "qsub16" }, +            Opcode::SHSUB16 => { "shsub16" }, +            Opcode::SADD8 => { "sadd8" }, +            Opcode::QADD8 => { "qadd8" }, +            Opcode::SHADD8 => { "shadd8" }, +            Opcode::SSUB8 => { "ssub8" }, +            Opcode::QSUB8 => { "qsub8" }, +            Opcode::SHSUB8 => { "shsub8" }, +            Opcode::UADD16 => { "uadd16" }, +            Opcode::UQADD16 => { "uqadd16" }, +            Opcode::UHADD16 => { "uhadd16" }, +            Opcode::UASX => { "uasx" }, +            Opcode::UQASX => { "uqasx" }, +            Opcode::UHASX => { "uhasx" }, +            Opcode::USAX => { "usax" }, +            Opcode::UQSAX => { "uqsax" }, +            Opcode::UHSAX => { "uhsax" }, +            Opcode::USUB16 => { "usub16" }, +            Opcode::UQSUB16 => { "uqsub16" }, +            Opcode::UHSUB16 => { "uhsub16" }, +            Opcode::UADD8 => { "uadd8" }, +            Opcode::UQADD8 => { "uqadd8" }, +            Opcode::UHADD8 => { "uhadd8" }, +            Opcode::USUB8 => { "usub8" }, +            Opcode::UQSUB8 => { "uqsub8" }, +            Opcode::UHSUB8 => { "uhsub8" }, +        } +    } +} + +impl fmt::Display for Bank { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        f.write_str(self.name()) +    } +} + +impl Bank { +    fn name(&self) -> &'static str { +        match self { +            Bank::Usr => "usr", +            Bank::Fiq => "fiq", +            Bank::Irq => "irq", +            Bank::Svc => "svc", +            Bank::Abt => "abt", +            Bank::Und => "und", +            Bank::Mon => "mon", +            Bank::Hyp => "hyp", +        } +    } +} + +impl fmt::Display for StatusRegMask { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        f.write_str(self.name()) +    } +} + +impl StatusRegMask { +    fn name(&self) -> &'static str { +        match self { +            StatusRegMask::CPSR_C => "cpsr_c", +            StatusRegMask::CPSR_X => "cpsr_x", +            StatusRegMask::CPSR_XC => "cpsr_xc", +            StatusRegMask::APSR_G => "apsr_g", +            StatusRegMask::CPSR_SC => "cpsr_sc", +            StatusRegMask::CPSR_SX => "cpsr_sx", +            StatusRegMask::CPSR_SXC => "cpsr_sxc", +            StatusRegMask::APSR_NZCVQ => "apsr_nzcvq", +            StatusRegMask::CPSR_FC => "cpsr_fc", +            StatusRegMask::CPSR_FX => "cpsr_fx", +            StatusRegMask::CPSR_FXC => "cpsr_fxc", +            StatusRegMask::APSR_NZCVQG => "apsr_nzcvqg", +            StatusRegMask::CPSR_FSC => "cpsr_fsc", +            StatusRegMask::CPSR_FSX => "cpsr_fsx", +            StatusRegMask::CPSR_FSXC => "cpsr_fsxc", +            StatusRegMask::SPSR => "spsr", +            StatusRegMask::SPSR_C => "spsr_c", +            StatusRegMask::SPSR_X => "spsr_x", +            StatusRegMask::SPSR_XC => "spsr_xc", +            StatusRegMask::SPSR_S => "spsr_s", +            StatusRegMask::SPSR_SC => "spsr_sc", +            StatusRegMask::SPSR_SX => "spsr_sx", +            StatusRegMask::SPSR_SXC => "spsr_sxc", +            StatusRegMask::SPSR_F => "spsr_f", +            StatusRegMask::SPSR_FC => "spsr_fc", +            StatusRegMask::SPSR_FX => "spsr_fx", +            StatusRegMask::SPSR_FXC => "spsr_fxc", +            StatusRegMask::SPSR_FS => "spsr_fs", +            StatusRegMask::SPSR_FSC => "spsr_fsc", +            StatusRegMask::SPSR_FSX => "spsr_fsx", +            StatusRegMask::SPSR_FSXC => "spsr_fsxc", +        } +    } +} + +#[allow(deprecated)] +impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for super::Operand { +    fn colorize(&self, _colors: &Y, f: &mut T) -> fmt::Result { +        let mut f = yaxpeax_arch::display::FmtSink::new(f); +        let mut visitor = DisplayingOperandVisitor { +            f: &mut f, +        }; +        self.visit(&mut visitor) +    } +} + +impl super::Operand { +    fn visit<V: super::OperandVisitor>(&self, visitor: &mut V) -> Result<V::Ok, V::Error> { +        match *self { +            Operand::RegList(list) => { +                visitor.visit_reglist(list) +            } +            Operand::BankedReg(bank, reg) => { +                visitor.visit_banked_reg(bank, reg.number() as u16) +            }, +            Operand::BankedSPSR(bank) => { +                visitor.visit_banked_spsr(bank) +            }, +            Operand::Reg(reg) => { +                visitor.visit_reg(reg) +            } +            Operand::RegDeref(reg) => { +                visitor.visit_reg_deref(reg) +            } +            Operand::RegShift(shift) => { +                visitor.visit_reg_shift(shift) +            } +            Operand::RegDerefPostindexRegShift(reg, shift, add, wback) => { +                visitor.visit_reg_deref_postindex_reg_shift(reg, shift, add, wback) +            } +            Operand::RegDerefPreindexRegShift(reg, shift, add, wback) => { +                visitor.visit_reg_deref_preindex_reg_shift(reg, shift, add, wback) +            } +            Operand::RegDerefPostindexOffset(reg, offs, add, wback) => { +                visitor.visit_reg_deref_postindex_offset(reg, offs, add, wback) +            } +            Operand::RegDerefPreindexOffset(reg, offs, add, wback) => { +                visitor.visit_reg_deref_preindex_offset(reg, offs, add, wback) +            } +            Operand::RegDerefPostindexReg(reg, offsreg, add, wback) => { +                visitor.visit_reg_deref_postindex_reg(reg, offsreg, add, wback) +            } +            Operand::RegDerefPreindexReg(reg, offsreg, add, wback) => { +                visitor.visit_reg_deref_preindex_reg(reg, offsreg, add, wback) +            } +            Operand::Imm12(imm) => { +                visitor.visit_imm12(imm) +            } +            Operand::Imm32(imm) => { +                visitor.visit_imm32(imm) +            } +            Operand::BranchOffset(imm) => { +                visitor.visit_branch_offset(imm) +            } +            Operand::BranchThumbOffset(imm) => { +                visitor.visit_blx_offset(imm) +            } +            #[allow(deprecated)] +            Operand::Coprocessor(_num) => { +                unreachable!("Operand::Coprocessor is never constructed and will be removed in a future release"); +            } +            Operand::CoprocOption(num) => { +                visitor.visit_coprocessor_option(num) +            } +            Operand::RegWBack(reg, wback) => { +                visitor.visit_reg_wback(reg, wback) +            } +            Operand::CReg(creg) => { +                visitor.visit_creg(creg) +            } +            Operand::StatusRegMask(mask) => { +                visitor.visit_status_reg_mask(mask) +            } +            Operand::APSR => { +                visitor.visit_apsr() +            }, +            Operand::SPSR => { +                visitor.visit_spsr() +            }, +            Operand::CPSR => { +                visitor.visit_cpsr() +            }, +            Operand::Nothing => { panic!("tried to print Nothing operand") }, +        } +    } +} + +#[cfg(all(feature="alloc", feature="fmt"))] +mod buffer_sink { +    use core::fmt; +    use super::{InstructionDisplayer}; + +    // this exists to size `InstructionTextBuffer`'s buffer. it ideally would be somehow related to +    // `Arch`, but i'm not sure how to wire that up. so instead, calculate an appropriate max size +    // here. +    // +    // the longest instruction would be something like the longest mnemonic and four register +    // lists. in reality an ARM instruction has at most one register list so this is a gross +    // overestimate. but on with the show; assume 16 bytes for the longest mnemonic, then register +    // lists as `{(rNN, ){16}}`. each register list, then, is 80 bytes, brackets are two more, four +    // spaces for separation, and three commas for internal spacing. that's 16 + 320 + + 8 + 4 + 3. +    // round again and that gets you to 32 + 320 ~~ 352 bytes. out of sheer paranoia and excessive +    // padding desire, i'm going with 512 here as the next power of two. +    const MAX_INSTRUCTION_LEN: usize = 512; + +    /// helper to format `armv7` instructions with highest throughput and least configuration. this is +    /// functionally a buffer for one armv7 instruction's text. +    /// +    /// ### when to use this over `fmt::Display`? +    /// +    /// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may +    /// support formatting options that may be difficult to configure for a `Display` impl. +    /// additionally, `InstructionTextBuffer` may be able to specialize more effectively where +    /// `fmt::Display`, writing to a generic `fmt::Write`, may not. +    /// +    /// if your use case for `yaxpeax-arm` involves being bounded on the speed of disassembling and +    /// formatting instructions, `InstructionTextBuffer` may be a better choice. +    /// +    /// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-arm` +    /// requires allocations never occurring, it is not an appropriate tool. +    /// +    /// ### example +    /// +    /// ```text +    /// use yaxpeax_arm::armv7::InstDecoder; +    /// use yaxpeax_arm::armv7::InstructionTextBuffer; +    /// +    /// let bytes = &[0x34, 0x78, 0xbf, 0xfd]; +    /// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); +    /// let mut text_buf = InstructionTextBuffer::new(); +    /// assert_eq!( +    ///     text_buf.format_inst(&inst).expect("can format"), +    ///     "ldc2 p8, c7, [pc, 0xd0]" +    /// ); +    /// +    /// // or, getting the formatted instruction with `text_str`: +    /// assert_eq!( +    ///     text_buf.text_str(), +    ///     "ldc2 p8, c7, [pc, 0xd0]" +    /// ); +    /// ``` +    pub struct InstructionTextBuffer { +        content: alloc::string::String, +    } + +    impl InstructionTextBuffer { +        /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default +        /// settings format instructions identically to their corresponding `fmt::Display`. +        pub fn new() -> Self { +            let mut buf = alloc::string::String::new(); +            buf.reserve(MAX_INSTRUCTION_LEN); +            Self { +                content: buf, +            } +        } + +        /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. +        /// +        /// this clears and reuses an internal buffer; if an instruction had been previously formatted +        /// through this buffer, it will be overwritten. +        pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { +            // Safety: this sink is used to format exactly one instruction and then dropped. it can +            // never escape `format_inst`. +            let mut handle = unsafe { self.write_handle() }; + +            super::visit_inst(&display.instr, &mut handle)?; + +            Ok(self.text_str()) +        } + +        /// return a borrow of the internal buffer. if an instruction has been formatted, the +        /// returned `&str` contains that instruction's buffered text. +        pub fn text_str(&self) -> &str { +            self.content.as_str() +        } + +        /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction +        /// into. +        /// +        /// SAFETY: callers must print at most one instruction into this handle. +        unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { +            self.content.clear(); +            // Safety: `content` was just cleared, so writing begins at the start of the buffer. +            // `content`is large enough to hold a fully-formatted instruction (see +            // `InstructionTextBuffer::new`). +            yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) +        } +    } +} + +#[cfg(all(feature="alloc", feature="fmt"))] +pub use buffer_sink::InstructionTextBuffer; @@ -48,6 +48,8 @@  #![no_std]  #![deny(missing_docs)] +#[cfg(feature="alloc")] +extern crate alloc;  #[cfg(feature="use-serde")]  #[macro_use] extern crate serde_derive;  #[cfg(feature="use-serde")] diff --git a/tests/no_std_check/Cargo.toml b/tests/no_std_check/Cargo.toml index f5ad827..3edf154 100644 --- a/tests/no_std_check/Cargo.toml +++ b/tests/no_std_check/Cargo.toml @@ -4,7 +4,7 @@ name = "no-std-test"  version = "0.0.1"  authors = ["iximeow <me@iximeow.net>"]  license = "0BSD" -description = "test crate to check that yaxpeax-arm is actually no-stds" +description = "test crate to check that yaxpeax-arm is actually no-std"  edition = "2021"  publish = false | 
