pub const GT_OP: u8 = 0x08;
pub const GTW_OP: u8 = GT_OP + M32_OFFSET;
pub const LT_ABS_NP_OP: u8 = 0x50;
pub const LT_ABS_PN_OP: u8 = 0x51;
pub const M32_OFFSET: u8 = 0x10;
#[cfg(test)]
mod tests {
use super::*;
use zisk_core::zisk_ops::{OpType, ZiskOp};
const M32_PAIRS: [(u8, u8); 12] = [
(ZiskOp::MINU, ZiskOp::MINU_W),
(ZiskOp::MIN, ZiskOp::MIN_W),
(ZiskOp::MAXU, ZiskOp::MAXU_W),
(ZiskOp::MAX, ZiskOp::MAX_W),
(ZiskOp::LTU, ZiskOp::LTU_W),
(ZiskOp::LT, ZiskOp::LT_W),
(GT_OP, GTW_OP),
(ZiskOp::EQ, ZiskOp::EQ_W),
(ZiskOp::ADD, ZiskOp::ADD_W),
(ZiskOp::SUB, ZiskOp::SUB_W),
(ZiskOp::LEU, ZiskOp::LEU_W),
(ZiskOp::LE, ZiskOp::LE_W),
];
const INTERNAL_OPS: [u8; 4] = [GT_OP, GTW_OP, LT_ABS_NP_OP, LT_ABS_PN_OP];
fn binary_opcodes() -> Vec<u8> {
let mut ops = INTERNAL_OPS.to_vec();
for code in ZiskOp::MIN_OPCODE..=ZiskOp::MAX_OPCODE {
if matches!(ZiskOp::try_from_code(code), Ok(op) if op.op_type() == OpType::Binary) {
ops.push(code);
}
}
ops
}
#[test]
fn m32_pairs_are_one_offset_apart() {
for (op, op_w) in M32_PAIRS {
assert_eq!(op + M32_OFFSET, op_w, "0x{op:02x} and 0x{op_w:02x} are not an m32 pair");
}
}
#[test]
fn no_binary_opcode_shadows_an_unrelated_operation() {
for op in binary_opcodes() {
if M32_PAIRS.iter().any(|&(_, op_w)| op_w == op) {
continue;
}
let Some(shadow) = op.checked_add(M32_OFFSET) else { continue };
if M32_PAIRS.contains(&(op, shadow)) {
continue;
}
assert!(
!INTERNAL_OPS.contains(&shadow),
"0x{op:02x} shadows the internal binary opcode 0x{shadow:02x}"
);
if let Ok(taken) = ZiskOp::try_from_code(shadow) {
panic!(
"0x{op:02x} ({}) shadows 0x{shadow:02x} ({}): the m32 slot of a binary \
opcode must stay empty",
ZiskOp::try_from_code(op).map(|o| o.name()).unwrap_or("internal"),
taken.name(),
);
}
}
}
}