Mercurial > hg-old > index.cgi
diff old-trunk/lwasm/old/output.c @ 339:eb230fa7d28e
Prepare for migration to hg
author | lost |
---|---|
date | Fri, 19 Mar 2010 02:54:14 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old-trunk/lwasm/old/output.c Fri Mar 19 02:54:14 2010 +0000 @@ -0,0 +1,458 @@ +/* +output.c +Copyright © 2009 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 <ctype.h> +#include <errno.h> +#include <stdio.h> +//#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#define __output_c_seen__ +//#include "instab.h" +#include "lwasm.h" +#include "util.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 lwasm_output(asmstate_t *as) +{ + FILE *of; + + if (as -> errorcount > 0) + { + fprintf(stderr, "Not doing output due to assembly errors.\n"); + return; + } + + of = fopen(as -> outfile, "wb"); + if (!of) + { + fprintf(stderr, "Cannot open '%s' for output", as -> outfile); + perror(""); + return; + } + + switch (as -> outformat) + { + 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 -> outfile); + 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) +{ + lwasm_line_t *cl; + + for (cl = as -> lineshead; cl; cl = cl -> next) + { + if (cl -> codelen == 0) + continue; + + fseek(of, cl -> codeaddr, SEEK_SET); + writebytes(cl -> bytes, cl -> codelen, 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) +{ + lwasm_line_t *cl; + + for (cl = as -> lineshead; cl; cl = cl -> next) + { + if (cl -> nocodelen) + { + int i; + for (i = 0; i < cl -> nocodelen; i++) + writebytes("\0", 1, 1, of); + continue; + } + writebytes(cl -> bytes, cl -> codelen, 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) +{ + lwasm_line_t *cl; + + for (cl = as -> lineshead; cl; cl = cl -> next) + { + if (cl -> inmod == 0) + continue; + if (cl -> nocodelen) + { + int i; + for (i = 0; i < cl -> nocodelen; i++) + writebytes("\0", 1, 1, of); + continue; + } + writebytes(cl -> bytes, cl -> codelen, 1, of); + } +} + +void write_code_decb(asmstate_t *as, FILE *of) +{ + long preambloc; + lwasm_line_t *cl; + int blocklen = -1; + int nextcalc = -1; + unsigned char outbuf[5]; + + for (cl = as -> lineshead; cl; cl = cl -> next) + { + if (cl -> nocodelen) + continue; + if (cl -> codeaddr != nextcalc && cl -> codelen > 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 = cl -> codeaddr; + 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 -> codelen; + writebytes(cl -> bytes, cl -> codelen, 1, of); + blocklen += cl -> codelen; + } + 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 = lwasm_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) +{ + lwasm_line_t *l; + sectiontab_t *s; + lwasm_symbol_ent_t *se; + export_list_t *ex; + section_reloc_list_t *re; + lwasm_expr_stack_node_t *sn; + + 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 -> lineshead; l; l = l -> next) + { + if (l -> sect) + { + // we're in a section - need to output some bytes + for (i = 0; i < l -> codelen; i++) + write_code_obj_sbadd(l -> sect, l -> bytes[i]); + for (i = 0; i < l -> nocodelen; i++) + write_code_obj_sbadd(l -> sect, 0); + + // do we have a "relocation"? If so, add a reference to the + // relocation table + if (l -> relocoff >= 0) + { + // build the relocation reference for the linker + re = lwasm_alloc(sizeof(section_reloc_list_t)); + re -> next = l -> sect -> rl; + l -> sect -> rl = re; + + re -> offset = l -> codeaddr + l -> relocoff; + re -> expr = l -> exprs[0]; + re -> context = l -> context; + re -> relocsize = 2; + if (l -> reloc8bit) + re -> relocsize = 1; + } + } + } + + // 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_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 -> symhead; se; se = se -> next) + { + // ignore symbols not in this section + if (se -> sect != s) + continue; + + if (se -> flags & SYMBOL_SET) + continue; + + if (se -> flags & SYMBOL_EXTERN) + continue; + + 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); + } + // the "" is NOT an error + writebytes("", 1, 1, of); + + // write the address + buf[0] = (se -> value >> 8) & 0xff; + buf[1] = 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 + for (ex = s -> exports; ex; ex = ex -> next) + { + writebytes(ex -> sym, strlen(ex -> sym) + 1, 1, of); + buf[0] = (ex -> offset >> 8) & 0xff; + buf[1] = ex -> offset & 0xff; + writebytes(buf, 2, 1, of); + } + + // flag end of exported symbols - "" is NOT an error + writebytes("", 1, 1, of); + + // now output the "incomplete references" + // this being the most complex bit + 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_BSS)) + { + writebytes(s -> obytes, s -> oblen, 1, of); + } + } + + // flag no more sections + // the "" is NOT an error + writebytes("", 1, 1, of); +}