/**************************************************************************** * * This code is Public Domain. * * ======================================================================== * * Description: handles EQU and '=' directives. * ****************************************************************************/ #include #include "globals.h" #include "memalloc.h" #include "parser.h" #include "expreval.h" #include "equate.h" #include "tokenize.h" #include "macro.h" #include "fastpass.h" #include "listing.h" #include "input.h" #include "fixup.h" #include "myassert.h" extern void myatoi128( const char *, uint_64[], int, int ); #if defined(LLONG_MAX) || defined(__GNUC__) || defined(__TINYC__) /* gcc needs suffixes if the constants won't fit in long type */ const int_64 maxintvalues[] = { 0x00000000ffffffffULL, 0x00000000ffffffffULL, #if AMD64_SUPPORT 0x7fffffffffffffffULL #endif }; const int_64 minintvalues[] = { 0xffffffff00000000ULL, 0xffffffff00000000ULL, #if AMD64_SUPPORT 0x8000000000000000ULL #endif }; #else /* the "i64" suffixes shouldn't be necessary, but sometimes it's needed (OCC) */ const int_64 maxintvalues[] = { 0x00000000ffffffffi64, 0x00000000ffffffffi64, #if AMD64_SUPPORT 0x7fffffffffffffffi64 #endif }; const int_64 minintvalues[] = { 0xffffffff00000000i64, 0xffffffff00000000i64, #if AMD64_SUPPORT 0x8000000000000000i64 #endif }; #endif /* set the value of a constant (EQU) or an assembly time variable (=) */ static void SetValue( struct asym *sym, struct expr *opndx ) /**********************************************************/ { sym->isequate = TRUE; sym->state = SYM_INTERNAL; sym->isdefined = TRUE; if ( opndx->kind == EXPR_CONST ) { /* v2.07: use expression's memtype */ //sym->mem_type = MT_ABS; sym->mem_type = opndx->mem_type; sym->uvalue = opndx->uvalue; sym->value3264 = opndx->hvalue; sym->segment = NULL; sym->isproc = FALSE; } else { sym->isproc = opndx->sym->isproc; /* for a PROC alias, copy the procinfo extension! */ if ( sym->isproc ) { struct dsym *dir = (struct dsym *)sym; dir->e.procinfo = ((struct dsym *)opndx->sym)->e.procinfo; } sym->mem_type = opndx->mem_type; /* v2.01: allow equates of variables with arbitrary type. * Currently the expression evaluator sets opndx.mem_type * to the mem_type of the type (i.e. QWORD for a struct with size 8), * which is a bad idea in this case. So the original mem_type of the * label is used instead. */ if ( opndx->sym->mem_type == MT_TYPE && opndx->explicit == FALSE ) { sym->mem_type = opndx->sym->mem_type; sym->type = opndx->sym->type; } sym->value3264 = 0; /* v2.09: added */ sym->segment = opndx->sym->segment; #if 1 /* v2.10: added */ /* labels are supposed to be added to the current segment's label_list chain. * this isn't done for alias equates, for various reasons. * consequently, if the alias was forward referenced, ensure that a third pass * will be done! regression test forward5.asm. */ if ( sym->variable ) { sym->offset = opndx->sym->offset + opndx->value; if ( Parse_Pass == PASS_2 && sym->fwdref ) { #ifdef DEBUG_OUT if ( !ModuleInfo.PhaseError ) DebugMsg(("SetValue(%s): Phase error, enforced by alias equate\n", sym->name )); #endif ModuleInfo.PhaseError = TRUE; } } else { if( Parse_Pass != PASS_1 && sym->offset != ( opndx->sym->offset + opndx->value ) ) { #ifdef DEBUG_OUT if ( !ModuleInfo.PhaseError ) DebugMsg1(("SetValue(%s): Phase error, enforced by alias equate %" I32_SPEC "X != %" I32_SPEC "X\n", sym->name, sym->offset, opndx->sym->offset + opndx->value )); #endif ModuleInfo.PhaseError = TRUE; } sym->offset = opndx->sym->offset + opndx->value; BackPatch( sym ); } #endif } return; } /* the '=' directive defines an assembly time variable. * this can only be a number (constant or relocatable). */ static struct asym *CreateAssemblyTimeVariable( struct asm_tok tokenarray[] ) /***************************************************************************/ { struct asym *sym; const char *name = tokenarray[0].string_ptr; int i = 2; struct expr opnd; DebugMsg1(( "CreateAssemblyTimeVariable(%s) enter\n", name )); /* v2.08: for plain numbers ALWAYS avoid to call evaluator */ if ( tokenarray[2].token == T_NUM && //tokenarray[3].token == T_FINAL && //tokenarray[2].itemlen <= 8 ) { tokenarray[3].token == T_FINAL ) { //opnd.llvalue = tokenarray[2].value64; //opnd.llvalue = *(uint_64 *)(tokenarray[2].string_ptr - sizeof(uint_64) ); myatoi128( tokenarray[i].string_ptr, &opnd.llvalue, tokenarray[i].numbase, tokenarray[i].itemlen ); check_number: opnd.kind = EXPR_CONST; opnd.mem_type = MT_EMPTY; /* v2.07: added */ /* v2.08: check added. the number must be 32-bit */ if ( opnd.hlvalue != 0 || opnd.value64 < minintvalues[ModuleInfo.Ofssize] || opnd.value64 > maxintvalues[ModuleInfo.Ofssize] ) { EmitConstError( &opnd ); return( NULL ); } } else { /* v2.09: don't create not-(yet)-defined symbols. Example: * E1 = E1 or 1 * must NOT create E1. */ if ( EvalOperand( &i, tokenarray, Token_Count, &opnd, 0 ) == ERROR ) return( NULL ); if( tokenarray[i].token != T_FINAL ) { EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ); return( NULL ); } /* expression may be a constant or a relocatable item. * v2.09: kind may be EXPR_CONST and still include an undefined symbol. * This is caused by MakeConst() in expreval.c. Brackets changed so * opnd.sym is also checked for opnd.kind == EXPR_CONST. */ if( opnd.kind != EXPR_CONST && ( opnd.kind != EXPR_ADDR || opnd.indirect == TRUE ) || ( opnd.sym != NULL && opnd.sym->state != SYM_INTERNAL ) ) { DebugMsg(( "CreateAssemblyTimeVariable(%s) kind=%u sym=%p state=%u\n", name, opnd.kind, opnd.sym, opnd.sym ? opnd.sym->state : 0 )); /* v2.09: no error if argument is a forward reference, * but don't create the variable either. Will enforce an * error if referenced symbol is still undefined in pass 2. */ if( opnd.sym && opnd.sym->state == SYM_UNDEFINED && opnd.indirect == FALSE ) { #if FASTPASS if ( StoreState == FALSE ) FStoreLine(0); /* make sure this line is evaluated in pass two */ #endif } else EmitError( CONSTANT_EXPECTED ); return( NULL ); } /* v2.08: accept any result that fits in 64-bits from expression evaluator */ if ( opnd.hlvalue != 0 ) { EmitConstError( &opnd ); return( NULL ); } /* for quoted strings, the same restrictions as for plain numbers apply */ if ( opnd.quoted_string ) goto check_number; } sym = SymSearch( name ); if( sym == NULL || sym->state == SYM_UNDEFINED ) { if( sym == NULL ) { sym = SymCreate( name ); } else { sym_remove_table( &SymTables[TAB_UNDEF], (struct dsym *)sym ); sym->fwdref = TRUE; } //sym->variable = TRUE; #if FASTPASS sym->issaved = StoreState; /* v2.10: added */ #endif //} else if ( sym->state == SYM_EXTERNAL && sym->weak == TRUE && sym->mem_type == MT_ABS ) { } else if ( sym->state == SYM_EXTERNAL && sym->weak == TRUE && sym->mem_type == MT_EMPTY ) { sym_ext2int( sym ); //sym->variable = TRUE; #if FASTPASS sym->issaved = StoreState; /* v2.10: added */ #endif } else { if ( sym->state != SYM_INTERNAL || ( sym->variable == FALSE && ( opnd.uvalue != sym->uvalue || opnd.hvalue != sym->value3264 ) ) ) { EmitErr( SYMBOL_REDEFINITION, sym->name ); return( NULL ); } #if FASTPASS /* v2.04a regression in v2.04. Do not save the variable when it * is defined the first time * v2.10: store state only when variable is changed and has been * defined BEFORE SaveState() has been called. */ //if ( StoreState && sym->issaved == FALSE && sym->isdefined == TRUE ) { if ( StoreState && sym->issaved == FALSE ) { SaveVariableState( sym ); } #endif } sym->variable = TRUE; #ifdef DEBUG_OUT if ( Parse_Pass > PASS_1 ) { if ( opnd.kind == EXPR_CONST && sym->uvalue != opnd.uvalue ) DebugMsg1(( "CreateAssemblyTimeVariable(%s): kind=%u, value changed ( %d -> %d )\n", name, opnd.kind, sym->uvalue, opnd.uvalue ) ); else if ( opnd.kind == EXPR_ADDR && sym->uvalue != ( opnd.uvalue + opnd.sym->uvalue ) ) DebugMsg1(( "CreateAssemblyTimeVariable(%s): kind=%u, value changed ( %d -> %d )\n", name, opnd.kind, sym->uvalue, opnd.uvalue + opnd.sym->uvalue ) ); } #endif /* v2.09: allow internal variables to be set */ if ( sym->predefined && sym->sfunc_ptr ) sym->sfunc_ptr( sym, &opnd ); else SetValue( sym, &opnd ); DebugMsg1(( "CreateAssemblyTimeVariable(%s) memtype=%Xh value=%d\n", name, sym->mem_type, sym->value )); return( sym ); } /* '=' directive.*/ ret_code EqualSgnDirective( int i, struct asm_tok tokenarray[] ) /**************************************************************/ { struct asym *sym; if( tokenarray[0].token != T_ID ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[0].string_ptr ) ); } if ( (sym = CreateAssemblyTimeVariable( tokenarray )) != NULL ) { if ( ModuleInfo.list == TRUE ) { LstWrite( LSTTYPE_EQUATE, 0, sym ); } return( NOT_ERROR ); } return( ERROR ); } /* CreateVariable(). * define an assembly time variable directly without using the token buffer. * this is used for some internally generated variables (SIZESTR, INSTR, @Cpu) * NO listing is written! The value is ensured to be max 16-bit wide. */ struct asym *CreateVariable( const char *name, int value ) /********************************************************/ { struct asym *sym; DebugMsg1(( "CreateVariable(%s, %d ) enter\n", name, value )); sym = SymSearch( name ); if( sym == NULL ) { sym = SymCreate( name ); #if FASTPASS //sym->issaved = FALSE; sym->issaved = StoreState; /* v2.10 */ #endif } else if ( sym->state == SYM_UNDEFINED ) { sym_remove_table( &SymTables[TAB_UNDEF], (struct dsym *)sym ); sym->fwdref = TRUE; #if FASTPASS sym->issaved = StoreState; /* v2.10 */ #endif } else { if ( sym->isequate == FALSE ) { EmitErr( SYMBOL_REDEFINITION, name ); return( NULL ); } #if FASTPASS /* * v2.09: don't save variable when it is defined the first time * v2.10: store state only when variable is changed and has been * defined BEFORE SaveState() has been called. */ //if ( StoreState && sym->issaved == FALSE && sym->isdefined == TRUE ) { if ( StoreState && sym->issaved == FALSE ) { SaveVariableState( sym ); } #endif } sym->isdefined = TRUE; sym->state = SYM_INTERNAL; //sym->mem_type = MT_ABS; sym->variable = TRUE; sym->value = value; sym->value3264 = 0; sym->isequate = TRUE; return( sym ); } /* * CreateConstant() * this is the worker behind EQU. * EQU may define 3 different types of equates: * - numeric constants (with or without "type") * - relocatable items ( aliases ) * - text macros * the argument may be * - an expression which can be evaluated to a number or address * - a text literal (enclosed in <>) * - anything. This will also become a text literal. */ struct asym *CreateConstant( struct asm_tok tokenarray[] ) /********************************************************/ { struct asym *sym; const char *name = tokenarray[0].string_ptr; int i = 2; ret_code rc; char *p; bool cmpvalue = FALSE; struct expr opnd; char argbuffer[MAX_LINE_LEN]; DebugMsg1(( "CreateConstant(%s) enter\n", name )); sym = SymSearch( name ); /* if a literal follows, the equate MUST be(come) a text macro */ if ( tokenarray[2].token == T_STRING && tokenarray[2].string_delim == '<' ) return ( SetTextMacro( tokenarray, sym, name, NULL ) ); if( sym == NULL || sym->state == SYM_UNDEFINED || ( sym->state == SYM_EXTERNAL && sym->weak == TRUE && sym->isproc == FALSE ) ) { /* It's a "new" equate. * wait with definition until type of equate is clear */ } else if( sym->state == SYM_TMACRO ) { return ( SetTextMacro( tokenarray, sym, name, tokenarray[2].tokpos ) ); } else if( sym->isequate == FALSE ) { DebugMsg1(( "CreateConstant(%s) state=%u, mem_type=%Xh, value=%" I32_SPEC "X, symbol redefinition\n", name, sym->state, sym->mem_type, sym->value)); EmitErr( SYMBOL_REDEFINITION, name ); return( NULL ); } else { if ( sym->asmpass == ( Parse_Pass & 0xFF ) ) cmpvalue = TRUE; sym->asmpass = Parse_Pass; } /* try to evaluate the expression */ if ( tokenarray[2].token == T_NUM && Token_Count == 3 ) { p = tokenarray[2].string_ptr; do_single_number: /* value is a plain number. it will be accepted only if it fits into 32-bits. * Else a text macro is created. */ myatoi128( tokenarray[2].string_ptr, &opnd.llvalue, tokenarray[2].numbase, tokenarray[2].itemlen ); check_single_number: opnd.instr = EMPTY; opnd.kind = EXPR_CONST; opnd.mem_type = MT_EMPTY; /* v2.07: added */ opnd.flags1 = 0; /* v2.08: does it fit in 32-bits */ if ( opnd.hlvalue == 0 && opnd.value64 >= minintvalues[ModuleInfo.Ofssize] && opnd.value64 <= maxintvalues[ModuleInfo.Ofssize] ) { rc = NOT_ERROR; DebugMsg1(( "CreateConstant(%s): simple numeric value=%" I64_SPEC "d\n", name, opnd.value64 )); i++; } else return ( SetTextMacro( tokenarray, sym, name, p ) ); } else { p = tokenarray[2].tokpos; if ( Parse_Pass == PASS_1 ) { /* if the expression cannot be evaluated to a numeric value, * it's to become a text macro. The value of this macro will be * the original (unexpanded!) line - that's why it has to be * saved here to argbuffer[]. */ strcpy( argbuffer, p ); DebugMsg1(("CreateConstant(%s): before ExpandLineItems: >%s<\n", name, p )); /* expand EQU argument (macro functions won't be expanded!) */ if ( ExpandLineItems( p, 2, tokenarray, FALSE, TRUE ) ) /* v2.08: if expansion result is a plain number, handle is specifically. * this is necessary because values of expressions may be 64-bit now. */ p = argbuffer; /* ensure that p points to unexpanded source */ if ( tokenarray[2].token == T_NUM && Token_Count == 3 ) { goto do_single_number; } DebugMsg1(("CreateConstant(%s): after ExpandLineItems: >%s<\n", name, p )); } rc = EvalOperand( &i, tokenarray, Token_Count, &opnd, EXPF_NOERRMSG | EXPF_NOUNDEF ); /* v2.08: if it's a quoted string, handle it like a plain number */ /* v2.10: quoted_string field is != 0 if kind == EXPR_FLOAT, * so this is a regression in v2.08-2.09. */ if ( opnd.quoted_string && opnd.kind == EXPR_CONST ) { i--; /* v2.09: added; regression in v2.08 and v2.08a */ goto check_single_number; } /* check here if last token has been reached? */ } /* what is an acceptable 'number' for EQU? * 1. a numeric value - if magnitude is <= 64 (or 32, if it's a plain number) * This includes struct fields. * 2. an address - if it is direct, has a label and is of type SYM_INTERNAL - * that is, no forward references, no seg, groups, externals; * Anything else will be stored as a text macro. * v2.04: large parts rewritten. */ if ( rc != ERROR && tokenarray[i].token == T_FINAL && ( ( opnd.kind == EXPR_CONST && opnd.hlvalue == 0 ) || /* magnitude <= 64 bits? */ ( opnd.kind == EXPR_ADDR && opnd.indirect == FALSE && opnd.sym != NULL && //opnd.sym->state != SYM_EXTERNAL ) ) && /* SYM_SEG, SYM_GROUP are also not ok */ opnd.sym->state == SYM_INTERNAL ) ) && ( opnd.instr == EMPTY ) ) { if ( !sym ) { sym = SymCreate( name ); sym->asmpass = Parse_Pass; } else if ( sym->state == SYM_UNDEFINED ) { sym_remove_table( &SymTables[TAB_UNDEF], (struct dsym *)sym ); sym->fwdref = TRUE; } else if ( sym->state == SYM_EXTERNAL ) { sym_ext2int( sym ); } else if ( cmpvalue ) { if ( opnd.kind == EXPR_CONST ) { /* for 64bit, it may be necessary to check 64bit value! */ /* v2.08: always compare 64-bit values */ //if ( sym->value != opnd.value ) { if ( sym->value != opnd.value || sym->value3264 != opnd.hvalue ) { DebugMsg(("CreateConstant(%s), CONST value changed: old=%X, new=%X\n", name, sym->offset, opnd.value )); EmitErr( SYMBOL_REDEFINITION, name ); return( NULL ); } } else if ( opnd.kind == EXPR_ADDR ) { if ( ( sym->offset != ( opnd.sym->offset + opnd.value ) ) || ( sym->segment != opnd.sym->segment ) ) { DebugMsg(("CreateConstant(%s), ADDR value changed: old=%X, new ofs+val=%X+%X\n", name, sym->offset, opnd.sym->offset, opnd.value)); EmitErr( SYMBOL_REDEFINITION, name ); return( NULL ); } } } /* change from alias to number is ok if value (=offset) won't change! * memtype must not be checked! */ //if ( opnd.kind == EXPR_CONST ) { // if ( sym->mem_type != MT_ABS && sym->mem_type != MT_EMPTY ) { // EmitErr( SYMBOL_REDEFINITION, name ); // return( NULL ); // } //} sym->variable = FALSE; SetValue( sym, &opnd ); DebugMsg1(("CreateConstant(%s): memtype=%Xh value=%" I64_SPEC "X isproc=%u variable=%u type=%s\n", name, sym->mem_type, (uint_64)sym->value + ( (uint_64)sym->value3264 << 32), sym->isproc, sym->variable, sym->type ? sym->type->name : "NULL" )); return( sym ); } DebugMsg1(("CreateConstant(%s): calling SetTextMacro() [MI.Ofssize=%u]\n", name, ModuleInfo.Ofssize )); return ( SetTextMacro( tokenarray, sym, name, argbuffer ) ); } /* EQU directive. * This function is called rarely, since EQU * is a preprocessor directive handled directly inside PreprocessLine(). * However, if fastpass is on, the preprocessor step is skipped in * pass 2 and later, and then this function may be called. */ ret_code EquDirective( int i, struct asm_tok tokenarray[] ) /*********************************************************/ { struct asym *sym; if( tokenarray[0].token != T_ID ) { return( EmitErr( SYNTAX_ERROR_EX, tokenarray[0].string_ptr ) ); } DebugMsg1(("EquDirective(%s): calling CreateConstant\n", tokenarray[0].string_ptr )); if ( (sym = CreateConstant( tokenarray )) != NULL ) { /**/myassert( sym->state == SYM_INTERNAL ); /* must not be a text macro */ if ( ModuleInfo.list == TRUE ) { LstWrite( LSTTYPE_EQUATE, 0, sym ); } return( NOT_ERROR ); } return( ERROR ); }