/**************************************************************************** * * 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: expression evaluator. * ****************************************************************************/ #include #include #include "globals.h" #include "parser.h" #include "reswords.h" #include "expreval.h" #include "segment.h" #include "proc.h" #include "assume.h" #include "tokenize.h" #include "types.h" #include "label.h" #include "atofloat.h" #include "myassert.h" #define ALIAS_IN_EXPR 1 /* allow alias names in expression */ #define UNARY_PLUSMINUS 0 #define BINARY_PLUSMINUS 1 /* activate if a detailed error location is needed and -dt cant be used */ #if 0 #define ERRLOC( i ) printf("Error at %s.%u: %u >%s< >%s<\n", __FILE__, __LINE__, i, ModuleInfo.tokenarray[i].string_ptr, ModuleInfo.tokenarray[0].tokpos ) //#undef DebugMsg1 //#define DebugMsg1( x ) printf x #else #define ERRLOC( i ) #endif #if STACKBASESUPP==0 extern enum special_token basereg[]; #else extern uint_32 StackAdj; #endif #ifdef DEBUG_OUT static int evallvl = 0; #endif /* the following static variables should be moved to ModuleInfo. */ static struct asym *thissym; /* helper symbol for THIS operator */ static struct asym *nullstruct; /* used for T_DOT if second op is a forward ref */ static struct asym *nullmbr; /* used for T_DOT if "current" struct is a forward ref */ static int (* fnEmitErr)( int, ... ); static int noEmitErr( int msg, ... ); /* code label type values - returned by SIZE and TYPE operators */ enum labelsize { LS_SHORT = 0xFF01, /* it's documented, but can a label be "short"? */ //LS_NEAR16 = 0xFF02, /* v2.09: the near values are calculated */ //LS_NEAR32 = 0xFF04, //LS_NEAR64 = 0xFF08, LS_FAR16 = 0xFF05, LS_FAR32 = 0xFF06, }; static void init_expr( struct expr *opnd ) /****************************************/ { opnd->value = 0; opnd->hvalue = 0; opnd->hlvalue = 0; opnd->quoted_string = NULL; opnd->base_reg = NULL; opnd->idx_reg = NULL; opnd->label_tok = NULL; opnd->override = NULL; opnd->instr = EMPTY; opnd->kind = EXPR_EMPTY; opnd->mem_type = MT_EMPTY; opnd->scale = 0; opnd->Ofssize = USE_EMPTY; opnd->flags1 = 0; opnd->sym = NULL; opnd->mbr = NULL; opnd->type = NULL; } static void TokenAssign( struct expr *opnd1, const struct expr *opnd2 ) /*********************************************************************/ { #if 1 /* note that offsetof() is used. This means, don't change position of field in expr! */ memcpy( opnd1, opnd2, offsetof( struct expr, type ) ); #else opnd1->llvalue = opnd2->llvalue; opnd1->hlvalue = opnd2->hlvalue; opnd1->quoted_string = opnd2->quoted_string; /* probably useless */ opnd1->base_reg = opnd2->base_reg; opnd1->idx_reg = opnd2->idx_reg; opnd1->label_tok = opnd2->label_tok; opnd1->override = opnd2->override; opnd1->instr = opnd2->instr; opnd1->kind = opnd2->kind; opnd1->mem_type = opnd2->mem_type; opnd1->scale = opnd2->scale; opnd1->Ofssize = opnd2->Ofssize; opnd1->flags1 = opnd2->flags1; opnd1->sym = opnd2->sym; opnd1->mbr = opnd2->mbr; // opnd1->type = opnd2->type; #endif } //#define BRACKET_PRECEDENCE 1 //#define PTR_PRECEDENCE 4 //#define PLUS_PRECEDENCE 9 #define CMP_PRECEDENCE 10 static int get_precedence( const struct asm_tok *item ) /*****************************************************/ { /* The following table is taken verbatim from MASM 6.1 Programmer's Guide, * page 14, Table 1.3. * 1 (), [] * 2 LENGTH, SIZE, WIDTH, MASK, LENGTHOF, SIZEOF * 3 . (structure-field-name operator) * 4 : (segment override operator), PTR * 5 LROFFSET, OFFSET, SEG, THIS, TYPE * 6 HIGH, HIGHWORD, LOW, LOWWORD * 7 +, - (unary) * 8 *, /, MOD, SHL, SHR * 9 +, - (binary) * 10 EQ, NE, LT, LE, GT, GE * 11 NOT * 12 AND * 13 OR, XOR * 14 OPATTR, SHORT, .TYPE * The following table appears in QuickHelp online documentation for * both MASM 6.0 and 6.1. It's slightly different! * 1 LENGTH, SIZE, WIDTH, MASK * 2 (), [] * 3 . (structure-field-name operator) * 4 : (segment override operator), PTR * 5 THIS, OFFSET, SEG, TYPE * 6 HIGH, LOW * 7 +, - (unary) * 8 *, /, MOD, SHL, SHR * 9 +, - (binary) * 10 EQ, NE, LT, LE, GT, GE * 11 NOT * 12 AND * 13 OR, XOR * 14 SHORT, OPATTR, .TYPE, ADDR * japheth: the first table is the prefered one. Reasons: * - () and [] must be first. * - it contains operators SIZEOF, LENGTHOF, HIGHWORD, LOWWORD, LROFFSET * - ADDR is no operator for expressions. It's exclusively used inside * INVOKE directive. * However, what's wrong in both tables is the precedence of * the dot operator: Actually for both JWasm and Wasm the dot precedence * is 2 and LENGTH, SIZE, ... have precedence 3 instead. * Precedence of operator TYPE was 5 in original Wasm source. It has * been changed to 4, as described in the Masm docs. This allows syntax * "TYPE DWORD ptr xxx" * v2.02: another case which is problematic: * mov al,BYTE PTR CS:[] * Since PTR and ':' have the very same priority, the evaluator will * first calculate 'BYTE PTR CS'. This is invalid, but didn't matter * prior to v2.02 because register coercion was never checked for * plausibility. Solution: priority of ':' is changed from 4 to 3. */ switch( item->token ) { case T_UNARY_OPERATOR: case T_BINARY_OPERATOR: return( item->precedence ); case T_OP_BRACKET: case T_OP_SQ_BRACKET: /* v2.08: with -Zm, the priority of [] and (), if * used as binary operator, is 9 (like binary +/-). * test cases: mov ax,+5[bx] * mov ax,-5[bx] */ //return( 1 ); return( ModuleInfo.m510 ? 9 : 1 ); case T_DOT: return( 2 ); case T_COLON: //return( 4 ); return( 3 ); /* changed for v2.02 */ case '*': case '/': return( 8 ); case '+': case '-': return( item->specval ? 9 : 7 ); } /* shouldn't happen! */ DebugMsg(("get_precedence: unexpected operator=%s\n", item->string_ptr)); fnEmitErr( SYNTAX_ERROR_EX, item->string_ptr ); return( ERROR ); } #if 0 static bool is_operator( enum tok_type tt ) /*****************************************/ /* determine if token is an operator */ { /* T_OP_BRACKET and above: "(,[,],},:,.,+,-,*,/" */ /* rest: T_REG, T_STYPE, T_RES_ID, T_ID, T_STRING, * T_NUM, T_FLOAT, T_BAD_NUM, T_DBL_COLON, T_PERCENT */ return( tt >= T_OP_BRACKET || tt == T_UNARY_OPERATOR || tt == T_BINARY_OPERATOR ); } static bool is_unary_op( enum tok_type tt ) /*****************************************/ /* determine if token is an unary operator */ { return( tt == T_OP_BRACKET || tt == T_OP_SQ_BRACKET || tt == '+' || tt == '-' || tt == T_UNARY_OPERATOR ); } #else #define is_operator( tt ) ( tt >= T_OP_BRACKET || tt == T_UNARY_OPERATOR || tt == T_BINARY_OPERATOR ) #define is_unary_op( tt ) ( tt == T_OP_BRACKET || tt == T_OP_SQ_BRACKET || tt == '+' || tt == '-' || tt == T_UNARY_OPERATOR ) #endif /* get value for simple types * NEAR, FAR and PROC are handled slightly differently: * the HIBYTE is set to 0xFF, and PROC depends on the memory model */ static unsigned int GetTypeSize( enum memtype mem_type, int Ofssize ) /*******************************************************************/ { 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: return ( 0xFF00 | ( 2 << Ofssize ) ) ; case MT_FAR: return ( ( Ofssize == USE16 ) ? LS_FAR16 : 0xFF00 | ( ( 2 << Ofssize ) + 2 ) ); } /* shouldn't happen */ return( 0 ); } #if AMD64_SUPPORT static uint_64 GetRecordMask( struct dsym *record ) #else static uint_32 GetRecordMask( struct dsym *record ) #endif /*************************************************/ { #if AMD64_SUPPORT uint_64 mask = 0; #else uint_32 mask = 0; #endif int i; struct sfield *fl; for ( fl = record->e.structinfo->head; fl; fl = fl->next ) { struct asym *sym = &fl->sym; for ( i = sym->offset ;i < sym->offset + sym->total_size; i++ ) mask |= 1 << i; } return( mask ); } /* v2.06: the value of number strings is now evaluated here. * Prior to v2.06, it was evaluated in the tokenizer and the * value was stored in the token string buffer. Since the content * of the token buffer is no longer destroyed when macros or * generated code is run, the old strategy needed too much space. */ void myatoi128( const char *src, uint_64 dst[], int base, int size ) /******************************************************************/ { uint_32 val; unsigned len; const char *end = src + size; uint_16 *px; dst[0] = 0; dst[1] = 0; do { val = ( *src <= '9' ? *src - '0' : ( *src | 0x20 ) - 'a' + 10 ); px = (uint_16 *)dst; for ( len = ( 2 * sizeof( uint_64 ) ) >> 1; len; len-- ) { val += (uint_32)*px * base; *(px++) = val; val >>= 16; }; //myassert( val == 0 ); /* if number doesn't fit in 128 bits */ src++; } while( src < end ); return; } /* get an operand. operands are: * - integer constant : EXPR_CONST * - quoted string : EXPR_CONST * - register : EXPR_REG (indirect = 1/0) * - user identifier (T_ID): EXPR_ADDR | EXPR_CONST * - reserved ID (T_RES_ID): EXPR_CONST ( EXPR_ADDR if id=FLAT ) * - float constant : EXPR_FLOAT * * valid user identifiers are * - TYPE ( struct/union, typedef, record ) * - STRUCT FIELD (also bitfield) * - variable (internal, external, stack ) or constant (EQU, '=') * valid reserved IDs are types (BYTE, WORD, ... ) and FLAT */ static ret_code get_operand( struct expr *opnd, int *idx, struct asm_tok tokenarray[], const uint_8 flags ) /*********************************************************************************************************/ { char *tmp; struct asym *sym; int i = *idx; int j; char labelbuff[16];/* for anonymous labels */ DebugMsg1(("%u get_operand(idx=%u >%s<) enter [memtype=%Xh]\n", evallvl, i, tokenarray[i].tokpos, opnd->mem_type )); switch( tokenarray[i].token ) { case T_NUM: DebugMsg1(("%u get_operand: T_NUM, %s, base=%u, len=%u\n", evallvl, tokenarray[i].string_ptr, tokenarray[i].numbase, tokenarray[i].itemlen )); opnd->kind = EXPR_CONST; myatoi128( tokenarray[i].string_ptr, &opnd->llvalue, tokenarray[i].numbase, tokenarray[i].itemlen ); //opnd->llvalue = tokenarray[i].value64; //opnd->hlvalue = ( tokenarray[i].numflg == NF_NULL ? 0 : *(uint_64 *)( tokenarray[i].string_ptr - sizeof(uint_64) ) ); break; case T_STRING: DebugMsg1(("%u get_operand: T_STRING, %s, size=%u\n", evallvl, tokenarray[i].string_ptr, tokenarray[i].stringlen )); /* string enclosed in <> or {} are rejected since v1.94! */ if ( tokenarray[i].string_delim != '"' && tokenarray[i].string_delim != '\'') { if ( opnd->is_opattr ) /* OPATTR operator accepts anything! */ break; /* v2.0: display a comprehensible error msg if a quote is missing */ if ( tokenarray[i].string_delim == NULLC && ( *tokenarray[i].string_ptr == '"' || *tokenarray[i].string_ptr == '\'' )) fnEmitErr( MISSING_QUOTATION_MARK_IN_STRING ); else fnEmitErr( UNEXPECTED_LITERAL_FOUND_IN_EXPRESSION, tokenarray[i].tokpos ); return( ERROR ); } opnd->kind = EXPR_CONST; opnd->quoted_string = &tokenarray[i]; //opnd->value = 0; tmp = tokenarray[i].string_ptr + 1; /* skip the quote */ /* v2.06: use max. 16 bytes to create the "value". * Prior to 2.06, max 8 bytes were used for 64-bit and * max 4 bytes were used for 16-/32-bit. */ j = ( tokenarray[i].stringlen > sizeof( opnd->chararray ) ? sizeof( opnd->chararray ) : tokenarray[i].stringlen ); for( ; j; j-- ) opnd->chararray[j-1] = *tmp++; break; case T_REG: DebugMsg1(( "%u get_operand: T_REG, string=%s, tokval=%u, regno=%u\n", evallvl, tokenarray[i].string_ptr, tokenarray[i].tokval, tokenarray[i].bytval )); opnd->kind = EXPR_REG; opnd->base_reg = &tokenarray[i]; j = tokenarray[i].tokval; /* check if cpu is sufficient for register */ if( ( ( GetCpuSp( j ) & P_EXT_MASK ) && (( GetCpuSp( j ) & ModuleInfo.curr_cpu & P_EXT_MASK) == 0) || ( ModuleInfo.curr_cpu & P_CPU_MASK ) < ( GetCpuSp( j ) & P_CPU_MASK ) ) ) { /* v2.11: do not exit in indirect mode; avoids additional syntax error caused by ']' */ if ( flags & EXPF_IN_SQBR ) { opnd->kind = EXPR_ERROR; fnEmitErr( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE ); } else return( fnEmitErr( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE ) ); } if( flags & EXPF_IN_SQBR ) { /* a valid index register? */ if ( GetSflagsSp( j ) & SFR_IREG ) { opnd->indirect = TRUE; opnd->assumecheck = TRUE; } else if ( GetValueSp( j ) & OP_SR ) { /* a segment register inside square brackets is only * accepted by Masm if it is the segment part of an * address (mov ax,[bx+cs:label])! */ /* v2.10: check moved here avain. regression v2.08-2.09, where * it was in colon_op(). see regression test OVERRID3.ASC. */ //if( tokenarray[i+1].token != T_COLON ) { if( tokenarray[i+1].token != T_COLON || ( Options.strict_masm_compat && tokenarray[i+2].token == T_REG ) ) { return( fnEmitErr( INVALID_USE_OF_REGISTER ) ); } } else { if ( opnd->is_opattr ) /* v2.11: just set error for opattr */ opnd->kind = EXPR_ERROR; else return( fnEmitErr( MUST_BE_INDEX_OR_BASE_REGISTER ) ); } } break; case T_ID: tmp = tokenarray[i].string_ptr; //if ( opnd->type ) { /* v2.11 */ if ( opnd->is_dot ) { DebugMsg1(("%u get_operand: T_ID, is_dot=1, id=%s, opnd.type=%s\n", evallvl, tokenarray[i].string_ptr, opnd->type ? opnd->type->name : "NULL" )); opnd->value = 0; sym = ( opnd->type ? SearchNameInStruct( opnd->type, tmp, &opnd->uvalue, 0 ) : NULL ); DebugMsg1(("get_operand(%s): is_dot, sym=%s, offset=%" I32_SPEC "Xh\n", tmp, sym ? sym->name : "NULL", opnd->uvalue )); if ( sym == NULL ) { sym = SymSearch( tmp ); if ( sym ) { /* * skip a type specifier matching the data item's type * that's something like ".." */ if ( sym->state == SYM_TYPE ) { /* * v2.07: "if" added. * Masm accepts a different type spec if the "assumed" * type is undefined * v2.09: the change in v2.07 is a regression. if it's a type, * then "usually" assume a type coercion and "switch" to the * new type - but not for register assume. This isn't fixed * yet, because there's no way to find out if a register assume * did set field 'type'. * v2.09: oldstructs condition added, see regression test dotop4.asm. * v2.11: fixme? opnd->type may be NULL here? * v2.12: for opnd->type==NULL test case, see expr5.aso. */ //if ( sym == opnd->type || opnd->type->isdefined == FALSE ) //if ( sym == opnd->type || opnd->type->isdefined == FALSE || ModuleInfo.oldstructs ) if ( sym == opnd->type || ( opnd->type && opnd->type->isdefined == FALSE ) || ModuleInfo.oldstructs ) ; //opnd->sym = sym; else { sym = NULL; } } else if ( ModuleInfo.oldstructs && ( sym->state == SYM_STRUCT_FIELD || sym->state == SYM_EXTERNAL || /* v2.01: added */ /* v2.05: changed */ //( sym->state == SYM_INTERNAL && sym->mem_type == MT_ABS ) ) ) sym->state == SYM_INTERNAL ) ) //opnd->sym = sym; ; else { /* fixme: clear sym? * if the symbol is not a type, it's an error which can * be detected in pass 1 already. dot_op() will emit * 'struct field expected' if sym isn't cleared. * v2.11: always clear sym. */ //if ( opnd->type != nullstruct ) sym = NULL; } } } } else { DebugMsg1(("%u get_operand: T_ID, id=%s\n", evallvl, tokenarray[i].string_ptr )); /* ensure anonym labels are uppercase */ /* v2.06: changed. Previously member 'string_ptr' was used to * store the anonymous label, but one cannot safely assume that * there's enough free space for a larger symbol name! It (partly) * worked by accident, because @F/@B usually are the last tokens * in a line [ but see: .if ( eax == @F && ecx == 2 ) ]. */ if ( *tmp == '@' && *(tmp+2 ) == NULLC ) { if ( *(tmp+1) == 'b' || *(tmp+1 ) == 'B' ) tmp = GetAnonymousLabel( labelbuff, 0 ); else if (*(tmp+1) == 'f' || *(tmp+1 ) == 'F' ) tmp = GetAnonymousLabel( labelbuff, 1 ); } sym = SymSearch( tmp ); } if ( sym == NULL || sym->state == SYM_UNDEFINED || ( sym->state == SYM_TYPE && sym->typekind == TYPE_NONE ) || /* v2.10: added */ #if ALIAS_IN_EXPR == 0 sym->state == SYM_ALIAS || /* v2.04: added */ #endif sym->state == SYM_MACRO || sym->state == SYM_TMACRO ) { /* for OPATTR, anything is ok */ if ( opnd->is_opattr ) { DebugMsg1(( "get_operand(%s): OPATTR, symbol invalid\n", tokenarray[i].string_ptr )); opnd->kind = EXPR_ERROR; break; } #if 0 /* v2.10: obsolete, since fnEmitErr() won't display anything in "EQU" mode */ /* if it is EQU, don't display an error, but return ERROR */ if ( flags & EXPF_NOERRMSG ) { DebugMsg1(("get_operand(%s): EQU, symbol invalid\n", tokenarray[i].string_ptr)); return( ERROR ); } #endif if ( sym && ( sym->state == SYM_MACRO || #if ALIAS_IN_EXPR == 0 sym->state == SYM_ALIAS || /* v2.04: added */ #endif sym->state == SYM_TMACRO ) ) { DebugMsg1(("get_operand(%s): symbol is macro/textmacro/alias!\n", tokenarray[i].string_ptr)); fnEmitErr( INVALID_SYMBOL_TYPE_IN_EXPRESSION, sym->name ); return( ERROR ); } /* v2.11: flag EXPF_NOUNDEF won't accept undefined symbols anymore. * previously, it did just avoid to create a label with state SYM_UNDEFINED - * hence the old name, EXPF_NOLCREATE */ //if( Parse_Pass == PASS_1 ) { if( Parse_Pass == PASS_1 && !( flags & EXPF_NOUNDEF ) ) { /* if symbol wasn't found, assume it is a forward ref! */ if ( sym == NULL ) { /* v2.11: flag EXPF_NOLCREATE has got another meaning */ //if ( opnd->type == NULL && !( flags & EXPF_NOLCREATE ) ) { /* added v1.95 */ if ( opnd->type == NULL ) { sym = SymLookup( tmp ); sym->state = SYM_UNDEFINED; sym_add_table( &SymTables[TAB_UNDEF], (struct dsym *)sym ); /* add UNDEFINED */ DebugMsg1(("get_operand(%s): symbol not (yet) defined, CurrProc=%s\n", tmp, CurrProc ? CurrProc->sym.name : "NULL" )); // } else if ( opnd->type == NULL || opnd->type != nullstruct ) { /* v2.08: if changed */ // } else if ( opnd->type == NULL || opnd->type->typekind != TYPE_NONE ) { /* v2.11: if changed */ } else if ( opnd->type->typekind != TYPE_NONE ) { /* no struct or struct is known and defined */ DebugMsg(("get_operand(%s): symbol error (type=%s typekind=%u)\n", tmp, opnd->type ? opnd->type->name : "NULL", opnd->type ? opnd->type->typekind : 0 )); if ( *opnd->type->name ) fnEmitErr( MEMBER_NOT_DEFINED, opnd->type->name, tmp ); else fnEmitErr( SYMBOL_NOT_DEFINED, tmp ); return( ERROR ); } else { /* forward reference to a struct. * In these cases, assume everything is ok. */ if ( !nullmbr ) { nullmbr = SymAlloc( "" ); } DebugMsg(("get_operand(%s): forward reference to a struct (using nullmbr)\n", tmp )); /* "break" because nullmbr has state SYM_UNDEFINED */ opnd->mbr = nullmbr; opnd->kind = EXPR_CONST; break; } } } else { DebugMsg1(("get_operand(%s): symbol %s not defined, pass > 1, curr proc=>%s<, \n", tokenarray[i].string_ptr, tmp, CurrProc ? CurrProc->sym.name : "NULL" )); if ( opnd->type && *opnd->type->name ) { fnEmitErr( MEMBER_NOT_DEFINED, opnd->type->name, tmp ); } else { fnEmitErr( SYMBOL_NOT_DEFINED, *(tmp+1) == '&' ? "@@" : tmp ); } return( ERROR ); } #if ALIAS_IN_EXPR /* v2.04b: added */ } else if ( sym->state == SYM_ALIAS ) { /* ALIAS symbols are not really useable in expressions. * The alias' substitute symbol is, however. */ sym = sym->substitute; /* can't be NULL */ #endif } /* set default values */ sym->used = TRUE; DebugMsg1(("get_operand(%s): sym->state=%u type=>%s< ofs=%X memtype=%Xh total_size=%u defined=%u\n", tokenarray[i].string_ptr, sym->state, sym->type ? sym->type->name : "NULL", sym->offset, sym->mem_type, sym->total_size, sym->isdefined )); switch ( sym->state ) { case SYM_TYPE: /* STRUCT, UNION, RECORD, TYPEDEF */ /* v2.09: no structinfo data for typedefs */ if ( sym->typekind != TYPE_TYPEDEF && ((struct dsym *)sym)->e.structinfo->isOpen ) { DebugMsg1(("get_operand(%s): struct/union definition isn't closed!\n", sym->name )); opnd->kind = EXPR_ERROR; break; } /* skip "alias" types */ for ( ; sym->type; sym = sym->type ); opnd->kind = EXPR_CONST; opnd->mem_type = sym->mem_type; opnd->is_type = TRUE; opnd->type = sym; DebugMsg1(("get_operand(%s): symbol.typekind=%u (STRUCT/UNION/TYPEDEF/RECORD)\n", sym->name, sym->typekind )); /* v2.08: if() removed. This was an old hack. */ //if ( tokenarray[i-1].token != T_DOT && tokenarray[i+1].token != T_DOT ) /* v2.06: the default value for RECORD types is the mask value */ if ( sym->typekind == TYPE_RECORD ) { #if AMD64_SUPPORT opnd->llvalue = GetRecordMask( (struct dsym *)sym ); #else opnd->value = GetRecordMask( (struct dsym *)sym ); #endif } else if ( ( sym->mem_type & MT_SPECIAL_MASK ) == MT_ADDRESS ) { /* v2.09: added */ if ( sym->mem_type == MT_PROC ) { opnd->value = sym->total_size; opnd->Ofssize = sym->Ofssize; } else opnd->value = GetTypeSize( sym->mem_type, sym->Ofssize ); } else opnd->value = sym->total_size; break; case SYM_STRUCT_FIELD: DebugMsg1(("get_operand(%s): structure field, ofs=%Xh\n", sym->name, sym->offset )); /* opnd->value might have been set by SearchNameInStruct() already! */ opnd->value += sym->offset; opnd->kind = EXPR_CONST; opnd->mbr = sym; /* skip "alias" types (probably obsolete by now!) */ for ( ; sym->type; sym = sym->type ); opnd->mem_type = sym->mem_type; /* * check if the member field is a type (struct or union). * If yes, set the member! * this cannot be done in PrepareOp() */ opnd->type = ( sym->state == SYM_TYPE && sym->typekind != TYPE_TYPEDEF ) ? sym : NULL; DebugMsg1(("get_operand: mem_type=%Xh type=%s\n", opnd->mem_type, opnd->type ? opnd->type->name : "NULL" )); break; default: /* SYM_INTERNAL, SYM_EXTERNAL, SYM_SEG, SYM_GRP, SYM_STACK */ opnd->kind = EXPR_ADDR; /* call internal function (@Line, ... ) */ if ( sym->predefined && sym->sfunc_ptr ) sym->sfunc_ptr( sym, NULL ); //if( opnd->sym->mem_type == MT_ABS ) { if( sym->state == SYM_INTERNAL && sym->segment == NULL ) { opnd->kind = EXPR_CONST; opnd->uvalue = sym->uvalue; opnd->hvalue = sym->value3264; DebugMsg1(("get_operand(%s): equate hval=%Xh, lval=%Xh\n", sym->name, opnd->hvalue, opnd->uvalue )); opnd->mem_type = sym->mem_type; /* don't set the symbol reference, it isn't a label */ } else if( sym->state == SYM_EXTERNAL && sym->mem_type == MT_EMPTY && sym->iscomm == FALSE ) { /* type remains EXPR_ADDR, to force fixup creation */ //opnd->mem_type = sym->mem_type; /* v2.10: unnecessary, init value IS MT_EMPTY */ opnd->is_abs = TRUE; opnd->sym = sym; } else { opnd->label_tok = &tokenarray[i]; /* a variable with arbitrary type? */ /* v2.05: added check for MT_EMPTY */ //if( opnd->sym->type ) { if( sym->type && sym->type->mem_type != MT_EMPTY ) { /* skip "alias" types */ /* v2.05: obsolete */ //for ( sym2 = opnd->sym; sym2->type; sym2 = sym2->type ); //opnd->mem_type = sym2->mem_type; opnd->mem_type = sym->type->mem_type; } else { opnd->mem_type = sym->mem_type; } /* since there is no fixup for auto variables, the "offset" must be stored in the field */ if ( sym->state == SYM_STACK ) { #if STACKBASESUPP opnd->llvalue = sym->offset + StackAdj; #else opnd->llvalue = sym->offset; #endif opnd->indirect = TRUE; /* v2.10: base register values now set here */ opnd->base_reg = &tokenarray[i]; #if STACKBASESUPP tokenarray[i].tokval = CurrProc->e.procinfo->basereg; #else tokenarray[i].tokval = basereg[ModuleInfo.Ofssize]; #endif tokenarray[i].bytval = GetRegNo( tokenarray[i].tokval ); } opnd->sym = sym; /* v2.09: added (also see change in PrepareOp() ) * and see case SYM_STRUCT_FIELD. */ for ( ; sym->type; sym = sym->type ); opnd->type = ( sym->state == SYM_TYPE && sym->typekind != TYPE_TYPEDEF ) ? sym : NULL; } break; } break; case T_STYPE: DebugMsg1(("%u get_operand: T_STYPE (>%s<, value=%X)\n", evallvl, tokenarray[i].string_ptr, tokenarray[i].tokval)); opnd->kind = EXPR_CONST; /* for types, return the size as numeric constant */ /* fixme: mem_type should be set only when used as first arg of PTR op! */ opnd->mem_type = GetMemtypeSp( tokenarray[i].tokval ); opnd->Ofssize = GetSflagsSp( tokenarray[i].tokval ); opnd->value = GetTypeSize( opnd->mem_type, opnd->Ofssize ); opnd->is_type = TRUE; opnd->type = NULL; /* v2.08: added */ break; case T_RES_ID: DebugMsg1(("%u get_operand: T_RES_ID (>%s<, value=%X)\n", evallvl, tokenarray[i].string_ptr, tokenarray[i].tokval)); if ( tokenarray[i].tokval == T_FLAT ) { /* v2.09: query NOUNDEF flag */ //if ( error_msg ) { /* don't define FLAT group in EQU expression! */ if ( ( flags & EXPF_NOUNDEF ) == 0 ) { /* v2.08 cpu check added */ if( ( ModuleInfo.curr_cpu & P_CPU_MASK ) < P_386 ) { fnEmitErr( INSTRUCTION_OR_REGISTER_NOT_ACCEPTED_IN_CURRENT_CPU_MODE ); return( ERROR ); } DefineFlatGroup(); } if ( !( opnd->sym = &ModuleInfo.flat_grp->sym ) ) return( ERROR ); opnd->label_tok = &tokenarray[i]; opnd->kind = EXPR_ADDR; } else { return( fnEmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) ); } break; case T_FLOAT: /* v2.05 */ DebugMsg1(("%u get_operand: T_FLOAT (>%s<)\n", evallvl, tokenarray[i].string_ptr )); opnd->kind = EXPR_FLOAT; opnd->float_tok = &tokenarray[i]; //opnd->ftype = ( tokenarray[i].floattype != 0 ); break; //case T_CL_BRACKET: //case T_CL_SQ_BRACKET: default: DebugMsg1(("%u get_operand: default (token=%u, string=%s)\n", evallvl, tokenarray[i].token, tokenarray[i].string_ptr)); if ( opnd->is_opattr ) { /* for OPATTR, allow any operand */ if ( tokenarray[i].token == T_FINAL || tokenarray[i].token == T_CL_BRACKET || tokenarray[i].token == T_CL_SQ_BRACKET ) /* don't go beyond T_FINAL, ) or ] ! */ return( NOT_ERROR ); break; } if ( tokenarray[i].token == T_BAD_NUM ) /* Masm complains even if in EQU-mode */ fnEmitErr( NONDIGIT_IN_NUMBER, tokenarray[i].string_ptr ); else if ( tokenarray[i].token == T_COLON ) fnEmitErr( SYNTAX_ERROR_UNEXPECTED_COLON ); else if ( isalpha( *tokenarray[i].string_ptr ) ) fnEmitErr( EXPRESSION_EXPECTED, tokenarray[i].tokpos ); /* better error msg */ else fnEmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ); return( ERROR ); } (*idx)++; DebugMsg1(("%u get_operand exit, ok, kind=%d value=%" I64_SPEC "X hvalue=%" I64_SPEC "X mem_type=%Xh abs=%u string=%s is_type=%u type=>%s< sym=%s mbr=%s\n", evallvl, opnd->kind, opnd->llvalue, opnd->hlvalue, opnd->mem_type, opnd->is_abs, opnd->quoted_string ? opnd->quoted_string->string_ptr : "NULL", opnd->is_type, opnd->type ? opnd->type->name : "NULL", opnd->sym ? opnd->sym->name : "NULL", opnd->mbr ? opnd->mbr->name : "NULL" )); return( NOT_ERROR ); } #if 0 static bool check_same( struct expr *opnd1, struct expr *opnd2, enum exprtype kind ) /**********************************************************************************/ /* Check if both tok_1 and tok_2 equal type */ { return( ( opnd1->kind == kind && opnd2->kind == kind ) ? TRUE : FALSE ); } #else #define check_same( first, second, KIND ) ( first->kind == KIND && second->kind == KIND ) #endif static bool check_both( const struct expr *opnd1, const struct expr *opnd2, enum exprtype type1, enum exprtype type2 ) /********************************************************************************************************************/ /* Check if tok_1 == type1 and tok_2 == type2 or vice versa */ { if( opnd1->kind == type1 && opnd2->kind == type2 ) return( TRUE ); if( opnd1->kind == type2 && opnd2->kind == type1 ) return( TRUE ); return( FALSE ); } static ret_code index_connect( struct expr *opnd1, const struct expr *opnd2 ) /***************************************************************************/ /* Connects the register lists. called by plus_op() and dot_op() */ { /* move opnd2.base to either opnd1.base or opnd1.idx */ if ( opnd2->base_reg != NULL ) { if ( opnd1->base_reg == NULL ) opnd1->base_reg = opnd2->base_reg; else if ( opnd1->idx_reg == NULL ) { /* v2.10: exchange base and index register. * was previously in parser.c, and only done * if -Zg was active. */ if ( opnd1->base_reg->bytval != 4 ) { /* if base isn't [E|R]SP, exchange regs */ opnd1->idx_reg = opnd1->base_reg; opnd1->base_reg = opnd2->base_reg; } else { opnd1->idx_reg = opnd2->base_reg; } } else { return( fnEmitErr( MULTIPLE_INDEX_REGISTERS_NOT_ALLOWED ) ); } opnd1->indirect = TRUE; } /* move opnd2.idx to opnd1.index - if it is free */ if( opnd2->idx_reg != NULL ) { //if ( opnd2->scale == 0 && opnd1->base_reg == NULL ) { // opnd1->base_reg = opnd2->idx_reg; //} else if ( opnd1->idx_reg == NULL ) { if ( opnd1->idx_reg == NULL ) { opnd1->idx_reg = opnd2->idx_reg; opnd1->scale = opnd2->scale; } else { return( fnEmitErr( MULTIPLE_INDEX_REGISTERS_NOT_ALLOWED ) ); } opnd1->indirect = TRUE; } return( NOT_ERROR ); } /* convert an address operand to a const operand if possible. * called for '*', '/', '+', '-' and binary (EQ,NE, ... SHL, SHR, ... OR,AND,XOR) operators. * the main purpose is: if a forward reference is included, * assume it is referencing a constant, not a label. */ static void MakeConst( struct expr *opnd ) /****************************************/ { if( ( opnd->kind != EXPR_ADDR ) || opnd->indirect ) /* v2.09: check for indirect added */ return; if( opnd->sym ) { if ( Parse_Pass > PASS_1 ) return; /* added for v1.94: if the evaluator assumed an address because * the label wasn't defined yet, then negate this. Also, an * EXTERNDEF:ABS is to be accepted. * v2.07: if the "not yet defined" label was an argument of * an (OFFSET) operator, do NOT change the type! */ if ( ( opnd->sym->state == SYM_UNDEFINED && opnd->instr == EMPTY ) || ( opnd->sym->state == SYM_EXTERNAL && opnd->sym->weak == TRUE && opnd->is_abs == TRUE ) ) ; else return; /* assume a value != 0 to avoid problems with div */ opnd->value = 1; } opnd->label_tok = NULL; if( opnd->mbr != NULL ) { if( opnd->mbr->state == SYM_STRUCT_FIELD ) { #if 0 /* v2.09: mbr can only be SYM_STRUCT_FIELD or SYM_UNDEFINED (if nullmbr) */ } else if( opnd->mbr->state == SYM_TYPE ) { opnd->value += opnd->mbr->total_size; opnd->mbr = NULL; #endif } else { return; } } #if 0 /* v2.09: obsolete */ if( opnd->base_reg != NULL ) return; if( opnd->idx_reg != NULL ) return; #endif if( opnd->override != NULL ) return; opnd->instr = EMPTY; opnd->kind = EXPR_CONST; //opnd->indirect = FALSE; /* not needed */ opnd->explicit = FALSE; opnd->mem_type = MT_EMPTY; } /* used by EQ, NE, GT, GE, LE, LT if item is a direct address */ static ret_code MakeConst2( struct expr *opnd1, struct expr *opnd2 ) /******************************************************************/ { if ( opnd1->sym->state == SYM_EXTERNAL ) { return( fnEmitErr( INVALID_USE_OF_EXTERNAL_SYMBOL, opnd1->sym->name ) ); } else if ( ( opnd1->sym->segment != opnd2->sym->segment && /* v2.07: ignore segments if at least one label is a fwd ref */ opnd1->sym->state != SYM_UNDEFINED && opnd2->sym->state != SYM_UNDEFINED ) || opnd2->sym->state == SYM_EXTERNAL ) { return( fnEmitErr( OPERANDS_MUST_BE_IN_SAME_SEGMENT ) ); } opnd1->kind = EXPR_CONST; opnd1->value += opnd1->sym->offset; opnd2->kind = EXPR_CONST; opnd2->value += opnd2->sym->offset; return( NOT_ERROR ); } static ret_code ConstError( struct expr *opnd1, struct expr *opnd2 ) /******************************************************************/ { if ( opnd1->is_opattr ) return( NOT_ERROR ); if ( opnd1->kind == EXPR_FLOAT || opnd2->kind == EXPR_FLOAT ) fnEmitErr( REAL_OR_BCD_NUMBER_NOT_ALLOWED ); else fnEmitErr( CONSTANT_EXPECTED ); return( ERROR ); } /* used by + and - binary operators */ static void fix_struct_value( struct expr *opnd ) /***********************************************/ { if( opnd->mbr && ( opnd->mbr->state == SYM_TYPE ) ) { opnd->value += opnd->mbr->total_size; opnd->mbr = NULL; } } static int check_direct_reg( const struct expr *opnd1, const struct expr *opnd2 ) /*******************************************************************************/ { if( ( opnd1->kind == EXPR_REG ) && ( opnd1->indirect == FALSE ) || ( opnd2->kind == EXPR_REG ) && ( opnd2->indirect == FALSE ) ) { return( ERROR ); } return( NOT_ERROR ); } static unsigned GetSizeValue( struct asym *sym ) /**********************************************/ { if ( sym->mem_type == MT_PTR ) return( SizeFromMemtype( sym->isfar ? MT_FAR : MT_NEAR, sym->Ofssize, sym->type ) ); return( SizeFromMemtype( sym->mem_type, sym->Ofssize, sym->type ) ); } static unsigned IsOffset( struct expr *opnd ) /*******************************************/ { if ( opnd->mem_type == MT_EMPTY ) if ( opnd->instr == T_OFFSET || #if IMAGERELSUPP opnd->instr == T_IMAGEREL || #endif #if SECTIONRELSUPP opnd->instr == T_SECTIONREL || #endif opnd->instr == T_LROFFSET ) return( 1 ); return( 0 ); } static ret_code invalid_operand( struct expr *opnd, char *oprtr, char *operand ) /******************************************************************************/ { if ( !opnd->is_opattr ) fnEmitErr( INVALID_OPERAND_FOR_OPERATOR, _strupr( oprtr), operand ); return( ERROR ); } /* operators * LENGTH: number of items of first initializer's first dimension * SIZE: size in bytes of first initializer's first dimension * LENGTHOF: number of elements in an array * SIZEOF: size in bytes of item (array/struct) * * these operators accept structure fields, stack variables and data labels. * the old SIZE and LENGTH ops also accept code labels (memtype NEAR/FAR). * in Masm, symbolic constants (defined with EQU or =) are * also accepted, but no plain numbers!? */ static ret_code sizlen_op( int oper, struct expr *opnd1, struct expr *opnd2, struct asym *sym, char *name ) /*********************************************************************************************************/ { opnd1->kind = EXPR_CONST; DebugMsg1(("sizlen_op(%s): sym=%X, mbr=%X, type=>%s<\n", GetResWName( oper, NULL ), opnd2->sym, opnd2->mbr, opnd2->type ? opnd2->type->name : "NULL" )); if ( sym ) { if ( sym->state == SYM_STRUCT_FIELD || sym->state == SYM_STACK ) ; else if ( sym->state == SYM_UNDEFINED ) { /* v2.10: forward references should have attributes EXPR_ADDR + sym.state=SYM_UNDEFINED */ opnd1->kind = EXPR_ADDR; opnd1->sym = sym; } else if ( ( sym->state == SYM_EXTERNAL || sym->state == SYM_INTERNAL) && //sym->mem_type != MT_ABS && sym->mem_type != MT_EMPTY && //sym->mem_type != MT_PROC && /* MT_PROC probably obsolete */ sym->mem_type != MT_FAR && sym->mem_type != MT_NEAR ) ; else if ( sym->state == SYM_GRP || sym->state == SYM_SEG ) { return( fnEmitErr( EXPECTED_DATA_LABEL ) ); } else if ( oper == T_SIZE || oper == T_LENGTH ) ; else { return( fnEmitErr( EXPECTED_DATA_LABEL ) ); } } switch( oper ) { case T_LENGTH: /* data items and struct fields have a "first" count. * for procedure locals (+arguments) and code labels, always 1 is returned. */ /* v2.09: first_length is valid if isdata is set */ //opnd1->value = ( sym->state != SYM_STACK && sym->isarray ) ? sym->first_length : 1; opnd1->value = sym->isdata ? sym->first_length : 1; break; case T_LENGTHOF: /* LENGTHOF needs either a data label or a structure field */ /* a TYPE (structure, typedef) is invalid */ if( opnd2->kind == EXPR_CONST ) { opnd1->value = opnd2->mbr->total_length; #if 0 /* v2.09: unnecessary */ } else if( sym->state == SYM_UNDEFINED && Parse_Pass == PASS_1 ) { opnd1->value = sym->total_length; #endif } else if ( sym->state == SYM_EXTERNAL && sym->iscomm == FALSE ) { /* for externals other than COMM, total_length field is used otherwise */ opnd1->value = 1; } else { opnd1->value = sym->total_length; } break; case T_SIZE: /* v2.04: first_size is no longer set for SYM_STACK. */ if( sym == NULL ) { /* v2.09: check memtype */ if ( ( opnd2->mem_type & MT_SPECIAL_MASK ) == MT_ADDRESS ) opnd1->value = 0xFF00 | opnd2->value; else opnd1->value = opnd2->value; } else if ( sym->isdata ) { opnd1->value = sym->first_size; #if 0 /* v2.09: can't happen, since for a type, sym is NULL */ } else if( sym->state == SYM_TYPE ) { opnd1->value = sym->total_size; #endif } else if( sym->state == SYM_STACK ) { opnd1->value = GetSizeValue( sym ); } else if( sym->mem_type == MT_NEAR ) { /* v2.09: also handle 64-bit */ //opnd1->value = GetSymOfssize( sym ) ? LS_NEAR32 : LS_NEAR16; opnd1->value = 0xFF00 | ( 2 << GetSymOfssize( sym ) ); } else if( sym->mem_type == MT_FAR ) { opnd1->value = GetSymOfssize( sym ) ? LS_FAR32 : LS_FAR16; } else { opnd1->value = GetSizeValue( sym ); } DebugMsg1(("sizlen_op(SIZE): result=%u [symbol %s, first_size=%u]\n", opnd1->value, sym ? sym->name : "NULL", sym ? sym->first_size : 0 )); break; case T_SIZEOF: #ifdef DEBUG_OUT if (sym) DebugMsg1(("sizlen_op(sizeof): symbol %s, state=%u, size=%u\n", sym->name, sym->state, sym->total_size )); else if ( opnd2->is_type && opnd2->type ) DebugMsg1(("sizlen_op(sizeof): symbol %s (TYPE), opnd2.value=%u\n", opnd2->type->name, opnd2->value )); else DebugMsg1(("sizlen_op(sizeof): symbol NULL, opnd2.value=%u\n", opnd2->value )); #endif /* if sym is NULL, then operand is a type constant */ if ( sym == NULL ) { /* v2.06: default value of RECORD types is the mask! */ if ( opnd2->is_type && opnd2->type && opnd2->type->typekind == TYPE_RECORD ) opnd1->value = opnd2->type->total_size; else opnd1->value = opnd2->value; #if 1 /* v2.05: don't use total_size for externals anymore! */ } else if ( sym->state == SYM_EXTERNAL && sym->iscomm == FALSE ) { opnd1->value = GetSizeValue( sym ); //if ( sym->iscomm == TRUE ) // opnd1->value *= sym->total_length; #endif } else opnd1->value = sym->total_size; break; } return( NOT_ERROR ); } /* TYPE operator */ static ret_code type_op( int oper, struct expr *opnd1, struct expr *opnd2, struct asym *sym, char *name ) /*******************************************************************************************************/ { DebugMsg1(("type_op: opnd2 kind=%d memtype=%X sym=%s type=%s instr=%d istype=%u explicit=%u\n", opnd2->kind, opnd2->mem_type, sym ? sym->name : "NULL", opnd2->type ? opnd2->type->name : "NULL", opnd2->instr, opnd2->is_type, opnd2->explicit )); opnd1->kind = EXPR_CONST; /* TYPE accepts arrays/structs/unions */ /* v2.11: if memtype isn't empty, ignore any unary operator * test cases: * - type qword ptr sym. * - type qword ptr offset sym * operators LOW, HIGH, LOWWORD, HIGHWORD, LOW32, HIGH32, * OFFSET, IMAGEREL, SECTIONREL and LROFFSET * will set opnd.memtype to MT_EMPTY. */ if( opnd2->instr != EMPTY && opnd2->mem_type != MT_EMPTY ) { opnd2->instr = EMPTY; sym = NULL; } if( opnd2->instr != EMPTY ) { if ( opnd2->sym ) { switch ( opnd2->instr ) { case T_LOW: case T_HIGH: opnd1->value = 1; break; case T_LOWWORD: case T_HIGHWORD: //case T_SEG: /* masm returns 0 for TYPE SEG