Create a pi extension that plays NES games (Zelda, Mario, Metroid, and other ROMs) inside pi using an overlay renderer and keyboard input.
- Build a pi extension that registers a
/nescommand. - Use a native NES core (
nes_rustvia napi-rs) with mapper support for classic titles. - Render frames in a custom TUI component using ANSI half‑block characters (same approach as
examples/extensions/doom-overlay). - Persist battery-backed SRAM per ROM (native core support pending).
- Primary terminal: Kitty (truecolor + Kitty keyboard protocol).
- Rely on key-up events for smoother input.
- Optional future: image-mode renderer using Kitty inline images.
- Native core only (Rust
nes_rustvia napi-rs). - Battery-backed SRAM persistence is planned (see issue #3).
/nesopens a ROM picker (from configured ROM directory) or reattaches to a running session/nes <path>loads a ROM directly (reuses the running session if same ROM)/nes debug [<path>]enables a debug overlay (FPS/memory stats)Ctrl+Qdetaches overlay (session keeps running)Qquits emulator and persists SRAM- Optional:
/nes config(guided) or/nes-config(JSON) to update config
pi-nes/
package.json # pi package manifest (pi-package)
extensions/
nes/
index.ts # registers /nes command
nes-component.ts # TUI component + render loop
nes-core.ts # wrapper around emulator core
nes-session.ts # runtime session (tick + save loop)
config.ts # config loading (rom/save dirs, audio)
input-map.ts # key mapping + config
roms.ts # ROM discovery + picker helpers
saves.ts # SRAM load/save
native/
kitty-shm/ # napi-rs shared memory addon (Kitty t=s)
nes-core/ # napi-rs native NES core (nes_rust)
- Use overlay via
ctx.ui.custom(..., { overlay: true }). - Default: render 256×240 frames via Kitty image protocol for higher resolution (full-screen, no overlay), using shared memory (
t=s) when the native addon is available, falling back to file transport (t=f) and PNG elsewhere. - Shared-memory transport requires building the native addon (
extensions/nes/native/kitty-shm). - Fallback: half‑block ANSI renderer for terminals without image support (overlay).
- Target 60fps emulation with frame skipping if rendering is slow.
- D‑pad: arrows / WASD
- A/B: Z / X
- Start/Select: Enter/Space / Tab
Ctrl+Qdetaches,Qquits- Use
isKeyRelease()for clean key‑up events.
- Store SRAM at
<saveDir>/<rom-name>-<hash>.sav(default/roms/nes/saves). - Hash is derived from the full ROM path to avoid collisions; old
<rom-name>.savfiles are ignored. - Load SRAM on ROM start.
- Persist on exit and periodically (e.g., every 5–10 seconds).
~/.pi/nes/config.jsonwith:romDirsaveDirenableAudiorenderer("image" or "text")imageQuality("balanced" or "high", controls render fps)videoFilter("off", "ntsc-composite", "ntsc-svideo", "ntsc-rgb")pixelScale(float, e.g. 1.0)keybindings(button-to-keys map, e.g.{ "a": ["z"] })
Note: audio output is opt-in; requires a native core built with audio-cpal and enableAudio: true.
- Skeleton extension +
/nescommand + overlay renderer - ROM loading + framebuffer rendering
- Input mapping + smooth key handling
- SRAM load/save + per-ROM persistence
- ROM picker UI + config command
- Optional: audio + performance tuning
- Core: native (required) via nes_rust.
- Audio: disabled (no safe dependency selected).
- Default ROM dir:
/roms/nes(configurable). - Default core:
native. - Default image quality:
balanced(30 fps). - Default video filter:
ntsc-composite. - Default pixel scale:
1.0. - Default save dir:
/roms/nes/saves(configurable).