6502/vasm/expr.c

1465 lines
31 KiB
C

/* expr.c expression handling for vasm */
/* (c) in 2002-2019 by Volker Barthelmann and Frank Wille */
#include "vasm.h"
char current_pc_char='$';
int unsigned_shift;
static char *s;
static symbol *cpc;
static int make_tmp_lab;
static int exp_type;
#ifndef EXPSKIP
#define EXPSKIP() s=expskip(s)
#endif
static expr *expression();
static char *expskip(char *s)
{
s=skip(s);
if(*s==commentchar) *s='\0'; /* rest of line is commented out */
return s;
}
expr *new_expr(void)
{
expr *new=mymalloc(sizeof(*new));
new->left=new->right=0;
return new;
}
expr *make_expr(int type,expr *left,expr *right)
{
expr *new=mymalloc(sizeof(*new));
new->left=left;
new->right=right;
new->type=type;
return new;
}
expr *copy_tree(expr *old)
{
expr *new=0;
if(old){
new=make_expr(old->type,copy_tree(old->left),copy_tree(old->right));
new->c=old->c;
}
return new;
}
expr *curpc_expr(void)
{
expr *new=new_expr();
if(!cpc){
cpc=new_import(" *current pc dummy*");
cpc->type=LABSYM;
cpc->flags|=VASMINTERN|PROTECTED;
}
new->type=SYM;
new->c.sym=cpc;
return new;
}
static void update_curpc(expr *exp,section *sec,taddr pc)
{
if(exp->c.sym==cpc&&sec!=NULL){
cpc->sec=sec;
cpc->pc=pc;
if(sec->flags&ABSOLUTE)
cpc->flags|=ABSLABEL;
else
cpc->flags&=~ABSLABEL;
}
}
static expr *primary_expr(void)
{
expr *new;
char *m,*name;
int base;
if(*s=='('){
s++;
EXPSKIP();
new=expression();
if(*s!=')')
general_error(6,')');
else
s++;
EXPSKIP();
return new;
}
if(name=get_local_label(&s)){
symbol *sym=find_symbol(name);
if(!sym)
sym=new_import(name);
sym->flags|=USED;
if (sym->type!=EXPRESSION){
new=new_expr();
new->type=SYM;
new->c.sym=sym;
}else
new=copy_tree(sym->expr);
myfree(name);
return new;
}
m=const_prefix(s,&base);
if(base!=0){
char *start=s;
utaddr val,nval;
thuge huge;
tfloat flt;
switch(exp_type){
case NUM:
s=m;
val=0;
if(base<=10){
while(*s>='0'&&*s<base+'0'){
nval=base*val;
if (nval/base!=val)
goto hugeval; /* taddr overflow, read a thuge value instead */
val=nval+*s++-'0';
}
if(base==10&&(*s=='e'||*s=='E'||(*s=='.'&&*(s+1)>='0'&&*(s+1)<='9')))
goto fltval; /* decimal point or exponent: read floating point */
}else if(base==16){
for(;;){
nval=val<<4;
if(*s>='0'&&*s<='9')
nval+=*s++-'0';
else if(*s>='a'&&*s<='f')
nval+=*s++-'a'+10;
else if(*s>='A'&&*s<='F')
nval+=*s++-'A'+10;
else break;
if (nval>>4!=val)
goto hugeval; /* taddr overflow, read a thuge value instead */
else
val=nval;
}
}else ierror(0);
break;
hugeval:
exp_type=HUG;
case HUG:
s=m;
huge=huge_zero();
if(base<=10){
while(*s>='0'&&*s<base+'0')
huge=haddi(hmuli(huge,base),*s++-'0');
if(base==10&&(*s=='e'||*s=='E'||
(*s=='.'&&*(s+1)>='0'&&*(s+1)<='9')))
goto fltval; /* decimal point or exponent: read floating point */
}else if(base==16){
for(;;){
if(*s>='0'&&*s<='9')
huge=haddi(hmuli(huge,16),*s++-'0');
else if(*s>='a'&&*s<='f')
huge=haddi(hmuli(huge,16),*s++-'a'+10);
else if(*s>='A'&&*s<='F')
huge=haddi(hmuli(huge,16),*s++-'A'+10);
else break;
}
}else ierror(0);
break;
fltval:
exp_type=FLT;
case FLT:
if(base!=10) goto hugeval;
s=m;
flt=strtotfloat(s,&s);
break;
default:
ierror(0);
break;
}
s=const_suffix(start,s);
EXPSKIP();
new=new_expr();
switch(new->type=exp_type){
case NUM: new->c.val=val; break;
case HUG: new->c.huge=huge; break;
case FLT: new->c.flt=flt; break;
}
return new;
}
if(*s==current_pc_char && !ISIDCHAR(*(s+1))){
s++;
EXPSKIP();
if(make_tmp_lab){
new=new_expr();
new->type=SYM;
new->c.sym=new_tmplabel(0);
add_atom(0,new_label_atom(new->c.sym));
}else new=curpc_expr();
return new;
}
if(name=parse_identifier(&s)){
symbol *sym;
EXPSKIP();
sym=find_symbol(name);
if(!sym){
#ifdef NARGSYM
if(!strcmp(name,NARGSYM)){
new=new_expr();
new->type=NUM;
new->c.val=cur_src->num_params; /*@@@ check for macro mode? */
return new;
}
#endif
sym=new_import(name);
}
sym->flags|=USED;
if (sym->type!=EXPRESSION){
new=new_expr();
new->type=SYM;
new->c.sym=sym;
}
else
new=copy_tree(sym->expr);
myfree(name);
return new;
}
if(*s=='\''||*s=='\"'){
taddr val=0;
int shift=0,cnt=0;
char quote=*s++;
while(*s){
char c;
if(*s=='\\')
s=escape(s,&c);
else{
c=*s++;
if(c==quote){
if(*s==quote)
s++; /* allow """" to be recognized as " and '''' as ' */
else break;
}
}
if(++cnt>bytespertaddr){
general_error(21,bytespertaddr*8); /* target data type overflow */
break;
}
if(BIGENDIAN){
val=(val<<8)+c;
}else if(LITTLEENDIAN){
val+=c<<shift;
shift+=8;
}else
ierror(0);
}
EXPSKIP();
new=new_expr();
new->type=NUM;
new->c.val=val;
return new;
}
general_error(9);
new=new_expr();
new->type=NUM;
new->c.val=-1;
return new;
}
static expr *unary_expr()
{
expr *new;
char *m;
int len;
if(*s=='+'||*s=='-'||*s=='!'||*s=='~'){
m=s++;
EXPSKIP();
}
else if(len=EXT_UNARY_NAME(s)){
m=s;
s+=len;
EXPSKIP();
}else
return primary_expr();
if(*m=='+')
return primary_expr();
new=new_expr();
if(*m=='-')
new->type=NEG;
else if(*m=='!')
new->type=NOT;
else if(*m=='~')
new->type=CPL;
else if(EXT_UNARY_NAME(m))
new->type=EXT_UNARY_TYPE(m);
new->left=primary_expr();
return new;
}
static expr *shift_expr()
{
expr *left,*new;
char m;
left=unary_expr();
EXPSKIP();
while((*s=='<'||*s=='>')&&s[1]==*s){
m=*s;
s+=2;
EXPSKIP();
new=new_expr();
if(m=='<')
new->type=LSH;
else
new->type=unsigned_shift?RSHU:RSH;
new->left=left;
new->right=unary_expr();
left=new;
EXPSKIP();
}
return left;
}
static expr *and_expr(void)
{
expr *left,*new;
left=shift_expr();
EXPSKIP();
while(*s=='&'&&s[1]!='&'){
s++;
EXPSKIP();
new=new_expr();
new->type=BAND;
EXPSKIP();
new->left=left;
new->right=shift_expr();
left=new;
}
return left;
}
static expr *exclusive_or_expr(void)
{
expr *left,*new;
left=and_expr();
EXPSKIP();
while(*s=='^'||*s=='~'){
s++;
EXPSKIP();
new=new_expr();
new->type=XOR;
EXPSKIP();
new->left=left;
new->right=and_expr();
left=new;
}
return left;
}
static expr *inclusive_or_expr(void)
{
expr *left,*new;
left=exclusive_or_expr();
EXPSKIP();
while((*s=='|'&&s[1]!='|')||(*s=='!'&&s[1]!='=')){
s++;
EXPSKIP();
new=new_expr();
new->type=BOR;
EXPSKIP();
new->left=left;
new->right=exclusive_or_expr();
left=new;
}
return left;
}
static expr *multiplicative_expr(void)
{
expr *left,*new;
char m;
left=inclusive_or_expr();
EXPSKIP();
while(*s=='*'||*s=='/'||*s=='%'){
m=*s++;
EXPSKIP();
new=new_expr();
if(m=='/'){
if(*s=='/'){
s++;
new->type=MOD;
}
else
new->type=DIV;
}
else if(m=='*')
new->type=MUL;
else
new->type=MOD;
new->left=left;
new->right=inclusive_or_expr();
left=new;
EXPSKIP();
}
return left;
}
static expr *additive_expr(void)
{
expr *left,*new;
char m;
left=multiplicative_expr();
EXPSKIP();
while((*s=='+'&&s[1]!='+')||(*s=='-'&&s[1]!='-')){
m=*s++;
EXPSKIP();
new=new_expr();
if(m=='+')
new->type=ADD;
else
new->type=SUB;
new->left=left;
new->right=multiplicative_expr();
left=new;
EXPSKIP();
}
return left;
}
static expr *relational_expr(void)
{
expr *left,*new;
char m1,m2=0;
left=additive_expr();
EXPSKIP();
while(((*s=='<'&&s[1]!='>')||*s=='>')&&s[1]!=*s){
m1=*s++;
if(*s=='=')
m2=*s++;
EXPSKIP();
new=new_expr();
if(m1=='<'){
if(m2)
new->type=LEQ;
else
new->type=LT;
}else{
if(m2)
new->type=GEQ;
else
new->type=GT;
}
EXPSKIP();
new->left=left;
new->right=additive_expr();
left=new;
}
return left;
}
static expr *equality_expr(void)
{
expr *left,*new;
char m;
left=relational_expr();
EXPSKIP();
while(*s=='='||(*s=='!'&&s[1]=='=')||(*s=='<'&&s[1]=='>')){
m=*s++;
if(m==*s||m!='=')
s++;
EXPSKIP();
new=new_expr();
if(m=='=')
new->type=EQ;
else
new->type=NEQ;
EXPSKIP();
new->left=left;
new->right=relational_expr();
left=new;
}
return left;
}
static expr *logical_and_expr(void)
{
expr *left,*new;
left=equality_expr();
EXPSKIP();
while(*s=='&'&&s[1]=='&'){
s+=2;
EXPSKIP();
new=new_expr();
new->type=LAND;
EXPSKIP();
new->left=left;
new->right=equality_expr();
left=new;
}
return left;
}
static expr *expression()
{
expr *left,*new;
left=logical_and_expr();
EXPSKIP();
while(*s=='|'&&s[1]=='|'){
s+=2;
EXPSKIP();
new=new_expr();
new->type=LOR;
EXPSKIP();
new->left=left;
new->right=logical_and_expr();
left=new;
}
return left;
}
/* Tries to parse the string as a constant value. Sets pp to the
end of the parse. Already defined absolute symbols are
recognized.
Automatically switches to a HUG expression type, when encountering a
constant which doesn't fit into taddr.
Automatically switches to a FLT expression type, when reading a decimal
point or an exponent in a decimal constant. */
expr *parse_expr(char **pp)
{
expr *tree;
s=*pp;
make_tmp_lab=0;
exp_type=NUM;
tree=expression();
simplify_expr(tree);
*pp=s;
return tree;
}
expr *parse_expr_tmplab(char **pp)
{
expr *tree;
s=*pp;
make_tmp_lab=1;
exp_type=NUM;
tree=expression();
simplify_expr(tree);
*pp=s;
return tree;
}
/* Tries to parse the string as a huge-integer (128 bits) constant (thuge).
No labels allowed in this mode. */
expr *parse_expr_huge(char **pp)
{
expr *tree;
s=*pp;
make_tmp_lab=0;
exp_type=HUG;
tree=expression();
simplify_expr(tree);
*pp=s;
return tree;
}
/* Tries to parse the string as a floating point constant (tfloat).
No labels allowed in this mode.
When the constant is not decimal switch to reading a HUG expression type. */
expr *parse_expr_float(char **pp)
{
expr *tree;
s=*pp;
make_tmp_lab=0;
exp_type=FLT;
tree=expression();
simplify_expr(tree);
*pp=s;
return tree;
}
void free_expr(expr *tree)
{
if(!tree)
return;
free_expr(tree->left);
free_expr(tree->right);
myfree(tree);
}
/* Return type of expression.
Either NUM, HUG or FLT. Labels or unknown symbols default to NUM.
Returns 0 in case of an error (e.g. epxression is NULL pointer). */
int type_of_expr(expr *tree)
{
int ltype,rtype;
if(tree==NULL)
return 0;
ltype=tree->type;
if(ltype==SYM){
if(tree->c.sym->flags&INEVAL)
general_error(18,tree->c.sym->name);
tree->c.sym->flags|=INEVAL;
ltype=tree->c.sym->type==EXPRESSION?type_of_expr(tree->c.sym->expr):NUM;
tree->c.sym->flags&=~INEVAL;
return ltype;
}else if(ltype==NUM||ltype==HUG||ltype==FLT)
return ltype;
ltype=type_of_expr(tree->left);
rtype=type_of_expr(tree->right);
return rtype>ltype?rtype:ltype;
}
/* Try to evaluate expression as far as possible. Subexpressions
only containing constants or absolute symbols are simplified. */
void simplify_expr(expr *tree)
{
taddr ival;
thuge hval;
tfloat fval;
int type=0;
if(!tree)
return;
simplify_expr(tree->left);
simplify_expr(tree->right);
if(tree->type==SUB&&tree->right->type==SYM&&tree->left->type==SUB&&
tree->left->left->type!=SYM&&tree->left->right->type==SYM){
/* Rearrange nodes from "const-symbol-symbol", so that "symbol-symbol"
is evaluated first, as it may yield a constant. */
expr *x=tree->right;
tree->right=tree->left;
tree->left=tree->right->left;
tree->right->left=tree->right->right;
tree->right->right=x;
}
if(tree->left){
if(tree->left->type==NUM||tree->left->type==HUG||tree->left->type==FLT)
type=tree->left->type;
else return;
}
if(tree->right){
if(tree->right->type==NUM||tree->right->type==HUG||tree->right->type==FLT){
if(tree->right->type>type)
type=tree->right->type;
} else return;
}
if(type==NUM){
switch(tree->type){
case ADD:
ival=(tree->left->c.val+tree->right->c.val);
break;
case SUB:
ival=(tree->left->c.val-tree->right->c.val);
break;
case MUL:
ival=(tree->left->c.val*tree->right->c.val);
break;
case DIV:
if(tree->right->c.val==0){
general_error(41);
ival=0;
}else if(tree->left->c.val==taddrmin&&tree->right->c.val==-1){
general_error(21,sizeof(taddr)*8); /* target data type overflow */
ival=taddrmin;
}else
ival=(tree->left->c.val/tree->right->c.val);
break;
case MOD:
if(tree->right->c.val==0){
general_error(41);
ival=0;
}else if(tree->left->c.val==taddrmin&&tree->right->c.val==-1){
general_error(21,sizeof(taddr)*8); /* target data type overflow */
ival=taddrmin;
}else
ival=(tree->left->c.val%tree->right->c.val);
break;
case NEG:
ival=(-tree->left->c.val);
break;
case CPL:
ival=(~tree->left->c.val);
break;
case LAND:
ival=BOOLEAN(tree->left->c.val&&tree->right->c.val);
break;
case LOR:
ival=BOOLEAN(tree->left->c.val||tree->right->c.val);
break;
case BAND:
ival=(tree->left->c.val&tree->right->c.val);
break;
case BOR:
ival=(tree->left->c.val|tree->right->c.val);
break;
case XOR:
ival=(tree->left->c.val^tree->right->c.val);
break;
case NOT:
ival=(!tree->left->c.val);
break;
case LSH:
ival=(tree->left->c.val<<tree->right->c.val);
break;
case RSH:
ival=(tree->left->c.val>>tree->right->c.val);
break;
case RSHU:
ival=((utaddr)tree->left->c.val>>tree->right->c.val);
break;
case LT:
ival=BOOLEAN(tree->left->c.val<tree->right->c.val);
break;
case GT:
ival=BOOLEAN(tree->left->c.val>tree->right->c.val);
break;
case LEQ:
ival=BOOLEAN(tree->left->c.val<=tree->right->c.val);
break;
case GEQ:
ival=BOOLEAN(tree->left->c.val>=tree->right->c.val);
break;
case NEQ:
ival=BOOLEAN(tree->left->c.val!=tree->right->c.val);
break;
case EQ:
ival=BOOLEAN(tree->left->c.val==tree->right->c.val);
break;
default:
#ifdef EXT_UNARY_EVAL
if (tree->left && EXT_UNARY_EVAL(tree->type,tree->left->c.val,&ival,1))
break;
#endif
return;
}
}
else if(type==HUG){
thuge lval,rval;
if(tree->left){
if(tree->left->type==NUM)
lval=huge_from_int(tree->left->c.val);
else
lval=tree->left->c.huge;
}
if(tree->right){
if(tree->right->type==NUM)
rval=huge_from_int(tree->right->c.val);
else
rval=tree->right->c.huge;
}
switch(tree->type){
case ADD:
hval=hadd(lval,rval);
break;
case SUB:
hval=hsub(lval,rval);
break;
case MUL:
hval=hmul(lval,rval);
break;
case DIV:
if(hcmp(rval,huge_zero())==0){
general_error(41);
hval=huge_zero();
}else
hval=hdiv(lval,rval);
break;
case MOD:
if(hcmp(rval,huge_zero())==0){
general_error(41);
hval=huge_zero();
}else
hval=hmod(lval,rval);
break;
case NEG:
hval=hneg(lval);
break;
case CPL:
hval=hcpl(lval);
break;
case LAND:
ival=BOOLEAN(hcmp(lval,huge_zero())!=0&&hcmp(rval,huge_zero())!=0);
type=NUM;
break;
case LOR:
ival=BOOLEAN(hcmp(lval,huge_zero())!=0||hcmp(rval,huge_zero())!=0);
type=NUM;
break;
case BAND:
hval=hand(lval,rval);
break;
case BOR:
hval=hor(lval,rval);
break;
case XOR:
hval=hxor(lval,rval);
break;
case NOT:
hval=hnot(lval);
break;
case LSH:
hval=hshl(lval,huge_to_int(rval));
break;
case RSH:
hval=hshra(lval,huge_to_int(rval));
break;
case RSHU:
hval=hshr(lval,huge_to_int(rval));
break;
case LT:
ival=BOOLEAN(hcmp(lval,rval)<0);
type=NUM;
break;
case GT:
ival=BOOLEAN(hcmp(lval,rval)>0);
type=NUM;
break;
case LEQ:
ival=BOOLEAN(hcmp(lval,rval)<=0);
type=NUM;
break;
case GEQ:
ival=BOOLEAN(hcmp(lval,rval)>=0);
type=NUM;
break;
case NEQ:
ival=BOOLEAN(hcmp(lval,rval)!=0);
type=NUM;
break;
case EQ:
ival=BOOLEAN(hcmp(lval,rval)==0);
type=NUM;
break;
default:
return;
}
}
else if(type==FLT){
tfloat lval,rval;
if(tree->left){
switch(tree->left->type){
case NUM: lval=(tfloat)tree->left->c.val; break;
case HUG: lval=huge_to_float(tree->left->c.huge); break;
case FLT: lval=tree->left->c.flt; break;
}
}
if(tree->right){
switch(tree->right->type){
case NUM: rval=(tfloat)tree->right->c.val; break;
case HUG: rval=huge_to_float(tree->right->c.huge); break;
case FLT: rval=tree->right->c.flt; break;
}
}
switch(tree->type){
case ADD:
fval=(lval+rval);
break;
case SUB:
fval=(lval-rval);
break;
case MUL:
fval=(lval*rval);
break;
case DIV:
if(rval==0.0){
general_error(41);
fval=0.0;
}else
fval=(lval/rval);
break;
case NEG:
fval=(-lval);
break;
case LT:
ival=BOOLEAN(lval<rval);
type=NUM;
break;
case GT:
ival=BOOLEAN(lval>rval);
type=NUM;
break;
case LEQ:
ival=BOOLEAN(lval<=rval);
type=NUM;
break;
case GEQ:
ival=BOOLEAN(lval>=rval);
type=NUM;
break;
case NEQ:
ival=BOOLEAN(lval!=rval);
type=NUM;
break;
case EQ:
ival=BOOLEAN(lval==rval);
type=NUM;
break;
default:
return;
}
}
else{
if(tree->type==SYM&&tree->c.sym->type==EXPRESSION){
switch(tree->c.sym->expr->type){
case NUM:
ival=tree->c.sym->expr->c.val;
type=NUM;
break;
case HUG:
hval=tree->c.sym->expr->c.huge;
type=HUG;
break;
case FLT:
fval=tree->c.sym->expr->c.flt;
type=FLT;
break;
default:
return;
}
}else return;
}
free_expr(tree->left);
free_expr(tree->right);
tree->left=tree->right=NULL;
switch(tree->type=type){
case NUM: tree->c.val=ival; break;
case HUG: tree->c.huge=hval; break;
case FLT: tree->c.flt=fval; break;
default: ierror(0); break;
}
}
/* Evaluate an expression using current values of all symbols.
Result is written to *result. The return value specifies
whether the result is constant (i.e. only depending on
constants or absolute symbols). */
int eval_expr(expr *tree,taddr *result,section *sec,taddr pc)
{
taddr val,lval,rval;
symbol *lsym,*rsym;
int cnst=1;
if(!tree)
ierror(0);
if(tree->left&&!eval_expr(tree->left,&lval,sec,pc))
cnst=0;
if(tree->right&&!eval_expr(tree->right,&rval,sec,pc))
cnst=0;
switch(tree->type){
case ADD:
val=(lval+rval);
break;
case SUB:
find_base(tree->left,&lsym,sec,pc);
find_base(tree->right,&rsym,sec,pc);
if(cnst==0&&rsym!=NULL&&LOCREF(rsym)){
if(lsym!=NULL&&LOCREF(lsym)&&lsym->sec==rsym->sec){
/* l2-l1 is constant when both have a valid symbol-base, and both
symbols are LABSYMs from the same section, e.g. (sym1+x)-(sym2-y) */
cnst=1;
}else if(lsym!=NULL&&(rsym->sec==sec&&(EXTREF(lsym)||LOCREF(lsym)))){
/* Difference between symbols from different section or between an
external symbol and a symbol from the current section can be
represented by a REL_PC, so we calculate the addend. */
if((rsym->flags&ABSLABEL)&&(lsym->flags&ABSLABEL))
cnst=1; /* constant, when labels are from two ORG sections */
else{
/* prepare a value which works with REL_PC */
val=(pc-rval+lval-(lsym->sec?lsym->sec->org:0));
break;
}
}else if(lsym==NULL&&(rsym->flags&ABSLABEL))
/* const-label is valid and yields a const in absolute ORG sections */
cnst=1;
}
val=(lval-rval);
break;
case MUL:
val=(lval*rval);
break;
case DIV:
if(rval==0){
general_error(41);
val=0;
}else if(lval==taddrmin&&rval==-1){
general_error(21,sizeof(taddr)*8); /* target data type overflow */
val=taddrmin;
}else
val=(lval/rval);
break;
case MOD:
if(rval==0){
general_error(41);
val=0;
}else if(lval==taddrmin&&rval==-1){
general_error(21,sizeof(taddr)*8); /* target data type overflow */
val=taddrmin;
}else
val=(lval%rval);
break;
case NEG:
val=(-lval);
break;
case CPL:
val=(~lval);
break;
case LAND:
val=BOOLEAN(lval&&rval);
break;
case LOR:
val=BOOLEAN(lval||rval);
break;
case BAND:
val=(lval&rval);
break;
case BOR:
val=(lval|rval);
break;
case XOR:
val=(lval^rval);
break;
case NOT:
val=(!lval);
break;
case LSH:
val=(lval<<rval);
break;
case RSH:
val=(lval>>rval);
break;
case RSHU:
val=((utaddr)lval>>rval);
break;
case LT:
val=BOOLEAN(lval<rval);
break;
case GT:
val=BOOLEAN(lval>rval);
break;
case LEQ:
val=BOOLEAN(lval<=rval);
break;
case GEQ:
val=BOOLEAN(lval>=rval);
break;
case NEQ:
val=BOOLEAN(lval!=rval);
break;
case EQ:
val=BOOLEAN(lval==rval);
break;
case SYM:
if(tree->c.sym->type==EXPRESSION){
if(tree->c.sym->flags&INEVAL)
general_error(18,tree->c.sym->name);
tree->c.sym->flags|=INEVAL;
cnst=eval_expr(tree->c.sym->expr,&val,sec,pc);
tree->c.sym->flags&=~INEVAL;
}else if(LOCREF(tree->c.sym)){
update_curpc(tree,sec,pc);
val=tree->c.sym->pc;
cnst=tree->c.sym->sec==NULL?0:(tree->c.sym->sec->flags&UNALLOCATED)!=0;
}else{
/* IMPORT */
cnst=0;
val=0;
}
break;
case NUM:
val=tree->c.val;
break;
case HUG:
if (!huge_chkrange(tree->c.huge,bytespertaddr*8))
general_error(21,bytespertaddr*8); /* target data type overflow */
val=huge_to_int(tree->c.huge);
break;
case FLT:
if (!flt_chkrange(tree->c.flt,bytespertaddr*8))
general_error(21,bytespertaddr*8); /* target data type overflow */
val=(taddr)tree->c.flt;
break;
default:
#ifdef EXT_UNARY_EVAL
if (EXT_UNARY_EVAL(tree->type,lval,&val,cnst))
break;
#endif
ierror(0);
}
*result=val;
return cnst;
}
/* Evaluate a huge integer expression using current values of all symbols.
Result is written to *result. The return value specifies whether all
operations were valid. */
int eval_expr_huge(expr *tree,thuge *result)
{
thuge val,lval,rval;
if(!tree)
ierror(0);
if(tree->left&&!eval_expr_huge(tree->left,&lval))
return 0;
if(tree->right&&!eval_expr_huge(tree->right,&rval))
return 0;
switch (tree->type){
case ADD:
val=hadd(lval,rval);
break;
case SUB:
val=hsub(lval,rval);
break;
case MUL:
val=hmul(lval,rval);
break;
case DIV:
if(hcmp(rval,huge_zero())==0){
general_error(41);
val=huge_zero();
}else
val=hdiv(lval,rval);
break;
case MOD:
if(hcmp(rval,huge_zero())==0){
general_error(41);
val=huge_zero();
}else
val=hmod(lval,rval);
break;
case NEG:
val=hneg(lval);
break;
case CPL:
val=hcpl(lval);
break;
case LAND:
val=huge_from_int(BOOLEAN(hcmp(lval,huge_zero())!=0
&&hcmp(rval,huge_zero())!=0));
break;
case LOR:
val=huge_from_int(BOOLEAN(hcmp(lval,huge_zero())!=0
||hcmp(rval,huge_zero())!=0));
break;
case BAND:
val=hand(lval,rval);
break;
case BOR:
val=hor(lval,rval);
break;
case XOR:
val=hxor(lval,rval);
break;
case NOT:
val=hnot(lval);
break;
case LSH:
val=hshl(lval,huge_to_int(rval));
break;
case RSH:
val=hshra(lval,huge_to_int(rval));
break;
case RSHU:
val=hshr(lval,huge_to_int(rval));
break;
case LT:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)<0));
break;
case GT:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)>0));
break;
case LEQ:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)<=0));
break;
case GEQ:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)>=0));
break;
case NEQ:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)!=0));
break;
case EQ:
val=huge_from_int(BOOLEAN(hcmp(lval,rval)==0));
break;
case SYM:
if(tree->c.sym->type==EXPRESSION){
int ok;
if(tree->c.sym->flags&INEVAL)
general_error(18,tree->c.sym->name);
tree->c.sym->flags|=INEVAL;
ok=eval_expr_huge(tree->c.sym->expr,&val);
tree->c.sym->flags&=~INEVAL;
if(!ok) return 0;
}
#if 0 /* all relocations should be representable by taddr */
else if(EXTREF(tree->c.sym))
val=huge_zero();
#endif
else
return 0;
break;
case NUM:
val=huge_from_int(tree->c.val);
break;
case HUG:
val=tree->c.huge;
break;
case FLT:
val=huge_from_float(tree->c.flt);
break;
default:
return 0;
}
*result=val;
return 1;
}
/* Evaluate a floating point expression using current values of all symbols.
Result is written to *result. The return value specifies whether all
operations were valid. */
int eval_expr_float(expr *tree,tfloat *result)
{
tfloat val,lval,rval;
if(!tree)
ierror(0);
if(tree->left&&!eval_expr_float(tree->left,&lval))
return 0;
if(tree->right&&!eval_expr_float(tree->right,&rval))
return 0;
switch(tree->type){
case ADD:
val=(lval+rval);
break;
case SUB:
val=(lval-rval);
break;
case MUL:
val=(lval*rval);
break;
case DIV:
if(rval==0.0){
general_error(41);
val=0.0;
}else
val=(lval/rval);
break;
case NEG:
val=(-lval);
break;
case LT:
val=BOOLEAN(lval<rval);
break;
case GT:
val=BOOLEAN(lval>rval);
break;
case LEQ:
val=BOOLEAN(lval<=rval);
break;
case GEQ:
val=BOOLEAN(lval>=rval);
break;
case NEQ:
val=BOOLEAN(lval!=rval);
break;
case EQ:
val=BOOLEAN(lval==rval);
break;
case SYM:
if(tree->c.sym->type==EXPRESSION){
int ok;
if(tree->c.sym->flags&INEVAL)
general_error(18,tree->c.sym->name);
tree->c.sym->flags|=INEVAL;
ok=eval_expr_float(tree->c.sym->expr,&val);
tree->c.sym->flags&=~INEVAL;
if(!ok) return 0;
}else
return 0;
break;
case NUM:
val=(tfloat)tree->c.val;
break;
case HUG:
val=huge_to_float(tree->c.huge);
break;
case FLT:
val=tree->c.flt;
break;
default:
return 0;
}
*result=val;
return 1;
}
void print_expr(FILE *f,expr *p)
{
if(p==NULL)
ierror(0);
simplify_expr(p);
if(p->type==NUM)
fprintf(f,"%lld=0x%llx",(long long)p->c.val,ULLTADDR(p->c.val));
else if(p->type==HUG)
fprintf(f,"0x%016llx%016llx",(long long)p->c.huge.hi,(long long)p->c.huge.lo);
else if(p->type==FLT)
fprintf(f,"%.8g",(double)p->c.flt);
else
fprintf(f,"complex expression");
}
/* return a single base symbol from an absolute ORG section, when present */
static int find_abs_base(expr *tree,symbol **base)
{
if(tree==NULL)
return 1;
if(tree->type==SYM){
if(tree->c.sym->type==EXPRESSION){
int ok;
if(tree->c.sym->flags&INEVAL)
return 0;
tree->c.sym->flags|=INEVAL;
ok=find_abs_base(tree->c.sym->expr,base);
tree->c.sym->flags&=~INEVAL;
return ok;
}else if(LOCREF(tree->c.sym)){
if(tree->c.sym->flags&ABSLABEL){
*base=tree->c.sym;
return 1;
}
}
#if 0 /* There is no reason to forbid that? Especially not in RORG sections,
which are embedded in a normal relocatable, linkable code section. */
else{
if(current_section!=NULL&&(current_section->flags&ABSOLUTE)){
/* IMPORT in ORG section, will be flagged as BASE_ILLEGAL */
*base=tree->c.sym;
return 1;
}
}
#endif
return 0;
}
if(!find_abs_base(tree->left,base))
return 0;
if(*base!=NULL){
symbol *tstbase=NULL;
if(!find_abs_base(tree->right,&tstbase))
return 0;
if(tstbase!=NULL){
*base=NULL;
return 1;
}
}else{
if(!find_abs_base(tree->right,base))
return 0;
}
return 1;
}
static int _find_base(expr *p,symbol **base,section *sec,taddr pc)
{
if(p->type==SYM){
update_curpc(p,sec,pc);
if(p->c.sym->type==EXPRESSION)
return _find_base(p->c.sym->expr,base,sec,pc);
else{
if(base)
*base=p->c.sym;
return BASE_OK;
}
}
if(p->type==ADD){
taddr val;
if(eval_expr(p->left,&val,sec,pc)&&
_find_base(p->right,base,sec,pc)==BASE_OK)
return BASE_OK;
if(eval_expr(p->right,&val,sec,pc)&&
_find_base(p->left,base,sec,pc)==BASE_OK)
return BASE_OK;
}
if(p->type==SUB){
taddr val;
symbol *pcsym;
if(eval_expr(p->right,&val,sec,pc)&&
_find_base(p->left,base,sec,pc)==BASE_OK)
return BASE_OK;
if(_find_base(p->left,base,sec,pc)==BASE_OK&&
_find_base(p->right,&pcsym,sec,pc)==BASE_OK) {
if(LOCREF(pcsym)&&pcsym->sec==sec&&(LOCREF(*base)||EXTREF(*base)))
return BASE_PCREL;
}
}
return BASE_ILLEGAL;
}
/* Tests, if an expression is based only on one non-absolute
symbol plus constants. Returns that symbol or zero.
Note: Does not find all possible solutions. */
int find_base(expr *p,symbol **base,section *sec,taddr pc)
{
#ifdef EXT_FIND_BASE
int ret;
if(ret=EXT_FIND_BASE(base,p,sec,pc))
return ret;
#endif
if(base){
*base=NULL;
if(find_abs_base(p,base)){
/* a base label from an absolute ORG section */
if(*base!=NULL)
return EXTREF(*base)?BASE_ILLEGAL:BASE_OK;
return BASE_NONE;
}
}
return _find_base(p,base,sec,pc);
}
expr *number_expr(taddr val)
{
expr *new=new_expr();
new->type=NUM;
new->c.val=val;
return new;
}
expr *huge_expr(thuge val)
{
expr *new=new_expr();
new->type=HUG;
new->c.huge=val;
return new;
}
expr *float_expr(tfloat val)
{
expr *new=new_expr();
new->type=FLT;
new->c.flt=val;
return new;
}
taddr parse_constexpr(char **s)
{
expr *tree;
taddr val = 0;
if (tree = parse_expr(s)) {
simplify_expr(tree);
switch(tree->type){
case NUM:
val = tree->c.val;
break;
case HUG:
general_error(59); /* cannot evaluate huge integer */
break;
case FLT:
general_error(60); /* cannot evaluate floating point */
break;
default:
general_error(30); /* expression must be a constant */
break;
}
free_expr(tree);
}
return val;
}