ounce-core is an experimental, non-commercial AArch64 execution and high-level service implementation research runtime written in Rust. It contains an interpreter, three execution tiers, a paged memory model, a subset of Horizon-style supervisor calls (SVCs) and services, software models of audio and input, and a graphics model whose output is drawn by a CPU rasterizer by default (an optional gpu-present build replays a subset of draw commands through wgpu). It is a test harness for studying these subsystems. It is a clean-room research runtime, not a tool for running commercial software, and makes no claim of compatibility with any commercial software.
- Safe Rust only: the crate is
#![forbid(unsafe_code)], andgrep -rnE "unsafe[ ({]" srcreturns no matches. - Five runtime dependencies:
byteorder,cpal(host audio playback),gilrs(gamepad input),minifb(host window),thiserror. The optionalgpu-presentfeature addswgpu,winitandpollsterfor GPU presentation. - No network code, no telemetry. The only URL in the source is the project's own repository link in the CLI banner.
Most of the code, tests and documentation here are written by AI coding agents (Claude Code and Antigravity), directed and reviewed by the maintainer. Agent-made commits carry the agent as author or co-author. Every change must pass CI and the stage harness before it is merged. That harness checks programs written for this repository; it does not show that any other software works. Treat anything not covered by a test as unverified.
What this repository does not contain
- No firmware, system archives, or code or data derived from them.
- No encryption keys. The loader decrypts only containers built with the in-repo synthetic test key; retail keys are rejected (
LoadError::RestrictedCipher). Aprod.keys-format parser exists for unit tests of the key-file format only. The.gitignoreexcludes key files. - No game content, ROMs, or dumps. Test inputs are synthetic: they are generated at runtime or written as AArch64 instructions in source.
- No code copied from third-party projects. Earlier revisions contained firmware-derived and third-party-derived byte sequences; they were removed from the current tree but are still reachable in git history. See Known Issues.
Trademarks. "Nintendo", "Nintendo Switch", "Switch 2", "NVIDIA", "Tegra", "Horizon" and related names are trademarks of their respective owners. This project is not affiliated with, endorsed by, or sponsored by any of them. Names appear only to describe the hardware and ABI being modelled.
No warranty; not legal advice. The software is provided as is. Anyone using, modifying or distributing it is responsible for complying with the copyright, anti-circumvention and other laws that apply where they are. Consult a lawyer before any use beyond private research.
License. MIT. See LICENSE. The license covers the code in this repository only. It does not cover any third-party material that may appear in earlier commits (see Known Issues).
Warning
Third parties may distribute executables labelled "ounce-core" that contain malware. The only source of this project is the repository above. Build from source with the standard Rust toolchain.
Status reflects what the code does and what the tests exercise. "Modelled" means the behaviour is simulated in software, not measured against hardware.
| Subsystem | Status | Notes |
|---|---|---|
| AArch64 CPU | Implemented (subset) | Interpreter plus Tier-1 block cache, Tier-2 chained blocks and Tier-3 super-blocks. Workspace sub-crate crates/ounce-recompiler (feature native-jit) provides executable memory management (MAP_JIT on Apple Silicon, VirtualProtect on Windows, mprotect on Linux) for Tier-4 JIT recompilation. |
| Memory | Implemented | 4 KB-page MMU with intra-page boundary alignment and per-page permissions. No DRAM timing model. |
| Kernel (SVC) | Implemented (subset) | Threads, events, mutexes, memory and process queries needed by the test programs. |
| IPC | Implemented (subset) | CMIF and TIPC message formats. Services are synthetic. |
| Services | Modelled | sm:, set:sys (Drake T239 model, language, nickname), am, vi, hid, fsp-srv (directory enumeration, persistent save/sdmc partitions), pl:u (placeholder font data), time:u / time:s (nanosecond steady clock, UTC epoch timestamp), nvdrv, ro:1. |
| GPU | Modelled; optional host-GPU replay | Guest pushbuffers (Maxwell-class 3D method numbers; Ampere class planned) are decoded by a software model and drawn by a CPU rasterizer in the default build. With the optional gpu-present feature, METHOD_DRAW_ARRAYS and METHOD_DRAW_INDEXED geometry is replayed through wgpu (Metal/Vulkan/DX12) with depth testing and textures. This is not a GPU driver or NVN implementation and is checked only by the in-repo tests. |
| Shader translation | Partial | A SASS-subset decoder that emits SPIR-V 1.5 bytes. Not validated against a driver. |
Vulkan HAL (gpu/vulkan.rs) |
Model only | Structures and SPIR-V module checks. It does not call a Vulkan loader or driver. |
Host presentation (gpu/present/) |
Implemented | Guest framebuffer and 3D pushbuffer geometry presented through a host GPU hardware swapchain (wgpu: Metal on macOS, Vulkan on Linux/Windows; feature gpu-present), software window (minifb) or headless mode. |
Upscaler (gpu/upscaler.rs) |
Modelled | 2× bilinear interpolation with sharpening. Not DLSS; there is no neural network. |
| Audio | Implemented | Software 64-voice mixer; continuous non-blocking ring-buffer streaming from audout:u and audren:u to 48 kHz stereo host audio sink via cpal with polyphonic resampling, underrun tracking and latency metrics. |
Input (hid/) |
Implemented | Controller state filled from host keyboard and physical USB/Bluetooth gamepads via gilrs (analog stick threshold synthesis to D-pad bitmasks). |
| Containers | Implemented (parsing) | PFS0/NSP, HFS0/XCI, NCA3, NSO0, NRO0, NPDM, RomFS, unencrypted loose directories. |
| Dynamic linking | Implemented (subset) | MOD0 .dynamic parsing; R_AARCH64_RELATIVE, ABS64, GLOB_DAT, JUMP_SLOT; imports resolved across modules in loose packages; DT_REL/DT_RELA tables; unresolved imports reported; DT_GNU_HASH symbol counts; TLS relocations (TPREL64, DTPMOD64, DTPREL64, TLSDESC) against a static TLS layout; DT_INIT/INIT_ARRAY/FINI_ARRAY/DT_FINI runner (opt-in, OUNCE_RUN_INIT=1). PT_TLS templates (.tdata copied, .tbss zeroed, p_align honoured) when supplied from an ELF; each svcCreateThread thread gets its own TLS area and TPIDR_EL0. Limits: NSO/NRO carry no PT_TLS, so for them TLS block sizes come from STT_TLS symbols, alignment is assumed 16, and blocks start zeroed. Guest SVCs inside initialisers are not serviced. |
| Crypto | Implemented | AES-128 checked against FIPS-197 vectors. AES-128-CTR is used for NCA sections when the user supplies keys. |
Known limitations
- Timing and MIPS figures are host measurements of small synthetic programs. They are not comparable to hardware and are not pass/fail criteria.
- Behaviour that is not in the test suite should be assumed unverified.
ounce-core/
├── assets/ # Vector artwork (banner, logo, architecture diagram)
├── src/
│ ├── audio/ # Audio renderer model and PCM output sink
│ ├── cpu/ # AArch64 decoder, interpreter, Tier-1/2 JIT, Tier-3 super-block engine
│ ├── crypto/ # AES-128 and AES-128-CTR (user-supplied keys)
│ ├── fs/ # Virtual filesystem, RomFS parser, save-data storage
│ ├── gpu/ # Command-buffer model, rasterizer, shader translator, upscaler model
│ ├── hid/ # Controller state model and deadzone filters
│ ├── kernel/ # SVC dispatcher, IPC, scheduler, synchronization, SDK-style HLE runtime
│ ├── loader/ # Container parsers and synthetic image builders
│ ├── mmu/ # Paged memory model
│ ├── ui/ # Headless package inspector and CLI telemetry
│ └── main.rs # CLI and the 25-stage integration harness
├── Cargo.toml
└── README.md
cargo test --release --features gpu-present # 103 unit tests
cargo run --release --features gpu-present -- test # 25-stage integration harness (exits 0xEEE)The harness runs synthetic AArch64 programs through the CPU, kernel and service models. Each stage passes when its guest program returns the expected exit code. Host timings are printed for information only.
| Stage | Subsystem | What it checks | Exit |
|---|---|---|---|
| 01 | AArch64 ALU & branching | Fibonacci N=10 through the block cache |
55 |
| 02 | Debug SVC | svcOutputDebugString with a guest string |
— |
| 03 | Calling convention | Stack frame, BL and RET |
42 |
| 04 | CMIF IPC & vector math | sm: lookup of nvdrv, IEEE-754 FADD |
D0 = 42.0 |
| 05 | RomFS & PCM audio | Reads romfs:/title.txt, queues audio to audout:u |
— |
| 06 | AES-CTR & threads | NCA section decryption, SASS-subset decode, thread spawn | — |
| 07 | Package boot & rasterizer | Boots an NSP, futex arbitration, textured triangle | — |
| 08 | crt0 & depth | svcGetInfo, direct-chained JIT, Z-buffer occlusion |
— |
| 09 | NEON & upscaler | 128-bit vector arithmetic, 2× upscaler, save sync | — |
| 10 | FS query payload | BTI landing pad, process/thread queries, challenge response | — |
| 11 | Dynamic linker | RO NSO0 module over TIPC, NS2ROEXE reply |
— |
| 12 | Thread-affinity wrapper | BL into a leaf that returns 0xFF, then AND X0, X0, #7 |
0x07 |
| 13 | Timing & events | Synthetic event init, try-wait and tick frequency | 0x124F801 (19,200,001) |
| 14 | XCI container boot | Synthetic cartridge image → HFS0 → NCA3 → NSO0 | — |
| 15 | NVN command buffer | SVC #0x105 (research range) submission, multi-section RomFS |
— |
| 16 | audren & Vulkan model | Memory pools, voice mixing, SPIR-V module construction | — |
| 17 | Service handshake | set:sys, am:appletOE, vi:m, hid, fsp-srv |
0x777 |
| 18 | ro:1 & texture pools | LoadNro, BC7 descriptor binding |
0x888 |
| 19 | SDK-style HLE runtime | Standard allocator, SleepThread, nn::fs::OpenFile |
0x999 |
| 20 | Tier-3 super-blocks | Hot-loop fusion (host MIPS printed, not gated) | 0xAAA |
| 21 | Loose packages | NPDM parse, multi-module link, loose RomFS | 0xBBB |
| 22 | Shared font & clock | pl:u placeholder mapping, time:u, ASTC 4×4 |
0xCCC |
| 23 | Applet & GPU model | IApplicationFunctions, ISelfController, ASTC 8×8 |
0xDDD |
| 24 | FP16 SASS & NVN ring | FP16 SASS vector math (HADD2, HMUL2, HFMA2), dynamic NVN buffer cycling |
0xEEE |
| 25 | Tensor core MMA | Warp-level FP16 matrix multiply-accumulate (HMMA.1688, HMMA.16816) with FP32 accumulate, checked against IEEE binary16 vectors |
0xEEE |
- Rust 1.85 or newer (edition 2024).
- Host:
aarch64orx86_64, Linux, macOS or Windows. - Prebuilt Linux binaries dynamically link to the system ALSA and udev libraries. On Debian/Ubuntu:
sudo apt install libasound2 libudev1. On Fedora:sudo dnf install alsa-lib systemd-libs. On Arch:sudo pacman -S alsa-lib systemd-libs. Building from source also needs the development headers (libasound2-dev libudev-devon Debian/Ubuntu). - GPU presentation (optional): build with
--features gpu-present. macOS uses Metal and needs nothing extra. Windows uses Vulkan or DX12 from the installed GPU driver. Linux needs a Vulkan driver pluslibvulkan1andlibxkbcommon-x11-0on Debian/Ubuntu (vulkan-loader libxkbcommon-x11on Fedora,vulkan-icd-loader libxkbcommon-x11on Arch);mesa-vulkan-driversgives a software Vulkan driver for machines without a GPU. If the GPU path cannot start, the runtime falls back to the software window and then to headless.
cargo run --release -- info # Show modelled hardware and engine status
cargo run --release -- test # Run the 25-stage harness
cargo run --release -- run tests/fixtures/nro/hello/hello.nro fixture # Boot the libnx homebrew fixture (prints result=0x00000EEE)
cargo run --release -- inspect ./dumps/title # Inspect a user-supplied unencrypted directory
cargo run --release -- run ./dumps/title --gui # Boot an unencrypted loose directory in a window
cargo run --release -- demo-dir ./demo --gui # Generate a synthetic loose package and boot it
cargo run --release -- play # Keyboard-driven controller and graphics demo
cargo run --release -- 3d 120 # Software rasterizer benchmark
cargo run --release --features gpu-present -- gfx --present=gpu # Present through the GPU swapchain
cargo run --release -- gfx 60 --present=headless # No window; OUNCE_DUMP_FRAMES=<dir> writes PPM frames--present=auto|gpu|minifb|headless (or OUNCE_PRESENT) picks the presentation backend; auto tries the GPU first when the feature is compiled in. WGPU_BACKEND (vulkan, metal, dx12, gl) and WGPU_ADAPTER_NAME select the GPU, and OUNCE_VSYNC=0 disables vsync.
- Git history. The history has not been fully purged yet. Commits before
76ce766(for examplec7f16f2and4be9640) contain firmware-derived and third-party-derived byte sequences and a loader for an external payload file. They are still reachable frommain, all branches and thev0.3.xtags through merge25205cc, and from GitHub pull request refs. A purge is in progress. Do not use or redistribute those commits. - Commercial container decryption. Decryption in
loader/nca.rsis restricted by policy to synthetic test vectors (SYNTHETIC_TEST_KEY). Retail key decryption is disabled; research binaries must be supplied as unencrypted loose directories or homebrew packages. - License scope. The MIT license applies to this tree only. It does not cover material in the earlier commits listed above.
Changes must keep the test suite and the 25-stage harness passing. New test inputs must be synthetic or written by hand from the public AArch64 and SPIR-V specifications. Do not add firmware, keys, game content, or code or data copied from other projects.