zone/float.asm

1545 lines
24 KiB
NASM

; ============================================================
; Z80 Soft Float Library (4-byte) + Print + Parse (vasm syntax)
; ============================================================
; Float format in memory (big-endian, 4 bytes):
; byte0: EXP (8-bit biased exponent, 0 = zero)
; byte1: S|F22..F16 (bit7 = sign, bits6..0 = top 7 fraction bits)
; byte2: F15..F8
; byte3: F7..F0
;
; For EXP != 0:
; value = (-1)^S * (1.F) * 2^(EXP - FP_BIAS)
; FP_BIAS = 127
;
; Calling convention (in-place ops):
; HL -> A (4 bytes)
; DE -> B (4 bytes)
; fp_add: A = A + B (stored back at HL)
; fp_sub: A = A - B
; fp_mul: A = A * B
; fp_div: A = A / B
;
; Extra:
; fp_print: print float at (HL) using external printChar (A=ASCII)
; fp_parse: parse null-terminated string at (DE) into float at (HL)
;
; Limitations:
; - No NaN/Inf/denormals
; - Truncation (no rounding)
; - fp_print prints fixed decimals with a lightweight fraction path
; - fp_parse supports optional +/- and '.' up to MAX_FRAC digits, no exponent notation
; ============================================================
.equ FP_BIAS,127
.equ FRAC_DIGITS,6
.equ MAX_FRAC,6
.global printChar
; ============================================================
; CODE
; ============================================================
.text
; ------------------------------------------------------------
; External routine you provide:
; printChar: prints ASCII character in A
; ------------------------------------------------------------
; printChar is external, not defined here.
; ============================================================
; Public API: fp_add / fp_sub / fp_mul / fp_div
; ============================================================
; ------------------------------------------------------------
; fp_add: A = A + B
; ------------------------------------------------------------
fp_add:
push hl
push de
call fp_unpackA
pop de
call fp_unpackB
pop hl
; zero short-cuts
ld a,(A_exp)
or a
jr nz,.checkB
; A==0 => result=B
call fp_pack_from_B_into_A
ret
.checkB:
ld a,(B_exp)
or a
ret z
; if signs same -> magnitude add
ld a,(A_sign)
ld b,a
ld a,(B_sign)
xor b
jr z,fp_add_same_sign
; signs differ -> magnitude subtract
jp fp_add_diff_sign
; ------------------------------------------------------------
; fp_sub: A = A - B (flip B sign in memory, add, flip back)
; ------------------------------------------------------------
fp_sub:
; Flip sign bit of B byte1 (DE+1)
push hl
push de
inc de
ld a,(de)
xor 080h
ld (de),a
pop de
pop hl
call fp_add
; Flip sign bit back
push hl
push de
inc de
ld a,(de)
xor 080h
ld (de),a
pop de
pop hl
ret
; ------------------------------------------------------------
; fp_mul: A = A * B
; ------------------------------------------------------------
fp_mul:
push hl
push de
call fp_unpackA
pop de
call fp_unpackB
pop hl
; if A==0 or B==0 => 0
ld a,(A_exp)
or a
jr z,fp_store_zero_A
ld a,(B_exp)
or a
jr z,fp_store_zero_A
; sign = A_sign XOR B_sign
ld a,(A_sign)
ld b,a
ld a,(B_sign)
xor b
ld (A_sign),a
; exponent = A_exp + B_exp - BIAS
ld a,(A_exp)
ld b,a
ld a,(B_exp)
add a,b
sub FP_BIAS
ld (A_exp),a
; product = A_mant * B_mant (24x24 => 48)
call mul24x24_schoolbook
; normalize product into A mantissa
call norm_product_to_A
; pack back into (HL)
call fp_packA
ret
; ------------------------------------------------------------
; fp_div: A = A / B
; ------------------------------------------------------------
fp_div:
push hl
push de
call fp_unpackA
pop de
call fp_unpackB
pop hl
; A==0 => 0
ld a,(A_exp)
or a
jr z,fp_store_zero_A
; B==0 => return 0 (simple “error” behavior)
ld a,(B_exp)
or a
jr z,fp_store_zero_A
; sign = A_sign XOR B_sign
ld a,(A_sign)
ld b,a
ld a,(B_sign)
xor b
ld (A_sign),a
; exponent = A_exp - B_exp + BIAS
ld a,(A_exp)
ld b,a
ld a,(B_exp)
ld c,a
ld a,b
sub c
add a,FP_BIAS
ld (A_exp),a
; mantissa division
call div_mantissas_to_A
call normalize_A_mant
call fp_packA
ret
; ============================================================
; Add/Sub core (unpacked)
; ============================================================
fp_add_same_sign:
call align_exponents_A_B
call add24_A_plus_B
; if carry: shift right, exponent++
jr nc,.noCarry
call shr24_A_1
ld a,(A_exp)
inc a
ld (A_exp),a
.noCarry:
call normalize_A_mant
call fp_packA
ret
fp_add_diff_sign:
; compare |A| vs |B|, do larger - smaller, sign = sign(larger)
call compare_mag_A_B
jr c,.A_ge_B
; |B| > |A| => swap
call swap_A_B_unpacked
.A_ge_B:
call align_exponents_A_B
call sub24_A_minus_B
call is_A_mant_zero
jr z,fp_store_zero_A
call normalize_A_mant
call fp_packA
ret
; ============================================================
; Unpack / Pack helpers
; ============================================================
; Unpack A from (HL)
fp_unpackA:
ld a,(hl)
ld (A_exp),a
or a
jr z,.zeroA
inc hl
ld a,(hl)
ld b,a
; sign bit -> A_sign (0/1)
and 080h
jr z,.sa0
ld a,1
jr .sa1
.sa0:
xor a
.sa1:
ld (A_sign),a
; mantissa bytes with hidden 1 inserted
ld a,b
and 07Fh
or 080h
ld (A_m2),a
inc hl
ld a,(hl)
ld (A_m1),a
inc hl
ld a,(hl)
ld (A_m0),a
ret
.zeroA:
xor a
ld (A_sign),a
ld (A_m2),a
ld (A_m1),a
ld (A_m0),a
ret
; Unpack B from (DE)
fp_unpackB:
ld a,(de)
ld (B_exp),a
or a
jr z,.zeroB
inc de
ld a,(de)
ld b,a
and 080h
jr z,.sb0
ld a,1
jr .sb1
.sb0:
xor a
.sb1:
ld (B_sign),a
ld a,b
and 07Fh
or 080h
ld (B_m2),a
inc de
ld a,(de)
ld (B_m1),a
inc de
ld a,(de)
ld (B_m0),a
ret
.zeroB:
xor a
ld (B_sign),a
ld (B_m2),a
ld (B_m1),a
ld (B_m0),a
ret
; Pack unpacked A back into memory at (HL)
fp_packA:
ld a,(A_exp)
or a
jr nz,.packNZ
; store 0
ld (hl),0
inc hl
ld (hl),0
inc hl
ld (hl),0
inc hl
ld (hl),0
ret
.packNZ:
ld a,(A_exp)
ld (hl),a
inc hl
; remove hidden 1
ld a,(A_m2)
and 07Fh
ld b,a
; apply sign bit7
ld a,(A_sign)
or a
jr z,.sign0
ld a,b
or 080h
jr .storeB1
.sign0:
ld a,b
.storeB1:
ld (hl),a
inc hl
ld a,(A_m1)
ld (hl),a
inc hl
ld a,(A_m0)
ld (hl),a
ret
; Pack from unpacked B into memory A (HL points to A destination)
fp_pack_from_B_into_A:
ld a,(B_exp)
ld (hl),a
inc hl
ld a,(B_m2)
and 07Fh
ld b,a
ld a,(B_sign)
or a
jr z,.bs0
ld a,b
or 080h
jr .bs1
.bs0:
ld a,b
.bs1:
ld (hl),a
inc hl
ld a,(B_m1)
ld (hl),a
inc hl
ld a,(B_m0)
ld (hl),a
ret
fp_store_zero_A:
xor a
ld (A_exp),a
ld (A_sign),a
ld (A_m2),a
ld (A_m1),a
ld (A_m0),a
jp fp_packA
; ============================================================
; Exponent alignment / compare / swap
; ============================================================
; Ensure A_exp >= B_exp; shift smaller mantissa right by diff
align_exponents_A_B:
ld a,(A_exp)
ld b,a
ld a,(B_exp)
cp b
jr z,.done
jr c,.A_bigger_exp ; B_exp < A_exp
call swap_A_B_unpacked ; make A the larger exponent
.A_bigger_exp:
ld a,(A_exp)
ld b,a
ld a,(B_exp)
ld c,a
ld a,b
sub c ; A = diff
call shr24_B_by_A
ld a,(A_exp)
ld (B_exp),a
.done:
ret
; Carry set if |A| >= |B|, else carry clear
compare_mag_A_B:
ld a,(A_exp)
ld b,a
ld a,(B_exp)
cp b
jr z,.cmpMant
jr c,.A_ge
or a
ret
.A_ge:
scf
ret
.cmpMant:
ld a,(A_m2)
ld b,a
ld a,(B_m2)
cp b
jr z,.m1
jr c,.A_ge2
or a
ret
.A_ge2:
scf
ret
.m1:
ld a,(A_m1)
ld b,a
ld a,(B_m1)
cp b
jr z,.m0
jr c,.A_ge3
or a
ret
.A_ge3:
scf
ret
.m0:
ld a,(A_m0)
ld b,a
ld a,(B_m0)
cp b
jr c,.A_ge4
scf
ret
.A_ge4:
scf
ret
swap_A_B_unpacked:
ld a,(A_exp)
ld b,a
ld a,(B_exp)
ld (A_exp),a
ld a,b
ld (B_exp),a
ld a,(A_sign)
ld b,a
ld a,(B_sign)
ld (A_sign),a
ld a,b
ld (B_sign),a
ld a,(A_m2)
ld b,a
ld a,(B_m2)
ld (A_m2),a
ld a,b
ld (B_m2),a
ld a,(A_m1)
ld b,a
ld a,(B_m1)
ld (A_m1),a
ld a,b
ld (B_m1),a
ld a,(A_m0)
ld b,a
ld a,(B_m0)
ld (A_m0),a
ld a,b
ld (B_m0),a
ret
; ============================================================
; 24-bit mantissa ops
; ============================================================
add24_A_plus_B:
ld a,(A_m0)
add a,(B_m0)
ld (A_m0),a
ld a,(A_m1)
adc a,(B_m1)
ld (A_m1),a
ld a,(A_m2)
adc a,(B_m2)
ld (A_m2),a
ret ; carry meaningful
sub24_A_minus_B:
ld a,(A_m0)
sub (B_m0)
ld (A_m0),a
ld a,(A_m1)
sbc a,(B_m1)
ld (A_m1),a
ld a,(A_m2)
sbc a,(B_m2)
ld (A_m2),a
ret
is_A_mant_zero:
ld a,(A_m2)
or (A_m1)
or (A_m0)
ret
shr24_A_1:
ld a,(A_m2)
srl a
ld (A_m2),a
ld a,(A_m1)
rr a
ld (A_m1),a
ld a,(A_m0)
rr a
ld (A_m0),a
ret
; Shift B mantissa right by A bits (A=0..255)
shr24_B_by_A:
ld (SHCNT),a
ld a,(SHCNT)
cp 24
jr c,.ok
xor a
ld (B_m2),a
ld (B_m1),a
ld (B_m0),a
ret
.ok:
ld a,(SHCNT)
or a
ret z
.loop:
ld a,(B_m2)
srl a
ld (B_m2),a
ld a,(B_m1)
rr a
ld (B_m1),a
ld a,(B_m0)
rr a
ld (B_m0),a
ld a,(SHCNT)
dec a
ld (SHCNT),a
jr nz,.loop
ret
normalize_A_mant:
call is_A_mant_zero
jr nz,.nz
xor a
ld (A_exp),a
ret
.nz:
ld a,(A_m2)
bit 7,a
ret nz
.left_loop:
ld a,(A_m0)
add a,a
ld (A_m0),a
ld a,(A_m1)
adc a,a
ld (A_m1),a
ld a,(A_m2)
adc a,a
ld (A_m2),a
ld a,(A_exp)
dec a
ld (A_exp),a
ld a,(A_m2)
bit 7,a
jr z,.left_loop
ret
; ============================================================
; 8x8 -> 16 multiply (unsigned), shift-add
; in: A = multiplicand, C = multiplier
; out: HL = 16-bit product
; ============================================================
mul8u:
ld h,0
ld l,0
ld b,8
.m8:
srl c
jr nc,.noadd
ld e,a
ld d,0
add hl,de
.noadd:
add a,a
djnz .m8
ret
; ============================================================
; 24x24 schoolbook multiply into P0..P5 (P0 LSB)
; ============================================================
mul24x24_schoolbook:
xor a
ld (P0),a
ld (P1),a
ld (P2),a
ld (P3),a
ld (P4),a
ld (P5),a
; (0,0) offset 0
ld a,(A_m0)
ld c,(B_m0)
call mul8u
call add16_to_P_at0
; (0,1) offset 1
ld a,(A_m0)
ld c,(B_m1)
call mul8u
call add16_to_P_at1
; (0,2) offset 2
ld a,(A_m0)
ld c,(B_m2)
call mul8u
call add16_to_P_at2
; (1,0) offset 1
ld a,(A_m1)
ld c,(B_m0)
call mul8u
call add16_to_P_at1
; (1,1) offset 2
ld a,(A_m1)
ld c,(B_m1)
call mul8u
call add16_to_P_at2
; (1,2) offset 3
ld a,(A_m1)
ld c,(B_m2)
call mul8u
call add16_to_P_at3
; (2,0) offset 2
ld a,(A_m2)
ld c,(B_m0)
call mul8u
call add16_to_P_at2
; (2,1) offset 3
ld a,(A_m2)
ld c,(B_m1)
call mul8u
call add16_to_P_at3
; (2,2) offset 4
ld a,(A_m2)
ld c,(B_m2)
call mul8u
call add16_to_P_at4
ret
add16_to_P_at0:
ld a,(P0)
add a,l
ld (P0),a
ld a,(P1)
adc a,h
ld (P1),a
ret
add16_to_P_at1:
ld a,(P1)
add a,l
ld (P1),a
ld a,(P2)
adc a,h
ld (P2),a
ret
add16_to_P_at2:
ld a,(P2)
add a,l
ld (P2),a
ld a,(P3)
adc a,h
ld (P3),a
ret
add16_to_P_at3:
ld a,(P3)
add a,l
ld (P3),a
ld a,(P4)
adc a,h
ld (P4),a
ret
add16_to_P_at4:
ld a,(P4)
add a,l
ld (P4),a
ld a,(P5)
adc a,h
ld (P5),a
ret
; ============================================================
; Normalize product P into A mantissa
; P is 48-bit, P0 LSB .. P5 MSB
; ============================================================
norm_product_to_A:
ld a,(P5)
bit 7,a
jr z,.shift23
ld a,24
call shr48_P_by_A
ld a,(A_exp)
inc a
ld (A_exp),a
jr .take
.shift23:
ld a,23
call shr48_P_by_A
.take:
ld a,(P2)
ld (A_m2),a
ld a,(P1)
ld (A_m1),a
ld a,(P0)
ld (A_m0),a
ret
shr48_P_by_A:
ld (SHCNT),a
ld a,(SHCNT)
or a
ret z
.loop:
ld a,(P5)
srl a
ld (P5),a
ld a,(P4)
rr a
ld (P4),a
ld a,(P3)
rr a
ld (P3),a
ld a,(P2)
rr a
ld (P2),a
ld a,(P1)
rr a
ld (P1),a
ld a,(P0)
rr a
ld (P0),a
ld a,(SHCNT)
dec a
ld (SHCNT),a
jr nz,.loop
ret
; ============================================================
; Mantissa division (restoring-style)
; A_m = (A_m << 23) / B_m
; ============================================================
div_mantissas_to_A:
; P = A_m as 48-bit, then shift left 23
xor a
ld (P3),a
ld (P4),a
ld (P5),a
ld a,(A_m0)
ld (P0),a
ld a,(A_m1)
ld (P1),a
ld a,(A_m2)
ld (P2),a
ld a,23
call shl48_P_by_A
; clear quotient
xor a
ld (A_m2),a
ld (A_m1),a
ld (A_m0),a
ld b,24
.div_loop:
call shl24_A_1
call shl48_P_1
; subtract divisor from high 24 bits of P (P5..P3)
call sub24_Phigh_minus_B
jr c,.restore
; success => set quotient LSB = 1
ld a,(A_m0)
or 001h
ld (A_m0),a
jr .next
.restore:
call add24_Phigh_plus_B
.next:
djnz .div_loop
ret
shl48_P_by_A:
ld (SHCNT),a
ld a,(SHCNT)
or a
ret z
.loop:
call shl48_P_1
ld a,(SHCNT)
dec a
ld (SHCNT),a
jr nz,.loop
ret
shl48_P_1:
ld a,(P0)
add a,a
ld (P0),a
ld a,(P1)
adc a,a
ld (P1),a
ld a,(P2)
adc a,a
ld (P2),a
ld a,(P3)
adc a,a
ld (P3),a
ld a,(P4)
adc a,a
ld (P4),a
ld a,(P5)
adc a,a
ld (P5),a
ret
sub24_Phigh_minus_B:
ld a,(P3)
sub (B_m0)
ld (P3),a
ld a,(P4)
sbc a,(B_m1)
ld (P4),a
ld a,(P5)
sbc a,(B_m2)
ld (P5),a
ret ; carry set indicates borrow
add24_Phigh_plus_B:
ld a,(P3)
add a,(B_m0)
ld (P3),a
ld a,(P4)
adc a,(B_m1)
ld (P4),a
ld a,(P5)
adc a,(B_m2)
ld (P5),a
ret
; ============================================================
; fp_print: fixed format printing
; Prints: [-]I.FFFFFF (FRAC_DIGITS digits)
; Uses printChar (A=char)
; ============================================================
fp_print:
; zero?
ld a,(hl)
or a
jr nz,.nz
ld a,'0'
call printChar
ld a,'.'
call printChar
ld b,FRAC_DIGITS
.zf:
ld a,'0'
call printChar
djnz .zf
ret
.nz:
; EXP -> PR_E (unbiased)
ld a,(hl)
sub FP_BIAS
ld (PR_E),a
inc hl
; sign + top fraction
ld a,(hl)
ld b,a
and 080h
jr z,.ps0
ld a,1
jr .ps1
.ps0:
xor a
.ps1:
ld (PR_SIGN),a
; mantissa with hidden 1 inserted
ld a,b
and 07Fh
or 080h
ld (PR_M2),a
inc hl
ld a,(hl)
ld (PR_M1),a
inc hl
ld a,(hl)
ld (PR_M0),a
; print '-'
ld a,(PR_SIGN)
or a
jr z,.mag
ld a,'-'
call printChar
.mag:
; S = (E - 23)
ld a,(PR_E)
sub 23
; clear int and remainder helpers
xor a
ld (PR_INT0),a
ld (PR_INT1),a
ld (PR_INT2),a
ld (PR_INT3),a
ld (PR_R3),a
bit 7,a
jr z,.S_nonneg
; S negative: INT = [M2][M1][M0][00] (i.e., M << 8), then shift right by -S
neg
ld b,a ; B = shift count
xor a
ld (PR_INT0),a
ld a,(PR_M0)
ld (PR_INT1),a
ld a,(PR_M1)
ld (PR_INT2),a
ld a,(PR_M2)
ld (PR_INT3),a
call shr32_INT_to_INT_with_remainder
jr .print_int_and_frac
.S_nonneg:
; S non-negative: INT = M (24-bit) then shift left S (cap at 31)
cp 32
jr c,.doShl
ld a,31
.doShl:
ld b,a
ld a,(PR_M0)
ld (PR_INT0),a
ld a,(PR_M1)
ld (PR_INT1),a
ld a,(PR_M2)
ld (PR_INT2),a
xor a
ld (PR_INT3),a
call shl32_INT_by_B
.print_int_and_frac:
call print_u32_dec
ld a,'.'
call printChar
ld b,FRAC_DIGITS
.fr:
call mul_remainder_by_10
ld a,(PR_R3)
add a,'0'
call printChar
xor a
ld (PR_R3),a
djnz .fr
ret
; Shift-right PR_INT by B, collect shifted-out bits into PR_R3 (simplified)
shr32_INT_to_INT_with_remainder:
xor a
ld (PR_R3),a
ld a,b
or a
ret z
.loop:
ld a,(PR_INT3)
srl a
ld (PR_INT3),a
ld a,(PR_INT2)
rr a
ld (PR_INT2),a
ld a,(PR_INT1)
rr a
ld (PR_INT1),a
ld a,(PR_INT0)
rr a
ld (PR_INT0),a
; carry has shifted-out bit; accumulate into PR_R3
ld a,(PR_R3)
add a,a
adc a,0
ld (PR_R3),a
djnz .loop
ret
shl32_INT_by_B:
ld a,b
or a
ret z
.loop:
ld a,(PR_INT0)
add a,a
ld (PR_INT0),a
ld a,(PR_INT1)
adc a,a
ld (PR_INT1),a
ld a,(PR_INT2)
adc a,a
ld (PR_INT2),a
ld a,(PR_INT3)
adc a,a
ld (PR_INT3),a
djnz .loop
ret
mul_remainder_by_10:
ld a,(PR_R3)
ld b,a
add a,a ; *2
add a,a ; *4
add a,a ; *8
add a,b ; *9
add a,b ; *10
ld (PR_R3),a
ret
; Print PR_INT (u32) as decimal
print_u32_dec:
ld a,(PR_INT0)
or (PR_INT1)
or (PR_INT2)
or (PR_INT3)
jr nz,.nz
ld a,'0'
call printChar
ret
.nz:
xor a
ld (DIGLEN),a
.dloop:
call u32_div10_inplace ; remainder in A, quotient back in PR_INT
ld hl,DIGBUF
ld b,0
ld c,(DIGLEN)
add hl,bc
add a,'0'
ld (hl),a
ld a,(DIGLEN)
inc a
ld (DIGLEN),a
ld a,(PR_INT0)
or (PR_INT1)
or (PR_INT2)
or (PR_INT3)
jr nz,.dloop
; print in reverse
ld a,(DIGLEN)
ld b,a
.pr:
dec b
ld hl,DIGBUF
ld c,b
ld b,0
add hl,bc
ld a,(hl)
call printChar
ld a,c
or a
jr nz,.pr
ret
; Divide PR_INT (u32) by 10, return remainder in A (0..9)
u32_div10_inplace:
ld b,0 ; remainder
ld hl,PR_INT3
call .step
inc hl
call .step
inc hl
call .step
inc hl
call .step
ld a,b
ret
.step:
; DE = remainder*256 + byte
ld a,b
ld d,a
ld e,(hl)
ld c,0 ; quotient byte
.div:
ld a,d
or a
jr nz,.sub
ld a,e
cp 10
jr c,.done
.sub:
ld a,e
sub 10
ld e,a
ld a,d
sbc a,0
ld d,a
inc c
jr .div
.done:
ld (hl),c
ld b,e
ret
; ============================================================
; fp_parse: parse decimal string -> float
; DE -> "[-]ddd[.ddd]\0"
; HL -> output float
; ============================================================
fp_parse:
xor a
ld (P_SIGN),a
ld (P_FRACN),a
ld (P_S0),a
ld (P_S1),a
ld (P_S2),a
ld (P_S3),a
; optional sign
ld a,(de)
cp '-'
jr nz,.chkplus
ld a,1
ld (P_SIGN),a
inc de
jr .intpart
.chkplus:
ld a,(de)
cp '+'
jr nz,.intpart
inc de
.intpart:
ld a,(de)
call is_digit
jr nc,.maybe_dot
.il:
ld a,(de)
sub '0'
ld c,a
call u32_mul10_scaled
call u32_add8_scaled
inc de
ld a,(de)
call is_digit
jr c,.il
.maybe_dot:
ld a,(de)
cp '.'
jr nz,.finish_scaled
inc de
ld b,MAX_FRAC
.fl:
ld a,(de)
call is_digit
jr nc,.finish_scaled
ld a,(de)
sub '0'
ld c,a
call u32_mul10_scaled
call u32_add8_scaled
ld a,(P_FRACN)
inc a
ld (P_FRACN),a
inc de
djnz .fl
.finish_scaled:
; convert scaled u32 to float into (HL)
call fp_from_u32_scaled_to_A
; divide by 10^k if needed
ld a,(P_FRACN)
or a
jr z,.apply_sign
; DE = &pow10_table[k]
push hl
ld e,a
ld d,0
ld hl,pow10_table
add hl,de
add hl,de
add hl,de
add hl,de
ex de,hl
pop hl
call fp_div
.apply_sign:
ld a,(P_SIGN)
or a
ret z
inc hl
ld a,(hl)
xor 080h
ld (hl),a
ret
is_digit:
cp '0'
jr c,.no
cp '9'+1
jr nc,.no
scf
ret
.no:
or a
ret
; P_S = P_S*10 (uses PR_INT and PR_R0..3 as scratch)
u32_mul10_scaled:
; PR_INT = P
ld a,(P_S0)
ld (PR_INT0),a
ld a,(P_S1)
ld (PR_INT1),a
ld a,(P_S2)
ld (PR_INT2),a
ld a,(P_S3)
ld (PR_INT3),a
; PR_INT *=2
ld b,1
call shl32_INT_by_B
; PR_R = P
ld a,(P_S0)
ld (PR_R0),a
ld a,(P_S1)
ld (PR_R1),a
ld a,(P_S2)
ld (PR_R2),a
ld a,(P_S3)
ld (PR_R3),a
; PR_R *=8 (shift left 3)
ld b,3
call shl32_R_by_B
; P = PR_INT + PR_R
ld a,(PR_INT0)
add a,(PR_R0)
ld (P_S0),a
ld a,(PR_INT1)
adc a,(PR_R1)
ld (P_S1),a
ld a,(PR_INT2)
adc a,(PR_R2)
ld (P_S2),a
ld a,(PR_INT3)
adc a,(PR_R3)
ld (P_S3),a
ret
shl32_R_by_B:
ld a,b
or a
ret z
.loop:
ld a,(PR_R0)
add a,a
ld (PR_R0),a
ld a,(PR_R1)
adc a,a
ld (PR_R1),a
ld a,(PR_R2)
adc a,a
ld (PR_R2),a
ld a,(PR_R3)
adc a,a
ld (PR_R3),a
djnz .loop
ret
; P_S += C (0..9)
u32_add8_scaled:
ld a,(P_S0)
add a,c
ld (P_S0),a
ld a,(P_S1)
adc a,0
ld (P_S1),a
ld a,(P_S2)
adc a,0
ld (P_S2),a
ld a,(P_S3)
adc a,0
ld (P_S3),a
ret
; Convert P_S (u32) to float at (HL). Positive only; sign handled by caller.
fp_from_u32_scaled_to_A:
ld a,(P_S0)
or (P_S1)
or (P_S2)
or (P_S3)
jr nz,.nz
ld (hl),0
inc hl
ld (hl),0
inc hl
ld (hl),0
inc hl
ld (hl),0
ret
.nz:
; find MSB index in B (0..31)
ld b,31
ld a,(P_S3)
ld c,a
or a
jr nz,.scan
ld b,23
ld a,(P_S2)
ld c,a
or a
jr nz,.scan
ld b,15
ld a,(P_S1)
ld c,a
or a
jr nz,.scan
ld b,7
ld a,(P_S0)
ld c,a
.scan:
.find:
bit 7,c
jr nz,.found
add c,c
dec b
jr .find
.found:
; EXP = FP_BIAS + B
ld a,b
add a,FP_BIAS
ld (hl),a
inc hl
; shift value left by (23-B), take top 24 bits
ld a,23
sub b
ld b,a
; PR_INT = P_S
ld a,(P_S0)
ld (PR_INT0),a
ld a,(P_S1)
ld (PR_INT1),a
ld a,(P_S2)
ld (PR_INT2),a
ld a,(P_S3)
ld (PR_INT3),a
call shl32_INT_by_B
; store sign=0, fraction = top 23 bits of mantissa (hidden 1 removed)
ld a,(PR_INT3)
and 07Fh
ld (hl),a
inc hl
ld a,(PR_INT2)
ld (hl),a
inc hl
ld a,(PR_INT1)
ld (hl),a
ret
.data
; ============================================================
; pow10_table: 10^k constants (k=0..6) in THIS float encoding
; Verified:
; 1.0 = 127 00 00 00
; 10.0 = 130 20 00 00
; 100.0 = 133 48 00 00
; 1000.0 = 136 7A 00 00
; 10000.0 = 140 1C 40 00
; 100000.0 = 143 43 50 00
; 1000000.0= 146 74 24 00
; ============================================================
pow10_table:
.byte 127, 0x00, 0x00, 0x00 ; 10^0 = 1
.byte 130, 0x20, 0x00, 0x00 ; 10^1 = 10
.byte 133, 0x48, 0x00, 0x00 ; 10^2 = 100
.byte 136, 0x7A, 0x00, 0x00 ; 10^3 = 1000
.byte 140, 0x1C, 0x40, 0x00 ; 10^4 = 10000
.byte 143, 0x43, 0x50, 0x00 ; 10^5 = 100000
.byte 146, 0x74, 0x24, 0x00 ; 10^6 = 1000000
; ============================================================
; BSS / WORKSPACE
; ============================================================
.bss
; Unpacked A
.comm A_exp,1
.comm A_sign,1
.comm A_m2,1
.comm A_m1,1
.comm A_m0,1
; Unpacked B
.comm B_exp,1
.comm B_sign,1
.comm B_m2,1
.comm B_m1,1
.comm B_m0,1
; 48-bit workspace (P0 LSB .. P5 MSB)
.comm P0,1
.comm P1,1
.comm P2,1
.comm P3,1
.comm P4,1
.comm P5,1
.comm SHCNT,1
; Print temps
.comm PR_SI,1
.comm PR_E,1
.comm PR_M2,1
.comm PR_M1,1
.comm PR_M0,1
.comm PR_INT0,1
.comm PR_INT1,1
.comm PR_INT2,1
.comm PR_INT3,1
.comm PR_R0,1
.comm PR_R1,1
.comm PR_R2,1
.comm PR_R3,1
; Parse temps
.comm P_SIGN,1
.comm P_FRACN,1
.comm P_S0,1
.comm P_S1,1
.comm P_S2,1
.comm P_S3,1
; Digit buffer
.comm DIGBUF,1
.comm DIGLEN,1