forked from KolibriOS/kolibrios
836c97f0ac
git-svn-id: svn://kolibrios.org@553 a494cfbc-eb01-0410-851d-a64ba20cac60
485 lines
22 KiB
NASM
485 lines
22 KiB
NASM
;*****************************************************************************
|
|
;*
|
|
;* Open Watcom Project
|
|
;*
|
|
;* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
|
;*
|
|
;* ========================================================================
|
|
;*
|
|
;* This file contains Original Code and/or Modifications of Original
|
|
;* Code as defined in and that are subject to the Sybase Open Watcom
|
|
;* Public License version 1.0 (the 'License'). You may not use this file
|
|
;* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
|
;* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
|
;* provided with the Original Code and Modifications, and is also
|
|
;* available at www.sybase.com/developer/opensource.
|
|
;*
|
|
;* The Original Code and all software distributed under the License are
|
|
;* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
;* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
|
;* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
|
;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
|
;* NON-INFRINGEMENT. Please see the License for the specific language
|
|
;* governing rights and limitations under the License.
|
|
;*
|
|
;* ========================================================================
|
|
;*
|
|
;* Description: REAL*8 math library.
|
|
;*
|
|
;*****************************************************************************
|
|
|
|
|
|
;
|
|
; inputs: EDX,EAX - operand 1 (high word, low word resp. ) (op1)
|
|
; ECX,EBX - operand 2 (op2)
|
|
;
|
|
; operations are performed as op1 (*) op2 where (*) is the selected
|
|
; operation
|
|
;
|
|
; output: EDX,EAX - result (high word, low word resp. )
|
|
;
|
|
;
|
|
include mdef.inc
|
|
include struct.inc
|
|
|
|
.8087
|
|
modstart fdmth386
|
|
|
|
xref __8087 ; indicate that NDP instructions are present
|
|
|
|
datasegment
|
|
extrn __real87 : byte ; 8087.asm
|
|
enddata
|
|
|
|
xref F8DivZero ; Fstatus
|
|
xref F8OverFlow ; Fstatus
|
|
xref F8UnderFlow ; Fstatus
|
|
|
|
xdefp __FDA ; add real*8 to real*8
|
|
xdefp __FDS ; subtract real*8 from real*8
|
|
xdefp __FDM ; 8-byte real multiply
|
|
|
|
|
|
defpe __FDS
|
|
xor ECX,80000000h ; flip the sign of op2 and add
|
|
|
|
defpe __FDA
|
|
or EBX,EBX ; if low word of op2 is 0
|
|
_if e ; then
|
|
_shl ECX,1 ; - place sign in carry
|
|
je ret_op1 ; - if op2 is 0 then return operand 1
|
|
rcr ECX,1 ; - put sign back
|
|
_endif ; endif
|
|
or EAX,EAX ; if op1 is 0
|
|
_if e ; then
|
|
_shl EDX,1 ; - place sign in carry
|
|
_if e ; - if op1 really is 0
|
|
mov EDX,ECX ; - - return operand 2
|
|
mov EAX,EBX ; - - . . .
|
|
ret_op1: ret ; - - return
|
|
_endif ; - endif
|
|
rcr EDX,1 ; - put sign back
|
|
_endif ; endif
|
|
|
|
cmp byte ptr __real87,0; if 8087 not to be used
|
|
je short __FDAemu ; then emulate
|
|
|
|
__FDA87:
|
|
push EDX ; push operand 1
|
|
push EAX ; . . .
|
|
fld qword ptr [ESP] ; load operand 1
|
|
push ECX ; push operand 2
|
|
push EBX ; . . .
|
|
fadd qword ptr [ESP] ; add operand 2 to operand 1
|
|
_ret87:
|
|
fstp qword ptr 8[ESP]; store result
|
|
add ESP,8 ; clean up stack
|
|
fwait ; wait
|
|
pop EAX ; load result into EDX:EAX
|
|
pop EDX ; . . .
|
|
cmp EDX,80000000H ; is it a negative zero?
|
|
_if e ; if it is then
|
|
sub EDX,EDX ; - make it positive 0.0
|
|
mov EAX,EDX ; - ...
|
|
_endif ; endif
|
|
ret ; return
|
|
|
|
__FDAemu:
|
|
push EBP ; save EBP
|
|
push EDI ; save EDI
|
|
push ESI ; save EDI
|
|
mov EDI,EDX ; get high part of op1
|
|
mov ESI,ECX ; get high part of op2
|
|
sar EDI,20 ; shift exponent to bottom, duplicating sign
|
|
sar ECX,20 ; shift exponent to bottom, duplicating sign
|
|
and EDI,0800007FFh ; isolate signs and exponent
|
|
and ECX,0800007FFh ; ...
|
|
mov EBP,ECX ; assume op1 < op2
|
|
rol EDI,16 ; rotate signs to bottom
|
|
rol ECX,16 ; ...
|
|
add CX,DI ; calc sign of result
|
|
rol EDI,16 ; rotate signs to top
|
|
rol ECX,16 ; ...
|
|
and EDX,000FFFFFh ; isolate fraction
|
|
and ESI,000FFFFFh ; isolate fraction
|
|
or DI,DI ; if op1 is not a denormal
|
|
_if ne ; then
|
|
or EDX,00100000h ; - turn on implied 1 bit
|
|
_endif ; endif
|
|
or CX,CX ; if op2 is not a denormal
|
|
_if ne ; then
|
|
or ESI,00100000h ; - turn on implied 1 bit
|
|
_endif ; endif
|
|
_shl EAX,1 ; shift left 1 to make room for guard bit
|
|
_rcl EDX,1 ; ...
|
|
_shl EBX,1 ; ...
|
|
_rcl ESI,1 ; ...
|
|
sub CX,DI ; calculate difference in exponents
|
|
_if ne ; if different
|
|
_if b ; - if op1 < op2
|
|
mov EBP,EDI ; - - get larger exponent for result
|
|
neg CX ; - - negate the shift count
|
|
xchg EAX,EBX ; - - flip operands
|
|
xchg EDX,ESI ; - - . . .
|
|
_endif ; - endif
|
|
cmp CX,53+1 ; - if shift count too big
|
|
_if a ; - then, return operand 1
|
|
mov EDX,ESI ; - - get value in correct registers
|
|
mov EAX,EBX ; - - . . .
|
|
_shl EBP,1 ; - - get sign
|
|
rcr EDX,1 ; - - rebuild operand 1
|
|
rcr EAX,1 ; - - ...
|
|
and EDX,800FFFFFh ; - - ...
|
|
ror EBP,13 ; - - rotate exponent into position
|
|
and EBP,7FF00000h ; - - ...
|
|
or EDX,EBP ; - - put in exponent
|
|
pop ESI ; - - restore ESI
|
|
pop EDI ; - - restore EDI
|
|
pop EBP ; - - restore EBP
|
|
ret ; - - return
|
|
_endif ; - endif
|
|
_endif ; endif
|
|
or ECX,ECX ; get bit 0 of sign word - value is 0 if
|
|
; both operands have same sign, 1 if not
|
|
_if s ; if signs are different
|
|
neg ESI ; - negate the fraction of op2
|
|
neg EBX ; - . . .
|
|
sbb ESI,0 ; - . . .
|
|
xor EBP,80000000h ; - flip sign
|
|
_endif ; endif
|
|
sub EDI,EDI ; get a zero for sticky bits
|
|
cmp CL,0 ; if shifting required
|
|
_if ne ; then
|
|
push EBX ; - save EBX
|
|
sub EBX,EBX ; - for zero fill
|
|
cmp CL,32 ; - if shift count >= 32
|
|
_if ae ; - then
|
|
or EAX,EAX ; - - check low order word for 1 bits
|
|
setne BL ; - - BL=1 if EAX non zero
|
|
mov EDI,EBX ; - - save sticky bits
|
|
sub EBX,EBX ; - - for zero fill
|
|
mov EAX,EDX ; - - shift right 32
|
|
sub EDX,EDX ; - - zero high word
|
|
;;; sub CL,32 ; - - adjust shift count
|
|
_endif ; - endif
|
|
shrd EBX,EAX,CL ; - get the extra sticky bits
|
|
or EDI,EBX ; - save them
|
|
sub EBX,EBX ; - for zero fill
|
|
shrd EAX,EDX,CL ; - align the fractions
|
|
shrd EDX,EBX,CL ; - ...
|
|
pop EBX ; - restore EBX
|
|
_endif ; endif
|
|
|
|
add EAX,EBX ; add the fractions
|
|
adc EDX,ESI ; . . .
|
|
_if s ; if answer is negative
|
|
cmp CL,53 ; - if shift count >= 53
|
|
_if ae ; - then
|
|
test EDI,7FFFFFFFh ; - - check the sticky bits
|
|
setne BL ; - - make single sticky bit
|
|
shr EBX,1 ; - - carry set if sticky=1
|
|
adc EAX,0 ; - - round up fraction if required
|
|
adc EDX,0 ; - - . . .
|
|
_endif ; - endif
|
|
neg EDX ; - negate the fraction
|
|
neg EAX ; - . . .
|
|
sbb EDX,0 ; - . . .
|
|
xor EBP,80000000h ; - flip the sign
|
|
_endif ; endif
|
|
mov EBX,EAX ; get result
|
|
or EBX,EDX ; if not zero
|
|
_if ne ; then
|
|
or BP,BP ; - if exponent is 0
|
|
je short denormal ; - denormal when exponent hits 0
|
|
_loop ; - loop (normalize)
|
|
test EDX,7FE00000h ; - - stop when bit slides into exponent field
|
|
_quif ne ; - - ...
|
|
dec BP ; - - decrement exponent
|
|
je short denormal; - - denormal when exponent hits 0
|
|
_shl EAX,1 ; - - shift fraction left one bit
|
|
_rcl EDX,1 ; - - ...
|
|
_endloop ; - endloop
|
|
test EDX,00400000h ; - if we got a carry
|
|
_if ne ; - then
|
|
shr EDX,1 ; - - shift fraction right 1
|
|
rcr EAX,1 ; - - ...
|
|
adc EDI,0 ; - - keep sticky bit
|
|
inc BP ; - - increment exponent
|
|
cmp BP,07FFh ; - - quit if overflow
|
|
je add_oflow ; - - . . .
|
|
_endif ; - endif
|
|
; normalize the fraction
|
|
shr EDX,1 ; - get guard bit
|
|
rcr EAX,1 ; - ...
|
|
_if c ; - if guard bit is on
|
|
or EDI,EDI ; - - check the sticky bits
|
|
setne BL ; - - make single sticky bit
|
|
or EBX,EAX ; - - or sticky bit with bottom bit
|
|
shr EBX,1 ; - - carry set if sticky=1 or bottom=1
|
|
adc EAX,0 ; - - round up fraction if required
|
|
adc EDX,0 ; - - . . .
|
|
test EDX,00200000h ; - - if we got a carry (02-nov-90)
|
|
_if ne ; - - then
|
|
shr EDX,1 ; - - - shift fraction right 1
|
|
rcr EAX,1 ; - - - ...
|
|
inc BP ; - - - increment exponent
|
|
cmp BP,07FFh ; - - - quit if overflow
|
|
je add_oflow ; - - - . . .
|
|
_endif ; - - endif
|
|
_endif ; - endif
|
|
and EDX,000FFFFFh ; - get rid of implied 1 bit
|
|
mov ECX,EBP ; - get sign
|
|
shl EBP,21 ; - shift exponent to top
|
|
_shl ECX,1 ; - get sign
|
|
rcr EBP,1 ; - put it in
|
|
or EDX,EBP ; - put exponent and sign into result
|
|
_endif ; endif
|
|
pop ESI ; restore ESI
|
|
pop EDI ; restore EDI
|
|
pop EBP ; restore EBP
|
|
ret ; return
|
|
|
|
denormal: ; handle denormal
|
|
_shl EBP,1 ; get sign
|
|
rcr EDX,1 ; put it in result
|
|
rcr EAX,1 ; ...
|
|
pop ESI ; restore ESI
|
|
pop EDI ; restore EDI
|
|
pop EBP ; restore EBP
|
|
ret ; return
|
|
|
|
add_oflow: ; handle overflow
|
|
mov EAX,EBP ; get proper sign for infinity
|
|
pop ESI ; restore ESI
|
|
pop EDI ; restore EDI
|
|
pop EBP ; restore EBP
|
|
jmp F8OverFlow ; handle overflow
|
|
endproc __FDA
|
|
endproc __FDS
|
|
;=====================================================================
|
|
;<> multiplies X by Y and places result in C.
|
|
;<> X2 and X1 represent the high and low words of X. Similarly for Y and C
|
|
;<> Special care is taken to use only six registers, so the code is a bit
|
|
;<> obscure
|
|
|
|
defpe __FDM
|
|
_guess ; guess: one of the operands is 0
|
|
or EAX,EAX ; - see if first arg is zero
|
|
_quif ne ; - quit if op1 is not 0
|
|
_shl EDX,1 ; - place sign in carry
|
|
_if e ; - if operand one is 0
|
|
ret ; - - return
|
|
_endif ; - endif
|
|
rcr EDX,1 ; - restore sign
|
|
_endguess ; endguess
|
|
_guess ; guess: op2 is 0
|
|
or EBX,EBX ; - quit if op2 is not 0
|
|
_quif ne ; - . . .
|
|
_shl ECX,1 ; - place sign in carry
|
|
_if e ; - if operand 2 is 0
|
|
sub EAX,EAX ; - - set result to 0
|
|
sub EDX,EDX ; - - . . .
|
|
ret ; - - return
|
|
_endif ; - endif
|
|
rcr ECX,1 ; - restore sign of op2
|
|
_endguess ; endguess
|
|
|
|
cmp byte ptr __real87,0; if 8087 not to be used
|
|
je short __FDMemu ; then emulate
|
|
|
|
__FDM87:
|
|
push EDX ; push operand 1
|
|
push EAX ; . . .
|
|
fld qword ptr [ESP] ; load operand 1
|
|
push ECX ; push operand 2
|
|
push EBX ; . . .
|
|
fmul qword ptr [ESP] ; multiply operand 1 by operand 2
|
|
jmp _ret87 ; goto common epilogue
|
|
|
|
__FDMemu:
|
|
push EBP ; save EBP
|
|
push EDI ; save EDI
|
|
push ESI ; save EDI
|
|
mov EDI,EDX ; get high part of op1
|
|
mov ESI,ECX ; get high part of op2
|
|
sar EDI,20 ; shift exponent to bottom, duplicating sign
|
|
sar ECX,20 ; shift exponent to bottom, duplicating sign
|
|
and EDI,0800007FFh ; isolate signs and exponent
|
|
and ECX,0800007FFh ; ...
|
|
rol EDI,16 ; rotate signs to bottom
|
|
rol ECX,16 ; ...
|
|
add CX,DI ; calc sign of result
|
|
rol EDI,16 ; rotate signs to top
|
|
rol ECX,16 ; ...
|
|
and EDX,000FFFFFh ; isolate fraction
|
|
and ESI,000FFFFFh ; isolate fraction
|
|
or DI,DI ; if op1 is a denormal
|
|
_if e ; then
|
|
inc DI ; - adjust exponent by 1
|
|
_loop ; - loop (normalize it) 27-jul-90
|
|
dec DI ; - - decrement exponent
|
|
_shl EAX,1 ; - - shift left 1
|
|
_rcl EDX,1 ; - - ...
|
|
test EDX,00100000h ; - - check for implied 1 bit
|
|
_until ne ; - until normalized
|
|
_endif ; endif
|
|
or EDX,00100000h ; turn on implied 1 bit
|
|
or CX,CX ; if op2 is a denormal
|
|
_if e ; then
|
|
inc CX ; - adjust exponent by 1
|
|
_loop ; - loop (normalize it) 27-jul-90
|
|
dec CX ; - - decrement exponent
|
|
_shl EBX,1 ; - - shift left 1
|
|
_rcl ESI,1 ; - - ...
|
|
test ESI,00100000h ; - - check for implied 1 bit
|
|
_until ne ; - until normalized
|
|
_endif ; endif
|
|
or ESI,00100000h ; turn on implied 1 bit
|
|
|
|
_guess ; guess: overflow
|
|
add CX,DI ; - determine exponent of result
|
|
sub CX,03ffh ; - remove extra bias
|
|
_quif s ; - quit if exponent is negative
|
|
cmp CX,07FFh ; - quit if not overflow
|
|
_quif b ; - . . .
|
|
mov EAX,ECX ; - put sign into EAX
|
|
pop ESI ; - restore ESI
|
|
pop EDI ; - restore EDI
|
|
pop EBP ; - restore EBP
|
|
jmp F8OverFlow ; - handle overflow
|
|
_endguess ; endguess
|
|
cmp CX,-53 ; if exponent is too small
|
|
_if l ; then underflow
|
|
pop ESI ; - restore ESI
|
|
pop EDI ; - restore EDI
|
|
pop EBP ; - restore EBP
|
|
jmp F8UnderFlow ; - handle underflow
|
|
_endif ; endif
|
|
push ECX ; save sign and exponent
|
|
mov CL,11 ; shift fractions to top of registers
|
|
shld EDX,EAX,CL ; ...
|
|
shld EAX,EBP,CL ; ...
|
|
and EAX,0FFFFF800h ; ...
|
|
shld ESI,EBX,CL ; ...
|
|
shld EBX,EBP,CL ; ...
|
|
and EBX,0FFFFF800h ; ...
|
|
|
|
sub EBP,EBP ; zero EBP
|
|
push ESI ; save high part of op2
|
|
push EDX ; save high part of op1
|
|
push EAX ; save low part of op1
|
|
mul EBX ; low part of op1 * low part of op2
|
|
xchg EAX,ESI ; ESI becomes start of the sticky bits
|
|
mov ECX,EDX ; save high part of result
|
|
pop EDX ; restore low part of op1
|
|
mul EDX ; low part of op1 * high part of op2
|
|
mov EDI,EDX ; save high part of product
|
|
add ECX,EAX ; add partial product
|
|
adc EDI,EBP ; ...
|
|
adc EBP,EBP ; ...
|
|
pop EAX ; restore high part of op1
|
|
xchg EAX,EBX ; flip with low part of op2
|
|
mul EBX ; high part of op1 * low part of op2
|
|
add ECX,EAX ; add partial product
|
|
adc EDI,EDX ; ...
|
|
adc EBP,0 ; ...
|
|
mov EAX,EBX ; get high part of op1
|
|
pop EDX ; restore high part of op2
|
|
mul EDX ; high part of op1 * high part of op2
|
|
add EAX,EDI ; add partial product
|
|
adc EDX,EBP ; ...
|
|
sub EBX,EBX ; get zero for zero fill
|
|
mov CL,10 ; shift result over
|
|
shrd EBX,EAX,CL ; ... get sticky bits 18-feb-91
|
|
shrd EAX,EDX,CL ; ...
|
|
shrd EDX,EBX,CL ; ...
|
|
pop ECX ; restore sign and exponent
|
|
|
|
_loop ; loop
|
|
test EDX,00200000h ; - test to see if bit in exponent field
|
|
_quif e ; - quit if not
|
|
shr EDX,1 ; - shift result right
|
|
rcr EAX,1 ; - . . .
|
|
rcr EBX,1 ; - save carry
|
|
inc CX ; - inc exponent for every shift
|
|
cmp CX,07FFh ; - quit if overflow
|
|
je mul_oflow ; - . . .
|
|
_endloop ; endloop
|
|
_shl EBX,1 ; get guard bit
|
|
_if c ; if set
|
|
_if e ; - if rest of sticky bits are 0
|
|
or ESI,ESI ; - - check the bottom sticky bits
|
|
setne BL ; - - ...
|
|
shr EBX,1 ; - - if all sticky bits are zero
|
|
_if nc ; - - then
|
|
mov ESI,EAX ; - - - get bottom bit of result
|
|
shr ESI,1 ; - - - as rounding bit
|
|
_endif ; - - endif
|
|
_endif ; - endif
|
|
adc EAX,0 ; - round up
|
|
adc EDX,0 ; - ...
|
|
test EDX,00200000h ; - test to see if bit in exponent field
|
|
_if ne ; - if fraction overflowed
|
|
shr EDX,1 ; - - shift right
|
|
rcr EAX,1 ; - - ...
|
|
inc CX ; - - increment exponent
|
|
cmp CX,07FFh ; - - quit if overflow
|
|
je mul_oflow ; - - . . .
|
|
_endif ; - endif
|
|
_endif ; endif
|
|
or CX,CX ; if exponent <= 0
|
|
_if le ; then (denormal result)
|
|
_if e ; - if exponent = 0
|
|
mov CL,1 ; - - set shift count to 1
|
|
_else ; - else
|
|
neg CX ; - - negate to get shift count
|
|
dec CX ; - - adjust
|
|
_endif ; - endif
|
|
sub EBX,EBX ; - for zero fill
|
|
shrd EAX,EDX,CL ; - align the fraction
|
|
shrd EDX,EBX,CL ; - ...
|
|
sub CX,CX ; - set exponent to 0
|
|
_endif ; endif
|
|
and EDX,000FFFFFh ; isolate fraction
|
|
mov ESI,ECX ; get copy of sign
|
|
ror ECX,11 ; get exponent
|
|
_shl ESI,1 ; get sign
|
|
rcr ECX,1 ; put it in
|
|
and ECX,0FFF00000h ; isolate sign and exponent
|
|
or EDX,ECX ; place it in result
|
|
pop ESI ; restore ESI
|
|
pop EDI ; restore EDI
|
|
pop EBP ; restore EBP
|
|
ret ; return
|
|
|
|
mul_oflow: ; overflow
|
|
mov EAX,ECX ; get sign of infinity
|
|
pop ESI ; restore ESI
|
|
pop EDI ; restore EDI
|
|
pop EBP ; restore EBP
|
|
jmp F8OverFlow ; handle overflow
|
|
endproc __FDM
|
|
|
|
endmod
|
|
end
|