use crate::*;
const DATA_ADDR: u64 = 0x7000;
fn write_u16(mem: u64, addr: u64, val: u16) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, &val.to_le_bytes());
}
fn read_u16(mem: u64, addr: u64) -> u16 {
let emu = emu64(); let mut buf = [0u8; 2];
emu.maps.read_bytes_buff(&mut buf, addr);
u16::from_le_bytes(buf)
}
fn write_u32(mem: u64, addr: u64, val: u32) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, &val.to_le_bytes());
}
fn read_u32(mem: u64, addr: u64) -> u32 {
let emu = emu64(); let mut buf = [0u8; 4];
emu.maps.read_bytes_buff(&mut buf, addr);
u32::from_le_bytes(buf)
}
fn write_f64(mem: u64, addr: u64, val: f64) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, &val.to_le_bytes());
}
fn read_f64(mem: u64, addr: u64) -> f64 {
let emu = emu64(); let mut buf = [0u8; 8];
emu.maps.read_bytes_buff(&mut buf, addr);
f64::from_le_bytes(buf)
}
fn write_bytes(mem: u64, addr: u64, data: &[u8]) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, data);
}
fn read_bytes(mem: u64, addr: u64, len: usize) -> Vec<u8> {
let emu = emu64(); let mut buf = vec![0u8; len];
emu.maps.read_bytes_buff(&mut buf, addr);
buf
}
const FXSAVE_FCW: u64 = 0; const FXSAVE_FSW: u64 = 2; const FXSAVE_FTW: u64 = 4; const FXSAVE_FOP: u64 = 6; const FXSAVE_ST0: u64 = 32; const FXSAVE_ST1: u64 = 48;
const FXSAVE_ST2: u64 = 64;
const FXSAVE_ST3: u64 = 80;
const FXSAVE_ST4: u64 = 96;
const FXSAVE_ST5: u64 = 112;
const FXSAVE_ST6: u64 = 128;
const FXSAVE_ST7: u64 = 144;
const FXSAVE_SIZE: u64 = 512;
#[test]
fn test_fxsave_basic() {
let mut emu = emu64(); let code = [
0x0F, 0xAE, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.run(None).unwrap();
let fcw = emu.maps.read_word(0x2000 + FXSAVE_FCW).unwrap();
assert!(fcw < 0xFFFF, "FCW should be valid after FXSAVE");
}
#[test]
fn test_fxsave_with_fpu_data() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x20, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x18, 0x20, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.maps.write_f64(0x2008, 2.5);
emu.run(None).unwrap();
let fcw = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
assert!(fcw < 0xFFFF, "FCW should be saved");
let fsw = emu.maps.read_word(0x3000 + FXSAVE_FSW).unwrap();
assert!(fsw < 0xFFFF, "FSW should be saved");
}
#[test]
fn test_fxsave_saves_control_word() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, 0x037F);
emu.run(None).unwrap();
let saved_cw = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
assert_eq!(saved_cw, 0x037F, "FCW should be saved correctly");
}
#[test]
fn test_fxsave_saves_status_word() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 3.14159);
emu.run(None).unwrap();
let fsw = emu.maps.read_word(0x3000 + FXSAVE_FSW).unwrap();
assert!(fsw < 0xFFFF, "FSW should be saved");
}
#[test]
fn test_fxsave_multiple_areas() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x31, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 2.5);
emu.run(None).unwrap();
let fcw1 = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
let fcw2 = emu.maps.read_word(0x3100 + FXSAVE_FCW).unwrap();
assert_eq!(fcw1, fcw2, "Multiple FXSAVE should save identical state");
}
#[test]
fn test_fxrstor_basic() {
let mut emu = emu64(); let code = [
0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xD9, 0x3C, 0x25, 0x00, 0x40, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x3000 + FXSAVE_FCW, 0x037F);
emu.run(None).unwrap();
let cw = emu.maps.read_word(0x4000).unwrap();
assert!(cw < 0xFFFF, "Control word should be valid after FXRSTOR");
}
#[test]
fn test_fxsave_fxrstor_roundtrip() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDB, 0xE3, 0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x40, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x4008).unwrap();
assert_eq!(result, 1.5, "Value should be preserved through FXSAVE/FXRSTOR");
}
#[test]
fn test_fxsave_fxrstor_multiple_values() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDB, 0xE3, 0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x40, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x18, 0x40, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.maps.write_f64(0x2008, 2.5);
emu.run(None).unwrap();
let v1 = emu.maps.read_f64(0x4010).unwrap();
let v2 = emu.maps.read_f64(0x4018).unwrap();
assert_eq!(v1, 2.5, "Second value should be popped first");
assert_eq!(v2, 1.5, "First value should be popped second");
}
#[test]
fn test_fxsave_area_512_bytes() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 3.14159);
emu.run(None).unwrap();
let fcw = emu.maps.read_word(0x3000).unwrap();
let fsw = emu.maps.read_word(0x3002).unwrap();
let ftw = emu.maps.read_word(0x3004).unwrap();
assert!(fcw < 0xFFFF, "FCW should be valid");
assert!(fsw < 0xFFFF, "FSW should be valid");
assert!(ftw < 0xFFFF, "FTW should be valid");
}
#[test]
fn test_fxsave_different_control_words() {
let mut emu = emu64(); let test_cws = vec![0x037F, 0x027F, 0x0C7F];
for test_cw in test_cws {
let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, test_cw);
emu.run(None).unwrap();
let saved_cw = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
assert_eq!(saved_cw, test_cw, "Control word 0x{:04X} should be saved", test_cw);
}
}
#[test]
fn test_fxrstor_from_prepared_area() {
let mut emu = emu64(); let code = [
0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x20, 0x00, 0x00, 0xD9, 0x3C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000 + FXSAVE_FCW, 0x037F);
emu.maps.write_word(0x2000 + FXSAVE_FSW, 0x0000);
emu.run(None).unwrap();
let cw = emu.maps.read_word(0x3000).unwrap();
assert_eq!(cw, 0x037F, "Control word should be restored from prepared area");
}
#[test]
fn test_sequential_fxsave() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x32, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.run(None).unwrap();
let fcw1 = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
let fcw2 = emu.maps.read_word(0x3200 + FXSAVE_FCW).unwrap();
assert_eq!(fcw1, fcw2, "Multiple FXSAVE should produce identical results");
}
#[test]
fn test_fxsave_after_arithmetic() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0xDE, 0xC1, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 2.5);
emu.maps.write_f64(0x2008, 3.5);
emu.run(None).unwrap();
let fsw = emu.maps.read_word(0x3000 + FXSAVE_FSW).unwrap();
assert!(fsw < 0xFFFF, "FSW should be saved after arithmetic");
}
#[test]
fn test_fxrstor_then_arithmetic() {
let mut emu = emu64(); let code = [
0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0xDE, 0xC1, 0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000 + FXSAVE_FCW, 0x037F);
emu.maps.write_word(0x2000 + FXSAVE_FSW, 0x0000);
emu.maps.write_f64(0x2008, 1.5);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, 1.5, "Arithmetic should work after FXRSTOR");
}
#[test]
fn test_fxsave_preserves_control_precision() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, 0x037F);
emu.run(None).unwrap();
let saved_cw = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
let precision = (saved_cw >> 8) & 0x3;
assert_eq!(precision, 0x3, "Precision should be saved as 64-bit");
}
#[test]
fn test_fxsave_preserves_control_rounding() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, 0x037F);
emu.run(None).unwrap();
let saved_cw = emu.maps.read_word(0x3000 + FXSAVE_FCW).unwrap();
let rounding = (saved_cw >> 10) & 0x3;
assert_eq!(rounding, 0x0, "Rounding should be saved as nearest");
}
#[test]
fn test_fxsave_fxrstor_complete_flow() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDB, 0xE3, 0xDD, 0x04, 0x25, 0x10, 0x20, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x18, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x20, 0x40, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x28, 0x40, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.maps.write_f64(0x2008, 2.5);
emu.maps.write_f64(0x2010, 99.0);
emu.run(None).unwrap();
let v1 = emu.maps.read_f64(0x4020).unwrap();
let v2 = emu.maps.read_f64(0x4028).unwrap();
assert_eq!(v1, 2.5, "Second restored value should be 2.5");
assert_eq!(v2, 1.5, "First restored value should be 1.5");
}
#[test]
fn test_fxsave_fxrstor_multiple_cycles() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDB, 0xE3, 0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x40, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0x0F, 0xAE, 0x04, 0x25, 0x00, 0x32, 0x00, 0x00, 0xDB, 0xE3, 0x0F, 0xAE, 0x0C, 0x25, 0x00, 0x32, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x40, 0x00, 0x00, 0xF4, ];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.5);
emu.maps.write_f64(0x2008, 2.5);
emu.run(None).unwrap();
let r1 = emu.maps.read_f64(0x4008).unwrap();
let r2 = emu.maps.read_f64(0x4010).unwrap();
assert_eq!(r1, 1.5, "First cycle result");
assert_eq!(r2, 2.5, "Second cycle result");
}