view src/insn_indexed.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 f59c0916753d
children
line wrap: on
line source

/*
insn_indexed.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/>.
*/

/*
for handling indexed mode instructions
*/

#define __insn_indexed_c_seen__

#include <ctype.h>
#include <string.h>

#include "lwasm.h"
#include "instab.h"
#include "expr.h"

void insn_indexed_aux(asmstate_t *as, lwasm_line_t *l, const char **p, int *b1, int *b2, int *b3)
{
	static const char *regs = "X  Y  U  S  W  PCRPC ";
	static const struct { char *opstr; int pb; } simpleindex[] =
	{
		{",x", 0x84},		{",y", 0xa4},		{",u", 0xc4},		{",s", 0xe4},
		{",x+", 0x80},		{",y+", 0xa0},		{",u+", 0xc0},		{",s+", 0xe0},
		{",x++", 0x81},		{",y++", 0xa1},		{",u++", 0xc1},		{",s++", 0xe1},
		{",-x", 0x82},		{",-y", 0xa2},		{",-u", 0xc2},		{",-s", 0xe2},
		{",--x", 0x83},		{",--y", 0xa3},		{",--u", 0xc3},		{",--s", 0xe3},
		{"a,x", 0x86},		{"a,y", 0xa6},		{"a,u", 0xc6},		{"a,s", 0xe6},
		{"b,x", 0x85},		{"b,y", 0xa5},		{"b,u", 0xc5},		{"b,s", 0xe5},
		{"e,x", 0x87},		{"e,y", 0xa7},		{"e,u", 0xc7},		{"e,s", 0xe7},
		{"f,x",	0x8a},		{"f,y",	0xaa},		{"f,u", 0xca},		{"f,s", 0xea},
		{"d,x", 0x8b},		{"d,y", 0xab},		{"d,u", 0xcb},		{"d,s", 0xed},
		{"w,x", 0x8e},		{"w,y", 0xae},		{"w,u", 0xce},		{"w,s", 0xee},
		{",w", 0x8f},							{",w++", 0xcf},		{",--w", 0xef},
		
		{"[,x]", 0x94},		{"[,y]", 0xb4},		{"[,u", 0xd4},		{"[,s]", 0xf4},
		{"[,x++]", 0x91},	{"[,y++]", 0xb1},	{"[,u++]", 0xd1},	{"[,s++]", 0xf1},
		{"[,--x]", 0x93},	{"[,--y]", 0xb3},	{"[,--u]", 0xd3},	{"[,--s]", 0xf3},
		{"[a,x]", 0x96},	{"[a,y]", 0xb6},	{"[a,u]", 0xd6},	{"[a,s]", 0xf6},
		{"[b,x]", 0x95},	{"[b,y]", 0xb5},	{"[b,u]", 0xd5},	{"[b,s]", 0xf5},
		{"[e,x]", 0x97},	{"[e,y]", 0xb7},	{"[e,u]", 0xd7},	{"[e,s]", 0xf7},
		{"[f,x]", 0x9a},	{"[f,y]", 0xba},	{"[f,u]", 0xda},	{"[f,s]", 0xfa},
		{"[d,x]", 0x9b},	{"[d,y]", 0xbb},	{"[d,u]", 0xdb},	{"[d,s]", 0xfd},
		{"[w,x]", 0x9e},	{"[w,y]", 0xbe},	{"[w,u]", 0xde},	{"[w,s]", 0xfe},
		{"[,w]", 0x90},							{"[,w++]", 0xd0},	{"[,--w]", 0xf0},
		
		{ "", -1 }
	};
	char stbuf[25];
	int i, j, rn;
	int f8 = 0, f16 = 0, f0 = 0;
	int r, v;
	int indir = 0;
	int fs8 = 0, fs16 = 0;
	
	// initialize output bytes
	*b1 = *b2 = *b3 = -1;
	
	// fetch out operand for lookup
	for (i = 0; i < 24; i++)
	{
		if (*((*p) + i) && !isspace(*((*p) + i)))
			stbuf[i] = *((*p) + i);
		else
			break;
	}
	stbuf[i] = '\0';
	
	// now look up operand in "simple" table
	if (!*((*p) + i) || isspace(*((*p) + i)))
	{
		// do simple lookup
		for (j = 0; simpleindex[j].opstr[0]; j++)
		{
			if (!strcasecmp(stbuf, simpleindex[j].opstr))
				break;
		}
		if (simpleindex[j].opstr[0])
		{
			*b1 = simpleindex[j].pb;
			(*p) += i;
			return;
		}
	}

	// now do the "hard" ones

	// is it indirect?
	if (**p == '[')
	{
		indir = 1;
		(*p)++;
	}
	
	// look for a "," - all indexed modes have a "," except extended indir
	rn = 0;
	for (i = 0; (*p)[i] && !isspace((*p)[i]); i++)
	{
		if ((*p)[i] == ',')
		{
			rn = 1;
			break;
		}
	}

	// if no "," and indirect, do extended indir
	if (!rn && indir)
	{
		// extended indir
		*b1 = 0x9f;
		*b2 = 0;
		*b3 = 0;
		r = lwasm_expr_result2(as, l, (char **)p, 0, &v, 0);
		if (r < 0)
		{
			return;
		}
		if (**p != ']')
		{
			register_error(as, l, 1, "Bad operand");
			return;
		}
		
		(*p)++;
		
		if (r == 1 && as -> passnum == 2)
		{
			l -> relocoff = as -> addr - l -> codeaddr + 1;
		}
		
		*b2 = (v >> 8) & 0xff;
		*b3 = v & 0xff;
		return;
	}

	// if we've previously forced the offset size, make a note of it
	if (l -> fsize == 1)
		f8 = 1;
	else if (l -> fsize == 2)
		f16 = 1;

	if (**p == '<')
	{
		fs8 = 1;
		(*p)++;
	}
	else if (**p == '>')
	{
		fs16 = 1;
		(*p)++;
	}

	if (**p == '0' && *(*p+1) == ',')
	{
		f0 = 1;
	}
	
	// now we have to evaluate the expression
	r = lwasm_expr_result2(as, l, (char **)p, 0, &v, 0);
	if (r < 0)
	{
		return;
	}
	// now look for a comma; if not present, explode
	if (*(*p)++ != ',')
	{
		// syntax error; force 0 bit
		*b1 = 00;
		l -> fsize = 0;
		return;
	}
	
	// now get the register
	rn = lwasm_lookupreg3(regs, p);
	if (rn < 0)
	{
		*b1 = 0;
		l -> fsize = 0;
		register_error(as, l, 1, "Bad register");
		return;
	}
	
	if (indir)
	{
		if (**p != ']')
		{
			register_error(as, l, 1, "Bad operand");
			l -> fsize = 0;
			*b1 = 0;
			return;
		}
		else
			(*p)++;
	}
	
	// incomplete reference on pass 1 forces 16 bit
	if (r == 1 && as -> passnum == 1)
	{
		f16 = 1;
		l -> fsize = 2;
	}
	
	// incomplete reference on pass 2 needs relocoff set
	if (r == 1 && as -> passnum == 2)
	{
		l -> relocoff = as -> addr - l -> codeaddr + 1;
	}

	// nnnn,W is only 16 bit (or 0 bit)
	if (rn == 4)
	{
		if (f8)
		{
			register_error(as, l, 1, "n,W cannot be 8 bit");
			l -> fsize = 0;
			*b1 = 0;
			return;
		}
		// note: set f16 above for incomplete references
		// also set reloc offset
		if (!f16 && !f0 && !(as -> pragmas & PRAGMA_NOINDEX0TONONE) && v == 0)
		{
			if (indir)
				*b1 = 0x90;
			else
				*b1 = 0x8f;
			return;
		}
		
		if (indir)
			*b1 = 0xb0;
		else
			*b1 = 0xcf;
		*b2 = (v >> 8) & 0xff;
		*b3 = v & 0xff;
		return;
	}
	
	// set indir to correct bit value
	if (indir) indir = 0x10;
	
	// PCR? then we have PC relative addressing (like B??, LB??)
	if (rn == 5)
	{
		lwasm_expr_term_t *t;
		// external references are handled exactly the same as for
		// relative addressing modes
		// on pass 1, adjust the expression for a subtraction of the
		// current address
		
		// need to re-evaluate the expression with "SECTCONST"...
		r = lwasm_expr_result2(as, l, (char **)p, EXPR_SECTCONST | EXPR_REEVAL, &v, 0);
		if (r != 0)
			v = 0;
		if (as -> passnum == 1)
		{
			l -> fsize = 0;
		}
		f8 = f16 = 0;
		if (r == 1 && as -> passnum == 1 && !fs8)
		{
			l -> fsize = 2;	
			f16 = 1;
		}
		if (fs8)
			f8 = 1;
		if (fs16)
			f16 = 1;
		if (l -> fsize == 2)
			f16 = 1;
		else if (l -> fsize == 1)
			f8 = 1;
		if (as -> passnum == 1)
			v -= as -> addr;
		
		// we have a slight problem here
		// PCR based on current insn loc is really
		// -125 <= offset <= +130 (8 bit offset)
		// NOTE: when we are called, we already have the opcode emitted
		// so we only need to worry about the size of the operand
		// hence the 2 and 3 magic numbers below instead of 3 and 4
		// (and that also avoids errors with two byte opcodes, etc)
		if (f8 || (!f16 && v >= -125 && v <= 130))
		{
			f8 = 1;
			l -> fsize = 1;
			*b1 = indir | 0x8C;
			if (as -> passnum == 1)
				v -= 2;
			if (v < -128 || v > 127)
				register_error(as, l, 2, "Byte overflow");
			*b2 = v & 0xff;
			if (r != 0 && as -> passnum == 2)
			{
				register_error(as, l, 2, "Illegal incomplete reference");
			}
			goto finpcr;
		}
		
		// anything else is 16 bit offset
		// need 16 bit
		
		l -> fsize = 2;
		*b1 = indir | 0x8D;
		if (as -> passnum == 1)
			v -= 3;
		*b2 = (v >> 8) & 0xff;
		*b3 = v & 0xff;
		if (as -> passnum == 2 && r == 1)
		{
			t = lwasm_expr_term_create_secbase();
			lwasm_expr_stack_push(l -> exprs[0], t);
			lwasm_expr_term_free(t);
			t = lwasm_expr_term_create_oper(LWASM_OPER_MINUS);
			lwasm_expr_stack_push(l -> exprs[0], t);
			lwasm_expr_term_free(t);
		}

	finpcr:
		if (as -> passnum == 1)
		{
			// need to adjust the expression
			if (l -> exprs[0])
			{
				t = lwasm_expr_term_create_int(as -> addr + (f8 ? 2 : 3));
				lwasm_expr_stack_push(l -> exprs[0], t);
				lwasm_expr_term_free(t);
				t = lwasm_expr_term_create_oper(LWASM_OPER_MINUS);
				lwasm_expr_stack_push(l -> exprs[0], t);
				lwasm_expr_term_free(t);
			}
			else
			{
				l -> exprvals[0] -= as -> addr + (f8 ? 2 : 3);
			}
		}
		return;
	}
	if (fs16)
		f16 = 1;
	if (fs8)
		f8 = 1;

	if (f8 && r != 0)
	{
		register_error(as, l, 2, "Illegal external or inter-section reference");
		r = 0;
		v = 0;
	}

	// constant offset from PC (using PC as regular register :) )
	// FIXME: handle external references intelligently
	if (rn == 6)
	{
		if (f8 || (!f16 && v >= -128 && v <= 127))
		{
			*b1 = indir | 0x8C;
			if (v < -128 || v > 127)
				register_error(as, l, 2, "Byte overflow");
			*b2 = v & 0xff;
			return;
		}
		
		// everything else must be 16 bit
		// need 16 bit
		*b1 = indir | 0x8D;
		*b2 = (v >> 8) & 0xff;
		*b3 = v & 0xff;
		return;
	}

	// we only have to deal with x,y,u,s here
	if (!f8 && !f16 && v >= -16 && v <= 15)
	{
		// zero offset going to ,R?
		if (v == 0 && !f0 && !(as -> pragmas & PRAGMA_NOINDEX0TONONE))
		{
			*b1 = rn << 5 | indir | 0x80 | 0x04;
			return;
		}
		
		// no 5 bit on indirect
		if (indir)
		{
			f8 = 1;
			l -> fsize = 1;
			goto no5bit;
		}
		
		// 5 bit addressing
		*b1 = rn << 5 | (v & 0x1F);
		return;
	}

no5bit:
	if (f16 || (!f8 && (v < -128 || v > 127)))
	{
		// must be a 16 bit offset here
		*b1 = rn << 5 | indir | 0x80 | 0x09;
		*b2 = (v >> 8) & 0xff;
		*b3 = v & 0xff;
		return;
	}
	
	// if we're here, we have an 8 bit offset
	// note: cannot get here if incomplete reference detected above
	*b1 = rn << 5 | indir | 0x80 | 0x08;
	if (v < -128 || v > 127)
		register_error(as, l, 2, "Byte overflow");
	*b2 = v & 0xff;
	return;
}

OPFUNC(insn_indexed)
{
	int b1, b2, b3;
	
	lwasm_emitop(as, l, instab[opnum].ops[0]);

	insn_indexed_aux(as, l, (const char **)p, &b1, &b2, &b3);
	if (b1 != -1)
		lwasm_emit(as, l, b1);
	if (b2 != -1)
		lwasm_emit(as, l, b2);
	if (b3 != -1)
		lwasm_emit(as, l, b3);
}