# Hacking guide: NHL 2026 - 94 Edition v5.1W (Genesis)

## Current public website release — 2026-11-04-r2

Exact already-qualified ROM `89734acf17e7412e4b0aef71039cdcd4737231ba2fa801ccaece72f6eb794355` (CRC32 `2C608F83`),472 native/test sources, source-set `58ff79db84a60e54628e7ac96cbf450f226b3d7984de1af8790f7275da4a772d`.128/128 mandatory groups plus13 supplemental groups pass, with the initial127/128 capture-selector failure and complete18-child affected requalification disclosed.768 footer contexts and real opened H32/H40 pictures pass; grey fields, calibrated Edmonton94→99 OVR, replay continuation and player-centre blue-line penalty rules are included.

Website-only publication: no ROM edits, new test matrix, local/Bodin installation, save reset or user RetroArch operation. Downloads/remaps/progress are preserved. The latest-BIN button is centred first;101-word English introduction precedes the retained4:57 film, accurately labelled as a2026-11-03-r2 recording. Manual controller/live-multiplayer acceptance remains. [Current release, rules, source and QA](H32_FOOTER_RELEASE_20261104_R2.md). Earlier dated sections below are history; the former Nov5 wait was explicitly revoked and the full qualification is complete.



## Penaltys50% investigation complete —2026-11-03-r2

Exact released20793fce… unchanged.24 matched simulation cohorts per setting:100%442/442 candidates admitted,50%241/468 (51.5%),227 filtered. Actual committed432 versus236; icing662 versus664. Cohort50% fractions39.1–71.4%,7–33 candidates each; no exact per-match/whistle-halving claim. Current live-input measurement observer E900/HUD collision exposed and old live counts withdrawn. Final exact-ROM host-only read observer validated72 whole emulated-state controls, with typed host-pointer ELF relocation and active clock/target negatives. Actual naturally completed Full/Light/Mini saves retain50% after reboot/lock attempts. No native game-rule fix or new ROM release required. [Full current audit](PENALTIES50_AUDIT_20261103_R2.md).


## Final current checkpoint —2026-11-03-r2 (Beta)

Released locally and publicly: SHA256 `20793fce15e640bf60d31c48e95bd0d284105fc07f8ae01ed08b1a579b37e99f`;
CRC32 `966FE68B`. **128/128 required groups pass on465 exact native/test
source hashes**, with the original failed full batch retained and four entire
affected groups requalified through an audited six-file host-test-only closure.
Twenty independent control/rollback builds, extracted-source exact rebuild and
original-base IPS reconstruction pass. No later website task changes this ROM.

Main-menu speed/sequencer continuity, exact root Games credits URL, eligible
downed-skater Y selection and consistent actual-roster team OVR are qualified.
Quickgame retains four human splits; Allstar additionally offers CPU and black/
red uniforms. Preseason transfers and the restored native presentation remain.

The separate image-to-title audit checks123 original image files/232 old placements,
corrects21 explicit bindings and inventories **239 final placements/128 image URLs**.
Fresh same-ROM captures show actual owned purchases, framed centred menu help,
dollar amounts, keeper possession/arrows, faceoff stars, Overall and a matched
172KMH shot/replay pair. Historical originals/notes/audit bytes are retained;
fixture scopes, current captions and manual limits are explicit.
Final public desktop1440/mobile390/320 browser evidence and image/body/link checks
are recorded in [the completed image audit](IMAGE_TITLE_AUDIT_20261103_R2.md).

The public market guide explicitly explains separate cash transactions, Free
Agents, no automatic player swaps/replacements or CPU recruitment, legal squad/
budget guards and a conditional McDavid/Draisaitl example. This is documentation
of existing behavior; the native transfer economy/rules remain unchanged.

Local installation preserves configs/remaps and138 unrelated saves. Three old
matching progress files were cleared only at this new release, after normal user
frontend exit. All three original runtime histories remain hash-preserved and
are merged into the current item. Legitimate new-version play is preserved.
Bodin delivery remains queued while the known host is offline; no remote install
is claimed. Physical controllers, extended human play/audio preference and live
jdc/Bodin multiplayer remain beta acceptance. The older9:33 film is clearly dated
2026-11-02-r2. Earlier dated sections below are historical snapshots, superseded
by this current checkpoint.


## Main-menu, explicit Y and actual-roster OVR —2026-11-03-r2

Main-menu-only sequencer service preserves complete checksums and continuous
music timing; maximum blocked frames24→10 for scrolling and69→57 for mode changes.
Both final traces match idle clock/channels exactly. A literal restart command
was not reproduced. Startup credits show https://games.jdc.se. Explicit Y admits
an eligible downed human's keeper request without healing or bypassing native
guards. Ordinary team presentations now use the actual roster mean before/after
transfers: fresh Edmonton57, improving trades61 (two sales) or62 (four sales).
Player attributes, OVR-based prices/budgets and season accounting remain unchanged.

**128/128 mandatory groups pass on465 exact native/test source hashes**;20 independent
full controls/rollbacks, extracted-source rebuild and original-base IPS pass.
123 actual native originals are opened, refreshing102 existing current picture IDs.
[Behavior/scope](MAIN_MENU_Y_OVR_20261103_R2.md) ·
[Exact qualification](QA_20261103_R2_LOGS.md) · [Native review](GRAPHICAL_FINAL_20261103_R2.md).
Local/public delivery and opened public desktop/mobile review: pending.
Physical controllers/live multiplayer and subjective feel remain manual beta limits.
Older dated sections below are historical snapshots, superseded by this checkpoint.


## Transfer dollars and framed menu controls —2026-11-03

Transfer prices, budgets, purchases and sales now display dollar amounts with dot
thousands separators: one unchanged internal unit is $1.000. Initial $16.042.000
still exactly funds an OVR99 star plus an OVR79 middle player. The native economy,
rosters and save accounting remain unchanged. Main-menu help is centred inside
the existing native frame style; all 26 settings remain selectable through five
visible rows. QG-V01 was already repaired in r2; fresh reproductions correct the
previous stale unresolved label without changing Stars native code.

**125/125 required groups pass on 444 native/test source hashes**, with all original
failed batches retained and complete affected test-only requalification disclosed.
Fifteen independent controls/rollbacks, exact ZIP-source rebuild and original-base
IPS reconstruction pass. All 102 current website picture IDs use this final ROM;
123 native captures and 18 public desktop/mobile screenshots are opened and pass.
718 public HTTPS checks pass; historical assets are preserved.
The sound tour remains explicitly the authentic r2 recording.

SHA256 `7d1fc931f5a5a4958b1eefcc05f00661b5321a6a1b2404d7563c98a22b8eb262`; CRC32 `61F87B0A`.
[Behavior/scale/Stars erratum](USD_FOOTER_20261103.md) ·
[Exact qualification](QA_20261103_LOGS.md) · [Opened graphics](GRAPHICAL_FINAL_20261103.md).
Local/public delivery: installed and verified. Bodin: offline, authorized queue pending.
Physical USB/phone operation and subjective play feel remain manual.


## Final website and native-sound tour acceptance — 2026-11-02-r2

M24 complete:102 opened native images +36 opened public desktop/mobile pages.
M25 delivered with disclosed limits:9:33/71 native-sound clips, highest homepage
player;9 final public playback screenshots opened at1440/390/320. Actual H264/AAC
playback, nonsilent audio graph and71 chapter links pass. All50 public game-feature
sections +4 groups inventoried; not every edge case is filmed.
[Movie/coverage](../../video/) · [Playback report](VIDEO_FEATURE_TOUR_20261102_R2.md).
ROM remains qualified124/124 on443 sources, unchanged c68333a8422b6e3754350ef2abd646951d85f6e2c2cd0f987dff1f184aa53b17.
Local/site released; Bodin offline queued. No user frontend or new progress touched.
Physical USB/phones, subjective play feel/loudness and explicit unshown cases
remain limitations; inherited compact Stars shootout tie remains documented.


## Transfers, Allstar CPU and presentation —2026-11-02-r2

Preseason Transfers On/Off sits directly below Icing. Native OVR² prices and
budget16042 cover one star(85–99) plus one middle(60–79) at both upper bounds;
valid squads, nonnegative funds, Free Agents, atomic storage and played-season
locks are retained. The original native overall/hot-cold presentation is restored
with current ordinary, transferred and Allstar data. Allstar adds1vsCPU and keeps
four human splits with blackA/redB uniforms; Quickgame offers only four human
splits under the latest explicit policy. Ordinary32club uniforms stay exact.

HUD/menu/default refinements include right-side Momentum bars, framed above-clock
powerplay, clean replay, shared shot/pass charge, KMH/MPH, optional fragile glass,
permanent painted dots/OT rules, consistent readable page help and five native
0:40 beeps. Real Draisaitl line editing is repaired and permanently regression-tested.
The friendly planned-catch guard preserves native movement/physics and general
busy-shot exclusions; no new Hard strength search or win-rate claim.

**124/124 required checks pass** on **443 native/test source hashes**.
The complete same-ROM matrix plus complete affected test-only observer rechecks
and strict host-timeout retry are separately recorded, with original failed
batches retained.14 independent controls/rollbacks, byte-exact source/ZIP rebuild
and original-base IPS reconstruction pass.102 native originals are opened; current
website images replace77 old slots, while32logos/115historical assets are retained.
Local installation and public browser checks are tracked separately; Bodin delivery
requires connection/normal exit. Physical USB/play feel/audio salience stay manual.

SHA256 `c68333a8422b6e3754350ef2abd646951d85f6e2c2cd0f987dff1f184aa53b17`; CRC32 `4B82D65A`.
[Current behavior/pricing](TRANSFERS_REFINEMENTS_20261102_R2.md) ·
[Exact qualification](QA_20261102_R2_LOGS.md) ·
[Opened graphics](GRAPHICAL_FINAL_20261102_R2.md).
M25 native-sound video remains LAST after release and M24 public review.


## Replay HUD, Quickgame CPU option and menu texture — 2026-11-02

Automatic replay retains the measured shot's coloured bar, both speed units and
pre-replay lower-left clock/frame. REPLAY uses its own native VCR pattern bank,
so the speed text no longer corrupts the heading. Power-play captions retain
ownership of their clock interior, including during a temporary native redraw.

The mode is displayed as **Quickgame**. Its four human splits remain, alongside
**1 vs CPU** (P1 home; entirely CPU away), using ordinary pads or the adapter.
Allstar retains its four human-only splits and black A/red B uniforms. Games,
OT/shootouts and returning to an existing season retain native results/progress.

The supplied image 78 is used only for the menu background. The NHL94/Season
Edition startup picture and routine remain byte-identical, with matching real
startup screenshots. Native club/logo/font/panel palettes remain in use. P3 now uses bright neon
green; P1 blue, P2 red, P4 yellow and the red goal retain their entries. The
fixed green entry is shared with a few small crowd/background details, which
also brighten. Ordinary club and Allstar jersey palettes remain unchanged.

All **112 mandatory suites pass** on this exact ROM and **353 hashed sources**. The whole matrix ran freshly. Nine independent builds, exact R7 rollback, the pre-neon control, six matched controls and original-base IPS reconstruction pass. Original AutoReplayOn scenes: **38/40 raw-exact**; every partial native decoder sample is recorded and proven against the same packed record. Separate declared AutoReplayOff scenes: **40/40 raw-exact**. Ten active negative controls reject invalid changes. Physical USB/four-person play feel remains manual. No new Hard win-rate claim. Preseason transfers and the original season overall/hot-cold screen remain ordered next work.

ROM SHA256: <code style="overflow-wrap:anywhere">b29af4d9300da2f4d989e9537a0c94f2da43f701fdb6054f4c5b9ce5040fc3d8</code>; CRC32: `887FFBD3`.
See [REPLAY_QUICK_MENU_20261102.md](REPLAY_QUICK_MENU_20261102.md),
[exact QA](QA_STATUS.json), and [opened native images](../../audits/replay-quickgame-menu-20261102/).


## Native Allstar teams — 2026-11-01-r7

**ALLSTAR MODE** uses the same human-only 1v1, 1v2, 1v3 and 2v2 flow as Quickgame.
Two virtual teams contain 52 unique players selected by the actual native
zero-hot/cold overall calculation: best 6 G, 16 D, 10 C, 10 LW and 10 RW. A,B,B,A snake
allocation within each natural position gives each team 3 G, 8 D, 5 C, 5 LW and 5 RW,
with 8 valid native lines. Base attributes, names, portraits and critical-shot
ratings follow the real selected players. Ordinary 32 club records stay intact.

Allstar A/B replace inherited city-title bitmaps. Team overall is the floor of the mean of all 26 actual native zero-hot/cold player overall values (82/82); ordinary club ratings remain unchanged. Allstar A wears all-black uniforms and Allstar B wears all-red uniforms, in both native uniform banks. Original rink/centre-logo artwork remains. Native AI teammates fill
other positions; there is no entirely CPU opponent option. All four human
splits, B/Y control and actual pad rotation in tied-OT shootouts are checked.

All **106 mandatory suites pass** on this exact ROM and **339 hashed sources**. The entire matrix executed freshly. Independent rebuild, exact R6 rollback, six fresh matched controls and original-base IPS reconstruction pass. **36/40** ordinary 600-frame scenes are raw-exact to fresh R6; every difference and the complete older comparison chain remain recorded. No new Hard win-rate claim. Physical USB hardware and subjective play feel remain manual tests.
No new Hard win-rate claim. Transfers and the original overall/hot-cold screen
remain dependent work, not implemented by this release.

ROM SHA256:<code style="overflow-wrap:anywhere">6e66c0afe2496eb3f6a688c29888ca62b17aee0b0f51efec96d11dce5e842b31</code>; CRC32:`9F6EBE75`.
See [ALLSTAR_R7.md](ALLSTAR_R7.md), [exact QA](QA_STATUS.json), and
[opened native screenshots](../../audits/allstar-r7-20261101/).


## Human-versus-human Quickgame — 2026-11-01-r6

Native **QUICKGAME** offers exactly **1 vs 1,1 vs 2,1 vs 3,2 vs 2** human
controllers. P1 is home; P2 is away in1v1; P2–P3 away in1v2; P2–P4 away in1v3;
P1–P2 home/P3–P4 away in2v2. No CPU-only opponent option is offered. Native AI
teammates fill the other positions. A four-player adapter is required for3/4pads;
without it the menu/actual sides use1v1. Modern Y keeper selection and immediate
B switching cover both human teams. Tied-OT shootouts rotate real physical
pads on each side, including P3/P4, preserving native attempt/keeper/score rules.

Quick pregame, pause and result menus exclude Simulate Match, Abandon Season,
Season Standings and Scoring Leaders. Existing season records are preserved by
Quick matches and by return to Full/Light/Mini; this differs from the mandatory
fresh-progress reset performed when installing any new ROM release.

All **103 mandatory suites pass** on this exact ROM and **333 hashed sources**. Full matrix plus an independently repeated maintenance-report aggregation correction; unchanged checks retain their exact executed source bytes. Independent rebuild, exact R5 rollback, six fresh matched controls and original-base IPS reconstruction pass. Eight of40 ordinary600-frame scenes are raw-exact to fresh R5; every difference and the complete older comparison chain are retained. No new Hard win-rate claim.
ROM SHA256:`49c8b23b940a9c5368580dfcad11626a3ffbd4a51cdcae10ca8c9ce01f642487`;
CRC32:`F137D497`. See [QUICKGAME_R6.md](QUICKGAME_R6.md),
[exact current QA](QA_STATUS.json) and the
[dated native screenshot gallery](../../audits/quickgame-r6-20261101/).


## Momentum, lower-left shot meter and native positioning — 2026-11-01-r5

**Momentum: On/Off**, default On, controls the existing earned up-to-±10% boost.
Off removes scaling, consumes events and hides bars; Easy100/20/full energy is
independent. The dual-unit shot meter replaces the lower-left NHL badge, with
an automatic power-play-caption fallback. Native receiving and Hard defensive
arrival now retain actual tracking and braking without writing motion/results.

All **100 required suites pass** on this exact image and 325 hashed test/build
sources. The formerly retained receiver geometry check passes. Independent
rebuild, exact Nov1r4 rollback and original-base IPS reconstruction pass.
7/40 ordinary 600-frame scenes are raw-exact to fresh r4 controls; every
difference and the complete older comparison chain remain recorded. No new
Hard win-rate series. Earlier 90/100, 95/100 and 99/100 failures are retained.

See [MOMENTUM_LEFT_R5.md](MOMENTUM_LEFT_R5.md),
[MOMENTUM_LEFT_RESULT.json](MOMENTUM_LEFT_RESULT.json), and the additional
[request-by-request audit](FINAL_AUDIT_R5.md). Human keeper-outlet confirmation,
retained-variant preferences and unreachable Bodin delivery remain explicit.
ROM SHA256: `db244dd005f960a9923c44f0dc4168fb938af227a5e9aa97f24d376fd295291e`; CRC32: `99166AFE`.

## Optional painted-dot faceoffs — 2026-11-01-r4

Independent start-menu **Faceoff Dots: Off/On**, default Off. On maps the final
native stoppage point to the nearest painted point within its native region.
Off retains the inherited free neutral-zone point policy. Native formations,
puck placement, line selection, camera and renderer remain responsible.
Native and Full/Light/Mini save separately and seasons lock after match one.

The whole-formation displacement is reproduced on exact unmodified old/current
ROMs after a declared native queued minor: point(-14,-12), centres(-14,-27) and
(-14,3). On gives point(0,0), centres(0,-15) and(0,15). This matches the reported
appearance; the exact original incident history is not authenticated. Earlier
centre-only observers excluded displaced points. This is an optional coordinate
policy, not a claim of full NHL faceoff-location rules or a renderer repair.

All 97 suites execute:96 pass; retained finisher_hold remains failed with current
measurements disclosed. Strict gate FAILED. New geometry/ABI/save/menu/native
foul/lifetime tests pass. Independent rebuild, exact Nov1 r3 rollback, five matched
AI controls and original-base IPS reconstruction pass. 38/40 ordinary scenes
are raw-exact to fresh Nov1 r3; every difference and older comparison is retained.
See FACEOFF_DOTS_R4.md and FACEOFF_DOTS_RESULT.json.

ROM SHA256:c32a79e34127757b52cd488c124c9247bf7b875088bac89956a6b7ca2b132cf6; CRC32:AD52D7BA.

## Optional Hard AI shot planning — 2026-11-01-r3

Independent start-menu **AI Shot Plan: Off/On**, default Off. On lets a quiet
Hard CPU carrier defer a covered middle-distance ordinary shot, preserving
native passing and using two lateral approach targets when space permits.
Real keeper openings, close finishes, useful long shots and already buffered
one-timers retain native decisions. Pressure keeps native steering/evasion;
after 96 actual carrier-target observations the deferral ends. Native movement,
attributes, shot/pass physics, ownership and results remain responsible for play.

Native and Full/Light/Mini preferences save separately and lock after match one.
Normal/Easy and every active human pad are guarded. The accepted38-win base
remains complete; this optional experiment makes no new win-rate claim.

All 96 required suites execute:95 pass; the retained receiver-hold test remains
failed, with changed measurements explicitly recorded. Strict gate FAILED.
The original complete matrix and its Easy test-observer failure are preserved;
the entire Easy suite passes after correcting its loaded-actor packet predicate,
with256 native observer ABI cases and an active bad-skill negative control.
No cartridge byte changes for that correction. Independent rebuild, exact Nov1 r2
rollback, five matched AI controls and original-base IPS reconstruction pass.
21/40 ordinary scenes are raw-exact to fresh Nov1 r2; every difference and the
entire earlier chain remain recorded. See AI_SHOT_PLAN_R3.md and
AI_SHOT_PLAN_RESULT.json for behavior, all hooks, tests and limits.

ROM SHA256:0b4b104cf31e3b78d5f686349bbaad73ed54ba491c23c6ac661aca02eca396ca; CRC32:BD66F658.

## Optional overtime shootout roster and spin rules — 2026-11-01-r2

Independent start-menu **OT SO Rules: Off/On**, default Off. On rotates the
available native skaters before reuse, excluding every actual goalkeeper
and native injury states. Both clubs may reuse when the smaller eligible pool
has shot. Actual native roster/attribute loading, attempt advancement, goals,
keeper paths and single-miss accounting remain responsible for the result.
A full signed body rotation before the shot ends the attempt. Facing uses its
complete native16.16 angle, so a nearly complete spin remains legal.

This applies to the tied-overtime extension; native standalone Arcade Shootout
keeps its original roster behavior. Native play and Full/Light/Mini save
separately; seasons lock after match one. Existing auto-start, progress,
single-shot and controller behavior remain independent.

All 95 required suites execute:94 pass; only the inherited receiver-hold
geometry failure remains. Strict gate FAILED. 4 complete affected suites
were requalified after scope/navigation test corrections; the first full matrix
and every original failure remain retained. Independent rebuild, exactNov1
rollback, five matched AI controls and original-base IPS reconstruction pass.
40/40 ordinary scenes are raw-exact to fresh Nov1; every earlier comparison
and retained difference remains recorded. No Hard win-rate search.
See STRICT_SO_R2.md and STRICT_SO_RESULT.json.

ROM SHA256:984854649bb9528f9dfc2200d43237cb2a3ab2cbe5cc847fde1ef10d375e660f; CRC32:95D98D33.

## Optional alternative goalkeepers — 2026-11-01

Independent start-menu **Alt Goalies: Off/On**, default Off, implements an
experimental reach21 and catch/rebound model from floating#217606. On keeps
native keeper animations, ownership, saves and outlets; real released shots
may produce cornerward, short/long rinkward or slow/fast goalward rebounds.
Distance and screening change the explicit experimental outcome distribution.
Rebound speed is conservatively capped by incoming horizontal speed. A same-shot
rebound entering the net counts one native goal, one SOG and one keeper SA.
Native play and Full/Light/Mini save separately; seasons lock after match one.

All94 required suites execute:93 pass; only the inherited receiver-hold
geometry failure remains, so the strict gate is FAILED. One complete pass-mode
suite was requalified on the sameROM after extending its actual-input window;
the first full matrix and its P3 coverage failure remain retained. Independent rebuild,
exact r11 rollback, matched AI controls and original-base IPS reconstruction
pass. 19/40 ordinary scenes are raw-exact to fresh r11 controls; every
difference and earlier parent chain remain recorded. No Hard win-rate search.
See ALT_GOALIES_20261101.md and ALT_GOALIES_RESULT.json.

ROM SHA256:331a12164cd1102d4dbbaecea21d7ea12d7f710b79d9c6eaa971cb7d31bc1840; CRC32:C1A1B3B5.

## Optional pass pickup delay — 2026-10-31-r11

The start menu now has an independent **Pickup Delay: Off/On**, default Off.
On adds a minimum six native updates of contact cooldown to active skaters on
both teams when an ordinary human B pass is actually released. During this
short interval skaters cannot take stick possession or make body deflections.
Goalkeepers, boards and nets retain their native response. Existing longer
cooldowns stay intact; CPU passes and requested CPU keeper outlets stay native.
This is an experimental passing choice, separate from all other pass settings.
It saves separately for native play and each season and locks after match one.

All 93 suites execute: 92 pass; the inherited receiver-hold geometry failure
remains failed. Strict gate: FAILED. Four affected complete suites were
requalified on this unchanged ROM after observer corrections; the first complete
matrix and every original failure remain recorded. Rebuild, exact r10 rollback, matched AI
controls and original-base IPS reconstruction pass. All 40 ordinary scenes are
freshly compared with r10: 40 are raw-exact; every difference and the earlier
comparison chain remain recorded. No Hard win-rate search resumes.
See PICKUP_DELAY_R11.md and PICKUP_DELAY_RESULT.json.

ROM SHA256: 3d1f54ae96b0308163474b8c138be9ed880fa96d2213e871a8e77e71bdbcccc5; CRC32: E07B3785.

## Hold/release passing and corrected controller stars — 2026-10-31-r10

Start-menu Pass Charge Off/On, default Off. With On, ordinary owned human B
passes wait for release. Holding increases native pass power from25%to100%,
capped after30 owning input updates; the cap never auto-releases. Aim can change
while held. Assisted/Directional, both teams, P1–P4 and manual keepers are
covered. CPU keeper requests and B switching without puck stay immediate.
Height/Speed/Cone/Mode/Penaltys remain independent. Native and each season save
separate charge records; the first-match season lock still applies.

Physical stars: **P1blue, P2red, P3green, P4yellow**. This is jdc's final mapping,
superseding the earlier P2green/P3red request. The original red goal is restored.
All fixed rink palette bytes and the native rendering instructions are original;
P3 uses its own identical-shape green art in proven low-ROM padding. Its signed
native tile key is validated through actual cache/DMA/VRAM/SAT rendering.

All92 suites execute;91pass and the retained finisher_hold geometry failure
remains failed. Strict gate:FAILED. Six complete affected suites were
requalified on the same ROM after test-only record, observation and report
corrections; every original functional criterion stays covered. Fresh rebuild,
exactr9rollback, matched AI controls and original-base IPS reconstruction pass.
All40 ordinary scenes are freshly compared with r9;40are raw-exact, with
all earlier r8-and-prior comparisons retained independently. No Hard win-rate
search resumes. See PASS_CHARGE_COLORS_R10.md and PASS_CHARGE_COLORS_RESULT.json.

ROM SHA256:5e3d4aedcf006f2c92dacdae965f79b15dfd078fae6a0862f77b6520a60d1eee; CRC32:0301E4D0.

## Easy assistance and distinct controller markers — 2026-10-31-r9

Easy gives the entire human club maximum skills (100-equivalent), the CPU-only
opposition fixed20-equivalent skills, and refills all26 human roster energies
to4096 every game frame. Keepers, AI teammates, co-op and native roster
substitutions are covered. Native energy/physics routines are unchanged.
In-game skill/Overall columns actually draw100/20, bypassing the native low-
rating lift only in Easy. Weight/jersey/handedness retain their native identity;
awareness uses the correct inverted timers. Normal/Hard, Crit Off, defaults and
season settings locks retain their established behavior.

P2 green, P3 red, P4 yellow; P1 retains its original appearance. Static star art,
source indices and three fixed rink palette entries change. No runtime drawing
hook or new RAM/SRAM. All869 native sprite frames retain their shape; only the
three requested marker frames change indexed artwork/attributes. Team uniform
and palette0 rink-line colors are untouched; edge pixels sharing those three
palette1 entries take their new hues.

All91 exact-source suites execute:90pass, only the retained finisher_hold
geometry failure. The strict release gate remains FAILED. All original positive
criteria pass on the final image. The first complete91matrix was rejected for
7new failures; its exact ROM/source/results remain recorded. Rebuild, exactr8
rollback, matched AI controls and original-base IPS reconstruction pass.
All40 ordinary scenes are recorded against fresh r8 controls;33 are raw-
exact and every difference plus the complete earlier audit chain is retained.
No Hard win-rate search resumes. See EASY_COLORS_R9.md, EASY_COLORS_RESULT.json
and QA_STATUS.json for precise field scales, native tests and limitations.

ROM SHA256:443c96a23923e96529120cd19c486db1483101896787c76a8d2082e5836e6186; CRC32:9108C83B.

## Separate penalty frequency and preserved native rules — 2026-10-31-r8

The new final start-menu row is Penaltys: 100% / 50% / Off, default 100%. The original
combined Penalties option is renamed Rules and keeps Off/On/On except offside.
The percentage affects ordinary foul admission and whole new fighting incidents;
icing, offside and penalty-shot eligibility retain their existing controls.
A suppressed injury foul uses the native injury-only stoppage instead of losing
its report. Existing pending calls are not rerolled or erased.

All five independent percentage/pass choices save per season and lock after the
first match; native play has separate preferences. No extra SRAM bytes are used.
Buffered human contact-to-shot setup now runs as one protected transition, so an
intervening native sprite update cannot change the measured stick hotspot. The
old42px failure is reproduced independently; native register/CCR/IRQ-mask/stack
contracts are compared at every IRQ level. All 89 exact-source suites execute: 88 pass, only the retained finisher_hold geometry
failure, strict gate FAILED. Independent rebuild, exact r7 rollback and original-
base IPS reconstruction pass. See PENALTYS_R8.md and PENALTYS_RESULT.json.

All 40 ordinary 600-frame scenes are recorded against fresh exact r7 controls;37 are raw-exact. Every difference and the complete earlier audit chain remain retained. No Hard win-rate investigation resumes.

ROM SHA256:501da8aaf8fc1fb4f5fdd22f4d8f8d893a8fe06de9ec28cb4ecaf0a6e18297eb; CRC32:F2C8334E.

## Optional directional passing and vector targeting cone — 2026-10-31-r7

Two new start-menu rows: Pass Mode Assisted/Directional (default Assisted), and
Target Cone Default/15 degrees/30 degrees/45 degrees (default Default).
Directional sends ordinary human B passes along the actual eight-direction aim,
without selecting a receiver or transferring control at release. The explicit
cone choices limit Assisted receivers using actual vector half-angles; Default
retains the previous native octant policy. P1–P4 and manual keepers are covered;
CPU keeper outlet requests retain their requested receiver. Height and Speed
remain independent. Seasons save all four choices and lock after the first game;
standalone preferences are separate. Hold/release charging remains research.

The first shared-path prototype reduced AI finish releases in the gameplay test
and was rejected. The final design hooks human B input and existing human-only
scan branches, preserving the CPU pass/scan/outlet instruction paths exactly.
All 40 ordinary 600-frame scenes match fresh r6 controls byte-for-byte; the earlier
comparison chain is retained. This is bounded regression evidence, not a claim
that every possible game is identical. No Hard win-rate investigation resumes.

All 88 shared suites execute: 87 pass and the retained receiver-hold geometry check
fails, so the strict release gate remains FAILED. No new regression is waived.
Independent rebuild, exact r6 rollback and original-base IPS reconstruction pass.
See PASS_MODE_R7.md and PASS_MODE_RESULT.json for code, tests and delivery.

ROM SHA256: 8172a7e4b3d55754076d4f31de2def263d423bad832b2057538fa3b5c1ca86c7; CRC32: 5DC93C86.

## Fair automatic co-op pickup control — 2026-10-31-r6

When a free CPU skater on your team takes possession, eligible active human pads
now share a random chance to control that skater. Native P1 priority no longer
wins every such co-op pickup. Only actual accepted pickups are considered.
Already controlled receivers, requested keeper outlets, manual keepers, pending Y,
locked skaters, single-human teams and special modes retain their native paths.
Other pads keep their existing skaters. There is no new menu setting here.

The hook at1538A calls a bounded helper at2EFC00. It uses the real controller-side
map, native RNG and native C0F8 handoff, with temporary eligibility data on the
stack. No RAM/SRAM field, forced pickup, rating or movement change is added.
Native tests cover316 assignment contexts,36 unchanged guards,112 paired ABI cases,
32 full collision contexts and48 physical-pad input branches.24 advancing-RNG series
exercise 144000 controlled native pickup draws, exactly matching the native RNG
control. These are automated fixtures, not 144000 matches or a hardware playtest.

All 87 shared suites execute;86 pass and the retained finisher_hold geometry failure
remains, so the strict gate is FAILED. All 40 current ordinary scenes and fresh r5
controls are recorded, including timing differences. Rebuild, exact r5 rollback
and original-base IPS reconstruction pass. No new Hard-strength claim.
See FAIR_PICKUP_R6.md and FAIR_PICKUP_RESULT.json.

ROM SHA256: 548615ad01079d6a93fc62a0409b4a8d6ec4e53676f8a629081f8cc34da712fa; CRC32: 26911EBB.

## Independent pass speed and safe standalone preferences — 2026-10-31-r5

