#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PCodeDataType {
UI1,
I2,
I4,
R4,
R8,
Cy,
Str,
Var,
Bool,
Ad,
Date,
FPR4,
FPR8,
Varg,
}
impl PCodeDataType {
pub fn eval_stack_bytes(self) -> u8 {
match self {
Self::UI1 | Self::I2 | Self::I4 | Self::Bool | Self::Str | Self::Ad => 4,
Self::R8 | Self::Cy | Self::Date => 8,
Self::Var | Self::Varg => 16,
Self::R4 | Self::FPR4 | Self::FPR8 => 0,
}
}
pub fn eval_stack_slots(self) -> u8 {
match self {
Self::UI1 | Self::I2 | Self::I4 | Self::Bool | Self::Str | Self::Ad => 1,
Self::R8 | Self::Cy | Self::Date => 2,
Self::Var | Self::Varg => 4,
Self::R4 | Self::FPR4 | Self::FPR8 => 0,
}
}
pub fn is_fpu(self) -> bool {
matches!(
self,
Self::R4 | Self::R8 | Self::FPR4 | Self::FPR8 | Self::Date
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpcodeSemantics {
Load {
source: LoadSource,
},
Store {
target: StoreTarget,
},
Arithmetic {
op: ArithOp,
},
Unary {
op: ArithOp,
},
Compare,
Convert {
from: Option<PCodeDataType>,
to: Option<PCodeDataType>,
},
Branch {
conditional: bool,
},
Call {
kind: CallKind,
},
Return,
Stack,
Nop,
Io,
Unclassified,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoadSource {
Frame,
Literal,
Memory,
Indirect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoreTarget {
Frame,
Memory,
Indirect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArithOp {
Add,
Sub,
Mul,
Div,
IDiv,
Mod,
Pow,
Neg,
Concat,
And,
Or,
Xor,
Not,
Eqv,
Imp,
Abs,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CallKind {
VCall,
ThisVCall,
ImpAdCall,
LateCall,
Other,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pcode::opcode::{
LEAD0_TABLE, LEAD1_TABLE, LEAD2_TABLE, LEAD3_TABLE, LEAD4_TABLE, PRIMARY_TABLE,
};
#[test]
fn test_data_type_sizes() {
assert_eq!(PCodeDataType::I4.eval_stack_slots(), 1);
assert_eq!(PCodeDataType::R8.eval_stack_slots(), 2);
assert_eq!(PCodeDataType::Var.eval_stack_slots(), 4);
assert_eq!(PCodeDataType::FPR4.eval_stack_slots(), 0);
assert!(PCodeDataType::FPR8.is_fpu());
assert!(!PCodeDataType::I4.is_fpu());
}
#[test]
fn test_data_type_from_opcode() {
let info = &PRIMARY_TABLE[0xAA]; assert_eq!(info.mnemonic, "AddI4");
assert_eq!(info.data_type, Some(PCodeDataType::I4));
let info = &PRIMARY_TABLE[0x6F]; assert_eq!(info.data_type, Some(PCodeDataType::FPR8));
let info = &PRIMARY_TABLE[0x1E]; assert_eq!(info.data_type, None);
let info = &PRIMARY_TABLE[0x01];
assert_eq!(info.data_type, None);
}
#[test]
fn test_semantics_from_opcode() {
let info = &PRIMARY_TABLE[0xAA];
assert_eq!(
info.semantics,
OpcodeSemantics::Arithmetic { op: ArithOp::Add }
);
let info = &PRIMARY_TABLE[0x04];
assert_eq!(
info.semantics,
OpcodeSemantics::Load {
source: LoadSource::Frame
}
);
let info = &PRIMARY_TABLE[0x1E];
assert_eq!(
info.semantics,
OpcodeSemantics::Branch { conditional: false }
);
let info = &PRIMARY_TABLE[0x1C];
assert_eq!(
info.semantics,
OpcodeSemantics::Branch { conditional: true }
);
let info = &PRIMARY_TABLE[0x0D];
assert_eq!(
info.semantics,
OpcodeSemantics::Call {
kind: CallKind::VCall
}
);
let info = &PRIMARY_TABLE[0x00];
assert_eq!(info.semantics, OpcodeSemantics::Nop);
let info = &PRIMARY_TABLE[0x01];
assert_eq!(info.semantics, OpcodeSemantics::Unclassified);
}
#[test]
fn test_convert_types() {
let info = &LEAD1_TABLE[0x00]; assert_eq!(info.mnemonic, "CStrR8");
let OpcodeSemantics::Convert { from, to } = info.semantics else {
panic!("expected Convert, got {:?}", info.semantics);
};
assert_eq!(from, Some(PCodeDataType::R8));
assert_eq!(to, Some(PCodeDataType::Str));
}
#[test]
fn test_all_implemented_opcodes_have_semantics() {
let tables: [&[crate::pcode::opcode::OpcodeInfo; 256]; 6] = [
&PRIMARY_TABLE,
&LEAD0_TABLE,
&LEAD1_TABLE,
&LEAD2_TABLE,
&LEAD3_TABLE,
&LEAD4_TABLE,
];
let mut classified = 0;
for table in &tables {
for info in table.iter() {
if info.is_implemented() && !info.is_lead_byte() {
if info.semantics != OpcodeSemantics::Unclassified {
classified += 1;
} else {
assert!(
info.category.is_empty(),
"Implemented opcode {} (table {:?}, 0x{:02X}) with category '{}' is Unclassified",
info.mnemonic,
info.table,
info.index,
info.category
);
}
}
}
}
assert!(
classified > 1000,
"Expected >1000 classified opcodes, got {classified}"
);
}
}