// 32-bit IEEE 754 soft-float runtime for the W65816 backend.
//
// Implements the libcalls clang emits for float ops:
//   __addsf3, __subsf3, __mulsf3, __divsf3
//   __negsf2
//   __cmpsf2, __eqsf2, __nesf2, __ltsf2, __gtsf2, __lesf2, __gesf2
//   __floatsisf, __floatunsisf
//   __fixsfsi, __fixunssfsi
//
// All routines operate on the 32-bit IEEE representation cast through
// `unsigned long` so the compiler treats them as integers.  No actual
// float operators appear in the source, so no recursive __addsf3 etc.
// references are emitted; the only libcalls used are __mulsi3 (for
// multiplying mantissas) and shift helpers, which already exist in
// libgcc.s.
//
// Limitations (V1):
//   - No subnormal / denormal handling — values flush to zero.
//   - No NaN / Inf handling — operations on these give garbage but
//     don't crash.
//   - Round-to-zero (truncation) only; no banker's rounding.
//   - Add/sub use a 24-bit mantissa; underflow rounding is crude.
//
// These are correct enough for end-to-end test programs that do
// "normal" arithmetic in the representable range.  Production-grade
// IEEE compliance is a significantly bigger project.

typedef unsigned long u32;
typedef long          s32;
typedef unsigned int  u16;
typedef int           s16;

// IEEE 754 single bit fields.
#define SIGN_BIT   0x80000000UL
#define EXP_MASK   0x7F800000UL
#define EXP_SHIFT  23
#define EXP_BIAS   127
#define MANT_MASK  0x007FFFFFUL
#define MANT_LEAD  0x00800000UL  // implicit leading 1

static u16 fpClass(u32 x, u32 *out_sign, s16 *out_exp, u32 *out_mant) {
    *out_sign = x & SIGN_BIT;
    s16 e = (s16)((x >> EXP_SHIFT) & 0xFF);
    u32 m = x & MANT_MASK;
    if (e == 0) {
        // Zero or subnormal — treat as zero (flush).
        *out_exp = 0;
        *out_mant = 0;
        return 0;  // zero
    }
    if (e == 0xFF) {
        // Inf or NaN — return as-is, caller decides.
        *out_exp = 0xFF;
        *out_mant = m;
        return (m == 0) ? 2 : 3;  // 2=inf, 3=nan
    }
    // Normal — restore implicit leading 1.
    *out_exp = e - EXP_BIAS;
    *out_mant = m | MANT_LEAD;
    return 1;  // normal
}

static u32 fpPack(u32 sign, s16 exp, u32 mant) {
    if (mant == 0) return sign;  // zero
    // Normalize: shift mantissa until bit 23 is the leading 1.
    while ((mant & MANT_LEAD) == 0 && (mant & 0xFF800000UL) == 0) {
        mant <<= 1;
        exp--;
    }
    while (mant & 0xFF000000UL) {
        mant >>= 1;
        exp++;
    }
    s16 biased = exp + EXP_BIAS;
    if (biased <= 0) return sign;          // underflow -> 0
    if (biased >= 0xFF) return sign | EXP_MASK;  // overflow -> +/-inf
    return sign | ((u32)biased << EXP_SHIFT) | (mant & MANT_MASK);
}

u32 __addsf3(u32 a, u32 b) {
    u32 sa, sb, ma, mb;
    s16 ea, eb;
    u16 ca = fpClass(a, &sa, &ea, &ma);
    u16 cb = fpClass(b, &sb, &eb, &mb);
    if (ca == 0) return b;
    if (cb == 0) return a;

    // Align: shift smaller-exp mantissa right.
    if (ea > eb) {
        s16 d = ea - eb;
        if (d > 25) return a;  // b becomes negligible
        mb >>= d;
        eb = ea;
    } else if (eb > ea) {
        s16 d = eb - ea;
        if (d > 25) return b;
        ma >>= d;
        ea = eb;
    }

    // Combine, respecting signs.
    if (sa == sb) {
        u32 m = ma + mb;
        return fpPack(sa, ea, m);
    } else {
        // Different signs — subtract the smaller magnitude.
        if (ma >= mb) {
            return fpPack(sa, ea, ma - mb);
        } else {
            return fpPack(sb, eb, mb - ma);
        }
    }
}

