# HG changeset patch # User Michael Pavone # Date 1673217744 28800 # Node ID 9ead0fe69d9b193ad8150a03f7ad23db8068d635 # Parent 3b5fef8964755bf548f7cfa05fac3560e11b5d1d Implement savestate support for Sega CD diff -r 3b5fef896475 -r 9ead0fe69d9b cdd_fader.c --- a/cdd_fader.c Sun Jan 08 14:20:43 2023 -0800 +++ b/cdd_fader.c Sun Jan 08 14:42:24 2023 -0800 @@ -76,3 +76,24 @@ } } } + +void cdd_fader_serialize(cdd_fader *fader, serialize_buffer *buf) +{ + save_int16(buf, fader->cur_attenuation); + save_int16(buf, fader->dst_attenuation); + save_int16(buf, fader->attenuation_step); + save_int8(buf, fader->flags); + save_buffer8(buf, fader->bytes, sizeof(fader->bytes)); + save_int8(buf, fader->byte_counter); +} + +void cdd_fader_deserialize(deserialize_buffer *buf, void *vfader) +{ + cdd_fader *fader = vfader; + fader->cur_attenuation = load_int16(buf); + fader->dst_attenuation = load_int16(buf); + fader->attenuation_step = load_int16(buf); + fader->flags = load_int8(buf); + load_buffer8(buf, fader->bytes, sizeof(fader->bytes)); + fader->byte_counter = load_int8(buf); +} diff -r 3b5fef896475 -r 9ead0fe69d9b cdd_fader.h --- a/cdd_fader.h Sun Jan 08 14:20:43 2023 -0800 +++ b/cdd_fader.h Sun Jan 08 14:42:24 2023 -0800 @@ -2,6 +2,7 @@ #define CDD_FADER_H_ #include "render_audio.h" +#include "serialize.h" typedef struct { audio_source *audio; @@ -19,5 +20,7 @@ void cdd_fader_attenuation_write(cdd_fader *fader, uint16_t attenuation); void cdd_fader_data(cdd_fader *fader, uint8_t byte); void cdd_fader_pause(cdd_fader *fader); +void cdd_fader_serialize(cdd_fader *fader, serialize_buffer *buf); +void cdd_fader_deserialize(deserialize_buffer *buf, void *vfader); #endif //CDD_FADER_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b cdd_mcu.c --- a/cdd_mcu.c Sun Jan 08 14:20:43 2023 -0800 +++ b/cdd_mcu.c Sun Jan 08 14:42:24 2023 -0800 @@ -809,3 +809,90 @@ context->next_subcode_cycle -= cd_deduction; } } + +void cdd_mcu_serialize(cdd_mcu *context, serialize_buffer *buf) +{ + save_int32(buf, context->cycle); + save_int32(buf, context->next_int_cycle); + save_int32(buf, context->next_subcode_int_cycle); + save_int32(buf, context->last_sector_cycle); + save_int32(buf, context->last_nibble_cycle); + save_int32(buf, context->next_byte_cycle); + save_int32(buf, context->next_subcode_cycle); + save_int8(buf, context->current_status_nibble); + save_int8(buf, context->current_cmd_nibble); + save_int16(buf, context->current_sector_byte); + save_int8(buf, context->current_subcode_byte); + save_int8(buf, context->current_subcode_dest); + save_int32(buf, context->head_pba); + save_int32(buf, context->seek_pba); + save_int32(buf, context->pause_pba); + save_int32(buf, context->coarse_seek); + save_buffer8(buf, (uint8_t *)&context->cmd_buffer, sizeof(context->cmd_buffer)); + save_buffer8(buf, (uint8_t *)&context->status_buffer, sizeof(context->status_buffer)); + save_int8(buf, context->requested_format); + save_int8(buf, context->status); + save_int8(buf, context->error_status); + save_int8(buf, context->requested_track); + save_int8(buf, context->cmd_recv_wait); + save_int8(buf, context->cmd_recv_pending); + save_int8(buf, context->int_pending); + save_int8(buf, context->subcode_int_pending); + save_int8(buf, context->toc_valid); + save_int8(buf, context->first_cmd_received); + save_int8(buf, context->seeking); + save_int8(buf, context->in_fake_pregap); +} + +static int sign_extend8(uint8_t value) +{ + if (value & 0x80) { + return value | 0xFFFFFF00; + } else { + return value; + } +} + +static int sign_extend16(uint16_t value) +{ + if (value & 0x8000) { + return value | 0xFFFF0000; + } else { + return value; + } +} + +void cdd_mcu_deserialize(deserialize_buffer *buf, void *vcontext) +{ + cdd_mcu *context = vcontext; + context->cycle = load_int32(buf); + context->next_int_cycle = load_int32(buf); + context->next_subcode_int_cycle = load_int32(buf); + context->last_sector_cycle = load_int32(buf); + context->last_nibble_cycle = load_int32(buf); + context->next_byte_cycle = load_int32(buf); + context->next_subcode_cycle = load_int32(buf); + context->current_status_nibble = sign_extend8(load_int8(buf)); + context->current_cmd_nibble = sign_extend8(load_int8(buf)); + context->current_sector_byte = sign_extend16(load_int16(buf)); + context->current_subcode_byte = sign_extend8(load_int8(buf)); + context->current_subcode_dest = sign_extend8(load_int8(buf)); + context->head_pba = load_int32(buf); + context->seek_pba = load_int32(buf); + context->pause_pba = load_int32(buf); + context->coarse_seek = load_int32(buf); + load_buffer8(buf, (uint8_t *)&context->cmd_buffer, sizeof(context->cmd_buffer)); + load_buffer8(buf, (uint8_t *)&context->status_buffer, sizeof(context->status_buffer)); + context->requested_format = load_int8(buf); + context->status = load_int8(buf); + context->error_status = load_int8(buf); + context->requested_track = load_int8(buf); + context->cmd_recv_wait = load_int8(buf); + context->cmd_recv_pending = load_int8(buf); + context->int_pending = load_int8(buf); + context->subcode_int_pending = load_int8(buf); + context->toc_valid = load_int8(buf); + context->first_cmd_received = load_int8(buf); + context->seeking = load_int8(buf); + context->in_fake_pregap = load_int8(buf); +} diff -r 3b5fef896475 -r 9ead0fe69d9b cdd_mcu.h --- a/cdd_mcu.h Sun Jan 08 14:20:43 2023 -0800 +++ b/cdd_mcu.h Sun Jan 08 14:42:24 2023 -0800 @@ -3,6 +3,7 @@ #include "system.h" #include "lc8951.h" #include "cdd_fader.h" +#include "serialize.h" typedef enum { SF_ABSOLUTE, @@ -172,5 +173,7 @@ void cdd_hock_disabled(cdd_mcu *context); void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array); void cdd_mcu_adjust_cycle(cdd_mcu *context, uint32_t deduction); +void cdd_mcu_serialize(cdd_mcu *context, serialize_buffer *buf); +void cdd_mcu_deserialize(deserialize_buffer *buf, void *vcontext); #endif //CD_MCU_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b cdimage.c --- a/cdimage.c Sun Jan 08 14:20:43 2023 -0800 +++ b/cdimage.c Sun Jan 08 14:42:24 2023 -0800 @@ -561,3 +561,41 @@ media->read_subcodes = bin_subcode_read; return media->size; } + +void cdimage_serialize(system_media *media, serialize_buffer *buf) +{ + if (media->type != MEDIA_CDROM) { + return; + } + save_int32(buf, media->cur_track); + save_int32(buf, media->cur_sector); + if (media->cur_track < media->num_tracks && media->tracks[media->cur_track].f) { + save_int32(buf, ftell(media->tracks[media->cur_track].f)); + } else { + save_int32(buf, 0); + } + save_int8(buf, media->in_fake_pregap); + save_int8(buf, media->byte_storage); + if (media->tmp_buffer) { + save_buffer8(buf, media->tmp_buffer, 96); + } +} + +void cdimage_deserialize(deserialize_buffer *buf, void *vmedia) +{ + system_media *media = vmedia; + if (media->type != MEDIA_CDROM) { + return; + } + media->cur_track = load_int32(buf); + media->cur_sector = load_int32(buf); + uint32_t seekpos = load_int32(buf); + if (media->cur_track < media->num_tracks && media->tracks[media->cur_track].f) { + fseek(media->tracks[media->cur_track].f, seekpos, SEEK_SET); + } + media->in_fake_pregap = load_int8(buf); + media->byte_storage = load_int8(buf); + if (media->tmp_buffer) { + load_buffer8(buf, media->tmp_buffer, 96); + } +} diff -r 3b5fef896475 -r 9ead0fe69d9b cdimage.h --- a/cdimage.h Sun Jan 08 14:20:43 2023 -0800 +++ b/cdimage.h Sun Jan 08 14:42:24 2023 -0800 @@ -4,5 +4,7 @@ uint8_t parse_cue(system_media *media); uint8_t parse_toc(system_media *media); uint32_t make_iso_media(system_media *media, const char *filename); +void cdimage_serialize(system_media *media, serialize_buffer *buf); +void cdimage_deserialize(deserialize_buffer *buf, void *vmedia); #endif //CUE_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b genesis.c --- a/genesis.c Sun Jan 08 14:20:43 2023 -0800 +++ b/genesis.c Sun Jan 08 14:42:24 2023 -0800 @@ -81,6 +81,9 @@ save_int8(buf, gen->z80->reset); save_int8(buf, gen->z80->busreq); save_int16(buf, gen->z80_bank_reg); + //I think the refresh logic may technically be part of the VDP, but whatever + save_int32(buf, gen->last_sync_cycle); + save_int32(buf, gen->refresh_counter); end_section(buf); start_section(buf, SECTION_SEGA_IO_1); @@ -117,6 +120,10 @@ cart_serialize(&gen->header, buf); } + if (gen->expansion) { + segacd_context *cd = gen->expansion; + segacd_serialize(cd, buf, all); + } } static uint8_t *serialize(system_header *sys, size_t *size_out) @@ -182,6 +189,15 @@ gen->z80->reset = load_int8(buf); gen->z80->busreq = load_int8(buf); gen->z80_bank_reg = load_int16(buf) & 0x1FF; + if ((buf->size - buf->cur_pos) >= 2 * sizeof(uint32_t)) { + gen->last_sync_cycle = load_int32(buf); + gen->refresh_counter = load_int32(buf); + } else { + //save state is from an older version of BlastEm that lacks these fields + //set them to reasonable values + gen->last_sync_cycle = gen->m68k->current_cycle; + gen->refresh_counter = 0; + } } static void tmss_deserialize(deserialize_buffer *buf, void *vgen) @@ -194,6 +210,7 @@ static void adjust_int_cycle(m68k_context * context, vdp_context * v_context); static void check_tmss_lock(genesis_context *gen); static void toggle_tmss_rom(genesis_context *gen); +#include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen) { register_section_handler(buf, (section_handler){.fun = m68k_deserialize, .data = gen->m68k}, SECTION_68000); @@ -209,6 +226,10 @@ register_section_handler(buf, (section_handler){.fun = zram_deserialize, .data = gen}, SECTION_SOUND_RAM); register_section_handler(buf, (section_handler){.fun = cart_deserialize, .data = gen}, SECTION_MAPPER); register_section_handler(buf, (section_handler){.fun = tmss_deserialize, .data = gen}, SECTION_TMSS); + if (gen->expansion) { + segacd_context *cd = gen->expansion; + segacd_register_section_handlers(cd, buf); + } uint8_t tmss_old = gen->tmss; gen->tmss = 0xFF; while (buf->cur_pos < buf->size) @@ -230,19 +251,24 @@ } update_z80_bank_pointer(gen); adjust_int_cycle(gen->m68k, gen->vdp); + //HACK: Fix this once PC/IR is represented in a better way in 68K core + //Would be better for this hack to live in side the 68K core itself, but it's better to do it + //after RAM has been loaded to avoid any unnecessary retranslation + gen->m68k->resume_pc = get_native_address_trans(gen->m68k, gen->m68k->last_prefetch_address); + if (gen->expansion) { + segacd_context *cd = gen->expansion; + cd->m68k->resume_pc = get_native_address_trans(cd->m68k, cd->m68k->last_prefetch_address); + } free(buf->handlers); buf->handlers = NULL; } -#include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up static void deserialize(system_header *sys, uint8_t *data, size_t size) { genesis_context *gen = (genesis_context *)sys; deserialize_buffer buffer; init_deserialize(&buffer, data, size); genesis_deserialize(&buffer, gen); - //HACK: Fix this once PC/IR is represented in a better way in 68K core - gen->m68k->resume_pc = get_native_address_trans(gen->m68k, gen->m68k->last_prefetch_address); } uint16_t read_dma_value(uint32_t address) @@ -424,14 +450,8 @@ //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); } -//My refresh emulation isn't currently good enough and causes more problems than it solves -#define REFRESH_EMULATION -#ifdef REFRESH_EMULATION #define REFRESH_INTERVAL 128 #define REFRESH_DELAY 2 -uint32_t last_sync_cycle; -uint32_t refresh_counter; -#endif #include #define ADJUST_BUFFER (8*MCLKS_LINE*313) @@ -442,14 +462,12 @@ genesis_context * gen = context->system; vdp_context * v_context = gen->vdp; z80_context * z_context = gen->z80; -#ifdef REFRESH_EMULATION //lame estimation of refresh cycle delay - refresh_counter += context->current_cycle - last_sync_cycle; + gen->refresh_counter += context->current_cycle - gen->last_sync_cycle; if (!gen->bus_busy) { - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); } - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); -#endif + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); uint32_t mclks = context->current_cycle; sync_z80(gen, mclks); @@ -587,9 +605,7 @@ context->sync_cycle = context->current_cycle + 1; } } -#ifdef REFRESH_EMULATION - last_sync_cycle = context->current_cycle; -#endif + gen->last_sync_cycle = context->current_cycle; return context; } @@ -604,13 +620,13 @@ } vdp_port &= 0x1F; //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); -#ifdef REFRESH_EMULATION + //do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; -#endif + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; + sync_components(context, 0); vdp_context *v_context = gen->vdp; uint32_t before_cycle = v_context->cycles; @@ -687,18 +703,17 @@ } else { vdp_test_port_write(gen->vdp, value); } -#ifdef REFRESH_EMULATION - last_sync_cycle -= 4 * MCLKS_PER_68K; + + gen->last_sync_cycle -= 4 * MCLKS_PER_68K; //refresh may have happened while we were waiting on the VDP, //so advance refresh_counter but don't add any delays if (vdp_port >= 4 && vdp_port < 8 && v_context->cycles != before_cycle) { - refresh_counter = 0; + gen->refresh_counter = 0; } else { - refresh_counter += (context->current_cycle - last_sync_cycle); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->refresh_counter += (context->current_cycle - gen->last_sync_cycle); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); } - last_sync_cycle = context->current_cycle; -#endif + gen->last_sync_cycle = context->current_cycle; return context; } @@ -746,13 +761,13 @@ } vdp_port &= 0x1F; uint16_t value; -#ifdef REFRESH_EMULATION + //do refresh check here so we can avoid adding a penalty for a refresh that happens during a VDP access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; -#endif + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; + sync_components(context, 0); vdp_context * v_context = gen->vdp; uint32_t before_cycle = context->current_cycle; @@ -778,14 +793,13 @@ sync_z80(gen, context->current_cycle); gen->bus_busy = 0; } -#ifdef REFRESH_EMULATION - last_sync_cycle -= 4 * MCLKS_PER_68K; + + gen->last_sync_cycle -= 4 * MCLKS_PER_68K; //refresh may have happened while we were waiting on the VDP, //so advance refresh_counter but don't add any delays - refresh_counter += (context->current_cycle - last_sync_cycle); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; -#endif + gen->refresh_counter += (context->current_cycle - gen->last_sync_cycle); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; return value; } @@ -844,13 +858,13 @@ static m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) { genesis_context * gen = context->system; -#ifdef REFRESH_EMULATION + //do refresh check here so we can avoid adding a penalty for a refresh that happens during an IO area access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle - 4*MCLKS_PER_68K; -#endif + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle - 4*MCLKS_PER_68K; + if (location < 0x10000) { //Access to Z80 memory incurs a one 68K cycle wait state context->current_cycle += MCLKS_PER_68K; @@ -976,11 +990,10 @@ } } } -#ifdef REFRESH_EMULATION + //no refresh delays during IO access - refresh_counter += context->current_cycle - last_sync_cycle; - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); -#endif + gen->refresh_counter += context->current_cycle - gen->last_sync_cycle; + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); return context; } @@ -1004,13 +1017,13 @@ { uint8_t value; genesis_context *gen = context->system; -#ifdef REFRESH_EMULATION + //do refresh check here so we can avoid adding a penalty for a refresh that happens during an IO area access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle - 4*MCLKS_PER_68K; -#endif + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle - 4*MCLKS_PER_68K; + if (location < 0x10000) { //Access to Z80 memory incurs a one 68K cycle wait state context->current_cycle += MCLKS_PER_68K; @@ -1106,12 +1119,11 @@ } } } -#ifdef REFRESH_EMULATION + //no refresh delays during IO access - refresh_counter += context->current_cycle - last_sync_cycle; - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; -#endif + gen->refresh_counter += context->current_cycle - gen->last_sync_cycle; + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; return value; } @@ -1224,16 +1236,14 @@ { m68k_context *context = vcontext; genesis_context *gen = context->system; -#ifdef REFRESH_EMULATION if (location >= 0x800000) { //do refresh check here so we can avoid adding a penalty for a refresh that happens during an IO area access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter += 4*MCLKS_PER_68K; - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter += 4*MCLKS_PER_68K; + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; } -#endif if (location < 0x800000 || (location >= 0xA13000 && location < 0xA13100) || (location >= 0xA12000 && location < 0xA12100)) { //Only called if the cart/exp doesn't have a more specific handler for this region @@ -1289,16 +1299,15 @@ { m68k_context *context = vcontext; genesis_context *gen = context->system; -#ifdef REFRESH_EMULATION if (location >= 0x800000) { //do refresh check here so we can avoid adding a penalty for a refresh that happens during an IO area access - refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - last_sync_cycle; - context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); - refresh_counter += 4*MCLKS_PER_68K; - refresh_counter = refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); - last_sync_cycle = context->current_cycle; + gen->refresh_counter += context->current_cycle - 4*MCLKS_PER_68K - gen->last_sync_cycle; + context->current_cycle += REFRESH_DELAY * MCLKS_PER_68K * (gen->refresh_counter / (MCLKS_PER_68K * REFRESH_INTERVAL)); + gen->refresh_counter += 4*MCLKS_PER_68K; + gen->refresh_counter = gen->refresh_counter % (MCLKS_PER_68K * REFRESH_INTERVAL); + gen->last_sync_cycle = context->current_cycle; } -#endif + uint8_t has_tmss = gen->version_reg & 0xF; if (has_tmss && (location == 0xA14000 || location == 0xA14002)) { gen->tmss_lock[location >> 1 & 1] = value; @@ -1412,8 +1421,6 @@ if (load_from_file(&state, statepath)) { genesis_deserialize(&state, gen); free(state.data); - //HACK - pc = gen->m68k->last_prefetch_address; ret = 1; } else { strcpy(statepath + strlen(statepath)-strlen("state"), "gst"); @@ -1421,7 +1428,7 @@ ret = pc != 0; } if (ret) { - gen->m68k->resume_pc = get_native_address_trans(gen->m68k, pc); + debug_message("Loaded state from %s\n", statepath); } done: free(statepath); diff -r 3b5fef896475 -r 9ead0fe69d9b genesis.h --- a/genesis.h Sun Jan 08 14:20:43 2023 -0800 +++ b/genesis.h Sun Jan 08 14:42:24 2023 -0800 @@ -62,6 +62,8 @@ uint32_t last_flush_cycle; uint32_t soft_flush_cycles; uint32_t tmss_write_offset; + uint32_t last_sync_cycle; + uint32_t refresh_counter; uint16_t z80_bank_reg; uint16_t tmss_lock[2]; uint16_t mapper_start_index; diff -r 3b5fef896475 -r 9ead0fe69d9b gst.c --- a/gst.c Sun Jan 08 14:20:43 2023 -0800 +++ b/gst.c Sun Jan 08 14:42:24 2023 -0800 @@ -412,6 +412,7 @@ return 1; } +#include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up uint32_t load_gst(genesis_context * gen, char * fname) { char buffer[4096]; @@ -461,6 +462,7 @@ i++; } } + gen->m68k->resume_pc = get_native_address_trans(gen->m68k, pc); fclose(gstfile); return pc; diff -r 3b5fef896475 -r 9ead0fe69d9b lc8951.c --- a/lc8951.c Sun Jan 08 14:20:43 2023 -0800 +++ b/lc8951.c Sun Jan 08 14:42:24 2023 -0800 @@ -428,3 +428,48 @@ } printf("cycle is now %u, decode_end %u, transfer_end %u\n", context->cycle, context->decode_end, context->transfer_end); } + +void lc8951_serialize(lc8951 *context, serialize_buffer *buf) +{ + save_int32(buf, context->cycle); + save_int32(buf, context->decode_end); + save_int32(buf, context->transfer_end); + save_int32(buf, context->next_byte_cycle); + save_int16(buf, context->sector_counter); + save_buffer8(buf, context->buffer, sizeof(context->buffer)); + save_buffer8(buf, context->regs, sizeof(context->regs)); + save_buffer8(buf, context->comin, sizeof(context->comin)); + save_int16(buf, context->dac); + save_int8(buf, context->comin_write); + save_int8(buf, context->comin_count); + save_int8(buf, context->ifctrl); + save_int8(buf, context->ctrl0); + save_int8(buf, context->ctrl1); + save_int8(buf, context->ar); + save_int8(buf, context->ar_mask); + save_int8(buf, context->triggered); + save_int8(buf, context->sync_counter); +} + +void lc8951_deserialize(deserialize_buffer *buf, void *vcontext) +{ + lc8951 *context = vcontext; + context->cycle = load_int32(buf); + context->decode_end = load_int32(buf); + context->transfer_end = load_int32(buf); + context->next_byte_cycle = load_int32(buf); + context->sector_counter = load_int16(buf); + load_buffer8(buf, context->buffer, sizeof(context->buffer)); + load_buffer8(buf, context->regs, sizeof(context->regs)); + load_buffer8(buf, context->comin, sizeof(context->comin)); + context->dac = load_int16(buf); + context->comin_write = load_int8(buf); + context->comin_count = load_int8(buf); + context->ifctrl = load_int8(buf); + context->ctrl0 = load_int8(buf); + context->ctrl1 = load_int8(buf); + context->ar = load_int8(buf); + context->ar_mask = load_int8(buf); + context->triggered = load_int8(buf); + context->sync_counter = load_int8(buf); +} diff -r 3b5fef896475 -r 9ead0fe69d9b lc8951.h --- a/lc8951.h Sun Jan 08 14:20:43 2023 -0800 +++ b/lc8951.h Sun Jan 08 14:42:24 2023 -0800 @@ -2,6 +2,7 @@ #define LC8951_H_ #include +#include "serialize.h" typedef uint8_t (*lcd8951_byte_recv_fun)(void *data, uint8_t byte); @@ -43,5 +44,7 @@ uint32_t lc8951_next_interrupt(lc8951 *context); void lc8951_resume_transfer(lc8951 *context, uint32_t cycle); void lc8951_adjust_cycles(lc8951 *context, uint32_t deduction); +void lc8951_serialize(lc8951 *context, serialize_buffer *buf); +void lc8951_deserialize(deserialize_buffer *buf, void *vcontext); #endif //LC8951_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b rf5c164.c --- a/rf5c164.c Sun Jan 08 14:20:43 2023 -0800 +++ b/rf5c164.c Sun Jan 08 14:42:24 2023 -0800 @@ -262,3 +262,50 @@ pcm->channels[i].scope_channel = scope_add_channel(scope, names[i], 50000000 / (pcm->clock_step * 96)); } } + +void rf5c164_serialize(rf5c164* pcm, serialize_buffer *buf) +{ + save_int32(buf, pcm->cycle); + save_buffer16(buf, pcm->ram, sizeof(pcm->ram)/sizeof(*pcm->ram)); + save_int16(buf, pcm->ram_bank); + save_int16(buf, pcm->pending_address); + save_int32(buf, pcm->left); + save_int32(buf, pcm->right); + for (int i = 0; i < 8; i++) + { + rf5c164_channel *chan = pcm->channels + i; + save_int32(buf, chan->cur_ptr); + save_buffer8(buf, chan->regs, sizeof(chan->regs)); + save_int8(buf, chan->sample); + } + save_int8(buf, pcm->pending_byte); + save_int8(buf, pcm->channel_enable); + save_int8(buf, pcm->selected_channel); + save_int8(buf, pcm->cur_channel); + save_int8(buf, pcm->step); + save_int8(buf, pcm->flags); +} + +void rf5c164_deserialize(deserialize_buffer *buf, void *vpcm) +{ + rf5c164 *pcm = vpcm; + pcm->cycle = load_int32(buf); + load_buffer16(buf, pcm->ram, sizeof(pcm->ram)/sizeof(*pcm->ram)); + pcm->ram_bank = load_int16(buf); + pcm->pending_address = load_int16(buf); + pcm->left = load_int32(buf); + pcm->right = load_int32(buf); + for (int i = 0; i < 8; i++) + { + rf5c164_channel *chan = pcm->channels + i; + chan->cur_ptr = load_int32(buf); + load_buffer8(buf, chan->regs, sizeof(chan->regs)); + chan->sample = load_int8(buf); + } + pcm->pending_byte = load_int8(buf); + pcm->channel_enable = load_int8(buf); + pcm->selected_channel = load_int8(buf); + pcm->cur_channel = load_int8(buf); + pcm->step = load_int8(buf); + pcm->flags = load_int8(buf); +} diff -r 3b5fef896475 -r 9ead0fe69d9b rf5c164.h --- a/rf5c164.h Sun Jan 08 14:20:43 2023 -0800 +++ b/rf5c164.h Sun Jan 08 14:42:24 2023 -0800 @@ -3,6 +3,7 @@ #include #include "render_audio.h" #include "oscilloscope.h" +#include "serialize.h" typedef struct { uint32_t cur_ptr; @@ -39,5 +40,7 @@ void rf5c164_write(rf5c164* pcm, uint16_t address, uint8_t value); uint8_t rf5c164_read(rf5c164* pcm, uint16_t address); void rf5c164_enable_scope(rf5c164* pcm, oscilloscope *scope); +void rf5c164_serialize(rf5c164* pcm, serialize_buffer *buf); +void rf5c164_deserialize(deserialize_buffer *buf, void *vpcm); #endif //RF5C164_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b segacd.c --- a/segacd.c Sun Jan 08 14:20:43 2023 -0800 +++ b/segacd.c Sun Jan 08 14:42:24 2023 -0800 @@ -7,6 +7,7 @@ #include "debug.h" #include "gdb_remote.h" #include "blastem.h" +#include "cdimage.h" #define SCD_MCLKS 50000000 #define SCD_PERIPH_RESET_CLKS (SCD_MCLKS / 10) @@ -1057,14 +1058,17 @@ context->current_cycle += num_refresh * REFRESH_DELAY; scd_peripherals_run(cd, context->current_cycle); - if (address && cd->enter_debugger) { - genesis_context *gen = cd->genesis; - cd->enter_debugger = 0; - if (gen->header.debugger_type == DEBUGGER_NATIVE) { - debugger(context, address); - } else { - gdb_debug_enter(context, address); + if (address) { + if (cd->enter_debugger) { + genesis_context *gen = cd->genesis; + cd->enter_debugger = 0; + if (gen->header.debugger_type == DEBUGGER_NATIVE) { + debugger(context, address); + } else { + gdb_debug_enter(context, address); + } } + cd->m68k_pc = address; } switch (context->int_ack) { @@ -1453,14 +1457,8 @@ memcpy(cd->rom_mut, cd->rom, adjusted_size); cd->rom_mut[0x72/2] = 0xFFFF; - //memset(info, 0, sizeof(*info)); - //tern_node *db = get_rom_db(); - //*info = configure_rom(db, media->buffer, media->size, media->chain ? media->chain->buffer : NULL, media->chain ? media->chain->size : 0, NULL, 0); - cd->prog_ram = calloc(512*1024, 1); cd->word_ram = calloc(256*1024, 1); - cd->pcm_ram = calloc(64*1024, 1); - //TODO: Load state from file cd->bram = calloc(8*1024, 1); @@ -1497,12 +1495,144 @@ m68k_options_free(cd->m68k->options); free(cd->m68k); free(cd->bram); - free(cd->pcm_ram); free(cd->word_ram); free(cd->prog_ram); free(cd->rom_mut); } +void segacd_serialize(segacd_context *cd, serialize_buffer *buf, uint8_t all) +{ + if (all) { + start_section(buf, SECTION_SUB_68000); + m68k_serialize(cd->m68k, cd->m68k_pc, buf); + end_section(buf); + + start_section(buf, SECTION_GATE_ARRAY); + save_buffer16(buf, cd->gate_array, sizeof(cd->gate_array)/sizeof(*cd->gate_array)); + save_buffer16(buf, cd->prog_ram, 256*1024); + save_buffer16(buf, cd->word_ram, 128*1024); + save_int16(buf, cd->rom_mut[0x72/2]); + save_int32(buf, cd->stopwatch_cycle); + save_int32(buf, cd->int2_cycle); + save_int32(buf, cd->graphics_int_cycle); + save_int32(buf, cd->periph_reset_cycle); + save_int32(buf, cd->last_refresh_cycle); + save_int32(buf, cd->graphics_cycle); + save_int32(buf, cd->base); + save_int32(buf, cd->graphics_x); + save_int32(buf, cd->graphics_y); + save_int32(buf, cd->graphics_dx); + save_int32(buf, cd->graphics_dy); + save_int32(buf, cd->graphics_dst_x); + save_buffer8(buf, cd->graphics_pixels, sizeof(cd->graphics_pixels)); + save_int8(buf, cd->timer_pending); + save_int8(buf, cd->timer_value); + save_int8(buf, cd->busreq); + save_int8(buf, cd->reset); + save_int8(buf, cd->need_reset); + save_int8(buf, cd->cdc_dst_low); + save_int8(buf, cd->cdc_int_ack); + save_int8(buf, cd->graphics_step); + save_int8(buf, cd->graphics_dst_y); + save_int8(buf, cd->main_has_word2m); + save_int8(buf, cd->main_swap_request); + save_int8(buf, cd->bank_toggle); + save_int8(buf, cd->sub_paused_wordram); + end_section(buf); + + start_section(buf, SECTION_CDD_MCU); + cdd_mcu_serialize(&cd->cdd, buf); + end_section(buf); + + start_section(buf, SECTION_LC8951); + lc8951_serialize(&cd->cdc, buf); + end_section(buf); + + start_section(buf, SECTION_CDROM); + cdimage_serialize(cd->cdd.media, buf); + end_section(buf); + } + start_section(buf, SECTION_RF5C164); + rf5c164_serialize(&cd->pcm, buf); + end_section(buf); + + start_section(buf, SECTION_CDD_FADER); + cdd_fader_serialize(&cd->fader, buf); + end_section(buf); +} + +static void gate_array_deserialize(deserialize_buffer *buf, void *vcd) +{ + segacd_context *cd = vcd; + load_buffer16(buf, cd->gate_array, sizeof(cd->gate_array)/sizeof(*cd->gate_array)); + load_buffer16(buf, cd->prog_ram, 256*1024); + load_buffer16(buf, cd->word_ram, 128*1024); + cd->rom_mut[0x72/2] = load_int16(buf); + cd->stopwatch_cycle = load_int32(buf); + cd->int2_cycle = load_int32(buf); + cd->graphics_int_cycle = load_int32(buf); + cd->periph_reset_cycle = load_int32(buf); + cd->last_refresh_cycle = load_int32(buf); + cd->graphics_cycle = load_int32(buf); + cd->base = load_int32(buf); + cd->graphics_x = load_int32(buf); + cd->graphics_y = load_int32(buf); + cd->graphics_dx = load_int32(buf); + cd->graphics_dy = load_int32(buf); + cd->graphics_dst_x = load_int32(buf); + load_buffer8(buf, cd->graphics_pixels, sizeof(cd->graphics_pixels)); + cd->timer_pending = load_int8(buf); + cd->timer_value = load_int8(buf); + cd->busreq = load_int8(buf); + cd->reset = load_int8(buf); + cd->need_reset = load_int8(buf); + cd->cdc_dst_low = load_int8(buf); + cd->cdc_int_ack = load_int8(buf); + cd->graphics_step = load_int8(buf); + cd->graphics_dst_y = load_int8(buf); + cd->main_has_word2m = load_int8(buf); + cd->main_swap_request = load_int8(buf); + cd->bank_toggle = load_int8(buf); + cd->sub_paused_wordram = load_int8(buf); + + if (cd->gate_array[GA_MEM_MODE] & BIT_MEM_MODE) { + //1M mode + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 1] = NULL; + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 2] = NULL; + cd->m68k->mem_pointers[0] = NULL; + cd->m68k->mem_pointers[1] = cd->bank_toggle ? cd->word_ram : cd->word_ram + 1; + } else { + //2M mode + if (cd->main_has_word2m) { + //main CPU has word ram + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 1] = cd->word_ram; + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 2] = cd->word_ram + 0x10000; + cd->m68k->mem_pointers[0] = NULL; + cd->m68k->mem_pointers[1] = NULL; + } else { + //sub cpu has word ram + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 1] = NULL; + cd->genesis->m68k->mem_pointers[cd->memptr_start_index + 2] = NULL; + cd->m68k->mem_pointers[0] = cd->word_ram; + cd->m68k->mem_pointers[1] = NULL; + } + } + + m68k_invalidate_code_range(cd->m68k, 0, 0x0E0000); + m68k_invalidate_code_range(cd->genesis->m68k, cd->base + 0x200000, cd->base + 0x240000); +} + +void segacd_register_section_handlers(segacd_context *cd, deserialize_buffer *buf) +{ + register_section_handler(buf, (section_handler){.fun = m68k_deserialize, .data = cd->m68k}, SECTION_SUB_68000); + register_section_handler(buf, (section_handler){.fun = gate_array_deserialize, .data = cd}, SECTION_GATE_ARRAY); + register_section_handler(buf, (section_handler){.fun = cdd_mcu_deserialize, .data = &cd->cdd}, SECTION_CDD_MCU); + register_section_handler(buf, (section_handler){.fun = lc8951_deserialize, .data = &cd->cdc}, SECTION_LC8951); + register_section_handler(buf, (section_handler){.fun = rf5c164_deserialize, .data = &cd->pcm}, SECTION_RF5C164); + register_section_handler(buf, (section_handler){.fun = cdd_fader_deserialize, .data = &cd->fader}, SECTION_CDD_FADER); + register_section_handler(buf, (section_handler){.fun = cdimage_deserialize, .data = cd->cdd.media}, SECTION_CDROM); +} + memmap_chunk *segacd_main_cpu_map(segacd_context *cd, uint8_t cart_boot, uint32_t *num_chunks) { static memmap_chunk main_cpu_map[] = { diff -r 3b5fef896475 -r 9ead0fe69d9b segacd.h --- a/segacd.h Sun Jan 08 14:20:43 2023 -0800 +++ b/segacd.h Sun Jan 08 14:42:24 2023 -0800 @@ -4,6 +4,7 @@ #include "genesis.h" #include "cdd_mcu.h" #include "rf5c164.h" +#include "serialize.h" typedef struct { m68k_context *m68k; @@ -14,7 +15,6 @@ uint16_t *rom_mut; //ROM with low 16-bit of HINT vector modified by register write uint16_t *prog_ram; uint16_t *word_ram; - uint8_t *pcm_ram; uint8_t *bram; uint32_t stopwatch_cycle; uint32_t int2_cycle; @@ -23,6 +23,7 @@ uint32_t last_refresh_cycle; uint32_t graphics_cycle; uint32_t base; + uint32_t m68k_pc; uint32_t graphics_x; uint32_t graphics_y; uint32_t graphics_dx; @@ -37,10 +38,6 @@ uint8_t reset; uint8_t need_reset; uint8_t memptr_start_index; - rf5c164 pcm; - lc8951 cdc; - cdd_mcu cdd; - cdd_fader fader; uint8_t cdc_dst_low; uint8_t cdc_int_ack; uint8_t graphics_step; @@ -50,6 +47,10 @@ uint8_t main_swap_request; uint8_t bank_toggle; uint8_t sub_paused_wordram; + rf5c164 pcm; + lc8951 cdc; + cdd_mcu cdd; + cdd_fader fader; } segacd_context; segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info); @@ -60,5 +61,7 @@ void scd_adjust_cycle(segacd_context *cd, uint32_t deduction); void scd_toggle_graphics_debug(segacd_context *cd); void segacd_set_speed_percent(segacd_context *cd, uint32_t percent); +void segacd_serialize(segacd_context *cd, serialize_buffer *buf, uint8_t all); +void segacd_register_section_handlers(segacd_context *cd, deserialize_buffer *buf); #endif //SEGACD_H_ diff -r 3b5fef896475 -r 9ead0fe69d9b serialize.h --- a/serialize.h Sun Jan 08 14:20:43 2023 -0800 +++ b/serialize.h Sun Jan 08 14:42:24 2023 -0800 @@ -43,7 +43,14 @@ SECTION_MAPPER, SECTION_EEPROM, SECTION_CART_RAM, - SECTION_TMSS + SECTION_TMSS, + SECTION_SUB_68000, + SECTION_GATE_ARRAY, + SECTION_CDD_MCU, + SECTION_LC8951, + SECTION_RF5C164, + SECTION_CDD_FADER, + SECTION_CDROM }; void init_serialize(serialize_buffer *buf);