windows-nt/Source/XPSP1/NT/base/ntos/rtl/i386/largeint.asm
2020-09-26 16:20:57 +08:00

856 lines
20 KiB
NASM

TITLE "Large Integer Arithmetic"
;++
;
; Copyright (c) 1989 Microsoft Corporation
;
; Module Name:
;
; largeint.s
;
; Abstract:
;
; This module implements routines for performing extended integer
; arithmtic.
;
; Author:
;
; David N. Cutler (davec) 24-Aug-1989
;
; Environment:
;
; Any mode.
;
; Revision History:
;
;--
.386p
.xlist
include ks386.inc
include callconv.inc ; calling convention macros
.list
IFNDEF BLDR_KERNEL_RUNTIME
EXTRNP _RtlRaiseStatus, 1
ENDIF
_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "RtlLargeIntegerAdd"
;++
;
; LARGE_INTEGER
; RtlLargeIntegerAdd (
; IN LARGE_INTEGER Addend1,
; IN LARGE_INTEGER Addend2
; )
;
; Routine Description:
;
; This function adds a signed large integer to a signed large integer and
; returns the signed large integer result.
;
; Arguments:
;
; (TOS+4) = Addend1 - first addend value
; (TOS+12) = Addend2 - second addend value
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc _RtlLargeIntegerAdd ,4
cPublicFpo 4,0
mov eax,[esp]+4 ; (eax)=add1.low
add eax,[esp]+12 ; (eax)=sum.low
mov edx,[esp]+8 ; (edx)=add1.hi
adc edx,[esp]+16 ; (edx)=sum.hi
stdRET _RtlLargeIntegerAdd
stdENDP _RtlLargeIntegerAdd
page
subttl "Enlarged Integer Multiply"
;++
;
; LARGE_INTEGER
; RtlEnlargedIntegerMultiply (
; IN LONG Multiplicand,
; IN LONG Multiplier
; )
;
; Routine Description:
;
; This function multiplies a signed integer by an signed integer and
; returns a signed large integer result.
;
; Arguments:
;
; (TOS+4) = Factor1
; (TOS+8) = Factor2
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc __RtlEnlargedIntegerMultiply ,2
cPublicFpo 2,0
mov eax,[esp]+4 ; (eax) = factor1
imul dword ptr [esp]+8 ; (edx:eax) = signed result
stdRET __RtlEnlargedIntegerMultiply
stdENDP __RtlEnlargedIntegerMultiply
page
subttl "Enlarged Unsigned Integer Multiply"
;++
;
; LARGE_INTEGER
; RtlEnlargedUnsignedMultiply (
; IN ULONG Multiplicand,
; IN ULONG Multiplier
; )
;
; Routine Description:
;
; This function multiplies an un signed integer by an unsigned integer and
; returns a signed large integer result.
;
; Arguments:
;
; (TOS+4) = Factor1
; (TOS+8) = Factor2
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc __RtlEnlargedUnsignedMultiply ,2
cPublicFpo 2,0
mov eax,[esp]+4 ; (eax) = factor1
mul dword ptr [esp]+8 ; (edx:eax) = unsigned result
stdRET __RtlEnlargedUnsignedMultiply
stdENDP __RtlEnlargedUnsignedMultiply
page
subttl "Enlarged Unsigned Integer Divide"
;++
;
; ULONG
; RtlEnlargedUnsignedDivide (
; IN ULARGE_INTEGER Dividend,
; IN ULONG Divisor,
; IN PULONG Remainder
; )
;
;
; Routine Description:
;
; This function divides an unsigned large integer by an unsigned long
; and returns the resultant quotient and optionally the remainder.
;
; Arguments:
;
; Dividend - Supplies the dividend value.
;
; Divisor - Supplies the divisor value.
;
; Remainder - Supplies an optional pointer to a variable that
; receives the remainder.
;
; Return Value:
;
; The unsigned long integer quotient is returned as the function value.
;
;--
cPublicProc __RtlEnlargedUnsignedDivide,4
cPublicFpo 4,0
mov eax, [esp+4] ; (eax) = Dividend.LowPart
mov edx, [esp+8] ; (edx) = Dividend.HighPart
mov ecx, [esp+16] ; (ecx) = pRemainder
div dword ptr [esp+12] ; divide by Divisor
or ecx, ecx ; return remainder?
jnz short @f
stdRET __RtlEnlargedUnsignedDivide ; (eax) = Quotient
align 4
@@: mov [ecx], edx ; save remainder
stdRET __RtlEnlargedUnsignedDivide ; (eax) = Quotient
stdENDP __RtlEnlargedUnsignedDivide
page
subttl "Extended Large Integer Divide"
;++
;
; LARGE_INTEGER
; RtlExtendedLargeIntegerDivide (
; IN LARGE_INTEGER Dividend,
; IN ULONG Divisor,
; OUT PULONG Remainder OPTIONAL
; )
;
; Routine Description:
;
; This routine divides an unsigned 64 bit dividend by a 32 bit divisor
; and returns a 64-bit quotient, and optionally the 32-bit remainder.
;
;
; Arguments:
;
; Dividend - Supplies the 64 bit dividend for the divide operation.
;
; Divisor - Supplies the 32 bit divisor for the divide operation.
;
; Remainder - Supplies an optional pointer to a variable which receives
; the remainder
;
; Return Value:
;
; The 64-bit quotient is returned as the function value.
;
;--
cPublicProc _RtlExtendedLargeIntegerDivide, 4
cPublicFpo 4,3
push esi
push edi
push ebx
mov eax, [esp+16] ; (eax) = Dividend.LowPart
mov edx, [esp+20] ; (edx) = Dividend.HighPart
lid00: mov ebx, [esp+24] ; (ebx) = Divisor
or ebx, ebx
jz short lid_zero ; Attempted a divide by zero
push ebp
mov ecx, 64 ; Loop count
xor esi, esi ; Clear partial remainder
; (edx:eax) = Dividend
; (ebx) = Divisor
; (ecx) = Loop count
; (esi) = partial remainder
align 4
lid10: shl eax, 1 ; (LowPart << 1) | 0
rcl edx, 1 ; (HighPart << 1) | CF
rcl esi, 1 ; (Partial << 1) | CF
sbb edi, edi ; clone CF into edi (0 or -1)
cmp esi, ebx ; check if partial remainder less then divisor
cmc
sbb ebp, ebp ; clone CF intp ebp
or edi, ebp ; merge with remainder of high bit
sub eax, edi ; merge quotient bit
and edi, ebx ; Select divisor or 0
sub esi, edi
dec ecx ; dec interration count
jnz short lid10 ; go around again
pop ebp
pop ebx
pop edi
mov ecx, [esp+20] ; (ecx) = Remainder
or ecx, ecx
jnz short lid20
pop esi
stdRET _RtlExtendedLargeIntegerDivide
align 4
lid20:
mov [ecx], esi ; store remainder
pop esi
stdRET _RtlExtendedLargeIntegerDivide
lid_zero:
IFNDEF BLDR_KERNEL_RUNTIME
stdCall _RtlRaiseStatus, <STATUS_INTEGER_DIVIDE_BY_ZERO>
ENDIF
pop ebx
pop edi
pop esi
stdRET _RtlExtendedLargeIntegerDivide
stdENDP _RtlExtendedLargeIntegerDivide
page
subttl "Extended Magic Divide"
;++
;
; LARGE_INTEGER
; RtlExtendedMagicDivide (
; IN LARGE_INTEGER Dividend,
; IN LARGE_INTEGER MagicDivisor,
; IN CCHAR ShiftCount
; )
;
; Routine Description:
;
; This function divides a signed large integer by an unsigned large integer
; and returns the signed large integer result. The division is performed
; using reciprocal multiplication of a signed large integer value by an
; unsigned large integer fraction which represents the most significant
; 64-bits of the reciprocal divisor rounded up in its least significant bit
; and normalized with respect to bit 63. A shift count is also provided
; which is used to truncate the fractional bits from the result value.
;
; Arguments:
;
; (ebp+8) = Dividend
; (ebp+16) = MagicDivisor value is a 64-bit multiplicative reciprocal
; (ebp+24) = ShiftCount - Right shift adjustment value.
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
RemdDiv equ [ebp+8] ; Dividend
RemdRec equ [ebp+16] ; Reciprocal (magic divisor)
RemdShift equ [ebp+24]
RemdTmp1 equ [ebp-4]
RemdTmp2 equ [ebp-8]
RemdTmp3 equ [ebp-12]
cPublicProc _RtlExtendedMagicDivide ,5
push ebp
mov ebp,esp
sub esp,12
push esi
mov esi, RemdDiv+4
test esi,80000000h
jz remd10 ; no sign, no need to negate
neg dword ptr RemdDiv+4
neg dword ptr RemdDiv
sbb dword ptr RemdDiv+4,0 ; negate
remd10: mov eax,RemdRec
mul dword ptr RemdDiv ; (edx:eax) = Div.lo * Rec.lo
mov RemdTmp1,edx
mov eax,RemdRec
mul dword ptr RemdDiv+4 ; (edx:eax) = Div.hi * Rec.lo
mov RemdTmp2,eax
mov RemdTmp3,edx
mov eax,RemdRec+4
mul dword ptr RemdDiv ; (edx:eax) = Div.lo * Rec.hi
;
; Col 0 doesn't matter
; Col 1 = Hi(Div.lo * Rec.lo) + Low(Div.Hi * Rec.lo) + Low(Div.lo * Rec.hi)
; = RemdTmp1 + RemdTmp2 + eax
; -> Only want carry from Col 1
;
xor ecx,ecx ; (ecx) = 0
add eax,RemdTmp1
adc ecx, 0 ; save carry in ecx
add eax,RemdTmp2
adc ecx, 0 ; Save Carry, all we want from Col2
mov RemdTmp1,edx
mov eax,RemdRec+4
mul dword ptr RemdDiv+4 ; (edx:eax) = Div.Hi * Rec.Hi
;
; TOS = carry flag from Col 1
;
; Col 2 = Col1 CF +
; Hi(Div.Hi * Rec.Lo) + Hi(Div.Lo * Rec.Hi) + Low(Div.Hi * Rec.Hi)
; = CF + RemdTmp3 + RemdTmp1 + eax
;
; Col 3 = Col2 CF + Hi(Div.Hi * Rec.Hi)
; = CF + edx
;
add eax,RemdTmp1
adc edx, 0 ; add carry to edx
add eax,RemdTmp3 ; (eax) = col 2
adc edx, 0 ; add carry to edx
add eax, ecx
adc edx, 0 ; (edx) = col 3
;
; (edx:eax) = the high 64 bits of the multiply, shift it right by
; shift count to discard bits to right of virtual decimal pt.
;
; RemdShift could be as large as 63 and still not 0 the result, 386
; will only shift 31 bits at a time, so must do the sift multiple
; times to get correct effect.
;
mov cl,RemdShift
remd20: cmp cl,31
jbe remd30
sub cl,31
shrd eax,edx,31
shr edx,31
jmp remd20
remd30: shrd eax,edx,cl
shr edx,cl
;
; Negate the result if need be
;
test esi,80000000h
jz remd40 ; no sign, go return without negate
neg edx
neg eax
sbb edx,0
;
; Store the result
;
remd40:
; results in (edx:eax)
pop esi
mov esp,ebp
pop ebp
stdRET _RtlExtendedMagicDivide
stdENDP _RtlExtendedMagicDivide
page
subttl "Extended Integer Multiply"
;++
;
; LARGE_INTEGER
; RtlExtendedIntegerMultiply (
; IN LARGE_INTEGER Multiplicand,
; IN ULONG Multiplier
; )
;
; Routine Description:
;
; This function multiplies a signed large integer by a signed integer and
; returns the signed large integer result.
;
; Arguments:
;
; (ebp+8,12)=multiplican (MCAN)
; (ebp+16)=multiplier (MPER)
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
ReimMCAN equ <dword ptr [ebp+8]>
ReimMPER equ <dword ptr [ebp+16]>
cPublicProc _RtlExtendedIntegerMultiply ,3
push ebp
mov ebp,esp
push esi
mov esi,ReimMPER
xor esi,ReimMCAN+4 ; (esi) = result sign
test ReimMCAN+4,80000000h
jz short reim10 ; MCAN pos, go look at MPER
neg dword ptr ReimMCAN+4
neg dword ptr ReimMCAN
sbb dword ptr ReimMCAN+4,0 ; negate multiplican
reim10: test ReimMPER,80000000h
jz short reim20 ; MPER pos, go do multiply
neg dword ptr ReimMPER ; negate multiplier
reim20: mov eax,ReimMPER
mul dword ptr ReimMCAN ; (edx:eax) = MPER * MCAN.low
push edx
mov ecx, eax
mov eax,ReimMPER
mul dword ptr ReimMCAN+4 ; (edx:eax) = MPER * MCAN.high
add eax,[esp] ; (eax) = hi part of MPER*MCAN.low
; plus low part of MPER*MCAN.hi
test esi,80000000h
jz short reim30 ; result sign is OK, go return
neg eax
neg ecx
sbb eax,0 ; negate result
reim30: add esp,4 ; clean eax off stack
pop esi ; restore nonvolatile reg
mov edx,eax ; (edx:ecx) = result
mov eax,ecx ; (edx:eax) = result
pop ebp
stdRET _RtlExtendedIntegerMultiply
stdENDP _RtlExtendedIntegerMultiply
page
subttl "Large Integer Shift Left"
;++
;
; LARGE_INTEGER
; RtlLargeIntegerShiftLeft (
; IN LARGE_INTEGER LargeInteger,
; IN CCHAR ShiftCount
; )
;
;
; Routine Description:
;
; This routine does a left logical shift of a large integer by a
; specified amount (ShiftCount) modulo 64.
;
; Arguments:
;
; LargeInteger - Supplies the large integer to be shifted
;
; ShiftCount - Supplies the left shift count
;
; Return Value:
;
; LARGE_INTEGER - Receives the shift large integer result
;
;--
cPublicProc _RtlLargeIntegerShiftLeft,3
cPublicFpo 3,0
mov ecx, [esp+12] ; (ecx) = ShiftCount
and ecx, 3fh ; mod 64
cmp ecx, 32
jnc short sl10
;
; Shift count is less then 32 bits.
;
mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
shld edx, eax, cl
shl eax, cl
stdRET _RtlLargeIntegerShiftLeft
align 4
sl10:
;
; Shift count is greater than or equal 32 bits - low half of result is zero,
; high half is the low half shifted left by remaining count.
;
mov edx, [esp+4] ; (edx) = LargeInteger.LowPart
xor eax, eax ; store lowpart
shl edx, cl ; store highpart
stdRET _RtlLargeIntegerShiftLeft
stdENDP _RtlLargeIntegerShiftLeft
page
subttl "Large Integer Shift Right"
;--
;
;LARGE_INTEGER
;RtlLargeIntegerShiftRight (
; IN LARGE_INTEGER LargeInteger,
; IN CCHAR ShiftCount
; )
;
;Routine Description:
;
; This routine does a right logical shift of a large integer by a
; specified amount (ShiftCount) modulo 64.
;
;Arguments:
;
; LargeInteger - Supplies the large integer to be shifted
;
; ShiftCount - Supplies the right shift count
;
;Return Value:
;
; LARGE_INTEGER - Receives the shift large integer result
;
;--*/
cPublicProc _RtlLargeIntegerShiftRight,3
cPublicFpo 3,0
mov ecx, [esp+12] ; (ecx) = ShiftCount
and ecx, 3fh ; mod 64
cmp ecx, 32
jnc short sr10
;
; Shift count is less then 32 bits.
;
mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
shrd eax, edx, cl
shr edx, cl
stdRET _RtlLargeIntegerShiftRight
align 4
sr10:
;
; Shift count is greater than or equal 32 bits - high half of result is zero,
; low half is the high half shifted right by remaining count.
;
mov eax, [esp+8] ; (eax) = LargeInteger.HighPart
xor edx, edx ; store highpart
shr eax, cl ; store lowpart
stdRET _RtlLargeIntegerShiftRight
stdENDP _RtlLargeIntegerShiftRight
;++
;
;LARGE_INTEGER
;RtlLargeIntegerArithmeticShift (
; IN LARGE_INTEGER LargeInteger,
; IN CCHAR ShiftCount
; )
;
;Routine Description:
;
; This routine does a right arithmetic shift of a large integer by a
; specified amount (ShiftCount) modulo 64.
;
;Arguments:
;
; LargeInteger - Supplies the large integer to be shifted
;
; ShiftCount - Supplies the right shift count
;
;Return Value:
;
; LARGE_INTEGER - Receives the shift large integer result
;
;--
cPublicProc _RtlLargeIntegerArithmeticShift,3
cPublicFpo 3,0
mov ecx, [esp+12] ; (ecx) = ShiftCount
and ecx, 3fh ; mod 64
cmp ecx, 32
jc short sar10
;
; Shift count is greater than or equal 32 bits - high half of result is sign
; bit, low half is the high half shifted right by remaining count.
;
mov eax, [esp+8] ; (eax) = LargeInteger.HighPart
sar eax, cl ; store highpart
bt eax, 31 ; sign bit set?
sbb edx, edx ; duplicate sign bit into highpart
stdRET _RtlLargeIntegerArithmeticShift
align 4
sar10:
;
; Shift count is less then 32 bits.
;
;
mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
shrd eax, edx, cl
sar edx, cl
stdRET _RtlLargeIntegerArithmeticShift
stdENDP _RtlLargeIntegerArithmeticShift,3
page
subttl "Large Integer Negate"
;++
;
; LARGE_INTEGER
; RtlLargeIntegerNegate (
; IN LARGE_INTEGER Subtrahend
; )
;
; Routine Description:
;
; This function negates a signed large integer and returns the signed
; large integer result.
;
; Arguments:
;
; (TOS+4) = Subtrahend
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc _RtlLargeIntegerNegate ,2
cPublicFpo 2,0
mov eax,[esp]+4 ; (eax) = lo
mov edx,[esp]+8
neg edx ; (edx) = 2's comp of hi part
neg eax ; if ((eax) == 0) CF = 0
; else CF = 1
sbb edx,0 ; (edx) = (edx) - CF
; (edx:eax) = result
stdRET _RtlLargeIntegerNegate
stdENDP _RtlLargeIntegerNegate
page
subttl "Large Integer Subtract"
;++
;
; LARGE_INTEGER
; RtlLargeIntegerSubtract (
; IN LARGE_INTEGER Minuend,
; IN LARGE_INTEGER Subtrahend
; )
;
; Routine Description:
;
; This function subtracts a signed large integer from a signed large
; integer and returns the signed large integer result.
;
; Arguments:
;
; (TOS+4) = Minuend
; (TOS+12) = Subtrahend
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc _RtlLargeIntegerSubtract ,4
cPublicFpo 4,0
mov eax,[esp]+4
sub eax,[esp]+12 ; (eax) = result.low
mov edx,[esp]+8
sbb edx,[esp]+16 ; (edx) = result.high
stdRET _RtlLargeIntegerSubtract
stdENDP _RtlLargeIntegerSubtract
page
subttl "Convert Long to Large Integer"
;++
;
; LARGE_INTEGER
; RtlConvertLongToLargeInteger (
; IN LONG SignedInteger
; )
;
; Routine Description:
;
; This function converts the input signed integer to a signed large
; integer and returns the latter as the result.
;
; Arguments:
;
; (TOS+4) = SignedInteger
;
; Return Value:
;
; The large integer result is stored (edx:eax)
;
;--
cPublicProc ___RtlConvertLongToLargeInteger ,1
cPublicFpo 1,0
mov eax,[esp]+4 ; (eax) = SignedInteger
cdq ; (edx:eax) = signed LargeInt
stdRET ___RtlConvertLongToLargeInteger
stdENDP ___RtlConvertLongToLargeInteger
page
subttl "Convert Ulong to Large Integer"
;++
;
; LARGE_INTEGER
; RtlConvertUlongToLargeInteger (
; IN LONG UnsignedInteger
; )
;
; Routine Description:
;
; This function converts the input unsigned integer to a signed large
; integer and returns the latter as the result.
;
; Arguments:
;
; (TOS+4) = UnsignedInteger
;
; Return Value:
;
; The large integer result is stored in (edx:eax)
;
;--
cPublicProc ___RtlConvertUlongToLargeInteger ,1
cPublicFpo 1,0
mov eax,[esp]+4 ; store low
xor edx,edx ; store 0 in high
stdRET ___RtlConvertUlongToLargeInteger
stdENDP ___RtlConvertUlongToLargeInteger
_TEXT$00 ends
end