Zig examples targeting MOS 6502 platforms via zig-mos-bootstrap and llvm-mos-sdk.
- zig-mos-bootstrap β Zig toolchain with LLVM-MOS backend
git clone https://github.com/kassane/zig-mos-examples.git
cd zig-mos-examples
zig build --summary allNamed steps (build one at a time):
# NES
zig build nes-hello1
zig build nes-hello2
zig build nes-hello3
zig build nes-zig-logo
zig build nes-fade
zig build nes-sprites
zig build nes-pads
zig build nes-color-cycle
zig build nes-fullbg
zig build nes-random
zig build nes-bat-ball
zig build nes-megablast
zig build nes-gg-demo
zig build nes-mappers
zig build nes-cnrom-hello
zig build nes-cnrom-sprites
zig build nes-unrom-hello
zig build nes-unrom-color-cycle
zig build nes-mmc1-hello
zig build nes-mmc1-sprites
zig build nes-mmc3-hello
zig build nes-mmc3-pads
zig build nes-gtrom-hello
zig build nes-gtrom-color-cycle
zig build nes-unrom-512-hello
zig build nes-action53-hello
zig build fds-hello
# Commodore 64
zig build c64-hello
zig build c64-fibonacci
zig build c64-plasma
# Commodore 128
zig build c128-hello
# Commodore PET
zig build pet-hello
# Supervision
zig build supervision-hello
# Dodo
zig build dodo-hello
# Commander X16
zig build cx16-hello
zig build cx16-k-console-test
zig build vic20-hello
# Atari Lynx
zig build lynx-hello
# Atari 2600
zig build atari2600-colorbar
zig build atari2600-3e-colorbar
# Atari 8-bit
zig build atari8dos-hello
zig build atari8-cart-hello
zig build atari5200-cart-hello
zig build atari8-megacart-hello
zig build atari8-xegs-hello
# GEOS-CBM
zig build geos-hello
# PC Engine
zig build pce-color-cycle
zig build pce-color-cycle-banked
# Neo6502
zig build neo6502-graphics
# SNES
zig build snes-hello
zig build snes-color-cycle
zig build snes-zig-logo
zig build snes-pi-test
zig build snes-pi-fastrom
zig build snes-hirom-hello
zig build snes-pads
# mos-sim (6502 simulator)
zig build sim-helloOptional platforms:
# Ben Eater 6502
zig build eater-hello-lcd
zig build eater-asm-clobber
# MEGA65 β mega65-libc fetched automatically via build.zig.zon
zig build mega65-hello
zig build mega65-plasma
zig build mega65-viciv
# Apple II β dependency fetched automatically via build.zig.zon
zig build apple2-helloOutput files land in zig-out/bin/.
| Example | Preview |
|---|---|
c64-plasma β Commodore 64 plasma effect |
|
pce-color-cycle-banked β PC Engine banked colour cycle |
|
atari2600-colorbar β Atari 2600 colour bars |
| Step | Platform | CPU | Output |
|---|---|---|---|
nes-hello1, nes-hello2, nes-hello3 |
NES NROM | mos6502 | .nes |
nes-zig-logo |
NES NROM | mos6502 | .nes |
nes-fade |
NES NROM | mos6502 | .nes |
nes-fullbg |
NES NROM | mos6502 | .nes |
nes-sprites |
NES NROM | mos6502 | .nes |
nes-random |
NES NROM | mos6502 | .nes |
nes-pads |
NES NROM | mos6502 | .nes |
nes-color-cycle |
NES NROM | mos6502 | .nes |
nes-bat-ball |
NES NROM | mos6502 | .nes |
nes-megablast |
NES NROM | mos6502 | .nes |
nes-gg-demo |
NES NROM | mos6502 | .nes |
nes-mappers |
NES CNROM (4-bank) | mos6502 | .nes |
nes-cnrom-hello |
NES CNROM | mos6502 | .nes |
nes-cnrom-sprites |
NES CNROM sprites | mos6502 | .nes |
nes-unrom-hello |
NES UNROM | mos6502 | .nes |
nes-unrom-color-cycle |
NES UNROM colour-cycle | mos6502 | .nes |
nes-mmc1-hello |
NES MMC1 | mos6502 | .nes |
nes-mmc1-sprites |
NES MMC1 sprites (CHR RAM) | mos6502 | .nes |
nes-mmc3-hello |
NES MMC3 | mos6502 | .nes |
nes-mmc3-pads |
NES MMC3 controller + collision | mos6502 | .nes |
nes-gtrom-hello |
NES GTROM | mos6502 | .nes |
nes-gtrom-color-cycle |
NES GTROM colour-cycle + LED | mos6502 | .nes |
nes-unrom-512-hello |
NES UNROM-512 | mos6502 | .nes |
nes-action53-hello |
NES Action53 (mapper 28) | mos6502 | .nes |
fds-hello |
Famicom Disk System | mos6502 | .fds |
c64-hello, c64-fibonacci |
Commodore 64 | mos6502 | .prg |
c64-plasma |
Commodore 64 | mos6502 | .prg |
vic20-hello |
Commodore VIC-20 (24K) | mos6502 | .prg |
c128-hello |
Commodore 128 | mos6502 | .prg |
pet-hello |
Commodore PET | mos6502 | .prg |
supervision-hello |
Watara Supervision | mos65c02 | .sv |
dodo-hello |
Dodo | mos65c02 | (raw) |
cx16-hello |
Commander X16 | mosw65c02 | .prg |
cx16-k-console-test |
Commander X16 | mosw65c02 | .prg |
lynx-hello |
Atari Lynx | mos6502 | .bll |
atari2600-colorbar |
Atari 2600 | mos6502 | .a26 |
atari2600-3e-colorbar |
Atari 2600 (3E) | mos6502 | .a26 |
atari8dos-hello |
Atari 8-bit DOS | mos6502 | .xex |
atari8-cart-hello |
Atari 8-bit cart | mos6502 | .rom |
atari5200-cart-hello |
Atari 5200 cart | mos6502 | .rom |
atari8-megacart-hello |
Atari 8-bit MegaCart | mos6502 | .rom |
atari8-xegs-hello |
Atari 8-bit XEGS cart | mos6502 | .rom |
geos-hello |
GEOS-CBM | mos6502 | .cvt |
pce-color-cycle |
PC Engine | mosw65c02 | .pce |
pce-color-cycle-banked |
PC Engine banked | mosw65c02 | .pce |
neo6502-graphics |
Neo6502 | mosw65c02 | .neo |
snes-hello |
SNES LoROM | mosw65816 | .sfc |
snes-color-cycle |
SNES LoROM | mosw65816 | .sfc |
snes-zig-logo |
SNES LoROM | mosw65816 | .sfc |
snes-pi-test |
SNES LoROM | mosw65816 | .sfc |
snes-pi-fastrom |
SNES FastROM | mosw65816 | .sfc |
snes-hirom-hello |
SNES HiROM | mosw65816 | .sfc |
snes-pads |
SNES LoROM | mosw65816 | .sfc |
sim-hello |
mos-sim (6502 simulator) | mos6502 | binary |
eater-hello-lcd |
Ben Eater 6502 LCD | mosw65c02 | .rom |
eater-asm-clobber |
Ben Eater 6502 asm clobber test | mosw65c02 | .rom |
mega65-hello, mega65-plasma |
MEGA65 | mos45gs02 | .prg |
mega65-viciv |
MEGA65 VICIV | mos45gs02 | .prg |
apple2-hello |
Apple IIe ProDOS | mos6502 | .sys |
Build and run in one step (no prebuilt mos-sim binary required):
zig build run-sim-helloOr build the simulator separately first:
zig build build-mos-sim # compiles mos-sim from llvm-mos-sdk source
zig build sim-hello
zig-out/bin/mos-sim zig-out/bin/sim-hellomos-sim benchmarks
==================
fib(10) = 55 ( 7 cycles)
fib(20) = 6765 ( 4 cycles)
sieve<127>: 31 primes (6905 cycles)
Built automatically alongside the examples (zig build --summary all).
Identifies and inspects any MOS-platform output binary.
zig-out/bin/bininfo <file> [filesβ¦] [flags]| Flag | Short | Description |
|---|---|---|
--sections |
-S |
List ELF sections |
--symbols |
-n |
List ELF symbols |
--dwarf |
-d |
Dump DWARF section inventory, CU headers, and subprogram listing |
--xxd |
-x |
Hex+ASCII dump (xxd style) |
--xxd-limit N |
Cap xxd output at N bytes | |
--disasm |
-D |
6502 disassembly of file payload |
Flags may appear before or after filenames.
Detected formats: iNES 1.0/2.0 (.nes), SNES SFC/SMC (.sfc/.smc β SMC 512-byte copier header auto-stripped), FDS raw (.fds), GEOS CVT (.cvt), CBM PRG (.prg), Atari 2600 (.a26), Atari 8-bit cart (.rom), Atari XEX (.xex), Lynx BLL (.bll), PC Engine (.pce), Watara Supervision (.sv), Neo6502 (.neo), Apple IIe ProDOS (.sys), mos-sim binary, ELF. HiROM vs LoROM is auto-detected from map-mode byte.
# Inspect a built NES ROM
bininfo zig-out/bin/hello1.nes --sections
# Dump first 64 bytes of an FDS binary
bininfo zig-out/bin/fds-hello.fds -x --xxd-limit 64
# Disassemble a PRG (auto-skips 2-byte load-address header)
bininfo zig-out/bin/c64-hello.prg -Dzig-out/bin/romtool <subcommand> [options] <file>| Subcommand | Description |
|---|---|
disasm |
6502 / 65816 disassembly |
unpack |
Extract PRG/CHR banks + header.txt |
pack nes |
Assemble PRG+CHR banks back into an iNES ROM |
pack snes |
Assemble bank binaries into an SFC ROM with correct checksum |
disasm options:
| Flag | Description |
|---|---|
--bank N |
NES: PRG bank N (16 KB, 0-based); SNES: 32 KB bank N (0-based) |
--base 0xNNNN |
Override load address displayed |
--offset N |
Skip N bytes before disassembling (address adjusts automatically) |
--length N |
Disassemble at most N bytes |
--m8 / --x8 |
SNES: start with M/X flags set (8-bit accumulator/index) |
# Disassemble NES PRG bank 1 from offset $1234
romtool disasm game.nes --bank 1 --base 0xC000 --offset 0x1234 --length 128
# Disassemble SNES RESET vector (HiROM, bank 1, offset $7F98)
romtool disasm game.smc --bank 1 --base 0x8000 --offset 0x7F98 --m8 --x8 --length 40
# Unpack all banks to a directory
romtool unpack game.nes /path/to/game-banks/
# Reassemble NES ROM from PRG + CHR binaries
romtool pack nes -o repacked.nes prg-bank-00.bin chr-bank-00.bin
# Assemble SNES ROM from 32KB bank binaries (checksum auto-computed)
romtool pack snes -o out.sfc --map lorom bank-00.bin [bank-01.bin ...]
# Same with SMC copier header and custom title
romtool pack snes -o out.smc --map hirom --title "MY GAME" --smc bank-00.bin# CHR ROM β SVG (view/edit tiles in any SVG editor)
chr2svg zig-out/bin/chr-bank-00.bin tiles.svg --scale 3 --cols 16
# SVG β CHR ROM (after editing)
svg2chr tiles.svg chr-bank-00.binChecks that an SVG tile file is well-formed and compatible with svg2chr (correct viewBox, pixel dimensions, tile count).
svgcheck tiles.svgConverts a MOS ELF debug binary to a Mesen .mlb label file for source-level symbol display in the Mesen emulator debugger.
elf2mlb zig-out/bin/hello1.nes hello1.mlb hello1.nes.elfThe gen-labels build step runs this automatically for all NES examples.
- NES mappers β CNROM 4-bank CHR demo; press Start to cycle through 4 CHR banks with distinct palettes. ROM: 32 KB PRG + 32 KB CHR ROM (4Γ8 KB banks).
- NES CNROM hello β uses translated
mapper.hviab.addTranslateC; callsset_chr_bank(0)to initialise the CNROM CHR bank. ROM: 32 KB PRG + 8 KB CHR ROM. - NES CNROM sprites β CNROM OAM sprite rendering with banked CHR. ROM: 32 KB PRG + 8 KB CHR ROM.
- NES UNROM hello β uses translated
mapper.h; callsset_prg_bank(0)to initialise the UNROM PRG bank. ROM: 256 KB PRG + 8 KB CHR RAM. - NES UNROM colour-cycle β cycles backdrop colour across multiple UNROM PRG banks. ROM: 256 KB PRG + CHR RAM.
- NES MMC1 hello β uses translated
mapper.h; callsset_prg_bank(0)andset_mirroring(MIRROR_VERTICAL)to initialise MMC1 registers. ROM: 256 KB PRG + 8 KB CHR RAM. - NES MMC1 sprites β MMC1 OAM sprites with 8 KB CHR uploaded to CHR RAM via
vram_write; callsset_mmc1_ctrl(0x0E)first to set 8 KB CHR mode. - NES MMC3 hello β uses translated
mapper.h; callsset_prg_bank(0)to initialise MMC3 PRG bank. ROM: 512 KB PRG + CHR RAM. - NES MMC3 pads β MMC3 controller input + sprite-background collision; uploads CHR tiles to CHR RAM.
- NES GTROM hello β uses translated
mapper.h; GTROM (Codemasters) flash mapper. ROM: 512 KB PRG flash. - NES GTROM colour-cycle β GTROM PRG bank cycling with LED toggle via
$5000write. - NES UNROM-512 hello β uses translated
mapper.h; callsset_prg_bank(0)andset_chr_bank(0)to initialise mapper 30 registers. ROM: 512 KB PRG + 32 KB CHR RAM. - NES Action53 hello β uses translated
mapper.h; mapper 28 (Action53 multicart). ROM: 64 KB PRG + 8 KB CHR RAM. - FDS hello β Famicom Disk System; uses raw PPU register writes via
nes/hardware.zig(no neslib β FDS has no CHR ROM, raw PPU only). Writes dark-green backdrop ($1A) to palette$3F00. - VIC-20 hello β uses CBM KERNAL
cbm_k_chroutto print "HELLO VIC20!", then cycles VIC chip background/border colour register ($900F). Targets 24K memory expansion, loads at$1201. - C64 hello β uses translated
c64.h(VIC-II typed struct) viab.addTranslateC; cycles VIC-II border colour register. - CX16 hello β uses CBM KERNAL
cbm_k_chroutto print "HELLO X16!", then cycles the border colour register. - Lynx hello β uses translated
_mikey.h(MIKEY typed struct) viab.addTranslateC; animates all 32 palette entries. - SNES FastROM β same Ο demo as
snes-pi-testbuilt with map mode$30(FastROM) and MEMSEL=1; ROM mirrored at$80:8000, runs at full 3.58 MHz. - SNES HiROM β 64 KB bank layout with header at
$FFC0; data bank register forced to$00vialda #$00; pha; plbin crt0 (safe for both LoROM and HiROM). - Atari 8-bit DOS hello β uses
std.c.printfvia CIO-backed libc (E: screen editor device). - Atari 8-bit cart hello β uses translated
_gtia.h(GTIA write struct) viab.addTranslateC; cycles COLBK background colour, synced to ANTIC VCOUNT. - Atari 5200 cart hello β cycles GTIA COLBK register (
$C01A) through all hues via direct*volatile u8write. No CIO/stdio on 5200; pure hardware register loop. - Atari 8-bit MegaCart hello β uses translated
_gtia.h; cycles COLBK synced to ANTIC VCOUNT. MegaCart bank 0 is selected at cold start via the SDK'stail0.sstub beforemain()runs. - Atari 8-bit XEGS hello β same GTIA colour-cycle as MegaCart but targeting the XEGS cartridge format; XEGS
_cart_initwrites$D500to select bank 0 beforemain()runs. - GEOS-CBM hello β uses GEOS kernel jump-table entries (
__GraphicsString,__PutString,__UseSystemFont,__MainLoop) asextern fndeclarations resolved bygeos.ld; passes arguments via zero-page registers__r0($0002) and__r11($0018). Output is a.cvtVLIR-formatted GEOS application file, verified by03 00 FF+llvm-mos VLIRmagic at offset$00. - sim-hello β uses translated
sim-io.h(typed MMIO struct) viab.addTranslateC; benchmarks fib(10), fib(20), and sieve of Eratosthenes for primes < 128.
Apache 2.0 β see LICENSE.











