use crate::{Address, ControlFlow, Ins};
#[must_use = "located instructions carry address-dependent information"]
#[derive(Clone, Copy, Debug)]
pub struct LocatedIns<'a> {
instruction: &'a Ins,
address: u32,
}
impl Ins {
pub const fn at(&self, address: u32) -> LocatedIns<'_> {
LocatedIns { instruction: self, address }
}
pub const fn control_flow(&self) -> ControlFlow {
match self {
Self::Bt { .. } | Self::Bf { .. } => ControlFlow::Conditional { delay_slot: false },
#[cfg(feature = "sh2")]
Self::Bts { .. } | Self::Bfs { .. } => ControlFlow::Conditional { delay_slot: true },
Self::Bra { .. } => ControlFlow::Direct { call: false, delay_slot: true },
Self::Bsr { .. } => ControlFlow::Direct { call: true, delay_slot: true },
Self::JmpAtRn { .. } => ControlFlow::Indirect { call: false, delay_slot: true },
Self::JsrAtRn { .. } => ControlFlow::Indirect { call: true, delay_slot: true },
#[cfg(feature = "sh2")]
Self::BrafRn { .. } => ControlFlow::Indirect { call: false, delay_slot: true },
#[cfg(feature = "sh2")]
Self::BsrfRn { .. } => ControlFlow::Indirect { call: true, delay_slot: true },
Self::Rts => ControlFlow::Return { exception: false, delay_slot: true },
Self::Rte => ControlFlow::Return { exception: true, delay_slot: true },
Self::Trapa { .. } => ControlFlow::Trap,
_ => ControlFlow::Fallthrough,
}
}
pub const fn is_branch(&self) -> bool {
!matches!(self.control_flow(), ControlFlow::Fallthrough)
}
pub const fn has_delay_slot(&self) -> bool {
match self.control_flow() {
ControlFlow::Conditional { delay_slot }
| ControlFlow::Direct { delay_slot, .. }
| ControlFlow::Indirect { delay_slot, .. }
| ControlFlow::Return { delay_slot, .. } => delay_slot,
ControlFlow::Fallthrough | ControlFlow::Trap => false,
}
}
}
impl<'a> LocatedIns<'a> {
pub const fn instruction(self) -> &'a Ins {
self.instruction
}
pub const fn address(self) -> u32 {
self.address
}
pub fn branch_target(self) -> Option<Address> {
let address = self.address;
match self.instruction {
Ins::Bt { disp } | Ins::Bf { disp } => Some(Address::new(
address.wrapping_add(4).wrapping_add_signed(i32::from(disp.value()) * 2),
)),
Ins::Bra { disp } | Ins::Bsr { disp } => Some(Address::new(
address.wrapping_add(4).wrapping_add_signed(i32::from(disp.value()) * 2),
)),
#[cfg(feature = "sh2")]
Ins::Bts { disp } | Ins::Bfs { disp } => Some(Address::new(
address.wrapping_add(4).wrapping_add_signed(i32::from(disp.value()) * 2),
)),
_ => None,
}
}
pub fn pc_relative_address(self) -> Option<Address> {
let address = self.address;
match self.instruction {
Ins::MovwAtDispPcRn { disp, .. } => Some(Address::new(
address.wrapping_add(4).wrapping_add(u32::from(disp.value()) * 2),
)),
Ins::MovlAtDispPcRn { disp, .. } | Ins::Mova { disp } => Some(Address::new(
(address.wrapping_add(4) & !3).wrapping_add(u32::from(disp.value()) * 4),
)),
_ => None,
}
}
}