//++
//
// Module Name:
//
//     intrlock.s
//
// Abstract:
//
//    This module implements the 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:
//
//    William K. Cheung (wcheung) 27-Sep-95
//
// Revision History:
//
//    07-Jul-97  bl    Updated to EAS2.3
//
//    02-Feb-96        Updated to EAS2.1
//
//--

#include "ksia64.h"

         .file    "intrlock.s"


//++
//
// 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 initial value of the addend variable is stored at the address
//    supplied by v0.
//
//--


#if !defined(NT_UP)

        LEAF_ENTRY(ExInterlockedAddLargeInteger)

//
// disable interrupt and then acquire the spinlock
//

        rsm         1 << PSR_I                  // disable interrupt
        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eiali10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a2], a2
(pt1)   ld8.nt1     t0 = [a2]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eiali10
        ;;

        ld8         v0 = [a0]
        ;;
        add         t1 = a1, v0                 // do the add
        ;;

        st8         [a0] = t1                   // save result
        st8.rel.nta [a2] = zero                 // release spinlock

        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedAddLargeInteger)

#else

        LEAF_ENTRY(ExInterlockedAddLargeInteger)

        rsm         1 << PSR_I                  // disable interrupt
        ;;
        ld8.acq     v0 = [a0]
        ;;

        add         t0 = a1, v0
        ;;
        st8.rel     [a0] = t0
        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk brp
        ;;

        LEAF_EXIT(ExInterlockedAddLargeInteger)

#endif // !defined(NT_UP)


//++
//
// 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.
//
//--

        LEAF_ENTRY(ExInterlockedAddLargeStatistic)

        ld8.nt1     v0 = [a0]
        extr.u      a1 = a1, 0, 32              // sanitize the top 32 bits
        ;;

Eials10:
        nop.m       0
        mov         ar.ccv = v0
        add         t0 = a1, v0
        ;;

//
// If the addend has been modified since the last load, pt8 will be set
// to TRUE and need to branch back to Eiali10 to retry the operation again.
//

        cmpxchg8.rel t1 = [a0], t0, ar.ccv
        ;;
        cmp.ne      pt8, pt7 = v0, t1
        mov         v0 = t1
        ;;

        nop.m       0
 (pt8)  br.cond.spnt Eials10                    // if failed, then try again
 (pt7)  br.ret.sptk.clr brp                     // otherwise, return
        ;;

        LEAF_EXIT(ExInterlockedAddLargeStatistic)


//++
//
// 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.
//
//--


#if !defined(NT_UP)

        LEAF_ENTRY(ExInterlockedAddUlong)

//
// disable interrupt and then acquire the spinlock
//

        rsm         1 << PSR_I                  // disable interrupt
        zxt4        a1 = a1                     // sanitize the top 32 bits

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eiau10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a2], a2
(pt1)   ld8.nt1     t0 = [a2]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eiau10
        ;;

//
// lock acquired; load the addend, perform the addition and write it back.
// then release the lock and enable interrupts.
//

        ld4         v0 = [a0]
        ;;
        add         t0 = a1, v0                 // do the add
        ;;

        st4         [a0] = t0                   // save result
        st8.rel.nta [a2] = zero                 // release spinlock

        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk.clr brp                     // return

        LEAF_EXIT(ExInterlockedAddUlong)

#else

        LEAF_ENTRY(ExInterlockedAddUlong)

        rsm         1 << PSR_I                  // disable interrupt
        ;;
        ld4.acq     v0 = [a0]
        zxt4        a1 = a1                     // sanitize the top 32 bits
        ;;
        add         t0 = a1, v0
        ;;
        st4.rel     [a0] = t0
        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk brp
        ;;

        LEAF_EXIT(ExInterlockedAddUlong)

#endif // !defined(NT_UP)


//++
//
// 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 IA64 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.
//
// 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(ExInterlockedExchangeUlong)
        ALTERNATE_ENTRY(ExIa64InterlockedExchangeUlong)

        mf
        zxt4        a1 = a1          // sanitize the upper 32 bits
        ;;

        xchg4       v0 = [a0], a1
        br.ret.sptk.clr brp

        LEAF_EXIT(ExInterlockedExchangeUlong)

