        title  "Interlocked Support"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    intrlfst.asm
;
; Abstract:
;
;    This module implements functions to support interlocked operations.
;    Interlocked operations can only operate on nonpaged data.
;
;    This module implements the fast call version of the interlocked
;    functions.
;
;--
.386p
        .xlist
include ks386.inc
include callconv.inc                    ; calling convention macros
include mac386.inc
        .list

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

;++
;
;   General Notes on Interlocked Procedures:
;
;       These procedures assume that neither their code, nor any of
;       the data they touch, will cause a page fault.
;
;       They use spinlocks to achieve MP atomicity, iff it's an MP machine.
;       (The spinlock macros generate zilch if NT_UP = 1, and
;        we if out some aux code here as well.)
;
;       They turn off interrupts so that they can be used for synchronization
;       between ISRs and driver code.  Flags are preserved so they can
;       be called in special code (Like IPC interrupt handlers) that
;       may have interrupts off.
;
;--


;;      align  512

        page ,132
        subttl  "ExInterlockedAddLargeStatistic"
;++
;
; VOID
; FASTCALL
; ExInterlockedAddLargeStatistic (
;    IN PLARGE_INTEGER Addend,
;    IN ULONG Increment
;    )
;
; Routine Description:
;
;    This function performs an interlocked add of an increment value to an
;    addend variable of type unsigned large integer.
;
; Arguments:
;
;    (ecx) Addend - Supplies a pointer to the variable whose value is
;                     adjusted by the increment value.
;
;    (edx) Increment - Supplies the increment value that is added to the
;                      addend variable.
;
; Return Value:
;
;    None.
;
;--

cPublicFastCall ExInterlockedAddLargeStatistic, 2
cPublicFpo 0,0

ifdef NT_UP

        add dword ptr [ecx], edx        ; add low part of large statistic
        adc dword ptr [ecx+4], 0        ; add carry to high part

else

        lock add dword ptr [ecx], edx   ; add low part of large statistic
        jc      short Eils10            ; if c, add generated a carry
        fstRET  ExInterlockedAddLargeStatistic ; return

Eils10: lock adc dword ptr [ecx+4], 0   ; add carry to high part

endif

        fstRET  ExInterlockedAddLargeStatistic ; return

fstENDP ExInterlockedAddLargeStatistic

        page , 132
        subttl  "Interlocked Add Unsigned Long"
