<!-- Research receipt: original investigation notes. File references below refer to paths inside the downloadable evidence archive. Later findings supersede explicitly earlier scope statements. -->

# How stock MvC makes moves mashable

Investigation date: 2026-09-13. Target: `mvsc`, Euro 980123, repaired
`mvce.04a`. Archive SHA-256:
`fbb9c9a0fc44c6f26481cc018bdd13d9aac5376806e736c428c4b54b274c032c`.
All 21 member CRCs match MAME's stock parent definition. ZIP hashes can vary
with packaging; the member hashes in `provenance.json` identify the contents.

## Result and scope

Ten paired diagnostic trials (five inputs per move) on unmodified stock ROMs:

| Input after the command | Kikosho | Senretsukyaku |
|---|---:|---:|
| Release | 9 | 15 |
| Hold all six | 9 | 15 |
| Tap LP every other emulator frame | 13 | 25 |
| Tap all six together every other emulator frame | 13 | 25 |
| Rotate cardinal directions with neutral gaps | 9 | 15 |

These are measured hits against one standing Spider-Man fixture, not universal
minimums, maximums, or a roster census. Positions settle to x=191/251,
y=472/472 at the first observation. Both players have human control and 144 HP.
The ROM has no changes. Health, meter and positions are seeded before saving;
all trials restore the same move-specific state. Move commands and mashing
use the emulated input ports. There are no gameplay writes during a trial.

The score is the maximum native attacker `+$120` combo byte while the first
Hyper state `+$B0=$2A` is active. This deliberately excludes later buffered
normals. MAME callback-frame counts are not a frame-data claim: game speed
and hitstop affect the relation between those counts and move updates.

## Code findings

- `$0136BC..$0136C4`: `new = held & ~previous`; stores `+$D4` and `+$DA`.
- `$01A3CC`: human button-edge helper. Nonzero `+$2B9` rejects the human
  path. Otherwise tests `+$D4 & $0770` and returns success in carry.
  One or six simultaneous button edges give one Boolean success.
- `$01A3A8`: separate direction-edge helper using `$000F`.
- `$01A3EE`: combined `$077F` helper exists; the direct absolute-JSR scan
  found no caller. This scan is not an exhaustive reachability proof.
- These helpers use a separate random/table path for CPU fighters (`+$03=0`).
- Kikosho: `$04E2A4` initializes a budget of 8 at `+$398`.
  `$04E2FA` checks buttons; `$04E302` spends one credit.
  When the remaining budget is even, `$04E310..$04E314` follows the child
  pointer at `+$392` and increments that object's `+$50` repeat count.
  Eight accepted events can thus buy four extra repeats.
- Kikosho child: `$0986FA` initializes `+$50=8`; `$09879A..$0987CE`
  rearms an attack, increments the attack-generation count, and decrements
  the repeat counter. The helper creates opportunities; collision still
  decides whether a repeat connects.
- Senretsukyaku: `$04E348` initializes 60 mash credits for humans; the CPU
  path substitutes 20 at `$04E354`. The phase timer begins at 60 at `$04E35A`.
  `$04E446` checks buttons, `$04E44E` spends a credit and returns, skipping
  the timer decrement at `$04E454`. Without an accepted press or after
  credits run out, the timer decreases toward the exit branch.
- Static additional example: Mega Man `$064564` checks buttons; `$06457C`
  subtracts four additional ticks from a shot scheduler that normally
  subtracts one at `$06455E`; `$064594` calls an emitter. This scheduling
  mechanism was not live tested or assigned a move name in this investigation.
- Static special-move example: Wolverine `$0352D8..$0352E4` directly tests
  new punches (`$0070`) and sets a continuation latch. Its continuation
  branch reads that latch at `$035302`. This is not a proof that every
  mashable move uses the common helper, nor was this move live tested here.

Addresses are 68000 CPU addresses in this exact set, not ZIP file offsets.
Instructions and PC-relative fetches need the opcode view; absolute ROM data
tables need the data view. For example, the character dispatch pointers at
`$018DF0` are read PC-relative, while navigation at `$0E2C8C` is an absolute
data read. Blindly disassembling one flat decrypted image mislabels data.

## Reproduce

Supply your own matching stock ROM and QSound dependency. With MAME 0.287,
place `probe.lua` in a fresh absolute output directory, then run the command
below for `kikosho` and again with `MASH_MOVE=senretsukyaku`. Replace the three
example paths. Each run exits after five trials; the time limit is a fallback.

```sh
MASH_DIR=/absolute/output MASH_MOVE=kikosho mame mvsc \
  -rompath /absolute/roms -noreadconfig \
  -cfg_directory /absolute/output/cfg \
  -nvram_directory /absolute/output/nvram \
  -video none -sound none -nothrottle -skip_gameinfo \
  -autoboot_delay 0 -autoboot_script /absolute/output/probe.lua \
  -seconds_to_run 120 > /absolute/output/kikosho.log 2>&1
```

The saved states are local intermediates, not part of the public evidence
package. The input navigator moves left from default Ryu to Chun-Li, and
aborts if a different fighter reaches the match. It joins both players.
This is a fixture-specific harness, not a general training-mode tool.

`python3 summarize.py` validates the two CSVs/logs and writes `summary.json`.
It checks all 2,000 rows per move, identical initial observations across
input modes, human-control flags, move IDs, and expected peak hit counts.

The archive contains no ROM, decrypted image, or emulator save state.
The complete local linear scan is exploratory and is not published as
evidence; the short code descriptions above were checked at instruction
boundaries and cross-checked against runtime writes at `$04E302`/`$04E44E`.

## Applying it to another move

Feasible as a per-move ROM change: define an input mask, a bounded active
window, fresh move-local state, and how an accepted event buys another attack
cycle, emission, or timer update. Re-arm the appropriate attack identity so
the same lingering hitbox is not assumed to be a new hit. Test hit, block,
whiff, airborne opponents, hitstop, interruption, tag/team/DHC paths, and
object cleanup. A beam extension also has to coordinate parent and child
lifetimes. Reusing the input helper alone does not provide these behaviors.

No universal all-moves patch was implemented or tested. No new balance
behavior is shipped with this investigation.

External primary references:

- [MAME CPS-2 driver and stock ROM identities](https://github.com/mamedev/mame/blob/master/src/mame/capcom/cps2.cpp)
- [MAME Lua machine, save/load and video interfaces](https://docs.mamedev.org/luascript/ref-core.html)
- [MAME Lua memory access and taps](https://docs.mamedev.org/luascript/ref-mem.html)