//++
//
// 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:
//
//     (r8) - The initial value of the addend.
//
//--

        LEAF_ENTRY(InterlockedExchangeAdd)

        ld4.nt1     v0 = [a0]
        ;;

Iea10:
        mov         ar.ccv = v0
        add         t1 = a1, v0
        mov         t0 = v0
        ;;

        cmpxchg4.acq v0 = [a0], t1, ar.ccv
        nop.m       0
        nop.i       0
        ;;

        cmp.ne      pt7, pt8 = v0, t0
(pt7)   br.cond.spnt Iea10
(pt8)   br.ret.sptk.clr brp
        ;;

        LEAF_EXIT(InterlockedExchangeAdd)


//++
//
// 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:
//
//     (r8) - The initial destination value.
//
//--

        LEAF_ENTRY(InterlockedCompareExchange)

        mf
        mov         ar.ccv = a2
        ;;

        cmpxchg4.acq v0 = [a0], a1, ar.ccv
        br.ret.sptk.clr brp
        ;;

        LEAF_EXIT(InterlockedCompareExchange)


//++
//
// 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 IA64 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.
//
//        RESULT_ZERO = 0
//        RESULT_NEGATIVE = 1
//        RESULT_POSITIVE = 2
//
// Implementation Notes:
//
//    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(ExIa64InterlockedDecrementLong)

        fetchadd4.acq t1 = [a0], -1
        mov         v0 = 0                      // assume result is zero
        ;;

        cmp4.le     pt7, p0 = 2, t1
        cmp4.ge     pt8, p0 = 0, t1
        ;;
.pred.rel "mutex",pt7,pt8
  (pt8) mov         v0 = 1                      // negative result
  (pt7) mov         v0 = 2                      // positive result
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedDecrementLong)


//++
//
// 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 IA64 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 Notes:
//
//    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(ExIa64InterlockedIncrementLong)

        fetchadd4.acq t1 = [a0], 1
        mov         v0 = 0                      // assume result is zero
        ;;

        cmp4.le     pt7, p0 = 0, t1
        cmp4.ge     pt8, p0 = -2, t1
        ;;
.pred.rel "mutex",pt7,pt8
  (pt8) mov         v0 = 1                      // negative result
  (pt7) mov         v0 = 2                      // positive result
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedIncrementLong)


//++
//
// 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)

//
// disable interrupt and then try to acquire the spinlock
//

        add         t3 = LsFlink, a0
        add         t5 = LsBlink, a1
        add         t4 = LsFlink, a1

        rsm         1 << PSR_I                  // disable interrupt
#if !defined(NT_UP)

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eiihl10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a2], a2
(pt1)   ld8.nt1     t0 = [a2]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eiihl10

#endif // !defined(NT_UP)
        ;;

        LDPTR(v0, t3)                           // get address of next entry
        STPTR(t5, a0)                           // store previous link in next

        STPTR(t3, a1)                           // store next link in head
        ;;
        STPTR(t4, v0)                           // store next link in entry
        add         t6 = LsBlink, v0
        ;;

        STPTR(t6, a1)                           // store previous link in next
#if !defined(NT_UP)
        st8.rel.nta [a2] = zero                 // release the spinlock
#endif // !defined(NT_UP)
        cmp.eq      pt4, p0 = v0, a0            // if v0 == a0, list empty
        ;;

        ssm         1 << PSR_I                  // enable interrupt
  (pt4) mov         v0 = zero
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedInsertHeadList)

//++
//
// 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)

//
// disable interrupt and then try to acquire the spinlock
//

        add         t3 = LsBlink, a0
        add         t4 = LsFlink, a1
        add         t5 = LsBlink, a1

        rsm         1 << PSR_I                  // disable interrupt
#if !defined(NT_UP)

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eiitl10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a2], a2
(pt1)   ld8.nt1     t0 = [a2]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eiitl10

