diff --git a/sys/src/games/nes/dat.h b/sys/src/games/nes/dat.h index b7f1cc887..502c91f79 100644 --- a/sys/src/games/nes/dat.h +++ b/sys/src/games/nes/dat.h @@ -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, +}; diff --git a/sys/src/games/nes/fns.h b/sys/src/games/nes/fns.h index e19af0336..7467f0991 100644 --- a/sys/src/games/nes/fns.h +++ b/sys/src/games/nes/fns.h @@ -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); diff --git a/sys/src/games/nes/mem.c b/sys/src/games/nes/mem.c index 5b14cdd82..7b0395edd 100644 --- a/sys/src/games/nes/mem.c +++ b/sys/src/games/nes/mem.c @@ -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){ diff --git a/sys/src/games/nes/mkfile b/sys/src/games/nes/mkfile index 6119264a2..8b34fa02f 100644 --- a/sys/src/games/nes/mkfile +++ b/sys/src/games/nes/mkfile @@ -7,6 +7,7 @@ OFILES=\ mem.$O\ nes.$O\ ppu.$O\ + state.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/nes/nes.c b/sys/src/games/nes/nes.c index ee72e2b8a..6c9c8c69d 100644 --- a/sys/src/games/nes/nes.c +++ b/sys/src/games/nes/nes.c @@ -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; + } + } } } diff --git a/sys/src/games/nes/state.c b/sys/src/games/nes/state.c new file mode 100644 index 000000000..563492570 --- /dev/null +++ b/sys/src/games/nes/state.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#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); +}