diff src/insn_indexed.c @ 101:f59c0916753d

Fixed relative branches and PCR addressing to handle constant intra-section references properly
author lost
date Fri, 23 Jan 2009 03:36:27 +0000
parents e12edcfbebd5
children
line wrap: on
line diff
--- a/src/insn_indexed.c	Sat Jan 17 07:35:18 2009 +0000
+++ b/src/insn_indexed.c	Fri Jan 23 03:36:27 2009 +0000
@@ -67,6 +67,7 @@
 	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;
@@ -156,12 +157,12 @@
 
 	if (**p == '<')
 	{
-		f8 = 1;
+		fs8 = 1;
 		(*p)++;
 	}
 	else if (**p == '>')
 	{
-		f16 = 1;
+		fs16 = 1;
 		(*p)++;
 	}
 
@@ -176,13 +177,6 @@
 	{
 		return;
 	}
-	if (f8 && r != 0)
-	{
-		register_error(as, l, 2, "Illegal external or inter-section reference");
-		r = 0;
-		v = 0;
-	}
-
 	// now look for a comma; if not present, explode
 	if (*(*p)++ != ',')
 	{
@@ -264,8 +258,36 @@
 	// PCR? then we have PC relative addressing (like B??, LB??)
 	if (rn == 5)
 	{
-		// FIXME: handle external references sensibly
-		v -= as -> addr;
+		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
@@ -276,22 +298,71 @@
 		// (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;
-			v -= 2;
+			if (as -> passnum == 1)
+				v -= 2;
 			if (v < -128 || v > 127)
 				register_error(as, l, 2, "Byte overflow");
 			*b2 = v & 0xff;
-			return;
+			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;
-		v -= 3;
+		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