# Bounded MvC1 mash study, 14 September 2026

The completed set contains **59 cases, 354 trials and 177 baseline replays**. Every case starts with three neutral replays from its own saved fixture. Their entire normalized event streams are identical within that case, including observer and native-input clocks. This establishes repeatability of these fixtures; it does not establish a global hit maximum.

## Main observed results

Counts below are executions of the verified connected-hit store during the first move window, rather than inferred changes to a counter that stops at 99.

| Move and fixture | Neutral | Changed input | Observation |
| --- | ---: | ---: | --- |
| Ryu Shinkuu Hadoken, Spider-Man, realized separation 62 | 9 | 10 | Rotating buttons; MAME and custom FBNeo agree |
| Ryu Shinkuu Hadoken, Spider-Man, separation 160 | 20 | 21 | MAME; same 12 credits as close fixture |
| Ryu Shinkuu Hadoken, Spider-Man, separation 325 | 15 | 17 | Both engines |
| Ryu Shinkuu Hadoken, airborne Hulk, separation 325 | 16 | 20 | Both engines; first impact Y=466 versus ground Y=472 |
| Chun-Li Senretsukyaku, Spider-Man | 15 | 26 | MAME alternate on/off LP or all buttons, 45 credits spent |
| Chun-Li Senretsukyaku, Spider-Man | 15 | 30 | Rotating buttons spends all 60 credits; both engines |
| Gold War Machine Proton Cannon, ordinary 144 HP | 25 | 181 | Both engines, dense cardinal rotation; native counter peaks at 99 |
| Gold War Machine Proton Cannon, diagnostic 1024 HP | 25 | 166 / 149 | MAME / custom FBNeo; separate diagnostic, not ordinary-health totals |
| Spider-Man Maximum Spider | 5 | 5 | Custom FBNeo corroborates sibling MAME negative fixture |
| Mega Man Beat Plane | 3 | 23 | Custom FBNeo fresh rotating buttons; sibling MAME also 23 |
| Roll Beat Plane | 3 | 23 | Custom FBNeo first-move window matches sibling MAME; one further post-exit event is retained separately |

Ryu's exact community **22-to-26 airborne-Hulk example was not reproduced**. Standing Hulk at separation160 produced22-to-22; other standing and jumping fixtures produced four additional hits, including20-to-24 and16-to-20. Distance effects are not monotonic. An early requested position can be changed by the game's camera/push constraints, so `realizedInitialSeparation` and first-contact coordinates take precedence over a case's nickname or requested coordinates. Cases named `air16`, `air32` and delayed jump attempts are not automatically airborne at impact: inspect first-contact Y/state and the sampled defender inputs. These tests support the mechanism and distance/target dependence without authenticating an unseen community macro.

Thirty hits is now demonstrated for Senretsukyaku. The previous 25-hit fixture was not a maximum. Holding buttons and using directions produced15, while rotating buttons reached30 with60 credit spends and60 timer decrements. Timer extensions are measured in accepted move updates, not promised additional hits.

Gold's ordinary-health181 includes continued hit processing after KO: **114 MAME / 116 FBNeo events occurred with positive health immediately before damage, including the lethal event**. The remaining67 /65 occurred after health reached zero while the first move continued. All181 belong to the observed first-move window; do not describe them as181 hits against a living full-health opponent. Both engines made180 successful missile allocations and627 timer reimbursements against723 decrements: `96 + 627 - 723 = 0`. The move then exited. This falsifies60 as a universal numerical ceiling and proves that the displayed99 is not a processing ceiling. It does not demonstrate infinite firing or establish the largest possible total.

The high-health Gold diagnostic changes only initial defender HP to1024. It yielded165 allocations and565 reimbursements against661 decrements in both engines, but166 versus149 connections. Initial primary RNG seeds differ between engines (MAME17740, custom FBNeo17719); subsequent RNG writes are logged and never frozen. Damage/collision totals are therefore not expected to match across those separate boot fixtures. Within each engine the three baseline replays are identical.

## ROM and emulator provenance

The input is stock `mvsc.zip`, Euro980123, ZIP SHA-256 `fbb9c9a0fc44c6f26481cc018bdd13d9aac5376806e736c428c4b54b274c032c`. `rom-provenance.json` lists all21 members and their checksums. No program patch is applied. No ROM, full disassembly, save state or NVRAM is included in the public evidence.

Primary emulator: MAME0.287. Independent core: **custom instrumented FBNeo schema12**, executable SHA-256 `b86cb4b864e45047d4c79e4d5f95541fb8b4ba5df31b40c388141c89be68af91`. Its local source checkout base is `c025d0ecfb9187a6962dc94ac3c240a8ae258fc9`; the executable includes local instrumentation and is identified by its binary hash, not by that base revision alone. These are new gameplay replays, beyond the earlier capability canary. They are not a test of an unmodified retail Fightcade client.

