ref: 830a9b59c94cff0e733b20d7094bddf6c7cefc74
parent: 0181117b5f56341e71f72f1cda37e815853eaacc
author: aiju <devnull@localhost>
date: Fri Feb 21 15:48:23 EST 2014
games/nes: added state saving & bug fixes
--- a/sys/src/games/nes/dat.h
+++ b/sys/src/games/nes/dat.h
@@ -2,15 +2,17 @@
extern u8int rA, rX, rY, rS, rP;
extern uchar mem[32768], ppuram[16384], oam[256];
extern u16int pput, ppuv;
-extern u8int ppusx;
-extern int mirr;
+extern u8int ppusx, vrambuf;
+extern int mirr, ppux, ppuy, odd, vramlatch, keylatch;
extern int map, scale;
extern uchar *prg, *chr;
-extern int nprg, nchr, nmi, map;
+extern int nprg, nchr, nmi, map, chrram;
-extern int keys;
+extern int keys, clock, ppuclock;
+extern void (*mapper[])(int, u8int);
+
enum {
FLAGC = 1<<0,
FLAGZ = 1<<1,
@@ -76,4 +78,10 @@
MSINGA,
MSINGB,
MFOUR
+};
+
+enum {
+ INIT = -1,
+ SAVE = -2,
+ RSTR = -3,
};
--- a/sys/src/games/nes/fns.h
+++ b/sys/src/games/nes/fns.h
@@ -4,3 +4,8 @@
u8int ppuread(u16int);
void ppuwrite(u16int, u8int);
void ppustep(void);
+void loadstate(char *);
+void savestate(char *);
+void message(char *, ...);
+void put8(u8int);
+int get8(void);
--- a/sys/src/games/nes/mem.c
+++ b/sys/src/games/nes/mem.c
@@ -10,13 +10,23 @@
uchar oam[256];
uchar *prgb[2], *chrb[2];
u16int pput, ppuv;
-u8int ppusx;
-static int vramlatch = 1, keylatch = 0xFF;
+u8int ppusx, vrambuf;
+int vramlatch = 1, keylatch = 0xFF;
static void
+nope(int p)
+{
+ print("unimplemented mapper function %d (mapper %d)\n", p, map);
+}
+
+static void
nrom(int p, u8int)
{
- if(p < 0){
+ if(p >= 0)
+ return;
+ switch(p){
+ case INIT:
+ case RSTR:
prgb[0] = prg;
if(nprg == 1)
prgb[1] = prg;
@@ -24,8 +34,12 @@
prgb[1] = prg + 0x4000;
chrb[0] = chr;
chrb[1] = chr + 0x1000;
+ break;
+ case SAVE:
+ break;
+ default:
+ nope(p);
}
- return;
}
static void
@@ -32,14 +46,33 @@
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;
+ switch(v){
+ case INIT:
+ mode = 0x0C;
+ goto t;
+ case RSTR:
+ mode = get8();
+ c0 = get8();
+ c1 = get8();
+ pr = get8();
+ n = get8();
+ s = get8();
+ goto t;
+ case SAVE:
+ put8(mode);
+ put8(c0);
+ put8(c1);
+ put8(pr);
+ put8(n);
+ put8(s);
+ break;
+ default:
+ nope(v);
+ }
+ return;
}
if((p & 0x80) != 0){
n = 0;
@@ -53,66 +86,74 @@
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;
+t:
+ 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((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;
+ }
}
static void
mmc7(int v, u8int p)
{
- if(v < 0){
- nrom(-1, 0);
- p = 0;
- }
- prgb[0] = prg + (p & 3) * 0x8000;
+ static int b;
+
+ if(v >= 0)
+ b = p;
+ else
+ switch(v){
+ case INIT:
+ nrom(INIT, 0);
+ b = 0;
+ break;
+ case SAVE:
+ put8(b);
+ return;
+ case RSTR:
+ b = get8();
+ break;
+ default:
+ nope(v);
+ return;
+ }
+ prgb[0] = prg + (b & 3) * 0x8000;
prgb[1] = prgb[0] + 0x4000;
}
@@ -135,7 +176,6 @@
u8int
memread(u16int p)
{
- static u8int vrambuf;
u8int v;
if(p < 0x2000){
--- a/sys/src/games/nes/mkfile
+++ b/sys/src/games/nes/mkfile
@@ -7,6 +7,7 @@
mem.$O\
nes.$O\
ppu.$O\
+ state.$O\
HFILES=dat.h fns.h
--- a/sys/src/games/nes/nes.c
+++ b/sys/src/games/nes/nes.c
@@ -8,18 +8,31 @@
#include "fns.h"
extern uchar ppuram[16384];
-int nprg, nchr, map;
+int nprg, nchr, map, chrram;
uchar *prg, *chr;
int scale;
Rectangle picr;
Image *tmp, *bg;
-int clock, ppuclock, syncclock, syncfreq, checkclock, sleeps;
+int clock, ppuclock, syncclock, syncfreq, checkclock, msgclock, sleeps;
Mousectl *mc;
-int keys;
-extern void (*mapper[])(int, u8int);
+int keys, paused, savereq, loadreq;
int mirr;
+QLock pauselock;
void
+message(char *fmt, ...)
+{
+ va_list va;
+ static char buf[512];
+
+ va_start(va, fmt);
+ vsnprint(buf, sizeof buf, fmt, va);
+ string(screen, Pt(10, 10), display->black, ZP, display->defaultfont, buf);
+ msgclock = FREQ;
+ va_end(va);
+}
+
+void
loadrom(char *file)
{
int fd;
@@ -62,6 +75,7 @@
sysfatal("malloc: %r");
if(readn(fd, prg, nprg * PRGSZ) < nprg * PRGSZ)
sysfatal("read: %r");
+ chrram = nchr == 0;
if(nchr != 0){
chr = malloc(nchr * CHRSZ);
if(chr == nil)
@@ -70,7 +84,7 @@
sysfatal("read: %r");
}else{
nchr = 16;
- chr = malloc(16 * CHRSZ);
+ chr = malloc(nchr * CHRSZ);
if(chr == nil)
sysfatal("malloc: %r");
}
@@ -88,8 +102,9 @@
void
keyproc(void *)
{
- int fd;
- char buf[256], *s;
+ int fd, k;
+ static char buf[256];
+ char *s;
Rune r;
fd = open("/dev/kbd", OREAD);
@@ -96,11 +111,15 @@
if(fd < 0)
sysfatal("open: %r");
for(;;){
- if(read(fd, buf, 256) <= 0)
+ if(read(fd, buf, sizeof(buf) - 1) <= 0)
sysfatal("read /dev/kbd: %r");
if(buf[0] == 'c'){
if(utfrune(buf, Kdel))
threadexitsall(nil);
+ if(utfrune(buf, KF|5))
+ savereq = 1;
+ if(utfrune(buf, KF|6))
+ loadreq = 1;
if(utfrune(buf, 't'))
trace ^= 1;
}
@@ -107,21 +126,29 @@
if(buf[0] != 'k' && buf[0] != 'K')
continue;
s = buf + 1;
- keys = 0;
+ k = 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;
+ case 'x': k |= 1<<0; break;
+ case 'z': k |= 1<<1; break;
+ case Kshift: k |= 1<<2; break;
+ case 10: k |= 1<<3; break;
+ case Kup: k |= 1<<4; break;
+ case Kdown: k |= 1<<5; break;
+ case Kleft: k |= 1<<6; break;
+ case Kright: k |= 1<<7; break;
+ case Kesc:
+ if(paused)
+ qunlock(&pauselock);
+ else
+ qlock(&pauselock);
+ paused = !paused;
+ break;
}
- }
+ }
+ keys = k;
}
}
@@ -164,6 +191,18 @@
syncfreq = FREQ / 30;
old = nsec();
for(;;){
+ if(savereq){
+ savestate("nes.save");
+ savereq = 0;
+ }
+ if(loadreq){
+ loadstate("nes.save");
+ loadreq = 0;
+ }
+ if(paused){
+ qlock(&pauselock);
+ qunlock(&pauselock);
+ }
t = step() * 12;
clock += t;
ppuclock += t;
@@ -191,6 +230,13 @@
old = new;
checkclock = 0;
sleeps = 0;
+ }
+ if(msgclock > 0){
+ msgclock -= t;
+ if(msgclock <= 0){
+ draw(screen, screen->r, bg, nil, ZP);
+ msgclock = 0;
+ }
}
}
}
--- /dev/null
+++ b/sys/src/games/nes/state.c
@@ -1,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static int fd;
+
+void
+put8(u8int i)
+{
+ write(fd, &i, 1);
+}
+
+void
+put16(u16int i)
+{
+ put8(i);
+ put8(i >> 8);
+}
+
+void
+put32(u32int i)
+{
+ put8(i);
+ put8(i >> 8);
+ put8(i >> 16);
+ put8(i >> 24);
+}
+
+int
+get8(void)
+{
+ u8int c;
+
+ read(fd, &c, 1);
+ return c;
+}
+
+int
+get16(void)
+{
+ int i;
+
+ i = get8();
+ i |= get8() << 8;
+ return i;
+}
+
+int
+get32(void)
+{
+ int i;
+
+ i = get8();
+ i |= get8() << 8;
+ i |= get8() << 16;
+ i |= get8() << 24;
+ return i;
+}
+
+void
+loadstate(char *file)
+{
+ fd = open(file, OREAD);
+ if(fd < 0){
+ message("open: %r");
+ return;
+ }
+ read(fd, mem, sizeof(mem));
+ read(fd, ppuram, sizeof(ppuram));
+ read(fd, oam, sizeof(oam));
+ if(chrram)
+ read(fd, chr, nchr * CHRSZ);
+ rA = get8();
+ rX = get8();
+ rY = get8();
+ rS = get8();
+ rP = get8();
+ nmi = get8();
+ pc = get16();
+ pput = get16();
+ ppuv = get16();
+ ppusx = get8();
+ ppux = get16();
+ ppuy = get16();
+ mirr = get8();
+ odd = get8();
+ vramlatch = get8();
+ keylatch = get8();
+ vrambuf = get8();
+ clock = get32();
+ ppuclock = get32();
+ mapper[map](RSTR, 0);
+ close(fd);
+}
+
+void
+savestate(char *file)
+{
+ fd = create(file, ORDWR, 0666);
+ if(fd < 0){
+ message("create: %r");
+ return;
+ }
+ write(fd, mem, sizeof(mem));
+ write(fd, ppuram, sizeof(ppuram));
+ write(fd, oam, sizeof(oam));
+ if(chrram)
+ write(fd, chr, nchr * CHRSZ);
+ put8(rA);
+ put8(rX);
+ put8(rY);
+ put8(rS);
+ put8(rP);
+ put8(nmi);
+ put16(pc);
+ put16(pput);
+ put16(ppuv);
+ put8(ppusx);
+ put16(ppux);
+ put16(ppuy);
+ put8(mirr);
+ put8(odd);
+ put8(vramlatch);
+ put8(keylatch);
+ put8(vrambuf);
+ put32(clock);
+ put32(ppuclock);
+ mapper[map](SAVE, 0);
+ close(fd);
+}