]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/games/nes/nes.c
games/doom: fix idclev cheat in doom2 and final doom (thanks qu7uux)
[plan9front.git] / sys / src / games / nes / nes.c
index ee72e2b8a7259082f8180e48261fc8c26c4a94a5..78c25adb369320894d86a3e429345ed73cb5e314 100644 (file)
@@ -8,24 +8,47 @@
 #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)
@@ -62,6 +85,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)
@@ -69,8 +93,8 @@ loadrom(char *file)
                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");
        }
@@ -80,117 +104,225 @@ loadrom(char *file)
                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();
                }
        }
 }