Skip to content

Navigation Menu

Sign in
Sign up

Mobile and Cross Platform

DoubleGate edited this page Jul 8, 2026 · 1 revision

Mobile and Cross-Platform

rusty2600-mobile is a std, host-testable, platform-agnostic bridge over the core System, exposing a small UniFFI surface (load_rom/run_frame/save_state/load_state/is_rom_loaded) that the same generated bindings drive from both Kotlin (Android) and Swift (iOS) — one Rust implementation, two mobile hosts, unchanged across both.

Design: data out, not a surface handle

The original plan called for a dedicated rusty2600-android glue crate doing JNI, native-window-to-wgpu rendering, and AAudio — "the only unsafe surface" on that platform. The actual design instead has run_frame return a plain owned Vec<u8> RGBA8 framebuffer and Vec<f32> normalized audio samples — data, not a handle to draw into. That reframing made a native-rendering glue crate unnecessary entirely: Android's own Bitmap (via copyPixelsFromBuffer) and AudioTrack (via ENCODING_PCM_FLOAT) already consume exactly those two shapes with zero hand-written JNI or unsafe on the Kotlin side — a strict improvement on this project's own unsafe-confinement convention, not a corner cut. The iOS app follows the identical pattern with Metal + AVAudioEngine.

API surface

  • MobileEmulatornew(), load_rom(bytes, rom_tag), is_rom_loaded(), run_frame(input), save_state(), load_state(bytes).
  • MobileInput — both joysticks, all four paddles, and console switches, as named fields (UniFFI's Record derive doesn't support fixed-size array fields).
  • FrameOutputrgba: Vec<u8> (the emulated frame, same crop the desktop and WebAssembly builds apply) and audio_samples: Vec<f32> (DC-blocked, normalized, at the TIA's native rate — the host resamples, not this crate).

Cross-compilation and bindings generation

cargo-ndk cross-compiles the Android cdylib; a bindgen-gated uniffi-bindgen binary target generates the Kotlin bindings from the compiled library's own embedded metadata. A key discovery from the iOS work: uniffi-bindgen generate only introspects that embedded metadata, which is identical regardless of which platform compiled the library — so a plain Linux x86_64 build of rusty2600-mobile is enough to generate real, correct Swift bindings, even on a machine with no Apple toolchain at all. The Swift bindings checked into the iOS app were genuinely generated this way.

The Android app

A real Gradle project (not a placeholder): a custom View blitting the RGBA8 framebuffer via Bitmap.copyPixelsFromBuffer, a MainActivity driving run_frame at ~60 Hz on a background thread, audio through AudioTrack, and on-screen joystick/console-switch buttons. Verified running on a real Android emulator — booted, installed, launched crash-free, a test ROM loaded through the real system file picker, screenshots captured.

Save-state UI

Mirrors the desktop convention exactly (see Frontend-Architecture): 8 numbered slots per ROM, one file per slot, kept under a directory keyed by the ROM's own content-hash tag so two different ROMs' slots can never collide even if written under the wrong key by mistake.

The iOS app

Reuses rusty2600-mobile unchanged for a SwiftUI host, following the identical MobileInput/FrameOutput data-not-handle pattern. Unlike Android, the iOS app has not been compiled or run anywhere in this project's development environment — there is no Mac toolchain available — so it ships source-complete-but-unverified, stated honestly rather than claimed as tested. This is a permanent boundary of the current development environment, not a scope decision that will be revisited by more releases; it needs a real Mac session whenever one becomes available.

Cloud save-state sync

Researched, not implemented: the reference implementations for cloud sync on each platform (CloudKit on iOS, Google Play Games Services v2 Snapshots on Android) both require live backend credentials this development environment has none of. Deferred with the concrete reference implementation documented for a future session with the right credentials, rather than guessed at.

See also

Architecture-Overview · Frontend-Architecture · Deferred-Features

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /