//! TO-field evaluation for `tw`, `twi`, `td`, `tdi`. //! //! The TO field (5 bits) encodes which comparison outcomes trigger a trap: //! //! | bit | condition | //! |-----|-----------| //! | 0 | a < b (signed) | //! | 1 | a > b (signed) | //! | 2 | a == b | //! | 3 | a < b (unsigned) | //! | 4 | a > b (unsigned) | //! //! The bit numbering matches PowerISA ("MSB is bit 0"): TO[0] corresponds to //! the high bit of the 5-bit field, i.e. (to >> 4) & 1. //! //! `tw` / `twi` compare the low 32 bits of the operands (sign-extended back to //! 64 for the signed comparison); `td` / `tdi` compare the full 64 bits. #[derive(Clone, Copy, Debug)] pub enum TrapWidth { Word, // tw, twi: 32-bit Doubleword, // td, tdi: 64-bit } const TO_SLT: u32 = 1 << 4; // a < b signed const TO_SGT: u32 = 1 << 3; // a > b signed const TO_EQ: u32 = 1 << 2; // a == b const TO_ULT: u32 = 1 << 1; // a < b unsigned const TO_UGT: u32 = 1 << 0; // a > b unsigned /// Returns true when the trap should fire. pub fn evaluate(to: u32, a: u64, b: u64, width: TrapWidth) -> bool { let (sa, sb, ua, ub): (i64, i64, u64, u64) = match width { TrapWidth::Word => ( a as i32 as i64, b as i32 as i64, a as u32 as u64, b as u32 as u64, ), TrapWidth::Doubleword => (a as i64, b as i64, a, b), }; if (to & TO_SLT) != 0 && sa < sb { return true; } if (to & TO_SGT) != 0 && sa > sb { return true; } if (to & TO_EQ) != 0 && ua == ub { return true; } if (to & TO_ULT) != 0 && ua < ub { return true; } if (to & TO_UGT) != 0 && ua > ub { return true; } false } #[cfg(test)] mod tests { use super::*; #[test] fn to_zero_never_traps() { assert!(!evaluate(0, 0, 0, TrapWidth::Doubleword)); assert!(!evaluate(0, 5, 3, TrapWidth::Doubleword)); assert!(!evaluate(0, !0, 0, TrapWidth::Doubleword)); } #[test] fn to_31_always_traps_when_any_condition_holds() { // 31 = 0b11111 = all conditions enabled assert!(evaluate(31, 1, 2, TrapWidth::Doubleword)); // slt+ult assert!(evaluate(31, 2, 1, TrapWidth::Doubleword)); // sgt+ugt assert!(evaluate(31, 7, 7, TrapWidth::Doubleword)); // eq } #[test] fn to_eq_only() { // TO[2] = 0b00100 = 4 assert!(evaluate(4, 5, 5, TrapWidth::Doubleword)); assert!(!evaluate(4, 5, 6, TrapWidth::Doubleword)); } #[test] fn to_signed_vs_unsigned_on_negative() { // a=-1 (as u64 = all-ones). TO[0]=slt enabled = 0b10000 = 16 // Signed: -1 < 0 → true let neg1 = (-1i64) as u64; assert!(evaluate(16, neg1, 0, TrapWidth::Doubleword)); // TO[3]=ult enabled = 0b00010 = 2 → unsigned: all-ones < 0 is false assert!(!evaluate(2, neg1, 0, TrapWidth::Doubleword)); } #[test] fn word_width_ignores_high_32_bits() { // a's low 32 = 1, high 32 = different; b = 1. With TO=eq, should trap. let a = 0xDEAD_BEEF_0000_0001u64; assert!(evaluate(4, a, 1, TrapWidth::Word)); // In doubleword, different. assert!(!evaluate(4, a, 1, TrapWidth::Doubleword)); } }