//       TITLE("Interlocked Support")
//++
//
// Copyright (c) 1990  Microsoft Corporation
// Copyright (c) 1992  Digital Equipment Corporation
//
// Module Name:
//
//    intrlock.s
//
// Abstract:
//
//    This module implements functions to support interlocked operations.
//    Interlocked operations can only operate on nonpaged data and the
//    specified spinlock cannot be used for any other purpose.
//
// Author:
//
//    David N. Cutler (davec) 26-Mar-1990
//
// Environment:
//
//    Kernel mode.
//
// Revision History:
//
//    Thomas Van Baak (tvb) 18-May-1992
//
//        Adapted for Alpha AXP.
//
//--

#include "ksalpha.h"

        SBTTL("Interlocked Add Large Integer")
//++
//
// LARGE_INTEGER
// ExInterlockedAddLargeInteger (
//    IN PLARGE_INTEGER Addend,
//    IN LARGE_INTEGER Increment,
//    IN PKSPIN_LOCK Lock
//    )
//
// Routine Description:
//
//    This function performs an interlocked add of an increment value to an
//    addend variable of type large integer. The initial value of the addend
//    variable is returned as the function value.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       adjusted by the increment value.
//
//    Increment (a1) - Supplies the increment value to be added to the
//       addend variable.
//
//    Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
//       access to the addend variable.
//
// Return Value:
//
//    The result of the interlocked large integer add.
//
// Implementation Note:
//
//    The specification of this function requires that the given lock must be
//    used to synchronize the update even though on Alpha the operation can
//    actually done atomically without using the specified lock.
//
//--

        LEAF_ENTRY(ExInterlockedAddLargeInteger)

10:     DISABLE_INTERRUPTS              // disable interrupts

#if !defined(NT_UP)

        LDP_L   t0, 0(a2)               // get current lock value - locked
        bne     t0, 20f                 // if ne, spin lock owned
        mov     a2, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a2)               // set spin lock owned - conditionally
        beq     t0, 20f                 // if eq, conditional store failed
        mb                              // synchronize memory access


#endif

        ldq     t0, 0(a0)               // get addend
        addq    t0, a1, v0              // do the add
        stq     v0, 0(a0)               // store result

#if !defined(NT_UP)

        mb                              // synchronize memory access
        STP     zero, 0(a2)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // enable interrupts

        ret     zero, (ra)              // return


//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

20:     ENABLE_INTERRUPTS               // enable interrupts

22:     LDP     t0, 0(a2)               // read current lock value
        beq     t0, 10b                 // if eq, lock not owned
        br      zero, 22b               // spin in cache until available

#endif

        .end    ExInterlockedAddLargeInteger

        SBTTL("Interlocked Add Large Statistic")
//++
//
// VOID
// 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 large integer.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       adjusted by the increment value.
//
//    Increment (a1) - Supplies the increment value to be added to the
//       addend variable.
//
// Return Value:
//
//    None.
//
// Implementation Note:
//
//    The specification of this function requires that the given lock must be
//    used to synchronize the update even though on Alpha the operation can
//    actually done atomically without using the specified lock.
//
//--

        LEAF_ENTRY(ExInterlockedAddLargeStatistic)

        zap     a1, 0xf0, a1            // zero extend increment value
10:     ldq_l   t0, 0(a0)               // get addend
        addq    t0, a1, t0              // do the add
        stq_c   t0, 0(a0)               // store result
        beq     t0, 20f                 // if eq, store conditional failed
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

20:     br      zero, 10b               // try again

        .end    ExInterlockedAddLargeStatistic

        SBTTL("Interlocked Add Unsigned Long")
