/// Manual references in this crate, both figure and page numbers, are with respect to the document /// `DDI0406C_d_armv7ar_arm.pdf` /// `sha256: 294668ae6480133b32d85e9567cc77c5eb0e1232decdf42cac7ab480e884f6e0` //#[cfg(feature="use-serde")] //use serde::{Serialize, Deserialize}; use std::fmt::{self, Display, Formatter}; use yaxpeax_arch::{Arch, Colorize, Decoder, LengthedInstruction, NoColors, ShowContextual, YaxColors}; pub struct ConditionedOpcode(pub Opcode, pub bool, pub ConditionCode); impl Display for ConditionedOpcode { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "{}{}{}", self.0, if self.1 { "s" } else { "" }, self.2) } } pub struct NoContext; fn reg_name_colorize>(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)] impl > ShowContextual for Instruction { fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { match self.opcode { 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.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.condition).colorize(colors, out)?; return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); }, _ => {} } }, Opcode::LDM(true, false, true, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?; write!(out, " ")?; return format_reg_list(out, list, colors); } _ => {} } } Opcode::STM(false, true, true, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(Opcode::PUSH, self.s, 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::Reg(Rr), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; write!( out, " {}{}, ", reg_name_colorize(Rr, colors), if wback { "!" } else { "" } )?; return format_reg_list(out, list, colors); }, _ => { unreachable!(); } } }, Opcode::Incomplete(word) => { write!(out, "incomplete: {:#x}", word) }, 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::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.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(()) } } } } impl > Colorize for ConditionedOpcode { fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { match self.0 { Opcode::Incomplete(_) | Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) }, Opcode::B | Opcode::BL | Opcode::BLX | Opcode::BX | Opcode::BXJ => { write!(out, "{}", colors.control_flow_op(self)) }, Opcode::AND | Opcode::EOR | Opcode::ORR | 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::CLZ | Opcode::MUL | Opcode::MLA | Opcode::UMAAL | Opcode::MLS | Opcode::UMULL | Opcode::UMLAL | 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::MOV | Opcode::MOVT | Opcode::MVN => { write!(out, "{}", colors.data_op(self)) }, Opcode::SRS(_, _) | Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) }, Opcode::ERET | Opcode::RFE(_, _) | Opcode::HVC | Opcode::SMC | Opcode::LDC2(_) | Opcode::LDC2L(_) | Opcode::STC2(_) | Opcode::STC2L(_) | Opcode::MCRR2(_, _) | Opcode::MCR2(_, _, _) | Opcode::MRRC2(_, _) | Opcode::MRC2(_, _, _) | 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::LDC2(_) => { write!(f, "ldc2") }, Opcode::LDC2L(_) => { write!(f, "ldc2l") }, 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::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::Incomplete(word) => { write!(f, "incomplete: {:#x}", word) }, 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") }, 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") }, 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::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" }) }, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] pub enum Opcode { Incomplete(u32), Invalid, /* * These two don't really have direct encodings, but are for the specific instances * where the semantics of the original instruction are the same as push (specifically * ldm/stm/mov that write to the stack and increment/decrement appropriately */ POP, PUSH, B, BL, BLX, BX, BXJ, AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, TST, TEQ, CMP, CMN, ORR, MOV, BIC, MVN, LSL, LSR, ASR, RRX, ROR, ADR, MSR, MRS, CLZ, LDREXH, STREXH, LDREXB, STREXB, LDREXD, STREXD, LDREX, STREX, LDM(bool, bool, bool, bool), STM(bool, bool, bool, bool), LDR, STR, LDRH, STRH, LDRB, STRB, LDRSH, LDRSHT, LDRSB, LDRSBT, STRD, LDRD, LDC2(u8), LDC2L(u8), STC2(u8), STC2L(u8), MCRR2(u8, u8), MCR2(u8, u8, u8), MRRC2(u8, u8), MRC2(u8, u8, u8), CDP2(u8, u8, u8), SRS(bool, bool), RFE(bool, bool), LDRT, STRT, LDRHT, STRHT, LDRBT, STRBT, SWP, SWPB, MUL, MLA, UMAAL, MLS, UMULL, UMLAL, SMULL, SMUL(bool, bool), SMLA(bool, bool), SMLAL, SMLAL_halfword(bool, bool), SMAL(bool, bool), SMLAW(bool), ERET, BKPT, HVC, SMC, MOVT, QDSUB, QDADD, QSUB, QADD, } static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ Opcode::AND, Opcode::EOR, Opcode::SUB, Opcode::RSB, Opcode::ADD, Opcode::ADC, Opcode::SBC, Opcode::RSC, Opcode::TST, Opcode::TEQ, Opcode::CMP, Opcode::CMN, Opcode::ORR, Opcode::MOV, Opcode::BIC, Opcode::MVN ]; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[repr(transparent)] pub struct RegShift { data: u16 } impl RegShift { fn into_shift(&self) -> RegShiftStyle { if self.data & 0b1000 == 0 { RegShiftStyle::RegImm(RegImmShift { data: self.data }) } else { RegShiftStyle::RegReg(RegRegShift { data: self.data }) } } pub fn from_raw(data: u16) -> Self { RegShift { data } } } pub enum RegShiftStyle { RegImm(RegImmShift), RegReg(RegRegShift), } #[repr(transparent)] pub struct RegRegShift { data: u16 } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ShiftStyle { LSL = 0, LSR = 1, ASR = 2, ROR = 3, } 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"), } } } impl ShiftStyle { fn from(bits: u8) -> ShiftStyle { match bits { 0b00 => ShiftStyle::LSL, 0b01 => ShiftStyle::LSR, 0b10 => ShiftStyle::ASR, 0b11 => ShiftStyle::ROR, _ => unreachable!("bad ShiftStyle index") } } } impl RegRegShift { pub fn shifter(&self) -> Reg { Reg::from_u8((self.data >> 8) as u8 & 0b1111) } pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } #[repr(transparent)] pub struct RegImmShift { data: u16 } impl RegImmShift { pub fn imm(&self) -> u8 { (self.data >> 7) as u8 & 0b11111 } pub fn stype(&self) -> ShiftStyle { ShiftStyle::from((self.data >> 5) as u8 & 0b11) } pub fn shiftee(&self) -> Reg { Reg::from_u8(self.data as u8 & 0b1111) } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct Reg { bits: u8 } impl Reg { #[allow(non_snake_case)] fn from_sysm(R: bool, M: u8) -> Option { /* * Is one of: * • _, encoded with R==0. * • ELR_hyp, encoded with R==0. * • SPSR_, encoded with R==1. * For a full description of the encoding of this field, see Encoding and use of Banked register * transfer * instructions on page B9-1959. * */ if R == false { [ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(8))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(9))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(10))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(11))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(12))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(14))), None, Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(8))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(9))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(10))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(11))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(12))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(14))), None, Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Und, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Und, Reg::from_u8(14))), None, None, None, None, Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(14))), Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(13))), Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(14))), ][M as usize] } else { if M == 0b01110 { Some(Operand::BankedSPSR(Bank::Fiq)) } else if M == 0b10000 { Some(Operand::BankedSPSR(Bank::Irq)) } else if M == 0b10010 { Some(Operand::BankedSPSR(Bank::Svc)) } else if M == 0b10100 { Some(Operand::BankedSPSR(Bank::Abt)) } else if M == 0b10110 { Some(Operand::BankedSPSR(Bank::Und)) } else if M == 0b11100 { Some(Operand::BankedSPSR(Bank::Mon)) } else if M == 0b11110 { Some(Operand::BankedSPSR(Bank::Hyp)) } else { None } } } pub fn from_u8(bits: u8) -> Reg { if bits > 0b1111 { panic!("register number out of range"); } Reg { bits } } pub fn number(&self) -> u8 { self.bits } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(transparent)] pub struct CReg { bits: u8 } impl Display for CReg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "c{}", self.bits) } } impl CReg { pub fn from_u8(bits: u8) -> CReg { if bits > 0b1111 { panic!("register number out of range"); } CReg { bits } } pub fn number(&self) -> u8 { self.bits } } 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)] pub enum StatusRegMask { // Note 0b0000 is unused (as is 0b10000) CPSR_C = 0b0001, CPSR_X = 0b0010, CPSR_XC = 0b0011, APSR_G = 0b0100, CPSR_SC = 0b0101, CPSR_SX = 0b0110, CPSR_SXC = 0b0111, APSR_NZCVQ = 0b1000, CPSR_FC = 0b1001, CPSR_FX = 0b1010, CPSR_FXC = 0b1011, APSR_NZCVQG = 0b1100, CPSR_FSC = 0b1101, CPSR_FSX = 0b1110, CPSR_FSXC = 0b1111, SPSR = 0b10000, SPSR_C = 0b10001, SPSR_X = 0b10010, SPSR_XC = 0b10011, SPSR_S = 0b10100, SPSR_SC = 0b10101, SPSR_SX = 0b10110, SPSR_SXC = 0b10111, SPSR_F = 0b11000, SPSR_FC = 0b11001, SPSR_FX = 0b11010, SPSR_FXC = 0b11011, SPSR_FS = 0b11100, SPSR_FSC = 0b11101, SPSR_FSX = 0b11110, SPSR_FSXC = 0b11111, } impl StatusRegMask { fn from_raw(raw: u8) -> StatusRegMask { if raw == 0 { panic!("invalid status reg mask value"); } [ StatusRegMask::CPSR_C, // actually unreachable StatusRegMask::CPSR_C, StatusRegMask::CPSR_X, StatusRegMask::CPSR_XC, StatusRegMask::APSR_G, StatusRegMask::CPSR_SC, StatusRegMask::CPSR_SX, StatusRegMask::CPSR_SXC, StatusRegMask::APSR_NZCVQ, StatusRegMask::CPSR_FC, StatusRegMask::CPSR_FX, StatusRegMask::CPSR_FXC, StatusRegMask::APSR_NZCVQG, StatusRegMask::CPSR_FSC, StatusRegMask::CPSR_FSX, StatusRegMask::CPSR_FSXC, StatusRegMask::SPSR, StatusRegMask::SPSR_C, StatusRegMask::SPSR_X, StatusRegMask::SPSR_XC, StatusRegMask::SPSR_S, StatusRegMask::SPSR_SC, StatusRegMask::SPSR_SX, StatusRegMask::SPSR_SXC, StatusRegMask::SPSR_F, StatusRegMask::SPSR_FC, StatusRegMask::SPSR_FX, StatusRegMask::SPSR_FXC, StatusRegMask::SPSR_FS, StatusRegMask::SPSR_FSC, StatusRegMask::SPSR_FSX, StatusRegMask::SPSR_FSXC, ][raw as usize] } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Operand { Reg(Reg), RegWBack(Reg, bool), RegList(u16), RegDeref(Reg), RegDisp(Reg, i16), RegShift(RegShift), RegDerefPostindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback RegDerefPreindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback RegDerefPostindexOffset(Reg, u16, bool, bool), // add/sub, wback RegDerefPreindexOffset(Reg, u16, bool, bool), // add/sub, wback RegDerefPostindexReg(Reg, Reg, bool, bool), // add/sub, wback RegDerefPreindexReg(Reg, Reg, bool, bool), // add/sub, wback Imm12(u16), Imm32(u32), BranchOffset(i32), BranchThumbOffset(i32), Coprocessor(u8), CoprocOption(u8), CReg(CReg), APSR, CPSR, SPSR, BankedReg(Bank, Reg), BankedSPSR(Bank), StatusRegMask(StatusRegMask), Nothing, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Bank { Usr, Fiq, Irq, Svc, Abt, Und, Mon, 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"), } } } impl > Colorize 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::RegDisp(reg, imm) => { write!(f, "[{} + {:#x}]", reg_name_colorize(*reg, colors), imm) } 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::CPSR => { write!(f, "cpsr") }, Operand::SPSR => { write!(f, "spsr") }, Operand::Nothing => { panic!("tried to print Nothing operand") }, } } } #[derive(Debug, PartialEq, Eq)] pub struct Instruction { pub condition: ConditionCode, pub opcode: Opcode, pub operands: [Operand; 4], pub s: bool } #[derive(Debug, PartialEq)] pub enum DecodeError { ExhaustedInput, InvalidOpcode, InvalidOperand, Incomplete, Nonconforming, Undefined, } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { DecodeError::ExhaustedInput => write!(f, "exhausted input"), DecodeError::InvalidOpcode => write!(f, "invalid opcode"), DecodeError::InvalidOperand => write!(f, "invalid operand"), DecodeError::Incomplete => write!(f, "incomplete decoder"), DecodeError::Nonconforming => write!(f, "invalid reserved bits"), DecodeError::Undefined => write!(f, "undefined encoding"), } } } impl yaxpeax_arch::DecodeError for DecodeError { fn data_exhausted(&self) -> bool { self == &DecodeError::ExhaustedInput } fn bad_opcode(&self) -> bool { self == &DecodeError::InvalidOpcode } fn bad_operand(&self) -> bool { self == &DecodeError::InvalidOperand } } impl yaxpeax_arch::Instruction for Instruction { // TODO: this is wrong!! fn well_defined(&self) -> bool { true } } impl Default for Instruction { fn default() -> Self { Instruction { condition: ConditionCode::AL, opcode: Opcode::Invalid, operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing], s: false } } } impl Instruction { fn set_s(&mut self, value: bool) { self.s = value; } pub fn s(&self) -> bool { self.s } } fn format_reg_list>(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)] fn format_shift>(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)] fn format_reg_shift_mem>(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)] fn format_reg_imm_mem>(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, true) => { unreachable!("I don't know how to render an operand with postindex and wback==true, this seems like it should be LDRT"); }, (false, 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, true) => { unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT"); }, (false, false) => { write!(f, "[{}]", reg_name_colorize(Rn, colors)) } } } } impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { self.contextualize(&NoColors, 0, Some(&NoContext), f) } } impl LengthedInstruction for Instruction { type Unit = ::Address; fn min_size() -> Self::Unit { 4 } fn len(&self) -> Self::Unit { 4 } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ConditionCode { EQ, NE, HS, LO, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL } 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(()) } } } impl ConditionCode { pub fn build(value: u8) -> ConditionCode { match value { 0b0000 => ConditionCode::EQ, 0b0001 => ConditionCode::NE, 0b0010 => ConditionCode::HS, 0b0011 => ConditionCode::LO, 0b0100 => ConditionCode::MI, 0b0101 => ConditionCode::PL, 0b0110 => ConditionCode::VS, 0b0111 => ConditionCode::VC, 0b1000 => ConditionCode::HI, 0b1001 => ConditionCode::LS, 0b1010 => ConditionCode::GE, 0b1011 => ConditionCode::LT, 0b1100 => ConditionCode::GT, 0b1101 => ConditionCode::LE, 0b1110 => ConditionCode::AL, _ => { // this means the argument `value` must never be outside [0,15] // which itself means this function shouldn't be public unreachable!(); } } } } #[derive(Debug, Copy, Clone)] #[allow(dead_code)] enum DecodeMode { User, FIQ, IRQ, Supervisor, Monitor, Abort, Hyp, Undefined, System, /// Catch-all mode to try decoding all ARM instructions. Some instructions are `UNDEFINED` or /// `UNPREDICTABLE` in some modes, but `Any` will attempt to decode all. Any, } impl Default for DecodeMode { fn default() -> Self { DecodeMode::Any } } impl DecodeMode { fn is_user(&self) -> bool { match self { DecodeMode::Any | DecodeMode::User => true, _ => false } } #[allow(dead_code)] fn is_supervisor(&self) -> bool { match self { DecodeMode::Any | DecodeMode::Supervisor => true, _ => false } } fn is_hyp(&self) -> bool { match self { DecodeMode::Any | DecodeMode::Hyp => true, _ => false } } fn is_system(&self) -> bool { match self { DecodeMode::Any | DecodeMode::System => true, _ => false } } fn is_any(&self) -> bool { match self { DecodeMode::Any => true, _ => false, } } } #[derive(Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] enum ARMVersion { v4, v5, v6, v6t2, v7, v7ve, v7vese, Any, } impl Default for ARMVersion { fn default() -> Self { ARMVersion::Any } } #[derive(Debug)] pub struct InstDecoder { mode: DecodeMode, version: ARMVersion, should_is_must: bool, } impl Default for InstDecoder { fn default() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::Any, should_is_must: true, } } } impl InstDecoder { pub fn armv4() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v4, should_is_must: true, } } pub fn armv5() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v5, should_is_must: true, } } pub fn armv6() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v6, should_is_must: true, } } pub fn armv6t2() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v6t2, should_is_must: true, } } pub fn armv7() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7, should_is_must: true, } } pub fn armv7ve() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7ve, should_is_must: true, } } pub fn armv7vese() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v7vese, should_is_must: true, } } } #[allow(non_snake_case)] impl Decoder for InstDecoder { type Error = DecodeError; fn decode_into>(&self, inst: &mut Instruction, bytes: T) -> Result<(), Self::Error> { fn read_word>(bytes: T) -> Result { let mut iter = bytes.into_iter(); let instr: u32 = ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u32) ) | ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 8 ) | ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 16) | ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u32) << 24); Ok(instr) } let word = read_word(bytes)?; let (cond, opc_upper) = { let top_byte = word >> 24; ( ((top_byte >> 4) & 0xf) as u8, ((top_byte >> 1) & 0x7) as u8 ) }; if cond == 0b1111 { // unconditional instructions, section A5.7/page A5-214 inst.condition = ConditionCode::AL; let op1 = (word >> 20) as u8; if op1 >= 0b1000_0000 { match (op1 >> 5) & 0b11 { 0b00 => { match op1 & 0b101 { 0b000 | 0b101 => { return Err(DecodeError::InvalidOpcode); } 0b100 => { // SRS (see table A5.7, op1 = 0b100xx1x0, page A5-214) if !self.mode.is_any() && self.mode.is_hyp() { return Err(DecodeError::Undefined); } if self.should_is_must { if word & 0x000fffe0 != 0x000d0500 { return Err(DecodeError::Nonconforming); } } let puxw = (word >> 21) & 0b1111; let P = puxw & 0b1000 != 0; let U = puxw & 0b0100 != 0; let W = puxw & 0b0001 != 0; inst.opcode = Opcode::SRS(P, U); inst.operands = [ Operand::RegWBack(Reg::from_u8(13), W), Operand::Imm32(word & 0b1111), Operand::Nothing, Operand::Nothing, ]; }, 0b001 => { // RFE (see table A5.7, op1 = 0b100xx0x1, page A5-214) if !self.mode.is_any() && self.mode.is_hyp() { return Err(DecodeError::Undefined); } if self.should_is_must { if word & 0xffff != 0x0a00 { return Err(DecodeError::Nonconforming); } } let puxw = (word >> 21) & 0b1111; let P = puxw & 0b1000 != 0; let U = puxw & 0b0100 != 0; let W = puxw & 0b0001 != 0; inst.opcode = Opcode::RFE(P, U); inst.operands = [ Operand::RegWBack(Reg::from_u8((word >> 16) as u8 & 0b1111), W), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } _ => { unreachable!("op1 mask is 0b101 but somehow we got an invalid pattern"); } } } 0b01 => { inst.opcode = Opcode::BLX; let operand = ((word & 0xffffff) as i32) << 8 >> 7; inst.operands = [ Operand::BranchThumbOffset( operand | ( ((word >> 24) & 0b1) as i32 ) ), Operand::Nothing, Operand::Nothing, Operand::Nothing, ]; } 0b10 => { // op1=0b110xxxxx, see table A5-23 if (word >> 20) & 0b11010 == 0b00000 { // the `not 11000x0{0,1}` cases in table A5-23, MCRR or MRRC // but first check that bit 2 of op1 is in fact 1: if (word >> 20) & 0b00100 != 0 { // actually MCRR or MRRC let CRm = word as u8 & 0b1111; let opc1 = (word >> 4) as u8 & 0b1111; let coproc = (word >> 8) as u8 & 0b1111; if coproc & 0b1110 == 0b1010 { // TODO: `UNDEFINED` return Err(DecodeError::InvalidOpcode); } let Rt = (word >> 12) as u8 & 0b1111; let Rt2 = (word >> 16) as u8 & 0b1111; if Rt == 15 || Rt2 == 15 || Rt == Rt2 { // TODO: actually `UNPREDICTABLE` return Err(DecodeError::InvalidOperand); } if (word >> 20) & 0b00001 != 0 { inst.opcode = Opcode::MRRC2(coproc, opc1); } else { inst.opcode = Opcode::MCRR2(coproc, opc1); } inst.operands = [ Operand::Reg(Reg::from_u8(Rt)), Operand::Reg(Reg::from_u8(Rt2)), Operand::CReg(CReg::from_u8(CRm)), Operand::Nothing, ]; } else { return Err(DecodeError::InvalidOpcode); } } else { // STC or LDC let pudw = (word >> 21) as u8 & 0b1111; let Rn = (word >> 16) as u8 & 0b1111; let CRd = (word >> 12) as u8 & 0b1111; let coproc = (word >> 8) as u8 & 0b1111; let imm8 = word & 0b11111111; if coproc & 0b1110 == 0b1010 { return Err(DecodeError::InvalidOpcode); } if (word >> 20) & 0b00001 == 0 { // op=110xxxx0, STC // page A8-663 if pudw & 0b0010 != 0 { inst.opcode = Opcode::STC2L(coproc); } else { inst.opcode = Opcode::STC2(coproc); } } else { // op=110xxxx1, LDC // page A8-393 if pudw & 0b0010 != 0 { inst.opcode = Opcode::LDC2L(coproc); } else { inst.opcode = Opcode::LDC2(coproc); } } let P = pudw & 0b1000 != 0; let U = pudw & 0b0100 != 0; let W = pudw & 0b0001 != 0; inst.operands = [ Operand::CReg(CReg::from_u8(CRd)), if P { Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, W) } else { if W { // preindex has no wback Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, false) } else { Operand::RegDeref(Reg::from_u8(Rn)) } }, if !P && !W { // TODO: not sure what ldc2{l}'s