use core::ptr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Instruction {
Arm(u32),
Thumb16(u16),
Thumb32([u16; 2]),
}
#[must_use]
pub const fn is_thumb32(halfword: u16) -> bool {
matches!(halfword >> 11, 0b11101..=0b11111)
}
impl Instruction {
#[must_use]
pub const fn is_thumb(self) -> bool {
matches!(self, Self::Thumb16(_) | Self::Thumb32(_))
}
#[must_use]
pub const fn size(self) -> usize {
match self {
Self::Arm(instr) => size_of_val(&instr),
Self::Thumb16(instr) => size_of_val(&instr),
Self::Thumb32(instr) => size_of_val(&instr),
}
}
#[must_use]
pub const fn as_usize(self) -> usize {
match self {
Self::Arm(i) => i as usize,
Self::Thumb16(i) => i as usize,
Self::Thumb32(i) => bytemuck::must_cast(i),
}
}
#[must_use]
pub unsafe fn read(addr: *const u32, thumb: bool) -> Self {
debug_assert!(!addr.is_null());
if thumb {
let addr = addr.cast::<u16>();
assert!(addr.is_aligned());
let hw1 = unsafe { ptr::read_volatile(addr) };
if is_thumb32(hw1) {
let hw2 = unsafe { ptr::read_volatile(addr.add(1)) };
Self::Thumb32([hw1, hw2])
} else {
Self::Thumb16(hw1)
}
} else {
assert!(addr.is_aligned());
Self::Arm(unsafe { ptr::read_volatile(addr) })
}
}
pub unsafe fn write_to(self, addr: *mut u32) {
debug_assert!(!addr.is_null());
match self {
Self::Arm(instr) => unsafe {
core::ptr::write_volatile(addr, instr);
},
Self::Thumb16(instr) => unsafe {
core::ptr::write_volatile(addr.cast(), instr);
},
Self::Thumb32(instr) => unsafe {
core::ptr::write_volatile(addr.cast(), instr);
},
}
}
}