//++
//
// ULONG
// ExInterlockedAddUlong (
//    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.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       adjusted by the increment value.
//
//    Increment (a1) - Supplies the increment value to be added to the
//       addend variable.
//
//    Lock (a2) - 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.
//
// Implementation Note:
//
//    The specification of this function requires that the given lock must be
//    used to synchronize the update even though on Alpha the operation can
//    actually done atomically without using the specified lock.
//
//--

        LEAF_ENTRY(ExInterlockedAddUlong)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a2)               // get current lock value - locked
        bne     t0, 20f                 // if ne, spin lock still owned
        mov     a2, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a2)               // set spin lock owned - conditionally
        beq     t0, 20f                 // if eq, conditional store failed
        mb                              // synchronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        ldl     t0, 0(a0)               // get addend value (return value also)
        addl    t0, a1, t1              // compute adjusted value
        stl     t1, 0(a0)               // store updated value

#if !defined(NT_UP)

        mb                              // synchronize memory access
        STP     zero, 0(a2)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        mov     t0, v0                  // set return value
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

20:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

22:     LDP     t0, 0(a2)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 22b               // spin in cache until available

#endif

        .end    ExInterlockedAddUlong

        SBTTL("Interlocked Exchange Unsigned Long")
//++
//
// ULONG
// ExInterlockedExchangeUlong (
//    IN PULONG Source,
//    IN ULONG Value,
//    IN PKSPIN_LOCK Lock
//    )
//
// Routine Description:
//
//    This function performs an interlocked exchange of a longword value with
//    a longword in memory and returns the memory value.
//
//    N.B. There is an alternate entry point provided for this routine which
//         is ALPHA target specific and whose prototype does not include the
//         spinlock parameter. Since the routine never refers to the spinlock
//         parameter, no additional code is required.
//
// Arguments:
//
//    Source (a0) - Supplies a pointer to a variable whose value is to be
//       exchanged.
//
//    Value (a1) - Supplies the value to exchange with the source value.
//
//    Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
//       access to the source variable.
//
// Return Value:
//
//    The source value is returned as the function value.
//
//--

        LEAF_ENTRY(ExInterlockedExchangeUlong)

        ALTERNATE_ENTRY(ExAlphaInterlockedExchangeUlong)

10:     ldl_l   v0, 0(a0)               // get current source value
        bis     a1, zero, t0            // set exchange value
        stl_c   t0, 0(a0)               // replace source value
        beq     t0, 20f                 // if eq, conditional store failed
        ret     zero, (ra)              // return old value to caller

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

20:     br      zero,10b                // go try spin lock again

        .end    ExInterlockedExchangeUlong

        SBTTL("Interlocked Decrement Long")
//++
//
// INTERLOCKED_RESULT
// ExInterlockedDecrementLong (
//    IN PLONG Addend,
//    IN PKSPIN_LOCK Lock
//    )
//
// Routine Description:
//
//    This function performs an interlocked decrement on an addend variable
//    of type signed long. The sign and whether the result is zero is returned
//    as the function value.
//
//    N.B. There is an alternate entry point provided for this routine which
//         is ALPHA target specific and whose prototype does not include the
//         spinlock parameter. Since the routine never refers to the spinlock
//         parameter, no additional code is required.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       decremented.
//
//    Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
//       access to the addend variable.
//
// Return Value:
//
//    RESULT_NEGATIVE is returned if the resultant addend value is negative.
//    RESULT_ZERO is returned if the resultant addend value is zero.
//    RESULT_POSITIVE is returned if the resultant addend value is positive.
//
// Implementation Note:
//
//    The specification of this function does not require that the given lock
//    be used to synchronize the update as long as the update is synchronized
//    somehow. On Alpha a single load locked-store conditional does the job.
//
//--

        LEAF_ENTRY(ExInterlockedDecrementLong)

        ALTERNATE_ENTRY(ExAlphaInterlockedDecrementLong)

10:     ldl_l   v0, 0(a0)               // get current addend value - locked
        subl    v0, 1, v0               // decrement addend value
        mov     v0, t0                  // copy updated value to t0 for store
        stl_c   t0, 0(a0)               // store updated value - conditionally
        beq     t0, 20f                 // if eq, conditional store failed

