summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/games/gb/cpu.c2
-rw-r--r--sys/src/games/gb/daa.c2
-rw-r--r--sys/src/games/gb/dat.h6
-rw-r--r--sys/src/games/gb/fns.h4
-rw-r--r--sys/src/games/gb/gb.c135
-rw-r--r--sys/src/games/gb/mem.c82
-rw-r--r--sys/src/games/gb/mkfile1
-rw-r--r--sys/src/games/gb/ppu.c18
-rw-r--r--sys/src/games/gb/state.c126
9 files changed, 336 insertions, 40 deletions
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 <u.h>
#include <libc.h>
+#include <draw.h>
#include <thread.h>
+#include <mouse.h>
+#include <cursor.h>
#include <keyboard.h>
-#include <draw.h>
#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
@@ -47,15 +47,6 @@ pixelbelow(int x, int y, int val)
}
static void
-zeropic(void)
-{
- int i;
-
- for(i = 0; i < sizeof pic; i++)
- pic[i] = ((i & 3) == 3) ? 0 : 0xFF;
-}
-
-static void
drawbg(void)
{
u8int Y, x, y, ty, toy, tx, tox, tnl1, tnl2, pal, val,h;
@@ -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 <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#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);
+}