use super::*;
#[test]
fn test_bn_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, (-42i64) as u64);
mmix.write_tetra(0, 0x40010005); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 20); }
#[test]
fn test_bn_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 42);
mmix.write_tetra(0, 0x40010005); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_bnb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, (-42i64) as u64);
mmix.write_tetra(100, 0x4101FFFD); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 88); }
#[test]
fn test_bz_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x4201000A); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 40); }
#[test]
fn test_bz_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 1);
mmix.write_tetra(0, 0x4201000A); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bzb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x4301FFFB); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 80); }
#[test]
fn test_bp_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 42);
mmix.write_tetra(0, 0x44010008); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 32); }
#[test]
fn test_bp_not_taken_zero() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x44010008); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bp_not_taken_negative() {
let mut mmix = MMix::new();
mmix.set_register(1, (-1i64) as u64);
mmix.write_tetra(0, 0x44010008); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bpb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(200);
mmix.set_register(1, 100);
mmix.write_tetra(200, 0x4501FFFE); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 192); }
#[test]
fn test_bod_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 7);
mmix.write_tetra(0, 0x46010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 12); }
#[test]
fn test_bod_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 8);
mmix.write_tetra(0, 0x46010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bodb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 15);
mmix.write_tetra(100, 0x4701FFFC); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 84); }
#[test]
fn test_bnn_taken_positive() {
let mut mmix = MMix::new();
mmix.set_register(1, 42);
mmix.write_tetra(0, 0x48010006); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 24); }
#[test]
fn test_bnn_taken_zero() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x48010006); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 24);
}
#[test]
fn test_bnn_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, (-1i64) as u64);
mmix.write_tetra(0, 0x48010006); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bnnb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x4901FFFD); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 88); }
#[test]
fn test_bnz_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 1);
mmix.write_tetra(0, 0x4A010007); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 28); }
#[test]
fn test_bnz_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x4A010007); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bnzb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(200);
mmix.set_register(1, 99);
mmix.write_tetra(200, 0x4B01FFF6); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 160); }
#[test]
fn test_bnp_taken_negative() {
let mut mmix = MMix::new();
mmix.set_register(1, (-5i64) as u64);
mmix.write_tetra(0, 0x4C010004); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 16); }
#[test]
fn test_bnp_taken_zero() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x4C010004); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 16);
}
#[test]
fn test_bnp_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 1);
mmix.write_tetra(0, 0x4C010004); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bnpb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x4D01FFFF); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 96); }
#[test]
fn test_bev_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 8);
mmix.write_tetra(0, 0x4E01000C); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 48); }
#[test]
fn test_bev_not_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 7);
mmix.write_tetra(0, 0x4E01000C); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_bevb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x4F01FFFE); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 92); }
#[test]
fn test_pbn_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, (-10i64) as u64);
mmix.write_tetra(0, 0x50010005); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 20); }
#[test]
fn test_pbnb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, (-1i64) as u64);
mmix.write_tetra(100, 0x5101FFFD); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 88); }
#[test]
fn test_pbz_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 0);
mmix.write_tetra(0, 0x52010006); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 24); }
#[test]
fn test_pbzb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x5301FFFC); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 84); }
#[test]
fn test_pbp_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 50);
mmix.write_tetra(0, 0x54010008); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 32); }
#[test]
fn test_pbpb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 1);
mmix.write_tetra(100, 0x5501FFFE); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 92); }
#[test]
fn test_pbod_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 11);
mmix.write_tetra(0, 0x56010003); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 12); }
#[test]
fn test_pbodb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 99);
mmix.write_tetra(100, 0x5701FFFB); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 80); }
#[test]
fn test_pbnn_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 100);
mmix.write_tetra(0, 0x58010007); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 28); }
#[test]
fn test_pbnnb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x5901FFFF); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 96); }
#[test]
fn test_pbnz_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 42);
mmix.write_tetra(0, 0x5A010009); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 36); }
#[test]
fn test_pbnzb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(200);
mmix.set_register(1, 1);
mmix.write_tetra(200, 0x5B01FFFA); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 176); }
#[test]
fn test_pbnp_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, (-100i64) as u64);
mmix.write_tetra(0, 0x5C010004); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 16); }
#[test]
fn test_pbnpb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x5D01FFF8); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 68); }
#[test]
fn test_pbev_taken() {
let mut mmix = MMix::new();
mmix.set_register(1, 100);
mmix.write_tetra(0, 0x5E01000A); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 40); }
#[test]
fn test_pbevb_taken() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.set_register(1, 0);
mmix.write_tetra(100, 0x5F01FFF9); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 72); }
#[test]
fn test_jmp_forward() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xF000000A); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 40); }
#[test]
fn test_jmp_large_forward_offset() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.write_tetra(100, 0xF0FFFFFB); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 100 + 16777211 * 4); }
#[test]
fn test_jmpb() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.write_tetra(100, 0xF1FFFFFB); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 80); }
#[test]
fn test_geta() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.write_tetra(100, 0xF401000A); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 140); assert_eq!(mmix.get_pc(), 104); }
#[test]
fn test_getab() {
let mut mmix = MMix::new();
mmix.set_pc(100);
mmix.write_tetra(100, 0xF501FFFB); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 80); assert_eq!(mmix.get_pc(), 104);
}
#[test]
fn pushjb_reaches_a_backward_callee() {
let source = "\
\tLOC\t#100
AddFunc\tADDU\t$0,$0,$1
\tPOP\t1,0
Main\tSETI\t$1,40
\tSETI\t$2,2
\tPUSHJB\t$0,AddFunc
\tSET\t$255,$0
\tTRAP\t0,Halt,0
";
assert_eq!(run_to_halt(source), 42);
}
#[test]
fn forward_branch_past_half_the_field_still_goes_forward() {
let source = "\
\tLOC\t#100
Start\tSETI\t$1,0
\tBZ\t$1,Far
\tSETI\t$255,1
\tTRAP\t0,Halt,0
\tLOC\t#20110
Far\tSETI\t$255,42
\tTRAP\t0,Halt,0
";
assert_eq!(run_to_halt(source), 42);
}
#[test]
fn backward_branch_past_half_the_field_still_goes_backward() {
let source = "\
\tLOC\t#100
Back\tSETI\t$255,42
\tTRAP\t0,Halt,0
\tLOC\t#20100
Main\tSETI\t$1,0
\tBZB\t$1,Back
\tSETI\t$255,1
\tTRAP\t0,Halt,0
";
assert_eq!(run_to_halt(source), 42);
}
#[test]
fn pushj_forward_past_half_the_field_still_goes_forward() {
let source = "\
\tLOC\t#100
Main\tSETI\t$1,40
\tPUSHJ\t$0,Far
\tSET\t$255,$0
\tTRAP\t0,Halt,0
\tLOC\t#20120
Far\tSETI\t$0,42
\tPOP\t1,0
";
assert_eq!(run_to_halt(source), 42);
}
#[test]
fn test_geta_forward_field_above_half_the_range() {
let mut mmix = MMix::new();
mmix.set_pc(0x100);
mmix.write_tetra(0x100, 0xF401C000); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x100 + 49152 * 4);
}
#[test]
fn test_jmpb_small_field_is_a_far_backward_jump() {
let mut mmix = MMix::new();
mmix.set_pc(0x4000000);
mmix.write_tetra(0x4000000, 0xF1000005); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x4000000 - (16777216 - 5) * 4);
}
#[test]
fn test_getab_small_field_is_a_far_backward_address() {
let mut mmix = MMix::new();
mmix.set_pc(0x40000);
mmix.write_tetra(0x40000, 0xF5010005); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0x40000 - (65536 - 5) * 4);
}
#[test]
fn test_go() {
let mut mmix = MMix::new();
mmix.set_register(2, 1000);
mmix.set_register(3, 24);
mmix.write_tetra(0, 0x9E010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 4); assert_eq!(mmix.get_pc(), 1024); }
#[test]
fn test_goi() {
let mut mmix = MMix::new();
mmix.set_register(2, 5000);
mmix.write_tetra(0, 0x9F0102C8); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 4); assert_eq!(mmix.get_pc(), 5200); }
#[test]
fn test_go_at_top_of_memory_wraps_return_address() {
let mut mmix = MMix::new();
mmix.set_pc(0xFFFFFFFFFFFFFFFC);
mmix.set_register(2, 1000);
mmix.set_register(3, 24);
mmix.write_tetra(0xFFFFFFFFFFFFFFFC, 0x9E010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); assert_eq!(mmix.get_pc(), 1024); }
#[test]
fn test_goi_at_top_of_memory_wraps_return_address() {
let mut mmix = MMix::new();
mmix.set_pc(0xFFFFFFFFFFFFFFFC);
mmix.set_register(2, 5000);
mmix.write_tetra(0xFFFFFFFFFFFFFFFC, 0x9F0102C8); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(1), 0); assert_eq!(mmix.get_pc(), 5200); }
#[test]
fn test_go_off_tetra_target_keeps_low_bits_in_pc() {
let mut mmix = MMix::new();
mmix.set_pc(0x100);
mmix.set_register(2, 0x200);
mmix.set_register(3, 1);
mmix.write_tetra(0x100, 0x9E010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x201);
assert_eq!(mmix.get_register(1), 0x104);
mmix.write_tetra(0x200, 0xF4050002); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(5), 0x209);
assert_eq!(mmix.get_pc(), 0x205);
}
#[test]
fn test_goi_off_tetra_target_keeps_low_bits_in_pc() {
let mut mmix = MMix::new();
mmix.set_pc(0x100);
mmix.set_register(2, 0x200);
mmix.write_tetra(0x100, 0x9F010201); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x201);
assert_eq!(mmix.get_register(1), 0x104);
mmix.write_tetra(0x200, 0xF4050002); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_register(5), 0x209);
assert_eq!(mmix.get_pc(), 0x205);
}
#[test]
fn test_go_from_off_tetra_pc_return_address_carries_low_bits() {
let mut mmix = MMix::new();
mmix.set_pc(0x102);
mmix.set_register(2, 0x200);
mmix.set_register(3, 0);
mmix.write_tetra(0x100, 0x9E010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x200);
assert_eq!(mmix.get_register(1), 0x106);
}
#[test]
fn test_goi_from_off_tetra_pc_return_address_carries_low_bits() {
let mut mmix = MMix::new();
mmix.set_pc(0x103);
mmix.set_register(2, 0x200);
mmix.write_tetra(0x100, 0x9F010200); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x200);
assert_eq!(mmix.get_register(1), 0x107);
}
#[test]
fn test_go_at_top_of_memory_off_tetra_wraps_return_address() {
let mut mmix = MMix::new();
mmix.set_pc(0xFFFFFFFFFFFFFFFD);
mmix.set_register(2, 0x200);
mmix.set_register(3, 0);
mmix.write_tetra(0xFFFFFFFFFFFFFFFC, 0x9E010203); assert!(mmix.execute_instruction());
assert_eq!(mmix.get_pc(), 0x200);
assert_eq!(mmix.get_register(1), 1);
}
#[test]
fn test_go_off_tetra_target_halts_at_the_computed_pc() {
use crate::debugger::{entry_point, write_image};
use crate::mmixal::MMixAssembler;
const SOURCE: &str = "\
\tLOC\t#100
Main\tGETA\t$1,T
\tADDU\t$1,$1,1
\tGO\t$0,$1,0
T\tSET\t$2,5
\tSET\t$255,0
\tTRAP\t0,Halt,0
";
let mut asm = MMixAssembler::new(SOURCE, "<test>");
asm.parse().expect("program must assemble");
let (host, handle) = CaptureHost::new();
let mut mmix = MMix::with_host(host);
write_image(&mut mmix, &asm);
mmix.set_pc(entry_point(&asm));
mmix.run();
let diagnostics = handle.diagnostics();
assert!(
diagnostics
.iter()
.any(|d| d.contains("HALT trap at PC=0x0000000000000115, exit code=0")),
"got {diagnostics:?}"
);
assert_eq!(mmix.get_register(1), 0x10D);
}