title   "Critical Section Support"
;++
;
;  Copyright (c) 1991  Microsoft Corporation
;
;  Module Name:
;
;     critsect.asm
;
;  Abstract:
;
;     This module implements functions to support user mode interlocked operations.
;
;  Author:
;
;     Bryan M. Willman (bryanwi) 2-Oct-91
;
;  Environment:
;
;     Any mode.
;
;  Revision History:
;
;
;   WARNING!!!!!!!!!! Some of this code is duplicated in
;   ntos\dll\i386\critsect.asm
;
;   Some day we should put it in a .inc file that both include.
;
;--

.486p
        .xlist
include ks386.inc
include callconv.inc
        .list

_DATA   SEGMENT DWORD PUBLIC 'DATA'
    public _BasepLockPrefixTable
_BasepLockPrefixTable    label dword
        dd offset FLAT:Lock1
        dd offset FLAT:Lock2
        dd offset FLAT:Lock3
        dd offset FLAT:Lock4
        dd offset FLAT:Lock5
        dd 0
_DATA   ENDS


_TEXT   SEGMENT PARA PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING


Addend      equ     [esp + 4]
FlagMask    equ     0c0000000H
FlagShift   equ     24
FlagSelect  equ     30

        page , 132
        subttl  "InterlockedIncrement"

;++
;
; LONG
; InterlockedIncrement(
;    IN PLONG Addend
;    )
;
; Routine Description:
;
;    This function performs an interlocked add of one to the addend variable.
;
;    No checking is done for overflow.
;
; Arguments:
;
;    Addend - Supplies a pointer to a variable whose value is to be
;       incremented by one.
;
; Return Value:
;
;   (eax) - the incremented value.
;
;--

cPublicProc _InterlockedIncrement,1
cPublicFpo 1,0
        mov     ecx,Addend              ; get pointer to addend variable
        mov     eax,1                   ; set increment value
Lock1:
   lock xadd    [ecx],eax               ; interlocked increment
        inc     eax                     ; adjust return value
        stdRET _InterlockedIncrement    ;

stdENDP _InterlockedIncrement

        page , 132
        subttl  "InterlockedDecrment"
;++
;
; LONG
; InterlockedDecrement(
;    IN PLONG Addend
;    )
;
; Routine Description:
;
;    This function performs an interlocked add of -1 to the addend variable.
;
;    No checking is done for overflow
;
; Arguments:
;
;    Addend - Supplies a pointer to a variable whose value is to be
;       decremented by one.
;
; Return Value:
;
;   (eax) - The decremented value.
;
;--

cPublicProc _InterlockedDecrement,1
cPublicFpo 1,0

        mov     ecx,Addend              ; get pointer to addend variable
        mov     eax,-1                  ; set decrement value
Lock2:
   lock xadd    [ecx],eax               ; interlocked decrement
        dec     eax                     ; adjust return value
        stdRET _InterlockedDecrement    ;

stdENDP _InterlockedDecrement

        page , 132
        subttl  "Interlocked Exchange"
;++
;
; LONG
; InterlockedExchange(
;    IN OUT LPLONG Target,
;    IN LONG Value
;    )
;
; Routine Description:
;
;    This function atomically exchanges the Target and Value, returning
;    the prior contents of Target
;
; Arguments:
;
;    Target - Address of LONG to exchange
;    Value  - New value of LONG
;
; Return Value:
;
;    (eax) - The prior value of target.
;--

cPublicProc _InterlockedExchange, 2
cPublicFpo 2,0

        mov     ecx, [esp+4]                ; (ecx) = Target
        mov     edx, [esp+8]                ; (edx) = Value
        mov     eax, [ecx]                  ; get comperand value
Ixchg:
Lock5:
   lock cmpxchg [ecx], edx                  ; compare and swap
        jnz     Ixchg                       ; if nz, exchange failed
        stdRET  _InterlockedExchange

stdENDP _InterlockedExchange

        page , 132
        subttl  "Interlocked Compare Exchange"
;++
;
;   PVOID
;   InterlockedCompareExchange (
;       IN OUT PVOID *Destination,
;       IN PVOID Exchange,
;       IN PVOID Comperand
;       )
;
;   Routine Description:
;
;    This function performs an interlocked compare of the destination
;    value with the comperand value. If the destination value is equal
;    to the comperand value, then the exchange value is stored in the
;    destination. Otherwise, no operation is performed.
;
; Arguments:
;
;    Destination - Supplies a pointer to destination value.
;
;    Exchange - Supplies the exchange value.
;
;    Comperand - Supplies the comperand value.
;
; Return Value:
;
;    (eax) - The initial destination value.
;
;--

