# Changes and measurements — NHL 2026 - 94 Edition v5.1W

## 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.


## Private player blue-line penalty rule —2026-11-04; full gate NOV5 ONLY

User confirmed shooter position (native88 body centre) activates first-crossing
latch; actual retreat then ends as native miss, never a steering clamp. Free
pre-line and purely lateral post-line movement; no new puck retreat/still rule.
Private Beta3 SHA0c9763eef54cc35812bbbb95b6fefb6e0dcfbea2fc86de1b9641f3515b62c4f6.
Targeted64 boundary/ABI,3 resets,5 native routes,6 controller-result variants,
2 complete autonomousCPU attempts, retouch/ordinary controls and exact repeat/
legacy rollback pass.37 native and6 browser pictures opened. See private handoff
penalty-blue-line-20261104/REPORT.md. Revised shared whole-group oracle/input
cases syntax checked ONLY. Full matrix expressly deferred until2026-11-05;
no official release/public-image promotion or old128-pass transplant today.


## User feedback fixes —2026-11-04; FULL MATRIX DEFERRED TO NOV5

- [x] Remove flat light/dark name backings with actual patterned font masks;
 native glyph/frame RGB checks and opened UI pictures, separate test2 preview.
- [x] Restore original team-OVR scale: EDM94 unchanged,99 after copied user trades;
 native seven-case/whole-SRAM controls and real94/99 Matchups images.
- [x] Reproduce automatic replay freeze; preserve alternate native9CDC entry;
 targeted clean/post-LineEditor replay→active clock and manual return checks.
- [x] Archive/verify then remove about380MB inactive /tmp data under NAS backups.
- [ ] **2026-11-05, at jdc's explicit request:** run the full shared release gate
 on the exact frozen/versioned final candidate. Include all32 calibrated OVR
 cases/direct ABI and automatic/manual/post-LineEditor replay continuation;
 no stale128 passes, relaxed requirements or silent ROM replacement.
- [ ] After that gate passes: proper new release, source/IPS/rollback/deployment
 policy and affected current website images/accurate video-version labels.

Today is targeted private testing only. Do not start the large matrix on Nov4.
[Source/evidence/scope](GREY_FEEDBACK_PREVIEW2_20261104.md).


## Grey framed menus —2026-11-04 private preview

Separately named local Beta preview897c575f… includes native grey panels,
centred trimmed help and the prior corner repair.91 real screenshots/21 groups
opened; matched transfer-sale SRAM, four read-only season saves,10 closed-menu
asset/palette controls, full72 period-logo tiles/all64 colours, two portrait
crops and exact rebuild checked. Full release suite intentionally not run at
jdc's explicit request. Public20793fce…/465 native/test sources stay unchanged.
[Exact source boundaries, evidence and limits](GREY_MENU_PREVIEW_20261104.md).



## Shorter native-sound feature tour completed —2026-11-04

New4:57/45-chapter current2026-11-03-r2 film replaces homepage9:33 tour.
All26 menu rows; three genuine~20sec Crits; actual charged directional pass,
Allstar CPU/black-red, dollar transactions/current roster/OVR/hot-cold and
explicitly labelled coherent synthetic standings/leaders.45 final export images
and27 public browser screenshots opened. Full decode, zero audio clipping,
six zero-lag PCM/AAC controls and1440/390/320 playback/seek/45chapters pass;
68 HTTPS checks pass. Old film/provenance archived unchanged. No native ROM,
controls or user saves changed; no native requalification/reset required.
[Movie/report/scope](VIDEO_SHORT_TOUR_20261104_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.


## Games domain and website structure —2026-11-03

Website restored at https://games.jdc.se/nhl94/; Manager/NHL94 logo links are
available at https://games.jdc.se/. Public images live in pics/, page code in
system/, other content in site/ subdirectories.909 image originals and historical
HTML retained;444 native/test source bytes and current ROM unchanged.2,564 initial
HTTP checks and30 individually opened desktop/mobile screenshots PASS.
[Structure and evidence](WEBSITE_LAYOUT_20261103.md).
Pending next: in-game startup URL and native menu speed/music restart investigation.
This website move does not release a ROM or reset progress.


## 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.

## Hard mode accepted as complete — 2026-10-31

At jdc's request, mark the released38-win Hard base completed on the website and
in the task list. The earlier39/50 benchmark remains unachieved historical research,
not an open task. Preserve all measured results, failed experiments and known QA
limitations. Status/documentation only; currentr8ROM, saves and controls unchanged.

## 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.

## Penalty menu discovery — 2026-10-31

Documented that native Penalties combines Off/On/On except offside at D056;
its value also gates penalty-shot eligibility. The requested frequency menu is
separate work, default 100%, preserving existing rules. Injury/fighting event
handling requires tests. See PENALTYS_RESEARCH.md. Documentation only; r7 and
all save/controller data remain unchanged. Investigation is active.


## Private visitor statistics — 2026-10-31

Added page views and unique visitor IP counts above the existing owner-only
download statistics. Public page content is served unchanged through a fixed
allowlist; counts require no tracking cookies or browser JavaScript. Aliases are
normalized, and private panel views, HEAD, errors and prefetches are excluded.
Existing authentication, HTTP-denied storage and download records are retained.
All 34 live visitor/delivery/security checks passed. No ROM or save changes.

## 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 consolidation (2026-10-29; no ROM change)

At jdc's request, moved all former `~/.config/retroarch/roms/claude` contents
into the canonical NHL94 project's `.workspace/` directory. Verified SHA256
and permissions for all 198,590 files, plus all directories and four symlinks,
before removing the local copy. The mount cannot read those four internal
symlinks, so they were subsequently materialized as exact target-file copies;
each adaptation and hash is recorded in `.workspace/.migration/symlink-relocations.json`.
The old local directory is a compatibility symlink.
Tools configuration and permanent agent/workflow instructions use the project
root and `.workspace/work/`. Original historical evidence remains unchanged.

HTTP access to the private workspace is denied (403); the public site returns
200. Current ROM SHA256 is unchanged. Existing 38-check exact-source receipt
validates; a separate native headless smoke run loads the canonical ROM and
runs 1,201 frames. No live saves or controller mappings were changed. The
existing delivery worker was paused for the move and resumed afterward.
Migration manifest and verification: `.workspace/.migration/`.

## 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/.


## Season settings kept; season penalty minutes (2026-10-28)

NHL94 forum report: "create a season with manual goalie, penalties on, line changes on, fight on,
finish the first game, exit the emulator: when you play again your settings are locked on the
wrong ones" and "I don't see PIMs being saved". Cause 1: Per.Length $D050, Goalie Ctrl $D052,
Penalties $D056, Line Changes $D058 (and $D072) and Fighting $F004 lived only in RAM; power-on
defaults reset them and the season locks those rows after game 1 (r22). seasonplayers.s
diffstore (each new game, season view) now also writes a record at logical SRAM $1FF6..$1FFC
('S', the five values, xor check), unused in every bank; load (boot / slot switch) restores
them. Seasons without the record (saved before this version) get NOSET $FFF0DC = $5345 and
their rows 8-16 stay editable (seasonmenu.s calls seasonplayers +56 openrow) until the next
game records them. Tested: change all five, start game 1, reboot -> restored (record
53 02 01 01 01 01 58); jdc's own save: rows open (forced 1, Penalties changed).
Cause 2: season stats held G/A/SOG only. pim.s ($2EE000): after every played season game the
team blocks' per-player minutes (+$102 + roster slot, bytes) are added (saturating) to logical
SRAM $09A0 + 25*team (league, 800 bytes, unused) or $07C0 + 25*SEEDS slot (playoffs; postats.s
clear now runs to $096A), keeping the checksum with season.s DELTA; a new season zeroes the
league block (season.s clearall). Simulated games add none (the sim has no penalty model).
seasonend.s: a PM column on the club pages (league / playoffs / both by tab). Tested with
injected 4+5 and 2 minutes: blocks 9 and 30 correct, checksum valid, shown on the page.
PASS critical 10, critical_flash, season_navigation 18/342, finished_season 9/108, menu_team
18/240, menu_font, season_slots 20, overtime/shootout/playoffs 18. SHA256
02fccc36c6d4a8002aaf2b4c1e028a4912cf3c7aab9ac80297cb02fff1882c86, CRC 687C11B1.

## Restart a finished season from the main menu; bar fixes (2026-10-27)

jdc: after a finished season the main menu only offered "See Old Full Season"; a new one had to
be started from inside it. Play Mode of a finished slot now has two steps: See Old ... (as
before) and, one step Right, "Restart Full/Light/Mini Season" (20-character labels: the menu
only draws the new text, longer labels left their tail behind, seen with a 22-character try).
Right again goes on to the next slot, Left steps back; arriving from the right lands on Restart.
Start on Restart goes straight to the finished.s confirmation (X + B = restart that slot only,
Start = cancel back to the main menu). Code: finished.s +28 step / +32 arrive and the label
switch (NEWPICK $FFE3FE = $4E57, menu RAM, cleared on every slot change and on use);
seasonmenu.s only calls them (it is at its 2 KB limit). Tested with the finished-season seed:
Right -> Restart label, Start -> confirm, cancel -> menu, Left -> See Old, A+C -> "New Full
Season 1/84" and the slot header reset (SESN 0,0,0,84,1,3).
Also: the shot bar had transparent top/bottom rows (from the sprite era) inside its black panel:
mkbar d3 bit1 = solid rows. The crowd-bar tiles moved from $7D8 (VRAM $FB00, window rows 22-23)
to $7E5 ($FCA0, the old crowd-tile area): jdc saw ice/boards garble in the corner after a glass
broke - the window plane is probably used there. Not reproduced headlessly (no way to force a
glass break found); the move returns to the VRAM footprint used safely for many releases.
PASS critical 10, critical_flash, critical_trail 1422/1422, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font, season_slots 20. SHA256
f72c395640b42496e765696dd24d4b996fb3bf27a0b628c51afd0b30eab6de9d, CRC D7FF22ED.

## Meters in the HUD plane; flames after season menus (2026-10-26-r37/r38)

r37 (jdc: flames gone after a replay and in the game afterwards; red puck and sound fine,
EDM-COL): reproduced only from jdc's real season save: at the first frame of play trail.s RAM
held season-menu text (HN 32768, HT 3348, TL 32025). HT counts down only while a crit is
active and uploads the flame tiles at -1, so they stayed blank for ~3300 frames of crit time.
crowd.s valid (new game) now clears $FFF630..$FFF637 and trail.s resets TL > 24, HT > 31,
HN > 9. Forced-crit comparison in jdc's season: r36 no flames, r37 flames.
r38 (jdc: skaters skate OVER the crowd bars and the shot bar): sprites of ours lose to every
earlier native sprite. Bars are HUD-plane ($E000) cells now, in front of the players like the
score box: crowd.s +36 mkbar composes a 32x8 bar (rows 0/7 transparent, 1/6 background, 2-5
fill x 1..fill, optional neutral tick at x 16) into 4 tiles; white = palette 0 (1 on 4), red =
palette 1 (A on 7, tick 1). Crowd bars: tiles $7D8..$7DF (VRAM $FB00, window rows 22-23, never
shown; REPLAY borrows $7E1..) at rows 24 (away) / 26 (home), columns 27-30; erased once in
replays / other views (BSHOWN $FFE8FE). Shot bar: tiles $7E1..$7E4 in Codex's panel row 19,
columns 34-37 (replay keeps the dash glyphs). Buffers $FFE900 (3 x 128), keys $FFE8F0: tiles
are composed and DMA'd only when a fill/colour changes (composing every frame cost enough CPU
that verify_critical_trail saw lag frames: 1399/1491; with keys 1422/1422). The 27 static
crowd sprite tiles are no longer uploaded; sprites drawn by crowd/shotmeter: none.
PASS critical 10, critical_flash, critical_trail 1422/1422, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font. SHA256
b00475a53ed761424d618612debd05c0d7e57dd5a5ab0e4a36ee560346ccbf50, CRC CCA479E8.

## Difficulty saved per season (2026-10-26-r36)

jdc: Continue Mini showed Difficulty Normal although the season was played on Easy. DIFF
($FFF07A) was RAM only and crowd.s's power-on defaults clear it; since r22 it locks after the
first season game, so it could not be set back. seasonplayers.s: new optional record in the
unused logical SRAM $1EF4..$1EF5 (physical $203DE9/$203DEB: difficulty, ~difficulty), right
after Codex's PC count record $1EF0..$1EF3, inside the 8 KB bank the slot manager snapshots.
load (slot init at boot / slot switch, mapper on) restores DIFF from a valid record; legacy
slots without it keep the current value. +52 diffstore, called by crowd.s valid (new game, or
Team Roster), writes DIFF in the season view only when it differs, then recomputes the native
checksum (the same tail as store). jdc's save was migrated by a real r36 game start with Easy:
4 bytes differ from r35 (record + checksum); the r35 save is kept in
work/difficulty-save-20261026/. Measured: Easy stored, reboot -> DIFF 2. PASS season_slots 20,
critical 10, season_navigation 18/342, finished_season 9/108, menu_team 18/240, menu_font.
SHA256 7c372516c2e24ea57f54b9926877b27de8360ce76d2d72504e6e25a4c1514125, CRC 2DEF2B52.

## Faster live Scoring Leaders (2026-10-26-r35)

