]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/games/gb/gb.c
demote libemu to common code
[plan9front.git] / sys / src / games / gb / gb.c
index e1edd904322ce5625029db34dab066b88d5b6d70..b82729023b002070bd8775445669ac70ab283b64 100644 (file)
 #include <u.h>
 #include <libc.h>
-#include <draw.h>
 #include <thread.h>
-#include <mouse.h>
-#include <cursor.h>
+#include <draw.h>
 #include <keyboard.h>
+#include "../eui.h"
 #include "dat.h"
 #include "fns.h"
 
-uchar *cart, *ram;
-int mbc, rombanks, rambanks, clock, ppuclock, divclock, timerclock, syncclock, syncfreq, sleeps, checkclock, msgclock, timerfreq, timer, keys, savefd, savereq, loadreq, scale, paused;
-Rectangle picr;
-Image *bg, *tmp;
-Mousectl *mc;
-QLock pauselock;
+int cpuhalt;
+int backup;
+int savefd = -1, saveframes;
+ulong clock;
+u8int mbc, feat, mode;
+extern MBC3Timer timer, timerl;
 
+extern double TAU; 
 void
-message(char *fmt, ...)
+tauup(void)
 {
-       va_list va;
-       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 = CPUFREQ;
-       va_end(va);
+       TAU += 5000;
+}
+void
+taudn(void)
+{
+       TAU -= 5000;
+}
+
+void
+writeback(void)
+{
+       if(saveframes == 0)
+               saveframes = 15;
+}
+
+void
+timerload(uchar *buf)
+{
+       timer.ns = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24 | (uvlong)buf[4] << 32 | (uvlong)buf[5] << 40 | (uvlong)buf[6] << 48LL | (uvlong)buf[7] << 56LL;
+       timer.sec = buf[8];
+       timer.min = buf[9];
+       timer.hr = buf[10];
+       timer.dl = buf[11];
+       timer.dh = buf[12];
+       timerl.sec = buf[13];
+       timerl.min = buf[14];
+       timerl.hr = buf[15];
+       timerl.dl = buf[16];
+       timerl.dh = buf[17];
+}
+
+void
+timersave(uchar *buf)
+{
+       buf[0] = timer.ns;
+       buf[1] = timer.ns >> 8;
+       buf[2] = timer.ns >> 16;
+       buf[3] = timer.ns >> 24;
+       buf[4] = timer.ns >> 32;
+       buf[5] = timer.ns >> 40;
+       buf[6] = timer.ns >> 48;
+       buf[7] = timer.ns >> 56;
+       buf[8] = timer.sec;
+       buf[9] = timer.min;
+       buf[10] = timer.hr;
+       buf[11] = timer.dl;
+       buf[12] = timer.dh;
+       buf[13] = timerl.sec;
+       buf[14] = timerl.min;
+       buf[15] = timerl.hr;
+       buf[16] = timerl.dl;
+       buf[17] = timerl.dh;
+}
+
+void
+flushback(void)
+{
+       uchar buf[TIMERSIZ];
+
+       if(savefd >= 0){
+               pwrite(savefd, back, nback, 0);
+               timersave(buf);
+               pwrite(savefd, buf, TIMERSIZ, nback);
+       }
+       saveframes = 0;
+}
+
+void
+loadsave(char *file)
+{
+       char *buf, *p;
+       uchar tim[TIMERSIZ];
+
+       buf = emalloc(strlen(file) + 4);
+       strcpy(buf, file);
+       p = strrchr(buf, '.');
+       if(p == nil)
+               p = buf + strlen(buf);
+       strcpy(p, ".sav");
+       savefd = open(buf, ORDWR);
+       if(savefd < 0){
+               savefd = create(buf, OWRITE, 0664);
+               if(savefd < 0){
+                       fprint(2, "create: %r");
+                       free(buf);
+                       return;
+               }
+               back = emalloc(nback);
+               memset(back, 0, nback);
+               write(savefd, back, nback);
+               free(buf);
+               if((feat & FEATTIM) != 0){
+                       timer.ns = nsec();
+                       timersave(tim);
+                       write(savefd, tim, TIMERSIZ);
+               }
+               atexit(flushback);
+               return;
+       }
+       back = emalloc(nback);
+       readn(savefd, back, nback);
+       if((feat & FEATTIM) != 0){
+               readn(savefd, tim, TIMERSIZ);
+               timerload(tim);
+       }
+       atexit(flushback);
+       free(buf);
 }
 
 void
 loadrom(char *file)
 {
-       int fd, i;
-       vlong len;
-       u8int ck;
-       char buf[512];
-       char title[17];
-       Point p;
-       char *s;
-       extern int battery, ramen;
+       int fd;
+       vlong sz;
+       static uchar nintendo[24] = {
+               0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83,
+               0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E
+       };
+       static u8int mbctab[31] = {
+               0, 1, 1, 1, -1, 2, 2, -1,
+               0, 0, -1, 6, 6, 6, -1, 3,
+               3, 3, 3, 3, -1, 4, 4, 4,
+               -1, 5, 5, 5, 5, 5, 5};
+       static u8int feattab[31] = {
+               0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT, 0,
+               FEATRAM, FEATRAM|FEATBAT, 0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATTIM|FEATBAT,
+               FEATTIM|FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT, 0, 0, FEATRAM, FEATRAM|FEATBAT,
+               0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT
+       };
        
        fd = open(file, OREAD);
        if(fd < 0)
                sysfatal("open: %r");
-       len = seek(fd, 0, 2);
-       if(len < 0)
-               sysfatal("seek: %r");
-       if(len == 0 || len > 16*1048576)
-               sysfatal("are you sure this is a ROM?");
-       cart = malloc(len);
-       if(cart == nil)
-               sysfatal("malloc: %r");
+       sz = seek(fd, 0, 2);
+       if(sz <= 0 || sz > 32*1024*1024)
+               sysfatal("invalid file size");
        seek(fd, 0, 0);
-       if(readn(fd, cart, len) < len)
+       nrom = sz;
+       rom = emalloc(nrom);
+       if(readn(fd, rom, sz) < sz)
                sysfatal("read: %r");
        close(fd);
-
-       ck = 0;
-       for(i = 0x134; i <= 0x14C; i++)
-               ck -= cart[i] + 1;
-       if(ck != cart[0x14D])
-               sysfatal("checksum mismatch: %.2x != %.2x", ck, cart[0x14D]);
-       memcpy(mem, cart, 32768);
-       memset(title, 0, sizeof(title));
-       memcpy(title, cart+0x134, 16);
-       battery = 0;
-       switch(cart[0x147]){
-       case 0x09:
-               battery = 1;
-       case 0x08:
-               ramen = 1;
-       case 0x00:
-               mbc = 0;
-               break;
-       case 0x03:
-               battery = 1;
-       case 0x01: case 0x02:
-               mbc = 1;
-               break;
-       case 0x06:
-               battery = 1;
-       case 0x05:
-               mbc = 2;
-               break;
-       case 0x0F: case 0x10: case 0x13:
-               battery = 1;
-       case 0x11: case 0x12:
-               mbc = 3;
-               break;
-       case 0x1B: case 0x1E:
-               battery = 1;
-       case 0x19: case 0x1A: case 0x1C: case 0x1D:
-               mbc = 5;
-               break;
-       default:
-               sysfatal("%s: unknown cartridge type %.2x", file, cart[0x147]);
-       }
-
-       switch(cart[0x148]){
-       case 0: case 1: case 2:
-       case 3: case 4: case 5:
-       case 6: case 7:
-               rombanks = 2 << (uint)cart[0x148];
-               break;
-       case 52:
-               rombanks = 72;
-               break;
-       case 53:
-               rombanks = 80;
-               break;
-       case 54:
-               rombanks = 96;
-               break;
-       default:
-               sysfatal("header field 0x148 (%.2x) invalid", cart[0x148]);
-       }
-       switch(cart[0x149]){
-       case 0:
-               if(mbc != 2){
-                       rambanks = 0;
+       if(memcmp(rom + 0x104, nintendo, 24) != 0)
+               sysfatal("not a gameboy rom");
+       if(rom[0x147] > 0x1f)
+               sysfatal("unsupported mapper ([0x147] = %.2ux)", rom[0x147]);
+       mbc = mbctab[rom[0x147]];
+       feat = feattab[rom[0x147]];
+       if((feat & FEATRAM) != 0){
+               switch(rom[0x149]){
+               case 0:
+                       if(mbc == 2)
+                               nback = 512;
+                       else
+                               feat &= ~FEATRAM;
                        break;
+               case 1: nback = 2048; break;
+               case 2: nback = 8192; break;
+               case 3: nback = 32768; break;
+               default: sysfatal("invalid ram size");
                }
-               /*fallthrough*/
-       case 1: case 2:
-               rambanks = 1;
-               break;
-       case 3:
-               rambanks = 4;
-               break;
-       default:
-               sysfatal("header field 0x149 (%.2x) invalid", cart[0x149]);
        }
-       if(rambanks > 0){
-               ram = mallocz(rambanks * 8192, 1);
-               if(ram == nil)
-                       sysfatal("malloc: %r");
+       if(nback == 0)
+               nbackbank = 1;
+       else
+               nbackbank = nback + 8191 >> 13;
+       if((feat & (FEATRAM|FEATTIM)) == 0)
+               feat &= ~FEATBAT;
+       if((rom[0x143] & 0x80) != 0 && (mode & FORCEDMG) == 0)
+               mode = CGB|COL;
+       if((feat & FEATBAT) != 0)
+               loadsave(file); 
+       switch(mbc){
+       case 0: case 1: case 2: case 3: case 5: break;
+       default: sysfatal("unsupported mbc %d", mbc);
+       }
+
+}
+
+void
+flush(void)
+{
+       extern uchar pic[];
+       static vlong old, delta;
+
+       flushmouse(1);
+       flushscreen();
+       flushaudio(audioout);
+       if(saveframes > 0 && --saveframes == 0)
+               flushback();
+       if(savereq){
+               savestate("gb.save");
+               savereq = 0;
+       }
+       if(loadreq){
+               loadstate("gb.save");
+               loadreq = 0;
        }
-       if(len < rombanks * 0x4000)
-               sysfatal("cartridge image is too small, %.4x < %.4x", (int)len, rombanks * 0x4000);
-       initdraw(nil, nil, title);
-       originwindow(screen, Pt(0, 0), screen->r.min);
-       p = divpt(addpt(screen->r.min, screen->r.max), 2);
-       picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
-       bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
-       if(screen->chan != XRGB32 || screen->chan != XBGR32)
-               tmp = allocimage(display, Rect(0, 0, scale * 160, scale * 144), XRGB32, 0, 0);
-       draw(screen, screen->r, bg, nil, ZP);
+}
+
+void
+usage(void)
+{
+       fprint(2, "usage: %s [-23aTcd] [-C col0,col1,col2,col3] rom\n", argv0);
+       exits("usage");
+}
+
+void
+colinit(void)
+{
+       int i;
+       union { u8int c[4]; u32int l; } c;
        
-       if(ram && battery){
-               strncpy(buf, file, sizeof buf - 4);
-               s = buf + strlen(buf) - 3;
-               if(s < buf || strcmp(s, ".gb") != 0)
-                       s += 3;
-               strcpy(s, ".gbs");
-               savefd = create(buf, ORDWR|OEXCL, 0666);
-               if(savefd < 0)
-                       savefd = open(buf, ORDWR);
-               if(savefd < 0)
-                       message("open: %r");
-               else
-                       readn(savefd, ram, rambanks * 8192);
-               atexit(flushram);
+       c.c[3] = 0;
+       for(i = 0; i < 4; i++){
+               c.c[0] = c.c[1] = c.c[2] = i * 0x55;
+               moncols[i] = c.l;
        }
 }
 
 void
-keyproc(void *)
+colparse(char *p)
 {
-       int fd;
-       char buf[256], *s;
-       Rune r;
+       int i;
+       union { u8int c[4]; u32int l; } c;
+       u32int l;
        
-       fd = open("/dev/kbd", OREAD);
-       if(fd < 0)
-               sysfatal("open: %r");
-       for(;;){
-               if(read(fd, buf, 256) <= 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(buf[0] != 'k' && buf[0] != 'K')
-                       continue;
-               s = buf + 1;
-               keys = 0;
-               while(*s != 0){
-                       s += chartorune(&r, s);
-                       switch(r){
-                       case Kesc:
-                               if(paused)
-                                       qunlock(&pauselock);
-                               else
-                                       qlock(&pauselock);
-                               paused = !paused;
-                               break;
-                       case Kdel:
-                               threadexitsall(nil);
-                       case Kdown:
-                               keys |= 1<<3;
-                               break;
-                       case Kup:
-                               keys |= 1<<2;
-                               break;
-                       case Kleft:
-                               keys |= 1<<1;
-                               break;
-                       case Kright:
-                               keys |= 1<<0;
-                               break;
-                       case 'x':
-                               keys |= 1<<4;
-                               break;
-                       case 'z':
-                               keys |= 1<<5;
-                               break;
-                       case Kshift:
-                               keys |= 1<<6;
-                               break;
-                       case 10:
-                               keys |= 1<<7;
-                               break;
-                       }
-               }
+       c.c[3] = 0;
+       for(i = 0; i < 4; i++){
+               l = strtol(p, &p, 16);
+               if(*p != (i == 3 ? 0 : ',') || l > 0xffffff)
+                       usage();
+               p++;
+               c.c[0] = l;
+               c.c[1] = l >> 8;
+               c.c[2] = l >> 16;
+               moncols[i] = c.l;
        }
 }
 
 void
-threadmain(int argc, char** argv)
+threadmain(int argc, char **argv)
 {
        int t;
-       vlong old, new, diff;
-       Mouse m;
-       Point p;
 
-       scale = 1;
-       ARGBEGIN{
+       colinit();
+       ARGBEGIN {
        case 'a':
-               initaudio();
+               audioinit();
                break;
-       case '2':
-               scale = 2;
+       case 'c':
+               mode |= CGB;
                break;
-       case '3':
-               scale = 3;
+       case 'd':
+               mode |= FORCEDMG;
+               break;
+       case 'C':
+               colparse(EARGF(usage()));
                break;
        default:
-               sysfatal("unknown flag -%c", ARGC());
-       }ARGEND;
-       if(argc == 0)
-               sysfatal("argument missing");
-       pc = 0x100;
-       sp = 0xFFFE;
-       R[rA] = 0x01;
-       R[rC] = 0x13;
-       R[rE] = 0xD8;
-       R[rL] = 0x4D;
-       R[rH] = 0x01;
-       Fl = 0xB0;
+               usage();
+       } ARGEND;
+       if(argc < 1)
+               usage();
+
        loadrom(argv[0]);
-       mc = initmouse(nil, screen);
-       if(mc == nil)
-               sysfatal("init mouse: %r");
-       proccreate(keyproc, nil, 8192);
-       syncfreq = CPUFREQ / 50;
-       old = nsec();
+       initemu(PICW, PICH, 4, XRGB32, 1, nil);
+       regkey("b", 'z', 1<<5);
+       regkey("a", 'x', 1<<4);
+       regkey("control", Kshift, 1<<6);
+       regkey("start", '\n', 1<<7);
+       regkey("up", Kup, 1<<2);
+       regkey("down", Kdown, 1<<3);
+       regkey("left", Kleft, 1<<1);
+       regkey("right", Kright, 1<<0);
+       regkeyfn(KF|9, tauup);
+       regkeyfn(KF|10, taudn);
+
+       eventinit();
+       meminit();
+       ppuinit();
+       reset();
        for(;;){
-               if(savereq){
-                       savestate("gb.save");
-                       savereq = 0;
-               }
-               if(loadreq){
-                       loadstate("gb.save");
-                       loadreq = 0;
-               }
                if(paused){
                        qlock(&pauselock);
                        qunlock(&pauselock);
                }
-               t = step();
+               if(dma > 0)
+                       t = dmastep();
+               else
+                       t = step();
+               if((mode & TURBO) == 0)
+                       t += t;
                clock += t;
-               ppuclock += t;
-               divclock += t;
-               timerclock += t;
-               syncclock += t;
-               checkclock += t;
-               if(ppuclock >= 456){
-                       ppustep();
-                       ppuclock -= 456;
-                       while(nbrecv(mc->c, &m) > 0)
-                               ;
-                       if(nbrecvul(mc->resizec) > 0){
-                               if(getwindow(display, Refnone) < 0)
-                                       sysfatal("resize failed: %r");
-                               p = divpt(addpt(screen->r.min, screen->r.max), 2);
-                               picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
-                               bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
-                       }
-               }
-               if(divclock >= 256){
-                       mem[DIV]++;
-                       divclock = 0;
-               }
-               if(timer && timerclock >= timerfreq){
-                       mem[TIMA]++;
-                       if(mem[TIMA] == 0){
-                               mem[TIMA] = mem[TMA];
-                               interrupt(INTTIMER);
-                       }
-                       timerclock = 0;
-               }
-               if(syncclock >= syncfreq){
-                       sleep(10);
-                       sleeps++;
-                       syncclock = 0;
-               }
-               if(checkclock >= CPUFREQ){
-                       new = nsec();
-                       diff = new - old - sleeps * 10 * MILLION;
-                       diff = BILLION - diff;
-                       if(diff <= 0)
-                               syncfreq = CPUFREQ;
-                       else
-                               syncfreq = ((vlong)CPUFREQ) * 10 * MILLION / diff;
-                       old = new;
-                       checkclock = 0;
-                       sleeps = 0;
-               }
-               if(msgclock > 0){
-                       msgclock -= t;
-                       if(msgclock <= 0){
-                               draw(screen, screen->r, bg, nil, ZP);
-                               msgclock = 0;
-                       }
-               }
+               if((elist->time -= t) <= 0)
+                       popevent();
        }
 }