# HG changeset patch # User William Astle # Date 1479529543 25200 # Node ID 58cafa61ab4037ae4468b6fbd26e6a69c698668d # Parent 6df8d62302e28e7e0ab2a444b7efcc5a475b04d7 Add support for undocumented custom module format (for LW) Nothing to see here. Move along. These are not the droids you are looking for. diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/lwasm.c --- a/lwasm/lwasm.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/lwasm.c Fri Nov 18 21:25:43 2016 -0700 @@ -119,6 +119,10 @@ { // 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) @@ -272,6 +276,7 @@ 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 W_ENDSTRUCT_WITHOUT: return "ENDSTRUCT without STRUCT"; @@ -947,6 +952,47 @@ /* 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; @@ -963,7 +1009,78 @@ // handle external/cross-section/incomplete references here else { - if (l -> as -> output_format == OUTPUT_OBJ) + 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; diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/lwasm.h --- a/lwasm/lwasm.h Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/lwasm.h Fri Nov 18 21:25:43 2016 -0700 @@ -61,7 +61,8 @@ OUTPUT_OS9, // os9 module target OUTPUT_SREC, // motorola SREC format OUTPUT_IHEX, // intel hex format - OUTPUT_HEX // generic hexadecimal format + OUTPUT_HEX, // generic hexadecimal format + OUTPUT_LWMOD // special module format for LW }; enum lwasm_flags_e @@ -131,6 +132,7 @@ lw_expr_t offset; // offset for next instance int oblen; // size of section output int obsize; // size of output buffer + int tbase; // temporary base value for resolution unsigned char *obytes; // output buffer reloctab_t *reloctab; // table of relocations sectiontab_t *next; @@ -201,7 +203,8 @@ E_UNKNOWN_OPERATION = 55, E_USER_SPECIFIED = 56, E_ORG_NOT_FOUND = 57, - + E_COMPLEX_INCOMPLETE = 58, + /* warnings must be 1000 or greater */ W_DUPLICATE_SECTION = 1000, @@ -380,6 +383,7 @@ int skipmacro; // are we skipping in a macro? int endseen; // have we seen an "end" pseudo? int execaddr; // address from "end" + lw_expr_t execaddr_expr; // address from "end" but as an expression int inmod; // inside an os9 module? int undefzero; // used for handling "condundefzero" int pretendmax; // set if we need to pretend the instruction is max length diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/main.c --- a/lwasm/main.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/main.c Fri Nov 18 21:25:43 2016 -0700 @@ -194,6 +194,10 @@ as -> pragmas |= PRAGMA_DOLLARNOTLOCAL; as -> output_format = OUTPUT_OS9; } + else if (!strcasecmp(arg, "lwmod")) + { + as -> output_format = OUTPUT_LWMOD; + } else { fprintf(stderr, "Invalid output format: %s\n", arg); diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/output.c --- a/lwasm/output.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/output.c Fri Nov 18 21:25:43 2016 -0700 @@ -42,6 +42,7 @@ void write_code_hex(asmstate_t *as, FILE *of); void write_code_srec(asmstate_t *as, FILE *of); void write_code_ihex(asmstate_t *as, FILE *of); +void write_code_lwmod(asmstate_t *as, FILE *of); // this prevents warnings about not using the return value of fwrite() // r++ prevents the "set but not used" warnings; should be optimized out @@ -103,6 +104,10 @@ write_code_ihex(as, of); break; + case OUTPUT_LWMOD: + write_code_lwmod(as, of); + break; + default: fprintf(stderr, "BUG: unrecognized output format when generating output file\n"); fclose(of); @@ -1000,3 +1005,241 @@ // the "" is NOT an error writebytes("", 1, 1, of); } + + +void write_code_lwmod(asmstate_t *as, FILE *of) +{ + line_t *l; + sectiontab_t *s; + reloctab_t *re; + int initsize, bsssize, mainsize, callsnum, namesize; + unsigned char *initcode, *maincode, *callscode, *namecode; + int relocsize; + unsigned char *reloccode; + int tsize, bssoff; + int initaddr = -1; + + int i; + unsigned char buf[16]; + + // the magic number + buf[0] = 0x8f; + buf[1] = 0xcf; + + // run through the entire system and build the byte streams for each + // section; we will make sure we only have simple references for + // any undefined references. That means at most an ADD (or SUB) operation + // with a single BSS symbol reference and a single constant value. + // We will use the constant value in the code stream and record the + // offset in a separate code stream for the BSS relocation table. + + // We build everything in memory here because we need to calculate the + // sizes of everything before we can output the complete header. + + for (l = as -> line_head; l; l = l -> next) + { + if (l -> csect) + { + // we're in a section - need to output some bytes + if (l -> outputl > 0) + for (i = 0; i < l -> outputl; i++) + write_code_obj_sbadd(l -> csect, l -> output[i]); + else if (l -> outputl == 0 || l -> outputl == -1) + for (i = 0; i < l -> len; i++) + write_code_obj_sbadd(l -> csect, 0); + } + } + + // now run through sections and set various parameters + initsize = 0; + bsssize = 0; + mainsize = 0; + callsnum = 0; + callscode = NULL; + maincode = NULL; + initcode = NULL; + namecode = NULL; + namesize = 0; + relocsize = 0; + for (s = as -> sections; s; s = s -> next) + { + if (!strcmp(s -> name, "bss")) + { + bsssize = s -> oblen; + } + else if (!strcmp(s -> name, "main")) + { + maincode = s -> obytes; + mainsize = s -> oblen; + } + else if (!strcmp(s -> name, "init")) + { + initcode = s -> obytes; + initsize = s -> oblen; + } + else if (!strcmp(s -> name, "calls")) + { + callscode = s -> obytes; + callsnum = s -> oblen / 2; + } + else if (!strcmp(s -> name, "modname")) + { + namecode = s -> obytes; + namesize = 0; + } + for (re = s -> reloctab; re; re = re -> next) + { + if (re -> expr == NULL) + relocsize += 2; + } + } + if (namesize == 0) + { + namecode = (unsigned char *)(as -> output_file); + } + else + { + if (namecode[namesize - 1] != '\0') + { + namecode[namesize - 1] = '\0'; + } + if (!*namecode) + namecode = (unsigned char *)(as -> output_file); + } + namesize = strlen((char *)namecode); + + tsize = namesize + 1 + initsize + mainsize + callsnum * 2 + relocsize + 11; + bssoff = namesize + 1 + mainsize + callsnum * 2 + 11; + // set up section base addresses + for (s = as -> sections; s; s = s -> next) + { + if (!strcmp(s -> name, "main")) + { + s -> tbase = 11 + namesize + 1 + callsnum * 2; + } + else if (!strcmp(s -> name, "init")) + { + s -> tbase = bssoff + relocsize; + } + else if (!strcmp(s -> name, "calls")) + { + s -> tbase = 11; + } + else if (!strcmp(s -> name, "modname")) + { + s -> tbase = 11 + callsnum * 2; + } + } + + // resolve the "init" address + if (as -> execaddr_expr) + { + // need to resolve address with proper section bases + lwasm_reduce_expr(as, as -> execaddr_expr); + initaddr = lw_expr_intval(as -> execaddr_expr); + } + else + { + initaddr = as -> execaddr; + } + + // build relocation data + reloccode = NULL; + if (relocsize) + { + unsigned char *tptr; + reloccode = lw_alloc(relocsize); + tptr = reloccode; + + for (s = as -> sections; s; s = s -> next) + { + for (re = s -> reloctab; re; re = re -> next) + { + lw_expr_t te; + line_t tl; + int offset; + + tl.as = as; + as -> cl = &tl; + as -> csect = s; +// as -> exportcheck = 1; + + if (re -> expr) + { + int val; + int x; + + te = lw_expr_copy(re -> expr); + lwasm_reduce_expr(as, te); + if (!lw_expr_istype(te, lw_expr_type_int)) + { + val = 0; + } + else + { + val = lw_expr_intval(te); + } + lw_expr_destroy(te); + x = s -> tbase; + s -> tbase = 0; + te = lw_expr_copy(re -> offset); + lwasm_reduce_expr(as, te); + offset = lw_expr_intval(te); + lw_expr_destroy(te); + s -> tbase = x; + // offset *should* be the offset in the section + s -> obytes[offset] = val >> 8; + s -> obytes[offset + 1] = val & 0xff; + continue; + } + + offset = 0; + te = lw_expr_copy(re -> offset); + lwasm_reduce_expr(as, te); + if (!lw_expr_istype(te, lw_expr_type_int)) + { + lw_expr_destroy(te); + offset = 0; + continue; + } + offset = lw_expr_intval(te); + lw_expr_destroy(te); + //offset += sbase; + + *tptr++ = offset >> 8; + *tptr++ = offset & 0xff; + } + } + } + + // total size + buf[2] = tsize >> 8; + buf[3] = tsize & 0xff; + // offset to BSS relocs + buf[4] = bssoff >> 8; + buf[5] = bssoff & 0xff; + // BSS size + buf[6] = bsssize >> 8; + buf[7] = bsssize & 0xff; + // init routine offset + buf[8] = initaddr >> 8; + buf[9] = initaddr & 0xff; + // number of call entries + buf[10] = callsnum; + // write the header + writebytes(buf, 11, 1, of); + // call data + if (callsnum) + writebytes(callscode, callsnum * 2, 1, of); + // module name + writebytes(namecode, namesize + 1, 1, of); + // main code + if (mainsize) + writebytes(maincode, mainsize, 1, of); + // bss relocs + if (relocsize) + writebytes(reloccode, relocsize, 1, of); + // init stuff + if (initsize) + writebytes(initcode, initsize, 1, of); +} diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/pass5.c --- a/lwasm/pass5.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/pass5.c Fri Nov 18 21:25:43 2016 -0700 @@ -43,7 +43,7 @@ return 0; if (lw_expr_istype(e, lw_expr_type_oper)) return 0; - if (lw_expr_istype(e, lw_expr_type_special) && as -> output_format == OUTPUT_OBJ) + if (lw_expr_istype(e, lw_expr_type_special) && (as -> output_format == OUTPUT_OBJ || as -> output_format == OUTPUT_LWMOD)) { int t; t = lw_expr_specint(e); diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/pass6.c --- a/lwasm/pass6.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/pass6.c Fri Nov 18 21:25:43 2016 -0700 @@ -46,11 +46,11 @@ if (lw_expr_istype(e, lw_expr_type_int)) return 0; - if (as -> output_format == OUTPUT_OBJ) + if (as -> output_format == OUTPUT_OBJ || as -> output_format == OUTPUT_LWMOD) { if (lw_expr_istype(e, lw_expr_type_oper)) return 0; - if (lw_expr_istype(e, lw_expr_type_special) && as -> output_format == OUTPUT_OBJ) + if (lw_expr_istype(e, lw_expr_type_special) && (as -> output_format == OUTPUT_OBJ || as -> output_format == OUTPUT_LWMOD)) { int t; t = lw_expr_specint(e); diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/pseudo.c --- a/lwasm/pseudo.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/pseudo.c Fri Nov 18 21:25:43 2016 -0700 @@ -102,7 +102,7 @@ as->endseen = 1; - if ((as -> output_format != OUTPUT_DECB) && (as -> output_format != OUTPUT_BASIC)) + if ((as -> output_format != OUTPUT_DECB) && (as -> output_format != OUTPUT_BASIC) && (as -> output_format != OUTPUT_LWMOD)) { skip_operand(p); return; @@ -136,9 +136,21 @@ if (addr) { if (!lw_expr_istype(addr, lw_expr_type_int)) - lwasm_register_error(as, l, E_EXEC_ADDRESS); + { + if (as -> output_format == OUTPUT_LWMOD) + { + as -> execaddr_expr = lw_expr_copy(addr); + } + else + { + lwasm_register_error(as, l, E_EXEC_ADDRESS); + } + } else + { + as -> execaddr_expr = NULL; as -> execaddr = lw_expr_intval(addr); + } } as -> endseen = 1; } diff -r 6df8d62302e2 -r 58cafa61ab40 lwasm/section.c --- a/lwasm/section.c Wed Nov 16 19:36:16 2016 -0700 +++ b/lwasm/section.c Fri Nov 18 21:25:43 2016 -0700 @@ -35,7 +35,7 @@ char *opts = NULL; sectiontab_t *s; - if (as -> output_format != OUTPUT_OBJ) + if (as -> output_format != OUTPUT_OBJ && as -> output_format != OUTPUT_LWMOD) { lwasm_register_error(as, l, E_SECTION_TARGET); return; @@ -62,7 +62,7 @@ sn = lw_strndup(*p, p2 - *p); *p = p2; - if (**p == ',') + if (**p == ',' && as -> output_format != OUTPUT_LWMOD) { // have opts (*p)++; @@ -74,6 +74,20 @@ *p = p2; } + if (as -> output_format == OUTPUT_LWMOD) + { + for (p2 = sn; *p2; p2++) + *p2 = tolower(*p2); + + if (strcmp(sn, "bss") && strcmp(sn, "main") && strcmp(sn, "init") && strcmp(sn, "calls") && strcmp(sn, "modname")) + { + lwasm_register_error(as, l, E_SECTION_NAME); + lw_free(sn); + lw_free(opts); + return; + } + } + for (s = as -> sections; s; s = s -> next) { if (!strcmp(s -> name, sn)) @@ -90,6 +104,7 @@ s -> oblen = 0; s -> obsize = 0; s -> obytes = NULL; + s -> tbase = -1; s -> name = lw_strdup(sn); s -> offset = lw_expr_build(lw_expr_type_special, lwasm_expr_secbase, s); s -> flags = section_flag_none;