#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);
	}
}

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);
	}
}

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);
	}
}

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);
	}
}

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);
	}
}

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);
	}
}

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);
	}
	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);
	}
	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);
	}
}
