aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-11-29 20:40:45 -0800
committeriximeow <me@iximeow.net>2020-12-06 11:58:57 -0800
commit72f9a2500b2f2b65e61a5d19b3606225084c896d (patch)
tree9bf181831cb752442a82ab273bd7f7c6beb2bb14
parent27fbbff209e9ae781126a7f44b2813ea0bfde49f (diff)
add thumb/thumb2 decoding
16-bit instructions only, for now
-rw-r--r--src/armv7.rs272
-rw-r--r--src/armv7/thumb.rs3103
-rw-r--r--test/armv7.rs10
-rw-r--r--test/armv7/thumb.rs3396
4 files changed, 6777 insertions, 4 deletions
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<C: fmt::Display, Y: YaxColors<C>>(reg: Reg, colors: &Y) ->
impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u32, NoContext, Color, T, Y> for Instruction {
fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
match self.opcode {
+ Opcode::IT => {
+ if let (Operand::Imm32(cond), Operand::Imm32(mask)) = (&self.operands[0], &self.operands[1]) {
+ let inv = cond & 1 == 1;
+ let condition = ConditionCode::build(*cond as u8);
+ if mask & 0b0001 != 0 {
+ // three flags
+ write!(
+ out,
+ "it{}{}{} {}",
+ if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" },
+ if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" },
+ if inv ^ ((mask & 0b0010) != 0) { "e" } else { "t" },
+ condition,
+ )?;
+ } else if mask & 0b0010 != 0 {
+ // two flags
+ write!(
+ out,
+ "it{}{} {}",
+ if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" },
+ if inv ^ ((mask & 0b0100) != 0) { "e" } else { "t" },
+ condition,
+ )?;
+ } else if mask & 0b0100 != 0 {
+ // one flag
+ write!(
+ out,
+ "it{} {}",
+ if inv ^ ((mask & 0b1000) != 0) { "e" } else { "t" },
+ condition,
+ )?;
+ } else {
+ // no flags
+ write!(out, "it {}", condition)?;
+ }
+ // if the condition is AL, it won't get displayed. append it here.
+ if *cond == 14 {
+ write!(out, "al")?;
+ }
+ return Ok(());
+ } else {
+ panic!("impossible it operand");
+ }
+ }
+ Opcode::CPS(_) => {
+ if let Operand::Imm12(aif) = &self.operands[0] {
+ 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 <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
fn colorize(&self, colors: &Y, out: &mut T) -> 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 <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::AND |
Opcode::EOR |
Opcode::ORR |
+ Opcode::ORN |
Opcode::LSL |
Opcode::LSR |
Opcode::ROR |
@@ -356,16 +430,58 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::SWPB |
Opcode::MSR |
Opcode::MRS |
+ Opcode::CLREX |
+ Opcode::SXTAB |
+ Opcode::SXTAB16 |
+ Opcode::SXTAH |
+ Opcode::SXTB |
+ Opcode::SXTB16 |
+ Opcode::SXTH |
+ Opcode::UXTAB |
+ Opcode::UXTAB16 |
+ Opcode::UXTAH |
+ Opcode::UXTB |
+ Opcode::UXTB16 |
+ Opcode::UXTH |
+ Opcode::PKHTB |
+ Opcode::PKHBT |
+ Opcode::REV |
+ Opcode::REV16 |
+ Opcode::REVSH |
+ Opcode::SSAT |
+ Opcode::SSAT16 |
+ Opcode::SBFX |
+ Opcode::USAT |
+ Opcode::USAT16 |
+ Opcode::UBFX |
+ Opcode::BFI |
+ Opcode::BFC |
Opcode::MOV |
Opcode::MOVT |
Opcode::MVN => { 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<Instruction> for InstDecoder {
type Error = DecodeError;
fn decode_into<T: IntoIterator<Item=u8>>(&self, inst: &mut Instruction, bytes: T) -> Result<(), Self::Error> {
+ if self.thumb {
+ return thumb::decode_into(&self, inst, bytes);
+ }
+
fn read_word<T: IntoIterator<Item=u8>>(bytes: T) -> Result<u32, DecodeError> {
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<T: IntoIterator<Item=u8>>(_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 `[<Rn>, #<imm>]`, 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 `[<Rn>, #<imm>]`, 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 `<Rm>, 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), // #<mode> ? what's the syntax here? #<the literal>?
+ 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), // #<mode> ? what's the syntax here? #<the literal>?
+ 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 <v7VE, `subs pc, lr, #0`
+ inst.opcode = Opcode::ERET;
+ inst.operands = [
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ } else {
+ // `SUBS PC, LR (Thumb)` (`B9-1996`)
+ // v6T2
+ inst.opcode = Opcode::SUB;
+ inst.s = true;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(15)), // pc
+ Operand::Reg(Reg::from_u8(14)), // lr
+ Operand::Imm12(lower & 0b11111111),
+ Operand::Nothing,
+ ];
+ }
+ } else {
+ // `op` is `0b0111110` or `0b0111111`, both are `MRS` but there's
+ // some discerning to do still.
+ let imm8 = lower & 0b11111111;
+ if imm8 & 0b00100000 != 0 {
+ // `MRS (Banked register)` (`B9-1978`)
+ // v7VE
+ let r = instr & 0b10000;
+ let sysm = (lower & 0b10000) | (instr & 0b1111);
+ let rd = ((lower >> 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: "<spec_reg>"?
+ 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: "<spec_reg>"?
+ 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 = <ARMv7 as Arch>::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 = <ARMv7 as Arch>::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"
+ );
+}