# HG changeset patch # User Michael Pavone # Date 1502003196 25200 # Node ID 4e5797b3935a701cb1e06d1871264ae39833669a # Parent 957325c990d5867a550e78c5ee7fc198e4b55ded WIP - New savestate format diff -r 957325c990d5 -r 4e5797b3935a Makefile --- a/Makefile Fri Jul 07 21:44:49 2017 -0700 +++ b/Makefile Sun Aug 06 00:06:36 2017 -0700 @@ -127,7 +127,7 @@ AUDIOOBJS=ym2612.o psg.o wave.o CONFIGOBJS=config.o tern.o util.o -MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) +MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) ifeq ($(CPU),x86_64) CFLAGS+=-DX86_64 -m64 @@ -162,7 +162,7 @@ $(CC) -o $@ $^ $(LDFLAGS) $(FIXUP) ./$@ -blastjag$(EXE) : jaguar.o jag_video.o render_sdl.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) +blastjag$(EXE) : jaguar.o jag_video.o render_sdl.o serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) $(CC) -o $@ $^ $(LDFLAGS) dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o @@ -177,27 +177,27 @@ libemu68k.a : $(M68KOBJS) $(TRANSOBJS) ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS) -trans : trans.o $(M68KOBJS) $(TRANSOBJS) util.o +trans : trans.o serialize.o $(M68KOBJS) $(TRANSOBJS) util.o $(CC) -o trans trans.o $(M68KOBJS) $(TRANSOBJS) util.o transz80 : transz80.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o transz80 transz80.o $(Z80OBJS) $(TRANSOBJS) -ztestrun : ztestrun.o $(Z80OBJS) $(TRANSOBJS) +ztestrun : ztestrun.o serialize.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o ztestrun ztestrun.o $(Z80OBJS) $(TRANSOBJS) ztestgen : ztestgen.o z80inst.o $(CC) -ggdb -o ztestgen ztestgen.o z80inst.o -stateview$(EXE) : stateview.o vdp.o render_sdl.o ppm.o $(CONFIGOBJS) gst.o +stateview$(EXE) : stateview.o vdp.o render_sdl.o ppm.o serialize.o $(CONFIGOBJS) gst.o $(CC) -o $@ $^ $(LDFLAGS) $(FIXUP) ./$@ -vgmplay$(EXE) : vgmplay.o render_sdl.o ppm.o $(CONFIGOBJS) $(AUDIOOBJS) +vgmplay$(EXE) : vgmplay.o render_sdl.o ppm.o serialize.o $(CONFIGOBJS) $(AUDIOOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(FIXUP) ./$@ -blastcpm : blastcpm.o util.o $(Z80OBJS) $(TRANSOBJS) +blastcpm : blastcpm.o util.o serialize.o $(Z80OBJS) $(TRANSOBJS) $(CC) -o $@ $^ test : test.o vdp.o diff -r 957325c990d5 -r 4e5797b3935a genesis.c --- a/genesis.c Fri Jul 07 21:44:49 2017 -0700 +++ b/genesis.c Sun Aug 06 00:06:36 2017 -0700 @@ -32,6 +32,98 @@ #define MAX_SOUND_CYCLES 100000 +void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc) +{ + start_section(buf, SECTION_68000); + m68k_serialize(gen->m68k, m68k_pc, buf); + end_section(buf); + + start_section(buf, SECTION_Z80); + z80_serialize(gen->z80, buf); + end_section(buf); + + start_section(buf, SECTION_VDP); + vdp_serialize(gen->vdp, buf); + end_section(buf); + + start_section(buf, SECTION_YM2612); + ym_serialize(gen->ym, buf); + end_section(buf); + + start_section(buf, SECTION_PSG); + psg_serialize(gen->psg, buf); + end_section(buf); + + //TODO: bus arbiter state + + start_section(buf, SECTION_SEGA_IO_1); + io_serialize(gen->io.ports, buf); + end_section(buf); + + start_section(buf, SECTION_SEGA_IO_2); + io_serialize(gen->io.ports + 1, buf); + end_section(buf); + + start_section(buf, SECTION_SEGA_IO_EXT); + io_serialize(gen->io.ports + 2, buf); + end_section(buf); + + start_section(buf, SECTION_MAIN_RAM); + save_int8(buf, RAM_WORDS * 2 / 1024); + save_buffer16(buf, gen->work_ram, RAM_WORDS); + end_section(buf); + + start_section(buf, SECTION_SOUND_RAM); + save_int8(buf, Z80_RAM_BYTES / 1024); + save_buffer8(buf, gen->zram, Z80_RAM_BYTES); + end_section(buf); + + //TODO: mapper state +} + +static void ram_deserialize(deserialize_buffer *buf, void *vgen) +{ + genesis_context *gen = vgen; + uint32_t ram_size = load_int8(buf) * 1024 / 2; + if (ram_size > RAM_WORDS) { + fatal_error("State has a RAM size of %d bytes", ram_size * 2); + } + load_buffer16(buf, gen->work_ram, ram_size); +} + +static void zram_deserialize(deserialize_buffer *buf, void *vgen) +{ + genesis_context *gen = vgen; + uint32_t ram_size = load_int8(buf) * 1024; + if (ram_size > Z80_RAM_BYTES) { + fatal_error("State has a Z80 RAM size of %d bytes", ram_size); + } + load_buffer8(buf, gen->zram, ram_size); +} + +void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen) +{ + register_section_handler(buf, (section_handler){.fun = m68k_deserialize, .data = gen->m68k}, SECTION_68000); + register_section_handler(buf, (section_handler){.fun = z80_deserialize, .data = gen->z80}, SECTION_Z80); + register_section_handler(buf, (section_handler){.fun = vdp_deserialize, .data = gen->vdp}, SECTION_VDP); + register_section_handler(buf, (section_handler){.fun = ym_deserialize, .data = gen->ym}, SECTION_YM2612); + register_section_handler(buf, (section_handler){.fun = psg_deserialize, .data = gen->psg}, SECTION_PSG); + //TODO: bus arbiter + //HACK + gen->z80->reset = 0; + gen->z80->busreq = 0; + register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = gen->io.ports}, SECTION_SEGA_IO_1); + register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = gen->io.ports + 1}, SECTION_SEGA_IO_2); + register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = gen->io.ports + 2}, SECTION_SEGA_IO_EXT); + register_section_handler(buf, (section_handler){.fun = ram_deserialize, .data = gen}, SECTION_MAIN_RAM); + register_section_handler(buf, (section_handler){.fun = zram_deserialize, .data = gen}, SECTION_SOUND_RAM); + //TODO: mapper state + while (buf->cur_pos < buf->size) + { + load_section(buf); + } +} + uint16_t read_dma_value(uint32_t address) { genesis_context *genesis = (genesis_context *)current_system; @@ -252,7 +344,14 @@ char const *parts[] = {gen->header.save_dir, PATH_SEP, slotname}; save_path = alloc_concat_m(3, parts); } - save_gst(gen, save_path, address); + serialize_buffer state; + init_serialize(&state); + genesis_serialize(gen, &state, address);; + FILE *statefile = fopen(save_path, "wb"); + fwrite(state.data, 1, state.size, statefile); + fclose(statefile); + free(state.data); + //save_gst(gen, save_path, address); printf("Saved state to %s\n", save_path); if (slot != QUICK_SAVE_SLOT) { free(save_path); @@ -906,9 +1005,26 @@ set_keybindings(&gen->io); render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC); if (statefile) { + //first try loading as a GST format savestate uint32_t pc = load_gst(gen, statefile); if (!pc) { - fatal_error("Failed to load save state %s\n", statefile); + //switch to native format if that fails + FILE *f = fopen(statefile, "rb"); + if (!f) { + goto state_error; + } + long statesize = file_size(f); + deserialize_buffer state; + void *statedata = malloc(statesize); + if (statesize != fread(statedata, 1, statesize, f)) { + goto state_error; + } + fclose(f); + init_deserialize(&state, statedata, statesize); + genesis_deserialize(&state, gen); + free(statedata); + //HACK + pc = gen->m68k->last_prefetch_address; } printf("Loaded %s\n", statefile); if (gen->header.enter_debugger) { @@ -926,6 +1042,9 @@ m68k_reset(gen->m68k); } handle_reset_requests(gen); + return; +state_error: + fatal_error("Failed to load save state %s\n", statefile); } static void resume_genesis(system_header *system) diff -r 957325c990d5 -r 4e5797b3935a io.c --- a/io.c Fri Jul 07 21:44:49 2017 -0700 +++ b/io.c Sun Aug 06 00:06:36 2017 -0700 @@ -15,6 +15,7 @@ #include #include +#include "serialize.h" #include "io.h" #include "blastem.h" #include "genesis.h" @@ -2130,4 +2131,74 @@ return value; } +void io_serialize(io_port *port, serialize_buffer *buf) +{ + save_int8(buf, port->output); + save_int8(buf, port->control); + save_int8(buf, port->serial_out); + save_int8(buf, port->serial_in); + save_int8(buf, port->serial_ctrl); + save_int8(buf, port->device_type); + save_buffer32(buf, port->slow_rise_start, 8); + switch (port->device_type) + { + case IO_GAMEPAD6: + save_int32(buf, port->device.pad.timeout_cycle); + save_int16(buf, port->device.pad.th_counter); + break; + case IO_MOUSE: + save_int32(buf, port->device.mouse.ready_cycle); + save_int16(buf, port->device.mouse.last_read_x); + save_int16(buf, port->device.mouse.last_read_y); + save_int16(buf, port->device.mouse.latched_x); + save_int16(buf, port->device.mouse.latched_y); + save_int8(buf, port->device.mouse.tr_counter); + break; + case IO_SATURN_KEYBOARD: + case IO_XBAND_KEYBOARD: + save_int8(buf, port->device.keyboard.tr_counter); + if (port->device_type == IO_XBAND_KEYBOARD) { + save_int8(buf, port->device.keyboard.mode); + save_int8(buf, port->device.keyboard.cmd); + } + break; + } +} +void io_deserialize(deserialize_buffer *buf, void *vport) +{ + io_port *port = vport; + port->output = load_int8(buf); + port->control = load_int8(buf); + port->serial_out = load_int8(buf); + port->serial_in = load_int8(buf); + port->serial_ctrl = load_int8(buf); + uint8_t device_type = load_int8(buf); + if (device_type != port->device_type) { + warning("Loaded save state has a different device type from the current configuration"); + return; + } + switch (port->device_type) + { + case IO_GAMEPAD6: + port->device.pad.timeout_cycle = load_int32(buf); + port->device.pad.th_counter = load_int16(buf); + break; + case IO_MOUSE: + port->device.mouse.ready_cycle = load_int32(buf); + port->device.mouse.last_read_x = load_int16(buf); + port->device.mouse.last_read_y = load_int16(buf); + port->device.mouse.latched_x = load_int16(buf); + port->device.mouse.latched_y = load_int16(buf); + port->device.mouse.tr_counter = load_int8(buf); + break; + case IO_SATURN_KEYBOARD: + case IO_XBAND_KEYBOARD: + port->device.keyboard.tr_counter = load_int8(buf); + if (port->device_type == IO_XBAND_KEYBOARD) { + port->device.keyboard.mode = load_int8(buf); + port->device.keyboard.cmd = load_int8(buf); + } + break; + } +} diff -r 957325c990d5 -r 4e5797b3935a io.h --- a/io.h Fri Jul 07 21:44:49 2017 -0700 +++ b/io.h Sun Aug 06 00:06:36 2017 -0700 @@ -8,6 +8,7 @@ #include #include "tern.h" #include "romdb.h" +#include "serialize.h" enum { IO_GAMEPAD2, @@ -109,6 +110,8 @@ void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay); void handle_mousedown(int mouse, int button); void handle_mouseup(int mouse, int button); +void io_serialize(io_port *port, serialize_buffer *buf); +void io_deserialize(deserialize_buffer *buf, void *vport); #endif //IO_H_ diff -r 957325c990d5 -r 4e5797b3935a m68k_core.c --- a/m68k_core.c Fri Jul 07 21:44:49 2017 -0700 +++ b/m68k_core.c Sun Aug 06 00:06:36 2017 -0700 @@ -9,6 +9,7 @@ #include "backend.h" #include "gen.h" #include "util.h" +#include "serialize.h" #include #include #include @@ -1202,3 +1203,54 @@ context->reset_handler = (code_ptr)reset_handler; return context; } + +void m68k_serialize(m68k_context *context, uint32_t pc, serialize_buffer *buf) +{ + for (int i = 0; i < 8; i++) + { + save_int32(buf, context->dregs[i]); + } + for (int i = 0; i < 9; i++) + { + save_int32(buf, context->aregs[i]); + } + save_int32(buf, pc); + uint16_t sr = context->status << 3; + for (int flag = 4; flag >= 0; flag--) { + sr <<= 1; + sr |= context->flags[flag] != 0; + } + save_int16(buf, sr); + save_int32(buf, context->current_cycle); + save_int32(buf, context->int_cycle); + save_int8(buf, context->int_num); + save_int8(buf, context->int_pending); + save_int8(buf, context->trace_pending); +} + +void m68k_deserialize(deserialize_buffer *buf, void *vcontext) +{ + m68k_context *context = vcontext; + for (int i = 0; i < 8; i++) + { + context->dregs[i] = load_int32(buf); + } + for (int i = 0; i < 9; i++) + { + context->aregs[i] = load_int32(buf); + } + //hack until both PC and IR registers are represented properly + context->last_prefetch_address = load_int32(buf); + uint16_t sr = load_int16(buf); + context->status = sr >> 8; + for (int flag = 0; flag < 5; flag++) + { + context->flags[flag] = sr & 1; + sr >>= 1; + } + context->current_cycle = load_int32(buf); + context->int_cycle = load_int32(buf); + context->int_num = load_int8(buf); + context->int_pending = load_int8(buf); + context->trace_pending = load_int8(buf); +} diff -r 957325c990d5 -r 4e5797b3935a m68k_core.h --- a/m68k_core.h Fri Jul 07 21:44:49 2017 -0700 +++ b/m68k_core.h Sun Aug 06 00:06:36 2017 -0700 @@ -8,6 +8,7 @@ #include #include #include "backend.h" +#include "serialize.h" //#include "68kinst.h" struct m68kinst; @@ -116,6 +117,8 @@ uint16_t m68k_get_ir(m68k_context *context); void m68k_print_regs(m68k_context * context); void m68k_invalidate_code_range(m68k_context *context, uint32_t start, uint32_t end); +void m68k_serialize(m68k_context *context, uint32_t pc, serialize_buffer *buf); +void m68k_deserialize(deserialize_buffer *buf, void *vcontext); #endif //M68K_CORE_H_ diff -r 957325c990d5 -r 4e5797b3935a menu.c --- a/menu.c Fri Jul 07 21:44:49 2017 -0700 +++ b/menu.c Sun Aug 06 00:06:36 2017 -0700 @@ -346,8 +346,10 @@ char *cur = buffer; if (gen->header.next_context && gen->header.next_context->save_dir) { char *end = buffer + SAVE_INFO_BUFFER_SIZE; - char slotfile[] = "slot_0.gst"; + char slotfile[] = "slot_0.state"; + char slotfilegst[] = "slot_0.gst"; char const * parts[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfile}; + char const * partsgst[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfilegst}; struct tm ltime; char *fname; time_t modtime; @@ -362,20 +364,37 @@ cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, <ime)); } else { - cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i); + slotfilegst[5] = i + '0'; + fname = alloc_concat_m(3, partsgst); + modtime = get_modification_time(fname); + free(fname); + if (modtime) { + cur += snprintf(cur, end-cur, "Slot %d - ", i); + cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, <ime)); + } else { + cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i); + } } //advance past the null terminator for this entry cur++; } if (cur < end) { - parts[2] = "quicksave.gst"; + parts[2] = "quicksave.state"; fname = alloc_concat_m(3, parts); modtime = get_modification_time(fname); free(fname); if (modtime) { cur += strftime(cur, end-cur, "Quick - %c", localtime_r(&modtime, <ime)); - } else if ((end-cur) > strlen("Quick - EMPTY")){ - cur += strlen(strcpy(cur, "Quick - EMPTY")); + } else { + parts[2] = "quicksave.gst"; + fname = alloc_concat_m(3, parts); + modtime = get_modification_time(fname); + free(fname); + if (modtime) { + cur += strftime(cur, end-cur, "Quick - %c", localtime_r(&modtime, <ime)); + } else if ((end-cur) > strlen("Quick - EMPTY")){ + cur += strlen(strcpy(cur, "Quick - EMPTY")); + } } //advance past the null terminator for this entry cur++; @@ -401,10 +420,10 @@ case 6: //load state if (gen->header.next_context && gen->header.next_context->save_dir) { - char numslotname[] = "slot_0.gst"; + char numslotname[] = "slot_0.state"; char *slotname; if (dst == QUICK_SAVE_SLOT) { - slotname = "quicksave.gst"; + slotname = "quicksave.state"; } else { numslotname[5] = '0' + dst; slotname = numslotname; @@ -412,6 +431,7 @@ char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname}; char *gstpath = alloc_concat_m(3, parts); genesis_context *next = (genesis_context *)gen->header.next_context; + uint32_t pc = load_gst(next, gstpath); free(gstpath); if (!pc) { diff -r 957325c990d5 -r 4e5797b3935a psg.c --- a/psg.c Fri Jul 07 21:44:49 2017 -0700 +++ b/psg.c Sun Aug 06 00:06:36 2017 -0700 @@ -151,3 +151,35 @@ } } +void psg_serialize(psg_context *context, serialize_buffer *buf) +{ + save_int16(buf, context->lsfr); + save_buffer16(buf, context->counter_load, 4); + save_buffer16(buf, context->counters, 4); + save_buffer8(buf, context->volume, 4); + uint8_t output_state = context->output_state[0] << 3 | context->output_state[1] << 2 + | context->output_state[2] << 1 | context->output_state[3] + | context->noise_use_tone << 4; + save_int8(buf, output_state); + save_int8(buf, context->noise_type); + save_int8(buf, context->latch); + save_int32(buf, context->cycles); +} + +void psg_deserialize(deserialize_buffer *buf, void *vcontext) +{ + psg_context *context = vcontext; + context->lsfr = load_int16(buf); + load_buffer16(buf, context->counter_load, 4); + load_buffer16(buf, context->counters, 4); + load_buffer8(buf, context->volume, 4); + uint8_t output_state = load_int8(buf); + context->output_state[0] = output_state & 8 >> 3; + context->output_state[1] = output_state & 4 >> 2; + context->output_state[2] = output_state & 2 >> 1; + context->output_state[3] = output_state & 1; + context->noise_use_tone = output_state & 0x10 >> 4; + context->noise_type = load_int8(buf); + context->latch = load_int8(buf); + context->cycles = load_int32(buf); +} diff -r 957325c990d5 -r 4e5797b3935a psg.h --- a/psg.h Fri Jul 07 21:44:49 2017 -0700 +++ b/psg.h Sun Aug 06 00:06:36 2017 -0700 @@ -7,6 +7,7 @@ #define PSG_CONTEXT_H_ #include +#include "serialize.h" typedef struct { int16_t *audio_buffer; @@ -38,6 +39,8 @@ void psg_adjust_master_clock(psg_context * context, uint32_t master_clock); void psg_write(psg_context * context, uint8_t value); void psg_run(psg_context * context, uint32_t cycles); +void psg_serialize(psg_context *context, serialize_buffer *buf); +void psg_deserialize(deserialize_buffer *buf, void *vcontext); #endif //PSG_CONTEXT_H_ diff -r 957325c990d5 -r 4e5797b3935a serialize.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serialize.c Sun Aug 06 00:06:36 2017 -0700 @@ -0,0 +1,276 @@ +#include +#include +#include +#include "serialize.h" +#include "util.h" + +#ifndef SERIALIZE_DEFAULT_SIZE +#define SERIALIZE_DEFAULT_SIZE (256*1024) //default to enough for a Genesis save state +#endif + + +void init_serialize(serialize_buffer *buf) +{ + buf->storage = SERIALIZE_DEFAULT_SIZE; + buf->size = 0; + buf->current_section_start = 0; + buf->data = malloc(SERIALIZE_DEFAULT_SIZE); +} + +static void reserve(serialize_buffer *buf, size_t amount) +{ + if (amount > (buf->storage - buf->size)) { + buf->storage *= 2; + buf = realloc(buf, buf->storage + sizeof(*buf)); + } +} + +void save_int32(serialize_buffer *buf, uint32_t val) +{ + reserve(buf, sizeof(val)); + buf->data[buf->size++] = val >> 24; + buf->data[buf->size++] = val >> 16; + buf->data[buf->size++] = val >> 8; + buf->data[buf->size++] = val; +} + +void save_int16(serialize_buffer *buf, uint16_t val) +{ + reserve(buf, sizeof(val)); + buf->data[buf->size++] = val >> 8; + buf->data[buf->size++] = val; +} + +void save_int8(serialize_buffer *buf, uint8_t val) +{ + reserve(buf, sizeof(val)); + buf->data[buf->size++] = val; +} + +void save_string(serialize_buffer *buf, char *val) +{ + size_t len = strlen(val); + save_buffer8(buf, val, len); +} + +void save_buffer8(serialize_buffer *buf, void *val, size_t len) +{ + reserve(buf, len); + memcpy(&buf->data[buf->size], val, len); + buf->size += len; +} + +void save_buffer16(serialize_buffer *buf, uint16_t *val, size_t len) +{ + reserve(buf, len * sizeof(*val)); + for(; len != 0; len--, val++) { + buf->data[buf->size++] = *val >> 8; + buf->data[buf->size++] = *val; + } +} + +void save_buffer32(serialize_buffer *buf, uint32_t *val, size_t len) +{ + reserve(buf, len * sizeof(*val)); + for(; len != 0; len--, val++) { + buf->data[buf->size++] = *val >> 24; + buf->data[buf->size++] = *val >> 16; + buf->data[buf->size++] = *val >> 8; + buf->data[buf->size++] = *val; + } +} + +void start_section(serialize_buffer *buf, uint16_t section_id) +{ + save_int16(buf, section_id); + //reserve some space for size once we end this section + reserve(buf, sizeof(uint32_t)); + buf->size += sizeof(uint32_t); + //save start point for use in end_device + buf->current_section_start = buf->size; +} + +void end_section(serialize_buffer *buf) +{ + size_t section_size = buf->size - buf->current_section_start; + if (section_size > 0xFFFFFFFFU) { + fatal_error("Sections larger than 4GB are not supported"); + } + uint32_t size = section_size; + uint8_t *field = buf->data + buf->current_section_start - sizeof(uint32_t); + *(field++) = size >> 24; + *(field++) = size >> 16; + *(field++) = size >> 8; + *(field++) = size; + buf->current_section_start = 0; +} + +void register_section_handler(deserialize_buffer *buf, section_handler handler, uint16_t section_id) +{ + if (section_id > buf->max_handler) { + uint16_t old_max = buf->max_handler; + if (buf->max_handler < 0x8000) { + buf->max_handler *= 2; + } else { + buf->max_handler = 0xFFFF; + } + buf->handlers = realloc(buf->handlers, (buf->max_handler+1) * sizeof(handler)); + memset(buf->handlers + old_max + 1, 0, (buf->max_handler - old_max) * sizeof(handler)); + } + if (!buf->handlers) { + buf->handlers = calloc(buf->max_handler + 1, sizeof(handler)); + } + buf->handlers[section_id] = handler; +} + +void init_deserialize(deserialize_buffer *buf, uint8_t *data, size_t size) +{ + buf->size = size; + buf->cur_pos = 0; + buf->data = data; + buf->handlers = NULL; + buf->max_handler = 8; +} + +uint32_t load_int32(deserialize_buffer *buf) +{ + uint32_t val; + if ((buf->size - buf->cur_pos) < sizeof(val)) { + fatal_error("Failed to load required int32 field"); + } + val = buf->data[buf->cur_pos++] << 24; + val |= buf->data[buf->cur_pos++] << 16; + val |= buf->data[buf->cur_pos++] << 8; + val |= buf->data[buf->cur_pos++]; + return val; +} + +uint16_t load_int16(deserialize_buffer *buf) +{ + uint16_t val; + if ((buf->size - buf->cur_pos) < sizeof(val)) { + fatal_error("Failed to load required int16 field"); + } + val = buf->data[buf->cur_pos++] << 8; + val |= buf->data[buf->cur_pos++]; + return val; +} + +uint8_t load_int8(deserialize_buffer *buf) +{ + uint8_t val; + if ((buf->size - buf->cur_pos) < sizeof(val)) { + fatal_error("Failed to load required int8 field"); + } + val = buf->data[buf->cur_pos++]; + return val; +} + +void load_buffer8(deserialize_buffer *buf, void *dst, size_t len) +{ + if ((buf->size - buf->cur_pos) < len) { + fatal_error("Failed to load required buffer of size %d", len); + } + memcpy(dst, buf->data + buf->cur_pos, len); + buf->cur_pos += len; +} + +void load_buffer16(deserialize_buffer *buf, uint16_t *dst, size_t len) +{ + if ((buf->size - buf->cur_pos) < len * sizeof(uint16_t)) { + fatal_error("Failed to load required buffer of size %d\n", len); + } + for(; len != 0; len--, dst++) { + uint16_t value = buf->data[buf->cur_pos++] << 8; + value |= buf->data[buf->cur_pos++]; + *dst = value; + } +} +void load_buffer32(deserialize_buffer *buf, uint32_t *dst, size_t len) +{ + if ((buf->size - buf->cur_pos) < len * sizeof(uint32_t)) { + fatal_error("Failed to load required buffer of size %d\n", len); + } + for(; len != 0; len--, dst++) { + uint32_t value = buf->data[buf->cur_pos++] << 24; + value |= buf->data[buf->cur_pos++] << 16; + value |= buf->data[buf->cur_pos++] << 8; + value |= buf->data[buf->cur_pos++]; + *dst = value; + } +} + +void load_section(deserialize_buffer *buf) +{ + if (!buf->handlers) { + fatal_error("load_section called on a deserialize_buffer with no handlers registered\n"); + } + uint16_t section_id = load_int16(buf); + uint32_t size = load_int32(buf); + if (size > (buf->size - buf->cur_pos)) { + fatal_error("Section is bigger than remaining space in file"); + } + if (section_id > buf->max_handler || !buf->handlers[section_id].fun) { + warning("No handler for section ID %d, save state may be from a newer version\n", section_id); + buf->cur_pos += size; + return; + } + deserialize_buffer section; + init_deserialize(§ion, buf->data + buf->cur_pos, size); + buf->handlers[section_id].fun(§ion, buf->handlers[section_id].data); + buf->cur_pos += size; +} + +static const char sz_ident[] = "BLSTSZ\x01\x07"; + +uint8_t save_to_file(serialize_buffer *buf, char *path) +{ + FILE *f = fopen(path, "wb"); + if (!f) { + return 0; + } + if (fwrite(sz_ident, 1, sizeof(sz_ident)-1, f) != sizeof(sz_ident)-1) { + fclose(f); + return 0; + } + if (fwrite(buf->data, 1, buf->size, f) != buf->size) { + fclose(f); + return 0; + } + fclose(f); + return 1; +} + +uint8_t load_from_file(deserialize_buffer *buf, char *path) +{ + FILE *f = fopen(path, "rb"); + if (!f) { + return 0; + } + char ident[sizeof(sz_ident)-1]; + long size = file_size(f); + if (size < sizeof(ident)) { + fclose(f); + return 0; + } + if (fread(ident, 1, sizeof(ident), f) != sizeof(ident)) { + fclose(f); + return 0; + } + fclose(f); + if (memcmp(ident, sz_ident, sizeof(ident))) { + return 0; + } + buf->size = size - sizeof(ident); + buf->cur_pos = 0; + buf->data = malloc(buf->size); + buf->handlers = NULL; + buf->max_handler = 8; + if (fread(buf->data, 1, buf->size, f) != buf->size) { + free(buf->data); + buf->data = NULL; + buf->size = 0; + return 0; + } + return 1; +} diff -r 957325c990d5 -r 4e5797b3935a serialize.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serialize.h Sun Aug 06 00:06:36 2017 -0700 @@ -0,0 +1,69 @@ +#ifndef SERIALIZE_H_ +#define SERIALIZE_H_ + +#include +#include + +typedef struct { + size_t size; + size_t storage; + size_t current_section_start; + uint8_t *data; +} serialize_buffer; + +typedef struct deserialize_buffer deserialize_buffer; +typedef void (*section_fun)(deserialize_buffer *buf, void *data); + +typedef struct { + section_fun fun; + void *data; +} section_handler; + +struct deserialize_buffer { + size_t size; + size_t cur_pos; + uint8_t *data; + section_handler *handlers; + uint16_t max_handler; +}; + +enum { + SECTION_HEADER, + SECTION_68000, + SECTION_Z80, + SECTION_VDP, + SECTION_YM2612, + SECTION_PSG, + SECTION_GEN_BUS_ARBITER, + SECTION_SEGA_IO_1, + SECTION_SEGA_IO_2, + SECTION_SEGA_IO_EXT, + SECTION_MAIN_RAM, + SECTION_SOUND_RAM, + SECTION_SEGA_MAPPER, + SECTION_SMS_MAPPER, + SECTION_EEPROM +}; + +void init_serialize(serialize_buffer *buf); +void save_int32(serialize_buffer *buf, uint32_t val); +void save_int16(serialize_buffer *buf, uint16_t val); +void save_int8(serialize_buffer *buf, uint8_t val); +void save_string(serialize_buffer *buf, char *val); +void save_buffer8(serialize_buffer *buf, void *val, size_t len); +void save_buffer16(serialize_buffer *buf, uint16_t *val, size_t len); +void save_buffer32(serialize_buffer *buf, uint32_t *val, size_t len); +void start_section(serialize_buffer *buf, uint16_t section_id); +void end_section(serialize_buffer *buf); +void register_section_handler(deserialize_buffer *buf, section_handler handler, uint16_t section_id); +void init_deserialize(deserialize_buffer *buf, uint8_t *data, size_t size); +uint32_t load_int32(deserialize_buffer *buf); +uint16_t load_int16(deserialize_buffer *buf); +uint8_t load_int8(deserialize_buffer *buf); +void load_buffer8(deserialize_buffer *buf, void *dst, size_t len); +void load_buffer16(deserialize_buffer *buf, uint16_t *dst, size_t len); +void load_buffer32(deserialize_buffer *buf, uint32_t *dst, size_t len); +void load_section(deserialize_buffer *buf); +uint8_t save_to_file(serialize_buffer *buf, char *path); +uint8_t load_from_file(deserialize_buffer *buf, char *path); +#endif //SERIALIZE_H diff -r 957325c990d5 -r 4e5797b3935a vdp.c --- a/vdp.c Fri Jul 07 21:44:49 2017 -0700 +++ b/vdp.c Sun Aug 06 00:06:36 2017 -0700 @@ -2996,7 +2996,6 @@ // if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { //DMA copy or 68K -> VDP, transfer starts immediately - context->dma_cd = context->cd; //printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot); if (!(context->regs[REG_DMASRC_H] & 0x80)) { //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]); @@ -3526,3 +3525,164 @@ } } +void vdp_serialize(vdp_context *context, serialize_buffer *buf) +{ + save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing + save_buffer8(buf, context->vdpmem, VRAM_SIZE); + save_buffer16(buf, context->cram, CRAM_SIZE); + save_buffer16(buf, context->vsram, VSRAM_SIZE); + save_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); + for (int i = 0; i <= REG_DMASRC_H; i++) + { + save_int8(buf, context->regs[i]); + } + save_int32(buf, context->address); + save_int32(buf, context->serial_address); + save_int8(buf, context->cd); + uint8_t fifo_size; + if (context->fifo_read < 0) { + fifo_size = 0; + } else if (context->fifo_write > context->fifo_read) { + fifo_size = context->fifo_write - context->fifo_read; + } else { + fifo_size = context->fifo_write + FIFO_SIZE - context->fifo_read; + } + save_int8(buf, fifo_size); + for (int i = 0, cur = context->fifo_read; i < fifo_size; i++) + { + fifo_entry *entry = context->fifo + cur; + cur = (cur + 1) & (FIFO_SIZE - 1); + save_int32(buf, entry->cycle); + save_int32(buf, entry->address); + save_int16(buf, entry->value); + save_int8(buf, entry->cd); + save_int8(buf, entry->partial); + } + //FIXME: Flag bits should be rearranged for maximum correspondence to status reg + save_int16(buf, context->flags2 << 8 | context->flags); + save_int32(buf, context->frame); + save_int16(buf, context->vcounter); + save_int8(buf, context->hslot); + save_int16(buf, context->hv_latch); + save_int8(buf, context->state); + save_int16(buf, context->hscroll_a); + save_int16(buf, context->hscroll_b); + save_int16(buf, context->vscroll_latch[0]); + save_int16(buf, context->vscroll_latch[1]); + save_int16(buf, context->col_1); + save_int16(buf, context->col_2); + save_int16(buf, context->test_port); + save_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE); + save_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE); + save_int8(buf, context->buf_a_off); + save_int8(buf, context->buf_b_off); + //FIXME: Sprite rendering state is currently a mess + save_int8(buf, context->sprite_index); + save_int8(buf, context->sprite_draws); + save_int8(buf, context->slot_counter); + save_int8(buf, context->cur_slot); + for (int i = 0; i < MAX_DRAWS; i++) + { + sprite_draw *draw = context->sprite_draw_list + i; + save_int16(buf, draw->address); + save_int16(buf, draw->x_pos); + save_int8(buf, draw->pal_priority); + save_int8(buf, draw->h_flip); + } + for (int i = 0; i < MAX_SPRITES_LINE; i++) + { + sprite_info *info = context->sprite_info_list + i; + save_int8(buf, info->size); + save_int8(buf, info->index); + save_int16(buf, info->y); + } + save_buffer8(buf, context->linebuf, LINEBUF_SIZE); + + save_int32(buf, context->cycles); + save_int32(buf, context->pending_vint_start); + save_int32(buf, context->pending_hint_start); +} + +void vdp_deserialize(deserialize_buffer *buf, void *vcontext) +{ + vdp_context *context = vcontext; + uint8_t vramk = load_int8(buf); + load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE); + if ((vramk * 1024) > VRAM_SIZE) { + buf->cur_pos += (vramk * 1024) - VRAM_SIZE; + } + load_buffer16(buf, context->cram, CRAM_SIZE); + load_buffer16(buf, context->vsram, VSRAM_SIZE); + load_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); + for (int i = 0; i <= REG_DMASRC_H; i++) + { + context->regs[i] = load_int8(buf); + } + context->address = load_int32(buf); + context->serial_address = load_int32(buf); + context->cd = load_int8(buf); + uint8_t fifo_size = load_int8(buf); + if (fifo_size > FIFO_SIZE) { + fatal_error("Invalid fifo size %d", fifo_size); + } + if (fifo_size) { + context->fifo_read = 0; + context->fifo_write = fifo_size & (FIFO_SIZE - 1); + for (int i = 0; i < fifo_size; i++) + { + fifo_entry *entry = context->fifo + i; + entry->cycle = load_int32(buf); + entry->address = load_int32(buf); + entry->value = load_int16(buf); + entry->cd = load_int8(buf); + entry->partial = load_int8(buf); + } + } else { + context->fifo_read = -1; + context->fifo_write = 0; + } + uint16_t flags = load_int16(buf); + context->flags2 = flags >> 8; + context->flags = flags; + context->frame = load_int32(buf); + context->vcounter = load_int16(buf); + context->hslot = load_int8(buf); + context->hv_latch = load_int16(buf); + context->state = load_int8(buf); + context->hscroll_a = load_int16(buf); + context->hscroll_b = load_int16(buf); + context->vscroll_latch[0] = load_int16(buf); + context->vscroll_latch[1] = load_int16(buf); + context->col_1 = load_int16(buf); + context->col_2 = load_int16(buf); + context->test_port = load_int16(buf); + load_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE); + load_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE); + context->buf_a_off = load_int8(buf) & SCROLL_BUFFER_MASK; + context->buf_b_off = load_int8(buf) & SCROLL_BUFFER_MASK; + context->sprite_index = load_int8(buf); + context->sprite_draws = load_int8(buf); + context->slot_counter = load_int8(buf); + context->cur_slot = load_int8(buf); + for (int i = 0; i < MAX_DRAWS; i++) + { + sprite_draw *draw = context->sprite_draw_list + i; + draw->address = load_int16(buf); + draw->x_pos = load_int16(buf); + draw->pal_priority = load_int8(buf); + draw->h_flip = load_int8(buf); + } + for (int i = 0; i < MAX_SPRITES_LINE; i++) + { + sprite_info *info = context->sprite_info_list + i; + info->size = load_int8(buf); + info->index = load_int8(buf); + info->y = load_int16(buf); + } + load_buffer8(buf, context->linebuf, LINEBUF_SIZE); + + context->cycles = load_int32(buf); + context->pending_vint_start = load_int32(buf); + context->pending_hint_start = load_int32(buf); + update_video_params(context); +} diff -r 957325c990d5 -r 4e5797b3935a vdp.h --- a/vdp.h Fri Jul 07 21:44:49 2017 -0700 +++ b/vdp.h Sun Aug 06 00:06:36 2017 -0700 @@ -9,6 +9,7 @@ #include #include #include "system.h" +#include "serialize.h" #define VDP_REGS 24 #define CRAM_SIZE 64 @@ -199,11 +200,9 @@ uint8_t max_sprites_line; uint8_t fetch_tmp[2]; uint8_t v_offset; - uint8_t dma_cd; uint8_t hint_counter; uint8_t flags2; uint8_t double_res; - uint8_t b32; uint8_t buf_a_off; uint8_t buf_b_off; uint8_t debug; @@ -250,5 +249,7 @@ void vdp_pbc_pause(vdp_context *context); void vdp_release_framebuffer(vdp_context *context); void vdp_reacquire_framebuffer(vdp_context *context); +void vdp_serialize(vdp_context *context, serialize_buffer *buf); +void vdp_deserialize(deserialize_buffer *buf, void *vcontext); #endif //VDP_H_ diff -r 957325c990d5 -r 4e5797b3935a ym2612.c --- a/ym2612.c Fri Jul 07 21:44:49 2017 -0700 +++ b/ym2612.c Sun Aug 06 00:06:36 2017 -0700 @@ -1058,3 +1058,117 @@ context->timer_control & BIT_TIMERB_ENABLE ? "yes" : "no"); } +void ym_serialize(ym2612_context *context, serialize_buffer *buf) +{ + save_buffer8(buf, context->part1_regs, YM_PART1_REGS); + save_buffer8(buf, context->part2_regs, YM_PART2_REGS); + for (int i = 0; i < NUM_OPERATORS; i++) + { + save_int32(buf, context->operators[i].phase_counter); + save_int16(buf, context->operators[i].envelope); + save_int16(buf, context->operators[i].output); + save_int8(buf, context->operators[i].env_phase); + save_int8(buf, context->operators[i].inverted); + } + for (int i = 0; i < NUM_CHANNELS; i++) + { + save_int16(buf, context->channels[i].output); + save_int16(buf, context->channels[i].op1_old); + //Due to the latching behavior, these need to be saved + //even though duplicate info is probably in the regs array + save_int8(buf, context->channels[i].block); + save_int8(buf, context->channels[i].fnum); + } + for (int i = 0; i < 3; i++) + { + //Due to the latching behavior, these need to be saved + //even though duplicate info is probably in the regs array + save_int8(buf, context->ch3_supp[i].block); + save_int8(buf, context->ch3_supp[i].fnum); + } + save_int16(buf, context->timer_a); + save_int8(buf, context->timer_b); + save_int8(buf, context->sub_timer_b); + save_int16(buf, context->env_counter); + save_int8(buf, context->current_op); + save_int8(buf, context->current_env_op); + save_int8(buf, context->lfo_counter); + save_int8(buf, context->csm_keyon); + save_int8(buf, context->status); + save_int8(buf, context->selected_reg); + save_int8(buf, context->selected_part); + save_int32(buf, context->current_cycle); + save_int32(buf, context->write_cycle); + save_int32(buf, context->busy_cycles); +} + +void ym_deserialize(deserialize_buffer *buf, void *vcontext) +{ + ym2612_context *context = vcontext; + uint8_t temp_regs[YM_PART1_REGS]; + load_buffer8(buf, temp_regs, YM_PART1_REGS); + context->selected_part = 0; + for (int i = 0; i < YM_PART1_REGS; i++) + { + uint8_t reg = YM_PART1_START + i; + if (reg != REG_FNUM_LOW && reg != REG_KEY_ONOFF && reg != REG_TIME_CTRL) { + context->selected_reg = reg; + ym_data_write(context, temp_regs[i]); + } + } + load_buffer8(buf, temp_regs, YM_PART2_REGS); + context->selected_part = 1; + for (int i = 0; i < YM_PART2_REGS; i++) + { + uint8_t reg = YM_PART2_START + i; + if (reg != REG_FNUM_LOW) { + context->selected_reg = reg; + ym_data_write(context, temp_regs[i]); + } + } + for (int i = 0; i < NUM_OPERATORS; i++) + { + context->operators[i].phase_counter = load_int32(buf); + context->operators[i].envelope = load_int16(buf); + context->operators[i].output = load_int16(buf); + context->operators[i].env_phase = load_int8(buf); + if (context->operators[i].env_phase > PHASE_RELEASE) { + context->operators[i].env_phase = PHASE_RELEASE; + } + context->operators[i].inverted = load_int8(buf) != 0; + } + for (int i = 0; i < NUM_CHANNELS; i++) + { + context->channels[i].output = load_int16(buf); + context->channels[i].op1_old = load_int16(buf); + context->channels[i].block = load_int8(buf); + context->channels[i].fnum = load_int8(buf); + context->channels[i].keycode = context->channels[i].block << 2 | fnum_to_keycode[context->channels[i].fnum >> 7]; + } + for (int i = 0; i < 3; i++) + { + context->ch3_supp[i].block = load_int8(buf); + context->ch3_supp[i].fnum = load_int8(buf); + context->ch3_supp[i].keycode = context->ch3_supp[i].block << 2 | fnum_to_keycode[context->ch3_supp[i].fnum >> 7]; + } + context->timer_a = load_int16(buf); + context->timer_b = load_int8(buf); + context->sub_timer_b = load_int8(buf); + context->env_counter = load_int16(buf); + context->current_op = load_int8(buf); + if (context->current_op >= NUM_OPERATORS) { + context->current_op = 0; + } + context->current_env_op = load_int8(buf); + if (context->current_env_op >= NUM_OPERATORS) { + context->current_env_op = 0; + } + context->lfo_counter = load_int8(buf); + context->csm_keyon = load_int8(buf); + context->status = load_int8(buf); + context->selected_reg = load_int8(buf); + context->selected_part = load_int8(buf); + context->current_cycle = load_int32(buf); + context->write_cycle = load_int32(buf); + context->busy_cycles = load_int32(buf); +} diff -r 957325c990d5 -r 4e5797b3935a ym2612.h --- a/ym2612.h Fri Jul 07 21:44:49 2017 -0700 +++ b/ym2612.h Sun Aug 06 00:06:36 2017 -0700 @@ -8,6 +8,7 @@ #include #include +#include "serialize.h" #define NUM_PART_REGS (0xB7-0x30) #define NUM_CHANNELS 6 @@ -143,6 +144,8 @@ uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile); void ym_print_channel_info(ym2612_context *context, int channel); void ym_print_timer_info(ym2612_context *context); +void ym_serialize(ym2612_context *context, serialize_buffer *buf); +void ym_deserialize(deserialize_buffer *buf, void *vcontext); #endif //YM2612_H_ diff -r 957325c990d5 -r 4e5797b3935a z80_to_x86.c --- a/z80_to_x86.c Fri Jul 07 21:44:49 2017 -0700 +++ b/z80_to_x86.c Sun Aug 06 00:06:36 2017 -0700 @@ -3830,3 +3830,104 @@ } } +void z80_serialize(z80_context *context, serialize_buffer *buf) +{ + for (int i = 0; i <= Z80_A; i++) + { + save_int8(buf, context->regs[i]); + } + uint8_t f = context->flags[ZF_S]; + f <<= 1; + f |= context->flags[ZF_Z] ; + f <<= 2; + f |= context->flags[ZF_H]; + f <<= 2; + f |= context->flags[ZF_PV]; + f <<= 1; + f |= context->flags[ZF_N]; + f <<= 1; + f |= context->flags[ZF_C]; + f |= context->flags[ZF_XY] & 0x28; + save_int8(buf, f); + for (int i = 0; i <= Z80_A; i++) + { + save_int8(buf, context->alt_regs[i]); + } + f = context->alt_flags[ZF_S]; + f <<= 1; + f |= context->alt_flags[ZF_Z] ; + f <<= 2; + f |= context->alt_flags[ZF_H]; + f <<= 2; + f |= context->alt_flags[ZF_PV]; + f <<= 1; + f |= context->alt_flags[ZF_N]; + f <<= 1; + f |= context->alt_flags[ZF_C]; + f |= context->flags[ZF_XY] & 0x28; + save_int8(buf, f); + save_int16(buf, context->pc); + save_int16(buf, context->sp); + save_int8(buf, context->im); + save_int8(buf, context->iff1); + save_int8(buf, context->iff2); + save_int8(buf, context->int_is_nmi); + save_int32(buf, context->current_cycle); + save_int32(buf, context->int_cycle); + save_int32(buf, context->int_enable_cycle); + save_int32(buf, context->int_pulse_start); + save_int32(buf, context->int_pulse_end); + save_int32(buf, context->nmi_start); +} + +void z80_deserialize(deserialize_buffer *buf, void *vcontext) +{ + z80_context *context = vcontext; + for (int i = 0; i <= Z80_A; i++) + { + context->regs[i] = load_int8(buf); + } + uint8_t f = load_int8(buf); + context->flags[ZF_XY] = f & 0x28; + context->flags[ZF_C] = f & 1; + f >>= 1; + context->flags[ZF_N] = f & 1; + f >>= 1; + context->flags[ZF_PV] = f & 1; + f >>= 2; + context->flags[ZF_H] = f & 1; + f >>= 2; + context->flags[ZF_Z] = f & 1; + f >>= 1; + context->flags[ZF_S] = f; + for (int i = 0; i <= Z80_A; i++) + { + context->alt_regs[i] = load_int8(buf); + } + f = load_int8(buf); + context->alt_flags[ZF_XY] = f & 0x28; + context->alt_flags[ZF_C] = f & 1; + f >>= 1; + context->alt_flags[ZF_N] = f & 1; + f >>= 1; + context->alt_flags[ZF_PV] = f & 1; + f >>= 2; + context->alt_flags[ZF_H] = f & 1; + f >>= 2; + context->alt_flags[ZF_Z] = f & 1; + f >>= 1; + context->alt_flags[ZF_S] = f; + context->pc = load_int16(buf); + context->sp = load_int16(buf); + context->im = load_int8(buf); + context->iff1 = load_int8(buf); + context->iff2 = load_int8(buf); + context->int_is_nmi = load_int8(buf); + context->current_cycle = load_int32(buf); + context->int_cycle = load_int32(buf); + context->int_enable_cycle = load_int32(buf); + context->int_pulse_start = load_int32(buf); + context->int_pulse_end = load_int32(buf); + context->nmi_start = load_int32(buf); +} + diff -r 957325c990d5 -r 4e5797b3935a z80_to_x86.h --- a/z80_to_x86.h Fri Jul 07 21:44:49 2017 -0700 +++ b/z80_to_x86.h Sun Aug 06 00:06:36 2017 -0700 @@ -7,6 +7,7 @@ #define Z80_TO_X86_H_ #include "z80inst.h" #include "backend.h" +#include "serialize.h" #define ZNUM_MEM_AREAS 4 #ifdef Z80_LOG_ADDRESS @@ -108,6 +109,8 @@ void z80_assert_nmi(z80_context *context, uint32_t cycle); uint8_t z80_get_busack(z80_context * context, uint32_t cycle); void z80_adjust_cycles(z80_context * context, uint32_t deduction); +void z80_serialize(z80_context *context, serialize_buffer *buf); +void z80_deserialize(deserialize_buffer *buf, void *vcontext); #endif //Z80_TO_X86_H_