From 72f9a2500b2f2b65e61a5d19b3606225084c896d Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 29 Nov 2020 20:40:45 -0800 Subject: add thumb/thumb2 decoding 16-bit instructions only, for now --- src/armv7.rs | 272 +++++ src/armv7/thumb.rs | 3103 ++++++++++++++++++++++++++++++++++++++++++++++ test/armv7.rs | 10 +- test/armv7/thumb.rs | 3396 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 6777 insertions(+), 4 deletions(-) create mode 100644 src/armv7/thumb.rs create mode 100644 test/armv7/thumb.rs diff --git a/src/armv7.rs b/src/armv7.rs index 007ded9..ac84ad9 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -9,6 +9,8 @@ use std::fmt::{self, Display, Formatter}; use yaxpeax_arch::{Arch, AddressDiff, Colorize, Decoder, LengthedInstruction, NoColors, ShowContextual, YaxColors}; +mod thumb; + pub struct ConditionedOpcode(pub Opcode, pub bool, pub ConditionCode); impl Display for ConditionedOpcode { @@ -45,6 +47,71 @@ fn reg_name_colorize>(reg: Reg, colors: &Y) -> impl > ShowContextual 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] { + return write!( + out, + "{} {}{}{}", + &self.opcode, + if aif & 0b100 != 0 { "a" } else { "" }, + if aif & 0b010 != 0 { "i" } else { "" }, + if aif & 0b001 != 0 { "f" } else { "" }, + ); + } 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? @@ -264,7 +331,13 @@ impl > Colorize fmt::Result { match self.0 { Opcode::Incomplete(_) | + 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 | @@ -274,6 +347,7 @@ impl > Colorize> Colorize { write!(out, "{}", colors.data_op(self)) }, + Opcode::HINT | + Opcode::NOP | + Opcode::ISB | + Opcode::DMB | + Opcode::DSB | + Opcode::CSDB | Opcode::SRS(_, _) | Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) }, + Opcode::DBG | + Opcode::CPS(_) | + 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::LDC2(_) | Opcode::LDC2L(_) | @@ -453,10 +569,12 @@ impl Display for Opcode { 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") }, @@ -496,6 +614,55 @@ impl Display for Opcode { Opcode::SMLAW(second) => { write!(f, "smlaw{}", if *second { "t" } else { "b" }) }, + 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::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") }, } } } @@ -606,6 +773,57 @@ pub enum Opcode { QDADD, QSUB, QADD, + + TBB, + TBH, + UDF, + SVC, + WFE, + WFI, + SEV, + CSDB, + YIELD, + HINT, + NOP, + LEAVEX, + ENTERX, + CLREX, + DSB, + DMB, + ISB, + SXTH, + UXTH, + SXTB16, + UXTB16, + SXTB, + UXTB, + SXTAH, + UXTAH, + SXTAB16, + UXTAB16, + SXTAB, + UXTAB, + CBZ, + CBNZ, + SETEND, + CPS(bool), + REV, + REV16, + REVSH, + IT, + + PKHTB, + PKHBT, + ORN, + SSAT, + SSAT16, + SBFX, + USAT, + USAT16, + UBFX, + BFI, + BFC, + DBG, } static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ @@ -635,6 +853,7 @@ pub struct RegShift { impl RegShift { fn into_shift(&self) -> RegShiftStyle { + // TODO: is this mask really off by one. should it be 0b10000?? if self.data & 0b1000 == 0 { RegShiftStyle::RegImm(RegImmShift { data: self.data }) } else { @@ -1416,6 +1635,7 @@ pub struct InstDecoder { mode: DecodeMode, version: ARMVersion, should_is_must: bool, + thumb: bool, } impl Default for InstDecoder { @@ -1424,16 +1644,31 @@ impl Default for InstDecoder { mode: DecodeMode::Any, version: ARMVersion::Any, should_is_must: true, + thumb: false, } } } impl InstDecoder { + pub fn set_thumb_mode(&mut self, thumb: bool) { + self.thumb = thumb; + } + + pub fn with_thumb_mode(mut self, thumb: bool) -> Self { + self.set_thumb_mode(thumb); + self + } + + pub fn default_thumb() -> Self { + Self::default().with_thumb_mode(true) + } + pub fn armv4() -> Self { Self { mode: DecodeMode::Any, version: ARMVersion::v4, should_is_must: true, + thumb: false, } } @@ -1442,6 +1677,7 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v5, should_is_must: true, + thumb: false, } } @@ -1450,6 +1686,7 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v6, should_is_must: true, + thumb: false, } } @@ -1458,6 +1695,16 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v6t2, should_is_must: true, + thumb: false, + } + } + + pub fn armv6t2_thumb() -> Self { + Self { + mode: DecodeMode::Any, + version: ARMVersion::v6t2, + should_is_must: true, + thumb: true, } } @@ -1466,6 +1713,16 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v7, should_is_must: true, + thumb: false, + } + } + + pub fn armv7_thumb() -> Self { + Self { + mode: DecodeMode::Any, + version: ARMVersion::v7, + should_is_must: true, + thumb: true, } } @@ -1474,6 +1731,16 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v7ve, should_is_must: true, + thumb: false, + } + } + + pub fn armv7ve_thumb() -> Self { + Self { + mode: DecodeMode::Any, + version: ARMVersion::v7ve, + should_is_must: true, + thumb: true, } } @@ -1482,6 +1749,7 @@ impl InstDecoder { mode: DecodeMode::Any, version: ARMVersion::v7vese, should_is_must: true, + thumb: false, } } } @@ -1491,6 +1759,10 @@ impl Decoder for InstDecoder { type Error = DecodeError; fn decode_into>(&self, inst: &mut Instruction, bytes: T) -> Result<(), Self::Error> { + if self.thumb { + return thumb::decode_into(&self, inst, bytes); + } + fn read_word>(bytes: T) -> Result { let mut iter = bytes.into_iter(); let instr: u32 = diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs new file mode 100644 index 0000000..d786305 --- /dev/null +++ b/src/armv7/thumb.rs @@ -0,0 +1,3103 @@ +use std::fmt; + +// use yaxpeax_arch::{Arch, AddressDiff, Decoder, LengthedInstruction}; + +use armv7::ConditionCode; +use armv7::DecodeError; +use armv7::Reg; +use armv7::RegShift; +use armv7::Operand; +use armv7::Opcode; +use armv7::Instruction; +use armv7::InstDecoder; +use armv7::StatusRegMask; + +#[allow(non_snake_case)] +fn ROR_C(x: u32, shift: u16) -> (u32, bool) { +// let m = shift % 32; // `shift` is known to be 31 or lower + let m = shift; + let result = (x >> m) | (x << (32 - m)); + let carry_out = (result >> 31) & 1 != 0; + (result, carry_out) +} + +#[allow(non_snake_case)] +fn ThumbExpandImm_C(imm: u16) -> u32 { + if imm & 0b1100_0000_0000 == 0 { + let ty = (imm >> 8) & 0b11; + let imm_low = (imm & 0b11111111) as u32; + match ty { + 0b00 => { + imm_low + } + 0b01 => { + if imm_low == 0 { + panic!("unpredictable"); + } + (imm_low << 16) | imm_low + } + 0b10 => { + if imm_low == 0 { + panic!("unpredictable"); + } + (imm_low << 24) | (imm_low << 8) + } + 0b11 => { + if imm_low == 0 { + panic!("unpredictable"); + } + (imm_low << 24) | (imm_low << 16) | (imm_low << 8) | imm_low + } + _ => { + unreachable!("impossible bit pattern"); + } + } + } else { + let unrotated_value = ((1 << 7) | (imm & 0b1111111)) as u32; + let rot = (imm >> 7) & 0b11111; + // TODO: figure out what to do with carry_out + let (imm32, _carry_out) = ROR_C(unrotated_value, rot); + imm32 + } +} + +#[allow(non_snake_case)] +fn DecodeImmShift(reg: u8, ty: u8, imm5: u8) -> RegShift { + let imm = match ty { + 0b00 => { imm5 }, + 0b01 => { + if imm5 == 0 { + 32 + } else { + imm5 + } + }, + 0b10 => { + if imm5 == 0 { + 32 + } else { + imm5 + } + }, + 0b11 => { + // ignores the `if ty == 11 && imm5 = 00000 then shift_t = RRX && shift_n = 1` + imm5 + } + _ => { + unreachable!("impossible bit pattern"); + } + }; + RegShift::from_raw( + 0b1000 | // `RegImm` + reg as u16 | + ((ty as u16) << 5)| + ((imm as u16) << 7) + ) +} + +#[allow(non_snake_case)] +pub fn decode_into>(_decoder: &InstDecoder, inst: &mut Instruction, bytes: T) -> Result<(), DecodeError> { + let mut iter = bytes.into_iter(); + let instr: u16 = + ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) ) | + ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8 ); + + let opword = instr >> 11; + + // `A6.1 Thumb instruction set encoding` + if opword >= 0b11101 { + let lower: u16 = + ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) ) | + ((iter.next().ok_or(DecodeError::ExhaustedInput)? as u16) << 8 ); + + let op2 = (instr >> 5) & 0b1111111; + + // 32b instruction - `A6-228, 32-bit Thumb instruction encoding` + // opword low bits 01, 10, and 11 correspond to `op1` in table `A6-9` + if opword < 0b11110 { + // op1 == 0b01 + if op2 < 0b1000000 { + // `op2` is `0b00.. ` or `0b01..` + if op2 < 0b0100000 { + // `Load/store`, either `multiple` or `dual` + let rn = (instr & 0b1111) as u8; + // TODO: double check + if op2 & 0b0000100 != 0 { + // `Load/store dual, load/store exclusive, table branch` (`A6-236`) + let op1op2 = ((instr >> 7) & 0b11) | ((instr >> 4) & 0b11); + let rn = (instr & 0b1111) as u8; + let imm8 = lower & 0b11111111; + let rd = ((lower >> 8) & 0b1111) as u8; + let rt = ((lower >> 12) & 0b1111) as u8; + + match op1op2 { + 0b0000 => { + // `STREX` (`A8-691`) + // v6T2 + if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + if rd == rn || rd == rt { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STREX; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rt)), + // preindex for `[, #]`, no writeback. imm is zero + // extended, so not signed; always add. + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8 << 2, + true, + false, + ), + Operand::Nothing, + ]; + } + 0b0001 => { + // `LDREX` (`A8-433`) + // v6T2 + if rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rd == 0b1111 + inst.opcode = Opcode::LDREX; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + // preindex for `[, #]`, no writeback. imm is zero + // extended, so not signed; always add. + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8 << 2, + true, + false, + ), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0010 | + 0b0110 => { + // `STRD (immediate)` (`A8-687`) + // v6T2 + // bit 5 (w) == 0 + let w = false; + let u = (instr >> 7) & 1 != 0; + let p = (instr >> 8) & 1 != 0; + // `if P == '0' && W == '0' then SEE "Related encodings"` -> this + // would imply tbb/tbh, should be unreachable + if rn == 15 || rt == 13 || rt == 15 || rd == 13 || rd == 15 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, + Operand::Nothing, + ]; + } + 0b0011 | + 0b0111 => { + // `LDRD (immediate)`/`(literal)` (`A8-687`) + // bit 5 (w) == 0 + let w = false; + let u = (instr >> 7) & 1 != 0; + let p = (instr >> 8) & 1 != 0; + // `if P == '0' && W == '0' then SEE "Related encodings"` -> this + // would imply tbb/tbh, should be unreachable + if rt == 13 || rt == 15 || rd == 13 || rd == 15 || rd == rt { + todo!("UNPREDICTABLE"); + } + if w && (rn == rt || rn == rd) { + todo!("UNPREDICTABLE"); + } + if rn != 0b1111 { + // `LDRD (immediate)` (`A8-427`) + // v6T2 + inst.opcode = Opcode::LDRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, + Operand::Nothing, + ]; + } else { + // `LDRD (literal)` (`A8-429`) + // v6T2 + if w { + todo!("UNPREDICTABLE"); + } + // which because !(p == 0 && w == 0), we know p is true + inst.opcode = Opcode::LDRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), + Operand::Nothing, + ]; + } + } + 0b0100 => { + // `STREX_` + // v7 + let op3 = (lower >> 4) & 0b1111; + let rt2 = rd; + let rd = (imm8 & 0b1111) as u8; + match op3 { + 0b0100 => { + // `STREXB` (`A8-693`) + if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + if rd == rn || rd == rt { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rt2 == 0b1111 + inst.opcode = Opcode::STREXB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDeref(Reg::from_u8(rn)), + Operand::Nothing, + ]; + } + 0b0101 => { + // `STREXH` (`A8-693`) + if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + if rd == rn || rd == rt { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rt2 == 0b1111 + inst.opcode = Opcode::STREXH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDeref(Reg::from_u8(rn)), + Operand::Nothing, + ]; + } + 0b0111 => { + // `STREXD` (`A8-693`) + if rd == 13 || rd == 15 || rt == 13 || rt == 15 || rt2 == 13 || rt2 == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + if rd == rn || rd == rt || rd == rt2 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STREXD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rt2)), + Operand::RegDeref(Reg::from_u8(rn)), + ]; + } + _ => { + return Err(DecodeError::Undefined); + } + } + } + 0b0101 => { + // `TBB`/`TBH`/`LDREX_` + let op3 = (lower >> 4) & 0b1111; + let rt2 = rd; + let rd = (imm8 & 0b1111) as u8; + match op3 { + 0b0000 => { + // `TBB` + // TODO: should_is_must() + // rt == 0b1111 + // rd == 0b0000 + inst.opcode = Opcode::TBB; + inst.operands = [ + Operand::RegDerefPreindexReg( + Reg::from_u8(rn), + Reg::from_u8(rd), + true, // add + false, // no wback + ), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0001 => { + // `TBH` + // TODO: should_is_must() + // rt == 0b1111 + // rd == 0b0000 + inst.opcode = Opcode::TBB; + inst.operands = [ + Operand::RegDerefPreindexRegShift( + Reg::from_u8(rn), + // want `, LSL #1`, construct a raw shift + // ourselves + RegShift::from_raw( + 0b1000 | // `RegImm` + rd as u16 | // reg == rd + (0b00 << 5) | // LSL + (1 << 7) // shift == #1 + ), + true, // add + false, // no wback + ), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0100 => { + // `LDREXB` + if rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rt2 == 0b1111 + // rd == 0b1111 + inst.opcode = Opcode::LDREXB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDeref(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0101 => { + // `LDREXH` + if rt == 13 || rt == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rt2 == 0b1111 + // rd == 0b1111 + inst.opcode = Opcode::LDREXH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDeref(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0110 => { + // `LDREXD` + if rt == 13 || rt == 15 || rt2 == 13 || rt2 == 15 || rn == 15 { + todo!("UNPREDICTABLE"); + } + // TODO: should_is_must() + // rd == 0b1111 + inst.opcode = Opcode::LDREXD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rt2)), + Operand::RegDeref(Reg::from_u8(rn)), + Operand::Nothing, + ]; + } + _ => { + return Err(DecodeError::Undefined); + } + } + } + 0b1000 | + 0b1010 | + 0b1100 | + 0b1110 => { + // `STRD (immediate)` (`A8-687`) + // v6T2 + // bit 5 (w) == 1 + let w = true; + let u = (instr >> 7) & 1 != 0; + let p = (instr >> 8) & 1 != 0; + // `if P == '0' && W == '0' then SEE "Related encodings"` -> this + // would imply tbb/tbh, should be unreachable + if rn == 15 || rt == 13 || rt == 15 || rd == 13 || rd == 15 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, + Operand::Nothing, + ]; + } + 0b1001 | + 0b1011 | + 0b1101 | + 0b1111 => { + // `LDRD (immediate)` (`A8-687`) + // v6T2 + // bit 5 (w) == 1 + let w = true; + let u = (instr >> 7) & 1 != 0; + let p = (instr >> 8) & 1 != 0; + // `if P == '0' && W == '0' then SEE "Related encodings"` -> this + // would imply tbb/tbh, should be unreachable + if rt == 13 || rt == 15 || rd == 13 || rd == 15 || rd == rt { + todo!("UNPREDICTABLE"); + } + if w && (rn == rt || rn == rd) { + todo!("UNPREDICTABLE"); + } + if rn != 0b1111 { + // `LDRD (immediate)` (`A8-427`) + // v6T2 + inst.opcode = Opcode::LDRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, + Operand::Nothing, + ]; + } else { + // `LDRD (literal)` (`A8-429`) + // v6T2 + if w { + todo!("UNPREDICTABLE"); + } + // which because !(p == 0 && w == 0), we know p is true + inst.opcode = Opcode::LDRD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::Reg(Reg::from_u8(rd)), + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), + Operand::Nothing, + ]; + } + } + _ => { + unreachable!("impossible bit pattern"); + } + } + } else { + let w = instr & 0b100000 != 0; + // `Load/store multiple` (`A6-235`) + if instr & 0b10000 == 0 { + // `L == 0` + match (instr >> 7) & 0b11 { + 0b00 => { + // `SRS (Thumb)` (`B9-1990`) + // v6T2 + inst.opcode = Opcode::SRS(false, true); // `srsdb` + inst.operands = [ + Operand::RegWBack(Reg::from_u8(13), w), + Operand::Imm12(lower & 0b1111), // # ? what's the syntax here? #? + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b01 => { + // `STM (STMIA, STMEA)` (`A8-665`) + // v6T2 + if rn == 15 || lower.count_ones() < 2 { + todo!("UNPREDICTABLE"); + } + if w && (lower & (1 << rn)) != 0 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STM( + true, // add + true, // preincrement + w, // wback + true, // usermode + ); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b10 => { + // `STMDB`/`STMFD` (`A8-669`) + // or `PUSH` (`A8-539`) + if w && rn == 0b1101 { + // `PUSH` + // v6T2 + if lower.count_ones() < 2 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::PUSH; + inst.operands = [ + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `STMDB` + // v6T2 + if rn == 15 || lower.count_ones() < 2 { + todo!("UNPREDICTABLE"); + } + if w && (lower & (1 << rn)) != 0 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::STM( + false, // decrement + true, // preincrement + w, // wback + true, // usermode? + ); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b11 => { + // `SRS (Thumb)` (`B9-1990`) + // v6T2 + inst.opcode = Opcode::SRS(false, true); // `srsia` + inst.operands = [ + Operand::RegWBack(Reg::from_u8(13), w), + Operand::Imm12(lower & 0b1111), // # ? what's the syntax here? #? + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + unreachable!(); + } + } + } else { + // `L == 1` + match (instr >> 7) & 0b11 { + 0b00 => { + // `RFE` (`B9-1986`) + // v6T2 + if rn == 15 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::RFE(false, true); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b01 => { + // `LDM/LDMIA/LDMFD (Thumb)` (`A8-397`) + if w && rn == 0b1101 { + // `POP` (`A8-535`) + if lower.count_ones() < 2 || (lower & 0xc000) == 0xc000 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::POP; + inst.operands = [ + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `LDM/LDMIA/LDMFD` + if rn == 15 || lower.count_ones() < 2 { + todo!("UNPREDICTABLE"); + } + if w && (lower & (1 << rn)) != 0 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::LDM(true, true, w, true); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b10 => { + // `LDMDB/LDMEA` (`A8-403`) + if rn == 15 || lower.count_ones() < 2 { + todo!("UNPREDICTABLE"); + } + if w && (lower & (1 << rn)) != 0 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::LDM(false, true, w, true); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::RegList(lower), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b11 => { + // `RFE` (`B9-1986`) + // v6T2 + if rn == 15 { + todo!("UNPREDICTABLE"); + } + inst.opcode = Opcode::RFE(true, false); + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + unreachable!(); + } + } + } + } + } else { + // `Data-processing (shfited register)` (`A6-241`) + // v6T2 + let op = op2 & 0b1111; + let s = instr & 0b10000 != 0; + let rn = (instr & 0b1111) as u8; + let rd = ((lower >> 8) & 0b1111) as u8; + + let imm3 = (lower >> 12) & 0b111; + let imm2 = (lower >> 6) & 0b11; + let tp = (lower >> 4) & 0b11; + let rm = (lower & 0b1111) as u8; + + let shift = RegShift::from_raw( + 0b1000 | // reg-imm shift. TODO: probably need to change the const + rm as u16 | + (imm2 << 7) | (imm3 << 9) | + tp << 5 + ); + let shift = Operand::RegShift(shift); + + match op { + 0b0000 => { + if rd == 0b1111 && s { + // `TST` (`A8-747`) + // v6T2 + inst.opcode = Opcode::TST; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `AND` (`A8-324`) + // v6T2 + // TODO: S + inst.opcode = Opcode::AND; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b0001 => { + // `BIC` (`A8-340`) + // v6T2 + // TODO: S + inst.opcode = Opcode::BIC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + 0b0010 => { + if rn == 0b1111 { + // `Move register and immediate shifts`, also `A6-241` + let tp = (lower >> 4) & 0b11; + let imm2 = (lower >> 6) & 0b11; + let imm3 = (lower >> 12) & 0b111; + match tp { + 0b00 => { + if imm2 | imm3 == 0 { + // `MOV (register, Thumb)` (`A8-487`) + return Err(DecodeError::Incomplete); + } else { + // `LSL (immediate)` (`A8-469`) + return Err(DecodeError::Incomplete); + } + }, + 0b01 => { + // `LSR (immediate)` (`A8-473`) + return Err(DecodeError::Incomplete); + } + 0b10 => { + // `ASR (immediate)` (`A8-328`) + return Err(DecodeError::Incomplete); + } + 0b11 => { + if imm2 | imm3 == 0 { + // `RRX` (`A8-573`) + return Err(DecodeError::Incomplete); + } else { + // `ROR (immediate)` (`A8-569`) + return Err(DecodeError::Incomplete); + } + } + _ => { + unreachable!("impossible bit pattern for `tp`"); + } + } + } else { + // `ORR` (`A8-519`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ORR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b0011 => { + if rn == 0b1111 { + // `MVN` (`A8-507`) + // v6T2 + // TODO: S + inst.opcode = Opcode::MVN; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + shift, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `ORN` (`A8-515`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ORN; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b0100 => { + if rd == 0b1111 && s { + // `TEQ` (`A8-741`) + // v6T2 + inst.opcode = Opcode::TEQ; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `EOR` (`A8-385`) + // v6T2 + // TODO: S + inst.opcode = Opcode::EOR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b0110 => { + // `PKH` (`A8-523`) + // v6T2 + // TODO: fix shift + // TODO: check opcode + // TODO: S + inst.opcode = if lower & 0b10000 != 0 { + Opcode::PKHTB + } else { + Opcode::PKHBT + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + 0b1000 => { + if rd == 0b1111 && s { + // `CMN` (`A8-364`) + // v6T2 + inst.opcode = Opcode::CMN; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `ADD` (`A8-308`) + // TODO: S + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b1010 => { + // `ADC` (`A8-300`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ADC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + 0b1011 => { + // `SBC` (`A8-595`) + // v6T2 + // TODO: S + inst.opcode = Opcode::SBC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + 0b1101 => { + if rd == 0b1111 && s { + // `CMP` (`A8-370`) + // v6T2 + inst.opcode = Opcode::CMP; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `SUB` (`A8-713`) + // v6T2 + // TODO: S + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + } + 0b1110 => { + // `RSB` (`A8-577`) + inst.opcode = Opcode::RSB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + shift, + Operand::Nothing, + ]; + } + _ => { + // undefined encoding + return Err(DecodeError::Undefined); + } + } + } + } else { + // `Coprocessor, Advanced SIMD, and Floating-point instructions` (`A6-249`) + // v6T2 + eprintln!("TODO: coproc/simd/fp"); + return Err(DecodeError::Incomplete); + } + } else if opword < 0b11111 { + // op1 == 0b10 + if lower & 0x80 == 0 { + // op == 0 + if op2 & 0b0100000 == 0 { + // `A6.3.1` `Data-processing (modified immediate)` (`A6-229`) + // see `A6.3.2` for `Modified immediate constants in Thumb instructions` on how + // to decode immediates + // v6T2 + let op = op2 & 0b1111; + let i = instr >> 10 & 1; + let s = instr & 0b10000 != 0; + let rn = (instr & 0b1111) as u8; + let imm3 = (lower >> 12) & 0b111; + let rd = ((lower >> 8) & 0b1111) as u8; + let imm8 = lower & 0b11111111; + let imm = (i << 11) | (imm3 << 8) | imm8; + + let imm = ThumbExpandImm_C(imm); + + match op { + 0b0000 => { + if rd == 0b1111 && s { + // `TST` (`A8-745`) + // v6T2 + inst.opcode = Opcode::TST; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `AND` (`A8-322`) + // v6T2 + // TODO: S + inst.opcode = Opcode::AND; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b0001 => { + // `BIC` (`A8-338`) + // v6T2 + // TODO: S + inst.opcode = Opcode::BIC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + 0b0010 => { + if rn == 0b1111 { + // `MOV` (`A8-485`) + // v6T2 + // TODO: S + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `ORR` (`A8-517`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ORR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b0011 => { + if rn == 0b1111 { + // `MVN` (`A8-505`) + // v6T2 + // TODO: S + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `ORN` (`A8-513`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ORN; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b0100 => { + if rd == 0b1111 && s { + // `TEQ` (`A8-739`) + // v6T2 + // TODO: S + inst.opcode = Opcode::TEQ; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `EOR` (`A8-383`) + // v6T2 + // TODO: S + inst.opcode = Opcode::EOR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b1000 => { + if rd == 0b1111 && s { + // `CMN` (`A8-362`) + // v6T2 + // TODO: S + inst.opcode = Opcode::CMN; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `ADD` (`A8-304`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b1010 => { + // `ADC` (`A8-298`) + // v6T2 + // TODO: S + inst.opcode = Opcode::ADC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + 0b1011 => { + // `SBC` (`A8-593`) + // v6T2 + // TODO: S + inst.opcode = Opcode::SBC; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + 0b1101 => { + if rd == 0b1111 && s { + // `CMP` (`A8-368`) + // v6T2 + // TODO: S + inst.opcode = Opcode::CMP; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `SUB` (`A8-709`) + // v6T2 + // TODO: S + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + } + 0b1110 => { + // `RSB` (`A8-575`) + // v6T2 + // TODO: S + inst.opcode = Opcode::RSB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } + _ => { + // undefined encoding + return Err(DecodeError::Undefined); + } + } + } else { + // `Data-processing (plain binary immediate)` (`A6-232`) + // v6T2 + let op = (instr >> 4) & 0b11111; + let i = instr >> 10 & 1; + let s = instr & 0b10000 != 0; + inst.s = s; + let rn = (instr & 0b1111) as u8; + let imm3 = (lower >> 12) & 0b111; + let rd = ((lower >> 8) & 0b1111) as u8; + let imm8 = lower & 0b11111111; + let imm = (i << 11) | (imm3 << 8) | imm8; + + match op { + 0b00000 => { + if rn != 0b1111 { + // `ADD` (`A8-304`) + // v6T2 + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } else { + // `ADR` (`A8-320`) + // v6T2 + // TODO: add = TRUE; + inst.opcode = Opcode::ADR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b00100 => { + // `MOV` (`A8-485`) + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32 | ((rn as u32) << 16)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b01010 => { + if rn != 0b1111 { + // `SUB` (`A8-709`) + // v6T2 + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(imm as u32), + Operand::Nothing, + ]; + } else { + // `ADR` (`A8-320`) + // v6T2 + // TODO: add = FALSE; + inst.opcode = Opcode::ADR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b01100 => { + // `MOVT` (`A8-492`) + // v6T2 + inst.opcode = Opcode::MOVT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm as u32 | ((rn as u32) << 16)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b10000 => { + // `SSAT` (`A8-653`) + // v6T2 + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + let sh = 0; // from the opcode + let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); + inst.opcode = Opcode::SSAT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b11111) as u32), + Operand::RegShift(shift), + Operand::Nothing, + ]; + } + 0b10010 => { + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + if imm3_2 != 0 { + let shift = DecodeImmShift(rn, ((instr >> 4) & 0b10) as u8, imm3_2 as u8); + // `SSAT` + // v6T2 + inst.opcode = Opcode::SSAT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b11111) as u32), + Operand::RegShift(shift), + Operand::Nothing, + ]; + } else { + // `SSAT16` + // v6T2 + inst.opcode = Opcode::SSAT16; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b11111) as u32), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + ]; + } + } + 0b10100 => { + // `SBFX` (`A8-599`) + // v6T2 + inst.opcode = Opcode::SBFX; + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm12(imm3_2), + Operand::Imm12((lower & 0b11111) + 1), + ]; + } + 0b10110 => { + if rn != 0b1111 { + // `BFI` (`A8-336`) + // v6T2 + inst.opcode = Opcode::BFI; + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm12(imm3_2), + // TODO: this is `msb` but the operand here should be `width` + Operand::Imm12(lower & 0b11111), + ]; + } else { + // `BFC` (`A8-334`) + // v6T2 + inst.opcode = Opcode::BFC; + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm12(imm3_2), + // TODO: this is `msb` but the operand here should be `width` + Operand::Imm32((lower & 0b1111) as u32), + ]; + } + } + 0b11000 => { + // `USAT` (`A8-797`) + // v6T2 + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + let sh = 0; // from the opcode + let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); + inst.opcode = Opcode::USAT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b1111) as u32), + Operand::RegShift(shift), + Operand::Nothing, + ]; + } + 0b11010 => { + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + if imm3_2 != 0 { + let sh = 1; // from the opcode + let shift = DecodeImmShift(rn, sh << 1, imm3_2 as u8); + // `USAT` + // v6T2 + inst.opcode = Opcode::USAT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b1111) as u32), + Operand::RegShift(shift), + Operand::Nothing, + ]; + } else { + // `USAT16` + // v6T2 + inst.opcode = Opcode::USAT16; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32((lower & 0b1111) as u32), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + ]; + } + } + 0b11100 => { + // `UBFX` (`A8-757`) + // v6T2 + inst.opcode = Opcode::UBFX; + let imm3_2 = ((lower >> 10) & 0b11100) | ((lower >> 6) & 0b11); + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm12(imm3_2), + Operand::Imm12((lower & 0b11111) + 1), + ]; + } + _ => { + return Err(DecodeError::Undefined); + } + } + } + } else { + // op == 1 + // `Branches and miscellaneous control` (`A6-233`) + let imm8 = lower & 0b11111111; + let op2 = (lower >> 8) & 0b0111; + let op1 = (lower >> 12) & 0b111; + let op = (instr >> 4) & 0b1111111; + if op1 & 0b101 == 0b000 { + // the high bit of op is a sign bit, if a conditional branch. otherwise, it is + // 0 for valid instructiosn other than `udf`, `hvc`, and `smc`. `Branch` is + // ruled out as `op1` is `0x1`, so see if this is any of the misc + // instructions: + if op & 0b0111000 != 0b0111000 { + // `Conditional branch` (`A8-332`) + // v6T2 + inst.condition = ConditionCode::build(((instr >> 8) & 0b1111) as u8); + inst.opcode = Opcode::B; + inst.operands = [ + Operand::BranchThumbOffset(((instr & 0b11111111) + 1) as i8 as i32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // some misc instruction, rule out `udf`, `hvc`, `smc`: + if op < 0b1000000 { + // misc instruction + if op < 0b0111010 { + // `MSR` in some form, slightly more work to figure this out + let rn = (instr & 0b1111) as u8; + if imm8 & 0b00100000 != 0 { + // `MSR` (`B9-1980`) + // v7VE + let sysm = (((lower >> 4) & 1) << 4) | ((lower >> 8) & 0b1111); + let r = (instr >> 4) & 1; + inst.opcode = Opcode::MSR; + inst.operands = [ + // TODO: is this the appropriate banked reg? + Reg::from_sysm(r != 0, sysm as u8).expect("from_sysm works"), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + if op == 0b0111000 { + if op2 & 0b0011 == 0b00 { + // `Move to Special register, Application level` (`A8-501`) + let mask = (lower >> 10) & 0b11; + let spec_reg = match mask { + 0b00 => { + todo!("UNPREDICTABLE"); + } + 0b01 => { + StatusRegMask::APSR_G + } + 0b10 => { + StatusRegMask::APSR_NZCVQ + } + 0b11 => { + StatusRegMask::APSR_NZCVQG + } + _ => { + unreachable!("impossible mask bits"); + } + }; + inst.opcode = Opcode::MSR; + inst.operands = [ + Operand::StatusRegMask(spec_reg), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `Move to Special register, System level` (`B9-1984`) + let mask = ((lower >> 8) & 0b1111) as u8; + let r = (instr >> 4) & 1; + inst.opcode = Opcode::MSR; + inst.operands = [ + // TODO: is this the appropriate? + Reg::from_sysm(r != 0, mask).expect("from_sysm works"), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } + } else { + // `Move to Special register, System level` (`B9-1984`) + let mask = ((lower >> 8) & 0b1111) as u8; + let r = (instr >> 4) & 1; + inst.opcode = Opcode::MSR; + inst.operands = [ + // TODO: is this the appropriate? + Reg::from_sysm(r != 0, mask).expect("from_sysm works"), + Operand::Reg(Reg::from_u8(rn)), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + } else if op < 0b0111011 { + // `Change Processor State, and hints` (`A6-234`) + let op1 = (lower >> 8) & 0b111; + let op2 = lower & 0b11111111; + if op1 != 0b000 { + // `CPS (Thumb)` (`B9-1964`) + // v6T2 + let _mode = lower & 0b11111; + let _aif = (lower >> 5) & 0b111; + let _m = (lower >> 8) & 1; + let _imod = (lower >> 9) & 0b11; + // TODO: no CPS instruction? yet? + eprintln!("cps support is not complete"); + return Err(DecodeError::Incomplete); + // inst.opcode = Opcode::CPS; + } else { + if op2 >= 0b11110000 { + // `DBG` (`A8-378`) + let option = lower & 0b1111; + inst.opcode = Opcode::DBG; + inst.operands = [ + Operand::Imm12(option), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + match op2 { + 0b00000000 => { + // `NOP` (`A8-511`) + // v6T2 + // TODO: should_is_must + inst.opcode = Opcode::NOP; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b00000001 => { + // `YIELD` (`A8-1109`) + // v7 + inst.opcode = Opcode::YIELD; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b00000010 => { + // `WFE` (`A8-1105`) + // v7 + inst.opcode = Opcode::WFE; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b00000011 => { + // `WFI` (`A8-1107`) + // v7 + inst.opcode = Opcode::WFI; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b00000100 => { + // `SEV` (`A8-607`) + // v7 + inst.opcode = Opcode::SEV; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b00010100 => { + // `CSDB` (`A8-376`) + // v6T2 + inst.opcode = Opcode::CSDB; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + return Err(DecodeError::Undefined); + } + } + } + } + } else if op < 0b0111100 { + // `Miscellaneous control instructions` (`A6-235`) + let op = (lower >> 4) & 0b1111; + match op { + 0b0000 => { + // `ENTERX` (`A9-1116`) + // ThumbEE + inst.opcode = Opcode::LEAVEX; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0001 => { + // `ENTERX` (`A9-1116`) + // ThumbEE + inst.opcode = Opcode::ENTERX; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0010 => { + // `CLREX` (`A8-358`) + // v7 + inst.opcode = Opcode::CLREX; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0100 => { + // `DSB` (`A8-381`) + // v7 + let option = lower & 0b1111; + inst.opcode = Opcode::DSB; + inst.operands = [ + Operand::Imm12(option), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0101 => { + // `DMB` (`A8-379`) + // v7 + let option = lower & 0b1111; + inst.opcode = Opcode::DMB; + inst.operands = [ + Operand::Imm12(option), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0110 => { + // `ISB` (`A8-390`) + // v7 + let option = lower & 0b1111; + inst.opcode = Opcode::ISB; + inst.operands = [ + Operand::Imm12(option), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + }, + _ => { + return Err(DecodeError::Undefined); + } + } + } else if op < 0b0111101 { + // `BXJ` (`A8-352`) + // v6T2 + let rm = (instr & 0b1111) as u8; + inst.opcode = Opcode::BXJ; + inst.operands = [ + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if op < 0b0111110 { + // `ERET` or `SUBS PC, LR` + // v6T2 + // `v7VE` defines `ERET` here, identical to `subs pc, lr` with + // `imm8 == 0`. `v7VE` does not change the behavior of this + // instruction at `PL1`. + let imm8 = lower & 0b11111111; + if imm8 == 0 { + // `ERET` (`B9-1968`) + // v6T2 + // if > 8) & 0b1111) as u8; + inst.opcode = Opcode::MRS; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Reg::from_sysm(r != 0, sysm as u8).expect("from_sysm works"), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + if op == 0b0111110 { + // `MRS` (`A8-497`) + // v6T2 + inst.opcode = Opcode::MRS; + let rd = ((lower >> 8) & 0b1111) as u8; + inst.opcode = Opcode::MRS; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + // TODO: ""? + Reg::from_sysm(false, 0).expect("from_sysm works"), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `MRS` (`B9-1976`) + // v6T2 + inst.opcode = Opcode::MRS; + let rd = ((lower >> 8) & 0b1111) as u8; + let r = (instr >> 4) & 1; + inst.opcode = Opcode::MRS; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + // TODO: ""? + Reg::from_sysm(r != 0, 0).expect("from_sysm works"), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + } + } else { + if op == 0b1111111 { + if op1 == 0b000 { + // `SMC` (aka `SMI`) (`B9-1988`) + // "Security Extensions" + let imm = instr & 0b1111; + inst.opcode = Opcode::SMC; + inst.operands = [ + Operand::Imm12(imm), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `UDF` (`A8-759`) + // All (first defined in issue `C.a`) + // TODO: should this decode to an intentional `UDF` + // instruction? + return Err(DecodeError::Undefined); + } + } else if op == 0b1111110 { + if op1 == 0b000 { + // `HVC` (`B8-1970`) + // v7VE + let imm = lower & 0b1111_1111_1111; + inst.opcode = Opcode::HVC; + inst.operands = [ + Operand::Imm12(imm), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // undefined, but by not being mentioned in the manual + return Err(DecodeError::Undefined); + } + } else { + // undefined, but by not being mentioned in the manual + return Err(DecodeError::Undefined); + } + } + } + } else if op1 & 0b101 == 0b001 { + // `Branch` (`A8-332`) + // v6T2 + // + let imm11 = lower & 0b111_1111_1111; + let imm6 = instr & 0b11_1111; + let j1 = (lower >> 13) & 1; + let j2 = (lower >> 11) & 1; + let s = (instr >> 10) & 1; + let imm = + (imm11 as u32) | + ((imm6 as u32) << 11) | + ((j1 as u32) << 17) | + ((j2 as u32) << 18) | + ((s as u32) << 19); + let imm = (imm << 12) >> 12; + inst.opcode = Opcode::B; + inst.operands = [ + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if op1 & 0b101 == 0b100 { + // `Branch with Link and Exchange` (`A8-346`) + // `UNDEFINED` in v4T + // v5T + let imm11 = lower & 0b111_1111_1111; + let imm10 = instr & 0b11_1111_1111; + let j1 = (lower >> 13) & 1; + let j2 = (lower >> 11) & 1; + let s = (instr >> 10) & 1; + let imm = + (imm11 as u32) | + ((imm10 as u32) << 11) | + ((j1 as u32) << 21) | + ((j2 as u32) << 22) | + ((s as u32) << 23); + let imm = (imm << 8) >> 8; + inst.opcode = Opcode::BLX; + inst.operands = [ + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `Brach with Link` (`A8-346`) + // v4T + let imm11 = lower & 0b111_1111_1111; + let imm10 = instr & 0b11_1111_1111; + let j1 = (lower >> 13) & 1; + let j2 = (lower >> 11) & 1; + let s = (instr >> 10) & 1; + let imm = + (imm11 as u32) | + ((imm10 as u32) << 11) | + ((j1 as u32) << 21) | + ((j2 as u32) << 22) | + ((s as u32) << 23); + let imm = (imm << 8) >> 8; + inst.opcode = Opcode::BL; + inst.operands = [ + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + } else { + // op1 == 0b11 + if op2 & 0b1000000 == 0 { + if op2 & 0b0100000 == 0 { + // loads, stores + if op2 & 0b0000001 == 0 { + // store single item, or advanced simd load/store + if op2 & 0b00100000 == 0 { + // `Store single data item` (`A6-240`) + let rn = (instr & 0b1111) as u8; + let op1 = (instr >> 5) & 0b111; + let size_bits = op1 & 0b011; + let op2 = (lower >> 6) & 0b111111; + match size_bits { + 0b00 => { + // `STRB_` + if op2 == 0 { + // `STRB (register)` (`A8-683`) + // encoding T2 + // v6T2 + let rm = (lower & 0b1111) as u8; + let imm2 = (lower >> 4) & 0b11; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexRegShift( + Reg::from_u8(rn), + RegShift::from_raw( + // do things + 0b0000 | // imm shift + (imm2 << 7) | // imm + rm as u16 | // shiftee + (0b00 << 5) // shift style (lsl) + ), + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else if (op2 & 0b111100) == 0b111000 { + // `STRBT` (`A8-685`) + // v6T2 + let imm8 = lower & 0b1111_1111; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRBT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `STRB (immediate, Thumb)` (`A8-679`) + // encoding T3 + // v6T2 + let imm8 = lower & 0b1111_1111; + let puw = (lower >> 8) & 0b111; + let p = puw & 0b100 != 0; + let u = puw & 0b010 != 0; + let w = puw & 0b001 != 0; + // assert!(puw != 0b110) // would be `strbt` + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + // do the puw + if p { + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + } else { + Operand::RegDerefPostindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + }, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b01 => { + // `STRH_` + // v6T2 + if op2 == 0 { + // `STRH (register)` (`A8-703`) + let rm = (lower & 0b1111) as u8; + let imm2 = (lower >> 4) & 0b11; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexRegShift( + Reg::from_u8(rn), + RegShift::from_raw( + // do things + 0b0000 | // imm shift + (imm2 << 7) | // imm + rm as u16 | // shiftee + (0b00 << 5) // shift style (lsl) + ), + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else if (op2 & 0b111100) == 0b111000 { + // `STRHT` (`A8-705`) + let imm8 = lower & 0b1111_1111; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRHT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `STRH (immediate, Thumb)` (`A8-699`) + // encoding T3 + // v6T2 + let imm8 = lower & 0b1111_1111; + let puw = (lower >> 8) & 0b111; + let p = puw & 0b100 != 0; + let u = puw & 0b010 != 0; + let w = puw & 0b001 != 0; + // assert!(puw != 0b110) // would be `strbt` + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + // do the puw + if p { + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + } else { + Operand::RegDerefPostindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + }, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b10 => { + // `STR_` + if op2 == 0 { + // `STR (register)` (`A8-677`) + // v6T2 + let rm = (lower & 0b1111) as u8; + let imm2 = (lower >> 4) & 0b11; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexRegShift( + Reg::from_u8(rn), + RegShift::from_raw( + // do things + 0b0000 | // imm shift + (imm2 << 7) | // imm + rm as u16 | // shiftee + (0b00 << 5) // shift style (lsl) + ), + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else if (op2 & 0b111100) == 0b111000 { + // `STRT` (`A8-707`) + let imm8 = lower & 0b1111_1111; + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STRT; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `STR (immediate, Thumb)` (`A8-673`) + // encoding T4 + // v6T2 + let imm8 = lower & 0b1111_1111; + let puw = (lower >> 8) & 0b111; + let p = puw & 0b100 != 0; + let u = puw & 0b010 != 0; + let w = puw & 0b001 != 0; + // assert!(puw != 0b110) // would be `strbt` + let rt = ((lower >> 12) & 0b1111) as u8; + inst.opcode = Opcode::STR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + // do the puw + if p { + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + } else { + Operand::RegDerefPostindexOffset( + Reg::from_u8(rn), + imm8, + u, // add + w, // wback + ) + }, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + 0b11 => { + return Err(DecodeError::Undefined); + } + _ => { + unreachable!("impossible bit pattern"); + } + } + } else { + // `Advanced SIMD element or structure load/store instructions` + // (`A7-273`) + } + } else { + // load {byte, halfword, word} + let size_bits = (op2 >> 1) & 0b11; + if size_bits == 0b00 { + // `Load byte, memory hints` (`A6-239`) + } else if size_bits == 0b01 { + // `Load halfword, memory hints` (`A6-238`) + } else if size_bits == 0b10 { + // `Load word` (`A6-237`) + } else { + // `UNDEFINED` + return Err(DecodeError::Undefined); + } + } + } else { + if op2 & 0b0010000 == 0 { + // `Data-processing (register)` (`A6-243`) + let op1 = (instr >> 4) & 0b1111; + let op2 = (lower >> 4) & 0b1111; + let rn = (instr & 0b1111) as u8; + if op1 < 0b1000 { + // `LSL`, `LSR`, `ASR`, `ROR`, `SXTAH`, .... out of table `A6-24` + if op2 < 0b1000 { + // `LSL`, `LSR`, `ASR`, `ROR` + // v6T2 + let op = [ + Opcode::LSL, + Opcode::LSR, + Opcode::ASR, + Opcode::ROR, + ][((op2 >> 1) & 0b11) as usize]; + let rd = ((lower >> 8) & 0b1111) as u8; + let rm = (lower & 0b1111) as u8; + inst.opcode = op; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + ]; + } else { + // `SXTAH` and friends + if op1 > 0b101 { + return Err(DecodeError::Undefined); + } + + if rn == 0b1111 { + let op = [ + Opcode::SXTH, + Opcode::UXTH, + Opcode::SXTB16, + Opcode::UXTB16, + Opcode::SXTB, + Opcode::UXTB, + ][(op1 & 0b111) as usize]; + + let rm = (lower & 0b1111) as u8; + let rotate = (lower >> 1) & 0b11000; + let rd = ((lower >> 8) & 0b1111) as u8; + + inst.opcode = op; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm32(rotate as u32), + Operand::Nothing, + ]; + } else { + let op = [ + Opcode::SXTAH, + Opcode::UXTAH, + Opcode::SXTAB16, + Opcode::UXTAB16, + Opcode::SXTAH, + Opcode::UXTAB, + ][(op1 & 0b111) as usize]; + + let rm = (lower & 0b1111) as u8; + let rotate = (lower >> 1) & 0b11000; + let rd = ((lower >> 8) & 0b1111) as u8; + + inst.opcode = op; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm32(rotate as u32), + ]; + }; + } + } else { + if op2 < 0b0100 { + // `Parallel addition and subtraction, signed` + } else if op2 < 0b1000 { + // `Parallel addition and subtraction, unsigned` (`A6-244`) + } else if op2 < 0b1100 { + // `Miscellaneous operations` (`A6-246`) + } else { + return Err(DecodeError::Undefined); + } + } + } else { + if op2 & 0b0001000 == 0 { + // `Multiply, multiply accumulate, and absolute difference` (`A6-247`) + } else { + // `Long multiply, long multiply accumulate, and divide` (`A6-248`) + } + } + } + } else { + // `Coprocessor, Advanced SIMD, and Floating-point instructions` (`A6-249`) + } + } + } else { + // 16b instruction - `A6-221, 16-bit Thumb instruction encoding` + // `Table A6-1` + if opword < 0b01000 { + // `Shift (immediate), add, subtract, move, and compare` page `A6-222` + // v4T + let opcode = opword & 0b111; + // TODO: `S` iff outside `IT` block + inst.s = true; + + match opcode { + 0b000 => { + // LSL (immediate) + // footnote: when opcode is 0, bits 8:6 are 0, encoding is `MOV`. see `A8-487`. + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + let imm5 = (instr >> 6) & 0b11111; + inst.opcode = Opcode::LSL; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm5), + Operand::Nothing, + ]; + } + 0b001 => { + /* LSR on page A8-473 */ + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + let imm5 = (instr >> 6) & 0b11111; + let imm = if imm5 == 0 { + 0x20 + } else { + imm5 + }; + inst.opcode = Opcode::LSR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm), + Operand::Nothing, + ]; + } + 0b010 => { + /* ASR on page A8-328 */ + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + let imm5 = (instr >> 6) & 0b11111; + let imm = if imm5 == 0 { + 0x20 + } else { + imm5 + }; + inst.opcode = Opcode::ASR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(imm), + Operand::Nothing, + ]; + } + 0b011 => { + /* ADD, SUB (register/immediate) */ + let oplower = (instr >> 9) & 0b11; + let rd = (instr & 0b111) as u8; + let rn = ((instr >> 3) & 0b111) as u8; + let rm = ((instr >> 6) & 0b111) as u8; + + match oplower { + 0b00 => { + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + ]; + } + 0b01 => { + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + ]; + } + 0b10 => { + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(rm as u32), + Operand::Nothing, + ]; + } + 0b11 => { + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rn)), + Operand::Imm32(rm as u32), + Operand::Nothing, + ]; + } + _ => { + unreachable!("impossible bit pattern"); + } + } + } + 0b100 => { + /* MOV on page A8-485 */ + + let imm8 = instr & 0b1111_1111; + let rd = ((instr >> 8) & 0b111) as u8; + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm8 as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b101 => { + /* CMP on page A8-368 */ + inst.s = false; + let imm8 = instr & 0b1111_1111; + let rd = ((instr >> 8) & 0b111) as u8; + inst.opcode = Opcode::CMP; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm8 as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b110 => { + /* ADD (immediate, Thumb) on page A8-304 */ + let imm8 = instr & 0b1111_1111; + let rdn = ((instr >> 8) & 0b111) as u8; + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Imm32(imm8 as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b111 => { + /* SUB (immediate, Thumb) on page A8-709 */ + let imm8 = instr & 0b1111_1111; + let rdn = ((instr >> 8) & 0b111) as u8; + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Imm32(imm8 as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + unreachable!("impossible bit pattern"); + } + } + } else if opword < 0b01001 { + let opcode_bits = (instr >> 6) & 0b1111; + // `Data-processing` on page `A6-223` or `Special data instructions and branch and + // exchange` on page `A6-224` + if (instr >> 10) < 0b010001 { + // `Data-processing` on page `A6-223` + // v4T + // TODO: condition inside IT block, no S + inst.s = true; + let rdn = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + if opcode_bits == 0b1101 { + inst.opcode = Opcode::MUL; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Reg(Reg::from_u8(rdn)), + Operand::Nothing, + ]; + } else if opcode_bits == 0b1001 { + inst.opcode = Opcode::RSB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Imm12(0), + Operand::Nothing, + ]; + } else { + let opcode = [ + Opcode::AND, + Opcode::EOR, + Opcode::LSL, + Opcode::LSR, + Opcode::ASR, + Opcode::ADC, + Opcode::SBC, + Opcode::ROR, + Opcode::TST, + Opcode::RSB, + Opcode::CMP, + Opcode::CMN, + Opcode::ORR, + Opcode::MUL, + Opcode::BIC, + Opcode::MVN, + ][opcode_bits as usize]; + inst.opcode = opcode; + if opcode_bits == 8 || opcode_bits == 10 || opcode_bits == 11 { + inst.s = false; + } + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } + } else { + // `Special data instructions and branch and exchange` on page `A6-224` + match opcode_bits { + 0b0000 => { + // `Add Low Registers` (`A8-308`) + // v6T2, `UNPREDICTABLE` in earlier versions + let rdn = ((instr & 0b111) | ((instr >> 4) & 0b1000)) as u8; + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0001 | + 0b0010 | + 0b0011 => { + // `Add High Registers` (`A8-308`) + // v4T + let rdn = ((instr & 0b111) | ((instr >> 4) & 0b1000)) as u8; + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rdn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b0100 | + 0b0101 | + 0b0110 | + 0b0111 => { + // `Compare High Registers` (`A8-307`) + // v4T + let rn = ((instr & 0b111) | ((instr >> 4) & 0b1000)) as u8; + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::CMP; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b1000 => { + // `Move Low Registers` (`A8-487`) + // v6, `UNPREDICTABLE` in earlier versions + // (encoding T1) + let rd = ((instr & 0b111) | ((instr >> 4) & 0b1000)) as u8; + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b1001 | + 0b1010 | + 0b1011 => { + // `Move High Registers` (`A8-487`) + // v4T + let rd = ((instr & 0b111) | ((instr >> 4) & 0b1000)) as u8; + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::MOV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + }, + 0b1100 | + 0b1101 => { + // `Branch and Exchange` (`A8-350`) + // v4T + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::BX; + inst.operands = [ + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b1110 | + 0b1111 => { + // `Branch and Link with Exchange` (`A8-348`) + // v5T, `UNPREDICTABLE` in earlier versions + let rm = ((instr >> 3) & 0b1111) as u8; + inst.opcode = Opcode::BLX; + inst.operands = [ + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + unreachable!("bad bit pattern"); + } + } + } + } else if opword < 0b01010 { + // `LDR (literal)` on page `A8-411` -- v4T + let imm8 = instr & 0b1111_1111; + let rt = ((instr >> 8) & 0b111) as u8; + inst.opcode = Opcode::LDR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(0b1111), + imm8 << 2, + true, // add + false, // no wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opword < 0b10100 { + let op_b = (instr >> 9) & 0b111; + let op_a = instr >> 12; + // `Load/store single data item` on page `A6-225` + // v4T + let rt = (instr & 0b111) as u8; + let rn = ((instr >> 3) & 0b111) as u8; + let rm = ((instr >> 6) & 0b111) as u8; + let imm5 = (instr >> 6) & 0b11111; + if op_a == 0b0101 { + let op = [ + Opcode::STR, + Opcode::STRH, + Opcode::STRB, + Opcode::LDRSB, + Opcode::LDR, + Opcode::LDRH, + Opcode::LDRB, + Opcode::LDRSH, + ][op_b as usize]; + inst.opcode = op; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexReg( + Reg::from_u8(rn), + Reg::from_u8(rm), + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // opword is 0b0110, 0b0111, 0b1000, or 0b1001. opb bit 2 can be used to form a + // three-bit index and select an opcode. operands are shared. except the last two. + // those are sp-relative. + let upper = op_a - 0b0110; + let idx = (upper << 1) | (op_b >> 2); + let op = [ + Opcode::STR, + Opcode::LDR, + Opcode::STRB, + Opcode::LDRB, + Opcode::STRH, + Opcode::LDRH, + Opcode::STR, + Opcode::LDR, + ][idx as usize]; + inst.opcode = op; + if idx < 6 { + let shift = match idx >> 1 { + 0b00 => 2, + 0b01 => 0, + 0b10 => 1, + _ => { unreachable!("impossible bit pattern"); } + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(rn), + imm5 << shift, + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + let rt = ((instr >> 8) & 0b111) as u8; + let imm8 = instr & 0b1111_1111; + inst.operands = [ + Operand::Reg(Reg::from_u8(rt)), + Operand::RegDerefPreindexOffset( + Reg::from_u8(13), // sp + imm8 << 2, + true, // add + false, // wback + ), + Operand::Nothing, + Operand::Nothing, + ]; + } + } + } else if opword < 0b10101 { + // `ADR` on page `A8-320` -- v4T + let rd = ((instr >> 8) & 0b111) as u8; + let imm8 = instr & 0b1111_1111; + inst.opcode = Opcode::ADR; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Imm32(imm8 as u32 * 4), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opword < 0b10110 { + // `ADD (SP plus immediate)` on `A8-314` -- v4T + let rd = ((instr >> 8) & 0b111) as u8; + let imm8 = instr & 0b1111_1111; + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(13)), // sp + Operand::Imm32(imm8 as u32 * 4), + Operand::Nothing, + ]; + } else if opword < 0b11000 { + // `Miscellaneous 16-bit instructions` on page `A6-226` + let opcode_bits = (instr >> 5) & 0b1111111; + if opcode_bits < 0b0000100 { + // `Add Immediate to SP` (`A8-314`) + // v4T + // encoding T2 + let imm7 = instr & 0b111_1111; + inst.s = false; + inst.opcode = Opcode::ADD; + inst.operands = [ + Operand::Reg(Reg::from_u8(13)), + Operand::Reg(Reg::from_u8(13)), + Operand::Imm32((imm7 << 2) as u32), + Operand::Nothing, + ]; + } else if opcode_bits < 0b0001000 { + // `Subtract Immediate to SP` (`A8-717`) + // v4T + let imm7 = instr & 0b111_1111; + inst.s = false; + inst.opcode = Opcode::SUB; + inst.operands = [ + Operand::Reg(Reg::from_u8(13)), + Operand::Reg(Reg::from_u8(13)), + Operand::Imm32((imm7 << 2) as u32), + Operand::Nothing, + ]; + } else if opcode_bits < 0b0010000 { + // `Compare and Branch on Zero` (`A8-354`) + // v6T2 + let op = (instr >> 11) & 1 != 0; + let rn = (instr & 0b111) as u8; + let imm5 = (instr >> 3) & 0b11111; + let imm = (((instr >> 9) & 1) << 5) | imm5; + inst.opcode = if op { + Opcode::CBNZ + } else { + Opcode::CBZ + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::BranchThumbOffset(imm as i32 + 1), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0010010 { + // `Signed Extend Halfword` (`A8-735`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::SXTH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0010100 { + // `Signed Extend Byte` (`A8-731`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::SXTB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0010110 { + // `Unsigned Extend Halfword` (`A8-817`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::UXTH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0011000 { + // `Unsigned Extend Byte` (`A8-813`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::UXTB; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0100000 { + // `Compare and Branch on Zero` (`A8-354`) + // v6T2 + let op = (instr >> 11) & 1 != 0; + let rn = (instr & 0b111) as u8; + let imm5 = (instr >> 3) & 0b11111; + let imm = (((instr >> 9) & 1) << 5) | imm5; + inst.opcode = if op { + Opcode::CBNZ + } else { + Opcode::CBZ + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::BranchThumbOffset(imm as i32 + 1), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0110000 { + // `Push multiple registers` (`A8-539`) + // v4T + let m = (instr >> 8) & 1; + let reglist = (instr & 0b1111_1111) | (m << (6 + 8)); + inst.opcode = Opcode::PUSH; + inst.operands = [ + Operand::RegList(reglist), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0110010 { + // undefined encoding between `PUSH` and `SETEND` + return Err(DecodeError::Undefined); + } else if opcode_bits < 0b0110011 { + // opword == 0b0110010 + // `Set Endianness` (`A8-605`) + // v6 + let e = (instr >> 3) & 1; + inst.opcode = Opcode::SETEND; + inst.operands = [ + Operand::Imm12(e), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b0110100 { + // opword == 0b0110011 + // `Change Processor State` (`B9-1964`) + // v6 + let aif = instr & 0b111; + let im = (instr >> 4) & 1; + inst.opcode = Opcode::CPS(im != 0); + inst.operands = [ + Operand::Imm12(aif), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1001000 { + // undefined encoding between `CPS` and `CBNZ/CBZ` + return Err(DecodeError::Undefined); + } else if opcode_bits < 0b1010000 { + // `Compare and Branch on Nonzero` (`A8-354`) + // v6T2 + let op = (instr >> 11) & 1 != 0; + let rn = (instr & 0b111) as u8; + let imm5 = (instr >> 3) & 0b11111; + let imm = (((instr >> 9) & 1) << 5) | imm5; + inst.opcode = if op { + Opcode::CBNZ + } else { + Opcode::CBZ + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::BranchThumbOffset(imm as i32 + 1), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1010010 { + // `Byte-Reverse Word` (`A8-563`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::REV; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1010100 { + // `Byte-Reverse Packed Halfword` (`A8-565`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::REV16; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1010110 { + // undefined encoding where `Byte-Reverse Signed Word` might go + return Err(DecodeError::Undefined); + } else if opcode_bits < 0b1011000 { + // `Byte-Reverse Signed Halfword` (`A8-567`) + // v6 + let rd = (instr & 0b111) as u8; + let rm = ((instr >> 3) & 0b111) as u8; + inst.opcode = Opcode::REVSH; + inst.operands = [ + Operand::Reg(Reg::from_u8(rd)), + Operand::Reg(Reg::from_u8(rm)), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1100000 { + // `Compare and Branch on Nonzero` (`A8-354`) + // v6T2 + let op = (instr >> 11) & 1 != 0; + let rn = (instr & 0b111) as u8; + let imm5 = (instr >> 3) & 0b11111; + let imm = (((instr >> 9) & 1) << 5) | imm5; + inst.opcode = if op { + Opcode::CBNZ + } else { + Opcode::CBZ + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(rn)), + Operand::BranchThumbOffset(imm as i32 + 1), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1110000 { + // `Pop Multiple Registers` (`A8-535`) + // v4T + let p = (instr >> 8) & 1; + let reglist = (instr & 0b1111_1111) | (p << (7 + 8)); + inst.opcode = Opcode::POP; + inst.operands = [ + Operand::RegList(reglist), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode_bits < 0b1111000 { + // `Breakpoint` (`A8-344`) + // v5 + let imm8 = instr & 0b1111_1111; + inst.opcode = Opcode::BKPT; + inst.operands = [ + Operand::Imm32(imm8 as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `If-Then, and hints` (`A6-227`) + let opb = instr & 0b1111; + let opa = (instr >> 4) & 0b1111; + + if opb != 0 { + // `IT` (`A8-391`) + // v6T2 + let firstcond = opa; + let mask = opb; + inst.opcode = Opcode::IT; + if firstcond == 0b1111 { + return Err(DecodeError::InvalidOperand); + } + inst.operands = [ + Operand::Imm32(firstcond as u32), + Operand::Imm32(mask as u32), + Operand::Nothing, + Operand::Nothing, + ]; + } else { + match opa { + 0b0000 => { + // `NOP` (`A8-511`) + // v6T2 + inst.opcode = Opcode::NOP; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0001 => { + // `YIELD` (`A8-1109`) + // v7 + inst.opcode = Opcode::YIELD; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0010 => { + // `WFE` (`A8-1105`) + // v7 + inst.opcode = Opcode::WFE; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0011 => { + // `WFI` (`A8-1107`) + // v7 + inst.opcode = Opcode::WFI; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b0100 => { + // `SEV` (`A8-607`) + // v7 + inst.opcode = Opcode::SEV; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + hint => { + // `Other encodings in this space are unallocated hints. They execute + // as NOPs, but software must not use them.` + inst.opcode = Opcode::HINT; + inst.operands = [ + Operand::Imm12(hint), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + } + } + } else if opword < 0b11001 { + // `STM (STMIA, STMEA)` on page `A8-665` -- v4T + let rn = ((instr >> 8) & 0b111) as u8; + let reglist = instr & 0b1111_1111; + inst.opcode = Opcode::STM(true, true, false, true); // stmia, no wback, yes usermode + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), true), // always wback + Operand::RegList(reglist as u16), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opword < 0b11010 { + // `LDM/LDMIA/LDMFD (Thumb)` on page `A8-397` -- v4T + let rn = ((instr >> 8) & 0b111) as u8; + let reglist = instr & 0b1111_1111; + let w = (reglist & (1 << rn)) == 0; + inst.opcode = Opcode::LDM(true, true, false, true); // ldmia, no wback, yes usermode + inst.operands = [ + Operand::RegWBack(Reg::from_u8(rn), w), + Operand::RegList(reglist as u16), + Operand::Nothing, + Operand::Nothing, + ]; + } else if opword < 0b11100 { + // `Conditional branch, and Supervisor Call` on page `A6-227` + let opcode = (instr >> 8) & 0b1111; + if opcode < 0b1110 { + // `B` (`A8-332`) + // v4T + inst.opcode = Opcode::B; + let imm = instr & 0b1111_1111; + let imm = imm as i8 as i32; + let cond = (instr >> 8) & 0b1111; + inst.condition = ConditionCode::build(cond as u8); + inst.operands = [ + Operand::BranchThumbOffset(imm + 1), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if opcode < 0b1111 { + // `UDF` (`A8-759`) + // v4T + // first described in revision `C.a` + inst.opcode = Opcode::UDF; + inst.operands = [ + Operand::Imm32((instr & 0xff) as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else { + // `SVC` (`A8-721`) + // v4T + inst.opcode = Opcode::SVC; + inst.operands = [ + Operand::Imm32((instr & 0xff) as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + } else { + // `B` on page `A8-332` -- v4T + inst.opcode = Opcode::B; + let imm = instr & 0b111_1111_1111; + let imm = ((imm as i32) << 21) >> 20; + inst.operands = [ + Operand::Imm32(imm as u32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + } + Ok(()) +} diff --git a/test/armv7.rs b/test/armv7.rs index 865aeb5..09a57e2 100644 --- a/test/armv7.rs +++ b/test/armv7.rs @@ -1,6 +1,8 @@ use yaxpeax_arch::{Arch, Decoder, LengthedInstruction}; use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, DecodeError, Operand, Opcode, Reg, RegShift}; +mod thumb; + type InstDecoder = ::Decoder; fn test_invalid_under(decoder: &InstDecoder, data: [u8; 4]) { @@ -351,9 +353,9 @@ fn test_decode_pop() { [0xf0, 0x40, 0x2d, 0xe9], Instruction { condition: ConditionCode::AL, - opcode: Opcode::STM(false, true, true, false), + opcode: Opcode::STM(false, true, false, false), operands: [ - Operand::Reg(Reg::from_u8(13)), + Operand::RegWBack(Reg::from_u8(13), true), Operand::RegList(16624), Operand::Nothing, Operand::Nothing, @@ -369,9 +371,9 @@ fn test_decode_pop() { [0xf0, 0x80, 0xbd, 0x18], Instruction { condition: ConditionCode::NE, - opcode: Opcode::LDM(true, false, true, false), + opcode: Opcode::LDM(true, false, false, false), operands: [ - Operand::Reg(Reg::from_u8(13)), + Operand::RegWBack(Reg::from_u8(13), true), Operand::RegList(33008), Operand::Nothing, Operand::Nothing, diff --git a/test/armv7/thumb.rs b/test/armv7/thumb.rs new file mode 100644 index 0000000..f61e94d --- /dev/null +++ b/test/armv7/thumb.rs @@ -0,0 +1,3396 @@ +use yaxpeax_arch::{Arch, Decoder, LengthedInstruction}; +use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, DecodeError, Operand, Opcode, Reg, RegShift}; + +type InstDecoder = ::Decoder; + +fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) { + match decoder.decode(data.to_vec()) { + Err(_) => { }, + Ok(inst) => { + panic!( + "unexpected successful decode for {:#x?}\ngot: {}", + data, + inst + ); + } + } +} + +fn test_display_under(decoder: &InstDecoder, data: [u8; 4], expected: &'static str) { + let instr = decoder.decode(data.to_vec()).unwrap_or_else(|_| panic!("failed to decode {:#x?}", data)); + let displayed = format!("{}", instr); + assert!( + displayed == expected, + "decode error for {:#x?}:\n decoded: {:?}\n expected: {:?}\n", + data, + displayed, expected + ); +} + +fn test_decode(data: &[u8], expected: Instruction) { + let instr = InstDecoder::default_thumb().decode(data.to_vec()).unwrap(); + assert!( + instr == expected, + "decode error for {:#x?}:\n decoded: {:?}\n expected: {:?}\n", + data, + instr, expected + ); +} + +fn test_invalid(data: &[u8]) { + test_invalid_under(&InstDecoder::default(), data); +} + +fn test_display(data: &[u8], expected: &'static str) { + let instr = InstDecoder::default_thumb().decode(data.to_vec()).unwrap(); + let text = format!("{}", instr); + assert!( + text == expected, + "display error for {:#x?}\n decoded: {:?}\n displayed: {}\n expected: {}\n", + data, + instr, + text, expected + ); +} + +#[test] +fn test_decode_add_cases() { + test_display( + &[0x01, 0x44], + "add r1, r0" + ); + test_display( + &[0x01, 0xa8], + "add r0, sp, 0x4" + ); + test_display( + &[0x01, 0xa9], + "add r1, sp, 0x4" + ); + test_display( + &[0x01, 0xaa], + "add r2, sp, 0x4" + ); + test_display( + &[0x01, 0xab], + "add r3, sp, 0x4" + ); + test_display( + &[0x01, 0xad], + "add r5, sp, 0x4" + ); + test_display( + &[0x01, 0xae], + "add r6, sp, 0x4" + ); + test_display( + &[0x01, 0xaf], + "add r7, sp, 0x4" + ); + test_display( + &[0x05, 0xac], + "add r4, sp, 0x14" + ); + test_display( + &[0x61, 0xb0], + "add sp, sp, 0x184" + ); + test_display( + &[0x01, 0xb0], + "add sp, sp, 0x4" + ); + test_display( + &[0x02, 0x44], + "add r2, r0" + ); + test_display( + &[0x02, 0xb0], + "add sp, sp, 0x8" + ); + test_display( + &[0x03, 0x44], + "add r3, r0" + ); + test_display( + &[0x17, 0x44], + "add r7, r2" + ); + test_display( + &[0x1b, 0x44], + "add r3, r3" + ); + test_display( + &[0x54, 0x44], + "add r4, r10" + ); + test_display( + &[0x57, 0x44], + "add r7, r10" + ); + test_display( + &[0x5a, 0x44], + "add r2, fp" + ); + test_display( + &[0x61, 0x44], + "add r1, ip" + ); + test_display( + &[0x68, 0x44], + "add r0, sp" + ); + test_display( + &[0x69, 0x44], + "add r1, sp" + ); + test_display( + &[0x6a, 0x44], + "add r2, sp" + ); + test_display( + &[0x6b, 0x44], + "add r3, sp" + ); + test_display( + &[0x6d, 0x44], + "add r5, sp" + ); + test_display( + &[0x6e, 0x44], + "add r6, sp" + ); + test_display( + &[0x6f, 0x44], + "add r7, sp" + ); + test_display( + &[0x75, 0x44], + "add r5, lr" + ); + test_display( + &[0x79, 0x44], + "add r1, pc" + ); + test_display( + &[0xc0, 0x44], + "add r8, r8" + ); + test_display( + &[0xc1, 0x44], + "add sb, r8" + ); + test_display( + &[0xc2, 0x44], + "add r10, r8" + ); + test_display( + &[0xc3, 0x44], + "add fp, r8" + ); + test_display( + &[0xc4, 0x44], + "add ip, r8" + ); + test_display( + &[0xc5, 0x44], + "add sp, r8" + ); + test_display( + &[0xc6, 0x44], + "add lr, r8" + ); + test_display( + &[0xc7, 0x44], + "add pc, r8" + ); + test_display( + &[0xc8, 0x44], + "add r8, sb" + ); + test_display( + &[0xc9, 0x44], + "add sb, sb" + ); + test_display( + &[0xca, 0x44], + "add r10, sb" + ); + test_display( + &[0xcb, 0x44], + "add fp, sb" + ); + test_display( + &[0xcc, 0x44], + "add ip, sb" + ); + test_display( + &[0xcd, 0x44], + "add sp, sb" + ); + test_display( + &[0xce, 0x44], + "add lr, sb" + ); + test_display( + &[0xcf, 0x44], + "add pc, sb" + ); + test_display( + &[0xd0, 0x44], + "add r8, r10" + ); + test_display( + &[0xd1, 0x44], + "add sb, r10" + ); + test_display( + &[0xd2, 0x44], + "add r10, r10" + ); + test_display( + &[0xd3, 0x44], + "add fp, r10" + ); + test_display( + &[0xd4, 0x44], + "add ip, r10" + ); + test_display( + &[0xd5, 0x44], + "add sp, r10" + ); + test_display( + &[0xd6, 0x44], + "add lr, r10" + ); + test_display( + &[0xd7, 0x44], + "add pc, r10" + ); + test_display( + &[0xd8, 0x44], + "add r8, fp" + ); + test_display( + &[0xd9, 0x44], + "add sb, fp" + ); + test_display( + &[0xda, 0x44], + "add r10, fp" + ); + test_display( + &[0xdb, 0x44], + "add fp, fp" + ); + test_display( + &[0xdc, 0x44], + "add ip, fp" + ); + test_display( + &[0xdd, 0x44], + "add sp, fp" + ); + test_display( + &[0xde, 0x44], + "add lr, fp" + ); + test_display( + &[0xdf, 0x44], + "add pc, fp" + ); + test_display( + &[0xe0, 0x44], + "add r8, ip" + ); + test_display( + &[0xe1, 0x44], + "add sb, ip" + ); + test_display( + &[0xe2, 0x44], + "add r10, ip" + ); + test_display( + &[0xe3, 0x44], + "add fp, ip" + ); + test_display( + &[0xe4, 0x44], + "add ip, ip" + ); + test_display( + &[0xe5, 0x44], + "add sp, ip" + ); + test_display( + &[0xe6, 0x44], + "add lr, ip" + ); + test_display( + &[0xe7, 0x44], + "add pc, ip" + ); + test_display( + &[0xe8, 0x44], + "add r8, sp" + ); + test_display( + &[0xe9, 0x44], + "add sb, sp" + ); + test_display( + &[0xea, 0x44], + "add r10, sp" + ); + test_display( + &[0xeb, 0x44], + "add fp, sp" + ); + test_display( + &[0xec, 0x44], + "add ip, sp" + ); + test_display( + &[0xed, 0x44], + "add sp, sp" + ); + test_display( + &[0xee, 0x44], + "add lr, sp" + ); + test_display( + &[0xef, 0x44], + "add pc, sp" + ); + test_display( + &[0xf0, 0x44], + "add r8, lr" + ); + test_display( + &[0xf1, 0x44], + "add sb, lr" + ); + test_display( + &[0xf2, 0x44], + "add r10, lr" + ); + test_display( + &[0xf3, 0x44], + "add fp, lr" + ); + test_display( + &[0xf4, 0x44], + "add ip, lr" + ); + test_display( + &[0xf5, 0x44], + "add sp, lr" + ); + test_display( + &[0xf6, 0x44], + "add lr, lr" + ); + test_display( + &[0xf7, 0x44], + "add pc, lr" + ); + test_display( + &[0xf8, 0x44], + "add r8, pc" + ); + test_display( + &[0xf9, 0x44], + "add sb, pc" + ); + test_display( + &[0xfa, 0x44], + "add r10, pc" + ); + test_display( + &[0xfb, 0x44], + "add fp, pc" + ); + test_display( + &[0xfc, 0x44], + "add ip, pc" + ); + test_display( + &[0xfd, 0x44], + "add sp, pc" + ); + test_display( + &[0xfe, 0x44], + "add lr, pc" + ); + test_display( + &[0xff, 0x44], + "add pc, pc" + ); +} +#[test] +fn test_decode_adr_cases() { + test_display( + &[0x00, 0xa3], + "adr r3, 0x0" + ); + test_display( + &[0x28, 0xa7], + "adr r7, 0xa0" + ); + test_display( + &[0x29, 0xa0], + "adr r0, 0xa4" + ); + test_display( + &[0xff, 0xa6], + "adr r6, 0x3fc" + ); + test_display( + &[0xff, 0xa7], + "adr r7, 0x3fc" + ); +} +#[test] +fn test_decode_bcc_cases() { + test_display( + &[0x80, 0x47], + "blx r0" + ); + test_display( + &[0x88, 0x47], + "blx r1" + ); + test_display( + &[0x90, 0x47], + "blx r2" + ); + test_display( + &[0x98, 0x47], + "blx r3" + ); + test_display( + &[0xa0, 0x47], + "blx r4" + ); + test_display( + &[0xa8, 0x47], + "blx r5" + ); + test_display( + &[0xb0, 0x47], + "blx r6" + ); + test_display( + &[0xb8, 0x47], + "blx r7" + ); + test_display( + &[0xc0, 0x47], + "blx r8" + ); + test_display( + &[0xc8, 0x47], + "blx sb" + ); + test_display( + &[0xd0, 0x47], + "blx r10" + ); + test_display( + &[0xd8, 0x47], + "blx fp" + ); + test_display( + &[0xe0, 0x47], + "blx ip" + ); + test_display( + &[0xe8, 0x47], + "blx sp" + ); + test_display( + &[0xf0, 0x47], + "blx lr" + ); + test_display( + &[0xf8, 0x47], + "blx pc" + ); + test_display( + &[0xfe, 0xd0], + "beq $-0x2" + ); + test_display( + &[0xfe, 0xd1], + "bne $-0x2" + ); + test_display( + &[0xfe, 0xd2], + "bhs $-0x2" + ); + test_display( + &[0xfe, 0xd3], + "blo $-0x2" + ); + test_display( + &[0xfe, 0xd4], + "bmi $-0x2" + ); + test_display( + &[0xfe, 0xd5], + "bpl $-0x2" + ); + test_display( + &[0xfe, 0xd6], + "bvs $-0x2" + ); + test_display( + &[0xfe, 0xd7], + "bvc $-0x2" + ); + test_display( + &[0xfe, 0xd8], + "bhi $-0x2" + ); + test_display( + &[0xfe, 0xd9], + "bls $-0x2" + ); + test_display( + &[0xfe, 0xda], + "bge $-0x2" + ); + test_display( + &[0xfe, 0xdb], + "blt $-0x2" + ); + test_display( + &[0xfe, 0xdc], + "bgt $-0x2" + ); + test_display( + &[0xfe, 0xdd], + "ble $-0x2" + ); + test_display( + &[0xd3, 0xd0], + "beq $-0x58" + ); + test_display( + &[0xd3, 0xd1], + "bne $-0x58" + ); + test_display( + &[0xd3, 0xd2], + "bhs $-0x58" + ); + test_display( + &[0xd3, 0xd3], + "blo $-0x58" + ); + test_display( + &[0xd3, 0xd4], + "bmi $-0x58" + ); + test_display( + &[0xd3, 0xd5], + "bpl $-0x58" + ); + test_display( + &[0xd3, 0xd6], + "bvs $-0x58" + ); + test_display( + &[0xd3, 0xd7], + "bvc $-0x58" + ); + test_display( + &[0xd3, 0xd8], + "bhi $-0x58" + ); + test_display( + &[0xd3, 0xd9], + "bls $-0x58" + ); + test_display( + &[0xd3, 0xda], + "bge $-0x58" + ); + test_display( + &[0xd3, 0xdb], + "blt $-0x58" + ); + test_display( + &[0xd3, 0xdc], + "bgt $-0x58" + ); + test_display( + &[0xd3, 0xdd], + "ble $-0x58" + ); + test_display( + &[0xfd, 0xd0], + "beq $-0x4" + ); + test_display( + &[0xfd, 0xd1], + "bne $-0x4" + ); + test_display( + &[0xfd, 0xd2], + "bhs $-0x4" + ); + test_display( + &[0xfd, 0xd3], + "blo $-0x4" + ); + test_display( + &[0xfd, 0xd4], + "bmi $-0x4" + ); + test_display( + &[0xfd, 0xd5], + "bpl $-0x4" + ); + test_display( + &[0xfd, 0xd6], + "bvs $-0x4" + ); + test_display( + &[0xfd, 0xd7], + "bvc $-0x4" + ); + test_display( + &[0xfd, 0xd8], + "bhi $-0x4" + ); + test_display( + &[0xfd, 0xd9], + "bls $-0x4" + ); + test_display( + &[0xfd, 0xda], + "bge $-0x4" + ); + test_display( + &[0xfd, 0xdb], + "blt $-0x4" + ); + test_display( + &[0xfd, 0xdc], + "bgt $-0x4" + ); + test_display( + &[0xfd, 0xdd], + "ble $-0x4" + ); +} +#[test] +fn test_decode_bkpt_cases() { + test_display( + &[0x00, 0xbe], + "bkpt 0x0" + ); + test_display( + &[0x75, 0xbe], + "bkpt 0x75" + ); + test_display( + &[0xff, 0xbe], + "bkpt 0xff" + ); +} +#[test] +fn test_decode_bx_cases() { + test_display( + &[0x00, 0x47], + "bx r0" + ); + test_display( + &[0x01, 0x47], + "bx r0" + ); + test_display( + &[0x02, 0x47], + "bx r0" + ); + test_display( + &[0x03, 0x47], + "bx r0" + ); + test_display( + &[0x04, 0x47], + "bx r0" + ); + test_display( + &[0x05, 0x47], + "bx r0" + ); + test_display( + &[0x06, 0x47], + "bx r0" + ); + test_display( + &[0x07, 0x47], + "bx r0" + ); + test_display( + &[0x08, 0x47], + "bx r1" + ); + test_display( + &[0x09, 0x47], + "bx r1" + ); + test_display( + &[0x0a, 0x47], + "bx r1" + ); + test_display( + &[0x0b, 0x47], + "bx r1" + ); + test_display( + &[0x0c, 0x47], + "bx r1" + ); + test_display( + &[0x0d, 0x47], + "bx r1" + ); + test_display( + &[0x0e, 0x47], + "bx r1" + ); + test_display( + &[0x0f, 0x47], + "bx r1" + ); + test_display( + &[0x10, 0x47], + "bx r2" + ); + test_display( + &[0x11, 0x47], + "bx r2" + ); + test_display( + &[0x12, 0x47], + "bx r2" + ); + test_display( + &[0x13, 0x47], + "bx r2" + ); + test_display( + &[0x14, 0x47], + "bx r2" + ); + test_display( + &[0x15, 0x47], + "bx r2" + ); + test_display( + &[0x16, 0x47], + "bx r2" + ); + test_display( + &[0x17, 0x47], + "bx r2" + ); + test_display( + &[0x18, 0x47], + "bx r3" + ); + test_display( + &[0x19, 0x47], + "bx r3" + ); + test_display( + &[0x1a, 0x47], + "bx r3" + ); + test_display( + &[0x1b, 0x47], + "bx r3" + ); + test_display( + &[0x1c, 0x47], + "bx r3" + ); + test_display( + &[0x1d, 0x47], + "bx r3" + ); + test_display( + &[0x1e, 0x47], + "bx r3" + ); + test_display( + &[0x1f, 0x47], + "bx r3" + ); + test_display( + &[0x20, 0x47], + "bx r4" + ); + test_display( + &[0x21, 0x47], + "bx r4" + ); + test_display( + &[0x22, 0x47], + "bx r4" + ); + test_display( + &[0x23, 0x47], + "bx r4" + ); + test_display( + &[0x24, 0x47], + "bx r4" + ); + test_display( + &[0x25, 0x47], + "bx r4" + ); + test_display( + &[0x26, 0x47], + "bx r4" + ); + test_display( + &[0x27, 0x47], + "bx r4" + ); + test_display( + &[0x28, 0x47], + "bx r5" + ); + test_display( + &[0x29, 0x47], + "bx r5" + ); + test_display( + &[0x2a, 0x47], + "bx r5" + ); + test_display( + &[0x2b, 0x47], + "bx r5" + ); + test_display( + &[0x2c, 0x47], + "bx r5" + ); + test_display( + &[0x2d, 0x47], + "bx r5" + ); + test_display( + &[0x2e, 0x47], + "bx r5" + ); + test_display( + &[0x2f, 0x47], + "bx r5" + ); + test_display( + &[0x30, 0x47], + "bx r6" + ); + test_display( + &[0x31, 0x47], + "bx r6" + ); + test_display( + &[0x32, 0x47], + "bx r6" + ); + test_display( + &[0x33, 0x47], + "bx r6" + ); + test_display( + &[0x34, 0x47], + "bx r6" + ); + test_display( + &[0x35, 0x47], + "bx r6" + ); + test_display( + &[0x36, 0x47], + "bx r6" + ); + test_display( + &[0x37, 0x47], + "bx r6" + ); + test_display( + &[0x38, 0x47], + "bx r7" + ); + test_display( + &[0x39, 0x47], + "bx r7" + ); + test_display( + &[0x3a, 0x47], + "bx r7" + ); + test_display( + &[0x3b, 0x47], + "bx r7" + ); + test_display( + &[0x3c, 0x47], + "bx r7" + ); + test_display( + &[0x3d, 0x47], + "bx r7" + ); + test_display( + &[0x3e, 0x47], + "bx r7" + ); + test_display( + &[0x3f, 0x47], + "bx r7" + ); + test_display( + &[0x40, 0x47], + "bx r8" + ); + test_display( + &[0x41, 0x47], + "bx r8" + ); + test_display( + &[0x42, 0x47], + "bx r8" + ); + test_display( + &[0x43, 0x47], + "bx r8" + ); + test_display( + &[0x44, 0x47], + "bx r8" + ); + test_display( + &[0x45, 0x47], + "bx r8" + ); + test_display( + &[0x46, 0x47], + "bx r8" + ); + test_display( + &[0x47, 0x47], + "bx r8" + ); + test_display( + &[0x48, 0x47], + "bx sb" + ); + test_display( + &[0x49, 0x47], + "bx sb" + ); + test_display( + &[0x4a, 0x47], + "bx sb" + ); + test_display( + &[0x4b, 0x47], + "bx sb" + ); + test_display( + &[0x4c, 0x47], + "bx sb" + ); + test_display( + &[0x4d, 0x47], + "bx sb" + ); + test_display( + &[0x4e, 0x47], + "bx sb" + ); + test_display( + &[0x4f, 0x47], + "bx sb" + ); + test_display( + &[0x50, 0x47], + "bx r10" + ); + test_display( + &[0x51, 0x47], + "bx r10" + ); + test_display( + &[0x52, 0x47], + "bx r10" + ); + test_display( + &[0x53, 0x47], + "bx r10" + ); + test_display( + &[0x54, 0x47], + "bx r10" + ); + test_display( + &[0x55, 0x47], + "bx r10" + ); + test_display( + &[0x56, 0x47], + "bx r10" + ); + test_display( + &[0x57, 0x47], + "bx r10" + ); + test_display( + &[0x58, 0x47], + "bx fp" + ); + test_display( + &[0x59, 0x47], + "bx fp" + ); + test_display( + &[0x5a, 0x47], + "bx fp" + ); + test_display( + &[0x5b, 0x47], + "bx fp" + ); + test_display( + &[0x5c, 0x47], + "bx fp" + ); + test_display( + &[0x5d, 0x47], + "bx fp" + ); + test_display( + &[0x5e, 0x47], + "bx fp" + ); + test_display( + &[0x5f, 0x47], + "bx fp" + ); + test_display( + &[0x60, 0x47], + "bx ip" + ); + test_display( + &[0x61, 0x47], + "bx ip" + ); + test_display( + &[0x62, 0x47], + "bx ip" + ); + test_display( + &[0x63, 0x47], + "bx ip" + ); + test_display( + &[0x64, 0x47], + "bx ip" + ); + test_display( + &[0x65, 0x47], + "bx ip" + ); + test_display( + &[0x66, 0x47], + "bx ip" + ); + test_display( + &[0x67, 0x47], + "bx ip" + ); + test_display( + &[0x68, 0x47], + "bx sp" + ); + test_display( + &[0x69, 0x47], + "bx sp" + ); + test_display( + &[0x6a, 0x47], + "bx sp" + ); + test_display( + &[0x6b, 0x47], + "bx sp" + ); + test_display( + &[0x6c, 0x47], + "bx sp" + ); + test_display( + &[0x6d, 0x47], + "bx sp" + ); + test_display( + &[0x6e, 0x47], + "bx sp" + ); + test_display( + &[0x6f, 0x47], + "bx sp" + ); + test_display( + &[0x70, 0x47], + "bx lr" + ); + test_display( + &[0x71, 0x47], + "bx lr" + ); + test_display( + &[0x72, 0x47], + "bx lr" + ); + test_display( + &[0x73, 0x47], + "bx lr" + ); + test_display( + &[0x74, 0x47], + "bx lr" + ); + test_display( + &[0x75, 0x47], + "bx lr" + ); + test_display( + &[0x76, 0x47], + "bx lr" + ); + test_display( + &[0x77, 0x47], + "bx lr" + ); + test_display( + &[0x78, 0x47], + "bx pc" + ); + test_display( + &[0x79, 0x47], + "bx pc" + ); + test_display( + &[0x7a, 0x47], + "bx pc" + ); + test_display( + &[0x7b, 0x47], + "bx pc" + ); + test_display( + &[0x7c, 0x47], + "bx pc" + ); + test_display( + &[0x7d, 0x47], + "bx pc" + ); + test_display( + &[0x7e, 0x47], + "bx pc" + ); + test_display( + &[0x7f, 0x47], + "bx pc" + ); +} +#[test] +fn test_decode_cbz_cbnz_cases() { + test_display( + &[0x01, 0xb1], + "cbz r1, $+0x2" // original test: 0x4. assume address is 0, so $ is 2, +2 makes 4? + ); + test_display( + &[0x01, 0xb3], + "cbz r1, $+0x42" // original test: 0x4. assume address is 0, so $ is 2, +2 makes 4? + ); + test_display( + &[0x01, 0xb9], + "cbnz r1, $+0x2" // original test: 0x4. assume address is 0, so $ is 2, +2 makes 4? + ); + test_display( + &[0x01, 0xbb], + "cbnz r1, $+0x42" // original test: 0x4. assume address is 0, so $ is 2, +2 makes 4? + ); + test_display( + &[0x07, 0xb1], + "cbz r7, $+0x2" // original test: 0x4. assume address is 0, so $ is 2, +2 makes 4? + ); + test_display( + &[0x07, 0xb3], + "cbz r7, $+0x42" + ); + test_display( + &[0x07, 0xb9], + "cbnz r7, $+0x2" + ); + test_display( + &[0x07, 0xbb], + "cbnz r7, $+0x42" + ); + test_display( + &[0xff, 0xb1], + "cbz r7, $+0x40" + ); + test_display( + &[0xff, 0xb3], + "cbz r7, $+0x80" + ); + test_display( + &[0xff, 0xb9], + "cbnz r7, $+0x40" + ); + test_display( + &[0xff, 0xbb], + "cbnz r7, $+0x80" + ); +} +#[test] +fn test_decode_cmn_test_cases() { + test_display( + &[0x33, 0x42], + "tst r3, r6" + ); + test_display( + &[0xf3, 0x42], + "cmn r3, r6" + ); +} +#[test] +fn test_decode_cmp_cases() { + test_display( + &[0x00, 0x45], + "cmp r0, r0" + ); + test_display( + &[0x01, 0x45], + "cmp r1, r0" + ); + test_display( + &[0x02, 0x28], + "cmp r0, 0x2" + ); + test_display( + &[0x02, 0x29], + "cmp r1, 0x2" + ); + test_display( + &[0x02, 0x2a], + "cmp r2, 0x2" + ); + test_display( + &[0x02, 0x2b], + "cmp r3, 0x2" + ); + test_display( + &[0x02, 0x2c], + "cmp r4, 0x2" + ); + test_display( + &[0x02, 0x2d], + "cmp r5, 0x2" + ); + test_display( + &[0x02, 0x2e], + "cmp r6, 0x2" + ); + test_display( + &[0x02, 0x2f], + "cmp r7, 0x2" + ); + test_display( + &[0xff, 0x28], + "cmp r0, 0xff" + ); + test_display( + &[0xff, 0x29], + "cmp r1, 0xff" + ); + test_display( + &[0xff, 0x2a], + "cmp r2, 0xff" + ); + test_display( + &[0xff, 0x2b], + "cmp r3, 0xff" + ); + test_display( + &[0xff, 0x2c], + "cmp r4, 0xff" + ); + test_display( + &[0xff, 0x2d], + "cmp r5, 0xff" + ); + test_display( + &[0xff, 0x2e], + "cmp r6, 0xff" + ); + test_display( + &[0xff, 0x2f], + "cmp r7, 0xff" + ); + test_display( + &[0x53, 0x45], + "cmp r3, r10" + ); + test_display( + &[0x6c, 0x45], + "cmp r4, sp" + ); + test_display( + &[0x7e, 0x45], + "cmp r6, pc" + ); + test_display( + &[0xfe, 0x45], + "cmp lr, pc" + ); + test_display( + &[0xff, 0x45], + "cmp pc, pc" + ); +} +#[test] +fn test_decode_it_cases() { + test_display( + &[0x01, 0xbf], + "itttt eq" + ); + test_display( + &[0x03, 0xbf], + "ittte eq" + ); + test_display( + &[0x05, 0xbf], + "ittet eq" + ); + test_display( + &[0x07, 0xbf], + "ittee eq" + ); + test_display( + &[0x09, 0xbf], + "itett eq" + ); + test_display( + &[0x0b, 0xbf], + "itete eq" + ); + test_display( + &[0x0d, 0xbf], + "iteet eq" + ); + test_display( + &[0x0f, 0xbf], + "iteee eq" + ); + test_display( + &[0x11, 0xbf], + "iteee ne" + ); + test_display( + &[0x13, 0xbf], + "iteet ne" + ); + test_display( + &[0x15, 0xbf], + "itete ne" + ); + test_display( + &[0x17, 0xbf], + "itett ne" + ); + test_display( + &[0x19, 0xbf], + "ittee ne" + ); + test_display( + &[0x1b, 0xbf], + "ittet ne" + ); + test_display( + &[0x1d, 0xbf], + "ittte ne" + ); + test_display( + &[0x1f, 0xbf], + "itttt ne" + ); + test_display( + &[0x21, 0xbf], + "itttt hs" + ); + test_display( + &[0x23, 0xbf], + "ittte hs" + ); + test_display( + &[0x25, 0xbf], + "ittet hs" + ); + test_display( + &[0x27, 0xbf], + "ittee hs" + ); + test_display( + &[0x29, 0xbf], + "itett hs" + ); + test_display( + &[0x2b, 0xbf], + "itete hs" + ); + test_display( + &[0x2d, 0xbf], + "iteet hs" + ); + test_display( + &[0x2f, 0xbf], + "iteee hs" + ); + test_display( + &[0x31, 0xbf], + "iteee lo" + ); + test_display( + &[0x33, 0xbf], + "iteet lo" + ); + test_display( + &[0x35, 0xbf], + "itete lo" + ); + test_display( + &[0x37, 0xbf], + "itett lo" + ); + test_display( + &[0x39, 0xbf], + "ittee lo" + ); + test_display( + &[0x3b, 0xbf], + "ittet lo" + ); + test_display( + &[0x3d, 0xbf], + "ittte lo" + ); + test_display( + &[0x3f, 0xbf], + "itttt lo" + ); + test_display( + &[0x41, 0xbf], + "itttt mi" + ); + test_display( + &[0x43, 0xbf], + "ittte mi" + ); + test_display( + &[0x45, 0xbf], + "ittet mi" + ); + test_display( + &[0x47, 0xbf], + "ittee mi" + ); + test_display( + &[0x49, 0xbf], + "itett mi" + ); + test_display( + &[0x4b, 0xbf], + "itete mi" + ); + test_display( + &[0x4d, 0xbf], + "iteet mi" + ); + test_display( + &[0x4f, 0xbf], + "iteee mi" + ); + test_display( + &[0x51, 0xbf], + "iteee pl" + ); + test_display( + &[0x53, 0xbf], + "iteet pl" + ); + test_display( + &[0x55, 0xbf], + "itete pl" + ); + test_display( + &[0x57, 0xbf], + "itett pl" + ); + test_display( + &[0x59, 0xbf], + "ittee pl" + ); + test_display( + &[0x5b, 0xbf], + "ittet pl" + ); + test_display( + &[0x5d, 0xbf], + "ittte pl" + ); + test_display( + &[0x5f, 0xbf], + "itttt pl" + ); + test_display( + &[0x61, 0xbf], + "itttt vs" + ); + test_display( + &[0x63, 0xbf], + "ittte vs" + ); + test_display( + &[0x65, 0xbf], + "ittet vs" + ); + test_display( + &[0x67, 0xbf], + "ittee vs" + ); + test_display( + &[0x69, 0xbf], + "itett vs" + ); + test_display( + &[0x6b, 0xbf], + "itete vs" + ); + test_display( + &[0x6d, 0xbf], + "iteet vs" + ); + test_display( + &[0x6f, 0xbf], + "iteee vs" + ); + test_display( + &[0x71, 0xbf], + "iteee vc" + ); + test_display( + &[0x73, 0xbf], + "iteet vc" + ); + test_display( + &[0x75, 0xbf], + "itete vc" + ); + test_display( + &[0x77, 0xbf], + "itett vc" + ); + test_display( + &[0x79, 0xbf], + "ittee vc" + ); + test_display( + &[0x7b, 0xbf], + "ittet vc" + ); + test_display( + &[0x7d, 0xbf], + "ittte vc" + ); + test_display( + &[0x7f, 0xbf], + "itttt vc" + ); + test_display( + &[0x81, 0xbf], + "itttt hi" + ); + test_display( + &[0x83, 0xbf], + "ittte hi" + ); + test_display( + &[0x85, 0xbf], + "ittet hi" + ); + test_display( + &[0x87, 0xbf], + "ittee hi" + ); + test_display( + &[0x89, 0xbf], + "itett hi" + ); + test_display( + &[0x8b, 0xbf], + "itete hi" + ); + test_display( + &[0x8d, 0xbf], + "iteet hi" + ); + test_display( + &[0x8f, 0xbf], + "iteee hi" + ); + test_display( + &[0x91, 0xbf], + "iteee ls" + ); + test_display( + &[0x93, 0xbf], + "iteet ls" + ); + test_display( + &[0x95, 0xbf], + "itete ls" + ); + test_display( + &[0x97, 0xbf], + "itett ls" + ); + test_display( + &[0x99, 0xbf], + "ittee ls" + ); + test_display( + &[0x9b, 0xbf], + "ittet ls" + ); + test_display( + &[0x9d, 0xbf], + "ittte ls" + ); + test_display( + &[0x9f, 0xbf], + "itttt ls" + ); + test_display( + &[0xa1, 0xbf], + "itttt ge" + ); + test_display( + &[0xa3, 0xbf], + "ittte ge" + ); + test_display( + &[0xa5, 0xbf], + "ittet ge" + ); + test_display( + &[0xa7, 0xbf], + "ittee ge" + ); + test_display( + &[0xa9, 0xbf], + "itett ge" + ); + test_display( + &[0xab, 0xbf], + "itete ge" + ); + test_display( + &[0xad, 0xbf], + "iteet ge" + ); + test_display( + &[0xaf, 0xbf], + "iteee ge" + ); + test_display( + &[0xb1, 0xbf], + "iteee lt" + ); + test_display( + &[0xb3, 0xbf], + "iteet lt" + ); + test_display( + &[0xb5, 0xbf], + "itete lt" + ); + test_display( + &[0xb7, 0xbf], + "itett lt" + ); + test_display( + &[0xb9, 0xbf], + "ittee lt" + ); + test_display( + &[0xbb, 0xbf], + "ittet lt" + ); + test_display( + &[0xbd, 0xbf], + "ittte lt" + ); + test_display( + &[0xbf, 0xbf], + "itttt lt" + ); + test_display( + &[0xc1, 0xbf], + "itttt gt" + ); + test_display( + &[0xc3, 0xbf], + "ittte gt" + ); + test_display( + &[0xc5, 0xbf], + "ittet gt" + ); + test_display( + &[0xc7, 0xbf], + "ittee gt" + ); + test_display( + &[0xc9, 0xbf], + "itett gt" + ); + test_display( + &[0xcb, 0xbf], + "itete gt" + ); + test_display( + &[0xcd, 0xbf], + "iteet gt" + ); + test_display( + &[0xcf, 0xbf], + "iteee gt" + ); + test_display( + &[0xd1, 0xbf], + "iteee le" + ); + test_display( + &[0xd3, 0xbf], + "iteet le" + ); + test_display( + &[0xd5, 0xbf], + "itete le" + ); + test_display( + &[0xd7, 0xbf], + "itett le" + ); + test_display( + &[0xd9, 0xbf], + "ittee le" + ); + test_display( + &[0xdb, 0xbf], + "ittet le" + ); + test_display( + &[0xdd, 0xbf], + "ittte le" + ); + test_display( + &[0xdf, 0xbf], + "itttt le" + ); + test_display( + &[0xe1, 0xbf], + "itttt al" + ); + test_display( + &[0xe3, 0xbf], + "ittte al" + ); + test_display( + &[0xe5, 0xbf], + "ittet al" + ); + test_display( + &[0xe7, 0xbf], + "ittee al" + ); + test_display( + &[0xe9, 0xbf], + "itett al" + ); + test_display( + &[0xeb, 0xbf], + "itete al" + ); + test_display( + &[0xed, 0xbf], + "iteet al" + ); + test_display( + &[0xef, 0xbf], + "iteee al" + ); +} +#[test] +fn test_decode_ldm_16b_cases() { + test_display( + &[0x80, 0xc8], + "ldm r0!, {r7}" + ); + test_display( + &[0x80, 0xc9], + "ldm r1!, {r7}" + ); + test_display( + &[0x80, 0xca], + "ldm r2!, {r7}" + ); + test_display( + &[0x80, 0xcb], + "ldm r3!, {r7}" + ); + test_display( + &[0x80, 0xcc], + "ldm r4!, {r7}" + ); + test_display( + &[0x80, 0xcd], + "ldm r5!, {r7}" + ); + test_display( + &[0x80, 0xce], + "ldm r6!, {r7}" + ); + test_display( + &[0x80, 0xcf], + "ldm r7, {r7}" + ); + test_display( + &[0xb0, 0xce], + "ldm r6!, {r4, r5, r7}" + ); + test_display( + &[0xb0, 0xcf], + "ldm r7, {r4, r5, r7}" + ); + test_display( + &[0xc0, 0xcb], + "ldm r3!, {r6, r7}" + ); + test_display( + &[0xfe, 0xc8], + "ldm r0!, {r1, r2, r3, r4, r5, r6, r7}" + ); + test_display( + &[0xed, 0xcc], + "ldm r4!, {r0, r2, r3, r5, r6, r7}" + ); + test_display( + &[0xef, 0xcc], + "ldm r4!, {r0, r1, r2, r3, r5, r6, r7}" + ); + test_display( + &[0xfe, 0xce], + "ldm r6, {r1, r2, r3, r4, r5, r6, r7}" + ); + test_display( + &[0xff, 0xcd], + "ldm r5, {r0, r1, r2, r3, r4, r5, r6, r7}" + ); +} +#[test] +fn test_decode_ldr_16b_cases() { + test_display( + &[0x00, 0x48], + "ldr r0, [pc]" + ); + test_display( + &[0x00, 0x49], + "ldr r1, [pc]" + ); + test_display( + &[0x00, 0x4a], + "ldr r2, [pc]" + ); + test_display( + &[0x00, 0x4b], + "ldr r3, [pc]" + ); + test_display( + &[0x00, 0x4c], + "ldr r4, [pc]" + ); + test_display( + &[0x00, 0x4d], + "ldr r5, [pc]" + ); + test_display( + &[0x00, 0x4e], + "ldr r6, [pc]" + ); + test_display( + &[0x00, 0x4f], + "ldr r7, [pc]" + ); + test_display( + &[0x00, 0x56], + "ldrsb r0, [r0, r0]" + ); + test_display( + &[0x00, 0x57], + "ldrsb r0, [r0, r4]" + ); + test_display( + &[0x00, 0x58], + "ldr r0, [r0, r0]" + ); + test_display( + &[0x00, 0x59], + "ldr r0, [r0, r4]" + ); + test_display( + &[0x00, 0x5a], + "ldrh r0, [r0, r0]" + ); + test_display( + &[0x00, 0x5b], + "ldrh r0, [r0, r4]" + ); + test_display( + &[0x00, 0x5c], + "ldrb r0, [r0, r0]" + ); + test_display( + &[0x00, 0x5d], + "ldrb r0, [r0, r4]" + ); + test_display( + &[0x00, 0x5e], + "ldrsh r0, [r0, r0]" + ); + test_display( + &[0x00, 0x5f], + "ldrsh r0, [r0, r4]" + ); + test_display( + &[0x00, 0x68], + "ldr r0, [r0]" + ); + test_display( + &[0x00, 0x69], + "ldr r0, [r0, 0x10]" + ); + test_display( + &[0x00, 0x6a], + "ldr r0, [r0, 0x20]" + ); + test_display( + &[0x00, 0x6b], + "ldr r0, [r0, 0x30]" + ); + test_display( + &[0x00, 0x6c], + "ldr r0, [r0, 0x40]" + ); + test_display( + &[0x00, 0x6d], + "ldr r0, [r0, 0x50]" + ); + test_display( + &[0x00, 0x6e], + "ldr r0, [r0, 0x60]" + ); + test_display( + &[0x00, 0x6f], + "ldr r0, [r0, 0x70]" + ); + test_display( + &[0x00, 0x78], + "ldrb r0, [r0]" + ); + test_display( + &[0x00, 0x79], + "ldrb r0, [r0, 0x4]" + ); + test_display( + &[0x00, 0x7a], + "ldrb r0, [r0, 0x8]" + ); + test_display( + &[0x00, 0x7b], + "ldrb r0, [r0, 0xc]" + ); + test_display( + &[0x00, 0x7c], + "ldrb r0, [r0, 0x10]" + ); + test_display( + &[0x00, 0x7d], + "ldrb r0, [r0, 0x14]" + ); + test_display( + &[0x00, 0x7e], + "ldrb r0, [r0, 0x18]" + ); + test_display( + &[0x00, 0x7f], + "ldrb r0, [r0, 0x1c]" + ); + test_display( + &[0x00, 0x88], + "ldrh r0, [r0]" + ); + test_display( + &[0x00, 0x89], + "ldrh r0, [r0, 0x8]" + ); + test_display( + &[0x00, 0x8a], + "ldrh r0, [r0, 0x10]" + ); + test_display( + &[0x00, 0x8b], + "ldrh r0, [r0, 0x18]" + ); + test_display( + &[0x00, 0x8c], + "ldrh r0, [r0, 0x20]" + ); + test_display( + &[0x00, 0x8d], + "ldrh r0, [r0, 0x28]" + ); + test_display( + &[0x00, 0x8e], + "ldrh r0, [r0, 0x30]" + ); + test_display( + &[0x00, 0x8f], + "ldrh r0, [r0, 0x38]" + ); + test_display( + &[0x00, 0x98], + "ldr r0, [sp]" + ); + test_display( + &[0x00, 0x99], + "ldr r1, [sp]" + ); + test_display( + &[0x00, 0x9a], + "ldr r2, [sp]" + ); + test_display( + &[0x00, 0x9b], + "ldr r3, [sp]" + ); + test_display( + &[0x00, 0x9c], + "ldr r4, [sp]" + ); + test_display( + &[0x00, 0x9d], + "ldr r5, [sp]" + ); + test_display( + &[0x00, 0x9e], + "ldr r6, [sp]" + ); + test_display( + &[0x00, 0x9f], + "ldr r7, [sp]" + ); + test_display( + &[0x01, 0x48], + "ldr r0, [pc, 0x4]" + ); + test_display( + &[0x01, 0x49], + "ldr r1, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4a], + "ldr r2, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4b], + "ldr r3, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4c], + "ldr r4, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4d], + "ldr r5, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4e], + "ldr r6, [pc, 0x4]" + ); + test_display( + &[0x01, 0x4f], + "ldr r7, [pc, 0x4]" + ); + test_display( + &[0xff, 0x48], + "ldr r0, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x49], + "ldr r1, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4a], + "ldr r2, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4b], + "ldr r3, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4c], + "ldr r4, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4d], + "ldr r5, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4e], + "ldr r6, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x4f], + "ldr r7, [pc, 0x3fc]" + ); + test_display( + &[0xff, 0x56], + "ldrsb r7, [r7, r3]" + ); + test_display( + &[0xff, 0x58], + "ldr r7, [r7, r3]" + ); + test_display( + &[0xff, 0x5a], + "ldrh r7, [r7, r3]" + ); + test_display( + &[0xff, 0x5c], + "ldrb r7, [r7, r3]" + ); + test_display( + &[0xff, 0x5e], + "ldrsh r7, [r7, r3]" + ); + test_display( + &[0xff, 0x68], + "ldr r7, [r7, 0xc]" + ); + test_display( + &[0xff, 0x69], + "ldr r7, [r7, 0x1c]" + ); + test_display( + &[0xff, 0x6a], + "ldr r7, [r7, 0x2c]" + ); + test_display( + &[0xff, 0x6b], + "ldr r7, [r7, 0x3c]" + ); + test_display( + &[0xff, 0x6c], + "ldr r7, [r7, 0x4c]" + ); + test_display( + &[0xff, 0x6d], + "ldr r7, [r7, 0x5c]" + ); + test_display( + &[0xff, 0x6e], + "ldr r7, [r7, 0x6c]" + ); + test_display( + &[0xff, 0x6f], + "ldr r7, [r7, 0x7c]" + ); + test_display( + &[0xff, 0x78], + "ldrb r7, [r7, 0x3]" + ); + test_display( + &[0xff, 0x79], + "ldrb r7, [r7, 0x7]" + ); + test_display( + &[0xff, 0x7a], + "ldrb r7, [r7, 0xb]" + ); + test_display( + &[0xff, 0x7b], + "ldrb r7, [r7, 0xf]" + ); + test_display( + &[0xff, 0x7c], + "ldrb r7, [r7, 0x13]" + ); + test_display( + &[0xff, 0x7d], + "ldrb r7, [r7, 0x17]" + ); + test_display( + &[0xff, 0x7e], + "ldrb r7, [r7, 0x1b]" + ); + test_display( + &[0xff, 0x7f], + "ldrb r7, [r7, 0x1f]" + ); + test_display( + &[0xff, 0x88], + "ldrh r7, [r7, 0x6]" + ); + test_display( + &[0xff, 0x89], + "ldrh r7, [r7, 0xe]" + ); + test_display( + &[0xff, 0x8a], + "ldrh r7, [r7, 0x16]" + ); + test_display( + &[0xff, 0x8b], + "ldrh r7, [r7, 0x1e]" + ); + test_display( + &[0xff, 0x8c], + "ldrh r7, [r7, 0x26]" + ); + test_display( + &[0xff, 0x8d], + "ldrh r7, [r7, 0x2e]" + ); + test_display( + &[0xff, 0x8e], + "ldrh r7, [r7, 0x36]" + ); + test_display( + &[0xff, 0x8f], + "ldrh r7, [r7, 0x3e]" + ); + test_display( + &[0xff, 0x98], + "ldr r0, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x99], + "ldr r1, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9a], + "ldr r2, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9b], + "ldr r3, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9c], + "ldr r4, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9d], + "ldr r5, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9e], + "ldr r6, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x9f], + "ldr r7, [sp, 0x3fc]" + ); +} +#[test] +fn test_decode_misc_cases() { + test_display( + &[0x00, 0xbf], + "nop" + ); + test_display( + &[0x10, 0xbf], + "yield" + ); + test_display( + &[0x20, 0xbf], + "wfe" + ); + test_display( + &[0x30, 0xbf], + "wfi" + ); + test_display( + &[0x40, 0xbf], + "sev" + ); + test_display( + &[0x50, 0xb6], + "setend le" + ); + test_display( + &[0x50, 0xbf], + "hint 0x5" + ); + test_display( + &[0x58, 0xb6], + "setend be" + ); + test_display( + &[0x60, 0xbf], + "hint 0x6" + ); + test_display( + &[0x61, 0xb6], + "cpsie f" + ); + test_display( + &[0x62, 0xb6], + "cpsie i" + ); + test_display( + &[0x63, 0xb6], + "cpsie if" + ); + test_display( + &[0x64, 0xb6], + "cpsie a" + ); + test_display( + &[0x65, 0xb6], + "cpsie af" + ); + test_display( + &[0x66, 0xb6], + "cpsie ai" + ); + test_display( + &[0x67, 0xb6], + "cpsie aif" + ); + test_display( + &[0x70, 0xbf], + "hint 0x7" + ); + test_display( + &[0x71, 0xb6], + "cpsid f" + ); + test_display( + &[0x72, 0xb6], + "cpsid i" + ); + test_display( + &[0x73, 0xb6], + "cpsid if" + ); + test_display( + &[0x74, 0xb6], + "cpsid a" + ); + test_display( + &[0x75, 0xb6], + "cpsid af" + ); + test_display( + &[0x76, 0xb6], + "cpsid ai" + ); + test_display( + &[0x77, 0xb6], + "cpsid aif" + ); + test_display( + &[0x80, 0xbf], + "hint 0x8" + ); + test_display( + &[0x90, 0xbf], + "hint 0x9" + ); + test_display( + &[0xa0, 0xbf], + "hint 0xa" + ); + test_display( + &[0xb0, 0xbf], + "hint 0xb" + ); + test_display( + &[0xc0, 0xbf], + "hint 0xc" + ); + test_display( + &[0xd0, 0xbf], + "hint 0xd" + ); + test_display( + &[0xe0, 0xbf], + "hint 0xe" + ); + test_display( + &[0xf0, 0xbf], + "hint 0xf" + ); + test_display( + &[0xfe, 0xde], + "udf 0xfe" + ); +} +#[test] +fn test_decode_mov_cases() { + test_display( + &[0x21, 0x46], + "mov r1, r4" + ); + test_display( + &[0x90, 0x46], + "mov r8, r2" + ); + test_display( + &[0x91, 0x46], + "mov sb, r2" + ); + test_display( + &[0x92, 0x46], + "mov r10, r2" + ); + test_display( + &[0x93, 0x46], + "mov fp, r2" + ); + test_display( + &[0x94, 0x46], + "mov ip, r2" + ); + test_display( + &[0x95, 0x46], + "mov sp, r2" + ); + test_display( + &[0x96, 0x46], + "mov lr, r2" + ); + test_display( + &[0x97, 0x46], + "mov pc, r2" + ); + test_display( + &[0xc0, 0x46], + "mov r8, r8" + ); + test_display( + &[0xc1, 0x46], + "mov sb, r8" + ); + test_display( + &[0xc2, 0x46], + "mov r10, r8" + ); + test_display( + &[0xc3, 0x46], + "mov fp, r8" + ); + test_display( + &[0xc4, 0x46], + "mov ip, r8" + ); + test_display( + &[0xc5, 0x46], + "mov sp, r8" + ); + test_display( + &[0xc6, 0x46], + "mov lr, r8" + ); + test_display( + &[0xc7, 0x46], + "mov pc, r8" + ); + test_display( + &[0xc8, 0x46], + "mov r8, sb" + ); + test_display( + &[0xc9, 0x46], + "mov sb, sb" + ); + test_display( + &[0xca, 0x46], + "mov r10, sb" + ); + test_display( + &[0xcb, 0x46], + "mov fp, sb" + ); + test_display( + &[0xcc, 0x46], + "mov ip, sb" + ); + test_display( + &[0xcd, 0x46], + "mov sp, sb" + ); + test_display( + &[0xce, 0x46], + "mov lr, sb" + ); + test_display( + &[0xcf, 0x46], + "mov pc, sb" + ); + test_display( + &[0xfc, 0x46], + "mov ip, pc" + ); + test_display( + &[0xfd, 0x46], + "mov sp, pc" + ); + test_display( + &[0xfe, 0x46], + "mov lr, pc" + ); + test_display( + &[0xff, 0x46], + "mov pc, pc" + ); +} +#[test] +fn test_decode_op_s_cases() { + test_display( + &[0x00, 0x18], + "adds r0, r0, r0" + ); + test_display( + &[0x00, 0x19], + "adds r0, r0, r4" + ); + test_display( + &[0x00, 0x1c], + "adds r0, r0, 0x0" + ); + test_display( + &[0x00, 0x1d], + "adds r0, r0, 0x4" + ); + test_display( + &[0x00, 0x30], + "adds r0, 0x0" + ); + test_display( + &[0x00, 0x31], + "adds r1, 0x0" + ); + test_display( + &[0x00, 0x32], + "adds r2, 0x0" + ); + test_display( + &[0x00, 0x33], + "adds r3, 0x0" + ); + test_display( + &[0x00, 0x34], + "adds r4, 0x0" + ); + test_display( + &[0x00, 0x35], + "adds r5, 0x0" + ); + test_display( + &[0x00, 0x36], + "adds r6, 0x0" + ); + test_display( + &[0x00, 0x37], + "adds r7, 0x0" + ); + test_display( + &[0x00, 0x40], + "ands r0, r0" + ); + test_display( + &[0x00, 0x43], + "orrs r0, r0" + ); + test_display( + &[0x01, 0x08], + "lsrs r1, r0, 0x20" + ); + test_display( + &[0x01, 0x09], + "lsrs r1, r0, 0x4" + ); + test_display( + &[0x01, 0x0a], + "lsrs r1, r0, 0x8" + ); + test_display( + &[0x01, 0x0b], + "lsrs r1, r0, 0xc" + ); + test_display( + &[0x01, 0x0c], + "lsrs r1, r0, 0x10" + ); + test_display( + &[0x01, 0x0d], + "lsrs r1, r0, 0x14" + ); + test_display( + &[0x01, 0x0e], + "lsrs r1, r0, 0x18" + ); + test_display( + &[0x01, 0x0f], + "lsrs r1, r0, 0x1c" + ); + test_display( + &[0x01, 0x10], + "asrs r1, r0, 0x20" + ); + test_display( + &[0x01, 0x11], + "asrs r1, r0, 0x4" + ); + test_display( + &[0x01, 0x12], + "asrs r1, r0, 0x8" + ); + test_display( + &[0x01, 0x13], + "asrs r1, r0, 0xc" + ); + test_display( + &[0x01, 0x14], + "asrs r1, r0, 0x10" + ); + test_display( + &[0x01, 0x15], + "asrs r1, r0, 0x14" + ); + test_display( + &[0x01, 0x16], + "asrs r1, r0, 0x18" + ); + test_display( + &[0x01, 0x17], + "asrs r1, r0, 0x1c" + ); + test_display( + &[0x01, 0x18], + "adds r1, r0, r0" + ); + test_display( + &[0x01, 0x19], + "adds r1, r0, r4" + ); + test_display( + &[0x01, 0x1c], + "adds r1, r0, 0x0" + ); + test_display( + &[0x01, 0x1d], + "adds r1, r0, 0x4" + ); + test_display( + &[0x01, 0x30], + "adds r0, 0x1" + ); + test_display( + &[0x01, 0x31], + "adds r1, 0x1" + ); + test_display( + &[0x01, 0x32], + "adds r2, 0x1" + ); + test_display( + &[0x01, 0x33], + "adds r3, 0x1" + ); + test_display( + &[0x01, 0x34], + "adds r4, 0x1" + ); + test_display( + &[0x01, 0x35], + "adds r5, 0x1" + ); + test_display( + &[0x01, 0x36], + "adds r6, 0x1" + ); + test_display( + &[0x01, 0x37], + "adds r7, 0x1" + ); + test_display( + &[0x0a, 0x01], + "lsls r2, r1, 0x4" + ); + test_display( + &[0x0a, 0x02], + "lsls r2, r1, 0x8" + ); + test_display( + &[0x0a, 0x03], + "lsls r2, r1, 0xc" + ); + test_display( + &[0x0a, 0x04], + "lsls r2, r1, 0x10" + ); + test_display( + &[0x0a, 0x05], + "lsls r2, r1, 0x14" + ); + test_display( + &[0x0a, 0x06], + "lsls r2, r1, 0x18" + ); + test_display( + &[0x0a, 0x07], + "lsls r2, r1, 0x1c" + ); + test_display( + &[0x13, 0x1a], + "subs r3, r2, r0" + ); + test_display( + &[0x13, 0x1b], + "subs r3, r2, r4" + ); + test_display( + &[0x13, 0x1e], + "subs r3, r2, 0x0" + ); + test_display( + &[0x13, 0x1f], + "subs r3, r2, 0x4" + ); + test_display( + &[0x3d, 0x40], + "ands r5, r7" + ); + test_display( + &[0x3d, 0x43], + "orrs r5, r7" + ); + test_display( + &[0x7d, 0x40], + "eors r5, r7" + ); + test_display( + &[0x7d, 0x41], + "adcs r5, r7" + ); + test_display( + &[0x7d, 0x42], + "rsbs r5, r7, 0x0" + ); + test_display( + &[0x7d, 0x43], + "muls r5, r7, r5" + ); + test_display( + &[0xbd, 0x41], + "sbcs r5, r7" + ); + test_display( + &[0xbd, 0x43], + "bics r5, r7" + ); + test_display( + &[0xd6, 0x41], + "rors r6, r2" + ); + test_display( + &[0xd6, 0x43], + "mvns r6, r2" + ); + test_display( + &[0xfd, 0x20], + "movs r0, 0xfd" + ); + test_display( + &[0xfd, 0x20], + "movs r0, 0xfd" + ); + test_display( + &[0xfd, 0x21], + "movs r1, 0xfd" + ); + test_display( + &[0xfd, 0x21], + "movs r1, 0xfd" + ); + test_display( + &[0xfd, 0x22], + "movs r2, 0xfd" + ); + test_display( + &[0xfd, 0x22], + "movs r2, 0xfd" + ); + test_display( + &[0xfd, 0x23], + "movs r3, 0xfd" + ); + test_display( + &[0xfd, 0x23], + "movs r3, 0xfd" + ); + test_display( + &[0xfd, 0x24], + "movs r4, 0xfd" + ); + test_display( + &[0xfd, 0x24], + "movs r4, 0xfd" + ); + test_display( + &[0xfd, 0x25], + "movs r5, 0xfd" + ); + test_display( + &[0xfd, 0x25], + "movs r5, 0xfd" + ); + test_display( + &[0xfd, 0x26], + "movs r6, 0xfd" + ); + test_display( + &[0xfd, 0x26], + "movs r6, 0xfd" + ); + test_display( + &[0xfd, 0x27], + "movs r7, 0xfd" + ); + test_display( + &[0xfd, 0x27], + "movs r7, 0xfd" + ); + test_display( + &[0xfe, 0x00], + "lsls r6, r7, 0x3" + ); + test_display( + &[0xfe, 0x01], + "lsls r6, r7, 0x7" + ); + test_display( + &[0xfe, 0x02], + "lsls r6, r7, 0xb" + ); + test_display( + &[0xfe, 0x03], + "lsls r6, r7, 0xf" + ); + test_display( + &[0xfe, 0x04], + "lsls r6, r7, 0x13" + ); + test_display( + &[0xfe, 0x05], + "lsls r6, r7, 0x17" + ); + test_display( + &[0xfe, 0x06], + "lsls r6, r7, 0x1b" + ); + test_display( + &[0xfe, 0x07], + "lsls r6, r7, 0x1f" + ); + test_display( + &[0xfe, 0x08], + "lsrs r6, r7, 0x3" + ); + test_display( + &[0xfe, 0x09], + "lsrs r6, r7, 0x7" + ); + test_display( + &[0xfe, 0x0a], + "lsrs r6, r7, 0xb" + ); + test_display( + &[0xfe, 0x0b], + "lsrs r6, r7, 0xf" + ); + test_display( + &[0xfe, 0x0c], + "lsrs r6, r7, 0x13" + ); + test_display( + &[0xfe, 0x0d], + "lsrs r6, r7, 0x17" + ); + test_display( + &[0xfe, 0x0e], + "lsrs r6, r7, 0x1b" + ); + test_display( + &[0xfe, 0x0f], + "lsrs r6, r7, 0x1f" + ); + test_display( + &[0xfe, 0x10], + "asrs r6, r7, 0x3" + ); + test_display( + &[0xfe, 0x11], + "asrs r6, r7, 0x7" + ); + test_display( + &[0xfe, 0x12], + "asrs r6, r7, 0xb" + ); + test_display( + &[0xfe, 0x13], + "asrs r6, r7, 0xf" + ); + test_display( + &[0xfe, 0x14], + "asrs r6, r7, 0x13" + ); + test_display( + &[0xfe, 0x15], + "asrs r6, r7, 0x17" + ); + test_display( + &[0xfe, 0x16], + "asrs r6, r7, 0x1b" + ); + test_display( + &[0xfe, 0x17], + "asrs r6, r7, 0x1f" + ); + test_display( + &[0xfe, 0x40], + "lsrs r6, r7" + ); + test_display( + &[0xfe, 0x41], + "rors r6, r7" + ); + test_display( + &[0xfe, 0x43], + "mvns r6, r7" + ); + test_display( + &[0xff, 0x18], + "adds r7, r7, r3" + ); + test_display( + &[0xff, 0x19], + "adds r7, r7, r7" + ); + test_display( + &[0xff, 0x1c], + "adds r7, r7, 0x3" + ); + test_display( + &[0xff, 0x1d], + "adds r7, r7, 0x7" + ); + test_display( + &[0xff, 0x30], + "adds r0, 0xff" + ); + test_display( + &[0xff, 0x31], + "adds r1, 0xff" + ); + test_display( + &[0xff, 0x32], + "adds r2, 0xff" + ); + test_display( + &[0xff, 0x33], + "adds r3, 0xff" + ); + test_display( + &[0xff, 0x34], + "adds r4, 0xff" + ); + test_display( + &[0xff, 0x35], + "adds r5, 0xff" + ); + test_display( + &[0xff, 0x36], + "adds r6, 0xff" + ); + test_display( + &[0xff, 0x37], + "adds r7, 0xff" + ); +} +#[test] +fn test_decode_pop_cases() { + test_display( + &[0x00, 0xbd], + "pop {pc}" + ); + test_display( + &[0x01, 0xbc], + "pop {r0}" + ); + test_display( + &[0x7f, 0xbd], + "pop {r0, r1, r2, r3, r4, r5, r6, pc}" + ); + test_display( + &[0xff, 0xbd], + "pop {r0, r1, r2, r3, r4, r5, r6, r7, pc}" + ); +} +#[test] +fn test_decode_push_cases() { + test_display( + &[0x00, 0xb5], + "push {lr}" + ); + test_display( + &[0x01, 0xb4], + "push {r0}" + ); + test_display( + &[0xff, 0xb5], + "push {r0, r1, r2, r3, r4, r5, r6, r7, lr}" + ); +} +#[test] +fn test_decode_rev_cases() { + test_display( + &[0x0a, 0xba], + "rev r2, r1" + ); + test_display( + &[0x4a, 0xba], + "rev16 r2, r1" + ); + test_display( + &[0xca, 0xba], + "revsh r2, r1" + ); +} +#[test] +fn test_decode_stm_16b_cases() { + test_display( + &[0x01, 0xc0], + "stmia r0!, {r0}" + ); + test_display( + &[0x01, 0xc1], + "stmia r1!, {r0}" + ); + test_display( + &[0x01, 0xc2], + "stmia r2!, {r0}" + ); + test_display( + &[0x01, 0xc3], + "stmia r3!, {r0}" + ); + test_display( + &[0x01, 0xc4], + "stmia r4!, {r0}" + ); + test_display( + &[0x01, 0xc5], + "stmia r5!, {r0}" + ); + test_display( + &[0x01, 0xc6], + "stmia r6!, {r0}" + ); + test_display( + &[0x01, 0xc7], + "stmia r7!, {r0}" + ); + test_display( + &[0xff, 0xc3], + "stmia r3!, {r0, r1, r2, r3, r4, r5, r6, r7}" + ); +} +#[test] +fn test_decode_str_16b_cases() { + test_display( + &[0x00, 0x50], + "str r0, [r0, r0]" + ); + test_display( + &[0x00, 0x51], + "str r0, [r0, r4]" + ); + test_display( + &[0x00, 0x52], + "strh r0, [r0, r0]" + ); + test_display( + &[0x00, 0x53], + "strh r0, [r0, r4]" + ); + test_display( + &[0x00, 0x54], + "strb r0, [r0, r0]" + ); + test_display( + &[0x00, 0x55], + "strb r0, [r0, r4]" + ); + test_display( + &[0xfe, 0x60], + "str r6, [r7, 0xc]" + ); + test_display( + &[0xfe, 0x61], + "str r6, [r7, 0x1c]" + ); + test_display( + &[0xfe, 0x62], + "str r6, [r7, 0x2c]" + ); + test_display( + &[0xfe, 0x63], + "str r6, [r7, 0x3c]" + ); + test_display( + &[0xfe, 0x64], + "str r6, [r7, 0x4c]" + ); + test_display( + &[0xfe, 0x65], + "str r6, [r7, 0x5c]" + ); + test_display( + &[0xfe, 0x66], + "str r6, [r7, 0x6c]" + ); + test_display( + &[0xfe, 0x67], + "str r6, [r7, 0x7c]" + ); + test_display( + &[0xfe, 0x70], + "strb r6, [r7, 0x3]" + ); + test_display( + &[0xfe, 0x71], + "strb r6, [r7, 0x7]" + ); + test_display( + &[0xfe, 0x72], + "strb r6, [r7, 0xb]" + ); + test_display( + &[0xfe, 0x73], + "strb r6, [r7, 0xf]" + ); + test_display( + &[0xfe, 0x74], + "strb r6, [r7, 0x13]" + ); + test_display( + &[0xfe, 0x75], + "strb r6, [r7, 0x17]" + ); + test_display( + &[0xfe, 0x76], + "strb r6, [r7, 0x1b]" + ); + test_display( + &[0xfe, 0x77], + "strb r6, [r7, 0x1f]" + ); + test_display( + &[0xfe, 0x80], + "strh r6, [r7, 0x6]" + ); + test_display( + &[0xfe, 0x81], + "strh r6, [r7, 0xe]" + ); + test_display( + &[0xfe, 0x82], + "strh r6, [r7, 0x16]" + ); + test_display( + &[0xfe, 0x83], + "strh r6, [r7, 0x1e]" + ); + test_display( + &[0xfe, 0x84], + "strh r6, [r7, 0x26]" + ); + test_display( + &[0xfe, 0x85], + "strh r6, [r7, 0x2e]" + ); + test_display( + &[0xfe, 0x86], + "strh r6, [r7, 0x36]" + ); + test_display( + &[0xfe, 0x87], + "strh r6, [r7, 0x3e]" + ); + test_display( + &[0xfe, 0x90], + "str r0, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x91], + "str r1, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x92], + "str r2, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x93], + "str r3, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x94], + "str r4, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x95], + "str r5, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x96], + "str r6, [sp, 0x3f8]" + ); + test_display( + &[0xfe, 0x97], + "str r7, [sp, 0x3f8]" + ); + test_display( + &[0xff, 0x50], + "str r7, [r7, r3]" + ); + test_display( + &[0xff, 0x90], + "str r0, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x91], + "str r1, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x92], + "str r2, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x93], + "str r3, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x94], + "str r4, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x95], + "str r5, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x96], + "str r6, [sp, 0x3fc]" + ); + test_display( + &[0xff, 0x97], + "str r7, [sp, 0x3fc]" + ); +} +#[test] +fn test_decode_sub_cases() { + test_display( + &[0xd8, 0xb0], + "sub sp, sp, 0x160" + ); +} +#[test] +fn test_decode_svc_cases() { + test_display( + &[0x27, 0xdf], + "svc 0x27" + ); + test_display( + &[0xad, 0xdf], + "svc 0xad" + ); +} +#[test] +fn test_decode_udf_cases() { + test_display( + &[0x27, 0xde], + "udf 0x27" + ); + test_display( + &[0xad, 0xde], + "udf 0xad" + ); +} +#[test] +fn test_decode_ux_sx_cases() { + test_display( + &[0x0a, 0xb2], + "sxth r2, r1" + ); + test_display( + &[0x4a, 0xb2], + "sxtb r2, r1" + ); + test_display( + &[0x8a, 0xb2], + "uxth r2, r1" + ); + test_display( + &[0xca, 0xb2], + "uxtb r2, r1" + ); +} -- cgit v1.1