        title   FTOA - floating point to ASCII
        page    55,132

; FTOA.ASM ---  Convert Binary Floating Point
;               Number on 80x87 Stack to ASCII                  
;               (also requires FALOG from FALOG.ASM)
;
; Copyright (C) 1989 Ziff Davis Communications
; PC Magazine * Ray Duncan
;
; Call with:    ST(0) = floating point number
;               DS:SI = buffer to receive string
;
; Returns:      DS:SI = address of converted string
;               AX    = length of string
;               Coprocessor stack "popped"
;
; Uses:         Nothing
;
; Make sure coprocessor has been properly initialized
; with a previous call to INIT87!

_DATA   segment word public 'DATA'

sign    dw      0                       ; receives FXAM status
oldcw   dw      0                       ; previous rounding mode
newcw   dw      0                       ; new rounding mode
exp     dw      0                       ; extracted power of ten
status  dw      0                       ; receives FCOM status
mant    dt      0                       ; mantissa as BCD value

fp1e17  dq      1.0e17                  ; constant for scaling
fp1e18  dq      1.0e18                  ; constant for scaling
int10   dw      10                      ; constant for scaling

pzstr   db      '+0.000000000000000000E+000'    ; displayed if
pz_len  equ     $-pzstr                         ; ST(0) = +0

mzstr   db      '-0.000000000000000000E+000'    ; displayed if
mz_len  equ     $-mzstr                         ; ST(0) = -0

pistr   db      '<+infinity>'           ; displayed if ST(0)
pi_len  equ     $-pistr                 ; is positive infinity

mistr   db      '<-infinity>'           ; displayed if ST(0)
mi_len  equ     $-mistr                 ; is negative infinity

nanstr  db      '<NaN>'                 ; displayed if ST(0)
nan_len equ     $-nanstr                ; contains Not-A-Number

unstr   db      '<unnormal>'            ; displayed for positive
un_len  equ     $-unstr                 ; or negative unnormals

destr   db      '<denormal>'            ; displayed for positive
de_len  equ     $-destr                 ; or negative denormals

empstr  db      '<empty>'               ; displayed if ST(0)
emp_len equ     $-empstr                ; is tagged "empty"

                                        ; number types from
                                        ; condition code bits
typetab dw      ftoa14                  ; 0000 +unnormal
        dw      ftoa15                  ; 0001 +NaN
        dw      ftoa14                  ; 0010 -unnormal
        dw      ftoa15                  ; 0011 -Nan
        dw      ftoa1                   ; 0100 +normal
        dw      ftoa16                  ; 0101 +infinity
        dw      ftoa1                   ; 0110 -normal
        dw      ftoa17                  ; 0111 -infinity
        dw      ftoa10                  ; 1000 +zero
        dw      ftoa12                  ; 1001 empty
        dw      ftoa11                  ; 1010 -zero
        dw      ftoa12                  ; 1011 empty
        dw      ftoa13                  ; 1100 +denormal
        dw      ftoa12                  ; 1101 empty
        dw      ftoa13                  ; 1110 -denormal
        dw      ftoa12                  ; 1111 empty

_DATA   ends

_TEXT   segment word public 'CODE'

        assume  cs:_TEXT,ds:_DATA

        extrn   falog:near              ; we need FALOG routine 

        public  ftoa
ftoa    proc    near                    ; floating point to ASCII

        push    bx                      ; save registers
        push    cx
        push    dx
        push    di
        push    es

        push    ds                      ; let ES point to _DATA
        pop     es

        fxam                            ; test type of number
        fstsw   sign                    ; unload FXAM status
        fwait
        mov     bx,sign                 ; retrieve status and
        and     bx,4700h                ; shift C3, C2, C1, C0
        rol     bx,1                    ; status bits to form
        rol     bx,1                    ; value 0-15, then
        shl     bh,1                    ; *2 for jump index
        shl     bh,1
        shl     bh,1
        rol     bx,1
        rol     bx,1
        rol     bx,1
        shl     bx,1
        jmp     [typetab+bx]            ; branch by number type

ftoa1:  fabs                            ; force number positive
        fxtract                         ; extract exponent
        fxch    st(1)                   ; put exponent on top
        fldlg2                          ; form power of 10
        fmulp   st(1),st(0)
        fstcw   oldcw                   ; save current rounding mode
        fwait
        mov     ax,oldcw                ; set bit field for rounding
        or      ax,0c00h                ; mode to "chop"
        mov     newcw,ax
        fldcw   newcw                   ; force new rounding mode
        fld     st(0)                   ; duplicate power of ten
        frndint                         ; find integer part of 
        fist    exp                     ; exponent and save it
        fldcw   oldcw                   ; restore old rounding mode
        fsubp   st(1),st(0)             ; find fractional part of
        call    falog                   ; power of ten
        fmulp   st(1),st(0)             ; then times mantissa
        fmul    fp1e18                  ; scale mantissa for BCD
        frndint                         ; zap any remaining fraction
        
