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 + -
显示快捷键?