#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, apuclock, dmcclock, dmcfreq, sampclock, msgclock, saveclock;
Mousectl *mc;
-int keys;
-extern void (*mapper[])(int, u8int);
+int keys, keys2, paused, savereq, loadreq, oflag, savefd = -1;
int mirr;
+QLock pauselock;
void
-loadrom(char *file)
+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("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)
if(readn(fd, chr, nchr * CHRSZ) < nchr * CHRSZ)
sysfatal("read: %r");
}else{
- nchr = 16;
- chr = malloc(16 * CHRSZ);
+ nchr = 1;
+ chr = malloc(nchr * CHRSZ);
if(chr == nil)
sysfatal("malloc: %r");
}
mirr = MVERT;
else
mirr = MHORZ;
- mapper[map](-1, 0);
+ 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);
}
extern int trace;
+void
+joyproc(void *)
+{
+ char *s, *down[9];
+ static char buf[64];
+ int n, k, j;
+
+ j = 1;
+ for(;;){
+ n = read(0, buf, sizeof(buf) - 1);
+ if(n <= 0)
+ sysfatal("read: %r");
+ buf[n] = 0;
+ n = getfields(buf, down, nelem(down), 1, " ");
+ k = 0;
+ for(n--; n >= 0; n--){
+ s = down[n];
+ if(strcmp(s, "joy1") == 0)
+ j = 1;
+ else if(strcmp(s, "joy2") == 0)
+ j = 2;
+ else if(strcmp(s, "a") == 0)
+ k |= 1<<0;
+ else if(strcmp(s, "b") == 0)
+ k |= 1<<1;
+ else if(strcmp(s, "control") == 0)
+ k |= 1<<2;
+ else if(strcmp(s, "start") == 0)
+ k |= 1<<3;
+ else if(strcmp(s, "up") == 0)
+ k |= 1<<4;
+ else if(strcmp(s, "down") == 0)
+ k |= 1<<5;
+ else if(strcmp(s, "left") == 0)
+ k |= 1<<6;
+ else if(strcmp(s, "right") == 0)
+ k |= 1<<7;
+ }
+ if(j == 2)
+ keys2 = k;
+ else
+ keys = k;
+ }
+}
+
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))
+ if(utfrune(buf, Kdel)){
+ close(fd);
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 Kdel: close(fd); threadexitsall(nil);
+ 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;
}
}
void
threadmain(int argc, char **argv)
{
- int t;
+ int t, h, sflag;
Point p;
- uvlong old, new, diff;
scale = 1;
+ h = 240;
+ sflag = 0;
ARGBEGIN {
+ case 'a':
+ initaudio();
+ break;
case '2':
scale = 2;
break;
case '3':
scale = 3;
break;
+ case 'o':
+ oflag = 1;
+ h -= 16;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ goto usage;
} ARGEND;
- if(argc < 1)
- sysfatal("missing argument");
- loadrom(argv[0]);
+ if(argc != 1){
+ usage:
+ fprint(2, "usage: %s [-23aos] rom\n", argv0);
+ threadexitsall("usage");
+ }
+ loadrom(argv[0], sflag);
if(initdraw(nil, nil, nil) < 0)
sysfatal("initdraw: %r");
mc = initmouse(nil, screen);
if(mc == nil)
sysfatal("initmouse: %r");
+ proccreate(joyproc, nil, 8192);
proccreate(keyproc, nil, 8192);
originwindow(screen, Pt(0, 0), screen->r.min);
p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 120)), addpt(p, Pt(scale * 128, scale * 120))};
- if(screen->chan != XRGB32)
- tmp = allocimage(display, Rect(0, 0, scale * 256, scale * 240), XRGB32, 0, 0);
+ picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
+ tmp = allocimage(display, Rect(0, 0, scale * 256, scale * h), XRGB32, 0, 0);
bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
draw(screen, screen->r, bg, nil, ZP);
pc = memread(0xFFFC) | memread(0xFFFD) << 8;
rP = FLAGI;
- syncfreq = FREQ / 30;
- old = nsec();
+ 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;
- syncclock += t;
- checkclock += t;
+ apuclock += t;
+ sampclock += t;
+ dmcclock += t;
while(ppuclock >= 4){
ppustep();
ppuclock -= 4;
}
- if(syncclock >= syncfreq){
- sleep(10);
- sleeps++;
- syncclock = 0;
+ 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){
+ draw(screen, screen->r, bg, nil, ZP);
+ msgclock = 0;
+ }
}
- if(checkclock >= FREQ){
- new = nsec();
- diff = new - old - sleeps * 10 * MILLION;
- diff = BILLION - diff;
- if(diff <= 0)
- syncfreq = FREQ;
- else
- syncfreq = ((vlong)FREQ) * 10 * MILLION / diff;
- if(syncfreq < FREQ / 100)
- syncfreq = FREQ / 100;
- old = new;
- checkclock = 0;
- sleeps = 0;
+ if(saveclock > 0){
+ saveclock -= t;
+ if(saveclock <= 0)
+ flushram();
}
}
}