commit f4984e3852c510e8d3705d8adfe8e686b674cd74
parent b0e4f19592eaefccc9f735982be5796f9b58570b
Author: Brian Swetland <swetland@frotz.net>
Date:   Thu,  5 Apr 2012 18:42:47 -0700
emulator: fix incorrect handling of IFx instructions
- operand decoder is side-effecty, so skipped instructions
  that modified SP still modified SP
- ditch the "skip" register and instead add a simplified
  instruction decoder for skipping instructions
- tidy up some other details of the main emulation path
- grumble about non-uniformity of the instruction encoding
  (a must be decoded before b, but they're different fields
  in the two different instruction formats)
Diffstat:
| M | emulator.c |  |  | 85 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- | 
1 file changed, 42 insertions(+), 43 deletions(-)
diff --git a/emulator.c b/emulator.c
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <ctype.h>
 
+typedef uint8_t u8;
 typedef uint16_t u16;
 typedef uint32_t u32;
 
@@ -47,7 +48,7 @@ struct dcpu {
 	u16 pc;
 	u16 sp;
 	u16 ov;
-	u16 skip;
+	u16 unused;
 	u16 m[65536];
 };
 
@@ -90,28 +91,26 @@ u16 *dcpu_opr(struct dcpu *d, u16 code) {
 	}
 }
 
+static u8 skiptable[32] = { /* operand forms that advance pc */
+	[0x10] = 1, [0x11] = 1, [0x12] = 1, [0x13] = 1,
+	[0x14] = 1, [0x15] = 1, [0x16] = 1, [0x17] = 1,
+	[0x1E] = 1, [0x1F] = 1,
+};
+
+void dcpu_skip(struct dcpu *d) {
+	u16 op = d->m[d->pc++];
+	d->pc += skiptable[op >> 10];
+	if ((op & 15) == 0)
+		d->pc += skiptable[(op >> 4) & 31];	
+}
+
 void dcpu_step(struct dcpu *d) {
 	u16 op = d->m[d->pc++];
 	u16 dst;
 	u32 res;
 	u16 a, b, *aa;
 
-	if ((op & 0xF) == 0) {
-		switch ((op >> 4) & 0x3F) {
-		case 0x01:
-			a = *dcpu_opr(d, op >> 10);
-			if (d->skip) {
-				d->skip = 0;
-			} else {
-				d->m[--(d->sp)] = d->pc;
-				d->pc = a;
-			}
-			return;
-		default:
-			fprintf(stderr, "< ILLEGAL OPCODE >\n");
-			exit(0);
-		}
-	}
+	if ((op & 0xF) == 0) goto extended;
 
 	aa = dcpu_opr(d, dst = (op >> 4) & 0x3F);
 	a = *aa;
@@ -119,50 +118,50 @@ void dcpu_step(struct dcpu *d) {
 
 	switch (op & 0xF) {
 	case 0x1: res = b; break;
-	case 0x2: res = a + b; break;	
-	case 0x3: res = a - b; break;
-	case 0x4: res = a * b; break;
-	case 0x5: if (b) { res = a / b; } else { res = 0; } break;
+	case 0x2: res = a + b; d->ov = res >> 16; break;	
+	case 0x3: res = a - b; d->ov = res >> 16; break;
+	case 0x4: res = a * b; d->ov = res >> 16; break;
+	case 0x5: if (b) { res = a / b; } else { res = 0; } d->ov = res >> 16; break;
 	case 0x6: if (b) { res = a % b; } else { res = 0; } break;
-	case 0x7: res = a << b; break;
-	case 0x8: res = a >> b; break;
+	case 0x7: res = a << b; d->ov = res >> 16; break;
+	case 0x8: res = a >> b; d->ov = res >> 16; break;
 	case 0x9: res = a & b; break;
 	case 0xA: res = a | b; break;
 	case 0xB: res = a ^ b; break;
-	case 0xC: res = (a==b); break;
-	case 0xD: res = (a!=b); break;
-	case 0xE: res = (a>b); break;
-	case 0xF: res = ((a&b)!=0); break;
+	case 0xC: if (a!=b) dcpu_skip(d); return;
+	case 0xD: if (a==b) dcpu_skip(d); return;
+	case 0xE: if (a<=b) dcpu_skip(d); return;
+	case 0xF: if ((a&b)==0) dcpu_skip(d); return;
 	}
 
-	if (d->skip) {
-		d->skip = 0;
-		return;
-	}
+	if (dst < 0x1f) *aa = res;
+	return;
 
-	switch (op & 0xF) {
-	case 0x2: case 0x3: case 0x4: case 0x5: case 0x7: case 0x8:
-		d->ov = res >> 16;
-	case 0x1: case 0x6: case 0x9: case 0xA: case 0xB:
-		if (dst < 0x1f) *aa = res;
-		break;
-	case 0xC: case 0xD: case 0xE: case 0xF:
-		d->skip = !res;
+extended:
+	a = *dcpu_opr(d, op >> 10);
+	switch ((op >> 4) & 0x3F) {
+	case 0x01:
+		d->m[--(d->sp)] = d->pc;
+		d->pc = a;
+		return;
+	default:
+		fprintf(stderr, "< ILLEGAL OPCODE >\n");
+		exit(0);
 	}
 }
 
 void dumpheader(void) {
 	fprintf(stderr,
-		"PC   SP   OV   SKIP A    B    C    X    Y    Z    I    J    Instruction\n"
-		"---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -----------\n");
+		"PC   SP   OV   A    B    C    X    Y    Z    I    J    Instruction\n"
+		"---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -----------\n");
 }
 
 void dumpstate(struct dcpu *d) {
 	char out[128];
 	disassemble(d->m + d->pc, out);
 	fprintf(stderr,
-		"%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %s\n",
-		d->pc, d->sp, d->ov, d->skip,
+		"%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %s\n",
+		d->pc, d->sp, d->ov,
 		d->r[0], d->r[1], d->r[2], d->r[3],
 		d->r[4], d->r[5], d->r[6], d->r[7],
 		out);