changeset 432:58cafa61ab40

Add support for undocumented custom module format (for LW) Nothing to see here. Move along. These are not the droids you are looking for.
author William Astle <lost@l-w.ca>
date Fri, 18 Nov 2016 21:25:43 -0700
parents 6df8d62302e2
children b1adf549d181
files lwasm/lwasm.c lwasm/lwasm.h lwasm/main.c lwasm/output.c lwasm/pass5.c lwasm/pass6.c lwasm/pseudo.c lwasm/section.c
diffstat 8 files changed, 405 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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
--- 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);
--- 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);
+}
--- 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);
--- 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);
--- 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;
 }
--- 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;