# HG changeset patch # User Michael Pavone # Date 1588147257 25200 # Node ID c3c62dbf1ceb2c283e3c252608cdaa172bb59a35 # Parent ba7231d2411c4a73305629a943d3f2cfa271386d WIP netplay support diff -r ba7231d2411c -r c3c62dbf1ceb Makefile --- a/Makefile Wed Apr 29 01:00:15 2020 -0700 +++ b/Makefile Wed Apr 29 01:00:57 2020 -0700 @@ -195,7 +195,7 @@ endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o render_audio.o +AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o @@ -213,7 +213,7 @@ MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ diff -r ba7231d2411c -r c3c62dbf1ceb blastem.c --- a/blastem.c Wed Apr 29 01:00:15 2020 -0700 +++ b/blastem.c Wed Apr 29 01:00:57 2020 -0700 @@ -30,6 +30,7 @@ #include "bindings.h" #include "menu.h" #include "zip.h" +#include "event_log.h" #ifndef DISABLE_NUKLEAR #include "nuklear_ui/blastem_nuklear.h" #endif @@ -430,6 +431,23 @@ update_title(game_system->info.name); } +char *parse_addr_port(char *arg) +{ + while (*arg && *arg != ':') { + ++arg; + } + if (!*arg) { + return NULL; + } + char *end; + int port = strtol(arg + 1, &end, 10); + if (port && !*end) { + *arg = 0; + return arg + 1; + } + return NULL; +} + int main(int argc, char ** argv) { set_exe_str(argv[0]); @@ -441,10 +459,12 @@ system_type stype = SYSTEM_UNKNOWN, force_stype = SYSTEM_UNKNOWN; char * romfname = NULL; char * statefile = NULL; + event_reader reader = {0}; debugger_type dtype = DEBUGGER_NATIVE; uint8_t start_in_debugger = 0; uint8_t fullscreen = FULLSCREEN_DEFAULT, use_gl = 1; uint8_t debug_target = 0; + char *port; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { @@ -468,6 +488,18 @@ dtype = DEBUGGER_GDB; start_in_debugger = 1; break; + case 'e': + i++; + if (i >= argc) { + fatal_error("-e must be followed by a file name\n"); + } + port = parse_addr_port(argv[i]); + if (port) { + event_log_tcp(argv[i], port); + } else { + event_log_file(argv[i]); + } + break; case 'f': fullscreen = !fullscreen; break; @@ -555,18 +587,24 @@ " -v Display version number and exit\n" " -l Log 68K code addresses (useful for assemblers)\n" " -y Log individual YM-2612 channels to WAVE files\n" + " -e FILE Write hardware event log to FILE\n" ); return 0; default: fatal_error("Unrecognized switch %s\n", argv[i]); } } else if (!loaded) { - if (!(cart.size = load_rom(argv[i], &cart.buffer, stype == SYSTEM_UNKNOWN ? &stype : NULL))) { - fatal_error("Failed to open %s for reading\n", argv[i]); + char *port = parse_addr_port(argv[i]); + if (port) { + init_event_reader_tcp(&reader, argv[i], port); + } else { + if (!(cart.size = load_rom(argv[i], &cart.buffer, stype == SYSTEM_UNKNOWN ? &stype : NULL))) { + fatal_error("Failed to open %s for reading\n", argv[i]); + } + cart.dir = path_dirname(argv[i]); + cart.name = basename_no_extension(argv[i]); + cart.extension = path_extension(argv[i]); } - cart.dir = path_dirname(argv[i]); - cart.name = basename_no_extension(argv[i]); - cart.extension = path_extension(argv[i]); romfname = argv[i]; loaded = 1; } else if (width < 0) { @@ -645,13 +683,22 @@ } if (loaded) { - if (stype == SYSTEM_UNKNOWN) { - stype = detect_system_type(&cart); + if (stype == SYSTEM_UNKNOWN || reader.socket) { + if (reader.socket) { + stype = reader_system_type(&reader); + } else { + stype = detect_system_type(&cart); + } } if (stype == SYSTEM_UNKNOWN) { fatal_error("Failed to detect system type for %s\n", romfname); } - current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region); + + if (reader.socket) { + current_system = alloc_config_player(stype, &reader); + } else { + current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region); + } if (!current_system) { fatal_error("Failed to configure emulated machine for %s\n", romfname); } diff -r ba7231d2411c -r c3c62dbf1ceb genesis.c --- a/genesis.c Wed Apr 29 01:00:15 2020 -0700 +++ b/genesis.c Wed Apr 29 01:00:57 2020 -0700 @@ -19,6 +19,7 @@ #include "bindings.h" #include "jcart.h" #include "config.h" +#include "event_log.h" #define MCLKS_NTSC 53693175 #define MCLKS_PAL 53203395 @@ -49,15 +50,17 @@ #define Z80_OPTS options #endif -void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc) +void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc, uint8_t all) { - 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); + if (all) { + 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); @@ -71,35 +74,37 @@ psg_serialize(gen->psg, buf); end_section(buf); - start_section(buf, SECTION_GEN_BUS_ARBITER); - save_int8(buf, gen->z80->reset); - save_int8(buf, gen->z80->busreq); - save_int16(buf, gen->z80_bank_reg); - end_section(buf); - - 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); - - cart_serialize(&gen->header, buf); + if (all) { + start_section(buf, SECTION_GEN_BUS_ARBITER); + save_int8(buf, gen->z80->reset); + save_int8(buf, gen->z80->busreq); + save_int16(buf, gen->z80_bank_reg); + end_section(buf); + + 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); + + cart_serialize(&gen->header, buf); + } } static uint8_t *serialize(system_header *sys, size_t *size_out) @@ -119,7 +124,7 @@ init_serialize(&state); uint32_t address = read_word(4, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k) << 16; address |= read_word(6, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k); - genesis_serialize(gen, &state, address); + genesis_serialize(gen, &state, address, 1); if (size_out) { *size_out = state.size; } @@ -411,6 +416,7 @@ if (gen->reset_cycle != CYCLE_NEVER) { gen->reset_cycle -= deduction; } + event_cycle_adjust(mclks, deduction); } } gen->frame_end = vdp_cycles_to_frame_end(v_context); @@ -449,16 +455,18 @@ } } #endif - char *save_path = slot == SERIALIZE_SLOT ? NULL : get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst"); - if (use_native_states || slot == SERIALIZE_SLOT) { + char *save_path = slot >= SERIALIZE_SLOT ? NULL : get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst"); + if (use_native_states || slot >= SERIALIZE_SLOT) { serialize_buffer state; init_serialize(&state); - genesis_serialize(gen, &state, address); + genesis_serialize(gen, &state, address, slot != EVENTLOG_SLOT); if (slot == SERIALIZE_SLOT) { gen->serialize_tmp = state.data; gen->serialize_size = state.size; context->sync_cycle = context->current_cycle; context->should_return = 1; + } else if (slot == EVENTLOG_SLOT) { + event_state(context->current_cycle, &state); } else { save_to_file(&state, save_path); free(state.data); @@ -1533,6 +1541,7 @@ gen->int_latency_prev2 = MCLKS_PER_68K * 16; render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC); + event_system_start(SYSTEM_GENESIS, (gen->version_reg & HZ50) ? VID_PAL : VID_NTSC, rom->name); gen->ym = malloc(sizeof(ym2612_context)); char *fm = tern_find_ptr_default(model, "fm", "discrete 2612"); diff -r ba7231d2411c -r c3c62dbf1ceb genesis.h --- a/genesis.h Wed Apr 29 01:00:15 2020 -0700 +++ b/genesis.h Wed Apr 29 01:00:57 2020 -0700 @@ -71,7 +71,7 @@ m68k_context * sync_components(m68k_context *context, uint32_t address); genesis_context *alloc_config_genesis(void *rom, uint32_t rom_size, void *lock_on, uint32_t lock_on_size, uint32_t system_opts, uint8_t force_region); -void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc); +void genesis_serialize(genesis_context *gen, serialize_buffer *buf, uint32_t m68k_pc, uint8_t all); void genesis_deserialize(deserialize_buffer *buf, genesis_context *gen); #endif //GENESIS_H_ diff -r ba7231d2411c -r c3c62dbf1ceb psg.c --- a/psg.c Wed Apr 29 01:00:15 2020 -0700 +++ b/psg.c Wed Apr 29 01:00:57 2020 -0700 @@ -5,6 +5,7 @@ */ #include "psg.h" #include "blastem.h" +#include "event_log.h" #include #include #include @@ -35,6 +36,7 @@ if (context->vgm) { vgm_sn76489_write(context->vgm, context->cycles, value); } + event_log(EVENT_PSG_REG, context->cycles, sizeof(value), &value); if (value & 0x80) { context->latch = value & 0x70; uint8_t channel = value >> 5 & 0x3; diff -r ba7231d2411c -r c3c62dbf1ceb render.h --- a/render.h Wed Apr 29 01:00:15 2020 -0700 +++ b/render.h Wed Apr 29 01:00:57 2020 -0700 @@ -77,8 +77,6 @@ #define FRAMEBUFFER_UI 2 #define FRAMEBUFFER_USER_START 3 -#include "vdp.h" - typedef enum { VID_NTSC, VID_PAL, diff -r ba7231d2411c -r c3c62dbf1ceb saves.h --- a/saves.h Wed Apr 29 01:00:15 2020 -0700 +++ b/saves.h Wed Apr 29 01:00:57 2020 -0700 @@ -7,6 +7,7 @@ #define QUICK_SAVE_SLOT 10 #define SERIALIZE_SLOT 11 +#define EVENTLOG_SLOT 12 typedef struct { char *desc; diff -r ba7231d2411c -r c3c62dbf1ceb serialize.c --- a/serialize.c Wed Apr 29 01:00:15 2020 -0700 +++ b/serialize.c Wed Apr 29 01:00:57 2020 -0700 @@ -20,8 +20,13 @@ 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)); + if (amount < buf->storage) { + buf->storage *= 2; + } else { + //doublign isn't enough, increase by the precise amount needed + buf->storage += amount - (buf->storage - buf->size); + } + buf->data = realloc(buf->data, buf->storage + sizeof(*buf)); } } diff -r ba7231d2411c -r c3c62dbf1ceb system.c --- a/system.c Wed Apr 29 01:00:15 2020 -0700 +++ b/system.c Wed Apr 29 01:00:57 2020 -0700 @@ -1,6 +1,7 @@ #include #include "system.h" #include "genesis.h" +#include "gen_player.h" #include "sms.h" uint8_t safe_cmp(char *str, long offset, uint8_t *buffer, long filesize) @@ -21,6 +22,14 @@ ) { return SYSTEM_SMS; } + if (safe_cmp("BLSTEL\x02", 0, media->buffer, media->size)) { + uint8_t *buffer = media->buffer; + if (media->size > 9 && buffer[7] == 0) { + return buffer[8] + 1; + } + } + + //TODO: Detect Jaguar ROMs here //Header based detection failed, examine filename for clues @@ -60,6 +69,8 @@ { case SYSTEM_GENESIS: return &(alloc_config_genesis(media->buffer, media->size, lock_on, lock_on_size, opts, force_region))->header; + case SYSTEM_GENESIS_PLAYER: + return &(alloc_config_gen_player(media->buffer, media->size))->header; #ifndef NO_Z80 case SYSTEM_SMS: return &(alloc_configure_sms(media, opts, force_region))->header; @@ -68,3 +79,13 @@ return NULL; } } + +system_header *alloc_config_player(system_type stype, event_reader *reader) +{ + switch(stype) + { + case SYSTEM_GENESIS: + return &(alloc_config_gen_player_reader(reader))->header; + } + return NULL; +} diff -r ba7231d2411c -r c3c62dbf1ceb system.h --- a/system.h Wed Apr 29 01:00:15 2020 -0700 +++ b/system.h Wed Apr 29 01:00:57 2020 -0700 @@ -9,8 +9,10 @@ typedef enum { SYSTEM_UNKNOWN, SYSTEM_GENESIS, + SYSTEM_GENESIS_PLAYER, SYSTEM_SMS, - SYSTEM_JAGUAR + SYSTEM_SMS_PLAYER, + SYSTEM_JAGUAR, } system_type; typedef enum { @@ -33,6 +35,7 @@ #include "arena.h" #include "romdb.h" +#include "event_log.h" struct system_header { system_header *next_context; @@ -87,5 +90,6 @@ system_type detect_system_type(system_media *media); system_header *alloc_config_system(system_type stype, system_media *media, uint32_t opts, uint8_t force_region); +system_header *alloc_config_player(system_type stype, event_reader *reader); #endif //SYSTEM_H_ diff -r ba7231d2411c -r c3c62dbf1ceb vdp.c --- a/vdp.c Wed Apr 29 01:00:15 2020 -0700 +++ b/vdp.c Wed Apr 29 01:00:57 2020 -0700 @@ -9,6 +9,7 @@ #include #include "render.h" #include "util.h" +#include "event_log.h" #define NTSC_INACTIVE_START 224 #define PAL_INACTIVE_START 240 @@ -908,14 +909,17 @@ { case VRAM_WRITE: if ((context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) == (BIT_128K_VRAM|BIT_MODE_5)) { + event_vram_word(context->cycles, start->address, start->value); vdp_check_update_sat(context, start->address, start->value); write_vram_word(context, start->address, start->value); } else { uint8_t byte = start->partial == 1 ? start->value >> 8 : start->value; - vdp_check_update_sat_byte(context, start->address ^ 1, byte); - write_vram_byte(context, start->address ^ 1, byte); + uint32_t address = start->address ^ 1; + event_vram_byte(context->cycles, start->address, byte, context->regs[REG_AUTOINC]); + vdp_check_update_sat_byte(context, address, byte); + write_vram_byte(context, address, byte); if (!start->partial) { - start->address = start->address ^ 1; + start->address = address; start->partial = 1; //skip auto-increment and removal of entry from fifo return; @@ -924,18 +928,20 @@ break; case CRAM_WRITE: { //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); + uint16_t val; if (start->partial == 3) { - uint16_t val; if ((start->address & 1) && (context->regs[REG_MODE_2] & BIT_MODE_5)) { val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF) | start->value << 8; } else { uint16_t address = (context->regs[REG_MODE_2] & BIT_MODE_5) ? start->address >> 1 & (CRAM_SIZE-1) : start->address & 0x1F; val = (context->cram[address] & 0xFF00) | start->value; } - write_cram(context, start->address, val); } else { - write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value); + val = start->partial ? context->fifo[context->fifo_write].value : start->value; } + uint8_t buffer[3] = {start->address, val >> 8, val}; + event_log(EVENT_CRAM, context->cycles, sizeof(buffer), buffer); + write_cram(context, start->address, val); break; } case VSRAM_WRITE: @@ -952,6 +958,8 @@ } else { context->vsram[(start->address/2) & 63] = start->partial ? context->fifo[context->fifo_write].value : start->value; } + uint8_t buffer[3] = {(start->address/2) & 63, context->vsram[(start->address/2) & 63] >> 8, context->vsram[(start->address/2) & 63]}; + event_log(EVENT_VSRAM, context->cycles, sizeof(buffer), buffer); } break; @@ -2110,6 +2118,8 @@ context->pushed_frame = 1; context->fb = NULL; } + //TODO: Check whether this happens before or after the cycle increment + event_flush(context->cycles); vdp_update_per_frame_debug(context); context->h40_lines = 0; context->frame++; @@ -3760,6 +3770,8 @@ /*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) { printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame); }*/ + uint8_t buffer[2] = {reg, value}; + event_log(EVENT_VDP_REG, context->cycles, sizeof(buffer), buffer); context->regs[reg] = value; if (reg == REG_MODE_4) { context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); @@ -4551,3 +4563,78 @@ } } } + +void vdp_replay_event(vdp_context *context, uint8_t event, event_reader *reader) +{ + uint32_t address; + deserialize_buffer *buffer = &reader->buffer; + switch (event) + { + case EVENT_VRAM_BYTE: + address = load_int16(buffer); + break; + case EVENT_VRAM_BYTE_DELTA: + address = reader->last_byte_address + load_int8(buffer); + break; + case EVENT_VRAM_BYTE_ONE: + address = reader->last_byte_address + 1; + break; + case EVENT_VRAM_BYTE_AUTO: + address = reader->last_byte_address + context->regs[REG_AUTOINC]; + break; + case EVENT_VRAM_WORD: + address = load_int8(buffer) << 16; + address |= load_int16(buffer); + break; + case EVENT_VRAM_WORD_DELTA: + address = reader->last_word_address + load_int8(buffer); + break; + case EVENT_VDP_REG: + case EVENT_CRAM: + case EVENT_VSRAM: + address = load_int8(buffer); + break; + } + + switch (event) + { + case EVENT_VDP_REG: { + uint8_t value = load_int8(buffer); + context->regs[address] = value; + if (address == REG_MODE_4) { + context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); + if (!context->double_res) { + context->flags2 &= ~FLAG2_EVEN_FIELD; + } + } + if (address == REG_MODE_1 || address == REG_MODE_2 || address == REG_MODE_4) { + update_video_params(context); + } + break; + } + case EVENT_VRAM_BYTE: + case EVENT_VRAM_BYTE_DELTA: + case EVENT_VRAM_BYTE_ONE: + case EVENT_VRAM_BYTE_AUTO: { + uint8_t byte = load_int8(buffer); + reader->last_byte_address = address; + vdp_check_update_sat_byte(context, address ^ 1, byte); + write_vram_byte(context, address ^ 1, byte); + break; + } + case EVENT_VRAM_WORD: + case EVENT_VRAM_WORD_DELTA: { + uint16_t value = load_int16(buffer); + reader->last_word_address = address; + vdp_check_update_sat(context, address, value); + write_vram_word(context, address, value); + break; + } + case EVENT_CRAM: + write_cram(context, address, load_int16(buffer)); + break; + case EVENT_VSRAM: + context->vsram[address] = load_int16(buffer); + break; + } +} diff -r ba7231d2411c -r c3c62dbf1ceb vdp.h --- a/vdp.h Wed Apr 29 01:00:15 2020 -0700 +++ b/vdp.h Wed Apr 29 01:00:57 2020 -0700 @@ -285,5 +285,6 @@ void vdp_inc_debug_mode(vdp_context *context); //to be implemented by the host system uint16_t read_dma_value(uint32_t address); +void vdp_replay_event(vdp_context *context, uint8_t event, event_reader *reader); #endif //VDP_H_ diff -r ba7231d2411c -r c3c62dbf1ceb ym2612.c --- a/ym2612.c Wed Apr 29 01:00:15 2020 -0700 +++ b/ym2612.c Wed Apr 29 01:00:57 2020 -0700 @@ -11,6 +11,7 @@ #include "render.h" #include "wave.h" #include "blastem.h" +#include "event_log.h" //#define DO_DEBUG_PRINT #ifdef DO_DEBUG_PRINT @@ -825,6 +826,8 @@ } context->part1_regs[context->selected_reg - YM_PART1_START] = value; } + uint8_t buffer[3] = {context->selected_part, context->selected_reg, value}; + event_log(EVENT_YM_REG, context->current_cycle, sizeof(buffer), buffer); dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1); if (context->selected_reg < 0x30) { //Shared regs