jwasm/equate.c
qWord 33e0c070c7 Reuse of equates in CreateVariable was erroneous
If an equate already exist CreateVariable did not zero bits 32...63
of that equate. This cause that the predefined macros SIZESTR
and INSTR return wrong values when the result-equate (for example)
did contain negative values before macro invocation.

Fixed by setting sym->value3264 to zero in case of reuse.

Resolves: #156 resp. SF-BUG-303
2016-05-06 19:31:13 +02:00

525 lines
21 KiB
C

/****************************************************************************
*
* This code is Public Domain.
*
* ========================================================================
*
* Description: handles EQU and '=' directives.
*
****************************************************************************/
#include <ctype.h>
#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 );
}