use crate::*;
#[test]
fn test_sub_al_imm8_basic() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x05, 0xf4];
emu.regs_mut().rax = 15;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 10, "SUB AL, 5: 15 - 5 = 10");
assert!(!emu.flags().f_zf, "ZF should be clear (result != 0)");
assert!(!emu.flags().f_sf, "SF should be clear (result positive)");
assert!(!emu.flags().f_cf, "CF should be clear (no borrow)");
}
#[test]
fn test_sub_al_imm8_zero_result() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x55, 0xf4];
emu.regs_mut().rax = 0x55;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0, "SUB AL, 0x55: 0x55 - 0x55 = 0");
assert!(emu.flags().f_zf, "ZF should be set (result = 0)");
assert!(!emu.flags().f_cf, "CF should be clear (no borrow)");
}
#[test]
fn test_sub_al_imm8_borrow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x01, 0xf4];
emu.regs_mut().rax = 0;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0xFF, "SUB AL, 1: 0 - 1 = 0xFF (with borrow)");
assert!(!emu.flags().f_zf, "ZF should be clear");
assert!(emu.flags().f_cf, "CF should be set (borrow)");
assert!(emu.flags().f_sf, "SF should be set (result negative)");
}
#[test]
fn test_sub_al_imm8_signed_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x01, 0xf4];
emu.regs_mut().rax = 0x80;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0x7F, "SUB AL, 1: 0x80 - 1 = 0x7F");
assert!(emu.flags().f_of, "OF should be set (signed overflow)");
assert!(!emu.flags().f_sf, "SF should be clear (result positive)");
assert!(!emu.flags().f_cf, "CF should be clear (no unsigned borrow)");
}
#[test]
fn test_sub_al_imm8_negative_result() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x0a, 0xf4];
emu.regs_mut().rax = 5;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0xFB, "SUB AL, 10: 5 - 10 = 0xFB (-5)");
assert!(emu.flags().f_sf, "SF should be set (result negative)");
assert!(emu.flags().f_cf, "CF should be set (borrow)");
assert!(!emu.flags().f_of, "OF should be clear (no signed overflow)");
}
#[test]
fn test_sub_al_imm8_parity() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x03, 0xf4];
emu.regs_mut().rax = 0x0F;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0x0C);
assert!(emu.flags().f_pf, "PF should be set (even parity)");
}
#[test]
fn test_sub_al_imm8_preserves_high_bytes() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x05, 0xf4];
emu.regs_mut().rax = 0xDEADBEEF_1234567D;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0x7D - 0x05);
assert_eq!(emu.regs().rax & !0xFF, 0xDEADBEEF_12345600, "High bytes should be preserved");
}
#[test]
fn test_sub_ax_imm16() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x2d, 0x34, 0x12, 0xf4];
emu.regs_mut().rax = 0x5678;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x4444, "SUB AX, 0x1234: 0x5678 - 0x1234 = 0x4444");
}
#[test]
fn test_sub_eax_imm32() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2d, 0x78, 0x56, 0x34, 0x12, 0xf4];
emu.regs_mut().rax = 0xFFFFFFFF_23456789;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x11111111, "SUB EAX clears high 32 bits of RAX");
}
#[test]
fn test_sub_rax_imm32_sign_extended() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x2d, 0xff, 0xff, 0xff, 0xff, 0xf4];
emu.regs_mut().rax = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 101, "SUB RAX, -1 (sign-extended): 100 - (-1) = 101");
}
#[test]
fn test_sub_rm8_imm8_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x80, 0xe9, 0x0a, 0xf4];
emu.regs_mut().rcx = 25;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx & 0xFF, 15, "SUB CL, 10: 25 - 10 = 15");
}
#[test]
fn test_sub_rm8_imm8_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x80, 0x2b, 0x0a, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.load_code_bytes(&code);
emu.maps.write_byte(DATA_ADDR, 50);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_byte(DATA_ADDR).unwrap(), 40, "SUB [RBX], 10: 50 - 10 = 40");
}
#[test]
fn test_sub_rm32_imm32_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x81, 0xe9, 0x78, 0x56, 0x34, 0x12, 0xf4];
emu.regs_mut().rcx = 0x23456789;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x11111111, "SUB ECX, 0x12345678");
}
#[test]
fn test_sub_rm64_imm32_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x81, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xf4];
emu.regs_mut().rcx = 0x100000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x100000001, "SUB RCX, -1: 0x100000000 - (-1) = 0x100000001");
}
#[test]
fn test_sub_rm32_imm32_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x81, 0x2b, 0x00, 0x10, 0x00, 0x00, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR, 0x12345678);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_dword(DATA_ADDR).unwrap(), 0x12344678, "SUB [RBX], 0x1000");
}
#[test]
fn test_sub_rm32_imm8_positive() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x83, 0xe9, 0x0a, 0xf4];
emu.regs_mut().rcx = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 90, "SUB ECX, 10: 100 - 10 = 90");
}
#[test]
fn test_sub_rm32_imm8_negative() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x83, 0xe9, 0xf6, 0xf4];
emu.regs_mut().rcx = 100;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 110, "SUB ECX, -10: 100 - (-10) = 110");
}
#[test]
fn test_sub_rm64_imm8_sign_extended() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x83, 0xe9, 0xff, 0xf4];
emu.regs_mut().rcx = 0x100000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x100000001, "SUB RCX, -1: 0x100000000 + 1 = 0x100000001");
}
#[test]
fn test_sub_rm16_imm8_sign_extended() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x83, 0xe9, 0xff, 0xf4];
emu.regs_mut().rcx = 0x1000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx & 0xFFFF, 0x1001, "SUB CX, -1: 0x1000 + 1 = 0x1001");
}
#[test]
fn test_sub_rm8_r8_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x28, 0xc8, 0xf4];
emu.regs_mut().rax = 15;
emu.regs_mut().rcx = 5;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 10, "SUB AL, CL: 15 - 5 = 10");
}
#[test]
fn test_sub_rm8_r8_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x28, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 20;
emu.load_code_bytes(&code);
emu.maps.write_byte(DATA_ADDR, 50);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_byte(DATA_ADDR).unwrap(), 30, "SUB [RBX], CL: 50 - 20 = 30");
}
#[test]
fn test_sub_rm8_r8_same_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x28, 0xc0, 0xf4];
emu.regs_mut().rax = 50;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0, "SUB AL, AL: 50 - 50 = 0");
assert!(emu.flags().f_zf, "ZF should be set (result = 0)");
}
#[test]
fn test_sub_rm32_r32_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x29, 0xc8, 0xf4];
emu.regs_mut().rax = 0x23456789;
emu.regs_mut().rcx = 0x11111111;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x12345678, "SUB EAX, ECX");
}
#[test]
fn test_sub_rm64_r64_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x29, 0xc8, 0xf4];
emu.regs_mut().rax = 0x00000001_00000000;
emu.regs_mut().rcx = 0x00000000_00000001;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x00000000_FFFFFFFF, "SUB RAX, RCX");
}
#[test]
fn test_sub_rm64_r64_underflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x29, 0xc8, 0xf4];
emu.regs_mut().rax = 0;
emu.regs_mut().rcx = 1;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0xFFFFFFFFFFFFFFFF, "SUB RAX, RCX: 0 - 1 = max");
assert!(emu.flags().f_cf, "CF should be set (borrow)");
}
#[test]
fn test_sub_rm32_r32_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x29, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 0x1000;
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR, 0x12345678);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_dword(DATA_ADDR).unwrap(), 0x12344678, "SUB [RBX], ECX");
}
#[test]
fn test_sub_rm64_r64_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x29, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 0x1;
emu.load_code_bytes(&code);
emu.maps.write_qword(DATA_ADDR, 0x100000000);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_qword(DATA_ADDR).unwrap(), 0xFFFFFFFF, "SUB [RBX], RCX (64-bit)");
}
#[test]
fn test_sub_r8_rm8_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2a, 0xc8, 0xf4];
emu.regs_mut().rax = 5;
emu.regs_mut().rcx = 15;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx & 0xFF, 10, "SUB CL, AL: 15 - 5 = 10");
}
#[test]
fn test_sub_r8_rm8_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2a, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 50;
emu.load_code_bytes(&code);
emu.maps.write_byte(DATA_ADDR, 20);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx & 0xFF, 30, "SUB CL, [RBX]: 50 - 20 = 30");
}
#[test]
fn test_sub_r32_rm32_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2b, 0xc8, 0xf4];
emu.regs_mut().rax = 0x11111111;
emu.regs_mut().rcx = 0x33333333;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x22222222, "SUB ECX, EAX");
}
#[test]
fn test_sub_r64_rm64_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x2b, 0xc8, 0xf4];
emu.regs_mut().rax = 0x1111111111111111;
emu.regs_mut().rcx = 0x3333333333333333;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x2222222222222222, "SUB RCX, RAX (64-bit)");
}
#[test]
fn test_sub_r32_rm32_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2b, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 0x12345678;
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR, 0x1000);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x12344678, "SUB ECX, [RBX]");
}
#[test]
fn test_sub_r64_rm64_memory() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x2b, 0x0b, 0xf4];
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 0x200000000;
emu.load_code_bytes(&code);
emu.maps.write_qword(DATA_ADDR, 0x100000000);
emu.run(None).unwrap();
assert_eq!(emu.regs().rcx, 0x100000000, "SUB RCX, [RBX] (64-bit)");
}
#[test]
fn test_sub_r8_extended() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x41, 0x83, 0xe8, 0x32, 0xf4];
emu.regs_mut().r8 = 150;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().r8, 100, "SUB R8D, 50: 150 - 50 = 100");
}
#[test]
fn test_sub_r15_extended() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x49, 0x29, 0xc7, 0xf4];
emu.regs_mut().rax = 0x1000;
emu.regs_mut().r15 = 0x3000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().r15, 0x2000, "SUB R15, RAX: 0x3000 - 0x1000 = 0x2000");
}
#[test]
fn test_sub_flags_32bit_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x83, 0xe8, 0x01, 0xf4]; emu.regs_mut().rax = 0x80000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x7FFFFFFF);
assert!(emu.flags().f_of, "OF should be set (32-bit signed overflow)");
assert!(!emu.flags().f_sf, "SF should be clear (result positive in 32-bit)");
assert!(!emu.flags().f_cf, "CF should be clear (no unsigned borrow)");
}
#[test]
fn test_sub_flags_64bit_overflow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x83, 0xe8, 0x01, 0xf4]; emu.regs_mut().rax = 0x8000000000000000;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0x7FFFFFFFFFFFFFFF);
assert!(emu.flags().f_of, "OF should be set (64-bit signed overflow)");
assert!(!emu.flags().f_sf, "SF should be clear (result positive)");
}
#[test]
fn test_sub_flags_64bit_borrow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x48, 0x83, 0xe8, 0x01, 0xf4]; emu.regs_mut().rax = 0;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0xFFFFFFFFFFFFFFFF);
assert!(emu.flags().f_cf, "CF should be set (borrow)");
assert!(emu.flags().f_sf, "SF should be set (result negative)");
}
#[test]
fn test_sub_auxiliary_carry() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x01, 0xf4]; emu.regs_mut().rax = 0x10;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0x0F);
assert!(emu.flags().f_af, "AF should be set (borrow from bit 4)");
}
#[test]
fn test_sub_no_auxiliary_carry() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2c, 0x01, 0xf4]; emu.regs_mut().rax = 0x1F;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFF, 0x1E);
assert!(!emu.flags().f_af, "AF should be clear (no borrow from bit 4)");
}
#[test]
fn test_sub_with_displacement() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2b, 0x43, 0x08, 0xf4];
emu.regs_mut().rax = 150;
emu.regs_mut().rbx = DATA_ADDR;
emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR + 8, 50);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 100, "SUB EAX, [RBX + 8]: 150 - 50 = 100");
}
#[test]
fn test_sub_with_sib() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x2b, 0x04, 0x8b, 0xf4];
emu.regs_mut().rax = 125;
emu.regs_mut().rbx = DATA_ADDR;
emu.regs_mut().rcx = 2; emu.load_code_bytes(&code);
emu.maps.write_dword(DATA_ADDR + 8, 25);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 100, "SUB EAX, [RBX + RCX*4]: 125 - 25 = 100");
}
#[test]
fn test_sub_rm16_r16() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x29, 0xc8, 0xf4];
emu.regs_mut().rax = 0xDEAD_5678;
emu.regs_mut().rcx = 0xBEEF_1234;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0x4444, "SUB AX, CX: 0x5678 - 0x1234 = 0x4444");
assert_eq!(emu.regs().rax & 0xFFFF0000, 0xDEAD0000, "High word of EAX should be preserved");
}
#[test]
fn test_sub_16bit_borrow() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [0x66, 0x83, 0xe8, 0x01, 0xf4]; emu.regs_mut().rax = 0;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax & 0xFFFF, 0xFFFF, "SUB AX, 1: 0 - 1 = 0xFFFF");
assert!(emu.flags().f_cf, "CF should be set (16-bit borrow)");
assert!(emu.flags().f_sf, "SF should be set");
}
#[test]
fn test_sub_chain_multi_register() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x29, 0xd8, 0x29, 0xc8, 0x29, 0xd0, 0xf4, ];
emu.regs_mut().rax = 100;
emu.regs_mut().rbx = 10;
emu.regs_mut().rcx = 20;
emu.regs_mut().rdx = 30;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 40, "100 - 10 - 20 - 30 = 40");
}
#[test]
fn test_sub_self_zero() {
let DATA_ADDR = 0x7000;
let mut emu = emu64();
let code = [
0x48, 0x29, 0xc0, 0xf4, ];
emu.regs_mut().rax = 0xDEADBEEF_CAFEBABE;
emu.load_code_bytes(&code);
emu.run(None).unwrap();
assert_eq!(emu.regs().rax, 0, "SUB RAX, RAX always produces 0");
assert!(emu.flags().f_zf, "ZF should be set");
assert!(!emu.flags().f_cf, "CF should be clear");
assert!(!emu.flags().f_of, "OF should be clear");
}