shithub: riscv

Download patch

ref: 040166493db3bd6f528cc08e2d0cc2c638c560a5
parent: 99e004c72e5a95a37a5060d273c3c1ce99bed6ec
author: aiju <devnull@localhost>
date: Sat Oct 18 15:08:38 EDT 2014

games/gba: add state saving

--- a/sys/src/games/gba/apu.c
+++ b/sys/src/games/gba/apu.c
@@ -50,6 +50,16 @@
 	}
 };
 
+Var apuvars[] = {
+	ARR(snddma), VAR(envctr), VAR(envrel), VAR(envmod), VAR(sweepen),
+	VAR(sweepctr), VAR(sweepfreq), ARR(wave), VAR(wpos), VAR(wbank), VAR(lfsr),
+	VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].fctr), VAR(sndch[0].fthr), VAR(sndch[0].finc), VAR(sndch[0].vol),
+	VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].fctr), VAR(sndch[1].fthr), VAR(sndch[1].finc), VAR(sndch[1].vol),
+	VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].fctr), VAR(sndch[2].fthr), VAR(sndch[2].finc), VAR(sndch[2].vol),
+	VAR(sndch[3].ectr), VAR(sndch[3].len), VAR(sndch[3].fctr), VAR(sndch[3].fthr), VAR(sndch[3].finc), VAR(sndch[3].vol),
+	{nil, 0, 0},
+};
+
 void
 rate(int i, u16int v)
 {
--- a/sys/src/games/gba/cpu.c
+++ b/sys/src/games/gba/cpu.c
@@ -36,6 +36,12 @@
 u32int instr0, instr1, pipel = -1;
 int cyc, trace;
 
+Var cpuvars[] = {
+	ARR(r), VAR(cpsr), VAR(spsr), ARR(saver), VAR(irq),
+	VAR(instr0), VAR(instr1), VAR(pipel),
+	{nil, 0, 0},
+};
+
 #define pipeflush() {io(); pipel = -1;}
 #define io() cyc++
 
@@ -1266,4 +1272,13 @@
 	setcpsr(0xd3);
 	r[15] = 0;
 	pipel = -1;
+}
+
+void
+cpuload(void)
+{
+	if((cpsr & FLAGT) != 0)
+		step = stepthumb;
+	else
+		step = steparm;
 }
--- a/sys/src/games/gba/dat.h
+++ b/sys/src/games/gba/dat.h
@@ -142,3 +142,13 @@
 	BACKTYPELEN = 64,
 	HZ = 16777216,
 };
+
+typedef struct Var Var;
+
+struct Var {
+	void *a;
+	int s, n;
+};
+#define VAR(a) {&a, sizeof(a), 1}
+#define ARR(a) {a, sizeof(*a), nelem(a)}
+enum { NEVENT = 6 };
--- a/sys/src/games/gba/ev.c
+++ b/sys/src/games/gba/ev.c
@@ -22,6 +22,19 @@
 Event *elist;
 Timer timers[4];
 Event evhblank;
+extern Event evsamp;
+Event *events[NEVENT] = {&timers[0].Event, &timers[1].Event, &timers[2].Event, &timers[3].Event, &evhblank, &evsamp};
+
+Var evvars[] = {
+	VAR(clock),
+	ARR(sndfifo[0].d), VAR(sndfifo[0].head), VAR(sndfifo[0].level), VAR(sndfifo[0].headpos),
+	ARR(sndfifo[1].d), VAR(sndfifo[1].head), VAR(sndfifo[1].level), VAR(sndfifo[1].headpos),
+	VAR(timers[0].val), VAR(timers[0].clock), VAR(timers[0].sh), VAR(timers[0].snd),
+	VAR(timers[1].val), VAR(timers[1].clock), VAR(timers[1].sh), VAR(timers[1].snd),
+	VAR(timers[2].val), VAR(timers[2].clock), VAR(timers[2].sh), VAR(timers[2].snd),
+	VAR(timers[3].val), VAR(timers[3].clock), VAR(timers[3].sh), VAR(timers[3].snd),
+	{nil, 0, 0},
+};
 
 void
 addevent(Event *ev, int time)
--- a/sys/src/games/gba/fns.h
+++ b/sys/src/games/gba/fns.h
@@ -23,3 +23,6 @@
 void audioinit(void);
 int audioout(void);
 void sndwrite(u16int, u16int);
+void loadstate(char *);
+void savestate(char *);
+void cpuload(void);
--- a/sys/src/games/gba/gba.c
+++ b/sys/src/games/gba/gba.c
@@ -16,6 +16,7 @@
 QLock pauselock;
 int savefd, saveframes;
 int clock;
+int savereq, loadreq;
 
 char *biosfile = "/sys/games/lib/gbabios.bin";
 
