#![no_std]
#![no_main]
extern crate alloc;
extern crate mos_alloc;
use alloc::vec::Vec;
use core::ffi::c_int;
use core::panic::PanicInfo;
use ufmt_stdio::*;
use c64::{
hal::{
basic::BasicRom,
fp::{ARG, FAC, FPSTR},
kernal::KernalRom,
AddressSpace, Basic,
},
pac::{basic, kernal::F40, Peripherals},
};
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
println!("PANIC!!!");
loop {}
}
#[no_mangle]
extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int {
let p = Peripherals::take().unwrap();
#[cfg(not(feature = "ultimate"))]
let asp = AddressSpace::at_boot(p.ASP).ok().unwrap();
#[cfg(feature = "ultimate")]
let asp = AddressSpace::at_web_executor(p.ASP)
.ok()
.unwrap()
.enable_basic();
let kernal = KernalRom::new(&asp);
let basic = BasicRom::new(&asp);
let mut fac = FAC::with_basic(p.FAC, &basic);
let mut arg = ARG::new(p.ARG);
let mut fpstr = FPSTR::new(p.FPSTR);
let log = basic.log_constants();
let exp_c = basic.exp_constants();
let trig = kernal.trig_constants();
let one: F40 = F40::one();
let zero: F40 = F40::zero();
let neg_one: F40 = -F40::one();
let half: F40 = -log.neg_0_point_5; let neg_half: F40 = log.neg_0_point_5; let root_2: F40 = log.root_2;
let pi_over_2: F40 = trig.pi_over_2;
let pi_times_2: F40 = trig.pi_times_2;
let zero_25: F40 = trig.zero_point_2_5;
let two = F40::new(2, 0).unwrap();
let three = F40::new(3, 0).unwrap();
let four = F40::new(4, 0).unwrap();
let five = F40::new(5, 0).unwrap();
let six = F40::new(6, 0).unwrap();
let eight = F40::new(8, 0).unwrap();
let ten = F40::new(10, 0).unwrap();
let neg_five = F40::new(-5, 0).unwrap();
let neg_ten = F40::new(-10, 0).unwrap();
const ULP_NEAR_ONE: u8 = 0x68;
let mut r = Report::new();
reset(&mut fac, &mut arg);
fac.load_i16(0);
check_fac(&mut r, "givayf(0)", &zero, &mut fac);
fac.load_i16(1);
check_fac(&mut r, "givayf(1)", &one, &mut fac);
fac.load_i16(-1);
check_fac(&mut r, "givayf(-1)", &neg_one, &mut fac);
fac.load_i16(42);
check_i16(&mut r, "facinx(fp(42))", 42, fac.to_i16());
fac.load_i16(-30000);
check_i16(&mut r, "facinx(fp(-30000))", -30000, fac.to_i16());
fac.load_i16(0);
check_i16(&mut r, "facinx(fp(0))", 0, fac.to_i16());
reset(&mut fac, &mut arg);
fac.load(&pi_over_2);
check_fac(&mut r, "movfm/movmf(pi/2)", &pi_over_2, &mut fac);
fac.load(&pi_times_2);
check_fac(&mut r, "movfm/movmf(2*pi)", &pi_times_2, &mut fac);
fac.load(&neg_half);
check_fac(&mut r, "movfm/movmf(-0.5)", &neg_half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&root_2);
let _ = fac.to_cstr(&mut fpstr);
check_fac(&mut r, "to_cstr preserves FAC", &root_2, &mut fac);
fac.load(&root_2);
let _ = fac.into_cstr(&mut fpstr);
check_fac(&mut r, "into_cstr resets FAC", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
check_bool(&mut r, "fp(1) == fp(1)", true, fac == one);
check_bool(&mut r, "fp(1) < pi/2", true, fac < pi_over_2);
check_bool(&mut r, "fp(1) > -0.5", true, fac > neg_half);
check_bool(&mut r, "fp(1) == fp(0)", false, fac == zero);
fac.load(&zero);
check_bool(&mut r, "fp(0) == fp(0)", true, fac == zero);
reset(&mut fac, &mut arg);
fac.load(&neg_half);
check_bool(&mut r, "sign(-0.5) +ve", false, fac.is_positive());
check_bool(&mut r, "sign(-0.5) -ve", true, fac.is_negative());
fac.load(&zero);
check_bool(&mut r, "sign(0) +ve", false, fac.is_positive());
check_bool(&mut r, "sign(0) -ve", false, fac.is_negative());
fac.load(&root_2);
check_bool(&mut r, "sign(sqrt(2)) +ve", true, fac.is_positive());
check_bool(&mut r, "sign(sqrt(2)) -ve", false, fac.is_negative());
reset(&mut fac, &mut arg);
fac.load(&pi_times_2);
fac.copy_to_arg(&mut arg);
fac.load(&zero);
fac.load_from_arg(&arg);
check_fac(&mut r, "movfa→movaf(2*pi)", &pi_times_2, &mut fac);
fac.load(&neg_half);
fac.copy_to_arg(&mut arg);
fac.load(&one);
fac.load_from_arg(&arg);
check_fac(&mut r, "movfa→movaf(-0.5)", &neg_half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.negate();
check_fac(&mut r, "negate(1)", &neg_one, &mut fac);
fac.load(&neg_one);
fac.negate();
check_fac(&mut r, "negate(-1)", &one, &mut fac);
fac.load(&neg_half);
fac.negate();
check_fac(&mut r, "negate(-0.5)", &half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&neg_half);
fac.abs();
check_fac(&mut r, "abs(-0.5)", &half, &mut fac);
fac.load(&one);
fac.abs();
check_fac(&mut r, "abs(1)", &one, &mut fac);
fac.load(&neg_one);
fac.abs();
check_fac(&mut r, "abs(-1)", &one, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&root_2);
fac.signum();
check_fac(&mut r, "sgn(sqrt(2))", &one, &mut fac);
fac.load(&neg_half);
fac.signum();
check_fac(&mut r, "sgn(-0.5)", &neg_one, &mut fac);
fac.load(&zero);
fac.signum();
check_fac(&mut r, "sgn(0)", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
unsafe { basic::round() };
check_fac(&mut r, "round(1)", &one, &mut fac);
fac.load(&neg_half);
unsafe { basic::round() };
check_fac(&mut r, "round(-0.5)", &neg_half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&pi_times_2);
fac.floor();
check_i16(&mut r, "int(2*pi)", 6, fac.to_i16());
fac.load(&neg_half);
fac.floor();
check_i16(&mut r, "int(-0.5)", -1, fac.to_i16());
fac.load(&zero_25);
fac.floor();
check_i16(&mut r, "int(0.25)", 0, fac.to_i16());
fac.load(&one);
fac.floor();
check_i16(&mut r, "int(1)", 1, fac.to_i16());
reset(&mut fac, &mut arg);
fac.load(&pi_times_2);
unsafe { basic::qint() };
reset(&mut fac, &mut arg);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.add_assign(&one, &mut arg);
check_fac(&mut r, "fadd(1, 1) == 2", &two, &mut fac);
fac.load(&zero);
fac.add_assign(&root_2, &mut arg);
check_fac(&mut r, "fadd(0, sqrt(2))", &root_2, &mut fac);
let neg_root_2: F40 = -root_2;
fac.load(&root_2);
fac.add_assign(&neg_root_2, &mut arg);
check_fac(&mut r, "fadd(sqrt(2), -sqrt(2))", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.copy_to_arg(&mut arg);
fac.load(&one);
fac += &arg;
check_fac(&mut r, "faddt(1+1)", &two, &mut fac);
fac.load(&root_2);
fac.copy_to_arg(&mut arg);
fac.load(&zero);
fac += &arg;
check_fac(&mut r, "faddt(0+sqrt(2))", &root_2, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.sub_from(&two, &mut arg);
check_fac(&mut r, "fsub(2 - 1)", &one, &mut fac);
fac.load(&two);
fac.sub_from(&one, &mut arg);
check_fac(&mut r, "fsub(1 - 2)", &neg_one, &mut fac);
let neg_root_2: F40 = -root_2;
fac.load(&root_2);
fac.sub_from(&zero, &mut arg);
check_fac(&mut r, "fsub(0 - sqrt(2))", &neg_root_2, &mut fac);
fac.load(&root_2);
fac.sub_from(&root_2, &mut arg);
check_fac(&mut r, "fsub(sqrt(2) - sqrt(2))", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&two);
fac.copy_to_arg(&mut arg);
fac.load(&one);
arg.sub(&mut fac);
check_fac(&mut r, "fsubt(2 - 1)", &one, &mut fac);
fac.load(&one);
fac.copy_to_arg(&mut arg);
fac.load(&two);
arg.sub(&mut fac);
check_fac(&mut r, "fsubt(1 - 2)", &neg_one, &mut fac);
fac.load(&root_2);
fac.copy_to_arg(&mut arg);
fac.load(&root_2);
arg.sub(&mut fac);
check_fac(&mut r, "fsubt(sqrt(2) - sqrt(2))", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&two);
fac.mul_assign(&three, &mut arg);
check_fac(&mut r, "fmult(2 * 3)", &six, &mut fac);
fac.load(&four);
fac.mul_assign(&half, &mut arg);
check_fac(&mut r, "fmult(4 * 0.5)", &two, &mut fac);
fac.load(&zero);
fac.mul_assign(&root_2, &mut arg);
check_fac(&mut r, "fmult(0 * sqrt(2))", &zero, &mut fac);
fac.load(&neg_one);
fac.mul_assign(&half, &mut arg);
check_fac(&mut r, "fmult(-1 * 0.5)", &neg_half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.mul_10(&mut arg);
check_fac(&mut r, "mul_10(1)", &ten, &mut fac);
fac.load(&half);
fac.mul_10(&mut arg);
check_fac(&mut r, "mul_10(0.5)", &five, &mut fac);
fac.load(&zero);
fac.mul_10(&mut arg);
check_fac(&mut r, "mul_10(0)", &zero, &mut fac);
fac.load(&neg_half);
fac.mul_10(&mut arg);
check_fac(&mut r, "mul_10(-0.5)", &neg_five, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&two);
fac.divide(&four, &mut arg);
check_fac(&mut r, "fdiv(4 / 2)", &two, &mut fac);
fac.load(&two);
fac.divide(&one, &mut arg);
check_fac(&mut r, "fdiv(1 / 2)", &half, &mut fac);
fac.load(&two);
fac.divide(&neg_one, &mut arg);
check_fac(&mut r, "fdiv(-1 / 2)", &neg_half, &mut fac);
fac.load(&root_2);
fac.divide(&root_2, &mut arg);
check_fac_close(
&mut r,
"fdiv(sqrt(2)/sqrt(2))",
&one,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
reset(&mut fac, &mut arg);
fac.load(&four);
fac.copy_to_arg(&mut arg);
arg.divf(&two, &mut fac);
check_fac(&mut r, "fdivarg(4 / 2)", &two, &mut fac);
fac.load(&two);
fac.copy_to_arg(&mut arg);
arg.divf(&four, &mut fac);
check_fac(&mut r, "fdivarg(2 / 4)", &half, &mut fac);
fac.load(&neg_one);
fac.copy_to_arg(&mut arg);
arg.divf(&two, &mut fac);
check_fac(&mut r, "fdivarg(-1 / 2)", &neg_half, &mut fac);
fac.load(&one);
fac.copy_to_arg(&mut arg);
arg.divf(&neg_one, &mut fac);
check_fac(&mut r, "fdivarg(1 / -1)", &neg_one, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&four);
fac.copy_to_arg(&mut arg);
fac.load(&two);
arg.div(&mut fac);
check_fac(&mut r, "fdivt(4 / 2)", &two, &mut fac);
fac.load(&two);
fac.copy_to_arg(&mut arg);
fac.load(&four);
arg.div(&mut fac);
check_fac(&mut r, "fdivt(2 / 4)", &half, &mut fac);
fac.load(&one);
fac.copy_to_arg(&mut arg);
fac.load(&neg_one);
arg.div(&mut fac);
check_fac(&mut r, "fdivt(1 / -1)", &neg_one, &mut fac);
fac.load(&root_2);
fac.copy_to_arg(&mut arg);
fac.load(&root_2);
arg.div(&mut fac);
check_fac_close(
&mut r,
"fdivt(sqrt(2)/sqrt(2))",
&one,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
reset(&mut fac, &mut arg);
fac.load(&ten);
fac.abs_div_10(&mut arg);
check_fac(&mut r, "div10(10)", &one, &mut fac);
fac.load(&neg_ten);
fac.abs_div_10(&mut arg);
check_fac(&mut r, "div10(-10) (abs)", &one, &mut fac);
fac.load(&zero);
fac.abs_div_10(&mut arg);
check_fac(&mut r, "div10(0)", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load_i16(0);
fac.sqrt(&mut arg);
check_fac(&mut r, "sqrt(0)", &zero, &mut fac);
fac.load_i16(1);
fac.sqrt(&mut arg);
check_fac(&mut r, "sqrt(1)", &one, &mut fac);
fac.load(&four);
fac.sqrt(&mut arg);
check_fac(&mut r, "sqrt(4)", &two, &mut fac);
fac.load(&zero_25);
fac.sqrt(&mut arg);
check_fac(&mut r, "sqrt(0.25)", &half, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&two);
fac.copy_to_arg(&mut arg);
arg.powf(&zero, &mut fac);
check_fac_close(&mut r, "fpwr(2^0)", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&four);
fac.copy_to_arg(&mut arg);
arg.powf(&half, &mut fac);
check_fac_close(
&mut r,
"fpwr(4^0.5)",
&two,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
fac.load(&one);
fac.copy_to_arg(&mut arg);
arg.powf(&pi_times_2, &mut fac);
check_fac_close(
&mut r,
"fpwr(1^2pi)",
&one,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
reset(&mut fac, &mut arg);
fac.load(&two);
fac.copy_to_arg(&mut arg);
fac.load(&zero);
arg.pow(&mut fac);
check_fac_close(&mut r, "fpwrt(2^0)", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&four);
fac.copy_to_arg(&mut arg);
fac.load(&half);
arg.pow(&mut fac);
check_fac_close(
&mut r,
"fpwrt(4^0.5)",
&two,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
fac.load(&two);
fac.copy_to_arg(&mut arg);
fac.load(&three);
arg.pow(&mut fac);
check_fac_close(
&mut r,
"fpwrt(2^3)",
&eight,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
reset(&mut fac, &mut arg);
fac.load(&zero);
fac.exp(&mut arg);
check_fac_close(&mut r, "exp(0)", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
reset(&mut fac, &mut arg);
fac.load(&one);
fac.ln(&mut arg);
check_fac_close(&mut r, "ln(1)", &zero, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&one);
fac.exp(&mut arg);
fac.ln(&mut arg);
check_fac_close(&mut r, "ln(exp(1))", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
reset(&mut fac, &mut arg);
fac.load(&zero);
fac.sin();
check_fac(&mut r, "sin(0)", &zero, &mut fac);
fac.load(&pi_over_2);
fac.sin();
check_fac_close(&mut r, "sin(pi/2)", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&zero);
fac.cos();
check_fac_close(&mut r, "cos(0)", &one, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&pi_over_2);
fac.cos();
check_fac_close(&mut r, "cos(pi/2)", &zero, ULP_NEAR_ONE, &mut fac, &mut arg);
fac.load(&zero);
fac.tan();
check_fac(&mut r, "tan(0)", &zero, &mut fac);
fac.load(&zero);
fac.arctan();
check_fac(&mut r, "atn(0)", &zero, &mut fac);
reset(&mut fac, &mut arg);
fac.load(&zero);
fac.poly2(&exp_c.two_pow_x);
check_fac_close(
&mut r,
"poly2(2^X, 0)",
&one,
ULP_NEAR_ONE,
&mut fac,
&mut arg,
);
reset(&mut fac, &mut arg);
fac.load(&zero);
fac.poly1(&log.logcn2);
check_fac(&mut r, "poly1(logcn2, 0)", &zero, &mut fac);
println!("=== fp-test ===");
let passed = r.total - r.failures.len() as u16;
println!("{} / {} passed", passed, r.total);
if !r.failures.is_empty() {
println!("--- failures ---");
for f in &r.failures {
println!("{}", f);
}
}
0
}
struct Report {
total: u16,
failures: Vec<Failure>,
}
impl Report {
fn new() -> Self {
Self {
total: 0,
failures: Vec::new(),
}
}
}
enum Failure {
Value {
name: &'static str,
expected: [u8; 5],
actual: [u8; 5],
},
I16 {
name: &'static str,
expected: i16,
actual: i16,
},
Bool {
name: &'static str,
expected: bool,
actual: bool,
},
}
impl uDisplay for Failure {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
match self {
Failure::Value {
name,
expected,
actual,
} => {
uwriteln!(f, "FAIL {}", name)?;
uwriteln!(
f,
" exp: {} {} {} {} {}",
expected[0],
expected[1],
expected[2],
expected[3],
expected[4]
)?;
uwrite!(
f,
" got: {} {} {} {} {}",
actual[0],
actual[1],
actual[2],
actual[3],
actual[4]
)
}
Failure::I16 {
name,
expected,
actual,
} => {
uwriteln!(f, "FAIL {}", name)?;
uwriteln!(f, " exp: {}", *expected)?;
uwrite!(f, " got: {}", *actual)
}
Failure::Bool {
name,
expected,
actual,
} => {
uwriteln!(f, "FAIL {}", name)?;
uwriteln!(f, " exp: {}", *expected)?;
uwrite!(f, " got: {}", *actual)
}
}
}
}
fn f40_bytes(f: &F40) -> [u8; 5] {
let raw = unsafe { core::ptr::read(f as *const F40 as *const [u8; 5]) };
if raw[0] == 0 {
[0; 5]
} else {
raw
}
}
fn check_fac(report: &mut Report, name: &'static str, expected: &F40, fac: &mut FAC<'_, Basic>) {
report.total += 1;
let actual = fac.read();
fac.load(&actual);
let exp_bytes = f40_bytes(expected);
let act_bytes = f40_bytes(&actual);
if exp_bytes != act_bytes {
report.failures.push(Failure::Value {
name,
expected: exp_bytes,
actual: act_bytes,
});
}
}
fn check_fac_close(
report: &mut Report,
name: &'static str,
expected: &F40,
max_diff_exp: u8,
fac: &mut FAC<'_, Basic>,
arg: &mut ARG,
) {
report.total += 1;
let actual = fac.read();
let exp_bytes = f40_bytes(expected);
let act_bytes = f40_bytes(&actual);
if exp_bytes == act_bytes {
fac.load(&actual);
return;
}
fac.load(&actual);
fac.sub_from(expected, arg);
fac.abs();
let diff = fac.read();
let diff_bytes = f40_bytes(&diff);
if diff_bytes[0] != 0 && diff_bytes[0] >= max_diff_exp {
report.failures.push(Failure::Value {
name,
expected: exp_bytes,
actual: act_bytes,
});
}
fac.load(&actual);
}
fn check_i16(report: &mut Report, name: &'static str, expected: i16, actual: i16) {
report.total += 1;
if expected != actual {
report.failures.push(Failure::I16 {
name,
expected,
actual,
});
}
}
fn check_bool(report: &mut Report, name: &'static str, expected: bool, actual: bool) {
report.total += 1;
if expected != actual {
report.failures.push(Failure::Bool {
name,
expected,
actual,
});
}
}
fn reset(fac: &mut FAC<'_, Basic>, arg: &mut ARG) {
fac.load(&F40::zero());
fac.copy_to_arg(arg);
fac.load(&F40::zero());
}