Mercurial > hg > index.cgi
view lwasm/lwasm.c @ 569:37014b2935bf lwtools-4.22
Bump version in preparation for release
author | William Astle <lost@l-w.ca> |
---|---|
date | Fri, 22 Dec 2023 21:03:18 -0700 |
parents | 87f904e2b304 |
children | 40edb7de3857 |
line wrap: on
line source
/* lwasm.c Copyright © 2010 William Astle This file is part of LWTOOLS. LWTOOLS 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/>. */ #include <stdio.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #include <lw_expr.h> #include <lw_alloc.h> #include <lw_string.h> #include <lw_error.h> #include "lwasm.h" #include "instab.h" void lwasm_skip_to_next_token(line_t *cl, char **p) { if (CURPRAGMA(cl, PRAGMA_NEWSOURCE)) { for (; **p && isspace(**p); (*p)++) /* do nothing */ ; } } int lwasm_expr_exportable(asmstate_t *as, lw_expr_t expr) { return 0; } int lwasm_expr_exportval(asmstate_t *as, lw_expr_t expr) { return 0; } void lwasm_dividezero(void *priv) { asmstate_t *as = (asmstate_t *)priv; lwasm_register_error(as, as -> cl, E_DIV0); } lw_expr_t lwasm_evaluate_var(char *var, void *priv) { asmstate_t *as = (asmstate_t *)priv; lw_expr_t e; importlist_t *im; struct symtabe *s; debug_message(as, 225, "eval var: look up %s", var); s = lookup_symbol(as, as -> cl, var); if (s) { debug_message(as, 225, "eval var: symbol found %s = %s (%p)", s -> symbol, lw_expr_print(s -> value), s); e = lw_expr_build(lw_expr_type_special, lwasm_expr_syment, s); return e; } if (as -> undefzero) { debug_message(as, 225, "eval var: undefined symbol treated as 0"); e = lw_expr_build(lw_expr_type_int, 0); return e; } // undefined here is undefied unless output is object if (as -> output_format != OUTPUT_OBJ) goto nomatch; // check for import for (im = as -> importlist; im; im = im -> next) { if (!strcmp(im -> symbol, var)) break; } // check for "undefined" to import automatically if ((as -> passno != 0) && !im && CURPRAGMA(as -> cl, PRAGMA_UNDEFEXTERN)) { debug_message(as, 225, "eval var: importing undefined symbol"); im = lw_alloc(sizeof(importlist_t)); im -> symbol = lw_strdup(var); im -> next = as -> importlist; as -> importlist = im; } if (!im) goto nomatch; e = lw_expr_build(lw_expr_type_special, lwasm_expr_import, im); return e; nomatch: if (as -> badsymerr) { lwasm_register_error2(as, as -> cl, E_SYMBOL_UNDEFINED, "%s", var); } debug_message(as, 225, "eval var: undefined symbol - returning NULL"); return NULL; } lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv) { switch (t) { case lwasm_expr_secbase: { // sectiontab_t *s = priv; asmstate_t *as = priv; if (((sectiontab_t *)ptr) -> tbase != -1) { return lw_expr_build(lw_expr_type_int, ((sectiontab_t *)ptr) -> tbase); } if (as -> exportcheck && ptr == as -> csect) return lw_expr_build(lw_expr_type_int, 0); if (((sectiontab_t *)ptr) -> flags & section_flag_constant) return lw_expr_build(lw_expr_type_int, 0); return NULL; } case lwasm_expr_linedlen: { line_t *cl = ptr; if (cl -> dlen == -1) return NULL; return lw_expr_build(lw_expr_type_int, cl -> dlen); } break; case lwasm_expr_linelen: { line_t *cl = ptr; if (cl -> len != -1) return lw_expr_build(lw_expr_type_int, cl -> len); if (cl -> as -> pretendmax) { if (cl -> maxlen != 0) { //fprintf(stderr, "Pretending max, len = %d\n", cl -> maxlen); return lw_expr_build(lw_expr_type_int, cl -> maxlen); } } return NULL; } break; case lwasm_expr_linedaddr: { line_t *cl = ptr; return lw_expr_copy(cl -> daddr); } case lwasm_expr_lineaddr: { line_t *cl = ptr; if (cl -> addr) return lw_expr_copy(cl -> addr); else return NULL; } case lwasm_expr_syment: { struct symtabe *sym = ptr; return lw_expr_copy(sym -> value); } case lwasm_expr_import: { return NULL; } case lwasm_expr_nextbp: { line_t *cl = ptr; for (cl = cl -> next; cl; cl = cl -> next) { if (cl -> isbrpt) break; } if (cl) { return lw_expr_copy(cl -> addr); } return NULL; } case lwasm_expr_prevbp: { line_t *cl = ptr; for (cl = cl -> prev; cl; cl = cl -> prev) { if (cl -> isbrpt) break; } if (cl) { return lw_expr_copy(cl -> addr); } return NULL; } } return NULL; } const char* lwasm_lookup_error(lwasm_errorcode_t error_code) { switch (error_code) { case E_6309_INVALID: return "Illegal use of 6309 instruction in 6809 mode"; case E_6809_INVALID: return "Illegal use of 6809 instruction in 6309 mode"; case E_ALIGNMENT_INVALID: return "Invalid alignment"; case E_BITNUMBER_INVALID: return "Invalid bit number"; case E_BITNUMBER_UNRESOLVED: return "Bit number must be fully resolved"; case E_BYTE_OVERFLOW: return "Byte overflow"; case E_CONDITION_P1: return "Conditions must be constant on pass 1"; case E_DIRECTIVE_OS9_ONLY: return "Directive only valid for OS9 target"; case E_DIV0: return "Division by zero"; case E_EXEC_ADDRESS: return "Exec address not constant!"; case E_EXPRESSION_BAD: return "Bad expression"; case E_EXPRESSION_NOT_CONST: return "Expression must be constant"; case E_EXPRESSION_NOT_RESOLVED: return "Expression not fully resolved"; case E_FILE_OPEN: return "Cannot open file"; case E_FILENAME_MISSING: return "Missing filename"; case E_FILL_INVALID: return "Invalid fill length"; case E_IMMEDIATE_INVALID: return "Immediate mode not allowed"; case E_IMMEDIATE_UNRESOLVED: return "Immediate byte must be fully resolved"; case E_INSTRUCTION_FAILED: return "Instruction failed to resolve."; case E_INSTRUCTION_SECTION: return "Instruction generating output outside of a section"; case E_LINE_ADDRESS: return "Cannot resolve line address"; case E_LINED_ADDRESS: return "Cannot resolve line data address"; case E_OBJTARGET_ONLY: return "Only supported for object target"; case E_OPCODE_BAD: return "Bad opcode"; case E_OPERAND_BAD: return "Bad operand"; case E_PADDING_BAD: return "Bad padding"; case E_PRAGMA_UNRECOGNIZED: return "Unrecognized pragma string"; case E_REGISTER_BAD: return "Bad register"; case E_SETDP_INVALID: return "SETDP not permitted for object target"; case E_SETDP_NOT_CONST: return "SETDP must be constant on pass 1"; case E_STRING_BAD: return "Bad string condition"; case E_SYMBOL_BAD: return "Bad symbol"; case E_SYMBOL_MISSING: return "Missing symbol"; case E_SYMBOL_UNDEFINED: return "Undefined symbol"; case E_SYMBOL_UNDEFINED_EXPORT: return "Undefined exported symbol"; case E_MACRO_DUPE: return "Duplicate macro definition"; case E_MACRO_ENDM: return "ENDM without MACRO"; case E_MACRO_NONAME: return "Missing macro name"; case E_MACRO_RECURSE: return "Attempt to define a macro inside a macro"; case E_MODULE_IN: return "Already in a module!"; case E_MODULE_NOTIN: return "Not in a module!"; case E_NEGATIVE_BLOCKSIZE: return "Negative block sizes make no sense!"; case E_NEGATIVE_RESERVATION: return "Negative reservation sizes make no sense!"; case E_NW_8: return "n,W cannot be 8 bit"; case E_SECTION_END: return "ENDSECTION without SECTION"; case E_SECTION_EXTDEP: return "EXTDEP must be within a section"; case E_SECTION_FLAG: return "Unrecognized section flag"; case E_SECTION_NAME: return "Need section name"; case E_SECTION_TARGET: return "Cannot use sections unless using the object target"; case E_STRUCT_DUPE: return "Duplicate structure definition"; case E_STRUCT_NONAME: return "Cannot declare a structure without a symbol name."; case E_STRUCT_NOSYMBOL: return "Structure definition with no effect - no symbol"; case E_STRUCT_RECURSE: return "Attempt to define a structure inside a structure"; case E_SYMBOL_DUPE: return "Multiply defined symbol"; case E_UNKNOWN_OPERATION: return "Unknown operation"; case E_ORG_NOT_FOUND: return "Previous ORG not found"; case E_COMPLEX_INCOMPLETE: return "Incomplete expression too complex"; case E_USER_SPECIFIED: return "User Specified:"; case E_ILL5: return "Illegal 5 bit offset"; case E_INCLUDEBIN_ILL_START: return "Start value out of range"; case E_INCLUDEBIN_ILL_LENGTH: return "Length value out of range"; case W_ENDSTRUCT_WITHOUT: return "ENDSTRUCT without STRUCT"; case W_DUPLICATE_SECTION: return "Section flags can only be specified the first time; ignoring duplicate definition"; case W_NOT_SUPPORTED: return "Not supported"; case W_OPERAND_SIZE: return "Operand size larger than required"; default: return "Error"; } } /* keeping this as a separate error output for stability in unit test scripts */ void lwasm_error_testmode(line_t *cl, const char* msg, int fatal) { cl -> as -> testmode_errorcount++; fprintf(stderr, "line %d: %s : %s\n", cl->lineno, msg, cl->ltext); if (fatal == 1) lw_error("aborting\n"); } /* parse unit test input data from comment field */ void lwasm_parse_testmode_comment(line_t *l, lwasm_testflags_t *flags, lwasm_errorcode_t *err, int *len, char **buf) { *flags = 0; if (!l) return; char* s = strstr(l -> ltext, ";."); if (s == NULL) return; char* t = strstr(s, ":"); if (t == NULL) { /* parse: ;.8E0FCE (emitted code) */ if (buf == NULL) return; int i; *flags = TF_EMIT; s = s + 2; /* skip ;. prefix */ t = s; while (*t > 32) t++; if ((t - s) & 1) { lwasm_error_testmode(l, "bad test data (wrong length of hex chars)", 1); return; } *len = (t - s) / 2; t = lw_alloc(*len); *buf = t; for (i = 0; i < *len; i++) { int val; sscanf(s, "%2x", &val); *t++ = (char) val; s += 2; } } else { /* parse: ;.E:1000 or ;.E:7 (warnings or errors) */ *flags = TF_ERROR; char ch = toupper(*(t - 1)); if (ch != 'E') lwasm_error_testmode(l, "bad test data (expected E: flag)", 1); sscanf(t + 1, "%d", (int*) err); } } void lwasm_register_error_real(asmstate_t *as, line_t *l, lwasm_errorcode_t error_code, const char *msg) { lwasm_error_t *e; if (!l) return; if (CURPRAGMA(l, PRAGMA_TESTMODE)) { lwasm_testflags_t flags; lwasm_errorcode_t testmode_error_code; lwasm_parse_testmode_comment(l, &flags, &testmode_error_code, NULL, NULL); if (flags == TF_ERROR) { l -> len = 0; /* null out bogus line */ l -> insn = -1; l -> err_testmode = error_code; if (testmode_error_code == error_code) return; /* expected error: ignore and keep assembling */ char buf[128]; sprintf(buf, "wrong error code (%d)", error_code); lwasm_error_testmode(l, buf, 0); return; } } e = lw_alloc(sizeof(lwasm_error_t)); if (error_code >= 1000) { e->next = l->warn; l->warn = e; as->warningcount++; } else { e->next = l->err; l->err = e; as->errorcount++; } e -> code = error_code; e -> charpos = -1; e -> mess = lw_strdup(msg); } void lwasm_register_error(asmstate_t *as, line_t *l, lwasm_errorcode_t err) { lwasm_register_error_real(as, l, err, lwasm_lookup_error(err)); } void lwasm_register_error2(asmstate_t *as, line_t *l, lwasm_errorcode_t err, const char* fmt, ...) { char errbuff[1024]; char f[128]; sprintf(f, "%s %s", lwasm_lookup_error(err), fmt); va_list args; va_start(args, fmt); (void) vsnprintf(errbuff, 1024, f, args); lwasm_register_error_real(as, l, err, errbuff); va_end(args); } int lwasm_next_context(asmstate_t *as) { int r; r = as -> nextcontext; as -> nextcontext++; return r; } void lwasm_emit(line_t *cl, int byte) { if (CURPRAGMA(cl, PRAGMA_NOOUTPUT)) return; if (cl -> as -> output_format == OUTPUT_OBJ && cl -> csect == NULL) { lwasm_register_error(cl -> as, cl, E_INSTRUCTION_SECTION); return; } if (cl -> outputl < 0) cl -> outputl = 0; if (cl -> outputl == cl -> outputbl) { cl -> output = lw_realloc(cl -> output, cl -> outputbl + 8); cl -> outputbl += 8; } cl -> output[cl -> outputl++] = byte & 0xff; if (cl -> inmod) { asmstate_t *as = cl -> as; // update module CRC // this is a direct transliteration from the nitros9 asm source // to C; it can, no doubt, be optimized for 32 bit processing byte &= 0xff; byte ^= (as -> crc)[0]; (as -> crc)[0] = (as -> crc)[1]; (as -> crc)[1] = (as -> crc)[2]; (as -> crc)[1] ^= (byte >> 7); (as -> crc)[2] = (byte << 1); (as -> crc)[1] ^= (byte >> 2); (as -> crc)[2] ^= (byte << 6); byte ^= (byte << 1); byte ^= (byte << 2); byte ^= (byte << 4); if (byte & 0x80) { (as -> crc)[0] ^= 0x80; (as -> crc)[2] ^= 0x21; } } } void lwasm_emitop(line_t *cl, int opc) { if (cl->cycle_base == 0) lwasm_cycle_update_count(cl, opc); /* only call first time, never on postbyte */ if (opc > 0x100) lwasm_emit(cl, opc >> 8); lwasm_emit(cl, opc); } lw_expr_t lwasm_parse_term(char **p, void *priv) { asmstate_t *as = priv; int neg = 1; int val; if (!**p) return NULL; if (**p == '.' && !((*p)[1] >= 'A' && (*p)[1] <= 'Z') && !((*p)[1] >= 'a' && (*p)[1] <= 'z') && !((*p)[1] >= '0' && (*p)[1] <= '9') ) { (*p)++; return lw_expr_build(lw_expr_type_special, lwasm_expr_linedaddr, as -> cl); } if (**p == '*') { // special "symbol" for current line addr (*) (*p)++; return lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, as -> cl); } // branch points if (**p == '<') { (*p)++; return lw_expr_build(lw_expr_type_special, lwasm_expr_prevbp, as -> cl); } if (**p == '>') { (*p)++; return lw_expr_build(lw_expr_type_special, lwasm_expr_nextbp, as -> cl); } // double ascii constant if (**p == '"') { int v; (*p)++; if (!**p) return NULL; if (!*((*p)+1)) return NULL; v = (unsigned char)**p << 8 | (unsigned char)*((*p)+1); (*p) += 2; if (**p == '"') (*p)++; return lw_expr_build(lw_expr_type_int, v); } /* double ASCII constant, like LDD #'MG */ if (CURPRAGMA(as->cl, PRAGMA_M80EXT)) { if (((**p == '"') || (**p == '\'')) && (as->cl->genmode == 16)) { int v; (*p)++; if (!**p) return NULL; if (!*((*p) + 1)) return NULL; v = (unsigned char) **p << 8 | (unsigned char) *((*p) + 1); (*p) += 2; if ((**p == '"') || (**p == '\'')) (*p)++; return lw_expr_build(lw_expr_type_int, v); } } /* single ASCII constant, like LDA #'E */ if (**p == '\'') { int v; (*p)++; if (!**p) return NULL; v = (unsigned char)**p; (*p)++; if (**p == '\'') (*p)++; return lw_expr_build(lw_expr_type_int, v); } if (**p == '&') { val = 0; // decimal constant (*p)++; if (**p == '-') { (*p)++; neg = -1; } if (!**p || !strchr("0123456789", **p)) { (*p)--; if (neg < 0) (*p)--; return NULL; } while (**p && strchr("0123456789", **p)) { val = val * 10 + (**p - '0'); (*p)++; } return lw_expr_build(lw_expr_type_int, val * neg); } if (**p == '%') { val = 0; // binary constant (*p)++; if (**p == '-') { (*p)++; neg = -1; } if (**p != '0' && **p != '1') { (*p)--; if (neg < 0) (*p)--; return NULL; } while (**p && (**p == '0' || **p == '1')) { val = val * 2 + (**p - '0'); (*p)++; } return lw_expr_build(lw_expr_type_int, val * neg); } if (**p == '$') { // hexadecimal constant int v = 0, v2; (*p)++; if (**p == '-') { (*p)++; neg = -1; } if (!**p || !strchr("0123456789abcdefABCDEF", **p)) { (*p)--; if (neg < 0) (*p)--; return NULL; } while (**p && strchr("0123456789abcdefABCDEF", **p)) { v2 = toupper(**p) - '0'; if (v2 > 9) v2 -= 7; v = v * 16 + v2; (*p)++; } return lw_expr_build(lw_expr_type_int, v * neg); } if (**p == '0' && (*((*p)+1) == 'x' || *((*p)+1) == 'X')) { // hexadecimal constant, C style int v = 0, v2; (*p)+=2; if (!**p || !strchr("0123456789abcdefABCDEF", **p)) { (*p) -= 2; return NULL; } while (**p && strchr("0123456789abcdefABCDEF", **p)) { v2 = toupper(**p) - '0'; if (v2 > 9) v2 -= 7; v = v * 16 + v2; (*p)++; } return lw_expr_build(lw_expr_type_int, v); } if (**p == '@' && (*((*p)+1) >= '0' && *((*p)+1) <= '7')) { // octal constant int v = 0; (*p)++; if (**p == '-') { (*p)++; neg = -1; } if (!**p || !strchr("01234567", **p)) { (*p)--; if (neg < 0) (*p)--; return NULL; } while (**p && strchr("01234567", **p)) { v = v * 8 + (**p - '0'); (*p)++; } return lw_expr_build(lw_expr_type_int, v * neg); } // symbol or bare decimal or suffix constant here do { int havedol = 0; int l = 0; while ((*p)[l] && strchr(SYMCHARS, (*p)[l])) { if ((*p)[l] == '$') havedol = 1; l++; } if (l == 0) return NULL; if ((*p)[l] == '{') { while ((*p)[l] && (*p)[l] != '}') l++; l++; } if (havedol || **p < '0' || **p > '9') { // have a symbol here char *sym; lw_expr_t term; sym = lw_strndup(*p, l); (*p) += l; term = lw_expr_build(lw_expr_type_var, sym); lw_free(sym); return term; } } while (0); if (!**p) return NULL; // we have a numeric constant here, either decimal or postfix base notation { int decval = 0, binval = 0, hexval = 0, octval = 0; int valtype = 15; // 1 = bin, 2 = oct, 4 = dec, 8 = hex int bindone = 0; int val; int dval; while (1) { if (!**p || !strchr("0123456789ABCDEFabcdefqhoQHO", **p)) { // we can legally be bin or decimal here if (bindone) { // just finished a binary value val = binval; break; } else if (valtype & 4) { val = decval; break; } else { // bad value return NULL; } } dval = toupper(**p); (*p)++; if (bindone) { // any characters past "B" means it is not binary bindone = 0; valtype &= 14; } switch (dval) { case 'Q': case 'O': if (valtype & 2) { val = octval; valtype = -1; break; } else { return NULL; } /* can't get here */ case 'H': if (valtype & 8) { val = hexval; valtype = -1; break; } else { return NULL; } /* can't get here */ case 'B': // this is a bit of a sticky one since B may be a // hex number instead of the end of a binary number // so it falls through to the digit case if (valtype & 1) { // could still be binary of hex bindone = 1; valtype = 9; } /* fall through intented */ default: // digit dval -= '0'; if (dval > 9) dval -= 7; if (valtype & 8) hexval = hexval * 16 + dval; if (valtype & 4) { if (dval > 9) valtype &= 11; else decval = decval * 10 + dval; } if (valtype & 2) { if (dval > 7) valtype &= 13; else octval = octval * 8 + dval; } if (valtype & 1) { if (dval > 1) valtype &= 14; else binval = binval * 2 + dval; } } if (valtype == -1) break; // return if no more valid types if (valtype == 0) return NULL; val = decval; // in case we fall through } // get here if we have a value return lw_expr_build(lw_expr_type_int, val); } // can't get here } lw_expr_t lwasm_parse_expr(asmstate_t *as, char **p) { lw_expr_t e; if (as->exprwidth != 16) { lw_expr_setwidth(as->exprwidth); if (CURPRAGMA(as -> cl, PRAGMA_NEWSOURCE)) e = lw_expr_parse(p, as); else e = lw_expr_parse_compact(p, as); lw_expr_setwidth(0); } else { if (CURPRAGMA(as -> cl, PRAGMA_NEWSOURCE)) e = lw_expr_parse(p, as); else e = lw_expr_parse_compact(p, as); } lwasm_skip_to_next_token(as -> cl, p); return e; } int lwasm_reduce_expr(asmstate_t *as, lw_expr_t expr) { if (expr) lw_expr_simplify(expr, as); return 0; } void lwasm_save_expr(line_t *cl, int id, lw_expr_t expr) { struct line_expr_s *e; for (e = cl -> exprs; e; e = e -> next) { if (e -> id == id) { lw_expr_destroy(e -> expr); e -> expr = expr; return; } } e = lw_alloc(sizeof(struct line_expr_s)); e -> expr = expr; e -> id = id; e -> next = cl -> exprs; cl -> exprs = e; } lw_expr_t lwasm_fetch_expr(line_t *cl, int id) { struct line_expr_s *e; for (e = cl -> exprs; e; e = e -> next) { if (e -> id == id) { return e -> expr; } } return NULL; } void skip_operand_real(line_t *cl, char **p) { if (CURPRAGMA(cl, PRAGMA_NEWSOURCE)) return; for (; **p && !isspace(**p); (*p)++) /* do nothing */ ; } struct auxdata { int v; int oc; int ms; }; int lwasm_emitexpr_auxlwmod(lw_expr_t expr, void *arg) { struct auxdata *ad = arg; if (lw_expr_istype(expr, lw_expr_type_int)) { ad -> v = lw_expr_intval(expr); return 0; } if (lw_expr_istype(expr, lw_expr_type_special)) { if (lw_expr_specint(expr) == lwasm_expr_secbase) { sectiontab_t *s; s = lw_expr_specptr(expr); if (strcmp(s -> name, "main") == 0) { ad -> ms = 1; return 0; } if (strcmp(s -> name, "bss")) return -1; return 0; } return -1; } if (lw_expr_whichop(expr) == lw_expr_oper_plus) { if (ad -> oc) return -1; ad -> oc = 1; return 0; } return -1; } int lwasm_emitexpr(line_t *l, lw_expr_t expr, int size) { int v = 0; int ol; ol = l -> outputl; if (ol == -1) ol = 0; if (lw_expr_istype(expr, lw_expr_type_int)) { v = lw_expr_intval(expr); } // handle external/cross-section/incomplete references here else { if (l -> as -> output_format == OUTPUT_LWMOD) { reloctab_t *re; lw_expr_t te; struct auxdata ad; ad.v = 0; ad.oc = 0; ad.ms = 0; if (l -> csect == NULL) { lwasm_register_error(l -> as, l, E_INSTRUCTION_SECTION); return -1; } if (size != 2) { lwasm_register_error(l -> as, l, E_OPERAND_BAD); return -1; } // we have a 16 bit reference here - we need to check to make sure // it's at most a + or - with the BSS section base v = lw_expr_whichop(expr); if (v == -1) { v = 0; if (lw_expr_testterms(expr, lwasm_emitexpr_auxlwmod, &ad) != 0) { lwasm_register_error(l -> as, l, E_COMPLEX_INCOMPLETE); return -1; } v = ad.v; } else if (v == lw_expr_oper_plus) { v = 0; if (lw_expr_operandcount(expr) > 2) { lwasm_register_error(l -> as, l, E_COMPLEX_INCOMPLETE); return -1; } if (lw_expr_testterms(expr, lwasm_emitexpr_auxlwmod, &ad) != 0) { lwasm_register_error(l -> as, l, E_COMPLEX_INCOMPLETE); return -1; } v = ad.v; } else { lwasm_register_error(l -> as, l, E_COMPLEX_INCOMPLETE); return -1; } // add "expression" record to section table re = lw_alloc(sizeof(reloctab_t)); re -> next = l -> csect -> reloctab; l -> csect -> reloctab = re; te = lw_expr_build(lw_expr_type_int, ol); re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te); lw_expr_destroy(te); lwasm_reduce_expr(l -> as, re -> offset); re -> size = size; if (ad.ms == 1) re -> expr = lw_expr_copy(expr); else re -> expr = NULL; lwasm_emit(l, v >> 8); lwasm_emit(l, v & 0xff); return 0; } else if (l -> as -> output_format == OUTPUT_OBJ) { reloctab_t *re; lw_expr_t te; if (l -> csect == NULL) { lwasm_register_error(l -> as, l, E_INSTRUCTION_SECTION); return -1; } if (size == 4) { // create a two part reference because lwlink doesn't // support 32 bit references lw_expr_t te2; te = lw_expr_build(lw_expr_type_int, 0x10000); te2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_divide, expr, te); lw_expr_destroy(te); re = lw_alloc(sizeof(reloctab_t)); re -> next = l -> csect -> reloctab; l -> csect -> reloctab = re; te = lw_expr_build(lw_expr_type_int, ol); re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te); lw_expr_destroy(te); lwasm_reduce_expr(l -> as, re -> offset); re -> expr = te2; re -> size = 2; te = lw_expr_build(lw_expr_type_int, 0xFFFF); te2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_bwand, expr, te); lw_expr_destroy(te); re = lw_alloc(sizeof(reloctab_t)); re -> next = l -> csect -> reloctab; l -> csect -> reloctab = re; te = lw_expr_build(lw_expr_type_int, ol + 2); re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te); lw_expr_destroy(te); lwasm_reduce_expr(l -> as, re -> offset); re -> expr = te2; re -> size = 2; } else { // add "expression" record to section table re = lw_alloc(sizeof(reloctab_t)); re -> next = l -> csect -> reloctab; l -> csect -> reloctab = re; te = lw_expr_build(lw_expr_type_int, ol); re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te); lw_expr_destroy(te); lwasm_reduce_expr(l -> as, re -> offset); re -> size = size; re -> expr = lw_expr_copy(expr); } for (v = 0; v < size; v++) lwasm_emit(l, 0); return 0; } lwasm_register_error(l->as, l, E_EXPRESSION_NOT_RESOLVED); return -1; } switch (size) { case 4: lwasm_emit(l, v >> 24); lwasm_emit(l, v >> 16); /* fallthrough intended */ case 2: lwasm_emit(l, v >> 8); /* fallthrough intended */ case 1: lwasm_emit(l, v); } return 0; } int lwasm_lookupreg2(const char *regs, char **p) { int rval = 0; while (*regs) { if (toupper(**p) == *regs) { if (regs[1] == ' ' && !isalpha(*(*p + 1))) break; if (toupper(*(*p + 1)) == regs[1]) break; } regs += 2; rval++; } if (!*regs) return -1; if (regs[1] == ' ') (*p)++; else (*p) += 2; return rval; } int lwasm_lookupreg3(const char *regs, char **p) { int rval = 0; while (*regs) { if (toupper(**p) == *regs) { if (regs[1] == ' ' && !isalpha(*(*p + 1))) break; if (toupper(*(*p + 1)) == regs[1]) { if (regs[2] == ' ' && !isalpha(*(*p + 2))) break; if (toupper(*(*p + 2)) == regs[2]) break; } } regs += 3; rval++; } if (!*regs) return -1; if (regs[1] == ' ') (*p)++; else if (regs[2] == ' ') (*p) += 2; else (*p) += 3; return rval; } void lwasm_show_errors(asmstate_t *as) { line_t *cl; lwasm_error_t *e; for (cl = as -> line_head; cl; cl = cl -> next) { if (!(cl -> err) && !(cl -> warn)) continue; // trim "include:" if it appears char* s = cl->linespec; if ((strlen(s) > 8) && (s[7] == ':')) s += 8; while (*s == ' ') s++; for (e = cl -> err; e; e = e -> next) { fprintf(stderr, "%s(%d) : ERROR : %s\n", s, cl->lineno, e->mess); } for (e = cl -> warn; e; e = e -> next) { fprintf(stderr, "%s(%d) : WARNING : %s\n", s, cl->lineno, e->mess); } fprintf(stderr, "%s:%05d %s\n\n", cl -> linespec, cl -> lineno, cl -> ltext); } } /* this does any passes and other gymnastics that might be useful to see if an expression reduces early */ void do_pass3(asmstate_t *as); void do_pass4_aux(asmstate_t *as, int force); void lwasm_interim_reduce(asmstate_t *as) { do_pass3(as); // do_pass4_aux(as, 0); } lw_expr_t lwasm_parse_cond(asmstate_t *as, char **p) { lw_expr_t e; debug_message(as, 250, "Parsing condition"); e = lwasm_parse_expr(as, p); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); if (!e) { lwasm_register_error(as, as -> cl, E_EXPRESSION_BAD); return NULL; } /* handle condundefzero */ if (CURPRAGMA(as -> cl, PRAGMA_CONDUNDEFZERO)) { as -> undefzero = 1; lwasm_reduce_expr(as, e); as -> undefzero = 0; } /* we need to simplify the expression here */ debug_message(as, 250, "Doing interim reductions"); lwasm_interim_reduce(as); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); debug_message(as, 250, "Reducing expression"); lwasm_reduce_expr(as, e); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); /* lwasm_reduce_expr(as, e); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); lwasm_reduce_expr(as, e); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); lwasm_reduce_expr(as, e); debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e)); */ lwasm_save_expr(as -> cl, 4242, e); if (!lw_expr_istype(e, lw_expr_type_int)) { debug_message(as, 250, "Non-constant expression"); lwasm_register_error(as, as -> cl, E_CONDITION_P1); return NULL; } debug_message(as, 250, "Returning expression"); return e; } struct range_data { int min; int max; asmstate_t *as; }; int lwasm_calculate_range(asmstate_t *as, lw_expr_t expr, int *min, int *max); int lwasm_calculate_range_tf(lw_expr_t e, void *info) { struct range_data *rd = info; int i; if (lw_expr_istype(e, lw_expr_type_int)) { i = lw_expr_intval(e); rd -> min += i; rd -> max += i; return 0; } if (lw_expr_istype(e, lw_expr_type_special)) { line_t *l; if (lw_expr_specint(e) != lwasm_expr_linelen) { rd -> min = -1; return -1; } l = (line_t *)lw_expr_specptr(e); if (l -> len == -1) { rd -> min += l -> minlen; rd -> max += l -> maxlen; } else { rd -> min += l -> len; } return 0; } if (lw_expr_istype(e, lw_expr_type_var)) { lw_expr_t te; te = lw_expr_copy(e); lwasm_reduce_expr(rd -> as, te); if (lw_expr_istype(te, lw_expr_type_int)) { i = lw_expr_intval(te); rd -> min += i; rd -> max += i; } else { rd -> min = -1; } lw_expr_destroy(te); if (rd -> min == -1) return -1; return 0; } if (lw_expr_istype(e, lw_expr_type_oper)) { if (lw_expr_whichop(e) == lw_expr_oper_plus) return 0; rd -> min = -1; return -1; } rd -> min = -1; return -1; } int lwasm_calculate_range(asmstate_t *as, lw_expr_t expr, int *min, int *max) { struct range_data rd; rd.min = 0; rd.max = 0; rd.as = as; if (!expr) return -1; lw_expr_testterms(expr, lwasm_calculate_range_tf, (void *)&rd); *min = rd.min; *max = rd.max; if (rd.min == -1) return -1; return 0; } void lwasm_reduce_line_exprs(line_t *cl) { asmstate_t *as; struct line_expr_s *le; int i; as = cl -> as; as -> cl = cl; // simplify address lwasm_reduce_expr(as, cl -> addr); // simplify data address lwasm_reduce_expr(as, cl -> daddr); // simplify each expression for (i = 0, le = cl -> exprs; le; le = le -> next, i++) { debug_message(as, 101, "Reduce expressions: (pre) exp[%d] = %s", i, lw_expr_print(le -> expr)); lwasm_reduce_expr(as, le -> expr); debug_message(as, 100, "Reduce expressions: exp[%d] = %s", i, lw_expr_print(le -> expr)); } if (cl -> len == -1 || cl -> dlen == -1) { // try resolving the instruction length // but don't force resolution if (cl -> insn >= 0 && instab[cl -> insn].resolve) { (instab[cl -> insn].resolve)(as, cl, 0); if ((cl -> inmod == 0) && cl -> len >= 0 && cl -> dlen >= 0) { if (cl -> len == 0) cl -> len = cl -> dlen; else cl -> dlen = cl -> len; } } } debug_message(as, 100, "Reduce expressions: len = %d", cl -> len); debug_message(as, 100, "Reduce expressions: dlen = %d", cl -> dlen); debug_message(as, 100, "Reduce expressions: addr = %s", lw_expr_print(cl -> addr)); debug_message(as, 100, "Reduce expressions: daddr = %s", lw_expr_print(cl -> daddr)); }