use core::fmt;
#[inline]
#[must_use]
pub const fn field(word: u32, hi: u32, lo: u32) -> u32 {
(word >> lo) & (u32::MAX >> (31 - (hi - lo)))
}
#[inline]
#[must_use]
pub const fn bit(word: u32, n: u32) -> bool {
word & (1 << n) != 0
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Cond(pub u8);
impl Cond {
pub const EQ: Cond = Cond(0x0);
pub const NE: Cond = Cond(0x1);
pub const CS: Cond = Cond(0x2);
pub const CC: Cond = Cond(0x3);
pub const MI: Cond = Cond(0x4);
pub const PL: Cond = Cond(0x5);
pub const VS: Cond = Cond(0x6);
pub const VC: Cond = Cond(0x7);
pub const HI: Cond = Cond(0x8);
pub const LS: Cond = Cond(0x9);
pub const GE: Cond = Cond(0xa);
pub const LT: Cond = Cond(0xb);
pub const GT: Cond = Cond(0xc);
pub const LE: Cond = Cond(0xd);
pub const AL: Cond = Cond(0xe);
pub const NV: Cond = Cond(0xf);
#[must_use]
pub const fn name(self) -> &'static str {
match self.0 & 0xf {
0x0 => "EQ",
0x1 => "NE",
0x2 => "CS",
0x3 => "CC",
0x4 => "MI",
0x5 => "PL",
0x6 => "VS",
0x7 => "VC",
0x8 => "HI",
0x9 => "LS",
0xa => "GE",
0xb => "LT",
0xc => "GT",
0xd => "LE",
0xe => "",
_ => "NV",
}
}
#[must_use]
pub const fn passes(self, psr: u32) -> bool {
let n = bit(psr, 31);
let z = bit(psr, 30);
let c = bit(psr, 29);
let v = bit(psr, 28);
match self.0 & 0xf {
0x0 => z,
0x1 => !z,
0x2 => c,
0x3 => !c,
0x4 => n,
0x5 => !n,
0x6 => v,
0x7 => !v,
0x8 => c && !z,
0x9 => !c || z,
0xa => n == v,
0xb => n != v,
0xc => !z && n == v,
0xd => z || n != v,
0xe => true,
_ => false,
}
}
}
impl fmt::Display for Cond {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShiftType {
Lsl,
Lsr,
Asr,
Ror,
}
impl ShiftType {
#[must_use]
pub const fn from_bits(bits: u32) -> ShiftType {
match bits & 0b11 {
0 => ShiftType::Lsl,
1 => ShiftType::Lsr,
2 => ShiftType::Asr,
_ => ShiftType::Ror,
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
ShiftType::Lsl => "LSL",
ShiftType::Lsr => "LSR",
ShiftType::Asr => "ASR",
ShiftType::Ror => "ROR",
}
}
}
impl fmt::Display for ShiftType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Shift {
Imm {
ty: ShiftType,
amount: u8,
},
Reg {
ty: ShiftType,
rs: u8,
},
}
impl Shift {
#[must_use]
pub const fn is_none(self) -> bool {
matches!(
self,
Shift::Imm {
ty: ShiftType::Lsl,
amount: 0
}
)
}
}
impl fmt::Display for Shift {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Shift::Imm {
ty: ShiftType::Ror,
amount: 0,
} => f.write_str("RRX"),
Shift::Imm { ty, amount: 0 } => write!(f, "{ty} #32"),
Shift::Imm { ty, amount } => write!(f, "{ty} #{amount}"),
Shift::Reg { ty, rs } => write!(f, "{ty} {}", RegName(rs)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Operand {
Imm {
imm8: u8,
rotate: u8,
},
Reg {
rm: u8,
shift: Shift,
},
}
impl Operand {
#[must_use]
pub const fn immediate(self) -> Option<u32> {
match self {
Operand::Imm { imm8, rotate } => Some((imm8 as u32).rotate_right((rotate as u32) * 2)),
Operand::Reg { .. } => None,
}
}
#[must_use]
pub const fn is_register_shifted(self) -> bool {
matches!(
self,
Operand::Reg {
shift: Shift::Reg { .. },
..
}
)
}
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Operand::Imm { .. } => {
let v = self.immediate().unwrap_or(0);
write!(f, "#{v}")
}
Operand::Reg { rm, shift } if shift.is_none() => write!(f, "{}", RegName(rm)),
Operand::Reg { rm, shift } => write!(f, "{}, {shift}", RegName(rm)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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 DpOp {
And,
Eor,
Sub,
Rsb,
Add,
Adc,
Sbc,
Rsc,
Tst,
Teq,
Cmp,
Cmn,
Orr,
Mov,
Bic,
Mvn,
}
impl DpOp {
#[must_use]
pub const fn from_bits(bits: u32) -> DpOp {
match bits & 0xf {
0x0 => DpOp::And,
0x1 => DpOp::Eor,
0x2 => DpOp::Sub,
0x3 => DpOp::Rsb,
0x4 => DpOp::Add,
0x5 => DpOp::Adc,
0x6 => DpOp::Sbc,
0x7 => DpOp::Rsc,
0x8 => DpOp::Tst,
0x9 => DpOp::Teq,
0xa => DpOp::Cmp,
0xb => DpOp::Cmn,
0xc => DpOp::Orr,
0xd => DpOp::Mov,
0xe => DpOp::Bic,
_ => DpOp::Mvn,
}
}
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
DpOp::And => "AND",
DpOp::Eor => "EOR",
DpOp::Sub => "SUB",
DpOp::Rsb => "RSB",
DpOp::Add => "ADD",
DpOp::Adc => "ADC",
DpOp::Sbc => "SBC",
DpOp::Rsc => "RSC",
DpOp::Tst => "TST",
DpOp::Teq => "TEQ",
DpOp::Cmp => "CMP",
DpOp::Cmn => "CMN",
DpOp::Orr => "ORR",
DpOp::Mov => "MOV",
DpOp::Bic => "BIC",
DpOp::Mvn => "MVN",
}
}
#[must_use]
pub const fn writes_result(self) -> bool {
!matches!(self, DpOp::Tst | DpOp::Teq | DpOp::Cmp | DpOp::Cmn)
}
#[must_use]
pub const fn reads_rn(self) -> bool {
!matches!(self, DpOp::Mov | DpOp::Mvn)
}
#[must_use]
pub const fn is_logical(self) -> bool {
matches!(
self,
DpOp::And
| DpOp::Eor
| DpOp::Tst
| DpOp::Teq
| DpOp::Orr
| DpOp::Mov
| DpOp::Bic
| DpOp::Mvn
)
}
}
impl fmt::Display for DpOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.mnemonic())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Index {
Pre {
writeback: bool,
},
Post {
unprivileged: bool,
},
}
impl Index {
#[must_use]
pub const fn writes_base(self) -> bool {
match self {
Index::Pre { writeback } => writeback,
Index::Post { .. } => true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Offset {
Imm(u16),
Reg {
rm: u8,
shift: Shift,
},
}
impl Offset {
#[must_use]
pub const fn is_zero(self) -> bool {
matches!(self, Offset::Imm(0))
}
}
struct Addressing {
rn: u8,
up: bool,
index: Index,
offset: Offset,
}
impl fmt::Display for Addressing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sign = if self.up { "" } else { "-" };
let base = RegName(self.rn);
match self.index {
Index::Pre { writeback } if self.offset.is_zero() && !writeback => {
write!(f, "[{base}]")
}
Index::Pre { writeback } => {
write!(f, "[{base}, ")?;
write_offset(f, self.offset, sign)?;
f.write_str("]")?;
if writeback {
f.write_str("!")?;
}
Ok(())
}
Index::Post { .. } => {
write!(f, "[{base}], ")?;
write_offset(f, self.offset, sign)
}
}
}
}
fn write_offset(f: &mut fmt::Formatter<'_>, offset: Offset, sign: &str) -> fmt::Result {
match offset {
Offset::Imm(imm) => write!(f, "#{sign}{imm}"),
Offset::Reg { rm, shift } if shift.is_none() => write!(f, "{sign}{}", RegName(rm)),
Offset::Reg { rm, shift } => write!(f, "{sign}{}, {shift}", RegName(rm)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExtraOp {
Strh,
Ldrh,
Ldrsb,
Ldrsh,
Ldrd,
Strd,
}
impl ExtraOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
ExtraOp::Strh => "STRH",
ExtraOp::Ldrh => "LDRH",
ExtraOp::Ldrsb => "LDRSB",
ExtraOp::Ldrsh => "LDRSH",
ExtraOp::Ldrd => "LDRD",
ExtraOp::Strd => "STRD",
}
}
#[must_use]
pub const fn is_load(self) -> bool {
matches!(
self,
ExtraOp::Ldrh | ExtraOp::Ldrsb | ExtraOp::Ldrsh | ExtraOp::Ldrd
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SatOp {
QAdd,
QSub,
QDAdd,
QDSub,
}
impl SatOp {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
match self {
SatOp::QAdd => "QADD",
SatOp::QSub => "QSUB",
SatOp::QDAdd => "QDADD",
SatOp::QDSub => "QDSUB",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Half {
Bottom,
Top,
}
impl Half {
#[must_use]
pub const fn suffix(self) -> &'static str {
match self {
Half::Bottom => "B",
Half::Top => "T",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HalfMulOp {
Smla,
Smlaw,
Smulw,
Smlal,
Smul,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Insn {
DataProc {
op: DpOp,
s: bool,
rd: u8,
rn: u8,
operand: Operand,
},
Mrs {
rd: u8,
spsr: bool,
},
Msr {
spsr: bool,
mask: u8,
operand: Operand,
},
Bx {
rm: u8,
},
BlxReg {
rm: u8,
},
Clz {
rd: u8,
rm: u8,
},
Bkpt {
imm: u16,
},
Branch {
link: bool,
offset: i32,
},
BlxImm {
offset: i32,
},
Mul {
accumulate: bool,
s: bool,
rd: u8,
rn: u8,
rm: u8,
rs: u8,
},
MulLong {
signed: bool,
accumulate: bool,
s: bool,
rdhi: u8,
rdlo: u8,
rm: u8,
rs: u8,
},
Saturating {
op: SatOp,
rd: u8,
rm: u8,
rn: u8,
},
HalfMul {
op: HalfMulOp,
rd: u8,
rn: u8,
rm: u8,
rs: u8,
x: Half,
y: Half,
},
LoadStore {
load: bool,
byte: bool,
up: bool,
index: Index,
rn: u8,
rd: u8,
offset: Offset,
},
LoadStoreExtra {
op: ExtraOp,
up: bool,
index: Index,
rn: u8,
rd: u8,
offset: Offset,
},
BlockTransfer {
load: bool,
before: bool,
up: bool,
user: bool,
writeback: bool,
rn: u8,
list: u16,
},
Swap {
byte: bool,
rd: u8,
rn: u8,
rm: u8,
},
Swi {
imm: u32,
},
Pld {
rn: u8,
up: bool,
offset: Offset,
},
Cdp {
cp: u8,
opc1: u8,
crd: u8,
crn: u8,
crm: u8,
opc2: u8,
second: bool,
},
CpReg {
cp: u8,
load: bool,
opc1: u8,
rd: u8,
crn: u8,
crm: u8,
opc2: u8,
second: bool,
},
CpRegPair {
cp: u8,
load: bool,
opc: u8,
rd: u8,
rn: u8,
crm: u8,
},
CpTransfer {
cp: u8,
load: bool,
long: bool,
crd: u8,
rn: u8,
index: Index,
up: bool,
offset: u8,
second: bool,
},
Undefined,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Decoded {
pub raw: u32,
pub cond: Cond,
pub insn: Insn,
}
impl Decoded {
#[must_use]
pub const fn passes(&self, psr: u32) -> bool {
self.cond.passes(psr)
}
#[must_use]
pub const fn is_undefined(&self) -> bool {
matches!(self.insn, Insn::Undefined)
}
}
#[must_use]
pub fn decode(raw: u32) -> Decoded {
let cond = Cond(field(raw, 31, 28) as u8);
if cond == Cond::NV {
return Decoded {
raw,
cond: Cond::AL,
insn: decode_unconditional(raw),
};
}
Decoded {
raw,
cond,
insn: decode_conditional(raw),
}
}
fn decode_unconditional(raw: u32) -> Insn {
match field(raw, 27, 25) {
0b101 => {
let imm24 = field(raw, 23, 0);
let signed = ((imm24 << 8) as i32) >> 6;
let h = if bit(raw, 24) { 2 } else { 0 };
Insn::BlxImm { offset: signed | h }
}
0b010 | 0b011 if bit(raw, 24) && bit(raw, 22) && bit(raw, 20) && !bit(raw, 21) => {
decode_pld(raw)
}
0b110 => decode_coprocessor_transfer(raw, true),
0b111 if !bit(raw, 24) => decode_coprocessor_op(raw, true),
_ => Insn::Undefined,
}
}
fn decode_pld(raw: u32) -> Insn {
let offset = if bit(raw, 25) {
Offset::Reg {
rm: field(raw, 3, 0) as u8,
shift: Shift::Imm {
ty: ShiftType::from_bits(field(raw, 6, 5)),
amount: field(raw, 11, 7) as u8,
},
}
} else {
Offset::Imm(field(raw, 11, 0) as u16)
};
Insn::Pld {
rn: field(raw, 19, 16) as u8,
up: bit(raw, 23),
offset,
}
}
fn decode_conditional(raw: u32) -> Insn {
match field(raw, 27, 25) {
0b000 => decode_group_000(raw),
0b001 => decode_group_001(raw),
0b010 => decode_load_store(raw, false),
0b011 => {
if bit(raw, 4) {
Insn::Undefined
} else {
decode_load_store(raw, true)
}
}
0b100 => decode_block_transfer(raw),
0b101 => {
let imm24 = field(raw, 23, 0);
let offset = ((imm24 << 8) as i32) >> 6;
Insn::Branch {
link: bit(raw, 24),
offset,
}
}
0b110 => decode_coprocessor_transfer(raw, false),
_ => {
if bit(raw, 24) {
Insn::Swi {
imm: field(raw, 23, 0),
}
} else {
decode_coprocessor_op(raw, false)
}
}
}
}
fn decode_group_000(raw: u32) -> Insn {
if bit(raw, 7) && bit(raw, 4) {
return if field(raw, 7, 4) == 0b1001 {
decode_multiply_or_swap(raw)
} else {
decode_extra_load_store(raw)
};
}
if field(raw, 24, 23) == 0b10 && !bit(raw, 20) {
return decode_misc(raw);
}
decode_data_proc_reg(raw)
}
fn decode_multiply_or_swap(raw: u32) -> Insn {
match field(raw, 27, 23) {
0b00000 => Insn::Mul {
accumulate: bit(raw, 21),
s: bit(raw, 20),
rd: field(raw, 19, 16) as u8,
rn: field(raw, 15, 12) as u8,
rm: field(raw, 3, 0) as u8,
rs: field(raw, 11, 8) as u8,
},
0b00001 => Insn::MulLong {
signed: bit(raw, 22),
accumulate: bit(raw, 21),
s: bit(raw, 20),
rdhi: field(raw, 19, 16) as u8,
rdlo: field(raw, 15, 12) as u8,
rm: field(raw, 3, 0) as u8,
rs: field(raw, 11, 8) as u8,
},
0b00010 if field(raw, 21, 20) == 0 && field(raw, 11, 8) == 0 => Insn::Swap {
byte: bit(raw, 22),
rd: field(raw, 15, 12) as u8,
rn: field(raw, 19, 16) as u8,
rm: field(raw, 3, 0) as u8,
},
_ => Insn::Undefined,
}
}
fn decode_extra_load_store(raw: u32) -> Insn {
let load = bit(raw, 20);
let op = match (load, field(raw, 6, 5)) {
(false, 0b01) => ExtraOp::Strh,
(false, 0b10) => ExtraOp::Ldrd,
(false, 0b11) => ExtraOp::Strd,
(true, 0b01) => ExtraOp::Ldrh,
(true, 0b10) => ExtraOp::Ldrsb,
(true, 0b11) => ExtraOp::Ldrsh,
_ => return Insn::Undefined,
};
let offset = if bit(raw, 22) {
Offset::Imm(((field(raw, 11, 8) << 4) | field(raw, 3, 0)) as u16)
} else {
Offset::Reg {
rm: field(raw, 3, 0) as u8,
shift: Shift::Imm {
ty: ShiftType::Lsl,
amount: 0,
},
}
};
let index = if bit(raw, 24) {
Index::Pre {
writeback: bit(raw, 21),
}
} else {
Index::Post {
unprivileged: false,
}
};
Insn::LoadStoreExtra {
op,
up: bit(raw, 23),
index,
rn: field(raw, 19, 16) as u8,
rd: field(raw, 15, 12) as u8,
offset,
}
}
fn decode_misc(raw: u32) -> Insn {
let op = field(raw, 22, 21);
if bit(raw, 7) {
let x = if bit(raw, 5) { Half::Top } else { Half::Bottom };
let y = if bit(raw, 6) { Half::Top } else { Half::Bottom };
let op = match op {
0b00 => HalfMulOp::Smla,
0b01 if bit(raw, 5) => HalfMulOp::Smulw,
0b01 => HalfMulOp::Smlaw,
0b10 => HalfMulOp::Smlal,
_ => HalfMulOp::Smul,
};
return Insn::HalfMul {
op,
rd: field(raw, 19, 16) as u8,
rn: field(raw, 15, 12) as u8,
rm: field(raw, 3, 0) as u8,
rs: field(raw, 11, 8) as u8,
x,
y,
};
}
match field(raw, 7, 4) {
0b0000 if op & 0b01 == 0 => Insn::Mrs {
rd: field(raw, 15, 12) as u8,
spsr: bit(raw, 22),
},
0b0000 => Insn::Msr {
spsr: bit(raw, 22),
mask: field(raw, 19, 16) as u8,
operand: Operand::Reg {
rm: field(raw, 3, 0) as u8,
shift: Shift::Imm {
ty: ShiftType::Lsl,
amount: 0,
},
},
},
0b0001 if op == 0b01 => Insn::Bx {
rm: field(raw, 3, 0) as u8,
},
0b0001 if op == 0b11 => Insn::Clz {
rd: field(raw, 15, 12) as u8,
rm: field(raw, 3, 0) as u8,
},
0b0011 if op == 0b01 => Insn::BlxReg {
rm: field(raw, 3, 0) as u8,
},
0b0101 => Insn::Saturating {
op: match op {
0b00 => SatOp::QAdd,
0b01 => SatOp::QSub,
0b10 => SatOp::QDAdd,
_ => SatOp::QDSub,
},
rd: field(raw, 15, 12) as u8,
rm: field(raw, 3, 0) as u8,
rn: field(raw, 19, 16) as u8,
},
0b0111 if op == 0b01 => Insn::Bkpt {
imm: ((field(raw, 19, 8) << 4) | field(raw, 3, 0)) as u16,
},
_ => Insn::Undefined,
}
}
fn decode_data_proc_reg(raw: u32) -> Insn {
let ty = ShiftType::from_bits(field(raw, 6, 5));
let shift = if bit(raw, 4) {
Shift::Reg {
ty,
rs: field(raw, 11, 8) as u8,
}
} else {
Shift::Imm {
ty,
amount: field(raw, 11, 7) as u8,
}
};
Insn::DataProc {
op: DpOp::from_bits(field(raw, 24, 21)),
s: bit(raw, 20),
rd: field(raw, 15, 12) as u8,
rn: field(raw, 19, 16) as u8,
operand: Operand::Reg {
rm: field(raw, 3, 0) as u8,
shift,
},
}
}
fn decode_group_001(raw: u32) -> Insn {
let operand = Operand::Imm {
imm8: field(raw, 7, 0) as u8,
rotate: field(raw, 11, 8) as u8,
};
if field(raw, 24, 23) == 0b10 && !bit(raw, 20) {
return if bit(raw, 21) {
Insn::Msr {
spsr: bit(raw, 22),
mask: field(raw, 19, 16) as u8,
operand,
}
} else {
Insn::Undefined
};
}
Insn::DataProc {
op: DpOp::from_bits(field(raw, 24, 21)),
s: bit(raw, 20),
rd: field(raw, 15, 12) as u8,
rn: field(raw, 19, 16) as u8,
operand,
}
}
fn decode_load_store(raw: u32, register_offset: bool) -> Insn {
let offset = if register_offset {
Offset::Reg {
rm: field(raw, 3, 0) as u8,
shift: Shift::Imm {
ty: ShiftType::from_bits(field(raw, 6, 5)),
amount: field(raw, 11, 7) as u8,
},
}
} else {
Offset::Imm(field(raw, 11, 0) as u16)
};
let index = if bit(raw, 24) {
Index::Pre {
writeback: bit(raw, 21),
}
} else {
Index::Post {
unprivileged: bit(raw, 21),
}
};
Insn::LoadStore {
load: bit(raw, 20),
byte: bit(raw, 22),
up: bit(raw, 23),
index,
rn: field(raw, 19, 16) as u8,
rd: field(raw, 15, 12) as u8,
offset,
}
}
fn decode_block_transfer(raw: u32) -> Insn {
Insn::BlockTransfer {
load: bit(raw, 20),
before: bit(raw, 24),
up: bit(raw, 23),
user: bit(raw, 22),
writeback: bit(raw, 21),
rn: field(raw, 19, 16) as u8,
list: field(raw, 15, 0) as u16,
}
}
fn decode_coprocessor_transfer(raw: u32, second: bool) -> Insn {
if field(raw, 24, 21) == 0b0010 {
return Insn::CpRegPair {
cp: field(raw, 11, 8) as u8,
load: bit(raw, 20),
opc: field(raw, 7, 4) as u8,
rd: field(raw, 15, 12) as u8,
rn: field(raw, 19, 16) as u8,
crm: field(raw, 3, 0) as u8,
};
}
let index = if bit(raw, 24) {
Index::Pre {
writeback: bit(raw, 21),
}
} else if bit(raw, 21) {
Index::Post {
unprivileged: false,
}
} else {
Index::Pre { writeback: false }
};
Insn::CpTransfer {
cp: field(raw, 11, 8) as u8,
load: bit(raw, 20),
long: bit(raw, 22),
crd: field(raw, 15, 12) as u8,
rn: field(raw, 19, 16) as u8,
index,
up: bit(raw, 23),
offset: field(raw, 7, 0) as u8,
second,
}
}
fn decode_coprocessor_op(raw: u32, second: bool) -> Insn {
if bit(raw, 4) {
Insn::CpReg {
cp: field(raw, 11, 8) as u8,
load: bit(raw, 20),
opc1: field(raw, 23, 21) as u8,
rd: field(raw, 15, 12) as u8,
crn: field(raw, 19, 16) as u8,
crm: field(raw, 3, 0) as u8,
opc2: field(raw, 7, 5) as u8,
second,
}
} else {
Insn::Cdp {
cp: field(raw, 11, 8) as u8,
opc1: field(raw, 23, 20) as u8,
crd: field(raw, 15, 12) as u8,
crn: field(raw, 19, 16) as u8,
crm: field(raw, 3, 0) as u8,
opc2: field(raw, 7, 5) as u8,
second,
}
}
}
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("}")
}
}
const fn block_suffix(before: bool, up: bool) -> &'static str {
match (before, up) {
(false, true) => "IA",
(true, true) => "IB",
(false, false) => "DA",
(true, false) => "DB",
}
}
const fn msr_fields(mask: u8) -> [&'static str; 4] {
[
if mask & 0b0001 != 0 { "c" } else { "" },
if mask & 0b0010 != 0 { "x" } else { "" },
if mask & 0b0100 != 0 { "s" } else { "" },
if mask & 0b1000 != 0 { "f" } else { "" },
]
}
impl fmt::Display for Decoded {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let c = self.cond;
match self.insn {
Insn::DataProc {
op,
s,
rd,
rn,
operand,
} => {
let flag = if s && op.writes_result() { "S" } else { "" };
write!(f, "{op}{c}{flag} ")?;
if op.writes_result() {
write!(f, "{}, ", RegName(rd))?;
}
if op.reads_rn() {
write!(f, "{}, ", RegName(rn))?;
}
write!(f, "{operand}")
}
Insn::Mrs { rd, spsr } => {
let src = if spsr { "SPSR" } else { "CPSR" };
write!(f, "MRS{c} {}, {src}", RegName(rd))
}
Insn::Msr {
spsr,
mask,
operand,
} => {
let dst = if spsr { "SPSR" } else { "CPSR" };
let [a, b, cc, d] = msr_fields(mask);
write!(f, "MSR{c} {dst}_{a}{b}{cc}{d}, {operand}")
}
Insn::Bx { rm } => write!(f, "BX{c} {}", RegName(rm)),
Insn::BlxReg { rm } => write!(f, "BLX{c} {}", RegName(rm)),
Insn::Clz { rd, rm } => write!(f, "CLZ{c} {}, {}", RegName(rd), RegName(rm)),
Insn::Bkpt { imm } => write!(f, "BKPT #{imm}"),
Insn::Branch { link, offset } => {
let l = if link { "L" } else { "" };
write!(f, "B{l}{c} {offset:+}")
}
Insn::BlxImm { offset } => write!(f, "BLX {offset:+}"),
Insn::Mul {
accumulate,
s,
rd,
rn,
rm,
rs,
} => {
let flag = if s { "S" } else { "" };
if accumulate {
write!(
f,
"MLA{c}{flag} {}, {}, {}, {}",
RegName(rd),
RegName(rm),
RegName(rs),
RegName(rn)
)
} else {
write!(
f,
"MUL{c}{flag} {}, {}, {}",
RegName(rd),
RegName(rm),
RegName(rs)
)
}
}
Insn::MulLong {
signed,
accumulate,
s,
rdhi,
rdlo,
rm,
rs,
} => {
let sign = if signed { "S" } else { "U" };
let kind = if accumulate { "MLAL" } else { "MULL" };
let flag = if s { "S" } else { "" };
write!(
f,
"{sign}{kind}{c}{flag} {}, {}, {}, {}",
RegName(rdlo),
RegName(rdhi),
RegName(rm),
RegName(rs)
)
}
Insn::Saturating { op, rd, rm, rn } => write!(
f,
"{}{c} {}, {}, {}",
op.mnemonic(),
RegName(rd),
RegName(rm),
RegName(rn)
),
Insn::HalfMul {
op,
rd,
rn,
rm,
rs,
x,
y,
} => match op {
HalfMulOp::Smla => write!(
f,
"SMLA{}{}{c} {}, {}, {}, {}",
x.suffix(),
y.suffix(),
RegName(rd),
RegName(rm),
RegName(rs),
RegName(rn)
),
HalfMulOp::Smlaw => write!(
f,
"SMLAW{}{c} {}, {}, {}, {}",
y.suffix(),
RegName(rd),
RegName(rm),
RegName(rs),
RegName(rn)
),
HalfMulOp::Smulw => write!(
f,
"SMULW{}{c} {}, {}, {}",
y.suffix(),
RegName(rd),
RegName(rm),
RegName(rs)
),
HalfMulOp::Smlal => write!(
f,
"SMLAL{}{}{c} {}, {}, {}, {}",
x.suffix(),
y.suffix(),
RegName(rn),
RegName(rd),
RegName(rm),
RegName(rs)
),
HalfMulOp::Smul => write!(
f,
"SMUL{}{}{c} {}, {}, {}",
x.suffix(),
y.suffix(),
RegName(rd),
RegName(rm),
RegName(rs)
),
},
Insn::LoadStore {
load,
byte,
up,
index,
rn,
rd,
offset,
} => {
let name = if load { "LDR" } else { "STR" };
let b = if byte { "B" } else { "" };
let t = match index {
Index::Post {
unprivileged: true, ..
} => "T",
_ => "",
};
write!(
f,
"{name}{c}{b}{t} {}, {}",
RegName(rd),
Addressing {
rn,
up,
index,
offset
}
)
}
Insn::LoadStoreExtra {
op,
up,
index,
rn,
rd,
offset,
} => write!(
f,
"{}{c} {}, {}",
op.mnemonic(),
RegName(rd),
Addressing {
rn,
up,
index,
offset
}
),
Insn::BlockTransfer {
load,
before,
up,
user,
writeback,
rn,
list,
} => {
let name = if load { "LDM" } else { "STM" };
let mode = block_suffix(before, up);
let w = if writeback { "!" } else { "" };
let u = if user { "^" } else { "" };
write!(
f,
"{name}{c}{mode} {}{w}, {}{u}",
RegName(rn),
RegList(list)
)
}
Insn::Swap { byte, rd, rn, rm } => {
let b = if byte { "B" } else { "" };
write!(
f,
"SWP{c}{b} {}, {}, [{}]",
RegName(rd),
RegName(rm),
RegName(rn)
)
}
Insn::Swi { imm } => write!(f, "SWI{c} #{imm}"),
Insn::Pld { rn, up, offset } => write!(
f,
"PLD {}",
Addressing {
rn,
up,
index: Index::Pre { writeback: false },
offset
}
),
Insn::Cdp {
cp,
opc1,
crd,
crn,
crm,
opc2,
second,
} => {
let two = if second { "2" } else { "" };
let cond = if second { Cond::AL } else { c };
write!(
f,
"CDP{two}{cond} p{cp}, #{opc1}, c{crd}, c{crn}, c{crm}, #{opc2}"
)
}
Insn::CpReg {
cp,
load,
opc1,
rd,
crn,
crm,
opc2,
second,
} => {
let name = if load { "MRC" } else { "MCR" };
let two = if second { "2" } else { "" };
let cond = if second { Cond::AL } else { c };
write!(
f,
"{name}{two}{cond} p{cp}, #{opc1}, {}, c{crn}, c{crm}, #{opc2}",
RegName(rd)
)
}
Insn::CpRegPair {
cp,
load,
opc,
rd,
rn,
crm,
} => {
let name = if load { "MRRC" } else { "MCRR" };
write!(
f,
"{name}{c} p{cp}, #{opc}, {}, {}, c{crm}",
RegName(rd),
RegName(rn)
)
}
Insn::CpTransfer {
cp,
load,
long,
crd,
rn,
index,
up,
offset,
second,
} => {
let name = if load { "LDC" } else { "STC" };
let two = if second { "2" } else { "" };
let l = if long { "L" } else { "" };
let cond = if second { Cond::AL } else { c };
write!(
f,
"{name}{two}{cond}{l} p{cp}, c{crd}, {}",
Addressing {
rn,
up,
index,
offset: Offset::Imm(u16::from(offset) * 4),
}
)
}
Insn::Undefined => write!(f, "UNDEFINED ; 0x{:08x}", self.raw),
}
}
}