shithub: riscv

Download patch

ref: 830a9b59c94cff0e733b20d7094bddf6c7cefc74
parent: 0181117b5f56341e71f72f1cda37e815853eaacc
author: aiju <devnull@localhost>
date: Fri Feb 21 15:48:23 EST 2014

games/nes: added state saving & bug fixes

--- a/sys/src/games/nes/dat.h
+++ b/sys/src/games/nes/dat.h
@@ -2,15 +2,17 @@
 extern u8int rA, rX, rY, rS, rP;
 extern uchar mem[32768], ppuram[16384], oam[256];
 extern u16int pput, ppuv;
-extern u8int ppusx;
-extern int mirr;
+extern u8int ppusx, vrambuf;
+extern int mirr, ppux, ppuy, odd, vramlatch, keylatch;
 
 extern int map, scale;
 extern uchar *prg, *chr;
-extern int nprg, nchr, nmi, map;
+extern int nprg, nchr, nmi, map, chrram;
 
-extern int keys;
+extern int keys, clock, ppuclock;
 
+extern void (*mapper[])(int, u8int);
+
 enum {
 	FLAGC = 1<<0,
 	FLAGZ = 1<<1,
@@ -76,4 +78,10 @@
 	MSINGA,
 	MSINGB,
 	MFOUR
+};
+
+enum {
+	INIT = -1,
+	SAVE = -2,
+	RSTR = -3,
 };
--- a/sys/src/games/nes/fns.h
+++ b/sys/src/games/nes/fns.h
@@ -4,3 +4,8 @@
 u8int	ppuread(u16int);
 void	ppuwrite(u16int, u8int);
 void	ppustep(void);
+void	loadstate(char *);
+void	savestate(char *);
+void	message(char *, ...);
+void	put8(u8int);
+int	get8(void);
--- a/sys/src/games/nes/mem.c
+++ b/sys/src/games/nes/mem.c
@@ -10,13 +10,23 @@
 uchar oam[256];
 uchar *prgb[2], *chrb[2];
 u16int pput, ppuv;
-u8int ppusx;
-static int vramlatch = 1, keylatch = 0xFF;
+u8int ppusx, vrambuf;
+int vramlatch = 1, keylatch = 0xFF;
 
 static void
