Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1484 lines
48 KiB

page ,132
;------------------------------Module-Header----------------------------;
; Module Name: math.asm ;
; ;
; This module contains the arithmetics routines for the engine internal ;
; floating point type EFLOAT. It is mostly adapted from ChuckWh's ;
; math.asm for PM. ;
; ;
; Created: 14-Nov-1990 ;
; Author: Wendy Wu [wendywu] ;
; ;
; Copyright (c) 1990 Microsoft Corporation ;
;-----------------------------------------------------------------------;
.386
.model small,c
assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
assume fs:nothing,gs:nothing
.xlist
include stdcall.inc
include gdii386.inc
.list
.data
if DBG
MATH_DivideError db 'divff3_c - Divide overflow',10,0
endif
.code
if DBG
extrn DbgPrint:proc
endif
NUMBER_OF_INTEGER_BITS_LONG equ 32
NUMBER_OF_INTEGER_BITS_FIX equ 28
IEEE_NAN equ 7FFFFFFFh ; not a number exp = 255 mant != 0
EXPONENT_OF_ONE equ 2
MANTISSA_OF_ONE equ 040000000h
BOOL_TRUNCATE equ 0
BOOL_ROUND equ 1
RESULT_NEGATIVE equ 1
public cmp_table_1
public cmp_table_2
public cmp_table_3
public cmp_table_4
cmp_table_1 label dword
dd 01000000h
dd 00000100h
cmp_table_2 label dword
dd 10000000h
dd 00100000h
dd 00001000h
dd 00000010h
cmp_table_3 label dword
dd 40000000h
dd 04000000h
dd 00400000h
dd 00040000h
dd 00004000h
dd 00000400h
dd 00000040h
dd 00000004h
cmp_table_4 label dword
dd 80000000h
dd 20000000h
dd 08000000h
dd 02000000h
dd 00800000h
dd 00200000h
dd 00080000h
dd 00020000h
dd 00008000h
dd 00002000h
dd 00000800h
dd 00000200h
dd 00000080h
dd 00000020h
dd 00000008h
dd 00000002h
;---------------------------Private-Routine-----------------------------;
; dNormalize
;
; Normalizes a DWORD so that its absolute value has 0 in sign bit and
; 1 in the highest order non-sign bit. Return the number of shifts done.
;
; Entry:
; EAX = DWORD
; Returns:
; ZF = 1 if zero, 0 otherwise
; EAX = normalized DWORD
; ECX = shift count
; Registers Destroyed:
; EDX
; Calls:
; None
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc dNormalize
cdq ; save the sign
xor ecx,ecx
xor eax,edx ; absolute eax
sub eax,edx
jz dNormalize_zero
js dNormalize_80000000
cmp eax,10000h
adc cl,cl ; cl = 0 if eax >= 10000h
; cl = 1 if eax < 10000h
cmp eax,cmp_table_1[4*ecx]
adc cl,cl ; cl = 0 if eax > 1000000h
; cl = 1 if 1000000h > eax > 10000h
; cl = 2 if 10000h > eax > 100h
; cl = 3 if eax < 100h
cmp eax,cmp_table_2[4*ecx]
adc cl,cl ; 0 <= cl <= 7
cmp eax,cmp_table_3[4*ecx]
adc cl,cl ; 0 <= cl <= 15
cmp eax,cmp_table_4[4*ecx]
adc cl,cl ; 1 <= cl <= 31
; cl will never be 0 since eax > 0
dec cl ; offset to highest non-sign bit
shl eax,cl ; shift to highest non-sign bit
xor eax,edx ; negate eax if was negative number
sub eax,edx
dNormalize_zero:
cRet dNormalize
dNormalize_80000000:
sar eax,1
mov ecx,-1
cRet dNormalize
endProc dNormalize
;-----------------------------Private-Routine---------------------------;
; ltoef
;
; Convert a LONG integer to an EFLOAT format number.
;
; Entry:
; EAX = LONG integer to be converted
; Returns:
; ZF = 1 if zero, 0 otherwise
; EAX = mantissa of the EFLOAT number
; ECX = exponent of the EFLOAT number
; Registers Destroyed:
; EDX
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc ltoef
cCall dNormalize ; mant = normalized LONG
jz @F
neg ecx
add ecx,NUMBER_OF_INTEGER_BITS_LONG ; exp = 32 - shift count
@@:
cRet dNormalize
endproc ltoef
;---------------------------Public-Routine------------------------------;
; ltoef_c
;
; Convert a LONG integer to an EFLOAT format number.
;
; Arguments:
; IN l LONG integer to be converted
; OUT pef points to the EFLOAT number
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc ltoef_c,8,< \
l: dword, \
pef: ptr EFLOAT >
mov eax,l
cCall dNormalize ; mant = normalized LONG
jz @F
neg ecx
add ecx,NUMBER_OF_INTEGER_BITS_LONG ; exp = 32 - shift count
@@:
mov edx,pef
mov [edx].ef_lMant,eax
mov [edx].ef_lExp,ecx
cRet ltoef_c
endProc ltoef_c
;-----------------------------Private-Routine---------------------------;
; fxtoef
;
; Convert a FIX number to an EFLOAT format number.
;
; Entry:
; EAX = FIX number to be converted
; Returns:
; ZF = 1 if zero, 0 otherwise
; EAX = mantissa of the EFLOAT number
; ECX = exponent of the EFLOAT number
; Registers Destroyed:
; EDX
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc fxtoef
cCall dNormalize ; mant = normalized fIX number
jz @F
neg ecx
add ecx,NUMBER_OF_INTEGER_BITS_FIX ; exp = 28 - shift count
@@:
cRet fxtoef
endProc fxtoef
;---------------------------Public-Routine------------------------------;
; fxtoef_c
;
; Convert a FIX number to an EFLOAT format number.
;
; Arguments:
; IN fx FIX number to be converted
; OUT pEf points to the EFLOAT number
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc fxtoef_c,8,< \
fx: dword, \
pEf: ptr EFLOAT >
mov eax,fx
cCall dNormalize ; mant = normalized LONG
jz @F
neg ecx
add ecx,NUMBER_OF_INTEGER_BITS_FIX ; exp = 28 - shift count
@@:
mov edx,pEf
mov [edx].ef_lMant,eax
mov [edx].ef_lExp,ecx
cRet fxtoef_c
endProc fxtoef_c
;---------------------------Public-Routine------------------------------;
; ftoef_c
;
; Convert an IEEE FLOAT format number to an EFLOAT format number.
;
; Arguments:
; IN e IEEE FLOAT number to be converted
; OUT pEf points to the EFLOAT number
; Calls:
; None
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc ftoef_c,8,< \
e: dword, \
pEf: ptr EFLOAT >
mov eax,e
shld ecx,eax,9
and ecx,0FFh ; ecx = e.exponent
jnz @F
xor eax,eax ; return 0 if (exp == 0)
jmp ftoef_zero
@@:
; We got a non-zero number
; EFLOAT.mantissa = FLOAT.mantissa + hidden one
cdq ; sign extend eax to edx
and eax,07FFFFFh ; mask off sign bit and exponent
shl eax,7 ; shift to its position
or eax,040000000h ; or in the hidden one
;ASSERT(((eax & 080000000h) == 0),"ftoef_c error");
xor eax,edx ; negate mantissa if (e < 0)
sub eax,edx
; ef.exp = e.exp + 2 - 127
sub ecx,125
ftoef_zero:
mov edx,pEf
mov [edx].ef_lMant,eax
mov [edx].ef_lExp,ecx
cRet ftoef_c
endProc ftoef_c
;-----------------------------Public-Routine----------------------------;
; eftofx
;
; Convert an EFLOAT number to a FIX number. Fractions of 1/32 or greater
; are rounded up.
;
; Entry:
; EAX = mantissa
; ECX = exponent
; Returns:
; OF = 0
; EAX = converted long integer
; Error Returns:
; OF = 1
; Registers Destroyed:
; EDX,ECX
; Calls:
; eftol
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc eftofx
add ecx,4 ; so we can call eftol
mov edx,BOOL_ROUND
eftofx_fall_through::
endProc eftofx ; fall through to eftol
;-----------------------------Public-Routine----------------------------;
; eftol
;
; Convert an EFLOAT number to a LONG integer. Fractions are either
; rounded or truncated depending on the flag in EDX.
;
; Entry:
; EAX = mantissa
; ECX = exponent
; EDX = roundoff boolean
; Returns:
; OF = 0
; EAX = converted long integer
; Error Returns:
; OF = 1
; Registers Destroyed:
; ECX
; Calls:
; None
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc eftol
.errnz $-eftofx_fall_through
; if exp < 1, return 0.
cmp ecx,1
jl eftol_exp_small
cmp ecx,32
jg eftol_exp_big
jz @F ; return mantissa if (exp == 32)
;ASSERT((1 <= ecx <= 31),"eftol exponent error")
xor cl,31 ; 31 - exp
; EDX = 1 if we'll do truncation, LONG = ((mant >> (31 - exp)) + 1) >> 1
; EDX = 0 if we'll do rounding, LONG = (mant >> (31 - exp)) >> 1
;ASSERT(((edx & FFFFFFFEh) == 0),"eftol rounding boolean error")
sar eax,cl
add eax,edx
jo eftol_7fffffff ; overflow if mant = 7fffffff, exp = 31
sar eax,1
@@:
cRet eftol ; normal return
eftol_exp_small:
xor eax,eax ; exponent too small, return 0
cRet eftol ; OF = 0 by xor
eftol_exp_big:
mov cl,1 ; OF = 1 if exp > 32
add cl,7fh
cRet eftol
eftol_7fffffff:
shr eax,1 ; return 40000000h
xor cl,cl ; OF = 0
cRet eftol
endProc eftol
;---------------------------Public-Routine------------------------------;
; eftol_c
;
; Convert an EFLOAT number to a LONG integer. Fractions are rounded
; or truncated depending on the passed in flag.
;
; Arguments:
; IN pEf points to the EFLOAT number
; OUT pL points to the LONG integer
; IN bRound roundoff boolean
; Returns:
; EAX = 1 if success
; Error Returns:
; EAX = 0 if overflow
; Calls:
; eftol
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;
;CR!!! Delete this if nobody uses it.
;-----------------------------------------------------------------------;
cProc eftol_c,12,< \
pEf: ptr EFLOAT, \
pL: ptr LONG, \
bRound: dword >
mov edx,pEf
mov eax,[edx].ef_lMant
mov ecx,[edx].ef_lExp
mov edx,bRound
cCall eftol
mov edx,pL
mov [edx],eax
mov eax,0
setno al
cRet eftol_c
endProc eftol_c
;---------------------------Public-Routine------------------------------;
; eftofx_c
;
; Convert an EFLOAT number to a FIX number. Fractions of 1/32 or greater
; are rounded up.
;
; Arguments:
; IN pEf points to the EFLOAT number
; OUT pFx points to the FIX number
; Returns:
; EAX = 1 if success
; Error Returns:
; EAX = 0 if overflow
; Calls:
; eftol
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;
;CR!!! Delete this if nobody uses it.
;-----------------------------------------------------------------------;
cProc eftofx_c,8,< \
pEf: ptr EFLOAT, \
pFx: ptr FIX >
mov edx,pEf
mov eax,[edx].ef_lMant
mov ecx,[edx].ef_lExp
add ecx,4
mov edx,BOOL_ROUND
cCall eftol
mov edx,pFx
mov [edx],eax
mov eax,0
setno al
cRet eftofx_c
endProc eftofx_c
;---------------------------Public-Routine------------------------------;
; eftof_c
;
; Convert an EFLOAT number to an IEEE FLOAT format number.
;
; Arguments:
; IN pEf points to the EFLOAT number
; Returns:
; EAX = resulting IEEE FLOAT number
; Error Returns:
; EAX = NAN if overflow
; Calls:
; None
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc eftof_c,4,< \
pEf: ptr EFLOAT >
xor eax,eax ; assume result is 0
mov edx,pEf
mov ecx,[edx].ef_lMant
or ecx,ecx ; faster than jcxz
jz eftof_zero
mov edx,[edx].ef_lExp
jns eftof_positive
or eax,080000000h ; turn on sign bit if negative number
neg ecx ; make it positive
eftof_positive:
and ecx,3FFFFFFFh ; mask off sign bit and hidden one
shr ecx,7 ; shift mantissa to its position
adc ecx,0 ; round the result
test ecx,0800000h
jnz eftof_rounding_overflow
eftof_mant_done:
add edx,125 ; FLOAT.exp = EFLOAT.exp + 127 - 2
jo eftof_NAN
shl edx,23 ; shift exponent to its position
or eax,ecx
or eax,edx ; or sign, mant, exp bits together
eftof_zero:
cRet eftof_c
eftof_rounding_overflow:
xor ecx,ecx
inc edx
jno eftof_mant_done
eftof_NAN:
mov eax,IEEE_NAN
cRet eftof_c
endProc eftof_c
;---------------------------Public-Routine------------------------------;
; fraction_c
;
; Get the fraction part of an EFLOAT number.
;
; Arguments:
; IN pEfIn points to the EFLOAT number its fractional part is
; to be computed
; OUT pEfOut points to the EFLOAT number that stores the fractional
; part result
; Returns:
; Nothing
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
;!!!CR Does Andrew use this? Is the result correct when a negative number
;!!!CR is passed in?
cProc fraction_c,8,< \
pEfOut: ptr EFLOAT, \
pEfIn: ptr EFLOAT >
mov edx,pEfIn
mov eax,[edx].ef_lMant ; EAX = mantissa
mov ecx,[edx].ef_lExp ; ECX = exponent
cmp ecx,0
jle got_fractions ; if (exp <= 1) return itself
cmp ecx,32
jge no_fraction
; shift off integer part
have_fraction:
shl eax,cl ; shift off the integer bits
shr eax,1 ; make it positive
cCall dNormalize
jz no_fraction
neg ecx ; exp = 1 - left shift count
inc ecx
; We get here either because the number is too big, no fraction is stored
; in the mantissa or this number is an integer.
store_results:
mov edx,pEfOut
mov [edx].ef_lMant,eax
mov [edx].ef_lExp,ecx
mov eax,edx
cRet fraction_c
got_fractions: ; take care of negative fractions here
or eax,eax
jge store_results
neg ecx
sar eax,cl
shr eax,1 ; make it positive
neg ecx
inc ecx
jmp short store_results
no_fraction:
xor eax,eax ; exp >= 32 return 0
xor ecx,ecx
jmp store_results
endProc fraction_c
;-----------------------------Public-Routine----------------------------;
; addff
;
; Add two EFLOAT numbers together.
;
; Entry:
; EDX = mant
; EBX = exp
; EAX = mant
; ECX = exp
; Returns:
; EAX = mant
; ECX = exp
; OF = 0
; ZF = 1 if zero
; Error Returns:
; OF = 1 if overflow
; Registers Destroyed:
; EBX,EDX
; Calls:
; dNormalize
; History:
; 14-Nov-1990 -by- Wendy Wu [wendywu]
; Wrote it
;-----------------------------------------------------------------------;
cProc addff
or edx,edx ; early out if either one is 0
jz addff_exit
or eax,eax
jz addff_xchg_exit
cmp ecx,ebx
jge second_summand_larger
xchg eax,edx
xchg ecx,ebx
second_summand_larger:
sub ecx,ebx
cmp ecx,30 ; only 30 bits of pre-adding precision
jbe first_summand_not_small ;
add ecx,ebx ; clear overflow flag
jmp addff_exit
first_summand_not_small:
add ebx,ecx ; restore the bigger exp in ebx
sar edx,cl ; shift the smaller number right by
; difference in exp bit
sar eax,1 ; prevent overflow
; adc eax,0 ; round the bigger number
sar edx,1 ; prevent overflow
; adc edx,0 ; round the smaller number
add eax,edx ; add mantissa together
cCall dNormalize
jz addff_exit
; sum's exponent = EBX - ECX + 1
neg ecx
add ecx,ebx
inc ecx ; compensate for the right shift
addff_exit:
cRet addff
addff_xchg_exit:
mov eax,edx
mov ecx,ebx
cRet addff
endProc addff
;---------------------------Public-Routine------------------------------;
; subff_c ;
; ;
; Subtract an EFLOAT number from another. The result overwrites the ;
; subtrahend. ;
; ;
; Arguments: ;
; IN OUT pSub points to the EFLOAT subtrahend ;
; IN pMin points to the EFLOAT minuend ;
; Returns: ;
; EAX = 1 if success ;
; Error Returns: ;
; EAX = 0 if overflow ;
; Calls: ;
; addff ;
; History: ;
; Thu 19-Mar-1992 17:44:34 -by- Charles Whitmer [chuckwh] ;
; Added 3 parameter entry point. This gives us better C++ code. ;
; ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
cProc subff_c,8,< \
uses ebx, \
pSub: ptr EFLOAT, \
pMin: ptr EFLOAT >
mov ebx, pSub
mov edx, [ebx].ef_lMant
mov ebx, [ebx].ef_lExp
mov ecx, pMin
mov eax, [ecx].ef_lMant
mov ecx, [ecx].ef_lExp
neg eax
cCall addff
mov ebx,pSub
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
mov eax,0
setno al
cRet subff_c
endProc subff_c
cProc subff3_c,12,< \
uses ebx, \
pRes: ptr EFLOAT, \
pSub: ptr EFLOAT, \
pMin: ptr EFLOAT >
mov ebx, pSub
mov edx, [ebx].ef_lMant
mov ebx, [ebx].ef_lExp
mov ecx, pMin
mov eax, [ecx].ef_lMant
mov ecx, [ecx].ef_lExp
neg eax
cCall addff
mov ebx,pRes
jo short @F
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
@@: mov eax,ebx
cRet subff3_c
endProc subff3_c
;---------------------------Public-Routine------------------------------;
; addff_c ;
; ;
; Add two EFLOAT numbers together. The result overwrites the first ;
; summand. ;
; ;
; Arguments: ;
; IN OUT pSum1 points to the first EFLOAT summand ;
; IN pSum2 points to the second EFLOAT summand ;
; Returns: ;
; EAX = 1 if success ;
; Error Returns: ;
; EAX = 0 if overflow ;
; Calls: ;
; addff ;
; History: ;
; Thu 19-Mar-1992 17:44:34 -by- Charles Whitmer [chuckwh] ;
; Added 3 parameter entry point. This gives us better C++ code. ;
; ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
cProc addff_c,8,< \
uses ebx, \
pSum1: ptr EFLOAT, \
pSum2: ptr EFLOAT >
mov ebx, pSum2
mov edx, [ebx].ef_lMant
mov ebx, [ebx].ef_lExp
mov ecx, pSum1
mov eax, [ecx].ef_lMant
mov ecx, [ecx].ef_lExp
cCall addff
mov ebx,pSum1
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
mov eax,0
setno al
cRet addff_c
endProc addff_c
cProc addff3_c,12,< \
uses ebx, \
pRes: ptr EFLOAT, \
pSum1: ptr EFLOAT, \
pSum2: ptr EFLOAT >
mov ebx, pSum2
mov edx, [ebx].ef_lMant
mov ebx, [ebx].ef_lExp
mov ecx, pSum1
mov eax, [ecx].ef_lMant
mov ecx, [ecx].ef_lExp
cCall addff
mov ebx,pRes
jo short @F
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
@@: mov eax,ebx
cRet addff3_c
endProc addff3_c
;-----------------------------Public-Routine----------------------------;
; mulff ;
; ;
; Multiply two EFLOAT numbers together. ;
; ;
; Entry: ;
; EDX = first multiplicand's mant ;
; EBX = first multiplicand's exp ;
; EAX = second multiplicand's mant ;
; ECX = second multiplicand's exp ;
; Returns: ;
; EAX = mant ;
; ECX = exp ;
; OF = 0 ;
; ZF = 1 if zero ;
; Error Returns: ;
; OF = 1 if overflow ;
; Registers Destroyed: ;
; EDX,EBX ;
; Calls: ;
; None ;
; History: ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
mul_shift_table label byte ; highest 4 bits
db 0 ; 0000 should have been early out
db 2 ; 0001
db 1 ; 0010
db 1 ; 0011
cProc mulff
add ebx,ecx ; add exponent together
jo mul_exit
xor ecx,ecx
imul edx ; edx:eax = edx * eax
; the product of two mantissas is now in edx:eax. Since on entry
; to this function both mantissas were normalized the absolute value
; of this product is <= 7fffffff * 7fffffff = 3fffffff:00000001;
; The legal values of the most significant nibble in edx are 0, 1, 2, 3.
or edx,edx
jz mul_exit ; eax == 0 if (edx == 0), so it's
; safe to early out here
js mul_neg
shld ecx,edx,4 ; normalize the result
mov cl,mul_shift_table[ecx] ; find the shift count from the table
shld edx,eax,cl
shl eax,cl
; now do the rounding. We should add the most significant bit of eax to edx.
; Adding this extra bit to edx could result in the
; loss of normalization of edx which should then be restored by shr edx,1
; and adjusting the exponent accordingly.
add eax,80000000h
adc edx,0
js mul_restore_normalization
mul_store_result:
neg ecx
mov eax,edx ; return mantissa in eax
add ecx,ebx ; exponent = ebx - ecx
mul_exit:
cRet mulff
mul_restore_normalization:
shr edx,1 ; edx now normalized == 40000000h
dec ecx ; adjust exponent
jmp mul_store_result
; the result is negative:
mul_neg:
; We can't normalize a negative number. e.g. the shift count for 1110
; can be either 1 or 2.
neg eax ; negate edx:eax
adc edx,0
neg edx
shld ecx,edx,4 ; normalize the result
mov cl,mul_shift_table[ecx] ; find the shift count from the table
shld edx,eax,cl
shl eax,cl
; do the rounding as in the positive case
add eax,80000000h
adc edx,0
js mul_restore_normalization_neg
; all the information is now in edx, including least significant bit
mul_store_result_neg:
neg edx ; restore the sign
neg ecx ; finish off as in the positive case
add ecx,ebx ; exponent = ebx - ecx
mov eax,edx ; return mantissa in eax
cRet mulff
mul_restore_normalization_neg:
shr edx,1 ; edx now normalized == 40000000h
dec ecx ; adjust exponent
jmp mul_store_result_neg
endProc mulff
;---------------------------Public-Routine------------------------------;
; mulff_c ;
; ;
; Multiply two EFLOAT numbers together. The result overwrites the ;
; first multiplicand. ;
; ;
; Arguments: ;
; IN OUT pMult1 points to the first EFLOAT multiplicand. ;
; IN pMult2 points to the second EFLOAT multiplicand. ;
; Returns: ;
; EAX = 1 if success ;
; Error Returns: ;
; EAX = 0 if overflow ;
; Calls: ;
; mulff ;
; History: ;
; Thu 19-Mar-1992 17:44:34 -by- Charles Whitmer [chuckwh] ;
; Added 3 parameter entry point. This gives us better C++ code. ;
; ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
cProc mulff_c,8,< \
uses ebx, \
pMult1: ptr EFLOAT, \
pMult2: ptr EFLOAT >
mov ebx,pMult2
mov eax,[ebx].ef_lMant
mov ecx,[ebx].ef_lExp
mov ebx,pMult1
mov edx,[ebx].ef_lMant
mov ebx,[ebx].ef_lExp
cCall mulff
mov ebx,pMult1
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
mov eax,0
setno al
cRet mulff_c
endProc mulff_c
cProc mulff3_c,12,< \
uses ebx, \
pRes: ptr EFLOAT, \
pMult1: ptr EFLOAT, \
pMult2: ptr EFLOAT >
mov ebx,pMult2
mov eax,[ebx].ef_lMant
mov ecx,[ebx].ef_lExp
mov ebx,pMult1
mov edx,[ebx].ef_lMant
mov ebx,[ebx].ef_lExp
cCall mulff
mov ebx,pRes
jo short @F
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,ecx
@@: mov eax,ebx
cRet mulff3_c
endProc mulff3_c
;---------------------------Private-Routine-----------------------------;
; divff ;
; ;
; Divide an EFLOAT number by another. ;
; ;
; Entry: ;
; EDX = dividend's mantissa ;
; EBX = dividend's exponent ;
; EAX = divisor's mantissa, should never be 0 ;
; ECX = divisor's exponent ;
; Returns: ;
; EAX = quotient's mantissa ;
; ECX = quotient's exponent ;
; OF = 0 ;
; ZF = 1 if zero ;
; Error Returns: ;
; OF = 1 if overflow ;
; Registers Destroyed: ;
; EDX,EBX ;
; Calls: ;
; dNormalize ;
; History: ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
;
; cProc divff
;
; ;assert((eax != 0), "divff error: divide by 0")
;
; sub ebx,ecx ; subtract divisor's exp from dividend's
; jo divff_exit
;
; mov ecx,eax ; save divisor's mantissa
; xor eax,eax
; shrd eax,edx,2 ; prevent overflow, shift dividend
; sar edx,2 ; right by 2
;
; idiv ecx
; cCall dNormalize
; jz divff_exit ; eax = ecx = 0 if ZF = 1
;
; neg ecx
; inc ecx ; compensate for right shift by 2
; inc ecx
; add ecx,ebx ; add the exponent of the dividend
;
; divff_exit:
; cRet divff
; endProc divff
;
;---------------------------Public-Routine------------------------------;
; divff_c ;
; ;
; Divide an EFLOAT number by another. The result overwrites the ;
; dividend. ;
; ;
; Arguments: ;
; IN OUT pDvdend points to the EFLOAT dividend. ;
; IN pDvsor points to the EFLOAT divisor. ;
; Returns: ;
; EAX = 1 if success ;
; Error Returns: ;
; EAX = 0 if overflow or zero divisor ;
; Calls: ;
; divff ;
; History: ;
; Thu 19-Mar-1992 17:44:34 -by- Charles Whitmer [chuckwh] ;
; Added 3 parameter entry point. This gives us better C++ code. ;
; ;
; 14-Nov-1990 -by- Wendy Wu [wendywu] ;
; Wrote it ;
;-----------------------------------------------------------------------;
;
; cProc divff_c,8,< \
; uses ebx, \
; pDvdend: ptr EFLOAT, \
; pDvsor: ptr EFLOAT >
;
; mov ebx,pDvsor
; mov eax,[ebx].ef_lMant
; or eax,eax
; jz divff_error
;
; mov ecx,[ebx].ef_lExp
; mov ebx,pDvdend
; mov edx,[ebx].ef_lMant
; mov ebx,[ebx].ef_lExp
;
; cCall divff
;
; mov edx,pDvdend
; mov [edx].ef_lMant,eax
; mov [edx].ef_lExp,ecx
;
; mov eax,0
; setno al
;
; cRet divff_c
;
; divff_error:
; xor eax,eax
; cRet divff_c
;
; endProc divff_c
;
; cProc divff_old3_c,12,< \
; uses ebx, \
; pRes: ptr EFLOAT, \
; pDvdend: ptr EFLOAT, \
; pDvsor: ptr EFLOAT >
;
; mov ebx,pDvsor
; mov eax,[ebx].ef_lMant
; or eax,eax
; jz short divff_old3_error
;
; mov ecx,[ebx].ef_lExp
; mov ebx,pDvdend
; mov edx,[ebx].ef_lMant
; mov ebx,[ebx].ef_lExp
;
; cCall divff
;
; mov edx,pRes
; jo short @F
; mov [edx].ef_lMant,eax
; mov [edx].ef_lExp,ecx
; @@: mov eax,edx
; cRet divff_old3_c
;
; divff_old3_error:
; mov eax,pRes
; cRet divff_old3_c
; endProc divff_old3_c
;
;------------------------------Public-Routine------------------------------;
; divff3_c ;
; ;
; A newer concept for EFLOAT division. This makes maximal use of the ;
; assumption that the given numbers are normalized. I think the older ;
; method spends a lot of time in dNormalize for no reason. ;
; ;
; Fri 14-Jan-1994 -by- Bodin Dresevic [BodinD] ;
; update: added all the comments while trying to debug this routine ;
; ;
; Sun 22-Mar-1992 02:04:09 -by- Charles Whitmer [chuckwh] ;
; Wrote it. ;
;--------------------------------------------------------------------------;
cProc divff3_c,12,< \
uses ebx esi, \
pRes: ptr EFLOAT, \
pNum: ptr EFLOAT, \
pDenom: ptr EFLOAT >
mov ebx,pNum
mov esi,[ebx].ef_lExp
mov ecx,[ebx].ef_lMant
or ecx,ecx
jz short divff3_zero
mov eax,ecx
cdq
xor ecx,edx
sub ecx,edx ; ecx = |MantN|
mov ebx,pDenom
sub esi,[ebx].ef_lExp ; esi = ExpN - ExpD == expR
jo short divff3_error
mov eax,[ebx].ef_lMant
mov ebx,edx ; save sgn(MantN) into ebx
cdq
xor eax,edx
sub eax,edx ; eax = |MantD|
xor ebx,edx ; ebx = sgn(MantN) * sgn(MantD)
mov edx,ecx
mov ecx,eax
add ecx,ecx ; ecx = 2 * |MantD|
jz short divff3_error
xor eax,eax ; edx:eax = |MantN|:0, ecx = 2 * |MantD|
div ecx ; the result in eax, remainder in edx
shr ecx,1 ; ecx = |MantD|
cmp ecx,edx ; if remainder <= |MantD| need roundoff bit
sbb edx,edx
neg edx ; edx = roundoff bit
xor ecx,ecx ; ecx = 0, ecx will be used to store correction to ExpR
or eax,eax ; eax = unnormalized |MantR|, may need to clear sign bit
setns cl ; cl = 1 iff sign bit in eax NOT set
and edx,ecx ; fix roundoff bit
xor cl,1 ; cl = 1 iff sign bit in eax IS set
shr eax,cl ; normalize eax, will set CF iff least significant bit is set in eax before shr
adc eax,edx ; round the |MantR|, add CF if it is set
inc ecx ; because we divided by 2 * |MantD| above
add esi,ecx ; adjust expR
jo short divff3_error
xor eax,ebx ; restore the sign of MantR
sub eax,ebx ; eax = MantR
mov ebx,pRes
mov [ebx].ef_lMant,eax
mov [ebx].ef_lExp,esi
mov eax,ebx
cRet divff3_c
divff3_zero:
mov eax,pRes
mov [eax].ef_lMant,ecx
mov [eax].ef_lExp,ecx
cRet divff3_c
divff3_error:
if DBG
push offset MATH_DivideError
call DbgPrint
add esp,4
int 3
endif
mov eax,pRes
cRet divff3_c
endProc divff3_c
;------------------------------Public--Routine-----------------------------;
; void sqrtf2_c (pRes,pef) ;
; ;
; Takes the square root of an EFLOAT ;
; ;
; History: ;
; Thu 19-Mar-1992 17:38:58 -by- Charles Whitmer [chuckwh] ;
; Added result pointer. Removed 9 instructions from the calculation. ;
; Doesn't change return value on error. ;
; ;
; Tue 02-Apr-1991 11:45:09 -by- Kirk Olynyk [kirko] ;
; Uses Newton's method now. ;
; ;
; Mon 25-Mar-1991 14:08:47 -by- Kirk Olynyk [kirko] ;
; Uses a loop instead of IREPT ;
; ;
; Fri 01-Mar-1991 07:34:05 -by- Kirk Olynyk [kirko] ;
; Wrote it. ;
;--------------------------------------------------------------------------;
cProc sqrtf2_c,8,< \
uses esi edi ebx, \
pRes: ptr EFLOAT, \
pef: ptr EFLOAT >
mov esi,pef
mov edi,[esi].ef_lMant
mov ebx,[esi].ef_lExp ; EDI:EBX = mant,exp
; quick out for zero
mov esi,pRes ; ESI -> Result
or edi,edi
js short sqrtf_error
jz short sqrtf_early_out
; quick out for one
mov eax,ebx
mov edx,edi
sub eax,EXPONENT_OF_ONE
sub edx,MANTISSA_OF_ONE
or edx,eax
jz short sqrtf_early_out
; Calculate the exponent of the square root
sar ebx,1
sbb ecx,ecx ; ECX = (exp is odd) ? FFFFFFFFh : 0h
inc ebx
mov [esi].ef_lExp,ebx ; return the exponent
; The mantissa must be shifted. Calculate the shift factor
; CL = (exp is odd) ? 1 : 2
add cl,2
; Shift the mantissa, keep extra bits in ESI.
xor esi,esi ; esi = 0
shrd esi,edi,cl
shr edi,cl ; EDI = M / 2^shift_factor = P
; extract high and low word of the mantissa
shld ecx,edi,16 ; DI = lo word of P, CX = hi word of P
; recursion formula is x(n+1) = [x(n) + t/x(n)]/2, where we are trying to
; compute sqrt(t), the value below has empirically proven to be a good guess for
; x(0) in our range of results [bodind]
; form zero'th guess for square root
lea ebx,[ecx+4000h] ; BX:0000 = zeroth guess
; calculate 1'st guess to 16 bit accuracy
mov edx,ecx
mov eax,edi
div bx
add bx,ax
rcr bx,1 ; BX:0000 = 1'st guess
; calculate 2'nd guess to 16 bit accuracy
mov edx,ecx
mov eax,edi
div bx
add bx,ax
rcr bx,1 ; BX:0000 = 2'nd guess
; calculate 3'rd guess to 32 bit accuracy, division done with
; proper rounding [bodind]
shl ebx,16
mov edx,edi
mov eax,esi ; recover the lost bits
; do division with rounding, make sure that after adding 1/2 denom
; that carry bit if ehists is added properly to edx [bodind]
mov ecx,ebx
shr ecx,1
add eax,ecx
adc edx,0
div ebx
add ebx,eax
rcr ebx,1 ; EBX = 3'rd guess
mov esi,pRes ; ESI -> to the result
; done except that maybe our result is not normalized properly
or ebx,ebx ; too big a number, adjust mant and exp
jns short sqrtf_return_mantissa
shr ebx,1 ; divide mantissa by 2
inc dword ptr [esi].ef_lExp ; add one to the exponent
; return the mantissa
sqrtf_return_mantissa:
mov [esi].ef_lMant,ebx
sqrtf_error:
mov eax,esi
cRet sqrtf2_c
; Do early outs.
sqrtf_early_out:
mov [esi].ef_lMant,edi
mov [esi].ef_lExp,ebx
mov eax,esi
cRet sqrtf2_c
endProc sqrtf2_c
;------------------------------Public--Routine-----------------------------;
; VOID vEfToLfx(pefloat,plfx) ;
; ;
; Converts an EFLOAT to a 32.32 fix point number. ;
; ;
; Warning: ;
; No checks are made to see if the EFLOAT can fit. ;
; ;
; History: ;
; Tue 17-Mar-1992 00:32:48 -by- Charles Whitmer [chuckwh] ;
; Wrote it. ;
;--------------------------------------------------------------------------;
cProc vEfToLfx,8,< \
pefloat: ptr EFLOAT, \
plfx: ptr LARGE_INTEGER >
; Load the EFLOAT.
mov ecx,pefloat
mov eax,[ecx].ef_lMant
mov ecx,[ecx].ef_lExp
; Sign extend into EDX. The EFLOAT is now EDX.EAX * 2^ECX.
cdq
; Decide about shifting.
or ecx,ecx
jz short saveit
jl short shift_right
; Shift it left.
shld edx,eax,cl
shl eax,cl
saveit:
mov ecx,plfx
mov [ecx].li_LowPart,eax
mov [ecx].li_HighPart,edx
cRet vEfToLfx
; Shift it right.
shift_right:
neg ecx
shrd eax,edx,cl
sar edx,cl
mov ecx,plfx
mov [ecx].li_LowPart,eax
mov [ecx].li_HighPart,edx
cRet vEfToLfx
endProc vEfToLfx
end