The last main-menu row now offers Pass Speed: Normal / Slow, default Normal.
Slow reruns the native intercept calculation at80%of nominal pass power, keeping
receiver movement, native time bounds and catch timers in the calculation.
It never chooses an earlier arrival. Fast receivers outside that lower-power
range retain their native pass. This is not an exact20%reduction for every pass.
Pass Height Normal/Low remains independent, using the original arrival estimate
and preserving native fractional lift. Both teams and keeper targeted B passes
share these settings; A flips, C shots and blind-pass code retain their paths.

Each season saves both choices and locks them after the first completed match.
Standalone play has separate preferences. A paired native test also exposed
r4's standalone height record at2E20overlapping Full-season goalie data. Both
standalone preferences now use4D20in Light's excluded shared-profile gap; menu
writes preserve Full goalie bytes and survive slot circles and cold boots.

Native tests cover120direct clamp ABI cases,1152Normal/invalid full solver ABI
comparisons,384Slow comparisons against the original native lower-power solver,
34real menu/lifecycle cases, paired r4/current goalie-storage reproduction and
four co-op input games covering both speeds and heights. All86shared suites run;
85pass and the retained finisher_hold geometry failure remains, strict gate
FAILED. All40current ordinary scenes and fresh r4controls are recorded; additional
instructions may change native HV-counter decisions. No unchanged-gameplay,
pass-completion or new Hard-strength claim. Rebuild, exactr4rollback and original-
base IPS reconstruction pass. See PASS_SPEED_R5.md and PASS_SPEED_RESULT.json.

ROM SHA256:40f388dc0beaedbd0883fdd4101a30debfb0fbbe62aab9d7a382e61476d513e0; CRC32:F82519B6.

## Optional pass height — 2026-10-31-r4

The main menu now has Pass Height: Normal / Low, default Normal. Low lowers the
vertical lift of targeted B passes on both teams, including keeper passes.
Short passes retain their existing lift. The native horizontal velocity,
arrival-time estimate and receiver intercept arithmetic remain unchanged.
A flips, C shots and blind passes retain their code. Lower lift can change
landing and interception; no pass-completion or competitive balance claim.

Full, Light and Mini save their own setting, which locks after the first
completed game and survives a reboot. Native exhibition/playoffs have a separate
preference. Missing/corrupt records default Normal without unlocking a season.
Confirmed season restart resets only that season to Normal.

Native tests cover80paired register/CCR/stack/fraction contexts,64moving-target
contexts across5profiles,28controller/menu/slot/boot/lock/restart cases, and real
co-op directional/B input with both Normal and Low passes above the lower cap.
The full85-suite exact-source matrix has84pass and the retained finisher_hold
geometry failure; the strict gate remains FAILED. Forty ordinary native scenes
and fresh r3 controls are recorded; added dispatch timing may change HV-based
decisions, so no identical-gameplay claim. The earlier strict chain is retained.
Independent rebuild, exact r3rollback and original-base IPS reconstruction pass.
No Hard win-rate investigation is resumed. See PASS_HEIGHT_R4.md and
PASS_HEIGHT_RESULT.json for the exact evidence, scope and delivery status.

ROM SHA256:0f7f080a30b52e536d7107ffc75044c21c1c617aa5d0007ad6203349604e2a69; CRC32:4B4E78D6.

## Downed pass receivers — 2026-10-31-r3

Falling/downed skaters are no longer selected by the native B-pass search or
keeper fallback, and cannot take stick possession while the fall/recovery
animation remains active. A receiver knocked down after launch is protected too.
The puck can still hit the body and deflect. Standing windups/one-timers, keeper
dives/catches and skaters who have recovered retain their native eligibility.

Two pass-scan eligibility tests and the existing actual stick-contact gate call
one small helper. No general frame/animation hook, RAM field, forced movement,
physics, rating or RNG write is added. The original pickup/crit gate and native
body response remain. Additional instructions can alter native HV-counter timing,
so ordinary game trajectories are recorded rather than claimed byte-identical.

The paired native tests cover84CCR/register/stack contexts,114real receiver
searches and44full stick/body collision cases. A native co-op input test injects
only a synthetic receiver fall after an actual pass: the parent shows6downed
possession episodes (579observations); current has zero. Recovery and subsequent
legal possession occur naturally. These are not physical-controller recordings.

All84shared suites execute:83pass and the retained finisher_hold geometry failure;
the strict gate remains FAILED. All40current ordinary scenes and fresh exact
r2controls are recorded, including every difference. The complete earlier strict
comparison chain remains on those parent controls. Rebuild, exact r2 rollback
and original-base IPS reconstruction pass. No Hard win-rate search is resumed.
See DOWNED_RECEIVER_R3.md, DOWNED_RECEIVER_RESULT.json and QA_STATUS.json.

ROM SHA256:40b3a5700ae7c3ce6f0536b878a22b2e7284a8e418231ed6a6ea4ce891fb3548; CRC32:16DD6925.

## Shootout puck progress and automatic start — 2026-10-31-r2

Shootout attempts now start without a controller shortcut. The tied-OT fixture
reaches the first live human attempt with no input in180frames (about3seconds),
previously619frames. A player may react before starting the approach; the normal
attempt timeout still applies. Human and CPU countdowns both use60native ticks.

Before a shot, the puck must keep moving toward the opponent's goal. Substantial
retracement, a stopped approach or crossing the goal line outside the mouth ends
the attempt through the existing native miss event. Body orientation alone is
not a foul: an unshot loose puck still moving forward may be recovered. Stick
movement uses an8-world-unit retracement allowance and12non-forward native
observations of stop tolerance. These are game tolerances, not literal NHL units.

Native shot/windup, loose-shot retouch, keeper ownership and shot-flight handling
remain. The native post-shot retouch queues one ending; no general pickup or
physics hook was added. This is an attempt-control fix, not complete NHL roster,
spinorama or eligibility parity. Custom OT lengths and season formats remain.

All83 exact-source suites execute:82pass and the identical inherited
finisher_hold geometry failure; strict gate remains FAILED.54paired native
boundary/ABI contexts,28both-end native-input cases and the no-input starts pass.
All40 ordinary raw scenes match40fresh2026-10-31 controls exactly. Earlier net
impact differences versus r13 remain independently audited. Rebuild, exact
2026-10-31 rollback and original-base IPS reconstruction pass. No Hard search.
See SHOOTOUT_RULES_R2.md, SHOOTOUT_RULES_RESULT.json and QA_STATUS.json.

ROM SHA256:dc279e63f35b2607ebba4eb5fb439eeb47f36239bddcfaae7c3d24e29f99cb08; CRC32:90081498.

## Less sensitive goal nets — 2026-10-31

Goal nets now require a50%higher impact-speed component before the existing
native dislodgement can trigger:8192→12288 in either direction on either axis.
Ordinary collisions keep the solid-net response; a heavy burst can still move
it. Both ends and every skater/keeper use the same rule. Four threshold immediate
bytes change; instruction timing, random gate, puck proximity, native release
velocity, stoppage, scoring and reset code remain exact.

A genuine CPU-play sequence reproduced one old dislodgement with40moving-net
observations; the corrected sequence has none. A real human burst reaches13868,
above the new threshold, with its input/motion trace exact to r13. The native
helper passes368paired boundary/actor/end/direction/guard/ABI contexts. No user
recording or physical-controller playtest is claimed.

All82 exact-source suites execute:81pass, only the identical inherited
finisher_hold geometry failure. Strict gate remains FAILED. All40 scenes are compared with fresh r13 controls: raw-exact, or exact until
a verified native net release. Post-impact differences are recorded. Rebuild,
exact r13 rollback and original-base IPS reconstruction pass. No new Hard win-rate series. See NET_CONTACT_20261031.md,
NET_CONTACT_RESULT.json and QA_STATUS.json.

ROM SHA256:aa035e55db8b43d1d38f3b178a739cc0edfab1fb8ef7837de718248fde5721f1; CRC32:BF43956E.

## Manually controlled keeper passes — 2026-10-30-r13

A fresh pass press while your manually selected keeper holds the puck now
reaches the native keeper passing routine. The immediate B-switch correction
previously treated an owned keeper as a switch request. Owned skaters retain
their native pass path; without possession B still switches to an eligible
skater nearest the puck. Automatic keeper catches and P1–P4 CPU-outlet requests
retain their established behavior.

Only the early ownership branch in switch.s and fourgoalie.s changes. There is
no new RAM, global hook, pass target override, physics, AI or season change.
Mini remains seven games. Native control may move to a receiver when the pass
is released; this is separate from switching away before executing the pass.

All81 exact-source suites execute:80pass, only the identical inherited
finisher_hold geometry failure. Strict gate remains FAILED. All40 current raw
ordinary-game traces match40 fresh r12 controls. Independent rebuild, exact
r12 rollback and original-base IPS reconstruction pass. No Hard strength series.
See MANUAL_KEEPER_R13.md, MANUAL_KEEPER_RESULT.json and QA_STATUS.json.

ROM SHA256:3070c2bbac4ec9fe6cf578c7fd0a3260869371a232b05ed76f33e44ca71ea899; CRC32:4D62BA8E.

## Mini is seven games — 2026-10-30-r12

Mini now has seven regular-season games: one against each of the seven other
clubs in your division. The new-game menu shows1/7, the saved length is7 and
all32 clubs finish with7GP. The eighth return fixture is no longer scheduled.
Single-player home/away alternation and co-op home games retain their policy.

Qualification and the season-summary boundary use the seventh game. Mini
retains one game per playoff pairing; Full remains best-of-seven and Light
retains its own schedule. Existing played legacy eight-game saves retain their
original length, lock and eighth-game cursor limit. Changed releases still use
fresh progress; an already running older ROM is left alone.

All80 exact-source suites execute:79pass, only the identical inherited
finisher_hold geometry failure. Strict release gate remains FAILED. All40 raw
ordinary-game traces match40fresh r11 controls exactly. Rebuild, exact r11
rollback and original-base IPS reconstruction pass. No new Hard strength series.
See MINI_SEVEN_R12.md, MINI_SEVEN_RESULT.json and QA_STATUS.json.

ROM SHA256:2ca0be4ef69f1d875c5a25e1ef91e42bda6b87396c5471fa8a417eb652ec8d60; CRC32:FB7FA2F0.

## Fouled player keeps the penalty shot — 2026-10-30-r11

P1–P4 now retain the penalty shot awarded for a foul against their controlled
breakaway attacker. Native setup can move the shooter to another object slot;
the physical controller is captured when getBAplayerInfo accepts the award,
then used when the two penalty-shot participants are assigned. Other attacking
pads cannot claim the shooter first. Existing team-side settings are restored
before returning from setup; normal assignment resumes after the attempt.

This release addresses priority1 only. Manual-keeper passing, downed receivers,
fair random unassigned pickup ownership, Penaltys100%/50%/Off and seven-game
Mini remain queued in TODO.md. Mini currently still has eight divisional games.

The native capture/setup, both teams and P1–P4 input/return regressions pass.
The full79-suite exact-source matrix has78pass and the identical inherited
finisher_hold failure; strict gate FAILED. All40 current ordinary-game raw
traces match40 fresh r10 controls exactly. Independent rebuild, exact r10
rollback and original-base IPS reconstruction pass. No new Hard strength
series or39/50 qualification is claimed. See PENALTY_OWNER_R11.md,
PENALTY_OWNER_RESULT.json and QA_STATUS.json for exact evidence and limits.

ROM SHA256:8f84f537ab945ea849ffc69eeba6794e9e1651556d3f878648ed846f8ebc4703; CRC32:41B60F46.

## Human one-timer stick contact — 2026-10-30-r10

jdc reported that receiving a pass while repeatedly shooting launched the puck
far from the stick. Native co-op input tests reproduced buffered releases up to
36.8 game pixels from GetHot's stick hotspot, with the shot animation at0.
The human buffer now waits for actual stick contact (radius4), then starts the
native contact/follow-through phase24. A fresh owned C cannot start a competing
ordinary windup while that request waits, and another pad's C cannot erase it.

The request stays responsive to native movement and possession loss. No puck
teleport or pickup-radius change is used; AI and normal shot physics retain
their code. Mini's divisional schedule, OT, momentum, defaults and locks remain.
See HUMAN_CONTACT_R10.md, HUMAN_CONTACT_RESULT.json and exact QA_STATUS.json.
The inherited finisher_hold geometry limitation remains failed; no new Hard
strength series is claimed. SHA256:b28fa2c8f42bd792f3118bc3d71df2fed5f959957d4adc60f372f2d4863c2038; CRC32:F157EFEB.

## Division-only Mini season — 2026-10-30-r9

Mini now plays eight regular-season games exclusively within the selected club's
own division. Its first seven rounds cover every divisional rival once; round8
is a return against the round4 opponent. All four simulated divisions use the
same eight-team round robin. Every team still plays eight games. Existing
single-player home/away alternation, co-op home fixtures, season locks and NHL
playoff qualification remain. Full and Light retain their league-wide schedules.

jdc reported Edmonton meeting Detroit in Mini. Exact r8 reproduces this from a
saved first-opponent choice; r9 restricts the cursor and actual fixtures to the
Pacific division. See MINI_DIVISION_R9.md, MINI_DIVISION_RESULT.json and current
QA_STATUS.json. The pre-existing receiver-hold geometry limitation is retained;
no new Hard win-rate series is claimed. ROM SHA256:29672052309b0bcad831716cd0edf35b498a265f0063d99173c70dff4df47c3a; CRC32:FD1A8A3A.

## Selected-length overtime and whistle momentum — 2026-10-30-r8

Overtime now uses the selected period length:5,7,10 or20 minutes. Native sudden
death and the existing shootout after a tied overtime remain. Shots/checks while
the clock is stopped, during a faceoff, pause, foreign view, replay or Stars no
longer earn crowd momentum or rating boosts. Blocked counters are consumed
immediately; resuming cannot bank them. Existing earned momentum is retained.
Normal and Hard retain the same earned-meter rules; Easy retains fixed ratings.

Changes are confined to shootout.s and crowd.s, plus release strings/checksum.
Rebuild, exact r7 rollback and original-base IPS reconstruction pass. All shared
checks are freshly run against this image; see OVERTIME_MOMENTUM_R8.md and
QA_STATUS.json for the exact results and retained AI limitation. No new Hard
strength matches are claimed. ROM SHA256:083f45ffbfff3986d55fbe2ea13384916c4c5b4f1d0b6c5e21e39aa80af80e02; CRC32:D813665F.

## Icing On default restored — 2026-10-30-r7

jdc clarified that **Icing must default On**, despite Off in the previous menu
screenshot. Fresh boot, empty Full/Light/Mini formats and explicit restarts now
use On. All other r6 menu defaults and keeper/current-skater outlet behavior stay
unchanged. Manually chosen saved settings and played-season locks still apply.

Only three default immediate bytes change1→0 outside version/checksum fields;
all other game code is byte-identical to r6. The 75 shared checks ran:74pass,
only the known inherited finisher_hold geometry failure; strict gate FAILED.
All40 current raw no-B scenes match fresh r6 controls exactly. The older r6/r5
observation audit remains separately disclosed. Rebuild, exactr6 rollback and
original-base IPS reconstruction pass. See ICING_DEFAULT_R7.md and QA_STATUS.json.
SHA256 48af31f9f7c85766951bf42745360571d6e7bf025bdce936363a58a178ab05f6; CRC32 273334B0.

## Keeper catch handoff and requested defaults — 2026-10-30-r6

A keeper already selected before catching the puck now returns to CPU control
on the accepted catch, and that pad receives an eligible free skater. The native
AI marker follows this ownership change. P1–P4 request an outlet to their current
skater; pass no longer switches that requester first. Explicit Y after the catch,
CPU-only keepers, opposing human teams and special modes retain their rules.

Fresh defaults follow jdc's latest screenshot: Auto lines, Fighting Off, Hard,
Auto Replays Off, Crit Shots Off and Icing Off; Edmonton remains the default club.
The screenshot's Icing Off supersedes the earlier r5 On request in this release.
Existing valid saved choices and played-season locks remain readable.

