Building up MAME JS (Was – Deploy ?Something?)

You have a k8s cluster setup – now what?

I’ve had a lot of fun retro gaming with a retropie system I setup last year. Maybe I could setup a virtual arcade in my little cluster…

My RetroPie setup in the garage.

I’m thinking I’ll try to get a build of the Multi Arcade Machine Emulator (MAME) working. I went and got the code for MAME and built up galaxian and pacman emulators and bippidy, boppady, blah, blah, blah!

Not!

Building a copy of MAME to run pacman went fine, but I wanted the javascript build and that was much harder to get going – which was frustrating because it’s already out there on the Internet Archive working! I guess I could just go grab a copy of their javascripts, but I want some sort of repo to build off of so that I’ll be able to demo some gitops – like maybe ArgoCD.

Not sure, the javascript build seems like the ugly step child of MAME but the instructions didn’t work for me. Anyway, – not bitter – here’s what I did to get it working. It’s a pretty power hungry build, so I do it on my main server.

Start out by following the Compiling MAME instructions.

sudo apt-get install git build-essential python3 libsdl2-dev libsdl2-ttf-dev libfontconfig-dev libpulse-dev qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake
git clone https://github.com/mamedev/mame#
cd mame
make SUBTARGET=pacman SOURCES=src/mame/pacman/pacman.cpp TOOLS=0 REGENIE=1 -j16

That built up the emulator off the master branch – and it worked.

To get the js/wasm build going you are supposed to do something like:

It’s been a couple days since I started working on this…I’m pretty sure that initial build finished fine. But there’s a big difference between a finished build and a working build.

You then have to setup a webserver and copy in most of the files from Emularity and create a html page to point to it all. Emularity is some javascript that makes it easier to launch the MAME emulator.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>example arcade game</title>
</head>
<body>
<canvas id="canvas" style="width: 50%; height: 50%; display: block; margin: 0 auto;"></canvas>
<script type="text/javascript" src="es6-promise.js"></script>
<script type="text/javascript" src="browserfs.min.js"></script>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript">
var emulator =
new Emulator(document.querySelector("#canvas"),
null,
new MAMELoader(MAMELoader.driver("pacman"),
MAMELoader.nativeResolution(224, 256),
MAMELoader.scale(3),
MAMELoader.emulatorWASM("emulator/pacman.wasm"),
MAMELoader.emulatorJS("emulator/pacman.js"),
MAMELoader.mountFile("emulator/pacman.zip",
MAMELoader.fetchFile("Game File",
"/roms/pacman.zip"))))
emulator.start({ waitAfterDownloading: true });
</script>
</body>
</html>

Great but it didn’t load… It just crashed out with an Uncaught (in promise) error (with some random addr).


There isn’t a ton of info on this build system. I did find this post that implied that there’s some version matching that has to go on. The javascript version is basically the binary build of a C++ application massaged into web assembly by emscripten.

I hacked around for a couple days trying to add symbols and debug in the code and trying to get a break point in the javascript console. Ultimately, I kinda cheated and just tried to have a look at what the Internet Archive had done.

If you look in the console on a successful run you can see that the MAME emulator is run with -verbose on so you get a dump of a couple things:

Critically, they’re running the build on version 0.239 of MAME. Figuring out the emsdk version was a little harder – but I could see they are running Clang 14.0.0 from llvm tools. You can run ./emsdk list to list the emscripten versions. Ultimately by playing with it a bit (sort of loop over testing different versions of emcc – ./emsdk install 2.0.0; ./emsdk activate 2.0.0; source ./emsdk_env.sh; emcc -v) I settled on version 3.0.0 which had Clang 14.0.0. There’s tags in the MAME repo for each version so to get my build working I did this:

cd ~/emsdk
./emsdk install 3.0.0
./emsdk activate 3.0.0
source ~/emsdk_env.sh
emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.0.0 (3fd52e107187b8a169bb04a02b9f982c8a075205)
clang version 14.0.0 (https://github.com/llvm/llvm-project 4348cd42c385e71b63e5da7e492172cff6a79d7b)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/sandy/emsdk/upstream/bin

cd ~/mame
git checkout mame0239
./buildpacman.sh REGENIE=1

Where my buildpacman.sh script looked like this:

#!/bin/bash 

subtarget=pacman

emmake make -j 16 SUBTARGET=$subtarget SOURCES=src/mame/drivers/pacman.cpp ARCHOPTS=" -s EXCEPTION_CATCHING_ALLOWED=[instantiateArrayBuffer,instantiateAsync,createWasm] " WEBASSEMBLY=1 OPTIMIZE=s $@

There’s a couple things to mention here:

  • First is that in this older version of MAME, the SOURCES are in a different place src/mame/drivers/pacman.cpp instead of src/mame/pacman/pacman.cpp
  • Next, the EXCEPTION_CATCHING_ALLOWED clause was required. To get a list of functions where catching is allowed, I had to enable DEBUG=1 and SYMBOLS=1 and OPTIMIZE=0 to get a better trace of the stack on that crash. That bit is probably the biggest part of the fix. It seems like there’s some async loading of the webassembly going on. That exception needs to be caught (and likely ignored) so that a retry can be attempted
  • The default compiler optimization level in the MAME build is OPTIMIZE=3 – I found pacman a little choppy at that level, so I increased it to OPTIMIZE=s – it runs smooth and makes the download smaller too.


So now it’s working at least as a proof of concept. Hooray.

In the image I’m just running nginx on my pink macbook air the web site files look like this:

Then the final pacman.html that runs the loader looks like this:

The extraArgs call was a good find too. You can pass args like -showconfig in there which help you trouble shoot where to put the rom file etc.

I know I started by saying I was gonna deploy something into a kubernetes cluster – but I’m a little fried from the hacking I’ve done already. All in all, it was a lot of fun hacking on a C++ / WebAssembly build. Next time I’ll go onto automating the build for a couple games and dockerizing the results.

-Sandy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *