# NHL94 forum fixes — 2026-10-28-r2

Released ROM: `NHL 2026 - 94 Edition v5.1W - Season - jdc - 2026-10-28-r2.bin`.
SHA256 `8caf93b508276b255e274b59573934c6ce7c09804d17bacec99ca93d22090662`;
CRC32 `14EDE156`. Build input remains the unchanged plain patched v5.1W.

## Reports and production changes

77's settings/PIM report was released in 2026-10-28 and remains included.
Tickenest's mechanics report:
https://forum.nhl94.com/index.php?/topic/40511-2-guys-one-ai/#comment-217601
Research: https://github.com/nhl94dotcom/NHL94MatchViewer/blob/main/docs/GENESIS_MECHANICS.md
and https://github.com/abdulahmad/NHL94Genesis . Credits: CREDITS.md.

| Problem | Change in this release |
|---|---|
| Goalie selects a boxed skater | Native pass scan $BD02 BEQ -> BLE after TST position; cached fallback $2FF1AC does the same. Both reject position <= 0. |
| Runaway human skater | $AC12 calls forumfix +0; a human's pflags bit 0 is cleared before friction. Native CPU glide behavior is retained. This repairs a stale mode, not an arbitrary hard speed cap. |
| Penalty shot late goal / ghost puck | $ED96 calls +4: clear X/Y/Z velocity and set puck pflags bit 2. $EF92 calls +8: first assignment clears bit 2 and preserves the caller's Z branch semantics; later calls keep the native early-return branch. |
| Wing/center awareness | $D0DE/$D2D6 read $6A (offensive) instead of $6B (defensive). |
| Very fast short targeted passes | $BF54 calls +12, wrapping the exact cached $2FF140 goalie/minimum timing routine. Skaters get minimum time 2 instead of 0/1; goalkeeper timing is unchanged. This mitigates the worst short-pass truncation, not a claim that all rounding at every distance is eliminated. |
| CPU pulls only when two down | Already corrected in base v5.1W ($F754); no new change. Base credits McMarkis. |

`forumfix.s` is 104 bytes at $2EF000. No new persistent RAM/SRAM fields.
Claude had edited patch.py, but build_test.py reads a cached plain-patched ROM:
those edits alone would not have reached this release. forumfix.py therefore
installs both cached pass fixes explicitly with byte guards. Original patch.py
also retains the corresponding source changes for future base builds.

## Startup credits and website

After the existing first image, a three-second credits card precedes the main
menu; Start (or another native skip button) continues early. The picture is
preserved. `credits.s` at $2D8000 follows the existing intro renderer; its own
18,502-byte tile card is at $2D0000. `prepare_boot_credits.py` renders maintained
attribution to tools/data/boot-credits.png and .bin. Uses existing intro palette,
VDP setup, timing/RNG words and return contract, no SRAM writes.

Main-page navigation now includes forum.nhl94.com and Credits. The forum section
separates reports, releases, already inherited changes and research suggestions.
Both prominent Download ROM buttons include the exact version; legacy files
remain available. Full credits link the base authors, inherited work and engine
references. Brodeur30's diving edit and reply drafts: FORUM_FOLLOWUP.md.

## Verification on the exact final ROM

Private evidence:
`~/.config/retroarch/roms/claude/work/forum-fixes-20261028/`.
All 16 final suites pass (final-checks/regression.json):

- Critical probability/lookups: 10; flash lifecycle; signed velocity: 88.
- Crit flames: 20 real native crit shots, **1,362/1,362** draw-boundary/world checks.
- Season navigation: 18 cases / 342 checks; finished seasons: 9 / 108;
  menu team labels: 18 / 240; native fonts; independent slots: 20.
- Overtime/shootout results: 18 ordinary and 16 four-pad cases, including
  playoff progression, alternating shooters, normal stats and checksums.
- Four-pad goalkeeper: **80** native adapter cases (both sides, all 1..4 counts,
  each requester, one-frame taps, held/mashing, priority, Y and no stealing).
- Ordinary goalie requests: **41**, with short taps spanning two frontend
  frames; both sides, CPU/manual/auto, catches and recovery.
- Targeted native forum instruction paths: **682** cases. 256 friction flags,
  82 skater/goalie timing inputs, 256 penalty award/setup flags, two offensive
  assignments, both actual receiver scans and 84 moving-target passes. The old
  release reproduces the stale friction, penalty/awareness/receiver problems.
  Worst short-pass speed **31,675 -> 12,287**; goalkeeper timing matches exactly.
- Shot HUD/replay: 8 cases / 162 VRAM checks.
- Four-pad lifecycles: 7 two-match runs / 14 games / 14 cold boots, with 3/4
  players, movement, pause/resume and menu circles. The natural two-game Mini
  run has 21 keeper requests, 21 passes and 15 observed receptions.
- A separate complete all-CPU 5-minute-period Mini game ends **1-2**, native
  season/keeper/scorer totals and checksum validated (cpu/checks.json).
- Real cold boot shows first picture -> credits -> menu; Start skip tested.
  Same sources rebuild byte-for-byte; 18,950 changed bytes stay within guarded
  hooks/card/code/title/checksum regions. No test probes in production. IPS
  against the original unmodified v5.1W reconstructs the final ROM exactly.

Reproduction: use run_final_regression.py in the private evidence folder, or the
published verify_*.py commands with explicit private seeds/captures. The targeted
runner is public: `verify_forum_fixes.py ROM /private/output [--baseline]`.

### Test-harness findings, not silently counted as passes

The old trail checker paired frontend samples across a possible mid-draw VBlank;
one sample failed after the new hook altered timing. The new private probe at
$2EF800 records history/camera and the actual completed sprite count/positions
at the draw boundary. It excludes stale unused sprite slots and incomplete
snapshots, preserving a stronger exact geometry check. Production trail code
is unchanged.

The four-pad observer moved from $2EF000 to $2EF400 because forumfix now occupies
the old scratch address. The velocity driver moved $2D8000 -> $2D9000 for the
credits renderer. The lifecycle reboot fixture now respects player counts saved
since r20 instead of blindly cycling away from the saved count.

Two historical limitations remain visible in raw paired logs: a one-frontend-
frame ordinary-pad tap (`clear-11-coop-p1-d10-hold1`) can miss a native input
update in **both** old and new releases; with two-frame taps all 41 cases pass.
The legacy home-dive transplant (`080-dive-g5-p1-n4`) gets no native catch in
**both** versions. It is not counted among the 80 valid adapter scenarios. This
release does not claim those old timing-sensitive fixtures pass or that every
possible hardware/controller situation has been manually tested.

## Delivery

Fresh basename replaces the preceding current ROM/IPS on the site, in Downloads
and Bodin's existing playlist entry, with source/notes/tools ZIP updated.
Save/state/remap/config/history migrations preserve file hashes. An active local
RetroArch is never terminated: the guarded local migration waits for its normal
exit, then moves the latest save and updates any history written at exit.
