fpufltoa.asm

来自「工欲善其事」· 汇编 代码 · 共 399 行

ASM
399
字号
; #########################################################################
;
;                             FpuFLtoA
;
;##########################################################################

  ; -----------------------------------------------------------------------
  ; This procedure was written by Raymond Filiatreault, December 2002
  ;
  ; This FpuFLtoA function converts an 80-bit REAL number (Src) to its
  ; decimal representation as a zero terminated alphanumeric string which
  ; is returned at the specified memory destination unless an invalid
  ; operation is reported by the FPU or the definition of the parameters
  ; (with uID) is invalid. The format of the string can be specified
  ; as regular (default) or scientific notation. The number of decimal
  ; places returned must also be specified but the total number of digits
  ; must not exceed 18.
  ;
  ; The source can be an 80-bit REAL number from the FPU itself or from
  ; memory, or an immediate DWORD value or one in memory. The FPU
  ; constants are not allowed as input. If the source is taken from the
  ; FPU, its value will be preserved there if no error is reported.
  ;
  ; The source is not checked for validity. This is the programmer's
  ; responsibility.
  ;
  ; This procedure is based on using an FPU instruction to convert the
  ; REAL number into a specific packed decimal format. After unpacking,
  ; the decimal point is positioned as required.
  ;
  ; Only EAX is used to return error or success. All other registers are
  ; preserved.
  ;
  ; -----------------------------------------------------------------------

    .486
    .model flat, stdcall  ; 32 bit memory model
    option casemap :none  ; case sensitive

    include Fpu.inc

    .data
    
    tempdw  dd    0    
    esize   dd    0
    eflags  dd    0
    Decimal dd    0
    oldcw   dw    0
    truncw  dw    0
    stword  dw    0
    bcdstr  dt    0    

    .code

; #########################################################################

FpuFLtoA proc public lpSrc:DWORD, lpDecimal:DWORD, lpDest:DWORD, uID:DWORD

      fclex                   ;clear exception flags on FPU

;get the specified number of decimals for result
;and make corrections if necessary

      mov   eax,lpDecimal
      test  uID,SRC2_DMEM
      jz    @F
      mov   eax,[eax]
   @@:
      or    eax,eax           ;check if negative
      jns   @F
      xor   eax,eax           ;change to zero if it was negative
   @@:
      cmp   eax,16
      jb    @F
      mov   eax,15            ;a maximum of 15 decimals is allowed
   @@:
      mov   Decimal,eax

; the FPU will be initialized only if the source parameter is not taken
; from the FPU itself.

      test  uID,SRC1_FPU      ;is Src taken from FPU?
      jz    @F
      fld   st(0)             ;copy it to preserve the original value
      jmp   dest0             ;go complete process
@@:
      finit
      
;----------------------------------------
;check source for Src and load it to FPU
;----------------------------------------

      test  uID,SRC1_REAL     ;is Src an 80-bit real in memory?
      jz    srcerr            ;source not properly identified
      mov   eax,lpSrc
      fld   tbyte ptr [eax]
      jmp   dest0             ;go complete process
      
srcerr:
      finit
      xor   eax,eax
      ret

dest0:

;-----------------------------------------------------
;check first if value on FPU is valid or equal to zero
;-----------------------------------------------------

      ftst                    ;test value on FPU
      fstsw stword            ;get result
      fwait
      test  stword,4000h      ;check it for zero or NAN
      jz    @F                ;continue if valid non-zero
      test  stword,100h       ;now check it for NAN
      jnz   srcerr            ;Src is NAN or infinity - cannot convert

;-------------------------
;value to be converted = 0
;-------------------------

      push  ebx
      mov   ebx,lpDest
      mov   ax,30h            ;"0" szstring
      mov   [ebx],ax          ;write it
      pop   ebx
      jmp   finish1
      
;---------------------------
; get the size of the number
;---------------------------

@@:
      fld   st(0)             ;copy it
      fabs                    ;insures a positive value
      fld1
      fldl2t
      fdiv                    ;->1/[log2(10)]
      fxch
      fyl2x                   ;->[log2(Src)]/[log2(10)] = log10(Src)
      
      fstcw oldcw             ;get current control word
      fwait
      mov   ax,oldcw
      or    ax,0c00h          ;code it for truncating
      mov   truncw,ax
      fldcw truncw            ;change rounding code of FPU to truncate
      
      fist  esize             ;store characteristic of logarithm
      fldcw oldcw             ;load back the former control word

      ftst                    ;test logarithm for its sign
      fstsw stword            ;get result
      fwait
      test  stword,100h       ;check if negative
      jz    @F
      dec   esize
@@:
      fstp  st(0)             ;get rid of the logarithm

;---------------------------------------------------------------------
; multiply the number by a power of 10 to generate a 16-digit integer
;---------------------------------------------------------------------

      mov   eax,15
      sub   eax,esize         ;exponent required to get a 16-digit integer
      jz    @F                ;no need if already a 16-digit integer
      mov   tempdw,eax
      fild  tempdw
      fldl2t
      fmul                    ;->log2(10)*exponent
      fld   st(0)
      frndint                 ;get the characteristic of the log
      fxch
      fsub st,st(1)           ;get only the fractional part but keep the characteristic
      f2xm1                   ;->2^(fractional part)-1
      fld1
      fadd                    ;add 1 back
      fscale                  ;re-adjust the exponent part of the REAL number
      fxch
      fstp  st(0)             ;get rid of the characteristic of the log
      fmul                    ;->16-digit integer

@@:
      fbstp bcdstr            ;->TBYTE containing a 16-digit packed decimal
      fstsw stword            ;retrieve exception flags from FPU
      fwait
      test  stword,1          ;test for invalid operation
      jnz   srcerr            ;clean-up and return error

