A terminal-native dual-screen handheld emulator, written from scratch in Rust
Status · Install · Run · Documentation · Contributing · Korean
ninge (said "ninji"; a twist on hinge, the joint between two screens) emulates a Nintendo DS–compatible handheld inside the terminal. Both screens are drawn with the kitty graphics protocol, so it runs in Ghostty and kitty only. The core is our own, with no code from another emulator, and it boots without any BIOS or firmware dump.
ninge is early. Work follows the phases in docs/PLAN.md; the current one is
in docs/STATUS.md.
| Works today | Not yet |
|---|---|
| Both CPUs (ARMv5TE and ARMv4T) on a deterministic event scheduler | The bottom screen (2D engine B) |
| Shared work RAM, video RAM bank A, IPC, interrupts, timers, DMA | Input: the keypad always reads "nothing pressed" |
| HLE BIOS calls and direct boot: no dump needed | Touch screen, cartridge reads and its file system |
| Text backgrounds on the top screen | Sprites, 3D and sound |
| A homebrew program's console output on the top screen | The real-time clock (reads 0) |
The limits of each release are in docs/release-notes/.
ninge needs Rust (the version pinned in rust-toolchain.toml is the
one CI uses). From crates.io, from version 0.1.0 on:
cargo install ningeFrom a clone of this repository:
cargo install --locked --path crates/frontendmacOS (Apple Silicon first) and Linux are supported; Windows is planned.
Open Ghostty or kitty, then:
ninge path/to/program.nds # run a ROM
ninge # no ROM: show a test patternQuit with q, Esc or Ctrl-C. ninge runs programs you have the right to run, such as
homebrew you built yourself; it ships no ROMs and never needs a BIOS or firmware file.
./scripts/verify.sh # the whole gate: config checks, fmt, clippy, tests, 90% coverage
./scripts/build_testroms.sh # build our own test ROMs into testroms/build/ (needs Docker)The test ROMs are bare-metal programs in testroms/, built with the
BlocksDS SDK from a Docker image pinned by digest, so
verify.sh needs Docker. ninge-runner runs a ROM headlessly
and prints a hash of each screen:
cargo run -p ninge-runner -- testroms/build/smoke.nds --frames 3
# frames=3 top=<hash> bottom=<hash>develop and main only change through pull requests, and CI must be green:
<prefix>/<summary> ──PR──▶ develop ──release PR──▶ main ──tag vX.Y.Z──▶ crates.io
A branch off develop is merged by a PR whose title starts with a prefix (ADD:, FIX:,
REF:, DEL:, DOCS:, UPT:). A release is a PR from develop into main that bumps the
version, followed by a vX.Y.Z tag on main. The tag runs
release.yml, which publishes ninge-core and ninge to
crates.io after the maintainer approves it, and creates the GitHub Release with the notes
from docs/release-notes/. ninge-runner and ninge-tracediff are
repository tools and are not published.
- No commercial ROM, save, BIOS or firmware dump, or anything extracted from one, is in this
repository — not even our own built test ROMs. Tests use only programs built from
testroms/. - Hardware behavior is implemented from notes written in our own words in
docs/hw/, mainly from GBATEK. Uncertain behavior is marked unverified, not guessed. - No code from another emulator is copied or translated. A reference emulator's release binary is used only as a black-box oracle.
The full rules are in AGENTS.md §3.
crates/core/ ninge-core the emulator: pure logic, no IO
crates/frontend/ ninge the TUI and the `ninge` binary
crates/runner/ ninge-runner headless runner: tests, traces, golden frames
crates/tracediff/ ninge-tracediff trace comparison against a reference emulator
testroms/ our own bare-metal test ROM sources
tests/ integration tests, golden-frame hashes
docs/hw/ hardware notes in our own words
docs/ the plan, the current status, release notes, the logo
scripts/ verify.sh, test-ROM build, agent hooks
AGENTS.md project conventions for every coding agent and contributor
| Document | What is in it |
|---|---|
docs/PLAN.md (Korean) |
Goals, architecture, the phased roadmap (§5), references (§9) |
docs/STATUS.md |
The current phase, each phase's done-when, settled decisions |
docs/release-notes/ |
What each release can and cannot do |
docs/hw/ |
Hardware notes in our own words: the clean-room boundary |
AGENTS.md |
Architecture rules (§2), clean room (§3), tests (§5) |
testroms/README.md |
Each test ROM and what it checks |
tests/golden/README.md |
How golden-frame hashes are made and when they may change |
Issues labeled decision |
Design decisions with their context and alternatives |
Read AGENTS.md first: it holds the architecture rules, the clean-room rules
and the test conventions, and ./scripts/verify.sh must pass before a pull request. Pull
requests go to develop.