This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Nx86 is a GUI-first Nintendo Switch emulator and AArch64→x86_64-v4 native binary translation system, written as a Rust monorepo. Its defining technique is Continuous Dynamic Compilation (CDC): compile a title before play, profile runtime fallbacks, promote discoveries, and passively rebuild toward higher Native Coverage (the project's main progress metric — functional readiness toward "Pure AOT" native output, not bytes compiled).
Nx86 includes NDX (N Dynamic X), a built-in modloader tightly integrated
with the AOT-first runtime. NDX is core architecture, not an optional plugin
system. See the NDX section in SPEC.md for the full spec.
SPEC.md is authoritative for direction, terminology, roadmap, platform
strategy, and the legal boundary. When code and SPEC.md disagree, SPEC.md wins.
Read it before making non-trivial design decisions.
cargo build --workspace
cargo run -p nx86-app # egui desktop shell (Linux only)
cargo run -p nx86-app -- --worker compiler-smoke # JSON-line worker IPC smoke
cargo run -p nx86-app -- --worker runtime-smokeThe full check set (matches CI in .github/workflows/ci.yml, all must pass):
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targetsRun a single test or crate:
cargo test -p nx86-runtime # one crate
cargo test -p nx86-runtime run_synthetic # tests matching a substringThe development host is Apple Silicon (aarch64-apple-darwin), but the
product target is Linux x86_64-v4. This shapes everything:
- Phases 0–15 are deliberately host-independent — pure logic, no native code generation — so they run and test on the Mac dev host. Phases 16–18 add the first native-codegen slice, but actual code execution is available only on Linux x86_64; Apple Silicon reports a clean unsupported-host outcome.
- The GUI shell (
cargo run -p nx86-app) is Linux-only; do not expect it to run on the dev host. Exercise GUI changes on the Linux target. - The Phase 16–29 backend crates (
nx86-x64-asm,nx86-jit,nx86-x64-v4,nx86-backend,nx86-regalloc,nx86-object,nx86-cache) are active for the narrow integer path: register allocation with spills, persistent.nxoobjects, a managed cache, multi-block dispatch through unconditional branches, emergency single-block JIT fallback, guarded forward block chaining, runtime profiles, profile-guided rebuilds, CFG inspection, and guard/deopt metadata.
The repo is split into ~36 crates by emulator subsystem so compiler, runtime,
GUI, storage, and graphics boundaries stay explicit. Many are placeholders that
compile but await later phases (see docs/developer/crate-plan.md for the
active-vs-skeleton breakdown). The currently substantive pieces:
Compiler front half (the working pipeline today):
nx86-arm64-decode (narrow AArch64 decoder: MOV/ADD/SUB/logical/loads/stores/
B/B.cond/ADDS/SUBS/CMP/SVC) → nx86-arm64-lift (AArch64 → NxIR with basic-block
CFG) → nx86-ir (NxIR: the shared SSA IR used by both AOT and JIT — module/
function/block/typed-SSA-values + a verifier in verify.rs) → nx86-ir-opt
(passes, e.g. dead-flag elimination).
Differential testing oracle: nx86-runtime contains both a tiny synthetic
interpreter and an independent NxIR evaluator (eval.rs). run_synthetic_test
runs a program through both and asserts they agree on final register state and
observed memory. It also attempts the Phase 18 native x86_64 tiny-block path
from the same verified NxIR function. On Apple Silicon, native execution should
report unavailable; on Linux x86_64, supported single- and multi-block
programs should match the interpreter.
Native backend slice: nx86-x64-asm owns exact byte emission for the small
assembler API; nx86-jit owns executable memory, trusted generated-code call
wrappers, and the emergency compiler/cache path; nx86-x64-v4 lowers the
supported NxIR integer subset per block;
nx86-object and nx86-cache persist and manage native blocks; nx86-backend
orchestrates single-block execution and multi-block dispatch.
Shared foundations:
nx86-core: shared config, storage layout, the IPC model (ipc.rs), and guest CPU state + NZCV flag / condition-code semantics (guest.rs). Flag and condition semantics live once here — do not duplicate them in the lifter.nx86-vmm: 64 GiB guest-memory arena boundary + software page tables. Its Linux arena reservation and thesmc_signal.rsSIGSEGV self-modifying-code handler are two of the allowed scopedunsafeislands.nx86-jit: executable-memory allocation and generated-code calls. Keep all mmap/transmuteunsafeinside its platform module with narrow safety comments and W^X permissions. Executing generated code is an explicitunsafeAPI contract; the documented generated-code call sites (innx86-x64-v4andnx86-runtime/src/pure_aot.rs) must state why the generated bytes match the requested ABI. Other sanctioned islands:nx86-profile'sflockcall.nx86-title-db: SQLite title database + human-readable TOML sidecars.nx86-testsuite: synthetic ARM64 test file format and framebuffer spec.nx86-vulkan: the safe boundary aroundash. Raw Vulkan handles and unsafe Vulkan calls must not leak into higher-level crates. As of Phase 48 it holds realashunsafe(instance/physical-device/logical-device bring-up incontext.rs, offscreen render-to-image inoffscreen.rs, windowed present inswapchain.rs) contained behind that boundary — not just safe placeholders.ash/wgpu/vulkanoare not interchangeable here — see the Vulkan policy incrate-plan.md.
App + GUI: nx86-app is the thin binary entrypoint (logging, config load,
GUI launch, worker dispatch). nx86-gui is the egui shell, styled after
Eden/yuzu's desktop layout: a menu bar + status bar over a library
centerpiece (cover-art grid / sortable list with search and filters,
per-title context menus, a Properties window), a tabbed Configure-style
Settings screen, drag-and-drop import (the primary import UX), the
first-launch wizard, framebuffer rendering, and NxIR/native dump views.
Modules: library.rs/library_model.rs (views + pure view-model),
menu.rs, status_bar.rs, settings_tabs.rs, title_details.rs,
dnd.rs, workers.rs, theme.rs (design tokens + palette — visual tweaks
belong there, not as literals in screens), screens.rs (wizard + developer
screens). GUI logic is headless-testable: view functions return action
enums handled by Nx86App, and pure view-model/menu-model functions carry
the behavior tests.
NDX modloader (planned core architecture): NDX (N Dynamic X) is Nx86's
built-in native mod system — core long-term architecture, not an optional
plugin layer. It is not yet implemented: there is no nx86-ndx crate today,
and the full design (import/bake pipeline, module layout, cheat manager, dev
mode, export mode) lives in the NDX section of SPEC.md, which is authoritative.
The intended design: mods are build-time inputs, not loose runtime-loaded
folders — normal launch never scans mod folders or resolves conflicts at
runtime. The user-facing source package is .ndx (with required nxmod.toml
metadata); the baked build/profile-cache artifact is .ndxb. NDX will convert
Eden-compatible mod layouts (romfs/, romfs_ext/, exefs/, cheats/)
through its own import/validation pipeline rather than loading foreign mod
folders directly at runtime. Mods are stored per-game by Title ID, drag-and-drop
is the primary import UX, and NDX ties into AOT cache planning so partial
recompilation is preferred and stale cache is hard-blocked. See the NDX section
in SPEC.md for the full spec and phase breakdown.
Process model: multi-process by design — GUI process, isolated compiler worker process, and isolated per-title runtime process, talking over a versioned JSON-line IPC layer. The GUI never executes guest code; only one title runs at a time. Worker modes already emit versioned JSON-line events.
- Workspace lints (in root
Cargo.toml) are warnings the project treats as hard:unsafe_code,unwrap_used,todo,dbg_macro,rust_2018_idioms. Clippy runs with-D warningsin CI, so any of these fails the build. Avoid.unwrap(),todo!(),dbg!(), and newunsafe. - Edition 2024,
rustfmt.tomlsetsmax_width = 100and Unix newlines. - Phases are tracked in
docs/phases/; each completed range gets a review indocs/developer/(e.g.phase-11-15-review.md) that records what landed, boundary checks, and the verification run. Follow that pattern.
- Before every commit, the full check set must be green:
cargo fmt --all -- --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace --all-targets. Compiler/runtime changes also run the release--ignoredfull-scale studies and thepure-aot+open-world-researchworker smokes. - Commit locally on
mainin the house style, one commit per logical change. Never open PRs. Push to origin only when the user explicitly says to; before pushing, confirm the push is a fast-forward and review the outgoing file list for strays. - For long multi-agent workflows, keep a persistent ledger file (e.g.
temp.mdat repo root, untracked) recording completed commits, in-flight agents with their scope, and the remaining queue — updated after every stage so an interrupted session resumes cleanly from the file. - Concurrent subagents must own disjoint files; agents that would edit the
same file (e.g.
nx86-gui/src/lib.rs) run sequentially. Read-only agents parallelize freely.
Nx86 must not ship or request copyrighted game dumps, proprietary firmware, console keys, proprietary SDK code, shared saves, copyrighted binary blobs, or personal user data. The prototype does not import or run real Switch software; the first real software target after the synthetic native-codegen foundations is homebrew. Do not add code paths that import or embed any of the above.
NDX repository/index features must never download, distribute, or assist with obtaining Nintendo keys, title keys, firmware, copyrighted game files, copyrighted Nintendo system files, or leaked SDK content. Users are responsible for using mods legally with their own legally obtained game content.
Title metadata (Phase 60) is keyless by default: names/art come from the file
name, plaintext container structure, a user-imported title database, or an
explicit opt-in online lookup. A separate, opt-in crate (nx86-keys, keys
feature off by default) may use the user's OWN locally-provided keys to read the
real name/icon from the user's OWN content. This stays within the boundary only
because Nx86 ships no keys/firmware, never fetches or distributes them, never
requires them for import, and gives no acquisition/extraction instructions.
Recovered names/media are local-only and excluded from shareable profiles.