summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile14
-rw-r--r--genesis.c123
-rw-r--r--io.c71
-rw-r--r--io.h3
-rw-r--r--m68k_core.c52
-rw-r--r--m68k_core.h3
-rw-r--r--menu.c34
-rw-r--r--psg.c32
-rw-r--r--psg.h3
-rw-r--r--serialize.c276
-rw-r--r--serialize.h69
-rw-r--r--vdp.c162
-rw-r--r--vdp.h5
-rw-r--r--ym2612.c114
-rw-r--r--ym2612.h3
-rw-r--r--z80_to_x86.c101
-rw-r--r--z80_to_x86.h3
17 files changed, 1049 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index d16ef71..b44fb88 100644
--- a/Makefile
+++ b/Makefile
@@ -127,7 +127,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
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 @@ blastem$(EXE) : $(MAINOBJS)
$(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 @@ zdis$(EXE) : zdis.o z80inst.o
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 --git a/genesis.c b/genesis.c
index aae2577..7e3c145 100644
--- a/genesis.c
+++ b/genesis.c
@@ -32,6 +32,98 @@ uint32_t MCLKS_PER_68K;
#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 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
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 @@ static void start_genesis(system_header *system, char *statefile)
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 @@ static void start_genesis(system_header *system, char *statefile)
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 --git a/io.c b/io.c
index 97f9d3d..71c04ae 100644
--- a/io.c
+++ b/io.c
@@ -15,6 +15,7 @@
#include <string.h>
#include <stdlib.h>
+#include "serialize.h"
#include "io.h"
#include "blastem.h"
#include "genesis.h"
@@ -2130,4 +2131,74 @@ uint8_t io_data_read(io_port * port, uint32_t current_cycle)
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 --git a/io.h b/io.h
index 5814c7a..6018753 100644
--- a/io.h
+++ b/io.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include "tern.h"
#include "romdb.h"
+#include "serialize.h"
enum {
IO_GAMEPAD2,
@@ -109,6 +110,8 @@ void handle_joy_added(int joystick);
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 --git a/m68k_core.c b/m68k_core.c
index 3ed8185..7a54966 100644
--- a/m68k_core.c
+++ b/m68k_core.c
@@ -9,6 +9,7 @@
#include "backend.h"
#include "gen.h"
#include "util.h"
+#include "serialize.h"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
@@ -1202,3 +1203,54 @@ m68k_context * init_68k_context(m68k_options * opts, m68k_reset_handler reset_ha
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 --git a/m68k_core.h b/m68k_core.h
index a18d706..529a8a7 100644
--- a/m68k_core.h
+++ b/m68k_core.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdio.h>
#include "backend.h"
+#include "serialize.h"
//#include "68kinst.h"
struct m68kinst;
@@ -116,6 +117,8 @@ uint32_t get_instruction_start(m68k_options *opts, uint32_t address);
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 --git a/menu.c b/menu.c
index f92bccf..751d8f3 100644
--- a/menu.c
+++ b/menu.c
@@ -346,8 +346,10 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
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 @@ void * menu_write_w(uint32_t address, void * context, uint16_t value)
cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
} 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, &ltime));
+ } 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, &ltime));
- } 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, &ltime));
+ } 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 @@ 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) {
- 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 @@ 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 *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 --git a/psg.c b/psg.c
index 48d220b..e4ba11d 100644
--- a/psg.c
+++ b/psg.c
@@ -151,3 +151,35 @@ void psg_run(psg_context * context, uint32_t cycles)
}
}
+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 --git a/psg.h b/psg.h
index d1b8541..0e11328 100644
--- a/psg.h
+++ b/psg.h
@@ -7,6 +7,7 @@
#define PSG_CONTEXT_H_
#include <stdint.h>
+#include "serialize.h"
typedef struct {
int16_t *audio_buffer;
@@ -38,6 +39,8 @@ void psg_free(psg_context *context);
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 --git a/serialize.c b/serialize.c
new file mode 100644
index 0000000..08ee7e8
--- /dev/null
+++ b/serialize.c
@@ -0,0 +1,276 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#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(&section, buf->data + buf->cur_pos, size);
+ buf->handlers[section_id].fun(&section, 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 --git a/serialize.h b/serialize.h
new file mode 100644
index 0000000..df57075
--- /dev/null
+++ b/serialize.h
@@ -0,0 +1,69 @@
+#ifndef SERIALIZE_H_
+#define SERIALIZE_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+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 --git a/vdp.c b/vdp.c
index 50cfd8a..898facb 100644
--- a/vdp.c
+++ b/vdp.c
@@ -2996,7 +2996,6 @@ int vdp_control_port_write(vdp_context * context, uint16_t value)
//
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_int_ack(vdp_context * context)
}
}
+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 --git a/vdp.h b/vdp.h
index 6c805c2..a8285c1 100644
--- a/vdp.h
+++ b/vdp.h
@@ -9,6 +9,7 @@
#include <stdint.h>
#include <stdio.h>
#include "system.h"
+#include "serialize.h"
#define VDP_REGS 24
#define CRAM_SIZE 64
@@ -199,11 +200,9 @@ typedef struct {
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_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t v
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 --git a/ym2612.c b/ym2612.c
index 7049740..836fc91 100644
--- a/ym2612.c
+++ b/ym2612.c
@@ -1058,3 +1058,117 @@ void ym_print_timer_info(ym2612_context *context)
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 --git a/ym2612.h b/ym2612.h
index 124e3e1..9f2bd25 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdio.h>
+#include "serialize.h"
#define NUM_PART_REGS (0xB7-0x30)
#define NUM_CHANNELS 6
@@ -143,6 +144,8 @@ uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile);
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 --git a/z80_to_x86.c b/z80_to_x86.c
index 5155e98..75b0b33 100644
--- a/z80_to_x86.c
+++ b/z80_to_x86.c
@@ -3830,3 +3830,104 @@ void zremove_breakpoint(z80_context * context, uint16_t address)
}
}
+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 --git a/z80_to_x86.h b/z80_to_x86.h
index 3663402..0a1f09c 100644
--- a/z80_to_x86.h
+++ b/z80_to_x86.h
@@ -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_clear_busreq(z80_context * context, uint32_t cycle);
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_