c64 0.1.0-alpha.0

Driver for the Commodore 64 platform
//! BASIC ROM constants and functions.

use core::cmp::Ordering;

use crate::pac::kernal::{AllTerms, OddTerms, Polynomial, F40};

extern "C" {
    fn basic_facinx() -> i16;
    fn basic_givayf(value: i16);
    fn basic_fadd(addr: *const u8);
    fn basic_faddt();
    fn basic_movfm(addr: *const u8);
    fn basic_movmf(addr: *mut u8);
    fn basic_movaf() -> u8;
    fn basic_movfa() -> u8;
    fn basic_fcomp(addr: *const u8) -> i8;
    fn basic_qint();
    fn basic_fout() -> *const i8;
    fn basic_negop();
}

/// Pointer to the BASIC ROM identifier "CBMBASIC".
pub const IDENTIFIER: *const [u8; 8] = 0xa004 as *const _;

/// Pointer to the logarithm constants in the BASIC ROM.
pub const LOG_CONSTANTS: *const LogConstants = 0xb9bc as *const _;

/// The logarithm constants in the BASIC ROM.
#[repr(C, packed)]
pub struct LogConstants {
    /// The encoding of 1.0.
    pub one: F40,
    /// The polynomial table approximating `log_2(√2/(1 - X) - √0.5)` over the domain `[-(3-2√2), (3-2√2))`.
    pub logcn2: Polynomial<OddTerms, 4>,
    /// The approximation of (√2)/2.
    pub root_2_over_2: F40,
    /// The approximation of √2.
    pub root_2: F40,
    /// The encoding of -0.5.
    pub neg_0_point_5: F40,
}

/// Pointer to the inverse natural log (e^x) constants in the BASIC ROM.
pub const EXP_CONSTANTS: *const ExpConstants = 0xbfbf as *const _;

/// The inverse natural log (e^x) constants in the BASIC ROM.
#[repr(C, packed)]
pub struct ExpConstants {
    /// The approximation of `1/ln(2)`.
    pub one_over_ln_2: F40,
    /// The polynomial table approximating `2^X` over the domain `[0, 1)`.
    pub two_pow_x: Polynomial<AllTerms, 8>,
}

/// Convert number in FAC into a 16-bit signed integer.
///
/// Triggers an `?ILLEGAL QUANTITY ERROR` if the number in FAC is not in range for `i16`.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn facinx() -> i16 {
    unsafe { basic_facinx() }
}

/// Convert 16-bit signed integer into a floating point number in FAC.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn givayf(value: i16) {
    unsafe { basic_givayf(value) }
}

/// Sets `FAC = FAC + value`.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn fadd(value: *const F40) {
    unsafe { basic_fadd(value as _) }
}

/// Sets `FAC = ARG + FAC`.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn faddt() {
    unsafe { basic_faddt() }
}

/// Load FAC from memory.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn movfm(value: *const F40) {
    unsafe { basic_movfm(value as _) }
}

/// Store FAC to memory.
///
/// Triggers an `?OVERFLOW ERROR` if, after rounding, the contents of FAC can no longer be
/// represented as a floating point number on the C64.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn movmf(value: *mut F40) {
    unsafe { basic_movmf(value as _) }
}

/// Copy a number currently in ARG, over into FAC.
///
/// Returns the exponent of the number.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn movaf() -> u8 {
    unsafe { basic_movaf() }
}

/// Copy a number currently in FAC, over into ARG.
///
/// Returns the exponent of the number.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn movfa() -> u8 {
    unsafe { basic_movfa() }
}

/// Compares `FAC` to the given number.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn fcomp(value: *const F40) -> Ordering {
    let ret = unsafe { basic_fcomp(value as _) };
    match ret {
        1 => Ordering::Greater,
        0 => Ordering::Equal,
        -1 => Ordering::Less,
        _ => unreachable!(),
    }
}

/// Converts `FAC` into an `i32` internally, truncating all decimal places.
///
/// A negative number is encoded in the FAC's mantissa slot as two's complement, instead
/// of using the normal separate sign bit.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn qint() {
    unsafe { basic_qint() }
}

/// Converts the contents of FAC into a nul-terminated ASCII string.
///
/// After this call, the contents of FAC is undefined.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn fout() -> *const i8 {
    unsafe { basic_fout() }
}

/// Switches sign on the number in FAC, if non-zero.
///
/// # Safety
///
/// - Do not call this function if the BASIC ROM overlay is disabled.
#[inline]
pub unsafe fn negop() {
    unsafe { basic_negop() }
}