1094 lines
37 KiB
C++
1094 lines
37 KiB
C++
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Assemble X86 machine implementation.
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation, 2000-2001.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include "ntsdp.hpp"
|
||
|
|
||
|
#include "i386_asm.h"
|
||
|
|
||
|
UCHAR asm386(ULONG, PUCHAR, PUCHAR);
|
||
|
|
||
|
UCHAR CheckData(void);
|
||
|
PUCHAR ProcessOpcode(void);
|
||
|
PUCHAR GetTemplate(PUCHAR);
|
||
|
UCHAR MatchTemplate(PULONG);
|
||
|
void CheckTemplate(void);
|
||
|
UCHAR CheckPrefix(PUCHAR);
|
||
|
void AssembleInstr(void);
|
||
|
UCHAR MatchOperand(PASM_VALUE, UCHAR);
|
||
|
void OutputInstr(void);
|
||
|
void OutputValue(UCHAR size, PUCHAR pchValue);
|
||
|
|
||
|
extern UCHAR PeekAsmChar(void);
|
||
|
extern ULONG PeekAsmToken(PULONG);
|
||
|
extern void AcceptAsmToken(void);
|
||
|
|
||
|
extern void GetAsmExpr(PASM_VALUE, UCHAR);
|
||
|
extern void GetAsmOperand(PASM_VALUE);
|
||
|
extern PUCHAR X86SearchOpcode(PUCHAR);
|
||
|
extern ULONG savedAsmClass;
|
||
|
extern OPNDTYPE mapOpndType[];
|
||
|
|
||
|
// flags and values to build the assembled instruction
|
||
|
|
||
|
static UCHAR fWaitPrfx; // if set, use WAIT prefix for float instr
|
||
|
static UCHAR fOpndOvrd; // if set, use operand override prefix
|
||
|
static UCHAR fAddrOvrd; // if set, use address override prefix
|
||
|
static UCHAR segOvrd; // if nonzero, use segment override prefix
|
||
|
static UCHAR preOpcode; // if nonzero, use byte before opcode
|
||
|
static UCHAR inOpcode; // opcode of instruction
|
||
|
static UCHAR postOpcode; // if nonzero, use byte after opcode
|
||
|
static UCHAR fModrm; // if set, modrm byte is defined
|
||
|
static UCHAR modModrm; // if fModrm, mod component of modrm
|
||
|
static UCHAR regModrm; // if fModrm, reg component of modrm
|
||
|
static UCHAR rmModrm; // if fModrm, rm component of modrm
|
||
|
static UCHAR fSib; // if set, sib byte is defined
|
||
|
static UCHAR scaleSib; // if fSib, scale component of sib
|
||
|
static UCHAR indexSib; // if fSib, index component of sib
|
||
|
static UCHAR baseSib; // if fSib, base component of sib
|
||
|
static UCHAR fSegPtr; // if set, segment for far call defined
|
||
|
static USHORT segPtr; // if fSegPtr, value of far call segment
|
||
|
static UCHAR addrSize; // size of address: 0, 1, 2, 4
|
||
|
static LONG addrValue; // value of address, if used
|
||
|
static UCHAR immedSize; // size of immediate: 0, 1, 2, 4
|
||
|
static LONG immedValue; // value of immediate, if used
|
||
|
static UCHAR immedSize2; // size of second immediate, if used
|
||
|
static LONG immedValue2; // value of second immediate, if used
|
||
|
static ULONG addrAssem; // assembly address (formal)
|
||
|
static PUCHAR pchBin; // pointer to binary result string
|
||
|
|
||
|
// flags and values of the current instruction template being used
|
||
|
|
||
|
static UCHAR cntTmplOpnd; // count of operands in template
|
||
|
static UCHAR tmplType[3]; // operand types for current template
|
||
|
static UCHAR tmplSize[3]; // operand sizes for current template
|
||
|
static UCHAR fForceSize; // set if operand size must be specified
|
||
|
static UCHAR fAddToOp; // set if addition to opcode
|
||
|
static UCHAR fNextOpnd; // set if character exists for next operand
|
||
|
static UCHAR fSegOnly; // set if only segment is used for operand
|
||
|
static UCHAR fMpNext; // set on 'Mv' tmpl if next tmpl is 'Mp'
|
||
|
static UCHAR segIndex; // index of segment for PUSH/POP
|
||
|
|
||
|
// values describing the operands processed from the command line
|
||
|
|
||
|
static UCHAR cntInstOpnd; // count of operands read from input line
|
||
|
static UCHAR sizeOpnd; // size of operand for template with size v
|
||
|
static ASM_VALUE avInstOpnd[3]; // asm values from input line
|
||
|
|
||
|
PUCHAR pchAsmLine; // pointer to input line (formal)
|
||
|
UCHAR fDBit = TRUE; // set for 32-bit addr/operand mode
|
||
|
|
||
|
UCHAR segToOvrdByte[] = {
|
||
|
0x00, // segX
|
||
|
0x26, // segES
|
||
|
0x2e, // segCS
|
||
|
0x36, // segSS
|
||
|
0x3e, // segDS
|
||
|
0x64, // segFS
|
||
|
0x65 // segGS
|
||
|
};
|
||
|
|
||
|
void
|
||
|
BaseX86MachineInfo::Assemble(PADDR paddr, PSTR pchInput)
|
||
|
{
|
||
|
ULONG length;
|
||
|
UCHAR chBinary[60];
|
||
|
|
||
|
length = (ULONG)asm386((ULONG)Flat(*paddr), (PUCHAR)pchInput, chBinary);
|
||
|
|
||
|
if (length) {
|
||
|
// printf("setting memory at addr: %s - count: %d\n",
|
||
|
// FormatAddr64(Flat(*paddr)), length);
|
||
|
if (length != SetMemString(paddr, chBinary, length)) {
|
||
|
error(MEMORY);
|
||
|
}
|
||
|
AddrAdd(paddr,length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UCHAR asm386 (ULONG addrAssemble, PUCHAR pchAssemble, PUCHAR pchBinary)
|
||
|
{
|
||
|
PUCHAR pchTemplate;
|
||
|
|
||
|
UCHAR index; // loop index and temp
|
||
|
ULONG temp; // general temporary value
|
||
|
|
||
|
UCHAR errIndex; // error index of all templates
|
||
|
ULONG errType; // error type of all templates
|
||
|
|
||
|
// initialize flags and state variables
|
||
|
|
||
|
addrAssem = addrAssemble; // make assembly address global
|
||
|
pchAsmLine = pchAssemble; // make input string pointer global
|
||
|
pchBin = pchBinary; // make binary string pointer global
|
||
|
|
||
|
savedAsmClass = (ULONG)-1; // no peeked token
|
||
|
|
||
|
segOvrd = 0; // no segment override
|
||
|
cntInstOpnd = 0; // no input operands read yet
|
||
|
fModrm = fSib = fSegPtr = FALSE; // no modrm, sib, or far seg
|
||
|
addrSize = immedSize = immedSize2 = 0; // no addr or immed
|
||
|
|
||
|
// check for data entry commands for byte (db), word (dw), dword (dd)
|
||
|
// if so, process multiple operands directly
|
||
|
|
||
|
if (!CheckData()) {
|
||
|
|
||
|
// from the string in pchAsmLine, parse and lookup the opcode
|
||
|
// to return a pointer to its template. check and process
|
||
|
// any prefixes, reading the next opcode for each prefix
|
||
|
|
||
|
do
|
||
|
pchTemplate = ProcessOpcode();
|
||
|
while (CheckPrefix(pchTemplate));
|
||
|
|
||
|
// if a pending opcode to process, pchTemplate is not NULL
|
||
|
|
||
|
if (pchTemplate) {
|
||
|
|
||
|
// fNextOpnd is initially set on the condition of characters
|
||
|
// being available for the first operand on the input line
|
||
|
|
||
|
fNextOpnd = (UCHAR)(PeekAsmToken(&temp) != ASM_EOL_CLASS);
|
||
|
|
||
|
// continue until match occurs or last template read
|
||
|
|
||
|
errIndex = 0; // start with no error
|
||
|
do {
|
||
|
|
||
|
// get infomation on next template - return pointer to
|
||
|
// next template or NULL if last in list
|
||
|
|
||
|
pchTemplate = GetTemplate(pchTemplate);
|
||
|
|
||
|
// match the loaded template against the operands input
|
||
|
// if mismatch, index has the operand index + 1 of
|
||
|
// the error while temp has the error type.
|
||
|
|
||
|
index = MatchTemplate(&temp);
|
||
|
|
||
|
// determine the error to report as templates are matched
|
||
|
// update errIndex to index if later operand
|
||
|
// if same operand index, prioritize to give best error:
|
||
|
// high: SIZE, BADRANGE, OVERFLOW
|
||
|
// medium: OPERAND
|
||
|
// low: TOOFEW, TOOMANY
|
||
|
|
||
|
if (index > errIndex
|
||
|
|| (index == errIndex &&
|
||
|
(errType == TOOFEW || errType == TOOMANY
|
||
|
|| temp == SIZE || temp == BADRANGE
|
||
|
|| temp == OVERFLOW))) {
|
||
|
errIndex = index;
|
||
|
errType = temp;
|
||
|
};
|
||
|
|
||
|
}
|
||
|
while (index && pchTemplate);
|
||
|
|
||
|
// if error occured on template match, process it
|
||
|
|
||
|
if (index)
|
||
|
error(errType);
|
||
|
|
||
|
// preliminary type and size matching has been
|
||
|
// successful on the current template.
|
||
|
// perform further checks for size ambiguity.
|
||
|
// at this point, the assembly is committed to the current
|
||
|
// template. either an error or a successful assembly
|
||
|
// follows.
|
||
|
|
||
|
CheckTemplate();
|
||
|
|
||
|
// from the template and operand information, set the field
|
||
|
// information of the assembled instruction
|
||
|
|
||
|
AssembleInstr();
|
||
|
|
||
|
// from the assembled instruction information, create the
|
||
|
// corresponding binary information
|
||
|
|
||
|
OutputInstr();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return the size of the binary string output (can be zero)
|
||
|
|
||
|
return (UCHAR)(pchBin - pchBinary); // length of binary string
|
||
|
}
|
||
|
|
||
|
UCHAR CheckData (void)
|
||
|
{
|
||
|
PUCHAR pchBinStart = pchBin;
|
||
|
UCHAR ch;
|
||
|
UCHAR size = 0;
|
||
|
ASM_VALUE avItem;
|
||
|
ULONG temp;
|
||
|
|
||
|
// perform an explicit parse for 'db', 'dw', and 'dd'
|
||
|
// and set size to that of the data item
|
||
|
|
||
|
ch = PeekAsmChar();
|
||
|
if (tolower(ch) == 'd') {
|
||
|
ch = (UCHAR)tolower(*(pchAsmLine + 1));
|
||
|
if (ch == 'b')
|
||
|
size = 1;
|
||
|
if (ch == 'w')
|
||
|
size = 2;
|
||
|
if (ch == 'd')
|
||
|
size = 4;
|
||
|
if (size) {
|
||
|
ch = *(pchAsmLine + 2);
|
||
|
if (ch != ' ' && ch != '\t' && ch != '\0')
|
||
|
size = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if a valid command entered, then size is nonzero
|
||
|
|
||
|
if (size) {
|
||
|
|
||
|
// move pointer over command and set loop condition
|
||
|
|
||
|
pchAsmLine += 2;
|
||
|
temp = ASM_COMMA_CLASS;
|
||
|
|
||
|
// for each item in list:
|
||
|
// check for binary buffer overflow
|
||
|
// get expression value - error if not immediate value
|
||
|
// test for byte and word overflow, if applicable
|
||
|
// write the value to the binary buffer
|
||
|
// check for comma for next operand
|
||
|
|
||
|
while (temp == ASM_COMMA_CLASS) {
|
||
|
if (pchBin >= pchBinStart + 40)
|
||
|
error(LISTSIZE);
|
||
|
GetAsmExpr(&avItem, FALSE);
|
||
|
if (avItem.flags != fIMM)
|
||
|
error(OPERAND);
|
||
|
if (avItem.reloc > 1)
|
||
|
error(RELOC);
|
||
|
if ((size == 1 && ((LONG)avItem.value < -0x80L
|
||
|
|| (LONG)avItem.value > 0xffL))
|
||
|
|| (size == 2 && ((LONG)avItem.value < -0x8000L
|
||
|
|| (LONG)avItem.value > 0xffffL)))
|
||
|
error(OVERFLOW);
|
||
|
OutputValue(size, (PUCHAR)&avItem.value);
|
||
|
|
||
|
temp = PeekAsmToken(&temp);
|
||
|
if (temp == ASM_COMMA_CLASS)
|
||
|
AcceptAsmToken();
|
||
|
else if (temp != ASM_EOL_CLASS)
|
||
|
error(SYNTAX);
|
||
|
}
|
||
|
|
||
|
// check for any remaining part after the last operand
|
||
|
|
||
|
if (PeekAsmChar() != '\0')
|
||
|
error(SYNTAX);
|
||
|
}
|
||
|
|
||
|
// return size of item listed (zero for none)
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
PUCHAR ProcessOpcode (void)
|
||
|
{
|
||
|
UCHAR ch;
|
||
|
UCHAR cbOpcode = 0;
|
||
|
PUCHAR pchTemplate;
|
||
|
UCHAR szOpcode[12];
|
||
|
|
||
|
// skip over any leading white space
|
||
|
|
||
|
do
|
||
|
ch = *pchAsmLine++;
|
||
|
while (ch == ' ' || ch == '\t');
|
||
|
|
||
|
// return NULL if end of line
|
||
|
|
||
|
if (ch == '\0')
|
||
|
return NULL;
|
||
|
|
||
|
// parse out opcode - first string [a-z] [0-9] (case insensitive)
|
||
|
|
||
|
ch = (UCHAR)tolower(ch);
|
||
|
while (((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) &&
|
||
|
cbOpcode < 11) {
|
||
|
szOpcode[cbOpcode++] = ch;
|
||
|
ch = (UCHAR)tolower(*pchAsmLine); pchAsmLine++;
|
||
|
}
|
||
|
|
||
|
// if empty or too long, then error
|
||
|
|
||
|
if (cbOpcode == 0 || cbOpcode == 11)
|
||
|
error(BADOPCODE);
|
||
|
|
||
|
// allow opcode to have trailing colon and terminate
|
||
|
|
||
|
if (ch == ':') {
|
||
|
szOpcode[cbOpcode++] = ch;
|
||
|
ch = (UCHAR)tolower(*pchAsmLine); pchAsmLine++;
|
||
|
}
|
||
|
szOpcode[cbOpcode] = '\0';
|
||
|
pchAsmLine--;
|
||
|
|
||
|
// get pointer to template series for opcode found
|
||
|
|
||
|
pchTemplate = X86SearchOpcode(szOpcode);
|
||
|
if (pchTemplate == NULL)
|
||
|
error(BADOPCODE);
|
||
|
|
||
|
return pchTemplate;
|
||
|
}
|
||
|
|
||
|
PUCHAR GetTemplate (PUCHAR pchTemplate)
|
||
|
{
|
||
|
UCHAR ch;
|
||
|
UCHAR ftEnd; // set if tEnd for last template in list
|
||
|
UCHAR feEnd; // set if eEnd for last token in template
|
||
|
|
||
|
// initialize template variables and flags
|
||
|
|
||
|
cntTmplOpnd = segIndex = 0;
|
||
|
tmplType[0] = tmplType[1] = tmplType[2] = typNULL;
|
||
|
tmplSize[0] = tmplSize[1] = tmplSize[2] = sizeX;
|
||
|
fForceSize = fAddToOp = fSegOnly = fMpNext = FALSE;
|
||
|
|
||
|
fWaitPrfx = FALSE; // no WAIT prefix
|
||
|
fOpndOvrd = fAddrOvrd = FALSE; // no operand or addr overrides
|
||
|
preOpcode = postOpcode = 0; // no pre- or post-opcode
|
||
|
regModrm = 0; // this is part of some opcodes
|
||
|
|
||
|
ch = *pchTemplate++;
|
||
|
|
||
|
// set pre-opcode for two-byte opcodes (0x0f??) and advance
|
||
|
// template if needed
|
||
|
|
||
|
if (ch == 0x0f) {
|
||
|
preOpcode = ch;
|
||
|
ch = *pchTemplate++;
|
||
|
}
|
||
|
|
||
|
inOpcode = ch; // set opcode
|
||
|
|
||
|
// set post-opcode and advance template for floating-point
|
||
|
// instructions (0xd8 - 0xdf) using a second byte in
|
||
|
// the range 0xc0 - 0xff that is read from the template
|
||
|
|
||
|
if ((ch & ~0x7) == 0xd8) {
|
||
|
ch = *pchTemplate;
|
||
|
if (ch >= 0xc0) {
|
||
|
postOpcode = ch;
|
||
|
pchTemplate++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// loop for each flag and/or operand token in template
|
||
|
// the last token in the list has the eEnd bit set.
|
||
|
|
||
|
do {
|
||
|
// read the next template token
|
||
|
|
||
|
ch = *pchTemplate++;
|
||
|
|
||
|
// extract the tEnd and eEnd bits from the token
|
||
|
|
||
|
ftEnd = (UCHAR)(ch & tEnd);
|
||
|
feEnd = (UCHAR)(ch & eEnd);
|
||
|
ch &= ~(tEnd | eEnd);
|
||
|
|
||
|
// if extracted token is a flag, do the appropriate action
|
||
|
|
||
|
if (ch < asRegBase)
|
||
|
switch (ch) {
|
||
|
case as0x0a:
|
||
|
|
||
|
// the postOpcode is set for some decimal instructions
|
||
|
|
||
|
postOpcode = 0x0a;
|
||
|
break;
|
||
|
|
||
|
case asOpRg:
|
||
|
|
||
|
// fAddToOp is set if the register index is added
|
||
|
// directly to the base opcode value
|
||
|
|
||
|
fAddToOp = TRUE;
|
||
|
break;
|
||
|
|
||
|
case asSiz0:
|
||
|
|
||
|
// fOpndOvrd is set or cleared to force a 16-bit operand
|
||
|
|
||
|
fOpndOvrd = fDBit;
|
||
|
break;
|
||
|
|
||
|
case asSiz1:
|
||
|
|
||
|
// fOpndOvrd is set or cleared to force a 32-bit operand
|
||
|
|
||
|
fOpndOvrd = (UCHAR)!fDBit;
|
||
|
break;
|
||
|
|
||
|
case asWait:
|
||
|
|
||
|
// the flag fWaitPrfx is set to emit WAIT before the
|
||
|
// instruction
|
||
|
|
||
|
fWaitPrfx = TRUE;
|
||
|
break;
|
||
|
|
||
|
case asSeg:
|
||
|
|
||
|
// in XLAT, the optional memory operand is used to
|
||
|
// just specify a segment override prefix
|
||
|
|
||
|
fSegOnly = TRUE;
|
||
|
break;
|
||
|
|
||
|
case asFSiz:
|
||
|
|
||
|
// fForceSize is set when a specific size of a memory
|
||
|
// operand must be given for some floating instrs
|
||
|
|
||
|
fForceSize = TRUE;
|
||
|
break;
|
||
|
|
||
|
case asMpNx:
|
||
|
|
||
|
// fMpNext is set when the next template operand is
|
||
|
// 'Mp' and is used to determine how to match
|
||
|
// 'Md' since it matches both 'Mp' and 'Mv'
|
||
|
|
||
|
fMpNext = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// if token is REG value bit, set the variable regModrm to
|
||
|
// set the opcode-dependent reg value in the modrm byte
|
||
|
|
||
|
else if (ch < opnBase)
|
||
|
regModrm = (UCHAR)(ch - asRegBase);
|
||
|
|
||
|
// otherwise, token is operand descriptor.
|
||
|
// if segment operand, get segment number from template
|
||
|
// normalize and map to get operand type and size.
|
||
|
|
||
|
else {
|
||
|
if (ch == opnSeg)
|
||
|
segIndex = *pchTemplate++;
|
||
|
ch -= opnBase;
|
||
|
tmplType[cntTmplOpnd] = mapOpndType[ch].type;
|
||
|
tmplSize[cntTmplOpnd++] = mapOpndType[ch].size;
|
||
|
}
|
||
|
}
|
||
|
while (!ftEnd);
|
||
|
|
||
|
// return either the pointer to the next template or NULL if
|
||
|
// the last template for the opcode has been processed
|
||
|
|
||
|
return (feEnd ? NULL : pchTemplate);
|
||
|
}
|
||
|
|
||
|
UCHAR MatchTemplate (PULONG pErrType)
|
||
|
{
|
||
|
UCHAR fMatch = TRUE;
|
||
|
UCHAR index;
|
||
|
ULONG temp;
|
||
|
PASM_VALUE pavInstOpnd; // pointer to current operand from input
|
||
|
|
||
|
// process matching for each operand in the specified template
|
||
|
// stop at last operand or when mismatch occurs
|
||
|
|
||
|
for (index = 0; index < cntTmplOpnd && fMatch; index++) {
|
||
|
|
||
|
// set pointer to current instruction operand
|
||
|
|
||
|
pavInstOpnd = &avInstOpnd[index];
|
||
|
|
||
|
// if input operand has not yet been read, check flag
|
||
|
// for existence and process it.
|
||
|
|
||
|
if (index == cntInstOpnd) {
|
||
|
fMatch = fNextOpnd;
|
||
|
*pErrType = TOOFEW;
|
||
|
if (fMatch) {
|
||
|
cntInstOpnd++;
|
||
|
GetAsmOperand(pavInstOpnd);
|
||
|
|
||
|
// recompute existence of next possible operand
|
||
|
// comma implies TRUE, EOL implies FALSE, else error
|
||
|
|
||
|
temp = PeekAsmToken(&temp);
|
||
|
if (temp == ASM_COMMA_CLASS) {
|
||
|
AcceptAsmToken();
|
||
|
fNextOpnd = TRUE;
|
||
|
}
|
||
|
else if (temp == ASM_EOL_CLASS)
|
||
|
fNextOpnd = FALSE;
|
||
|
else
|
||
|
error(EXTRACHARS); // bad parse - immediate error
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fMatch) {
|
||
|
fMatch = MatchOperand(pavInstOpnd, tmplType[index]);
|
||
|
*pErrType = OPERAND;
|
||
|
}
|
||
|
|
||
|
// if the template and operand type match, do preliminary
|
||
|
// check on size based solely on template size specified
|
||
|
|
||
|
if (fMatch) {
|
||
|
if (tmplType[index] == typJmp) {
|
||
|
|
||
|
// for relative jumps, test if byte offset is
|
||
|
// sufficient by computing offset which is
|
||
|
// the target offset less the offset of the
|
||
|
// next instruction. (assume Jb instructions
|
||
|
// are two bytes in length.
|
||
|
|
||
|
temp = pavInstOpnd->value - (addrAssem + 2);
|
||
|
fMatch = (UCHAR)(tmplSize[index] == sizeV
|
||
|
|| ((LONG)temp >= -0x80 && (LONG)temp <= 0x7f));
|
||
|
*pErrType = BADRANGE;
|
||
|
}
|
||
|
|
||
|
else if (tmplType[index] == typImm) {
|
||
|
|
||
|
// for immediate operand,
|
||
|
// template sizeV matches sizeB, sizeW, sizeV (all)
|
||
|
// template sizeW matches sizeB, sizeW
|
||
|
// template sizeB matches sizeB
|
||
|
|
||
|
fMatch = (UCHAR)(tmplSize[index] == sizeV
|
||
|
|| pavInstOpnd->size == tmplSize[index]
|
||
|
|| pavInstOpnd->size == sizeB);
|
||
|
*pErrType = OVERFLOW;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
// for nonimmediate operand,
|
||
|
// template sizeX (unspecified) matches all
|
||
|
// operand sizeX (unspecified) matches all
|
||
|
// same template and operand size matches
|
||
|
// template sizeV matches operand sizeW and sizeD
|
||
|
// (EXCEPT for sizeD when fMpNext and fDBit set)
|
||
|
// template sizeP matches operand sizeD and sizeF
|
||
|
// template sizeA matches operand sizeD and sizeQ
|
||
|
|
||
|
fMatch = (UCHAR)(tmplSize[index] == sizeX
|
||
|
|| pavInstOpnd->size == sizeX
|
||
|
|| tmplSize[index] == pavInstOpnd->size
|
||
|
|| (tmplSize[index] == sizeV
|
||
|
&& (pavInstOpnd->size == sizeW
|
||
|
|| (pavInstOpnd->size == sizeD
|
||
|
&& (!fMpNext || fDBit))))
|
||
|
|| (tmplSize[index] == sizeP
|
||
|
&& (pavInstOpnd->size == sizeD
|
||
|
|| pavInstOpnd->size == sizeF))
|
||
|
|| (tmplSize[index] == sizeA
|
||
|
&& (pavInstOpnd->size == sizeD
|
||
|
|| pavInstOpnd->size == sizeQ)));
|
||
|
*pErrType = SIZE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if more operands to read, then no match
|
||
|
|
||
|
if (fMatch & fNextOpnd) {
|
||
|
fMatch = FALSE;
|
||
|
index++; // next operand is in error
|
||
|
*pErrType = TOOMANY;
|
||
|
}
|
||
|
|
||
|
return fMatch ? (UCHAR)0 : index;
|
||
|
}
|
||
|
|
||
|
void CheckTemplate (void)
|
||
|
{
|
||
|
UCHAR index;
|
||
|
|
||
|
// if fForceSize is set, then the first (and only) operand is a
|
||
|
// memory type. return an error if its size is unspecified.
|
||
|
|
||
|
if (fForceSize && avInstOpnd[0].size == sizeX)
|
||
|
error(OPERAND);
|
||
|
|
||
|
// test for template with leading entries of 'Xb', where
|
||
|
// 'X' includes all types except immediate ('I'). if any
|
||
|
// are defined, at least one operand must have a byte size.
|
||
|
// this handles the cases of byte or word/dword ambiguity for
|
||
|
// instructions with no register operands.
|
||
|
|
||
|
sizeOpnd = sizeX;
|
||
|
for (index = 0; index < 2; index++)
|
||
|
if (tmplType[index] != typImm && tmplSize[index] == sizeB) {
|
||
|
if (avInstOpnd[index].size != sizeX)
|
||
|
sizeOpnd = avInstOpnd[index].size;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
if (index != 0 && sizeOpnd == sizeX)
|
||
|
error(SIZE);
|
||
|
|
||
|
// for templates with one entry of 'Xp', where 'X' is
|
||
|
// not 'A', allowable sizes are sizeX (unspecified),
|
||
|
// sizeD (dword), and sizeF (fword). process by
|
||
|
// mapping entry sizes 'p' -> 'v', sizeD -> sizeW,
|
||
|
// and sizeF -> sizeD
|
||
|
// (template 'Ap' is absolute with explicit segment and
|
||
|
// 'v'-sized offset - really treated as 'Av')
|
||
|
|
||
|
if (tmplSize[0] == sizeP) {
|
||
|
tmplSize[0] = sizeV;
|
||
|
if (avInstOpnd[0].size == sizeD)
|
||
|
avInstOpnd[0].size = sizeW;
|
||
|
if (avInstOpnd[0].size == sizeF)
|
||
|
avInstOpnd[0].size = sizeD;
|
||
|
}
|
||
|
|
||
|
// for templates with the second entry of 'Ma', the
|
||
|
// allowable sizes are sizeX (unspecified),
|
||
|
// sizeD (dword), and sizeQ (qword). process by
|
||
|
// mapping entry sizes 'a' -> 'v', sizeD -> sizeW,
|
||
|
// and sizeQ -> sizeD
|
||
|
// (template entry 'Ma' is used only with the BOUND instruction)
|
||
|
|
||
|
if (tmplSize[1] == sizeA) {
|
||
|
tmplSize[1] = sizeV;
|
||
|
if (avInstOpnd[1].size == sizeD)
|
||
|
avInstOpnd[1].size = sizeW;
|
||
|
if (avInstOpnd[1].size == sizeQ)
|
||
|
avInstOpnd[1].size = sizeD;
|
||
|
}
|
||
|
|
||
|
// test for template with leading entries of 'Xv' optionally
|
||
|
// followed by one 'Iv' entry. if two 'Xv' entries, set
|
||
|
// size error if one is word and the other is dword. if
|
||
|
// 'Iv' entry, test for overflow.
|
||
|
|
||
|
sizeOpnd = sizeX;
|
||
|
for (index = 0; index < 3; index++)
|
||
|
if (tmplSize[index] == sizeV)
|
||
|
if (tmplType[index] != typImm) {
|
||
|
|
||
|
// template entry is 'Xv', set size and check size
|
||
|
|
||
|
if (avInstOpnd[index].size != sizeX) {
|
||
|
if (sizeOpnd != sizeX && sizeOpnd
|
||
|
!= avInstOpnd[index].size)
|
||
|
error(SIZE);
|
||
|
sizeOpnd = avInstOpnd[index].size;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
// template entry is 'Iv', set sizeOpnd to either
|
||
|
// sizeW or sizeD and check for overflow
|
||
|
|
||
|
if (sizeOpnd == sizeX)
|
||
|
sizeOpnd = (UCHAR)(fDBit ? sizeD : sizeW);
|
||
|
if (sizeOpnd == sizeW && avInstOpnd[index].size == sizeD)
|
||
|
error(OVERFLOW);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UCHAR CheckPrefix (PUCHAR pchTemplate)
|
||
|
{
|
||
|
UCHAR fPrefix;
|
||
|
|
||
|
fPrefix = (UCHAR)(pchTemplate && *pchTemplate != 0x0f
|
||
|
&& (*pchTemplate & ~7) != 0xd8
|
||
|
&& *(pchTemplate + 1) == (asPrfx + tEnd + eEnd));
|
||
|
if (fPrefix)
|
||
|
*pchBin++ = *pchTemplate;
|
||
|
|
||
|
return fPrefix;
|
||
|
}
|
||
|
|
||
|
void AssembleInstr (void)
|
||
|
{
|
||
|
UCHAR size;
|
||
|
UCHAR index;
|
||
|
PASM_VALUE pavInstOpnd;
|
||
|
|
||
|
// set operand override flag if operand size differs than fDBit
|
||
|
// (the flag may already be set due to opcode template flag)
|
||
|
|
||
|
if ((sizeOpnd == sizeW && fDBit)
|
||
|
|| (sizeOpnd == sizeD && !fDBit))
|
||
|
fOpndOvrd = TRUE;
|
||
|
|
||
|
// for each operand of the successfully matched template,
|
||
|
// build the assembled instruction
|
||
|
// for template entries with size 'v', sizeOpnd has the size
|
||
|
|
||
|
for (index = 0; index < cntTmplOpnd; index++) {
|
||
|
pavInstOpnd = &avInstOpnd[index];
|
||
|
size = tmplSize[index];
|
||
|
if (size == sizeV)
|
||
|
size = sizeOpnd;
|
||
|
|
||
|
switch (tmplType[index]) {
|
||
|
case typExp:
|
||
|
case typMem:
|
||
|
if (!segOvrd) // first one only (movsb...)
|
||
|
segOvrd = segToOvrdByte[pavInstOpnd->segovr];
|
||
|
if (fSegOnly)
|
||
|
break;
|
||
|
|
||
|
fModrm = TRUE;
|
||
|
if (pavInstOpnd->flags == fREG) {
|
||
|
modModrm = 3;
|
||
|
rmModrm = pavInstOpnd->base;
|
||
|
}
|
||
|
else {
|
||
|
addrValue = (LONG)pavInstOpnd->value;
|
||
|
|
||
|
// for 16-bit or 32-bit index off (E)BP, make
|
||
|
// zero displacement a byte one
|
||
|
|
||
|
if (addrValue == 0
|
||
|
&& (pavInstOpnd->flags != fPTR16
|
||
|
|| pavInstOpnd->base != 6)
|
||
|
&& (pavInstOpnd->flags != fPTR32
|
||
|
|| pavInstOpnd->base != indBP))
|
||
|
modModrm = 0;
|
||
|
else if (addrValue >= -0x80L && addrValue <= 0x7fL) {
|
||
|
modModrm = 1;
|
||
|
addrSize = 1;
|
||
|
}
|
||
|
else if (pavInstOpnd->flags == fPTR32
|
||
|
|| (pavInstOpnd->flags == fPTR && fDBit)) {
|
||
|
modModrm = 2;
|
||
|
addrSize = 4;
|
||
|
}
|
||
|
else if (addrValue >= -0x8000L && addrValue <= 0xffffL) {
|
||
|
modModrm = 2;
|
||
|
addrSize = 2;
|
||
|
}
|
||
|
else
|
||
|
error(OVERFLOW);
|
||
|
if (pavInstOpnd->flags == fPTR) {
|
||
|
modModrm = 0;
|
||
|
addrSize = (UCHAR)((1 + fDBit) << 1);
|
||
|
rmModrm = (UCHAR)(6 - fDBit);
|
||
|
}
|
||
|
else if (pavInstOpnd->flags == fPTR16) {
|
||
|
fAddrOvrd = fDBit;
|
||
|
rmModrm = pavInstOpnd->base;
|
||
|
if (modModrm == 0 && rmModrm == 6)
|
||
|
modModrm = 1;
|
||
|
}
|
||
|
else {
|
||
|
fAddrOvrd = (UCHAR)!fDBit;
|
||
|
if (pavInstOpnd->index == 0xff
|
||
|
&& pavInstOpnd->base != indSP) {
|
||
|
rmModrm = pavInstOpnd->base;
|
||
|
if (modModrm == 0 && rmModrm == 5)
|
||
|
modModrm++;
|
||
|
}
|
||
|
else {
|
||
|
rmModrm = 4;
|
||
|
fSib = TRUE;
|
||
|
if (pavInstOpnd->base != 0xff) {
|
||
|
baseSib = pavInstOpnd->base;
|
||
|
if (modModrm == 0 && baseSib == 5)
|
||
|
modModrm++;
|
||
|
}
|
||
|
else
|
||
|
baseSib = 5;
|
||
|
if (pavInstOpnd->index != 0xff) {
|
||
|
indexSib = pavInstOpnd->index;
|
||
|
scaleSib = pavInstOpnd->scale;
|
||
|
}
|
||
|
else {
|
||
|
indexSib = 4;
|
||
|
scaleSib = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case typGen:
|
||
|
if (fAddToOp)
|
||
|
inOpcode += pavInstOpnd->base;
|
||
|
else
|
||
|
regModrm = pavInstOpnd->base;
|
||
|
break;
|
||
|
|
||
|
case typSgr:
|
||
|
regModrm = (UCHAR)(pavInstOpnd->base - 1);
|
||
|
// remove list offset
|
||
|
break;
|
||
|
|
||
|
case typReg:
|
||
|
rmModrm = pavInstOpnd->base;
|
||
|
break;
|
||
|
|
||
|
case typImm:
|
||
|
if (immedSize == 0) {
|
||
|
immedSize = size;
|
||
|
immedValue = pavInstOpnd->value;
|
||
|
}
|
||
|
else {
|
||
|
immedSize2 = size;
|
||
|
immedValue2 = pavInstOpnd->value;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case typJmp:
|
||
|
|
||
|
// compute displacment for byte offset instruction
|
||
|
// and test if in range
|
||
|
|
||
|
addrValue = pavInstOpnd->value - (addrAssem + 2);
|
||
|
if (addrValue >= -0x80L && addrValue <= 0x7fL)
|
||
|
addrSize = 1;
|
||
|
else {
|
||
|
|
||
|
// too large for byte, compute for word offset
|
||
|
// and test again if in range
|
||
|
// also allow for two-byte opcode 0f xx
|
||
|
|
||
|
addrValue -= 1 + (preOpcode == 0x0f);
|
||
|
if (!fDBit) {
|
||
|
if (addrValue >= -0x8000L && addrValue <= 0x7fffL)
|
||
|
addrSize = 2;
|
||
|
else
|
||
|
error(BADRANGE);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
// recompute again for dword offset instruction
|
||
|
|
||
|
addrValue -= 2;
|
||
|
addrSize = 4;
|
||
|
}
|
||
|
}
|
||
|
fOpndOvrd = FALSE; // operand size override is NOT set
|
||
|
break;
|
||
|
|
||
|
case typCtl:
|
||
|
case typDbg:
|
||
|
case typTrc:
|
||
|
fModrm = TRUE;
|
||
|
modModrm = 3;
|
||
|
regModrm = pavInstOpnd->base;
|
||
|
break;
|
||
|
|
||
|
case typSti:
|
||
|
postOpcode += pavInstOpnd->base;
|
||
|
break;
|
||
|
|
||
|
case typSeg:
|
||
|
break;
|
||
|
|
||
|
case typXsi:
|
||
|
case typYdi:
|
||
|
fAddrOvrd = (UCHAR)
|
||
|
((UCHAR)(pavInstOpnd->flags == fPTR32) != fDBit);
|
||
|
break;
|
||
|
|
||
|
case typOff:
|
||
|
segOvrd = segToOvrdByte[pavInstOpnd->segovr];
|
||
|
goto jumpAssem;
|
||
|
|
||
|
case typAbs:
|
||
|
fSegPtr = TRUE;
|
||
|
segPtr = pavInstOpnd->segment;
|
||
|
jumpAssem:
|
||
|
addrValue = (LONG)pavInstOpnd->value;
|
||
|
if (!fDBit)
|
||
|
if (addrValue >= -0x8000L && addrValue <= 0xffffL)
|
||
|
addrSize = 2;
|
||
|
else
|
||
|
error(OVERFLOW);
|
||
|
else
|
||
|
addrSize = 4;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UCHAR MatchOperand (PASM_VALUE pavOpnd, UCHAR tmplType)
|
||
|
{
|
||
|
UCHAR fMatch;
|
||
|
|
||
|
// if immediate operand, set minimum unsigned size
|
||
|
|
||
|
if (pavOpnd->flags == fIMM) {
|
||
|
if ((LONG)pavOpnd->value >= -0x80L && (LONG)pavOpnd->value <= 0xffL)
|
||
|
pavOpnd->size = sizeB;
|
||
|
else if ((LONG)pavOpnd->value >= -0x8000L
|
||
|
&& (LONG)pavOpnd->value <= 0xffffL)
|
||
|
pavOpnd->size = sizeW;
|
||
|
else
|
||
|
pavOpnd->size = sizeD;
|
||
|
}
|
||
|
|
||
|
// start matching of operands
|
||
|
// compare the template and input operand types
|
||
|
|
||
|
switch (tmplType) {
|
||
|
case typAX:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags & fREG)
|
||
|
&& pavOpnd->index == regG && pavOpnd->base == indAX);
|
||
|
break;
|
||
|
|
||
|
case typCL:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags & fREG)
|
||
|
&& pavOpnd->index == regG && pavOpnd->size == sizeB
|
||
|
&& pavOpnd->base == indCX);
|
||
|
break;
|
||
|
|
||
|
case typDX:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags & fREG)
|
||
|
&& pavOpnd->index == regG && pavOpnd->size == sizeW
|
||
|
&& pavOpnd->base == indDX);
|
||
|
break;
|
||
|
|
||
|
case typAbs:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags & fFPTR);
|
||
|
break;
|
||
|
|
||
|
case typExp:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regG)
|
||
|
|| (pavOpnd->flags == fIMM && pavOpnd->reloc == 1)
|
||
|
|| (pavOpnd->flags & (fPTR | fPTR16 | fPTR32)) != 0);
|
||
|
break;
|
||
|
|
||
|
case typGen:
|
||
|
case typReg:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regG);
|
||
|
break;
|
||
|
|
||
|
case typIm1:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fIMM && pavOpnd->value == 1);
|
||
|
break;
|
||
|
|
||
|
case typIm3:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fIMM && pavOpnd->value == 3);
|
||
|
break;
|
||
|
|
||
|
case typImm:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fIMM && pavOpnd->reloc == 0);
|
||
|
break;
|
||
|
|
||
|
case typJmp:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fIMM);
|
||
|
break;
|
||
|
|
||
|
case typMem:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags == fIMM && pavOpnd->reloc == 1)
|
||
|
|| ((pavOpnd->flags & (fPTR | fPTR16 | fPTR32)) != 0));
|
||
|
break;
|
||
|
|
||
|
case typCtl:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regC);
|
||
|
break;
|
||
|
|
||
|
case typDbg:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regD);
|
||
|
break;
|
||
|
|
||
|
case typTrc:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regT);
|
||
|
break;
|
||
|
|
||
|
case typSt:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regF);
|
||
|
break;
|
||
|
|
||
|
case typSti:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regI);
|
||
|
break;
|
||
|
|
||
|
case typSeg:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG && pavOpnd->index == regS
|
||
|
&& pavOpnd->base == segIndex);
|
||
|
break;
|
||
|
|
||
|
case typSgr:
|
||
|
fMatch = (UCHAR)(pavOpnd->flags == fREG
|
||
|
&& pavOpnd->index == regS);
|
||
|
break;
|
||
|
|
||
|
case typXsi:
|
||
|
fMatch = (UCHAR)(((pavOpnd->flags == fPTR16 && pavOpnd->base == 4)
|
||
|
|| (pavOpnd->flags == fPTR32 && pavOpnd->base == indSI
|
||
|
&& pavOpnd->index == 0xff))
|
||
|
&& pavOpnd->value == 0
|
||
|
&& (pavOpnd->segovr == segX
|
||
|
|| pavOpnd->segovr == segDS));
|
||
|
break;
|
||
|
|
||
|
case typYdi:
|
||
|
fMatch = (UCHAR)(((pavOpnd->flags == fPTR16 && pavOpnd->base == 5)
|
||
|
|| (pavOpnd->flags == fPTR32 && pavOpnd->base == indDI
|
||
|
&& pavOpnd->index == 0xff))
|
||
|
&& pavOpnd->value == 0
|
||
|
&& pavOpnd->segovr == segES);
|
||
|
break;
|
||
|
|
||
|
case typOff:
|
||
|
fMatch = (UCHAR)((pavOpnd->flags == fIMM && pavOpnd->reloc == 1)
|
||
|
|| pavOpnd->flags == fPTR);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
fMatch = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return fMatch;
|
||
|
}
|
||
|
|
||
|
void OutputInstr (void)
|
||
|
{
|
||
|
if (fWaitPrfx)
|
||
|
*pchBin++ = 0x9b;
|
||
|
if (fAddrOvrd)
|
||
|
*pchBin++ = 0x67;
|
||
|
if (fOpndOvrd)
|
||
|
*pchBin++ = 0x66;
|
||
|
if (segOvrd)
|
||
|
*pchBin++ = segOvrd;
|
||
|
if (preOpcode)
|
||
|
*pchBin++ = preOpcode;
|
||
|
*pchBin++ = inOpcode;
|
||
|
if (postOpcode)
|
||
|
*pchBin++ = postOpcode;
|
||
|
if (fModrm)
|
||
|
*pchBin++ = (UCHAR)((((modModrm << 3) + regModrm) << 3) + rmModrm);
|
||
|
if (fSib)
|
||
|
*pchBin++ = (UCHAR)((((scaleSib << 3) + indexSib) << 3) + baseSib);
|
||
|
|
||
|
OutputValue(addrSize, (PUCHAR)&addrValue); // size = 0, 1, 2, 4
|
||
|
OutputValue((UCHAR)(fSegPtr << 1), (PUCHAR)&segPtr); // size = 0, 2
|
||
|
OutputValue(immedSize, (PUCHAR)&immedValue); // size = 0, 1, 2, 4
|
||
|
OutputValue(immedSize2, (PUCHAR)&immedValue2); // size = 0, 1, 2, 4
|
||
|
}
|
||
|
|
||
|
void OutputValue (UCHAR size, PUCHAR pchValue)
|
||
|
{
|
||
|
while (size--)
|
||
|
*pchBin++ = *pchValue++;
|
||
|
}
|