Developing NES games on Raspberry Pi

Published 2023-03-04

This is a log of how I set up a NES development environment on a raspberry pi. It's not intended to teach you 6502 assembly, game development, or anything else besides perhaps being an endurance workout for attention spans.

Table of contents


It has been a hot summer here in Queensland, Australia. My normal desktop computer only adds to the temperature, converting kilowatts into heat. I decided to see what I could achieve on a smaller, single-board computer as a way to reduce the amount of power used and heat produced, but also just to see how much of a retro videogame studio I could squeeze onto one single board computer.

The hardware

I'm using a Raspberry Pi 4 with 8GB of RAM. This is overkill for the NES setup described here, but is useful for other development work. Also it was the first model that became available, there's a global shortage of single board computers at the time of writing.

Raspberry Pi 4 board beside a measuring rule

Because this is going to be sitting on my desk, it needs a protective case. The bundle I bought came with an 'Argon One' case, which also upgrades the mini-hdmi ports to full size hdmi via a daughterboard that plugs into the Pi.

Raspberry Pi 4 board plugged into hdmi daughterboard

I also grabbed a USB switcher to let me share my mouse and keyboard between the Pi and my desktop, for personal laziness and to reduce wear on the Pi USB ports.
Raspberry Pi 4 in an Argon One case, connected to a USB input switcher

Operating system

Nothing unusual here, I installed the official 64bit version of 'RasperryPi OS', which is Debian with some tweaks specific for the Raspberry Pi. The entire OS is written to a 32GB microSD card that slots into the underside of the Raspi case, and runs directly from there. This is the weakest part of the setup: microSDs are generally much slower than 'real' harddrives, and are quite susceptible to corruption if the Raspi is powered down unexpectedly, during a powercut or a child flicking the power switch.

Aside from some slowness if trying to browse javascript or media heavy websites like instagram or youtube, the Pi with 64bit Debian is pretty snappy. By default it looks like this:

Raspberry Pi default desktop appearance

But after some fussing and deciding to try using a more graphical setup than usual, I have the subjectively more handsome setup below:

Riced up handsomeman desktop environment


To make NES games, the following is needed:

  • A text editor, to write the code,
  • An assembler, to convert 6502 assembly to machine instructions,
  • A graphics editor, to draw the visual elements of the game. This can be done on paper and bytecode manually calculated, but I'd rather have the computer do that for me.
  • An audio editor, for creating sound effects and music. Again this can be done manually but it's a lot easier with some good tools.
  • An emulator to test the game, preferably with a good debugger.

Text editor

You can use whatever editor you like, but I'm using vim. VSCODE (or its free and open source alternative 'CODE') are also great choices that I've experimented with. 6502 Syntax highlighting is probably available for many programming focused editors. Vim, everyones favourite text editor and escape room challenge


The cc65 suite does everything I want and is actively maintained. I'm not sure there's a great deal of difference between the assemblers beyond some syntax differences. cc65 is available directly from the Debian repositories for aarch64 platform that the Raspberry Pi uses in 64bit mode, so no problems here.

Graphics Editor

This is where things start to get a little more tricky. It's possible to use any art package, photoshop, aseprite, grafx2, even MS PAINT.EXE and run the output through a converter to NES CHR (character graphics) data, and this probably path I'd need to take if I were working with an artist unfamiliar with NES technical issues. But things change in the conversion process and you might not have as much control as you'd like, so we can make life easier by using a dedicated NES graphics editing tool. At the time of writing, frankengfx's NEXXT is version 0.22.0 but already includes more functionality than I could need. It lets you draw and edit tiles, place those tiles into tilemaps (i.e to draw part of a level or a title screen), all while seeing how the graphics will actually appear on the NES when palette limitations apply.

NEXXT graphics editor showing a game in progress

The only problem is that is windows only. "But you can just run it through WINE!" I hear you cry. No child, it is not so simple. Wine is not an emulator, it can run windows applications outside of windows, but it can't convert x86 to ARM aarch64. You could run aarch64 windows applications through wine (if any exist?), but not nexxt, which is a 32bit x86 app. So why am I talking about nexxt still? Well, it turns out that with some help wine can run nexxt.