#endif // !defined(NT_UP)
        ;;

        LDPTR(v0, t3)                           // get address of previous entry
        STPTR(t4, a0)                           // store next link in entry

        STPTR(t3, a1)                           // store previous link in head
        ;;
        STPTR(t5, v0)                           // store previous link in entry
        add         t6 = LsFlink, v0
        ;;

        STPTR(t6, a1)                           // store next in previous entry
#if !defined(NT_UP)
        st8.rel.nta [a2] = zero                 // release the spinlock
#endif // !defined(NT_UP)
        cmp.eq      pt4, p0 = v0, a0            // if v0 == a0, list empty
        ;;

        ssm         1 << PSR_I                  // enable interrupt
  (pt4) mov         v0 = zero
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedInsertTailList)


//++
//
// 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)

//
// disable interrupt and then acquire the spinlock
//

        rsm         1 << PSR_I                  // disable interrupt
        add         t3 = LsFlink, a0
#if !defined(NT_UP)

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eirhl10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a1], a1
(pt1)   ld8.nt1     t0 = [a1]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eirhl10

#endif // !defined(NT_UP)
        ;;

        LDPTR(v0, t3)                           // get address of next entry
        ;;
        cmp.eq      pt4, pt3 = v0, a0           // if v0 == a0, list is empty
        add         t5 = LsFlink, v0
        ;;

        PLDPTR(pt3, t6, t5)                     // get address of next in next
        ;;
        PSTPTR(pt3, t3, t6)                     // store address of next in head
(pt3)   add         t7 = LsBlink, t6
        ;;

        PSTPTR(pt3, t7, a0)                     // store addr of prev in next
#if !defined(NT_UP)
        st8.rel.nta [a1] = zero                 // release the spinlock
#endif // !defined(NT_UP)

        ssm         1 << PSR_I                  // enable interrupt
(pt4)   mov         v0 = zero
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedRemoveHeadList)


//++
//
// 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)

//
// disable interrupt and then acquire the spinlock
//

        rsm         1 << PSR_I                  // disable interrupt
#if !defined(NT_UP)

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eipopel10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a1], a1
(pt1)   ld8.nt1     t0 = [a1]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eipopel10

#endif // !defined(NT_UP)
        ;;

        LDPTR(v0, a0)                           // get address of next entry
        ;;
        cmp.ne      pt4, p0 = zero, v0          // if v0 == NULL, list is empty
        ;;

        PLDPTR(pt4, t6, v0)                     // get address of next entry
        ;;
        PSTPTR(pt4, a0, t6)                     // store address of next in head

#if !defined(NT_UP)
        st8.rel.nta [a1] = zero                 // release the spinlock
#endif // !defined(NT_UP)
        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk.clr brp                     // return
        ;;

        LEAF_EXIT(ExInterlockedPopEntryList)


//++
//
// 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)

//
// disable interrupt and then acquire the spinlock
//

        rsm         1 << PSR_I                  // disable interrupt
#if !defined(NT_UP)

        cmp.eq      pt0, pt1 = zero, zero
        cmp.eq      pt2 = zero, zero
        ;;

Eipushel10:
.pred.rel "mutex",pt0,pt1
(pt0)   xchg8       t0 = [a2], a2
(pt1)   ld8.nt1     t0 = [a2]
        ;;
(pt0)   cmp.ne      pt2 = zero, t0
        cmp.eq      pt0, pt1 = zero, t0
        ;;
(pt1)   ssm         1 << PSR_I                  // enable interrupt
(pt0)   rsm         1 << PSR_I                  // disable interrupt
(pt2)   br.dpnt     Eipushel10

#endif // !defined(NT_UP)
        ;;

        LDPTR(v0, a0)                           // get address of next entry
        STPTR(a0, a1)                           // set address of first entry
        ;;

        STPTR(a1, v0)                           // set addr of next in new entry
#if !defined(NT_UP)
        st8.rel.nta [a2] = zero                 // release the spinlock
#endif // !defined(NT_UP)

        ssm         1 << PSR_I                  // enable interrupt
        br.ret.sptk brp                         // return
        ;;

        LEAF_EXIT(ExInterlockedPushEntryList)