jdc: after looking at the Scoring Leaders, Start had to be pressed twice (a black screen
otherwise). Not an exit bug: one Start always returned (tested after a real season game from
jdc's save, both pad types, every button, sorts/pages). The r34 ranking scanned all 800
players 32 times with a scoring subroutine and a bitmap test each time: about 60 frames
after opening (and after every C sort) with no input read, so a Start press then was lost.
leaders.s rank is now one pass: each player scored once, inserted into a sorted top 32
(scores in STR, free until draw; quick reject below the 32nd; strict > keeps the first found
on ties, as before). draw's G/A temp moved from KEYS to TAKEN, so a sort re-ranks without
re-reading SRAM. Same LORD bytes as r34 for PTS/G/A on the synthetic 800-player season and on
jdc's save. List visible ~20 frames sooner; the remaining ~30 frames are the native fade.
PASS critical 10, season_navigation 18/342, finished_season 9/108, menu_team 18/240,
menu_font. SHA256 c9b227db1c9696ed78bf20b89a112a23f9a6f4ee933310b625ec9ed1f91277f6, CRC C6899E68.

## Sortable live Scoring Leaders (2026-10-26-r34)

jdc: after a game the season's Scoring Leaders (pre-game menu) could not be sorted by goals /
assists like the finished-season browser. Codex (tools/season/leaders.s): C (Xbox B) cycles
PTS -> G -> A (one step per new press), raw G/A words with an 800-bit selection bitmap
(TAKEN $FFEA40; zero and maximum stats rank correctly), LONG sort scores (points may exceed
255; ties: points/goals, goals/points, assists/points), selected heading in palette 3 (a copy
of the font palette 2 with red, restored before the native exit), footer "Sort: Xbox B /
Mega Drive C". RAM moved: LORD $FFEAA4, PAGE $FFEB24, SORT $FFEB26, HDRB $FFEB28, STR $FFEB40
(ends before $F000), PALCOPY $FFF180. Claude finished it: the in-place redraw left the ends of
longer names ("Jeff Malottovace"), so names are padded to 16 characters. Probe (synthetic
season, 800 players): PTS/G/A orders, Right/Left paging, Start back to the menu, the menu
reopens normally. PASS critical 10, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font. SHA256
a8b5515ac0b03de4cea1802c8707b0468ffcdff8615d54e70b311ccdc155f2c1, CRC 6E810C21.

## 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.

## No goal replay after the final horn (2026-10-26-r30)

jdc: a replay played when the game was finished. Reproduced headlessly (period 3, 0:05,
2-1, no goal): at the horn replay.s armed its 90-frame countdown. A debug build saved the
values at arming: goals 3, LASTG ($FFF73E) 0 - the end-of-game code zeroes that RAM, so the
unchanged score looked like three new goals. replay.s now stores the goal sum tagged ($A5xx);
an untagged LASTG (zeroed by the game, or crowd.s's clear at game start) only resyncs, it
never arms. Measured: no replay after the horn (regulation and tie/OT runs); the goal replay
still plays. Also checked jdc's Easy report (18 blue-line shots, no goal): a human forehand
slapshot does crit in Easy (red puck, release power 404 -> 491 in the fixture); a crit is
+25 % power, not a guaranteed goal, so the goalie can still save it. PASS critical 10,
season_navigation 18/342, finished_season 9/108, menu_team 18/240. SHA256
a01aa1ef581199af2a50256613c2470cc9ca3329ef8a3117b3fdef1c6776b556, CRC DA8E5281.

## Shot speed as a score-box panel (2026-10-26-r29)

jdc: make the km/h / mph readout a box like the EDM 1 / STL 0 score box. The score box lives in
the fixed HUD plane ($E000, 64 cells wide, transparent $07FF elsewhere; rows 23-27, cols 32-39)
and uses resident tiles: frame $3FF-$407 (palette 1 + priority, $A3FF..), black fill $403, font
palette 0 + priority: '/' $41D, digits $41E+d, H $435, K $438, M $43A, P $43C. shotmeter.s now
writes a 10x4 panel of those map words at rows 8-11, cols 30-39 (top right, under the bar) on
release (" 165 KM/H", " 102 MPH ", leading blanks = $A403), with interrupts off and VDP
auto-increment 2; it writes $07FF back when SHOW runs out (240 frames), at a new wind-up, or on
the first replay/other-view frame (PANEL $FFF64C). The narrow r28 font, the $FB00 text tiles,
the per-frame DMA and the text sprites are gone. Measured: panel at the stands and the rink,
gone in the goal replay. PASS critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. SHA256
f641c2c7ae671728c4ea3068efb145dcac7b8d2050424099a4a258130b99d7a0, CRC 1CDF67B2.

## Shot meter lower and stable; replay hint in score-box style (2026-10-26-r28)

jdc: the goal banner (top) covered the speed text; after a broken glass the text was garbage;
and the replay's A+C hint should look like the score box (EDM 1 / STL 0). Cause of the
garbage: the glass shards load into tiles $3EF.. (the r27 text). VRAM check over 80 saved
states: window rows 14-21 ($F700-$FAFF) hold game tile data, rows 22-23 ($FB00-$FBFF, tiles
$7D8-$7DF) are never shown and empty in all of them. shotmeter.s now composes the two lines
with a 3x5 font, two characters per tile (8 tiles, shotmeter.py), in RAM $FFE900 (statistics
browser RAM, free in play; glyph indices at +$100) and queues a DMA of the 256 bytes to $FB00
every frame while shown (a5 queue in the crowd.s frame), so even a borrowed tile comes back
at once; each line on crowd.s's black 32x8 plate; bar at y 56 (below the GOAL / Face Off
banners). Replay hint: replaytitle.py draws "X+B SKIP" pixel by pixel in the score-box style
(56x16: frame palette 0 5/6, black plate, 6x7 blocky letters colour 3). Measured: text
steady at the stands; hint shown in the replay. PASS critical 10, critical_flash,
season_navigation 18/342, finished_season 9/108, menu_team 18/240, replay 23/24 distinct;
four_goalie 080-dive remains the r27 fixture artifact. SHA256
09b4508be92d9c650de53166615975d4694962ebd9152c3024ea361aa312cf3f, CRC DA83A102.

## Shot power meter with km/h / mph; Difficulty Easy (2026-10-26-r27)

jdc: a small meter top right for the slapshot charge (full = red blink = shoot), the shot
speed under it in mph and km/h, honest rink scale, crits may beat the record; and a third
difficulty, Easy: meters always full and 100 % crits on (forehand) slapshots.
Charge: native windup animation $7FC / $92E, counter $5A (< $10 charging), BEDE 15 -> 34
(+~1 per frame, full after ~18 frames); release power = charge x (shot power rating + 20) x
0.32 (rating $6C through $15730 = x energy). Measured releases: hold 6 -> 236 (~95 km/h), 12
-> 326, full -> 382 (148 km/h); backhand 303; crit +25 %. Speed: |($B772,$B774)| / 3842 = rink
units per frame (checked against positions); centre line - goal line 262 units = 89 ft =
27.13 m, 60 frames/s: km/h = V x 763 / 2^17, mph = V x 237 / 2^16. Hardest normal shot
(power 30, full energy, full charge, forehand) 148-153 km/h / 92-95 mph; crit 176-184 km/h /
109-114 mph vs Chara's 108.8 mph (2012 NHL All-Star Skills, Guinness).
shotmeter.s $2ED000: +0 logic (crowd.s frame): the pads' own players (C320.., sides C328..)
winding up with the puck -> SHOOT/CHG/LIVE; on the loose puck with BED8 = shooter: integer
sqrt, two 8-tile text lines written to tiles $3EF.. (empty in play at every camera position;
$3BC is used by the crowd graphics near the stands), SHOW 240 frames. +4 draw (trail.s
end-of-list hook, 80-sprite limit): bar at x 280 y 8 with crowd.s bar tiles (white; full:
red, blinking only while winding up), two 4x1 text sprite pairs under it. RAM $FFF640..
(magic 'SM', cleared by crowd.s gameend). Font: shotmeter.py (5x6 glyphs, outline).
Easy: menu row 9 values Normal / Hard / Easy (DIFF 0/1/2; count 2 -> 3 at $2095AE/$2095C2
+18, text table $209700). crowd.s frame: DIFF 2 mirrors Hard (humans +10 % full bar, CPU -10
% empty); critroll: Easy + human -> threshold 1000; new +32 easylookup replaces bsr.w lookup /
tst.w d0 at $2FF45A so a human with Crit 0 still reaches the roll. Measured: shooter with
Crit 0: Normal 382 (no crit), Easy 477 (crit, red puck); Easy bars full for WAS, empty CPU.
PASS critical 10, critical_flash, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font. verify_four_goalie 080-dive-g5-p1-n4 ('no native catch')
fails only with the meter's end-of-list sprites present (a no-draw variant passes all 88;
the meter does not write game RAM): a cycle-sensitive dive fixture, like the r23 ones.
SHA256 109cfa631878ea9435d641b6e33f61cd42622c30f5511ba42a269d68242d73b7, CRC 54E6F043.

## +3 crit points; co-op passes reach a human teammate within +-90 deg (2026-10-26-r26)

jdc: crit chance +3 % overall, and better passes between the human teammates.
Crit: crowd.s critroll adds 30 to the RNG(1000) threshold after the meter scaling (crit
rating 0 still never crits: critical.s skips the roll). Measured 10000 shots: rating 100
1302 (13.0 %), 50 818 (8.2 %), 0: 0. Team Roster shows the plain rating (critshow
unchanged). verify_critical expects (rating + 30)/1000.
Passes: the native scan ($BCF8) only considers teammates within one direction octant of the
pass direction ($BEDC; cmp.l #$10000 on the squared octant difference at $BD3A); the base
hack scorer $2FF000 (and its fallback $2FF180) then give a human receiver score 0. A buddy
a bit off the aim lost to a CPU skater straight ahead. passcone.s (base hack free bytes
$2FF02A) replaces the compare: two octants (+-90 deg, $40000) when passer and candidate are
both human. $2FF01C/$2FF1E2 moveq #0,d2 -> lsr.l #8,d2: humans stay far ahead of CPU
skaters, the nearest human wins among several. Staged test (P1 with the puck holding
Right, CPU teammate straight right, P2 at ~70 deg): buddy above: r25 and r26 aim P2; buddy
below: r25 aims the CPU skater, r26 aims and delivers to P2. PASS four_goalie 88, critical
10, critical_flash, season_navigation 18/342, finished_season 9/108, menu_team 18/240
(goalie_request clear-11-p2only remains the r23 timing artifact). SHA256
030d4dca53de8aa07f428a713f7dbfbfede7c5257d196649a8b7104ee620d951, CRC FDD66E23.

## Replay: "X+B SKIP" hint and no Options box on exit (2026-10-26-r25)

jdc: the replay's own menu (B Options / C Reverse Angle / START = RETURN TO GAME, dimmed
screen) flashed after REPLAY; and a smaller skip hint in the same font. Exit: replay.s state
$A503 used Start twice (first opens that menu, $A33E -> $A4A8, second leaves); now it sets
$C2F6 bit0 (menu open) before the single Start, so the native loop takes $A358 -> $A39E and
leaves at once, also for the A + C skip. Hint: replaytitle.py renders "X+B SKIP" 56x16
(7x2 tiles, same font and chrome bands) into the box's own 14 tiles (B026, reloaded from
ROM by every replay); layout REPLAY rows 0-2, hint rows 3-4, columns 2-8 (+ two cleared
box cells), all inside the rows 0-4 the game redraws on exit. Measured: replay snapshots
show both; the end and an A + C skip fade straight to the game (no box). PASS four_goalie
88, critical 10, critical_flash, season_navigation 18/342, finished_season 9/108,
menu_team 18/240. SHA256 9f6eb8c1e2081702b5b5815c315874d6a276ff90838d8d09f81433f0ef728294,
CRC F16CC862.

## Four pads: Y takes the goalie while a teammate skater has the puck (2026-10-26-r24)

jdc: in 4-player co-op Y cycled skaters instead of taking the goalie when he had no puck
(B from the goalie back to a skater worked). fourgoalie.s reader/handler treated the WHOLE
team's possession as "Y = B" (yoldb/yasb, handler no_y); with r22's fast B switch that B
switched skaters. Now Y = B only when this pad's player carries the puck or the pad's own
goalie holds it (Codex's ypass outlet); a teammate skater's puck, the opponent's or a loose
puck: Y takes the keeper (reader ywant; handler compares B7AA with D6 and the same-side
goalie). Measured (4 pads, Y then B per pad): r23 Y switched skaters with a teammate's puck;
r24 Y -> goalie (teammate/opponent puck), B -> skater, Y with own puck = pass. PASS
four_goalie 88 (incl. 8 ypass), critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240; the two r23 fixed-frame artifacts remain
(season_slots full84 playoff game, goalie_request clear-11-p2only). SHA256
1825b5e48711e287025989c2b9c95323a2d899dd18d88e08242a577257627659, CRC C71CE1C9.

## Big "REPLAY" title on the automatic replay (2026-10-26-r23)

jdc: the grey box with letters (native VCR controls) should be a big REPLAY in the NHL'94
logo style. The box is window-plane art ($F000, 64 cells: rows 2-4, columns 2-9, tiles
$178..$185 = B026, palette 0, priority) that $9FD0 loads from $BB4F6 at every replay start
and the game redraws (its whole panel) on exit. New replaytitle.s at $2EC000 (code + 27
tiles generated by replaytitle.py: Open Sans Condensed ExtraBold Italic, 72x24, 1 px black
outline, silver bands in in-game palette 0 greys 1/3/5/6, black 4), called by replay.s on
every playing frame (ARST $A502): first frame copies the tiles to the crowd meters' VRAM
($FC20, tile $7E1); every frame writes window rows 1-3, columns 2-10 (priority, palette 0)
and clears the box shadow row 4. crowd.s `inreplay` (ARST $A502..$A506) skips the meter
sprites and their tile re-upload; replay.s clears TICK after $9FD0 so the meter tiles come
back next frame. The pause-menu Instant Replay is unchanged. Measured: REPLAY on every
replay snapshot, meters hidden during and back after it, HUD normal.
Regressions: PASS four_goalie 88, critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. Two fixed-frame fixtures shift with the extra
crowd.s cycles (proved with a variant whose inreplay never reports a replay: same result):
verify_season_slots full84-final-and-playoff-game (the poked 2-0 at frame 9000 now gets its
automatic replay before the final whistle, +1000 frames; with later Start presses the result
and password are recorded, 8 bracket wins) and verify_goalie_request clear-11-p2only (the
trace samples the transient pass descriptor $F058 inside the pass frame 111; same pass, same
receiver at 184, controls unchanged). SHA256
c00fd040d566ee13d1720ade44bafe668cdd8d7d720a774422c2482b6be69ebd, CRC 986E7996.

## Four-pad B switch; Fighting / Difficulty locked in a season (2026-10-26-r22)

jdc: Difficulty and Fighting could be changed after a season game; with four teammates B
did not switch to the free skater, nor from a Y-selected goalie. Lock: r18 had left rows
>= 8 (Fighting d7 = 16, Difficulty 18; Line Changes is 14) open after the freeze whose real
cause was the slot restore clearing D046 (fixed in r18 slots.s); r15 had left Difficulty
open on purpose. seasonmenu writehook now applies the normal FORCED rules to every row:
editable before the first game, locked once the season runs, in season playoffs (Players
excepted, r20) and completed seasons. Checked: mid-season Right on Difficulty/Fighting
keeps Normal/Off with and without the adapter, menu responsive (Play Mode 0 -> 2), two
full adapter Play Mode cycles; before the first game Difficulty still changes.
Switch: with the adapter and co-op sides fourgoalie.s early sent B to the native handler
(nothing for a non-carrier; the native held-B switch is off in co-op so Y takes the
keeper). New `fast` in fourgoalie.s early, after the outlet request and the own-goalie
pass-in-flight check: on a new B press when this pad's player is not the carrier and no
pass is under way, choose the nearest (puck + velocity, as switch.s) skater of the bank's
side that is not a goalie, not human, not down/unavailable, assign with C0BC/C0DA (bank
offset 0/2; P3/P4 copied back by the native swap), set BHELD for the physical pad, rts.
Measured (4 pads, 12 B presses): r21 1 switch; r22 9 switches, the 3 others are the
carrier passing (2) and an outlet request while the goalie held the puck (1). Pad 1 as
human goalie: B -> skater 3 (r21 stays goalie). PASS four_goalie 88, goalie_request 41,
season_slots 20, season_navigation 18/342, finished_season 9/108, menu_team 18/240,
menu_font, critical_flash. verify_four_lifecycle fails at slot1-pads3-fast-reboot on r21
too (side check after reboot; predates r22, likely the r20 per-season Players change).
SHA256 3f5aa9a5c867519b7ededd4d3ddfcfdc05ec9f4c14a8a3c830ba1d35575ac9aa, CRC 9F8518B5.

## 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.

## Main menu froze with 4-Way Play when switching Play Mode (2026-10-26-r18)

jdc: with both ports on 4-Way Play, browsing Play Mode froze the menu. Reproduced on his
save: Mini -> New Playoffs -> /7 -> Shootout -> See Old Full Season, then no input at all
(cards still animate). Cause: slots.s restore reads the slot's $D046..$D087 snapshot, and
$D046 is the 4-Way Play hardware flag; the Full slot was saved without the adapter, so
D046 became 0 and the menu read the pads the non-multitap way (no input), while the menu
rewrote rows 8/10 every frame (writehook ~3 calls per frame; a locked slot blocked the
game's correction of a saved Line Changes Auto). Fix: restore keeps the live D046 around
the read; seasonmenu writehook never locks rows >= 8 (Line Changes, Difficulty, row 10)
(replaces the Difficulty-only exception). Measured: two full Play Mode cycles respond
(Mini, Playoffs, /7, Shootout, See Old Full, New Light, Mini ...). Diagnosis with private
debug builds (writehook/restore counters at $FFF0FC/$FFF0FE, not released). PASS
season_slots 20, goalie_request 41, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font, critical_flash. SHA256
8e0b1468026afea2401e31faa9e181844684f860c6d55bcbf3b8af3d4f661f5c, CRC B1E8E59E.

## Three / Four - Teammates: the season's first game kept no team (2026-10-26-r17)

jdc (r16, 4 players): after the first match the main menu hung and said Regular Season.
Reproduced on his save: season.s starts a new season from the first game's Players value
and knew only 1 / 3 (home) and 2 (away); 5 / 6 went to "just record", leaving the header
(1, 0, 0, 0, 0, 3): no team, length 0 -> the menu showed New Season Mini 1/84 and the
season state was broken. Now Players 0 / 4 record only, 2 = away team, everything else
(1, 3, 5, 6) the home team (shorter code). slots.s set-up kept only 3 (and 6 -> 3, the
playoff-state Teammates), so Three became One: Players 5 / 6 now stay when mtap TM is 3 / 4.
Measured: 4-player game won 3-0 -> header (1, 31, 1, 8, 0, 3), reboot shows Continue Mini
2/8 at PIT (Players back to One: TM is RAM only). Also: the game detects 4-Way Play only
when PORT 2 is set to it (port 1 alone: D046 = 0), and only at power-on/Restart.
test_core.py: GPGX_DEV2 sets port 2 separately. PASS season_slots 20, goalie_request 41,
season_navigation 18/342, finished_season 9/108, menu_team 18/240, menu_font,
critical_flash. SHA256 5d69777a2bf7c37bd2cced37f9442b396709dbd9b34fe5240f501828ebce78ef,
CRC 9CF8708E.

## Three / Four - Teammates with 4-Way Play (2026-10-26-r16)

jdc: three, then four players on the same team. Native (D046 = 4-Way Play): Regular Season
Players 1 One-Home, 2 One-Visitor, 3 Two-Teammates, 4 Two-Head to Head, 5 Three (pads 1+3
v 2), 6 Four (1+3 v 2+4), 0 Demo; playoff modes only One ($0B) / Head to Head (7) /
Teammates (8). Side assignment $17E42 (clears C328..C32E, tables $17F52/$17F72/$17FE6,
exit $17FE0). New mtap.s at $2EA400: +0 hook $17FE0 (jmp): with D046 and TM ($FFF07C; or
Players 5/6 in Regular Season) >= 3, pads 2..4 take pad 1's side (Four: all four; Three:
pad 4 none); +4 coop (Z for Players 3/5/6) replaces seasonmenu's five cmpi.w #3,PLAYERS
(same 6 bytes); +8 Players row: One / Two - Teammates (/ Three / Four with D046), sets TM.
Strings "Three"/"Four" -> "Three - Teammates"/"Four - Teammates" (F7EA4/F7EFC/2093A0/2093F8,
F7EBA/F7F12/2093B6/20940E). crowd.s defaults clears TM with DIFF (clr.l $FFF07A).
Measured (GPGX_DEV=1281): row cycles One/Two/Three/Four - Teammates, Game "(co-op)"; in the
game sides 1/1/1/1, pads 3 and 4 move their own skaters (Right held: x 9 -> 118, -68 ->
114). Native behaviour remains with D046: switch.s / outlet.s / goalie_ai.s keep native
paths (pad 1 takes the goalie when he has the puck, Manual); occasionally pad 2 picks pad
4's skater for one frame (native auto-select). TM is RAM only: after a power cycle in the
season's playoffs, Four falls back to two humans until Players is set again (row locked
there). RetroArch: port 1 and 2 device "MD Joypad 6 Button + 4-WayPlay" (1281) only when
playing 3-4; the jdc remap stays 513. PASS goalie_request 41, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font, critical 10, critical_flash. SHA256
ed854658d2d224951a9cfd6efd3ea0b3ed99c79ebff0d1f74fbb3c83791a9b17, CRC 017DE68C.