//
// Determine the INTERLOCKED_RESULT value based on the updated addend value.
// N.B. RESULT_ZERO = 0, RESULT_NEGATIVE = 1, RESULT_POSITIVE = 2.
//

        sra     v0, 63, t0              // replicate the sign bit to every bit
        addl    t0, 2, t0               // if t0 = 0 return 2, if -1 return 1
        cmovne  v0, t0, v0              // if v0 = 0 return 0
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

20:     br      zero, 10b               // go try spin lock again

        .end    ExInterlockedDecrementLong

        SBTTL("Interlocked Increment Long")
//++
//
// INTERLOCKED_RESULT
// ExInterlockedIncrementLong (
//    IN PLONG Addend,
//    IN PKSPIN_LOCK Lock
//    )
//
// Routine Description:
//
//    This function performs an interlocked increment on an addend variable
//    of type signed long. The sign and whether the result is zero is returned
//    as the function value.
//
//    N.B. There is an alternate entry point provided for this routine which
//         is ALPHA target specific and whose prototype does not include the
//         spinlock parameter. Since the routine never refers to the spinlock
//         parameter, no additional code is required.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       incremented.
//
//    Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
//       access to the addend variable.
//
// Return Value:
//
//    RESULT_NEGATIVE is returned if the resultant addend value is negative.
//    RESULT_ZERO is returned if the resultant addend value is zero.
//    RESULT_POSITIVE is returned if the resultant addend value is positive.
//
// Implementation Note:
//
//    The specification of this function does not require that the given lock
//    be used to synchronize the update as long as the update is synchronized
//    somehow. On Alpha a single load locked-store conditional does the job.
//
//--

        LEAF_ENTRY(ExInterlockedIncrementLong)

        ALTERNATE_ENTRY(ExAlphaInterlockedIncrementLong)

10:     ldl_l   v0, 0(a0)               // get current addend value - locked
        addl    v0, 1, v0               // increment addend value
        mov     v0, t0                  // copy updated value to t0 for store
        stl_c   t0, 0(a0)               // store updated value - conditionally
        beq     t0, 20f                 // if eq, conditional store failed

//
// Determine the INTERLOCKED_RESULT value based on the updated addend value.
// N.B. RESULT_ZERO = 0, RESULT_NEGATIVE = 1, RESULT_POSITIVE = 2.
//

        sra     v0, 63, t0              // replicate the sign bit to every bit
        addl    t0, 2, t0               // if t0 = 0 return 2, if -1 return 1
        cmovne  v0, t0, v0              // if v0 = 0 return 0
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

20:     br      zero, 10b               // go try spin lock again

        .end    ExInterlockedIncrementLong

        SBTTL("Interlocked Insert Head List")
//++
//
// PLIST_ENTRY
// ExInterlockedInsertHeadList (
//    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.
//
// Arguments:
//
//    ListHead (a0) - Supplies a pointer to the head of the doubly linked
//       list into which an entry is to be inserted.
//
//    ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
//       head of the list.
//
//    Lock (a2) - 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.
//
//--

        LEAF_ENTRY(ExInterlockedInsertHeadList)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a2)               // get current lock value - locked
        bne     t0, 20f                 // if ne, spin lock still owned
        mov     a2, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a2)               // set spin lock owned - conditionally
        beq     t0, 20f                 // if eq, conditional store failed
        mb                              // synchronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        LDP     t0, LsFlink(a0)         // get address of next entry (return value also)
        STP     t0, LsFlink(a1)         // store next link in entry
        STP     a0, LsBlink(a1)         // store previous link in entry
        STP     a1, LsBlink(t0)         // store previous link in next
        STP     a1, LsFlink(a0)         // store next link in head

#if !defined(NT_UP)

        mb                              // sychronize memory access
        STP     zero, 0(a2)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        xor     t0, a0, v0              // if t0=a0, list empty, set v0 to NULL
        cmovne  v0, t0, v0              // else return previous entry at head
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

20:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

22:     LDP     t0, 0(a2)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 22b               // spin in cache until available

#endif

        .end    ExInterlockedInsertHeadList

        SBTTL("Interlocked Insert Tail List")
