changeset 345:7416c3f9c321

Basic macro processor ported forward; added context break handling for local symbols
author lost@starbug
date Thu, 25 Mar 2010 23:17:54 -0600
parents 0215a0fbf61b
children a82c55070624
files lwasm/Makefile.am lwasm/instab.c lwasm/lwasm.c lwasm/lwasm.h lwasm/macro.c lwasm/main.c lwasm/pass1.c
diffstat 7 files changed, 398 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/lwasm/Makefile.am	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/Makefile.am	Thu Mar 25 23:17:54 2010 -0600
@@ -1,5 +1,5 @@
 AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib -I$(top_builddir)/lwlib -I$(top_srcdir)/lwlib
 bin_PROGRAMS = lwasm
-lwasm_SOURCES = main.c pragma.c input.c pass1.c lwasm.c instab.c symbol.c
+lwasm_SOURCES = main.c pragma.c input.c pass1.c lwasm.c instab.c symbol.c macro.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/instab.c	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/instab.c	Thu Mar 25 23:17:54 2010 -0600
@@ -26,7 +26,10 @@
 #define __instab_c_seen__
 #include "instab.h"
 
-extern PARSEFUNC(insn_parse_inh);
+// don't need to parse anything for inh
+// so it can just be NULL
+#define insn_parse_inh	NULL
+
 extern RESOLVEFUNC(insn_resolve_inh);
 extern PARSEFUNC(insn_parse_gen8);
 extern RESOLVEFUNC(insn_resolve_gen8);
--- a/lwasm/lwasm.c	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/lwasm.c	Thu Mar 25 23:17:54 2010 -0600
@@ -77,3 +77,11 @@
 	
 	va_end(args);
 }
+
+int lwasm_next_context(asmstate_t *as)
+{
+	int r;
+	r = as -> nextcontext;
+	as -> nextcontext++;
+	return r;
+}
--- a/lwasm/lwasm.h	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/lwasm.h	Thu Mar 25 23:17:54 2010 -0600
@@ -104,6 +104,15 @@
 	struct symtabe *head;				// start of symbol table
 } symtab_t;
 
