diff old-trunk/lwasm/old/pseudo.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/pseudo.c	Fri Mar 19 02:54:14 2010 +0000
@@ -0,0 +1,1336 @@
+/*
+pseudo.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/>.
+
+
+This file implements the various pseudo operations.
+*/
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lwasm.h"
+#include "instab.h"
+#include "expr.h"
+#include "util.h"
+
+extern int lwasm_read_file(asmstate_t *as, const char *filename);
+
+OPFUNC(pseudo_noop)
+{
+	skip_operand(p);
+}
+
+OPFUNC(pseudo_org)
+{
+	int v, r;
+
+	if (as -> csect)
+	{
+		register_error(as, l, 1, "ORG not allowed within sections");
+		return;
+	}
+	if (as -> inmod)
+	{
+		register_error(as, l, 1, "ORG not allowed within modules");
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		// org is not needed to be processed on pass 2
+		// this will prevent phasing errors for forward references that
+		// resolve on the second pass
+		// we saved the org address in l -> codeaddr on pass 1
+		as -> addr = l -> codeaddr;
+		skip_operand(p);
+		return;
+	}
+	
+	if (l -> sym)
+	{
+		register_error(as, l, 1, "No symbol allowed with ORG");
+	}
+	
+	r = lwasm_expr_result2(as, l, p, EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	l -> codeaddr = v;
+	l -> addrset = 1;
+	as -> addr = v;
+}
+
+/*
+The operand for include is a string optionally enclosed in "
+*/
+OPFUNC(pseudo_include)
+{
+	int v1;
+	char *fn;
+	
+	// only include files on pass 1
+	// but make sure local include context is right
+	// for the next line...
+	if (as -> passnum != 1)
+	{
+		as -> context = lwasm_next_context(as);
+		skip_operand(p);
+		return;
+	}
+
+	while (**p && isspace(**p))
+		(*p)++;
+
+	if (!**p)
+	{
+		register_error(as, l, 1, "Bad file name");
+		return;
+	}
+
+	if (**p == '"')
+	{
+		// search for ending "
+		(*p)++;
+		for (v1 = 0; *((*p)+v1) && *((*p)+v1) != '"'; v1++)
+			/* do nothing */ ;
+		if (*((*p)+v1) != '"')
+		{
+			register_error(as, l, 1, "Bad file name");
+			return;
+		}
+	}
+	else
+	{
+		// search for a space type character
+		for (v1 = 0; *((*p)+v1) && !isspace(*((*p)+v1)); v1++)
+			;
+	}
+
+	fn = lwasm_alloc(v1 + 1);
+	memcpy(fn, *p, v1);
+	fn[v1] = '\0';
+
+	(*p) += v1;
+	if (**p == '"')
+		(*p)++;
+
+	// end local label context on include	
+	as -> context = lwasm_next_context(as);
+	if (lwasm_read_file(as, fn) < 0)
+	{
+		register_error(as, l, 1, "File include error (%s)", fn);
+	}
+	lwasm_free(fn);
+}
+
+/*
+The operand for includebin is a string optionally enclosed in "
+*/
+OPFUNC(pseudo_includebin)
+{
+	int v1;
+	char *fn;
+	FILE *f;
+	
+	// only include files on pass 1
+	while (**p && isspace(**p))
+		(*p)++;
+
+	if (!**p)
+	{
+		register_error(as, l, 1, "Bad file name");
+		return;
+	}
+
+	if (**p == '"')
+	{
+		// search for ending "
+		(*p)++;
+		for (v1 = 0; *((*p)+v1) && *((*p)+v1) != '"'; v1++)
+			/* do nothing */ ;
+		if (*((*p)+v1) != '"')
+		{
+			register_error(as, l, 1, "Bad file name");
+			return;
+		}
+	}
+	else
+	{
+		// search for a space type character
+		for (v1 = 0; *((*p)+v1) && !isspace(*((*p)+v1)); v1++)
+			;
+	}
+
+	fn = lwasm_alloc(v1 + 1);
+	memcpy(fn, *p, v1);
+	fn[v1] = '\0';
+
+	(*p) += v1;
+	if (**p == '"')
+		(*p)++;
+
+	// open the file
+	f = fopen(fn, "rb");
+	if (!f)
+	{
+		register_error(as, l, 1, "Cannot open file: %s", strerror(errno));
+		register_error(as, l, 2, "Cannot open file: %s", strerror(errno));
+	}
+	
+	// don't need fn any more
+	lwasm_free(fn);
+	
+	// read the contents of the file and "emit()" it
+	while (!feof(f) && !ferror(f))
+	{
+		v1 = fgetc(f);
+		if (v1 == EOF)
+			break;
+		lwasm_emit(as, l, v1);
+	}
+	// close file
+	fclose(f);
+}
+
+OPFUNC(pseudo_rmb)
+{
+	int r, v;
+	
+	if (as -> passnum == 2)
+	{
+		as -> addr += l -> nocodelen;
+		skip_operand(p);
+		return;
+	}
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, -1);
+	if (r != 0)
+		return;
+	l -> nocodelen = v;
+	as -> addr += v;
+}
+
+OPFUNC(pseudo_rmd)
+{
+	int r, v;
+	
+	if (as -> passnum == 2)
+	{
+		as -> addr += l -> nocodelen;
+		skip_operand(p);
+		return;
+	}
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	v *= 2;
+	l -> nocodelen = v;
+	as -> addr += v;
+}
+
+OPFUNC(pseudo_rmq)
+{
+	int r, v;
+	
+	if (as -> passnum == 2)
+	{
+		as -> addr += l -> nocodelen;
+		skip_operand(p);
+		return;
+	}
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	v *= 4;
+	l -> nocodelen = v;
+	as -> addr += v;
+}
+
+OPFUNC(pseudo_zmb)
+{
+	int r, v;
+	
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	while (v--)
+		lwasm_emit(as, l, 0);
+}
+
+OPFUNC(pseudo_zmd)
+{
+	int r, v;
+	
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	v *= 2;
+	while (v--)
+		lwasm_emit(as, l, 0);
+}
+
+OPFUNC(pseudo_zmq)
+{
+	int r, v;
+	
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+	v *= 4;
+	while (v--)
+		lwasm_emit(as, l, 0);
+}
+
+OPFUNC(pseudo_end)
+{
+	int r, v;
+	lwasm_expr_stack_t *s;
+	
+
+	as -> endseen = 1;
+	
+	// address only matters for DECB output
+	if (as -> outformat != OUTPUT_DECB)
+	{
+		skip_operand(p);
+		return;
+	}
+	
+	r = lwasm_expr_result2(as, l, p, 0, &v, 0);
+	if (r != 0)
+	{
+		register_error(as, l, 2, "Bad operand");
+	}
+
+	v = v & 0xffff;
+	if (as -> passnum == 2)
+	{
+		as -> execaddr = v;
+		l -> symaddr = v;
+		l -> addrset = 2;
+	}
+}
+
+
+OPFUNC(pseudo_align)
+{
+	int cn;
+	int r, v;
+	int pad = 0;
+// we have to parse this on pass 2 so that we get the pad value	
+//	if (as -> passnum == 2)
+//	{
+//		skip_operand(p);
+//		while (as -> addr < l -> symaddr)
+//			lwasm_emit(as, l, 0);
+//		return;
+//	}
+	
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+	{
+		l -> symaddr = as -> addr;
+		return;
+	}
+
+	if (v < 1)
+	{
+		register_error(as, l, 1, "Illegal alignment %d", v);
+		return;
+	}
+	
+	cn = l -> codeaddr % v;
+	if (cn)
+		cn = v - cn; 
+
+	if (**p == ',')
+	{
+		// we have a padding value specified
+		(*p)++;
+		r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST, &pad, 1);
+		if (r != 0 && as -> passnum == 2)
+		{
+			register_error(as, l, 2, "Illegal padding value - must be constant on pass 2");
+			return;
+		}
+	}
+
+	while (cn--)
+	{
+		lwasm_emit(as, l, pad);
+	}
+	l -> symaddr = as -> addr;
+}
+
+OPFUNC(pseudo_equ)
+{
+	int r, v;
+
+	if (l -> sym == NULL)
+	{
+		register_error(as, l, 1, "No symbol specified");
+		return;
+	}
+
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r < 0)
+		v = 0;
+
+	l -> symaddr = v & 0xFFFF;
+	l -> addrset = 2;
+	
+	// note: we need to do this because the symbol might have resolved
+	// to a constant!
+	lwasm_register_symbol(as, l, l -> sym, v, (r > 0 ? SYMBOL_COMPLEX: SYMBOL_NORM) | SYMBOL_FORCE | (l -> forceglobal ? SYMBOL_GLOBAL : SYMBOL_NORM));
+}
+
+OPFUNC(pseudo_set)
+{
+	int r, v;
+
+	// set MUST run on both passes as the symbol value changes!
+
+	if (l -> sym == NULL)
+	{
+		register_error(as, l, 1, "No symbol specified");
+		return;
+	}
+
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r < 0)
+		v = 0;
+
+	l -> symaddr = v & 0xFFFF;
+	l -> addrset = 2;
+	
+	lwasm_register_symbol(as, l, l -> sym, v, (r > 0 ? SYMBOL_COMPLEX: SYMBOL_NORM) | SYMBOL_SET);
+}
+
+OPFUNC(pseudo_setdp)
+{
+	int r, v;
+
+	if (as -> outformat == OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "SETDP not permitted with OBJ target");
+		return;
+	}
+	
+	// setdp is needed on both passes; must resolve to a constant on pass 1
+	r = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v, 0);
+	if (r != 0)
+		return;
+
+	if (v < -127 || v > 255)
+	{
+		register_error(as, l, 1, "Byte overflow");
+		return;
+	}
+	
+	l -> symaddr = v & 0xFF;
+	l -> addrset = 2;
+	
+	as -> dpval = v & 0xFF;
+}
+
+// used to get a byte from a string
+// -1 is end of line
+int pseudo_fcc_fetchchar(asmstate_t *as, char **p)
+{
+	int c;
+	
+	// -
+	if (!**p)
+		return -1;
+	
+	c = (unsigned char)(**p);
+	(*p)++;
+	
+	if (as -> pragmas & PRAGMA_CESCAPES && c == '\\')
+	{
+		// decode escapes if needed
+		if (!**p)
+			return c;
+		
+		c = **p;
+		(*p)++;
+		
+		switch (c)
+		{
+		// octal value
+		// 1, 2, or 3 digits
+		// NOTE: \0 for NUL is included in this...
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+			c -= '0';
+			if (**p < '0' || **p > '9')
+				return c;
+			c = c << 3;
+			c |= **p - '0';
+			(*p)++;
+			if (**p < '0' || **p > '9')
+				return c;
+			c = c << 3;
+			c |= **p - '0';
+			(*p)++;
+			return c;
+
+		// LF
+		case 'n':
+			return 10;
+		
+		// CR
+		case 'r':
+			return 13;
+		
+		// TAB
+		case 't':
+			return 9;
+		
+		// VT
+		case 'v':
+			return 11;
+		
+		// BS
+		case 'b':
+			return 8;
+		
+		// FF
+		case 'f':
+			return 12;
+		
+		// BEL
+		case 'a':
+			return 7;
+		
+		// hex char code (2 chars)
+		case 'x':
+		{
+			int c2;
+			if (!**p)
+				return 'x';
+			c = toupper(**p);
+			(*p)++;
+			if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+				return 0;
+			c -= '0';
+			if (c > 9)
+				c -= 7;
+			c2 = c << 4;
+			if (!**p)
+				return 0;
+			c = toupper(**p);
+			(*p)++;
+			if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+				return 0;
+			c -= '0';
+			if (c > 9)
+				c -= 7;
+			c2 |= c;
+			return c2;
+		}
+		// everything else stands for itself as a fall back or legit		
+		default:
+			return c;
+		}
+	}
+	return c;
+}
+
+OPFUNC(pseudo_fcc)
+{
+	int delim = 0;
+	int c;
+				
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	for (;;)
+	{
+		c = pseudo_fcc_fetchchar(as, p);
+		if (c == delim || c < 0)
+			break;
+
+		lwasm_emit(as, l, c);
+	}
+}
+		
+
+OPFUNC(pseudo_fcs)
+{
+	int delim = 0;
+	int c, lc = -1;
+	
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	for (;;)
+	{
+		c = pseudo_fcc_fetchchar(as, p);
+		if (c == delim || c < 0)
+		{
+			if (lc >= 0)
+				lwasm_emit(as, l, lc | 0x80);
+			break;
+		}
+		if (lc >= 0)
+			lwasm_emit(as, l, lc);
+		lc = c;
+	}
+}
+
+OPFUNC(pseudo_fcn)
+{		
+	int delim = 0;
+	int c;
+	
+	delim = **p;
+	if (!delim)
+	{
+		register_error(as, l, 1, "Bad operand");
+		return;
+	}
+	*p += 1;
+	for (;;)
+	{
+		c = pseudo_fcc_fetchchar(as, p);
+		if (c == delim || c < 0)
+			break;
+
+		lwasm_emit(as, l, c);
+	}
+	lwasm_emit(as, l, 0);
+}
+
+OPFUNC(pseudo_fcb)
+{
+	int r, v;
+	
+fcb_again:
+	r = lwasm_expr_result2(as, l, p, 0, &v, -1);
+	if (r < 0)
+		return;
+
+	if (r > 0)
+	{
+		register_error(as, l, 2, "Illegal external or inter-segment reference");
+		v = 0;
+	}
+
+	if (v < -127 || v > 255)
+	{
+		register_error(as, l, 1, "Byte overflow");
+	}
+	
+	lwasm_emit(as, l, v);
+	if (**p == ',')
+	{
+		(*p)++;
+		goto fcb_again;
+	}
+}
+
+// FIXME: handle external references in an intelligent way
+OPFUNC(pseudo_fdb)
+{			
+	int r, v;
+	int extseen = 0;
+	char *p1;
+	
+fdb_again:
+	p1 = *p;
+	r = lwasm_expr_result2(as, l, p, 0, &v, -1);
+	if (r < 0)
+		return;
+
+	if (r > 0 && extseen == 1)
+	{
+		register_error(as, l, 2, "Illegal external or inter-segment reference (only 1 per FDB line)");
+		v = 0;
+	}
+	else if (r > 0)
+	{
+		l -> relocoff = as -> addr - l -> codeaddr;
+		*p = p1;
+		r = lwasm_expr_result2(as, l, p, 0, &v, 0);
+	}
+
+	lwasm_emit(as, l, v >> 8);
+	lwasm_emit(as, l, v & 0xff);
+	if (**p == ',')
+	{
+		(*p)++;
+		goto fdb_again;
+	}
+}
+
+// FIXME: handle external references in a sensible way
+OPFUNC(pseudo_fqb)
+{	
+	int r, v;
+	
+fqb_again:
+	r = lwasm_expr_result2(as, l, p, 0, &v, -1);
+	if (r < 0)
+		return;
+
+	if (r > 0)
+	{
+		register_error(as, l, 2, "Illegal external or inter-segment reference");
+		v = 0;
+	}
+
+	lwasm_emit(as, l, v >> 24);
+	lwasm_emit(as, l, v >> 16);
+	lwasm_emit(as, l, v >> 8);
+	lwasm_emit(as, l, v & 0xff);
+	if (**p == ',')
+	{
+		(*p)++;
+		goto fqb_again;
+	}
+}
+
+// don't need to do anything if we are executing one of these
+OPFUNC(pseudo_endc)
+{
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount -= 1;
+		if (as -> skipcount <= 0)
+		{
+			as -> skipcond = 0;
+		}
+	}
+	return;
+}
+
+// if "else" executes, we must be going into an "ignore" state
+OPFUNC(pseudo_else)
+{
+	if (as -> skipmacro)
+		return;
+	
+	if (as -> skipcond)
+	{
+		if (as -> skipcount == 1)
+		{
+			as -> skipcount = 0;
+			as -> skipcond = 0;
+		}
+		return;
+	}
+	
+	as -> skipcond = 1;
+	as -> skipcount = 1;
+}
+
+OPFUNC(pseudo_ifne)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (!v1)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_ifdef)
+{
+	lwasm_symbol_ent_t *se;
+	char *sym;
+	char *p2;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		skip_operand(p);
+		if (!(l -> fsize))
+		{
+			as -> skipcond = 1;
+			as -> skipcount = 1;
+		}
+		return;
+	}
+
+	if (!**p)
+	{
+		register_error(as, l, 1, "Need symbol name");
+		return;
+	}
+	
+	for (p2 = *p; **p && !isspace(**p); (*p)++)
+		/* do nothing */ ;
+	
+	sym = lwasm_alloc(*p - p2 + 1);
+	memcpy(sym, p2, *p - p2);
+	sym[*p - p2] = '\0';
+
+//	fprintf(stderr, "STUFF: %s; '%s'; '%s' (%d)\n", p2, *p, sym, as -> passnum);
+	se = lwasm_find_symbol(as, sym, l -> context);
+	if (!se)
+		se = lwasm_find_symbol(as, sym, -1);
+	
+	lwasm_free(sym);
+	
+	if (!se)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+		l -> fsize = 0;
+	}
+	else
+	{
+		l -> fsize = 1;
+	}
+}
+
+OPFUNC(pseudo_ifndef)
+{
+	lwasm_symbol_ent_t *se;
+	char *sym;
+	char *p2;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		skip_operand(p);
+		if (l -> fsize)
+		{
+			as -> skipcond = 1;
+			as -> skipcount = 1;
+		}
+		return;
+	}
+
+	if (!**p)
+	{
+		register_error(as, l, 1, "Need symbol name");
+		return;
+	}
+	
+	for (p2 = *p; *p2 && !isspace(*p2); p2++)
+		/* do nothing */ ;
+	
+	sym = lwasm_alloc(p2 - *p + 1);
+	memcpy(sym, *p, p2 - *p);
+	sym[p2 - *p] = '\0';
+	
+	*p = p2;
+
+//	fprintf(stderr, "STUFF2: %s; '%s'; '%s' (%d)\n", *p, p2, sym, as -> passnum);
+	se = lwasm_find_symbol(as, sym, l -> context);
+	if (!se)
+		se = lwasm_find_symbol(as, sym, -1);
+	
+	lwasm_free(sym);
+	
+	if (se)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+		l -> fsize = 0;
+	}
+	else
+	{
+		l -> fsize = 1;
+	}
+}
+
+OPFUNC(pseudo_ifp1)
+{
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	if (as -> passnum != 1)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+	
+OPFUNC(pseudo_ifp2)
+{
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	if (as -> passnum != 2)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+	
+OPFUNC(pseudo_ifeq)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (v1)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_iflt)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (v1 >= 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_ifle)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (v1 > 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_ifgt)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (v1 <= 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_ifge)
+{
+	int v1;
+	int rval;
+
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	rval = lwasm_expr_result2(as, l, p, EXPR_SECTCONST | EXPR_PASS1CONST, &v1, 0);
+	if (rval != 0)
+		return;
+	if (v1 < 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+OPFUNC(pseudo_error)
+{
+	register_error(as, l, 1, "User error: %s", *p);
+}
+
+
+OPFUNC(pseudo_section)
+{
+	sectiontab_t *s;
+	char *p2;
+	char *sn;
+	char *opts;
+	
+
+	if (as -> outformat != OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "Sections only supported for obj target");
+		return;
+	}
+	
+	if (as -> csect)
+	{
+		as -> csect -> offset = as -> addr;
+		as -> csect = NULL;
+	}
+	
+	if (!**p)
+	{
+		register_error(as, l, 1, "Need section name");
+		return;
+	}
+	
+	for (p2 = *p; *p2 && !isspace(*p2); p2++)
+		/* do nothing */ ;
+	
+	sn = lwasm_alloc(p2 - *p + 1);
+	memcpy(sn, *p, p2 - *p);
+	sn[p2 - *p] = '\0';
+	
+	*p = p2;
+	
+	opts = strchr(sn, ',');
+	if (opts)
+	{
+		*opts++ = '\0';
+	}
+	
+	// have we seen the section name already?
+	for (s = as -> sections; s; s = s -> next)
+	{
+		if (!strcmp(s -> name, sn))
+			break;
+	}
+	
+	if (s && as -> passnum == 1)
+	{
+		lwasm_free(sn);
+		if (opts)
+		{
+			register_error(as, l, 1, "Section options can only be specified the first time");
+			return;
+		}
+	}
+	else if (!s)
+	{
+		s = lwasm_alloc(sizeof(sectiontab_t));
+		s -> name = sn;
+		s -> offset = 0;
+		s -> flags = 0;
+		s -> obytes = NULL;
+		s -> oblen = 0;
+		s -> obsize = 0;
+		s -> rl = NULL;
+		s -> exports = NULL;
+		// if name of section is "bss" or ".bss", assume bss flag
+		// which can be overridden with !bss flag
+		if (!strcasecmp(sn, "bss") || !strcasecmp(sn, ".bss"))
+			s -> flags = SECTION_BSS;
+		// parse options; only one "bss"
+		if (opts && as -> passnum == 1)
+		{
+			if (!strcasecmp(opts, "bss"))
+			{
+				s -> flags |= SECTION_BSS;
+			}
+			else if (!strcasecmp(opts, "!bss"))
+			{
+				s -> flags &= ~SECTION_BSS;
+			}
+			else
+			{
+				register_error(as, l, 1, "Unrecognized section option '%s'", opts);
+				lwasm_free(s -> name);
+				lwasm_free(s);
+				return;
+			}
+		}
+		
+		s -> next = as -> sections;
+		as -> sections = s;
+	}
+	as -> addr = s -> offset;
+	as -> csect = s;
+	as -> context = lwasm_next_context(as);
+}
+
+OPFUNC(pseudo_endsection)
+{
+	if (as -> outformat != OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "Sections only supported for obj target");
+		return;
+	}
+	
+	if (!(as -> csect))
+	{
+		register_error(as, l, 1, "ENDSECTION when not in a section");
+		return;
+	}
+	
+	as -> csect -> offset = as -> addr;
+	as -> addr = 0;
+	as -> csect = 0;
+	as -> context = lwasm_next_context(as);
+	skip_operand(p);
+}
+
+OPFUNC(pseudo_extern)
+{
+	if (as -> passnum != 1)
+		return;
+
+	if (as -> outformat != OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "External references only supported for obj target");
+		return;
+	}
+	
+	if (as -> csect)
+	{
+		register_error(as, l, 1, "Cannot declare external symbols within a section");
+		return;
+	}
+	
+	if (l -> sym)
+	{
+		lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN);
+		for ( ; **p; (*p)++) ;
+		return;
+	}
+	
+	while (**p)
+	{
+		char *sym2, *sym3;
+		for (sym2 = *p; **p && !isspace(**p) && **p != ','; (*p)++)
+			/* do nothing */ ;
+
+		if (l -> sym)
+			lwasm_free(l -> sym);
+
+		sym3 = lwasm_alloc(*p - sym2 + 1);
+		memcpy(sym3, sym2, *p - sym2);
+		sym3[*p - sym2] = '\0';
+			
+		l -> sym = sym3;
+		debug_message(2, "import symbol: '%s'", sym3);
+		lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN);
+		if (**p && (**p != ','))
+		{
+			register_error(as, l, 1, "Bad import list");
+			return;
+		}
+		if (**p == ',')
+			(*p)++;
+	}
+	if (!(l -> sym))
+		register_error(as, l, 1, "Bad import list");
+}
+
+OPFUNC(pseudo_export)
+{
+	lwasm_symbol_ent_t *se;
+	export_list_t *ex;
+	char *sym2, *sym3;
+	int after = 0;
+
+	if (as -> outformat != OUTPUT_OBJ)
+	{
+		register_error(as, l, 1, "Symbol exports only supported for obj target");
+		return;
+	}
+
+	if (as -> passnum == 1)
+	{
+		skip_operand(p);
+		return;
+	}
+
+	if (l -> sym)
+	{
+		for ( ; **p; (*p)++) ;
+	}
+
+again:
+	if (!(l -> sym) || after == 1)
+	{
+	
+		after = 1;
+		// look for symbol after op
+		if (**p)
+		{
+			for (sym2 = *p; **p && !isspace(**p) && **p != ','; (*p)++)
+				/* do nothing */ ;
+
+			debug_message(2, "export expression: %s", sym2);			
+			if (l -> sym)
+				lwasm_free(l -> sym);
+			sym3 = lwasm_alloc(*p - sym2 + 1);
+			memcpy(sym3, sym2, *p - sym2);
+			sym3[*p - sym2] = '\0';
+			
+			l -> sym = sym3;
+			debug_message(2, "export symbol: '%s'", sym3);
+		}
+	}
+	if (!(l -> sym))
+	{
+		register_error(as, l, 2, "No symbol");
+		return;
+	}
+
+
+	// the symbol better be defined at this point (pass 2)
+	// local symbols cannot be exported nor can "global" symbols
+	se = lwasm_find_symbol(as, l -> sym, -1);
+	if ((!se || (se -> flags & SYMBOL_EXTERN)) && (as -> pragmas & PRAGMA_IMPORTUNDEFEXPORT))
+	{
+		void *p;
+		p = as -> csect;
+		as -> csect = NULL;
+		lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN);
+		as -> csect = p;
+	}
+	else
+	{
+		if (!se)
+		{
+			register_error(as, l, 2, "Exported symbols must be fully defined within a section");
+			return;
+		}
+		if (se -> sect == NULL)
+		{
+			register_error(as, l, 2, "Only non-local symbols within a section can be exported");
+			return;
+		}
+
+		if (se -> flags & SYMBOL_SET)
+		{
+			register_error(as, l, 2, "You cannot export symbols defined with SET");
+			return;
+		}
+
+		// if the symbol is not already a simple value, re-evaluate it
+		// and see if it becomes simple
+	
+
+		if (se -> flags & SYMBOL_COMPLEX)
+		{
+			register_error(as, l, 2, "Exported symbols must be fully resolved on pass 2");
+			return;
+		}
+
+		// search for existing export
+		for (ex = se -> sect -> exports; ex; ex = ex -> next)
+			if (!strcmp(l -> sym, ex -> sym))
+				break;
+		if (ex)
+		{
+			register_error(as, l, 2, "Symbol %s already exported", l -> sym);
+			return;
+		}
+
+		// add an external reference
+		ex = lwasm_alloc(sizeof(export_list_t));
+		ex -> next = se -> sect -> exports;
+		se -> sect -> exports = ex;
+		ex -> offset = se -> value;
+		ex -> sym = lwasm_strdup(se -> sym);
+	}
+next:
+	if (after == 1)
+	{
+		if (**p == ',')
+		{
+			debug_message(2, "Export another symbol: %s", *p);
+			(*p)++;
+			for ( ; **p && isspace(**p); (*p)++)
+				/* do nothing */ ;
+			goto again;
+		}
+	}
+}