shithub: riscv

Download patch

ref: 85a414751a1b9ba09dd7a72eee8746d81a7dcf53
parent: eed487167489ee16dd16effe9ebff9a5086ffc70
author: aiju <devnull@localhost>
date: Thu Mar 13 16:07:36 EDT 2014

added games/snes

diff: cannot open b/sys/src/games/snes//null: file does not exist: 'b/sys/src/games/snes//null'
--- /dev/null
+++ b/sys/src/games/snes/cpu.c
@@ -1,0 +1,961 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int rP, emu, irq, nmi, dma;
+u16int rA, rX, rY, rS, rD, pc;
+u32int rDB, rPB, curpc, hdma;
+static u8int m8, x8;
+static int cyc;
+static u32int lastpc;
+
+static u8int
+fetch8(void)
+{
+	return memread(pc++ | rPB);
+}
+
+static u16int
+fetch16(void)
+{
+	u16int r;
+	
+	r = memread(pc++ | rPB);
+	r |= memread(pc++ | rPB) << 8;
+	return r;
+}
+
+static u16int
+mem16(u32int a)
+{
+	u16int r;
+
+	r = memread(a++);
+	r |= memread(a) << 8;
+	return r;
+}
+
+static u16int
+mem816(u32int a, u16int v)
+{
+	if(m8)
+		return memread(a) | v;
+	cyc++;
+	return mem16(a);
+}
+
+static u16int
+memx816(u32int a)
+{
+	if(x8)
+		return memread(a);
+	cyc++;
+	return mem16(a);
+}
+
+
+static void
+memw816(u32int a, u16int v)
+{
+	memwrite(a, v);
+	if(m8)
+		return;
+	memwrite(++a, v >> 8);
+	cyc++;
+}
+
+static void
+memwx816(u32int a, u16int v)
+{
+	memwrite(a, v);
+	if(x8)
+		return;
+	memwrite(++a, v >> 8);
+	cyc++;
+}
+
+static void
+push8(u8int a)
+{
+	memwrite(rS, a);
+	if(emu && (rS & 0xFF) == 0)
+		rS |= 0xFF;
+	else
+		rS--;
+}
+
+static u8int
+pop8(void)
+{
+	if(emu && (rS & 0xFF) == 0xFF)
+		rS &= ~0xFF;
+	else
+		rS++;
+	return memread(rS);
+}
+
+static void
+push16(u16int a)
+{
+	push8(a >> 8);
+	push8(a);
+}
+
+static u16int
+pop16(void)
+{
+	u16int r;
+	
+	r = pop8();
+	r |= pop8() << 8;
+	return r;
+}
+
+static void
+push816(u16int v, int m)
+{
+	if(!m){
+		push8(v >> 8);
+		cyc++;
+	}
+	push8(v);
+}
+
+static u16int
+pop816(u16int a, int m)
+{
+	u16int r;
+
+	r = pop8();
+	if(m)
+		return r | a;
+	cyc++;
+	r |= pop8() << 8;
+	return r;
+}
+
+static u16int
+nz8(u16int v)
+{
+	rP &= ~(FLAGN | FLAGZ);
+	if((v & 0xFF) == 0)
+		rP |= FLAGZ;
+	rP |= (v & 0x80);
+	return v;
+}
+
+static u16int
+nz16(u16int v)
+{
+	rP &= ~(FLAGN | FLAGZ);
+	if(v == 0)
+		rP |= FLAGZ;
+	if((v & 0x8000) != 0)
+		rP |= FLAGN;
+	return v;
+}
+
+static u16int
+nz(u16int v)
+{
+	if(m8)
+		return nz8(v);
+	return nz16(v);
+}
+
+static u16int
+nzx(u16int v)
+{
+	if(x8)
+		return nz8(v);
+	return nz16(v);
+}
+
+static u16int
+imm(int a)
+{
+	if(m8)
+		return fetch8() | a;
+	cyc++;
+	return fetch16();
+}
+
+static u16int
+immx(int a)
+{
+	if(x8)
+		return fetch8() | a;
+	cyc++;
+	return fetch16();
+}
+
+static u32int
+abso(int l, int x)
+{
+	u32int p;
+	
+	p = fetch16();
+	if(l)
+		p |= fetch8() << 16;
+	else
+		p |= rDB;
+	switch(x){
+	case 1: p += rX; break;
+	case 2: p += rY; break;
+	}
+	return p;
+}
+
+static u32int
+absi(int x)
+{
+	u16int p;
+	u32int b, r;
+	
+	p = fetch16();
+	if(x){
+		p += rX;
+		b = rPB;
+	}else
+		b = 0;
+	r = memread(p++ | b);
+	r |= memread(p | b) << 8;
+	return r;
+}
+
+static u32int
+dp(int x)
+{
+	u32int p;
+	
+	if((rD & 0xFF) != 0)
+		cyc++;
+	p = fetch8();
+	switch(x){
+	case 1: p += rX; break;
+	case 2: p += rY; break;
+	}
+	if(emu && (rD & 0xFF) == 0)
+		p = rD & 0xFF00 | p & 0xFF;
+	else
+		p = (p + rD) & 0xFFFF;
+	return p;
+}
+
+static u32int
+dpi(int l, int x, int y)
+{
+	u32int p, r, s;
+	u32int b;
+	
+	p = dp(x);
+	r = memread(p++);
+	if(emu && (rD & 0xFF) == 0){
+		if((p & 0xFF) == 0)
+			p -= 0x100;
+	}else
+		p &= 0xFFFF;
+	r |= memread(p++) << 8;
+	if(l){
+		if(emu && (rD & 0xFF) == 0){
+			if((p & 0xFF) == 0)
+				p -= 0x100;
+		}else
+			p &= 0xFFFF;
+		b = memread(p) << 16;
+	}else
+		b = rDB;
+	if(y){
+		s = r + rY;
+		if(x8 && ((r ^ s) & 0xFF00) != 0)
+			cyc++;
+		r = s;
+	}
+	r += b;
+	return r;
+}
+
+static u32int
+sr(void)
+{
+	return (rS + fetch8()) & 0xFFFF;
+}
+
+static u32int
+sry(void)
+{
+	return (mem16((rS + fetch8()) & 0xFFFF) | rDB) + rY;
+}
+
+static void
+rmw(u32int a, u16int, u16int w)
+{
+	memw816(a, w);
+	nz(w);
+}
+
+static int
+branch(void)
+{
+	signed char t;
+	u16int npc;
+	
+	t = fetch8();
+	npc = pc + t;
+	if(emu && (npc ^ pc) >> 8){
+		pc = npc;
+		return 4;
+	}
+	pc = npc;
+	return 3;
+}
+
+static void
+setrp(u8int v)
+{
+	if(emu)
+		v |= 0x30;
+	else if((v & 0x10) != 0){
+		rX &= 0xff;
+		rY &= 0xff;
+	}
+	rP = v;
+}
+
+static void
+adc(u16int a)
+{
+	int r;
+
+	if((rP & FLAGD) != 0)
+		print("decimal mode\n");
+	if(m8){
+		r = (rA & 0xff) + a + (rP & FLAGC);
+		rP &= ~(FLAGC | FLAGN | FLAGV | FLAGZ);
+		if(r > 0xFF)
+			rP |= FLAGC;
+		rP |= r & 0x80;
+		if((~(rA ^ a) & (rA ^ r)) & 0x80)
+			rP |= FLAGV;
+		r &= 0xFF;
+		if(r == 0)
+			rP |= FLAGZ;
+		rA = rA & 0xFF00 | r;
+	}else{
+		r = rA + a + (rP & FLAGC);
+		rP &= ~(FLAGC | FLAGN | FLAGV | FLAGZ);
+		if(r > 0xFFFF)
+			rP |= FLAGC;
+		if((r & 0x8000) != 0)
+			rP |= FLAGN;
+		if((~(rA ^ a) & (rA ^ r)) & 0x8000)
+			rP |= FLAGV;
+		rA = r;
+		if(rA == 0)
+			rP |= FLAGZ;
+	}
+}
+
+static void
+asl(u32int a)
+{
+	u16int v;
+
+	v = mem816(a, 0);
+	rP &= ~FLAGC;
+	rP |= v >> (m8 ? 7 : 15);
+	rmw(a, v, v << 1);
+}
+
+static void
+bit(u16int a)
+{
+	rP &= ~(FLAGN | FLAGZ | FLAGV);
+	if((a & rA) == 0)
+		rP |= FLAGZ;
+	if(m8)
+		rP |= a & 0xC0;
+	else
+		rP |= (a >> 8) & 0xC0;
+}
+
+static void
+block(int incr)
+{
+	u32int sb;
+	
+	rDB = fetch8() << 16;
+	sb = fetch8() << 16;
+	memwrite(rDB | rY, memread(sb | rX));
+	if(incr){
+		rX++;
+		rY++;
+	}else{
+		rX--;
+		rY--;
+	}
+	if(x8){
+		rX &= 0xff;
+		rY &= 0xff;
+	}
+	if(rA-- != 0)
+		pc -= 3;
+}
+
+static void
+cmp(u16int a, u16int b, int m)
+{
+	if(m){
+		a &= 0xff;
+		b &= 0xff;
+	}
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	if(a == b)
+		rP |= FLAGZ;
+	if(a >= b)
+		rP |= FLAGC;
+	if((a - b) & (m ? 0x80 : 0x8000))
+		rP |= FLAGN;
+}
+
+static void
+dec(u32int a)
+{
+	u16int v;
+	
+	v = mem816(a, 0);
+	rmw(a, v, v-1);
+}
+
+static void
+inc(u32int a)
+{
+	u16int v;
+
+	v = mem816(a, 0);
+	rmw(a, v, v+1);
+}
+
+static void
+lsr(u32int a)
+{
+	u16int v;
+	
+	v = mem816(a, 0);
+	rP &= ~FLAGC;
+	rP |= v & 1;
+	rmw(a, v, v>>1);
+}
+
+static void
+rol(u32int a)
+{
+	u16int v, w;
+	
+	v = rP & FLAGC;
+	w = mem816(a, 0);
+	rP &= ~FLAGC;
+	rP |= w >> (m8 ? 7 : 15);
+	rmw(a, w, w<<1 | v);
+}
+
+static void
+ror(u32int a)
+{
+	u16int v, w;
+	
+	v = (rP & FLAGC) << (m8 ? 7 : 15);
+	w = mem816(a, 0);
+	rP &= ~FLAGC;
+	rP |= w & 1;
+	rmw(a, w, w>>1 | v);
+}
+
+static void
+sbc(u16int a)
+{
+	int r;
+
+	if((rP & FLAGD) != 0)
+		print("decimal mode\n");
+	if(m8){
+		r = (rA & 0xff) + (a ^ 0xff) + (rP & FLAGC);
+		rP &= ~(FLAGC | FLAGN | FLAGV | FLAGZ);
+		if(r > 0xFF)
+			rP |= FLAGC;
+		rP |= r & 0x80;
+		if(((rA ^ a) & (rA ^ r)) & 0x80)
+			rP |= FLAGV;
+		r &= 0xFF;
+		if(r == 0)
+			rP |= FLAGZ;
+		rA = rA & 0xFF00 | r;
+	}else{
+		r = rA + (a ^ 0xffff) + (rP & FLAGC);
+		rP &= ~(FLAGC | FLAGN | FLAGV | FLAGZ);
+		if(r > 0xFFFF)
+			rP |= FLAGC;
+		if((r & 0x8000) != 0)
+			rP |= FLAGN;
+		if(((rA ^ a) & (rA ^ r)) & 0x8000)
+			rP |= FLAGV;
+		rA = r;
+		if(rA == 0)
+			rP |= FLAGZ;
+	}
+
+}
+
+static void
+setra(u16int a)
+{
+	if(m8)
+		rA = rA & 0xff00 | nz8(a & 0xff);
+	else
+		rA = nz16(a);
+}
+
+static void
+setx(u16int a, u16int *b)
+{
+	if(x8)
+		*b = nz8(a & 0xff);
+	else
+		*b = nz16(a);
+}
+
+static void
+tsb(u32int a, int set)
+{
+	u16int v;
+
+	v = mem816(a, 0);
+	rP &= ~FLAGZ;
+	if(m8){
+		if((rA & v & 0xFF) == 0)
+			rP |= FLAGZ;
+	}else
+		if((rA & v) == 0)
+			rP |= FLAGZ;
+	if(set)
+		memw816(a, v | rA);
+	else
+		memw816(a, v & ~rA);
+}
+
+enum { COP = 0, BRK = 1, NMI = 3, IRQ = 5 };
+
+static void
+interrupt(int src)
+{
+	if(!emu)
+		push8(rPB >> 16);
+	push16(pc);
+	if(emu && src != BRK)
+		push8(rP & ~(1<<5));
+	else
+		push8(rP);
+	if(emu && src == BRK)
+		src = IRQ;
+	pc = mem16(0xffe4 + src * 2 + emu * 0x10);
+	rP |= FLAGI;
+	rP &= ~FLAGD;
+	rPB = 0;
+	if(emu)
+		rDB = 0;
+}
+
+void
+cpureset(void)
+{
+	pc = mem16(0xfffc);
+	rD = 0;
+	rDB = 0;
+	rPB = 0;
+	rS = 0x100;
+	rP = 0x35;
+}
+
+int trace;
+
+int
+cpustep(void)
+{
+	u8int op;
+	int a;
+	static int cnt;
+
+	if(nmi)
+		if(--nmi == 0){
+			interrupt(NMI);
+			return 8 - emu;
+		}
+	if((hdma & 0xffff) != 0){
+		curpc = -1;
+		return hdmastep();
+	}
+	if(dma){
+		curpc = -1;
+		return dmastep();
+	}
+	if(irq && (rP & FLAGI) == 0){
+		interrupt(IRQ);
+		return 8 - emu;
+	}
+	curpc = pc|rPB;
+	m8 = (rP & FLAGM) != 0;
+	x8 = (rP & FLAGX) != 0;
+	op = fetch8();
+	if(op == 0)
+		print("BRK PC=%.6x from PC=%.6x\n", curpc, lastpc);
+	lastpc = curpc;
+	if(trace)
+		print("%.6x %.2x A=%.4x X=%.4x Y=%.4x P=%.2x %.2x %x\n", curpc, op, rA, rX, rY, rP, rS, memread(0x05));
+	cyc = 0;
+	switch(op){
+	case 0x00: pc++; interrupt(BRK); return 8 - emu;
+	case 0x01: nz(rA |= mem816(dpi(0, 1, 0), 0)); return 6+cyc;
+	case 0x02: pc++; interrupt(COP); return 8 - emu;
+	case 0x03: nz(rA |= mem816(sr(), 0)); return 4+cyc;
+	case 0x04: tsb(dp(0), 1); return 5+cyc;
+	case 0x05: nz(rA |= mem816(dp(0), 0)); return 3+cyc;
+	case 0x06: asl(dp(0)); return 5+cyc;
+	case 0x07: nz(rA |= mem816(dpi(1, 0, 0), 0)); return 6+cyc;
+	case 0x08: push8(rP); return 3+cyc;
+	case 0x09: nz(rA |= imm(0)); return 3+cyc;
+	case 0x0A:
+		rP &= ~FLAGC;
+		if(m8){
+			rP |= (rA >> 7) & 1;
+			rA = (rA & 0xFF00) | ((rA << 1) & 0xFF);
+		}else{
+			rP |= (rA >> 15) & 1;
+			rA <<= 1;
+		}
+		nz(rA);
+		return 2;
+	case 0x0B: push16(rD); return 4+cyc;
+	case 0x0C: tsb(abso(0, 0), 1); return 6+cyc;
+	case 0x0D: nz(rA |= mem816(abso(0, 0), 0)); return 4+cyc;
+	case 0x0E: asl(abso(0, 0)); return 6+cyc;
+	case 0x0F: nz(rA |= mem816(abso(1, 0), 0)); return 5+cyc;
+	case 0x10: if((rP & FLAGN) == 0) return branch(); pc++; return 2;
+	case 0x11: nz(rA |= mem816(dpi(0, 0, 1), 0)); return 5+cyc;
+	case 0x12: nz(rA |= mem816(dpi(0, 0, 0), 0)); return 5+cyc;
+	case 0x13: nz(rA |= mem816(sry(), 0)); return 7+cyc;
+	case 0x14: tsb(dp(0), 0); return 5+cyc;
+	case 0x15: nz(rA |= mem816(dp(1), 0)); return 4+cyc;
+	case 0x16: asl(dp(1)); return 6+cyc;
+	case 0x17: nz(rA |= mem816(dpi(1, 0, 1), 0)); return 6+cyc;
+	case 0x18: rP &= ~FLAGC; return 2;
+	case 0x19: nz(rA |= mem816(abso(0, 2), 0)); return 4+cyc;
+	case 0x1A:
+		if(m8 && (rA & 0xFF) == 0xFF)
+			rA &= ~0xFF;
+		else
+			rA++;
+		nz(rA);
+		return 2;
+	case 0x1B: rS = rA; if(emu) rS = rS & 0xff | 0x100; return 2;
+	case 0x1C: tsb(abso(0, 0), 0); return 6+cyc;
+	case 0x1D: nz(rA |= mem816(abso(0, 1), 0)); return 4+cyc;
+	case 0x1E: asl(abso(0, 1)); return 7+cyc;
+	case 0x1F: nz(rA |= mem816(abso(1, 1), 0)); return 4+cyc;
+	case 0x20: push16(pc+1); pc = fetch16(); return 6+cyc;
+	case 0x21: nz(rA &= mem816(dpi(0, 1, 0), 0xFF00)); return 6+cyc;
+	case 0x22: push8(rPB>>16); push16(pc+2); a = fetch16(); rPB = fetch8()<<16; pc = a; return 8+cyc;
+	case 0x23: nz(rA &= mem816(sr(), 0xFF00)); return 4+cyc;
+	case 0x24: bit(mem816(dp(0), 0)); return 3+cyc;
+	case 0x25: nz(rA &= mem816(dp(0), 0xFF00)); return 3+cyc;
+	case 0x26: rol(dp(0)); return 5+cyc;
+	case 0x27: nz(rA &= mem816(dpi(1, 0, 0), 0xFF00)); return 6+cyc;
+	case 0x28: setrp(pop8()); return 5+cyc;
+	case 0x29: nz(rA &= imm(0xFF00)); return 2+cyc;
+	case 0x2A:
+		a = rP & FLAGC;
+		rP &= ~FLAGC;
+		if(m8){
+			rP |= (rA >> 7) & 1;
+			rA = (rA & 0xFF00) | ((rA << 1) & 0xFF) | a;
+		}else{
+			rP |= (rA >> 15) & 1;
+			rA = (rA << 1) | a;
+		}
+		nz(rA);
+		return 2;
+	case 0x2B: nz16(rD = pop16()); return 5;
+	case 0x2C: bit(mem816(abso(0, 0), 0)); return 4+cyc;
+	case 0x2D: nz(rA &= mem816(abso(0, 0), 0xFF00)); return 4+cyc;
+	case 0x2E: rol(abso(0, 0)); return 6+cyc;
+	case 0x2F: nz(rA &= mem816(abso(1, 0), 0xFF00)); return 5+cyc;
+	case 0x30: if((rP & FLAGN) != 0) return branch(); pc++; return 2;
+	case 0x31: nz(rA &= mem816(dpi(0, 0, 1), 0xFF00)); return 5+cyc;
+	case 0x32: nz(rA &= mem816(dpi(0, 0, 0), 0xFF00)); return 5+cyc;
+	case 0x33: nz(rA &= mem816(sry(), 0xFF00)); return 7+cyc;
+	case 0x34: bit(mem816(dp(1), 0)); return 4+cyc;
+	case 0x35: nz(rA &= mem816(dp(1), 0xFF00)); return 4+cyc;
+	case 0x36: rol(dp(1)); return 6+cyc;
+	case 0x37: nz(rA &= mem816(dpi(1, 0, 1), 0xFF00)); return 6+cyc;
+	case 0x38: rP |= FLAGC; return 2;
+	case 0x39: nz(rA &= mem816(abso(0, 2), 0xFF00)); return 4+cyc;
+	case 0x3A:
+		if(m8 && (rA & 0xFF) == 0)
+			rA |= 0xFF;
+		else
+			rA--;
+		nz(rA);
+		return 2;
+	case 0x3B: nz(rA = rS); return 2;
+	case 0x3C: bit(mem816(abso(0, 1), 0)); return 4+cyc;
+	case 0x3D: nz(rA &= mem816(abso(0, 1), 0xFF00)); return 4+cyc;
+	case 0x3E: rol(abso(0, 1)); return 7+cyc;
+	case 0x3F: nz(rA &= mem816(abso(1, 1), 0xFF00)); return 4+cyc;
+	case 0x40:
+		setrp(pop8());
+		pc = pop16();
+		if(!emu)
+			rPB = pop8() << 16;
+		return 7 - emu;
+	case 0x41: nz(rA ^= mem816(dpi(0, 1, 0), 0)); return 6+cyc;
+	case 0x42: fetch8(); return 2;
+	case 0x43: nz(rA ^= mem816(sr(), 0)); return 4+cyc;
+	case 0x44: block(0); return 7;
+	case 0x45: nz(rA ^= mem816(dp(0), 0)); return 3+cyc;
+	case 0x46: lsr(dp(0)); return 5+cyc;
+	case 0x47: nz(rA ^= mem816(dpi(1, 0, 0), 0)); return 6+cyc;
+	case 0x48: push816(rA, m8); return 3+cyc;
+	case 0x49: nz(rA ^= imm(0)); return 2+cyc;
+	case 0x4A:
+		rP &= ~FLAGC;
+		rP |= rA & 1;
+		if(m8)
+			rA = rA & 0xFF00 | (rA >> 1) & 0x7F;
+		else
+			rA >>= 1;
+		nz(rA);
+		return 2;
+	case 0x4B: push8(rPB >> 16); return 3;
+	case 0x4C: pc = fetch16(); return 3;
+	case 0x4D: nz(rA ^= mem816(abso(0, 0), 0)); return 4+cyc;
+	case 0x4E: lsr(abso(0, 0)); return 6+cyc;
+	case 0x4F: nz(rA ^= mem816(abso(1, 0), 0)); return 5+cyc;
+	case 0x50: if((rP & FLAGV) == 0) return branch(); pc++; return 2;
+	case 0x51: nz(rA ^= mem816(dpi(0, 0, 1), 0)); return 5+cyc;
+	case 0x52: nz(rA ^= mem816(dpi(0, 0, 0), 0)); return 5+cyc;
+	case 0x53: nz(rA ^= mem816(sry(), 0)); return 7+cyc;
+	case 0x54: block(1); return 7;
+	case 0x55: nz(rA ^= mem816(dp(1), 0)); return 4+cyc;
+	case 0x56: lsr(dp(1)); return 6+cyc;
+	case 0x57: nz(rA ^= mem816(dpi(1, 0, 1), 0)); return 6+cyc;
+	case 0x58: rP &= ~FLAGI; return 2;
+	case 0x59: nz(rA ^= mem816(abso(0, 2), 0)); return 4+cyc;
+	case 0x5A: push816(rY, x8); return 3+cyc;
+	case 0x5B: nz16(rD = rA); return 2;
+	case 0x5C: a = fetch16(); rPB = fetch8() << 16; pc = a; return 4;
+	case 0x5D: nz(rA ^= mem816(abso(0, 1), 0)); return 4+cyc;
+	case 0x5E: lsr(abso(0, 1)); return 7+cyc;
+	case 0x5F: nz(rA ^= mem816(abso(1, 1), 0)); return 5+cyc;
+	case 0x60: pc = pop16() + 1; return 6;
+	case 0x61: adc(mem816(dpi(0, 1, 0), 0)); return 6+cyc;
+	case 0x62: a = fetch16(); push16(a + pc); return 6;
+	case 0x63: adc(mem816(sr(), 0)); return 4+cyc;
+	case 0x64: memw816(dp(0), 0); return 3+cyc;
+	case 0x65: adc(mem816(dp(0), 0)); return 3+cyc;
+	case 0x66: ror(dp(0)); return 5+cyc;
+	case 0x67: adc(mem816(dpi(1, 0, 0), 0)); return 6+cyc;
+	case 0x68: nz(rA = pop816(rA & 0xFF00, m8)); return 4+cyc;
+	case 0x69: adc(imm(0)); return 2+cyc;
+	case 0x6A:
+		a = rP & FLAGC;
+		rP &= ~FLAGC;
+		rP |= rA & 1;
+		if(m8)
+			rA = rA & 0xFF00 | (rA >> 1) & 0x7F | a << 7;
+		else
+			rA = rA >> 1 | a << 15;
+		nz(rA);
+		return 2;
+	case 0x6B: pc = pop16() + 1; rPB = pop8() << 16; return 6;
+	case 0x6C: pc = absi(0); return 5;
+	case 0x6D: adc(mem816(abso(0, 0), 0)); return 4+cyc;
+	case 0x6E: ror(abso(0, 0)); return 6+cyc;
+	case 0x6F: adc(mem816(abso(1, 0), 0)); return 6+cyc;
+	case 0x70: if((rP & FLAGV) != 0) return branch(); pc++; return 2;
+	case 0x71: adc(mem816(dpi(0, 0, 1), 0)); return 5+cyc;
+	case 0x72: adc(mem816(dpi(0, 0, 0), 0)); return 5+cyc;
+	case 0x73: adc(mem816(sry(), 0)); return 7+cyc;
+	case 0x74: memw816(dp(1), 0); return 4+cyc;
+	case 0x75: adc(mem816(dp(1), 0)); return 4+cyc;
+	case 0x76: ror(dp(1)); return 6+cyc;
+	case 0x77: adc(mem816(dpi(1, 0, 1), 0)); return 6+cyc;
+	case 0x78: rP |= FLAGI; return 2;
+	case 0x79: adc(mem816(abso(0, 2), 0)); return 6+cyc;
+	case 0x7A: nzx(rY = pop816(0, x8)); return 4+cyc;
+	case 0x7B: nz16(rA = rD); return 2;
+	case 0x7C: pc = absi(1); return 6;
+	case 0x7D: adc(mem816(abso(0, 1), 0)); return 4+cyc;
+	case 0x7E: ror(abso(0, 1)); return 7+cyc;
+	case 0x7F: adc(mem816(abso(1, 1), 0)); return 5+cyc;
+	case 0x80: return branch();
+	case 0x81: memw816(dpi(0, 1, 0), rA); return 6+cyc;
+	case 0x82: a = fetch16(); pc += a; return 4;
+	case 0x83: memw816(sr(), rA); return 4+cyc;
+	case 0x84: memwx816(dp(0), rY); return 3+cyc;
+	case 0x85: memw816(dp(0), rA); return 3+cyc;
+	case 0x86: memwx816(dp(0), rX); return 3+cyc;
+	case 0x87: memw816(dpi(1, 0, 0), rA); return 6+cyc;
+	case 0x88: 
+		rY--;
+		if(x8)
+			rY &= 0xff;
+		nzx(rY);
+		return 2;
+	case 0x89: bit(imm(0)); return 2+cyc;
+	case 0x8A: setra(rX); return 2+cyc;
+	case 0x8B: push8(rDB >> 16); return 3;
+	case 0x8C: memwx816(abso(0, 0), rY); return 4+cyc;
+	case 0x8D: memw816(abso(0, 0), rA); return 4+cyc;
+	case 0x8E: memwx816(abso(0, 0), rX); return 4+cyc;
+	case 0x8F: memw816(abso(1, 0), rA); return 5+cyc;
+	case 0x90: if((rP & FLAGC) == 0) return branch(); pc++; return 2;
+	case 0x91: memw816(dpi(0, 0, 1), rA); return 6+cyc;
+	case 0x92: memw816(dpi(0, 0, 0), rA); return 6+cyc;
+	case 0x93: memw816(sry(), rA); return 6+cyc;
+	case 0x94: memwx816(dp(1), rY); return 4+cyc;
+	case 0x95: memw816(dp(1), rA); return 4+cyc;
+	case 0x96: memwx816(dp(2), rX); return 4+cyc;
+	case 0x97: memw816(dpi(1, 0, 1), rA); return 6+cyc;
+	case 0x98: setra(rY); return 2;
+	case 0x99: memw816(abso(0, 2), rA); return 3+cyc;
+	case 0x9A: rS = rX; if(emu) rS = rS & 0xff | 0x100; return 2;
+	case 0x9B: setx(rX, &rY); return 2;
+	case 0x9C: memw816(abso(0, 0), 0); return 3+cyc;
+	case 0x9D: memw816(abso(0, 1), rA); return 5+cyc;
+	case 0x9E: memw816(abso(0, 1), 0); return 5+cyc;
+	case 0x9F: memw816(abso(1, 1), rA); return 4+cyc;
+	case 0xA0: nzx(rY = immx(0)); return 2+cyc;
+	case 0xA1: nz(rA = mem816(dpi(0, 1, 0), rA & 0xFF00)); return 6+cyc;
+	case 0xA2: nzx(rX = immx(0)); return 2+cyc;
+	case 0xA3: nz(rA = mem816(sr(), rA & 0xFF00)); return 4+cyc;
+	case 0xA4: nzx(rY = memx816(dp(0))); return 3+cyc;
+	case 0xA5: nz(rA = mem816(dp(0), rA & 0xFF00)); return 3+cyc;
+	case 0xA6: nzx(rX = memx816(dp(0))); return 3+cyc;
+	case 0xA7: nz(rA = mem816(dpi(1, 0, 0), rA & 0xFF00)); return 6+cyc;
+	case 0xA8: setx(rA, &rY); return 2;
+	case 0xA9: nz(rA = imm(rA & 0xFF00)); return 2+cyc;
+	case 0xAA: setx(rA, &rX); return 2;
+	case 0xAB: rDB = nz8(pop8()) << 16; return 4;
+	case 0xAC: nzx(rY = memx816(abso(0, 0))); return 4+cyc;
+	case 0xAD: nz(rA = mem816(abso(0, 0), rA & 0xFF00)); return 4+cyc;
+	case 0xAE: nzx(rX = memx816(abso(0, 0))); return 4+cyc;
+	case 0xAF: nz(rA = mem816(abso(1, 0), rA & 0xFF00)); return 5+cyc;
+	case 0xB0: if((rP & FLAGC) != 0) return branch(); pc++; return 2;
+	case 0xB1: nz(rA = mem816(dpi(0, 0, 1), rA & 0xFF00)); return 5+cyc;
+	case 0xB2: nz(rA = mem816(dpi(0, 0, 0), rA & 0xFF00)); return 5+cyc;
+	case 0xB3: nz(rA = mem816(sry(), rA & 0xFF00)); return 7+cyc;
+	case 0xB4: nzx(rY = memx816(dp(1))); return 4+cyc;
+	case 0xB5: nz(rA = mem816(dp(1), rA & 0xFF00)); return 4+cyc;
+	case 0xB6: nzx(rX = memx816(dp(2))); return 4+cyc;
+	case 0xB7: nz(rA = mem816(dpi(1, 0, 1), rA & 0xFF00)); return 6+cyc;
+	case 0xB8: rP &= ~FLAGV; return 2;
+	case 0xB9: nz(rA = mem816(abso(0, 2), rA & 0xFF00)); return 4+cyc;
+	case 0xBA: setx(rS, &rX); return 2;
+	case 0xBB: setx(rY, &rX); return 2;
+	case 0xBC: nzx(rY = memx816(abso(0, 1))); return 4+cyc;
+	case 0xBD: nz(rA = mem816(abso(0, 1), rA & 0xFF00)); return 4+cyc;
+	case 0xBE: nzx(rX = memx816(abso(0, 2))); return 4+cyc;
+	case 0xBF: nz(rA = mem816(abso(1, 1), rA & 0xFF00)); return 5+cyc;
+	case 0xC0: cmp(rY, immx(0), x8); return 2+cyc;
+	case 0xC1: cmp(rA, mem816(dpi(0, 1, 0), 0), m8); return 6+cyc;
+	case 0xC2: setrp(rP & ~fetch8()); return 3;
+	case 0xC3: cmp(rA, mem816(sr(), 0), m8); return 4+cyc;
+	case 0xC4: cmp(rY, memx816(dp(0)), x8); return 3+cyc;
+	case 0xC5: cmp(rA, mem816(dp(0), 0), m8); return 3+cyc;
+	case 0xC6: dec(dp(0)); return 5+cyc;
+	case 0xC7: cmp(rA, mem816(dpi(1, 0, 0), 0), m8); return 6+cyc;
+	case 0xC8: 
+		rY++;
+		if(x8)
+			rY &= 0xff;
+		nzx(rY);
+		return 2;
+	case 0xC9: cmp(rA, imm(0), m8); return 2+cyc;
+	case 0xCA:
+		rX--;
+		if(x8)
+			rX &= 0xff;
+		nzx(rX);
+		return 2;
+	case 0xCC: cmp(rY, memx816(abso(0, 0)), x8); return 4+cyc;
+	case 0xCD: cmp(rA, mem816(abso(0, 0), 0), m8); return 4+cyc;
+	case 0xCE: dec(abso(0, 0)); return 6+cyc;
+	case 0xCF: cmp(rA, mem816(abso(1, 0), 0), m8); return 4+cyc;
+	case 0xD0: if((rP & FLAGZ) == 0) return branch(); pc++; return 2;
+	case 0xD1: cmp(rA, mem816(dpi(0, 0, 1), 0), m8); return 5+cyc;
+	case 0xD2: cmp(rA, mem816(dpi(0, 0, 0), 0), m8); return 5+cyc;
+	case 0xD3: cmp(rA, mem816(sry(), 0), m8); return 7+cyc;
+	case 0xD4: push16(dpi(0, 0, 0)); return 6+cyc;
+	case 0xD5: cmp(rA, mem816(dp(1), 0), m8); return 4+cyc;
+	case 0xD6: dec(dp(1)); return 6+cyc;
+	case 0xD7: cmp(rA, mem816(dpi(1, 0, 1), 0), m8); return 6+cyc;
+	case 0xD8: rP &= ~FLAGD; return 2;
+	case 0xD9: cmp(rA, mem816(abso(0, 2), 0), m8); return 4+cyc;
+	case 0xDA: push816(rX, x8); return 3+cyc;
+	case 0xDC: a = fetch16(); pc = memread(a) | memread((u16int)(a+1))<<8; rPB = memread((u16int)(a+2)) << 16; return 6;
+	case 0xDD: cmp(rA, mem816(abso(0, 1), 0), m8); return 4+cyc;
+	case 0xDE: dec(abso(0, 1)); return 7+cyc;
+	case 0xDF: cmp(rA, mem816(abso(1, 1), 0), m8); return 7+cyc;
+	case 0xE0: cmp(rX, immx(0), x8); return 2+cyc;
+	case 0xE1: sbc(mem816(dpi(0, 1, 0), 0)); return 6+cyc;
+	case 0xE2: setrp(rP | fetch8()); return 2;
+	case 0xE3: sbc(mem816(sr(), 0)); return 4+cyc;
+	case 0xE4: cmp(rX, memx816(dp(0)), x8); return 3+cyc;
+	case 0xE5: sbc(mem816(dp(0), 0)); return 3+cyc;
+	case 0xE6: inc(dp(0)); return 5+cyc;
+	case 0xE7: sbc(mem816(dpi(1, 0, 0), 0)); return 6+cyc;
+	case 0xE8:
+		rX++;
+		if(x8)
+			rX &= 0xff;
+		nzx(rX);
+		return 2;
+	case 0xE9: sbc(imm(0)); return 2+cyc;
+	case 0xEA: return 2;
+	case 0xEB: nz8(rA = (rA >> 8) | (rA << 8)); return 3;
+	case 0xEC: cmp(rX, memx816(abso(0, 0)), x8); return 4+cyc;
+	case 0xED: sbc(mem816(abso(0, 0), 0)); return 4+cyc;
+	case 0xEE: inc(abso(0, 0)); return 6+cyc;
+	case 0xEF: sbc(mem816(abso(1, 0), 0)); return 5+cyc;
+	case 0xF0: if((rP & FLAGZ) != 0) return branch(); pc++; return 2;
+	case 0xF1: sbc(mem816(dpi(0, 0, 1), 0)); return 5+cyc;
+	case 0xF2: sbc(mem816(dpi(0, 0, 0), 0)); return 5+cyc;
+	case 0xF3: sbc(mem816(sry(), 0)); return 7+cyc;
+	case 0xF4: push16(fetch16()); return 5;
+	case 0xF5: sbc(mem816(dp(1), 0)); return 4+cyc;
+	case 0xF6: inc(dp(1)); return 6+cyc;
+	case 0xF7: sbc(mem816(dpi(1, 0, 1), 0)); return 6+cyc;
+	case 0xF8: rP |= FLAGD; return 2;
+	case 0xF9: sbc(mem816(abso(0, 2), 0)); return 4+cyc;
+	case 0xFA: nzx(rX = pop816(0, x8)); return 4+cyc;
+	case 0xFB:
+		a = emu;
+		emu = rP & 1;
+		if(emu){
+			rX &= 0xff;
+			rY &= 0xff;
+			rS = rS & 0xff | 0x100;
+		}
+		rP &= ~1;
+		rP |= 0x30 | a;
+		return 2;
+	case 0xFC: push16(pc+1); pc = absi(1); return 8+cyc;
+	case 0xFD: sbc(mem816(abso(0, 1), 0)); return 4+cyc;
+	case 0xFE: inc(abso(0, 1)); return 7+cyc;
+	case 0xFF: sbc(mem816(abso(1, 1), 0)); return 5+cyc;
+	default:
+		print("undefined %#x (pc %#.6x)\n", op, curpc);
+		return 2;
+	}
+}
--- /dev/null
+++ b/sys/src/games/snes/dat.h
@@ -1,0 +1,70 @@
+extern u16int pc;
+extern u32int rPB, curpc;
+extern u8int dma, nmi, irq;
+extern u32int hdma;
+extern int trace;
+
+extern uchar *prg, *sram;
+extern int nprg, nsram, keys;
+extern u16int keylatch;
+extern u8int reg[32768], spcmem[65536], vram[65536], oam[544];
+extern u16int cgram[256];
+
+extern int ppux, ppuy;
+extern u16int vtime, htime, subcolor, oamaddr;
+extern u16int m7[6], hofs[4], vofs[4];
+
+extern int battery, saveclock;
+
+enum {
+	FLAGC = 1<<0,
+	FLAGZ = 1<<1,
+	FLAGI = 1<<2,
+	FLAGD = 1<<3,
+	FLAGX = 1<<4,
+	FLAGM = 1<<5,
+	FLAGV = 1<<6,
+	FLAGN = 1<<7,
+};
+
+enum {
+	FREQ = 21477272,
+	SPCDIV = 21,
+	SAVEFREQ = FREQ / 4,
+	
+	XLEFT = 22,
+	XRIGHT = 22 + 255,
+};
+
+enum {
+	INIDISP = 0x2100,
+	FORBLANK = 0x80,
+	OBSEL = 0x2101,
+	OAMADDH = 0x2103,
+	BGMODE = 0x2105,
+	MOSAIC = 0x2106,
+	WIN1 = 2,
+	INVW1 = 1,
+	WIN2 = 8,
+	INVW2 = 4,
+	TM = 0x212c,
+	TS = 0x212d,
+	TMW = 0x212e,
+	TSW = 0x212f,
+	CGWSEL = 0x2130,
+	CGADSUB = 0x2131,
+	DIRCOL = 1,
+	SETINI = 0x2133,
+	OVERSCAN = 1<<2,
+	AUTOJOY = 1,
+	NMITIMEN = 0x4200,
+	RDNMI = 0x4210,
+	VBLANK = 1<<7,
+	VCNTIRQ = 1<<5,
+	HCNTIRQ = 1<<4,
+};
+
+enum {
+	IRQPPU = 1<<7,
+};
+
--- /dev/null
+++ b/sys/src/games/snes/fns.h
@@ -1,0 +1,13 @@
+u8int	memread(u32int);
+void	memwrite(u32int, u8int);
+void	cpureset(void);
+int	cpustep(void);
+void	spcreset(void);
+int	spcstep(void);
+void	spctimerstep(void);
+int	dmastep(void);
+void	ppustep(void);
+void	memreset(void);
+int	hdmastep(void);
+void	flush(void);
+void	message(char *, ...);
--- /dev/null
+++ b/sys/src/games/snes/mem.c
@@ -1,0 +1,509 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int reg[32768];
+u8int mem[131072];
+u8int oam[544], vram[65536];
+u16int cgram[256];
+u16int oamaddr, vramlatch, keylatch;
+enum {
+	OAMLATCH,
+	CGLATCH,
+	CGLH,
+	OFSPREV,
+	M7PREV,
+	OPCTLATCH,
+	OPHCTH,
+	OPVCTH,
+};
+
+static void
+incvram(int i, int r)
+{
+	u16int a, b;
+	int c;
+	
+	c = reg[0x2115];
+	if((c >> 7) != i)
+		return;
+	a = reg[0x2116] | reg[0x2117] << 8;
+	if((c & 0x0c) != 0)
+		print("address remapping\n");
+	if(r){
+		b = a<<1;
+		vramlatch = vram[b++];
+		vramlatch |= vram[b] << 8;
+	}
+	switch(c & 3){
+	case 0: a++; break;
+	case 1: a += 32; break;
+	default: a += 128; break;
+	}
+	reg[0x2116] = a;
+	reg[0x2117] = (a >> 8) & 0x7f;
+}
+
+static void
+hvlatch(void)
+{
+	reg[0x213c] = ppux;
+	reg[OPHCTH] = ppux >> 8;
+	reg[0x213d] = ppuy;
+	reg[OPVCTH] = ppuy >> 8;
+	reg[OPCTLATCH] |= 0x40;
+}
+
+static u16int
+swaprb(u16int a)
+{
+	return (a & 0x83e0) | (a & 0x7c00) >> 10 | (a & 0x001f) << 10;
+}
+
+u8int
+regread(u16int p)
+{
+	u8int v;
+	u16int a;
+	int r;
+
+	if(p < 0x2000)
+		return mem[p];
+	switch(p){
+	case 0x2134: case 0x2135: case 0x2136:
+		r = ((signed short)m7[0] * (signed char)reg[0x211c]) & 0xffffff;
+		return r >> 8 * (p - 0x2134);
+	case 0x2137:
+		if((reg[0x4201] & 0x80) != 0)
+			hvlatch();
+		return 0;
+	case 0x2138:
+		if(oamaddr < 0x200)
+			v = oam[oamaddr];
+		else
+			v = oam[oamaddr & 0x21f];
+		oamaddr = (oamaddr + 1) & 0x3ff;
+		return v;
+	case 0x2139:
+		v = vramlatch;
+		incvram(0, 1);
+		return v;
+	case 0x213a:
+		v = vramlatch >> 8;
+		incvram(1, 1);
+		return v;
+	case 0x213b:
+		a = swaprb(cgram[reg[0x2121]]);
+		if(reg[CGLH] != 0){
+			a >>= 8;
+			reg[0x2121]++;
+		}
+		reg[CGLH] ^= 1;
+		return a;
+	case 0x213c:
+		reg[OPCTLATCH] ^= 1;
+		if((reg[OPCTLATCH] & 1) == 0)
+			return reg[OPHCTH];
+		break;
+	case 0x213d:
+		reg[OPCTLATCH] ^= 2;
+		if((reg[OPCTLATCH] & 2) == 0)
+			return reg[OPVCTH];
+		break;
+	case 0x213f:
+		v = 2 | reg[OPCTLATCH] & 0x40;
+		if((reg[0x4201] & 0x80) != 0)
+			reg[OPCTLATCH] &= ~0x43;
+		else
+			reg[OPCTLATCH] &= ~3;
+		return v;
+	case 0x2180:
+		v = memread(reg[0x2181] | reg[0x2182] << 8 | reg[0x2183] << 16);
+		reg[0x2181]++;
+		return v;
+	case 0x4016:
+		if((reg[0x4016] & 1) != 0)
+			return keylatch >> 15;
+		v = keylatch >> 15;
+		keylatch = (keylatch << 1) | 1;
+		return v;
+	case 0x4017:
+		return 0;
+	case 0x4211:
+		v = irq;
+		irq &= ~IRQPPU;
+		return v;
+	case 0x4212:
+		v = 0;
+		if(ppux >= 274 || ppux == 0)
+			v |= 0x40;
+		a = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
+		if(ppuy >= a){
+			v |= 0x80;
+			if(ppuy <= a + 2 && (reg[NMITIMEN] & AUTOJOY) != 0)
+				v |= 1;
+		}
+		return v;
+	}
+	if((p & 0xff40) == 0x2140)
+		return spcmem[0xf4 | p & 3];
+	return reg[p];
+}
+
+void
+regwrite(u16int p, u8int v)
+{
+	u16int a;
+
+	if(p < 0x2000){
+		mem[p] = v;
+		return;
+	}
+	switch(p){
+	case 0x2102:
+		oamaddr &= 0x200;
+		oamaddr |= v << 1;
+		break;
+	case 0x2103:
+		oamaddr &= 0x1fe;
+		oamaddr |= (v & 1) << 9;
+		break;
+	case 0x2104:
+		if((oamaddr & 1) == 0)
+			reg[OAMLATCH] = v;
+		if(oamaddr < 0x200){
+			if((oamaddr & 1) != 0){
+				oam[oamaddr - 1] = reg[OAMLATCH];
+				oam[oamaddr] = v;
+			}
+		}else
+			oam[oamaddr & 0x21f] = v;
+		oamaddr = (oamaddr + 1) & 0x3ff;
+		return;
+	case 0x210d: case 0x210f:
+	case 0x2111: case 0x2113:
+		a = (p - 0x210d) >> 1;
+		hofs[a] = (v << 8) | reg[OFSPREV] & ~7 | (hofs[a] >> 8) & 7;
+		reg[OFSPREV] = v;
+		break;
+	case 0x210e: case 0x2110:
+	case 0x2112: case 0x2114:
+		vofs[(p - 0x210e) >> 1] = (v << 8) | reg[OFSPREV];
+		reg[OFSPREV] = v;
+		break;
+	case 0x2116:
+		break;
+	case 0x2117:
+		v &= 0x7f;
+		break;
+	case 0x2118:
+		a = reg[0x2116] << 1 | reg[0x2117] << 9;
+		vram[a] = v;
+		incvram(0, 0);
+		return;
+	case 0x2119:
+		a = reg[0x2116] << 1 | reg[0x2117] << 9;
+		vram[a|1] = v;
+		incvram(1, 0);
+		return;
+	case 0x211b: case 0x211c: case 0x211d:
+	case 0x211e: case 0x211f: case 0x2120:
+		m7[p - 0x211b] = (v << 8) | reg[M7PREV];
+		reg[M7PREV] = v;
+		break;
+	case 0x2121:
+		reg[CGLH] = 0;
+		break;
+	case 0x2122:
+		if(reg[CGLH] == 0)
+			reg[CGLATCH] = v;
+		else
+			cgram[reg[0x2121]++] = swaprb(reg[CGLATCH] | v << 8);
+		reg[CGLH] ^= 1;
+		break;
+	case 0x2132:
+		if((v & 0x80) != 0) subcolor = subcolor & 0x7fe0 | v & 0x1f;
+		if((v & 0x40) != 0) subcolor = subcolor & 0x7c1f | (v & 0x1f) << 5;
+		if((v & 0x20) != 0) subcolor = subcolor & 0x03ff | (v & 0x1f) << 10;
+		return;
+	case 0x2180:
+		memwrite(reg[0x2181] | reg[0x2182] << 8 | reg[0x2183] << 16, v);
+		reg[0x2181]++;
+		return;
+	case 0x213e:
+		return;
+	case 0x4016:
+		if((reg[0x4016] & 1) != 0 && (v & 1) == 0)
+			keylatch = keys;
+		break;
+	case 0x4200:
+		if((reg[0x4200] & 0x80) == 0 && (v & 0x80) != 0 && (reg[RDNMI] & 0x80) != 0)
+			nmi = 2;
+		break;
+	case 0x4201:
+		if((reg[0x4201] & 0x80) == 0 && (v & 0x80) != 0)
+			hvlatch();
+		break;
+	case 0x4203:
+		a = reg[0x4202] * v;
+		reg[0x4216] = a;
+		reg[0x4217] = a >> 8;
+		break;
+	case 0x4206:
+		if(v == 0){
+			reg[0x4214] = 0xff;
+			reg[0x4215] = 0xff;
+			reg[0x4216] = reg[0x4204];
+			reg[0x4217] = reg[0x4205];
+		}else{
+			a = reg[0x4204] | reg[0x4205] << 8;
+			reg[0x4214] = a / v;
+			reg[0x4215] = (a / v) >> 8;
+			reg[0x4216] = a % v;
+			reg[0x4217] = (a % v) >> 8;
+		}
+		break;
+	case 0x4207:
+		htime = htime & 0x100 | v;
+		break;
+	case 0x4208:
+		htime = htime & 0xff | (v & 1) << 8;
+		break;
+	case 0x4209:
+		vtime = vtime & 0x100 | v;
+		break;
+	case 0x420a:
+		vtime = vtime & 0xff | (v & 1) << 8;
+		break;
+	case 0x420b:
+		dma |= v & ~reg[0x420c];
+		break;
+	case 0x4210:
+		return;
+	case 0x4211:
+		irq &= ~IRQPPU;
+		return;
+	case 0x4216: case 0x4217:
+	case 0x4218: case 0x4219: case 0x421a: case 0x421b:
+	case 0x421c: case 0x421d: case 0x421e: case 0x421f:
+		return;
+	}
+	if((p & 0xff40) == 0x2140)
+		p &= 0xff43;
+	reg[p] = v;
+}
+
+u8int
+memread(u32int a)
+{
+	u16int al;
+	u8int b;
+
+	al = a;
+	b = (a>>16) & 0x7f;
+	if(al < 0x8000){
+		if(b < 0x40)
+			return regread(al);
+		if(b >= 0x70 && b < 0x78 && nsram != 0)
+			return sram[((a & 0x7fffff) - 0x700000) & (nsram - 1)];
+	}
+	if(b >= 0x7e && (a & (1<<23)) == 0)
+		return mem[a - 0x7e0000];
+	return prg[(b%nprg) << 15 | al & 0x7fff];
+}
+
+void
+memwrite(u32int a, u8int v)
+{
+	u16int al;
+	u8int b;
+
+	al = a;
+	b = (a>>16) & 0x7f;
+	if(b >= 0x7e && a < 0x800000)
+		mem[a - 0x7e0000] = v;
+	if(al < 0x8000){
+		if(b < 0x40){
+			regwrite(a, v);
+			return;
+		}
+		if(b >= 0x70 && b < 0x78 && nsram != 0){
+			sram[((a & 0x7fffff) - 0x700000) & (nsram - 1)] = v;
+			if(saveclock == 0)
+				saveclock = SAVEFREQ;
+			return;
+		}
+	}
+}
+
+static u8int nbytes[] = {1, 2, 2, 4, 4, 4, 2, 4};
+static u8int modes[] = {0x00, 0x04, 0x00, 0x50, 0xe4, 0x44, 0x00, 0x50};
+
+static int
+dmavalid(int a, int b)
+{
+	if(b)
+		return 1;
+	if((a & 0x400000) != 0)
+		return 1;
+	switch(a >> 8){
+	case 0x21: return 0;
+	case 0x42: return a != 0x420b && a != 0x420c;
+	case 0x43: return 0;
+	}
+	return 1;
+}
+
+int
+dmastep(void)
+{
+	int i, j, n, m, cyc;
+	u32int a;
+	u8int b, c, *p;
+	u32int v;
+	
+	cyc = 0;
+	for(i = 0; i < 8; i++)
+		if((dma & (1<<i)) != 0)
+			break;
+	if(i == 8)
+		return 0;
+	p = reg + 0x4300 + (i << 4);
+	c = *p;
+	n = nbytes[c & 7];
+	m = modes[c & 7];
+	for(j = 0; j < n; j++){
+		a = p[2] | p[3] << 8 | p[4] << 16;
+		b = p[1] + (m & 3);
+		if((c & 0x80) != 0){
+			v = dmavalid(b, 1) ? memread(0x2100 | b) : 0;
+			if(dmavalid(a, 0))
+				memwrite(a, v);
+		}else{
+			v = dmavalid(a, 0) ? memread(a) : 0;
+			if(dmavalid(b, 1))
+				memwrite(0x2100 | b, v);
+		}
+		cyc++;
+		m >>= 2;
+		if((c & 0x08) == 0){
+			if((c & 0x10) != 0){
+				if(--p[2] == 0xff)
+					p[3]--;
+			}else{
+				if(++p[2] == 0x00)
+					p[3]++;
+			}
+		}
+		if(p[5] == 0){
+			p[5] = 0xff;
+			p[6]--;
+		}else if(--p[5] == 0 && p[6] == 0){
+			dma &= ~(1<<i);
+			break;
+		}
+	}
+	return cyc;
+}
+
+static int
+hdmaload(u8int *p)
+{
+	u32int a;
+
+	a = p[8] | p[9] << 8 | p[4] << 16;
+	p[10] = dmavalid(a, 0) ? memread(a) : 0;
+	a++;
+	if((p[0] & 0x40) != 0){
+		p[5] = dmavalid(a, 0) ? memread(a) : 0;
+		a++;
+		p[6] = dmavalid(a, 0) ? memread(a) : 0;
+		a++;
+	}
+	p[8] = a;
+	p[9] = a >> 8;
+	return (p[0] & 0x40) != 0 ? 3 : 1;
+}
+
+int
+hdmastep(void)
+{
+	int i, j, cyc;
+	u8int *p, *q, n, m, b, v, c;
+	u32int a;
+	
+	cyc = 0;
+	if(dma != 0)
+		dma &= ~((hdma & 0xff00) >> 8 | (hdma & 0xff));
+	if((hdma & 0xff) == 0)
+		goto init;
+	cyc += 2;
+	for(i = 0; i < 8; i++){
+		if(((hdma >> i) & (1<<24|1)) != 1)
+			continue;
+		p = reg + 0x4300 + (i << 4);
+		c = p[0];
+		if((hdma & (1<<(16+i))) != 0){
+			n = nbytes[c & 7];
+			m = modes[c & 7];
+			if((c & 0x40) != 0)
+				q = p + 5;
+			else
+				q = p + 8;
+			for(j = 0; j < n; j++){
+				a = q[0] | q[1] << 8 | p[4] << 16;
+				b = p[1] + (m & 3);
+				if((c & 0x80) != 0){
+					v = dmavalid(b, 1) ? memread(0x2100 | b) : 0;
+					if(dmavalid(a, 0))
+						memwrite(a, v);
+				}else{
+					v = dmavalid(a, 0) ? memread(a) : 0;
+					if(dmavalid(b, 1))
+						memwrite(0x2100 | b, v);
+				}
+				if(++q[0] == 0)
+					q[1]++;
+				cyc++;
+				m >>= 2;
+			}
+		}
+		p[10]--;
+		hdma = (hdma & ~(1<<(16+i))) | ((p[10] & 0x80) << (9+i));
+		cyc++;
+		if((p[10] & 0x7f) == 0){
+			cyc += hdmaload(p)-1;
+			if(p[10] == 0)
+				hdma |= 1<<(24+i);
+			hdma |= 1<<(16+i);
+		}
+	}
+	hdma &= ~0xff;
+	if((hdma & 0xff00) == 0)
+		return cyc;
+init:
+	for(i = 0; i < 8; i++){
+		if((hdma & (1<<(8+i))) == 0)
+			continue;
+		p = reg + 0x4300 + (i << 4);
+		p[8] = p[2];
+		p[9] = p[3];
+		cyc += hdmaload(p);
+		if(p[10] == 0)
+			hdma |= 1<<(24+i);
+		hdma |= 1<<(16+i);
+	}
+	cyc += 2;
+	hdma &= ~0xff00;
+	return cyc;
+}
+
+void
+memreset(void)
+{
+	reg[0x213e] = 1;
+	reg[0x4201] = 0xff;
+	reg[0x4210] = 2;
+}
--- /dev/null
+++ b/sys/src/games/snes/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=snes
+OFILES=\
+	cpu.$O\
+	snes.$O\
+	mem.$O\
+	ppu.$O\
+	spc.$O\
+
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/snes/ppu.c
@@ -1,0 +1,548 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+int ppux, ppuy, rx;
+static u8int mode, bright, pixelpri[2];
+static u32int pixelcol[2];
+u16int vtime = 0x1ff, htime = 0x1ff, subcolor, mosatop;
+uchar pic[256*239*2*9];
+u16int m7[6], hofs[4], vofs[4];
+
+enum { OBJ = 4, COL = 5, OBJNC = 6 };
+
+static u16int
+darken(u16int v)
+{
+	u8int r, g, b;
+	
+	r = (v >> 10) & 0x1f;
+	g = (v >> 5) & 0x1f;
+	b = v & 0x1f;
+	r = r * bright / 15;
+	g = g * bright / 15;
+	b = b * bright / 15;
+	return r << 10 | g << 5 | b;
+}
+
+static void
+pixeldraw(int x, int y, u16int v)
+{
+	uchar *p;
+
+	if(bright != 0xf)
+		v = darken(v);
+	p = pic + (x + y * 256) * 2;
+	*p++ = v;
+	*p = v >> 8;
+}
+
+static int
+window(int n)
+{
+	int a, w1, w2;
+
+	a = reg[0x2123 + (n >> 1)];
+	if((n & 1) != 0)
+		a >>= 4;
+	if((a & (WIN1|WIN2)) == 0)
+		return 0;
+	w1 = rx >= reg[0x2126] && rx <= reg[0x2127];
+	w2 = rx >= reg[0x2128] && rx <= reg[0x2129];
+	if((a & INVW1) != 0)
+		w1 = !w1;
+	if((a & INVW2) != 0)
+		w2 = !w2;
+	if((a & (WIN1|WIN2)) != (WIN1|WIN2))
+		return (a & WIN1) != 0 ? w1 : w2;
+	a = reg[0x212a + (n >> 2)] >> ((n & 3) << 1);
+	switch(a & 3){
+	case 1: return w1 & w2;
+	case 2: return w1 ^ w2;
+	case 3: return w1 ^ w2 ^ 1;
+	}
+	return w1 | w2;
+}
+
+static void
+pixel(int n, int v, int pri)
+{
+	int a;
+	
+	a = 1<<n;
+	if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
+		pixelcol[0] = v;
+		pixelpri[0] = pri;
+	}
+	if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
+		pixelcol[1] = v;
+		pixelpri[1] = pri;
+	}
+}
+
+static u16int
+tile(int n, int tx, int ty)
+{
+	int a;
+	u16int ta;
+	u16int t;
+
+	a = reg[0x2107 + n];
+	ta = ((a & ~3) << 9) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
+	if((a & 1) != 0)
+		ta += (tx & 0x20) << 6;
+	if((a & 2) != 0)
+		ta += (ty & 0x20) << (6 + (a & 1));
+	t = vram[ta++];
+	return t | vram[ta] << 8;
+}
+
+static void
+chr(int n, int nb, int sz, u16int t, int x, int y, u8int c[])
+{
+	int i;
+	u16int a;
+
+	if(sz == 16){
+		if(y >= 8){
+			t += ((x >> 3 ^ t >> 14) & 1) + ((~t >> 11) & 16);
+			y -= 8;
+		}else
+			t += ((x >> 3 ^ t >> 14) & 1) + ((t >> 11) & 16);
+	}
+	if((t & 0x8000) != 0)
+		y = 7 - y;
+	a = reg[0x210b + (n >> 1)];
+	if((n & 1) != 0)
+		a >>= 4;
+	else
+		a &= 0xf;
+	a = (a << 13) + (t & 0x3ff) * 8 * nb + y * 2;
+	for(i = 0; i < nb; i += 2){
+		c[i] = vram[a++];
+		c[i+1] = vram[a];
+		a += 15;
+	}
+}
+
+static int
+palette(int n, int p)
+{
+	switch(mode){
+	case 0:
+		return p << 2 | n << 5;
+	case 1:
+		if(n >= 2)
+			return p << 2;
+	case 2:
+	case 6:
+		return p << 4;
+	case 5:
+		if(n == 0)
+			return p << 4;
+		return p << 2;
+	case 3:
+		if(n != 0)
+			return p << 4;
+	case 4:
+		if(n != 0)
+			return p << 2;
+		if((reg[CGWSEL] & DIRCOL) != 0)
+			return 0x10000;
+	}
+	return 0;
+}
+
+static void
+shift(u8int *c, int nb, int n, int d)
+{
+	u8int *e;
+	
+	e = c + nb;
+	if(d)
+		while(c < e)
+			*c++ >>= n;
+	else
+		while(c < e)
+			*c++ <<= n;
+}
+
+static u8int
+bgpixel(u8int *c, int nb, int d)
+{
+	u8int v;
+	int i;
+	
+	v = 0;
+	if(d)
+		for(i = 0; i < nb; i++){
+			v |= (*c & 1) << i;
+			*c++ >>= 1;
+		}
+	else
+		for(i = 0; i < nb; i++){
+			v |= (*c & 0x80) >> (7 - i);
+			*c++ <<= 1;
+		}
+	return v;
+}
+
+static void
+bg(int n, int nb, int prilo, int prihi)
+{
+	static struct bg {
+		u8int sz, szsh;
+		u16int tx, ty, tnx, tny;
+		u16int t;
+		u8int c[8];
+		int pal;
+		u8int msz, mv, mx;
+	} bgs[4];
+	struct bg *p;
+	int v, sx, sy;
+
+	p = bgs + n;
+	if(rx == 0){
+		p->szsh = (reg[BGMODE] & (1<<(4+n))) != 0 ? 4 : 3;
+		if(mode >= 5)
+			p->szsh = 4;
+		p->sz = 1<<p->szsh;
+		sx = hofs[n];
+		sy = vofs[n] + ppuy;
+		if(reg[MOSAIC] != 0 && (reg[MOSAIC] & (1<<n)) != 0){
+			p->msz = (reg[MOSAIC] >> 4) + 1;
+			if(p->msz != 1){
+				sx -= p->mx = sx % p->msz;
+				sy -= sy % p->msz;
+			}
+		}else
+			p->msz = 1;
+	redo:
+		p->tx = sx >> p->szsh;
+		p->tnx = sx & (p->sz - 1);
+		p->ty = sy >> p->szsh;
+		p->tny = sy & (p->sz - 1);
+		p->t = tile(n, p->tx, p->ty);
+		chr(n, nb, p->sz, p->t, p->tnx, p->tny, p->c);
+		p->pal = palette(n, p->t >> 10 & 7);
+		if(p->tnx != 0)
+			shift(p->c, nb, p->tnx, p->t & 0x4000);
+		if(p->msz != 1 && p->mx != 0 && sx % p->msz == 0){
+			p->mv = bgpixel(p->c, nb, p->t & 0x4000);
+			if(p->tnx + p->mx >= 8){
+				sx += p->mx;
+				goto redo;
+			}else if(p->mx > 1)
+				shift(p->c, nb, p->mx - 1, p->t & 0x4000);
+		}
+	}
+	v = bgpixel(p->c, nb, p->t & 0x4000);
+	if(p->msz != 1)
+		if(p->mx++ == 0)
+			p->mv = v;
+		else{
+			if(p->mx == p->msz)
+				p->mx = 0;
+			v = p->mv;
+		}
+	if(v != 0)
+		pixel(n, p->pal + v, (p->t & 0x2000) != 0 ? prihi : prilo);
+	if(++p->tnx == p->sz){
+		p->tx++;
+		p->tnx = 0;
+		p->t = tile(n, p->tx, p->ty);
+		p->pal = palette(n, p->t >> 10 & 7);
+	}
+	if((p->tnx & 7) == 0)
+		chr(n, nb, p->sz, p->t, p->tnx, p->tny, p->c);
+}
+
+static void
+bgs(void)
+{
+	static int bitch[8];
+
+	switch(mode){
+	case 0:
+		bg(0, 2, 0x80, 0xb0);
+		bg(1, 2, 0x71, 0xa1);
+		bg(2, 2, 0x22, 0x52);
+		bg(3, 2, 0x13, 0x43);
+		break;
+	case 1:
+		bg(0, 4, 0x80, 0xb0);
+		bg(1, 4, 0x71, 0xa1);
+		bg(2, 2, 0x12, (reg[BGMODE] & 8) != 0 ? 0xd2 : 0x42);
+		break;
+	default:
+		if(bitch[mode]++ == 0)
+			print("bg mode %d not implemented\n", mode);
+	}
+}
+
+static void
+sprites(void)
+{
+	static struct {
+		short x;
+		u8int y, i, c, sx, sy;
+		u16int t0, t1;
+	} s[32], *sp;
+	static struct {
+		short x;
+		u8int sx, i, c, pal, pri;
+		u8int *ch;
+	} t[32], *tp;
+	static uchar ch[34*4], *cp;
+	static uchar *p, q, over;
+	static int n, m;
+	static int *sz;
+	static int szs[] = {
+		8, 8, 16, 16, 8, 8, 32, 32,
+		8, 8, 64, 64, 16, 16, 32, 32,
+		16, 16, 64, 64, 32, 32, 64, 64,
+		16, 32, 32, 64, 16, 32, 32, 32
+	};
+	static u16int base[2];
+	u8int dy, v, col, pri0, pri1, prio;
+	u16int a;
+	int i, nt, dx;
+
+	if(rx == 0){
+		n = 0;
+		over = 1;
+		sp = s;
+		sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
+		base[0] = (reg[OBSEL] & 0x07) << 14;
+		base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
+	}
+	if((rx & 1) == 0){
+		p = oam + 2 * rx;
+		if(p[1] == 0xf0)
+			goto nope;
+		q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
+		dy = ppuy - p[1];
+		sp->sx = sz[q & 2];
+		sp->sy = sz[(q & 2) + 1];
+		if(dy >= sp->sy)
+			goto nope;
+		sp->x = p[0];
+		if((q & 1) != 0)
+			sp->x |= 0xff00;
+		if(sp->x < -(short)sp->sx && sp->x != -256)
+			goto nope;
+		if(n == 32){
+			over |= 0x40;
+			goto nope;
+		}
+		sp->i = rx >> 1;
+		sp->y = p[1];
+		sp->c = p[3];
+		sp->t0 = p[2] << 5;
+		sp->t1 = base[sp->c & 1];
+		sp++;
+		n++;
+	}
+nope:
+	if(ppuy != 0){
+		col = 0;
+		pri0 = 0;
+		pri1 = 128;
+		if((reg[OAMADDH] & 0x80) != 0)
+			prio = oamaddr >> 2;
+		else
+			prio = 0;
+		for(i = 0, tp = t; i < m; i++, tp++){
+			dx = rx - tp->x;
+			if(dx < 0 || dx >= tp->sx)
+				continue;
+			p = tp->ch + (dx >> 1 & 0xfc);
+			if((tp->c & 0x40) != 0){
+				v = p[2] & 1 | p[3] << 1 & 2 | p[0] << 2 & 4 | p[1] << 3 & 8;
+				p[0] >>= 1;
+				p[1] >>= 1;
+				p[2] >>= 1;
+				p[3] >>= 1;
+			}else{
+				v = p[0] >> 7 & 1 | p[1] >> 6 & 2 | p[2] >> 5 & 4 | p[3] >> 4 & 8;
+				p[0] <<= 1;
+				p[1] <<= 1;
+				p[2] <<= 1;
+				p[3] <<= 1;
+			}
+			nt = (tp->i - prio) & 0x7f;
+			if(v != 0 && nt < pri1){
+				col = tp->pal + v;
+				pri0 = tp->pri;
+				pri1 = nt;
+			}
+		}
+		if(col > 0)
+			pixel(OBJ, col, pri0);
+	}
+	if(rx == 255){
+		cp = ch;
+		m = n;
+		for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
+			tp->x = sp->x;
+			tp->sx = 0;
+			tp->c = sp->c;
+			tp->pal = 0x80 | sp->c << 3 & 0x70;
+			tp->pri = 3 * (0x10 + (sp->c & 0x30));
+			if((tp->c & 8) != 0)
+				tp->pri |= OBJ;
+			else
+				tp->pri |= OBJNC;
+			tp->ch = cp;
+			tp->i = sp->i;
+			nt = sp->sx >> 2;
+			dy = ppuy - sp->y;
+			if((sp->c & 0x80) != 0)
+				dy = sp->sy - 1 - dy;
+			a = sp->t0 | (dy & 7) << 1;
+			if(dy >= 8)
+				a += (dy & ~7) << 6;
+			if((sp->c & 0x40) != 0){
+				a += sp->sx * 4;
+				for(i = 0; i < nt; i++){
+					if(cp < ch + sizeof(ch)){
+						a -= 16;
+						*(u16int*)cp = *(u16int*)&vram[sp->t1 | a & 0x1fff];
+						cp += 2;
+						tp->sx += 4;
+					}else
+						over |= 0x80;
+				}
+			}else
+				for(i = 0; i < nt; i++){
+					if(cp < ch + sizeof(ch)){
+						*(u16int*)cp = *(u16int*)&vram[sp->t1 | a & 0x1fff];
+						cp += 2;
+						tp->sx += 4;
+						a += 16;
+					}else
+						over |= 0x80;
+				}
+		}
+		reg[0x213e] = over;
+	}
+}
+
+static u16int
+colormath(void)
+{
+	u16int v, w, r, g, b;
+	u8int m, m2;
+	int cw;
+	
+	m = reg[CGWSEL];
+	m2 = reg[CGADSUB];
+	cw = -1;
+	switch(m >> 6){
+	default: v = 1; break;
+	case 1: v = cw = window(COL); break;
+	case 2: v = !(cw = window(COL)); break;
+	case 3: v = 0; break;
+	}
+	if(v){
+		if((pixelcol[0] & 0x10000) != 0)
+			v = pixelcol[0];
+		else
+			v = cgram[pixelcol[0] & 0xff];
+	}
+	if((m2 & (1 << (pixelpri[0] & 0xf))) == 0)
+		return v;
+	switch((m >> 4) & 3){
+	case 0: break;
+	case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
+	case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
+	default: return v;
+	}
+	if((m & 2) != 0){
+		if((pixelcol[1] & 0x10000) != 0)
+			w = pixelcol[1];
+		else
+			w = cgram[pixelcol[1] & 0xff];
+	}else
+		w = subcolor;
+	if((m2 & 0x80) != 0){
+		r = (v & 0x7c00) - (w & 0x7c00);
+		g = (v & 0x03e0) - (w & 0x03e0);
+		b = (v & 0x001f) - (w & 0x001f);
+		if((m2 & 0x40) != 0){
+			r = (r >> 1) & 0xfc00;
+			g = (g >> 1) & 0xffe0;
+			b >>= 1;
+		}
+		if(r > 0x7c00) r = 0;
+		if(g > 0x03e0) g = 0;
+		if(b > 0x001f) b = 0;
+		return r | g | b;
+	}else{
+		r = (v & 0x7c00) + (w & 0x7c00);
+		g = (v & 0x03e0) + (w & 0x03e0);
+		b = (v & 0x001f) + (w & 0x001f);
+		if((m2 & 0x40) != 0){
+			r = (r >> 1) & 0xfc00;
+			g = (g >> 1) & 0xffe0;
+			b >>= 1;
+		}
+		if(r > 0x7c00) r = 0x7c00;
+		if(g > 0x03e0) g = 0x03e0;
+		if(b > 0x001f) b = 0x001f;
+		return r | g | b;
+	}
+}
+
+void
+ppustep(void)
+{
+	int yvbl;
+
+	mode = reg[BGMODE] & 7;
+	bright = reg[INIDISP] & 0xf;
+	yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
+
+	if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
+		rx = ppux - XLEFT;
+		if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
+			pixelcol[0] = 0;
+			pixelpri[0] = COL;
+			pixelcol[1] = 0x10000 | subcolor;
+			pixelpri[1] = COL;	
+			bgs();
+			sprites();
+			if(ppuy != 0)
+				pixeldraw(rx, ppuy - 1, colormath());
+		}else if(ppuy != 0)
+			pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x31c8 : 0);
+	}
+
+	if(ppux == 0x116 && ppuy <= yvbl)
+		hdma |= reg[0x420c];
+	if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
+		irq |= IRQPPU;
+	if(++ppux >= 340){
+		ppux = 0;
+		if(++ppuy >= 262){
+			ppuy = 0;
+			reg[RDNMI] &= ~VBLANK;
+			hdma = reg[0x420c]<<8;
+			flush();
+		}
+		if(ppuy == yvbl){
+			reg[RDNMI] |= VBLANK;
+			if((reg[NMITIMEN] & VBLANK) != 0)
+				nmi = 2;
+			if((reg[NMITIMEN] & AUTOJOY) != 0){
+				reg[0x4218] = keys;
+				reg[0x4219] = keys >> 8;
+				keylatch = 0xffff;
+			}
+		}
+		if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
+			irq |= IRQPPU;
+	}
+}
--- /dev/null
+++ b/sys/src/games/snes/snes.c
@@ -1,0 +1,258 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <ctype.h>
+#include "dat.h"
+#include "fns.h"
+
+uchar *prg, *sram;
+int nprg, nsram, hirom, battery;
+
+int ppuclock, spcclock, stimerclock, saveclock, msgclock, paused;
+Mousectl *mc;
+QLock pauselock;
+int keys, savefd;
+int scale;
+Rectangle picr;
+Image *tmp, *bg;
+
+void
+flushram(void)
+{
+	if(savefd >= 0)
+		pwrite(savefd, sram, nsram, 0);
+	saveclock = 0;
+}
+
+void
+loadrom(char *file)
+{
+	static char buf[512];
+	char *s;
+	int fd;
+	vlong size;
+
+	fd = open(file, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	size = seek(fd, 0, 2);
+	if(size < 0)
+		sysfatal("seek: %r");
+	if(size == 0)
+		sysfatal("empty file");
+	if((size & 1023) == 512){
+		size -= 512;
+		seek(fd, 512, 0);
+	}else if((size & 1023) == 0)
+		seek(fd, 0, 0);
+	else
+		sysfatal("invalid rom size");
+	if(size >= 16*1048576)
+		sysfatal("rom too big");
+	nprg = (size + 32767) / 32768;
+	prg = malloc(nprg * 32768);
+	if(prg == nil)
+		sysfatal("malloc: %r");
+	if(readn(fd, prg, size) < size)
+		sysfatal("read: %r");
+	close(fd);
+	switch(memread(0xffd6)){
+	case 0:
+		break;
+	case 2:
+		battery++;
+	case 1:
+		nsram = memread(0xffd8);
+		if(nsram == 0)
+			break;
+		if(nsram >= 0x0c)
+			sysfatal("invalid rom (too much ram specified)");
+		nsram = 1<<(nsram + 10);
+		sram = malloc(nsram);
+		if(sram == nil)
+			sysfatal("malloc: %r");
+		break;
+	default:
+		print("unknown rom type %d\n", memread(0xffd5));
+	}
+	if(battery && nsram != 0){
+		strncpy(buf, file, sizeof buf - 5);
+		s = buf + strlen(buf) - 4;
+		if(s < buf || strcmp(s, ".smc") != 0)
+			s += 4;
+		strcpy(s, ".sav");
+		savefd = create(buf, ORDWR | OEXCL, 0666);
+		if(savefd < 0)
+			savefd = open(buf, ORDWR);
+		if(savefd < 0)
+			message("open: %r");
+		else
+			readn(savefd, sram, nsram);
+		atexit(flushram);
+	}
+}
+
+void
+keyproc(void *)
+{
+	int fd, k;
+	static char buf[256];
+	char *s;
+	Rune r;
+
+	fd = open("/dev/kbd", OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	for(;;){
+		if(read(fd, buf, sizeof(buf) - 1) <= 0)
+			sysfatal("read /dev/kbd: %r");
+		if(buf[0] == 'c'){
+			if(utfrune(buf, Kdel)){
+				close(fd);
+				threadexitsall(nil);
+			}
+			if(utfrune(buf, 't'))
+				trace = !trace;
+		}
+		if(buf[0] != 'k' && buf[0] != 'K')
+			continue;
+		s = buf + 1;
+		k = 0;
+		while(*s != 0){
+			s += chartorune(&r, s);
+			switch(r){
+			case Kdel: close(fd); threadexitsall(nil);
+			case 'z': k |= 1<<15; break;
+			case 'x': k |= 1<<7; break;
+			case 'a': k |= 1<<14; break;
+			case 's': k |= 1<<6; break;
+			case 'q': k |= 1<<5; break;
+			case 'w': k |= 1<<4; break;
+			case Kshift: k |= 1<<13; break;
+			case 10: k |= 1<<12; break;
+			case Kup: k |= 1<<11; break;
+			case Kdown: k |= 1<<10; break;
+			case Kleft: k |= 1<<9; break;
+			case Kright: k |= 1<<8; break;
+			case Kesc:
+				if(paused)
+					qunlock(&pauselock);
+				else
+					qlock(&pauselock);
+				paused = !paused;
+				break;
+			}
+		}
+		keys = k;
+	}
+}
+
+void
+screeninit(void)
+{
+	Point p;
+
+	originwindow(screen, Pt(0, 0), screen->r.min);
+	p = divpt(addpt(screen->r.min, screen->r.max), 2);
+	picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 112)), addpt(p, Pt(scale * 128, scale * 112))};
+	tmp = allocimage(display, Rect(0, 0, scale * 256, scale * 239), RGB15, 0, 0);
+	bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+	draw(screen, screen->r, bg, nil, ZP);	
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int t;
+	extern u16int pc;
+
+	scale = 1;
+	ARGBEGIN {
+	case 's':
+		battery++;
+		break;
+	} ARGEND;
+	
+	if(argc != 1){
+		fprint(2, "usage: %s rom\n", argv0);
+		threadexitsall("usage");
+	}
+	loadrom(argv[0]);
+	if(initdraw(nil, nil, nil) < 0)
+		sysfatal("initdraw: %r");
+	mc = initmouse(nil, screen);
+	if(mc == nil)
+		sysfatal("initmouse: %r");
+	screeninit();
+	proccreate(keyproc, 0, 8192);
+	cpureset();
+	memreset();
+	spcreset();
+	for(;;){
+		if(paused){
+			qlock(&pauselock);
+			qunlock(&pauselock);
+		}
+		t = cpustep() * 8;
+		spcclock -= t;
+		stimerclock += t;
+		ppuclock += t;
+
+		while(ppuclock >= 4){
+			ppustep();
+			ppuclock -= 4;
+		}
+		while(spcclock < 0)
+			spcclock += spcstep() * SPCDIV;
+		if(stimerclock >= SPCDIV*16){
+			spctimerstep();
+			stimerclock -= SPCDIV*16;
+		}
+		if(saveclock > 0){
+			saveclock -= t;
+			if(saveclock <= 0)
+				flushram();
+		}
+		if(msgclock > 0){
+			msgclock -= t;
+			if(msgclock <= 0){
+				draw(screen, screen->r, bg, nil, ZP);
+				msgclock = 0;
+			}
+		}
+	}
+}
+
+void
+flush(void)
+{
+	extern uchar pic[256*240*2*9];
+	Mouse m;
+
+	while(nbrecv(mc->c, &m) > 0)
+		;
+	if(nbrecvul(mc->resizec) > 0){
+		if(getwindow(display, Refnone) < 0)
+			sysfatal("resize failed: %r");
+		screeninit();
+	}
+	loadimage(tmp, tmp->r, pic, 256*239*2*scale*scale);
+	draw(screen, picr, tmp, nil, ZP);
+	flushimage(display, 1);
+}
+
+void
+message(char *fmt, ...)
+{
+	va_list va;
+	static char buf[512];
+	
+	va_start(va, fmt);
+	vsnprint(buf, sizeof buf, fmt, va);
+	string(screen, Pt(10, 10), display->black, ZP, display->defaultfont, buf);
+	msgclock = FREQ;
+	va_end(va);
+}
--- /dev/null
+++ b/sys/src/games/snes/spc.c
@@ -1,0 +1,740 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int sA, sX, sY, sP, sS;
+u16int spc, scurpc;
+u8int spcmem[65536];
+u8int spctimer[4];
+static u8int ipl[64];
+
+enum {
+	SPCN = 1<<7,
+	SPCV = 1<<6,
+	SPCP = 1<<5,
+	SPCB = 1<<4,
+	SPCH = 1<<3,
+	SPCI = 1<<2,
+	SPCZ = 1<<1,
+	SPCC = 1<<0,
+};
+
+static u8int
+spcread(u16int p)
+{
+	u8int v;
+
+	if(p >= 0xffc0 && (spcmem[0xf1] & 0x80) != 0)
+		return ipl[p - 0xffc0];
+	if((p & 0xfff0) == 0x00f0)
+		switch(p){
+		case 0xf4:
+		case 0xf5:
+		case 0xf6:
+		case 0xf7:
+			return reg[0x2140 | p & 3];
+		case 0xfa:
+		case 0xfb:
+		case 0xfc:
+			return 0;
+		case 0xfd:
+		case 0xfe:
+		case 0xff:
+			v = spcmem[p];
+			spcmem[p] = 0;
+			return v;
+		}
+	return spcmem[p];
+}
+
+static void
+spcwrite(u16int p, u8int v)
+{
+	if((p & 0xfff0) == 0x00f0)
+		switch(p){
+		case 0xf0:
+			if(v != 0x0a)
+				print("SPC test register set to value %#x != 0xa\n", v);
+			return;
+		case 0xf1:
+			if((v & 0x10) != 0)
+				reg[0x2140] = reg[0x2141] = 0;
+			if((v & 0x20) != 0)
+				reg[0x2142] = reg[0x2143] = 0;
+			if((spcmem[0xf1] & 1) == 0 && (v & 1) != 0)
+				spctimer[0] = spcmem[0xfd] = 0;
+			if((spcmem[0xf1] & 2) == 0 && (v & 2) != 0)
+				spctimer[1] = spcmem[0xfe] = 0;
+			if((spcmem[0xf1] & 4) == 0 && (v & 4) != 0)
+				spctimer[2] = spcmem[0xff] = 0;
+			break;
+		case 0xfd:
+		case 0xfe:
+		case 0xff:
+			return;
+		}
+	spcmem[p] = v;
+}
+
+void
+spctimerstep(void)
+{
+	u8int m;
+	
+	m = spcmem[0xf1];
+	if(spctimer[3] == 7){
+		spctimer[3] = 0;
+		if((m & 1) != 0 && ++spctimer[0] == spcmem[0xfa]){
+			spctimer[0] = 0;
+			spcmem[0xfd] = (spcmem[0xfd] + 1) & 0xf;
+		}
+		if((m & 2) != 0 && ++spctimer[1] == spcmem[0xfb]){
+			spctimer[1] = 0;
+			spcmem[0xfe] = (spcmem[0xfe] + 1) & 0xf;
+		}
+	}else
+		spctimer[3]++;
+	if((m & 4) != 0 && ++spctimer[2] == spcmem[0xfc]){
+		spctimer[2] = 0;
+		spcmem[0xff] = (spcmem[0xff] + 1) & 0xf;
+	}
+}
+
+static u8int
+fetch8(void)
+{
+	return spcread(spc++);
+}
+
+static u16int
+fetch16(void)
+{
+	int a;
+	
+	a = fetch8();
+	a |= fetch8() << 8;
+	return a;
+}
+
+static u16int
+mem16(u16int p)
+{
+	int a;
+
+	a = spcread(p++);
+	a |= spcread(p) << 8;
+	return a;
+}
+
+static u16int
+memd16(u16int p)
+{
+	int a;
+	
+	a = spcread(p);
+	if((p & 0xff) == 0xff)
+		p &= ~0xff;
+	else
+		p++;
+	a |= spcread(p) << 8;
+	return a;
+}
+
+static void
+push8(u8int v)
+{
+	spcwrite(0x100 | sS--, v);
+}
+
+static void
+push16(u16int v)
+{
+	spcwrite(0x100 | sS--, v>>8);
+	spcwrite(0x100 | sS--, v);
+}
+
+static u8int
+pop8(void)
+{
+	return spcread(0x100 | ++sS);
+}
+
+static u16int
+pop16(void)
+{
+	u16int v;
+	
+	v = spcread(0x100 | ++sS);
+	v |= spcread(0x100 | ++sS) << 8;
+	return v;
+}
+
+#define imm() fetch8()
+#define dp ((sP&SPCP)<<3)
+#define azp() (fetch8()|dp)
+#define azpX() ((u8int)(fetch8()+sX)|dp)
+#define azpY() ((u8int)(fetch8()+sY)|dp)
+#define zp() spcread(azp())
+#define zpX() spcread(azpX())
+#define zpY() spcread(azpY())
+#define abs() spcread(fetch16())
+#define absX() spcread(fetch16()+sX)
+#define absY() spcread(fetch16()+sY)
+#define indX() spcread(aindX())
+#define indY() spcread(aindY())
+
+static u16int
+aindX(void)
+{
+	u8int r;
+	u16int a;
+	
+	r = fetch8() + sX;
+	a = spcread(r++ | dp);
+	a |= spcread(r | dp) << 8;
+	return a;
+}
+
+static u16int
+aindY(void)
+{
+	u8int r;
+	u16int a;
+	
+	r = fetch8();
+	a = spcread(r++ | dp) + sY;
+	a += spcread(r | dp) << 8;
+	return a;
+}
+
+static u8int
+nz(u8int v)
+{
+	sP &= ~(SPCN|SPCZ);
+	sP |= v & 0x80;
+	if(v == 0)
+		sP |= SPCZ;
+	return v;
+}
+
+static void
+nz16(void)
+{
+	sP &= ~(SPCN|SPCZ);
+	if(sA == 0 && sY == 0)
+		sP |= SPCZ;
+	if(sY & 0x80)
+		sP |= SPCN;
+}
+
+static int
+branch(int c, int n)
+{
+	static char s;
+	u16int npc;
+	
+	if(!c){
+		spc++;
+		return n;
+	}
+	s = fetch8();
+	npc = spc + s;
+	if(((npc ^ spc) & 0xff00) != 0)
+		n++;
+	spc = npc;
+	return ++n;	
+}
+
+static void
+clrb(u16int a, int b)
+{
+	spcwrite(a, spcread(a) & ~(1<<b));
+}
+
+static void
+cmp(u8int a, u8int b)
+{
+	sP &= ~(SPCZ|SPCN|SPCC);
+	if(a >= b)
+		sP |= SPCC;
+	if(a == b)
+		sP |= SPCZ;
+	if((a - b) & 0x80)
+		sP |= SPCN;
+}
+
+static u8int
+adc(u8int a, u8int b)
+{
+	u16int r;
+	u8int r8;
+	
+	r8 = r = a + b + (sP & SPCC);
+	sP &= ~(SPCC|SPCZ|SPCH|SPCV|SPCN);
+	if(r >= 0x100)
+		sP |= SPCC;
+	sP |= r8 & SPCN;
+	if((a ^ b ^ r) & 0x10)
+		sP |= SPCH;
+	if((~(a ^ b) & (a ^ r)) & 0x80)
+		sP |= SPCV;
+	if(r8 == 0)
+		sP |= SPCZ;
+	return r8;
+}
+
+static void
+inc(u16int a)
+{
+	spcwrite(a, nz(spcread(a) + 1));
+}
+
+static void
+jsr(u16int a)
+{
+	push16(spc);
+	spc = a;
+}
+static void
+asl(u16int a)
+{
+	u8int v;
+	
+	v = spcread(a);
+	sP &= ~SPCC;
+	sP |= v >> 7 & 1;
+	spcwrite(a, nz(v << 1));
+}
+
+static void
+lsr(u16int a)
+{
+	u8int v;
+	
+	v = spcread(a);
+	sP &= ~SPCC;
+	sP |= v & 1;
+	spcwrite(a, nz(v >> 1));
+}
+
+static void
+rol(u16int a)
+{
+	u8int v, c;
+	
+	v = spcread(a);
+	c = sP & SPCC;
+	sP &= ~SPCC;
+	sP |= v >> 7 & 1;
+	v = v<<1 | c;
+	spcwrite(a, nz(v));
+}
+
+static void
+inc16(u16int a, int c)
+{
+	u16int v;
+
+	v = memd16(a) + c;
+	sP &= ~(SPCN|SPCZ);
+	if(v == 0)
+		sP |= SPCZ;
+	if((v & 0x8000) != 0)
+		sP |= SPCN;
+	spcwrite(a, v);
+	spcwrite(a+1, v>>8);
+}
+
+static void
+ror(u16int a)
+{
+	u8int v, c;
+	
+	v = spcread(a);
+	c = sP & SPCC;
+	sP &= ~SPCC;
+	sP |= v & 1;
+	v = v>>1 | c<<7;
+	spcwrite(a, nz(v));
+}
+
+static u8int
+sbc(u8int a, u8int b)
+{
+	return adc(a, ~b);
+}
+
+static void
+setb(u16int a, int b)
+{
+	spcwrite(a, spcread(a) | (1<<b));
+}
+
+static void
+setnbit(u16int a, int c)
+{
+	u8int v, b;
+	
+	b = a >> 13;
+	v = spcread(a & 0x1fff) & ~(1<<b);
+	if(c)
+		v |= (1<<b);
+	spcwrite(a & 0x1fff, v);
+}
+
+static void
+tset(u16int a, int set)
+{
+	u8int v;
+	
+	v = spcread(a);
+	nz(sA - v);
+	if(set)
+		v |= sA;
+	else
+		v &= ~sA;
+	spcwrite(a, v);
+}
+
+static void
+div(void)
+{
+	u32int v, x;
+	int i;
+	
+	sP &= ~(SPCH|SPCV);
+	if((sX & 0xf) <= (sY & 0xf))
+		sP |= SPCH;
+	v = sA | sY << 8;
+	x = sX << 9;
+	for(i = 0; i < 9; i++){
+		v = (v << 1 | v >> 16) & 0x1ffff;
+		if(v >= x)
+			v ^= 1;
+		if((v & 1) != 0)
+			v = (v - x) & 0x1ffff;
+	}
+	nz(sA = v);
+	sY = v >> 9;
+	if((v & 0x100) != 0)
+		sP |= SPCV;
+}
+
+void
+spcreset(void)
+{
+	spcmem[0xf0] = 0x0a;
+	spcmem[0xf1] = 0xb0;
+	spc = spcread(0xfffe) | spcread(0xffff) << 8;
+}
+
+int
+spcstep(void)
+{
+	static int ctr;
+	u8int op, a;
+	u16int b, c;
+
+	scurpc = spc;
+	op = fetch8();
+	if(trace)
+		print("SPC %.4x %.2x A=%.2x X=%.2x Y=%.2x P=%.2x S=%.2x\n", spc-1, op, sA, sX, sY, sP, sS);
+	switch(op){
+	case 0x01: jsr(mem16(0xffde)); return 8;
+	case 0x02: setb(azp(), 0); return 4;
+	case 0x03: return branch((zp() & 0x01) != 0, 5);
+	case 0x04: nz(sA |= zp()); return 3;
+	case 0x05: nz(sA |= abs()); return 4;
+	case 0x06: nz(sA |= spcread(sX|dp)); return 3;
+	case 0x07: nz(sA |= indX()); return 6;
+	case 0x08: nz(sA |= imm()); return 2;
+	case 0x09: b = zp(); c = azp(); spcwrite(c, nz(b | spcread(c))); return 6;
+	case 0x0A: b = fetch16(); sP |= (spcread(b & 0x1fff) >> (b >> 13)) & 1;  return 4;
+	case 0x0B: asl(azp()); return 5;
+	case 0x0C: asl(fetch16()); return 5;
+	case 0x0D: push8(sP); return 4;
+	case 0x0E: tset(fetch16(), 1); return 6;
+	case 0x10: return branch((sP & SPCN) == 0, 2);
+	case 0x11: jsr(mem16(0xffdc)); return 8;
+	case 0x12: clrb(azp(), 0); return 4;
+	case 0x13: return branch((zp() & 0x01) == 0, 5);
+	case 0x14: nz(sA |= zpX()); return 4;
+	case 0x15: nz(sA |= absX()); return 5;
+	case 0x16: nz(sA |= absY()); return 5;
+	case 0x17: nz(sA |= indY()); return 6;
+	case 0x18: a = imm(); b = azp(); spcwrite(b, nz(spcread(b) | a)); return 5;
+	case 0x19: spcwrite(sX|dp, nz(spcread(sX|dp) | spcread(sY|dp))); return 5;
+	case 0x1A: inc16(azp(), -1); return 6;
+	case 0x1B: asl(azpX()); return 5;
+	case 0x1C: sP &= ~SPCC; sP |= sA >> 7; nz(sA <<= 1); return 2;
+	case 0x1D: nz(--sX); return 2;
+	case 0x1E: cmp(sX, abs()); return 4;
+	case 0x1F: spc = mem16(fetch16() + sX); return 6;
+	case 0x20: sP &= ~SPCP; return 2;
+	case 0x21: jsr(mem16(0xffda)); return 8;
+	case 0x22: setb(azp(), 1); return 4;
+	case 0x23: return branch((zp() & 0x02) != 0, 5);
+	case 0x24: nz(sA &= zp()); return 3;
+	case 0x25: nz(sA &= abs()); return 4;
+	case 0x26: nz(sA &= spcread(sX|dp)); return 3;
+	case 0x27: nz(sA &= indX()); return 6;
+	case 0x28: nz(sA &= imm()); return 2;
+	case 0x29: b = zp(); c = azp(); spcwrite(c, nz(b & spcread(c))); return 6;
+	case 0x2A: b = fetch16(); sP |= (~spcread(b & 0x1fff) >> (b >> 13)) & 1;  return 4;
+	case 0x2B: rol(azp()); return 4;
+	case 0x2C: rol(fetch16()); return 5;
+	case 0x2D: push8(sA); return 4;
+	case 0x2E: return branch(sA != zp(), 5);
+	case 0x2F: return branch(1, 2);
+	case 0x30: return branch((sP & SPCN) != 0, 2);
+	case 0x31: jsr(mem16(0xffd8)); return 8;
+	case 0x32: clrb(azp(), 1); return 4;
+	case 0x33: return branch((zp() & 0x02) == 0, 5);
+	case 0x34: nz(sA &= zpX()); return 4;
+	case 0x35: nz(sA &= absX()); return 5;
+	case 0x36: nz(sA &= absY()); return 5;
+	case 0x37: nz(sA &= indY()); return 6;
+	case 0x38: a = imm(); b = azp(); spcwrite(b, nz(spcread(b) & a)); return 5;
+	case 0x39: spcwrite(sX|dp, nz(spcread(sX|dp) & spcread(sY|dp))); return 5;
+	case 0x3A: inc16(azp(), 1); return 6;
+	case 0x3B: rol(azpX()); return 5;
+	case 0x3C:
+		a = sP & SPCC;
+		sP &= ~SPCC;
+		sP |= sA >> 7 & 1;
+		sA = sA << 1 | a;
+		nz(sA);
+		return 2;
+	case 0x3D: nz(++sX); return 2;
+	case 0x3E: cmp(sX, zp()); return 3;
+	case 0x3F: jsr(fetch16()); return 8;
+	case 0x40: sP |= SPCP; return 2;
+	case 0x41: jsr(mem16(0xffd6)); return 8;
+	case 0x42: setb(azp(), 2); return 4;
+	case 0x43: return branch((zp() & 0x04) != 0, 5);
+	case 0x44: nz(sA ^= zp()); return 3;
+	case 0x45: nz(sA ^= abs()); return 4;
+	case 0x46: nz(sA ^= spcread(sX|dp)); return 3;
+	case 0x47: nz(sA ^= indX()); return 6;
+	case 0x48: nz(sA ^= imm()); return 2;
+	case 0x49: b = zp(); c = azp(); spcwrite(c, nz(b ^ spcread(c))); return 6;
+	case 0x4A: b = fetch16(); sP &= 0xfe | (spcread(b & 0x1fff) >> (b >> 13)) & 1;  return 4;
+	case 0x4B: lsr(azp()); return 4;
+	case 0x4C: lsr(fetch16()); return 5;
+	case 0x4D: push8(sX); return 4;
+	case 0x4E: tset(fetch16(), 0); return 5;
+	case 0x4F: jsr(0xff00 | fetch8()); return 6;
+	case 0x50: return branch((sP & SPCV) == 0, 2);
+	case 0x51: jsr(mem16(0xffd4)); return 8;
+	case 0x52: clrb(azp(), 2); return 4;
+	case 0x53: return branch((zp() & 0x04) == 0, 5);
+	case 0x54: nz(sA ^= zpX()); return 4;
+	case 0x55: nz(sA ^= absX()); return 5;
+	case 0x56: nz(sA ^= absY()); return 5;
+	case 0x57: nz(sA ^= indY()); return 6;
+	case 0x58: a = imm(); b = azp(); spcwrite(b, nz(spcread(b) ^ a)); return 5;
+	case 0x59: spcwrite(sX|dp, nz(spcread(sX|dp) ^ spcread(sY|dp))); return 5;
+	case 0x5A: 
+		b = sA | sY << 8;
+		c = memd16(azp());
+		sP &= ~(SPCN|SPCZ|SPCC);
+		if(b >= c)
+			sP |= SPCC;
+		if(b == c)
+			sP |= SPCZ;
+		if(((b - c) & 0x8000) != 0)
+			sP |= SPCN;
+		return 4;
+	case 0x5B: lsr(azpX()); return 4;
+	case 0x5C: sP &= ~SPCC; sP |= sA & 1; nz(sA >>= 1); return 2;
+	case 0x5D: nz(sX = sA); return 2;
+	case 0x5E: cmp(sY, abs()); return 4;
+	case 0x5F: spc = fetch16(); return 3;
+	case 0x60: sP &= ~SPCC; return 2;
+	case 0x61: jsr(mem16(0xffd2)); return 8;
+	case 0x62: setb(azp(), 3); return 4;
+	case 0x63: return branch((zp() & 0x08) != 0, 5);
+	case 0x64: cmp(sA, zp()); return 3;
+	case 0x65: cmp(sA, abs()); return 4;
+	case 0x66: cmp(sA, spcread(sX|dp)); return 3;
+	case 0x67: cmp(sA, indX()); return 6;
+	case 0x68: cmp(sA, imm()); return 2;
+	case 0x69: a = zp(); cmp(zp(), a); return 6;
+	case 0x6A: b = fetch16(); sP &= ~((spcread(b & 0x1fff) >> (b >> 13)) & 1);  return 4;
+	case 0x6B: ror(azp()); return 4;
+	case 0x6C: ror(fetch16()); return 5;
+	case 0x6D: push8(sY); return 4;
+	case 0x6E: b = azp(); a = spcread(b)-1; spcwrite(b, a); return branch(a != 0, 5);
+	case 0x6F: spc = pop16(); return 5;
+	case 0x70: return branch((sP & SPCV) != 0, 2);
+	case 0x72: clrb(azp(), 3); return 4;
+	case 0x71: jsr(mem16(0xffd0)); return 8;
+	case 0x73: return branch((zp() & 0x08) == 0, 5);
+	case 0x74: cmp(sA, zpX()); return 4;
+	case 0x75: cmp(sA, absX()); return 5;
+	case 0x76: cmp(sA, absY()); return 5;
+	case 0x77: cmp(sA, indY()); return 6;
+	case 0x78: a = imm(); cmp(zp(), a); return 5;
+	case 0x79: cmp(spcread(sX|dp), spcread(sY|dp)); return 5;
+	case 0x7A:
+		b = memd16(azp());
+		sA = adc(sA, b);
+		sY = adc(sY, b >> 8);
+		if(sA != 0)
+			sP &= ~SPCZ;
+		return 5;
+	case 0x7B: ror(azpX()); return 5;
+	case 0x7C:
+		a = sP & SPCC;
+		sP &= ~SPCC;
+		sP |= sA & 1;
+		sA = sA >> 1 | a << 7;
+		nz(sA);
+		return 2;
+	case 0x7D: nz(sA = sX); return 2;
+	case 0x7E: cmp(sY, zp()); return 3;
+	case 0x7F: sP = pop8(); spc = pop16(); return 6;
+	case 0x80: sP |= SPCC; return 2;
+	case 0x81: jsr(mem16(0xffce)); return 8;
+	case 0x82: setb(azp(), 4); return 4;
+	case 0x83: return branch((zp() & 0x10) != 0, 5);
+	case 0x84: sA = adc(sA, zp()); return 3;
+	case 0x85: sA = adc(sA, abs()); return 4;
+	case 0x86: sA = adc(sA, spcread(sX|dp)); return 3;
+	case 0x87: sA = adc(sA, indX()); return 6;
+	case 0x88: sA = adc(sA, imm()); return 2;
+	case 0x89: b = zp(); c = azp(); spcwrite(c, adc(b, spcread(c))); return 6;
+	case 0x8A: b = fetch16(); sP ^= (spcread(b & 0x1fff) >> (b >> 13)) & 1;  return 4;
+	case 0x8B: b = azp(); spcwrite(b, nz(spcread(b)-1)); return 4;
+	case 0x8C: b = fetch16(); spcwrite(b, nz(spcread(b)-1)); return 4;
+	case 0x8D: nz(sY = imm()); return 2;
+	case 0x8E: sP = pop8(); return 2;
+	case 0x8F: a = fetch8(); spcwrite(azp(), a); return 5;
+	case 0x90: return branch((sP & SPCC) == 0, 2);
+	case 0x91: jsr(mem16(0xffcc)); return 8;
+	case 0x92: clrb(azp(), 4); return 4;
+	case 0x93: return branch((zp() & 0x10) == 0, 5);
+	case 0x94: sA = adc(sA, zpX()); return 4;
+	case 0x95: sA = adc(sA, absX()); return 5;
+	case 0x96: sA = adc(sA, absY()); return 5;
+	case 0x97: sA = adc(sA, indY()); return 6;
+	case 0x98: a = imm(); b = azp(); spcwrite(b, adc(spcread(b), a)); return 5;
+	case 0x99: spcwrite(sX|dp, adc(spcread(sX|dp), spcread(sY|dp))); return 5;
+	case 0x9A:
+		b = memd16(azp());
+		sA = sbc(sA, b);
+		sY = sbc(sY, b >> 8);
+		if(sA != 0)
+			sP &= ~SPCZ;
+		return 5;
+	case 0x9B: b = azpX(); spcwrite(b, nz(spcread(b)-1)); return 4;
+	case 0x9C: nz(--sA); return 2;
+	case 0x9D: nz(sX = sS); return 2;
+	case 0x9E: div(); return 12;
+	case 0x9F: nz(sA = sA >> 4 | sA << 4); return 5;
+	case 0xA0: sP |= SPCI; return 2;
+	case 0xA1: jsr(mem16(0xffca)); return 8;
+	case 0xA2: setb(azp(), 5); return 4;
+	case 0xA3: return branch((zp() & 0x20) != 0, 5);
+	case 0xA4: sA = sbc(sA, zp()); return 3;
+	case 0xA5: sA = sbc(sA, abs()); return 4;
+	case 0xA6: sA = sbc(sA, spcread(sX|dp)); return 3;
+	case 0xA7: sA = sbc(sA, indX()); return 6;
+	case 0xA8: sA = sbc(sA, imm()); return 2;
+	case 0xA9: b = zp(); c = azp(); spcwrite(c, sbc(spcread(c), b)); return 6;
+	case 0xAA: b = fetch16(); sP &= ~1; sP |= (spcread(b & 0x1fff) >> (b >> 13)) & 1;  return 4;
+	case 0xAB: inc(azp()); return 4;
+	case 0xAC: inc(fetch16()); return 5;
+	case 0xAD: cmp(sY, imm()); return 2;
+	case 0xAE: sA = pop8(); return 2;
+	case 0xAF: spcwrite(sX++|dp, sA); return 4;
+	case 0xB0: return branch((sP & SPCC) != 0, 2);
+	case 0xB1: jsr(mem16(0xffc8)); return 8;
+	case 0xB2: clrb(azp(), 5); return 4;
+	case 0xB3: return branch((zp() & 0x20) == 0, 5);
+	case 0xB4: sA = sbc(sA, zpX()); return 4;
+	case 0xB5: sA = sbc(sA, absX()); return 5;
+	case 0xB6: sA = sbc(sA, absY()); return 5;
+	case 0xB7: sA = sbc(sA, indY()); return 6;
+	case 0xB8: a = imm(); b = azp(); spcwrite(b, sbc(spcread(b), a)); return 5;
+	case 0xB9: spcwrite(sX|dp, sbc(spcread(sX|dp), spcread(sY|dp))); return 5;
+	case 0xBA: a = fetch8(); sA = spcread(a++|dp); sY = spcread(a|dp); nz16(); return 5;
+	case 0xBB: inc(azpX()); return 4;
+	case 0xBC: nz(++sA); return 2;
+	case 0xBD: sS = sX; return 2;
+	case 0xBF: nz(sA = spcread(sX++|dp)); return 3;
+	case 0xC0: sP &= ~SPCI; return 2;
+	case 0xC1: jsr(mem16(0xffc6)); return 8;
+	case 0xC2: setb(azp(), 6); return 4;
+	case 0xC3: return branch((zp() & 0x40) != 0, 5);
+	case 0xC4: spcwrite(azp(), sA); return 4;
+	case 0xC5: spcwrite(fetch16(), sA); return 5;
+	case 0xC6: spcwrite(sX|dp, sA); return 4;
+	case 0xC7: spcwrite(aindX(), sA); return 7;
+	case 0xC8: cmp(sX, imm()); return 2;
+	case 0xC9: spcwrite(fetch16(), sX); return 5;
+	case 0xCA: b = fetch16(); setnbit(b, sP & SPCC); return 6;
+	case 0xCB: spcwrite(azp(), sY); return 4;
+	case 0xCC: spcwrite(fetch16(), sY); return 5;
+	case 0xCD: nz(sX = imm()); return 2;
+	case 0xCE: sX = pop8(); return 2;
+	case 0xCF: b = sY * sA; nz(sY = b >> 8); sA = b; return 9;
+	case 0xD0: return branch((sP & SPCZ) == 0, 2);
+	case 0xD1: jsr(mem16(0xffc4)); return 8;
+	case 0xD2: clrb(azp(), 6); return 4;
+	case 0xD3: return branch((zp() & 0x40) == 0, 5);
+	case 0xD4: spcwrite(azpX(), sA); return 4;
+	case 0xD5: spcwrite(fetch16() + sX, sA); return 6;
+	case 0xD6: spcwrite(fetch16() + sY, sA); return 6;
+	case 0xD7: spcwrite(aindY(), sA); return 7;
+	case 0xD8: spcwrite(azp(), sX); return 4;
+	case 0xD9: spcwrite(azpY(), sX); return 5;
+	case 0xDA: a = fetch8(); spcwrite(a++|dp, sA); spcwrite(a|dp, sY); return 5;
+	case 0xDB: spcwrite(azpX(), sY); return 5;
+	case 0xDC: nz(--sY); return 2;
+	case 0xDD: nz(sA = sY); return 2;
+	case 0xDE: return branch(sA != zpX(), 6);
+	case 0xE0: sP &= ~(SPCV|SPCH); return 2;
+	case 0xE1: jsr(mem16(0xffc2)); return 8;
+	case 0xE2: setb(azp(), 7); return 4;
+	case 0xE3: return branch((zp() & 0x80) != 0, 5);
+	case 0xE4: nz(sA = zp()); return 3;
+	case 0xE5: nz(sA = abs()); return 4;
+	case 0xE6: nz(sA = spcread(sX|dp)); return 3;
+	case 0xE7: nz(sA = indX()); return 6;
+	case 0xE8: nz(sA = imm()); return 2;
+	case 0xE9: nz(sX = abs()); return 4;
+	case 0xEA: b = fetch16(); spcwrite(b & 0x1fff, spcread(b & 0x1fff) ^ (1<<(b>>13))); return 6;
+	case 0xEB: nz(sY = zp()); return 3;
+	case 0xEC: nz(sY = abs()); return 4;
+	case 0xED: sP ^= SPCC; return 3;
+	case 0xEE: sY = pop8(); return 4;
+	case 0xF0: return branch((sP & SPCZ) != 0, 2);
+	case 0xF1: jsr(mem16(0xffc0)); return 8;
+	case 0xF2: clrb(azp(), 7); return 4;
+	case 0xF3: return branch((zp() & 0x80) == 0, 5);
+	case 0xF4: nz(sA = zpX()); return 4;
+	case 0xF5: nz(sA = absX()); return 5;
+	case 0xF6: nz(sA = absY()); return 5;
+	case 0xF7: nz(sA = indY()); return 6;
+	case 0xF8: nz(sX = zp()); return 3;
+	case 0xF9: nz(sX = zpY()); return 4;
+	case 0xFA: a = zp(); spcwrite(azp(), a); return 5;
+	case 0xFB: nz(sY = zpX()); return 4;
+	case 0xFC: nz(++sY); return 2;
+	case 0xFD: nz(sY = sA); return 2;
+	case 0xFE: return branch(--sY, 4);
+	default:
+		print("undefined spc op %.2x at %.4x\n", op, spc-1);
+		return 2;
+	}
+}
+
+static u8int ipl[64] = {
+	0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa, 0xf4, 0x8f, 0xbb, 0xf5, 0x78,
+	0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19, 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5,
+	0xcb, 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e, 0xf4, 0x10, 0xeb, 0xba,
+	0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4, 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff,           
+};