Full savestating to memory

This commit is contained in:
2025-05-23 15:49:26 -06:00
parent e9d5b359d0
commit b86d550812
23 changed files with 378 additions and 56 deletions

View File

@ -135,6 +135,13 @@ typedef struct {
} audio_context; } audio_context;
typedef struct {
audio_context ctx;
} audio_state;
void audio_save_state(audio_state*);
void audio_load_state(const audio_state*);
void audio_init(); void audio_init();
void audio_tick(); void audio_tick();
void audio_period_tick(); void audio_period_tick();

View File

@ -19,6 +19,46 @@ typedef struct {
u16 global_checksum; u16 global_checksum;
} rom_header; } rom_header;
typedef struct {
char filename[1024];
u32 rom_size;
u8 *rom_data;
rom_header *header;
//mbc1 data
bool ram_enabled;
bool ram_banking;
u8 *rom_bank_x;
u8 *rom_bank_x2;
u8 banking_mode;
u8 rom_bank_value;
u8 rom_bank_value_2;
u8 ram_bank_value;
u8 *ram_bank;
u8 *ram_banks[16];
//battery
bool battery;
bool need_save;
} cart_context;
typedef struct {
bool ram_enabled;
bool ram_banking;
u8 banking_mode;
u8 rom_bank_value;
u8 rom_bank_value_2;
u8 ram_bank_value;
u8 ram_banks[16][0x2000];
bool need_save;
} cart_state;
void cart_load_state(const cart_state*);
void cart_save_state(cart_state*);
bool cart_load(char *cart); bool cart_load(char *cart);
rom_header *get_rom_header(); rom_header *get_rom_header();

View File

@ -35,6 +35,13 @@ typedef struct {
u8 int_flags; u8 int_flags;
} cpu_context; } cpu_context;
typedef struct {
cpu_context ctx;
} cpu_state;
void cpu_save_state(cpu_state*);
void cpu_load_state(const cpu_state*);
void cpu_init(); void cpu_init();
bool cpu_step(); bool cpu_step();

View File

@ -2,6 +2,20 @@
#include <common.h> #include <common.h>
typedef struct {
bool active;
u8 byte;
u8 value;
u8 start_delay;
} dma_context;
typedef struct {
dma_context ctx;
} dma_state;
void dma_save_state(dma_state*);
void dma_load_state(const dma_state*);
void dma_start(u8 start); void dma_start(u8 start);
void dma_tick(); void dma_tick();

View File

@ -14,4 +14,7 @@ typedef struct {
int emu_run(int, char**); int emu_run(int, char**);
emu_context *emu_get_context(); emu_context *emu_get_context();
void emu_cycles(int cpu_cycles); void emu_cycles(int cpu_cycles);
void emu_reset();
void emu_stop();
void emu_start();

View File

@ -13,6 +13,20 @@ typedef struct {
bool right; bool right;
} gamepad_state; } gamepad_state;
typedef struct {
bool button_sel;
bool dir_sel;
gamepad_state controller;
gamepad_state prev;
} gamepad_context;
typedef struct {
gamepad_context ctx;
} ctlr_state;
void gamepad_save_state(ctlr_state*);
void gamepad_load_state(const ctlr_state*);
void gamepad_init(); void gamepad_init();
bool gamepad_button_sel(); bool gamepad_button_sel();
bool gamepad_dir_sel(); bool gamepad_dir_sel();

View File

@ -29,6 +29,13 @@ typedef enum {
MODE_XFER MODE_XFER
} lcd_mode; } lcd_mode;
typedef struct {
lcd_context ctx;
} lcd_state;
void lcd_save_state(lcd_state*);
void lcd_load_state(const lcd_state*);
lcd_context *lcd_get_context(); lcd_context *lcd_get_context();
#define LCDC_BGW_ENABLE (BIT(lcd_get_context()->lcdc, 0)) #define LCDC_BGW_ENABLE (BIT(lcd_get_context()->lcdc, 0))

View File