All75 shared checks ran:74pass,only the inherited finisher_hold geometry failure.
Strict gate remains FAILED. New coverage includes44 native ABI/object guards,
148 four-pad keeper cases (68 added handoff/current-requester cases),5 branches
of the reported r5 catch and6 native default/menu-render cases. All40 current puck/clock/action timelines match fresh controls.35 raw traces
are exact;15 isolated position/cache/render observations across5 traces are
recorded separately with exact whole rows before and after each difference. Rebuild, exactr5 rollback and original-base IPS reconstruction pass.
No new Hard win-rate series. See GOALIE_CATCH_R6.md and QA_STATUS.json.
SHA256 74f099b10459e000b4b50870e41617983641445f83c813328dba019b6b02fd7d; CRC32 25BBB406.

## Icing switch and Edmonton defaults — 2026-10-30-r5

Icing has its own **On / Off** row at the bottom of the start menu, after
Crit Shots. **On is the default**, explicitly confirmed by jdc. Off suppresses
the native touch-icing award. It follows the ordinary season-settings lock
after the first completed match and is saved independently for Full/Light/Mini.
Fresh startup, new empty formats and an explicit restart default to Edmonton;
manual team selection and the identity of an ongoing saved season still work.

All 72 shared checks executed: 71 pass; only the inherited finisher_hold geometry
failure remains. The strict gate remains FAILED. The new suites cover 34 native
menu/persistence/default-team cases and 56 synthetic native rule cases. Forty
current no-B scenes match fresh r4 and r3 controls exactly; all40 fresh r2 controls
still reproduce the immutable original traces. Rebuild, exact r4 rollback and
original-base IPS reconstruction pass. No new Hard win-rate series was run.
See ICING_MENU_R5.md and QA_STATUS.json for scope and exact-source evidence.
SHA256 a7e8bef0e8c78da6d37eacb432769af3f6d4b99e0196e451875c7991656ca182; CRC32 33894753.

## Keeper pass request at the possession deadline — 2026-10-30-r4

A timely pass request could switch the skater yet lose the keeper outlet to the
native possession timeout before it was processed. The first accepted request
now reserves up to four native processing ticks when the counter is still
nonnegative. Holding B does not refresh that reservation. An already queued
native timeout, negative counter or actual whistle retains the native outcome.
On jdc's controller this is physical A, the game's logical B. Y keeps its outlet
behavior. Native passing, target choice, physics, ratings and the r3 keeper
correction remain. No new Hard-versus-Normal strength series was run.

Paired timing tests: 1,792 valid requests per ROM; r3 passed 1,776 and r4 passes
all 1,792. All 128 late/stopped requests remain without a pass. The 1,920 cases
per ROM vary two native catches, timing and 1/2/5/12-frame taps; they are not
independent matches. Shared regression: 38 native ABI/object/guard cases and
108 paired input contexts pass. All 70 matrix checks executed: 69 pass; only the
same inherited finisher_hold geometry failure remains. Strict gate: FAILED.
The affected replay check was rerun after declaring the input modules; all
40 current no-B trajectories exactly match fresh r3 controls, and all 40 fresh
r2 controls still match the immutable original scenes. The original rejected
scope check is retained. Five rebuild/rollback comparisons and original-base
IPS reconstruction pass. See OUTLET_DEADLINE_R4.md and QA_STATUS.json.

SHA256 4dd53440a7a4b4b7920a432b1e70a18e0ddb43e2a57a553b65cd052a0a0dca50;
CRC32 4884D486. Fresh progress for this release; remaps/config/history retained.
The intermittent faceoff alignment report is still open.


## CPU keeper lateral-carry correction — 2026-10-30-r3

CPU keepers follow the actual carried puck instead of the body/stick midpoint.
If an opposing carrier moves across a committed lateral save, its stale final
hold is capped at eight native ticks and its cached movement choice is cleared.
Natural recovery, manual keepers, real loose-shot saves, catches and physics
retain their rules. Crit/B/Stars fixes from r2 remain.

All 69 exact-source checks ran: 68 passed. The inherited receiver-hold positioning check still fails, so the strict release gate remains FAILED. This is the same documented AI limitation retained in the user-selected playtest.
The new keeper suites pass 82 native ABI/object/guard cases and 48 paired live
approaches: r2 10 goals/15 catches, current 4 goals/22 catches. This is not a new
win-rate series. Five independent rebuild/rollback comparisons and original-base
IPS pass; only 219 declared bytes differ from r2, including header/checksum.
All 40 old r2 control traces were freshly reproduced and all 40 current scenes
recorded; current trajectories may differ because keeper behavior changed.
See GOALIE_LATERAL_R3.md and QA_STATUS.json. Fresh progress; remaps/config/history
preserved. The Hard win-rate investigation stays stopped.
SHA256 3453b3fba1a4e3b76c8f859311a499f3f2bf8d9a493280f43ad41eca8b784057; CRC32 A33889B1.


## Current maintenance release — 2026-10-30-r2

Crit Shots locks after the selected season completes match 1; Auto Replays and
other options keep their behavior. Fresh B selects the eligible teammate nearest
the actual puck without skater possession, including own-goalie possession and
old pass animation; Y and own-skater passing retain their rules. A native goalie
outlet goes to that pad's newly selected skater. Stars of the Game keeps its whole
native panel; shot/crowd meters retire before it draws.

The selected 38-win AI base is preserved. All 40 original no-B playbook trajectories
(24,000 frames) match byte-for-byte. New regressions pass; all 67 checks executed:
66 pass and the exact inherited finisher geometry failure remains. Strict QA is
FAILED, as already documented for the chosen playtest. The first draft's new AI
timing regression was rejected; no per-frame guards/padding/RNG forcing remain.
Seven independent build/rollback comparisons and original-base IPS pass.
SHA256 f2a6d2ee40f4ac6c7bfe3277fe1859930fc1c579f1cb15aed742c1798f8fd9a3;
CRC32 AD60AF42. Fresh progress per release; remaps/config/history preserved.
See MAINTENANCE38_R2.md and QA_STATUS.json. The win-rate search stays stopped;
blue-line/one-timer/lateral-deke research is deferred, and human variant tests last.


## Hard cross-ice passing and shot openings (2026-10-30)

User-requested playable test release: Edmonton Hard38/50versus Chicago Normal,
goals132–66,25home each; matchedr3control32/50and immediateparent36/50.
Stable39/50plus independent confirmation remains unfinished; all results retained.
Hard now plans accepted native passes, supports real receivers, checks the
keeper opening on the actual catch and selects only more open native post
targets while retaining height/accuracy/power/physics/ratings. Coverage continues
through real passes. Native scorer cache and keeper attacking/defensive counters
are accounted correctly. See AI_TACTICS.md and HARD_STRENGTH_RESULT.json.

All63 exact-ROM/source checks executed:62pass; finisher_hold fails its live
geometry improvement criterion (slot distance1.90to2.78; early releases2to2).
The strict release gate remains FAILED. This exact38image is provided only as
the interim playtest requested by jdc, with that AI regression retained. Independent current-source rebuild, exact
r3rollback, feature ablations and original-base IPS reconstruction pass. Normal/
Easy/human eligibility and physical-shot contracts pass; two Easy live controls
retain sampled frame-timing differences. No physical-controller session claimed.
SHA256 fdf88b7f1933304dd08917b88e1b2e4805072355c1538ff41d876e6e015688c4; CRC32 853481AF.
Fresh progress each release, controller/remap/config/history retained.


## Crit On/Off value visible on every redraw (2026-10-29-r3)

User reported the Crit Shots label without its On/Off value. Native full value
redraw stopped after logical row20, skipping the new row22. Extend F7B44 from
0014 to0016. Both On and Off now render on initial selection, cold boots and
full redraws, in the same column as Auto Replays. The preference already changed
and persisted in r2; those tests missed the actual initial drawing call.

All40 exact-ROM/source checks pass. New native renderer regression reproduces
r2's missing initial value and verifies8 ordinary/adapter cases,32 observations:
default/saved/corrupt values, Left/Right, row changes, exact strings and column.
Existing43 Crit behavior and7 replay cases, plus full natural Hard CPU game,
also pass. Independent rebuild, exact r2 rollback (--legacy-crit-value-draw)
and original-base IPS reconstruction verified. Only4 ROM bytes differ: two
version-header bytes, checksum byte and native draw-bound byte. No AI, power,
controller behavior, save format or gameplay changes. SHA256
0d196f206294ebf6c8bda449a41aa7cf4f6702585a57cc9ecceaee960915a8db;
CRC32 3493DFFC. Evidence: .workspace/work/crit-render-fix-20261029/.
Normal fresh-progress/remap policy applies after normal exit of an older game.
Hard AI35/50 target remains unfinished; no hold/timing/point prototype included.


## Global Crit Shots On/Off (2026-10-29-r2)

The twelfth main-menu row is Crit Shots: On / Off, default On. This global
preference survives cold boots, Full/Light/Mini slot switches and native modes.
It stays editable during a locked season without unlocking match settings.
Off disables the critical roll, extra power, red puck, flame trail and special
sound for CPU and human shots on every difficulty, including Easy's otherwise
guaranteed critical forehands. Auto Replays is saved independently. Ordinary
shots and the separate older long-range goalie-miss rule retain their behavior.

All39 exact-ROM/source checks pass. The new Crit suite covers43 cases: locked
menu edits, cold boots/slot/native-mode persistence, absent/corrupt independent
records, read-only boots,24 direct native CPU/human/team/difficulty On/Off
cases (10,000 shots each, native SFX-entry instrumentation), and six real
controller Off releases including all four Easy pads. Seven strict replay
preference cases still pass. A timing-dependent season-slot fixture now skips
native other-game highlights with ordinary C inputs while keeping the exact
bracket/password/checksum assertions; no gameplay code was changed for it.

Independent rebuild, exact preceding29 rollback with --no-crit-option and
original-v5.1W IPS reconstruction pass; guarded1,311-byte ROM diff. Current
SHA256 fd9f76a4e29dcbd904b898aaba2018ceb1d4f0eff7e8d46f940435c55981b78f,
CRC32 44FEF889. Full evidence: .workspace/work/crit-option-20261029/.
The separate Hard AI35/50 target remains unfinished. No additional AI prototype
is included. Controller mappings retained; progress clears after normal exit
if a preceding user game is still running.


Workspace policy (jdc, 2026-10-29): the project root is
`/home/jdc/mount/truenas/www/dela.jdc.se/nhl94`. Canonical ROMs live in
`.workspace/`, and all fixtures, experiments and evidence in `.workspace/work/`.
The old `roms/claude` path in historical examples is a compatibility symlink.
Keep the private workspace's HTTP deny rule intact; see WORKFLOW.md.

## Hard CPU coaching, native crash fixes and keeper statistics (2026-10-29)

CPU Hard chooses stronger real keepers and its best first forward unit, with
unchanged player attributes, physics and true fatigue. Normal/Easy AI decisions
retained; human teams protected. Native rewind wrap and powerplay-display team
pointers corrected; reserve/third/multiple keeper season statistics use actual
individual counters. All38 exact-build checks pass, including46 line/46 keeper/
12 accounting/6 rewind/4 PP cases and a full Hard CPU game. Same failing0-2 game
passes with identical goals. Rebuild, exact r9 rollback and original-base IPS
verified. SHA256 b81bbd5510edc8496d9a50cce269dee653067977f6ae05b3ca89cdf3ad20bd8c;
CRC32 B999E29D. See HARD_COACH.md. User requested this playable copy before the
unfinished35/50 strength goal; later experiments and requested Crit toggle
are not included. Fresh progress; controller mappings retained.


## Hard pressure, point shots and catch-and-release combinations (2026-10-28-r9)

Nearest CPU skater pursues even a stationary opposing carrier. Clear blue-line
shots are considered more often; wide/point receivers catch and relay, while
free slot receivers can release a buffered native one-timer on the actual catch.
Hard only; native physics/ratings/controls retained. New transient FFF086..08B,
guarded module2DD000, ai_attack and onetimer integration. See AI_PLAYBOOK.md for
exact scope, native RNG timing effects,32 paired gameplay cases and limits.
SHA256 811c912c42d2aacb6a7326c8131090df71972deb63a597d0298b049c05277cea; CRC32 010305B0.


## Hard defensive pair / earlier pressure (2026-10-28-r8)

Nearer back contains the opposing carry goal-side; partner covers the slot.
Hard only, native skating/checking and human controls retained. New ai_gap.s
590 bytes at2DC000, CF8A hook; no persistent RAM or rating/physics boosts.
44 native contract cases and48 paired live scenarios; Hard mean gap -33.33
units (15/16 improve), 24 rushes show11->7 observed attacker releases and7->6
team goals. Small controlled sample, no win-rate claim. See AI_GAP.md.
SHA256 27248d4097dae5f2af9e5f73d02f1811befad2f034a615f2951bf2ff810813c9; CRC32 33EB70DA.


## Shot charge colour progression (2026-10-28-r7)

White at low charge, native orange through the middle, solid red at maximum;
no full-charge blanking. HUD-only shotmeter.s change; original charge/physics,
replay lifetime, crowd bars and palette data retained. Twelve native pad/mode
colour cases and shared exact-build gate documented in SHOT_COLOUR.md.
SHA256 a0cd85710be2004a89a5c0b3d8c97ff1fd77649c79d4d5fe487eaa7831aabd65; CRC32 47DF3530.


## Intermission, ticker, team control and replay preference (2026-10-28-r6)

Scoring Leaders exits with one Start after automatic replay and before period3;
replay's stale no-redraw latch is consumed at its origin and at leaders exit.
Crowd meters no longer erase native penalty ticker cells (old208 damaged pixels,
new0). A resumed solo away fixture keeps the controller on the chosen club,
preserving One-Visitor across locked-settings reload. New main-menu Auto Replays
On/Off is a global saved preference, default On, editable during a locked season;
manual Instant Replay remains available. Sources/addresses: USER_REPORTS_R6.md.

All28 exact-ROM/source checks pass, including32 native identity starts (16 cases
with two cold boots each), eight leaders exit cases, native ticker pixel checks,
seven preference/manual replay cases, four-pad goalies, shootouts, locks and a
complete Hard CPU game. Exact rebuild; guarded5,283-byte r5 diff; original-base
IPS verified. Scripted native inputs; no new manual hardware test claimed.
SHA256 526ae0fd082774a83ca5f74b87b11e638d298f2a672d2a6974fd57d224339c35; CRC32 1599055D.
Fresh progress on jdc/Bodin; remaps retained. Replaces the same current download
and Bodin playlist item. Private evidence: work/intermission-review-20261028/.

## Hard attack AI, dynamic momentum, Start-only credits (2026-10-28-r5)

Hard forwards now support the carrier with slot/return options, avoiding a
marked preferred point. CPU pass decisions scan for a free receiver in a better
position; poor-angle shots are deferred when one exists. Human controls, native
physics and Normal/Easy decision paths retained. Hard now shares Normal's real
crowd momentum/rating effect, removing the permanent ±10% handicap. Easy bonuses
retained. Credits wait indefinitely for a fresh Start press; no other button or
timeout skips them. Sources/addresses/guard limits: AI_ATTACK.md.

76 paired native cases, 16 live attack scenarios (free-slot passes 3/8->8/8,
receptions 2/8->4/8), 12 crowd profiles/84 observations and both credits ports.
All 24 shared checks pass for the exact ROM/source, including keeper/shootouts,
settings locks and full Hard CPU game. Small controlled samples, no win-rate
claim. Exact rebuild; legacy flags reproduce r4; guarded 2,373-byte diff and
original-base IPS verified. r4 backup: private work/ai-attack-20261028/backup-r4.zip.
SHA256 f2b62c979f44da6635faff9ee5cefc28fc0e684da1e7c8ba55c3362a0338b0b7; CRC32 59269517.
Fresh progress, controller remaps retained; current files and same Bodin item.

## Weak-side AI coverage (2026-10-28-r4)

