On my infinitely long list of things I wanted to implement one of them was taking a binary and stuffing it into a webpage to run entirely client side. Today I have accomplished just that with my new Atari 2600 IDE. This allows you to write an assembly program entirely in browser and run it without setting up any kind of toolchain on your own machine. The following are my notes on getting emscripten, a way to compile c/c++ to run in browser using llvm and wasm, to generate a binary and interfacing that binary with some vanilla Javascript.
What I Built
In order to write programs for the Atari 2600, you must first set up an assembler and text editor. The recommended tool seems to be dasm, a command line assembler for 6502 assembly. This is what we’re going to compile to run entirely in browser. However, this program has some issues that interfere with emscripten. It uses a lot of global variables, and was never really meant to run without being reset.
I was inspired by Retro Game Mechanics Explained to make an easier way to write code to run on these vintage systems. I always wanted to write code for these vintage systems, and now I can with my little IDE I built using emscripten. The image is clickable, and this program will only run on Chrome on desktop. I haven’t tested it any where else.
Special thanks to the Javatari project for making a fantastic easy to use and embed emulator.
Emscripten Setup
The first step is grab the emsdk tool and set it up. We are not doing advanced setup so just follow the defaults. The commands I used are listed below:
This gives us a new C compiler and a couple other tools to use. We’re only
interested in compiling a C program, so just export CC=emcc
to fix it for
most makefiles you’ll encounter in the wild. There’s other things to help with
complex programs that use automake and etc.
Compiling Your Program
Next you’ll have to probably modify the makefile at the linker stage. This is
after all the object files have been built, and now they’re being strung
together. Various flags can be passed to emcc
, to make the runtime do
what you want. You might want to bundle some local files with the binary or run
the binary multiple times inside of your program. You’ll also probably have to
export some functions for your use. I’ve listed the flags I set below, and why
as a definition list. The following all use the -s
flag and argument like such.
EXPORTED_FUNCTIONS=_main
- This allows the main function from our program to be exported and callable.
EXPORTED_RUNTIME_METHODS=FS,callMain
- We need to use the FS and callMain functions on the returned object. FS is
the file system use by emscripten. It’s a fake virtual thing that is not
persisted. We can add things to it using the
-embed-file path
flag during linking.callMain
allows actually executing the thing. EXIT_RUNTIME=1
- This causes the runtime to properly terminate instead of persisting. DASM uses a lot of global state, and this isn’t cleaned up fully at the end of execution. Which is the right way to write command line programs. The operating system will take care of it for you.
MODULARIZE
- This allows us to get a factory function to handle creating our runtime repeatably.
EXPORT_NAME="createDasmUtil"
- What the factory function should be called.
INVOKE_RUN=0
- By default when you create a runtime it will be executed automatically as soon as it’s created with no arguments to main. We don’t want that to happen since we need to callMain with real arguments.
Once we have configured our flags in the linker line, we are ready to go. We’ll build the
program with make
and then copy over the wasm and associated javascript file to our
website. Now comes the fun part with integrating into the html.
Integrating into the Web Page
Now it’s time to write some JavaScript and explain how everything works. You could probably use a bundler here, but I did everything with vanilla JS to keep it as simple as humanly possible. I spent a lot of time using the console interactively to figure everything out.
First load your friendly emscripten wrapper script..
Next, remember what you used as your EXPORT_NAME
, we’ll use what I defined
above as an example. We’ll also make the function be an event handler for
invoking our program. You can attach it however you would like.
As we can see it’s not that much boilerplate to actually invoke our C code. We just need to be careful with what we pass around, and how we operate on these byte buffers.
The filesystem is strictly in memory, but there are ways to override and
persist it that I’m not going to cover since it wasn’t needed for my project.
The file system can easily be viewed with instance.FS.readdir(path)
, this is
your best ls
alternative here. I highly recommend understanding where your
program is running from and what are the relevant paths. At the end of the day,
its the same troubles you might have with docker. What is my current working
directory, and did I copy everything in place where it needs to go?
Wrapping Up
Hopefully you found this to be an enlightening introduction to emscripten, and hopefully it helps you setup and compile your own programs to run on the web with it. It’s quite neat that we can take some fairly sophisticated programs and open them up to the general internet without any server interaction. I have other projects I want to work on with this stuff including getting other old school programs to run in browser and be interactive. I also want to make some physics simulations available here. Especially the stuff I worked on at NIST, as it would be really cool to open that up to the general public.