use alloc::vec::Vec;
use core::fmt;
use super::isa::{Decoded, Insn};
use super::thumb::Thumb;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Missing {
Untranslated,
Unmapped,
}
impl fmt::Display for Missing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Missing::Untranslated => f.write_str("not mapped"),
Missing::Unmapped => f.write_str("no memory"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Listed {
Arm {
addr: u32,
insn: Decoded,
},
Thumb {
addr: u32,
raw: u16,
insn: Thumb,
},
Unreadable {
addr: u32,
thumb: bool,
why: Missing,
},
}
impl Listed {
#[must_use]
pub const fn addr(&self) -> u32 {
match *self {
Listed::Arm { addr, .. }
| Listed::Thumb { addr, .. }
| Listed::Unreadable { addr, .. } => addr,
}
}
#[must_use]
pub const fn byte_len(&self) -> u32 {
match *self {
Listed::Arm { .. } => 4,
Listed::Thumb { .. } => 2,
Listed::Unreadable { thumb, .. } => {
if thumb {
2
} else {
4
}
}
}
}
#[must_use]
pub const fn is_thumb(&self) -> bool {
matches!(
self,
Listed::Thumb { .. } | Listed::Unreadable { thumb: true, .. }
)
}
#[must_use]
pub const fn branch_target(&self) -> Option<u32> {
match *self {
Listed::Arm { addr, insn } => match insn.insn {
Insn::Branch { offset, .. } | Insn::BlxImm { offset } => {
Some(addr.wrapping_add(8).wrapping_add(offset as u32))
}
_ => None,
},
Listed::Thumb { addr, insn, .. } => match insn {
Thumb::Branch { offset } | Thumb::BranchCond { offset, .. } => {
Some(addr.wrapping_add(4).wrapping_add(offset as u32))
}
_ => None,
},
Listed::Unreadable { .. } => None,
}
}
}
impl fmt::Display for Listed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Listed::Arm { addr, insn } => {
write!(f, "{addr:08x}: {:08x} ", insn.raw)?;
match self.branch_target() {
Some(target) => match insn.insn {
Insn::Branch { link, .. } => {
let l = if link { "L" } else { "" };
write!(f, "B{l}{} 0x{target:08x}", insn.cond)
}
_ => write!(f, "BLX 0x{target:08x}"),
},
None => write!(f, "{insn}"),
}
}
Listed::Thumb { addr, raw, insn } => {
write!(f, "{addr:08x}: {raw:04x} ")?;
match (self.branch_target(), insn) {
(Some(target), Thumb::BranchCond { cond, .. }) => {
write!(f, "B{cond} 0x{target:08x}")
}
(Some(target), _) => write!(f, "B 0x{target:08x}"),
(None, _) => write!(f, "{insn}"),
}
}
Listed::Unreadable { addr, why, .. } => write!(f, "{addr:08x}: ?? <{why}>"),
}
}
}
#[must_use]
pub fn disassemble_arm(addr: u32, word: u32) -> Listed {
Listed::Arm {
addr,
insn: super::isa::decode(word),
}
}
#[must_use]
pub fn disassemble_thumb(addr: u32, half: u16) -> Listed {
Listed::Thumb {
addr,
raw: half,
insn: super::thumb::decode(half),
}
}
pub fn disassemble_run(
addr: u32,
count: usize,
thumb: bool,
mut read: impl FnMut(u32) -> Result<u8, Missing>,
) -> Vec<Listed> {
let mut out = Vec::with_capacity(count);
let mut at = addr;
for _ in 0..count {
let width = if thumb { 2 } else { 4 };
let mut word = 0u32;
let mut missing = None;
for i in 0..width {
match read(at.wrapping_add(i)) {
Ok(byte) => word |= u32::from(byte) << (8 * i),
Err(why) => missing = missing.or(Some(why)),
}
}
let listed = if let Some(why) = missing {
Listed::Unreadable {
addr: at,
thumb,
why,
}
} else if thumb {
disassemble_thumb(at, word as u16)
} else {
disassemble_arm(at, word)
};
at = at.wrapping_add(listed.byte_len());
out.push(listed);
}
out
}