;------------------------------------------------------------------------------
; unpack bcd, the 10 bytes returned by the FPU being in the little-endian style
;------------------------------------------------------------------------------

      push  ecx
      push  esi
      push  edi
      lea   esi,bcdstr+9      ;go to the most significant byte (sign byte)
      mov   edi,lpDest
      mov   al,[esi]          ;sign byte
      dec   esi
      dec   esi               ;skips next most significant byte left blank on purpose
      .if   al == 80h
            mov   al,"-"      ;insert sign if negative number
      .else
            mov   al," "      ;insert space if positive number
      .endif
      stosb

      test  uID,STR_SCI       ;scientific notation?
      jnz   scientific

;************************
; REGULAR STRING NOTATION
;************************

      cmp   esize,15
      jg    scientific        ;use scientific notation if size >= 10^16
                              ;which would have 17 or more digits

;-------------------------------------------------------
;verify number of decimals required vs maximum allowable
;-------------------------------------------------------

      mov   eax,15
      sub   eax,esize
      cmp   eax,Decimal
      jae   @F                ;continue if OK or if EAX is negative
      mov   Decimal,eax 

;------------------------
;check for integer digits
;------------------------      
 
   @@:
      mov   ecx,esize
      or    ecx,ecx           ;is it negative (i.e. smaller than 1)
      jns   regular1

;-------------------------------------------------
;insert required leading 0's before decimal digits
;-------------------------------------------------

      mov   al,"0"
      mov   ah,"."
      stosw                   ;start with  0.  when smaller than 1
      neg   ecx
      cmp   ecx,Decimal
      jbe   @F
      jmp   finish            ;there would be too many 0's before 1st digit
   @@:
      dec   ecx
      jz    reg1
      stosb                   ;continue with as many 0's as necessary
      jmp   @B
   reg1:
      mov   ecx,Decimal
      inc   ecx
      add   ecx,esize
      jg    regular2          ;go display the decimals if any
      jmp   finish            ;otherwise clean up and exit

;-------------------
; do integer digits
;-------------------

regular1:
      inc   ecx               ;number of integer digits is size+1
   @@:
      xor   eax,eax
      mov   al,[esi]          ;get packed decimal
      dec   esi
      ror   ax,4
      ror   ah,4              ;unpack it
      add   ax,3030h          ;convert to characters
      stosw                   ;add to string
      sub   ecx,2
      jg    @B                ;continue if more left
      jz    @F                ;just enough integer digits
      dec   edi               ;one too many integer digit
   @@:
      cmp   Decimal,0
      jz    finish            ;terminate if no decimal digit required
      mov   al,"."
      stosb                   ;insert decimal separator
      or    ecx,ecx
      mov   ecx,Decimal
      jz    regular2          ;go if just enough integer digits
      mov   al,ah
      stosb                   ;re-insert the last digit as a decimal digit
      dec   ecx               ;make correction
      jz    finish            ;terminate if done

;-----------------
;do decimal digits
;-----------------
      
regular2:
      xor   eax,eax
      mov   al,[esi]          ;get packed decimal
      dec   esi
      ror   ax,4
      ror   ah,4              ;unpack it
      add   ax,3030h          ;convert to characters
      stosw                   ;add to string
      sub   ecx,2
      jg    regular2          ;continue if more left
      jz    @F
      dec   edi               ;to disregard last character
   @@:
      jmp   finish

;********************
; SCIENTIFIC NOTATION
;********************

scientific:
      mov   ecx,Decimal
      inc   ecx               ;for the integer digit
      xor   eax,eax
      mov   al,[esi]          ;get packed decimal
      dec   esi
      ror   ax,4
      ror   ah,4              ;unpack it
      add   ax,3030h          ;convert to characters
      stosb                   ;insert integer digit
      mov   al,"."
      stosb                   ;insert decimal separator
      mov   al,ah
      stosb                   ;insert 1st decimal digit
      sub   ecx,2
      jz    exponent
      jns   @F                ;continue with other decimal digits
      dec   edi               ;to disregard last decimal digit
      jmp   exponent
   @@:
      xor   eax,eax
      mov   al,[esi]
      dec   esi
      ror   ax,4
      ror   ah,4
      add   ax,3030h
      stosw
      sub   ecx,2
      jg    @B
      jz    exponent
      dec   edi

exponent:
      mov   al,"E"
      stosb
      mov   al,"+"
      mov   ecx,esize
      or    ecx,ecx
      jns   @F
      mov   al,"-"
      neg   ecx               ;make number positive
   @@:
      stosb                   ;insert proper sign

;Note: the absolute value of the size could not exceed 4931
      
      mov   eax,ecx
      mov   cl,100
      div   cl                ;->thousands & hundreds in AL, tens & units in AH
      push  eax
      and   eax,0ffh          ;keep only the thousands & hundreds
      mov   cl,10
      div   cl                ;->thousands in AL, hundreds in AH
      add   ax,3030h          ;convert to characters
      stosw                   ;insert them
      pop   eax
      shr   eax,8             ;get the tens & units in AL
      div   cl                ;tens in AL, units in AH
      add   ax,3030h          ;convert to characters
      stosw                   ;insert them

finish:
      xor   eax,eax
      stosb                   ;string terminating 0
      pop   edi
      pop   esi
      pop   ecx

finish1:
      test  uID,SRC1_FPU
      jnz   @F                ;retain original number on FPU if it was the source
      finit

@@:
      or    al,1              ;to insure EAX!=0
      ret
      
FpuFLtoA endp

; #########################################################################

end

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?