diff options
Diffstat (limited to 'src/VBox/ValidationKit/utils/cpu/cidet-core.cpp')
-rw-r--r-- | src/VBox/ValidationKit/utils/cpu/cidet-core.cpp | 2368 |
1 files changed, 2368 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp new file mode 100644 index 00000000..ff350861 --- /dev/null +++ b/src/VBox/ValidationKit/utils/cpu/cidet-core.cpp @@ -0,0 +1,2368 @@ +/* $Id: cidet-core.cpp $ */ +/** @file + * CPU Instruction Decoding & Execution Tests - Simple Instructions. + */ + +/* + * Copyright (C) 2014-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* 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 <iprt/errcore.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) +{ + RT_NOREF_PV(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) +{ + RT_NOREF_PV(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) +{ + RT_NOREF_PV(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; + RT_FALL_THRU(); + case X86_SREG_ES: + pThis->uSegPrf = X86_SREG_CS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_CS) + return true; + RT_FALL_THRU(); + case X86_SREG_CS: + pThis->uSegPrf = X86_SREG_SS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_SS) + return true; + RT_FALL_THRU(); + case X86_SREG_SS: + pThis->uSegPrf = X86_SREG_DS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_DS) + return true; + RT_FALL_THRU(); + case X86_SREG_DS: + pThis->uSegPrf = X86_SREG_FS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_FS) + return true; + RT_FALL_THRU(); + case X86_SREG_FS: + pThis->uSegPrf = X86_SREG_GS; + if (pThis->fTestCfg & CIDET_TESTCFG_SEG_PRF_GS) + return true; + RT_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) +{ + RT_NOREF_PV(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) +{ + RT_NOREF_PV(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++; RT_FALL_THRU(); + case 2: off++; RT_FALL_THRU(); + 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++; RT_FALL_THRU(); + case 2: pThis->abInstr[off++] = *pbOpcode++; RT_FALL_THRU(); + 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); RT_FALL_THRU(); + case 7: pThis->abInstr[off + 3] = (uDispValue >> 48) & UINT8_C(0xff); RT_FALL_THRU(); + case 6: pThis->abInstr[off + 3] = (uDispValue >> 40) & UINT8_C(0xff); RT_FALL_THRU(); + case 5: pThis->abInstr[off + 3] = (uDispValue >> 32) & UINT8_C(0xff); RT_FALL_THRU(); + case 4: pThis->abInstr[off + 3] = (uDispValue >> 24) & UINT8_C(0xff); RT_FALL_THRU(); + case 3: pThis->abInstr[off + 2] = (uDispValue >> 16) & UINT8_C(0xff); RT_FALL_THRU(); + case 2: pThis->abInstr[off + 1] = (uDispValue >> 8) & UINT8_C(0xff); RT_FALL_THRU(); + 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); RT_FALL_THRU(); + case 7: pThis->abInstr[off + 3] = (uImmValue >> 48) & UINT8_C(0xff); RT_FALL_THRU(); + case 6: pThis->abInstr[off + 3] = (uImmValue >> 40) & UINT8_C(0xff); RT_FALL_THRU(); + case 5: pThis->abInstr[off + 3] = (uImmValue >> 32) & UINT8_C(0xff); RT_FALL_THRU(); + case 4: pThis->abInstr[off + 3] = (uImmValue >> 24) & UINT8_C(0xff); RT_FALL_THRU(); + case 3: pThis->abInstr[off + 2] = (uImmValue >> 16) & UINT8_C(0xff); RT_FALL_THRU(); + case 2: pThis->abInstr[off + 1] = (uImmValue >> 8) & UINT8_C(0xff); RT_FALL_THRU(); + 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); +#else + RT_NOREF_PV(iSubTest); +#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; +} + |