Mercurial > hg-old > index.cgi
diff lwasm/output.c @ 374:d99322ef6f21
Stage 1: actually do output
author | lost@starbug |
---|---|
date | Sat, 24 Apr 2010 14:15:18 -0600 |
parents | old-trunk/lwasm/old/output.c@eb230fa7d28e |
children | 3498b2d88376 |
line wrap: on
line diff
--- /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 <http://www.gnu.org/licenses/>. + + +Contains the code for actually outputting the assembled code +*/ +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <lw_alloc.h> +#include <lw_expr.h> + +#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); +}