Release 2026-11-04-r2 · SHA256 b41b0a23dc743a45f6f9a6303289f982241ecbd110b7d1cb6cfb2ca0c275c79c · Click a line number for a direct link.
1 .ifndef WITH_MANUAL_KEEPER_PASS2 .equ WITH_MANUAL_KEEPER_PASS,13 .endif4| 4-Way Play goalie hold/request/Y, preserving native P3/P4 input-bank swaps.5| +0 early B, +4 legacy B, +8 Y reader, +12 Y handler, +16 cooperative sides,6| +20 new-game reset. Native D4 is always0/2; C318>=0 identifies P3/P4.7 .equ YREQ,0xFFF0B2 | four bytes at even offsets0/2/4/68 .equ YHELD,0xFFF0BA | four held-Y states, same offsets9 .equ BHELD,0xFFF0C2 | four words: consume B until released10 .equ MAGIC,0xFFF0CA11 .equ ASK,0xFFF06412 .equ OBJ,0xFFFFB04A13 .text14 .globl _start15_start:16 bra.w early17 bra.w legacy18 bra.w reader19 bra.w handler20 bra.w cooperative21 bra.w reset22reset:23 clr.w MAGIC24 clr.l ASK25 rts26init:27 cmpi.w #0x3447,MAGIC28 beq.s 9f29 clr.l YREQ30 clr.l YREQ+431 clr.l YHELD32 clr.l YHELD+433 clr.l BHELD34 clr.l BHELD+435 move.w #0x3447,MAGIC369: rts3738| Z only when at least one human is present and every active pad is on one side.39| Opposing human teams retain the native adapter behaviour. Registers preserved.40cooperative:41 movem.l %d0-%d2/%a0,-(%sp)42 lea 0xFFFFC328,%a043 moveq #0,%d044 moveq #3,%d2451: move.w (%a0)+,%d146 beq.s 3f47 cmpi.w #2,%d148 bhi.s 8f49 tst.w %d050 beq.s 2f51 cmp.w %d0,%d152 bne.s 8f532: move.w %d1,%d0543: dbra %d2,1b55 tst.w %d056 beq.s 8f57 moveq #0,%d058 bra.s 9f598: moveq #-1,%d0609: movem.l (%sp)+,%d0-%d2/%a061 rts6263allowed:64 bsr.s cooperative65 .if WITH_IMMEDIATE_B66 bne.s b_noncooperative67 .else68 bne.s 8f69 .endif70allowed_states:71 btst #0,0xFFFFC2FA72 bne.s 8f73 tst.w 0xFFFFD41274 bne.s 8f75 btst #2,0xFFFFC2F276 bne.s 8f77 btst #3,0x62(%a3)78 beq.s 8f79 btst #0,0x63(%a3)80 bne.s 8f81 moveq #0,%d082 rts838: moveq #-1,%d084 rts85 .if WITH_IMMEDIATE_B86b_noncooperative:87 btst #4,%d188 beq.s 8b89 bra.w allowed_states90 .endif91realpad: | input handler bank -> physical offset, D0 only92 move.w %d4,%d093 tst.w 0xFFFFC31894 bmi.s 9f95 addq.w #4,%d0969: rts97early:98 tst.w 0xFFFFD04699 beq.w oldearly100 movem.l %d0-%d7/%a0-%a6,-(%sp)101 bsr.w init102 bsr.s allowed103 bne.w native_early104 bsr.s realpad105 move.w %d0,%d4106 lea YREQ,%a0107 tst.b (%a0,%d4.w)108 bne.w native_early | explicit Y wins; its reader did not synthesize B109 jsr 0x2FDC0C110 tst.w %d0111 beq.w 1f112 .if WITH_IMMEDIATE_B113 cmpi.w #2,%d0114 beq.w fast115 .endif116 lea BHELD,%a0117 move.w #1,(%a0,%d4.w)118 bra.w consume1191: lea BHELD,%a0120 tst.w (%a0,%d4.w)121 beq.s flight122 btst #4,%d3123 bne.w consume124 clr.w (%a0,%d4.w)125 bra.w consume126flight: | do not switch while a pass from your goalie is travelling127 tst.w 0xFFFFB7AA128 bpl.s fast129 move.w 0xFFFFBEDA,%d0130 cmpi.w #5,%d0131 beq.w 2f132 cmpi.w #11,%d0133 bne.s fast1342: lsl.w #7,%d0135 lea OBJ,%a0136 move.b 0x62(%a0,%d0.w),%d0137 move.b 0x62(%a3),%d2138 eor.b %d2,%d0139 btst #6,%d0140 bne.s fast141 bra.w consume142| fast (2026-10-26-r22, jdc): with the adapter there was no B switch at all for co-op pads143| (the native held-B switch is off so that Y takes the keeper). A new B press, when this144| pad's player is not the puck carrier and no pass is in progress, selects the free skater145| of the pad's side nearest the puck (no goalie, no other human's player, not knocked146| down), also from a human goalie. D4 = physical pad 0..6 here; native assignment C0BC /147| C0DA keeps the bank's 0/2 (P3/P4 run in a swapped P1/P2 bank, copied back by the game).148fast:149 btst #4,%d1150 beq.w native_early151 move.w 0x52(%a3),%d0152 cmp.w 0xFFFFB7AA,%d0153 .if WITH_IMMEDIATE_B154 .if WITH_MANUAL_KEEPER_PASS155 beq.w native_early | owning manual keeper must reach native B72E/BC06 passing156 .else157 bne.s fast_without_possession158 tst.w 0x34(%a3)159 bne.w native_early160 .endif161fast_without_possession:162 .else163 beq.w native_early164 btst #3,0x63(%a3)165 bne.w native_early166 .endif167 move.w %d4,%d7 | physical pad168 andi.w #2,%d7 | the bank's own offset169 .if WITH_IMMEDIATE_B170 move.w 0xFFFFB74A,%d2171 move.w 0xFFFFB75E,%d3172 .else173 move.w 0xFFFFB772,%d2 | puck, a few frames ahead (as switch.s)174 asr.w #8,%d2175 add.w 0xFFFFB74A,%d2176 move.w 0xFFFFB774,%d3177 asr.w #8,%d3178 add.w 0xFFFFB75E,%d3179 .endif180 lea OBJ,%a0181 lea 0xFFFFC328,%a1182 cmpi.w #1,(%a1,%d7.w)183 beq.s 3f184 cmpi.w #2,(%a1,%d7.w)185 bne.w native_early186 adda.w #0x300,%a01873: moveq #-1,%d5 | best object188 moveq #-1,%d6 | best squared distance189 swap %d7190 move.w #5,%d71914: tst.w 0x34(%a0)192 ble.s 5f | goalie / injured / unavailable slot193 move.w 0x52(%a0),%d0194 cmp.w 0x52(%a3),%d0195 beq.s 5f | another player, not the same one196 btst #2,0x63(%a0)197 bne.s 5f198 btst #3,0x62(%a0)199 bne.s 5f | never another human's player200 btst #5,0x62(%a0)201 bne.s 5f | knocked down / unavailable202 move.w %d2,%d0203 sub.w (%a0),%d0204 muls.w %d0,%d0205 move.w %d3,%d1206 sub.w 0x14(%a0),%d1207 muls.w %d1,%d1208 add.l %d1,%d0209 cmp.l %d6,%d0210 bhi.s 5f211 move.l %d0,%d6212 move.w 0x52(%a0),%d52135: adda.w #0x80,%a0214 dbra %d7,4b215 swap %d7216 tst.w %d5217 bmi.w native_early | nobody free: as before218 .if WITH_IMMEDIATE_B219 move.w %sr,-(%sp)220 ori.w #0x700,%sr221 move.b 0x63(%a3),-(%sp)222 bclr #3,0x63(%a3) | Native assignment otherwise refuses the released pass animation223 .endif224 move.w %d5,%d0225 tst.w %d7226 bne.s 6f227 jsr 0xC0BC228 bra.s 7f2296: jsr 0xC0DA2307:231 .if WITH_IMMEDIATE_B232 move.b (%sp)+,0x63(%a3) | preserve the actual in-flight pass/animation state233 move.w (%sp)+,%sr234 | A brief B tap must also publish the outlet for the newly selected skater.235 | Use the original physical press, even if the hold ends before its next tick.236 move.l 4(%sp),%d1237 move.l 12(%sp),%d3238 bclr #4,%d1239 ori.w #16,%d3240 move.l %a3,-(%sp)241 move.w %d5,%d0242 lsl.w #7,%d0243 lea 0xFFFFB04A,%a3244 adda.w %d0,%a3245 jsr 0x2FDC0C246 movea.l (%sp)+,%a3247 .endif248 lea BHELD,%a0 | consume this press until B is released249 move.w #1,(%a0,%d4.w)250 movem.l (%sp)+,%d0-%d7/%a0-%a6251 rts | the old object's input handler ends after selection252consume:253 movem.l (%sp)+,%d0-%d7/%a0-%a6254 bclr #4,%d1255 bclr #4,%d2256 bclr #4,%d3257 bra.s original_flags258native_early:259 movem.l (%sp)+,%d0-%d7/%a0-%a6260original_flags:261 btst #3,0x63(%a3)262 bne.s 1f263 jmp 0xB1C02641: jmp 0xBA88265oldearly:266 .if WITH_IMMEDIATE_B267 jmp 0x2DB000268 .else269 jmp 0x2FD900 | original two-pad fast-switch code and RAM stay intact270 .endif271legacy:272 tst.w 0xFFFFD046273 beq.s 9f274 move.l %d0,-(%sp)275 bsr.w allowed276 tst.w %d0277 movem.l (%sp)+,%d0278 bne.s original_b279 jmp 0xB42E | co-op adapter: Y, not a held B, explicitly takes the keeper280original_b:281 btst #4,%d3282 beq.s 1f283 jmp 0xB2E22841: jmp 0xB35C2859:286 .if WITH_IMMEDIATE_B287 jmp 0x2DB004288 .else289 jmp 0x2FD904290 .endif291292reader:293 tst.w 0xFFFFD046294 beq.w oldreader295 .if WITH_QUICKGAME296 tst.w 0xFFFFF84C297 bne.s quick_reader298 .endif299 bsr.w cooperative300 bne.w oldreader301 .if WITH_QUICKGAME302quick_reader:303 .endif304 movem.l %d3/%a1,-(%sp)305 bsr.w init306 moveq #0,%d0307 move.b 0xFFFFBF06,%d0 | hardware reader's bits7:6 select virtual pad0..3308 lsr.w #6,%d0309 add.w %d0,%d0310 bsr.w latch_b | keep a brief hardware B request even between skater input updates311 lea YREQ,%a1312 btst #1,%d2313 bne.w yrelease314 tst.b 8(%a1,%d0.w)315 bmi.w yreturn316 bne.w yasb317 btst #0,0xFFFFC2FA | native shootout/penalty-shot controls remain untouched318 bne.w yasb319 tst.w 0xFFFFD412320 bne.w yasb321 lea 0xFFFFC328,%a1322 move.w (%a1,%d0.w),%d3323 beq.w yasb324 cmpi.w #2,%d3325 bhi.w yasb326 subq.w #1,%d3327 add.w %d3,%d3328 lea 0xFFFFD05A,%a1329 tst.w (%a1,%d3.w) | goalie control is per side, not per physical pad330 bne.w yasb331 lea 0xFFFFC320,%a1332 move.w (%a1,%d0.w),%d3333 bmi.w yasb334 cmpi.w #11,%d3335 bhi.w yasb336 move.w 0xFFFFB7AA,%d2337 bmi.s ywant338 cmp.w %d2,%d3 | r24 (jdc): Y stays a pass only with YOUR puck or your own339 beq.s yoldb | goalie's (the outlet); with a teammate skater's puck Y took340 cmpi.w #5,%d2 | B's place and (r22) switched skaters instead of the keeper341 beq.s 1f342 cmpi.w #11,%d2343 bne.s ywant3441: subq.w #6,%d2345 subq.w #6,%d3346 eor.w %d2,%d3347 bpl.s yoldb348ywant:349 clr.l ASK | an explicit manual-Y request wins over a queued outlet350 lea YREQ,%a1351 st (%a1,%d0.w)352 st 8(%a1,%d0.w)353 bra.s yreturn354yoldb:355 lea YHELD,%a1356 move.b #1,(%a1,%d0.w)357 bra.s yasb358yrelease:359 clr.b (%a1,%d0.w)360 clr.b 8(%a1,%d0.w)361yreturn:362 movem.l (%sp)+,%d3/%a1363 jmp 0x1FFBF0364yasb:365 bclr #4,%d1366 bsr.w latch_b367 movem.l (%sp)+,%d3/%a1368 jmp 0x1FFC0A369oldreader:370 jmp 0x2FE040371372| Raw active-low B in D1, physical pad offset in D0. Called while polling that373| adapter pad, before the native game can replace a one-frame input sample.374| Only requests a human-side AI keeper already holding/within24 px of the puck.375latch_b:376 movem.l %d0-%d5/%a0-%a1,-(%sp)377 move.w %sr,-(%sp)378 btst #4,%d1379 bne.w latch_done380 btst #7,0xFFFFC2EC | raw menu/replay buttons must not become deferred game requests381 bne.w latch_done382 btst #0,0xFFFFC2EC383 bne.w latch_done384 btst #0,0xFFFFC2FA385 bne.w latch_done386 tst.w 0xFFFFD412387 bne.w latch_done388 move.b 0xFFFFC2EA,%d1389 andi.b #0x11,%d1390 bne.w latch_done391 btst #2,0xFFFFC2F2392 bne.w latch_done393 btst #3,0xFFFFC2F6394 bne.w latch_done395 lea 0xFFFFC328,%a1396 move.w (%a1,%d0.w),%d1397 moveq #5,%d2398 cmpi.w #1,%d1399 beq.s 1f400 moveq #11,%d2401 cmpi.w #2,%d1402 bne.w latch_done4031: lea 0xFFFFC320,%a1404 move.w (%a1,%d0.w),%d3405 cmpi.w #11,%d3406 bhi.w latch_done407 lsl.w #7,%d3408 lea OBJ,%a1409 tst.w 0x34(%a1,%d3.w)410 ble.w latch_done411 btst #3,0x62(%a1,%d3.w)412 beq.w latch_done413 move.w %d2,%d3414 lsl.w #7,%d3415 lea (%a1,%d3.w),%a0416 tst.w 0x34(%a0)417 bne.w latch_done418 btst #3,0x62(%a0)419 bne.w latch_done420 move.w 0xFFFFB7AA,%d3421 cmp.w %d2,%d3422 beq.s 4f423 tst.w %d3424 bpl.s latch_done425 move.w 0xFFFFB74A,%d3426 sub.w (%a0),%d3427 bpl.s 2f428 neg.w %d34292: cmpi.w #24,%d3430 bhi.s latch_done431 move.w 0xFFFFB75E,%d3432 sub.w 0x14(%a0),%d3433 bpl.s 3f434 neg.w %d34353: cmpi.w #24,%d3436 bhi.s latch_done4374: ori.w #0x700,%sr | check-and-publish together: first valid hardware request wins438 cmp.w ASK+2,%d2439 bne.s 5f440 tst.w ASK441 bne.s latch_done4425: addq.w #1,%d0443 move.w %d0,ASK444 move.w %d2,ASK+2445 .if WITH_OUTLET_DEADLINE446 jsr 0x2DB400 | first hardware publication only; holds never refresh it447 .endif448latch_done:449 move.w (%sp)+,%sr450 movem.l (%sp)+,%d0-%d5/%a0-%a1451 rts452453handler:454 tst.w 0xFFFFD046455 beq.w oldhandler456 .if WITH_QUICKGAME457 tst.w 0xFFFFF84C458 bne.s quick_handler459 .endif460 bsr.w cooperative461 bne.w oldhandler462 .if WITH_QUICKGAME463quick_handler:464 .endif465 movem.l %d0-%d1/%a1,-(%sp)466 bsr.w realpad467 lea YREQ,%a1468 tst.b (%a1,%d0.w)469 beq.w no_y470 clr.b (%a1,%d0.w)471 lea BHELD,%a1472 clr.w (%a1,%d0.w)473 tst.w 0xFFFFD412474 bne.w no_y475 btst #0,0xFFFFC2FA476 bne.w no_y477 move.w 0xFFFFB7AA,%d0478 bmi.s 1f479 cmp.w %d6,%d0 | r24: refuse while this player carries the puck or your own480 beq.s no_y | goalie holds it (Y is then the outlet request)481 cmpi.w #5,%d0482 beq.s 4f483 cmpi.w #11,%d0484 bne.s 1f4854: move.w %d6,%d1486 subq.w #6,%d0487 subq.w #6,%d1488 eor.w %d1,%d0489 bpl.s no_y4901: moveq #5,%d0491 cmpi.w #5,%d6492 ble.s 2f493 moveq #11,%d04942: jsr 0xB86A495 tst.w %d0496 bmi.s no_y497 move.w %d0,%d1498 lsl.w #7,%d1499 lea OBJ,%a1500 btst #3,0x62(%a1,%d1.w)501 bne.s no_y | never steal a keeper already controlled by another pad502 clr.l ASK503 addq.l #4,%sp504 movem.l (%sp)+,%d1/%a1505 tst.w %d4 | physical P3/P4 use the native temporary C320/C322 bank506 bne.s 3f507 jmp 0xC0BC5083: jmp 0xC0DA509no_y:510 movem.l (%sp)+,%d0-%d1/%a1511 jmp 0x20011A512oldhandler:513 jmp 0x2FE100514