/* evaluates variables Copyright (C) 2002, 2003, 2004, 2005 Craig Franklin This file is part of gputils. gputils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. gputils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gputils; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdhdr.h" #include "libgputils.h" #include "gpasm.h" #include "evaluate.h" #include "directive.h" #include "gperror.h" #include "parse.h" #include "coff.h" extern int _16bit_core; int enforce_arity(int arity, int must_be) { if (arity == must_be) return 1; else { if (arity < must_be) { gperror(GPE_MISSING_ARGU, NULL); } else { gperror(GPE_TOO_MANY_ARGU, NULL); } return 0; } } int enforce_simple(struct pnode *p) { if (p->tag == symbol) { return 1; } else { gperror(GPE_ILLEGAL_ARGU, NULL); return 0; } } int list_length(struct pnode *L) { if (L == NULL) { return 0; } else { return 1 + list_length(TAIL(L)); } } int can_evaluate_concatenation(struct pnode *p) { char buf[BUFSIZ]; switch (p->tag) { case constant: return 1; case offset: return can_evaluate_concatenation(p->value.offset); case symbol: return 1; case unop: return can_evaluate_concatenation(p->value.unop.p0); case binop: return can_evaluate_concatenation(p->value.binop.p0) && can_evaluate_concatenation(p->value.binop.p1); case string: snprintf(buf, sizeof(buf), "Illegal argument (%s).", p->value.string); gperror(GPE_ILLEGAL_ARGU, buf); return 0; default: assert(0); } return 0; } char *evaluate_concatenation(struct pnode *p) { switch (p->tag) { case symbol: return p->value.symbol; case binop: assert(p->value.binop.op == CONCAT); { char *s[2], *new; size_t sizeof_new; s[0] = evaluate_concatenation(p->value.binop.p0); s[1] = evaluate_concatenation(p->value.binop.p1); sizeof_new =strlen(s[0]) + 1 + strlen(s[1]) + 1; new = malloc(sizeof_new); if (new) { strncpy(new, s[0], sizeof_new); strncat(new, s[1], sizeof_new); } return new; } case unop: assert(p->value.unop.op == VAR); { char buf[80]; snprintf(buf, sizeof(buf), "%d", maybe_evaluate(p->value.unop.p0)); return (strdup(buf)); } default: assert(0); } return NULL; } /* Attempt to evaluate concatenation 'p'. Return its value if * successful, otherwise generate an error message and return NULL. */ char *maybe_evaluate_concat(struct pnode *p) { char *r = NULL; if (((p->tag == unop) && (p->value.unop.op != VAR)) || ((p->tag == binop) && (p->value.binop.op != CONCAT))) { gperror(GPE_ILLEGAL_ARGU, NULL); } else if (p && can_evaluate_concatenation(p)) { r = evaluate_concatenation(p); } return r; } int can_evaluate(struct pnode *p) { char buf[BUFSIZ]; if ((p->tag == binop) && (p->value.binop.op == CONCAT)) { return can_evaluate_concatenation(p); } switch (p->tag) { case constant: return 1; case offset: if (state.extended_pic16e == false) { gperror(GPE_BADCHAR, "Illegal character ([)"); } return can_evaluate(p->value.offset); case symbol: { struct symbol *s; /* '$' means current org, which we can always evaluate */ if (strcmp(p->value.symbol, "$") == 0) { return 1; } else { struct variable *var = NULL; /* Otherwise look it up */ s = get_symbol(state.stTop, p->value.symbol); if (s == NULL) { snprintf(buf, sizeof(buf), "Symbol not previously defined (%s).", p->value.symbol); gperror(GPE_NOSYM, buf); } else { var = get_symbol_annotation(s); if (var == NULL) { snprintf(buf, sizeof(buf), "Symbol not assigned a value (%s).", p->value.symbol); gpwarning(GPW_UNKNOWN, buf); } } return ((s != NULL) && (var != NULL)); } } case unop: return can_evaluate(p->value.unop.p0); case binop: return can_evaluate(p->value.binop.p0) && can_evaluate(p->value.binop.p1); case string: snprintf(buf, sizeof(buf), "Illegal argument (%s).", p->value.string); gperror(GPE_ILLEGAL_ARGU, buf); return 0; default: assert(0); } return 0; } gpasmVal evaluate(struct pnode *p) { struct variable *var; gpasmVal p0, p1; if (((p->tag == binop) && (p->value.binop.op == CONCAT)) || ((p->tag == unop) && (p->value.unop.op == VAR))) { char *string = evaluate_concatenation(p); struct symbol *s; s = get_symbol(state.stTop, string); if (s == NULL) { char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "Symbol not previously defined (%s).", string); gperror(GPE_NOSYM, buf); return 0; } else { var = get_symbol_annotation(s); assert(var != NULL); return var->value; } } switch (p->tag) { case constant: return p->value.constant; case offset: return evaluate(p->value.offset); case symbol: { struct symbol *s; if (strcmp(p->value.symbol, "$") == 0) { return state.org << _16bit_core; } else { s = get_symbol(state.stTop, p->value.symbol); var = get_symbol_annotation(s); assert(var != NULL); return var->value; } } case unop: switch (p->value.unop.op) { case '!': return !evaluate(p->value.unop.p0); case '+': return evaluate(p->value.unop.p0); case '-': return -evaluate(p->value.unop.p0); case '~': return ~evaluate(p->value.unop.p0); case UPPER: return (evaluate(p->value.unop.p0) >> 16) & 0xff; case HIGH: return (evaluate(p->value.unop.p0) >> 8) & 0xff; case LOW: return evaluate(p->value.unop.p0) & 0xff; case INCREMENT: return evaluate(p->value.unop.p0) + 1; case DECREMENT: return evaluate(p->value.unop.p0) - 1; default: assert(0); } case binop: p0 = evaluate(p->value.binop.p0); p1 = evaluate(p->value.binop.p1); switch (p->value.binop.op) { case '+': return p0 + p1; case '-': return p0 - p1; case '*': return p0 * p1; case '/': if (p1 == 0){ gperror(GPE_DIVBY0, NULL); return 0; } else { return p0 / p1; } case '%': if (p1 == 0){ gperror(GPE_DIVBY0, NULL); return 0; } else { return p0 % p1; } case '&': return p0 & p1; case '|': return p0 | p1; case '^': return p0 ^ p1; case LSH: return p0 << p1; case RSH: return p0 >> p1; case EQUAL: return p0 == p1; case '<': return p0 < p1; case '>': return p0 > p1; case NOT_EQUAL: return p0 != p1; case GREATER_EQUAL: return p0 >= p1; case LESS_EQUAL: return p0 <= p1; case LOGICAL_AND: return p0 && p1; case LOGICAL_OR: return p0 || p1; case '=': gperror(GPE_BADCHAR, "Illegal character (=)"); return 0; default: assert(0); /* Unhandled binary operator */ } default: assert(0); /* Unhandled parse node tag */ } return (0); /* Should never reach here */ } /* Attempt to evaluate expression 'p'. Return its value if * successful, otherwise generate an error message and return 0. */ gpasmVal maybe_evaluate(struct pnode *p) { gpasmVal r; if (p && can_evaluate(p)) { r = evaluate(p); } else { r = 0; } return r; } /* count the number of relocatable addesses in the expression */ int count_reloc(struct pnode *p) { struct symbol *s; struct variable *var; char *string; if (state.mode == absolute) return 0; if ((p->tag == binop) && (p->value.binop.op == CONCAT)) { string = evaluate_concatenation(p); s = get_symbol(state.stTop, string); if (s != NULL) { var = get_symbol_annotation(s); assert(var != NULL); switch(var->type) { case gvt_extern: case gvt_global: case gvt_static: case gvt_address: return 1; default: return 0; } } return 0; } switch (p->tag) { case constant: return 0; case offset: return count_reloc(p->value.offset); case symbol: if (strcmp(p->value.symbol, "$") == 0) { return 1; } else { s = get_symbol(state.stTop, p->value.symbol); if (s != NULL) { var = get_symbol_annotation(s); if (var != NULL) { switch(var->type) { case gvt_extern: case gvt_global: case gvt_static: case gvt_address: return 1; default: return 0; } } } } return 0; case unop: return count_reloc(p->value.unop.p0); case binop: return count_reloc(p->value.binop.p0) + count_reloc(p->value.binop.p1); default: assert(0); } return 0; } /* When generating object files, operands with relocatable addresses can only be [UPPER|HIGH|LOW]([] + []) */ static void add_reloc(struct pnode *p, short offset, unsigned short type) { char *string = NULL; struct symbol *s = NULL; struct variable *var = NULL; if ((p->tag == binop) && (p->value.binop.op == CONCAT)) { string = evaluate_concatenation(p); s = get_symbol(state.stTop, string); if (s != NULL) { var = get_symbol_annotation(s); assert(var != NULL); switch(var->type) { case gvt_extern: case gvt_global: case gvt_static: case gvt_address: coff_reloc(var->coff_num, offset, type); return; default: return; } } return; } switch (p->tag) { case symbol: if (strcmp(p->value.symbol, "$") == 0) { char buffer[BUFSIZ]; snprintf(buffer, sizeof(buffer), "_$_%06x", state.org << _16bit_core); set_global(buffer, state.org << _16bit_core, PERMANENT, gvt_static); s = get_symbol(state.stTop, buffer); } else { s = get_symbol(state.stTop, p->value.symbol); } if (s != NULL) { var = get_symbol_annotation(s); if (var != NULL) { switch(var->type) { case gvt_extern: case gvt_global: case gvt_static: case gvt_address: coff_reloc(var->coff_num, offset, type); return; default: return; } } } return; case unop: switch (p->value.unop.op) { case UPPER: add_reloc(p->value.unop.p0, offset, RELOCT_UPPER); return; case HIGH: add_reloc(p->value.unop.p0, offset, RELOCT_HIGH); return; case LOW: add_reloc(p->value.unop.p0, offset, RELOCT_LOW); return; case '!': case '+': case '-': case '~': case INCREMENT: case DECREMENT: gperror(GPE_UNRESOLVABLE, NULL); return; default: assert(0); } case binop: switch (p->value.binop.op) { case '+': /* The symbol can be in either position */ if (count_reloc(p->value.binop.p0) == 1) { add_reloc(p->value.binop.p0, offset + maybe_evaluate(p->value.binop.p1), type); } else { add_reloc(p->value.binop.p1, offset + maybe_evaluate(p->value.binop.p0), type); } return; case '-': /* The symbol has to be first */ if (count_reloc(p->value.binop.p0) == 1) { add_reloc(p->value.binop.p0, offset - maybe_evaluate(p->value.binop.p1), type); } else { gperror(GPE_UNRESOLVABLE, NULL); } return; case '*': case '/': case '%': case '&': case '|': case '^': case LSH: case RSH: case EQUAL: case '<': case '>': case NOT_EQUAL: case GREATER_EQUAL: case LESS_EQUAL: case LOGICAL_AND: case LOGICAL_OR: case '=': gperror(GPE_UNRESOLVABLE, NULL); return; default: assert(0); /* Unhandled binary operator */ } return; case constant: default: assert(0); } return; } /* Determine if the expression is the difference between two symbols in the same section. If so, calculate the offset and don't generate a relocation. [UPPER|HIGH|LOW]([] - []) */ static int same_section(struct pnode *p) { struct pnode *p0; struct pnode *p1; struct symbol *sym0; struct symbol *sym1; struct variable *var0; struct variable *var1; if(!state.obj.enabled) return 0; if ((p->tag == unop) && ((p->value.unop.op == UPPER) || (p->value.unop.op == HIGH) || (p->value.unop.op == LOW))) { p = p->value.unop.p0; } if ((p->tag != binop) || (p->value.binop.op != '-') || (count_reloc(p->value.binop.p0) != 1)) return 0; p0 = p->value.binop.p0; p1 = p->value.binop.p1; if ((p0->tag != symbol) || (p1->tag != symbol)) return 0; sym0 = get_symbol(state.stTop, p0->value.symbol); sym1 = get_symbol(state.stTop, p1->value.symbol); var0 = get_symbol_annotation(sym0); var1 = get_symbol_annotation(sym1); /* They must come from the same section. Debug symbols are not placed in the global symbol table, so don't worry about symbol type. */ if (var0->coff_section_num != var1->coff_section_num) return 0; return 1; } gpasmVal reloc_evaluate(struct pnode *p, unsigned short type) { gpasmVal r = 0; int count = 0; if (state.mode == absolute) { r = maybe_evaluate(p); } else { count = count_reloc(p); if (count == 0) { /* no relocatable addresses */ r = maybe_evaluate(p); } else if (count > 1) { if ((count == 2) && (same_section(p))) { /* It is valid to take the difference between two symbols in the same section. Evaluate, but don't add a relocation. */ r = maybe_evaluate(p); } else { /* too many relocatable addresses */ gperror(GPE_UNRESOLVABLE, NULL); r = 0; } } else { /* add the coff relocation */ add_reloc(p, 0, type); r = 0; } } return r; } /* evaluate the number of passes for the "fill" directive*/ int eval_fill_number(struct pnode *p) { int number; number = maybe_evaluate(p); if(_16bit_core) { /* For 16 bit core devices number is bytes not words */ if ((number & 0x1) == 1){ /* The number is divided by two, so it can't be odd */ gperror(GPE_FILL_ODD, NULL); } else { number = number / 2; } } return number; }