plan9front/sys/src/games/nes/nes.c

215 lines
4.1 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <keyboard.h>
#include "../eui.h"
#include "dat.h"
#include "fns.h"
extern uchar ppuram[16384];
int nprg, nchr, map, chrram;
uchar *prg, *chr;
int clock, ppuclock, apuclock, dmcclock, dmcfreq, sampclock, msgclock, saveclock;
int oflag, savefd = -1;
int mirr;
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
flushram(void)
{
if(savefd >= 0)
pwrite(savefd, mem + 0x6000, 0x2000, 0);
saveclock = 0;
}
void
loadrom(char *file, int sflag)
{
int fd;
int nes20;
char *s;
static uchar header[16];
static u32int flags;
static char buf[512];
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");
chrram = nchr == 0;
if(nchr != 0){
chr = malloc(nchr * CHRSZ);
if(chr == nil)
sysfatal("malloc: %r");
if(readn(fd, chr, nchr * CHRSZ) < 1)
sysfatal("read: %r");
}else{
nchr = 1;
chr = malloc(nchr * CHRSZ);
if(chr == nil)
sysfatal("malloc: %r");
}
if((flags & FLFOUR) != 0)
mirr = MFOUR;
else if((flags & FLMIRROR) != 0)
mirr = MVERT;
else
mirr = MHORZ;
if(sflag){
strncpy(buf, file, sizeof buf - 5);
s = buf + strlen(buf) - 4;
if(s < buf || strcmp(s, ".nes") != 0)
s += 4;
strcpy(s, ".sav");
savefd = create(buf, ORDWR | OEXCL, 0666);
if(savefd < 0)
savefd = open(buf, ORDWR);
if(savefd < 0)
message("open: %r");
else
readn(savefd, mem + 0x6000, 0x2000);
atexit(flushram);
}
mapper[map](INIT, 0);
}
void
usage(void)
{
fprint(2, "usage: %s [-aos] [-x scale] rom\n", argv0);
exits("usage");
}
void
threadmain(int argc, char **argv)
{
int t, sflag;
sflag = 0;
ARGBEGIN {
case 'a':
initaudio();
break;
case 'o':
oflag = 1;
break;
case 's':
sflag = 1;
break;
case 'x':
fixscale = strtol(EARGF(usage()), nil, 0);
break;
default:
usage();
} ARGEND;
if(argc < 1)
usage();
loadrom(argv[0], sflag);
initemu(256, 240 - oflag * 16, 4, XRGB32, 1, nil);
regkey("b", 'z', 1<<1);
regkey("a", 'x', 1<<0);
regkey("control", Kshift, 1<<2);
regkey("start", '\n', 1<<3);
regkey("up", Kup, 1<<4);
regkey("down", Kdown, 1<<5);
regkey("left", Kleft, 1<<6);
regkey("right", Kright, 1<<7);
pc = memread(0xFFFC) | memread(0xFFFD) << 8;
rP = FLAGI;
dmcfreq = 12 * 428;
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;
apuclock += t;
sampclock += t;
dmcclock += t;
while(ppuclock >= 4){
ppustep();
ppuclock -= 4;
}
if(apuclock >= APUDIV){
apustep();
apuclock -= APUDIV;
}
if(sampclock >= SAMPDIV){
audiosample();
sampclock -= SAMPDIV;
}
if(dmcclock >= dmcfreq){
dmcstep();
dmcclock -= dmcfreq;
}
if(msgclock > 0){
msgclock -= t;
if(msgclock <= 0){
extern Image *bg;
draw(screen, screen->r, bg, nil, ZP);
msgclock = 0;
}
}
if(saveclock > 0){
saveclock -= t;
if(saveclock <= 0)
flushram();
}
}
}