u32 __subsf3(u32 a, u32 b) {
    return __addsf3(a, b ^ SIGN_BIT);
}

u32 __negsf2(u32 a) {
    return a ^ SIGN_BIT;
}

u32 __mulsf3(u32 a, u32 b) {
    u32 sa, sb, ma, mb;
    s16 ea, eb;
    u16 ca = fpClass(a, &sa, &ea, &ma);
    u16 cb = fpClass(b, &sb, &eb, &mb);
    u32 sign = sa ^ sb;
    if (ca == 0 || cb == 0) return sign;  // zero

    // 24-bit x 24-bit -> 48-bit product.  Take top 24 bits.
    // We approximate by multiplying the 16-bit halves and combining.
    u32 a_lo = ma & 0xFFFFUL;
    u32 a_hi = ma >> 16;        // 0..0xFF (8 bits significant)
    u32 b_lo = mb & 0xFFFFUL;
    u32 b_hi = mb >> 16;
    // p = a_lo*b_lo + (a_lo*b_hi + a_hi*b_lo)<<16 + a_hi*b_hi<<32
    u32 p_ll = a_lo * b_lo;     // 0..0xFFFE0001 (32 bits)
    u32 p_lh = a_lo * b_hi;     // 0..0xFE0001FF (24 bits ~)
    u32 p_hl = a_hi * b_lo;
    u32 p_hh = a_hi * b_hi;     // small
    // Top 32 bits of 48-bit product:
    //   (p_hh << 16) + p_lh + p_hl + (p_ll >> 16) + carries
    u32 mid = p_lh + p_hl;      // may overflow — track
    u32 carry_mid = (mid < p_lh) ? 0x10000UL : 0;
    u32 top = (p_hh << 16) + carry_mid + (mid >> 16) + (p_ll >> 16);
    // top is the upper 32 bits of the 48-bit product.  Bit 23 of the
    // INPUT mantissa is the leading 1, so the product's leading 1 is
    // at bit 47 (or 46 if both inputs have leading 1).  For two
    // normalised inputs, product is in [2^46, 2^48).  The top 32-bit
    // word (bits 16..47) holds the mantissa we want; we just need the
    // upper 24 bits as our output mantissa.
    s16 new_exp = ea + eb;
    if (top & 0x80000000UL) {
        // bit 47 set -> shift right to put bit 46 at 23
        top >>= 8;  // bring bit 47 to bit 39, then bit 39 to 31, then ...
        // Want the mantissa at bits 23..0 (24 bits with leading 1 at 23).
        // We have top 32 bits of 48-bit product; bit 47 = bit 31 of `top`.
        // After (top >> 8), bit 47 is at bit 23 — exactly where we want it.
        new_exp += 1;
    } else {
        // bit 46 set -> shift right by 7 to get bit 46 at 23
        top >>= 7;
    }
    return fpPack(sign, new_exp, top & 0xFFFFFFUL);
}

u32 __divsf3(u32 a, u32 b) {
    u32 sa, sb, ma, mb;
    s16 ea, eb;
    u16 ca = fpClass(a, &sa, &ea, &ma);
    u16 cb = fpClass(b, &sb, &eb, &mb);
    u32 sign = sa ^ sb;
    if (cb == 0) return sign | EXP_MASK;  // div-by-zero -> inf
    if (ca == 0) return sign;

    // Long division: quotient = ma/mb, in 24+1 bits.  We shift ma left
    // until larger than mb, accumulating quotient bits.  Use a 32-bit
    // numerator (ma starts at bit 23, gets up to bit 30 after shifts).
    u32 q = 0;
    u32 num = ma;
    for (s16 i = 0; i < 24; i++) {
        q <<= 1;
        if (num >= mb) {
            num -= mb;
            q |= 1;
        }
        num <<= 1;
    }
    // q has 24 bits.  Result exponent: ea - eb.  Then normalize.
    s16 new_exp = ea - eb;
    return fpPack(sign, new_exp, q);
}

