Metropoli BBS
VIEWER: ftoa.asm MODE: TEXT (LATIN1)
;included by math_bc.asm and math_wc.asm

;data used is in math_com.asm

.code

fint macro @@QWORDPTR               ;integrerize float number
    fld     @@QWORDPTR              ;load value to get integer part
    frndint                         ;round to integrer
    fstsw   ax                      ;store flags
    fistp   dptr [result]         ;store (rounded) integrer
    fwait

    .if ah & 2  ;mask C1
      mov eax,dptr [result]
      dec eax
    .else
      mov eax,dptr [result]
    .endif
endm

frnd macro @@QWORDPTR, @@DIGITS     ;round "dizimas periodicas"
    push eax
    push ebx
    push ecx
    push edx         ;save regs
    mov     edx, @@DIGITS
    inc     edx                     
    callp    _pow,10,edx            ;pow(10, digits+1)
    mov     ecx, eax                ;save power of 10 in ecx
    fld     @@QWORDPTR              ;load value
    fint    @@QWORDPTR              ;integrerize
    mov     dptr [tmpd], eax       ;move integrer to temp buffer
    fisub   dptr [tmpd]            ;subtract whole part
    mov     dptr [tmpd], ecx       ;move power of 10 to temp buffer
    fimul   dptr [tmpd]            ;mul fractional part by power
    fstp    qptr [result]         ;store powered fractional part
    fint    result                ;integrerize powered fractional part
    xor     edx, edx                ;clear edx
    mov     ebx, 10                 ;divide by 10
    div     ebx
    xor     ebx,ebx
    cmp     edx, 9                  ;set ebx to 1 if remainder=9
    sete    bl
    mov     dptr [tmpd], ebx       ;move 1 or 0 to temp buffer
    fild    dptr [tmpd]            ;load it
    mov     dptr [tmpd], ecx       ;move power of 10 to temp buffer
    fidiv   dptr [tmpd]            ;divide 1 or 0 by power of 10
    fstp    qptr [result]         ;store fractional number to add
    fld     @@QWORDPTR              ;reload original value
    fadd    result                ;add 0.0000xxxx1 or not
    fstp    @@QWORDPTR              ;store rounded value
    pop edx
    pop ecx
    pop ebx
    pop eax
endm

chk_nan_inf proc private
    ;check for NAN and INF in st
    fxam
    fstsw ax
    ffree st
    fwait
    and ah,1000111b    ;mask C? flags

    .if ah == 001b
      callp strcpy,edi,"+NAN"
      mov eax,1
      ret
    .endif
    .if ah == 011b
      callp strcpy,edi,"-NAN"
      mov eax,1
      ret
    .endif
    .if ah == 101b
      callp strcpy,edi,"+INF"
      mov eax,1
      ret
    .endif
    .if ah == 111b
      callp strcpy,edi,"-INF"
      mov eax,1
      ret
    .endif
    xor eax,eax
    ret
chk_nan_inf endp

.data
MAX_FTOA real8 +1.0e8
MIN_FTOA real8 -1.0e8

.code

ftoa proc, a:REAL8, string:dword, decimals:dword

    pushad

    fld a
    mov edi,string
    call chk_nan_inf
    .if eax
      popad
      mov eax,string
      ret
    .endif

    fld a
    fcomp MAX_FTOA
    fstsw ax
    fwait
    sahf
    jbe @f
    jmp _etoa
@@:
    fld a
    fcomp MIN_FTOA
    fstsw ax
    fwait
    sahf
    jae @f
    jmp _etoa
@@:

    fldcw rnd_0   ;setup RC

    mov     [sign], 0               ;clear sign marker

    fint    a                       ;`integrerize' number
    mov     edi, [string]           ;load EDI with string    

    cmp     eax, 0                  ;below zero?
    jge     @@FTOAabovezero         ;NO=>goto FTOAabovezero
    mov     [sign], 1               ;negative number
    neg     eax                     ;clear sign bit
    xor bptr[a+7],80h               ;remove sign
    mov     [edi], bptr '-'         ;put minus on start of edi

@@FTOAabovezero:
    mov     edx, decimals
    frnd    a , edx                 ;round .499999999 to .5

    fint    a                       ;integrerize rounded positive number
    call    nibblecount             ;get # of digits
    inc     ecx                     ;increase (nibblecount returns #-1)

    add     edi, ecx                ;add digit count to EDI
    movzx   ebx, bptr sign
    add     edi, ebx                ;if negative one more char is needed
    push    ecx                     ;save for further use
    push    edi                     ;save for further use

    mov     bptr [edi], '.'         ;put the point
    dec     edi                     ;decrease

    mov     ebx, 10                 ;divider
@@FTOAloop1:
    xor     edx, edx                ;divide eax by 10 until
    div     ebx                     ;ecx decreases from max    
    add     edx, '0'                ;number of digits to 0.
    mov     [edi], bptr dl          ;save number MOD 10+30h
    dec     edi                     ;so it's ASCII code
    dec     ecx                     ;decrease count
    jnz     @@FTOAloop1

    pop     edi                     ;restore string pos at point
    pop     ecx                     ;restore digit count
    inc     edi                     ;goto after point

    ;*********************** [þFRACTIONALþ] ***********************

    mov     ecx, decimals           ;make ECX a count of decimals desired
    .if !ecx
      mov al,'0'
      stosb
      jmp nodec
    .endif
    fint    a                       ;`integrerize' float number
    mov     [tmpd], eax              ;move int number to temp location
    fld     a                       ;load float number
    fisub   dptr [tmpd]              ;subtract whole part so only frac is left
    
    mov     tmpd, 10
    mov     ebx, 10          

