diff options
| author | aiju <devnull@localhost> | 2014-02-16 20:51:11 +0100 |
|---|---|---|
| committer | aiju <devnull@localhost> | 2014-02-16 20:51:11 +0100 |
| commit | 1225ebec7181167fbefc5499300260e93c9b5191 (patch) | |
| tree | 84c7dbb323e7cf8c289ce811d933d90ed0bd01f7 | |
| parent | ae41f49f09de058a500a02f4a0fa2b463b8b44de (diff) | |
| download | plan9front-1225ebec7181167fbefc5499300260e93c9b5191.tar.xz | |
added games/nes
| -rw-r--r-- | sys/src/games/nes/cpu.c | 438 | ||||
| -rw-r--r-- | sys/src/games/nes/dat.h | 73 | ||||
| -rw-r--r-- | sys/src/games/nes/fns.h | 6 | ||||
| -rw-r--r-- | sys/src/games/nes/mem.c | 267 | ||||
| -rw-r--r-- | sys/src/games/nes/mkfile | 13 | ||||
| -rw-r--r-- | sys/src/games/nes/nes.c | 192 | ||||
| -rw-r--r-- | sys/src/games/nes/ppu.c | 295 |
7 files changed, 1284 insertions, 0 deletions
diff --git a/sys/src/games/nes/cpu.c b/sys/src/games/nes/cpu.c new file mode 100644 index 000000000..1377cf2fd --- /dev/null +++ b/sys/src/games/nes/cpu.c @@ -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 index 000000000..d8f0da025 --- /dev/null +++ b/sys/src/games/nes/dat.h @@ -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 index 000000000..e19af0336 --- /dev/null +++ b/sys/src/games/nes/fns.h @@ -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 index 000000000..ff5ecc2dd --- /dev/null +++ b/sys/src/games/nes/mem.c @@ -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 index 000000000..6119264a2 --- /dev/null +++ b/sys/src/games/nes/mkfile @@ -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 index 000000000..3b3d62d77 --- /dev/null +++ b/sys/src/games/nes/nes.c @@ -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 index 000000000..cb1e7b7a0 --- /dev/null +++ b/sys/src/games/nes/ppu.c @@ -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; + } +} |
