games/nes: added state saving & bug fixes

This commit is contained in:
aiju 2014-02-21 20:48:23 +01:00
parent 0181117b5f
commit 830a9b59c9
6 changed files with 300 additions and 67 deletions

View file

@ -2,14 +2,16 @@ extern u16int pc, curpc;
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,
@ -77,3 +79,9 @@ enum {
MSINGB,
MFOUR
};
enum {
INIT = -1,
SAVE = -2,
RSTR = -3,
};

View file

@ -4,3 +4,8 @@ void memwrite(u16int, u8int);
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);

View file

@ -10,13 +10,23 @@ uchar ppuram[16384];
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,22 +34,45 @@ nrom(int p, u8int)
prgb[1] = prg + 0x4000;
chrb[0] = chr;
chrb[1] = chr + 0x1000;
break;
case SAVE:
break;
default:
nope(p);
}
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;
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,32 +86,27 @@ mmc1(int v, u8int p)
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;
}
s = 0;
n = 0;
t:
if(wprg)
switch(mode & 0x0c){
case 0x08:
prgb[0] = prg;
@ -93,7 +121,6 @@ t:
prgb[1] = prg + (pr | 1) * 0x4000;
break;
}
if(wchr)
if((mode & 0x10) != 0){
chrb[0] = chr + c0 * 0x1000;
chrb[1] = chr + c1 * 0x1000;
@ -101,18 +128,32 @@ t:
chrb[0] = chr + (c0 & 0xfe) * 0x1000;
chrb[1] = chr + (c0 | 1) * 0x1000;
}
s = 0;
n = 0;
}
static void
mmc7(int v, u8int p)
{
if(v < 0){
nrom(-1, 0);
p = 0;
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 + (p & 3) * 0x8000;
prgb[0] = prg + (b & 3) * 0x8000;
prgb[1] = prgb[0] + 0x4000;
}
@ -135,7 +176,6 @@ incvram(void)
u8int
memread(u16int p)
{
static u8int vrambuf;
u8int v;
if(p < 0x2000){

View file

@ -7,6 +7,7 @@ OFILES=\
mem.$O\
nes.$O\
ppu.$O\
state.$O\
HFILES=dat.h fns.h

View file

@ -8,16 +8,29 @@
#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)
@ -62,6 +75,7 @@ loadrom(char *file)
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 @@ loadrom(char *file)
sysfatal("read: %r");
}else{
nchr = 16;
chr = malloc(16 * CHRSZ);
chr = malloc(nchr * CHRSZ);
if(chr == nil)
sysfatal("malloc: %r");
}
@ -88,40 +102,53 @@ extern int trace;
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);
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;
}
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 @@ threadmain(int argc, char **argv)
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;
@ -192,5 +231,12 @@ threadmain(int argc, char **argv)
checkclock = 0;
sleeps = 0;
}
if(msgclock > 0){
msgclock -= t;
if(msgclock <= 0){
draw(screen, screen->r, bg, nil, ZP);
msgclock = 0;
}
}
}
}

133
sys/src/games/nes/state.c Normal file
View file

@ -0,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);
}