use super::*;
#[test]
fn test_trip_to_unloaded_vector_halts_with_diagnostic_and_nonzero_exit() {
let (host, handle) = CaptureHost::new();
let mut mmix = MMix::with_host(host);
mmix.set_pc(0x100);
mmix.write_tetra(0x100, 0xFF000000); let result = mmix.execute_instruction();
assert!(!result);
assert_eq!(mmix.get_pc(), 0x100, "PC stays on the tripping instruction");
assert_eq!(mmix.get_exit_code(), 1);
assert_eq!(mmix.get_special(SpecialReg::RW), 0x104);
assert_eq!(handle.diagnostics().len(), 1);
assert!(handle.diagnostics()[0].contains("TRIP"));
}
#[test]
fn test_trip() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x00, 0xFD000000); mmix.set_pc(0x100);
mmix.set_register(255, 0xAAAA);
mmix.set_register(2, 0x2222);
mmix.set_register(3, 0x3333);
mmix.set_special(SpecialReg::RJ, 0xBEEF);
mmix.write_tetra(0x100, 0xFF110203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x00);
assert_eq!(mmix.get_special(SpecialReg::RW), 0x104);
assert_eq!(mmix.get_special(SpecialReg::RX), 0x80000000FF110203);
assert_eq!(mmix.get_special(SpecialReg::RY), 0x2222);
assert_eq!(mmix.get_special(SpecialReg::RZ), 0x3333);
assert_eq!(mmix.get_special(SpecialReg::RB), 0xAAAA);
assert_eq!(mmix.get_register(255), 0xBEEF);
}
#[test]
fn test_divide_check_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x10, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_D << 8); mmix.set_pc(0x100);
mmix.set_register(2, 7);
mmix.set_register(3, 0);
mmix.write_tetra(0x100, 0x1C010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x10, "trips to D's vector");
assert_eq!(
mmix.get_register(1),
0,
"DIV's own zero result is written first"
);
assert_eq!(mmix.get_special(SpecialReg::RY), 7);
assert_eq!(
mmix.get_special(SpecialReg::RA) & RA_D,
0,
"a tripped exception's own event bit stays clear"
);
}
#[test]
fn test_integer_overflow_trips_and_the_handler_sees_the_pre_write_operand() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x20, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_V << 8); mmix.set_pc(0x100);
mmix.set_register(5, i64::MAX as u64);
mmix.set_register(3, 1);
mmix.write_tetra(0x100, 0x20050503);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x20);
assert_eq!(
mmix.get_register(5),
i64::MIN as u64,
"the wrapped result is written before the trip"
);
assert_eq!(
mmix.get_special(SpecialReg::RY),
i64::MAX as u64,
"rY holds $5's value from before ADD overwrote it"
);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_V, 0);
}
#[test]
fn test_immediate_overflow_trip_reports_the_literal_z() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x20, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_V << 8); mmix.set_pc(0x100);
mmix.set_register(2, i64::MAX as u64);
mmix.set_register(5, 0xDEAD_BEEF);
mmix.write_tetra(0x100, 0x21030205);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x20);
assert_eq!(
mmix.get_special(SpecialReg::RZ),
5,
"rZ holds the literal Z, not $5's contents"
);
}
#[test]
fn test_store_overflow_trip_reports_address_and_stored_value() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x20, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_V << 8); mmix.set_pc(0x100);
mmix.set_register(1, 200); mmix.set_register(2, 0x4000);
mmix.set_register(3, 8);
mmix.write_byte(0x4009, 0xAB);
mmix.write_tetra(0x100, 0xA0010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x20);
assert_eq!(
mmix.get_special(SpecialReg::RY),
0x4008,
"rY holds the computed address, not raw $Y"
);
assert_eq!(
mmix.get_special(SpecialReg::RZ),
0xC8AB_0000_0000_0000,
"rZ holds the aligned octabyte after the store: the stored byte \
(200 = 0xC8) in place, the neighbouring byte and the rest of \
memory unchanged"
);
}
#[test]
fn test_stsf_overflow_trip_reports_address_and_merged_octa() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x50, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_O << 8); mmix.set_pc(0x100);
mmix.set_register(1, f64::MAX.to_bits()); mmix.set_register(2, 0x4000);
mmix.set_register(3, 8);
mmix.write_byte(0x400C, 0xAB);
mmix.write_tetra(0x100, 0xB0010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x50);
assert!(f32::from_bits(mmix.read_tetra(0x4008)).is_infinite());
assert_eq!(
mmix.get_special(SpecialReg::RY),
0x4008,
"rY holds the computed address, not raw $Y โ catches a revert \
to the plain $Y/$Z operands"
);
assert_eq!(
mmix.get_special(SpecialReg::RZ),
0x7F80_0000_AB00_0000,
"rZ holds the aligned octabyte after the store: the stored \
+inf tetra in place, the neighbouring byte unchanged โ catches \
a revert to the plain $Y/$Z operands"
);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_O, 0);
}
#[test]
fn test_invalid_operation_trips_when_enabled() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x40, 0xFD000000); mmix.set_special(SpecialReg::RA, RA_I << 8); mmix.set_pc(0x100);
mmix.set_register(2, f64::NAN.to_bits());
mmix.set_register(3, 0);
mmix.write_tetra(0x100, 0x01010203);
assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x40);
assert_eq!(mmix.get_special(SpecialReg::RA) & RA_I, 0);
}
#[test]
fn test_two_enabled_exceptions_trip_to_the_leftmost_and_drop_the_other_silently() {
let mut mmix = MMix::new();
load_tetra(&mut mmix, 0x50, 0xFD000000); mmix.set_special(SpecialReg::RA, (RA_O << 8) | (RA_X << 8)); mmix.set_pc(0x100);
mmix.set_register(2, f64::MAX.to_bits());
mmix.set_register(3, f64::MAX.to_bits());
mmix.write_tetra(0x100, 0x10010203);
assert!(mmix.execute_instruction());
assert_eq!(
mmix.get_pc(),
0x50,
"trips to O, the leftmost enabled exception"
);
assert_eq!(
mmix.get_special(SpecialReg::RA) & 0xFF,
0,
"O tripped and X was enabled but not leftmost: neither sets an event bit"
);
}
fn run_store(word: u32, value: u64) -> u64 {
let mut mmix = MMix::new();
mmix.set_register(1, value);
mmix.write_tetra(0, word);
assert!(mmix.execute_instruction());
mmix.get_special(SpecialReg::RA)
}
#[test]
fn test_signed_stores_raise_v_when_the_value_does_not_fit() {
for (word, wide, narrow) in [
(0xA0010203u32, 200u64, 127u64), (0xA1010200, 200, 127), (0xA4010203, 40000, 32767), (0xA5010200, 40000, 32767), (0xA8010203, 1 << 32, 0x7FFFFFFF), (0xA9010200, 1 << 32, 0x7FFFFFFF), ] {
assert_eq!(run_store(word, wide) & RA_V, RA_V, "{word:#010X} overflows");
assert_eq!(run_store(word, narrow), 0, "{word:#010X} in range");
}
}