@@FTOAloop2:
    fimul   dptr [tmpd]              ;multiplicate by 10
    fst     qptr [result]           ;save mul'ed result
    fwait
    fint    result                  ;`integrerize' it

    xor     edx, edx                ;clear edx
    div     ebx                     ;divide by 10 and get remainder

    add     dl, '0'                 ;add 30h to value
    mov     [edi], bptr dl          ;save on string

    inc     edi                     ;increase string index
    dec     ecx                     ;decrease 'precision' counter
    jnz     @@FTOAloop2             

nodec:
    ffree   st(0)                   ;kill st(0)

    mov     [edi], bptr 0           ;put '\0' at end of string
    xor     eax, eax

    fldcw cw_def   ;setup RC to default
    fwait
    popad
    mov eax,string
    ret
ftoa endp

.data
  _ws db ?  ;whole part sign

.code
etoa proc, a:REAL8, string:dword, decimals:dword
  ;print decimal part then 'e' then exponential part

  pushad

  fld a
  mov edi,string
  call chk_nan_inf
  .if eax
    popad
    mov eax,string
    ret
  .endif

_etoa::

  fld a
  ftst         ;cmp with 0.0  ;must account for decimals
  fstsw ax
  ffree st
  fwait
  sahf
  .if zero?
    callp ftoa,a,edi,decimals
    callp strlen,edi
    add edi,eax
    callp strcpy,edi,"e+00"
    popad
    mov eax,string
    ret
  .endif
  mov _ws,0
  .if carry?  ;negative #
    mov _ws,1
    xor bptr[a+7],80h  ;remove sign bit
  .endif

  fldcw rnd_dw    ;make sure frndint rounds towards down
  
  callp log10,a   ;get exponent part
  LOADF
  fstp tmpr
  fwait
  frnd tmpr, 1    ;round .9999999 to 1
  fld tmpr
  frndint         ;chop decimal part
  fst tmpr        ;save for later
  fistp tmpw      ;and here trunc.
  fwait

  callp pow,ten,tmpr   ;find 10 ** tmp
  LOADF
  fld a
  fxch
  fdivp st(1),st     ;find 'a' / (10 ** tmp) which will leave the dec part
  fstp tmpr          ;save dec part
  fwait
  
  .if _ws  ;put in neg #
    mov al,'-'
    stosb
  .endif
  callp ftoa,tmpr,edi,decimals
  callp strlen,edi
  add edi,eax

  fldcw cw_def       ;reset cw

  mov al,'e'
  stosb
  mov bx,tmpw
  .if bx & 8000h
    neg bx
    mov al,'-'
    stosb
  .else
    mov al,'+'
    stosb
  .endif
  .if bx<10     ;make sure there are at least 2 digits (as BC)
    mov al,'0'
    stosb
  .endif
  callp num2strs,bx,edi,10

  popad
  mov eax,string

  ret
etoa endp

nibblecount proc private
    push eax
    push ebx
    push edx

    xor     ecx, ecx
    mov     ebx, 10

@@NCloop:
    xor     edx, edx
    div     ebx
    cmp     eax, 0
    je      @@NCend
    inc     ecx
    jmp     @@NCloop

@@NCend:
    pop edx
    pop ebx
    pop eax
    ret
nibblecount endp

;This is not an ANSI C pow (this is an integer POW)

_pow PROC private uses ebx ecx edx,a1:dword,a2:dword       ;power(number, power)
  mov ecx, a2
  cmp ecx, 0
  .if carry? || zero?
    mov eax,1
    ret
  .endif

  mov ebx, a1
  mov eax, ebx
  xor edx, edx
  jmp start
@@:
  mul ebx
start:
  dec ecx
  jnz @b
  ret
_pow ENDP

[ RETURN TO DIRECTORY ]