# 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

```sh
./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.
