view src/lwasm.c @ 96:7fbccdd1defb

Added doc subdirectory to distribution
author lost
date Sat, 17 Jan 2009 07:09:02 +0000
parents 83ba34ed11b3
children 81fc353d4d69
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 <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 (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 -> 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;
}

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;
};	

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 -> as -> outformat == OUTPUT_OBJ && se -> sect != NULL)
	{
		// do not resolve any section symbols in object mode
		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;
	}
	
	// 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)
{
	struct symstateinfo st;
	
	st.as = as;
	st.l = l;
	
	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)
{
	struct symstateinfo st;
	
	st.as = as;
	st.l = l;
	
	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.

Symbol resolution will be modified for the object target as follows:
- a symbol which is not defined within a section will evaluate as a constant
- a symbol which is defined within the same section will evaluate as a constant
- a symbol defined in another section will remain unresolved
- external references will also remain unresolved

EXPR_PASS2PASS will cause the result from pass 1 along with the offset to
the end of the expression to be stored in the line data. There can only be
one such expression per source line. In this case, the expression is parsed
and evaluated on pass 1 but the intermediate representation is re-evaluated
on pass 2.
*/
/*
int lwasm_expr_result(asmstate_t *as, lwasm_line_t *l, char **inp, int flag, int *val)
{
	lwasm_expr_stack_t *s;
	const char *ep;
	int rval;
		
	s = lwasm_evaluate_expr(as, l, *inp, &ep);
	if (!s)
	{
		register_error(as, l, 1, "Bad expression");
		*val = 0;
		return -1;
	}
	*inp = (char *)ep;
	
	if (flag & EXPR_PASS1CONST && as -> passnum == 1 && !lwasm_expr_result_ckconst(as, s))
	{
		register_error(as, l, 1, "Undefined reference (pass 1)");
		*val = 0;
		lwasm_expr_stack_free(s);
		return -1;
	}
	if (flag & EXPR_PASS2CONST && as -> passnum == 2 && !lwasm_expr_result_ckconst(as, s))
	{
		register_error(as, l, 2, "Undefined reference (pass 2)");
		*val = 0;
		lwasm_expr_stack_free(s);
		return -1;
	}
	if (flag & EXPR_NOINTERSECT && !lwasm_expr_is_constant(s))
	{
		register_error(as, l, 2, "Invalid inter-section reference");
	}
	*val = lwasm_expr_get_value(s);
	if (l -> expr)
	{
		lwasm_expr_stack_free(l -> expr);
		l -> expr = NULL;
	}
	if (lwasm_is_constant(s))
	{
		// fully resolved value here
		lwasm_expr_stack_free(s);
	}
	else
	{
		// incomplete reference here
		l -> expr = s;
	}

	if (flag & EXPR_BYTE && as -> passnum == 2 && (*val < -128 || *val > 255))
	{
		register_error(as, l, 2, "Byte overflow");
		*val &= 0xff;
		return -1;
	}
	if (flag & EXPR_BYTE)
	{
		*val &= 0xff;
	}

	return 0;
}
*/
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 || slot < 0)
	{		
		s = lwasm_evaluate_expr(as, l, *inp, &ep);
		if (slot >= 0)
			l -> exprs[slot] = s;
		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);
		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;
}