Detour: installing wine with box64 and box86

Unlike wine, box64 and box86 are emulators. They can run x64 and x86 linux programs on non x86 linux systems, respectively. Following this guide carefully will result in a special wine configuration that can run nexxt and let us draw all the little NES pixels we like on a 64bit raspberry pi!

# Lifted directly from, best to check there for updates
# Backup any old wine installations
sudo mv ~/wine ~/wine-old
sudo mv ~/.wine ~/.wine-old
sudo mv /usr/local/bin/wine /usr/local/bin/wine-old
sudo mv /usr/local/bin/wineboot /usr/local/bin/wineboot-old
sudo mv /usr/local/bin/winecfg /usr/local/bin/winecfg-old
sudo mv /usr/local/bin/wineserver /usr/local/bin/wineserver-old
# Download, extract wine, and install wine (last I checked, the Twister OS FAQ page had Wine 5.13-devel)
wget -O ~/wine.tgz
tar -xzvf ~/wine.tgz
rm ~/wine.tgz # clean up

# Install shortcuts (make launcher & symlinks. Credits: grayduck, Botspot)
echo -e '#!/bin/bash\nsetarch linux32 -L '"$HOME/wine/bin/wine "'"$#"' | \
sudo tee -a /usr/local/bin/wine >/dev/null # Create a script to launch wine programs as 32bit only
#sudo ln -s ~/wine/bin/wine /usr/local/bin/wine # You could aslo just make a symlink, but box86 only works for 32bit apps at the moment                                                                           
sudo ln -s ~/wine/bin/wineboot /usr/local/bin/wineboot
sudo ln -s ~/wine/bin/winecfg /usr/local/bin/winecfg
sudo ln -s ~/wine/bin/wineserver /usr/local/bin/wineserver
sudo chmod +x /usr/local/bin/wine /usr/local/bin/wineboot /usr/local/bin/winecfg /usr/local/bin/wineserver

# Boot wine (make fresh wineprefix in ~/.wine )
wine wineboot

Finally, nexxt can be launched via $ wine NEXXT.exe, and it will be emulated via box. Initial loading can take a few extra seconds, but performance during use is indistinguishable from running on native Windows.

Audio Editor

The sensible choice here is to run one of the various forks of famitracker. However, if you have read this far you will realise we are going to get the newest, shiniest Music and SFX tool for the NES, which is the amazing famistudio.

This is a mono app, which means that it can in theory run anywhere the mono runtime has been ported. Later on I'll be using dotnet, which should really be able to run any given mono app, but it isn't the case for the .net 4.x family of runtimes. I don't know why either.

At the time of writing the stable Debian mono packages are too old to build Famistudio, so we follow the official mono guide to add their repository containing the newer mono builds.

sudo apt install apt-transport-https dirmngr gnupg ca-certificates
sudo apt-key adv --keyserver hkp:// --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb stable-buster main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list                                                                          
sudo apt update

$ apt install mono-complete now gets the newer mono tools installed. $ git clone gets us the FamiStudio source.

FamiStudio needs a few native libraries to build and run successfully. Build scripts are included for some.

$ cd FamiStudio/ThirdParty/NesSndEmu; ./; cd -
$ cd FamiStudio/ThirdParty/GifDec; ./; cd -
$ cd FamiStudio/ThirdParty/NotSoFatso; ./; cd -
$ cd FamiStudio/ThirdParty/ShineMp3; ./; cd -
# apt install libvorbis-dev
$ cd FamiStudio/ThirdParty/Vorbis; ./; cd -

Then build FamiStudio itself from the FamiStudio directory. $ msbuild /p:Configuration=Release FamiStudio.Linux.sln

Famistudio was built in FamiStudio/FamiStudio/bin/Release, but the bundled versions of some libs are x86-64 and not aarch64, e.g:

FamiStudio/FamiStudio/bin/Release $ file ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, 
BuildID[sha1]=6f9fa5a932d45618f374da91dc71adbd1b85b3b9, not stripped                                               

Install a good libglfw with # apt install libglfw3 and copy or link it to the FamiStudio Release dir cp /usr/lib/aarch64-linux-gnu/ ./

And OpenAL (already installed for me) cp /usr/lib/aarch64-linux-gnu/

