use crate::*;
#[test]
fn test_imul_al_positive_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0xf6, 0xeb, 0xf4, ];
emu.regs_mut().rax = 0x05; emu.regs_mut().rbx = 0x03; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x000F, "5 * 3 = 15");
assert!(!emu.flags().f_cf, "CF should be clear (fits in AL)");
assert!(!emu.flags().f_of, "OF should be clear");
}
#[test]
fn test_imul_al_negative_by_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 0xFB; emu.regs_mut().rbx = 0x03; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0xFFF1, "-5 * 3 = -15 (0xFFF1)");
assert!(
!emu.flags().f_cf,
"CF should be clear (fits in sign-extended AL)"
);
}
#[test]
fn test_imul_al_negative_by_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 0xFB; emu.regs_mut().rbx = 0xFD; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x000F, "-5 * -3 = 15");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_al_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 100; emu.regs_mut().rbx = 2; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x00C8, "100 * 2 = 200");
assert!(
emu.flags().f_cf,
"CF should be set (doesn't fit in i8 range)"
);
assert!(emu.flags().f_of, "OF should be set");
}
#[test]
fn test_imul_al_max_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 127; emu.regs_mut().rbx = 1; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 127, "127 * 1 = 127");
assert!(!emu.flags().f_cf, "CF should be clear (fits)");
}
#[test]
fn test_imul_al_min_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 0x80; emu.regs_mut().rbx = 1; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0xFF80, "-128 * 1 = -128");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_al_overflow_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 0x80; emu.regs_mut().rbx = 2; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0xFF00, "-128 * 2 = -256");
assert!(emu.flags().f_cf, "CF should be set (overflow)");
}
#[test]
fn test_imul_al_zero() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf6, 0xeb, 0xf4]; emu.regs_mut().rax = 0; emu.regs_mut().rbx = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0, "0 * 100 = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_ax_positive_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x66, 0xf7, 0xeb, 0xf4,
];
emu.regs_mut().rax = 100; emu.regs_mut().rbx = 50; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 5000, "AX: 100 * 50 = 5000");
assert_eq!(emu.regs().rdx & 0xFFFF, 0, "DX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_ax_negative_by_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 0xFF9C; emu.regs_mut().rbx = 50;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0xEC78, "AX (low word)");
assert_eq!(
emu.regs().rdx & 0xFFFF,
0xFFFF,
"DX (high word, sign extension)"
);
assert!(
!emu.flags().f_cf,
"CF should be clear (fits in sign-extended)"
);
}
#[test]
fn test_imul_ax_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 1000;
emu.regs_mut().rbx = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x86A0, "AX (low)");
assert_eq!(emu.regs().rdx & 0xFFFF, 0x0001, "DX (high)");
assert!(emu.flags().f_cf, "CF should be set (overflow)");
}
#[test]
fn test_imul_ax_max_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 0x7FFF; emu.regs_mut().rbx = 1;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x7FFF, "32767 * 1 = 32767");
assert_eq!(emu.regs().rdx & 0xFFFF, 0, "DX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_eax_positive_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0xf7, 0xeb, 0xf4,
];
emu.regs_mut().rax = 1000;
emu.regs_mut().rbx = 2000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 2000000, "EAX: 1000 * 2000 = 2000000");
assert_eq!(emu.regs().rdx, 0, "EDX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_eax_negative_by_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = (-1000i32) as u64;
emu.regs_mut().rbx = 2000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
emu.regs().rax as i32,
-2000000,
"EAX: -1000 * 2000 = -2000000"
);
assert_eq!(emu.regs().rdx, 0xFFFFFFFF, "EDX (sign extension)");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_eax_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 100000;
emu.regs_mut().rbx = 100000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x540BE400, "EAX (low)");
assert_eq!(emu.regs().rdx, 0x00000002, "EDX (high)");
assert!(emu.flags().f_cf, "CF should be set (overflow)");
}
#[test]
fn test_imul_eax_max_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 0x7FFFFFFF;
emu.regs_mut().rbx = 1;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x7FFFFFFF, "max i32 * 1");
assert_eq!(emu.regs().rdx, 0, "EDX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_rax_positive_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x48, 0xf7, 0xeb, 0xf4,
];
emu.regs_mut().rax = 1000000;
emu.regs_mut().rbx = 2000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 2000000000000, "RAX: 1M * 2M");
assert_eq!(emu.regs().rdx, 0, "RDX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_rax_negative_by_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = (-1000000i64) as u64;
emu.regs_mut().rbx = 2000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax as i64, -2000000000000, "RAX: product");
assert_eq!(emu.regs().rdx, 0xFFFFFFFFFFFFFFFF, "RDX (sign extension)");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_rax_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 0x0001000000000000;
emu.regs_mut().rbx = 0x0001000000000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x0000000000000000, "RAX = 0");
assert_eq!(emu.regs().rdx, 0x0000000100000000, "RDX (high bits)");
assert!(emu.flags().f_cf, "CF should be set");
}
#[test]
fn test_imul_rax_max_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0xf7, 0xeb, 0xf4]; emu.regs_mut().rax = 0x7FFFFFFFFFFFFFFF;
emu.regs_mut().rbx = 1;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x7FFFFFFFFFFFFFFF, "max i64 * 1");
assert_eq!(emu.regs().rdx, 0, "RDX = 0");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_16bit_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x66, 0x0f, 0xaf, 0xd8, 0xf4,
];
emu.regs_mut().rax = 100; emu.regs_mut().rbx = 50; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
emu.regs().rbx & 0xFFFF,
5000,
"BX = BX * AX = 50 * 100 = 5000"
);
assert!(!emu.flags().f_cf, "CF should be clear (no truncation)");
}
#[test]
fn test_imul_two_op_16bit_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x0f, 0xaf, 0xd8, 0xf4]; emu.regs_mut().rax = 100;
emu.regs_mut().rbx = (-50i16) as u64;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
(emu.regs().rbx & 0xFFFF) as i16,
-5000,
"BX = -50 * 100 = -5000"
);
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_16bit_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x0f, 0xaf, 0xd8, 0xf4]; emu.regs_mut().rax = 100;
emu.regs_mut().rbx = 1000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx & 0xFFFF, 0x86A0, "BX = truncated result");
assert!(emu.flags().f_cf, "CF should be set (truncation occurred)");
assert!(emu.flags().f_of, "OF should be set");
}
#[test]
fn test_imul_two_op_32bit_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x0f, 0xaf, 0xd8, 0xf4,
];
emu.regs_mut().rax = 1000;
emu.regs_mut().rbx = 2000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 2000000, "EBX = 2000 * 1000 = 2000000");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_32bit_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x0f, 0xaf, 0xd8, 0xf4]; emu.regs_mut().rax = 2000;
emu.regs_mut().rbx = (-1000i32) as u64;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
emu.regs().rbx as i32,
-2000000,
"EBX = -1000 * 2000 = -2000000"
);
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_32bit_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x0f, 0xaf, 0xd8, 0xf4]; emu.regs_mut().rax = 100000;
emu.regs_mut().rbx = 100000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 0x540BE400, "EBX = truncated to 32 bits");
assert!(emu.flags().f_cf, "CF should be set (truncation)");
}
#[test]
fn test_imul_two_op_64bit_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x48, 0x0f, 0xaf, 0xd8, 0xf4,
];
emu.regs_mut().rax = 1000000;
emu.regs_mut().rbx = 2000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 2000000000000, "RBX = 2M * 1M");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_64bit_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x0f, 0xaf, 0xd8, 0xf4]; emu.regs_mut().rax = 2000000;
emu.regs_mut().rbx = (-1000000i64) as u64;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx as i64, -2000000000000, "RBX = product");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_two_op_other_registers() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x0f, 0xaf, 0xca, 0xf4]; emu.regs_mut().rcx = 123;
emu.regs_mut().rdx = 456;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 56088, "ECX = 123 * 456");
}
#[test]
fn test_imul_three_op_imm8_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x6b, 0xd8, 0x0A, 0xf4,
];
emu.regs_mut().rax = 50; emu.regs_mut().rbx = 0; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx & 0xFFFF, 500, "BX = AX * 10 = 50 * 10 = 500");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm8_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x6b, 0xd8, 0xFB, 0xf4]; emu.regs_mut().rax = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
(emu.regs().rbx & 0xFFFF) as i16,
-500,
"BX = 100 * -5 = -500"
);
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm8_32bit() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x6b, 0xd8, 0x14, 0xf4,
];
emu.regs_mut().rax = 1000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 20000, "EBX = 1000 * 20 = 20000");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm8_64bit() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x48, 0x6b, 0xd8, 0x0A, 0xf4,
];
emu.regs_mut().rax = 123456789;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 1234567890, "RBX = 123456789 * 10");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm16_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x66, 0x69, 0xd8, 0xE8, 0x03, 0xf4,
];
emu.regs_mut().rax = 10; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx & 0xFFFF, 10000, "BX = 10 * 1000 = 10000");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm32_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x69, 0xd8, 0x10, 0x27, 0x00, 0x00, 0xf4,
];
emu.regs_mut().rax = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 1000000, "EBX = 100 * 10000 = 1000000");
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_imm32_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x69, 0xd8, 0x18, 0xFC, 0xFF, 0xFF, 0xf4,
];
emu.regs_mut().rax = 2000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(
emu.regs().rbx as i32,
-2000000,
"EBX = 2000 * -1000 = -2000000"
);
assert!(!emu.flags().f_cf, "CF should be clear");
}
#[test]
fn test_imul_three_op_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x66, 0x69, 0xd8, 0x10, 0x27, 0xf4,
];
emu.regs_mut().rax = 10000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert!(emu.flags().f_cf, "CF should be set (overflow)");
assert!(emu.flags().f_of, "OF should be set");
}
#[test]
fn test_imul_three_op_different_regs() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x6b, 0xca, 0x05, 0xf4];
emu.regs_mut().rdx = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 500, "ECX = EDX * 5 = 100 * 5");
}
#[test]
fn test_imul_r8_one_operand() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x41, 0xf6, 0xe8, 0xf4,
];
emu.regs_mut().rax = 20; emu.regs_mut().r8 = 5; emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 100, "AX = 20 * 5 = 100");
}
#[test]
fn test_imul_r10_two_operand() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x49, 0x0f, 0xaf, 0xd2, 0xf4,
];
emu.regs_mut().rdx = 123;
emu.regs_mut().r10 = 456;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rdx, 56088, "RDX = RDX * R10 = 123 * 456");
}
#[test]
fn test_imul_r11_three_operand() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x4d, 0x6b, 0xdb, 0x0A, 0xf4,
];
emu.regs_mut().r11 = 999;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().r11, 9990, "R11 = R11 * 10 = 999 * 10");
}
#[test]
fn test_imul_byte_ptr_mem() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0xf6, 0x2d, 0xfa, 0x0f, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_byte(DATA_ADDR, 10);
emu.regs_mut().rax = 20;
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 200, "AX = 20 * 10 = 200");
}
#[test]
fn test_imul_two_op_mem() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x0f, 0xaf, 0x1d, 0xf9, 0x0f, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR, 123);
emu.regs_mut().rbx = 456;
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 56088, "EBX = 456 * 123");
}
#[test]
fn test_imul_three_op_mem() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x6b, 0x1d, 0xf9, 0x0f, 0x00, 0x00, 0x05, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR, 100);
emu.run(None).unwrap();
assert_eq!(emu.regs().rbx, 500, "EBX = mem * 5 = 100 * 5");
}