## Main menu picture stays grey; menu music back (2026-10-26-r15)

jdc: the r14 menu picture changed colour (orange with Philadelphia away) and the menu music
had been missing for a long time. Palette 2 is the away logo's palette (palette 1 the home
logo's); the r14 check happened with Los Angeles (black/grey) away. Checked 10 team pairs:
palettes 0 and 3 never change in the menu. menubg.s now maps the intro's levels onto palette
0's greys (000=4, 049=14, 092=7, 0DB=6, 16D=5, 1B6=3, 1FF=1; the missing $124 as a 2x2
checker of 0DB/16D, two remap tables alternating per pixel row) and sets the cells to
palette 0. Music: the native title screen ($FF042, skipped by intro.s since r10) started
the menu music; menubg.s now plays $79 via $11156 when no channel in $D3D4 is on a music
sequence ($1A304's test: pointer >= ($2C708)). Measured: grey picture with 8 team pairs;
GPGX_WAV RMS > 2000 every second from the menu on. PASS menu_font, menu_team 18/240,
season_navigation 18/342, finished_season 9/108, goalie_request 41, critical 10,
critical_flash. SHA256 8cee7dfc68b2b81a3e2f330a02d923b442ef6f68f60f3a23c0acf675fb9a1041,
CRC EDCAE239.

## Main menu background = the Season Edition start-up picture (2026-10-26-r14)

jdc: the start-up picture should also be the main menu's. Menu set-up $F8168: background
asset $1F0000 via $1169A at $F8252 (plane $C000, 64 cells wide, tiles from d4 = 243, one
tile per cell: 1076 photo cells palette 3, 44 cells palette 1 behind AWAY/HOME). Palette 3
is shared with 29 menu sprites (logos, faces), so it stays; palette 2 already holds the 8
grey levels ($000, $049 .. $1B6, $1FF at indices 6, 15..10, 5) and never changes in the
menu (checked across player-card changes). New menubg.s at $2EA000, hook $F8258 (move.w
d4,$D432 / addi.w #$24,d4 -> jsr / nop): for every palette-3 cell the intro picture's tile
for that cell ($2A0000 data, levels 1..8) is written to the cell's VRAM tile through a byte
remap table, and the cell is switched to palette 2. Measured: menu snaps show the shield
behind the panels; text readable. PASS menu_font, menu_team 18/240, season_navigation
18/342, finished_season 9/108, goalie_request 41, critical 10, critical_flash. SHA256
94fb334a0c7eebb491f04db7a0575d2d9e97b7c52dafe057c53ddd6683043038, CRC F86F9366.

## Goalie outlet: a B tap during the catch is queued, not a switch (2026-10-26-r13)

jdc (Goalie Ctrl Manual): passing out from the goalie sometimes switched skaters instead
("works mostly, but sometimes"). With the puck owned by the goalie every tested tap passes
(single B at frames 151..191 after ownership: 21/21). Failure window: the puck is in or at
the goalie but $B7AA is still loose; outlet.s request only accepted owner 5/11, so a tap
that ended before ownership was an ordinary switch.s switch and no outlet followed.
Reproduced by holding the puck loose in the glove for frames 150..160 and tapping B (2
frames) at 153: r12 switched to skater 3 and the goalie kept the puck.
Fix: outlet.s request: a loose puck within 24 px (x and y) of this pad's team goalie (slot
5 / 11) counts as his, so the tap is queued in ASK and consumed (no switch); goalie_ai.s
($2FE400 pickup guard) keeps ASK when the picker is that goalie (ASK+2), other pickups still
clear it. Measured r13: no switch, ownership at 161, outlet at 163, received at 199.
Side effect: B does not switch skaters while a loose puck lies within 24 px of your goalie.
PASS goalie_request 41, critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. SHA256
27d802ffa84516bbb4714ded125ef2e18eb81b2bbe96cf633f149241e766ed6f, CRC 576CB7E8.

## Scoring leaders: top 100 with pages, tab/sort in place (2026-10-26-r12)

jdc: the leaders page redrew everything on a tab change and showed only 18; Down should
scroll to place 100. seasonend.s moved to $2E7000 (limit $3000; the 4 KB at $2E3000 were
full); $2E3000/4/8 keep bra.w stubs (4 bytes each, a 6-byte jmp table broke +4/+8), so
$172E4, simulate.s and finished.s are unchanged. LDR = $FFE860, 100 x 8 bytes ($E800..$E85F
slots.s META/HEAD, $EA40.. leaders.s scratch for its own screen only); entries inserted by
bubbling only the new last one (sortlast). LPG $FFE538: Up/Down pages of 18 (no page past
the list), tab/sort rebuild the list in place and reset the page; rows past the list are
printed as spaces (see-through on the front plane). Rank 100 prints "100 " and drops the
space after the padded name. Footer "U/D page A tab C sort B teams Start".
Measured on jdc's finished Full: pages 1-18, 19-36 ... 91-100 (blank rows after), extra
Down no-op, A -> Regular Season page 1, C goals, Start -> menu, SRAM unchanged; season end
through the stubs reaches the playoffs. PASS finished_season 9/108, menu_team 18/240,
season_navigation 18/342, critical_flash. SHA256
6a6775ed0335d8b01097ff6fc23cff2f138fc7979674a3f8ff51a42f6e4cb36d, CRC 9D03133D.

## Season Complete menu: Scoring Leaders choice, Start returns at once (2026-10-26-r11)

jdc: after See Results, Start should go back one step at once (it went to the leaders
first, so two presses), and the menu should offer that season's scoring leaders.
finished.s: four choices (See Results / Scoring Leaders / Start New ... / Back), rows
11/14/17/20, marker cells 4 (BG). It sets seasonend.s DIRECT ($FFE536) = 1 (club page) or 2
(leaders first) around jsr $2E3008 and clears it after. seasonend.s players: DIRECT 2 jumps
to the leaders; with DIRECT set, Start on the club page returns at once (B on the leaders
still opens the clubs); the end-of-season flow clears DIRECT and keeps club page -> leaders.
verify_finished_season.py: Start returns to the menu, Down + C opens Scoring Leaders, menu
indices +1 (SEL 1 after leaders, 2 = Start New). seasonend.s has ~13 bytes left.
PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342, critical_flash.
SHA256 cfa26060986cdd9d26114e85a2e1d39869064e52b50782501498cabe9721a4f3, CRC 1257FF5C.

## Season Complete / statistics: no dark boxes, dimmer background (2026-10-26-r10)

jdc: remove the dark area behind the text and darken the background a little. The text was
printed into the BACK plane ($C000, B02E = 4) where table.s's setup draws the SPORTS
pattern (palette 3, e.g. $6336): each glyph replaced a pattern cell, so its colour-0 pixels
showed the dark backdrop. Planes on these screens: B004 index 0 = $E000 front (empty,
$07FF), index 4 = $C000 back, index 8 = $B000 window (off); 128-cell rows.
Now finished.s line and seasonend.s out print to the front plane (B02E = 0); highlight
reads the front plane ($E600); the menu marker cell is in $E000. finished.s +24 setup =
$2FC004 then palette 3 colours 1..15 at 3/4 (each of B/G/R: n - n/4, even) in the fade target
buffer $BD8A..; seasonend.s calls it instead of $2FC004 (same size). Half brightness was too
dark. PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342,
critical_flash. SHA256 9f6cdda867614d88064a1d77c7668ab235c1222440a14d0d4584d26ea2a54a2b,
CRC 2C153741.

## Statistics: team / tab switch in place (2026-10-26-r9)

jdc: browse between clubs as quickly as between G/A/PTS. seasonend.s club page: a team or
tab change (teamkeys) is written over the page (loadteam, build, ppage; the banner is a
direct nametable write via $1169A) when the old page was page 1 with 18 rows and the new
list also has >= 18 skaters; page 2 (goalies) or a shorter list still gets the full redraw
so no stale rows remain. seasonend.s hit its 4 KB limit (4118 bytes): hush moved to
finished.s +20 ($2E6014). Measured: Dallas -> Detroit -> Edmonton, tab A, Left, page 2 +
Right all correct, no fade; RMS 0 on the statistics. PASS finished_season 9/108,
menu_team 18/240, season_navigation 18/342, critical_flash. SHA256
281d6305f7d85ad1a88b49b8cd909b546dbae85bae3d7c57ad8e1f0b71d5ed10, CRC 5EA0AD12.

## Statistics: sort in place, no music (2026-10-26-r8)

jdc: the statistics page should not redraw when switching G/A/PTS, and the music should
stop there. seasonend.s: C on the club page now does nextsort/build/ppage only (on the
leaders page nextsort/leaders/lpage), no $2FC004 set-up and no fade; other keys as before.
Music: every fadein pushed sound ID $79 to $11156 (play sound; IDs >= $30 are music), which
restarted the menu music on each page; fadein now passes -1 (skipped), and players: calls
hush (all 8 channel pointers at $D3D4 = -1 as $1A304 does for the music channel, then
$1A264 voices off). $1A264 alone or $1A304 did not stop it. Measured with GPGX_WAV: RMS 0
for every half second on the statistics pages; music returns on the Season Complete menu.
The leaders page needs a few frames to rescan the league after C (no blackout).
verify_season_browser.py cannot be used after seasonend.s edits: its state was saved with
the CPU inside the old browser code (r8 fails at the first case; r7 already failed at the
final DONE marker). Instead: native cold-boot flows (club/leaders sort, SRAM unchanged) and
PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342, critical_flash.
SHA256 9cd0daf7f296018e1720f4dd7aced24a64296b23ba99a6fe1534edac97119957, CRC F0BDF3CA.

## Freeze on "Resume Game" before the faceoff: sprite list overflow (2026-10-26-r7)

jdc: Continue Full Season 84/84 at FLA, Start Game -> pre-game menu shows "Resume Game",
clock blank, nothing responds (Codex saw the same on 2026-10-25). Not the save (checksum,
83/84 fine). Reproduced headless from his save; --no-crowd starts. The native list builder
($15EC0: a6 = $C018, d6 = 1, ... $15F06 end, $15F20 C2E8 = list words) has no limit; the
game intro (both teams skating in) builds ~90 sprites, up to $C2E8. crowd.s drew its 8 bar
sprites FIRST (r24 order), so the intro list ran over $C2E8..$C2EF: C2EA/C2EC = sprite
words (e.g. $075B/$EB95), C2EC bit0 = pause -> frozen menu. TL ($FFF634, menu RAM) could
also hold menu text, drawing afterglow flames first as well.
Fix: crowd.s +28 draw = the bars, called from trail.s's end-of-list hook ($15F06) and only
while a6 < $C298 (80 sprites, the VDP limit); trail draw (start of list, on top of the
goalie) only while LIFE ($FFF060, set by a real crit only) or with GOK ($FFF636 = $A5C3,
set with TL by a real crit, cleared when TL ends and in gameend). Note: $C466 is 0 during
period 1 (not a usable "in play" test).
Measured: his save starts the game (clock 300 -> 226 by frame 4500, C2EC clean); bars in
play; forced-crit shot flames identical to r6 including the afterglow over the goalie;
replay 23/24 distinct snaps. PASS: season_navigation 18/342, critical 10, critical_flash,
finished_season 9/108, menu_team 18/240. SHA256
589bc62b698d97ca33415beddaacd98bbba779c74fd1209fb71f7a769571445d, CRC DFCCBFF7.
Evidence: work/sprite-overflow-20261026/.

## See Old Season opens the Season Complete menu first (2026-10-26-r6)

jdc: wants to land in the Season Complete menu directly. finished.s view now reads the
header and goes straight to the menu; "See Results Again" is "See Results" and calls the
browser ($2E3008), returning to the menu. verify_finished_season.py presses C once at
frame 1500 (See Results) before its old browser checks. PASS: finished_season 9/108,
menu_team 18/240, season_navigation 18/342. SHA256
99dea2ea9827bb07e489dfeb8fa1a84bad46d50c6aadebf4120edd78c7e15c08, CRC 3F6A942D.

## Season Complete menu moves only the marker (2026-10-26-r5)

jdc: the menu after See Old Full/Light/Mini Season (See Results Again / Start New ... /
Back to Main Menu) felt slow, as if redrawn for every choice. It was: finished.s Up/Down
jumped to redraw (screen setup $2FC004, all lines, fade). Now Up/Down put back the old
marker cell and print "*" at the new one, no redraw/fade. The SPORTS background shares the
text plane ($C000, 128-cell rows: $11952 row << 7, words), and any printed glyph (space,
"@" = $7FF + font base) leaves a dark box, so keepbg reads the three marker cells' words
from VRAM before the first marker (BG = $FFE5C0) and unmark writes them back. Untouched
cells hold pattern words such as $632C. Measured: snaps after Down/Down/Down/Up show one
marker, clean background. Regressions PASS: finished_season 9/108, menu_team 18/240,
season_navigation 18/342, critical_flash. SHA256
18888df745faf4b97d265ff465f42364d3e1ab8f4bf6469ad5ec55e2b0ffcf46, CRC C3685531.
Evidence: work/finished-menu-fast-20261026/.

## Replay played a still picture; finished menu showed your club twice (2026-10-26-r4)

jdc: the automatic goal replay started but only showed a still image; on a finished
season's menu, Right on the Game row gave Edmonton vs Edmonton. (His missed playoffs were
legitimate: Mini 4-3-1, 9 pts, 19th of 32.)
Replay: the native Instant Replay ($9FD0) toggles play on a NEW C press (d1 bit5, $A25E:
bchg #1,$C2F0), not on C held (d3). The pad hook held C in d3 only, so the buffer stayed on
its first frame for the whole 540 frames. Now frame 2 of our replay sends C as a new press
(d1/d2/d3 = $20) and nothing after; the native loop plays the buffer (rewound at entry,
$A02A) to its end. Measured: snaps every 40 frames during the replay all differ (r3: 13 of
17 identical), exit Start/Start and play resume as before.
Menu: finished.s lockmenu set Home ($D04C) to the saved club but left a stale Away; if
Away equals Home it now takes the next club. r3 vs r4 with Away poked to Home: 0a/0a vs
0a/0b, logos FLA/EDM.
Regressions PASS: season_navigation 18/342, critical 10, critical_flash, finished_season
9/108, menu_team 18/240. SHA256 01e3e46332cfdf5ca27a14728e1d827e90aa72b46acb9975fd67a98d1c8f7065,
CRC 01D931FA. Evidence: work/replay-play-20261026/.

## Start Game hung after viewing season results (2026-10-26-r3)

jdc: deleted the finished Mini, started a new one (Edmonton vs Montreal) and the game hung
when starting the match. Reproduced from his pre-delete save in one session: See Old Mini
Season -> results/leaders -> Start New Mini Season (X+B) -> Game row to MTL -> Start ->
"Resume Game" pre-game menu, period 0 / 5:00, frozen; from a cold boot of the deleted
save the same match starts. Cause: replay.s kept its state in $FFF738.. (crowd block),
which the results/leaders/team-stats screens overwrite with text; a stale nonzero ARST made
the pad hook at $A41E (also used by the pre-game/pause menus) feed scripted input, so the
menu never saw the player's buttons. Fix: replay states are $A500..$A506 (anything else is
idle, the pad hook scripts only $A502..$A506), and crowd.s valid() (first frame of every
game) clears onetimer PEND $FFF736, replay $FFF738..$FFF73F and trail HN/HT/TL
$FFF630..$FFF635 (stale afterglow could have drawn flames from menu text).
Same flow now starts Montreal vs Edmonton and the clock runs. verify_finished_season 9/108,
verify_menu_team 18/240, verify_season_navigation 18/342, verify_goalie_request 41,
verify_critical_flash PASS; auto replay still runs ($A502..$A506).
Rule: never keep in-game state in $FFF100..$FFF7FF without a magic/validity check and a
reset at game start: menus use it as scratch.
SHA256 b6fb71635631a24f1c84c81a4802ff0ccfc821e891cb42ae63a5fe9a2c359239, CRC 4009ADA6.

## Longer crit flames, crits in shootouts (2026-10-26-r2)

