#ifndef included_atof32
#define included_atof32
#include "f32mul.z80"
#include "ascii_to_BCD.z80"
#include "ascii_to_BCD.z80"
#include "f32_pow10_LUT.z80"
#ifndef TOK_ENG
#define TOK_ENG 'e'
#endif
; Negative sign
#ifndef TOK_NEG
#define TOK_NEG '-'
#endif
; Decimal point
#ifndef TOK_DECIMAL
#define TOK_DECIMAL '.'
#endif
#define ptr_sto scrap
atof32:
;Inputs:
; HL points to the string
; BC points to where to write the float
;Output:
; AHL is the f32 float
; scrap is the pointer to the end of the string
;Destroys:
; BC, DE
; 2 bytes at scrap
;
;
call pushpop
push bc ; save the pointer to the output
;Check for a negative sign, save for later, and advance the pointer if so.
; Advance ptr
ld a,(hl)
sub TOK_NEG
sub 1
push af
jr nc,$+3
inc hl
;Skip all leading zeroes
ld a,(hl)
cp '0'
jr z,$-4 ;jumps back to the `inc hl`
;Set exponent to 0
ld b,0
;Check if the next char is TOK_DECIMAL
sub TOK_DECIMAL
or a ;to reset the carry flag
jr nz,atof32_skip_1
.db $FE ;start of cp *
;Get rid of zeroes
dec b
inc hl
ld a,(hl)
cp '0'
jr z,$-5 ;jumps back to the `dec b`
scf
atof32_skip_1:
; at this point, we ought to pointing at our first non-zero digit, unless all
; digits were 0, in which case we might be pointing to TOK_ENG
rl c
ld a,(hl)
sub '0'
cp 10
jp nc,atof32_zero_check_eng
srl c
;Now we read in the next 8 digits. Ideally, we want 27 bits worth, but 8 digits
; gives us about 26.5 bits
call ascii_to_BCD
ld d,a
call ascii_to_BCD
ld e,a
push de
call ascii_to_BCD
ld d,a
call ascii_to_BCD
ld e,a
;Now `scrap` holds the 4-digit base-100 number (little-endian)
;b is the exponent
;if carry flag is set, just need to get rid of remaining digits
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
sbc a,a
ld c,a
atof32_loop_1:
ld a,(hl)
cp TOK_DECIMAL
jr nz,$+8
dec c
jp pe,atof32_skip_7 ; we've reached our first decimal
jr atof32_skip_2 ; else C was already FF, then a decimal was already encountered
sub '0'
cp 10
jr nc,atof32_skip_6
; carry flag is set
atof32_skip_7:
inc hl
ld a,b
adc a,c
; if carry flag is set, then we added 0
jr c,atof32_loop_1
jp z,atof32_inf2
ld b,a
jr atof32_loop_1
atof32_skip_6:
ld a,(hl)
;Now check for engineering `E` to modify the exponent
cp TOK_ENG
call z,atof32_str_eng_exp
atof32_skip_2:
ld (ptr_sto),hl ; save the pointer to the end of the string
pop hl
ex de,hl ; DEHL is the number
push bc ; B is the base-10 exponent
call atof32_scrap_times_256
ld b,c
call atof32_scrap_times_256
push bc ; BC is the upper 16 bis of the result
call atof32_scrap_times_256
ld b,c
call atof32_scrap_times_256
pop de
ld h,b
ld l,c
; DEHL is 32 bits of the significand
pop bc ; B is the base-10 exponent
;HLC holds the 3-digit significand, need to normalize it
; make sure it isn't zero
ld a,e
or d
or l
or h
jp z,atof32_zero
ld c,$7E
ld a,d
or a
jr atof32_loop_3_start
atof32_loop_3:
dec c
add hl,hl
rl e
adc a,a
atof32_loop_3_start:
jp p,atof32_loop_3
add a,a
ld d,a
pop af
rr c
rr d
; round
sla l
jr nc,+_
inc h
jr nz,+_
inc e
jr nz,+_
inc d
jr nz,+_
inc c
_:
;CDEH * 10^B
; write the float in CDEH to the output
ld a,h
pop hl
ld (hl),a
inc hl
ld (hl),e
inc hl
ld (hl),d
inc hl
ld (hl),c
dec hl
dec hl
dec hl
; now multiply the float at HL by 10^B
xor a
sub b
ld de,f32_pown10_LUT
jp p,$+7
ld a,b
ld de,f32_pow10_LUT
cp 38
jr c,$+4
ld a,-1
ld b,h
ld c,l
call atof32_mul_first
call atof32_mul_twice
atof32_mul_twice:
call atof32_mul_once
atof32_mul_once:
inc de
inc de
inc de
inc de
atof32_mul_first:
rra
call c,f32mul
ret
atof32_str_eng_exp:
; HL points to the string, B is the current exponent, returns adjusted exponent in B
push de ; save lower digits of input
ld e,0
inc hl
ld a,(hl)
sub TOK_NEG ;negative exponent?
ld c,a ;backup result
jr nz,atof32_skip_5
jr atof32_skip_3
atof32_loop_2:
ld d,a ; save the digit to add
; make sure E doesn't exceed 3 (else the exponent is over 40)
ld a,e
cp 4
jr nc,atof32_eng_overflow
add a,a
add a,a
add a,e
add a,a ; E*10
add a,d ; E*10+D
ld e,a
atof32_skip_3:
inc hl
atof32_skip_5:
ld a,(hl)
sub '0'
cp 10
jr c,atof32_loop_2
ld a,e
cp 60
jr nc,atof32_eng_overflow
ld a,c
or a
ld a,b
jr nz,atof32_skip_4
sub e
.db $FE ; start of `cp *` to skip the next byte
atof32_skip_4:
add a,e
ld b,a
pop de
ret
atof32_scrap_times_256:
call atof32_scrap_times_16
atof32_scrap_times_16:
call atof32_scrap_times_4
atof32_scrap_times_4:
call atof32_scrap_times_2
atof32_scrap_times_2:
;DEHL 8 BCD digits, need to multiply by 2
ld a,l
add a,a
daa
ld l,a
ld a,h
adc a,a
daa
ld h,a
ld a,e
adc a,a
daa
ld e,a
ld a,d
adc a,a
daa
ld d,a
rl c
ret
atof32_inf2:
ld c,-1
atof32_eng_overflow:
pop de ; pop off saved digits
pop af ; pop off the return address for the call
pop hl ; pop off the other saved digits
ld a,c
or a
jr z,atof32_zero
atof32_inf:
;return inf
pop af
pop hl
ld a,0
ld (hl),a
inc hl
ld (hl),a
inc hl
ld (hl),a
dec a
rra
rr (hl)
inc hl
ld (hl),a
ret
atof32_zero_check_eng:
cp TOK_ENG-'0'
jr nz,atof32_zero_write_hl
; otherwise, we need to read through the engineering exponent
inc hl
ld a,(hl)
cp TOK_NEG
jr nz,$+3
inc hl
ld a,(hl)
sub '0'
cp 10
jr c,$-6 ; jumps back to the `inc hl`
atof32_zero_write_hl:
ld (ptr_sto),hl ; save the pointer to the end of the string
atof32_zero:
pop af
pop hl
ld a,0
ld (hl),a
inc hl
ld (hl),a
inc hl
ld (hl),a
inc hl
rra
ld (hl),a
ret
#endif