windows-nt/Source/XPSP1/NT/base/wow64/mscpu/fraglib/shareda.c
2020-09-26 16:20:57 +08:00

849 lines
18 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
shareda.c
Abstract:
Instruction fragments with common (shared) BYTE, WORD, and DWORD flavors.
Compiled twice per flavor, once with UNALIGNED and once with ALIGNED
pointers.
Author:
05-Nov-1995 BarryBo
Revision History:
--*/
// THIS FILE IS #include'd INTO FILES WHICH DEFINE THE FOLLOWING MACROS:
// MSB - most significant bit
// UTYPE - UNSIGNED type which defines registers (BYTE/USHORT/DWORD)
// STYPE - SIGNED type which defines registers (char/short/long)
// GET_VAL - dereference a pointer of the right type (GET_BYTE/...)
// PUT_VAL - writes a value into memory
// FRAGCOMMON{0,1,2} - mangles the function name and declares parameters
// AREG - al/ax/eax
// BREG - ...
// CREG - ...
// DREG - ...
FRAGCOMMON2(AddFrag)
{
UTYPE result;
UTYPE op1 = GET_VAL(pop1);
result = op1 + op2;
PUT_VAL(pop1, result);
SET_FLAGS_ADD(result, op1, op2, MSB);
}
FRAGCOMMON2(AddNoFlagsFrag)
{
UTYPE result;
UTYPE op1 = GET_VAL(pop1);
result = op1 + op2;
PUT_VAL(pop1, result);
}
FRAGCOMMON1(IncFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1+1;
PUT_VAL(pop1, result);
SET_FLAGS_INC(result, op1);
}
FRAGCOMMON1(IncNoFlagsFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1+1;
PUT_VAL(pop1, result);
}
FRAGCOMMON1(DecFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1-1;
PUT_VAL(pop1, result);
SET_FLAGS_DEC(result, op1);
}
FRAGCOMMON1(DecNoFlagsFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1-1;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(OrFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1 | op2;
PUT_VAL(pop1, result);
SET_ZFLAG(result);
SET_PFLAG(result);
SET_SFLAG(result << (31-LMB));
SET_CFLAG_OFF;
SET_OFLAG_OFF;
}
FRAGCOMMON2(OrNoFlagsFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = op1 | op2;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(AdcFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 + op2 + (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, result);
SET_FLAGS_ADD(result, op1, op2, MSB);
}
FRAGCOMMON2(AdcNoFlagsFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 + op2 + (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(SbbFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 - op2 - (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, result);
SET_FLAGS_SUB(result, op1, op2, MSB);
}
FRAGCOMMON2(SbbNoFlagsFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 - op2 - (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(AndFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 & op2;
PUT_VAL(pop1, result);
SET_ZFLAG(result);
SET_PFLAG(result);
SET_SFLAG(result << (31-LMB));
SET_CFLAG_OFF;
SET_OFLAG_OFF;
}
FRAGCOMMON2(AndNoFlagsFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 & op2;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(SubFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 - op2;
PUT_VAL(pop1, result);
SET_FLAGS_SUB(result, op1, op2, MSB);
}
FRAGCOMMON2(SubNoFlagsFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 - op2;
PUT_VAL(pop1, result);
}
FRAGCOMMON2(XorFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 ^ op2;
PUT_VAL(pop1, result);
SET_ZFLAG(result);
SET_PFLAG(result);
SET_SFLAG(result << (31-LMB));
SET_CFLAG_OFF;
SET_OFLAG_OFF;
}
FRAGCOMMON2(XorNoFlagsFrag)
{
UTYPE result, op1;
op1 = GET_VAL(pop1);
result = op1 ^ op2;
PUT_VAL(pop1, result);
}
// Note: both pop1 and pop2 are given by reference
FRAGCOMMON2REF(XchgFrag)
{
XCHG_MEM(UTYPE, pop1, pop2);
}
FRAGCOMMON2REF(XaddFrag)
{
UTYPE op1, op2, result;
op1 = GET_VAL(pop1);
op2 = GET_VAL(pop2);
result = op1+op2;
PUT_VAL(pop2, op1);
PUT_VAL(pop1, result);
SET_FLAGS_ADD(result, op1, op2, MSB);
}
FRAGCOMMON2REF(XaddNoFlagsFrag)
{
UTYPE op1, op2, result;
op1 = GET_VAL(pop1);
op2 = GET_VAL(pop2);
result = op1+op2;
PUT_VAL(pop2, op1);
PUT_VAL(pop1, result);
}
FRAGCOMMON2REF(CmpXchgFrag)
{
UTYPE op1, op2;
UTYPE Value = AREG;
op1 = GET_VAL(pop1);
op2 = GET_VAL(pop2);
SET_FLAGS_SUB(Value-op1, Value, op1, MSB);
if (Value == op1) {
PUT_VAL(pop1, op2);
SET_ZFLAG(0); // zf has inverse logic
} else {
AREG = op1;
SET_ZFLAG(1); // zf has inverse logic
}
}
FRAGCOMMON2(RolFrag)
{
op2 &= LMB;
if (op2) {
UTYPE b;
b = GET_VAL(pop1);
#if _PPC_ && (LMB==31)
b = _rotl(b, op2); // an instrinsic rotlw instruction on PPC
#else
b = (b << op2) | (b >> (LMB-op2+1));
#endif
PUT_VAL(pop1, b);
SET_CFLAG((DWORD)b << 31);
}
}
FRAGCOMMON2(RorFrag)
{
op2 &= LMB;
if (op2) {
UTYPE b;
b = GET_VAL(pop1);
#if _PPC_ && (LMB==31)
b = _rotr(b, op2); // an instrinsic rotlw instruction on PPC
#else
b = (b >> op2) | (b << (LMB-op2+1));
#endif
PUT_VAL(pop1, b)
SET_CFLAG((DWORD)b << (31-LMB));
}
}
FRAGCOMMON2(RclFrag)
{
op2 &= LMB;
if (op2) {
UTYPE b;
DWORD temp_cf;
b = GET_VAL(pop1);
temp_cf = (DWORD)b << (31-LMB+op2-1);
b = (b << op2) | (b >> (LMB-op2+2)) | ((UTYPE)GET_CFLAGZO << (op2-1));
PUT_VAL(pop1, b);
SET_CFLAG(temp_cf);
}
}
FRAGCOMMON2(RcrFrag)
{
op2 &= LMB;
if (op2) {
UTYPE b;
DWORD temp_cf;
b = GET_VAL(pop1);
temp_cf = (DWORD)b << (32-op2);
b = (b << (LMB-op2+2)) | (b >> op2) | (UTYPE)((GET_CFLAGZO) << (LMB-op2+1));
PUT_VAL(pop1, b)
SET_CFLAG(temp_cf);
}
}
FRAGCOMMON2(ShlFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b;
UTYPE newb;
b = GET_VAL(pop1);
b <<= (op2-1);
newb = b << 1;
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << (31-LMB));
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG(newb << (31-LMB));
}
}
FRAGCOMMON2(ShlNoFlagsFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b;
b = GET_VAL(pop1);
b <<= op2;
PUT_VAL(pop1, b);
}
}
FRAGCOMMON2(ShrFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b;
UTYPE newb;
b = GET_VAL(pop1);
b >>= (op2-1);
newb = b >> 1;
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << 31);
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG_OFF;
}
}
FRAGCOMMON2(ShrNoFlagsFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b;
b = GET_VAL(pop1);
b >>= op2;
PUT_VAL(pop1, b);
}
}
FRAGCOMMON2(SarFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b, temp, newb;
b = GET_VAL(pop1);
temp = b & MSB;
b >>= (op2-1);
newb = b >> 1;
if (temp) {
newb |= ~((1u << (LMB-op2+1))-1u);
}
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << 31);
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG(newb << (31-LMB));
}
}
FRAGCOMMON2(SarNoFlagsFrag)
{
op2 &= 0x1f;
if (op2) {
UTYPE b, temp, newb;
b = GET_VAL(pop1);
temp = b & MSB;
newb = b >> op2;
if (temp) {
newb |= ~((1u << (LMB-op2+1))-1u);
}
PUT_VAL(pop1, newb);
}
}
FRAGCOMMON1(Rol1Frag)
{
UTYPE b, temp_cf;
b = GET_VAL(pop1);
temp_cf = b & MSB;
#if _PPC_ && (LMB==31)
b = _rotl(b, 1); // an instrinsic rotlw instruction on PPC
#else
b = (b<<1) + (b >> LMB);
#endif
PUT_VAL(pop1, b);
SET_CFLAG((DWORD)temp_cf << (31-LMB));
SET_OFLAG((DWORD)(temp_cf ^ b) << (31-LMB));
}
FRAGCOMMON1(Rol1NoFlagsFrag)
{
UTYPE b, temp_cf;
b = GET_VAL(pop1);
temp_cf = b & MSB;
#if _PPC_ && (LMB==31)
b = _rotl(b, 1); // an instrinsic rotlw instruction on PPC
#else
b = (b<<1) + (b >> LMB);
#endif
PUT_VAL(pop1, b);
}
FRAGCOMMON1(Ror1Frag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
#if _PPC_ && (LMB==31)
newb = _rotr(b, 1); // an instrinsic rotlw instruction on PPC
#else
newb = (b >> 1) | (b << LMB);
#endif
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << 31);
SET_OFLAG((DWORD)((newb>>LMB) ^ (newb>>(LMB-1)) & 1) << 31); // xor top 2 bits together
}
FRAGCOMMON1(Ror1NoFlagsFrag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
#if _PPC_ && (LMB==31)
newb = _rotr(b, 1); // an instrinsic rotlw instruction on PPC
#else
newb = (b >> 1) | (b << LMB);
#endif
PUT_VAL(pop1, newb);
}
FRAGCOMMON1(Rcl1Frag)
{
UTYPE b, temp_cf;
b = GET_VAL(pop1);
temp_cf = b & MSB;
b = (b<<1) + (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, b);
SET_CFLAG(temp_cf << (31-LMB));
SET_OFLAG((DWORD)(temp_cf ^ b) << (31-LMB));
}
FRAGCOMMON1(Rcl1NoFlagsFrag)
{
UTYPE b;
b = GET_VAL(pop1);
b = (b<<1) + (UTYPE)GET_CFLAGZO;
PUT_VAL(pop1, b);
}
FRAGCOMMON1(Rcr1Frag)
{
UTYPE b, temp_cf;
b = GET_VAL(pop1);
temp_cf = b & 1;
b = (b >> 1) + (UTYPE)((cpu->flag_cf & 0x80000000) >> (31-LMB));
PUT_VAL(pop1, b);
SET_CFLAG((DWORD)temp_cf << 31);
SET_OFLAG(((DWORD)b << (31-LMB)) ^ ((DWORD)b << (31-LMB+1))); // xor top 2 bits together
}
FRAGCOMMON1(Rcr1NoFlagsFrag)
{
UTYPE b;
b = GET_VAL(pop1);
b = (b >> 1) + (UTYPE)((cpu->flag_cf & 0x80000000) >> (31-LMB));
PUT_VAL(pop1, b);
}
FRAGCOMMON1(Shl1Frag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
newb = b << 1;
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << (31-LMB));
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG(newb << (31-LMB));
SET_OFLAG(GET_CFLAG ^ GET_SFLAG);
}
FRAGCOMMON1(Shl1NoFlagsFrag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
newb = b << 1;
PUT_VAL(pop1, newb);
}
FRAGCOMMON1(Shr1Frag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
newb = b >> 1;
PUT_VAL(pop1, newb);
SET_OFLAG((DWORD)b << (31-LMB));
SET_CFLAG((DWORD)b << 31);
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG_OFF;
}
FRAGCOMMON1(Shr1NoFlagsFrag)
{
UTYPE b, newb;
b = GET_VAL(pop1);
newb = b >> 1;
PUT_VAL(pop1, newb);
}
FRAGCOMMON1(Sar1Frag)
{
UTYPE b, temp, newb;
b = GET_VAL(pop1);
temp = b & MSB;
newb = (b >> 1) + temp;
PUT_VAL(pop1, newb);
SET_CFLAG((DWORD)b << 31);
SET_ZFLAG(newb);
SET_PFLAG(newb);
SET_SFLAG(newb << (31-LMB));
SET_OFLAG_OFF;
}
FRAGCOMMON1(Sar1NoFlagsFrag)
{
UTYPE b, temp, newb;
b = GET_VAL(pop1);
temp = b & MSB;
newb = (b >> 1) + temp;
PUT_VAL(pop1, newb);
}
FRAGCOMMON1(NotFrag)
{
PUT_VAL(pop1, ~GET_VAL(pop1));
}
FRAGCOMMON1(NegFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = (UTYPE)-(STYPE)op1;
PUT_VAL(pop1, result);
SET_CFLAG_IND(result != 0);
SET_ZFLAG(result);
SET_PFLAG(result);
SET_AUXFLAG(op1 ^ result);
SET_SFLAG(result << (31-LMB));
SET_OFLAG((DWORD)(op1 & result) << (31-LMB));
}
FRAGCOMMON1(NegNoFlagsFrag)
{
UTYPE op1, result;
op1 = GET_VAL(pop1);
result = (UTYPE)-(STYPE)op1;
PUT_VAL(pop1, result);
}
FRAGCOMMON1(MulFrag)
{
#if MSB == 0x80
USHORT result;
result = (USHORT)al * (USHORT)GET_BYTE(pop1);
if (result <= 0xff) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
ax = result;
#elif MSB == 0x8000
ULONG result;
result = (ULONG)ax * (ULONG)GET_SHORT(pop1);
if (result <= 0xffff) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
ax = LOWORD(result);
dx = HIWORD(result);
#else // MSB == 0x80000000
LARGE_INTEGER result;
result = RtlEnlargedUnsignedMultiply(eax, GET_LONG(pop1));
if (result.HighPart == 0) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
eax = result.LowPart;
edx = result.HighPart;
#endif
}
FRAGCOMMON1(MulNoFlagsFrag)
{
#if MSB == 0x80
USHORT result;
result = (USHORT)al * (USHORT)GET_BYTE(pop1);
ax = result;
#elif MSB == 0x8000
ULONG result;
result = (ULONG)ax * (ULONG)GET_SHORT(pop1);
ax = LOWORD(result);
dx = HIWORD(result);
#else // MSB == 0x80000000
LARGE_INTEGER result;
result = RtlEnlargedUnsignedMultiply(eax, GET_LONG(pop1));
eax = result.LowPart;
edx = result.HighPart;
#endif
}
FRAGCOMMON1(MuliFrag)
{
#if MSB == 0x80
SHORT result;
result = (short)(char)al * (short)(char)GET_BYTE(pop1);
if ((result & 0xff80) == 0 || (result & 0xff80) == 0xff80) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
ax = result;
#elif MSB == 0x8000
LONG result;
result = (long)(short)ax * (long)(short)GET_SHORT(pop1);
if ((result & 0xffff8000) == 0 || (result & 0xffff8000) == 0xffff8000) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
ax = LOWORD(result);
dx = HIWORD(result);
#else // MSB == 0x80000000
LARGE_INTEGER result;
LONGLONG ll;
ll = Int32x32To64(eax, (long)GET_LONG(pop1));
result = *(LARGE_INTEGER *)&ll;
if ((result.HighPart == 0 && (result.LowPart & 0x80000000) == 0) ||
(result.HighPart == 0xffffffff && (result.LowPart & 0x80000000) == 0x80000000)) {
SET_CFLAG_OFF;
SET_OFLAG_OFF;
} else {
SET_CFLAG_ON;
SET_OFLAG_ON;
}
eax = result.LowPart;
edx = result.HighPart;
#endif
}
FRAGCOMMON1(MuliNoFlagsFrag)
{
#if MSB == 0x80
SHORT result;
result = (short)(char)al * (short)(char)GET_BYTE(pop1);
ax = result;
#elif MSB == 0x8000
LONG result;
result = (long)(short)ax * (long)(short)GET_SHORT(pop1);
ax = LOWORD(result);
dx = HIWORD(result);
#else // MSB == 0x80000000
LARGE_INTEGER result;
LONGLONG ll;
ll = Int32x32To64(eax, (long)GET_LONG(pop1));
result = *(LARGE_INTEGER *)&ll;
eax = result.LowPart;
edx = result.HighPart;
#endif
}
FRAGCOMMON1(DivFrag)
{
#if MSB == 0x80
USHORT result, remainder;
USHORT dividend, divisor;
dividend = (USHORT)ax;
divisor = GET_VAL(pop1);
result = dividend / divisor; // may get div-by-zero fault
remainder = dividend % divisor;
if ((result & 0xff00) == 0) {
al = (UTYPE)result;
ah = (UTYPE)remainder;
} else {
OVERFLOW_INSTR;
}
#elif MSB == 0x8000
DWORD result, remainder;
DWORD dividend, divisor;
dividend = (((DWORD)dx)<<16) | (DWORD)ax;
divisor = (DWORD)GET_VAL(pop1);
result = dividend / divisor; // may get div-by-zero fault
remainder = dividend % divisor;
if ((result & 0xffff0000) == 0) {
AREG = (UTYPE)result;
DREG = (UTYPE)remainder;
} else {
OVERFLOW_INSTR;
}
#else // MSB == 0x80000000
LARGE_INTEGER result;
LARGE_INTEGER remainder;
LARGE_INTEGER dividend;
LARGE_INTEGER divisor;
// build large_integers, without sign extending the 32-bit values
dividend.LowPart = AREG;
dividend.HighPart = DREG;
divisor.LowPart = (long)GET_LONG(pop1);
divisor.HighPart = 0;
result = RtlLargeIntegerDivide(dividend, divisor, &remainder);
if (result.HighPart == 0) {
AREG = result.LowPart;
DREG = remainder.LowPart;
} else {
OVERFLOW_INSTR;
}
#endif
}
FRAGCOMMON1(IdivFrag)
{
#if MSB == 0x80
short result, remainder;
result = (signed short)ax / (STYPE)GET_VAL(pop1); // may get div-by-zero fault
remainder = (signed short)ax % (STYPE)GET_VAL(pop1);
if ((result & 0xff80) == 0 || (result & 0xff80) == 0xff80) {
al = (UTYPE)result;
ah = (UTYPE)remainder;
} else {
OVERFLOW_INSTR;
}
#elif MSB == 0x8000
LONG result, remainder;
result = (signed long)((dx<<16) | ax) / (signed long)(STYPE)GET_VAL(pop1); // may get div-by-zero fault
remainder = (signed long)((dx<<16) | ax) % (signed long)(STYPE)GET_VAL(pop1);
if ((result & 0xffff8000) == 0 || (result & 0xffff8000) == 0xffff8000) {
AREG = (UTYPE)result;
DREG = (UTYPE)remainder;
} else {
OVERFLOW_INSTR;
}
#else // MSB == 0x80000000
LARGE_INTEGER result;
LARGE_INTEGER remainder;
LARGE_INTEGER dividend;
LARGE_INTEGER divisor;
DWORD op1;
//
// Since RtlLargeIntegerDivide and all of the overhead is large,
// it is worthwhile making this check:
//
if ((long)DREG == -(long)(AREG >> 31)) {
//
// EDX:EAX is really just the value of EAX sign-extended into EDX.
// This division can be performed with 32-bit arithmetic and no
// overflow checking.
//
OPT_CwdIdivFrag32(cpu, pop1);
return;
}
op1 = GET_LONG(pop1);
// build UNSIGNED large_integers
dividend.LowPart = AREG;
dividend.HighPart = DREG;
if (dividend.QuadPart < 0) {
dividend.QuadPart = -dividend.QuadPart;
}
if ((long)op1 < 0) {
divisor.LowPart = -(long)op1;
} else {
divisor.LowPart = op1;
}
divisor.HighPart = 0;
// perform UNSIGNED division
result = RtlLargeIntegerDivide(dividend, divisor, &remainder);
// if divisor and dividend signs are different, fudge the result
if ((dividend.HighPart != (int)DREG) ^ (divisor.LowPart != (int)op1)) {
result.QuadPart = -result.QuadPart;
}
// adjust the sign of the remainder if the dividend is negative
if (dividend.HighPart != (int)DREG) {
remainder.QuadPart = -remainder.QuadPart;
}
if ((result.HighPart == 0 && (result.LowPart & MSB) == 0) ||
(result.HighPart == 0xffffffff && (result.LowPart & MSB))) {
AREG = (UTYPE)result.LowPart;
DREG = (UTYPE)remainder.LowPart;
} else {
OVERFLOW_INSTR;
}
#endif
}