changeset 374:d99322ef6f21

Stage 1: actually do output
author lost@starbug
date Sat, 24 Apr 2010 14:15:18 -0600
parents 8f9d72cfb897
children 3498b2d88376
files lwasm/Makefile.am lwasm/lwasm.c lwasm/lwasm.h lwasm/main.c lwasm/output.c lwasm/pass1.c lwasm/pass2.c lwasm/section.c lwasm/symbol.c
diffstat 9 files changed, 496 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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;
 	}
--- 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
 };
--- 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");
 }
--- /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);
+}
--- 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;
--- 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;
--- 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);
 	
--- 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;
 }