On Hard, a deep, wide opposing carry makes the off-puck AI winger protect the low slot /
most dangerous far-side receiver. Velocity-based short look-ahead, inside and
goal-side target; compact support if none. Both teams/ends; native strong-side,
humans, goalie, ratings, shots and save format unchanged. Hook D086 -> 482-byte
ai_defence.s at 2D8400; no new RAM/RNG. See AI_DEFENCE.md for scope and credits.

44 paired native target/guard/register cases and 19 paired live skating cases
pass (11 Hard improvements and 8 unchanged Normal/Easy controls; Hard mean coverage distance reduction 26.82 units, range 13.14..34.30).
This is not a win-rate claim. All 20 required shared release checks pass for
this exact ROM/source, including four-pad goalie and full CPU game. Rebuild
identical; --no-ai-defence reproduces exact r3; guarded 483-byte diff and
original-base IPS reconstruction verified. Requested r3 backup ZIP with manifest
is private in work/ai-improvement-20261028/; details in AI_DEFENCE.md.
SHA256 e6c727618a252e962e5d120a7f945c25e2634f2a396382c90766386a2fe81c97;
CRC32 975E5D61. Fresh progress, controller remaps preserved; same Bodin item.

## Strict season settings lock (2026-10-28-r3)

jdc: Continue Mini must never reopen the match settings after a reboot.
Release policy updated in the same session: clear game progress on jdc and
Bodin at EVERY new version; keep controller mappings (starts with r3). Removed
the legacy NOSET/open-row exception; saved choices are restored and remain locked
after game 1. New pre-Start T records preserve Auto line changes before native
D058 normalization; old records/snapshots are validated and recovered, without
unlocking rows. Explicit restart permits new choices. Players stays editable.
Old snapshots lack Fighting: deterministic Off fallback, original value asked
of jdc; no claim to reconstruct missing history. See SETTINGS_LOCK.md.

PASS 36 targeted cases/261 observations, actual first-game result -> cold lock,
and a private copy of jdc's Mini; all 16 existing regression suites, including
four-pad goalie, shootouts, menus/slots/HUD/crit. Exact rebuild and guarded diff,
original-base IPS verified. SHA256
c0bb6213131a4ce2daf763a3bfa06b740fe9a81e8dc252b6e2b32b78e0159738;
CRC32 68A92922. Evidence: work/season-settings-lock-20261028/.


## Forum gameplay fixes and boot credits (2026-10-28-r2)

Five forum gameplay corrections: boxed-player pass selection in both scans,
stale human friction mode, penalty-shot late/ghost puck, offensive awareness,
and the worst short-pass speed truncation. Explicit cached-base guards ensure
all fixes enter the experimental build. The CPU goalie pull was already fixed
in v5.1W; no new authorship claimed. New skippable credits card after the first
picture; forum.nhl94.com / Credits navigation, versioned Download labels and
full source attribution. Brodeur30 follow-up / guarded diving-timer tool added.

PASS all 16 final suites: 682 targeted native cases, 80 adapter goalie + 41
ordinary goalie cases (two-frame short taps), 34 OT/shootout results, 14 matches /
14 cold boots, menus/slots/HUD/crit. Old one-frame input and legacy home-dive
fixture limitations reproduced on the baseline and documented, not counted as
passes. Exact source rebuild, guarded 18,950-byte diff, IPS verified; independent
CPU game 1-2 saved correctly. Full details/commands: FORUM_FIXES.md; attribution:
CREDITS.md; review/reply drafts: FORUM_FOLLOWUP.md. No forum messages posted.
SHA256 8caf93b508276b255e274b59573934c6ce7c09804d17bacec99ca93d22090662;
CRC32 14EDE156. Evidence: work/forum-fixes-20261028/.


## Crit flames follow the moving camera (2026-10-26-r33)

Reproduced Mini/Easy/5min/Fighting Off,EDM-CHI: red puck+bomb sound but
the six flame circles overlapped because HIST stored old screen coordinates.
trail.s now stores camera-independent anchors and projects at draw time;
other views normalize their native puck sprite.20 native crit shots show
median span11->42px;1424/1424 world-path checks pass(previous0/1424).
Same art,VRAM,RAM,6 sprites,80-sprite cap and24-frame afterglow. Crit physics
and goalie bonus unchanged. Critical10,velocity88,flash,panel8/161,navigation
12/228 and full CPU game pass. Exact rebuild,guarded115-byte UI/title/checksum
diff,IPS verified. See [CRITICAL_TRAIL.md](CRITICAL_TRAIL.md).
SHA25607c63ff6583b8edd235f7b2cf22b4584ace23174c7ba1009385a37b115ea62e9;
CRC7BB0CADE. Replaces r32 with save/remap hashes preserved.

## Strong crits and shot balance audit (2026-10-26-r32)

Fixed signed-word overflow above release power481: the strongest crits could
reverse sideways and trigger the native slow-Y fallback. New90-byte
critical_velocity.s at2ED800 uses wide math and signed-component saturation;
low powers retain the native path. Paired14,400 controller shots,3 clubs,all
3 difficulties/4 period lengths: lower-power outcomes unchanged. Requested
Bouchard Easy run32/100; goalie bonus541/3600(15.03%),0/3600 below 160;
Easy2400/2400 actual crits. Matched1,000-shot controls248goals production,
187without goalie bonus,161without crit power. Native boundary88/88(fixes32
previous failures),crit/flash/panel/navigation/menu and full CPU game pass.
Exact rebuild; guarded91-byte diff; IPS verified. See [SHOT_BALANCE_AUDIT.md](SHOT_BALANCE_AUDIT.md).
SHA2568a7304d8fd5708907dced70ea016fffaa8f3096389be18c809f0ebc086423fd9;
CRC92065481. Replaces r31 with saves/remaps preserved.

## Shot panel retained through replay, above the score box (2026-10-26-r31)

The64x40px shot panel now matches the score box and sits immediately above it,
with the charge bar inside. Recorded km/h/mph and charge stay visible throughout
the automatic replay and disappear on return (also on skip). Live bar remains
smooth/red; replay uses native HUD segments so REPLAY keeps its borrowed tiles.
No new pattern memory, palette or DMA. UI full-charge point/sampling now matches
native31; physics/crit/sound and all other game modules remain unchanged.
PASS8 native cases/161 VRAM checks,critical10,flash,navigation14/266,finished9/108,
menu18/240;80 standard goalie cases pass, old home-dive capture fails equally on
r30/r31. Rebuild exact,guarded1099-byte diff,IPS verified. See [SHOT_PANEL.md](SHOT_PANEL.md).
SHA2568e45bb86e1728c5933bbe4614a6a89007459b0a88839570f4415b90cde8c48da;
CRC1A35E68D. Replaces r30 everywhere with save/remap hashes preserved.

## Three/four in original playoffs (2026-10-26-r21)

Original BO1/BO7 and Continue Playoffs now offer Three/Four - Teammates, save
that choice separately from the seasons and retain Two - Head to Head.
PASS10 native result cases/18 cold resumes, independent Full1/Light2/Mini3/
native4 roundtrip/reboot, Continue player edits with unchanged password;
season24/48starts,slots20,nav14/266,font14/3124,goalie88+41 and3 four-pad
season shootout results. Exact rebuild, guarded1691-byte diff, IPS verified.
Source/storage/tests: [NATIVE_PLAYOFF_PLAYERS.md](NATIVE_PLAYOFF_PLAYERS.md).
Replaces r20 everywhere with saves/remaps carried unchanged.
SHA25637feaa90d54806517026d26e67e5fb77ff4992408f3ce26c458f2302f0c38759;
CRCADE85330.

## Saved player counts, including Continue Playoffs (2026-10-26-r20)

Players is now reachable and editable in a season's Continue Playoffs menu.
Choose One/Two/Three/Four - Teammates; each Full/Light/Mini slot remembers its
own count across slot changes and cold boots, in both league and playoffs.
The optional PC/count/complement record at logical SRAM1EF0..1EF3 follows
existing snapshots/checksums; old saves remain compatible. Without an adapter,
a saved Three/Four runs as Two and returns when the adapter is restored.

New seasonplayers.s at2EB000 (622bytes), cursor hookF7782, seasonmenu2002 and
slots1556. Only Players is unlocked; club/results/tree/statistics stay intact.
PASS24 native player-choice cases/48 cold match starts, mixed Full4/Light2/
Mini3 roundtrip and reboot,18 played playoff OT/shootout result/save cases,
goalie88+41,slots20,navigation14/266,finished9/108,club labels18/240,font14
cases/3124 calls/zero wrong fonts. Exact rebuild, guarded3579-byte diff,
original-base IPS230941 bytes verified. See [SEASON_PLAYERS.md](SEASON_PLAYERS.md)
for source, RAM, storage and reproduction. Replaces r19 everywhere with matching
save/state/remap contents preserved.
SHA256a67ecaffe77beb7c13071caee9f507767a4efb7471af3c9ff9487d3b3fb9ae3e;
CRC086E5C8E.

## Four-pad goalie hold, B request and Y control (2026-10-26-r19)

The goalie patch now handles all four physical pads in cooperative 4-Way Play:
AI holds on pickup, B requests a pass to the requester, Y explicitly takes the
keeper. Native P3/P4 reuse D4=0/2 with temporary control banks; requests now
retain physical IDs 0/2/4/6. Short hardware B taps survive the catch/recovery;
first valid request wins, and pause/replay input cannot queue an outlet.
Also fixed: switching from Four into a One-player season could still start
four humans (stale TM), and shootouts left pads 3/4 active with only two actors.
Ordinary two-pad switch/Y code is byte-identical. No SRAM format change.

PASS 88 adapter goalie +41 ordinary goalie cases; 7 two-match lifecycles
(14 games, 14 cold boots, P3/P4 movement, pause/resume, menu circles); a complete
natural two-game Mini run produced 27 keeper passes/17 receptions. PASS 50
OT/shootout cases across ordinary/three/four pads and all playoff formats;
slots20, adapter navigation14/266, completed-season9/108, club labels18/240,
menu font. Exact rebuild, guarded2870-byte diff, original-base IPS verified.
Full source/RAM/test details and the known RAM-only playoff player-count
limitation: [FOUR_PAD_GOALIE.md](FOUR_PAD_GOALIE.md).
User-requested private r18 ZIP backup and hash manifest verified. r19 replaces
r18 locally/site/Bodin's same playlist item, carrying save/state/remap hashes.
SHA2567262f8e4a39d6970b660e24803f1d7212b29aed26a933202296b788aed8d9ec6;
CRC D82B8F78.

## Completed slots open results (2026-10-26)

See Old Full/Light/Mini Season replaces Continue for finished slots. Start
opens saved results; after leaders, view again, start a new selected-slot
season (Xbox X+B confirms, Start cancels), or return. Finished slots retain
the saved club and show Season finished. Fixes fictitious/unrecorded mirror
matches; r30 fixed only the stale Team text. finished.s1104 at2E6000, Start
hook F843E before settings save; seasonmenu1966, results3926 with +8 browser
export and both-pad Start-release wait. No SRAM format change.9 native flows /
108 checks,18/240 menu,12/228 navigation,347 browser pass; source rebuild,
guarded3547-byte diff and IPS verified. Actual user saves unchanged. New dated
2026-10-26 replaces25-r30 locally/site/Bodin, remaps carried. See
FINISHED_SEASONS.md and release.json for full implementation and hashes.


## Completed-season team label (2026-10-25-r30)

Main-menu Team text now follows native Home when the completed season has
FORCED=0; USERW is used only for locked/pregame season selection. This fixes
logos changing while the old name stayed. Active away games retain the user's
club. No new RAM/save field; seasonmenu1894 bytes. 18 native cases / 798 club
checks, 12 navigation cases / 228 checks, exact rebuild and IPS pass. Current
user SRAM stays byte-identical after ordinary menu selection. Statistics code
unchanged. New r30 replaces r29 everywhere, with remaps/saves/states carried.
Full cause, source, evidence and hashes: MENU_TEAM_LABEL.md and release.json.


## Statistics alignment and footer spacing (2026-10-25-r29)

Club skater and goalie headings move one character left to match the values:
column ends23/27/31/35. Leader column ends26/30/34/38 remain correct. Both
screens move headers to row6 and all18 skater rows to7..24, leaving row25
completely free of text above Xbox help26 and footer27. Page2's goalie block
also moves up one row. All players, numeric positions, inputs and saves stay
unchanged. Orange selection now uses row6 C600 and club PTS29/G23/A27;
leaders use +3 columns. seasonend3926 bytes,2E3000..2E3F55,170 bytes below trail.
347 native browser checks pass, including selected glyphs, both planes' empty
spacer row, boundary redraw counters and unchanged SRAM. Fresh production-safe
champion preview, exact rebuild, guarded591-byte UI/title/checksum diff and
original-base IPS reconstruction pass. Source/site/screenshot/docs updated;
r29 replacesr28 locally, Downloads, site and Bodin's same item with saves/remaps
carried. Private evidence:work/stats-spacing-20261025-234630/.
SHA256bd4eb6465d81e72b7a07dcffcee1093719a370eb049307ecf5d100007946bcc7,
CRC8170380A. release.json is authoritative for the current version.


## Xbox sorting-button help (2026-10-25-r28)

Both end-of-season statistics pages now explicitly show
"Sort: Xbox B / Mega Drive C" above the original footer. This is the same
sorting action on the existing six-button remap; the input code is unchanged.
Retains r27's orange G/A/PTS heading and no page-boundary redraw. Module3928
bytes,2E3000..2E3F57,168 bytes below trail.347 native browser checks, fresh
production-safe champion preview, exact rebuild and original-base IPS pass.
Guarded UI/title/checksum diff:1896 bytes vsr27,3215 vsr26. Details and memory
map:STATS_DISPLAY.md. Newr28 replacesr27 locally, Downloads, site and Bodin's
same item; saves/remaps carried.
SHA256990a751d77fe5b7a4b2fdb46c606ce36e9a604ba34ffe6a75e1c3346be505e81,
CRCF96D9E75. release.json is authoritative for the current version.


## Statistics heading colour and page boundaries (2026-10-25-r27)

Selected G/A/PTS is orange in the end-of-season club browser and scoring
leaders. Up at page1 / Down at page2 no longer redraw/fade the same screen.
seasonend3880 bytes at2E3000..2E3F27; cloned glyphs use disabled-window VRAM
B040..B09F, not the live front plane. No SRAM or gameplay change.347 native
browser checks, VRAM glyph/palette assertions and redraw-count regressions
pass; paired capture changes only selected glyphs. Exact rebuild, guarded3170
byte UI/title/checksum diff and IPS verified. Fresh local Dallas champion
preview tested against production code; battery progress/remaps carried.
Replacesr26 locally, Downloads, site and Bodin's same item. STATS_DISPLAY.md
has implementation, memory map and reproduction details.
SHA25668f8f60bd60e9058eb2a9d277ada8672ca5bca8fa66b05dfc23b9a2b368ce7e6,
CRCB7B97FA0. release.json is authoritative for the current version.


Reference for changing the game further. First written 2026-10-20 after the pass fix, goalie passes and blue-line shots.
What each change does and the measurements are in `CHANGELOG.md`. This file is about *how* to make the next change.





## Goalie catch input and all-team end statistics (2026-10-25-r26)

B held across a native goalie catch now reaches the outlet request before the
previous switch's HELD latch. Reproduced r25's skater4/1→0 then goalie5 catch,
with no pass; r26 serves the pending B in One and both Teammates pads. No B still
means hold. switch510/outlet750, no new RAM/save field. After Season Summary,
Left/Right cycles32 teams; A changes combined/league/playoffs tab, C sorts,
Up/Down pages; Start opens leaders, B there returns to the selected club.
seasonend3618 bytes; BTEAM FFE6FC/SCREEN FFE6FE, actual TEAM and all saves intact.
41 goalie cases,682 native browser checks with exact source totals/unchanged
SRAM,14/266 navigation, clean CPU Mini0–1, exact rebuild/guarded3313-byte diff/IPS
pass. Full cause, controls, sources and fixtures:GOALIE_AND_STATS.md.
SHA2564b42e4b6fa9079bb3bdb40376ff473bed4a0ef6116d9df98f5fef123d1047c8a,
CRC4ACB9735. Read release.json for the current version; older hash claims below
refer to their historical snapshots.

