/**************************************************************************** * * Open Watcom Project * * Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved. * * ======================================================================== * * This file contains Original Code and/or Modifications of Original * Code as defined in and that are subject to the Sybase Open Watcom * Public License version 1.0 (the 'License'). You may not use this file * except in compliance with the License. BY USING THIS FILE YOU AGREE TO * ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is * provided with the Original Code and Modifications, and is also * available at www.sybase.com/developer/opensource. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR * NON-INFRINGEMENT. Please see the License for the specific language * governing rights and limitations under the License. * * ======================================================================== * * Description: Parser * ****************************************************************************/ #include #include #include "globals.h" #include "memalloc.h" #include "parser.h" #include "preproc.h" #include "reswords.h" #include "codegen.h" #include "expreval.h" #include "fixup.h" #include "types.h" #include "label.h" #include "segment.h" #include "assume.h" #include "proc.h" #include "myassert.h" #include "input.h" #include "tokenize.h" #include "listing.h" #include "data.h" #include "fastpass.h" #include "omf.h" #include "omfspec.h" #include "condasm.h" #include "extern.h" #include "atofloat.h" #define ADDRSIZE( s, x ) ( ( ( x ) ^ ( s ) ) ? TRUE : FALSE ) #define IS_ADDR32( s ) ( s->Ofssize ? ( s->prefix.adrsiz == FALSE ) : ( s->prefix.adrsiz == TRUE )) #define OPSIZE32( s ) ( ( s->Ofssize ) ? FALSE : TRUE ) #define OPSIZE16( s ) ( ( s->Ofssize ) ? TRUE : FALSE ) #define InWordRange( val ) ( (val > 65535 || val < -65535) ? FALSE : TRUE ) extern ret_code (* const directive_tab[])( int, struct asm_tok[] ); /* parsing of branch instructions with imm operand is found in branch.c */ extern ret_code process_branch( struct code_info *, unsigned, const struct expr * ); extern enum proc_status ProcStatus; //extern struct asym symPC; /* '$' symbol */ extern const int_64 maxintvalues[]; extern const int_64 minintvalues[]; extern const struct opnd_class opnd_clstab[]; #if AVXSUPP extern const uint_8 vex_flags[]; #endif extern int_8 Frame_Type; /* Frame of current fixup */ extern uint_16 Frame_Datum; /* Frame datum of current fixup */ struct asym *SegOverride; static enum assume_segreg LastRegOverride;/* needed for CMPS */ /* linked lists of: index *-------------------------------- * - undefined symbols TAB_UNDEF * - externals TAB_EXT * - segments TAB_SEG * - groups TAB_GRP * - procedures TAB_PROC * - aliases TAB_ALIAS */ struct symbol_queue SymTables[TAB_LAST]; /* add item to linked list of symbols */ void sym_add_table( struct symbol_queue *queue, struct dsym *item ) /*****************************************************************/ { #ifdef DEBUG_OUT if ( queue == &SymTables[TAB_UNDEF] ) item->sym.fwdref = TRUE; #endif if( queue->head == NULL ) { queue->head = queue->tail = item; item->next = item->prev = NULL; } else { item->prev = queue->tail; queue->tail->next = item; queue->tail = item; item->next = NULL; } } /* remove an item from a symbol queue. * this is called only for TAB_UNDEF and TAB_EXT, * segments, groups, procs or aliases never change their state. */ void sym_remove_table( struct symbol_queue *queue, struct dsym *item ) /********************************************************************/ { /* unlink the node */ if( item->prev ) item->prev->next = item->next; if( item->next ) item->next->prev = item->prev; //if ( dir->next == NULL ) // dir->next = dir->prev; if ( queue->head == item ) queue->head = item->next; if ( queue->tail == item ) queue->tail = item->prev; item->next = NULL; item->prev = NULL; } void sym_ext2int( struct asym *sym ) /**********************************/ /* Change symbol state from SYM_EXTERNAL to SYM_INTERNAL. * called by: * - CreateConstant() EXTERNDEF name:ABS -> constant * - CreateAssemblyTimeVariable() EXTERNDEF name:ABS -> assembly-time variable * - CreateLabel() EXTERNDEF name:NEAR|FAR|PROC -> code label * - data_dir() EXTERNDEF name:typed memref -> data label * - ProcDir() PROTO or EXTERNDEF name:NEAR|FAR|PROC -> PROC */ { /* v2.07: GlobalQueue has been removed */ if ( sym->isproc == FALSE && sym->ispublic == FALSE ) { sym->ispublic = TRUE; AddPublicData( sym ); } sym_remove_table( &SymTables[TAB_EXT], (struct dsym *)sym ); if ( sym->isproc == FALSE ) /* v2.01: don't clear flags for PROTO */ sym->first_size = 0; sym->state = SYM_INTERNAL; } ret_code GetLangType( int *i, struct asm_tok tokenarray[], enum lang_type *plang ) /********************************************************************************/ { if( tokenarray[*i].token == T_RES_ID ) { #if 1 /* v2.03: simplified */ if ( tokenarray[(*i)].tokval >= T_C && tokenarray[(*i)].tokval <= T_FASTCALL ) { *plang = tokenarray[(*i)].bytval; (*i)++; return( NOT_ERROR ); } #else switch( tokenarray[(*i)].tokval ) { case T_C: *plang = LANG_C; break; case T_SYSCALL: *plang = LANG_SYSCALL; break; case T_STDCALL: *plang = LANG_STDCALL; break; case T_PASCAL: *plang = LANG_PASCAL; break; case T_FORTRAN: *plang = LANG_FORTRAN; break; case T_BASIC: *plang = LANG_BASIC; break; case T_FASTCALL: *plang = LANG_FASTCALL; break; default: return( ERROR ); } (*i)++; return( NOT_ERROR ); #endif } return( ERROR ); } /* get size of a register * v2.06: rewritten, since the sflags field * does now contain size for GPR, STx, MMX, XMM regs. */ int SizeFromRegister( int registertoken ) /***************************************/ { unsigned flags; flags = GetSflagsSp( registertoken ) & SFR_SIZMSK; if ( flags ) return( flags ); flags = GetValueSp( registertoken ); if ( flags & OP_SR ) return( CurrWordSize ); /* CRx, DRx, TRx remaining */ #if AMD64_SUPPORT return( ModuleInfo.Ofssize == USE64 ? 8 : 4 ); #else return( 4 ); #endif } /* get size from memory type */ /* MT_PROC memtype is set ONLY in typedefs ( state=SYM_TYPE, typekind=TYPE_TYPEDEF) * and makes the type a PROTOTYPE. Due to technical (obsolete?) restrictions the * prototype data is stored in another symbol and is referenced in the typedef's * target_type member. */ int SizeFromMemtype( enum memtype mem_type, int Ofssize, struct asym *type ) /**************************************************************************/ { if ( ( mem_type & MT_SPECIAL) == 0 ) return ( (mem_type & MT_SIZE_MASK) + 1 ); if ( Ofssize == USE_EMPTY ) Ofssize = ModuleInfo.Ofssize; switch ( mem_type ) { case MT_NEAR: DebugMsg1(("SizeFromMemtype( MT_NEAR, Ofssize=%u )=%u\n", Ofssize, 2 << Ofssize )); return ( 2 << Ofssize ); case MT_FAR: DebugMsg1(("SizeFromMemtype( MT_FAR, Ofssize=%u )=%u\n", Ofssize, ( 2 << Ofssize ) + 2 )); return ( ( 2 << Ofssize ) + 2 ); case MT_PROC: DebugMsg1(("SizeFromMemtype( MT_PROC, Ofssize=%u, type=%s )=%u\n", Ofssize, type->name, ( 2 << Ofssize ) + ( type->isfar ? 2 : 0 ) )); /* v2.09: use type->isfar setting */ //return( ( 2 << Ofssize ) + ( ( SIZE_CODEPTR & ( 1 << ModuleInfo.model ) ) ? 2 : 0 ) ); return( ( 2 << Ofssize ) + ( type->isfar ? 2 : 0 ) ); case MT_PTR: DebugMsg1(("SizeFromMemtype( MT_PTR, Ofssize=%u )=%u\n", Ofssize, ( 2 << Ofssize ) + ( ( SIZE_DATAPTR & ( 1 << ModuleInfo.model ) ) ? 2 : 0 ) )); return( ( 2 << Ofssize ) + ( ( SIZE_DATAPTR & ( 1 << ModuleInfo.model ) ) ? 2 : 0 ) ); case MT_TYPE: if ( type ) return( type->total_size ); default: DebugMsg1(("SizeFromMemtype( memtype=%Xh, Ofssize=%u )=%u\n", mem_type, Ofssize, 0 )); return( 0 ); } } /* get memory type from size */ ret_code MemtypeFromSize( int size, enum memtype *ptype ) /*******************************************************/ { int i; for ( i = T_BYTE; SpecialTable[i].type == RWT_STYPE; i++ ) { if( ( SpecialTable[i].bytval & MT_SPECIAL ) == 0 ) { /* the size is encoded 0-based in field mem_type */ if( ( ( SpecialTable[i].bytval & MT_SIZE_MASK) + 1 ) == size ) { *ptype = SpecialTable[i].bytval; return( NOT_ERROR ); } } } return( ERROR ); } int OperandSize( enum operand_type opnd, const struct code_info *CodeInfo ) /*************************************************************************/ { /* v2.0: OP_M8_R8 and OP_M16_R16 have the DFT bit set! */ if( opnd == OP_NONE ) { return( 0 ); } else if( opnd == OP_M ) { return( SizeFromMemtype( CodeInfo->mem_type, CodeInfo->Ofssize, NULL ) ); } else if( opnd & ( OP_R8 | OP_M08 | OP_I8 ) ) { return( 1 ); } else if( opnd & ( OP_R16 | OP_M16 | OP_I16 | OP_SR ) ) { return( 2 ); } else if( opnd & ( OP_R32 | OP_M32 | OP_I32 ) ) { return( 4 ); #if AMD64_SUPPORT } else if( opnd & ( OP_R64 | OP_M64 | OP_MMX | OP_I64 ) ) { #else } else if( opnd & ( OP_M64 | OP_MMX ) ) { #endif return( 8 ); // } else if( opnd & ( OP_I | OP_I48 ) ) { } else if( opnd & ( OP_I48 | OP_M48 ) ) { return( 6 ); } else if( opnd & ( OP_STI | OP_M80 ) ) { return( 10 ); } else if( opnd & ( OP_XMM | OP_M128 ) ) { return( 16 ); #if AVXSUPP } else if( opnd & ( OP_YMM | OP_M256 ) ) { return( 32 ); #endif } else if( opnd & OP_RSPEC ) { #if AMD64_SUPPORT return( ( CodeInfo->Ofssize == USE64 ) ? 8 : 4 ); #else return( 4 ); #endif } DebugMsg1(("OperandSize: unhandled operand type %Xh!!!\n", opnd )); return( 0 ); } static int comp_mem16( int reg1, int reg2 ) /*****************************************/ /* - compare and return the r/m field encoding of 16-bit address mode; - call by set_rm_sib() only; */ { switch( reg1 ) { case T_BX: switch( reg2 ) { case T_SI: return( RM_BX_SI ); /* 00 */ case T_DI: return( RM_BX_DI ); /* 01 */ } break; case T_BP: switch( reg2 ) { case T_SI: return( RM_BP_SI ); /* 02 */ case T_DI: return( RM_BP_DI ); /* 03 */ } break; default: return( EmitError( MULTIPLE_INDEX_REGISTERS_NOT_ALLOWED ) ); } return( EmitError( MULTIPLE_BASE_REGISTERS_NOT_ALLOWED ) ); } static void check_assume( struct code_info *CodeInfo, const struct asym *sym, enum assume_segreg default_reg ) /************************************************************************************************************/ /* Check if an assumed segment register is found, and * set CodeInfo->RegOverride if necessary. * called by seg_override(). * at least either sym or SegOverride is != NULL. */ { enum assume_segreg reg; struct asym *assume; if( sym && sym->state == SYM_UNDEFINED ) return; reg = GetAssume( SegOverride, sym, default_reg, &assume ); /* set global vars Frame and Frame_Datum */ DebugMsg1(("check_assume(%s): calling SetFixupFrame(%s, FALSE)\n", sym ? sym->name : "NULL", assume ? assume->name : "NULL" )); SetFixupFrame( assume, FALSE ); if( reg == ASSUME_NOTHING ) { if ( sym ) { //if( sym->state != SYM_EXTERNAL && sym->state != SYM_STACK ) { /* v1.95: condition changed. Now there's an error msg only if * the symbol has an explicite segment. */ if( sym->segment != NULL ) { DebugMsg1(("check_assume: no segment register available to access label %s\n", sym->name )); EmitErr( CANNOT_ACCESS_LABEL_THROUGH_SEGMENT_REGISTERS, sym->name ); } else CodeInfo->prefix.RegOverride = default_reg; } else { DebugMsg1(("check_assume: no segment register available to access seg-label %s\n", SegOverride->name )); EmitErr( CANNOT_ACCESS_LABEL_THROUGH_SEGMENT_REGISTERS, SegOverride->name ); } } else if( default_reg != EMPTY ) { CodeInfo->prefix.RegOverride = reg; } } static void seg_override( struct code_info *CodeInfo, int seg_reg, const struct asym *sym, bool direct ) /******************************************************************************************************/ /* * called by set_rm_sib(). determine if segment override is necessary * with the current address mode; * - seg_reg: register index (T_DS, T_BP, T_EBP, T_BX, ... ) */ { enum assume_segreg default_seg; struct asym *assume; /* don't touch segment overrides for string instructions */ //if ( InstrTable[optable_idx[CodeInfo->token]].allowed_prefix == AP_REP || // InstrTable[optable_idx[CodeInfo->token]].allowed_prefix == AP_REPxx ) if ( CodeInfo->pinstr->allowed_prefix == AP_REP || CodeInfo->pinstr->allowed_prefix == AP_REPxx ) return; if( CodeInfo->token == T_LEA ) { CodeInfo->prefix.RegOverride = EMPTY; /* skip segment override */ SetFixupFrame( sym, FALSE ); return; } switch( seg_reg ) { //case T_SS: /* doesn't happen */ case T_BP: case T_EBP: case T_ESP: /* todo: check why cases T_RBP/T_RSP aren't needed! */ default_seg = ASSUME_SS; break; default: default_seg = ASSUME_DS; } if( CodeInfo->prefix.RegOverride != EMPTY ) { assume = GetOverrideAssume( CodeInfo->prefix.RegOverride ); /* assume now holds assumed SEG/GRP symbol */ if ( sym ) { DebugMsg1(("seg_override: sym=%s\n", sym->name )); SetFixupFrame( assume ? assume : sym, FALSE ); } else if ( direct ) { /* no label attached (DS:[0]). No fixup is to be created! */ if ( assume ) { DebugMsg1(("seg_override, direct addressing: prefix.adrsiz will be set, assume=%s CI->ofssize=%u\n", assume->name, CodeInfo->Ofssize )); CodeInfo->prefix.adrsiz = ADDRSIZE( CodeInfo->Ofssize, GetSymOfssize( assume ) ); //DebugMsg1(("seg_override: CI->prefix.adrsiz=%u\n", CodeInfo->prefix.adrsiz )); } else { /* v2.01: if -Zm, then use current CS offset size. * This isn't how Masm v6 does it, but it matches Masm v5. */ if ( ModuleInfo.m510 ) CodeInfo->prefix.adrsiz = ADDRSIZE( CodeInfo->Ofssize, ModuleInfo.Ofssize ); else CodeInfo->prefix.adrsiz = ADDRSIZE( CodeInfo->Ofssize, ModuleInfo.defOfssize ); } } } else { if ( sym || SegOverride ) check_assume( CodeInfo, sym, default_seg ); if ( sym == NULL && SegOverride ) { CodeInfo->prefix.adrsiz = ADDRSIZE( CodeInfo->Ofssize, GetSymOfssize( SegOverride ) ); } } if( CodeInfo->prefix.RegOverride == default_seg ) { CodeInfo->prefix.RegOverride = EMPTY; } } /* prepare fixup creation * called by: * - idata_fixup() * - process_branch() in branch.c * - data_item() in data.c */ void set_frame( const struct asym *sym ) /**************************************/ { SetFixupFrame( SegOverride ? SegOverride : sym, FALSE ); } /* set fixup frame if OPTION OFFSET:SEGMENT is set and * OFFSET or SEG operator was used. * called by: * - idata_fixup() * - data_item() */ void set_frame2( const struct asym *sym ) /***************************************/ { SetFixupFrame( SegOverride ? SegOverride : sym, TRUE ); } static ret_code set_rm_sib( struct code_info *CodeInfo, unsigned CurrOpnd, char ss, int index, int base, const struct asym *sym ) /*******************************************************************************************************************************/ /* * encode ModRM and SIB byte for memory addressing. * called by memory_operand(). * in: ss = scale factor (00=1,40=2,80=4,C0=8) * index = index register (T_DI, T_ESI, ...) * base = base register (T_EBP, ... ) * sym = symbol (direct addressing, displacement) * out: CodeInfo->rm_byte, CodeInfo->sib, CodeInfo->prefix.rex */ { int temp; unsigned char mod_field; unsigned char rm_field; unsigned char base_reg; unsigned char idx_reg; #if AMD64_SUPPORT unsigned char bit3_base; unsigned char bit3_idx; unsigned char rex; #endif DebugMsg1(("set_rm_sib(scale=%u, index=%d, base=%d, sym=%s) enter [CI.adrsiz=%u]\n", 1 << (ss >> 6), index, base, sym ? sym->name : "NULL", CodeInfo->prefix.adrsiz )); /* clear mod */ rm_field = 0; #if AMD64_SUPPORT bit3_base = 0; bit3_idx = 0; rex = 0; #endif if( CodeInfo->opnd[CurrOpnd].InsFixup != NULL ) { /* symbolic displacement given? */ mod_field = MOD_10; } else if( CodeInfo->opnd[CurrOpnd].data32l == 0 ) { /* no displacement (or 0) */ mod_field = MOD_00; } else if( ( CodeInfo->opnd[CurrOpnd].data32l > SCHAR_MAX ) || ( CodeInfo->opnd[CurrOpnd].data32l < SCHAR_MIN ) ) { mod_field = MOD_10; /* full size displacement */ } else { mod_field = MOD_01; /* byte size displacement */ } if( ( index == EMPTY ) && ( base == EMPTY ) ) { /* direct memory. * clear the rightmost 3 bits */ CodeInfo->isdirect = TRUE; mod_field = MOD_00; /* default is DS:[], DS: segment override is not needed */ seg_override( CodeInfo, T_DS, sym, TRUE ); DebugMsg1(( "set_rm_sib: direct addressing, CI.Ofssize=%u / adrsize=%u / data=%" I32_SPEC "X\n", CodeInfo->Ofssize, CodeInfo->prefix.adrsiz, CodeInfo->opnd[CurrOpnd].data32l )); //if( !IS_ADDR32( CodeInfo ) ) { if( ( CodeInfo->Ofssize == USE16 && CodeInfo->prefix.adrsiz == 0 ) || ( CodeInfo->Ofssize == USE32 && CodeInfo->prefix.adrsiz == 1 )) { if( !InWordRange( CodeInfo->opnd[CurrOpnd].data32l ) ) { /* expect 16-bit but got 32-bit address */ DebugMsg1(( "set_rm_sib: error, Ofssize=%u, adrsize=%u, data=%" I32_SPEC "X\n", CodeInfo->Ofssize, CodeInfo->prefix.adrsiz, CodeInfo->opnd[CurrOpnd].data32l )); return( EmitError( MAGNITUDE_OF_OFFSET_EXCEEDS_16BIT ) ); } rm_field = RM_D16; /* D16=110b */ } else { rm_field = RM_D32; /* D32=101b */ #if AMD64_SUPPORT /* v2.03: the non-RIP encoding for 64bit uses a redundant SIB mode (base=none, index=none) */ /* v2.11: always use 64-bit non-RIP addressing if no fixup has been created. */ //if ( CodeInfo->Ofssize == USE64 && CodeInfo->prefix.RegOverride != EMPTY && SegOverride != &ModuleInfo.flat_grp->sym ) { if ( CodeInfo->Ofssize == USE64 && CodeInfo->opnd[CurrOpnd].InsFixup == NULL ) { DebugMsg1(( "set_rm_sib: 64-bit, no fixup, data64=%" I64_SPEC "X\n", CodeInfo->opnd[CurrOpnd].data64 )); rm_field = RM_SIB; CodeInfo->sib = 0x25; /* IIIBBB, base=101b, index=100b */ } #endif } DebugMsg1(("set_rm_sib, direct, CodeInfo->prefix.adrsiz=%u\n", CodeInfo->prefix.adrsiz )); } else if( ( index == EMPTY ) && ( base != EMPTY ) ) { /* for SI, DI and BX: default is DS:[], * DS: segment override is not needed * for BP: default is SS:[], SS: segment override is not needed */ switch( base ) { case T_SI: rm_field = RM_SI; /* 4 */ break; case T_DI: rm_field = RM_DI; /* 5 */ break; case T_BP: rm_field = RM_BP; /* 6 */ if( mod_field == MOD_00 ) { mod_field = MOD_01; } break; case T_BX: rm_field = RM_BX; /* 7 */ break; default: /* for 386 and up */ base_reg = GetRegNo( base ); #if AMD64_SUPPORT bit3_base = base_reg >> 3; base_reg &= BIT_012; #endif rm_field = base_reg; DebugMsg1(("set_rm_sib: base_reg is %u\n", base_reg )); if ( base_reg == 4 ) { /* 4 is RSP/ESP or R12/R12D, which must use SIB encoding. * SSIIIBBB, ss = 00, index = 100b ( no index ), base = 100b ( ESP ) */ CodeInfo->sib = 0x24; } else if ( base_reg == 5 && mod_field == MOD_00 ) { /* 5 is [E|R]BP or R13[D]. Needs displacement */ mod_field = MOD_01; } #if AMD64_SUPPORT /* v2.02 */ //rex = ( bit3_base << 2 ); /* set REX_R */ rex = bit3_base; /* set REX_R */ #endif } #if AMD64_SUPPORT DebugMsg1(("set_rm_sib, indirect with base, mod_field=%X, rm_field=%X, rex=%X\n", mod_field, rm_field, rex )); #else DebugMsg1(("set_rm_sib, indirect with base, rm_field=%X\n", rm_field )); #endif seg_override( CodeInfo, base, sym, FALSE ); } else if( ( index != EMPTY ) && ( base == EMPTY ) ) { idx_reg = GetRegNo( index ); #if AMD64_SUPPORT bit3_idx = idx_reg >> 3; idx_reg &= BIT_012; #endif /* mod field is 00 */ mod_field = MOD_00; /* s-i-b is present ( r/m = 100b ) */ rm_field = RM_SIB; /* scale factor, index, base ( 0x05 => no base reg ) */ CodeInfo->sib = ( ss | ( idx_reg << 3 ) | 0x05 ); #if AMD64_SUPPORT rex = (bit3_idx << 1); /* set REX_X */ #endif /* default is DS:[], DS: segment override is not needed */ seg_override( CodeInfo, T_DS, sym, FALSE ); } else { /* base != EMPTY && index != EMPTY */ base_reg = GetRegNo( base ); idx_reg = GetRegNo( index ); #if AMD64_SUPPORT bit3_base = base_reg >> 3; bit3_idx = idx_reg >> 3; base_reg &= BIT_012; idx_reg &= BIT_012; #endif if ( ( GetSflagsSp( base ) & GetSflagsSp( index ) & SFR_SIZMSK ) == 0 ) { return( EmitError( CANNOT_MIX_16_AND_32_BIT_REGISTERS ) ); } switch( index ) { case T_BX: case T_BP: if( ( temp = comp_mem16( index, base ) ) == ERROR ) return( ERROR ); rm_field = temp; seg_override( CodeInfo, index, sym, FALSE ); break; case T_SI: case T_DI: if( ( temp = comp_mem16( base, index ) ) == ERROR ) return( ERROR ); rm_field = temp; seg_override( CodeInfo, base, sym, FALSE ); break; #if AMD64_SUPPORT case T_RSP: #endif case T_ESP: //EmitErr( CANNOT_BE_USED_AS_INDEX_REGISTER, ??? ); return( EmitError( INVALID_USE_OF_REGISTER ) ); default: if( base_reg == 5 ) { /* v2.03: EBP/RBP/R13/R13D? */ if( mod_field == MOD_00 ) { mod_field = MOD_01; } } /* s-i-b is present ( r/m = 100b ) */ rm_field |= RM_SIB; CodeInfo->sib = ( ss | idx_reg << 3 | base_reg ); #if AMD64_SUPPORT rex = (bit3_idx << 1) + (bit3_base); /* set REX_X + REX_B */ #endif seg_override( CodeInfo, base, sym, FALSE ); } /* end switch(index) */ #if AMD64_SUPPORT DebugMsg1(("set_rm_sib, indirect, base+index: mod_field=%X, rm_field=%X, rex=%X\n", mod_field, rm_field, rex )); #else DebugMsg1(("set_rm_sib, indirect, base+index: rm_field=%X\n", rm_field )); #endif } if( CurrOpnd == OPND2 ) { /* shift the register field to left by 3 bit */ CodeInfo->rm_byte = mod_field | ( rm_field << 3 ) | ( CodeInfo->rm_byte & BIT_012 ); #if AMD64_SUPPORT /* v2.02: exchange B and R, keep X */ //CodeInfo->prefix.rex |= (rex >> 2 ); CodeInfo->prefix.rex |= ( ( rex >> 2 ) | ( rex & REX_X ) | (( rex & 1) << 2 ) ); #endif } else if( CurrOpnd == OPND1 ) { CodeInfo->rm_byte = mod_field | rm_field; #if AMD64_SUPPORT CodeInfo->prefix.rex |= rex; #endif } return( NOT_ERROR ); } /* override handling * called by * - process_branch() * - idata_fixup() * - memory_operand() (CodeInfo != NULL) * - data_item() * 1. If it's a segment register, set CodeInfo->prefix.RegOverride. * 2. Set global variable SegOverride if it's a SEG/GRP symbol * (or whatever is assumed for the segment register) */ ret_code segm_override( const struct expr *opndx, struct code_info *CodeInfo ) /****************************************************************************/ { struct asym *sym; if( opndx->override != NULL ) { if( opndx->override->token == T_REG ) { int temp = GetRegNo( opndx->override->tokval ); if ( SegAssumeTable[temp].error ) { DebugMsg(("segm_override: assume error, reg=%u\n", temp )); return( EmitError( USE_OF_REGISTER_ASSUMED_TO_ERROR ) ); } #if AMD64_SUPPORT /* ES,CS,SS and DS overrides are invalid in 64-bit */ if ( CodeInfo && CodeInfo->Ofssize == USE64 && temp < ASSUME_FS ) { return( EmitError( ILLEGAL_USE_OF_SEGMENT_REGISTER ) ); } #endif sym = GetOverrideAssume( temp ); if ( CodeInfo ) { /* hack: save the previous reg override value (needed for CMPS) */ LastRegOverride = CodeInfo->prefix.RegOverride; CodeInfo->prefix.RegOverride = temp; } } else { sym = SymSearch( opndx->override->string_ptr ); } if ( sym && ( sym->state == SYM_GRP || sym->state == SYM_SEG )) SegOverride = sym; } return( NOT_ERROR ); } /* get an immediate operand without a fixup. * output: * - ERROR: error * - NOT_ERROR: ok, * CodeInfo->opnd_type[CurrOpnd] = OP_Ix * CodeInfo->data[CurrOpnd] = value * CodeInfo->prefix.opsiz * CodeInfo->iswide */ static ret_code idata_nofixup( struct code_info *CodeInfo, unsigned CurrOpnd, const struct expr *opndx ) /******************************************************************************************************/ { enum operand_type op_type; int_32 value; int size; DebugMsg1(("idata_nofixup( CurrOpnd=%u ) enter [opnd kind=%u mem_type=%Xh value=%" I64_SPEC "X]\n", CurrOpnd, opndx->kind, opndx->mem_type, opndx->value64)); /* jmp/call/jxx/loop/jcxz/jecxz? */ if( IS_ANY_BRANCH( CodeInfo->token ) ) { return( process_branch( CodeInfo, CurrOpnd, opndx ) ); } value = opndx->value; CodeInfo->opnd[CurrOpnd].data32l = value; #if AMD64_SUPPORT /* 64bit immediates are restricted to MOV , */ if ( opndx->hlvalue != 0 ) { /* magnitude > 64 bits? */ DebugMsg1(("idata_nofixup: error, hlvalue=%" I64_SPEC "X\n", opndx->hlvalue )); return( EmitConstError( opndx ) ); } /* v2.03: handle QWORD type coercion here as well! * This change also reveals an old problem in the expression evaluator: * the mem_type field is set whenever a (simple) type token is found. * It should be set ONLY when the type is used in conjuction with the * PTR operator! * current workaround: query the 'explicit' flag. */ //if ( opndx->value64 <= minintvalues[0] || opndx->value64 > maxintvalues[0] ) { /* use long format of MOV for 64-bit if value won't fit in a signed DWORD */ if ( CodeInfo->Ofssize == USE64 && CodeInfo->token == T_MOV && CurrOpnd == OPND2 && ( CodeInfo->opnd[OPND1].type & OP_R64 ) && ( opndx->value64 > INT32_MAX || opndx->value64 < INT32_MIN || (opndx->explicit && ( opndx->mem_type == MT_QWORD || opndx->mem_type == MT_SQWORD ) ) ) ) { // CodeInfo->iswide = 1; /* has been set by first operand already */ CodeInfo->opnd[CurrOpnd].type = OP_I64; CodeInfo->opnd[CurrOpnd].data32h = opndx->hvalue; return( NOT_ERROR ); } if ( opndx->value64 <= minintvalues[0] || opndx->value64 > maxintvalues[0] ) { DebugMsg1(("idata_nofixup: error, hvalue=%Xh\n", opndx->hvalue )); return( EmitConstError( opndx ) ); } #endif /* v2.06: code simplified. * to be fixed: the "wide" bit should not be set here! * Problem: the "wide" bit isn't set in memory_operand(), * probably because of the instructions which accept both * signed and unsigned arguments (ADD, CMP, ... ). */ if ( opndx->explicit ) { /* size coercion for immediate value */ CodeInfo->const_size_fixed = TRUE; size = SizeFromMemtype( opndx->mem_type, opndx->Ofssize, opndx->type ); /* don't check if size and value are compatible. */ switch ( size ) { case 1: op_type = OP_I8; break; case 2: op_type = OP_I16; break; case 4: op_type = OP_I32; break; default: DebugMsg1(("idata_nofixup: invalid size %d for immediate operand\n", size )); return( EmitError( INVALID_INSTRUCTION_OPERANDS ) ); } } else { /* use true signed values for BYTE only! */ if ( (int_8)value == value ) op_type = OP_I8; //else if ( value <= SHRT_MAX && value >= SHRT_MIN ) /* v2.04: range FFFF0000-FFFF7FFF is also acceptable for 16-bit */ //else if ( value <= USHRT_MAX && value >= SHRT_MIN ) /* v2.04b: JWASMR needs a 1L */ //else if( value <= USHRT_MAX && value >= - (USHRT_MAX+1) ) /* v2.07: JWASMR needs 0L before the - op */ //else if( value <= USHRT_MAX && value >= - USHRT_MAX ) else if( value <= USHRT_MAX && value >= 0L - USHRT_MAX ) op_type = OP_I16; else { op_type = OP_I32; } } switch ( CodeInfo->token ) { case T_PUSH: if ( opndx->explicit == FALSE ) { if ( CodeInfo->Ofssize > USE16 && op_type == OP_I16 ) op_type = OP_I32; } if ( op_type == OP_I16 ) CodeInfo->prefix.opsiz = OPSIZE16( CodeInfo ); else if ( op_type == OP_I32 ) CodeInfo->prefix.opsiz = OPSIZE32( CodeInfo ); break; case T_PUSHW: if ( op_type != OP_I32 ) { op_type = OP_I16; if( (int_8)value == (int_16)value ) { op_type = OP_I8; } } break; case T_PUSHD: if ( op_type == OP_I16 ) op_type = OP_I32; break; } /* v2.11: set the wide-bit if a mem_type size of > BYTE is set??? * actually, it should only be set if immediate is second operand * ( and first operand is a memory ref with a size > 1 ) */ if ( CurrOpnd == OPND2 ) if ( !(CodeInfo->mem_type & MT_SPECIAL) && ( CodeInfo->mem_type & MT_SIZE_MASK ) ) CodeInfo->iswide = 1; CodeInfo->opnd[CurrOpnd].type = op_type; DebugMsg1(("idata_nofixup exit, op_type=%" I32_SPEC "X\n", op_type )); return( NOT_ERROR ); } /* get an immediate operand with a fixup. * output: * - ERROR: error * - NOT_ERROR: ok, * CodeInfo->opnd_type[CurrOpnd] = OP_Ix * CodeInfo->data[CurrOpnd] = value * CodeInfo->InsFixup[CurrOpnd] = fixup * CodeInfo->mem_type * CodeInfo->prefix.opsiz * to be fixed: don't modify CodeInfo->mem_type here! */ ret_code idata_fixup( struct code_info *CodeInfo, unsigned CurrOpnd, struct expr *opndx ) /***************************************************************************************/ { //struct fixup *fixup; enum fixup_types fixup_type; enum fixup_options fixup_option = OPTJ_NONE; int size; uint_8 Ofssize; /* 1=32bit, 0=16bit offset for fixup */ DebugMsg1(("idata_fixup( CurrOpnd=%u ) enter [opndx.kind=%u mem_type=%Xh, CodeInfo.mem_type=%Xh]\n", CurrOpnd, opndx->kind, opndx->mem_type, CodeInfo->mem_type)); /* jmp/call/jcc/loopcc/jxcxz? */ if( IS_ANY_BRANCH( CodeInfo->token ) ) { return( process_branch( CodeInfo, CurrOpnd, opndx ) ); } CodeInfo->opnd[CurrOpnd].data32l = opndx->value; if ( opndx->Ofssize != USE_EMPTY ) { Ofssize = opndx->Ofssize; } else if( ( opndx->sym->state == SYM_SEG ) || ( opndx->sym->state == SYM_GRP ) || ( opndx->instr == T_SEG ) ) { Ofssize = USE16; } else if( opndx->is_abs ) { /* an (external) absolute symbol? */ Ofssize = USE16; } else { Ofssize = GetSymOfssize( opndx->sym ); } if( opndx->instr == T_SHORT ) { /* short works for branch instructions only */ return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } /* the code below should be rewritten. * - an address operator ( OFFSET, LROFFSET, IMAGEREL, SECTIONREL, * LOW, HIGH, LOWWORD, HIGHWORD, LOW32, HIGH32, SEG ) should not * force a magnitude, but may set a minimal magnitude - and the * fixup type, of course. * - check if Codeinfo->mem_type really has to be set here! */ /* v2.06: added */ /* v2.10: modified */ //if ( opndx->explicit ) { if ( opndx->explicit && !opndx->is_abs ) { CodeInfo->const_size_fixed = TRUE; if ( CodeInfo->mem_type == MT_EMPTY ) CodeInfo->mem_type = opndx->mem_type; } /* v2.03: don't ignore a "NEAR32 ptr" qualifier */ //if ( CodeInfo->mem_type == MT_EMPTY && CurrOpnd > OPND1 ) { if ( CodeInfo->mem_type == MT_EMPTY && CurrOpnd > OPND1 && opndx->Ofssize == USE_EMPTY ) { size = OperandSize( CodeInfo->opnd[OPND1].type, CodeInfo ); /* may be a forward reference, so wait till pass 2 */ if( Parse_Pass > PASS_1 && opndx->instr != EMPTY ) { switch ( opndx->instr ) { case T_SEG: /* v2.04a: added */ if( size && (size < 2 ) ) { return( EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, size, 2 ) ); } break; case T_OFFSET: case T_LROFFSET: #if IMAGERELSUPP case T_IMAGEREL: #endif #if SECTIONRELSUPP case T_SECTIONREL: #endif if( size && (size < 2 || ( Ofssize && size < 4 ))) { return( EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, size, ( 2 << Ofssize ) ) ); } } } switch ( size ) { case 1: /* v2.05: if () added */ if ( opndx->is_abs || opndx->instr == T_LOW || opndx->instr == T_HIGH ) CodeInfo->mem_type = MT_BYTE; break; case 2: /* v2.05: if () added */ if ( opndx->is_abs || CodeInfo->Ofssize == USE16 || opndx->instr == T_LOWWORD || opndx->instr == T_HIGHWORD ) CodeInfo->mem_type = MT_WORD; break; case 4: CodeInfo->mem_type = MT_DWORD; break; #if AMD64_SUPPORT case 8: /* v2.05: it's questionable if size 8 is a good assumption for an * immediate constant. It's valid for MOV , only. */ //case 8: CodeInfo->mem_type = MT_QWORD;break; /* v2.05a: added */ if ( Ofssize == USE64 ) { if ( CodeInfo->token == T_MOV && ( CodeInfo->opnd[OPND1].type & OP_R64 ) ) CodeInfo->mem_type = MT_QWORD; else if ( opndx->instr == T_LOW32 || opndx->instr == T_HIGH32 ) /* v2.10:added; LOW32/HIGH32 in expreval.c won't set mem_type anymore. */ CodeInfo->mem_type = MT_DWORD; } break; #endif } } if ( CodeInfo->mem_type == MT_EMPTY ) { if( opndx->is_abs ) { //if( opndx->mem_type != MT_EMPTY && opndx->mem_type != MT_ABS ) { if( opndx->mem_type != MT_EMPTY ) { CodeInfo->mem_type = opndx->mem_type; } else if ( CodeInfo->token == T_PUSHW ) { /* v2.10: special handling PUSHW */ CodeInfo->mem_type = MT_WORD; } else { CodeInfo->mem_type = ( IS_OPER_32( CodeInfo ) ? MT_DWORD : MT_WORD ); } } else { switch ( CodeInfo->token ) { case T_PUSHW: case T_PUSHD: case T_PUSH: /* for forward reference, assume BYTE */ /* v2.02: don't assume BYTE if it is SEG/GRP */ //if ( opndx->mem_type == MT_EMPTY ) { /* v2.07: added cases IMAGEREL and SECTIONREL */ if ( opndx->mem_type == MT_EMPTY ) { switch( opndx->instr ) { case EMPTY: case T_LOW: case T_HIGH: opndx->mem_type = MT_BYTE; break; case T_LOW32: /* v2.10: added - low32_op() doesn't set mem_type anymore. */ #if IMAGERELSUPP case T_IMAGEREL: #endif #if SECTIONRELSUPP case T_SECTIONREL: #endif opndx->mem_type = MT_DWORD; break; }; } /* default: push offset only */ /* for PUSH + undefined symbol, assume BYTE */ if ( opndx->mem_type == MT_FAR && ( opndx->explicit == FALSE ) ) opndx->mem_type = MT_NEAR; /* v2.04: curly brackets added */ if ( CodeInfo->token == T_PUSHW ) { if ( SizeFromMemtype( opndx->mem_type, Ofssize, opndx->type ) < 2 ) opndx->mem_type = MT_WORD; } else if ( CodeInfo->token == T_PUSHD ) { if ( SizeFromMemtype( opndx->mem_type, Ofssize, opndx->type ) < 4 ) opndx->mem_type = MT_DWORD; } break; } /* if a WORD size is given, don't override it with */ /* anything what might look better at first glance */ if( opndx->mem_type != MT_EMPTY ) CodeInfo->mem_type = opndx->mem_type; /* v2.04: assume BYTE size if symbol is undefined */ else if ( opndx->sym->state == SYM_UNDEFINED ) { CodeInfo->mem_type = MT_BYTE; fixup_option = OPTJ_PUSH; } else #if AMD64_SUPPORT /* v2.06d: changed */ CodeInfo->mem_type = ( Ofssize == USE64 ? MT_QWORD : Ofssize == USE32 ? MT_DWORD : MT_WORD ); #else CodeInfo->mem_type = ( Ofssize > USE16 ? MT_DWORD : MT_WORD ); #endif } } size = SizeFromMemtype( CodeInfo->mem_type, Ofssize, NULL ); switch( size ) { case 1: CodeInfo->opnd[CurrOpnd].type = OP_I8; CodeInfo->prefix.opsiz = FALSE; /* v2.10: reset opsize is not really a good idea - might have been set by previous operand */ break; case 2: CodeInfo->opnd[CurrOpnd].type = OP_I16; CodeInfo->prefix.opsiz = OPSIZE16( CodeInfo ); break; case 4: CodeInfo->opnd[CurrOpnd].type = OP_I32; CodeInfo->prefix.opsiz = OPSIZE32( CodeInfo ); break; #if AMD64_SUPPORT case 8: /* v2.05: do only assume size 8 if the constant won't fit in 4 bytes. */ if ( opndx->value64 > INT32_MAX || opndx->value64 < INT32_MIN || (opndx->explicit && ( opndx->mem_type & MT_SIZE_MASK ) == 7 ) ) { CodeInfo->opnd[CurrOpnd].type = OP_I64; CodeInfo->opnd[CurrOpnd].data32h = opndx->hvalue; //} else if ( Ofssize == USE64 ) { /* v2.11: assume 64-bit only for OFFSET or MOV r64, xxx */ } else if ( Ofssize == USE64 && ( opndx->instr == T_OFFSET || ( CodeInfo->token == T_MOV && ( CodeInfo->opnd[OPND1].type & OP_R64 ) ) ) ) { /* v2.06d: in 64-bit, ALWAYS set OP_I64, so "mov m64, ofs" will fail, * This was accepted in v2.05-v2.06c) */ CodeInfo->opnd[CurrOpnd].type = OP_I64; CodeInfo->opnd[CurrOpnd].data32h = opndx->hvalue; } else { CodeInfo->opnd[CurrOpnd].type = OP_I32; } CodeInfo->prefix.opsiz = OPSIZE32( CodeInfo ); break; #endif #ifdef DEBUG_OUT default: DebugMsg1(("idata_fixup, unexpected size %u\n", size )); /**/myassert( 0 ); #endif } /* set fixup_type */ if( opndx->instr == T_SEG ) { fixup_type = FIX_SEG; } else if( CodeInfo->mem_type == MT_BYTE ) { DebugMsg1(("idata_fixup, mem_type=BYTE\n" )); if ( opndx->instr == T_HIGH ) { DebugMsg1(("idata_fixup, FIX_HIBYTE\n" )); fixup_type = FIX_HIBYTE; } else { DebugMsg1(("idata_fixup, FIX_OFF8\n" )); fixup_type = FIX_OFF8; } #if 0 } else if( CodeInfo->mem_type == MT_FAR ) { /* v2.04: to be tested. this code is most likely obsolete. * There's never a PTR16|PTR32 fixup here. Far JMP/CALL are handled * elsewhere, and data items also. */ /* temporary */ printf("idata_fixup: MT_FAR occured at %s:%" I32_SPEC "u\n", CurrFName[ASM], LineNumber ); fixup_type = ( Ofssize ) ? FIX_PTR32 : FIX_PTR16; CodeInfo->isfar = TRUE; /* needed for mark_fixupp() */ if ( opndx->Ofssize != USE_EMPTY ) CodeInfo->Ofssize = opndx->Ofssize; #endif } else if( IS_OPER_32( CodeInfo ) ) { #if AMD64_SUPPORT /* v2.06: changed */ //if ( Ofssize == USE64 && CodeInfo->mem_type == MT_QWORD ) /* v2.10: changed */ //if ( CodeInfo->opnd[CurrOpnd].type == OP_I64 ) if ( CodeInfo->opnd[CurrOpnd].type == OP_I64 && ( opndx->instr == EMPTY || opndx->instr == T_OFFSET ) ) fixup_type = FIX_OFF64; else #endif /* v2.04: changed, no longer depends on OfsSize */ /* v2.05a: changed, so size==8 won't get a FIX_OFF16 type */ //if ( size == 4 ) if ( size >= 4 && opndx->instr != T_LOWWORD ) { /* v2.06: added branch for PTR16 fixup. * it's only done if type coercion is FAR (Masm-compat) */ if ( opndx->explicit && Ofssize == USE16 && opndx->mem_type == MT_FAR ) fixup_type = FIX_PTR16; else fixup_type = FIX_OFF32; } else fixup_type = FIX_OFF16; } else { /* v2.04: changed, no longer depends on OfsSize */ //if ( CodeInfo->mem_type == MT_DWORD ) { /* fixme !!!! warning * operand size is 16bit * but fixup is 32-bit */ // fixup_type = FIX_OFF32; //} else fixup_type = FIX_OFF16; } /* v2.04: 'if' added, don't set W bit if size == 1 * code example: * extern x:byte * or al,x * v2.11: set wide bit only if immediate is second operand. * and first operand is a memory reference with size > 1 */ //if ( size != 1 ) if ( CurrOpnd == OPND2 && size != 1 ) CodeInfo->iswide = 1; segm_override( opndx, NULL ); /* set SegOverride global var */ /* set frame type in variables Frame_Type and Frame_Datum for fixup creation */ if ( ModuleInfo.offsettype == OT_SEGMENT && ( opndx->instr == T_OFFSET || opndx->instr == T_SEG )) set_frame2( opndx->sym ); else set_frame( opndx->sym ); //DebugMsg1(("idata_fixup: calling CreateFixup(%s, %u)\n", opndx->sym->name, fixup_type )); CodeInfo->opnd[CurrOpnd].InsFixup = CreateFixup( opndx->sym, fixup_type, fixup_option ); if ( opndx->instr == T_LROFFSET ) CodeInfo->opnd[CurrOpnd].InsFixup->loader_resolved = TRUE; #if IMAGERELSUPP if ( opndx->instr == T_IMAGEREL && fixup_type == FIX_OFF32 ) CodeInfo->opnd[CurrOpnd].InsFixup->type = FIX_OFF32_IMGREL; #endif #if SECTIONRELSUPP if ( opndx->instr == T_SECTIONREL && fixup_type == FIX_OFF32 ) CodeInfo->opnd[CurrOpnd].InsFixup->type = FIX_OFF32_SECREL; #endif DebugMsg1(("idata_fixup exit [CodeInfo.mem_type=%Xh Ofssize=%u opsiz=%u fixup.type=%u fixup.frame=%d]\n", CodeInfo->mem_type, CodeInfo->Ofssize, CodeInfo->prefix.opsiz, CodeInfo->opnd[CurrOpnd].InsFixup->type, CodeInfo->opnd[CurrOpnd].InsFixup->frame_type )); return( NOT_ERROR ); } /* convert MT_PTR to MT_WORD, MT_DWORD, MT_FWORD, MT_QWORD. * MT_PTR cannot be set explicitely (by the PTR operator), * so this value must come from a label or a structure field. * (above comment is most likely plain wrong, see 'PF16 ptr [reg]'! * This code needs cleanup! */ static void SetPtrMemtype( struct code_info *CodeInfo, struct expr *opndx ) /*************************************************************************/ { struct asym *sym = opndx->sym; int size = 0; if ( opndx->mbr ) /* the mbr field has higher priority */ sym = opndx->mbr; /* v2.10: the "explicit" condition is now handled FIRST */ #if 1 /* v2.0: handle PF16 ptr [ebx], which didn't work in v1.96 */ if ( opndx->explicit && opndx->type ) { size = opndx->type->total_size; CodeInfo->isfar = opndx->type->isfar; } else #endif if ( sym ) { if ( sym->type ) { size = sym->type->total_size; CodeInfo->isfar = sym->type->isfar; /* there's an ambiguity with pointers of size DWORD, since they can be either NEAR32 or FAR16 */ if ( size == 4 && sym->type->Ofssize != CodeInfo->Ofssize ) opndx->Ofssize = sym->type->Ofssize; } else if ( sym->mem_type == MT_PTR ) { size = SizeFromMemtype( sym->isfar ? MT_FAR : MT_NEAR, sym->Ofssize, NULL ); CodeInfo->isfar = sym->isfar; } else { if ( sym->isarray ) size = sym->total_size / sym->total_length; else size = sym->total_size; } } else { if ( SIZE_DATAPTR & ( 1 << ModuleInfo.model ) ) { DebugMsg1(("SetPtrMemtype: model with FAR data pointers\n" )); size = 2; } size += (2 << ModuleInfo.defOfssize ); } if ( size ) MemtypeFromSize( size, &opndx->mem_type ); DebugMsg1(("SetPtrMemtype: size=%u, new memtype=0x%x\n", size, opndx->mem_type )); } /* * set fields in CodeInfo: * - mem_type * - prefix.opsiz * - prefix.rex REX_W * called by memory_operand() */ static void Set_Memtype( struct code_info *CodeInfo, enum memtype mem_type ) /**************************************************************************/ { if( CodeInfo->token == T_LEA ) return; /* v2.05: changed. Set "data" types only. */ if( mem_type == MT_EMPTY || mem_type == MT_TYPE || mem_type == MT_NEAR || mem_type == MT_FAR ) return; CodeInfo->mem_type = mem_type; if( CodeInfo->Ofssize > USE16 ) { /* if we are in use32 mode, we have to add OPSIZ prefix for * most of the 386 instructions when operand has type WORD. * Exceptions ( MOVSX and MOVZX ) are handled in check_size(). */ if ( IS_MEM_TYPE( mem_type, WORD ) ) CodeInfo->prefix.opsiz = TRUE; #if AMD64_SUPPORT /* * set rex Wide bit if a QWORD operand is found (not for FPU/MMX/SSE instr). * This looks pretty hackish now and is to be cleaned! * v2.01: also had issues with SSE2 MOVSD/CMPSD, now fixed! */ /* v2.06: with AVX, SSE tokens may exist twice, one * for "legacy", the other for VEX encoding! */ else if ( IS_MEMTYPE_SIZ( mem_type, sizeof( uint_64 ) ) ) { switch( CodeInfo->token ) { case T_PUSH: /* for PUSH/POP, REX_W isn't needed (no 32-bit variants in 64-bit mode) */ case T_POP: case T_CMPXCHG8B: #if VMXSUPP case T_VMPTRLD: case T_VMPTRST: case T_VMCLEAR: case T_VMXON: #endif break; default: /* don't set REX for opcodes that accept memory operands * of any size. */ if ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND1] == OP_M_ANY ) { //printf( "Set_Memtype: OP_M_ANY detected, file=%s, instr=%s\n", CurrFName[ASM], GetResWName( CodeInfo->token, NULL ) ); break; } /* don't set REX for FPU opcodes */ if ( CodeInfo->pinstr->cpu & P_FPU_MASK ) break; /* don't set REX for - most - MMX/SSE opcodes */ if ( CodeInfo->pinstr->cpu & P_EXT_MASK ) { switch ( CodeInfo->token ) { /* [V]CMPSD and [V]MOVSD are also candidates, * but currently they are handled in HandleStringInstructions() */ case T_CVTSI2SD: /* v2.06: added */ case T_CVTSI2SS: /* v2.06: added */ case T_PEXTRQ: /* v2.06: added */ case T_PINSRQ: /* v2.06: added */ case T_MOVD: #if AVXSUPP case T_VCVTSI2SD: case T_VCVTSI2SS: case T_VPEXTRQ: case T_VPINSRQ: case T_VMOVD: #endif CodeInfo->prefix.rex |= REX_W; break; default: break; } } else CodeInfo->prefix.rex |= REX_W; } } #endif /* v2.05: IS_MEM_TYPE() doesn't work with MT_REALx */ //} else if( CodeInfo->Ofssize == USE16 && ( IS_MEM_TYPE( mem_type, DWORD ) ) ) { } else { if( IS_MEMTYPE_SIZ( mem_type, sizeof(uint_32) ) ) { /* in 16bit mode, a DWORD memory access usually requires an OPSIZ * prefix. A few instructions, which access m16:16 operands, * are exceptions. */ switch( CodeInfo->token ) { case T_LDS: case T_LES: case T_LFS: case T_LGS: case T_LSS: case T_CALL: /* v2.0: added */ case T_JMP: /* v2.0: added */ /* in these cases, opsize does NOT need to be changed */ break; default: CodeInfo->prefix.opsiz = TRUE; break; } } #if AMD64_SUPPORT /* v2.06: added because in v2.05, 64-bit memory operands were * accepted in 16-bit code */ else if ( IS_MEMTYPE_SIZ( mem_type, sizeof(uint_64) ) ) { if ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[OPND1] == OP_M_ANY ) { //printf( "Set_Memtype: OP_M_ANY detected, file=%s, instr=%s\n", CurrFName[ASM], GetResWName( CodeInfo->token, NULL ) ); } else if ( CodeInfo->pinstr->cpu & ( P_FPU_MASK | P_EXT_MASK ) ) { ; } else if ( CodeInfo->token != T_CMPXCHG8B ) /* setting REX.W will cause an error in codegen */ CodeInfo->prefix.rex |= REX_W; } #endif } return; } /* * process direct or indirect memory operand * in: opndx=operand to process * in: CurrOpnd=no of operand (0=first operand,1=second operand) * out: CodeInfo->data[] * out: CodeInfo->opnd_type[] */ static ret_code memory_operand( struct code_info *CodeInfo, unsigned CurrOpnd, struct expr *opndx, bool with_fixup ) /******************************************************************************************************************/ { char ss = SCALE_FACTOR_1; int index; int base; int j; struct asym *sym; uint_8 Ofssize; enum fixup_types fixup_type; DebugMsg1(("memory_operand(opndx.value=%X / sym=%s / memtype=%Xh, with_fixup=%u) enter, [CodeInfo->memtype=%Xh, Ofssize=%u, adrsiz=%u]\n", opndx->value, opndx->sym ? opndx->sym->name : "NULL", opndx->mem_type, with_fixup, CodeInfo->mem_type, CodeInfo->Ofssize, CodeInfo->prefix.adrsiz )); /* v211: use full 64-bit value */ //CodeInfo->opnd[CurrOpnd].data = opndx->value; CodeInfo->opnd[CurrOpnd].data64 = opndx->value64; CodeInfo->opnd[CurrOpnd].type = OP_M; sym = opndx->sym; segm_override( opndx, CodeInfo ); /* change pointer types ( MT_NEAR, MT_FAR, MT_PTR */ /* v2.04a: should not be called if OFFSET was used */ //if ( opndx->mem_type == MT_PTR ) /* this was before v2.04 */ //if ( ( opndx->mem_type & MT_SPECIAL_MASK ) == MT_ADDRESS ) /* v2.05: change reverted */ //if ( ( opndx->mem_type & MT_SPECIAL_MASK ) == MT_ADDRESS && opndx->instr == EMPTY ) if ( opndx->mem_type == MT_PTR ) SetPtrMemtype( CodeInfo, opndx ); else if ( ( opndx->mem_type & MT_SPECIAL_MASK ) == MT_ADDRESS ) { int size; if ( opndx->Ofssize == USE_EMPTY && sym ) opndx->Ofssize = GetSymOfssize( sym ); /* v2.09: use opndx->type ( for MT_PROC ) */ //size = SizeFromMemtype( opndx->mem_type, opndx->Ofssize, NULL ); size = SizeFromMemtype( opndx->mem_type, opndx->Ofssize, opndx->type ); MemtypeFromSize( size, &opndx->mem_type ); } Set_Memtype( CodeInfo, opndx->mem_type ); if( opndx->mbr != NULL ) { /* if the struct field is just another struct, use it's total size * to set CodeInfo->mem_type. */ //if ( opndx->mbr->mem_type == MT_TYPE ) { /* v2: don't overwrite opndx->mem_type, * testcase: cmp (dword ptr ), 0 */ if ( opndx->mbr->mem_type == MT_TYPE && opndx->mem_type == MT_EMPTY ) { enum memtype mem_type; DebugMsg1(("memory_operand: mbr %s has mem_type MT_TYPE, total_size=%u\n", opndx->mbr->name, opndx->mbr->total_size )); if ( MemtypeFromSize( opndx->mbr->total_size, &mem_type ) == NOT_ERROR ) Set_Memtype( CodeInfo, mem_type ); } //else /* v2: obsolete */ // Set_Memtype( CodeInfo, opndx->mbr->mem_type ); /* v2.06b: tell codegen that the member is a forward ref */ if ( opndx->mbr->state == SYM_UNDEFINED ) CodeInfo->undef_sym = TRUE; } /* instruction-specific handling */ switch ( CodeInfo->token ) { case T_JMP: case T_CALL: /* the 2 branch instructions are peculiar because they * will work with an unsized label. */ /* v1.95: convert MT_NEAR/MT_FAR and display error if no type. * For memory operands, expressions of type MT_NEAR/MT_FAR are * call [bx+] */ if ( CodeInfo->mem_type == MT_EMPTY ) { /* with -Zm, no size needed for indirect CALL/JMP */ if ( ModuleInfo.m510 == FALSE && ( Parse_Pass > PASS_1 && opndx->sym == NULL ) ) { DebugMsg1(("memory_operand, JMP/CALL: CodeInfo->memtype=empty, instruction operand must have size\n" )); return( EmitError( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ) ); } #if AMD64_SUPPORT opndx->mem_type = (CodeInfo->Ofssize == USE64) ? MT_QWORD : (CodeInfo->Ofssize == USE32) ? MT_DWORD : MT_WORD; #else opndx->mem_type = (CodeInfo->Ofssize == USE32) ? MT_DWORD : MT_WORD; #endif Set_Memtype( CodeInfo, opndx->mem_type ); DebugMsg1(("memory_operand, JMP/CALL: CodeInfo->memtype set to %Xh\n", CodeInfo->mem_type )); } j = SizeFromMemtype( CodeInfo->mem_type, CodeInfo->Ofssize, NULL ); if ( ( j == 1 || j > 6 ) #if AMD64_SUPPORT && ( CodeInfo->Ofssize != USE64 ) #endif ) { /* CALL/JMP possible for WORD/DWORD/FWORD memory operands only */ DebugMsg1(("memory_operand: invalid operand, size=%u\n", j )); return( EmitError( INVALID_OPERAND_SIZE ) ); } if( opndx->mem_type == MT_FAR || CodeInfo->mem_type == MT_FWORD || #if AMD64_SUPPORT ( CodeInfo->mem_type == MT_TBYTE && CodeInfo->Ofssize == USE64 ) || #endif ( CodeInfo->mem_type == MT_DWORD && (( CodeInfo->Ofssize == USE16 && opndx->Ofssize != USE32 ) || ( CodeInfo->Ofssize == USE32 && opndx->Ofssize == USE16 )))) { CodeInfo->isfar = TRUE; } DebugMsg1(("memory_operand: JMP/CALL, CodeInfo->far=%u\n", CodeInfo->isfar )); break; } if ( ( CodeInfo->mem_type & MT_SPECIAL) == 0 ) { switch ( CodeInfo->mem_type & MT_SIZE_MASK ) { /* size is encoded 0-based */ case 0: CodeInfo->opnd[CurrOpnd].type = OP_M08; break; case 1: CodeInfo->opnd[CurrOpnd].type = OP_M16; break; case 3: CodeInfo->opnd[CurrOpnd].type = OP_M32; break; case 5: CodeInfo->opnd[CurrOpnd].type = OP_M48; break; case 7: CodeInfo->opnd[CurrOpnd].type = OP_M64; break; case 9: CodeInfo->opnd[CurrOpnd].type = OP_M80; break; case 15: CodeInfo->opnd[CurrOpnd].type = OP_M128; break; #if AVXSUPP case 31: CodeInfo->opnd[CurrOpnd].type = OP_M256; break; #endif #ifdef DEBUG_OUT default: DebugMsg1(("memory_operand: unexpected mem_type=%X\n", CodeInfo->mem_type )); /**/myassert( 0 ); #endif } #if 0 /* v2.06: the wide flag isn't set for memory operands currently, */ if ( CodeInfo->opnd_type[CurrOpnd] & ( OP_M16 | OP_M32 | OP_M64 ) ) CodeInfo->iswide = 1; #endif } else if ( CodeInfo->mem_type == MT_EMPTY ) { /* v2.05: added */ switch ( CodeInfo->token ) { case T_INC: case T_DEC: /* jwasm v1.94-v2.04 accepted unsized operand for INC/DEC */ if ( opndx->sym == NULL ) { return( EmitError( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ) ); } break; case T_PUSH: case T_POP: if ( opndx->mem_type == MT_TYPE ) { return( EmitError( INVALID_INSTRUCTION_OPERANDS ) ); } break; } } base = ( opndx->base_reg ? opndx->base_reg->tokval : EMPTY ); index = ( opndx->idx_reg ? opndx->idx_reg->tokval : EMPTY ); /* use base + index from here - don't use opndx-> base_reg/idx_reg! */ #if 0 /* v2.10: moved to expreval.c */ if ( sym && sym->state == SYM_STACK ) { if( base != EMPTY ) { if( index != EMPTY ) { /* no free index register */ return( EmitError( MULTIPLE_INDEX_REGISTERS_NOT_ALLOWED ) ); } else { index = base; } } base = basereg[CodeInfo->Ofssize]; } #endif /* check for base registers */ if ( base != EMPTY ) { if ( ( ( GetValueSp( base ) & OP_R32) && CodeInfo->Ofssize == USE32 ) || #if AMD64_SUPPORT ( ( GetValueSp( base ) & OP_R64) && CodeInfo->Ofssize == USE64 ) || #endif ( ( GetValueSp( base ) & OP_R16) && CodeInfo->Ofssize == USE16 ) ) CodeInfo->prefix.adrsiz = FALSE; else { CodeInfo->prefix.adrsiz = TRUE; #if AMD64_SUPPORT /* 16bit addressing modes don't exist in long mode */ if ( ( GetValueSp( base ) & OP_R16) && CodeInfo->Ofssize == USE64 ) { return( EmitError( INVALID_ADDRESSING_MODE_WITH_CURRENT_CPU_SETTING ) ); } #endif } } /* check for index registers */ if( index != EMPTY ) { if ( ( ( GetValueSp( index ) & OP_R32) && CodeInfo->Ofssize == USE32 ) || #if AMD64_SUPPORT ( ( GetValueSp( index ) & OP_R64) && CodeInfo->Ofssize == USE64 ) || #endif ( ( GetValueSp( index ) & OP_R16) && CodeInfo->Ofssize == USE16 ) ) { CodeInfo->prefix.adrsiz = FALSE; } else { CodeInfo->prefix.adrsiz = TRUE; } /* v2.10: register swapping has been moved to expreval.c, index_connect(). * what has remained here is the check if R/ESP is used as index reg. */ if ( GetRegNo( index ) == 4 ) { /* [E|R]SP? */ DebugMsg(( "memory_operand: error, base regno=%u, index regno=%u, opnd.scale=%u\n", GetRegNo( base ), GetRegNo( index ), opndx->scale )); //int tmp = index; if( opndx->scale ) { /* no scale must be set */ EmitErr( CANNOT_BE_USED_AS_INDEX_REGISTER, GetResWName( index, NULL ) ); //return( ERROR ); } else { //if ( GetRegNo( base ) == 4 ) { EmitErr( MULTIPLE_BASE_REGISTERS_NOT_ALLOWED ); //return( ERROR ); } return( ERROR ); /* swap base and index */ //index = base; //base = tmp; #if 0 } else if ( Options.masm_compat_gencode && opndx->scale == 0 && GetRegNo( base ) != 4 ) { /* v2.08: Masm 6+ swaps base and index, even if -Zm is set (Masm 5.1 does NOT swap) */ int tmp = index; /* swap base and index */ index = base; base = tmp; #endif } /* 32/64 bit indirect addressing? */ if( ( CodeInfo->Ofssize == USE16 && CodeInfo->prefix.adrsiz == 1 ) || #if AMD64_SUPPORT CodeInfo->Ofssize == USE64 || #endif ( CodeInfo->Ofssize == USE32 && CodeInfo->prefix.adrsiz == 0 ) ) { if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_386 ) { /* scale, 0 or 1->00, 2->40, 4->80, 8->C0 */ switch( opndx->scale ) { case 0: case 1: break; /* ss = 00 */ case 2: ss = SCALE_FACTOR_2; break; /* ss = 01 */ case 4: ss = SCALE_FACTOR_4; break; /* ss = 10 */ case 8: ss = SCALE_FACTOR_8; break; /* ss = 11 */ default: /* must be * 1, 2, 4 or 8 */ return( EmitError( SCALE_FACTOR_MUST_BE_1_2_4_OR_8 ) ); } } else { /* 286 and down cannot use this memory mode */ return( EmitError( INVALID_ADDRESSING_MODE_WITH_CURRENT_CPU_SETTING ) ); } } else { /* v2.01: 16-bit addressing mode. No scale possible */ if ( opndx->scale ) { return( EmitError( INVALID_USE_OF_REGISTER ) ); } } } if( with_fixup ) { if( opndx->is_abs ) { Ofssize = IS_ADDR32( CodeInfo ); } else if ( sym ) { Ofssize = GetSymOfssize( sym ); } else if ( SegOverride ) { Ofssize = GetSymOfssize( SegOverride ); } else Ofssize = CodeInfo->Ofssize; /* now set fixup_type. * for direct addressing, the fixup type can easily be set by * the symbol's offset size. */ if( base == EMPTY && index == EMPTY ) { CodeInfo->prefix.adrsiz = ADDRSIZE( CodeInfo->Ofssize, Ofssize ); #if AMD64_SUPPORT if ( Ofssize == USE64 ) /* v2.03: override with a segment assumed != FLAT? */ if ( opndx->override != NULL && SegOverride != &ModuleInfo.flat_grp->sym ) fixup_type = FIX_OFF32; else fixup_type = FIX_RELOFF32; else #endif fixup_type = ( Ofssize ) ? FIX_OFF32 : FIX_OFF16; DebugMsg1(( "memory_operand: direct addressing, fixup type=%u\n", fixup_type )); } else { DebugMsg1(( "memory_operand: CodeInfo->Ofssize=%u/prefix.adrsize=%u, Ofssize=%u\n", CodeInfo->Ofssize, CodeInfo->prefix.adrsiz, Ofssize )); #if AMD64_SUPPORT if( Ofssize == USE64 ) { fixup_type = FIX_OFF32; } else #endif if( IS_ADDR32( CodeInfo ) ) { /* address prefix needed? */ /* changed for v1.95. Probably more tests needed! * test case: * mov eax,[ebx*2-10+offset var] ;code and var are 16bit! * the old code usually works fine because HiWord of the * symbol's offset is zero. However, if there's an additional * displacement which makes the value stored at the location * < 0, then the target's HiWord becomes <> 0. */ //fixup_type = ( Ofssize ) ? FIX_OFF32 : FIX_OFF16; fixup_type = FIX_OFF32; } else { fixup_type = FIX_OFF16; if( Ofssize && Parse_Pass == PASS_2 ) { /* address size is 16bit but label is 32-bit. * example: use a 16bit register as base in FLAT model: * test buff[di],cl */ EmitWarn( 2, WORD_FIXUP_FOR_32BIT_LABEL, sym->name ); } } } #if IMAGERELSUPP || SECTIONRELSUPP /* v2.10: added; IMAGEREL/SECTIONREL for indirect memory operands */ if ( fixup_type == FIX_OFF32 ) if ( opndx->instr == T_IMAGEREL ) fixup_type = FIX_OFF32_IMGREL; else if ( opndx->instr == T_SECTIONREL ) fixup_type = FIX_OFF32_SECREL; #endif /* no fixups are needed for memory operands of string instructions and XLAT/XLATB. * However, CMPSD and MOVSD are also SSE2 opcodes, so the fixups must be generated * anyways. */ if ( CodeInfo->token != T_XLAT && CodeInfo->token != T_XLATB ) { //DebugMsg1(("memory_operand: calling CreateFixup(%s, fixup=%u) [CodeInfo->memtype=%Xh]\n", sym ? sym->name : "NULL", fixup_type, CodeInfo->mem_type)); CodeInfo->opnd[CurrOpnd].InsFixup = CreateFixup( sym, fixup_type, OPTJ_NONE ); } } if( set_rm_sib( CodeInfo, CurrOpnd, ss, index, base, sym ) == ERROR ) { return( ERROR ); } /* set frame type/data in fixup if one was created */ if ( CodeInfo->opnd[CurrOpnd].InsFixup ) { CodeInfo->opnd[CurrOpnd].InsFixup->frame_type = Frame_Type; CodeInfo->opnd[CurrOpnd].InsFixup->frame_datum = Frame_Datum; } DebugMsg1(("memory_operand exit, ok, opndx.type/value=%Xh/%Xh, CodeInfo.memtype/rmbyte=%X/%X opndtype=%Xh fix=%Xh\n", opndx->type, opndx->value, CodeInfo->mem_type, CodeInfo->rm_byte, CodeInfo->opnd[CurrOpnd].type, CodeInfo->opnd[CurrOpnd].InsFixup )); return( NOT_ERROR ); } static ret_code process_address( struct code_info *CodeInfo, unsigned CurrOpnd, struct expr *opndx ) /**************************************************************************************************/ /* * parse the memory reference operand * CurrOpnd is 0 for first operand, 1 for second, ... * valid return values: NOT_ERROR, ERROR */ { if( opndx->indirect ) { /* indirect register operand or stack var */ DebugMsg1(("process_address: INDIRECT, sym=%s mbr=%s ci.adrsiz=%u\n", opndx->sym ? opndx->sym->name : "NULL", opndx->mbr ? opndx->mbr->name : "NULL", CodeInfo->prefix.adrsiz )); /* if displacement doesn't fit in 32-bits: * Masm (both ML and ML64) just truncates. * JWasm throws an error in 64bit mode and * warns (level 3) in the other modes. * todo: this check should also be done for direct addressing! */ if ( opndx->hvalue && ( opndx->hvalue != -1 || opndx->value >= 0 ) ) { DebugMsg1(("process_address: displacement doesn't fit in 32 bits: %" I64_SPEC "X\n", opndx->value64 )); #if AMD64_SUPPORT if ( ModuleInfo.Ofssize == USE64 ) { return( EmitConstError( opndx ) ); } #endif EmitWarn( 3, DISPLACEMENT_OUT_OF_RANGE, opndx->value64 ); } if( opndx->sym == NULL || opndx->sym->state == SYM_STACK ) { return( memory_operand( CodeInfo, CurrOpnd, opndx, FALSE ) ); } /* do default processing */ } else if( opndx->instr != EMPTY ) { /* instr is OFFSET | LROFFSET | SEG | LOW | LOWWORD, ... */ DebugMsg1(("process_address: instr=%s\n", GetResWName( opndx->instr, NULL ) )); if( opndx->sym == NULL ) { /* better to check opndx->type? */ return( idata_nofixup( CodeInfo, CurrOpnd, opndx ) ); } else { /* allow "lea , [offset ]" */ if( CodeInfo->token == T_LEA && opndx->instr == T_OFFSET ) return( memory_operand( CodeInfo, CurrOpnd, opndx, TRUE ) ); return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } } else if( opndx->sym == NULL ) { /* direct operand without symbol */ DebugMsg1(("process_address: symbol=NULL\n" )); if( opndx->override != NULL ) { /* direct absolute memory without symbol. DS:[0] won't create a fixup, but DGROUP:[0] will create one! */ #if AMD64_SUPPORT /* for 64bit, always create a fixup, since RIP-relative addressing is used * v2.11: don't create fixup in 64-bit. */ //if ( opndx->override->token == T_REG && CodeInfo->Ofssize != USE64 ) if ( opndx->override->token == T_REG || CodeInfo->Ofssize == USE64 ) #else if ( opndx->override->token == T_REG ) #endif return( memory_operand( CodeInfo, CurrOpnd, opndx, FALSE ) ); else return( memory_operand( CodeInfo, CurrOpnd, opndx, TRUE ) ); } else { return( idata_nofixup( CodeInfo, CurrOpnd, opndx ) ); } } else if( ( opndx->sym->state == SYM_UNDEFINED ) && !opndx->explicit ) { DebugMsg1(("process_address: sym=SYM_UNDEFINED, name=%s, state=%X\n", opndx->sym->name, opndx->sym->state )); /* v2.04: unnecessary, the expression evaluator will have emitted an error already */ //if( Parse_Pass != PASS_1 ) { // EmitErr( SYMBOL_NOT_DEFINED, opndx->sym->name ); // return( ERROR ); //} /* undefined symbol, it's not possible to determine * operand type and size currently. However, for backpatching * a fixup should be created. */ /* assume a code label for branch instructions! */ if( IS_ANY_BRANCH( CodeInfo->token ) ) return( process_branch( CodeInfo, CurrOpnd, opndx ) ); switch( CodeInfo->token ) { case T_PUSH: case T_PUSHW: case T_PUSHD: /* v2.0: don't assume immediate operand if cpu is 8086 */ if ( ( ModuleInfo.curr_cpu & P_CPU_MASK ) > P_86 ) { //return( idata_nofixup( CodeInfo, opndx ) ); /* v1.96: changed */ return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } break; default: /* v2.04: if operand is the second argument (and the first is NOT * a segment register!), scan the * instruction table if the instruction allows an immediate! * If so, assume the undefined symbol is a constant. */ if ( CurrOpnd == OPND2 && (( CodeInfo->opnd[OPND1].type & OP_SR ) == 0 ) ) { const struct instr_item *p = CodeInfo->pinstr; do { if ( opnd_clstab[p->opclsidx].opnd_type[OPND2] & OP_I ) { return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } p++; } while ( p->first == FALSE ); } /* v2.10: if current operand is the third argument, always assume an immediate */ if ( CurrOpnd == OPND3 ) return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } /* do default processing */ } else if( ( opndx->sym->state == SYM_SEG ) || ( opndx->sym->state == SYM_GRP ) ) { DebugMsg1(("process_address: sym->state=SEG/GROUP\n")); /* SEGMENT and GROUP symbol is converted to SEG symbol * for next processing */ opndx->instr = T_SEG; return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } else { DebugMsg1(("process_address direct, sym=%s sym.memtype=%X opndx.memtype=%X\n", opndx->sym->name, opndx->sym->mem_type, opndx->mem_type )); /* symbol external, but absolute? */ if( opndx->is_abs ) { return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } /* CODE location is converted to OFFSET symbol */ if ( opndx->mem_type == MT_NEAR || opndx->mem_type == MT_FAR ) { if( CodeInfo->token == T_LEA ) { return( memory_operand( CodeInfo, CurrOpnd, opndx, TRUE ) ); //} else if( opndx->sym == &symPC ) { // return( idata_fixup( CodeInfo, opndx ) ); } else if( opndx->mbr != NULL ) { /* structure field? */ return( memory_operand( CodeInfo, CurrOpnd, opndx, TRUE ) ); } else { return( idata_fixup( CodeInfo, CurrOpnd, opndx ) ); } } } /* default processing: memory with fixup */ return( memory_operand( CodeInfo, CurrOpnd, opndx, TRUE ) ); } /* Handle constant operands. * These never need a fixup. Externals - even "absolute" ones - * are always labeled as EXPR_ADDR by the expression evaluator. */ static ret_code process_const( struct code_info *CodeInfo, unsigned CurrOpnd, struct expr *opndx ) /************************************************************************************************/ { #if 0 /* v2.06: obsolete */ /* hack for IMUL: compress the operands so there are 2 only */ if( ( CodeInfo->token == T_IMUL ) && ( CodeInfo->opnd_type[OPND1] & OP_R ) ) { if( CurrOpnd == OPND2 ) { #if AMD64_SUPPORT CodeInfo->prefix.rex |= ((CodeInfo->prefix.rex & REX_B) ? REX_R : 0); #endif CodeInfo->rm_byte = ( CodeInfo->rm_byte & ~BIT_345 ) | ( ( CodeInfo->rm_byte & BIT_012 ) << 3 ); } else if( CurrOpnd == OPND3 ) { /* v2.04b: if op2 was assumed an immediate due to fwd ref, * change it back to a mem ref now. */ if ( ( CodeInfo->opnd_type[OPND2] & OP_I ) && CodeInfo->InsFixup[OPND2] && CodeInfo->InsFixup[OPND2]->sym->state == SYM_UNDEFINED ) CodeInfo->opnd_type[OPND2] = OP_M; CodeInfo->opnd_type[OPND1] = CodeInfo->opnd_type[OPND2]; CodeInfo->opnd_type[OPND2] = OP_NONE; CodeInfo->data[OPND1] = CodeInfo->data[OPND2]; CodeInfo->data[OPND2] = 0; CodeInfo->InsFixup[OPND1] = CodeInfo->InsFixup[OPND2]; CodeInfo->InsFixup[OPND2] = NULL; CurrOpnd = OPND2; } } #endif /* v2.11: don't accept an empty string */ if ( opndx->quoted_string && opndx->quoted_string->stringlen == 0 ) return( EmitError( EMPTY_STRING ) ); /* optimization: skip if it is 0 and instruction * is RET[W|D|N|F]. */ /* v2.06: moved here and checked the opcode directly, so * RETD and RETW are also handled. */ if ( ( ( CodeInfo->pinstr->opcode & 0xf7 ) == 0xc2 ) && CurrOpnd == OPND1 && opndx->value == 0 ) { // (CodeInfo.token == T_RET || // CodeInfo.token == T_RETN || // CodeInfo.token == T_RETF)) { //if ( opndx.sym == NULL || opndx.sym->state == SYM_INTERNAL ) { return( NOT_ERROR ); } return( idata_nofixup( CodeInfo, CurrOpnd, opndx ) ); } static ret_code process_register( struct code_info *CodeInfo, unsigned CurrOpnd, const struct expr opndx[] ) /**********************************************************************************************************/ /* * parse and encode direct register operands. Modifies: * - CodeInfo->opnd_type * - CodeInfo->rm_byte (depending on CurrOpnd) * - CodeInfo->iswide * - CodeInfo->x86hi_used/x64lo_used * - CodeInfo->prefix.rex */ { enum special_token regtok; int regno; uint_32 flags; DebugMsg1(( "process_register enter (%s)\n", opndx[CurrOpnd].base_reg->string_ptr )); regtok = opndx[CurrOpnd].base_reg->tokval; regno = GetRegNo( regtok ); /* the register's "OP-flags" are stored in the 'value' field */ flags = GetValueSp( regtok ); CodeInfo->opnd[CurrOpnd].type = flags; if ( flags & OP_R8 ) { /* it's probably better to not reset the wide bit at all */ if ( flags != OP_CL ) /* problem: SHL AX|AL, CL */ CodeInfo->iswide = 0; #if AMD64_SUPPORT if ( CodeInfo->Ofssize == USE64 && regno >=4 && regno <=7 ) if ( SpecialTable[regtok].cpu == P_86 ) CodeInfo->x86hi_used = 1; /* it's AH,BH,CH,DH */ else CodeInfo->x64lo_used = 1; /* it's SPL,BPL,SIL,DIL */ #endif if ( StdAssumeTable[regno].error & (( regtok >= T_AH && regtok <= T_BH ) ? RH_ERROR : RL_ERROR ) ) { DebugMsg(("process_register: assume error, reg=%u\n", regno )); return( EmitError( USE_OF_REGISTER_ASSUMED_TO_ERROR ) ); } } else if ( flags & OP_R ) { /* 16-, 32- or 64-bit GPR? */ CodeInfo->iswide = 1; if ( StdAssumeTable[regno].error & flags & OP_R ) { DebugMsg(("process_register: assume error, reg=%u\n", regno )); return( EmitError( USE_OF_REGISTER_ASSUMED_TO_ERROR ) ); } if ( flags & OP_R16 ) { if ( CodeInfo->Ofssize > USE16 ) CodeInfo->prefix.opsiz = TRUE; } else { if( CodeInfo->Ofssize == USE16 ) CodeInfo->prefix.opsiz = TRUE; } } else if ( flags & OP_SR ) { if( regno == 1 ) { /* 1 is CS */ /* POP CS is not allowed */ if( CodeInfo->token == T_POP ) { return( EmitError( POP_CS_IS_NOT_ALLOWED ) ); } } } else if ( flags & OP_ST ) { regno = opndx[CurrOpnd].st_idx; if ( regno > 7 ) { /* v1.96: index check added */ return( EmitError( INVALID_COPROCESSOR_REGISTER ) ); } CodeInfo->rm_byte |= regno; if( regno != 0 ) CodeInfo->opnd[CurrOpnd].type = OP_ST_REG; /* v2.06: exit, rm_byte is already set. */ return( NOT_ERROR ); } else if ( flags & OP_RSPEC ) { /* CRx, DRx, TRx */ if( CodeInfo->token != T_MOV ) { return( EmitError( ONLY_MOV_CAN_USE_SPECIAL_REGISTER ) ); } /* v2.04: previously there were 3 flags, OP_CR, OP_DR and OP_TR. * this was summoned to one flag OP_RSPEC to free 2 flags, which * are needed if AVC ( new YMM registers ) is to be supported. * To distinguish between CR, DR and TR, the register number is * used now: CRx are numbers 0-F, DRx are numbers 0x10-0x1F and * TRx are 0x20-0x2F. */ if ( regno >= 0x20 ) { /* TRx? */ CodeInfo->opc_or |= 0x04; /* TR3-TR5 are available on 486-586 * TR6+TR7 are available on 386-586 * v2.11: simplified. */ if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) >= P_686 ) { return( EmitErr( CANNOT_USE_TRN_TO_TRM_WITH_CURRENT_CPU_SETTING, regno > 0x25 ? 6 : 3, regno > 0x25 ? 7 : 5 ) ); } } else if ( regno >= 0x10 ) { /* DRx? */ CodeInfo->opc_or |= 0x01; } regno &= 0x0F; } #if AMD64_SUPPORT /* if it's a x86-64 register (SIL, R8W, R8D, RSI, ... */ if ( ( SpecialTable[regtok].cpu & P_CPU_MASK ) == P_64 ) { CodeInfo->prefix.rex |= 0x40; if ( flags & OP_R64 ) CodeInfo->prefix.rex |= REX_W; } #endif if( CurrOpnd == OPND1 ) { /* the first operand * r/m is treated as a 'reg' field */ CodeInfo->rm_byte |= MOD_11; #if AMD64_SUPPORT CodeInfo->prefix.rex |= (regno & 8 ) >> 3; /* set REX_B */ regno &= BIT_012; #endif /* fill the r/m field */ CodeInfo->rm_byte |= regno; } else { /* the second operand * XCHG can use short form if op1 is AX/EAX/RAX */ if( ( CodeInfo->token == T_XCHG ) && ( CodeInfo->opnd[OPND1].type & OP_A ) && ( 0 == (CodeInfo->opnd[OPND1].type & OP_R8 ) ) ) { #if AMD64_SUPPORT CodeInfo->prefix.rex |= (regno & 8 ) >> 3; /* set REX_B */ regno &= BIT_012; #endif CodeInfo->rm_byte = ( CodeInfo->rm_byte & BIT_67 ) | regno; } else { /* fill reg field with reg */ #if AMD64_SUPPORT CodeInfo->prefix.rex |= (regno & 8 ) >> 1; /* set REX_R */ regno &= BIT_012; #endif CodeInfo->rm_byte = ( CodeInfo->rm_byte & ~BIT_345 ) | ( regno << 3 ); } } return( NOT_ERROR ); } /* special handling for string instructions * CMPS[B|W|D|Q] * INS[B|W|D] * LODS[B|W|D|Q] * MOVS[B|W|D|Q] * OUTS[B|W|D] * SCAS[B|W|D|Q] * STOS[B|W|D|Q] * the peculiarity is that these instructions ( optionally ) * have memory operands, which aren't used for code generation * contains the last operand. */ static void HandleStringInstructions( struct code_info *CodeInfo, const struct expr opndx[] ) /*******************************************************************************************/ { int opndidx = OPND1; int op_size; switch( CodeInfo->token ) { #if AVXSUPP case T_VCMPSD: #endif case T_CMPSD: /* filter SSE2 opcode CMPSD */ if ( CodeInfo->opnd[OPND1].type & (OP_XMM | OP_MMX)) { /* v2.01: QWORD operand for CMPSD/MOVSD may have set REX_W! */ #if AMD64_SUPPORT CodeInfo->prefix.rex &= ~REX_W; #endif return; } /* fall through */ case T_CMPS: case T_CMPSB: case T_CMPSW: #if AMD64_SUPPORT case T_CMPSQ: #endif /* cmps allows prefix for the first operand (=source) only */ if ( CodeInfo->prefix.RegOverride != EMPTY ) { if ( opndx[OPND2].override != NULL ) { if ( CodeInfo->prefix.RegOverride == ASSUME_ES ) { /* content of LastRegOverride is valid if * CodeInfo->RegOverride is != EMPTY. */ if ( LastRegOverride == ASSUME_DS ) CodeInfo->prefix.RegOverride = EMPTY; else CodeInfo->prefix.RegOverride = LastRegOverride; } else { DebugMsg1(("HandleStringInstructions: CMPS: CodeInfo->RegOverride=%X, opndx->override=%s\n", CodeInfo->prefix.RegOverride, opndx[OPND2].override->string_ptr )); EmitError( INVALID_INSTRUCTION_OPERANDS ); } } else if ( CodeInfo->prefix.RegOverride == ASSUME_DS ) { /* prefix for first operand? */ CodeInfo->prefix.RegOverride = EMPTY; } } break; #if AVXSUPP case T_VMOVSD: #endif case T_MOVSD: /* filter SSE2 opcode MOVSD */ if ( ( CodeInfo->opnd[OPND1].type & (OP_XMM | OP_MMX) ) || ( CodeInfo->opnd[OPND2].type & (OP_XMM | OP_MMX) ) ) { /* v2.01: QWORD operand for CMPSD/MOVSD may have set REX_W! */ #if AMD64_SUPPORT CodeInfo->prefix.rex &= ~REX_W; #endif return; } /* fall through */ case T_MOVS: case T_MOVSB: case T_MOVSW: #if AMD64_SUPPORT case T_MOVSQ: #endif /* movs allows prefix for the second operand (=source) only */ if ( CodeInfo->prefix.RegOverride != EMPTY ) if ( opndx[OPND2].override == NULL ) EmitError( INVALID_INSTRUCTION_OPERANDS ); else if ( CodeInfo->prefix.RegOverride == ASSUME_DS ) CodeInfo->prefix.RegOverride = EMPTY; break; case T_OUTS: case T_OUTSB: case T_OUTSW: case T_OUTSD: /* v2.01: remove default DS prefix */ if ( CodeInfo->prefix.RegOverride == ASSUME_DS ) CodeInfo->prefix.RegOverride = EMPTY; opndidx = OPND2; break; case T_LODS: case T_LODSB: case T_LODSW: case T_LODSD: #if AMD64_SUPPORT case T_LODSQ: #endif /* v2.10: remove unnecessary DS prefix ( Masm-compatible ) */ if ( CodeInfo->prefix.RegOverride == ASSUME_DS ) CodeInfo->prefix.RegOverride = EMPTY; break; default: /*INS[B|W|D], SCAS[B|W|D|Q], STOS[B|W|D|Q] */ /* INSx, SCASx and STOSx don't allow any segment prefix != ES for the memory operand. */ if ( CodeInfo->prefix.RegOverride != EMPTY ) if ( CodeInfo->prefix.RegOverride == ASSUME_ES ) CodeInfo->prefix.RegOverride = EMPTY; else EmitError( INVALID_INSTRUCTION_OPERANDS ); } if ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[opndidx] == OP_NONE ) { CodeInfo->iswide = 0; CodeInfo->prefix.opsiz = FALSE; } /* if the instruction is the variant without suffix (MOVS, LODS, ..), * then use the operand's size to get further info. */ //if ( CodeInfo->pinstr->opnd_type[opndidx] != OP_NONE && if ( opnd_clstab[CodeInfo->pinstr->opclsidx].opnd_type[opndidx] != OP_NONE && CodeInfo->opnd[opndidx].type != OP_NONE ) { op_size = OperandSize( CodeInfo->opnd[opndidx].type, CodeInfo ); /* v2.06: added. if memory operand has no size */ if ( op_size == 0 ) { if ( CodeInfo->opnd[opndidx].InsFixup == NULL || CodeInfo->opnd[opndidx].InsFixup->sym->state != SYM_UNDEFINED ) EmitError( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ); op_size = 1; /* assume shortest format */ } switch( op_size ) { case 1: CodeInfo->iswide = 0; //if( CodeInfo->Ofssize ) CodeInfo->prefix.opsiz = FALSE; break; case 2: CodeInfo->iswide = 1; CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? TRUE : FALSE; break; case 4: CodeInfo->iswide = 1; CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? FALSE : TRUE; break; #if AMD64_SUPPORT case 8: if ( CodeInfo->Ofssize == USE64 ) { CodeInfo->iswide = 1; CodeInfo->prefix.opsiz = FALSE; CodeInfo->prefix.rex = REX_W; } break; #endif } } return; } static ret_code check_size( struct code_info *CodeInfo, const struct expr opndx[] ) /*********************************************************************************/ /* * - use to make sure the size of first operand match the size of second operand; * - optimize MOV instruction; * - opndx contains last operand * todo: BOUND second operand check ( may be WORD/DWORD or DWORD/QWORD ). * tofix: add a flag in instr_table[] if there's NO check to be done. */ { enum operand_type op1 = CodeInfo->opnd[OPND1].type; enum operand_type op2 = CodeInfo->opnd[OPND2].type; ret_code rc = NOT_ERROR; int op1_size; int op2_size; //int op_size = 0; DebugMsg1(("check_size enter, optype1=%" I32_SPEC "X, optype2=%" I32_SPEC "X\n", op1, op2 )); switch( CodeInfo->token ) { case T_IN: if( op2 == OP_DX ) { /* wide and size is NOT determined by DX, but * by the first operand, AL|AX|EAX */ switch( op1 ) { case OP_AX: break; case OP_AL: CodeInfo->iswide = 0; /* clear w-bit */ case OP_EAX: if( CodeInfo->Ofssize ) { CodeInfo->prefix.opsiz = FALSE; } break; } } break; case T_OUT: if( op1 == OP_DX ) { switch( op2 ) { case OP_AX: break; case OP_AL: CodeInfo->iswide = 0; /* clear w-bit */ case OP_EAX: if( CodeInfo->Ofssize ) { CodeInfo->prefix.opsiz = FALSE; } } } break; case T_LEA: #if 0 /* first op must be 16/32 register, but this condition is checked in CodeGen. operands 1 and 2 can be mixed: lea cx,[bp] lea cx,[ebp] lea ecx,[bp] lea ecx,[ebp] are all valid. However, Masm sometimes complains "cannot use 16-bit register with a 32-bit address" */ switch( OperandSize( op1, CodeInfo ) ) { case 2: case 4: break; default: EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, OperandSize( op1, CodeInfo ), ModuleInfo.Ofssize ? 4 : 2); rc = ERROR; } #endif break; case T_RCL: case T_RCR: case T_ROL: case T_ROR: case T_SAL: case T_SAR: case T_SHL: case T_SHR: /* v2.11: added */ if ( CodeInfo->opnd[OPND1].type == OP_M && CodeInfo->undef_sym == FALSE && ( opndx[OPND1].sym == NULL || opndx[OPND1].sym->state != SYM_UNDEFINED ) ) { EmitErr( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ); rc = ERROR; break; } //if ( CodeInfo->opnd[OPND1].type == OP_M && Parse_Pass == PASS_2 ) // EmitWarn( 2, SIZE_NOT_SPECIFIED_ASSUMING, "BYTE" ); /* v2.0: if second argument is a forward reference, * change type to "immediate 1" */ if ( opndx[OPND2].kind == EXPR_ADDR && Parse_Pass == PASS_1 && opndx[OPND2].indirect == FALSE && opndx[OPND2].sym && opndx[OPND2].sym->state == SYM_UNDEFINED ) { CodeInfo->opnd[OPND2].type = OP_I8; CodeInfo->opnd[OPND2].data32l = 1; } /* v2.06: added (because if first operand is memory, wide bit * isn't set!) */ if ( OperandSize( op1, CodeInfo ) > 1 ) CodeInfo->iswide = 1; /* v2.06: makes the OP_CL_ONLY case in codegen.c obsolete */ if ( op2 == OP_CL ) { /* CL is encoded in bit 345 of rm_byte, but we don't need it * so clear it here */ CodeInfo->rm_byte &= NOT_BIT_345; } break; case T_LDS: case T_LES: case T_LFS: case T_LGS: case T_LSS: op1_size = OperandSize( op1, CodeInfo ) + 2; /* add 2 for the impl. segment register */ op2_size = OperandSize( op2, CodeInfo ); if ( op2_size != 0 && op1_size != op2_size ) { return( EmitError( INVALID_OPERAND_SIZE ) ); } break; case T_ENTER: #if 0 /* v2.11: operand sizes are checked in codegen */ /* ENTER has to be OP_I16, OP_I8_U */ if( op1 == OP_I32 ) { /* parse_phase_1 will treat 16-bit data as OP_I32 if CPU is 386 */ if( CodeInfo->opnd[OPND1].data32l > (int_32)USHRT_MAX ) { /* if op1 is really 32-bit data, then error */ EmitError( INVALID_OPERAND_SIZE ); rc = ERROR; } } /* type cast op1 to OP_I16 */ CodeInfo->opnd[OPND1].type = OP_I16; /* op2 have to be 8-bit data */ if( op2 >= OP_I16 ) { if( CodeInfo->opnd[OPND2].data32l > UCHAR_MAX ) { EmitError( INVALID_OPERAND_SIZE ); rc = ERROR; } CodeInfo->opnd[OPND2].type = OP_I8; } #endif break; case T_MOVSX: case T_MOVZX: CodeInfo->iswide = 0; op1_size = OperandSize( op1, CodeInfo ); op2_size = OperandSize( op2, CodeInfo ); DebugMsg1(("check_size, MOVZX/MOVSX: op2_size=%u, opndx.memtype=%Xh, opndx.sym=%X\n", op2_size, opndx[OPND2].mem_type, opndx[OPND2].sym )); if ( op2_size == 0 && Parse_Pass == PASS_2 ) if ( op1_size == 2 ) { EmitWarn( 2, SIZE_NOT_SPECIFIED_ASSUMING, "BYTE" ); } else EmitErr( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ); switch( op1_size ) { #if AMD64_SUPPORT case 8: //if ( CodeInfo->Ofssize == USE64 ) // break; #endif case 4: if (op2_size < 2) ; else if (op2_size == 2) CodeInfo->iswide = 1; else { EmitError( OP2_TOO_BIG ); rc = ERROR; } CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? FALSE : TRUE; break; case 2: if( op2_size >= 2 ) { EmitError( OP2_TOO_BIG ); rc = ERROR; } CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? TRUE : FALSE; break; default: /* op1 must be r16/r32/r64 */ EmitError( OP1_TOO_SMALL ); rc = ERROR; } break; #if AMD64_SUPPORT case T_MOVSXD: break; #endif case T_ARPL: /* v2.06: new, avoids the OP_R16 hack in codegen.c */ CodeInfo->prefix.opsiz = 0; goto def_check; break; #if AMD64_SUPPORT case T_LAR: /* v2.04: added */ case T_LSL: /* 19-sep-93 */ #if 1 /* v2.04: changed */ if ( ModuleInfo.Ofssize != USE64 || ( ( op2 & OP_M ) == 0 ) ) goto def_check; /* in 64-bit, if second argument is memory operand, * ensure it has size WORD ( or 0 if a forward ref ) */ op2_size = OperandSize( op2, CodeInfo ); if ( op2_size != 2 && op2_size != 0 ) { return( EmitError( INVALID_OPERAND_SIZE ) ); } /* the opsize prefix depends on the FIRST operand only! */ op1_size = OperandSize( op1, CodeInfo ); if ( op1_size != 2 ) CodeInfo->prefix.opsiz = FALSE; #else op1_size = OperandSize( op1, CodeInfo ); switch( op1_size ) { case 2: if( CodeInfo->Ofssize ) CodeInfo->prefix.opsiz = TRUE; break; case 4: if( CodeInfo->Ofssize ) CodeInfo->prefix.opsiz = FALSE; break; default: return( EmitError( INVALID_OPERAND_SIZE ) ); } op2_size = OperandSize( op2, CodeInfo ); switch( op2_size ) { case 2: case 4: break; default: EmitError( INVALID_OPERAND_SIZE ); rc = ERROR; break; } #endif break; #endif case T_IMUL: /* v2.06: check for 3rd operand must be done here */ if ( CodeInfo->opnd[OPND3].type != OP_NONE ) { int op3_size; op1_size = OperandSize( op1, CodeInfo ); op3_size = OperandSize( CodeInfo->opnd[OPND3].type, CodeInfo ); /* the only case which must be checked here * is a WORD register as op1 and a DWORD immediate as op3 */ if ( op1_size == 2 && op3_size > 2 ) { EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, op1_size, op3_size ); rc = ERROR; break; } if ( CodeInfo->opnd[OPND3].type & ( OP_I16 | OP_I32 ) ) CodeInfo->opnd[OPND3].type = ( op1_size == 2 ? OP_I16 : OP_I32 ); } goto def_check; break; case T_CVTSD2SI: case T_CVTTSD2SI: case T_CVTSS2SI: case T_CVTTSS2SI: //case T_MOVNTI: /* v2.05: removed */ #if AVXSUPP case T_VBROADCASTSD: case T_VBROADCASTF128: case T_VEXTRACTF128: case T_VINSERTF128: case T_VCVTSD2SI: case T_VCVTTSD2SI: case T_VCVTSS2SI: case T_VCVTTSS2SI: #endif #if VMXSUPP /* v2.09: added */ case T_INVEPT: case T_INVVPID: #endif #if SVMSUPP /* v2.09: added */ case T_INVLPGA: #endif break; #if AVXSUPP case T_VCVTPD2DQ: case T_VCVTTPD2DQ: case T_VCVTPD2PS: if ( op2 == OP_M && opndx[OPND2].indirect ) { return( EmitError( INSTRUCTION_OPERAND_MUST_HAVE_SIZE ) ); } break; case T_VMOVDDUP: if ( !( op1 & OP_YMM ) ) break; /* fall through */ case T_VPERM2F128: /* has just one memory variant, and VX_L isnt set */ if ( op2 == OP_M ) CodeInfo->opnd[OPND2].type |= OP_M256; break; #endif #if SSE4SUPP case T_CRC32: /* v2.02: for CRC32, the second operand determines whether an * OPSIZE prefix byte is to be written. */ op2_size = OperandSize( op2, CodeInfo ); if ( op2_size < 2) CodeInfo->prefix.opsiz = FALSE; else if ( op2_size == 2 ) CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? TRUE : FALSE; else CodeInfo->prefix.opsiz = CodeInfo->Ofssize ? FALSE : TRUE; break; #endif case T_MOVD: #if 0 op1_size = OperandSize( op1, CodeInfo ); op2_size = OperandSize( op2, CodeInfo ); if( ( op1_size != 0 ) && ( op1_size != 4 ) || ( op2_size != 0 ) && ( op2_size != 4 ) ) { EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, op1_size, op2_size ); rc = ERROR; } #endif break; case T_MOV: if( op1 & OP_SR ) { /* segment register as op1? */ op2_size = OperandSize( op2, CodeInfo ); if( ( op2_size == 2 ) || ( op2_size == 4 ) #if AMD64_SUPPORT || ( op2_size == 8 && ModuleInfo.Ofssize == USE64 ) #endif ) { return( NOT_ERROR ); } } else if( op2 & OP_SR ) { op1_size = OperandSize( op1, CodeInfo ); if( ( op1_size == 2 ) || ( op1_size == 4 ) #if AMD64_SUPPORT || ( op1_size == 8 && ModuleInfo.Ofssize == USE64 ) #endif ) { return( NOT_ERROR ); } } else if( ( op1 & OP_M ) && ( op2 & OP_A ) ) { /* 1. operand memory reference, 2. AL|AX|EAX|RAX? */ if ( CodeInfo->isdirect == FALSE ) { /* address mode is indirect. * don't use the short format (opcodes A0-A3) - it exists for direct * addressing only. Reset OP_A flag! */ CodeInfo->opnd[OPND2].type &= ~OP_A; DebugMsg1(("check_size: OP_A flag reset, new op2=%X\n", CodeInfo->opnd[OPND2].type )); #if AMD64_SUPPORT } else if ( CodeInfo->Ofssize == USE64 && ( CodeInfo->opnd[OPND1].data64 < 0x80000000 || CodeInfo->opnd[OPND1].data64 >= 0xffffffff80000000 ) ) { /* for 64bit, opcodes A0-A3 ( direct memory addressing with AL/AX/EAX/RAX ) * are followed by a full 64-bit moffs. This is only used if the offset won't fit * in a 32-bit signed value. */ CodeInfo->opnd[OPND2].type &= ~OP_A; DebugMsg1(("check_size: OP_A flag reset, new op2=%X\n", CodeInfo->opnd[OPND2].type )); #endif } } else if( ( op1 & OP_A ) && ( op2 & OP_M ) ) { /* 2. operand memory reference, 1. AL|AX|EAX|RAX? */ if ( CodeInfo->isdirect == FALSE ) { CodeInfo->opnd[OPND1].type &= ~OP_A; DebugMsg1(("check_size: OP_A flag reset, new op1=%X\n", CodeInfo->opnd[OPND1].type )); #if AMD64_SUPPORT } else if ( CodeInfo->Ofssize == USE64 && ( CodeInfo->opnd[OPND2].data64 < 0x80000000 || CodeInfo->opnd[OPND2].data64 >= 0xffffffff80000000 ) ) { CodeInfo->opnd[OPND1].type &= ~OP_A; DebugMsg1(("check_size: OP_A flag reset, new op2=%X\n", CodeInfo->opnd[OPND1].type )); #endif } } /* fall through */ default: //#if AMD64_SUPPORT def_check: //#endif /* make sure the 2 opnds are of the same type */ op1_size = OperandSize( op1, CodeInfo ); op2_size = OperandSize( op2, CodeInfo ); DebugMsg1(("check_size default: op1_size1=%u, op2_size=%u\n", op1_size, op2_size)); if( op1_size > op2_size ) { if( ( op2 >= OP_I8 ) && ( op2 <= OP_I32 ) ) { /* immediate */ op2_size = op1_size; /* promote to larger size */ } } #if 1 /* v2.04: check in idata_nofixup was signed, * so now add -256 - -129 and 128-255 to acceptable byte range. * Since Masm v8, the check is more restrictive, -255 - -129 * is no longer accepted. */ if( ( op1_size == 1 ) && ( op2 == OP_I16 ) && ( CodeInfo->opnd[OPND2].data32l <= UCHAR_MAX ) && //CodeInfo->opnd[OPND2].data32l >= -128 ) ) { ( CodeInfo->opnd[OPND2].data32l >= -255 ) ) { return( rc ); /* OK cause no sign extension */ } #endif #if 0 /* v2.03: this "if" made JWasm accept any 32-bit constant * for 16-bit destinations, which is Masm compatibel, * "mov ax, 12345h" * the test is a bit too liberal here, IMO, because * it makes JWasm accept "mov ax, near32 ptr var", * which is rejected by Masm. */ if( ( op1_size == 2 ) && ( op2 == OP_I32 ) && ( CodeInfo->data[OPND2] <= USHRT_MAX ) ) { return( rc ); /* OK cause no sign extension */ } #endif if( op1_size != op2_size ) { /* if one or more are !defined, set them appropriately */ #if AVXSUPP if( ( op1 | op2 ) & ( OP_MMX | OP_XMM | OP_YMM ) ) { #else if( ( op1 | op2 ) & ( OP_MMX | OP_XMM ) ) { #endif } else if( ( op1_size != 0 ) && ( op2_size != 0 ) ) { EmitErr( OPERANDS_MUST_BE_THE_SAME_SIZE, op1_size, op2_size ); rc = ERROR; } /* size == 0 is assumed to mean "undefined", but there * is also the case of an "empty" struct or union. The * latter case isn't handled correctly. */ if( op1_size == 0 ) { if( ( op1 & OP_M_ANY ) && ( op2 & OP_I ) ) { char *p = "WORD"; if( (uint_32)CodeInfo->opnd[OPND2].data32l > USHRT_MAX || op2_size == 4 ) { CodeInfo->iswide = 1; DebugMsg1(("check_size: op1=%X op1_size=0, op2=%X, op2_size=%u CodeInfo->data[2]=%X\n", op1, op2, op2_size, CodeInfo->opnd[OPND2].data32l )); #if 1 /* added v1.95: in 16bit code, 'mov [di],8000h' should warn: assuming WORD */ if ( ModuleInfo.Ofssize == USE16 && op2_size > 2 && InWordRange( CodeInfo->opnd[OPND2].data32l ) ) op2_size = 2; #endif if (op2_size <= 2 && CodeInfo->opnd[OPND2].data32l > SHRT_MIN && ModuleInfo.Ofssize == USE16 ) { CodeInfo->mem_type = MT_WORD; CodeInfo->opnd[OPND2].type = OP_I16; } else { CodeInfo->mem_type = MT_DWORD; CodeInfo->opnd[OPND2].type = OP_I32; p = "DWORD"; } } else if( (uint_32)CodeInfo->opnd[OPND2].data32l > UCHAR_MAX || op2_size == 2 ) { CodeInfo->mem_type = MT_WORD; CodeInfo->iswide = 1; CodeInfo->opnd[OPND2].type = OP_I16; } else { CodeInfo->mem_type = MT_BYTE; CodeInfo->opnd[OPND2].type = OP_I8; p = "BYTE"; } if( opndx[OPND2].explicit == FALSE ) { /* v2.06: emit warning at pass one if mem op isn't a forward ref */ /* v2.06b: added "undefined" check */ if ( ( CodeInfo->opnd[OPND1].InsFixup == NULL && Parse_Pass == PASS_1 && CodeInfo->undef_sym == FALSE ) || ( CodeInfo->opnd[OPND1].InsFixup && Parse_Pass == PASS_2 ) ) EmitWarn( 1, SIZE_NOT_SPECIFIED_ASSUMING, p ); } } else if( ( op1 & OP_M_ANY ) && ( op2 & ( OP_R | OP_SR ) ) ) { } else if( ( op1 & ( OP_MMX | OP_XMM ) ) && ( op2 & OP_I ) ) { if( (uint_32)CodeInfo->opnd[OPND2].data32l > USHRT_MAX ) { CodeInfo->opnd[OPND2].type = OP_I32; } else if( (uint_32)CodeInfo->opnd[OPND2].data32l > UCHAR_MAX ) { CodeInfo->opnd[OPND2].type = OP_I16; } else { CodeInfo->opnd[OPND2].type = OP_I8; } } else if( ( op1 | op2 ) & ( OP_MMX | OP_XMM ) ) { } else { //AsmIntErr( 1 ); /* printf("internal error = %u", 1 ) */ switch( op2_size ) { case 1: CodeInfo->mem_type = MT_BYTE; if( ( Parse_Pass == PASS_1 ) && ( op2 & OP_I ) ) { EmitWarn( 1, SIZE_NOT_SPECIFIED_ASSUMING, "BYTE" ); } break; case 2: CodeInfo->mem_type = MT_WORD; CodeInfo->iswide = 1; if( ( Parse_Pass == PASS_1 ) && ( op2 & OP_I ) ) { EmitWarn( 1, SIZE_NOT_SPECIFIED_ASSUMING, "WORD" ); } if( CodeInfo->Ofssize ) CodeInfo->prefix.opsiz = TRUE; break; case 4: CodeInfo->mem_type = MT_DWORD; CodeInfo->iswide = 1; if( ( Parse_Pass == PASS_1 ) && ( op2 & OP_I ) ) { EmitWarn( 1, SIZE_NOT_SPECIFIED_ASSUMING, "DWORD" ); } break; } } } } } DebugMsg1(("check_size exit [CodeInfo->mem_type=%Xh]\n", CodeInfo->mem_type)); return( rc ); } static struct asym *IsType( const char *name ) /********************************************/ { struct asym *sym; sym = SymSearch( name ); if ( sym && (sym->state == SYM_TYPE ) ) return( sym ); return( NULL ); } /* * ParseLine() is the main parser function. * It scans the tokens in tokenarray[] and does: * - for code labels: call CreateLabel() * - for data items and data directives: call data_dir() * - for other directives: call directive[]() * - for instructions: fill CodeInfo and call codegen() */ ret_code ParseLine( struct asm_tok tokenarray[] ) /**********************************************/ { int i; int j; unsigned dirflags; unsigned CurrOpnd; ret_code temp; struct asym *sym; uint_32 oldofs; #ifdef DEBUG_OUT char *instr; #endif struct code_info CodeInfo; #if AVXSUPP struct expr opndx[MAX_OPND+1]; #else struct expr opndx[MAX_OPND]; #endif DebugMsg1(("ParseLine enter, Token_Count=%u, ofs=%Xh\n", Token_Count, GetCurrOffset() )); i = 0; /* Does line start with a code label? */ if ( tokenarray[0].token == T_ID && ( tokenarray[1].token == T_COLON || tokenarray[1].token == T_DBL_COLON ) ) { i = 2; DebugMsg1(("ParseLine T_COLON, code label=%s\n", tokenarray[0].string_ptr )); if( ProcStatus & PRST_PROLOGUE_NOT_DONE ) write_prologue( tokenarray ); /* create a global or local code label */ if( CreateLabel( tokenarray[0].string_ptr, MT_NEAR, NULL, ( ModuleInfo.scoped && CurrProc && tokenarray[1].token != T_DBL_COLON ) ) == NULL ) { DebugMsg(("ParseLine, CreateLabel(%s) failed, exit\n", tokenarray[0].string_ptr )); return( ERROR ); } if ( tokenarray[i].token == T_FINAL ) { /* v2.06: this is a bit too late. Should be done BEFORE * CreateLabel, because of '@@'. There's a flag supposed to * be used for this handling, LOF_STORED in line_flags. * It's only a problem if a '@@:' is the first line * in the code section. * v2.10: is no longer an issue because the label counter has * been moved to module_vars (see global.h). */ FStoreLine(0); if ( CurrFile[LST] ) { LstWrite( LSTTYPE_LABEL, 0, NULL ); } return( NOT_ERROR ); } } /* handle directives and (anonymous) data items */ if ( tokenarray[i].token != T_INSTRUCTION ) { /* a code label before a data item is only accepted in Masm5 compat mode */ Frame_Type = FRAME_NONE; SegOverride = NULL; if ( i == 0 && tokenarray[0].token == T_ID ) { /* token at pos 0 may be a label. * it IS a label if: * 1. token at pos 1 is a directive (lbl dd ...) * 2. token at pos 0 is NOT a userdef type ( lbl DWORD ...) * 3. inside a struct and token at pos 1 is a userdef type * or a predefined type. (usertype DWORD|usertype ... ) * the separate namespace allows this syntax here. */ if( tokenarray[1].token == T_DIRECTIVE ) i++; else { sym = IsType( tokenarray[0].string_ptr ); if ( sym == NULL ) i++; else if ( CurrStruct && ( ( tokenarray[1].token == T_STYPE ) || ( tokenarray[1].token == T_ID && ( IsType( tokenarray[1].string_ptr ) ) ) ) ) i++; } } switch ( tokenarray[i].token ) { case T_DIRECTIVE: DebugMsg1(("ParseLine: T_DIRECTIVE >%s<\n", tokenarray[i].string_ptr )); if ( tokenarray[i].dirtype == DRT_DATADIR ) { return( data_dir( i, tokenarray, NULL ) ); } dirflags = GetValueSp( tokenarray[i].tokval ); if( CurrStruct && ( dirflags & DF_NOSTRUC ) ) { return( EmitError( STATEMENT_NOT_ALLOWED_INSIDE_STRUCTURE_DEFINITION ) ); } /* label allowed for directive? */ //if ( tokenarray[i].flags & DF_LABEL ) { if ( dirflags & DF_LABEL ) { if ( i && tokenarray[0].token != T_ID ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[0].string_ptr ) ); } } else if ( i && tokenarray[i-1].token != T_COLON && tokenarray[i-1].token != T_DBL_COLON ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i-1].string_ptr ) ); } /* must be done BEFORE FStoreLine()! */ if( ( ProcStatus & PRST_PROLOGUE_NOT_DONE ) && ( dirflags & DF_PROC ) ) write_prologue( tokenarray ); #if FASTPASS if ( StoreState || ( dirflags & DF_STORE ) ) { /* v2.07: the comment must be stored as well * if a listing (with -Sg) is to be written and * the directive will generate lines */ if ( ( dirflags & DF_CGEN ) && ModuleInfo.CurrComment && ModuleInfo.list_generated_code ) { FStoreLine(1); } else FStoreLine(0); } #endif if ( tokenarray[i].dirtype > DRT_DATADIR ) { temp = directive_tab[tokenarray[i].dirtype]( i, tokenarray ); } else { temp = ERROR; /* ENDM, EXITM and GOTO directives should never be seen here */ switch( tokenarray[i].tokval ) { case T_ENDM: EmitError( UNMATCHED_MACRO_NESTING ); break; case T_EXITM: case T_GOTO: EmitError( DIRECTIVE_MUST_APPEAR_INSIDE_A_MACRO ); break; default: /* this error may happen if * CATSTR, SUBSTR, MACRO, ... * aren't at pos 1 */ EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ); break; } } /* v2.0: for generated code it's important that list file is * written in ALL passes, to update file position! */ //if ( ModuleInfo.list && (( line_flags & LOF_LISTED ) == 0 ) && Parse_Pass == PASS_1 ) #if FASTPASS /* v2.08: UseSavedState == FALSE added */ if ( ModuleInfo.list && ( Parse_Pass == PASS_1 || ModuleInfo.GeneratedCode || UseSavedState == FALSE ) ) #else if ( ModuleInfo.list ) #endif LstWriteSrcLine(); return( temp ); case T_STYPE: DebugMsg1(("ParseLine: T_STYPE >%s<\n", tokenarray[i].string_ptr )); return( data_dir( i, tokenarray, NULL ) ); case T_ID: DebugMsg1(("ParseLine: T_ID >%s<\n", tokenarray[i].string_ptr )); if( (sym = IsType( tokenarray[i].string_ptr )) != NULL ) { return( data_dir( i, tokenarray, sym ) ); } break; default: if ( tokenarray[i].token == T_COLON ) { DebugMsg(("ParseLine: unexpected colon\n" )); return( EmitError( SYNTAX_ERROR_UNEXPECTED_COLON ) ); } break; } /* end switch (tokenarray[i].token) */ if ( i && tokenarray[i-1].token == T_ID ) i--; DebugMsg(("ParseLine: unexpected token=%u, i=%u, string=%s\n", tokenarray[i].token, i, tokenarray[i].string_ptr)); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) ); } DebugMsg1(("ParseLine: %s\n", tokenarray[i].string_ptr)); /* v2.04 added */ if( CurrStruct ) { return( EmitError( STATEMENT_NOT_ALLOWED_INSIDE_STRUCTURE_DEFINITION ) ); } if( ProcStatus & PRST_PROLOGUE_NOT_DONE ) write_prologue( tokenarray ); /* v2.07: moved because special handling is needed for RET/IRET */ //FStoreLine(); /* must be placed AFTER write_prologue() */ if ( CurrFile[LST] ) oldofs = GetCurrOffset(); /* init CodeInfo */ CodeInfo.prefix.ins = EMPTY; CodeInfo.prefix.RegOverride = EMPTY; #if AMD64_SUPPORT CodeInfo.prefix.rex = 0; #endif CodeInfo.prefix.adrsiz = FALSE; CodeInfo.prefix.opsiz = FALSE; CodeInfo.mem_type = MT_EMPTY; for( j = 0; j < MAX_OPND; j++ ) { CodeInfo.opnd[j].type = OP_NONE; #ifdef DEBUG_OUT CodeInfo.opnd[j].data32l = -1; /* make sure it's invalid */ CodeInfo.opnd[j].InsFixup = (void *)0xffffffff; #endif } CodeInfo.rm_byte = 0; CodeInfo.sib = 0; /* assume ss is *1 */ CodeInfo.Ofssize = ModuleInfo.Ofssize; CodeInfo.opc_or = 0; #if AVXSUPP CodeInfo.vexregop = 0; #endif CodeInfo.flags = 0; /* instruction prefix? * T_LOCK, T_REP, T_REPE, T_REPNE, T_REPNZ, T_REPZ */ if ( tokenarray[i].tokval >= T_LOCK && tokenarray[i].tokval <= T_REPZ ) { CodeInfo.prefix.ins = tokenarray[i].tokval; i++; /* prefix has to be followed by an instruction */ if( tokenarray[i].token != T_INSTRUCTION ) { DebugMsg(("ParseLine: unexpected token %u after prefix, exit, error\n", tokenarray[i].token )); return( EmitError( PREFIX_MUST_BE_FOLLOWED_BY_AN_INSTRUCTION ) ); } DebugMsg1(("ParseLine: %s\n", tokenarray[i].tokpos)); }; if( CurrProc ) { switch( tokenarray[i].tokval ) { case T_RET: case T_IRET: /* IRET is always 16-bit; OTOH, IRETW doesn't exist */ case T_IRETD: #if AMD64_SUPPORT case T_IRETQ: #endif if ( !( ProcStatus & PRST_INSIDE_EPILOGUE ) && ModuleInfo.epiloguemode != PEM_NONE ) { /* v2.07: special handling for RET/IRET */ FStoreLine( ( ModuleInfo.CurrComment && ModuleInfo.list_generated_code ) ? 1 : 0 ); ProcStatus |= PRST_INSIDE_EPILOGUE; temp = RetInstr( i, tokenarray, Token_Count ); ProcStatus &= ~PRST_INSIDE_EPILOGUE; return( temp ); } /* default translation: just RET to RETF if proc is far */ /* v2.08: this code must run even if PRST_INSIDE_EPILOGUE is set */ if ( tokenarray[i].tokval == T_RET && CurrProc->sym.mem_type == MT_FAR ) tokenarray[i].tokval = T_RETF; } } FStoreLine(0); /* must be placed AFTER write_prologue() */ #ifdef DEBUG_OUT instr = tokenarray[i].string_ptr; #endif CodeInfo.token = tokenarray[i].tokval; /* get the instruction's start position in InstrTable[] */ CodeInfo.pinstr = &InstrTable[IndexFromToken( CodeInfo.token )]; i++; if( CurrSeg == NULL ) { return( EmitError( MUST_BE_IN_SEGMENT_BLOCK ) ); } if( CurrSeg->e.seginfo->segtype == SEGTYPE_UNDEF ) { CurrSeg->e.seginfo->segtype = SEGTYPE_CODE; } if ( ModuleInfo.CommentDataInCode ) omf_OutSelect( FALSE ); /* get the instruction's arguments. * This loop accepts up to 4 arguments if AVXSUPP is on */ for ( j = 0; j < sizeof(opndx)/sizeof(opndx[0]) && tokenarray[i].token != T_FINAL; j++ ) { if ( j ) { if ( tokenarray[i].token != T_COMMA ) break; i++; } DebugMsg1(("ParseLine(%s): calling EvalOperand, i=%u\n", instr, i)); if( EvalOperand( &i, tokenarray, Token_Count, &opndx[j], 0 ) == ERROR ) { DebugMsg(("ParseLine(%s): EvalOperand() failed\n", instr )); return( ERROR ); } switch ( opndx[j].kind ) { case EXPR_FLOAT: /* v2.06: accept float constants for PUSH */ if ( j == OPND2 || CodeInfo.token == T_PUSH || CodeInfo.token == T_PUSHD ) { #if FPIMMEDIATE if ( Options.strict_masm_compat == FALSE ) { /* convert to REAL4, unless REAL8 coercion is requested */ atofloat( &opndx[j].fvalue, opndx[j].float_tok->string_ptr, opndx[j].mem_type == MT_REAL8 ? 8 : 4, opndx[j].negative, opndx[j].float_tok->floattype ); opndx[j].kind = EXPR_CONST; opndx[j].float_tok = NULL; break; } #endif /* Masm message is: real or BCD number not allowed */ return( EmitError( FP_INITIALIZER_IGNORED ) ); } /* fall through */ case EXPR_EMPTY: if ( i == Token_Count ) i--; /* v2.08: if there was a terminating comma, display it */ /* fall through */ case EXPR_ERROR: DebugMsg(("ParseLine(%s): unexpected operand kind=%d, error, exit\n", instr, opndx[j].kind )); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) ); } } if ( tokenarray[i].token != T_FINAL ) { DebugMsg(("ParseLine(%s): too many operands (%s) \n", instr, tokenarray[i].tokpos )); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) ); } for ( CurrOpnd = 0; CurrOpnd < j && CurrOpnd < MAX_OPND; CurrOpnd++ ) { Frame_Type = FRAME_NONE; SegOverride = NULL; /* segreg prefix is stored in RegOverride */ CodeInfo.opnd[CurrOpnd].data32l = 0; CodeInfo.opnd[CurrOpnd].InsFixup = NULL; #if AVXSUPP /* if encoding is VEX and destination op is XMM, YMM or memory, * the second argument may be stored in the vexregop field. */ if ( CodeInfo.token >= VEX_START && CurrOpnd == OPND2 && ( CodeInfo.opnd[OPND1].type & ( OP_XMM | OP_YMM | OP_M | OP_M256 ) ) ) { if ( vex_flags[CodeInfo.token - VEX_START] & VX_NND ) ; else if ( ( vex_flags[CodeInfo.token - VEX_START] & VX_IMM ) && ( opndx[OPND3].kind == EXPR_CONST ) && ( j > 2 ) ) ; else if ( ( vex_flags[CodeInfo.token - VEX_START] & VX_NMEM ) && ( ( CodeInfo.opnd[OPND1].type & OP_M ) || /* v2.11: VMOVSD and VMOVSS always have 2 ops if a memory op is involved */ ( ( CodeInfo.token == T_VMOVSD || CodeInfo.token == T_VMOVSS ) && ( opndx[OPND2].kind != EXPR_REG || opndx[OPND2].indirect == TRUE ) ) ) ) ; else { if ( opndx[OPND2].kind != EXPR_REG || ( ! ( GetValueSp( opndx[CurrOpnd].base_reg->tokval ) & ( OP_XMM | OP_YMM ) ) ) ) { DebugMsg(("ParseLine(%s,%u): avx invalid operand, op2.kind=%u\n", instr, CurrOpnd, opndx[OPND2].kind )); return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } /* fixme: check if there's an operand behind OPND2 at all! * if no, there's no point to continue with switch (opndx[].kind). * v2.11: additional check for j <= 2 added */ if ( j <= 2 ) { DebugMsg(("ParseLine(%s,%u): avx not enough operands (%u)\n", instr, CurrOpnd, opndx[OPND2].kind, j )); /* v2.11: next line should be activated - currently the error is emitted below as syntax error */ //return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } else /* flag VX_DST is set if an immediate is expected as operand 3 */ if ( ( vex_flags[CodeInfo.token - VEX_START] & VX_DST ) && ( opndx[OPND3].kind == EXPR_CONST ) ) { DebugMsg1(("ParseLine(%s,%u): avx VX_DST, op3.kind=CONST (value=%u), numops=%u\n", instr, CurrOpnd, opndx[OPND3].kind, opndx[OPND3].value, j )); if ( opndx[OPND1].base_reg ) { /* first operand register is moved to vexregop */ /* handle VEX.NDD */ CodeInfo.vexregop = opndx[OPND1].base_reg->bytval + 1; memcpy( &opndx[OPND1], &opndx[CurrOpnd], sizeof( opndx[0] ) * 3 ); CodeInfo.rm_byte = 0; if ( process_register( &CodeInfo, OPND1, opndx ) == ERROR ) return( ERROR ); } } else { unsigned flags = GetValueSp( opndx[CurrOpnd].base_reg->tokval ); DebugMsg1(("ParseLine(%s,%u): opnd2 is avx reg (%s), flags=%X ci.type[0]=%X numops=%u\n", instr, CurrOpnd, opndx[CurrOpnd].base_reg->string_ptr, flags, CodeInfo.opnd[OPND1].type, j )); #if 1 /* v2.08: no error here if first op is an untyped memory reference * note that OP_M includes OP_M128, but not OP_M256 (to be fixed?) */ if ( CodeInfo.opnd[OPND1].type == OP_M ) ; else #endif if ( ( flags & ( OP_XMM | OP_M128 ) ) && ( CodeInfo.opnd[OPND1].type & (OP_YMM | OP_M256 ) ) || ( flags & ( OP_YMM | OP_M256 ) ) && ( CodeInfo.opnd[OPND1].type & (OP_XMM | OP_M128 ) ) ) { DebugMsg(("ParseLine(%s,%u): avx invalid opnd 2, flags=%X ci.type[0]=%X\n", instr, CurrOpnd, flags, CodeInfo.opnd[OPND1].type )); return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } /* second operand register is moved to vexregop */ /* to be fixed: CurrOpnd is always OPND2, so use this const here */ CodeInfo.vexregop = opndx[CurrOpnd].base_reg->bytval + 1; memcpy( &opndx[CurrOpnd], &opndx[CurrOpnd+1], sizeof( opndx[0] ) * 2 ); } j--; } } #endif DebugMsg1(("ParseLine(%s,%u): type/value/mem_type/ofssize=%Xh/%" I64_SPEC "Xh/%Xh/%d\n", instr, CurrOpnd, opndx[CurrOpnd].kind, opndx[CurrOpnd].value64, opndx[CurrOpnd].mem_type, opndx[CurrOpnd].Ofssize )); switch( opndx[CurrOpnd].kind ) { case EXPR_ADDR: DebugMsg1(("ParseLine(%s,%u): type ADDRESS\n", instr, CurrOpnd )); if ( process_address( &CodeInfo, CurrOpnd, &opndx[CurrOpnd] ) == ERROR ) return( ERROR ); break; case EXPR_CONST: DebugMsg1(("ParseLine(%s,%u): type CONST, opndx.memtype=%Xh\n", instr, CurrOpnd, opndx[CurrOpnd].mem_type)); if ( process_const( &CodeInfo, CurrOpnd, &opndx[CurrOpnd] ) == ERROR ) return( ERROR ); break; case EXPR_REG: DebugMsg1(("ParseLine(%s,%u): type REG\n", instr, CurrOpnd )); if( opndx[CurrOpnd].indirect ) { /* indirect operand ( "[EBX+...]" )? */ if ( process_address( &CodeInfo, CurrOpnd, &opndx[CurrOpnd] ) == ERROR ) return( ERROR ); } else { /* process_register() can't handle 3rd operand */ if ( CurrOpnd == OPND3 ) { CodeInfo.opnd[OPND3].type = GetValueSp( opndx[CurrOpnd].base_reg->tokval ); CodeInfo.opnd[OPND3].data32l = opndx[CurrOpnd].base_reg->bytval; } else if ( process_register( &CodeInfo, CurrOpnd, opndx ) == ERROR ) return( ERROR ); } break; } } /* end for */ #if AVXSUPP /* 4 arguments are valid vor AVX only */ if ( CurrOpnd != j ) { for ( ; tokenarray[i].token != T_COMMA; i-- ); DebugMsg(("ParseLine(%s): CurrOpnd != j ( %u - %u ) >%s<\n", instr, CurrOpnd, j, tokenarray[i].tokpos )); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) ); } #endif /* for FAR calls/jmps some special handling is required: * in the instruction tables, the "far" entries are located BEHIND * the "near" entries, that's why it's needed to skip all items * until the next "first" item is found. */ if ( CodeInfo.isfar ) { if ( CodeInfo.token == T_CALL || CodeInfo.token == T_JMP ) { do { CodeInfo.pinstr++; } while ( CodeInfo.pinstr->first == FALSE ); } } /* special handling for string instructions */ if ( CodeInfo.pinstr->allowed_prefix == AP_REP || CodeInfo.pinstr->allowed_prefix == AP_REPxx ) { HandleStringInstructions( &CodeInfo, opndx ); #if SVMSUPP /* v2.09, not active because a bit too hackish yet - it "works", though. */ } else if ( CodeInfo.token >= T_VMRUN && CodeInfo.token <= T_INVLPGA && CodeInfo.pinstr->opclsidx ) { /* the size of the first operand is to trigger the address size byte 67h, * not the operand size byte 66h! */ CodeInfo.prefix.adrsiz = CodeInfo.prefix.opsiz; CodeInfo.prefix.opsiz = 0; /* the first op must be EAX/AX or RAX/EAX. The operand class * used in the instruction table is OP_A ( which is AL/AX/EAX/RAX ). */ if ( ( CodeInfo.opnd[OPND1].type & ( CodeInfo.Ofssize == USE64 ? OP_R64 | OP_R32 : OP_R32 | OP_R16 ) ) == 0 ) { DebugMsg(("ParseLine(%s): opnd1 unexpected type=%X\n", instr, CodeInfo.opnd[OPND1].type )); return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } /* the INVLPGA instruction has a fix second operand (=ECX). However, there's no * operand class for ECX alone. So it has to be ensured here that the register IS ecx. */ if ( CodeInfo.token == T_INVLPGA ) if ( ( CodeInfo.rm_byte & BIT_345 ) != ( 1 << 3 ) ) { /* ECX is register 1 */ DebugMsg(("ParseLine(%s): opnd2 is not ecx\n", instr )); return( EmitErr( INVALID_INSTRUCTION_OPERANDS ) ); } #endif } else { if( CurrOpnd > 1 ) { /* v1.96: check if a third argument is ok */ if ( CurrOpnd > 2 ) { do { //if ( CodeInfo.pinstr->opnd_type_3rd != OP3_NONE ) if ( opnd_clstab[CodeInfo.pinstr->opclsidx].opnd_type_3rd != OP3_NONE ) break; CodeInfo.pinstr++; if ( CodeInfo.pinstr->first == TRUE ) { DebugMsg(("ParseLine(%s): no third operand expected\n", instr )); for ( ; tokenarray[i].token != T_COMMA; i-- ); return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) ); } } while ( 1 ); } /* v2.06: moved here from process_const() */ if ( CodeInfo.token == T_IMUL ) { /* the 2-operand form with an immediate as second op * is actually a 3-operand form. That's why the rm byte * has to be adjusted. */ if ( CodeInfo.opnd[OPND3].type == OP_NONE && ( CodeInfo.opnd[OPND2].type & OP_I ) ) { #if AMD64_SUPPORT CodeInfo.prefix.rex |= ((CodeInfo.prefix.rex & REX_B) ? REX_R : 0 ); #endif CodeInfo.rm_byte = ( CodeInfo.rm_byte & ~BIT_345 ) | ( ( CodeInfo.rm_byte & BIT_012 ) << 3 ); } else if ( ( CodeInfo.opnd[OPND3].type != OP_NONE ) && ( CodeInfo.opnd[OPND2].type & OP_I ) && CodeInfo.opnd[OPND2].InsFixup && CodeInfo.opnd[OPND2].InsFixup->sym->state == SYM_UNDEFINED ) CodeInfo.opnd[OPND2].type = OP_M; } if( check_size( &CodeInfo, opndx ) == ERROR ) { DebugMsg(("ParseLine(%s): check_size() failed, exit\n", instr )); return( ERROR ); } } #if AMD64_SUPPORT if ( CodeInfo.Ofssize == USE64 ) { //if ( CodeInfo.x86hi_used && ( CodeInfo.x64lo_used || CodeInfo.prefix.rex & 7 )) if ( CodeInfo.x86hi_used && CodeInfo.prefix.rex ) EmitError( INVALID_USAGE_OF_AHBHCHDH ); /* for some instructions, the "wide" flag has to be removed selectively. * this is to be improved - by a new flag in struct instr_item. */ switch ( CodeInfo.token ) { case T_PUSH: case T_POP: /* v2.06: REX.W prefix is always 0, because size is either 2 or 8 */ //if ( CodeInfo.opnd_type[OPND1] & OP_R64 ) CodeInfo.prefix.rex &= 0x7; break; case T_CALL: case T_JMP: #if VMXSUPP /* v2.09: added */ case T_VMREAD: case T_VMWRITE: #endif /* v2.02: previously rex-prefix was cleared entirely, * but bits 0-2 are needed to make "call rax" and "call r8" * distinguishable! */ //CodeInfo.prefix.rex = 0; CodeInfo.prefix.rex &= 0x7; break; case T_MOV: /* don't use the Wide bit for moves to/from special regs */ if ( CodeInfo.opnd[OPND1].type & OP_RSPEC || CodeInfo.opnd[OPND2].type & OP_RSPEC ) CodeInfo.prefix.rex &= 0x7; break; } } #endif } /* now call the code generator */ return( codegen( &CodeInfo, oldofs ) ); } /* process a file. introduced in v2.11 */ void ProcessFile( struct asm_tok tokenarray[] ) /*********************************************/ { while ( ModuleInfo.EndDirFound == FALSE && GetTextLine( CurrSource ) ) { if ( PreprocessLine( CurrSource, tokenarray ) ) { ParseLine( tokenarray ); if ( Options.preprocessor_stdout == TRUE && Parse_Pass == PASS_1 ) WritePreprocessedLine( CurrSource ); } } return; }