use core::fmt;
#[inline]
#[must_use]
pub const fn field(word: u32, hi: u32, lo: u32) -> u32 {
(word >> lo) & ((1u32 << (hi - lo + 1)) - 1)
}
#[inline]
#[must_use]
pub const fn bit(word: u32, n: u32) -> bool {
word & (1 << n) != 0
}
#[inline]
#[must_use]
pub const fn sign_extend(value: u32, bits: u32) -> i32 {
let shift = 32 - bits;
((value << shift) as i32) >> shift
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Cond(pub u8);
impl Cond {
pub const EQ: Cond = Cond(0);
pub const AL: Cond = Cond(0b1110);
#[must_use]
pub const fn passes(self, apsr: u32) -> bool {
let n = apsr & (1 << 31) != 0;
let z = apsr & (1 << 30) != 0;
let c = apsr & (1 << 29) != 0;
let v = apsr & (1 << 28) != 0;
let base = match self.0 >> 1 {
0b000 => z,
0b001 => c,
0b010 => n,
0b011 => v,
0b100 => c && !z,
0b101 => n == v,
0b110 => (n == v) && !z,
_ => true,
};
if self.0 & 1 != 0 && self.0 != 0b1111 {
!base
} else {
base
}
}
#[must_use]
pub const fn suffix(self) -> &'static str {
match self.0 & 0xf {
0b0000 => "EQ",
0b0001 => "NE",
0b0010 => "CS",
0b0011 => "CC",
0b0100 => "MI",
0b0101 => "PL",
0b0110 => "VS",
0b0111 => "VC",
0b1000 => "HI",
0b1001 => "LS",
0b1010 => "GE",
0b1011 => "LT",
0b1100 => "GT",
0b1101 => "LE",
_ => "",
}
}
}
impl fmt::Display for Cond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.suffix())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RegName(pub u8);
impl fmt::Display for RegName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 & 0xf {
13 => f.write_str("sp"),
14 => f.write_str("lr"),
15 => f.write_str("pc"),
n => write!(f, "r{n}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShiftType {
Lsl,
Lsr,
Asr,
Ror,
Rrx,
}
impl ShiftType {
#[must_use]
pub const fn from_bits(bits: u32) -> ShiftType {
match bits & 3 {
0 => ShiftType::Lsl,
1 => ShiftType::Lsr,
2 => ShiftType::Asr,
_ => ShiftType::Ror,
}
}
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
ShiftType::Lsl => "LSL",
ShiftType::Lsr => "LSR",
ShiftType::Asr => "ASR",
ShiftType::Ror => "ROR",
ShiftType::Rrx => "RRX",
}
}
}
impl fmt::Display for ShiftType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.mnemonic())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Shift {
pub ty: ShiftType,
pub amount: u8,
}
impl Shift {
pub const NONE: Shift = Shift {
ty: ShiftType::Lsl,
amount: 0,
};
#[must_use]
pub const fn is_none(self) -> bool {
matches!(self.ty, ShiftType::Lsl) && self.amount == 0
}
}
impl fmt::Display for Shift {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() {
return Ok(());
}
if self.ty == ShiftType::Rrx {
return f.write_str(", RRX");
}
write!(f, ", {} #{}", self.ty, self.amount)
}
}
#[must_use]
pub const fn decode_imm_shift(ty: u32, imm5: u32) -> Shift {
match ty & 3 {
0 => Shift {
ty: ShiftType::Lsl,
amount: imm5 as u8,
},
1 => Shift {
ty: ShiftType::Lsr,
amount: if imm5 == 0 { 32 } else { imm5 as u8 },
},
2 => Shift {
ty: ShiftType::Asr,
amount: if imm5 == 0 { 32 } else { imm5 as u8 },
},
_ => {
if imm5 == 0 {
Shift {
ty: ShiftType::Rrx,
amount: 1,
}
} else {
Shift {
ty: ShiftType::Ror,
amount: imm5 as u8,
}
}
}
}
}
#[must_use]
pub const fn thumb_expand_imm(imm12: u32) -> (u32, Option<bool>) {
if field(imm12, 11, 10) == 0 {
let byte = imm12 & 0xff;
let value = match field(imm12, 9, 8) {
0b00 => byte,
0b01 => (byte << 16) | byte,
0b10 => (byte << 24) | (byte << 8),
_ => (byte << 24) | (byte << 16) | (byte << 8) | byte,
};
(value, None)
} else {
let unrotated = 0x80 | (imm12 & 0x7f);
let value = unrotated.rotate_right(field(imm12, 11, 7));
(value, Some(value & 0x8000_0000 != 0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DpOp {
And,
Bic,
Orr,
Orn,
Eor,
Add,
Adc,
Sbc,
Sub,
Rsb,
Mov,
Mvn,
Tst,
Teq,
Cmp,
Cmn,
}
impl DpOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
DpOp::And => "AND",
DpOp::Bic => "BIC",
DpOp::Orr => "ORR",
DpOp::Orn => "ORN",
DpOp::Eor => "EOR",
DpOp::Add => "ADD",
DpOp::Adc => "ADC",
DpOp::Sbc => "SBC",
DpOp::Sub => "SUB",
DpOp::Rsb => "RSB",
DpOp::Mov => "MOV",
DpOp::Mvn => "MVN",
DpOp::Tst => "TST",
DpOp::Teq => "TEQ",
DpOp::Cmp => "CMP",
DpOp::Cmn => "CMN",
}
}
#[must_use]
pub const fn is_test(self) -> bool {
matches!(self, DpOp::Tst | DpOp::Teq | DpOp::Cmp | DpOp::Cmn)
}
#[must_use]
pub const fn is_unary(self) -> bool {
matches!(self, DpOp::Mov | DpOp::Mvn)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Size {
Byte,
Half,
Word,
}
impl Size {
#[must_use]
pub const fn bytes(self) -> u32 {
match self {
Size::Byte => 1,
Size::Half => 2,
Size::Word => 4,
}
}
#[must_use]
pub const fn suffix(self) -> &'static str {
match self {
Size::Byte => "B",
Size::Half => "H",
Size::Word => "",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemOffset {
Imm(u32),
Reg {
rm: u8,
lsl: u8,
},
}
impl fmt::Display for MemOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MemOffset::Imm(v) => write!(f, "#{v}"),
MemOffset::Reg { rm, lsl: 0 } => write!(f, "{}", RegName(rm)),
MemOffset::Reg { rm, lsl } => write!(f, "{}, LSL #{lsl}", RegName(rm)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Operand {
Imm {
value: u32,
carry: Option<bool>,
},
Reg {
rm: u8,
shift: Shift,
},
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Operand::Imm { value, .. } => write!(f, "#{value}"),
Operand::Reg { rm, shift } => write!(f, "{}{shift}", RegName(rm)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HalfMulOp {
Smul,
Smla,
Smulw,
Smlaw,
Smlal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DualMulOp {
Smuad,
Smlad,
Smusd,
Smlsd,
Smmul,
Smmla,
Smmls,
Smlald,
Smlsld,
}
impl DualMulOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
DualMulOp::Smuad => "SMUAD",
DualMulOp::Smlad => "SMLAD",
DualMulOp::Smusd => "SMUSD",
DualMulOp::Smlsd => "SMLSD",
DualMulOp::Smmul => "SMMUL",
DualMulOp::Smmla => "SMMLA",
DualMulOp::Smmls => "SMMLS",
DualMulOp::Smlald => "SMLALD",
DualMulOp::Smlsld => "SMLSLD",
}
}
#[must_use]
pub const fn bit_is_round(self) -> bool {
matches!(self, DualMulOp::Smmul | DualMulOp::Smmla | DualMulOp::Smmls)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SatQOp {
Qadd,
Qsub,
Qdadd,
Qdsub,
}
impl SatQOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
SatQOp::Qadd => "QADD",
SatQOp::Qsub => "QSUB",
SatQOp::Qdadd => "QDADD",
SatQOp::Qdsub => "QDSUB",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SimdMode {
Signed,
SignedSat,
SignedHalve,
Unsigned,
UnsignedSat,
UnsignedHalve,
}
impl SimdMode {
#[must_use]
pub const fn prefix(self) -> &'static str {
match self {
SimdMode::Signed => "S",
SimdMode::SignedSat => "Q",
SimdMode::SignedHalve => "SH",
SimdMode::Unsigned => "U",
SimdMode::UnsignedSat => "UQ",
SimdMode::UnsignedHalve => "UH",
}
}
#[must_use]
pub const fn is_unsigned(self) -> bool {
matches!(
self,
SimdMode::Unsigned | SimdMode::UnsignedSat | SimdMode::UnsignedHalve
)
}
#[must_use]
pub const fn sets_ge(self) -> bool {
matches!(self, SimdMode::Signed | SimdMode::Unsigned)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SimdShape {
Add8,
Sub8,
Add16,
Sub16,
Asx,
Sax,
}
impl SimdShape {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
SimdShape::Add8 => "ADD8",
SimdShape::Sub8 => "SUB8",
SimdShape::Add16 => "ADD16",
SimdShape::Sub16 => "SUB16",
SimdShape::Asx => "ASX",
SimdShape::Sax => "SAX",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExtendOp {
Sxtb,
Sxth,
Sxtb16,
Uxtb,
Uxth,
Uxtb16,
}
impl ExtendOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
ExtendOp::Sxtb => "SXTB",
ExtendOp::Sxth => "SXTH",
ExtendOp::Sxtb16 => "SXTB16",
ExtendOp::Uxtb => "UXTB",
ExtendOp::Uxth => "UXTH",
ExtendOp::Uxtb16 => "UXTB16",
}
}
#[must_use]
pub const fn accumulating_mnemonic(self) -> &'static str {
match self {
ExtendOp::Sxtb => "SXTAB",
ExtendOp::Sxth => "SXTAH",
ExtendOp::Sxtb16 => "SXTAB16",
ExtendOp::Uxtb => "UXTAB",
ExtendOp::Uxth => "UXTAH",
ExtendOp::Uxtb16 => "UXTAB16",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MiscOp {
Clz,
Rbit,
Rev,
Rev16,
Revsh,
}
impl MiscOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
MiscOp::Clz => "CLZ",
MiscOp::Rbit => "RBIT",
MiscOp::Rev => "REV",
MiscOp::Rev16 => "REV16",
MiscOp::Revsh => "REVSH",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BitfieldOp {
Sbfx,
Ubfx,
Bfi,
Bfc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BarrierOp {
Dmb,
Dsb,
Isb,
}
impl BarrierOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
BarrierOp::Dmb => "DMB",
BarrierOp::Dsb => "DSB",
BarrierOp::Isb => "ISB",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HintOp {
Nop,
Yield,
Wfe,
Wfi,
Sev,
Dbg(u8),
Preload,
}
impl HintOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
HintOp::Nop => "NOP",
HintOp::Yield => "YIELD",
HintOp::Wfe => "WFE",
HintOp::Wfi => "WFI",
HintOp::Sev => "SEV",
HintOp::Dbg(_) => "DBG",
HintOp::Preload => "PLD",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Insn {
DataProc {
op: DpOp,
s: bool,
rd: u8,
rn: u8,
operand: Operand,
},
ShiftReg {
ty: ShiftType,
s: bool,
rd: u8,
rn: u8,
rm: u8,
},
Adr {
rd: u8,
imm: u32,
add: bool,
},
MovImm16 {
top: bool,
rd: u8,
imm: u16,
},
Branch {
cond: Option<Cond>,
offset: i32,
},
BranchLink {
offset: i32,
},
Bx {
rm: u8,
},
Blx {
rm: u8,
},
Cbz {
nonzero: bool,
rn: u8,
offset: u32,
},
TableBranch {
rn: u8,
rm: u8,
half: bool,
},
It {
cond: Cond,
mask: u8,
},
LoadStore {
load: bool,
size: Size,
signed: bool,
rt: u8,
rn: u8,
offset: MemOffset,
index: bool,
add: bool,
wback: bool,
unpriv: bool,
},
LoadLiteral {
size: Size,
signed: bool,
rt: u8,
imm: u32,
add: bool,
},
LoadStoreDual {
load: bool,
rt: u8,
rt2: u8,
rn: u8,
imm: u32,
index: bool,
add: bool,
wback: bool,
},
LoadStoreExclusive {
load: bool,
size: Size,
rd: u8,
rt: u8,
rn: u8,
imm: u32,
},
ClearExclusive,
LoadStoreMultiple {
load: bool,
rn: u8,
list: u16,
wback: bool,
before: bool,
},
Mul {
rd: u8,
rn: u8,
rm: u8,
ra: Option<u8>,
sub: bool,
s: bool,
},
MulLong {
signed: bool,
accumulate: bool,
rdlo: u8,
rdhi: u8,
rn: u8,
rm: u8,
umaal: bool,
},
Div {
signed: bool,
rd: u8,
rn: u8,
rm: u8,
},
HalfMul {
op: HalfMulOp,
rd: u8,
rn: u8,
rm: u8,
ra: u8,
x: bool,
y: bool,
},
DualMul {
op: DualMulOp,
rd: u8,
rn: u8,
rm: u8,
ra: u8,
x: bool,
},
Sat {
unsigned: bool,
halves: bool,
rd: u8,
rn: u8,
imm: u8,
shift: Shift,
},
SatQ {
op: SatQOp,
rd: u8,
rn: u8,
rm: u8,
},
Simd {
mode: SimdMode,
shape: SimdShape,
rd: u8,
rn: u8,
rm: u8,
},
Sel {
rd: u8,
rn: u8,
rm: u8,
},
Usad {
rd: u8,
rn: u8,
rm: u8,
ra: u8,
},
Pkh {
tb: bool,
rd: u8,
rn: u8,
rm: u8,
shift: Shift,
},
Extend {
op: ExtendOp,
rd: u8,
rn: u8,
rm: u8,
rotate: u8,
},
Misc {
op: MiscOp,
rd: u8,
rm: u8,
},
Bitfield {
op: BitfieldOp,
rd: u8,
rn: u8,
lsb: u8,
width: u8,
},
Mrs {
rd: u8,
sysm: u8,
},
Msr {
rn: u8,
sysm: u8,
mask: u8,
},
Cps {
enable: bool,
i: bool,
f: bool,
},
Barrier {
op: BarrierOp,
option: u8,
},
Hint {
op: HintOp,
},
Bkpt {
imm: u8,
},
Svc {
imm: u8,
},
Udf {
imm: u16,
},
Coproc {
cp: u8,
},
Undefined,
}
impl Insn {
#[must_use]
pub const fn width_of(first: u16) -> u32 {
if is_32bit(first) { 4 } else { 2 }
}
}
#[inline]
#[must_use]
pub const fn is_32bit(first: u16) -> bool {
matches!(first >> 11, 0b11101..=0b11111)
}
#[must_use]
pub fn decode(first: u16, second: u16) -> Insn {
if is_32bit(first) {
decode_32(first, second)
} else {
decode_16(first)
}
}
#[must_use]
#[allow(clippy::too_many_lines)] pub fn decode_16(raw: u16) -> Insn {
let w = u32::from(raw);
let rd = field(w, 2, 0) as u8;
let rn = field(w, 5, 3) as u8;
match field(w, 15, 10) {
0b000000..=0b001111 => decode_16_shift_add(w),
0b010000 => {
let rm = rn;
match field(w, 9, 6) {
0b0000 => dp_reg(DpOp::And, true, rd, rd, rm),
0b0001 => dp_reg(DpOp::Eor, true, rd, rd, rm),
0b0010 => Insn::ShiftReg {
ty: ShiftType::Lsl,
s: true,
rd,
rn: rd,
rm,
},
0b0011 => Insn::ShiftReg {
ty: ShiftType::Lsr,
s: true,
rd,
rn: rd,
rm,
},
0b0100 => Insn::ShiftReg {
ty: ShiftType::Asr,
s: true,
rd,
rn: rd,
rm,
},
0b0101 => dp_reg(DpOp::Adc, true, rd, rd, rm),
0b0110 => dp_reg(DpOp::Sbc, true, rd, rd, rm),
0b0111 => Insn::ShiftReg {
ty: ShiftType::Ror,
s: true,
rd,
rn: rd,
rm,
},
0b1000 => dp_reg(DpOp::Tst, true, 0, rd, rm),
0b1001 => Insn::DataProc {
op: DpOp::Rsb,
s: true,
rd,
rn: rm,
operand: Operand::Imm {
value: 0,
carry: None,
},
},
0b1010 => dp_reg(DpOp::Cmp, true, 0, rd, rm),
0b1011 => dp_reg(DpOp::Cmn, true, 0, rd, rm),
0b1100 => dp_reg(DpOp::Orr, true, rd, rd, rm),
0b1101 => Insn::Mul {
rd,
rn: rm,
rm: rd,
ra: None,
sub: false,
s: true,
},
0b1110 => dp_reg(DpOp::Bic, true, rd, rd, rm),
_ => dp_reg(DpOp::Mvn, true, rd, 0, rm),
}
}
0b010001 => {
let rm = field(w, 6, 3) as u8;
let hd = rd | if bit(w, 7) { 8 } else { 0 };
match field(w, 9, 8) {
0b00 => Insn::DataProc {
op: DpOp::Add,
s: false,
rd: hd,
rn: hd,
operand: Operand::Reg {
rm,
shift: Shift::NONE,
},
},
0b01 => Insn::DataProc {
op: DpOp::Cmp,
s: true,
rd: 0,
rn: hd,
operand: Operand::Reg {
rm,
shift: Shift::NONE,
},
},
0b10 => Insn::DataProc {
op: DpOp::Mov,
s: false,
rd: hd,
rn: 0,
operand: Operand::Reg {
rm,
shift: Shift::NONE,
},
},
_ => {
if bit(w, 7) {
Insn::Blx { rm }
} else {
Insn::Bx { rm }
}
}
}
}
0b010010 | 0b010011 => Insn::LoadLiteral {
size: Size::Word,
signed: false,
rt: field(w, 10, 8) as u8,
imm: field(w, 7, 0) * 4,
add: true,
},
0b010100..=0b010111 => {
let rm = field(w, 8, 6) as u8;
let (load, size, signed) = match field(w, 11, 9) {
0b000 => (false, Size::Word, false),
0b001 => (false, Size::Half, false),
0b010 => (false, Size::Byte, false),
0b011 => (true, Size::Byte, true),
0b100 => (true, Size::Word, false),
0b101 => (true, Size::Half, false),
0b110 => (true, Size::Byte, false),
_ => (true, Size::Half, true),
};
Insn::LoadStore {
load,
size,
signed,
rt: rd,
rn,
offset: MemOffset::Reg { rm, lsl: 0 },
index: true,
add: true,
wback: false,
unpriv: false,
}
}
0b011000..=0b100011 => {
let size = match field(w, 15, 12) {
0b0110 => Size::Word,
0b0111 => Size::Byte,
_ => Size::Half,
};
Insn::LoadStore {
load: bit(w, 11),
size,
signed: false,
rt: rd,
rn,
offset: MemOffset::Imm(field(w, 10, 6) * size.bytes()),
index: true,
add: true,
wback: false,
unpriv: false,
}
}
0b100100..=0b100111 => Insn::LoadStore {
load: bit(w, 11),
size: Size::Word,
signed: false,
rt: field(w, 10, 8) as u8,
rn: 13,
offset: MemOffset::Imm(field(w, 7, 0) * 4),
index: true,
add: true,
wback: false,
unpriv: false,
},
0b101000 | 0b101001 => Insn::Adr {
rd: field(w, 10, 8) as u8,
imm: field(w, 7, 0) * 4,
add: true,
},
0b101010 | 0b101011 => Insn::DataProc {
op: DpOp::Add,
s: false,
rd: field(w, 10, 8) as u8,
rn: 13,
operand: Operand::Imm {
value: field(w, 7, 0) * 4,
carry: None,
},
},
0b101100..=0b101111 => decode_16_misc(w),
0b110000 | 0b110001 => Insn::LoadStoreMultiple {
load: false,
rn: field(w, 10, 8) as u8,
list: (w & 0xff) as u16,
wback: true,
before: false,
},
0b110010 | 0b110011 => {
let rn = field(w, 10, 8) as u8;
let list = (w & 0xff) as u16;
Insn::LoadStoreMultiple {
load: true,
rn,
list,
wback: list & (1 << rn) == 0,
before: false,
}
}
0b110100..=0b110111 => match field(w, 11, 8) {
0b1110 => Insn::Udf {
imm: (w & 0xff) as u16,
},
0b1111 => Insn::Svc {
imm: (w & 0xff) as u8,
},
cond => Insn::Branch {
cond: Some(Cond(cond as u8)),
offset: sign_extend(w & 0xff, 8) * 2,
},
},
_ => Insn::Branch {
cond: None,
offset: sign_extend(w & 0x7ff, 11) * 2,
},
}
}
const fn dp_reg(op: DpOp, s: bool, rd: u8, rn: u8, rm: u8) -> Insn {
Insn::DataProc {
op,
s,
rd,
rn,
operand: Operand::Reg {
rm,
shift: Shift::NONE,
},
}
}
const fn dp_imm(op: DpOp, s: bool, rd: u8, rn: u8, value: u32) -> Insn {
Insn::DataProc {
op,
s,
rd,
rn,
operand: Operand::Imm { value, carry: None },
}
}
fn decode_16_shift_add(w: u32) -> Insn {
let rd = field(w, 2, 0) as u8;
let rn = field(w, 5, 3) as u8;
match field(w, 13, 9) {
0b00000..=0b01011 => {
let shift = decode_imm_shift(field(w, 12, 11), field(w, 10, 6));
Insn::DataProc {
op: DpOp::Mov,
s: true,
rd,
rn: 0,
operand: Operand::Reg { rm: rn, shift },
}
}
0b01100 => dp_reg(DpOp::Add, true, rd, rn, field(w, 8, 6) as u8),
0b01101 => dp_reg(DpOp::Sub, true, rd, rn, field(w, 8, 6) as u8),
0b01110 => dp_imm(DpOp::Add, true, rd, rn, field(w, 8, 6)),
0b01111 => dp_imm(DpOp::Sub, true, rd, rn, field(w, 8, 6)),
0b10000..=0b10011 => dp_imm(DpOp::Mov, true, field(w, 10, 8) as u8, 0, w & 0xff),
0b10100..=0b10111 => dp_imm(DpOp::Cmp, true, 0, field(w, 10, 8) as u8, w & 0xff),
0b11000..=0b11011 => {
let r = field(w, 10, 8) as u8;
dp_imm(DpOp::Add, true, r, r, w & 0xff)
}
_ => {
let r = field(w, 10, 8) as u8;
dp_imm(DpOp::Sub, true, r, r, w & 0xff)
}
}
}
fn decode_16_misc(w: u32) -> Insn {
let rd = field(w, 2, 0) as u8;
let rm = field(w, 5, 3) as u8;
match field(w, 11, 5) {
0b0000000..=0b0000011 => dp_imm(DpOp::Add, false, 13, 13, field(w, 6, 0) * 4),
0b0000100..=0b0000111 => dp_imm(DpOp::Sub, false, 13, 13, field(w, 6, 0) * 4),
0b0001000..=0b0001111
| 0b0011000..=0b0011111
| 0b1001000..=0b1001111
| 0b1011000..=0b1011111 => Insn::Cbz {
nonzero: bit(w, 11),
rn: rd,
offset: (field(w, 9, 9) << 6) | (field(w, 7, 3) << 1),
},
0b0010000 | 0b0010001 => extend16(ExtendOp::Sxth, rd, rm),
0b0010010 | 0b0010011 => extend16(ExtendOp::Sxtb, rd, rm),
0b0010100 | 0b0010101 => extend16(ExtendOp::Uxth, rd, rm),
0b0010110 | 0b0010111 => extend16(ExtendOp::Uxtb, rd, rm),
0b0100000..=0b0101111 => Insn::LoadStoreMultiple {
load: false,
rn: 13,
list: ((w & 0xff) as u16) | if bit(w, 8) { 0x4000 } else { 0 },
wback: true,
before: true,
},
0b0110011 => Insn::Cps {
enable: !bit(w, 4),
i: bit(w, 1),
f: bit(w, 0),
},
0b1010000 | 0b1010001 => Insn::Misc {
op: MiscOp::Rev,
rd,
rm,
},
0b1010010 | 0b1010011 => Insn::Misc {
op: MiscOp::Rev16,
rd,
rm,
},
0b1010110 | 0b1010111 => Insn::Misc {
op: MiscOp::Revsh,
rd,
rm,
},
0b1100000..=0b1101111 => Insn::LoadStoreMultiple {
load: true,
rn: 13,
list: ((w & 0xff) as u16) | if bit(w, 8) { 0x8000 } else { 0 },
wback: true,
before: false,
},
0b1110000..=0b1110111 => Insn::Bkpt {
imm: (w & 0xff) as u8,
},
0b1111000..=0b1111111 => {
let mask = field(w, 3, 0) as u8;
if mask != 0 {
Insn::It {
cond: Cond(field(w, 7, 4) as u8),
mask,
}
} else {
Insn::Hint {
op: match field(w, 7, 4) {
0b0000 => HintOp::Nop,
0b0001 => HintOp::Yield,
0b0010 => HintOp::Wfe,
0b0011 => HintOp::Wfi,
0b0100 => HintOp::Sev,
_ => HintOp::Nop,
},
}
}
}
_ => Insn::Undefined,
}
}
const fn extend16(op: ExtendOp, rd: u8, rm: u8) -> Insn {
Insn::Extend {
op,
rd,
rn: 15,
rm,
rotate: 0,
}
}
fn decode_32(hw1: u16, hw2: u16) -> Insn {
let a = u32::from(hw1);
let b = u32::from(hw2);
let op1 = field(a, 12, 11);
let op2 = field(a, 10, 4);
match op1 {
0b01 => {
if op2 & 0b1000000 != 0 {
Insn::Coproc {
cp: field(b, 11, 8) as u8,
}
} else if op2 & 0b1100100 == 0b0000000 {
decode_32_ldm_stm(a, b)
} else if op2 & 0b1100100 == 0b0000100 {
decode_32_dual_exclusive(a, b)
} else {
decode_32_dp_shifted(a, b)
}
}
0b10 => {
if bit(b, 15) {
decode_32_branch_misc(a, b)
} else if bit(a, 9) {
decode_32_dp_plain_imm(a, b)
} else {
decode_32_dp_modified_imm(a, b)
}
}
0b11 => {
if op2 & 0b1000000 != 0 {
Insn::Coproc {
cp: field(b, 11, 8) as u8,
}
} else if op2 & 0b1110001 == 0b0000000 {
decode_32_store_single(a, b)
} else if op2 & 0b1100111 == 0b0000001
|| op2 & 0b1100111 == 0b0000011
|| op2 & 0b1100111 == 0b0000101
{
decode_32_load_single(a, b)
} else if op2 & 0b1110000 == 0b0100000 {
decode_32_dp_register(a, b)
} else if op2 & 0b1111000 == 0b0110000 {
decode_32_multiply(a, b)
} else if op2 & 0b1111000 == 0b0111000 {
decode_32_long_multiply(a, b)
} else {
Insn::Undefined
}
}
_ => Insn::Undefined,
}
}
fn dp_op_from_bits(op: u32, s: bool, rd: u8, rn: u8) -> Option<(DpOp, u8, u8)> {
let (base, test, unary) = match op {
0b0000 => (DpOp::And, Some(DpOp::Tst), None),
0b0001 => (DpOp::Bic, None, None),
0b0010 => (DpOp::Orr, None, Some(DpOp::Mov)),
0b0011 => (DpOp::Orn, None, Some(DpOp::Mvn)),
0b0100 => (DpOp::Eor, Some(DpOp::Teq), None),
0b1000 => (DpOp::Add, Some(DpOp::Cmn), None),
0b1010 => (DpOp::Adc, None, None),
0b1011 => (DpOp::Sbc, None, None),
0b1101 => (DpOp::Sub, Some(DpOp::Cmp), None),
0b1110 => (DpOp::Rsb, None, None),
_ => return None,
};
if rd == 15
&& s
&& let Some(test) = test
{
return Some((test, 0, rn));
}
if rn == 15
&& let Some(unary) = unary
{
return Some((unary, rd, 0));
}
if rd == 15 {
return None;
}
Some((base, rd, rn))
}
fn decode_32_dp_modified_imm(a: u32, b: u32) -> Insn {
let s = bit(a, 4);
let rn = field(a, 3, 0) as u8;
let rd = field(b, 11, 8) as u8;
let imm12 = (field(a, 10, 10) << 11) | (field(b, 14, 12) << 8) | (b & 0xff);
let (value, carry) = thumb_expand_imm(imm12);
match dp_op_from_bits(field(a, 8, 5), s, rd, rn) {
Some((op, rd, rn)) => Insn::DataProc {
op,
s,
rd,
rn,
operand: Operand::Imm { value, carry },
},
None => Insn::Undefined,
}
}
fn decode_32_dp_plain_imm(a: u32, b: u32) -> Insn {
let rn = field(a, 3, 0) as u8;
let rd = field(b, 11, 8) as u8;
let i = field(a, 10, 10);
let imm3 = field(b, 14, 12);
let imm8 = b & 0xff;
let imm12 = (i << 11) | (imm3 << 8) | imm8;
let imm5 = (imm3 << 2) | field(b, 7, 6);
match field(a, 8, 4) {
0b00000 => {
if rn == 15 {
Insn::Adr {
rd,
imm: imm12,
add: true,
}
} else {
dp_imm(DpOp::Add, false, rd, rn, imm12)
}
}
0b00100 => Insn::MovImm16 {
top: false,
rd,
imm: ((field(a, 3, 0) << 12) | imm12) as u16,
},
0b01010 => {
if rn == 15 {
Insn::Adr {
rd,
imm: imm12,
add: false,
}
} else {
dp_imm(DpOp::Sub, false, rd, rn, imm12)
}
}
0b01100 => Insn::MovImm16 {
top: true,
rd,
imm: ((field(a, 3, 0) << 12) | imm12) as u16,
},
0b10000 | 0b10010 => {
let sh = u32::from(bit(a, 5));
if sh == 1 && imm5 == 0 {
Insn::Sat {
unsigned: false,
halves: true,
rd,
rn,
imm: (field(b, 4, 0) + 1) as u8,
shift: Shift::NONE,
}
} else {
Insn::Sat {
unsigned: false,
halves: false,
rd,
rn,
imm: (field(b, 4, 0) + 1) as u8,
shift: decode_imm_shift(if sh == 1 { 2 } else { 0 }, imm5),
}
}
}
0b10100 => Insn::Bitfield {
op: BitfieldOp::Sbfx,
rd,
rn,
lsb: imm5 as u8,
width: (field(b, 4, 0) + 1) as u8,
},
0b10110 => {
let msb = field(b, 4, 0);
if msb < imm5 {
return Insn::Undefined;
}
Insn::Bitfield {
op: if rn == 15 {
BitfieldOp::Bfc
} else {
BitfieldOp::Bfi
},
rd,
rn,
lsb: imm5 as u8,
width: (msb - imm5 + 1) as u8,
}
}
0b11000 | 0b11010 => {
let sh = u32::from(bit(a, 5));
if sh == 1 && imm5 == 0 {
Insn::Sat {
unsigned: true,
halves: true,
rd,
rn,
imm: field(b, 4, 0) as u8,
shift: Shift::NONE,
}
} else {
Insn::Sat {
unsigned: true,
halves: false,
rd,
rn,
imm: field(b, 4, 0) as u8,
shift: decode_imm_shift(if sh == 1 { 2 } else { 0 }, imm5),
}
}
}
0b11100 => Insn::Bitfield {
op: BitfieldOp::Ubfx,
rd,
rn,
lsb: imm5 as u8,
width: (field(b, 4, 0) + 1) as u8,
},
_ => Insn::Undefined,
}
}
fn decode_32_dp_shifted(a: u32, b: u32) -> Insn {
let s = bit(a, 4);
let rn = field(a, 3, 0) as u8;
let rd = field(b, 11, 8) as u8;
let rm = field(b, 3, 0) as u8;
let shift = decode_imm_shift(field(b, 5, 4), (field(b, 14, 12) << 2) | field(b, 7, 6));
let op = field(a, 8, 5);
if op == 0b0110 {
if s || bit(b, 4) {
return Insn::Undefined;
}
let tb = bit(b, 5);
return Insn::Pkh {
tb,
rd,
rn,
rm,
shift: if tb {
decode_imm_shift(2, (field(b, 14, 12) << 2) | field(b, 7, 6))
} else {
decode_imm_shift(0, (field(b, 14, 12) << 2) | field(b, 7, 6))
},
};
}
match dp_op_from_bits(op, s, rd, rn) {
Some((DpOp::Mov, rd, _)) => {
Insn::DataProc {
op: DpOp::Mov,
s,
rd,
rn: 0,
operand: Operand::Reg { rm, shift },
}
}
Some((op, rd, rn)) => Insn::DataProc {
op,
s,
rd,
rn,
operand: Operand::Reg { rm, shift },
},
None => Insn::Undefined,
}
}
fn decode_32_branch_misc(a: u32, b: u32) -> Insn {
let op = field(a, 10, 4);
let op1 = field(b, 14, 12);
let s = field(a, 10, 10);
let j1 = field(b, 13, 13);
let j2 = field(b, 11, 11);
let imm11 = b & 0x7ff;
match op1 {
0b000 | 0b010 => {
if op & 0b0111000 == 0b0111000 {
return decode_32_system(a, b, op);
}
let imm = (s << 20) | (j2 << 19) | (j1 << 18) | (field(a, 9, 6) << 12) | (imm11 << 1);
Insn::Branch {
cond: Some(Cond(field(a, 9, 6) as u8)),
offset: sign_extend(imm, 21),
}
}
0b001 | 0b011 => {
let i1 = 1 - (j1 ^ s);
let i2 = 1 - (j2 ^ s);
let imm = (s << 24) | (i1 << 23) | (i2 << 22) | (field(a, 9, 0) << 12) | (imm11 << 1);
Insn::Branch {
cond: None,
offset: sign_extend(imm, 25),
}
}
0b101 | 0b111 => {
let i1 = 1 - (j1 ^ s);
let i2 = 1 - (j2 ^ s);
let imm = (s << 24) | (i1 << 23) | (i2 << 22) | (field(a, 9, 0) << 12) | (imm11 << 1);
Insn::BranchLink {
offset: sign_extend(imm, 25),
}
}
_ => Insn::Undefined,
}
}
fn decode_32_system(a: u32, b: u32, op: u32) -> Insn {
match op {
0b0111000 | 0b0111001 => Insn::Msr {
rn: field(a, 3, 0) as u8,
sysm: (b & 0xff) as u8,
mask: field(b, 11, 10) as u8,
},
0b0111010 => {
if field(b, 10, 8) == 0 {
Insn::Hint {
op: match b & 0xff {
0x00 => HintOp::Nop,
0x01 => HintOp::Yield,
0x02 => HintOp::Wfe,
0x03 => HintOp::Wfi,
0x04 => HintOp::Sev,
v if v & 0xf0 == 0xf0 => HintOp::Dbg((v & 0xf) as u8),
_ => HintOp::Nop,
},
}
} else {
Insn::Undefined
}
}
0b0111011 => match field(b, 7, 4) {
0b0010 => Insn::ClearExclusive,
0b0100 => Insn::Barrier {
op: BarrierOp::Dsb,
option: field(b, 3, 0) as u8,
},
0b0101 => Insn::Barrier {
op: BarrierOp::Dmb,
option: field(b, 3, 0) as u8,
},
0b0110 => Insn::Barrier {
op: BarrierOp::Isb,
option: field(b, 3, 0) as u8,
},
_ => Insn::Undefined,
},
0b0111110 | 0b0111111 => Insn::Mrs {
rd: field(b, 11, 8) as u8,
sysm: (b & 0xff) as u8,
},
0b1111111 => Insn::Udf {
imm: ((field(a, 3, 0) << 12) | (b & 0xfff)) as u16,
},
_ => Insn::Undefined,
}
}
fn decode_32_ldm_stm(a: u32, b: u32) -> Insn {
let l = bit(a, 4);
let w = bit(a, 5);
let rn = field(a, 3, 0) as u8;
let list = b as u16;
match field(a, 8, 7) {
0b01 => Insn::LoadStoreMultiple {
load: l,
rn,
list,
wback: w && !(l && list & (1 << rn) != 0),
before: false,
},
0b10 => Insn::LoadStoreMultiple {
load: l,
rn,
list,
wback: w && !(l && list & (1 << rn) != 0),
before: true,
},
_ => Insn::Undefined,
}
}
fn decode_32_dual_exclusive(a: u32, b: u32) -> Insn {
let p = bit(a, 8);
let u = bit(a, 7);
let w = bit(a, 5);
let l = bit(a, 4);
let rn = field(a, 3, 0) as u8;
let rt = field(b, 15, 12) as u8;
let rt2 = field(b, 11, 8) as u8;
if !p && !w {
if !u {
return Insn::LoadStoreExclusive {
load: l,
size: Size::Word,
rd: rt2,
rt,
rn,
imm: (b & 0xff) * 4,
};
}
return match (l, field(b, 7, 4)) {
(false, 0b0100) => Insn::LoadStoreExclusive {
load: false,
size: Size::Byte,
rd: field(b, 3, 0) as u8,
rt,
rn,
imm: 0,
},
(false, 0b0101) => Insn::LoadStoreExclusive {
load: false,
size: Size::Half,
rd: field(b, 3, 0) as u8,
rt,
rn,
imm: 0,
},
(true, 0b0000) => Insn::TableBranch {
rn,
rm: field(b, 3, 0) as u8,
half: false,
},
(true, 0b0001) => Insn::TableBranch {
rn,
rm: field(b, 3, 0) as u8,
half: true,
},
(true, 0b0100) => Insn::LoadStoreExclusive {
load: true,
size: Size::Byte,
rd: 0,
rt,
rn,
imm: 0,
},
(true, 0b0101) => Insn::LoadStoreExclusive {
load: true,
size: Size::Half,
rd: 0,
rt,
rn,
imm: 0,
},
_ => Insn::Undefined,
};
}
Insn::LoadStoreDual {
load: l,
rt,
rt2,
rn,
imm: (b & 0xff) * 4,
index: p,
add: u,
wback: w,
}
}
fn decode_32_store_single(a: u32, b: u32) -> Insn {
let size = match field(a, 6, 5) {
0b00 => Size::Byte,
0b01 => Size::Half,
0b10 => Size::Word,
_ => return Insn::Undefined,
};
let rn = field(a, 3, 0) as u8;
let rt = field(b, 15, 12) as u8;
if rn == 15 {
return Insn::Undefined;
}
if bit(a, 7) {
return Insn::LoadStore {
load: false,
size,
signed: false,
rt,
rn,
offset: MemOffset::Imm(b & 0xfff),
index: true,
add: true,
wback: false,
unpriv: false,
};
}
if field(b, 11, 6) == 0 {
return Insn::LoadStore {
load: false,
size,
signed: false,
rt,
rn,
offset: MemOffset::Reg {
rm: field(b, 3, 0) as u8,
lsl: field(b, 5, 4) as u8,
},
index: true,
add: true,
wback: false,
unpriv: false,
};
}
if !bit(b, 11) {
return Insn::Undefined;
}
let (p, u, wb) = (bit(b, 10), bit(b, 9), bit(b, 8));
Insn::LoadStore {
load: false,
size,
signed: false,
rt,
rn,
offset: MemOffset::Imm(b & 0xff),
index: p,
add: u,
wback: wb,
unpriv: p && u && !wb,
}
}
fn decode_32_load_single(a: u32, b: u32) -> Insn {
let signed = bit(a, 8);
let size = match field(a, 6, 5) {
0b00 => Size::Byte,
0b01 => Size::Half,
0b10 => Size::Word,
_ => return Insn::Undefined,
};
if signed && size == Size::Word {
return Insn::Undefined;
}
let rn = field(a, 3, 0) as u8;
let rt = field(b, 15, 12) as u8;
if rt == 15 && size != Size::Word {
return Insn::Hint {
op: HintOp::Preload,
};
}
if rn == 15 {
return Insn::LoadLiteral {
size,
signed,
rt,
imm: b & 0xfff,
add: bit(a, 7),
};
}
if bit(a, 7) {
return Insn::LoadStore {
load: true,
size,
signed,
rt,
rn,
offset: MemOffset::Imm(b & 0xfff),
index: true,
add: true,
wback: false,
unpriv: false,
};
}
if field(b, 11, 6) == 0 {
return Insn::LoadStore {
load: true,
size,
signed,
rt,
rn,
offset: MemOffset::Reg {
rm: field(b, 3, 0) as u8,
lsl: field(b, 5, 4) as u8,
},
index: true,
add: true,
wback: false,
unpriv: false,
};
}
if !bit(b, 11) {
return Insn::Undefined;
}
let (p, u, wb) = (bit(b, 10), bit(b, 9), bit(b, 8));
Insn::LoadStore {
load: true,
size,
signed,
rt,
rn,
offset: MemOffset::Imm(b & 0xff),
index: p,
add: u,
wback: wb,
unpriv: p && u && !wb,
}
}
fn decode_32_dp_register(a: u32, b: u32) -> Insn {
let rn = field(a, 3, 0) as u8;
let rd = field(b, 11, 8) as u8;
let rm = field(b, 3, 0) as u8;
let op1 = field(a, 7, 4);
let op2 = field(b, 7, 4);
if field(b, 15, 12) != 0b1111 {
return Insn::Undefined;
}
if op2 == 0 && op1 & 0b1000 == 0 {
return Insn::ShiftReg {
ty: ShiftType::from_bits(field(a, 6, 5)),
s: bit(a, 4),
rd,
rn,
rm,
};
}
if op1 & 0b1000 == 0 && op2 & 0b1000 != 0 {
let op = match op1 & 0b0111 {
0b000 => ExtendOp::Sxth,
0b001 => ExtendOp::Uxth,
0b010 => ExtendOp::Sxtb16,
0b011 => ExtendOp::Uxtb16,
0b100 => ExtendOp::Sxtb,
0b101 => ExtendOp::Uxtb,
_ => return Insn::Undefined,
};
return Insn::Extend {
op,
rd,
rn,
rm,
rotate: (field(b, 5, 4) * 8) as u8,
};
}
if op1 & 0b1000 != 0 && op2 & 0b1000 == 0 {
let mode = match field(b, 6, 4) {
0b000 => SimdMode::Signed,
0b001 => SimdMode::SignedSat,
0b010 => SimdMode::SignedHalve,
0b100 => SimdMode::Unsigned,
0b101 => SimdMode::UnsignedSat,
0b110 => SimdMode::UnsignedHalve,
_ => return Insn::Undefined,
};
let shape = match field(a, 6, 4) {
0b000 => SimdShape::Add8,
0b001 => SimdShape::Add16,
0b010 => SimdShape::Asx,
0b100 => SimdShape::Sub8,
0b101 => SimdShape::Sub16,
0b110 => SimdShape::Sax,
_ => return Insn::Undefined,
};
return Insn::Simd {
mode,
shape,
rd,
rn,
rm,
};
}
if field(a, 7, 6) == 0b10 && field(b, 7, 6) == 0b10 {
return match (field(a, 5, 4), field(b, 5, 4)) {
(0b00, 0b00) => Insn::SatQ {
op: SatQOp::Qadd,
rd,
rn,
rm,
},
(0b00, 0b01) => Insn::SatQ {
op: SatQOp::Qdadd,
rd,
rn,
rm,
},
(0b00, 0b10) => Insn::SatQ {
op: SatQOp::Qsub,
rd,
rn,
rm,
},
(0b00, 0b11) => Insn::SatQ {
op: SatQOp::Qdsub,
rd,
rn,
rm,
},
(0b01, 0b00) => Insn::Misc {
op: MiscOp::Rev,
rd,
rm,
},
(0b01, 0b01) => Insn::Misc {
op: MiscOp::Rev16,
rd,
rm,
},
(0b01, 0b10) => Insn::Misc {
op: MiscOp::Rbit,
rd,
rm,
},
(0b01, 0b11) => Insn::Misc {
op: MiscOp::Revsh,
rd,
rm,
},
(0b10, 0b00) => Insn::Sel { rd, rn, rm },
(0b11, 0b00) => Insn::Misc {
op: MiscOp::Clz,
rd,
rm,
},
_ => Insn::Undefined,
};
}
Insn::Undefined
}
fn decode_32_multiply(a: u32, b: u32) -> Insn {
let rn = field(a, 3, 0) as u8;
let ra = field(b, 15, 12) as u8;
let rd = field(b, 11, 8) as u8;
let rm = field(b, 3, 0) as u8;
let op2 = field(b, 5, 4);
match field(a, 6, 4) {
0b000 => match op2 {
0b00 => Insn::Mul {
rd,
rn,
rm,
ra: if ra == 15 { None } else { Some(ra) },
sub: false,
s: false,
},
0b01 => Insn::Mul {
rd,
rn,
rm,
ra: Some(ra),
sub: true,
s: false,
},
_ => Insn::Undefined,
},
0b001 => Insn::HalfMul {
op: if ra == 15 {
HalfMulOp::Smul
} else {
HalfMulOp::Smla
},
rd,
rn,
rm,
ra,
x: bit(b, 5),
y: bit(b, 4),
},
0b010 if op2 & 0b10 == 0 => Insn::DualMul {
op: if ra == 15 {
DualMulOp::Smuad
} else {
DualMulOp::Smlad
},
rd,
rn,
rm,
ra,
x: bit(b, 4),
},
0b011 if op2 & 0b10 == 0 => Insn::HalfMul {
op: if ra == 15 {
HalfMulOp::Smulw
} else {
HalfMulOp::Smlaw
},
rd,
rn,
rm,
ra,
x: false,
y: bit(b, 4),
},
0b100 if op2 & 0b10 == 0 => Insn::DualMul {
op: if ra == 15 {
DualMulOp::Smusd
} else {
DualMulOp::Smlsd
},
rd,
rn,
rm,
ra,
x: bit(b, 4),
},
0b101 if op2 & 0b10 == 0 => Insn::DualMul {
op: if ra == 15 {
DualMulOp::Smmul
} else {
DualMulOp::Smmla
},
rd,
rn,
rm,
ra,
x: bit(b, 4),
},
0b110 if op2 & 0b10 == 0 => Insn::DualMul {
op: DualMulOp::Smmls,
rd,
rn,
rm,
ra,
x: bit(b, 4),
},
0b111 if op2 == 0 => Insn::Usad { rd, rn, rm, ra },
_ => Insn::Undefined,
}
}
fn decode_32_long_multiply(a: u32, b: u32) -> Insn {
let rn = field(a, 3, 0) as u8;
let rdlo = field(b, 15, 12) as u8;
let rdhi = field(b, 11, 8) as u8;
let rm = field(b, 3, 0) as u8;
let op2 = field(b, 7, 4);
match (field(a, 6, 4), op2) {
(0b000, 0b0000) => Insn::MulLong {
signed: true,
accumulate: false,
rdlo,
rdhi,
rn,
rm,
umaal: false,
},
(0b001, 0b1111) => Insn::Div {
signed: true,
rd: rdhi,
rn,
rm,
},
(0b010, 0b0000) => Insn::MulLong {
signed: false,
accumulate: false,
rdlo,
rdhi,
rn,
rm,
umaal: false,
},
(0b011, 0b1111) => Insn::Div {
signed: false,
rd: rdhi,
rn,
rm,
},
(0b100, 0b0000) => Insn::MulLong {
signed: true,
accumulate: true,
rdlo,
rdhi,
rn,
rm,
umaal: false,
},
(0b100, 0b1000..=0b1011) => Insn::HalfMul {
op: HalfMulOp::Smlal,
rd: rdhi,
rn,
rm,
ra: rdlo,
x: bit(b, 5),
y: bit(b, 4),
},
(0b100, 0b1100 | 0b1101) => Insn::DualMul {
op: DualMulOp::Smlald,
rd: rdhi,
rn,
rm,
ra: rdlo,
x: bit(b, 4),
},
(0b101, 0b1100 | 0b1101) => Insn::DualMul {
op: DualMulOp::Smlsld,
rd: rdhi,
rn,
rm,
ra: rdlo,
x: bit(b, 4),
},
(0b110, 0b0000) => Insn::MulLong {
signed: false,
accumulate: true,
rdlo,
rdhi,
rn,
rm,
umaal: false,
},
(0b110, 0b0110) => Insn::MulLong {
signed: false,
accumulate: true,
rdlo,
rdhi,
rn,
rm,
umaal: true,
},
_ => Insn::Undefined,
}
}
struct RegList(u16);
impl fmt::Display for RegList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("{")?;
let mut first = true;
let mut i = 0u8;
while i < 16 {
if self.0 & (1 << i) == 0 {
i += 1;
continue;
}
let start = i;
while i < 16 && self.0 & (1 << i) != 0 {
i += 1;
}
let end = i - 1;
if !first {
f.write_str(", ")?;
}
first = false;
match end - start {
0 => write!(f, "{}", RegName(start))?,
1 => write!(f, "{}, {}", RegName(start), RegName(end))?,
_ => write!(f, "{}-{}", RegName(start), RegName(end))?,
}
}
f.write_str("}")
}
}
struct Address<'a> {
rn: u8,
offset: &'a MemOffset,
index: bool,
add: bool,
wback: bool,
}
impl fmt::Display for Address<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sign = if self.add { "" } else { "-" };
let zero = matches!(*self.offset, MemOffset::Imm(0));
if self.index {
if zero {
write!(f, "[{}]", RegName(self.rn))?;
} else {
write!(f, "[{}, {sign}{}]", RegName(self.rn), self.offset)?;
}
if self.wback {
f.write_str("!")?;
}
Ok(())
} else {
write!(f, "[{}], {sign}{}", RegName(self.rn), self.offset)
}
}
}
impl fmt::Display for Insn {
#[allow(clippy::too_many_lines)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = |on: bool| if on { "S" } else { "" };
match *self {
Insn::DataProc {
op,
s: sf,
rd,
rn,
operand,
} => {
if op == DpOp::Mov
&& let Operand::Reg { rm, shift } = operand
&& !shift.is_none()
{
if shift.ty == ShiftType::Rrx {
return write!(f, "RRX{} {}, {}", s(sf), RegName(rd), RegName(rm));
}
return write!(
f,
"{}{} {}, {}, #{}",
shift.ty,
s(sf),
RegName(rd),
RegName(rm),
shift.amount
);
}
if op.is_test() {
return write!(f, "{} {}, {operand}", op.mnemonic(), RegName(rn));
}
if op.is_unary() {
return write!(f, "{}{} {}, {operand}", op.mnemonic(), s(sf), RegName(rd));
}
write!(
f,
"{}{} {}, {}, {operand}",
op.mnemonic(),
s(sf),
RegName(rd),
RegName(rn)
)
}
Insn::ShiftReg {
ty,
s: sf,
rd,
rn,
rm,
} => write!(
f,
"{ty}{} {}, {}, {}",
s(sf),
RegName(rd),
RegName(rn),
RegName(rm)
),
Insn::Adr { rd, imm, add } => {
let sign = if add { "" } else { "-" };
write!(f, "ADR {}, {sign}#{imm}", RegName(rd))
}
Insn::MovImm16 { top, rd, imm } => write!(
f,
"{} {}, #{imm:#x}",
if top { "MOVT" } else { "MOVW" },
RegName(rd)
),
Insn::Branch { cond, offset } => match cond {
Some(c) => write!(f, "B{c} {offset:+}"),
None => write!(f, "B {offset:+}"),
},
Insn::BranchLink { offset } => write!(f, "BL {offset:+}"),
Insn::Bx { rm } => write!(f, "BX {}", RegName(rm)),
Insn::Blx { rm } => write!(f, "BLX {}", RegName(rm)),
Insn::Cbz {
nonzero,
rn,
offset,
} => write!(
f,
"{} {}, +{offset}",
if nonzero { "CBNZ" } else { "CBZ" },
RegName(rn)
),
Insn::TableBranch { rn, rm, half } => {
if half {
write!(f, "TBH [{}, {}, LSL #1]", RegName(rn), RegName(rm))
} else {
write!(f, "TBB [{}, {}]", RegName(rn), RegName(rm))
}
}
Insn::It { cond, mask } => {
f.write_str("IT")?;
let last = mask.trailing_zeros();
let mut i = 3u32;
while i > last {
let same = u32::from(mask >> i) & 1 == u32::from(cond.0) & 1;
f.write_str(if same { "T" } else { "E" })?;
i -= 1;
}
write!(f, " {cond}")
}
Insn::LoadStore {
load,
size,
signed,
rt,
rn,
offset,
index,
add,
wback,
unpriv,
} => write!(
f,
"{}{}{}{} {}, {}",
if load { "LDR" } else { "STR" },
if signed { "S" } else { "" },
size.suffix(),
if unpriv { "T" } else { "" },
RegName(rt),
Address {
rn,
offset: &offset,
index,
add,
wback
}
),
Insn::LoadLiteral {
size,
signed,
rt,
imm,
add,
} => write!(
f,
"LDR{}{} {}, [pc, {}#{imm}]",
if signed { "S" } else { "" },
size.suffix(),
RegName(rt),
if add { "" } else { "-" }
),
Insn::LoadStoreDual {
load,
rt,
rt2,
rn,
imm,
index,
add,
wback,
} => write!(
f,
"{}D {}, {}, {}",
if load { "LDR" } else { "STR" },
RegName(rt),
RegName(rt2),
Address {
rn,
offset: &MemOffset::Imm(imm),
index,
add,
wback
}
),
Insn::LoadStoreExclusive {
load,
size,
rd,
rt,
rn,
imm,
} => {
if load {
write!(
f,
"LDREX{} {}, [{}, #{imm}]",
size.suffix(),
RegName(rt),
RegName(rn)
)
} else {
write!(
f,
"STREX{} {}, {}, [{}, #{imm}]",
size.suffix(),
RegName(rd),
RegName(rt),
RegName(rn)
)
}
}
Insn::ClearExclusive => f.write_str("CLREX"),
Insn::LoadStoreMultiple {
load,
rn,
list,
wback,
before,
} => {
if rn == 13 && wback && load && !before {
return write!(f, "POP {}", RegList(list));
}
if rn == 13 && wback && !load && before {
return write!(f, "PUSH {}", RegList(list));
}
write!(
f,
"{}{} {}{}, {}",
if load { "LDM" } else { "STM" },
if before { "DB" } else { "IA" },
RegName(rn),
if wback { "!" } else { "" },
RegList(list)
)
}
Insn::Mul {
rd,
rn,
rm,
ra,
sub,
s: sf,
} => match ra {
None => write!(
f,
"MUL{} {}, {}, {}",
s(sf),
RegName(rd),
RegName(rn),
RegName(rm)
),
Some(ra) => write!(
f,
"{} {}, {}, {}, {}",
if sub { "MLS" } else { "MLA" },
RegName(rd),
RegName(rn),
RegName(rm),
RegName(ra)
),
},
Insn::MulLong {
signed,
accumulate,
rdlo,
rdhi,
rn,
rm,
umaal,
} => {
let name = if umaal {
"UMAAL"
} else {
match (signed, accumulate) {
(true, false) => "SMULL",
(true, true) => "SMLAL",
(false, false) => "UMULL",
(false, true) => "UMLAL",
}
};
write!(
f,
"{name} {}, {}, {}, {}",
RegName(rdlo),
RegName(rdhi),
RegName(rn),
RegName(rm)
)
}
Insn::Div { signed, rd, rn, rm } => write!(
f,
"{} {}, {}, {}",
if signed { "SDIV" } else { "UDIV" },
RegName(rd),
RegName(rn),
RegName(rm)
),
Insn::HalfMul {
op,
rd,
rn,
rm,
ra,
x,
y,
} => {
let half = |top: bool| if top { "T" } else { "B" };
match op {
HalfMulOp::Smul => write!(
f,
"SMUL{}{} {}, {}, {}",
half(x),
half(y),
RegName(rd),
RegName(rn),
RegName(rm)
),
HalfMulOp::Smla => write!(
f,
"SMLA{}{} {}, {}, {}, {}",
half(x),
half(y),
RegName(rd),
RegName(rn),
RegName(rm),
RegName(ra)
),
HalfMulOp::Smulw => write!(
f,
"SMULW{} {}, {}, {}",
half(y),
RegName(rd),
RegName(rn),
RegName(rm)
),
HalfMulOp::Smlaw => write!(
f,
"SMLAW{} {}, {}, {}, {}",
half(y),
RegName(rd),
RegName(rn),
RegName(rm),
RegName(ra)
),
HalfMulOp::Smlal => write!(
f,
"SMLAL{}{} {}, {}, {}, {}",
half(x),
half(y),
RegName(ra),
RegName(rd),
RegName(rn),
RegName(rm)
),
}
}
Insn::DualMul {
op,
rd,
rn,
rm,
ra,
x,
} => {
let suffix = match (x, op.bit_is_round()) {
(false, _) => "",
(true, false) => "X",
(true, true) => "R",
};
match op {
DualMulOp::Smuad | DualMulOp::Smusd | DualMulOp::Smmul => write!(
f,
"{}{suffix} {}, {}, {}",
op.mnemonic(),
RegName(rd),
RegName(rn),
RegName(rm)
),
DualMulOp::Smlald | DualMulOp::Smlsld => write!(
f,
"{}{suffix} {}, {}, {}, {}",
op.mnemonic(),
RegName(ra),
RegName(rd),
RegName(rn),
RegName(rm)
),
_ => write!(
f,
"{}{suffix} {}, {}, {}, {}",
op.mnemonic(),
RegName(rd),
RegName(rn),
RegName(rm),
RegName(ra)
),
}
}
Insn::Sat {
unsigned,
halves,
rd,
rn,
imm,
shift,
} => write!(
f,
"{}SAT{} {}, #{imm}, {}{shift}",
if unsigned { "U" } else { "S" },
if halves { "16" } else { "" },
RegName(rd),
RegName(rn)
),
Insn::SatQ { op, rd, rn, rm } => write!(
f,
"{} {}, {}, {}",
op.mnemonic(),
RegName(rd),
RegName(rm),
RegName(rn)
),
Insn::Simd {
mode,
shape,
rd,
rn,
rm,
} => write!(
f,
"{}{} {}, {}, {}",
mode.prefix(),
shape.mnemonic(),
RegName(rd),
RegName(rn),
RegName(rm)
),
Insn::Sel { rd, rn, rm } => {
write!(f, "SEL {}, {}, {}", RegName(rd), RegName(rn), RegName(rm))
}
Insn::Usad { rd, rn, rm, ra } => {
if ra == 15 {
write!(f, "USAD8 {}, {}, {}", RegName(rd), RegName(rn), RegName(rm))
} else {
write!(
f,
"USADA8 {}, {}, {}, {}",
RegName(rd),
RegName(rn),
RegName(rm),
RegName(ra)
)
}
}
Insn::Pkh {
tb,
rd,
rn,
rm,
shift,
} => write!(
f,
"PKH{} {}, {}, {}{shift}",
if tb { "TB" } else { "BT" },
RegName(rd),
RegName(rn),
RegName(rm)
),
Insn::Extend {
op,
rd,
rn,
rm,
rotate,
} => {
let rot = if rotate == 0 {
Rotate(None)
} else {
Rotate(Some(rotate))
};
if rn == 15 {
write!(f, "{} {}, {}{rot}", op.mnemonic(), RegName(rd), RegName(rm))
} else {
write!(
f,
"{} {}, {}, {}{rot}",
op.accumulating_mnemonic(),
RegName(rd),
RegName(rn),
RegName(rm)
)
}
}
Insn::Misc { op, rd, rm } => {
write!(f, "{} {}, {}", op.mnemonic(), RegName(rd), RegName(rm))
}
Insn::Bitfield {
op,
rd,
rn,
lsb,
width,
} => match op {
BitfieldOp::Sbfx => {
write!(f, "SBFX {}, {}, #{lsb}, #{width}", RegName(rd), RegName(rn))
}
BitfieldOp::Ubfx => {
write!(f, "UBFX {}, {}, #{lsb}, #{width}", RegName(rd), RegName(rn))
}
BitfieldOp::Bfi => {
write!(f, "BFI {}, {}, #{lsb}, #{width}", RegName(rd), RegName(rn))
}
BitfieldOp::Bfc => write!(f, "BFC {}, #{lsb}, #{width}", RegName(rd)),
},
Insn::Mrs { rd, sysm } => {
write!(f, "MRS {}, {}", RegName(rd), SysReg(sysm))
}
Insn::Msr { rn, sysm, .. } => {
write!(f, "MSR {}, {}", SysReg(sysm), RegName(rn))
}
Insn::Cps { enable, i, f: ff } => write!(
f,
"CPSI{} {}{}",
if enable { "E" } else { "D" },
if i { "i" } else { "" },
if ff { "f" } else { "" }
),
Insn::Barrier { op, option } => write!(f, "{} #{option}", op.mnemonic()),
Insn::Hint { op } => match op {
HintOp::Dbg(n) => write!(f, "DBG #{n}"),
other => f.write_str(other.mnemonic()),
},
Insn::Bkpt { imm } => write!(f, "BKPT #{imm}"),
Insn::Svc { imm } => write!(f, "SVC #{imm}"),
Insn::Udf { imm } => write!(f, "UDF #{imm}"),
Insn::Coproc { cp } => write!(f, "<coproc p{cp}>"),
Insn::Undefined => f.write_str("UNDEFINED"),
}
}
}
struct Rotate(Option<u8>);
impl fmt::Display for Rotate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
None => Ok(()),
Some(n) => write!(f, ", ROR #{n}"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SysReg(pub u8);
impl fmt::Display for SysReg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self.0 {
0 => "APSR",
1 => "IAPSR",
2 => "EAPSR",
3 => "XPSR",
5 => "IPSR",
6 => "EPSR",
7 => "IEPSR",
8 => "MSP",
9 => "PSP",
16 => "PRIMASK",
17 => "BASEPRI",
18 => "BASEPRI_MAX",
19 => "FAULTMASK",
20 => "CONTROL",
_ => return write!(f, "SYSm#{}", self.0),
})
}
}