view src/insn_indexed.c @ 67:d5fe306f1ab1

Fixed numerous bugs in macro handling
author lost
date Mon, 05 Jan 2009 05:40:33 +0000
parents 538e15927776
children b6b1e79cc277
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;
	int f8 = 0, f16 = 0;
	int rn;
	int indir = 0;
	int rval;
	int f0 = 0;
 	const char *p2;
 	lwasm_expr_stack_t *s;
 	
	*b1 = *b2 = *b3 = -1;
	
	for (i = 0; i < 24; i++)
	{
		if (*((*p) + i) && !isspace(*((*p) + i)))
			stbuf[i] = *((*p) + i);
		else
			break;
	}
	stbuf[i] = '\0';
	if (!*((*p) + i) || isspace(*((*p) + i)))
	{
		int j;
		// 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 we have the hard ones to work out here
	
	// if we've previously forced the sizes, make a note of it
	if (l -> fsize == 1)
		f8 = 1;
	else if (l -> fsize == 2)
		f16 = 1;
	
	if (**p == '[')
	{
		indir = 1;
		(*p)++;
	}
	
	rn = 0;
	for (i = 0; (*p)[i] && !isspace((*p)[i]); i++)
	{
		if ((*p)[i] == ',')
		{
			rn = 1;
			break;
		}
	}
		
	if (!rn && indir)
	{
		// extended indir
		*b1 = 0x9f;
		*b2 = 0;
		*b3 = 0;
		s = lwasm_evaluate_expr(as, l, *p, &p2);
		if (!s)
		{
			register_error(as, l, 1, "Bad expression");
			return;
		}
		*p = p2;
		if (**p != ']')
		{
			register_error(as, l, 1, "Bad operand");
			return;
		}
		(*p)++;
		if (!lwasm_expr_is_constant(s))
		{
			register_error(as, l, 2, "Incomplete reference");
		}
		rn = lwasm_expr_get_value(s);
		lwasm_expr_stack_free(s);
		*b2 = (rn >> 8) & 0xff;
		*b3 = rn & 0xff;
		return;
	}

	if (**p == '<')
	{
		f8 = 1;
		(*p)++;
	}
	else if (**p == '>')
	{
		f16 = 1;
		(*p)++;
	}

	if (**p == '0' && *(*p+1) == ',')
	{
		f0 = 1;
	}
	
	// now we have to evaluate the expression
	s = lwasm_evaluate_expr(as, l, *p, &p2);
	*p = p2;
	if (!s)
	{
		register_error(as, l, 1, "Bad expression");
		return;
	}
	if (!lwasm_expr_is_constant(s))
	{
		register_error(as, l, 2, "Incomplete reference");
		if (!f8 && !f0)
		{
			f16 = 1;
			l -> fsize = 2;
		}
	}
	rval = lwasm_expr_get_value(s);
	lwasm_expr_stack_free(s);
	
	// now look for a comma; if not present, explode
	if (*(*p)++ != ',')
	{
		// syntax error; force 0 bit
		*b1 = 00;
		return;
	}
	
	// now get the register
	rn = lwasm_lookupreg3(regs, p);
	if (rn < 0)
		goto reterr;

	if (indir)
	{
		if (**p != ']')
			goto reterr;
		else
			(*p)++;
	}
	
	// nnnn,W is only 16 bit
	if (rn == 4)
	{
		if (f8)
			goto reterr;
		if (!f16 && !f0 && !(as -> pragmas & PRAGMA_NOINDEX0TONONE) && rval == 0)
		{
			if (indir)
				*b1 = 0x90;
			else
				*b1 = 0x8f;
			return;
		}
		if (indir)
			*b1 = 0xb0;
		else
			*b1 = 0xcf;
		*b2 = (rval >> 8) & 0xff;
		*b3 = rval & 0xff;
		return;
	}
	
	if (indir) indir = 0x10;
	
	// PCR? redo v1, v2 relative to current address
	if (rn == 5)
	{
		rval -= 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 && rval >= -125 && rval <= 130))
		{
			*b1 = indir | 0x8C;
			rval -= 2;
			if (rval < -128 || rval > 127)
				register_error(as, l, 2, "Byte overflow");
			*b2 = rval & 0xff;
			return;
		}
		
		// anything else is 16 bit offset
		// need 16 bit
		*b1 = indir | 0x8D;
		rval -= 3;
		*b2 = (rval >> 8) & 0xff;
		*b3 = rval & 0xff;
		return;
	}

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

	// we only have to deal with x,y,u,s here
	if (!f8 && !f16 && rval >= -16 && rval <= 15)
	{
		if (rval == 0 && !f0 && !(as -> pragmas & PRAGMA_NOINDEX0TONONE))
		{
			*b1 = rn << 5 | indir | 0x80 | 0x04;
			return;
		}
		// we pick the smallest value here
		if (indir)
		{
			f8 = 1;
			goto no5bit;
		}
		*b1 = rn << 5 | (rval & 0x1F);
		return;
	}
no5bit:
	if (f16 || (!f8 && (rval < -128 || rval > 127)))
	{
		// must be a 16 bit offset here
		*b1 = rn << 5 | indir | 0x80 | 0x09;
		*b2 = (rval >> 8) & 0xff;
		*b3 = rval & 0xff;
		return;
	}
	
	// if we're here, we have an 8 bit offset
	*b1 = rn << 5 | indir | 0x80 | 0x08;
	if (rval < -128 || rval > 127)
		register_error(as, l, 2, "Byte overflow");
	*b2 = rval & 0xff;
	return;
reterr:
	*b1 = 0;
	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);
}