shithub: riscv

Download patch

ref: 6f80913ac72490cca7645b85b64de5ed52a7ce91
parent: 1ecdf09aeeb16dd359251e3e698c8ea7798bd285
author: aiju <devnull@localhost>
date: Wed Jan 22 08:09:09 EST 2020

add v8e

--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -48,6 +48,7 @@
 	sokoban\
 	sudoku\
 	timmy\
+	v8e\
 
 4s.$O 5s.$O xs.$O:	xs.h
 $O.4s $O.5s: xs.$O
--- /dev/null
+++ b/sys/src/games/v8e/cpu.c
@@ -1,0 +1,1166 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+u32int r[16];
+u32int curpc;
+u32int ps;
+int trace;
+
+enum {
+	ADD,
+	SUB,
+	MUL,
+	DIV,
+	CMP,
+	TST,
+	BIC,
+	BIS,
+	XOR,
+	BIT,
+};
+
+#define fetch8() memread8(r[15]++)
+
+static u16int
+fetch16(void)
+{
+	u16int v;
+	v = memread16(r[15]);
+	r[15] += 2;
+	return v;
+}
+
+static u32int
+fetch32(void)
+{
+	u32int v;
+	v = memread32(r[15]);
+	r[15] += 4;
+	return v;
+}
+
+static u32int
+sxt(u32int v, int s)
+{
+	switch(s){
+	case 0: return (s8int) v;
+	case 1: return (s16int) v;
+	default: return v;
+	}
+}
+
+static void
+nz(u32int v, int s)
+{
+	int i;
+
+	i = sxt(v, s);
+	ps &= ~(FLAGN|FLAGZ);
+	if(i == 0) ps |= FLAGZ;
+	if(i < 0) ps |= FLAGN;
+}
+
+static void
+nz64(u64int v, int s)
+{
+	if(s < 3)
+		nz(v, s);
+	else{
+		if(v == 0) ps |= FLAGZ;
+		else if((s64int)v < 0) ps |= FLAGN;
+	}
+}
+
+static void
+nzv(u32int v, int s)
+{
+	nz(v, s);
+	ps &= ~FLAGV;
+}
+
+u32int
+addrof(vlong v)
+{
+	if(v < 0) sysfatal("addr of register or literal (pc=%.8ux)", curpc);
+	return v;
+}
+
+vlong
+amode(int s)
+{
+	u8int c;
+	u32int v;
+	
+	s &= 0xf;
+	c = fetch8();
+	switch(c >> 4){
+	case 0: case 1: case 2: case 3: return ~(vlong)(64 | c & 63);
+	case 4: v = addrof(amode(s)); v += r[c & 15] << s; return v;
+	case 5: return ~(vlong)(c & 15);
+	case 6: return r[c & 15];
+	case 7: return r[c & 15] -= 1<<s;
+	case 8: v = r[c & 15]; r[c & 15] += 1<<s; return v;
+	case 9: v = r[c & 15]; r[c & 15] += 4; return memread32(v);
+	case 10: v = fetch8(); return (u32int)(r[c & 15] + (s8int) v);
+	case 11: v = fetch8(); return memread32(r[c & 15] + (s8int) v);
+	case 12: v = fetch16(); return (u32int)(r[c & 15] + (s16int) v);
+	case 13: v = fetch16(); return memread32(r[c & 15] + (s16int) v);
+	case 14: v = fetch32(); return (u32int)(r[c & 15] + v);
+	case 15: v = fetch32(); return memread32(r[c & 15] + v);
+	default: sysfatal("unimplemented addressing mode %.2x", c); return -1;
+	}
+}
+
+u32int
+readm(vlong a, int s)
+{
+	vlong v;
+
+	if(a < 0){
+		if(a <= ~64LL){
+			if(s >= 0x10)
+				return (~a & 63) << 4 | 0x4000;
+			return ~a & 63;
+		}
+		assert(a >= ~15LL);
+		v = r[~a];
+		switch(s & 0xf){
+		case 0: return (uchar) v;
+		case 1: return (ushort) v;
+		case 2: return v;
+		}
+	}
+	switch(s & 0xf){
+	case 0: return memread8(a);
+	case 1: return memread16(a);
+	case 2: return memread32(a);
+	default: sysfatal("readm: unimplemented size %d (a=%.llx, pc=%.8x)", s, a, curpc); return -1;
+	}
+}
+
+static vlong
+highw(vlong v)
+{
+	if(v >= 0)
+		return (u32int)(v + 4);
+	if(v <= ~64LL)
+		return ~64LL;
+	return v - 1 | ~15LL;
+}
+
+u64int
+readm64(vlong a, int s)
+{
+	u64int v;
+	
+	if((s & 0xf) == 3){
+		v = readm(a, s - 1);
+		if(a > ~64LL)
+			v |= (u64int)readm(highw(a), 2) << 32;
+		return v;
+	}
+	return readm(a, s);
+}
+
+void
+writem(vlong a, u32int v, int s)
+{
+	int n;
+
+	assert(a >= ~15LL);
+	s &= 0xf;
+	if(a < 0){
+		switch(s){
+		case 0: r[~a] = r[~a] & ~0xff | v & 0xff; break;
+		case 1: r[~a] = r[~a] & ~0xffff | v & 0xffff; break;
+		case 2: r[~a] = v; break;
+		default: sysfatal("writem: unimplemented size %d", s);
+		}
+		return;
+	}
+	switch(s){
+	case 0:
+		n = (a & 3) << 3;
+		memwrite(a & -4, v << n, 0xff << n);
+		break;
+	case 1:
+		n = (a & 3) << 3;
+		memwrite(a & -4, v << n, 0xffff << n);
+		if(n == 24) memwrite(-(-a & -4), v >> 8, 0xff);
+		break;
+	case 2:
+		n = (a & 3) << 3;
+		memwrite(a & -4, v << n, -1 << n);
+		if(n != 0) memwrite(-(-a & -4), v >> 32 - n, (u32int)-1 >> 32 - n);
+		break;
+	default: sysfatal("writem: unimplemented size %d", s);
+	}
+}
+
+void
+writem64(vlong a, u64int v, int s)
+{
+	if((s & 0xf) == 3){
+		writem(a, v, 2);
+		writem(highw(a), v >> 32, 2);
+	}else
+		writem(a, v, s);
+}
+
+static u32int
+add(u32int a, u32int b, int s)
+{
+	int v;
+
+	ps &= ~(FLAGC|FLAGV);
+	switch(s){
+	case 0:
+		v = (u8int)a + (u8int)b;
+		if(v >= 0x100) ps |= FLAGC;
+		if(((a ^ ~b) & (v ^ a) & 0x80) != 0) ps |= FLAGV;
+		return v;
+	case 1:
+		v = (u16int)a + (u16int)b;
+		if(v >= 0x10000) ps |= FLAGC;
+		if(((a ^ ~b) & (v ^ a) & 0x8000) != 0) ps |= FLAGV;
+		return v;
+	case 2:
+		v = a + b;
+		if((u32int)v < a) ps |= FLAGC;
+		if(((a ^ ~b) & (v ^ a) & 0x80000000) != 0) ps |= FLAGV;
+		return v;
+	default:
+		sysfatal("subtract: unimplemented size %d", s);
+		return 0;
+	}
+}
+
+static void
+adwc(void)
+{
+	vlong ad;
+	u32int a, b, v;
+	u8int c;
+	
+	a = readm(amode(2), 2);
+	ad = amode(2);
+	b = readm(ad, 2);
+	c = ps & FLAGC;
+	ps &= ~15;
+	v = a + b + c;
+	if(v < a || c && v == a) ps |= FLAGC;
+	if(((a ^ ~b) & (v ^ a) & 0x80000000) != 0) ps |= FLAGV;
+	writem(ad, v, 2);
+	nzv(v, 2);
+}
+
+static u32int
+subtract(u32int a, u32int b, int s)
+{
+	int v;
+
+	ps &= ~(FLAGC|FLAGV);
+	switch(s){
+	case 0:
+		v = (u8int)b - (u8int)a;
+		if(v < 0) ps |= FLAGC;
+		if(((a ^ b) & (v ^ a) & 0x80) != 0) ps |= FLAGV;
+		return v;
+	case 1:
+		v = (u16int)b - (u16int)a;
+		if(v < 0) ps |= FLAGC;
+		if(((a ^ b) & (v ^ a) & 0x8000) != 0) ps |= FLAGV;
+		return v;
+	case 2:
+		v = b - a;
+		if((u32int)v > b) ps |= FLAGC;
+		if(((a ^ b) & (v ^ a) & 0x80000000) != 0) ps |= FLAGV;
+		return v;
+	default:
+		sysfatal("subtract: unimplemented size %d", s);
+		return 0;
+	}
+}
+
+static void
+sbwc(void)
+{
+	vlong ad;
+	u32int a, b, v;
+	u8int c;
+	
+	a = readm(amode(2), 2);
+	ad = amode(2);
+	b = readm(ad, 2);
+	c = ps & FLAGC;
+	ps &= ~15;
+	v = a - b - c;
+	if(v > a || c && v == a) ps |= FLAGC;
+	if(((a ^ b) & (v ^ a) & 0x80000000) != 0) ps |= FLAGV;
+	writem(ad, v, 2);
+	nzv(v, 2);
+}
+
+static void
+cmp(u32int a, u32int b, int s)
+{
+	ps &= ~15;
+	switch(s){
+	case 0:
+		if((s8int) a < (s8int) b) ps |= FLAGN;
+		if((u8int) a == (u8int) b) ps |= FLAGZ;
+		if((u8int) a < (u8int) b) ps |= FLAGC;
+		break;
+	case 1:
+		if((s16int) a < (s16int) b) ps |= FLAGN;
+		if((u16int) a == (u16int) b) ps |= FLAGZ;
+		if((u16int) a < (u16int) b) ps |= FLAGC;
+		break;
+	case 2:
+		if((s32int) a < (s32int) b) ps |= FLAGN;
+		if(a == b) ps |= FLAGZ;
+		if(a < b) ps |= FLAGC;
+		break;
+	default:
+		sysfatal("cmp: unimplemented size %d", s);
+	}
+}
+
+static u32int
+mul(u32int a, u32int b, int s)
+{
+	vlong v;
+
+	ps &= ~(FLAGC|FLAGV);
+	switch(s){
+	case 0:
+		v = (s8int) a * (s8int) b;
+		if((uvlong)(v + 0x80) > 0xff) ps |= FLAGV;
+		return v;
+	case 1:
+		v = (s16int) a * (s16int) b;
+		if((uvlong)(v + 0x8000) > 0xffff) ps |= FLAGV;
+		return v;
+	case 2:
+		v = (s32int)a * (s32int) b;
+		if((uvlong)(v + 0x80000000) > 0xffffffff) ps |= FLAGV;
+		return v;
+	default:
+		sysfatal("mul: unimplemented size %d", s);
+		return 0;
+	}
+}
+
+static u32int
+div(u32int a, u32int b, int s)
+{
+	vlong v;
+
+	ps &= ~(FLAGC|FLAGV);
+	switch(s){
+	case 0:
+		if((s8int) a == 0 || (s8int) b == -0x80 && (s8int) a == -1){
+			ps |= FLAGV;
+			return b;
+		}
+		v = (s8int) b / (s8int) a;
+		return v;
+	case 1:
+		if((s16int) b == 0 || (s16int) b == -0x8000 && (s16int) a == -1){
+			ps |= FLAGV;
+			return b;
+		}
+		v = (s16int) b / (s16int) a;
+		return v;
+	case 2:
+		if(b == 0 || (s32int) b == -0x8000 && (s32int) a == -1){
+			ps |= FLAGV;
+			return b;
+		}
+		v = (s32int) b / (s32int) a;
+		return v;
+	default:
+		sysfatal("div: unimplemented size %d", s);
+		return 0;
+	}
+}
+
+static void
+alu(int o, int r, int s)
+{
+	u32int a, b, v;
+	vlong c;
+	
+	switch(r){
+	case 1:
+		c = amode(s);
+		if(o == ADD || o == SUB)
+			a = 1;
+		else
+			a = 0;
+		b = readm(c, s);
+		break;
+	case 2:
+		a = readm(amode(s), s);
+		c = amode(s);
+		b = readm(c, s);
+		break;
+	case 3:
+		a = readm(amode(s), s);
+		b = readm(amode(s), s);
+		c = amode(s);
+		break;
+	case 4:
+		a = readm(amode(s), s);
+		if(o == XOR)
+			b = -1;
+		else
+			b = 0;
+		c = amode(s);
+		break;
+	default:
+		sysfatal("alu: unimplemented %d", r);
+		return;
+	}
+	switch(o){
+	case ADD: v = add(a, b, s); break;
+	case SUB: v = subtract(a, b, s); break;
+	case MUL: v = mul(a, b, s); break;
+	case DIV: v = div(a, b, s); break;
+	case CMP: cmp(a, b, s); return;
+	case TST: cmp(b, 0, s); return;
+	case BIC: v = ~a & b; ps &= ~FLAGV; break;
+	case BIS: v = a | b; ps &= ~FLAGV; break;
+	case XOR: v = a ^ b; ps &= ~FLAGV; break;
+	case BIT: nzv(a & b, s); return;
+	default: sysfatal("unimplemented %d in alu", o); v = 0;
+	}
+	nz(v, s);
+	writem(c, v, s);
+}
+
+static void
+ediv(void)
+{
+	s32int divr;
+	vlong divd;
+	vlong q;
+	s32int r;
+	vlong quo, rem;
+	
+	divr = readm(amode(2), 2);
+	divd = readm64(amode(3), 3);
+	quo = amode(2);
+	rem = amode(2);
+	ps &= ~15;
+	if(divr == 0){
+	nope:
+		writem(quo, divd, 2);
+		writem(rem, 0, 2);
+		nz(divd, 2);
+		return;
+	}
+	q = divd / divr;
+	r = divd % divr;
+	if((uvlong)(q + 0x80000000) > 0xffffffff)
+		goto nope;
+	writem(quo, q, 2);
+	writem(rem, r, 2);
+	nz(q, 2);
+}
+
+static void
+move(int s)
+{
+	u32int v, w;
+	vlong src, dst;
+	
+	src = amode(s);
+	dst = amode(s);
+	if(s != 3){
+		v = readm(src, s);
+		writem(dst, v, s);
+		nzv(v, s);
+	}else{
+		v = readm(src, 2);
+		w = readm(highw(src), 2);
+		writem(dst, v, 2);
+		writem(highw(dst), w, 2);
+		nzv(w, 2);
+		if(v != 0) ps &= ~FLAGZ;
+	}
+}
+
+static void
+cvt(int s, int t)
+{
+	u32int v;
+	
+	v = readm(amode(s), s);
+	v = sxt(v, s);
+	writem(amode(t), v, t);
+	nzv(v, t);
+	switch(t){
+	case 0: if((uvlong)(v + 0x80) > 0xff) ps |= FLAGV; break;
+	case 1: if((uvlong)(v + 0x8000) > 0xffff) ps |= FLAGV; break;
+	}
+	ps &= ~FLAGC;
+}
+
+static void
+movez(int s, int t)
+{
+	u32int v;
+	
+	v = readm(amode(s), s);
+	writem(amode(t), v, t);
+	nzv(v, t);
+}
+
+static void
+movea(int s)
+{
+	vlong v;
+	
+	v = amode(s);
+	if(v < 0) sysfatal("invalid movea (pc=%.8x)", curpc);
+	writem(amode(2), v, 2);
+	nzv(v, 2);
+}
+
+static void
+pusha(int s)
+{
+	vlong v;
+	
+	v = amode(s);
+	if(v < 0) sysfatal("invalid pusha (pc=%.8x)", curpc);
+	writem(r[14] -= 4, v, 2);
+	nzv(v, 2);
+}
+
+static void
+pushl(void)
+{
+	u32int v;
+	
+	v = readm(amode(2), 2);
+	writem(r[14] -= 4, v, 2);
+	nzv(v, 2);
+}
+
+static void
+branch(int s, int c)
+{
+	int off;
+	
+	if(s == 0)
+		off = (s8int) fetch8();
+	else
+		off = (s16int) fetch16();
+	if(c)
+		r[15] += off;
+}
+
+static void
+calls(void)
+{
+	u32int narg, sp;
+	vlong dst;
+	u16int m;
+	int i;
+	
+	narg = readm(amode(2), 2);
+	dst = amode(0);
+	if(dst < 0) sysfatal("call to illegal location pc=%.8ux", curpc);
+	writem(r[14] -= 4, narg, 2);
+	sp = r[14];
+	r[14] &= -4;
+	m = readm(dst, 1);
+	for(i = 12; --i >= 0; )
+		if((m & 1<<i) != 0)
+			writem(r[14] -= 4, r[i], 2);
+	writem(r[14] -= 4, r[15], 2);
+	writem(r[14] -= 4, r[13], 2);
+	writem(r[14] -= 4, r[12], 2);
+	ps &= ~0xf;
+	writem(r[14] -= 4, (sp & 3)<<30|1<<29|(m & 0xfff)<<16|ps&~0x10, 2);
+	ps &= ~0xc0;
+	if((m & 0x8000) != 0) ps |= 0x80;
+	if((m & 0x4000) != 0) ps |= 0x40;
+	writem(r[14] -= 4, 0, 2);
+	r[13] = r[14];
+	r[12] = sp;
+	r[15] = dst + 2;
+}
+
+static void
+ret(void)
+{
+	u32int m;
+	u8int n;
+	int i;
+
+	r[14] = r[13] + 4;
+	m = readm(r[14], 2);
+	r[14] += 4;
+	r[12] = readm(r[14], 2); r[14] += 4;
+	r[13] = readm(r[14], 2); r[14] += 4;
+	r[15] = readm(r[14], 2); r[14] += 4;
+	for(i = 0; i < 12; i++)
+		if((m & 1<<16+i) != 0){
+			r[i] = readm(r[14], 2);
+			r[14] += 4;
+		}
+	r[14] += m >> 30;
+	ps = (u16int) m;
+	if((m & 1<<29) != 0){
+		n = readm(r[14], 2);
+		r[14] += 4 + 4 * n;
+	}
+}
+
+static void
+bbs(int inv, int new)
+{
+	u32int pos;
+	vlong base;
+	s8int displ;
+	u32int val;
+	
+	pos = readm(amode(2), 2);
+	base = amode(0);
+	displ = fetch8();
+	if(base < 0){
+		if(pos >= 32) sysfatal("invalid bbs (pc=%.8ux)", curpc);
+		val = readm(base, 2);
+		if((val >> pos & 1) != inv)
+			r[15] += displ;
+		if(new != 0){
+			if(new > 0) val |= 1<<pos;
+			else val &= ~(1<<pos);
+			writem(base, val, 2);
+		}
+	}else{
+		base += pos >> 3;
+		pos &= 7;
+		val = readm(base, 0);
+		if((val >> pos & 1) != inv)
+			r[15] += displ;
+		if(new != 0){
+			if(new > 0) val |= 1<<pos;
+			else val &= ~(1<<pos);
+			writem(base, val, 0);
+		}
+	}
+}
+
+static void
+ashl(void)
+{
+	s8int cnt;
+	s32int v;
+	
+	cnt = readm(amode(0), 0);
+	v = readm(amode(2), 2);
+	ps &= ~15;
+	if(cnt >= 32){
+		if(v != 0) ps |= FLAGV;
+		v = 0;
+	}else if(cnt >= 0){
+		if(v + (v & 1<<31 >> cnt) != 0)
+			ps |= FLAGV;
+		v <<= cnt;
+	}else if(cnt > -32)
+		v >>= -cnt;
+	else
+		v >>= 31;
+	nz(v, 2);
+	writem(amode(2), v, 2);
+}
+
+static void
+rotl(void)
+{
+	s8int cnt;
+	s32int v;
+	
+	cnt = readm(amode(0), 0);
+	v = readm(amode(2), 2);
+	ps &= ~FLAGV;
+	cnt &= 31;
+	v = v << cnt | v >> 32 - cnt;
+	nz(v, 2);
+	writem(amode(2), v, 2);
+}
+
+static void
+ashq(void)
+{
+	s8int cnt;
+	s64int v;
+	
+	cnt = readm(amode(0), 0);
+	v = readm64(amode(3), 3);
+	ps &= ~15;
+	if(cnt >= 64){
+		if(v != 0) ps |= FLAGV;
+		v = 0;
+	}else if(cnt >= 0){
+		if(v + (v & 1ULL<<63 >> cnt) != 0)
+			ps |= FLAGV;
+		v <<= cnt;
+	}else if(cnt > -64)
+		v >>= -cnt;
+	else
+		v >>= 63;
+	nz64(v, 3);
+	writem64(amode(3), v, 3);
+}
+
+static void
+blb(int val)
+{
+	u32int v;
+	s8int disp;
+	
+	v = readm(amode(2), 2);
+	disp = fetch8();
+	if((v & 1) == val)
+		r[15] += disp;
+}
+
+static void
+sob(int geq)
+{
+	vlong v;
+	s32int i;
+	s8int disp;
+	
+	v = amode(2);
+	disp = fetch8();
+	i = readm(v, 2) - 1;
+	writem(v, i, 2);
+	nzv(i, 2);
+	if(i == 0x7fffffff) ps |= FLAGV;
+	if(i > 0 || i == 0 && geq)
+		r[15] += disp;
+}
+
+static void
+aob(int leq)
+{
+	s32int l, v;
+	vlong a;
+	s8int disp;
+	
+	l = readm(amode(2), 2);
+	a = amode(2);
+	disp = fetch8();
+	v = readm(a, 2) + 1;
+	writem(a, v, 2);
+	nzv(v, 2);
+	if(v == 0x80000000) ps |= FLAGV;
+	if(v < l || v == l && leq)
+		r[15] += disp;
+}
+
+static void
+bsb(int s)
+{
+	u32int v;
+	
+	switch(s){
+	case 0:
+		v = fetch8();
+		writem(r[14] -= 4, r[15], 2);
+		r[15] += (s8int) v;
+		break;
+	case 1:
+		v = fetch16();
+		writem(r[14] -= 4, r[15], 2);
+		r[15] += (s16int) v;		
+		break;
+	case 2:
+		v = addrof(amode(0));
+		writem(r[14] -= 4, r[15], 2);
+		r[15] = v;	
+		break;
+	}
+}
+
+static void
+casei(int s)
+{
+	u32int sel, base, lim;
+	
+	sel = readm(amode(s), s);
+	base = readm(amode(s), s);
+	lim = readm(amode(s), s);
+	sel -= base;
+	if(sel <= lim)
+		r[15] += (s16int) readm(r[15] + 2 * sel, 1);
+	else
+		r[15] += 2 * (lim + 1);
+}
+
+static void
+pushr(void)
+{
+	u16int m;
+	u32int sp;
+	int i;
+	
+	m = readm(amode(1), 1);
+	sp = r[14];
+	for(i = 15; --i >= 0; )
+		if((m & 1<<i) != 0)
+			writem(sp -= 4, r[i], 2);
+	r[14] = sp;
+}
+
+static void
+popr(void)
+{
+	u16int m;
+	u32int sp;
+	int i;
+	
+	m = readm(amode(1), 1);
+	sp = r[14];
+	for(i = 0; i < 15; i++)
+		if((m & 1<<i) != 0){
+			r[i] = readm(sp, 2);
+			sp += 4;
+		}
+	if((m & 1<<14) == 0)
+		r[14] = sp;
+}
+
+static void
+acb(int s)
+{
+	vlong a;
+	s32int lim, n, v;
+	s16int disp;
+	int c;
+	
+	lim = readm(amode(s), s);
+	n = readm(amode(s), s);
+	a = amode(s);
+	v = readm(a, s);
+	disp = fetch16();
+	
+	c = ps & FLAGC;
+	v = add(v, n, s);
+	nz(v, s);
+	ps |= c;
+	writem(a, v, s);
+	
+	if(n >= 0 && v <= lim || n < 0 && v >= lim)
+		r[15] += disp;
+}
+
+static void
+extv(int sx)
+{
+	u32int pos, v;
+	u8int c;
+	u8int size;
+	int i, s;
+	vlong base;
+	vlong dst;
+	
+	pos = readm(amode(2), 2);
+	size = readm(amode(0), 0);
+	base = amode(0);
+	dst = amode(2);
+	if(size > 32 || pos >= 32 && base < 0) sysfatal("extv size=%d pos=%d (pc=%#.8ux)", size, pos, curpc);
+	if(base < 0){
+		v = readm(base, 2) >> pos;
+		if(size < 32) 
+			v &= (1<<size) - 1;
+	}else{
+		base += pos >> 3;
+		pos &= 7;
+		v = 0;
+		for(i = 0; i < size; i += s){
+			c = readm(base, 0);
+			c >>= pos;
+			s = 8 - pos;
+			if(s > size) s = size;
+			v |= (c & (1<<s) - 1) << i;
+			base++;
+			pos = 0;
+		}
+	}
+	if(sx)
+		v = (s32int)(v << 32 - size) >> 32 - size;
+	writem(dst, v, 2);
+	ps &= ~FLAGC;
+	nzv(v, 2);
+}
+
+static void
+insv(void)
+{
+	u32int src, pos;
+	u8int size;
+	vlong base;
+	u32int v, m;
+	int i, s;
+
+	src = readm(amode(2), 2);
+	pos = readm(amode(2), 2);
+	size = readm(amode(0), 0);
+	base = amode(0);
+	if(size > 32 || pos >= 32 && base < 0) sysfatal("extv");
+	if(base < 0){
+		v = readm(base, 2);
+		m = (size == 32 ? 0 : 1<<size) - 1 << pos;
+		v = v & ~m | src << pos & m;
+		writem(base, v, 2);
+	}else{
+		base += pos >> 3;
+		pos &= 7;
+		for(i = 0; i < size; i += s){
+			v = readm(base, 0);
+			s = 8 - pos;
+			if(s > size) s = size;
+			m = (1<<s) - 1 << pos;
+			v = v & ~m | src << pos & m;
+			writem(base, v, 0);
+			src >>= s;
+			base++;
+		}
+	}
+	ps &= ~15;
+}
+
+void addp(int, int);
+void editpc(void);
+void cvtlp(void);
+void movp(void);
+void cmpp(int);
+void ashp(void);
+void alufp(int, int, int);
+void movefp(int, int);
+void cvtfp(int, int, int);
+void locc(int);
+void cmpc(int);
+void movc(int);
+void emod(int);
+
+void
+step(void)
+{
+	uchar op;
+
+	curpc = r[15];
+	op = fetch8();
+	if(trace || 0 && op >= 0x40 && op < 0x78)
+		print("%.8ux %.2ux %.2ux %.8ux %.8ux %.8ux %.8ux\n", curpc, op, ps, r[14], r[0], r[1], r[5]);
+	switch(op){
+	case 0x04: ret(); break;
+	case 0x05: r[15] = readm(r[14], 2); r[14] += 4; break;
+	case 0x10: bsb(0); break;
+	case 0x11: branch(0, 1); break;
+	case 0x12: branch(0, (ps & FLAGZ) == 0); break;
+	case 0x13: branch(0, (ps & FLAGZ) != 0); break;
+	case 0x14: branch(0, (ps & (FLAGZ|FLAGN)) == 0); break;
+	case 0x15: branch(0, (ps & (FLAGZ|FLAGN)) != 0); break;
+	case 0x16: bsb(2); break;
+	case 0x17: r[15] = amode(0); break;
+	case 0x18: branch(0, (ps & FLAGN) == 0); break;
+	case 0x19: branch(0, (ps & FLAGN) != 0); break;
+	case 0x1a: branch(0, (ps & (FLAGZ|FLAGC)) == 0); break;
+	case 0x1b: branch(0, (ps & (FLAGZ|FLAGC)) != 0); break;
+	case 0x1c: branch(0, (ps & FLAGV) == 0); break;
+	case 0x1d: branch(0, (ps & FLAGV) != 0); break;
+	case 0x1e: branch(0, (ps & FLAGC) == 0); break;
+	case 0x1f: branch(0, (ps & FLAGC) != 0); break;
+	case 0x20: addp(4, 0); break;
+	case 0x21: addp(6, 0); break;
+	case 0x22: addp(4, 1); break;
+	case 0x23: addp(6, 1); break;
+	case 0x28: movc(0); break;
+	case 0x29: cmpc(0); break;
+	case 0x2c: movc(1); break;
+	case 0x2d: cmpc(1); break;
+	case 0x30: bsb(1); break;
+	case 0x31: branch(1, 1); break;
+	case 0x32: cvt(1, 2); break;
+	case 0x33: cvt(1, 0); break;
+	case 0x34: movp(); break;
+	case 0x35: cmpp(3); break;
+	case 0x37: cmpp(4); break;
+	case 0x38: editpc(); break;
+	case 0x3a: locc(0); break;
+	case 0x3b: locc(1); break;
+	case 0x3c: movez(1, 2); break;
+	case 0x3d: acb(1); break;
+	case 0x3e: movea(1); break;
+	case 0x3f: pusha(1); break;
+	case 0x40: alufp(ADD, 2, 0x12); break;
+	case 0x41: alufp(ADD, 3, 0x12); break;
+	case 0x42: alufp(SUB, 2, 0x12); break;
+	case 0x43: alufp(SUB, 3, 0x12); break;
+	case 0x44: alufp(MUL, 2, 0x12); break;
+	case 0x45: alufp(MUL, 3, 0x12); break;
+	case 0x46: alufp(DIV, 2, 0x12); break;
+	case 0x47: alufp(DIV, 3, 0x12); break;
+	case 0x48: cvtfp(0x12, 0, 0); break;
+	case 0x49: cvtfp(0x12, 1, 0); break;
+	case 0x4a: cvtfp(0x12, 2, 0); break;
+	case 0x4b: cvtfp(0x12, 2, 1); break;
+	case 0x4c: cvtfp(0, 0x12, 0); break;
+	case 0x4d: cvtfp(1, 0x12, 0); break;
+	case 0x4e: cvtfp(2, 0x12, 0); break;
+	case 0x50: movefp(0x12, 0); break;
+	case 0x51: alufp(CMP, 2, 0x12); break;
+	case 0x52: movefp(0x12, 1); break;
+	case 0x53: alufp(CMP, 1, 0x12); break;
+	case 0x54: emod(0x12); break;
+	case 0x56: cvtfp(0x12, 0x13, 0); break;
+	case 0x60: alufp(ADD, 2, 0x13); break;
+	case 0x61: alufp(ADD, 3, 0x13); break;
+	case 0x62: alufp(SUB, 2, 0x13); break;
+	case 0x63: alufp(SUB, 3, 0x13); break;
+	case 0x64: alufp(MUL, 2, 0x13); break;
+	case 0x65: alufp(MUL, 3, 0x13); break;
+	case 0x66: alufp(DIV, 2, 0x13); break;
+	case 0x67: alufp(DIV, 3, 0x13); break;
+	case 0x68: cvtfp(0x13, 0, 0); break;
+	case 0x69: cvtfp(0x13, 1, 0); break;
+	case 0x6a: cvtfp(0x13, 2, 0); break;
+	case 0x6b: cvtfp(0x13, 2, 1); break;
+	case 0x6c: cvtfp(0, 0x13, 0); break;
+	case 0x6d: cvtfp(1, 0x13, 0); break;
+	case 0x6e: cvtfp(2, 0x13, 0); break;
+	case 0x70: movefp(0x13, 0); break;
+	case 0x71: alufp(CMP, 2, 0x13); break;
+	case 0x72: movefp(0x13, 1); break;
+	case 0x73: alufp(CMP, 1, 0x13); break;
+	case 0x74: emod(0x13); break;
+	case 0x76: cvtfp(0x13, 0x12, 0); break;
+	case 0x78: ashl(); break;
+	case 0x79: ashq(); break;
+	case 0x7b: ediv(); break;
+	case 0x7c: writem64(amode(3), 0, 3); nzv(0, 0); break;
+	case 0x7d: move(3); break;
+	case 0x7e: movea(3); break;
+	case 0x7f: pusha(3); break;
+	case 0x80: alu(ADD, 2, 0); break;
+	case 0x81: alu(ADD, 3, 0); break;
+	case 0x82: alu(SUB, 2, 0); break;
+	case 0x83: alu(SUB, 3, 0); break;
+	case 0x84: alu(MUL, 2, 0); break;
+	case 0x85: alu(MUL, 3, 0); break;
+	case 0x86: alu(DIV, 2, 0); break;
+	case 0x87: alu(DIV, 3, 0); break;
+	case 0x88: alu(BIS, 2, 0); break;
+	case 0x89: alu(BIS, 3, 0); break;
+	case 0x8a: alu(BIC, 2, 0); break;
+	case 0x8b: alu(BIC, 3, 0); break;
+	case 0x8c: alu(XOR, 2, 0); break;
+	case 0x8d: alu(XOR, 3, 0); break;
+	case 0x8e: alu(SUB, 4, 0); break;
+	case 0x8f: casei(0); break;
+	case 0x90: move(0); break;
+	case 0x91: alu(CMP, 2, 0); break;
+	case 0x92: alu(XOR, 4, 0); break;
+	case 0x93: alu(BIT, 2, 0); break;
+	case 0x94: writem(amode(0), 0, 0); nzv(0, 0); break;
+	case 0x95: alu(TST, 1, 0); break;
+	case 0x96: alu(ADD, 1, 0); break;
+	case 0x97: alu(SUB, 1, 0); break;
+	case 0x98: cvt(0, 2); break;
+	case 0x99: cvt(0, 1); break;
+	case 0x9a: movez(0, 2); break;
+	case 0x9b: movez(0, 1); break;
+	case 0x9c: rotl(); break;
+	case 0x9d: acb(0); break;
+	case 0x9e: movea(0); break;
+	case 0x9f: pusha(0); break;
+	case 0xa0: alu(ADD, 2, 1); break;
+	case 0xa1: alu(ADD, 3, 1); break;
+	case 0xa2: alu(SUB, 2, 1); break;
+	case 0xa3: alu(SUB, 3, 1); break;
+	case 0xa4: alu(MUL, 2, 1); break;
+	case 0xa5: alu(MUL, 3, 1); break;
+	case 0xa6: alu(DIV, 2, 1); break;
+	case 0xa7: alu(DIV, 3, 1); break;
+	case 0xa8: alu(BIS, 2, 1); break;
+	case 0xa9: alu(BIS, 3, 1); break;
+	case 0xaa: alu(BIC, 2, 1); break;
+	case 0xab: alu(BIC, 3, 1); break;
+	case 0xac: alu(XOR, 2, 1); break;
+	case 0xad: alu(XOR, 3, 1); break;
+	case 0xae: alu(SUB, 4, 1); break;
+	case 0xaf: casei(1); break;
+	case 0xb0: move(1); break;
+	case 0xb1: alu(CMP, 2, 1); break;
+	case 0xb2: alu(XOR, 4, 1); break;
+	case 0xb3: alu(BIT, 2, 1); break;
+	case 0xb4: writem(amode(1), 0, 1); nzv(0, 1); break;
+	case 0xb5: alu(TST, 1, 1); break;
+	case 0xb6: alu(ADD, 1, 1); break;
+	case 0xb7: alu(SUB, 1, 1); break;
+	case 0xba: popr(); break;
+	case 0xbb: pushr(); break;
+	case 0xbc: syscall(readm(amode(1), 1)); break;
+	case 0xc0: alu(ADD, 2, 2); break;
+	case 0xc1: alu(ADD, 3, 2); break;
+	case 0xc2: alu(SUB, 2, 2); break;
+	case 0xc3: alu(SUB, 3, 2); break;
+	case 0xc4: alu(MUL, 2, 2); break;
+	case 0xc5: alu(MUL, 3, 2); break;
+	case 0xc6: alu(DIV, 2, 2); break;
+	case 0xc7: alu(DIV, 3, 2); break;
+	case 0xc8: alu(BIS, 2, 2); break;
+	case 0xc9: alu(BIS, 3, 2); break;
+	case 0xca: alu(BIC, 2, 2); break;
+	case 0xcb: alu(BIC, 3, 2); break;
+	case 0xcc: alu(XOR, 2, 2); break;
+	case 0xcd: alu(XOR, 3, 2); break;
+	case 0xce: alu(SUB, 4, 2); break;
+	case 0xcf: casei(2); break;
+	case 0xd0: move(2); break;
+	case 0xd1: alu(CMP, 2, 2); break;
+	case 0xd2: alu(XOR, 4, 2); break;
+	case 0xd3: alu(BIT, 2, 2); break;
+	case 0xd4: writem(amode(2), 0, 2); nzv(0, 2); break;
+	case 0xd5: alu(TST, 1, 2); break;
+	case 0xd6: alu(ADD, 1, 2); break;
+	case 0xd7: alu(SUB, 1, 2); break;
+	case 0xd8: adwc(); break;
+	case 0xd9: sbwc(); break;
+	case 0xdd: pushl(); break;
+	case 0xde: movea(2); break;
+	case 0xdf: pusha(2); break;
+	case 0xe0: bbs(0, 0); break;
+	case 0xe1: bbs(1, 0); break;
+	case 0xe2: bbs(0, 1); break;
+	case 0xe3: bbs(1, 1); break;
+	case 0xe4: bbs(0, -1); break;
+	case 0xe5: bbs(1, -1); break;
+	case 0xe8: blb(1); break;
+	case 0xe9: blb(0); break;
+	case 0xee: extv(1); break;
+	case 0xef: extv(0); break;
+	case 0xf0: insv(); break;
+	case 0xf1: acb(2); break;
+	case 0xf2: aob(0); break;
+	case 0xf3: aob(1); break;
+	case 0xf4: sob(1); break;
+	case 0xf5: sob(0); break;
+	case 0xf6: cvt(2, 0); break;
+	case 0xf7: cvt(2, 1); break;
+	case 0xf8: ashp(); break;
+	case 0xf9: cvtlp(); break;
+	case 0xfb: calls(); break;
+	default: sysfatal("unimplemented op %.2x (pc=%.8x)", op, curpc);
+	}
+}
--- /dev/null
+++ b/sys/src/games/v8e/cpubcd.c
@@ -1,0 +1,516 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+static s8int
+getsign(u16int l, u32int a)
+{
+	u8int c;
+	
+	if(l == 0) return 1;
+	c = readm(a + l/2, 0);
+	c &= 0xf;
+	if(c == 11 || c == 13) return -1;
+	return 1;
+}
+
+static int
+getdig(u16int l, u32int a, int i)
+{
+	u8int c;
+
+	if(i >= l) return 0;
+	i = l - 1 - i;
+	if((l & 1) == 0) i++;
+	a += i/2;
+	c = readm(a, 0);
+	if((i & 1) == 0) return c >> 4;
+	return c & 0xf;
+}
+
+static void
+putdig(u16int l, u32int a, int i, int v)
+{
+	u8int c;
+
+	if(i >= l){
+		if(v != 0)
+			ps |= FLAGV;
+		return;
+	}
+	i = l - 1 - i;
+	if((l & 1) == 0) i++;
+	a += i/2;
+	if((l & 1) == 0 && i == 1)
+		c = 0;
+	else
+		c = readm(a, 0);
+	if((i & 1) == 0) c = c & 0x0f | v << 4;
+	else c = c & 0xf0 | v;
+	writem(a, c, 0);
+}
+
+static void
+putsign(u16int l, u32int a, s8int s)
+{
+	u8int c;
+
+	a += l/2;
+	c = readm(a, 0);
+	c = c & 0xf0 | 12 | s < 0;
+	writem(a, c, 0);
+}
+
+static u32int
+sigdig(u16int l, u32int a)
+{
+	u32int e;
+	
+	e = a + l/2 + 1;
+	for(; a < e; a++)
+		if(readm(a, 0) != 0)
+			return a;
+	return a;
+}
+
+void
+cvtlp(void)
+{
+	s32int x;
+	u16int l;
+	u32int a;
+	u8int v;
+	int i;
+	
+	x = readm(amode(2), 2);
+	l = readm(amode(1), 1);
+	a = addrof(amode(0));
+	
+	ps = ps & ~15 | FLAGZ;
+	if(x < 0){
+		x = -x;
+		ps |= FLAGN;
+	}
+	for(i = 0; i < l; i++){
+		v = x % 10;
+		x /= 10;
+		putdig(l, a, i, v);
+		if(v != 0) ps &= ~FLAGZ;
+	}
+	if(x != 0) ps |= FLAGV;
+	if((ps & (FLAGN|FLAGZ)) == 0)
+		ps &= ~FLAGN;
+	putsign(l, a, -((ps & FLAGN) != 0));
+	r[0] = 0;
+	r[1] = 0;
+	r[2] = 0;
+	r[3] = sigdig(l, a);
+}
+
+static uchar
+editread(void)
+{
+	u8int rc;
+
+	if((s32int) r[0] <= 0){
+		if(r[0] == 0)
+			sysfatal("editread");
+		r[0] += 0x10000;
+		return 0;
+	}else{
+		rc = readm(r[1], 0);
+		if((r[0] & 1) != 0)
+			rc >>= 4;
+		else
+			rc &= 0xf;
+		r[0]--;
+		if((r[0] & 1) != 0)
+			r[1]++;
+		return rc;
+	}
+}
+
+void
+editpc(void)
+{
+	u8int p, c;
+	int i;
+	
+	r[0] = readm(amode(1), 1);
+	r[1] = addrof(amode(0));
+	r[2] = 0x2020;
+	r[3] = addrof(amode(0));
+	r[5] = addrof(amode(0));
+	
+	ps = ps & ~15 | FLAGZ;
+	c = readm(r[1] + r[0]/2, 0) & 0xf;
+	if(c == 11 || c == 13){
+		ps |= FLAGN;
+		r[2] |= 0xd00;
+	}
+	
+	for(;;){
+		p = readm(r[3]++, 0);
+		switch(p){
+		case 0x00:
+			if(r[0] != 0) sysfatal("editpc");
+			if((ps & FLAGZ) != 0)
+				ps &= ~FLAGN;
+			return;
+		case 0x01:
+			if((ps & FLAGC) == 0){
+				writem(r[5]++, r[2] >> 8, 0);
+				ps |= FLAGC;
+			}
+			break;
+		case 0x02: ps &= ~FLAGC; break;
+		case 0x03: ps |= FLAGC; break;
+		case 0x04: writem(r[5]++, r[2] >> 8, 0); break;
+		case 0x40: r[2] = r[2] & ~0xff | (u8int)readm(r[3]++, 0); break;
+		case 0x41: r[2] = r[2] & ~0xff00 | (u8int)readm(r[3]++, 0) << 8; break;
+		case 0x42:
+			p = readm(r[3]++, 0);
+			if((ps & FLAGN) == 0)
+				r[2] = r[2] & ~0xff00 | p << 8;
+			break;
+		case 0x43:
+			p = readm(r[3]++, 0);
+			if((ps & FLAGN) != 0)
+				r[2] = r[2] & ~0xff00 | p << 8;
+			break;
+		case 0x44:
+			p = readm(r[3]++, 0);
+			if((ps & FLAGC) != 0)
+				writem(r[5]++, p, 0);
+			else
+				writem(r[5]++, r[2], 0);
+			break;
+		case 0x45:
+			p = readm(r[3]++, 0);
+			if((ps & FLAGZ) != 0){
+				r[5] -= p;
+				while(p-- != 0)
+					writem(r[5]++, r[2], 0);
+			}
+			break;
+		case 0x46:
+			p = readm(r[3]++, 0);
+			if((ps & (FLAGN|FLAGZ)) == (FLAGN|FLAGZ))
+				writem(r[5] - p, r[2], 0);
+			break;
+		case 0x47:
+			p = readm(r[3]++, 0);
+			if((u16int)r[0] > p){
+				r[0] = (u16int) r[0];
+				i = r[0] - p;
+				while(i-- > 0)
+					if(editread() != 0)
+						ps = ps & ~FLAGZ | FLAGV;
+			}else
+				r[0] = (u16int) r[0] | r[0] - p << 16;
+		case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+		case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+			for(; p > 0x80; p--)
+				writem(r[5]++, r[2], 0);
+			break;
+		case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
+		case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+			for(; p > 0x90; p--){
+				c = editread();
+				if(c != 0) ps = ps & ~FLAGZ | FLAGC;
+				if((ps & FLAGC) == 0)
+					writem(r[5]++, r[2], 0);
+				else
+					writem(r[5]++, '0' + c, 0);
+			}
+			break;
+		case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+		case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
+			for(; p > 0xa0; p--){
+				c = editread();
+				if(c != 0){
+					if((ps & FLAGC) == 0)
+						writem(r[5]++, r[2] >> 8, 0);
+					ps = ps & ~FLAGZ | FLAGC;
+				}
+				if((ps & FLAGC) == 0)
+					writem(r[5]++, r[2], 0);
+				else
+					writem(r[5]++, '0' + c, 0);
+			}
+			break;
+		default: sysfatal("editpc: unknown pattern %.2x (pc=%.8ux)", p, curpc);
+		}
+	}
+}
+
+void
+movp(void)
+{
+	u16int l;
+	u32int sa, da;
+	u8int c, d;
+	int i, n;
+	
+	l = readm(amode(1), 1);
+	sa = addrof(amode(0));
+	da = addrof(amode(1));
+	n = l/2 + 1;
+	ps = ps & ~(FLAGN|FLAGV) | FLAGZ;
+	for(i = 0; i < n-1; i++){
+		c = readm(sa++, 0);
+		writem(da++, c, 0);
+		if(c != 0) ps &= ~FLAGZ;
+	}
+	c = readm(sa, 0);
+	if((c & 0xf0) != 0) ps &= ~FLAGZ;
+	d = c & 0xf0;
+	c &= 0xf;
+	if(c == 11 || c == 13) ps |= FLAGN;
+	if((ps & (FLAGN|FLAGZ)) == (FLAGN|FLAGZ)) ps &= ~FLAGN;
+	if((ps & FLAGN) != 0) d |= 13;
+	else d |= 12;
+	writem(da, d, 0);
+}
+
+
+static int
+cmpmag(u16int l1, u32int a1, u16int l2, u32int a2)
+{
+	int i;
+	u8int c1, c2;
+	
+	for(i = l1 > l2 ? l1 : l2; --i >= 0; ){
+		c1 = getdig(l1, a1, i);
+		c2 = getdig(l2, a2, i);
+		if(c1 > c2) return 1;
+		if(c1 < c2) return -1;
+	}
+	return 0;
+}
+
+void
+addp(int rn, int sub)
+{
+	u16int l1, l2, l3;
+	u32int a1, a2, a3;
+	s8int s1, s2;
+	int c, i, l, t;
+	
+	l1 = readm(amode(1), 1);
+	a1 = addrof(amode(0));
+	l2 = readm(amode(1), 1);
+	a2 = addrof(amode(0));
+	if(rn == 6){
+		l3 = readm(amode(1), 1);
+		a3 = addrof(amode(0));
+	}else{
+		l3 = l2;
+		a3 = a2;
+	}
+	s1 = getsign(l1, a1);
+	s2 = getsign(l2, a2);
+	r[0] = 0;
+	r[1] = sigdig(l1, a1);
+	r[2] = 0;
+	r[3] = sigdig(l2, a2);
+	r[4] = 0;
+	if(sub) s1 ^= -2;
+	if(s1 == s2){
+		c = 0;
+		l = l1;
+		if(l2 > l) l = l2;
+		if(l3 > l) l = l3;
+		for(i = 0; i < l; i++){
+			t = c + getdig(l1, a1, i) + getdig(l2, a2, i);
+			for(c = 0; t >= 10; c++) t -= 10;
+			putdig(l3, a3, i, t);
+		}
+	}else{
+		if(cmpmag(l1, a1, l2, a2) < 0){
+			t = l1; l1 = l2; l2 = t;
+			t = a1; a1 = a2; a2 = t;
+			s1 = s2;
+		}
+		c = 0;
+		l = l3 > l1 ? l3 : l1;
+		for(i = 0; i < l; i++){
+			t = getdig(l1, a1, i) - getdig(l2, a2, i);
+			for(c = 0; t < 0; c++) t += 10;
+			putdig(l3, a3, i, t);
+		}
+	}
+	if(c != 0) ps |= FLAGV;
+	putsign(l3, a3, s1);
+	r[5] = sigdig(l3, a3);
+}
+
+void
+cmpp(int rn)
+{
+	u16int l1, l2;
+	u32int a1, a2;
+	s8int s1, s2;
+	int rc;
+	
+	l1 = readm(amode(1), 1);
+	a1 = addrof(amode(0));
+	if(rn == 4)
+		l2 = readm(amode(1), 1);
+	else
+		l2 = l1;
+	a2 = addrof(amode(0));
+	s1 = getsign(l1, a1);
+	s2 = getsign(l2, a2);
+	r[0] = 0;
+	r[1] = sigdig(l1, a1);
+	r[2] = 0;
+	r[3] = sigdig(l2, a2);
+	ps &= ~15;
+	if(s1 != s2){
+		if(s1 < s2) ps |= FLAGN;
+		return;
+	}
+	rc = cmpmag(l1, a1, l2, a2);
+	if(rc == 0) ps |= FLAGZ;
+	else if(rc > 0) ps |= FLAGN;
+}
+
+void
+ashp(void)
+{
+	s8int cnt, rnd;
+	s16int sl, dl;
+	u32int sa, da;
+	int i, c, x;
+	
+	cnt = readm(amode(0), 0);
+	sl = readm(amode(1), 1);
+	sa = addrof(amode(2));
+	rnd = readm(amode(0), 0);
+	dl = readm(amode(1), 1);
+	da = addrof(amode(2));
+	ps = ps & ~15 | FLAGZ;
+	x = getsign(sl, sa);
+	if(x < 0) ps |= FLAGN;
+	putsign(dl, da, x);
+	for(i = 0; i < cnt; i++)
+		putdig(dl, da, i, 0);
+	c = cnt < 0 && getdig(sl, sa, -1-cnt) >= rnd;
+	for(i = cnt >= 0 ? 0 : -cnt; i < sl; i++){
+		x = getdig(sl, sa, i) + c;
+		for(c = 0; x >= 10; c++) x -= 10;
+		putdig(dl, da, i + cnt, x);
+		if(x != 0) ps &= ~FLAGZ;
+	}
+	putdig(dl, da, i + cnt, c);
+	r[0] = 0;
+	r[1] = sigdig(sl, sa);
+	r[2] = 0;
+	r[3] = sigdig(dl, da);
+}
+
+void
+locc(int inv)
+{
+	u8int chr;
+	u16int len;
+	u32int addr;
+	
+	chr = readm(amode(0), 0);
+	len = readm(amode(1), 1);
+	addr = addrof(amode(0));
+	ps &= ~15;
+	for(; len != 0; addr++, len--)
+		if(inv ^ (readm(addr, 0) == chr))
+			break;
+	if(len == 0) ps |= FLAGZ;
+	r[0] = len;
+	r[1] = addr;
+}
+
+void
+cmpc(int op5)
+{
+	u16int l1, l2;
+	u32int a1, a2;
+	u8int a, b, f;
+	
+	if(op5){
+		l1 = readm(amode(1), 1);
+		a1 = addrof(amode(2));
+		f = readm(amode(0), 0);
+		l2 = readm(amode(1), 1);
+		a2 = addrof(amode(2));
+	}else{
+		l1 = l2 = readm(amode(1), 1);
+		a1 = addrof(amode(2));
+		a2 = addrof(amode(2));
+		f = 0;
+	}
+	a = b = f;
+	for(; l1 > 0 && l2 > 0; l1--, l2--, a1++, a2++){
+		a = readm(a1, 0);
+		b = readm(a2, 0);
+		if(a != b) goto ineq;
+	}
+	for(; l1 > 0; l1--, a1++){
+		a = readm(a1, 0);
+		b = f;
+		if(a != b) goto ineq;
+	}
+	for(; l2 > 0; l2--, a2++){
+		a = f;
+		b = readm(a2, 0);
+		if(a != b) goto ineq;
+	}
+ineq:
+	ps = ps & ~15;
+	if((s8int)a < (s8int)b) ps |= FLAGN;
+	if(a == b) ps |= FLAGZ;
+	if(a < b) ps |= FLAGC;
+	r[0] = l1;
+	r[1] = a1;
+	r[2] = l2;
+	r[3] = a2;
+}
+
+void
+movc(int op5)
+{
+	u16int sl, dl, l;
+	u32int sa, da;
+	int i;
+	u8int f;
+	
+	sl = readm(amode(1), 1);
+	sa = addrof(amode(0));
+	if(op5){
+		f = readm(amode(0), 0);
+		dl = readm(amode(1), 1);
+	}else{
+		f = 0;
+		dl = sl;
+	}
+	da = addrof(amode(0));
+	l = sl < dl ? sl : dl;
+	if(sa < da)
+		for(i = l; --i >= 0; )
+			writem(da + i, readm(sa + i, 0), 0);
+	else
+		for(i = 0; i < l; i++)
+			writem(da + i, readm(sa + i, 0), 0);
+	for(i = l; i < dl; i++)
+		writem(da + i, f, 0);
+	r[0] = sl - l;
+	r[1] = sa + sl;
+	r[2] = 0;
+	r[3] = da + dl;
+	r[4] = 0;
+	r[5] = 0;
+	ps &= ~15;
+	if((s16int) sl < (s16int) dl) ps |= FLAGN;
+	else if(sl == dl) ps |= FLAGZ;
+	if(sl < dl) ps |= FLAGC;
+}
--- /dev/null
+++ b/sys/src/games/v8e/cpufp.c
@@ -1,0 +1,521 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+/* BUGS: Not bit accurate. */
+
+enum {
+	ADD,
+	SUB,
+	MUL,
+	DIV,
+	CMP,
+};
+
+enum {
+	EBIAS = 129
+};
+
+#define zero(x) (((x) & 0xff80) == 0)
+#define inval(x) (((x) & 0xff80) == 0x8000)
+#define expo(x) ((x) >> 7 & 0xff)
+#define mantf(x) (1<<23 | ((x) & 0x7f) << 16 | (x) >> 16)
+#define mantd(x) (1ULL<<55|((x)&0x7f)<<48|((x)&0xffff0000)<<16| \
+	(x)>>16&0xffff0000|(u16int)((x)>>48))
+#define sign(x) ((int)x << 16 >> 30 | 1)
+#define makef(s, e, m) ((s)&0x8000|(e)<<7|(m)<<16|(m)>>16&0x7f)
+#define maked(s, e, m) (s&0x8000|(e)<<7|(uvlong)(m)<<48|(uvlong)((m)&0xffff0000)<<16| \
+	(m)>>16&0xffff0000|(m)>>48&0x7f)
+
+static double
+vfc(u32int a)
+{
+	union { u32int a; float b; } u;
+	
+	if(zero(a)) return 0;
+	a -= 0x100;
+	u.a = a >> 16 | a << 16;
+	return u.b;
+}
+
+static double
+vdc(u64int a)
+{
+	union { u64int a; double b; } u;
+	
+	if(zero(a)) return 0;
+	u.a = mantd(a) >> 3 & (1ULL<<52)-1 | expo(a) + 894 << 52 | sign(a) & 1ULL<<63;
+	return u.b;
+}
+
+static int
+clz32(u32int a)
+{
+	int n;
+	static uchar c[16] = {4, 3, 2, 2, 1, 1, 1, 1};
+
+	n = 0;
+	if((a >> 16) == 0){n += 16; a <<= 16;}
+	if((a >> 24) == 0){n += 8; a <<= 8;}
+	if((a >> 28) == 0){n += 4; a <<= 4;}
+	return n + c[a >> 28];
+}
+
+static int
+clz64(uvlong a)
+{
+	int n;
+	static uchar c[16] = {4, 3, 2, 2, 1, 1, 1, 1};
+
+	n = 0;
+	if((a >> 32) == 0){n += 32; a <<= 32;}
+	if((a >> 48) == 0){n += 16; a <<= 16;}
+	if((a >> 56) == 0){n += 8; a <<= 8;}
+	if((a >> 60) == 0){n += 4; a <<= 4;}
+	return n + c[a >> 60];
+}
+
+static int
+magcmpd(u64int a, u64int b)
+{
+	int e;
+	s64int m;
+
+	e = expo(a) - expo(b);
+	if(e > 0) return 1;
+	if(e < 0) return -1;
+	m = mantd(a) - mantd(b);
+	if(m > 0) return 1;
+	if(m < 0) return -1;
+	return 0;
+}
+
+static int
+cmpd(u64int a, u64int b)
+{
+	int s;
+
+	if(zero(a)){
+		if(zero(b)) return 0;
+		return -sign(b);
+	}
+	if(zero(b)) return sign(a);
+	s = sign(a) - sign(b);
+	if(s > 0) return 1;
+	if(s < 0) return -1;
+	return magcmpd(a, b);
+}
+
+static u32int
+addf(u32int a, u32int b, int sub)
+{
+	int e1, e2, m1, m2, s1, s2;
+	int n;
+	u32int c;
+
+	if(inval(a) || inval(b)) return 0x8000;
+	if(zero(b)) return a;
+	if(sub) b ^= 0x8000;
+	if(zero(a)) return b;
+	if(magcmpd(a, b) < 0){
+		c = a;
+		a = b;
+		b = c;
+	}
+	e1 = expo(a); m1 = mantf(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantf(b); s2 = sign(b);
+	if(e1 - e2 >= 24) return a;
+	m2 >>= e1 - e2;
+	if(s1 == s2)
+		m1 += m2;
+	else
+		m1 -= m2;
+	if(m1 == 0) return 0;
+	n = 8 - clz32(m1);
+	e1 += n;
+	if(n > 0) m1 >>= n;
+	else m1 <<= -n;
+	return makef(s1, e1, m1);
+}
+
+static u32int
+mulf(u32int a, u32int b)
+{
+	int e1, e2, m1, m2, s1, s2, l;
+	
+	if(zero(a) || zero(b)) return 0;
+	e1 = expo(a); m1 = mantf(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantf(b); s2 = sign(b);
+	s1 ^= s2 & -2;
+	e1 += e2 - EBIAS;
+	if(e1 <= 0) return 0;
+	l = (uvlong)m1 * m2 + (1<<22) >> 23;
+	if((l & 1<<24) != 0){
+		l >>= 1;
+		e1++;
+	}
+	if(e1 >= 256) return 0x8000;
+	return makef(s1, e1, l);
+}
+
+static u32int
+divf(u32int a, u32int b)
+{
+	int e1, e2, m1, m2, s1, s2;
+	uvlong l;
+
+	if(zero(a)) return 0;
+	if(zero(b)) return 0x8000;
+	e1 = expo(a); m1 = mantf(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantf(b); s2 = sign(b);
+	s1 ^= s2 & -2;
+	e1 -= e2 - EBIAS;
+	l = (uvlong) m1 << 40;
+	l /= m2;
+	l >>= 17;
+	if(l == 0) return 0;
+	while((l & 1<<23) == 0){
+		l <<= 1;
+		e1--;
+	}
+	if(e1 <= 0) return 0;
+	if(e1 >= 256) return 0x8000;
+	return makef(s1, e1, l);
+}
+
+static u64int
+addd(u64int a, u64int b, int sub)
+{
+	int e1, e2, s1, s2;
+	u64int m1, m2;
+	int n;
+	u64int c;
+
+	if(inval(a) || inval(b)) return 0x8000;
+	if(zero(b)) return a;
+	if(sub) b ^= 0x8000;
+	if(zero(a)) return b;
+	if(magcmpd(a, b) < 0){
+		c = a;
+		a = b;
+		b = c;
+	}
+	e1 = expo(a); m1 = mantd(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantd(b); s2 = sign(b);
+	if(e1 - e2 >= 56) return a;
+	m2 >>= e1 - e2;
+	if(s1 == s2)
+		m1 += m2;
+	else
+		m1 -= m2;
+	if(m1 == 0) return 0;
+	n = 8 - clz64(m1);
+	e1 += n;
+	if(n > 0) m1 >>= n;
+	else m1 <<= -n;
+	return maked(s1, e1, m1);
+}
+
+static u64int
+mul55(u64int a, u64int b)
+{
+	u64int l;
+
+	l = (uvlong)(u32int)a * (u32int)b >> 32;
+	l += (a >> 32) * (u32int)b;
+	l += (u32int)a * (b >> 32);
+	l = l + (1<<21) >> 22;
+	l += (a >> 32) * (b >> 32) << 10;
+	l = l + 1 >> 1;
+	return l;
+}
+
+static u64int
+mul62(u64int a, u64int b)
+{
+	u64int l;
+
+	l = (uvlong)(u32int)a * (u32int)b >> 32;
+	l += (a >> 32) * (u32int)b;
+	l += (u32int)a * (b >> 32);
+	l = l + (1<<28) >> 29;
+	l += (a >> 32) * (b >> 32) << 3;
+	l = l + 1 >> 1;
+	return l;
+}
+
+static u64int
+muld(u64int a, u64int b)
+{
+	int e1, e2, s1, s2;
+	uvlong m1, m2;
+	uvlong l;
+	
+	if(zero(a) || zero(b)) return 0;
+	e1 = expo(a); m1 = mantd(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantd(b); s2 = sign(b);
+	s1 ^= s2 & -2;
+	e1 += e2 - EBIAS;
+	if(e1 <= 0) return 0;
+	l = mul55(m1, m2);
+	if((l & 1ULL<<56) != 0){
+		l >>= 1;
+		e1++;
+	}
+	if(e1 >= 256) return 0x8000;
+	return maked(s1, e1, l);
+}
+
+static u64int
+divd(u64int a, u64int b)
+{
+	int e1, e2, s1, s2;
+	uvlong m1, m2, l;
+
+	if(zero(a)) return 0;
+	if(zero(b)) return 0x8000;
+	e1 = expo(a); m1 = mantd(a); s1 = sign(a);
+	e2 = expo(b); m2 = mantd(b); s2 = sign(b);
+	s1 ^= s2 & -2;
+	e1 -= e2 - EBIAS;
+	l = (1ULL<<63) / (m2 >> 28) << 26;
+	m2 <<= 7;
+	l = mul62(l, (1ULL<<63) - mul62(l, m2));
+	l = mul62(l, (1ULL<<63) - mul62(l, m2));
+	l = mul62(l, (1ULL<<63) - mul62(l, m2));
+	l = mul62(l, m1 << 7);
+	l += 1<<6;
+	l >>= 7;
+	if(l == 0) return 0;
+	while((l & 1ULL<<55) == 0){
+		l <<= 1;
+		e1--;
+	}
+	if(e1 <= 0) return 0;
+	if(e1 >= 256) return 0x8000;
+	return maked(s1, e1, l);
+}
+
+void
+alufp(int o, int r, int s)
+{
+	u64int a, b, v;
+	vlong c;
+	int i;
+	
+	switch(r){
+	case 2:
+		b = readm64(amode(s), s);
+		c = amode(s);
+		a = readm64(c, s);
+		break;
+	case 3:
+		b = readm64(amode(s), s);
+		a = readm64(amode(s), s);
+		c = amode(s);
+		break;
+	default: sysfatal("alufp: r==%d", r);
+	}
+	switch(o){
+	case ADD:
+		if(s == 0x13) v = addd(a, b, 0);
+		else v = addf(a, b, 0);
+		break;
+	case SUB:
+		if(s == 0x13) v = addd(a, b, 1);
+		else v = addf(a, b, 1);
+		break;
+	case MUL:
+		if(s == 0x13) v = muld(a, b);
+		else v = mulf(a, b);
+		break;
+	case DIV:
+		if(s == 0x13) v = divd(a, b);
+		else v = divf(a, b);
+		break;
+	case CMP:
+		ps &= ~15;
+		i = cmpd(b, a);
+		if(i < 0) ps |= FLAGN;
+		if(i == 0) ps |= FLAGZ;
+		return;
+	default:
+		sysfatal("alufp: unimplemented op=%d", o);
+	}
+//	print("%.8ux %d %20.16g %20.16g %20.16g\n", curpc, o, vdc(a), vdc(b), vdc(v));
+	ps &= ~15;
+	if(zero(v)) ps |= FLAGZ;
+	if((v & 0x8000) != 0) ps |= FLAGN;
+	writem64(c, v, s);
+}
+
+static u64int
+itof(s32int i)
+{
+	int n;
+	u64int l;
+
+	l = 0;
+	if(i < 0){
+		l |= 0x8000;
+		i = -i;
+	}else if(i == 0)
+		return 0;
+	n = clz32(i);
+	l |= maked(0, 160 - n, (uvlong)i << 24 + n);
+	return l;
+}
+
+static s64int
+ftoi(u64int l)
+{
+	int s, e;
+	
+	s = sign(l);
+	e = expo(l);
+	l = mantd(l);
+	if(e >= EBIAS + 64) return 1LL<<63;
+	if(e < EBIAS) return 0;
+	l >>= EBIAS + 55 - e;
+	if(s < 0) return -l;
+	else return l;
+}
+
+void
+cvtfp(int s, int t, int r)
+{
+	u64int l;
+	int si, e;
+
+	switch(s){
+	case 0: l = itof((s8int) readm(amode(0), 0)); break;
+	case 1: l = itof((s16int) readm(amode(1), 1)); break;
+	case 2: l = itof(readm(amode(2), 2)); break;
+	case 0x12: l = readm(amode(2), 2); break;
+	case 0x13: l = readm64(amode(3), 3); break;
+	default: sysfatal("cvtfp: s==%d", s);
+	}
+	if(r) l = addd(l, maked(sign(l), 128, 0), 0);
+	if(t < 0x10) l = ftoi(l);
+	ps &= ~15;
+	switch(t){
+	case 0:
+		if((s64int)l != (s8int)l) ps |= FLAGV;
+		l = (s8int) l;
+		break;
+	case 1:
+		if((s64int)l != (s16int)l) ps |= FLAGV;
+		l = (s16int) l;
+		break;
+	case 2:
+		if((s64int)l != (s32int)l) ps |= FLAGV;
+		l = (s32int) l;
+		break;	
+	case 0x12:
+		si = sign(l);
+		e = expo(l);
+		l = mantd(l);
+		l += 1ULL<<31;
+		if((l & 1ULL<<56) != 0){
+			l >>= 1;
+			e++;
+		}
+		l = maked(si, e, l);
+		break;
+	}
+	writem64(amode(t), l, t);
+	if(t >= 0x10){
+		if(zero(l)) ps |= FLAGZ;
+		if((l & 0x8000) != 0) ps |= FLAGN;
+	}else{
+		if(l == 0) ps |= FLAGZ;
+		if((s64int)l < 0) ps |= FLAGN;
+	}
+}
+
+void
+movefp(int t, int n)
+{
+	u64int x;
+
+	x = readm64(amode(t), t);
+	if(inval(x)) sysfatal("invalid float");
+	ps &= ~(FLAGN|FLAGZ|FLAGV);
+	if(zero(x))
+		ps |= FLAGZ;
+	else{
+		if(n) x ^= 0x8000;
+		if((x & 0x8000) != 0) ps |= FLAGN;
+	}
+	writem64(amode(t), x, t);
+}
+
+void
+emod(int s)
+{
+	u64int a, b, m1, m2, l;
+	u8int a8;
+	vlong ai, af;
+	int e1, e2, s1, s2, n;
+
+	a = readm64(amode(s), s);
+	a8 = readm(amode(0), 0);
+	b = readm64(amode(s), s);
+	ai = amode(2);
+	af = amode(s);
+	
+	if(zero(a) || zero(b)){
+		ps = ps & ~15 | FLAGZ;
+		writem(ai, 0, 2);
+		writem64(af, 0, s);
+		return;
+	}
+	e1 = expo(a); m1 = mantd(a) << 8 | a8; s1 = sign(a);
+	e2 = expo(b); m2 = mantd(b); s2 = sign(b);
+	s1 ^= s2 & -2;
+	e1 += e2 - EBIAS;
+	if(e1 <= 0){
+		ps = ps & ~15 | FLAGZ;
+		writem(ai, 0, 2);
+		writem64(af, 0, s);
+		return;
+	}
+	l = (uvlong)(u32int)m1 * (u32int)m2 >> 32;
+	l += (m1 >> 32) * (u32int)m2;
+	l += (u32int)m1 * (m2 >> 32);
+	l = l + (1<<29) >> 30;
+	l += (m1 >> 32) * (m2 >> 32) << 2;
+	l = l + 1 >> 1;
+	while((l & 1ULL<<56) != 0){
+		l = l + 1 >> 1;
+		e1++;
+	}
+	if(e1 >= 256){
+		ps |= FLAGV;
+		return;
+	}
+	if(e1 < EBIAS){
+		writem(ai, 0, 2);
+		writem64(af, maked(s1, e1, l), s);
+		if(s1 < 0) ps |= FLAGN;
+		return;
+	}
+	writem(ai, l >> 55+EBIAS-e1, 2);
+	l &= (1ULL<<55+EBIAS-e1) - 1;
+	if(l == 0){
+		writem64(af, 0, s);
+		ps |= FLAGZ;
+		return;
+	}
+	n = clz64(l)-8;
+	l <<= n;
+	e1 -= n;
+	writem64(af, maked(s1, e1, l), s);
+	if(s1 < 0) ps |= FLAGN;
+}
+
+void
+fptest(void)
+{
+}
--- /dev/null
+++ b/sys/src/games/v8e/dat.h
@@ -1,0 +1,57 @@
+typedef char s8int;
+typedef short s16int;
+typedef int s32int;
+typedef vlong s64int;
+
+extern u32int r[16];
+extern u32int ps;
+extern u32int curpc;
+extern int trace;
+
+#define U32(x) ((x)[0] | (x)[1] << 8 | (x)[2] << 16 | (x)[3] << 24)
+
+typedef struct Segment Segment;
+typedef struct Chan Chan;
+
+struct Segment {
+	enum {
+		SEGRO = 1,
+	} flags;
+	u32int start, size;
+	u32int *data;
+};
+
+extern Segment segs[3];
+
+enum {
+	STACKSIZE = 16*1024*1024
+};
+
+enum {
+	EPERM = 1,
+	ENOENT = 2,
+	EIO = 5,
+	EBADF = 9,
+	EINVAL = 22,
+	EMFILE = 24,
+	ENOTTY = 25,
+};
+
+struct Chan {
+	int fd;
+	enum {
+		DONTCLOSE = 1,
+		DIR = 2,
+		FAKETTY = 4,
+	} flags;
+	char *buf, *bufp, *bufe;
+};
+
+enum { NCHANS = 128 };
+
+enum {
+	FLAGN = 8,
+	FLAGZ = 4,
+	FLAGV = 2,
+	FLAGC = 1,
+};
--- /dev/null
+++ b/sys/src/games/v8e/fns.h
@@ -1,0 +1,16 @@
+void step(void);
+u8int memread8(u32int);
+u16int memread16(u32int);
+u32int memread32(u32int);
+void memwrite(u32int, u32int, u32int);
+void syscall(u16int);
+void writem(vlong, u32int, int);
+void *emalloc(ulong);
+void sysinit(void);
+u32int readm(vlong, int);
+u64int readm64(vlong, int);
+u32int addrof(vlong);
+void writem(vlong, u32int, int);
+void writem64(vlong, u64int, int);
+vlong amode(int);
+int load(char *, char **, char **);
--- /dev/null
+++ b/sys/src/games/v8e/mem.c
@@ -1,0 +1,74 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+static Segment *
+seglook(u32int a, int n, u32int **lp)
+{
+	Segment *s;
+	
+	for(s = segs; s < segs + nelem(segs); s++)
+		if(a >= s->start && a < s->start + s->size)
+			break;
+	if(s == segs + nelem(segs)) return nil;
+	if(a + n > s->start + s->size) return nil;
+	if(lp != nil) *lp = s->data + (a - s->start >> 2);
+	return s;
+}
+
+u8int
+memread8(u32int a)
+{
+	u32int *p;
+	
+	if(seglook(a, 1, &p) == nil) sysfatal("invalid read from %.8ux (pc=%.8ux)", a, curpc);
+	switch(a & 3){
+	case 0: return *p;
+	case 1: return *p >> 8;
+	case 2: return *p >> 16;
+	case 3: return *p >> 24;
+	}
+	return 0;
+}
+
+u16int
+memread16(u32int a)
+{
+	u32int *p;
+	
+	if(seglook(a, 2, &p) == nil) sysfatal("invalid read from %.8ux (pc=%.8ux)", a, curpc);
+	switch(a & 3){
+	case 0: return *p;
+	case 1: return *p >> 8;
+	case 2: return *p >> 16;
+	case 3: return *p >> 24 | p[1] << 8;
+	}
+	return 0;
+}
+
+u32int
+memread32(u32int a)
+{
+	u32int *p;
+	
+	if(seglook(a, 4, &p) == nil) sysfatal("invalid read from %.8ux (pc=%.8ux)", a, curpc);
+	switch(a & 3){
+	case 0: return *p;
+	case 1: return *p >> 8 | p[1] << 24;
+	case 2: return *p >> 16 | p[1] << 16;
+	case 3: return *p >> 24 | p[1] << 8;
+	}
+	return *p;
+}
+
+void
+memwrite(u32int a, u32int v, u32int m)
+{
+	u32int *p;
+	Segment *s;
+	
+	s = seglook(a, 4, &p);
+	if(s == nil || (s->flags & SEGRO) != 0) sysfatal("invalid write to %.8ux=%.8ux (mask=%.8ux, pc=%.8ux)", a, v, m, curpc);
+	*p = *p & ~m | v & m;
+}
--- /dev/null
+++ b/sys/src/games/v8e/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=v8e
+HFILES=dat.h fns.h
+OFILES=\
+	v8e.$O \
+	cpu.$O \
+	cpubcd.$O \
+	cpufp.$O \
+	mem.$O \
+	sys.$O \
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/v8e/sys.c
@@ -1,0 +1,660 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+Chan chans[NCHANS];
+
+int systrace=1;
+#define dprint print
+
+static u32int
+arg(int n)
+{
+	return memread32(r[12] + (n + 1) * 4);
+}
+
+static char *
+strget(u32int addr)
+{
+	int n;
+	char *s;
+	
+	for(n = 0; memread8(addr + n) != 0; n++)
+		;
+	s = emalloc(n + 1);
+	for(n = 0; n == 0 || s[n-1] != 0; n++)
+		s[n] = memread8(addr + n);
+	return s;
+}
+
+static char **
+vecget(u32int addr)
+{
+	int n;
+	u32int u;
+	char **s;
+	
+	for(n = 0; readm(addr + n, 2) != 0; n += 4)
+		;
+	s = emalloc((n + 1) * sizeof(char *));
+	for(n = 0; u = readm(addr + n * 4, 2), u != 0; n++)
+		s[n] = strget(u);
+	return s;
+}
+
+static Chan *
+getfd(int n)
+{
+	if((unsigned)n >= NCHANS || chans[n].fd < 0)
+		return nil;
+	return &chans[n];
+}
+
+static Chan *
+newfd(void)
+{
+	Chan *c;
+
+	for(c = chans; c < chans + nelem(chans); c++)
+		if(c->fd < 0)
+			return c;
+	return nil;
+}
+
+static int
+toerrno(void)
+{
+	char buf[ERRMAX];
+	
+	rerrstr(buf, sizeof(buf));
+	if(strstr(buf, "not found")) return -ENOENT;
+	print("couldn't translate %s\n", buf);
+	return -EIO;
+}
+
+static int
+toerrnoi(int rc)
+{
+	if(rc >= 0)
+		return rc;
+	return toerrno();
+}
+
+static int
+dostat(u32int buf, Dir *d)
+{
+	int m;
+
+	if(d == nil) return toerrno();
+	writem(buf, 0, 1); /* dev */
+	writem(buf + 2, d->qid.path, 1); /* ino */
+	m = d->mode & 0777;
+	if((d->mode & DMDIR) != 0) m |= 040000;
+	else m |= 010000;
+	writem(buf + 4, m, 1); /* mode */
+	writem(buf + 6, 1, 1); /* nlink */
+	writem(buf + 8, 0, 1); /* uid */
+	writem(buf + 10, 0, 1); /* gid */
+	writem(buf + 12, d->dev, 1); /* dev */
+	writem(buf + 16, d->length, 2); /* size */
+	writem(buf + 20, d->atime, 2); /* atime */
+	writem(buf + 24, d->mtime, 2); /* mtime */
+	writem(buf + 28, d->mtime, 2); /* ctime */
+	free(d);
+	return 0;
+}
+
+static int
+sysexit(void)
+{
+	int no;
+	
+	no = arg(0);
+	if(no == 0) exits(nil);
+	exits(smprint("%d", no));
+	return 0;
+}
+
+static int
+dodirread(Chan *c)
+{
+	Dir *d, *dp;
+	int rc;
+	
+	free(c->buf);
+	c->buf = c->bufp = c->bufe = nil;	
+	rc = dirread(c->fd, &d);
+	if(rc <= 0) return rc;
+	c->bufp = c->bufe = c->buf = emalloc(16 * rc);
+	for(dp = d; --rc >= 0; dp++){
+		*c->bufe++ = dp->qid.path;
+		*c->bufe++ = dp->qid.path >> 8;
+		strncpy(c->bufe, dp->name, 14);
+		c->bufe += 14;
+	}
+	free(d);
+	return 0;
+}
+
+static int
+sysread(void)
+{
+	int fd, sz, rc, i;
+	u32int addr;
+	char *buf;
+	Chan *c;
+	
+	fd = arg(0);
+	addr = arg(1);
+	sz = arg(2);
+	if(systrace) dprint("read(%d, %#ux, %d)\n", fd, addr, sz);
+	c = getfd(fd);
+	if(sz < 0) return -EINVAL;
+	if(c == nil) return -EBADF;
+	if((c->flags & DIR) != 0){
+		if(c->bufp >= c->bufe)
+			if(dodirread(c) < 0)
+				return toerrno();
+		for(rc = 0; sz > 0 && c->bufp < c->bufe; rc++, sz--)
+			writem(addr++, *c->bufp++, 0);
+		return rc;
+	}else{
+		buf = emalloc(sz);
+		rc = read(c->fd, buf, sz);
+		for(i = 0; i < rc; i++)
+			writem(addr + i, buf[i], 0);
+		free(buf);
+		if(rc < 0) return toerrno();
+	}
+	return rc;
+}
+
+static int
+syswrite(void)
+{
+	int fd, sz, rc, i;
+	u32int addr;
+	Chan *c;
+	char *buf;
+	
+	fd = arg(0);
+	addr = arg(1);
+	sz = arg(2);
+	if(systrace) dprint("write(%d, %#ux, %d)\n", fd, addr, sz);
+	c = getfd(fd);
+	if(sz < 0) return -EINVAL;
+	if(c == nil) return -EBADF;
+	buf = emalloc(sz);
+	for(i = 0; i < sz; i++)
+		buf[i] = memread8(addr + i);
+	rc = write(c->fd, buf, sz);
+	free(buf);
+	return toerrnoi(rc);
+}
+
+static int
+sysopen(void)
+{
+	char *s;
+	Chan *c;
+	int m;
+	Dir *d;
+	
+	s = strget(arg(0));
+	m = arg(1);
+	if(systrace) dprint("open(\"%s\", %#uo)\n", s, m);
+	switch(m){
+	case 0: m = OREAD; break;
+	case 1: m = OWRITE; break;
+	case 2: m = ORDWR; break;
+	default: free(s); return -EINVAL;
+	}
+	c = newfd();
+	if(c == nil){
+		free(s);
+		return -EMFILE;
+	}
+	c->fd = open(s, m);
+	free(s);
+	if(c->fd < 0) return toerrno();
+	d = dirfstat(c->fd);
+	if(d == nil){
+		close(c->fd);
+		return toerrno();
+	}
+	if((d->mode & DMDIR) != 0)
+		c->flags |= DIR;
+	free(d);
+	return c - chans;
+}
+
+static int
+sysclose(void)
+{
+	int fd;
+	Chan *c;
+	
+	fd = arg(0);
+	if(systrace) dprint("close(%d)\n", fd);
+	c = getfd(fd);
+	if(c == nil) return -EBADF;
+	if((c->flags & DONTCLOSE) == 0)
+		close(c->fd);
+	c->fd = -1;
+	return 0;
+}
+
+static int
+sysfstat(void)
+{
+	int fd, buf;
+	Chan *c;
+	Dir *d;
+	
+	fd = arg(0);
+	buf = arg(1);
+	if(systrace) dprint("fstat(%d, %#ux)\n", fd, buf);
+	c = getfd(fd);
+	if(c == nil) return -EBADF;
+	d = dirfstat(c->fd);
+	return dostat(buf, d);
+}
+
+static int
+syslstat(void)
+{
+	char *s;
+	int buf;
+	Dir *d;
+	
+	s = strget(arg(0));
+	buf = arg(1);
+	if(systrace) dprint("lstat(\"%s\", %#ux)\n", s, buf);
+	d = dirstat(s);
+	free(s);
+	return dostat(buf, d);
+}
+
+static int
+sysioctl(void)
+{
+	int fd, ctl;
+	u32int addr;
+	Chan *c;
+	
+	fd = arg(0);
+	ctl = arg(1);
+	addr = arg(2);
+	if(systrace) dprint("lstat(%d, %#ux, %#ux)\n", fd, ctl, addr);
+	c = getfd(fd);
+	if(c == nil) return -EBADF;
+	switch(ctl){
+	case 't'<<8|8:
+		if((c->flags & FAKETTY) != 0){
+			writem(addr, 13 | 13<<8 | '#'<<16 | '@'<<24, 2);
+			writem(addr + 4, 06010, 2);
+			return 0;
+		}
+		return -ENOTTY;
+	case 'j'<<8|3: return -EINVAL;
+	default:
+		fprint(2, "unknown ioctl %c%d\n", ctl >> 8, (u8int)ctl);
+		return -EINVAL;
+	}
+}
+
+static int
+systime(void)
+{
+	u32int addr;
+	int t;
+	
+	addr = arg(0);
+	if(systrace) dprint("time(%#ux)\n", addr);
+	t = time(0);
+	if(addr != 0) writem(addr, t, 2);
+	return t;
+}
+
+static int
+sysbreak(void)
+{
+	u32int a;
+	int ns;
+	
+	a = arg(0);
+	if(systrace) dprint("break(%#ux)\n", a);
+	a = -(-a & -1024);
+	ns = a - segs[1].start;
+	if(ns > segs[1].size){
+		segs[1].data = realloc(segs[1].data, ns);
+		memset((uchar *) segs[1].data + segs[1].size, 0, ns - segs[1].size);
+		segs[1].size = ns;
+	}
+	return 0;
+}
+
+static int
+sysftime(void)
+{
+	u32int p;
+	vlong n;
+	Tm *t;
+	
+	p = arg(0);
+	if(systrace) dprint("ftime(%#ux)\n", p);
+	n = nsec();
+	n /= 1000000;
+	writem(p + 4, n % 1000, 1);
+	n /= 1000;
+	writem(p, n, 2);
+	t = localtime(n);
+	writem(p + 6, -t->tzoff / 60, 1);
+	writem(p + 8, 0, 1);
+	return 0;
+}
+
+static int
+syssignal(void)
+{
+	return 0;
+}
+
+static int
+sysgetpid(void)
+{
+	if(systrace) dprint("getpid()\n");
+	return getpid() & 0xffff;
+}
+
+static int
+sysaccess(void)
+{
+	char *s;
+	int m, rc;
+	
+	s = strget(arg(0));
+	m = arg(1);
+	if(systrace) dprint("access(\"%s\", %#ux)\n", s, m);
+	rc = access(s, m & 7);
+	free(s);
+	return toerrnoi(rc);	
+}
+
+static int
+syscreat(void)
+{
+	char *s;
+	Chan *c;
+	int m;
+	
+	s = strget(arg(0));
+	m = arg(1);
+	if(systrace) dprint("creat(\"%s\", %#uo)\n", s, m);
+	c = newfd();
+	if(c == nil){
+		free(s);
+		return -EMFILE;
+	}
+	c->fd = create(s, OWRITE, m & 0777);
+	free(s);
+	if(c->fd < 0) return toerrno();
+	return c - chans;
+}
+
+static int
+sysseek(void)
+{
+	int fd, off, wh;
+	Chan *c;
+	
+	fd = arg(0);
+	off = arg(1);
+	wh = arg(2);
+	if(systrace) dprint("seek(%d, %d, %d)\n", fd, off, wh);
+	c = getfd(fd);
+	if(c == nil || off < 0 || (uint)wh > 2) return -EBADF;
+	return toerrnoi(seek(c->fd, off, wh));
+}
+
+static int
+sysunlink(void)
+{
+	char *s;
+	int rc;
+	
+	s = strget(arg(0));
+	if(systrace) dprint("unlink(\"%s\")\n", s);
+	rc = remove(s);
+	free(s);
+	return toerrnoi(rc);
+}
+
+static int
+syschdir(void)
+{
+	char *s;
+	int rc;
+	
+	s = strget(arg(0));
+	if(systrace) dprint("chdir(\"%s\")\n", s);
+	rc = chdir(s);
+	free(s);
+	return toerrnoi(rc);
+}
+
+static int
+sysgetuid(void)
+{
+	return 0;
+}
+
+static int
+sysfork(void)
+{
+	int rc;
+	
+	if(systrace) dprint("fork()\n");
+	rc = fork();
+	if(rc < 0) return toerrno();
+	if(rc == 0){
+		r[1] = 1;
+		return getppid();
+	}
+	r[1] = 0;
+	return rc;
+}
+
+static int
+sysexece(void)
+{
+	char *file, **argv, **env, **p;
+	int rc;
+	
+	file = strget(arg(0));
+	argv = vecget(arg(1));
+	env = vecget(arg(2));
+	if(systrace) dprint("exece(\"%s\", ..., ...)\n", file);
+	rc = load(file, argv, env);
+	for(p = argv; *p != nil; p++)
+		free(*p);
+	for(p = env; *p != nil; p++)
+		free(*p);
+	free(file);
+	free(argv);
+	free(env);
+	return toerrnoi(rc);
+}
+
+static int
+syswait(void)
+{
+	Waitmsg *w;
+	int rc, st;
+	u32int addr;
+	char *p;
+	
+	addr = arg(0);
+	if(systrace) dprint("wait(%#ux)\n", addr);
+	w = wait();
+	if(w == nil) return toerrno();
+	rc = w->pid;
+	if(addr != 0){
+		st = strtol(w->msg, &p, 10) & 255 << 8;
+		if(*p == 0) st = 127 << 8;
+		writem(addr, st, 2);
+	}
+	free(w);
+	return rc;
+}
+
+int mask = 022;
+static int
+sysumask(void)
+{
+	int rc;
+	
+	rc = mask;
+	mask = arg(0);
+	if(systrace) dprint("umask(%#uo)\n", mask);
+	return rc;
+}
+
+static int
+syslink(void)
+{
+	char *a, *b;
+	int f0, f1, rc, n;
+	char buf[8192];
+	
+	a = strget(arg(0));
+	b = strget(arg(1));
+	if(systrace) dprint("link(\"%s\", \"%s\")\n", a, b);
+	f0 = open(a, OREAD);
+	f1 = create(b, OWRITE | OEXCL, 0777 ^ mask);
+	if(f0 < 0 || f1 < 0) {
+	err:
+		rc = toerrno();
+		goto out;
+	}
+	for(;;){
+		n = read(f0, buf, sizeof(buf));
+		if(n < 0) goto err;
+		if(n == 0) break;
+		if(write(f1, buf, n) < n) goto err;
+	}
+	rc = 0;
+out:
+	if(f0 >= 0) close(f0);
+	if(f1 >= 0) close(f1);
+	free(a);
+	free(b);
+	return rc;
+}
+
+static int
+syschmod(void)
+{
+	char *a;
+	int mode;
+	Dir d;
+	Dir *e;
+	
+	a = strget(arg(0));
+	mode = arg(1);
+	if(systrace) dprint("chmod(\"%s\", %#uo)\n", a, mode);
+	e = dirstat(a);
+	if(e == nil){
+		free(a);
+		return toerrno();
+	}
+	nulldir(&d);
+	d.mode = e->mode & ~0777 | mode & 0777;
+	free(e);
+	if(dirwstat(a, &d) < 0){
+		free(a);
+		return toerrno();
+	}
+	free(a);
+	return 0;
+}
+
+static int
+sysdup(void)
+{
+	int fd;
+	Chan *c, *d;
+	
+	fd = arg(0);
+	if(systrace) dprint("dup(%d)\n", fd);
+	c = getfd(fd);
+	if(c == nil) return -EBADF;
+	d = newfd();
+	if(d == nil) return -EMFILE;
+	d->fd = c->fd;
+	d->flags = c->flags;
+	return d - chans;
+}
+
+void
+syscall(u16int c)
+{
+	int rc;
+
+	static int (*calls[])(void) = {
+		[1] sysexit,
+		[2] sysfork,
+		[3] sysread,
+		[4] syswrite,
+		[5] sysopen,
+		[6] sysclose,
+		[7] syswait,
+		[8] syscreat,
+		[9] syslink,
+		[10] sysunlink,
+		[12] syschdir,
+		[13] systime,
+		[15] syschmod,
+		[17] sysbreak,
+		[18] syslstat,
+		[19] sysseek,
+		[20] sysgetpid,
+		[24] sysgetuid,
+		[28] sysfstat,
+		[33] sysaccess,
+		[35] sysftime,
+		[40] syslstat,
+		[41] sysdup,
+		[48] syssignal,
+		[54] sysioctl,
+		[59] sysexece,
+		[60] sysumask,
+		[66] sysfork,
+	};
+	
+	if(c >= nelem(calls) || calls[c] == nil) sysfatal("unknown syscall %d", c);
+	rc = calls[c]();
+	if(rc < 0){
+		r[0] = -rc;
+		ps |= 1;
+	}else{
+		r[0] = rc;
+		ps &= ~1;
+	}
+}
+
+void
+sysinit(void)
+{
+	int i;
+	
+	for(i = 0; i < NCHANS; i++)
+		chans[i].fd = -1;
+	chans[0].fd = 0;
+	chans[0].flags = DONTCLOSE|FAKETTY;
+	chans[1].fd = 1;
+	chans[1].flags = DONTCLOSE|FAKETTY;
+	chans[2].fd = 2;
+	chans[2].flags = DONTCLOSE|FAKETTY;
+}
--- /dev/null
+++ b/sys/src/games/v8e/v8e.c
@@ -1,0 +1,205 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <ctype.h>
+#include "dat.h"
+#include "fns.h"
+
+Segment segs[3];
+
+void *
+emalloc(ulong n)
+{
+	void *v;
+	
+	v = malloc(n);
+	if(v == nil) sysfatal("malloc: %r");
+	memset(v, 0, n);
+	setmalloctag(v, getcallerpc(&n));
+	return v;
+}
+
+enum {
+	OMAGIC = 0407,
+	NMAGIC = 0410,
+	ZMAGIC = 0413,
+};
+
+static int
+readn32(int fd, u32int *l, int sz)
+{
+	static uchar buf[8192];
+	uchar *p;
+	int n, rc;
+	
+	while(sz > 0){
+		n = 8192;
+		if(n > sz) n = sz;
+		rc = readn(fd, buf, n);
+		if(rc < 0) return -1;
+		if(rc < n){
+			werrstr("unexpected eof");
+			return -1;
+		}
+		sz -= n;
+		p = buf;
+		for(; n >= 4; n -= 4){
+			*l++ = U32(p);
+			p += 4;
+		}
+		switch(n){
+		case 1: *l = p[0]; break;
+		case 2: *l = p[0] | p[1] << 8; break;
+		case 3: *l = p[0] | p[1] << 8 | p[2] << 16; break;
+		}
+	}
+	return 0;
+}
+
+static void
+setupstack(char **argv)
+{
+	u32int *nargv;
+	int i, j;
+	int argc;
+
+	r[14] = 0x7ffff400;
+	#define push32(x) { r[14] -= 4; memwrite(r[14], x, -1); }
+	for(argc = 0; argv[argc] != nil; argc++)
+		;
+	nargv = emalloc(sizeof(u32int) * argc);
+	for(i = argc; --i >= 0; ){
+		r[14] -= strlen(argv[i]) + 1;
+		nargv[i] = r[14];
+		for(j = 0; argv[i][j] != 0; j++)
+			writem(r[14] + j, argv[i][j], 0);
+	}
+	r[14] &= -4;
+	push32(0);
+	push32(0);
+	push32(0);
+	for(i = argc; --i >= 0; )
+		push32(nargv[i]);
+	push32(argc);
+	free(nargv);
+}
+
+static int
+shload(int fd, char *file, char **argv, char **envp)
+{
+	char buf[256];
+	char *s, *a;
+	char *p;
+	int rc;
+	char **nargv, **pp;
+	int n;
+	
+	rc = read(fd, buf, sizeof(buf) - 1);
+	if(rc <= 0){
+		werrstr("invalid magic");
+		return -1;
+	}
+	close(fd);
+	buf[rc] = 0;
+	p = strchr(buf, '\n');
+	if(p == nil) *p = 0;
+	p = buf;
+	while(isspace(*p)) p++;
+	s = p;
+	while(*p != 0 && !isspace(*p)) p++;
+	if(*p != 0){
+		*p = 0;
+		while(isspace(*p)) p++;
+		if(*p != 0){
+			a = p;
+			while(*p != 0 && !isspace(*p)) p++;
+		}else a = nil;
+	}else a = nil;
+	for(n = 0; argv[n] != nil; n++)
+		;
+	nargv = emalloc((n + 3) * sizeof(char *));
+	pp = nargv;
+	*pp++ = s;
+	if(a != nil) *pp++ = a;
+	while(n--)
+		*pp++ = *argv++;
+	load(s, nargv, envp);
+	free(nargv);
+	return 0;
+}
+
+int
+load(char *file, char **argv, char **envp)
+{
+	uchar hdr[32];
+	int fd;
+	u32int hmagic, htext, hdata, hbss, hentry;
+	
+	fd = open(file, OREAD);
+	if(fd < 0) return -1;
+	if(readn(fd, hdr, 2) < 2) return -1;
+	if(hdr[0] == '#' && hdr[1] == '!')
+		return shload(fd, file, argv, envp);
+	if(readn(fd, hdr + 2, 30) < 30) return -1;
+	hmagic = U32(&hdr[0]);
+	htext = U32(&hdr[4]);
+	hdata = U32(&hdr[8]);
+	hbss = U32(&hdr[12]);
+	hentry = U32(&hdr[20]);
+	switch(hmagic){
+	case ZMAGIC: case OMAGIC: case NMAGIC: break;
+	default:
+		werrstr("invalid magic %.6o", hmagic);
+		return -1;
+	}
+	free(segs[0].data);
+	free(segs[1].data);
+	free(segs[2].data);
+	segs[0].start = 0;
+	segs[0].size = htext;
+	segs[0].data = emalloc(-(-htext & -4));
+	segs[1].start = -(-htext & -1024);
+	segs[1].size = hdata + hbss;
+	segs[1].data = emalloc(-(-(hdata + hbss) & -4));
+	segs[2].start = 0x7ffff400 - STACKSIZE;
+	segs[2].size = STACKSIZE;
+	segs[2].data = emalloc(STACKSIZE);
+	if(hmagic != OMAGIC)
+		segs[0].flags = SEGRO;
+	if(hmagic == ZMAGIC)
+		seek(fd, 1024, 0);
+	if(readn32(fd, segs[0].data, htext) < 0) exits(smprint("%r"));
+	if(readn32(fd, segs[1].data, hdata) < 0) exits(smprint("%r"));
+	close(fd);
+	memset(r, 0, sizeof(r));
+	r[15] = hentry + 2;
+	setupstack(argv);
+	return 0;
+}
+
+static void
+usage(void)
+{
+	sysfatal("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	extern void fptest(void);
+	
+	rfork(RFNAMEG);
+	fptest();
+	ARGBEGIN{
+	case 'N':
+		if(addns(nil, EARGF(usage())) < 0)
+			sysfatal("addns: %r");
+		break;
+	default: usage();
+	}ARGEND;
+	
+	if(argc < 1) usage();
+	sysinit();
+	if(load(argv[0], argv, nil) < 0) sysfatal("load: %r");
+	for(;;) step();
+}