summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2017-08-16 20:06:28 -0700
committerMichael Pavone <pavone@retrodev.com>2017-08-16 20:06:28 -0700
commit120310c083d538ec8770651ebd017b928524c10a (patch)
treea7b5154d91f1961e5d9cfc7b5a1ef2ef9d224cb0
parent6393973204570eec25522b681749c843fb283ff0 (diff)
Added save states to SMS emulation
-rw-r--r--genesis.c36
-rw-r--r--menu.c5
-rw-r--r--serialize.h6
-rw-r--r--sms.c185
-rw-r--r--system.h2
-rw-r--r--z80_to_x86.c1
6 files changed, 222 insertions, 13 deletions
diff --git a/genesis.c b/genesis.c
index c35d459..a8e3374 100644
--- a/genesis.c
+++ b/genesis.c
@@ -1003,6 +1003,41 @@ static void handle_reset_requests(genesis_context *gen)
vdp_release_framebuffer(gen->vdp);
}
+#include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up
+static uint8_t load_state(system_header *system, uint8_t slot)
+{
+ genesis_context *gen = (genesis_context *)system;
+ char numslotname[] = "slot_0.state";
+ char *slotname;
+ if (slot == QUICK_SAVE_SLOT) {
+ slotname = "quicksave.state";
+ } else {
+ numslotname[5] = '0' + slot;
+ slotname = numslotname;
+ }
+ char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname};
+ char *statepath = alloc_concat_m(3, parts);
+ deserialize_buffer state;
+ uint32_t pc = 0;
+ uint8_t ret;
+ if (load_from_file(&state, statepath)) {
+ genesis_deserialize(&state, gen);
+ free(state.data);
+ //HACK
+ pc = gen->m68k->last_prefetch_address;
+ ret = 1;
+ } else {
+ strcpy(statepath + strlen(statepath)-strlen("state"), "gst");
+ pc = load_gst(gen, statepath);
+ ret = pc != 0;
+ }
+ if (ret) {
+ gen->m68k->resume_pc = get_native_address_trans(gen->m68k, pc);
+ }
+ free(statepath);
+ return ret;
+}
+
static void start_genesis(system_header *system, char *statefile)
{
genesis_context *gen = (genesis_context *)system;
@@ -1146,6 +1181,7 @@ genesis_context *alloc_init_genesis(rom_info *rom, void *main_rom, void *lock_on
gen->header.resume_context = resume_genesis;
gen->header.load_save = load_save;
gen->header.persist_save = persist_save;
+ gen->header.load_state = load_state;
gen->header.soft_reset = soft_reset;
gen->header.free_context = free_genesis;
gen->header.get_open_bus_value = get_open_bus_value;
diff --git a/menu.c b/menu.c
index c8ccc43..cd1d58a 100644
--- a/menu.c
+++ b/menu.c
@@ -420,6 +420,9 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
case 6:
//load state
if (gen->header.next_context && gen->header.next_context->save_dir) {
+ if (!gen->header.next_context->load_state(gen->header.next_context, dst)) {
+ break;
+ }/*
char numslotname[] = "slot_0.state";
char *slotname;
if (dst == QUICK_SAVE_SLOT) {
@@ -430,6 +433,7 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
}
char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname};
char *statepath = alloc_concat_m(3, parts);
+ gen->header.next_context->load_state
genesis_context *next = (genesis_context *)gen->header.next_context;
deserialize_buffer state;
uint32_t pc = 0;
@@ -447,6 +451,7 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
break;
}
next->m68k->resume_pc = get_native_address_trans(next->m68k, pc);
+ */
}
m68k->should_return = 1;
break;
diff --git a/serialize.h b/serialize.h
index df57075..add9194 100644
--- a/serialize.h
+++ b/serialize.h
@@ -40,9 +40,9 @@ enum {
SECTION_SEGA_IO_EXT,
SECTION_MAIN_RAM,
SECTION_SOUND_RAM,
- SECTION_SEGA_MAPPER,
- SECTION_SMS_MAPPER,
- SECTION_EEPROM
+ SECTION_MAPPER,
+ SECTION_EEPROM,
+ SECTION_CART_RAM
};
void init_serialize(serialize_buffer *buf);
diff --git a/sms.c b/sms.c
index 9d1ac49..45b950c 100644
--- a/sms.c
+++ b/sms.c
@@ -109,14 +109,10 @@ static uint8_t io_read(uint32_t location, void *vcontext)
return 0xFF;
}
-static void *mapper_write(uint32_t location, void *vcontext, uint8_t value)
+static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value)
{
- z80_context *z80 = vcontext;
- sms_context *sms = z80->system;
+ z80_context *z80 = sms->z80;
void *old_value;
- location &= 3;
- sms->ram[0x1FFC + location] = value;
- sms->bank_regs[location] = value;
if (location) {
uint32_t idx = location - 1;
old_value = z80->mem_pointers[idx];
@@ -139,6 +135,16 @@ static void *mapper_write(uint32_t location, void *vcontext, uint8_t value)
z80_invalidate_code_range(z80, 0x8000, 0xC000);
}
}
+}
+
+static void *mapper_write(uint32_t location, void *vcontext, uint8_t value)
+{
+ z80_context *z80 = vcontext;
+ sms_context *sms = z80->system;
+ location &= 3;
+ sms->ram[0x1FFC + location] = value;
+ sms->bank_regs[location] = value;
+ update_mem_map(location, sms, value);
return vcontext;
}
@@ -189,6 +195,150 @@ static void set_speed_percent(system_header * system, uint32_t percent)
psg_adjust_master_clock(context->psg, context->master_clock);
}
+void sms_serialize(sms_context *sms, serialize_buffer *buf)
+{
+ start_section(buf, SECTION_Z80);
+ z80_serialize(sms->z80, buf);
+ end_section(buf);
+
+ start_section(buf, SECTION_VDP);
+ vdp_serialize(sms->vdp, buf);
+ end_section(buf);
+
+ start_section(buf, SECTION_PSG);
+ psg_serialize(sms->psg, buf);
+ end_section(buf);
+
+ start_section(buf, SECTION_SEGA_IO_1);
+ io_serialize(sms->io.ports, buf);
+ end_section(buf);
+
+ start_section(buf, SECTION_SEGA_IO_2);
+ io_serialize(sms->io.ports + 1, buf);
+ end_section(buf);
+
+ start_section(buf, SECTION_MAIN_RAM);
+ save_int8(buf, sizeof(sms->ram) / 1024);
+ save_buffer8(buf, sms->ram, sizeof(sms->ram));
+ end_section(buf);
+
+ start_section(buf, SECTION_MAPPER);
+ save_int8(buf, 1);//mapper type, 1 for Sega mapper
+ save_buffer8(buf, sms->bank_regs, sizeof(sms->bank_regs));
+ end_section(buf);
+
+ start_section(buf, SECTION_CART_RAM);
+ save_int8(buf, SMS_CART_RAM_SIZE / 1024);
+ save_buffer8(buf, sms->cart_ram, SMS_CART_RAM_SIZE);
+ end_section(buf);
+}
+
+static void ram_deserialize(deserialize_buffer *buf, void *vsms)
+{
+ sms_context *sms = vsms;
+ uint32_t ram_size = load_int8(buf) * 1024;
+ if (ram_size > sizeof(sms->ram)) {
+ fatal_error("State has a RAM size of %d bytes", ram_size);
+ }
+ load_buffer8(buf, sms->ram, ram_size);
+}
+
+static void cart_ram_deserialize(deserialize_buffer *buf, void *vsms)
+{
+ sms_context *sms = vsms;
+ uint32_t ram_size = load_int8(buf) * 1024;
+ if (ram_size > SMS_CART_RAM_SIZE) {
+ fatal_error("State has a cart RAM size of %d bytes", ram_size);
+ }
+ load_buffer8(buf, sms->cart_ram, ram_size);
+}
+
+static void mapper_deserialize(deserialize_buffer *buf, void *vsms)
+{
+ sms_context *sms = vsms;
+ uint8_t mapper_type = load_int8(buf);
+ if (mapper_type != 1) {
+ warning("State contains an unrecognized mapper type %d, it may be from a newer version of BlastEm\n", mapper_type);
+ return;
+ }
+ for (int i = 0; i < sizeof(sms->bank_regs); i++)
+ {
+ sms->bank_regs[i] = load_int8(buf);
+ update_mem_map(i, sms, sms->bank_regs[i]);
+ }
+}
+
+void sms_deserialize(deserialize_buffer *buf, sms_context *sms)
+{
+ register_section_handler(buf, (section_handler){.fun = z80_deserialize, .data = sms->z80}, SECTION_Z80);
+ register_section_handler(buf, (section_handler){.fun = vdp_deserialize, .data = sms->vdp}, SECTION_VDP);
+ register_section_handler(buf, (section_handler){.fun = psg_deserialize, .data = sms->psg}, SECTION_PSG);
+ register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports}, SECTION_SEGA_IO_1);
+ register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports + 1}, SECTION_SEGA_IO_2);
+ register_section_handler(buf, (section_handler){.fun = ram_deserialize, .data = sms}, SECTION_MAIN_RAM);
+ register_section_handler(buf, (section_handler){.fun = mapper_deserialize, .data = sms}, SECTION_MAPPER);
+ register_section_handler(buf, (section_handler){.fun = cart_ram_deserialize, .data = sms}, SECTION_CART_RAM);
+ //TODO: cart RAM
+ while (buf->cur_pos < buf->size)
+ {
+ load_section(buf);
+ }
+ z80_invalidate_code_range(sms->z80, 0xC000, 0x10000);
+ if (sms->bank_regs[0] & 8) {
+ //cart RAM is enabled, invalidate the region in case there is any code there
+ z80_invalidate_code_range(sms->z80, 0x8000, 0xC000);
+ }
+}
+
+static void save_state(sms_context *sms, uint8_t slot)
+{
+ char *save_path;
+ if (slot == QUICK_SAVE_SLOT) {
+ save_path = save_state_path;
+ } else {
+ char slotname[] = "slot_0.state";
+ slotname[5] = '0' + slot;
+ char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
+ save_path = alloc_concat_m(3, parts);
+ }
+ serialize_buffer state;
+ init_serialize(&state);
+ sms_serialize(sms, &state);
+ save_to_file(&state, save_path);
+ printf("Saved state to %s\n", save_path);
+ free(state.data);
+}
+
+static uint8_t load_state_path(sms_context *sms, char *path)
+{
+ deserialize_buffer state;
+ uint8_t ret;
+ if ((ret = load_from_file(&state, path))) {
+ sms_deserialize(&state, sms);
+ free(state.data);
+ printf("Loaded %s\n", path);
+ }
+ return ret;
+}
+
+static uint8_t load_state(system_header *system, uint8_t slot)
+{
+ sms_context *sms = (sms_context *)system;
+ char numslotname[] = "slot_0.state";
+ char *slotname;
+ if (slot == QUICK_SAVE_SLOT) {
+ slotname = "quicksave.state";
+ } else {
+ numslotname[5] = '0' + slot;
+ slotname = numslotname;
+ }
+ char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
+ char *statepath = alloc_concat_m(3, parts);
+ uint8_t ret = load_state_path(sms, statepath);
+ free(statepath);
+ return ret;
+}
+
static void run_sms(system_header *system)
{
render_disable_ym();
@@ -215,6 +365,16 @@ static void run_sms(system_header *system)
target_cycle = sms->z80->current_cycle;
vdp_run_context(sms->vdp, target_cycle);
psg_run(sms->psg, target_cycle);
+
+ if (system->save_state) {
+ while (!sms->z80->pc) {
+ //advance Z80 to an instruction boundary
+ z80_run(sms->z80, sms->z80->current_cycle + 1);
+ }
+ save_state(sms, system->save_state - 1);
+ system->save_state = 0;
+ }
+
target_cycle += 3420*16;
if (target_cycle > 0x10000000) {
uint32_t adjust = sms->z80->current_cycle - 3420*262*2;
@@ -243,14 +403,18 @@ static void start_sms(system_header *system, char *statefile)
sms_context *sms = (sms_context *)system;
set_keybindings(&sms->io);
+ z80_assert_reset(sms->z80, 0);
+ z80_clear_reset(sms->z80, 128*15);
+
+ if (statefile) {
+ load_state_path(sms, statefile);
+ }
+
if (system->enter_debugger) {
system->enter_debugger = 0;
- zinsert_breakpoint(sms->z80, 0, (uint8_t *)zdebugger);
+ zinsert_breakpoint(sms->z80, sms->z80->pc, (uint8_t *)zdebugger);
}
- z80_assert_reset(sms->z80, 0);
- z80_clear_reset(sms->z80, 128*15);
-
run_sms(system);
}
@@ -372,6 +536,7 @@ sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t for
sms->header.resume_context = resume_sms;
sms->header.load_save = load_save;
sms->header.persist_save = persist_save;
+ sms->header.load_state = load_state;
sms->header.free_context = free_sms;
sms->header.get_open_bus_value = get_open_bus_value;
sms->header.request_exit = request_exit;
diff --git a/system.h b/system.h
index 2bfe5b1..b71097b 100644
--- a/system.h
+++ b/system.h
@@ -22,6 +22,7 @@ typedef uint16_t (*system_fun_r16)(system_header *);
typedef void (*system_str_fun)(system_header *, char *);
typedef uint8_t (*system_str_fun_r8)(system_header *, char *);
typedef void (*speed_system_fun)(system_header *, uint32_t);
+typedef uint8_t (*system_u8_fun_r8)(system_header *, uint8_t);
#include "arena.h"
#include "romdb.h"
@@ -32,6 +33,7 @@ struct system_header {
system_fun resume_context;
system_fun load_save;
system_fun persist_save;
+ system_u8_fun_r8 load_state;
system_fun request_exit;
system_fun soft_reset;
system_fun free_context;
diff --git a/z80_to_x86.c b/z80_to_x86.c
index 75b0b33..4ab2700 100644
--- a/z80_to_x86.c
+++ b/z80_to_x86.c
@@ -3929,5 +3929,6 @@ void z80_deserialize(deserialize_buffer *buf, void *vcontext)
context->int_pulse_start = load_int32(buf);
context->int_pulse_end = load_int32(buf);
context->nmi_start = load_int32(buf);
+ context->native_pc = NULL;
}