jdc: the red crit animation was too short and did not show the flames; does crit work in
shootouts? Measured: a natural crit flash lasted 18 frames (goal / save ends it); the trail
needed the puck's sprite in the 64-sprite list. trail.s now takes the puck's position from
its x/y/height and the camera (screen x = x - $BD1C + 285, y = $BD18 - y + 233 - height,
sprite coordinates; constant over 35 samples), list search only if $C2EC bit7; six flames
(2..7 frames back: big, big, medium, medium, small, small) and a 24-frame afterglow after
the flash ends (the tail dies out: 6 -> 0 flames). RAM $FFF634 TL. Code grew past 256 bytes:
tiles moved to $2E4200.
Shootouts: the crit roll and +25% power always worked, but $C2EA bit0 (clock stopped) is
set throughout a shootout, so critical_flash ended the red puck after one frame.
critical_flash.s now skips that check when $C2FA bit0 (shootout); possession still ends it.
Shootout test (native Shootout mode, forced crit): flash 127 -> lasts about 160 frames,
red puck and flames up to the goalie.
verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS;
verify_shootout: 12 pass, then the known playoff fixture case (also on 2026-10-26).
Site: version and Light/Mini now in the header, goalie section updated to today's controls.
SHA256 78b36c6e3f9a925a49d92d756ad40174f7d9b494004b04544c2fe7d258ef77df, CRC 3DBBCA6E.

## 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.


Technical reference for further changes (routines, RAM, free space, hook pattern, test rig): see `HACKING.md`.
Picture of where the blue-line bonus applies: `blalinjeskott.png` (Swedish labels) and the English version on the site.

Started 2026-10-13. Installed ROM: `~/.config/retroarch/roms/claude/NHL 2026 - 94 Edition v5.1W.bin`.
Base (unpatched): `NHL 2026 - 94 Edition v5.1W.bin.orig.bak` in the same folder (md5 8f863ecb108c1d213953bada9b98f4f2).
Earlier builds are kept privately in `~/.config/retroarch/roms/claude/work/backups/` (`NHL94-v5.1W-passfixN.bin.bak`, then dated files).




## 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.

## Automatic goal replay (2026-10-25-r25)

jdc: automatic replays after every goal, skippable with X + B (Genesis A + C, like Abandon
Season). experimental/replay.s at $2E5000:
- +0 tick (called every frame by crowd.s): score $C6DA + $CA3E rose (not in a shootout,
  $C2FA bit0) -> 90-frame countdown -> ARST = 1 and set $C2EC bit0 (pause request).
- +4 replaces bsr $7CF8 / bra $7938 at $7948 (main loop pause call). ARST = 1: the pause
  routine's prologue ($10F32, VBlank wait, $1A264, push $C2EC, $7E0E), then the native
  Instant Replay $9FD0 instead of the menu, then the pause routine's own exit $7D66 (pops
  $C2EC, restores the game screen). Otherwise the ordinary pause menu.
- +8 replaces tst.w $D046 / bne.w $A434 at $A41E (the replay's pad read: $11340/$11358
  give d0 dir, d1 new, d2 changed, d3 held). During the auto replay it returns "hold C"
  (play) for 540 reads (the buffer takes about 520 frames), then Start (options), release,
  Start (return to game). A + C held on either real pad jumps to the Start sequence.
- The replay starts rewound (native $A4F6 loop), A/B/C = rewind/stop/play, Start = options.
- RAM $FFF738 ARST, $FFF73A ARCNT, $FFF73C CNTDN, $FFF73E LASTG; crowd.s gameend clears them.
Tests: poked goal -> replay 402..1073, clock frozen during it, then resumes; A + C at
frame 520 -> back in play at 605. Natural Mini run: 4 games, every goal (periods 1-3 and
late in the third) replayed, games and season (4/8) continued normally.
verify_goalie_request 38, verify_critical 10, verify_critical_flash,
verify_season_navigation 18/342 PASS.
SHA256 cffacb9192269424bdcb136e60db64f9ab10d0bb0be5648376eebcee545c1f84, CRC 7B803E38.

## Crit flame trail always shows (2026-10-25-r24)

jdc: the crit trail only showed sometimes. Cause: the trail sprites were appended at the
END of the sprite list, which already holds about 51 native sprites + 8 meter sprites;
the native list stops at 64 ($40, RAM after $C218 is in use, no room to extend), so the
trail was often dropped. trail.s now has +0 record (end hook $15F06: puck position into
HIST) and +4 draw, called by crowd.s at the START of the next list, drawing HIST 2..5
frames ago. The flames now draw in front of skaters and always get a slot.
verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS.
SHA256 46db9ac09de9a90f866e4f0ecb8eef60e47e4c3f420938d5d77180be4af7d237, CRC 6BD98C96.

## Flame trail behind the crit puck (2026-10-25-r23)

jdc: cool speed streaks / flames behind the puck on a crit. experimental/trail.s at
$2E4000 (tiles at $2E4100), hook $15F06 at the end of the sprite list build
(cmpa.w #$C018,a6 / bne.w $15F12 -> jsr / bne.s $15F12; the routine ends with the same
compare). While $FFF060 (critical_flash lifetime) is set it finds the puck's sprite in
the list (tile = puck +$12; the shadow is +1), keeps 9 screen positions (RAM $FFF600..$FFF633,
menu-only RAM) and appends four sprites at 1/2/3/4 frames ago (big, big, medium, small)
behind everything: palette 1 colours A (crit red), C (orange), 6 (yellow-orange core).
Tiles $7FC..$7FE (VRAM $FF80..$FFDF) via the native DMA queue every 32 frames.
Pitfall: the first try at $FFA0..$FFFF (DMA ending exactly at $10000) wrapped onto tile 0
and covered the whole rink with dots. Tests: forced-crit capture shows a continuous red
tail; verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS.
SHA256 e6d7d5143bdd3352fa6a10a92d89edfcceb8d4599d9abaa80a7937e9e13de087, CRC 88B2C78F.

## B no longer switches skaters while your goalie's pass travels (2026-10-25-r22)

jdc: mashing the pass button while the goalie holds the puck gives no pass and the cursor
jumps between skaters as if switching. Probe (GPGX, gk5-clear fixture, B every 6 frames):
the first B is the outlet request and the goalie passes at once (frame 101 -> 103); every
further B while the puck travels hit switch.s ordinary_switch and flipped control 4 <-> 3
(the pass target stayed 4). Fix: onetimer.s +8 gkflight (puck loose, last passer = a
goalie of your team -> Z, and switch.s Q cleared); switch.s calls it before the ordinary
switch and consumes B (switch.s must stay <= $200 bytes before glass at $2FDB00, now 510).
After the fix control stays on the receiver until the catch (frame 103..135); later B
presses pass/switch natively. verify_season_navigation 18/342, verify_critical 10 PASS;
verify_goalie_request: the known cycle-sensitive clear-5-coop-p2-d10-hold1 (1-frame tap).
SHA256 1a1ce11286b94377e228fbfd939474369c7d8b63920391028c4e3055b92b53cd, CRC 05BAFC54.

## Playoff statistics apart, three tabs, sorting, scoring leaders (2026-10-25-r21)

jdc: see the scoring leaders too; with playoffs, one tab with ALL stats, one with only
the league, one with only the playoffs; if you go out, the playoffs are simulated to the
end in the background so all stats are there; filter goals / assists / points.
- Playoff block: SRAM $0002 "PO", $0010 + 100*slot player rows (slot = place in SEEDS,
  16 = scratch), $06B4 + 15*slot goalies, to $07A2. $0002..$0D1F is free: native code
  only writes the whole 8 KB on a format, User Records are off; the slot banks copy and
  checksum it with the rest. Cleared by playoffs.s setflag(1) (playoffs set up).
- season.s rowload/rowsave/gkput take their offset from postats.s ($2E2000 +0 ROWOFF,
  +4 GKOFF; same size as the replaced mulu/addi): POGAME set -> playoff block. So the
  season's own playoff games now go there, not into the league rows (r19 merged them).
- postats.s +12 others: after each of your season playoff games one game for every
  other pairing of that round; +16 finish: when you are out, every later round plays its
  series to the winners the game has already drawn ($CEF4 holds the whole drawn tree,
  re-drawn each round), one game or best of seven (0..3 losses). Uses season.s
  goals/dist/gksim via season-symbols.inc; checksum via DELTA (fixsum).
- seasonend.s moved to $2E3000 (hooks $172E4 / Simulate exit updated). After the summary:
  your team (banner, tab title, sort, skaters, Up/Down page 2 with goalies) and then the
  league's top 18 Scoring Leaders (rank, player, team, G A PTS SOG). Left/Right = tab
  (Season + Playoffs / Regular Season / Playoffs), C = sort (points, goals, assists),
  Start = next / main menu. ALL = league + playoff rows added on display.
Tests: Mini out in round 1: 15 playoff games (30 goalie GP), league rows/goalies
unchanged, checksum ok; tabs/sort/pages/leaders captured (Thompson 7 league + 1 playoff
= 8 GP). Full best-of-seven champion: 60 playoff games (16 yours + 28 + 12 + 4), league
GP 84 for all, checksum ok. verify_season_navigation 18/342, verify_critical 10,
verify_critical_flash PASS. The font has no < > (footer: "L/R tab").
SHA256 3115bde11904a3a00cb4201fa11cef8f7eff810573aed220d14417f46a803e87, CRC 3693B1E0.

## Co-op game row without at/vs (2026-10-25-r20)

jdc: switching Players One <-> Two - Teammates changed the Game row from "at" to "vs"
(co-op always plays at home), which looked misleading. seasonmenu.s gameinfo now
writes "84/84 FLA (co-op)" when Players = 3 (Two - Teammates); One keeps "at"/"vs".
Tested from a Full save at 84/84 (Edmonton, 83 simulated games): One "84/84 at FLA",
Two - Teammates "84/84 FLA (co-op)", back to One "at FLA".
SHA256 f073a6928cf8e9ade13c003d6ef94fcbe72c4030aa188a651f2d9a0c1d3828df, CRC 85089A9A.

## End-of-season summary and whole-run player statistics (2026-10-25-r19)

jdc: after the Cup (won) or the bracket (out), go on with a button to a screen with
all the statistics of the whole run. New experimental/seasonend.s at $2E1400 (+0 hook
$172E4 after the post-game tree/table and before the $172FA RAM reset, +4 from the
Simulate Match result screen, whose single jmp $172FA seasonend.py redirects).
classify (season view, valid finished season): Regular Season last game and the team
not in SEEDS -> missed (flag 5); the season's playoffs on (flag 2) and the game has
switched back to New Playoffs (D048 2/3) -> out (flag 3, round = $CEEC-1, the game
advances the round before) or champion (flag 4, your score > theirs in this game).
The flag is written once (playoffs.s new +28 setflag), so the summary shows once.
Screens use table.s's set-up (new table.s +4 = setup: background $2000, palettes,
scroll, VSRAM, no window/sprites); the native panel ($9BD8, simulate/teamstats) is
invisible in this post-tree context (wrong palette; $11594 clears VRAM). Season
Summary: banner, record W-L-OTL, points, GF-GA, result; Start -> "Season + Playoffs"
list: skaters by points (as teamstats build), G A PTS SOG, page 2 rest + goalies GP
W GA SA, Left/Right page, Start -> main menu. Start waits release/press (no skip).
A Cup win first shows the game's own Playoff Stats and Champions page (now reached).
Whole-run stats: season.s playoff games of the season (playoffs.s +32 poseason: in
progress, your team, flag 1/2) go through the normal save path (addreal/gkreal +
checksum) without round marking and league simulation; team table rows unchanged.
So teamstats/leaders also include playoff points after the season.
playoffs.s: played no longer reopens finished playoffs (flag >= 3; the post-season
standalone New Playoffs set 3 -> 2 before); spcheck treats >= 3 as over.
Tests (GPGX, work/season-end-test-20261025): Mini win -> Champions page, summary
"Stanley Cup champions", players, page 2 goalies (Thompson 11 GP = 8 + 4); Mini
loss -> bracket, "Out in the first round"; Mini missed via Simulate Match -> summary
"Missed the playoffs" (Vancouver 0-6-2); Full BO7 champion flag 4, goalie GP 84+16.
verify_season_navigation 18/342, verify_critical 10, verify_critical_flash PASS.
Known: after a season the menu still reads "Continue <slot>" (games no longer count).
SHA256 a877c327a8c66674172fce3a72359d24185ee37aeb99494efaf9febb561e7291, CRC C8CC833B.

## Season-to-playoffs test run; finished season takes no more games (2026-10-25-r18)

jdc: test that every season reaches the playoffs, the playoffs run to the end.
GPGX driver (work/season-end-test-20261025/po_hook.py): Simulate Match through
menu input for the league, then real playoff games (clock held at 1 s, score set,
Start outside play), plus fully natural games without any pokes. SRAM validated
after every game (checksum, GP, W/L, scorers, goalie GP/GA).
- Full 84/84, Light 31/31, Mini 8/8 simulated: all checks ok, Continue to Playoffs.
- Playoffs: Mini and Light single games won all four rounds (round 0..4, flag 3);
  Full best-of-seven advanced round by round to the final; Mini/Full loss in
  round 1 ends the playoffs (bracket shown, flag 3).
- After a Cup win NO champion screen appears (Stars of the Game, Exit Game, then
  the main menu with New Playoffs); after elimination the bracket is shown.
- After the season's playoffs the menu shows native New Playoffs; starting it
  sets the season's playoff flag $1210 from 3 back to 2 (standalone and season
  playoff state still interact; to be fixed).
- BUG fixed: a season that missed the playoffs still counted real Start Game
  games (natural Light run reached 45/31, team GP 44..59; Mini 10/8 in r17).
  season.s haveuser now returns when games >= length. r18 natural Mini: 8/8, all
  teams GP 8. The menu after such a season still shows "Continue <slot>" with a
  meaningless "Game 1/8" line (to be replaced by the end-of-season flow).
- Expected, not bugs: natural games show team GF > scorer goals (shootout winners
  add a team goal only) and team GA > goalie GA (empty-net goals).
SHA256 33baa63541d44a71f2e43d88a0dfcb8bcf752611598462794feaece74c7f13ef, CRC BE3CEBD9.

## Buffered one-timer for human receivers; meter scales Crit (2026-10-25-r17)

jdc: in co-op, when P2 presses/holds C while P1's pass is on its way, P2 stops and
the pass slides by. Cause: receiver input $B6C8 (C new press, puck loose, we are
$BEE0) queues action $23 (one-timer) at once; the windup ($C1B8, anim $07FC) stops
the skater and clears $BEE0, and the lead pass misses (probe: speed 0, puck past).
New experimental/onetimer.s at $2E1000 (+0 input hook $B6C8 -> $B6D0/$10750, +4
frame hook replacing jsr $FD78A at $15EF2): while the pass is in flight a press is
remembered in $FFF736 (object+1) and the receiver keeps skating; when that object
owns the puck the frame hook sets +$64 bit3 (native one-timer: power $1F at $C35E),
anim $07FC via $1073A and calls the native shot $C2F2 = immediate full-power
forehand one-timer (crit-eligible: $FFF0B0 set). A press is forgotten when the
pass is cut off. Probe: receiver catches at full speed (3902) on the same frame as
without C, puck leaves at 20264 with slap flag set; hold and mash identical. Only
human receivers (input path); passer-C native one-timer for AI receivers unchanged.
--no-onetimer keeps the native behaviour. NEAR_PX (0) can release early instead.
Crit: crowd +20 replaces move.w d0,d1/move.w #1000,d0 at $2FF462 (rating scaled
by the shooter side's percent before RNG(1000)), +24 replaces jmp $8E4C at $2FF4B8
(Team Roster Crit column, same scaling). Hard WSH -10%: Crit 77->70, 48->44, 79->72.
verify_critical 10, verify_critical_flash, verify_season_navigation 18/342 PASS;
verify_goalie_request still the known cycle-chaotic clear-5-coop-p2-d10-hold1.
SHA256 ff1ec02d17173e9a91496bd8d35ceae557f618ebbdb0a99a0e1fad34cd4507b7, CRC B71C5B22.

## Measured: do crits score more? (2026-10-25)

jdc asked. Two copies of r16 differing only in the crit roll at $2FF470 (bcc ->
nop = every qualifying shot crits, -> bra = never). GPGX hook: from one live-play
state, P1 skater teleported with the puck to x -60..60 and 60/100/140/180 px from
the goal line, other skaters moved away (goalie only), facing 6/7 (forehand), C
held 40 frames, aim none/Left/Right, 40 start delays; 4800 shots per ROM, all
qualifying full forehand slapshots ($FFF0B0 set).
No crit: speed 18364, goal 4.9%, save 76.4%, over 1.0%, wide 17.7%.
Crit:    speed 22912 (+25%), goal 6.2%, save 67.6%, over 1.7%, wide 24.5%.
Goals +1.31 points (+27% relative), z = 2.8. Faster pucks beat the goalie more
often but also miss the net/bar more. By distance: 60 1.4->2.2, 100 2.8->1.4,
140 2.0->7.1, 180 13.4->14.1 (180 is inside the blue-line bonus zone). Artificial
setup (empty ice, one goalie, teleport); real games differ. Rig and raw results:
work/crit-goal-rate-20261025/.

