Mercurial > repos > blastem
changeset 2600:251cc75574af
Basic emscripten support
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 13 Feb 2025 02:18:30 -0800 |
parents | ca8141c2d6ba |
children | 6a84a38b3cf9 |
files | Makefile blastem.c cpu_dsl.py genesis.c nuklear_ui/blastem_nuklear.c nuklear_ui/blastem_nuklear.h render.h render_sdl.c sms.c z80.cpu |
diffstat | 10 files changed, 196 insertions(+), 49 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sun Feb 09 22:46:07 2025 -0800 +++ b/Makefile Thu Feb 13 02:18:30 2025 -0800 @@ -9,6 +9,7 @@ endif SDL_UPPER:=$(shell echo $(SDL) | tr '[a-z]' '[A-Z]') FIXUP:=true +Z80_DISPATCH:=goto BUNDLED_LIBZ:=zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/deflate.o zlib/gzclose.o zlib/gzlib.o zlib/gzread.o\ zlib/gzwrite.o zlib/infback.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o zlib/uncompr.o zlib/zutil.o @@ -62,6 +63,10 @@ else SO:=so +ifeq ($(CPU),wasm) +USE_GLES:=1 +endif + ifdef USE_FBDEV LIBS=alsa ifndef NOGL @@ -76,6 +81,11 @@ LIBS=$(SDL) glew gl endif #USE_GLES endif #USE_FBDEV +ifeq ($(CPU),wasm) +CHOOSER:=nuklear_ui/filechooser_null.o +FONT:=nuklear_ui/font_web.o +Z80_DISPATCH:=call +else #CPU=wasm FONT:=nuklear_ui/font.o ifneq ($(MAKECMDGOALS),libblastem.$(SO)) CHOOSER:=nuklear_ui/filechooser_gtk.o @@ -87,6 +97,7 @@ endif endif endif #neq ($(MAKECMDGOALS),libblastem.$(SO)) +endif #CPU=wasm ifeq ($(GTKFLAGS),) else EXTRA_NUKLEAR_LDFLAGS:=-ldl @@ -106,6 +117,12 @@ CFLAGS+= -Wno-logical-op-parentheses endif +ifeq ($(CPU),wasm) +CFLAGS+= --use-port=sdl2 +LDFLAGS+= --use-port=sdl2 --embed-file rom.db --embed-file default.cfg --embed-file systems.cfg --embed-file shaders/ --embed-file images/ --embed-file DroidSans.ttf --embed-file roms/ +EXE:=.html +else #CPU=wasm + ifdef PORTABLE ifdef USE_GLES ifndef GLES_LIB @@ -152,6 +169,7 @@ endif endif #PORTABLE +endif #CPU=wasm endif #Windows ifndef OPT @@ -372,8 +390,9 @@ m68k.c : m68k.cpu cpu_dsl.py ./cpu_dsl.py -d call $< > $@ +.PRECIOUS: %.c %.c : %.cpu cpu_dsl.py - ./cpu_dsl.py -d goto $< > $@ + ./cpu_dsl.py -d $(Z80_DISPATCH) $< > $@ %.db.c : %.db sed $< -e 's/"/\\"/g' -e 's/^\(.*\)$$/"\1\\n"/' -e'1s/^\(.*\)$$/const char $(shell echo $< | tr '.' '_')_data[] = \1/' -e '$$s/^\(.*\)$$/\1;/' > $@
--- a/blastem.c Sun Feb 09 22:46:07 2025 -0800 +++ b/blastem.c Thu Feb 13 02:18:30 2025 -0800 @@ -30,6 +30,9 @@ #ifndef DISABLE_NUKLEAR #include "nuklear_ui/blastem_nuklear.h" #endif +#ifdef __EMSCRIPTEN__ +#include <emscripten.h> +#endif #include "version.inc" @@ -271,6 +274,64 @@ update_title(game_system->info.name); } +static uint8_t menu; +static uint8_t use_nuklear; +#ifdef __EMSCRIPTEN__ +void handle_frame_presented(void) +{ + if (current_system) { + current_system->request_exit(current_system); + } +} + +void browser_main_loop(void) +{ + static uint8_t system_started; +#ifndef DISABLE_NUKLEAR + static uint8_t was_menu; + if (use_nuklear) { + if (menu && !was_menu) { + ui_enter(); + } else if (!menu && was_menu) { + ui_exit(); + } + if (menu) { + render_update_display(); + } + } +#endif + if (!current_system && game_system) { + current_system = game_system; + menu = 0; +#ifndef DISABLE_NUKLEAR + was_menu = 0; + ui_exit(); +#endif + } + if (current_system) { + if (current_system->next_rom) { + char *next_rom = current_system->next_rom; + current_system->next_rom = NULL; + init_system_with_media(next_rom, 0); + system_started = 0; + menu = 0; + current_system = game_system; + } else if (!menu) { + if (system_started) { + current_system->resume_context(current_system); + } else { + system_started = 1; + current_system->start_context(current_system, NULL); + } + if (current_system->force_release) { + menu = 1; + } + } + } + +} +#endif + char *parse_addr_port(char *arg) { while (*arg && *arg != ':') { @@ -478,6 +539,12 @@ if (config_fullscreen && !strcmp("on", config_fullscreen)) { fullscreen = !fullscreen; } +#ifdef __EMSCRIPTEN__ + config = tern_insert_path(config, "system\0sync_source\0", (tern_val){.ptrval = strdup("video")}, TVAL_PTR); + config = tern_insert_path(config, "ui\0initial_path\0", (tern_val){.ptrval = strdup("/roms")}, TVAL_PTR); + render_set_frame_presented_fun(handle_frame_presented); + emscripten_set_main_loop(browser_main_loop, 0, 0); +#endif if (!headless) { if (reader_addr) { render_set_external_sync(1); @@ -486,9 +553,8 @@ render_set_drag_drop_handler(on_drag_drop); } set_bindings(); + menu = !loaded; - uint8_t menu = !loaded; - uint8_t use_nuklear = 0; #ifndef DISABLE_NUKLEAR use_nuklear = !headless && is_nuklear_available(); #endif @@ -554,8 +620,10 @@ #ifndef DISABLE_NUKLEAR if (use_nuklear) { blastem_nuklear_init(!menu); +#ifndef __EMSCRIPTEN__ current_system = game_system; menu = 0; +#endif } #endif @@ -574,6 +642,7 @@ current_system->debugger_type = dtype; current_system->enter_debugger = start_in_debugger && menu == debug_target; +#ifndef __EMSCRIPTEN__ current_system->start_context(current_system, menu ? NULL : statefile); render_video_loop(); for(;;) @@ -614,6 +683,7 @@ break; } } +#endif //__EMSCRIPTEN__ return 0; }
--- a/cpu_dsl.py Sun Feb 09 22:46:07 2025 -0800 +++ b/cpu_dsl.py Thu Feb 13 02:18:30 2025 -0800 @@ -243,7 +243,7 @@ del self.locals[name] if prog.dispatch == 'call': - begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context, uint32_t target_cycle)\n{' + begin = '\nstatic void ' + self.generateName(value) + '(' + prog.context_type + ' *context, uint32_t target_cycle)\n{' elif prog.dispatch == 'goto': begin = '\n' + self.generateName(value) + ': {' else:
--- a/genesis.c Sun Feb 09 22:46:07 2025 -0800 +++ b/genesis.c Thu Feb 13 02:18:30 2025 -0800 @@ -2077,6 +2077,7 @@ sync_components(gen->m68k, 0); m68k_execute(gen->m68k, gen->m68k->target_cycle); } + gen->m68k->should_return = 0; #endif handle_reset_requests(gen); return; @@ -2099,7 +2100,15 @@ } render_resume_source(gen->psg->audio); } +#ifdef NEW_CORE + while (!gen->m68k->should_return) { + sync_components(gen->m68k, 0); + m68k_execute(gen->m68k, gen->m68k->target_cycle); + } + gen->m68k->should_return = 0; +#else resume_68k(gen->m68k); +#endif handle_reset_requests(gen); }
--- a/nuklear_ui/blastem_nuklear.c Sun Feb 09 22:46:07 2025 -0800 +++ b/nuklear_ui/blastem_nuklear.c Thu Feb 13 02:18:30 2025 -0800 @@ -2453,7 +2453,9 @@ {"Save State", view_save_state}, {"Load State", view_load_state}, {"Settings", view_settings}, +#ifndef __EMSCRIPTEN__ {"Exit", NULL} +#endif }; static menu_item sc3k_items[] = { {"Resume", view_play}, @@ -2462,7 +2464,9 @@ {"Save State", view_save_state}, {"Load State", view_load_state}, {"Settings", view_settings}, +#ifndef __EMSCRIPTEN__ {"Exit", NULL} +#endif }; if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) { @@ -2511,9 +2515,25 @@ } } +void ui_enter(void) +{ + render_enable_gamepad_events(1); +} + +void ui_exit(void) +{ + if (config_dirty) { + apply_updated_config(); + persist_config(config); + config_dirty = 0; + } + render_enable_gamepad_events(0); +} + void ui_idle_loop(void) { - render_enable_gamepad_events(1); +#ifndef __EMSCRIPTEN__ + ui_enter(); const uint32_t MIN_UI_DELAY = 15; static uint32_t last; while (current_view != view_play) @@ -2525,12 +2545,8 @@ last = current; render_update_display(); } - if (config_dirty) { - apply_updated_config(); - persist_config(config); - config_dirty = 0; - } - render_enable_gamepad_events(0); + ui_exit(); +#endif } static void handle_event(SDL_Event *event) {
--- a/nuklear_ui/blastem_nuklear.h Sun Feb 09 22:46:07 2025 -0800 +++ b/nuklear_ui/blastem_nuklear.h Thu Feb 13 02:18:30 2025 -0800 @@ -15,6 +15,9 @@ void show_play_view(void); uint8_t is_nuklear_active(void); uint8_t is_nuklear_available(void); +//ui_idle_loop calls these automatically, needed externally for browser target +void ui_enter(void); +void ui_exit(void); void ui_idle_loop(void); #endif //BLASTEM_NUKLEAR_H_
--- a/render.h Sun Feb 09 22:46:07 2025 -0800 +++ b/render.h Thu Feb 13 02:18:30 2025 -0800 @@ -142,6 +142,7 @@ void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create); void render_set_ui_render_fun(ui_render_fun); void render_set_ui_fb_resize_handler(ui_render_fun resize); +void render_set_frame_presented_fun(ui_render_fun); void render_video_loop(void); uint8_t render_should_release_on_exit(void); void render_set_external_sync(uint8_t ext_sync_on);
--- a/render_sdl.c Sun Feb 09 22:46:07 2025 -0800 +++ b/render_sdl.c Thu Feb 13 02:18:30 2025 -0800 @@ -107,7 +107,11 @@ uint8_t render_should_release_on_exit(void) { +#ifdef __EMSCRIPTEN__ + return 0; +#else return sync_src != SYNC_AUDIO_THREAD; +#endif } void render_buffer_consumed(audio_source *src) @@ -1271,7 +1275,11 @@ SDL_DisplayMode mode; //TODO: Explicit multiple monitor support SDL_GetCurrentDisplayMode(0, &mode); +#ifdef __EMSCRIPTEN__ + display_hz = 60; //TODO: FIXME +#else display_hz = mode.refresh_rate; +#endif if (fullscreen) { //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP @@ -1509,10 +1517,12 @@ free(path); } +#ifndef __EMSCRIPTEN__ void GLAPIENTRY gl_message_callback(GLenum source, GLenum type, GLenum id, GLenum severity, GLsizei length, const GLchar *message, const void *user) { fprintf(stderr, "GL Message: %d, %d, %d - %s\n", source, type, severity, message); } +#endif uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler) { @@ -1553,10 +1563,12 @@ if (render_gl) { extras[win_idx].gl_context = SDL_GL_CreateContext(extras[win_idx].win); SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context); +#ifndef __EMSCRIPTEN__ glEnable(GL_DEBUG_OUTPUT); if (glDebugMessageCallback) { glDebugMessageCallback(gl_message_callback, NULL); } +#endif glGenTextures(2, extras[win_idx].gl_texture); for (int i = 0; i < 2; i++) { @@ -2256,6 +2268,12 @@ render_ui = fun; } +static ui_render_fun frame_presented; +void render_set_frame_presented_fun(ui_render_fun fun) +{ + frame_presented = fun; +} + void render_update_display() { #ifndef DISABLE_OPENGL @@ -2328,6 +2346,9 @@ process_events(); } events_processed = 0; + if (frame_presented) { + frame_presented(); + } } uint32_t render_emulated_width()
--- a/sms.c Sun Feb 09 22:46:07 2025 -0800 +++ b/sms.c Thu Feb 13 02:18:30 2025 -0800 @@ -1099,12 +1099,6 @@ { sms_context *sms = (sms_context *)system; uint32_t target_cycle = sms->z80->Z80_CYCLE + 3420*16; - //TODO: PAL support - if (sms->vdp->type == VDP_GAMEGEAR) { - render_set_video_standard(VID_GAMEGEAR); - } else { - render_set_video_standard(VID_NTSC); - } while (!sms->should_return) { if (system->delayed_load_slot) { @@ -1214,6 +1208,12 @@ sms_context *sms = (sms_context *)system; if (sms->header.force_release || render_should_release_on_exit()) { sms->header.force_release = 0; + //TODO: PAL support + if (sms->vdp->type == VDP_GAMEGEAR) { + render_set_video_standard(VID_GAMEGEAR); + } else { + render_set_video_standard(VID_NTSC); + } bindings_reacquire_capture(); vdp_reacquire_framebuffer(sms->vdp); render_resume_source(sms->psg->audio); @@ -1239,6 +1239,12 @@ #endif } + //TODO: PAL support + if (sms->vdp->type == VDP_GAMEGEAR) { + render_set_video_standard(VID_GAMEGEAR); + } else { + render_set_video_standard(VID_NTSC); + } run_sms(system); }
--- a/z80.cpu Sun Feb 09 22:46:07 2025 -0800 +++ b/z80.cpu Thu Feb 13 02:18:30 2025 -0800 @@ -90,40 +90,42 @@ dispatch scratch1 z80_interrupt - if cycles >=U int_cycle - - iff1 = 0 - iff2 = 0 - cycles 6 - update_sync - - switch imode - case 0 - dispatch int_value - - case 1 - dispatch 0xFF + if cycles >=U nmi_cycle - case 2 - pc = i << 8 - pc |= int_value - #CD is call - dispatch 0xCD - end - + nmi_cycle = 0xFFFFFFFF + iff1 = 0 + local pch 8 + pch = pc >> 8 + meta high pch + meta low pc + z80_push + pc = 0x66 + update_sync + else - if cycles >=U nmi_cycle - - nmi_cycle = 0xFFFFFFFF - iff1 = 0 - local pch 8 - pch = pc >> 8 - meta high pch - meta low pc - z80_push - pc = 0x66 - update_sync - + if iff1 + if cycles >=U int_cycle + + iff1 = 0 + iff2 = 0 + cycles 6 + update_sync + + switch imode + case 0 + dispatch int_value + + case 1 + dispatch 0xFF + + case 2 + pc = i << 8 + pc |= int_value + #CD is call + dispatch 0xCD + end + + end end end