view old-trunk/lwasm/old/lwasm.c @ 344:0215a0fbf61b

Added assembly error system and additional checks for symbol syntax
author lost@starbug
date Thu, 25 Mar 2010 22:06:50 -0600
parents eb230fa7d28e
children
line wrap: on
line source

/*
lwasm.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 random functions used by the assembler
*/

#define __lwasm_c_seen__
#include <config.h>

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>

#include "lwasm.h"
#include "util.h"
#include "expr.h"

int debug_level = 0;

int register_error(asmstate_t *as, lwasm_line_t *l, int pass, const char *fmt, ...)
{
	lwasm_error_t *e;
	va_list args;
	char errbuff[1024];
	int r;
	
	if (!l)
		return;

	if (as -> passnum != pass)
		return;
	
	va_start(args, fmt);
	
	e = lwasm_alloc(sizeof(lwasm_error_t));
	
	e -> next = l -> err;
	l -> err = e;
	
	as -> errorcount++;
	
	r = vsnprintf(errbuff, 1024, fmt, args);
	e -> mess = lwasm_strdup(errbuff);
	
	va_end(args);
	
	return r;
}

void lwasm_emit(asmstate_t *as, lwasm_line_t *l, int b)
{
	as -> addr += 1;
	as -> addr &= 0xffff;
	
	if (as -> outformat == OUTPUT_OBJ && !(as -> csect))
	{
		register_error(as, l, 1, "Output not allowed outside sections with obj target");
		return;
	}
	if (as -> outformat == OUTPUT_OBJ && as -> csect -> flags & SECTION_BSS)
	{
		register_error(as, l, 1, "Output not allowed inside BSS sections");
		return;
	}
	
	if (as -> outformat == OUTPUT_OS9 && as -> inmod == 0)
	{
		register_error(as, l, 1, "Output not allowed outside of module in OS9 mode");
		return;
	}
	
	if (as -> passnum == 1)
		return;


	if (l -> codelen >= l -> codesize)
	{
		l -> bytes = realloc(l -> bytes, l -> codesize + 16);
		l -> codesize += 16;
	}
	l -> bytes[l -> codelen] = b & 0xff;
	l -> codelen += 1;
	
	if (as -> outformat == OUTPUT_OS9 && as -> inmod)
	{
		// calc the CRC
		// this is a direct transliteration from the nitros9 asm source
		// to C; it can, no doubt, be optimized for 32 bit processing
		b &= 0xff;
		
		b ^= (as -> crc)[0];
		(as -> crc)[0] = (as -> crc)[1];
		(as -> crc)[1] = (as -> crc)[2];
		(as -> crc)[1] ^= (b >> 7);
		(as -> crc)[2] = (b << 1);
		(as -> crc)[1] ^= (b >> 2);
		(as -> crc)[2] ^= (b << 6);
		b ^= (b << 1);
		b ^= (b << 2);
		b ^= (b << 4);
		if (b & 0x80)
		{
			(as -> crc)[0] ^= 0x80;
			(as -> crc)[2] ^= 0x21;
		}
	}
}

void lwasm_emitop(asmstate_t *as, lwasm_line_t *l, int o)
{
	if (o >= 0x100)
		lwasm_emit(as, l, o >> 8);
	lwasm_emit(as, l, o & 0xff);
}

int lwasm_lookupreg2(const char *reglist, char **str)
{
	int rval = 0;

	while (*reglist)
	{
		if (toupper(**str) == *reglist)
		{
			// first char matches
			if (reglist[1] == ' ' && !isalpha(*(*str + 1)))
				break;
			if (toupper(*(*str + 1)) == reglist[1])
				break;
		}
		reglist += 2;
		rval++;
	}
	if (!*reglist)
		return -1;
	if (reglist[1] == ' ')
		(*str)++;
	else
		(*str) += 2;
	return rval;
}

