#ifndef included_f32toa
#define included_f32toa
#include "pushpop.z80"
#include "f32_pow10_LUT.z80"
#include "f32mul.z80"
#include "mov4.z80"
#include "common_str.z80"
;#define EXTERNAL_FORMAT_LEN ;Uses an external reference to get the format length
;#define EXTERNAL_FORMAT_LEN fmtDigits ;Use for TI-OS
; Set a hard upper limit to the number of digits used
; This routine uses at most MAX_FORMAT_LEN+8 bytes of space
#ifndef MAX_FORMAT_LEN
#define MAX_FORMAT_LEN 8
#endif
; Set the default number of digits used. If EXTERNAL_FORMAT_LEN is defined and
; that value is 0 or greater than MAX_FORMAT_LEN, then FORMAT_LEN is used.
#ifndef FORMAT_LEN
#define FORMAT_LEN 6
#endif
; set the char to use for `e` notation
#ifndef TOK_ENG
#define TOK_ENG 'e'
#endif
; set the char to use for negatives
#ifndef TOK_NEG
#define TOK_NEG '-'
#endif
; set the char to use for decimal points
#ifndef TOK_DECIMAL
#define TOK_DECIMAL '.'
#endif
#define f32toa_x scrap
f32toa:
;Inputs:
; HL points to the input float
; BC points to where the string gets written.
call pushpop
ld e,(hl)
inc hl
ld d,(hl)
inc hl
ld (f32toa_x),de
ld a,(hl)
ld e,a
add a,a
inc hl
ld a,(hl)
ld d,a
adc a,a
ld h,b
ld l,c
inc a
jp z,f32toa_return_infnan
res 7,d
ld (f32toa_x+2),de
jr nc,+_
ld (hl),TOK_NEG ;negative sign
inc hl
_:
;DEBC is the float, A is a copy of the exponent, HL points to the next byte to write
dec a
jp z,f32toa_return_0
push hl
ld (hl),'0' ; pad one more '0' to make room for rounding (i.e. 9.999999=>10.000000)
inc hl
push hl
; approximate the base-10 exponent as floor((A-127)*log10(2))
; which is approximately floor((A-127)*77/256)
ld h,0
ld l,a
ld b,h
ld c,a
add hl,hl ;2
add hl,hl ;4
add hl,hl ;8
add hl,bc ;9
add hl,hl ;18
add hl,bc ;19
add hl,hl ;38
add hl,hl ;76
add hl,bc ;77
; now subtract 127*77 from HL and keep the upper 8 bits of the result
ld a,205
add a,l
ld a,217
adc a,h
; A is the approximate base-10 exponent
;f32toa_x needs to be multipled by 10^-A
push af ; save the exponent
call nz,f32toa_adjust
;the float is now on [1, 20)
;Let's no work on the float as a 0.24 fixed-point value
;we'll first need to extract the integer component
ld hl,(f32toa_x)
ld de,(f32toa_x+2)
ld a,e
rlca
scf
rra
ld e,a
ld a,d
adc a,a
sub 126
; A is how many bits to shift out of HLE
ld b,a
xor a
f32toa_first_digit_loop:
add hl,hl
rl e
rla
djnz f32toa_first_digit_loop
; A is the first digit
pop bc ; B is the base-10 exponent
ex (sp),hl
ld d,0
cp 10
jr c,f32toa_first_digit_fixed
inc d
dec hl
ld (hl),'1'
inc hl
sub 10
f32toa_first_digit_fixed:
add a,'0'
ld (hl),a
ld a,b
ex (sp),hl
pop bc
;A is the base-10 exponent
;BC is where to write the digits
;EHL is the 0.24 fixed-point number
;D is 1 if we have 2 digits already, else D is 0
push af
call f32toa_digits
;BC points to the last digit. We'll want to round!
ld h,b
ld l,c
ld a,(hl)
ld (hl),0
add a,5
jr f32toa_round_start
f32toa_round_loop:
dec hl
inc (hl)
ld a,(hl)
f32toa_round_start:
cp '9'+1
jr nc,f32toa_round_loop
; the string is rounded
inc hl
ld (hl),0
; pop the exponent off the stack and sign-extend
pop af
ld e,a
add a,a
sbc a,a
ld d,a
pop hl
; check if rounding caused overflow; increment exponent if so
ld a,(hl)
cp '0'
jp z,formatstr
inc de
jp formatstr
f32toa_digits:
; How many digits do we need?
#ifdef EXTERNAL_FORMAT_LEN
; we define the number of digits externally
ld a,(EXTERNAL_FORMAT_LEN)
; if A is 0, use FORMAT_LEN
or a
jr nz,$+4
ld a,FORMAT_LEN
; if A > MAX_FORMAT_LEN, set A to MAX_FORMAT_LEN
cp MAX_FORMAT_LEN
jr c,$+4
ld a,MAX_FORMAT_LEN
; the first digit is written.
; if we only wanted 1 digit, then A is 0 and we should stop
or a
ret z
#else
; the number of digits is not declared externally
; the first digit is written.
; if we only wanted 1 digit, then A is 0 and we should stop
#if FORMAT_LEN < 2
ret
#else
ld a,FORMAT_LEN
#endif
#endif
; we want D more digits (an extra one for rounding)
f32toa_digits_loop:
push af
call f32toa_next_digit
pop af
f32toa_digits_start:
dec a
jr nz,f32toa_digits_loop
ret
f32toa_next_digit:
; multiply 0.EBC by 10
push bc
ld b,h
ld c,l
ld a,e
ld d,6 ;overflow digit. We shift d 3 times, that 6 turns into a 0x30 == '0'
add hl,hl
adc a,a
rl d
add hl,hl
adc a,a
rl d
add hl,bc
adc a,e
jr nc,$+3
inc d
add hl,hl
adc a,a
ld e,a
ld a,d
adc a,a
pop bc
inc bc
ld (bc),a
ret
f32toa_adjust:
ld hl,f32_pown10_LUT-4
jr c,f32toa_pow10LUT_mul
neg
ld hl,f32_pow10_LUT-4
f32toa_pow10LUT_mul:
;HL points to the first entry of the LUT
;(f32toa_x) is the accumulator
;bottom 6 bits of A control which terms to multiply by
ld de,f32toa_x
ld b,d
ld c,e
; process f32toa_pow10LUT_mul_sub 6 times
call f32toa_pow10LUT_mul_sub3
f32toa_pow10LUT_mul_sub3:
call f32toa_pow10LUT_mul_sub
call f32toa_pow10LUT_mul_sub
f32toa_pow10LUT_mul_sub:
inc hl
inc hl
inc hl
inc hl
rra
jp c,f32mul
ret
f32toa_return_0:
ld (hl),'0'
inc hl
ld (hl),0
ret
f32toa_return_infnan:
rl b ; save the sign
ld a,e
add a,a
ld de,(f32toa_x)
or d
or e
ex de,hl
ld hl,str_NaN
jr nz,f32toa_return
ld hl,str_inf
rr b
jr nc,f32toa_return
ld a,'-'
ld (de),a
inc de
f32toa_return:
jp mov4
#include "formatstr.z80"
#endif