;; FP2DEC.ASM--translate a floating point number on top of the stack to a character string ;; using a g-type format and storing the string at es:di ;; ;; Library source text from "Assembly Language for the IBM PC Family" by ;; William B. Jones, (c) Copyright 1997, Scott/Jones Inc. ;; .MODEL SMALL fcompare MACRO comparand fcom &comparand& fstsw StatWd ; This version valid on 8087 mov ah, BYTE PTR StatWd+1 sahf ENDM .DATA HI_Status RECORD B:1, C3:1, TOP:3, C2:1, C1:1, C0:1 SIGN_BIT EQU (MASK C1) TYPE_MASK EQU ((MASK C0) + (MASK C1) + (MASK C2) + (MASK C3)) NORMAL_TYPE EQU (MASK C2) INFINITY_TYPE EQU ((MASK C2) + (MASK C0)) ZERO_TYPE EQU (MASK C3) StatWd DW ? INF_char EQU 236 ; Extended ASCII infinity character ERRMSG DB '*error*' errlen EQU $ - ERRMSG ; Pos tables--corresponding entries are 10 ** (11+2**N), 10**(-2**N) PosCMP DQ 1E267, 1E139, 1E75, 1E43, 1E27, 1E19, 1E15, 1E13 Ten2the12 DQ 1E12 PosMUL DQ 1E-256,1E-128,1E-64,1E-32,1E-16,1E-8, 1E-4, 1E-2, 1E-1 ; Neg tables--corresponding entries are 10 ** (12 - 2**N), 10**(2**N) NegCMP DQ 1E-244,1E-116,1E-53,1E-20,1E-4, 1E4, 1E8, 1E10 Ten2the11 DQ 1E11 NegMUL DQ 1E256, 1E128, 1E64, 1E32, 1E16, 1E8, 1E4, 1E2, 1E1 P10 DW ? Rounder DQ 5e5 BCDNum DT ? ASCIIVal DB 13 DUP (?) .CODE PUBLIC Fp2Dec Fp2Dec PROC push ax push bx push cx push dx push si push ds ; Set ds to access .DATA mov ax, @data mov ds, ax cld ; Set direction increasing fxam ; Examine top of stack fstsw StatWd mov ax, StatWd ; ah = HI_Status test ah, SIGN_BIT jz positive mov al, '-' stosb ; add negative sign xor ah, SIGN_BIT ; turn off sign bit indicator fabs ; and remove sign from top of stack positive: and ah, TYPE_MASK cmp ah, NORMAL_TYPE je normal cmp ah, INFINITY_TYPE je infinity cmp ah, ZERO_TYPE jne error mov al, '0' ; Floating point zero jmp store1 infinity: mov al, INF_char store1: stosb return: pop ds pop si pop dx pop cx pop bx pop ax ret error: mov si, OFFSET ERRMSG mov cx, errlen rep movsb jmp return normal: mov dx, 11 ; P10; looking for 12 significant digits ; We want to arrange it so that 10^11 <= top of stack < 10^12, that is ; the integral part of top of stack is 12 digits long scale1: fcompare PosCMP ; while ( tos >= 10^267 ) jb scale2 ; (use unsigned compare) fmul PosMUL ; tos *= 10^-256 add dx, 256 jmp scale1 scale2: ; At this point, tos < 10^267, and if it was ; so from the beginning, it is unchanged mov cx, 256 ; tos < 10^(11+cx) mov si, 0 scale2A: add si, 8 shr cx, 1 ; At this point, tos < 10^(11+2cx)) ; (loop invariant) jcxz scale3 fcompare <[PosCMP + si]>; if ( tos >= 10^(11+cx) ) jb scale2A fmul [PosMUL + si] ; tos *= 10^(-cx) ; At this point, either ; (1) tos < 10^(11+cx) ; or ; (2) originally 10^(11+cx)<= tos ( < ; 10^(11+2cx)) ; and as [PosMul + si] = 10^(-cx), now ; 10^11 <= tos < 10^(11+cx), i.e., ; the loop invariant is maintained add dx, cx jmp scale2A scale3: ; At this point, tos < 10^(11+2*.5) = 10^12 fcompare NegCMP ; while (tos < 10^(-244) ) jae scale4 fmul NegMUL ; tos *= 10^256 sub dx, 256 jmp scale3 scale4: ; At this point, tos >= 10^-244 = 10^(12-2^8) ; also, if we ever did any actual multi- ; plications, the result was tos < ; 10^(-244 + 256) = 10^12, so tos < 10^12 ; is maintained mov cx, 256 ; tos >= 10^(12-cx) mov si, 0 scale4A: add si, 8 shr cx, 1 ; At this point, 10^(12-2cx) <= tos ; < 10^12 (loop invariant) jcxz DoneScaling fcompare <[NegCMP + si]> ; if ( tos < 10^(12-cx) ) jae scale4A fmul [NegMUL + si] ; tos *= 10^cx ; At this point, either ; (1) 10^(12-cx) <= tos < 10^12 ; or ; (2) originally tos < 10^(12-cx) so after ; multiplication, tos < 10^(12-cx+cx) ; = 10^12 and as originally, tos >= ; 10^(12-2cx), now tos >= 10^(12-cx) ; and the loop invariant is maintained sub dx, cx jmp scale4A DoneScaling: mov P10, dx ; At this point, 10^(12-2*.5) = 10^11 ; <= top of stack < 10^12 and ; original value = top of stack * 10^P10 frndint ; Round to 12 significant digits fcompare Ten2the12 ; Have we rounded out the left end jb RndOK fcomp ST ; Pop the old number off the stack fld Ten2the11 ; and push the new one inc P10 RndOK: fbstp BCDNum ; convert top of stack to packed decimal integer mov dx, 12 ; Count number of significant digits mov cx, 6 mov si, OFFSET BCDNum ; Points to least significant dig pair CtSigDig: lodsb test al, al jnz Cted sub dx, 2 loop CtSigDig jmp error ; Can't happen (I hope) Cted: test al, 0fh ; Is first digit zero? jnz NonZ ; no dec dx ; yes NonZ: ; At this point, dx = SD = number of ; significant digits in the output ; ; Now decide format for printing mov bx, P10 test bx, bx jge exponPos exponNeg: mov ax, dx sub ax, bx cmp ax, 12 ; if (SD - P10 <= 12) jnle scientific ; output in non-scientific form mov al, '0' stosb mov al, '.' stosb mov cx, -1 sub cx, bx ; cx = -1 - P10 = number of zeros to add mov al, '0' ; after the decimal point and before the rep stosb ; significant digits mov bl, -1 ; Position of decimal point; i.e., no further ; decimal point added mov P10, 0 ; P10=0 indicates no exponent jmp DoDigits scientific: mov bl, dl ; dec bl ; Put decimal point at DP = SD-1 jmp DoDigits exponPos: cmp bx, 12 ; P10 >= 0; if P10 < 12 jnl scientific mov P10, 0 ; indicate no exponent inc bx cmp bx, dx ; if P10+1 < SD jnl NoDP neg bl add bl, dl ; DP at SD - P10 - 1 jmp DoDigits NoDP: mov dx, bx ; else no decimal point mov bl, -1 ; and SD = P10+1 DoDigits: mov si, OFFSET BCDNum + 5 mov cl, 4 DigLoop: ; Finally, output the digits mov al, [si] ; Get next digit pair dec si shl ax, cl shr al, cl and ax, 0f0fh add ax, '00' ; Convert to ASCII xchg ah, al stosb dec dl jz DoExponent cmp dl, bl jne NoDPHere1 mov al, '.' stosb NoDPHere1: xchg ah, al stosb dec dl jz DoExponent cmp dl, bl jne DigLoop mov al, '.' stosb jmp DigLoop DoExponent: mov bx, P10 test bx, bx jz JmpReturn mov al, 'e' stosb mov ax, bx EXTRN Bin2Dec : NEAR call Bin2Dec JmpReturn: jmp return Fp2Dec ENDP END