FamiStudio should now run as expected. MIDI and ogg export will need the respective libraries fixed first though if needed.

$ mono FamiStudio.exe FamiStudio running on aarch64 linux

Unfortunately the demo songs aren't included in the source release but can easily be grabbed from one of the zipped binary distributions on the FamiStudio website.


The final hurdle is an emulator with a good debugger. For a recent 48 hour gamejam I used fceux, a working build of which can be installed from the Debian repository easily with # apt install fceux. However, for the latest in cutting edge NES technology, we can install Sour's Mesen2, which has some amazing debugging features. It's also not supported on aarch64, but it's another dotnet app, so we can just run it via mono right?

Sour answering no

So instead it's time to install dotnet. I went directly to the source and installed the Microsoft Spyware from

I had trouble piping it directly into bash from curl, so I downloaded via wget $ wget And then installed both the SDK $ ./ --version latest and the runtime $ ./ --version latest --runtime aspnetcore Add dotnet to the PATH $ vim ~/.profile


# set PATH so it includes dotnet
if [ -d "$HOME/.dotnet" ] ; then
#Ask dotnet not to phone-home to the MS MotherShip

Mesen isn't purely dotnet, it also has native C++ that requires C++17 support. Clang is recommended, the default gcc provided by Debian doesn't work.
# apt install clang-13 Set clang 13 as default clang. There's probably a proper Debian way to do this.

ln -s /usr/bin/clang-13 /usr/bin/clang
ln -s /usr/bin/clang++-13 /usr/bin/clang++
ln -s /usr/bin/clang-cpp-13 /usr/bin/clang-cpp

Clone Mesen2 source. $ git clone (As of 2023 March 4, Mesen2 is on commit 88c0c76. It's possible the following changes won't always be necessary).
Edit the makefile $ vim Mesen2/makefile Add at line 52

ifeq ($(MACHINE),aarch64)
       MESENPLATFORM := $(MESENOS)-arm64

nd around line 142 change from

      cd UI && dotnet publish -c Release -r $(MESENPLATFORM) -p:OptimizeUi="true" --no-self-contained true -p:PublishSingleFile=true
      cd UI && dotnet publish -c Release -r $(MESENPLATFORM) -p:OptimizeUi="true" --no-self-contained true -p:PublishSingleFile=true


      cd UI && dotnet publish -c Release -r $(MESENPLATFORM) -p:OptimizeUi="true" --self-contained -p:PublishSingleFile=true
      cd UI && dotnet publish -c Release -r $(MESENPLATFORM) -p:OptimizeUi="true" --self-contained -p:PublishSingleFile=true

Finally we need to make sure the UI is built for linux-arm64.
$ vim UI/UI.csproj Change line 18 from

<PropertyGroup Condition="'$(RuntimeIdentifier)'=='osx-x64' Or '$(RuntimeIdentifier)'=='osx-arm64' Or '$(RuntimeIdentifier)'=='linux-x64'">                                                                       


<PropertyGroup Condition="'$(RuntimeIdentifier)'=='osx-x64' Or '$(RuntimeIdentifier)'=='osx-arm64' Or '$(RuntimeIdentifier)'=='linux-arm64'">

and 545 from

<Target Name="PreBuildLinux" BeforeTargets="PreBuildEvent" Condition="'$(RuntimeIdentifier)'=='linux-x64'">


<Target Name="PreBuildLinux" BeforeTargets="PreBuildEvent" Condition="'$(RuntimeIdentifier)'=='linux-arm64'">

The build process can then be launched from the Mesen2 root directory with $ make, resulting in a standalone executable hidden at bin/linux-arm64/Release/linux-arm64/publish/Mesen. On the first launch Mesen seemed to take a while to cache the icons used in the menus and was frozen for at least 20 seconds. But since then it has ran without problems.

Mesen2 running with many debug windows open

What a trek! I feel like a fish that had to climb a long and difficult series of platforms, all the while trying not to run out of oxygen, but now we have a full NES development studio on a Raspberry PI! Hopefully this will serve as a useful reference to me and perhaps even someone else someday. Thanks!
A fish stands below a sign reading 'The End'


Tom 'voxel' Purnell