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
|
||
|