Login

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download | RSS feed

#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