        TITLE   "Large Integer Arithmetic"
;++
;
; Module Name:
;
;    largeint.s
;
; Abstract:
;
;    This module implements routines for performing extended integer
;    arithmtic.
;
; Environment:
;
;    Any mode.
;
; Revision History:
;
;--

.386p
        .xlist
include callconv.inc            ; calling convention macros
        .list


_TEXT   SEGMENT DWORD USE32 PUBLIC 'CODE'
        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        page ,132
        subttl  "LargeIntegerAdd"
;++
;
; LARGE_INTEGER
; LargeIntegerAdd (
;    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 _LargeIntegerAdd ,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    _LargeIntegerAdd

stdENDP _LargeIntegerAdd


        page
        subttl  "Enlarged Integer Multiply"
;++
;
; LARGE_INTEGER
; EnlargedIntegerMultiply (
;    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 _EnlargedIntegerMultiply ,2
cPublicFpo 2,0

        mov     eax,[esp]+4             ; (eax) = factor1
        imul    dword ptr [esp]+8       ; (edx:eax) = signed result
        stdRET    _EnlargedIntegerMultiply

stdENDP _EnlargedIntegerMultiply


        page
        subttl  "Enlarged Unsigned Integer Multiply"
;++
;
; LARGE_INTEGER
; EnlargedUnsignedMultiply (
;    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 _EnlargedUnsignedMultiply ,2
cPublicFpo 2,0

        mov     eax,[esp]+4             ; (eax) = factor1
        mul     dword ptr [esp]+8       ; (edx:eax) = unsigned result
        stdRET    _EnlargedUnsignedMultiply

stdENDP _EnlargedUnsignedMultiply

        page
        subttl  "Enlarged Unsigned Integer Divide"

;++
;
; ULONG
; EnlargedUnsignedDivide (
;    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.
;
;    N.B. It is assumed that no overflow will occur.
;
; 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 _EnlargedUnsignedDivide,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    _EnlargedUnsignedDivide    ; (eax) = Quotient

align 4
@@:     mov     [ecx], edx              ; save remainder
        stdRET    _EnlargedUnsignedDivide    ; (eax) = Quotient

stdENDP _EnlargedUnsignedDivide

        page
        subttl  "Extended Large Integer Divide"

;++
;
; LARGE_INTEGER
; ExtendedLargeIntegerDivide (
;     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 _ExtendedLargeIntegerDivide, 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  _ExtendedLargeIntegerDivide

align 4
lid20:
        mov     [ecx], esi          ; store remainder
        pop     esi
        stdRET  _ExtendedLargeIntegerDivide

lid_zero:
        div     dword ptr [esp+24]  ; cause divide by zero exception
        pop     ebx
        pop     edi
        pop     esi
        stdRET  _ExtendedLargeIntegerDivide
stdENDP     _ExtendedLargeIntegerDivide

        page
        subttl  "Extended Magic Divide"
;++
;
; LARGE_INTEGER
; ExtendedMagicDivide (
;    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 _ExtendedMagicDivide ,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    _ExtendedMagicDivide

stdENDP _ExtendedMagicDivide


        page
        subttl  "Extended Integer Multiply"
;++
;
; LARGE_INTEGER
; ExtendedIntegerMultiply (
;    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 [ebp+8]
ReimMPER    equ [ebp+16]

cPublicProc _ExtendedIntegerMultiply ,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    _ExtendedIntegerMultiply

stdENDP _ExtendedIntegerMultiply

        page
        subttl  "Large Integer Shift Left"
;++
;
; LARGE_INTEGER
; LargeIntegerShiftLeft (
;     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 _LargeIntegerShiftLeft,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  _LargeIntegerShiftLeft

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  _LargeIntegerShiftLeft

stdENDP _LargeIntegerShiftLeft

        page
        subttl  "Large Integer Shift Right"

;--
;
;LARGE_INTEGER
;LargeIntegerShiftRight (
;    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 _LargeIntegerShiftRight,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  _LargeIntegerShiftRight

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  _LargeIntegerShiftRight

stdENDP _LargeIntegerShiftRight

;++
;
;LARGE_INTEGER
;LargeIntegerArithmeticShift (
;    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 _LargeIntegerArithmeticShift,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  _LargeIntegerArithmeticShift

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  _LargeIntegerArithmeticShift

stdENDP _LargeIntegerArithmeticShift,3


        page
        subttl  "Large Integer Negate"
;++
;
; LARGE_INTEGER
; LargeIntegerNegate (
;    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 _LargeIntegerNegate  ,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    _LargeIntegerNegate

stdENDP _LargeIntegerNegate


        page
        subttl  "Large Integer Subtract"
;++
;
; LARGE_INTEGER
; LargeIntegerSubtract (
;    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 _LargeIntegerSubtract    ,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    _LargeIntegerSubtract

stdENDP _LargeIntegerSubtract

        page
        subttl  "Convert Long to Large Integer"
;++
;
; LARGE_INTEGER
; ConvertLongToLargeInteger (
;     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 _ConvertLongToLargeInteger   ,1
cPublicFpo 1,0

        mov     eax,[esp]+4             ; (eax) = SignedInteger
        cdq                             ; (edx:eax) = signed LargeInt
        stdRET    _ConvertLongToLargeInteger

stdENDP _ConvertLongToLargeInteger


        page
        subttl  "Convert Ulong to Large Integer"
;++
;
; LARGE_INTEGER
; ConvertUlongToLargeInteger (
;     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 _ConvertUlongToLargeInteger  ,1
cPublicFpo 1,0

        mov     eax,[esp]+4             ; store low
        xor     edx,edx                 ; store 0 in high
        stdRET    _ConvertUlongToLargeInteger

stdENDP _ConvertUlongToLargeInteger


_TEXT   ends
        end
