// Free Disassembler and Assembler -- Assembler // // Copyright (C) 2001 Oleh Yuschuk // // 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; either version 2 of the License, or // (at your option) any later version. // // 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, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // 16.01.2002 - corrected error in processing of immediate constants. #define STRICT //#include #include #include #include //#include #include #include //#pragma hdrstop #include "disasm.h" static int ideal=0; // Force IDEAL decoding mode static int sizesens=0; // How to decode size-sensitive mnemonics //////////////////////////////////////////////////////////////////////////////// ///////////////////////////// ASSEMBLER FUNCTIONS ////////////////////////////// // Scanner modes. #define SA_NAME 0x0001 // Don't try to decode labels #define SA_IMPORT 0x0002 // Allow import pseudolabel // Types of input tokens reported by scanner. #define SCAN_EOL 0 // End of line #define SCAN_REG8 1 // 8-bit register #define SCAN_REG16 2 // 16-bit register #define SCAN_REG32 3 // 32-bit register #define SCAN_SEG 4 // Segment register #define SCAN_FPU 5 // FPU register #define SCAN_MMX 6 // MMX register #define SCAN_CR 7 // Control register #define SCAN_DR 8 // Debug register #define SCAN_OPSIZE 9 // Operand size modifier #define SCAN_JMPSIZE 10 // Jump size modifier #define SCAN_LOCAL 11 // Address on stack in form LOCAL.decimal #define SCAN_ARG 12 // Address on stack in form ARG.decimal #define SCAN_PTR 20 // PTR in MASM addressing statements #define SCAN_REP 21 // REP prefix #define SCAN_REPE 22 // REPE prefix #define SCAN_REPNE 23 // REPNE prefix #define SCAN_LOCK 24 // LOCK prefix #define SCAN_NAME 25 // Command or label #define SCAN_ICONST 26 // Hexadecimal constant #define SCAN_DCONST 27 // Decimal constant #define SCAN_OFS 28 // Undefined constant #define SCAN_FCONST 29 // Floating-point constant #define SCAN_EIP 30 // Register EIP #define SCAN_SIGNED 31 // Keyword "SIGNED" (in expressions) #define SCAN_UNSIGNED 32 // Keyword "UNSIGNED" (in expressions) #define SCAN_CHAR 33 // Keyword "CHAR" (in expressions) #define SCAN_FLOAT 34 // Keyword "FLOAT" (in expressions) #define SCAN_DOUBLE 35 // Keyword "DOUBLE" (in expressions) #define SCAN_FLOAT10 36 // Keyword "FLOAT10" (in expressions) #define SCAN_STRING 37 // Keyword "STRING" (in expressions) #define SCAN_UNICODE 38 // Keyword "UNICODE" (in expressions) #define SCAN_MSG 39 // Pseudovariable MSG (in expressions) #define SCAN_SYMB 64 // Any other character #define SCAN_IMPORT 65 // Import pseudolabel #define SCAN_ERR 255 // Definitely bad item // Definition used by Assembler to report command matching errors. #define MA_JMP 0x0001 // Invalid jump size modifier #define MA_NOP 0x0002 // Wrong number of operands #define MA_TYP 0x0004 // Bad operand type #define MA_NOS 0x0008 // Explicit operand size expected #define MA_SIZ 0x0010 // Bad operand size #define MA_DIF 0x0020 // Different operand sizes #define MA_SEG 0x0040 // Invalid segment register #define MA_RNG 0x0080 // Constant out of expected range typedef struct t_asmoperand { int type; // Operand type, see beginning of file int size; // Operand size or 0 if yet unknown int index; // Index or other register int scale; // Scale int base; // Base register if present long offset; // Immediate value or offset int anyoffset; // Offset is present but undefined int segment; // Segment in address if present int jmpmode; // Specified jump size } t_asmoperand; static char *asmcmd; // Pointer to 0-terminated source line static int scan; // Type of last scanned element static int prio; // Priority of operation (0: highest) static char sdata[TEXTLEN]; // Last scanned name (depends on type) static long idata; // Last scanned value static long double fdata; // Floating-point number static char *asmerror; // Explanation of last error, or NULL // Simple and slightly recursive scanner shared by Assemble(). The scanner is // straightforward and ineffective, but high speed is not a must here. As // input, it uses global pointer to source line asmcmd. On exit, it fills in // global variables scan, prio, sdata, idata and/or fdata. If some error is // detected, asmerror points to error message, otherwise asmerror remains // unchanged. static void Scanasm(int mode) { int i,j,base,maxdigit; long decimal,hex; long double floating,divisor; char s[TEXTLEN],*pcmd; sdata[0]='\0'; idata=0; if (asmcmd==NULL) { asmerror="NULL input line"; scan=SCAN_ERR; return; }; while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; // Skip leading spaces if (*asmcmd=='\0' || *asmcmd==';') { scan=SCAN_EOL; return; }; // Empty line if (isalpha((unsigned char)*asmcmd) || *asmcmd=='_' || *asmcmd=='@') { sdata[0]=*asmcmd++; i=1; // Some keyword or identifier while ((isalnum((unsigned char)*asmcmd) || *asmcmd=='_' || *asmcmd=='@') && i=sizeof(sdata)) { asmerror="Too long identifier"; scan=SCAN_ERR; return; }; sdata[i]='\0'; while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; // Skip trailing spaces strcpy(s,sdata); strupr(s); for (j=0; j<=8; j++) { // j==8 means "any register" if (strcmp(s,regname[0][j])!=0) continue; idata=j; scan=SCAN_REG8; // 8-bit register return; }; for (j=0; j<=8; j++) { if (strcmp(s,regname[1][j])!=0) continue; idata=j; scan=SCAN_REG16; // 16-bit register return; }; for (j=0; j<=8; j++) { if (strcmp(s,regname[2][j])!=0) continue; idata=j; scan=SCAN_REG32; // 32-bit register return; }; for (j=0; j<6; j++) { if (strcmp(s,segname[j])!=0) continue; idata=j; scan=SCAN_SEG; // Segment register while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; // Skip trailing spaces return; }; if (strcmp(s,"ST")==0) { pcmd=asmcmd; Scanasm(SA_NAME); // FPU register if (scan!=SCAN_SYMB || idata!='(') { asmcmd=pcmd; // Undo last scan idata=0; scan=SCAN_FPU; return; }; Scanasm(SA_NAME); j=idata; if ((scan!=SCAN_ICONST && scan!=SCAN_DCONST) || idata<0 || idata>7) { asmerror="FPU registers have indexes 0 to 7"; scan=SCAN_ERR; return; }; Scanasm(SA_NAME); if (scan!=SCAN_SYMB || idata!=')') { asmerror="Closing parenthesis expected"; scan=SCAN_ERR; return; }; idata=j; scan=SCAN_FPU; return; }; for (j=0; j<=8; j++) { if (strcmp(s,fpuname[j])!=0) continue; idata=j; scan=SCAN_FPU; // FPU register (alternative coding) return; }; for (j=0; j<=8; j++) { if (strcmp(s,mmxname[j])!=0) continue; idata=j; scan=SCAN_MMX; // MMX register return; }; for (j=0; j<=8; j++) { if (strcmp(s,crname[j])!=0) continue; idata=j; scan=SCAN_CR; // Control register return; }; for (j=0; j<=8; j++) { if (strcmp(s,drname[j])!=0) continue; idata=j; scan=SCAN_DR; // Debug register return; }; for (j=0; j9) { asmerror="Not a decimal number"; scan=SCAN_ERR; return; }; asmcmd++; if (isdigit((unsigned char)*asmcmd) || toupper((unsigned char)*asmcmd)=='E') { divisor=1.0; while (isdigit((unsigned char)*asmcmd)) { // Floating-point number divisor/=10.0; floating+=divisor*(*asmcmd-'0'); asmcmd++; }; if (toupper((unsigned char)*asmcmd)=='E') { asmcmd++; if (*asmcmd=='-') { base=-1; asmcmd++; } else base=1; if (!isdigit((unsigned char)*asmcmd)) { asmerror="Invalid exponent"; scan=SCAN_ERR; return; }; decimal=0; while (isdigit((unsigned char)*asmcmd)) { if (decimal<65536L) decimal=decimal*10+(*asmcmd++)-'0'; }; floating*=pow10l(decimal*base); }; fdata=floating; scan=SCAN_FCONST; return; } else { idata=decimal; scan=SCAN_DCONST; while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; return; }; }; idata=hex; scan=SCAN_ICONST; // Default is hexadecimal while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; return; } else if (*asmcmd=='\'') { // Character constant asmcmd++; if (*asmcmd=='\0' || (*asmcmd=='\\' && asmcmd[1]=='\0')) { asmerror="Unterminated character constant"; scan=SCAN_ERR; return; }; if (*asmcmd=='\'') { asmerror="Empty character constant"; scan=SCAN_ERR; return; }; if (*asmcmd=='\\') asmcmd++; idata=*asmcmd++; if (*asmcmd!='\'') { asmerror="Unterminated character constant"; scan=SCAN_ERR; return; }; asmcmd++; while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++; scan=SCAN_ICONST; return; } else { // Any other character or combination idata=sdata[0]=*asmcmd++; sdata[1]=sdata[2]='\0'; if (idata=='|' && *asmcmd=='|') { //idata='||'; prio=10; // '||' idata=(('|'<<8)|'|'); prio=10; // '||' sdata[1]=*asmcmd++; } else if (idata=='&' && *asmcmd=='&') { //idata='&&'; prio=9; // '&&' idata=(('&'<<8)|'&'); prio=9; // '&&' sdata[1]=*asmcmd++; } else if (idata=='=' && *asmcmd=='=') { //idata='=='; prio=5; // '==' idata=(('='<<8)|'='); prio=5; // '==' sdata[1]=*asmcmd++; } else if (idata=='!' && *asmcmd=='=') { //idata='!='; prio=5; // '!=' idata=(('!'<<8)|'='); prio=5; // '!=' sdata[1]=*asmcmd++; } else if (idata=='<' && *asmcmd=='=') { //idata='<='; prio=4; // '<=' idata=(('<'<<8)|'='); prio=4; // '<=' sdata[1]=*asmcmd++; } else if (idata=='>' && *asmcmd=='=') { //idata='>='; prio=4; // '>=' idata=(('>'<<8)|'='); prio=4; // '>=' sdata[1]=*asmcmd++; } else if (idata=='<' && *asmcmd=='<') { //idata='<<'; prio=3; // '<<' idata=(('<'<<8)|'<'); prio=3; // '<<' sdata[1]=*asmcmd++; } else if (idata=='>' && *asmcmd=='>') { //idata='>>'; prio=3; // '>>' idata=(('>'<<8)|'>'); prio=3; // '>>' sdata[1]=*asmcmd++; } else if (idata=='|') prio=8; // '|' else if (idata=='^') prio=7; // '^' else if (idata=='&') prio=6; // '&' else if (idata=='<') { if (*asmcmd=='&') { // Import pseudolabel (for internal use) if ((mode & SA_IMPORT)==0) { asmerror="Syntax error"; scan=SCAN_ERR; return; }; asmcmd++; i=0; while (*asmcmd!='\0' && *asmcmd!='>') { sdata[i++]=*asmcmd++; if (i>=sizeof(sdata)) { asmerror="Too long import name"; scan=SCAN_ERR; return; }; }; if (*asmcmd!='>') { asmerror="Unterminated import name"; scan=SCAN_ERR; return; }; asmcmd++; sdata[i]='\0'; scan=SCAN_IMPORT; return; } else prio=4; } // '<' else if (idata=='>') prio=4; // '>' else if (idata=='+') prio=2; // '+' else if (idata=='-') prio=2; // '-' else if (idata=='*') prio=1; // '*' else if (idata=='/') prio=1; // '/' else if (idata=='%') prio=1; // '%' else if (idata==']') { pcmd=asmcmd; Scanasm(SA_NAME); if (scan!=SCAN_SYMB || idata!='[') { idata=']'; asmcmd=pcmd; prio=0; } else { idata='+'; prio=2; // Translate '][' to '+' }; } else prio=0; // Any other character scan=SCAN_SYMB; return; }; }; // Fetches one complete operand from the input line and fills in structure op // with operand's data. Expects that first token of the operand is already // scanned. Supports operands in generalized form (for example, R32 means any // of general-purpose 32-bit integer registers). static void Parseasmoperand(t_asmoperand *op) { int i,j,bracket,sign,xlataddr; int reg,r[9]; long offset; if (scan==SCAN_EOL || scan==SCAN_ERR) return; // No or bad operand // Jump or call address may begin with address size modifier(s) SHORT, LONG, // NEAR and/or FAR. Not all combinations are allowed. After operand is // completely parsed, this function roughly checks whether modifier is // allowed. Exact check is done in Assemble(). if (scan==SCAN_JMPSIZE) { j=0; while (scan==SCAN_JMPSIZE) { j|=idata; // Fetch all size modifiers Scanasm(0); }; if ( ((j & 0x03)==0x03) || // Mixed SHORT and LONG ((j & 0x0C)==0x0C) || // Mixed NEAR and FAR ((j & 0x09)==0x09) // Mixed FAR and SHORT ) { asmerror="Invalid combination of jump address modifiers"; scan=SCAN_ERR; return; }; if ((j & 0x08)==0) j|=0x04; // Force NEAR if not FAR op->jmpmode=j; }; // Simple operands are either register or constant, their processing is // obvious and straightforward. if (scan==SCAN_REG8 || scan==SCAN_REG16 || scan==SCAN_REG32) { op->type=REG; op->index=idata; // Integer general-purpose register if (scan==SCAN_REG8) op->size=1; else if (scan==SCAN_REG16) op->size=2; else op->size=4; } else if (scan==SCAN_FPU) { // FPU register op->type=RST; op->index=idata; } else if (scan==SCAN_MMX) { // MMX or 3DNow! register op->type=RMX; op->index=idata; } else if (scan==SCAN_CR) { // Control register op->type=CRX; op->index=idata; } else if (scan==SCAN_DR) { // Debug register op->type=DRX; op->index=idata; } else if (scan==SCAN_SYMB && idata=='-') { Scanasm(0); // Negative constant if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) { asmerror="Integer number expected"; scan=SCAN_ERR; return; }; op->type=IMM; op->offset=-idata; if (scan==SCAN_OFS) op->anyoffset=1; } else if (scan==SCAN_SYMB && idata=='+') { Scanasm(0); // Positive constant if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) { asmerror="Integer number expected"; scan=SCAN_ERR; return; }; op->type=IMM; op->offset=idata; if (scan==SCAN_OFS) op->anyoffset=1; } else if (scan==SCAN_ICONST || scan==SCAN_DCONST || scan==SCAN_OFS) { j=idata; if (scan==SCAN_OFS) op->anyoffset=1; Scanasm(0); if (scan==SCAN_SYMB && idata==':') { Scanasm(0); // Absolute long address (seg:offset) if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) { asmerror="Integer address expected"; scan=SCAN_ERR; return; }; op->type=JMF; op->offset=idata; op->segment=j; if (scan==SCAN_OFS) op->anyoffset=1; } else { op->type=IMM; op->offset=j; // Constant without sign return; // Next token already scanned }; } else if (scan==SCAN_FCONST) { asmerror="Floating-point numbers are not allowed in command"; scan=SCAN_ERR; return; } // Segment register or address. else if (scan==SCAN_SEG || scan==SCAN_OPSIZE || (scan==SCAN_SYMB && idata=='[') ) { // Segment register or address bracket=0; if (scan==SCAN_SEG) { j=idata; Scanasm(0); if (scan!=SCAN_SYMB || idata!=':') { op->type=SGM; op->index=j; // Segment register as operand return; }; // Next token already scanned op->segment=j; Scanasm(0); }; // Scan 32-bit address. This parser does not support 16-bit addresses. // First of all, get size of operand (optional), segment register (optional) // and opening bracket (required). while (1) { if (scan==SCAN_SYMB && idata=='[') { if (bracket) { // Bracket asmerror="Only one opening bracket allowed"; scan=SCAN_ERR; return; }; bracket=1; } else if (scan==SCAN_OPSIZE) { if (op->size!=0) { // Size of operand asmerror="Duplicated size modifier"; scan=SCAN_ERR; return; }; op->size=idata; } else if (scan==SCAN_SEG) { if (op->segment!=SEG_UNDEF) { // Segment register asmerror="Duplicated segment register"; scan=SCAN_ERR; return; }; op->segment=idata; Scanasm(0); if (scan!=SCAN_SYMB || idata!=':') { asmerror="Semicolon expected"; scan=SCAN_ERR; return; }; } else if (scan==SCAN_ERR) return; else break; // None of expected address elements Scanasm(0); }; if (bracket==0) { asmerror="Address expression requires brackets"; scan=SCAN_ERR; return; }; // Assembling a 32-bit address may be a kind of nigthmare, due to a large // number of allowed forms. Parser collects immediate offset in op->offset // and count for each register in array r[]. Then it decides whether this // combination is valid and determines scale, index and base. Assemble() // will use these numbers to select address form (with or without SIB byte, // 8- or 32-bit offset, use segment prefix or not). As a useful side effect // of this technique, one may specify, for example, [EAX*5] which will // correctly assemble to [EAX*4+EAX]. for (i=0; i<=8; i++) r[i]=0; sign='+'; // Default sign for the first operand xlataddr=0; while (1) { // Get SIB and offset if (scan==SCAN_SYMB && (idata=='+' || idata=='-')) { sign=idata; Scanasm(0); }; if (scan==SCAN_ERR) return; if (sign=='?') { asmerror="Syntax error"; scan=SCAN_ERR; return; }; // Register AL appears as part of operand of (seldom used) command XLAT. if (scan==SCAN_REG8 && idata==GREG_EAX) { if (sign=='-') { asmerror="Unable to subtract register"; scan=SCAN_ERR; return; }; if (xlataddr!=0) { asmerror="Too many registers"; scan=SCAN_ERR; return; }; xlataddr=1; Scanasm(0); } else if (scan==SCAN_REG16) { asmerror="Sorry, 16-bit addressing is not supported"; scan=SCAN_ERR; return; } else if (scan==SCAN_REG32) { if (sign=='-') { asmerror="Unable to subtract register"; scan=SCAN_ERR; return; }; reg=idata; Scanasm(0); if (scan==SCAN_SYMB && idata=='*') { Scanasm(0); // Try index*scale if (scan==SCAN_ERR) return; if (scan==SCAN_OFS) { asmerror="Undefined scale is not allowed"; scan=SCAN_ERR; return; }; if (scan!=SCAN_ICONST && scan!=SCAN_DCONST) { asmerror="Syntax error"; scan=SCAN_ERR; return; }; if (idata==6 || idata==7 || idata>9) { asmerror="Invalid scale"; scan=SCAN_ERR; return; }; r[reg]+=idata; Scanasm(0); } else r[reg]++; } // Simple register else if (scan==SCAN_LOCAL) { r[GREG_EBP]++; op->offset-=idata*4; Scanasm(0); } else if (scan==SCAN_ARG) { r[GREG_EBP]++; op->offset+=(idata+1)*4; Scanasm(0); } else if (scan==SCAN_ICONST || scan==SCAN_DCONST) { offset=idata; Scanasm(0); if (scan==SCAN_SYMB && idata=='*') { Scanasm(0); // Try scale*index if (scan==SCAN_ERR) return; if (sign=='-') { asmerror="Unable to subtract register"; scan=SCAN_ERR; return; }; if (scan==SCAN_REG16) { asmerror="Sorry, 16-bit addressing is not supported"; scan=SCAN_ERR; return; }; if (scan!=SCAN_REG32) { asmerror="Syntax error"; scan=SCAN_ERR; return; }; if (offset==6 || offset==7 || offset>9) { asmerror="Invalid scale"; scan=SCAN_ERR; return; }; r[idata]+=offset; Scanasm(0); } else { if (sign=='-') op->offset-=offset; else op->offset+=offset; }; } else if (scan==SCAN_OFS) { Scanasm(0); if (scan==SCAN_SYMB && idata=='*') { asmerror="Undefined scale is not allowed"; scan=SCAN_ERR; return; } else { op->anyoffset=1; }; } else break; // None of expected address elements if (scan==SCAN_SYMB && idata==']') break; sign='?'; }; if (scan==SCAN_ERR) return; if (scan!=SCAN_SYMB || idata!=']') { asmerror="Syntax error"; scan=SCAN_ERR; return; }; // Process XLAT address separately. if (xlataddr!=0) { // XLAT address in form [EBX+AX] for (i=0; i<=8; i++) { // Check which registers used if (i==GREG_EBX) continue; if (r[i]!=0) break; }; if (i<=8 || r[GREG_EBX]!=1 || op->offset!=0 || op->anyoffset!=0) { asmerror="Invalid address"; scan=SCAN_ERR; return; }; op->type=MXL; } // Determine scale, index and base. else { j=0; // Number of used registers for (i=0; i<=8; i++) { if (r[i]==0) continue; // Unused register if (r[i]==3 || r[i]==5 || r[i]==9) { if (op->index>=0 || op->base>=0) { if (j==0) asmerror="Invalid scale"; else asmerror="Too many registers"; scan=SCAN_ERR; return; }; op->index=op->base=i; op->scale=r[i]-1; } else if (r[i]==2 || r[i]==4 || r[i]==8) { if (op->index>=0) { if (j<=1) asmerror="Only one register may be scaled"; else asmerror="Too many registers"; scan=SCAN_ERR; return; }; op->index=i; op->scale=r[i]; } else if (r[i]==1) { if (op->base<0) op->base=i; else if (op->index<0) { op->index=i; op->scale=1; } else { asmerror="Too many registers"; scan=SCAN_ERR; return; }; } else { asmerror="Invalid scale"; scan=SCAN_ERR; return; }; j++; }; op->type=MRG; }; } else { asmerror="Unrecognized operand"; scan=SCAN_ERR; return; }; // In general, address modifier is allowed only with address expression which // is a constant, a far address or a memory expression. More precise check // will be done later in Assemble(). if (op->jmpmode!=0 && op->type!=IMM && op->type!=JMF && op->type!=MRG) { asmerror="Jump address modifier is not allowed"; scan=SCAN_ERR; return; }; Scanasm(0); // Fetch next token from input line }; // Function assembles text into 32-bit 80x86 machine code. It supports imprecise // operands (for example, R32 stays for any general-purpose 32-bit register). // This allows to search for incomplete commands. Command is precise when all // significant bytes in model.mask are 0xFF. Some commands have more than one // decoding. By calling Assemble() with attempt=0,1... and constsize=0,1,2,3 one // gets also alternative variants (bit 0x1 of constsize is responsible for size // of address constant and bit 0x2 - for immediate data). However, only one // address form is generated ([EAX*2], but not [EAX+EAX]; [EBX+EAX] but not // [EAX+EBX]; [EAX] will not use SIB byte; no DS: prefix and so on). Returns // number of bytes in assembled code or non-positive number in case of detected // error. This number is the negation of the offset in the input text where the // error encountered. Unfortunately, BC 4.52 is unable to compile the switch // (arg) in this code when any common subexpression optimization is on. The // next #pragma statement disables all optimizations. //#pragma option -Od // No optimizations, or BC 4.52 crashes int Assemble(char *cmd,ulong ip,t_asmmodel *model,int attempt, int constsize,char *errtext) { int i,j,k,namelen,nameok,arg,match = 0,datasize,addrsize,bytesize,minop,maxop; int rep,lock,segment,jmpsize,jmpmode,longjump; int hasrm,hassib,dispsize,immsize; int anydisp,anyimm,anyjmp; long l,displacement,immediate,jmpoffset = 0; char name[32],*nameend; char tcode[MAXCMDSIZE],tmask[MAXCMDSIZE]; t_asmoperand aop[3],*op; // Up to 3 operands allowed const t_cmddata *pd; if (model!=NULL) model->length=0; if (cmd==NULL || model==NULL || errtext==NULL) { if (errtext!=NULL) strcpy(errtext,"Internal OLLYDBG error"); return 0; }; // Error in parameters asmcmd=cmd; rep=lock=0; errtext[0]='\0'; Scanasm(SA_NAME); if (scan==SCAN_EOL) // End of line, nothing to assemble return 0; while (1) { // Fetch all REPxx and LOCK prefixes if (scan==SCAN_REP || scan==SCAN_REPE || scan==SCAN_REPNE) { if (rep!=0) { strcpy(errtext,"Duplicated REP prefix"); goto error; }; rep=scan; } else if (scan==SCAN_LOCK) { if (lock!=0) { strcpy(errtext,"Duplicated LOCK prefix"); goto error; }; lock=scan; } else break; // No more prefixes Scanasm(SA_NAME); }; if (scan!=SCAN_NAME || idata>16) { strcpy(errtext,"Command mnemonic expected"); goto error; }; nameend=asmcmd; strupr(sdata); // Prepare full mnemonic (including repeat prefix, if any). if (rep==SCAN_REP) snprintf(name,sizeof(name)-1,"REP %s",sdata); else if (rep==SCAN_REPE) snprintf(name,sizeof(name)-1,"REPE %s",sdata); else if (rep==SCAN_REPNE) snprintf(name,sizeof(name)-1,"REPNE %s",sdata); else strncpy(name,sdata, sizeof(name)-1); Scanasm(0); // Parse command operands (up to 3). Note: jump address is always the first // (and only) operand in actual command set. for (i=0; i<3; i++) { aop[i].type=NNN; // No operand aop[i].size=0; // Undefined size aop[i].index=-1; // No index aop[i].scale=0; // No scale aop[i].base=-1; // No base aop[i].offset=0; // No offset aop[i].anyoffset=0; // No offset aop[i].segment=SEG_UNDEF; // No segment aop[i].jmpmode=0; }; // No jump size modifier Parseasmoperand(aop+0); jmpmode=aop[0].jmpmode; if (jmpmode!=0) jmpmode|=0x80; if (scan==SCAN_SYMB && idata==',') { Scanasm(0); Parseasmoperand(aop+1); if (scan==SCAN_SYMB && idata==',') { Scanasm(0); Parseasmoperand(aop+2); }; }; if (scan==SCAN_ERR) { strcpy(errtext,asmerror); goto error; }; if (scan!=SCAN_EOL) { strcpy(errtext,"Extra input after operand"); goto error; }; // If jump size is not specified, function tries to use short jump. If // attempt fails, it retries with long form. longjump=0; // Try short jump on the first pass retrylongjump: nameok=0; // Some commands allow different number of operands. Variables minop and // maxop accumulate their minimal and maximal counts. The numbers are not // used in assembly process but allow for better error diagnostics. minop=3; maxop=0; // Main assembly loop: try to find the command which matches all operands, // but do not process operands yet. namelen=strlen(name); for (pd=cmddata; pd->mask!=0; pd++) { if (pd->name[0]=='&') { // Mnemonic depends on operand size j=1; datasize=2; addrsize=4; while (1) { // Try all mnemonics (separated by ':') for (i=0; pd->name[j]!='\0' && pd->name[j]!=':'; j++) { if (pd->name[j]=='*') { if (name[i]=='W') { datasize=2; i++; } else if (name[i]=='D') { datasize=4; i++; } else if (sizesens==0) datasize=2; else datasize=4; } else if (pd->name[j]==name[i]) i++; else break; }; if (name[i]=='\0' && (pd->name[j]=='\0' || pd->name[j]==':')) break; // Bingo! while (pd->name[j]!='\0' && pd->name[j]!=':') j++; if (pd->name[j]==':') { j++; datasize=4; } // Retry with 32-bit mnenonic else { i=0; break; // Comparison failed }; }; if (i==0) continue; } else if (pd->name[0]=='$') { // Mnemonic depends on address size j=1; datasize=0; addrsize=2; while (1) { // Try all mnemonics (separated by ':') for (i=0; pd->name[j]!='\0' && pd->name[j]!=':'; j++) { if (pd->name[j]=='*') { if (name[i]=='W') { addrsize=2; i++; } else if (name[i]=='D') { addrsize=4; i++; } else if (sizesens==0) addrsize=2; else addrsize=4; } else if (pd->name[j]==name[i]) i++; else break; }; if (name[i]=='\0' && (pd->name[j]=='\0' || pd->name[j]==':')) break; // Bingo! while (pd->name[j]!='\0' && pd->name[j]!=':') j++; if (pd->name[j]==':') { j++; addrsize=4; } // Retry with 32-bit mnenonic else { i=0; break; // Comparison failed }; }; if (i==0) continue; } else { // Compare with all synonimes j=k=0; datasize=0; // Default settings addrsize=4; while (1) { while (pd->name[j]!=',' && pd->name[j]!='\0') j++; if (j-k==namelen && strnicmp(name,pd->name+k,namelen)==0) break; k=j+1; if (pd->name[j]=='\0') break; j=k; }; if (k>j) continue; }; // For error diagnostics it is important to know whether mnemonic exists. nameok++; if (pd->arg1==NNN || pd->arg1>=PSEUDOOP) minop=0; else if (pd->arg2==NNN || pd->arg2>=PSEUDOOP) { if (minop>1) minop=1; if (maxop<1) maxop=1; } else if (pd->arg3==NNN || pd->arg3>=PSEUDOOP) { if (minop>2) minop=2; if (maxop<2) maxop=2; } else maxop=3; // Determine default and allowed operand size(s). if (pd->bits==FF) datasize=2; // Forced 16-bit size if (pd->bits==WW || pd->bits==WS || pd->bits==W3 || pd->bits==WP) bytesize=1; // 1-byte size allowed else bytesize=0; // Word/dword size only // Check whether command operands match specified. If so, variable match // remains zero, otherwise it contains kind of mismatch. This allows for // better error diagnostics. match=0; for (j=0; j<3; j++) { // Up to 3 operands op=aop+j; if (j==0) arg=pd->arg1; else if (j==1) arg=pd->arg2; else arg=pd->arg3; if (arg==NNN || arg>=PSEUDOOP) { if (op->type!=NNN) // No more arguments match|=MA_NOP; break; }; if (op->type==NNN) { match|=MA_NOP; break; }; // No corresponding operand switch (arg) { case REG: // Integer register in Reg field case RCM: // Integer register in command byte case RAC: // Accumulator (AL/AX/EAX, implicit) if (op->type!=REG) match|=MA_TYP; if (arg==RAC && op->index!=GREG_EAX && op->index!=8) match|=MA_TYP; if (bytesize==0 && op->size==1) match|=MA_SIZ; if (datasize==0) datasize=op->size; if (datasize!=op->size) match|=MA_DIF; break; case RG4: // Integer 4-byte register in Reg field if (op->type!=REG) match|=MA_TYP; if (op->size!=4) match|=MA_SIZ; if (datasize==0) datasize=op->size; if (datasize!=op->size) match|=MA_DIF; break; case RAX: // AX (2-byte, implicit) if (op->type!=REG || (op->index!=GREG_EAX && op->index!=8)) match|=MA_TYP; if (op->size!=2) match|=MA_SIZ; if (datasize==0) datasize=op->size; if (datasize!=op->size) match|=MA_DIF; break; case RDX: // DX (16-bit implicit port address) if (op->type!=REG || (op->index!=GREG_EDX && op->index!=8)) match|=MA_TYP; if (op->size!=2) match|=MA_SIZ; break; case RCL: // Implicit CL register (for shifts) if (op->type!=REG || (op->index!=GREG_ECX && op->index!=8)) match|=MA_TYP; if (op->size!=1) match|=MA_SIZ; break; case RS0: // Top of FPU stack (ST(0)) if (op->type!=RST || (op->index!=0 && op->index!=8)) match|=MA_TYP; break; case RST: // FPU register (ST(i)) in command byte if (op->type!=RST) match|=MA_TYP; break; case RMX: // MMX register MMx case R3D: // 3DNow! register MMx if (op->type!=RMX) match|=MA_TYP; break; case MRG: // Memory/register in ModRM byte if (op->type!=MRG && op->type!=REG) match|=MA_TYP; if (bytesize==0 && op->size==1) match|=MA_SIZ; if (datasize==0) datasize=op->size; if (op->size!=0 && op->size!=datasize) match|=MA_DIF; break; case MR1: // 1-byte memory/register in ModRM byte if (op->type!=MRG && op->type!=REG) match|=MA_TYP; if (op->size!=0 && op->size!=1) match|=MA_SIZ; break; case MR2: // 2-byte memory/register in ModRM byte if (op->type!=MRG && op->type!=REG) match|=MA_TYP; if (op->size!=0 && op->size!=2) match|=MA_SIZ; break; case MR4: // 4-byte memory/register in ModRM byte if (op->type!=MRG && op->type!=REG) match|=MA_TYP; if (op->size!=0 && op->size!=4) match|=MA_SIZ; break; case RR4: // 4-byte memory/register (register only) if (op->type!=REG) match|=MA_TYP; if (op->size!=0 && op->size!=4) match|=MA_SIZ; break; case MRJ: // Memory/reg in ModRM as JUMP target if (op->type!=MRG && op->type!=REG) match|=MA_TYP; if (op->size!=0 && op->size!=4) match|=MA_SIZ; if ((jmpmode & 0x09)!=0) match|=MA_JMP; jmpmode&=0x7F; break; case MR8: // 8-byte memory/MMX register in ModRM case MRD: // 8-byte memory/3DNow! register in ModRM if (op->type!=MRG && op->type!=RMX) match|=MA_TYP; if (op->size!=0 && op->size!=8) match|=MA_SIZ; break; case RR8: // 8-byte MMX register only in ModRM case RRD: // 8-byte memory/3DNow! (register only) if (op->type!=RMX) match|=MA_TYP; if (op->size!=0 && op->size!=8) match|=MA_SIZ; break; case MMA: // Memory address in ModRM byte for LEA if (op->type!=MRG) match|=MA_TYP; break; case MML: // Memory in ModRM byte (for LES) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=6) match|=MA_SIZ; if (datasize==0) datasize=4; else if (datasize!=4) match|=MA_DIF; break; case MMS: // Memory in ModRM byte (as SEG:OFFS) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=6) match|=MA_SIZ; if ((jmpmode & 0x07)!=0) match|=MA_JMP; jmpmode&=0x7F; break; case MM6: // Memory in ModRm (6-byte descriptor) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=6) match|=MA_SIZ; break; case MMB: // Two adjacent memory locations (BOUND) if (op->type!=MRG) match|=MA_TYP; k=op->size; if (ideal==0 && k>1) k/=2; if (k!=0 && k!=datasize) match|=MA_DIF; break; case MD2: // Memory in ModRM byte (16-bit integer) case MB2: // Memory in ModRM byte (16-bit binary) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=2) match|=MA_SIZ; break; case MD4: // Memory in ModRM byte (32-bit integer) case MF4: // Memory in ModRM byte (32-bit float) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=4) match|=MA_SIZ; break; case MD8: // Memory in ModRM byte (64-bit integer) case MF8: // Memory in ModRM byte (64-bit float) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=8) match|=MA_SIZ; break; case MDA: // Memory in ModRM byte (80-bit BCD) case MFA: // Memory in ModRM byte (80-bit float) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0 && op->size!=10) match|=MA_SIZ; break; case MFE: // Memory in ModRM byte (FPU environment) case MFS: // Memory in ModRM byte (FPU state) case MFX: // Memory in ModRM byte (ext. FPU state) if (op->type!=MRG) match|=MA_TYP; if (op->size!=0) match|=MA_SIZ; break; case MSO: // Source in string operands ([ESI]) if (op->type!=MRG || op->base!=GREG_ESI || op->index!=-1 || op->offset!=0 || op->anyoffset!=0) match|=MA_TYP; if (datasize==0) datasize=op->size; if (op->size!=0 && op->size!=datasize) match|=MA_DIF; break; case MDE: // Destination in string operands ([EDI]) if (op->type!=MRG || op->base!=GREG_EDI || op->index!=-1 || op->offset!=0 || op->anyoffset!=0) match|=MA_TYP; if (op->segment!=SEG_UNDEF && op->segment!=SEG_ES) match|=MA_SEG; if (datasize==0) datasize=op->size; if (op->size!=0 && op->size!=datasize) match|=MA_DIF; break; case MXL: // XLAT operand ([EBX+AL]) if (op->type!=MXL) match|=MA_TYP; break; case IMM: // Immediate data (8 or 16/32) case IMU: // Immediate unsigned data (8 or 16/32) if (op->type!=IMM) match|=MA_TYP; break; case VXD: // VxD service (32-bit only) if (op->type!=IMM) match|=MA_TYP; if (datasize==0) datasize=4; if (datasize!=4) match|=MA_SIZ; break; case JMF: // Immediate absolute far jump/call addr if (op->type!=JMF) match|=MA_TYP; if ((jmpmode & 0x05)!=0) match|=MA_JMP; jmpmode&=0x7F; break; case JOB: // Immediate byte offset (for jumps) if (op->type!=IMM || longjump) match|=MA_TYP; if ((jmpmode & 0x0A)!=0) match|=MA_JMP; jmpmode&=0x7F; break; case JOW: // Immediate full offset (for jumps) if (op->type!=IMM) match|=MA_TYP; if ((jmpmode & 0x09)!=0) match|=MA_JMP; jmpmode&=0x7F; break; case IMA: // Immediate absolute near data address if (op->type!=MRG || op->base>=0 || op->index>=0) match|=MA_TYP; break; case IMX: // Immediate sign-extendable byte if (op->type!=IMM) match|=MA_TYP; if (op->offset<-128 || op->offset>127) match|=MA_RNG; break; case C01: // Implicit constant 1 (for shifts) if (op->type!=IMM || (op->offset!=1 && op->anyoffset==0)) match|=MA_TYP; break; case IMS: // Immediate byte (for shifts) case IM1: // Immediate byte if (op->type!=IMM) match|=MA_TYP; if (op->offset<-128 || op->offset>255) match|=MA_RNG; break; case IM2: // Immediate word (ENTER/RET) if (op->type!=IMM) match|=MA_TYP; if (op->offset<0 || op->offset>65535) match|=MA_RNG; break; case SGM: // Segment register in ModRM byte if (op->type!=SGM) match|=MA_TYP; if (datasize==0) datasize=2; if (datasize!=2) match|=MA_DIF; break; case SCM: // Segment register in command byte if (op->type!=SGM) match|=MA_TYP; break; case CRX: // Control register CRx case DRX: // Debug register DRx if (op->type!=arg) match|=MA_TYP; if (datasize==0) datasize=4; if (datasize!=4) match|=MA_DIF; break; case PRN: // Near return address (pseudooperand) case PRF: // Far return address (pseudooperand) case PAC: // Accumulator (AL/AX/EAX, pseudooperand) case PAH: // AH (in LAHF/SAHF, pseudooperand) case PFL: // Lower byte of flags (pseudooperand) case PS0: // Top of FPU stack (pseudooperand) case PS1: // ST(1) (pseudooperand) case PCX: // CX/ECX (pseudooperand) case PDI: // EDI (pseudooperand in MMX extensions) break; default: // Undefined type of operand strcpy(errtext,"Internal Assembler error"); goto error; }; // End of switch (arg) if ((jmpmode & 0x80)!=0) match|=MA_JMP; if (match!=0) break; // Some of the operands doesn't match }; // End of operand matching loop if (match==0) { // Exact match found if (attempt>0) { --attempt; nameok=0; } // Well, try to find yet another match else break; }; }; // End of command search loop // Check whether some error was detected. If several errors were found // similtaneously, report one (roughly in order of significance). if (nameok==0) { // Mnemonic unavailable strcpy(errtext,"Unrecognized command"); asmcmd=nameend; goto error; }; if (match!=0) { // Command not found if (minop>0 && aop[minop-1].type==NNN) strcpy(errtext,"Too few operands"); else if (maxop<3 && aop[maxop].type!=NNN) strcpy(errtext,"Too many operands"); else if (nameok>1) // More that 1 command strcpy(errtext,"Command does not support given operands"); else if (match & MA_JMP) strcpy(errtext,"Invalid jump size modifier"); else if (match & MA_NOP) strcpy(errtext,"Wrong number of operands"); else if (match & MA_TYP) strcpy(errtext,"Command does not support given operands"); else if (match & MA_NOS) strcpy(errtext,"Please specify operand size"); else if (match & MA_SIZ) strcpy(errtext,"Bad operand size"); else if (match & MA_DIF) strcpy(errtext,"Different size of operands"); else if (match & MA_SEG) strcpy(errtext,"Invalid segment register"); else if (match & MA_RNG) strcpy(errtext,"Constant out of expected range"); else strcpy(errtext,"Erroneous command"); goto error; }; // Exact match found. Now construct the code. hasrm=0; // Whether command has ModR/M byte hassib=0; // Whether command has SIB byte dispsize=0; // Size of displacement (if any) immsize=0; // Size of immediate data (if any) segment=SEG_UNDEF; // Necessary segment prefix jmpsize=0; // No relative jumps memset(tcode,0,sizeof(tcode)); // Fix weak aliasing { unsigned int *word = (unsigned int*)&tcode; unsigned int *mask = (unsigned int*)&tmask; *(ulong *)word = pd->code & pd->mask; memset(tmask, 0, sizeof(tmask)); *(ulong *)mask = pd->mask; } i=pd->len-1; // Last byte of command itself if (rep) i++; // REPxx prefixes count as extra byte // In some cases at least one operand must have explicit size declaration (as // in MOV [EAX],1). This preliminary check does not include all cases. if (pd->bits==WW || pd->bits==WS || pd->bits==WP) { if (datasize==0) { strcpy(errtext,"Please specify operand size"); goto error; } else if (datasize>1) tcode[i]|=0x01; // WORD or DWORD size of operands tmask[i]|=0x01; } else if (pd->bits==W3) { if (datasize==0) { strcpy(errtext,"Please specify operand size"); goto error; } else if (datasize>1) tcode[i]|=0x08; // WORD or DWORD size of operands tmask[i]|=0x08; }; // Present suffix of 3DNow! command as immediate byte operand. if ((pd->type & C_TYPEMASK)==C_NOW) { immsize=1; immediate=(pd->code>>16) & 0xFF; }; // Process operands again, this time constructing the code. anydisp=anyimm=anyjmp=0; for (j=0; j<3; j++) { // Up to 3 operands op=aop+j; if (j==0) arg=pd->arg1; else if (j==1) arg=pd->arg2; else arg=pd->arg3; if (arg==NNN) break; // All operands processed switch (arg) { case REG: // Integer register in Reg field case RG4: // Integer 4-byte register in Reg field case RMX: // MMX register MMx case R3D: // 3DNow! register MMx case CRX: // Control register CRx case DRX: // Debug register DRx hasrm=1; if (op->index<8) { tcode[i+1]|=(char)(op->index<<3); tmask[i+1]|=0x38; }; break; case RCM: // Integer register in command byte case RST: // FPU register (ST(i)) in command byte if (op->index<8) { tcode[i]|=(char)op->index; tmask[i]|=0x07; }; break; case RAC: // Accumulator (AL/AX/EAX, implicit) case RAX: // AX (2-byte, implicit) case RDX: // DX (16-bit implicit port address) case RCL: // Implicit CL register (for shifts) case RS0: // Top of FPU stack (ST(0)) case MDE: // Destination in string op's ([EDI]) case C01: // Implicit constant 1 (for shifts) break; // Simply skip implicit operands case MSO: // Source in string op's ([ESI]) case MXL: // XLAT operand ([EBX+AL]) if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS) segment=op->segment; break; case MRG: // Memory/register in ModRM byte case MRJ: // Memory/reg in ModRM as JUMP target case MR1: // 1-byte memory/register in ModRM byte case MR2: // 2-byte memory/register in ModRM byte case MR4: // 4-byte memory/register in ModRM byte case RR4: // 4-byte memory/register (register only) case MR8: // 8-byte memory/MMX register in ModRM case RR8: // 8-byte MMX register only in ModRM case MRD: // 8-byte memory/3DNow! register in ModRM case RRD: // 8-byte memory/3DNow! (register only) hasrm=1; if (op->type!=MRG) { // Register in ModRM byte tcode[i+1]|=0xC0; tmask[i+1]|=0xC0; if (op->index<8) { tcode[i+1]|=(char)op->index; tmask[i+1]|=0x07; }; break; }; // Note: NO BREAK, continue with address case MMA: // Memory address in ModRM byte for LEA case MML: // Memory in ModRM byte (for LES) case MMS: // Memory in ModRM byte (as SEG:OFFS) case MM6: // Memory in ModRm (6-byte descriptor) case MMB: // Two adjacent memory locations (BOUND) case MD2: // Memory in ModRM byte (16-bit integer) case MB2: // Memory in ModRM byte (16-bit binary) case MD4: // Memory in ModRM byte (32-bit integer) case MD8: // Memory in ModRM byte (64-bit integer) case MDA: // Memory in ModRM byte (80-bit BCD) case MF4: // Memory in ModRM byte (32-bit float) case MF8: // Memory in ModRM byte (64-bit float) case MFA: // Memory in ModRM byte (80-bit float) case MFE: // Memory in ModRM byte (FPU environment) case MFS: // Memory in ModRM byte (FPU state) case MFX: // Memory in ModRM byte (ext. FPU state) hasrm=1; displacement=op->offset; anydisp=op->anyoffset; if (op->base<0 && op->index<0) { dispsize=4; // Special case of immediate address if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS) segment=op->segment; tcode[i+1]|=0x05; tmask[i+1]|=0xC7; } else if (op->index<0 && op->base!=GREG_ESP) { tmask[i+1]|=0xC0; // SIB byte unnecessary if (op->offset==0 && op->anyoffset==0 && op->base!=GREG_EBP) ; // [EBP] always requires offset else if ((constsize & 1)!=0 && ((op->offset>=-128 && op->offset<128) || op->anyoffset!=0) ) { tcode[i+1]|=0x40; // Disp8 dispsize=1; } else { tcode[i+1]|=0x80; // Disp32 dispsize=4; }; if (op->base<8) { if (op->segment!=SEG_UNDEF && op->segment!=addr32[op->base].defseg) segment=op->segment; tcode[i+1]|= (char)op->base; // Note that case [ESP] has base<0. tmask[i+1]|=0x07; } else segment=op->segment; } else { // SIB byte necessary hassib=1; if (op->base==GREG_EBP && // EBP as base requires offset, optimize op->index>=0 && op->scale==1 && op->offset==0 && op->anyoffset==0) { op->base=op->index; op->index=GREG_EBP; }; if (op->index==GREG_ESP && // ESP cannot be an index, reorder op->scale<=1) { op->index=op->base; op->base=GREG_ESP; op->scale=1; }; if (op->base<0 && // No base means 4-byte offset, optimize op->index>=0 && op->scale==2 && op->offset>=-128 && op->offset<128 && op->anyoffset==0) { op->base=op->index; op->scale=1; }; if (op->index==GREG_ESP) { // Reordering was unsuccessful strcpy(errtext,"Invalid indexing mode"); goto error; }; if (op->base<0) { tcode[i+1]|=0x04; dispsize=4; } else if (op->offset==0 && op->anyoffset==0 && op->base!=GREG_EBP) tcode[i+1]|=0x04; // No displacement else if ((constsize & 1)!=0 && ((op->offset>=-128 && op->offset<128) || op->anyoffset!=0) ) { tcode[i+1]|=0x44; // Disp8 dispsize=1; } else { tcode[i+1]|=0x84; // Disp32 dispsize=4; }; tmask[i+1]|=0xC7; // ModRM completed, proceed with SIB if (op->scale==2) tcode[i+2]|=0x40; else if (op->scale==4) tcode[i+2]|=0x80; else if (op->scale==8) tcode[i+2]|=0xC0; tmask[i+2]|=0xC0; if (op->index<8) { if (op->index<0) op->index=0x04; tcode[i+2]|=(char)(op->index<<3); tmask[i+2]|=0x38; }; if (op->base<8) { if (op->base<0) op->base=0x05; if (op->segment!=SEG_UNDEF && op->segment!=addr32[op->base].defseg) segment=op->segment; tcode[i+2]|=(char)op->base; tmask[i+2]|=0x07; } else segment=op->segment; }; break; case IMM: // Immediate data (8 or 16/32) case IMU: // Immediate unsigned data (8 or 16/32) case VXD: // VxD service (32-bit only) if (datasize==0 && pd->arg2==NNN && (pd->bits==SS || pd->bits==WS)) datasize=4; if (datasize==0) { strcpy(errtext,"Please specify operand size"); goto error; }; immediate=op->offset; anyimm=op->anyoffset; if (pd->bits==SS || pd->bits==WS) { if (datasize>1 && (constsize & 2)!=0 && ((immediate>=-128 && immediate<128) || op->anyoffset!=0)) { immsize=1; tcode[i]|=0x02; } else immsize=datasize; tmask[i]|=0x02; } else immsize=datasize; break; case IMX: // Immediate sign-extendable byte case IMS: // Immediate byte (for shifts) case IM1: // Immediate byte if (immsize==2) // To accommodate ENTER instruction immediate=(immediate & 0xFFFF) | (op->offset<<16); else immediate=op->offset; anyimm|=op->anyoffset; immsize++; break; case IM2: // Immediate word (ENTER/RET) immediate=op->offset; anyimm=op->anyoffset; immsize=2; break; case IMA: // Immediate absolute near data address if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS) segment=op->segment; displacement=op->offset; anydisp=op->anyoffset; dispsize=4; break; case JOB: // Immediate byte offset (for jumps) jmpoffset=op->offset; anyjmp=op->anyoffset; jmpsize=1; break; case JOW: // Immediate full offset (for jumps) jmpoffset=op->offset; anyjmp=op->anyoffset; jmpsize=4; break; case JMF: // Immediate absolute far jump/call addr displacement=op->offset; anydisp=op->anyoffset; dispsize=4; immediate=op->segment; anyimm=op->anyoffset; immsize=2; break; case SGM: // Segment register in ModRM byte hasrm=1; if (op->index<6) { tcode[i+1]|=(char)(op->index<<3); tmask[i+1]|=0x38; }; break; case SCM: // Segment register in command byte if (op->index==SEG_FS || op->index==SEG_GS) { tcode[0]=0x0F; tmask[0]=0xFF; i=1; if (strcmp(name,"PUSH")==0) tcode[i]=(char)((op->index<<3) | 0x80); else tcode[i]=(char)((op->index<<3) | 0x81); tmask[i]=0xFF; } else if (op->index<6) { if (op->index==SEG_CS && strcmp(name,"POP")==0) { strcpy(errtext,"Unable to POP CS"); goto error; }; tcode[i]=(char)((tcode[i] & 0xC7) | (op->index<<3)); } else { tcode[i]&=0xC7; tmask[i]&=0xC7; }; break; case PRN: // Near return address (pseudooperand) case PRF: // Far return address (pseudooperand) case PAC: // Accumulator (AL/AX/EAX, pseudooperand) case PAH: // AH (in LAHF/SAHF, pseudooperand) case PFL: // Lower byte of flags (pseudooperand) case PS0: // Top of FPU stack (pseudooperand) case PS1: // ST(1) (pseudooperand) case PCX: // CX/ECX (pseudooperand) case PDI: // EDI (pseudooperand in MMX extensions) break; // Simply skip preudooperands default: // Undefined type of operand strcpy(errtext,"Internal Assembler error"); goto error; }; }; // Gather parts of command together in the complete command. j=0; if (lock!=0) { // Lock prefix specified model->code[j]=0xF0; model->mask[j]=0xFF; j++; }; if (datasize==2 && pd->bits!=FF) { // Data size prefix necessary model->code[j]=0x66; model->mask[j]=0xFF; j++; }; if (addrsize==2) { // Address size prefix necessary model->code[j]=0x67; model->mask[j]=0xFF; j++; }; if (segment!=SEG_UNDEF) { // Segment prefix necessary if (segment==SEG_ES) model->code[j]=0x26; else if (segment==SEG_CS) model->code[j]=0x2E; else if (segment==SEG_SS) model->code[j]=0x36; else if (segment==SEG_DS) model->code[j]=0x3E; else if (segment==SEG_FS) model->code[j]=0x64; else if (segment==SEG_GS) model->code[j]=0x65; else { strcpy(errtext,"Internal Assembler error"); goto error; }; model->mask[j]=0xFF; j++; }; if (dispsize>0) { memcpy(tcode+i+1+hasrm+hassib,&displacement,dispsize); if (anydisp==0) memset(tmask+i+1+hasrm+hassib,0xFF,dispsize); }; if (immsize>0) { if (immsize==1) l=0xFFFFFF00L; else if (immsize==2) l=0xFFFF0000L; else l=0L; if ((immediate & l)!=0 && (immediate & l)!=l) { strcpy(errtext,"Constant does not fit into operand"); goto error; }; memcpy(tcode+i+1+hasrm+hassib+dispsize,&immediate,immsize); if (anyimm==0) memset(tmask+i+1+hasrm+hassib+dispsize,0xFF,immsize); }; i=i+1+hasrm+hassib+dispsize+immsize; jmpoffset -= (i+j+jmpsize); model->jmpsize=jmpsize; model->jmpoffset=jmpoffset; model->jmppos=i+j; if (jmpsize!=0) { if (ip!=0) { jmpoffset=jmpoffset-ip; if (jmpsize==1 && anyjmp==0 && (jmpoffset<-128 || jmpoffset>=128)) { if (longjump==0 && (jmpmode & 0x03)==0) { longjump=1; goto retrylongjump; }; sprintf(errtext, "Relative jump out of range, use %s LONG form",name); goto error; }; memcpy(tcode+i,&jmpoffset,jmpsize); }; if (anyjmp==0) memset(tmask+i,0xFF,jmpsize); i+=jmpsize; }; memcpy(model->code+j,tcode,i); memcpy(model->mask+j,tmask,i); i+=j; model->length=i; return i; // Positive value: length of code error: model->length=0; return cmd-asmcmd; // Negative value: position of error }; //#pragma option -O. // Restore old optimization options