cPublicProc _InterlockedCompareExchange, 3
cPublicFpo 3,0

        mov     ecx, [esp + 4]          ; get destination address
        mov     edx, [esp + 8]          ; get exchange value
        mov     eax, [esp + 12]         ; get comperand value
Lock3:
   lock cmpxchg [ecx], edx              ; compare and exchange

        stdRET  _InterlockedCompareExchange

stdENDP _InterlockedCompareExchange

        page , 132
        subttl  "Interlocked Exchange Add"
;++
;
;   LONG
;   InterlockedExchangeAdd (
;       IN OUT PLONG Addend,
;       IN LONG Increment
;       )
;
;   Routine Description:
;
;    This function performs an interlocked add of an increment value to an
;    addend variable of type unsinged long. The initial value of the addend
;    variable is returned as the function value.
;
;       It is NOT possible to mix ExInterlockedDecrementLong and
;       ExInterlockedIncrementong with ExInterlockedAddUlong.
;
;
; Arguments:
;
;    Addend - Supplies a pointer to a variable whose value is to be
;       adjusted by the increment value.
;
;    Increment - Supplies the increment value to be added to the
;       addend variable.
;
; Return Value:
;
;    (eax) - The initial value of the addend.
;
;--

cPublicProc _InterlockedExchangeAdd, 2
cPublicFpo 2,0

        mov     ecx, [esp + 4]          ; get addend address
        mov     eax, [esp + 8]          ; get increment value
Lock4:
   lock xadd    [ecx], eax              ; exchange add

        stdRET  _InterlockedExchangeAdd

stdENDP _InterlockedExchangeAdd

        page , 132
        subttl  "Multiply and Divide"
;++
;
; LONG
; MulDiv(
;    IN LONG nNumber,
;    IN LONG nNumerator,
;    IN LONG nDenominator
;    )
;
; Routine Description:
;
;    This function multiples two 32-bit numbers forming a 64-bit product.
;    The 64-bit product is rounded and then divided by a 32-bit divisor
;    yielding a 32-bit result.
;
; Arguments:
;
;    nNumber - Supllies the multiplier.
;
;    nNumerator - Supplies the multiplicand.
;
;    nDenominator - Supplies the divisor.
;
; Return Value:
;
;    If the divisor is zero or an overflow occurs, then a value of -1 is
;    returned as the function value. Otherwise, the rounded quotient is
;    returned as the funtion value.
;
;--

nNumber      equ [esp + 4]
nNumerator   equ [esp + 8]
nDenominator equ DWORD PTR [esp + 12]

cPublicProc _MulDiv, 3
cPublicFpo 3,0
        mov     eax, nNumber            ; get multiplier absolute value
        or      eax, eax                ;
        js      short MD32_First        ; if s, multiplier is negative

;
; The multiplier is positive.
;

        mov     edx, nNumerator         ; get multiplicand absolute value
        or      edx, edx                ;
        js      MD32_Second             ; if s, multiplicand is negative

;
; The multiplicand is positive.
;

        mul     edx                     ; compute 64-bit product
        mov     ecx, nDenominator       ; get denominator absolute value
        or      ecx, ecx                ;
        js      MD32_Third              ; if s, divisor is negative

;
; The divisor is positive.
;

        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        cmp     edx, nDenominator       ; check for overflow
        jae     short MD32_error        ; if ae, overflow or divide by 0
        div     nDenominator            ; compute quotient

;
; The result is postive.
;

        or      eax, eax                ; check for overflow
        js      short MD32_error        ; if s, overlfow has occured

        stdRET  _MulDiv

MD32_error:
        xor     eax, eax                ; set return value to - 1
        dec     eax                     ;

        stdRET  _MulDiv

;
; The multiplier is negative.
;

MD32_First:                             ;
        neg     eax                     ; negate multiplier
        mov     edx, nNumerator         ; get multiplicand absolute value
        or      edx, edx                ;
        js      short MD32_First10      ; if s, multiplicand is negative

;
; The multiplicand is positive.
;

        mul     edx                     ; compute 64-bit product
        mov     ecx, nDenominator       ; get denominator absolute value
        or      ecx, ecx                ;
        js      short MD32_First20      ; if s, divisor is negative

;
; The divisor is positive.
;

        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        cmp     edx, nDenominator       ; check for overflow
        jae     short MD32_error10      ; if ae, overflow or divide by 0
        div     nDenominator            ; compute quotient

