0,0 → 1,789 |
;FpuAtoFL PROTO :DWORD,:DWORD,:DWORD |
;FpuFLtoA PROTO :DWORD,:DWORD,:DWORD,:DWORD |
|
;FpuAdd PROTO :DWORD,:DWORD,:DWORD,:DWORD |
;FpuSub PROTO :DWORD,:DWORD,:DWORD,:DWORD |
;FpuMul PROTO :DWORD,:DWORD,:DWORD,:DWORD |
;FpuDiv PROTO :DWORD,:DWORD,:DWORD,:DWORD |
;FpuSqrt PROTO :DWORD,:DWORD,:DWORD |
;FpuXexpY PROTO :DWORD,:DWORD,:DWORD,:DWORD |
;FpuAbs PROTO :DWORD,:DWORD,:DWORD |
;FpuTrunc PROTO :DWORD,:DWORD,:DWORD |
;FpuRound PROTO :DWORD,:DWORD,:DWORD |
;FpuChs PROTO :DWORD,:DWORD,:DWORD |
|
;FpuLnx PROTO :DWORD,:DWORD,:DWORD |
;FpuLogx PROTO :DWORD,:DWORD,:DWORD |
;FpuEexpX PROTO :DWORD,:DWORD,:DWORD |
;FpuTexpX PROTO :DWORD,:DWORD,:DWORD |
|
;FpuSin PROTO :DWORD,:DWORD,:DWORD |
;FpuCos PROTO :DWORD,:DWORD,:DWORD |
;FpuTan PROTO :DWORD,:DWORD,:DWORD |
;FpuArcsin PROTO :DWORD,:DWORD,:DWORD |
;FpuArccos PROTO :DWORD,:DWORD,:DWORD |
;FpuArctan PROTO :DWORD,:DWORD,:DWORD |
|
;FpuSinh PROTO :DWORD,:DWORD,:DWORD |
;FpuCosh PROTO :DWORD,:DWORD,:DWORD |
;FpuTanh PROTO :DWORD,:DWORD,:DWORD |
;FpuArcsinh PROTO :DWORD,:DWORD,:DWORD |
;FpuArccosh PROTO :DWORD,:DWORD,:DWORD |
;FpuArctanh PROTO :DWORD,:DWORD,:DWORD |
|
;FpuSize PROTO :DWORD,:DWORD,:DWORD |
;FpuComp PROTO :DWORD,:DWORD,:DWORD |
;FpuExam PROTO :DWORD,:DWORD |
;FpuState PROTO :DWORD |
|
SRC1_FPU EQU 1 |
SRC1_REAL EQU 2 |
SRC1_DMEM EQU 4 |
SRC1_DIMM EQU 8 |
SRC1_CONST EQU 16 |
|
ANG_DEG EQU 0 |
ANG_RAD EQU 32 |
|
DEST_MEM EQU 0 |
DEST_IMEM EQU 64 |
DEST_FPU EQU 128 |
|
SRC2_FPU EQU 256 |
SRC2_REAL EQU 512 |
SRC2_DMEM EQU 1024 |
SRC2_DIMM EQU 2048 |
SRC2_CONST EQU 4096 |
|
STR_REG EQU 0 |
STR_SCI EQU 32768 |
|
FPU_PI EQU 1 |
FPU_NAPIER EQU 2 |
|
XAM_VALID EQU 1 |
XAM_ZERO EQU 2 |
XAM_NEG EQU 4 |
XAM_SMALL EQU 8 |
XAM_INFINIT EQU 16 |
|
CMP_EQU EQU 1 |
CMP_GREATER EQU 2 |
CMP_LOWER EQU 4 |
|
|
|
|
; ######################################################################### |
; |
; FpuFLtoA |
; |
;########################################################################## |
|
; ----------------------------------------------------------------------- |
; This procedure was written by Raymond Filiatreault, December 2002 |
; and modified April 2003. A minor flaw was corrected in July 2003. |
; Modified March 2004 to avoid any potential data loss from the FPU |
; |
; 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 significant digits must not exceed 16. |
; When the regular format is specified, the integer portion can also be |
; padded with preceding spaces to position the decimal point at a |
; specified location from the start of the string. |
; |
; The source can be an 80-bit REAL number from the FPU itself or from |
; memory. |
; |
; 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 CPU registers |
; are preserved. All FPU registers are preserved. |
; |
; ----------------------------------------------------------------------- |
|
|
; ######################################################################### |
align 4 |
proc FpuFLtoA stdcall, lpSrc:DWORD, lpDecimal:DWORD, lpDest:DWORD, uID:DWORD |
|
locals |
tempdw dd ? |
esize dd ? |
Padding dd ? |
Decimal dd ? |
content: times 108 db ? |
tempst dt ? |
bcdstr dt ? |
oldcw dw ? |
truncw dw ? |
unpacked: times 20 db ? |
endl |
|
;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] ;get the decimals from memory |
@@: |
push eax |
movzx eax,al ;low byte - number of decimal digits |
cmp eax,15 |
jbe @F |
mov eax,15 ;a maximum of 15 decimals is allowed |
@@: |
mov [Decimal],eax |
pop eax |
movzx eax,ah ;2nd byte - number of char before decimal point |
cmp eax,17 |
jbe @F |
mov eax,17 ;a maximum of 17 characters is allowed |
@@: |
mov [Padding],eax |
|
test [uID],SRC1_FPU ;is data taken from FPU? |
jz @F ;continue if not |
|
;------------------------------- |
;check if top register is empty |
;------------------------------- |
|
fxam ;examine its content |
fstsw ax ;store results in AX |
fwait ;for precaution |
sahf ;transfer result bits to CPU flag |
jnc @F ;not empty if Carry flag not set |
jpe @F ;not empty if Parity flag set |
jz srcerr1 ;empty if Zero flag set |
|
@@: |
fsave [content] |
|
;---------------------------------------- |
;check source for Src and load it to FPU |
;---------------------------------------- |
|
test [uID],SRC1_FPU |
jz @F |
lea eax,[content] |
fld tbyte [eax+28] |
jmp dest0 |
|
@@: |
test [uID],SRC1_REAL ;is Src an 80-bit REAL in memory? |
jz srcerr ;no proper source identificaiton |
mov eax,[lpSrc] |
fld tbyte [eax] |
jmp dest0 ;go complete process |
|
srcerr: |
frstor [content] |
srcerr1: |
push edi |
mov edi,[lpDest] |
mov eax,"ERRO";ORRE" |
stosd |
mov ax,"R" |
stosw |
pop edi |
xor eax,eax |
ret |
|
dest0: |
|
;------------------------------------------- |
;first examine the value on FPU for validity |
;------------------------------------------- |
|
fxam ;examine value on FPU |
fstsw ax ;get result |
fwait |
sahf ;transfer to CPU flags |
jz maybezero |
jpo srcerr ;C3=0 and C2=0 would be NAN or unsupported |
jnc getnumsize ;continue if normal finite number |
|
;-------------------------------- |
;value to be converted = INFINITY |
;-------------------------------- |
|
push ecx |
push esi |
push edi |
mov edi,[lpDest] |
mov al,"+" |
test ah,2 ;C1 field = sign |
jz @F |
mov al,"-" |
@@: |
stosb |
mov eax,"INFI";IFNI" |
stosd |
mov eax,"NITY";YTIN" |
stosd |
jmp finish |
|
;------------------------- |
;value to be converted = 0 |
;------------------------- |
|
maybezero: |
jpe getnumsize ;would be denormalized number |
fstp st0 ;flush that 0 value off the FPU |
push ecx |
push esi |
push edi |
mov edi,[lpDest] |
test [uID],STR_SCI ;scientific notation? |
jnz @F ;no padding |
mov ecx,[Padding] |
sub ecx,2 |
jle @F ;no padding specified or necessary |
mov al," " |
rep stosb |
@@: |
mov ax,3020h ;" 0" szstring |
stosw ;write it |
jmp finish |
|
;--------------------------- |
; get the size of the number |
;--------------------------- |
|
getnumsize: |
fldlg2 ;log10(2) |
fld st1 ;copy Src |
fabs ;insures a positive value |
fyl2x ;->[log2(Src)]*[log10(2)] = log10(Src) |
|
fstcw [oldcw] ;get current control word |
fwait |
mov ax,[oldcw] |
or ax,0c00h ;code it for truncating |
mov [truncw],ax |
fldcw [truncw] ;insure rounding code of FPU to truncating |
|
fist [esize] ;store characteristic of logarithm |
fldcw [oldcw] ;load back the former control word |
|
ftst ;test logarithm for its sign |
fstsw ax ;get result |
fwait |
sahf ;transfer to CPU flags |
sbb [esize],0 ;decrement esize if log is negative |
fstp st0 ;get rid of the logarithm |
|
;----------------------------------------------------------------------- |
; get the power of 10 required to generate an integer with the specified |
; number of significant digits |
;----------------------------------------------------------------------- |
|
mov eax,[uID] |
and eax,STR_SCI |
jnz .els0 ;regular decimal notation |
mov eax,[esize] |
or eax,eax ;check if number is < 1 |
js @F |
; .if eax > 15 ;if number is >= 10^16 |
cmp eax,15 |
jbe .endif0 |
or [uID],STR_SCI ;switch to scientific notation |
mov [Decimal],15 ;insures 15 decimal places in result |
jmp scific |
.endif0: |
add eax,[Decimal] |
; .if eax > 15 ;if integer + decimal digits > 16 |
cmp eax,15 |
jbe .endif1 |
sub eax,15 |
sub [Decimal],eax ;reduce decimal digits as required |
.endif1: |
@@: |
push [Decimal] |
pop [tempdw] |
jmp @f |
.els0: ;scientific notation |
scific: |
mov eax,[Decimal] |
sub eax,[esize] |
mov [tempdw],eax |
; .endif |
@@: |
|
;---------------------------------------------------------------------------------------- |
; multiply the number by the power of 10 to generate required integer and store it as BCD |
;---------------------------------------------------------------------------------------- |
|
;.if tempdw != 0 |
cmp [tempdw],0 |
je @f |
fild [tempdw] |
fldl2t |
fmulp st1,st0 ;->log2(10)*exponent |
fld st0 |
frndint ;get the characteristic of the log |
fxch |
fsub st,st1 ;get only the fractional part but keep the characteristic |
f2xm1 ;->2^(fractional part)-1 |
fld1 |
faddp st1,st0 ;add 1 back |
fscale ;re-adjust the exponent part of the REAL number |
fstp st1 ;get rid of the characteristic of the log |
fmulp st1,st0 ;->16-digit integer |
;.endif |
@@: |
|
fbstp [bcdstr] ;->TBYTE containing the packed digits |
fstsw ax ;retrieve exception flags from FPU |
fwait |
shr eax,1 ;test for invalid operation |
jc 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) |
lea edi,[unpacked] |
mov eax,3020h |
mov cl,byte [esi] ;sign byte |
; .if cl == 80h |
cmp cl,0x80 |
jne @f |
mov al,"-" ;insert sign if negative number |
; .endif |
@@: |
stosw |
mov ecx,9 |
@@: |
dec esi |
movzx eax,byte [esi] |
ror ax,4 |
ror ah,4 |
add ax,3030h |
stosw |
dec ecx |
jnz @B |
|
mov edi,[lpDest] |
lea esi,[unpacked] |
test [uID],STR_SCI ;scientific notation? |
jnz scientific |
|
;************************ |
; REGULAR STRING NOTATION |
;************************ |
|
;------------------------------ |
; check if padding is specified |
;------------------------------ |
|
mov ecx,[Padding] |
or ecx,ecx ;check if padding is specified |
jz nopadding |
|
mov edx,2 ;at least 1 integer + sign |
mov eax,[esize] |
or eax,eax |
js @F ;only 1 integer digit if size is < 1 |
add edx,eax ;->number of integer digits |
@@: |
sub ecx,edx |
jle nopadding |
mov al," " |
rep stosb |
|
nopadding: |
pushfd ;save padding flags |
movsb ;insert sign |
mov ecx,1 ;at least 1 integer digit |
mov eax,[esize] |
or eax,eax ;is size negative (i.e. number smaller than 1) |
js @F |
add ecx,eax |
@@: |
mov eax,[Decimal] |
add eax,ecx ;->total number of digits to be displayed |
sub eax,19 |
sub esi,eax ;address of 1st digit to be displayed |
pop eax ;retrieve padding flags in EAX |
; .if byte ptr[esi-1] == "1" |
cmp byte [esi-1],"1" |
jne @f |
dec esi |
inc ecx |
push eax ;transfer padding flags through stack |
popfd ;retrieve padding flags |
jle @F ;no padding was necessary |
dec edi ;adjust for one less padding byte |
; .endif |
@@: |
@@: |
rep movsb ;copy required integer digits |
mov ecx,[Decimal] |
or ecx,ecx |
jz @F |
mov al,"." |
stosb |
rep movsb ;copy required decimal digits |
@@: |
jmp finish |
|
;******************** |
; SCIENTIFIC NOTATION |
;******************** |
|
scientific: |
movsb ;insert sign |
mov ecx,[Decimal] |
mov eax,18 |
sub eax,ecx |
add esi,eax |
cmp byte [esi-1],"1" |
pushfd ;save flags for extra "1" |
jnz @F |
dec esi |
@@: |
movsb ;copy the integer |
mov al,"." |
stosb |
rep movsb ;copy the decimal digits |
|
mov al,"E" |
stosb |
mov al,"+" |
mov ecx,[esize] |
popfd ;retrieve flags for extra "1" |
jnz @F ;no extra "1" |
inc ecx ;adjust exponent |
@@: |
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 |
|
frstor [content] |
|
or al,1 ;to insure EAX!=0 |
ret |
|
endp |
|
; ######################################################################### |
|
|
align 4 |
|
; ######################################################################### |
; FpuCos |
;########################################################################## |
; |
; cos(Src) -> Dest |
FpuCos: |
test [flags],(1 shl 30) |
jz @f ;jump if angle already in radians |
fldpi ;load pi (3.14159...) on FPU |
fmulp |
test [flags],(1 shl 31) |
jnz .1 |
pushd 200 |
jmp .2 |
.1: pushd 180 |
.2: fidiv dword [esp] ;value now in radians |
fwait |
add esp,4 ;clean the stack |
@@: fldpi |
fadd st,st ;->2pi |
fxch |
|
@@: fprem ;reduce the angle |
fcos |
fstsw ax ;retrieve exception flags from FPU |
fwait |
shr al,1 ;test for invalid operation |
sahf ;transfer to the CPU flags |
jpe @B ;reduce angle again if necessary |
fstp st1 ;get rid of the 2pi |
ret |
|
align 4 |
; ######################################################################### |
; FpuSin |
;########################################################################## |
; |
; sin(Src) -> Dest |
FpuSin: |
test [flags],(1 shl 30) |
jz @f ;jump if angle already in radians |
fldpi ;load pi (3.14159...) on FPU |
fmulp |
test [flags],(1 shl 31) |
jnz .1 |
pushd 200 |
jmp .2 |
.1: pushd 180 |
.2: fidiv dword [esp] ;value now in radians |
fwait |
add esp,4 ;clean the stack |
@@: |
fldpi |
fadd st,st ;->2pi |
fxch |
|
fprem ;reduce the angle |
fsin |
fstp st1 ;get rid of the 2pi |
ret |
|
align 4 |
; ######################################################################### |
; FpuArctan |
;########################################################################## |
; |
; atan(Src) -> Dest |
FpuArctan: |
fld1 |
fpatan |
test [flags],(1 shl 30) |
jz @F ;jump if angle is required in radians |
test [flags],(1 shl 31) |
jnz .1 |
pushd 200 |
jmp .2 |
.1: pushd 180 |
.2: fimul dword [esp] ;*180 degrees |
fldpi ;load pi (3.14159...) on FPU |
fdivp ;*180/pi, angle now in degrees |
add esp,4 ;clean CPU stack |
|
ftst ;check for negative angle |
fstsw ax ;retrieve status word from FPU |
fwait |
sahf |
jnc @F ;jump if positive number |
test [flags],(1 shl 31) |
jnz .3 |
pushd 400 |
jmp .4 |
.3: pushd 360 |
.4: fiadd dword [esp] ;angle now 0-360 |
fwait |
add esp,4 ;clean CPU stack |
|
@@: |
ret |
|
align 4 |
; ######################################################################### |
; FpuTan |
;########################################################################## |
|
; a = tan(x) |
FpuTan: |
fldpi ;load pi (3.14159...) on FPU |
fadd st,st ;->2pi |
fxch |
test [flags],(1 shl 30) |
jz @F ;jump if angle already in radians |
test [flags],(1 shl 31) |
jnz .1 |
pushd 400 |
jmp .2 |
.1: pushd 360 |
.2: fmul st,st1 |
fidiv dword [esp] ;value now in radians |
pop eax ;clean the stack |
@@: fprem ;reduce the angle |
fptan |
|
fstp st ;get rid of the 1 |
fstp st1 ;get rid of the 2pi |
ret |
|
align 4 |
; ######################################################################### |
; FpuArccos |
;########################################################################## |
; sqrt(1-Src^2) |
; acos(Src) = atan ------------- -> Dest |
; Src |
|
FpuArccos: |
fld st ;copy cosine value |
fmul st0,st1 ;cos^2 |
fld1 |
fsubrp ;1-cos^2 = sin^2 |
fsqrt ;->sin |
fxch |
fpatan ;i.e. arctan(sin/cos) |
|
test [flags],(1 shl 30) |
jz @F ;jump if angle is required in radians |
test [flags],(1 shl 31) |
jnz .1 |
pushd 200 |
jmp .2 |
.1: pushd 180 |
.2: fimul dword [esp] ;*180 degrees |
fldpi ;load pi (3.14159...) on FPU |
fdivp ;*180/pi, angle now in degrees |
pop eax ;clean CPU stack |
@@: |
ret |
|
align 4 |
; ######################################################################### |
; FpuArcsin |
;########################################################################## |
; Src |
; asin(Src) = atan ------------- -> Dest |
; sqrt(1-Src^2) |
FpuArcsin: |
fld st0 ;copy sine value |
fmul st0,st0 ;sin^2 |
fld1 |
fsubrp ;1-sin^2 = cos^2 |
fsqrt ;->cos |
fpatan ;i.e. arctan(sin/cos) = arcsin |
test [flags],(1 shl 30) |
jz @F ;jump if angle is required in radians |
test [flags],(1 shl 31) |
jnz .1 |
pushd 200 |
jmp .2 |
.1: pushd 180 |
.2: fimul dword [esp] ;*180 degrees |
fldpi ;load pi (3.14159...) on FPU |
fdivrp ;*180/pi, angle now in degrees |
add esp,4 ;clean CPU stack |
@@: |
ret |
|
align 4 |
; ######################################################################### |
; FpuEexpX |
;########################################################################## |
|
; e^(Src) = antilog2[ log2(e) * Src ] -> Dest |
|
FpuEexpX: |
|
fldl2e ;->log2(e) |
fmulp ;->log2(e)*Src |
|
fld st0 ;copy the logarithm |
frndint ;keep only the characteristic |
fsub st1,st ;keeps only the mantissa |
fxch ;get the mantissa on top |
|
f2xm1 ;->2^(mantissa)-1 |
fld1 |
faddp ;add 1 back |
|
fscale ;scale it with the characteristic |
|
fstp st1 ;get rid of the characteristic |
ret |
|
align 4 |
; ######################################################################### |
; FpuXexpY |
;########################################################################## |
|
; Src1^Src2 = antilog2[ log2(Src1) * Src2 ] -> Dest |
|
FpuXexpY: |
fxch ;set up FPU registers for next operation |
fyl2x ;->log2(Src1)*exponent |
|
fld st0 ;copy the logarithm |
frndint ;keep only the characteristic |
fsub st1,st ;keeps only the mantissa |
fxch ;get the mantissa on top |
|
f2xm1 ;->2^(mantissa)-1 |
fld1 |
faddp ;add 1 back |
|
fscale ;scale it with the characteristic |
|
fstp st1 ;overwrite the characteristic |
ret |
|
|
if 0 |
exp: |
.N equ 10 |
xor eax,eax |
mov ecx,.N |
fld1 |
.0: dec ecx |
jz .end |
mov eax,.N |
sub eax,ecx |
mov [.d],eax |
fild [.d] |
@@: dec eax |
jz @f |
mov [.d],eax |
fild [.d] |
fmulp |
jmp @b |
@@: mov eax,.N |
sub eax,ecx |
fld st2 |
@@: dec eax |
jz @f |
fld st3 |
fmulp |
jmp @b |
@@: fdivrp |
faddp |
jmp .0 |
.end: fxch |
fstp st0 |
ret |
.d dd 0 |
endf |