    .ifndef WITH_REFINEMENTS
    .equ WITH_REFINEMENTS,0
    .endif
    .ifndef WITH_REPLAY_HUD
    .equ WITH_REPLAY_HUD,0
    .endif
| r31: 8x5 shot panel, exactly the score box's size, directly above it:
| HUD plane E000 rows18..22 cols32..39. Smooth charge sprite centered inside;
| native KMH/MPH text uses six interior cells. Full = native raw charge31.
| Preserve recorded speed/charge through automatic replay; use resident HUD
| dash segments there because REPLAY temporarily borrows the crowd meter tiles.
| Clear on the first normal-rink frame after replay (including skip), or after
| the existing240-frame lifetime for ordinary shots. No new pattern VRAM/DMA.
| +0 logic from crowd.s; +4 sprite append (existing80-sprite bound).
    .equ MAGIC,0xFFF640
    .equ SHOOT,0xFFF642                | shooter object + 1
    .equ CHG,0xFFF644
    .equ SHOW,0xFFF646
    .equ LIVE,0xFFF648
    .equ BLINK,0xFFF64A
    .equ OBJ,0xFFFFB04A
    .equ TILE,0x7E1                    | crowd.s bar tiles: +0 black 32x8, +4 white, +8 red, partials
    .equ PANEL,0xFFF64C                | 1 while the panel is in the HUD plane
    .equ ROW0,18
    .if WITH_LEFT_PANEL
    .equ COL0,0
    .else
    .equ COL0,32
    .endif
    .equ HELD,0xFFF64E                 | last shot held throughout automatic replay
    .equ KMH,0xFFF650
    .equ MPH,0xFFF652
    .equ STILE,0x7E1                   | r38: the bar's own 4 tiles (crowd.s mkbar), HUD-plane cells
    .equ SBUF,0xFFEA00                 | crowd.s BBUF+256
    .equ BX,272+128                   | original smooth bar centered inside the 48px interior
    .equ BY,152+128
    .equ VDATA,0xC00000
    .equ VCTRL,0xC00004
    .text
    .globl _start
_start:
    bra.w logic
    bra.w draw
    .if WITH_STARS_HUD
    bra.w stars_prepare                 | +8 called BEFORE original Stars window drawing
    .else
    .if WITH_LEFT_PANEL
    rts
    nop
    .endif
    .endif
    .if WITH_LEFT_PANEL
    bra.w shotbar                      | +C shared native bar renderer
    bra.w erase                        | +10 ownership-aware clear
    bra.w panel_above                  | +14 native-font fallback
    .endif

skip:                                  | C set: not on the normal rink view / in the automatic replay
    btst #7,0xFFFFC2EC
    bne.s 1f
    move.l %d0,-(%sp)
    move.w 0xFFF738,%d0
    subi.w #0xA502,%d0
    .if WITH_STARS_HUD
    cmpi.w #6,%d0                      | postgame A507 is a foreign HUD owner, not replay playback
    .else
    cmpi.w #5,%d0
    .endif
    movem.l (%sp)+,%d0
    bcs.s 1f
    andi.b #0xFE,%ccr
    rts
1:  ori.b #1,%ccr
    rts

logic:
    movem.l %d0-%d7/%a0-%a1,-(%sp)
    cmpi.w #0x534D,MAGIC
    beq.s 1f
    move.w #0x534D,MAGIC
    clr.l SHOOT
    clr.l SHOW
    clr.w BLINK
    clr.w HELD
    .if WITH_REFINEMENTS
    clr.w 0xFFFFF8DC
    .endif
1:  move.w 0xFFF738,%d0
    subi.w #0xA502,%d0
    cmpi.w #4,%d0
    bhi.s not_replay
    tst.w SHOW
    beq.w 9f
    move.w #1,HELD                    | freeze measured speed and charge, ignore replayed actors
    bra.w 9f
not_replay:
    tst.w HELD
    beq.s normal_view
    clr.w HELD
    clr.w SHOW
    clr.w SHOOT
    clr.w LIVE
    bsr.w erase                        | replay finished: remove the panel immediately
    bra.w 9f
normal_view:
    bsr.w skip
    bcc.s 11f
    clr.w SHOW                         | ordinary pause/menus keep no stale panel
    clr.w SHOOT
    clr.w LIVE
    bsr.w erase
    bra.w 9f