## Current release naming policy —2026-10-25

Current **Season - jdc -2026-10-25**, SHA256
2d4d53a50b2e153be6f5ebfe4b12b9e31d6b391d87dc75ecef57dd0cefed3f47,
CRCFEF0867F. The user now requires a fresh name/version for EVERY changed
release, replacing the preceding current file/playlist entry on Bodin, the
website (ROM+IPS) and Downloads. First daily versionYYYY-MM-DD, then-r2/-r3.
Migrate matching saves/states/remaps/configs and histories with content hashes;
do not retain an old filename just to match saves. release.json supplies the
builder default; --version overrides it for historical reproduction. Current
rename changes only the two header title date bytes147/177; gameplay/assets and
save contents are unchanged. See RELEASES.md for locations and complete process.
This policy supersedes historical filename-preservation text below.

## RAM rule (2026-10-26-r3)

$FFF100..$FFF7FF is menu scratch (team stats, leaders, results text). In-game state kept there needs a
magic/validity check and a reset at game start (crowd.s valid clears onetimer/replay/trail state).

## Automatic replay (r25)

`experimental/replay.s` $2E5000 (+0 tick from crowd.s, +4 at $7948 pause call, +8 at $A41E replay pad read).
Pause: main loop $7938 checks $C2EC bit0 -> $7CF8 (prologue, menu loop $7D4C, exit $7D66). Instant Replay = $9FD0.
Replay controls (pad read $A41E, d1 = new presses, d3 = held): d1 bit5 C toggles play ($A25E, $C2F0 bit1); d3 bit6 A held rewinds, d3 bit4 B held steps/slow; d1 bit7 Start opens the options menu, Start again leaves. The buffer is rewound on entry ($A02A). Holding C does NOT play (r4 fix).

## 4-Way Play (r16)

D046 = multitap (hardware: slots.s restore keeps it, r18). Pads: sides C328/C32A/C32C/C32E (0 none, 1 home, 2 away), controlled objects C320..C326. Side assignment $17E42 (exit $17FE0 hooked by mtap.s). TM $FFF07C = 3/4 teammates. r19: fourgoalie.s at2EA600 dispatches adapter B/Y using physical pad IDs; outlet.s/goalie_ai.s now serve all cooperative pads. Ordinary two-pad switch.s/ygoalie.s bytes remain intact. See FOUR_PAD_GOALIE.md.

## Main menu background (r14)

$F8168 menu set-up; background $1F0000 at $F8252 (plane $C000, tiles from 243, palette 3 photo). menubg.s $2EA000 (hook $F8258) repaints it with the $2A0000 intro picture in palette 0 greys (r15) and starts the menu music $79 if none plays. Menu palettes: 0 panels/font (constant), 1 home logo, 2 away logo, 3 photo + menu sprites (constant).

## Shot meter / Easy (r27)

Slapshot charge: anim $7FC/$92E, $5A < $10, BEDE 15..34; release power BEDE = charge x (rating+20) x 0.32 x energy. Puck speed units: |($B772,$B774)|/3842 per frame; 262 units = 27.13 m. shotmeter.s $2ED000 (speed panel = HUD plane $E000 map words, r31 rows18-22 cols32-39, resident frame $3FF-$407 and font $41D-$443; VRAM $FB00 / $3BC / $3EF are not safe for own tiles). DIFF $FFF07A: 0 Normal, 1 Hard, 2 Easy; saved per season slot at logical SRAM $1EF4/$1EF5 (value, ~value; seasonplayers.s load / +52 diffstore, r36).

## Replay overlay (r23)

Native replay VCR box: window plane $F000 rows 2-4 cols 2-9, tiles from B026 (loaded from $BB4F6 by $9FD0). replaytitle.s $2EC000 draws REPLAY there from tile $7E1 (crowd meter VRAM, unused during the automatic replay). Fixed-frame tests can shift when per-frame code changes.

## seasonend.s location (r12)

Code at $2E7000 (limit $3000); $2E3000/$2E3004/$2E3008 are bra.w stubs (keep 4-byte spacing). RAM: DIRECT $FFE536, LPG $FFE538, LDR $FFE860..$FFEB7F (100 x 8).

## Planes on table.s screens (r10)

B004 table: index 0 = $E000 front plane (empty $07FF), index 4 = $C000 back plane (SPORTS pattern, palette 3), index 8 = $B000 window. Text in the back plane gives a dark box per glyph; print with B02E = 0 for text over the pattern. Palette fade target buffer $BD28 (palette 0) .. $BD88 (palette 3).

## Sound (r8)

$11156(id on stack) plays sound id ($11132 also stores it in $C016); ids >= $30 are music; -1 = nothing. Fades in table.s/seasonend.s/finished.s pass $79 (menu music). Stop music: channel table $D3D4 (8 x pointer.l, delay.w; -1 = free), then $1A264 (voices off).

## Sprite list limit (r7)

Native list $C018.. has NO bound: the game intro builds ~90 sprites, up to $C2E8 (C2E8 = list words, C2EA/C2EC state right after). Never draw extra sprites first unless the native count is known low (crit flash in play); otherwise add them at the end hook $15F06 with a6 < $C298 (80 sprites). $C466 (period) is 0 in period 1.

## Crit flame trail (r23)

`experimental/trail.s` $2E4000 (tiles $2E4200), hook $15F06 + draw from crowd.s, RAM $FFF600..$FFF635, VRAM tiles $7FC..$7FE ($FF80).
Puck screen position (sprite coords): x - $BD1C + 285, $BD18 - y + 233 - height. Shootout: $C2EA bit0 is always set.
Never DMA up to VRAM $10000 exactly: it wraps onto tile 0. VRAM $FC20..$FFDF is now used (crowd + trail).

## End of season (r19)

`experimental/seasonend.s` $2E3000 (+0 hook $172E4, +4 Simulate Match exit), RAM $FFE400..$FFE76F.
`experimental/postats.s` $2E2000: season playoff stats in SRAM $0002..$07A2 (free User Records area); season.s row offsets go through it.
`table.s` +4 = its screen set-up (use it for any post-game screen; the native panel is invisible there).
SRAM $1210: 0 none, 1 set up, 2 playoff game played, 3 out, 4 champion, 5 missed (3..5 = summary shown).
`playoffs.s` +28 setflag, +32 poseason. `season.s` POGAME ($FFF0D2) = playoff save path.

## Crowd momentum meters (2026-10-25-r12, rows fixed r13)

Score box: TOP row = $CA32 team, BOTTOM row = $C6CE team ($C331).

`experimental/crowd.s` $2E0000 (+0 frame, +4 loader tail, +8 power-on defaults, +12 Roster value
hook $8D70, +16 game-end reset hook $172FA, +20/+24 crit roll/Roster scaling at $2FF462/$2FF4B8).
`experimental/onetimer.s` $2E1000 (hooks $B6C8 receiver input, $15EF2 frame), RAM $FFF736;
receiver C path $B6C8 queues action $23 (one-timer); one-timer power = +$64 bit3 in $C2F2, tiles
$2E0800..$2E0B5F, RAM $FFF700..$FFF7FF, Difficulty word $FFF07A (menu row 9), VRAM $FC20..$FF9F (tiles $7E1..$7FB). Hooks $15ED4 (sprite
list start) and $15D7C (rating loader tail). Native crowd meter $B8A2/$B89C has no
rating effect. Rating loader $15AA4 fills object +$66..+$75 (scale 0..30); native
team modifiers $CF2E/$CF2F/$CF31/$CF30 (+2 when trailing late) are added there.
Sprite list $FFC018, d6 link/count (max $40), DMA queue a5 from $B8B4 (end saved
in $BD14), processed by $15E82 -> $113E4 (a0 source, d0 words, d1 VRAM). Details:
CHANGELOG.md.

## Current: critical slapshots and Twin Cobra sound (2026-10-25)

Only full forehand windups (native animation07FC, unscaled charge>=31) now roll
Crit Chance; quick wrist releases and backhands do not. Forehand one-timers
qualify. Critical shots play the selected Twin Cobra Digital effect13 (local
clip19) once, alongside the existing red puck. CRT chance/25% bonus, ratings and
saves remain unchanged for qualifying shots. New critical_slap.s126 bytes at
2AD000, extended sound/PCM tables2AD200/2AD400, stream2AD480 and4497-byte PCM
asset2AE000..2AF190; transientFFF0B0 is reserved. Existing sound IDs, samples,
Z80 driver and music are retained. See CRITICAL_SLAP.md for exact hooks, original
sample extraction, source links, native playback and all validation.
110,000 native boundary/probability/sound/physics trials +110,000 without flash,
eight live audio/pad cases, music continuity and a clean full CPU Mini game pass.
Exact rebuild/legacy baseline and guarded5297-byte diff/IPS reconstruction pass.
Current dated SHA2560312bd6009741447726088115801a9e2cd0bd9f385b616743e393cadd3396a1a,
CRCFC0D6B4F. --legacy-crit-shots reproduces the preceding portrait build; older
entries/hashes below describe historical feature snapshots. Dated filename and
three test seasons carry over; no additional backup requested by jdc.

## Correct player-card portraits (2026-10-25)

The dated build now maps all832 cards to the reviewed player identity, instead
of retaining the pre-roster-update face.702 slot associations reuse existing
portraits,123 use new real NHL photos and7 have an explicit neutral silhouette.
586 pointers repaired;130 new874-byte records at2B0000..2CBBD3. Shared palette,
all gameplay code, roster/stat/save data and SRAM layout are unchanged.
Source: experimental/player_cards.py, prepare_player_cards.py, player_cards/
manifest.json and native verify_player_cards.py. Builds are offline; optional
--no-player-cards reproduces the preceding OT/shootout ROM exactly. Unknown
identities fail rather than assigning an unrelated face. Detailed IDs, sources,
seven missing photos, aliases, ROM format and validation: PLAYER_CARDS.md.
832 native lookups/decodes,28 paired WSH and28 EDM screenshots, byte-identical
rebuild and clean CPU1–4 pass. Current SHA256
0e5c8ee01f69d7219456d414a15b58fe593c0b957c5630886d138027822d0461,
CRC694C73C2. Dated filename/save matching and earlier releases are retained.

## Ten-minute OT, then shootouts (2026-10-25)

The dated build now uses10-minute sudden-death OT in played league and playoff
games, followed by three shootout attempts per club, early clinch and paired
sudden death. In Teammates, P1/P2 alternate their club's attempts on either side.
The native shootout panel displays attempts/score; the winner adds one final
team goal, without adding player/keeper goals or shots. League OTW/OTL and
native BO7/single-game playoff progression work normally. No SRAM layout change.
Full implementation,900-byte2AC000 code, FFF090..0AF reservation, nine native
hooks, Three Stars/late-penalty guards and18 integration cases are documented in
EXPERIMENTAL_BUILD.md. Source: experimental/shootout.s, verify_shootout.py and
testcases/shootout_hook.py. Save reader accepts shootout deciders/empty-net GA.
Overtime snapshot SHA25627349dff9ef46db0b1115f607535c72e03573a91896af03fa25df1b16f6443f7,
CRC57008214. Earlier releases are historical; dated filename/save matching stay.

## Native menu font state (2026-10-25)

seasonmenu.s::playmode must compare D3 with PLAYMODE and return WRITEVAL for
unchanged values BEFORE SLOTKEY branching. F78A2 sets C2F8 byte bit3 before the
F7B20 value pass; an unchanged callback routed through relabel/F8070 clears it.
The remaining original values then use AAC52 large glyphs instead of BE26A small
glyphs, corrupting only native main-menu text after cycling cup modes. Skip that
redundant relabel, retaining both real slot-switch and empty-Continue behavior.
1836-byte menu, no new allocation. Private native F7B66→11B92 tail-call probe
checks16 cases/3608 prints with zero failures; old ROM reproduces66 failures.
Rendered changes confined to text, initial screen pixel-identical.27-byte menu/
checksum diff only. Full source/evidence:EXPERIMENTAL_BUILD.md.

## Empty native Continue validation freeze (2026-10-25)

Current seasonmenu.s1836 bytes. In playmode, repeated callbacks with SLOTKEY set
must still enter original_modes when SLOTVIEW=0. Native F7AD4 decodes Continue's
password and F7AE2 retries via F7A6E when it is empty; blocking those writes locks
the menu while VBlank continues. A retry to Regular0 must restore the slot too,
not just WRITEVAL. Repeated SLOTVIEW1 writes remain blocked to avoid skipping
slots. Reproduced from jdc's empty Mini save; all18/348 full-circle checks and
20 slot/615 club regressions pass. Only menu/checksum bytes changed. Complete
reproduction/evidence/current hash:EXPERIMENTAL_BUILD.md.

## Standalone native playoff context (2026-10-25-r9)

Logical D20..E1F (CF36/D45A user profiles) is shared, never copied to/from banks;
banks keep their own checksum (`sumbank`). Full bank logical 2D20: "NPO1" + RAM
D088..D175 (238 bytes) + sum/~sum = standalone native playoff. suspend loads it,
$FE6B2 hook (slots+20) saves it outside the season view. See EXPERIMENTAL_BUILD.md.

## Three-slot season storage (2026-10-25)

Current manager `season/slots.s`:1182 bytes at2AA000..2AA49D; +0 init/migrate,
+4 select,+8 suspend,+12 reset,+16 pre-game list. Season-only playoff9-entry
list2AB000..2AB0E3 via8-byte hook13134. SRAM descriptor200001..20FFFF (odd,
32KB logical/64KB physical); active native8KB unchanged, backups atlogical
2000/4000/6000. S3SV/version1 directory in formerly free1EA0..1EF5 stores
active slot, backup mask, view flag and66 bytes of live D046..D087 settings.
Each backup is a full active image with its checksum and native password/stats.
Active is authoritative; validate destination before backing up/restoring.
Suspend before standalone modes and restore on return/reboot. Legacy82/84→Full,
31→Light,8→Mini; preserve running stats and clear stale pending New choices.

Full32-bit odd-address pointer copies with bounded interrupt masking; existing
native helpers sign-extend doubled word offsets, so cannot address all banks.
Recompute active checksum directly without FF0000 replay corruption. Native
D076 settings/password, CF36 records and D45A array follow restored contexts.
RuntimeFFF068..06F = tag/slot/view/key latch; scratchFFE800..E85F. Do not reuse.
Menu1836 bytes (relinked), newseason340, playoffs1074. Direction is normalized
and one transition latched at mainlr because native validation can repeat the
writer and skip formats. Removed destructive legacy setsel helper.
Builder/read tool/core harness all understand expanded SRAM. All20 slot cases,
615 club screens,38 goalie requests and a clean CPU1–2 OT pass; native RetroArch
preserves populated64KB saves. Full details/source/reproduction:EXPERIMENTAL_BUILD.

## Critical glass SFX at shot release (2026-10-25)

`experimental/critical_sound.s` is12 bytes at$2FF600..$2FF60B (14 without flash).
critical_sound.py changes the critical-success JSR at2FF472 from2FF504 to2FF600.
The helper writes SFX14 to22(SP), then JMPs2FF504 to retain the flash/power read.
Stack22 = return4 + critical saved registers12 + native target coordinates6.
Native shot exitC538 calls11132 once and cleans up its sound argument; this
replaces24/20 rather than layering a second immediate sound. True glass collision
14C18 uses the same14; its animation/whistle handler is never called here.
No RAM/register/save change. Without flash, replay BEDE read and RTS instead.
Default builder enables it; --no-crit-sound omits this sound change while
retaining current goalie/menu changes. Historical ddb73d... belongs to the
preceding snapshot. Tests and source details:EXPERIMENTAL_BUILD.md.

## Current season logo refresh ordering (2026-10-24)

