        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:
;
;    slist.asm
;
; Abstract:
;
;    This module implements functions to support interlocked S-List
;    operations.
;
;--
.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

        page , 132
        subttl  "Interlocked Flush Sequenced List"
;++
;
; PSINGLE_LIST_ENTRY
; FASTCALL
; RtlpInterlockedFlushSList (
;    IN PSINGLE_LIST_ENTRY ListHead
;    )
;
; Routine Description:
;
;    This function removes the entire list from a sequenced singly
;    linked list so that access to the list is synchronized in an MP system.
;    If there are no entries in the list, then a value of NULL is returned.
;    Otherwise, the address of the entry at the top of the list is removed
;    and returned as the function value and the list header is set to point
;    to NULL.
;
; Arguments:
;
;    (ecx) = ListHead - Supplies a pointer to the sequenced listhead from
;         which the list is to be flushed.
;
; Return Value:
;
;    The address of the entire current list, or NULL if the list is
;    empty.
;
;--

;
; These old interfaces just fall into the new ones
;
cPublicFastCall ExInterlockedFlushSList, 1
fstENDP ExInterlockedFlushSList

cPublicFastCall RtlpInterlockedFlushSList, 1

cPublicFpo 0,1

;
; Save nonvolatile registers and read the listhead sequence number followed
; by the listhead next link.
;
; N.B. These two dwords MUST be read exactly in this order.
;

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        xor     ebx, ebx                ; zero out new pointer
        mov     ebp, ecx                ; save listhead address
        mov     edx, [ebp] + 4          ; get current sequence number
        mov     eax, [ebp] + 0          ; get current next link

;
; N.B. The following code is the retry code should the compare
;      part of the compare exchange operation fail
;
; If the list is empty, then there is nothing that can be removed.
;

Efls10: or      eax, eax                ; check if list is empty
        jz      short Efls20            ; if z set, list is empty
        mov     ecx, edx   		; copy sequence number
        mov     cx, bx                  ; clear depth leaving sequence number

.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr [ebp]       ; compare and exchange

endif
.386

        jnz     short Efls10            ; if z clear, exchange failed

;
; Restore nonvolatile registers and return result.
;

cPublicFpo 0,0

Efls20: pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET    RtlpInterlockedFlushSList

fstENDP RtlpInterlockedFlushSList

        page , 132
        subttl  "Interlocked Pop Entry Sequenced List"
;++
;
; PVOID
; FASTCALL
; RtlpInterlockedPopEntrySList (
;    IN PSLIST_HEADER ListHead
;    )
;
; Routine Description:
;
;    This function removes an entry from the front of a sequenced singly
;    linked list so that access to the list is synchronized in an MP 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 sequenced listhead from
;         which an entry is to be removed.
;
; Return Value:
;
;    The address of the entry removed from the list, or NULL if the list is
;    empty.
;
;--

;
; These older interfaces just fall into the new code below
;

cPublicFastCall InterlockedPopEntrySList, 1
fstENDP InterlockedPopEntrySList

cPublicFastCall ExInterlockedPopEntrySList, 2
fstENDP ExInterlockedPopEntrySList

cPublicFastCall RtlpInterlockedPopEntrySList, 1

cPublicFpo 0,2

;
; Save nonvolatile registers and read the listhead sequence number followed
; by the listhead next link.
;
; N.B. These two dwords MUST be read exactly in this order.
;

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, ecx                ; save listhead address
;
; N.B. The following code is the continuation address should a fault
;      occur in the rare case described below.
;

        public  ExpInterlockedPopEntrySListResume
        public  _ExpInterlockedPopEntrySListResume@0
ExpInterlockedPopEntrySListResume:      ;
_ExpInterlockedPopEntrySListResume@0:   ;

Epop10: mov     edx,[ebp] + 4           ; get current sequence number
        mov     eax,[ebp] + 0           ; get current next link

;
; If the list is empty, then there is nothing that can be removed.
;

        or      eax, eax                ; check if list is empty
        jz      short Epop20            ; if z set, list is empty
        lea     ecx, [edx-1]            ; Adjust depth only

;
; N.B. It is possible for the following instruction to fault in the rare
;      case where the first entry in the list is allocated on another
;      processor and free between the time the free pointer is read above
;      and the following instruction. When this happens, the access fault
;      code continues execution by skipping the following instruction.
;      This results in the compare failing and the entire operation is
;      retried.
;

        public  ExpInterlockedPopEntrySListFault
        public  _ExpInterlockedPopEntrySListFault@0
ExpInterlockedPopEntrySListFault:       ;
_ExpInterlockedPopEntrySListFault@0:

        mov     ebx, [eax]              ; get address of successor entry

        public  ExpInterlockedPopEntrySListEnd
        public  _ExpInterlockedPopEntrySListEnd@0
ExpInterlockedPopEntrySListEnd:         ;
_ExpInterlockedPopEntrySListEnd@0:      ;

.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr [ebp]       ; compare and exchange

endif
.386

        jnz     short Epop10            ; if z clear, exchange failed

;
; Restore nonvolatile registers and return result.
;

cPublicFpo 0,0

Epop20: pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET    RtlpInterlockedPopEntrySList

fstENDP RtlpInterlockedPopEntrySList

        page , 132
        subttl  "Interlocked Push Entry Sequenced List"
