# HG changeset patch # User Michael Pavone # Date 1589092784 25200 # Node ID a7b753e260a251210510c2654e9614bb7dd7585e # Parent cafde1255ad3bcd8f278d71e30bb6eaf17b589a0# Parent 81df9aa2de9b3087321d10830d8e145bc9149575 Merge from default diff -r cafde1255ad3 -r a7b753e260a2 Makefile --- a/Makefile Sun Apr 19 00:59:49 2020 -0700 +++ b/Makefile Sat May 09 23:39:44 2020 -0700 @@ -31,7 +31,7 @@ GLUDIR:=x64 endif GLEW32S_LIB:=$(GLEW_PREFIX)/lib/Release/$(GLUDIR)/glew32s.lib -CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration +CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wpointer-arith -Werror=pointer-arith LDFLAGS:=-lm -lmingw32 -lws2_32 -mwindows ifneq ($(MAKECMDGOALS),libblastem.dll) CFLAGS+= -I"$(SDL2_PREFIX)/include/SDL2" -I"$(GLEW_PREFIX)/include" -DGLEW_STATIC @@ -47,7 +47,7 @@ EXE:= HAS_PROC:=$(shell if [ -d /proc ]; then /bin/echo -e -DHAS_PROC; fi) -CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value $(HAS_PROC) -DHAVE_UNISTD_H +CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value -Wpointer-arith -Werror=pointer-arith $(HAS_PROC) -DHAVE_UNISTD_H ifeq ($(OS),Darwin) LIBS=sdl2 glew @@ -199,7 +199,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 @@ -217,8 +217,8 @@ #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 MAINOBJS=blastem.o system.o genesis.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) \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.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 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 \ @@ -265,7 +265,7 @@ CFLAGS+= -DFONT_PATH='"'$(FONT_PATH)'"' endif -ALL=dis$(EXE) zdis$(EXE) stateview$(EXE) vgmplay$(EXE) blastem$(EXE) +ALL=dis$(EXE) zdis$(EXE) vgmplay$(EXE) blastem$(EXE) ifneq ($(OS),Windows) ALL+= termhelper endif @@ -313,10 +313,6 @@ ztestgen : ztestgen.o z80inst.o $(CC) -ggdb -o ztestgen ztestgen.o z80inst.o -stateview$(EXE) : stateview.o vdp.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) gst.o render_audio.o - $(CC) -o $@ $^ $(LDFLAGS) - $(FIXUP) ./$@ - vgmplay$(EXE) : vgmplay.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) $(AUDIOOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(FIXUP) ./$@ @@ -350,7 +346,7 @@ m68k.c : m68k.cpu cpu_dsl.py ./cpu_dsl.py -d call $< > $@ - + %.c : %.cpu cpu_dsl.py ./cpu_dsl.py -d goto $< > $@ diff -r cafde1255ad3 -r a7b753e260a2 backend.c --- a/backend.c Sun Apr 19 00:59:49 2020 -0700 +++ b/backend.c Sat May 09 23:39:44 2020 -0700 @@ -85,7 +85,7 @@ : memmap[chunk].buffer; if (!base) { if (memmap[chunk].flags & MMAP_AUX_BUFF) { - return memmap[chunk].buffer + (address & memmap[chunk].aux_mask); + return ((uint8_t *)memmap[chunk].buffer) + (address & memmap[chunk].aux_mask); } return NULL; } @@ -110,7 +110,7 @@ : memmap[chunk].buffer; if (!base) { if (memmap[chunk].flags & MMAP_AUX_BUFF) { - return memmap[chunk].buffer + (address & memmap[chunk].aux_mask); + return ((uint8_t *)memmap[chunk].buffer) + (address & memmap[chunk].aux_mask); } return NULL; } diff -r cafde1255ad3 -r a7b753e260a2 bindings.c --- a/bindings.c Sun Apr 19 00:59:49 2020 -0700 +++ b/bindings.c Sat May 09 23:39:44 2020 -0700 @@ -409,7 +409,7 @@ show_pause_menu(); } else { #endif - current_system->request_exit(current_system); + system_request_exit(current_system, 1); if (current_system->type == SYSTEM_GENESIS) { genesis_context *gen = (genesis_context *)current_system; if (gen->extra) { diff -r cafde1255ad3 -r a7b753e260a2 blastem.c --- a/blastem.c Sun Apr 19 00:59:49 2020 -0700 +++ b/blastem.c Sat May 09 23:39:44 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 @@ -157,8 +158,9 @@ for (offset = 0; offset + SMD_BLOCK_SIZE + SMD_HEADER_SIZE <= out_size; offset += SMD_BLOCK_SIZE) { uint8_t tmp[SMD_BLOCK_SIZE]; - memcpy(tmp, *dst + offset + SMD_HEADER_SIZE, SMD_BLOCK_SIZE); - process_smd_block(*dst + offset, tmp, SMD_BLOCK_SIZE); + uint8_t *u8dst = *dst; + memcpy(tmp, u8dst + offset + SMD_HEADER_SIZE, SMD_BLOCK_SIZE); + process_smd_block((void *)(u8dst + offset), tmp, SMD_BLOCK_SIZE); } out_size = offset; } @@ -325,7 +327,7 @@ free(current_system->next_rom); } current_system->next_rom = strdup(filename); - current_system->request_exit(current_system); + system_request_exit(current_system, 1); if (menu_system && menu_system->type == SYSTEM_GENESIS) { genesis_context *gen = (genesis_context *)menu_system; if (gen->extra) { @@ -366,7 +368,7 @@ num_parts--; } current_system->next_rom = alloc_concat_m(num_parts, start); - current_system->request_exit(current_system); + system_request_exit(current_system, 1); } void lockon_media(char *lock_on_path) @@ -434,6 +436,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]); @@ -445,10 +464,13 @@ system_type stype = SYSTEM_UNKNOWN, force_stype = SYSTEM_UNKNOWN; char * romfname = NULL; char * statefile = NULL; + char *reader_addr = NULL, *reader_port = 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]) { @@ -474,6 +496,18 @@ start_in_debugger = 1; break; #endif + 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; @@ -561,18 +595,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) { + reader_port = parse_addr_port(argv[i]); + if (reader_port) { + reader_addr = argv[i]; + } 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]); + } romfname = argv[i]; loaded = 1; } else if (width < 0) { @@ -605,6 +645,9 @@ fullscreen = !fullscreen; } if (!headless) { + if (reader_addr) { + render_set_external_sync(1); + } render_init(width, height, "BlastEm", fullscreen); render_set_drag_drop_handler(on_drag_drop); } @@ -650,13 +693,14 @@ warning("%s is not a valid value for the ui.state_format setting. Valid values are gst and native\n", state_format); } - if (loaded) { + if (loaded && !reader_addr) { if (stype == SYSTEM_UNKNOWN) { 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 (!current_system) { fatal_error("Failed to configure emulated machine for %s\n", romfname); @@ -679,6 +723,19 @@ } #endif + if (reader_addr) { + init_event_reader_tcp(&reader, reader_addr, reader_port); + stype = reader_system_type(&reader); + if (stype == SYSTEM_UNKNOWN) { + fatal_error("Failed to detect system type for %s\n", romfname); + } + game_system = current_system = alloc_config_player(stype, &reader); + //free inflate stream as it was inflateCopied to an internal event reader in the player + inflateEnd(&reader.input_stream); + setup_saves(&cart, current_system); + update_title(current_system->info.name); + } + current_system->debugger_type = dtype; current_system->enter_debugger = start_in_debugger && menu == debug_target; current_system->start_context(current_system, menu ? NULL : statefile); @@ -698,6 +755,7 @@ current_system->debugger_type = dtype; current_system->enter_debugger = start_in_debugger && menu == debug_target; current_system->start_context(current_system, statefile); + render_video_loop(); } else if (menu && game_system) { #ifndef NEW_CORE current_system->arena = set_current_arena(game_system->arena); @@ -719,6 +777,7 @@ } if (!current_system->next_rom) { current_system->resume_context(current_system); + render_video_loop(); } } else { break; diff -r cafde1255ad3 -r a7b753e260a2 build_release --- a/build_release Sun Apr 19 00:59:49 2020 -0700 +++ b/build_release Sat May 09 23:39:44 2020 -0700 @@ -34,11 +34,11 @@ fi make menu.bin if [ $OS = "Windows" -o $OS = "Win64" ]; then - binaries="dis.exe zdis.exe stateview.exe vgmplay.exe blastem.exe $SDLDLLPATH/SDL2.dll" + binaries="dis.exe zdis.exe vgmplay.exe blastem.exe $SDLDLLPATH/SDL2.dll" verstr=`sed -E -n 's/^[^B]+BLASTEM_VERSION "([^"]+)"/blastem \1/p' blastem.c` txt=".txt" else - binaries="dis zdis stateview vgmplay blastem termhelper" + binaries="dis zdis vgmplay blastem termhelper" if [ $OS = "Darwin" ]; then binaries="$binaries Frameworks" else diff -r cafde1255ad3 -r a7b753e260a2 cpu_dsl.py --- a/cpu_dsl.py Sun Apr 19 00:59:49 2020 -0700 +++ b/cpu_dsl.py Sat May 09 23:39:44 2020 -0700 @@ -1597,7 +1597,11 @@ pieces.append('\n\t\tif (context->cycles >= context->sync_cycle) {') self.meta = {} self.temp = {} - self.subroutines[self.interrupt].inline(self, [], pieces, otype, None) + intpieces = [] + self.subroutines[self.interrupt].inline(self, [], intpieces, otype, None) + for size in self.temp: + pieces.append('\n\tuint{sz}_t gen_tmp{sz}__;'.format(sz=size)) + pieces += intpieces pieces.append('\n\t\t}') self.meta = {} self.temp = {} diff -r cafde1255ad3 -r a7b753e260a2 debug.c --- a/debug.c Sun Apr 19 00:59:49 2020 -0700 +++ b/debug.c Sat May 09 23:39:44 2020 -0700 @@ -95,6 +95,12 @@ } } +static uint8_t m68k_read_byte(uint32_t address, m68k_context *context) +{ + //TODO: share this implementation with GDB debugger + return read_byte(address, (void **)context->mem_pointers, &context->options->gen, context); +} + uint16_t m68k_read_word(uint32_t address, m68k_context *context) { return read_word(address, (void **)context->mem_pointers, &context->options->gen, context); @@ -158,6 +164,8 @@ uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), &after, 16); if (after[0] == '.' && after[1] == 'l') { value = m68k_read_long(p_addr, context); + } else if (after[0] == '.' && after[1] == 'b') { + value = m68k_read_byte(p_addr, context); } else { value = m68k_read_word(p_addr, context); } @@ -166,6 +174,8 @@ uint32_t p_addr = param[1] == 'a' ? context->aregs[reg] : context->dregs[reg]; if (param[4] == '.' && param[5] == 'l') { value = m68k_read_long(p_addr, context); + } else if (param[4] == '.' && param[5] == 'b') { + value = m68k_read_byte(p_addr, context); } else { value = m68k_read_word(p_addr, context); } diff -r cafde1255ad3 -r a7b753e260a2 default.cfg --- a/default.cfg Sun Apr 19 00:59:49 2020 -0700 +++ b/default.cfg Sat May 09 23:39:44 2020 -0700 @@ -296,6 +296,9 @@ gl on #scaling can be linear (for linear interpolation) or nearest (for nearest neighbor) scaling linear + #When off, a 512x512 texture is used for each field, when turned on a smaller texture is used + #turning this on seems to help performance on certain mobile GPUs like Mali + npot_textures off ntsc { overscan { #these values will result in square pixels in H40 mode diff -r cafde1255ad3 -r a7b753e260a2 event_log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/event_log.c Sat May 09 23:39:44 2020 -0700 @@ -0,0 +1,746 @@ +#ifdef _WIN32 +#define WINVER 0x501 +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include "event_log.h" +#include "util.h" +#include "blastem.h" +#include "saves.h" +#include "zlib/zlib.h" + +enum { + CMD_GAMEPAD_DOWN, + CMD_GAMEPAD_UP, +}; + +static uint8_t active, fully_active; +static FILE *event_file; +static serialize_buffer buffer; +static uint8_t *compressed; +static size_t compressed_storage; +static z_stream output_stream; +static uint32_t last; + +static void event_log_common_init(void) +{ + init_serialize(&buffer); + compressed_storage = 128*1024; + compressed = malloc(compressed_storage); + deflateInit(&output_stream, 9); + output_stream.avail_out = compressed_storage; + output_stream.next_out = compressed; + output_stream.avail_in = 0; + output_stream.next_in = buffer.data; + last = 0; + active = 1; +} + +static uint8_t multi_count; +static size_t multi_start; +static void finish_multi(void) +{ + buffer.data[multi_start] |= multi_count - 2; + multi_count = 0; +} + +static void file_finish(void) +{ + fwrite(compressed, 1, output_stream.next_out - compressed, event_file); + output_stream.next_out = compressed; + output_stream.avail_out = compressed_storage; + int result = deflate(&output_stream, Z_FINISH); + if (Z_STREAM_END != result) { + fatal_error("Final deflate call returned %d\n", result); + } + fwrite(compressed, 1, output_stream.next_out - compressed, event_file); + fclose(event_file); +} + +static const char el_ident[] = "BLSTEL\x02\x00"; +void event_log_file(char *fname) +{ + event_file = fopen(fname, "wb"); + if (!event_file) { + warning("Failed to open event file %s for writing\n", fname); + return; + } + fwrite(el_ident, 1, sizeof(el_ident) - 1, event_file); + event_log_common_init(); + fully_active = 1; + atexit(file_finish); +} + +typedef struct { + uint8_t *send_progress; + int sock; + uint8_t players[1]; //TODO: Expand when support for multiple players per remote is added + uint8_t num_players; +} remote; + +static int listen_sock; +static remote remotes[7]; +static int num_remotes; +static uint8_t available_players[7] = {2,3,4,5,6,7,8}; +static int num_available_players = 7; +void event_log_tcp(char *address, char *port) +{ + struct addrinfo request, *result; + socket_init(); + memset(&request, 0, sizeof(request)); + request.ai_family = AF_INET; + request.ai_socktype = SOCK_STREAM; + request.ai_flags = AI_PASSIVE; + getaddrinfo(address, port, &request, &result); + + listen_sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (listen_sock < 0) { + warning("Failed to open event log listen socket on %s:%s\n", address, port); + goto cleanup_address; + } + int param = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)¶m, sizeof(param)); + if (bind(listen_sock, result->ai_addr, result->ai_addrlen) < 0) { + warning("Failed to bind event log listen socket on %s:%s\n", address, port); + socket_close(listen_sock); + goto cleanup_address; + } + if (listen(listen_sock, 3) < 0) { + warning("Failed to listen for event log remotes on %s:%s\n", address, port); + socket_close(listen_sock); + goto cleanup_address; + } + socket_blocking(listen_sock, 0); + event_log_common_init(); +cleanup_address: + freeaddrinfo(result); +} + +static uint8_t *system_start; +static size_t system_start_size; +void event_system_start(system_type stype, vid_std video_std, char *name) +{ + if (!active) { + return; + } + save_int8(&buffer, stype); + save_int8(&buffer, video_std); + size_t name_len = strlen(name); + if (name_len > 255) { + name_len = 255; + } + save_int8(&buffer, name_len); + save_buffer8(&buffer, name, strlen(name)); + if (listen_sock) { + system_start = malloc(buffer.size); + system_start_size = buffer.size; + memcpy(system_start, buffer.data, buffer.size); + } else { + //system start header is never compressed, so write to file immediately + fwrite(buffer.data, 1, buffer.size, event_file); + } + buffer.size = 0; +} + +//header formats +//Single byte: 4 bit type, 4 bit delta (16-31) +//Three Byte: 8 bit type, 16-bit delta +//Four byte: 8-bit type, 24-bit signed delta +#define FORMAT_3BYTE 0xE0 +#define FORMAT_4BYTE 0xF0 +static uint8_t last_event_type = 0xFF; +static uint32_t last_delta; +static void event_header(uint8_t type, uint32_t cycle) +{ + uint32_t delta = cycle - last; + if (multi_count) { + if (type != last_event_type || delta != last_delta) { + finish_multi(); + } else { + ++multi_count; + if (multi_count == 17) { + finish_multi(); + last_event_type = 0xFF; + } + return; + } + } else if (type == last_event_type && delta == last_delta && type != EVENT_FLUSH) { + //make some room + save_int8(&buffer, 0); + //shift existing command + memmove(buffer.data + multi_start + 1, buffer.data + multi_start, buffer.size - multi_start - 1); + buffer.data[multi_start] = EVENT_MULTI << 4; + multi_count = 2; + return; + } + multi_start = buffer.size; + last_event_type = type; + last_delta = delta; + + if (delta > 65535) { + save_int8(&buffer, FORMAT_4BYTE | type); + save_int8(&buffer, delta >> 16); + save_int16(&buffer, delta); + } else if (delta >= 16 && delta < 32) { + save_int8(&buffer, type << 4 | (delta - 16)); + } else { + save_int8(&buffer, FORMAT_3BYTE | type); + save_int16(&buffer, delta); + } +} + +void event_cycle_adjust(uint32_t cycle, uint32_t deduction) +{ + if (!fully_active) { + return; + } + event_header(EVENT_ADJUST, cycle); + last = cycle - deduction; + save_int32(&buffer, deduction); +} + +static uint8_t next_available_player(void) +{ + uint8_t lowest = 0xFF; + int lowest_index = -1; + for (int i = 0; i < num_available_players; i++) + { + if (available_players[i] < lowest) { + lowest = available_players[i]; + lowest_index = i; + } + } + if (lowest_index >= 0) { + available_players[lowest_index] = available_players[num_available_players - 1]; + --num_available_players; + } + return lowest; +} + +static void flush_socket(void) +{ + int remote_sock = accept(listen_sock, NULL, NULL); + if (remote_sock != -1) { + if (num_remotes == 7) { + socket_close(remote_sock); + } else { + printf("remote %d connected\n", num_remotes); + uint8_t player = next_available_player(); + remotes[num_remotes++] = (remote){ + .sock = remote_sock, + .send_progress = NULL, + .players = {player}, + .num_players = player == 0xFF ? 0 : 1 + }; + current_system->save_state = EVENTLOG_SLOT + 1; + } + } + uint8_t *min_progress = compressed; + for (int i = 0; i < num_remotes; i++) { + if (remotes[i].send_progress) { + uint8_t recv_buffer[1500]; + int bytes = recv(remotes[i].sock, recv_buffer, sizeof(recv_buffer), 0); + for (int j = 0; j < bytes; j++) + { + uint8_t cmd = recv_buffer[j]; + switch(cmd) + { + case CMD_GAMEPAD_DOWN: + case CMD_GAMEPAD_UP: { + ++j; + if (j < bytes) { + uint8_t button = recv_buffer[j]; + uint8_t pad = (button >> 5) - 1; + button &= 0x1F; + if (pad < remotes[i].num_players) { + pad = remotes[i].players[pad]; + if (cmd == CMD_GAMEPAD_DOWN) { + current_system->gamepad_down(current_system, pad, button); + } else { + current_system->gamepad_up(current_system, pad, button); + } + } + } else { + warning("Received incomplete command %X\n", cmd); + } + break; + } + default: + warning("Unrecognized remote command %X\n", cmd); + j = bytes; + } + } + int sent = 1; + while (sent && output_stream.next_out > remotes[i].send_progress) + { + sent = send(remotes[i].sock, remotes[i].send_progress, output_stream.next_out - remotes[i].send_progress, 0); + if (sent >= 0) { + remotes[i].send_progress += sent; + } else if (!socket_error_is_wouldblock()) { + socket_close(remotes[i].sock); + for (int j = 0; j < remotes[i].num_players; j++) { + available_players[num_available_players++] = remotes[i].players[j]; + } + remotes[i] = remotes[num_remotes-1]; + num_remotes--; + if (!num_remotes) { + //last remote disconnected, reset buffers/deflate + fully_active = 0; + deflateReset(&output_stream); + output_stream.next_out = compressed; + output_stream.avail_out = compressed_storage; + buffer.size = 0; + } + i--; + break; + } + if (remotes[i].send_progress > min_progress) { + min_progress = remotes[i].send_progress; + } + } + } + } + if (min_progress == output_stream.next_out) { + output_stream.next_out = compressed; + output_stream.avail_out = compressed_storage; + for (int i = 0; i < num_remotes; i++) { + if (remotes[i].send_progress) { + remotes[i].send_progress = compressed; + } + } + } +} + +uint8_t wrote_since_last_flush; +void event_log(uint8_t type, uint32_t cycle, uint8_t size, uint8_t *payload) +{ + if (!fully_active) { + return; + } + event_header(type, cycle); + last = cycle; + save_buffer8(&buffer, payload, size); + if (!multi_count) { + last_event_type = 0xFF; + output_stream.avail_in = buffer.size - (output_stream.next_in - buffer.data); + int result = deflate(&output_stream, Z_NO_FLUSH); + if (result != Z_OK) { + fatal_error("deflate returned %d\n", result); + } + if (listen_sock) { + if ((output_stream.next_out - compressed) > 1280 || !output_stream.avail_out) { + flush_socket(); + wrote_since_last_flush = 1; + } + } else if (!output_stream.avail_out) { + fwrite(compressed, 1, compressed_storage, event_file); + output_stream.next_out = compressed; + output_stream.avail_out = compressed_storage; + } + if (!output_stream.avail_in) { + buffer.size = 0; + output_stream.next_in = buffer.data; + } + } +} + +static uint32_t last_word_address; +void event_vram_word(uint32_t cycle, uint32_t address, uint16_t value) +{ + uint32_t delta = address - last_word_address; + if (delta < 256) { + uint8_t buffer[3] = {delta, value >> 8, value}; + event_log(EVENT_VRAM_WORD_DELTA, cycle, sizeof(buffer), buffer); + } else { + uint8_t buffer[5] = {address >> 16, address >> 8, address, value >> 8, value}; + event_log(EVENT_VRAM_WORD, cycle, sizeof(buffer), buffer); + } + last_word_address = address; +} + +static uint32_t last_byte_address; +void event_vram_byte(uint32_t cycle, uint16_t address, uint8_t byte, uint8_t auto_inc) +{ + uint32_t delta = address - last_byte_address; + if (delta == 1) { + event_log(EVENT_VRAM_BYTE_ONE, cycle, sizeof(byte), &byte); + } else if (delta == auto_inc) { + event_log(EVENT_VRAM_BYTE_AUTO, cycle, sizeof(byte), &byte); + } else if (delta < 256) { + uint8_t buffer[2] = {delta, byte}; + event_log(EVENT_VRAM_BYTE_DELTA, cycle, sizeof(buffer), buffer); + } else { + uint8_t buffer[3] = {address >> 8, address, byte}; + event_log(EVENT_VRAM_BYTE, cycle, sizeof(buffer), buffer); + } + last_byte_address = address; +} + +static size_t send_all(int sock, uint8_t *data, size_t size, int flags) +{ + size_t total = 0, sent = 1; + while(sent > 0 && total < size) + { + sent = send(sock, data + total, size - total, flags); + if (sent > 0) { + total += sent; + } + } + return total; +} + +void deflate_flush(uint8_t full) +{ + output_stream.avail_in = buffer.size - (output_stream.next_in - buffer.data); + uint8_t force = full; + while (output_stream.avail_in || force) + { + if (!output_stream.avail_out) { + size_t old_storage = compressed_storage; + uint8_t *old_compressed = compressed; + compressed_storage *= 2; + compressed = realloc(compressed, compressed_storage); + output_stream.next_out = compressed + old_storage; + output_stream.avail_out = old_storage; + for (int i = 0; i < num_remotes; i++) { + if (remotes[i].send_progress) { + remotes[i].send_progress = compressed + (remotes[i].send_progress - old_compressed); + } + } + } + int result = deflate(&output_stream, full ? Z_FINISH : Z_SYNC_FLUSH); + if (result != (full ? Z_STREAM_END : Z_OK)) { + fatal_error("deflate returned %d\n", result); + } + if (full && result == Z_STREAM_END) { + result = deflateReset(&output_stream); + if (result != Z_OK) { + fatal_error("deflateReset returned %d\n", result); + } + } + force = 0; + } + output_stream.next_in = buffer.data; + buffer.size = 0; +} + +void event_state(uint32_t cycle, serialize_buffer *state) +{ + if (!fully_active) { + last = cycle; + } + uint8_t header[] = { + EVENT_STATE << 4, last >> 24, last >> 16, last >> 8, last, + last_word_address >> 16, last_word_address >> 8, last_word_address, + last_byte_address >> 8, last_byte_address, + state->size >> 16, state->size >> 8, state->size + }; + uint8_t sent_system_start = 0; + for (int i = 0; i < num_remotes; i++) + { + if (!remotes[i].send_progress) { + if (send_all(remotes[i].sock, system_start, system_start_size, 0) == system_start_size) { + sent_system_start = 1; + } else { + socket_close(remotes[i].sock); + remotes[i] = remotes[num_remotes-1]; + num_remotes--; + i--; + } + } + } + if (sent_system_start) { + if (fully_active) { + if (multi_count) { + finish_multi(); + } + //full flush is needed so new and old clients can share a stream + deflate_flush(1); + } + save_buffer8(&buffer, header, sizeof(header)); + save_buffer8(&buffer, state->data, state->size); + size_t old_compressed_size = output_stream.next_out - compressed; + deflate_flush(1); + size_t state_size = output_stream.next_out - compressed - old_compressed_size; + for (int i = 0; i < num_remotes; i++) { + if (!remotes[i].send_progress) { + if (send_all(remotes[i].sock, compressed + old_compressed_size, state_size, 0) == state_size) { + remotes[i].send_progress = compressed + old_compressed_size; + socket_blocking(remotes[i].sock, 0); + int flag = 1; + setsockopt(remotes[i].sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&flag, sizeof(flag)); + fully_active = 1; + } else { + socket_close(remotes[i].sock); + remotes[i] = remotes[num_remotes-1]; + num_remotes--; + i--; + } + } + } + output_stream.next_out = compressed + old_compressed_size; + output_stream.avail_out = compressed_storage - old_compressed_size; + } +} + +void event_flush(uint32_t cycle) +{ + if (!active) { + return; + } + if (fully_active) { + event_header(EVENT_FLUSH, cycle); + last = cycle; + + deflate_flush(0); + } + if (event_file) { + fwrite(compressed, 1, output_stream.next_out - compressed, event_file); + fflush(event_file); + output_stream.next_out = compressed; + output_stream.avail_out = compressed_storage; + } else if (listen_sock) { + flush_socket(); + wrote_since_last_flush = 0; + } +} + +void event_soft_flush(uint32_t cycle) +{ + if (!fully_active || wrote_since_last_flush || event_file) { + return; + } + event_header(EVENT_FLUSH, cycle); + last = cycle; + + deflate_flush(0); + flush_socket(); +} + +static void init_event_reader_common(event_reader *reader) +{ + reader->last_cycle = 0; + reader->repeat_event = 0xFF; + reader->storage = 512 * 1024; + init_deserialize(&reader->buffer, malloc(reader->storage), reader->storage); + reader->buffer.size = 0; + memset(&reader->input_stream, 0, sizeof(reader->input_stream)); + +} + +void init_event_reader(event_reader *reader, uint8_t *data, size_t size) +{ + reader->socket = 0; + reader->last_cycle = 0; + reader->repeat_event = 0xFF; + init_event_reader_common(reader); + uint8_t name_len = data[1]; + reader->buffer.size = name_len + 2; + memcpy(reader->buffer.data, data, reader->buffer.size); + reader->input_stream.next_in = data + reader->buffer.size; + reader->input_stream.avail_in = size - reader->buffer.size; + + int result = inflateInit(&reader->input_stream); + if (Z_OK != result) { + fatal_error("inflateInit returned %d\n", result); + } + reader->input_stream.next_out = reader->buffer.data + reader->buffer.size; + reader->input_stream.avail_out = reader->storage - reader->buffer.size; + result = inflate(&reader->input_stream, Z_NO_FLUSH); + if (Z_OK != result && Z_STREAM_END != result) { + fatal_error("inflate returned %d\n", result); + } + reader->buffer.size = reader->input_stream.next_out - reader->buffer.data; +} + +void init_event_reader_tcp(event_reader *reader, char *address, char *port) +{ + struct addrinfo request, *result; + socket_init(); + memset(&request, 0, sizeof(request)); + request.ai_family = AF_INET; + request.ai_socktype = SOCK_STREAM; + request.ai_flags = AI_PASSIVE; + getaddrinfo(address, port, &request, &result); + + reader->socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (reader->socket < 0) { + fatal_error("Failed to create socket for event log connection to %s:%s\n", address, port); + } + if (connect(reader->socket, result->ai_addr, result->ai_addrlen) < 0) { + fatal_error("Failed to connect to %s:%s for event log stream\n", address, port); + } + + init_event_reader_common(reader); + reader->socket_buffer_size = 256 * 1024; + reader->socket_buffer = malloc(reader->socket_buffer_size); + + while(reader->buffer.size < 3 || reader->buffer.size < 3 + reader->buffer.data[2]) + { + int bytes = recv(reader->socket, reader->buffer.data + reader->buffer.size, reader->storage - reader->buffer.size, 0); + if (bytes < 0) { + fatal_error("Failed to receive system init from %s:%s\n", address, port); + } + reader->buffer.size += bytes; + } + size_t init_msg_len = 3 + reader->buffer.data[2]; + memcpy(reader->socket_buffer, reader->buffer.data + init_msg_len, reader->buffer.size - init_msg_len); + reader->input_stream.next_in = reader->socket_buffer; + reader->input_stream.avail_in = reader->buffer.size - init_msg_len; + reader->buffer.size = init_msg_len; + int res = inflateInit(&reader->input_stream); + if (Z_OK != res) { + fatal_error("inflateInit returned %d\n", res); + } + reader->input_stream.next_out = reader->buffer.data + init_msg_len; + reader->input_stream.avail_out = reader->storage - init_msg_len; + res = inflate(&reader->input_stream, Z_NO_FLUSH); + if (Z_OK != res && Z_BUF_ERROR != res) { + fatal_error("inflate returned %d in init_event_reader_tcp\n", res); + } + int flag = 1; + setsockopt(reader->socket, IPPROTO_TCP, TCP_NODELAY, (const char *)&flag, sizeof(flag)); +} + +static void read_from_socket(event_reader *reader) +{ + if (reader->socket_buffer_size - reader->input_stream.avail_in < 128 * 1024) { + reader->socket_buffer_size *= 2; + uint8_t *new_buf = malloc(reader->socket_buffer_size); + memcpy(new_buf, reader->input_stream.next_in, reader->input_stream.avail_in); + free(reader->socket_buffer); + reader->socket_buffer = new_buf; + reader->input_stream.next_in = new_buf; + } else if ( + reader->input_stream.next_in - reader->socket_buffer >= reader->input_stream.avail_in + && reader->input_stream.next_in - reader->socket_buffer + reader->input_stream.avail_in >= reader->socket_buffer_size/2 + ) { + memmove(reader->socket_buffer, reader->input_stream.next_in, reader->input_stream.avail_in); + reader->input_stream.next_in = reader->socket_buffer; + } + uint8_t *space_start = reader->input_stream.next_in + reader->input_stream.avail_in; + size_t space = (reader->socket_buffer + reader->socket_buffer_size) - space_start; + int bytes = recv(reader->socket, space_start, space, 0); + if (bytes >= 0) { + reader->input_stream.avail_in += bytes; + } else if (!socket_error_is_wouldblock()) { + fatal_error("Connection closed, error = %X\n", socket_last_error()); + } +} + +static void inflate_flush(event_reader *reader) +{ + if (reader->buffer.cur_pos > reader->storage / 2) { + memmove(reader->buffer.data, reader->buffer.data + reader->buffer.cur_pos, reader->buffer.size - reader->buffer.cur_pos); + reader->buffer.size -= reader->buffer.cur_pos; + reader->buffer.cur_pos = 0; + reader->input_stream.next_out = reader->buffer.data + reader->buffer.size; + reader->input_stream.avail_out = reader->storage - reader->buffer.size; + } + int result = inflate(&reader->input_stream, Z_SYNC_FLUSH); + if (Z_OK != result && Z_STREAM_END != result) { + fatal_error("inflate returned %d\n", result); + } + reader->buffer.size = reader->input_stream.next_out - reader->buffer.data; + if (result == Z_STREAM_END && (reader->socket || reader->input_stream.avail_in)) { + inflateReset(&reader->input_stream); + if (reader->input_stream.avail_in) { + inflate_flush(reader); + } + } + +} + +void reader_ensure_data(event_reader *reader, size_t bytes) +{ + if (reader->buffer.size - reader->buffer.cur_pos < bytes) { + if (reader->input_stream.avail_in) { + inflate_flush(reader); + } + if (reader->socket) { + while (reader->buffer.size - reader->buffer.cur_pos < bytes) { + read_from_socket(reader); + inflate_flush(reader); + } + } + } +} + +uint8_t reader_next_event(event_reader *reader, uint32_t *cycle_out) +{ + if (reader->repeat_remaining) { + reader->repeat_remaining--; + *cycle_out = reader->last_cycle + reader->repeat_delta; + reader->last_cycle = *cycle_out; + return reader->repeat_event; + } + reader_ensure_data(reader, 1); + uint8_t header = load_int8(&reader->buffer); + uint8_t ret; + uint32_t delta; + uint8_t multi_start = 0; + if ((header & 0xF0) == (EVENT_MULTI << 4)) { + reader->repeat_remaining = (header & 0xF) + 1; + multi_start = 1; + reader_ensure_data(reader, 1); + header = load_int8(&reader->buffer); + } + if ((header & 0xF0) < FORMAT_3BYTE) { + delta = (header & 0xF) + 16; + ret = header >> 4; + } else if ((header & 0xF0) == FORMAT_3BYTE) { + reader_ensure_data(reader, 2); + delta = load_int16(&reader->buffer); + ret = header & 0xF; + } else { + reader_ensure_data(reader, 3); + delta = load_int8(&reader->buffer) << 16; + //sign extend 24-bit delta to 32-bit + if (delta & 0x800000) { + delta |= 0xFF000000; + } + delta |= load_int16(&reader->buffer); + ret = header & 0xF; + } + if (multi_start) { + reader->repeat_event = ret; + reader->repeat_delta = delta; + } + *cycle_out = reader->last_cycle + delta; + reader->last_cycle = *cycle_out; + if (ret == EVENT_ADJUST) { + reader_ensure_data(reader, 4); + size_t old_pos = reader->buffer.cur_pos; + uint32_t adjust = load_int32(&reader->buffer); + reader->buffer.cur_pos = old_pos; + reader->last_cycle -= adjust; + } else if (ret == EVENT_STATE) { + reader_ensure_data(reader, 8); + reader->last_cycle = load_int32(&reader->buffer); + reader->last_word_address = load_int8(&reader->buffer) << 16; + reader->last_word_address |= load_int16(&reader->buffer); + reader->last_byte_address = load_int16(&reader->buffer); + } + return ret; +} + +uint8_t reader_system_type(event_reader *reader) +{ + return load_int8(&reader->buffer); +} + +void reader_send_gamepad_event(event_reader *reader, uint8_t pad, uint8_t button, uint8_t down) +{ + uint8_t buffer[] = {down ? CMD_GAMEPAD_DOWN : CMD_GAMEPAD_UP, pad << 5 | button}; + //TODO: Deal with the fact that we're not in blocking mode so this may not actually send all + //if the buffer is full + send_all(reader->socket, buffer, sizeof(buffer), 0); +} diff -r cafde1255ad3 -r a7b753e260a2 event_log.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/event_log.h Sat May 09 23:39:44 2020 -0700 @@ -0,0 +1,60 @@ +#ifndef EVENT_LOG_H_ +#define EVENT_LOG_H_ + +enum { + EVENT_FLUSH = 0, + EVENT_ADJUST = 1, + EVENT_PSG_REG = 2, + EVENT_YM_REG = 3, + EVENT_VDP_REG = 4, + EVENT_VRAM_BYTE = 5, + EVENT_VRAM_BYTE_DELTA = 6, + EVENT_VRAM_BYTE_ONE = 7, + EVENT_VRAM_BYTE_AUTO = 8, + EVENT_VRAM_WORD = 9, + EVENT_VRAM_WORD_DELTA = 10, + EVENT_VDP_INTRAM = 11, + EVENT_STATE = 12, + EVENT_MULTI = 13 + //14 and 15 are reserved for header types +}; + +#include "serialize.h" +#include "zlib/zlib.h" +typedef struct { + size_t storage; + uint8_t *socket_buffer; + size_t socket_buffer_size; + int socket; + uint32_t last_cycle; + uint32_t last_word_address; + uint32_t last_byte_address; + uint32_t repeat_delta; + deserialize_buffer buffer; + z_stream input_stream; + uint8_t repeat_event; + uint8_t repeat_remaining; +} event_reader; + +#include "system.h" +#include "render.h" + +void event_log_file(char *fname); +void event_log_tcp(char *address, char *port); +void event_system_start(system_type stype, vid_std video_std, char *name); +void event_cycle_adjust(uint32_t cycle, uint32_t deduction); +void event_log(uint8_t type, uint32_t cycle, uint8_t size, uint8_t *payload); +void event_vram_word(uint32_t cycle, uint32_t address, uint16_t value); +void event_vram_byte(uint32_t cycle, uint16_t address, uint8_t byte, uint8_t auto_inc); +void event_state(uint32_t cycle, serialize_buffer *state); +void event_flush(uint32_t cycle); +void event_soft_flush(uint32_t cycle); + +void init_event_reader(event_reader *reader, uint8_t *data, size_t size); +void init_event_reader_tcp(event_reader *reader, char *address, char *port); +uint8_t reader_next_event(event_reader *reader, uint32_t *cycle_out); +void reader_ensure_data(event_reader *reader, size_t bytes); +uint8_t reader_system_type(event_reader *reader); +void reader_send_gamepad_event(event_reader *reader, uint8_t pad, uint8_t button, uint8_t down); + +#endif //EVENT_LOG_H_ diff -r cafde1255ad3 -r a7b753e260a2 gdb_remote.c --- a/gdb_remote.c Sun Apr 19 00:59:49 2020 -0700 +++ b/gdb_remote.c Sat May 09 23:39:44 2020 -0700 @@ -18,13 +18,13 @@ #define GDB_OUT_FD STDOUT_FILENO #define GDB_READ read #define GDB_WRITE write +#include #endif #include "gdb_remote.h" #include "68kinst.h" #include "debug.h" #include "util.h" -#include #include #include #include @@ -132,22 +132,10 @@ } } -static uint8_t m68k_read_byte(m68k_context * context, uint32_t address) +static uint8_t m68k_read_byte(m68k_context *context, uint32_t address) { - - genesis_context *gen = context->system; - //TODO: Use generated read/write functions to support access to hardware that is not ROM or RAM - uint16_t * word = get_native_pointer(address & 0xFFFFFFFE, (void **)context->mem_pointers, &context->options->gen); - if (word) { - if (address & 1) { - return *word; - } - return *word >> 8; -} - if (address >= 0xA00000 && address < 0xA04000) { - return gen->zram[address & 0x1FFF]; - } - return 0; + //TODO: share this implementation with builtin debugger + return read_byte(address, (void **)context->mem_pointers, &context->options->gen, context); } static void m68k_write_byte(m68k_context * context, uint32_t address, uint8_t value) @@ -401,6 +389,10 @@ gdb_send_command("m1"); } else if (!strcmp("sThreadInfo", command + 1)) { gdb_send_command("l"); + } else if (!memcmp("ThreadExtraInfo", command+1, strlen("ThreadExtraInfo"))) { + gdb_send_command(""); + } else if (command[1] == 'P') { + gdb_send_command(""); } else { goto not_impl; } @@ -558,21 +550,13 @@ } } -#ifdef _WIN32 -void gdb_cleanup(void) -{ - WSACleanup(); -} -WSADATA wsa_data; -#endif - void gdb_remote_init(void) { buf = malloc(INITIAL_BUFFER_SIZE); curbuf = NULL; bufsize = INITIAL_BUFFER_SIZE; #ifdef _WIN32 - WSAStartup(MAKEWORD(2,2), &wsa_data); + socket_init(); struct addrinfo request, *result; memset(&request, 0, sizeof(request)); @@ -588,6 +572,7 @@ if (bind(listen_sock, result->ai_addr, result->ai_addrlen) < 0) { fatal_error("Failed to bind GDB remote debugging socket"); } + freeaddrinfo(result); if (listen(listen_sock, 1) < 0) { fatal_error("Failed to listen on GDB remote debugging socket"); } @@ -595,7 +580,7 @@ if (gdb_sock < 0) { fatal_error("accept returned an error while listening on GDB remote debugging socket"); } - closesocket(listen_sock); + socket_close(listen_sock); #else disable_stdout_messages(); #endif diff -r cafde1255ad3 -r a7b753e260a2 gen_player.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen_player.c Sat May 09 23:39:44 2020 -0700 @@ -0,0 +1,172 @@ +#include "gen_player.h" +#include "event_log.h" +#include "render.h" + +#ifdef IS_LIB +#define MAX_SOUND_CYCLES (MCLKS_PER_YM*NUM_OPERATORS*6*4) +#else +#define MAX_SOUND_CYCLES 100000 +#endif + +static void sync_sound(gen_player *gen, uint32_t target) +{ + //printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2); + while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { + uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; + //printf("Running PSG to cycle %d\n", cur_target); + psg_run(gen->psg, cur_target); + //printf("Running YM-2612 to cycle %d\n", cur_target); + ym_run(gen->ym, cur_target); + } + psg_run(gen->psg, target); + ym_run(gen->ym, target); + + //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); +} + +static void run(gen_player *player) +{ + while(player->reader.socket || player->reader.buffer.cur_pos < player->reader.buffer.size) + { + uint32_t cycle; + uint8_t event = reader_next_event(&player->reader, &cycle); + switch (event) + { + case EVENT_FLUSH: + sync_sound(player, cycle); + vdp_run_context(player->vdp, cycle); + break; + case EVENT_ADJUST: { + sync_sound(player, cycle); + vdp_run_context(player->vdp, cycle); + uint32_t deduction = load_int32(&player->reader.buffer); + ym_adjust_cycles(player->ym, deduction); + vdp_adjust_cycles(player->vdp, deduction); + player->psg->cycles -= deduction; + break; + case EVENT_PSG_REG: + sync_sound(player, cycle); + reader_ensure_data(&player->reader, 1); + psg_write(player->psg, load_int8(&player->reader.buffer)); + break; + case EVENT_YM_REG: { + sync_sound(player, cycle); + reader_ensure_data(&player->reader, 3); + uint8_t part = load_int8(&player->reader.buffer); + uint8_t reg = load_int8(&player->reader.buffer); + uint8_t value = load_int8(&player->reader.buffer); + if (part) { + ym_address_write_part2(player->ym, reg); + } else { + ym_address_write_part1(player->ym, reg); + } + ym_data_write(player->ym, value); + break; + case EVENT_STATE: { + reader_ensure_data(&player->reader, 3); + uint32_t size = load_int8(&player->reader.buffer) << 16; + size |= load_int16(&player->reader.buffer); + reader_ensure_data(&player->reader, size); + deserialize_buffer buffer; + init_deserialize(&buffer, player->reader.buffer.data + player->reader.buffer.cur_pos, size); + register_section_handler(&buffer, (section_handler){.fun = vdp_deserialize, .data = player->vdp}, SECTION_VDP); + register_section_handler(&buffer, (section_handler){.fun = ym_deserialize, .data = player->ym}, SECTION_YM2612); + register_section_handler(&buffer, (section_handler){.fun = psg_deserialize, .data = player->psg}, SECTION_PSG); + while (buffer.cur_pos < buffer.size) + { + load_section(&buffer); + } + player->reader.buffer.cur_pos += size; + free(buffer.handlers); + break; + } + default: + vdp_run_context(player->vdp, cycle); + vdp_replay_event(player->vdp, event, &player->reader); + } + } + + } + if (!player->reader.socket) { + reader_ensure_data(&player->reader, 1); + } + } +} + +static int thread_main(void *player) +{ + run(player); + return 0; +} + +void start_context(system_header *sys, char *statefile) +{ + gen_player *player = (gen_player *)sys; + if (player->reader.socket) { + render_create_thread(&player->thread, "player", thread_main, player); + } else { + run(player); + } +} + +static void gamepad_down(system_header *system, uint8_t gamepad_num, uint8_t button) +{ + gen_player *player = (gen_player *)system; + reader_send_gamepad_event(&player->reader, gamepad_num, button, 1); +} + +static void gamepad_up(system_header *system, uint8_t gamepad_num, uint8_t button) +{ + gen_player *player = (gen_player *)system; + reader_send_gamepad_event(&player->reader, gamepad_num, button, 0); +} + +#define MCLKS_NTSC 53693175 +#define MCLKS_PAL 53203395 +#define MCLKS_PER_YM 7 +#define MCLKS_PER_Z80 15 +#define MCLKS_PER_PSG (MCLKS_PER_Z80*16) + +static void config_common(gen_player *player) +{ + uint8_t vid_std = load_int8(&player->reader.buffer); + uint8_t name_len = load_int8(&player->reader.buffer); + player->header.info.name = calloc(1, name_len + 1); + load_buffer8(&player->reader.buffer, player->header.info.name, name_len); + + player->vdp = init_vdp_context(vid_std == VID_PAL, 0); + render_set_video_standard(vid_std); + uint32_t master_clock = vid_std == VID_NTSC ? MCLKS_NTSC : MCLKS_PAL; + + player->ym = malloc(sizeof(ym2612_context)); + ym_init(player->ym, master_clock, MCLKS_PER_YM, 0); + + player->psg = malloc(sizeof(psg_context)); + psg_init(player->psg, master_clock, MCLKS_PER_PSG); + + player->header.start_context = start_context; + player->header.gamepad_down = gamepad_down; + player->header.gamepad_up = gamepad_up; + player->header.type = SYSTEM_GENESIS_PLAYER; + player->header.info.save_type = SAVE_NONE; +} + +gen_player *alloc_config_gen_player(void *stream, uint32_t rom_size) +{ + uint8_t *data = stream; + gen_player *player = calloc(1, sizeof(gen_player)); + init_event_reader(&player->reader, data + 9, rom_size - 9); + config_common(player); + return player; +} + +gen_player *alloc_config_gen_player_reader(event_reader *reader) +{ + gen_player *player = calloc(1, sizeof(gen_player)); + player->reader = *reader; + inflateCopy(&player->reader.input_stream, &reader->input_stream); + render_set_external_sync(1); + config_common(player); + return player; +} + diff -r cafde1255ad3 -r a7b753e260a2 gen_player.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen_player.h Sat May 09 23:39:44 2020 -0700 @@ -0,0 +1,24 @@ +#ifndef GEN_PLAYER_H_ +#define GEN_PLAYER_H_ + +#include "render.h" +#include "system.h" +#include "vdp.h" +#include "psg.h" +#include "ym2612.h" +#include "event_log.h" + +typedef struct { + system_header header; + + vdp_context *vdp; + ym2612_context *ym; + psg_context *psg; + render_thread thread; + event_reader reader; +} gen_player; + +gen_player *alloc_config_gen_player(void *stream, uint32_t rom_size); +gen_player *alloc_config_gen_player_reader(event_reader *reader); + +#endif //GEN_PLAYER_H_ diff -r cafde1255ad3 -r a7b753e260a2 gen_x86.c --- a/gen_x86.c Sun Apr 19 00:59:49 2020 -0700 +++ b/gen_x86.c Sat May 09 23:39:44 2020 -0700 @@ -171,7 +171,7 @@ }; #ifdef X86_64 -#define CHECK_DISP(disp) (disp <= 0x7FFFFFFF && disp >= -2147483648) +#define CHECK_DISP(disp) (disp <= ((ptrdiff_t)INT32_MAX) && disp >= ((ptrdiff_t)INT32_MIN)) #else #define CHECK_DISP(disp) 1 #endif @@ -1261,7 +1261,7 @@ check_alloc_code(code, 14); code_ptr out = code->cur; uint8_t sign_extend = 0; - if (size == SZ_Q && val <= 0x7FFFFFFF && val >= -2147483648) { + if (size == SZ_Q && val <= ((int64_t)INT32_MAX) && val >= ((int64_t)INT32_MIN)) { sign_extend = 1; } if (size == SZ_W) { diff -r cafde1255ad3 -r a7b753e260a2 genesis.c --- a/genesis.c Sun Apr 19 00:59:49 2020 -0700 +++ b/genesis.c Sat May 09 23:39:44 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 @@ -50,15 +51,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); @@ -72,35 +75,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) @@ -120,7 +125,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; } @@ -351,9 +356,6 @@ //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); } -//TODO: move this inside the system context -static uint32_t last_frame_num; - //My refresh emulation isn't currently good enough and causes more problems than it solves #define REFRESH_EMULATION #ifdef REFRESH_EMULATION @@ -392,9 +394,11 @@ context->should_return = 1; gen->reset_cycle = CYCLE_NEVER; } - if (v_context->frame != last_frame_num) { - //printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", last_frame_num, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot); - last_frame_num = v_context->frame; + if (v_context->frame != gen->last_frame) { + //printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", gen->last_frame, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot); + gen->last_frame = v_context->frame; + event_flush(mclks); + gen->last_flush_cycle = mclks; if(exit_after){ --exit_after; @@ -421,7 +425,12 @@ if (gen->reset_cycle != CYCLE_NEVER) { gen->reset_cycle -= deduction; } + event_cycle_adjust(mclks, deduction); + gen->last_flush_cycle -= deduction; } + } else if (mclks - gen->last_flush_cycle > gen->soft_flush_cycles) { + event_soft_flush(mclks); + gen->last_flush_cycle = mclks; } gen->frame_end = vdp_cycles_to_frame_end(v_context); context->sync_cycle = gen->frame_end; @@ -461,9 +470,9 @@ } } #endif - char *save_path = slot == SERIALIZE_SLOT ? NULL : get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst"); + char *save_path = slot >= SERIALIZE_SLOT ? NULL : get_slot_name(&gen->header, slot, use_native_states ? "state" : "gst"); #ifndef NEW_CORE - if (use_native_states || slot == SERIALIZE_SLOT) { + if (use_native_states || slot >= SERIALIZE_SLOT) { #endif serialize_buffer state; init_serialize(&state); @@ -473,6 +482,8 @@ 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); @@ -482,7 +493,9 @@ save_gst(gen, save_path, address); } #endif - printf("Saved state to %s\n", save_path); + if (slot != SERIALIZE_SLOT) { + debug_message("Saved state to %s\n", save_path); + } free(save_path); } else if(gen->header.save_state) { context->sync_cycle = context->current_cycle + 1; @@ -644,11 +657,11 @@ uint16_t value; #ifdef REFRESH_EMULATION if (context->current_cycle - 4*MCLKS_PER_68K > last_sync_cycle) { - //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; + //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 genesis_context *gen = context->system; @@ -1198,8 +1211,10 @@ if (region & HZ50) { gen->normal_clock = MCLKS_PAL; + gen->soft_flush_cycles = MCLKS_LINE * 262 / 3 + 2; } else { gen->normal_clock = MCLKS_NTSC; + gen->soft_flush_cycles = MCLKS_LINE * 313 / 3 + 2; } gen->master_clock = gen->normal_clock; } @@ -1265,7 +1280,7 @@ resume_68k(gen->m68k); } } - if (render_should_release_on_exit()) { + if (gen->header.force_release || render_should_release_on_exit()) { bindings_release_capture(); vdp_release_framebuffer(gen->vdp); render_pause_source(gen->ym->audio); @@ -1321,7 +1336,8 @@ static void resume_genesis(system_header *system) { genesis_context *gen = (genesis_context *)system; - if (render_should_release_on_exit()) { + if (gen->header.force_release || render_should_release_on_exit()) { + gen->header.force_release = 0; render_set_video_standard((gen->version_reg & HZ50) ? VID_PAL : VID_NTSC); bindings_reacquire_capture(); vdp_reacquire_framebuffer(gen->vdp); @@ -1565,8 +1581,13 @@ 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"); + if (!strcmp(fm + strlen(fm) -4, "3834")) { + system_opts |= YM_OPT_3834; + } ym_init(gen->ym, gen->master_clock, MCLKS_PER_YM, system_opts); gen->psg = malloc(sizeof(psg_context)); @@ -1676,6 +1697,7 @@ gen->bank_regs[i] = i; } } + gen->reset_cycle = CYCLE_NEVER; return gen; } diff -r cafde1255ad3 -r a7b753e260a2 genesis.h --- a/genesis.h Sun Apr 19 00:59:49 2020 -0700 +++ b/genesis.h Sat May 09 23:39:44 2020 -0700 @@ -51,6 +51,9 @@ uint32_t int_latency_prev1; uint32_t int_latency_prev2; uint32_t reset_cycle; + uint32_t last_frame; + uint32_t last_flush_cycle; + uint32_t soft_flush_cycles; uint8_t bank_regs[8]; uint16_t z80_bank_reg; uint16_t tmss_lock[2]; @@ -71,7 +74,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 cafde1255ad3 -r a7b753e260a2 libblastem.c --- a/libblastem.c Sun Apr 19 00:59:49 2020 -0700 +++ b/libblastem.c Sat May 09 23:39:44 2020 -0700 @@ -381,7 +381,7 @@ last_height = height; } retro_video_refresh(fb + overscan_left + LINEBUF_SIZE * overscan_top, width, height, LINEBUF_SIZE * sizeof(uint32_t)); - current_system->request_exit(current_system); + system_request_exit(current_system, 0); } uint8_t render_get_active_framebuffer(void) diff -r cafde1255ad3 -r a7b753e260a2 m68k.cpu --- a/m68k.cpu Sun Apr 19 00:59:49 2020 -0700 +++ b/m68k.cpu Sat May 09 23:39:44 2020 -0700 @@ -401,6 +401,8 @@ m68k_prefetch 1101DDD1ZZMMMRRR add_dn_ea + invalid M 0 + invalid M 1 invalid M 7 R 2 invalid M 7 R 3 invalid M 7 R 4 @@ -439,6 +441,7 @@ 00000110ZZMMMRRR addi local immed 32 invalid Z 3 + invalid M 1 invalid M 7 R 2 invalid M 7 R 3 invalid M 7 R 4 @@ -578,7 +581,286 @@ mov aregs.D scratch2 m68k_write_size Z 0 m68k_prefetch + +1100DDD0ZZMMMRRR and_ea_dn + invalid M 1 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_src_ea M R Z + and src dregs.D dregs.D Z + update_flags NZV0C0 + m68k_prefetch + +1100DDD1ZZMMMRRR and_dn_ea + invalid M 0 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_dst_ea M R Z + + and dregs.D dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +00000010ZZMMMRRR andi + local immed 32 + invalid Z 3 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + #fetch immediate operand + m68k_prefetch + switch Z + case 2 + lsl prefetch 16 immed + m68k_prefetch + or prefetch immed immed + default + mov prefetch immed + end + #fetch dst EA + m68k_fetch_dst_ea M R Z + + and immed dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +0000001000111100 andi_to_ccr + #fetch immediate operand + m68k_prefetch + and prefetch ccr ccr + m68k_prefetch + +1011DDD1ZZMMMRRR eor_dn_ea + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_dst_ea M R Z + + xor dregs.D dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +00001010ZZMMMRRR eori + local immed 32 + invalid Z 3 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + #fetch immediate operand + m68k_prefetch + switch Z + case 2 + lsl prefetch 16 immed + m68k_prefetch + or prefetch immed immed + default + mov prefetch immed + end + #fetch dst EA + m68k_fetch_dst_ea M R Z + + xor immed dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +0000001000111100 eori_to_ccr + #fetch immediate operand + m68k_prefetch + xor prefetch ccr ccr + m68k_prefetch + +1000DDD0ZZMMMRRR or_ea_dn + invalid M 1 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_src_ea M R Z + + or src dregs.D dregs.D Z + update_flags NZV0C0 + m68k_prefetch + +1000DDD1ZZMMMRRR or_dn_ea + invalid M 0 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_dst_ea M R Z + + or dregs.D dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +00000000ZZMMMRRR ori + local immed 32 + invalid Z 3 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + #fetch immediate operand + m68k_prefetch + switch Z + case 2 + lsl prefetch 16 immed + m68k_prefetch + or prefetch immed immed + default + mov prefetch immed + end + #fetch dst EA + m68k_fetch_dst_ea M R Z + + or immed dst dst Z + update_flags NZV0C0 + m68k_save_dst Z + m68k_prefetch + +0000000000111100 ori_to_ccr + #fetch immediate operand + m68k_prefetch + or prefetch ccr ccr + m68k_prefetch + +1001DDD0ZZMMMRRR sub_ea_dn + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_src_ea M R Z + + sub src dregs.D dregs.D Z + update_flags XNZVC + m68k_prefetch + +1001DDD1ZZMMMRRR sub_dn_ea + invalid M 0 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + invalid Z 3 + m68k_fetch_dst_ea M R Z + + sub dregs.D dst dst Z + update_flags XNZVC + m68k_save_dst Z + m68k_prefetch + +1001AAAZ11MMMRRR suba + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + local size 16 + local ext_src 32 + if Z + mov 2 size + else + mov 1 size + end + m68k_fetch_src_ea M R size + switch size + case 1 + sext 32 src ext_src + meta src ext_src + end + + sub src aregs.A aregs.A + m68k_prefetch + +00000100ZZMMMRRR subi + local immed 32 + invalid Z 3 + invalid M 1 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + #fetch immediate operand + m68k_prefetch + switch Z + case 2 + lsl prefetch 16 immed + m68k_prefetch + or prefetch immed immed + default + mov prefetch immed + end + #fetch dst EA + m68k_fetch_dst_ea M R Z + + sub immed dst dst Z + update_flags XNZVC + m68k_save_dst Z + m68k_prefetch + +0101III1ZZMMMRRR subq + invalid Z 3 + invalid M 7 R 2 + invalid M 7 R 3 + invalid M 7 R 4 + invalid M 7 R 5 + invalid M 7 R 6 + invalid M 7 R 7 + local src 32 + switch I + case 0 + mov 8 src + default + mov I src + end + + m68k_fetch_dst_ea M R Z + switch M + case 1 + sub src dst dst Z + default + sub src dst dst Z + update_flags XNZVC + end + m68k_save_dst Z + m68k_prefetch 00ZZRRRMMMEEESSS move invalid Z 0 diff -r cafde1255ad3 -r a7b753e260a2 m68k_core_x86.c --- a/m68k_core_x86.c Sun Apr 19 00:59:49 2020 -0700 +++ b/m68k_core_x86.c Sat May 09 23:39:44 2020 -0700 @@ -1315,8 +1315,6 @@ numcycles = 6; } else if (inst->op == M68K_AND && inst->variant == VAR_IMMEDIATE) { numcycles = 6; - } else if (inst->op == M68K_ADD && inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD && inst->variant == VAR_QUICK) { - numcycles = 4; } else if (inst->dst.addr_mode <= MODE_AREG) { numcycles = inst->src.addr_mode <= MODE_AREG || inst->src.addr_mode == MODE_IMMEDIATE ? 8 : 6; } else { diff -r cafde1255ad3 -r a7b753e260a2 m68k_util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/m68k_util.c Sat May 09 23:39:44 2020 -0700 @@ -0,0 +1,89 @@ +#include + +void m68k_read_8(m68k_context *context) +{ + context->cycles += 4 * context->opts->gen.clock_divider; + context->scratch1 = read_byte(context->scratch1, context->mem_pointers, &context->opts->gen, context); +} + +void m68k_read_16(m68k_context *context) +{ + context->cycles += 4 * context->opts->gen.clock_divider; + context->scratch1 = read_word(context->scratch1, context->mem_pointers, &context->opts->gen, context); +} + +void m68k_write_8(m68k_context *context) +{ + context->cycles += 4 * context->opts->gen.clock_divider; + write_byte(context->scratch2, context->scratch1, context->mem_pointers, &context->opts->gen, context); +} + +void m68k_write_16(m68k_context *context) +{ + context->cycles += 4 * context->opts->gen.clock_divider; + write_word(context->scratch2, context->scratch1, context->mem_pointers, &context->opts->gen, context); +} + +void m68k_sync_cycle(m68k_context *context, uint32_t target_cycle) +{ + //TODO: interrupt stuff + context->sync_cycle = target_cycle; +} + +void init_m68k_opts(m68k_options *opts, memmap_chunk * memmap, uint32_t num_chunks, uint32_t clock_divider) +{ + memset(opts, 0, sizeof(*opts)); + opts->gen.memmap = memmap; + opts->gen.memmap_chunks = num_chunks; + opts->gen.address_mask = 0xFFFFFF; + opts->gen.byte_swap = 1; + opts->gen.max_address = 0x1000000; + opts->gen.bus_cycles = 4; + opts->gen.clock_divider = clock_divider; +} + +m68k_context *init_68k_context(m68k_options * opts, m68k_reset_handler reset_handler) +{ + m68k_context *context = calloc(1, sizeof(m68k_context)); + context->opts = opts; + context->reset_handler = reset_handler; + context->int_cycle = 0xFFFFFFFFU; + return context; +} + +void m68k_reset(m68k_context *context) +{ + //read initial SP + context->scratch1 = 0; + m68k_read_16(context); + context->aregs[7] = context->scratch1 << 16; + context->scratch1 = 2; + m68k_read_16(context); + context->aregs[7] |= context->scratch1; + + //read initial PC + context->scratch1 = 4; + m68k_read_16(context); + context->pc = context->scratch1 << 16; + context->scratch1 = 6; + m68k_read_16(context); + context->pc |= context->scratch1; + + context->scratch1 = context->pc; + m68k_read_16(context); + context->prefetch = context->scratch1; + context->pc += 2; + + context->status = 0x27; +} + +void m68k_print_regs(m68k_context *context) +{ + printf("XNZVC\n%d%d%d%d%d\n", context->xflag != 0, context->nflag != 0, context->zflag != 0, context->vflag != 0, context->cflag != 0); + for (int i = 0; i < 8; i++) { + printf("d%d: %X\n", i, context->dregs[i]); + } + for (int i = 0; i < 8; i++) { + printf("a%d: %X\n", i, context->aregs[i]); + } +} diff -r cafde1255ad3 -r a7b753e260a2 megawifi.c --- a/megawifi.c Sun Apr 19 00:59:49 2020 -0700 +++ b/megawifi.c Sat May 09 23:39:44 2020 -0700 @@ -6,15 +6,28 @@ #define WINVER 0x501 #include #include +#include #else #include +#include #include #include +#include #endif #include #include +#include #include "genesis.h" #include "net.h" +#include "util.h" + +#ifdef _WIN32 +# if BYTE_ORDER == LITTLE_ENDIAN +#define htobe64(val) ((((uint64_t)htonl((val)&0xFFFFFFFF))<<32) | htonl((val)>>32)) +# else +#define htobe64(val) (val) +# endif +#endif enum { TX_IDLE, @@ -25,7 +38,7 @@ }; #define STX 0x7E #define ETX 0x7E -#define MAX_RECV_SIZE 1440 +#define MAX_RECV_SIZE 1460 #define E(N) N enum { @@ -43,7 +56,7 @@ #define MSG_NOSIGNAL 0 #endif -enum { +enum mw_state { STATE_IDLE=1, STATE_AP_JOIN, STATE_SCAN, @@ -51,6 +64,21 @@ STATE_TRANSPARENT }; +enum { + SOCKST_NONE = 0, + SOCKST_TCP_LISTEN, + SOCKST_TCP_EST, + SOCKST_UDP_READY +}; + +// TCP/UDP address message +struct mw_addr_msg { + char dst_port[6]; + char src_port[6]; + uint8_t channel; + char host[]; +}; + #define FLAG_ONLINE typedef struct { @@ -68,6 +96,7 @@ uint8_t flags; uint8_t transmit_buffer[4096]; uint8_t receive_buffer[4096]; + struct sockaddr_in remote_addr[15]; // Needed for UDP sockets } megawifi; static megawifi *get_megawifi(void *context) @@ -75,11 +104,12 @@ m68k_context *m68k = context; genesis_context *gen = m68k->system; if (!gen->extra) { + socket_init(); gen->extra = calloc(1, sizeof(megawifi)); megawifi *mw = gen->extra; mw->module_state = STATE_IDLE; - for (int i = 0; i < 15; i++) - { + mw->flags = 0xE0; // cfg_ok, dt_ok, online + for (int i = 0; i < 15; i++) { mw->sock_fds[i] = -1; } } @@ -112,14 +142,88 @@ mw->receive_bytes += count; } -static void mw_puts(megawifi *mw, char *s) +static void mw_puts(megawifi *mw, const char *s) +{ + size_t len = strlen(s); + mw_copy(mw, (uint8_t*)s, len); +} + +static void udp_recv(megawifi *mw, uint8_t idx) { - uint32_t len = strlen(s); - if ((mw->receive_bytes + len) > sizeof(mw->receive_buffer)) { - return; + ssize_t recvd; + int s = mw->sock_fds[idx]; + struct sockaddr_in remote; + socklen_t addr_len = sizeof(struct sockaddr_in); + + if (mw->remote_addr[idx].sin_addr.s_addr != htonl(INADDR_ANY)) { + // Receive only from specified address + recvd = recvfrom(s, (char*)mw->receive_buffer + 3, MAX_RECV_SIZE, 0, + (struct sockaddr*)&remote, &addr_len); + if (recvd > 0) { + if (remote.sin_addr.s_addr != mw->remote_addr[idx].sin_addr.s_addr) { + printf("Discarding UDP packet from unknown addr %s:%d\n", + inet_ntoa(remote.sin_addr), ntohs(remote.sin_port)); + recvd = 0; + } + } + } else { + // Reuse mode, data is preceded by remote IPv4 and port + recvd = recvfrom(s, (char*)mw->receive_buffer + 9, MAX_RECV_SIZE - 6, + 0, (struct sockaddr*)&remote, &addr_len); + if (recvd > 0) { + mw->receive_buffer[3] = remote.sin_addr.s_addr; + mw->receive_buffer[4] = remote.sin_addr.s_addr>>8; + mw->receive_buffer[5] = remote.sin_addr.s_addr>>16; + mw->receive_buffer[6] = remote.sin_addr.s_addr>>24; + mw->receive_buffer[7] = remote.sin_port; + mw->receive_buffer[8] = remote.sin_port>>8; + recvd += 6; + } } - memcpy(mw->receive_buffer + mw->receive_bytes, s, len); - mw->receive_bytes += len; + + if (recvd > 0) { + mw_putc(mw, STX); + mw_putc(mw, (recvd >> 8) | ((idx+1) << 4)); + mw_putc(mw, recvd); + mw->receive_bytes += recvd; + mw_putc(mw, ETX); + //should this set the channel flag? + } else if (recvd < 0 && !socket_error_is_wouldblock()) { + socket_close(mw->sock_fds[idx]); + mw->channel_state[idx] = SOCKST_NONE; + mw->channel_flags |= 1 << (idx + 1); + } +} + +static void udp_send(megawifi *mw, uint8_t idx) +{ + struct sockaddr_in remote; + int s = mw->sock_fds[idx]; + int sent; + char *data = (char*)mw->transmit_buffer; + + if (mw->remote_addr[idx].sin_addr.s_addr != htonl(INADDR_ANY)) { + sent = sendto(s, data, mw->transmit_bytes, 0, (struct sockaddr*)&mw->remote_addr[idx], + sizeof(struct sockaddr_in)); + } else { + // Reuse mode, extract address from leading bytes + // NOTE: mw->remote_addr[idx].sin_addr.s_addr == INADDR_ANY + remote.sin_addr.s_addr = *((int32_t*)data); + remote.sin_port = *((int16_t*)(data + 4)); + remote.sin_family = AF_INET; + memset(remote.sin_zero, 0, sizeof(remote.sin_zero)); + sent = sendto(s, data + 6, mw->transmit_bytes - 6, 0, (struct sockaddr*)&remote, + sizeof(struct sockaddr_in)) + 6; + } + if (sent < 0 && !socket_error_is_wouldblock()) { + socket_close(s); + mw->sock_fds[idx] = -1; + mw->channel_state[idx] = SOCKST_NONE; + mw->channel_flags |= 1 << (idx + 1); + } else if (sent < mw->transmit_bytes) { + //TODO: save this data somewhere so it can be sent in poll_socket + printf("Sent %d bytes on channel %d, but %d were requested\n", sent, idx + 1, mw->transmit_bytes); + } } static void poll_socket(megawifi *mw, uint8_t channel) @@ -127,28 +231,25 @@ if (mw->sock_fds[channel] < 0) { return; } - if (mw->channel_state[channel] == 1) { + if (mw->channel_state[channel] == SOCKST_TCP_LISTEN) { int res = accept(mw->sock_fds[channel], NULL, NULL); if (res >= 0) { - close(mw->sock_fds[channel]); -#ifndef _WIN32 -//FIXME: Set nonblocking on Windows too - fcntl(res, F_SETFL, O_NONBLOCK); -#endif + socket_close(mw->sock_fds[channel]); + socket_blocking(res, 0); mw->sock_fds[channel] = res; - mw->channel_state[channel] = 2; + mw->channel_state[channel] = SOCKST_TCP_EST; mw->channel_flags |= 1 << (channel + 1); } else if (errno != EAGAIN && errno != EWOULDBLOCK) { - close(mw->sock_fds[channel]); - mw->channel_state[channel] = 0; + socket_close(mw->sock_fds[channel]); + mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << (channel + 1); } - } else if (mw->channel_state[channel] == 2 && mw->receive_bytes < sizeof(mw->receive_buffer) - 4) { + } else if (mw->channel_state[channel] == SOCKST_TCP_EST && mw->receive_bytes < (sizeof(mw->receive_buffer) - 4)) { size_t max = sizeof(mw->receive_buffer) - 4 - mw->receive_bytes; if (max > MAX_RECV_SIZE) { max = MAX_RECV_SIZE; } - int bytes = recv(mw->sock_fds[channel], mw->receive_buffer + mw->receive_bytes + 3, max, 0); + int bytes = recv(mw->sock_fds[channel], (char*)(mw->receive_buffer + mw->receive_bytes + 3), max, 0); if (bytes > 0) { mw_putc(mw, STX); mw_putc(mw, bytes >> 8 | (channel+1) << 4); @@ -156,11 +257,13 @@ mw->receive_bytes += bytes; mw_putc(mw, ETX); //should this set the channel flag? - } else if (bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - close(mw->sock_fds[channel]); - mw->channel_state[channel] = 0; + } else if (bytes < 0 && !socket_error_is_wouldblock()) { + socket_close(mw->sock_fds[channel]); + mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << (channel + 1); } + } else if (mw->channel_state[channel] == SOCKST_UDP_READY && !mw->receive_bytes) { + udp_recv(mw, channel); } } @@ -172,6 +275,7 @@ } } + static void start_reply(megawifi *mw, uint8_t cmd) { mw_putc(mw, STX); @@ -197,152 +301,443 @@ mw_putc(mw, ETX); } -static void process_packet(megawifi *mw) +static void cmd_ap_cfg_get(megawifi *mw) +{ + char ssid[32] = {0}; + char pass[64] = {0}; + uint8_t slot = mw->transmit_buffer[4]; + + sprintf(ssid, "BLASTEM! SSID %d", slot + 1); + sprintf(pass, "BLASTEM! PASS %d", slot + 1); + start_reply(mw, CMD_OK); + mw_putc(mw, slot); + mw_putc(mw, 7); /// 11bgn + mw_copy(mw, (uint8_t*)ssid, 32); + mw_copy(mw, (uint8_t*)pass, 64); + end_reply(mw); +} + +static void cmd_ip_cfg_get(megawifi *mw) +{ + uint32_t ipv4s[5] = {0}; + + start_reply(mw, CMD_OK); + mw_putc(mw, mw->transmit_buffer[4]); + mw_putc(mw, 0); + mw_putc(mw, 0); + mw_putc(mw, 0); + mw_copy(mw, (uint8_t*)ipv4s, sizeof(ipv4s)); + end_reply(mw); +} + +static void cmd_tcp_con(megawifi *mw, uint32_t size) +{ + struct mw_addr_msg *addr = (struct mw_addr_msg*)(mw->transmit_buffer + 4); + struct addrinfo hints; + struct addrinfo *res = NULL; + int s; + int err; + + uint8_t channel = addr->channel; + if (!channel || channel > 15 || mw->sock_fds[channel - 1] >= 0) { + start_reply(mw, CMD_ERROR); + end_reply(mw); + return; + } + channel--; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; +#ifndef _WIN32 + hints.ai_flags = AI_NUMERICSERV; +#endif + hints.ai_socktype = SOCK_STREAM; + + if ((err = getaddrinfo(addr->host, addr->dst_port, &hints, &res)) != 0) { + printf("getaddrinfo failed: %s\n", gai_strerror(err)); + start_reply(mw, CMD_ERROR); + end_reply(mw); + return; + } + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + goto err; + } + + // Should this be handled in a separate thread to avoid blocking emulation? + if (connect(s, res->ai_addr, res->ai_addrlen) != 0) { + goto err; + } + + socket_blocking(s, 0); + mw->sock_fds[channel] = s; + mw->channel_state[channel] = SOCKST_TCP_EST; + mw->channel_flags |= 1 << (channel + 1); + printf("Connection established on ch %d with %s:%s\n", channel + 1, + addr->host, addr->dst_port); + + if (res) { + freeaddrinfo(res); + } + start_reply(mw, CMD_OK); + end_reply(mw); + return; + +err: + freeaddrinfo(res); + printf("Connection to %s:%s failed, %s\n", addr->host, addr->dst_port, strerror(errno)); + start_reply(mw, CMD_ERROR); + end_reply(mw); +} + +static void cmd_close(megawifi *mw) +{ + int channel = mw->transmit_buffer[4] - 1; + + if (channel >= 15 || mw->sock_fds[channel] < 0) { + start_reply(mw, CMD_ERROR); + end_reply(mw); + return; + } + + socket_close(mw->sock_fds[channel]); + mw->sock_fds[channel] = -1; + mw->channel_state[channel] = SOCKST_NONE; + mw->channel_flags |= 1 << (channel + 1); + start_reply(mw, CMD_OK); + end_reply(mw); +} + +static void cmd_udp_set(megawifi *mw) { - if (mw->transmit_channel == 0) { - uint32_t command = mw->transmit_buffer[0] << 8 | mw->transmit_buffer[1]; - uint32_t size = mw->transmit_buffer[2] << 8 | mw->transmit_buffer[3]; - if (size > mw->transmit_bytes - 4) { - size = mw->transmit_bytes - 4; + struct mw_addr_msg *addr = (struct mw_addr_msg*)(mw->transmit_buffer + 4); + unsigned int local_port, remote_port; + int s; + struct addrinfo *raddr; + struct addrinfo hints; + struct sockaddr_in local; + int err; + + uint8_t channel = addr->channel; + if (!channel || channel > 15 || mw->sock_fds[channel - 1] >= 0) { + goto err; + } + channel--; + local_port = atoi(addr->src_port); + remote_port = atoi(addr->dst_port); + + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + printf("Datagram socket creation failed\n"); + goto err; + } + + memset(local.sin_zero, 0, sizeof(local.sin_zero)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(local_port); + if (remote_port && addr->host[0]) { + // Communication with remote peer + printf("Set UDP ch %d, port %d to addr %s:%d\n", addr->channel, + local_port, addr->host, remote_port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; +#ifndef _WIN32 + hints.ai_flags = AI_NUMERICSERV; +#endif + hints.ai_socktype = SOCK_DGRAM; + + if ((err = getaddrinfo(addr->host, addr->dst_port, &hints, &raddr)) != 0) { + printf("getaddrinfo failed: %s\n", gai_strerror(err)); + goto err; } - int orig_receive_bytes = mw->receive_bytes; - switch (command) - { - case CMD_VERSION: + mw->remote_addr[channel] = *((struct sockaddr_in*)raddr->ai_addr); + freeaddrinfo(raddr); + } else if (local_port) { + // Server in reuse mode + printf("Set UDP ch %d, src port %d\n", addr->channel, local_port); + mw->remote_addr[channel] = local; + } else { + printf("Invalid UDP socket data\n"); + goto err; + } + + if (bind(s, (struct sockaddr*)&local, sizeof(struct sockaddr_in)) < 0) { + printf("bind to port %d failed\n", local_port); + goto err; + } + + socket_blocking(s, 0); + mw->sock_fds[channel] = s; + mw->channel_state[channel] = SOCKST_UDP_READY; + mw->channel_flags |= 1 << (channel + 1); + + start_reply(mw, CMD_OK); + end_reply(mw); + + return; + +err: + start_reply(mw, CMD_ERROR); + end_reply(mw); +} + +#define AVATAR_BYTES (32 * 48 / 2) +static void cmd_gamertag_get(megawifi *mw) +{ + uint32_t id = htonl(1); + char buf[AVATAR_BYTES]; + + start_reply(mw, CMD_OK); + // TODO Get items from config file + mw_copy(mw, (uint8_t*)&id, 4); + strncpy(buf, "doragasu on Blastem!", 32); + mw_copy(mw, (uint8_t*)buf, 32); + strncpy(buf, "My cool password", 32); + mw_copy(mw, (uint8_t*)buf, 32); + strncpy(buf, "All your WiFi are belong to me!", 32); + mw_copy(mw, (uint8_t*)buf, 32); + memset(buf, 0, 64); // Telegram token + mw_copy(mw, (uint8_t*)buf, 64); + mw_copy(mw, (uint8_t*)buf, AVATAR_BYTES); // Avatar tiles + mw_copy(mw, (uint8_t*)buf, 32); // Avatar palette + end_reply(mw); +} + +static void cmd_hrng_get(megawifi *mw) +{ + uint16_t len = (mw->transmit_buffer[4]<<8) + mw->transmit_buffer[5]; + if (len > (MAX_RECV_SIZE - 4)) { + start_reply(mw, CMD_ERROR); + end_reply(mw); + return; + } + // Pseudo-random, but who cares + start_reply(mw, CMD_OK); + srand(time(NULL)); + for (uint16_t i = 0; i < len; i++) { + mw_putc(mw, rand()); + } + end_reply(mw); +} + +static void cmd_datetime(megawifi *mw) +{ + start_reply(mw, CMD_OK); +#ifdef _WIN32 + __time64_t t = _time64(NULL); + int64_t t_be = htobe64(t); + mw_copy(mw, (uint8_t*)&t_be, sizeof(int64_t)); + mw_puts(mw, _ctime64(&t)); +#else + time_t t = time(NULL); + int64_t t_be = htobe64(t); + mw_copy(mw, (uint8_t*)&t_be, sizeof(int64_t)); + mw_puts(mw, ctime(&t)); +#endif + + mw_putc(mw, '\0'); + end_reply(mw); +} + +static void process_command(megawifi *mw) +{ + uint32_t command = mw->transmit_buffer[0] << 8 | mw->transmit_buffer[1]; + uint32_t size = mw->transmit_buffer[2] << 8 | mw->transmit_buffer[3]; + if (size > mw->transmit_bytes - 4) { + size = mw->transmit_bytes - 4; + } + int orig_receive_bytes = mw->receive_bytes; + switch (command) + { + case CMD_VERSION: + start_reply(mw, CMD_OK); + mw_putc(mw, 1); + mw_putc(mw, 3); + mw_putc(mw, 0); + mw_puts(mw, "blastem"); + mw_putc(mw, '\0'); + end_reply(mw); + break; + case CMD_ECHO: + mw->receive_bytes = mw->transmit_bytes; + memcpy(mw->receive_buffer, mw->transmit_buffer, mw->transmit_bytes); + break; + case CMD_AP_CFG_GET: + cmd_ap_cfg_get(mw); + break; + case CMD_IP_CURRENT: { + iface_info i; + if (get_host_address(&i)) { start_reply(mw, CMD_OK); - mw_putc(mw, 1); - mw_putc(mw, 0); - mw_puts(mw, "blastem"); - end_reply(mw); - break; - case CMD_ECHO: - mw->receive_bytes = mw->transmit_bytes; - memcpy(mw->receive_buffer, mw->transmit_buffer, mw->transmit_bytes); - break; - case CMD_IP_CURRENT: { - iface_info i; - if (get_host_address(&i)) { - start_reply(mw, CMD_OK); - //config number and reserved bytes - mw_set(mw, 0, 4); - //ip - mw_copy(mw, i.ip, sizeof(i.ip)); - //net mask - mw_copy(mw, i.net_mask, sizeof(i.net_mask)); - //gateway guess - mw_putc(mw, i.ip[0] & i.net_mask[0]); - mw_putc(mw, i.ip[1] & i.net_mask[1]); - mw_putc(mw, i.ip[2] & i.net_mask[2]); - mw_putc(mw, (i.ip[3] & i.net_mask[3]) + 1); - //dns - static const uint8_t localhost[] = {127,0,0,1}; - mw_copy(mw, localhost, sizeof(localhost)); - mw_copy(mw, localhost, sizeof(localhost)); - - } else { - start_reply(mw, CMD_ERROR); - } + //config number and reserved bytes + mw_set(mw, 0, 4); + //ip + mw_copy(mw, i.ip, sizeof(i.ip)); + //net mask + mw_copy(mw, i.net_mask, sizeof(i.net_mask)); + //gateway guess + mw_putc(mw, i.ip[0] & i.net_mask[0]); + mw_putc(mw, i.ip[1] & i.net_mask[1]); + mw_putc(mw, i.ip[2] & i.net_mask[2]); + mw_putc(mw, (i.ip[3] & i.net_mask[3]) + 1); + //dns + static const uint8_t localhost[] = {127,0,0,1}; + mw_copy(mw, localhost, sizeof(localhost)); + mw_copy(mw, localhost, sizeof(localhost)); + + } else { + start_reply(mw, CMD_ERROR); + } + end_reply(mw); + break; + } + case CMD_IP_CFG_GET: + cmd_ip_cfg_get(mw); + break; + case CMD_DEF_AP_CFG_GET: + start_reply(mw, CMD_OK); + mw_putc(mw, 0); + end_reply(mw); + break; + case CMD_AP_JOIN: + mw->module_state = STATE_READY; + start_reply(mw, CMD_OK); + end_reply(mw); + break; + case CMD_TCP_CON: + cmd_tcp_con(mw, size); + break; + case CMD_TCP_BIND:{ + if (size < 7){ + start_reply(mw, CMD_ERROR); end_reply(mw); break; } - case CMD_AP_JOIN: - mw->module_state = STATE_READY; - start_reply(mw, CMD_OK); + uint8_t channel = mw->transmit_buffer[10]; + if (!channel || channel > 15) { + start_reply(mw, CMD_ERROR); + end_reply(mw); + break; + } + channel--; + if (mw->sock_fds[channel] >= 0) { + socket_close(mw->sock_fds[channel]); + } + mw->sock_fds[channel] = socket(AF_INET, SOCK_STREAM, 0); + if (mw->sock_fds[channel] < 0) { + start_reply(mw, CMD_ERROR); end_reply(mw); break; - case CMD_TCP_BIND:{ - if (size < 7){ - start_reply(mw, CMD_ERROR); - end_reply(mw); - break; - } - uint8_t channel = mw->transmit_buffer[10]; - if (!channel || channel > 15) { - start_reply(mw, CMD_ERROR); - end_reply(mw); - break; - } - channel--; - if (mw->sock_fds[channel] >= 0) { - close(mw->sock_fds[channel]); - } - mw->sock_fds[channel] = socket(AF_INET, SOCK_STREAM, 0); - if (mw->sock_fds[channel] < 0) { - start_reply(mw, CMD_ERROR); - end_reply(mw); - break; - } - int value = 1; - setsockopt(mw->sock_fds[channel], SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); - struct sockaddr_in bind_addr; - memset(&bind_addr, 0, sizeof(bind_addr)); - bind_addr.sin_family = AF_INET; - bind_addr.sin_port = htons(mw->transmit_buffer[8] << 8 | mw->transmit_buffer[9]); - if (bind(mw->sock_fds[channel], (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) { - close(mw->sock_fds[channel]); - mw->sock_fds[channel] = -1; - start_reply(mw, CMD_ERROR); - end_reply(mw); - break; - } - int res = listen(mw->sock_fds[channel], 2); - start_reply(mw, res ? CMD_ERROR : CMD_OK); - if (res) { - close(mw->sock_fds[channel]); - mw->sock_fds[channel] = -1; - } else { - mw->channel_flags |= 1 << (channel + 1); - mw->channel_state[channel] = 1; -#ifndef _WIN32 -//FIXME: Set nonblocking on Windows too - fcntl(mw->sock_fds[channel], F_SETFL, O_NONBLOCK); -#endif - } + } + int value = 1; + setsockopt(mw->sock_fds[channel], SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); + struct sockaddr_in bind_addr; + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(mw->transmit_buffer[8] << 8 | mw->transmit_buffer[9]); + if (bind(mw->sock_fds[channel], (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) { + socket_close(mw->sock_fds[channel]); + mw->sock_fds[channel] = -1; + start_reply(mw, CMD_ERROR); + end_reply(mw); + break; + } + int res = listen(mw->sock_fds[channel], 2); + start_reply(mw, res ? CMD_ERROR : CMD_OK); + if (res) { + socket_close(mw->sock_fds[channel]); + mw->sock_fds[channel] = -1; + } else { + mw->channel_flags |= 1 << (channel + 1); + mw->channel_state[channel] = SOCKST_TCP_LISTEN; + socket_blocking(mw->sock_fds[channel], 0); + } + end_reply(mw); + break; + } + case CMD_CLOSE: + cmd_close(mw); + break; + case CMD_UDP_SET: + cmd_udp_set(mw); + break; + case CMD_SOCK_STAT: { + uint8_t channel = mw->transmit_buffer[4]; + if (!channel || channel > 15) { + start_reply(mw, CMD_ERROR); end_reply(mw); break; } - case CMD_SOCK_STAT: { - uint8_t channel = mw->transmit_buffer[4]; - if (!channel || channel > 15) { - start_reply(mw, CMD_ERROR); - end_reply(mw); - break; + mw->channel_flags &= ~(1 << channel); + channel--; + poll_socket(mw, channel); + start_reply(mw, CMD_OK); + mw_putc(mw, mw->channel_state[channel]); + end_reply(mw); + break; + } + case CMD_DATETIME: + cmd_datetime(mw); + break; + case CMD_SYS_STAT: + poll_all_sockets(mw); + start_reply(mw, CMD_OK); + mw_putc(mw, mw->module_state); + mw_putc(mw, mw->flags); + mw_putc(mw, mw->channel_flags >> 8); + mw_putc(mw, mw->channel_flags); + end_reply(mw); + break; + case CMD_GAMERTAG_GET: + cmd_gamertag_get(mw); + break; + case CMD_LOG: + start_reply(mw, CMD_OK); + puts((char*)&mw->transmit_buffer[4]); + end_reply(mw); + break; + case CMD_HRNG_GET: + cmd_hrng_get(mw); + break; + case CMD_SERVER_URL_GET: + start_reply(mw, CMD_OK); + // FIXME: This should be get from config file + mw_puts(mw, "doragasu.com"); + mw_putc(mw,'\0'); + end_reply(mw); + break; + default: + printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size); + break; + } +} + +static void process_packet(megawifi *mw) +{ + if (mw->transmit_channel == 0) { + process_command(mw); + } else { + uint8_t channel = mw->transmit_channel - 1; + int channel_state = mw->channel_state[channel]; + int sock_fd = mw->sock_fds[channel]; + if (sock_fd >= 0 && channel_state == SOCKST_TCP_EST) { + int sent = send(sock_fd, (char*)mw->transmit_buffer, mw->transmit_bytes, 0); + if (sent < 0 && !socket_error_is_wouldblock()) { + socket_close(sock_fd); + mw->sock_fds[channel] = -1; + mw->channel_state[channel] = SOCKST_NONE; + mw->channel_flags |= 1 << mw->transmit_channel; + } else if (sent < mw->transmit_bytes) { + //TODO: save this data somewhere so it can be sent in poll_socket + printf("Sent %d bytes on channel %d, but %d were requested\n", sent, mw->transmit_channel, mw->transmit_bytes); } - mw->channel_flags &= ~(1 << channel); - channel--; - poll_socket(mw, channel); - start_reply(mw, CMD_OK); - mw_putc(mw, mw->channel_state[channel]); - end_reply(mw); - break; + } else if (sock_fd >= 0 && channel_state == SOCKST_UDP_READY) { + udp_send(mw, channel); + } else { + printf("Unhandled receive of MegaWiFi data on channel %d\n", mw->transmit_channel); } - case CMD_SYS_STAT: - poll_all_sockets(mw); - start_reply(mw, CMD_OK); - mw_putc(mw, mw->module_state); - mw_putc(mw, mw->flags); - mw_putc(mw, mw->channel_flags >> 8); - mw_putc(mw, mw->channel_flags); - end_reply(mw); - break; - default: - printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size); - break; - } - } else if (mw->sock_fds[mw->transmit_channel - 1] >= 0 && mw->channel_state[mw->transmit_channel - 1] == 2) { - uint8_t channel = mw->transmit_channel - 1; - int sent = send(mw->sock_fds[channel], mw->transmit_buffer, mw->transmit_bytes, MSG_NOSIGNAL); - if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - close(mw->sock_fds[channel]); - mw->sock_fds[channel] = -1; - mw->channel_state[channel] = 0; - mw->channel_flags |= 1 << mw->transmit_channel; - } else if (sent < mw->transmit_bytes) { - //TODO: save this data somewhere so it can be sent in poll_socket - printf("Sent %d bytes on channel %d, but %d were requested\n", sent, mw->transmit_channel, mw->transmit_bytes); - } - } else { - printf("Unhandled receive of MegaWiFi data on channel %d\n", mw->transmit_channel); } mw->transmit_bytes = mw->expected_bytes = 0; } diff -r cafde1255ad3 -r a7b753e260a2 mw_commands.c --- a/mw_commands.c Sun Apr 19 00:59:49 2020 -0700 +++ b/mw_commands.c Sat May 09 23:39:44 2020 -0700 @@ -5,7 +5,7 @@ E(CMD_AP_CFG), E(CMD_AP_CFG_GET), E(CMD_IP_CURRENT), - E(CMD_RESERVED), + E(CMD_RESERVED_7), E(CMD_IP_CFG), E(CMD_IP_CFG_GET), E(CMD_DEF_AP_CFG), @@ -14,10 +14,10 @@ E(CMD_AP_LEAVE), E(CMD_TCP_CON), E(CMD_TCP_BIND), - E(CMD_TCP_ACCEPT), - E(CMD_TCP_DISC), + E(CMD_RESERVED_16), + E(CMD_CLOSE), E(CMD_UDP_SET), - E(CMD_UDP_CLR), + E(CMD_RESERVED_19), E(CMD_SOCK_STAT), E(CMD_PING), E(CMD_SNTP_CFG), @@ -30,4 +30,27 @@ E(CMD_FLASH_ID), E(CMD_SYS_STAT), E(CMD_DEF_CFG_SET), - E(CMD_HRNG_GET), \ No newline at end of file + E(CMD_HRNG_GET), + E(CMD_BSSID_GET), + E(CMD_GAMERTAG_SET), + E(CMD_GAMERTAG_GET), + E(CMD_LOG), + E(CMD_FACTORY_RESET), + E(CMD_SLEEP), + E(CMD_HTTP_URL_SET), + E(CMD_HTTP_METHOD_SET), + E(CMD_HTTP_CERT_QUERY), + E(CMD_HTTP_CERT_SET), + E(CMD_HTTP_HDR_ADD), + E(CMD_HTTP_HDR_DEL), + E(CMD_HTTP_OPEN), + E(CMD_HTTP_FINISH), + E(CMD_HTTP_CLEANUP), + E(CMD_RESERVED_48), + E(CMD_SERVER_URL_GET), + E(CMD_SERVER_URL_SET), + E(CMD_WIFI_ADV_GET), + E(CMD_WIFI_ADV_SET), + E(CMD_NV_CFG_SAVE), + E(CMD_UPGRADE_LIST), + E(CMD_UPGRADE_PERFORM), diff -r cafde1255ad3 -r a7b753e260a2 nuklear_ui/blastem_nuklear.c --- a/nuklear_ui/blastem_nuklear.c Sun Apr 19 00:59:49 2020 -0700 +++ b/nuklear_ui/blastem_nuklear.c Sat May 09 23:39:44 2020 -0700 @@ -2220,7 +2220,7 @@ context->style.window.fixed_background = nk_style_item_color(nk_rgba(0, 0, 0, 128)); current_view = view_pause; context->input.selected_widget = 0; - current_system->request_exit(current_system); + system_request_exit(current_system, 1); } else if (current_system && !set_binding) { clear_view_stack(); show_play_view(); diff -r cafde1255ad3 -r a7b753e260a2 nuklear_ui/font.c --- a/nuklear_ui/font.c Sun Apr 19 00:59:49 2020 -0700 +++ b/nuklear_ui/font.c Sat May 09 23:39:44 2020 -0700 @@ -14,7 +14,8 @@ return strdup(FONT_PATH); } #endif - FILE *fc_pipe = popen("fc-match -f '%{file}'", "r"); + //TODO: specify language dynamically once BlastEm is localized + FILE *fc_pipe = popen("fc-match :lang=en -f '%{file}'", "r"); if (!fc_pipe) { return NULL; } diff -r cafde1255ad3 -r a7b753e260a2 psg.c --- a/psg.c Sun Apr 19 00:59:49 2020 -0700 +++ b/psg.c Sat May 09 23:39:44 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 cafde1255ad3 -r a7b753e260a2 render.h --- a/render.h Sun Apr 19 00:59:49 2020 -0700 +++ b/render.h Sat May 09 23:39:44 2020 -0700 @@ -65,6 +65,7 @@ #define RENDER_DPAD_LEFT SDL_HAT_LEFT #define RENDER_DPAD_RIGHT SDL_HAT_RIGHT #define render_relative_mouse SDL_SetRelativeMouseMode +typedef SDL_Thread* render_thread; #endif #endif @@ -77,8 +78,6 @@ #define FRAMEBUFFER_UI 2 #define FRAMEBUFFER_USER_START 3 -#include "vdp.h" - typedef enum { VID_NTSC, VID_PAL, @@ -95,6 +94,7 @@ typedef void (*drop_handler)(const char *filename); typedef void (*window_close_handler)(uint8_t which); typedef void (*ui_render_fun)(void); +typedef int (*render_thread_fun)(void*); uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); void render_save_screenshot(char *path); @@ -137,6 +137,8 @@ void render_set_ui_fb_resize_handler(ui_render_fun resize); void render_video_loop(void); uint8_t render_should_release_on_exit(void); +void render_set_external_sync(uint8_t ext_sync_on); +uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data); #endif //RENDER_H_ diff -r cafde1255ad3 -r a7b753e260a2 render_fbdev.c --- a/render_fbdev.c Sun Apr 19 00:59:49 2020 -0700 +++ b/render_fbdev.c Sat May 09 23:39:44 2020 -0700 @@ -50,200 +50,21 @@ static uint32_t last_frame = 0; static snd_pcm_uframes_t buffer_samples; +static size_t buffer_bytes; static unsigned int output_channels, sample_rate; -static uint32_t missing_count; static uint8_t quitting = 0; -struct audio_source { - int16_t *front; - int16_t *back; - double dt; - uint64_t buffer_fraction; - uint64_t buffer_inc; - uint32_t buffer_pos; - uint32_t read_start; - uint32_t read_end; - uint32_t lowpass_alpha; - uint32_t mask; - int16_t last_left; - int16_t last_right; - uint8_t num_channels; - uint8_t front_populated; -}; - -static audio_source *audio_sources[8]; -static audio_source *inactive_audio_sources[8]; -static uint8_t num_audio_sources; -static uint8_t num_inactive_audio_sources; -static uint32_t min_buffered; - -typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len); - -static int32_t mix_s16(audio_source *audio, void *vstream, int len) -{ - int samples = len/(sizeof(int16_t)*output_channels); - int16_t *stream = vstream; - int16_t *end = stream + output_channels*samples; - int16_t *src = audio->front; - uint32_t i = audio->read_start; - uint32_t i_end = audio->read_end; - int16_t *cur = stream; - size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; - if (audio->num_channels == 1) { - while (cur < end && i != i_end) - { - *cur += src[i]; - cur += first_add; - *cur += src[i++]; - cur += second_add; - i &= audio->mask; - } - } else { - while (cur < end && i != i_end) - { - *cur += src[i++]; - cur += first_add; - *cur += src[i++]; - cur += second_add; - i &= audio->mask; - } - } - - if (cur != end) { - printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); - } - if (cur != end) { - //printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); - return (cur-end)/2; - } else { - return ((i_end - i) & audio->mask) / audio->num_channels; - } -} - -static int32_t mix_f32(audio_source *audio, void *vstream, int len) -{ - int samples = len/(sizeof(float)*output_channels); - float *stream = vstream; - float *end = stream + output_channels*samples; - int16_t *src = audio->front; - uint32_t i = audio->read_start; - uint32_t i_end = audio->read_end; - float *cur = stream; - size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; - if (audio->num_channels == 1) { - while (cur < end && i != i_end) - { - *cur += ((float)src[i]) / 0x7FFF; - cur += first_add; - *cur += ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } else { - while(cur < end && i != i_end) - { - *cur += ((float)src[i++]) / 0x7FFF; - cur += first_add; - *cur += ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } - if (cur != end) { - printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); - return (cur-end)/2; - } else { - return ((i_end - i) & audio->mask) / audio->num_channels; - } -} - -static int32_t mix_null(audio_source *audio, void *vstream, int len) -{ - return 0; -} - -static mix_func mix; static void render_close_audio() { } -#define BUFFER_INC_RES 0x40000000UL - -void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) -{ - src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; -} - -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) -{ - audio_source *ret = NULL; - uint32_t alloc_size = channels * buffer_samples; - if (num_audio_sources < 8) { - ret = malloc(sizeof(audio_source)); - ret->back = malloc(alloc_size * sizeof(int16_t)); - ret->front = malloc(alloc_size * sizeof(int16_t)); - ret->front_populated = 0; - ret->num_channels = channels; - audio_sources[num_audio_sources++] = ret; - } - if (!ret) { - fatal_error("Too many audio sources!"); - } else { - render_audio_adjust_clock(ret, master_clock, sample_divider); - double lowpass_cutoff = get_lowpass_cutoff(config); - double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); - double alpha = ret->dt / (ret->dt + rc); - ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); - ret->buffer_pos = 0; - ret->buffer_fraction = 0; - ret->last_left = ret->last_right = 0; - ret->read_start = 0; - ret->read_end = buffer_samples * channels; - ret->mask = 0xFFFFFFFF; - } - return ret; -} - -void render_pause_source(audio_source *src) -{ - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (audio_sources[i] == src) { - audio_sources[i] = audio_sources[--num_audio_sources]; - break; - } - } - inactive_audio_sources[num_inactive_audio_sources++] = src; -} - -void render_resume_source(audio_source *src) -{ - if (num_audio_sources < 8) { - audio_sources[num_audio_sources++] = src; - } - for (uint8_t i = 0; i < num_inactive_audio_sources; i++) - { - if (inactive_audio_sources[i] == src) { - inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; - } - } -} - -void render_free_source(audio_source *src) -{ - render_pause_source(src); - - free(src->front); - free(src->back); - free(src); -} -snd_pcm_t *audio_handle; -static void do_audio_ready(audio_source *src) +static snd_pcm_t *audio_handle; +static void *output_buffer; +void render_do_audio_ready(audio_source *src) { if (src->front_populated) { fatal_error("Audio source filled up a buffer a second time before other sources finished their first\n"); @@ -254,22 +75,12 @@ src->front_populated = 1; src->buffer_pos = 0; - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (!audio_sources[i]->front_populated) { - //at least one audio source is not ready yet. - return; - } + if (!all_sources_ready()) { + return; } - - size_t bytes = (mix == mix_s16 ? sizeof(int16_t) : sizeof(float)) * output_channels * buffer_samples; - void *buffer = malloc(bytes); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - mix(audio_sources[i], buffer, bytes); - audio_sources[i]->front_populated = 0; - } - int frames = snd_pcm_writei(audio_handle, buffer, buffer_samples); + mix_and_convert(output_buffer, buffer_bytes, NULL); + + int frames = snd_pcm_writei(audio_handle, output_buffer, buffer_samples); if (frames < 0) { frames = snd_pcm_recover(audio_handle, frames, 0); } @@ -278,60 +89,6 @@ } } -static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) -{ - int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); - current = tmp >> 16; - return current; -} - -static void interp_sample(audio_source *src, int16_t last, int16_t current) -{ - int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); - tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); - src->back[src->buffer_pos++] = tmp >> 16; -} - -void render_put_mono_sample(audio_source *src, int16_t value) -{ - value = lowpass_sample(src, src->last_left, value); - src->buffer_fraction += src->buffer_inc; - uint32_t base = 0; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - interp_sample(src, src->last_left, value); - - if (((src->buffer_pos - base) & src->mask) >= buffer_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = value; -} - -void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) -{ - left = lowpass_sample(src, src->last_left, left); - right = lowpass_sample(src, src->last_right, right); - src->buffer_fraction += src->buffer_inc; - uint32_t base = 0; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - - interp_sample(src, src->last_left, left); - interp_sample(src, src->last_right, right); - - if (((src->buffer_pos - base) & src->mask)/2 >= buffer_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = left; - src->last_right = right; -} - int render_width() { return main_width; diff -r cafde1255ad3 -r a7b753e260a2 render_sdl.c --- a/render_sdl.c Sun Apr 19 00:59:49 2020 -0700 +++ b/render_sdl.c Sat May 09 23:39:44 2020 -0700 @@ -50,7 +50,14 @@ static SDL_cond *audio_ready, *frame_ready; static uint8_t quitting = 0; -static uint8_t sync_to_audio, run_on_audio_thread; +enum { + SYNC_AUDIO, + SYNC_AUDIO_THREAD, + SYNC_VIDEO, + SYNC_EXTERNAL +}; + +static uint8_t sync_src; static uint32_t min_buffered; uint32_t **frame_buffers; @@ -64,12 +71,12 @@ uint8_t render_is_audio_sync(void) { - return sync_to_audio || run_on_audio_thread; + return sync_src < SYNC_VIDEO; } uint8_t render_should_release_on_exit(void) { - return !run_on_audio_thread; + return sync_src != SYNC_AUDIO_THREAD; } void render_buffer_consumed(audio_source *src) @@ -120,7 +127,7 @@ void render_lock_audio() { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_LockMutex(audio_mutex); } else { SDL_LockAudio(); @@ -129,7 +136,7 @@ void render_unlock_audio() { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_UnlockMutex(audio_mutex); } else { SDL_UnlockAudio(); @@ -164,34 +171,40 @@ void render_audio_created(audio_source *source) { - if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { + if (render_is_audio_sync()) { SDL_PauseAudio(0); } - if (current_system) { - current_system->request_exit(current_system); + if (current_system && sync_src == SYNC_AUDIO_THREAD) { + system_request_exit(current_system, 0); } } void render_source_paused(audio_source *src, uint8_t remaining_sources) { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_CondSignal(audio_ready); } - if (!remaining_sources) { - SDL_PauseAudio(0); + if (!remaining_sources && render_is_audio_sync()) { + SDL_PauseAudio(1); + if (sync_src == SYNC_AUDIO_THREAD) { + SDL_CondSignal(frame_ready); + } } } void render_source_resumed(audio_source *src) { - if (sync_to_audio) { + if (render_is_audio_sync()) { SDL_PauseAudio(0); } + if (current_system && sync_src == SYNC_AUDIO_THREAD) { + system_request_exit(current_system, 0); + } } void render_do_audio_ready(audio_source *src) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD) { int16_t *tmp = src->front; src->front = src->back; src->back = tmp; @@ -199,9 +212,9 @@ src->buffer_pos = 0; if (all_sources_ready()) { //we've emulated far enough to fill the current buffer - current_system->request_exit(current_system); + system_request_exit(current_system, 0); } - } else if (sync_to_audio) { + } else if (sync_src == SYNC_AUDIO) { SDL_LockMutex(audio_mutex); while (src->front_populated) { SDL_CondWait(src->opaque, audio_mutex); @@ -253,8 +266,21 @@ #endif } +static uint8_t external_sync; +void render_set_external_sync(uint8_t ext_sync_on) +{ + if (ext_sync_on != external_sync) { + external_sync = ext_sync_on; + if (windowed_width) { + //only do this if render_init has already been called + render_config_updated(); + } + } +} + #ifndef DISABLE_OPENGL -static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; +static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, un_texsize, at_pos; +static int tex_width, tex_height; static GLfloat vertex_data_default[] = { -1.0f, -1.0f, @@ -360,6 +386,15 @@ char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR; glGenTextures(3, textures); + def.ptrval = "off"; + char *npot_textures = tern_find_path_default(config, "video\0npot_textures\0", def, TVAL_PTR).ptrval; + if (!strcmp(npot_textures, "on")) { + tex_width = LINEBUF_SIZE; + tex_height = 294; //PAL height with full borders + } else { + tex_width = tex_height = 512; + } + printf("Using %dx%d textures\n", tex_width, tex_height); for (int i = 0; i < 3; i++) { glBindTexture(GL_TEXTURE_2D, textures[i]); @@ -369,7 +404,7 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (i < 2) { //TODO: Fixme for PAL + invalid display mode - glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 512, 512, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf); + glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, tex_width, tex_height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf); } else { uint32_t blank = 255 << 24; glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank); @@ -398,6 +433,7 @@ un_textures[1] = glGetUniformLocation(program, "textures[1]"); un_width = glGetUniformLocation(program, "width"); un_height = glGetUniformLocation(program, "height"); + un_texsize = glGetUniformLocation(program, "texsize"); at_pos = glGetAttribLocation(program, "pos"); } @@ -913,7 +949,17 @@ } debug_message("config says: %d\n", samples); desired.samples = samples*2; - desired.callback = sync_to_audio ? audio_callback : run_on_audio_thread ? audio_callback_run_on_audio : audio_callback_drc; + switch (sync_src) + { + case SYNC_AUDIO: + desired.callback = audio_callback; + break; + case SYNC_AUDIO_THREAD: + desired.callback = audio_callback_run_on_audio; + break; + default: + desired.callback = audio_callback_drc; + } desired.userdata = NULL; if (SDL_OpenAudio(&desired, &actual) < 0) { @@ -943,12 +989,31 @@ } tern_val def = {.ptrval = "audio"}; - char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; - sync_to_audio = !strcmp(sync_src, "audio"); - run_on_audio_thread = !strcmp(sync_src, "audio_thread"); + if (external_sync) { + sync_src = SYNC_EXTERNAL; + } else { + char *sync_src_str = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; + if (!strcmp(sync_src_str, "audio")) { + sync_src = SYNC_AUDIO; + } else if (!strcmp(sync_src_str, "audio_thread")) { + sync_src = SYNC_AUDIO_THREAD; + } else { + sync_src = SYNC_VIDEO; + } + } + + if (!num_buffers && (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL)) { + frame_mutex = SDL_CreateMutex(); + free_buffer_mutex = SDL_CreateMutex(); + frame_ready = SDL_CreateCond(); + buffer_storage = 4; + frame_buffers = calloc(buffer_storage, sizeof(uint32_t*)); + frame_buffers[0] = texture_buf; + num_buffers = 1; + } const char *vsync; - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { def.ptrval = "off"; vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; } else { @@ -1108,16 +1173,6 @@ audio_mutex = SDL_CreateMutex(); audio_ready = SDL_CreateCond(); - if (run_on_audio_thread) { - frame_mutex = SDL_CreateMutex(); - free_buffer_mutex = SDL_CreateMutex(); - frame_ready = SDL_CreateCond(); - buffer_storage = 4; - frame_buffers = calloc(buffer_storage, sizeof(uint32_t*)); - frame_buffers[0] = texture_buf; - num_buffers = 1; - } - init_audio(); uint32_t db_size; @@ -1136,13 +1191,10 @@ atexit(render_quit); } -#include static int in_toggle; void render_config_updated(void) { - uint8_t old_sync_to_audio = sync_to_audio; - free_surfaces(); #ifndef DISABLE_OPENGL if (render_gl) { @@ -1336,14 +1388,14 @@ uint32_t locked_pitch; uint32_t *render_get_framebuffer(uint8_t which, int *pitch) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { *pitch = LINEBUF_SIZE * sizeof(uint32_t); uint32_t *buffer; SDL_LockMutex(free_buffer_mutex); if (num_buffers) { buffer = frame_buffers[--num_buffers]; } else { - buffer = calloc(512*512, sizeof(uint32_t)); + buffer = calloc(tex_width*(tex_height + 1), sizeof(uint32_t)); } SDL_UnlockMutex(free_buffer_mutex); locked_pixels = buffer; @@ -1362,14 +1414,14 @@ warning("Request for invalid framebuffer number %d\n", which); return NULL; } - void *pixels; - if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) { + uint8_t *pixels; + if (SDL_LockTexture(sdl_textures[which], NULL, (void **)&pixels, pitch) < 0) { warning("Failed to lock texture: %s\n", SDL_GetError()); return NULL; } static uint8_t last; if (which <= FRAMEBUFFER_EVEN) { - locked_pixels = pixels; + locked_pixels = (uint32_t *)pixels; if (which == FRAMEBUFFER_EVEN) { pixels += *pitch; } @@ -1379,7 +1431,7 @@ } last = which; } - return pixels; + return (uint32_t *)pixels; #ifndef DISABLE_OPENGL } #endif @@ -1408,7 +1460,7 @@ static void process_framebuffer(uint32_t *buffer, uint8_t which, int width) { static uint8_t last; - if (!render_is_audio_sync() && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { + if (sync_src == SYNC_VIDEO && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { source_frame++; if (source_frame >= source_hz) { source_frame = 0; @@ -1464,7 +1516,7 @@ } } else { #endif - //TODO: Support run_on_audio_thread for render API framebuffers + //TODO: Support SYNC_AUDIO_THREAD/SYNC_EXTERNAL for render API framebuffers if (which <= FRAMEBUFFER_EVEN && last != which) { uint8_t *cur_dst = (uint8_t *)locked_pixels; uint8_t *cur_saved = (uint8_t *)texture_buf; @@ -1613,7 +1665,7 @@ void render_framebuffer_updated(uint8_t which, int width) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { SDL_LockMutex(frame_mutex); while (frame_queue_len == 4) { SDL_CondSignal(frame_ready); @@ -1651,25 +1703,29 @@ void render_video_loop(void) { - if (!run_on_audio_thread) { + if (sync_src != SYNC_AUDIO_THREAD && sync_src != SYNC_EXTERNAL) { return; } - SDL_PauseAudio(0); SDL_LockMutex(frame_mutex); for(;;) { - while (!frame_queue_len) + while (!frame_queue_len && SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) { SDL_CondWait(frame_ready, frame_mutex); } - for (int i = 0; i < frame_queue_len; i++) + while (frame_queue_len) { frame f = frame_queue[frame_queue_read++]; frame_queue_read &= 0x3; + frame_queue_len--; + SDL_UnlockMutex(frame_mutex); process_framebuffer(f.buffer, f.which, f.width); release_buffer(f.buffer); + SDL_LockMutex(frame_mutex); } - frame_queue_len = 0; + if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) { + break; + } } SDL_UnlockMutex(frame_mutex); @@ -1699,6 +1755,7 @@ glUniform1f(un_width, render_emulated_width()); glUniform1f(un_height, last_height); + glUniform2f(un_texsize, tex_width, tex_height); glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0); @@ -1974,3 +2031,9 @@ } return 0xFF; } + +uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data) +{ + *thread = SDL_CreateThread(fun, name, data); + return *thread != 0; +} diff -r cafde1255ad3 -r a7b753e260a2 romdb.c --- a/romdb.c Sun Apr 19 00:59:49 2020 -0700 +++ b/romdb.c Sat May 09 23:39:44 2020 -0700 @@ -675,7 +675,9 @@ *map = lock_info.map[i]; if (map->start < 0x200000) { if (map->buffer) { - map->buffer += (0x200000 - map->start) & ((map->flags & MMAP_AUX_BUFF) ? map->aux_mask : map->mask); + uint8_t *buf = map->buffer; + buf += (0x200000 - map->start) & ((map->flags & MMAP_AUX_BUFF) ? map->aux_mask : map->mask); + map->buffer = buf; } map->start = 0x200000; } diff -r cafde1255ad3 -r a7b753e260a2 saves.h --- a/saves.h Sun Apr 19 00:59:49 2020 -0700 +++ b/saves.h Sat May 09 23:39:44 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 cafde1255ad3 -r a7b753e260a2 serialize.c --- a/serialize.c Sun Apr 19 00:59:49 2020 -0700 +++ b/serialize.c Sat May 09 23:39:44 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 cafde1255ad3 -r a7b753e260a2 shaders/crt.f.glsl --- a/shaders/crt.f.glsl Sun Apr 19 00:59:49 2020 -0700 +++ b/shaders/crt.f.glsl Sat May 09 23:39:44 2020 -0700 @@ -1,5 +1,3 @@ -#version 110 - /* Subtle CRT shader usable in fullscreen - AnaĆ«l Seghezzi [anael(at)maratis3d.com] This shader is free software distributed under the terms of the GNU General Public License version 3 or higher. This gives you the right to redistribute and/or @@ -10,63 +8,64 @@ #define M_PI 3.14159265358979323846 uniform sampler2D textures[2]; -uniform float width, height; -varying vec2 texcoord; -varying vec2 screencoord; +uniform mediump float width, height; +uniform mediump vec2 texsize; +varying mediump vec2 texcoord; +varying mediump vec2 screencoord; -float nrand(vec2 n) { +mediump float nrand(vec2 n) { return fract(sin(dot(n.xy, vec2(12.9898, 78.233))) * 43758.5453); } -float scanline(vec2 texco) +mediump float scanline(vec2 texco) { - return (1.0 - abs(cos(texco.y * 512.0 * M_PI))); + return (1.0 - abs(cos(texco.y * texsize.y * M_PI))); } -vec2 sharp_coord(vec2 texco, vec2 dim, vec2 sharpness) +mediump vec2 sharp_coord(mediump vec2 texco, mediump vec2 dim, mediump vec2 sharpness) { - vec2 texcoif = texco * dim; - vec2 texcoi = floor(texcoif); - vec2 mu = (texcoif - 0.5) - texcoi; - vec2 mub = pow(abs(mu) * 2.0, sharpness) * sign(mu) * 0.5; + mediump vec2 texcoif = texco * dim; + mediump vec2 texcoi = floor(texcoif); + mediump vec2 mu = (texcoif - 0.5) - texcoi; + mediump vec2 mub = pow(abs(mu) * 2.0, sharpness) * sign(mu) * 0.5; return (texcoi + mub + 0.5) / dim; } void main() { - float v = 1.0 / 512.0; - float yforce = 0.175; - float vign = length(screencoord); + mediump float v = 1.0 / texsize.y; + mediump float yforce = 0.175; + mediump float vign = length(screencoord); // monitor deformation - vec2 monitorcoord = (screencoord + screencoord * vign * 0.025); + mediump vec2 monitorcoord = (screencoord + screencoord * vign * 0.025); if (monitorcoord.x < -1.0 || monitorcoord.y < -1.0 || monitorcoord.x > 1.0 || monitorcoord.y > 1.0) { gl_FragColor = vec4(0.0); return; } - vec2 texco = monitorcoord * vec2(width/1024.0, height/-1024.0) + vec2(width/1024.0, height/1024.0); + mediump vec2 texco = monitorcoord * vec2(0.5*width/texsize.x, -0.5 * height/texsize.y) + vec2(0.5*width/texsize.x, 0.5*height/texsize.y); // mask - float maskx = 1.0 - pow(abs(monitorcoord.x), 200.0); - float masky = 1.0 - pow(abs(-monitorcoord.y), 200.0); - float mask = clamp(maskx * masky, 0.0, 1.0); + mediump float maskx = 1.0 - pow(abs(monitorcoord.x), 200.0); + mediump float masky = 1.0 - pow(abs(-monitorcoord.y), 200.0); + mediump float mask = clamp(maskx * masky, 0.0, 1.0); // sharp texcoord - vec2 texco_sharp0 = sharp_coord(texco, vec2(512.0, 512.0), vec2(4.0, 8.0)); - vec2 texco_sharp1 = sharp_coord(texco - vec2(0.0, 1.0 / 1024.0), vec2(512.0, 512.0), vec2(4.0, 8.0)); + mediump vec2 texco_sharp0 = sharp_coord(texco, texsize, vec2(4.0, 8.0)); + mediump vec2 texco_sharp1 = sharp_coord(texco - vec2(0.0, 0.5 / texsize.y), texsize, vec2(4.0, 8.0)); - vec4 src0 = texture2D(textures[0], texco_sharp0); - vec4 src1 = texture2D(textures[1], texco_sharp1); + mediump vec4 src0 = texture2D(textures[0], texco_sharp0); + mediump vec4 src1 = texture2D(textures[1], texco_sharp1); // interlace mix - float interlace = cos((texco.y * 1024.0) * M_PI); - vec4 src_mix = mix(src0, src1, interlace * 0.5 + 0.5); + mediump float interlace = cos((texco.y * 2.0 * texsize.y) * M_PI); + mediump vec4 src_mix = mix(src0, src1, interlace * 0.5 + 0.5); // blur - vec4 src_blur = mix(texture2D(textures[0], texco), texture2D(textures[1], texco), 0.5); + mediump vec4 src_blur = mix(texture2D(textures[0], texco), texture2D(textures[1], texco), 0.5); #ifdef NO_SCANLINE @@ -75,11 +74,11 @@ #else // multisample scanline with grain // TODO: offset grain with time (needs a "frame" uniform) - float cosy; - cosy = scanline(texco + vec2(0.125, v * (nrand(texcoord + vec2(0.0, 1.0)) * 0.25) + 0.3333)); - cosy += scanline(texco + vec2(0.25, v * (nrand(texcoord + vec2(0.0, 2.0)) * 0.25) + 0.25)); - cosy += scanline(texco + vec2(0.50, v * (nrand(texcoord + vec2(0.0, 3.0)) * 0.25) + 0.6666)); - cosy += scanline(texco + vec2(0.75, v * (nrand(texcoord + vec2(0.0, 4.0)) * 0.25) + 0.75)); + mediump float cosy; + cosy = scanline(texco + vec2(0.125, v * (nrand(texcoord + vec2(0.0, 512.0/texsize.y)) * 0.25) + 512.0*0.3333/texsize.y)); + cosy += scanline(texco + vec2(0.25, v * (nrand(texcoord + vec2(0.0, 1024.0/texsize.y)) * 0.25) + 512.0*0.25/texsize.y)); + cosy += scanline(texco + vec2(0.50, v * (nrand(texcoord + vec2(0.0, 1536.0/texsize.y)) * 0.25) + 512.0*0.6666/texsize.y)); + cosy += scanline(texco + vec2(0.75, v * (nrand(texcoord + vec2(0.0, 2048.0/texsize.y)) * 0.25) + 512.0*0.75/texsize.y)); cosy *= 0.25; // final scanline + burn diff -r cafde1255ad3 -r a7b753e260a2 shaders/crt.v.glsl --- a/shaders/crt.v.glsl Sun Apr 19 00:59:49 2020 -0700 +++ b/shaders/crt.v.glsl Sat May 09 23:39:44 2020 -0700 @@ -1,13 +1,13 @@ -#version 110 attribute vec2 pos; -varying vec2 texcoord; -varying vec2 screencoord; -uniform float width, height; +varying mediump vec2 texcoord; +varying mediump vec2 screencoord; +uniform mediump float width, height; +uniform mediump vec2 texsize; void main() { gl_Position = vec4(pos, 0.0, 1.0); - texcoord = sign(pos) * vec2(width/1024.0, height/-1024.0) + vec2(width/1024.0, height/1024.0); + texcoord = sign(pos) * vec2(0.5*width/texsize.x, -0.5*height/texsize.y) + vec2(0.5*width/texsize.x, 0.5*height/texsize.y); screencoord = sign(pos); } \ No newline at end of file diff -r cafde1255ad3 -r a7b753e260a2 shaders/default.f.glsl --- a/shaders/default.f.glsl Sun Apr 19 00:59:49 2020 -0700 +++ b/shaders/default.f.glsl Sat May 09 23:39:44 2020 -0700 @@ -1,15 +1,16 @@ uniform sampler2D textures[2]; +uniform mediump vec2 texsize; varying mediump vec2 texcoord; void main() { - mediump vec2 modifiedCoord0 = vec2(texcoord.x, (floor(texcoord.y * 512.0 + 0.25) + 0.5)/512.0); - mediump vec2 modifiedCoord1 = vec2(texcoord.x, (floor(texcoord.y * 512.0 - 0.25) + 0.5)/512.0); + mediump vec2 modifiedCoord0 = vec2(texcoord.x, (floor(texcoord.y * texsize.y + 0.25) + 0.5)/texsize.y); + mediump vec2 modifiedCoord1 = vec2(texcoord.x, (floor(texcoord.y * texsize.y - 0.25) + 0.5)/texsize.y); gl_FragColor = mix( texture2D(textures[1], modifiedCoord1), texture2D(textures[0], modifiedCoord0), - (sin(texcoord.y * 1024.0 * 3.14159265359) + 1.0) * 0.5 + (sin(texcoord.y * texsize.y * 6.283185307) + 1.0) * 0.5 ); } diff -r cafde1255ad3 -r a7b753e260a2 shaders/default.v.glsl --- a/shaders/default.v.glsl Sun Apr 19 00:59:49 2020 -0700 +++ b/shaders/default.v.glsl Sat May 09 23:39:44 2020 -0700 @@ -2,9 +2,10 @@ attribute vec2 pos; varying mediump vec2 texcoord; uniform mediump float width, height; +uniform mediump vec2 texsize; void main() { gl_Position = vec4(pos, 0.0, 1.0); - texcoord = sign(pos) * vec2(width / 1024.0, height / -1024.0) + vec2(width / 1024.0, height / 1024.0); + texcoord = sign(pos) * vec2(0.5 * width / texsize.x, -0.5 * height / texsize.y) + vec2(0.5 * width / texsize.x, 0.5 * height / texsize.y); } diff -r cafde1255ad3 -r a7b753e260a2 shaders/sharp.f.glsl --- a/shaders/sharp.f.glsl Sun Apr 19 00:59:49 2020 -0700 +++ b/shaders/sharp.f.glsl Sat May 09 23:39:44 2020 -0700 @@ -1,20 +1,18 @@ uniform sampler2D textures[2]; +uniform mediump vec2 texsize; varying mediump vec2 texcoord; void main() { - mediump float x0 = (floor(texcoord.x * 512.0 - 0.25) + 0.5)/512.0; - mediump float x1 = (floor(texcoord.x * 512.0 + 0.25) + 0.5)/512.0; - mediump float y0 = (floor(texcoord.y * 512.0 + 0.25) + 0.5)/512.0; - mediump float y1 = (floor(texcoord.y * 512.0 - 0.25) + 0.5)/512.0; + mediump float x0 = (floor(texcoord.x * texsize.x - 0.25) + 0.5)/texsize.x; + mediump float x1 = (floor(texcoord.x * texsize.x + 0.25) + 0.5)/texsize.x; + mediump float y0 = (floor(texcoord.y * texsize.y + 0.25) + 0.5)/texsize.y; + mediump float y1 = (floor(texcoord.y * texsize.y - 0.25) + 0.5)/texsize.y; - - mediump vec2 modifiedCoord0 = vec2(texcoord.x, (floor(texcoord.y * 512.0 + 0.25) + 0.5)/512.0); - mediump vec2 modifiedCoord1 = vec2(texcoord.x, (floor(texcoord.y * 512.0 - 0.25) + 0.5)/512.0); - mediump float ymix = (sin(texcoord.y * 1024.0 * 3.14159265359) + 1.0) * 0.5; - mediump float xmix = (sin(texcoord.x * 1024.0 * 3.14159265359) + 1.0) * 0.5; + mediump float ymix = (sin(texcoord.y * texsize.y * 6.283185307) + 1.0) * 0.5; + mediump float xmix = (sin(texcoord.x * texsize.x * 6.283185307) + 1.0) * 0.5; gl_FragColor = mix( mix( texture2D(textures[1], vec2(x0, y1)), diff -r cafde1255ad3 -r a7b753e260a2 sms.c --- a/sms.c Sun Apr 19 00:59:49 2020 -0700 +++ b/sms.c Sat May 09 23:39:44 2020 -0700 @@ -430,7 +430,7 @@ target_cycle -= adjust; } } - if (render_should_release_on_exit()) { + if (sms->header.force_release || render_should_release_on_exit()) { bindings_release_capture(); vdp_release_framebuffer(sms->vdp); render_pause_source(sms->psg->audio); @@ -441,7 +441,8 @@ static void resume_sms(system_header *system) { sms_context *sms = (sms_context *)system; - if (render_should_release_on_exit()) { + if (sms->header.force_release || render_should_release_on_exit()) { + sms->header.force_release = 0; bindings_reacquire_capture(); vdp_reacquire_framebuffer(sms->vdp); render_resume_source(sms->psg->audio); diff -r cafde1255ad3 -r a7b753e260a2 stateview.c --- a/stateview.c Sun Apr 19 00:59:49 2020 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - Copyright 2013 Michael Pavone - This file is part of BlastEm. - BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. -*/ -#include -#include -#include "vdp.h" -#include "render.h" -#include "util.h" -#include "genesis.h" -#include "config.h" - - -uint16_t read_dma_value(uint32_t address) -{ - return 0; -} - -m68k_context *m68k_handle_code_write(uint32_t address, m68k_context *context) -{ - return NULL; -} - -z80_context *z80_handle_code_write(uint32_t address, z80_context *context) -{ - return NULL; -} - -void ym_data_write(ym2612_context * context, uint8_t value) -{ -} - -void ym_address_write_part1(ym2612_context * context, uint8_t address) -{ -} - -void ym_address_write_part2(ym2612_context * context, uint8_t address) -{ -} - -void handle_keydown(int keycode, uint8_t scancode) -{ -} - -void handle_keyup(int keycode, uint8_t scancode) -{ -} - -void handle_joydown(int joystick, int button) -{ -} - -void handle_joyup(int joystick, int button) -{ -} - -void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) -{ -} - -void handle_joy_axis(int joystick, int axis, int16_t value) -{ -} - -void handle_joy_added(int joystick) -{ -} - -void handle_mousedown(int mouse, int button) -{ -} - -void handle_mouseup(int mouse, int button) -{ -} - -void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay) -{ -} - -tern_node * config; -int headless = 0; - -int main(int argc, char ** argv) -{ - if (argc < 2) { - fatal_error("Usage: stateview FILENAME\n"); - } - FILE * state_file = fopen(argv[1], "rb"); - if (!state_file) { - fatal_error("Failed to open %s\n", argv[1]); - } - set_exe_str(argv[0]); - config = load_config(argv[0]); - int width = -1; - int height = -1; - if (argc > 2) { - width = atoi(argv[2]); - if (argc > 3) { - height = atoi(argv[3]); - } - } - int def_width = 0; - char *config_width = tern_find_ptr(config, "videowidth"); - if (config_width) { - def_width = atoi(config_width); - } - if (!def_width) { - def_width = 640; - } - width = width < 320 ? def_width : width; - height = height < 240 ? (width/320) * 240 : height; - - render_init(width, height, "GST State Viewer", 0); - vdp_context *context = init_vdp_context(0, 0); - vdp_load_gst(context, state_file); - vdp_run_to_vblank(context); - vdp_print_sprite_table(context); - printf("Display %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); - if (!(context->regs[REG_MODE_2] & DISPLAY_ENABLE)) { - puts("Forcing display on"); - vdp_control_port_write(context, 0x8000 | REG_MODE_2 << 8 | context->regs[REG_MODE_2] | DISPLAY_ENABLE); - } - render_wait_quit(); - return 0; -} diff -r cafde1255ad3 -r a7b753e260a2 system.c --- a/system.c Sun Apr 19 00:59:49 2020 -0700 +++ b/system.c Sat May 09 23:39:44 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,19 @@ 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; +} + +void system_request_exit(system_header *system, uint8_t force_release) +{ + system->force_release = force_release; + system->request_exit(system); +} diff -r cafde1255ad3 -r a7b753e260a2 system.h --- a/system.h Sun Apr 19 00:59:49 2020 -0700 +++ b/system.h Sat May 09 23:39:44 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; @@ -70,6 +73,7 @@ uint8_t delayed_load_slot; uint8_t has_keyboard; uint8_t vgm_logging; + uint8_t force_release; debugger_type debugger_type; system_type type; }; @@ -87,5 +91,7 @@ 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); +void system_request_exit(system_header *system, uint8_t force_release); #endif //SYSTEM_H_ diff -r cafde1255ad3 -r a7b753e260a2 util.c --- a/util.c Sun Apr 19 00:59:49 2020 -0700 +++ b/util.c Sat May 09 23:39:44 2020 -0700 @@ -7,7 +7,6 @@ #include #include -#include #include #ifdef __ANDROID__ @@ -442,6 +441,10 @@ exit(1); } +#ifndef _WIN32 +#include +#endif + void warning(char *format, ...) { va_list args; @@ -532,6 +535,8 @@ } #ifdef _WIN32 +#define WINVER 0x501 +#include #include #include @@ -679,7 +684,80 @@ return CreateDirectory(path, NULL); } +static WSADATA wsa_data; +static void socket_cleanup(void) +{ + WSACleanup(); +} + +void socket_init(void) +{ + static uint8_t started; + if (!started) { + started = 1; + WSAStartup(MAKEWORD(2,2), &wsa_data); + atexit(socket_cleanup); + } +} + +int socket_blocking(int sock, int should_block) +{ + u_long param = !should_block; + if (ioctlsocket(sock, FIONBIO, ¶m)) { + return WSAGetLastError(); + } + return 0; +} + +void socket_close(int sock) +{ + closesocket(sock); +} + +int socket_last_error(void) +{ + return WSAGetLastError(); +} + +int socket_error_is_wouldblock(void) +{ + return WSAGetLastError() == WSAEWOULDBLOCK; +} + #else +#include +#include + +void socket_init(void) +{ + //SIGPIPE on network sockets is not desired + //would be better to do this in a more limited way, + //but the alternatives are not portable + signal(SIGPIPE, SIG_IGN); +} + +int socket_blocking(int sock, int should_block) +{ + if (fcntl(sock, F_SETFL, should_block ? 0 : O_NONBLOCK)) { + return errno; + } + return 0; +} + +void socket_close(int sock) +{ + close(sock); +} + +int socket_last_error(void) +{ + return errno; +} + +int socket_error_is_wouldblock(void) +{ + return errno == EAGAIN || errno == EWOULDBLOCK; +} char * get_home_dir() { diff -r cafde1255ad3 -r a7b753e260a2 util.h --- a/util.h Sun Apr 19 00:59:49 2020 -0700 +++ b/util.h Sat May 09 23:39:44 2020 -0700 @@ -90,5 +90,15 @@ void disable_stdout_messages(void); //Deletes a file, returns true on success, false on failure uint8_t delete_file(char *path); +//Initializes the socket library on platforms that need it +void socket_init(void); +//Sets a sockt to blocking or non-blocking mode +int socket_blocking(int sock, int should_block); +//Close a socket +void socket_close(int sock); +//Return the last error on a socket operation +int socket_last_error(void); +//Returns if the last socket error was EAGAIN/EWOULDBLOCK +int socket_error_is_wouldblock(void); #endif //UTIL_H_ diff -r cafde1255ad3 -r a7b753e260a2 vdp.c --- a/vdp.c Sun Apr 19 00:59:49 2020 -0700 +++ b/vdp.c Sat May 09 23:39:44 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 & 127, val >> 8, val}; + event_log(EVENT_VDP_INTRAM, 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) + 128, context->vsram[(start->address/2) & 63] >> 8, context->vsram[(start->address/2) & 63]}; + event_log(EVENT_VDP_INTRAM, context->cycles, sizeof(buffer), buffer); } break; @@ -3760,6 +3768,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 +4561,85 @@ } } } + +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: + reader_ensure_data(reader, 3); + address = load_int16(buffer); + break; + case EVENT_VRAM_BYTE_DELTA: + reader_ensure_data(reader, 2); + address = reader->last_byte_address + load_int8(buffer); + break; + case EVENT_VRAM_BYTE_ONE: + reader_ensure_data(reader, 1); + address = reader->last_byte_address + 1; + break; + case EVENT_VRAM_BYTE_AUTO: + reader_ensure_data(reader, 1); + address = reader->last_byte_address + context->regs[REG_AUTOINC]; + break; + case EVENT_VRAM_WORD: + reader_ensure_data(reader, 4); + address = load_int8(buffer) << 16; + address |= load_int16(buffer); + break; + case EVENT_VRAM_WORD_DELTA: + reader_ensure_data(reader, 3); + address = reader->last_word_address + load_int8(buffer); + break; + case EVENT_VDP_REG: + case EVENT_VDP_INTRAM: + reader_ensure_data(reader, event == EVENT_VDP_REG ? 2 : 3); + 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_VDP_INTRAM: + if (address < 128) { + write_cram(context, address, load_int16(buffer)); + } else { + context->vsram[address&63] = load_int16(buffer); + } + break; + } +} diff -r cafde1255ad3 -r a7b753e260a2 vdp.h --- a/vdp.h Sun Apr 19 00:59:49 2020 -0700 +++ b/vdp.h Sat May 09 23:39:44 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 cafde1255ad3 -r a7b753e260a2 vgmplay.c --- a/vgmplay.c Sun Apr 19 00:59:49 2020 -0700 +++ b/vgmplay.c Sat May 09 23:39:44 2020 -0700 @@ -13,6 +13,7 @@ #include #include #include "vgm.h" +#include "system.h" #define MCLKS_NTSC 53693175 #define MCLKS_PAL 53203395 @@ -22,6 +23,8 @@ #define MCLKS_PER_Z80 15 #define MCLKS_PER_PSG (MCLKS_PER_Z80*16) +system_header *current_system; + void handle_keydown(int keycode) { } diff -r cafde1255ad3 -r a7b753e260a2 ym2612.c --- a/ym2612.c Sun Apr 19 00:59:49 2020 -0700 +++ b/ym2612.c Sat May 09 23:39:44 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