Mercurial > repos > blastem
diff sms.c @ 1433:c886c54d8cf1
Added save states to SMS emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 16 Aug 2017 20:06:28 -0700 |
parents | b56c8c51ca5d |
children | da1dce39e846 2455662378ed |
line wrap: on
line diff
--- 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;