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);
+}