ftoa2:  fcom    fp1e17                  ; is mantissa < 1.0e17?
        fstsw   status
        fwait
        mov     ax,status
        sahf
        jae     ftoa3                   ; no, proceed
        fimul   int10                   ; yes, mantissa * 10
        dec     exp                     ; and decrement exponent
        jmp     ftoa2

ftoa3:  fcom    fp1e18                  ; is mantissa < 1.0e18?
        fstsw   status
        fwait
        mov     ax,status
        sahf
        jb      ftoa4                   ; yes, proceed
        fidiv   int10                   ; yes, mantissa / 10
        inc     exp                     ; and increment exponent
        jmp     ftoa3

ftoa4:  fbstp   mant                    ; unload mantissa in BCD
        fwait
        mov     di,si                   ; address for ASCII string
        mov     al,'+'                  ; assume positive
        test    sign,200h               ; check FXAM flags
        jz      ftoa5                   ; jump, value was positive
        mov     al,'-'

ftoa5:  stosb                           ; store + or - sign
        mov     al,'0'                  ; store leading zero
        stosb
        mov     al,'.'                  ; store decimal point
        stosb
        mov     bx,8                    ; point to last BCD digits

ftoa6:  mov     al,byte ptr [bx+mant]   ; convert BCD byte to
        shr     al,1                    ; two ASCII digits
        shr     al,1
        shr     al,1
        shr     al,1
        call    digit                   ; convert high nibble
        mov     al,byte ptr [bx+mant]
        call    digit                   ; convert low nibble
        dec     bx                      ; back up through BCD value
        jns     ftoa6                   ; until 18 digits converted

        mov     al,'E'                  ; store 'E' for exponent
        stosb
        mov     bx,exp                  ; test sign of exponent
        mov     al,'+'
        or      bx,bx
        jns     ftoa7                   ; jump, exponent positive
        mov     al,'-'
        neg     bx                      ; abs. value of exponent

ftoa7:  stosb                           ; store sign of exponent

        mov     ax,bx                   ; convert exponent to
        cwd                             ; three ASCII digits
        mov     cx,100
        div     cx
        call    digit                   ; exponent hundreds digit
        mov     ax,dx                      
        cwd
        mov     cx,10
        div     cx
        call    digit                   ; exponent tens digit
        mov     ax,dx
        call    digit                   ; exponent ones digit

ftoa8:  mov     ax,di                   ; return AX = string length
        sub     ax,si                   ; and DS:SI = string address
        
        pop     es                      ; restore registers
        pop     di
        pop     dx
        pop     cx
        pop     bx
        ret

ftoa10: mov     di,offset pzstr         ; +zero value
        mov     cx,pz_len
        jmp     ftoa20

ftoa11: mov     di,offset mzstr         ; -zero value
        mov     cx,mz_len
        jmp     ftoa20

ftoa12: mov     di,offset empstr        ; empty value
        mov     cx,emp_len
        jmp     ftoa20

ftoa13: mov     di,offset destr         ; denormal value
        mov     cx,de_len
        jmp     ftoa20

ftoa14: mov     di,offset unstr         ; unnormal value
        mov     cx,un_len
        jmp     ftoa20

ftoa15: mov     di,offset nanstr        ; NaN value
        mov     cx,nan_len
        jmp     ftoa20

ftoa16: mov     di,offset pistr         ; +infinity value
        mov     cx,pi_len
        jmp     ftoa20

ftoa17: mov     di,offset mistr         ; -infinity value
        mov     cx,mi_len

ftoa20: xchg    si,di                   ; SI = canned string
        push    di                      ; DI = caller's buffer
        rep movsb                       ; copy canned string
        pop     si                      ; restore SI
        fstp    st(0)                   ; discard original value
        jmp     ftoa8                   ; go to common exit point

ftoa    endp

;
; DIGIT:        Convert low nibble of AL to ASCII digit
;               and store at address given by DS:DI
; Call with:    AL    = value to be converted in bits 0-3
;               ES:DI = address to store ASCII character
; Returns:      AL    = unchanged
;               ES:DI = address+1
;
digit   proc    near                    ; nibble to ASCII digit

        push    ax                      ; save register
        and     al,0fh                  ; isolate nibble
        add     al,'0'                  ; convert to ASCII char
        stosb                           ; store the character
        pop     ax                      ; restore register
        ret

digit   endp

_TEXT   ends

        end