FBNeo's `src/cpu/m68k/m68kcpu.c` lines1381–1397 set `REG_PPC`, fetch the opcode, call the `FBN_LUA_EXECHOOK` callback at that instruction address, then execute the instruction handler. The harness observes pre-instruction register values and computes the imminent store value. The MAME harness instead observes actual memory writes with pass-through write taps and returns no replacement data. [MAME memory API](https://docs.mamedev.org/luascript/ref-mem.html) and [scheduled save/load operations](https://docs.mamedev.org/luascript/ref-core.html) describe these facilities.

## Fixture and input protocol

1. Boot the native game, coin/start two human players, and write only the pre-match selection slots for the desired characters. Let their normal initialization run; assert initialized character IDs and human-control bytes.
2. Wait360 observer callbacks after match initialization. Seed P1 meter3 with zero fraction, ordinary HP144 and requested horizontal coordinates. Diagnostic cases explicitly seed P2 HP1024. Let the game settle30 more callbacks, save once, and replay each input condition from that same save.
3. Deliver a native quarter-circle command: four Down callbacks, four Down+Right callbacks, four Right+two attack-button callbacks. Ryu/Gold/Spider use two punches; Chun and both Beat Planes use two kicks. Optional P2 jumps also use native controller input. There is no forced move-state or animation-state write.
4. Begin the chosen mash condition after command callback12 and only after expected move entry. Stop delivering it at the first observed exit from that exact move selector. Continue a tail observation window to diagnose buffered follow-up hits, and exclude those later hits from the authoritative first-move score.

`setup-writes.jsonl` logs every actual harness write, including address, width, before/after, reason and phase. The harness asserts no setup write occurs in phase`running`; the scorer verifies this for every case. The stock-gameplay claim means **stock program, native commands after a documented training-style fixture**, not an untouched arcade boot with no setup writes.

Controls include neutral, held-all, every-other-callback LP, every-other-callback all buttons, rotating one button per callback, alternating LP/MP, held Right, cardinal directions with neutral gaps, and dense cardinal rotation. Dense rotation emits one legal cardinal direction each callback, never opposite directions together. P2 supplies no CPU behavior or attacks; human bytes and the common mash-helper gate are checked throughout active rows. Some bounded distance-grid cases use only three neutral replays and rotating buttons, because the full mask controls are established in the core fixture. Consult each `config.json` for its exact modes.

An observer callback is **not** a native input sample or move update. Each event carries both `callbackIndex` and `nativeInputIndex`; the latter counts actual P1 edge-word stores at PC`$0136C0`. The CPU may make multiple such stores per callback, or none while stopped. Accepted credit, reimbursement and decrement PCs provide move-specific update evidence. The input recipe is scheduled in emulator callbacks; the receipts show what the game actually sampled, rather than assuming those schedules are equivalent clocks.

## Field and event semantics

| Mechanism | Verified CPU instruction / RAM |
| --- | --- |
| Held/previous/fresh input | `$0136A2..$0136C4`; P1`+$D0`, `+$D2`, `+$D4`; fresh=`held & ~previous` |
| Common button acceptance | `$01A3CC`; human flag`+$03`, gate`+$2B9`, fresh mask`$0770` |
| Common direction acceptance | `$01A3A8`; same flags, fresh mask`$000F` |
| Ryu human credit initialization | `$047908`: word`+$382`=12; timer`+$4E`=64 at`$047926` |
| Ryu accepted update | `$047ABE`: decrement credit instead of timer; timer countdown at`$047AC4` |
| Chun human credit initialization | `$04E348`: word`+$398`=60; word timer`+$4E`=60 at`$04E35A` |
| Chun accepted update | `$04E44E`: decrement credit instead of timer; timer countdown at`$04E454` |
| Gold initial countdown | `$02A8E2`: byte`+$4E`=96; this is not a credit word |
| Gold accepted direction | `$02A9D8`: increment timer byte; normal countdown at`$02A944` still runs |
| Gold missile phase | `$02A90E`: byte`+$50` increments; allocator called every fourth phase update |
| Successful Gold missile allocation | `$094D82`, with owner A6=P1; type`$1C` stored in allocated object |
| Primary RNG | `$001932` writes word`$FF4494`; Gold projectile setup calls RNG at`$09586E` |
| Connected-hit count | `$01D1A8`, owner A0=P1, stores combo byte`+$120`; preceding code explicitly clamps to99 |

`code-excerpts.txt` contains these short opcode-view ranges. They are CPU instruction addresses, not raw ZIP-file offsets. Ryu's harness `credits_init` lookup mistakenly names`$047902`, so it does not emit that initialization event; the correct store is`$047908`. Initial12 and all subsequent spends are independently visible in observations and excerpts. This does not affect credit-spend or timer counts.

Snapshot fields on a memory-write event are mostly read before the store. Use explicit `before`, `after`, `width`, `address` and `mask` for the write itself. FBNeo's `after` is computed from the imminent verified instruction; it is not a second post-instruction sample. `ui-event-example.json` identifies the Ryu move and explicitly applies the changed field's `after` value in its reduced display registers.

