metasyn-icon
sun
pastel rainbow stripes

learn-uxn

the learn-uxn project is a project for taking the uxn emulator and assembler and making them work in the browser. specifically, the project involves using emscripten to translate the C-based uxntal assembler and uxn rom emulator from the uxn repo into javascript and web assembly code that can run easily in the browser.

additionally, the site uses codemirror 6 and the lezer parser to add an editor complete with a parser and syntax highlighter for the uxntal language. from the main page, you can load uxntal source code (left side), and assemble it into uxn roms, and run them in the emulator in the top right. logs and debugging information are printed in the bottom right.

permacomputing

ironically, the point of the uxn emulator and the varvara computing system is to encourage and explore ideas around permacomputing, which is about as far as you can get from trying to port things to the web browser. however, in an effort to both understand uxn and uxntal more generally, and have an environment where you can test out some changes, it might be a useful tool to those learning about the system. my hope is that this tool, while not permacomputing in itself, will be useful to others who are interested in the project, and hopefully encourage more people to consider why permacomputing is interesting and important.

implementation

the emscripten based port of the C-based tools works by taking the C source code and generating the javascript and web assembly modules that can run in the browser. the C-based tools rely primarily on the Simple DirectMedia Layer (SDL2) package for their operation, which covers things like the ability to create a mouse device/cursor, graphical rendering, as well as audio. thankfully, the emscripten community has already created an SDL2 port that can be easily used.

there are two separate programs tha are compiled via a call to the emscripten compiler with a number of options that make the code amenable to being used inthe browser. various flags need to be added to the emcc call in order for it to work:

EMCC_DEBUG=1 emcc \
    -s WASM=1 \
    -s ASSERTIONS=1 \
    -s ENVIRONMENT=web \
    -s ASYNCIFY \
    -s USE_SDL=2 \
    -s USE_SDL_MIXER=2 \
    -s FORCE_FILESYSTEM=1 \
    -s EXPORTED_FUNCTIONS='["_main"]' \
    -s EXPORTED_RUNTIME_METHODS='["callMain", "FS"]' \
    -s NO_EXIT_RUNTIME=1 \
    --shell-file=shell-uxnemu.html \
    --extern-pre-js=pre-uxnemu.js \
    -O3 \
    -o site/uxnemu.html \
        uxn/src/uxn-fast.c \
        uxn/src/devices/ppu.c \
        uxn/src/devices/apu.c \
        uxn/src/uxnemu.c

the shell file and extern-pre-js are basically just the template file that will be created and the javascript that will be injected to the final output (the html file). In order to properly shutdown and restart the whole process, the html file that is created in the very end is wrapped in an iframe so that we can trigger a reload of the whole iframe, by default, if you you don’t add the MODULARIZE option, all the javascript stuff will be more or less in the global (window) scope in a variable called Module. this can make it hard to have multiple emscripten projects running simultaneously. the modularize option looked promising but ended up causing some issues for the workflow, and the iframe approach seemed simpler, so i just went with that.

for each iframe, there is a content window in the object that has access to that tool’s Module. with that reference, we can operate on the virtual file system provided by emscripten. so in the case of our application, we write uxntal source code to a file on the uxn assembler’s virtual file system, then read the rom back from that, and write the rom to the uxn emulator’s virtual file system. there is a little trickery to make sure that the file exists after reloading the iframe but before the main function is called. other than that though, the majority of the operation is pretty straight forward and linear.

in order to pass data from the assembler to the emualtor, we simply stash the b64 encoded contents of the rom on the global window object of that iframe, so that the start up function can check if it’s present and create a virtual file before calling the main function: this operation can be seen here.

synchronicity

for the SDL based implementation, there is a call to SDL_Delay that keeps the framerate correct. however, in the javascript/webassembly version, we don’t need this, but instead, need the emscripten equivalent. currently this is handled by some hacky code due to the way that the source programs are structured:

if ! grep -q 'emscripten_sleep' uxn/src/uxnemu.c; then
    sed -i -e '1s/^/#include <emscripten.h>\n/;/SDL_Delay/s/^/emscripten_sleep(10);\n/' uxn/src/uxnemu.c
fi

shout out to @alderwick@merveilles.town for helping me come up with this hack/solution to modifying the uxn source without having to do any refactoring! this is the only change overall to the uxn source code that is needed to make this whole project work.


incoming links: now

2021.09.17