view lwasm/pseudo.c @ 159:71561c12b20b

Updated docs to reflect new cescapes pragma and discuss implicit assumption of the bss section flag for sections named bss and .bss
author lost
date Sat, 31 Jan 2009 06:32:27 +0000
parents f0527dc3804d
children b061350c17e4
line wrap: on
line source

/*
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 <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)
{

}

OPFUNC(pseudo_org)
{
	int v, r;

	if (as -> csect)
	{
		register_error(as, l, 1, "ORG not allowed within sections");
		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;
		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);
		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';

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

OPFUNC(pseudo_rmb)
{
	int r, v;
	
	if (as -> passnum == 2)
	{
		as -> addr += l -> nocodelen;
		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;
		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;
		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)
		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;
	
	if (as -> passnum == 2)
	{
		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; 

	while (cn--)
	{
		lwasm_emit(as, l, 0);
	}
	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);
}

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

OPFUNC(pseudo_fcc)
{
	int delim = 0;
				
	delim = **p;
	if (!delim)
	{
		register_error(as, l, 1, "Bad operand");
		return;
	}
	*p += 1;
	while (**p && **p != delim)
	{
		lwasm_emit(as, l, **p);
		(*p)++;
	}
	if (**p)
		(*p)++;
}
		

OPFUNC(pseudo_fcs)
{
	int delim = 0;

	delim = **p;
	if (!delim)
	{
		register_error(as, l, 1, "Bad operand");
		return;
	}
	*p += 1;
	while (**p && **p != delim)
	{
		if (!*((*p) + 1) || *((*p) + 1) == delim)
			lwasm_emit(as, l, **p | 0x80);
		else
			lwasm_emit(as, l, **p);
		(*p)++;
	}
	if (**p)
		(*p)++;
}

OPFUNC(pseudo_fcn)
{		
	int delim = 0;
				
	delim = **p;
	if (!delim)
	{
		register_error(as, l, 1, "Bad operand");
		return;
	}
	*p += 1;
	while (**p && **p != delim)
	{
		lwasm_emit(as, l, **p);
		(*p)++;
	}
	if (**p)
		(*p)++;
	lwasm_emit(as, l, 0);
}

// FIXME: handle external, etc., references in a useful manner
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++;
		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++;
		return;
	}

	if (as -> passnum != 1)
	{
		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;

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

	if (as -> passnum != 1)
	{
		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;

	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_ifeq)
{
	int v1;
	int rval;

	if (as -> skipcond && !(as -> skipmacro))
	{
		as -> skipcount++;
		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++;
		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++;
		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++;
		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++;
		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;
		// parse options; only one "bss"
		if (opts && as -> passnum == 1)
		{
			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);
}

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;
	}
	
	lwasm_register_symbol(as, l, l -> sym, 0, SYMBOL_EXTERN);
}

OPFUNC(pseudo_export)
{
	lwasm_symbol_ent_t *se;
	export_list_t *ex;
	char *sym2, *sym3;

	if (as -> outformat != OUTPUT_OBJ)
	{
		register_error(as, l, 1, "Symbol exports only supported for obj target");
		return;
	}

	if (as -> passnum == 1)
		return;

	if (!(l -> sym))
	{
		// look for symbol after op
		if (**p)
		{
			for (sym2 = *p; **p && !isspace(**p); (*p)++)
				/* do nothing */
			sym3 = lwasm_alloc(*p - sym2 + 1);
			memcpy(sym3, sym2, *p - sym2);
			sym3[*p - sym2] = '\0';
			
			l -> sym = 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)
	{
		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);
}