@@ -237,10 +238,10 @@
 		if(read(fd, buf, sizeof(buf) - 1) <= 0)
 			sysfatal("read /dev/kbd: %r");
 		if(buf[0] == 'c'){
-			/*if(utfrune(buf, KF|5))
+			if(utfrune(buf, KF|5))
 				savereq = 1;
 			if(utfrune(buf, KF|6))
-				loadreq = 1;*/
+				loadreq = 1;
 			if(utfrune(buf, Kdel)){
 				close(fd);
 				threadexitsall(nil);
@@ -424,6 +425,14 @@
 	memreset();
 	reset();
 	for(;;){
+		if(savereq){
+			savestate("gba.save");
+			savereq = 0;
+		}
+		if(loadreq){
+			loadstate("gba.save");
+			loadreq = 0;
+		}
 		if(paused){
 			qlock(&pauselock);
 			qunlock(&pauselock);
--- a/sys/src/games/gba/mem.c
+++ b/sys/src/games/gba/mem.c
@@ -20,6 +20,12 @@
 u8int waitst[16] = {5, 5, 5, 5, 3, 5, 5, 9, 8, 10, 10, 14};
 u32int eepstart;
 
+Var memvars[] = {
+	ARR(wram0), ARR(wram1), ARR(vram), ARR(pram), ARR(oam), ARR(reg),
+	VAR(dmaact), ARR(dmar), ARR(waitst),
+	{nil, 0, 0},
+};
+
 extern int cyc;
 
 static int eepromread(void);
--- a/sys/src/games/gba/mkfile
+++ b/sys/src/games/gba/mkfile
@@ -9,6 +9,7 @@
 	ppu.$O\
 	ev.$O\
 	apu.$O\
+	state.$O\
 
 HFILES=dat.h fns.h
 
--- a/sys/src/games/gba/ppu.c
+++ b/sys/src/games/gba/ppu.c
@@ -27,6 +27,13 @@
 };
 static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}};
 
+Var ppuvars[] = {
+	VAR(hblank), VAR(ppuy), VAR(hblclock),
+	VAR(bldy), VAR(blda), VAR(bldb), VAR(objalpha),
+	VAR(bgst[2].rpx0), VAR(bgst[2].rpy0), VAR(bgst[3].rpx0), VAR(bgst[3].rpy0),
+	{nil, 0, 0},
+};
+
 typedef struct sprite sprite;
 struct sprite {
 	uchar w, wb, h;
--- /dev/null
+++ b/sys/src/games/gba/state.c
@@ -1,0 +1,165 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+extern Var cpuvars[], ppuvars[], memvars[], apuvars[], evvars[];
+extern Event *events[NEVENT], *elist;
+
+static void
+putevents(Biobuf *bp)
+{
+	int i, j;
+	Event *e;
+	
+	for(i = 0; i < NEVENT; i++)
+		if(elist == events[i])
+			break;
+	if(i == NEVENT && elist != nil)
+		print("unknown event %p in chain\n", elist);
+	Bputc(bp, i);
+	for(i = 0; i < NEVENT; i++){
+		e = events[i];
+		Bputc(bp, e->time);
+		Bputc(bp, e->time >> 8);
+		Bputc(bp, e->time >> 16);
+		Bputc(bp, e->time >> 24);
+		for(j = 0; j < NEVENT; j++)
+			if(e->next == events[j])
+				break;
+		if(j == NEVENT && e->next != nil)
+			print("unknown event %p in chain\n", e->next);
+		Bputc(bp, j);
+	}
+		
+}
+
+static void
+getevents(Biobuf *bp)
+{
+	int i, j;
+	Event *e;
+	
+	i = Bgetc(bp);
+	elist = i >= NEVENT ? nil : events[i];
+	for(i = 0; i < NEVENT; i++){
+		e = events[i];
+		e->time = Bgetc(bp);
+		e->time |= Bgetc(bp) << 8;
+		e->time |= Bgetc(bp) << 16;
+		e->time |= Bgetc(bp) << 24;
+		j = Bgetc(bp);
+		e->next = j >= NEVENT ? nil : events[j];
+	}
+}
+
+static void
+getvars(Biobuf *bp, Var *v)
+{
+	int n;
+	u16int *p, w;
+	u32int *q, l;
+
+	for(; v->a != nil; v++)
+		switch(v->s){
+		case 1:
+			Bread(bp, v->a, v->n);
+			break;
+		case 2:
+			n = v->n;
+			p = v->a;
+			while(n--){
+				w = Bgetc(bp);
+				*p++ = w | Bgetc(bp) << 8;
+			}
+			break;
+		case 4:
+			n = v->n;
+			q = v->a;
+			while(n--){
+				l = Bgetc(bp);
+				l |= Bgetc(bp) << 8;
+				l |= Bgetc(bp) << 16;
+				*q++ = l | Bgetc(bp) << 24;
+			}
+			break;
+		}
+
+}
+
+static void
+putvars(Biobuf *bp, Var *v)
+{
+	int n;
+	u16int *p;
+	u32int *q;
+
+	for(; v->a != nil; v++)
+		switch(v->s){
+		case 1:
+			Bwrite(bp, v->a, v->n);
+			break;
+		case 2:
+			n = v->n;
+			p = v->a;
+			while(n--){
+				Bputc(bp, *p & 0xff);
+				Bputc(bp, *p++ >> 8);
+			}
+			break;
+		case 4:
+			n = v->n;
+			q = v->a;
+			while(n--){
+				Bputc(bp, *q);
+				Bputc(bp, *q >> 8);
+				Bputc(bp, *q >> 16);
+				Bputc(bp, *q++ >> 24);
+			}
+			break;
+		}
+}
+
+void
+savestate(char *file)
+{
+	Biobuf *bp;
+
+	flushback();
+	bp = Bopen(file, OWRITE);
+	if(bp == nil){
+		print("open: %r\n");
+		return;
+	}
+	putvars(bp, cpuvars);
+	putvars(bp, ppuvars);
+	putvars(bp, memvars);
+	putvars(bp, apuvars);
+	putvars(bp, evvars);
+	putevents(bp);
+	Bterm(bp);
+}
+
+void
+loadstate(char *file)
+{
+	Biobuf *bp;
+	extern u32int r[16];
+
+	bp = Bopen(file, OREAD);
+	if(bp == nil){
+		print("open: %r\n");
+		return;
+	}
+	getvars(bp, cpuvars);
+	getvars(bp, ppuvars);
+	getvars(bp, memvars);
+	getvars(bp, apuvars);
+	getvars(bp, evvars);
+	getevents(bp);
+	cpuload();
+	Bterm(bp);
+}