@ -86,6 +86,13 @@ typedef struct {
bool debug; bool debug;
} ppu_context; } ppu_context;
typedef struct {
ppu_context ctx;
} ppu_state;
void ppu_save_state(ppu_state*);
void ppu_load_state(const ppu_state*);
void ppu_init(); void ppu_init();
void ppu_tick(); void ppu_tick();

View File

@ -2,6 +2,18 @@
#include <common.h> #include <common.h>
typedef struct {
u8 wram[0x2000];
u8 hram[0x80];
} ram_context;
typedef struct {
ram_context ctx;
} ram_state;
void ram_save_state(ram_state*);
void ram_load_state(const ram_state*);
u8 wram_read(u16 address); u8 wram_read(u16 address);
void wram_write(u16 address, u8 value); void wram_write(u16 address, u8 value);

25
include/state.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <audio.h>
#include <cpu.h>
#include <ppu.h>
#include <cart.h>
#include <dma.h>
#include <gamepad.h>
#include <ram.h>
#include <timer.h>
#include <lcd.h>
typedef struct {
audio_state audio;
ppu_state ppu;
cpu_state cpu;
cart_state cart;
dma_state dma;
ctlr_state ctlr;
ram_state ram;
timer_state timer;
lcd_state lcd;
} save_state;
void state_save(save_state*);
void state_load(const save_state*);

View File

@ -9,6 +9,13 @@ typedef struct {
u8 tac; u8 tac;
} timer_context; } timer_context;
typedef struct {
timer_context ctx;
} timer_state;
void timer_save_state(timer_state*);
void timer_load_state(const timer_state*);
void timer_init(); void timer_init();
void timer_tick(); void timer_tick();

View File

@ -992,3 +992,15 @@ void audio_write(u16 address, u8 value){
} }
} }
} }
void audio_save_state(audio_state* state) {
state->ctx = ctx;
state->ctx.left_audio_buffer = 0;
state->ctx.right_audio_buffer = 0;
}
void audio_load_state(const audio_state* state) {
ctx = state->ctx;
ctx.left_audio_buffer = malloc(sizeof(float) * FRAMES_PER_BUFFER);
ctx.right_audio_buffer = malloc(sizeof(float) * FRAMES_PER_BUFFER);
}

View File

