/* $Id$ */
/** @file
* CPU Instruction Decoding & Execution Tests - Simple Instructions.
*/
/*
* Copyright (C) 2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define CIDET_INSTR_TEST_OP_FLAG(a_pInstr, a_fFlag) \
( ((a_pInstr)->afOperands[0] & (a_fFlag)) \
|| ((a_pInstr)->afOperands[1] & (a_fFlag)) \
|| ( (a_pInstr)->cOperands > 2 \
&& ( ((a_pInstr)->afOperands[2] & (a_fFlag)) \
|| ((a_pInstr)->afOperands[3] & (a_fFlag)) ) ) )
#define CIDET_INSTR_TEST_OP_MASK_VALUE(a_pInstr, a_fMask, a_fValue) \
( ((a_pInstr)->afOperands[0] & (a_fMask)) == (a_fValue) \
|| ((a_pInstr)->afOperands[1] & (a_fMask)) == (a_fValue) \
|| ( (a_pInstr)->cOperands > 2 \
&& ( ((a_pInstr)->afOperands[2] & (a_fMask)) == (a_fValue) \
|| ((a_pInstr)->afOperands[3] & (a_fMask)) == (a_fValue) ) ) )
/** @def CIDET_DPRINTF
* Debug printf. */
#if 1 //def DEBUG_bird
# define CIDET_DPRINTF(a) do { RTPrintf a; } while (0)
# define CIDET_DPRINTF_ENABLED
#else
# define CIDET_DPRINTF(a) do { } while (0)
#endif
/** @def CIDET_DEBUG_DISAS
* Enables instruction disassembly. */
#if defined(DOXYGEN_RUNNING)
# define CIDET_DEBUG_DISAS 1
#endif
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "cidet.h"
#include <iprt/assert.h>
#include <iprt/rand.h>
#include <iprt/param.h>
#include <iprt/string.h>
#include <VBox/err.h>
#if defined(CIDET_DPRINTF_ENABLED) || defined(CIDET_DEBUG_DISAS)
# include <VBox/dis.h>
# include <iprt/stream.h>
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** For translating CIDET_OF_Z_XXX values (after shifting). */
uint16_t const g_acbCidetOfSizes[] =
{
/* [CIDET_OF_Z_NONE] = */ 0,
/* [CIDET_OF_Z_BYTE] = */ 1,
/* [CIDET_OF_Z_WORD] = */ 2,
/* [CIDET_OF_Z_DWORD] = */ 4,
/* [CIDET_OF_Z_QWORD] = */ 8,
/* [CIDET_OF_Z_TBYTE] = */ 10,
/* [CIDET_OF_Z_OWORD] = */ 16,
/* [CIDET_OF_Z_YWORD] = */ 32,
/* [CIDET_OF_Z_ZWORD] = */ 64,
/* [CIDET_OF_Z_VAR_WDQ] = */ UINT16_MAX,
/* [0xa] = */ 0,
/* [0xb] = */ 0,
/* [0xc] = */ 0,
/* [0xd] = */ 0,
/* [0xe] = */ 0,
/* [CIDET_OF_Z_SPECIAL] = */ UINT16_MAX - 1,
};
/** Converts operand sizes in bytes to 64-bit masks. */
static const uint64_t g_au64ByteSizeToMask[] =
{
UINT64_C(0x0000000000000000),
UINT64_C(0x00000000000000ff),
UINT64_C(0x000000000000ffff),
UINT64_C(0x0000000000ffffff),
UINT64_C(0x00000000ffffffff),
UINT64_C(0x000000ffffffffff),
UINT64_C(0x0000ffffffffffff),
UINT64_C(0x00ffffffffffffff),
UINT64_C(0xffffffffffffffff),
};
/** Converts operand sizes in bytes to 64-bit signed max values. */
static const int64_t g_ai64ByteSizeToMax[] =
{
INT64_C(0x0000000000000000),
INT64_C(0x000000000000007f),
INT64_C(0x0000000000007fff),
INT64_C(0x00000000007fffff),
INT64_C(0x000000007fffffff),
INT64_C(0x0000007fffffffff),
INT64_C(0x00007fffffffffff),
INT64_C(0x007fffffffffffff),
INT64_C(0x7fffffffffffffff),
};
bool CidetInstrHasMrmMemOperand(PCCIDETINSTR pInstr)
{
return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_M);
}
bool CidetInstrHasMrmRegOperand(PCCIDETINSTR pInstr)
{
return CIDET_INSTR_TEST_OP_FLAG(pInstr, CIDET_OF_M_RM_ONLY_R);
}
bool CidetInstrRespondsToOperandSizePrefixes(PCCIDETINSTR pInstr)
{
return CIDET_INSTR_TEST_OP_MASK_VALUE(pInstr, CIDET_OF_Z_MASK, CIDET_OF_Z_VAR_WDQ);
}
int CidetCoreInit(PCIDETCORE pThis, RTRAND hRand)
{
AssertPtr(pThis);
AssertPtr(hRand);
RT_ZERO(*pThis);
pThis->u32Magic = CIDETCORE_MAGIC;
pThis->hRand = hRand;
return VINF_SUCCESS;
}
void CidetCoreDelete(PCIDETCORE pThis)
{
AssertPtr(pThis); Assert(pThis->u32Magic == CIDETCORE_MAGIC);
RTRandAdvDestroy(pThis->hRand);
RT_ZERO(*pThis);
}
/**
* Report a test failure via CIDET::pfnFailure
*
* @returns false
* @param pThis Pointer to the core structure.
* @param pszFormat Format string containing failure details.
* @param va Arguments referenced in @a pszFormat.
*/
int CidetCoreSetErrorV(PCIDETCORE pThis, const char *pszFormat, va_list va)
{
pThis->pfnFailure(pThis, pszFormat, va);
return false;
}
/**
* Report a test failure via CIDET::pfnFailure
*
* @returns false
* @param pThis Pointer to the core structure.
* @param pszFormat Format string containing failure details.
* @param ... Arguments referenced in @a pszFormat.
*/
bool CidetCoreSetError(PCIDETCORE pThis, const char *pszFormat, ...)
{
va_list va;
va_start(va, pszFormat);
CidetCoreSetErrorV(pThis, pszFormat, va);
va_end(va);
return false;
}
/**
* Get a signed random number, with a given number of significant bytes.
*
* @returns Random number.
* @param pThis Pointer to the core structure.
* @param cbSignificant The number of significant bytes.
*/
int64_t CidetCoreGetRandS64(PCIDETCORE pThis, uint8_t cbSignificant)
{
int64_t iVal = RTRandAdvS64(pThis->hRand);
switch (cbSignificant)
{
case 1: return (int8_t)iVal;
case 2: return (int16_t)iVal;
case 4: return (int32_t)iVal;
case 8: return iVal;
default:
AssertReleaseFailed();
return iVal;
}
}
/**
* Get an unsigned random number, with a given number of significant bytes.
*
* @returns Random number.
* @param pThis Pointer to the core structure.
* @param cbSignificant The number of significant bytes.
*/
uint64_t CidetCoreGetRandU64(PCIDETCORE pThis, uint8_t cbSignificant)
{
Assert(cbSignificant == 1 || cbSignificant == 2 || cbSignificant == 4 || cbSignificant == 8);
uint64_t uVal = RTRandAdvU64(pThis->hRand);
uVal &= g_au64ByteSizeToMask[cbSignificant];
return uVal;
}
void CidetCoreInitializeCtxTemplate(PCIDETCORE pThis)
{
pThis->InTemplateCtx.rip = UINT64_MAX;
pThis->InTemplateCtx.rfl = X86_EFL_1 | X86_EFL_ID | X86_EFL_IF;
unsigned i = RT_ELEMENTS(pThis->InTemplateCtx.aGRegs);
if (CIDETMODE_IS_LM(pThis->bMode))
while (i-- > 0)
pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0x3fefcc00daba005d)
| ((uint64_t)i << 32)
| ((uint32_t)i << 8);
else
while (i-- > 0)
pThis->InTemplateCtx.aGRegs[i] = UINT64_C(0xfada009b)
| ((uint32_t)i << 12)
| ((uint32_t)i << 8);
i = RT_ELEMENTS(pThis->InTemplateCtx.aSRegs);
while (i-- > 0)
pThis->InTemplateCtx.aSRegs[i] = 0; /* Front end sets these afterwards. */
pThis->InTemplateCtx.cr2 = 0;
#ifndef CIDET_REDUCED_CTX
pThis->InTemplateCtx.tr = 0;
pThis->InTemplateCtx.ldtr = 0;
pThis->InTemplateCtx.cr0 = 0;
pThis->InTemplateCtx.cr3 = 0;
pThis->InTemplateCtx.cr4 = 0;
pThis->InTemplateCtx.cr8 = 0;
#endif
pThis->InTemplateCtx.fIgnoredRFlags = 0;
pThis->InTemplateCtx.uXcpt = UINT32_MAX;
pThis->InTemplateCtx.uErr = UINT64_MAX;
pThis->InTemplateCtx.fTrickyStack = false;
}
/**
* Sets the target mode.
*
* Caller must set up default selector values after calling this function.
*
* @returns VBox status code.
* @param pThis Pointer to the core structure.
* @param bMode The new mode.
*/
int CidetCoreSetTargetMode(PCIDETCORE pThis, uint8_t bMode)
{
AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertReturn(pThis->u32Magic == CIDETCORE_MAGIC, VERR_INVALID_HANDLE);
switch (bMode)
{
//case CIDETMODE_RM:
//case CIDETMODE_PE_16:
//case CIDETMODE_PE_32:
//case CIDETMODE_PE_V86:
//case CIDETMODE_PP_16:
case CIDETMODE_PP_32:
//case CIDETMODE_PP_V86:
//case CIDETMODE_PAE_16:
case CIDETMODE_PAE_32:
//case CIDETMODE_PAE_V86:
//case CIDETMODE_LM_S16:
//case CIDETMODE_LM_32:
case CIDETMODE_LM_64:
break;
default:
return VERR_NOT_IMPLEMENTED;
}
pThis->bMode = bMode;
CidetCoreInitializeCtxTemplate(pThis);
return VINF_SUCCESS;
}
bool CidetCoreIsEncodingCompatibleWithInstruction(PCIDETCORE pThis)
{
return true;
}
/**
* Selects the next address size mode.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
*/
static bool cidetCoreSetupNextBaseEncoding_AddressSize(PCIDETCORE pThis)
{
if (pThis->fAddrSizePrf)
{
/*
* Reset to default.
*/
pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode);
pThis->fAddrSizePrf = false;
}
else
{
/*
* The other addressing size.
*/
if (CIDETMODE_IS_64BIT(pThis->bMode))
pThis->cbAddrMode = 4;
else if (CIDETMODE_IS_32BIT(pThis->bMode))
pThis->cbAddrMode = 2;
else
{
AssertRelease(CIDETMODE_IS_16BIT(pThis->bMode));
pThis->cbAddrMode = 2;
}
pThis->fAddrSizePrf = true;
}
return pThis->fAddrSizePrf;
}
/**
* Selects the first REG encoding.
*
* @param pThis The core state structure.
*/
static void cidetCoreSetupFirstBaseEncoding_MrmReg(PCIDETCORE pThis)
{
pThis->aOperands[pThis->idxMrmRegOp].iReg = 0;
pThis->aOperands[pThis->idxMrmRegOp].fIsMem = false;
pThis->aOperands[pThis->idxMrmRegOp].fIsRipRelative = false;
pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false;
pThis->aOperands[pThis->idxMrmRegOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRegOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRegOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRegOp].uMemScale = 1;
pThis->aOperands[pThis->idxMrmRegOp].iEffSeg = UINT8_MAX;
pThis->bModRm &= ~X86_MODRM_REG_MASK;
pThis->fRexR = false;
}
/**
* Selects the next REG (ModR/M) encoding.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_MrmReg(PCIDETCORE pThis, uint8_t iReg)
{
Assert(pThis->idxMrmRegOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRegOp].fIsMem);
Assert(iReg < 16);
/*
* Clear the collision flags here because of the byte register kludge.
*/
pThis->fHasRegCollisionDirect = false;
pThis->fHasRegCollisionMemBase = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasRegCollisionMem = false;
/*
* Clear the REX prefix and high byte register tracking too. ASSUMES MrmReg is after MrmRmMod.
*/
Assert(!pThis->fNoRexPrefixMrmRm);
Assert(!pThis->fHasHighByteRegInMrmRm);
pThis->fNoRexPrefixMrmReg = false;
pThis->fNoRexPrefix = false;
pThis->fHasHighByteRegInMrmReg = false;
pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = false;
/*
* Special kludge for ah, ch, dh, bh, spl, bpl, sil, and dil.
* Needs extra care in 64-bit mode and special collision detection code.
*/
CIDET_DPRINTF(("aOperands[%u].cb=%u fGpr=%u iReg=%d fRex=%d fRexW=%u fRexX=%u fRexB=%u fRexR=%d\n",
pThis->idxMrmRegOp, pThis->aOperands[pThis->idxMrmRegOp].cb, CIDET_OF_K_IS_GPR(pThis->fMrmRegOp), iReg,
pThis->fRex, pThis->fRexW, pThis->fRexX, pThis->fRexB, pThis->fRexR));
if ( pThis->aOperands[pThis->idxMrmRegOp].cb == 1
&& CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
&& iReg >= 3
&& ( iReg <= 6
|| (CIDETMODE_IS_64BIT(pThis->bMode) && iReg == 7 && !pThis->fRex)) )
{
if (!pThis->fRex && iReg >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix)
{
/* The AMD64 low variants: spl, bpl, sil and dil. */
pThis->fRex = true;
pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP;
/* Check for collisions. */
if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
{
Assert(!pThis->fHasHighByteRegInMrmRm);
if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp)
&& iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg;
else
{
Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
}
}
else
{
/* Next register: ah, ch, dh and bh. */
iReg++;
pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg;
pThis->bModRm &= ~X86_MODRM_REG_MASK;
pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT;
pThis->fRex = false;
pThis->fRexR = false;
pThis->fNoRexPrefixMrmReg = true;
pThis->fNoRexPrefix = true;
pThis->fHasHighByteRegInMrmReg = true;
pThis->fHasStackRegInMrmReg = false;
pThis->aOperands[pThis->idxMrmRegOp].fIsHighByteRegister = true;
Assert(!pThis->fRexW); Assert(!pThis->fRexX); Assert(!pThis->fRexB);
/* Check for collisions. */
if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
{
if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRmOp)
&& ( ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1
&& iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg
&& pThis->fHasHighByteRegInMrmRm)
|| ( pThis->aOperands[pThis->idxMrmRmOp].cb > 1
&& iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iReg));
else
{
Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
pThis->fHasRegCollisionMemBase = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
pThis->fHasRegCollisionMemIndex = iReg - 4 == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
}
}
return true;
}
Assert(!pThis->fRex || (iReg == 7 && CIDETMODE_IS_64BIT(pThis->bMode)));
pThis->fRex = false;
/*
* Next register.
*/
iReg = (iReg + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) ? 15 : 7);
pThis->aOperands[pThis->idxMrmRegOp].iReg = iReg;
pThis->bModRm &= ~X86_MODRM_REG_MASK;
pThis->bModRm |= (iReg & X86_MODRM_REG_SMASK) << X86_MODRM_REG_SHIFT;
pThis->fRexR = iReg >= 8;
pThis->fHasStackRegInMrmReg = iReg == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
/*
* Register collision detection.
*/
if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
{
if (!pThis->aOperands[pThis->idxMrmRmOp].fIsMem)
pThis->fHasRegCollisionDirect = iReg == pThis->aOperands[pThis->idxMrmRmOp].iReg
&& CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
else if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
{
Assert(!pThis->fUsesVexIndexRegs || pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg == UINT8_MAX);
pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
}
Assert(!pThis->fSib);
return iReg != 0;
}
/**
* Selects the next MOD & R/M encoding, 16-bit addressing variant.
*
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg)
{
if (CidetInstrHasMrmRegOperand(pThis->pCurInstr))
{
pThis->aOperands[pThis->idxMrmRmOp].iReg = 0;
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT;
pThis->fRexB = false;
pThis->fRexX = false;
pThis->fHasMemoryOperand = false;
pThis->fHasRegCollisionDirect = iReg == 0
&& CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
pThis->fHasRegCollisionMem = false;
pThis->fHasRegCollisionMemBase = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasStackRegInMrmRmBase = false;
}
else
{
Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr));
pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->fRexB = false;
pThis->fRexX = false;
pThis->fHasMemoryOperand = true;
pThis->fHasRegCollisionDirect = false;
iReg -= pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
pThis->fHasStackRegInMrmRmBase = false;
}
}
/**
* Selects the next MOD & R/M encoding, 16-bit addressing variant.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(PCIDETCORE pThis, uint8_t iReg)
{
AssertRelease(!pThis->fRexB);
AssertRelease(!pThis->fRexX);
uint8_t iRm = pThis->bModRm & X86_MODRM_RM_MASK;
uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK;
if (iMod == 3)
{
/*
* Register access mode.
*/
Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
Assert(!pThis->fHasMemoryOperand);
Assert(!pThis->fHasRegCollisionMem);
Assert(!pThis->fHasRegCollisionMemBase);
Assert(!pThis->fHasRegCollisionMemIndex);
if (iRm < 7)
{
iRm++;
pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
pThis->bModRm &= ~X86_MODRM_RM_MASK;
pThis->bModRm |= iRm;
pThis->fHasRegCollisionDirect = iRm == iReg
&& CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRmOp);
return true;
}
/* If no memory modes, we're done. */
if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr))
{
cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg);
return false;
}
/* Next mode: 16-bit memory addressing without displacement. */
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
iMod = 0;
}
else
{
/*
* Memory access mode.
*/
Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
Assert(pThis->fHasMemoryOperand);
if (iRm < 7)
{
iRm++;
switch (iRm)
{
case 1:
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
break;
case 2:
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
break;
case 3:
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
break;
case 4:
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
break;
case 5:
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xDI;
break;
case 6:
if (iMod == 0)
{
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 2;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
}
else
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBP;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
break;
case 7:
if (iMod == 0)
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
break;
default: AssertReleaseFailed();
}
pThis->bModRm &= ~X86_MODRM_RM_MASK;
pThis->bModRm |= iRm;
if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
{
iReg -= pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
return true;
}
/* Last mode? */
if (iMod >= 2)
{
cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, iReg);
return false;
}
/* Next memory addressing mode (if any). */
iMod++;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp++;
}
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = X86_GREG_xBX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = X86_GREG_xSI;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT;
pThis->fHasMemoryOperand = true;
pThis->fHasRegCollisionDirect = false;
pThis->fHasStackRegInMrmRmBase = false;
if (CIDET_OF_K_IS_GPR(pThis->fMrmRmOp))
{
iReg -= pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasRegCollisionMemBase = iReg == X86_GREG_xBX;
pThis->fHasRegCollisionMemIndex = iReg == X86_GREG_xSI;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
return true;
}
/**
* Selects the first MOD & R/M encoding, 32-bit and 64-bit addressing variant.
*
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
* @param f64Bit Set if 64-bit, clear if 32-bit.
*/
static void cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit)
{
if (CidetInstrHasMrmRegOperand(pThis->pCurInstr))
{
pThis->aOperands[pThis->idxMrmRmOp].iReg = 0;
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->bModRm |= 3 << X86_MODRM_MOD_SHIFT;
pThis->fRexB = false;
pThis->fRexX = false;
pThis->fHasMemoryOperand = false;
pThis->fHasRegCollisionDirect = iReg == 0
&& CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
pThis->fHasRegCollisionMem = false;
pThis->fHasRegCollisionMemBase = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasStackRegInMrmRmBase = false;
}
else
{
Assert(CidetInstrHasMrmMemOperand(pThis->pCurInstr));
pThis->aOperands[pThis->idxMrmRmOp].iReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->aOperands[pThis->idxMrmRmOp].iEffSeg = UINT8_MAX;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->fRexB = false;
pThis->fRexX = false;
pThis->fHasMemoryOperand = true;
pThis->fHasRegCollisionDirect = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4 && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase;
pThis->fHasStackRegInMrmRmBase = false;
}
}
/**
* Selects the next MOD & R/M encoding, 32-bit and 64-bit addressing variant.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
* @param f64Bit Set if 64-bit, clear if 32-bit.
*/
static bool cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(PCIDETCORE pThis, uint8_t iReg, bool f64Bit)
{
AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode));
AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode));
uint8_t iRm = (pThis->bModRm & X86_MODRM_RM_MASK) + pThis->fRexB * 8;
uint8_t iMod = (pThis->bModRm >> X86_MODRM_MOD_SHIFT) & X86_MODRM_MOD_SMASK;
if (iMod == 3)
{
/*
* Register access mode.
*/
Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && !pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
Assert(!pThis->fHasMemoryOperand);
Assert(!pThis->fHasRegCollisionMem);
Assert(!pThis->fHasRegCollisionMemBase);
Assert(!pThis->fHasRegCollisionMemIndex);
if (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX && !pThis->fNoRexPrefix) /* should be ignored. */
{
pThis->fRexX = true;
return true;
}
/* Reset the byte register kludges variables. */
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = false;
pThis->fHasHighByteRegInMrmRm = false;
pThis->fNoRexPrefixMrmRm = false;
pThis->fNoRexPrefix = pThis->fNoRexPrefixMrmReg;
if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7))
{
/*
* Byte register kludge.
*/
if ( pThis->aOperands[pThis->idxMrmRmOp].cb == 1
&& CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
&& iRm >= 3
&& ( iRm <= 6
|| (iRm == 7 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fRexX) ) )
{
if (!pThis->fRexX && iRm >= 4 && CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix)
{
/* The AMD64 low variants: spl, bpl, sil and dil. (Using fRexX here as REG covers fRex.) */
pThis->fRexX = true;
pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
&& iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
}
else
{
/* Next register: ah, ch, dh and bh. */
iRm++;
pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
pThis->bModRm &= ~X86_MODRM_RM_MASK;
pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
pThis->fRexB = false;
pThis->fRexX = false;
if (!pThis->fRexR && !pThis->fRexW && !pThis->fRex)
{
pThis->fNoRexPrefixMrmRm = true;
pThis->fNoRexPrefix = true;
pThis->fHasHighByteRegInMrmRm = true;
pThis->aOperands[pThis->idxMrmRmOp].fIsHighByteRegister = true;
pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
&& iRm - 4 == iReg - pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasStackRegInMrmRmBase = false;
}
else
{
/* Can't do the high stuff, so do the spl, bpl, sil and dil variation instead.
Note! We don't set the RexX yet since the base register or operand width holds it down. */
pThis->fHasRegCollisionDirect = CIDET_OF_K_IS_GPR(pThis->fMrmRegOp)
&& iRm == iReg - pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
}
}
}
/*
* Normal register.
*/
else
{
iRm++;
pThis->aOperands[pThis->idxMrmRmOp].iReg = iRm;
pThis->bModRm &= ~X86_MODRM_RM_MASK;
pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
pThis->fRexB = iRm >= 8;
pThis->fRexX = false;
pThis->fHasRegCollisionDirect = iRm == iReg && CIDET_OF_K_IS_SAME(pThis->fMrmRmOp, pThis->fMrmRegOp);
pThis->fHasStackRegInMrmRmBase = iRm == X86_GREG_xSP && CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
}
return true;
}
/* If no memory modes, we're done. */
if (!CidetInstrHasMrmMemOperand(pThis->pCurInstr))
{
cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit);
return false;
}
/* Next mode: 32-bit/64-bit memory addressing without displacement. */
pThis->aOperands[pThis->idxMrmRmOp].fIsMem = true;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
iMod = 0;
}
else
{
/*
* Memory access mode.
*/
Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
Assert(pThis->fHasMemoryOperand);
Assert(!pThis->fHasStackRegInMrmRmBase);
if (iRm < (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7))
{
iRm++;
if (iRm == 12)
iRm++; /* Leave REX.B=1 to the next-sib-base function. */
if (iRm == 4)
{
/* SIB */
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = 0;
pThis->fSib = true;
pThis->bSib = 0;
}
else if ((iRm & 7) == 5 && iMod == 0)
{
/* Absolute or wrt rip addressing. */
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = CIDETMODE_IS_64BIT(pThis->bMode);
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4;
}
else
{
if ((iRm & 7) == 6 && iMod == 0)
{
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].fIsRipRelative = false;
}
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iRm;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
}
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->bModRm &= ~X86_MODRM_RM_MASK;
pThis->bModRm |= iRm & X86_MODRM_RM_MASK;
pThis->fRexB = iRm >= 8;
pThis->fRexX = false;
if (CIDET_OF_K_IS_GPR(pThis->fMrmRegOp))
{
iReg -= pThis->fHasHighByteRegInMrmReg * 4;
pThis->fHasRegCollisionMemBase = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg;
pThis->fHasRegCollisionMemIndex = iReg == pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg;
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
}
return true;
}
/* Last mode? */
if (iMod >= 2)
{
cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, f64Bit);
return false;
}
/* Next memory addressing mode (if any). */
iMod++;
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = iMod == 1 ? 1 : 4;
}
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
pThis->bModRm &= ~(X86_MODRM_RM_MASK | X86_MODRM_MOD_MASK);
pThis->bModRm |= iMod << X86_MODRM_MOD_SHIFT;
pThis->fRexB = false;
pThis->fRexX = false;
pThis->fHasMemoryOperand = true;
pThis->fHasRegCollisionDirect = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasRegCollisionMemBase = iReg == pThis->fHasHighByteRegInMrmReg * 4
&& CIDET_OF_K_IS_GPR(pThis->fMrmRmOp);
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase;
pThis->fHasStackRegInMrmRmBase = false;
return true;
}
/**
* Selects the next MOD & R/M encoding.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_MrmRmMod(PCIDETCORE pThis, uint8_t iReg)
{
if (pThis->cbAddrMode == 2)
return cidetCoreSetupNextBaseEncoding_MrmRmMod_16bit(pThis, iReg);
if (pThis->cbAddrMode == 4)
return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, false);
if (pThis->cbAddrMode == 8)
return cidetCoreSetupNextBaseEncoding_MrmRmMod_32bit64bit(pThis, iReg, true);
AssertReleaseFailedReturn(false);
}
/**
* Selects the next SIB base register (/ encoding).
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_SibBase(PCIDETCORE pThis, uint8_t iReg)
{
AssertRelease(!pThis->fRexB || CIDETMODE_IS_64BIT(pThis->bMode));
uint8_t iBase = (pThis->bSib & X86_SIB_BASE_MASK) + pThis->fRexB * 8;
iBase = (iBase + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7);
if ((iBase & 7) == 5 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0)
{
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 4;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = UINT8_MAX;
}
else
{
if ((iBase & 7) == 6 && (pThis->bModRm & X86_MODRM_MOD_MASK) == 0)
pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp = 0;
pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg = iBase;
}
pThis->bSib &= ~X86_SIB_BASE_MASK;
pThis->bSib |= iBase & X86_SIB_BASE_MASK;
pThis->fRexB = iBase >= 8;
pThis->fHasRegCollisionMemBase = pThis->aOperands[pThis->idxMrmRmOp].iMemBaseReg
== iReg - pThis->fHasHighByteRegInMrmReg * 4
&& CIDET_OF_K_IS_GPR(pThis->fMrmRegOp);
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
pThis->fHasStackRegInMrmRmBase = iBase == X86_GREG_xSP;
return iBase != 0;
}
/**
* Selects the next SIB index register (/ encoding).
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_SibIndex(PCIDETCORE pThis, uint8_t iReg)
{
AssertRelease(!pThis->fRexX || CIDETMODE_IS_64BIT(pThis->bMode));
Assert(pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands) && pThis->aOperands[pThis->idxMrmRmOp].fIsMem);
uint8_t iIndex = ((pThis->bSib >> X86_SIB_INDEX_SHIFT) & X86_SIB_INDEX_SMASK) + pThis->fRexX * 8;
iIndex = (iIndex + 1) & (CIDETMODE_IS_64BIT(pThis->bMode) && !pThis->fNoRexPrefix ? 15 : 7);
if (iIndex == 4 && !pThis->fUsesVexIndexRegs)
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = UINT8_MAX;
else
pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg = iIndex;
pThis->bSib &= ~X86_SIB_INDEX_MASK;
pThis->bSib |= (iIndex & X86_SIB_INDEX_SMASK) << X86_SIB_INDEX_SHIFT;
pThis->fRexX = iIndex >= 8;
pThis->fHasRegCollisionMemIndex = pThis->aOperands[pThis->idxMrmRmOp].iMemIndexReg
== iReg - pThis->fHasHighByteRegInMrmReg * 4
&& ( !pThis->fUsesVexIndexRegs
? CIDET_OF_K_IS_GPR(pThis->fMrmRegOp) : CIDET_OF_K_IS_VRX(pThis->fMrmRegOp) );
pThis->fHasRegCollisionMem = pThis->fHasRegCollisionMemBase || pThis->fHasRegCollisionMemIndex;
return iIndex != 0;
}
/**
* Selects the next SIB scale.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
* @param iReg The value of MODRM.REG /w REX.R applied.
*/
static bool cidetCoreSetupNextBaseEncoding_SibScale(PCIDETCORE pThis, uint8_t iReg)
{
switch ((pThis->bSib >> X86_SIB_SCALE_SHIFT) & X86_SIB_SCALE_SMASK)
{
case 0:
pThis->bSib |= 1 << X86_SIB_SCALE_SHIFT;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 2;
return true;
case 1:
pThis->bSib &= ~X86_SIB_SCALE_MASK;
pThis->bSib |= 2 << X86_SIB_SCALE_SHIFT;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 4;
return true;
case 2:
pThis->bSib |= 3 << X86_SIB_SCALE_SHIFT;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 8;
return true;
case 3:
pThis->bSib &= ~X86_SIB_SCALE_MASK;
pThis->aOperands[pThis->idxMrmRmOp].uMemScale = 1;
return false;
default: AssertReleaseFailedReturn(false);
}
}
/**
* Selects the next segment prefix.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
*/
static bool cidetCoreSetupNextBaseEncoding_SegmentPrefix(PCIDETCORE pThis)
{
if ( pThis->fHasMemoryOperand
&& (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_MASK))
{
switch (pThis->uSegPrf)
{
case X86_SREG_COUNT:
pThis->uSegPrf = X86_SREG_ES;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_ES)
return true;
/* fall thru */
case X86_SREG_ES:
pThis->uSegPrf = X86_SREG_CS;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_CS)
return true;
/* fall thru */
case X86_SREG_CS:
pThis->uSegPrf = X86_SREG_SS;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_SS)
return true;
/* fall thru */
case X86_SREG_SS:
pThis->uSegPrf = X86_SREG_DS;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_DS)
return true;
/* fall thru */
case X86_SREG_DS:
pThis->uSegPrf = X86_SREG_FS;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_FS)
return true;
/* fall thru */
case X86_SREG_FS:
pThis->uSegPrf = X86_SREG_GS;
if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_GS)
return true;
/* fall thru */
case X86_SREG_GS:
break;
default: AssertReleaseFailedBreak();
}
pThis->uSegPrf = X86_SREG_COUNT;
}
return false;
}
/**
* Updates the variable sized operands.
*
* @param pThis The core state structure.
*/
static void cidetCoreUpdateOperandSizes(PCIDETCORE pThis)
{
uint8_t iOp = pThis->cOperands;
while (iOp-- > 0)
pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp);
}
/**
* Selects the next operand size.
*
* @returns @c true if done, @c false if the next wheel needs to be moved.
* @param pThis The core state structure.
*/
static bool cidetCoreSetupNextBaseEncoding_OperandSize(PCIDETCORE pThis)
{
if (CidetInstrRespondsToOperandSizePrefixes(pThis->pCurInstr))
{
if (CIDETMODE_IS_64BIT(pThis->bMode))
{
switch (pThis->fOpSizePrf + pThis->fRexW * 2)
{
case 0:
pThis->fOpSizePrf = true;
cidetCoreUpdateOperandSizes(pThis);
return true;
case 1:
pThis->fOpSizePrf = false;
if (pThis->fNoRexPrefix)
break;
pThis->fRexW = true;
cidetCoreUpdateOperandSizes(pThis);
return true;
case 2:
pThis->fOpSizePrf = true; /* check that it's ignored. */
cidetCoreUpdateOperandSizes(pThis);
return true;
default: AssertReleaseFailed();
case 3:
break;
}
}
else
{
if (!pThis->fOpSizePrf)
{
pThis->fOpSizePrf = true;
cidetCoreUpdateOperandSizes(pThis);
return true;
}
}
pThis->fRexW = false;
pThis->fOpSizePrf = false;
cidetCoreUpdateOperandSizes(pThis);
}
return false;
}
bool CidetCoreSetupNextBaseEncoding(PCIDETCORE pThis)
{
if (pThis->fUsesModRm)
{
/*
* The wheels are lined up as follows:
* 1. Address size prefix.
* 2. MODRM.MOD
* 3. MODRM.REG + REX.R
* 4. MODRM.R/M + REX.B
* 5. SIB - MODRM.R/M == 4 && MODRM.MOD != 3:
* 5a) SIB.BASE + REX.B
* 5b) SIB.INDEX + REX.X
* 5c) SIB.SCALE
* 6. Segment prefix overrides if applicable and supported (memory).
* 7. Operand size prefix and REX.W if applicable.
*/
if (cidetCoreSetupNextBaseEncoding_OperandSize(pThis))
return true;
if (cidetCoreSetupNextBaseEncoding_SegmentPrefix(pThis))
return true;
/* The ModR/M register value for collision detection. */
uint8_t iReg = ((pThis->bModRm >> X86_MODRM_REG_SHIFT) & X86_MODRM_REG_SMASK) + pThis->fRexR * 8;
if (pThis->fSib)
{
AssertRelease(pThis->fHasMemoryOperand);
if (cidetCoreSetupNextBaseEncoding_SibScale(pThis, iReg))
return true;
if (cidetCoreSetupNextBaseEncoding_SibIndex(pThis, iReg))
return true;
if (cidetCoreSetupNextBaseEncoding_SibBase(pThis, iReg))
return true;
Assert(pThis->bSib == 0);
pThis->fSib = false;
}
if (cidetCoreSetupNextBaseEncoding_MrmRmMod(pThis, iReg))
return true;
if (cidetCoreSetupNextBaseEncoding_MrmReg(pThis, iReg))
return true;
if (cidetCoreSetupNextBaseEncoding_AddressSize(pThis))
return true;
}
else
AssertFailedReturn(false);
return false;
}
bool CidetCoreSetupFirstBaseEncoding(PCIDETCORE pThis)
{
/*
* Reset all the knobs and wheels.
*/
pThis->fSib = false;
pThis->uSegPrf = X86_SREG_COUNT;
pThis->fAddrSizePrf = false;
pThis->fOpSizePrf = false;
pThis->fRexW = false;
pThis->fRexR = false;
pThis->fRexX = false;
pThis->fRexB = false;
pThis->fRex = false;
pThis->bModRm = 0;
pThis->bSib = 0;
/* Indicators. */
pThis->cbAddrMode = CIDETMODE_GET_BYTE_COUNT(pThis->bMode);
pThis->fHasMemoryOperand = false;
pThis->fHasRegCollisionMem = false;
pThis->fHasRegCollisionMemBase = false;
pThis->fHasRegCollisionMemIndex = false;
pThis->fHasStackRegInMrmRmBase = false;
/*
* Now, drill down on the instruction encoding.
*/
if (pThis->pCurInstr->fFlags & CIDET_IF_MODRM)
{
Assert(pThis->fUsesModRm == true);
cidetCoreSetupFirstBaseEncoding_MrmReg(pThis);
if (pThis->cbAddrMode == 2)
cidetCoreSetupFirstBaseEncoding_MrmRmMod_16bit(pThis, 0);
else if (pThis->cbAddrMode == 4)
cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, false);
else if (pThis->cbAddrMode == 8)
cidetCoreSetupFirstBaseEncoding_MrmRmMod_32bit64bit(pThis, 0, true);
else
AssertReleaseFailedReturn(false);
}
else
AssertFailedReturn(false);
return true;
}
/**
* The next memory operand configuration.
*
* @returns true if new one to test, false if we've reached end already.
* @param pThis The core state structure.
*/
bool CidetCoreSetupNextMemoryOperandConfig(PCIDETCORE pThis)
{
return false;
}
/**
* Sets up the first memory operand configuration and counts memory operands.
*
* @returns true on success, false if no data buffers configured or failure.
* @param pThis The core state structure.
*/
bool CidetCoreSetupFirstMemoryOperandConfig(PCIDETCORE pThis)
{
pThis->cMemoryOperands = 0;
PCIDETBUF pDataBuf = &pThis->DataBuf;
uint8_t idxOp = pThis->cOperands;
while (idxOp-- > 0)
if (!pThis->aOperands[idxOp].fIsMem)
pThis->aOperands[idxOp].pDataBuf = NULL;
else
{
if (RT_UNLIKELY(!pThis->cDataBufConfigs))
return false;
pDataBuf->idxCfg = 0;
pDataBuf->pCfg = &pThis->paDataBufConfigs[0];
pDataBuf->off = 0;
pDataBuf->cb = pThis->aOperands[idxOp].cb;
pDataBuf->cbSegLimit = UINT16_MAX;
pDataBuf->offSegBase = 0;
pDataBuf->fActive = false;
pDataBuf->idxOp = idxOp;
pDataBuf->fXcptAfterInstruction = false;
pDataBuf->enmExpectXcpt = kCidetExpectXcpt_None;
pThis->aOperands[idxOp].pDataBuf = pDataBuf;
pThis->cMemoryOperands++;
pDataBuf++;
}
/** @todo implement more than one memory operand. */
AssertReleaseReturn(pThis->cMemoryOperands <= 1, false);
return true;
}
/**
* The next code buffer configuration.
*
* @returns true if new one to test, false if we've reached end already.
* @param pThis The core state structure.
*/
bool CidetCoreSetupNextCodeBufferConfig(PCIDETCORE pThis)
{
return false;
}
/**
* Sets up the first code buffer configuration.
*
* @returns true on success, false if no data buffers configured or failure.
* @param pThis The core state structure.
*/
bool CidetCoreSetupFirstCodeBufferConfig(PCIDETCORE pThis)
{
Assert(pThis->cCodeBufConfigs > 0);
Assert(CIDETBUF_IS_CODE(pThis->paCodeBufConfigs[0].fFlags));
pThis->CodeBuf.idxCfg = 0;
pThis->CodeBuf.pCfg = &pThis->paCodeBufConfigs[0];
pThis->CodeBuf.off = 0;
pThis->CodeBuf.cb = 0x1000;
pThis->CodeBuf.cbSegLimit = UINT16_MAX;
pThis->CodeBuf.offSegBase = 0;
pThis->CodeBuf.fActive = true;
pThis->CodeBuf.idxOp = 7;
pThis->CodeBuf.fXcptAfterInstruction = false;
pThis->CodeBuf.enmExpectXcpt = kCidetExpectXcpt_None;
return true;
}
/**
* Gets the (encoded) size of the given operand in the current context.
*
* @returns Size in bytes.
* @param pThis The core state structure (for context).
* @param iOp The operand index.
*/
uint32_t CidetCoreGetOperandSize(PCIDETCORE pThis, uint8_t iOp)
{
Assert(iOp < RT_ELEMENTS(pThis->aOperands));
uint32_t cbOp = g_acbCidetOfSizes[(pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) >> CIDET_OF_Z_SHIFT];
if (cbOp == UINT16_MAX)
{
Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_VAR_WDQ);
if (CIDETMODE_IS_64BIT(pThis->bMode))
{
if (pThis->fRexW)
cbOp = 8;
else if (!pThis->fOpSizePrf)
cbOp = 4;
else
cbOp = 2;
}
else if (CIDETMODE_IS_32BIT(pThis->bMode))
cbOp = !pThis->fOpSizePrf ? 4 : 2;
else
{
Assert(CIDETMODE_IS_16BIT(pThis->bMode));
cbOp = !pThis->fOpSizePrf ? 2 : 4;
}
return cbOp;
}
if (cbOp == UINT16_MAX - 1)
{
Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_SPECIAL);
AssertReleaseFailedReturn(0);
}
if (cbOp)
{
#ifdef VBOX_STRICT
switch (cbOp)
{
case 1: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_BYTE); break;
case 2: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_WORD); break;
case 4: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_DWORD); break;
case 8: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_QWORD); break;
case 10: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_TBYTE); break;
case 16: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_OWORD); break;
case 32: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_YWORD); break;
case 64: Assert((pThis->aOperands[iOp].fFlags & CIDET_OF_Z_MASK) == CIDET_OF_Z_ZWORD); break;
default: AssertFailed();
}
#endif
return cbOp;
}
AssertReleaseFailedReturn(0);
}
bool CideCoreSetInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr)
{
AssertReleaseMsgReturn(RT_VALID_PTR(pInstr), ("%p\n", pInstr), false);
pThis->pCurInstr = pInstr;
/*
* Extract info from the instruction descriptor.
*/
pThis->fUsesModRm = false;
pThis->fUsesVexIndexRegs = false;
pThis->idxMrmRegOp = 7;
pThis->idxMrmRmOp = 7;
pThis->fMrmRegOp = 0;
pThis->fMrmRmOp = 0;
pThis->fInstrFlags = pInstr->fFlags;
pThis->cOperands = pInstr->cOperands;
if (pInstr->fFlags & CIDET_IF_MODRM)
{
pThis->fUsesModRm = true;
for (uint8_t iOp = 0; iOp < pInstr->cOperands; iOp++)
if (pInstr->afOperands[iOp] & CIDET_OF_M_REG)
{
pThis->idxMrmRegOp = iOp;
pThis->fMrmRegOp = pInstr->afOperands[iOp];
}
else if (pInstr->afOperands[iOp] & CIDET_OF_M_RM)
{
pThis->idxMrmRmOp = iOp;
pThis->fMrmRmOp = pInstr->afOperands[iOp];
}
}
else
AssertFailedReturn(false);
uint8_t iOp;
for (iOp = 0; iOp < pInstr->cOperands; iOp++)
{
pThis->aOperands[iOp].fFlags = pInstr->afOperands[iOp];
pThis->aOperands[iOp].iReg = UINT8_MAX;
pThis->aOperands[iOp].cb = (uint8_t)CidetCoreGetOperandSize(pThis, iOp);
pThis->aOperands[iOp].fIsImmediate = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_IMM;
pThis->aOperands[iOp].fIsMem = (pInstr->afOperands[iOp] & CIDET_OF_K_MASK) == CIDET_OF_K_MEM;
pThis->aOperands[iOp].fIsRipRelative = false;
pThis->aOperands[iOp].cbMemDisp = 0;
pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[iOp].uMemScale = 1;
pThis->aOperands[iOp].iEffSeg = UINT8_MAX;
pThis->aOperands[iOp].offSeg = UINT64_MAX;
pThis->aOperands[iOp].uEffAddr = UINT64_MAX;
pThis->aOperands[iOp].uImmDispValue = UINT64_MAX;
pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX;
pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX;
pThis->aOperands[iOp].In.pv = NULL;
pThis->aOperands[iOp].Expected.pv = NULL;
pThis->aOperands[iOp].pDataBuf = NULL;
}
for (; iOp < RT_ELEMENTS(pThis->aOperands); iOp++)
{
pThis->aOperands[iOp].fFlags = 0;
pThis->aOperands[iOp].iReg = UINT8_MAX;
pThis->aOperands[iOp].cb = 0;
pThis->aOperands[iOp].fIsImmediate = false;
pThis->aOperands[iOp].fIsMem = false;
pThis->aOperands[iOp].fIsRipRelative = false;
pThis->aOperands[iOp].cbMemDisp = 0;
pThis->aOperands[iOp].iMemBaseReg = UINT8_MAX;
pThis->aOperands[iOp].iMemIndexReg = UINT8_MAX;
pThis->aOperands[iOp].uMemScale = 1;
pThis->aOperands[iOp].iEffSeg = UINT8_MAX;
pThis->aOperands[iOp].offSeg = UINT64_MAX;
pThis->aOperands[iOp].uEffAddr = UINT64_MAX;
pThis->aOperands[iOp].uImmDispValue = UINT64_MAX;
pThis->aOperands[iOp].uMemBaseRegValue = UINT64_MAX;
pThis->aOperands[iOp].uMemIndexRegValue = UINT64_MAX;
pThis->aOperands[iOp].In.pv = NULL;
pThis->aOperands[iOp].Expected.pv = NULL;
pThis->aOperands[iOp].pDataBuf = NULL;
}
/*
* Reset various things.
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aiInOut); i++)
pThis->aiInOut[i] = 0;
return true;
}
bool CidetCoreSetupInOut(PCIDETCORE pThis)
{
/*
* Enumerate the operands.
*/
uint8_t *pbBuf = &pThis->abBuf[0];
pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
uint8_t idxOp = pThis->cOperands;
while (idxOp-- > 0)
{
if (pThis->aOperands[idxOp].fIsMem)
{
/*
* Memory operand.
*/
Assert(pThis->aOperands[idxOp].fIsMem);
/* Set the In & Expected members to point to temporary buffer space. */
pThis->aOperands[idxOp].Expected.pu8 = pbBuf;
pbBuf += pThis->aOperands[idxOp].cb;
pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
pThis->aOperands[idxOp].In.pu8 = pbBuf;
pbBuf += pThis->aOperands[idxOp].cb;
pbBuf = RT_ALIGN_PT(pbBuf, 16, uint8_t *);
/* Initialize the buffer we're gonna use. */
pThis->aOperands[idxOp].iEffSeg = pThis->uSegPrf != X86_SREG_COUNT
? pThis->uSegPrf
: !(pThis->aOperands[idxOp].fFlags & CIDET_OF_ALWAYS_SEG_ES) ? X86_SREG_DS
: X86_SREG_ES;
PCIDETBUF pDataBuf = pThis->aOperands[idxOp].pDataBuf;
AssertReleaseReturn(pDataBuf, false);
Assert(pDataBuf->cb == pThis->aOperands[idxOp].cb);
Assert(pDataBuf->idxOp == idxOp);
if (!pThis->pfnReInitDataBuf(pThis, pDataBuf))
{
pThis->cSkippedReInitDataBuf++;
return false;
}
pDataBuf->fActive = true;
/* Calc buffer related operand members. */
pThis->aOperands[idxOp].uEffAddr = pDataBuf->uEffBufAddr + pDataBuf->off;
uint64_t offSeg = pThis->aOperands[idxOp].uEffAddr - pDataBuf->uSegBase;
pThis->aOperands[idxOp].offSeg = offSeg;
AssertRelease(offSeg <= g_au64ByteSizeToMask[pThis->cbAddrMode]);
/*
* Select register and displacement values for the buffer addressing (works on offSeg).
*/
uint8_t const iMemIndexReg = pThis->aOperands[idxOp].iMemIndexReg;
uint8_t const iMemBaseReg = pThis->aOperands[idxOp].iMemBaseReg;
if (pThis->aOperands[idxOp].fIsRipRelative)
{
/* rip relative. */
pThis->aOperands[idxOp].uImmDispValue = offSeg - (pThis->InCtx.rip + pThis->cbInstr);
Assert(pThis->aOperands[idxOp].cbMemDisp == 4);
if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue > INT32_MAX
|| (int64_t)pThis->aOperands[idxOp].uImmDispValue < INT32_MIN)
{
pThis->cSkippedDataBufWrtRip++;
return false;
}
}
else if (iMemBaseReg != UINT8_MAX)
{
if ( iMemBaseReg != iMemIndexReg
|| pThis->fUsesVexIndexRegs)
{
/* [base] or [base + disp] or [base + index * scale] or [base + index * scale + disp] */
if (pThis->aOperands[idxOp].cbMemDisp > 0)
{
pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
}
if (iMemIndexReg != UINT8_MAX)
{
pThis->aOperands[idxOp].uMemIndexRegValue = CidetCoreGetRandU64(pThis, pThis->cbAddrMode);
offSeg -= pThis->aOperands[idxOp].uMemIndexRegValue * pThis->aOperands[idxOp].uMemScale;
}
pThis->aOperands[idxOp].uMemBaseRegValue = offSeg & g_au64ByteSizeToMask[pThis->cbAddrMode];
}
else
{
/* base == index; [base + index * scale] or [base * (scale + 1)]. */
uint8_t const uEffScale = pThis->aOperands[idxOp].uMemScale + 1;
if (pThis->aOperands[idxOp].cbMemDisp > 0)
{
pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode];
uint8_t uRemainder = offSeg % uEffScale;
if (uRemainder != 0)
{
Assert(pThis->aOperands[idxOp].cbMemDisp < 8);
Assert( (int64_t)pThis->aOperands[idxOp].uImmDispValue
<= g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp]);
pThis->aOperands[idxOp].uImmDispValue = (int64_t)pThis->aOperands[idxOp].uImmDispValue
+ uRemainder;
offSeg -= uRemainder;
if ( (int64_t)pThis->aOperands[idxOp].uImmDispValue
> g_ai64ByteSizeToMax[pThis->aOperands[idxOp].cbMemDisp])
{
pThis->aOperands[idxOp].uImmDispValue -= uEffScale;
offSeg += uEffScale;
}
Assert(offSeg % uEffScale == 0);
}
}
else
{
offSeg &= g_au64ByteSizeToMask[pThis->cbAddrMode];
if (offSeg % uEffScale != 0)
{
pThis->cSkippedSameBaseIndexRemainder++;
return false;
}
}
offSeg /= uEffScale;
pThis->aOperands[idxOp].uMemBaseRegValue = pThis->aOperands[idxOp].uMemIndexRegValue = offSeg;
}
}
else if (iMemIndexReg != UINT8_MAX)
{
/* [index * scale] or [index * scale + disp] */
if (pThis->aOperands[idxOp].cbMemDisp > 0)
{
pThis->aOperands[idxOp].uImmDispValue = CidetCoreGetRandS64(pThis, pThis->aOperands[idxOp].cbMemDisp);
offSeg -= (int64_t)pThis->aOperands[idxOp].uImmDispValue;
pThis->aOperands[idxOp].uImmDispValue += offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1);
offSeg &= ~(RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1);
}
else if (offSeg & (RT_BIT_64(pThis->aOperands[idxOp].uMemScale) - 1))
{
pThis->cSkippedOnlyIndexRemainder++;
return false;
}
pThis->aOperands[idxOp].uMemIndexRegValue = offSeg / pThis->aOperands[idxOp].uMemScale;
Assert((offSeg % pThis->aOperands[idxOp].uMemScale) == 0);
AssertRelease(!pThis->fUsesVexIndexRegs); /** @todo implement VEX indexing */
}
else
{
/* [disp] */
Assert( pThis->aOperands[idxOp].cbMemDisp == 8
|| pThis->aOperands[idxOp].cbMemDisp == 4
|| pThis->aOperands[idxOp].cbMemDisp == 2
|| pThis->aOperands[idxOp].cbMemDisp == 1);
if ( pThis->aOperands[idxOp].cbMemDisp == 4
? (int64_t)offSeg != (int32_t)offSeg
: pThis->aOperands[idxOp].cbMemDisp == 2
? (int64_t)offSeg != (int16_t)offSeg
: pThis->aOperands[idxOp].cbMemDisp == 1
? (int64_t)offSeg != (int8_t)offSeg
: false /* 8 */)
{
pThis->cSkippedDirectAddressingOverflow++;
return false;
}
pThis->aOperands[idxOp].uImmDispValue = offSeg;
}
/*
* Modify the input and expected output contexts with the base and
* index register values. To simplify verification and the work
* here, we update the uMemBaseRegValue and uMemIndexRegValue
* members to reflect the whole register.
*/
if (iMemBaseReg != UINT8_MAX)
{
if (pThis->cbAddrMode == 4)
{
pThis->aOperands[idxOp].uMemBaseRegValue &= UINT32_MAX;
pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffff00000000);
}
else if (pThis->cbAddrMode == 2)
{
pThis->aOperands[idxOp].uMemBaseRegValue &= UINT16_MAX;
pThis->aOperands[idxOp].uMemBaseRegValue |= pThis->InCtx.aGRegs[iMemBaseReg] & UINT64_C(0xffffffffffff0000);
}
pThis->InCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue;
pThis->ExpectedCtx.aGRegs[iMemBaseReg] = pThis->aOperands[idxOp].uMemBaseRegValue;
}
if (iMemIndexReg != UINT8_MAX)
{
if (pThis->cbAddrMode == 4)
{
pThis->aOperands[idxOp].uMemIndexRegValue &= UINT32_MAX;
pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffff00000000);
}
else if (pThis->cbAddrMode == 2)
{
pThis->aOperands[idxOp].uMemIndexRegValue &= UINT16_MAX;
pThis->aOperands[idxOp].uMemIndexRegValue |= pThis->InCtx.aGRegs[iMemIndexReg] & UINT64_C(0xffffffffffff0000);
}
pThis->InCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue;
pThis->ExpectedCtx.aGRegs[iMemIndexReg] = pThis->aOperands[idxOp].uMemIndexRegValue;
}
}
else
{
/*
* Non-memory, so clear the memory related members.
*/
Assert(!pThis->aOperands[idxOp].fIsMem);
pThis->aOperands[idxOp].iEffSeg = UINT8_MAX;
pThis->aOperands[idxOp].offSeg = UINT64_MAX;
pThis->aOperands[idxOp].uEffAddr = UINT64_MAX;
pThis->aOperands[idxOp].pDataBuf = NULL;
switch (pThis->aOperands[idxOp].fFlags & CIDET_OF_K_MASK)
{
case CIDET_OF_K_GPR:
if (!pThis->aOperands[idxOp].fIsHighByteRegister)
{
pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg];
pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg];
}
else
{
pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4];
pThis->aOperands[idxOp].In.pu8++;
pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aGRegs[pThis->aOperands[idxOp].iReg - 4];
pThis->aOperands[idxOp].Expected.pu8++;
}
break;
case CIDET_OF_K_IMM:
pThis->aOperands[idxOp].In.pv = NULL;
pThis->aOperands[idxOp].Expected.pv = NULL;
break;
case CIDET_OF_K_SREG:
if (pThis->aOperands[idxOp].iReg < RT_ELEMENTS(pThis->InCtx.aSRegs))
{
pThis->aOperands[idxOp].In.pv = &pThis->InCtx.aSRegs[pThis->aOperands[idxOp].iReg];
pThis->aOperands[idxOp].Expected.pv = &pThis->ExpectedCtx.aSRegs[pThis->aOperands[idxOp].iReg];
}
else
{
pThis->aOperands[idxOp].In.pv = NULL;
pThis->aOperands[idxOp].Expected.pv = NULL;
}
break;
case CIDET_OF_K_CR:
case CIDET_OF_K_SSE:
case CIDET_OF_K_AVX:
case CIDET_OF_K_AVX512:
case CIDET_OF_K_FPU:
case CIDET_OF_K_MMX:
case CIDET_OF_K_AVXFUTURE:
case CIDET_OF_K_SPECIAL:
case CIDET_OF_K_TEST:
/** @todo Implement testing these registers. */
case CIDET_OF_K_NONE:
default:
AssertReleaseFailedReturn(false);
}
}
}
AssertRelease((uintptr_t)pbBuf - (uintptr_t)&pThis->abBuf[0] <= sizeof(pThis->abBuf));
/*
* Call instruction specific setup function (for operand values and flags).
*/
int rc = pThis->pCurInstr->pfnSetupInOut(pThis, false /*fInvalid*/);
if (RT_FAILURE(rc))
{
pThis->cSkippedSetupInOut++;
return false;
}
/*
* Do the 2nd set of the memory operand preparations.
*/
if (pThis->fHasMemoryOperand)
{
idxOp = pThis->cOperands;
while (idxOp-- > 0)
if (pThis->aOperands[idxOp].fIsMem)
{
Assert(pThis->aOperands[idxOp].pDataBuf);
if (!pThis->pfnSetupDataBuf(pThis, pThis->aOperands[idxOp].pDataBuf, pThis->aOperands[idxOp].In.pv))
{
pThis->cSkippedSetupDataBuf++;
return false;
}
Assert( pThis->aOperands[idxOp].iMemBaseReg == UINT8_MAX
|| pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemBaseReg] == pThis->aOperands[idxOp].uMemBaseRegValue);
Assert( pThis->aOperands[idxOp].iMemIndexReg == UINT8_MAX
|| ( !pThis->fUsesVexIndexRegs
? pThis->InCtx.aGRegs[pThis->aOperands[idxOp].iMemIndexReg]
== pThis->aOperands[idxOp].uMemIndexRegValue
: false /** @todo VEX indexing */));
}
}
return true;
}
/**
* Figures the instruction length.
*
* This is a duplicate of CidetCoreAssemble() with the buffer updates removed.
*
* @returns true and pThis->cbInstr on success, false on failure.
* @param pThis The core state structure (for context).
*/
bool CidetCoreAssembleLength(PCIDETCORE pThis)
{
uint8_t off = 0;
/*
* Prefixes.
*/
if (1)
{
if (pThis->fAddrSizePrf)
off++;
if (pThis->fOpSizePrf)
off++;
}
else
{
/** @todo prefix list. */
}
/*
* Prefixes that must come right before the opcode.
*/
/** @todo VEX and EVEX. */
if (pThis->fVex)
{
/** @todo VEX and EVEX. */
}
else if (pThis->fEvex)
{
/** @todo VEX and EVEX. */
}
else
{
if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex)
off++;
}
/*
* The opcode.
*/
uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode;
switch (pThis->pCurInstr->cbOpcode)
{
case 3: off++;
case 2: off++;
case 1: off++;
break;
default:
AssertReleaseFailedReturn(false);
}
/*
* Mod R/M
*/
if (pThis->fUsesModRm)
{
off++;
if (pThis->fSib)
off++;
if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
{
uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue;
switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp)
{
case 0: break;
case 8:
case 7:
case 6:
case 5:
case 4:
case 3:
case 2:
case 1:
break;
default: AssertReleaseFailedReturn(false);
}
off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp;
}
}
/*
* Immediates.
*/
uint8_t iOp = pThis->cOperands;
while (iOp-- > 0)
if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM)
{
uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue;
switch (pThis->aOperands[iOp].cb)
{
case 8:
case 7:
case 6:
case 5:
case 4:
case 3:
case 2:
case 1:
break;
default: AssertReleaseFailedReturn(false);
}
off += pThis->aOperands[iOp].cb;
}
pThis->cbInstr = off;
return true;
}
/**
* Assembles the instruction.
*
* This is a duplicate of CidetCoreAssembleLength() with buffer writes.
*
* @returns true and pThis->cbInstr and pThis->abInstr on success, false on
* failure.
* @param pThis The core state structure (for context).
*/
bool CidetCoreAssemble(PCIDETCORE pThis)
{
uint8_t off = 0;
/*
* Prefixes.
*/
if (1)
{
if (pThis->fAddrSizePrf)
pThis->abInstr[off++] = 0x67;
if (pThis->fOpSizePrf)
pThis->abInstr[off++] = 0x66;
}
else
{
/** @todo prefix list. */
}
/*
* Prefixes that must come right before the opcode.
*/
/** @todo VEX and EVEX. */
if (pThis->fVex)
{
/** @todo VEX and EVEX. */
}
else if (pThis->fEvex)
{
/** @todo VEX and EVEX. */
}
else
{
if (pThis->fRexB || pThis->fRexX || pThis->fRexR || pThis->fRexW || pThis->fRex)
pThis->abInstr[off++] = 0x40 | (pThis->fRexB * 1) | (pThis->fRexX * 2) | (pThis->fRexR * 4) | (pThis->fRexW * 8);
}
/*
* The opcode.
*/
uint8_t const *pbOpcode = pThis->pCurInstr->abOpcode;
switch (pThis->pCurInstr->cbOpcode)
{
case 3: pThis->abInstr[off++] = *pbOpcode++;
case 2: pThis->abInstr[off++] = *pbOpcode++;
case 1: pThis->abInstr[off++] = *pbOpcode++;
break;
default:
AssertReleaseFailedReturn(false);
}
/*
* Mod R/M
*/
if (pThis->fUsesModRm)
{
pThis->abInstr[off++] = pThis->bModRm;
if (pThis->fSib)
pThis->abInstr[off++] = pThis->bSib;
if (pThis->idxMrmRmOp < RT_ELEMENTS(pThis->aOperands))
{
uint64_t uDispValue = pThis->aOperands[pThis->idxMrmRmOp].uImmDispValue;
switch (pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp)
{
case 0: break;
case 8: pThis->abInstr[off + 3] = (uDispValue >> 56) & UINT8_C(0xff);
case 7: pThis->abInstr[off + 3] = (uDispValue >> 48) & UINT8_C(0xff);
case 6: pThis->abInstr[off + 3] = (uDispValue >> 40) & UINT8_C(0xff);
case 5: pThis->abInstr[off + 3] = (uDispValue >> 32) & UINT8_C(0xff);
case 4: pThis->abInstr[off + 3] = (uDispValue >> 24) & UINT8_C(0xff);
case 3: pThis->abInstr[off + 2] = (uDispValue >> 16) & UINT8_C(0xff);
case 2: pThis->abInstr[off + 1] = (uDispValue >> 8) & UINT8_C(0xff);
case 1: pThis->abInstr[off] = uDispValue & UINT8_C(0xff);
break;
default: AssertReleaseFailedReturn(false);
}
off += pThis->aOperands[pThis->idxMrmRmOp].cbMemDisp;
}
}
/*
* Immediates.
*/
uint8_t iOp = pThis->cOperands;
while (iOp-- > 0)
if ((pThis->aOperands[iOp].fFlags & CIDET_OF_K_MASK) == CIDET_OF_K_IMM)
{
uint64_t uImmValue = pThis->aOperands[iOp].uImmDispValue;
switch (pThis->aOperands[iOp].cb)
{
case 8: pThis->abInstr[off + 3] = (uImmValue >> 56) & UINT8_C(0xff);
case 7: pThis->abInstr[off + 3] = (uImmValue >> 48) & UINT8_C(0xff);
case 6: pThis->abInstr[off + 3] = (uImmValue >> 40) & UINT8_C(0xff);
case 5: pThis->abInstr[off + 3] = (uImmValue >> 32) & UINT8_C(0xff);
case 4: pThis->abInstr[off + 3] = (uImmValue >> 24) & UINT8_C(0xff);
case 3: pThis->abInstr[off + 2] = (uImmValue >> 16) & UINT8_C(0xff);
case 2: pThis->abInstr[off + 1] = (uImmValue >> 8) & UINT8_C(0xff);
case 1: pThis->abInstr[off] = uImmValue & UINT8_C(0xff);
break;
default: AssertReleaseFailedReturn(false);
}
off += pThis->aOperands[iOp].cb;
}
pThis->cbInstr = off;
return true;
}
bool CidetCoreReInitCodeBuf(PCIDETCORE pThis)
{
/*
* Re-initialize the buffer. Requires instruction length and positioning.
*/
if (CidetCoreAssembleLength(pThis))
{
pThis->CodeBuf.cb = pThis->cbInstr;
pThis->CodeBuf.off = CIDET_CODE_BUF_SIZE - PAGE_SIZE - pThis->cbInstr;
if (pThis->pfnReInitCodeBuf(pThis, &pThis->CodeBuf))
{
pThis->CodeBuf.fActive = true;
/*
* Update the RIP and CS values in the input and expected contexts.
*/
pThis->InCtx.rip = pThis->CodeBuf.uEffBufAddr + pThis->CodeBuf.offActive - pThis->CodeBuf.uSegBase;
pThis->ExpectedCtx.rip = pThis->InCtx.rip + pThis->cbInstr; /** @todo account for expected traps. */
if (pThis->CodeBuf.uSeg != UINT32_MAX)
{
pThis->InCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg;
pThis->ExpectedCtx.aSRegs[X86_SREG_CS] = pThis->CodeBuf.uSeg;
}
return true;
}
else
pThis->cSkippedReInitCodeBuf++;
}
else
pThis->cSkippedAssemble++;
return false;
}
#ifdef CIDET_DEBUG_DISAS
/**
* @callback_method_impl{FNDISREADBYTES}
*/
static DECLCALLBACK(int) cidetCoreDisReadBytes(PDISSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead)
{
PCIDETCORE pThis = (PCIDETCORE)pDis->pvUser;
memcpy(&pDis->abInstr[offInstr], &pThis->abInstr[offInstr], cbMaxRead);
pDis->cbCachedInstr = offInstr + cbMaxRead;
return VINF_SUCCESS;
}
#endif
bool CidetCoreSetupCodeBuf(PCIDETCORE pThis, unsigned iSubTest)
{
if (CidetCoreAssemble(pThis))
{
//CIDET_DPRINTF(("%04u: %.*Rhxs\n", i, pThis->cbInstr, pThis->abInstr));
#ifdef CIDET_DEBUG_DISAS
DISCPUSTATE Dis;
char szInstr[80] = {0};
uint32_t cbInstr;
int rcDis = DISInstrToStrEx(pThis->InCtx.rip,
CIDETMODE_IS_64BIT(pThis->bMode) ? DISCPUMODE_64BIT
: CIDETMODE_IS_32BIT(pThis->bMode) ? DISCPUMODE_32BIT : DISCPUMODE_16BIT,
cidetCoreDisReadBytes,
pThis,
DISOPTYPE_ALL,
&Dis,
&cbInstr,
szInstr, sizeof(szInstr));
CIDET_DPRINTF(("%04u: %s", iSubTest, szInstr));
Assert(cbInstr == pThis->cbInstr);
#endif
if (pThis->pfnSetupCodeBuf(pThis, &pThis->CodeBuf, pThis->abInstr))
{
return true;
}
pThis->cSkippedSetupCodeBuf++;
}
else
pThis->cSkippedAssemble++;
return false;
}
/**
* Compares the output with the output expectations.
*
* @returns true if ok, false if not (calls pfnFailure too).
* @param pThis The core state structure.
*/
bool CidetCoreCheckResults(PCIDETCORE pThis)
{
if (memcmp(&pThis->ActualCtx, &pThis->ExpectedCtx, CIDETCPUCTX_COMPARE_SIZE) == 0)
return true;
unsigned cDiffs = 0;
#define IF_FIELD_DIFFERS_SET_ERROR(a_Field, a_Fmt) \
if (pThis->ActualCtx.a_Field != pThis->ExpectedCtx.a_Field) \
{ \
CidetCoreSetError(pThis, #a_Field " differs: got %#llx expected %#llx", \
pThis->ActualCtx.a_Field, pThis->ExpectedCtx.a_Field); \
cDiffs++; \
} else do { } while (0)
IF_FIELD_DIFFERS_SET_ERROR(rip, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(rfl, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xAX], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBX], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xCX], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDX], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSP], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xBP], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xSI], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_xDI], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x8], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x9], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x10], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x11], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x12], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x13], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x14], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aGRegs[X86_GREG_x15], "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_CS], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_SS], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_DS], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_ES], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_FS], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(aSRegs[X86_SREG_GS], "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(uXcpt, "%#04x");
IF_FIELD_DIFFERS_SET_ERROR(uErr, "%#04llx");
IF_FIELD_DIFFERS_SET_ERROR(cr2, "%#010llx");
#ifndef CIDET_REDUCED_CTX
IF_FIELD_DIFFERS_SET_ERROR(tr, "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(ldtr, "%#06x");
IF_FIELD_DIFFERS_SET_ERROR(cr0, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(cr3, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(cr4, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(cr8, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr0, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr1, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr2, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr3, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr6, "%#010llx");
IF_FIELD_DIFFERS_SET_ERROR(dr7, "%#010llx");
#endif
AssertMsgFailed(("cDiffs=%d\n", cDiffs));
Assert(cDiffs > 0);
return cDiffs == 0;
}
bool CidetCoreTest_Basic(PCIDETCORE pThis)
{
/*
* Iterate all encodings.
*/
if (!CidetCoreSetupFirstBaseEncoding(pThis))
return CidetCoreSetError(pThis, "CidetCoreSetupFirstBaseEncoding failed");
unsigned cExecuted = 0;
unsigned cSkipped = 0;
do
{
/*
* Iterate data buffer configurations (one iteration if none).
*/
if (CidetCoreSetupFirstMemoryOperandConfig(pThis))
{
do
{
/*
* Iterate code buffer configurations.
*/
if (!CidetCoreSetupFirstCodeBufferConfig(pThis))
return CidetCoreSetError(pThis, "CidetCoreSetupFirstMemoryOperandConfig failed");
do
{
/*
* Set up inputs and expected outputs, then emit the test code.
*/
pThis->InCtx = pThis->InTemplateCtx;
pThis->InCtx.fTrickyStack = pThis->fHasStackRegInMrmRmBase || pThis->fHasStackRegInMrmReg;
pThis->ExpectedCtx = pThis->InCtx;
if ( CidetCoreReInitCodeBuf(pThis)
&& CidetCoreSetupInOut(pThis)
&& CidetCoreSetupCodeBuf(pThis, cSkipped + cExecuted)
)
{
if (pThis->pfnExecute(pThis))
{
cExecuted++;
/*
* Check the result against our expectations.
*/
CidetCoreCheckResults(pThis);
/** @todo check result. */
}
else
cSkipped++;
}
else
cSkipped++;
} while (CidetCoreSetupNextCodeBufferConfig(pThis));
} while (CidetCoreSetupNextMemoryOperandConfig(pThis));
}
else
cSkipped++;
} while (CidetCoreSetupNextBaseEncoding(pThis));
CIDET_DPRINTF(("CidetCoreTest_Basic: cExecuted=%u cSkipped=%u\n"
" cSkippedSetupInOut =%u\n"
" cSkippedReInitDataBuf =%u\n"
" cSkippedSetupDataBuf =%u\n"
" cSkippedDataBufWrtRip =%u\n"
" cSkippedAssemble =%u\n"
" cSkippedReInitCodeBuf =%u\n"
" cSkippedSetupCodeBuf =%u\n"
" cSkippedSameBaseIndexRemainder =%u\n"
" cSkippedOnlyIndexRemainder =%u\n"
" cSkippedDirectAddressingOverflow =%u\n"
,
cExecuted, cSkipped,
pThis->cSkippedSetupInOut,
pThis->cSkippedReInitDataBuf,
pThis->cSkippedSetupDataBuf,
pThis->cSkippedDataBufWrtRip,
pThis->cSkippedAssemble,
pThis->cSkippedReInitCodeBuf,
pThis->cSkippedSetupCodeBuf,
pThis->cSkippedSameBaseIndexRemainder,
pThis->cSkippedOnlyIndexRemainder,
pThis->cSkippedDirectAddressingOverflow
));
return true;
}
bool CidetCoreTestInstruction(PCIDETCORE pThis, PCCIDETINSTR pInstr)
{
AssertReleaseMsgReturn(RT_VALID_PTR(pThis), ("%p\n", pThis), false);
AssertReleaseReturn(pThis->u32Magic == CIDETCORE_MAGIC, false);
AssertReleaseReturn(pThis->cCodeBufConfigs > 0, false);
if (!CideCoreSetInstruction(pThis, pInstr))
return CidetCoreSetError(pThis, "CideCoreSetInstruction failed");
bool fResult = CidetCoreTest_Basic(pThis);
return fResult;
}