NESFab: a compiler language for writing NES games
Programming language that targets the Nintendo Entertainment System
At a glance
- What is it?
- NESFab is a programming language and toolchain for the Nintendo Entertainment System, written in C++ and built with make, with a standard library, examples covering mappers, music and tile graphics, and a licence split that keeps the games you write free of copyleft obligations. It suits hobbyist and homebrew developers who want more than raw 6502 assembly.
- Who is it for?
- Use NESFab if you are writing homebrew for the NES and want a language with a standard library and a real toolchain rather than hand-written assembly, and if you want your game code to stay free of attribution and copyleft obligations, which the split licence provides.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 124 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A language and toolchain for the 8-bit cartridge
NESFab is described as a new programming language for creating NES games, and the repository description calls it a programming language that targets the Nintendo Entertainment System. The project page, the documentation and a Discord server are all linked from the README.
What it is, concretely, is a compiler written in C++ together with a standard library and a set of examples. The compiler source lives in src/ and the library in lib/, and the whole toolchain builds with a plain makefile, so the barrier to trying it is a compiler and a make.
The value proposition is a level of abstraction above the 6502 processor the NES uses, with a library that covers the recurring chores. The examples directory is the best evidence of what that means in practice, and it is substantial: hello_world, animation, collision, counter, fade, hang_glider, maze, logging, music, metasprite, meta_meta_tiles, mouse, billiards, 4_player, flash_save, fn_ptr and mapfab, alongside mapper examples for cnrom, mapper_30, mapper_189, mmc1, mmc3 and mmc5.
That list tells you two things. The first is that the library covers the awkward parts of NES development, from the mapper chips that decide how much ROM and RAM a cartridge has, through sprite handling and music, to save files on cartridge RAM. The second is that the examples double as the documentation, which matters for a language whose official documentation is a single page on a personal site.
Debug builds carry the sanity checks
The build has two modes, and the distinction is about what the compiler does at runtime rather than about optimisation.
In debug mode the compiler includes runtime sanity checks to ensure the compiler is working correctly. In release mode there are no checks and the build is optimised for speed. The Makefile backs this up: the debug target compiles with -O0 -g, while release adds -O3 -DNDEBUG and suppresses unused variable warnings. There are also static and profile variants, where static links the binary and profile keeps optimisation with debug symbols, and an all target that builds the compiler and the test suite.
The practical instruction is in the bug reporting section. When testing bugs, use the debug-mode compiler, because it gives far better diagnostics. That is a design decision worth noticing: a compiler that can miscompile code is a difficult class of bug to debug, so the project puts self-checking behaviour into the default development build.
The test suite follows the same convention. The Makefile has a test target that depends on tests and runs the resulting binary, and the top level of the repository includes a docker-compose-test.yml for running that environment.
There is also a run target that executes the freshly built compiler, and a clean target. The makefile help text lists all of them, and the repository includes a graph.sh script at the top level.
Unofficial instructions by default, legal mode on request
The default build of the NESFab compiler produces binaries that use unofficial, or illegal, instructions. To force the compiler to only use official ones, you compile it with a flag:
make ISA=legalThis is one of the more consequential decisions a new user has to make, and it is a build-time property of the compiler rather than a per-project option.
The reason for the default is performance. The NES processor is a 6502 variant, and the unofficial opcodes give a real instruction set advantage, which is why homebrew code commonly uses them. The reason the legal mode exists is that unofficial opcodes do not work on original hardware, and some emulators and flash carts handle them differently or not at all.
The Makefile implements the switch with a single conditional, adding -DLEGAL to the compiler flags when ISA is LEGAL, which means the entire behaviour is controlled by one preprocessor symbol inside the compiler. The examples do not say which mode they were built in, so a project that cares about running on original hardware has to make that choice explicitly.
The same applies to the rest of the pipeline. A game built with a legal compiler will be the only one that runs everywhere, and a game built with the default compiler is the one that runs fastest.
Cross-building for other hosts and other architectures
By default the compiler builds executables for the x86-64 architecture, and to target others you specify a blank ARCH flag:
make ARCH= releaseThe Makefile shows how the architecture selection works. The default value is AMD64, which adds the popcnt, sse4, mcx16 and mmovbe options to the compiler flags. There is an AMD64_OLD variant that keeps only popcnt and sse4, and a MINGW_CROSS value that switches the compiler to x86-64-w64-mingw32-g++ for Windows cross builds. The blank ARCH value is what a developer uses to override the default for a different host.
The compiler itself is set with the usual CXX variable, which the README points out is useful on macOS:
make CXX=clang++That is the practical note for anyone on a Mac, where GCC is not the system compiler. The Makefile also uses CXXFLAGS additions that disable a set of warnings, including unknown warning options, narrowing and missing field initialisers, and it sets a template depth of 100 and a maximum of three errors before stopping, which are the kind of choices a compiler author makes for their own codebase rather than for its users.
Requirements are modest: a GCC compiler supporting the C++20 standard, the Boost library, which may also mean the libboost-program-options-dev package, and make. The Makefile links against boost_program_options, which is the command line parsing library.
Generated tables and the Docker build
The build process uses generated tables. The README states that these tables are included in the repository, and that you can generate them yourself with:
make tablesShipping the generated output in the repository means a fresh clone builds without a table generation step, which is a kindness to anyone trying the project for five minutes. It also means the tables can drift from the generator, so the target exists for maintainers and for anyone changing the language.
There is a Dockerfile at the repository root for a reproducible build environment. It is based on debian:bookworm-slim, copies the tree into /app, installs gcc, g++, make and libboost-program-options-dev, and then runs the release build:
RUN apt-get update && \
apt-get install -y \
gcc \
g++ \
make \
libboost-program-options-dev
RUN make releaseSo a developer without a compiler toolchain can still get a working compiler, and the CI path uses the same recipe.
The syntax_highlighting directory at the top level is a small but telling addition. A language that is meant to be edited in an editor needs editor support, and shipping the highlighting definitions in the repository is the minimum viable version of that.
The licence split that keeps your game private
The licensing arrangement is unusual enough that it deserves its own section, because it is the clearest answer to the question a new user asks first.
The compiler, in src/, is licensed under GPL 3.0, with the text in the COPYING file. The standard library in lib/, the examples in examples/ and the documentation in doc/ are licensed under the Boost Software License 1.0, with the licence text at lib/LICENSE_1_0.txt.
The README states the consequence in one line, and it is the point of the whole arrangement: if you make a game using NESFab, your code can remain private, and you do not need to include attribution in your binary release.
The reason this works is that a GPL compiler does not impose copyleft on the output of a program, and the runtime library shipped with your game is Boost licensed rather than GPL licensed. If the library had been GPL, the copyleft would have reached the game, and if the compiler had been Boost licensed it would have been a weaker guarantee for the compiler itself. The split is a deliberate and coherent decision.
For a publisher, that means the ROM you ship carries no requirement to open source your game and no requirement to credit the toolchain. Read the two licence files for yourself before shipping anything commercially, since this is a licensing question and not a technical one.
On the project itself, the repository is not archived, the last push was on 2026-06-21, and the recent releases are v1.8 on 2025-09-19, v1.7 on 2025-08-13 and a v1.6 Mac snapshot from 2025-03-16. The contributing guide is modest about the shape of contributions: small fixes are welcome as pull requests, while anything larger should start with a GitHub issue prefixed with Contrib Request: and wait for approval.
Editorial conclusion
Use NESFab if you are writing homebrew for the NES and want a language with a standard library and a real toolchain rather than hand-written assembly, and if you want your game code to stay free of attribution and copyleft obligations, which the split licence provides. Do not choose it if you need to ship on original hardware today without testing, since the compiler emits unofficial instructions by default and the legal mode changes what your code may use, or if you are looking for a language with a large existing community of tooling. Before you build, install a GCC that supports C++20 along with Boost and make, run make debug first because the debug build has runtime sanity checks that give far better diagnostics, and note that the latest release is v1.8 from 2025-09-19 with the last push on 2026-06-21.
Frequently asked questions
What is NESFab and what do I need to build it?
NESFab is a programming language for creating NES games, with the compiler written in C++. The requirements are a GCC compiler supporting C++20, the Boost library, which may also mean libboost-program-options-dev, and make.
How do I build the NESFab compiler in debug and release mode?
Run make debug for a build with runtime sanity checks that ensure the compiler is working correctly, or make release for an optimised build with no checks. The README asks you to use the debug-mode compiler when testing bugs because it gives far better diagnostics.
What does the ISA=legal flag do?
By default the compiler builds binaries that use unofficial, so called illegal, instructions. Compiling with make ISA=legal forces the compiler to use only official ones, which matters if the game has to run on original hardware.
Do I have to open source a game I write in NESFab?
No. The compiler source in src/ is GPL 3.0, while the standard library, examples and documentation are Boost Software License 1.0, and the README states that your code can remain private with no attribution required in your binary release.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pubby-nesfab)Was this useful?