#[cfg(feature = "sh3")]
use crate::BankReg;
use crate::{Architecture, Reg};
#[cfg(feature = "sh4")]
use crate::{DReg, FReg, VecReg};
const MAX_RESOURCES: usize = 16;
const MAX_MEMORY_ACCESSES: usize = 3;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SystemReg {
Sr,
Gbr,
Vbr,
Ssr,
Spc,
Sgr,
Dbr,
Pr,
Mach,
Macl,
Fpul,
Fpscr,
Tra,
Expevt,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StatusBit {
T,
S,
Q,
M,
}
#[cfg(feature = "sh4")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FpuResource {
Fr(FReg),
Xf(FReg),
Dr(DReg),
Xd(DReg),
Vector(VecReg),
XVector(VecReg),
Matrix,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Resource {
Gp(Reg),
#[cfg(feature = "sh3")]
Bank(BankReg),
System(SystemReg),
Status(StatusBit),
#[cfg(feature = "sh4")]
Fpu(FpuResource),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResourceSet {
resources: [Option<Resource>; MAX_RESOURCES],
len: u8,
}
impl ResourceSet {
pub(crate) const fn new() -> Self {
Self { resources: [None; MAX_RESOURCES], len: 0 }
}
pub(crate) fn insert(&mut self, resource: Resource) {
if self.contains(resource) {
return;
}
let len = usize::from(self.len);
assert!(len < MAX_RESOURCES, "generated resource capacity exceeded");
self.resources[len] = Some(resource);
self.len += 1;
}
pub fn contains(&self, resource: Resource) -> bool {
self.iter().any(|candidate| candidate == resource)
}
pub fn iter(&self) -> impl Iterator<Item = Resource> + '_ {
self.resources[..usize::from(self.len)].iter().filter_map(|resource| *resource)
}
pub const fn len(&self) -> usize {
self.len as usize
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct FpscrState {
pub pr: Option<bool>,
pub sz: Option<bool>,
pub fr: Option<bool>,
}
impl FpscrState {
pub const fn new(pr: Option<bool>, sz: Option<bool>, fr: Option<bool>) -> Self {
Self { pr, sz, fr }
}
}
#[must_use = "effect contexts must be passed to instruction effect analysis"]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct EffectContext {
pub architecture: Architecture,
pub fpscr: FpscrState,
}
impl EffectContext {
pub const fn new(architecture: Architecture) -> Self {
Self { architecture, fpscr: FpscrState { pr: None, sz: None, fr: None } }
}
#[must_use = "builder methods return the updated effect context"]
pub const fn with_fpscr(mut self, fpscr: FpscrState) -> Self {
self.fpscr = fpscr;
self
}
}
impl Default for EffectContext {
fn default() -> Self {
Self::new(Architecture::default())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum ControlFlow {
#[default]
Fallthrough,
Conditional {
delay_slot: bool,
},
Direct {
call: bool,
delay_slot: bool,
},
Indirect {
call: bool,
delay_slot: bool,
},
Return {
exception: bool,
delay_slot: bool,
},
Trap,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum MemoryAccessKind {
Read,
Write,
ReadWrite,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AccessWidth {
Byte,
Word,
Long,
Quad,
FpscrSz,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AddressingMode {
Indirect,
PreDecrement,
PostIncrement,
Displacement,
PcRelative,
Gbr,
Indexed,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct MemoryAccess {
pub kind: MemoryAccessKind,
pub width: AccessWidth,
pub addressing: AddressingMode,
}
#[must_use = "instruction effects must be inspected to be useful"]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Effects {
must_read: ResourceSet,
may_read: ResourceSet,
must_write: ResourceSet,
may_write: ResourceSet,
memory: [Option<MemoryAccess>; MAX_MEMORY_ACCESSES],
memory_len: u8,
control_flow: ControlFlow,
}
impl Effects {
pub const fn must_read(&self) -> &ResourceSet {
&self.must_read
}
pub const fn may_read(&self) -> &ResourceSet {
&self.may_read
}
pub const fn must_write(&self) -> &ResourceSet {
&self.must_write
}
pub const fn may_write(&self) -> &ResourceSet {
&self.may_write
}
pub fn memory(&self) -> impl Iterator<Item = MemoryAccess> + '_ {
self.memory[..usize::from(self.memory_len)].iter().filter_map(|access| *access)
}
pub const fn control_flow(&self) -> ControlFlow {
self.control_flow
}
}
pub(crate) struct EffectsBuilder {
effects: Effects,
#[cfg_attr(not(feature = "sh4"), allow(dead_code))]
context: EffectContext,
}
impl EffectsBuilder {
pub(crate) const fn new(context: EffectContext, control_flow: ControlFlow) -> Self {
Self {
context,
effects: Effects {
must_read: ResourceSet::new(),
may_read: ResourceSet::new(),
must_write: ResourceSet::new(),
may_write: ResourceSet::new(),
memory: [None; MAX_MEMORY_ACCESSES],
memory_len: 0,
control_flow,
},
}
}
fn insert_read(&mut self, resource: Resource, definite: bool) {
self.effects.may_read.insert(resource);
if definite {
self.effects.must_read.insert(resource);
}
}
fn insert_write(&mut self, resource: Resource, definite: bool) {
self.effects.may_write.insert(resource);
if definite {
self.effects.must_write.insert(resource);
}
}
pub(crate) fn read(&mut self, resource: Resource) {
self.insert_read(resource, true);
}
pub(crate) fn write(&mut self, resource: Resource) {
self.insert_write(resource, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn read_freg(&mut self, reg: FReg) {
self.freg(reg, false, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn write_freg(&mut self, reg: FReg) {
self.freg(reg, true, true);
}
#[cfg(feature = "sh4")]
fn fpu(&mut self, resource: FpuResource, write: bool, definite: bool) {
if write {
self.insert_write(Resource::Fpu(resource), definite);
} else {
self.insert_read(Resource::Fpu(resource), definite);
}
}
#[cfg(feature = "sh4")]
fn freg(&mut self, reg: FReg, write: bool, definite: bool) {
let fr = self.context.fpscr.fr;
match fr {
Some(false) => self.fpu(FpuResource::Fr(reg), write, definite),
Some(true) => self.fpu(FpuResource::Xf(reg), write, definite),
None => {
self.fpu(FpuResource::Fr(reg), write, false);
self.fpu(FpuResource::Xf(reg), write, false);
}
}
}
#[cfg(feature = "sh4")]
fn dreg(&mut self, reg: DReg, write: bool, definite: bool) {
match self.context.fpscr.fr {
Some(false) => self.fpu(FpuResource::Dr(reg), write, definite),
Some(true) => self.fpu(FpuResource::Xd(reg), write, definite),
None => {
self.fpu(FpuResource::Dr(reg), write, false);
self.fpu(FpuResource::Xd(reg), write, false);
}
}
}
#[cfg(feature = "sh4")]
fn xdreg(&mut self, reg: DReg, write: bool, definite: bool) {
match self.context.fpscr.fr {
Some(false) => self.fpu(FpuResource::Xd(reg), write, definite),
Some(true) => self.fpu(FpuResource::Dr(reg), write, definite),
None => {
self.fpu(FpuResource::Dr(reg), write, false);
self.fpu(FpuResource::Xd(reg), write, false);
}
}
}
#[cfg(feature = "sh4")]
fn precision_reg(&mut self, reg: FReg, write: bool) {
match self.context.fpscr.pr {
Some(false) => self.freg(reg, write, true),
Some(true) => self.dreg(DReg::from_u8(reg.number() / 2), write, true),
None => {
self.freg(reg, write, false);
self.dreg(DReg::from_u8(reg.number() / 2), write, false);
}
}
}
#[cfg(feature = "sh4")]
fn transfer_reg(&mut self, reg: FReg, write: bool) {
match self.context.fpscr.sz {
Some(false) => self.freg(reg, write, true),
Some(true) => self.transfer_double_reg(reg, write, true),
None => {
self.freg(reg, write, false);
self.transfer_double_reg(reg, write, false);
}
}
}
#[cfg(feature = "sh4")]
fn transfer_double_reg(&mut self, reg: FReg, write: bool, definite: bool) {
let double_reg = DReg::from_u8(reg.number() / 2);
if reg.number() & 1 == 0 {
self.dreg(double_reg, write, definite);
} else {
self.xdreg(double_reg, write, definite);
}
}
#[cfg(feature = "sh4")]
pub(crate) fn read_precision_reg(&mut self, reg: FReg) {
self.precision_reg(reg, false);
}
#[cfg(feature = "sh4")]
pub(crate) fn write_precision_reg(&mut self, reg: FReg) {
self.precision_reg(reg, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn read_transfer_reg(&mut self, reg: FReg) {
self.transfer_reg(reg, false);
}
#[cfg(feature = "sh4")]
pub(crate) fn write_transfer_reg(&mut self, reg: FReg) {
self.transfer_reg(reg, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn read_dreg(&mut self, reg: DReg) {
self.dreg(reg, false, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn write_dreg(&mut self, reg: DReg) {
self.dreg(reg, true, true);
}
#[cfg(feature = "sh4")]
pub(crate) fn read_vec(&mut self, reg: VecReg) {
self.vec(reg, false);
}
#[cfg(feature = "sh4")]
pub(crate) fn write_vec(&mut self, reg: VecReg) {
self.vec(reg, true);
}
#[cfg(feature = "sh4")]
fn vec(&mut self, reg: VecReg, write: bool) {
match self.context.fpscr.fr {
Some(false) => self.fpu(FpuResource::Vector(reg), write, true),
Some(true) => self.fpu(FpuResource::XVector(reg), write, true),
None => {
self.fpu(FpuResource::Vector(reg), write, false);
self.fpu(FpuResource::XVector(reg), write, false);
}
}
}
pub(crate) fn memory(&mut self, access: MemoryAccess) {
let memory_len = usize::from(self.effects.memory_len);
assert!(memory_len < MAX_MEMORY_ACCESSES, "generated memory capacity exceeded");
let access = match (access.width, self.context.fpscr.sz) {
(AccessWidth::FpscrSz, Some(false)) => {
MemoryAccess { width: AccessWidth::Long, ..access }
}
(AccessWidth::FpscrSz, Some(true)) => {
MemoryAccess { width: AccessWidth::Quad, ..access }
}
_ => access,
};
self.effects.memory[memory_len] = Some(access);
self.effects.memory_len += 1;
}
pub(crate) fn finish(self) -> Effects {
self.effects
}
}