917 lines
24 KiB
NASM
917 lines
24 KiB
NASM
|
page ,132
|
|||
|
;---------------------------Module-Header-------------------------------;
|
|||
|
; Module Name: MATH.ASM
|
|||
|
;
|
|||
|
; Contains FIXED point math routines.
|
|||
|
;
|
|||
|
; Created: Sun 30-Aug-1987 19:28:30
|
|||
|
; Author: Charles Whitmer [chuckwh]
|
|||
|
;
|
|||
|
; Copyright (c) 1987 Microsoft Corporation
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
|
|||
|
?WIN = 0
|
|||
|
?PLM = 1
|
|||
|
?NODATA = 0
|
|||
|
|
|||
|
.286
|
|||
|
|
|||
|
.xlist
|
|||
|
include cmacros.inc
|
|||
|
include windows.inc
|
|||
|
.list
|
|||
|
|
|||
|
externA __WinFlags
|
|||
|
|
|||
|
UQUAD struc
|
|||
|
uq0 dw ?
|
|||
|
uq1 dw ?
|
|||
|
uq2 dw ?
|
|||
|
uq3 dw ?
|
|||
|
UQUAD ends
|
|||
|
|
|||
|
; The following two equates are just used as shorthand
|
|||
|
; for the "word ptr" and "byte ptr" overrides.
|
|||
|
|
|||
|
wptr equ word ptr
|
|||
|
bptr equ byte ptr
|
|||
|
|
|||
|
; The following structure should be used to access high and low
|
|||
|
; words of a DWORD. This means that "word ptr foo[2]" -> "foo.hi".
|
|||
|
|
|||
|
LONG struc
|
|||
|
lo dw ?
|
|||
|
hi dw ?
|
|||
|
LONG ends
|
|||
|
|
|||
|
EAXtoDXAX macro
|
|||
|
shld edx,eax,16 ; move HIWORD(eax) to dx
|
|||
|
endm
|
|||
|
|
|||
|
DXAXtoEAX macro
|
|||
|
ror eax,16 ; xchg HIWORD(eax) and LOWORD(eax)
|
|||
|
shrd eax,edx,16 ; move LOWORD(edx) to HIWORD(eax)
|
|||
|
endm
|
|||
|
|
|||
|
neg32 macro hi, lo
|
|||
|
neg lo
|
|||
|
adc hi,0 ; carry set unless lo zero
|
|||
|
neg hi
|
|||
|
endm
|
|||
|
|
|||
|
ifndef SEGNAME
|
|||
|
SEGNAME equ <_TEXT>
|
|||
|
endif
|
|||
|
|
|||
|
createSeg %SEGNAME, CodeSeg, word, public, CODE
|
|||
|
|
|||
|
sBegin CodeSeg
|
|||
|
assumes cs,CodeSeg
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; long muldiv32(long, long, long)
|
|||
|
;
|
|||
|
; multiples two 32 bit values and then divides the result by a third
|
|||
|
; 32 bit value with full 64 bit presision
|
|||
|
;
|
|||
|
; lResult = (lNumber * lNumerator) / lDenominator with correct rounding
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; lNumber = number to multiply by nNumerator
|
|||
|
; lNumerator = number to multiply by nNumber
|
|||
|
; lDenominator = number to divide the multiplication result by.
|
|||
|
;
|
|||
|
; Returns:
|
|||
|
; DX:AX = result of multiplication and division.
|
|||
|
;
|
|||
|
; Error Returns:
|
|||
|
; none
|
|||
|
; Registers Preserved:
|
|||
|
; DS,ES,SI,DI
|
|||
|
; History:
|
|||
|
; Fri 05-Oct-1990 -by- Rob Williams [Robwi]
|
|||
|
; Behavior consistent with MulDiv16 routine (signed, no int 0 on overflow)
|
|||
|
; Stole muldiv16 psuedocode
|
|||
|
;
|
|||
|
; Wed 14-June-1990 -by- Todd Laney [ToddLa]
|
|||
|
; converted it to 386/286 code. (by checking __WinFlags)
|
|||
|
;
|
|||
|
; Tue 08-May-1990 -by- Rob Williams [Robwi]
|
|||
|
; Wrote it.
|
|||
|
;
|
|||
|
;----------------------------Pseudo-Code--------------------------------;
|
|||
|
; long FAR PASCAL muldiv32(long, long, long)
|
|||
|
; long l;
|
|||
|
; long Numer;
|
|||
|
; long Denom;
|
|||
|
; {
|
|||
|
;
|
|||
|
; Sign = sign of Denom; // Sign will keep track of final sign //
|
|||
|
;
|
|||
|
;
|
|||
|
; if (Denom < 0)
|
|||
|
; {
|
|||
|
; negate Denom; // make sure Denom is positive //
|
|||
|
; }
|
|||
|
;
|
|||
|
; if (l < 0)
|
|||
|
; {
|
|||
|
; negate l; // make sure l is positive //
|
|||
|
; }
|
|||
|
;
|
|||
|
; make Sign reflect any sign change;
|
|||
|
;
|
|||
|
;
|
|||
|
; if (Numer < 0)
|
|||
|
; {
|
|||
|
; negate Numer; // make sure Numer is positive //
|
|||
|
; }
|
|||
|
;
|
|||
|
; make Sign reflect any sign change;
|
|||
|
;
|
|||
|
; Numer *= l;
|
|||
|
; Numer += (Denom/2); // adjust for rounding //
|
|||
|
;
|
|||
|
; if (overflow) // check for overflow, and handle divide by zero //
|
|||
|
; {
|
|||
|
; jump to md5;
|
|||
|
; }
|
|||
|
;
|
|||
|
; result = Numer/Denom;
|
|||
|
;
|
|||
|
; if (overflow) // check again to see if overflow occured //
|
|||
|
; {
|
|||
|
; jump to md5;
|
|||
|
; }
|
|||
|
;
|
|||
|
; if (Sign is negative) // put sign on the result //
|
|||
|
; {
|
|||
|
; negate result;
|
|||
|
; }
|
|||
|
;
|
|||
|
;md6:
|
|||
|
; return(result);
|
|||
|
;
|
|||
|
;md5:
|
|||
|
; DX = 7FFF; // indicate overflow by //
|
|||
|
; AX = 0xFFFF // return largest integer //
|
|||
|
; if (Sign is negative)
|
|||
|
; {
|
|||
|
; DX = 0x8000; // with correct sign //
|
|||
|
; AX = 0x0000;
|
|||
|
; }
|
|||
|
;
|
|||
|
; jump to md6;
|
|||
|
; }
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc muldiv32,<PUBLIC,FAR,NODATA,NONWIN>,<>
|
|||
|
; ParmD lNumber
|
|||
|
; ParmD lNumerator
|
|||
|
; ParmD lDenominator
|
|||
|
cBegin <nogen>
|
|||
|
mov ax,__WinFlags
|
|||
|
test ax,WF_CPU286+WF_CPU086+WF_CPU186
|
|||
|
jz md32_1
|
|||
|
jmp NEAR PTR muldiv32_286
|
|||
|
md32_1:
|
|||
|
errn$ muldiv32_386
|
|||
|
cEnd <nogen>
|
|||
|
|
|||
|
cProc muldiv32_386,<PUBLIC,FAR,NODATA,NONWIN>,<>
|
|||
|
ParmD lNumber
|
|||
|
ParmD lNumerator
|
|||
|
ParmD lDenominator
|
|||
|
cBegin
|
|||
|
.386
|
|||
|
|
|||
|
|
|||
|
mov ebx,lDenominator ; get the demoninator
|
|||
|
mov ecx,ebx ; ECX holds the final sign in hiword
|
|||
|
or ebx,ebx ; ensure the denominator is positive
|
|||
|
jns md386_1
|
|||
|
neg ebx
|
|||
|
|
|||
|
md386_1:
|
|||
|
mov eax,lNumber ; get the long we are multiplying
|
|||
|
xor ecx,eax ; make ECX reflect any sign change
|
|||
|
or eax,eax ; ensure the long is positive
|
|||
|
jns md386_2
|
|||
|
neg eax
|
|||
|
|
|||
|
md386_2:
|
|||
|
mov edx,lNumerator ; get the numerator
|
|||
|
xor ecx,edx ; make ECX reflect any sign change
|
|||
|
or edx,edx ; ensure the numerator is positive
|
|||
|
jns md386_3
|
|||
|
neg edx
|
|||
|
|
|||
|
md386_3:
|
|||
|
mul edx ; multiply
|
|||
|
mov cx,bx ; get half of the demoninator to adjust for rounding
|
|||
|
sar ebx,1
|
|||
|
add eax,ebx ; adjust for possible rounding error
|
|||
|
adc edx,0 ; this is really a long addition
|
|||
|
sal ebx,1 ; restore the demoninator
|
|||
|
or bx,cx ; fix bottom bit
|
|||
|
cmp edx,ebx ; check for overflow
|
|||
|
jae md386_5 ; (ae handles /0 case)
|
|||
|
div ebx ; divide
|
|||
|
or eax,eax ; If sign is set, then overflow occured
|
|||
|
js md386_5 ; Overflow.
|
|||
|
or ecx,ecx ; put the sign on the result
|
|||
|
jns md386_6
|
|||
|
neg eax
|
|||
|
|
|||
|
md386_6:
|
|||
|
EAXtoDXAX ; convert eax to dx:ax for 16 bit programs
|
|||
|
|
|||
|
.286
|
|||
|
cEnd
|
|||
|
.386
|
|||
|
|
|||
|
md386_5:
|
|||
|
mov eax,7FFFFFFFh ; return the largest integer
|
|||
|
or ecx,ecx ; with the correct sign
|
|||
|
jns md386_6
|
|||
|
not eax
|
|||
|
jmp md386_6
|
|||
|
|
|||
|
.286
|
|||
|
|
|||
|
cProc muldiv32_286,<PUBLIC,FAR,NODATA,NONWIN>,<di,si>
|
|||
|
ParmD lNumber
|
|||
|
ParmD lNumerator
|
|||
|
ParmD lDenominator
|
|||
|
LocalW wSign
|
|||
|
|
|||
|
cBegin
|
|||
|
|
|||
|
mov dx,lDenominator.hi ; get the demoninator
|
|||
|
mov si,dx ; SI holds the final sign
|
|||
|
or dx,dx ; ensure the denominator is positive
|
|||
|
jns md286_1
|
|||
|
neg32 dx, lDenominator.lo
|
|||
|
mov lDenominator.hi, dx
|
|||
|
|
|||
|
|
|||
|
md286_1:
|
|||
|
mov ax,lNumber.lo ; get the long we are multiplying
|
|||
|
mov dx,lNumber.hi
|
|||
|
xor si,dx ; make ECX reflect any sign change
|
|||
|
or dx,dx ; ensure the long is positive
|
|||
|
jns md286_2
|
|||
|
neg32 dx, ax
|
|||
|
|
|||
|
md286_2:
|
|||
|
mov bx,lNumerator.lo ; get the numerator
|
|||
|
mov cx,lNumerator.hi ; get the numerator
|
|||
|
xor si,cx ; make ECX reflect any sign change
|
|||
|
or cx,cx ; ensure the numerator is positive
|
|||
|
jns md286_3
|
|||
|
neg32 cx, bx
|
|||
|
|
|||
|
md286_3:
|
|||
|
mov wSign, si ; save sign
|
|||
|
call dmul ; multiply (result in dx:cx:bx:ax)
|
|||
|
mov si, lDenominator.hi
|
|||
|
mov di, lDenominator.lo
|
|||
|
sar si, 1 ; get half of the demoninator
|
|||
|
rcr di, 1 ; to adjust for rounding
|
|||
|
add ax, di ; adjust for possible rounding error
|
|||
|
adc bx, si
|
|||
|
adc cx, 0
|
|||
|
adc dx, 0 ; this is really a long addition
|
|||
|
|
|||
|
sal di, 1 ; restore the demoninator
|
|||
|
rcl si, 1
|
|||
|
|
|||
|
or di, lDenominator.lo ; fix bottom bit
|
|||
|
|
|||
|
cmp dx, si ; begin overflow check (unsigned for div 0 check)
|
|||
|
ja md286_5 ; overflow
|
|||
|
jb md286_7 ; no overflow
|
|||
|
cmp cx, di
|
|||
|
jae md286_5 ; overflow
|
|||
|
|
|||
|
md286_7:
|
|||
|
call qdiv ; DX:AX is quotient
|
|||
|
or dx,dx ; If sign is set, then overflow occured
|
|||
|
js md286_5 ; Overflow.
|
|||
|
mov cx, wSign
|
|||
|
or cx,cx ; put the sign on the result
|
|||
|
jns md286_6
|
|||
|
neg32 dx,ax
|
|||
|
|
|||
|
md286_6:
|
|||
|
|
|||
|
cEnd
|
|||
|
|
|||
|
md286_5:
|
|||
|
mov cx, wSign
|
|||
|
mov ax, 0FFFFh ; return the largest integer
|
|||
|
mov dx, 7FFFh
|
|||
|
or cx, cx ; with the correct sign
|
|||
|
jns md286_6
|
|||
|
not dx
|
|||
|
not ax
|
|||
|
jmp md286_6
|
|||
|
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; idmul
|
|||
|
;
|
|||
|
; This is an extended precision multiply routine, intended to emulate
|
|||
|
; 80386 imul instruction.
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; DX:AX = LONG
|
|||
|
; CX:BX = LONG
|
|||
|
; Returns:
|
|||
|
; DX:CX:BX:AX = QUAD product
|
|||
|
; Registers Destroyed:
|
|||
|
; none
|
|||
|
; History:
|
|||
|
; Tue 26-Jan-1988 23:47:02 -by- Charles Whitmer [chuckwh]
|
|||
|
; Wrote it.
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc idmul,<PUBLIC,NEAR>,<si,di>
|
|||
|
localQ qTemp
|
|||
|
cBegin
|
|||
|
|
|||
|
; put one argument in safe registers
|
|||
|
|
|||
|
mov si,dx
|
|||
|
mov di,ax
|
|||
|
|
|||
|
; do the low order unsigned product
|
|||
|
|
|||
|
mul bx
|
|||
|
mov qTemp.uq0,ax
|
|||
|
mov qTemp.uq1,dx
|
|||
|
|
|||
|
; do the high order signed product
|
|||
|
|
|||
|
mov ax,si
|
|||
|
imul cx
|
|||
|
mov qTemp.uq2,ax
|
|||
|
mov qTemp.uq3,dx
|
|||
|
|
|||
|
; do a mixed product
|
|||
|
|
|||
|
mov ax,si
|
|||
|
cwd
|
|||
|
and dx,bx
|
|||
|
sub qTemp.uq2,dx ; adjust for sign bit
|
|||
|
sbb qTemp.uq3,0
|
|||
|
mul bx
|
|||
|
add qTemp.uq1,ax
|
|||
|
adc qTemp.uq2,dx
|
|||
|
adc qTemp.uq3,0
|
|||
|
|
|||
|
; do the other mixed product
|
|||
|
|
|||
|
mov ax,cx
|
|||
|
cwd
|
|||
|
and dx,di
|
|||
|
sub qTemp.uq2,dx
|
|||
|
sbb qTemp.uq3,0
|
|||
|
mul di
|
|||
|
|
|||
|
; pick up the answer
|
|||
|
|
|||
|
mov bx,ax
|
|||
|
mov cx,dx
|
|||
|
xor dx,dx
|
|||
|
|
|||
|
mov ax,qTemp.uq0
|
|||
|
add bx,qTemp.uq1
|
|||
|
adc cx,qTemp.uq2
|
|||
|
adc dx,qTemp.uq3
|
|||
|
cEnd
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; dmul
|
|||
|
;
|
|||
|
; This is an extended precision multiply routine, intended to emulate
|
|||
|
; 80386 mul instruction.
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; DX:AX = LONG
|
|||
|
; CX:BX = LONG
|
|||
|
; Returns:
|
|||
|
; DX:CX:BX:AX = QUAD product
|
|||
|
; Registers Destroyed:
|
|||
|
; none
|
|||
|
; History:
|
|||
|
; Tue 02-Feb-1988 10:50:44 -by- Charles Whitmer [chuckwh]
|
|||
|
; Copied from idmul and modified.
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc dmul,<PUBLIC,NEAR>,<si,di>
|
|||
|
localQ qTemp
|
|||
|
cBegin
|
|||
|
|
|||
|
; put one argument in safe registers
|
|||
|
|
|||
|
mov si,dx
|
|||
|
mov di,ax
|
|||
|
|
|||
|
; do the low order product
|
|||
|
|
|||
|
mul bx
|
|||
|
mov qTemp.uq0,ax
|
|||
|
mov qTemp.uq1,dx
|
|||
|
|
|||
|
; do the high order product
|
|||
|
|
|||
|
mov ax,si
|
|||
|
mul cx
|
|||
|
mov qTemp.uq2,ax
|
|||
|
mov qTemp.uq3,dx
|
|||
|
|
|||
|
; do a mixed product
|
|||
|
|
|||
|
mov ax,si
|
|||
|
mul bx
|
|||
|
add qTemp.uq1,ax
|
|||
|
adc qTemp.uq2,dx
|
|||
|
adc qTemp.uq3,0
|
|||
|
|
|||
|
; do the other mixed product
|
|||
|
|
|||
|
mov ax,cx
|
|||
|
mul di
|
|||
|
|
|||
|
; pick up the answer
|
|||
|
|
|||
|
mov bx,ax
|
|||
|
mov cx,dx
|
|||
|
xor dx,dx
|
|||
|
mov ax,qTemp.uq0
|
|||
|
add bx,qTemp.uq1
|
|||
|
adc cx,qTemp.uq2
|
|||
|
adc dx,qTemp.uq3
|
|||
|
cEnd
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; iqdiv
|
|||
|
;
|
|||
|
; This is an extended precision divide routine which is intended to
|
|||
|
; emulate the 80386 64 bit/32 bit IDIV instruction. We don't have the
|
|||
|
; 32 bit registers to work with, but we pack the arguments and results
|
|||
|
; into what registers we do have. We will divide two signed numbers
|
|||
|
; and return the quotient and remainder. We will do INT 0 for overflow,
|
|||
|
; just like the 80386 microcode. This should ease conversion later.
|
|||
|
;
|
|||
|
; This routine just keeps track of the signs and calls qdiv to do the
|
|||
|
; real work.
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; DX:CX:BX:AX = QUAD Numerator
|
|||
|
; SI:DI = LONG Denominator
|
|||
|
; Returns:
|
|||
|
; DX:AX = quotient
|
|||
|
; CX:BX = remainder
|
|||
|
; Registers Destroyed:
|
|||
|
; DI,SI
|
|||
|
; History:
|
|||
|
; Tue 26-Jan-1988 02:49:19 -by- Charles Whitmer [chuckwh]
|
|||
|
; Wrote it.
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
|
|||
|
WIMP equ 1
|
|||
|
|
|||
|
IQDIV_RESULT_SIGN equ 1
|
|||
|
IQDIV_REM_SIGN equ 2
|
|||
|
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc iqdiv,<PUBLIC,NEAR>
|
|||
|
localB flags
|
|||
|
cBegin
|
|||
|
mov flags,0
|
|||
|
|
|||
|
; take the absolute value of the denominator
|
|||
|
|
|||
|
or si,si
|
|||
|
jns denominator_is_cool
|
|||
|
xor flags,IQDIV_RESULT_SIGN
|
|||
|
neg di
|
|||
|
adc si,0
|
|||
|
neg si
|
|||
|
denominator_is_cool:
|
|||
|
|
|||
|
; take the absolute value of the denominator
|
|||
|
|
|||
|
or dx,dx
|
|||
|
jns numerator_is_cool
|
|||
|
xor flags,IQDIV_RESULT_SIGN + IQDIV_REM_SIGN
|
|||
|
not ax
|
|||
|
not bx
|
|||
|
not cx
|
|||
|
not dx
|
|||
|
add ax,1
|
|||
|
adc bx,0
|
|||
|
adc cx,0
|
|||
|
adc dx,0
|
|||
|
numerator_is_cool:
|
|||
|
|
|||
|
; do the unsigned division
|
|||
|
|
|||
|
call qdiv
|
|||
|
ifdef WIMP
|
|||
|
jo iqdiv_exit
|
|||
|
endif
|
|||
|
|
|||
|
; check for overflow
|
|||
|
|
|||
|
or dx,dx
|
|||
|
jns have_a_bit_to_spare
|
|||
|
ifdef WIMP
|
|||
|
mov ax,8000h
|
|||
|
dec ah
|
|||
|
jmp short iqdiv_exit
|
|||
|
else
|
|||
|
int 0 ; You're toast, Jack!
|
|||
|
endif
|
|||
|
have_a_bit_to_spare:
|
|||
|
|
|||
|
; negate the result, if required
|
|||
|
|
|||
|
test flags,IQDIV_RESULT_SIGN
|
|||
|
jz result_is_done
|
|||
|
neg ax
|
|||
|
adc dx,0
|
|||
|
neg dx
|
|||
|
result_is_done:
|
|||
|
|
|||
|
; negate the remainder, if required
|
|||
|
|
|||
|
test flags,IQDIV_REM_SIGN
|
|||
|
jz remainder_is_done
|
|||
|
neg bx
|
|||
|
adc cx,0
|
|||
|
neg cx
|
|||
|
remainder_is_done:
|
|||
|
iqdiv_exit:
|
|||
|
cEnd
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; qdiv
|
|||
|
;
|
|||
|
; This is an extended precision divide routine which is intended to
|
|||
|
; emulate the 80386 64 bit/32 bit DIV instruction. We don't have the
|
|||
|
; 32 bit registers to work with, but we pack the arguments and results
|
|||
|
; into what registers we do have. We will divide two unsigned numbers
|
|||
|
; and return the quotient and remainder. We will do INT 0 for overflow,
|
|||
|
; just like the 80386 microcode. This should ease conversion later.
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; DX:CX:BX:AX = UQUAD Numerator
|
|||
|
; SI:DI = ULONG Denominator
|
|||
|
; Returns:
|
|||
|
; DX:AX = quotient
|
|||
|
; CX:BX = remainder
|
|||
|
; Registers Destroyed:
|
|||
|
; none
|
|||
|
; History:
|
|||
|
; Tue 26-Jan-1988 00:02:09 -by- Charles Whitmer [chuckwh]
|
|||
|
; Wrote it.
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc qdiv,<PUBLIC,NEAR>,<si,di>
|
|||
|
localQ uqNumerator
|
|||
|
localD ulDenominator
|
|||
|
localD ulQuotient
|
|||
|
localW cShift
|
|||
|
cBegin
|
|||
|
|
|||
|
; stuff the quad word into local memory
|
|||
|
|
|||
|
mov uqNumerator.uq0,ax
|
|||
|
mov uqNumerator.uq1,bx
|
|||
|
mov uqNumerator.uq2,cx
|
|||
|
mov uqNumerator.uq3,dx
|
|||
|
|
|||
|
|
|||
|
; check for overflow
|
|||
|
|
|||
|
qdiv_restart:
|
|||
|
cmp si,dx
|
|||
|
ja qdiv_no_overflow
|
|||
|
jb qdiv_overflow
|
|||
|
cmp di,cx
|
|||
|
ja qdiv_no_overflow
|
|||
|
qdiv_overflow:
|
|||
|
ifdef WIMP
|
|||
|
mov ax,8000h
|
|||
|
dec ah
|
|||
|
jmp qdiv_exit
|
|||
|
else
|
|||
|
int 0 ; You're toast, Jack!
|
|||
|
jmp qdiv_restart
|
|||
|
endif
|
|||
|
qdiv_no_overflow:
|
|||
|
|
|||
|
; check for a zero Numerator
|
|||
|
|
|||
|
or ax,bx
|
|||
|
or ax,cx
|
|||
|
or ax,dx
|
|||
|
jz qdiv_exit_relay ; quotient = remainder = 0
|
|||
|
|
|||
|
; handle the special case when the denominator lives in the low word
|
|||
|
|
|||
|
or si,si
|
|||
|
jnz not_that_special
|
|||
|
|
|||
|
; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=0):DI
|
|||
|
|
|||
|
cmp di,1 ; separate out the trivial case
|
|||
|
jz div_by_one
|
|||
|
xchg dx,cx ; CX = remainder.hi = 0
|
|||
|
mov ax,bx
|
|||
|
div di
|
|||
|
mov bx,ax ; BX = quotient.hi
|
|||
|
mov ax,uqNumerator.uq0
|
|||
|
div di ; AX = quotient.lo
|
|||
|
xchg bx,dx ; DX = quotient.hi, BX = remainder.lo
|
|||
|
ifdef WIMP
|
|||
|
or ax,ax ; clear OF
|
|||
|
endif
|
|||
|
qdiv_exit_relay:
|
|||
|
jmp qdiv_exit
|
|||
|
|
|||
|
; calculate (DX=0):(CX=0):BX:uqNumerator.uq0 / (SI=0):(DI=1)
|
|||
|
|
|||
|
div_by_one:
|
|||
|
xchg dx,bx ; DX = quotient.hi, BX = remainder.lo = 0
|
|||
|
mov ax,uqNumerator.uq0 ; AX = quotient.lo
|
|||
|
jmp qdiv_exit
|
|||
|
not_that_special:
|
|||
|
|
|||
|
; handle the special case when the denominator lives in the high word
|
|||
|
|
|||
|
or di,di
|
|||
|
jnz not_this_special_either
|
|||
|
|
|||
|
; calculate DX:CX:BX:uqNumerator.uq0 / SI:(DI=0)
|
|||
|
|
|||
|
cmp si,1 ; separate out the trivial case
|
|||
|
jz div_by_10000h
|
|||
|
mov ax,cx
|
|||
|
div si
|
|||
|
mov cx,ax ; CX = quotient.hi
|
|||
|
mov ax,bx
|
|||
|
div si ; AX = quotient.lo
|
|||
|
xchg cx,dx ; DX = quotient.hi, CX = remainder.hi
|
|||
|
mov bx,uqNumerator.uq0 ; BX = remainder.lo
|
|||
|
ifdef WIMP
|
|||
|
or ax,ax ; clear OF
|
|||
|
endif
|
|||
|
jmp qdiv_exit
|
|||
|
|
|||
|
; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=1):(DI=0)
|
|||
|
|
|||
|
div_by_10000h:
|
|||
|
xchg cx,dx ; DX = quotient.hi, CX = remainder.hi = 0
|
|||
|
mov ax,bx ; AX = quotient.lo
|
|||
|
mov bx,uqNumerator.uq0 ; BX = remainder.lo
|
|||
|
jmp qdiv_exit
|
|||
|
not_this_special_either:
|
|||
|
|
|||
|
; normalize the denominator
|
|||
|
|
|||
|
mov dx,si
|
|||
|
mov ax,di
|
|||
|
call ulNormalize ; DX:AX = normalized denominator
|
|||
|
mov cShift,cx ; CX < 16
|
|||
|
mov ulDenominator.lo,ax
|
|||
|
mov ulDenominator.hi,dx
|
|||
|
|
|||
|
|
|||
|
; shift the Numerator by the same amount
|
|||
|
|
|||
|
jcxz numerator_is_shifted
|
|||
|
mov si,-1
|
|||
|
shl si,cl
|
|||
|
not si ; SI = mask
|
|||
|
mov bx,uqNumerator.uq3
|
|||
|
shl bx,cl
|
|||
|
mov ax,uqNumerator.uq2
|
|||
|
rol ax,cl
|
|||
|
mov di,si
|
|||
|
and di,ax
|
|||
|
or bx,di
|
|||
|
mov uqNumerator.uq3,bx
|
|||
|
xor ax,di
|
|||
|
mov bx,uqNumerator.uq1
|
|||
|
rol bx,cl
|
|||
|
mov di,si
|
|||
|
and di,bx
|
|||
|
or ax,di
|
|||
|
mov uqNumerator.uq2,ax
|
|||
|
xor bx,di
|
|||
|
mov ax,uqNumerator.uq0
|
|||
|
rol ax,cl
|
|||
|
mov di,si
|
|||
|
and di,ax
|
|||
|
or bx,di
|
|||
|
mov uqNumerator.uq1,bx
|
|||
|
xor ax,di
|
|||
|
mov uqNumerator.uq0,ax
|
|||
|
numerator_is_shifted:
|
|||
|
|
|||
|
; set up registers for division
|
|||
|
|
|||
|
mov dx,uqNumerator.uq3
|
|||
|
mov ax,uqNumerator.uq2
|
|||
|
mov di,uqNumerator.uq1
|
|||
|
mov cx,ulDenominator.hi
|
|||
|
mov bx,ulDenominator.lo
|
|||
|
|
|||
|
; check for case when Denominator has only 16 bits
|
|||
|
|
|||
|
or bx,bx
|
|||
|
jnz must_do_long_division
|
|||
|
div cx
|
|||
|
mov si,ax
|
|||
|
mov ax,uqNumerator.uq1
|
|||
|
div cx
|
|||
|
xchg si,dx ; DX:AX = quotient
|
|||
|
mov di,uqNumerator.uq0 ; SI:DI = remainder (shifted)
|
|||
|
jmp short unshift_remainder
|
|||
|
must_do_long_division:
|
|||
|
|
|||
|
; do the long division, part IZ@NL@%
|
|||
|
|
|||
|
cmp dx,cx ; we only know that DX:AX < CX:BX!
|
|||
|
jb first_division_is_safe
|
|||
|
mov ulQuotient.hi,0 ; i.e. 10000h, our guess is too big
|
|||
|
mov si,ax
|
|||
|
sub si,bx ; ... remainder is negative
|
|||
|
jmp short first_adjuster
|
|||
|
first_division_is_safe:
|
|||
|
div cx
|
|||
|
mov ulQuotient.hi,ax
|
|||
|
mov si,dx
|
|||
|
mul bx ; fix remainder for low order term
|
|||
|
sub di,ax
|
|||
|
sbb si,dx
|
|||
|
jnc first_adjuster_done ; The remainder is UNSIGNED! We have
|
|||
|
first_adjuster: ; to use the carry flag to keep track
|
|||
|
dec ulQuotient.hi ; of the sign. The adjuster loop
|
|||
|
add di,bx ; watches for a change to the carry
|
|||
|
adc si,cx ; flag which would indicate a sign
|
|||
|
jnc first_adjuster ; change IF we had more bits to keep
|
|||
|
first_adjuster_done: ; a sign in.
|
|||
|
|
|||
|
; do the long division, part II
|
|||
|
|
|||
|
mov dx,si
|
|||
|
mov ax,di
|
|||
|
mov di,uqNumerator.uq0
|
|||
|
cmp dx,cx ; we only know that DX:AX < CX:BX!
|
|||
|
jb second_division_is_safe
|
|||
|
mov ulQuotient.lo,0 ; i.e. 10000h, our guess is too big
|
|||
|
mov si,ax
|
|||
|
sub si,bx ; ... remainder is negative
|
|||
|
jmp short second_adjuster
|
|||
|
second_division_is_safe:
|
|||
|
div cx
|
|||
|
mov ulQuotient.lo,ax
|
|||
|
mov si,dx
|
|||
|
mul bx ; fix remainder for low order term
|
|||
|
sub di,ax
|
|||
|
sbb si,dx
|
|||
|
jnc second_adjuster_done
|
|||
|
second_adjuster:
|
|||
|
dec ulQuotient.lo
|
|||
|
add di,bx
|
|||
|
adc si,cx
|
|||
|
jnc second_adjuster
|
|||
|
second_adjuster_done:
|
|||
|
mov ax,ulQuotient.lo
|
|||
|
mov dx,ulQuotient.hi
|
|||
|
|
|||
|
; unshift the remainder in SI:DI
|
|||
|
|
|||
|
unshift_remainder:
|
|||
|
mov cx,cShift
|
|||
|
jcxz remainder_unshifted
|
|||
|
mov bx,-1
|
|||
|
shr bx,cl
|
|||
|
not bx
|
|||
|
shr di,cl
|
|||
|
ror si,cl
|
|||
|
and bx,si
|
|||
|
or di,bx
|
|||
|
xor si,bx
|
|||
|
remainder_unshifted:
|
|||
|
mov cx,si
|
|||
|
mov bx,di
|
|||
|
ifdef WIMP
|
|||
|
or ax,ax ; clear OF
|
|||
|
endif
|
|||
|
qdiv_exit:
|
|||
|
cEnd
|
|||
|
|
|||
|
;---------------------------Public-Routine------------------------------;
|
|||
|
; ulNormalize
|
|||
|
;
|
|||
|
; Normalizes a ULONG so that the highest order bit is 1. Returns the
|
|||
|
; number of shifts done. Also returns ZF=1 if the ULONG was zero.
|
|||
|
;
|
|||
|
; Entry:
|
|||
|
; DX:AX = ULONG
|
|||
|
; Returns:
|
|||
|
; DX:AX = normalized ULONG
|
|||
|
; CX = shift count
|
|||
|
; ZF = 1 if the ULONG is zero, 0 otherwise
|
|||
|
; Registers Destroyed:
|
|||
|
; none
|
|||
|
; History:
|
|||
|
; Mon 25-Jan-1988 22:07:03 -by- Charles Whitmer [chuckwh]
|
|||
|
; Wrote it.
|
|||
|
;-----------------------------------------------------------------------;
|
|||
|
assumes ds,nothing
|
|||
|
assumes es,nothing
|
|||
|
|
|||
|
cProc ulNormalize,<PUBLIC,NEAR>
|
|||
|
cBegin
|
|||
|
|
|||
|
; shift by words
|
|||
|
|
|||
|
xor cx,cx
|
|||
|
or dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
jnz top_word_ok
|
|||
|
xchg ax,dx
|
|||
|
or dx,dx
|
|||
|
jz ulNormalize_exit ; the zero exit
|
|||
|
mov cl,16
|
|||
|
js ulNormalize_exit
|
|||
|
top_word_ok:
|
|||
|
|
|||
|
; shift by bytes
|
|||
|
|
|||
|
or dh,dh
|
|||
|
jnz top_byte_ok
|
|||
|
xchg dh,dl
|
|||
|
xchg dl,ah
|
|||
|
xchg ah,al
|
|||
|
add cl,8
|
|||
|
or dh,dh
|
|||
|
js ulNormalize_exit
|
|||
|
top_byte_ok:
|
|||
|
|
|||
|
; do the rest by bits
|
|||
|
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
js ulNormalize_exit
|
|||
|
inc cx
|
|||
|
add ax,ax
|
|||
|
adc dx,dx
|
|||
|
ulNormalize_exit:
|
|||
|
cEnd
|
|||
|
|
|||
|
sEnd CodeSeg
|
|||
|
|
|||
|
end
|
|||
|
|