c64 0.1.0-alpha.1

Driver for the Commodore 64 platform
Documentation
//! HAL for Commodore 64 floating point calculations.

use core::{cmp::Ordering, ffi::CStr, marker::PhantomData, ops::AddAssign};

use crate::{
    hal::{basic::BasicRom, Basic},
    pac::{
        basic,
        kernal::{self, F40},
        peripheral,
    },
};

/// The Floating Point Accumulator register.
pub struct FAC<'a, ROM> {
    _fac: peripheral::FAC,
    _cur: PhantomData<&'a ROM>,
}

impl<'a> FAC<'a, Basic> {
    /// Enables using the floating point functions in the Kernal and BASIC ROMs.
    pub fn with_basic<CHAREN>(fac: peripheral::FAC, basic: &'a BasicRom<'_, CHAREN>) -> Self {
        // We don't actually need to store the BASIC ROM, we just take its lifetime.
        let _ = basic;
        Self {
            _fac: fac,
            _cur: PhantomData,
        }
    }
}

impl<'a> FAC<'a, Basic> {
    /// Loads the given floating point number to FAC.
    pub fn load(&mut self, value: &F40) {
        unsafe { basic::movfm(value) };
    }

    /// Rounds the contents of FAC and reads it.
    ///
    /// Triggers an `?OVERFLOW ERROR` if, after rounding, the contents of FAC can no
    /// longer be represented as a floating point number on the C64.
    pub fn read(&mut self) -> F40 {
        let mut ret = F40::zero();
        unsafe { basic::movmf(&mut ret) };
        ret
    }

    /// Converts a 16-bit signed integer into a floating point number in FAC.
    pub fn load_i16(&mut self, value: i16) {
        unsafe { basic::givayf(value) }
    }

    /// Converts the number in FAC into a 32-bit signed integer, and then returns that
    /// integer cast to an `i16`.
    ///
    /// # Errors
    ///
    /// Triggers an `?ILLEGAL QUANTITY ERROR` if the number in FAC is not in range for `i16`.
    pub fn to_i16(&mut self) -> i16 {
        unsafe { basic::facinx() }
    }

    /// Copies ARG into FAC.
    pub fn load_from_arg(&mut self, arg: &ARG) {
        let _ = arg;
        unsafe { basic::movaf() };
    }

    /// Rounds FAC and then copies it to ARG.
    pub fn copy_to_arg(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::movfa() };
    }

    /// Rounds FAC and converts the result into a nul-terminated ASCII string.
    ///
    /// This method preserves the (rounded) value of FAC, at the cost of latency, code
    /// size, and printing the rounded value. To print the contents of FAC exactly and
    /// more quickly (at the cost of resetting it), use [`Self::into_cstr`].
    pub fn to_cstr<'s>(&mut self, fpstr: &'s mut FPSTR) -> &'s CStr {
        let _ = fpstr;
        let val = self.read();
        let ptr = unsafe { basic::fout() };
        self.load(&val);
        unsafe { CStr::from_ptr(ptr) }
    }

    /// Converts the contents of FAC into a nul-terminated ASCII string.
    ///
    /// After this call, the contents of FAC is reset to 0. To round FAC instead (at the
    /// cost of printing the rounded value), use [`Self::to_cstr`].
    pub fn into_cstr<'s>(&mut self, fpstr: &'s mut FPSTR) -> &'s CStr {
        let _ = fpstr;
        let ptr = unsafe { basic::fout() };
        self.load_i16(0);
        unsafe { CStr::from_ptr(ptr) }
    }

    /// Sets FAC to the largest integer less than or equal to the number in FAC.
    pub fn floor(&mut self) {
        unsafe { basic::int() };
    }

    /// Switches sign on the number in FAC, if non-zero.
    pub fn negate(&mut self) {
        unsafe { basic::negop() };
    }

    /// Sets `ARG = rhs`, then `FAC = FAC + rhs`.
    pub fn add_assign(&mut self, rhs: &F40, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::fadd(rhs) }
    }

    /// Sets `ARG = value`, then `FAC = value - FAC`.
    pub fn sub_from(&mut self, value: &F40, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::fsub(value) };
    }

    /// Sets `FAC = FAC * rhs`.
    ///
    /// After this call, the contents of ARG is undefined.
    pub fn mul_assign(&mut self, rhs: &F40, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::fmult(rhs) }
    }

    /// Sets `ARG = FAC`, then `FAC = FAC * 10`.
    pub fn mul_10(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::mul10() };
    }

    /// Sets `ARG = value`, then `FAC = value / FAC`.
    pub fn divide(&mut self, value: &F40, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::fdiv(value) };
    }

    /// Sets `ARG = FAC`, then `FAC = √FAC`.
    pub fn sqrt(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::sqr() };
    }

    /// Sets `FAC = e^FAC` (the exponential function).
    ///
    /// After this call, the contents of ARG is undefined.
    pub fn exp(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::exp() };
    }

    /// Sets `FAC = ln(FAC)` (the natural logarithm).
    ///
    /// After this call, the contents of ARG is undefined.
    pub fn ln(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::log() };
    }

    /// Sets `FAC = cos(FAC)`.
    pub fn cos(&mut self) {
        unsafe { kernal::cos() }
    }

    /// Sets `FAC = sin(FAC)`.
    pub fn sin(&mut self) {
        unsafe { kernal::sin() }
    }

    /// Sets `FAC = tan(FAC)`.
    pub fn tan(&mut self) {
        unsafe { kernal::tan() }
    }

    /// Sets `FAC = arctan(FAC)`.
    pub fn arctan(&mut self) {
        unsafe { kernal::atn() }
    }

    /// Sets `FAC = poly(FAC)`.
    pub fn poly1<const N: usize>(&mut self, poly: &kernal::Polynomial<kernal::OddTerms, N>) {
        unsafe { poly.poly() };
    }

    /// Sets `FAC = poly(FAC)`.
    pub fn poly2<const N: usize>(&mut self, poly: &kernal::Polynomial<kernal::AllTerms, N>) {
        unsafe { poly.poly() };
    }

    /// Sets `FAC = |FAC|` (the absolute value).
    pub fn abs(&mut self) {
        unsafe { basic::abs() };
    }

    /// Sets `ARG = FAC`, then `FAC = |FAC| / 10`.
    ///
    /// Ignores the sign of the number in FAC; the result is always positive.
    pub fn abs_div_10(&mut self, arg: &mut ARG) {
        let _ = arg;
        unsafe { basic::div10() };
    }

    /// Sets FAC to a number representing the sign of the number currently in FAC.
    ///
    /// - `0` if the number is zero.
    /// - `1` if the number is positive.
    /// - `-1` if the number is negative.
    pub fn signum(&mut self) {
        unsafe { basic::sgn() };
    }

    /// Returns `true` if the number in FAC is positive and `false` if the number is zero or negative.
    pub fn is_positive(&self) -> bool {
        matches!(unsafe { basic::sign() }, Ordering::Greater)
    }

    /// Returns `true` if the number in FAC is negative and `false` if the number is zero or positive.
    pub fn is_negative(&self) -> bool {
        matches!(unsafe { basic::sign() }, Ordering::Less)
    }
}