The late raw field named `hitSourceObject` is only the24-bit value read from defender`+$8C`; its name is not validated ownership semantics, and it may point into ROM. It is **not used for attribution or scoring**. Keep it as an explicitly raw observation or omit it from a user-facing model. Missile ownership is instead checked with A6 at the verified allocation instruction.

External scoring counts each execution of the verified hit store, including99-to-99 stores. `externalPeakComboDuringFirstMove` starts a new external chain when the native pre-store combo is zero. Attribution is by a controlled single-move fixture and the first-move interval ending at its first observer-detected exit, not by an invented per-projectile hit owner. Tail events, including buffered attacks caused by held/mash buttons, are retained but reported separately as `postMoveHitEventsExcluded`. Child hits after that interval are not included in the headline score. This makes the score a defined observation window, not a claim to have counted every delayed child hit.

Roll's initially reported FBNeo24 was its raw aggregate through the tail:23 occur within the first-move window and one after exit. That arithmetic explains the apparent23/24 discrepancy; this study has not independently identified the late event's object owner. Gold's canonical dense ordinary-health and high-health cases have zero post-exit connected-hit events in their100/150-callback tails in both engines. That negative observation does not replace general projectile-owner tracing.

Additional isolated outside-window controls were run for Ryu, Chun and Gold in both engines, with three baselines per case. The early-only condition rotates buttons or legal cardinal directions during command-adjacent callbacks13–24, after the quarter-circle command but before the first timer-processing update. The game actually sampled10–11 fresh inputs there; they are not merely scheduled-but-unsampled presses. Inputs were then fully released. The late-only condition supplies24 callbacks of rotation strictly after the first observed move exit, yielding23 native fresh samples. All12 early/late trials produced zero credit spends or timer reimbursements, and the same first-move hit/decrement counts as neutral: Ryu9/64, Chun15/60, Gold25/96. MAME Gold early-only exited one observer callback later despite identical budget and hit counts; the receipt preserves that difference. Late Chun inputs triggered3 MAME /2 FBNeo post-exit hits, which are correctly excluded rather than counted as super extension. `outside-window-controls.json` records exact event IDs, windows and assertions; `verify_window_controls.py` recomputes them. These are bounded timing controls, not an exhaustive sweep of every pre/post boundary.

Two wider questions remain open: collision ownership of arbitrary delayed child hits after player-state exit was not instrumented, and the exact community Ryu22-to-26 macro was not supplied or reproduced. Do not mark those broader tests complete or infer their results from the controls above. Maximum Spider25 and Beat Plane36 likewise remain unverified community numbers, not falsified global possibilities merely because these bounded fixtures observed fewer.

Gold's phase byte wrapped twice in the canonical dense MAME run; that byte is a cycling launch phase, not a hit total or finite-credit budget. The countdown's reimbursement never wrapped255-to0 in the tested runs. FBNeo does not instrument the phase-increment PC, so its phase-wrap field is null rather than a misleading zero. The inspected Gold loop has no separate finite input-credit budget; timer width, native edge sampling, hitstop and the round-state exit remain relevant. A finite search cannot establish unbounded duration.

## Reproduction and evidence

`result-summary.json` is the authoritative scored output; individual raw `result.json` includes tail totals and must not replace it. `baseline-reproducibility.json` provides exact event hashes for each triple. Each case includes configuration, actual setup writes, execution receipt, original events/observations and the exact harness whose SHA-256 matches the execution receipt. Historical FBNeo source was reconstructed byte-for-byte from the preserved input harness and adapter; its digest matches the original receipt.

Run a published case with Python3 and your own stock ROMs/emulator:

```sh
python3 run_case.py --engine mame --emulator /path/to/mame --roms /path/to/roms --case mame/chun-spider-near --out /path/to/new-result
python3 run_case.py --engine fbneo --emulator /path/to/instrumented-fbneo --roms /path/to/roms --case fbneo/chun-spider-near --out /path/to/new-result
python3 analyze.py
```

The FBNeo executable must implement the documented instruction hook; an arbitrary retail binary is not a drop-in substitute. Save states are regenerated locally. `execution.json` records the actual source harness digest. Original artifacts are retained locally; the public package replaces machine-specific path prefixes in execution receipts with labeled placeholders, records original/public hashes in `public-transformations.json`, and hashes all published files. No event values or observations are changed. `baseline-reproducibility.json` compares normalized events with only trial and mode labels removed.

Calibration attempts named`smoke` are excluded. Two failed because an earlier fixture saved during the opening sequence; the successful smoke repeated the later validated protocol but is also excluded to avoid duplicate counting. Earlier Gold runs stopped at a750-callback bound; their finite totals are censored observations, superseded for complete-duration claims by the`verified` cases. No global ceiling is inferred from any bounded result.