+typedef struct macrotab_s macrotab_t;
+struct macrotab_s
+{
+	char *name;							// name of macro
+	char **lines;						// macro lines
+	int numlines;						// number lines in macro
+	macrotab_t *next;					// next macro in list
+};
+
 typedef struct
 {
 	int output_format;					// output format
@@ -112,13 +121,18 @@
 	int flags;							// assembly flags
 	int pragmas;						// pragmas currently in effect
 	int errorcount;						// number of errors encountered
+	int inmacro;						// are we in a macro?
+	int skipcond;						// skipping a condition?
+	int skipmacro;						// are we skipping in a macro?	
 
 	line_t *line_head;					// start of lines list
 	line_t *line_tail;					// tail of lines list
 	
 	int context;						// the current "context"
-
+	int nextcontext;					// the next available context
+	
 	symtab_t symtab;					// meta data for the symbol table
+	macrotab_t *macros;					// macro table
 
 	char *list_file;					// name of file to list to
 	char *output_file;					// output file name	
@@ -138,6 +152,7 @@
 #ifndef ___lwasm_c_seen___
 
 extern void lwasm_register_error(asmstate_t *as, line_t *cl, const char *msg, ...);
+extern int lwasm_next_context(asmstate_t *as);
 
 #endif
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/macro.c	Thu Mar 25 23:17:54 2010 -0600
@@ -0,0 +1,290 @@
+/*
+macro.c
+Copyright © 2008 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 stuff associated with macro processing
+*/
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "input.h"
+#include "instab.h"
+
+PARSEFUNC(pseudo_macro_parse)
+{
+	macrotab_t *m;
+	
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 1;
+		return;
+	}
+	
+	if (as -> inmacro)
+	{
+		lwasm_register_error(as, l, "Attempt to define a macro inside a macro");
+		return;
+	}
+	
+	if (!(l -> sym))
+	{
+		lwasm_register_error(as, l, "Missing macro name");
+		return;
+	}
+
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(m -> name, l -> sym))
+			break;
+	}
+	if (m)
+	{
+		lwasm_register_error(as, l, "Duplicate macro definition");
+		return;
+	}
+	
+	m = lw_alloc(sizeof(macrotab_t));
+	m -> name = lw_strdup(l -> sym);
+	m -> next = as -> macros;
+	m -> lines = NULL;
+	m -> numlines = 0;
+	as -> macros = m;
+	
+	while (**p && !isspace(**p))
+		(*p)++;
+	
+	as -> inmacro = 1;
+}
+
+PARSEFUNC(pseudo_endm_parse)
+{
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 0;
+		return;
+	}
+	
+	if (!as -> inmacro)
+	{
+		lwasm_register_error(as, l, "ENDM without MACRO");
+		return;
+	}
+	
+	as -> inmacro = 0;
+	
+	// a macro definition counts as a context break for local symbols
+	as -> context = lwasm_next_context(as);
+}
+
+// the current macro will ALWAYS be the first one in the table
+int add_macro_line(asmstate_t *as, char *optr)
+{
+	if (!as -> inmacro)
+		return 0;
+	
+	as -> macros -> lines = lw_realloc(as -> macros -> lines, sizeof(char *) * (as -> macros -> numlines + 1));
+	as -> macros -> lines[as -> macros -> numlines] = lw_strdup(optr);
+	as -> macros -> numlines += 1;
+	return 1;
+}
+
+void macro_add_to_buff(char **buff, int *loc, int *len, char c)
+{
+	if (*loc == *len)
+	{
+		*buff = lw_realloc(*buff, *len + 32);
+		*len += 32;
+	}
+	(*buff)[(*loc)++] = c;
+}
+
+// this is just like a regular operation function
+/*
+macro args are referenced by "\n" where 1 <= n <= 9
+or by "\{n}"; a \ can be included by writing \\
+a comma separates argument but one can be included with "\,"
+whitespace ends argument list but can be included with "\ " or the like
+
+*/
+int expand_macro(asmstate_t *as, line_t *l, char **p, char *opc)
+{
+	int lc;
+	line_t *cl, *nl;
+	int oldcontext;
+	macrotab_t *m;
+
+	char **args = NULL;		// macro arguments
+	int nargs = 0;			// number of arguments
+
+	char *p2, *p3;
+	
+	int bloc, blen;
+	char *linebuff;
+
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(opc, m -> name))
+			break;
+	}
+	// signal no macro expansion
+	if (!m)
+		return -1;
+	
+	// save current symbol context for after macro expansion
+	oldcontext = as -> context;
+
+	cl = l;
+
+	as -> context = lwasm_next_context(as);
+
+	while (**p && !isspace(**p) && **p != ',')
+	{
+		p2 = *p;
+		while (*p2 && !isspace(*p2) && *p2 != ',')
+		{
+			if (*p2 == '\\')
+			{
+				if (p2[1])
+					p2++;
+			}
+			p2++;
+		}
+
+		// have arg here
+		args = lw_realloc(args, sizeof(char *) * (nargs + 1));
+		args[nargs] = lw_alloc(p2 - *p + 1);
+		args[nargs][p2 - *p] = '\0';
+		memcpy(args[nargs], *p, p2 - *p);
+		*p = p2;
+
+		// now collapse out "\" characters
+		for (p3 = p2 = args[nargs]; *p2; p2++, p3++)
+		{
+			if (*p2 == '\\' && p2[1])
+			{
+				p2++;
+			}
+			*p3 = *p2;
+		}
+		*p3 = '\0';
+		
+		nargs++;
+		if (**p == ',')
+			(*p)++;
+	}
+	
+
+	// now create a string for the macro
+	// and push it into the front of the input stack
+	bloc = blen = 0;
+	linebuff = NULL;
+	
+	for (lc = 0; lc < m -> numlines; lc++)
+	{
+		for (p2 = m -> lines[lc]; *p2; p2++)
+		{
+			if (*p2 == '\\' && isdigit(p2[1]))
+			{
+				int n;
+					
+				p2++;
+				n = *p2 - '0';
+				if (n == 0)
+				{
+					for (p3 = m -> name; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				if (n < 1 || n > nargs)
+					continue;
+				for (p3 = args[n - 1]; *p3; p3++)
+					macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+				continue;
+			}
+			else if (*p2 == '{')
+			{
+				int n = 0, n2;
+				p2++;
+				while (*p2 && isdigit(*p2))
+				{
+					n2 = *p2 - '0';
+					if (n2 < 0 || n2 > 9)
+						n2 = 0;
+					n = n * 10 + n2;
+					p2++;
+				}
+				if (*p2 == '}')
+					p2++;
+				 
+				if (n == 0)
+				{
+					for (p3 = m -> name; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				if (n < 1 || n > nargs)
+					continue;
+				for (p3 = args[n - 1]; *p3; p3++)
+					macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+				continue;
+			}
+			else
+			{
+				macro_add_to_buff(&linebuff, &bloc, &blen, *p2);
+			}
+		}
+
+		macro_add_to_buff(&linebuff, &bloc, &blen, '\n');
+		
+	}
+
+	{
+		char ctcbuf[100];
+		char *p;
+		snprintf(ctcbuf, 100, "\001\001SETCONTEXT %d\n", oldcontext);
+		for (p = ctcbuf; *p; p++)
+			macro_add_to_buff(&linebuff, &bloc, &blen, *p);
+	}
+	
+	// push the macro into the front of the stream
+	input_openstring(as, opc, linebuff);
+
+	lw_free(linebuff);
+
+	// clean up
+	if (args)
+	{
+		while (nargs)
+		{
+			lw_free(args[--nargs]);
+		}
+		lw_free(args);
+	}
+
+	// indicate a macro was expanded
+	return 0;	
+}
--- a/lwasm/main.c	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/main.c	Thu Mar 25 23:17:54 2010 -0600
@@ -180,6 +180,7 @@
 	/* initialize assembler state */
 	asmstate.include_list = lw_stringlist_create();
 	asmstate.input_files = lw_stringlist_create();
+	asmstate.nextcontext = 1;
 
 	/* parse command line arguments */	
 	argp_parse(&argp, argc, argv, 0, 0, &asmstate);
--- a/lwasm/pass1.c	Thu Mar 25 22:06:50 2010 -0600
+++ b/lwasm/pass1.c	Thu Mar 25 23:17:54 2010 -0600
@@ -59,6 +59,22 @@
 		line = input_readline(as);
 		if (!line)
 			break;
+		if (line[0] == 1 && line[1] == 1)
+		{
+			// special internal directive
+			// these DO NOT appear in the output anywhere
+			// they are generated by the parser to pass information
+			// forward
+			for (p1 = line + 2; *p1 && !isspace(*p1); p1++)
+				/* do nothing */ ;
+			*p1++ = '\0';
+			if (!strcmp(line + 2, "SETCONTEXT"))
+			{
+				as -> context = strtol(p1, NULL, 10);
+			}
+			lw_free(line);
+			continue;
+		}
 		printf("%s\n", line);
 		
 		cl = lw_alloc(sizeof(line_t));
@@ -84,12 +100,15 @@
 		as -> line_tail = cl;
 
 		// blank lines don't count for anything
+		// except a local symbol context break
 		if (!*line)
 		{
+			as -> context = lwasm_next_context(as);
 			goto nextline;
 		}
 	
 		// skip comments
+		// commends do not create a context break
 		if (*line == '*' || *line == ';' || *line == '#')
 			goto nextline;
 
@@ -105,9 +124,14 @@
 				p1++;
 		}
 
+		// blank line - context break
 		if (!*p1)
+		{
+			as -> context = lwasm_next_context(as);
 			goto nextline;
+		}
 
+		// comment - no context break
 		if (*p1 == '*' || *p1 == ';' || *p1 == '#')
 			goto nextline;
 
@@ -120,8 +144,14 @@
 		else
 			stspace = 0;
 
-		if (*p1 == '*' || *p1 == ';' || *p1 == '#' || !*p1)
+		if (*p1 == '*' || *p1 == ';' || *p1 == '#')
 			goto nextline;
+		if (!*p1)
+		{
+			// nothing but whitespace - context break
+			as -> context = lwasm_next_context(as);
+			goto nextline;
+		}
 
 		// find the end of the first token
 		for (tok = p1; *p1 && !isspace(*p1) && *p1 != ':'; p1++)
@@ -156,50 +186,77 @@
 				if (!strcasecmp(instab[opnum].opcode, sym))
 					break;
 			}
-			lw_free(sym);
 			
 			// p1 points to the start of the operand
 			
+			// if we're inside a macro definition and not at ENDM,
+			// add the line to the macro definition and continue
+			if (as -> inmacro && !(instab[opnum].flags & lwasm_insn_endm))
+			{
+				add_macro_line(as, line);
+				goto linedone;
+			}
+			
+			// if skipping a condition and the operation code doesn't
+			// operate within a condition (not a conditional)
+			// do nothing
+			if (as -> skipcond && !(instab[opnum].flags & lwasm_insn_cond))
+				goto linedone;
+        			
 			if (instab[opnum].opcode == NULL)
 			{
 				cl -> insn = -1;
 				if (*tok != ';' && *tok != '*')
 				{
 					// bad opcode; check for macro here
-					lwasm_register_error(as, cl, "Bad opcode");
+					if (expand_macro(as, cl, &p1, sym) != 0)
+					{
+						// macro expansion failed
+						lwasm_register_error(as, cl, "Bad opcode");
+					}
 				}
 			}
 			else
 			{
 				cl -> insn = opnum;
-				// call parse function
-				
-				if (*p1 && !isspace(*p1))
+				// no parse func means operand doesn't matter
+				if (instab[opnum].parse)
 				{
-					// flag bad operand error
-					lwasm_register_error(as, cl, "Bad operand (%s)", p1);
+					// call parse function
+					(instab[opnum].parse)(as, cl, &p1);
+					
+					if (*p1 && !isspace(*p1))
+					{
+						// flag bad operand error
+						lwasm_register_error(as, cl, "Bad operand (%s)", p1);
+					}
 				}
 			}
 		}
+	
+	linedone:
+		lw_free(sym);
 		
-		if (cl -> sym && cl -> symset == 0)
+		if (!as -> skipcond && !as -> inmacro)
 		{
-			printf("Register symbol %s:", sym);
+			if (cl -> sym && cl -> symset == 0)
+			{
+				printf("Register symbol %s:", sym);
+				lw_expr_print(cl -> addr);
+				printf("\n");
+	
+				// register symbol at line address
+				if (!register_symbol(as, cl, cl -> sym, cl -> addr, symbol_flag_none))
+				{
+					// symbol error
+					lwasm_register_error(as, cl, "Bad symbol '%s'", cl -> sym);
+				}
+			}
+		
 			lw_expr_print(cl -> addr);
 			printf("\n");
-
-			// register symbol at line address
-			if (!register_symbol(as, cl, cl -> sym, cl -> addr, symbol_flag_none))
-			{
-				// symbol error
-				lwasm_register_error(as, cl, "Bad symbol '%s'", cl -> sym);
-			}
 		}
 		
-		lw_expr_print(cl -> addr);
-		printf("\n");
-		// now parse the line
-		
 	nextline:
 		lw_free(line);
 	}