# HG changeset patch # User lost@starbug # Date 1272140118 21600 # Node ID d99322ef6f217e147e5cfb32ec95c1913df39728 # Parent 8f9d72cfb897955da79854a441c4b82539cf2214 Stage 1: actually do output diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/Makefile.am --- a/lwasm/Makefile.am Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/Makefile.am Sat Apr 24 14:15:18 2010 -0600 @@ -4,6 +4,6 @@ instab.c symbol.c macro.c pass2.c pass3.c pass4.c pass5.c pass6.c \ insn_inh.c insn_rtor.c insn_tfm.c insn_rlist.c insn_rel.c \ insn_bitbit.c insn_indexed.c insn_gen.c insn_logicmem.c \ - pseudo.c section.c os9.c pass7.c debug.c + pseudo.c section.c os9.c pass7.c debug.c output.c lwasm_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -L$(top_builddir)/lwlib -L$(top_srcdir)/lwlib -lgnu -llw EXTRA_DIST = lwasm.h input.h instab.h diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/lwasm.c --- a/lwasm/lwasm.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/lwasm.c Sat Apr 24 14:15:18 2010 -0600 @@ -189,6 +189,9 @@ void lwasm_emit(line_t *cl, int byte) { + if (cl -> outputl < 0) + cl -> outputl = 0; + if (cl -> outputl == cl -> outputbl) { cl -> output = lw_realloc(cl -> output, cl -> outputbl + 8); @@ -605,6 +608,23 @@ // handle external/cross-section/incomplete references here else { + if (l -> as -> output_format == OUTPUT_OBJ) + { + reloctab_t *re; + + // add "expression" record to section table + v = lw_expr_intval(l -> addr) + l -> outputl; + re = lw_alloc(sizeof(reloctab_t)); + re -> next = l -> csect -> reloctab; + l -> csect -> reloctab = re; + re -> offset = v; + 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, "Expression not fully resolved"); return -1; } diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/lwasm.h --- a/lwasm/lwasm.h Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/lwasm.h Sat Apr 24 14:15:18 2010 -0600 @@ -84,12 +84,25 @@ section_flag_none = 0 // no flags }; +typedef struct reloctab_s reloctab_t; +struct reloctab_s +{ + int offset; // offset of relocation + int size; // size of relocation + lw_expr_t *expr; // relocation expression + reloctab_t *next; +}; + typedef struct sectiontab_s sectiontab_t; struct sectiontab_s { char *name; // section name int flags; // section flags; lw_expr_t offset; // offset for next instance + int oblen; // size of section output + int obsize; // size of output buffer + unsigned char *obytes; // output buffer + reloctab_t *reloctab; // table of relocations sectiontab_t *next; }; @@ -113,6 +126,7 @@ struct exportlist_s { char *symbol; // symbol to export + struct symtabe *se; // symbol table entry line_t *line; // line the export is on exportlist_t *next; // next in the export list }; @@ -163,6 +177,7 @@ int context; // symbol context (-1 for global) int version; // version of the symbol (for "set") int flags; // flags for the symbol + sectiontab_t *section; // section the symbol is defined in lw_expr_t value; // symbol value struct symtabe *next; // next symbol in the table }; diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/main.c --- a/lwasm/main.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/main.c Sat Apr 24 14:15:18 2010 -0600 @@ -174,6 +174,7 @@ extern void do_pass5(asmstate_t *as); extern void do_pass6(asmstate_t *as); extern void do_pass7(asmstate_t *as); +extern void do_output(asmstate_t *as); extern lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv); extern lw_expr_t lwasm_evaluate_var(char *var, void *priv); extern lw_expr_t lwasm_parse_term(char **p, void *priv); @@ -234,4 +235,9 @@ exit(1); } } + + debug_message(&asmstate, 50, "Doing output"); + do_output(&asmstate); + + debug_message(&asmstate, 50, "Done assembly"); } diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/output.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwasm/output.c Sat Apr 24 14:15:18 2010 -0600 @@ -0,0 +1,450 @@ +/* +output.c +Copyright © 2009, 2010 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 . + + +Contains the code for actually outputting the assembled code +*/ +#include +#include +#include +#include +#include + +#include +#include + +#include "lwasm.h" + +void write_code_raw(asmstate_t *as, FILE *of); +void write_code_decb(asmstate_t *as, FILE *of); +void write_code_rawrel(asmstate_t *as, FILE *of); +void write_code_obj(asmstate_t *as, FILE *of); +void write_code_os9(asmstate_t *as, FILE *of); + +// this prevents warnings about not using the return value of fwrite() +#define writebytes(s, l, c, f) do { int r; r = fwrite((s), (l), (c), (f)); } while (0) + +void do_output(asmstate_t *as) +{ + FILE *of; + + if (as -> errorcount > 0) + { + fprintf(stderr, "Not doing output due to assembly errors.\n"); + return; + } + + of = fopen(as -> output_file, "wb"); + if (!of) + { + fprintf(stderr, "Cannot open '%s' for output", as -> output_file); + perror(""); + return; + } + + switch (as -> output_format) + { + case OUTPUT_RAW: + write_code_raw(as, of); + break; + + case OUTPUT_DECB: + write_code_decb(as, of); + break; + + case OUTPUT_RAWREL: + write_code_rawrel(as, of); + break; + + case OUTPUT_OBJ: + write_code_obj(as, of); + break; + + case OUTPUT_OS9: + write_code_os9(as, of); + break; + + default: + fprintf(stderr, "BUG: unrecognized output format when generating output file\n"); + fclose(of); + unlink(as -> output_file); + return; + } + + fclose(of); +} + +/* +rawrel output treats an ORG directive as an offset from the start of the +file. Undefined results will occur if an ORG directive moves the output +pointer backward. This particular implementation uses "fseek" to handle +ORG requests and to skip over RMBs. + +This simple brain damanged method simply does an fseek before outputting +each instruction. +*/ +void write_code_rawrel(asmstate_t *as, FILE *of) +{ + line_t *cl; + + for (cl = as -> line_head; cl; cl = cl -> next) + { + if (cl -> outputl <= 0) + continue; + + fseek(of, lw_expr_intval(cl -> addr), SEEK_SET); + writebytes(cl -> output, cl -> outputl, 1, of); + } +} + +/* +raw merely writes all the bytes directly to the file as is. ORG is just a +reference for the assembler to handle absolute references. Multiple ORG +statements will produce mostly useless results +*/ +void write_code_raw(asmstate_t *as, FILE *of) +{ + line_t *cl; + + for (cl = as -> line_head; cl; cl = cl -> next) + { + if (cl -> len > 0 && cl -> outputl == 0) + { + int i; + for (i = 0; i < cl -> len; i++) + writebytes("\0", 1, 1, of); + continue; + } + else if (cl -> outputl > 0) + writebytes(cl -> output, cl -> outputl, 1, of); + } +} + + +/* +OS9 target also just writes all the bytes in order. No need for anything +else. +*/ +void write_code_os9(asmstate_t *as, FILE *of) +{ + line_t *cl; + + for (cl = as -> line_head; cl; cl = cl -> next) + { + if (cl -> inmod == 0) + continue; + if (cl -> len > 0 && cl -> outputl == 0) + { + int i; + for (i = 0; i < cl -> len; i++) + writebytes("\0", 1, 1, of); + continue; + } + else if (cl -> outputl > 0) + writebytes(cl -> output, cl -> outputl, 1, of); + } +} + +void write_code_decb(asmstate_t *as, FILE *of) +{ + long preambloc; + line_t *cl; + int blocklen = -1; + int nextcalc = -1; + unsigned char outbuf[5]; + int caddr; + + for (cl = as -> line_head; cl; cl = cl -> next) + { + if (cl -> outputl < 0) + continue; + caddr = lw_expr_intval(cl -> addr); + if (caddr != nextcalc && cl -> outputl > 0) + { + // need preamble here + if (blocklen > 0) + { + // update previous preamble if needed + fseek(of, preambloc, SEEK_SET); + outbuf[0] = (blocklen >> 8) & 0xFF; + outbuf[1] = blocklen & 0xFF; + writebytes(outbuf, 2, 1, of); + fseek(of, 0, SEEK_END); + } + blocklen = 0; + nextcalc = caddr; + outbuf[0] = 0x00; + outbuf[1] = 0x00; + outbuf[2] = 0x00; + outbuf[3] = (nextcalc >> 8) & 0xFF; + outbuf[4] = nextcalc & 0xFF; + preambloc = ftell(of) + 1; + writebytes(outbuf, 5, 1, of); + } + nextcalc += cl -> outputl; + writebytes(cl -> output, cl -> outputl, 1, of); + blocklen += cl -> outputl; + } + if (blocklen > 0) + { + fseek(of, preambloc, SEEK_SET); + outbuf[0] = (blocklen >> 8) & 0xFF; + outbuf[1] = blocklen & 0xFF; + writebytes(outbuf, 2, 1, of); + fseek(of, 0, SEEK_END); + } + + // now write postamble + outbuf[0] = 0xFF; + outbuf[1] = 0x00; + outbuf[2] = 0x00; + outbuf[3] = (as -> execaddr >> 8) & 0xFF; + outbuf[4] = (as -> execaddr) & 0xFF; + writebytes(outbuf, 5, 1, of); +} + +void write_code_obj_sbadd(sectiontab_t *s, unsigned char b) +{ + if (s -> oblen >= s -> obsize) + { + s -> obytes = lw_realloc(s -> obytes, s -> obsize + 128); + s -> obsize += 128; + } + s -> obytes[s -> oblen] = b; + s -> oblen += 1; +} + +void write_code_obj(asmstate_t *as, FILE *of) +{ + line_t *l; + sectiontab_t *s; + reloctab_t *re; + struct symtabe *se; + + int i; + unsigned char buf[16]; + + // output the magic number and file header + // the 8 is NOT an error + writebytes("LWOBJ16", 8, 1, of); + + // run through the entire system and build the byte streams for each + // section; at the same time, generate a list of "local" symbols to + // output for each section + // NOTE: for "local" symbols, we will append \x01 and the ascii string + // of the context identifier (so sym in context 1 would be "sym\x011" + // we can do this because the linker can handle symbols with any + // character other than NUL. + // also we will generate a list of incomplete references for each + // section along with the actual definition that will be output + + // once all this information is generated, we will output each section + // to the file + + // NOTE: we build everything in memory then output it because the + // assembler accepts multiple instances of the same section but the + // linker expects only one instance of each section in the object file + // so we need to collect all the various pieces of a section together + // (also, the assembler treated multiple instances of the same section + // as continuations of previous sections so we would need to collect + // them together anyway. + + 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) + for (i = 0; i < l -> len; i++) + write_code_obj_sbadd(l -> csect, 0); + } + } + + // run through the sections + for (s = as -> sections; s; s = s -> next) + { + // write the name + writebytes(s -> name, strlen(s -> name) + 1, 1, of); + + // write the flags + if (s -> flags & section_flag_bss) + writebytes("\x01", 1, 1, of); + + // indicate end of flags - the "" is NOT an error + writebytes("", 1, 1, of); + + // now the local symbols + for (se = as -> symtab.head; se; se = se -> next) + { + // ignore symbols not in this section + if (se -> section != s) + continue; + + if (se -> flags & symbol_flag_set) + continue; + + // don't output non-constant symbols + if (!lw_expr_istype(se -> value, lw_expr_type_int)) + continue; + + writebytes(se -> symbol, strlen(se -> symbol), 1, of); + if (se -> context >= 0) + { + writebytes("\x01", 1, 1, of); + sprintf(buf, "%d", se -> context); + writebytes(buf, strlen(buf), 1, of); + } + // the "" is NOT an error + writebytes("", 1, 1, of); + + // write the address + buf[0] = (lw_expr_intval(se -> value) >> 8) & 0xff; + buf[1] = lw_expr_intval(se -> value) & 0xff; + writebytes(buf, 2, 1, of); + } + // flag end of local symbol table - "" is NOT an error + writebytes("", 1, 1, of); + + // now the exports -- FIXME +/* for (ex = as -> exportlist; ex; ex = ex -> next) + { + int eval; + ex -> se -> section != s) + continue; + if (!lwasm_expr_exportable(ex -> se -> value)) + continue; + eval = lwasm_expr_exportval(ex -> se -> value); + writebytes(ex -> symbol, strlen(ex -> symbol) + 1, 1, of); + buf[0] = (eval >> 8) & 0xff; + buf[1] = eval & 0xff; + writebytes(buf, 2, 1, of); + } +*/ + // flag end of exported symbols - "" is NOT an error + writebytes("", 1, 1, of); + + // FIXME - relocation table +/* for (re = s -> rl; re; re = re -> next) + { + if (re -> expr == NULL) + { + // this is an error but we'll simply ignore it + // and not output this expression + continue; + } + + // work through each term in the expression and output + // the proper equivalent to the object file + if (re -> relocsize == 1) + { + // flag an 8 bit relocation (low 8 bits will be used) + buf[0] = 0xFF; + buf[1] = 0x01; + writebytes(buf, 2, 1, of); + } + for (sn = re -> expr -> head; sn; sn = sn -> next) + { + switch (sn -> term -> term_type) + { + case LWASM_TERM_OPER: + buf[0] = 0x04; + buf[1] = sn -> term -> value; + writebytes(buf, 2, 1, of); + break; + + case LWASM_TERM_INT: + buf[0] = 0x01; + buf[1] = (sn -> term -> value >> 8) & 0xff; + buf[2] = sn -> term -> value & 0xff; + writebytes(buf, 3, 1, of); + break; + + case LWASM_TERM_SECBASE: + writebytes("\x05", 1, 1, of); + break; + + case LWASM_TERM_SYM: + // now for the ugly part - resolve a symbol reference + // and determine whether it's internal, external, or + // a section base + se = lwasm_find_symbol(as, sn -> term -> symbol, re -> context); + if (!se) + se = lwasm_find_symbol(as, sn -> term -> symbol, -1); + if (!se || se -> flags & SYMBOL_EXTERN) + { + // not found - assume external reference + // found but flagged external - handle it + writebytes("\x02", 1, 1, of); + writebytes(se -> sym, strlen(se -> sym) + 1, 1, of); + break; + } + // a local symbol reference here + writebytes("\x03", 1, 1, of); + writebytes(se -> sym, strlen(se -> sym), 1, of); + if (se -> context >= 0) + { + writebytes("\x01", 1, 1, of); + sprintf(buf, "%d", se -> context); + writebytes(buf, strlen(buf), 1, of); + } + writebytes("", 1, 1, of); + break; + + default: + // unrecognized term type - replace with integer 0 + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x00; + writebytes(buf, 3, 1, of); + break; + } + } + + // flag end of expressions + writebytes("", 1, 1, of); + + // write the offset + buf[0] = (re -> offset >> 8) & 0xff; + buf[1] = re -> offset & 0xff; + writebytes(buf, 2, 1, of); + } +*/ + // flag end of incomplete references list + writebytes("", 1, 1, of); + + // now blast out the code + + // length + buf[0] = s -> oblen >> 8 & 0xff; + buf[1] = s -> oblen & 0xff; + writebytes(buf, 2, 1, of); + + if (!(s -> flags & section_flag_bss)) + { + writebytes(s -> obytes, s -> oblen, 1, of); + } + } + + // flag no more sections + // the "" is NOT an error + writebytes("", 1, 1, of); +} diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/pass1.c --- a/lwasm/pass1.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/pass1.c Sat Apr 24 14:15:18 2010 -0600 @@ -80,6 +80,7 @@ cl = lw_alloc(sizeof(line_t)); memset(cl, 0, sizeof(line_t)); + cl -> outputl = -1; cl -> prev = as -> line_tail; cl -> insn = -1; cl -> as = as; diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/pass2.c --- a/lwasm/pass2.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/pass2.c Sat Apr 24 14:15:18 2010 -0600 @@ -72,6 +72,7 @@ lwasm_register_error(as, ex -> line, "Undefined exported symbol"); } } + ex -> se = s; } if (as -> errorcount > 0) return; diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/section.c --- a/lwasm/section.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/section.c Sat Apr 24 14:15:18 2010 -0600 @@ -203,6 +203,7 @@ e -> next = as -> exportlist; e -> symbol = lw_strdup(sym); e -> line = l; + e -> se = NULL; as -> exportlist = e; lw_free(sym); diff -r 8f9d72cfb897 -r d99322ef6f21 lwasm/symbol.c --- a/lwasm/symbol.c Thu Apr 22 18:30:30 2010 -0600 +++ b/lwasm/symbol.c Sat Apr 24 14:15:18 2010 -0600 @@ -108,6 +108,7 @@ se -> flags = flags; se -> value = lw_expr_copy(val); se -> symbol = lw_strdup(sym); + se -> section = cl -> csect; return se; }