An NES emulator written in Rust
Official release artifacts are attached to each GitHub Release:
| Artifact | Platform |
|---|---|
nerust-vX.Y.Z-linux-x86_64.tar.gz |
Linux x86_64 |
nerust-vX.Y.Z-linux-aarch64.tar.gz |
Linux aarch64 |
nerust-vX.Y.Z-macos-aarch64.app.zip |
macOS aarch64 |
nerust-vX.Y.Z-android-arm64-v8a.apk |
Android arm64-v8a |
Each tarball contains nerust_tao (the Tao frontend binary), README.md,
and LICENSE. The Android artifact is a signed APK. Each artifact has a
matching .sha256 sidecar. The macOS bundle is ad-hoc signed and not
notarized.
The official desktop frontend is Tao (nerust_tao). The Android frontend
ships as an arm64-v8a APK. The GTK4 frontend (nerust_gtk) is maintained for
build-health but is not a release artifact.
Releases are tag-driven.
- Tag the release commit as
vX.Y.Z. - The Publish release workflow creates the GitHub release draft.
- The reusable Release artifacts workflow builds the assets from that tag and publishes them.
The Release artifacts workflow also validates artifact creation for PRs into
master when release automation, workflow, packaging, or artifact-input files
change. Those runs build the same artifacts with a validation-only tag suffix
and never publish assets.
The workflows derive the release version from the tag name, and the built-in
GITHUB_TOKEN is used for release and PR updates.
Android signing uses ANDROID_CERTIFICATE and ANDROID_PRIVATE_KEY. If the
private key is encrypted, also set ANDROID_PRIVATE_KEY_PASSWORD.
- The default workspace developer path (
cargo build,cargo test) now coversnerust_core,nerust_persistence, andnerust_console. - Their in-workspace dependencies still build transitively, but GUI frontends, backend-specific crates, and ROM tooling are now validated with explicit package commands.
cargo test -p nerust_core persistence_tests --lib
cargo test -p nerust_console --lib
cargo test -p nerust_persistence --libRun support-crate unit tests explicitly when touching cartridge parsing, filters, buffers, or timing:
cargo test -p nerust_cartridge_data --lib
cargo test -p nerust_screen_buffer --lib
cargo test -p nerust_screen_filter --lib
cargo test -p nerust_timer --libRun ROM tooling and generated regression tests explicitly when touching manifest,
tooling, or ROM-test behavior. The manifest lives at rom_test/rom_tests.yaml.
cargo test -p nerust_rom_test --releaseRun frontend and backend validation explicitly when touching OpenGL or UI code:
cargo test -p nerust_screen_opengl --lib
cargo test -p nerust_gui_runtime --lib
cargo build -p nerust_android
cargo build -p nerust_gtk --release
cargo build -p nerust_tao --releaseBuild the Android APK with the Gradle packaging project:
packaging/android/package.shThis requires Java 17, the Android SDK/NDK, and cargo-ndk on the host.
If ANDROID_CERTIFICATE and ANDROID_PRIVATE_KEY are exported, the packaging
script generates a temporary JKS keystore automatically before Gradle signs the
release APK.
ROM import on Android uses the system document picker (ACTION_OPEN_DOCUMENT)
and persists the user-selected URI grant, so no broad storage permission is
declared in the manifest.
Rust and Kotlin Android logs share the Nerust tag. When the app closes
immediately on a device, clear logcat, start a filtered capture, then launch the
app:
adb logcat -c
adb logcat -v threadtime -s NerustKeep the capture running until the app closes so JNI_OnLoad, android_main,
lifecycle, popup attach, renderer resume, and panic breadcrumbs are included in
the output.
The Tao frontend is the official release target with wgpu-based rendering.
- Cargo + Rust
- Linux: GTK3 development headers (
libgtk-3-dev), OpenAL (libopenal-dev) - macOS: no additional system packages required
cargo build -p nerust_tao --releasetarget/release/nerust_tao [Rom File Path]Launch without arguments and use File → Open to load a ROM.
Note: GTK4 is maintained for build-health but is not an official release artifact. Use the Tao frontend for distribution.
- Cargo + Rust
- GTK 4.0 or greater (
libgtk-4-dev), OpenAL (libopenal-dev)
cargo build -p nerust_gtk --releasetarget/release/nerust_gtkROM regression cases are defined in rom_test/rom_tests.yaml, with
NESdev-style categories and short descriptions for each case.
# Run all ROM regression cases (requires ROM assets under roms/)
cargo test -p nerust_rom_test --release
# Validate configured ROM cases with an HTML report in target/rom-tests/validate/
cargo run -p nerust_rom_test --bin rom_tool -- validate
# Capture actual hashes/screenshots for a specific case
cargo run -p nerust_rom_test --bin rom_tool -- capture --case cpu.nestest
# Benchmark perf-enabled ROM cases
cargo run -p nerust_rom_test --bin perf --release -- --case cpu.nestestnerust_coreownsPERSISTENCE_SCHEMA_VERSION,MachineStatePayload,MapperSavePayload, and the nestedRomIdentity/CoreOptionschecks used during import.nerust_consoleownsCONSOLE_STATE_SCHEMA_VERSION,ConsoleStatePayload,ControllerStatePayload, and thepaused/frame_counter/source_framewrapper fields around opaque core state bytes.nerust_persistenceownsSTATE_ARCHIVE_SCHEMA_VERSION,StateArchiveMetadata, archive entry names, slot filtering, and thumbnail presence/blob handling;state.binremains opaque console state.- Nested payloads without their own version are covered by the
nearest owning outer schema version. For example, changing
controller representation bumps
CONSOLE_STATE_SCHEMA_VERSION, while changingRomIdentityorCoreOptionscomparison semantics bumps the owning core/archive schema and corresponding reject/filter tests. - Field addition, removal, type changes, or meaning changes that affect accepted bytes are schema changes. Bump the owning version constant before refactoring those fields.
- After merge to
master, payloads produced by the shipped schema versions must not break silently. Either keep them loadable with explicit compatibility tests, or intentionally reject them behind a version bump with explicit reject tests.
Schema change workflow:
- Identify the owning layer (
core,console, orpersistence). - Decide whether the change alters accepted bytes, target comparison, or archive interpretation.
- Bump the owning schema version when compatibility changes.
- Update the representative fixtures plus compatibility/reject tests for that layer.
- Confirm how previously shipped
masterpayloads are handled before starting the refactor.
- NRom (Mapper 0)
- MMC1 SxRom (Mapper 1)
- UxRom (Mapper 2)
- CnRom (Mapper 3, Mapper 185)
- MMC3 / MMC6 (Mapper 4)
- MMC5 (Mapper 5)
- AxRom (Mapper 7)
- BnRom (Mapper 34)
- NINA-001 (Mapper 34)
- TxSROM (Mapper 118)
- Load & Save
- Android support
- Other Mappers
- Network multiplay