## Team Roster shows the meter effect; reset after every game (2026-10-25-r16)

jdc: with the meter up, McDavid's numbers in Team Roster had not changed; show the
effect, reset after each game. Roster values come from $FA9F8 (18 x rating nibble +
per-player adjustment byte at team block +$1A2, limit 99) and never read the object
ratings. Hook $8D70 (jsr $FA9F8, reached through the crit hook for every non-Crit
mode) -> crowd +12: for mode $0A (Overall and the 0..99 ratings; not Handed, Weight,
Energy, Fighting, Crit) and a2 = $C6CE/$CA32 it shows value + value*p/100 (trunc,
0..limit-1), only while MAG is valid. Game end: $172FA (bsr $169D0 / move #$2700,sr,
reached after every game via $172E0 and at boot, right before the B000..D03E reset)
-> crowd +16, which clears MAG; the meter, percents and stored bases start over and
Roster outside a game shows base values. Test: Hard, Washington human -10%: Overall
Chychrun 76 -> 69, Roy 55 -> 50, Kyrou 70 -> 63 (r15 showed 76/55/70).
verify_season_navigation 18/342, verify_critical 10, verify_critical_flash PASS.
SHA256 b6d72438d540cb6c81c96a4183744f0ebef5040e7a4796b28651fff718124c56, CRC AA7610A9.

## Difficulty Normal/Hard (2026-10-25-r15)

