253 lines
5.9 KiB
NASM
253 lines
5.9 KiB
NASM
page ,132
|
|
subttl emfdiv.asm - Division
|
|
;***
|
|
;emfdiv.asm - Division
|
|
;
|
|
; Copyright (c) 1986-89, Microsoft Corporation
|
|
;
|
|
;Purpose:
|
|
; Division
|
|
;
|
|
; This Module contains Proprietary Information of Microsoft
|
|
; Corporation and should be treated as Confidential.
|
|
;
|
|
;Revision History:
|
|
; See emulator.hst
|
|
;
|
|
;*******************************************************************************
|
|
|
|
|
|
;-----------------------------------------;
|
|
; ;
|
|
; Division ;
|
|
; ;
|
|
;-----------------------------------------;
|
|
|
|
ProfBegin FDIV
|
|
|
|
RDBRQQ: ; Routine Div Both must see if we have two singles.
|
|
|
|
if fastSP
|
|
MOV BX,DX
|
|
XOR BX,Single + 256*Single
|
|
TEST BX,Single + 256*Single
|
|
JNZ RDDRQQ
|
|
MOV bx,offset TDSRQQ
|
|
JMP [bx]
|
|
endif ;fastSP
|
|
|
|
|
|
pub RDDRQQ ; Routine Division Double
|
|
; Now we have
|
|
; SI --> numerator , AX - Expon , DL - Sign
|
|
; DI --> denominator , CX - Expon , DH - Sign
|
|
|
|
if fastSP
|
|
CALL CoerceToDouble ; insure that both args are double
|
|
endif ;fastSP
|
|
|
|
STC ; exponent will be difference - 1
|
|
SBB AX,CX ; compute result exponent
|
|
|
|
; AH has the (tentative) true exponent of the result. It is correct if the
|
|
; result does not need normalizing. If normalizing is required, then this
|
|
; must be incremented to give the correct result exponent
|
|
|
|
XOR DH,DL ; Compute sign
|
|
PUSH ebp
|
|
PUSH edx ; Save sign
|
|
PUSH esi
|
|
PUSH edi
|
|
ADD esi,6
|
|
ADD edi,6
|
|
MOV ecx,4
|
|
STD
|
|
REP CMPS word ptr [esi],word ptr [edi] ; compare numerator mantissa
|
|
CLD ; with denominator mantissa
|
|
POP edi
|
|
POP esi
|
|
PUSHF ; save the flags from the compare
|
|
MOV BP,AX ; save the exponent
|
|
LODS word ptr [esi] ; Load up numerator
|
|
MOV CX,AX
|
|
LODS word ptr [esi]
|
|
MOV BX,AX
|
|
LODS word ptr [esi]
|
|
MOV DX,AX
|
|
LODS word ptr [esi]
|
|
XCHG AX,DX
|
|
|
|
; Move divisor to DAC so we can get at it easily.
|
|
|
|
MOV esi,edi ; Move divisor to DAC
|
|
MOV edi,offset DAC
|
|
ifdef i386
|
|
MOVSD
|
|
MOVSD
|
|
else
|
|
MOVSW
|
|
MOVSW
|
|
MOVSW
|
|
MOVSW
|
|
endif
|
|
|
|
; Now we're all set:
|
|
; DX:AX:BX:CX has dividend
|
|
; DAC has divisor (in normal format)
|
|
; Both are 64 bits with zeros and have implied bit set.
|
|
; Top of stack has sign and tentative exponent.
|
|
|
|
XOR DI,DI
|
|
POPF ; numerator mantissa < denominator?
|
|
; 80286 errata for POPF shouldn't
|
|
; apply because interrupts should be
|
|
; turned on in this context
|
|
JB short DivNoShift ; if so bypass numerator shift
|
|
SHR DX,1 ; Make sure dividend is smaller than divisor
|
|
RCR AX,1 ; by dividing it by two
|
|
RCR BX,1
|
|
RCR CX,1
|
|
RCR DI,1
|
|
INC BP ; increment result exponent
|
|
pub DivNoShift
|
|
PUSH ebp ; save result exponent
|
|
MOV [REMLSW],DI ; Save lsb of remainder
|
|
CALL DIV16 ; Get a quotient digit
|
|
PUSH edi
|
|
MOV [REMLSW],0 ; Turn off the shifted bit
|
|
CALL DIV16
|
|
PUSH edi
|
|
CALL DIV16
|
|
PUSH edi
|
|
CALL DIV16
|
|
MOV BP,8001H ; turn round and sticky on
|
|
SHL CX,1
|
|
RCL BX,1
|
|
RCL AX,1
|
|
RCL DX,1 ; multiply remainder by 2
|
|
JC short BPset ; if overflow, then round,sticky valid
|
|
MOV esi,offset DAC
|
|
CMP DX,[esi+6]
|
|
JNE short RemainderNotHalf
|
|
CMP AX,[esi+4]
|
|
JNE short RemainderNotHalf
|
|
CMP BX,[esi+2]
|
|
JNE short RemainderNotHalf
|
|
CMP CX,[esi] ; compare 2*remainder with denominator
|
|
|
|
;Observe, oh wondering one, how you can assume the result of this last
|
|
;compare is not equality. Use the following notation: n=numerator,
|
|
;d=denominator,q=quotient,r=remainder,b=base(2^64 here). If
|
|
;initially we had n < d then there was no shift and we will find q and r
|
|
;so that q*d+r=n*b, if initially we had n >= d then there was a shift and
|
|
;we will find q and r so that q*d+r=n*b/2. If we have equality here
|
|
;then r=d/2 ==> n={possibly 2*}(2*q+1)*d/(2*b), since this can only
|
|
;be integral if d is a multiple of b, but by definition b/2 <= d < b, we
|
|
;have a contradiction. Equality is thus impossible at this point.
|
|
|
|
pub RemainderNotHalf ; if 2*remainder > denominator
|
|
JAE short BPset ; then round and sticky are valid
|
|
OR AX,DX
|
|
OR AX,CX
|
|
OR AX,BX
|
|
OR AL,AH ; otherwise or sticky bits into AL
|
|
XOR AH,AH ; clear round bit
|
|
MOV BP,AX ; move round and sticky into BP
|
|
pub BPset
|
|
MOV DX,DI ; get low 16 bits into proper location
|
|
POP ecx
|
|
POP ebx
|
|
POP edi
|
|
POP esi ; Now restore exponent
|
|
|
|
JMP ROUND ; Result is normalized, round it
|
|
|
|
|
|
; Remainder in DX:AX:BX:CX:REMLSW
|
|
|
|
pub DIV16
|
|
MOV SI,[DAC+6] ; Get high word of divisor
|
|
XOR DI,DI ; Initialize quotient digit to zero
|
|
CMP DX,SI ; Will we overflow?
|
|
JAE MAXQUO ; If so, go handle special
|
|
OR DX,DX ; Is dividend small?
|
|
JNZ short DDIV
|
|
CMP SI,AX ; Will divisor fit at all?
|
|
JA short ZERQUO ; No - quotient is zero
|
|
|
|
pub DDIV
|
|
DIV SI ; AX is our digit "guess"
|
|
PUSH edx ; Save remainder -
|
|
PUSH ebx ; top 32 bits
|
|
XCHG AX,DI ; Quotient digit in DI
|
|
XOR BP,BP ; Initialize quotient * divisor
|
|
MOV SI,BP
|
|
MOV AX,[DAC]
|
|
OR AX,AX ; If zero, save multiply time
|
|
JZ short REM2
|
|
MUL DI ; Begin computing quotient * divisor
|
|
MOV SI,DX
|
|
|
|
pub REM2
|
|
PUSH eax ; Save lowest word of quotient * divisor
|
|
MOV AX,[DAC+2]
|
|
OR AX,AX
|
|
JZ short REM3
|
|
MUL DI
|
|
ADD SI,AX
|
|
ADC BP,DX
|
|
|
|
pub REM3
|
|
MOV AX,[DAC+4]
|
|
OR AX,AX
|
|
JZ short REM4
|
|
MUL DI
|
|
ADD BP,AX
|
|
ADC DX,0
|
|
XCHG AX,DX
|
|
|
|
; Remainder - Quotient * divisor
|
|
; [SP+4]:[SP+2]:CX:REMLSW - AX:BP:SI:[SP]
|
|
|
|
pub REM4
|
|
MOV DX,[REMLSW] ; Low word of remainder
|
|
POP ebx ; Recover lowest word of quotient * divisor
|
|
SUB DX,BX
|
|
SBB CX,SI
|
|
POP ebx
|
|
SBB BX,BP
|
|
POP ebp ; Remainder from DIV
|
|
SBB BP,AX
|
|
XCHG AX,BP
|
|
|
|
pub ZERQUO ; Remainder in AX:BX:CX:DX
|
|
XCHG AX,DX
|
|
XCHG AX,CX
|
|
XCHG AX,BX
|
|
JNC short DRET ; Remainder in DX:AX:BX:CX
|
|
|
|
pub RESTORE
|
|
DEC DI ; Drop quotient since it didn't fit
|
|
ADD CX,[DAC] ; Add divisor back in until remainder goes +
|
|
ADC BX,[DAC+2]
|
|
ADC AX,[DAC+4]
|
|
ADC DX,[DAC+6]
|
|
JNC RESTORE ; Loop is performed at most twice
|
|
|
|
pub DRET
|
|
RET
|
|
|
|
pub MAXQUO
|
|
DEC DI ; DI=FFFF=2**16-1, DX:AX:BX:CX is remainder,
|
|
SUB CX,[DAC] ; DX = [DAC+6], d = divisor = [DAC]
|
|
SBB BX,[DAC+2]
|
|
SBB AX,[DAC+4] ; subtract 2^16*d from DX:AX:BX:CX:0000H
|
|
ADD CX,[DAC+2] ; (DX-[DAC+6] = 0 is implied)
|
|
ADC BX,[DAC+4]
|
|
ADC AX,DX ; add high 48 bits of d to AX:BX:CX:0000H
|
|
MOV DX,[DAC] ; add low 16 bits of d to zero giving DX
|
|
CMC ; DI should be FFFEH if no carry from add
|
|
JMP ZERQUO
|
|
|
|
ProfEnd FDIV
|