11: addq.w #1,BLINK
    tst.w SHOW
    beq.s 2f
    subq.w #1,SHOW
    bne.s 2f
    bsr.w erase                        | time is up
2:  clr.w LIVE
    .if WITH_REFINEMENTS
    jsr 0x258010                        | actual held human pass, same native charge bar
    tst.w %d0
    bmi.s no_pass_bar
    move.w %d0,CHG
    move.w #1,LIVE
    move.w #1,0xFFFFF8DC
    clr.w SHOW
    clr.w SHOOT                        | releasing a pass must not be measured as a shot
    bra.w 9f
no_pass_bar:
    tst.w 0xFFFFF8DC
    beq.s shot_scan
    clr.w 0xFFFFF8DC
    clr.w SHOW
    clr.w SHOOT
    bsr.w erase
shot_scan:
    .endif
    moveq #0,%d6                       | the pads' own players only (C320.., sides C328..)
3:  lea 0xFFFFC328,%a1
    tst.w (%a1,%d6.w)
    beq.s 4f
    lea 0xFFFFC320,%a1
    move.w (%a1,%d6.w),%d7
    cmpi.w #11,%d7
    bhi.s 4f
    move.w %d7,%d0
    lsl.w #7,%d0
    lea OBJ,%a0
    adda.w %d0,%a0
    cmpi.w #0x7FC,0x58(%a0)            | winding up a slapshot with the puck
    beq.s 31f
    cmpi.w #0x92E,0x58(%a0)
    bne.s 4f
31: cmp.w 0xFFFFB7AA,%d7
    bne.s 4f
    move.w %d7,%d0
    addq.w #1,%d0
    move.w %d0,SHOOT
    move.w 0xFFFFBEDE,%d0
    cmpi.w #31,%d0
    bls.s 32f
    moveq #31,%d0
32: move.w %d0,CHG
    move.w #1,LIVE
    clr.w SHOW
    bra.w 9f
4:  addq.w #2,%d6
    cmpi.w #8,%d6
    bcs.s 3b
    move.w SHOOT,%d7                   | released?
    beq.w 9f
    subq.w #1,%d7
    move.w 0xFFFFB7AA,%d0
    cmp.w %d7,%d0
    beq.w 9f                           | still his (final swing frames)
    clr.w SHOOT
    tst.w %d0
    bpl.w 9f                           | someone else has it: no shot
    cmp.w 0xFFFFBED8,%d7               | the shooter of the loose puck
    bne.w 9f
    move.w 0xFFFFB772,%d0              | |v| = isqrt(vx^2 + vy^2)
    muls.w %d0,%d0
    move.w 0xFFFFB774,%d1
    muls.w %d1,%d1
    add.l %d1,%d0
    bsr.w isqrt
    move.l %d0,%d3
    mulu.w #763,%d0                    | km/h = V * 763 / 2^17
    moveq #17,%d1
    lsr.l %d1,%d0
    move.l %d3,%d4
    mulu.w #237,%d4                    | mph = V * 237 / 2^16
    swap %d4
    move.w %d0,KMH
    move.w %d4,MPH
    move.w #240,SHOW
9:  bsr.w refresh
    movem.l (%sp)+,%d0-%d7/%a0-%a1
    rts

| panel: d0 = km/h, d4 = mph -> the score-box panel in the HUD plane
panel:
    .if WITH_LEFT_PANEL
    jmp 0x227000
    .endif
panel_above:
    movem.l %d0-%d7/%a0-%a1,-(%sp)
    move.w %sr,-(%sp)
    ori.w #0x700,%sr
    lea VDATA,%a1
    move.w %d0,%d5
    moveq #0,%d3                       | top frame
    bsr.w cellcmd
    move.w #0xA3FF,(%a1)
    moveq #5,%d2
1:  move.w #0xA400,(%a1)
    dbra %d2,1b
    move.w #0xA401,(%a1)
    moveq #1,%d3                       | bar inside the first interior row
    bsr.w cellcmd
    move.w #0xA402,(%a1)
    tst.w HELD                         | r38 (jdc: skaters passed over the sprite bar): live, the
    bne.s held_bar                     | bar is four HUD-plane cells of its own, in front of them
coloured_bar:
    bsr.w shotbar
    move.w #0xA403,(%a1)
    move.w %d0,(%a1)
    addq.w #1,%d0
    move.w %d0,(%a1)
    addq.w #1,%d0
    move.w %d0,(%a1)
    addq.w #1,%d0
    move.w %d0,(%a1)
    move.w #0xA403,(%a1)
    bra.s bar_done
held_bar:
    .if WITH_REPLAY_HUD
    bra.w coloured_bar
    .endif
    moveq #0,%d6                       | replay uses six resident HUD dash segments
    tst.w HELD
    beq.s bar_empty
    move.w CHG,%d6
    subi.w #15,%d6
    bpl.s dash_positive
    moveq #0,%d6
dash_positive:
    cmpi.w #16,%d6
    bls.s dash_clamped
    moveq #16,%d6
dash_clamped:
    mulu.w #6,%d6
    addi.w #15,%d6
    divu.w #16,%d6
bar_empty:
    moveq #0,%d2
bar_cells:
    move.w #0xA403,%d0
    cmp.w %d6,%d2
    bcc.s dash_ready
    move.w #0x841B,%d0                  | native HUD minus, never shares replay-title pattern tiles
dash_ready:
    move.w %d0,(%a1)
    addq.w #1,%d2
    cmpi.w #6,%d2
    bcs.s bar_cells
bar_done:
    move.w #0xA404,(%a1)
    moveq #2,%d3                       | native six-cell text: 184KMH / 114MPH
    bsr.w cellcmd
    move.w #0xA402,(%a1)
    tst.w SHOW
    beq.s blank_speeds
    .if WITH_REFINEMENTS
    cmpi.w #1,0xFFFFF8D8
    beq.s selected_mph
    .endif
    move.w %d5,%d0
    bsr.w num3
    move.w #0x8438,(%a1)
    move.w #0x843A,(%a1)
    move.w #0x8435,(%a1)
    .if WITH_REFINEMENTS
    bra.s selected_speed_done
selected_mph:
    move.w %d4,%d0
    bsr.w num3
    move.w #0x843A,(%a1)
    move.w #0x843C,(%a1)
    move.w #0x8435,(%a1)
selected_speed_done:
    move.w #0xA404,(%a1)
    moveq #3,%d3
    bsr.w cellcmd
    move.w #0xA402,(%a1)
    moveq #5,%d2
selected_blank_row:
    move.w #0xA403,(%a1)
    dbra %d2,selected_blank_row
    bra.w speed_end
    .endif
    move.w #0xA404,(%a1)
    moveq #3,%d3
    bsr.w cellcmd
    move.w #0xA402,(%a1)
    move.w %d4,%d0
    bsr.w num3
    move.w #0x843A,(%a1)
    move.w #0x843C,(%a1)
    move.w #0x8435,(%a1)
    bra.s speed_end
blank_speeds:
    moveq #5,%d2
blank_first:
    move.w #0xA403,(%a1)
    dbra %d2,blank_first
    move.w #0xA404,(%a1)
    moveq #3,%d3
    bsr.w cellcmd
    move.w #0xA402,(%a1)
    moveq #5,%d2
blank_second:
    move.w #0xA403,(%a1)
    dbra %d2,blank_second
speed_end:
    move.w #0xA404,(%a1)
    moveq #4,%d3
    bsr.w cellcmd
    move.w #0xA405,(%a1)
    moveq #5,%d2
2:  move.w #0xA406,(%a1)
    dbra %d2,2b
    move.w #0xA407,(%a1)
    .if WITH_LEFT_PANEL
    move.w #2,PANEL
    .else
    move.w #1,PANEL
    .endif
    move.w (%sp)+,%sr
    movem.l (%sp)+,%d0-%d7/%a0-%a1
    rts
| shotbar: white at low charge, native orange through the middle, solid red at full.
| No palette writes or blink; compose into SBUF -> d0 = first HUD map word.
shotbar:
    movem.l %d1-%d5/%a0,-(%sp)
    move.w CHG,%d5
    subi.w #15,%d5
    bpl.s 1f
    moveq #0,%d5
1:  mulu.w #30,%d5
    divu.w #16,%d5
    cmpi.w #30,%d5
    bls.s 2f
    moveq #30,%d5
2:  moveq #1,%d1
    moveq #4,%d2
    move.w #0x8000+STILE,%d4
    cmpi.w #10,%d5                    | first third white; native palette 1 colour 5 = orange
    bcs.s 4f
    moveq #5,%d1
    moveq #7,%d2
    move.w #0xA000+STILE,%d4
    cmpi.w #30,%d5
    bcs.s 4f
    moveq #0xA,%d1                     | maximum stays red throughout the windup/release
4:  move.w %d1,%d3                     | key: colour, fill; compose only on a change, or when
    lsl.w #8,%d3                       | the panel is new (after a replay REPLAY used $7E1..)
    or.w %d5,%d3
    tst.w PANEL
    beq.s 5f
    cmp.w 0xFFE8F4,%d3
    beq.s 6f
5:  move.w %d3,0xFFE8F4
    move.w %d5,%d0
    moveq #2,%d3                       | solid top/bottom rows inside the panel (r39)
    lea SBUF,%a0
    jsr 0x2E0024                       | crowd.s mkbar
    move.l #SBUF,(%a5)+
    move.w #64,(%a5)+
    move.w #STILE*32,(%a5)+
6:  move.w %d4,%d0
    movem.l (%sp)+,%d1-%d5/%a0
    rts
erase:                                 | the panel's 8x5 cells back to transparent $07FF
    tst.w PANEL
    beq.s 9f
    .if WITH_LEFT_PANEL
    cmpi.w #1,PANEL
    bne.s erase_above
    movem.l %d0-%d2/%a1,-(%sp)
    jsr 0x227004
    tst.w %d0
    beq.s compact_erased
    jsr 0x22700C
compact_erased:
    clr.w PANEL
    movem.l (%sp)+,%d0-%d2/%a1
    rts
erase_above:
    movem.l %d0-%d1,-(%sp)
    jsr 0x227010                       | native foreign windows may now own this rectangle
    tst.w %d0
    movem.l (%sp)+,%d0-%d1
    bne.s erase_owned_above
    clr.w PANEL
    rts
erase_owned_above:
    .endif
    movem.l %d0-%d3/%a1,-(%sp)
    move.w %sr,-(%sp)
    ori.w #0x700,%sr
    lea VDATA,%a1
    moveq #0,%d3
1:  bsr.s cellcmd
    moveq #7,%d2
2:  move.w #0x07FF,(%a1)
    dbra %d2,2b
    addq.w #1,%d3
    cmpi.w #5,%d3
    bcs.s 1b
    clr.w PANEL
    move.w (%sp)+,%sr
    movem.l (%sp)+,%d0-%d3/%a1
9:  rts
cellcmd:                               | d3 = panel row 0..4 -> VDP write at (ROW0+d3, COL0) of the HUD plane
    movem.l %d0-%d1,-(%sp)
    moveq #ROW0,%d0                    | HUD plane $E000, 64 cells wide (VRAM dump r28)
    add.w %d3,%d0
    lsl.w #7,%d0
    addi.w #COL0*2,%d0                 | offset in the plane (< $2000)
    moveq #0,%d1
    move.w %d0,%d1
    swap %d1
    ori.l #0x60000003,%d1              | VRAM write at $E000 + offset
    move.w #0x8F02,VCTRL               | auto-increment 2
    move.l %d1,VCTRL
    movem.l (%sp)+,%d0-%d1
    rts
num3:                                  | d0 = 0..999 -> three HUD digit cells (leading blanks)
    movem.l %d0-%d2,-(%sp)
    andi.l #0xFFFF,%d0
    cmpi.w #999,%d0
    bls.s 1f
    move.w #999,%d0
1:  divu.w #100,%d0
    move.w %d0,%d1                     | hundreds
    swap %d0
    andi.l #0xFFFF,%d0
    divu.w #10,%d0                     | tens in d0.w, ones in the high word
    move.w #0xA403,%d2
    tst.w %d1
    beq.s 2f
    move.w #0x841E,%d2
    add.w %d1,%d2
2:  move.w %d2,(%a1)
    move.w #0xA403,%d2
    tst.w %d1
    bne.s 3f
    tst.w %d0
    beq.s 4f
3:  move.w #0x841E,%d2
    add.w %d0,%d2
4:  move.w %d2,(%a1)
    swap %d0
    addi.w #0x841E,%d0
    move.w %d0,(%a1)
    movem.l (%sp)+,%d0-%d2
    rts
isqrt:                                 | d0.l unsigned -> d0 = floor(sqrt)
    movem.l %d1-%d3,-(%sp)
    moveq #0,%d1                       | result
    move.l #0x40000000,%d2             | bit
1:  cmp.l %d0,%d2
    bls.s 2f
    lsr.l #2,%d2
    bne.s 1b
    bra.s 4f
2:  move.l %d1,%d3
    add.l %d2,%d3
    cmp.l %d3,%d0
    bcs.s 3f
    sub.l %d3,%d0
    lsr.l #1,%d1
    add.l %d2,%d1
    bra.s 5f
3:  lsr.l #1,%d1
5:  lsr.l #2,%d2
    bne.s 2b
4:  move.l %d1,%d0
    movem.l (%sp)+,%d1-%d3
    rts

| Keep native HUD map/pattern memory intact. In play the original smooth
| crowd-tile sprite bar sits inside the box; in replay those tiles belong to
| REPLAY, so the fixed recorded charge is shown with native HUD dash glyphs.
refresh:
    .if WITH_LEFT_PANEL
    bsr.w skip
    bcs.s refresh_body
    jsr 0x227008
refresh_body:
    .endif
    tst.w SHOW
    bne.s visible
    tst.w LIVE
    bne.s visible
    tst.w SHOOT
    beq.s refresh_done
visible:
    move.w KMH,%d0
    move.w MPH,%d4
    bsr.w panel
refresh_done:
    rts

draw:                                  | r38: no sprites (the bar is in the panel's HUD cells)
    rts
olddraw:
    cmpi.w #0x534D,MAGIC
    bne.w 9f
    bsr.w skip
    bcs.w 9f
    tst.w LIVE
    bne.s 1f
    tst.w SHOOT                        | the last swing frames before the release
    bne.s 1f
    tst.w SHOW
    beq.w 9f
1:  movem.l %d0-%d5,-(%sp)
    move.w CHG,%d5                     | fill 0..30 px of (CHG - 15) / 16
    subi.w #15,%d5
    bpl.s 2f
    moveq #0,%d5
2:  mulu.w #30,%d5
    divu.w #16,%d5
    move.w #0x8000+TILE+4,%d3          | white
    cmpi.w #30,%d5
    bcs.s 3f
    move.w #0xA000+TILE+8,%d3          | full: red (palette 1), blinking while still winding up
    tst.w SHOW
    bne.s 3f
    btst #2,BLINK+1
    bne.w 6f
3:  move.w #BY,%d2
    move.w %d5,%d0
    lsr.w #3,%d0                       | whole tiles
    beq.s 4f
    move.w %d0,%d1
    subq.w #1,%d1
    lsl.w #2,%d1
    move.b %d1,%d0
    move.w #BX+1,%d1
    bsr.w sprs
4:  moveq #7,%d0                       | partial width 1..7 after them (crowd.s tile order)
    and.w %d5,%d0
    beq.s 6f
    move.w %d3,%d4
    add.w #11,%d4
    add.w %d0,%d4
    subq.w #4,%d4
    cmpi.w #0xA000,%d3
    bcs.s 5f
    addq.w #3,%d4
5:  move.w %d3,-(%sp)
    move.w %d4,%d3
    move.w %d5,%d1
    andi.w #0xFFF8,%d1
    add.w #BX+1,%d1
    moveq #0,%d0
    bsr.w sprs
    move.w (%sp)+,%d3
6:  move.w #0x8000+TILE,%d3            | black 32x8 background
    moveq #0x0C,%d0
    move.w #BX,%d1
    move.w #BY,%d2
    bsr.w sprs
8:  movem.l (%sp)+,%d0-%d5
9:  rts
sprs:                                  | d0 size, d1 x, d2 y, d3 attribute (as crowd.s)
    cmpa.w #0xC298,%a6
    bcc.s 9f
    move.w %d2,(%a6)+
    move.b %d0,(%a6)+
    move.b %d6,(%a6)+
    move.w %d3,(%a6)+
    move.w %d1,(%a6)+
    addq.w #1,%d6
9:  rts
    .even

    .if WITH_STARS_HUD
stars_prepare:
    movem.l %d0-%d7/%a0-%a1,-(%sp)
    clr.l SHOOT
    clr.l SHOW
    clr.w HELD
    bsr.w erase                         | still our map cells: native Stars has not drawn yet
    movem.l (%sp)+,%d0-%d7/%a0-%a1
    rts
    .endif

