Mercurial > hg-old > index.cgi
view src/lwasm.c @ 8:f1df096aa76f 1.1
Tagged 1.1 bugfix release
author | lost |
---|---|
date | Sun, 04 Jan 2009 05:46:07 +0000 |
parents | 34568fab6058 |
children | 05d4115b4860 |
line wrap: on
line source
/* lwasm.c Copyright © 2008 William Astle This file is part of LWASM. LWASM 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 3 of the License, or (at your option) any later version. This program 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 this program. If not, see <http://www.gnu.org/licenses/>. Contains the main code for lwasm */ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define __lwasm_c_seen__ #include "instab.h" #include "lwasm.h" void lwasm_read_file(asmstate_t *as, char *fname); extern int add_macro_line(asmstate_t *as, sourceline_t *cl, char *optr); extern void expand_macro(asmstate_t *as, sourceline_t *cl, char **optr); #define debug(mess, ...) do { if (as->debug) { fprintf(stderr, "DEBUG: "); fprintf(stderr, (mess), ## __VA_ARGS__); } } while (0) void register_error(asmstate_t *as, sourceline_t *cl, int errcode) { errortab_t *e; e = malloc(sizeof(errortab_t)); e -> errnum = errcode; e -> line = cl; e -> next = cl -> errors; cl -> errors = e; as -> errorcount++; } int eval_expr(asmstate_t *as, sourceline_t *cl, char **optr, int *val); int eval_min(int v1, int v2, int v3, int v4) { if (v2 < v1) v1 = v2; if (v3 < v1) v1 = v3; if (v4 < v1) v1 = v4; return v1; } int eval_max(int v1, int v2, int v3, int v4) { if (v2 > v1) v1 = v2; if (v3 > v1) v1 = v3; if (v4 > v1) v1 = v4; return v1; } int lookupreg3(const char *rlist, char **str) { int rval = 0; int f = 0; const char *reglist = rlist; while (*reglist) { if (toupper(**str) == *reglist) { // first char matches if (reglist[1] == ' ') { f = 1; break; } if (toupper(*(*str + 1)) == reglist[1]) { // second char matches if (reglist[2] == ' ') { f = 1; break; } if (toupper(*(*str + 2)) == reglist[2]) { f = 1; break; } } } reglist += 3; rval++; } if (f == 0) return -1; reglist = rval * 3 + rlist; if (reglist[1] == ' ') (*str) += 1; else if (reglist[2] == ' ') (*str) += 2; else (*str)+=3; return rval; } int lookupreg(const char *reglist, char **str) { int rval = 0; while (*reglist) { if (toupper(**str) == *reglist) { // first char matches if (reglist[1] == ' ' && !isalpha(*(*str + 1))) break; if (toupper(*(*str + 1)) == reglist[1]) break; } reglist += 2; rval++; } if (!*reglist) return -1; if (reglist[1] == ' ') (*str)++; else (*str)+=2; return rval; } void addcodebyte(asmstate_t *as, sourceline_t *cl, int cb) { cl -> len += 1; if (as -> passnum != 2) return; if (cl -> numcodebytes >= cl -> codesize) { cl -> codebytes = realloc(cl -> codebytes, cl -> codesize + 32); cl -> codesize += 32; } debug("EMIT: %02x\n", cb & 0xff); cl -> codebytes[cl -> numcodebytes++] = cb & 0xFF; } // parse a symble out of the line and return a pointer // to a static pointer // return NULL if not a symbol or a bad symbol char *parse_symbol(asmstate_t *as, char **ptr) { static char *symptr = NULL; char *tptr = *ptr; int sl = 0; // symbol can start with _,a-z,A-Z if (!strchr(SYMCHAR_START, **ptr)) return NULL; while (*tptr && !isspace(*tptr) && strchr(SYMCHAR, *tptr)) { tptr++; sl++; } symptr = realloc(symptr, sl + 1); tptr = symptr; while (sl) { *tptr++ = *(*ptr)++; sl--; } *tptr = '\0'; return symptr; } // resolve an instruction void resolve_insn(asmstate_t *as, sourceline_t *cl) { char *optr; char opbuf[MAX_OP_LEN + 1]; char *symbol = NULL; int c; cl -> code_symloc = as -> addr; cl -> addrset = 0; cl -> isequ = 0; cl -> len = 0; cl -> undef = 0; // only parse line on first pass if (as -> passnum == 1) { optr = cl -> line; if (!*optr || *optr == '*' || *optr == ';') { cl -> opcode = -1; cl -> remainder = cl -> line; return; } if (!isspace(*optr)) { symbol = parse_symbol(as, &optr); if (*optr && !isspace(*optr) && !(as -> inmacro)) { errorp1(ERR_BADSYM); while (*optr && !isspace(*optr)) optr++; } if (symbol) { cl -> symstr = strdup(symbol); cl -> hassym = 1; } } while (isspace(*optr)) optr++; // parse opcode if (*optr && *optr != ';') { c = 0; while (c < MAX_OP_LEN && *optr && !isspace(*optr)) { opbuf[c++] = *optr++; } opbuf[c] = '\0'; if (*optr && !isspace(*optr) && !(as -> inmacro)) { errorp1(ERR_BADOP); cl -> opcode = -1; } else { cl -> opcstr = strdup(opbuf); for (c = 0; instab[c].opcode; c++) { if (!strcasecmp(opbuf, instab[c].opcode)) break; } if (!instab[c].opcode && opbuf[0] == '*') { cl -> opcode = -1; } else if (!instab[c].opcode && !(as -> inmacro)) { cl -> opcode = -1; // look up macro if (as -> macros) { macrotab_t *m; for (m = as -> macros; m; m = m -> next) { if (!strcmp(m -> name, opbuf)) break; } if (m) { // we have a macro here cl -> macro = m; while (*optr && isspace(*optr)) optr++; expand_macro(as, cl, &optr); return; } else { errorp1(ERR_BADOP); } } else { errorp1(ERR_BADOP); } } else cl -> opcode = c; } } else cl -> opcode = -1; if (as -> inmacro && cl -> opcode >= 0 && instab[cl -> opcode].specialnum != SPECIAL_ENDM) { add_macro_line(as, cl, cl -> line); cl -> opcode = -1; cl -> remainder = cl -> line; cl -> opcstr = NULL; cl -> operstr = NULL; cl -> symstr = NULL; cl -> hassym = 0; cl -> macrodef = 1; return; } // parse operand while (*optr && isspace(*optr)) optr++; cl -> operstr = optr; } else optr = cl -> operstr; if (as -> skipcond) { // if skipping a condition, need to skip a macro if (cl -> opcode >= 0) { if (instab[cl -> opcode].specialnum == SPECIAL_MACRO) { as -> skipmacro = 1; } else if (instab[cl -> opcode].specialnum == SPECIAL_ENDM) { as -> skipmacro = 0; } else if (instab[cl -> opcode].specialnum == SPECIAL_COND && !(as -> skipmacro)) { as -> skipcount++; } else if (instab[cl -> opcode].specialnum == SPECIAL_ENDC && !(as -> skipmacro)) { as -> skipcount--; if (as -> skipcount <= 0) { as -> skipcond = 0; as -> noelse = 0; } } else if (instab[cl -> opcode].specialnum == SPECIAL_ELSE && !(as -> skipmacro)) { if (as -> skipcount == 1) { as -> skipcount = 0; as -> skipcond = 0; as -> noelse = 1; return; } } } if (as -> skipcond) cl -> skipped = 1; return; } // do the code thing // on pass 1, no code is generated // on pass 2, code is generated using the "emit()" macro if (cl -> opcode >= 0) { if (instab[cl -> opcode].opfn) { (*(instab[cl -> opcode].opfn))(as, cl, &optr); if (as -> passnum == 1) { if (*optr) { char *t = optr; char t2; t2 = *optr; cl -> operstr = strdup(cl -> operstr); *optr = t2; while (*t && isspace(*t)) t++; cl -> remainder = strdup(t); } cl -> remainder = optr; } } else { errorp1(ERR_BADOP); cl -> opcode = -1; } } // address of the symbol may have been changed by a pseudo op // so we couldn't register it above // that means it may turn out to be a "forward ref" in pass 1 if (cl -> hassym) { register_symbol(as, cl, cl -> symstr, cl -> code_symloc, cl -> isset ? SYMFLAG_SET : SYMFLAG_NONE); } as -> addr += cl -> len; } void generate_code(asmstate_t *as) { sourceline_t *cl; as -> addr = 0; as -> dpval = 0; as -> passnum = 2; for (cl = as -> source_head; cl; cl = cl -> next) { resolve_insn(as, cl); } } void lwasm_read_file(asmstate_t *as, char *fname) { FILE *f; int cline = 0; sourceline_t *cl; size_t bufflen; char *buff = NULL; int retval; as -> passnum = 1; f = fopen(fname, "r"); if (!f) { fprintf(stderr, "Cannot open input file %s: %s\n", fname, strerror(errno)); return; } while (!feof(f)) { retval = getline(&buff, &bufflen, f); debug(" read line (%s:%d): %s\n", fname, cline, buff); if (retval < 0) { if (feof(f)) break; fprintf(stderr, "Error reading '%s': %s\n", fname, strerror(errno)); exit(1); } if (strchr(buff, '\n')) *strchr(buff, '\n') = '\0'; if (strchr(buff, '\r')) *strchr(buff, '\r') = '\0'; cl = calloc(sizeof(sourceline_t), 1); if (!cl) { perror("Malloc"); exit(1); } cl -> lineno = cline++; cl -> sourcefile = fname; cl -> opcode = -1; cl -> addrmode = -1; cl -> addr = as -> addr; cl -> dpval = as -> dpval; cl -> prev = as -> source_tail; if (as -> source_tail) as -> source_tail -> next = cl; as -> source_tail = cl; if (as -> source_head == NULL) as -> source_head = cl; cl -> line = strdup(buff); resolve_insn(as, cl); if (cl -> opcode >= 0 && instab[cl -> opcode].instype == INSTYPE_PSEUDO && instab[cl -> opcode].specialnum == SPECIAL_END) break; *buff = '\0'; } if (buff) free(buff); fclose(f); return; } /* below this point is the expression evaluation package Supported binary operators: + - / * % Supported unary operators: - <infix>: + | - | * | / | % <unary>: - <expr>: <term> <infix> <term> <term>: <unary> <term> <term>: ( <expr> ) <term>: <symbol> <term>: ' <char> <term>: " <char> <char> <term>: * <term>: <number> <number>: <dec> <number>: & <dec> <number>: $ <hex> <number>: <hex> H <number>: @ <oct> <number>: <oct> O <number>: <oct> Q <number>: % <bin> <number>: <bin> B <bin>: 0 | 1 <oct>: <bin> | 2 | 3 | 4 | 5 | 6 | 7 <dec>: <oct> | 8 | 9 <hex>: <dec> | A | B | C | D | E | F NOTE: hex values which start with a non-digit will need to be prefixed by $ or have a 0 as the leading digit; hence: $CC or 0CCH otherwise the assembler cannot tell the difference between CCH as a symbol or CCH as the value $CC */ // will throw an error and return 0 in tval if there's a problem // -1 is problem; cl -> undef set is undefined symbol int eval_term(asmstate_t *as, sourceline_t *cl, char **optr, int *tval) { char tc; int rval; int binval; int octval; int decval; int hexval; int valtype; int digval; int bindone = 0; *tval = 0; beginagain: tc = **optr; if (tc == '+') { // unary +, ignored for symetry (*optr)++; goto beginagain; } if (tc == '(') { (*optr)++; rval = eval_expr(as, cl, optr, tval); if (rval < 0) return rval; if (**optr != ')') { errorp1(ERR_BADEXPR); return -1; } (*optr)++; return 0; } if (tc == '-') { (*optr)++; rval = eval_term(as, cl, optr, tval); if (rval < 0) return rval; *tval = -*tval; return 0; } // current address (of current instruction, not PC) if (tc == '*') { *tval = cl -> addr; (*optr)++; return 0; } if (strchr("abcdefghijklmnopqrstuvwxyz_", tolower(tc))) { // evaluate a symbol char *symbuf; symbuf = parse_symbol(as, optr); if (!symbuf) { errorp1(ERR_BADSYM); *tval = 0; return -1; } debug(" looking up symbol: %s\n", symbuf); *tval = lookup_symbol(as, symbuf); // if not found, flag forward ref if (*tval == -1) { errorp2(ERR_UNDEF); cl -> undef = 1; *tval = 0; return 0; } return 0; } if (tc == '%') { // binary number int v1 = 0; (*optr)++; while (strchr("01", **optr)) { v1 = v1 << 1 | ((*(*optr)++) - '0'); } *tval = v1; return 0; } if (tc == '$') { // hex number int v1 = 0; (*optr)++; debug("HEX CONST: %s\n", *optr); while (**optr && strchr("01234567890ABCDEF", toupper(tc = **optr))) { debug("HEX 2: %02x\n", tc); if (**optr >= 'A') { v1 = v1 << 4 | (toupper((*(*optr)++)) - 'A' + 10); } else { v1 = v1 << 4 | ((*(*optr)++) - '0'); } } *tval = v1; return 0; } if (tc == '@') { // octal number int v1 = 0; (*optr)++; while (strchr("01234567", **optr)) { v1 = v1 << 3 | ((*(*optr)++) - '0'); } *tval = v1; return 0; } if (tc == '&') { // decimal number int v1 = 0; (*optr)++; while (strchr("0123456789", **optr)) { v1 = v1 * 10 + ((*(*optr)++) - '0'); } *tval = v1; return 0; } if (tc == '\'') { (*optr)++; if (!**optr) { errorp1(ERR_BADEXPR); return -2; } *tval = *(*optr)++; return 0; } if (tc == '"') { (*optr)++; if (!**optr || !*(*optr + 1)) { errorp1(ERR_BADEXPR); return -2; } *tval = *(*optr)++ << 8 | *(*optr)++; return 0; } // end of string if (tc == '\0') { // error if at EOS as we are looking for a term errorp1(ERR_BADEXPR); return -1; } // we have a generic number here which may be decimal, hex, binary, or octal // based on a suffix // possible data types are binary (1), octal (2), decimal(4), hex (8) valtype = 15; hexval = octval = decval = binval = 0; while (1) { // printf(" %c\n", **optr); if (!**optr || !strchr("ABCDEFabcdefqhoQHO0123456789", **optr)) { // end of string, must be decimal or the end of a bin if (bindone == 1) { *tval = binval; return 0; } if (valtype & 4) { *tval = decval; return 0; } else { errorp1(ERR_BADEXPR); return -1; } } tc = toupper(*(*optr)++); if (tc == 'H') { if (valtype & 8) { *tval = hexval; return 0; } else { // syntax error errorp1(ERR_BADEXPR); return -1; } } if (tc == 'Q' || tc == 'O') { if (valtype && 2) { *tval = octval; return 0; } else { errorp1(ERR_BADEXPR); return -1; } } digval = tc - '0'; if (digval > 9) digval -= 7; // if it's not in the range of a hex digit, error out if (tc < '0' || (tc > '9' && tc < 'A') || tc > 'F') { (*optr)--; if (valtype & 4) { *tval = decval; return 0; } // if we're in hex/bin mode and run to the end of the number // we must have a binary constant or an error // if the previous character is B, then we have binary // else we have error since hex would require a terminating H // which would be caught above if (valtype == 8 && toupper(*(*optr)) == 'B') { *tval = binval; return 0; } errorp1(ERR_BADEXPR); return -1; } // if we have any characters past the end of the B, it's not binary if (bindone == 1) bindone = 0; if (tc == 'B') bindone = 1; if (digval > 1) valtype &= 14; else if (digval > 7) valtype &= 13; else if (digval > 9) valtype &= 11; if (valtype & 8) { hexval = (hexval << 4) | digval; } if (valtype & 4) { decval = decval * 10 + digval; } if (valtype & 2) { octval = (octval << 3) | digval; } if (valtype & 1 && !bindone) { binval = (binval << 1) | digval; } } // can't get here from there } // returns -1 if the expression cannot be parsed // and returns -2 if there is an undefined symbol reference // resulting value will be in *val; undefined symbols are parsed as // value 0 but cl -> undef will be set. int eval_expr(asmstate_t *as, sourceline_t *cl, char **optr, int *val) { int left; int right; char oper; int rval; // by default, return 0 in val *val = 0; cl -> undef = 0; rval = eval_term(as, cl, optr, &left); if (rval < 0) return rval; nextop: oper = **optr; // end of expr if (isspace(oper) || oper == ',' || oper == '\0' || oper == ']' || oper == ')') goto retleft; // unrecognized chars if (!strchr("+-*/%", oper)) goto retleft; (*optr)++; rval = eval_term(as, cl, optr, &right); // propagate error if (rval < 0) return rval; // do the operation and put it in "left" switch (oper) { case '+': left += right; break; case '-': left -= right; break; case '*': left *= right; break; case '/': left /= right; break; case '%': left %= right; break; } goto nextop; retleft: *val = left; return 0; }