]> git.lizzy.rs Git - plan9front.git/commitdiff
added games/nes
authoraiju <devnull@localhost>
Sun, 16 Feb 2014 19:51:11 +0000 (20:51 +0100)
committeraiju <devnull@localhost>
Sun, 16 Feb 2014 19:51:11 +0000 (20:51 +0100)
sys/src/games/nes/cpu.c [new file with mode: 0644]
sys/src/games/nes/dat.h [new file with mode: 0644]
sys/src/games/nes/fns.h [new file with mode: 0644]
sys/src/games/nes/mem.c [new file with mode: 0644]
sys/src/games/nes/mkfile [new file with mode: 0644]
sys/src/games/nes/nes.c [new file with mode: 0644]
sys/src/games/nes/ppu.c [new file with mode: 0644]

diff --git a/sys/src/games/nes/cpu.c b/sys/src/games/nes/cpu.c
new file mode 100644 (file)
index 0000000..1377cf2
--- /dev/null
@@ -0,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;
+       }
+}
diff --git a/sys/src/games/nes/dat.h b/sys/src/games/nes/dat.h
new file mode 100644 (file)
index 0000000..d8f0da0
--- /dev/null
@@ -0,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
+};
diff --git a/sys/src/games/nes/fns.h b/sys/src/games/nes/fns.h
new file mode 100644 (file)
index 0000000..e19af03
--- /dev/null
@@ -0,0 +1,6 @@
+int    step(void);
+u8int  memread(u16int);
+void   memwrite(u16int, u8int);
+u8int  ppuread(u16int);
+void   ppuwrite(u16int, u8int);
+void   ppustep(void);
diff --git a/sys/src/games/nes/mem.c b/sys/src/games/nes/mem.c
new file mode 100644 (file)
index 0000000..ff5ecc2
--- /dev/null
@@ -0,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;
+}
+
diff --git a/sys/src/games/nes/mkfile b/sys/src/games/nes/mkfile
new file mode 100644 (file)
index 0000000..6119264
--- /dev/null
@@ -0,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
diff --git a/sys/src/games/nes/nes.c b/sys/src/games/nes/nes.c
new file mode 100644 (file)
index 0000000..3b3d62d
--- /dev/null
@@ -0,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;
+               }
+       }
+}
diff --git a/sys/src/games/nes/ppu.c b/sys/src/games/nes/ppu.c
new file mode 100644 (file)
index 0000000..cb1e7b7
--- /dev/null
@@ -0,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;
+       }
+}