;++
;
; PVOID
; FASTCALL
; RtlpInterlockedPushEntrySList (
;    IN PSLIST_HEADER ListHead,
;    IN PVOID ListEntry
;    )
;
; Routine Description:
;
;    This function inserts an entry at the head of a sequenced singly linked
;    list so that access to the list is synchronized in an MP system.
;
; Arguments:
;
;    (ecx) ListHead - Supplies a pointer to the sequenced listhead 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.
;
; Return Value:
;
;    Previous contents of ListHead.  NULL implies list went from empty
;       to not empty.
;
;--

;
; This old interface just fall into the new code below.
;

cPublicFastCall ExInterlockedPushEntrySList, 3
       pop	[esp]			; Drop the lock argument
fstENDP ExInterlockedPushEntrySList

cPublicFastCall InterlockedPushEntrySList, 2
fstENDP InterlockedPushEntrySList


cPublicFastCall RtlpInterlockedPushEntrySList, 2

cPublicFpo 0,2

;
; Save nonvolatile registers and read the listhead sequence number followed
; by the listhead next link.
;
; N.B. These two dwords MUST be read exactly in this order.
;

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, ecx                ; save listhead address
        mov     ebx, edx                ; save list entry address
        mov     edx,[ebp] + 4           ; get current sequence number
        mov     eax,[ebp] + 0           ; get current next link
Epsh10: mov     [ebx], eax              ; set next link in new first entry
        lea     ecx, [edx+010001H]      ; increment sequence number and depth

.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr[ebp]        ; compare and exchange

endif
.386

        jnz     short Epsh10            ; if z clear, exchange failed

;
; Restore nonvolatile registers and return result.
;

cPublicFpo 0,0

        pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET  RtlpInterlockedPushEntrySList

fstENDP RtlpInterlockedPushEntrySList

;++
;
; SINGLE_LIST_ENTRY
; FASTCALL
; InterlockedPushListSList (
;     IN PSLIST_HEADER ListHead,
;     IN PSINGLE_LIST_ENTRY List,
;     IN PSINGLE_LIST_ENTRY ListEnd,
;     IN ULONG Count
;    )
;
; Routine Description:
;
;    This function will push multiple entries onto an SList at once
;
; Arguments:
;
;     ListHead - List head to push the list to.
;
;     List - The list to add to the front of the SList
;     ListEnd - The last element in the chain
;     Count - The number of items in the chain
;
; Return Value:
;
;     PSINGLE_LIST_ENTRY - The old header pointer is returned
;
;--

cPublicFastCall InterlockedPushListSList, 4

cPublicFpo 0,4
        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, ecx                ; save listhead address
        mov     ebx, edx                ; save list entry address
        mov     edx,[ebp] + 4           ; get current sequence number
        mov     eax,[ebp] + 0           ; get current next link
Epshl10:
        mov     ecx, [esp+4*3]          ; Fetch address of list tail
        mov     [ecx], eax              ; Store new forward pointer in tail entry
        lea     ecx, [edx+010000H]      ; increment sequence number
        add     ecx, [esp+4*4]		; Add in new count to create correct depth
.586
ifndef NT_UP

   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange

else

        cmpxchg8b qword ptr[ebp]        ; compare and exchange

endif
.386
        jnz     short Epshl10           ; if z clear, exchange failed

cPublicFpo 0,0

        pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;

        fstRET  InterlockedPushListSList

fstENDP InterlockedPushListSList

;++
;
; PSINGLE_LIST_ENTRY
; FirstEntrySList (
;     IN PSLIST_HEADER SListHead
;     )
;
; Routine Description:
;
;   This function returns the address of the fisrt entry in the SLIST or
;   NULL.
;
; Arguments:
;
;   ListHead (rcx) - Supplies a pointer to the sequenced listhead from
;       which the first entry address is to be computed.
;
; Return Value:
;
;   The address of the first entry is the specified, or NULL if the list is
;   empty.
;
;--

cPublicProc _FirstEntrySList, 1
cPublicFpo 1,0

        mov       eax, [esp+4]
        mov       eax, [eax]
        stdRET    _FirstEntrySList

stdENDP _FirstEntrySList

;++
;
; LONGLONG
; FASTCALL
; RtlInterlockedCompareExchange64 (
;    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 RtlInterlockedCompareExchange64, 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  RtlInterlockedCompareExchange64

fstENDP RtlInterlockedCompareExchange64

;++
;
; LONGLONG
; InterlockedCompareExchange64 (
;    IN OUT PLONGLONG Destination,
;    IN LONGLONG Exchange,
;    IN LONGLONG Comperand
;    )
;
; Routine Description:
;
;    This function performs a compare and exchange of 64-bits.
;
; Arguments:
;
;    (esp+4) Destination - Supplies a pointer to the destination variable.
;
;    (esp+8) Exchange - Supplies the exchange value.
;
;    (esp+16) Comperand - Supplies the comperand value.
;
; Return Value:
;
;    The current destination value is returned as the function value.
;
;--

cPublicProc _InterlockedCompareExchange64, 5
cPublicFpo 5,0

        push    ebx                     ; save nonvolatile registers
        push    ebp                     ;
        mov     ebp, [esp+12]           ; set destination address
        mov     ebx, [esp+16]           ; get exchange value
        mov     ecx, [esp+20]           ;
        mov     eax, [esp+24]           ; get comperand value
        mov     edx, [esp+28]           
.586
   lock cmpxchg8b qword ptr [ebp]       ; compare and exchange
.386

cPublicFpo 0,0

        pop     ebp                     ; restore nonvolatile registers
        pop     ebx                     ;
        stdRET    _InterlockedCompareExchange64

stdENDP _InterlockedCompareExchange64

_TEXT$00   ends
        end

