diff --git a/include/audio.h b/include/audio.h index 61f2e53..f4af419 100644 --- a/include/audio.h +++ b/include/audio.h @@ -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(); diff --git a/include/cart.h b/include/cart.h index 66df39c..bc5342c 100644 --- a/include/cart.h +++ b/include/cart.h @@ -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(); diff --git a/include/cpu.h b/include/cpu.h index 7c5a043..5c2b742 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -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(); diff --git a/include/dma.h b/include/dma.h index e64aa42..a929209 100644 --- a/include/dma.h +++ b/include/dma.h @@ -2,6 +2,20 @@ #include +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(); diff --git a/include/emu.h b/include/emu.h index 4720782..1d7c0da 100644 --- a/include/emu.h +++ b/include/emu.h @@ -14,4 +14,7 @@ typedef struct { int emu_run(int, char**); emu_context *emu_get_context(); -void emu_cycles(int cpu_cycles); \ No newline at end of file +void emu_cycles(int cpu_cycles); +void emu_reset(); +void emu_stop(); +void emu_start(); \ No newline at end of file diff --git a/include/gamepad.h b/include/gamepad.h index 4692c97..0ffeaac 100644 --- a/include/gamepad.h +++ b/include/gamepad.h @@ -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(); diff --git a/include/lcd.h b/include/lcd.h index 76a8030..c059229 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -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)) diff --git a/include/ppu.h b/include/ppu.h index c08b3d9..07166d0 100644 --- a/include/ppu.h +++ b/include/ppu.h @@ -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(); diff --git a/include/ram.h b/include/ram.h index 87ef08f..660e9e1 100644 --- a/include/ram.h +++ b/include/ram.h @@ -2,6 +2,18 @@ #include +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); diff --git a/include/state.h b/include/state.h new file mode 100644 index 0000000..b5e1401 --- /dev/null +++ b/include/state.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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*); \ No newline at end of file diff --git a/include/timer.h b/include/timer.h index 1005447..34640e7 100644 --- a/include/timer.h +++ b/include/timer.h @@ -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(); diff --git a/lib/audio.c b/lib/audio.c index 4f9ffbb..745bff4 100644 --- a/lib/audio.c +++ b/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); +} \ No newline at end of file diff --git a/lib/cart.c b/lib/cart.c index c50d476..c20dcbe 100644 --- a/lib/cart.c +++ b/lib/cart.c @@ -2,32 +2,6 @@ #include #include -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); + } + } } \ No newline at end of file diff --git a/lib/cpu.c b/lib/cpu.c index a62b2b5..188a6ad 100644 --- a/lib/cpu.c +++ b/lib/cpu.c @@ -5,6 +5,7 @@ #include #include #include +#include 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; } \ No newline at end of file diff --git a/lib/dma.c b/lib/dma.c index 77a15bf..169cae0 100644 --- a/lib/dma.c +++ b/lib/dma.c @@ -2,13 +2,6 @@ #include #include -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; } \ No newline at end of file diff --git a/lib/emu.c b/lib/emu.c index 73edba0..bf4d592 100644 --- a/lib/emu.c +++ b/lib/emu.c @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -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 \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)) { diff --git a/lib/gamepad.c b/lib/gamepad.c index f3bc5fe..da6b0fa 100644 --- a/lib/gamepad.c +++ b/lib/gamepad.c @@ -2,13 +2,6 @@ #include #include -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; } \ No newline at end of file diff --git a/lib/lcd.c b/lib/lcd.c index 64f64d3..a198315 100644 --- a/lib/lcd.c +++ b/lib/lcd.c @@ -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; } \ No newline at end of file diff --git a/lib/ppu.c b/lib/ppu.c index d1ae9f8..c2c72fe 100644 --- a/lib/ppu.c +++ b/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)); } \ No newline at end of file diff --git a/lib/ram.c b/lib/ram.c index 2cf815d..9bb08f1 100644 --- a/lib/ram.c +++ b/lib/ram.c @@ -1,10 +1,5 @@ #include -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; } \ No newline at end of file diff --git a/lib/state.c b/lib/state.c new file mode 100644 index 0000000..7adeed6 --- /dev/null +++ b/lib/state.c @@ -0,0 +1,27 @@ +#include + +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); +} \ No newline at end of file diff --git a/lib/timer.c b/lib/timer.c index 59edfbd..79d5d70 100644 --- a/lib/timer.c +++ b/lib/timer.c @@ -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; +} \ No newline at end of file diff --git a/lib/ui.c b/lib/ui.c index eeba5a1..5254870 100644 --- a/lib/ui.c +++ b/lib/ui.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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;