diff lwasm/insn_rel.c @ 377:67373a053c49

Add ?rts target for branch instructions Add a ?rts target for branch instructions, which brances to the nearest RTS or inverts the branch logic to branch around a generated RTS. Activated by a pragma "qrts". Thanks to Erik G <erik@6809.org> for the patch.
author William Astle <lost@l-w.ca>
date Mon, 13 Jul 2015 20:50:02 -0600
parents 35d4213e6657
children 0af33282b518
line wrap: on
line diff
--- a/lwasm/insn_rel.c	Mon Jul 13 20:47:30 2015 -0600
+++ b/lwasm/insn_rel.c	Mon Jul 13 20:50:02 2015 -0600
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <lw_expr.h>
 
@@ -47,7 +48,7 @@
 */
 PARSEFUNC(insn_parse_relgen)
 {
-	lw_expr_t t, e1, e2;
+	lw_expr_t t = NULL, e1, e2;
 	
 	l -> lint = -1;
 	l -> maxlen = OPLEN(instab[l -> insn].ops[3]) + 2;
@@ -76,7 +77,69 @@
 	if (**p == '#')
 		(*p)++;
 
-	t = lwasm_parse_expr(as, p);
+	if (CURPRAGMA(l, PRAGMA_QRTS))
+	{
+		// handle ?RTS conditional return
+		if (**p == '?')
+		{
+			if (strncasecmp(*p, "?RTS", 4) == 0)
+			{
+				(*p) += 4;
+
+				line_t *cl = l;
+				for (cl = cl->prev; cl; cl = cl->prev)
+				{
+					if (cl->insn == -1)
+						continue;
+
+					if (l->addr->value - cl->addr->value > 128)
+					{
+						cl = NULL;
+						break;
+					}
+
+					if (cl->conditional_return)
+						break;
+
+					if (instab[cl->insn].ops[0] == 0x39)
+						break;
+				}
+
+				if (cl)
+				{
+					l->lint = -1;
+					if (cl->conditional_return)
+					{
+						e2 = lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, cl);
+						e1 = lw_expr_build(lw_expr_type_int, 2);
+						t = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, e1, e2);
+					}
+					else
+					{
+						t = lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, cl);
+					}
+				}
+				else
+				{
+					l->conditional_return = 1;
+
+					// t = * + 1
+
+					e2 = lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, l);
+					e1 = lw_expr_build(lw_expr_type_int, 1);
+					t = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, e1, e2);
+
+					lw_expr_destroy(e1);
+					lw_expr_destroy(e2);
+				}
+			}
+		}
+	}
+	
+	if (!t)
+	{
+		t = lwasm_parse_expr(as, p);
+	}
 
 	if (!t)
 	{
@@ -88,6 +151,7 @@
 	if (l -> lint == 8)
 	{
 		l -> len = OPLEN(instab[l -> insn].ops[2]) + 1;
+		if (l->conditional_return) l->len++;
 	}
 	else if (l -> lint == 16)
 	{
@@ -233,10 +297,19 @@
 			return;
 		}
 
-		lwasm_emitop(l, instab[l -> insn].ops[2]);
-		lwasm_emit(l, offs);
-
-		l -> cycle_adj = 2;
+		if (l->conditional_return)
+		{
+			lwasm_emitop(l, instab[l->insn].ops[2] ^ 1);	/* flip branch, add RTS */
+			lwasm_emit(l, 1);
+			lwasm_emit(l, 0x39);
+			l->cycle_adj = 3;
+		}
+		else
+		{
+			lwasm_emitop(l, instab[l->insn].ops[2]);
+			lwasm_emit(l, offs);
+			l->cycle_adj = 2;
+		}
 	}
 	else
 	{