Menu

#165 Emscripten support

future
open
nobody
None
5
2 days ago
2026-08-22
No

fuse supports SDL2, so with some changes can be built WASM version.
Could you make emscripten support available out of the box?

Discussion

  • Witold Filipczyk

    Hello,
    Here is a try to build emscripten version of fuse.

    First install emscripten and other fuse dependencies.

    Build libxml2:

    #!/bin/bash
    
    export EM_PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
    export LDFLAGS="-sUSE_ZLIB"
    export CFLAGS="-O3"
    ./autogen.sh
    emconfigure ./configure --enable-shared=no --enable-static=yes
    make -j$(nproc) install
    

    Build libspectrum:
    Apply this libspectrum.patch:

    diff --git a/myglib/ghash.c b/myglib/ghash.c
    index 2dde616..be4b6c6 100644
    --- a/myglib/ghash.c
    +++ b/myglib/ghash.c
    @@ -62,7 +62,7 @@ struct _GHashTable
     static GHashNode *node_free_list = NULL;
     static GHashNode *node_allocated_list = NULL;
    
    -#ifdef HAVE_STDATOMIC_H
    +#if defined(HAVE_STDATOMIC_H) && !defined(__EMSCRIPTEN__)
    
     static atomic_char atomic_locker = 0;
    
    diff --git a/myglib/gslist.c b/myglib/gslist.c
    index d8b3d46..070a75a 100644
    --- a/myglib/gslist.c
    +++ b/myglib/gslist.c
    @@ -43,7 +43,7 @@ static int FREE_LIST_ALLOCATE_CHUNK = 1024;
     GSList * free_list = NULL;
     GSList * allocated_list = NULL;
    
    -#ifdef HAVE_STDATOMIC_H
    +#if defined(HAVE_STDATOMIC_H) && !defined(__EMSCRIPTEN__)
    
     static atomic_char atomic_locker = 0;
    

    compile and install:

    #!/bin/bash
    
    export EM_PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
    export LDFLAGS="-sUSE_ZLIB"
    export CFLAGS="-O3"
    emconfigure ./configure --without-audiofile --without-bzip2 --enable-shared=no --enable-static=yes --with-fake-glib
    make -j$(nproc) install
    

    Buils fuse:
    fuse-ems.patch:

    diff --git a/Makefile.am b/Makefile.am
    index d95be661..76eeb23a 100644
    --- a/Makefile.am
    +++ b/Makefile.am
    @@ -64,7 +64,8 @@ fuse_LDADD = \
                  $(GLIB_LIBS) \
                  $(PNG_LIBS) \
                  $(X_LIBS) \
    
    -             $(XML_LIBS)
    +             $(XML_LIBS) \
    +             $(EXTRA_LIBS)
    
     if USE_SDL1
     fuse_LDADD += $(SDL1_LIBS)
    @@ -212,7 +213,10 @@ include ui/widget/Makefile.am
     include ui/wii/Makefile.am
     include ui/win32/Makefile.am
     include ui/xlib/Makefile.am
    +if EMSCRIPTEN
    +else
     include unittests/Makefile.am
    +endif
     include z80/Makefile.am
    
     check-local: fuse unittests/displaytest unittests/sdl2displaytest unittests/sdl2scalerstatetest unittests/sdl2joysticktest unittests/sdl2mousetest unittests/palcompositetest unittests/scalerexpandtest z80/coretest
    diff --git a/compat/unix/paths.c b/compat/unix/paths.c
    index 8fd203a0..7ff3539e 100644
    --- a/compat/unix/paths.c
    +++ b/compat/unix/paths.c
    @@ -59,7 +59,11 @@ compat_get_fallback_config_path( void )
     const char*
     compat_get_config_path( void )
     {
    +#if defined(__EMSCRIPTEN__)
    
    +  static char *config_path = ".";
    +#else
       static char *config_path = NULL;
    +#endif
       char path[ PATH_MAX ];
       const char *xdg_dir;
    
    diff --git a/configure.ac b/configure.ac
    index 05f9f5aa..b24c1b39 100644
    --- a/configure.ac
    +++ b/configure.ac
    @@ -889,6 +889,9 @@ case "$host_os" in
         COMPAT_OSNAME='unix'
         ;;
     esac
    +
    +AC_CHECK_PROG([EMCC], [emcc], [yes], [no])
    +
     AC_MSG_RESULT($COMPAT_OSNAME)
     AM_CONDITIONAL(COMPAT_AMIGA, test "$COMPAT_OSNAME" = 'amiga')
     AM_CONDITIONAL(COMPAT_LINUX, test "$COMPAT_OSNAME" = 'linux')
    @@ -896,6 +899,7 @@ AM_CONDITIONAL(COMPAT_MORPHOS, test "$COMPAT_OSNAME" = 'morphos')
     AM_CONDITIONAL(COMPAT_UNIX, test "$COMPAT_OSNAME" = 'unix')
     AM_CONDITIONAL(COMPAT_WII, test "$COMPAT_OSNAME" = 'wii')
     AM_CONDITIONAL(COMPAT_WIN32, test "$COMPAT_OSNAME" = 'win32')
    +AM_CONDITIONAL(EMSCRIPTEN, test "$EMCC" = 'yes')
    
     dnl These tell the Makefile to use SDL 1 and SDL 2 flags during the build
     AM_CONDITIONAL(USE_SDL1, test "$use_sdl" = "1")
    diff --git a/fuse.c b/fuse.c
    index 95217862..9b681835 100644
    --- a/fuse.c
    +++ b/fuse.c
    @@ -31,6 +31,10 @@
     #include <sys/types.h>
     #include <time.h>
    
    +#if defined(__EMSCRIPTEN__)
    +#include <emscripten.h>
    +#endif
    +
     #ifdef WIN32
     #include <windows.h>
     #endif             /* #ifdef WIN32 */
    @@ -108,7 +112,9 @@
     #include "ui/scaler/scaler.h"
     #include "ui/ui.h"
     #include "ui/uimedia.h"
    +#if !defined(__EMSCRIPTEN__)
     #include "unittests/unittests.h"
    +#endif
     #include "utils.h"
    
     #include "z80/z80.h"
    @@ -199,7 +205,11 @@ int main(int argc, char **argv)
           settings_current.show_version ) return 0;
    
       if( settings_current.unittests ) {
    +#if defined(__EMSCRIPTEN__)
    
    +    r = 0;
    +#else
         r = unittests_run();
    +#endif
       } else {
         while( !fuse_exiting ) {
           z80_do_opcodes();
    @@ -261,7 +271,7 @@ libxml2_register_startup( void )
     static int
     setuid_init( void *context )
     {
    -#ifdef HAVE_GETEUID
    +#if defined(HAVE_GETEUID) && !defined(__EMSCRIPTEN__)
       int error;
    
       /* Drop root privs if we have them */
    @@ -1029,6 +1039,8 @@ static int fuse_end(void)
     void
     fuse_abort( void )
     {
    +#if !defined(__EMSCRIPTEN__)
       fuse_end();
       abort();
    +#endif
     }
    diff --git a/timer/sdl.c b/timer/sdl.c
    index 91c740e3..0b4322a8 100644
    --- a/timer/sdl.c
    +++ b/timer/sdl.c
    @@ -24,6 +24,9 @@
     #include "config.h"
    
     #include <SDL.h>
    +#if defined(__EMSCRIPTEN__)
    +#include <emscripten.h>
    +#endif
    
     #include "timer.h"
    
    @@ -36,5 +39,9 @@ timer_get_time( void )
     void
     timer_sleep( int ms )
     {
    +#if defined(__EMSCRIPTEN__)
    
    +  emscripten_sleep( ms );
    +#else
       SDL_Delay( ms );
    +#endif
     }
    diff --git a/ui/display_timing.c b/ui/display_timing.c
    index f249e2fc..24c6e5b4 100644
    --- a/ui/display_timing.c
    +++ b/ui/display_timing.c
    @@ -25,6 +25,8 @@
    
     #ifdef _WIN32
     #include <windows.h>
    +#elif defined(__EMSCRIPTEN__)
    +#include <emscripten.h>
     #else
     #include <glib.h>
     #endif
    @@ -63,6 +65,8 @@ now_us( void )
       QueryPerformanceCounter( &now );
    
       return (long long)( (double)now.QuadPart * 1000000 / frequency.QuadPart );
    +#elif defined(__EMSCRIPTEN__)
    
    +  return emscripten_get_now() * 1000L;
     #else
       return g_get_monotonic_time();
     #endif
    

    compile:

    #!/bin/bash
    
    export LIBSPECTRUM_LIBS=/usr/local/lib/libspectrum.a
    export LIBSPECTRUM_CFLAGS=-I/usr/local/include
    
    export CFLAGS="-O3"
    export LDFLAGS="-sUSE_ZLIB -sUSE_SDL=2 -sINITIAL_MEMORY=128MB -sFORCE_FILESYSTEM -sASYNCIFY"
    
    export EXTRA_LIBS="--preload-file roms --preload-file lib --preload-file ui/widget --preload-file fuserc"
    export EM_PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"
    export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"
    
    emmake make clean
    emconfigure ./configure --host none --with-sdl --disable-sockets --without-joystick
    # --with-audio-driver=sdl
    
    ##make -j$(nproc) V=1 EXEEXT=.html fuse.html
    make -j$(nproc) V=1 EXEEXT=.html all
    

    fuserc:

    <?xml version="1.0"?>
    <settings>
      <accelerateloader>0</accelerateloader>
      <aspecthint>1</aspecthint>
      <autoload>1</autoload>
      <autosavesettings>0</autosavesettings>
      <beta128>0</beta128>
      <beta12848boot>1</beta12848boot>
      <bwtv>0</bwtv>
      <competitioncode>0</competitioncode>
      <competitionmode>0</competitionmode>
      <confirmactions>1</confirmactions>
      <covox>0</covox>
      <detectloader>0</detectloader>
      <didaktik80>0</didaktik80>
      <disciple>0</disciple>
      <diskaskmerge>1</diskaskmerge>
      <divide>0</divide>
      <dividewriteprotect>0</dividewriteprotect>
      <divmmc>0</divmmc>
      <divmmcwriteprotect>0</divmmcwriteprotect>
      <doublescanmode>1</doublescanmode>
      <drive40maxtrack>42</drive40maxtrack>
      <drive80maxtrack>84</drive80maxtrack>
      <embedsnapshot>1</embedsnapshot>
      <speed>100</speed>
      <fastload>0</fastload>
      <fbmode>320</fbmode>
      <rate>1</rate>
      <fullscreen>0</fullscreen>
      <fuller>0</fuller>
      <interface1>0</interface1>
      <interface2>0</interface2>
      <issue2>0</issue2>
      <kempston>0</kempston>
      <joystickprompt>0</joystickprompt>
      <joystick1fire1>4096</joystick1fire1>
      <joystick1fire10>4096</joystick1fire10>
      <joystick1fire11>4096</joystick1fire11>
      <joystick1fire12>4096</joystick1fire12>
      <joystick1fire13>4096</joystick1fire13>
      <joystick1fire14>4096</joystick1fire14>
      <joystick1fire15>4096</joystick1fire15>
      <joystick1fire2>4096</joystick1fire2>
      <joystick1fire3>4096</joystick1fire3>
      <joystick1fire4>4096</joystick1fire4>
      <joystick1fire5>4096</joystick1fire5>
      <joystick1fire6>4096</joystick1fire6>
      <joystick1fire7>4096</joystick1fire7>
      <joystick1fire8>4096</joystick1fire8>
      <joystick1fire9>4096</joystick1fire9>
      <joystick1output>0</joystick1output>
      <joystick2fire1>4096</joystick2fire1>
      <joystick2fire10>4096</joystick2fire10>
      <joystick2fire11>4096</joystick2fire11>
      <joystick2fire12>4096</joystick2fire12>
      <joystick2fire13>4096</joystick2fire13>
      <joystick2fire14>4096</joystick2fire14>
      <joystick2fire15>4096</joystick2fire15>
      <joystick2fire2>4096</joystick2fire2>
      <joystick2fire3>4096</joystick2fire3>
      <joystick2fire4>4096</joystick2fire4>
      <joystick2fire5>4096</joystick2fire5>
      <joystick2fire6>4096</joystick2fire6>
      <joystick2fire7>4096</joystick2fire7>
      <joystick2fire8>4096</joystick2fire8>
      <joystick2fire9>4096</joystick2fire9>
      <joystick2output>0</joystick2output>
      <joystickkeyboarddown>97</joystickkeyboarddown>
      <joystickkeyboardfire>32</joystickkeyboardfire>
      <joystickkeyboardleft>111</joystickkeyboardleft>
      <joystickkeyboardoutput>0</joystickkeyboardoutput>
      <joystickkeyboardright>112</joystickkeyboardright>
      <joystickkeyboardup>113</joystickkeyboardup>
      <kempstonmouse>0</kempstonmouse>
      <keyboardarrowsshifted>1</keyboardarrowsshifted>
      <latetimings>0</latetimings>
      <mdrlen>180</mdrlen>
      <mdrrandomlen>1</mdrrandomlen>
      <melodik>1</melodik>
      <mouseswapbuttons>0</mouseswapbuttons>
      <moviestopafterrzx>1</moviestopafterrzx>
      <multiface1>0</multiface1>
      <multiface128>0</multiface128>
      <multiface1stealth>0</multiface1stealth>
      <multiface3>0</multiface3>
      <opus>0</opus>
      <paltv2x>0</paltv2x>
      <phantomtypistmode>Auto</phantomtypistmode>
      <plus3detectspeedlock>1</plus3detectspeedlock>
      <plusd>0</plusd>
      <printer>0</printer>
      <graphicsfile>printout.pbm</graphicsfile>
      <textfile>printout.txt</textfile>
      <rawsnet>0</rawsnet>
      <recreatedspectrum>0</recreatedspectrum>
      <rom1280>128-0.rom</rom1280>
      <rom1281>128-1.rom</rom1281>
      <rom16>48.rom</rom16>
      <rom48>48.rom</rom48>
      <rombeta128>trdos.rom</rombeta128>
      <romdidaktik80>didaktik80.rom</romdidaktik80>
      <romdisciple>disciple.rom</romdisciple>
      <rominterfacei>if1-2.rom</rominterfacei>
      <rommultiface1>mf1.rom</rommultiface1>
      <rommultiface128>mf128.rom</rommultiface128>
      <rommultiface3>mf3.rom</rommultiface3>
      <romopus>opus.rom</romopus>
      <rompentagon10240>128p-0.rom</rompentagon10240>
      <rompentagon10241>128p-1.rom</rompentagon10241>
      <rompentagon10242>trdos.rom</rompentagon10242>
      <rompentagon10243>gluck.rom</rompentagon10243>
      <rompentagon5120>128p-0.rom</rompentagon5120>
      <rompentagon5121>128p-1.rom</rompentagon5121>
      <rompentagon5122>trdos.rom</rompentagon5122>
      <rompentagon5123>gluck.rom</rompentagon5123>
      <rompentagon0>128p-0.rom</rompentagon0>
      <rompentagon1>128p-1.rom</rompentagon1>
      <rompentagon2>trdos.rom</rompentagon2>
      <romplus20>plus2-0.rom</romplus20>
      <romplus21>plus2-1.rom</romplus21>
      <romplus2a0>plus3-0.rom</romplus2a0>
      <romplus2a1>plus3-1.rom</romplus2a1>
      <romplus2a2>plus3-2.rom</romplus2a2>
      <romplus2a3>plus3-3.rom</romplus2a3>
      <romplus30>plus3-0.rom</romplus30>
      <romplus31>plus3-1.rom</romplus31>
      <romplus32>plus3-2.rom</romplus32>
      <romplus33>plus3-3.rom</romplus33>
      <romplus3e0>plus3e-0.rom</romplus3e0>
      <romplus3e1>plus3e-1.rom</romplus3e1>
      <romplus3e2>plus3e-2.rom</romplus3e2>
      <romplus3e3>plus3e-3.rom</romplus3e3>
      <romplusd>plusd.rom</romplusd>
      <romscorpion0>256s-0.rom</romscorpion0>
      <romscorpion1>256s-1.rom</romscorpion1>
      <romscorpion2>256s-2.rom</romscorpion2>
      <romscorpion3>256s-3.rom</romscorpion3>
      <romspecse0>se-0.rom</romspecse0>
      <romspecse1>se-1.rom</romspecse1>
      <romspeccyboot>speccyboot-1.4.rom</romspeccyboot>
      <romtc2048>tc2048.rom</romtc2048>
      <romtc20680>tc2068-0.rom</romtc20680>
      <romtc20681>tc2068-1.rom</romtc20681>
      <romts20680>tc2068-0.rom</romts20680>
      <romts20681>tc2068-1.rom</romts20681>
      <romusource>usource.rom</romusource>
      <rs232handshake>0</rs232handshake>
      <rzxautosaves>1</rzxautosaves>
      <compressrzx>1</compressrzx>
      <simpleide>0</simpleide>
      <slttraps>1</slttraps>
      <sound>1</sound>
      <!--<sounddevice>sdl</sounddevice>-->
      <soundforce8bit>0</soundforce8bit>
      <soundfreq>32000</soundfreq>
      <loadingsound>1</loadingsound>
      <speakertype>TV speaker</speakertype>
      <speccyboot>0</speccyboot>
      <speccyboottap>tap0</speccyboottap>
      <specdrum>0</specdrum>
      <spectranet>0</spectranet>
      <spectranetdisable>0</spectranetdisable>
      <machine>2048</machine>
      <graphicsfilter>2x</graphicsfilter>
      <statusbar>1</statusbar>
      <separation>None</separation>
      <strictaspecthint>0</strictaspecthint>
      <!--<tapefile>roms/SIRWOOD.tap</tapefile>-->
      <tapefile>roms/timmy.tap</tapefile>
      <tapetraps>1</tapetraps>
      <unittests>0</unittests>
      <usource>0</usource>
      <volumeay>100</volumeay>
      <volumebeeper>100</volumebeeper>
      <volumecovox>100</volumecovox>
      <volumespecdrum>100</volumespecdrum>
      <writableroms>0</writableroms>
      <cmosz80>0</cmosz80>
      <zxatasp>0</zxatasp>
      <zxataspupload>0</zxataspupload>
      <zxataspwriteprotect>0</zxataspwriteprotect>
      <zxcf>0</zxcf>
      <zxcfupload>0</zxcfupload>
      <zxmmc>0</zxmmc>
      <zxprinter>0</zxprinter>
    </settings>
    

    Demo is visible at:
    http://tc2048.y0.pl/WASM/fuse.html

    On my machine is too slow.

    Are you interested in WASM?
    Any comments on patches?

     
  • Fredrick Meunier

    I've got to say it is just fun to see Fuse running in a web browser! It runs at full speed on my machine at least and doesn't seem to be too heavy in Safari for macOS.

    I'd want to try and tidy up the patches a bit to integrate it. I'd take libxml out to trim it down a bit.

     
  • Fredrick Meunier

    I might share the link on Spectrum Computing forums to share the fun!

     

Log in to post a comment.