int lwasm_lookupreg3(const char *rlist, const char **str)
{
	int rval = 0;
	int f = 0;
	const char *reglist = rlist;
		
	while (*reglist)
	{
		if (toupper(**str) == *reglist)
		{
			// first char matches
			if (reglist[1] == ' ')
			{
				f = 1;
				break;
			}
			if (toupper(*(*str + 1)) == reglist[1])
			{
				// second char matches
				if (reglist[2] == ' ')
				{
					f = 1;
					break;
				}
				if (toupper(*(*str + 2)) == reglist[2])
				{
					f = 1;
					break;
				}
			}
		}
		reglist += 3;
		rval++;
	}
	if (f == 0)
		return -1;
	
	
	reglist = rval * 3 + rlist;
	if (reglist[1] == ' ')
		(*str) += 1;
	else if (reglist[2] == ' ')
		(*str) += 2;
	else
		(*str)+=3;
	return rval;
}

struct symstateinfo
{
	asmstate_t *as;
	lwasm_line_t *l;
	int flags;
};	

lwasm_expr_stack_t *lwasm_expr_lookup_symbol(char *sym, void *state)
{
	lwasm_symbol_ent_t *se;
	struct symstateinfo *st;
	lwasm_expr_stack_t *rs;
	lwasm_expr_term_t *t;
	lwasm_expr_stack_node_t *n;
	
	int val;
	
	st = state;
	debug_message(3, "lwasm_expr_lookup_symbol(): find '%s' (context=%d)", sym, st -> as -> context);	

	// check for special symbols first...
	if (sym[1] == '\0')
	{
		switch (sym[0])
		{
		// current line address
		case '*':
		case '.':
			val = st -> l -> codeaddr;
			goto retconst;
		
		case '<':
			// previous branch point
			// not implemented
			break;
		case '>':
			// next branch point
			// not implemented
			break;
		}
	}

	// look for local symbol first then global symbol
	se = lwasm_find_symbol(st -> as, sym, st -> as -> context);
	if (!se)
		se = lwasm_find_symbol(st -> as, sym, -1);
	debug_message(3, "lwasm_expr_lookup_symbol(): got '%p'", se);
	if (!se)
	{
		register_error(st -> as, st -> l, 2, "Undefined symbol '%s'", sym);
		return NULL;
	}
	// external reference - can not resolve it
	if (se -> flags & SYMBOL_EXTERN)
	{
		return NULL;
	}
	if (st -> flags & EXPR_SECTCONST)
	{
		if (se -> sect == st -> l -> sect)
		{
			if (se -> expr)
				goto retsym;
			val = se -> value;
			goto retconst;
		}
	}
	if (st -> as -> outformat == OUTPUT_OBJ && se -> sect != NULL)
	{
		return NULL;
	}
	if (st -> as -> outformat != OUTPUT_OBJ || se -> sect == NULL)
	{
		// global symbol, intrasegment reference, or not an object target
		val = se -> value;
		goto retconst;
	}
	
	// an intersegment reference will return as NULL (to be resolved at output/link time)
	// if se -> expr is NULL, it has to be an intersegment reference here
	if (se -> expr == NULL)
	{
		return NULL;
	}
	
retsym:
	// duplicate the expression for return
	rs = lwasm_expr_stack_create();
	for (n = se -> expr -> head; n; n = n -> next)
	{
		lwasm_expr_stack_push(rs, n -> term);
	}
	return rs;

retconst:
	rs = lwasm_expr_stack_create();
	t = lwasm_expr_term_create_int(val);
	lwasm_expr_stack_push(rs, t);
	lwasm_expr_term_free(t);
	return rs;
}

lwasm_expr_stack_t *lwasm_evaluate_expr(asmstate_t *as, lwasm_line_t *l, const char *inp, const char **outp, int flags)
{
	struct symstateinfo st;
	
	st.as = as;
	st.l = l;
	st.flags = flags;
	
	debug_message(2, "Evaluate expression: %s", inp);
	
	return(lwasm_expr_eval(inp, outp, lwasm_expr_lookup_symbol, &st));
}


