# P1–P4 penalty-shot controller ownership — 2026-10-30-r11

## Report and result

jdc reported that a player fouled on a breakaway did not reliably get to control
the resulting penalty shot. The same physical P1/P2/P3/P4 pad now keeps it.
This does not change the foul decision, which NHL player shoots, shot physics,
keeper behavior, ordinary controls, overtime shootout rotation or season rules.

## Cause and implementation

The native penalty award remembers the attacker’s SCnum and roster number, but
later repacks the shooter into object0/6. FD22 allocates its two participants
by scanning controller sides in pad order; the earlier human pad can therefore
claim the shooter. Other adapter pads can retain nonparticipant object slots.

Capture after the native accepted getBAplayerInfo at121C2, before its saved
attacker is repacked. Player collisions execute atAEB8 after the AD66/AE00
input-bank swaps have both returned; C320..C326 are physical pad ownerships at
this capture. Match the actual human actor and its active team-side pad.

Setup atF20C selects only that attacking pad and the first native defending pad
while calling the real FD22/C0F8 allocation. Parked actors’ stale human ownership
and all four control slots are cleared for this two-participant allocation.
The full eight-byte team-side array is saved on the stack and restored before
returning, so public side choices and normal post-attempt assignment remain.

The transient award signature is consumed once. CPU/unmapped/inactive victims,
invalid records, disconnected/moved-side pads, nonpenalty contexts and native
shootouts use the original FD22 path. No per-frame hook or new RNG draw exists.
The original caller’s following D408→D0 instruction remains part of the wrapper.

## Hook and RAM map

- ROM121C2..121CC: `31C0 D406 227C FFFF B04A` →
  `4EB9 002D A800 4E71 4E71`.
- ROMF20C..F214: `6100 0B14 3038 D408` →
  `4EB9 002D A804 4E71`.
- Module2DA800,350bytes; exports+0capture,+4setup. Allocation ceiling2DB000.
- Transient RAMFFF654..FFF659: signature5053, physical pad offset+1(1/3/5/7),
  attacking side1/2. This lies after shotmeter’s F652 word and before F6C0 seeds.
  Not SRAM; capture overwrites it for every accepted award and setup consumes it.
- Only the two hook ranges, helper, version strings and checksum differ from r10.

## Evidence and limits

`experimental/verify_penalty_owner.py` is self-contained apart from its declared
exact r10 ROM and native four-pad fixture. It exercises76 paired native ABI and
controller/guard cases, plus40 paired synthetic award/full referee/setup/physical
controller/return integrations. The capture checks every register, CCR and stack;
ordinary guard branches retain native object and controller outcomes. The normal
cases verify ownership changes without other actor-coordinate/physics changes.

Integration starts from a real four-pad savestate. It explicitly sets its human
controller fixture, a breakaway eligibility bit and one private request. A private
CPU probe calls the actual PenShotChk/AddPenalty; the complete native referee,
roster selection, setup, pad reads, movement/shoot input and return continue.
No coordinates, clock, RNG, goals or successful results are injected. These are
synthetic-award native integrations, not unmodified natural foul captures or a
strength benchmark. Both sides, all active counts1–4 and each eligible pad run.
A failed early test incorrectly required the ordinary clock-running flag during
a penalty shot; the final observer uses native penalty-shot mode/start timing.
The old r10 fixture results remain as the negative control. Six older private
reset drivers were moved from2D9000 to2D5000 after their scratch span reached the
new helper. Both interrupted matrices are retained; the full matrix was restarted
with the final source manifest. Their assertions and tested native paths remain.

Independent rebuild and `--legacy-penalty-owner --version 2026-10-30-r10`
reproduce the exact current and previous ROMs. The IPS reconstructs from the
canonical original v5.1W. All79 shared suites execute; the pre-existing identical
finisher_hold geometry failure remains. Strict release gate remains FAILED.
All40 ordinary active-game raw traces match fresh r10 controls exactly; older
r6/r5 observation bounds remain separately disclosed. No Hard search restarted.

## Queued work

Only priority1 is in this release. The manual-keeper pass, prone pass recipient,
random pickup controller, penalties menu and seven-game Mini requests remain
explicitly pending in [TODO.md](TODO.md).

ROM SHA256:8f84f537ab945ea849ffc69eeba6794e9e1651556d3f878648ed846f8ebc4703; CRC32:41B60F46.

Final native negative control: 12 of20 old-ROM
synthetic awards gave a different physical pad the shooter. All20 current
controller/movement/attempt-return cases and all76 paired ABI cases pass.
Exact full matrix source files:218.
