ref: 1225ebec7181167fbefc5499300260e93c9b5191
parent: ae41f49f09de058a500a02f4a0fa2b463b8b44de
author: aiju <devnull@localhost>
date: Sun Feb 16 15:51:11 EST 2014
added games/nes
--- /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;
+ }
+}