use crate::*;
const DATA_ADDR: u64 = 0x7000;
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_u16(mem: u64, addr: u64, val: u16) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, &val.to_le_bytes());
}
const RC_NEAREST: u16 = 0x037F; const RC_DOWN: u16 = 0x077F; const RC_UP: u16 = 0x0B7F; const RC_ZERO: u16 = 0x0F7F;
#[test]
fn test_frndint_nearest_half_to_even() {
let mut emu = emu64(); let test_cases = vec![(2.5, 2.0), (3.5, 4.0), (4.5, 4.0), (5.5, 6.0)];
for (input, expected) in test_cases {
let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, expected, "FRNDINT({}) nearest", input);
}
}
#[test]
fn test_frndint_round_down() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0xD9, 0xFC, 0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xF4,
];
let test_cases = vec![
(2.1, 2.0), (2.9, 2.0), (-2.1, -3.0), (-2.9, -3.0),
(0.1, 0.0), (0.9, 0.0), (-0.1, -1.0), (-0.9, -1.0),
];
for (input, expected) in test_cases {
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, RC_DOWN);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, expected, "FRNDINT({}) down", input);
}
}
#[test]
fn test_frndint_round_up() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
let test_cases = vec![
(2.1, 3.0), (2.9, 3.0), (-2.1, -2.0), (-2.9, -2.0),
(0.1, 1.0), (0.9, 1.0), (-0.1, 0.0), (-0.9, 0.0),
];
for (input, expected) in test_cases {
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, RC_UP);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, expected, "FRNDINT({}) up", input);
}
}
#[test]
fn test_frndint_round_zero() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
let test_cases = vec![
(2.1, 2.0), (2.9, 2.0), (-2.1, -2.0), (-2.9, -2.0),
(0.1, 0.0), (0.9, 0.0), (-0.1, 0.0), (-0.9, 0.0),
];
for (input, expected) in test_cases {
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, RC_ZERO);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, expected, "FRNDINT({}) zero", input);
}
}
macro_rules! frndint_special_test {
($name:ident, $val:expr, $expected:expr) => {
#[test]
fn $name() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, $val);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
let expected_val: f64 = $expected;
if expected_val.is_nan() {
assert!(result.is_nan());
} else {
assert_eq!(result, $expected);
}
}
};
}
frndint_special_test!(test_frndint_inf, f64::INFINITY, f64::INFINITY);
frndint_special_test!(test_frndint_neg_inf, f64::NEG_INFINITY, f64::NEG_INFINITY);
frndint_special_test!(test_frndint_zero, 0.0, 0.0);
frndint_special_test!(test_frndint_neg_zero, -0.0, -0.0);
frndint_special_test!(test_frndint_nan, f64::NAN, f64::NAN);
frndint_special_test!(test_frndint_one, 1.0, 1.0);
frndint_special_test!(test_frndint_neg_one, -1.0, -1.0);
#[test]
fn test_frndint_large_integer() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1e15);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, 1e15);
}
#[test]
fn test_frndint_max_value() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, f64::MAX);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, f64::MAX);
}
#[test]
fn test_frndint_mode_switching() {
let mut emu = emu64(); let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xD9, 0x2C, 0x25, 0x10, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x18, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, RC_NEAREST);
emu.maps.write_f64(0x2008, 2.5);
emu.maps.write_word(0x2010, RC_DOWN);
emu.maps.write_f64(0x2018, 2.9);
emu.run(None).unwrap();
let r1 = emu.maps.read_f64(0x3000).unwrap();
let r2 = emu.maps.read_f64(0x3008).unwrap();
assert_eq!(r1, 2.0); assert_eq!(r2, 2.0); }
#[test]
fn test_frndint_all_modes_positive() {
let mut emu = emu64(); let input = 7.6;
let expected = [(RC_NEAREST, 8.0), (RC_DOWN, 7.0), (RC_UP, 8.0), (RC_ZERO, 7.0)];
for (mode, exp) in expected {
let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, mode);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, exp, "Mode {:04X}", mode);
}
}
#[test]
fn test_frndint_all_modes_negative() {
let mut emu = emu64(); let input = -7.6;
let expected = [(RC_NEAREST, -8.0), (RC_DOWN, -8.0), (RC_UP, -7.0), (RC_ZERO, -7.0)];
for (mode, exp) in expected {
let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, mode);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, exp, "Mode {:04X}", mode);
}
}
#[test]
fn test_frndint_sequence() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x10, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x10, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 1.4);
emu.maps.write_f64(0x2008, 2.5);
emu.maps.write_f64(0x2010, 3.6);
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 1.0);
assert_eq!(emu.maps.read_f64(0x3008).unwrap(), 2.0);
assert_eq!(emu.maps.read_f64(0x3010).unwrap(), 4.0);
}
macro_rules! batch_test {
($name:ident, $mode:expr, $cases:expr) => {
#[test]
fn $name() {
let mut emu = emu64(); for (input, expected) in $cases {
let code = [
0xD9, 0x2C, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_word(0x2000, $mode);
emu.maps.write_f64(0x2008, input);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, expected, "{}", input);
}
}
};
}
batch_test!(test_nearest_fractional, RC_NEAREST, vec![
(1.1, 1.0), (1.9, 2.0), (2.5, 2.0), (3.5, 4.0),
(-1.1, -1.0), (-1.9, -2.0), (-2.5, -2.0), (-3.5, -4.0)
]);
batch_test!(test_down_fractional, RC_DOWN, vec![
(1.1, 1.0), (1.9, 1.0), (2.5, 2.0), (3.5, 3.0),
(-1.1, -2.0), (-1.9, -2.0), (-2.5, -3.0), (-3.5, -4.0)
]);
batch_test!(test_up_fractional, RC_UP, vec![
(1.1, 2.0), (1.9, 2.0), (2.5, 3.0), (3.5, 4.0),
(-1.1, -1.0), (-1.9, -1.0), (-2.5, -2.0), (-3.5, -3.0)
]);
batch_test!(test_zero_fractional, RC_ZERO, vec![
(1.1, 1.0), (1.9, 1.0), (2.5, 2.0), (3.5, 3.0),
(-1.1, -1.0), (-1.9, -1.0), (-2.5, -2.0), (-3.5, -3.0)
]);
#[test]
fn test_frndint_tiny_values() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, 0.0001);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, 0.0);
}
#[test]
fn test_frndint_preserves_integers() {
let mut emu = emu64(); for val in &[1.0, 2.0, 100.0, -50.0, 999.0] {
let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, *val);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, *val);
}
}
#[test]
fn test_frndint_denormal() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xD9, 0xFC,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xF4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x2000, f64::MIN_POSITIVE / 2.0);
emu.run(None).unwrap();
let result = emu.maps.read_f64(0x3000).unwrap();
assert_eq!(result, 0.0);
}