impl<'a> PartialEq<F40> for FAC<'a, Basic> {
    fn eq(&self, other: &F40) -> bool {
        matches!(self.partial_cmp(other), Some(Ordering::Equal))
    }
}

impl<'a> PartialOrd<F40> for FAC<'a, Basic> {
    fn partial_cmp(&self, other: &F40) -> Option<Ordering> {
        Some(unsafe { basic::fcomp(other) })
    }
}

impl<'a> AddAssign<&ARG> for FAC<'a, Basic> {
    fn add_assign(&mut self, rhs: &ARG) {
        let _ = rhs;
        unsafe { basic::faddt() }
    }
}

/// The Floating Point Argument register.
pub struct ARG {
    _arg: peripheral::ARG,
}

impl ARG {
    /// Constructs an access API for the ARG register.
    pub fn new(arg: peripheral::ARG) -> Self {
        Self { _arg: arg }
    }

    /// Rounds FAC and then copies it to ARG.
    pub fn load_from_fac<'a>(&mut self, fac: &mut FAC<'a, Basic>) {
        fac.copy_to_arg(self);
    }

    /// Sets `FAC = ARG - FAC`.
    pub fn sub<'a>(&mut self, fac: &mut FAC<'a, Basic>) {
        let _ = fac;
        unsafe { basic::fsubt() };
    }

    /// Sets `FAC = ARG / value`.
    pub fn divf<'a>(&mut self, value: &F40, fac: &mut FAC<'a, Basic>) {
        let _ = fac;
        unsafe { basic::fdivarg(value) };
    }

    /// Sets `FAC = ARG / FAC`.
    pub fn div<'a>(&mut self, fac: &mut FAC<'a, Basic>) {
        let _ = fac;
        unsafe { basic::fdivt() };
    }

    /// Sets `FAC = ARG^value`.
    ///
    /// After this call, the contents of ARG is undefined.
    pub fn powf<'a>(&mut self, value: &F40, fac: &mut FAC<'a, Basic>) {
        let _ = fac;
        unsafe { basic::fpwr(value) };
    }

    /// Sets `FAC = ARG^FAC`.
    pub fn pow<'a>(&self, fac: &mut FAC<'a, Basic>) {
        let _ = fac;
        unsafe { basic::fpwrt() };
    }
}

/// The work area for floating point to string conversions.
pub struct FPSTR {
    _fpstr: peripheral::FPSTR,
}

impl FPSTR {
    /// Constructs an access API for the work area.
    pub fn new(fpstr: peripheral::FPSTR) -> Self {
        Self { _fpstr: fpstr }
    }
}