#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;
-Rectangle picr;
-Image *bg, *tmp;
-Mousectl *mc;
+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(strchr(buf, Kesc))
- 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:
- 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 'c':
+ mode |= CGB;
break;
- case '2':
- scale = 2;
+ case 'd':
+ mode |= FORCEDMG;
break;
- case '3':
- scale = 3;
+ 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();
}
}