//++
//
// PLIST_ENTRY
// ExInterlockedInsertTailList (
//    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.
//
// Arguments:
//
//    ListHead (a0) - Supplies a pointer to the head of the doubly linked
//       list into which an entry is to be inserted.
//
//    ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
//       tail of the list.
//
//    Lock (a2) - 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.
//
//--

        LEAF_ENTRY(ExInterlockedInsertTailList)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a2)               // get current lock value - locked
        bne     t0, 20f                 // if ne, spin lock still owned
        mov     a2, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a2)               // set spin lock owned - conditionally
        beq     t0, 20f                 // if eq, conditional store failed
        mb                              // sychronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        LDP     t0, LsBlink(a0)         // get address of previous entry (return value also)
        STP     a0, LsFlink(a1)         // store next link in entry
        STP     t0, LsBlink(a1)         // store previous link in entry
        STP     a1, LsBlink(a0)         // store previous link in next
        STP     a1, LsFlink(t0)         // store next link in head

#if !defined(NT_UP)

        mb                              // sychronize memory access
        STP     zero, 0(a2)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        xor     t0, a0, v0              // if t0=a0, list empty, set v0 to NULL
        cmovne  v0, t0, v0              // else return previous entry at tail
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

20:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

22:     LDP     t0, 0(a2)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 22b               // spin in cache until available

#endif

        .end    ExInterlockedInsertTailList

        SBTTL("Interlocked Remove Head List")
//++
//
// PLIST_ENTRY
// ExInterlockedRemoveHeadList (
//    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.
//
// Arguments:
//
//    ListHead (a0) - Supplies a pointer to the head of the doubly linked
//       list from which an entry is to be removed.
//
//    Lock (a1) - 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.
//
//--

        LEAF_ENTRY(ExInterlockedRemoveHeadList)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a1)               // get current lock value - locked
        bne     t0, 30f                 // if ne, spin lock still owned
        mov     a1, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a1)               // set spin lock owned - conditionally
        beq     t0, 30f                 // if eq, conditional store failed
        mb                              // synchronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        LDP     t2, LsFlink(a0)         // get address of next entry
        xor     t2, a0, t0              // if t2=a0, list empty, set t0 to NULL
        beq     t0, 20f                 // if eq, list is empty
        LDP     t1, LsFlink(t2)         // get address of next entry
        STP     t1, LsFlink(a0)         // store address of next in head
        STP     a0, LsBlink(t1)         // store address of previous in next
        mov     t2, t0                  // return the address of entry removed
20:                                     //

#if !defined(NT_UP)

        mb                              // synchronize memory access
        STP     zero, 0(a1)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        mov     t0, v0                  // set return value
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

30:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

32:     LDP     t0, 0(a1)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 32b               // spin in cache until available

#endif

        .end    ExInterlockedRemoveHeadList

        SBTTL("Interlocked Pop Entry List")
//++
//
// PSINGLE_LIST_ENTRY
// ExInterlockedPopEntryList (
//    IN PSINGLE_LIST_ENTRY ListHead,
//    IN PKSPIN_LOCK Lock
//    )
//
// Routine Description:
//
//    This function removes an entry from the head 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:
//
//    ListHead (a0) - Supplies a pointer to the head of the singly linked
//       list from which an entry is to be removed.
//
//    Lock (a1) - 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.
//
//--

        LEAF_ENTRY(ExInterlockedPopEntryList)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a1)               // get current lock value - locked
        bne     t0, 30f                 // if ne, spin lock still owned
        mov     a1, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a1)               // set spin lock owned - conditionally
        beq     t0, 30f                 // if eq, conditional store failed
        mb                              // synchronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        LDP     t0, 0(a0)               // get address of next entry (return value also)
        beq     t0, 20f                 // if eq [NULL], list is empty
        LDP     t1, 0(t0)               // get address of next entry
        STP     t1, 0(a0)               // store address of next in head
20:                                     //

