games/nes: added state saving & bug fixes
This commit is contained in:
parent
0181117b5f
commit
830a9b59c9
6 changed files with 300 additions and 67 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
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 @@ 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;
|
||||
}
|
||||
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 @@ incvram(void)
|
|||
u8int
|
||||
memread(u16int p)
|
||||
{
|
||||
static u8int vrambuf;
|
||||
u8int v;
|
||||
|
||||
if(p < 0x2000){
|
||||
|
|
|
@ -7,6 +7,7 @@ OFILES=\
|
|||
mem.$O\
|
||||
nes.$O\
|
||||
ppu.$O\
|
||||
state.$O\
|
||||
|
||||
HFILES=dat.h fns.h
|
||||
|
||||
|
|
|
@ -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
133
sys/src/games/nes/state.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue