        .include "features.inc"
| Season mode: playoffs after the season, NHL format. Mini/Light use one game per pairing; full season uses best of 7.
| 16 teams: the top 3 of each division and 2 wild cards per conference (the best of the rest). East = Atlantic +
| Metropolitan (tree slots 8-15, right side), West = Central + Pacific (slots 0-7, left side). In each conference the
| better division winner meets the second wild card, the other winner the first; 2nd meets 3rd in each division.
| Pairs in the tree: (0,1) (2,3) (4,5) (6,7), the winners of (0,1)/(2,3) and (4,5)/(6,7) meet in round 2.
|
| The game's playoffs: $CEF4 = the 16 teams, copied from the ROM table $5576 + 16 * $CEE8 (32 presets) by $17D80;
| New Playoffs ($17D16) picks a preset with your team (Home) in it and stores your slot in $CEEE. State is kept as the
| game's password at $D088 (saved with the settings in battery RAM), so Continue Playoffs works across power-offs.
| Hooks: $17D8E (movea.l #$5576,a1 / adda.w d1,a1) -> jsr fetch / nop: the season's 16 teams instead of a preset;
| $17D4A (move.w d1,$CEEE / move.w d0,$CEE8) -> jsr newpo / nop: your slot in the season's tree.
| "Season playoffs" = a valid season block, not New Season, and your games played >= the season length.
| SRAM $1210 (word, free): 0 not started, 1 set up (New Playoffs or New Playoffs/7 games, no game yet), 2 a playoff game was played
| (the game's own state is then in its password, Continue Playoffs), 3 over (the game ended the playoffs: menu free);
| jdc r18: 3 = out, 4 = champion, 5 = missed the playoffs; 3..5 = over, the season summary has been shown.

        .equ    SRAM_OFF, 0x1000
        .equ    NBYTES,   16+32*16
        .equ    MAGIC,    0x5345534E
        .equ    SRAM_RD,  0x1A244
        .equ    SRAM_WR,  0x1A1E4
        .equ    FLAG_OFF, 0x1210
        .equ    PBUF,     0xFFF400       | season block (528 bytes)
        .equ    KEYS,     0xFFF620       | 32 longs: (points*256 + goal difference + 128) * 32 + (31 - team)
        .equ    TAKEN,    0xFFF6A0       | 32 bytes
        .equ    SEEDS,    0xFFF6C0       | 16 teams in tree order
        .equ    QUAL,     0xFFF6D0       | 32 bytes: 1 = top 3 of the division, 2 = wild card, 0 = out
        .equ    DR,       0xFFF6F0       | 4 divisions x 3 teams
        .equ    TMP,      0xFFF700       | 4 bytes
        .equ    HOME,     0xFFD04C

        .text
        .globl  _start
_start: bra.w   fetch                   | +0  hook $17D8E
        bra.w   newpo                   | +4  hook $17D4A
        bra.w   seeds                   | +8  d0 = 0 (and Z) when SEEDS / QUAL are valid
        bra.w   autopo                  | +12 season over, first time: d0 = 0 when the playoffs should start now
        bra.w   clrflag                 | +16 a new season starts
        bra.w   spcheck                 | +20 season menu: are the season's playoffs on? (see there)
        bra.w   played                  | +24 a playoff game was played (season.s)
        bra.w   setflag                 | +28 d0 -> SRAM flag (seasonend.s: 3 out, 4 champion, 5 missed, summary shown)
        bra.w   poseason                | +32 d0 = 0: this game is one of the season's playoff games in progress

| ---- $17D8E: a1 = the 16 teams of this tree
fetch:  movem.l %d0-%d7/%a0/%a2-%a6,-(%sp)
        bsr.w   active
        movem.l (%sp)+,%d0-%d7/%a0/%a2-%a6   | (movem to registers keeps the flags)
        bne.s   1f
        lea     SEEDS,%a1
        rts
1:      movea.l #0x5576,%a1
        adda.w  %d1,%a1
        rts

| ---- $17D4A: New Playoffs has picked a preset (d0) and your slot (d1)
newpo:  move.w  %d1,0xFFCEEE
        move.w  %d0,0xFFCEE8
        movem.l %d0-%d7/%a0-%a6,-(%sp)
        bsr.w   active
        bne.s   9f
        move.w  HOME,%d0
        bsr.w   slotof
        bmi.s   9f
        move.w  %d1,0xFFCEEE
        clr.w   0xFFCEE8
9:      movem.l (%sp)+,%d0-%d7/%a0-%a6
        rts

| slotof: d0 = team -> d1 = slot in SEEDS (N flag set if not there)
slotof: lea     SEEDS,%a0
        moveq   #0,%d1
1:      cmp.b   (%a0,%d1.w),%d0
        beq.s   2f
        addq.w  #1,%d1
        cmpi.w  #16,%d1
        blt.s   1b
        moveq   #-1,%d1
        rts
2:      tst.w   %d1
        rts

| active: season playoffs? d0 = 0 (Z) and SEEDS filled, else d0 = -1
active: tst.w   0xFFFFF06C             | standalone native modes must not modify a suspended season
        beq.w   8f
        bsr.w   seeds
        bne.w   8f
        move.w  PBUF+8,%d0              | your games played
        cmp.w   PBUF+10,%d0             | season length
        bcs.s   8f
        tst.w   PBUF+6                  | a season with your team
        beq.s   8f
        moveq   #0,%d0
        rts
8:      moveq   #-1,%d0
        rts

| ---- autopo: called by the season menu when your season is over. d0 = 0 when it should switch to the playoffs now
| (first time only, and your team made it); the flag is set here.
autopo: movem.l %d1-%d7/%a0-%a6,-(%sp)
        lea     TMP,%a0
        move.l  #FLAG_OFF,%d0
        moveq   #2,%d1
        jsr     SRAM_RD
        tst.w   TMP
        bne.s   8f                      | already switched once
        bsr.w   active
        bne.s   8f
        move.w  PBUF+6,%d0
        subq.w  #1,%d0
        bsr.w   slotof
        bmi.s   8f                      | your team did not make it
        moveq   #1,%d0
        bsr.w   setflag
        moveq   #0,%d0
        bra.s   9f
8:      moveq   #-1,%d0
9:      movem.l (%sp)+,%d1-%d7/%a0-%a6
        tst.w   %d0
        rts

| ---- spcheck: d0 = -1 not the season's playoffs (menu as usual); 0 = they are on and the menu is already set;
| 1 / 2 / 3 = they are on and Play Mode must be set to this (1 Continue, 2 single game, 3 best of 7). d2 = your team.
spcheck:
        movem.l %d1/%d3-%d7/%a0-%a6,-(%sp)
        bsr.w   active
        bne.w   8f
        move.w  PBUF+6,%d0
        subq.w  #1,%d0
        move.w  %d0,%d2                 | your team
        bsr.w   slotof
        .if MINI_GAMES != 8
        bmi.w   8f                      | preserve reach after adding current and legacy Mini lengths
        .else
        bmi.s   8f                      | not in the playoffs
        .endif
        lea     TMP,%a0
        move.l  #FLAG_OFF,%d0
        moveq   #2,%d1
        jsr     SRAM_RD
        move.w  TMP,%d4
        cmpi.w  #3,%d4
        bcc.s   8f                      | over (3 out, 4 champion)
        moveq   #3,%d3                  | New Playoffs/7 games
        .if MINI_GAMES != 8
        cmpi.w  #MINI_GAMES,PBUF+10
        beq.s   7f
        .endif
        cmpi.w  #8,PBUF+10
        beq.s   7f
        cmpi.w  #31,PBUF+10
        bne.s   6f
7:      moveq   #2,%d3                  | New Playoffs: one game decides the pairing
6:
        cmpi.w  #2,%d4
        bne.s   2f
        move.w  0xFFD048,%d0            | after a game the game sets 1 (go on) or 2/3 (the playoffs are over)
        cmpi.w  #2,%d0
        beq.s   1f
        cmpi.w  #3,%d0
        bne.s   3f
1:      moveq   #3,%d0
        bsr.w   setflag
        bra.s   8f
3:      moveq   #1,%d3                  | Continue Playoffs
        bra.s   4f
2:      tst.w   %d4
        bne.s   4f
        moveq   #1,%d0
        bsr.w   setflag
4:      moveq   #0,%d0
        cmp.w   0xFFD048,%d3
        bne.s   5f
        cmp.w   HOME,%d2
        beq.s   9f
5:      move.w  %d3,%d0
        bra.s   9f
8:      moveq   #-1,%d0
9:      movem.l (%sp)+,%d1/%d3-%d7/%a0-%a6
        tst.w   %d0
        rts

| ---- played: a game in a playoff mode is over (season.s): flag 2 if these are the season's playoffs
played: movem.l %d0-%d7/%a0-%a6,-(%sp)
        bsr.w   active
        bne.s   1f
        lea     TMP,%a0                 | jdc r18: the season's playoffs are over (3..5): a later standalone
        move.l  #FLAG_OFF,%d0           | New Playoffs must not reopen them
        moveq   #2,%d1
        jsr     SRAM_RD
        cmpi.w  #3,TMP
        bcc.s   1f
        moveq   #2,%d0
        bsr.w   setflag
1:      movem.l (%sp)+,%d0-%d7/%a0-%a6
        rts

| ---- poseason (season.s at game over, before played): d0 = 0 when the season's playoffs are in progress (flag 1/2)
| and your team plays this game; else -1. Keeps all other registers.
poseason:
        movem.l %d1-%d7/%a0-%a6,-(%sp)
        bsr.w   active
        bne.s   8f
        move.w  PBUF+6,%d2
        subq.w  #1,%d2
        moveq   #0,%d0
        move.b  0xFFC331,%d0
        cmp.w   %d0,%d2
        beq.s   1f
        move.b  0xFFC333,%d0
        cmp.w   %d0,%d2
        bne.s   8f
1:      lea     TMP,%a0
        move.l  #FLAG_OFF,%d0
        moveq   #2,%d1
        jsr     SRAM_RD
        move.w  TMP,%d0
        beq.s   8f
        cmpi.w  #3,%d0
        bcc.s   8f
        moveq   #0,%d0
        bra.s   9f
8:      moveq   #-1,%d0
9:      movem.l (%sp)+,%d1-%d7/%a0-%a6
        tst.w   %d0
        rts

clrflag:
        movem.l %d0-%d7/%a0-%a6,-(%sp)
        moveq   #0,%d0
        bsr.s   setflag
        movem.l (%sp)+,%d0-%d7/%a0-%a6
        rts

| setflag: d0 = value -> SRAM $1210 (word), the hack's checksum ($1FFE = ~sum, $1FFF = sum) kept right
setflag:
        cmpi.w  #1,%d0                  | jdc r21: the playoffs are set up: clear their statistics block
        bne.s   0f
        jsr     0x2E2008
0:      movem.l %d0-%d7/%a0-%a6,-(%sp)
        move.w  %d0,%d7
        lea     TMP,%a0
        move.l  #FLAG_OFF,%d0
        moveq   #2,%d1
        jsr     SRAM_RD
        move.b  TMP,%d6                 | d6 = - old bytes + new bytes
        add.b   TMP+1,%d6
        neg.b   %d6
        move.w  %d7,%d0
        add.b   %d0,%d6
        lsr.w   #8,%d0
        add.b   %d0,%d6
        move.w  %d7,TMP
        lea     TMP,%a0
        move.l  #FLAG_OFF,%d0
        moveq   #2,%d1
        jsr     SRAM_WR
        lea     TMP,%a0
        move.l  #0x1FFE,%d0
        moveq   #2,%d1
        jsr     SRAM_RD
        move.b  TMP+1,%d0
        add.b   %d6,%d0
        move.b  %d0,TMP+1
        not.b   %d0
        move.b  %d0,TMP
        lea     TMP,%a0
        move.l  #0x1FFE,%d0
        moveq   #2,%d1
        jsr     SRAM_WR
        movem.l (%sp)+,%d0-%d7/%a0-%a6
        rts

| ---- seeds: the tree and who is in, from the season table in battery RAM
seeds:  movem.l %d1-%d7/%a0-%a6,-(%sp)
        lea     PBUF,%a0
        move.l  #SRAM_OFF,%d0
        move.l  #NBYTES,%d1
        jsr     SRAM_RD
        cmpi.l  #MAGIC,PBUF
        bne.w   90f
        tst.w   PBUF+12                 | New Season chosen
        bne.w   90f
        | keys
        lea     PBUF+16,%a0
        lea     KEYS,%a1
        lea     TAKEN,%a2
        lea     QUAL,%a3
        moveq   #0,%d0
1:      move.w  2(%a0),%d1              | 2 W + OTL + T
        add.w   %d1,%d1
        add.w   6(%a0),%d1
        add.w   12(%a0),%d1
        lsl.w   #8,%d1
        move.w  8(%a0),%d2              | goal difference, clamped
        sub.w   10(%a0),%d2
        cmpi.w  #127,%d2
        ble.s   2f
        moveq   #127,%d2
2:      cmpi.w  #-128,%d2
        bge.s   3f
        move.w  #-128,%d2
3:      addi.w  #128,%d2
        add.w   %d2,%d1
        andi.l  #0xFFFF,%d1
        lsl.l   #5,%d1
        moveq   #31,%d2
        sub.w   %d0,%d2
        or.w    %d2,%d1                 | equal: the lower team index first (as in the tables)
        bset    #31,%d1                 | never 0 (pick starts from 0)
        move.l  %d1,(%a1)+
        clr.b   (%a2)+
        clr.b   (%a3)+
        lea     16(%a0),%a0
        addq.w  #1,%d0
        cmpi.w  #32,%d0
        blt.s   1b
        | top 3 of each division
        lea     DR,%a4
        moveq   #0,%d3                  | division
4:      moveq   #2,%d4
5:      moveq   #0,%d5                  | 0 = by division
        bsr.w   pick
        move.b  %d0,(%a4)+
        lea     QUAL,%a0
        move.b  #1,(%a0,%d0.w)
        dbra    %d4,5b
        addq.w  #1,%d3
        cmpi.w  #4,%d3
        blt.s   4b
        | each conference: two wild cards, then the tree (conference 1 = West -> slots 0-7, 0 = East -> 8-15)
        moveq   #0,%d3
6:      moveq   #1,%d5                  | by conference
        bsr.w   pick
        move.w  %d0,%d6                 | WC1
        lea     QUAL,%a0
        move.b  #2,(%a0,%d0.w)
        bsr.w   pick
        move.w  %d0,%d7                 | WC2
        move.b  #2,(%a0,%d0.w)
        lea     DR,%a4
        move.w  %d3,%d0                 | divisions 2c and 2c+1
        add.w   %d0,%d0
        mulu.w  #3,%d0
        lea     (%a4,%d0.w),%a5         | a5 = first division's 3, a6 = second's
        lea     3(%a5),%a6
        lea     KEYS,%a0
        moveq   #0,%d0
        move.b  (%a5),%d0
        lsl.w   #2,%d0
        move.l  (%a0,%d0.w),%d1
        moveq   #0,%d0
        move.b  (%a6),%d0
        lsl.w   #2,%d0
        cmp.l   (%a0,%d0.w),%d1
        bhi.s   7f
        exg     %a5,%a6                 | the second division's winner is better
7:      lea     SEEDS+8,%a1             | East
        tst.w   %d3
        beq.s   8f
        lea     SEEDS,%a1               | West
8:      move.b  (%a5),(%a1)+            | better winner vs WC2
        move.b  %d7,(%a1)+
        move.b  1(%a5),(%a1)+           | 2nd vs 3rd
        move.b  2(%a5),(%a1)+
        move.b  (%a6),(%a1)+            | other winner vs WC1
        move.b  %d6,(%a1)+
        move.b  1(%a6),(%a1)+
        move.b  2(%a6),(%a1)+
        addq.w  #1,%d3
        cmpi.w  #2,%d3
        blt.w   6b
        moveq   #0,%d0
        bra.s   99f
90:     moveq   #-1,%d0
99:     movem.l (%sp)+,%d1-%d7/%a0-%a6
        tst.w   %d0
        rts

| pick: the best team not taken yet in division d3 (d5 = 0) or conference d3 (d5 = 1; 0 East, 1 West) -> d0, taken
pick:   movem.l %d1-%d4/%a0-%a2,-(%sp)
        lea     KEYS,%a0
        lea     TAKEN,%a1
        lea     divs,%a2
        moveq   #-1,%d0                 | best team
        moveq   #0,%d1                  | best key
        moveq   #0,%d2                  | team
1:      tst.b   (%a1,%d2.w)
        bne.s   3f
        moveq   #0,%d4
        move.b  (%a2,%d2.w),%d4
        tst.w   %d5
        beq.s   2f
        lsr.w   #1,%d4                  | conference
2:      cmp.w   %d3,%d4
        bne.s   3f
        move.w  %d2,%d4
        lsl.w   #2,%d4
        cmp.l   (%a0,%d4.w),%d1
        bcc.s   3f
        move.l  (%a0,%d4.w),%d1
        move.w  %d2,%d0
3:      addq.w  #1,%d2
        cmpi.w  #32,%d2
        blt.s   1b
        tst.w   %d0
        bmi.s   4f
        move.b  #1,(%a1,%d0.w)
4:      movem.l (%sp)+,%d1-%d4/%a0-%a2
        rts

| division per team (ANA..WPG): 0 Atlantic, 1 Metropolitan, 2 Central, 3 Pacific (conference = division / 2)
divs:   .byte   3,0,0,3,1,2,2,1,2,0,3,0,3,2,0,2,1,1,1,0,1,1,3,3,2,0,0,2,3,3,1,2
        .even
