836c97f0ac
git-svn-id: svn://kolibrios.org@553 a494cfbc-eb01-0410-851d-a64ba20cac60
454 lines
19 KiB
NASM
454 lines
19 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*4 math library.
|
|
;*
|
|
;*****************************************************************************
|
|
|
|
|
|
;
|
|
; inputs: EAX - operand 1 (high word, low word resp. ) (op1)
|
|
; EDX - operand 2 (op2)
|
|
;
|
|
; operations are performed as op1 (*) op2 where (*) is the selected
|
|
; operation
|
|
;
|
|
; output: EAX - result
|
|
;
|
|
; F4Add, F4Sub - written 28-apr-84
|
|
; - modified by A.Kasapi 15-may-84
|
|
; - to: Calculate sign of result
|
|
; - Guard bit in addition for extra accuracy
|
|
; Add documentation
|
|
; F4Mul - written 16-may-84
|
|
; - by Athos Kasapi
|
|
; F4DIV - written may-84 by "
|
|
;
|
|
include mdef.inc
|
|
include struct.inc
|
|
|
|
.287
|
|
modstart fsmth386
|
|
|
|
xref __8087 ; indicate that NDP instructions are present
|
|
|
|
xref __fdiv_m32
|
|
|
|
datasegment
|
|
extrn __real87 : byte ; cstart
|
|
extrn __chipbug : byte
|
|
fsadd dd _chkadd
|
|
fsmul dd _chkmul
|
|
fsdiv dd _chkdiv
|
|
enddata
|
|
|
|
|
|
xref F4DivZero ; Fstatus
|
|
xref F4OverFlow ; Fstatus
|
|
xref F4UnderFlow ; Fstatus
|
|
|
|
xdefp __FSA ; add real*4 to real*4
|
|
xdefp __FSS ; subtract real*4 from real*4
|
|
xdefp __FSM ; 4-byte real multiply
|
|
xdefp __FSD ; 4-byte real divide
|
|
|
|
|
|
defpe __FSS
|
|
or EDX,EDX ; if op2 is 0
|
|
je short ret_op1 ; then return operand 1
|
|
xor EDX,80000000h ; flip the sign of op2 and add
|
|
|
|
defpe __FSA
|
|
or EDX,EDX ; if op2 is 0
|
|
je short ret_op1 ; then return operand 1
|
|
or EAX,EAX ; if op1 is 0
|
|
_if e ; then
|
|
mov EAX,EDX ; - return operand 2
|
|
ret_op1: ret ; - return
|
|
_endif ; endif
|
|
jmp fsadd
|
|
|
|
__FSA87:
|
|
push EBP ; save EBP
|
|
mov EBP,ESP ; get access to stack
|
|
push EAX ; push operand 1
|
|
fld dword ptr -4[EBP]; load operand 1
|
|
push EDX ; push operand 2
|
|
fadd dword ptr -8[EBP]; add operand 2 to operand 1
|
|
_ret87:
|
|
fstp dword ptr -4[EBP]; store result
|
|
add ESP,4 ; clean up stack
|
|
fwait ; wait
|
|
pop EAX ; load result into EAX
|
|
cmp EAX,80000000H ; is result -0.0
|
|
_if e ; if it is then
|
|
xor EAX,EAX ; - make it positive
|
|
_endif ; endif
|
|
pop EBP ; restore EBP
|
|
ret ; return
|
|
|
|
__FSAemu:
|
|
push ECX ; save ECX
|
|
push EBX ; save EBX
|
|
;<> Scheme for calculating sign of result:
|
|
;<> The sign word is built and kept in CL
|
|
;<> Bits 0 and 1 hold the sum of the sign bits
|
|
;<> shifted out of op_1 and op_2
|
|
;<> Bit 2 holds the sign of the larger operand. It is assumed to be
|
|
;<> op_1 until op_2 is found larger
|
|
|
|
sub ECX,ECX ; clear ECX
|
|
_shl EAX,1 ; get sign of op1
|
|
_rcl CL,1 ;
|
|
mov CH,CL ;
|
|
_shl CL,1 ;
|
|
_shl CL,1 ;
|
|
add CL,CH ;
|
|
rol EAX,8 ; get exponent of op1 into AL
|
|
_shl EDX,1 ; get sign of op2
|
|
adc CL,0 ; place in CL
|
|
rol EDX,8 ; get exponent of op2 in DL
|
|
mov BL,AL ; get exponent of op1
|
|
mov BH,DL ; get exponent of op2
|
|
|
|
mov AL,0 ; zero rest of fraction
|
|
stc ; put implied 1 bit into top bit of
|
|
rcr EAX,1 ; ... fraction
|
|
mov DL,0 ; zero rest of fraction
|
|
stc ; put implied 1 bit into top bit
|
|
rcr EDX,1 ; ... of fraction
|
|
|
|
mov CH,BL ; assume op1 > op2
|
|
sub BL,BH ; calculate difference in exponents
|
|
_if ne ; if different
|
|
_if b ; - if op1 < op2
|
|
mov CH,BH ; - - get larger exponent for result
|
|
neg BL ; - - negate the shift count
|
|
xchg EAX,EDX ; - - flip operands
|
|
xor CL,4
|
|
|
|
;<> op_2 is larger, so its sign now occupies bit 2 of sign word. This
|
|
;<> information is only correct if the signs of op-1 and op-2 are different.
|
|
;<> Since we look past bit 1 for sign only if the signs are different, bit2
|
|
;<> will supply the correct information when it is needed. We get the sign of
|
|
;<> op_2 by flipping the sign of op_1, already in bit 2
|
|
|
|
_endif ; - endif
|
|
xchg CL,BL ; - get shift count
|
|
cmp CL,32 ; - if count >= 32
|
|
_if ge ; - then
|
|
sub EDX,EDX ; - - answer is 0
|
|
_else ; - else
|
|
shr EDX,CL ; - - align fraction
|
|
_endif ; - endif
|
|
xchg CL,BL ; - put back
|
|
_endif ; endif
|
|
shr CL,1 ; get bit 0 of sign word - value is 0 if
|
|
; both operands have same sign, 1 if not
|
|
_if nc ; if signs are the same
|
|
add EAX,EDX ; - add the fractions
|
|
_if c ; - if carry
|
|
rcr EAX,1 ; - - shift fraction right 1 bit
|
|
inc CH ; - - increment exponent
|
|
_if z ; - - if we overflowed
|
|
ror CL,1 ; - - - set sign of infinity
|
|
rcr EAX,1 ; - - - . . .
|
|
jmp short add_oflow;- - - handle overflow
|
|
_endif ; - - endif
|
|
_endif ; - endif
|
|
_else ; else (signs are different)
|
|
shr CL,1 ; - skip junk bit
|
|
sub EAX,EDX ; - subtract the fractions
|
|
_guess ; - guess
|
|
_quif nc ; - - quit if no borrow
|
|
inc CL ; - - sign := sign of op_2
|
|
neg EAX ; - - negate the fraction
|
|
_admit ; - admit
|
|
or EAX,EAX ; - - quit if answer is not 0
|
|
_quif ne ; - - . . .
|
|
pop EBX ; - - restore EBX
|
|
pop ECX ; - - restore ECX
|
|
ret ; - - return (answer is 0)
|
|
_endguess ; - endguess
|
|
_endif ; endif
|
|
|
|
; normalize the fraction
|
|
add EAX,00000080h ; round up fraction if required
|
|
mov AL,0 ; zero bottom 8 bits 10-jul-89
|
|
_guess underflow ; guess
|
|
_quif nc ; - quit if round up didn't overflow frac
|
|
inc CH ; - adjust exponent
|
|
_admit ; admit
|
|
_loop ; - loop (shift until high bit appears)
|
|
_shl EAX,1 ; - - shift fraction left
|
|
_quif c,underflow ; - - quit if carry has appeared
|
|
dec CH ; - - decrement exponent
|
|
_until e ; - until underflow
|
|
jmp short add_uflow ; - handle underflow
|
|
_endguess ; endguess
|
|
mov AL,CH ; get exponent
|
|
ror EAX,8 ; rotate into position
|
|
ror CL,1 ; get sign bit
|
|
rcr EAX,1 ; shift it into result
|
|
pop EBX ; restore EBX
|
|
pop ECX ; restore ECX
|
|
ret ; return
|
|
|
|
add_uflow: ; handle underflow
|
|
pop EBX ; restore EBX
|
|
pop ECX ; restore ECX
|
|
jmp F4UnderFlow ; goto underflow routine
|
|
|
|
add_oflow: ; handle overflow
|
|
pop EBX ; restore EBX
|
|
pop ECX ; restore ECX
|
|
jmp F4OverFlow ; handle overflow
|
|
endproc __FSA
|
|
endproc __FSS
|
|
|
|
;=====================================================================
|
|
|
|
defpe __FSM
|
|
|
|
;<> 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
|
|
|
|
_guess ; guess: answer not 0
|
|
or EAX,EAX ; - see if first arg is zero
|
|
_quif e ; - quit if op1 is 0
|
|
or EDX,EDX ; - quit if op2 is 0
|
|
_quif e ; - . . .
|
|
jmp fsmul ; - perform multiplication
|
|
_endguess ; endguess
|
|
sub EAX,EAX ; set answer to 0
|
|
ret ; return
|
|
|
|
__FSM87:
|
|
push EBP ; save EBP
|
|
mov EBP,ESP ; get access to stack
|
|
push EAX ; push operand 1
|
|
fld dword ptr -4[EBP]; load operand 1
|
|
push EDX ; push operand 2
|
|
fmul dword ptr -8[EBP]; mulitply operand 1 by operand 2
|
|
jmp _ret87 ; goto common epilogue
|
|
|
|
__FSMemu:
|
|
push ECX ; save ECX
|
|
_shl EAX,1 ; get sign of op1
|
|
_rcl ECX,1 ; save it
|
|
_shl EDX,1 ; get sign of op2
|
|
adc ECX,0 ; calc sign of result
|
|
ror ECX,1 ; move to the top
|
|
rol EAX,8 ; move exponent of op1 into AL
|
|
rol EDX,8 ; move exponent of op2 into DL
|
|
sub AL,7Fh ; remove bias from exponents
|
|
sub DL,7Fh ; . . .
|
|
add DL,AL ; add exponents
|
|
_if o ; if over or underflow
|
|
js short mul_oflow ; - report overflow if signed
|
|
jmp short mul_uflow ; - handle underflow
|
|
_endif ; endif
|
|
cmp DL,81h ; check for underflow
|
|
jle short mul_uflow ; quit if underflow
|
|
add DL,7fh+1 ; bias exponent
|
|
mov CL,DL ; save exponent
|
|
mov AL,0 ; zero rest of fraction
|
|
mov DL,0 ; ...
|
|
stc ; turn on implied 1 bit in fraction
|
|
rcr EAX,1 ; ...
|
|
stc ; turn on implied 1 bit in fraction
|
|
rcr EDX,1 ; ...
|
|
mul EDX ; calc fraction
|
|
or EDX,EDX ; check top bit
|
|
_if ns ; if not set
|
|
_shl EDX,1 ; - move left 1
|
|
dec CL ; - decrement exponent
|
|
_endif ; endif
|
|
sar EDX,8 ; place fraction in correct location
|
|
adc EDX,0 ; round up
|
|
adc CL,0 ; increment exponent if necessary
|
|
jz short mul_oflow ; report overflow if required
|
|
shl EDX,9 ; get rid of implied 1 bit
|
|
mov DL,CL ; get exponent
|
|
ror EDX,8 ; rotate into position except for sign
|
|
_shl ECX,1 ; get sign
|
|
rcr EDX,1 ; place sign in result
|
|
mov EAX,EDX ; place in correct register
|
|
pop ECX ; restore ECX
|
|
ret ; return
|
|
|
|
mul_uflow: ; underflow
|
|
pop ECX ; restore ECX
|
|
jmp F4UnderFlow ; . . .
|
|
|
|
mul_oflow: ; overflow
|
|
mov EAX,ECX ; get sign
|
|
pop ECX ; restore ECX
|
|
jmp F4OverFlow ; report overflow
|
|
endproc __FSM
|
|
|
|
;====================================================================
|
|
|
|
defpe __FSD
|
|
jmp fsdiv
|
|
|
|
__FSDbad_div:
|
|
push EBP ; save EBP
|
|
mov EBP,ESP ; get access to stack
|
|
push EAX ; push operand 1
|
|
fld dword ptr -4[EBP]; load operand 1
|
|
push EDX ; push operand 2
|
|
call __fdiv_m32 ; divide operand 1 by operand 2
|
|
push EDX ; __fdiv_m32 popped operand 2, _ret87 wants it
|
|
jmp _ret87 ; goto common epilogue
|
|
|
|
__FSD87:
|
|
push EBP ; save EBP
|
|
mov EBP,ESP ; get access to stack
|
|
push EAX ; push operand 1
|
|
fld dword ptr -4[EBP]; load operand 1
|
|
push EDX ; push operand 2
|
|
fdiv dword ptr -8[EBP]; divide operand 1 by operand 2
|
|
jmp _ret87 ; goto common epilogue
|
|
|
|
|
|
__FSDemu:
|
|
_shl EDX,1 ; shift sign of divisor into carry
|
|
_if e ; if divisor is zero
|
|
jmp F4DivZero ; - handle divide by zero
|
|
_endif ; endif
|
|
push ECX ; save ECX
|
|
_rcl ECX,1 ; save sign in ECX
|
|
_shl EAX,1 ; shift sign of dividend into carry
|
|
_if e ; if dividend is 0, then
|
|
pop ECX ; - restore ECX
|
|
ret ; - return
|
|
_endif ; endif
|
|
adc ECX,0 ; now calculate save sign of result in ECX
|
|
ror ECX,1 ; rotate sign to top
|
|
rol EAX,8 ; get exponent into AL
|
|
rol EDX,8 ; get exponent into DL
|
|
sub AL,7Fh ; calculate exponent of result
|
|
sub DL,7Fh ; . . .
|
|
sub AL,DL ; . . .
|
|
_if o ; if over or underflow
|
|
jns short div_uflow ; - handle underflow
|
|
_shl ECX,1 ; - get sign of infinity
|
|
rcr EAX,1 ; - . . .
|
|
jmp short div_oflow ; - handle overflow
|
|
_endif ; endif
|
|
cmp AL,81h ; check for underflow
|
|
jle short div_uflow ; . . .
|
|
add AL,7Fh ; restore bias to exponent
|
|
mov CH,AL ; save calculated exponent
|
|
mov AL,0 ; zero bottom of fraction
|
|
mov DL,0 ; ...
|
|
stc ; rotate implied '1'bit back into divisor
|
|
rcr EDX,1 ; . . .
|
|
stc ; rotate implied '1' bit into dividend
|
|
rcr EAX,1 ; . . .
|
|
push ECX ; save sign and exponent
|
|
mov ECX,EDX ; save divisor
|
|
mov EDX,EAX ; place dividend into EDX
|
|
sub EAX,EAX ; set rest to 0
|
|
shr EDX,1 ; so we don't get a divide overflow trap
|
|
div ECX ; do the divide
|
|
pop ECX ; restore sign and exponent
|
|
or EAX,EAX ; check top bit
|
|
_if ns ; if not set
|
|
_shl EAX,1 ; - move left 1
|
|
dec CH ; - decrement exponent
|
|
_endif ; endif
|
|
sar EAX,8 ; place fraction in correct location
|
|
adc EAX,0 ; round up
|
|
_guess ; guess have to inc exponent
|
|
_quif nc ; - quit if no carry
|
|
inc CH ; - increment exponent
|
|
_quif nz ; - quit if no overflow
|
|
mov EAX,ECX ; - get sign of infinity
|
|
jmp short div_oflow ; - handle overflow
|
|
_endguess ; endguess
|
|
shl EAX,9 ; get rid of implied 1 bit
|
|
mov AL,CH ; get exponent
|
|
ror EAX,8 ; rotate into position except for sign
|
|
_shl ECX,1 ; get sign
|
|
rcr EAX,1 ; place sign in result
|
|
pop ECX ; restore ECX
|
|
ret ; return to caller
|
|
|
|
div_uflow: ; handle underflow
|
|
pop ECX ; restore ECX
|
|
jmp F4UnderFlow ; handle underflow
|
|
|
|
|
|
div_oflow: ; handle overflow
|
|
pop ECX ; restore ECX
|
|
jmp F4OverFlow ; handle overflow
|
|
endproc __FSD
|
|
|
|
|
|
_chkadd: call _chk8087
|
|
jmp fsadd
|
|
|
|
_chkmul: call _chk8087
|
|
jmp fsmul
|
|
|
|
_chkdiv: call _chk8087
|
|
jmp fsdiv
|
|
|
|
|
|
_chk8087 proc near
|
|
push eax ; save AX
|
|
cmp byte ptr __real87,0 ; if real 80x87 NDP present
|
|
_if ne ; then
|
|
mov eax,offset __FSA87 ; - get addr of add rtn
|
|
mov fsadd,eax ; - ...
|
|
mov eax,offset __FSM87 ; - get addr of mul rtn
|
|
mov fsmul,eax ; - ...
|
|
test byte ptr __chipbug, 1 ; - if we've got a bad divider
|
|
_if ne ; - then
|
|
mov eax,offset __FSDbad_div ; - - get addr of div rtn
|
|
_else ; - else
|
|
mov eax,offset __FSD87 ; - - get addr of div rtn
|
|
_endif ; - endif
|
|
mov fsdiv,eax ; - ...
|
|
_else ; else
|
|
mov eax,offset __FSAemu ; - get addr of add rtn
|
|
mov fsadd,eax ; - ...
|
|
mov eax,offset __FSMemu ; - get addr of mul rtn
|
|
mov fsmul,eax ; - ...
|
|
mov eax,offset __FSDemu ; - get addr of div rtn
|
|
mov fsdiv,eax ; - ...
|
|
_endif ; endif
|
|
pop eax ; restore AX
|
|
ret ; return
|
|
endproc _chk8087
|
|
|
|
endmod
|
|
end
|