;++
;
; ULONG
; FASTCALL
; ExfInterlockedAddUlong (
;    IN PULONG Addend,
;    IN ULONG Increment,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function performs an interlocked add of an increment value to an
;    addend variable of type unsigned 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:
;
;    (ecx)  Addend - Supplies a pointer to a variable whose value is to be
;                    adjusted by the increment value.
;
;    (edx) Increment - Supplies the increment value to be added to the
;                      addend variable.
;
;    (esp+4) Lock - Supplies a pointer to a spin lock to be used to synchronize
;                   access to the addend variable.
;
; Return Value:
;
;    The initial value of the addend variable.
;
;--

cPublicFastCall ExfInterlockedAddUlong, 3
cPublicFpo 1, 1

ifdef NT_UP
;
; UP version of ExInterlockedAddUlong
;

        pushfd
        cli                             ; disable interrupts

        mov     eax, [ecx]              ; (eax)= initial addend value
        add     [ecx], edx              ; [ecx]=adjusted value

        popfd                           ; restore flags including ints
        fstRET  ExfInterlockedAddUlong

else

;
; MP version of ExInterlockedAddUlong
;
        pushfd
        mov     eax, [esp+8]            ; (eax) = SpinLock
Eial10: cli                             ; disable interrupts
        ACQUIRE_SPINLOCK eax, <short Eial20>

        mov     eax, [ecx]              ; (eax)=initial addend value
        add     [ecx], edx              ; [ecx]=adjusted value

        mov     edx, [esp+8]            ; (edx) = SpinLock
        RELEASE_SPINLOCK edx
        popfd
        fstRET    ExfInterlockedAddUlong

Eial20: popfd
        pushfd
        SPIN_ON_SPINLOCK eax, <short Eial10>
endif

fstENDP ExfInterlockedAddUlong

        page , 132
        subttl  "Interlocked Insert Head List"
;++
;
; PLIST_ENTRY
; ExfInterlockedInsertHeadList (
;    IN PLIST_ENTRY ListHead,
;    IN PLIST_ENTRY ListEntry,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function inserts an entry at the head of a doubly linked list
;    so that access to the list is synchronized in a multiprocessor system.
;
;    N.B. The pages of data which this routine operates on MUST be
;         present.  No page fault is allowed in this routine.
;
; Arguments:
;
;   (ecx) = ListHead - Supplies a pointer to the head of the doubly linked
;                       list into which an entry is to be inserted.
;
;   (edx) = ListEntry - Supplies a pointer to the entry to be inserted at the
;                       head of the list.
;
;   (esp+4)  Lock - Supplies a pointer to a spin lock to be used to synchronize
;                   access to the list.
;
; Return Value:
;
;    Pointer to entry that was at the head of the list or NULL if the list
;    was empty.
;
;--

cPublicFastCall ExfInterlockedInsertHeadList    , 3
cPublicFpo 1, 1

ifndef NT_UP
cPublicFpo 1, 2
        push    esi
        mov     esi, [esp+8]            ; Address of spinlock
endif
        pushfd

Eiih10: cli
        ACQUIRE_SPINLOCK    esi,<short Eiih20>

        mov     eax, LsFlink[ecx]       ; (eax)->next entry in the list
        mov     [edx]+LsFlink, eax      ; store next link in entry
        mov     [edx]+LsBlink, ecx      ; store previous link in entry
        mov     [ecx]+LsFlink, edx      ; store next link in head
        mov     [eax]+LsBlink, edx      ; store previous link in next

        RELEASE_SPINLOCK esi
        popfd
ifndef NT_UP
        pop     esi
endif
        xor     eax, ecx                ; return null if list was empty
        jz      short Eiih15
        xor     eax, ecx
Eiih15: fstRET  ExfInterlockedInsertHeadList

ifndef NT_UP
Eiih20: popfd
        pushfd
        SPIN_ON_SPINLOCK esi, <short Eiih10>
endif

fstENDP ExfInterlockedInsertHeadList

        page , 132
        subttl  "Interlocked Insert Tail List"
;++
;
; PLIST_ENTRY
; FASTCALL
; ExfInterlockedInsertTailList (
;    IN PLIST_ENTRY ListHead,
;    IN PLIST_ENTRY ListEntry,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function inserts an entry at the tail of a doubly linked list
;    so that access to the list is synchronized in a multiprocessor system.
;
;    N.B. The pages of data which this routine operates on MUST be
;         present.  No page fault is allowed in this routine.
;
; Arguments:
;
;   (ecx) =  ListHead - Supplies a pointer to the head of the doubly linked
;            list into which an entry is to be inserted.
;
;   (edx) =  ListEntry - Supplies a pointer to the entry to be inserted at the
;            tail of the list.
;
;   (esp+4)  Lock - Supplies a pointer to a spin lock to be used to synchronize
;            access to the list.
;
; Return Value:
;
;    Pointer to entry that was at the tail of the list or NULL if the list
;    was empty.
;
;--

cPublicFastCall ExfInterlockedInsertTailList, 3
cPublicFpo 1, 1

ifndef NT_UP
cPublicFpo 1, 2
        push    esi
        mov     esi, [esp+8]            ; Address of spinlock
endif
        pushfd

Eiit10: cli
        ACQUIRE_SPINLOCK    esi,<short Eiit20>

        mov     eax, LsBlink[ecx]       ; (eax)->prev entry in the list
        mov     [edx]+LsFlink, ecx      ; store next link in entry
        mov     [edx]+LsBlink, eax      ; store previous link in entry
        mov     [ecx]+LsBlink, edx      ; store next link in head
        mov     [eax]+LsFlink, edx      ; store previous link in next

        RELEASE_SPINLOCK esi
        popfd

ifndef NT_UP
        pop     esi
endif
        xor     eax, ecx                ; return null if list was empty
        jz      short Eiit15
        xor     eax, ecx
Eiit15: fstRET  ExfInterlockedInsertTailList

ifndef NT_UP
Eiit20: popfd
        pushfd
        SPIN_ON_SPINLOCK esi, <short Eiit10>
endif

fstENDP ExfInterlockedInsertTailList


        page , 132
        subttl  "Interlocked Remove Head List"
;++
;
; PLIST_ENTRY
; FASTCALL
; ExfInterlockedRemoveHeadList (
;    IN PLIST_ENTRY ListHead,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function removes an entry from the head of a doubly linked list
;    so that access to the list is synchronized in a multiprocessor system.
;    If there are no entries in the list, then a value of NULL is returned.
;    Otherwise, the address of the entry that is removed is returned as the
;    function value.
;
;    N.B. The pages of data which this routine operates on MUST be
;         present.  No page fault is allowed in this routine.
;
; Arguments:
;
;    (ecx) ListHead - Supplies a pointer to the head of the doubly linked
;          list from which an entry is to be removed.
;
;    (edx) Lock - Supplies a pointer to a spin lock to be used to synchronize
;          access to the list.
;
; Return Value:
;
;    The address of the entry removed from the list, or NULL if the list is
;    empty.
;
;--

cPublicFastCall ExfInterlockedRemoveHeadList    , 2
cPublicFpo 0, 1
ifdef NT_UP
;
; UP version
;
        pushfd
        cli

        mov     eax, [ecx]+LsFlink      ; (eax)-> next entry
        cmp     eax, ecx                ; Is list empty?
        je      short Eirh20            ; if e, list is empty, go Eirh20

        mov     edx, [eax]+LsFlink      ; (ecx)-> next entry(after deletion)
        mov     [ecx]+LsFlink, edx      ; store address of next in head
        mov     [edx]+LsBlink, ecx      ; store address of previous in next
if DBG
        mov     [eax]+LsFlink, 0baddd0ffh
        mov     [eax]+LsBlink, 0baddd0ffh
endif
        popfd                           ; restore flags including interrupts
        fstRET    ExfInterlockedRemoveHeadList

Eirh20: popfd
        xor     eax,eax                 ; (eax) = null for empty list
        fstRET  ExfInterlockedRemoveHeadList

else
;
; MP version
;

Eirh40: pushfd
        cli
        ACQUIRE_SPINLOCK edx, <short Eirh60>

        mov     eax, [ecx]+LsFlink      ; (eax)-> next entry
        cmp     eax, ecx                ; Is list empty?
        je      short Eirh50            ; if e, list is empty, go Eirh50

cPublicFpo 0,2
        push    ebx

        mov     ebx, [eax]+LsFlink      ; (ecx)-> next entry(after deletion)
        mov     [ecx]+LsFlink, ebx      ; store address of next in head
        mov     [ebx]+LsBlink, ecx      ; store address of previous in next
if DBG
        mov     ebx, 0badd0ffh
        mov     [eax]+LsFlink, ebx
        mov     [eax]+LsBlink, ebx
endif
        RELEASE_SPINLOCK  edx

cPublicFpo 0, 0
        pop     ebx
        popfd                           ; restore flags including interrupts
        fstRET  ExfInterlockedRemoveHeadList

Eirh50: RELEASE_SPINLOCK  edx
        popfd
        xor     eax,eax                 ; (eax) = null for empty list
        fstRET  ExfInterlockedRemoveHeadList

cPublicFpo 0, 0
Eirh60: popfd
        SPIN_ON_SPINLOCK edx, <Eirh40>
        fstRET  ExfInterlockedRemoveHeadList

endif   ; nt_up

fstENDP ExfInterlockedRemoveHeadList

        page , 132
        subttl  "Interlocked Pop Entry List"
;++
;
; PSINGLE_LIST_ENTRY
; FASTCALL
; ExfInterlockedPopEntryList (
;    IN PSINGLE_LIST_ENTRY ListHead,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function removes an entry from the front of a singly linked list
;    so that access to the list is synchronized in a multiprocessor system.
;    If there are no entries in the list, then a value of NULL is returned.
;    Otherwise, the address of the entry that is removed is returned as the
;    function value.
;
; Arguments:
;
;    (ecx) = ListHead - Supplies a pointer to the head of the singly linked
;            list from which an entry is to be removed.
;
;    (edx) = Lock - Supplies a pointer to a spin lock to be used to synchronize
;            access to the list.
;
; Return Value:
;
;    The address of the entry removed from the list, or NULL if the list is
;    empty.
;
;--

cPublicFastCall ExfInterlockedPopEntryList      , 2

ifdef NT_UP
;
; UP version
;
cPublicFpo 0,1
        pushfd
        cli                             ; disable interrupts

        mov     eax, [ecx]              ; (eax)-> next entry
        or      eax, eax                ; Is it empty?
        je      short Eipe05            ; if e, empty list, go Eipe05
        mov     edx, [eax]              ; (edx)->next entry (after deletion)
        mov     [ecx], edx              ; store address of next in head
if DBG
        mov     [eax], 0baddd0ffh
endif
cPublicFpo 0,0
        popfd                           ; restore flags including interrupts
        fstRET    ExfInterlockedPopEntryList    ; cReturn (eax)->removed entry

Eipe05: popfd
        xor     eax,eax
        fstRET  ExfInterlockedPopEntryList    ; cReturn (eax)=NULL

else    ; nt_up

;
; MP Version
;

cPublicFpo 0,1

Eipe10: pushfd
        cli                             ; disable interrupts

        ACQUIRE_SPINLOCK edx, <short Eipe30>

        mov     eax, [ecx]              ; (eax)-> next entry
        or      eax, eax                ; Is it empty?
        je      short Eipe20            ; if e, empty list, go Eipe20
cPublicFpo 0,2
        push    edx                     ; Save SpinLock address
        mov     edx, [eax]              ; (edx)->next entry (after deletion)
        mov     [ecx], edx              ; store address of next in head
        pop     edx                     ; Restore SpinLock address
if DBG
        mov     [eax], 0baddd0ffh
endif
        RELEASE_SPINLOCK edx

cPublicFpo 0,0
        popfd                           ; restore flags including interrupts
        fstRET    ExfInterlockedPopEntryList

Eipe20: RELEASE_SPINLOCK edx
        popfd
        xor     eax,eax
        fstRET    ExfInterlockedPopEntryList

Eipe30: popfd
        SPIN_ON_SPINLOCK edx, Eipe10

endif   ; nt_up

fstENDP ExfInterlockedPopEntryList

        page , 132
        subttl  "Interlocked Push Entry List"
;++
;
; PSINGLE_LIST_ENTRY
; FASTCALL
; ExInterlockedPushEntryList (
;    IN PSINGLE_LIST_ENTRY ListHead,
;    IN PSINGLE_LIST_ENTRY ListEntry,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function inserts an entry at the head of a singly linked list
;    so that access to the list is synchronized in a multiprocessor system.
;
; Arguments:
;
;    (ecx) ListHead - Supplies a pointer to the head of the singly linked
;          list into which an entry is to be inserted.
;
;    (edx) ListEntry - Supplies a pointer to the entry to be inserted at the
;          head of the list.
;
;    (esp+4) Lock - Supplies a pointer to a spin lock to be used to synchronize
;            access to the list.
;
; Return Value:
;
;    Previous contents of ListHead.  NULL implies list went from empty
;       to not empty.
;
;--

cPublicFastCall ExfInterlockedPushEntryList     , 3
ifdef NT_UP
;
; UP Version
;
cPublicFpo 0,1
        pushfd
        cli

        mov     eax, [ecx]              ; (eax)-> next entry (return value also)
        mov     [edx], eax              ; store address of next in new entry
        mov     [ecx], edx              ; set address of next in head

cPublicFpo 0,0
        popfd                           ; restore flags including interrupts
        fstRET    ExfInterlockedPushEntryList

else
;
; MP Version
;

cPublicFpo 1,1
        pushfd
        push    edx
        mov     edx, [esp+12]           ; (edx) = SpinLock

Eipl10: cli
        ACQUIRE_SPINLOCK edx, <short Eipl20>

        pop     edx                     ; (edx)-> Entry to be pushed
        mov     eax, [ecx]              ; (eax)-> next entry (return value also)
        mov     [edx], eax              ; store address of next in new entry
        mov     [ecx], edx              ; set address of next in head

        mov     edx, [esp+8]            ; (edx) = SpinLock
        RELEASE_SPINLOCK edx

cPublicFpo 0,0
        popfd                           ; restore flags including interrupts
        fstRET    ExfInterlockedPushEntryList

cPublicFpo 1,2
Eipl20: pop     edx
        popfd                           ; Restore interrupt state

        pushfd
        push    edx
        mov     edx, [esp+12]
        SPIN_ON_SPINLOCK edx, <short Eipl10>
endif

fstENDP ExfInterlockedPushEntryList

        page , 132

        subttl  "Interlocked i386 Increment Long"
;++
;
;   INTERLOCKED_RESULT
;   FASTCALL
;   Exfi386InterlockedIncrementLong (
;       IN PLONG Addend
;       )
;
;   Routine Description:
;
;       This function atomically increments Addend, returning an ennumerated
;       type which indicates what interesting transitions in the value of
;       Addend occurred due the operation.
;
;       See ExInterlockedIncrementLong.  This function is the i386
;       architectural specific version of ExInterlockedIncrementLong.
;       No source directly calls this function, instead
;       ExInterlockedIncrementLong is called and when built on x86 these
;       calls are macro-ed to the i386 optimized version.
;
;   Arguments:
;
;       (ecx) Addend - Pointer to variable to increment.
;
;   Return Value:
;
;       An ennumerated type:
;
;       ResultNegative if Addend is < 0 after increment.
;       ResultZero     if Addend is = 0 after increment.
;       ResultPositive if Addend is > 0 after increment.
;
;--

cPublicFastCall Exfi386InterlockedIncrementLong, 1
cPublicFpo 0, 0

ifdef NT_UP
        add dword ptr [ecx],1
else
        lock add dword ptr [ecx],1
endif
        lahf                            ; (ah) = flags
        and     eax,EFLAG_SELECT        ; clear all but sign and zero flags
        fstRET  Exfi386InterlockedIncrementLong

fstENDP Exfi386InterlockedIncrementLong


        page , 132
        subttl  "Interlocked i386 Decrement Long"
;++
;
;   INTERLOCKED_RESULT
;   FASTCALL
;   Exfi386InterlockedDecrementLong (
;       IN PLONG Addend,
;       IN PKSPIN_LOCK Lock
;       )
;
;   Routine Description:
;
;       This function atomically decrements Addend, returning an ennumerated
;       type which indicates what interesting transitions in the value of
;       Addend occurred due the operation.
;
;       See Exi386InterlockedDecrementLong.  This function is the i386
;       architectural specific version of ExInterlockedDecrementLong.
;       No source directly calls this function, instead
;       ExInterlockedDecrementLong is called and when built on x86 these
;       calls are macro-ed to the i386 optimized version.
;
;   Arguments:
;
;       Addend (esp+4) - Pointer to variable to decrement.
;
;       Lock (esp+8) - Spinlock used to implement atomicity.
;                      (not actually used on x86)
;
;   Return Value:
;
;       An ennumerated type:
;
;       ResultNegative if Addend is < 0 after decrement.
;       ResultZero     if Addend is = 0 after decrement.
;       ResultPositive if Addend is > 0 after decrement.
;
;--

cPublicFastCall Exfi386InterlockedDecrementLong , 1
cPublicFpo 0, 0

ifdef NT_UP
        sub dword ptr [ecx], 1
else
        lock sub dword ptr [ecx], 1
endif
        lahf                            ; (ah) = flags
        and     eax, EFLAG_SELECT       ; clear all but sign and zero flags
        fstRET  Exfi386InterlockedDecrementLong

fstENDP Exfi386InterlockedDecrementLong

        page , 132
        subttl  "Interlocked i386 Exchange Ulong"
;++
;
;   ULONG
;   FASTCALL
;   Exfi386InterlockedExchangeUlong (
;       IN PULONG Target,
;       IN ULONG Value
;       )
;
;   Routine Description:
;
;       This function atomically exchanges the Target and Value, returning
;       the prior contents of Target
;
;       See Exi386InterlockedExchangeUlong.  This function is the i386
;       architectural specific version of ExInterlockedDecrementLong.
;       No source directly calls this function, instead
;       ExInterlockedDecrementLong is called and when built on x86 these
;       calls are macro-ed to the i386 optimized version.
;
;   Arguments:
;
;       (ecx) = Source - Address of ULONG to exchange
;       (edx) = Value  - New value of ULONG
;
;   Return Value:
;
;       The prior value of Source
;--

cPublicFastCall Exfi386InterlockedExchangeUlong, 2
cPublicFpo 0,0

.486
ifndef NT_UP
        xchg    [ecx], edx                  ; make the exchange
        mov     eax, edx
else
        mov     eax, [ecx]                  ; get comperand value
Ixchg:  cmpxchg [ecx], edx                  ; compare and swap
        jnz     Ixchg                       ; if nz, exchange failed
endif
.386

        fstRET  Exfi386InterlockedExchangeUlong
fstENDP Exfi386InterlockedExchangeUlong

;++
;
; 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 (ecx) - Supplies a pointer to a variable whose value is to be
;       incremented by one.
;
; Return Value:
;
;   (eax) - The incremented value.
;
;--

cPublicFastCall __InterlockedIncrement,1
cPublicFpo 0,0

        mov     eax, 1                  ; set increment value

.486
ifndef NT_UP
   lock xadd    [ecx], eax              ; interlocked increment
else
        xadd    [ecx], eax              ; interlocked increment
endif
.386p
        inc     eax                     ; adjust return value

        fstRET __InterlockedIncrement

fstENDP __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 (ecx) - Supplies a pointer to a variable whose value is to be
;       decremented by one.
;
; Return Value:
;
;   (eax) - The decremented value.
;
;--

cPublicFastCall __InterlockedDecrement,1
cPublicFpo 0,0

        mov     eax, -1                 ; set decrment value

.486
ifndef NT_UP
   lock xadd    [ecx], eax              ; interlocked decrement
else
        xadd    [ecx], eax              ; interlocked decrement
endif
.386

        dec     eax                     ; adjust return value

        fstRET __InterlockedDecrement

fstENDP __InterlockedDecrement

        page , 132
        subttl  "Interlocked Compare Exchange"
;++
;
;   PVOID
;   FASTCALL
;   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:
;
;    (ecx)  Destination - Supplies a pointer to destination value.
;
;    (edx) Exchange - Supplies the exchange value.
;
;    [esp + 4] Comperand - Supplies the comperand value.
;
; Return Value:
;
;    The initial destination value is returned as the function value.
;
;--

cPublicFastCall __InterlockedCompareExchange, 3
cPublicFpo 0,0

        mov     eax, [esp + 4]          ; set comperand value
.486
ifndef NT_UP
   lock cmpxchg [ecx], edx              ; compare and exchange
else
        cmpxchg [ecx], edx              ; compare and exchange
endif
.386

        fstRET  __InterlockedCompareExchange

fstENDP __InterlockedCompareExchange

        subttl  "Interlocked Compare Exchange 64-bit - without lock"
;++
;
; LONGLONG
; FASTCALL
; ExfInterlockedCompareExchange64 (
;    IN OUT PLONGLONG Destination,
;    IN PLONGLONG Exchange,
;    IN PLONGLONG Comperand
;    )
;
; Routine Description:
;
;    This function performs a compare and exchange of 64-bits.
;
; Arguments:
;
;    (ecx) Destination - Supplies a pointer to the destination variable.
;
;    (edx) Exchange - Supplies a pointer to the exchange value.
;
;    (esp+4) Comperand - Supplies a pointer to the comperand value.
;
; Return Value:
;
;    The current destination value is returned as the function value.
;
;--

cPublicFastCall ExfInterlockedCompareExchange64, 3

cPublicFpo 0,2

;
; Save nonvolatile registers and read the exchange and comperand values.
;

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, ecx                ; set destination address
        mov     ebx, [edx]              ; get exchange value
        mov     ecx, [edx] + 4          ;
        mov     edx, [esp] + 12         ; get comperand address
        mov     eax, [edx]              ; get comperand value
        mov     edx, [edx] + 4          ;

.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr[ebp]        ; compare and exchange

endif
.386

;
; Restore nonvolatile registers and return result in edx:eax.
;

cPublicFpo 0,0

        pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET  ExfInterlockedCompareExchange64

fstENDP ExfInterlockedCompareExchange64

        page , 132
        subttl  "Interlocked Compare Exchange 64-bits - with lock"
;++
;
; LONGLONG
; FASTCALL
; ExInterlockedCompareExchange64 (
;    IN PLONGLONG Destination,
;    IN PLONGLONG Exchange,
;    IN PLONGLONG Comperand,
;    IN PKSPIN_LOCK Lock
;    )
;
; Routine Description:
;
;    This function performs a compare and exchange of 64-bits.
;
;    N.B. This routine is provided for backward compatibility only. The
;         lock address is not used.
;
; Arguments:
;
;    (ecx) Destination - Supplies a pointer to the destination variable.
;
;    (edx) Exchange - Supplies a pointer to the exchange value.
;
;    (esp+4) Comperand - Supplies a pointer to the comperand value.
;
;    (esp+4) Lock - Supplies a pointer to a spin lock to use if the cmpxchg8b
;        instruction is not available on the host system.
;
; Return Value:
;
;    The current destination value is returned as the function value.
;
;--

cPublicFastCall ExInterlockedCompareExchange64, 4

cPublicFpo 0,2

;
; Save nonvolatile registers and read the exchange and comperand values.
;

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, ecx                ; set destination address
        mov     ebx, [edx]              ; get exchange value
        mov     ecx, [edx] + 4          ;
        mov     edx, [esp] + 12         ; get comperand address
        mov     eax, [edx]              ; get comperand value
        mov     edx, [edx] + 4          ;

.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr[ebp]        ; compare and exchange

endif
.386

;
; Restore nonvolatile registers and return result in edx:eax.
;

cPublicFpo 0,0

        pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET    ExInterlockedCompareExchange64

fstENDP ExInterlockedCompareExchange64

        subttl  "Interlocked Exchange Add"
;++
;
;   LONG
;   FASTCALL
;   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 unsigned 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:
;
;    (ecx)  Addend - Supplies a pointer to a variable whose value is to be
;                    adjusted by the increment value.
;
;    (edx) Increment - Supplies the increment value to be added to the
;                      addend variable.
;
; Return Value:
;
;    The initial value of the addend variable.
;
;--

cPublicFastCall __InterlockedExchangeAdd, 2
cPublicFpo 0,0

.486
ifndef NT_UP
   lock xadd    [ecx], edx              ; exchange add
else
        xadd    [ecx], edx              ; exchange add
endif
.386

        mov     eax, edx                ; set initial value

        fstRET  __InterlockedExchangeAdd

fstENDP __InterlockedExchangeAdd

_TEXT$00   ends
        end

