#ifndef included_f24toa
 
#define included_f24toa
 
 
 
; Lot's of #define statements
 
; This first batch is to make sure char_NEG, TOK_NEG, char_DEC, and TOK_DECIMAL
 
; are defined. This is because I accidentally used two different standards early
 
; on, and then I decided to share common routines. Let this be a lesson, because
 
; now I have to do convoluted trickery for compatibility -__-
 
 
 
#ifndef char_NEG
 
#ifdef TOK_NEG
 
#define char_NEG TOK_NEG
 
#else
 
#define char_NEG '-'
 
#endif
 
#endif
 
 
 
#ifndef char_DEC
 
#ifdef TOK_DECIMAL
 
#define char_DEC TOK_DECIMAL
 
#else
 
#define char_DEC '.'
 
#endif
 
#endif
 
 
 
#ifndef TOK_NEG
 
#ifdef char_NEG
 
#define TOK_NEG char_NEG
 
#else
 
#define TOK_NEG '-'
 
#endif
 
#endif
 
 
 
#ifndef TOK_DECIMAL
 
#ifdef char_DECIMAL
 
#define TOK_DECIMAL char_DEC
 
#else
 
#define TOK_DECIMAL '.'
 
#endif
 
#endif
 
 
 
 
 
; Now we'll set some defaults
 
 
 
 
 
; Define the absolute maximum number of digits in the result string.
 
; If FORMAT_LEN is bigger than this value, then it will be reduced.
 
#ifndef MAX_FORMAT_LEN
 
#define MAX_FORMAT_LEN 7
 
#endif
 
 
 
; Define this to use an external reference to get the number of digits used
 
;#define EXTERNAL_FORMAT_LEN <pointer>
 
;
 
; For example, TI-OS has a byte at fmtDigits:
 
;#define EXTERNAL_FORMAT_LEN fmtDigits
 
 
 
; This is the max number of digits in the output.
 
; If EXTERNAL_FORMAT_LEN is used and it is 0, then this will be used.
 
#ifndef FORMAT_LEN
 
#define FORMAT_LEN 5
 
#endif
 
 
 
; Define this to use an external reference for the max exponent before switching
 
; to enginieering mode.
 
;#define EXTERNAL_FORMAT_MAX_ENGINEERING  <pointer>
 
 
 
; Define the max exponent to use before switching to engineering format
 
; If none is defined, this uses FORMAT_LEN.
 
; If EXTERNAL_FORMAT_MAX_ENGINEERING is used and is 0 or larger than FORMAT_LEN,
 
; then this will be used.
 
;#define FORMAT_MAX_ENGINEERING
 
 
 
 
 
; Define this to use an external reference for the min exponent before switching
 
; to enginieering mode.
 
;#define EXTERNAL_FORMAT_MIN_ENGINEERING  <pointer>
 
 
 
; Define the largest negative exponent to use engineering format
 
; If EXTERNAL_FORMAT_MIN_ENGINEERING is used and is 0 or larger than FORMAT_LEN,
 
; then this will be used.
 
#ifndef FORMAT_MIN_ENGINEERING
 
#define FORMAT_MIN_ENGINEERING  -3  ; causes exponent of -3 to be enginierring
 
#endif
 
 
 
 
 
 
 
; Finally, includes
 
#include "../f24/f24mul.z80"
 
#include "../f24/f24pow10_LUT.z80"
 
#include "formatstr.z80"
 
 
 
 
 
f24toa:
 
;converts a 24-bit float to a string
 
;Inputs:
 
;   AHL is the float to convert
 
;   DE points to where to write the string
 
;Output:
 
;   HL pointing to the string
 
;Destroys:
 
;   A,DE,BC
 
;Notes:
 
;   Uses up to 12 bytes to store the string
 
 
 
  ld b,a  ; save the exponent
 
 
 
  ; check if the input is 0
 
  add a,a
 
  jr nz,+_
 
  ex de,hl
 
  jr nc,$+5
 
  ld (hl),char_NEG
 
  inc hl
 
  ld (hl),'0'
 
  inc hl
 
  ld (hl),0
 
  dec hl
 
  ret nc
 
  dec hl
 
  ret
 
_:
 
 
 
;check if the input is inf or NaN
 
  push de
 
  cp $FE
 
  jr nz,f24toa_finite
 
  ld a,h
 
  or l
 
  ld hl,s_NaN
 
  jr nz,+_
 
  ld hl,s_NEGinf
 
  bit 7,b
 
  jr nz,+_
 
  inc hl
 
