home *** CD-ROM | disk | FTP | other *** search
- /* $VER: pasm eval.c V0.7 (01.01.98)
- *
- * This file is part of pasm, a portable PowerPC assembler.
- * Copyright (c) 1997-98 Frank Wille
- *
- * pasm is freeware and part of the portable and retargetable ANSI C
- * compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
- * pasm may be freely redistributed as long as no modifications are
- * made and nothing is charged for it. Non-commercial usage is allowed
- * without any restrictions.
- * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
- * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
- *
- *
- * v0.7 (01.01.98) phx
- * @ha, @h, @l are only allowed at the end of an operand, according
- * to GNU-as notation. '@' is only allowed as the first character in
- * a symbol (for @function and @object, for example).
- * Don't waste memory in makereloc() and makexref() by adding new
- * nodes to the current section if another pass is required
- * anyway.
- * v0.5 (11.10.97) phx
- * getarg() works with \' and \" now.
- * v0.4 (05.07.97) phx
- * R_PPC_TOC16 and R_PPC_REL14 support in makereloc() and makexref().
- * "(<term>)@l/h/ha" was not recognized.
- * A comment introducer '#' was erroneously treated as a macro
- * parameter in read_macro_params().
- * Undefined symbols are only automatically declared as externally
- * defined, if the -x option was given. Otherwise display an
- * error message.
- * Implementing backwards-evaluation of an expression in v0.3 was not
- * very smart (e.g. 4-1+2 => 1(!)) - changed to forward again... ;)
- * v0.3 (20.04.97) phx
- * Bug in eval_expression() fixed, which sometimes caused the loss
- * of the last argument of an expression.
- * v0.2 (25.03.97) phx
- * Writes ELF object for 32-bit PowerPC big-endian. Either absolute
- * or ELF output format may be selected. ELF is default for all
- * currently supported platforms. PPCasm supports nine different
- * relocation types (there are much more...).
- * Compiles and works also under NetBSD/amiga (68k).
- * Changed function declaration to 'new style' in all sources
- * (to avoid problems with '...' for example).
- * makexref() and makereloc() used wrong offset.
- * v0.1 (11.03.97) phx
- * First test version with all PowerPC instructions and most
- * important directives. Only raw, absolute output.
- * v0.0 (21.02.97) phx
- * File created.
- */
-
-
- #define EVAL_C
- #include "ppcasm.h"
-
-
- char *getsymbol(struct GlobalVars *,char *);
- char *getarg(struct GlobalVars *,char *);
- char *skipspaces(char *);
- char *remquotes(char *);
- void checkEOL(char *);
- char *skipexpression(struct GlobalVars *,char *);
- void read_macro_params(struct GlobalVars *,struct ParsedLine *,
- struct MacroParams *,char *);
- char *getexp(struct GlobalVars *,char *,uint32 *,uint8);
- uint32 makereloc(struct GlobalVars *,struct Expression *);
- uint32 makexref(struct GlobalVars *,struct Expression *,uint8);
- char *getintexp(struct GlobalVars *,char *,uint32 *);
- char *eval_expression(struct GlobalVars *,struct Expression *,char *);
-
- static uint32 hihalf(struct GlobalVars *,uint32);
- static uint32 lohalf(struct GlobalVars *,uint32);
- static uint32 read_hex(char *);
- static uint32 read_dec(char *);
- static uint32 read_oct(char *);
- static uint32 read_bin(char *);
- static uint32 read_str(char *);
-
-
-
- /* table of valid symbol characters,
- 0=invalid, 1=valid in whole symbol, 2=valid, but not as first char */
- static uint8 valid_symchars[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, /* $ . */
- 2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,1, /* 0-9 ? */
- 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* A-O */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* P-Z _ */
- 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* a-o */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, /* p-z */
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
- };
-
- /* table of valid argument characters,
- 0=invalid, 1=valid, 2=valid, but indicates string */
- static uint8 valid_argchars[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,2,0,1,0,0,2,0,0,0,0,0,0,1,0, /* " $ ' . */
- 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1, /* 0-9 ? */
- 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* A-O */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* P-Z _ */
- 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* a-o */
- 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, /* p-z */
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
- };
-
- /* valid operators */
- #define NUMOPS 10
- static char valid_operators[NUMOPS] = {
- '+','-','*','/','%','<','>','&','|','^'
- };
- #define MAXOPPRI 5
- static uint8 op_priority[NUMOPS] = {
- 4,4,5,5,5,3,3,2,0,1
- };
-
-
- char *getsymbol(struct GlobalVars *gv,char *s)
- /* read a symbol string into gv->strbuf */
- {
- uint8 *vsc = valid_symchars;
- char *b = gv->strbuf;
- int bsize = STRBUFSIZE-2;
-
- if (*s == '@') { /* '@' is allowed as first character */
- *b++ = *s++;
- bsize--;
- }
- if (vsc[(unsigned char)*s] == 1) { /* first character valid? */
- *b++ = *s++;
- while (vsc[(unsigned char)*s] && bsize--)
- *b++ = *s++;
- }
- *b = 0;
- return (s);
- }
-
-
- char *getarg(struct GlobalVars *gv,char *s)
- /* read next argument into gv->strbuf */
- {
- uint8 v,*vac = valid_argchars;
- char c,*b = gv->strbuf;
- int bsize = STRBUFSIZE-1;
-
- if (*s == '@') { /* '@' is allowed as first character */
- *b++ = *s++;
- bsize--;
- }
- while ((v = vac[(unsigned char)*s]) && bsize--) {
- *b++ = *s++;
- if (v==2) { /* string? */
- c = *(s-1);
- for (;;) {
- if (bsize--) {
- if (!(*b++ = *s)) /* string can only be terminated by EOL */
- return (s);
- }
- else
- return (s);
- if (*s++ == c) {
- if (*s==c || *(s-2)=='\\') {
- if (bsize--) /* "", '' \" or \' don't terminate the string */
- *b++ = *s++;
- else
- return (s);
- }
- else
- break;
- }
- }
- }
- }
- *b = 0;
- return (s);
- }
-
-
- char *skipspaces(char *s)
- /* advance string pointer to the first character, which is no white space */
- {
- while (*s==' ' || *s=='\t')
- ++s;
- return (s);
- }
-
-
- char *remquotes(char *s)
- /* remove " or ', if present and return new strbuf-pointer */
- {
- int len;
- char c = *s;
-
- if (c=='\"' || c=='\'') /* string is in quotes */
- if (len = strlen(++s) - 1)
- if (s[len] == c)
- s[len] = 0;
- return (s);
- }
-
-
- void checkEOL(char *s)
- /* check for illegal extra characters on line */
- {
- s = skipspaces(s);
- if (*s && *s!='#') /* only comment is allowed as an extra character */
- error(18); /* extra characters on line */
- }
-
-
- char *skipexpression(struct GlobalVars *gv,char *s)
- {
- char c;
-
- do {
- s = getarg(gv,s);
- s = skipspaces(s);
- c = *s++;
- }
- while (c!=0 && c!='#' && c!=',');
- return (--s);
- }
-
-
- void read_macro_params(struct GlobalVars *gv,struct ParsedLine *pl,
- struct MacroParams *mp,char *op)
- /* read parameters and store pointers to their first character */
- {
- char c;
-
- mp->param[0] = mp->param0;
- if (pl->branch_hint)
- mp->param0[0] = pl->branch_hint>0 ? '+' : '-';
- for (;;) {
- op = skipspaces(op);
- c = *op;
- if (c!=0 && c!='#') { /* another parameter? */
- if (mp->narg < (MAX_MACPARAMS-1)) {
- mp->param[++mp->narg] = op;
- op = skipexpression(gv,op);
- c = *op;
- if (c==0 || c=='#') {
- *op = 0; /* end of line / start of comment */
- return;
- }
- *op++ = 0; /* next parameter */
- }
- else {
- error(11); /* too many macro parameters */
- return;
- }
- }
- else
- return;
- }
- }
-
-
- char *getexp(struct GlobalVars *gv,char *s,uint32 *val,uint8 size)
- /* evaluate expression, create relocations or x-references, if required */
- {
- struct Expression exp;
-
- s = eval_expression(gv,&exp,s);
- if (exp.type != SYM_UNDEF) {
- if (exp.reloctype==R_PPC_ADDR32 && size==2)
- exp.reloctype = R_PPC_ADDR16;
- switch (exp.type) {
- case SYM_RELOC:
- *val = makereloc(gv,&exp); /* make relocation entry */
- break;
- case SYM_EXTERN: /* reference to external symbol */
- *val = makexref(gv,&exp,size);
- break;
- default: /* SYM_ABS */
- *val = exp.value;
- break;
- }
- }
- else
- error(19); /* undefined symbol */
- return (s);
- }
-
-
- static uint32 hihalf(struct GlobalVars *gv,uint32 x)
- {
- if (gv->signedexp)
- return ((int32)x/0x10000);
- else
- return (x>>16);
- }
-
-
- static uint32 lohalf(struct GlobalVars *gv,uint32 x)
- {
- if (gv->signedexp)
- return ((int16)x);
- else
- return (x&0xffff);
- }
-
-
- uint32 makereloc(struct GlobalVars *gv,struct Expression *exp)
- /* make new relocation entry for current section */
- {
- struct Reloc dummy,*reloc;
-
- if (gv->anotherpass) {
- /* don't waste memory, if another pass is required anyway */
- reloc = &dummy;
- }
- else {
- reloc = alloc(sizeof(struct Reloc));
- addtail(&gv->csect->reloclist,&reloc->n);
- }
- reloc->relocsect = exp->symbol->relsect;
- reloc->offset = gv->csect->pc;
- reloc->type = exp->reloctype;
- reloc->addend = exp->value;
- switch (exp->reloctype) {
- case R_PPC_ADDR16_HA:
- case R_PPC_ADDR16_HI:
- reloc->offset += 2;
- return (hihalf(gv,exp->value));
- case R_PPC_ADDR16_LO:
- reloc->offset += 2;
- return (lohalf(gv,exp->value));
- case R_PPC_TOC16:
- reloc->offset += 2;
- return (exp->value);
- case R_PPC_REL14:
- case R_PPC_REL14_BRTAKEN:
- case R_PPC_REL14_BRNTAKEN:
- if (gv->output == OFMT_EHF) {
- reloc->offset += 2;
- return (exp->value + 2);
- }
- default:
- return (exp->value);
- }
- }
-
-
- uint32 makexref(struct GlobalVars *gv,struct Expression *exp,uint8 size)
- {
- struct XReference dummy,*xref;
-
- if (gv->anotherpass) {
- /* don't waste memory, if another pass is required anyway */
- xref = &dummy;
- }
- else {
- xref = alloc(sizeof(struct XReference));
- addtail(&gv->csect->xreflist,&xref->n);
- }
- xref->xsymbol = exp->symbol;
- xref->offset = gv->csect->pc;
- xref->addend = exp->value;
- xref->type = exp->reloctype;
- xref->size = size;
- switch (exp->reloctype) {
- case R_PPC_ADDR16_HA:
- case R_PPC_ADDR16_HI:
- xref->offset += 2;
- return (hihalf(gv,exp->value));
- case R_PPC_ADDR16_LO:
- xref->offset += 2;
- return (lohalf(gv,exp->value));
- case R_PPC_TOC16:
- xref->offset += 2;
- return (exp->value);
- case R_PPC_REL14:
- case R_PPC_REL14_BRTAKEN:
- case R_PPC_REL14_BRNTAKEN:
- if (gv->output == OFMT_EHF) {
- xref->offset += 2;
- return (exp->value + 2);
- }
- default:
- return (exp->value);
- }
- }
-
-
- char *getintexp(struct GlobalVars *gv,char *s,uint32 *val)
- /* evaluate expression and check if absolute integer */
- {
- struct Expression exp;
-
- s = eval_expression(gv,&exp,s);
- if (exp.type != SYM_UNDEF) {
- if (exp.type == SYM_ABS)
- *val = exp.value;
- else
- error(24); /* constant integer expression required */
- }
- else
- error(19); /* undefined symbol */
- return (s);
- }
-
-
- char *eval_expression(struct GlobalVars *gv,struct Expression *exp_result,
- char *s)
- /* evaluate an integer expression and determine its type */
- {
- struct Expression exp[EXPSTACKSIZE];
- uint8 ops[EXPSTACKSIZE-1],op,ta,tb;
- int i,j,k,exp_args=0;
- uint8 unary; /* unary operator, 0=none, 1=plus, 2=minus, 3=not */
- char c;
- char *arg=gv->strbuf;
- struct Symbol *sym;
-
- exp_result->value = 0;
- exp_result->type = SYM_UNDEF;
-
- /* build expression stack */
- for (;;) {
- unary = 0;
- exp[exp_args].reloctype = R_PPC_ADDR32;
-
- for (;;) {
- s = skipspaces(s);
- s = getarg(gv,s); /* try to get next argument */
- if (c = *arg)
- break; /* got it */
-
- if ((c=*s++) == '(') { /* beginning of a term? */
- s = eval_expression(gv,&exp[exp_args],s);
- if (*s++ != ')') {
- error(21); /* missing closing parenthesis */
- s--;
- }
- *arg = 0; /* expression in term is already evaluated */
- break;
- }
-
- if (c==0 || c=='#' || c==')') {
- error(17); /* missing argument */
- s--;
- c = 0;
- break;
- }
-
- if (unary)
- error(20); /* double unary operator */
- else
- switch (c) {
- case '+':
- unary = 1;
- break;
- case '-':
- unary = 2;
- break;
- case '~':
- unary = 3;
- break;
- default:
- error(13); /* syntax error */
- break;
- }
- }
-
- if (!c)
- break; /* evaluate */
-
- if (c = *arg) {
-
- if (c>='0' && c<='9') { /* numeric constant */
- if (c=='0')
- switch (arg[1]) {
- case 'x':
- exp[exp_args].value = read_hex(&arg[2]);
- break;
- case 'b':
- exp[exp_args].value = read_bin(&arg[2]);
- break;
- case 0:
- exp[exp_args].value = 0;
- break;
- default:
- exp[exp_args].value = read_oct(&arg[1]);
- break;
- }
- else
- exp[exp_args].value = read_dec(arg);
- exp[exp_args].type = SYM_ABS;
- }
-
- else if (c=='\"' || c=='\'') { /* string constant */
- exp[exp_args].value = read_str(arg);
- exp[exp_args].type = SYM_ABS;
- }
-
- else { /* get symbol value */
- if (sym = search_symbol(gv,arg)) {
- exp[exp_args].value = sym->value;
- exp[exp_args].type = sym->type;
- exp[exp_args].symbol = sym;
- }
- else {
- if (gv->pass && gv->autoextern) { /* pass 2? auto. decl. extern */
- sym = add_symbol(gv,arg,SYM_EXTERN,0);
- exp[exp_args].value = sym->value;
- exp[exp_args].type = sym->type;
- exp[exp_args].symbol = sym;
- }
- else {
- exp[exp_args].value = 0;
- exp[exp_args].type = SYM_UNDEF;
- }
- }
- }
- }
-
- if (unary) {
- if (exp[exp_args].type >= SYM_RELOC)
- error(22); /* illegal operation for a relocatable expression */
- else
- switch (unary) {
- case 2: /* negate */
- exp[exp_args].value = -exp[exp_args].value;
- break;
- case 3: /* not */
- exp[exp_args].value = ~exp[exp_args].value;
- break;
- }
- }
- if (exp[exp_args].type == SYM_ABS)
- exp[exp_args].reloctype = R_NONE;
-
- /* check for operator */
- s = skipspaces(s);
- c = *s;
- i = 0;
- while (i<NUMOPS) {
- if (c == valid_operators[i])
- break;
- i++;
- }
- if (i==NUMOPS)
- break; /* end of expression - evaluate */
-
- ops[exp_args++] = i;
- if (*(++s) == c)
- if (i==5 || i==6) /* <<, >> */
- ++s;
- }
-
- /* evaluate expression stack */
- for (i=MAXOPPRI; i>=0; i--) {
- for (j=0; j<exp_args; ) {
- op = ops[j];
- if (op_priority[op] == i) {
- if (!(ta = exp[j].type) || !(tb = exp[j+1].type)) {
- exp[j].value = 0;
- exp[j].type = SYM_UNDEF;
- exp[j].reloctype = R_PPC_ADDR32;
- }
- else switch(op) {
-
- case 0: /* + */
- if (ta != tb) {
- if (ta == SYM_ABS) {
- exp[j].type = tb; /* reloc + abs = reloc */
- exp[j].reloctype = exp[j+1].reloctype;
- exp[j].symbol = exp[j+1].symbol;
- }
- else if (tb != SYM_ABS) {
- error(22); /* illegal reloc operation */
- break;
- }
- }
- else if (ta >= SYM_RELOC) {
- error(22);
- break;
- }
- exp[j].value += exp[j+1].value;
- break;
-
- case 1: /* - */
- if (ta != tb) { /* reloc - abs = reloc */
- if (tb != SYM_ABS) {
- error(22);
- break;
- }
- }
- else { /* reloc - reloc = abs | abs - abs = abs */
- if (ta != SYM_EXTERN) { /* extern - extern not supported */
- exp[j].type = SYM_ABS;
- if (ta == SYM_RELOC)
- if (exp[j].symbol->relsect != exp[j+1].symbol->relsect)
- error(23); /* symbols reside in different sections */
- }
- else {
- error(22);
- break;
- }
- }
- exp[j].value -= exp[j+1].value;
- break;
-
- case 2: /* * */
- if (ta == tb == SYM_ABS)
- exp[j].value *= exp[j+1].value;
- else
- error(22);
- break;
-
- case 3: /* / */
- if (ta == tb == SYM_ABS)
- exp[j].value /= exp[j+1].value;
- else
- error(22);
- break;
-
- case 4: /* % */
- if (ta == tb == SYM_ABS)
- exp[j].value %= exp[j+1].value;
- else
- error(22);
- break;
-
- case 5: /* << */
- if (ta == tb == SYM_ABS)
- exp[j].value <<= exp[j+1].value;
- else
- error(22);
- break;
-
- case 6: /* << */
- if (ta == tb == SYM_ABS)
- exp[j].value >>= exp[j+1].value;
- else
- error(22);
- break;
-
- case 7: /* & */
- if (ta == tb == SYM_ABS)
- exp[j].value &= exp[j+1].value;
- else
- error(22);
- break;
-
- case 8: /* | */
- if (ta == tb == SYM_ABS)
- exp[j].value |= exp[j+1].value;
- else
- error(22);
- break;
-
- case 9: /* ^ */
- if (ta == tb == SYM_ABS)
- exp[j].value ^= exp[j+1].value;
- else
- error(22);
- break;
- }
-
- exp_args--;
- for (k=j; k<exp_args; k++) {
- memcpy(&exp[k+1],&exp[k+2],sizeof(struct Expression));
- ops[k] = ops[k+1];
- }
- }
- else
- j++;
- }
- if (exp_args==0)
- break;
- }
-
- memcpy(exp_result,&exp[0],sizeof(struct Expression));
- if (*s == '@') { /* check for @ha, @h or @l */
- s++;
- switch (tolower((unsigned char)*s++)) {
- case 'l': /* symbol@l : lower half-word */
- if (exp_result->type == SYM_ABS) {
- exp_result->reloctype = R_NONE;
- exp_result->value = lohalf(gv,exp_result->value);
- }
- else
- exp_result->reloctype = R_PPC_ADDR16_LO;
- break;
- case 'h':
- if (tolower((unsigned char)*s) == 'a') {
- s++; /* symbol@ha : higher (addi) */
- if (exp_result->type == SYM_ABS) {
- exp_result->reloctype = R_NONE;
- if (exp_result->value & 0x8000)
- exp_result->value = hihalf(gv,exp_result->value + 0x10000);
- else
- exp_result->value = hihalf(gv,exp_result->value);
- }
- else
- exp_result->reloctype = R_PPC_ADDR16_HA;
- }
- else { /* symbol@h : higher half-word */
- if (exp_result->type == SYM_ABS) {
- exp_result->reloctype = R_NONE;
- exp_result->value = hihalf(gv,exp_result->value);
- }
- else
- exp_result->reloctype = R_PPC_ADDR16_HI;
- }
- break;
- default:
- s -= 2;
- break;
- }
- }
-
- return (s);
- }
-
-
- static uint32 read_hex(char *s)
- {
- uint32 x=0,y;
-
- while (y = (unsigned char)*s++) {
- if (y > '9')
- y = (y & 0x5f) - 7;
- x <<= 4;
- x += y - '0';
- }
- return (x);
- }
-
-
- static uint32 read_dec(char *s)
- {
- uint32 x=0,y;
-
- while (y = (unsigned char)*s++) {
- x *= 10;
- x += y - '0';
- }
- return (x);
- }
-
-
- static uint32 read_oct(char *s)
- {
- uint32 x=0,y;
-
- while (y = (unsigned char)*s++) {
- x <<= 3;
- x += y - '0';
- }
- return (x);
- }
-
-
- static uint32 read_bin(char *s)
- {
- uint32 x=0,y;
-
- while (y = (unsigned char)*s++) {
- x <<= 1;
- x += y & 1;
- }
- return (x);
- }
-
-
- static uint32 read_str(char *s)
- {
- uint32 x=0;
- char c,sc=*s++;
-
- while (c = *s++) {
- if (c==sc) {
- if (*s==sc) /* "" is converted into " */
- s++;
- else
- break;
- }
- else if (c=='\\') {
- if (!(c = escchar(*s++)))
- break;
- }
- x <<= 8;
- x += (unsigned char)c;
- }
- return (x);
- }
-