int lwasm_reevaluate_expr(asmstate_t *as, lwasm_line_t *l, lwasm_expr_stack_t *s, int flags)
{
	struct symstateinfo st;
	
	st.as = as;
	st.l = l;
	st.flags = flags;
	return(lwasm_expr_reval(s, lwasm_expr_lookup_symbol, &st));
}

// return 1 if no undefined symbols (externals and incompletes are okay)
// return 0 if there are undefined symbols
int lwasm_expr_result_ckconst(asmstate_t *as, lwasm_expr_stack_t *s)
{
	lwasm_expr_stack_node_t *n;
	lwasm_symbol_ent_t *se;
	
	if (as -> outformat != OUTPUT_OBJ)
	{
		if (lwasm_expr_is_constant(s))
			return 1;
		else
			return 0;
	}
	
	for (n = s -> head; n; n = n -> next)
	{
		if (n -> term -> term_type == LWASM_TERM_SYM)
		{
			se = lwasm_find_symbol(as, n -> term -> symbol, as -> context);
			if (!se)
				se = lwasm_find_symbol(as, n -> term -> symbol, -1);
			if (!se)
				return 0;
		}
	}
	return 1;
}

/*
Evaluate an expression according to the flag value. Return 0 if a constant result was
obtained, 1 if an incomplete result was obtained, and -1 if an error was flagged.

*/
int lwasm_expr_result2(asmstate_t *as, lwasm_line_t *l, char **inp, int flag, int *val, int slot)
{
	lwasm_expr_stack_t *s = NULL;
	const char *ep;
	int rval;

	if ((as -> passnum == 1 && !(flag & EXPR_REEVAL)) || slot < 0)
	{		
		s = lwasm_evaluate_expr(as, l, *inp, &ep, flag);
		if (slot >= 0)
		{
			l -> exprs[slot] = s;
			l -> exprends[slot] = ep;
		}
		if (!s)
		{
			register_error(as, l, 1, "Bad expression");
			*val = 0;
			return -1;
		}
		*inp = (char *)ep;
		if (slot >= 0)
		{
//			l -> exprends[slot] = (char *)ep;
			l -> exprvals[slot] = lwasm_expr_get_value(s);
		}
	}
	else if (l -> exprs[slot])
	{
		s = l -> exprs[slot];
		lwasm_reevaluate_expr(as, l, s, flag);
		l -> exprvals[slot] = lwasm_expr_get_value(s);
	}
	if (as -> passnum == 2 && slot >= 0)
		*inp = l -> exprends[slot];

	if (s && lwasm_expr_is_constant(s))
	{
		*val = lwasm_expr_get_value(s);
		lwasm_expr_stack_free(s);
		l -> exprs[slot] = NULL;
		s = NULL;
		return 0;
	}

	if (!s && slot >= 0)
	{
		*val = l -> exprvals[slot];
		return 0;
	}
	else if (!s)
	{
		*val = 0;
		return 0;
	}

	// was a constant result on pass 1 requested?
	// that means we must have a constant on either pass
	if (flag & EXPR_PASS1CONST)
	{
		*val = 0;
		if (slot >= 0)
			l -> exprvals[slot] = 0;
		register_error(as, l, 1, "Illegal forward, external, or inter-section reference");
		lwasm_expr_stack_free(s);
		if (slot >= 0)
			l -> exprs[slot] = NULL;
		return -1;
	}
	
	return 1;
}

void debug_message(int level, const char *fmt, ...)
{
	va_list args;
	
	va_start(args, fmt);
	if (debug_level >= level)
	{
		if (level > 0)
			fprintf(stderr, "DEBUG %d: ", level);
		vfprintf(stderr, fmt, args);
		fputc('\n', stderr);
	}
	va_end(args);
}

int lwasm_next_context(asmstate_t *as)
{
	int r;
	r = as -> nextcontext;
	as -> nextcontext += 1;
	debug_message(3, "lwasm_next_context(): %d (%d) pass %d", r, as -> nextcontext, as -> passnum);
	return r;
}