`seasonmenu.s` is1894 bytes at2F9800 (was1880). Generic permitted writes and
special Players writes now call WRITEVAL then force before returning to F75D2's
native settings→C330/C332→logo comparison/load sequence. Previously force ran
only at the later text redraw, so a changed user club could appear on both
logos while the final schedule was distinct. Same bug in all three formats.
Native cache IDs are low bytes of D42A/D42C; high FF is an invalidation tag.
Builder relinks teamrow/labelhook/mainlr/skipmatch targets; entry+0/+8 stable.
No new allocation/save format or gameplay change. Details/test command and
current hash are in EXPERIMENTAL_BUILD.md. Feature-off build flags now retain
this menu fix, so historical exact profile hashes belong to older source versions.

## Current red critical-puck flash allocation (2026-10-24)

`experimental/critical_flash.s`234 bytes at$2FF500..$2FF5E9; red copies of eight
puck tiles256 bytes at$2FFE00..$2FFEFF. RAMFFF060.w=lifetime,FFF062.w=last phase.
Hooks2FF43E/2FF472 (critical result),2FF39C (accepted pickup),167F8 (puck frame
cache),1686A (tile DMA source),168FC (sprite palette bits). All register/flag
continuations and original instructions are replayed. Only puck A3=FFFFB74A
changes, using native geometry and fixed rink palette0 indexA; shared palette
entries and shadow remain untouched. Dead puck is byteC2EA bit0; C3F4 is a
penalty report, NOT a live-play flag. Colour changes invalidate puck +8 frame
cache so DMA refreshes pixels even when animation frame is unchanged.
`--no-crit-flash` on the experimental builder reconstructs the critical-only
95a05f... profile. verify_critical_flash.py runs a headless lifecycle/DMA/cache/
pickup-gate regression. Full source/addresses/evidence:EXPERIMENTAL_BUILD.md.

## Current dated critical-shot allocation (2026-10-24)

`experimental/critical.s` occupies$2FF400..$2FF4CF (208 bytes); entries+0 shot,
+4 native attribute,+8 lookup. Table$2FF800..$2FFB3F has832 separate rating
bytes. Attribute-list copy$2FFC00..$2FFD8D has all17 native entries plus Crit
Chance (mask0/mode12). These areas are occupied in the dated build. Hooks:
C4A8(8),8D68(8), and pointer operands84DC/8BD8/FC85E/FC890. Shot lookup uses
object+$66 roster index and+$62 bit6 team membership, NOT bit7 attack direction.
Native attribute lookup resolves A2+$1E against$30E. No new persistent RAM/SRAM.
`teamstats.s`1756 bytes still at2FB000, guarded by generated WITH_CRIT.
`build_season.py --crit` is used only by the experimental builder; --no-crit on
the latter reproduces the preceding dated ROM. `critical.py` generates ratings;
`crit_overrides.json` allows explicit ABR:Name values. Complete bounds, formula,
exact hash and test protocol are in EXPERIMENTAL_BUILD.md. Private reset-vector
fixtures/shot taps at2FEA00 andFFF060 are NOT installed in the release ROM.

## Files and where everything is (since 2026-10-21)