@ -2,32 +2,6 @@
#include <common.h> #include <common.h>
#include <string.h> #include <string.h>
typedef struct {
char filename[1024];
u32 rom_size;
u8 *rom_data;
rom_header *header;
//mbc1 data
bool ram_enabled;
bool ram_banking;
u8 *rom_bank_x;
u8 *rom_bank_x2;
u8 banking_mode;
u8 rom_bank_value;
u8 rom_bank_value_2;
u8 ram_bank_value;
u8 *ram_bank;
u8 *ram_banks[16];
//battery
bool battery;
bool need_save;
} cart_context;
static cart_context ctx; static cart_context ctx;
bool cart_need_save () { bool cart_need_save () {
@ -174,7 +148,7 @@ const char *cart_lic_name() {
void cart_setup_bamking() { void cart_setup_bamking() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
ctx.ram_banks[1] = 0; ctx.ram_banks[i] = 0;
if((ctx.header->ram_size == 2 && i == 0) || if((ctx.header->ram_size == 2 && i == 0) ||
(ctx.header->ram_size == 3 && i < 4) || (ctx.header->ram_size == 3 && i < 4) ||
@ -190,6 +164,46 @@ void cart_setup_bamking() {
ctx.rom_bank_x2 = ctx.rom_data; ctx.rom_bank_x2 = ctx.rom_data;
} }
bool cart_init() {
char filename[1024];
u32 rom_size;
u8 *rom_data;
rom_header *header;
//mbc1 data
bool ram_enabled;
bool ram_banking;
u8 *rom_bank_x;
u8 *rom_bank_x2;
u8 banking_mode;
u8 rom_bank_value;
u8 rom_bank_value_2;
u8 ram_bank_value;
u8 *ram_bank;
u8 *ram_banks[16];
//battery
bool battery;
bool need_save;
ctx.ram_enabled = 0;
ctx.ram_banking = 0;
ctx.rom_bank_x = ctx.rom_data;
ctx.rom_bank_x2 = ctx.rom_data;
ctx.banking_mode = 0;
ctx.rom_bank_value = 1;
ctx.rom_bank_value_2 = 0;
ctx.ram_bank_value = 0;
cart_setup_bamking();
ctx.need_save = 0;
if(ctx.battery) {
cart_battery_load();
}
}
bool cart_load(char *cart) { bool cart_load(char *cart) {
snprintf(ctx.filename, sizeof(ctx.filename), "%s", cart); snprintf(ctx.filename, sizeof(ctx.filename), "%s", cart);
FILE *fp = fopen(cart, "r"); FILE *fp = fopen(cart, "r");
@ -387,4 +401,51 @@ bool cart_battery_save(){
u8 cart_get_rom_bank() { u8 cart_get_rom_bank() {
return ctx.rom_bank_value; return ctx.rom_bank_value;
}
void cart_load_state(const cart_state* state){
ctx.ram_enabled = state->ram_enabled;
ctx.ram_banking = state->ram_banking;
ctx.banking_mode = state->banking_mode;
ctx.rom_bank_value = state->rom_bank_value;
ctx.rom_bank_value_2 = state->rom_bank_value_2;
ctx.ram_bank_value = state->ram_bank_value;
ctx.need_save = state->need_save;
for(int i = 0; i < 16; i++) {
if(ctx.ram_banks[i]){
memcpy(ctx.ram_banks[i], &state->ram_banks[i], 0x2000);
}
}
if(ctx.banking_mode) {
ctx.rom_bank_x = ctx.rom_data + (0x4000 * (ctx.rom_bank_value+ctx.rom_bank_value_2));
ctx.rom_bank_x2 = ctx.rom_data + (0x4000 * (ctx.rom_bank_value_2));
} else {
ctx.rom_bank_x = ctx.rom_data + (0x4000 * (ctx.rom_bank_value));
ctx.rom_bank_x2 = ctx.rom_data;
}
if(ctx.ram_banking) {
ctx.ram_bank = ctx.ram_banks[ctx.ram_bank_value];
} else {
ctx.ram_bank = ctx.ram_banks[0];
}
}
void cart_save_state(cart_state* state){
state->ram_enabled = ctx.ram_enabled;
state->ram_banking = ctx.ram_banking;
state->banking_mode = ctx.banking_mode;
state->rom_bank_value = ctx.rom_bank_value;
state->rom_bank_value_2 = ctx.rom_bank_value_2;
state->ram_bank_value = ctx.ram_bank_value;
state->need_save = ctx.need_save;
for (int i=0; i<16; i++) {
if(ctx.ram_banks[i]) {
memcpy((&state->ram_banks[i]), ctx.ram_banks[i], 0x2000);
}
}
} }

View File

@ -5,6 +5,7 @@
#include <interrupts.h> #include <interrupts.h>
#include <dbg.h> #include <dbg.h>
#include <timer.h> #include <timer.h>
#include <memory.h>
cpu_context ctx = {0}; cpu_context ctx = {0};
#define CPU_DEBUG 0 #define CPU_DEBUG 0
@ -123,4 +124,12 @@ void cpu_request_interrupt(interrupt_type t) {
cpu_context *cpu_get_context() { cpu_context *cpu_get_context() {
return &ctx; return &ctx;
}
void cpu_save_state(cpu_state* state) {
state->ctx = ctx;
}
void cpu_load_state(const cpu_state* state) {
ctx = state->ctx;
} }

View File

@ -2,13 +2,6 @@
#include <ppu.h> #include <ppu.h>
#include <bus.h> #include <bus.h>
typedef struct {
bool active;
u8 byte;
u8 value;
u8 start_delay;
} dma_context;
static dma_context ctx; static dma_context ctx;
void dma_start(u8 start) { void dma_start(u8 start) {
@ -37,4 +30,12 @@ void dma_tick() {
bool dma_transferring() { bool dma_transferring() {
return ctx.active; return ctx.active;
}
void dma_save_state(dma_state* state) {
state->ctx = ctx;
}
void dma_load_state(const dma_state* state) {
ctx = state->ctx;
} }

View File

@ -7,6 +7,7 @@
#include <dma.h> #include <dma.h>
#include <ppu.h> #include <ppu.h>
#include <audio.h> #include <audio.h>
#include <cart.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@ -16,6 +17,7 @@
#endif #endif
static emu_context ctx; static emu_context ctx;
static HANDLE thread;
emu_context *emu_get_context() { emu_context *emu_get_context() {
return &ctx; return &ctx;
@ -26,10 +28,6 @@ DWORD WINAPI cpu_run(void *p) {
#else #else
void *cpu_run(void *p) { void *cpu_run(void *p) {
#endif #endif
ppu_init();
timer_init();
cpu_init();
audio_init();
ctx.running = true; ctx.running = true;
ctx.paused = false; ctx.paused = false;
@ -59,6 +57,26 @@ void sleep_ms(int milis) {
#endif #endif
} }
void emu_stop() {
ctx.running = false;
WaitForSingleObject(thread, INFINITE);
}
void emu_reset() {
ppu_init();
timer_init();
cpu_init();
audio_init();
cart_init();
}
void emu_start() {
thread = CreateThread(NULL, 0, cpu_run, NULL, 0, NULL);
if(!thread) {
fprintf(stderr, "Unable to create main CPU thread!\n");
}
}
int emu_run(int argc, char **argv) { int emu_run(int argc, char **argv) {
if (argc < 2) { if (argc < 2) {
printf("Usage: gbemu <rom_file>\n"); printf("Usage: gbemu <rom_file>\n");
@ -75,11 +93,8 @@ int emu_run(int argc, char **argv) {
ui_init(); ui_init();
#ifdef _WIN32 #ifdef _WIN32
HANDLE thread = CreateThread(NULL, 0, cpu_run, NULL, 0, NULL); emu_reset();
if(!thread) { emu_start();
fprintf(stderr, "Unable to create main CPU thread!\n");
return -1;
}
#else #else
pthread_t t1; pthread_t t1;
if(pthread_create(&t1, NULL, cpu_run, NULL)) { if(pthread_create(&t1, NULL, cpu_run, NULL)) {

View File

@ -2,13 +2,6 @@
#include <string.h> #include <string.h>
#include <interrupts.h> #include <interrupts.h>
typedef struct {
bool button_sel;
bool dir_sel;
gamepad_state controller;
gamepad_state prev;
} gamepad_context;
static gamepad_context ctx = {0}; static gamepad_context ctx = {0};
void gamepad_init(); void gamepad_init();
@ -79,4 +72,12 @@ u8 gamepad_get_output() {
} }
return output; return output;
}
void gamepad_save_state(ctlr_state* state){
state->ctx = ctx;
}
void gamepad_load_state(const ctlr_state* state){
ctx = state->ctx;
} }

View File

@ -78,4 +78,12 @@ void lcd_write(u16 address, u8 value) {
} else if(offset == 9) { } else if(offset == 9) {
update_palette(value & 0b11111100, 2); update_palette(value & 0b11111100, 2);
} }
}
void lcd_save_state(lcd_state* state){
state->ctx = ctx;
}
void lcd_load_state(const lcd_state* state) {
ctx = state->ctx;
} }

View File

@ -84,4 +84,19 @@ void ppu_vram_write(u16 address, u8 value){
u8 ppu_vram_read(u16 address){ u8 ppu_vram_read(u16 address){
return ctx.vram[address - 0x8000]; return ctx.vram[address - 0x8000];
}
void ppu_save_state(ppu_state* state) {
state->ctx = ctx;
state->ctx.line_sprites = NULL;
state->ctx.line_sprite_count = 0;
state->ctx.pfc.pixel_fifo.size = 0;
state->ctx.pfc.pixel_fifo.head = 0;
state->ctx.pfc.pixel_fifo.tail = 0;
}
void ppu_load_state(const ppu_state* state) {
free(ctx.video_buffer);
ctx = state->ctx;
ctx.video_buffer = malloc(YRES * XRES * sizeof(u32));
} }

View File

@ -1,10 +1,5 @@
#include <ram.h> #include <ram.h>
typedef struct {
u8 wram[0x2000];
u8 hram[0x80];
} ram_context;
static ram_context ctx; static ram_context ctx;
u8 wram_read(u16 address) { u8 wram_read(u16 address) {
@ -27,4 +22,12 @@ u8 hram_read(u16 address){
void hram_write(u16 address, u8 value){ void hram_write(u16 address, u8 value){
address -= 0xFF80; address -= 0xFF80;
ctx.hram[address] = value; ctx.hram[address] = value;
}
void ram_save_state(ram_state* state) {
state->ctx = ctx;
}
void ram_load_state(const ram_state* state) {
ctx = state->ctx;
} }

27
lib/state.c Normal file
View File

@ -0,0 +1,27 @@
#include <state.h>
void state_save(save_state* state) {
printf("Saving state\n");
cpu_save_state(&state->cpu);
ram_save_state(&state->ram);
ppu_save_state(&state->ppu);
lcd_save_state(&state->lcd);
dma_save_state(&state->dma);
timer_save_state(&state->timer);
audio_save_state(&state->audio);
gamepad_save_state(&state->ctlr);
cart_save_state(&state->cart);
}
void state_load(const save_state* state) {
printf("Loading state\n");
cpu_load_state(&state->cpu);
ram_load_state(&state->ram);
ppu_load_state(&state->ppu);
lcd_load_state(&state->lcd);
dma_load_state(&state->dma);
timer_load_state(&state->timer);
audio_load_state(&state->audio);
gamepad_load_state(&state->ctlr);
cart_load_state(&state->cart);
}

View File

@ -97,3 +97,11 @@ u8 timer_read(u16 address) {
timer_context *timer_get_context() { timer_context *timer_get_context() {
return &ctx; return &ctx;
} }
void timer_save_state(timer_state* state) {
state->ctx = ctx;
}
void timer_load_state(const timer_state* state) {
ctx = state->ctx;
}

View File

@ -8,6 +8,7 @@
#include <lcd.h> #include <lcd.h>
#include <cpu.h> #include <cpu.h>
#include <cart.h> #include <cart.h>
#include <state.h>
#include <SDL.h> #include <SDL.h>
#include <SDL_ttf.h> #include <SDL_ttf.h>
@ -24,10 +25,12 @@ SDL_Renderer *sdlDebugRenderer;
SDL_Texture *sdlDebugTexture; SDL_Texture *sdlDebugTexture;
SDL_Surface *debugScreen; SDL_Surface *debugScreen;
TTF_Font *sans; TTF_Font *sans;
save_state *state;
static int scale = 4; static int scale = 4;
void ui_init(){ void ui_init(){
state = malloc(sizeof(*state));
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
printf("SDL INIT\n"); printf("SDL INIT\n");
TTF_Init(); TTF_Init();
@ -334,6 +337,22 @@ void ui_on_key(bool down, u32 key_code) {
printf("PPU Debug Disabled\n"); printf("PPU Debug Disabled\n");
} }
} }
if(key_code == SDLK_r && down == true) {
emu_stop();
emu_reset();
emu_start();
}
if(key_code == SDLK_s && down == true) {
emu_stop();
state_save(state);
emu_start();
}
if(key_code == SDLK_l && down == true) {
emu_stop();
emu_reset();
state_load(state);
emu_start();
}
switch(key_code){ switch(key_code){
case SDLK_z: gamepad_get_state()->b = down; break; case SDLK_z: gamepad_get_state()->b = down; break;
case SDLK_x: gamepad_get_state()->a = down; break; case SDLK_x: gamepad_get_state()->a = down; break;