jdc: a start-menu choice Normal/Hard; Hard = the crowd meter is always at the
bottom for human players and full for the computer. Main menu row 9 (User Records,
hidden by fix 11) is now "Difficulty" with values Normal/Hard (strings at $209700,
offset table $2091FE+18). The fix 11 row map at $2FE232 sends row 9 to $FFF07A
(move.w #$2032,d1; no other ROM reference to $F07A) instead of User Records $D054,
which stays Off and unseen. Ten menu rows again: $F777A $14, $F7758/$F7762/$F7C08 4.
Power-on: $17C86 -> crowd +8 (fix 10 defaults, then clr $FFF07A = Normal). Not
saved to SRAM: after power-on it is Normal. seasonmenu.s lets row 9 through the
season lock (all other locked rows stay locked; tested on a running Light season).
crowd.s: Normal keeps one shared M; Hard scans objects 0..11 (+$62 bit3 human,
bit6 side) and remembers human sides for the game (cleared on a new game); human
side bar -1000 / -10%, CPU side +1000 / +10% (both human or both CPU: equal).
Ratings are now rescaled only when a side percent changes (the loader hook applies
it to new players). Code grew past 1 KB, tiles moved to $2E0800. RAM $FFF72A..735.
Tests: menu toggles Normal/Hard and back; Hard game: human WSH 20 -> 18, CPU COL
20 -> 22, bars empty/full; verify_season_navigation PASS 18/342;
verify_critical_flash PASS. verify_goalie_request is cycle-chaotic: the game takes
randomness from the VDP HV counter, so any change in main-loop cycles changes the
fixture's play from frame ~103. Proof: r14 with only 20 NOPs added before the crowd
hook fails clear-11-p2only "request leaked"; r15 fails clear-5-coop-p2-d10-hold1
"no requested pass" (a 1-frame B tap). Not a crowd logic fault; the fixtures need
to become timing-robust. SHA256 491fc96f9e810ac48cdf00054a5fa1faee45f1c4e4bc59407debc66cf5d7e6d8, CRC 27A68F5F.

## Team stats screen: no CRT, no frame notch (2026-10-25-r14)

jdc (Downloads/3.png, CHICAGO): a black notch in the title frame above "CH", and
CRT/footer need not be shown there. Notch: teamstats.s padded odd-length title
strings with a space AFTER the closing big-font code BD 07 01, which drew a black
tile in the frame; every odd-length city (CHICAGO, SEATTLE...) had it. The pad now
goes before BD 07 01. CRT: teamstats.s has its own SHOW_CRIT 0 (WITH_CRIT still
drives critical.s and the native attribute screens); SOG returns to column 34 and
column 37 is cleared after SOG so goalie SV% text cannot remain. Headless SEATTLE
capture (stats-freeze all-teams seed): no notch, no CRT, clean columns.
SHA256 a8b64f0f144b6e67a78f2ab16d817899f21b2b958a7cfa480264627c4e2ddc86, CRC 960E84A8.

## Crowd meter rows swapped (2026-10-25-r13)

jdc (Downloads/34.png): playing EDM, he scored and his bar dropped. The score box
lists the $CA32 team on the TOP row and the $C6CE team (team id $C331, called
"home" in crowd.s) on the BOTTOM row; r12 drew them the other way round. Ratings
were always applied to the right team; only the display was mirrored. crowd.s now
draws M (the $C6CE team) at y 206 and -M at y 191. Verified: test game C6CE = WSH
(id 30, bottom WAS row); poked WSH shots fill the bottom bar.
SHA256 6f9ff97b8b41e7dd6f6f4658237730ce29d5c89a827a4a7eb162854ef17880d1, CRC 8E50844E.

## Crowd momentum meters (2026-10-25-r12)

jdc asked whether a loud crowd raises ratings. Native: no. $B8A2 (the crowd meter
shown as Current/Average/Peak Level, Arena Record) is only read by $7A76 (peak/
average) and the stats page $9A9E; $B89C drives crowd sound selection ($7A9E,
table $18CFC) and PSG volume ($16976 -> $B044). Neither touches ratings.
New experimental/crowd.s (+crowd.py, --no-crowd reproduces r11 exactly) at
$2E0000, 27 tiles at $2E0400. One momentum M (-1000..1000, + = home) in RAM
$FFF700..$FFF7FF (menu-only RAM, empty in games; stack is at $FFFFF6):
+200/-200 per home/away shot on goal (team block +$00), +150/-150 per body check
(+$10); blocks $FFC6CE home, $FFCA32 away (+$364). Counters going down = new game
-> M=0. Each change of clock seconds $C468 subtracts M/128 (at least 1 toward 0),
so the pace follows the period length. PCT = M/100 (-10..+10). Ratings: the native
per-player loader $15AA4 (called when a player comes on) ends with jmp $1FF002
(hack joke-player nerf); hook $15D7C -> $2E0004 replays it, then stores +$66..+$75
as that object's base and applies the current percent. Each frame, objects 0..11
(goalies included) get base*(100+p)/100 (trunc, clamp 0..max(30,base)), p = PCT
for home, -PCT for +$62 bit6 (away). Scaled: $68..$6D, $6E, $70..$73, $75; $6A/$6B
are inverted timings (lower = better) so they get -p. Kept: $66 roster slot, $67
weight, $6F jersey, $74 flags. Only rewritten when p changes or after a reload.
Native temporary $69/$75 tweaks ($EA0E/$EAA0) save/restore inside one call, so no
clash. Display: hook $15ED4 (movea.w #$C018,a6 / moveq #1,d6) -> $2E0000 sets them
and puts 8 priority sprites first in the list (tick, fill, partial, 32x8 black bg
per team) at x 222, y 191 home / 206 away, left of the COL/WAS score box (which is
plane A, not sprites). Hidden when $C2EC bit7. Tiles go to VRAM $FC20 (tile $7E1;
H-scroll is full-screen, only $FC00..$FC03 used; reg 11 is only ever $8B00) through
the native VBlank DMA queue (a5: source.l, words, VRAM) every 32 frames. White fill
pal0 idx1, red pal1 idxA (the r11 crit red), black pal0 idx4. Tests: headless CPU
game, M reached -797 with away 5 checks vs 1 and home ratings 20 -> 19 at -7%;
verify_goalie_request PASS 38, verify_critical_flash PASS. verify_shootout: 12/18
then its fixture assertion "no normal goal before injection" fails because the
rating change alters play; the --no-crowd build passes 18/18 (shootout code is
untouched). SHA256 7280773be04f9424cf2910f20b9d020f1749d3add11737e05ce289c0247d4e91,
CRC 21A1898A.

## Bright red crit puck (2026-10-25-r11)

jdc: the red puck looked only slightly tinted. It used palette 0 index A ($064C,
the dithered rink-line red; GPGX RGB 200,68,96). Every palette-0 entry is visible
on the ice (15 green-substitution test ROMs during CPU play), so no rink colour was
changed. Rink palette 1 (ROM $59944) index A ($0062 dark green) is only used by a
few crowd shirt pixels: critical_flash.py patches $59958 $0062 -> $000E (pure red)
and critical_flash.s sets sprite palette 1 (andi #$9FFF, ori #$2000) for the red
phase; glint index 1 = pal1 $CCC. Test driver expects $2923. verify_critical_flash
PASS; forced-flash capture shows a pure red puck (232,0,0). Side effect: those
crowd shirts are red. Backhand question: classify already requires animation $07FC
with raw charge >= 31 (backhand $092E never qualifies); no change made.
SHA256 161efbdcee0cff042bc9903b89ad54b511c1c77348efce4abcbe00f02b80dae6, CRC FFD6745D.

## Season Edition intro, no EA logo (2026-10-25-r10)

jdc's own picture (Downloads/33.png -> tools/data/intro-season-edition.png) converted
by introimg.py to data/intro-season.bin (1120 tiles, 38118 bytes, same format as fix
12) and written by build_test.py over the plain ROM's intro at $2A0000 (asserted
equal to data/intro.bin first; the normal/plain release keeps the old picture).
EA logo screen $17A18 (called from $770C) keeps its VDP/plane set-up and
interrupt state, but $17A74 `movea.l #$FFAC0,a2` -> `bra.s $17AA0` skips drawing,
and $17AAA wait count 80 -> 0 (one 4-frame wait). NOP-ing the whole call blacks
the boot. Boot capture: frames 5-60 black, intro fades in ~80, menu ~550. Full CPU
season game afterwards (2-3) recorded. SHA256 b1288a0ff59e9fbbf983d55ec55dbb6d6d9b201e014801c8fd50bfbaf0efb071, CRC 57CD2624.

## Standalone native playoffs have their own context (2026-10-25-r9)

jdc asked for the thorough fix of the r8 limitation (a standalone New Playoffs
tournament could not coexist with a season in its playoffs). Native playoff state
= RAM D088..D175 (238 bytes: 10-byte password D088..D091, $18192 encode / $1803E
decode, plus packed playoff player stats unpacked by $1833A), inside the D076
settings block (SRAM 1EF6). Every 8KB image was full, so:
- User profiles at logical D20..E1F (CF36 and D45A, 2 x 128 bytes) are now shared:
  slots.s copybank copies 0..D1F and E20..1FFF only. Banks' D20..E1F are free.
  backup recomputes the bank checksum (new `sumbank`); restore ends with the
  active checksum. Old banks stay valid (restore ignores their profile copy).
- Standalone context lives in the Full bank's free range, logical 2D20:
  "NPO1", 238 bytes, sum, ~sum (`ctxsave`/`ctxload`); writing it re-sums the
  Full bank. Missing/invalid magic = no standalone playoff.
- suspend (season view -> native modes) calls ctxload after backing up the season:
  RAM D088.. and active SRAM 1EF6 get the standalone context (or zeros).
- New slots +20 `ctxhook` replaces `jsr $1A206` at $FE6B2 (end of the native
  settings/playoff save $FE696): outside the season view (TAG set, VIEW 0) it also
  saves the context, so it survives season switches and power-off. Boot always
  resumes the season view with the season's own context (no ctxload in init).
- r8's pocont hook at $F7AD4 is removed (original bytes restored).
slots.s grows (BORG 2AA000, < 2AB000). verify_season_navigation.py expectation
updated: season playoffs no longer show native Continue Playoffs (graph [2,3,4]).
Tests (GPGX headless, copy of jdc's save with Mini PO): standalone NP/7 CPU game
2-1 -> Continue Playoffs (CEEA/CEEC 0001/0000); Left to Mini PO 0007/0001 intact;
through Light, Full, Shootout, New Playoffs and back -> Continue Playoffs again;
reboot -> Mini PO 0007/0001 and Continue Playoffs 0001/0000 both present. All
four images' checksums valid, ctx magic in Full bank. verify_season_slots.py
PASS 20; verify_season_navigation.py PASS 18 / 342 checks. SHA256 5bb02972d6ea8d2a4e698f507bd7ddb6c9e4efba8219601110c0e517343de44f, CRC 9D205009.
Evidence: work/npo-ctx-20261025/.

## Native Continue Playoffs hidden for season playoffs (superseded by r9) (2026-10-25-r8)

jdc: after a Mini series the circle showed both "Continue Mini PO" and native
"Continue Playoffs" — the same season playoff (native password shared by the
active slot) offered outside the season. New `season/pocont.s` (2ABC00): hook at
$F7AD4 (jsr $17CA0 -> jsr $2ABC00) runs the native decode, then, with SLOTVIEW
$FFF06C == 0 and the active slot's SRAM $1210 playoff flag 1 or 2, clears
CEEA/CEEC so native validation skips Continue (as for no saved playoff).
Season view unchanged. Tests (GPGX headless): jdc's save (Mini PO active) r7
circle Mini PO -> Continue Playoffs -> New Playoffs..., r8 Mini PO -> New Playoffs
-> NP/7 -> Shootout -> Full -> Light -> Mini PO. Fresh save, standalone New
Playoffs/7 game, one CPU game: r8 shows Continue Playoffs. With Mini PO active, a
standalone New Playoffs game left Mini PO intact (CEEA/CEEC 0007/0001 before and
after, same fixture). SHA256 b0b82332af37f3737c41cd6fc3d1b2e512c17501f1f5c29f4c0e79e5768a57e5, CRC 1252F70E.

## Abandon dialog shows Xbox buttons (2026-10-25-r7)

jdc (Xbox pad) read "A + C" literally. newseason.s confirm line is now
"A + C (Xbox X + B) = yes, abandon" (fits at col 4). Headless check: dialog renders,
A+C still clears the slot (Light 0 GP, checksum OK). SHA256 c448ab56ec959b5bd5988b5b0677249491dc310cd0ebcea5d3ddc70e0627a4a8, CRC 2E5FC2C0.

## Abandon Season in the in-game pause menus (2026-10-25-r6)

jdc wanted to abandon a season from inside a game. build_season.py copies the
pause lists $19700 (normal, ref $7D1C), $1988C (after timeout, refs $7D44/$9D84)
and $19C04 (intermission table refs $131D0/D4/D8/E4/E8/EC) to $2AB100.. with an
appended "Abandon Season" entry -> $2FA800, and repoints the refs (asserted).
Shootout list $19664 and post-game Exit lists unchanged. newseason.s now starts
with a guard: proceed if $D048==0, or 1..3 with SLOTVIEW $FFF06C != 0; otherwise
jmp $9CD8 (shared lists also serve standalone playoffs). Headless on a copy of
jdc's save (Light 29 GP): pause -> Down x16 -> Abandon -> Start returns to the pause
menu, Resume continues (clock 141 -> 133 s); A+C -> main menu New Season Light 1/31.
Light slot 0 GP, Full/Mini statistics identical, checksums OK. Remap carried.
Controller note: Xbox pad A/B/X/Y = Genesis B/C/A/Y with device 513.
SHA256 84d77895c38452f5e96c8ed3ae43397b4a2445a0abe44c70353ac2b5e479c8bf, CRC 1B9CF260.

## Restart Season renamed Abandon Season (2026-10-25-r5)

jdc: "Exit Season" was considered but rejected as misleading (the entry erases the
slot); chose **Abandon Season**. Same 18-char pre-game item in build_season.py
(league and playoff lists), dialog title, "Abandon this season?" and
"A + C  = yes, abandon it" in newseason.s. Behaviour unchanged: A+C clears only the
selected slot and returns to the main menu on New Season <format>, where Team (and
the first opponent) can be chosen; Start cancels. Headless check on a copy of jdc's
save: Light -> Abandon -> New Season Light 1/31, Team Edmonton -> Florida.
Controller remap renamed with the ROM (local + Bodin). SHA256 8960475c5570a3fc6dbed5ccb2bae272c83243cbdcbb0134bb6728be8cc2561f, CRC E3F44C8D.

## Simulated games scale with Per. Length (2026-10-25-r4)

jdc asked that simulated fixtures match the period length he plays. Measured 118
complete headless CPU-vs-CPU games (GPGX, r3, both pads CPU via $C320/$C322, Start
at intermissions; hook/runner work/simlength-20261025/cpu_hook.py, one.sh), 30 per
length (2 seven-minute runs timed out). Regulation goals/team (sd) and SOG/team:
5 min 1.32 (1.15) / 11.7; 7 min 1.93 (1.27) / 16.8; 10 min 2.82 (1.78) / 24.8;
20 min 5.70 (2.31) / 49.5. Scoring is ~0.28 goals per team-minute.
`season/season.s`: `goals` tries = 4/6/8/16 by $FFD050 (0=5,1=7,2=10,3=20; other
values -> index 2 = previous 8-try model); chance formula 60+strength/4 unchanged.
`dist` shots = base 5/9/15/39 + rnd(14/16/20/22) (was 22+rnd 16). New `lenix`,
tables `tries`/`shotb`. season.s 3938 -> 3994 bytes (limit $2F9000). Applies to
league simulation and Simulate Match (shared GOALS_FN/DIST_FN). Per. Length is
locked after game 1, so a season uses one model. Played games unchanged.
Check: fresh Mini, 8 Simulate rounds per length (256 team games each): GF/team
1.50/2.14/2.80/5.55 (incl. OT goal), shots 11.5/16.6/24.1/48.6; checksums OK and
readsram problems 0. Played CPU games at 5 and 20 min completed and recorded.
Saves not migrated (jdc). SHA256 0d16d6e10d3e0b677f60af5d9d3878a748ab92d885530af0c8fb73e703c3a7bc, CRC EF17AFA2.

## Team-stats SOG column cleared (2026-10-25-r3)

jdc saw LAK shots like `141%`, `811`, `1289` (stored 143/82/131 — save data correct).
In WITH_CRIT, skater rows write SOG at cols 33-35 and CRT at 37-39; col 36 was never
written, so after page 2 (goalie header `SV%` and SV% values reach col 36) returning to
page 1 left those characters on display rows 10-12. `season/teamstats.s` now writes a
space at col 36 after SOG. Block $2FB000 grows by 4 bytes (diff $2FB25D..$2FB6DF, end
$2FB6DF). Headless GPGX on a copy of jdc's save: standings -> COL -> page 2 -> page 1:
r2 shows `49%`/`240`, r3 clean. Saves not migrated for r3 at jdc's request ("not
important"). SHA256 1a2d72de66d8919e997fbe3263539dcc114a247777f98c1b56e1bec93f2ed6d0, CRC AC761309.

## Opponent choice only before game 1 (2026-10-25-r2)

jdc clarified the 2026-10-23 request: Left/Right on the main-menu Game row was meant
to vary the FIRST opponent of a new season only; after that the schedule is fixed.
`season/opponents.s` `change` (+4 entry, $2FD600) now returns without touching SRAM
unless the season header has a pending new format (+12 != 0) or zero games played
(+8 == 0). Get/capture/commit, the round mask, schedule, simulation and SRAM layout
are unchanged; block grows 630 -> 646 bytes (ends $2FD885, below switch.s $2FD900).
Only $2FD600..$2FD885 plus header/checksum differ from 2026-10-25.
Tests (Genesis Plus GX headless, private save copies): jdc's Light 30/31 Right/Left —
old ROM EDM->FLA, r2 stays EDM; new Mini pending and empty Full GP=0 — both ROMs
COL->SEA->NJD; Mini after one simulated game 2/8 — old NJD->BUF, r2 stays NJD.
Exact baseline rebuild of 2026-10-25 matched before the change. Full CPU game not
rerun (menu-only change). Note: verify_season_teams.py's mid-season opponent changes
now expect the lock. SHA256 c6c08289c13ef5b82ba66b87e69182e0f417a1321ba4f2fec33db00ff9fe60af, CRC 98017587.
Evidence: ~/.config/retroarch/roms/claude/work/lock-opponent-20261025/.

## 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.

## 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 and NHL source pipeline (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 and co-op alternating shootouts (2026-10-25)

League/playoff ties now get10-minute sudden-death OT, then native three-round
shootouts with early clinch and paired sudden death. P1/P2 alternate their own
team's shots in Teammates, home or away. Preserve normal G/A/SOG/keeper stats,
add one deciding goal to the final team score, save league OTW/OTL and the
correct native playoff win/qualified club. Late penalties are normalized before
actor assignment; keep one defending pad and guard Three Stars' absent OT log.
Source900 bytes at2AC000, RAMFFF090..0AF, nine native hooks; SRAM unchanged.
18 native integration cases,20 slots,38 goalie requests,14 menu circles/3190
correct font calls and clean CPU Mini1–4 pass. Exact rebuild/guarded959-byte ROM
diff, probe absence and IPS89870-byte reconstruction pass. SHA256
27349dff9ef46db0b1115f607535c72e03573a91896af03fa25df1b16f6443f7,
CRC57008214. Dated local/Downloads/public ROM/IPS, Bodin, site/source/docs updated
with backups and existing saves/remaps retained. Full details: EXPERIMENTAL_BUILD;
evidence: work/overtime-shootout-20261025-025233/.

## Fix blurry original menu text when cycling cups/leagues (2026-10-25)

Redundant relabel calls during an unchanged row0 rewrite cleared native C2F8
bit3 halfway through the small-font value pass. Original values then used the
large glyph bank. Compare unchanged mode values before SLOTKEY and use the
native writer without relabelling. Menu size1836 and all allocations unchanged.
16 circle/reverse cases/3608 native prints have zero wrong-font selections;
preceding ROM reproduces66. Screenshots confirm only text pixels change and the
initial screen is identical.20 slot cases,615 club screens and CPU Mini1–2 OT
pass. Exact rebuild/guarded27-byte menu+checksum diff and IPS reconstruction pass.
SHA256d6483fb99c858b6531f8ca4217e0e5ae0bb85fba5a655b5eedb413d582b34e77,
CRC533E9A86. Source, dated ROM/IPS copies, Bodin, website and notes updated;
saves/remaps preserved with backups. Evidence:work/menu-font-20261025-021645/.

## Fix startup menu freeze after empty Mini (2026-10-25)

An empty native Continue Playoffs was retried by the original mode validator,
but the new SLOTKEY guard blocked its writes. Reproduced with jdc's save: input
stalled, Mini text remained and Home/Away could flicker. Repeated standalone
writes now use original_modes to skip empty Continue and correctly re-enter a
season; repeated slot transitions remain blocked. Menu1826→1836, no storage or
gameplay changes.18 navigation cases/348 checks,20 save/playoff cases,615 club
checks and complete CPU Mini4–1 pass. Prior ROM fails the same navigation test.
Exact rebuild/guarded551-byte menu+checksum diff and IPS reconstruction pass.
SHA256bf489dc5c47f26d22f0f3f3a01ee25977d42daa7604453e5b8d00abffc09c068,
CRC131A9424. Source, dated ROM/IPS copies, Bodin and website/docs updated with
backups; existing battery files preserved. Evidence:work/slots-menu-freeze-20261025-014252/.

## Separate Full, Light and Mini saves (2026-10-25)

Each format now resumes its own league, team, statistics and native playoff
password. Restart affects only the chosen slot; added it to season-playoff
pre-game menus. Legacy82/31/8 saves migrate without losing season data. Added
32KB logical SRAM with the original8KB active window and three8KB snapshots;
S3SV directory1EA0..1EF5, slots.s1182 at2AA000, pre-game list228 at2AB000,
menu1826/newseason340/playoffs1074. Native menu direction and repeated callback
handling fixed so one key advances one format. Read tool/harness support64KB
physical saves. Full implementation/layout is in EXPERIMENTAL_BUILD.md.

20 migration/switch/restart/reboot/three-concurrent-playoff cases pass, including
Full84 and legacy82 final games, BO7 wins and single-game round advancement;
615 club screens and38 goalie requests also pass. A full CPU Mini1–2 OT saved
valid totals/checksum. Native RetroArch preserves all backup bytes in its64KB
save. Prior ROM fails the first independent-slot storage check. Exact source
rebuild/guarded4420-byte diff and IPS reconstruction pass. Current SHA256
9a00998859863bda2676add272fd9689d2e0215b5d5d07aeb72eef0d0799720a,
CRC8B08E457. Dated name retained for save/remap matching. Evidence:
work/season-slots-20261025-010418/. Updated dated Downloads/local/public ROM/IPS,
Bodin's existing entry, source/docs and homepage; prior releases preserved.

## Goalie holds until P1/P2 requests a pass (2026-10-25)

The human team's AI goalie now holds the puck until a **fresh B press** from a
human skater. In One, P1 requests the pass. In Teammates, P1 passes to P1's
currently controlled skater and P2 to P2's; the other pad's skater and CPU skaters
are not substituted. No automatic proximity/ranking outlet remains. A request
is explicit even with an opponent nearby, so the native pass can be intercepted.
A tap during diving/catch recovery waits until the keeper can pass. First valid
request wins if both pads ask during recovery. B is consumed through hold/release
so it neither changes the requester nor immediately passes again on reception.
The ordinary finite possession timer still produces a faceoff after a prolonged
hold. Y/manual goalie controls, CPU-only keeper AI and special-mode fallbacks
retain their previous native paths; no save format or roster change.

Implementation:

- `outlet.s`:748 bytes at `$2FDC00–$2FDEEB`. Entries+0 decision,+4 exact target,
  +8 exact goalie vector,+12 new request handler. Existing hooks `$D75C`(8),
  `$BCDA`(12), `$BDBA`(10) remain. Only our synchronous native `$BC64` call uses
  the guarded `OUTL` exact-target/vector override; normal pass physics continue.
- `switch.s`:500 bytes at `$2FD900–$2FDAF3` (was512). Calls `$2FDC0C` before
  ordinary B switching; reuses its existing per-pad pending/HELD state. Equivalent
  sign-extended `$FFFFxxxx` constants allow absolute-word addressing to fit below
  glass `$2FDB00`. Builder now asserts `len <= $200` to enforce that boundary.
- `goalie_ai.s`:90 bytes at `$2FE400–$2FE459` (was86). Each accepted native
  pickup clears the new request, preventing reuse on a later possession.
- New scratch `$FFF064.w`=pending pad offset+1 (1=P1,3=P2), `$FFF066.w`=keeper
  index5/11. Clear on successful pass, invalid request, changed owner, native
  fallback or accepted pickup. `$FFF058–5F` remains the transient pass descriptor;
  `$FFF060–63` remains reserved for the critical-puck flash. No SRAM additions.
- The requester is revalidated against current pad side/object and active human
  skater flags when served. Recovery/pass lock or unavailable target waits within
  the native possession window. Clear all32 bits before DIVU6: a word-only copy
  leaves native register residue and incorrectly rejects real-pressure requests.

Headless regression (normal6-button device513):

    python3 tools/experimental/verify_goalie_request.py /private/current.bin \
      /private/request-tests --fixtures-root /home/jdc/.config/retroarch/roms/claude/work

`goalie_request_hook.py` sets pad assignments and injects real button presses;
it does not warp positions or bypass native pass/recovery. Captured gameplay
states remain private. Required relative fixtures beneath `--fixtures-root`:
`goalie-outlet-20261024-173340/capture/gk5-clear.state` and `gk11-clear.state`,
`goalie-recovery-20261024-145816/scenario-home/before-dive.state` and the matching
`scenario-away` path, and
`goalie-auto-20261023-142924/native6-coop-baseline/before-catch.state`.

All38 hold/request cases pass: both nets, One/Teammates, P1/P2-only, native Auto
setting,1/5/25-frame B, no-B holds, both pads requesting during recovery, actual
dives and close pressure, manual fallback and CPU-only outlets. Exact receiver,
unchanged requester, no early pass and cleared transient/pending state checked.
Existing26 old/new switch-input cases also pass, including own-carrier passing,
whistle/shootout paths and both pads' Y selection. A clean complete CPU Mini
match ended1–3 and saved valid league/player/keeper totals and checksum (including
native empty-net differences). Exact source rebuild is byte-identical; guarded
1276-byte diff is limited to switch/outlet/pickup routines and ROM checksum.
Private instrumentation region `$2FEA00–$2FEFFF` remains allFF. IPS reconstruction
against original v5.1W verified. Current CRC32 **B3EF6EF5**.
Evidence: private `work/goalie-request-20261025-004956/`, including the published
regression output, switch matrix, CPU save, verification and deployment reports.
The dated2026-10-24 filename remains for existing save/remap matching.


## Glass-break sound for critical shots (2026-10-25)

Critical releases use native glass SFX14 once instead of the ordinary shot sound;
red flash, chance and power remain. No glass collision/animation/whistle call.
12-byte stack-argument helper at2FF600, success-hook target at2FF472; no new RAM.
50,000 sound/physics/stack trials passed,40,000 more without flash; existing
90,000-shot/832-identity and136-render flash regressions pass. Six gameplay/WAV
fixtures cover single-player old/new crit and ordinary shots, both co-op pads;
ordinary audio is byte-identical, crit output differs and uses14 exactly once.
Clean CPU Mini0–1 saves correct totals/checksum. Exact source/prior-profile
rebuilds; only16 guarded ROM bytes differ. Current dated SHA256 `a328835ff33de5ca65545553f542b77856d4b1f208e4c498e62d4e7c14ff4ed2`.
Kept dated2026-10-24 filenames and existing saves/remaps/game entry.

## Team selection shows the actual two clubs (2026-10-24)

Fixed stale/identical logos in Full/Light/Mini team selection. EDM–DET was
already the actual fixture, but the native logo loader saw new HOME with the
old opponent before force recalculated it during text redraw. Permitted writes
and One/Teammates changes now force both clubs before the native logo comparisons.
seasonmenu.s1880→1894 bytes; symbol-based hooks relinked, no save/gameplay change.
All615 controller-driven menu checks passed over all32 teams, both opponent
arrow directions, three formats, One/co-op, first simulated rounds, continued
odd-round player changes and team locks. Previous ROM fails all six format/
player cases. Genuine81/82 legacy save passes five checks with season data
unchanged. Full CPU Mini ended2–5 with valid totals/checksum. Exact source
rebuild verified; dated SHA256 `ddb73d35bada5a140ac6b1d1f12099ed06a7284b0e36bf836963969a4b7e32ca`.

## Red flashing puck on critical shots (2026-10-24)

Added a puck-only red/ordinary flash, four visible renders per phase. Ends on
possession, dead puck, another shot or127-render expiry. Uses cloned native puck
pixels and fixed rink palette0; no shared palette edits, shadow/geometry changes,
RNG calls or power/chance changes. New code234 bytes at2FF500, red pixels256 at
2FFE00, RAMFFF060..063. Guarded six-hook install is critical_flash.py; details
and native cache/clock flag semantics are in EXPERIMENTAL_BUILD.md.
136-render lifecycle/cache test, all8 tile mappings, native successful/failed
pickup gate, shadow/palette/flip/register checks passed. Existing90,000-shot
critical regression and832 lookups match preceding results exactly. Natural
critical-shot RGB/VDP captures show repeated red/ordinary phases without test
physics overrides. Final-ROM full CPU Mini finished1–0 in OT, with valid team/
player/goalie totals/checksum. Exact reconstruction, prior profile reproduction
and guarded511-byte diff passed. Current SHA256 `4bb1a229251ed1699a1be11040530d15505e204eb310a29bf2772cd00a885c69`.

## Critical shots and visible Crit Chance (2026-10-24)

Added a separate0..100 rating per skater, with RNG(1000)<rating per released
shot; success adds floor(power/4). Higher offensive quality gives higher Crit;
MacKinnon100, McDavid/Kucherov96. Native Team Roster gains Crit Chance and
season players gain CRT; site team pages show sortable Crit values. Existing
native ratings and saves stay intact. Zero goalie ratings; all skaters/teams,
human and CPU, follow the same rule. Statistical league scores retain their model.
100,000 isolated shots across10 baseline/new/side/direction/exclusion cases passed:
50 yielded522/10000,100 yielded1028/10000,zero yielded0. Native velocity and
zero-RNG behavior checked; all832 shared/native-attribute lookup identities and
register preservation passed. All32 teams' sorted CRT values, both pages,
goalies/returns and unchanged SRAM verified through UI input. Instrumented and
clean full CPU games finished; final-ROM game1–3 saved valid totals/checksum.
Exact source rebuild and prior-profile --no-crit comparison passed. Sources,
layout and evidence: EXPERIMENTAL_BUILD.md; work/critical-shot-20261024-220545/.
New dated SHA256 `95a05f76f358ae94a1259d57ca266d4edf0d4ce4b0166be81fd88eabeee53b7a`.

## Duplicate period-warning sound at penalty expiry (2026-10-24)

SFX2 was shared by the clock warning and penalty release. The three-pulse sound
therefore also played when a penalty expired, including at1:00. Changed only
penalty expiry's immediate sound ID at$12960 from2 to-1; native silent-ID handling
keeps player release and stack cleanup. Five-second penalty ticks, music, horn
and other sounds remain. Ten old/new audio cases verify three pulses at40 in
all four period cases, none at60 even with penalty expiry, and identical penalty
release arrays. Full CPU game and exact source/IPS reconstruction passed.
Current SHA256 `bb08ce17e7c760920dbded861387bfbce5711c581fad495ef03bcc0056db3322`.

## AI goalie actively passes to P1/P2 (2026-10-24)

Replaced the human team's random AI outlet retry with a controlled-skater choice
once recovery/~24 possession ticks finish. Hold for opponents within40px; prefer
an unmarked forward P1/P2, then safe side/back outlets. Invalid targets and own-
goal crossings are rejected. Native BC64 physics/receive/animation remain; guarded
selector and vector hooks keep the chosen target and direction. Manual/Y and
CPU-only/special paths retained. Source outlet.s:800 bytes at2FDC00, hooksD75C/
BCDA/BDBA, RAMFFF058–5F, no save migration. 44 old/new +6 pressure/dive +7
extra cases passed, plus complete old/new CPU games and exact reconstruction.
Dated local/Downloads/site/Bodin updated; previous ROM/playlist and saves retained.
SHA256 `ab7db1c15df7537c9db658ca6163d8e1ed3931ca7431d12654ae3254e64b6148`.

## Glass breaking, 50% on hard far-end impacts (2026-10-24)

Expanded the original eight-pixel trigger slot to the flat far-end panes
(X -64..64) and added a native 50% roll per qualifying hard, high puck impact.
Kept height19..31 / Y>=280 / VY>=$1F00, native animation/sound/out-of-play/faceoff
and low/soft/side/near/corner behavior. Glass sprite follows the hit pane;
non-glass goal sirens keep native centring. Source glass.s: 140 bytes at
$2FDB00, hooks $14BE0/$10320, RAM $FFF056.w, no save-layout change.
256 seeds x six locations: 130/256 (50.78%) at each, correct pane placement;
excluded cases 0/16 each; faceoff/resume and old/new full CPU games passed.
CPU validation now correctly reconciles empty-net goals/shots with native keeper
counters; no statistics gameplay patch was needed. Exact source rebuild/IPS
verified, dated local/Downloads/site/Bodin updated with backups/saves retained.
SHA256: `9b3f72d514d65ee397a4a74099b55170503ea614a4f5dcf78c4797430d454a42`.

## Full Season menu reused an empty Mini header (2026-10-24)

When no team had started a season, a leftover length=8 could make New Full
Season display 1/8. Game text now requires `hasseason` before reading saved
length; otherwise the new Full default is 84. Pending Light/Mini choices and
active saved lengths keep priority. User's exact save reproduces old 1/8 and
fixed 1/84; 24 menu cases and the first simulated match pass without save reset.
Season menu grows 1874 -> 1880 bytes; builder relinks hooks. ROM SHA256:
`0c8da621ecf2b0c82bb62318e20d19d7c4648ece4884140e43fe8ab769fbb927`.

## New Full Season, 84 games (2026-10-24)

Renamed the main option to New Full Season and changed new full seasons from
82 to 84 games, matching the NHL 2026/27 game count. Continue preserves legacy
82-game saves and their playoff boundary. Light/Mini remain 31/8; Full playoffs
remain best-of-seven. Generic pre-game restart entry/dialog now say Restart
Season. No routine-size or save-layout change; no migration needed.
Tested all 84/31/8 simulation rounds with every team/player/goalie total and
checksum; correct 84th-game playoffs in single/co-op, legacy 82nd-game playoffs,
Mini playoffs and complete CPU game. Updated local/Downloads/site ROM, IPS,
Bodin's dated entry and all continuation docs, with older ROMs backed up.
SHA256: `da1f2f418ad0ae27d0862d43ce330ea083150804a52770e033a724cca837326d`.

## Diving goalie freezes too quickly (2026-10-24)

Diving catches use the ordinary 180-tick possession window instead of the native
5-tick special case (`$1531E`: `$0005` -> `$00B4`). This lets the goalie get up
and play the puck when the outlet opens; original recovery, passing and eventual
held-puck faceoff remain. Confirmed old freeze vs new pass in controlled gameplay
fixtures; 24 old/new cases cover both goalies and single/co-op/CPU, six more check
manual B and the finite timeout. Standing-catch trace unchanged; full Mini CPU
game saved consistent totals/checksum. Complete source rebuild and IPS verified.
Current dated ROM SHA256: `bd0b40705836d46af1f372388fef399d394a58dc40f5681ec722671b7e9426b7`.
Earlier dated ROM backed up, saves preserved. Full details: EXPERIMENTAL_BUILD.

## Dated 2026-10-24 test: responsive player switching

B without the puck switches on press to another available skater. Original
selection waited for release, could reselect the same nearest skater, and long
holds took the goalie. The measured 1/5/12/25-frame holds now all switch on the
next frame, versus 2/7/13/22-frame response in the original fixture. Both co-op
pads and away-team single-player checked; Y and puck passing retained. Full Mini
CPU game and all saved team/player/goalie totals/checksum passed. Exact source
rebuild SHA256: `fdeedb136d423cd9a9a637baa95666c164c5f97128002f6e5eb9c78f112c302d`.
New dated ROM/IPS downloads preserve the previous release. Separate dated game,
save and six-button remap installed locally and in Bodin's playlist; old files
verified unchanged. See EXPERIMENTAL_BUILD for hooks, RAM and test limitations.

## Mini season, opponent selection and statistics audit (2026-10-24, test profile)

New Season Mini records eight league games, then seeds the usual 16-team playoffs
with one game per pairing. The Game row's Left/Right selects an unplayed opponent;
selection and played-round mask are stored in the previously free 14 SRAM bytes
at `$1212–$121F`. Mini/Light simulate that entire chosen circle round. Full uses
the selected user fixture and its existing simulation for the other clubs, with
31-round mask resets. The next fixture, actual teams, team logos, saved statistics
and checksum agree after restart. Old saves derive a compatible default mask.

Verified all 8/31/82 chosen-opponent games through controller input, with all 32
clubs' GP/GF/GA, scorer/goalie totals and checksum checked. Mini/Light per-round
GF/GA deltas match the paired opponent. No duplicate opponents within a 31-round
cycle. Verified final Mini/Light/full qualification/format, one Mini playoff win
advancing round 0→1, restart of each format and circular menu navigation. A complete
single-player CPU game passed with Mini and the new recorder; MAME also passed.

Bodin's incident was single-player. His exact freeze was not reproduced. Final
single-player stress passed 64 cycles / 192 screen entries / 640 checks, plus
away-team pause/statistics/resume. Removed unused indirect exit marker/address
checks from standings/leaders: a controlled bad-marker case fails on old code
and exits normally after the fix. The separate co-op-only input improvement lets
both normal pads navigate/exit custom stats and retains native single/multitap input.

All earlier private changes are preserved. Their full source and an explicit-
output builder are now in `tools/experimental/`; season changes are in
`tools/season/`. The source build matches the tested ROM byte for byte. Read
[EXPERIMENTAL_BUILD.md](https://dela.jdc.se/nhl94/notes.html#EXPERIMENTAL_BUILD.md) for reproduction, hashes and memory
layout. Public download ROM/IPS files are unchanged.

## Team player-statistics entry crash — private ROM fix (2026-10-24)

Opening a team from Season Standings could raise Address Error. The caller put
the team in D0, then `unlite` overwrote it with a palette colour; TEAMSTATS tried
to read that colour as a team index. Preserve D0.w around the palette restore.
Standings grows 1762 → 1766 bytes at `$2F9000`, within its 2048-byte allocation.

Old/new tests reproduce and fix the crash in Genesis Plus GX and headless MAME.
All 32 teams, both player pages, goalies and return navigation passed; reading
statistics did not change saved season data. Scoring Leaders, simulation and a
complete normal CPU game also passed in the same core session. Corrected the
public assembly source for future builds; installed only the private Goalie AI
Test ROM, preserving all previous private changes. Public ROM/IPS unchanged.
Reproduction: private `work/stats-freeze-20261024-084813/README.md`.

## Experimental Simulate Match — private test ROM only (2026-10-23)

The league pre-game menu now has **Simulate Match** immediately below **Start Game**.
C resolves the fixture, saves the result plus team/player/goalie statistics and the
rest of the league round, and shows a result screen; Start returns to the main
menu. It uses the existing team-strength goal model, weighted scorers/assists,
shots, goalie selection and overtime rules. A completed season cannot simulate an
extra game. The option is in the league pre-game list; the playoff menu is unchanged.

Verified all 31 rounds in co-op and all 82 in single player with per-round SRAM
checksum/team/player/goalie checks. Tested final Light games in single/co-op and
full game 82 with qualified teams: correct 16-team bracket and single-game / best
of seven format. Held C and save/reload do not duplicate a game. MAME saved two
rounds with correct statistics/checksum; a full normal CPU game reached standings.
The website shows an explicitly labelled private-test example. Public downloads
and production build sources are unchanged. Full source and results are in private
`work/simulate-match-20261023-153017/`.

## Experimental short penalties — private test ROM only (2026-10-23)

Five-minute periods: ordinary penalties 2 → 1 minute, injury Elbowing 4 → 2 minutes,
fighting 5 → 2 minutes, fight instigator stays 2. Other period lengths retain the
original values. The displayed penalty summary, team penalty minutes and actual
countdown agree. Original coincidental-major classification is preserved before
normalizing the award, so both fighting players serve their penalties correctly.
24 Genesis Plus GX cases passed; MAME independently counted 60 down to 1, and a
CPU game reached the post-game season standings. Only the private Goalie AI Test
ROM was replaced (SHA256 `834a5e4d0d34c58c85079a84cdb513b10a658d91634c2f351ea941583db295e6`),
with its previous version backed up. Full sources, fixtures and exact results:
private `work/penalty-times-20261023-151151/`. Public downloads/build are unchanged.

## Experimental period warning — private test ROM only (2026-10-23)

The Goalie AI Test copy now starts its warning at 0:40 in every period and plays three short beeps, about +2 dB above the original first-pulse RMS. Both IRQ time comparisons at `$15DFE` / `$15E08` now use 40; sound ID 2 points to a 60-byte three-pulse sequence in unused padding at `$1960`. The original warning voice already used maximum velocity, so a quieter unison voice adds modest loudness. No global sound-volume change. Tested all ordinary periods and overtime in Genesis Plus GX, silence around 1:00, and a MAME real-clock countdown. A preview, exact measurements and reproduction are in private `work/clock-beep-20261023-145844/`. This is not in the public downloads or the production build script.

## Two - Teammates season fix (2026-10-23)

jdc: after a game in Two - Teammates the season did not go on to the next game; with One it did. Cause: `season.s` starts
the season (your team, game counter, length 31/82) on the first recorded game only when Players is 1 (One - Home) or 2
(One - Visitor); Players 3 (Two - Teammates) took the "demo / two players: just record" branch, so header +6/+8/+10 stayed 0
and the menu showed game 1 again. jdc's save confirmed it: 1 game recorded, team 0, games 0, length 0. Fix: Players 3 starts
the season with the home team (co-op always controls the home team).

Also: the Players row (One <-> Two - Teammates) can now be changed while a season is running. The game boots with One, and
the row was locked after game 1, so a co-op season could not be continued as co-op after a power-off. All other rows stay
locked; the playoffs are unchanged.

Genesis Plus GX (`gpgx.py`, jdc's save, Washington, period length poked short, 1-0 poked): old ROM → header 1, 0, 0, 0
(stuck); new ROM → team WSH, game 1/31, menu shows 2/31 at SEA. After a restart Players = One, Right → Two - Teammates
(home/away swap to WSH at home), other rows still locked, Right back → One; game 2 in co-op recorded (WSH 2 GP 2 W).

## New Season Light (2026-10-23)

Season-to-playoffs entry: after the final league game, the main menu now displays **Continue to Playoffs** for the qualified user's season instead of the underlying New Playoffs / New Playoffs/7 games label. Start opens the existing cup-and-bracket screen with the season's qualified teams. Verified in Genesis Plus GX using jdc's actual save after game 31: the menu text fits, all 16 bracket slots match the final table, Edmonton meets Los Angeles, and Light retains `$CEEA=7` (one game per pairing). The user's save is preserved. Once a playoff game has been played, the existing Continue Playoffs label applies.

Menu navigation fix: Play Mode now wraps through the available modes in both directions. The season handler only intercepts entry to the Regular Season value; it no longer redirects transitions between the other original modes back into the season choices. Light → New on Left now checks whether a saved season exists before overwriting the condition flags. With no saved season, New → Left exits directly to Shootout. Headless MAME verification pressed Right 12 times and Left 12 times, both with an empty save and with a five-game season: repeated full circles returned to the starting selection in both cases.

The main menu's Play Mode row now includes New Season Light. It has 31 rounds: each team meets all 31 opponents once. The user-team schedule uses the fixed `sched` permutation; the other 30 teams are paired by the circle method each round. The season length is saved in header word +10 (`31` for Light, `82` for the full season). On completion, Light chooses the game's New Playoffs format, which resolves each pairing in one game (`$CEEA=7`); the full season still chooses New Playoffs/7 games (`$CEEA=0`). The same 16-team NHL seeding applies to both.

Headless MAME checks: the Light menu shows Game 1/31; a completed Light save switches to Play Mode 2 with `$CEEA=7`; a completed 82-game save switches to Play Mode 3 with `$CEEA=0`. Both have valid playoff trees. A byte-for-byte backup of the previous installed season ROM is beside it.

## 1–2. Co-op passing (2026-10-13)

### The problem
In "Players: Two - Teammates", player 1's passes rarely went to player 2. Usually they went to a CPU teammate, who then
became player 1's skater. When a pass did go to player 2 it was often poor.

### Cause
The pass routine (ROM $BCF8–$BD5A) loops over the teammates and picks the one with the lowest "cost" = distance² + a penalty
if the player is not straight in the stick direction. Players outside ±1 of 8 directions (±45°) are not considered at all.
It makes no difference between humans and the CPU, so a closer CPU player almost always wins.

A CPU receiver skates into the pass by itself. A human receiver (P2) doesn't, so passes to P2 look worse than in single player.

### Changes (patch.py)
1. **Pass target** – hook at $BD44 → new code at $2FF000. If the passer is human-controlled and the candidate is too
   (object +$62 bit 3), the cost is set to 0, so the other human is chosen whenever he is roughly in the right direction.
   Single player is unaffected. Works both ways (P2 → P1 too).
2. **Receiving** – hook at $14E84 → new code at $2FF080. The human player who is the pass target ($FFBEE0) may receive the
   puck at distance 20 from the stick instead of 14.

Flags: `nosel` (skip change 1), `norecv` (skip change 2), `r=N` (radius). The script checks the original bytes before
patching, so it refuses if the addresses moved in another version (e.g. v4.1W or a future v5.2).

### Measured (bot test, about 100 passes per version)

| | Original | Patched |
|---|---|---|
| Pass aimed at P2 | 55 % | 86 % |
| P2 received the puck | 30 % | 44 % |
| CPU teammate took the puck | 28 % | 7 % |

Change 2 had no measurable effect in the bot test (the bot P2 never steers toward the puck). Not yet played with real pads.

Test: `run.sh` starts MAME with `h.lua`; `bot2.lua` makes P1 chase the puck and pass toward P2 (with ±35° sloppiness)
while P2 skates around the attacking zone. Needs a saved state `ingame0.sta` from a "Two - Teammates" game.
Never move players by writing x/y in RAM – it breaks the game's y-sorted collision list ($FFB84A/$FFB86A/$FFB88A)
and then nobody receives the puck.

Possible next step: let P2's skater steer toward the puck for the last frames of a pass to him, like CPU players do.

## 3. Goalie passes (2026-10-19)

Applies only when the passer is a goalie (object +$34 = 0):
- The goalie aims with the stick as usual (±45°, the other human is preferred inside the cone).
- Hook $BD5E → $2FF180: if there is no teammate in that direction the goalie no longer fires the puck "blind" straight out.
  He picks the other human (co-op) or else the nearest teammate.
- Hook $BF54 → $2FF140: the flight time (d2) is rounded down in the game, so short passes flew up to twice as fast.
  For the goalie 1 is added and at least 3 is used → softer passes. Flags `gadd=N`, `gmin=N`, `nogk`.

### Measured (`gkev3.lua` / `gkev3s.lua`, 8 positions × 9 stick directions = 72 passes)

| | Original co-op | Now co-op | Original single | Now single |
|---|---|---|---|---|
| "Blind" passes without a receiver | 21 | **0** | – | **0** |
| P2 got the puck | 9 | **32** | – | – |
| An opponent took it | 20 | **12** | 21 | **8** |

Aiming works: from the same position different stick directions give different receivers.
Positions are saved with `gk1b.lua` (gkp1–8) and run with `ROM_TEST=file.bin GK=gkev3.lua ./run.sh commands.txt`.
Earlier builds: passfix1 (P2 fix only), passfix2 (wider cone), passfix3 (goalie ignored the stick completely).

## 4. Shots from the blue line (2026-10-20, adjusted 2026-10-21)

- Background: the goalie stops everything below height 15 within 12–15 px, and the crossbar is at height 13. A goalie in
  position was practically unbeatable from distance (0.3 % of blue-line shots in the test).
- Hook $C408 → $2FF300 (shot routine): the shot distance is stored in free RAM $FFF000. If the distance is ≥ 160
  (140 until 2026-10-21; the blue line is about 180 straight out from the net) the game's own random generator ($11086)
  decides **once per shot**: 15 % chance that the goalie misses the puck ($FFF002 = countdown 40).
- Hook $15032 → $2FF340: while the countdown runs the goalie cannot reach the puck.
- Hook $150F6 → $2FF380: the goalie cannot pick up the low puck meanwhile either. Otherwise both flags are cleared when
  anyone takes the puck.
- Hook $C51A → $2FF3C0: long shots get 1/8 less lift, so fewer sail over the bar.
- The puck still has to go under the bar and between the posts. Applies to both teams, the CPU too.
- Flags: `blp=N` (chance in %, default 15, was 25 until the evening of 2026-10-20), `bld=N` (minimum distance, 160, was 140),
  `blz=N` (lift −1/2^N, 3, 0 = off), `blt=N` (countdown, 40), `nobl` (skip the change).
- The header checksum is recalculated (the game doesn't check it; the original was already wrong).

### Measured (`blev2.lua`, 12 positions × 6 stick directions × 2 shot lengths × 2 timings = 288 shots)

Positions `bl1`–`bl12` (P1 with the puck on the offensive blue line) are saved with `blsave.lua`.
Run: `EVOUT=out.txt ROM_TEST=file.bin GK=blev2.lua ./run.sh bt.txt` (bt.txt = `900000 exit`). Goals are counted via the goal log $FFC474.

| Version | Goals | Saves | Blocked | Wide/over |
|---|---|---|---|---|
| Original | 1 (0.3 %) | 128 | 49 | 110 |
| Only smaller reach (−3/−5 px) | 2–5 | 110–167 | 45–56 | 62–131 |
| 25 % miss + lower lift (passfix5) | **12 (4 %)** | 163 | 59 | 54 |
| 40 % miss | 21 (7 %) | 151 | 59 | 57 |

A full CPU period ran without errors, with about as many goals as the original. Not played with a pad.

### Edmonton test (2026-10-20, 25 % miss, zone from 140)

Ten demo games (CPU vs CPU, 10-minute periods), Edmonton away against WSH, ANA, BOS, CGY, CAR, CHI, COL, CBJ, DAL and WPG.
Scripts: `rung.sh N right-presses` and `edm.lua` (breakpoints on `$2FF32A` and `$14828`), analysis with `ana.py`.
Replays from other games during intermissions are filtered out with the team bytes `$FFC331`/`$FFC333`; the team bit is
`+$62 & $40`, the jersey is at `+$6F` (BCD).

| | Shots green | Goals green | Shots closer | Goals closer |
|---|---|---|---|---|
| McDavid | 25 | 4 (all via the goalie miss) | 64 | 5 |
| Draisaitl (second line) | 6 | 0 | 24 | 2 |
| All Edmonton | 104 | 9 (8.7 %) | 270 | 21 (7.8 %) |

About 14 goals could not be tied to a logged shot (someone took the puck in between) and are not included.

Controlled test with the same 288 blue-line shots (`blev3.lua`, `RATE=` = the shooter's rating bytes `$66–$75`):

| Shooter | Original | Patched |
|---|---|---|
| Average (WPG) | 1 | 12 (4.2 %) |
| McDavid | 0 | 21 (7.3 %) |
| Draisaitl | 2 | 17 (5.9 %) |

Rating bytes from the game: McDavid `0d30181d080d13131d97091d1d0e0009`, Draisaitl `05401a1a0a0b15151a290b1a1e00000b`.

### Lowered to 15 %

jdc felt 10–15 % was about right; 15 % was chosen (about 3 % goals from the point, like the real NHL).
Calculation: in the 25 % test about 17 % of the flagged shots went in → 15 % × 17 % + 0.3 % ≈ 2.8 % goals.
Note: the 288-shot test says little here — the positions are so similar that the random value is nearly the same:
both 12 % and 15 % gave 24 flagged shots and 1 goal. The flag is exactly `rnd(100) < blp` per shot, so calculate instead.

### Zone moved out to 160 (2026-10-21)

jdc wanted it a little harder. `bld` 140 → 160: the zone starts about 20 px inside the blue line straight out from the net
(blue line ≈ 180). The chance per shot is still 15 %.

### Shootouts (2026-10-20)

Play Mode → Shootout (Right ×3 from Regular Season), Players Demo or One. Script: `lua/sbot.lua` (bot P1); positions taken
in the original ROM and loaded in both ROMs.
- CPU vs CPU: original 0 goals on 85 attempts, patched 1 on 17 (a close shot). The CPU almost never scores.
- Bot, 810 attempts per ROM (9 positions × shot distance 100/125/170/205/230 × deke none/left/right × aim × wrist/slap):
  original 20 goals, patched 19. Almost every goal: skate straight, steer left ~15 frames about 60 px before, shoot ~38 px
  from the goal line. No goals from blue-line range in either ROM. So the patch doesn't noticeably change shootouts.
- The goalie stands at y ±247 in shootouts.

## 5. The off-screen arrow (2026-10-21)

Problem (jdc): in shootouts with a manual goalie the arrow at the top says the goalie is to the left, but when the shooter
arrives the goalie is in the middle of the net.
Cause: the arrow is drawn by the original code ($164D6), which centres it around x 128, the middle of the original NHL 94's
256 px wide screen. The hack runs 320 px wide (centre 160), so every edge arrow ended up 32 px too far left.
Fix: `addi.w #$100,d0` → `#$120` at $16590 (flag `noarrow` turns it off). Measured: the arrow for a goalie at x = 0 is now at
screen x 159 instead of 127.

## 6. Fighting Off/On that actually holds (2026-10-21)

The hack's fights: on a body check ($2001DE) it checks the setting, that nobody is a goalie, that play is live, and then a
threshold compared with both players' fighting rating (`+$74`, usually 0–6, enforcers about 14). Tension in the game and
penalty minutes lower the threshold, then 25 % chance. Penalties must not be Off. Arcade mode (value 2) lowers the threshold.
Bug: the setting was stored at $FFDF00, inside the hack's event log ($DEF6 + 14 bytes per entry). After the first entry it
held player data ($0D12, $0315 …), so the menu choice stopped meaning anything mid-game. Value 0 (= no fights) was shown as "Also On".
Fix: the setting moved to $FFF004 (all 6 `$DF00.w` + the menu's 3 `+$EA6` → `+$1FAA`), the menu has 2 choices, Off / On.
Flag `nofight` turns it off.
Test (CPU games, Winnipeg–Washington/Calgary): Off → $FFF004 = 0 the whole game (2422 checks); On → 1 the whole game (2801 + 2467).
No fights with normal ratings in 3 games (the threshold stopped 331 of 332). With every `+$74` set to $1F there was a fight
(Beauvillier–Toews, frame 10594). Scripts: `fc2.lua`, `st.lua`, `fz.lua`.

## 7. Edmonton roster (2026-10-21)

`rosters/EDM.json` (marked `"manual": true`), built by fix 7 in patch.py into `$2F0000` (the team pointer at `$336` is moved).
3 G / 15 F / 8 D as before. Lines (official NHL positions): Sc1 Hyman–McDavid–Podkolzin, Ekholm–Bouchard; Sc2
Formenton–Draisaitl–Kapanen, Shea–Murphy; Chk Howard–Dach–Joseph, Walman–Emberson. PP1 Hyman–McDavid–Draisaitl,
Ekholm–Bouchard; PP2 Howard–Hallander–Kapanen, Walman–Murphy; PK1 Kapanen–McDavid, Shea–Bouchard; PK2 Formenton–Michaels,
Ekholm–Murphy. Goalie Levi (Jarry, Andersen).
New: Levi, Formenton, Michaels, Hallander (ratings per `edm_lineup_2026-10-21.md`). Removed: Ingram, Pickard, Henrique, Lazar,
Roslovic, Nurse, Stastney, Janmark. Not included: Max Jones. Tested: the Line Editor shows the right players, a full CPU
game WPG–EDM 1–2 without errors.

## 8. All 32 rosters (2026-10-21)

All teams rebuilt from the NHL API (rosters, prospects, stats 2024–27) and DailyFaceoff lines with `update_rosters.sh`
(`fetch.py` + `gen.py`). Edmonton is kept as made by hand. Details in `HACKING.md` ("All teams") and `WORKFLOW.md`.
Tested: all 32 read back exactly from the ROM; full CPU games BOS–CAR, CBJ–OTT, NSH–TOR, SEA–ANA without errors.

## 9. Star ratings and placeholder fix (2026-10-21)

- jdc: Anton Frondell (CHI) and Ivar Stenberg (SJS) should have star status. They are set by hand in
  `rosters/overrides.json`, at about Connor Bedard's level: Frondell `75554532555343`, Stenberg `55553420645352`
  (Celebrini `66552420536263` for comparison). `gen.py` applies overrides before anything else, so they survive roster updates.
- Found while doing it: the hack gives some players placeholder ratings (all 1s): Frondell, L'Heureux, Lindstein, Holl,
  Cole Hutson. `gen.py` now treats those as missing and rates them from NHL stats, and leaves them out of the kNN training
  data. That also shifted a few other model-rated players slightly (22 team files changed, only new players' ratings).
- Tested: build OK, all teams read back, full CPU game CHI–SEA without errors.

## 10. Lines: no player in two lines; Fighting N/A when Penalties is Off (2026-10-21)

- jdc saw Draisaitl in both the first and the second line. Cause: each line entry has a 7th slot (extra skater). For
  Edmonton it was Draisaitl on line 1, and the first of the 8 line entries (purpose unknown) had Draisaitl at RW. `gen.py` put
  line 2's centre as line 1's extra on every team. Now the extras are 4th-line players (Sc1 = 4th C, Sc2 = 4th LW,
  Chk = 4th RW) and the first entry is a copy of Scoring 1. Checked: no team has a player in two even-strength lines.
- Fights already needed Penalties on (`$D056` ≠ 0 in `$2001DE`), but the menu still showed Off/On. Fix 8: hook on the menu's
  value printer at `$F7BAA` (code at `$2FE000`): option `$12` with Penalties = 0 prints "N/A" (`$F7CEE`), the same way Per. Length
  shows N/A in shootouts. Flag `nofightna`. Tested in the menu: Penalties Off → Fighting N/A; back on → the value shows again.

## 11. Season mode (2026-10-21) — project MEGAdriveHARDJOB

Built into the ROM in 68000 assembly (`tools/season/`), so it works in any emulator and on hardware. Regular Season is an
82-game season with your team, Continue/New Season in Play Mode, the menu locked after the first game, a Game row with the
next game, a pre-game hub (Start skips Matchups) with Season Standings by division, Scoring Leaders, team screens (G, A, PTS,
SOG per player) and New Season. Your games record the game's own per-player goals/assists/shots; the rest of the league
plays a simulated round with each of your games. Saved in battery RAM (`$1000` table, `$1220` players). Full details,
addresses and the lessons learned are in `SEASON_MODE.md`. Checked: player goals = team goals for all teams, assists ≤ 2 per
goal, shots ≥ goals. The IPS on the site is now this season ROM.

## 12. Y takes your goalie (2026-10-22) — fix 9

jdc: with Goalie Ctrl Manual, Y should pick the goalie when you don't have a player with the puck.
Found: the hack's 6-button reader (`$1A49A` → `$1FFBC4`) turns Y into a B press (tested in MAME with `-ctrl1 md6button`: Y passed
with the puck and switched to the nearest skater without it). The game already has a manual-goalie switch (`$B3E4`, reached through
the B hold timer `$BF06/$BF07`): goalie of the team via `$B86A`, skip if `+$62` bit 3 (other human), then `$C0BC` (P1) / `$C0DA` (P2).
`$D05A`/`$D05C` = Goalie Ctrl per pad, **0 = Manual**.

Fix (`tools/ygoalie.s` at `$2FE040`, hooks `$1FFC02` and `$B258`): when Y goes down on pad 1/2 (not 4-player), Goalie Ctrl is Manual
and your team doesn't have the puck → `YREQ` (`$FFF0E0`/`$FFF0E2`) and no B press; the input handler then does the game's goalie switch
(checks the puck again, the press may come a frame early). Otherwise Y = B as before. Y held keeps what the press did (`$FFF0E4/6`:
$FF goalie, 1 B) so a goalie who catches the puck while Y is still held doesn't pass.
Tested: home (→ object 5), away (→ 11), plain and season ROM, Y with the puck still passes. `noygoalie` skips it.

## 13. Season: goalie stats, overtime fix (2026-10-22)

- Goalie stats in the season: GP, W, GA, SA (SV% on the team screen, page 2). Details and addresses in `SEASON_MODE.md`
  ("Goalie stats"). Slot 0 of the game's per-player arrays is the goalie in net (goals/shots against), not a team total.
- Fixed: in the simulated league almost every loss counted as an overtime loss. The overtime flag was in `d1`, which the
  scorer/shot distribution (`dist`) takes as its goals argument, so it was non-zero whenever the team scored. Now kept in `OTF`
  (`$FFF0FA`). Seasons played before the fix keep their wrong OTL counts.
- Standings: separate **OTW** and **OTL** columns (jdc). Overtime wins are counted in the team row's spare word (`+14`);
  W now shows regulation wins, points unchanged (2 per win, 1 per OT loss or tie).
- **League table after every season game** (jdc): all 32 teams by points with the playoff tree's team banners, GP, PTS and
  the gap to the leader, your team marked, Up/Down to scroll, Start to go on (`season/table.s`, hook `$17336`).
- The table after a game shows one division per screen (Left/Right), the gap is to the division leader.

## 14. Start-up defaults (2026-10-22) — fix 10

jdc: Per. Length 5 minutes, Goalie Ctrl Manual, Line Changes Auto from the start. The defaults are a 9-word table at `$17C8E`
copied to `$D048–$D058` by `$17C72` (Per. Length 0=5 1=7 2=10 3=20; Goalie Ctrl 0=Manual; Line Changes 0=On 1=Off 2=Auto —
order measured in the menu). The main menu builder (`$F73B0`) sets Line Changes from `$D072` (kept between games, 0 after
power-on), so a hook after the copy (`$17C86` → `$2FE200`) sets `$D072` = 2 as well. `nodefaults` skips it.
In a season the menu is locked after the first game, so the season plays with the settings it started with.

## 15. Playoffs after the season (2026-10-22)

After game 82 the menu switches to the game's own New Playoffs/7 games with the season's 16 teams (top 3 per division +
2 wild cards per conference, NHL pairings) in its playoff tree (`season/playoffs.s`, hooks `$17D8E` and `$17D4A`).
The division table marks who is in (green top 3, teal wild card). Details in `SEASON_MODE.md` ("Playoffs after the season").

## 16. User Records out of the main menu (2026-10-22) — fix 11

jdc: hide User Records, then: no gap. The menu ties row r to its setting by d7 = 2r: accessors `$2095D6` (write),
`$2095F6` (read), `$20961A` (read for printing) use `$D048 + d7`; the option counts are at `$2095AE + d7` (`$2095C2` with
the 4-player adaptor), the value strings via offsets at `$2091FE + d7`, the labels in a 16-byte list at `$20915A`.
Fix: in all those tables rows 6–9 (User Records, Penalties, Line Changes, Fighting) become Penalties, Line Changes,
Fighting, User Records; the three accessors jump to code at `$2FE220` that maps the row to its setting; the menu has 9 rows
(cursor limit `$F777A` `$14`→`$12`, window top `$F7758`/`$F7762` 4→3, down arrow `$F7C08` 4→3). Fix 8's Fighting row is
`$10`. User Records stays Off. Pitfall found: the counts table that matters is `$2095AE`; with only `$2095C2` swapped the
menu's range check turned Penalties 2 into 0. Tested in Genesis Plus GX: defaults, every row changes its own setting,
Fighting N/A follows Penalties, a game starts. `nouserrec` skips it.

## 17. Own intro picture, no title screen (2026-10-22) — fix 12

jdc: remove the title screen (the player in red) and put his NHL shield picture where the hack's centre-ice screen was.
Boot: `$771E` calls the centre-ice screen `$FED70` (H40, two pictures through `$1169A`, fade, 80 × 4 frames or a button);
`$172F0` then runs the title screen `$FF042` (H32, credits, its first act is the random seeds `$D066/$D068` from the HV counter)
and the main menu. `introimg.py` turns `data/intro-nhl.png` into 320 × 224, 8 grey levels (Floyd–Steinberg), palette 0,
1120 tiles (`data/intro.bin`, 38 KB at `$2A0000`). `intro.s` (`$2FE280`): the same set-up as `$FED70`, tiles straight to VRAM
from tile 1, palette into the buffer `$BD28`, the map written to plane A (`$C000`, 128 cells per row) and plane B emptied
(the `$11B92` code BE before `$1169A` picks plane B with attribute bits — the first try showed the EA logo's leftovers), the
original fade and wait. `$172F4` calls `seed` (the two HV reads) instead of `$FF042`. `nointro` skips it.

## 18. Co-op passes received more often (2026-10-22)

jdc: passes between the two humans should get through more often. Bot test (`bot2.lua` from `ingame0.sta`, Start pressed
at frame 100, P1 passes toward a standing P2 with ±35° aim error, 3–4 seeds × 24000 frames per variant):

| Variant | Passes | P2 got it | Opponent got it |
|---|---|---|---|
| r=20 (before) | 76 | 48 % | 31 % |
| r=20, human→human flight time 70 % (`cps=70`) | 70 | 42 % | 30 % |
| r=32 | 92 | 55 % | 25 % |
| **r=40 (now the default)** | 83 | 59 % | 18 % |

Faster passes were worse (the puck was past before it could be taken; option `cps=N` kept, hook `$BF54` → `$2FE400`, off at 100).
The receiving reach of the human pass target (fix 2) is now 40 (`build.sh` default flags `r=40`); it only applies to the
human who is the target of a pass in flight. A real receiver who skates toward the puck gets more than the standing bot.