The working folder is the **web folder** `~/mount/truenas/www/dela.jdc.se/nhl94/tools/` (public: https://dela.jdc.se/nhl94/tools/).
Public release ROMs are kept in `../download/`; test states and backups stay outside the web folder.

| Where | What |
|---|---|
| `tools/build.sh` | Builds from the base ROM with `patch.py` and always installs to `~/.config/retroarch/roms/claude/NHL 2026 - 94 Edition v5.1W.bin`. Backs up the previous one to `work/backups/`. Refreshes the two public ROMs and IPS in `download/` |
| `tools/update_rosters.sh` | Fetches NHL rosters, prospects, stats and DailyFaceoff lines, runs `gen.py` (skips teams with `"manual": true`, e.g. EDM), builds |
| `tools/make_site_data.py` | Reads the installed ROM → `../teams.json` (the team pages) |
| `tools/patch.py` | All the changes. Refuses if the original bytes don't match |
| `tools/rosters/*.json` | One file per team (fix 7) |
| `tools/gen.py`, `fetch.py`, `data/` | Roster generator + downloaded NHL/DFO data |
| `tools/model.py`, `feat.py`, `romp.py`, `eap.py` | Analysis: kNN ratings, ROM reading, EA ratings (top 300 only) |
| `tools/run.sh`, `h.lua`, `lua/*.lua` | Headless MAME rig and test scripts |
| `tools/mkips.py`, `blueline_image.py`, `disasm.py` | IPS maker, blue-line zone picture, disassembler |
| `tools/config.sh` | Paths (ROMDIR, BASE_ROM, ROM, WORK) |
| `~/.config/retroarch/roms/claude/NHL 2026 - 94 Edition v5.1W.bin.orig.bak` | Base ROM, md5 8f863ecb108c1d213953bada9b98f4f2. ALWAYS build from this |
| `~/.config/retroarch/roms/claude/work/` | Private: `sta/` (saved states), `snap/`, `backups/`, build output, `old-passfix/` (the original working folder) |

Duplicate warning fix: `$12960.w` is `$FFFF` instead of `$0002` in the
penalty-expiry MOVE.W immediate at `$1295E`. The native negative sound-ID path
keeps stack cleanup and player release, but suppresses the shared SFX2 warning.
Clock IRQ at40 retains the three pulses; final-five penalty ticks remain SFX1.
No allocation change. Debug sound taps are private test instrumentation only.

AI keeper request passes: `experimental/outlet.s`,748 bytes at `$2FDC00–$2FDEEB`.
Entries+0 decision,+4 exact target,+8 goalie vector,+12 B request. Hooks D75C,
BCDA,BDBA remain. Human-side AI waits for B, serves the requesting pad's current
active human skater after recovery, and consumes the press through release.
First request wins. Native timer can still freeze the puck; CPU-only/manual and
special modes keep native behavior. REQ `$FFF058–5F` is transient BC64 descriptor;
new ASK `$FFF064–67` is pad+1/keeper for this possession. Pickup guard at2FE400
is90 bytes and clears ASK on each accepted native pickup. No SRAM change.
Use full32-bit initialization before DIVU6 in target-side validation. Current
request regression and fixture paths are in EXPERIMENTAL_BUILD.md.

Glass: `experimental/glass.s` at `$2FDB00`, 140 bytes. Hooks `$14BE0`
(36-byte impact gate) and `$10320` (8-byte shadow/siren position gate). 50%
for puck height19..31, Y>=280, X -64..64, VY>=$1F00; near/side/corners
keep native behavior. `$FFF056.w` stores nearest 32-pixel pane X, used only
for glass frames $28D..$291; other sirens stay centred. See experimental guide
for exact bytes, probability evidence and empty-net-aware CPU validation.

Empty-header fix: `gameinfo` now uses stored length only after `hasseason`
confirms a valid magic/team. An empty saved Mini header (team=0, length=8) must
show New Full Season 1/84, not 1/8. `seasonmenu.s` grows 6 bytes to 1880;
all symbol-based hooks are relinked. No SRAM change.

New full-season follow-up: `season.s` initializes header +10 to 84;
`seasonmenu.s` pending-full/default Game text uses 84, and the main label is
`New Full Season` (still padded to 20). Continue reads stored +10 and preserves
82-game saves. Pre-game entry/dialog use `Restart Season` for all formats.
Routine sizes/allocations unchanged; simulated-assist percentage 82 is unrelated.

Diving-catch follow-up: `$1531E.w` is 180 instead of 5 (immediate operand of
`MOVE.W #duration,$48(A2)` at `$1531C`). Standing catches already use 180 at
`$1530C`. Animation `$02F4` is the sprawl/dive; goalie `+$48` counts possession,
`+$58` is animation, `+$63` bit 1 locks recovery. AI countdown `$D6B8`, human
`$D4C6`; expiry calls held-puck event 8. No new allocation. Builder and controlled
old/new fixtures are described in EXPERIMENTAL_BUILD.

Latest dated test: `experimental/switch.s` is500 bytes at `$2FD900`; hooks
`$B1B6` and `$B2DA`, RAM `$FFF048–$FFF055`. B switches on press, excludes the
current/other-human/goalie slots, and consumes held B after selection. Native
passing and explicit Y remain. It calls outlet+12 before ordinary switching.
Sign-extended absolute-word constants keep it below glass2FDB00; builder asserts
size <=512. Use normal six-button device 513. See the complete
experimental guide for exact build hash and all behavior/bypass conditions.

Current sources (2026-10-24): `season.s` 3,938 bytes at `$2F8000`, `seasonmenu.s`
1,894 bytes at `$2F9800`, `playoffs.s` 1,066 bytes at `$2FC800`, standings 1,748
bytes, leaders 972 bytes, teamstats 1,670 bytes. Mini length is 8 (pending choice 3),
Light 31 (choice 2), full 84 (choice 1; legacy saves 82). Mini/Light playoffs are single-game;
full remains best of seven. Main-menu Game-row arrows select an unplayed circle
round through `opponents.s` (630 bytes at `$2FD600`), persisted at SRAM
`$1212–$121F`, with incremental checksum and a whole-league round. `statsinput.s`
at `$2FD500` is 78 bytes. Legacy indirect statistics exits were removed; Start
returns via `$9CD8`. See [EXPERIMENTAL_BUILD.md](https://dela.jdc.se/nhl94/notes.html#EXPERIMENTAL_BUILD.md) for all
current details and the complete experimental builder (byte-identical to the
tested ROM). Earlier dated experiment addresses below are historical snapshots.

Build and install:

    cd ~/mount/truenas/www/dela.jdc.se/nhl94/tools && ./build.sh          # default flags r=20
    python3 make_site_data.py                                             # refresh the team pages

## How a change is built (the pattern in patch.py)

1. Find the instruction in the original code where the change goes (see Routine map).
2. Replace at least 6 bytes (whole instructions!) with `jsr $2FFxxx` (6 bytes, `4EB9 + address`) plus `nop` (`4E71`) as padding.
   Use `jmp` (`4EF9`) when the hook has to return to different places.
3. Put the new code in free ROM. Always run the replaced original instructions in the hook.
   - If the original code after the hook uses flags (beq/bne/bgt…) from a replaced instruction: run that instruction **last** before `rts`/`jmp`. Neither `rts` nor `jmp` changes the flags.
   - Save registers you overwrite (`move.w d1,-(a7)` … `move.w (a7)+,d1`) unless you know they are free.
   - With `jsr` the return address is at 0(a7), so the original's stack values are shifted by 4.
4. `put(addr, code, expected_original_bytes)` checks the original. Always give the expected bytes at the hook.
5. `asm(base, [...])` is a minimal assembler: `W(word,...)` for raw words, `L(long)`, `('lbl','name')`,
   `('bra', opcode, 'name')` for 16-bit branches (opcodes: `6000` bra, `6700` beq, `6600` bne, `6D00` blt, `6C00` bge,
   `6E00` bgt, `6F00` ble, `6200` bhi, `6300` bls, `6A00` bpl, `6B00` bmi, `51CB` dbra d3).
6. Disassemble the result and read it through: `python3 disasm.py 2FF300 2FF3E0 out.bin` (needs python-capstone).
   Start disassembling on an instruction boundary, otherwise you get garbage.

## Free space

**ROM:** `$20963E–$2FFFBF` is `$FF` in the original (`$2FFFC0–$2FFFDF` holds a signature, don't touch).
In use:

| Address | Change |
|---|---|
| `$2F0000–` | Team records (fix 7), 16-byte aligned, about 27 KB for 32 teams |
| `$2FE000–$2FE025` | Fix 8: Fighting N/A when Penalties Off |
| `$2FE200–$2FE20B` | Fix 10: start-up defaults (hook `$17C86`) |
| `$2FE280–$2FE3FF` | Fix 12: intro screen code (`intro.s`) |
| `$2A0000–$2A94FF` | Fix 12: intro picture (palette, map, 1120 tiles) |
| `$2FE220–$2FE27F` | Fix 11: User Records out of the menu (row mapping for the accessors `$2095D6`/`$2095F6`/`$20961A`) |
| `$2FE040–$2FE1xx` | Fix 9: Y takes your goalie (`ygoalie.s`, reader hook at `$2FE040`, handler at `$2FE100`) |
| `$2F8000–$2FCFFF` | Season ROM only (project MEGAdriveHARDJOB): `season.s` `$2F8000`, `standings.s` `$2F9000`, `seasonmenu.s` `$2F9800`, `leaders.s` `$2FA000`, `newseason.s` `$2FA800`, pre-game list copy `$2FAC00`, `teamstats.s` `$2FB000`, `table.s` `$2FC000` (league table) |
| `$2FF000–$2FF029` | Pass target, human preferred |
| `$2FF080–$2FF0F1` | Receive radius |
| `$2FF140–$2FF163` | Goalie passes, flight time |
| `$2FF180–$2FF207` | Goalie passes, never blind |
| `$2FF300–$2FF333` | Blue line: shot hook |
| `$2FF340–$2FF35F` | Blue line: goalie reach |
| `$2FF380–$2FF3A3` | Blue line: pickup |
| `$2FF3C0–$2FF3DF` | Blue line: lower lift |

**Next free in the normal ROM: `$2FE400`** (and `$2AA000–$2EFFFF`) (keep `$2F8000–$2FCFFF` for the season project; `$2FF400–` is free again). `patch.py` asserts the areas are `$FF` before writing. Add a new assert for new areas.

**RAM:** `$FFE000–$FFF7FF` was never written during a full period with a watchpoint (the stack is above about `$FFFEB0`).
In use: `$FFF000.w` = last shot distance, `$FFF002.b` = goalie-miss countdown, `$FFF004.w` = Fighting setting (0 off, 1 on, 2 arcade).
`$FFF0E0–$FFF0E7` = Y goalie (fix 9), `$FFF0F0` = season DELTA, `$FFF100–` season rows/team screen.
`$FFF006` is now the Penaltys frequency word. Audit current module definitions before allocating more RAM.

**Menu settings:** `$D048` Play Mode, `$D04A` Players, `$D04C` Home, `$D04E` Away, `$D050` Per. Length (0=5 1=7 2=10 3=20 min),
`$D052` Goalie Ctrl (0 Manual), `$D054` User Records, `$D056` Penalties, `$D058` Line Changes (0 On, 1 Off, 2 Auto); defaults at
`$17C8E`; menu tables: labels `$20915A` (16 bytes per row), value-string offsets `$2091FE`, option counts `$2095AE`
(4-player: `$2095C2`), all indexed by d7 = 2 × row; since fix 11 rows 6–9 are Penalties, Line Changes, Fighting, User Records; `$D072`/`$D074` keep Line Changes / Play Mode between games.

**6-button pads:** the hack reads them at `$1FFBC4` (from `$1A49A`): four TH cycles via `$1FFC52`; the third TH-high byte has
Z/Y/X/Mode in bits 0–3 (active low). Test with MAME `-ctrl1 md6button` (`h.lua` finds the `md6button` port; buttons `X`, `Y`, `Z`, `Mode`).

Note: `$FFDEF4/$FFDEF6…` is the hack's event log (14 bytes per entry, written at `$1EFC12`) and grows upward over `$DF00`.
Never put your own variables just above `$DEF6`.

## Routine map (ROM addresses in the original)

| Address | What |
|---|---|
| `$BCF8–$BD5A` | Pass target choice: loop over teammates, cost = distance² + angle penalty (±1 octant) |
| `$BD5E` | "No receiver" → blind pass |
| `$BF54` | Pass flight time d2 (truncated, max $18) |
| `$BFC6–$C08C` | Pass receiver / switching the controlled player |
| `$C0F8` | Switch which player P1/P2 controls |
| `$C224` | Shot charging (`$5A(a3)`) |
| `$C2F2–$C540` | **Shot routine.** `$BEDE` = power. Target x from table `$C542` (8 directions × (x, height)): x 0/±16, height 12/6/0. Target y ±$108. Spread depends on distance d3 (>200 always, ≤250 halved, max $88) and rating `$6D`. `$B772/$B774` = puck speed x/y, `$B776` = lift (vz) |
| `$C408` | d3 = distance shooter→net (blue-line hook) |
| `$C51A` | Lift written to `$B776` (lower-lift hook) |
| `$14D8C–$14E0E` | Puck collision sweep over the y-sorted lists `$B84A/$B86A/$B88A` |
| `$14E10` | Puck against a player: pickup (height ≤5, radius 14, `$14E84`) else stick/body |
| `$14F78–$150C2` | **Goalie reach.** Box ±`$BF64` and radius² `$BF66` from tables `$14F28`/`$14F50` (12–15 px by rating `$68`). No save if puck height `$B762` > 15. Hit → `$15466` |
| `$15466–$1562C` | Save: catch (slow puck) or rebound |
| `$150E4` | **Pickup** (puck owner changes). `$150F6` hook |
| `$153CA` | Deflection off a stick |
| `$2001DE–$2003BE` | **Fight start** (called on a body check). Needs: Fighting on, both `$32` ≥ 10, no goalie, play live, `$C466` ≤ 2. Threshold = table `$200100` (by `$C46C`) − `$C46E`, /4, +10 (arcade +6) against the fighting rating `+$74`; then `$20042A` with penalty minutes (`$C6CE`+`$11C`), Penalties ≠ Off (`$D056`) and 25 % chance (VDP HV counter) |
| `$200544–` | The fight itself (punches, energy `$44`) |
| `$209596–$20963C` | Menu values: number of choices per option at `$2095AE` and `$2095C2`, strings from `$2091FE` + offsets at `$2091FC…`, settings at `$D048` + 2·index (index 9 = Fighting, via +`$EA6`) |
| `$164D6–$165BA` | **Edge arrow** for players off screen (e.g. the goalie in shootouts). Screen x = (x − camera `$BD1C`) + `$100`; fix 5 makes it `$120` (the hack's screen is 320 px, the original 256). Edge test ±`$74`/±`$64`, edge positions in table `$165BC` |
| `$14620–$14682` | **The net:** no interaction if puck height > 13 (= the crossbar). Half-width 16+`$BD22`(5), depth 2+`$BD24`(5) around the net object |
| `$14682–$1477C` | Goal or post ("ding" at the edge) |
| `$1481E` | Goal recorded: goal log `$FFC474` (+6 bytes per goal, index `$C472`) |
| `$11086` | Random: d0 in → 0..d0−1 out, touches only d0 |
| `$1107A` | Random: ±d0 |
| `$110BE` | sqrt(d0.l) |
| `$10676` | Direction (octant) from dx/dy |

## RAM map

Stats-entry fix (2026-10-24): in standings.s C handling, preserve the selected
team D0.w on the stack around `unlite`, then restore it before `jsr TEAMSTATS`.
`unlite` uses D0 for ORIGC and used to pass a palette colour (e.g. `$0800`) as the
team index, causing Address Error. Standings uses 1766 bytes at `$2F9000` after
the fix. Corrected source is in the normal build; installed binary is private
only. Old/new crash traces, all-32-team UI tests and source checks are in private
`work/stats-freeze-20261024-084813/README.md`.

Private simulation experiment (2026-10-23, not released): 964-byte menu/statistics
code at `$2FD000–$2FD3C3` (+0 handler, +4 statistics gate). The league pre-game list
at `$2FAC00` is extended to 9 entries / 228 bytes. Handler sets home/away IDs and
score using `season.s` goals, resolves ties in overtime, sets `$FFF00A=0x5349` and
calls `$2F8006` (season entry without the native game-over record call). At the
season `save` label `$2F8194`, JSR `$2FD004` + NOP replaces addreal/gkreal calls:
simulation uses dist/gksim, normal games keep addreal/gkreal. Flag is cleared
before the result panel. Temporary result string/header: `$FFE700`. Return through
`$172FA` resets game RAM and stack before menu forcing. Static result panel uses
VBlank `$17692`, disables sprite DMA at `$FFC2E8` and clears sprite zero at VRAM
`$B002` so old rink sprites cannot cover the result. Current source plus includes
was matched exactly to the baseline (3,904 bytes) before obtaining linked symbols.
See private `work/simulate-match-20261023-153017/README.md` for reproduction and
complete-season/boundary checks. Preserve this range in later private builds;
it remains free in the public build.

Private penalty experiment (2026-10-23, not released): positive-duration event records
are in the relative-offset table `$18E0C–$19139`; minute byte is +1. Injury event
`$12` (Elbowing) has 4 minutes, ordinary `$16–$24` 2, fighting `$26/$28` 5,
instigator `$2A` 2. `$FF` duration records are penalty-shot variants, not injuries.
Test code `$2FE500–$2FE56F` selects a shortened table `$2FE600–$2FE92D` only for
`$FFD050=0` (five-minute periods), using nine table-base hooks and preserving CCR.
Award lookup `$1226E` stays original so `$1228C` can still classify five-minute
majors; JMP hook `$1229A` normalizes D2 after classification and before team PIM
`$122EE` and `MULU #60,D2` `$1230E`. Live penalty words: team base `$FFC6CE`/
`$FFCA32` +`$66` + 2×roster index; timer plus flags. Event queue `$FFC3A4`, two
bytes per event (event ID, object index with pending bit 7). Source, paired-fight
checks and 24-case matrix: private `work/penalty-times-20261023-151151/README.md`.
These ranges are occupied in the private test ROM even though still free in the
public build. Preserve the previous goalie guard `$2FE400` and warning `$1960`.

Private warning experiment (2026-10-23, not released): clock seconds are `$FFC468`, fractional countdown `$FFC46A`, period `$FFC466`. IRQ `$15D9A` subtracts `$AAA` per frame and decrements seconds on borrow. Its comparisons at `$15DFE` / `$15E08` trigger sound ID 2 in the 61/60-second window. The test uses 40/40 and redirects sound-table entry `$2C650` to a 60-byte three-pulse sequence in unused alignment padding `$1960–$199B`. Sound events are four bytes (frame delay, MIDI-like status/channel, note/program, velocity). Do not put the new sequence in high free ROM: `$1A304` identifies music by pointer address relative to the first music sequence and would stop it on music changes. Full isolated source/verification: private `work/clock-beep-20261023-145844/README.md`.

- Objects: `$FFB04A + i*$80`. 0–4 team 1 skaters, 5 team 1 goalie, 6–10 team 2, 11 team 2 goalie, 12–13 the nets (y ±268), 14 the puck (`$FFB74A`).
  - `+$00` x, `+$14` y, `+$18` height (puck: `$B762`), `+$28/$2A` speed, `+$34` = 0 for goalie/net,
    `+$52` index, `+$54` direction, `+$5E` pickup lock, `+$62` bit 3 = human-controlled, bit 7 = team/attack direction,
    `+$66…+$75` ratings (expanded), `+$68` goalie rating, `+$6C` shot power, `+$6D` accuracy, `+$6F` jersey (BCD), `+$74` fighting.
- `$FFB7AA` puck owner (negative = loose). `$FFC320/$FFC322` object controlled by P1/P2.
- `$FFBEDC` stick/aim direction (0 = up, clockwise, 8 = none). `$FFBEDE` shot power. `$FFBEDA` last shooter. `$FFBEE0` pass target.
- `$FFC6CF` shots on goal (a counter). `$FFC474…` goal log (changes on every goal). `$FFC331/$FFC333` home/away team index.
- Coordinates: goal line ≈ y ±261, blue line about 180 from the net, centre 0. Screen = 1:1 with game pixels.

## Testing (headless MAME + Lua)

    cd tools
    EVOUT=out.txt ROM_TEST=file.bin GK=blev2.lua ./run.sh bt.txt   # bt.txt: 900000 exit; GK is looked up in lua/; runs in work/

- `run.sh` runs `mame genesis` **without a display**: `env -u DISPLAY -u WAYLAND_DISPLAY SDL_VIDEODRIVER=dummy`.
  `-video none` is not enough, windows still appear on jdc's screen. Run with `nice -n 19`.
  Check afterwards: `journalctl --user --since -2min | grep -c mame` should be 0.
- Battery RAM (SRAM, 8 KB): MAME saves it per ROM name. `run.sh` uses `work/nvram/` and clears it before every run, so menu
  settings from earlier tests don't leak in. Set `KEEP_NVRAM=1` to keep it (e.g. to test saving between games).
- Stop a long MAME run with an `exit` line in the command file. Killing MAME (SIGTERM via `pkill`/`timeout`) does
  **not** save the battery file — a 21-minute season run was lost that way.
- Start MAME **through run.sh**. Started directly with `timeout` it died silently.
- `-seconds_to_run` must be large (run.sh: `SECS`, default 100000). When a saved state is loaded MAME's clock jumps to the
  state's time, and if that is past the limit MAME exits without a message.
- `h.lua` reads the command file (`<frame> press|snap|save|load|dump|poke|exit`) and loads `GK=script.lua`. Load states no
  earlier than about frame 20.
- Screenshots: `manager.machine.video:snapshot()` → `work/snap/genesis/NNNN.png`. Works headless. `run.sh` clears the folder on start.
- Never move players by writing x/y in RAM: it breaks the y-sorted collision list.
- **Test in Genesis Plus GX too** (the core RetroArch uses): `tools/gpgx.py ROM cmds.txt [out_dir]` runs the libretro core
  headless from Python (ctypes), same command file as `run.sh` (+ `state file` = GPGX savestate), `GPGX_SRM=file.srm`
  (16 KB, data on odd bytes) for battery RAM. The core's controller metadata confirms `GPGX_DEV=513` = normal 6-button pad;
  `1281` = 6-button pad plus 4-WayPlay (used in an older remap, not equivalent to a normal 6-button device). About 4000 frames per second.
  MAME's VDP is less exact: the division table looked right in MAME but after a human game in RetroArch the front plane
  was scrolled off (hscroll $FE6E). In a GPGX savestate (1.7.6) the VDP registers are 32 bytes found by pattern
  (`04 64 38 2c 06 5e`), VSRAM 0x80 bytes before them, CRAM 0x80 before that, VRAM 0x10000 before that (words little-endian).
- Finding code: watchpoint in Lua, run with `-debug -debugger none`:
  `cpu.debug:wpset(space,"w",addr,len,"1",'printf "pc=%x",pc ; go')` and read `manager.machine.debugger.consolelog`.
  The PC is the address *after* the instruction that wrote.
- Same state + same input = same result (deterministic). Variation comes from different states/timings.
- Always compare against the original with the same test script before installing, and run a full CPU period as a stability check.
- Menu navigation for demo games (CPU vs CPU): Start; Down; Right ×4 = Demo; Down; Right ×N = home team counted from
  Washington; Down; Right ×N = away team; Start ×3. A game lasts about frames 1500–68000.
  Shootout: Play Mode Right ×3. In-game pause menu: Down ×3 = Edit Lines.

## Ideas that were tried and didn't work

- Shrinking the goalie's reach for long shots (−3/−5 px): the shots got past the goalie but over the bar. Almost no goals.
- Moving the corner aim 3 px inside the post: the spread from distance is too large for it to matter.
- Only lowering the lift: more saves and blocks.

## Teams and players (fix 7)

- Team table: 32 long pointers at `$30E` (index 0 Anaheim … 10 Edmonton … 30 Washington, 31 Winnipeg) → team record.
- Team record: 6-word header: `+0` offset to players (`$92`, Winnipeg `$16C`), `+2` palette (`$0C`), `+4` offset to the name
  strings (= end of players + 2; the word `$0002` sits before them), `+6` lines (`$52`), `+8`/`+10` small fields
  (`+$4C` = `00 00 00 0F FF F0` for teams with 3 goalies, `…FF 00` with 2 — except MTL — not decoded; keep each team's number of goalies).
- Lines: 8 × 8 bytes `{G, LD, RD, LW, C, RW, extra, 0}`, 1-based player indices. Order: ?, Sc1, Sc2, Chk, PP1, PP2, PK1, PK2
  (verified in the Line Editor).
- Players (26, goalies → forwards → defence): word length (2 + name, even), name, jersey BCD, 7 bytes = 14 nibbles:
  weight (1–14 ≈ 7.26 + 0.116·(lb − 202)), agility, speed, off. awareness, def. awareness, shot power, checking,
  handedness+fighting (bit0 right, bits 1–3 fighting 0–7), stickhandling, shot accuracy, endurance, unknown (1–5), passing,
  aggressiveness (others 1–6). Goalies: nibbles 6–9 = 0, the rest not fully mapped (4–5 follow save % most closely).
- Player-card labels and bitmasks: `$209100`.
- The F/D split does not matter to the game (teams with the same mask have different splits), the goalie count is kept.
- Change a team: put `rosters/<TEAM>.json` (see `rosters/EDM.json`), run build.sh. `"rom"` = take the ratings of the player with
  the same name anywhere in the original ROM, otherwise 14 hex digits.

## All teams (2026-10-21)

- `gen.py` picks 26 players per team with the original's number of goalies (forwards/defence may shift by a slot if there
  aren't enough defencemen). Priority: in the DFO lines → rest of the roster → injured → prospects with NHL games → other
  prospects → the team's old ROM players who are on no NHL list → the best free agent from the old ROM.
- Ratings: `rosters/overrides.json` wins first. Then a player found anywhere in the original ROM keeps the hack's ratings,
  unless they are the hack's placeholder (all nibbles 1 except weight). Otherwise kNN (k=12) on NHL stats 2024–27,
  pulled toward a "depth player" template under 20 games; no NHL games → the template. Goalies: average of the 8 ROM goalies
  with the closest save % (shrunk toward .895). A note per player in `rating_notes` in the team file.
- Lines: DFO's trios/pairs, each trio turned to official NHL positions (C in the middle, L/R on the wings; DFO's slot breaks ties).
  Sc1/Sc2/Chk = DFO lines 1–3 + pairs 1–3, PP/PK from DFO's units. Goalie = DFO's starter.
- DFO: `https://www.dailyfaceoff.com/teams/<slug>/line-combinations`, data in the page's `__NEXT_DATA__` (groupIdentifier
  f1–f4, d1–d3, g, pp1/pp2, pk1/pk2, ir; positionIdentifier lw/c/rw/ld/rd/g1/g2/sk1…).
- NHL: `api-web.nhle.com/v1/roster/<TEAM>/current`, `/v1/prospects/<TEAM>`, stats `api.nhle.com/stats/rest/en/skater/{summary,realtime,bios}`
  and `goalie/summary` with `cayenneExp=seasonId=… and gameTypeId=2`.
- EA's ratings site (NHL 27) only exposes the top 300 players; the data API needs the header `drop-referrer: <page url>`.
- Tested: all 32 read back exactly; four full CPU games (BOS–CAR, CBJ–OTT, NSH–TOR, SEA–ANA) without errors.


## HUD-plane bars (r38)

Crowd and shot bars are HUD-plane ($E000) cells, not sprites (any sprite of ours loses to earlier native sprites). crowd.s +36 mkbar composes a 32x8 bar into 4 tiles; crowd bars tiles $7E5..$7EC (r39; $7D8/$FB00 = window rows 22-23, used when a glass breaks) at rows 24/26 cols 27-30, shot bar tiles $7E1..$7E4 in the shot panel row 19 cols 34-37. Compose/DMA only on change (CPU budget: per-frame composing caused lag frames). trail.s RAM $FFF630..$FFF637 is cleared at each new game (season menu text lands there).


## Season settings record and penalty minutes (2026-10-28)

Logical SRAM $1FF6..$1FFC per slot: 'S', Per.Length, Goalie Ctrl, Penalties, Line Changes, Fighting, xor ($5A ^ the five) - seasonplayers.s diffstore/load; NOSET $FFF0DC. Penalty minutes: team block +$102 + roster slot (byte); pim.s $2EE000 (+0 game, +4 clearleague, +8 offset), SRAM $09A0 + 25*team (league), $07C0 + 25*slot (playoffs, postats.s PO_END $096A).