+nope(int p)
+{
+	print("unimplemented mapper function %d (mapper %d)\n", p, map);
+}
+
+static void
 nrom(int p, u8int)
 {
-	if(p < 0){
+	if(p >= 0)
+		return;
+	switch(p){
+	case INIT:
+	case RSTR:
 		prgb[0] = prg;
 		if(nprg == 1)
 			prgb[1] = prg;
@@ -24,8 +34,12 @@
 			prgb[1] = prg + 0x4000;
 		chrb[0] = chr;
 		chrb[1] = chr + 0x1000;
+		break;
+	case SAVE:
+		break;
+	default:
+		nope(p);
 	}
-	return;
 }
 
 static void
@@ -32,14 +46,33 @@
 mmc1(int v, u8int p)
 {
 	static u8int n, s, mode, c0, c1, pr;
-	int wchr, wprg;
 	static int mirrs[] = {MSINGB, MSINGA, MVERT, MHORZ};
 	
 	if(v < 0){
-		wchr = 1;
-		wprg = 1;
-		mode = 0x0C;
-		goto t;
+		switch(v){
+		case INIT:
+			mode = 0x0C;
+			goto t;
+		case RSTR:
+			mode = get8();
+			c0 = get8();
+			c1 = get8();
+			pr = get8();
+			n = get8();
+			s = get8();
+			goto t;
+		case SAVE:
+			put8(mode);
+			put8(c0);
+			put8(c1);
+			put8(pr);
+			put8(n);
+			put8(s);
+			break;
+		default:
+			nope(v);
+		}
+		return;
 	}
 	if((p & 0x80) != 0){
 		n = 0;
@@ -53,66 +86,74 @@
 		s >>= 1;
 		return;
 	}
-	wchr = wprg = 1;
 	switch(v & 0xE000){
 	case 0x8000:
 		mode = s;
 		mirr = mirrs[mode & 3];
-		wchr = wprg = 1;
 		break;
 	case 0xA000:
 		c0 = s & 0x1f;
 		c0 %= 2*nchr;
-		wchr = 1;
 		break;
 	case 0xC000:
 		c1 = s & 0x1f;
 		c1 %= 2*nchr;
-		if((mode & 0x10) != 0)
-			wchr = 1;
 		break;
 	case 0xE000:
 		pr = s & 0x0f;
 		pr %= nprg;
-		wprg = 1;
 		break;
 	}
-t:
-	if(wprg)
-		switch(mode & 0x0c){
-		case 0x08:
-			prgb[0] = prg;
-			prgb[1] = prg + pr * 0x4000;
-			break;
-		case 0x0C:
-			prgb[0] = prg + pr * 0x4000;
-			prgb[1] = prg + (0x0f % nprg) * 0x4000;
-			break;
-		default:
-			prgb[0] = prg + (pr & 0xfe) * 0x4000;
-			prgb[1] = prg + (pr | 1) * 0x4000;
-			break;
-		}
-	if(wchr)
-		if((mode & 0x10) != 0){
-			chrb[0] = chr + c0 * 0x1000;
-			chrb[1] = chr + c1 * 0x1000;
-		}else{
-			chrb[0] = chr + (c0 & 0xfe) * 0x1000;
-			chrb[1] = chr + (c0 | 1) * 0x1000;
-		}
 	s = 0;
 	n = 0;
+t:
+	switch(mode & 0x0c){
+	case 0x08:
+		prgb[0] = prg;
+		prgb[1] = prg + pr * 0x4000;
+		break;
+	case 0x0C:
+		prgb[0] = prg + pr * 0x4000;
+		prgb[1] = prg + (0x0f % nprg) * 0x4000;
+		break;
+	default:
+		prgb[0] = prg + (pr & 0xfe) * 0x4000;
+		prgb[1] = prg + (pr | 1) * 0x4000;
+		break;
+	}
+	if((mode & 0x10) != 0){
+		chrb[0] = chr + c0 * 0x1000;
+		chrb[1] = chr + c1 * 0x1000;
+	}else{
+		chrb[0] = chr + (c0 & 0xfe) * 0x1000;
+		chrb[1] = chr + (c0 | 1) * 0x1000;
+	}
 }
 
 static void
 mmc7(int v, u8int p)
 {
-	if(v < 0){
-		nrom(-1, 0);
-		p = 0;
-	}
-	prgb[0] = prg + (p & 3) * 0x8000;
+	static int b;
+
+	if(v >= 0)
+		b = p;
+	else
+		switch(v){
+		case INIT:
+			nrom(INIT, 0);
+			b = 0;
+			break;
+		case SAVE:
+			put8(b);
+			return;
+		case RSTR:
+			b = get8();
+			break;
+		default:
+			nope(v);
+			return;
+		}
+	prgb[0] = prg + (b & 3) * 0x8000;
 	prgb[1] = prgb[0] + 0x4000;
 }
 
@@ -135,7 +176,6 @@
 u8int
 memread(u16int p)
 {
-	static u8int vrambuf;
 	u8int v;
 
 	if(p < 0x2000){
--- a/sys/src/games/nes/mkfile
+++ b/sys/src/games/nes/mkfile
@@ -7,6 +7,7 @@
 	mem.$O\
 	nes.$O\
 	ppu.$O\
+	state.$O\
 	
 HFILES=dat.h fns.h
 
--- a/sys/src/games/nes/nes.c
+++ b/sys/src/games/nes/nes.c
@@ -8,18 +8,31 @@
 #include "fns.h"
 
 extern uchar ppuram[16384];
-int nprg, nchr, map;
+int nprg, nchr, map, chrram;
 uchar *prg, *chr;
 int scale;
 Rectangle picr;
 Image *tmp, *bg;
-int clock, ppuclock, syncclock, syncfreq, checkclock, sleeps;
+int clock, ppuclock, syncclock, syncfreq, checkclock, msgclock, sleeps;
 Mousectl *mc;
-int keys;
-extern void (*mapper[])(int, u8int);
+int keys, paused, savereq, loadreq;
 int mirr;
+QLock pauselock;
 
 void
+message(char *fmt, ...)
+{
+	va_list va;
+	static 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 = FREQ;
+	va_end(va);
+}
+
+void
 loadrom(char *file)
 {
 	int fd;
@@ -62,6 +75,7 @@
 		sysfatal("malloc: %r");
 	if(readn(fd, prg, nprg * PRGSZ) < nprg * PRGSZ)
 		sysfatal("read: %r");
+	chrram = nchr == 0;
 	if(nchr != 0){
 		chr = malloc(nchr * CHRSZ);
 		if(chr == nil)
@@ -70,7 +84,7 @@
 			sysfatal("read: %r");
 	}else{
 		nchr = 16;
-		chr = malloc(16 * CHRSZ);
+		chr = malloc(nchr * CHRSZ);
 		if(chr == nil)
 			sysfatal("malloc: %r");
 	}
@@ -88,8 +102,9 @@
 void
 keyproc(void *)
 {
-	int fd;
-	char buf[256], *s;
+	int fd, k;
+	static char buf[256];
+	char *s;
 	Rune r;
 
 	fd = open("/dev/kbd", OREAD);
@@ -96,11 +111,15 @@
 	if(fd < 0)
 		sysfatal("open: %r");
 	for(;;){
-		if(read(fd, buf, 256) <= 0)
+		if(read(fd, buf, sizeof(buf) - 1) <= 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(utfrune(buf, 't'))
 				trace ^= 1;
 		}
@@ -107,21 +126,29 @@
 		if(buf[0] != 'k' && buf[0] != 'K')
 			continue;
 		s = buf + 1;
-		keys = 0;
+		k = 0;
 		while(*s != 0){
 			s += chartorune(&r, s);
 			switch(r){
 			case Kdel: threadexitsall(nil);
-			case 'x': keys |= 1<<0; break;
-			case 'z': keys |= 1<<1; break;
-			case Kshift: keys |= 1<<2; break;
-			case 10: keys |= 1<<3; break;
-			case Kup: keys |= 1<<4; break;
-			case Kdown: keys |= 1<<5; break;
-			case Kleft: keys |= 1<<6; break;
-			case Kright: keys |= 1<<7; break;
+			case 'x': k |= 1<<0; break;
+			case 'z': k |= 1<<1; break;
+			case Kshift: k |= 1<<2; break;
+			case 10: k |= 1<<3; break;
+			case Kup: k |= 1<<4; break;
+			case Kdown: k |= 1<<5; break;
+			case Kleft: k |= 1<<6; break;
+			case Kright: k |= 1<<7; break;
+			case Kesc:
+				if(paused)
+					qunlock(&pauselock);
+				else
+					qlock(&pauselock);
+				paused = !paused;
+				break;
 			}
-		}	
+		}
+		keys = k;
 	}
 }
 
@@ -164,6 +191,18 @@
 	syncfreq = FREQ / 30;
 	old = nsec();
 	for(;;){
+		if(savereq){
+			savestate("nes.save");
+			savereq = 0;
+		}
+		if(loadreq){
+			loadstate("nes.save");
+			loadreq = 0;
+		}
+		if(paused){
+			qlock(&pauselock);
+			qunlock(&pauselock);
+		}
 		t = step() * 12;
 		clock += t;
 		ppuclock += t;
@@ -191,6 +230,13 @@
 			old = new;
 			checkclock = 0;
 			sleeps = 0;
+		}
+		if(msgclock > 0){
+			msgclock -= t;
+			if(msgclock <= 0){
+				draw(screen, screen->r, bg, nil, ZP);
+				msgclock = 0;
+			}
 		}
 	}
 }
--- /dev/null
+++ b/sys/src/games/nes/state.c
@@ -1,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static int fd;
+
+void
+put8(u8int i)
+{
+	write(fd, &i, 1);
+}
+
+void
+put16(u16int i)
+{
+	put8(i);
+	put8(i >> 8);
+}
+
+void
+put32(u32int i)
+{
+	put8(i);
+	put8(i >> 8);
+	put8(i >> 16);
+	put8(i >> 24);
+}
+
+int
+get8(void)
+{
+	u8int c;
+	
+	read(fd, &c, 1);
+	return c;
+}
+
+int
+get16(void)
+{
+	int i;
+	
+	i = get8();
+	i |= get8() << 8;
+	return i;
+}
+
+int
+get32(void)
+{
+	int i;
+	
+	i = get8();
+	i |= get8() << 8;
+	i |= get8() << 16;
+	i |= get8() << 24;
+	return i;
+}
+
+void
+loadstate(char *file)
+{
+	fd = open(file, OREAD);
+	if(fd < 0){
+		message("open: %r");
+		return;
+	}
+	read(fd, mem, sizeof(mem));
+	read(fd, ppuram, sizeof(ppuram));
+	read(fd, oam, sizeof(oam));
+	if(chrram)
+		read(fd, chr, nchr * CHRSZ);
+	rA = get8();
+	rX = get8();
+	rY = get8();
+	rS = get8();
+	rP = get8();
+	nmi = get8();
+	pc = get16();
+	pput = get16();
+	ppuv = get16();
+	ppusx = get8();
+	ppux = get16();
+	ppuy = get16();
+	mirr = get8();
+	odd = get8();
+	vramlatch = get8();
+	keylatch = get8();
+	vrambuf = get8();
+	clock = get32();
+	ppuclock = get32();
+	mapper[map](RSTR, 0);
+	close(fd);
+}
+
+void
+savestate(char *file)
+{
+	fd = create(file, ORDWR, 0666);
+	if(fd < 0){
+		message("create: %r");
+		return;
+	}
+	write(fd, mem, sizeof(mem));
+	write(fd, ppuram, sizeof(ppuram));
+	write(fd, oam, sizeof(oam));
+	if(chrram)
+		write(fd, chr, nchr * CHRSZ);
+	put8(rA);
+	put8(rX);
+	put8(rY);
+	put8(rS);
+	put8(rP);
+	put8(nmi);
+	put16(pc);
+	put16(pput);
+	put16(ppuv);
+	put8(ppusx);
+	put16(ppux);
+	put16(ppuy);
+	put8(mirr);
+	put8(odd);
+	put8(vramlatch);
+	put8(keylatch);
+	put8(vrambuf);
+	put32(clock);
+	put32(ppuclock);
+	mapper[map](SAVE, 0);
+	close(fd);
+}