s16 __cmpsf2(u32 a, u32 b) {
    // Returns -1 if a<b, 0 if a==b, 1 if a>b.
    // For NaN, libgcc returns 1 from cmpsf2 (no-NaN convention).  We
    // skip NaN handling.
    if (a == b) return 0;
    u32 sa = a & SIGN_BIT;
    u32 sb = b & SIGN_BIT;
    if (sa != sb) {
        // Different signs.  Negative is less, except both zeros.
        if ((a | b) << 1 == 0) return 0;  // +0 == -0
        return sa ? -1 : 1;
    }
    // Same sign.  Magnitude compare; if both negative, swap result.
    u32 am = a & 0x7FFFFFFFUL;
    u32 bm = b & 0x7FFFFFFFUL;
    s16 r = (am < bm) ? -1 : 1;
    return sa ? -r : r;
}

s16 __eqsf2(u32 a, u32 b) { return __cmpsf2(a, b) != 0; }
s16 __nesf2(u32 a, u32 b) { return __cmpsf2(a, b) != 0; }
s16 __ltsf2(u32 a, u32 b) { return __cmpsf2(a, b);     }
s16 __gtsf2(u32 a, u32 b) { return __cmpsf2(a, b);     }
s16 __lesf2(u32 a, u32 b) { return __cmpsf2(a, b);     }
s16 __gesf2(u32 a, u32 b) { return __cmpsf2(a, b);     }

// __unordsf2: 1 if either arg is NaN, else 0. Used for IEEE 754
// unordered comparisons (a < b is false if either is NaN, etc.).
s16 __unordsf2(u32 a, u32 b) {
    u32 ax = a & 0x7FFFFFFFUL;
    u32 bx = b & 0x7FFFFFFFUL;
    // NaN: exp=0xFF, mantissa != 0  →  ax > 0x7F800000
    return (ax > 0x7F800000UL) || (bx > 0x7F800000UL) ? 1 : 0;
}

u32 __floatsisf(s32 i) {
    if (i == 0) return 0;
    u32 sign = 0;
    u32 v;
    if (i < 0) {
        sign = SIGN_BIT;
        v = (u32)(-i);
    } else {
        v = (u32)i;
    }
    // Find leading 1 position (1..31).
    s16 lead = 31;
    while ((v & 0x80000000UL) == 0) { v <<= 1; lead--; }
    // After this loop, leading 1 is at bit 31.  We want it at bit 23
    // for IEEE mantissa (with implicit lead bit chopped at pack time).
    // Mantissa = top 24 bits of v.
    u32 mant = v >> 8;
    s16 exp = lead;
    return fpPack(sign, exp, mant);
}

u32 __floatunsisf(u32 v) {
    if (v == 0) return 0;
    s16 lead = 31;
    u32 t = v;
    while ((t & 0x80000000UL) == 0) { t <<= 1; lead--; }
    u32 mant = t >> 8;
    s16 exp = lead;
    return fpPack(0, exp, mant);
}

s32 __fixsfsi(u32 a) {
    u32 sa, ma;
    s16 ea;
    u16 ca = fpClass(a, &sa, &ea, &ma);
    if (ca == 0) return 0;
    if (ea < 0) return 0;          // |a| < 1
    if (ea >= 31) {                // overflow
        return sa ? -2147483647L - 1 : 2147483647L;
    }
    // Mantissa has leading 1 at bit 23.  Shift to put leading 1 at bit ea.
    u32 v;
    if (ea >= 23) v = ma << (ea - 23);
    else          v = ma >> (23 - ea);
    return sa ? -(s32)v : (s32)v;
}

u32 __fixunssfsi(u32 a) {
    u32 sa, ma;
    s16 ea;
    u16 ca = fpClass(a, &sa, &ea, &ma);
    if (ca == 0 || sa) return 0;   // negative -> 0
    if (ea < 0) return 0;
    if (ea >= 32) return 0xFFFFFFFFUL;
    if (ea >= 23) return ma << (ea - 23);
    return ma >> (23 - ea);
}