_:
 
  ldi
 
  ldi
 
  ldi
 
  ldi
 
  ld a,(hl)
 
  ld (de),a
 
  pop hl
 
  ret
 
f24toa_finite:
 
 
 
;BHL is the float and it is not a special number
 
;save the exponent
 
 
 
;write a negative sign if needed
 
  ld a,b
 
  add a,a
 
  jr nc,+_
 
  ex de,hl
 
  ld (hl),char_NEG
 
  inc hl
 
  ex de,hl
 
_:
 
 
 
;save the string pointer
 
  push de
 
 
 
;save the significand
 
  push hl
 
 
 
;Get an estimate of the power of 10
 
;multiply A/2 by 77
 
 
 
  ld l,a
 
  ld h,0
 
  rrca
 
  ld e,a
 
  ld d,h
 
 
 
  add hl,hl ;4
 
  add hl,hl ;8
 
  add hl,de ;9
 
  add hl,hl ;18
 
  add hl,de ;19
 
  add hl,hl ;38
 
  add hl,hl ;76
 
  add hl,de ;77
 
 
 
;now HL is approximately (exp+63)*log10(2)*256
 
 
 
;first, save H, the power-of-10 guess
 
;also restore the significand
 
  ld e,h
 
  ex (sp),hl
 
 
 
;now multiply by the appropriate power-of-10 to get our input in the [1,10]-ish
 
;range. Unlike the higher-precision floats, it is actually smaller to store the
 
;whole table. This will also be slightly more accurate and also faster.
 
  push hl
 
  ld hl,f24pow10_LUT
 
  add hl,de
 
  sla e
 
  add hl,de
 
  ld e,(hl)
 
  inc hl
 
  ld d,(hl)
 
  inc hl
 
  ld c,(hl)
 
  ld a,b
 
  and $7F
 
  pop hl
 
  call f24mul
 
 
 
  cp 63
 
  jr nc,+_
 
  ;decrement the power of 10 and multiply by 10
 
  pop de
 
  dec d
 
  push de
 
  ld c,$42
 
  ld de,$4000
 
  call f24mul
 
_:
 
 
 
;now AHL is a float on [1,20]
 
;let's convert it to an 8.16 unsigned fixed-point number
 
  sub 63
 
  ld b,a
 
  ld a,1
 
  jr z,+_
 
  add hl,hl
 
  rla
 
  djnz $-2
 
_:
 
 
 
;for rounding porpoises, add 3 to A:HL
 
  ld bc,3
 
  add hl,bc
 
  adc a,b
 
 
 
  pop bc    ;power-of-10 is in B
 
  pop de    ;where to write the output
 
 
 
 
 
  cp 10
 
  jr nc,+_
 
  add a,'0'
 
  ld (de),a
 
  inc de
 
;get a second digit
 
  push bc
 
  call f24toa_sub
 
  jr f24toa_write_digits
 
_:
 
  inc b
 
;save the power-of-10 exponent
 
  push bc
 
 
 
;for rounding purposes, add another 30 to HL
 
  ld bc,30
 
  add hl,bc
 
  adc a,b
 
 
 
  ;the first digit is either a 1 or a 2
 
  ex de,hl
 
  ld (hl),'1'
 
  sub 20
 
  jr c,$+4
 
  inc (hl)
 
  .db $01
 
  add a,10
 
  inc hl
 
  ex de,hl
 
  add a,'0'
 
  ld (de),a
 
  inc de
 
f24toa_write_digits:
 
;get the next three digits
 
  call f24toa_sub
 
  call f24toa_sub
 
  call f24toa_sub
 
  xor a
 
  ld (de),a
 
 
 
; need to determine what to do with the power-of-10 exponent
 
  pop af
 
 
 
  pop hl  ; pointer to the string
 
  push hl
 
  sub 19
 
  ld e,a
 
  add a,a
 
  sbc a,a
 
  ld d,a
 
  call formatstr
 
  pop hl
 
  ret
 
;
 
f24toa_sub:
 
;now need to multiply 0.HL by 10 and add '0'
 
  xor a
 
  ld b,h
 
  ld c,l
 
  add hl,hl \ rla
 
  add hl,hl \ rla
 
  add hl,bc \ adc a,'0'/2
 
  add hl,hl \ rla
 
  ld (de),a
 
  inc de
 
  ret
 
#endif