use super::*;
#[test]
fn test_memory_byte() {
let mut mmix = MMix::new();
mmix.write_byte(0x1000, 0x42);
assert_eq!(mmix.read_byte(0x1000), 0x42);
assert_eq!(mmix.read_byte(0x1001), 0);
}
#[test]
fn test_memory_wyde() {
let mut mmix = MMix::new();
mmix.write_wyde(0x1000, 0x1234);
assert_eq!(mmix.read_wyde(0x1000), 0x1234);
assert_eq!(mmix.read_byte(0x1000), 0x12);
assert_eq!(mmix.read_byte(0x1001), 0x34);
}
#[test]
fn test_memory_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0x1000, 0x12345678);
assert_eq!(mmix.read_tetra(0x1000), 0x12345678);
}
#[test]
fn test_memory_octa() {
let mut mmix = MMix::new();
mmix.write_octa(0x1000, 0x123456789ABCDEF0);
assert_eq!(mmix.read_octa(0x1000), 0x123456789ABCDEF0);
}
#[test]
fn test_fetch_instruction() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x20010203);
let (op, x, y, z) = mmix.fetch_instruction();
assert_eq!(op, 0x20);
assert_eq!(x, 0x01);
assert_eq!(y, 0x02);
assert_eq!(z, 0x03);
}
#[test]
fn test_sparse_memory() {
let mut mmix = MMix::new();
mmix.write_byte(0x1000, 0x42);
mmix.write_byte(0x1000, 0); assert_eq!(mmix.memory.len(), 0);
}
#[test]
fn test_ldb_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x80010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 127);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 127);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldb_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x80010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 0xFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldb_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8101020A);
mmix.set_register(2, 100);
mmix.write_byte(110, 0x80);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -128);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldbu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x82010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 0xFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 255);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldbu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x83010214);
mmix.set_register(2, 100);
mmix.write_byte(120, 200);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 200);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x84010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 32767);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 32767);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x84010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 0xFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8501020A);
mmix.set_register(2, 100);
mmix.write_wyde(110, 0x8000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -32768);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldwu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x86010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 0xFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 65535);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldwu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8701021E);
mmix.set_register(2, 100);
mmix.write_wyde(130, 50000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 50000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x88010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 2_147_483_647);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 2_147_483_647);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x88010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 0xFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x89010214);
mmix.set_register(2, 100);
mmix.write_tetra(120, 0x80000000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -2147483648);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldtu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8A010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 0xFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 4294967295);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldtu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8B010228);
mmix.set_register(2, 100);
mmix.write_tetra(140, 3_000_000_000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 3_000_000_000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldo_load_octa() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8C010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_octa(150, 0x123456789ABCDEF0);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldo_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8D010210);
mmix.set_register(2, 100);
mmix.write_octa(116, 0xFEDCBA9876543210);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0xFEDCBA9876543210);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldou_same_as_ldo() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8E010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_octa(150, 0x123456789ABCDEF0);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldou_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8F010208);
mmix.set_register(2, 1000);
mmix.write_octa(1008, 0xFFFFFFFFFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0xFFFFFFFFFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldsf_short_float() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x90010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
let float_val = std::f32::consts::PI;
mmix.write_tetra(150, float_val.to_bits());
mmix.execute_instruction();
let result_f64 = MMix::u64_to_f64(mmix.get_register(1));
assert!((result_f64 - std::f64::consts::PI).abs() < 0.0001);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldsfi_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9101020C);
mmix.set_register(2, 200);
let float_val = -2.5f32;
mmix.write_tetra(212, float_val.to_bits());
mmix.execute_instruction();
let result_f64 = MMix::u64_to_f64(mmix.get_register(1));
assert_eq!(result_f64, -2.5);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_lda_load_address() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x22010203);
mmix.set_register(2, 0x1000);
mmix.set_register(3, 0x500);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x1500);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_lda_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x23010240);
mmix.set_register(2, 0x2000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x2040);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_load_from_uninitialized_memory() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x83010064);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_load_address_wraparound() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x22010203);
mmix.set_register(2, u64::MAX - 100);
mmix.set_register(3, 200);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 99); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_wyde_set_replaces_whole_register() {
for (opcode, shift) in [(0xE0_u32, 48_u32), (0xE1, 32), (0xE2, 16), (0xE3, 0)] {
let mut mmix = MMix::new();
mmix.set_register(1, u64::MAX);
mmix.write_tetra(0, (opcode << 24) | (1 << 16) | 0xABCD);
mmix.execute_instruction();
let reg = mmix.get_register(1);
assert_eq!((reg >> shift) & 0xFFFF, 0xABCD, "opcode {opcode:#04X} wyde");
assert_eq!(
reg & !(0xFFFF_u64 << shift),
0,
"opcode {opcode:#04X} residue"
);
assert_eq!(mmix.get_pc(), 4);
}
}
#[test]
fn test_setl_zero_clears_whole_register() {
let mut mmix = MMix::new();
mmix.set_register(1, u64::MAX);
mmix.write_tetra(0, 0xE3010000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0);
}
fn assemble_and_run(source: &str, steps: usize) -> MMix {
use crate::debugger::write_image;
use crate::mmixal::MMixAssembler;
let mut asm = MMixAssembler::new(source, "<test>");
asm.parse().expect("test source must assemble");
let start = asm.instructions[0].0;
let mut mmix = MMix::new();
write_image(&mut mmix, &asm);
mmix.set_pc(start);
for _ in 0..steps {
mmix.execute_instruction();
}
mmix
}
#[test]
fn test_seti_lands_a_wide_constant() {
let mmix = assemble_and_run("SETI $1,#0123456789ABCDEF", 4);
assert_eq!(mmix.get_register(1), 0x0123456789ABCDEF);
}
#[test]
fn test_lda_large_address_lands_whole_address() {
let mmix = assemble_and_run(
"\tLOC\t#2000000000001200\nBase\tGREG\t@\n\tLOC\t#2000000000001234\nMain\tLDA\t$255,Main\n",
1,
);
assert_eq!(mmix.get_register(255), 0x2000_0000_0000_1234);
}
#[test]
fn test_lda_with_a_base_never_encodes_register_form_22() {
let mmix = assemble_and_run(
"\tLOC\t#100\nB\tGREG\t1\nMain\tSET\t$0,100\n\tLDA\t$1,5\n",
2,
);
assert_eq!(mmix.get_register(1), 5);
}
#[test]
fn test_inc_and_or_wydes_preserve_the_others() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x1111_2222_3333_4444);
mmix.write_tetra(0, 0xE6010001);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x1111_2222_3334_4444);
mmix.set_pc(4);
mmix.write_tetra(4, 0xEB01000F);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x1111_2222_3334_444F);
}
#[test]
fn test_stb_store_byte() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA0010203);
mmix.set_register(1, 0x42); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(150), 0x42);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stb_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA101020A);
mmix.set_register(1, 0x7F); mmix.set_register(2, 200);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(210), 0x7F);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stbu_store_byte_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA2010203);
mmix.set_register(1, 0xFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(150), 0xFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stbu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA3010214);
mmix.set_register(1, 0xAB);
mmix.set_register(2, 1000);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(1020), 0xAB);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stw_store_wyde() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA4010203);
mmix.set_register(1, 0x1234);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(150), 0x1234);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stw_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA501021E);
mmix.set_register(1, 0x7FFF); mmix.set_register(2, 2000);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(2030), 0x7FFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stwu_store_wyde_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA6010203);
mmix.set_register(1, 0xFFFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(150), 0xFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stwu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA7010228);
mmix.set_register(1, 0xABCD);
mmix.set_register(2, 5000);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(5040), 0xABCD);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stt_store_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA8010203);
mmix.set_register(1, 0x12345678);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0x12345678);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stt_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA9010232);
mmix.set_register(1, 0x7FFFFFFF); mmix.set_register(2, 10000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(10050), 0x7FFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sttu_store_tetra_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAA010203);
mmix.set_register(1, 0xFFFFFFFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0xFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sttu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAB01023C);
mmix.set_register(1, 0xDEADBEEF);
mmix.set_register(2, 20000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(20060), 0xDEADBEEF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sto_store_octa() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAC010203);
mmix.set_register(1, 0x123456789ABCDEF0);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sto_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAD010246);
mmix.set_register(1, 0xFEDCBA9876543210);
mmix.set_register(2, 30000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(30070), 0xFEDCBA9876543210);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stou_same_as_sto() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAE010203);
mmix.set_register(1, 0xFFFFFFFFFFFFFFFF);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 0xFFFFFFFFFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stou_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAF010250);
mmix.set_register(1, 0x0123456789ABCDEF);
mmix.set_register(2, 40000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(40080), 0x0123456789ABCDEF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stco_store_constant() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB42A0203); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 42);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stco_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB5FF025A); mmix.set_register(2, 50000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(50090), 255);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stht_store_high_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB2010203);
mmix.set_register(1, 0xDEADBEEF12345678);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0xDEADBEEF); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stht_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB3010264);
mmix.set_register(1, 0xABCD123456789ABC);
mmix.set_register(2, 60000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(60100), 0xABCD1234); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_store_and_load_roundtrip() {
let mut mmix = MMix::new();
let test_addr = 5000u64;
mmix.set_register(1, 0x123456789ABCDEF0);
mmix.set_register(2, test_addr);
mmix.write_tetra(0, 0xAD010200); mmix.execute_instruction();
mmix.set_pc(4);
mmix.write_tetra(4, 0x8D030200); mmix.execute_instruction();
assert_eq!(mmix.get_register(3), 0x123456789ABCDEF0);
}
#[test]
fn test_all_store_instructions_have_tests() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA0010203);
mmix.set_register(1, 0x5A);
mmix.set_register(2, 300);
mmix.set_register(3, 12);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_byte(312), 0x5A);
mmix.set_pc(0);
mmix.write_tetra(0, 0xA2010203);
mmix.set_register(1, 0xE1);
mmix.set_register(2, 300);
mmix.set_register(3, 13);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_byte(313), 0xE1);
mmix.set_pc(0);
mmix.write_tetra(0, 0xA4010203);
mmix.set_register(1, 0x4321);
mmix.set_register(2, 400);
mmix.set_register(3, 14);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_wyde(414), 0x4321);
mmix.set_pc(0);
mmix.write_tetra(0, 0xA6010203);
mmix.set_register(1, 0xBEEF);
mmix.set_register(2, 400);
mmix.set_register(3, 16);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_wyde(416), 0xBEEF);
mmix.set_pc(0);
mmix.write_tetra(0, 0xA8010203);
mmix.set_register(1, 0x87654321);
mmix.set_register(2, 500);
mmix.set_register(3, 20);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(520), 0x87654321);
mmix.set_pc(0);
mmix.write_tetra(0, 0xAA010203);
mmix.set_register(1, 0xCAFEBABE);
mmix.set_register(2, 500);
mmix.set_register(3, 24);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(524), 0xCAFEBABE);
mmix.set_pc(0);
mmix.write_tetra(0, 0xAC010203);
mmix.set_register(1, 0x0F1E2D3C4B5A6978);
mmix.set_register(2, 600);
mmix.set_register(3, 30);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(630), 0x0F1E2D3C4B5A6978);
mmix.set_pc(0);
mmix.write_tetra(0, 0xAE010203);
mmix.set_register(1, 0x1122334455667788);
mmix.set_register(2, 600);
mmix.set_register(3, 40);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(640), 0x1122334455667788);
mmix.set_pc(0);
mmix.write_tetra(0, 0xB4110203); mmix.set_register(2, 700);
mmix.set_register(3, 5);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(705), 17);
mmix.set_pc(0);
mmix.write_tetra(0, 0xB2010203);
mmix.set_register(1, 0x1357924600000000);
mmix.set_register(2, 800);
mmix.set_register(3, 8);
assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(808), 0x13579246); }
#[test]
fn test_ldht() {
let mut mmix = MMix::new();
mmix.set_register(2, 100);
mmix.set_register(3, 4);
mmix.write_tetra(104, 0x12345678);
mmix.write_tetra(0, 0x92010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x1234567800000000);
}
#[test]
fn test_ldhti() {
let mut mmix = MMix::new();
mmix.set_register(2, 100);
mmix.write_tetra(108, 0xABCDEF01);
mmix.write_tetra(0, 0x93010208); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xABCDEF0100000000);
}
#[test]
fn test_cswap_success() {
let mut mmix = MMix::new();
let addr = 1000u64;
let old_value = 0x123456789ABCDEF0u64;
let new_value = 0xFEDCBA9876543210u64;
mmix.write_octa(addr, old_value);
mmix.set_special(SpecialReg::RP, old_value); mmix.set_register(1, new_value); mmix.set_register(2, addr);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0x94010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); assert_eq!(mmix.read_octa(addr), new_value); }
#[test]
fn test_cswap_failure() {
let mut mmix = MMix::new();
let addr = 1000u64;
let mem_value = 0x123456789ABCDEF0u64;
let compare_value = 0x1111111111111111u64;
let new_value = 0xFEDCBA9876543210u64;
mmix.write_octa(addr, mem_value);
mmix.set_special(SpecialReg::RP, compare_value); mmix.set_register(1, new_value);
mmix.set_register(2, addr);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0x94010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); assert_eq!(mmix.read_octa(addr), mem_value); assert_eq!(mmix.get_special(SpecialReg::RP), mem_value); }
#[test]
fn test_cswapi() {
let mut mmix = MMix::new();
let addr = 2000u64;
let old_value = 0xAAAAAAAAAAAAAAAAu64;
let new_value = 0xBBBBBBBBBBBBBBBBu64;
mmix.write_octa(addr + 16, old_value);
mmix.set_special(SpecialReg::RP, old_value);
mmix.set_register(1, new_value);
mmix.set_register(2, addr);
mmix.write_tetra(0, 0x95010210); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 1); assert_eq!(mmix.read_octa(addr + 16), new_value);
}
#[test]
fn test_cswapi_failure() {
let mut mmix = MMix::new();
let addr = 2000u64;
let mem_value = 0x123456789ABCDEF0u64;
let compare_value = 0x1111111111111111u64;
let new_value = 0xFEDCBA9876543210u64;
mmix.write_octa(addr + 16, mem_value);
mmix.set_special(SpecialReg::RP, compare_value); mmix.set_register(1, new_value);
mmix.set_register(2, addr);
mmix.write_tetra(0, 0x95010210); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); assert_eq!(mmix.read_octa(addr + 16), mem_value); assert_eq!(mmix.get_special(SpecialReg::RP), mem_value); }
#[test]
fn octa_load_reads_the_aligned_base_from_any_address_in_the_block() {
let base = 800u64;
let value = 0x1122334455667788u64;
for offset in 0u64..8 {
let mut mmix = MMix::new();
mmix.write_octa(base, value);
mmix.set_register(2, base);
mmix.set_register(3, offset);
mmix.write_tetra(0, 0x8C010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), value, "offset {offset}");
}
}
#[test]
fn octa_store_at_misaligned_address_lands_at_aligned_base() {
let mut mmix = MMix::new();
let base = 800u64;
let value = 0x99AABBCCDDEEFF00u64;
mmix.set_register(1, value);
mmix.set_register(2, base);
mmix.set_register(3, 5);
mmix.write_tetra(0, 0xAC010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(base), value);
}
#[test]
fn tetra_load_reads_the_aligned_base_from_any_address_in_the_block() {
let base = 400u64;
let value = 0x11223344u32;
for offset in 0u64..4 {
let mut mmix = MMix::new();
mmix.write_tetra(base, value);
mmix.set_register(2, base);
mmix.set_register(3, offset);
mmix.write_tetra(0, 0x8A010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), value as u64, "offset {offset}");
}
}
#[test]
fn tetra_store_at_misaligned_address_lands_at_aligned_base() {
let mut mmix = MMix::new();
let base = 400u64;
let value = 0xAABBCCDDu32;
mmix.set_register(1, value as u64);
mmix.set_register(2, base);
mmix.set_register(3, 3);
mmix.write_tetra(0, 0xA8010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(base), value);
}
#[test]
fn wyde_load_reads_the_aligned_base_from_any_address_in_the_block() {
let base = 200u64;
let value = 0xBEEFu16;
for offset in 0u64..2 {
let mut mmix = MMix::new();
mmix.write_wyde(base, value);
mmix.set_register(2, base);
mmix.set_register(3, offset);
mmix.write_tetra(0, 0x86010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), value as u64, "offset {offset}");
}
}
#[test]
fn wyde_store_at_misaligned_address_lands_at_aligned_base() {
let mut mmix = MMix::new();
let base = 200u64;
let value = 0xCAFEu16;
mmix.set_register(1, value as u64);
mmix.set_register(2, base);
mmix.set_register(3, 1);
mmix.write_tetra(0, 0xA4010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_wyde(base), value);
}
#[test]
fn byte_access_at_an_odd_address_still_reads_that_byte() {
let mut mmix = MMix::new();
mmix.write_byte(801, 0x42);
assert_eq!(mmix.read_byte(801), 0x42);
assert_eq!(mmix.read_byte(800), 0);
}
#[test]
fn test_ldunc() {
let mut mmix = MMix::new();
mmix.set_register(2, 500);
mmix.set_register(3, 24);
mmix.write_octa(524, 0x0123456789ABCDEFu64);
mmix.write_tetra(0, 0x96010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x0123456789ABCDEFu64);
}
#[test]
fn test_ldunci() {
let mut mmix = MMix::new();
mmix.set_register(2, 600);
mmix.write_octa(632, 0xFEDCBA9876543210u64);
mmix.write_tetra(0, 0x97010220); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0xFEDCBA9876543210u64);
}
#[test]
fn test_ldvts() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x1000);
mmix.set_register(3, 0);
mmix.write_tetra(0, 0x98010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_ldvtsi() {
let mut mmix = MMix::new();
mmix.set_register(2, 0x2000);
mmix.write_tetra(0, 0x99010200); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); }
#[test]
fn test_preld() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9A010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_preldi() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9B010240); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_prego() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9C010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_pregoi() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9D010280); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_stht() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x1234567890ABCDEFu64);
mmix.set_register(2, 500);
mmix.set_register(3, 12);
mmix.write_tetra(0, 0xB2010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(512), 0x12345678);
}
#[test]
fn test_sthti() {
let mut mmix = MMix::new();
mmix.set_register(1, 0xFEDCBA9876543210u64);
mmix.set_register(2, 600);
mmix.write_tetra(0, 0xB7010218); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_tetra(624), 0xFEDCBA98);
}
#[test]
fn test_stco() {
let mut mmix = MMix::new();
mmix.set_register(2, 1500);
mmix.set_register(3, 8);
mmix.write_tetra(0, 0xB42A0203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(1508), 42);
}
#[test]
fn test_stcoi() {
let mut mmix = MMix::new();
mmix.set_register(2, 2500);
mmix.write_tetra(0, 0xB5640220); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(2532), 100);
}
#[test]
fn test_stunc() {
let mut mmix = MMix::new();
mmix.set_register(1, 0xABCDEF0123456789u64);
mmix.set_register(2, 3000);
mmix.set_register(3, 16);
mmix.write_tetra(0, 0xB6010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(3016), 0xABCDEF0123456789u64);
}
#[test]
fn test_stunci() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x123456789ABCDEFu64);
mmix.set_register(2, 4000);
mmix.write_tetra(0, 0xB7010228); assert!(mmix.execute_instruction());
assert_eq!(mmix.read_octa(4040), 0x123456789ABCDEFu64);
}
#[test]
fn test_syncd() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB8010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_syncdi() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB9010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_prest() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xBA010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_presti() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xBB010280); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn occupied_yields_nonzero_bytes_ascending_after_a_zero_write() {
let mut mmix = MMix::new();
mmix.write_byte(500, 5);
mmix.write_byte(100, 1);
mmix.write_byte(700, 7);
mmix.write_byte(300, 3);
mmix.write_byte(900, 9);
mmix.write_byte(400, 4);
mmix.write_byte(200, 6);
mmix.write_byte(200, 0);
let items: Vec<(u64, u8)> = mmix.occupied().collect();
assert_eq!(
items,
vec![(100, 1), (300, 3), (400, 4), (500, 5), (700, 7), (900, 9)]
);
}
#[test]
fn loaded_extent_includes_the_hello_world_nul_terminator_that_occupied_omits() {
use crate::debugger::write_image;
use crate::mmixal::MMixAssembler;
const HELLO_WORLD: &str = "\
\tLOC\tData_Segment
\tGREG\t@
Text\tBYTE\t\"Hello world!\",10,0
\tLOC\t#100
Main\tLDA\t$255,Text
\tTRAP\t0,Fputs,StdOut
\tTRAP\t0,Halt,0
";
let mut asm = MMixAssembler::new(HELLO_WORLD, "hello_world.mms");
asm.parse().expect("hello_world.mms must assemble");
let text_addr = *asm.labels.get("Text").expect("Text label");
let nul_addr = text_addr + 13;
let mut mmix = MMix::new();
write_image(&mut mmix, &asm);
let loaded: Vec<(u64, u8)> = mmix.loaded_extent().collect();
assert!(loaded.contains(&(nul_addr, 0)));
let occupied: Vec<(u64, u8)> = mmix.occupied().collect();
assert!(!occupied.iter().any(|&(addr, _)| addr == nul_addr));
}
#[test]
fn loaded_extent_is_unchanged_by_runtime_writes_during_execution() {
use crate::debugger::{entry_point, write_image};
use crate::mmixal::MMixAssembler;
const CALL_PROGRAM: &str = "\
\tLOC\t#100
Main\tPUSHJ\t$0,Sub
\tSETI\t$1,7
\tTRAP\t0,Halt,0
Sub\tSETI\t$0,3
\tPOP\t0,0
";
let mut asm = MMixAssembler::new(CALL_PROGRAM, "call.mms");
asm.parse().expect("call.mms must assemble");
let mut mmix = MMix::new();
write_image(&mut mmix, &asm);
let before: Vec<(u64, u8)> = mmix.loaded_extent().collect();
assert!(!before.is_empty(), "write_image must have loaded something");
mmix.set_pc(entry_point(&asm));
mmix.run();
let after: Vec<(u64, u8)> = mmix.loaded_extent().collect();
assert_eq!(
before, after,
"a runtime write (PUSHJ's register-stack spill) must not appear \
in loaded_extent"
);
}
#[test]
fn two_bytes_assembled_to_one_address_load_as_their_xor() {
use crate::debugger::write_image;
use crate::mmixal::MMixAssembler;
const SOURCE: &str = "\
\tLOC\t#1000
\tBYTE\t#0f
\tLOC\t#1000
\tBYTE\t#3c
";
let mut asm = MMixAssembler::new(SOURCE, "<test>");
asm.parse().expect("program must assemble");
let mut mmix = MMix::new();
write_image(&mut mmix, &asm);
assert_eq!(mmix.read_byte(0x1000), 0x33);
assert!(mmix.loaded_extent().any(|(addr, _)| addr == 0x1000));
}
#[test]
fn an_octa_with_a_wyde_assembled_over_it_combines_by_xor() {
use crate::debugger::write_image;
use crate::mmixal::MMixAssembler;
const SOURCE: &str = "\
\tLOC\t#1000
\tOCTA\t#0102030405060708
\tLOC\t#1002
\tWYDE\t#ffff
";
let mut asm = MMixAssembler::new(SOURCE, "<test>");
asm.parse().expect("program must assemble");
let mut mmix = MMix::new();
write_image(&mut mmix, &asm);
assert_eq!(mmix.read_octa(0x1000), 0x0102fcfb05060708);
}
#[test]
fn journal_records_writes_only_while_enabled_including_a_zero_write() {
let mut mmix = MMix::new();
mmix.write_byte(10, 1);
mmix.set_journal(true);
mmix.write_byte(20, 2);
mmix.write_byte(25, 0);
mmix.write_byte(30, 3);
mmix.set_journal(false);
mmix.write_byte(40, 4);
assert_eq!(mmix.take_journal(), vec![20, 25, 30]);
assert!(mmix.take_journal().is_empty());
}
#[test]
fn journal_enabled_flag_survives_reset_but_the_buffer_does_not() {
let mut mmix = MMix::new();
mmix.set_journal(true);
mmix.write_byte(50, 7);
assert_eq!(mmix.take_journal(), vec![50]);
mmix.reset();
assert!(mmix.take_journal().is_empty());
mmix.write_byte(60, 8);
assert_eq!(mmix.take_journal(), vec![60]);
}