#if !defined(NT_UP)

        mb                              // synchronize memory access
        STP     zero, 0(a1)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        mov     t0, v0                  // set return value
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

30:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

32:     LDP     t0, 0(a1)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 32b               // spin in cache until available

#endif

        .end    ExInterlockedPopEntryList

        SBTTL("Interlocked Push Entry List")
//++
//
// PSINGLE_LIST_ENTRY
// 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:
//
//    ListHead (a0) - Supplies a pointer to the head of the singly linked
//       list into which an entry is to be inserted.
//
//    ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
//       head of the list.
//
//    Lock (a2) - 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.
//
//--

        LEAF_ENTRY(ExInterlockedPushEntryList)

10:     DISABLE_INTERRUPTS              // (PALcode) v0 is clobbered

#if !defined(NT_UP)

        LDP_L   t0, 0(a2)               // get current lock value - locked
        bne     t0, 20f                 // if ne, spin lock still owned
        mov     a2, t0                  // set ownership value (lock address)
        STP_C   t0, 0(a2)               // set spin lock owned - conditionally
        beq     t0, 20f                 // if eq, conditional store failed
        mb                              // synchronize memory access

#endif

//
// Set the return value in t0 for now since PALcode may use v0.
//

        LDP     t0, 0(a0)               // get address of first entry (return value also)
        STP     t0, 0(a1)               // set address of next in new entry
        STP     a1, 0(a0)               // set address of first entry

#if !defined(NT_UP)

        mb                              // synchronize memory access
        STP     zero, 0(a2)             // set spin lock not owned

#endif

        ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

        mov     t0, v0                  // set return value
        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

#if !defined(NT_UP)

20:     ENABLE_INTERRUPTS               // (PALcode) v0 is clobbered

22:     LDP     t0, 0(a2)               // read current lock value
        beq     t0, 10b                 // try spinlock again if available
        br      zero, 22b               // spin in cache until available

