shithub: riscv

Download patch

ref: 1225ebec7181167fbefc5499300260e93c9b5191
parent: ae41f49f09de058a500a02f4a0fa2b463b8b44de
author: aiju <devnull@localhost>
date: Sun Feb 16 15:51:11 EST 2014

added games/nes

diff: cannot open b/sys/src/games/nes//null: file does not exist: 'b/sys/src/games/nes//null'
--- /dev/null
+++ b/sys/src/games/nes/cpu.c
@@ -1,0 +1,438 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+u16int pc, curpc;
+u8int rA, rX, rY, rS, rP;
+int nmi;
+
+static u8int
+fetch8(void)
+{
+	return memread(pc++);
+}
+
+static u16int
+fetch16(void)
+{
+	u16int r;
+	
+	r = memread(pc++);
+	r |= memread(pc++) << 8;
+	return r;
+}
+
+static void
+push8(u8int v)
+{
+	memwrite(0x100 | rS--, v);
+}
+
+static void
+push16(u16int v)
+{
+	memwrite(0x100 | rS--, v >> 8);
+	memwrite(0x100 | rS--, v);
+}
+
+static u8int
+pop8(void)
+{
+	return memread(0x100 | ++rS);
+}
+
+static u16int
+pop16(void)
+{
+	u16int v;
+	
+	v = memread(0x100 | ++rS);
+	v |= memread(0x100 | ++rS) << 8;
+	return v;
+}
+
+#define imm() fetch8()
+#define zp() memread(fetch8())
+#define zpX() memread((u8int)(fetch8()+rX))
+#define zpY() memread((u8int)(fetch8()+rY))
+#define abso() memread(fetch16())
+#define absX() memread(a=fetch16()+rX)
+#define absY() memread(a=fetch16()+rY)
+#define indX() memread(aindX())
+#define indY(c) memread(aindY(c))
+
+static u16int
+aindX(void)
+{
+	u8int r;
+	u16int a;
+	
+	r = fetch8() + rX;
+	a = memread(r++);
+	a |= memread(r) << 8;
+	return a;
+}
+
+static u16int
+aindY(int *c)
+{
+	u8int r;
+	u16int a;
+	
+	r = fetch8();
+	a = memread(r++) + rY;
+	*c = a > 0xFF;
+	a += memread(r) << 8;
+	return a;
+}
+
+static void
+adc(u8int d)
+{
+	int r;
+	
+	r = rA + d + (rP & FLAGC);
+	rP &= ~(FLAGN | FLAGZ | FLAGV | FLAGC);
+	if(r > 0xFF) rP |= FLAGC;
+	if(r & 0x80) rP |= FLAGN;
+	if((~(rA ^ d) & (rA ^ r)) & 0x80) rP |= FLAGV;
+	rA = r;
+	if(rA == 0) rP |= FLAGZ;
+}
+
+static u8int
+nz(u8int d)
+{
+	rP &= ~(FLAGN | FLAGZ);
+	if(d & 0x80) rP |= FLAGN;
+	if(d == 0) rP |= FLAGZ;
+	return d;
+}
+
+static void
+asl(u16int a)
+{
+	u8int v;
+
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	v = memread(a);
+	if(v & 0x80) rP |= FLAGC;
+	v <<= 1;
+	if(v == 0) rP |= FLAGZ;
+	if(v & 0x80) rP |= FLAGN;
+	memwrite(a, v);
+}
+
+static void
+lsr(u16int a)
+{
+	u8int v;
+
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	v = memread(a);
+	rP |= v & 1;
+	v >>= 1;
+	if(v == 0) rP |= FLAGZ;
+	if(v & 0x80) rP |= FLAGN;
+	memwrite(a, v);
+}
+
+static int
+branch(void)
+{
+	signed char t;
+	u16int npc;
+	
+	t = fetch8();
+	npc = pc + t;
+	if((npc ^ pc) >> 8){
+		pc = npc;
+		return 4;
+	}
+	pc = npc;
+	return 3;
+}
+
+static void
+cmp(u8int a, u8int d)
+{
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	if(a == d) rP |= FLAGZ;
+	if(a >= d) rP |= FLAGC;
+	if((a - d) & 0x80) rP |= FLAGN;
+}
+
+static void
+dec(u16int a)
+{
+	memwrite(a, nz(memread(a) - 1));
+}
+
+static void
+inc(u16int a)
+{
+	memwrite(a, nz(memread(a) + 1));
+}
+
+static void
+rol(u16int a)
+{
+	u8int v, b;
+	
+	v = memread(a);
+	b = rP & FLAGC;
+	rP &= ~(FLAGC | FLAGN | FLAGZ);
+	if(v & 0x80) rP |= FLAGC;
+	v = (v << 1) | b;
+	if(v & 0x80) rP |= FLAGN;
+	if(v == 0) rP |= FLAGZ;
+	memwrite(a, v);
+}
+
+static void
+ror(u16int a)
+{
+	u8int v, b;
+	
+	v = memread(a);
+	b = rP & FLAGC;
+	rP &= ~(FLAGC | FLAGN | FLAGZ);
+	rP |= v & 1;
+	v = (v >> 1) | (b << 7);
+	if(v & 0x80) rP |= FLAGN;
+	if(v == 0) rP |= FLAGZ;
+	memwrite(a, v);
+}
+
+static void
+sbc(u8int d)
+{
+	int r;
+	
+	r = rA + (u8int)~d + (rP & FLAGC);
+	rP &= ~(FLAGZ | FLAGV | FLAGC | FLAGN);
+	if(r > 0xFF) rP |= FLAGC;
+	if(((rA ^ d) & (rA ^ r)) & 0x80) rP |= FLAGV;
+	rA = r;
+	if(rA == 0) rP |= FLAGZ;
+	if(rA & 0x80) rP |= FLAGN;
+}
+
+static void
+interrupt(int nmi, int brk)
+{
+	push16(pc);
+	push8(rP | 0x20 | (brk << 4));
+	pc = memread(0xFFFA | (!nmi << 2));
+	pc |= memread(0xFFFB | (!nmi << 2)) << 8;
+	rP |= FLAGI;
+}
+
+int
+step(void)
+{
+	u8int op;
+	u16int a, v;
+	int c;
+	
+	if(nmi){
+		interrupt(1, 0);
+		nmi = 0;
+		return 7;
+	}
+	curpc = pc;
+	op = fetch8();
+	switch(op){
+	case 0x00: pc++; interrupt(0, 1); return 7;
+	case 0x01: nz(rA |= indX()); return 6;
+	case 0x05: nz(rA |= zp()); return 3;
+	case 0x06: asl(fetch8()); return 5;
+	case 0x08: push8(rP | 0x30); return 3;
+	case 0x09: nz(rA |= imm()); return 2;
+	case 0x0A:
+		rP &= ~(FLAGN | FLAGZ | FLAGC);
+		if(rA & 0x80) rP |= FLAGC;
+		rA <<= 1;
+		if(rA == 0) rP |= FLAGZ;
+		if(rA & 0x80) rP |= FLAGN;
+		return 2;
+	case 0x0D: nz(rA |= abso()); return 4;
+	case 0x0E: asl(fetch16()); return 6;
+	case 0x10: if((rP & FLAGN) == 0) return branch(); pc++; return 2;
+	case 0x11: nz(rA |= indY(&c)); return 5+c;
+	case 0x15: nz(rA |= zpX()); return 4;
+	case 0x16: asl((u8int)(fetch8() + rX)); return 6;
+	case 0x18: rP &= ~FLAGC; return 2;
+	case 0x19: nz(rA |= absY()); return 4 + ((u8int)a < rY);
+	case 0x1D: nz(rA |= absX()); return 4 + ((u8int)a < rX);
+	case 0x1E: asl(fetch16() + rX); return 7;
+	case 0x20: push16(pc+1); pc = fetch16(); return 6;
+	case 0x21: nz(rA &= indX()); return 6;
+	case 0x24:
+		a = memread(fetch8());
+		rP &= ~(FLAGN | FLAGZ | FLAGV);
+		rP |= a & 0xC0;
+		if((a & rA) == 0) rP |= FLAGZ;
+		return 3;
+	case 0x25: nz(rA &= zp()); return 3;
+	case 0x26: rol(fetch8()); return 5;
+	case 0x28: rP = pop8() & 0xcf; return 4;
+	case 0x29: nz(rA &= imm()); return 2;
+	case 0x2A:
+		a = rP & FLAGC;
+		rP &= ~(FLAGC | FLAGZ | FLAGN);
+		if(rA & 0x80) rP |= FLAGC;
+		rA = (rA << 1) | a;
+		if(rA & 0x80) rP |= FLAGN;
+		if(rA == 0) rP |= FLAGZ;
+		return 2;
+	case 0x2C:
+		a = memread(fetch16());
+		rP &= ~(FLAGN | FLAGZ | FLAGV);
+		rP |= a & 0xC0;
+		if((a & rA) == 0) rP |= FLAGZ;
+		return 4;
+	case 0x2D: nz(rA &= abso()); return 4;
+	case 0x2E: rol(fetch16()); return 6;
+	case 0x30: if((rP & FLAGN) != 0) return branch(); pc++; return 3;
+	case 0x31: nz(rA &= indY(&c)); return 5+c;
+	case 0x35: nz(rA &= zpX()); return 4;
+	case 0x36: rol((u8int)(fetch8() + rX)); return 6;
+	case 0x38: rP |= FLAGC; return 2;
+	case 0x39: nz(rA &= absY()); return 4 + ((u8int)a < rY);
+	case 0x3E: rol(fetch16() + rX); return 7;
+	case 0x3D: nz(rA &= absX()); return 4 + ((u8int)a < rX);
+	case 0x40: rP = pop8() & 0xcf; pc = pop16(); return 6;
+	case 0x41: nz(rA ^= indX()); return 6;
+	case 0x45: nz(rA ^= zp()); return 3;
+	case 0x46: lsr(fetch8()); return 5;
+	case 0x48: push8(rA); return 3;
+	case 0x49: nz(rA ^= imm()); return 2;
+	case 0x4A:
+		rP &= ~(FLAGN | FLAGZ | FLAGC);
+		rP |= rA & 1;
+		rA >>= 1;
+		if(rA == 0) rP |= FLAGZ;
+		if(rA & 0x80) rP |= FLAGN;
+		return 2;
+	case 0x4C: pc = fetch16(); return 3;
+	case 0x4D: nz(rA ^= abso()); return 4;
+	case 0x4E: lsr(fetch16()); return 6;
+	case 0x51: nz(rA ^= indY(&c)); return 5+c;
+	case 0x56: lsr((u8int)(fetch8() + rX)); return 6;
+	case 0x58: rP &= ~FLAGI; return 2;
+	case 0x50: if((rP & FLAGV) == 0) return branch(); pc++; return 3;
+	case 0x55: nz(rA ^= zpX()); return 4;
+	case 0x59: nz(rA ^= absY()); return 4 + ((u8int)a < rX);
+	case 0x5D: nz(rA ^= absX()); return 4 + ((u8int)a < rX);
+	case 0x5E: lsr(fetch16() + rX); return 7;
+	case 0x60: pc = pop16() + 1; return 6;
+	case 0x61: adc(indX()); return 6;
+	case 0x65: adc(zp()); return 3;
+	case 0x66: ror(fetch8()); return 5;
+	case 0x68: nz(rA = pop8()); return 4;
+	case 0x69: adc(imm()); return 2;
+	case 0x6A:
+		a = rP & FLAGC;
+		rP &= ~(FLAGC | FLAGN | FLAGZ);
+		rP |= rA & 1;
+		rA = (rA >> 1) | (a << 7);
+		if(rA & 0x80) rP |= FLAGN;
+		if(rA == 0) rP |= FLAGZ;
+		return 2;
+	case 0x6C: v = fetch16(); pc = memread(v) | (memread((v & 0xFF00) | (u8int)(v+1)) << 8); return 5;
+	case 0x6D: adc(abso()); return 4;
+	case 0x6E: ror(fetch16()); return 6;
+	case 0x70: if((rP & FLAGV) != 0) return branch(); pc++; return 3;
+	case 0x71: adc(indY(&c)); return 5+c;
+	case 0x75: adc(zpX()); return 4;
+	case 0x76: ror((u8int)(fetch8() + rX)); return 6;
+	case 0x78: rP |= FLAGI; return 2;
+	case 0x79: adc(absY()); return 4 + ((u8int)a < rY);
+	case 0x7D: adc(absX()); return 4 + ((u8int)a < rX);
+	case 0x7E: ror(fetch16() + rX); return 7;
+	case 0x81: memwrite(aindX(), rA); return 6;
+	case 0x84: memwrite(fetch8(), rY); return 3;
+	case 0x85: memwrite(fetch8(), rA); return 3;
+	case 0x86: memwrite(fetch8(), rX); return 3;
+	case 0x88: nz(--rY); return 2;
+	case 0x8A: nz(rA = rX); return 2;
+	case 0x8C: memwrite(fetch16(), rY); return 4;
+	case 0x8D: memwrite(fetch16(), rA); return 4;
+	case 0x8E: memwrite(fetch16(), rX); return 4;
+	case 0x90: if((rP & FLAGC) == 0) return branch(); pc++; return 3;
+	case 0x91: memwrite(aindY(&c), rA); return 6;
+	case 0x94: memwrite((u8int)(fetch8() + rX), rY); return 4;
+	case 0x95: memwrite((u8int)(fetch8() + rX), rA); return 4;
+	case 0x96: memwrite((u8int)(fetch8() + rY), rX); return 4;
+	case 0x98: nz(rA = rY); return 2;
+	case 0x99: memwrite(fetch16() + rY, rA); return 5;
+	case 0x9A: rS = rX; return 2;
+	case 0x9D: memwrite(fetch16() + rX, rA); return 5;
+	case 0xA0: nz(rY = imm()); return 2;
+	case 0xA1: nz(rA = indX()); return 6;
+	case 0xA2: nz(rX = imm()); return 2;
+	case 0xA4: nz(rY = zp()); return 3;
+	case 0xA5: nz(rA = zp()); return 3;
+	case 0xA6: nz(rX = zp()); return 3;
+	case 0xA8: nz(rY = rA); return 2;
+	case 0xA9: nz(rA = imm()); return 2;
+	case 0xAA: nz(rX = rA); return 2;
+	case 0xAC: nz(rY = abso()); return 4;
+	case 0xAE: nz(rX = abso()); return 4;
+	case 0xAD: nz(rA = abso()); return 4;
+	case 0xB0: if((rP & FLAGC) != 0) return branch(); pc++; return 3;
+	case 0xB1: nz(rA = indY(&c)); return 5+c;
+	case 0xB4: nz(rY = zpX()); return 4;
+	case 0xB5: nz(rA = zpX()); return 4;
+	case 0xB6: nz(rX = zpY()); return 4;
+	case 0xB8: rP &= ~FLAGV; return 2;
+	case 0xB9: nz(rA = absY()); return 4 + ((u8int)a < rY);
+	case 0xBA: nz(rX = rS); return 2;
+	case 0xBC: nz(rY = absX()); return 4 + ((u8int)a < rX);
+	case 0xBD: nz(rA = absX()); return 4 + ((u8int)a < rX);
+	case 0xBE: nz(rX = absY()); return 4 + ((u8int)a < rY);
+	case 0xC1: cmp(rA, indX()); return 6;
+	case 0xC5: cmp(rA, zp()); return 3;
+	case 0xC9: cmp(rA, imm()); return 2;
+	case 0xCD: cmp(rA, abso()); return 4;
+	case 0xD1: cmp(rA, indY(&c)); return 5 + c;
+	case 0xD5: cmp(rA, zpX()); return 4;
+	case 0xD8: rP &= ~FLAGD; return 2;
+	case 0xD9: cmp(rA, absY()); return 4 + ((u8int)a < rY);
+	case 0xDD: cmp(rA, absX()); return 4 + ((u8int)a < rX);
+	case 0xD0: if((rP & FLAGZ) == 0) return branch(); pc++; return 3;
+	case 0xC0: cmp(rY, imm()); return 2;
+	case 0xC4: cmp(rY, zp()); return 3;
+	case 0xC6: dec(fetch8()); return 5;
+	case 0xC8: nz(++rY); return 2;
+	case 0xCA: nz(--rX); return 2;
+	case 0xCC: cmp(rY, abso()); return 4;
+	case 0xCE: dec(fetch16()); return 6;
+	case 0xD6: dec((u8int)(fetch8() + rX)); return 6;
+	case 0xDE: dec(fetch16() + rX); return 7;
+	case 0xE0: cmp(rX, imm()); return 2;
+	case 0xE1: sbc(indX()); return 6;
+	case 0xE4: cmp(rX, zp()); return 3;
+	case 0xE5: sbc(zp()); return 3;
+	case 0xE6: inc(fetch8()); return 5;
+	case 0xE8: nz(++rX); return 2;
+	case 0xE9: sbc(imm()); return 2;
+	case 0xEA: return 2;
+	case 0xEC: cmp(rX, abso()); return 4;
+	case 0xED: sbc(abso()); return 4;
+	case 0xEE: inc(fetch16()); return 6;
+	case 0xF0: if((rP & FLAGZ) != 0) return branch(); pc++; return 3;
+	case 0xF1: sbc(indY(&c)); return 5+c;
+	case 0xF5: sbc(zpX()); return 4;
+	case 0xF6: inc((u8int)(fetch8() + rX)); return 6;
+	case 0xF8: rP |= FLAGD; return 2;
+	case 0xF9: sbc(absY()); return 4 + ((u8int)a < rY);
+	case 0xFD: sbc(absX()); return 4 + ((u8int)a < rX);
+	case 0xFE: inc(fetch16() + rX); return 7;
+	default:
+		print("undefined %#x (pc %#x)\n", op, curpc);
+		return 2;
+	}
+}
--- /dev/null
+++ b/sys/src/games/nes/dat.h
@@ -1,0 +1,73 @@
+extern u16int pc, curpc;
+extern u8int rA, rX, rY, rS, rP;
+extern uchar mem[32768];
+extern int scale;
+extern u16int pput, ppuv;
+extern u8int ppusx;
+
+enum {
+	FLAGC = 1<<0,
+	FLAGZ = 1<<1,
+	FLAGI = 1<<2,
+	FLAGD = 1<<3,
+	FLAGB = 1<<4,
+	FLAGV = 1<<6,
+	FLAGN = 1<<7
+};
+
+enum {
+	PPUCTRL = 0x2000,
+	PPUMASK = 0x2001,
+	PPUSTATUS = 0x2002,
+	PPUSCROLL = 0x2005,
+
+	PPUNMI = 1<<7,
+	BIGSPRITE = 1<<5,
+	BGTABLE = 1<<4,
+	SPRTABLE = 1<<3,
+	VRAMINC = 1<<2,
+
+	GRAYSCALE = 1<<0,
+	BG8DISP = 1<<1,
+	BG8SPRITE = 1<<2,
+	BGDISP = 1<<3,
+	SPRITEDISP = 1<<4,
+
+	PPUVBLANK = 1<<7,
+	SPRITE0HIT = 1<<6,
+};
+
+enum {
+	HPRG = 4,
+	HCHR = 5,
+	HRAM = 8,
+	HROMH = 9,
+	
+	FLMIRROR = 1<<0,
+	FLBAT = 1<<1,
+	FLTRAINER = 1<<2,
+	FLFOUR = 1<<3,
+	FLMAPPERL = 4,
+	FLMAPPERH = 12,
+	FLNES20M = 3<<10,
+	FLNES20V = 2<<10,
+	FLPC10 = 1<<9,
+	FLVS = 1<<8,
+	
+	PRGSZ = 1<<14,
+	CHRSZ = 1<<13,
+};
+
+enum {
+	FREQ = 21477272,
+	MILLION = 1000000,
+	BILLION = 1000000000,
+};
+
+enum {
+	MHORZ,
+	MVERT,
+	MSINGA,
+	MSINGB,
+	MFOUR
+};
--- /dev/null
+++ b/sys/src/games/nes/fns.h
@@ -1,0 +1,6 @@
+int	step(void);
+u8int	memread(u16int);
+void	memwrite(u16int, u8int);
+u8int	ppuread(u16int);
+void	ppuwrite(u16int, u8int);
+void	ppustep(void);
--- /dev/null
+++ b/sys/src/games/nes/mem.c
@@ -1,0 +1,267 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+uchar mem[32768];
+uchar ppuram[16384];
+uchar oam[256];
+uchar *prgb[2], *chrb[2];
+extern uchar *prg, *chr;
+extern int nprg, nchr;
+u16int pput, ppuv;
+u8int ppusx;
+static int vramlatch = 1, keylatch = 0xFF;
+extern int keys, nmi, map, mirr;
+
+static void
+nrom(int p, u8int)
+{
+	if(p < 0){
+		prgb[0] = prg;
+		if(nprg == 1)
+			prgb[1] = prg;
+		else
+			prgb[1] = prg + 0x4000;
+		chrb[0] = chr;
+		chrb[1] = chr + 0x1000;
+	}
+	return;
+}
+
+static void
+mmc1(int v, u8int p)
+{
+	static u8int n, s, mode, c0, c1, pr;
+	int wchr, wprg;
+	static int mirrs[] = {MSINGB, MSINGA, MVERT, MHORZ};
+	
+	if(v < 0){
+		wchr = 1;
+		wprg = 1;
+		mode = 0x0C;
+		goto t;
+	}
+	if((p & 0x80) != 0){
+		n = 0;
+		s = 0;
+		mode |= 0xC;
+		return;
+	}
+	s |= (p & 1) << 4;
+	if(n < 4){
+		n++;
+		s >>= 1;
+		return;
+	}
+	wchr = wprg = 1;
+	switch(v & 0xE000){
+	case 0x8000:
+		mode = s;
+		mirr = mirrs[mode & 3];
+		wchr = wprg = 1;
+		break;
+	case 0xA000:
+		c0 = s & 0x1f;
+		c0 %= 2*nchr;
+		wchr = 1;
+		break;
+	case 0xC000:
+		c1 = s & 0x1f;
+		c1 %= 2*nchr;
+		if((mode & 0x10) != 0)
+			wchr = 1;
+		break;
+	case 0xE000:
+		pr = s & 0x0f;
+		pr %= nprg;
+		wprg = 1;
+		break;
+	}
+t:
+	if(wprg)
+		switch(mode & 0x0c){
+		case 0x08:
+			prgb[0] = prg;
+			prgb[1] = prg + pr * 0x4000;
+			break;
+		case 0x0C:
+			prgb[0] = prg + pr * 0x4000;
+			prgb[1] = prg + (0x0f % nprg) * 0x4000;
+			break;
+		default:
+			prgb[0] = prg + (pr & 0xfe) * 0x4000;
+			prgb[1] = prg + (pr | 1) * 0x4000;
+			break;
+		}
+	if(wchr)
+		if((mode & 0x10) != 0){
+			chrb[0] = chr + c0 * 0x1000;
+			chrb[1] = chr + c1 * 0x1000;
+		}else{
+			chrb[0] = chr + (c0 & 0xfe) * 0x1000;
+			chrb[1] = chr + (c0 | 1) * 0x1000;
+		}
+	s = 0;
+	n = 0;
+}
+
+void (*mapper[256])(int, u8int) = {
+	[0] nrom,
+	[1] mmc1,
+};
+
+static void
+incvram(void)
+{
+	if((mem[PPUCTRL] & VRAMINC) != 0)
+		ppuv += 32;
+	else
+		ppuv += 1;
+	ppuv &= 0x3FFF;
+}
+
+u8int
+memread(u16int p)
+{
+	static u8int vrambuf;
+	u8int v;
+
+	if(p < 0x2000){
+		p &= 0x7FF;
+	}else if(p < 0x6000){
+		if(p < 0x4000)
+			p &= 0x2007;
+		switch(p){
+		case 0x2002:
+			v = mem[p];
+			mem[p] &= ~PPUVBLANK;
+			vramlatch = 1;
+			return v;
+		case 0x2004:
+			return oam[mem[0x2003]];
+		case 0x2007:
+			if(ppuv < 0x4000){
+				v = vrambuf;
+				vrambuf = ppuread(ppuv);
+				incvram();
+				return v;
+			}
+			vrambuf = ppuread(ppuv);
+			incvram();
+			return vrambuf;
+		case 0x4016:
+			if((mem[p] & 1) != 0)
+				return keys & 1;
+			v = keylatch & 1;
+			keylatch = (keylatch >> 1) | 0x80;
+			return v | 0x40;
+		case 0x4017:
+			return 0x40;
+		}
+	}
+	if(p >= 0x8000){
+		if((p & 0x4000) != 0)
+			return prgb[1][p - 0xC000];
+		else
+			return prgb[0][p - 0x8000];
+	}
+	return mem[p];
+}
+
+void
+memwrite(u16int p, u8int v)
+{
+	if(p < 0x2000){
+		p &= 0x7FF;
+	}else if(p < 0x6000){
+		if(p < 0x4000)
+			p &= 0x2007;
+		switch(p){
+		case PPUCTRL:
+			if((v & PPUNMI) != 0 && (mem[PPUSTATUS] & PPUVBLANK) != 0)
+				nmi = 1;
+			pput = (pput & 0xF3FF) | ((v & 3) << 10);
+			break;
+		case PPUSTATUS:
+			return;
+		case 0x2004:
+			oam[mem[0x2003]++] = v;
+			return;
+		case 0x2005:
+			if(vramlatch){
+				ppusx = v & 7;
+				pput = (pput & 0xFFE0) | (v >> 3);
+			}else
+				pput = (pput & 0x0C1F) | ((v & 0xF8) << 2) | ((v & 7) << 12);
+			vramlatch ^= 1;
+			return;
+		case 0x2006:
+			if(vramlatch)
+				pput = (pput & 0xFF) | (v << 8) & 0x3F00;
+			else{
+				pput = (pput & 0xFF00) | v;
+				ppuv = pput;
+			}
+			vramlatch ^= 1;
+			return;
+		case 0x2007:
+			ppuwrite(ppuv, v);
+			incvram();
+			return;
+		case 0x4014:
+			memcpy(oam, mem + (v<<8), sizeof(oam));
+			return;
+		case 0x4016:
+			if((mem[p] & 1) != 0 && (v & 1) == 0)
+				keylatch = keys;
+			break;
+		}
+	}else if(p >= 0x8000){
+		if(mapper[map] != nil)
+			mapper[map](p, v);
+		return;
+	}
+	mem[p] = v;
+}
+
+static uchar *
+ppumap(u16int p)
+{
+	if(p >= 0x3F00){
+		if((p & 3) == 0)
+			p &= 0x3F0F;
+		return ppuram + (p & 0x3F1F);
+	}
+	p &= 0x3FFF;
+	if(p >= 0x3000)
+		p &= 0x2FFF;
+	if(p >= 0x2000)
+		switch(mirr){
+		case MHORZ: if((p & 0x800) != 0) p |= 0x400; else p &= ~0x400; break;
+		case MVERT: if((p & 0x400) != 0) p |= 0x800; else p &= ~0x800; break;
+		case MSINGA: p &= ~0xC00; break;
+		case MSINGB: p |= 0xC00; break;
+		}
+	if(p < 0x1000)
+		return chrb[0] + p;
+	else if(p < 0x2000)
+		return chrb[1] + p - 0x1000;
+	else
+		return ppuram + p;
+}
+
+u8int
+ppuread(u16int p)
+{
+	return *ppumap(p);
+}
+
+void
+ppuwrite(u16int p, u8int v)
+{
+	*ppumap(p) = v;
+}
+
--- /dev/null
+++ b/sys/src/games/nes/mkfile
@@ -1,0 +1,13 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=nes
+OFILES=\
+	cpu.$O\
+	mem.$O\
+	nes.$O\
+	ppu.$O\
+	
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/nes/nes.c
@@ -1,0 +1,192 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include "dat.h"
+#include "fns.h"
+
+extern uchar ppuram[16384];
+int nprg, nchr, map;
+uchar *prg, *chr;
+int scale;
+Rectangle picr;
+Image *tmp, *bg;
+int clock, ppuclock, syncclock, syncfreq, checkclock, sleeps;
+Mousectl *mc;
+int keys;
+extern void (*mapper[])(int, u8int);
+int mirr;
+
+void
+loadrom(char *file)
+{
+	int fd;
+	int nes20;
+	static uchar header[16];
+	static u32int flags;
+	
+	fd = open(file, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	if(readn(fd, header, sizeof(header)) < sizeof(header))
+		sysfatal("read: %r");
+	if(memcmp(header, "NES\x1a", 4) != 0)
+		sysfatal("not a ROM");
+	if(header[15] != 0)
+		memset(header + 7, 0, 9);
+	flags = header[6] | header[7] << 8;
+	nes20 = (flags & FLNES20M) == FLNES20V;
+	if(flags & (FLVS | FLPC10))
+		sysfatal("ROM not supported");
+	nprg = header[HPRG];
+	if(nes20)
+		nprg |= (header[HROMH] & 0xf) << 8;
+	if(nprg == 0)
+		sysfatal("invalid ROM");
+	nchr = header[HCHR];
+	if(nes20)
+		nchr |= (header[HROMH] & 0xf0) << 4;
+	map = (flags >> FLMAPPERL) & 0x0f | (((flags >> FLMAPPERH) & 0x0f) << 4);
+	if(nes20)
+		map |= (header[8] & 0x0f) << 8;
+	if(map >= 256 || mapper[map] == nil)
+		sysfatal("unimplemented mapper %d", map);
+
+	memset(mem, 0, sizeof(mem));
+	if((flags & FLTRAINER) != 0 && readn(fd, mem + 0x7000, 512) < 512)
+			sysfatal("read: %r");
+	prg = malloc(nprg * PRGSZ);
+	if(prg == nil)
+		sysfatal("malloc: %r");
+	if(readn(fd, prg, nprg * PRGSZ) < nprg * PRGSZ)
+		sysfatal("read: %r");
+	if(nchr != 0){
+		chr = malloc(nchr * CHRSZ);
+		if(chr == nil)
+			sysfatal("malloc: %r");
+		if(readn(fd, chr, nchr * CHRSZ) < nchr * CHRSZ)
+			sysfatal("read: %r");
+	}else{
+		nchr = 16;
+		chr = malloc(16 * CHRSZ);
+		if(chr == nil)
+			sysfatal("malloc: %r");
+	}
+	if((flags & FLFOUR) != 0)
+		mirr = MFOUR;
+	else if((flags & FLMIRROR) != 0)
+		mirr = MVERT;
+	else
+		mirr = MHORZ;
+	mapper[map](-1, 0);
+}
+
+void
+keyproc(void *)
+{
+	int fd;
+	char buf[256], *s;
+	Rune r;
+
+	fd = open("/dev/kbd", OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	for(;;){
+		if(read(fd, buf, 256) <= 0)
+			sysfatal("read /dev/kbd: %r");
+		if(buf[0] == 'c'){
+			if(utfrune(buf, Kdel))
+				threadexitsall(nil);
+		}
+		if(buf[0] != 'k' && buf[0] != 'K')
+			continue;
+		s = buf + 1;
+		keys = 0;
+		while(*s != 0){
+			s += chartorune(&r, s);
+			switch(r){
+			case Kdel: threadexitsall(nil);
+			case 'x': keys |= 1<<0; break;
+			case 'z': keys |= 1<<1; break;
+			case Kshift: keys |= 1<<2; break;
+			case 10: keys |= 1<<3; break;
+			case Kup: keys |= 1<<4; break;
+			case Kdown: keys |= 1<<5; break;
+			case Kleft: keys |= 1<<6; break;
+			case Kright: keys |= 1<<7; break;
+			}
+		}	
+	}
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int t;
+	Point p;
+	uvlong old, new, diff;
+
+	scale = 1;
+	ARGBEGIN {
+	case '2':
+		scale = 2;
+		break;
+	case '3':
+		scale = 3;
+		break;
+	} ARGEND;
+
+	if(argc < 1)
+		sysfatal("missing argument");
+	loadrom(argv[0]);
+	if(initdraw(nil, nil, nil) < 0)
+		sysfatal("initdraw: %r");
+	mc = initmouse(nil, screen);
+	if(mc == nil)
+		sysfatal("initmouse: %r");
+	proccreate(keyproc, nil, 8192);
+	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 * 120)), addpt(p, Pt(scale * 128, scale * 120))};
+	if(screen->chan != XRGB32)
+		tmp = allocimage(display, Rect(0, 0, scale * 256, scale * 240), XRGB32, 0, 0);
+	bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+	draw(screen, screen->r, bg, nil, ZP);
+	
+	pc = memread(0xFFFC) | memread(0xFFFD) << 8;
+	rP = FLAGI;
+	syncfreq = FREQ / 30;
+	old = nsec();
+	for(;;){
+		t = step() * 12;
+		clock += t;
+		ppuclock += t;
+		syncclock += t;
+		checkclock += t;
+		while(ppuclock >= 4){
+			ppustep();
+			ppuclock -= 4;
+		}
+		if(syncclock >= syncfreq){
+			sleep(10);
+			sleeps++;
+			syncclock = 0;
+		}
+		if(checkclock >= FREQ){
+			new = nsec();
+			diff = new - old - sleeps * 10 * MILLION;
+			diff = BILLION - diff;
+			if(diff <= 0)
+				syncfreq = FREQ;
+			else
+				syncfreq = ((vlong)FREQ) * 10 * MILLION / diff;
+			if(syncfreq < FREQ / 100)
+				syncfreq = FREQ / 100;
+			old = new;
+			checkclock = 0;
+			sleeps = 0;
+		}
+	}
+}
--- /dev/null
+++ b/sys/src/games/nes/ppu.c
@@ -1,0 +1,295 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+int ppuy, ppux;
+uchar pic[256*240*4*9];
+extern uchar oam[256];
+
+static void
+pixel(int x, int y, int val, int back)
+{
+	int Y;
+	union { u8int c[4]; u32int l; } u;
+	u32int *p, l;
+	static u8int palred[64] = {
+		0x7C, 0x00, 0x00, 0x44, 0x94, 0xA8, 0xA8, 0x88, 
+		0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+		0xBC, 0x00, 0x00, 0x68, 0xD8, 0xE4, 0xF8, 0xE4, 
+		0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+		0xF8, 0x3C, 0x68, 0x98, 0xF8, 0xF8, 0xF8, 0xFC, 
+		0xF8, 0xB8, 0x58, 0x58, 0x00, 0x78, 0x00, 0x00, 
+		0xFC, 0xA4, 0xB8, 0xD8, 0xF8, 0xF8, 0xF0, 0xFC, 
+		0xF8, 0xD8, 0xB8, 0xB8, 0x00, 0xF8, 0x00, 0x00, 
+	};
+	static u8int palgreen[64] = {
+		0x7C, 0x00, 0x00, 0x28, 0x00, 0x00, 0x10, 0x14, 
+                0x30, 0x78, 0x68, 0x58, 0x40, 0x00, 0x00, 0x00, 
+                0xBC, 0x78, 0x58, 0x44, 0x00, 0x00, 0x38, 0x5C, 
+                0x7C, 0xB8, 0xA8, 0xA8, 0x88, 0x00, 0x00, 0x00, 
+                0xF8, 0xBC, 0x88, 0x78, 0x78, 0x58, 0x78, 0xA0, 
+                0xB8, 0xF8, 0xD8, 0xF8, 0xE8, 0x78, 0x00, 0x00, 
+                0xFC, 0xE4, 0xB8, 0xB8, 0xB8, 0xA4, 0xD0, 0xE0, 
+                0xD8, 0xF8, 0xF8, 0xF8, 0xFC, 0xD8, 0x00, 0x00, 
+	};
+	static u8int palblue[64] = {
+		0x7C, 0xFC, 0xBC, 0xBC, 0x84, 0x20, 0x00, 0x00, 
+                0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 
+                0xBC, 0xF8, 0xF8, 0xFC, 0xCC, 0x58, 0x00, 0x10, 
+                0x00, 0x00, 0x00, 0x44, 0x88, 0x00, 0x00, 0x00, 
+                0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0x98, 0x58, 0x44, 
+                0x00, 0x18, 0x54, 0x98, 0xD8, 0x78, 0x00, 0x00, 
+                0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xC0, 0xB0, 0xA8, 
+                0x78, 0x78, 0xB8, 0xD8, 0xFC, 0xF8, 0x00, 0x00, 
+	};
+
+	u.c[0] = palblue[val];
+	u.c[1] = palgreen[val];
+	u.c[2] = palred[val];
+	u.c[3] = back ? 0 : 0xFF;
+	l = u.l;
+	if(scale == 3){
+		p = ((u32int*)pic) + y * 3 * 3 * 256 + 3 * x;
+		for(Y = 0; Y < 3; Y++){
+			*p++ = l;
+			*p++ = l;
+			*p = l;
+			p += 3 * 256 - 2;
+		}
+	}else if(scale == 2){
+		p = ((u32int*)pic) + y * 2 * 2 * 256 + 2 * x;
+		*p++ = l;
+		*p = l;
+		p += 2 * 256 - 1;
+		*p++ = l;
+		*p = l;
+	}else{
+		p = ((u32int*)pic) + y * 256 + x;
+		*p = l;
+	}
+}
+
+static int
+iscolor(int x, int y)
+{
+	return pic[y * scale * scale * 256 * 4 + x * scale * 4 + 3] != 0;
+}
+
+static int
+pal(int c, int a, int spr)
+{
+	if(c == 0)
+		return ppuread(0x3F00);
+	return ppuread(0x3F00 | ((a&3)<<2) | (c & 3) | (spr << 4));
+}
+
+static void
+incppuy(void)
+{
+	int y;
+
+	if((ppuv & 0x7000) != 0x7000){
+		ppuv += 0x1000;
+		return;
+	}
+	y = (ppuv >> 5) & 31;
+	if(y++ == 29){
+		y = 0;
+		ppuv ^= 0x800;
+	}
+	ppuv = (ppuv & 0x0C1F) | ((y & 31) << 5);
+}
+
+static void
+drawbg(void)
+{
+	static int t;
+	u8int c, a;
+	static u8int nr1, nr2, na;
+	static u32int r1, r2, a1, a2;
+	
+	if(ppux >= 2 && ppux <= 257 || ppux >= 322 && ppux <= 337){
+		c = (r1 >> (15-ppusx)) & 1 | (r2 >> (14-ppusx)) & 2;
+		a = (a1 >> (15-ppusx)) & 1 | (a2 >> (14-ppusx)) & 2;
+		if(ppuy < 240 && ppux <= 257)
+			pixel(ppux-2, ppuy, pal(c, a, 0), c == 0);
+		r1 <<= 1;
+		r2 <<= 1;
+		a1 <<= 1;
+		a2 <<= 1;
+	}
+	if(ppux >= 256 && ppux <= 320){
+		if(ppux == 256)
+			incppuy();
+		if(ppux == 257)
+			ppuv = (ppuv & 0x7BE0) | (pput & 0x041F);
+		return;
+	}
+	switch(ppux & 7){
+	case 0:
+		if(ppux != 0){
+			if((ppuv & 0x1f) == 0x1f){
+				ppuv &= ~0x1f;
+				ppuv ^= 0x400;
+			}else
+				ppuv++;
+		}
+		break;
+	case 1:
+		t = ppuread(0x2000 | ppuv & 0x0FFF);
+		if(ppux != 1){
+			r1 |= nr1;
+			r2 |= nr2;
+			if(na & 1)
+				a1 |= 0xff;
+			if(na & 2)
+				a2 |= 0xff;
+		}
+		break;
+	case 3:
+		na = ppuread(0x23C0 | ppuv & 0x0C00 | ((ppuv & 0x0380) >> 4) | ((ppuv & 0x001C) >> 2));
+		if((ppuv & 0x0002) != 0) na >>= 2;
+		if((ppuv & 0x0040) != 0) na >>= 4;
+		break;
+	case 5:
+		nr1 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12);
+		break;
+	case 7:
+		nr2 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12 | 8);
+		break;
+	}
+}
+
+static void
+drawsprites(void)
+{
+	uchar *p;
+	int big, dx, dy, i, x;
+	u8int r1, r2, c;
+	static int n, m, nz, s0, t0;
+	static struct { u8int x, a; u16int t; } s[8], *sp;
+	static struct { u8int x, a, r1, r2; } t[8];
+
+	big = (mem[PPUCTRL] & BIGSPRITE) != 0;
+	if(ppux == 65){
+		s0 = 0;
+		for(p = oam, sp = s, n = 0; p < oam + sizeof(oam); p += 4){
+			if((dy = p[0]) >= 0xEF)
+				continue;
+			dy = ppuy - dy;
+			if(dy < 0 || dy >= (big ? 16 : 8))
+				continue;
+			if(p == oam)
+				s0 = 1;
+			sp->t = p[1];
+			sp->a = p[2];
+			sp->x = p[3];
+			if((sp->a & (1<<7)) != 0)
+				dy = (big ? 15 : 7) - dy;
+			if(big){
+				sp->t |= (sp->t & 1) << 8;
+				if(dy >= 8){
+					sp->t |= 1;
+					dy -= 8;
+				}else
+					sp->t &= 0x1fe;
+			}else
+				sp->t |= (mem[PPUCTRL] & SPRTABLE) << 5;
+			sp->t = sp->t << 4 | dy;
+			sp++;
+			if(++n == 8)
+				break;
+		}
+	}
+	if(ppux >= 2 && ppux <= 257 && m > 0){
+		x = ppux - 2;
+		dx = x - t[0].x;
+		if(t0 && dx >= 0 && dx < 8 && ppux != 257){
+			if((nz & 1) != 0 && iscolor(x, ppuy))
+				mem[PPUSTATUS] |= SPRITE0HIT;
+			nz >>= 1;
+		}
+		for(i = m - 1; i >= 0; i--){
+			dx = x - t[i].x;
+			if(dx < 0 || dx > 7)
+				continue;
+			c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1;
+			if(c != 0){
+				if((t[i].a & (1<<5)) == 0 || !iscolor(x, ppuy))
+					pixel(x, ppuy, pal(c, t[i].a & 3, 1), 0);
+			}
+			t[i].r1 >>= 1;
+			t[i].r2 >>= 1;
+		}
+	}
+	if(ppux == 257){
+		for(i = 0; i < n; i++){
+			r1 = ppuread(s[i].t);
+			r2 = ppuread(s[i].t | 8);
+			if((s[i].a & (1<<6)) == 0){
+				r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+				r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+			}
+			t[i].x = s[i].x;
+			t[i].a = s[i].a;
+			t[i].r1 = r1;
+			t[i].r2 = r2;
+		}
+		m = n;
+		nz = t[0].r1 | t[0].r2;
+		t0 = s0;
+	}
+}
+
+static void
+flush(void)
+{
+	extern Rectangle picr;
+	extern Image *tmp;
+
+	if(tmp){
+		loadimage(tmp, tmp->r, pic, 256*240*4*scale*scale);
+		draw(screen, picr, tmp, nil, ZP);
+	}else
+		loadimage(screen, picr, pic, 256*240*4*scale*scale);
+	flushimage(display, 1);
+	memset(pic, sizeof pic, 0);
+}
+
+void
+ppustep(void)
+{
+	extern int nmi;
+	int bg;
+
+	if(ppuy < 240 || ppuy == 261){
+		bg = (mem[PPUMASK] & BGDISP) != 0;
+		if(bg)
+			drawbg();
+		if((mem[PPUMASK] & SPRITEDISP) != 0 && ppuy != 261)
+			drawsprites();
+		if(ppuy == 261){
+			if(ppux == 1)
+				mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT);
+			else if(ppux >= 280 && ppux <= 304 && bg)
+				ppuv = (pput & 0x7BE0) | (ppuv & 0x041F);
+		}
+	}else if(ppuy == 241){
+		if(ppux == 1){
+			mem[PPUSTATUS] |= PPUVBLANK;
+			if((mem[PPUCTRL] & PPUNMI) != 0)
+				nmi = 1;
+			flush();
+		}
+	}
+	ppux++;
+	if(ppux > 340){
+		ppux = 0;
+		ppuy++;
+		if(ppuy > 261)
+			ppuy = 0;
+	}
+}