Files
pokemon_pinball_wiki/process-level-emulation.md

6.0 KiB

Process-Level Game Emulation

Scope

The process lane runs only the extracted AArch64 game/display target, game assets, extracted loader/library runtime, generated Mesa overlay, and clean-room machine model. It does not boot Buildroot and does not modify extracted evidence.

Implementation entry points:

  • spike3-emu
  • emulation/process/spike3_emu.py
  • emulation/process/config.default.json
  • emulation/process/Dockerfile

Generated state is confined to emulation/work/capsule/, emulation/work/runs/, and emulation/work/packs/; all three are excluded from Git.

Confirmed Static Runtime Contract

The launcher's dependency resolver was run against:

  • /games/pokemon_pro/game
  • /games/pokemon_pro/spike3/spike_menu/game
  • /games/pokemon_pro/spike3/bin/boot_display

It resolves 51 unique DT_NEEDED libraries with no unresolved SONAMEs. The highest required and provided version nodes both match: GLIBC_2.39 and GLIBCXX_3.4.32. This confirms that the extracted loader, glibc, and libstdc++ must remain the process runtime; substituting Debian bookworm's older libstdc++ would be incompatible.

The initial asset capsule retains the full read-only /games/pokemon_pro directory. trace inventories actual file opens, and pack --profile traced creates a generated minimal tree whose copied files are verified against the read-only source with SHA-256.

Runtime Architecture

Layer Implementation
Host selection Apple Silicon uses an ARM64 Linux container; AArch64 Linux uses native execution; x86-64 Linux uses qemu-user.
Isolation Bubblewrap mounts game and target libraries read-only, creates an offline target network namespace, and binds only generated writable state.
Machine I/O libspike3machine_client.so connects /dev/spidev4.0, /dev/i2c-10, GPIO, and netbridge calls to emulation/work/machine.sock.
Display libspike3emu_stub.so writes XRGB frames to a generated regular /dev/fb0; ffplay loops the frame into Xvfb, then x11vnc/noVNC serves port 6081.
Controls Dashboard port 8090 uses the private Unix RPC socket; loopback-only TCP port 8091 serves VPX and other external controllers.
Audio ALSA is converted to fixed 48 kHz stereo S16LE file output and finalized as audio.wav per run.
Debugging Native uses extracted gdbserver; qemu-user uses -g; generated helpers resolve the PIE slide and named Ghidra offsets.
System fallback --engine system delegates to the existing qemu-system-aarch64/virtio-gpu/noVNC workbench.

Generated safe command replacements log and block reboot, halt, poweroff, shutdown, and hostname changes. OpenOCD is replaced by a controlled stub whose exit status defaults to success. The machine service implements no physical outputs; the launcher's hardware backend is deliberately refused.

Usage

./spike3-emu prepare
./spike3-emu run boot-display
./spike3-emu run spike-menu
./spike3-emu run game
./spike3-emu debug game
./spike3-emu trace game
./spike3-emu pack --profile traced
./spike3-emu clean --legacy-system
./spike3-emu clean --legacy-system --yes

Run artifacts include effective configuration, stdout, syscall trace, broker transactions, display-service logs, asset hashes, blocked commands, PCM/WAV capture, GDB setup, and exit status.

Verified Runtime Results (2026-07-11)

./spike3-emu prepare completed on Apple Silicon with 51 resolved libraries. A repeat preparation completed from the dependency/config fingerprint cache in 0.56 seconds. Docker inspection reported the image as linux/arm64, 340,201,729 content bytes (1.36 GB virtual size). No QEMU process is used in process mode.

The ARM64 Mesa smoke command completed with framebuffer status 0x8cd5, pixel 64,128,191,255, EGL 1.4, and no GL error. Live boot-display, spike-menu, and game runs loaded the HLE preload and executed the target. The menu/game accepted netbridge CRC 0x357f5327. Dashboard port 8090 returned broker state, and a direct JSONL request to 127.0.0.1:8091 returned a revisioned state.delta. Manual Start/launch/drain events changed the virtual lifecycle.

A one-shot docker stats --no-stream sample shortly after game startup was 129.78% CPU, 200.9 MiB resident usage, and 63 container processes. The native game reached its netbridge CRC check in about 5.1 seconds in that run. This is a startup sample, not a five-minute steady-state benchmark. docker top showed the extracted ld-linux-aarch64.so.1 launching /games/game under bubblewrap and no qemu-system-* or qemu-aarch64 process. The legacy cleaner dry-run reported 6,505,625,192 removable bytes (6 GiB rootfs plus a 63,173,402-byte macOS runtime cache); those files were deliberately not removed automatically. Changing-game FPS is not measurable until the connector failure is resolved.

The remaining display limit is confirmed: software EGL and the synthetic DRM resource inventory initialize, but the proprietary window helper reports failed to get DRM connector for display_number 1; GBM buffers are therefore not allocated. Current nonzero framebuffer captures are HLE diagnostic frames, not genuine changing game frames. Short runs produced no WAV data. Five-minute attract mode, a target-observed full three-ball game, live GDB attachment, and VPX round-trip remain open.

Unit verification is 14 launcher tests, 25 machine-model tests, and 3 dashboard tests. Two machine socket-bind tests skip only because the managed test sandbox denies AF_UNIX/AF_INET binds; the live Docker TCP and Unix paths were exercised.

VPX Bridge

emulation/vpx-plugin/ is pinned to VPX commit 215129065954d265100e4fa7ff063154c6fedf5d. Its build produced a Mach-O ARM64 ControllerPlugin dylib with Spike3ControllerPluginLoad and Spike3ControllerPluginUnload exports. It forwards standard cabinet actions, publishes SPIKE binary inputs/controlled devices, reconnects safely, and exposes the Spike3.Controller script API. example-spike3.vbs covers target, lane, trough, and drain events. VPX is not installed in /Applications on this host, so actual VPX loading and bidirectional table verification are not claimed.