git: 9front

Download patch

ref: e7ae491b6fcc823ed415090f660086bee7dec659
parent: ee75c96a3cb4322ce5e6ce3414b18faaf287b3f7
author: aiju <aiju@phicode.de>
date: Fri Apr 6 19:29:37 EDT 2012

various games/gb improvements

--- 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)
--- a/sys/src/games/gb/daa.c
+++ b/sys/src/games/gb/daa.c
@@ -462,4 +462,4 @@
 	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
+};
--- 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;
--- a/sys/src/games/gb/fns.h
+++ b/sys/src/games/gb/fns.h
@@ -4,3 +4,7 @@
 void ppustep(void);
 void disasm(u16int);
 void interrupt(u8int);
+void message(char *, ...);
+void flushram(void);
+void savestate(char *);
+void loadstate(char *);
--- a/sys/src/games/gb/gb.c
+++ b/sys/src/games/gb/gb.c
@@ -1,24 +1,43 @@
 #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)
 {
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 		while(*s != 0){
 			s += chartorune(&r, s);
 			switch(r){
-			case 'q':
+			case Kesc:
 				threadexitsall(nil);
 			case Kdown:
 				keys |= 1<<3;
@@ -141,8 +220,10 @@
 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 @@
 	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 @@
 		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]++;
@@ -196,6 +294,13 @@
 				sleep(diff);
 			old = new;
 			syncclock = 0;
+		}
+		if(msgclock > 0){
+			msgclock -= t;
+			if(msgclock <= 0){
+				draw(screen, screen->r, bg, nil, ZP);
+				msgclock = 0;
+			}
 		}
 	}
 }
--- 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,10 +23,48 @@
 				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)
 {
 	if(p < 0x8000){
@@ -33,25 +72,48 @@
 		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;
--- a/sys/src/games/gb/mkfile
+++ b/sys/src/games/gb/mkfile
@@ -9,6 +9,7 @@
 	disasm.$O\
 	ppu.$O\
 	daa.$O\
+	state.$O\
 
 HFILES=dat.h fns.h
 
--- a/sys/src/games/gb/ppu.c
+++ b/sys/src/games/gb/ppu.c
@@ -47,15 +47,6 @@
 }
 
 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 @@
 ppustep(void)
 {
 	extern Rectangle picr;
+	extern Image *tmp;
 
 	if(mem[LY] == 144){
 		mem[STAT] &= ~3;
@@ -188,9 +180,13 @@
 	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);
 		}
 	}
 }
--- /dev/null
+++ b/sys/src/games/gb/state.c
@@ -1,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);
+}
--