diff --git a/sys/src/games/gb/cpu.c b/sys/src/games/gb/cpu.c index 278c499de..691527ccc 100644 --- a/sys/src/games/gb/cpu.c +++ b/sys/src/games/gb/cpu.c @@ -9,7 +9,7 @@ u8int R[8], Fl; u16int pc, sp, curpc; -int halt, IME, nobios; +int halt, IME; static void invalid(void) diff --git a/sys/src/games/gb/daa.c b/sys/src/games/gb/daa.c index 13ef4b39c..83a72d1af 100644 --- a/sys/src/games/gb/daa.c +++ b/sys/src/games/gb/daa.c @@ -462,4 +462,4 @@ u8int daa[] = { 0x87, 0x50, 0x88, 0x50, 0x89, 0x50, 0x8A, 0x50, 0x8B, 0x50, 0x8C, 0x50, 0x8D, 0x50, 0x8E, 0x50, 0x8F, 0x50, 0x90, 0x50, 0x91, 0x50, 0x92, 0x50, 0x93, 0x50, 0x94, 0x50, 0x95, 0x50, 0x96, 0x50, 0x97, 0x50, 0x98, 0x50, 0x99, 0x50 -}; \ No newline at end of file +}; diff --git a/sys/src/games/gb/dat.h b/sys/src/games/gb/dat.h index c35f7c902..1e13d918d 100644 --- a/sys/src/games/gb/dat.h +++ b/sys/src/games/gb/dat.h @@ -1,8 +1,10 @@ extern u16int pc, curpc, sp; extern u8int R[8], Fl; -extern int halt, IME, bank, keys; +extern int halt, IME, keys; extern int clock, ppuclock, divclock, timerclock, syncclock, timerfreq, timer; -extern uchar mem[]; +extern int rombank, rambank, ramen, battery, ramrom; + +extern uchar mem[], *ram; extern uchar *cart; extern int mbc, rombanks, rambanks; diff --git a/sys/src/games/gb/fns.h b/sys/src/games/gb/fns.h index 092628219..990f794e1 100644 --- a/sys/src/games/gb/fns.h +++ b/sys/src/games/gb/fns.h @@ -4,3 +4,7 @@ int step(void); void ppustep(void); void disasm(u16int); void interrupt(u8int); +void message(char *, ...); +void flushram(void); +void savestate(char *); +void loadstate(char *); diff --git a/sys/src/games/gb/gb.c b/sys/src/games/gb/gb.c index 5ba26ea4a..bed16e2cb 100644 --- a/sys/src/games/gb/gb.c +++ b/sys/src/games/gb/gb.c @@ -1,15 +1,31 @@ #include #include -#include -#include #include +#include +#include +#include +#include #include "dat.h" #include "fns.h" -uchar *cart; -int mbc, rombanks, clock, ppuclock, divclock, timerclock, syncclock, timerfreq, timer, keys; +uchar *cart, *ram; +int mbc, rombanks, rambanks, clock, ppuclock, divclock, timerclock, syncclock, msgclock, timerfreq, timer, keys, savefd, savereq, loadreq; Rectangle picr; -Image *bg; +Image *bg, *tmp; +Mousectl *mc; + +void +message(char *fmt, ...) +{ + 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); +} void loadrom(char *file) @@ -17,8 +33,11 @@ loadrom(char *file) int fd, i; vlong len; u8int ck; + char buf[512]; char title[17]; Point p; + char *s; + extern int battery, ramen; fd = open(file, OREAD); if(fd < 0) @@ -44,19 +63,34 @@ loadrom(char *file) 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 0x01: + case 0x03: + battery = 1; + case 0x01: case 0x02: mbc = 1; break; - case 0x13: + case 0x06: + battery = 1; + case 0x05: + mbc = 2; + break; + case 0x0F: case 0x10: case 0x13: + battery = 1; + case 0x11: case 0x12: mbc = 3; 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: @@ -72,17 +106,56 @@ loadrom(char *file) case 54: rombanks = 96; break; + default: + sysfatal("header field 0x148 (%.2x) invalid", cart[0x148]); + } + switch(cart[0x149]){ + case 0: + if(mbc != 2){ + rambanks = 0; + break; + } + /*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(len < rombanks * 0x4000) sysfatal("cartridge image is too small, %.4x < %.4x", (int)len, rombanks * 0x4000); - initdraw(nil, nil, title); - open("/dev/mouse", OREAD); originwindow(screen, Pt(0, 0), screen->r.min); p = divpt(addpt(screen->r.min, screen->r.max), 2); picr = (Rectangle){subpt(p, Pt(80, 72)), addpt(p, Pt(80, 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, 160, 144), XRGB32, 0, 0); draw(screen, screen->r, bg, nil, ZP); + + 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); + } } void @@ -98,8 +171,14 @@ keyproc(void *) for(;;){ if(read(fd, buf, 256) <= 0) sysfatal("read /dev/kbd: %r"); - if(buf[0] == 'c' && strchr(buf, 'q')) - threadexitsall(nil); + 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; @@ -107,7 +186,7 @@ keyproc(void *) while(*s != 0){ s += chartorune(&r, s); switch(r){ - case 'q': + case Kesc: threadexitsall(nil); case Kdown: keys |= 1<<3; @@ -141,8 +220,10 @@ keyproc(void *) void threadmain(int argc, char** argv) { - int t, count; + int t; vlong old, new, diff; + Mouse m; + Point p; ARGBEGIN{ default: @@ -159,12 +240,20 @@ threadmain(int argc, char** argv) R[rH] = 0x01; Fl = 0xB0; loadrom(argv[0]); + mc = initmouse(nil, screen); + if(mc == nil) + sysfatal("init mouse: %r"); proccreate(keyproc, nil, 8192); - count = 0; old = nsec(); for(;;){ - if(pc == 0x231 && count++) - break; + if(savereq){ + savestate("gb.save"); + savereq = 0; + } + if(loadreq){ + loadstate("gb.save"); + loadreq = 0; + } t = step(); clock += t; ppuclock += t; @@ -174,6 +263,15 @@ threadmain(int argc, char** argv) 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(80, 72)), addpt(p, Pt(80, 72))}; + bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); + } } if(divclock >= 256){ mem[DIV]++; @@ -197,5 +295,12 @@ threadmain(int argc, char** argv) old = new; syncclock = 0; } + if(msgclock > 0){ + msgclock -= t; + if(msgclock <= 0){ + draw(screen, screen->r, bg, nil, ZP); + msgclock = 0; + } + } } } diff --git a/sys/src/games/gb/mem.c b/sys/src/games/gb/mem.c index 9a186c290..4348a5567 100644 --- a/sys/src/games/gb/mem.c +++ b/sys/src/games/gb/mem.c @@ -6,7 +6,8 @@ #include "fns.h" uchar mem[65536]; -int bank; +int rombank, rambank, ramen, battery, ramrom; +extern int savefd; u8int memread(u16int p) @@ -22,9 +23,47 @@ memread(u16int p) return (mem[0xFF00] & 0xF0) | ~(keys & 0x0F); return (mem[0xFF00] & 0xF0) | 0x0F; } + if(!ramen && ((p & 0xE000) == 0xA000)) + return 0xFF; return mem[p]; } +static void +ramswitch(int state, int bank) +{ + if(ramen){ + memcpy(ram + 8192 * rambank, mem + 0xA000, 8192); + if(battery && savefd > 0){ + seek(savefd, rambank * 8192, 0); + write(savefd, ram + 8192 * rambank, 8192); + } + ramen = 0; + } + rambank = bank; + if(state){ + if(bank >= rambanks) + sysfatal("invalid RAM bank %d selected (pc = %.4x)", bank, curpc); + memcpy(mem + 0xA000, ram + 8192 * rambank, 8192); + ramen = 1; + } +} + +void +flushram(void) +{ + if(ramen) + ramswitch(ramen, rambank); +} + +static void +romswitch(int bank) +{ + if(bank >= rombanks) + sysfatal("invalid ROM bank %d selected (pc = %.4x)", bank, curpc); + rombank = bank; + memcpy(mem + 0x4000, cart + 0x4000 * bank, 0x4000); +} + void memwrite(u16int p, u8int v) { @@ -33,25 +72,48 @@ memwrite(u16int p, u8int v) case 0: return; case 1: + case 2: switch(p >> 13){ + case 0: + if((v & 0x0F) == 0x0A) + ramswitch(1, rambank); + else + ramswitch(0, rambank); + return; case 1: + v &= 0x1F; if(v == 0) v++; - bank = v; - if(bank >= rombanks) - sysfatal("invalid ROM bank %d selected (pc = %.4x)", bank, curpc); - memcpy(mem + 0x4000, cart + 0x4000 * bank, 0x4000); + romswitch((rombank & 0xE0) | v); + return; + case 2: + if(ramrom) + ramswitch(ramen, v & 3); + else + romswitch(((v & 3) << 5) | (rombank & 0x1F)); + return; + case 3: + ramrom = v; return; - } return; case 3: switch(p >> 13){ + case 0: + if((v & 0x0F) == 0x0A) + ramswitch(1, rambank); + else + ramswitch(0, rambank); + return; case 1: - bank = v; - if(bank >= rombanks) - sysfatal("invalid ROM bank %d selected (pc = %.4x)", bank, curpc); - memcpy(mem + 0x4000, cart + 0x4000 * bank, 0x4000); + v &= 0x7F; + if(v == 0) + v++; + romswitch(v); + return; + case 2: + if(v < 4) + ramswitch(ramen, v); return; } return; diff --git a/sys/src/games/gb/mkfile b/sys/src/games/gb/mkfile index 823f64cad..c500e64b0 100644 --- a/sys/src/games/gb/mkfile +++ b/sys/src/games/gb/mkfile @@ -9,6 +9,7 @@ OFILES=\ disasm.$O\ ppu.$O\ daa.$O\ + state.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/gb/ppu.c b/sys/src/games/gb/ppu.c index aacf293f7..a83ea1980 100644 --- a/sys/src/games/gb/ppu.c +++ b/sys/src/games/gb/ppu.c @@ -46,15 +46,6 @@ pixelbelow(int x, int y, int val) pixel(x, y, val, 0); } -static void -zeropic(void) -{ - int i; - - for(i = 0; i < sizeof pic; i++) - pic[i] = ((i & 3) == 3) ? 0 : 0xFF; -} - static void drawbg(void) { @@ -163,6 +154,7 @@ void ppustep(void) { extern Rectangle picr; + extern Image *tmp; if(mem[LY] == 144){ mem[STAT] &= ~3; @@ -188,9 +180,13 @@ ppustep(void) if(mem[LY] > 160){ mem[LY] = 0; if(mem[LCDC] & LCDOP){ - loadimage(screen, picr, pic, sizeof(pic)); + if(tmp){ + loadimage(tmp, tmp->r, pic, sizeof(pic)); + draw(screen, picr, tmp, nil, ZP); + }else + loadimage(screen, picr, pic, sizeof(pic)); flushimage(display, 1); - zeropic(); + memset(pic, sizeof pic, 0); } } } diff --git a/sys/src/games/gb/state.c b/sys/src/games/gb/state.c new file mode 100644 index 000000000..c5db3088a --- /dev/null +++ b/sys/src/games/gb/state.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static int fd; + +static void +put8(u8int i) +{ + write(fd, &i, 1); +} + +static void +put16(u16int i) +{ + put8(i); + put8(i >> 8); +} + +static void +put32(u32int i) +{ + put8(i); + put8(i >> 8); + put8(i >> 16); + put8(i >> 24); +} + +static int +get8(void) +{ + u8int c; + + read(fd, &c, 1); + return c; +} + +static int +get16(void) +{ + int i; + + i = get8(); + i |= get8() << 8; + return i; +} + +static int +get32(void) +{ + int i; + + i = get8(); + i |= get8() << 8; + i |= get8() << 16; + i |= get8() << 24; + return i; +} + +void +loadstate(char *file) +{ + flushram(); + fd = open(file, OREAD); + if(fd < 0){ + message("open: %r"); + return; + } + read(fd, mem, 65536); + if(ram != nil) + read(fd, ram, rambanks * 8192); + read(fd, R, sizeof R); + sp = get16(); + pc = get16(); + Fl = get8(); + halt = get32(); + IME = get32(); + clock = get32(); + ppuclock = get32(); + divclock = get32(); + syncclock = get32(); + timerfreq = get32(); + timer = get32(); + rombank = get32(); + rambank = get32(); + ramen = get32(); + battery = get32(); + ramrom = get32(); + close(fd); +} + +void +savestate(char *file) +{ + flushram(); + fd = create(file, ORDWR, 0666); + if(fd < 0){ + message("create: %r"); + return; + } + write(fd, mem, 65536); + if(ram != nil) + write(fd, ram, rambanks * 8192); + write(fd, R, sizeof R); + put16(sp); + put16(pc); + put8(Fl); + put32(halt); + put32(IME); + put32(clock); + put32(ppuclock); + put32(divclock); + put32(timerclock); + put32(syncclock); + put32(timerfreq); + put32(timer); + put32(rombank); + put32(rambank); + put32(ramen); + put32(battery); + put32(ramrom); + close(fd); +}