Full savestating to memory
This commit is contained in:
@ -135,6 +135,13 @@ typedef struct {
|
||||
|
||||
} 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_tick();
|
||||
void audio_period_tick();
|
||||
|
@ -19,6 +19,46 @@ typedef struct {
|
||||
u16 global_checksum;
|
||||
} 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);
|
||||
|
||||
rom_header *get_rom_header();
|
||||
|
@ -35,6 +35,13 @@ typedef struct {
|
||||
u8 int_flags;
|
||||
} 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();
|
||||
bool cpu_step();
|
||||
|
||||
|
@ -2,6 +2,20 @@
|
||||
|
||||
#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_tick();
|
||||
|
||||
|
@ -14,4 +14,7 @@ typedef struct {
|
||||
int emu_run(int, char**);
|
||||
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();
|
@ -13,6 +13,20 @@ typedef struct {
|
||||
bool right;
|
||||
} 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();
|
||||
bool gamepad_button_sel();
|
||||
bool gamepad_dir_sel();
|
||||
|
@ -29,6 +29,13 @@ typedef enum {
|
||||
MODE_XFER
|
||||
} 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();
|
||||
|
||||
#define LCDC_BGW_ENABLE (BIT(lcd_get_context()->lcdc, 0))
|
||||
|
@ -86,6 +86,13 @@ typedef struct {
|
||||
bool debug;
|
||||
} 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_tick();
|
||||
|
||||
|
@ -2,6 +2,18 @@
|
||||
|
||||
#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);
|
||||
void wram_write(u16 address, u8 value);
|
||||
|
||||
|
25
include/state.h
Normal file
25
include/state.h
Normal 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*);
|
@ -9,6 +9,13 @@ typedef struct {
|
||||
u8 tac;
|
||||
} 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_tick();
|
||||
|
||||
|
12
lib/audio.c
12
lib/audio.c
@ -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);
|
||||
}
|
115
lib/cart.c
115
lib/cart.c
@ -2,32 +2,6 @@
|
||||
#include <common.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;
|
||||
|
||||
bool cart_need_save () {
|
||||
@ -174,7 +148,7 @@ const char *cart_lic_name() {
|
||||
|
||||
void cart_setup_bamking() {
|
||||
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) ||
|
||||
(ctx.header->ram_size == 3 && i < 4) ||
|
||||
@ -190,6 +164,46 @@ void cart_setup_bamking() {
|
||||
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) {
|
||||
snprintf(ctx.filename, sizeof(ctx.filename), "%s", cart);
|
||||
FILE *fp = fopen(cart, "r");
|
||||
@ -387,4 +401,51 @@ bool cart_battery_save(){
|
||||
|
||||
u8 cart_get_rom_bank() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include <interrupts.h>
|
||||
#include <dbg.h>
|
||||
#include <timer.h>
|
||||
#include <memory.h>
|
||||
|
||||
cpu_context ctx = {0};
|
||||
#define CPU_DEBUG 0
|
||||
@ -123,4 +124,12 @@ void cpu_request_interrupt(interrupt_type t) {
|
||||
|
||||
cpu_context *cpu_get_context() {
|
||||
return &ctx;
|
||||
}
|
||||
|
||||
void cpu_save_state(cpu_state* state) {
|
||||
state->ctx = ctx;
|
||||
}
|
||||
|
||||
void cpu_load_state(const cpu_state* state) {
|
||||
ctx = state->ctx;
|
||||
}
|
15
lib/dma.c
15
lib/dma.c
@ -2,13 +2,6 @@
|
||||
#include <ppu.h>
|
||||
#include <bus.h>
|
||||
|
||||
typedef struct {
|
||||
bool active;
|
||||
u8 byte;
|
||||
u8 value;
|
||||
u8 start_delay;
|
||||
} dma_context;
|
||||
|
||||
static dma_context ctx;
|
||||
|
||||
void dma_start(u8 start) {
|
||||
@ -37,4 +30,12 @@ void dma_tick() {
|
||||
|
||||
bool dma_transferring() {
|
||||
return ctx.active;
|
||||
}
|
||||
|
||||
void dma_save_state(dma_state* state) {
|
||||
state->ctx = ctx;
|
||||
}
|
||||
|
||||
void dma_load_state(const dma_state* state) {
|
||||
ctx = state->ctx;
|
||||
}
|
33
lib/emu.c
33
lib/emu.c
@ -7,6 +7,7 @@
|
||||
#include <dma.h>
|
||||
#include <ppu.h>
|
||||
#include <audio.h>
|
||||
#include <cart.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@ -16,6 +17,7 @@
|
||||
#endif
|
||||
|
||||
static emu_context ctx;
|
||||
static HANDLE thread;
|
||||
|
||||
emu_context *emu_get_context() {
|
||||
return &ctx;
|
||||
@ -26,10 +28,6 @@ DWORD WINAPI cpu_run(void *p) {
|
||||
#else
|
||||
void *cpu_run(void *p) {
|
||||
#endif
|
||||
ppu_init();
|
||||
timer_init();
|
||||
cpu_init();
|
||||
audio_init();
|
||||
|
||||
ctx.running = true;
|
||||
ctx.paused = false;
|
||||
@ -59,6 +57,26 @@ void sleep_ms(int milis) {
|
||||
#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) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: gbemu <rom_file>\n");
|
||||
@ -75,11 +93,8 @@ int emu_run(int argc, char **argv) {
|
||||
|
||||
ui_init();
|
||||
#ifdef _WIN32
|
||||
HANDLE thread = CreateThread(NULL, 0, cpu_run, NULL, 0, NULL);
|
||||
if(!thread) {
|
||||
fprintf(stderr, "Unable to create main CPU thread!\n");
|
||||
return -1;
|
||||
}
|
||||
emu_reset();
|
||||
emu_start();
|
||||
#else
|
||||
pthread_t t1;
|
||||
if(pthread_create(&t1, NULL, cpu_run, NULL)) {
|
||||
|
@ -2,13 +2,6 @@
|
||||
#include <string.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};
|
||||
|
||||
void gamepad_init();
|
||||
@ -79,4 +72,12 @@ u8 gamepad_get_output() {
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void gamepad_save_state(ctlr_state* state){
|
||||
state->ctx = ctx;
|
||||
}
|
||||
|
||||
void gamepad_load_state(const ctlr_state* state){
|
||||
ctx = state->ctx;
|
||||
}
|
@ -78,4 +78,12 @@ void lcd_write(u16 address, u8 value) {
|
||||
} else if(offset == 9) {
|
||||
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;
|
||||
}
|
15
lib/ppu.c
15
lib/ppu.c
@ -84,4 +84,19 @@ void ppu_vram_write(u16 address, u8 value){
|
||||
|
||||
u8 ppu_vram_read(u16 address){
|
||||
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));
|
||||
}
|
13
lib/ram.c
13
lib/ram.c
@ -1,10 +1,5 @@
|
||||
#include <ram.h>
|
||||
|
||||
typedef struct {
|
||||
u8 wram[0x2000];
|
||||
u8 hram[0x80];
|
||||
} ram_context;
|
||||
|
||||
static ram_context ctx;
|
||||
|
||||
u8 wram_read(u16 address) {
|
||||
@ -27,4 +22,12 @@ u8 hram_read(u16 address){
|
||||
void hram_write(u16 address, u8 value){
|
||||
address -= 0xFF80;
|
||||
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
27
lib/state.c
Normal 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);
|
||||
}
|
@ -97,3 +97,11 @@ u8 timer_read(u16 address) {
|
||||
timer_context *timer_get_context() {
|
||||
return &ctx;
|
||||
}
|
||||
|
||||
void timer_save_state(timer_state* state) {
|
||||
state->ctx = ctx;
|
||||
}
|
||||
|
||||
void timer_load_state(const timer_state* state) {
|
||||
ctx = state->ctx;
|
||||
}
|
19
lib/ui.c
19
lib/ui.c
@ -8,6 +8,7 @@
|
||||
#include <lcd.h>
|
||||
#include <cpu.h>
|
||||
#include <cart.h>
|
||||
#include <state.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_ttf.h>
|
||||
@ -24,10 +25,12 @@ SDL_Renderer *sdlDebugRenderer;
|
||||
SDL_Texture *sdlDebugTexture;
|
||||
SDL_Surface *debugScreen;
|
||||
TTF_Font *sans;
|
||||
save_state *state;
|
||||
|
||||
static int scale = 4;
|
||||
|
||||
void ui_init(){
|
||||
state = malloc(sizeof(*state));
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
printf("SDL INIT\n");
|
||||
TTF_Init();
|
||||
@ -334,6 +337,22 @@ void ui_on_key(bool down, u32 key_code) {
|
||||
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){
|
||||
case SDLK_z: gamepad_get_state()->b = down; break;
|
||||
case SDLK_x: gamepad_get_state()->a = down; break;
|
||||
|
Reference in New Issue
Block a user