;
; The result is negative.
;

        neg     eax                     ; negate result
        jg      short MD32_error10      ; if g, overlfow has occured

        stdRET  _MulDiv

;
; The multiplier is negative and the multiplicand is negative.
;

MD32_First10:                           ;
        neg     edx                     ; negate multiplicand
        mul     edx                     ; compute 64-bit product
        mov     ecx, nDenominator       ; get denominator absolute value
        or      ecx, ecx                ;
        js      short MD32_First30      ; if s, divisor is negative

;
; The divisor is positive.
;

        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        cmp     edx, nDenominator       ; check for overflow
        jae     short MD32_error10      ; if ae, overflow or divide by 0
        div     nDenominator            ; compute quotient

;
; The result is positive.
;

        or      eax, eax                ; check for overflow
        js      short MD32_error10      ; if s, overlfow has occured

        stdRET  _MulDiv

MD32_error10:                           ;
        xor     eax, eax                ; set return value to - 1
        dec     eax                     ;

        stdRET  _MulDiv


;
; The multiplier is negative, the multiplicand is positive, and the
; divisor is negative.
;

MD32_First20:                           ;
        neg     ecx                     ; negate divisor
        push    ecx                     ; save absolute value of divisor
        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        pop     ecx                     ; restore divisor
        cmp     edx, ecx                ; check for overflow
        jae     short MD32_error10      ; if ae, overflow or divide by 0
        div     ecx                     ; compute quotient

;
; The result is postive.
;

        or      eax, eax                ; check for overflow
        js      short MD32_error10      ; if s, overlfow has occured

        stdRET  _MulDiv

;
; The multiplier is negative, the multiplier is negative, and the divisor
; is negative.
;

MD32_First30:                           ;
        neg     ecx                     ; negate divisor
        push    ecx                     ; save absolute value of divisor
        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        pop     ecx                     ; restore divisor
        cmp     edx, ecx                ; check for overflow
        jae     short MD32_error10      ; if ae, overflow or divide by 0
        div     ecx                     ; compute quotient

;
; The result is negative.
;

        neg     eax                     ; negate result
        jg      short MD32_error10      ; if g, overlfow has occured

        stdRET  _MulDiv

;
; The multiplier is positive and the multiplicand is negative.
;

MD32_Second:                            ;
        neg     edx                     ; negate multiplicand
        mul     edx                     ; compute 64-bit product
        mov     ecx, nDenominator       ; get denominator absolute value
        or      ecx, ecx                ;
        js      short MD32_Second10     ; if s, divisor is negative

;
; The divisor is positive.
;

        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        cmp     edx, nDenominator       ; check for overflow
        jae     short MD32_error20      ; if ae, overflow or divide by 0
        div     nDenominator            ; compute quotient

;
; The result is negative.
;

        neg     eax                     ; check for overflow
        jg      short MD32_error20      ; if g, overlfow has occured

        stdRET  _MulDiv

MD32_error20:                           ;
        xor     eax, eax                ; set return value to - 1
        dec     eax                     ;

        stdRET  _MulDiv

;
; The multiplier is positive, the multiplicand is negative, and the divisor
; is negative.
;

MD32_Second10:                          ;
        neg     ecx                     ; negate divisor
        push    ecx                     ; save absolute value of divisor
        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        pop     ecx                     ; restore divisor
        cmp     edx, ecx                ; check for overflow
        jae     short MD32_error20      ; if ae, overflow or divide by 0
        div     ecx                     ; compute quotient

;
; The result is positive.
;

        or      eax, eax                ; check for overflow
        js      short MD32_error10      ; if s, overlfow has occured

        stdRET  _MulDiv

;
; The multiplier is positive, the multiplicand is positive, the divisor
; is negative.
;

MD32_Third:                             ;
        neg     ecx                     ; negate divisor
        push    ecx                     ; save absolute value of divisor
        sar     ecx, 1                  ; compute rounding value
        add     eax, ecx                ; round the 64-bit produce by the
        adc     edx, 0                  ; divisor / 2
        pop     ecx                     ; restore divisor
        cmp     edx, ecx                ; check for overflow
        jae     short MD32_error20      ; if ae, overflow or divide by 0
        div     ecx                     ; compute quotient

;
; The result is negative.
;

        neg     eax                     ; negate result
        jg      short MD32_error20      ; if g, overflow has occured

        stdRET  _MulDiv

stdENDP _MulDiv

_TEXT   ends
        end