255 lines
14 KiB
Markdown
255 lines
14 KiB
Markdown
# Native macOS Playable Table
|
|
|
|
## Status
|
|
|
|
The native playable-cabinet implementation lives under `emulation/`. It
|
|
combines two independently useful runtimes:
|
|
|
|
- QEMU 11 with HVF runs the generated ARM64 guest and genuine Pokémon Pro game
|
|
code. Its Cocoa window and CoreAudio output are the backbox, game display,
|
|
music, speech, and game effects.
|
|
- Visual Pinball X 10.8.1 owns the physical balls, collision geometry,
|
|
switches, trough inventory, and moving playfield mechanisms.
|
|
|
|
The default VPX frontend is now the clean-room table assembled at
|
|
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`. The supported first
|
|
release is deliberately two-window. It does not use VNC,
|
|
noVNC, browser capture, JPEG screendumps, or the dashboard for gameplay. The
|
|
dashboard remains an optional diagnostic monitor. This is an
|
|
evidence-calibrated playable development recreation; it is not a
|
|
measurement-validated replica of ramp heights, coil forces, or rebound curves.
|
|
|
|
The one-command Finder entry point is `emulation/run-playable.command`; the
|
|
terminal entry point is `emulation/scripts/run-playable-macos.sh`.
|
|
|
|
### Clean rebuild workflow
|
|
|
|
With no table override, `emulation/scripts/run-playable-macos.sh` runs
|
|
`emulation/vpx-rebuild/build.sh` and selects
|
|
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`. The maintained sources are
|
|
`design/geometry.json`, `design/lights.json`, the VBS template, procedural
|
|
assets, and generator under `emulation/vpx-rebuild/`; generated `source/` and
|
|
`dist/` files must not be hand-edited. `emulation/vpx-rebuild/run-standalone.sh`
|
|
provides the short no-QEMU physics/render path.
|
|
|
|
`SPIKE3_VPX_TABLE` still permits an explicit custom table. If that table needs
|
|
a preparation step, set `SPIKE3_VPX_BUILD`; setting the custom table without a
|
|
build override prevents the clean rebuild script from modifying it. The
|
|
historical `emulation/vpx/PokemonPro_Greybox.vpx` and
|
|
`emulation/vpx/manual/PokemonPro_Manual.vpx` are deprecated historical
|
|
artifacts. The clean workflow does not inspect or write either file, and
|
|
neither is the active geometry source or default.
|
|
|
|
### Current stop point (2026-07-20)
|
|
|
|
The bridge now ingests host events immediately and routes service controls over
|
|
active-low CPU-SPI. Start and Action use the installed power-distribution
|
|
command `0x15` path, including unsolicited input reports. The genuine display
|
|
has acknowledged service navigation, and protocol tests cover a Start change
|
|
without a preceding host poll. Those are transport results, not a completed
|
|
game.
|
|
|
|
The clean table and canonical hardware contract pass their static validators.
|
|
`emulation/vpx-rebuild/qa/runtime-smoke.log` records a bounded VPX 10.8.1
|
|
`CaptureAttract` pass on the exact current table SHA-256
|
|
`ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`,
|
|
with table/VBS load, script compile/start, Metal initialization, two rendered
|
|
frames, clean close, and exit code 0. In integrated modes,
|
|
the clean script publishes physical contacts, consumes exact-address lamp
|
|
components, preserves ordered driver pulses, establishes reconnect baselines,
|
|
and releases transients/mechanisms on disconnect. Cabinet edges move the
|
|
flippers locally while cabinet/EOS state is still sent to the genuine game.
|
|
|
|
The final clean table also completed the supervised native QEMU/VPX startup
|
|
gate on 2026-07-20. The accepted session reached `Playable cabinet connected`
|
|
with synchronized VPX physics, 392 lamp updates, 7 LED frames, 1 driver pulse,
|
|
stable captive-ball contacts, and QEMU-keyboard coin/Start press-release
|
|
records. The default hardware-output wait is 420 seconds because normal game
|
|
initialization can take about five minutes. A transient bridge blip is accepted
|
|
only after a later synchronized reconnect; unrecovered disconnects still fail
|
|
closed.
|
|
|
|
The remaining immediate cabinet gate is completing and preserving a genuine
|
|
normal game cycle. The generated QEMU disk is writable and persistent, but the
|
|
current conagent `data_store` model is memory-only. After that gate, the final clean table still needs a captured normal game,
|
|
multiball, full physical shot coverage, integrated RGB/driver/mechanism traces,
|
|
restart recovery, and a 60-minute inventory soak. Earlier greybox/manual
|
|
runtime logs remain useful historical bridge evidence, but they do not prove
|
|
the clean table's geometry or final trajectories.
|
|
|
|
## Runtime ownership
|
|
|
|
| Concern | Authority |
|
|
| --- | --- |
|
|
| Rules, scoring, player/ball progression | Genuine game process in QEMU |
|
|
| Display, music, speech, game effects | QEMU Cocoa/CoreAudio |
|
|
| Ball physics and collision geometry | Native VPX at a 1,000 Hz physics step |
|
|
| Physical switch state and pulses | VPX table script and controller plugin |
|
|
| Lamps and mechanism driver commands | Genuine game through netbridge |
|
|
| Mechanical ball/rubber/flipper/gate sounds | VPX only |
|
|
| Window placement and focus | Native macOS helper |
|
|
|
|
`emulation/playfield-contract/pokemon-pro-hardware.json` is the canonical
|
|
geometry-free mapping. It records 45 playfield switches, 11 sensors, 9 cabinet
|
|
controls, 13 drivers, and 113 logical lamps expanded to 176 component bindings
|
|
at 150 exact SPIKE addresses. The plugin mapping header is generated from this
|
|
contract by `emulation/vpx-plugin/generate-layout-mapping.py`; it is not a
|
|
second handwritten address map. Ball inventory and placement live separately
|
|
in the clean geometry/script rather than becoming transport fields.
|
|
|
|
## Playfield protocol and safe recovery
|
|
|
|
`analysis/netbridge-emulator/netbridge_emu.py` provides a persistent,
|
|
loopback-only JSONL endpoint at `127.0.0.1:8091` using
|
|
`spike3-playfield/v1`. VPX sends `hello`, `switch.set`, `switch.pulse`,
|
|
`cabinet.set`, `release_transient`, and `heartbeat`. The bridge sends complete
|
|
`snapshot` records, coalescible `lamp.delta` records, lossless ordered
|
|
`driver.pulse` records, and `status` records.
|
|
|
|
Every server record has a process-unique `bridge_session`, a monotonic launcher
|
|
`bridge_generation`, sequence, model revision, and monotonic timestamp. A new
|
|
connection is not considered ready after TCP connect or `hello`. The bridge
|
|
first sends a disconnected snapshot and `synchronizing`; the plugin releases
|
|
transients, restores stable switch/cabinet state, and sends a heartbeat as an
|
|
ordering barrier. The bridge reports `ready` and sends QEMU monitor `cont` only
|
|
after that barrier and after the guest UART is connected.
|
|
|
|
With `physics_authority=vpx`, the netbridge synthetic shooter, trough, outlane,
|
|
VUK, flipper-EOS, and ball-return shortcuts are disabled from process start.
|
|
On a VPX disconnect, transient contacts open, VPX mechanisms de-energize,
|
|
stable trough/held-ball contacts remain, and QEMU receives monitor `stop`. A
|
|
supervised bridge restart removes the old readiness record, restores only
|
|
stable contacts plus lamp/driver snapshot baselines, assigns a new session and
|
|
generation, and never replays cumulative driver counts as new pulses.
|
|
|
|
The implementation and synchronization tests are in
|
|
`analysis/netbridge-emulator/test_netbridge_emu.py`. The native supervisor is
|
|
`emulation/scripts/run-macos.sh`; the outer cabinet lifecycle and identity
|
|
checks are in `emulation/scripts/run-playable-macos.sh`.
|
|
|
|
## VPX plugin and table modes
|
|
|
|
`emulation/vpx-plugin/` is rebased to the installed VPX commit
|
|
`ca80d1604e6a525db2a3f6d1ec704fc651d573fa`. Its builder checks the exact Git
|
|
revision, host binary SHA-256, ARM64 architecture, and bundle version before
|
|
writing `emulation/work/vpx-plugin/Spike3Controller/vpx-host-attestation.txt`.
|
|
The playable launcher copies the attested app to a private workspace runtime,
|
|
installs the matching plugin there, and ad-hoc signs that private copy. It does
|
|
not modify the user's source VPX app.
|
|
|
|
Network work runs on a background thread. Bounded queues keep socket I/O away
|
|
from VPX's render/physics thread; lamp state is coalesced, while driver events
|
|
retain order and cumulative counters. Reconnect snapshots establish a driver
|
|
counter baseline and cannot become stale physical events.
|
|
|
|
The table has explicit modes:
|
|
|
|
- `standalone`: provides local serving, flipper/plunger control, and mechanism
|
|
behavior for no-QEMU physics/render checks.
|
|
- `spike3`: QEMU is authoritative for lamps and every driver-driven mechanism.
|
|
- `qa-spike3`: integrated handshake/input/output acceptance without falling
|
|
back to standalone behavior.
|
|
|
|
In integrated mode, a missing controller is fail-safe: the table remains in
|
|
SPIKE mode, disables standalone serving/GI, de-energizes driver-controlled
|
|
mechanisms, and logs the error. Flippers are the deliberate exception to game
|
|
driver ownership: cabinet buttons actuate them locally and publish their
|
|
cabinet/EOS states to the bridge. Slings, pops, trough eject, auto-launch,
|
|
posts, gate, Meowth motor, and ejects move only from mapped game driver pulses.
|
|
|
|
## Physics and private visual assets
|
|
|
|
`emulation/vpx-rebuild/design/geometry.json` is the active clean-room geometry
|
|
and physics source. It fixes a 952 by 2115 VPX plane, 6.5-degree pitch, and
|
|
standard 50-unit-diameter ball. XY references follow the full 451 by 998 Pro
|
|
locator registration; Z paths, clearances, restitution, friction, coil force,
|
|
and mechanism timing remain simulation values until measured on a physical Pro
|
|
machine. `design/lights.json` independently records lamp placement and
|
|
confidence rather than deriving visual coordinates from the transport map.
|
|
|
|
The deterministically registered official Pro photograph is the provisional
|
|
playfield texture; it retains photographed hardware, shadows, and lighting
|
|
under the separately rendered VPX geometry. Original procedural meshes provide
|
|
posts, hardware, toys, and mechanisms, and the CC0 mechanical bank supplies
|
|
physics audio without copying game samples. The table, official photograph,
|
|
Pokemon/Stern imagery, reference manual, and assembled output are private-use
|
|
artifacts and are excluded from any redistributable package.
|
|
|
|
## Controls and native windows
|
|
|
|
The launcher writes a private VPX input profile without overwriting existing
|
|
service bindings. Defaults are:
|
|
|
|
| Input | Function |
|
|
| --- | --- |
|
|
| Left/Right Shift or LB/RB | Flippers |
|
|
| Return, Space, A, or right trigger | Action/launch |
|
|
| `1` or Menu | Start |
|
|
| `5` or View | Coin |
|
|
| `7`/`8`/`9`/`0` or D-pad | Service back/down/up/enter |
|
|
|
|
The QEMU guest keyboard relay now implements the same map as VPX. Previously,
|
|
only Shift was a cabinet control when the Cocoa backbox had focus; Return and
|
|
`1` through `9` still used an obsolete diagnostic switch map. That produced the
|
|
observed failure where flippers navigated Guided Setup while Start, coin,
|
|
Action, and service keys did nothing useful. The corrected relay maps `1`,
|
|
`5`, Return/Space, and `7`/`8`/`9`/`0` to the named cabinet inputs above.
|
|
|
|
The 2026-07-15 generated rootfs was rebuilt with the correction. The installed
|
|
AArch64 `/usr/local/spike-emu/bin/keyboard-switch-relay` was extracted from the
|
|
ext4 image and matched an independent source build at SHA-256
|
|
`55054003950873900b5ce203292846f158b861060dbdd2e04b5be4a8afa1869c`.
|
|
A headless QEMU monitor test injected all ten keys and
|
|
`emulation/work/logs/keyboard-relay.log` recorded matching press/release pairs
|
|
for both flippers, Start, coin, Action twice, and all four service controls.
|
|
That event-name check alone was insufficient: CPU and powerdist contacts do
|
|
not pass through a node input-bank poll. The playfield bridge now runs a
|
|
bounded host-event pump which routes those edges to their real transports
|
|
immediately while leaving ordinary node-bus edges queued until the relevant
|
|
bank read. The cabinet aliases and routes are loaded from
|
|
`emulation/playfield-contract/pokemon-pro-hardware.json`.
|
|
|
|
VPX normally receives focus. On two displays the AppKit/Accessibility helper attempts a
|
|
fullscreen VPX playfield plus fullscreen QEMU backbox. On one display it uses
|
|
approximately 65% for VPX and 35% for a 16:9 backbox. If Accessibility access
|
|
is declined or a window does not expose fullscreen through AX, both processes
|
|
remain running and the launcher prints the saved placement log and manual
|
|
layout.
|
|
|
|
## Verification boundary
|
|
|
|
The reproducible checks are split by what they prove:
|
|
|
|
- `python3 -m unittest discover -s analysis/netbridge-emulator -v` verifies
|
|
recovered serial behavior, protocol ordering, output identity, safe pause,
|
|
snapshot restore, synchronization, and lossless driver delivery.
|
|
- `emulation/vpx-plugin/tests/run-tests.sh` verifies the generated mapping,
|
|
background client, ordered input edges, output records, driver baseline, and
|
|
reconnect behavior against a loopback server.
|
|
- `python3 emulation/playfield-contract/validate_contract.py --json` verifies
|
|
the geometry-free schema, channel counts/routes, manual evidence, consumer
|
|
loading, and generated-header parity.
|
|
- `emulation/vpx-rebuild/build.sh` regenerates assets/source, verifies the
|
|
assembled VPX, compares the round-trip VBS, and runs contract/object,
|
|
geometry, clearance, lamp-route, audio, and no-ball-teleport checks recorded
|
|
in `emulation/vpx-rebuild/qa/rebuild-validation.json`.
|
|
- `emulation/vpx-rebuild/qa/runtime-smoke.log` records the exact current table
|
|
loading, compiling/starting its script, rendering two frames, and closing
|
|
with exit code 0 under VPX 10.8.1 `CaptureAttract`. It does not claim
|
|
interactive ball or mechanism coverage.
|
|
- The accepted live cabinet startup proves the real QEMU game returned lamp,
|
|
LED, and driver output through the final table/plugin path and received
|
|
QEMU-keyboard coin/Start edges. It does not prove a complete normal game.
|
|
|
|
Do not infer a normal three-ball game, multiball, measured frame-rate percentile,
|
|
or 60-minute invariant soak from the static/unit suites. Those acceptance rows
|
|
must remain explicitly open until their corresponding native run logs exist.
|
|
|
|
## Optional composed backbox
|
|
|
|
QEMU D-Bus shared-memory scanout is a follow-up only. It must demonstrate
|
|
correct page flips, sustained 60 fps for ten minutes, and no more than one
|
|
added frame before being connected to a VPX Backglass/ScoreView texture. The
|
|
working two-window Cocoa path remains the fallback; VNC/browser/screendump
|
|
capture is not an accepted gameplay fallback.
|