/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    lock.c

Abstract:
    
    32bit instructions with the LOCK prefix

Author:

    15-Aug-1995 t-orig (Ori Gershony)

Revision History:

        24-Aug-1999 [askhalid] copied from 32-bit wx86 directory and make work for 64bit.
        20-Sept-1999[barrybo]  added FRAG2REF(LockCmpXchg8bFrag32, ULONGLONG)

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include "fragp.h"
#include "lock.h"


// Define a macro which calls the lock helper functions
#define CALLLOCKHELPER0(fn)             fn ## LockHelper ()
#define CALLLOCKHELPER1(fn,a1)          fn ## LockHelper (a1)
#define CALLLOCKHELPER2(fn,a1,a2)       fn ## LockHelper (a1,a2)
#define CALLLOCKHELPER3(fn,a1,a2,a3)    fn ## LockHelper (a1,a2,a3)
#define CALLLOCKHELPER4(fn,a1,a2,a3,a4) fn ## LockHelper (a1,a2,a3,a4)

// Now define 32bit MSB
#define MSB		    0x80000000

#define SET_FLAGS_ADD   SET_FLAGS_ADD32
#define SET_FLAGS_SUB   SET_FLAGS_SUB32
#define SET_FLAGS_INC   SET_FLAGS_INC32
#define SET_FLAGS_DEC   SET_FLAGS_DEC32

FRAG2(LockAddFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER3(Add, &op1, pop1, op2); 
    SET_FLAGS_ADD(result, op1, op2, MSB);
}

FRAG2(LockOrFrag32, ULONG)
{
    ULONG result;

    result = CALLLOCKHELPER2(Or, pop1, op2); 
    SET_PFLAG(result);
    SET_ZFLAG(result);
    SET_SFLAG(result);
    SET_CFLAG_OFF;
    SET_OFLAG_OFF;
}

FRAG2(LockAdcFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER4(Adc, &op1, pop1, op2, cpu->flag_cf);
    SET_FLAGS_ADD(result, op1, op2, MSB);
}

FRAG2(LockSbbFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER4(Sbb, &op1, pop1, op2, cpu->flag_cf);
    SET_FLAGS_SUB(result, op1, op2, MSB);
}

FRAG2(LockAndFrag32, ULONG)
{
    ULONG result;

    result = CALLLOCKHELPER2(And, pop1, op2); 
    SET_ZFLAG(result);
    SET_PFLAG(result);
    SET_SFLAG(result);
    SET_CFLAG_OFF;
    SET_OFLAG_OFF;
}

FRAG2(LockSubFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER3(Sub, &op1, pop1, op2); 
    SET_FLAGS_SUB(result, op1, op2, MSB);
}

FRAG2(LockXorFrag32, ULONG)
{
    ULONG result;

    result = CALLLOCKHELPER2(Xor, pop1, op2); 
    SET_ZFLAG(result);
    SET_PFLAG(result);
    SET_SFLAG(result);
    SET_CFLAG_OFF;
    SET_OFLAG_OFF;
}

FRAG1(LockNotFrag32, ULONG)
{
    CALLLOCKHELPER1(Not, pop1);
}

FRAG1(LockNegFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER2(Neg, &op1, pop1);
    SET_CFLAG_IND(result == 0);
    SET_ZFLAG(result);
    SET_PFLAG(result);
    SET_SFLAG(result);
    SET_OFLAG_IND(op1 & result & MSB);
}

FRAG1(LockIncFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER3(Add, &op1, pop1, 1); 
    SET_FLAGS_INC(result, op1);
}

FRAG1(LockDecFrag32, ULONG)
{
    ULONG result, op1;

    result = CALLLOCKHELPER3(Sub, &op1, pop1, 1); 
    SET_FLAGS_DEC(result, op1);
}

FRAG2(LockBtsMemFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    pop1 += (op2 >> 5);
    SET_CFLAG_IND(CALLLOCKHELPER2(Bts, pop1, bit));
}

FRAG2(LockBtsRegFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    SET_CFLAG_IND(CALLLOCKHELPER2(Bts, pop1, bit));
}

FRAG2(LockBtrMemFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    pop1 += (op2 >> 5);
    SET_CFLAG_IND(CALLLOCKHELPER2(Btr, pop1, bit));
}

FRAG2(LockBtrRegFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    SET_CFLAG_IND(CALLLOCKHELPER2(Btr, pop1, bit));
}

FRAG2(LockBtcMemFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    pop1 += (op2 >> 5);
    SET_CFLAG_IND(CALLLOCKHELPER2(Btc, pop1, bit));
}

FRAG2(LockBtcRegFrag32, ULONG)
{
    ULONG bit = 1<<(op2&0x1f);

    SET_CFLAG_IND(CALLLOCKHELPER2(Btc, pop1, bit));
}

FRAG2REF(LockXchgFrag32, ULONG)
{
    CALLLOCKHELPER2(Xchg, pop1, pop2);
}

FRAG2REF(LockXaddFrag32, ULONG)
{
    ULONG op1, op2;

    op2 = CALLLOCKHELPER3(Xadd, &op1, pop1, pop2);
    // op1 has the original value of dest (*pop1)
    // op2 has the result of the XADD
    // so, op2-op1 is the original value of src
    SET_FLAGS_ADD(op2, (op2-op1), op1, MSB);
}
FRAG2REF(LockCmpXchgFrag32, ULONG)
{
    ULONG op1;
    ULONG Value = eax;

    SET_ZFLAG(CALLLOCKHELPER4(CmpXchg, &eax, pop1, pop2, &op1));
    SET_FLAGS_SUB(Value-op1, Value, op1, MSB);
}
FRAG2REF(LockCmpXchg8bFrag32, ULONGLONG)
{
    ULONGLONG op1;
    ULONGLONG EdxEax;
    ULONGLONG EcxEbx;

    EdxEax = (((ULONGLONG)edx) << 32) | (ULONGLONG)eax;
    EcxEbx = (ULONGLONG)ecx << 32 | (ULONGLONG)ebx;
    SET_ZFLAG(CALLLOCKHELPER3(CmpXchg8b, &EdxEax, &EcxEbx, pop1));
    edx = (ULONG)(EdxEax >> 32);
    eax = (ULONG)EdxEax;
}