#endif
        .end    ExInterlockedPushEntryList

        SBTTL("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 opeation is performed.
//
// Arguments:
//
//    Destination (a0) - Supplies a pointer to the destination value.
//
//    Exchange (a1) - Supplies the exchange.
//
//    Comperand (a2) - Supplies the comperand value.
//
// Return Value:
//
//    The initial destination value is returned as the function value.
//
//--

        LEAF_ENTRY(InterlockedCompareExchange)

10:                                     //

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

        ldl_l   v0, 0(a0)               // get current value
        bis     a1, zero, t0            // copy exchange value for store
        cmpeq   v0, a2, t1              // check if operands match
        beq     t1, 20f                 // if eq, operands mismatch
        stl_c   t0, 0(a0)               // store updated addend value
        beq     t0,25f                  // if eq, store conditional failed

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

20:     ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

25:     br      zero, 10b               // go try spin lock again

        .end    InterlockedCompareExchange

        SBTTL("Interlocked Exchange Add")
//++
//
// LONG
// ExInterlockedAdd (
//    IN PLONG Addend,
//    IN ULONG 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.
//
// Arguments:
//
//    Addend (a0) - Supplies a pointer to a variable whose value is to be
//       adjusted by the increment value.
//
//    Increment (a1) - Supplies the increment value to be added to the
//       addend variable.
//
// Return Value:
//
//    The initial value of the addend variable.
//
//--

        LEAF_ENTRY(InterlockedExchangeAdd)

10:                                     //

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

        ldl_l   v0, 0(a0)               // get current addend value - locked
        addl    v0, a1, t0              // increment addend value
        stl_c   t0, 0(a0)               // store updated value - conditionally
        beq     t0, 20f                 // if eq, conditonal store failed

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

        ret     zero, (ra)              // return

//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//

20:     br      zero, 10b               // go try spin lock again

        .end    InterlockedExchangeAdd

        SBTTL("Interlocked Pop Entry Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedPopEntrySList (
//    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 a 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:
//
//    ListHead (a0) - 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.
//
//--

        LEAF_ENTRY(ExpInterlockedPopEntrySList)

//
// N.B. The following code is the continuation address should a fault
//      occur in the rare case described below.
//

        ALTERNATE_ENTRY(ExpInterlockedPopEntrySListResume)

10:     ldq     t0, 0(a0)               // get next entry address and sequence

#if defined(_AXP64_)

        sra     t0, 63 - 42, v0         // extract next entry address
        bic     v0, 7, v0               //
        beq     v0, 30f                 // if eq, list is empty
        bis     t0, zero, t1            // copy depth and sequence

#else

        addl    t0, zero, v0            // sign extend next entry address
        beq     v0, 30f                 // if eq, list is empty
        srl     t0, 32, t1              // shift sequence to low 32-bits

#endif

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

//
// 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 freed between the time the free pointer is read above
//      and the following instruction. When this happens, the access fault
//      code continues execution above at the resumption address and the
//      entire operation is retried.
//

        ALTERNATE_ENTRY(ExpInterlockedPopEntrySListFault)

        LDP     t5, 0(v0)               // get address of successor entry

#if defined(_AXP64_)

        sll     t5, 63 - 42, t2         // shift address into position

#else

        zapnot  t5, 0xf ,t2             // clear high 32-bits for merge

#endif

        ldq_l   t3, 0(a0)               // reload next entry address and sequence
        ldil    t5, 0xffff              // decrement list depth and
        addl    t1, t5, t1              // increment sequence number

#if defined(_AXP64_)

        zapnot  t1, 0x7, t1             // clear upper five bytes

#else

        sll     t1, 32, t1              // shift depth and sequence into position

#endif

        cmpeq   t0, t3, t4              // check if listhead has changed
        beq     t4, 15f                 // if eq, listhead changed
        bis     t1, t2, t1              // merge address, depth, and sequence
        stq_c   t1, 0(a0)               // store next entry address and sequence
        beq     t1, 15f                 // if eq, store conditional failed

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

30:     ret     zero, (ra)              //

//
// Conditional store attempt failed or listhead changed.
//

15:     br      zero, 10b               // retry

        .end    ExpInterlockedPopEntrySList

        SBTTL("Interlocked Push Entry Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedPushEntrySList (
//    IN PSLIST_HEADER ListHead,
//    IN PSINGLE_LIST_ENTRY 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:
//
//    ListHead (a0) - Supplies a pointer to the sequenced listhead into which
//       an entry is to be inserted.
//
//    ListEntry (a1) - 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.
//
//--

        LEAF_ENTRY(ExpInterlockedPushEntrySList)

10:     ldq     t0, 0(a0)               // get next entry address and sequence

#if defined(_AXP64_)

        sra     t0, 63 - 42, v0         // extract next entry address
        bic     v0, 7, v0               //
        bis     t0, zero, t1            // copy depth and sequence number

#else

        addl    t0, zero, v0            // sign extend next entry address
        srl     t0, 32, t1              // shift sequence to low 32-bits

#endif

        STP     v0, 0(a1)               // set next link in new first entry

#if !defined(NT_UP)

        mb                              // synchronize memory access

#endif

#if defined(_AXP64_)

        sll     a1, 63 - 42, t2         // shift address into position

#else

        zapnot  a1, 0xf, t2             // zero extend new first entry

#endif

        ldq_l   t3, 0(a0)               // reload next entry address and sequence
        ldah    t5, 1(zero)             // get sequence adjustment value
        addl    t1, 1, t1               // increment list depth
        addl    t1, t5, t1              // increment sequence number

#if defined(_AXP64_)

        zapnot  t1, 0x7, t1             // clear upper five bytes

#else

        sll     t1, 32, t1              // merge new first entry address and sequence

#endif

        cmpeq   t0, t3, t4              // check if listhead changed
        beq     t4, 15f                 // if eq, listhead changed
        bis     t1, t2, t2              // merge address, depth, and sequence
        stq_c   t2, 0(a0)               // store next entry address and sequence
        beq     t2, 15f                 // if eq, store conditional failed
        ret     zero, (ra)              // return

//
// Conditional store attempt failed or listhead changed.
//

15:     br      zero, 10b               // retry

        .end    ExpInterlockedPushEntrySList

        SBTTL("Interlocked Flush Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedFlushSList (
//    IN PSLIST_HEADER ListHead
//    )
//
// Routine Description:
//
//    This function flushes the entire list of entries on a sequenced singly
//    linked list so that access to the list is synchronized in a MP system.
//    If there are no entries in the list, then a value of NULL is returned.
//    Otherwise, the address of the 1st entry on the list is returned as the
//    function value.
//
// Arguments:
//
//    ListHead (a0) - 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.
//
//--

        LEAF_ENTRY(ExpInterlockedFlushSList)

        and     t1, zero, t1            // set new listhead value
10:     ldq_l   t0, 0(a0)               // get next entry address and sequence
        stq_c	t1, 0(a0)               // store new listhead value
        beq     t1, 15f                 // if eq, store conditional failed

#if defined(_AXP64_)

        sra     t0, 63 - 42, v0         // extract next entry address
        bic     v0, 7, v0               //

#else

        addl    t0, zero, v0            // sign extend next entry address

#endif

        ret     zero, (ra)              // return

//
// Conditional store attempt failed or listhead changed.
//

15:     br      zero, 10b               // retry, store conditional failed

        .end    ExpInterlockedFlushSList

        SBTTL("Interlocked Compare Exchange 64-bits")
//++
//
// LONGLONG
// ExpInterlockedCompareExchange64 (
//    IN PLONGLONG Destination,
//    IN PLONGLONG Exchange,
//    IN PLONGLONG Comperand
//    )
//
// Routine Description:
//
//    This function performs an interlocked compare and exchange of 64-bits.
//
// Arguments:
//
//    Destination (a0) - Supplies a pointer to the destination variable.
//
//    Exchange (a1) - Supplies a pointer to the exchange value.
//
//    Comperand (a2) - Supplies a pointer to the comperand value.
//
// Return Value:
//
//    The current destination value are returned as the function value.
//
//--

        LEAF_ENTRY(ExpInterlockedCompareExchange64)

        ldq     t0, 0(a1)               // get exchange value
        ldq     t1, 0(a2)               // get comperand value
10:     ldq_l   v0, 0(a0)               // get current destination value
        bis     t0, zero, t2            // set exchange value
        cmpeq   v0, t1, t3              // check if current and comperand match
        beq     t3, 20f                 // if eq, current and comperand mismatch
        stq_c   t2, 0(a0)               // store exchange value
        beq     t2, 30f                 // if eq, store conditional failed
20:     ret     zero, (ra)

//
// Conditional store attempt failed.
//

30:     br      zero, 10b               // retry

        .end    ExpInterlockedCompareExchange64

        SBTTL("Interlocked Compare Exchange 64-bits")
//++
//
// LONGLONG
// InterlockedCompareExchange64 (
//    IN PLONGLONG Destination,
//    IN LONGLONG Exchange,
//    IN LONGLONG Comperand
//    )
//
// Routine Description:
//
//    This function performs an interlocked compare and exchange of 64-bits.
//
// Arguments:
//
//    Destination (a0) - Supplies a pointer to the destination variable.
//
//    Exchange (a1) - Supplies the exchange value.
//
//    Comperand (a2) - Supplies the comperand value.
//
// Return Value:
//
//    The current destination value are returned as the function value.
//
//--

#if !defined(_AXP64_)

        LEAF_ENTRY(InterlockedCompareExchange64)

10:     ldq_l   v0, 0(a0)               // get current destination value
        bis     a1, zero, t2            // set exchange value
        cmpeq   v0, a2, t3              // check if current and comperand match
        beq     t3, 20f                 // if eq, current and comperand mismatch
        stq_c   t2, 0(a0)               // store exchange value
        beq     t2, 10b                 // if eq, store conditional failed
20:     ret     zero, (ra)

        .end    InterlockedCompareExchange64

#endif