use crate::*;
use std::convert::TryInto;
const DATA_ADDR: u64 = 0x2000;
fn write_f64(mem: u64, addr: u64, value: f64) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, &value.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_bcd(mem: u64, addr: u64, value: &[u8; 10]) {
let mut emu = emu64(); emu.maps.write_bytes_slice(addr, value);
}
fn read_bcd(mem: u64, addr: u64) -> [u8; 10] {
let emu = emu64(); let mut buf = [0u8; 10];
emu.maps.read_bytes_buff(&mut buf, addr);
buf
}
fn make_bcd(value: i64) -> [u8; 10] {
let emu = emu64(); let mut bcd = [0u8; 10];
let is_negative = value < 0;
let mut abs_value = value.abs() as u64;
for i in 0..9 {
let low = (abs_value % 10) as u8;
abs_value /= 10;
let high = (abs_value % 10) as u8;
abs_value /= 10;
bcd[i] = (high << 4) | low;
}
bcd[9] = if is_negative { 0x80 } else { 0x00 };
bcd
}
fn parse_bcd(bcd: &[u8; 10]) -> Option<i64> {
let emu = emu64(); let is_negative = (bcd[9] & 0x80) != 0;
let mut value: i64 = 0;
let mut multiplier: i64 = 1;
for i in 0..9 {
let low = (bcd[i] & 0x0F) as i64;
let high = ((bcd[i] >> 4) & 0x0F) as i64;
if low > 9 || high > 9 {
return None; }
value += low * multiplier;
multiplier *= 10;
value += high * multiplier;
multiplier *= 10;
}
if is_negative {
Some(-value)
} else {
Some(value)
}
}
#[test]
fn test_fbld_zero() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(0));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 0.0);
}
#[test]
fn test_fbld_positive_one() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(1));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 1.0);
}
#[test]
fn test_fbld_negative_one() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(-1));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), -1.0);
}
#[test]
fn test_fbld_positive_123() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(123));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 123.0);
}
#[test]
fn test_fbld_negative_456() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(-456));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), -456.0);
}
#[test]
fn test_fbld_large_positive() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(123456789));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 123456789.0);
}
#[test]
fn test_fbld_large_negative() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(-987654321));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), -987654321.0);
}
#[test]
fn test_fbld_max_digits() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(999999999999999999));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 999999999999999999.0);
}
#[test]
fn test_fbld_pushes_to_stack() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(0x3000, 99.0);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(42));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3008).unwrap(), 42.0); assert_eq!(emu.maps.read_f64(0x3010).unwrap(), 99.0); }
#[test]
fn test_fbld_multiple() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDF, 0x24, 0x25, 0x0A, 0x20, 0x00, 0x00, 0xDF, 0x24, 0x25, 0x14, 0x20, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x08, 0x30, 0x00, 0x00, 0xDD, 0x1C, 0x25, 0x10, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(10));
emu.maps.write_bytes_slice(DATA_ADDR + 10, &make_bcd(20));
emu.maps.write_bytes_slice(DATA_ADDR + 20, &make_bcd(30));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 30.0);
assert_eq!(emu.maps.read_f64(0x3008).unwrap(), 20.0);
assert_eq!(emu.maps.read_f64(0x3010).unwrap(), 10.0);
}
#[test]
fn test_fbstp_zero() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 0.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(0));
}
#[test]
fn test_fbstp_positive_one() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 1.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(1));
}
#[test]
fn test_fbstp_negative_one() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, -1.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(-1));
}
#[test]
fn test_fbstp_positive_123() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 123.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(123));
}
#[test]
fn test_fbstp_negative_456() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, -456.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(-456));
}
#[test]
fn test_fbstp_large_positive() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 123456789.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(123456789));
}
#[test]
fn test_fbstp_large_negative() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, -987654321.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(-987654321));
}
#[test]
fn test_fbstp_rounds_down() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 123.4);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(123));
}
#[test]
fn test_fbstp_rounds_up() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 123.6);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(124));
}
#[test]
fn test_fbstp_pops_stack() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDF, 0x34, 0x25, 0x0A, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 100.0);
emu.maps.write_f64(DATA_ADDR + 8, 200.0);
emu.run(None).unwrap();
let bcd1 = emu.maps.read_bytes(0x3000, 10);
let bcd2 = emu.maps.read_bytes(0x300A, 10);
assert_eq!(parse_bcd((bcd1).try_into().unwrap()), Some(200));
assert_eq!(parse_bcd((bcd2).try_into().unwrap()), Some(100));
}
#[test]
fn test_fbld_fbstp_roundtrip_positive() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
let original_bcd = make_bcd(12345);
emu.maps.write_bytes_slice(DATA_ADDR, &original_bcd);
emu.run(None).unwrap();
let result_bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((result_bcd).try_into().unwrap()), Some(12345));
}
#[test]
fn test_fbld_fbstp_roundtrip_negative() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
let original_bcd = make_bcd(-67890);
emu.maps.write_bytes_slice(DATA_ADDR, &original_bcd);
emu.run(None).unwrap();
let result_bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((result_bcd).try_into().unwrap()), Some(-67890));
}
#[test]
fn test_fbld_fbstp_roundtrip_zero() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
let original_bcd = make_bcd(0);
emu.maps.write_bytes_slice(DATA_ADDR, &original_bcd);
emu.run(None).unwrap();
let result_bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((result_bcd).try_into().unwrap()), Some(0));
}
#[test]
fn test_fbld_fbstp_roundtrip_large() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
let original_bcd = make_bcd(999999999999);
emu.maps.write_bytes_slice(DATA_ADDR, &original_bcd);
emu.run(None).unwrap();
let result_bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((result_bcd).try_into().unwrap()), Some(999999999999));
}
#[test]
fn test_fbld_arithmetic() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDF, 0x24, 0x25, 0x0A, 0x20, 0x00, 0x00, 0xDE, 0xC1, 0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(100));
emu.maps.write_bytes_slice(DATA_ADDR + 10, &make_bcd(200));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 300.0);
}
#[test]
fn test_fbstp_after_arithmetic() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDD, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00, 0xDE, 0xC1, 0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 50.0);
emu.maps.write_f64(DATA_ADDR + 8, 30.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(80));
}
#[test]
fn test_fbld_fbstp_sequence() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00, 0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00, 0xDF, 0x24, 0x25, 0x0A, 0x20, 0x00, 0x00, 0xDF, 0x34, 0x25, 0x0A, 0x30, 0x00, 0x00, 0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(111));
emu.maps.write_bytes_slice(DATA_ADDR + 10, &make_bcd(222));
emu.run(None).unwrap();
let bcd1 = emu.maps.read_bytes(0x3000, 10);
let bcd2 = emu.maps.read_bytes(0x300A, 10);
assert_eq!(parse_bcd((bcd1).try_into().unwrap()), Some(111));
assert_eq!(parse_bcd((bcd2).try_into().unwrap()), Some(222));
}
#[test]
fn test_fbstp_very_large() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 999999999999999.0);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(999999999999999));
}
#[test]
fn test_fbld_all_nines() {
let mut emu = emu64(); let code = [
0xDF, 0x24, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDD, 0x1C, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_bytes_slice(DATA_ADDR, &make_bcd(999999));
emu.run(None).unwrap();
assert_eq!(emu.maps.read_f64(0x3000).unwrap(), 999999.0);
}
#[test]
fn test_fbstp_rounding_half() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, 99.5);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(100));
}
#[test]
fn test_fbstp_negative_rounding() {
let mut emu = emu64(); let code = [
0xDD, 0x04, 0x25, 0x00, 0x20, 0x00, 0x00,
0xDF, 0x34, 0x25, 0x00, 0x30, 0x00, 0x00,
0xf4,
];
emu.load_code_bytes(&code);
emu.maps.write_f64(DATA_ADDR, -99.7);
emu.run(None).unwrap();
let bcd = emu.maps.read_bytes(0x3000, 10);
assert_eq!(parse_bcd((bcd).try_into().unwrap()), Some(-100));
}