From 4661fc35bb187693af5482d9662bb5d2da32dc0f Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 17 Jul 2024 15:12:33 +0900 Subject: [PATCH 1/2] add load and save function --- .gitignore | 1 + cpu.c | 3 + ev.c | 3 + gb.c | 1 - gb.h | 22 +++++++ mem.c | 19 +++--- ppu.c | 3 + save.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 save.c diff --git a/.gitignore b/.gitignore index c6127b3..81af666 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.ko *.obj *.elf +.*.swp # Linker output *.ilk diff --git a/cpu.c b/cpu.c index 2b12e6d..0d44d93 100644 --- a/cpu.c +++ b/cpu.c @@ -30,6 +30,9 @@ u8 r[8], ime; u16 pc, sp, curpc; int halt; +Var cpuvars[] = {ARR(r), VAR(ime), VAR(pc), VAR(curpc), VAR(sp), VAR(halt), + {nil, 0, 0}}; + void state(void) { diff --git a/ev.c b/ev.c index fd68525..d711036 100644 --- a/ev.c +++ b/ev.c @@ -1,8 +1,11 @@ #include "gb.h" Event evhblank, evjoypad; +Event *events[NEVENT] = {&evhblank, &evjoypad, nil}; Event* elist; +Var evvars[] = {{nil, 0, 0}}; + void addevent(Event* ev, int time) { diff --git a/gb.c b/gb.c index fcf722e..d2c90b0 100644 --- a/gb.c +++ b/gb.c @@ -5,7 +5,6 @@ int cpuhalt; int backup; int savefd = -1; -u64 clock; u8 mbc, feat, mode; static void diff --git a/gb.h b/gb.h index b408d20..eedb964 100644 --- a/gb.h +++ b/gb.h @@ -1,5 +1,9 @@ #include +#define nelem(x) (sizeof(x)/sizeof(x[0])) + +#define VAR(a) {&a, sizeof(a), 1} +#define ARR(a) {a, sizeof(*a), nelem(a)} #define nil ((void*)0) #define JOYPAD_CYCLE (30) @@ -128,6 +132,8 @@ enum GB_KEY_START = 0x80 }; +enum { NEVENT = 2 + 1}; + typedef uint8_t u8; typedef uint16_t u16; typedef int8_t i8; @@ -136,6 +142,7 @@ typedef int32_t i32; typedef uint64_t u64; typedef int64_t i64; typedef struct Event Event; +typedef struct Var Var; struct Event { @@ -145,6 +152,15 @@ struct Event void* aux; }; +struct Var +{ + void *a; + int s, n; +}; + +#define VAR(a) {&a, sizeof(a), 1} +#define ARR(a) {a, sizeof(*a), nelem(a)} + extern u8 *rom, *back, reg[256], oam[256]; extern u8 vram[16384]; extern int nrom, nback, nbackbank; @@ -163,6 +179,7 @@ extern u32 moncols[4]; extern u32 white; extern u8* pic; extern int (*mapper)(int, int); +extern Event *events[NEVENT]; /* joypad */ void @@ -206,6 +223,11 @@ flush(); void initwindow(int scale); +/* save */ +void +putvars(Var *v); +void +getvars(Var *v); /* error */ void error(const char*, ...); diff --git a/mem.c b/mem.c index 3967f55..7701ee9 100644 --- a/mem.c +++ b/mem.c @@ -9,17 +9,18 @@ u8* rom; u8 *romb, *vramb, *wramb, *eramb; u8 wram[32768], vram[16384], oam[256], reg[256]; u8* back; -u8 palm[128]; u32 pal[64]; int nrom, nback, nbackbank; +u64 clock; u32 divclock; int prish; -u8 dma; u32 moncols[4]; u32 white; int (*mappers[])(int, int) = { mbc0, mbc1 }; int (*mapper)(int, int); +Var memvars[] = {ARR(wram), ARR(vram), ARR(oam), ARR(reg), VAR(clock), VAR(divclock), {nil, 0, 0}}; + static u8 regread(u16 a) { @@ -198,29 +199,29 @@ static int mbc1(int a, int v) { static u8 ramen, b0, b1, romram; + static Var mbc1vars[] = {VAR(ramen), VAR(b0), VAR(b1), VAR(romram), + {nil, 0,0}}; u16 b; if (a < 0) { switch(a) { case INIT: return 0; - case SAVE: - case RSTR: - break; - case READ: - return -1; + case SAVE: putvars(mbc1vars); break; + case RSTR: getvars(mbc1vars); break; + case READ: return -1; default: panic("MBC1 does not have function of %d", a); } } switch (a >> 13) { case 0: ramen = (v & 0xF) == 0xA; break; case 1: v &= 0x1F; b0 = v != 0 ? v : 1; break; - case 2: b1 = v & 3; b1 % nbackbank; break; + case 2: b1 = v & 3; b1 %= nbackbank; break; case 3: romram = v & 1; break; } b = b0; if (romram == 0) b |= b1 << 5; - b %= nrom >> 14; /* 32KB => 2bank */ + b %= nrom >> 14; romb = rom + (b << 14); if (ramen) { if (romram) diff --git a/ppu.c b/ppu.c index 87cf4b6..0791908 100644 --- a/ppu.c +++ b/ppu.c @@ -36,6 +36,9 @@ u64 hblclock, rendclock; static int cyc, ppux, ppux0; sprite spr[10], *sprm; +Var ppuvars[] = {VAR(ppustate), VAR(ppuy), VAR(hblclock), VAR(rendclock), + {nil, 0, 0}}; + void pputask(void* _) { diff --git a/save.c b/save.c new file mode 100644 index 0000000..12ff12e --- /dev/null +++ b/save.c @@ -0,0 +1,165 @@ +#include "gb.h" +#include + +extern Var cpuvars[], ppuvars[], memvars[], evvars[]; +static FILE *fp; + +void +putevents(void) +{ + u8 i, j; + Event *e; + + for(i = 0; i < NEVENT; ++i) + if(elist == events[i]) /* find head */ + break; + if(i == NEVENT && elist != nil) + error("Unkown event in chain [%p]", e->next); + fputc(i, fp); + for(i = 0; i < NEVENT; ++i){ + e = events[i]; + fputc(e->time & 0x000000ff, fp); + fputc(e->time & 0x0000ff00, fp); + fputc(e->time & 0x00ff0000, fp); + fputc(e->time & 0xff000000, fp); + for(j = 0; j < NEVENT; ++j) + if(e->next == events[j]) /* find next */ + break; + if(j == NEVENT && e->next != nil) + error("Unkown event in chain [%p]", e->next); + fputc(j, fp); + } +} + +void +putvars(Var *v) +{ + int n; + u16 *p; + u32 *q; + + for(; v->a != nil; v++){ + switch(v->s){ + case 1: + fwrite(v->a, 1, v->n, fp); + break; + case 2: + n = v->n; + p = v->a; + while(n--){ + fputc(*p & 0xff, fp); + fputc(*p++ >> 8, fp); + } + break; + case 4: + n = v->n; + q = v->a; + while(n--){ + fputc(*q, fp); + fputc(*q >> 8, fp); + fputc(*q >> 16, fp); + fputc(*q >> 24, fp); + } + break; + } + } +} + +void +getvars(Var *v) +{ + int n; + u16 *p, w; + u32 *q, l; + + for(; v->a != nil; v++){ + switch(v->s){ + case 1: fread(v->a, v->n, 1, fp); break; + case 2: + n = v->n; + p = v->a; + while(n--){ + w = fgetc(fp); + w |= fgetc(fp) << 8; + *p++ = w; + } + break; + case 4: + n = v->n; + q = v->a; + while(n--){ + l |= fgetc(fp); + l |= fgetc(fp) << 8; + l |= fgetc(fp) << 16; + l |= fgetc(fp) << 24; + *q++ = l; + } + break; + } + } +} + +int +savestate(const char *fname) +{ + fp = fopen(fname, "w"); + if(fp == 0){ + error("Can't open '%s'", fname); + return -1; + } + putvars(cpuvars); + putvars(ppuvars); + putvars(memvars); + putvars(evvars); + putevents(); + mapper(SAVE, 0); + fclose(fp); + fp = 0; + return 0; +} + +static void +getevents(void) +{ + int i, j; + Event *e; + + i = fgetc(fp); + if(i > NEVENT){ + error("Unkown event index [%d]", i); + elist = nil; + } + for(i = 0; i < NEVENT; ++i){ + e = events[i]; + e->time = fgetc(fp); + e->time |= fgetc(fp) << 8; + e->time |= fgetc(fp) << 16; + e->time |= fgetc(fp) << 24; + j = fgetc(fp); + if(j >= NEVENT){ + error("Unkown event index [%d]", j); + e->next = nil; + }else{ + e->next = events[j]; + } + } +} + +int +loadstate(const char *fname) +{ + fp = fopen(fname, "r"); + if(fp == 0){ + error("Can't load '%s'", fname); + return -1; + } + getvars(cpuvars); + getvars(ppuvars); + getvars(memvars); + getvars(evvars); + getevents(); + mapper(RSTR, 0); + fclose(fp); + fp = 0; + return 0; +} From bc76c36ed4d92e4894c53cfdfa63b842f18d09ce Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 18 Jul 2024 20:24:34 +0900 Subject: [PATCH 2/2] add loadsave loadsave: only for BAT features cartidge [load|save] state: for all cartidge. It's like snapshot --- .gitignore | 1 + eui.c | 10 +++----- ev.c | 2 +- gb.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++--- gb.h | 14 +++++++--- mem.c | 1 + save.c | 14 +++++----- 7 files changed, 95 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 81af666..779fd04 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.obj *.elf .*.swp +*.gb # Linker output *.ilk diff --git a/eui.c b/eui.c index 877b3e6..a2bb012 100644 --- a/eui.c +++ b/eui.c @@ -9,7 +9,7 @@ u8* pic; SDL_Renderer* renderer; SDL_Texture* bitmapTex; -static void +void render() { SDL_UpdateTexture(bitmapTex, nil, pic, 160 * sizeof(u32)); @@ -29,6 +29,8 @@ joypadevent(void*_) case SDL_KEYUP: keys = 0; break; case SDL_KEYDOWN: switch (evt.key.keysym.scancode) { + case SDL_SCANCODE_F1: savereq = 1; break; + case SDL_SCANCODE_F2: loadreq = 1; break; case SDL_SCANCODE_X: keys = GB_KEY_A; break; case SDL_SCANCODE_Z: keys = GB_KEY_B; break; case SDL_SCANCODE_UP: keys = GB_KEY_UP; break; @@ -45,12 +47,6 @@ joypadevent(void*_) return; } -void -flush() -{ - render(); -} - void initwindow(int scale) { diff --git a/ev.c b/ev.c index d711036..fc18341 100644 --- a/ev.c +++ b/ev.c @@ -1,7 +1,7 @@ #include "gb.h" Event evhblank, evjoypad; -Event *events[NEVENT] = {&evhblank, &evjoypad, nil}; +Event *events[NEVENT] = {&evhblank, &evjoypad}; Event* elist; Var evvars[] = {{nil, 0, 0}}; diff --git a/gb.c b/gb.c index d2c90b0..4eda52b 100644 --- a/gb.c +++ b/gb.c @@ -1,12 +1,56 @@ #include "gb.h" #include "co/task.h" #include +#include +#include +#include +int savereq, loadreq; int cpuhalt; int backup; -int savefd = -1; +FILE *savefp; +int saveframes; +const char *romname; u8 mbc, feat, mode; +void +writeback(void) +{ + if(saveframes == 0) + saveframes = 15; +} + +void +flushback(void) +{ + if(savefp != nil) + fwrite(back, 1, nback, savefp); + saveframes = 0; +} + +static void +loadsave(const char *file) +{ + u8 *buf, *p; + + buf = xalloc(strlen(file) + 4); + strcpy(buf, file); + p = strrchr(buf, '.'); + if(p == nil) + p = buf + strlen(buf); + strcpy(p, ".sav"); + savefp = fopen(buf, "w+"); + if(savefp == 0){ + error("Can't load save file '%s'", file); + free(buf); + return; + } + back = xalloc(nback); + fwrite(back, 1, nback, savefp); + free(buf); + atexit(flushback); +} + static void loadrom(const char* file) { @@ -47,7 +91,6 @@ loadrom(const char* file) default: panic("Unkown Ram size %d\n", rom[0x149]); } } - back = xalloc(nback); if(nback == 0) nbackbank = 1; else @@ -59,6 +102,32 @@ loadrom(const char* file) } if ((rom[0x143] & 0x80) != 0 && (mode & FORCEDMG) == 0) mode = CGB | COL; + if((feat & FEATBAT) != 0) + loadsave(file); +} + +void +flush() +{ + static char *savestatename; + if (savestatename == 0){ + int len = strlen(romname); + savestatename = xalloc(len + 4); + strncpy(savestatename, romname, len); + strcpy(savestatename + len, "-state.save"); + } + + render(); + if(saveframes > 0 && -- saveframes == 0) + flushback(); + if(savereq){ + savestate(savestatename); + savereq = 0; + } + if(loadreq){ + loadstate(savestatename); + loadreq = 0; + } } static void @@ -80,8 +149,8 @@ colinit(void) void taskmain(int argc, char* argv[]) { + loadrom(romname = argv[1]); colinit(); - loadrom(argv[1]); initwindow(5); initevent(); meminit(); diff --git a/gb.h b/gb.h index eedb964..bfef481 100644 --- a/gb.h +++ b/gb.h @@ -132,7 +132,7 @@ enum GB_KEY_START = 0x80 }; -enum { NEVENT = 2 + 1}; +enum { NEVENT = 2 }; typedef uint8_t u8; typedef uint16_t u16; @@ -180,6 +180,7 @@ extern u32 white; extern u8* pic; extern int (*mapper)(int, int); extern Event *events[NEVENT]; +extern int savereq, loadreq; /* joypad */ void @@ -219,15 +220,22 @@ reset(void); /* graphic */ void -flush(); -void initwindow(int scale); +void +render(); /* save */ void putvars(Var *v); void getvars(Var *v); +void +flush(); +int +savestate(const char *fname); +int +loadstate(const char *fname); + /* error */ void error(const char*, ...); diff --git a/mem.c b/mem.c index 7701ee9..a483482 100644 --- a/mem.c +++ b/mem.c @@ -160,6 +160,7 @@ memwrite(u16 a, u8 v) eramb[a - 0xa000] = v; else mapper(a, v); + writeback(); return; case 12: case 14: wram[a & 0xFFF] = v; diff --git a/save.c b/save.c index 12ff12e..293f23d 100644 --- a/save.c +++ b/save.c @@ -14,7 +14,7 @@ putevents(void) if(elist == events[i]) /* find head */ break; if(i == NEVENT && elist != nil) - error("Unkown event in chain [%p]", e->next); + error("Unkown event in chain [%p]", elist); fputc(i, fp); for(i = 0; i < NEVENT; ++i){ e = events[i]; @@ -125,10 +125,10 @@ getevents(void) Event *e; i = fgetc(fp); - if(i > NEVENT){ - error("Unkown event index [%d]", i); + if(i >= NEVENT) elist = nil; - } + else + elist = events[i]; for(i = 0; i < NEVENT; ++i){ e = events[i]; e->time = fgetc(fp); @@ -136,12 +136,10 @@ getevents(void) e->time |= fgetc(fp) << 16; e->time |= fgetc(fp) << 24; j = fgetc(fp); - if(j >= NEVENT){ - error("Unkown event index [%d]", j); + if(j >= NEVENT) e->next = nil; - }else{ + else e->next = events[j]; - } } }