season/slots.s

Download file

Release 2026-11-04-r2 · SHA256 53753f78156376d1c1cd2e191cf40673d7bd9add95da8ec15e37d557bfa087f4 · Click a line number for a direct link.

1| Three independent season saves. Active legacy8KB + three8KB snapshots.2| SRAM header expanded to odd $200001..$20FFFF. Native helpers remain8KB-only.3| Metadata in formerly unused logical $1EA0..$1EF5, covered by native checksum.4| +0 init/migrate; +4 select(D0=1/2/3, returns0/-1); +8 suspend; +12 restart; +16 prelist; +20 native save hook.5    .include "features.inc"6    .equ TAG,0xFFFFF0687    .equ MODE,0xFFFFF06A8    .equ VIEW,0xFFFFF06C9    .equ META,0xFFFFE80010    .equ HEAD,0xFFFFE85011    .equ MAGIC,0x53335356          | S3SV12    .equ METAOFF,0x1EA013    .equ EXTRA,0x1EAC             |66bytes D046..D08714    .equ MAP,0xA130F115    .text16    .globl _start17_start:18    bra.w init19    bra.w select20    bra.w suspend21    bra.w restart22    bra.w prelist23    bra.w ctxhook24init:25    cmpi.w #0x5333,TAG26    beq.w init_done27    movem.l %d0-%d7/%a0-%a6,-(%sp)28    move.w %sr,-(%sp)29    ori.w #0x700,%sr30    move.b #1,MAP31    .if WITH_TRANSFERS32    jsr 0x256004                   | Recover committed reset before directory/migration.33    .endif34    bsr.w readmeta35    cmpi.l #MAGIC,META36    bne.s migrate37    cmpi.w #1,META+438    bne.s migrate39    move.w META+6,%d040    subq.w #1,%d041    cmpi.w #2,%d042    bhi.s migrate43    cmpi.w #7,META+844    bhi.s migrate45    move.w META+6,MODE46    move.w #1,VIEW47    tst.w META+1048    bne.w initialized49    | Outside the season view, the selected season's snapshot is authoritative.50    move.w MODE,%d051    move.w %d0,%d652    bsr.w bankptr53    bsr.w validbank54    tst.w %d055    bne.s blocked_resume       | never replace current data with an invalid snapshot56    bsr.w restore              | boot resumes the season view: its own playoff context57    bra.w initialized58blocked_resume:59    clr.w VIEW60    bra.w initialized61migrate:62    moveq #1,%d563    move.l #0x1000,%d064    moveq #16,%d165    lea HEAD,%a066    bsr.w read67    cmpi.l #0x5345534E,HEAD68    bne.s migrated69    tst.w HEAD+670    beq.s empty_legacy71    .if MINI_GAMES != 872    cmpi.w #MINI_GAMES,HEAD+1073    beq.s legacy_mini74    .endif75    cmpi.w #8,HEAD+1076    beq.s legacy_mini77    cmpi.w #31,HEAD+1078    bne.s legacy_running79    moveq #2,%d580    bra.s legacy_running81legacy_mini:82    moveq #3,%d583legacy_running:84    clr.w HEAD+12              | browsing New in an old ROM must not discard its saved season85    move.l #0x1000,%d086    moveq #16,%d187    lea HEAD,%a088    bsr.w write89    bra.s migrated90empty_legacy:91    move.w HEAD+12,%d092    subq.w #1,%d093    cmpi.w #2,%d094    bhi.s migrated95    addq.w #1,%d096    move.w %d0,%d597migrated:98    move.l #MAGIC,META99    move.w #1,META+4100    move.w %d5,META+6101    clr.w META+8102    move.w #1,META+10103    move.w %d5,MODE104    move.w #1,VIEW105    bsr.w writemeta106    bsr.w checksum107initialized:108    .if WITH_TRANSFERS109    jsr 0x256010110    tst.l %d0111    beq.s reset_recovery_done112    move.w %d0,MODE113    move.w %d0,META+6114    move.w #1,VIEW115    move.w #1,META+10116    bsr.w clearseason117    bsr.w writemeta118    bsr.w checksum119    jsr 0x25600C120reset_recovery_done:121    .endif122    tst.w VIEW123    beq.s count_ready124    jsr 0x2EB000                     | optional per-slot player-count extension; mapper is enabled125count_ready:126    move.w #0x5333,TAG127    move.b #0,MAP128    move.w (%sp)+,%sr129    movem.l (%sp)+,%d0-%d7/%a0-%a6130    .if WITH_TRANSFERS131    jsr 0x236028                   | Initial boot now has committed MODE/VIEW and mapperOff.132    .endif133init_done:134    rts135136select:137    bsr.w init138    movem.l %d1-%d7/%a0-%a6,-(%sp)139    move.w %sr,-(%sp)140    ori.w #0x700,%sr141    move.b #1,MAP142    move.w %d0,%d6              | destination format143    subq.w #1,%d0144    cmpi.w #2,%d0145    bhi.w failed146    bsr.w readmeta147    cmp.w MODE,%d6148    bne.s different149    tst.w VIEW150    bne.w success             | active data is newer than its backup151 different:152    moveq #0,%d7153    move.w %d6,%d0154    subq.w #1,%d0155    btst %d0,META+9156    beq.s target_ready157    move.w %d6,%d0158    bsr.w bankptr159    bsr.w validbank            | validate before touching either current season or snapshot160    tst.w %d0161    bne.w failed162    moveq #1,%d7163 target_ready:164    tst.w VIEW165    beq.s no_backup166    bsr.w backup167 no_backup:168    tst.w %d7169    beq.s fresh170    move.w %d6,%d0171    bsr.w bankptr172    bsr.w restore173    bra.s selected174 fresh:175    move.w %d6,MODE176    bsr.w clearseason177 selected:178    move.w %d6,MODE179    move.w %d6,META+6180    move.w #1,META+10181    move.w #1,VIEW182    .if WITH_PASS_CHARGE183    jsr 0x2E820C                    | identity/view is committed AFTER restore184    .endif185    .if WITH_PICKUP_DELAY186    jsr 0x220004                    | load after destination identity/view commit187    .endif188    .if WITH_ALT_GOALIES189    jsr 0x221010                    | load after destination identity/view commit190    .endif191    .if WITH_STRICT_SO192    jsr 0x222010                    | independent extra-SO preference after context identity commit193    .endif194    .if WITH_MOMENTUM195    jsr 0x226000                    | actual destination context committed196    .endif197    .if WITH_FACEOFF_DOTS198    jsr 0x224008                    | load after actual destination identity199    .endif200    .if WITH_AI_SHOT_PLAN201    jsr 0x223010                    | destination identity/view committed202    .endif203    .if WITH_REFINEMENTS204    jsr 0x258000                     | destination Shot Units/Fragile Glass context205    .endif206    bsr.w writemeta            | preserve current directory mask when loading an older snapshot207    bsr.w checksum208    .if WITH_TRANSFERS209    jsr 0x25600C210    .endif211 success:212    moveq #0,%d0213    bra.s selected_done214 failed:215    moveq #-1,%d0216 selected_done:217    move.b #0,MAP218    move.w (%sp)+,%sr219    movem.l (%sp)+,%d1-%d7/%a0-%a6220    .if WITH_TRANSFERS221    jsr 0x236028                   | Refresh identity previews after actual slot/view commit.222    .endif223    rts224225suspend:226    bsr.w init227    movem.l %d0-%d7/%a0-%a6,-(%sp)228    move.w %sr,-(%sp)229    ori.w #0x700,%sr230    move.b #1,MAP231    bsr.w readmeta232    tst.w VIEW233    beq.s suspended234    bsr.w backup235    clr.w VIEW236    clr.w META+10237    bsr.w writemeta238    bsr.w ctxload              | standalone native playoffs use their own context239    bsr.w checksum240suspended:241    move.b #0,MAP242    move.w (%sp)+,%sr243    movem.l (%sp)+,%d0-%d7/%a0-%a6244    rts245246restart:247    bsr.w init248    movem.l %d0-%d7/%a0-%a6,-(%sp)249    move.w %sr,-(%sp)250    ori.w #0x700,%sr251    move.b #1,MAP252    bsr.w clearseason253    bsr.w checksum254    .if WITH_TRANSFERS255    jsr 0x25600C256    .endif257    move.b #0,MAP258    move.w (%sp)+,%sr259    movem.l (%sp)+,%d0-%d7/%a0-%a6260    rts261262| Add Restart Season to season-playoff pre-game menus, keeping standalone lists native.263prelist:264    lea 0x131CC,%a0265    movea.l (%a0,%d0.w),%a0266    cmpi.w #20,%d0267    bne.s list_ready268    tst.w VIEW269    beq.s list_ready270    lea 0x2AB000,%a0271list_ready:272    rts273274| Native settings/password can be newer in RAM than their battery image.275backup:276    lea 0xFFFFD076,%a0277    move.l #0x1EF6,%d0278    move.w #256,%d1279    bsr.w write280    lea 0xFFFFD046,%a0281    move.l #EXTRA,%d0282    moveq #66,%d1283    bsr.w write284    move.w MODE,%d0285    subq.w #1,%d0286    bset %d0,META+9287    move.w MODE,META+6288    move.w #1,META+10289    bsr.w writemeta290    bsr.w checksum291    move.w MODE,%d0292    bsr.w bankptr293    movea.l %a2,%a1294    lea 0x200001,%a0295    bsr.w copybank296    movea.l %a2,%a0297    bsr.w sumbank              | the bank's shared-profile bytes differ from the active window298    rts299300| A2=validated snapshot. META in RAM remains the current directory.301restore:302    movea.l %a2,%a0303    lea 0x200001,%a1304    bsr.w copybank305    move.w 0xFFFFD046,-(%sp)           | r18: $D046 is the 4-Way Play hardware flag, not a setting:306    lea 0xFFFFD046,%a0                 | a slot saved without the adapter cleared it, the menu then307    move.l #EXTRA,%d0                  | read the pads the wrong way and took no input at all308    moveq #66,%d1309    bsr.w read310    move.w (%sp)+,0xFFFFD046311    lea 0xFFFFD076,%a0312    move.l #0x1EF6,%d0313    move.w #256,%d1314    bsr.w read315    | Native records/settings arrays are read on boot but must also follow a live switch.316    lea 0xFFFFCF36,%a0317    move.l #0xD20,%d0318    move.w #128,%d1319    bsr.w read320    lea 0xFFFFD45A,%a0321    move.l #0xDA0,%d0322    move.w #128,%d1323    bsr.w read324    jsr 0x2EB000                     | restore the target slot's own human count, including three/four325    bra.w checksum             | active keeps its shared profiles, so its checksum changes326327clearseason:328    .if WITH_TRANSFERS329    jsr 0x256008                   | Full old active odd snapshot, committed before mutations.330    jsr 0x256014                   | Reset only this format's ownership and pending preference.331    .endif332    .if WITH_REFINEMENTS333    jsr 0x258008                     | reset new even preferences AFTER the reset journal commits334    .endif335    .if WITH_MOMENTUM336    jsr 0x226008                    | reset only confirmed selected season toOn337    .endif338    .if WITH_FACEOFF_DOTS339    jsr 0x224010                    | selected confirmed restart only340    .endif341    .if WITH_AI_SHOT_PLAN342    jsr 0x223018                    | selected confirmed restart only343    .endif344    .if WITH_STRICT_SO345    jsr 0x222018                    | confirmed selected season reset only346    .endif347    .if WITH_ALT_GOALIES348    jsr 0x221018                    | reset only selected season preference and transients349    .endif350    .if WITH_PICKUP_DELAY351    jsr 0x22000C                    | reset selected format, preserve native choice352    .endif353    .if WITH_PASS_CHARGE354    jsr 0x2E821C                    | reset this format only, mapped operation355    .endif356    .if WITH_PENALTY_RATE357    .if WITH_REFINEMENTS358    move.w #1,0xFFFFF006359    .else360    clr.w 0xFFFFF006                 | fresh100% frequency, separate from native rules361    .endif362    .endif363    .if WITH_DIRECTIONAL364    clr.l 0xFFFFF0FC                | Assisted and Default cone on this season restart365    .endif366    .if WITH_PASS_SPEED367    clr.w 0xFFFFF0E8              | separate fresh/restarted season uses native Normal speed368    .endif369    .if WITH_PASS_HEIGHT370    .if WITH_REFINEMENTS371    move.w #1,0xFFFFF0DC            | fresh Low lift372    .else373    clr.w 0xFFFFF0DC              | fresh/restarted season uses native Normal lift374    .endif375    .endif376    .if WITH_PREF_DEFAULTS377    move.w #1,0xFFFFF07A              | fresh format / explicit restart defaults Hard378    .endif379    .if WITH_ICING_MENU380    move.w #10,0xFFFFD04C           | new empty slot / explicit restart defaults to Edmonton381    .if WITH_PREF_DEFAULTS382    move.w #ICING_DEFAULT,0xFFFFF0EE | fresh/restarted format defaults Icing On383    .else384    clr.w 0xFFFFF0EE               | legacy r5 default On385    .endif386    .endif387    clr.b 0x203FED                    | discard the old season settings record on an explicit restart388    lea 0x201C41,%a0            | logicalE20: all goalie/header/table/player/opponent/playoff data389    move.w #0x1080-1,%d0390 clearloop:391    clr.b (%a0)392    addq.l #2,%a0393    dbra %d0,clearloop394    lea HEAD,%a0395    moveq #7,%d0396 clearhead:397    clr.w (%a0)+398    dbra %d0,clearhead399    move.l #0x5345534E,HEAD400    move.w MODE,HEAD+12401    move.w #3,HEAD+14402    move.w #84,HEAD+10403    cmpi.w #2,MODE404    bne.s not_light405    move.w #31,HEAD+10406 not_light:407    cmpi.w #3,MODE408    bne.s not_mini409    move.w #MINI_GAMES,HEAD+10410 not_mini:411    lea HEAD,%a0412    move.l #0x1000,%d0413    moveq #16,%d1414    bsr.w write415    clr.w 0xFFFFD048416    clr.w 0xFFFFD074417    | One and Teammates are the season's supported human configurations (r16: also Three /418    | Four - Teammates with 4-Way Play, mtap.s TM = 3 / 4 with Players 5 / 6).419    cmpi.w #3,0xFFFFD04A420    beq.s players_ready421    cmpi.w #5,0xFFFFD04A422    bne.s 1f423    cmpi.w #3,0xFFFFF07C424    beq.s players_ready4251:  cmpi.w #6,0xFFFFD04A426    bne.s single_player427    cmpi.w #4,0xFFFFF07C428    beq.s players_ready429    move.w #3,0xFFFFD04A430    bra.s players_ready431 single_player:432    move.w #1,0xFFFFD04A433 players_ready:434    jsr 0x2EB010                     | restart inherits the selected slot's preferred count435    jsr 0x2EB004                     | a fresh slot also needs its own optional PC record436    move.b #1,MAP                    | store closes the mapper; the slot operation continues below437    lea 0xFFFFD088,%a0438    move.w #0xEE/2-1,%d0439 clearpassword:440    clr.w (%a0)+441    dbra %d0,clearpassword442    lea 0xFFFFD076,%a0443    move.l #0x1EF6,%d0444    move.w #256,%d1445    bsr.w write446    rts447448readmeta:449    lea META,%a0450    move.l #METAOFF,%d0451    moveq #12,%d1452    bra.s read453writemeta:454    lea META,%a0455    move.l #METAOFF,%d0456    moveq #12,%d1457    bra.s write458459| Full32-bit SRAM pointer: native helpers sign-extend their doubled word offsets.460read:461    lea 0x200001,%a1462    add.l %d0,%d0463    adda.l %d0,%a1464    subq.w #1,%d1465 readloop:466    move.b (%a1),(%a0)+467    addq.l #2,%a1468    dbra %d1,readloop469    rts470write:471    lea 0x200001,%a1472    add.l %d0,%d0473    adda.l %d0,%a1474    subq.w #1,%d1475 writeloop:476    move.b (%a0)+,(%a1)477    addq.l #2,%a1478    dbra %d1,writeloop479    rts480bankptr:481    andi.l #0xFFFF,%d0482    lsl.l #8,%d0483    lsl.l #6,%d0                 | physicalstride$4000 = logical$2000484    lea 0x200001,%a2485    adda.l %d0,%a2486    rts487| Logical D20..E1F (user profiles CF36/D45A) are shared by all seasons since r9:488| they stay in the active window; the banks' copies of that range are free.489copybank:490    move.w #0xD20-1,%d0491 copyloop:492    move.b (%a0),(%a1)493    addq.l #2,%a0494    addq.l #2,%a1495    dbra %d0,copyloop496    adda.l #0x200,%a0497    adda.l #0x200,%a1498    move.w #0x11E0-1,%d0499 copyloop2:500    move.b (%a0),(%a1)501    addq.l #2,%a0502    addq.l #2,%a1503    dbra %d0,copyloop2504    rts505506| Standalone native playoff context (password + packed playoff stats, RAM507| D088..D175 = 238 bytes) in the Full bank's free profile range, logical 2D20:508| "NPO1", 238 bytes, sum, ~sum. MAP must be on, interrupts masked.509    .equ CTX,0x2D20510ctxload:511    movem.l %d0-%d2/%a0-%a1,-(%sp)512    lea 0x200001+CTX*2,%a1513    cmpi.b #0x4E,(%a1)514    bne.s ctx_empty515    cmpi.b #0x50,2(%a1)516    bne.s ctx_empty517    cmpi.b #0x4F,4(%a1)518    bne.s ctx_empty519    cmpi.b #0x31,6(%a1)520    bne.s ctx_empty521    addq.l #8,%a1522    moveq #0,%d2523    move.w #238-1,%d0524 ctx_sum:525    add.b (%a1),%d2526    addq.l #2,%a1527    dbra %d0,ctx_sum528    cmp.b (%a1),%d2529    bne.s ctx_empty530    not.b %d2531    cmp.b 2(%a1),%d2532    bne.s ctx_empty533    lea 0x200001+CTX*2+8,%a1534    lea 0xFFFFD088,%a0535    move.w #238-1,%d0536 ctx_copy:537    move.b (%a1),(%a0)+538    addq.l #2,%a1539    dbra %d0,ctx_copy540    bra.s ctx_store541 ctx_empty:542    lea 0xFFFFD088,%a0543    move.w #238-1,%d0544 ctx_clear:545    clr.b (%a0)+546    dbra %d0,ctx_clear547 ctx_store:548    jsr 0x2EB01C                     | separate standalone player preference, mapper already enabled549    lea 0xFFFFD076,%a0550    move.l #0x1EF6,%d0551    move.w #256,%d1552    bsr.w write553    movem.l (%sp)+,%d0-%d2/%a0-%a1554    rts555ctxsave:556    movem.l %d0-%d2/%a0-%a1,-(%sp)557    lea 0x200001+CTX*2,%a1558    move.b #0x4E,(%a1)559    move.b #0x50,2(%a1)560    move.b #0x4F,4(%a1)561    move.b #0x31,6(%a1)562    addq.l #8,%a1563    lea 0xFFFFD088,%a0564    moveq #0,%d2565    move.w #238-1,%d0566 ctxs_copy:567    move.b (%a0),(%a1)568    add.b (%a0)+,%d2569    addq.l #2,%a1570    dbra %d0,ctxs_copy571    move.b %d2,(%a1)572    not.b %d2573    move.b %d2,2(%a1)574    lea 0x204001,%a0           | Full bank: keep its own checksum valid575    bsr.w sumbank576    movem.l (%sp)+,%d0-%d2/%a0-%a1577    rts578| Native settings/playoff save $FE696 ends with jsr $1A206 (replaced by this).579| Outside the season view, its D088.. block is the standalone playoff: keep a copy.580ctxhook:581    jsr 0x1A206582    cmpi.w #0x5333,TAG583    bne.s ctxh_done584    tst.w VIEW585    bne.s ctxh_done586    move.w %sr,-(%sp)587    ori.w #0x700,%sr588    move.b #1,MAP589    bsr.w ctxsave590    move.b #0,MAP591    move.w (%sp)+,%sr592 ctxh_done:593    rts594595validbank:596    movea.l %a2,%a0597    moveq #0,%d0598    move.w #0x1FFE-1,%d1599 validloop:600    add.b (%a0),%d0601    addq.l #2,%a0602    dbra %d1,validloop603    not.b %d0604    cmp.b (%a0),%d0605    bne.s invalid606    not.b %d0607    cmp.b 2(%a0),%d0608    bne.s invalid609    movea.l %a2,%a1610    adda.l #METAOFF*2,%a1611    lea HEAD,%a0612    moveq #11,%d1613 validmeta:614    move.b (%a1),(%a0)+615    addq.l #2,%a1616    dbra %d1,validmeta617    cmpi.l #MAGIC,HEAD618    bne.s invalid619    cmpi.w #1,HEAD+4620    bne.s invalid621    cmp.w HEAD+6,%d6622    bne.s invalid623    moveq #0,%d0624    rts625 invalid:626    moveq #-1,%d0627    rts628checksum:629    lea 0x200001,%a0630| sumbank: A0 = first odd byte of an 8KB image; writes its ~sum/sum at 1FFE/1FFF.631sumbank:632    move.l %d1,-(%sp)633    moveq #0,%d0634    move.w #0x1FFE-1,%d1635 sumloop:636    add.b (%a0),%d0637    addq.l #2,%a0638    dbra %d1,sumloop639    not.b %d0640    move.b %d0,(%a0)641    not.b %d0642    move.b %d0,2(%a0)643    move.l (%sp)+,%d1644    rts645