# HG changeset patch # User Michael Pavone # Date 1502939188 25200 # Node ID c886c54d8cf1dff05f708569df5fd6fbcafecbbb # Parent 5e7e6d9b79ff0b855e4dba1dae667596129d4a05 Added save states to SMS emulation diff -r 5e7e6d9b79ff -r c886c54d8cf1 genesis.c --- a/genesis.c Sun Aug 13 22:59:01 2017 -0700 +++ b/genesis.c Wed Aug 16 20:06:28 2017 -0700 @@ -1003,6 +1003,41 @@ 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 @@ 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 -r 5e7e6d9b79ff -r c886c54d8cf1 menu.c --- a/menu.c Sun Aug 13 22:59:01 2017 -0700 +++ b/menu.c Wed Aug 16 20:06:28 2017 -0700 @@ -420,6 +420,9 @@ 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 @@ } 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 @@ break; } next->m68k->resume_pc = get_native_address_trans(next->m68k, pc); + */ } m68k->should_return = 1; break; diff -r 5e7e6d9b79ff -r c886c54d8cf1 serialize.h --- a/serialize.h Sun Aug 13 22:59:01 2017 -0700 +++ b/serialize.h Wed Aug 16 20:06:28 2017 -0700 @@ -40,9 +40,9 @@ 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 -r 5e7e6d9b79ff -r c886c54d8cf1 sms.c --- a/sms.c Sun Aug 13 22:59:01 2017 -0700 +++ b/sms.c Wed Aug 16 20:06:28 2017 -0700 @@ -109,14 +109,10 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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->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 -r 5e7e6d9b79ff -r c886c54d8cf1 system.h --- a/system.h Sun Aug 13 22:59:01 2017 -0700 +++ b/system.h Wed Aug 16 20:06:28 2017 -0700 @@ -22,6 +22,7 @@ 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 @@ 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 -r 5e7e6d9b79ff -r c886c54d8cf1 z80_to_x86.c --- a/z80_to_x86.c Sun Aug 13 22:59:01 2017 -0700 +++ b/z80_to_x86.c Wed Aug 16 20:06:28 2017 -0700 @@ -3929,5 +3929,6 @@ context->int_pulse_start = load_int32(buf); context->int_pulse_end = load_int32(buf); context->nmi_start = load_int32(buf); + context->native_pc = NULL; }