From retro-build
Build NES ROMs from 6502 assembly using ca65 + ld65 on macOS, and optionally launch the result in Mesen. Trigger on /retro-build:nes-build, or when the user asks to build/compile/assemble an NES ROM, run `make` in a clear NES project, or open a .nes file in Mesen. Do NOT trigger on generic 6502 assembly, .s files for Apple II / Commodore 64 / Atari / other targets, or ambiguous "build this assembly" requests — only when the project is unambiguously NES.
How this skill is triggered — by the user, by Claude, or both
Slash command
/retro-build:nes-buildThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Builds NES ROMs using the `cc65` toolchain (`ca65` assembler, `ld65` linker) and, on request, launches the resulting ROM in Mesen.
Builds NES ROMs using the cc65 toolchain (ca65 assembler, ld65 linker) and, on request, launches the resulting ROM in Mesen.
Before running any build command, verify the current directory contains at least one strong NES signal:
.nes file..cfg file (any name) whose contents reference NES-shaped segments like CHARS, VECTORS, or memory regions at $8000 / $FFFA..s file containing iNES header bytes ("NES" or $4E, $45, $53, $1A), PPU register addresses ($2000–$2007), or PPU register names (PPUCTRL, PPUMASK, PPUSTATUS, PPUADDR, PPUDATA).Makefile that invokes both ca65 and ld65.If none of these are present, stop and tell the user: "This doesn't look like an NES project — I expected an iNES header, PPU register references, or an NES-shaped .cfg." Do not fall back to building it as generic 6502 assembly.
Once confirmed as NES, pick the right command based on what's in the directory:
make..s + .cfg present → before linking, run the iNES header check below; then ca65 <name>.s -o <name>.o and ld65 -C <cfg> <name>.o -o <name>.nes..s file (with NES signals from Step 1) → show the user the default NROM linker config and Makefile templates below (substituting the actual .s basename for NAME) and ask whether to write them. Only write nes.cfg and Makefile once the user confirms. Then run the iNES header check and make..s or .cfg files with no Makefile → ask the user which to build rather than guessing.On success, report the path to the produced .nes. Do not launch Mesen automatically — wait for the user to ask.
ld65 call)The iNES header declares PRG and CHR bank counts that must match the linker config's memory sizes. A mismatch produces a ROM that loads but misbehaves silently.
.byte line in the HEADER segment of the .s file. Bytes 0–3 are the iNES magic. Byte 4 is the PRG bank count (×16 KB = ×$4000); byte 5 is the CHR bank count (×8 KB = ×$2000).nes.cfg, read PRG: size=… and CHR: size=….prg_expected = prg_banks × $4000, chr_expected = chr_banks × $2000.When the user explicitly asks to open / run / test / play the ROM in Mesen, first check whether Mesen is already running:
if pgrep -x Mesen >/dev/null; then
echo "Mesen is already running — press F11 (Power Cycle) or use File → Reload ROM to load the new build."
else
/Applications/Mesen.app/Contents/MacOS/Mesen <rom>.nes &
fi
The direct binary path matters — open -a Mesen <rom> does not pass the file argument through to Mesen on this system (Mesen opens to the recent-games list instead of loading the ROM).
If /Applications/Mesen.app is missing, run find /Applications -maxdepth 3 -name "Mesen*" and use whatever's there, or surface the error.
ca65 and ld65 come from the cc65 package: brew install cc65.ca65 or ld65 is not on PATH, stop and surface the install command. Don't try to build.ca65 are precise and useful.Use this as nes.cfg for the Step 2 fallback (2×16 KB PRG = 32 KB, 1×8 KB CHR, mapper 0):
MEMORY {
ZP: file="", start=$0000, size=$0100, type=rw;
RAM: file="", start=$0200, size=$0600, type=rw;
HDR: file=%O, start=$0000, size=$0010, fill=yes, fillval=$00;
PRG: file=%O, start=$8000, size=$8000, fill=yes, fillval=$FF;
CHR: file=%O, start=$0000, size=$2000, fill=yes, fillval=$00;
}
SEGMENTS {
ZEROPAGE: load=ZP, type=zp;
BSS: load=RAM, type=bss;
HEADER: load=HDR, type=ro;
CODE: load=PRG, type=ro, start=$8000;
RODATA: load=PRG, type=ro;
VECTORS: load=PRG, type=ro, start=$FFFA;
CHARS: load=CHR, type=ro;
}
Use this as Makefile for the Step 2 fallback. Replace hello with the detected .s basename:
NAME = hello
MESEN = /Applications/Mesen.app/Contents/MacOS/Mesen
$(NAME).nes: $(NAME).o nes.cfg
ld65 -C nes.cfg $(NAME).o -o $@
$(NAME).o: $(NAME).s
ca65 $< -o $@
run: $(NAME).nes
$(MESEN) $< &
clean:
rm -f $(NAME).o $(NAME).nes
.PHONY: run clean
Note: the indentation in recipe lines must be a tab, not spaces — make will error otherwise.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub jaymcgavren/ai-skills-marketplace --plugin retro-build