;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
;
; Auto-generated by Regenerator 2000 v0.9.17
; https://github.com/ricardoquesada/regenerator2000
;
; Exported from: pet_loderunner.regen2000proj
;
; Assemble with 64tass:
; 64tass -o pet_loderunner.prg pet_loderunner.asm
;
;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; EXTERNAL LABELS
; zpa_ = Zero Page Absolute Address
; zpp_ = Zero Page Pointer
; f_ = Field
; a_ = Absolute Address
; p_ = Pointer
; L_ = Other / User-defined
zp_key_row_down = $28 ; TXTTAB Pointer: Start of BASIC Text ; x-ref: $0E35, $1647, $1A38, $1A5F, $319A
zp_key_row_up = $29 ; x-ref: $0E26, $318B
zp_key_row_left = $2a ; VARTAB Pointer: Start of BASIC Variables ; x-ref: $0E44, $36E1
zp_key_row_right = $2b ; x-ref: $0E53, $36F0
zp_key_row_dig1 = $2c ; ARYTAB Pointer: Start of BASIC Arrays ; x-ref: $0E02, $31A9, $36B5
zp_key_row_dig2 = $2d ; x-ref: $0E14, $31B5, $36C1
zp_key_row_pause = $2e ; STREND Pointer End of BASIC Arrays (+1) ; x-ref: $290D
zp_key_col_down = $31 ; x-ref: $0E3D, $1645, $1A31, $1A4A, $1A5B, $1A7E, $1A83, $31A2
zp_key_col_up = $32 ; FRESPC Utility String Pointer ; x-ref: $0E2E, $3193
zp_key_col_left = $33 ; x-ref: $0E4C, $36E9
zp_key_col_right = $34 ; MEMSIZ Pointer: Highest Address Used by BASIC ; x-ref: $0E5B, $36F8
zp_key_col_dig1 = $35 ; x-ref: $0E0A, $31B1, $36BD
zp_key_col_dig2 = $36 ; CURLIN Current BASIC Line Number ; x-ref: $0E1C, $31BD, $36C9
zp_key_col_pause = $37 ; x-ref: $2915
zp_random_seed = $50 ; x-ref: $060B, $0772, $077D
zp_screen_width = $51 ; x-ref: $0843, $0B1E, $0B55, $0B86, $0BB7, $0BE8, $1666, $16EB
zp_ptr_screen_lo = $52 ; x-ref: $0DBC, $114A, $1177, $11C4, $162B
zp_ptr_screen_hi = $53 ; x-ref: $113F, $116F
zp_ptr_attr_lo = $54 ; TEMPF1 Temporary storage for FLPT value. ; x-ref: $0DBE, $1150, $1156, $115C, $117D, $1183, $1189, $11CF, ...
zp_ptr_attr_hi = $55 ; x-ref: $1142, $1172
zp_ptr_render_buf_lo = $56 ; x-ref: $11EA, $12B7, $12BF, $12C6, $12CE, $12D9, $12DD, $12E5, ...
zp_ptr_render_buf_hi = $57 ; x-ref: $11EE, $1215, $121A, $121F, $1224, $1226, $12BB, $1303, ...
zp_ptr_wipe_buf_lo = $58 ; x-ref: $09DF, $0A09, $0A27, $0A59, $0A8B, $0B2F, $0B69, $0B9A, ...
zp_ptr_wipe_buf_hi = $59 ; TEMPF2 Temporary storage for FLPT value. ; x-ref: $09F3, $09FC, $0A11, $0A1A, $0A2F, $0A3C, $0A4C, $0A61, ...
zp_ptr_playfield_lo = $5a ; x-ref: $0DC2, $0E8E, $0EBF, $0EF2, $0F0F, $0F2B, $0F4C, $0F71, ...
zp_ptr_playfield_hi = $5b ; x-ref: $0F6F, $0F73, $0FEB, $0FEF, $10B1, $1C13, $1C1E, $1C20, ...
zp_ptr_attr_ram_lo = $5c ; x-ref: $1CBA, $1CCA, $1D5F, $1D6D, $1D7A, $1D88, $1D97, $1DA3, ...
zp_ptr_attr_ram_hi = $5d ; x-ref: $1CC2, $1CD1, $1CD3, $1D13, $1D44, $1D5D, $1D78, $1D8E
zp_ptr_move_grid_lo = $5e ; FACEXP Floating-Point Accumulator #1: Exponent ; x-ref: $1CBE, $1D65, $1D80, $1D9B, $1DB2
zp_ptr_move_grid_hi = $5f ; FACHO Floating Accum. #1: Mantissa ; x-ref: $1CC4, $1D63, $1D7E, $1D90
zp_ptr_pathfinding_lo = $60 ; x-ref: $1C39, $2237
zp_ptr_pathfinding_hi = $61 ; x-ref: $1C34
zp_queue_head_temp = $62 ; x-ref: $1C8A, $1C8C, $1CAF, $1CDB, $1D48, $1D4D, $1D4F
zp_queue_tail = $63 ; FACSGN Floating Accum. #1: Sign ; x-ref: $1C7C, $1C7E, $1CAB, $1CB1, $1CDD, $1D46
zp_scratch_reg_a = $64 ; SGNFLG Pointer: Series Evaluation Constant Pointer ; x-ref: $13FF, $1405, $140B, $145B, $27C3, $27E2, $281D, $281F
zp_scratch_reg_x = $65 ; BITS Floating -accum. #1: Overflow Digit ; x-ref: $1C79, $1C87, $1C95, $1CB3
zp_scratch_reg_y = $66 ; ARGEXP Floating-Point Accumulator #2: Exponent ; x-ref: $1C97, $1CB5
zp_game_state_flag = $67 ; ARGHO Floating Accum. #2: Mantissa ; x-ref: $0615, $0767, $1631, $2FA2, $30C1, $3644, $37B9
zp_ptr_dig_screen_lo = $68 ; x-ref: $1EAC, $1EC4, $1ECB, $1EF2, $1F02, $1F54, $1F61, $1F82, ...
zp_ptr_dig_screen_hi = $69 ; x-ref: $1EBF, $1EC9, $1ED0, $1EE4, $1EF4, $1F47, $1F56, $1F7A, ...
zp_ptr_gold_screen_lo = $6a ; x-ref: $2532, $254A, $2561, $2568, $258B, $2591, $2597, $25AC, ...
zp_ptr_gold_screen_hi = $6b ; ARGSGN Floating Accum. #2: Sign ; x-ref: $2542, $255C, $2583, $25A7
zp_ptr_bonus_walk_lo = $6c ; ARISGN Sign Comparison Result: Accum. # 1 vs #2 ; x-ref: $2AF9, $2B26, $2B40, $2B45, $2B48, $2B55, $2BA9, $2BC0
zp_ptr_bonus_walk_hi = $6d ; FACOV Floating Accum. #1. Low-Order (Rounding) ; x-ref: $2B20, $2B58, $2BBE
zp_ptr_guard_top_lo = $6e ; FBUFPT Pointer: Cassette Buffer ; x-ref: $222D, $245E, $2490, $24B7, $2751
zp_ptr_guard_top_hi = $6f ; x-ref: $2453, $2488, $274F
zp_ptr_guard_bot_lo = $70 ; CHRGET Subroutine: Get Next Byte of BASIC Text ; x-ref: $222F, $2464, $246A, $2470, $2496, $249C, $24A2, $24C2, ...
zp_ptr_guard_bot_hi = $71 ; x-ref: $2456, $248B
zp_ptr_hole_screen_lo = $72 ; x-ref: $2BFF, $2C14, $2C1B, $2C30, $2C37, $2C5F, $2C65, $2C70, ...
zp_ptr_hole_screen_hi = $73 ; x-ref: $2C0C, $2C16, $2C2B, $2C35, $2C3C, $2C61, $2C72, $2C7F
zp_ptr_level_data = $74 ; x-ref: $06C0, $11F4, $1200, $120E, $122C, $1232, $1238, $123E, ...
zp_difficulty_scalar = $75 ; x-ref: $070C
zp_guard_draw_idx = $76 ; CHRGOT Entry to Get Same Byte of Text Again ; x-ref: $247A, $247C, $24DA
zp_ptr_ai_grid_lo = $77 ; TXTPTR Pointer: Current Byte of BASIC Text ; x-ref: $1EB0, $211F, $2123, $2127, $212B
zp_ptr_ai_grid_hi = $78 ; x-ref: $211B, $2121, $2129
zp_ptr_menu_buf_lo = $79 ; x-ref: $364C, $365D, $3675, $3686, $3733, $373B, $373F, $3754, ...
zp_ptr_menu_buf_hi = $7a ; x-ref: $3648, $3669, $3671, $368F, $3737, $3750, $3788, $37A9
zp_irq_vector_lo = $90 ; CINV Vector: Hardware Interrupt ; x-ref: $076B, $1638, $2FA6, $30E5, $3697, $37BD
zp_irq_vector_hi = $91 ; x-ref: $076F, $163C, $2FAA, $30E9, $369B, $37C1
zp_kernal_ff_sentinel = $ff ; x-ref: $1260
basic_start = $0400 ; 10 REM LODE RUNNER ON COMMODORE PET
playfield_row_0 = $6701 ; Playfield Row 0 buffer ; x-ref: $0888, $0929, $2EB2
playfield_row_0_offset_2b = $672b ; x-ref: $1A8C, $1A90, $1C0D, $1C11
playfield_row_1_page_base = $6800 ; Playfield Row 1 page base ; x-ref: $07A4, $07F7, $16AD, $1AAD, $1AB1, $33AB
playfield_row_1 = $6801 ; Playfield Row 1 buffer ; x-ref: $088E, $092F
playfield_row_1_offset_2b = $682b ; x-ref: $1AA5, $1AA9
playfield_row_2_page_base = $6900 ; Playfield Row 2 page base ; x-ref: $07A7, $07FA
playfield_row_2 = $6901 ; Playfield Row 2 buffer ; x-ref: $0894, $0935
playfield_row_2_col_34 = $6923 ; x-ref: $2954
playfield_row_3_page_base = $6a00 ; Playfield Row 3 page base ; x-ref: $07AA, $07FD, $16C8, $33C2
playfield_row_3 = $6a01 ; Playfield Row 3 buffer ; x-ref: $089A, $093B
playfield_intro_hi_score_thousands = $6a19 ; High Score thousands digit (Title Screen) ; x-ref: $3413
playfield_intro_hi_score_hundreds = $6a1a ; High Score hundreds digit (Title Screen) ; x-ref: $341E
playfield_intro_hi_score_tens = $6a1b ; High Score tens digit (Title Screen) ; x-ref: $342D
playfield_intro_hi_score_ones = $6a1c ; High Score ones digit (Title Screen) ; x-ref: $3438
hud_level_tens = $6a25 ; x-ref: $29C0
hud_level_units = $6a26 ; x-ref: $29B8, $29C5
playfield_row_4_page_base = $6b00 ; Playfield Row 4 page base ; x-ref: $07AD, $0800
playfield_row_4 = $6b01 ; Playfield Row 4 buffer ; x-ref: $08A0, $0941
playfield_row_5_page_base = $6c00 ; Playfield Row 5 page base ; x-ref: $07B0, $0803, $2E54, $2E58
playfield_row_5 = $6c01 ; Playfield Row 5 buffer ; x-ref: $08A6, $0947
playfield_row_5_offset_09_banner = $6c09 ; x-ref: $3786, $378A
playfield_row_6_page_base = $6d00 ; Playfield Row 6 page base ; x-ref: $07B3, $0806, $16E3, $33D9
playfield_row_6 = $6d01 ; Playfield Row 6 buffer ; x-ref: $08AC, $094D
playfield_row_6_col_16 = $6d10 ; x-ref: $16F3
playfield_row_6_col_34 = $6d23 ; x-ref: $2964
playfield_row_7_page_base = $6e00 ; Playfield Row 7 page base ; x-ref: $07B6, $0809, $33F0
playfield_row_7 = $6e01 ; Playfield Row 7 buffer ; x-ref: $08B2, $0953
hud_gold_count_tens = $6e24 ; HUD gold progress: count tens digit (offset $24 from Row 7 base $6E00) ; x-ref: $2A15
hud_gold_count_units = $6e25 ; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00) ; x-ref: $29D5, $2A04, $2A10
hud_gold_slash = $6e26 ; Gold HUD display separator '/' (ASCII $2F) ; x-ref: $29DA, $2A1A
hud_gold_total_tens = $6e27 ; HUD gold progress: total tens digit or units if total < 10 (offset $27 from Row 7 base $6E00) ; x-ref: $29E3, $29EB
hud_gold_total_units = $6e28 ; HUD gold progress: total units digit when total >= 10 (offset $28 from Row 7 base $6E00) ; x-ref: $29F7
playfield_row_8_page_base = $6f00 ; Playfield Row 8 page base ; x-ref: $07B9, $080C
playfield_row_8 = $6f01 ; Playfield Row 8 buffer ; x-ref: $08B8, $0959
playfield_row_9_page_base = $7000 ; Playfield Row 9 page base ; x-ref: $07BC, $080F, $2EBA
playfield_row_9 = $7001 ; Playfield Row 9 buffer ; x-ref: $08BE, $095F
playfield_row_10_page_base = $7100 ; Playfield Row 10 page base ; x-ref: $07BF, $0812, $1709, $17B4, $1823, $1894, $190C, $195F, ...
playfield_row_10 = $7101 ; Playfield Row 10 buffer ; x-ref: $08C4, $0965
playfield_row_10_col_34 = $7123 ; x-ref: $2974
playfield_row_11_page_base = $7200 ; Playfield Row 11 page base ; x-ref: $07C2, $0815, $2F65
playfield_row_11 = $7201 ; Playfield Row 11 buffer ; x-ref: $08CA, $096B
hud_timer_tens = $7225 ; x-ref: $2A38
playfield_row_11_col_37 = $7226 ; x-ref: $2A2B
playfield_row_12_page_base = $7300 ; Playfield Row 12 page base ; x-ref: $07C5, $0818, $09ED
playfield_row_12 = $7301 ; Playfield Row 12 buffer ; x-ref: $08D0, $0971
playfield_row_13_page_base = $7400 ; Playfield Row 13 page base ; x-ref: $07C8, $081B, $335D
playfield_row_13 = $7401 ; Playfield Row 13 buffer ; x-ref: $08D6, $0977
playfield_row_14_page_base = $7500 ; Playfield Row 14 page base ; x-ref: $07CB, $081E, $30F7, $3350
playfield_row_14 = $7501 ; Playfield Row 14 buffer ; x-ref: $08DC, $097D
playfield_row_14_col_8 = $7509 ; x-ref: $338B
playfield_row_14_col_31 = $7520 ; x-ref: $3390
playfield_row_14_col_34 = $7523 ; x-ref: $2984
playfield_row_15_page_base = $7600 ; Playfield Row 15 page base ; x-ref: $07CE, $0821
playfield_row_15 = $7601 ; Playfield Row 15 buffer ; x-ref: $08E2, $0983
playfield_row_16_page_base = $7700 ; Playfield Row 16 page base ; x-ref: $07D1, $0824
playfield_row_16 = $7701 ; Playfield Row 16 buffer ; x-ref: $08E8, $0989
playfield_row_16_col_34 = $7723 ; x-ref: $2994
playfield_row_17_page_base = $7800 ; Playfield Row 17 page base ; x-ref: $07D4, $0827
playfield_row_17 = $7801 ; Playfield Row 17 buffer ; x-ref: $08EE, $098F
hud_penalty_ten_thousands = $7824 ; x-ref: $2A45, $2A6D, $2A76
hud_penalty_thousands = $7825 ; x-ref: $2A50
hud_penalty_hundreds = $7826 ; x-ref: $2A5D
hud_penalty_tens = $7827 ; x-ref: $2A68
playfield_row_18_page_base = $7900 ; Playfield Row 18 page base ; x-ref: $07D7, $082A
playfield_row_18 = $7901 ; Playfield Row 18 buffer ; x-ref: $08F4, $0995
playfield_row_19_page_base = $7a00 ; Playfield Row 19 page base ; x-ref: $07DA, $082D
playfield_row_19 = $7a01 ; Playfield Row 19 buffer ; x-ref: $08FA, $099B
playfield_row_19_col_34 = $7a23 ; x-ref: $29A4
playfield_row_20_page_base = $7b00 ; Playfield Row 20 page base ; x-ref: $07DD, $0830
playfield_row_20 = $7b01 ; Playfield Row 20 buffer ; x-ref: $0900, $09A1
hud_score_ten_thousands = $7b24 ; x-ref: $2A88, $2AB0, $2AB9
hud_score_thousands = $7b25 ; x-ref: $2A93
hud_score_hundreds = $7b26 ; x-ref: $2AA0
playfield_row_20_col_39 = $7b27 ; x-ref: $2AAB
playfield_row_21_page_base = $7c00 ; Playfield Row 21 page base ; x-ref: $07E0, $0833
playfield_row_21 = $7c01 ; Playfield Row 21 buffer ; x-ref: $0906, $09A7
playfield_row_22_page_base = $7d00 ; Playfield Row 22 page base ; x-ref: $07E3, $0836, $171C
playfield_row_22 = $7d01 ; Playfield Row 22 buffer ; x-ref: $090C, $09AD
playfield_row_23_page_base = $7e00 ; Playfield Row 23 page base ; x-ref: $07E6, $0839, $172F, $2EA3, $3342
playfield_row_23 = $7e01 ; Playfield Row 23 buffer ; x-ref: $0912, $09B3
playfield_row_23_col_32 = $7e20 ; Playfield Row 23, column 32 (bottom-right corner of the border) ; x-ref: $2EB7
playfield_row_24_page_base = $7f00 ; Playfield Row 24 page base ; x-ref: $07E9, $083C, $2E9B
playfield_row_24 = $7f01 ; Playfield Row 24 buffer ; x-ref: $0918, $09B9
SCREEN_RAM_R0C0 = $8000 ; x-ref: $0784, $088B, $092C, $0B10, $0B6B, $0B9C, $0BCD, $0BFE
SCREEN_RAM_R1C0 = $8028 ; x-ref: $0891, $0932, $1658
SCREEN_RAM_R2C0 = $8050 ; x-ref: $0897, $0938
SCREEN_RAM_R3C0 = $8078 ; x-ref: $089D, $093E
SCREEN_RAM_R4C0 = $80a0 ; x-ref: $08A3, $0944
SCREEN_RAM_R5C0 = $80c8 ; x-ref: $08A9, $094A
SCREEN_RAM_R6C0 = $80f0 ; x-ref: $08AF, $0950
SCREEN_RAM_R6C16 = $8100 ; x-ref: $0787
SCREEN_RAM_R7C0 = $8118 ; x-ref: $08B5, $0956
SCREEN_RAM_R8C0 = $8140 ; x-ref: $08BB, $095C
SCREEN_RAM_R9C0 = $8168 ; x-ref: $08C1, $0962
SCREEN_RAM_R10C0 = $8190 ; x-ref: $08C7, $0968
SCREEN_RAM_R11C0 = $81b8 ; x-ref: $08CD, $096E
SCREEN_RAM_R11C39 = $81df ; x-ref: $0AB6
SCREEN_RAM_R12C0 = $81e0 ; x-ref: $08D3, $0974
SCREEN_RAM_R12C32 = $8200 ; x-ref: $078A
SCREEN_RAM_R13C0 = $8208 ; x-ref: $08D9, $097A
SCREEN_RAM_R14C0 = $8230 ; x-ref: $08DF, $0980
SCREEN_RAM_R15C0 = $8258 ; x-ref: $08E5, $0986
SCREEN_RAM_R16C0 = $8280 ; x-ref: $08EB, $098C
SCREEN_RAM_R17C0 = $82a8 ; x-ref: $08F1, $0992
SCREEN_RAM_R18C0 = $82d0 ; x-ref: $08F7, $0998
SCREEN_RAM_R19C0 = $82f8 ; x-ref: $08FD, $099E
SCREEN_RAM_R19C8 = $8300 ; x-ref: $078D
SCREEN_RAM_R20C0 = $8320 ; x-ref: $0903, $09A4
SCREEN_RAM_R21C0 = $8348 ; x-ref: $0909, $09AA
SCREEN_RAM_R22C0 = $8370 ; x-ref: $090F, $09B0
SCREEN_RAM_R23C0 = $8398 ; x-ref: $0915, $09B6
SCREEN_RAM_R24C0 = $83c0 ; x-ref: $091B, $09BC
SCREEN_RAM_R25C24 = $8400 ; x-ref: $0790
SCREEN_RAM_R32C0 = $8500 ; x-ref: $0793
SCREEN_RAM_R38C16 = $8600 ; x-ref: $0796
$0401.wordbasic_line_20; 10 REM LODE RUNNER ON COMMODORE PET
$0403.word$000a; first line number
$0405.byte$8f, $20, $4c, $4f, $44, $45, $20, $52; tokenized basic line, terminated with 00 followed by next link
$040D.byte$55, $4e, $4e, $45, $52, $20, $4f, $4e
$0415.byte$20, $43, $4f, $4d, $4d, $4f, $44, $4f
$041D.byte$52, $45, $20, $50, $45, $54, $00
$0424basic_line_20.wordbasic_line_30; 20 REM JIM.ORLANDO@GMAIL.COM ; x-ref: $0401
$0426.word$0014
$0428.byte$8f, $20, $4a, $49, $4d, $2e, $4f, $52
$0430.byte$4c, $41, $4e, $44, $4f, $40, $47, $4d
$0438.byte$41, $49, $4c, $2e, $43, $4f, $4d, $00
$0440basic_line_30.wordbasic_line_40; 30 REM TESTED ON 3032,4032,8032 ; x-ref: $0424
$0442.word$001e
$0444.byte$8f, $20, $54, $45, $53, $54, $45, $44
$044C.byte$20, $4f, $4e, $20, $33, $30, $33, $32
$0454.byte$2c, $34, $30, $33, $32, $2c, $38, $30
$045C.byte$33, $32, $00
$045Fbasic_line_40.wordbasic_line_50; 40 PRINT CHR$(142):PRINT CHR$(147) ; x-ref: $0440
$0461.word$0028
$0463.byte$99, $20, $c7, $28, $31, $34, $32, $29
$046B.byte$3a, $99, $20, $c7, $28, $31, $34, $37
$0473.byte$29, $00
$0475basic_line_50.wordbasic_line_60; 50 PRINT CHR$(42) ; x-ref: $045F
$0477.word$0032
$0479.byte$99, $20, $c7, $28, $34, $32, $29, $00
$0481basic_line_60.wordbasic_end; 60 SYS 1536 ; x-ref: $0475
$0483.word$003c
$0485.byte$9e, $20, $31, $35, $33, $36, $00
$048Cbasic_end.word$0000; x-ref: $0481
$048E.fill370, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Program entry point for PET Lode Runner.
;
; One-shot hardware init sequence followed by an infinite main loop. The main
; loop is deliberately lightweight — all heavy work (drawing, physics, input)
; is delegated to IRQ handlers installed into ZP vector $90/$91.
;
; Init sequence:
; 1. Configure VIA PCR ($E84C = $0C): CB2 low output (enables speaker)
; 2. Clear decimal mode
; 3. Init VIA Timer 2 audio engine
; 4. Seed LFSR random number generator
; 5. SEI → detect_environment (probes screen width / PET model,
; patches blit routines for 80-column if needed) → CLI
;
; Main loop — dispatches on game_state_flag each iteration:
; 0 — Idle / title not yet running: blit sidebar only
; 1 — Gameplay active: recalculate guard pathfinding + update sidebar
; in the background (gameplay_irq handles everything else via IRQ)
; 2+ — Modal dialog (game over, pause): idle spin
;
; get_random is called unconditionally every iteration to advance the LFSR
; continuously, ensuring the seed is always unpredictable when needed.
;
; Inputs: None (entry point)
; Outputs: VIA configured; interrupts enabled; infinite loop entered
; Side Effects: random_seed advanced each iteration; sidebar blitted each loop
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0600a9 0cstartlda#$0c; VIA PCR $E84C = $0C: CB2 low output — enables speaker transistor
$06028d 4c e8sta$e84c; PCR Peripheral Control Register; set to $0C or $0E at power on
$0605d8cld
$060620 7c 0cjsrinit_audio; configure VIA Timer 2 for square-wave audio
$0609a9 10lda#$10
$060B85 50stazp_random_seed; seed LFSR with $10
$060D78sei; disable IRQ during hardware probe
$060E20 25 16jsrdetect_environment; probe screen width / PET model; patches blit routines if 80-col
$061158cli; re-enable IRQ — IRQ handler now active via ZP $90/$91
$061220 72 07main_loopjsrget_random; advance LFSR every iteration — continuous entropy source ; x-ref: $061C, $062C, $062F
$0615a5 67ldazp_game_state_flag; 0=idle, 1=gameplay, 2+=modal dialog; ARGHO Floating Accum. #2: Mantissa
$0617d0 06bneb_061F
$061920 27 09jsrblit_sidebar; state 0: keep sidebar visible while title not yet started
$061C4c 12 06jmpmain_loop
$061Fc9 01b_061Fcmp#$01; x-ref: $0617
$0621d0 0cbneb_062F
$062320 e0 1cjsrupdate_guard_pathfinding; state 1: background pathfinding recalc (gameplay_irq handles all else)
$062620 ac 29jsrupdate_sidebar_status
$062920 27 09jsrblit_sidebar
$062C4c 12 06jmpmain_loop
$062F4c 12 06b_062Fjmpmain_loop; state 2+: modal dialog — idle spin until IRQ changes state ; x-ref: $0621
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Title-screen IRQ handler. Installed into ZP $90/$91 at $1636 when the title
; screen is initialized. Fires once per VIA timer tick for the duration of the
; title sequence.
;
; Each frame:
; 1. Erase player icon (restore tile beneath it in the offscreen buffer)
; 2. Advance title-screen state machine (attract mode, blinking, input)
; 3. Move player icon via joystick/keyboard
; 4. Save tile under icon and redraw it
; 5. Blit offscreen buffer → screen RAM
; 6. Acknowledge VIA interrupt ($E812) and restore registers
;
; Inputs: title_state, key state, offscreen buffer
; Outputs: Screen updated; title_state advanced
; Side Effects: Reads $E812 to clear VIA interrupt flag
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$063220 3c 11menu_irqjsrrestore_sprite_bg; erase player icon; restore tile beneath it in offscreen buffer ; x-ref: $1636, $163A
$063520 6c 16jsrtitle_screen_state_machine; advance attract mode, blink cursor, handle title input
$063820 66 10jsrprocess_player_state; move player icon based on joystick / key input
$063B20 5f 11jsrsave_bg_and_draw_sprite; save tile under icon; draw player icon into offscreen buffer
$063E20 86 08jsrblit_screen; copy offscreen buffer → screen RAM
$0641ad 12 e8lda$e812; acknowledge VIA timer interrupt (clears interrupt flag); PORT B or DDR B: Data Direction Register B
$064468pla; restore Y (pushed last by IRQ prologue)
$0645a8tay
$064668pla; restore X
$0647aatax
$064868pla; restore A
$064940rti
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main game loop — VIA hardware IRQ handler, fires once per frame.
;
; Executes all gameplay subsystems in a fixed sequence across five phases:
;
; Phase 1 — Audio & Timers
; play_sound_tick, update_game_timer, check_level_complete_bonus
;
; Phase 2 — Background Restore
; Erases previous frame's sprites by restoring saved tile backgrounds
; (player, gold, guards, holes, floating score) before any logic runs.
;
; Phase 3 — Game Logic
; Pathfinding, guard movement, keyboard scan, player movement, digging,
; hole aging, gold/guard interaction, floating score timer.
;
; Phase 4 — Draw
; Renders holes, guards, gold, and player sprite into the back-buffer,
; then blit_screen flushes the buffer to Screen RAM atomically.
;
; Phase 5 — Death Checks & State Transitions
; Checks all death conditions (guard collision, wall crush, hole fall,
; hazard), level completion, and pause key. Any triggered event will
; redirect the IRQ vector away from this handler.
;
; Epilogue: reads VIA IFR ($E812) to acknowledge the interrupt, restores
; registers from stack, and executes RTI.
;
; Installed via ZP vector $90/$91 by handle_player_death and game init.
;
; Inputs: Hardware interrupt; all global game state
; Outputs: Updated game state; Screen RAM flushed each frame
; Side Effects: Drives every game subsystem; acknowledges VIA IRQ at $E812
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$064A20 98 0cgameplay_irqjsrplay_sound_tick; --- Phase 1: Audio & Timers --- ; x-ref: $0769, $076D, $37BB, $37BF
$064D20 c2 2ajsrupdate_game_timer
$065020 59 26jsrcheck_level_complete_bonus
$065320 3c 11jsrrestore_sprite_bg; --- Phase 2: Background Restore (erase previous frame's sprites) ---
$065620 38 25jsrrestore_gold_backgrounds
$065920 47 24jsrrestore_guard_backgrounds
$065C20 02 2cjsrrestore_hole_backgrounds
$065F20 7b 25jsrerase_floating_score
$066220 09 2bjsrplay_bonus_audio_tick; --- Phase 3: Game Logic ---
$066520 27 1cjsrapply_pathfinding_to_guards
$066820 b3 23jsrprocess_guard_movement
$066B20 f7 0djsrscan_keyboard; read joystick / keyboard input
$066E20 66 10jsrprocess_player_state; move player, apply gravity
$067120 b3 1ejsrprocess_dig_animations
$067420 8e 2cjsrtick_hole_state_machine
$067720 de 25jsrcheck_gold_collection
$067A20 e2 26jsrcheck_guard_picks_up_gold
$067D20 79 27jsrcheck_hole_healing_crush
$068020 25 27jsrcheck_guard_drops_gold
$068320 9a 25jsrtick_floating_score
$068620 21 2cjsrsave_hole_bg_and_draw; --- Phase 4: Draw (render new frame into back-buffer) ---
$068920 78 24jsrdraw_guards
$068C20 52 25jsrdraw_gold
$068F20 5f 11jsrsave_bg_and_draw_sprite
$069220 86 08jsrblit_screen; flush back-buffer → Screen RAM
$069520 5f 28jsrcheck_player_guard_death; --- Phase 5: Death Checks & State Transitions ---
$069820 e8 28jsrcheck_player_wall_crush_death
$069B20 8c 28jsrcheck_player_hole_death
$069E20 b9 28jsrcheck_player_hazard_death
$06A120 c3 26jsrcheck_level_complete
$06A420 0d 29jsrcheck_pause_key
$06A7ad 12 e8lda$e812; acknowledge VIA interrupt (clear IFR at $E812); PORT B or DDR B: Data Direction Register B
$06AA68pla
$06ABa8tay
$06AC68pla
$06ADaatax
$06AE68pla
$06AF40rti; return from interrupt — next frame begins on next VIA tick
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes a completely new game.
; Flushes sound memory, resets BCD scores to zero, clears arrays via $0780.
;
; Inputs: Variable array arrays.
; Outputs: Cleared arrays, score variables reset to 0.
; Side Effects: Calls init routines, jumps to main loop at $06F6.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$06B020 92 0cinit_new_gamejsrstop_audio; x-ref: $322F
$06B320 7c 0cjsrinit_audio
$06B6a9 00lda#$00
$06B88d 45 29stascore_high_bcd
$06BB8d 46 29stascore_low_bcd
$06BEa9 00lda#$00
$06C085 74stazp_ptr_level_data
$06C220 80 07jsrclear_physical_screen
$06C54c f6 06jmpj_06F6
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Level transition handler — called after the victory jingle when the player
; completes a level. Decides whether to advance to the next level or end the
; game, resets all per-level state, then falls through into handle_player_death
; to reload and redraw the new level.
;
; Level cycling (10 levels, 1–10, wrapping):
; ui_level_num == level_select_num → full circuit complete:
; DEC ui_level_num (undo wrap), clamp to 10 if it hit 0,
; show game-over / high-score screen, and return WITHOUT loading a level.
; otherwise → INC ui_level_num, wrap 11 → 1 if needed, then reload.
;
; Per-level reset (normal advance only):
; gold_count, score_penalty zeroed; game_timer_bcd = $51 (BCD 51 seconds);
; a75 = $63 - ui_level_num (difficulty scalar: decreases as level increases).
;
; Falls through into handle_player_death at $0711, which does the full level
; teardown/reload sequence and restores the gameplay IRQ vector.
;
; Inputs: ui_level_num, level_select_num
; Outputs: ui_level_num updated; per-level vars reset; falls into handle_player_death
; Side Effects: Audio reset; may show game-over screen and return early
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$06C820 92 0cadvance_leveljsrstop_audio; audio reset before level transition ; x-ref: $26DE
$06CB20 7c 0cjsrinit_audio
$06CEad df 2aldaui_level_num
$06D1cd e0 2acmplevel_select_num; full circuit? (ui_level_num == player's chosen start level)
$06D4d0 11bneb_06E7; no — advance normally
$06D6ce df 2adecui_level_num; yes — full circuit complete: undo the wrap
$06D9ad df 2aldaui_level_num
$06DCd0 05bneb_06E3
$06DEa9 0alda#10; clamped to 0 — set level 10 instead
$06E08d df 2astaui_level_num
$06E320 49 2eb_06E3jsrshow_game_over_screen; all levels completed — show game over / high score screen ; x-ref: $06DC
$06E660rts; early exit — no level reload
$06E7ee df 2ab_06E7incui_level_num; normal advance: next level ; x-ref: $06D4
$06EAad df 2aldaui_level_num
$06EDc9 0bcmp#11; past level 10?
$06EFd0 05bnej_06F6
$06F1a9 01lda#$01; yes — wrap back to level 1
$06F38d df 2astaui_level_num
$06F6a9 00j_06F6lda#$00; --- reset per-level state --- ; x-ref: $06C5, $06EF
$06F88d df 24stagold_count
$06FB8d 47 29stascore_penalty_high_bcd
$06FE8d 48 29stascore_penalty_low_bcd
$0701a9 51lda#$51
$07038d 49 29stagame_timer_bcd; reset countdown timer to BCD 51 seconds
$0706a9 63lda#$63
$070838sec
$0709ed df 2asbcui_level_num
$070C85 75stazp_difficulty_scalar; $63 - level_num: difficulty scalar (smaller value = higher level = harder)
$070E20 21 25jsrinit_gold_arrays; fall through into handle_player_death — reloads and redraws the level
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles player death: applies pending BCD score penalty, resets gold count,
; reloads and redraws the current level, then resumes gameplay via the IRQ vector.
;
; Called from all death triggers (guard collision, tile death, hole fall) and
; from menu_dispatch_selection when the player chooses to continue.
;
; Inputs: a2947:a2948 — 2-byte BCD penalty to subtract from score
; Outputs: score_high_bcd:score_low_bcd updated; a2947:a2948 and gold_count zeroed;
; game_state_flag = 1; IRQ vector (a90:a91) set to tick_gameplay
; Side Effects: Stops and reinitializes audio; fully reinitializes all level
; data structures and redraws the playfield UI
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$071120 92 0chandle_player_deathjsrstop_audio; --- Phase 1: audio reset --- ; x-ref: $2884, $28B1, $28E0, $2909, $37CB
$071420 7c 0cjsrinit_audio
$0717f8sed; --- Phase 2: BCD score penalty --- enable BCD arithmetic
$0718ad 46 29ldascore_low_bcd
$071B38sec
$071Ced 48 29sbcscore_penalty_low_bcd; score_low -= penalty_low (BCD)
$071F8d 46 29stascore_low_bcd
$0722ad 45 29ldascore_high_bcd
$0725ed 47 29sbcscore_penalty_high_bcd; score_high -= penalty_high with borrow (BCD)
$07288d 45 29stascore_high_bcd
$072Bd8cld; back to binary mode
$072Ca9 00lda#$00
$072E8d 47 29stascore_penalty_high_bcd; --- Phase 3: clear transient state --- zero out penalty
$07318d 48 29stascore_penalty_low_bcd
$0734a9 00lda#$00
$07368d df 24stagold_count; gold back to 0 (level restart)
$073920 9d 07jsrclear_gameplay_grid; --- Phase 4: full level reinit ---
$073C20 9d 1ejsrinit_dig_slots
$073F20 ba 0djsrinit_game_variables
$074220 21 25jsrinit_gold_arrays
$074520 2b 22jsrinit_guard_arrays
$074820 ee 2bjsrinit_hole_slots
$074B20 75 25jsrinit_floating_score
$074E20 e9 2ajsrinit_bonus_sequencer
$075120 e8 11jsrunpack_level_data; reload compressed tile data into back-buffer
$075420 b8 1cjsrinit_ai_pathfinding_grid
$075720 e0 1cjsrupdate_guard_pathfinding
$075A20 4b 29jsrdraw_ui_text_labels
$075D20 b8 0ajsrtransition_from_empty_to_full; animated wipe-in reveals freshly drawn level
$0760a9 fflda#$ff; --- Phase 5: resume gameplay ---
$07628d b1 0dstaplayer_state; mark player alive/ready;
$0765a9 01lda#$01
$076785 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$0769a9 4alda#<gameplay_irq
$076B85 90stazp_irq_vector_lo; redirect IRQ lo → gameplay_irq; CINV Vector: Hardware Interrupt
$076Da9 06lda#>gameplay_irq
$076F85 91stazp_irq_vector_hi; redirect IRQ hi → next tick resumes gameplay
$077160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Pseudo-random number generator using an 8-bit LFSR with polynomial $1D.
; Updates random_seed.
;
; Inputs: random_seed ($50)
; Outputs: Updated random_seed
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0772a5 50get_randomldazp_random_seed; x-ref: $0612, $2735, $32D3, $3321
$0774f0 05beqb_077B
$07760aasla
$0777f0 04beqb_077D
$077990 02bccb_077D
$077B49 1db_077Beor#$1d; x-ref: $0774
$077D85 50b_077Dstazp_random_seed; x-ref: $0777, $0779
$077F60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears the physical PET screen RAM by filling $8000–$86FF (7 pages x 256
; bytes) with space characters ($20).
;
; Uses a single 256-iteration DEX/BNE loop writing A to seven consecutive
; page-aligned screen RAM regions in parallel, covering the full visible
; display.
;
; Inputs: None
; Outputs: None
; Side Effects: Screen RAM at $8000–$86FF filled with spaces ($20)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clear_physical_screen
$0780a9 20lda#$20; x-ref: $06C2, $1681
$0782a2 00ldx#$00
$07849d 00 80b_0784staSCREEN_RAM_R0C0,x; x-ref: $079A
$07879d 00 81staSCREEN_RAM_R6C16,x
$078A9d 00 82staSCREEN_RAM_R12C32,x
$078D9d 00 83staSCREEN_RAM_R19C8,x
$07909d 00 84staSCREEN_RAM_R25C24,x
$07939d 00 85staSCREEN_RAM_R32C0,x
$07969d 00 86staSCREEN_RAM_R38C16,x
$0799cadex
$079Ad0 e8bneb_0784
$079C60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears the full playfield back-buffer to space characters.
;
; Writes $20 (space) to column positions 0–42 (43 bytes) across all 26 back-
; buffer row pages ($6700–$7F00). The loop counts X down from 42 to 0
; inclusive (BPL exits after X wraps below 0), erasing the entire tile grid
; including 3 guard bytes beyond the 40-column display width.
;
; Compare with clear_sub_grid ($07F0), which only clears columns 0–32 (33
; bytes) and is used exclusively by the game-over screen renderer.
;
; Callers:
; $0739 — level init sequence, before unpacking level tile data
; $1684 — title screen state machine (title_state == 0)
; $30E0, $30F1 — level restart / demo mode paths
;
; Inputs: None
; Outputs: $6700–$7F00 columns 0–42 filled with $20 (space)
; Side Effects: Clobbers A and X
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$079Da9 20clear_gameplay_gridlda#$20; space char — fills every tile position ; x-ref: $0739, $1684, $30E0, $30F1
$079Fa2 2aldx#42; X = 42..0 inclusive (43 column positions, covers full 40-col display + 3 guard bytes)
$07A19d 00 67b_07A1staplayfield_row_buffer,x; row 0 ($6700) ; x-ref: $07ED
$07A49d 00 68staplayfield_row_1_page_base,x; Playfield Row 1 page base
$07A79d 00 69staplayfield_row_2_page_base,x; Playfield Row 2 page base
$07AA9d 00 6astaplayfield_row_3_page_base,x; Playfield Row 3 page base
$07AD9d 00 6bstaplayfield_row_4_page_base,x; Playfield Row 4 page base
$07B09d 00 6cstaplayfield_row_5_page_base,x; Playfield Row 5 page base
$07B39d 00 6dstaplayfield_row_6_page_base,x; Playfield Row 6 page base
$07B69d 00 6estaplayfield_row_7_page_base,x; Playfield Row 7 page base
$07B99d 00 6fstaplayfield_row_8_page_base,x; Playfield Row 8 page base
$07BC9d 00 70staplayfield_row_9_page_base,x; Playfield Row 9 page base
$07BF9d 00 71staplayfield_row_10_page_base,x; Playfield Row 10 page base
$07C29d 00 72staplayfield_row_11_page_base,x; Playfield Row 11 page base
$07C59d 00 73staplayfield_row_12_page_base,x; Playfield Row 12 page base
$07C89d 00 74staplayfield_row_13_page_base,x; Playfield Row 13 page base
$07CB9d 00 75staplayfield_row_14_page_base,x; Playfield Row 14 page base
$07CE9d 00 76staplayfield_row_15_page_base,x; Playfield Row 15 page base
$07D19d 00 77staplayfield_row_16_page_base,x; Playfield Row 16 page base
$07D49d 00 78staplayfield_row_17_page_base,x; Playfield Row 17 page base
$07D79d 00 79staplayfield_row_18_page_base,x; Playfield Row 18 page base
$07DA9d 00 7astaplayfield_row_19_page_base,x; Playfield Row 19 page base
$07DD9d 00 7bstaplayfield_row_20_page_base,x; Playfield Row 20 page base
$07E09d 00 7cstaplayfield_row_21_page_base,x; Playfield Row 21 page base
$07E39d 00 7dstaplayfield_row_22_page_base,x; Playfield Row 22 page base
$07E69d 00 7estaplayfield_row_23_page_base,x; Playfield Row 23 page base
$07E99d 00 7fstaplayfield_row_24_page_base,x; Playfield Row 24 page base
$07ECcadex; next column position
$07ED10 b2bplb_07A1; repeat until X wraps below 0 (covers cols 42..0)
$07EF60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears the full playfield back-buffer ready for a new screen render.
;
; Writes $20 (space) to column positions 0–32 (33 bytes) across all 26 back-
; buffer row pages ($6700–$7F00). The loop counts X down from 32 to 0
; inclusive (BPL terminates after X wraps past 0), so each row page receives
; 33 zeroed columns covering the full 32-column play area plus one guard byte.
;
; Only caller: show_game_over_screen ($2E49), which clears the buffer as its
; first step before rendering the GAME OVER text layout into the pages.
; Sibling routine clear_gameplay_grid ($079D) performs a similar wipe during
; level initialisation.
;
; Inputs: None
; Outputs: $6700–$7F00 columns 0–32 filled with $20 (space)
; Side Effects: Clobbers A and X
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$07F0a9 20clear_sub_gridlda#$20; space char — fills every tile position ; x-ref: $2E51
$07F2a2 20ldx#32; X = 32..0 inclusive (33 column positions)
$07F49d 00 67b_07F4staplayfield_row_buffer,x; row 0 ($6700) ; x-ref: $0840
$07F79d 00 68staplayfield_row_1_page_base,x; Playfield Row 1 page base
$07FA9d 00 69staplayfield_row_2_page_base,x; Playfield Row 2 page base
$07FD9d 00 6astaplayfield_row_3_page_base,x; Playfield Row 3 page base
$08009d 00 6bstaplayfield_row_4_page_base,x; Playfield Row 4 page base
$08039d 00 6cstaplayfield_row_5_page_base,x; Playfield Row 5 page base
$08069d 00 6dstaplayfield_row_6_page_base,x; Playfield Row 6 page base
$08099d 00 6estaplayfield_row_7_page_base,x; Playfield Row 7 page base
$080C9d 00 6fstaplayfield_row_8_page_base,x; Playfield Row 8 page base
$080F9d 00 70staplayfield_row_9_page_base,x; Playfield Row 9 page base
$08129d 00 71staplayfield_row_10_page_base,x; Playfield Row 10 page base
$08159d 00 72staplayfield_row_11_page_base,x; Playfield Row 11 page base
$08189d 00 73staplayfield_row_12_page_base,x; Playfield Row 12 page base
$081B9d 00 74staplayfield_row_13_page_base,x; Playfield Row 13 page base
$081E9d 00 75staplayfield_row_14_page_base,x; Playfield Row 14 page base
$08219d 00 76staplayfield_row_15_page_base,x; Playfield Row 15 page base
$08249d 00 77staplayfield_row_16_page_base,x; Playfield Row 16 page base
$08279d 00 78staplayfield_row_17_page_base,x; Playfield Row 17 page base
$082A9d 00 79staplayfield_row_18_page_base,x; Playfield Row 18 page base
$082D9d 00 7astaplayfield_row_19_page_base,x; Playfield Row 19 page base
$08309d 00 7bstaplayfield_row_20_page_base,x; Playfield Row 20 page base
$08339d 00 7cstaplayfield_row_21_page_base,x; Playfield Row 21 page base
$08369d 00 7dstaplayfield_row_22_page_base,x; Playfield Row 22 page base
$08399d 00 7estaplayfield_row_23_page_base,x; Playfield Row 23 page base
$083C9d 00 7fstaplayfield_row_24_page_base,x; Playfield Row 24 page base
$083Fcadex; next column position
$084010 b2bplb_07F4; repeat until X wraps below 0 (covers cols 32..0)
$084260rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Patcher for 80-column screen compatibility on Commodore PET business models.
; If screen_width ($51) is 80, loops through memory operands of main drawing loops
; and patches them to use 80-byte offsets instead of 40-byte offsets.
;
; Inputs: screen_width variable ($51).
; Outputs: Patches drawing vectors at $0886 and $0927.
; Side Effects: Self-modifying code.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
patch_80_column_compatibility
$0843a5 51ldazp_screen_width; x-ref: $1668
$0845c9 50cmp#80
$0847f0 01beqb_084A
$084960rts
$084Aa0 0cb_084Aldy#$0c; x-ref: $0847
$084Ca9 00lda#$00
$084E8d 8c 08stasmc_blit_screen_r0_lo
$08518d 2d 09stasmc_blit_sidebar_r0_lo
$0854a2 80ldx#$80
$08568e 8d 08stxsmc_blit_screen_r0_hi
$08598e 2e 09stxsmc_blit_sidebar_r0_hi
$085C18b_085Cclc; x-ref: $0879
$085D69 50adc#$50
$085F99 86 08stablit_screen,y
$086299 27 09stablit_sidebar,y
$086590 01bccb_0868
$0867e8inx
$0868c8b_0868iny; x-ref: $0865
$086948pha
$086A8atxa
$086B99 86 08stablit_screen,y
$086E99 27 09stablit_sidebar,y
$087168pla
$0872c8iny
$0873c8iny
$0874c8iny
$0875c8iny
$0876c8iny
$0877c0 9ccpy#$9c
$0879d0 e1bneb_085C
$087Ba9 83lda#$83
$087D8d b6 0astasmc_screen_addr_hi
$0880a9 bflda#$bf
$08828d b7 0astasmc_screen_addr_lo
$088560rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Flushes the play-area back-buffer to PET Screen RAM.
;
; Copies 32 columns (0–31) across all 25 display rows. The back-buffer rows
; are stored at $6701, $6801 … $7F01 (one 256-byte page per row, offset +1
; from the page base). Screen RAM rows are at $8000, $8028 … $83C0 (40 bytes
; apart, matching the PET's 40-column display).
;
; The loop iterates X = 0 to $1F: each pass copies one column position across
; all 25 rows in a single unrolled sequence of LDA/STA pairs, then advances X.
; Companion routine blit_sidebar ($0927) handles columns 32–39 (score/lives).
;
; Inputs: Back-buffer rows $6701–$7F01 (25 × 40 bytes, play area in cols 0–31)
; Outputs: Screen RAM $8000–$83E7 (columns 0–31 of all 25 rows)
; Side Effects: Clobbers X; called from IRQ handler and main game loop
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0886a2 00blit_screenldx#$00; X = column index (0–$1F), copied across all 25 rows each pass ; x-ref: $063E, $0692, $085F, $086B, $0AA4, $3002, $3030, $31F8, ...
$0888bd 01 67j_0888ldaplayfield_row_0,x; back-buffer row 0 ($6701 + X) ; x-ref: $0923 Playfield Row 0 buffer
smc_blit_screen_r0_lo =*+$01 ; x-ref: $084E
smc_blit_screen_r0_hi =*+$02 ; x-ref: $0856
$088B9d 00 80staSCREEN_RAM_R0C0,x; Screen RAM row 0 ($8000 + X)
$088Ebd 01 68ldaplayfield_row_1,x; Playfield Row 1 buffer
$08919d 28 80staSCREEN_RAM_R1C0,x
$0894bd 01 69ldaplayfield_row_2,x; Playfield Row 2 buffer
$08979d 50 80staSCREEN_RAM_R2C0,x
$089Abd 01 6aldaplayfield_row_3,x; Playfield Row 3 buffer
$089D9d 78 80staSCREEN_RAM_R3C0,x
$08A0bd 01 6bldaplayfield_row_4,x; Playfield Row 4 buffer
$08A39d a0 80staSCREEN_RAM_R4C0,x
$08A6bd 01 6cldaplayfield_row_5,x; Playfield Row 5 buffer
$08A99d c8 80staSCREEN_RAM_R5C0,x
$08ACbd 01 6dldaplayfield_row_6,x; Playfield Row 6 buffer
$08AF9d f0 80staSCREEN_RAM_R6C0,x
$08B2bd 01 6eldaplayfield_row_7,x; Playfield Row 7 buffer
$08B59d 18 81staSCREEN_RAM_R7C0,x
$08B8bd 01 6fldaplayfield_row_8,x; Playfield Row 8 buffer
$08BB9d 40 81staSCREEN_RAM_R8C0,x
$08BEbd 01 70ldaplayfield_row_9,x; Playfield Row 9 buffer
$08C19d 68 81staSCREEN_RAM_R9C0,x
$08C4bd 01 71ldaplayfield_row_10,x; Playfield Row 10 buffer
$08C79d 90 81staSCREEN_RAM_R10C0,x
$08CAbd 01 72ldaplayfield_row_11,x; Playfield Row 11 buffer
$08CD9d b8 81staSCREEN_RAM_R11C0,x
$08D0bd 01 73ldaplayfield_row_12,x; Playfield Row 12 buffer
$08D39d e0 81staSCREEN_RAM_R12C0,x
$08D6bd 01 74ldaplayfield_row_13,x; Playfield Row 13 buffer
$08D99d 08 82staSCREEN_RAM_R13C0,x
$08DCbd 01 75ldaplayfield_row_14,x; Playfield Row 14 buffer
$08DF9d 30 82staSCREEN_RAM_R14C0,x
$08E2bd 01 76ldaplayfield_row_15,x; Playfield Row 15 buffer
$08E59d 58 82staSCREEN_RAM_R15C0,x
$08E8bd 01 77ldaplayfield_row_16,x; Playfield Row 16 buffer
$08EB9d 80 82staSCREEN_RAM_R16C0,x
$08EEbd 01 78ldaplayfield_row_17,x; Playfield Row 17 buffer
$08F19d a8 82staSCREEN_RAM_R17C0,x
$08F4bd 01 79ldaplayfield_row_18,x; Playfield Row 18 buffer
$08F79d d0 82staSCREEN_RAM_R18C0,x
$08FAbd 01 7aldaplayfield_row_19,x; Playfield Row 19 buffer
$08FD9d f8 82staSCREEN_RAM_R19C0,x
$0900bd 01 7bldaplayfield_row_20,x; Playfield Row 20 buffer
$09039d 20 83staSCREEN_RAM_R20C0,x
$0906bd 01 7cldaplayfield_row_21,x; Playfield Row 21 buffer
$09099d 48 83staSCREEN_RAM_R21C0,x
$090Cbd 01 7dldaplayfield_row_22,x; Playfield Row 22 buffer
$090F9d 70 83staSCREEN_RAM_R22C0,x
$0912bd 01 7eldaplayfield_row_23,x; Playfield Row 23 buffer
$09159d 98 83staSCREEN_RAM_R23C0,x
$0918bd 01 7fldaplayfield_row_24,x; Playfield Row 24 buffer
$091B9d c0 83staSCREEN_RAM_R24C0,x
$091Ee8inx; advance to next column
$091Fe0 20cpx#32; all 32 play-area columns done?
$0921f0 03beqr_0926; yes — return
$09234c 88 08jmpj_0888; no — next column
$092660r_0926rts; x-ref: $0921
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Copies columns 32-39 from RAM buffers ($6701..$7F01) to Screen RAM ($8000..).
; Complements blit_screen by updating the sidebar. Called from main loop.
;
; Inputs: Data in buffers $6701..$7F01
; Outputs: Modifies Screen RAM $8000..$83FF (columns 32-39)
; Side Effects: Modifies X register.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0927a2 20blit_sidebarldx#$20; x-ref: $0619, $0629, $0862, $086E, $26D0, $31FB, $320C, $3241
$0929bd 01 67j_0929ldaplayfield_row_0,x; x-ref: $09C4 Playfield Row 0 buffer
smc_blit_sidebar_r0_lo =*+$01 ; x-ref: $0851
smc_blit_sidebar_r0_hi =*+$02 ; x-ref: $0859
$092C9d 00 80staSCREEN_RAM_R0C0,x
$092Fbd 01 68ldaplayfield_row_1,x; Playfield Row 1 buffer
$09329d 28 80staSCREEN_RAM_R1C0,x
$0935bd 01 69ldaplayfield_row_2,x; Playfield Row 2 buffer
$09389d 50 80staSCREEN_RAM_R2C0,x
$093Bbd 01 6aldaplayfield_row_3,x; Playfield Row 3 buffer
$093E9d 78 80staSCREEN_RAM_R3C0,x
$0941bd 01 6bldaplayfield_row_4,x; Playfield Row 4 buffer
$09449d a0 80staSCREEN_RAM_R4C0,x
$0947bd 01 6cldaplayfield_row_5,x; Playfield Row 5 buffer
$094A9d c8 80staSCREEN_RAM_R5C0,x
$094Dbd 01 6dldaplayfield_row_6,x; Playfield Row 6 buffer
$09509d f0 80staSCREEN_RAM_R6C0,x
$0953bd 01 6eldaplayfield_row_7,x; Playfield Row 7 buffer
$09569d 18 81staSCREEN_RAM_R7C0,x
$0959bd 01 6fldaplayfield_row_8,x; Playfield Row 8 buffer
$095C9d 40 81staSCREEN_RAM_R8C0,x
$095Fbd 01 70ldaplayfield_row_9,x; Playfield Row 9 buffer
$09629d 68 81staSCREEN_RAM_R9C0,x
$0965bd 01 71ldaplayfield_row_10,x; Playfield Row 10 buffer
$09689d 90 81staSCREEN_RAM_R10C0,x
$096Bbd 01 72ldaplayfield_row_11,x; Playfield Row 11 buffer
$096E9d b8 81staSCREEN_RAM_R11C0,x
$0971bd 01 73ldaplayfield_row_12,x; Playfield Row 12 buffer
$09749d e0 81staSCREEN_RAM_R12C0,x
$0977bd 01 74ldaplayfield_row_13,x; Playfield Row 13 buffer
$097A9d 08 82staSCREEN_RAM_R13C0,x
$097Dbd 01 75ldaplayfield_row_14,x; Playfield Row 14 buffer
$09809d 30 82staSCREEN_RAM_R14C0,x
$0983bd 01 76ldaplayfield_row_15,x; Playfield Row 15 buffer
$09869d 58 82staSCREEN_RAM_R15C0,x
$0989bd 01 77ldaplayfield_row_16,x; Playfield Row 16 buffer
$098C9d 80 82staSCREEN_RAM_R16C0,x
$098Fbd 01 78ldaplayfield_row_17,x; Playfield Row 17 buffer
$09929d a8 82staSCREEN_RAM_R17C0,x
$0995bd 01 79ldaplayfield_row_18,x; Playfield Row 18 buffer
$09989d d0 82staSCREEN_RAM_R18C0,x
$099Bbd 01 7aldaplayfield_row_19,x; Playfield Row 19 buffer
$099E9d f8 82staSCREEN_RAM_R19C0,x
$09A1bd 01 7bldaplayfield_row_20,x; Playfield Row 20 buffer
$09A49d 20 83staSCREEN_RAM_R20C0,x
$09A7bd 01 7cldaplayfield_row_21,x; Playfield Row 21 buffer
$09AA9d 48 83staSCREEN_RAM_R21C0,x
$09ADbd 01 7dldaplayfield_row_22,x; Playfield Row 22 buffer
$09B09d 70 83staSCREEN_RAM_R22C0,x
$09B3bd 01 7eldaplayfield_row_23,x; Playfield Row 23 buffer
$09B69d 98 83staSCREEN_RAM_R23C0,x
$09B9bd 01 7fldaplayfield_row_24,x; Playfield Row 24 buffer
$09BC9d c0 83staSCREEN_RAM_R24C0,x
$09BFe8inx
$09C0e0 28cpx#$28
$09C2f0 03beqr_09C7
$09C44c 29 09jmpj_0929
$09C760r_09C7rts; x-ref: $09C2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Busy-wait delay loop. Runs 16 × 255 = 4,080 inner iterations (~20 ms at
; 1 MHz). Preserves A, X, and Y across the call.
;
; Used wherever a fixed timing delay is needed: animation frame pacing,
; menu blink timing, victory sequence waits, etc.
;
; Inputs: None
; Outputs: None (pure delay)
; Side Effects: Burns ~20,480 CPU cycles
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$09C848busy_wait_pausepha; save A ; x-ref: $0C05, $0D03, $0D15, $26D5, $287B, $28A8, $28D7, $2900, ...
$09C98atxa; save X
$09CA48pha
$09CB98tya; save Y
$09CC48pha
$09CDa0 10ldy#$10; outer loop: 16 passes
$09CFa2 ffb_09CFldx#$ff; inner loop: 255 iterations each pass ; x-ref: $09D5
$09D1cab_09D1dex; burn cycles ; x-ref: $09D2
$09D2d0 fdbneb_09D1
$09D488dey; outer step; loop until Y=0
$09D5d0 f8bneb_09CF
$09D768pla; restore Y
$09D8a8tay
$09D968pla; restore X
$09DAaatax
$09DB68pla; restore A
$09DC60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Wipe transition: erases the playing field from the outside inward, leaving an
; empty screen. Called before level exits, deaths, and menu entries to blank the
; playing area.
;
; Each path step reads a (row_offset, col_offset) pair from the wipe path tables
; and positions four box corners symmetrically around the centre cell (page $73,
; col $11). Page $73 is the middle row of the playfield; the valid row range is
; $67 (top row) to $7F (bottom row), spaced 2 pages apart.
;
; bottom-right: page = $73 + row_off, col = $11 + col_off
; top-right: page = $73 - row_off, col = $11 + col_off
; bottom-left: page = $73 + row_off, col = $11 - col_off
; top-left: page = $73 - row_off, col = $11 - col_off
;
; At each corner, $20 (space) is written to erase the level tile there, EXCEPT
; when the corner lands exactly on a playfield boundary edge:
; page = $7F (bottom row) or page = $67 (top row) or col = $01 (left col)
; → the appropriate PETSCII border char is drawn instead ($4F/$4C/$63/$64/$65)
; to show the current sweep ring boundary.
;
; X starts at $FC (outermost ring, offsets large = full-screen box) and counts
; down toward 0 (innermost, offsets small = centre). The path tables cover every
; column of each concentric ring so the full playfield is progressively erased.
; $FF sentinels divide the path into display frames; at each sentinel the buffer
; is blitted to screen RAM and a frame-delay is inserted. A second $FF ends the
; animation.
;
; Inputs: wipe_path_row_table ($6300), wipe_path_col_table ($6400);
; offscreen buffer $6700-$7FFF (level tiles)
; Outputs: Offscreen buffer and screen RAM progressively erased outward→inward
; Side Effects: Calls blit_screen_and_pause each frame; modifies a58, a59
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
transition_from_full_to_empty
$09DDa9 00lda#$00; ZP pointer low byte = 0 (constant; a59 carries the row page) ; x-ref: $26DB, $2881, $28AE, $28DD, $2906, $37C8, $37D3
$09DF85 58stazp_ptr_wipe_buf_lo
$09E1a2 fcldx#$fc; start at outermost ring ($FC); contract inward via dex
$09E3bd 00 63j_09E3ldawipe_path_row_table,x; read row offset for this wipe step ; x-ref: $0A8E, $0A9C
$09E6c9 ffcmp#$ff; end-of-frame sentinel?
$09E8d0 03bneb_09ED; no: erase/mark this ring step
$09EA4c 91 0ajmpj_0A91; yes: blit frame to screen and delay
$09EDa9 73b_09EDlda#>playfield_row_12_page_base; --- bottom-right: page=$73+row_off (toward bottom row $7F), col=$11+col_off --- ; x-ref: $09E8
$09EF18clc
$09F07d 00 63adcwipe_path_row_table,x; bottom-half row page = $73 + row_offset
$09F385 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$09F5a9 11lda#$11
$09F718clc
$09F87d 00 64adcwipe_path_col_table,x; right col = $11 + col_offset
$09FBa8tay
$09FCa5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$09FEc9 7fcmp#$7f; reached bottom playfield edge ($7F)?
$0A00d0 05bneb_0A07; no: erase level tile
$0A02a9 64lda#$64; yes: bottom-right sweep boundary — bottom-edge char ($64)
$0A044c 09 0ajmpj_0A09
$0A07a9 20b_0A07lda#$20; erase level tile at this position ; x-ref: $0A00
$0A0991 58j_0A09sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A04
$0A0Ba9 73lda#$73; --- top-right: page=$73-row_off (toward top row $67), col=$11+col_off ---
$0A0D38sec
$0A0Efd 00 63sbcwipe_path_row_table,x; top-half row page = $73 - row_offset
$0A1185 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A13a9 11lda#$11
$0A1518clc
$0A167d 00 64adcwipe_path_col_table,x; right col = $11 + col_offset
$0A19a8tay
$0A1Aa5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A1Cc9 67cmp#$67; reached top playfield edge ($67)?
$0A1Ed0 05bneb_0A25; no: erase level tile
$0A20a9 63lda#$63; yes: top-right sweep boundary — top-edge char ($63)
$0A224c 27 0ajmpj_0A27
$0A25a9 20b_0A25lda#$20; erase level tile at this position ; x-ref: $0A1E
$0A2791 58j_0A27sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A22
$0A29a9 73lda#$73; --- bottom-left: page=$73+row_off (toward bottom row $7F), col=$11-col_off ---
$0A2B18clc
$0A2C7d 00 63adcwipe_path_row_table,x; bottom-half row page = $73 + row_offset
$0A2F85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A31a9 11lda#$11
$0A3338sec
$0A34fd 00 64sbcwipe_path_col_table,x; left col = $11 - col_offset
$0A37a8tay
$0A38c0 01cpy#$01; reached left playfield edge (col 1)?
$0A3Ad0 10bneb_0A4C; no: check row boundary only
$0A3Ca5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A3Ec9 7fcmp#$7f; col=1: also at bottom edge ($7F)?
$0A40d0 05bneb_0A47
$0A42a9 4clda#$4c; col=1 + row=$7F: bottom-left corner char ($4C)
$0A444c 59 0ajmpj_0A59
$0A47a9 65b_0A47lda#$65; col=1 only: left-edge char ($65) ; x-ref: $0A40
$0A494c 59 0ajmpj_0A59
$0A4Ca5 59b_0A4Cldazp_ptr_wipe_buf_hi; col≠1: check bottom edge ; x-ref: $0A3A TEMPF2 Temporary storage for FLPT value.
$0A4Ec9 7fcmp#$7f
$0A50d0 05bneb_0A57
$0A52a9 64lda#$64; col≠1 + row=$7F: bottom-edge char ($64)
$0A544c 59 0ajmpj_0A59
$0A57a9 20b_0A57lda#$20; inside playfield: erase level tile ; x-ref: $0A50
$0A5991 58j_0A59sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A44, $0A49, $0A54
$0A5Ba9 73lda#$73; --- top-left: page=$73-row_off (toward top row $67), col=$11-col_off ---
$0A5D38sec
$0A5Efd 00 63sbcwipe_path_row_table,x; top-half row page = $73 - row_offset
$0A6185 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A63a9 11lda#$11
$0A6538sec
$0A66fd 00 64sbcwipe_path_col_table,x; left col = $11 - col_offset
$0A69a8tay
$0A6Ac0 01cpy#$01; reached left playfield edge (col 1)?
$0A6Cd0 10bneb_0A7E; no: check row boundary only
$0A6Ea5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0A70c9 67cmp#$67; col=1: also at top edge ($67)?
$0A72d0 05bneb_0A79
$0A74a9 4flda#$4f; col=1 + row=$67: top-left corner char ($4F)
$0A764c 8b 0ajmpj_0A8B
$0A79a9 65b_0A79lda#$65; col=1 only: left-edge char ($65) ; x-ref: $0A72
$0A7B4c 8b 0ajmpj_0A8B
$0A7Ea5 59b_0A7Eldazp_ptr_wipe_buf_hi; col≠1: check top edge ; x-ref: $0A6C TEMPF2 Temporary storage for FLPT value.
$0A80c9 67cmp#$67
$0A82d0 05bneb_0A89
$0A84a9 63lda#$63; col≠1 + row=$67: top-edge char ($63)
$0A864c 8b 0ajmpj_0A8B
$0A89a9 20b_0A89lda#$20; inside playfield: erase level tile ; x-ref: $0A82
$0A8B91 58j_0A8Bsta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A76, $0A7B, $0A86
$0A8Dcadex; contract: next (inner) wipe step
$0A8E4c e3 09jmpj_09E3
$0A9120 a0 0aj_0A91jsrblit_screen_and_pause; $FF hit: blit frame to screen and delay one frame ; x-ref: $09EA
$0A94cadex; skip past the $FF sentinel
$0A95bd 00 63ldawipe_path_row_table,x; read next entry
$0A98c9 ffcmp#$ff; second $FF = all rings erased, animation complete
$0A9Af0 03beqr_0A9F; yes: done
$0A9C4c e3 09jmpj_09E3; no: continue next frame
$0A9F60r_0A9Frts; x-ref: $0A9A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Blits the off-screen row buffers to physical screen RAM, then busy-waits
; for a fixed delay to control animation frame timing.
;
; Preserves X and Y around the blit call. After blit_screen returns, runs a
; nested busy-wait loop (outer Y=$10, inner X=$FF → 16x255 = 4,080 iterations)
; to produce a ~17 ms delay before returning to the caller.
;
; Called by animate_level_select_cursor each time a $FF sentinel is hit in the
; path tables, i.e. once per animation frame.
;
; Inputs: None
; Outputs: None
; Side Effects: Calls blit_screen (updates screen RAM); burns ~4080 cycles
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
blit_screen_and_pause
$0AA08atxa; x-ref: $0A91
$0AA148pha
$0AA298tya
$0AA348pha
$0AA420 86 08jsrblit_screen
$0AA7a0 10ldy#$10
$0AA9a2 ffb_0AA9ldx#$ff; x-ref: $0AAF
$0AABcab_0AABdex; x-ref: $0AAC
$0AACd0 fdbneb_0AAB
$0AAE88dey
$0AAFd0 f8bneb_0AA9
$0AB168pla
$0AB2a8tay
$0AB368pla
$0AB4aatax
$0AB560rts
; Self-modifying screen RAM target address (high byte).
; Paired with smc_screen_addr_lo (low byte) to form a 16-bit address patched
; into the four absolute STA instructions in transition_from_empty_to_full
; Phase 2, directing tile-copy writes to the correct screen RAM row.
; Initial value $81 (hi) + $DF (lo) = $81DF.
; Reset to $83 (hi) + $BF (lo) = $83BF after blit table setup.
$0AB6smc_screen_addr_hi.byte>SCREEN_RAM_R11C39; x-ref: $087D, $0B45, $0B76, $0BA7, $0BD8
$0AB7smc_screen_addr_lo.byte<SCREEN_RAM_R11C39; x-ref: $0882, $0B4B, $0B7C, $0BAD, $0BDE
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Wipe transition: reveals the playing field from the centre outward, filling an
; empty screen with level content. Inverse of transition_from_full_to_empty.
;
; Phase 1 — Static border frame ($0AB8–$0B2B):
; Draws the outer border box directly to screen RAM ($8000), 25 rows × 33 cols
; (X: 0..$18 rows, Y: 0..$20 cols). Uses a 16-bit pointer (a0B11:a0B12, base
; $8000) advanced by screen_width each row with carry propagation:
; top-left ($4F), top-right ($65), bottom-left ($4C), bottom-right ($65),
; top-edge ($63), bottom-edge ($64), left/right edges ($65), interior ($20).
; This establishes the empty-field starting state visible between transitions.
;
; Phase 2 — Expanding reveal animation ($0B2D–$0C13):
; Walks wipe_path_row_table ($6300) and wipe_path_col_table ($6400) forward
; from index 2 (innermost ring) outward via inx. For each step, the four
; symmetric corner positions are computed using the same page=$73±off /
; col=$11±off formula as transition_from_full_to_empty, but the tile is copied
; FROM the offscreen buffer (level already loaded there) DIRECTLY to screen RAM
; via self-modifying absolute STA addresses (bypassing blit_screen).
; The SMC target address is computed per-corner by starting from
; smc_screen_addr_hi/lo and adding or subtracting screen_width exactly
; row_offset times (the loop at b0B51/b0B82/b0BB3/b0BE4). This gives the
; exact screen RAM row for each corner. Y then selects the column.
; busy_wait_pause delays each frame; a second $FF ends the animation.
;
; Inputs: wipe_path_row_table ($6300), wipe_path_col_table ($6400);
; smc_screen_addr_hi/lo — screen RAM base for SMC patches;
; offscreen buffer $6700-$7FFF (level tiles pre-loaded by unpack_level_data)
; Outputs: Screen RAM border drawn (Phase 1); level tiles revealed outward (Phase 2)
; Side Effects: Patches SMC operands at a0B6C/D, a0B9D/E, a0BCE/F, a0BFF/C00;
; modifies a58, a59 (ZP); calls busy_wait_pause
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
transition_from_empty_to_full
$0AB8a2 00ldx#$00; Phase 1: draw static border frame (25 rows × 33 cols) to screen RAM ; x-ref: $075D, $2F9D
$0ABAa0 00ldy#$00
$0ABCa9 00lda#$00
$0ABE8d 11 0bstasmc_draw_border_addr_lo; screen RAM pointer lo = 0
$0AC1a9 80lda#$80; screen RAM pointer hi = $80 → base $8000
$0AC38d 12 0bstasmc_draw_border_addr_hi
$0AC6e0 00b_0AC6cpx#$00; first row (row 0)? ; x-ref: $0B16, $0B2B
$0AC8d0 17bneb_0AE1
$0ACAc0 00cpy#$00; left col (col 0)?
$0ACCd0 05bneb_0AD3
$0ACEa9 4flda#$4f; top-left corner ($4F)
$0AD04c 10 0bjmpj_0B10
$0AD3c0 20b_0AD3cpy#$20; right col (col $20)? ; x-ref: $0ACC
$0AD5d0 05bneb_0ADC
$0AD7a9 65lda#$65; top-right corner ($65)
$0AD94c 10 0bjmpj_0B10
$0ADCa9 63b_0ADClda#$63; top-edge fill ($63) ; x-ref: $0AD5
$0ADE4c 10 0bjmpj_0B10
$0AE1e0 18b_0AE1cpx#$18; last row (row $18 = 24)? ; x-ref: $0AC8
$0AE3d0 17bneb_0AFC
$0AE5c0 00cpy#$00; left col?
$0AE7d0 05bneb_0AEE
$0AE9a9 4clda#$4c; bottom-left corner ($4C)
$0AEB4c 10 0bjmpj_0B10
$0AEEc0 20b_0AEEcpy#$20; right col? ; x-ref: $0AE7
$0AF0d0 05bneb_0AF7
$0AF2a9 65lda#$65; bottom-right corner ($65)
$0AF44c 10 0bjmpj_0B10
$0AF7a9 64b_0AF7lda#$64; bottom-edge fill ($64) ; x-ref: $0AF0
$0AF94c 10 0bjmpj_0B10
$0AFCc0 00b_0AFCcpy#$00; middle rows: left col? ; x-ref: $0AE3
$0AFEd0 05bneb_0B05
$0B00a9 65lda#$65; left-edge fill ($65)
$0B024c 10 0bjmpj_0B10
$0B05c0 20b_0B05cpy#$20; right col? ; x-ref: $0AFE
$0B07d0 05bneb_0B0E
$0B09a9 65lda#$65; right-edge fill ($65)
$0B0B4c 10 0bjmpj_0B10
$0B0Ea9 20b_0B0Elda#$20; interior cell: empty ($20) ; x-ref: $0B07
smc_draw_border_addr_lo =*+$01 ; x-ref: $0ABE, $0B1A, $0B20
smc_draw_border_addr_hi =*+$02 ; x-ref: $0AC3, $0B25
$0B1099 00 80j_0B10staSCREEN_RAM_R0C0,y; write char directly to screen RAM ; x-ref: $0AD0, $0AD9, $0ADE, $0AEB, $0AF4, $0AF9, $0B02, $0B0B
$0B13c8iny
$0B14c0 21cpy#$21; all 33 cols done?
$0B16d0 aebneb_0AC6
$0B18a0 00ldy#$00
$0B1Aad 11 0bldasmc_draw_border_addr_lo; advance screen pointer by screen_width (carry-propagated)
$0B1D18clc
$0B1E65 51adczp_screen_width
$0B208d 11 0bstasmc_draw_border_addr_lo
$0B2390 03bccb_0B28
$0B25ee 12 0bincsmc_draw_border_addr_hi
$0B28e8b_0B28inx; x-ref: $0B23
$0B29e0 19cpx#$19; all 25 rows done?
$0B2Bd0 99bneb_0AC6
; Phase 2: draw initial cursor highlight box at the first path-table entry.
$0B2Da9 00lda#$00; Phase 2: reveal level tiles from centre outward; a58=0 (ZP low byte)
$0B2F85 58stazp_ptr_wipe_buf_lo
$0B31a2 02ldx#$02; start at innermost ring (index 2); expand outward via inx
$0B33bd 00 63j_0B33ldawipe_path_row_table,x; read row offset for this reveal step ; x-ref: $0C02, $0C10
$0B36c9 ffcmp#$ff; end-of-frame sentinel?
$0B38d0 03bneb_0B3D
$0B3A4c 05 0cjmpj_0C05; yes: delay this frame then check for end
$0B3Da9 73b_0B3Dlda#$73; --- bottom-right: page=$73+row_off, col=$11+col_off; SMC adds screen_width --- ; x-ref: $0B38
$0B3F18clc
$0B407d 00 63adcwipe_path_row_table,x; offscreen buffer page = $73 + row_offset (bottom half)
$0B4385 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0B45ad b6 0aldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0B6B
$0B488d 6d 0bstasmc_wipe_br_addr_hi
$0B4Bad b7 0aldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset
$0B4Ebc 00 63ldywipe_path_row_table,x
$0B5188b_0B51dey; add screen_width × row_offset to reach target screen RAM row (carry → hi byte patch) ; x-ref: $0B57, $0B5C
$0B5230 0bbmib_0B5F
$0B5418clc
$0B5565 51adczp_screen_width
$0B5790 f8bccb_0B51
$0B59ee 6d 0bincsmc_wipe_br_addr_hi
$0B5C4c 51 0bjmpb_0B51
$0B5F8d 6c 0bb_0B5Fstasmc_wipe_br_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0B52
$0B62a9 11lda#$11
$0B6418clc
$0B657d 00 64adcwipe_path_col_table,x; right col = $11 + col_offset
$0B68a8tay
$0B69b1 58lda(zp_ptr_wipe_buf_lo),y; read level tile from offscreen buffer
smc_wipe_br_addr_lo =*+$01 ; x-ref: $0B5F
smc_wipe_br_addr_hi =*+$02 ; x-ref: $0B48, $0B59
$0B6B99 00 80staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col
$0B6Ea9 73lda#$73; --- top-left: page=$73-row_off, col=$11-col_off; SMC subtracts screen_width ---
$0B7038sec
$0B71fd 00 63sbcwipe_path_row_table,x; offscreen buffer page = $73 - row_offset (top half)
$0B7485 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0B76ad b6 0aldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0B9C
$0B798d 9e 0bstasmc_wipe_tl_addr_hi
$0B7Cad b7 0aldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset
$0B7Fbc 00 63ldywipe_path_row_table,x
$0B8288b_0B82dey; subtract screen_width × row_offset to reach target screen RAM row (borrow → hi byte patch) ; x-ref: $0B88, $0B8D
$0B8330 0bbmib_0B90
$0B8538sec
$0B86e5 51sbczp_screen_width
$0B88b0 f8bcsb_0B82
$0B8Ace 9e 0bdecsmc_wipe_tl_addr_hi
$0B8D4c 82 0bjmpb_0B82
$0B908d 9d 0bb_0B90stasmc_wipe_tl_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0B83
$0B93a9 11lda#$11
$0B9538sec
$0B96fd 00 64sbcwipe_path_col_table,x; left col = $11 - col_offset
$0B99a8tay
$0B9Ab1 58lda(zp_ptr_wipe_buf_lo),y; read level tile from offscreen buffer
smc_wipe_tl_addr_lo =*+$01 ; x-ref: $0B90
smc_wipe_tl_addr_hi =*+$02 ; x-ref: $0B79, $0B8A
$0B9C99 00 80staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col
$0B9Fa9 73lda#$73; --- bottom-left: page=$73+row_off, col=$11-col_off; SMC adds screen_width ---
$0BA118clc
$0BA27d 00 63adcwipe_path_row_table,x; offscreen buffer page = $73 + row_offset (bottom half)
$0BA585 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0BA7ad b6 0aldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0BCD
$0BAA8d cf 0bstasmc_wipe_bl_addr_hi
$0BADad b7 0aldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset
$0BB0bc 00 63ldywipe_path_row_table,x
$0BB388b_0BB3dey; add screen_width × row_offset to reach target screen RAM row (carry → hi byte patch) ; x-ref: $0BB9, $0BBE
$0BB430 0bbmib_0BC1
$0BB618clc
$0BB765 51adczp_screen_width
$0BB990 f8bccb_0BB3
$0BBBee cf 0bincsmc_wipe_bl_addr_hi
$0BBE4c b3 0bjmpb_0BB3
$0BC18d ce 0bb_0BC1stasmc_wipe_bl_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0BB4
$0BC4a9 11lda#$11
$0BC638sec
$0BC7fd 00 64sbcwipe_path_col_table,x; left col = $11 - col_offset
$0BCAa8tay
$0BCBb1 58lda(zp_ptr_wipe_buf_lo),y; read level tile from offscreen buffer
smc_wipe_bl_addr_lo =*+$01 ; x-ref: $0BC1
smc_wipe_bl_addr_hi =*+$02 ; x-ref: $0BAA, $0BBB
$0BCD99 00 80staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col
$0BD0a9 73lda#$73; --- top-right: page=$73-row_off, col=$11+col_off; SMC subtracts screen_width ---
$0BD238sec
$0BD3fd 00 63sbcwipe_path_row_table,x; offscreen buffer page = $73 - row_offset (top half)
$0BD685 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$0BD8ad b6 0aldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0BFE
$0BDB8d 00 0cstasmc_wipe_tr_addr_hi
$0BDEad b7 0aldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset
$0BE1bc 00 63ldywipe_path_row_table,x
$0BE488b_0BE4dey; subtract screen_width × row_offset to reach target screen RAM row (borrow → hi byte patch) ; x-ref: $0BEA, $0BEF
$0BE530 0bbmib_0BF2
$0BE738sec
$0BE8e5 51sbczp_screen_width
$0BEAb0 f8bcsb_0BE4
$0BECce 00 0cdecsmc_wipe_tr_addr_hi
$0BEF4c e4 0bjmpb_0BE4
$0BF28d ff 0bb_0BF2stasmc_wipe_tr_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0BE5
$0BF5a9 11lda#$11
$0BF718clc
$0BF87d 00 64adcwipe_path_col_table,x; right col = $11 + col_offset
$0BFBa8tay
$0BFCb1 58lda(zp_ptr_wipe_buf_lo),y; read level tile from offscreen buffer
smc_wipe_tr_addr_lo =*+$01 ; x-ref: $0BF2
smc_wipe_tr_addr_hi =*+$02 ; x-ref: $0BDB, $0BEC
$0BFE99 00 80staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col
$0C01e8inx; expand: next (outer) reveal step
$0C024c 33 0bjmpj_0B33
$0C0520 c8 09j_0C05jsrbusy_wait_pause; $FF sentinel: delay one frame ; x-ref: $0B3A
$0C08e8inx; skip past the $FF sentinel
$0C09bd 00 63ldawipe_path_row_table,x
$0C0Cc9 ffcmp#$ff; second $FF = all rings revealed, animation complete
$0C0Ef0 03beqr_0C13
$0C104c 33 0bjmpj_0B33
$0C1360r_0C13rts; x-ref: $0C0E
$0C14sound_data_table.byte$00, $0e, $1e, $3e, $0e, $00, $a0, $b4; x-ref: $0CA1, $0CFB, $0D0D
$0C1C.byte$88, $60, $00, $60, $88, $a0, $b4, $00
$0C24.byte$0e, $1e, $3e, $7e, $3e, $1e, $0e, $00
$0C2C.byte$6e, $64, $5a, $50, $46, $3c, $32, $28
$0C34.byte$3c, $50, $64, $78, $00, $7f, $01, $88
$0C3C.byte$01, $8f, $01, $98, $01, $9f, $01, $a8
$0C44.byte$01, $af, $01, $b8, $01, $bf, $01, $c8
$0C4C.byte$01, $cf, $01, $d8, $01, $df, $01, $ef
$0C54.byte$01, $ff, $01, $00, $3e, $34, $2a, $20
$0C5C.byte$16, $0c, $20, $34, $48, $00, $9e, $9e
$0C64.byte$76, $76, $58, $58, $76, $76, $00, $f0
$0C6C.byte$f0, $f0, $f0, $f0, $f0, $f5, $f5, $f5
$0C74.byte$f5, $fa, $fa, $ff, $ff, $00
$0C7Asound_active_flag.byte$00; x-ref: $0C8B, $0C98, $0CAD, $0CB8, $0CC3, $0CCE, $2815
$0C7Bsound_table_index.byte$00; x-ref: $0C8E, $0C9E, $0CA9, $0CB3, $0CBE, $0CC9
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes 6522 VIA audio engines hardware.
; Configures ACR at $E84B for free-running Timer 2 audio sequencing,
; and clears sound sequential array flags variables.
;
; Inputs: None.
; Outputs: Modifies VIA absolute vectors at $E84A, $E84B, and $E848.
; Side Effects: Resets dynamic audio arrays.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0C7Ca9 10init_audiolda#$10; x-ref: $0606, $06B3, $06CB, $0714
$0C7E8d 4b e8sta$e84b; ACR Aux. control register; set to $00 at power on
$0C81a9 0flda#$0f
$0C838d 4a e8sta$e84a; Shift register
$0C86a9 00lda#$00
$0C888d 48 e8sta$e848; Timer 2 LO
$0C8B8d 7a 0cstasound_active_flag
$0C8E8d 7b 0cstasound_table_index
$0C9160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Instantly halts hardware audio.
; Writes $00 to the 6522 VIA Shift Register at $E84A to silence speaker.
;
; Inputs: None.
; Outputs: Writes to absolute vector $E84A.
; Side Effects: Silences speaker.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0C92a9 00stop_audiolda#$00; x-ref: $06B0, $06C8, $0711, $2E49
$0C948d 4a e8sta$e84a; Shift register
$0C9760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Plays sound effects by writing frequency values from sound_data_table to VIA Timer 2.
; Called on interrupts. Uses sound_active_flag and sound_table_index.
;
; Inputs: sound_data_table ($0C14)
; Outputs: Modifies VIA Timer 2 ($E848)
; Side Effects: Modifies sound_active_flag and sound_table_index.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0C98ae 7a 0cplay_sound_tickldxsound_active_flag; x-ref: $064A
$0C9Bd0 01bneb_0C9E
$0C9D60rts
$0C9Eae 7b 0cb_0C9Eldxsound_table_index; x-ref: $0C9B
$0CA1bd 14 0cldasound_data_table,x
$0CA48d 48 e8sta$e848; Timer 2 LO
$0CA7f0 04beqb_0CAD
$0CA9ee 7b 0cincsound_table_index
$0CAC60rts
$0CADce 7a 0cb_0CADdecsound_active_flag; x-ref: $0CA7
$0CB060rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Triggers the guard-captured-in-hole sound effect.
;
; Sets sound_table_index to $25 (entry in sound_data_table at $0C39: a
; rising-pitch tone sequence written to VIA Timer 2 / $E848) and sets
; sound_active_flag to 1 so the audio driver begins playing it.
;
; Called when a guard's actor_status_table entry has the high bit set,
; indicating the guard has just fallen into a dug hole.
;
; Inputs: None
; Outputs: sound_table_index = $25, sound_active_flag = 1
; Side Effects: Audio driver will begin playing the capture tone on next tick
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
trigger_guard_captured_sound
$0CB1a9 25lda#$25; x-ref: $0FB8, $1034
$0CB38d 7b 0cstasound_table_index
$0CB6a9 01lda#$01
$0CB88d 7a 0cstasound_active_flag
$0CBB60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Triggers the time-bonus countdown beep sound effect.
;
; Sets sound_table_index to $06 (entry in sound_data_table at $0C1A: a short
; descending tone sequence — $A0, $B4, $88, $60, $00 — written to VIA
; Timer 2 / $E848) and sets sound_active_flag to 1.
;
; Called by play_bonus_audio_tick once every 8 frames while the bonus
; countdown is running after level completion.
;
; Inputs: None
; Outputs: sound_table_index = $06, sound_active_flag = 1
; Side Effects: Audio driver will play the countdown beep on next tick
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
trigger_countdown_sound
$0CBCa9 06lda#$06; x-ref: $2B15
$0CBE8d 7b 0cstasound_table_index
$0CC1a9 01lda#$01
$0CC38d 7a 0cstasound_active_flag
$0CC660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Triggers the gold-collection sound effect.
;
; Sets sound_table_index to $4E (entry in sound_data_table at $0C62: a short
; two-tone beep sequence — $58, $58, $76, $76, $00 — written to VIA
; Timer 2 / $E848) and sets sound_active_flag to 1.
;
; Called immediately after the player picks up a gold piece (gold_count
; incremented), just before the floating score display timer is armed.
;
; Inputs: None
; Outputs: sound_table_index = $4E, sound_active_flag = 1
; Side Effects: Audio driver will play the collection beep on next tick
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
trigger_collection_sound
$0CC7a9 4elda#$4e; x-ref: $2601
$0CC98d 7b 0cstasound_table_index
$0CCCa9 01lda#$01
$0CCE8d 7a 0cstasound_active_flag
$0CD160rts
; One-shot latch for the dynamic digging pitch tone.
; $FF = Armed / ready to trigger
; $00 = Fired / currently playing
$0CD2dig_pitch_latch.byte$ff; ; x-ref: $0CD3, $0CDA, $0CE9, $0CF0, $0DC6
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Plays a one-shot dynamic pitch tone based on the player's current screen row.
;
; Checks a0CD2 (the one-shot latch): if already $00 (fired this update), returns
; immediately without re-triggering. Otherwise clears the latch to $00 and
; computes the VIA Timer 2 frequency:
;
; frequency = (draw_page - $55) << 2
;
; The result is written directly to VIA Timer 2 Low ($E848), producing a
; square-wave tone whose pitch rises as draw_page increases (i.e. as the player
; moves to a lower screen row while digging).
;
; Called each frame when the player is actively digging (screen cell at player
; position has bit 4 set). Paired with silence_dynamic_pitch which is called
; when the player stops digging.
;
; Inputs: draw_page — player's current screen page (determines pitch);
; a0CD2 — one-shot latch ($FF = ready, $00 = already fired)
; Outputs: $E848 (VIA Timer 2 LO) written with computed frequency
; Side Effects: Clears a0CD2 to $00 after first trigger
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0CD3ad d2 0cplay_dynamic_pitchldadig_pitch_latch; x-ref: $0E75
$0CD6f0 05beqb_0CDD
$0CD8a9 00lda#$00
$0CDA8d d2 0cstadig_pitch_latch
$0CDDad b5 0db_0CDDldadraw_page; x-ref: $0CD6
$0CE038sec
$0CE1e9 55sbc#$55
$0CE30aasla
$0CE40aasla
$0CE58d 48 e8sta$e848; Timer 2 LO
$0CE860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Silences the dynamic dig pitch tone and re-arms the one-shot latch.
;
; Checks dynamic_pitch_latch: if non-zero ($FF = already silent/armed), returns
; immediately without writing to hardware (idempotent guard). Otherwise, the
; latch was $00 (pitch has been playing), so:
; 1. Resets dynamic_pitch_latch to $FF (re-arms for next dig event)
; 2. Writes $00 to VIA Timer 2 Low ($E848), stopping the square-wave tone
;
; Called each frame when the player is NOT actively digging (bit 4 clear in
; the cell under them). Paired with play_dynamic_pitch.
;
; Inputs: dynamic_pitch_latch — $00 means pitch is currently active
; Outputs: $E848 (VIA Timer 2 LO) set to $00 (silence)
; Side Effects: Resets dynamic_pitch_latch to $FF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
silence_dynamic_pitch
$0CE9ad d2 0cldadig_pitch_latch; x-ref: $10BF
$0CECd0 0abner_0CF8
$0CEEa9 fflda#$ff
$0CF08d d2 0cstadig_pitch_latch
$0CF3a9 00lda#$00
$0CF58d 48 e8sta$e848; Timer 2 LO
$0CF860r_0CF8rts; x-ref: $0CEC
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Plays the level-completion victory jingle synchronously.
;
; Steps through sound_data_table starting at index $18 ($0C2C: a 12-note
; descending-then-ascending melody — $6E,$64,$5A,$50,$46,$3C,$32,$28,$3C,
; $50,$64,$78,$00). Each non-zero byte is written to VIA Timer 2 Low
; ($E848) to set the square-wave pitch, then busy_wait_pause is called to
; hold the note for one frame before advancing. Stops on the $00 terminator.
;
; Blocking: this routine does not return until the full jingle has played.
; Called by check_level_complete after all gold has been collected.
;
; Inputs: sound_data_table[$18..$24] — note sequence
; Outputs: $E848 driven with each note frequency in turn
; Side Effects: Blocks for the full jingle duration (~12 x busy_wait_pause);
; leaves $E848 at $00 (silence) on return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0CF9a2 18play_victory_jingleldx#$18; x-ref: $26CA
$0CFBbd 14 0cj_0CFBldasound_data_table,x; x-ref: $0D07
$0CFE8d 48 e8sta$e848; Timer 2 LO
$0D01f0 07beqr_0D0A
$0D0320 c8 09jsrbusy_wait_pause
$0D06e8inx
$0D074c fb 0cjmpj_0CFB
$0D0A60r_0D0Arts; x-ref: $0D01
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Plays the player-death sound effect synchronously.
;
; Steps through sound_data_table starting at index $57 ($0C6B: 13 notes of
; large timer values — $F0,$F0,$F0,$F0,$F0,$F5,$F5,$F5,$F5,$FA,$FA,$FF,$FF,
; $00 — producing a deep, slow-frequency descending drone on VIA Timer 2
; / $E848). Each non-zero byte sets the pitch; busy_wait_pause holds the note
; before advancing. Stops on the $00 terminator.
;
; Blocking: does not return until the full sound has played.
; Called from all four player death triggers (guard collision, 1x2 object,
; tile death, hole fall) after a short delay loop.
;
; Inputs: sound_data_table[$57..$63] — note sequence
; Outputs: $E848 driven with each note frequency in turn
; Side Effects: Blocks for the full sound duration (~13 x busy_wait_pause);
; leaves $E848 at $00 (silence) on return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0D0Ba2 57play_death_soundldx#$57; x-ref: $2876, $28A3, $28D2, $28FB
$0D0Dbd 14 0cj_0D0Dldasound_data_table,x; x-ref: $0D19
$0D108d 48 e8sta$e848; Timer 2 LO
$0D13f0 07beqr_0D1C
$0D1520 c8 09jsrbusy_wait_pause
$0D18e8inx
$0D194c 0d 0djmpj_0D0D
$0D1C60r_0D1Crts; x-ref: $0D13
; Actor sprite animation table. 2 chars wide × 4 frames × N strips.
; Each strip = 8 bytes: [char_A, char_B] × 4 frames (animation_frame = 0,4,8,$C).
; Index = sprite_base_offset + animation_frame.
; $20 (space) = transparent cell; caller skips the sta if char == $20.
;
; Strip layout (sprite_base_offset values):
; $00 : Player walking right
; $08 : Player walking left
; $10 : Player climbing up
; $18 : Player descending / hanging
; $20 : Guard walking right
; $28 : Guard walking left
; $30 : Guard climbing up
; $38 : Guard descending
; $40 : Player digging right
; $48 : Guard variant (captured / in hole)
; $50 : Player digging left
; $58 : Guard variant 2
$0D1Dsprite_anim_table.byte$7b, $67, $2d, $20, $7b, $20, $54, $20; x-ref: $11BD, $11C8, $11D3, $11DE
$0D25.byte$6c, $20, $6b, $20, $6c, $20, $48, $20
$0D2D.byte$7b, $20, $47, $20, $7b, $20, $73, $20
$0D35.byte$6c, $20, $59, $20, $6c, $20, $2d, $65
$0D3D.byte$2e, $20, $18, $20, $2e, $20, $4d, $20
$0D45.byte$2e, $20, $4e, $20, $2e, $20, $56, $20
$0D4D.byte$2e, $20, $14, $20, $2e, $20, $1e, $20
$0D55.byte$2e, $20, $1e, $20, $2e, $20, $19, $20
$0D5D.byte$7b, $67, $27, $20, $7b, $20, $19, $20
$0D65.byte$6c, $20, $2f, $20, $6c, $20, $19, $20
$0D6D.byte$7b, $20, $19, $20, $7b, $20, $1c, $20
$0D75.byte$6c, $20, $19, $20, $6c, $20, $27, $65
$0D7D.byte$2e, $20, $14, $20, $2e, $20, $14, $20
$0D85.byte$2e, $20, $14, $20, $2e, $20, $14, $20
$0D8D.byte$6c, $20, $5b, $1e, $6c, $20, $5b, $1e
$0D95.byte$6c, $20, $5b, $20, $6c, $20, $5b, $20
$0D9D.byte$7b, $1e, $5b, $20, $7b, $1e, $5b, $20
$0DA5.byte$7b, $20, $5b, $20, $7b, $20, $5b, $20
$0DADtile_buf_0.byte$00; x-ref: $0DD5, $1147, $1179
$0DAEtile_buf_1.byte$00; x-ref: $0DD8, $114D, $117F
$0DAFtile_buf_2.byte$00; x-ref: $0DDB, $1153, $1185
$0DB0tile_buf_3.byte$00; x-ref: $0DDE, $1159, $118B
$0DB1player_state.byte$ff; ; x-ref: $0762, $0DCB, $0FDC, $1058, $1066, $107A, $10A9, $115F, ...
$0DB2respawn_timer.byte$01; ; x-ref: $0DD0, $1088, $1164
; Current animation frame offset within a sprite strip.
; Cycles 0 → 4 → 8 → $C → 0 (step ±4, masked with & $0C).
; Incremented on rightward/upward moves; decremented on leftward/downward.
; Reset to $08 when an actor is initialised.
$0DB3anim_frame.byte$00; x-ref: $0DE8, $0E7D, $0E85, $0E9B, $0EA5, $0EAE, $0EB6, $0ECC, ...
$0DB4draw_offset.byte$00; x-ref: $0F27, $0F6A, $0F87, $0FC5, $1003, $1041, $10B3, $1144, ...
$0DB5draw_page.byte$00; x-ref: $0CDD, $0E8A, $0EBB, $0EEE, $0F92, $0FCE, $100E, $104A, ...
; Selects animation strip inside sprite_anim_table.
; Set by movement/action handlers to encode actor type + direction:
; $00=player right, $08=player left, $10=player climb, $18=player hang,
; $20=guard right, $28=guard left, $30=guard climb, $38=guard hang,
; $40=player dig-right, $50=player dig-left, etc.
; Combined with animation_frame (0/4/8/$C) as the final table index.
$0DB6anim_strip_base.byte$00; x-ref: $0DE3, $0E7A, $0E94, $0EAB, $0EC5, $0EDE, $0EF8, $0F17, ...
$0DB7input_dx.byte$00; x-ref: $0DF0, $0DFC, $0E50, $0E5F, $0E62, $0E6E, $106D, $10CF, ...
$0DB8input_dy.byte$00; x-ref: $0DED, $0DF9, $0E32, $0E41, $0E67, $0E71, $1070, $10DC, ...
$0DB9input_state.byte$00; x-ref: $0DF3, $0DFF, $0E10, $0E22, $1073, $10C2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes zero page pointers and game state variables to their starting values.
; The previous labels and comments were hallucinated.
;
; Inputs: None
; Outputs: Modifies ZP pointers ($52, $54, $5A), clears input states, sets initial counters.
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0DBAa9 00init_game_variableslda#$00; x-ref: $073F, $2FBC, $3107
$0DBC85 52stazp_ptr_screen_lo
$0DBE85 54stazp_ptr_attr_lo; TEMPF1 Temporary storage for FLPT value.
$0DC0a9 2blda#$2b
$0DC285 5astazp_ptr_playfield_lo
$0DC4a9 fflda#$ff
$0DC68d d2 0cstadig_pitch_latch
$0DC9a9 fflda#$ff
$0DCB8d b1 0dstaplayer_state
$0DCEa9 80lda#$80
$0DD08d b2 0dstarespawn_timer
$0DD3a9 20lda#$20
$0DD58d ad 0dstatile_buf_0
$0DD88d ae 0dstatile_buf_1
$0DDB8d af 0dstatile_buf_2
$0DDE8d b0 0dstatile_buf_3
$0DE1a9 00lda#$00
$0DE38d b6 0dstaanim_strip_base
$0DE6a9 08lda#$08
$0DE88d b3 0dstaanim_frame
$0DEBa9 00lda#$00
$0DED8d b8 0dstainput_dy
$0DF08d b7 0dstainput_dx
$0DF38d b9 0dstainput_state
$0DF660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans Commodore PET keyboard matrix for movement and action (dig) keys.
; Checks PIA 1 Port A ($E810) against configured Zero Page row/col masks.
; Also cancels out diagonal movements, restricting motion to 4 directions.
;
; Inputs: Hardware matrix ($E810/$E812), ZP key masks ($28-$36)
; Outputs: Modifies input_state ($0DB9), input_dx ($0DB7), input_dy ($0DB8)
; Side Effects: Modifies PIA hardware control registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$0DF7a9 00scan_keyboardlda#$00; x-ref: $066B
$0DF98d b8 0dstainput_dy
$0DFC8d b7 0dstainput_dx
$0DFF8d b9 0dstainput_state
$0E02a5 2cldazp_key_row_dig1; ARYTAB Pointer: Start of BASIC Arrays
$0E048d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E07ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E0A25 35andzp_key_col_dig1
$0E0Cd0 06bneb_0E14
$0E0Ea9 01lda#$01
$0E108d b9 0dstainput_state
$0E1360rts
$0E14a5 2db_0E14ldazp_key_row_dig2; x-ref: $0E0C
$0E168d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E19ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E1C25 36andzp_key_col_dig2; CURLIN Current BASIC Line Number
$0E1Ed0 06bneb_0E26
$0E20a9 fflda#$ff
$0E228d b9 0dstainput_state
$0E2560rts
$0E26a5 29b_0E26ldazp_key_row_up; x-ref: $0E1E
$0E288d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E2Bad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E2E25 32andzp_key_col_up; FRESPC Utility String Pointer
$0E30d0 03bneb_0E35
$0E32ce b8 0ddecinput_dy
$0E35a5 28b_0E35ldazp_key_row_down; x-ref: $0E30 TXTTAB Pointer: Start of BASIC Text
$0E378d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E3Aad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E3D25 31andzp_key_col_down
$0E3Fd0 03bneb_0E44
$0E41ee b8 0dincinput_dy
$0E44a5 2ab_0E44ldazp_key_row_left; x-ref: $0E3F VARTAB Pointer: Start of BASIC Variables
$0E468d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E49ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E4C25 33andzp_key_col_left
$0E4Ed0 03bneb_0E53
$0E50ce b7 0ddecinput_dx
$0E53a5 2bb_0E53ldazp_key_row_right; x-ref: $0E4E
$0E558d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$0E58ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$0E5B25 34andzp_key_col_right; MEMSIZ Pointer: Highest Address Used by BASIC
$0E5Dd0 03bneb_0E62
$0E5Fee b7 0dincinput_dx
$0E62ad b7 0db_0E62ldainput_dx; x-ref: $0E5D
$0E65f0 0dbeqr_0E74
$0E67ad b8 0dldainput_dy
$0E6Af0 08beqr_0E74
$0E6Ca9 00lda#$00
$0E6E8d b7 0dstainput_dx
$0E718d b8 0dstainput_dy
$0E7460r_0E74rts; x-ref: $0E65, $0E6A
$0E7520 d3 0cj_0E75jsrplay_dynamic_pitch; x-ref: $10BC
$0E78a9 30lda#$30
$0E7A8d b6 0dstaanim_strip_base
$0E7Dad b3 0dldaanim_frame
$0E8018clc
$0E8169 04adc#$04
$0E8329 0cand#$0c
$0E858d b3 0dstaanim_frame
$0E88d0 03bner_0E8D
$0E8Aee b5 0dincdraw_page
$0E8D60r_0E8Drts; x-ref: $0E88
$0E8Eb1 5aj_0E8Elda(zp_ptr_playfield_lo),y; x-ref: $10D6
$0E9029 18and#$18
$0E92d0 15bneb_0EA9
$0E94ad b6 0dldaanim_strip_base
$0E97c9 20cmp#$20
$0E99d0 23bner_0EBE
$0E9Bad b3 0dldaanim_frame
$0E9E18clc
$0E9F69 04adc#$04
$0EA129 0cand#$0c
$0EA3f0 19beqr_0EBE
$0EA58d b3 0dstaanim_frame
$0EA860rts
$0EA9a9 20b_0EA9lda#$20; x-ref: $0E92
$0EAB8d b6 0dstaanim_strip_base
$0EAEad b3 0dldaanim_frame
$0EB118clc
$0EB269 04adc#$04
$0EB429 0cand#$0c
$0EB68d b3 0dstaanim_frame
$0EB9d0 03bner_0EBE
$0EBBee b5 0dincdraw_page
$0EBE60r_0EBErts; x-ref: $0E99, $0EA3, $0EB9
$0EBFb1 5aj_0EBFlda(zp_ptr_playfield_lo),y; x-ref: $10D9
$0EC129 04and#$04
$0EC3d0 17bneb_0EDC
$0EC5ad b6 0dldaanim_strip_base
$0EC8c9 20cmp#$20
$0ECAd0 25bner_0EF1
$0ECCad b3 0dldaanim_frame
$0ECF38sec
$0ED0e9 04sbc#$04
$0ED229 0cand#$0c
$0ED4c9 0ccmp#$0c
$0ED6f0 19beqr_0EF1
$0ED88d b3 0dstaanim_frame
$0EDB60rts
$0EDCa9 20b_0EDClda#$20; x-ref: $0EC3
$0EDE8d b6 0dstaanim_strip_base
$0EE1ad b3 0dldaanim_frame
$0EE438sec
$0EE5e9 04sbc#$04
$0EE729 0cand#$0c
$0EE98d b3 0dstaanim_frame
$0EECd0 03bner_0EF1
$0EEEce b5 0ddecdraw_page
$0EF160r_0EF1rts; x-ref: $0ECA, $0ED6, $0EEC
$0EF2b1 5aj_0EF2lda(zp_ptr_playfield_lo),y; x-ref: $10E3
$0EF429 02and#$02
$0EF6d0 17bneb_0F0F
$0EF8ad b6 0dldaanim_strip_base
$0EFBf0 04beqb_0F01
$0EFDc9 40cmp#$40
$0EFFd0 29bner_0F2A
$0F01ad b3 0db_0F01ldaanim_frame; x-ref: $0EFB
$0F0418clc
$0F0569 04adc#$04
$0F0729 0cand#$0c
$0F09f0 1fbeqr_0F2A
$0F0B8d b3 0dstaanim_frame
$0F0E60rts
$0F0Fb1 5ab_0F0Flda(zp_ptr_playfield_lo),y; x-ref: $0EF6
$0F1129 20and#$20
$0F13f0 02beqb_0F17
$0F15a9 40lda#$40
$0F178d b6 0db_0F17staanim_strip_base; x-ref: $0F13
$0F1Aad b3 0dldaanim_frame
$0F1D18clc
$0F1E69 04adc#$04
$0F2029 0cand#$0c
$0F228d b3 0dstaanim_frame
$0F25d0 03bner_0F2A
$0F27ee b4 0dincdraw_offset
$0F2A60r_0F2Arts; x-ref: $0EFF, $0F09, $0F25
$0F2Bb1 5aj_0F2Blda(zp_ptr_playfield_lo),y; x-ref: $10E6
$0F2D29 01and#$01
$0F2Fd0 1bbneb_0F4C
$0F31ad b6 0dldaanim_strip_base
$0F34c9 10cmp#$10
$0F36f0 04beqb_0F3C
$0F38c9 50cmp#$50
$0F3Ad0 31bner_0F6D
$0F3Cad b3 0db_0F3Cldaanim_frame; x-ref: $0F36
$0F3F38sec
$0F40e9 04sbc#$04
$0F4229 0cand#$0c
$0F44c9 0ccmp#$0c
$0F46f0 25beqr_0F6D
$0F488d b3 0dstaanim_frame
$0F4B60rts
$0F4Cb1 5ab_0F4Clda(zp_ptr_playfield_lo),y; x-ref: $0F2F
$0F4E29 20and#$20
$0F50f0 04beqb_0F56
$0F52a9 50lda#$50
$0F54d0 02bneb_0F58
$0F56a9 10b_0F56lda#$10; x-ref: $0F50
$0F588d b6 0db_0F58staanim_strip_base; x-ref: $0F54
$0F5Bad b3 0dldaanim_frame
$0F5E38sec
$0F5Fe9 04sbc#$04
$0F6129 0cand#$0c
$0F638d b3 0dstaanim_frame
$0F66c9 0ccmp#$0c
$0F68d0 03bner_0F6D
$0F6Ace b4 0ddecdraw_offset
$0F6D60r_0F6Drts; x-ref: $0F3A, $0F46, $0F68
$0F6Ec8j_0F6Einy; x-ref: $10C9
$0F6Fe6 5binczp_ptr_playfield_hi
$0F71b1 5alda(zp_ptr_playfield_lo),y
$0F73c6 5bdeczp_ptr_playfield_hi
$0F7588dey
$0F7629 40and#$40
$0F78f0 6fbeqr_0FE9
$0F7Aa2 09ldx#$09
$0F7Cbd 43 1eb_0F7Cldadig_slot_status,x; x-ref: $0F99
$0F7F30 17bmib_0F98
$0F81bd 57 1eldadig_slot_col,x
$0F8438sec
$0F85e9 01sbc#$01
$0F87cd b4 0dcmpdraw_offset
$0F8Ad0 0cbneb_0F98
$0F8Cbd 4d 1eldadig_slot_page,x
$0F8F38sec
$0F90e9 02sbc#$02
$0F92cd b5 0dcmpdraw_page
$0F95d0 01bneb_0F98
$0F9760rts
$0F98cab_0F98dex; x-ref: $0F7F, $0F8A, $0F95
$0F9910 e1bplb_0F7C
$0F9Bc8iny
$0F9Cb1 5alda(zp_ptr_playfield_lo),y
$0F9E88dey
$0F9F29 03and#$03
$0FA1d0 01bneb_0FA4
$0FA360rts
$0FA4c8b_0FA4iny; x-ref: $0FA1
$0FA5b1 5alda(zp_ptr_playfield_lo),y
$0FA788dey
$0FA829 0cand#$0c
$0FAAf0 01beqb_0FAD
$0FAC60rts
$0FADa2 09b_0FADldx#$09; x-ref: $0FAA
$0FAFbd 43 1eb_0FAFldadig_slot_status,x; x-ref: $0FB5
$0FB230 04bmib_0FB8
$0FB4cadex
$0FB510 f8bplb_0FAF
$0FB760rts
$0FB820 b1 0cb_0FB8jsrtrigger_guard_captured_sound; x-ref: $0FB2
$0FBBa9 00lda#$00
$0FBD9d 43 1estadig_slot_status,x
$0FC0a9 fflda#$ff
$0FC29d 93 1estadig_slot_hole_idx,x
$0FC5ad b4 0dldadraw_offset
$0FC89d 57 1estadig_slot_col,x
$0FCBfe 57 1eincdig_slot_col,x
$0FCEad b5 0dldadraw_page
$0FD19d 4d 1estadig_slot_page,x
$0FD4fe 4d 1eincdig_slot_page,x
$0FD7fe 4d 1eincdig_slot_page,x
$0FDAa9 01lda#$01
$0FDC8d b1 0dstaplayer_state
$0FDFa9 70lda#$70
$0FE18d b6 0dstaanim_strip_base
$0FE4a9 00lda#$00
$0FE68d b3 0dstaanim_frame
$0FE960r_0FE9rts; x-ref: $0F78
$0FEA88j_0FEAdey; x-ref: $10CC
$0FEBe6 5binczp_ptr_playfield_hi
$0FEDb1 5alda(zp_ptr_playfield_lo),y
$0FEFc6 5bdeczp_ptr_playfield_hi
$0FF1c8iny
$0FF229 40and#$40
$0FF4f0 6fbeqr_1065
$0FF6a2 09ldx#$09
$0FF8bd 43 1eb_0FF8ldadig_slot_status,x; x-ref: $1015
$0FFB30 17bmib_1014
$0FFDbd 57 1eldadig_slot_col,x
$100018clc
$100169 01adc#$01
$1003cd b4 0dcmpdraw_offset
$1006d0 0cbneb_1014
$1008bd 4d 1eldadig_slot_page,x
$100B38sec
$100Ce9 02sbc#$02
$100Ecd b5 0dcmpdraw_page
$1011d0 01bneb_1014
$101360rts
$1014cab_1014dex; x-ref: $0FFB, $1006, $1011
$101510 e1bplb_0FF8
$101788dey
$1018b1 5alda(zp_ptr_playfield_lo),y
$101Ac8iny
$101B29 03and#$03
$101Dd0 01bneb_1020
$101F60rts
$102088b_1020dey; x-ref: $101D
$1021b1 5alda(zp_ptr_playfield_lo),y
$1023c8iny
$102429 0cand#$0c
$1026f0 01beqb_1029
$102860rts
$1029a2 09b_1029ldx#$09; x-ref: $1026
$102Bbd 43 1eb_102Bldadig_slot_status,x; x-ref: $1031
$102E30 04bmib_1034
$1030cadex
$103110 f8bplb_102B
$103360rts
$103420 b1 0cb_1034jsrtrigger_guard_captured_sound; x-ref: $102E
$1037a9 00lda#$00
$10399d 43 1estadig_slot_status,x
$103Ca9 fflda#$ff
$103E9d 93 1estadig_slot_hole_idx,x
$1041ad b4 0dldadraw_offset
$10449d 57 1estadig_slot_col,x
$1047de 57 1edecdig_slot_col,x
$104Aad b5 0dldadraw_page
$104D9d 4d 1estadig_slot_page,x
$1050fe 4d 1eincdig_slot_page,x
$1053fe 4d 1eincdig_slot_page,x
$1056a9 01lda#$01
$10588d b1 0dstaplayer_state
$105Ba9 80lda#$80
$105D8d b6 0dstaanim_strip_base
$1060a9 00lda#$00
$10628d b3 0dstaanim_frame
$106560r_1065rts; x-ref: $0FF4
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main player logic tick. Processes the top-level player state (freeze, dig, idle).
; State $FF: Freeze waiting. Ends when respawn_timer hits 0 or user gives input.
; State $01: Digging active. Cycles anim_frame & waits for digging actors to clean up.
; State $00: Normal play. Checks if falling, delegates input_state (dig/move).
;
; Inputs: player_state, inputs, respawn_timer, actor_status_table
; Outputs: May end freeze (unfreezing guards), may process player movements.
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1066ad b1 0dprocess_player_stateldaplayer_state; x-ref: $0638, $066E, $3238
$1069f0 42beqb_10AD
$106B10 21bplb_108E
$106Dad b7 0dldainput_dx
$10700d b8 0dorainput_dy
$10730d b9 0dorainput_state
$1076f0 10beqb_1088
$1078a9 00b_1078lda#$00; x-ref: $108B
$107A8d b1 0dstaplayer_state
$107Da2 04ldx#$04
$107Fa9 02lda#$02
$10819d 1c 22b_1081staguard_move_rate,x; x-ref: $1085
$1084cadex
$108510 fabplb_1081
$108760rts
$1088ce b2 0db_1088decrespawn_timer; x-ref: $1076
$108Bf0 ebbeqb_1078
$108D60rts
$108Ead b3 0db_108Eldaanim_frame; x-ref: $106B
$109118clc
$109269 04adc#$04
$109429 0cand#$0c
$10968d b3 0dstaanim_frame
$1099a0 09ldy#$09
$109Bb9 43 1eb_109Bldadig_slot_status,y; x-ref: $10A5
$109Ef0 0cbeqr_10AC
$10A0c9 01cmp#$01
$10A2f0 08beqr_10AC
$10A488dey
$10A510 f4bplb_109B
$10A7a9 00lda#$00
$10A98d b1 0dstaplayer_state
$10AC60r_10ACrts; x-ref: $109E, $10A2
$10ADac b5 0db_10ADldydraw_page; x-ref: $1069
$10B0c8iny
$10B184 5bstyzp_ptr_playfield_hi
$10B3ac b4 0dldydraw_offset
$10B6b1 5alda(zp_ptr_playfield_lo),y
$10B829 10and#$10
$10BAf0 03beqb_10BF
$10BC4c 75 0ejmpj_0E75
$10BF20 e9 0cb_10BFjsrsilence_dynamic_pitch; x-ref: $10BA
$10C2ad b9 0dldainput_state
$10C5f0 08beqb_10CF
$10C730 03bmib_10CC
$10C94c 6e 0fjmpj_0F6E
$10CC4c ea 0fb_10CCjmpj_0FEA; x-ref: $10C7
$10CFad b7 0db_10CFldainput_dx; x-ref: $10C5
$10D2f0 08beqb_10DC
$10D430 03bmib_10D9
$10D64c 8e 0ejmpj_0E8E
$10D94c bf 0eb_10D9jmpj_0EBF; x-ref: $10D4
$10DCad b8 0db_10DCldainput_dy; x-ref: $10D2
$10DFf0 08beqb_10E9
$10E130 03bmib_10E6
$10E34c f2 0ejmpj_0EF2
$10E64c 2b 0fb_10E6jmpj_0F2B; x-ref: $10E1
$10E9ad b6 0db_10E9ldaanim_strip_base; x-ref: $10DF
$10ECd0 09bneb_10F7
$10EEad b3 0dldaanim_frame
$10F109 04ora#$04
$10F38d b3 0dstaanim_frame
$10F660rts
$10F7c9 10b_10F7cmp#$10; x-ref: $10EC
$10F9d0 09bneb_1104
$10FBad b3 0dldaanim_frame
$10FE29 08and#$08
$11008d b3 0dstaanim_frame
$110360rts
$1104c9 20b_1104cmp#$20; x-ref: $10F9
$1106d0 06bneb_110E
$1108a9 00lda#$00
$110A8d b3 0dstaanim_frame
$110D60rts
$110Ec9 30b_110Ecmp#$30; x-ref: $1106
$1110d0 06bneb_1118
$1112a9 00lda#$00
$11148d b3 0dstaanim_frame
$111760rts
$1118c9 70b_1118cmp#$70; x-ref: $1110
$111Ad0 0bbneb_1127
$111Ca9 00lda#$00
$111E8d b6 0dstaanim_strip_base
$1121a9 08lda#$08
$11238d b3 0dstaanim_frame
$112660rts
$1127c9 80b_1127cmp#$80; x-ref: $111A
$1129d0 0bbneb_1136
$112Ba9 10lda#$10
$112D8d b6 0dstaanim_strip_base
$1130a9 04lda#$04
$11328d b3 0dstaanim_frame
$113560rts
$1136a9 60b_1136lda#$60; x-ref: $1129
$11388d b6 0dstaanim_strip_base
$113B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Restores the 4 background tiles saved by save_bg_and_draw_sprite, erasing
; the sprite. Called at the top of each IRQ frame before position/animation
; updates — the sprite bg-restore counterpart of save_bg_and_draw_sprite.
;
; Writes tile_buf_0..3 back to screen + attribute RAM:
; tile_buf_0 -> screen page (draw_page), col draw_offset
; tile_buf_1 -> attr page (draw_page+1), col draw_offset-1
; tile_buf_2 -> attr page (draw_page+1), col draw_offset
; tile_buf_3 -> attr page (draw_page+1), col draw_offset+1
;
; Inputs: tile_buf_0..3, draw_page, draw_offset
; Outputs: Screen + attr RAM restored at sprite position
; Side Effects: a53/a55 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$113Cac b5 0drestore_sprite_bgldydraw_page; x-ref: $0632, $0653, $18D1, $1A0E, $2FDD, $3009, $3232
$113F84 53styzp_ptr_screen_hi
$1141c8iny
$114284 55styzp_ptr_attr_hi
$1144ac b4 0dldydraw_offset
$1147ad ad 0dldatile_buf_0
$114A91 52sta(zp_ptr_screen_lo),y
$114C88dey
$114Dad ae 0dldatile_buf_1
$115091 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$1152c8iny
$1153ad af 0dldatile_buf_2
$115691 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$1158c8iny
$1159ad b0 0dldatile_buf_3
$115C91 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$115E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Saves the background tiles at the sprite position then draws the current
; animation frame on top. Player-side equivalent of save_hole_bg_and_draw.
; Paired with draw_tile which restores the saved background.
;
; Early exit conditions (return without doing anything):
; - player_state < 0 (dead) AND respawn_timer & $10 set
; -> suppresses draw on alternate respawn flash phases
; - any actor has status $03 (digging) at draw_page/draw_offset
; AND actor_anim_step & $01 set -> suppresses draw on odd dig frames
;
; Background save (4 chars -> tile_buf_0..3):
; tile_buf_0: screen page (draw_page), col draw_offset
; tile_buf_1: attr page (draw_page+1), col draw_offset-1
; tile_buf_2: attr page (draw_page+1), col draw_offset
; tile_buf_3: attr page (draw_page+1), col draw_offset+1
;
; Sprite draw from sprite_anim_table[anim_strip_base + anim_frame] (4 chars):
; char[0] -> screen page, draw_offset
; char[1] -> attr page, draw_offset-1
; char[2] -> attr page, draw_offset
; char[3] -> attr page, draw_offset+1
; $20 (space) = transparent; skipped to preserve background.
;
; Sprite layout: 1 char on screen row + 3 chars on attribute row below.
;
; Inputs: draw_page/offset, anim_strip_base, anim_frame, player_state,
; respawn_timer, actor status/dig arrays
; Outputs: tile_buf_0..3 saved; sprite written to screen + attr RAM
; Side Effects: a53/a55 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
save_bg_and_draw_sprite
$115Fad b1 0dldaplayer_state; x-ref: $063B, $068F, $2FD3, $2FFF, $302D, $311E, $323B
$116210 08bplb_116C
$1164ad b2 0dldarespawn_timer
$116729 10and#$10
$1169f0 01beqb_116C
$116B60rts
$116Cac b5 0db_116Cldydraw_page; x-ref: $1162, $1169
$116F84 53styzp_ptr_screen_hi
$1171c8iny
$117284 55styzp_ptr_attr_hi
$1174ac b4 0dldydraw_offset
$1177b1 52lda(zp_ptr_screen_lo),y
$11798d ad 0dstatile_buf_0
$117C88dey
$117Db1 54lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$117F8d ae 0dstatile_buf_1
$1182c8iny
$1183b1 54lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$11858d af 0dstatile_buf_2
$1188c8iny
$1189b1 54lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$118B8d b0 0dstatile_buf_3
$118E88dey
$118Fa2 09ldx#$09
$1191bd 43 1eb_1191ldadig_slot_status,x; x-ref: $11B3
$119430 1cbmib_11B2
$1196c9 03cmp#$03
$1198d0 1bbneb_11B5
$119Abd 4d 1eldadig_slot_page,x
$119Dcd b5 0dcmpdraw_page
$11A0d0 10bneb_11B2
$11A2bd 57 1eldadig_slot_col,x
$11A5cd b4 0dcmpdraw_offset
$11A8d0 08bneb_11B2
$11AAbd 61 1eldadig_slot_anim_step,x
$11AD29 01and#$01
$11AFf0 04beqb_11B5
$11B160rts
$11B2cab_11B2dex; x-ref: $1194, $11A0, $11A8
$11B310 dcbplb_1191
$11B5ad b6 0db_11B5ldaanim_strip_base; x-ref: $1198, $11AF
$11B818clc
$11B96d b3 0dadcanim_frame
$11BCaatax
$11BDbd 1d 0dldasprite_anim_table,x
$11C0c9 20cmp#$20
$11C2f0 02beqb_11C6
$11C491 52sta(zp_ptr_screen_lo),y
$11C6e8b_11C6inx; x-ref: $11C2
$11C788dey
$11C8bd 1d 0dldasprite_anim_table,x
$11CBc9 20cmp#$20
$11CDf0 02beqb_11D1
$11CF91 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$11D1e8b_11D1inx; x-ref: $11CD
$11D2c8iny
$11D3bd 1d 0dldasprite_anim_table,x
$11D6c9 20cmp#$20
$11D8f0 02beqb_11DC
$11DA91 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$11DCe8b_11DCinx; x-ref: $11D8
$11DDc8iny
$11DEbd 1d 0dldasprite_anim_table,x
$11E1c9 20cmp#$20
$11E3f0 02beqr_11E7
$11E591 54sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value.
$11E760r_11E7rts; x-ref: $11E3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Full level initialisation pipeline. Seven sequential phases:
;
; 1. TILE RENDER ($11E8): Set a56=$01/a57=$67 (buffer start at $6701).
; For each row: read 16 bytes of level data (32 nibble-packed tile codes),
; call render_actor_sprite for each nibble (high then low), advance Y.
; After each row: check bit 7 of the last row-byte → a57 += 2 (normal row)
; or a57 += 1 (thin row). Stop when a57 reaches $80.
;
; 2. METADATA ($122C): Read 8 bytes from level data into:
; draw_offset/page (player start), bonus_walk_offset/page (exit walk),
; exit_offset/page, guard_spawn_offset/page.
;
; 3. GOLD ($125D): Read run of entries until $FF or byte >= $41.
; Each gold entry: col = byte - $20 → gold_offset[x], next byte → gold_page[x];
; increment gold_total, clear gold_active_flag[x].
;
; 4. GUARDS ($1280): Read run of entries until $FF.
; Each guard entry: col = byte - $40; find first free slot (guard_active=$FF)
; scanning x=4..0; place guard there (active=0, offset, page).
;
; 5. MOVEMENT GRID ($12B2): Call build_movement_grid.
;
; 6. AUTOTILE FIXUP PASSES ($12BD): Six scan passes over the full buffer
; performing contextual char substitutions to join tile edges correctly
; (ladder joins, floor edges, bottom-row variants, entity→brick cleanup).
; Each pass scans a57=$67..$7F, Y=$01..$20.
;
; 7. FINALISE ($13F8): draw_guards, draw_gold, RTS.
;
; Inputs: a74 (pointer to compressed level data)
; Outputs: Offscreen buffer populated; draw_*/exit_*/guard_spawn_* set;
; gold_offset/page/active/total loaded; guard slots populated
; Side Effects: a56/a57 clobbered; build_movement_grid called; gold_total reset
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$11E8a9 01unpack_level_datalda#$01; --- phase 1: tile render --- ; x-ref: $0751
$11EA85 56stazp_ptr_render_buf_lo; a56=$01/a57=$67: buffer start at $6701 (row 0, col 0)
$11ECa9 67lda#$67
$11EE85 57stazp_ptr_render_buf_hi
$11F0a0 00ldy#$00; Y = level data byte index; X = column (0..31)
$11F2a2 00b_11F2ldx#$00; x-ref: $122A
$11F4b1 74b_11F4lda(zp_ptr_level_data),y; high nibble (>>4) = left tile of pair → render at column X ; x-ref: $120B
$11F629 f0and#$f0
$11F84alsra
$11F94alsra
$11FA4alsra
$11FB4alsra
$11FC20 ff 13jsrrender_actor_sprite
$11FFe8inx; low nibble = right tile of pair → render at column X+1
$1200b1 74lda(zp_ptr_level_data),y
$120229 0fand#$0f
$120420 ff 13jsrrender_actor_sprite
$1207c8iny; advance to next byte; X+=2; loop until 32 tiles (X=$20)
$1208e8inx
$1209e0 20cpx#$20
$120Bd0 e7bneb_11F4
$120D88dey; re-read last row byte; bit 7: 0=2-page row, 1=1-page row
$120Eb1 74lda(zp_ptr_level_data),y
$1210c8iny
$121129 80and#$80
$1213d0 0abneb_121F
$1215a5 57ldazp_ptr_render_buf_hi; normal row: advance 2 buffer pages
$121718clc
$121869 02adc#$02
$121A85 57stazp_ptr_render_buf_hi
$121C4c 26 12jmpj_1226
$121Fa5 57b_121Fldazp_ptr_render_buf_hi; thin row: advance 1 buffer page ; x-ref: $1213
$122118clc
$122269 01adc#$01
$122485 57stazp_ptr_render_buf_hi
$1226a5 57j_1226ldazp_ptr_render_buf_hi; stop when a57 reaches $80 (past last screen row) ; x-ref: $121C
$1228c9 80cmp#$80
$122A90 c6bccb_11F2
$122Cb1 74lda(zp_ptr_level_data),y; --- phase 2: level metadata ---
$122E8d b4 0dstadraw_offset; player start column
$1231c8iny
$1232b1 74lda(zp_ptr_level_data),y; player start row
$12348d b5 0dstadraw_page
$1237c8iny
$1238b1 74lda(zp_ptr_level_data),y; exit walk start column+1
$123A8d e7 2astabonus_walk_offset
$123Dc8iny
$123Eb1 74lda(zp_ptr_level_data),y; exit walk start row page
$12408d e8 2astabonus_walk_page
$1243c8iny
$1244b1 74lda(zp_ptr_level_data),y; level exit tile column
$12468d e2 2astaexit_offset
$1249c8iny
$124Ab1 74lda(zp_ptr_level_data),y; level exit tile row
$124C8d e3 2astaexit_page
$124Fc8iny
$1250b1 74lda(zp_ptr_level_data),y; guard respawn column
$12528d e4 2astaguard_spawn_offset
$1255c8iny
$1256b1 74lda(zp_ptr_level_data),y; guard respawn row
$12588d e5 2astaguard_spawn_page
$125Ba2 00ldx#$00; --- phase 3: gold positions (byte < $41) ---
$125Dc8j_125Diny; x-ref: $127D
$125Eb1 74lda(zp_ptr_level_data),y
$1260c5 ffcmpzp_kernal_ff_sentinel; $FF = end of entity list
$1262f0 4ebeqb_12B2
$1264c9 41cmp#$41; byte >= $41 → guard entry
$1266b0 18bcsb_1280
$126838sec; gold column = byte - $20
$1269e9 20sbc#$20
$126B9d 01 25stagold_offset,x
$126Eee e0 24incgold_total; one more gold piece on this level
$1271a9 00lda#$00; mark gold as uncollected
$12739d e1 24stagold_active_flag,x
$1276c8iny
$1277b1 74lda(zp_ptr_level_data),y; next byte = gold row page
$12799d f1 24stagold_page,x
$127Ce8inx; --- phase 4: guard positions (byte $41–$FE) ---
$127D4c 5d 12jmpj_125D
$1280b1 74b_1280lda(zp_ptr_level_data),y; guard column = byte - $40; push to stack while scanning for free slot ; x-ref: $1266, $12A5, $12AF
$1282c9 ffcmp#$ff
$1284f0 2cbeqb_12B2
$128638sec
$1287e9 40sbc#$40
$128948pha
$128Aa2 04ldx#$04; scan slots 4..0 for a free entry ($FF = free)
$128Cbd f9 21b_128Cldaguard_active,x; x-ref: $12A9
$128Fc9 ffcmp#$ff
$1291d0 15bneb_12A8
$1293a9 00lda#$00; slot taken → try next
$12959d f9 21staguard_active,x
$129868pla
$12999d 03 22staguard_offset,x
$129Cc8iny; next byte = guard row page
$129Db1 74lda(zp_ptr_level_data),y
$129F9d 08 22staguard_page,x
$12A2c8iny
$12A3f0 0dbeqb_12B2
$12A54c 80 12jmpb_1280
$12A8cab_12A8dex; x-ref: $1291
$12A910 e1bplb_128C
$12AB68pla
$12ACc8iny
$12ADf0 03beqb_12B2
$12AF4c 80 12jmpb_1280
$12B220 8c 1ab_12B2jsrbuild_movement_grid; --- phase 5: build movement grid, then autotile fixup --- ; x-ref: $1262, $1284, $12A3, $12AD
$12B5a9 00lda#$00
$12B785 56stazp_ptr_render_buf_lo; --- fixup pass 1: ladder left/right edge joins (scan all rows, Y $01..$20) ---
$12B9a9 67lda#$67
$12BB85 57stazp_ptr_render_buf_hi
$12BDa0 01b_12BDldy#$01; x-ref: $1309
$12BFb1 56b_12BFlda(zp_ptr_render_buf_lo),y; found ladder char ($43): fix its left and right neighbours ; x-ref: $1301
$12C1c9 43cmp#$43
$12C3d0 39bneb_12FE
$12C588dey; left neighbour (Y-1): $20→$67, $63→$50
$12C6b1 56lda(zp_ptr_render_buf_lo),y
$12C8c9 20cmp#$20
$12CAd0 07bneb_12D3
$12CCa9 67lda#$67
$12CE91 56sta(zp_ptr_render_buf_lo),y
$12D04c db 12jmpj_12DB
$12D3c9 63b_12D3cmp#$63; x-ref: $12CA
$12D5d0 04bnej_12DB
$12D7a9 50lda#$50
$12D991 56sta(zp_ptr_render_buf_lo),y
$12DBc8j_12DBiny; right neighbour (Y+1): $20→$65, $63→$4F, $CC→$E4 ; x-ref: $12D0, $12D5
$12DCc8iny
$12DDb1 56lda(zp_ptr_render_buf_lo),y
$12DFc9 20cmp#$20
$12E1d0 07bneb_12EA
$12E3a9 65lda#$65
$12E591 56sta(zp_ptr_render_buf_lo),y
$12E74c f5 12jmpj_12F5
$12EAc9 63b_12EAcmp#$63; x-ref: $12E1
$12ECd0 07bnej_12F5
$12EEa9 4flda#$4f
$12F091 56sta(zp_ptr_render_buf_lo),y
$12F24c fd 12jmpj_12FD
$12F5c9 ccj_12F5cmp#$cc; x-ref: $12E7, $12EC
$12F7d0 04bnej_12FD
$12F9a9 e4lda#$e4
$12FB91 56sta(zp_ptr_render_buf_lo),y
$12FD88j_12FDdey; x-ref: $12F2, $12F7
$12FEc8b_12FEiny; x-ref: $12C3
$12FFc0 21cpy#$21; advance to next row; stop at page $80
$1301d0 bcbneb_12BF
$1303e6 57inczp_ptr_render_buf_hi
$1305a5 57ldazp_ptr_render_buf_hi
$1307c9 80cmp#$80
$1309d0 b2bneb_12BD
$130Ba9 00lda#$00; --- fixup pass 2: bottom-row char substitution (page $7F only) ---
$130D85 56stazp_ptr_render_buf_lo
$130Fa9 7flda#$7f
$131185 57stazp_ptr_render_buf_hi
$1313a0 01ldy#$01
$1315b1 56b_1315lda(zp_ptr_render_buf_lo),y; $20→$64, $67→$7A, $65→$4C (bottom-row visual variants) ; x-ref: $1338
$1317c9 20cmp#$20
$1319d0 07bneb_1322
$131Ba9 64lda#$64
$131D91 56sta(zp_ptr_render_buf_lo),y
$131F4c 35 13jmpj_1335
$1322c9 67b_1322cmp#$67; x-ref: $1319
$1324d0 07bneb_132D
$1326a9 7alda#$7a
$132891 56sta(zp_ptr_render_buf_lo),y
$132A4c 35 13jmpj_1335
$132Dc9 65b_132Dcmp#$65; x-ref: $1324
$132Fd0 04bnej_1335
$1331a9 4clda#$4c
$133391 56sta(zp_ptr_render_buf_lo),y
$1335c8j_1335iny; x-ref: $131F, $132A, $132F
$1336c0 21cpy#$21
$1338d0 dbbneb_1315
$133Aa9 00lda#$00; --- fixup pass 3: entity top→brick (scan all rows, one col per row) ---
$133C85 56stazp_ptr_render_buf_lo
$133Ea9 67lda#$67
$134085 57stazp_ptr_render_buf_hi
$1342a0 01ldy#$01
$1344b1 56b_1344lda(zp_ptr_render_buf_lo),y; $CC (entity top) → $E4 (brick bottom char) ; x-ref: $1354
$1346c9 cccmp#$cc
$1348d0 04bneb_134E
$134Aa9 e4lda#$e4
$134C91 56sta(zp_ptr_render_buf_lo),y
$134Ee6 57b_134Einczp_ptr_render_buf_hi; x-ref: $1348
$1350a5 57ldazp_ptr_render_buf_hi
$1352c9 80cmp#$80
$1354d0 eebneb_1344
$1356a9 00lda#$00; --- fixup pass 4: space+entity→brick (if Y is space and Y+1 is $CC → $E4) ---
$135885 56stazp_ptr_render_buf_lo
$135Aa9 67lda#$67
$135C85 57stazp_ptr_render_buf_hi
$135Ea0 01b_135Eldy#$01; x-ref: $137D
$1360b1 56b_1360lda(zp_ptr_render_buf_lo),y; space at Y: check right neighbour (Y+1) ; x-ref: $1375
$1362c9 20cmp#$20
$1364d0 0cbneb_1372
$1366c8iny
$1367b1 56lda(zp_ptr_render_buf_lo),y; $CC immediately right of space → $E4
$1369c9 cccmp#$cc
$136Bd0 04bneb_1371
$136Da9 e4lda#$e4
$136F91 56sta(zp_ptr_render_buf_lo),y
$137188b_1371dey; x-ref: $136B
$1372c8b_1372iny; x-ref: $1364
$1373c0 21cpy#$21
$1375d0 e9bneb_1360
$1377e6 57inczp_ptr_render_buf_hi
$1379a5 57ldazp_ptr_render_buf_hi
$137Bc9 80cmp#$80
$137Dd0 dfbneb_135E
$137Fa9 00lda#$00; --- fixup pass 5: floor edge substitution (all rows) ---
$138185 56stazp_ptr_render_buf_lo
$1383a9 67lda#$67
$138585 57stazp_ptr_render_buf_hi
$1387a0 01ldy#$01
$1389b1 56b_1389lda(zp_ptr_render_buf_lo),y; $20→$63, $67→$50, $65→$4F ; x-ref: $13AC
$138Bc9 20cmp#$20
$138Dd0 07bneb_1396
$138Fa9 63lda#$63
$139191 56sta(zp_ptr_render_buf_lo),y
$13934c a9 13jmpj_13A9
$1396c9 67b_1396cmp#$67; x-ref: $138D
$1398d0 07bneb_13A1
$139Aa9 50lda#$50
$139C91 56sta(zp_ptr_render_buf_lo),y
$139E4c a9 13jmpj_13A9
$13A1c9 65b_13A1cmp#$65; x-ref: $1398
$13A3d0 04bnej_13A9
$13A5a9 4flda#$4f
$13A791 56sta(zp_ptr_render_buf_lo),y
$13A9c8j_13A9iny; x-ref: $1393, $139E, $13A3
$13AAc0 21cpy#$21
$13ACd0 dbbneb_1389
$13AEa9 00lda#$00; --- fixup pass 6: final char substitution (all rows) ---
$13B085 56stazp_ptr_render_buf_lo
$13B2a9 67lda#$67
$13B485 57stazp_ptr_render_buf_hi
$13B6a0 01ldy#$01
$13B8b1 56b_13B8lda(zp_ptr_render_buf_lo),y; $20→$65, $63→$4F, $64→$4C, $43→$4F ; x-ref: $13E0
$13BAc9 20cmp#$20
$13BCd0 04bneb_13C2
$13BEa9 65lda#$65
$13C091 56sta(zp_ptr_render_buf_lo),y
$13C2c9 63b_13C2cmp#$63; x-ref: $13BC
$13C4d0 04bneb_13CA
$13C6a9 4flda#$4f
$13C891 56sta(zp_ptr_render_buf_lo),y
$13CAc9 64b_13CAcmp#$64; x-ref: $13C4
$13CCd0 04bneb_13D2
$13CEa9 4clda#$4c
$13D091 56sta(zp_ptr_render_buf_lo),y
$13D2c9 43b_13D2cmp#$43; x-ref: $13CC
$13D4d0 04bneb_13DA
$13D6a9 4flda#$4f
$13D891 56sta(zp_ptr_render_buf_lo),y
$13DAe6 57b_13DAinczp_ptr_render_buf_hi; x-ref: $13D4
$13DCa5 57ldazp_ptr_render_buf_hi
$13DEc9 80cmp#$80
$13E0d0 d6bneb_13B8
$13E2a9 00lda#$00; --- finalise: fill col $21 with $65, then draw guards and gold ---
$13E485 56stazp_ptr_render_buf_lo
$13E6a9 67lda#$67
$13E885 57stazp_ptr_render_buf_hi
$13EAa0 21ldy#$21
$13ECa9 65b_13EClda#$65; x-ref: $13F6
$13EE91 56sta(zp_ptr_render_buf_lo),y; write $65 at col $21 (right sidebar separator) on every row
$13F0e6 57inczp_ptr_render_buf_hi
$13F2a5 57ldazp_ptr_render_buf_hi
$13F4c9 80cmp#$80
$13F6d0 f4bneb_13EC
$13F820 78 24jsrdraw_guards; draw all guard sprites into buffer
$13FB20 52 25jsrdraw_gold; draw all gold pieces into buffer
$13FE60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Renders one tile into the offscreen buffer row(s) at the current position.
; Called by unpack_level_data for each nibble of compressed level data.
;
; On entry:
; A = 4-bit tile code (0–15)
; X = column index (0–31); converted to Y internally for (a56),Y writes
; a56/a57 = ZP pointer to current screen row buffer (a57 = page)
;
; Bit 3 of the tile code selects rendering mode:
; 0 (codes $00–$07): 2-row tile — writes top char to current row,
; bottom char to row below (inc/dec a57)
; 1 (codes $08–$0F): 1-row tile — writes single char to current row only
;
; 2-row tile codes (low nibble, bit 3 clear):
; $00 → empty (no write)
; $01 → $CC top / $DD bottom (entity sprite)
; $02 → $43 top / $43 bottom (ladder)
; $03 → $20 top / $63 bottom (open top / floor bottom)
; $04 → $A0 top / $E4 bottom (brick / solid tile)
;
; 1-row tile codes (bit 3 set, low 3 bits):
; $00 → empty (no write)
; $01 → $DB (rope)
; $02 → $43 (ladder half)
; $03 → $63 (floor half)
; $04 → $A0 (brick half)
;
; Inputs: A (tile code), X (column), a56/a57 (row buffer pointer)
; Outputs: 1 or 2 chars written to offscreen buffer
; Side Effects: a57 temporarily inc/dec'd for 2-row tiles; Y preserved
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$13FF85 64render_actor_spritestazp_scratch_reg_a; save tile code; preserve Y (= level data index in caller) ; x-ref: $11FC, $1204 SGNFLG Pointer: Series Evaluation Constant Pointer
$140198tya
$140248pha
$14038atxa; X (column index) → Y for (a56),Y screen writes
$1404a8tay
$1405a5 64ldazp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer
$140729 08and#$08; bit 3 set → 1-row tile path
$1409d0 50bneb_145B
$140Ba5 64ldazp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer
$140Dc9 00cmp#$00; --- 2-row tiles (bit 3 clear) ---
$140Fd0 03bneb_1414
$14114c 8c 14jmpj_148C
$1414c9 01b_1414cmp#$01; $01 → entity sprite: $CC top row, $DD bottom row ; x-ref: $140F
$1416d0 0fbneb_1427
$1418a9 cclda#$cc; write top char to current row
$141A91 56sta(zp_ptr_render_buf_lo),y
$141Ce6 57inczp_ptr_render_buf_hi; inc a57 → next page (row below); write bottom char; dec a57 → restore
$141Ea9 ddlda#$dd
$142091 56sta(zp_ptr_render_buf_lo),y
$1422c6 57deczp_ptr_render_buf_hi
$14244c 8c 14jmpj_148C
$1427c9 02b_1427cmp#$02; $02 → ladder: $43 both rows ; x-ref: $1416
$1429d0 0dbneb_1438
$142Ba9 43lda#$43
$142D91 56sta(zp_ptr_render_buf_lo),y
$142Fe6 57inczp_ptr_render_buf_hi
$143191 56sta(zp_ptr_render_buf_lo),y
$1433c6 57deczp_ptr_render_buf_hi
$14354c 8c 14jmpj_148C
$1438c9 03b_1438cmp#$03; $03 → open top / floor bottom: $20 top row, $63 bottom row ; x-ref: $1429
$143Ad0 0cbneb_1448
$143Ca9 20lda#$20
$143E91 56sta(zp_ptr_render_buf_lo),y
$1440e6 57inczp_ptr_render_buf_hi
$1442a9 63lda#$63
$144491 56sta(zp_ptr_render_buf_lo),y
$1446c6 57deczp_ptr_render_buf_hi
$1448c9 04b_1448cmp#$04; $04 → brick tile: $A0 top row, $E4 bottom row ; x-ref: $143A
$144Ad0 0cbneb_1458
$144Ca9 a0lda#$a0
$144E91 56sta(zp_ptr_render_buf_lo),y
$1450e6 57inczp_ptr_render_buf_hi
$1452a9 e4lda#$e4
$145491 56sta(zp_ptr_render_buf_lo),y
$1456c6 57deczp_ptr_render_buf_hi
$14584c 8c 14b_1458jmpj_148C; x-ref: $144A
$145Ba5 64b_145Bldazp_scratch_reg_a; --- 1-row tiles (bit 3 set): isolate low 3 bits for tile type --- ; x-ref: $1409 SGNFLG Pointer: Series Evaluation Constant Pointer
$145D29 07and#$07
$145Fc9 00cmp#$00
$1461d0 03bneb_1466; $08 ($00 after mask) → empty: no write
$14634c 8c 14jmpj_148C
$1466c9 01b_1466cmp#$01; $09 ($01 after mask) → rope: write $DB ; x-ref: $1461
$1468d0 07bneb_1471
$146Aa9 dblda#$db
$146C91 56sta(zp_ptr_render_buf_lo),y
$146E4c 8c 14jmpj_148C
$1471c9 02b_1471cmp#$02; $0A ($02 after mask) → ladder half: write $43 ; x-ref: $1468
$1473d0 07bneb_147C
$1475a9 43lda#$43
$147791 56sta(zp_ptr_render_buf_lo),y
$14794c 8c 14jmpj_148C
$147Cc9 03b_147Ccmp#$03; $0B ($03 after mask) → floor half: write $63 ; x-ref: $1473
$147Ed0 04bneb_1484
$1480a9 63lda#$63
$148291 56sta(zp_ptr_render_buf_lo),y
$1484c9 04b_1484cmp#$04; $0C ($04 after mask) → brick half: write $A0 ; x-ref: $147E
$1486d0 04bnej_148C
$1488a9 a0lda#$a0
$148A91 56sta(zp_ptr_render_buf_lo),y
$148C68j_148Cpla; restore Y (level data index) and return ; x-ref: $1411, $1424, $1435, $1458, $1463, $146E, $1479, $1486
$148Da8tay
$148E60rts
.encode
.enc"screen"
$148Ftxt_lode_runner_pet.text"LODE RUNNER ON COMMODORE PET", $ff; x-ref: $1693
.endencode
$14ACtxt_horizontal_bar.fill28, $63; x-ref: $16A6
$14C8.byte$ff
.encode
.enc"screen"
txt_jim_orlando_gmail
$14C9.text"JIM.ORLANDO@GMAIL.COM", $ff; x-ref: $16C1
$14DFtxt_40_columns.text"40 COLUMNS", $ff; x-ref: $16DC
txt_select_move_right
$14EA.text"SELECT KEY TO MOVE RIGHT (EG: L)", $ff; x-ref: $1702
.endencode
$150Bf_150B.fill40, $fa; x-ref: $1715
$1533.byte$ff
$1534f_1534.fill40, $c2; x-ref: $1728
$155C.byte$ff
.encode
.enc"screen"
$155Dtxt_select_move_left.text" SELECT KEY TO MOVE LEFT (EG: J)", $ff; x-ref: $17AD
$157Etxt_select_move_up.text"SELECT KEY TO MOVE UP (EG: I) ", $ff; x-ref: $181C
$159Ftxt_select_move_down.text" SELECT KEY TO MOVE DOWN (EG: K)", $ff; x-ref: $188D
$15C0txt_select_dig_right.text"SELECT KEY TO DIG RIGHT (EG: F) ", $ff; x-ref: $1905
$15E1txt_select_dig_left.text" SELECT KEY TO DIG LEFT (EG: D) ", $ff; x-ref: $1958
$1602txt_select_pause.text"SELECT KEY TO PAUSE GAME (EG: Q)", $ff; x-ref: $19BC
.endencode
$1623irq_counter.byte$00; x-ref: $1650, $166C, $1673
$1624title_state.byte$00; x-ref: $1655, $1679, $167E, $168C, $16BA, $16D5, $16FB, $175A, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Performs system initialization and environment detection. Sets up variables,
; configures IRQ vector to $0632, and detects screen width (40 or 80 cols).
;
; Inputs: None (reads screen RAM at $8028)
; Outputs: $51 (screen width), $28..$40 filled with $FF, $1623/$1624 set to $0010
; Side Effects: Calls s1E9D, s1C0D, s0843. Sets IRQ vector at $90/$91.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1625a9 2bdetect_environmentlda#$2b; x-ref: $060E
$162785 5astazp_ptr_playfield_lo
$1629a9 00lda#$00
$162B85 52stazp_ptr_screen_lo
$162D85 54stazp_ptr_attr_lo; TEMPF1 Temporary storage for FLPT value.
$162Fa9 00lda#$00
$163185 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$163320 9d 1ejsrinit_dig_slots
$1636a9 32lda#<menu_irq
$163885 90stazp_irq_vector_lo; CINV Vector: Hardware Interrupt
$163Aa9 06lda#>menu_irq
$163C85 91stazp_irq_vector_hi
$163E20 0d 1cjsrclear_playfield_buffer
$1641a2 00ldx#$00
$1643a9 fflda#$ff
$164595 31b_1645stazp_key_col_down,x; x-ref: $164C
$164795 28stazp_key_row_down,x; TXTTAB Pointer: Start of BASIC Text
$1649e8inx
$164Ae0 10cpx#$10
$164Cd0 f7bneb_1645
$164Ea9 10lda#$10
$16508d 23 16stairq_counter
$1653a9 00lda#$00
$16558d 24 16statitle_state
$1658ad 28 80ldaSCREEN_RAM_R1C0
$165Bc9 2acmp#$2a
$165Df0 05beqb_1664
$165Fa9 50lda#80; 40 columns
$16614c 66 16jmpj_1666
$1664a9 28b_1664lda#40; 80 columns ; x-ref: $165D
$166685 51j_1666stazp_screen_width; x-ref: $1661
$166820 43 08jsrpatch_80_column_compatibility
$166B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Title screen animation and input state machine (executed during interrupts).
; States 1-4: Progressively draw the title text and labels (Lode Runner, etc).
; State 5: Wait for keyboard input to start the game.
; State 6+: Play title screen transitions / animations.
;
; Inputs: title_state ($1624), keyboard input
; Outputs: Populates video memory/buffer, advances title_state.
; Side Effects: Calls clear_physical_screen, clear_gameplay_grid, process_actors.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
title_screen_state_machine
$166Cce 23 16decirq_counter; x-ref: $0635
$166Fd0 16bner_1687
$1671a9 01lda#$01
$16738d 23 16stairq_counter
$167620 b3 1ejsrprocess_dig_animations
$1679ad 24 16ldatitle_state
$167Cd0 0abneb_1688
$167Eee 24 16inctitle_state
$168120 80 07jsrclear_physical_screen
$168420 9d 07jsrclear_gameplay_grid
$168760r_1687rts; x-ref: $166F
$1688c9 01b_1688cmp#$01; x-ref: $167C
$168Ad0 2abneb_16B6
$168Cee 24 16inctitle_state
$168Fa2 00ldx#$00
$1691a0 07ldy#$07
$1693bd 8f 14j_1693ldatxt_lode_runner_pet,x; x-ref: $169F
$1696c9 ffcmp#$ff
$1698f0 08beqb_16A2
$169A99 00 67staplayfield_row_buffer,y
$169Dc8iny
$169Ee8inx
$169F4c 93 16jmpj_1693
$16A2a2 00b_16A2ldx#$00; x-ref: $1698
$16A4a0 07ldy#$07
$16A6bd ac 14j_16A6ldatxt_horizontal_bar,x; x-ref: $16B2
$16A9c9 ffcmp#$ff
$16ABf0 08beqr_16B5
$16AD99 00 68staplayfield_row_1_page_base,y; Playfield Row 1 page base
$16B0c8iny
$16B1e8inx
$16B24c a6 16jmpj_16A6
$16B560r_16B5rts; x-ref: $16AB, $16EF
$16B6c9 02b_16B6cmp#$02; x-ref: $168A
$16B8d0 17bneb_16D1
$16BAee 24 16inctitle_state
$16BDa2 00ldx#$00
$16BFa0 0aldy#$0a
$16C1bd c9 14j_16C1ldatxt_jim_orlando_gmail,x; x-ref: $16CD
$16C4c9 ffcmp#$ff
$16C6f0 08beqr_16D0
$16C899 00 6astaplayfield_row_3_page_base,y; Playfield Row 3 page base
$16CBc8iny
$16CCe8inx
$16CD4c c1 16jmpj_16C1
$16D060r_16D0rts; x-ref: $16C6
$16D1c9 03b_16D1cmp#$03; x-ref: $16B8
$16D3d0 22bneb_16F7
$16D5ee 24 16inctitle_state
$16D8a2 00ldx#$00
$16DAa0 10ldy#$10
$16DCbd df 14j_16DCldatxt_40_columns,x; x-ref: $16E8
$16DFc9 ffcmp#$ff
$16E1f0 08beqb_16EB
$16E399 00 6dstaplayfield_row_6_page_base,y; Playfield Row 6 page base
$16E6c8iny
$16E7e8inx
$16E84c dc 16jmpj_16DC
$16EBa5 51b_16EBldazp_screen_width; x-ref: $16E1
$16EDc9 28cmp#$28
$16EFf0 c4beqr_16B5
$16F1a9 38lda#$38
$16F38d 10 6dstaplayfield_row_6_col_16; Set columns digit to '8' for 80-column mode
$16F660rts
$16F7c9 04b_16F7cmp#$04; x-ref: $16D3
$16F9d0 56bneb_1751
$16FBee 24 16inctitle_state
$16FEa2 00ldx#$00
$1700a0 02ldy#$02
$1702bd ea 14j_1702ldatxt_select_move_right,x; x-ref: $170E
$1705c9 ffcmp#$ff
$1707f0 08beqb_1711
$170999 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$170Cc8iny
$170De8inx
$170E4c 02 17jmpj_1702
$1711a2 00b_1711ldx#$00; x-ref: $1707
$1713a0 01ldy#$01
$1715bd 0b 15j_1715ldaf_150B,x; x-ref: $1721
$1718c9 ffcmp#$ff
$171Af0 08beqb_1724
$171C99 00 7dstaplayfield_row_22_page_base,y; Playfield Row 22 page base
$171Fc8iny
$1720e8inx
$17214c 15 17jmpj_1715
$1724a2 00b_1724ldx#$00; x-ref: $171A
$1726a0 01ldy#$01
$1728bd 34 15j_1728ldaf_1534,x; x-ref: $1734
$172Bc9 ffcmp#$ff
$172Df0 08beqb_1737
$172F99 00 7estaplayfield_row_23_page_base,y; Playfield Row 23 page base
$1732c8iny
$1733e8inx
$17344c 28 17jmpj_1728
$1737a9 08b_1737lda#$08; x-ref: $172D
$17398d b3 0dstaanim_frame
$173Ca9 09lda#$09
$173E8d b4 0dstadraw_offset
$1741a9 00lda#$00
$17438d b6 0dstaanim_strip_base
$1746a9 7blda#$7b
$17488d b5 0dstadraw_page
$174Ba9 00lda#$00
$174D8d b1 0dstaplayer_state
$175060rts
$1751c9 05b_1751cmp#$05; x-ref: $16F9
$1753d0 13bneb_1768
$175520 1b 1ajsrmap_new_key
$1758d0 0dbner_1767
$175Aee 24 16inctitle_state
$175Da9 01lda#$01
$175F8d b8 0dstainput_dy
$1762a9 00lda#$00
$17648d b6 0dstaanim_strip_base
$176760r_1767rts; x-ref: $1758
$1768c9 06b_1768cmp#$06; x-ref: $1753
$176Ad0 1fbneb_178B
$176Cad b4 0dldadraw_offset
$176Fc9 18cmp#$18
$177190 17bccr_178A
$1773a9 18lda#$18
$17758d b4 0dstadraw_offset
$1778a9 00lda#$00
$177A8d b6 0dstaanim_strip_base
$177Da9 04lda#$04
$177F8d b3 0dstaanim_frame
$1782a9 00lda#$00
$17848d b8 0dstainput_dy
$1787ee 24 16inctitle_state
$178A60r_178Arts; x-ref: $1771
$178Bc9 07b_178Bcmp#$07; x-ref: $176A
$178Dd0 13bneb_17A2
$178F20 65 1ajsrwait_any_key
$1792f0 0dbeqr_17A1
$1794ee 24 16inctitle_state
$1797a9 00lda#$00
$17998d b3 0dstaanim_frame
$179Ca9 10lda#$10
$179E8d b6 0dstaanim_strip_base
$17A160r_17A1rts; x-ref: $1792
$17A2c9 08b_17A2cmp#$08; x-ref: $178D
$17A4d0 17bneb_17BD
$17A6ee 24 16inctitle_state
$17A9a2 00ldx#$00
$17ABa0 02ldy#$02
$17ADbd 5d 15j_17ADldatxt_select_move_left,x; x-ref: $17B9
$17B0c9 ffcmp#$ff
$17B2f0 08beqr_17BC
$17B499 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$17B7c8iny
$17B8e8inx
$17B94c ad 17jmpj_17AD
$17BC60r_17BCrts; x-ref: $17B2
$17BDc9 09b_17BDcmp#$09; x-ref: $17A4
$17BFd0 13bneb_17D4
$17C120 1b 1ajsrmap_new_key
$17C4d0 0dbner_17D3
$17C6ee 24 16inctitle_state
$17C9a9 10lda#$10
$17CB8d b6 0dstaanim_strip_base
$17CEa9 fflda#$ff
$17D08d b8 0dstainput_dy
$17D360r_17D3rts; x-ref: $17C4
$17D4c9 0ab_17D4cmp#$0a; x-ref: $17BF
$17D6d0 1abneb_17F2
$17D8ad b4 0dldadraw_offset
$17DBc9 08cmp#$08
$17DDb0 12bcsr_17F1
$17DFa9 07lda#$07
$17E18d b4 0dstadraw_offset
$17E4a9 04lda#$04
$17E68d b3 0dstaanim_frame
$17E9a9 00lda#$00
$17EB8d b8 0dstainput_dy
$17EEee 24 16inctitle_state
$17F160r_17F1rts; x-ref: $17DD
$17F2c9 0bb_17F2cmp#$0b; x-ref: $17D6
$17F4d0 1bbneb_1811
$17F620 65 1ajsrwait_any_key
$17F9f0 15beqr_1810
$17FBee 24 16inctitle_state
$17FEa9 08lda#$08
$18008d b3 0dstaanim_frame
$1803a9 10lda#$10
$18058d b6 0dstaanim_strip_base
$1808a9 01lda#$01
$180A85 56stazp_ptr_render_buf_lo
$180Ca9 7clda#$7c
$180E85 57stazp_ptr_render_buf_hi
$181060r_1810rts; x-ref: $17F9
$1811c9 0cb_1811cmp#$0c; x-ref: $17F4
$1813d0 30bneb_1845
$1815ee 24 16inctitle_state
$1818a2 00ldx#$00
$181Aa0 02ldy#$02
$181Cbd 7e 15j_181Cldatxt_select_move_up,x; x-ref: $1828
$181Fc9 ffcmp#$ff
$1821f0 08beqb_182B
$182399 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$1826c8iny
$1827e8inx
$18284c 1c 18jmpj_181C
$182Ba2 07b_182Bldx#$07; x-ref: $1821
$182Da0 05b_182Dldy#$05; x-ref: $1842
$182Fa9 67lda#$67
$183191 56sta(zp_ptr_render_buf_lo),y
$1833c8iny
$1834a9 40lda#$40
$183691 56sta(zp_ptr_render_buf_lo),y
$1838c8iny
$1839a9 65lda#$65
$183B91 56sta(zp_ptr_render_buf_lo),y
$183D88dey
$183E88dey
$183Fc6 57deczp_ptr_render_buf_hi
$1841cadex
$184210 e9bplb_182D
$184460rts
$1845c9 0db_1845cmp#$0d; x-ref: $1813
$1847d0 13bneb_185C
$184920 1b 1ajsrmap_new_key
$184Cd0 0dbner_185B
$184Eee 24 16inctitle_state
$1851a9 fflda#$ff
$18538d b7 0dstainput_dx
$1856a9 20lda#$20
$18588d b6 0dstaanim_strip_base
$185B60r_185Brts; x-ref: $184C
$185Cc9 0eb_185Ccmp#$0e; x-ref: $1847
$185Ed0 10bneb_1870
$1860ad b5 0dldadraw_page
$1863c9 74cmp#$74
$1865b0 08bcsr_186F
$1867a9 00lda#$00
$18698d b7 0dstainput_dx
$186Cee 24 16inctitle_state
$186F60r_186Frts; x-ref: $1865
$1870c9 0fb_1870cmp#$0f; x-ref: $185E
$1872d0 0ebneb_1882
$187420 65 1ajsrwait_any_key
$1877f0 08beqr_1881
$1879a9 04lda#$04
$187B8d b3 0dstaanim_frame
$187Eee 24 16inctitle_state
$188160r_1881rts; x-ref: $1877
$1882c9 10b_1882cmp#$10; x-ref: $1872
$1884d0 17bneb_189D
$1886ee 24 16inctitle_state
$1889a2 00ldx#$00
$188Ba0 02ldy#$02
$188Dbd 9f 15j_188Dldatxt_select_move_down,x; x-ref: $1899
$1890c9 ffcmp#$ff
$1892f0 08beqr_189C
$189499 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$1897c8iny
$1898e8inx
$18994c 8d 18jmpj_188D
$189C60r_189Crts; x-ref: $1892
$189Dc9 11b_189Dcmp#$11; x-ref: $1884
$189Fd0 13bneb_18B4
$18A120 1b 1ajsrmap_new_key
$18A4d0 0dbner_18B3
$18A6ee 24 16inctitle_state
$18A9a9 01lda#$01
$18AB8d b7 0dstainput_dx
$18AEa9 20lda#$20
$18B08d b6 0dstaanim_strip_base
$18B360r_18B3rts; x-ref: $18A4
$18B4c9 12b_18B4cmp#$12; x-ref: $189F
$18B6d0 10bneb_18C8
$18B8ad b5 0dldadraw_page
$18BBc9 7bcmp#$7b
$18BD90 08bccr_18C7
$18BFa9 00lda#$00
$18C18d b7 0dstainput_dx
$18C4ee 24 16inctitle_state
$18C760r_18C7rts; x-ref: $18BD
$18C8c9 13b_18C8cmp#$13; x-ref: $18B6
$18CAd0 29bneb_18F5
$18CC20 65 1ajsrwait_any_key
$18CFf0 23beqr_18F4
$18D120 3c 11jsrrestore_sprite_bg
$18D4a9 01lda#$01
$18D685 56stazp_ptr_render_buf_lo
$18D8a9 7clda#$7c
$18DA85 57stazp_ptr_render_buf_hi
$18DCa2 07ldx#$07
$18DEa0 05b_18DEldy#$05; x-ref: $18EF
$18E0a9 20lda#$20
$18E291 56sta(zp_ptr_render_buf_lo),y
$18E4c8iny
$18E591 56sta(zp_ptr_render_buf_lo),y
$18E7c8iny
$18E891 56sta(zp_ptr_render_buf_lo),y
$18EA88dey
$18EB88dey
$18ECc6 57deczp_ptr_render_buf_hi
$18EEcadex
$18EF10 edbplb_18DE
$18F1ee 24 16inctitle_state
$18F460r_18F4rts; x-ref: $18CF
$18F5c9 14b_18F5cmp#$14; x-ref: $18CA
$18F7d0 04bneb_18FD
$18F9ee 24 16inctitle_state
$18FC60rts
$18FDc9 15b_18FDcmp#$15; x-ref: $18F7
$18FFd0 17bneb_1918
$1901a2 00ldx#$00
$1903a0 02ldy#$02
$1905bd c0 15j_1905ldatxt_select_dig_right,x; x-ref: $1911
$1908c9 ffcmp#$ff
$190Af0 08beqb_1914
$190C99 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$190Fc8iny
$1910e8inx
$19114c 05 19jmpj_1905
$1914ee 24 16b_1914inctitle_state; x-ref: $190A
$191760rts
$1918c9 16b_1918cmp#$16; x-ref: $18FF
$191Ad0 27bneb_1943
$191C20 1b 1ajsrmap_new_key
$191Fd0 21bner_1942
$1921a9 00lda#$00
$19238d 43 1estadig_slot_status
$1926a9 7dlda#$7d
$19288d 4d 1estadig_slot_page
$192Ba9 08lda#$08
$192D8d 57 1estadig_slot_col
$1930a9 00lda#$00
$19328d b3 0dstaanim_frame
$1935a9 01lda#$01
$19378d b1 0dstaplayer_state
$193Aa9 70lda#$70
$193C8d b6 0dstaanim_strip_base
$193Fee 24 16inctitle_state
$194260r_1942rts; x-ref: $191F
$1943c9 17b_1943cmp#$17; x-ref: $191A
$1945d0 09bneb_1950
$194720 65 1ajsrwait_any_key
$194Af0 03beqr_194F
$194Cee 24 16inctitle_state
$194F60r_194Frts; x-ref: $194A
$1950c9 18b_1950cmp#$18; x-ref: $1945
$1952d0 17bneb_196B
$1954a2 00ldx#$00
$1956a0 02ldy#$02
$1958bd e1 15j_1958ldatxt_select_dig_left,x; x-ref: $1964
$195Bc9 ffcmp#$ff
$195Df0 08beqb_1967
$195F99 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$1962c8iny
$1963e8inx
$19644c 58 19jmpj_1958
$1967ee 24 16b_1967inctitle_state; x-ref: $195D
$196A60rts
$196Bc9 19b_196Bcmp#$19; x-ref: $1952
$196Dd0 31bneb_19A0
$196F20 1b 1ajsrmap_new_key
$1972d0 2bbner_199F
$1974a9 00lda#$00
$19768d 44 1estakbind_anim_step; overlaps dig_slot_status[1]; reused as wizard animation step counter (0..2) during key binding
$1979a9 7dlda#$7d
$197B8d 4e 1estakbind_sprite_page; overlaps dig_slot_page[1]; reused as player sprite page ($7D) during key binding
$197Ea9 06lda#$06
$19808d 58 1estakbind_sprite_col; overlaps dig_slot_col[1]; reused as player sprite column ($06) during key binding
$1983a9 00lda#$00
$19858d b3 0dstaanim_frame
$1988a9 01lda#$01
$198A8d b1 0dstaplayer_state
$198Da9 80lda#$80
$198F8d b6 0dstaanim_strip_base
$1992a9 01lda#$01
$19948d 6b 1estadig_slot_delay_inner
$1997a9 00lda#$00
$19998d 75 1estadig_slot_delay_outer
$199Cee 24 16inctitle_state
$199F60r_199Frts; x-ref: $1972
$19A0c9 1ab_19A0cmp#$1a; x-ref: $196D
$19A2d0 10bneb_19B4
$19A420 65 1ajsrwait_any_key
$19A7f0 0abeqr_19B3
$19A9ad 44 1eldakbind_anim_step; overlaps dig_slot_status[1]; reused as wizard animation step counter (0..2) during key binding
$19ACc9 02cmp#$02
$19AEd0 03bner_19B3
$19B0ee 24 16inctitle_state
$19B360r_19B3rts; x-ref: $19A7, $19AE
$19B4c9 1bb_19B4cmp#$1b; x-ref: $19A2
$19B6d0 21bneb_19D9
$19B8a2 00ldx#$00
$19BAa0 02ldy#$02
$19BCbd 02 16j_19BCldatxt_select_pause,x; x-ref: $19C8
$19BFc9 ffcmp#$ff
$19C1f0 08beqb_19CB
$19C399 00 71staplayfield_row_10_page_base,y; Playfield Row 10 page base
$19C6c8iny
$19C7e8inx
$19C84c bc 19jmpj_19BC
$19CBa9 01b_19CBlda#$01; x-ref: $19C1
$19CD8d b8 0dstainput_dy
$19D0a9 00lda#$00
$19D28d b6 0dstaanim_strip_base
$19D5ee 24 16inctitle_state
$19D860rts
$19D9c9 1cb_19D9cmp#$1c; x-ref: $19B6
$19DBd0 26bneb_1A03
$19DDad b4 0dldadraw_offset
$19E0c9 20cmp#$20
$19E2f0 0cbeqb_19F0
$19E4c9 07cmp#$07
$19E6d0 0dbneb_19F5
$19E8a9 01lda#$01
$19EA8d b8 0dstainput_dy
$19ED4c f5 19jmpb_19F5
$19F0a9 ffb_19F0lda#$ff; x-ref: $19E2
$19F28d b8 0dstainput_dy
$19F520 1b 1ab_19F5jsrmap_new_key; x-ref: $19E6, $19ED
$19F8d0 08bner_1A02
$19FAa9 00lda#$00
$19FC8d b8 0dstainput_dy
$19FFee 24 16inctitle_state
$1A0260r_1A02rts; x-ref: $19F8
$1A03c9 30b_1A03cmp#$30; x-ref: $19DB
$1A05f0 04beqb_1A0B
$1A07ee 24 16inctitle_state
$1A0A60rts
$1A0B20 7c 1ab_1A0Bjsrfinalize_key_bindings; x-ref: $1A05
$1A0E20 3c 11jsrrestore_sprite_bg
$1A11a9 fflda#$ff
$1A138d b1 0dstaplayer_state
$1A1620 bf 30jsrswitch_to_title_screen
$1A1960rts
$1A1A60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans the PET keyboard for any newly pressed key to bind as a control.
; Iterates rows 0-9 via PIA 1. If it sees a pressed key, it checks if it's already
; mapped in the key arrays ($28-$36 ZP region). If not, it binds it to the first free slot.
;
; Inputs: PIA 1 ($E810/E812)
; Outputs: A=0 if a new key was mapped, A=$FF if no new keys. Modifies ZP key config arrays.
; Side Effects: Modifies PIA hardware control registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1A1Ba2 00map_new_keyldx#$00; x-ref: $1755, $17C1, $1849, $18A1, $191C, $196F, $19F5
$1A1D8e 10 e8b_1A1Dstx$e810; x-ref: $1A2A PORT A or DDR A: Data Direction Register A
$1A20ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$1A23c9 ffcmp#$ff
$1A25d0 08bneb_1A2F
$1A27e8inx
$1A28e0 0acpx#$0a
$1A2Ad0 f1bneb_1A1D
$1A2Ca9 fflda#$ff
$1A2E60rts
$1A2Fa0 00b_1A2Fldy#$00; x-ref: $1A25
$1A31d9 31 00b_1A31cmp@w zp_key_col_down,y; x-ref: $1A45
$1A34d0 0cbneb_1A42
$1A3648pha
$1A378atxa
$1A38d9 28 00cmp@w zp_key_row_down,y; TXTTAB Pointer: Start of BASIC Text
$1A3Bd0 04bneb_1A41
$1A3D68pla
$1A3Ea9 fflda#$ff
$1A4060rts
$1A4168b_1A41pla; x-ref: $1A3B
$1A42c8b_1A42iny; x-ref: $1A34
$1A43c0 08cpy#$08
$1A45d0 eabneb_1A31
$1A4748pha
$1A48a0 00ldy#$00
$1A4Ab9 31 00b_1A4Alda@w zp_key_col_down,y; x-ref: $1A54
$1A4Dc9 ffcmp#$ff
$1A4Ff0 09beqb_1A5A
$1A51c8iny
$1A52c0 08cpy#$08
$1A54d0 f4bneb_1A4A
$1A5668pla
$1A57a9 fflda#$ff
$1A5960rts
$1A5A68b_1A5Apla; x-ref: $1A4F
$1A5B99 31 00sta@w zp_key_col_down,y
$1A5E8atxa
$1A5F99 28 00sta@w zp_key_row_down,y; TXTTAB Pointer: Start of BASIC Text
$1A62a9 00lda#$00
$1A6460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Simple blocking or polling loop that waits for ANY key on the keyboard.
; Scans all 10 rows on the PIA matrix.
;
; Inputs: None.
; Outputs: Returns Z flag based on whether a key is pressed.
; Side Effects: Modifies PIA hardware control registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1A65a2 00wait_any_keyldx#$00; x-ref: $178F, $17F6, $1874, $18CC, $1947, $19A4, $2FAD, $3173, ...
$1A678e 10 e8b_1A67stx$e810; x-ref: $1A74 PORT A or DDR A: Data Direction Register A
$1A6Aad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$1A6Dc9 ffcmp#$ff
$1A6Fd0 08bneb_1A79
$1A71e8inx
$1A72e0 0acpx#$0a
$1A74d0 f1bneb_1A67
$1A76a9 fflda#$ff
$1A7860rts
$1A79a9 00b_1A79lda#$00; x-ref: $1A6F
$1A7B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Finalizes key-binding configuration at the end of the binding wizard
; (title_state == $30). Inverts all 9 key column bitmasks at
; key_col_down ($31) through $39 via EOR #$FF.
;
; map_new_key stores the raw $E812 scan value when a key is pressed.
; On the PET, $E812 is active-low: the pressed key's bit reads as 0,
; all others as 1 (e.g. $F7 for a key in bit 3).
;
; The game's scan logic uses:
; lda $E812 / and key_col_X / bne not_pressed
; which requires an active-high mask (bit set = that key), e.g. $08.
; EOR #$FF converts $F7 -> $08, making the comparison work correctly.
;
; Called once only, at $1A0B, just before restore_sprite_bg and
; switch_to_title_screen.
;
; Inputs: key_col_down..$39 — raw active-low scan values from map_new_key
; Outputs: Same 9 bytes converted to active-high bitmasks
; Side Effects: Y modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
finalize_key_bindings
$1A7Ca0 00ldy#$00; x-ref: $1A0B
$1A7Eb9 31 00b_1A7Elda@w zp_key_col_down,y; x-ref: $1A89
$1A8149 ffeor#$ff
$1A8399 31 00sta@w zp_key_col_down,y
$1A86c8iny
$1A87c0 09cpy#$09
$1A89d0 f3bneb_1A7E
$1A8B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Builds the movement-property bitmask grid at $672B from the visual PETSCII
; tiles at $6800. Called once per level load at the end of unpack_level_data.
; The grid is read each frame by apply_pathfinding_to_guards and the AI.
;
; Phase 1: Zero the entire grid ($672B..$7F2B, 25 pages x 42 bytes).
;
; Phase 2: For each tile (pages $68-$7F, cols 1-$20), check the char value
; and its vertical neighbours, OR-ing property bits into the grid cell:
;
; bit $01 walk-left: floor below, left neighbour not wall
; bit $02 walk-right: floor below, right neighbour not wall
; bit $04 climb-up: ladder ($43) here, tile 2 rows above is not wall
; bit $08 climb-down: ladder here with open row below; rope ($63) here
; with open row below; or ladder directly below
; bit $10 fall-through: open tile (not rope/ladder), space or rope below
; bit $20 rope: tile is rope ($63)
; bit $40 solid wall: tile is $CC/$DB/$DD (brick variants); NOTE: STA not
; ORA — overwrites all other bits when wall is found
; bit $80 solid wall: same $CC/$DB/$DD tiles get $80 ORed on top of $40;
; always set together with $40 (result = $C0).
; $A0 (filled block) is treated as solid in neighbour
; checks but does NOT receive wall bits in the grid.
;
; Key tile chars:
; $20 = space $43 = ladder $63 = rope
; $A0 = solid block (no grid bits, but blocks walk/climb neighbours)
; $CC/$DB/$DD = brick/wall variants → grid = $C0
;
; Inputs: Visual tile buffer at $6800
; Outputs: Movement property grid at $672B
; Side Effects: a56/a57, a58/a59 modified (left pointing past end on exit)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1A8Ca9 2bbuild_movement_gridlda#<playfield_row_0_offset_2b; --- PHASE 1: zero the entire movement grid --- ; x-ref: $12B2
$1A8E85 58stazp_ptr_wipe_buf_lo; grid pointer low = $2B
$1A90a9 67lda#>playfield_row_0_offset_2b; grid pointer high = $67 (first row)
$1A9285 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$1A94a9 00b_1A94lda#$00; fill value = $00 ; x-ref: $1AA3
$1A96a0 29ldy#$29; 42 bytes per row (cols 0..$29)
$1A9891 58b_1A98sta(zp_ptr_wipe_buf_lo),y; x-ref: $1A9B
$1A9A88dey
$1A9B10 fbbplb_1A98
$1A9De6 59inczp_ptr_wipe_buf_hi; advance to next row; TEMPF2 Temporary storage for FLPT value.
$1A9Fa5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$1AA1c9 80cmp#$80; stop when high byte reaches $80 (end of grid)
$1AA3d0 efbneb_1A94
$1AA5a9 2blda#<playfield_row_1_offset_2b; --- PHASE 2: build property bits from visual tiles ---
$1AA785 58stazp_ptr_wipe_buf_lo
$1AA9a9 68lda#>playfield_row_1_offset_2b
$1AAB85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$1AADa9 00lda#<playfield_row_1_page_base; screen tile buffer low = $00
$1AAF85 56stazp_ptr_render_buf_lo; screen tile buffer high = $68 (row 1)
$1AB1a9 68lda#>playfield_row_1_page_base
$1AB385 57stazp_ptr_render_buf_hi
$1AB5a9 00lda#$00
$1AB7a0 01j_1AB7ldy#$01; start at col 1 (skip left border) ; x-ref: $1C09
$1AB9a5 57j_1AB9ldazp_ptr_render_buf_hi; --- per-tile loop (rows $68-$7F, cols 1-$20) --- ; x-ref: $1BFC
$1ABBc9 7fcmp#$7f; last row ($7F)? walk bits don't apply
$1ABDf0 27beqb_1AE6
$1ABFb1 56lda(zp_ptr_render_buf_lo),y; rope/ladder tile? can't walk from it
$1AC1c9 63cmp#$63
$1AC3f0 21beqb_1AE6
$1AC5c9 43cmp#$43
$1AC7f0 1dbeqb_1AE6
$1AC9e6 57inczp_ptr_render_buf_hi; peek at tile one row below
$1ACBb1 56lda(zp_ptr_render_buf_lo),y; restore row pointer
$1ACDc6 57deczp_ptr_render_buf_hi; wall/solid/ladder below? floor confirmed → check walk bits
$1ACFc9 ddcmp#$dd
$1AD1f0 13beqb_1AE6
$1AD3c9 cccmp#$cc
$1AD5f0 0fbeqb_1AE6
$1AD7c9 dbcmp#$db
$1AD9f0 0bbeqb_1AE6
$1ADBc9 a0cmp#$a0
$1ADDf0 07beqb_1AE6
$1ADFc9 43cmp#$43
$1AE1f0 03beqb_1AE6
$1AE34c 22 1bjmpj_1B22
$1AE6c0 01b_1AE6cpy#$01; --- bit $01: walk-left --- floor confirmed; at col 1 (left border)? skip ; x-ref: $1ABD, $1AC3, $1AC7, $1AD1, $1AD5, $1AD9, $1ADD, $1AE1
$1AE8f0 1abeqb_1B04
$1AEA88dey; peek at left neighbour
$1AEBb1 56lda(zp_ptr_render_buf_lo),y
$1AEDc8iny; restore Y
$1AEEc9 ddcmp#$dd
$1AF0f0 12beqb_1B04
$1AF2c9 cccmp#$cc
$1AF4f0 0ebeqb_1B04
$1AF6c9 dbcmp#$db; left neighbour is wall/solid → can't walk left
$1AF8f0 0abeqb_1B04
$1AFAc9 a0cmp#$a0
$1AFCf0 06beqb_1B04
$1AFEb1 58lda(zp_ptr_wipe_buf_lo),y; set bit $01 (walk-left accessible)
$1B0009 01ora#$01
$1B0291 58sta(zp_ptr_wipe_buf_lo),y
$1B04c0 20b_1B04cpy#$20; --- bit $02: walk-right --- at col $20 (right border)? skip ; x-ref: $1AE8, $1AF0, $1AF4, $1AF8, $1AFC
$1B06f0 1abeqj_1B22
$1B08c8iny; peek at right neighbour
$1B09b1 56lda(zp_ptr_render_buf_lo),y
$1B0B88dey; restore Y
$1B0Cc9 ddcmp#$dd
$1B0Ef0 12beqj_1B22
$1B10c9 cccmp#$cc
$1B12f0 0ebeqj_1B22
$1B14c9 dbcmp#$db; set bit $02 (walk-right accessible)
$1B16f0 0abeqj_1B22
$1B18c9 a0cmp#$a0
$1B1Af0 06beqj_1B22
$1B1Cb1 58lda(zp_ptr_wipe_buf_lo),y
$1B1E09 02ora#$02
$1B2091 58sta(zp_ptr_wipe_buf_lo),y
$1B22b1 56j_1B22lda(zp_ptr_render_buf_lo),y; --- bit $04: climb-up --- tile is $43 (ladder)? ; x-ref: $1AE3, $1B06, $1B0E, $1B12, $1B16, $1B1A
$1B24c9 43cmp#$43
$1B26d0 43bneb_1B6B
$1B28a5 57ldazp_ptr_render_buf_hi; first data row ($68)? can't climb higher → skip
$1B2Ac9 68cmp#$68
$1B2Cf0 1cbeqb_1B4A
$1B2Ec6 57deczp_ptr_render_buf_hi; peek 2 rows above (entity is 2 tiles tall)
$1B30c6 57deczp_ptr_render_buf_hi
$1B32b1 56lda(zp_ptr_render_buf_lo),y
$1B34e6 57inczp_ptr_render_buf_hi; restore row pointer
$1B36e6 57inczp_ptr_render_buf_hi
$1B38c9 cccmp#$cc; restore row pointer
$1B3Af0 0ebeqb_1B4A
$1B3Cc9 a0cmp#$a0; wall/solid 2 rows above? head would hit → no climb-up
$1B3Ef0 0abeqb_1B4A
$1B40c9 dbcmp#$db
$1B42f0 06beqb_1B4A
$1B44b1 58lda(zp_ptr_wipe_buf_lo),y; set bit $04 (can climb up)
$1B4609 04ora#$04
$1B4891 58sta(zp_ptr_wipe_buf_lo),y
$1B4Aa5 57b_1B4Aldazp_ptr_render_buf_hi; --- bit $08: climb-down (ladder) --- last row? can't descend → skip ; x-ref: $1B2C, $1B3A, $1B3E, $1B42
$1B4Cc9 7fcmp#$7f
$1B4Ef0 44beqb_1B94
$1B50e6 57inczp_ptr_render_buf_hi; peek one row below
$1B52b1 56lda(zp_ptr_render_buf_lo),y; restore row pointer
$1B54c6 57deczp_ptr_render_buf_hi
$1B56c9 cccmp#$cc; wall/solid below? ladder descend blocked
$1B58f0 3abeqb_1B94
$1B5Ac9 a0cmp#$a0
$1B5Cf0 36beqb_1B94
$1B5Ec9 dbcmp#$db
$1B60f0 32beqb_1B94
$1B62b1 58lda(zp_ptr_wipe_buf_lo),y; set bit $08 (can climb down)
$1B6409 08ora#$08
$1B6691 58sta(zp_ptr_wipe_buf_lo),y
$1B684c 94 1bjmpb_1B94
$1B6Bc9 63b_1B6Bcmp#$63; --- bit $08: climb-down (rope/ladder-below) --- tile is rope ($63)? ; x-ref: $1B26
$1B6Df0 0dbeqb_1B7C
$1B6Fe6 57inczp_ptr_render_buf_hi; not rope: peek row below for ladder ($43)
$1B71b1 56lda(zp_ptr_render_buf_lo),y
$1B73c6 57deczp_ptr_render_buf_hi
$1B75c9 43cmp#$43; ladder directly below → can step down onto it
$1B77f0 15beqb_1B8E
$1B794c 94 1bjmpb_1B94
$1B7Ce6 57b_1B7Cinczp_ptr_render_buf_hi; rope tile: peek row below; wall/solid → rope descent blocked ; x-ref: $1B6D
$1B7Eb1 56lda(zp_ptr_render_buf_lo),y
$1B80c6 57deczp_ptr_render_buf_hi
$1B82c9 cccmp#$cc
$1B84f0 0ebeqb_1B94
$1B86c9 a0cmp#$a0
$1B88f0 0abeqb_1B94
$1B8Ac9 dbcmp#$db
$1B8Cf0 06beqb_1B94
$1B8Eb1 58b_1B8Elda(zp_ptr_wipe_buf_lo),y; set bit $08 (can climb down from rope, or step onto ladder below) ; x-ref: $1B77
$1B9009 08ora#$08
$1B9291 58sta(zp_ptr_wipe_buf_lo),y
$1B94b1 56b_1B94lda(zp_ptr_render_buf_lo),y; --- bit $10: fall-through --- rope/ladder here? entity grabs it, no fall ; x-ref: $1B4E, $1B58, $1B5C, $1B60, $1B68, $1B79, $1B84, $1B88, ...
$1B96c9 63cmp#$63
$1B98f0 21beqb_1BBB
$1B9Ac9 43cmp#$43
$1B9Cf0 1dbeqb_1BBB
$1B9Ea5 57ldazp_ptr_render_buf_hi; peek one row below
$1BA0c9 7fcmp#$7f
$1BA2f0 17beqb_1BBB
$1BA4e6 57inczp_ptr_render_buf_hi
$1BA6b1 56lda(zp_ptr_render_buf_lo),y
$1BA8c6 57deczp_ptr_render_buf_hi; space ($20) or rope ($63) below → entity will fall / descend
$1BAAc9 20cmp#$20
$1BACf0 07beqb_1BB5
$1BAEc9 63cmp#$63
$1BB0f0 03beqb_1BB5
$1BB24c bb 1bjmpb_1BBB
$1BB5b1 58b_1BB5lda(zp_ptr_wipe_buf_lo),y; set bit $10 (can fall through) ; x-ref: $1BAC, $1BB0
$1BB709 10ora#$10
$1BB991 58sta(zp_ptr_wipe_buf_lo),y
$1BBBb1 56b_1BBBlda(zp_ptr_render_buf_lo),y; --- bit $20: rope --- tile is rope ($63)? ; x-ref: $1B98, $1B9C, $1BA2, $1BB2
$1BBDc9 63cmp#$63
$1BBFd0 06bneb_1BC7
$1BC1b1 58lda(zp_ptr_wipe_buf_lo),y; set bit $20 (rope tile)
$1BC309 20ora#$20
$1BC591 58sta(zp_ptr_wipe_buf_lo),y
$1BC7b1 56b_1BC7lda(zp_ptr_render_buf_lo),y; --- bits $40/$80: solid wall --- tile is $CC/$DB/$DD (brick variants)? ; x-ref: $1BBF
$1BC9c9 cccmp#$cc
$1BCBf0 0bbeqb_1BD8
$1BCDc9 dbcmp#$db
$1BCFf0 07beqb_1BD8
$1BD1c9 ddcmp#$dd
$1BD3f0 03beqb_1BD8
$1BD54c f7 1bjmpj_1BF7
$1BD8a9 40b_1BD8lda#$40; STA (not ORA): overwrites all previous bits; wall always wins ; x-ref: $1BCB, $1BCF, $1BD3
$1BDA91 58sta(zp_ptr_wipe_buf_lo),y; re-read tile to check for full-solid ($CC/$DB/$A0/$DD)
$1BDCb1 56lda(zp_ptr_render_buf_lo),y
$1BDEc9 cccmp#$cc
$1BE0f0 0fbeqb_1BF1
$1BE2c9 dbcmp#$db
$1BE4f0 0bbeqb_1BF1
$1BE6c9 a0cmp#$a0
$1BE8f0 07beqb_1BF1
$1BEAc9 ddcmp#$dd
$1BECf0 03beqb_1BF1
$1BEE4c f7 1bjmpj_1BF7
$1BF1b1 58b_1BF1lda(zp_ptr_wipe_buf_lo),y; set bit $80 (fully solid wall) ; x-ref: $1BE0, $1BE4, $1BE8, $1BEC
$1BF309 80ora#$80
$1BF591 58sta(zp_ptr_wipe_buf_lo),y
$1BF7c8j_1BF7iny; advance to next column ; x-ref: $1BD5, $1BEE
$1BF8c0 21cpy#$21; col $21 = past last data col → end of row
$1BFAf0 03beqb_1BFF
$1BFC4c b9 1ajmpj_1AB9
$1BFFe6 59b_1BFFinczp_ptr_wipe_buf_hi; advance grid pointer to next row ; x-ref: $1BFA TEMPF2 Temporary storage for FLPT value.
$1C01e6 57inczp_ptr_render_buf_hi
$1C03a5 57ldazp_ptr_render_buf_hi; all rows processed ($80)? done
$1C05c9 80cmp#$80
$1C07f0 03beqr_1C0C
$1C094c b7 1ajmpj_1AB7
$1C0C60r_1C0Crts; x-ref: $1C07
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Fills the entire playfield buffer at $672B-$7F54 with the default empty
; tile code $0F (25 pages x 42 bytes = 1050 bytes).
;
; Called from:
; $163E - game startup / key-binding init, before redirecting to menu_irq
; $30F4 - title_screen_irq state 0, paired with clear_gameplay_grid
;
; Inputs: None
; Outputs: $672B-$7F54 filled with $0F
; Side Effects: a5B left at $80 on exit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clear_playfield_buffer
$1C0Da9 2blda#<playfield_row_0_offset_2b; x-ref: $163E, $30F4
$1C0F85 5astazp_ptr_playfield_lo
$1C11a9 67lda#>playfield_row_0_offset_2b
$1C1385 5bstazp_ptr_playfield_hi
$1C15a9 0fb_1C15lda#$0f; x-ref: $1C24
$1C17a0 29ldy#$29
$1C1991 5ab_1C19sta(zp_ptr_playfield_lo),y; x-ref: $1C1C
$1C1B88dey
$1C1C10 fbbplb_1C19
$1C1Ee6 5binczp_ptr_playfield_hi
$1C20a5 5bldazp_ptr_playfield_hi
$1C22c9 80cmp#$80
$1C24d0 efbneb_1C15
$1C2660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Guard AI pathfinding dispatcher. Called once per frame for all 5 guard
; slots (4..0). Translates the pathfinding grid tile under each guard into
; a dx/dy movement vector.
;
; For each active guard:
; 1. Skip if guard_active is negative.
; 2. Set attribute RAM page: a5B = guard_page + 1.
; 3. Read pathfinding tile at (p60)[guard_offset], mask bit 7.
; 4. Self-modifying dispatch: store masked tile as the BPL branch offset
; at $1C41 — A is always < $80 so branch always taken, landing at
; $1C42 + (tile & $7F).
;
; Sparse jump table (NOP sled at $1C42-$1C49 absorbs values $00-$07):
; tile $00-$07 -> guard_dx=+1, guard_dy= 0 (move right)
; tile $10 -> guard_dx= 0, guard_dy=-1 (move up)
; tile $18 -> guard_dx=-1, guard_dy= 0 (move left)
; tile $20 -> guard_dx= 0, guard_dy=+1 (move down)
;
; These four values are the direction codes written by the AI pathfinding
; algorithm into attribute RAM each frame.
;
; Inputs: guard_active/page/offset[0..4], pathfinding attribute RAM
; Outputs: guard_dx/dy[x] set for each active guard
; Side Effects: Self-modifies BPL operand at $1C41
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
apply_pathfinding_to_guards
$1C27a2 04ldx#$04; x-ref: $0665
$1C29bd f9 21b_1C29ldaguard_active,x; x-ref: $1C6D, $1C76
$1C2C30 42bmib_1C70
$1C2Ebc 08 22ldyguard_page,x
$1C31c8iny
$1C3284 5bstyzp_ptr_playfield_hi
$1C3484 61styzp_ptr_pathfinding_hi
$1C36bc 03 22ldyguard_offset,x
$1C39b1 60lda(zp_ptr_pathfinding_lo),y
$1C3B29 7fand#$7f
$1C3D8d 41 1cstaa_1C41
a_1C41 =*+$01 ; x-ref: $1C3D
$1C4010 feb_1C40bplb_1C40; x-ref: $1C40
$1C42eanop
$1C43eanop
$1C44eanop
$1C45eanop
$1C46eanop
$1C47eanop
$1C48eanop
$1C49eanop
$1C4Aa9 01lda#$01
$1C4C9d 12 22staguard_vspeed,x
$1C4F4c 70 1cjmpb_1C70
$1C52a9 fflda#$ff
$1C549d 17 22staguard_hspeed,x
$1C574c 67 1cjmpj_1C67
$1C5Aa9 fflda#$ff
$1C5C9d 12 22staguard_vspeed,x
$1C5F4c 70 1cjmpb_1C70
$1C62a9 01lda#$01
$1C649d 17 22staguard_hspeed,x
$1C67a9 00j_1C67lda#$00; x-ref: $1C57
$1C699d 12 22staguard_vspeed,x
$1C6Ccadex
$1C6D10 babplb_1C29
$1C6F60rts
$1C70a9 00b_1C70lda#$00; x-ref: $1C2C, $1C4F, $1C5F
$1C729d 17 22staguard_hspeed,x
$1C75cadex
$1C7610 b1bplb_1C29
$1C7860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Pushes the X and Y coordinate pairs onto a custom FIFO queue at $6500/$6600.
; Increments the queue_tail pointer ($63) and stores X at $6500+tail, Y at $6600+tail.
;
; Inputs: X, Y (coordinates)
; Outputs: Increments queue_tail
; Side Effects: Modifies ZP $65 (temp storage)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1C7986 65queue_push_xystxzp_scratch_reg_x; x-ref: $1D10, $1D41, $1D6F, $1D8A, $1DA5, $1DBC BITS Floating -accum. #1: Overflow Digit
$1C7B8atxa
$1C7Ce6 63inczp_queue_tail; FACSGN Floating Accum. #1: Sign
$1C7Ea6 63ldxzp_queue_tail; FACSGN Floating Accum. #1: Sign
$1C809d 00 65staqueue_array_x,x
$1C8398tya
$1C849d 00 66staqueue_array_y,x
$1C87a6 65ldxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit
$1C8960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Pops an X, Y coordinate pair from the front of the FIFO queue.
; Reads from index 1 (using queue_head_temp), then shifts all remaining elements
; in the queue down by one index to keep the queue aligned.
;
; Inputs: queue arrays at $6500/$6600, queue_tail ($63)
; Outputs: Returns popped X and Y in the X and Y registers. Decrements queue_tail.
; Side Effects: Shifts arrays. Modifies ZP $62, $65, $66.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1C8Ae6 62queue_shift_pop_xyinczp_queue_head_temp
$1C8Ca4 62ldyzp_queue_head_temp
$1C8Eb9 00 66ldaqueue_array_y,y
$1C91be 00 65ldxqueue_array_x,y
$1C94a8tay
$1C9586 65stxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit
$1C9784 66styzp_scratch_reg_y; ARGEXP Floating-Point Accumulator #2: Exponent
$1C99a0 01ldy#$01
$1C9Ba2 00ldx#$00
$1C9Db9 00 65b_1C9Dldaqueue_array_x,y; x-ref: $1CAD
$1CA09d 00 65staqueue_array_x,x
$1CA3b9 00 66ldaqueue_array_y,y
$1CA69d 00 66staqueue_array_y,x
$1CA9e8inx
$1CAAc8iny
$1CABe4 63cpxzp_queue_tail; FACSGN Floating Accum. #1: Sign
$1CADd0 eebneb_1C9D
$1CAFc6 62deczp_queue_head_temp
$1CB1c6 63deczp_queue_tail; FACSGN Floating Accum. #1: Sign
$1CB3a6 65ldxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit
$1CB5a4 66ldyzp_scratch_reg_y; ARGEXP Floating-Point Accumulator #2: Exponent
$1CB760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the AI pathfinding or collision buffer at page offset $55 to #$01.
; Iterates through 25 pages (from $67 to $7F), setting the first 32 bytes of each page offset to #$01.
; Also sets up pointers at $5C/$5D and $5E/$5F for other buffers.
;
; Inputs: None.
; Outputs: Fills the $55-offset buffer with #$01.
; Side Effects: Modifies $5C/$5D, $5E/$5F, Y (data index), $62, and $6E.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
init_ai_pathfinding_grid
$1CB8a9 55lda#$55; x-ref: $0754
$1CBA85 5cstazp_ptr_attr_ram_lo
$1CBCa9 2blda#$2b
$1CBE85 5estazp_ptr_move_grid_lo; FACEXP Floating-Point Accumulator #1: Exponent
$1CC0a9 67lda#$67
$1CC285 5dstazp_ptr_attr_ram_hi
$1CC485 5fstazp_ptr_move_grid_hi; FACHO Floating Accum. #1: Mantissa
$1CC6a9 01b_1CC6lda#$01; x-ref: $1CD7
$1CC8a0 01ldy#$01
$1CCA91 5cb_1CCAsta(zp_ptr_attr_ram_lo),y; x-ref: $1CCF
$1CCCc8iny
$1CCDc0 21cpy#$21
$1CCFd0 f9bneb_1CCA
$1CD1e6 5dinczp_ptr_attr_ram_hi
$1CD3a5 5dldazp_ptr_attr_ram_hi
$1CD5c9 80cmp#$80
$1CD7d0 edbneb_1CC6
$1CD9a9 00lda#$00
$1CDB85 62stazp_queue_head_temp
$1CDD85 63stazp_queue_tail; FACSGN Floating Accum. #1: Sign
$1CDF60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Guard AI pathfinding BFS. Called every main_loop iteration during gameplay
; (game_state_flag==1) and once at level load ($0757).
;
; Seeds the BFS at the player's attribute-RAM position (draw_page+1,
; draw_offset) then flood-fills outward through all reachable tiles,
; writing a direction code into each cell's attribute byte so that
; apply_pathfinding_to_guards can steer every guard toward the player.
;
; Self-modifying generation-bit trick (avoids clearing the grid each frame):
; Each call toggles between two generations by patching 4 branch OPCODES
; ($10=BPL <-> $30=BMI) and 4 LDA #imm direction-code operands in-place:
; Gen A (opcode=$10, BPL): writes $08/$18/$10/$20 (bit 7 clear)
; skips cells with bit 7 CLEAR (already this gen)
; Gen B (opcode=$30, BMI): writes $88/$98/$90/$A0 (bit 7 set)
; skips cells with bit 7 SET (already this gen)
; Bit 7 of the attribute byte is the "visited this generation" marker.
;
; BFS neighbour expansion (movement grid at a5E, attr RAM at a5C):
; above (page-1): grid bits $08+$10 set -> write $08/$88 -> guard moves DOWN
; below (page+1): grid bit $04 set -> write $18/$98 -> guard moves UP
; right (col+1): grid bit $01 set -> write $10/$90 -> guard moves LEFT
; left (col-1): grid bit $02 set -> write $20/$A0 -> guard moves RIGHT
;
; Direction codes match the sparse jump table in apply_pathfinding_to_guards.
;
; Inputs: draw_page/offset (player pos), movement grid (a5E), BFS queue
; Outputs: Pathfinding direction codes written to attribute RAM (a5C)
; Side Effects: Self-modifies 4 branch opcodes and 4 LDA operands; BFS queue consumed
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_guard_pathfinding
$1CE0ad 61 1dldaa_1D61; read branch opcode at $1D61 to detect current generation ; x-ref: $0623, $0757
$1CE3c9 10cmp#$10; $10 = BPL = gen A currently active?
$1CE5d0 31bneb_1D18; yes (gen A active) -> switch to gen B
$1CE7a9 30lda#$30; --- switch to gen B: patch branch opcodes $10->$30 (BPL->BMI) ---
$1CE98d 61 1dstaa_1D61; patch BMI opcode at visited-check for above neighbour
$1CEC8d 7c 1dstaa_1D7C; patch BMI opcode at visited-check for below neighbour
$1CEF8d 99 1dstaa_1D99; patch BMI opcode at visited-check for right neighbour
$1CF28d b0 1dstaa_1DB0; patch BMI opcode at visited-check for left neighbour
$1CF5a9 88lda#$88; --- patch gen B direction codes (bit 7 set = visited marker) ---
$1CF78d 6c 1dstaa_1D6C; above: $88 = move-down + gen-B visited bit
$1CFAa9 98lda#$98; below: $98 = move-up + gen-B visited bit
$1CFC8d 87 1dstaa_1D87
$1CFFa9 90lda#$90; right: $90 = move-left + gen-B visited bit
$1D018d a2 1dstaa_1DA2
$1D04a9 a0lda#$a0; left: $A0 = move-right + gen-B visited bit
$1D068d b9 1dstaa_1DB9
$1D09ac b4 0dldydraw_offset; --- seed BFS at player position ---
$1D0Cae b5 0dldxdraw_page; X = draw_page + 1 (attr RAM row for player)
$1D0Fe8inx
$1D1020 79 1cjsrqueue_push_xy; init attr pointer high byte for BFS loop
$1D1386 5dstxzp_ptr_attr_ram_hi
$1D154c 46 1djmpj_1D46
$1D18a9 10b_1D18lda#$10; --- switch to gen A: patch branch opcodes $30->$10 (BMI->BPL) --- ; x-ref: $1CE5
$1D1A8d 61 1dstaa_1D61
$1D1D8d 7c 1dstaa_1D7C
$1D208d 99 1dstaa_1D99
$1D238d b0 1dstaa_1DB0
$1D26a9 08lda#$08; --- patch gen A direction codes (bit 7 clear = visited marker) ---
$1D288d 6c 1dstaa_1D6C; above: $08 = move-down (bit 7 clear)
$1D2Ba9 18lda#$18
$1D2D8d 87 1dstaa_1D87; below: $18 = move-up
$1D30a9 10lda#$10; right: $10 = move-left
$1D328d a2 1dstaa_1DA2
$1D35a9 20lda#$20; left: $20 = move-right
$1D378d b9 1dstaa_1DB9
$1D3Aac b4 0dldydraw_offset
$1D3Dae b5 0dldxdraw_page
$1D40e8inx
$1D4120 79 1cjsrqueue_push_xy
$1D4486 5dstxzp_ptr_attr_ram_hi
$1D46a5 63j_1D46ldazp_queue_tail; --- BFS loop: empty? (tail == head) -> done --- ; x-ref: $1D15, $1DC0 FACSGN Floating Accum. #1: Sign
$1D48c5 62cmpzp_queue_head_temp
$1D4Ad0 01bneb_1D4D
$1D4C60rts
$1D4De6 62b_1D4Dinczp_queue_head_temp; dequeue: advance head, read (col, page) from queue arrays ; x-ref: $1D4A
$1D4Fa4 62ldyzp_queue_head_temp
$1D51b9 00 66ldaqueue_array_y,y
$1D54be 00 65ldxqueue_array_x,y
$1D57a8tay; Y = col (column offset), X = page (row)
$1D58e0 68cpx#$68; first data row ($68)? no row above -> skip above-check
$1D5Af0 17beqb_1D73
$1D5Ccadex; --- above neighbour (page-1) ---
$1D5D86 5dstxzp_ptr_attr_ram_hi
$1D5Fb1 5clda(zp_ptr_attr_ram_lo),y; read attr at (page-1, col); BPL/BMI skips if already visited this gen
$1D6110 0fa_1D61bplb_1D72; x-ref: $1CE0, $1CE9, $1D1A
$1D6386 5fstxzp_ptr_move_grid_hi; update movement grid pointer to page-1; FACHO Floating Accum. #1: Mantissa
$1D65b1 5elda(zp_ptr_move_grid_lo),y; FACEXP Floating-Point Accumulator #1: Exponent
$1D6729 18and#$18; bits $08+$10: can guard fall/climb-down from (page-1) to reach us?
$1D69f0 07beqb_1D72
a_1D6C =*+$01 ; x-ref: $1CF7, $1D28
$1D6Ba9 08lda#$08; write "move DOWN" direction code (+ gen visited bit)
$1D6D91 5csta(zp_ptr_attr_ram_lo),y
$1D6F20 79 1cjsrqueue_push_xy
$1D72e8b_1D72inx; --- below neighbour (page+1) --- restore page; last row ($7F)? skip ; x-ref: $1D61, $1D69
$1D73e0 7fb_1D73cpx#$7f; x-ref: $1D5A
$1D75f0 17beqb_1D8E
$1D77e8inx; read attr at (page+1, col); BPL/BMI skips if already visited this gen
$1D7886 5dstxzp_ptr_attr_ram_hi
$1D7Ab1 5clda(zp_ptr_attr_ram_lo),y
$1D7C10 0fa_1D7Cbplb_1D8D; x-ref: $1CEC, $1D1D
$1D7E86 5fstxzp_ptr_move_grid_hi; bit $04: can guard climb-up from (page+1) to reach us?; FACHO Floating Accum. #1: Mantissa
$1D80b1 5elda(zp_ptr_move_grid_lo),y; FACEXP Floating-Point Accumulator #1: Exponent
$1D8229 04and#$04
$1D84f0 07beqb_1D8D
a_1D87 =*+$01 ; x-ref: $1CFC, $1D2D
$1D86a9 18lda#$18; write "move UP" direction code
$1D8891 5csta(zp_ptr_attr_ram_lo),y
$1D8A20 79 1cjsrqueue_push_xy
$1D8Dcab_1D8Ddex; restore page; set both attr+grid pointers back to current row ; x-ref: $1D7C, $1D84
$1D8E86 5db_1D8Estxzp_ptr_attr_ram_hi; x-ref: $1D75
$1D9086 5fstxzp_ptr_move_grid_hi; FACHO Floating Accum. #1: Mantissa
$1D92c0 20cpy#$20; --- right neighbour (col+1) --- rightmost col ($20)? skip
$1D94f0 13beqb_1DA9
$1D96c8iny; read attr at (page, col+1); BPL/BMI skips if already visited this gen
$1D97b1 5clda(zp_ptr_attr_ram_lo),y
$1D9910 0da_1D99bplb_1DA8; x-ref: $1CEF, $1D20
$1D9Bb1 5elda(zp_ptr_move_grid_lo),y; bit $01: can guard walk-left from (col+1) to reach us?; FACEXP Floating-Point Accumulator #1: Exponent
$1D9D29 01and#$01
$1D9Ff0 07beqb_1DA8
a_1DA2 =*+$01 ; x-ref: $1D01, $1D32
$1DA1a9 10lda#$10; write "move LEFT" direction code
$1DA391 5csta(zp_ptr_attr_ram_lo),y
$1DA520 79 1cjsrqueue_push_xy
$1DA888b_1DA8dey; --- left neighbour (col-1) --- restore col; leftmost col ($01)? skip ; x-ref: $1D99, $1D9F
$1DA9c0 01b_1DA9cpy#$01; x-ref: $1D94
$1DABf0 13beqb_1DC0
$1DAD88dey; read attr at (page, col-1); BPL/BMI skips if already visited this gen
$1DAEb1 5clda(zp_ptr_attr_ram_lo),y
$1DB010 0da_1DB0bplb_1DBF; x-ref: $1CF2, $1D23
$1DB2b1 5elda(zp_ptr_move_grid_lo),y; bit $02: can guard walk-right from (col-1) to reach us?; FACEXP Floating-Point Accumulator #1: Exponent
$1DB429 02and#$02
$1DB6f0 07beqb_1DBF
a_1DB9 =*+$01 ; x-ref: $1D06, $1D37
$1DB8a9 20lda#$20; write "move RIGHT" direction code
$1DBA91 5csta(zp_ptr_attr_ram_lo),y
$1DBC20 79 1cjsrqueue_push_xy
$1DBFc8b_1DBFiny; restore col; loop back for next queued cell ; x-ref: $1DB0, $1DB6
$1DC04c 46 1db_1DC0jmpj_1D46; x-ref: $1DAB
; Dig animation system — hole-digging state machine for 10 actor slots (player + 9 guards).
;
; Animation data (32 bytes each, indexed by actor_anim_step 0-31):
; dig_open_anim_top $1DC3: top-cell chars for hole-open phase (step 0-15 = tiles, 16-31 = $20 done)
; dig_open_anim_bot $1DE3: bot-cell chars for hole-open phase
; dig_close_anim_top $1E03: top-cell chars for hole-close phase ($00 = skip write sentinel)
; dig_close_anim_bot $1E23: bot-cell chars for hole-close phase ($a0 = space, done)
;
; Per-actor arrays (10 elements, indexed by actor slot 0-9):
; actor_status_table $1E43: state machine phase ($FF=idle, 0=save bg, 1=open anim,
; 2=delay, 3=close anim, 4=restore bg)
; actor_dig_page $1E4D: screen page of the dug cell
; actor_dig_offset $1E57: screen column offset of the dug cell
; actor_anim_step $1E61: animation step counter 0-31 (indexes into anim tables)
; actor_delay_inner $1E6B: inner delay countdown (8->0, then reload; status==2)
; actor_delay_outer $1E75: outer delay countdown ($2B->0, then advance; status==2)
; actor_saved_bg_top $1E7F: saved background char at top cell
; actor_saved_bg_bot $1E89: saved background char at bot cell
; actor_hole_slot $1E93: index into hole_slot_* arrays ($FF=none)
$1DC3dig_open_anim_top.byte$e3, $aa, $f7, $d8, $f8, $20, $62, $aa; x-ref: $1EE9
$1DCB.byte$79, $58, $6f, $20, $64, $2a, $20, $58
$1DD3.fill16, $20
; Dig-open animation bottom cell chars (32 steps). Paired with dig_open_anim_top.
$1DE3dig_open_anim_bot.byte$a0, $aa, $d8, $a0, $a0, $aa, $d8, $a0; x-ref: $1EF9
$1DEB.byte$a0, $aa, $d8, $a0, $a0, $aa, $d8, $a0
$1DF3.byte$e3, $aa, $f7, $20, $f8, $aa, $62, $d8
$1DFB.byte$79, $20, $6f, $58, $64, $2a, $58, $20
; Dig-close animation top cell chars (32 steps). $00 = skip write (cell stays blank).
$1E03dig_close_anim_top.fill16, $00; x-ref: $1F4C
$1E13.byte$64, $64, $6f, $6f, $79, $79, $62, $62
$1E1B.byte$f8, $f8, $f7, $f7, $e3, $e3, $a0, $a0
; Dig-close animation bottom cell chars (32 steps). $a0 (space) = done writing.
$1E23dig_close_anim_bot.byte$64, $64, $6f, $6f, $79, $79, $62, $62; x-ref: $1F5B
$1E2B.byte$f8, $f8, $f7, $f7, $e3, $e3
$1E31.fill18, $a0
; Dig state machine phase per player dig slot (10 simultaneous digs max).
; $FF = idle / no dig in progress
; $00 = save background chars under the dug cell, then advance
; $01 = play hole-open animation (32 steps via dig_open_anim_*)
; $02 = hold delay (dig_slot_delay_inner x dig_slot_delay_outer ticks)
; $03 = play hole-close animation (32 steps via dig_close_anim_*)
; $04 = restore saved background chars, clear hole_slot, set status $FF
; Note: bytes $1E44-$1E4C (slots 1-9) are reused by the key-binding wizard
; for animation parameters when no dig is in progress.
$1E43dig_slot_status.byte$ff; x-ref: $0F7C, $0FAF, $0FBD, $0FF8, $102B, $1039, $109B, $1191, ...
$1E44kbind_anim_step.fill9, $ff; overlaps dig_slot_status[1]; reused as wizard animation step counter (0..2) during key binding ; x-ref: $1976, $19A9
; Screen memory page for each actor's dug cell (set when dig starts, held through all phases).
$1E4Ddig_slot_page.byte$ff; x-ref: $0F8C, $0FD1, $0FD4, $0FD7, $1008, $104D, $1050, $1053, ...
$1E4Ekbind_sprite_page.fill9, $ff; overlaps dig_slot_page[1]; reused as player sprite page ($7D) during key binding ; x-ref: $197B
; Screen column offset for each actor's dug cell.
$1E57dig_slot_col.byte$ff; x-ref: $0F81, $0FC8, $0FCB, $0FFD, $1044, $1047, $11A2, $192D, ...
$1E58kbind_sprite_col.fill9, $ff; overlaps dig_slot_col[1]; reused as player sprite column ($06) during key binding ; x-ref: $1980
; Animation step counter 0-31. Incremented each frame during status 1 and 3.
; At 32 ($20), the current phase is complete and status advances.
$1E61dig_slot_anim_step.fill10, $ff; x-ref: $11AA, $1ED4, $1EE6, $1EF6, $1F04, $1F07, $1F37, $1F49, ...
; Inner delay countdown (status==2). Loaded with $08, decremented each frame.
; When it underflows, reloaded with $08 and actor_delay_outer is decremented.
$1E6Bdig_slot_delay_inner.fill10, $ff; x-ref: $1994, $1F13, $1F26, $1F2D
; Outer delay countdown (status==2). Loaded with $2B, decremented when inner underflows.
; When it underflows, actor_anim_step is reset and status advances to 3 (close animation).
$1E75dig_slot_delay_outer.fill10, $ff; x-ref: $1999, $1F18, $1F30
; Background char saved from top cell before digging. Restored at status 4.
$1E7Fdig_slot_saved_top.fill10, $ff; x-ref: $1EC6, $1F7F
; Background char saved from bottom cell before digging. Restored at status 4.
$1E89dig_slot_saved_bot.fill10, $ff; x-ref: $1ECD, $1F86
; Index of the linked hole_slot_* entry for this actor ($FF = none).
; Cleared at status 4 when background is restored and the slot is freed.
$1E93dig_slot_hole_idx.fill10, $ff; x-ref: $0FC2, $103E, $1EA4, $1F93, $27B7, $27FB, $2D76
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all 10 player dig slots to idle state ($FF) and zeros
; zero-page variables a68 and sets a77=$55.
; Called at game startup ($073C) and key-binding init ($1633).
;
; Inputs: None
; Outputs: dig_slot_status[0..9]=$FF, dig_slot_hole_idx[0..9]=$FF,
; a68=$00, a77=$55
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E9Da2 09init_dig_slotsldx#$09; x-ref: $073C, $1633
$1E9Fa9 fflda#$ff
$1EA19d 43 1eb_1EA1stadig_slot_status,x; x-ref: $1EA8
$1EA49d 93 1estadig_slot_hole_idx,x
$1EA7cadex
$1EA810 f7bplb_1EA1
$1EAAa9 00lda#$00
$1EAC85 68stazp_ptr_dig_screen_lo
$1EAEa9 55lda#$55
$1EB085 77stazp_ptr_ai_grid_lo; TXTPTR Pointer: Current Byte of BASIC Text
$1EB260rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Dig animation state machine for all 10 player dig slots (X: 9..0).
; Called each frame from the gameplay IRQ. Each slot independently drives
; a hole through 5 phases via dig_slot_status:
;
; $FF Inactive -> skip
; 0 Save bg -> read screen tiles at dig_slot_page/col into
; dig_slot_saved_top/bot; reset dig_slot_anim_step=0
; -> advance to 1
; 1 Open anim -> write dig_open_anim_top/bot[dig_slot_anim_step]
; to screen via get_adjacent_tile_border (32 steps);
; on step 32: advance to 2, dig_slot_delay_inner=$08,
; dig_slot_delay_outer=$2B, call update_hole_collision
; 2 Hold open -> countdown dig_slot_delay_inner(8) x
; dig_slot_delay_outer(44) = ~352 frame ticks;
; on expiry: anim_step=0 -> advance to 3
; 3 Close anim -> write dig_close_anim_top/bot[anim_step] (32
; steps); top char $00 = skip-write sentinel;
; on step 32 -> advance to 4
; 4 Restore bg -> write dig_slot_saved_top/bot back to screen;
; status=$FF (idle); call update_filled_hole_collision;
; if dig_slot_hole_idx valid: deactivate hole slot,
; reset guard (dx/dy=0, active=0, ai_bias=0,
; move_rate=2, sprite_strip=$30), restore guard
; to guard_spawn_page/offset
;
; Inputs: dig_slot_status/page/col/anim_step/delay_inner/delay_outer/
; saved_top/saved_bot/hole_idx[0..9]
; Outputs: Screen updated; movement grid updated on open/close;
; guard respawned at spawn point on hole close
; Side Effects: a69 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
process_dig_animations
$1EB3a2 09ldx#$09; iterate all 10 dig slots ; x-ref: $0671, $1676
$1EB5bd 43 1eb_1EB5ldadig_slot_status,x; $FF = inactive → skip ; x-ref: $1F1F
$1EB830 64bmib_1F1E
$1EBAd0 21bneb_1EDD; non-zero → not phase 0; dispatch further
$1EBCbd 4d 1eldadig_slot_page,x; --- phase 0: save background tiles ---
$1EBF85 69stazp_ptr_dig_screen_hi
$1EC1bc 57 1eldydig_slot_col,x; set screen pointer to dig row
$1EC4b1 68lda(zp_ptr_dig_screen_lo),y; save top tile (row)
$1EC69d 7f 1estadig_slot_saved_top,x
$1EC9e6 69inczp_ptr_dig_screen_hi; advance pointer to row+1 (bottom tile)
$1ECBb1 68lda(zp_ptr_dig_screen_lo),y; save bottom tile (row+1)
$1ECD9d 89 1estadig_slot_saved_bot,x
$1ED0c6 69deczp_ptr_dig_screen_hi; restore pointer
$1ED2a9 00lda#$00
$1ED49d 61 1estadig_slot_anim_step,x; reset anim step counter to 0
$1ED7fe 43 1eincdig_slot_status,x; advance to phase 1
$1EDA4c 1e 1fjmpb_1F1E
$1EDDc9 01b_1EDDcmp#$01; x-ref: $1EBA
$1EDFd0 41bneb_1F22
$1EE1bd 4d 1eldadig_slot_page,x; --- phase 1: open animation (32 steps) ---
$1EE485 69stazp_ptr_dig_screen_hi
$1EE6bc 61 1eldydig_slot_anim_step,x; set screen pointer to dig row
$1EE9b9 c3 1dldadig_open_anim_top,y
$1EECbc 57 1eldydig_slot_col,x
$1EEF20 2e 21jsrget_adjacent_tile_border; clip to border tile if at edge
$1EF291 68sta(zp_ptr_dig_screen_lo),y; write top char to screen (row)
$1EF4e6 69inczp_ptr_dig_screen_hi; advance pointer to row+1 (bottom tile)
$1EF6bc 61 1eldydig_slot_anim_step,x
$1EF9b9 e3 1dldadig_open_anim_bot,y; look up bottom char for this anim step
$1EFCbc 57 1eldydig_slot_col,x
$1EFF20 2e 21jsrget_adjacent_tile_border; write bottom char to screen (row+1)
$1F0291 68sta(zp_ptr_dig_screen_lo),y
$1F04fe 61 1eincdig_slot_anim_step,x; all 32 steps done?
$1F07bd 61 1eldadig_slot_anim_step,x
$1F0Ac9 20cmp#$20
$1F0Cd0 10bneb_1F1E
$1F0Efe 43 1eincdig_slot_status,x; arm hold-open counters: inner=8, outer=$2B (=43) → ~352 frame ticks
$1F11a9 08lda#$08
$1F139d 6b 1estadig_slot_delay_inner,x
$1F16a9 2blda#$2b
$1F189d 75 1estadig_slot_delay_outer,x
$1F1B20 cb 1fjsrupdate_hole_collision; update movement grid for open hole; advance to phase 2
$1F1Ecab_1F1Edex; x-ref: $1EB8, $1EDA, $1F0C, $1F29, $1F33, $1F3D, $1F6B, $1F70, ...
$1F1F10 94bplb_1EB5
$1F2160rts
$1F22c9 02b_1F22cmp#$02; --- phase 2: hold open (nested countdown) --- ; x-ref: $1EDF
$1F24d0 1abneb_1F40
$1F26de 6b 1edecdig_slot_delay_inner,x; inner still counting → wait
$1F2910 f3bplb_1F1E
$1F2Ba9 08lda#$08; reload inner = 8
$1F2D9d 6b 1estadig_slot_delay_inner,x
$1F30de 75 1edecdig_slot_delay_outer,x; outer still counting → wait another inner cycle
$1F3310 e9bplb_1F1E
$1F35a9 00lda#$00; both expired: reset anim step, advance to phase 3
$1F379d 61 1estadig_slot_anim_step,x
$1F3Afe 43 1eincdig_slot_status,x
$1F3D4c 1e 1fjmpb_1F1E
$1F40c9 03b_1F40cmp#$03; x-ref: $1F24
$1F42d0 2fbneb_1F73
$1F44bd 4d 1eldadig_slot_page,x; look up top close char; $00 = sentinel: skip writing top cell
$1F4785 69stazp_ptr_dig_screen_hi
$1F49bc 61 1eldydig_slot_anim_step,x
$1F4Cb9 03 1eldadig_close_anim_top,y
$1F4Ff0 05beqb_1F56
$1F51bc 57 1eldydig_slot_col,x
$1F5491 68sta(zp_ptr_dig_screen_lo),y
$1F56e6 69b_1F56inczp_ptr_dig_screen_hi; x-ref: $1F4F
$1F58bc 61 1eldydig_slot_anim_step,x
$1F5Bb9 23 1eldadig_close_anim_bot,y
$1F5Ebc 57 1eldydig_slot_col,x
$1F6191 68sta(zp_ptr_dig_screen_lo),y
$1F63fe 61 1eincdig_slot_anim_step,x; all 32 steps done → advance to phase 4
$1F66bd 61 1eldadig_slot_anim_step,x
$1F69c9 20cmp#$20
$1F6Bd0 b1bneb_1F1E
$1F6Dfe 43 1eincdig_slot_status,x
$1F704c 1e 1fjmpb_1F1E
$1F73c9 04b_1F73cmp#$04; --- phase 4: restore background tiles --- ; x-ref: $1F42
$1F75d0 a7bneb_1F1E
$1F77bd 4d 1eldadig_slot_page,x
$1F7A85 69stazp_ptr_dig_screen_hi
$1F7Cbc 57 1eldydig_slot_col,x
$1F7Fbd 7f 1eldadig_slot_saved_top,x; restore top bg tile to screen
$1F8291 68sta(zp_ptr_dig_screen_lo),y
$1F84e6 69inczp_ptr_dig_screen_hi; restore bottom bg tile to screen (row+1)
$1F86bd 89 1eldadig_slot_saved_bot,x
$1F8991 68sta(zp_ptr_dig_screen_lo),y
$1F8Ba9 fflda#$ff; mark slot idle ($FF)
$1F8D9d 43 1estadig_slot_status,x
$1F9020 aa 20jsrupdate_filled_hole_collision; update movement grid for closed hole
$1F93bd 93 1eldadig_slot_hole_idx,x; valid hole slot index? (>= 0) → respawn trapped guard
$1F9610 03bplb_1F9B
$1F984c 1e 1fjmpb_1F1E
$1F9Ba8b_1F9Btay; Y = hole slot index → index into guard arrays ; x-ref: $1F96
$1F9Ca9 00lda#$00; deactivate hole slot
$1F9E99 d0 2bstahole_slot_state,y
$1FA1a9 00lda#$00
$1FA399 fe 21staguard_anim_frame,y; reset: anim_frame=0, vspeed=0, hspeed=0
$1FA699 12 22staguard_vspeed,y
$1FA999 17 22staguard_hspeed,y
$1FAC99 f9 21staguard_active,y; guard_active=0 (active), ai_bias=0
$1FAF99 21 22staguard_ai_bias,y
$1FB2a9 02lda#$02; move_rate=2
$1FB499 1c 22staguard_move_rate,y
$1FB7a9 30lda#$30; sprite_strip=$30 (standing/walking)
$1FB999 0d 22staguard_sprite_strip,y
$1FBCad e4 2aldaguard_spawn_offset; teleport guard back to spawn position
$1FBF99 03 22staguard_offset,y
$1FC2ad e5 2aldaguard_spawn_page
$1FC599 08 22staguard_page,y
$1FC84c 1e 1fjmpb_1F1E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the movement property grid when a hole is freshly dug. Called from
; process_dig_animations when dig status transitions 1→2 (hole fully open).
; ZP pointer a5A/a5B addresses the grid; Y = dig_slot_col[x] = hole column.
;
; Movement bit flags:
; $01=walk-left $02=walk-right $04=climb-up $08=climb-down
; $10=fall-through $20=rope $40/$80=solid wall
;
; Row a69−2 (2 rows above):
; rope ($20) set → add climb-down ($08) ; can descend into new gap
; rope ($20) clear → add fall-through ($10)
;
; Row a69−1 (top edge of hole):
; force fall-through ($10)
;
; Row a69 (dug cell):
; row below has fall-through ($10) → clear solid, set fall-through ($10)
; otherwise → clear solid, set walk-left+walk-right+climb-down ($0B)
; prune walk-left ($01) if at left edge (col=$01) or left neighbor solid ($80)
; prune walk-right ($02) if at right edge (col=$20) or right neighbor solid ($80)
; prune climb-down ($08) if at last row ($7F) or row below has no climb-down ($08)
;
; Neighbour fixup (rows a69−1 and a69):
; left neighbor: set walk-right ($02) unless solid ($80) or fall-through ($10)
; right neighbor: set walk-left ($01) unless solid ($80) or fall-through ($10)
;
; Inputs: a69 (dig row page), dig_slot_col[x], movement grid at a5A/a5B
; Outputs: Movement grid updated for hole-open state
; Side Effects: a5B modified (restored to dig row on exit); Y preserved
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_hole_collision
$1FCBa5 69ldazp_ptr_dig_screen_hi; dig row page ; x-ref: $1F1B
$1FCD85 5bstazp_ptr_playfield_hi; a5B = dig row
$1FCFc6 5bdeczp_ptr_playfield_hi; a5B = dig row − 1
$1FD1c6 5bdeczp_ptr_playfield_hi; start 2 rows above dig row
$1FD3bc 57 1eldydig_slot_col,x; Y = hole column
$1FD6b1 5alda(zp_ptr_playfield_lo),y; read movement flags at row−2
$1FD829 20and#$20; rope present?
$1FDAf0 09beqb_1FE5; no rope → fall-through path
$1FDCb1 5alda(zp_ptr_playfield_lo),y
$1FDE09 08ora#$08; rope: add climb-down ($08) — gap below is reachable
$1FE091 5asta(zp_ptr_playfield_lo),y
$1FE24c eb 1fjmpj_1FEB
$1FE5b1 5ab_1FE5lda(zp_ptr_playfield_lo),y; no rope: add fall-through ($10) ; x-ref: $1FDA
$1FE709 10ora#$10
$1FE991 5asta(zp_ptr_playfield_lo),y
$1FEBe6 5bj_1FEBinczp_ptr_playfield_hi; advance to row−1 (top edge of hole) ; x-ref: $1FE2
$1FEDa9 10lda#$10; force fall-through at row−1
$1FEF91 5asta(zp_ptr_playfield_lo),y
$1FF1e6 5binczp_ptr_playfield_hi; advance to dig row
$1FF3a5 5bldazp_ptr_playfield_hi; at bottom of playfield ($7F)?
$1FF5c9 7fcmp#$7f
$1FF7f0 15beqb_200E; yes: skip below-check, use open-floor path
$1FF9e6 5binczp_ptr_playfield_hi; peek at row below
$1FFBb1 5alda(zp_ptr_playfield_lo),y; read flags of cell directly below hole
$1FFDc6 5bdeczp_ptr_playfield_hi; back to dig row
$1FFF29 10and#$10; row below has fall-through?
$2001f0 0bbeqb_200E; no → open-floor path
$2003b1 5alda(zp_ptr_playfield_lo),y
$200529 3fand#$3f; clear solid bits ($40/$80)
$200709 10ora#$10; hole over void/fall: propagate fall-through
$200991 5asta(zp_ptr_playfield_lo),y
$200B4c 59 20jmpj_2059
$200Eb1 5ab_200Elda(zp_ptr_playfield_lo),y; clear solid bits ; x-ref: $1FF7, $2001
$201029 3fand#$3f; set walk-left + walk-right + climb-down ($01|$02|$08)
$201209 0bora#$0b
$201491 5asta(zp_ptr_playfield_lo),y
$2016c0 01cpy#$01; at left edge (col 1)?
$2018f0 0bbeqb_2025; yes: prune walk-left
$201A88dey; check left neighbor
$201Bb1 5alda(zp_ptr_playfield_lo),y; read left neighbor flags
$201Dc8iny; restore Y
$201E29 80and#$80; left neighbor solid?
$2020d0 03bneb_2025; yes: prune walk-left
$20224c 2b 20jmpj_202B
$2025b1 5ab_2025lda(zp_ptr_playfield_lo),y; clear walk-left ($01) ; x-ref: $2018, $2020
$202729 feand#$fe
$202991 5asta(zp_ptr_playfield_lo),y
$202Bc0 20j_202Bcpy#$20; at right edge (col $20)? ; x-ref: $2022
$202Df0 0bbeqb_203A; yes: prune walk-right
$202Fc8iny; check right neighbor
$2030b1 5alda(zp_ptr_playfield_lo),y
$203288dey; restore Y
$203329 80and#$80; right neighbor solid?
$2035d0 03bneb_203A; yes: prune walk-right
$20374c 40 20jmpj_2040
$203Ab1 5ab_203Alda(zp_ptr_playfield_lo),y; clear walk-right ($02) ; x-ref: $202D, $2035
$203C29 fdand#$fd
$203E91 5asta(zp_ptr_playfield_lo),y
$2040a5 5bj_2040ldazp_ptr_playfield_hi; at last row? ; x-ref: $2037
$2042c9 7fcmp#$7f
$2044f0 0dbeqb_2053; yes: prune climb-down
$2046e6 5binczp_ptr_playfield_hi; peek at row below
$2048b1 5alda(zp_ptr_playfield_lo),y; read flags of cell below
$204Ac6 5bdeczp_ptr_playfield_hi; restore to dig row
$204C29 08and#$08; cell below has climb-down?
$204Ef0 03beqb_2053; no: prune climb-down
$20504c 59 20jmpj_2059
$2053b1 5ab_2053lda(zp_ptr_playfield_lo),y; clear climb-down ($08) ; x-ref: $2044, $204E
$205529 f7and#$f7
$205791 5asta(zp_ptr_playfield_lo),y
$205988j_2059dey; left neighbor column ; x-ref: $200B, $2050
$205Ab1 5alda(zp_ptr_playfield_lo),y; read left neighbor at dig row
$205C29 80and#$80; solid? skip
$205Ed0 0cbneb_206C
$2060b1 5alda(zp_ptr_playfield_lo),y; fall-through? skip
$206229 10and#$10
$2064d0 06bneb_206C; left neighbor can now walk right into open hole
$2066b1 5alda(zp_ptr_playfield_lo),y
$206809 02ora#$02
$206A91 5asta(zp_ptr_playfield_lo),y
$206Cc6 5bb_206Cdeczp_ptr_playfield_hi; advance to row−1 ; x-ref: $205E, $2064
$206Eb1 5alda(zp_ptr_playfield_lo),y; read left neighbor at row−1
$207029 80and#$80; solid? skip
$2072d0 0cbneb_2080
$2074b1 5alda(zp_ptr_playfield_lo),y; fall-through? skip
$207629 10and#$10
$2078d0 06bneb_2080; left neighbor at row−1 can now walk right into hole
$207Ab1 5alda(zp_ptr_playfield_lo),y
$207C09 02ora#$02
$207E91 5asta(zp_ptr_playfield_lo),y
$2080c8b_2080iny; col−1 → col → col+1 (right neighbor) ; x-ref: $2072, $2078
$2081c8iny
$2082b1 5alda(zp_ptr_playfield_lo),y; read right neighbor at row−1
$208429 80and#$80; solid? skip
$2086d0 0cbneb_2094
$2088b1 5alda(zp_ptr_playfield_lo),y; fall-through? skip
$208A29 10and#$10
$208Cd0 06bneb_2094; right neighbor at row−1 can now walk left into hole
$208Eb1 5alda(zp_ptr_playfield_lo),y
$209009 01ora#$01
$209291 5asta(zp_ptr_playfield_lo),y
$2094e6 5bb_2094inczp_ptr_playfield_hi; advance to dig row ; x-ref: $2086, $208C
$2096b1 5alda(zp_ptr_playfield_lo),y; read right neighbor at dig row
$209829 80and#$80; solid? skip
$209Ad0 0cbneb_20A8
$209Cb1 5alda(zp_ptr_playfield_lo),y; fall-through? skip
$209E29 10and#$10
$20A0d0 06bneb_20A8; right neighbor at dig row can now walk left into hole
$20A2b1 5alda(zp_ptr_playfield_lo),y
$20A409 01ora#$01
$20A691 5asta(zp_ptr_playfield_lo),y
$20A888b_20A8dey; restore Y to hole column ; x-ref: $209A, $20A0
$20A960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the movement property grid when a hole fully heals. Called from
; process_dig_animations when dig status reaches 4 (background restored).
; Inverse of update_hole_collision.
; ZP pointer a5A/a5B addresses the movement grid; Y = dig_slot_col[x] = column.
;
; Movement bit flags:
; $01=walk-left $02=walk-right $20=rope $40/$80=solid wall
;
; Row a69−2 (the now-restored floor brick):
; already solid ($C0 set) → nothing to restore, jump straight to j20F0
; not solid → restore walk-left+walk-right ($03), keep rope bit ($20)
; prune walk-right ($02) if right neighbor solid ($80) or at right edge (col=$20)
; prune walk-left ($01) if left neighbor solid ($80) or at left edge (col=$01)
;
; j20F0 — Rows a69−1 and a69 (the two hole rows):
; write $C0 (solid wall) to both cells — brick fully restored
;
; Neighbour fixup:
; left neighbor at a69−1 and a69: clear walk-right ($02)
; right neighbor at a69−1 and a69: clear walk-left ($01)
;
; AI pathfinding grid (a77/a78):
; zero 4 cells (left+right neighbors × 2 rows) to flush stale BFS direction codes
;
; Inputs: a69 (dig row page), dig_slot_col[x], movement grid a5A/a5B, AI grid a77/a78
; Outputs: Movement grid restored to solid; AI pathfinding grid cleared around hole
; Side Effects: a5B, a78 modified; Y left at col−1 on exit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_filled_hole_collision
$20AAa5 69ldazp_ptr_dig_screen_hi; dig row page ; x-ref: $1F90
$20AC85 5bstazp_ptr_playfield_hi; a5B = dig row
$20AEc6 5bdeczp_ptr_playfield_hi; a5B = dig row − 1
$20B0c6 5bdeczp_ptr_playfield_hi; start 2 rows above dig row
$20B2bc 57 1eldydig_slot_col,x; Y = hole column
$20B5b1 5alda(zp_ptr_playfield_lo),y; read movement flags at row−2
$20B729 c0and#$c0; solid ($40|$80)?
$20B9f0 03beqb_20BE; not solid: restore walk bits
$20BB4c f0 20jmpj_20F0; already solid: skip to hole-rows
$20BEb1 5ab_20BElda(zp_ptr_playfield_lo),y; keep rope bit ($20) only ; x-ref: $20B9
$20C029 20and#$20
$20C209 03ora#$03; restore walk-left ($01) + walk-right ($02)
$20C491 5asta(zp_ptr_playfield_lo),y
$20C6c8iny; check right neighbor
$20C7b1 5alda(zp_ptr_playfield_lo),y
$20C988dey; restore Y
$20CA29 80and#$80; right neighbor solid?
$20CCd0 07bneb_20D5; yes: prune walk-right
$20CEc0 20cpy#$20; at right edge (col $20)?
$20D0f0 03beqb_20D5; yes: prune walk-right
$20D24c db 20jmpj_20DB
$20D5b1 5ab_20D5lda(zp_ptr_playfield_lo),y; clear walk-right ($02) ; x-ref: $20CC, $20D0
$20D729 fdand#$fd
$20D991 5asta(zp_ptr_playfield_lo),y
$20DBc0 01j_20DBcpy#$01; at left edge (col 1)? ; x-ref: $20D2
$20DDf0 0bbeqb_20EA; yes: prune walk-left
$20DF88dey; check left neighbor
$20E0b1 5alda(zp_ptr_playfield_lo),y; read left neighbor flags
$20E2c8iny; restore Y
$20E329 80and#$80; left neighbor solid?
$20E5d0 03bneb_20EA; yes: prune walk-left
$20E74c f0 20jmpj_20F0
$20EAb1 5ab_20EAlda(zp_ptr_playfield_lo),y; clear walk-left ($01) ; x-ref: $20DD, $20E5
$20EC29 feand#$fe
$20EE91 5asta(zp_ptr_playfield_lo),y
$20F0a9 c0j_20F0lda#$c0; solid wall value ; x-ref: $20BB, $20E7
$20F2e6 5binczp_ptr_playfield_hi; advance to row−1
$20F491 5asta(zp_ptr_playfield_lo),y; seal row−1 as solid
$20F6e6 5binczp_ptr_playfield_hi; advance to dig row
$20F891 5asta(zp_ptr_playfield_lo),y; seal dig row as solid
$20FA88dey; left neighbor column
$20FBb1 5alda(zp_ptr_playfield_lo),y; read left neighbor at dig row
$20FD29 fdand#$fd; clear walk-right ($02) — hole is sealed, can't exit right
$20FF91 5asta(zp_ptr_playfield_lo),y
$2101c6 5bdeczp_ptr_playfield_hi; move to row−1
$2103b1 5alda(zp_ptr_playfield_lo),y; read left neighbor at row−1
$210529 fdand#$fd; clear walk-right ($02)
$210791 5asta(zp_ptr_playfield_lo),y
$2109c8iny; col−1 → col → col+1 (right neighbor)
$210Ac8iny
$210Bb1 5alda(zp_ptr_playfield_lo),y; read right neighbor at row−1
$210D29 feand#$fe; clear walk-left ($01) — hole is sealed, can't enter from right
$210F91 5asta(zp_ptr_playfield_lo),y
$2111e6 5binczp_ptr_playfield_hi; advance to dig row
$2113b1 5alda(zp_ptr_playfield_lo),y; read right neighbor at dig row
$211529 feand#$fe; clear walk-left ($01)
$211791 5asta(zp_ptr_playfield_lo),y
$2119a5 5bldazp_ptr_playfield_hi; a78 = dig row (AI grid high byte)
$211B85 78stazp_ptr_ai_grid_hi
$211Da9 00lda#$00; zero = clear BFS direction code
$211F91 77sta(zp_ptr_ai_grid_lo),y; zero right neighbor at dig row in AI grid; TXTPTR Pointer: Current Byte of BASIC Text
$2121c6 78deczp_ptr_ai_grid_hi; move to row−1
$212391 77sta(zp_ptr_ai_grid_lo),y; zero right neighbor at row−1 in AI grid; TXTPTR Pointer: Current Byte of BASIC Text
$212588dey; col+1 → col → col−1 (left neighbor)
$212688dey
$212791 77sta(zp_ptr_ai_grid_lo),y; zero left neighbor at row−1 in AI grid; TXTPTR Pointer: Current Byte of BASIC Text
$2129e6 78inczp_ptr_ai_grid_hi; advance to dig row
$212B91 77sta(zp_ptr_ai_grid_lo),y; zero left neighbor at dig row in AI grid; Y left at col−1; TXTPTR Pointer: Current Byte of BASIC Text
$212D60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Autotiling helper for the dig open-animation (called from process_dig_animations
; phase 1 for both the top and bottom cell characters).
;
; If A != $20 (space): returns A unchanged — only applies to empty cells.
;
; If A == $20: checks horizontal neighbours and returns a shadow/edge char:
; - Last row ($7F) special case:
; col 1 -> $4C (left floor-border char)
; other cols -> $64 (floor-border char)
; - Right neighbour (col+1) is $43/$63/$50 -> return $67 (right-edge shadow)
; - Left neighbour (col-1) is $43/$63/$4F -> return $65 (left-edge shadow)
; - At col 1 (no left neighbour possible) -> return $65 (implied left edge)
; - No solid neighbours -> return $20 (plain space)
;
; This gives holes visual depth by drawing shadow chars where the dug cell
; meets an adjacent solid tile.
;
; Inputs: A = animation char (from dig_open_anim_top/bot table),
; Y = column offset, a68/a69 = screen row pointer
; Outputs: A = display char (shadow, border, or original)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
get_adjacent_tile_border
$212Ec9 20cmp#$20; A != $20 (space)? pass through unchanged — only autotiles over empty cells ; x-ref: $1EEF, $1EFF
$2130d0 42bner_2174
$2132a5 69ldazp_ptr_dig_screen_hi; on last row ($7F)? use floor-border chars instead
$2134c9 7fcmp#$7f
$2136d0 0abneb_2142
$2138c0 01cpy#$01; col 1 (leftmost)? return $4C (left floor-border)
$213Ad0 03bneb_213F
$213Ca9 4clda#$4c
$213E60rts
$213Fa9 64b_213Flda#$64; any other col on last row: return $64 (floor-border) ; x-ref: $213A
$214160rts
$2142c8b_2142iny; --- check right neighbour (col+1) --- ; x-ref: $2136
$2143b1 68lda(zp_ptr_dig_screen_lo),y; peek tile at col+1
$214588dey; restore Y
$2146c9 43cmp#$43; solid tile to the right ($43/$63/$50)?
$2148f0 0bbeqb_2155
$214Ac9 63cmp#$63
$214Cf0 07beqb_2155
$214Ec9 50cmp#$50
$2150f0 03beqb_2155
$21524c 58 21jmpj_2158
$2155a9 67b_2155lda#$67; return $67: right-edge shadow char ; x-ref: $2148, $214C, $2150
$215760rts
$2158c0 01j_2158cpy#$01; --- check left neighbour (col-1) --- col 1? no left neighbour → still return left-edge char ; x-ref: $2152
$215Af0 13beqb_216F
$215C88dey; peek tile at col-1
$215Db1 68lda(zp_ptr_dig_screen_lo),y
$215Fc8iny; restore Y
$2160c9 43cmp#$43; solid tile to the left ($43/$63/$4F)?
$2162f0 0bbeqb_216F
$2164c9 63cmp#$63
$2166f0 07beqb_216F
$2168c9 4fcmp#$4f
$216Af0 03beqb_216F
$216C4c 72 21jmpj_2172
$216Fa9 65b_216Flda#$65; return $65: left-edge shadow char ; x-ref: $215A, $2162, $2166, $216A
$217160rts
$2172a9 20j_2172lda#$20; no solid neighbours: return $20 (plain space, no edge effect) ; x-ref: $216C
$217460r_2174rts; x-ref: $2130
; Guard sprite animation table (14 strips x 8 bytes = 112 bytes).
; Same structure as sprite_anim_table ($0D1D) but used exclusively for the 5 guard slots.
; Index = guard_sprite_strip + guard_anim_frame (both per-guard; guard_anim_frame = 0/4/8/$C).
; $20 = transparent cell (caller skips the sta).
;
; Strip layout (guard_sprite_strip values):
; $00 : Player walking right
; $08 : Player walking left
; $10 : Player climbing
; $18 : Player hanging / descending
; $20 : Guard walking left
; $28 : Guard walking left (alt)
; $30 : Guard walking right
; $38 : Guard walking right (alt)
; $40 : Guard falling / dropped
; $48 : Guard variant
; $50 : Guard variant
; $58 : Guard variant
; $60 : Guard dead / captured in hole
; $68 : Guard dead (alt)
$2175guard_sprite_table.byte$7b, $67, $2d, $20, $7b, $20, $54, $20; x-ref: $24B0, $24BB, $24C6, $24D1
$217D.byte$6c, $20, $6b, $20, $6c, $20, $48, $20
$2185.byte$7b, $20, $47, $20, $7b, $20, $73, $20
$218D.byte$6c, $20, $59, $20, $6c, $20, $2d, $65
$2195.byte$2e, $20, $18, $20, $2e, $20, $4e, $20
$219D.byte$2e, $20, $4d, $20, $2e, $20, $56, $20
$21A5.byte$2e, $20, $14, $20, $2e, $20, $1e, $20
$21AD.byte$2e, $20, $1e, $20, $2e, $20, $19, $20
$21B5.byte$7b, $67, $27, $20, $7b, $20, $19, $20
$21BD.byte$6c, $20, $2f, $20, $6c, $20, $19, $20
$21C5.byte$7b, $20, $19, $20, $7b, $20, $1c, $20
$21CD.byte$6c, $20, $19, $20, $6c, $20, $27, $65
$21D5.byte$2e, $20, $14, $20, $2e, $20, $14, $20
$21DD.byte$2e, $20, $14, $20, $2e, $20, $14, $20
; Guard sprite system — 5 guard slots (indices 0-4), parallel arrays.
;
; Background save (4 cells, T-shaped footprint per guard):
; guard_saved_top $21E5+x: top-center cell (page A, col y)
; guard_saved_bot_left $21EA+x: bottom-left cell (page B, col y-1)
; guard_saved_bot_mid $21EF+x: bottom-center cell (page B, col y)
; guard_saved_bot_right$21F4+x: bottom-right cell (page B, col y+1)
;
; Per-guard state:
; guard_active $21F9+x: $FF = slot empty/dead; other = alive
; guard_anim_frame $21FE+x: animation frame 0/4/8/$C (starts at $08)
; guard_offset $2203+x: screen column offset (X position)
; guard_page $2208+x: screen memory page (Y/row position)
; guard_sprite_strip$220D+x: strip selector into guard_sprite_table
; guard_dx $2212+x: horizontal move direction (+1=right, -1=left, 0=none)
; guard_dy $2217+x: vertical move direction (+1=down, -1=up, 0=none)
; guard_move_rate $221C+x: move pace countdown ($02=normal, $80=initial delay)
; guard_ai_bias $2221+x: AI horizontal direction bias counter
; guard_hole_status $2226+x: hole-trap status ($FF = free, other = trapped in hole)
$21E5guard_saved_top.byte$00, $00, $00, $00, $00; x-ref: $245B, $2492
; Saved background char: bottom-left cell (page B, col y-1) under guard sprite.
$21EAguard_saved_bot_left.byte$00, $00, $00, $00, $00; x-ref: $2461, $2498
; Saved background char: bottom-center cell (page B, col y) under guard sprite.
$21EFguard_saved_bot_mid.byte$00, $00, $00, $00, $00; x-ref: $2467, $249E
; Saved background char: bottom-right cell (page B, col y+1) under guard sprite.
guard_saved_bot_right
$21F4.byte$00, $00, $00, $00, $00; x-ref: $246D, $24A4
; Guard slot active flag. $FF = slot empty / guard dead. Other = alive and active.
$21F9guard_active.byte$ff, $ff, $ff, $ff, $ff; x-ref: $128C, $1295, $1C29, $1FAC, $223D, $23B5, $2449, $247E, ...
; Guard animation frame. Cycles 0->4->8->$C->0 (step +/-4, masked & $0C). Initialised to $08.
$21FEguard_anim_frame.byte$00, $00, $00, $00, $00; x-ref: $1FA3, $224F, $2268, $2270, $2298, $22A2, $22AB, $22B3, ...
; Guard screen column offset (X position). Inc on move-right, dec on move-left.
$2203guard_offset.byte$00, $00, $00, $00, $00; x-ref: $1299, $1C36, $1FBF, $2348, $2358, $239D, $23AD, $23E7, ...
; Guard screen memory page (Y/row position). Inc on move-down, dec on move-up.
$2208guard_page.byte$00, $00, $00, $00, $00; x-ref: $129F, $1C2E, $1FC5, $2275, $2285, $22B8, $22C8, $22FD, ...
; Guard sprite strip selector into guard_sprite_table.
; $00=idle-right, $20=left, $30=right, $40=fall, $60=dead/captured. Combine with guard_anim_frame.
$220Dguard_sprite_strip.byte$00, $00, $00, $00, $00; x-ref: $1FB9, $224A, $2265, $2291, $22A8, $22D4, $22ED, $2319, ...
; Guard horizontal move direction for current step. +1=right, -1=left, 0=not moving horizontally.
$2212guard_vspeed.byte$00, $00, $00, $00, $00; x-ref: $1C4C, $1C5C, $1C69, $1FA6, $2257, $23F3, $2D97
; Guard vertical move direction for current step. +1=down, -1=up, 0=not moving vertically.
$2217guard_hspeed.byte$00, $00, $00, $00, $00; x-ref: $1C54, $1C64, $1C72, $1FA9, $2254, $2400, $2D9A
; Guard movement pace countdown. Reloads to $02 each step; when it hits 0 the AI executes a move.
; Initialised to $80 to give guard a long pause before first move.
$221Cguard_move_rate.byte$00, $00, $00, $00, $00; x-ref: $1081, $1FB4, $225C, $23D0, $23D7, $2D9F
; Guard AI horizontal direction bias counter. Incremented/decremented by the pathfinding
; logic to favour one horizontal direction over another.
$2221guard_ai_bias.byte$00, $00, $00, $00, $00; x-ref: $1FAF, $2245, $23BC, $23C3, $23CD, $26F2, $2714, $272E, ...
; Guard hole-trap status. $FF = guard is free/not trapped. Other = guard is in a dug hole
; (value cross-links with hole_slot_* arrays via actor_hole_slot).
$2226guard_hole_status.byte$00, $00, $00, $00, $00; x-ref: $2240, $2718, $2757, $27CA
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all 5 guard slots at level load (called from $0745, just
; before init_hole_slots). Also sets up key zero-page pointers.
;
; Zero-page setup:
; a6E = $00 screen row pointer low byte (shared with a70)
; a70 = $00 screen row pointer low byte
; a5A = $2B movement grid column base offset ($xx2B)
; p60 = $55 pathfinding attribute RAM pointer low byte
;
; Guard slot defaults (X: 4..0):
; guard_active = $FF not yet spawned / inactive
; guard_hole_status = $FF not trapped in a hole
; guard_ai_bias = $00 no directional bias
; guard_sprite_strip = $00 default strip
; guard_anim_frame = $08 start at frame 8
; guard_vspeed = $00 stationary (vertical)
; guard_hspeed = $00 stationary (horizontal)
; guard_move_rate = $80 128-tick freeze before first move
; (vs normal $02 during gameplay) — gives
; the player a head-start window at level load
;
; Inputs: None
; Outputs: Guard arrays reset; zero-page pointers initialised
; Side Effects: a6E, a70, a5A, p60 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$222Ba9 00init_guard_arrayslda#$00; --- ZP pointer setup --- ; x-ref: $0745
$222D85 6estazp_ptr_guard_top_lo; a6E = $00 (screen row ptr low); FBUFPT Pointer: Cassette Buffer
$222F85 70stazp_ptr_guard_bot_lo; a70 = $00 (same); CHRGET Subroutine: Get Next Byte of BASIC Text
$2231a9 2blda#$2b; a5A = $2B (movement grid column base offset)
$223385 5astazp_ptr_playfield_lo
$2235a9 55lda#$55; p60 = $55 (pathfinding attr RAM pointer low byte)
$223785 60stazp_ptr_pathfinding_lo
$2239a2 04ldx#$04; --- init 5 guard slots (X: 4..0) ---
$223Ba9 ffb_223Blda#$ff; guard_active = $FF (inactive), guard_hole_status = $FF (not trapped) ; x-ref: $2260
$223D9d f9 21staguard_active,x
$22409d 26 22staguard_hole_status,x
$2243a9 00lda#$00; guard_ai_bias = 0
$22459d 21 22staguard_ai_bias,x
$2248a9 00lda#$00; guard_sprite_strip = 0 (default)
$224A9d 0d 22staguard_sprite_strip,x
$224Da9 08lda#$08; guard_anim_frame = $08
$224F9d fe 21staguard_anim_frame,x
$2252a9 00lda#$00; guard_hspeed = 0, guard_vspeed = 0 (stationary)
$22549d 17 22staguard_hspeed,x
$22579d 12 22staguard_vspeed,x
$225Aa9 80lda#$80; guard_move_rate = $80 (128-tick head-start delay)
$225C9d 1c 22staguard_move_rate,x
$225Fcadex; next guard
$226010 d9bplb_223B
$226260rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; GRAVITY FALL handler. Entered when movement grid bit $10 (fall-through) is
; set at (guard_page+1, guard_offset) — guard is over open air or a hole.
; Strip forced to $30 (walk-right). Advances frame +4 each tick (wraps 0-$C).
; Position (guard_page) incremented only when frame wraps to 0 (~4 ticks/cell).
; Collision check after page inc; rolls back on guard-guard or guard-hole hit.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2263a9 30guard_gravity_falllda#$30; strip=$30 (falling) ; x-ref: $23F0
$22659d 0d 22staguard_sprite_strip,x
$2268bd fe 21ldaguard_anim_frame,x; advance frame +4, wrap within 0-$C
$226B18clc
$226C69 04adc#$04
$226E29 0cand#$0c
$22709d fe 21staguard_anim_frame,x
$2273d0 0dbneb_2282; frame wrapped to 0 → move one row down
$2275fe 08 22incguard_page,x; collision check; roll back if hit
$227820 1b 28jsrcheck_guard_guard_collision
$227Bf0 08beqb_2285
$227D20 3c 28jsrcheck_guard_hole_collision
$2280f0 03beqb_2285
$22824c dd 23b_2282jmpguard_move_epilogue; x-ref: $2273
$2285de 08 22b_2285decguard_page,x; rollback: undo page inc ; x-ref: $227B, $2280
$22884c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; MOVE DOWN handler (guard_vspeed > 0, grid bits $08+$10 clear).
; If bits $08 or $10 are set: strip=$20, advance frame +4, inc page on wrap.
; If not: only animate (strip must be $20, stop when frame wraps to 0).
; Collision check after page inc; rolls back on hit.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$228Bb1 5aguard_move_downlda(zp_ptr_playfield_lo),y; bits $08+$10: climb-down or fall-through accessible? ; x-ref: $23FA
$228D29 18and#$18
$228Fd0 15bneb_22A6; no: animate only if strip already $20, stop on wrap
$2291bd 0d 22ldaguard_sprite_strip,x
$2294c9 20cmp#$20
$2296d0 2dbneb_22C5
$2298bd fe 21ldaguard_anim_frame,x
$229B18clc
$229C69 04adc#$04
$229E29 0cand#$0c
$22A0f0 23beqb_22C5
$22A29d fe 21staguard_anim_frame,x
$22A560rts; rts early: animate only, don't advance position
$22A6a9 20b_22A6lda#$20; yes: strip=$20, advance frame +4 ; x-ref: $228F
$22A89d 0d 22staguard_sprite_strip,x
$22ABbd fe 21ldaguard_anim_frame,x
$22AE18clc
$22AF69 04adc#$04
$22B129 0cand#$0c
$22B39d fe 21staguard_anim_frame,x
$22B6d0 0dbneb_22C5; frame wrapped to 0 → move one row down
$22B8fe 08 22incguard_page,x
$22BB20 1b 28jsrcheck_guard_guard_collision
$22BEf0 08beqb_22C8
$22C020 3c 28jsrcheck_guard_hole_collision
$22C3f0 03beqb_22C8
$22C54c dd 23b_22C5jmpguard_move_epilogue; x-ref: $2296, $22A0, $22B6
$22C8de 08 22b_22C8decguard_page,x; rollback: undo page inc ; x-ref: $22BE, $22C3
$22CB4c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; MOVE UP handler (guard_vspeed < 0, grid bit $04 clear).
; If bit $04 set: strip=$20, advance frame -4, dec page when frame underflows
; to 0 (i.e. result==0 after sbc+mask).
; If not: only animate (strip must be $20, stop when frame reaches $0C).
; Collision check after page dec; rolls back on hit.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$22CEb1 5aguard_move_uplda(zp_ptr_playfield_lo),y; bit $04: climb-up accessible? ; x-ref: $23FD
$22D029 04and#$04
$22D2d0 17bneb_22EB; no: animate only if strip already $20, stop when frame reaches $0C
$22D4bd 0d 22ldaguard_sprite_strip,x
$22D7c9 20cmp#$20
$22D9d0 2fbneb_230A
$22DBbd fe 21ldaguard_anim_frame,x
$22DE38sec
$22DFe9 04sbc#$04
$22E129 0cand#$0c
$22E3c9 0ccmp#$0c
$22E5f0 23beqb_230A
$22E79d fe 21staguard_anim_frame,x
$22EA60rts; rts early: animate only, don't advance position
$22EBa9 20b_22EBlda#$20; yes: strip=$20, advance frame -4 ; x-ref: $22D2
$22ED9d 0d 22staguard_sprite_strip,x
$22F0bd fe 21ldaguard_anim_frame,x
$22F338sec
$22F4e9 04sbc#$04
$22F629 0cand#$0c
$22F89d fe 21staguard_anim_frame,x
$22FBd0 0dbneb_230A; frame reached 0 → move one row up
$22FDde 08 22decguard_page,x
$230020 1b 28jsrcheck_guard_guard_collision
$2303f0 08beqb_230D
$230520 3c 28jsrcheck_guard_hole_collision
$2308f0 03beqb_230D
$230A4c dd 23b_230Ajmpguard_move_epilogue; x-ref: $22D9, $22E5, $22FB
$230Dfe 08 22b_230Dincguard_page,x; rollback: undo page dec ; x-ref: $2303, $2308
$23104c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; MOVE RIGHT handler (guard_hspeed > 0, grid bit $02 clear).
; If bit $02 set: strip=$00 (floor) or $40 (rope, if bit $20 also set),
; advance frame +4, inc offset on wrap.
; If not: only animate (strip must be $00 or $40, stop when frame wraps to 0).
; Collision check after offset inc; rolls back on hit.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2313b1 5aguard_move_rightlda(zp_ptr_playfield_lo),y; bit $02: walk-right accessible? ; x-ref: $2407
$231529 02and#$02
$2317d0 17bneb_2330; no: animate only if strip $00 or $40, stop on wrap to 0
$2319bd 0d 22ldaguard_sprite_strip,x
$231Cf0 04beqb_2322
$231Ec9 40cmp#$40
$2320d0 33bneb_2355
$2322bd fe 21b_2322ldaguard_anim_frame,x; x-ref: $231C
$232518clc
$232669 04adc#$04
$232829 0cand#$0c
$232Af0 29beqb_2355
$232C9d fe 21staguard_anim_frame,x
$232F60rts; rts early: animate only
$2330b1 5ab_2330lda(zp_ptr_playfield_lo),y; yes: bit $20 set → on rope → strip=$40; else strip=$00 (floor) ; x-ref: $2317
$233229 20and#$20
$2334f0 02beqb_2338
$2336a9 40lda#$40
$23389d 0d 22b_2338staguard_sprite_strip,x; x-ref: $2334
$233Bbd fe 21ldaguard_anim_frame,x
$233E18clc
$233F69 04adc#$04
$234129 0cand#$0c
$23439d fe 21staguard_anim_frame,x
$2346d0 0dbneb_2355; frame wrapped to 0 → move one col right
$2348fe 03 22incguard_offset,x
$234B20 1b 28jsrcheck_guard_guard_collision
$234Ef0 08beqb_2358
$235020 3c 28jsrcheck_guard_hole_collision
$2353f0 03beqb_2358
$23554c dd 23b_2355jmpguard_move_epilogue; x-ref: $2320, $232A, $2346
$2358de 03 22b_2358decguard_offset,x; rollback: undo offset inc ; x-ref: $234E, $2353
$235B4c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; MOVE LEFT handler (guard_hspeed < 0, grid bit $01 clear).
; If bit $01 set: strip=$10 (floor) or $50 (rope, if bit $20 also set),
; advance frame -4, dec offset when frame underflows to $0C.
; If not: only animate (strip must be $10 or $50, stop when frame reaches $0C).
; Collision check after offset dec; rolls back on hit.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$235Eb1 5aguard_move_leftlda(zp_ptr_playfield_lo),y; bit $01: walk-left accessible? ; x-ref: $240A
$236029 01and#$01
$2362d0 1bbneb_237F; no: animate only if strip $10 or $50, stop when frame reaches $0C
$2364bd 0d 22ldaguard_sprite_strip,x
$2367c9 10cmp#$10
$2369f0 04beqb_236F
$236Bc9 50cmp#$50
$236Dd0 3bbneb_23AA
$236Fbd fe 21b_236Fldaguard_anim_frame,x; x-ref: $2369
$237238sec
$2373e9 04sbc#$04
$237529 0cand#$0c
$2377c9 0ccmp#$0c
$2379f0 2fbeqb_23AA
$237B9d fe 21staguard_anim_frame,x
$237E60rts; rts early: animate only
$237Fb1 5ab_237Flda(zp_ptr_playfield_lo),y; yes: bit $20 set → on rope → strip=$50; else strip=$10 (floor) ; x-ref: $2362
$238129 20and#$20
$2383f0 04beqb_2389
$2385a9 50lda#$50
$2387d0 02bneb_238B
$2389a9 10b_2389lda#$10; x-ref: $2383
$238B9d 0d 22b_238Bstaguard_sprite_strip,x; x-ref: $2387
$238Ebd fe 21ldaguard_anim_frame,x
$239138sec
$2392e9 04sbc#$04
$239429 0cand#$0c
$23969d fe 21staguard_anim_frame,x
$2399c9 0ccmp#$0c
$239Bd0 0dbneb_23AA; frame underflowed to $0C → move one col left
$239Dde 03 22decguard_offset,x
$23A020 1b 28jsrcheck_guard_guard_collision
$23A3f0 08beqb_23AD
$23A520 3c 28jsrcheck_guard_hole_collision
$23A8f0 03beqb_23AD
$23AA4c dd 23b_23AAjmpguard_move_epilogue; x-ref: $236D, $2379, $239B
$23ADfe 03 22b_23ADincguard_offset,x; rollback: undo offset dec ; x-ref: $23A3, $23A8
$23B04c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main guard movement engine. Called once per gameplay IRQ for all 5 guards
; (X: 4..0). Sub-handlers j2263/j228B/j22CE/j2313/j235E are jumped to
; (not JSR'd) and share the common epilogue at j23DD (dex/bpl/rts).
;
; Per-guard each frame:
;
; 1. AI bias decay: guard_ai_bias drifts toward 0 each tick
; (positive: dec, negative: inc, +-$FF clamped).
;
; 2. Movement timer: guard_move_rate counts down, reloads to $02 on
; expiry. Guards only move every 2 frames.
;
; 3. Movement dispatch (on timer expiry):
; Read movement grid at (guard_page+1, guard_offset):
;
; grid $10 set -> j2263 GRAVITY FALL: strip=$30, frame+4;
; on wrap: inc guard_page, check collisions
; guard_vspeed > 0 -> j228B MOVE DOWN: grid $08/$10 check; strip=$20,
; frame+4; on wrap: inc guard_page
; guard_vspeed < 0 -> j22CE MOVE UP: grid $04 check; strip=$20,
; frame-4; on wrap: dec guard_page
; guard_hspeed > 0 -> j2313 MOVE RIGHT: grid $02 check; strip=$40/$00,
; frame+4; on wrap: inc guard_offset
; guard_hspeed < 0 -> j235E MOVE LEFT: grid $01 check; strip=$10/$50,
; frame-4; on wrap: dec guard_offset
; vspeed=hspeed=0 -> inline IDLE: animate frame based on sprite_strip
;
; Collision rollback: every handler calls check_guard_guard_collision
; and check_guard_hole_collision after advancing position; rolls back
; if either returns non-zero.
;
; Inputs: guard_active/page/offset/vspeed/hspeed/move_rate/ai_bias/
; sprite_strip/anim_frame[0..4], movement grid a5A/a5B
; Outputs: Guard positions, animation frames and strips updated
; Side Effects: check_guard_*_collision called; a5B modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
process_guard_movement
$23B3a2 04ldx#$04; process 5 guards ; x-ref: $0668
$23B5bd f9 21b_23B5ldaguard_active,x; skip inactive guards ($FF) ; x-ref: $23DE
$23B8c9 ffcmp#$ff
$23BAf0 21beqguard_move_epilogue
$23BCbd 21 22ldaguard_ai_bias,x; --- AI bias decay: drift toward 0 each tick ---
$23BFf0 0fbeqb_23D0
$23C130 06bmib_23C9
$23C3de 21 22decguard_ai_bias,x; positive bias: decrement
$23C64c d0 23jmpb_23D0
$23C9c9 ffb_23C9cmp#$ff; negative bias: $FF is clamped floor, else increment ; x-ref: $23C1
$23CBf0 03beqb_23D0
$23CDfe 21 22incguard_ai_bias,x
$23D0de 1c 22b_23D0decguard_move_rate,x; --- movement timer: decrement; skip movement if not expired --- ; x-ref: $23BF, $23C6, $23CB
$23D3d0 08bneguard_move_epilogue
$23D5a9 02lda#$02; reload timer = 2 (move every 2 frames)
$23D79d 1c 22staguard_move_rate,x
$23DA4c e1 23jmpguard_movement_dispatch
$23DDcaguard_move_epiloguedex; x-ref: $2282, $2288, $22C5, $22CB, $230A, $2310, $2355, $235B, ...
$23DE10 d5bplb_23B5
$23E060rts
guard_movement_dispatch
$23E1bc 08 22ldyguard_page,x; set grid pointer to guard_page+1 ; x-ref: $23DA
$23E4c8iny
$23E584 5bstyzp_ptr_playfield_hi
$23E7bc 03 22ldyguard_offset,x
$23EAb1 5alda(zp_ptr_playfield_lo),y; bit $10 set → gravity fall
$23EC29 10and#$10
$23EEf0 03beqb_23F3
$23F04c 63 22jmpguard_gravity_fall
$23F3bd 12 22b_23F3ldaguard_vspeed,x; vspeed > 0 → move down; vspeed < 0 → move up ; x-ref: $23EE
$23F6f0 08beqb_2400
$23F830 03bmib_23FD
$23FA4c 8b 22jmpguard_move_down
$23FD4c ce 22b_23FDjmpguard_move_up; x-ref: $23F8
$2400bd 17 22b_2400ldaguard_hspeed,x; hspeed > 0 → move right; hspeed < 0 → move left ; x-ref: $23F6
$2403f0 08beqb_240D
$240530 03bmib_240A
$24074c 13 23jmpguard_move_right
$240A4c 5e 23b_240Ajmpguard_move_left; x-ref: $2405
$240Dbd 0d 22b_240Dldaguard_sprite_strip,x; --- idle: vspeed=hspeed=0; animate based on strip --- ; x-ref: $2403
$2410d0 0bbneb_241D
$2412bd fe 21ldaguard_anim_frame,x; strip=$00: set bit 2 of frame (idle breathing)
$241509 04ora#$04
$24179d fe 21staguard_anim_frame,x
$241A4c dd 23jmpguard_move_epilogue
$241Dc9 10b_241Dcmp#$10; strip=$10: keep only bit 3 of frame ; x-ref: $2410
$241Fd0 0bbneb_242C
$2421bd fe 21ldaguard_anim_frame,x
$242429 08and#$08
$24269d fe 21staguard_anim_frame,x
$24294c dd 23jmpguard_move_epilogue
$242Cc9 20b_242Ccmp#$20; strip=$20: no idle animation ; x-ref: $241F
$242Ed0 03bneb_2433
$24304c dd 23jmpguard_move_epilogue
$2433c9 30b_2433cmp#$30; strip=$30: reset frame to 0 ; x-ref: $242E
$2435d0 08bneb_243F
$2437a9 00lda#$00
$24399d fe 21staguard_anim_frame,x
$243C4c dd 23jmpguard_move_epilogue
$243Fa9 60b_243Flda#$60; any other strip → switch to $60 (dead/captured) ; x-ref: $2435
$24419d 0d 22staguard_sprite_strip,x
$24444c dd 23jmpguard_move_epilogue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Erases the 5 Guards from the screen by restoring the background PETSCII characters
; that were saved underneath them during the previous frame.
; Iterates over all active guards and writes their 4 saved background characters (top,
; bottom-left, bottom-mid, bottom-right) back to the screen buffer.
;
; Inputs: Guard arrays (page, offset, saved background arrays)
; Outputs: Restores screen memory.
; Side Effects: Modifies zero page drawing pointers $6F and $71.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
restore_guard_backgrounds
$2447a2 00ldx#$00; x-ref: $0659
$2449bd f9 21b_2449ldaguard_active,x; x-ref: $2475
$244Cc9 ffcmp#$ff
$244Ef0 22beqb_2472
$2450bc 08 22ldyguard_page,x
$245384 6fstyzp_ptr_guard_top_hi
$2455c8iny
$245684 71styzp_ptr_guard_bot_hi
$2458bc 03 22ldyguard_offset,x
$245Bbd e5 21ldaguard_saved_top,x
$245E91 6esta(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer
$246088dey
$2461bd ea 21ldaguard_saved_bot_left,x
$246491 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$2466c8iny
$2467bd ef 21ldaguard_saved_bot_mid,x
$246A91 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$246Cc8iny
$246Dbd f4 21ldaguard_saved_bot_right,x
$247091 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$2472e8b_2472inx; x-ref: $244E
$2473e0 05cpx#$05
$2475d0 d2bneb_2449
$247760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws all 5 guards onto the screen (X: 4..0 via a76 counter).
; Inactive guards ($FF) are skipped.
;
; Each guard sprite is a T-shape spanning 2 rows and 3 columns:
;
; col-1 col col+1
; +-------+------+-------+
; | | [top]| | row = guard_page
; +-------+------+-------+
; | [bl] | [bm] | [br] | row+1 = guard_page+1
; +-------+------+-------+
;
; For each guard:
; 1. SAVE: reads the 4 screen chars at those positions into
; guard_saved_top / guard_saved_bot_left/mid/right (for later restore).
; 2. DRAW: looks up 4 chars from guard_sprite_table indexed by
; (guard_sprite_strip + guard_anim_frame). Any char == $20 (space)
; is transparent and left unwritten, preserving background tiles.
;
; Inputs: guard_active/page/offset/sprite_strip/anim_frame[0..4],
; guard_sprite_table
; Outputs: guard_saved_top/bot_left/bot_mid/bot_right updated;
; screen memory modified at guard positions
; Side Effects: ZP a76 used as loop counter; ZP a6E-a71 used as screen pointers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2478a2 04draw_guardsldx#$04; x-ref: $0689, $13F8
$247A86 76stxzp_guard_draw_idx; CHRGOT Entry to Get Same Byte of Text Again
$247Ca6 76b_247Cldxzp_guard_draw_idx; x-ref: $24DC CHRGOT Entry to Get Same Byte of Text Again
$247Ebd f9 21ldaguard_active,x
$2481c9 ffcmp#$ff
$2483f0 55beqb_24DA
$2485bc 08 22ldyguard_page,x
$248884 6fstyzp_ptr_guard_top_hi
$248Ac8iny
$248B84 71styzp_ptr_guard_bot_hi
$248Dbc 03 22ldyguard_offset,x
$2490b1 6elda(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer
$24929d e5 21staguard_saved_top,x
$249588dey
$2496b1 70lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$24989d ea 21staguard_saved_bot_left,x
$249Bc8iny
$249Cb1 70lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$249E9d ef 21staguard_saved_bot_mid,x
$24A1c8iny
$24A2b1 70lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$24A49d f4 21staguard_saved_bot_right,x
$24A788dey
$24A8bd 0d 22ldaguard_sprite_strip,x
$24AB18clc
$24AC7d fe 21adcguard_anim_frame,x
$24AFaatax
$24B0bd 75 21ldaguard_sprite_table,x
$24B3c9 20cmp#$20
$24B5f0 02beqb_24B9
$24B791 6esta(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer
$24B9e8b_24B9inx; x-ref: $24B5
$24BA88dey
$24BBbd 75 21ldaguard_sprite_table,x
$24BEc9 20cmp#$20
$24C0f0 02beqb_24C4
$24C291 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$24C4e8b_24C4inx; x-ref: $24C0
$24C5c8iny
$24C6bd 75 21ldaguard_sprite_table,x
$24C9c9 20cmp#$20
$24CBf0 02beqb_24CF
$24CD91 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$24CFe8b_24CFinx; x-ref: $24CB
$24D0c8iny
$24D1bd 75 21ldaguard_sprite_table,x
$24D4c9 20cmp#$20
$24D6f0 02beqb_24DA
$24D891 70sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text
$24DAc6 76b_24DAdeczp_guard_draw_idx; x-ref: $2483, $24D6 CHRGOT Entry to Get Same Byte of Text Again
$24DC10 9ebplb_247C
$24DE60rts
; Number of gold pieces collected by the player this level.
; Compared to gold_total to detect level completion (all gold collected -> call reset_bonus_timer).
$24DFgold_count.byte$00; x-ref: $06F8, $0736, $25FE, $264D, $29CF, $29FA
; Total number of gold pieces placed on the level.
; Set to $00 by clear_sprite_slots; incremented once per gold piece during level loading.
; Level is complete when gold_count == gold_total.
$24E0gold_total.byte$00; x-ref: $126E, $2534, $2650, $29C8, $29DD, $29EE
; Gold sprite system — 16 slots (indices 0-15), parallel arrays.
; Each slot tracks one gold chest piece on the current level.
;
; gold_active_flag $24E1+x: $FF=slot empty, $00=gold visible/collectible,
; $FE (any bmi)=collected (two-dec trick: $00->$FF->$FE)
; gold_page $24F1+x: Screen page (row) of this gold piece.
; gold_offset $2501+x: Screen column offset of this gold piece.
; gold_saved_bg $2511+x: Background char saved before drawing gold chest ($68).
; Init $20 (space). Restored when slot is erased.
;
; Call order each frame:
; 1. draw_sprites ($0656) - restores gold_saved_bg to screen (erases old gold)
; 2. (player moves)
; 3. save_sprite_background ($068C) - saves bg at current pos, draws char $68 (gold chest)
$24E1gold_active_flag.fill16, $00; x-ref: $1273, $2525, $253A, $2554, $25E0, $25F8, $25FB, $26E4, ...
; Screen memory page (row) for each gold piece. Loaded from level data during unpack.
; Collision check: f24F1,x - 1 == draw_page.
$24F1gold_page.fill16, $00; x-ref: $1279, $253F, $2559, $25ED, $260F, $26FA, $276C, $27DC, ...
; Screen column offset for each gold piece. Stored as raw_byte - $20 during unpack.
; Collision check: f2501,x == draw_offset.
$2501gold_offset.fill16, $00; x-ref: $126B, $2544, $255E, $25E5, $2615, $2705, $2763, $27D6
; Background screen char saved under each gold piece before drawing.
; Init $20 (space). Updated by save_sprite_background; restored by draw_sprites.
$2511gold_saved_bg.fill16, $00; x-ref: $252A, $2547, $2563
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all 16 slots of the Gold arrays for a new level.
; Iterates through all 16 index slots and marks them as inactive ($FF), and places
; a space character ($20) into the saved background array for each slot.
; It formally resets the player's gold_total to zero and clears ZP screen pointer $6A.
;
; Inputs: None
; Outputs: Modifies gold_active_flag, gold_saved_bg, and gold_total.
; Side Effects: Modifies ZP $6A.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2521a2 0finit_gold_arraysldx#$0f; x-ref: $070E, $0742
$2523a9 ffb_2523lda#$ff; x-ref: $252E
$25259d e1 24stagold_active_flag,x
$2528a9 20lda#$20
$252A9d 11 25stagold_saved_bg,x
$252Dcadex
$252E10 f3bplb_2523
$2530a9 00lda#$00
$253285 6astazp_ptr_gold_screen_lo
$25348d e0 24stagold_total
$253760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Erases the 16 active Gold instances from the screen memory so they can be
; physically updated/moved. It restores the background PETSCII character
; (stored in gold_saved_bg) at each gold piece's location.
;
; Inputs: gold arrays (active flag, page, offset, saved background)
; Outputs: Modifies screen memory to restore geometry under gold.
; Side Effects: Modifies pointer $6A/$6B.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
restore_gold_backgrounds
$2538a2 00ldx#$00; x-ref: $0656
$253Abd e1 24b_253Aldagold_active_flag,x; x-ref: $254F
$253D30 0dbmib_254C
$253Fbd f1 24ldagold_page,x
$254285 6bstazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign
$2544bc 01 25ldygold_offset,x
$2547bd 11 25ldagold_saved_bg,x
$254A91 6asta(zp_ptr_gold_screen_lo),y
$254Ce8b_254Cinx; x-ref: $253D
$254De0 10cpx#$10
$254Fd0 e9bneb_253A
$255160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the active gold pieces onto the screen.
; To prevent permanently destroying the level geometry (such as when gold falls
; down an open cavern), it reads the background char from the screen and stores it
; in gold_saved_bg. Then, it explicitly writes the Gold PETSCII character ($68)
; onto the screen buffer.
;
; Inputs: gold arrays (active flag, page, offset)
; Outputs: Saves background and draws the Gold pieces to screen memory.
; Side Effects: Modifies pointer $6A/$6B.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2552a2 0fdraw_goldldx#$0f; x-ref: $068C, $13FB
$2554bd e1 24b_2554ldagold_active_flag,x; x-ref: $256B
$255730 11bmib_256A
$2559bd f1 24ldagold_page,x
$255C85 6bstazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign
$255Ebc 01 25ldygold_offset,x
$2561b1 6alda(zp_ptr_gold_screen_lo),y
$25639d 11 25stagold_saved_bg,x
$2566a9 68lda#$68
$256891 6asta(zp_ptr_gold_screen_lo),y
$256Acab_256Adex; x-ref: $2557
$256B10 e7bplb_2554
$256D60rts
; Floating "+XX" score display shown over a collected gold piece.
;
; float_score_timer $256E: countdown ($FF=inactive, $50=just triggered, ticks to 0).
; Set $FF by init_floating_score, $50 on gold collection.
; float_score_page $256F: screen page (row) of the display position.
; float_score_offset $2570: screen column of the display position (gold col - 1).
; float_score_char0 $2571: char at col+0 (background save or '+' $2B)
; float_score_char1 $2572: char at col+1 (background save or tens digit)
; float_score_char2 $2573: char at col+2 (background save or units digit)
; float_score_bcd $2574: BCD byte from game_timer_bcd; upper nibble = tens digit,
; lower nibble = units digit (both rendered as $30 + digit).
;
; Call order each frame:
; draw_floating_score ($065F) - writes float_score_char0-2 to screen
; tick_floating_score ($0683) - reads screen background into char0-2,
; overwrites screen with '+', tens, units
$256Efloat_score_timer.byte$00; x-ref: $2577, $257B, $259A, $259F, $2606, $2672
; Screen page (row) where the floating '+XX' score is drawn. Set from gold_page on collection.
$256Ffloat_score_page.byte$00; x-ref: $2580, $25A4, $2612, $267E, $2681
; Screen column where the floating '+XX' score is drawn. Set to gold_offset - 1 (left-aligned).
$2570float_score_offset.byte$00; x-ref: $2585, $25A9, $2618, $261B, $2687, $268A, $268D, $2690
; Char buffer at col+0 of floating score ('+' $2B when active, saved bg otherwise).
$2571float_score_char0.byte$00; x-ref: $2588, $25AE
; Char buffer at col+1 of floating score (BCD tens digit: (float_score_bcd >> 4) + $30).
$2572float_score_char1.byte$00; x-ref: $258E, $25B4
; Char buffer at col+2 of floating score (BCD units digit: (float_score_bcd & $0F) + $30).
$2573float_score_char2.byte$00; x-ref: $2594, $25BA
; BCD byte copied from game_timer_bcd when gold is collected.
; Upper nibble = tens, lower nibble = units; displayed as two PETSCII decimal digits.
$2574float_score_bcd.byte$00; x-ref: $25C4, $25D3, $260C, $2678
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the system that handles the floating '+XX' score popups.
; This is called at the start of a level to ensure no score popup is actively
; trying to render. It simply writes $FF into the timer to turn it off.
;
; Inputs: None
; Outputs: Sets float_score_timer to $FF
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2575a9 ffinit_floating_scorelda#$ff; x-ref: $074B
$25778d 6e 25stafloat_score_timer
$257A60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; ERASE phase (called at start of IRQ loop).
; Restores the 3 background chars saved by tick_floating_score, effectively
; erasing the "+XX" floating score overlay from the screen.
; Does nothing if float_score_timer is negative (popup inactive).
;
; Inputs: float_score_timer, float_score_page/offset, float_score_char0/1/2
; Outputs: Screen memory restored at (float_score_page, float_score_offset+0..2)
; Side Effects: ZP a6A/a6B used as screen pointer
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$257Bad 6e 25erase_floating_scoreldafloat_score_timer; x-ref: $065F
$257E30 19bmir_2599
$2580ad 6f 25ldafloat_score_page
$258385 6bstazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign
$2585ac 70 25ldyfloat_score_offset
$2588ad 71 25ldafloat_score_char0
$258B91 6asta(zp_ptr_gold_screen_lo),y
$258Dc8iny
$258Ead 72 25ldafloat_score_char1
$259191 6asta(zp_ptr_gold_screen_lo),y
$2593c8iny
$2594ad 73 25ldafloat_score_char2
$259791 6asta(zp_ptr_gold_screen_lo),y
$259960r_2599rts; x-ref: $257E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; DRAW phase (called at end of IRQ loop).
; Decrements float_score_timer. When it reaches zero the popup expires.
; While active: saves the 3 screen chars under the popup into
; float_score_char0/1/2, then overwrites them with "+XX" where XX are the
; two BCD decimal digits of float_score_bcd (the bonus value awarded).
;
; Display format: + [tens] [units]
; e.g. float_score_bcd=$25 → "+25"
;
; Activated by check_gold_collection (timer=$50, bcd=game_timer_bcd) and
; check_level_complete_bonus (same values, at exit tile position).
;
; Inputs: float_score_timer, float_score_page/offset, float_score_bcd
; Outputs: float_score_char0/1/2 updated; screen memory overwritten with "+XX"
; Side Effects: ZP a6A/a6B used as screen pointer
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$259Aad 6e 25tick_floating_scoreldafloat_score_timer; x-ref: $0683
$259D30 3ebmir_25DD
$259Fce 6e 25decfloat_score_timer
$25A230 39bmir_25DD
$25A4ad 6f 25ldafloat_score_page
$25A785 6bstazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign
$25A9ac 70 25ldyfloat_score_offset
$25ACb1 6alda(zp_ptr_gold_screen_lo),y
$25AE8d 71 25stafloat_score_char0
$25B1c8iny
$25B2b1 6alda(zp_ptr_gold_screen_lo),y
$25B48d 72 25stafloat_score_char1
$25B7c8iny
$25B8b1 6alda(zp_ptr_gold_screen_lo),y
$25BA8d 73 25stafloat_score_char2
$25BD88dey
$25BE88dey
$25BFa9 2blda#$2b
$25C191 6asta(zp_ptr_gold_screen_lo),y
$25C3c8iny
$25C4ad 74 25ldafloat_score_bcd
$25C729 f0and#$f0
$25C94alsra
$25CA4alsra
$25CB4alsra
$25CC4alsra
$25CD18clc
$25CE69 30adc#$30
$25D091 6asta(zp_ptr_gold_screen_lo),y
$25D2c8iny
$25D3ad 74 25ldafloat_score_bcd
$25D629 0fand#$0f
$25D818clc
$25D969 30adc#$30
$25DB91 6asta(zp_ptr_gold_screen_lo),y
$25DD60r_25DDrts; x-ref: $259D, $25A2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks player/gold collisions for all 16 gold slots (X: 15..0).
;
; Collision condition (both must match):
; gold_offset,x == draw_offset (same column)
; gold_page,x == draw_page + 1 (gold on bottom row of 2-row player sprite)
;
; On a hit:
; 1. Double-dec gold_active_flag: $00 -> $FF -> $FE (marks slot inactive, bmi-safe)
; 2. Increment gold_count.
; 3. JSR trigger_collection_sound.
; 4. Arm floating score popup:
; float_score_timer = $50 (~80 frames)
; float_score_bcd = game_timer_bcd (value shown as "+XX")
; float_score_page/offset positioned left of gold piece
; 5. BCD add game_timer_bcd to score_low_bcd:score_high_bcd (displayed score).
; 6. BCD add game_timer_bcd to score_penalty_low_bcd:score_penalty_high_bcd
; (death-penalty accumulator — subtracted from score if player dies before
; next level start, then zeroed by handle_player_death).
;
; After all slots: if gold_count == gold_total -> JSR reset_bonus_timer
; (all gold collected; triggers level-complete sequence).
;
; Inputs: draw_page, draw_offset, gold_active_flag/page/offset arrays,
; game_timer_bcd, gold_count, gold_total
; Outputs: gold_active_flag updated; gold_count incremented;
; score_low/high_bcd and score_penalty_low/high_bcd updated in BCD;
; float_score_* variables armed
; Side Effects: trigger_collection_sound called; reset_bonus_timer called when
; all gold collected; Decimal flag toggled (sed/cld)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_gold_collection
$25DEa2 0fldx#$0f; x-ref: $0677
$25E0bd e1 24b_25E0ldagold_active_flag,x; x-ref: $264B
$25E330 65bmib_264A
$25E5bd 01 25ldagold_offset,x
$25E8cd b4 0dcmpdraw_offset
$25EBd0 5dbneb_264A
$25EDbd f1 24ldagold_page,x
$25F038sec
$25F1e9 01sbc#$01
$25F3cd b5 0dcmpdraw_page
$25F6d0 52bneb_264A
$25F8de e1 24decgold_active_flag,x
$25FBde e1 24decgold_active_flag,x
$25FEee df 24incgold_count
$260120 c7 0cjsrtrigger_collection_sound
$2604a9 50lda#$50
$26068d 6e 25stafloat_score_timer
$2609ad 49 29ldagame_timer_bcd
$260C8d 74 25stafloat_score_bcd
$260Fbd f1 24ldagold_page,x
$26128d 6f 25stafloat_score_page
$2615bd 01 25ldagold_offset,x
$26188d 70 25stafloat_score_offset
$261Bce 70 25decfloat_score_offset
$261Ef8sed
$261Fad 49 29ldagame_timer_bcd
$262218clc
$26236d 48 29adcscore_penalty_low_bcd
$26268d 48 29stascore_penalty_low_bcd
$262990 09bccb_2634
$262Bad 47 29ldascore_penalty_high_bcd
$262E18clc
$262F69 01adc#$01
$26318d 47 29stascore_penalty_high_bcd
$2634ad 49 29b_2634ldagame_timer_bcd; x-ref: $2629
$263718clc
$26386d 46 29adcscore_low_bcd
$263B8d 46 29stascore_low_bcd
$263E90 09bccb_2649
$2640ad 45 29ldascore_high_bcd
$264318clc
$264469 01adc#$01
$26468d 45 29stascore_high_bcd
$2649d8b_2649cld; x-ref: $263E
$264Acab_264Adex; x-ref: $25E3, $25EB, $25F6
$264B10 93bplb_25E0
$264Dad df 24ldagold_count
$2650cd e0 24cmpgold_total
$2653d0 03bner_2658
$265520 fc 2ajsrreset_bonus_timer
$265860r_2658rts; x-ref: $2653
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the player has reached the level exit and awards the time bonus.
; Called early in the IRQ loop ($0650), before any sprite restores.
;
; Gate: if bonus_timer == $FF (time expired), the exit is locked — skip entirely.
; The player cannot complete the level and must die to continue.
;
; Trigger condition (both must match):
; draw_page == exit_page (player on exit row)
; draw_offset == exit_offset (player on exit column)
;
; On trigger:
; 1. Arm floating score popup:
; float_score_timer = $50 (~80 frames)
; float_score_bcd = game_timer_bcd (value shown as "+XX")
; float_score_page = draw_page + 1
; float_score_offset = draw_offset - 3
; 2. BCD add game_timer_bcd to score_low_bcd:score_high_bcd (displayed score).
; 3. BCD add game_timer_bcd to score_penalty_low_bcd:score_penalty_high_bcd
; (death-penalty accumulator — subtracted from score if player dies).
; 4. inc level_complete_flag -> picked up next frame by check_level_complete.
;
; Inputs: bonus_timer, draw_page, draw_offset, exit_page, exit_offset,
; game_timer_bcd, score_low/high_bcd, score_penalty_low/high_bcd
; Outputs: float_score_* armed; score_low/high_bcd and score_penalty_low/high_bcd
; updated in BCD; level_complete_flag incremented
; Side Effects: Decimal flag toggled (sed/cld)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_level_complete_bonus
$2659ad e6 2aldabonus_timer; x-ref: $0650
$265Cc9 ffcmp#$ff
$265Ef0 62beqr_26C2
$2660ad b5 0dldadraw_page
$2663cd e3 2acmpexit_page
$2666d0 5abner_26C2
$2668ad b4 0dldadraw_offset
$266Bcd e2 2acmpexit_offset
$266Ed0 52bner_26C2
$2670a9 50lda#$50
$26728d 6e 25stafloat_score_timer
$2675ad 49 29ldagame_timer_bcd
$26788d 74 25stafloat_score_bcd
$267Bad b5 0dldadraw_page
$267E8d 6f 25stafloat_score_page
$2681ee 6f 25incfloat_score_page
$2684ad b4 0dldadraw_offset
$26878d 70 25stafloat_score_offset
$268Ace 70 25decfloat_score_offset
$268Dce 70 25decfloat_score_offset
$2690ce 70 25decfloat_score_offset
$2693f8sed
$2694ad 49 29ldagame_timer_bcd
$269718clc
$26986d 46 29adcscore_low_bcd
$269B8d 46 29stascore_low_bcd
$269E90 09bccb_26A9
$26A0ad 45 29ldascore_high_bcd
$26A318clc
$26A469 01adc#$01
$26A68d 45 29stascore_high_bcd
$26A9ad 49 29b_26A9ldagame_timer_bcd; x-ref: $269E
$26AC18clc
$26AD6d 48 29adcscore_penalty_low_bcd
$26B08d 48 29stascore_penalty_low_bcd
$26B390 09bccb_26BE
$26B5ad 47 29ldascore_penalty_high_bcd
$26B818clc
$26B969 01adc#$01
$26BB8d 47 29stascore_penalty_high_bcd
$26BEd8b_26BEcld; x-ref: $26B3
$26BFee e1 2ainclevel_complete_flag
$26C260r_26C2rts; x-ref: $265E, $2666, $266E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Fires when level_complete_flag == 1 (set by check_level_complete_bonus).
; Plays the victory jingle, updates and redraws the sidebar, waits 33 pause
; ticks, advances the level-select cursor, then loads the next level.
;
; Sequence:
; 1. JSR play_victory_jingle
; 2. JSR update_sidebar_status + blit_sidebar (redraw score/lives panel)
; 3. Busy-wait loop x33 (ldx #$20; jsr busy_wait_pause; dex; bpl)
; 4. JSR animate_level_select_cursor (advance level indicator in UI)
; 5. JSR advance_level
;
; Inputs: level_complete_flag
; Outputs: Triggers full level-transition sequence
; Side Effects: Audio, sidebar redrawn, level state reset by advance_level
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$26C3ad e1 2acheck_level_completeldalevel_complete_flag; x-ref: $06A1
$26C6c9 01cmp#$01
$26C8d0 17bner_26E1
$26CA20 f9 0cjsrplay_victory_jingle
$26CD20 ac 29jsrupdate_sidebar_status
$26D020 27 09jsrblit_sidebar
$26D3a2 20ldx#$20
$26D520 c8 09b_26D5jsrbusy_wait_pause; x-ref: $26D9
$26D8cadex
$26D910 fabplb_26D5
$26DB20 dd 09jsrtransition_from_full_to_empty
$26DE20 c8 06jsradvance_level
$26E160r_26E1rts; x-ref: $26C8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Collision detection that allows Enemy Guards to steal unclaimed Gold items.
; Loops through all 16 Gold slots and checks them against all 5 Guard slots.
; If an active Guard walks exactly over an active Gold piece (checking that their
; visual page buffers align), the Guard picks up the gold!
;
; When a guard picks up gold:
; 1. Gold is marked inactive ($FF).
; 2. The Guard's AI bias is set to $90 (potentially making them flee or walk slower).
; 3. The exact index of the stolen gold piece is stored in guard_hole_status so
; it can be properly dropped if the guard falls in a dug hole later.
;
; Inputs: gold arrays and guard arrays
; Outputs: Flags gold as inactive and sets guard 'carrying' states.
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_guard_picks_up_gold
$26E2a2 0fldx#$0f; x-ref: $067A
$26E4bd e1 24b_26E4ldagold_active_flag,x; x-ref: $2722
$26E730 38bmib_2721
$26E9a0 04ldy#$04
$26EBb9 f9 21b_26EBldaguard_active,y; x-ref: $271F
$26EEc9 ffcmp#$ff
$26F0f0 2cbeqb_271E
$26F2b9 21 22ldaguard_ai_bias,y
$26F5f0 03beqb_26FA
$26F74c 1e 27jmpb_271E
$26FAbd f1 24b_26FAldagold_page,x; x-ref: $26F5
$26FD38sec
$26FEe9 01sbc#$01
$2700d9 08 22cmpguard_page,y
$2703d0 19bneb_271E
$2705bd 01 25ldagold_offset,x
$2708d9 03 22cmpguard_offset,y
$270Bd0 11bneb_271E
$270Da9 fflda#$ff
$270F9d e1 24stagold_active_flag,x
$2712a9 90lda#$90
$271499 21 22staguard_ai_bias,y
$27178atxa
$271899 26 22staguard_hole_status,y
$271B4c 21 27jmpb_2721
$271E88b_271Edey; x-ref: $26F0, $26F7, $2703, $270B
$271F10 cabplb_26EB
$2721cab_2721dex; x-ref: $26E7, $271B
$272210 c0bplb_26E4
$272460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Logic that allows Guards carrying stolen gold to randomly drop it on the floor.
; Iterates through all the Guards. If their AI bias indicates they are carrying gold ($FF),
; it rolls a random number (3/256 or ~1.1% chance per tick).
; If the RNG check passes, it ensures the guard is standing on solid, walkable ground
; (collision mask $03) and that the screen tile is actually an empty space ($20).
;
; If valid, it retrieves the ID of the specific gold piece the guard stole (stored in
; guard_hole_status), reactivates it at the Guard's current X/Y coordinates, and resets
; the Guard's AI bias to $7F so they don't immediately pick it back up!
;
; Inputs: Guard arrays, random number generator
; Outputs: Reactivates dropped gold pieces at new coordinates.
; Side Effects: Modifies ZP pointers $5B and $6F briefly.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_guard_drops_gold
$2725a2 04ldx#$04; x-ref: $0680
$2727bd f9 21b_2727ldaguard_active,x; x-ref: $2776
$272Ac9 ffcmp#$ff
$272Cf0 47beqb_2775
$272Ebd 21 22ldaguard_ai_bias,x
$2731c9 ffcmp#$ff
$2733d0 40bneb_2775
$273520 72 07jsrget_random
$2738c9 04cmp#$04
$273Ab0 39bcsb_2775
$273Cbd 08 22ldaguard_page,x
$273F85 5bstazp_ptr_playfield_hi
$2741e6 5binczp_ptr_playfield_hi
$2743bd 03 22ldaguard_offset,x
$2746a8tay
$2747b1 5alda(zp_ptr_playfield_lo),y
$274929 03and#$03
$274Bf0 28beqb_2775
$274Da5 5bldazp_ptr_playfield_hi
$274F85 6fstazp_ptr_guard_top_hi
$2751b1 6elda(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer
$2753c9 20cmp#$20
$2755d0 1ebneb_2775
$2757bd 26 22ldaguard_hole_status,x
$275Aa8tay
$275Ba9 00lda#$00
$275D99 e1 24stagold_active_flag,y
$2760bd 03 22ldaguard_offset,x
$276399 01 25stagold_offset,y
$2766bd 08 22ldaguard_page,x
$276918clc
$276A69 01adc#$01
$276C99 f1 24stagold_page,y
$276Fa9 7flda#$7f
$27719d 21 22staguard_ai_bias,x
$277460rts
$277588b_2775dey; x-ref: $272C, $2733, $273A, $274B, $2755
$277610 afbplb_2727
$277860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Crushes guards that are trapped in a hole (guard_active == 0) when the hole
; heals over them. Iterates all 5 guards (X: 4..0).
;
; Per trapped guard:
; 1. Read movement grid at (guard_page+1, guard_offset).
; Bit $20 set -> hole still open -> skip this guard.
; Bit $20 clear -> hole has closed; proceed to crush check.
;
; 2. Search dig slots (Y: 9..0) for the matching hole:
; dig_slot_col,y == guard_offset,x
; dig_slot_page,y == guard_page,x + 2
; No match found -> skip guard.
;
; 3. dig_slot_hole_idx,y >= 0 -> already linked to a hole slot (in progress) -> skip.
;
; 4. Hole in early animation (dig_slot_status < 2):
; - Accelerate hole to phase 4 (dig_slot_status = $04)
; - Stop current sound ($E848 = $00, sound_active_flag = $00)
; - Defer crush to next frame.
;
; 5. Hole well advanced (dig_slot_status >= 2) -> crush now:
; a. If guard was carrying gold (guard_ai_bias < 0):
; Reactivate gold (gold_active_flag = $00) at hole position.
; b. Link hole_slot <-> dig_slot:
; hole_slot_offset,x = dig_slot_col,y
; hole_slot_page,x = dig_slot_page,y - 2
; hole_slot_actor,x = Y (dig slot index)
; dig_slot_hole_idx,y = X (guard index)
; c. hole_slot_state,x = $FF
; d. guard_active,x = $FF -> guard killed; respawn sequence begins.
;
; Inputs: guard_active/page/offset/ai_bias/hole_status arrays,
; movement grid (a5A), dig_slot arrays, hole_slot arrays
; Outputs: Trapped guards crushed and respawned; carried gold dropped;
; hole_slot and dig_slot cross-linked
; Side Effects: dig_slot_status accelerated; $E848 and sound_active_flag
; zeroed to stop current sound when hole is in early phase
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_hole_healing_crush
$2779a2 04ldx#$04; x-ref: $067D
$277Bbd f9 21b_277Bldaguard_active,x; x-ref: $2781
$277Ef0 04beqb_2784
$2780cab_2780dex; x-ref: $2792, $27B4, $27BA, $2808, $2818
$278110 f8bplb_277B
$278360rts
$2784bd 08 22b_2784ldaguard_page,x; x-ref: $277E
$278785 5bstazp_ptr_playfield_hi
$2789e6 5binczp_ptr_playfield_hi
$278Bbc 03 22ldyguard_offset,x
$278Eb1 5alda(zp_ptr_playfield_lo),y
$279029 20and#$20
$2792d0 ecbneb_2780
$2794a0 09ldy#$09
$2796b9 43 1eb_2796ldadig_slot_status,y; x-ref: $27B2
$279930 16bmib_27B1
$279Bb9 57 1eldadig_slot_col,y
$279Edd 03 22cmpguard_offset,x
$27A1d0 0ebneb_27B1
$27A3b9 4d 1eldadig_slot_page,y
$27A638sec
$27A7e9 02sbc#$02
$27A9dd 08 22cmpguard_page,x
$27ACd0 03bneb_27B1
$27AE4c b7 27jmpj_27B7
$27B188b_27B1dey; x-ref: $2799, $27A1, $27AC
$27B210 e2bplb_2796
$27B44c 80 27jmpb_2780
$27B7b9 93 1ej_27B7ldadig_slot_hole_idx,y; x-ref: $27AE
$27BA10 c4bplb_2780
$27BCb9 43 1eldadig_slot_status,y
$27BFc9 02cmp#$02
$27C190 48bccb_280B
$27C386 64stxzp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer
$27C5bd 21 22ldaguard_ai_bias,x
$27C810 18bplb_27E2
$27CAbd 26 22ldaguard_hole_status,x
$27CDaatax
$27CEa9 00lda#$00
$27D09d e1 24stagold_active_flag,x
$27D3b9 57 1eldadig_slot_col,y
$27D69d 01 25stagold_offset,x
$27D9b9 4d 1eldadig_slot_page,y
$27DC9d f1 24stagold_page,x
$27DFde f1 24decgold_page,x
$27E2a6 64b_27E2ldxzp_scratch_reg_a; x-ref: $27C8 SGNFLG Pointer: Series Evaluation Constant Pointer
$27E4b9 57 1eldadig_slot_col,y
$27E79d da 2bstahole_slot_offset,x
$27EAb9 4d 1eldadig_slot_page,y
$27ED9d d5 2bstahole_slot_page,x
$27F0de d5 2bdechole_slot_page,x
$27F3de d5 2bdechole_slot_page,x
$27F698tya
$27F79d e9 2bstahole_slot_actor,x
$27FA8atxa
$27FB99 93 1estadig_slot_hole_idx,y
$27FEa9 fflda#$ff
$28009d d0 2bstahole_slot_state,x
$2803a9 fflda#$ff
$28059d f9 21staguard_active,x
$28084c 80 27jmpb_2780
$280Ba9 04b_280Blda#$04; x-ref: $27C1
$280D99 43 1estadig_slot_status,y
$2810a9 00lda#$00
$28128d 48 e8sta$e848; Timer 2 LO
$28158d 7a 0cstasound_active_flag
$28184c 80 27jmpb_2780
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if guard X is occupying the same screen cell as any other active guard.
; Loops Y: 4..0, skipping self (Y == X) and inactive guards (guard_active bmi).
; Compares guard_offset and guard_page for an exact match.
;
; Returns with Z=1 if a collision is found, Z=0 if no collision.
; Called from all 5 movement sub-handlers in process_guard_movement to
; validate each move — caller rolls back the move if Z=1.
;
; Inputs: X (current guard index), guard_page/offset/active arrays
; Outputs: Z=1 on collision, Z=0 if clear
; Side Effects: ZP a64 used as temp index
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_guard_guard_collision
$281Ba0 04ldy#$04; x-ref: $2278, $22BB, $2300, $234B, $23A0
$281D84 64b_281Dstyzp_scratch_reg_a; x-ref: $2839 SGNFLG Pointer: Series Evaluation Constant Pointer
$281Fe4 64cpxzp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer
$2821f0 15beqb_2838
$2823b9 f9 21ldaguard_active,y
$282630 10bmib_2838
$2828bd 03 22ldaguard_offset,x
$282Bd9 03 22cmpguard_offset,y
$282Ed0 08bneb_2838
$2830bd 08 22ldaguard_page,x
$2833d9 08 22cmpguard_page,y
$2836f0 03beqr_283B
$283888b_2838dey; x-ref: $2821, $2826, $282E
$283910 e2bplb_281D
$283B60r_283Brts; x-ref: $2836
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the current Guard (index in X) has walked into an active dug hole.
; It loops through all 5 hole slot arrays. Because a Guard is 2 physical blocks tall
; and a hole is also 2 blocks tall, it checks for a collision against both
; the Guard's top block (guard_page) and their bottom block (guard_page + 1).
;
; If the Guard's boundaries intersect with the hole, it returns with the Zero Flag
; set, allowing the caller to initiate the "guard trapped" sequence.
;
; Inputs: X (current Guard index), guard coordinates, hole slot arrays
; Outputs: Sets Zero flag (Z=1) on collision.
; Side Effects: Modifies Y register.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_guard_hole_collision
$283Ca0 04ldy#$04; x-ref: $227D, $22C0, $2305, $2350, $23A5
$283Eb9 d0 2bb_283Eldahole_slot_state,y; x-ref: $285C
$2841f0 18beqb_285B
$2843bd 03 22ldaguard_offset,x
$2846d9 da 2bcmphole_slot_offset,y
$2849d0 10bneb_285B
$284Bbd 08 22ldaguard_page,x
$284Ed9 d5 2bcmphole_slot_page,y
$2851f0 0bbeqr_285E
$285318clc
$285469 01adc#$01
$2856d9 d5 2bcmphole_slot_page,y
$2859f0 03beqr_285E
$285B88b_285Bdey; x-ref: $2841, $2849
$285C10 e0bplb_283E
$285E60r_285Erts; x-ref: $2851, $2859
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the Player has collided with an enemy Guard.
; Iterates through all 5 Guard slots. If a Guard is active, it compares the
; player's coordinates (draw_page, draw_offset) with the Guard's exact coordinates.
;
; If they intersect exactly, it triggers the Player Death sequence:
; Plays the death sound, runs a 64-cycle busy-wait loop to freeze the game momentarily,
; and then calls handle_player_death to subtract a life and restart the level.
;
; Inputs: Player drawing coordinates, Guard arrays.
; Outputs: Triggers death/restart sequence.
; Side Effects: Calls sound routines and pauses the game execution.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_player_guard_death
$285Fa0 04ldy#$04; x-ref: $0695
$2861b9 f9 21b_2861ldaguard_active,y; x-ref: $2889
$286430 22bmib_2888
$2866ad b4 0dldadraw_offset
$2869d9 03 22cmpguard_offset,y
$286Cd0 1abneb_2888
$286Ead b5 0dldadraw_page
$2871d9 08 22cmpguard_page,y
$2874d0 12bneb_2888
$287620 0b 0djsrplay_death_sound
$2879a2 40ldx#$40
$287B20 c8 09b_287Bjsrbusy_wait_pause; x-ref: $287F
$287Ecadex
$287F10 fabplb_287B
$288120 dd 09jsrtransition_from_full_to_empty
$288420 11 07jsrhandle_player_death
$288760rts
$288888b_2888dey; x-ref: $2864, $286C, $2874
$288910 d6bplb_2861
$288B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the Player has fallen victim to a deadly hole or trap state.
; Iterates through the active hole slots. If the player's exact coordinates
; match the coordinates of a deadly hole state, it triggers the Player Death sequence.
;
; Similar to guard collision, it plays the death sound, runs a minor delay loop
; (17 iterations), and calls handle_player_death.
;
; Inputs: Player drawing coordinates, hole slot arrays.
; Outputs: Triggers death/restart sequence.
; Side Effects: Calls sound routines and pauses execution.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_player_hole_death
$288Ca0 04ldy#$04; x-ref: $069B
$288Eb9 d0 2bb_288Eldahole_slot_state,y; x-ref: $28B6
$2891f0 22beqb_28B5
$2893ad b4 0dldadraw_offset
$2896d9 da 2bcmphole_slot_offset,y
$2899d0 1abneb_28B5
$289Bad b5 0dldadraw_page
$289Ed9 d5 2bcmphole_slot_page,y
$28A1d0 12bneb_28B5
$28A320 0b 0djsrplay_death_sound
$28A6a2 10ldx#$10
$28A820 c8 09b_28A8jsrbusy_wait_pause; x-ref: $28AC
$28ABcadex
$28AC10 fabplb_28A8
$28AE20 dd 09jsrtransition_from_full_to_empty
$28B120 11 07jsrhandle_player_death
$28B460rts
$28B588b_28B5dey; x-ref: $2891, $2899, $28A1
$28B610 d6bplb_288E
$28B860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the player has collided with a generic hazard actor (state $04).
; Iterates through all 10 general actor slots. If it finds an actor in the
; explicitly deadly state $04 (such as a crushing block interaction or trap),
; and the player's coordinates (draw_page/offset) intersect exactly with it,
; it triggers the Player Death sequence (death sound, brief pause, death handler).
;
; Inputs: Player draw coordinates, actor arrays
; Outputs: Triggers death/restart sequence.
; Side Effects: Calls sound routines and pauses execution.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_player_hazard_death
$28B9a2 09ldx#$09; x-ref: $069E
$28BBbd 43 1eb_28BBldadig_slot_status,x; x-ref: $28E5
$28BEc9 04cmp#$04
$28C0d0 22bneb_28E4
$28C2bd 4d 1eldadig_slot_page,x
$28C5cd b5 0dcmpdraw_page
$28C8d0 1abneb_28E4
$28CAbd 57 1eldadig_slot_col,x
$28CDcd b4 0dcmpdraw_offset
$28D0d0 12bneb_28E4
$28D220 0b 0djsrplay_death_sound
$28D5a0 08ldy#$08
$28D720 c8 09b_28D7jsrbusy_wait_pause; x-ref: $28DB
$28DA88dey
$28DB10 fabplb_28D7
$28DD20 dd 09jsrtransition_from_full_to_empty
$28E020 11 07jsrhandle_player_death
$28E360rts
$28E4cab_28E4dex; x-ref: $28C0, $28C8, $28D0
$28E510 d4bplb_28BB
$28E760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the player has been crushed inside a solid brick of the level geometry.
; Reads the exact property of the collision grid mapped to the player's location.
; If the collision bitmask is completely empty (0), or if either of the highest bits
; are set ($C0 = Solid Wall/Restored Brick), the player is considered crushed
; inside a solid object and dies!
;
; Inputs: Player draw coordinates, collision grid ($672B)
; Outputs: Triggers death sequence on invalid tile positioning.
; Side Effects: Calls sound and delay routines, modifies zero page pointer $5B.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_player_wall_crush_death
$28E8ad b5 0dldadraw_page; x-ref: $0698
$28EB85 5bstazp_ptr_playfield_hi
$28EDe6 5binczp_ptr_playfield_hi
$28EFac b4 0dldydraw_offset
$28F2b1 5alda(zp_ptr_playfield_lo),y
$28F4f0 05beqb_28FB
$28F629 c0and#$c0
$28F8d0 01bneb_28FB
$28FA60rts
$28FB20 0b 0db_28FBjsrplay_death_sound; x-ref: $28F4, $28F8
$28FEa0 08ldy#$08
$290020 c8 09b_2900jsrbusy_wait_pause; x-ref: $2904
$290388dey
$290410 fabplb_2900
$290620 dd 09jsrtransition_from_full_to_empty
$290920 11 07jsrhandle_player_death
$290C60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans the hardware keyboard matrix for the player "Pause" key.
; Writes the row mask (key_row_pause) to Port A of PIA 1, reads the active
; columns from Port B, and masks it against key_col_pause.
;
; If pressed, it jumps into the pause handler to temporarily freeze gameplay.
;
; Inputs: PIA 1 ports, key_row_pause ($2E), key_col_pause ($37)
; Outputs: Pauses the game if pressed.
; Side Effects: Polls keyboard hardware.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$290Da5 2echeck_pause_keyldazp_key_row_pause; x-ref: $06A4 STREND Pointer End of BASIC Arrays (+1)
$290F8d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$2912ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$291525 37andzp_key_col_pause
$2917d0 03bner_291C
$291920 38 36jsropen_pause_menu
$291C60r_291Crts; x-ref: $2917
.encode
.enc"screen"
$291Dtxt_level.text"LEVEL", $ff; x-ref: $294D
$2923txt_gold.text"GOLD", $ff; x-ref: $295D
$2928txt_timer.text"TIMER", $ff; x-ref: $296D
$292Etxt_points.text"POINTS", $ff; x-ref: $297D
$2935txt_level_points.text"-LEVEL", $ff; x-ref: $298D
$293Ctxt_total_points.text"-TOTAL", $ff; x-ref: $299D
.endencode
$2943hi_score_high_bcd.byte$20; x-ref: $2EF9, $2F39, $2F69, $2F78, $3407, $3416
$2944hi_score_low_bcd.byte$21; x-ref: $2F06, $2F3F, $2F83, $2F92, $3421, $3430
$2945score_high_bcd.byte$00; x-ref: $06B8, $0722, $0728, $2640, $2646, $26A0, $26A6, $2A7E, ...
$2946score_low_bcd.byte$00; x-ref: $06BB, $0718, $071F, $2638, $263B, $2698, $269B, $2A96, ...
score_penalty_high_bcd
$2947.byte$00; x-ref: $06FB, $0725, $072E, $262B, $2631, $26B5, $26BB, $2A3B, ...
score_penalty_low_bcd
$2948.byte$00; x-ref: $06FE, $071C, $0731, $2623, $2626, $26AD, $26B0, $2A53, ...
$2949game_timer_bcd.byte$00; x-ref: $0703, $2609, $261F, $2634, $2675, $2694, $26A9, $2A1D, ...
$294Atimer_frame_counter.byte$00; x-ref: $2AC9, $2AD0
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the static UI text labels in the sidebar on the right side of the screen.
; Iterates through several hardcoded strings (LEVEL, GOLD, TIME, POINTS, etc.) and
; copies them to the playfield buffer at offset $23 (column 35) on various rows.
;
; Inputs: Hardcoded strings (txt_level, txt_gold, txt_timer, txt_points, etc.).
; Outputs: Populates the sidebar area in the playfield buffer with static labels.
; Side Effects: Modifies X register.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$294Ba2 00draw_ui_text_labelsldx#$00; x-ref: $075A
$294Dbd 1d 29j_294Dldatxt_level,x; x-ref: $2958
$2950c9 ffcmp#$ff
$2952f0 07beqb_295B
$29549d 23 69staplayfield_row_2_col_34,x; Write 'LEVEL' static text to sidebar (row 2, column 34)
$2957e8inx
$29584c 4d 29jmpj_294D
$295Ba2 00b_295Bldx#$00; x-ref: $2952
$295Dbd 23 29j_295Dldatxt_gold,x; x-ref: $2968
$2960c9 ffcmp#$ff
$2962f0 07beqb_296B
$29649d 23 6dstaplayfield_row_6_col_34,x; Write 'GOLD' static text to sidebar (row 6, column 34)
$2967e8inx
$29684c 5d 29jmpj_295D
$296Ba2 00b_296Bldx#$00; x-ref: $2962
$296Dbd 28 29j_296Dldatxt_timer,x; x-ref: $2978
$2970c9 ffcmp#$ff
$2972f0 07beqb_297B
$29749d 23 71staplayfield_row_10_col_34,x; Write 'TIME' static text to sidebar (row 10, column 34)
$2977e8inx
$29784c 6d 29jmpj_296D
$297Ba2 00b_297Bldx#$00; x-ref: $2972
$297Dbd 2e 29j_297Dldatxt_points,x; x-ref: $2988
$2980c9 ffcmp#$ff
$2982f0 07beqb_298B
$29849d 23 75staplayfield_row_14_col_34,x; Write 'POINTS' static text to sidebar (row 14, column 34)
$2987e8inx
$29884c 7d 29jmpj_297D
$298Ba2 00b_298Bldx#$00; x-ref: $2982
$298Dbd 35 29j_298Dldatxt_level_points,x; x-ref: $2998
$2990c9 ffcmp#$ff
$2992f0 07beqb_299B
$29949d 23 77staplayfield_row_16_col_34,x; Write '-LEVEL' static text to sidebar (row 16, column 34)
$2997e8inx
$29984c 8d 29jmpj_298D
$299Ba2 00b_299Bldx#$00; x-ref: $2992
$299Dbd 3c 29j_299Dldatxt_total_points,x; x-ref: $29A8
$29A0c9 ffcmp#$ff
$29A2f0 07beqr_29AB
$29A49d 23 7astaplayfield_row_19_col_34,x; Write '-TOTAL' static text to sidebar (row 19, column 34)
$29A7e8inx
$29A84c 9d 29jmpj_299D
$29AB60r_29ABrts; x-ref: $29A2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates all numeric displays in the sidebar. Converts BCD/binary values to
; PETSCII and writes them directly to PET screen memory.
;
; Section 1 — Level number -> a6A25/26:
; level < 10: single digit at a6A26
; level == 10: '1' at a6A25, '0' at a6A26
;
; Section 2 — Gold progress "count/total" -> a6E24..28:
; total < 10: " count/total " (single-digit each, '/' at a6E26)
; total >= 10: count at a6E24/25, '/' at a6E26, total tens at a6E27, units at a6E28
;
; Section 3 — Game timer -> a7225/26:
; BCD value; clamped: if $51 display as $50.
; tens digit at a7225, units digit at a7226.
;
; Section 4 — Death-penalty score -> a7824..27:
; 4-digit BCD (score_penalty_high/low_bcd); up to 3 leading '0's replaced with ' '.
;
; Section 5 — Current score -> a7B24..27:
; 4-digit BCD (score_high/low_bcd); up to 3 leading '0's replaced with ' '.
;
; Inputs: ui_level_num, gold_count, gold_total, game_timer_bcd,
; score_penalty_high/low_bcd, score_high/low_bcd
; Outputs: Screen memory at a6A25/26, a6E24-28, a7225/26, a7824-27, a7B24-27
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_sidebar_status
$29ACad df 2aldaui_level_num; --- section 1: level number --- ; x-ref: $0626, $26CD
$29AFc9 0acmp#$0a
$29B1f0 0bbeqb_29BE; level == 10? use two-digit path
$29B329 0fand#$0f; levels 1-9: low nibble + $30 -> PETSCII digit
$29B518clc
$29B669 30adc#$30
$29B88d 26 6astahud_level_units; store units digit
$29BB4c c8 29jmpj_29C8
$29BEa9 31b_29BElda#$31; level 10: '1' tens, '0' units ; x-ref: $29B1
$29C08d 25 6astahud_level_tens
$29C3a9 30lda#$30
$29C58d 26 6astahud_level_units
$29C8ad e0 24j_29C8ldagold_total; --- section 2: gold progress "count/total" --- total >= 10? two-digit path ; x-ref: $29BB
$29CBc9 0acmp#$0a
$29CDb0 1abcsb_29E9
$29CFad df 24ldagold_count; total < 10: count digit at a6E25
$29D218clc
$29D369 30adc#$30
$29D58d 25 6estahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00)
$29D8a9 2flda#$2f; total digit at a6E27
$29DA8d 26 6estahud_gold_slash; Gold HUD display separator '/' (ASCII $2F)
$29DDad e0 24ldagold_total
$29E018clc
$29E169 30adc#$30
$29E38d 27 6estahud_gold_total_tens; HUD gold progress: total tens digit (offset $27 from Row 7 base $6E00); HUD gold progress: total tens digit or units if total < 10 (offset $27 from Row 7 base $6E00)
$29E64c 18 2ajmpj_2A18
$29E9a9 31b_29E9lda#$31; total >= 10: tens='1' at a6E27, units=total-10 at a6E28 ; x-ref: $29CD
$29EB8d 27 6estahud_gold_total_tens; HUD gold progress: total tens digit (offset $27 from Row 7 base $6E00); HUD gold progress: total tens digit or units if total < 10 (offset $27 from Row 7 base $6E00)
$29EEad e0 24ldagold_total
$29F138sec
$29F2e9 0asbc#$0a
$29F418clc
$29F569 30adc#$30
$29F78d 28 6estahud_gold_total_units; HUD gold progress: total units digit (offset $28 from Row 7 base $6E00); HUD gold progress: total units digit when total >= 10 (offset $28 from Row 7 base $6E00)
; Format collected gold count as BCD display (tens/units) and write to HUD Row 7 (columns 35-36)
$29FAad df 24ldagold_count; count >= 10? tens='1' at hud_gold_count_tens, units at hud_gold_count_units
$29FDc9 0acmp#$0a
$29FFb0 09bcsb_2A0A
$2A0118clc
$2A0269 30adc#$30
$2A048d 25 6estahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00)
$2A074c 18 2ajmpj_2A18
$2A0A38b_2A0Asec; x-ref: $29FF
$2A0Be9 0asbc#$0a
$2A0D18clc
$2A0E69 30adc#$30
$2A108d 25 6estahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00)
$2A13a9 31lda#$31
$2A158d 24 6estahud_gold_count_tens; HUD gold progress: count tens digit (offset $24 from Row 7 base $6E00)
$2A18a9 2fj_2A18lda#$2f; Write '/' separator to hud_gold_sep ; x-ref: $29E6, $2A07
$2A1A8d 26 6estahud_gold_slash; HUD gold progress: '/' separator (offset $26 from Row 7 base $6E00); Gold HUD display separator '/' (ASCII $2F)
$2A1Dad 49 29ldagame_timer_bcd; --- section 3: game timer (2-digit BCD) --- clamp: $51 displayed as $50
$2A20c9 51cmp#$51
$2A22d0 02bneb_2A26
$2A24a9 50lda#$50
$2A2629 0fb_2A26and#$0f; low nibble -> units digit at a7226 ; x-ref: $2A22
$2A2818clc
$2A2969 30adc#$30
$2A2B8d 26 72staplayfield_row_11_col_37; Store converted units digit to HUD timer
$2A2Ead 49 29ldagame_timer_bcd; high nibble (>>4) -> tens digit at a7225
$2A314alsra
$2A324alsra
$2A334alsra
$2A344alsra
$2A3518clc
$2A3669 30adc#$30
$2A388d 25 72stahud_timer_tens; Store converted tens digit to HUD timer
$2A3Bad 47 29ldascore_penalty_high_bcd; --- Section 4: death-penalty score (score_penalty_high/low_bcd → a7824-27) ---
$2A3E4alsra; 4x LSR = extract high nibble (tens-thousands digit)
$2A3F4alsra
$2A404alsra
$2A414alsra
$2A4218clc
$2A4369 30adc#$30; +$30 = convert BCD nibble to PETSCII digit
$2A458d 24 78stahud_penalty_ten_thousands; HUD penalty: ten-thousands digit
$2A48ad 47 29ldascore_penalty_high_bcd
$2A4B29 0fand#$0f; mask low nibble (thousands digit)
$2A4D18clc
$2A4E69 30adc#$30
$2A508d 25 78stahud_penalty_thousands; HUD penalty: thousands digit
$2A53ad 48 29ldascore_penalty_low_bcd; low byte of penalty (hundreds + tens digits)
$2A564alsra
$2A574alsra
$2A584alsra
$2A594alsra
$2A5A18clc
$2A5B69 30adc#$30
$2A5D8d 26 78stahud_penalty_hundreds; HUD penalty: hundreds digit
$2A60ad 48 29ldascore_penalty_low_bcd
$2A6329 0fand#$0f; tens digit → sidebar penalty display
$2A6518clc
$2A6669 30adc#$30
$2A688d 27 78stahud_penalty_tens; HUD penalty: tens digit (lowest active digit, units is static '0')
$2A6Ba2 00ldx#$00; leading-zero suppression: scan left to right
$2A6Dbd 24 78b_2A6Dldahud_penalty_ten_thousands,x; check penalty digit[x] ; x-ref: $2A7C
$2A70c9 30cmp#$30; is it PETSCII '0'?
$2A72d0 0abneb_2A7E; non-zero digit: stop suppressing
$2A74a9 20lda#$20; replace leading '0' with space
$2A769d 24 78stahud_penalty_ten_thousands,x
$2A79e8inx
$2A7Ae0 03cpx#$03; stop before last digit (always show at least the units digit)
$2A7Cd0 efbneb_2A6D
$2A7Ead 45 29b_2A7Eldascore_high_bcd; --- Section 5: current score (score_high/low_bcd → a7B24-27) --- ; x-ref: $2A72
$2A814alsra; 4x LSR = extract high nibble (ten-thousands digit)
$2A824alsra
$2A834alsra
$2A844alsra
$2A8518clc
$2A8669 30adc#$30; +$30 = convert BCD nibble to PETSCII digit
$2A888d 24 7bstahud_score_ten_thousands; ten-thousands digit → sidebar score display
$2A8Bad 45 29ldascore_high_bcd
$2A8E29 0fand#$0f; mask low nibble (thousands digit)
$2A9018clc
$2A9169 30adc#$30
$2A938d 25 7bstahud_score_thousands; thousands digit → sidebar score display
$2A96ad 46 29ldascore_low_bcd; low byte of score (hundreds + tens digits)
$2A994alsra
$2A9A4alsra
$2A9B4alsra
$2A9C4alsra
$2A9D18clc
$2A9E69 30adc#$30
$2AA08d 26 7bstahud_score_hundreds; hundreds digit → sidebar score display
$2AA3ad 46 29ldascore_low_bcd
$2AA629 0fand#$0f; tens digit → sidebar score display
$2AA818clc
$2AA969 30adc#$30
$2AAB8d 27 7bstaplayfield_row_20_col_39; units digit → sidebar score display
$2AAEa2 00ldx#$00; leading-zero suppression: scan left to right
$2AB0bd 24 7bb_2AB0ldahud_score_ten_thousands,x; check score digit[x] ; x-ref: $2ABF
$2AB3c9 30cmp#$30; is it PETSCII '0'?
$2AB5d0 0abner_2AC1; non-zero digit: stop suppressing
$2AB7a9 20lda#$20; replace leading '0' with space
$2AB99d 24 7bstahud_score_ten_thousands,x; Replace leading zero with a space
$2ABCe8inx
$2ABDe0 03cpx#$03; stop before last digit (always show at least the units digit)
$2ABFd0 efbneb_2AB0
$2AC160r_2AC1rts; x-ref: $2AB5
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the game timer. Decrements frame counter (50 to 0).
; On rollover, decrements game_timer_bcd in Decimal Mode (once per second).
;
; Inputs: game_timer_bcd ($2949), timer_frame_counter ($294A)
; Outputs: Updated timer values.
; Side Effects: Toggles Decimal flag (sed/cld).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2AC2ad 49 29update_game_timerldagame_timer_bcd; x-ref: $064D
$2AC5c9 10cmp#$10
$2AC7f0 15beqr_2ADE
$2AC9ce 4a 29dectimer_frame_counter
$2ACC10 10bplr_2ADE
$2ACEa9 32lda#$32
$2AD08d 4a 29statimer_frame_counter
$2AD3f8sed
$2AD4ad 49 29ldagame_timer_bcd
$2AD738sec
$2AD8e9 01sbc#$01
$2ADA8d 49 29stagame_timer_bcd
$2ADDd8cld
$2ADE60r_2ADErts; x-ref: $2AC7, $2ACC
; Current level number (1-10). Incremented by advance_level on completion;
; wraps 11->1. When ui_level_num reaches level_select_num it wraps back instead.
$2ADFui_level_num.byte$01; x-ref: $06CE, $06D6, $06D9, $06E0, $06E7, $06EA, $06F3, $0709, ...
; Player-selected starting level. advance_level compares ui_level_num to this;
; if equal, decrements (cycles back) instead of advancing. Init $03.
$2AE0level_select_num.byte$03; x-ref: $06D1, $322C
; Level-complete flag. $FF = bonus sequence not yet triggered.
; Cleared by init_bonus_sequencer at level start; updated by check_level_complete_bonus.
$2AE1level_complete_flag.byte$ff; x-ref: $26BF, $26C3, $2AF4
; Level exit screen column offset. Loaded from level data during unpack_level_data.
; check_level_complete_bonus compares draw_offset against this to detect player reaching exit.
$2AE2exit_offset.byte$ff; x-ref: $1246, $266B
; Level exit screen page (row). Loaded from level data.
; check_level_complete_bonus compares draw_page against this.
$2AE3exit_page.byte$ff; x-ref: $124C, $2663
; Guard respawn screen column offset. Loaded from level data.
; Assigned to guard_offset,y when a guard is freed from a dug hole (process_actors status 4).
$2AE4guard_spawn_offset.byte$ff; x-ref: $1252, $1FBC
; Guard respawn screen page (row). Loaded from level data.
; Assigned to guard_page,y when a guard is freed from a dug hole.
$2AE5guard_spawn_page.byte$ff; x-ref: $1258, $1FC2
; Bonus phase countdown timer.
; $FF = inactive (all-gold bonus not yet triggered).
; $08 = loaded by reset_bonus_timer (triggered when all gold collected).
; Decremented each frame by play_bonus_audio_tick; on underflow plays sound and reloads to $08.
; check_level_complete_bonus skips if $FF.
$2AE6bonus_timer.byte$ff; x-ref: $2659, $2AEB, $2AFC, $2B05, $2B09, $2B11, $2B1A, $2BCC
; Bonus walk column offset (col+1): loaded from level data.
; Y = bonus_walk_offset - 1 is the actual screen column used during the walk animation.
$2AE7bonus_walk_offset.byte$00; x-ref: $123A, $2AEE, $2B22
; Bonus walk screen page (row): loaded from level data.
; Used as high byte of ZP pointer (a6D); decremented each step to move the walker up one row.
$2AE8bonus_walk_page.byte$00; x-ref: $1240, $2AF1, $2B1D, $2BB4, $2BB7
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets all bonus-sequence state to the disarmed sentinel ($FF) at level load.
;
; $FF is the "not yet active" value for every field:
; bonus_timer : play_bonus_audio_tick bails immediately (bit 7 set)
; bonus_walk_offset : no walk column set
; bonus_walk_page : no walk row set
; level_complete_flag: requires two inc's ($FF→$00→$01) before
; check_level_complete fires on cmp #$01 — the player
; must stand on the exit tile for 2 consecutive frames
;
; a6C (ZP pointer low byte used by play_bonus_audio_tick) is zeroed here so
; the walk pointer base address is clean when bonus_walk_page is later loaded.
;
; Inputs: None
; Outputs: bonus_timer=$FF, bonus_walk_offset=$FF, bonus_walk_page=$FF,
; level_complete_flag=$FF, a6C=$00
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2AE9a9 ffinit_bonus_sequencerlda#$ff; $FF = disarmed sentinel for all bonus fields ; x-ref: $074E
$2AEB8d e6 2astabonus_timer; play_bonus_audio_tick bails on bit-7 set
$2AEE8d e7 2astabonus_walk_offset; no walk column set yet
$2AF18d e8 2astabonus_walk_page; no walk row set yet
$2AF48d e1 2astalevel_complete_flag; needs $FF→$00→$01 before check_level_complete triggers
$2AF7a9 00lda#$00; a6C = $00: ZP pointer low byte for play_bonus_audio_tick walk animation
$2AF985 6cstazp_ptr_bonus_walk_lo; ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2AFB60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Arms the bonus countdown timer if it is currently inactive.
; $FF is the sentinel meaning "disarmed". If the timer is already running,
; returns immediately without touching it. Otherwise initializes it to $08
; so that play_bonus_audio_tick can begin its 8-frame beep countdown.
;
; Inputs: bonus_timer
; Outputs: bonus_timer set to $08 (only if it was $FF)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2AFCad e6 2areset_bonus_timerldabonus_timer; x-ref: $2655
$2AFFc9 ffcmp#$ff
$2B01d0 05bner_2B08
$2B03a9 08lda#$08
$2B058d e6 2astabonus_timer
$2B0860r_2B08rts; x-ref: $2B01
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Drives the level-exit bonus walk animation and its tick sound.
;
; Called every IRQ. bonus_timer is an 8-frame sub-tick countdown:
; $FF (bit 7 set) = inactive / not yet triggered → return immediately
; $FE = animation complete sentinel → return immediately
; $01..$08 = counting down, dec and return
; $00 = fire: play tick sound, reset to $08, advance walker one row
;
; On each fire tick:
; 1. Draws a 3-column walk animation at the current position:
; col-1: $63→$50 / space→$67 / $65→$43
; col : always $43
; col+1: $63→$4F / else→$65
; 2. Updates movement-grid bits at the walk column:
; set $04 at current row; conditionally $08 if above is open;
; $04 in row below if below has $08; $01/$02 if left/right are open.
; Writes $0F (all-directions open) to cell just left of walker if
; that cell still holds the $43 body char from a previous step.
; 3. Advances walker upward: dec bonus_walk_page (screen row page).
; 4. Checks termination:
; bonus_walk_page == $66 (past top of screen) → set $FE (done)
; next position is space ($20) or border ($63) → return (no $FE yet)
;
; Inputs: bonus_timer, bonus_walk_page (row page), bonus_walk_offset (col+1),
; a6C (ZP ptr low byte, = $00)
; Outputs: offscreen buffer updated with walk chars; movement grid updated
; Side Effects: bonus_timer set to $FE on completion; a6D clobbered
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
play_bonus_audio_tick
$2B09ad e6 2aldabonus_timer; load bonus_timer; bit 7 set ($FE/$FF) → inactive/done, bail out ; x-ref: $0662
$2B0C10 01bplb_2B0F
$2B0E60rts
$2B0Ff0 04b_2B0Fbeqb_2B15; not yet zero → decrement and wait ; x-ref: $2B0C
$2B11ce e6 2adecbonus_timer
$2B1460rts
$2B1520 bc 0cb_2B15jsrtrigger_countdown_sound; fire: play tick beep, reload 8-frame countdown ; x-ref: $2B0F
$2B18a9 08lda#$08
$2B1A8d e6 2astabonus_timer
$2B1Dad e8 2aldabonus_walk_page; a6D (ZP ptr high byte) = bonus_walk_page (screen page/row)
$2B2085 6dstazp_ptr_bonus_walk_hi; FACOV Floating Accum. #1. Low-Order (Rounding)
$2B22ac e7 2aldybonus_walk_offset; Y = bonus_walk_offset - 1 (column index)
$2B2588dey
$2B26b1 6clda(zp_ptr_bonus_walk_lo),y; read char at col-1; substitute walk animation frame; ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2B28c9 63cmp#$63; $63 (border) → $50
$2B2Ad0 05bneb_2B31
$2B2Ca9 50lda#$50
$2B2E4c 40 2bjmpj_2B40
$2B31c9 20b_2B31cmp#$20; $20 (space) → $67 ; x-ref: $2B2A
$2B33d0 05bneb_2B3A
$2B35a9 67lda#$67
$2B374c 40 2bjmpj_2B40
$2B3Ac9 65b_2B3Acmp#$65; $65 (trailing frame) → $43 (body char) ; x-ref: $2B33
$2B3Cd0 04bneb_2B42
$2B3Ea9 43lda#$43
$2B4091 6cj_2B40sta(zp_ptr_bonus_walk_lo),y; x-ref: $2B2E, $2B37 ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2B42c8b_2B42iny; Y++ → col; write $43 (body char) unconditionally ; x-ref: $2B3C
$2B43a9 43lda#$43
$2B4591 6csta(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2B47c8iny; Y++ → col+1; read char
$2B48b1 6clda(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2B4Ac9 63cmp#$63
$2B4Cd0 05bneb_2B53
$2B4Ea9 4flda#$4f
$2B504c 55 2bjmpj_2B55
$2B53a9 65b_2B53lda#$65; x-ref: $2B4C
$2B5591 6cj_2B55sta(zp_ptr_bonus_walk_lo),y; x-ref: $2B50 ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2B5788dey; Y-- back to col; a5B = current row page (movement grid)
$2B58a5 6dldazp_ptr_bonus_walk_hi; FACOV Floating Accum. #1. Low-Order (Rounding)
$2B5A85 5bstazp_ptr_playfield_hi
$2B5Ca9 04lda#$04
$2B5E91 5asta(zp_ptr_playfield_lo),y; write $04 (climb-up) at current movement grid position
$2B60e6 5binczp_ptr_playfield_hi; check row above for wall bits ($C0)
$2B62b1 5alda(zp_ptr_playfield_lo),y
$2B64c6 5bdeczp_ptr_playfield_hi
$2B6629 c0and#$c0
$2B68d0 1abneb_2B84; above is open: set $08 (climb-down) at current row
$2B6Ab1 5alda(zp_ptr_playfield_lo),y
$2B6C09 08ora#$08
$2B6E91 5asta(zp_ptr_playfield_lo),y
$2B70e6 5binczp_ptr_playfield_hi; check if row below already has climb-down bit ($08)
$2B72b1 5alda(zp_ptr_playfield_lo),y
$2B74c6 5bdeczp_ptr_playfield_hi
$2B7629 08and#$08
$2B78f0 0abeqb_2B84
$2B7Ae6 5binczp_ptr_playfield_hi; row below has $08: set $04 (climb-up) in row below too
$2B7Cb1 5alda(zp_ptr_playfield_lo),y
$2B7E09 04ora#$04
$2B8091 5asta(zp_ptr_playfield_lo),y
$2B82c6 5bdeczp_ptr_playfield_hi
$2B84c0 01b_2B84cpy#$01; at left edge (col 1): skip walk-left bit ; x-ref: $2B68, $2B78
$2B86f0 0ebeqb_2B96
$2B8888dey; left neighbor has no wall bits → set $01 (walk-left) at current pos
$2B89b1 5alda(zp_ptr_playfield_lo),y
$2B8Bc8iny
$2B8C29 c0and#$c0
$2B8Ed0 06bneb_2B96
$2B90b1 5alda(zp_ptr_playfield_lo),y
$2B9209 01ora#$01
$2B9491 5asta(zp_ptr_playfield_lo),y
$2B96c0 20b_2B96cpy#$20; at right edge (col $20): skip walk-right bit ; x-ref: $2B86, $2B8E
$2B98f0 0ebeqb_2BA8
$2B9Ac8iny; right neighbor has no wall bits → set $02 (walk-right) at current pos
$2B9Bb1 5alda(zp_ptr_playfield_lo),y
$2B9D88dey
$2B9E29 c0and#$c0
$2BA0d0 06bneb_2BA8
$2BA2b1 5alda(zp_ptr_playfield_lo),y
$2BA409 02ora#$02
$2BA691 5asta(zp_ptr_playfield_lo),y
$2BA888b_2BA8dey; Y-- → col-1; if still holds $43 body char: write $0F (all-dirs open) to movement grid ; x-ref: $2B98, $2BA0
$2BA9b1 6clda(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2BABc9 43cmp#$43
$2BADd0 04bneb_2BB3
$2BAFa9 0flda#$0f
$2BB191 5asta(zp_ptr_playfield_lo),y
$2BB3c8b_2BB3iny; Y++ back to col; advance walker: dec bonus_walk_page (move up one row) ; x-ref: $2BAD
$2BB4ce e8 2adecbonus_walk_page
$2BB7ad e8 2aldabonus_walk_page
$2BBAc9 66cmp#$66; page $66 = past top of screen → animation complete
$2BBCf0 0cbeqb_2BCA
$2BBEc6 6ddeczp_ptr_bonus_walk_hi; also dec a6D (ZP ptr high byte) to match new bonus_walk_page; FACOV Floating Accum. #1. Low-Order (Rounding)
$2BC0b1 6clda(zp_ptr_bonus_walk_lo),y; check next row's char: space or $63 border → stop for this tick (no $FE yet); ARISGN Sign Comparison Result: Accum. # 1 vs #2
$2BC2c9 20cmp#$20
$2BC4f0 09beqr_2BCF
$2BC6c9 63cmp#$63
$2BC8f0 05beqr_2BCF
$2BCAa9 feb_2BCAlda#$fe; $FE sentinel: animation done, play_bonus_audio_tick will bail on next call ; x-ref: $2BBC
$2BCC8d e6 2astabonus_timer
$2BCF60r_2BCFrts; x-ref: $2BC4, $2BC8
; 1x2 dynamic hole/trap slot arrays (5 slots, indexed 0-4, one byte per slot).
; [hole_slot_state] $2BD0+x: $00=empty, $FF=closing-animation, other=active.
; [hole_slot_page] $2BD5+x: Screen memory page (row bank) for the top cell.
; [hole_slot_offset] $2BDA+x: Screen column offset within that page.
; [hole_slot_bg_top] $2BDF+x: Saved background char under top cell.
; [hole_slot_bg_bot] $2BE4+x: Saved background char under bottom cell.
; [hole_slot_actor] $2BE9+x: Actor index that dug this hole ($FF=no owner).
$2BD0hole_slot_state.byte$00, $00, $00, $00, $00; x-ref: $1F9E, $2800, $283E, $288E, $2BF2, $2C04, $2C23, $2C49, ...
; Screen memory page (row bank) for the top cell of each 1x2 hole slot.
$2BD5hole_slot_page.byte$00, $00, $00, $00, $00; x-ref: $27ED, $27F0, $27F3, $284E, $2856, $289E, $2C09, $2C28, ...
; Screen column offset within the page for each 1x2 hole slot.
$2BDAhole_slot_offset.byte$00, $00, $00, $00, $00; x-ref: $27E7, $2846, $2896, $2C0E, $2C2D, $2C53, $2CA4, $2D0F, ...
; Saved background char under top cell; restored when the hole closes.
$2BDFhole_slot_bg_top.byte$00, $00, $00, $00, $00; x-ref: $2C11, $2C32
; Saved background char under bottom cell; restored when the hole closes.
$2BE4hole_slot_bg_bot.byte$00, $00, $00, $00, $00; x-ref: $2C18, $2C39
; Actor index that owns this slot. $FF = free / no owner.
$2BE9hole_slot_actor.byte$ff, $ff, $ff, $ff, $ff; x-ref: $27F7, $2BF7, $2C3E, $2D60, $2D70, $2D7B
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all 5 hole slots at level load time.
; Sets every slot's state to $00 (inactive) and actor to $FF (unoccupied).
; Also zeros a72, the screen pointer low-byte used by the draw/save routines.
; Called from the level-load sequence alongside init_bonus_sequencer and
; unpack_level_data.
;
; Inputs: None
; Outputs: hole_slot_state[0..4] = $00, hole_slot_actor[0..4] = $FF, a72 = $00
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2BEEa2 04init_hole_slotsldx#$04; x-ref: $0748
$2BF0a9 00b_2BF0lda#$00; x-ref: $2BFB
$2BF29d d0 2bstahole_slot_state,x
$2BF5a9 fflda#$ff
$2BF79d e9 2bstahole_slot_actor,x
$2BFAcadex
$2BFB10 f3bplb_2BF0
$2BFDa9 00lda#$00
$2BFF85 72stazp_ptr_hole_screen_lo
$2C0160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Restores the background tiles behind all active hole slots (first half of
; the bg save/restore pattern). Called at the top of the IRQ game loop to
; erase holes before logic runs; save_1x2_background at the bottom of the
; loop saves new bg tiles and redraws the holes in their updated state.
;
; Inputs: hole_slot_state/page/offset/bg_top/bg_bot[0..4], a72/a73 (screen ptr)
; Outputs: Screen memory restored at each active slot's position
; Side Effects: a73 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
restore_hole_backgrounds
$2C02a2 04ldx#$04; x-ref: $065C
$2C04bd d0 2bb_2C04ldahole_slot_state,x; x-ref: $2C1E
$2C07f0 14beqb_2C1D
$2C09bd d5 2bldahole_slot_page,x
$2C0C85 73stazp_ptr_hole_screen_hi
$2C0Ebc da 2bldyhole_slot_offset,x
$2C11bd df 2bldahole_slot_bg_top,x
$2C1491 72sta(zp_ptr_hole_screen_lo),y
$2C16e6 73inczp_ptr_hole_screen_hi
$2C18bd e4 2bldahole_slot_bg_bot,x
$2C1B91 72sta(zp_ptr_hole_screen_lo),y
$2C1Dcab_2C1Ddex; x-ref: $2C07
$2C1E10 e4bplb_2C04
$2C2060rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Second half of the hole bg save/restore pattern (called at the bottom of
; the IRQ loop, after restore_hole_backgrounds at the top).
; For each active slot (0..4):
; 1. Saves the two screen tiles at the slot position into hole_slot_bg_top/bot
; so restore_hole_backgrounds can erase them next frame.
; 2. Draws hole tile chars based on hole_slot_state:
; state >= $80 -> $2E / $56 (hole nearly healed)
; state & $10 -> $2E / $5B (hole mid-stage)
; else -> $2E / $19 (hole fully open)
; Skip entirely if actor_status_table[hole_slot_actor] == $03 and
; state & $01 is set (guard currently occupying the hole).
;
; Inputs: hole_slot_state/page/offset/actor[0..4], actor_status_table, a72/a73
; Outputs: hole_slot_bg_top/bot[0..4] saved; hole tiles written to screen
; Side Effects: a73 modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
save_hole_bg_and_draw
$2C21a2 00ldx#$00; X = 0: iterate slots 0..4 ; x-ref: $0686
$2C23bd d0 2bb_2C23ldahole_slot_state,x; x-ref: $2C8B
$2C26f0 60beqb_2C88; skip inactive slot (state=0)
$2C28bd d5 2bldahole_slot_page,x; --- step 1: save background tiles under hole sprite ---
$2C2B85 73stazp_ptr_hole_screen_hi; a73 = hole row page (top half)
$2C2Dbc da 2bldyhole_slot_offset,x; Y = hole column
$2C30b1 72lda(zp_ptr_hole_screen_lo),y; read tile at top row, col Y
$2C329d df 2bstahole_slot_bg_top,x; save top-row tile for restore next frame
$2C35e6 73inczp_ptr_hole_screen_hi; advance to bottom row page
$2C37b1 72lda(zp_ptr_hole_screen_lo),y; read tile at bottom row, col Y
$2C399d e4 2bstahole_slot_bg_bot,x; save bottom-row tile for restore next frame
$2C3Cc6 73deczp_ptr_hole_screen_hi; restore a73 to top row page for draw phase
$2C3Ebd e9 2bldahole_slot_actor,x; --- step 2: guard-occupying-hole skip check ---
$2C41a8tay; Y = linked dig slot index
$2C42b9 43 1eldadig_slot_status,y; read dig slot status
$2C45c9 03cmp#$03; status $03 = guard physically inside hole
$2C47d0 0abneb_2C53; no → proceed to draw
$2C49bd d0 2bldahole_slot_state,x; yes: also check state parity
$2C4C29 01and#$01; state & $01: odd tick gate
$2C4Ef0 03beqb_2C53; bit clear → proceed to draw anyway
$2C504c 88 2cjmpb_2C88; guard inside + odd tick → skip draw entirely
$2C53bc da 2bb_2C53ldyhole_slot_offset,x; --- step 3: draw hole tiles based on state --- ; x-ref: $2C47, $2C4E
$2C56bd d0 2bldahole_slot_state,x; reload state for threshold checks
$2C59c9 80cmp#$80; state >= $80 → nearly healed
$2C5B90 0dbccb_2C6A; no → check mid-stage bit
$2C5Da9 2elda#$2e; state >= $80: top=$2E (nearly-healed top)
$2C5F91 72sta(zp_ptr_hole_screen_lo),y
$2C61e6 73inczp_ptr_hole_screen_hi; advance to bottom row
$2C63a9 56lda#$56; bot=$56 (nearly-healed bottom)
$2C6591 72sta(zp_ptr_hole_screen_lo),y
$2C674c 88 2cjmpb_2C88
$2C6A29 10b_2C6Aand#$10; state & $10 set → mid-stage hole ; x-ref: $2C5B
$2C6Cf0 0dbeqb_2C7B; no → fully open hole
$2C6Ea9 2elda#$2e; mid-stage: top=$2E
$2C7091 72sta(zp_ptr_hole_screen_lo),y
$2C72e6 73inczp_ptr_hole_screen_hi; advance to bottom row
$2C74a9 5blda#$5b; bot=$5B (half-open hole)
$2C7691 72sta(zp_ptr_hole_screen_lo),y
$2C784c 88 2cjmpb_2C88
$2C7Ba9 2eb_2C7Blda#$2e; fully open: top=$2E ; x-ref: $2C6C
$2C7D91 72sta(zp_ptr_hole_screen_lo),y
$2C7Fe6 73inczp_ptr_hole_screen_hi; advance to bottom row
$2C81a9 19lda#$19; bot=$19 (fully open hole)
$2C8391 72sta(zp_ptr_hole_screen_lo),y
$2C854c 88 2cjmpb_2C88
$2C88e8b_2C88inx; next slot ; x-ref: $2C26, $2C50, $2C67, $2C78, $2C85
$2C89e0 05cpx#$05; all 5 slots done?
$2C8Bd0 96bneb_2C23
$2C8D60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Hole lifecycle state machine. Called once per frame for all 5 slots (4..0).
; Decrements hole_slot_state each tick and performs actions at key milestones:
;
; state $FF -> New hole: update color RAM attributes at slot pos and neighbors
; state $FA -> Shift hole down one row (inc hole_slot_page)
; state $F5 -> Shift hole down one more row
; state $30 -> Shift hole back up one row (dec hole_slot_page)
; state $10 -> Shift hole back up one more row
; state $05 -> Nudge hole_slot_offset left/right based on draw_offset and
; neighboring tile $C0 bit checks
; state $00 -> Seal hole: write healed-brick tile at actor's dig position,
; clear slot, copy hole pos to guard pos, reset guard
; (active=0, anim=0, dx/dy=0, move_rate=2, ai_bias=$7F)
; releasing the trapped guard back into normal AI
;
; Inputs: hole_slot_state/page/offset/actor[0..4], actor_dig_offset,
; actor_hole_slot, guard arrays, a5A/a5B
; Outputs: Color RAM updated; hole position shifted; guard state reset on expiry
; Side Effects: a5B modified; guard arrays written on hole expiry
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
tick_hole_state_machine
$2C8Ea2 04ldx#$04; iterate all 5 hole slots, high to low ; x-ref: $0674
$2C90bd d0 2bb_2C90ldahole_slot_state,x; skip inactive slot (state=0) ; x-ref: $2C96
$2C93d0 04bneb_2C99
$2C95caj_2C95dex; next slot (DEX + BPL = loop over 4..0) ; x-ref: $2D56, $2DA7
$2C9610 f8bplb_2C90
$2C9860rts
$2C99c9 ffb_2C99cmp#$ff; --- state $FF: new hole — write movement-grid property bits --- ; x-ref: $2C93
$2C9Bd0 3fbneb_2CDC
$2C9Dbd d5 2bldahole_slot_page,x; a5B = hole row page + 1 (points to property grid row)
$2CA085 5bstazp_ptr_playfield_hi
$2CA2e6 5binczp_ptr_playfield_hi
$2CA4bc da 2bldyhole_slot_offset,x
$2CA7b1 5alda(zp_ptr_playfield_lo),y; keep rope bit ($20), set walk-left+right bits ($03) → hole is passable both ways
$2CA929 20and#$20
$2CAB09 03ora#$03
$2CAD91 5asta(zp_ptr_playfield_lo),y
$2CAFc8iny; check right neighbor (Y+1) for wall bits ($C0)
$2CB0b1 5alda(zp_ptr_playfield_lo),y
$2CB288dey
$2CB329 c0and#$c0
$2CB5d0 07bneb_2CBE; right is wall → clear walk-right bit ($02)
$2CB7c0 20cpy#$20; at right edge (col $20) → also clear walk-right bit
$2CB9f0 03beqb_2CBE
$2CBB4c c4 2cjmpj_2CC4
$2CBEb1 5ab_2CBElda(zp_ptr_playfield_lo),y; right neighbor is open: keep walk-right bit, fall through to left check ; x-ref: $2CB5, $2CB9
$2CC029 fdand#$fd
$2CC291 5asta(zp_ptr_playfield_lo),y
$2CC4c0 01j_2CC4cpy#$01; at left edge (col $01) → clear walk-left bit ($01) ; x-ref: $2CBB
$2CC6f0 0bbeqb_2CD3
$2CC888dey; check left neighbor (Y-1) for wall bits ($C0)
$2CC9b1 5alda(zp_ptr_playfield_lo),y
$2CCBc8iny
$2CCC29 c0and#$c0
$2CCEd0 03bneb_2CD3; left neighbor is open: skip clearing walk-left bit → hole open both sides
$2CD04c 51 2djmpj_2D51
$2CD3b1 5ab_2CD3lda(zp_ptr_playfield_lo),y; left is wall or edge: clear walk-left bit ($01) ; x-ref: $2CC6, $2CCE
$2CD529 feand#$fe
$2CD791 5asta(zp_ptr_playfield_lo),y
$2CD94c 51 2djmpj_2D51
$2CDCc9 fab_2CDCcmp#$fa; --- state $FA: shift hole visual down one row --- ; x-ref: $2C9B
$2CDEd0 06bneb_2CE6
$2CE0fe d5 2binchole_slot_page,x; hole_slot_page++ (visual sinks one row into the floor)
$2CE34c 51 2djmpj_2D51
$2CE6c9 f5b_2CE6cmp#$f5; --- state $F5: shift hole visual down a second row --- ; x-ref: $2CDE
$2CE8d0 06bneb_2CF0
$2CEAfe d5 2binchole_slot_page,x; hole_slot_page++ (sinks another row)
$2CED4c 51 2djmpj_2D51
$2CF0c9 30b_2CF0cmp#$30; --- state $30: shift hole visual back up one row --- ; x-ref: $2CE8
$2CF2d0 06bneb_2CFA
$2CF4de d5 2bdechole_slot_page,x; hole_slot_page-- (hole rises back toward original row)
$2CF74c 51 2djmpj_2D51
$2CFAc9 10b_2CFAcmp#$10; --- state $10: shift hole visual back up a second row --- ; x-ref: $2CF2
$2CFCd0 06bneb_2D04
$2CFEde d5 2bdechole_slot_page,x; hole_slot_page-- (returns to original row)
$2D014c 51 2djmpj_2D51
$2D04c9 05b_2D04cmp#$05; --- state $05: nudge hole offset toward draw_offset (player column) --- ; x-ref: $2CFC
$2D06d0 49bnej_2D51
$2D08bd d5 2bldahole_slot_page,x; a5B = hole row page + 1 (property grid)
$2D0B85 5bstazp_ptr_playfield_hi
$2D0De6 5binczp_ptr_playfield_hi
$2D0Fbc da 2bldyhole_slot_offset,x
$2D12bd da 2bldahole_slot_offset,x
$2D15cd b4 0dcmpdraw_offset
$2D18b0 1dbcsb_2D37
$2D1Ac0 32cpy#$32; at right edge → can't go right, try nudge left instead
$2D1Cf0 08beqb_2D26
$2D1Ec8iny
$2D1Fb1 5alda(zp_ptr_playfield_lo),y; right neighbor has no wall bits → nudge right is safe
$2D2188dey
$2D2229 c0and#$c0
$2D24f0 0bbeqb_2D31
$2D2688b_2D26dey; right blocked or at edge: check left neighbor instead ; x-ref: $2D1C
$2D27b1 5alda(zp_ptr_playfield_lo),y
$2D29c8iny
$2D2A29 c0and#$c0
$2D2Cf0 20beqb_2D4E; left also blocked → neither direction free, leave offset unchanged
$2D2E4c 51 2djmpj_2D51
$2D31fe da 2bb_2D31inchole_slot_offset,x; nudge right: inc hole_slot_offset ; x-ref: $2D24, $2D49
$2D344c 51 2djmpj_2D51
$2D37c0 01b_2D37cpy#$01; hole_offset >= draw_offset: hole is right of player → try nudge left ; x-ref: $2D18
$2D39f0 08beqb_2D43; at left edge → can't go left, try nudge right instead
$2D3B88dey
$2D3Cb1 5alda(zp_ptr_playfield_lo),y
$2D3Ec8iny
$2D3F29 c0and#$c0
$2D41f0 0bbeqb_2D4E
$2D43c8b_2D43iny; left blocked or at edge: check right neighbor instead ; x-ref: $2D39
$2D44b1 5alda(zp_ptr_playfield_lo),y
$2D4688dey
$2D4729 c0and#$c0
$2D49f0 e6beqb_2D31; right also blocked → leave offset unchanged
$2D4B4c 51 2djmpj_2D51
$2D4Ede da 2bb_2D4Edechole_slot_offset,x; x-ref: $2D2C, $2D41
$2D51de d0 2bj_2D51dechole_slot_state,x; decrement state every tick; if it just hit 0 → seal the hole ; x-ref: $2CD0, $2CD9, $2CE3, $2CED, $2CF7, $2D01, $2D06, $2D2E, ...
$2D54f0 03beqb_2D59
$2D564c 95 2cjmpj_2C95
$2D59bd d5 2bb_2D59ldahole_slot_page,x; --- state $00: seal hole and release trapped guard --- ; x-ref: $2D54
$2D5C85 5bstazp_ptr_playfield_hi
$2D5Ee6 5binczp_ptr_playfield_hi
$2D60bd e9 2bldahole_slot_actor,x; get actor index that dug this hole
$2D63a8tay
$2D64b9 57 1eldadig_slot_col,y; read dig column → Y; write healed-brick property bits: keep rope ($20), set solid ($10)
$2D67a8tay
$2D68b1 5alda(zp_ptr_playfield_lo),y
$2D6A29 20and#$20
$2D6C09 10ora#$10
$2D6E91 5asta(zp_ptr_playfield_lo),y
$2D70bd e9 2bldahole_slot_actor,x
$2D73a8tay
$2D74a9 fflda#$ff
$2D7699 93 1estadig_slot_hole_idx,y; unlink actor from hole slot ($FF = none)
$2D79a9 fflda#$ff
$2D7B9d e9 2bstahole_slot_actor,x; unlink slot from actor ($FF = free)
$2D7Ebd d5 2bldahole_slot_page,x; teleport guard to sealed hole position
$2D819d 08 22staguard_page,x
$2D84bd da 2bldahole_slot_offset,x
$2D879d 03 22staguard_offset,x
$2D8Aa9 00lda#$00; reset guard to normal patrol state: active, no animation, no velocity
$2D8C9d f9 21staguard_active,x
$2D8Fa9 00lda#$00
$2D919d fe 21staguard_anim_frame,x
$2D949d 0d 22staguard_sprite_strip,x
$2D979d 12 22staguard_vspeed,x
$2D9A9d 17 22staguard_hspeed,x
$2D9Da9 02lda#$02; move_rate = 2 (normal patrol speed)
$2D9F9d 1c 22staguard_move_rate,x
$2DA2a9 7flda#$7f; ai_bias = $7F (neutral pathfinding bias)
$2DA49d 21 22staguard_ai_bias,x
$2DA74c 95 2cjmpj_2C95
; Player death animation phase (0-2).
; 0 = init: call init_game_variables, set draw_page/offset, read_tile -> advance to 1
; 1 = draw tile normally; when draw_offset == $1C advance to 2
; 2 = draw tile with anim strip ($10); when draw_offset == $05 back to 1
; Reset to 0 by render_text_stream at the start of the game-over screen.
$2DAAdeath_anim_state.byte$00; x-ref: $2E4E, $2FB7, $2FD6, $2FFC, $302A
.encode
.enc"screen"
$2DABtxt_game_over.text" GAME OVER", $ff; x-ref: $2E60
$2DBDtxt_game_over_l1.text" ", $63, $63, $63, $63, $63, $63, $63, $63, $63, $ff
$2DCFtxt_game_over_l2.text" ", $ff
$2DD1txt_game_over_l3.text" ", $ff
$2DD3txt_game_over_l4.text" SCORE:", $ff, $fe
$2DE2txt_hi_score.text" ", $ff; x-ref: $2F18
$2DE4txt_hi_score_l1.text" HIGH SCORE:", $ff
$2DF5txt_hi_score_l2.text" ", $ff
$2DF7txt_hi_score_l3.text" ", $ff
$2DF9txt_hi_score_l4.text" ", $ff
$2DFBtxt_hi_score_l5.text" ", $ff
$2DFDtxt_hi_score_l6.text" PRESS ANY KEY", $ff
$2E11txt_hi_score_l7.text" ", $ff, $fe
$2E14txt_new_hi_score.text" ", $ff; x-ref: $2F4A
$2E16txt_new_hi_score_l1.text" NEW HIGH SCORE:", $ff
$2E28txt_new_hi_score_l2.text" ", $ff
$2E2Atxt_new_hi_score_l3.text" ", $ff
$2E2Ctxt_new_hi_score_l4.text" ", $ff
$2E2Etxt_new_hi_score_l5.text" EMAIL ME YOUR SCORE ", $ff
$2E46txt_new_hi_score_l6.text" ", $ff, $fe
.endencode
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game-over screen setup. Called once when the player dies.
; 1. Stops audio, resets death_anim_state to 0, clears the sub-grid.
; 2. Renders txt_game_over stream into screen pages from $6C00 ($FF=newline,
; $FE=end).
; 3. Draws left/right border columns ($65) at cols 1 and $21 for every row.
; 4. Draws top/bottom decoration rows ($63/$C2/$FA) across cols 1..$20.
; 5. Renders current score as PETSCII digits at page $7000 offset $13,
; unpacking BCD nibbles of score_high_bcd / score_low_bcd (skips
; leading zero on the high nibble).
; 6. High score check:
; score <= hi-score -> render txt_hi_score at $7100; hi-score digits
; at $7200 offset $15
; score > hi-score -> save new hi-score to a2943/a2944; render
; txt_new_hi_score at $7100; new score at $7200
; offset $17
; 7. Draws playfield border, sets game_state_flag=2, redirects IRQ to
; handle_modal_dialog to wait for a key press.
;
; Inputs: score_high_bcd, score_low_bcd, a2943/a2944 (stored hi-score)
; Outputs: Screen populated with game-over layout; IRQ -> handle_modal_dialog
; Side Effects: death_anim_state=0, audio stopped, game_state_flag=2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
show_game_over_screen
$2E4920 92 0cjsrstop_audio; --- setup --- ; x-ref: $06E3
$2E4Ca9 00lda#$00; death_anim_state = 0 (reset walk animation)
$2E4E8d aa 2dstadeath_anim_state
$2E5120 f0 07jsrclear_sub_grid; wipe sub-grid (clears any leftover tile state)
$2E54a9 6clda#>playfield_row_5_page_base; --- section 1: render txt_game_over into offscreen buffer from $6C00 ---
$2E5685 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E58a9 00lda#<playfield_row_5_page_base; ZP pointer ($58/$59) = $6C00 (buffer start)
$2E5A85 58stazp_ptr_wipe_buf_lo
$2E5Ca2 00ldx#$00
$2E5Ea0 05j_2E5Eldy#$05; Y = col 5 (starting column of each text row) ; x-ref: $2E76
$2E60bd ab 2dj_2E60ldatxt_game_over,x; read layout byte: $FF=newline, $FE=end, else=char to write ; x-ref: $2E6F
$2E63c9 ffcmp#$ff
$2E65f0 0bbeqb_2E72
$2E67c9 fecmp#$fe
$2E69f0 0ebeqb_2E79
$2E6B91 58sta(zp_ptr_wipe_buf_lo),y; write char to offscreen buffer at current row+col
$2E6Dc8iny
$2E6Ee8inx
$2E6F4c 60 2ejmpj_2E60
$2E72e8b_2E72inx; $FF newline: advance to next buffer page (next row), skip one col ; x-ref: $2E65
$2E73c8iny
$2E74e6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E764c 5e 2ejmpj_2E5E
$2E79a9 67b_2E79lda#>playfield_row_buffer; --- section 2: draw left/right border columns ($65) on every row --- ; x-ref: $2E69
$2E7B85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E7Da0 01b_2E7Dldy#$01; reset page to playfield_row_buffer start ; x-ref: $2E8F
$2E7Fa9 65lda#$65; left border at col 1
$2E8191 58sta(zp_ptr_wipe_buf_lo),y
$2E83a0 21ldy#$21; right border at col $21
$2E85a9 65lda#$65
$2E8791 58sta(zp_ptr_wipe_buf_lo),y
$2E89e6 59inczp_ptr_wipe_buf_hi; stop at page $80 (past last screen row); TEMPF2 Temporary storage for FLPT value.
$2E8Ba5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E8Dc9 80cmp#$80
$2E8Fd0 ecbneb_2E7D
$2E91a0 01ldy#$01; --- section 3: draw top/bottom decoration rows across cols 1..$20 ---
$2E93a9 67b_2E93lda#>playfield_row_buffer; $63 = top border char → playfield top row ; x-ref: $2EAE
$2E9585 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E97a9 63lda#$63
$2E9991 58sta(zp_ptr_wipe_buf_lo),y
$2E9Ba9 7flda#>playfield_row_24_page_base; $C2 = bottom decoration char → page $7F (near-bottom row)
$2E9D85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2E9Fa9 c2lda#$c2
$2EA191 58sta(zp_ptr_wipe_buf_lo),y
$2EA3a9 7elda#>playfield_row_23_page_base; $FA = bottom border char → page $7E (bottom row)
$2EA585 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2EA7a9 falda#$fa
$2EA991 58sta(zp_ptr_wipe_buf_lo),y
$2EABc8iny; stop after col $20 (cols 1..$20 = 32 columns)
$2EACc0 21cpy#$21
$2EAEd0 e3bneb_2E93
$2EB0a9 4flda#$4f; top-left corner piece at $6701
$2EB28d 01 67staplayfield_row_0; Playfield Row 0 buffer
$2EB5a9 e4lda#$e4; bottom-right corner piece at $7E20
$2EB78d 20 7estaplayfield_row_23_col_32; Playfield Row 23, column 32 (bottom-right corner of the border)
$2EBAa9 70lda#>playfield_row_9_page_base; --- section 4: render current score at page $7000, col $13 ---
$2EBC85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2EBEa0 13ldy#$13; Y = $13: starting column for score digits
$2EC0ad 45 29ldascore_high_bcd; high nibble of score_high_bcd = ten-thousands digit
$2EC329 f0and#$f0
$2EC5f0 09beqb_2ED0; skip ten-thousands digit if zero (leading-zero suppression)
$2EC74alsra; 4x LSR + ADC #$30 → ten-thousands digit as PETSCII
$2EC84alsra
$2EC94alsra
$2ECA4alsra
$2ECB18clc
$2ECC69 30adc#$30
$2ECE91 58sta(zp_ptr_wipe_buf_lo),y; write ten-thousands digit to screen
$2ED0c8b_2ED0iny; low nibble → thousands digit (no suppression: always shown) ; x-ref: $2EC5
$2ED1ad 45 29ldascore_high_bcd
$2ED429 0fand#$0f
$2ED618clc
$2ED769 30adc#$30
$2ED991 58sta(zp_ptr_wipe_buf_lo),y
$2EDBc8iny
$2EDCad 46 29ldascore_low_bcd; high nibble of score_low_bcd → hundreds digit
$2EDF29 f0and#$f0
$2EE14alsra
$2EE24alsra
$2EE34alsra
$2EE44alsra
$2EE518clc
$2EE669 30adc#$30
$2EE891 58sta(zp_ptr_wipe_buf_lo),y
$2EEAc8iny
$2EEBad 46 29ldascore_low_bcd; low nibble of score_low_bcd → tens digit
$2EEE29 0fand#$0f
$2EF018clc
$2EF169 30adc#$30
$2EF391 58sta(zp_ptr_wipe_buf_lo),y
$2EF5c8iny; --- section 5: hi-score comparison ---
$2EF6ad 45 29ldascore_high_bcd
$2EF9cd 43 29cmphi_score_high_bcd
$2EFCf0 05beqb_2F03; high bytes equal → compare low bytes
$2EFE90 10bccb_2F10; score_high < hi_score_high → not a new record
$2F004c 36 2fjmpj_2F36; score_high > hi_score_high → new record
$2F03ad 46 29b_2F03ldascore_low_bcd; x-ref: $2EFC
$2F06cd 44 29cmphi_score_low_bcd
$2F09f0 05beqb_2F10; score_low <= hi_score_low → not a new record (equal counts as no new record)
$2F0B90 03bccb_2F10
$2F0D4c 36 2fjmpj_2F36
$2F10a9 71b_2F10lda#>playfield_row_10_page_base; --- section 6a: not a new record — render txt_hi_score at page $7100 --- ; x-ref: $2EFE, $2F09, $2F0B
$2F1285 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2F14a2 00ldx#$00
$2F16a0 05j_2F16ldy#$05; x-ref: $2F2E
$2F18bd e2 2dj_2F18ldatxt_hi_score,x; x-ref: $2F27
$2F1Bc9 ffcmp#$ff
$2F1Df0 0bbeqb_2F2A
$2F1Fc9 fecmp#$fe
$2F21f0 0ebeqb_2F31
$2F2391 58sta(zp_ptr_wipe_buf_lo),y
$2F25c8iny
$2F26e8inx
$2F274c 18 2fjmpj_2F18
$2F2Ae8b_2F2Ainx; x-ref: $2F1D
$2F2Bc8iny
$2F2Ce6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2F2E4c 16 2fjmpj_2F16
$2F31a0 15b_2F31ldy#$15; $FF newline: advance page, skip one col ; x-ref: $2F21
$2F334c 65 2fjmpj_2F65; hi-score digits start at col $15 → join shared digit-render path
$2F36ad 45 29j_2F36ldascore_high_bcd; --- section 6b: new record — save score, render txt_new_hi_score at $7100 --- ; x-ref: $2F00, $2F0D
$2F398d 43 29stahi_score_high_bcd; persist new hi-score high byte
$2F3Cad 46 29ldascore_low_bcd; persist new hi-score low byte
$2F3F8d 44 29stahi_score_low_bcd
$2F42a9 71lda#>playfield_row_10_page_base
$2F4485 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2F46a2 00ldx#$00
$2F48a0 05j_2F48ldy#$05; x-ref: $2F60
$2F4Abd 14 2ej_2F4Aldatxt_new_hi_score,x; x-ref: $2F59
$2F4Dc9 ffcmp#$ff
$2F4Ff0 0bbeqb_2F5C
$2F51c9 fecmp#$fe
$2F53f0 0ebeqb_2F63
$2F5591 58sta(zp_ptr_wipe_buf_lo),y
$2F57c8iny
$2F58e8inx
$2F594c 4a 2fjmpj_2F4A
$2F5Ce8b_2F5Cinx; x-ref: $2F4F
$2F5Dc8iny
$2F5Ee6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2F604c 48 2fjmpj_2F48
$2F63a0 17b_2F63ldy#$17; new hi-score digits start at col $17 (2 cols right of existing record position) ; x-ref: $2F53
$2F65a9 72j_2F65lda#>playfield_row_11_page_base; --- section 7: render hi-score digits at page $7200 (shared by both paths) --- ; x-ref: $2F33
$2F6785 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$2F69ad 43 29ldahi_score_high_bcd; high nibble → ten-thousands digit (no leading-zero suppression)
$2F6C29 f0and#$f0
$2F6E4alsra
$2F6F4alsra
$2F704alsra
$2F714alsra
$2F7218clc
$2F7369 30adc#$30
$2F7591 58sta(zp_ptr_wipe_buf_lo),y
$2F77c8iny; low nibble → thousands digit
$2F78ad 43 29ldahi_score_high_bcd
$2F7B29 0fand#$0f
$2F7D18clc
$2F7E69 30adc#$30
$2F8091 58sta(zp_ptr_wipe_buf_lo),y
$2F82c8iny
$2F83ad 44 29ldahi_score_low_bcd; high nibble of low byte → hundreds digit
$2F8629 f0and#$f0
$2F884alsra
$2F894alsra
$2F8A4alsra
$2F8B4alsra
$2F8C18clc
$2F8D69 30adc#$30
$2F8F91 58sta(zp_ptr_wipe_buf_lo),y
$2F91c8iny
$2F92ad 44 29ldahi_score_low_bcd; low nibble of low byte → tens digit
$2F9529 0fand#$0f
$2F9718clc
$2F9869 30adc#$30
$2F9A91 58sta(zp_ptr_wipe_buf_lo),y
$2F9Cc8iny
$2F9D20 b8 0ajsrtransition_from_empty_to_full; --- section 8: finalise and hand off to IRQ ---
$2FA0a9 02lda#$02; game_state_flag = 2 (game-over / modal state)
$2FA285 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$2FA4a9 adlda#<game_over_screen_irq; redirect IRQ → game_over_screen_irq (walk animation + keypress wait)
$2FA685 90stazp_irq_vector_lo; CINV Vector: Hardware Interrupt
$2FA8a9 2flda#>game_over_screen_irq
$2FAA85 91stazp_irq_vector_hi
$2FAC60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game-over screen IRQ handler. Installed by show_game_over_screen.
; Runs every frame while waiting for the player to press a key.
;
; Each frame:
; 1. Poll keyboard (wait_any_key):
; key pressed -> jsr switch_state_to_borders (exit game-over)
; no key -> run animation state machine below
;
; 2. death_anim_state drives a walk animation:
; 0 -> One-time init: init_game_variables, draw_page=$7C,
; draw_offset=$04, reset anim_frame/strip_base, read_tile
; -> advance to 1
; 1 -> Walk RIGHT: anim_strip_base=0, cycle anim_frame +4
; (0->4->8->12->0); on wrap inc draw_offset.
; When draw_offset==$1C -> advance to 2.
; read_tile + blit_screen.
; 2 -> Walk LEFT: anim_strip_base=$10, cycle anim_frame -4;
; on $0C dec draw_offset.
; When draw_offset==$05 -> back to 1.
; read_tile + blit_screen.
;
; Player oscillates right ($04->$1C) then left ($1C->$05) in a loop
; until a key is pressed.
;
; Inputs: death_anim_state, draw_page/offset, anim_frame/strip_base
; Outputs: Player walk animation rendered each frame
; Side Effects: Transitions to next game state on keypress via
; switch_state_to_borders
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2FAD20 65 1agame_over_screen_irqjsrwait_any_key; x-ref: $2FA4, $2FA8
$2FB0c9 ffcmp#$ff
$2FB2f0 03beqb_2FB7
$2FB420 bf 30jsrswitch_to_title_screen
$2FB7ad aa 2db_2FB7ldadeath_anim_state; x-ref: $2FB2
$2FBAd0 1dbneb_2FD9
$2FBC20 ba 0djsrinit_game_variables
$2FBFa9 7clda#$7c
$2FC18d b5 0dstadraw_page
$2FC4a9 04lda#$04
$2FC68d b4 0dstadraw_offset
$2FC9a9 00lda#$00
$2FCB8d b3 0dstaanim_frame
$2FCEa9 00lda#$00
$2FD08d b6 0dstaanim_strip_base
$2FD320 5f 11jsrsave_bg_and_draw_sprite
$2FD6ee aa 2dincdeath_anim_state
$2FD9c9 01b_2FD9cmp#$01; x-ref: $2FBA
$2FDBd0 28bneb_3005
$2FDD20 3c 11jsrrestore_sprite_bg
$2FE0a9 00lda#$00
$2FE28d b6 0dstaanim_strip_base
$2FE5ad b3 0dldaanim_frame
$2FE818clc
$2FE969 04adc#$04
$2FEB29 0cand#$0c
$2FED8d b3 0dstaanim_frame
$2FF0d0 03bneb_2FF5
$2FF2ee b4 0dincdraw_offset
$2FF5ad b4 0db_2FF5ldadraw_offset; x-ref: $2FF0
$2FF8c9 1ccmp#$1c
$2FFAd0 03bneb_2FFF
$2FFCee aa 2dincdeath_anim_state
$2FFF20 5f 11b_2FFFjsrsave_bg_and_draw_sprite; x-ref: $2FFA
$300220 86 08jsrblit_screen
$3005c9 02b_3005cmp#$02; x-ref: $2FDB
$3007d0 2abneb_3033
$300920 3c 11jsrrestore_sprite_bg
$300Ca9 10lda#$10
$300E8d b6 0dstaanim_strip_base
$3011ad b3 0dldaanim_frame
$301438sec
$3015e9 04sbc#$04
$301729 0cand#$0c
$30198d b3 0dstaanim_frame
$301Cc9 0ccmp#$0c
$301Ed0 03bneb_3023
$3020ce b4 0ddecdraw_offset
$3023ad b4 0db_3023ldadraw_offset; x-ref: $301E
$3026c9 05cmp#$05
$3028d0 03bneb_302D
$302Ace aa 2ddecdeath_anim_state
$302D20 5f 11b_302Djsrsave_bg_and_draw_sprite; x-ref: $3028
$303020 86 08jsrblit_screen
$3033ad 12 e8b_3033lda$e812; x-ref: $3007 PORT B or DDR B: Data Direction Register B
$303668pla
$3037a8tay
$303868pla
$3039aatax
$303A68pla
$303B40rti
; Title / level-select screen state machine.
; 0 = draw intro (print all text, draw level list, setup cursor)
; 1 = await player input (poll keys, update selected_level)
; 2 = confirmed (animate cursor flash, copy selected_level to ui_level_num, start game)
$303Ctitle_screen_state.byte$00; x-ref: $30C9, $30EC, $3161, $31CB
; Level highlighted in the level-select screen (1-10). Init $01.
; Modified each frame by level_select_delta (left -1 / right +1), clamped 1-10.
; Copied to ui_level_num and used to compute level_select_num when DIG is pressed.
$303Dselected_level.byte$00; x-ref: $30DD, $31DB, $31E8, $321C, $3251, $3296
.encode
.enc"screen"
; Title screen strings displayed on the level-select/intro screen.
; txt_title_lode_runner $303E: "LODE RUNNER ON COMMODORE PET"
; txt_title_underline $305B: 28 x $63 (decoration bar)
; txt_title_high_score $3078: "HIGH SCORE:"
; txt_title_select_level $3084: "SELECT STARTING LEVEL:"
; txt_title_dig_to_select $309B: "LEFT OR RIGHT, USE DIG TO SELECT"
; All strings are $FF-terminated PET screencodes.
txt_title_lode_runner
$303E.text"LODE RUNNER ON COMMODORE PET", $ff; x-ref: $339C
$305Btxt_title_underline.text$63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $ff; x-ref: $33B3
$3078txt_title_high_score.text"HIGH SCORE:", $ff; x-ref: $33CA
txt_title_select_level
$3084.text"SELECT STARTING LEVEL:", $ff; x-ref: $33E1
txt_title_dig_to_select
$309B.text"LEFT OR RIGHT, USE DIG TO SELECT", $ff; x-ref: $33F8
.endencode
; Left/right input delta for the level selector.
; Decremented ($-1) on left-key press, incremented (+1) on right-key press.
; Added to selected_level each frame; result clamped 1-10 before storing back.
$30BClevel_select_delta.byte$00; x-ref: $30CE, $3185, $3197, $31A6, $31D1, $31DF
; DIG-button confirm flag for the level-select screen.
; $01 = player pressed DIG/select; triggers transition to title_screen_state 2 (start game).
; $00 = not pressed.
$30BDselect_confirm_flag.byte$00; x-ref: $30D8, $3188, $31C3, $31C6
; Key debounce flag for the level selector.
; $FF = key currently held; input processing skipped until released.
; $00 = idle / key released; input polling active.
$30BEkey_debounce_flag.byte$00; x-ref: $30D3, $316E, $317D, $31D8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Transitions the game to the title / level-select screen.
; Resets all title screen state to defaults and redirects the IRQ vector
; to draw_layout_borders (the title screen handler).
;
; game_state_flag = 3
; title_screen_state = 0
; level_select_delta = 0
; key_debounce_flag = 0
; select_confirm_flag = 0
; selected_level = 1
; clears gameplay grid
; IRQ -> draw_layout_borders
;
; Called from game_over_screen_irq (key pressed), key-binding setup ($1A16),
; and end-of-game path ($37D6).
;
; Inputs: None
; Outputs: IRQ vector redirected; title screen variables reset
; Side Effects: gameplay grid cleared, game_state_flag=3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
switch_to_title_screen
$30BFa9 03lda#$03; x-ref: $1A16, $2FB4, $37D6
$30C185 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$30C3a9 00lda#$00
$30C585 58stazp_ptr_wipe_buf_lo
$30C7a9 00lda#$00
$30C98d 3c 30statitle_screen_state
$30CCa9 00lda#$00
$30CE8d bc 30stalevel_select_delta
$30D1a9 00lda#$00
$30D38d be 30stakey_debounce_flag
$30D6a9 00lda#$00
$30D88d bd 30staselect_confirm_flag
$30DBa9 01lda#$01
$30DD8d 3d 30staselected_level
$30E020 9d 07jsrclear_gameplay_grid
$30E3a9 eclda#<title_screen_irq
$30E585 90stazp_irq_vector_lo; CINV Vector: Hardware Interrupt
$30E7a9 30lda#>title_screen_irq
$30E985 91stazp_irq_vector_hi
$30EB60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Title screen / level-select IRQ handler. Installed by switch_to_title_screen.
; Three states driven by title_screen_state:
;
; State 0 (one-time draw):
; - Clear grids; draw $2F border line across row $75 (42 chars)
; - draw_static_ui_decorations, init_game_variables
; - draw_page=$7C, draw_offset=$10, input_dy=1, player_state=0
; - Draw all intro text (title, underline, high score, select level,
; dig-to-select, level number)
; - Write level numbers 01-10 at page $7100 in BCD (sed/adc #$01/cld),
; 4 columns apart starting at Y=2
; - draw_intro_selection_cursor -> inc title_screen_state -> state 1
;
; State 1 (level selection):
; - Debounce first keypress then each frame scan:
; Up key -> dec level_select_delta
; Down key -> inc level_select_delta
; Dig key -> select_confirm_flag = 1
; - Dig pressed -> inc title_screen_state -> state 2
; - Dir pressed -> clamp selected_level 1-10, redraw cursor
;
; State 2 (confirm + launch):
; - Flash cursor twice (erase/draw with busy_wait_pause between)
; - selected_level -> ui_level_num
; - level_select_num = (selected_level + 9) mod 10 (remaps display
; order 1-10 to internal level order)
; - jsr init_new_game -> launches game
;
; All states share epilogue j3232:
; draw_tile, ai_boundary_collision, process_player_state,
; read_tile, blit_screen, blit_sidebar, RTI.
;
; Inputs: title_screen_state, keyboard ($E810/$E812), selected_level
; Outputs: Title screen rendered; game launched on confirm
; Side Effects: title_screen_state advanced; ui_level_num/level_select_num
; set on confirm
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$30ECad 3c 30title_screen_irqldatitle_screen_state; --- State 0: one-time init --- check title_screen_state (0=init, 1=select, 2=launch) ; x-ref: $30E3, $30E7
$30EFd0 76bneb_3167; non-zero → skip init, go to state 1 or 2 dispatch
$30F120 9d 07jsrclear_gameplay_grid; clear back-buffer tile data
$30F420 0d 1cjsrclear_playfield_buffer; clear playfield collision/tile buffer
$30F7a9 75lda#>playfield_row_14_page_base; a5B = page $75 (border row target)
$30F985 5bstazp_ptr_playfield_hi
$30FBa9 2flda#$2f; $2F = PETSCII horizontal line char
$30FDa0 29ldy#$29; Y = 41 ($29) → 0, fill 42 columns with border char
$30FF91 5ab_30FFsta(zp_ptr_playfield_lo),y; x-ref: $3102
$310188dey
$310210 fbbplb_30FF
$310420 40 33jsrdraw_static_ui_decorations
$310720 ba 0djsrinit_game_variables
$310Aa9 10lda#$10
$310C8d b4 0dstadraw_offset
$310Fa9 7clda#$7c; draw_page = $7C: player icon row
$31118d b5 0dstadraw_page
$3114a9 01lda#$01; input_dy = 1: player walks downward
$31168d b8 0dstainput_dy
$3119a9 00lda#$00; player_state = 0: start walking animation
$311B8d b1 0dstaplayer_state
$311E20 5f 11jsrsave_bg_and_draw_sprite; place player icon at draw_page/draw_offset
$312120 94 33jsrdraw_intro_text_lode_runner; render all intro text strings
$312420 ab 33jsrdraw_intro_text_underline
$312720 c2 33jsrdraw_intro_text_high_score
$312A20 d9 33jsrdraw_intro_text_select_level
$312D20 f0 33jsrdraw_intro_text_dig_to_select
$313020 07 34jsrdraw_intro_hi_score
$3133a2 01ldx#$01; X = BCD level counter, starts at $01
$3135a9 71lda#>playfield_row_10_page_base
$313785 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3139a0 02ldy#$02; Y = column, starts at 2 (4 cols apart per level)
$313B8ab_313Btxa; high nibble of X → tens digit ; x-ref: $315C
$313C29 f0and#$f0
$313E4alsra
$313F4alsra
$31404alsra
$31414alsra
$314218clc
$314369 30adc#$30; +$30 → PETSCII tens digit, write to buffer
$314591 58sta(zp_ptr_wipe_buf_lo),y
$3147c8iny
$31488atxa; low nibble of X → units digit
$314929 0fand#$0f
$314B18clc
$314C69 30adc#$30; +$30 → PETSCII units digit, write to buffer
$314E91 58sta(zp_ptr_wipe_buf_lo),y
$3150c8iny
$3151c8iny; skip 2 cols (4 total per level)
$3152c8iny
$3153f8sed; BCD: X += 1 (01→02→03→...→10)
$31548atxa
$315518clc
$315669 01adc#$01
$3158aatax
$3159d8cld
$315Ae0 11cpx#$11; loop until all 10 level numbers drawn
$315Cd0 ddbneb_313B
$315E20 4d 32jsrdraw_level_select_cursor; draw selection box around default level
$3161ee 3c 30inctitle_screen_state; advance to state 1 (level selection)
$31644c 32 32jmpj_3232
$3167c9 01b_3167cmp#$01; --- State 1: level selection --- ; x-ref: $30EF
$3169f0 03beqb_316E
$316B4c f1 31jmpj_31F1
$316Ead be 30b_316Eldakey_debounce_flag; key_debounce_flag < 0 means debounced → skip wait ; x-ref: $3169
$317130 0dbmib_3180
$317320 65 1ajsrwait_any_key; wait for any key release before accepting input
$317630 03bmib_317B
$31784c 32 32jmpj_3232
$317Ba9 ffb_317Blda#$ff; armed: debounce_flag = $FF ; x-ref: $3176
$317D8d be 30stakey_debounce_flag
$318020 92 32b_3180jsrerase_level_select_cursor; clear delta and confirm each frame ; x-ref: $3171
$3183a9 00lda#$00
$31858d bc 30stalevel_select_delta
$31888d bd 30staselect_confirm_flag
$318Ba5 29ldazp_key_row_up; scan up key via PIA row/col
$318D8d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$3190ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$319325 32andzp_key_col_up; FRESPC Utility String Pointer
$3195d0 03bneb_319A
$3197ce bc 30declevel_select_delta; up pressed: delta = -1
$319Aa5 28b_319Aldazp_key_row_down; scan down key via PIA row/col ; x-ref: $3195 TXTTAB Pointer: Start of BASIC Text
$319C8d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$319Fad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$31A225 31andzp_key_col_down
$31A4d0 03bneb_31A9
$31A6ee bc 30inclevel_select_delta; down pressed: delta = +1
$31A9a5 2cb_31A9ldazp_key_row_dig1; scan dig key (two key combos checked) ; x-ref: $31A4 ARYTAB Pointer: Start of BASIC Arrays
$31AB8d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$31AEad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$31B125 35andzp_key_col_dig1
$31B3f0 0cbeqb_31C1
$31B5a5 2dldazp_key_row_dig2
$31B78d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$31BAad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$31BD25 36andzp_key_col_dig2; CURLIN Current BASIC Line Number
$31BFd0 05bneb_31C6
$31C1a9 01b_31C1lda#$01; dig pressed: set confirm flag ; x-ref: $31B3
$31C38d bd 30staselect_confirm_flag
$31C6ad bd 30b_31C6ldaselect_confirm_flag; dig confirmed → advance to state 2 ; x-ref: $31BF
$31C9f0 06beqb_31D1
$31CBee 3c 30inctitle_screen_state
$31CE4c eb 31jmpj_31EB
$31D1ad bc 30b_31D1ldalevel_select_delta; no delta → nothing to do ; x-ref: $31C9
$31D4f0 15beqj_31EB
$31D6a9 00lda#$00
$31D88d be 30stakey_debounce_flag; clear debounce for next move
$31DBad 3d 30ldaselected_level; selected_level += delta
$31DE18clc
$31DF6d bc 30adclevel_select_delta
$31E2f0 4ebeqj_3232; clamp: skip update if result == 0 (underflow)
$31E4c9 0bcmp#$0b; clamp: skip update if result == 11 (overflow)
$31E6f0 4abeqj_3232
$31E88d 3d 30staselected_level; valid: store new level selection
$31EB20 4d 32j_31EBjsrdraw_level_select_cursor; redraw cursor at new position ; x-ref: $31CE, $31D4
$31EE4c 32 32jmpj_3232
$31F1a0 01j_31F1ldy#$01; --- State 2: flash cursor and launch --- Y counts 2 flash cycles ; x-ref: $316B
$31F398b_31F3tya; erase cursor for flash-off frame ; x-ref: $321A
$31F448pha
$31F520 92 32jsrerase_level_select_cursor
$31F820 86 08jsrblit_screen
$31FB20 27 09jsrblit_sidebar
$31FEa2 05ldx#$05
$320020 c8 09b_3200jsrbusy_wait_pause; busy-wait x5 → flash-off hold ; x-ref: $3204
$3203cadex
$320410 fabplb_3200
$320620 4d 32jsrdraw_level_select_cursor; draw cursor for flash-on frame
$320920 86 08jsrblit_screen
$320C20 27 09jsrblit_sidebar
$320Fa2 05ldx#$05; busy-wait x5 → flash-on hold
$321120 c8 09b_3211jsrbusy_wait_pause; x-ref: $3215
$3214cadex; loop: DEY → repeat for second flash cycle
$321510 fabplb_3211
$321768pla
$3218a8tay
$321988dey
$321A10 d7bplb_31F3
$321Cad 3d 30ldaselected_level; selected_level → ui_level_num (active level)
$321F8d df 2astaui_level_num
$322218clc; (selected_level + 9) mod 10 → level_select_num
$322369 09adc#$09
$3225c9 0bcmp#$0b
$322790 03bccb_322C
$322938sec; remap overflow: >=11 → subtract 10
$322Ae9 0asbc#$0a
$322C8d e0 2ab_322Cstalevel_select_num; store full-circuit sentinel for advance_level ; x-ref: $3227
$322F20 b0 06jsrinit_new_game; launch game from this level
$323220 3c 11j_3232jsrrestore_sprite_bg; --- shared epilogue (all states) --- ; x-ref: $3164, $3178, $31E2, $31E6, $31EE
$323520 c5 32jsrai_boundary_collision; animate player icon walking on title
$323820 66 10jsrprocess_player_state
$323B20 5f 11jsrsave_bg_and_draw_sprite
$323E20 86 08jsrblit_screen
$324120 27 09jsrblit_sidebar
$3244ad 12 e8lda$e812; acknowledge VIA interrupt; PORT B or DDR B: Data Direction Register B
$324768pla
$3248a8tay
$324968pla
$324Aaatax
$324B68pla
$324C40rti
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the level-select cursor box on the title screen.
; Complement of erase_level_select_cursor — same column formula, same geometry.
;
; Column: Y = (selected_level - 1) * 4 + 1
; -> level 1=col 1, level 2=col 5, level 3=col 9, etc.
;
; Box layout (PET box-drawing chars):
; page $70 (row 9): $70 $40 $40 $6E (top edge: ┌──┐)
; page $71 (row 10): $5D $5D (sides: │ │)
; page $72 (row 11): $6D $40 $40 $7D (bottom edge: └──┘)
;
; Inputs: selected_level, a58/a59 (screen pointer)
; Outputs: 10 box-drawing chars written to pages $70/$71/$72
; Side Effects: a59 left at $72 on exit; Y modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_level_select_cursor
$324Da9 71lda#>playfield_row_10_page_base; x-ref: $315E, $31EB, $3206
$324F85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3251ad 3d 30ldaselected_level
$325438sec
$3255e9 01sbc#$01
$32570aasla
$32580aasla
$325918clc
$325A69 01adc#$01; A = (selected_level - 1) * 4 + 1
$325Ca8tay
$325Da9 5dlda#$5d; |
$325F91 58sta(zp_ptr_wipe_buf_lo),y
$3261c6 59deczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3263a9 70lda#$70; top-left corner
$326591 58sta(zp_ptr_wipe_buf_lo),y
$3267c8iny
$3268a9 40lda#$40; -
$326A91 58sta(zp_ptr_wipe_buf_lo),y
$326Cc8iny
$326Da9 40lda#$40; -
$326F91 58sta(zp_ptr_wipe_buf_lo),y
$3271c8iny
$3272a9 6elda#$6e; top-right corner
$327491 58sta(zp_ptr_wipe_buf_lo),y
$3276e6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3278a9 5dlda#$5d; |
$327A91 58sta(zp_ptr_wipe_buf_lo),y
$327Ce6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$327Ea9 7dlda#$7d; bottom-right corner
$328091 58sta(zp_ptr_wipe_buf_lo),y
$328288dey
$3283a9 40lda#$40; -
$328591 58sta(zp_ptr_wipe_buf_lo),y
$328788dey
$3288a9 40lda#$40; -
$328A91 58sta(zp_ptr_wipe_buf_lo),y
$328C88dey
$328Da9 6dlda#$6d; bottom-left corner
$328F91 58sta(zp_ptr_wipe_buf_lo),y
$329160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Erases the level-select cursor box on the title screen by writing spaces
; ($20) at the 10 outline positions of a 3-row x 4-col rectangle.
; Inverse of draw_intro_selection_cursor.
;
; Column: Y = (selected_level - 1) * 4 + 1
; -> level 1=col 1, level 2=col 5, level 3=col 9, etc.
;
; Positions cleared (clockwise):
; page $71 (row 10): col Y (left side)
; page $70 (row 9): cols Y..Y+3 (top edge)
; page $71 (row 10): col Y+3 (right side)
; page $72 (row 11): cols Y+3..Y (bottom edge)
;
; Inputs: selected_level, a58/a59 (screen pointer)
; Outputs: 10 screen cells at pages $70/$71/$72 cleared to $20
; Side Effects: a59 left at $72 on exit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
erase_level_select_cursor
$3292a9 71lda#>playfield_row_10_page_base; x-ref: $3180, $31F5
$329485 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3296ad 3d 30ldaselected_level
$329938sec
$329Ae9 01sbc#$01
$329C0aasla
$329D0aasla
$329E18clc
$329F69 01adc#$01
$32A1a8tay
$32A2a9 20lda#$20
$32A491 58sta(zp_ptr_wipe_buf_lo),y
$32A6c6 59deczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$32A891 58sta(zp_ptr_wipe_buf_lo),y
$32AAc8iny
$32AB91 58sta(zp_ptr_wipe_buf_lo),y
$32ADc8iny
$32AE91 58sta(zp_ptr_wipe_buf_lo),y
$32B0c8iny
$32B191 58sta(zp_ptr_wipe_buf_lo),y
$32B3e6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$32B591 58sta(zp_ptr_wipe_buf_lo),y
$32B7e6 59inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$32B991 58sta(zp_ptr_wipe_buf_lo),y
$32BB88dey
$32BC91 58sta(zp_ptr_wipe_buf_lo),y
$32BE88dey
$32BF91 58sta(zp_ptr_wipe_buf_lo),y
$32C188dey
$32C291 58sta(zp_ptr_wipe_buf_lo),y
$32C460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Steers the walking player icon on the title screen at playfield boundaries.
; Called every frame from the title_screen_irq epilogue (j3232), just before
; process_player_state moves the sprite.
;
; The sprite walks within a rectangle: cols $08–$21, row pages $74–$7C.
; When the sprite reaches a wall column ($08 or $21) AND a corner row ($74 or
; $7C), this routine updates input_dx/input_dy to turn it. Mid-wall positions
; are ignored — process_player_state handles those via tile collision.
;
; Corner decision table:
; col $21 (right), row $7C (bottom-right): random → up (dy=-1) or left (dx=-1)
; col $21 (right), row $74 (top-right): always turn down+right (dx=1, dy=0)
; col $08 (left), row $7C (bottom-left): always turn left (dx=-1, dy=0)
; col $08 (left), row $74 (top-left): random → right (dx=1) or down (dy=1)
; all other positions: no change — RTS immediately
;
; Inputs: draw_offset (sprite column), draw_page (sprite row page)
; Outputs: input_dx / input_dy updated with new movement direction
; Side Effects: Calls get_random at the two randomised corners
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ai_boundary_collision
$32C5ad b4 0dldadraw_offset; sprite column check ; x-ref: $3235
$32C8c9 21cmp#$21; right wall (col 33)?
$32CAd0 39bneb_3305; no → check left wall
$32CCad b5 0dldadraw_page; yes — check which row
$32CFc9 7ccmp#$7c; bottom-right corner?
$32D1d0 21bneb_32F4; no → check top-right corner
$32D320 72 07jsrget_random; yes: bottom-right corner — flip a coin
$32D629 01and#$01
$32D8f0 0dbeqb_32E7; rand bit 0 = 0 → turn up
$32DAa9 00lda#$00; rand bit 0 = 1 → turn left: dx=0, dy=-1
$32DC8d b7 0dstainput_dx
$32DFa9 fflda#$ff
$32E18d b8 0dstainput_dy; dy = -1 (move up)
$32E44c 3f 33jmpr_333F
$32E7a9 ffb_32E7lda#$ff; turn up: dx=0 (already 0), dy=-1 ($FF) ; x-ref: $32D8
$32E98d b7 0dstainput_dx
$32ECa9 00lda#$00
$32EE8d b8 0dstainput_dy; dy = 0 (move right/left only)
$32F14c 3f 33jmpr_333F
$32F4c9 74b_32F4cmp#$74; top-right corner (row $74)? ; x-ref: $32D1
$32F6d0 47bner_333F; no (mid-wall) → no change, return
$32F8a9 01lda#$01; yes: top-right corner — turn down: dx=1, dy=0
$32FA8d b7 0dstainput_dx
$32FDa9 00lda#$00
$32FF8d b8 0dstainput_dy
$33024c 3f 33jmpr_333F
$3305c9 08b_3305cmp#$08; left wall (col 8)? ; x-ref: $32CA
$3307d0 36bner_333F; no (mid-field) → no change, return
$3309ad b5 0dldadraw_page; yes — check which row
$330Cc9 7ccmp#$7c; bottom-left corner?
$330Ed0 0dbneb_331D; no → check top-left corner
$3310a9 fflda#$ff; yes: bottom-left corner — turn left: dx=-1, dy=0
$33128d b7 0dstainput_dx
$3315a9 00lda#$00
$33178d b8 0dstainput_dy
$331A4c 3f 33jmpr_333F
$331Dc9 74b_331Dcmp#$74; top-left corner (row $74)? ; x-ref: $330E
$331Fd0 1ebner_333F; no (mid-wall) → no change, return
$332120 72 07jsrget_random; yes: top-left corner — flip a coin
$332429 01and#$01
$3326f0 0dbeqb_3335; rand bit 0 = 0 → turn down
$3328a9 01lda#$01; rand bit 0 = 1 → turn right: dx=1, dy=0
$332A8d b7 0dstainput_dx
$332Da9 00lda#$00
$332F8d b8 0dstainput_dy
$33324c 3f 33jmpr_333F
$3335a9 00b_3335lda#$00; turn down: dx=0, dy=1 ; x-ref: $3326
$33378d b7 0dstainput_dx
$333Aa9 01lda#$01
$333C8d b8 0dstainput_dy
$333F60r_333Frts; common exit — all paths converge here ; x-ref: $32E4, $32F1, $32F6, $3302, $3307, $331A, $331F, $3332
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the fixed decorative frame of the title/level-select screen into the
; playfield back-buffer. Called once during state 0 of title_screen_irq.
;
; Phase 1 — Three horizontal bar rows, columns 34→7 (Y = $22→$07):
; One loop pass writes to three pages via a59 switching each iteration:
; page $7E col Y: $FA (bottom decoration bar)
; page $7F col Y: $C2 (near-bottom bar)
; page $75 col Y: $63 (top border bar, 28 columns)
;
; Phase 2 — Vertical pillar segments, pages $74–$7D (10 rows):
; Each page gets a 3-char sequence at two column groups:
; cols 7–9: $67, $43, $65 (left pillar)
; cols 32–34: $67, $43, $65 (right pillar)
;
; Phase 3 — Two corner pieces (absolute):
; $7509 (page $75, col 9): $4F (top-right corner)
; $7520 (page $75, col 32): $50 (top-left corner of divider)
;
; Note: a58 = $00 (set by caller); (a58),Y uses a59 as the page selector.
;
; Inputs: None (a58 = $00 must be set by caller)
; Outputs: Playfield buffer pages $74–$7F decorated with frame chars
; Side Effects: Modifies a59 (ZP pointer hi), Y
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_static_ui_decorations
$3340a0 22ldy#$22; --- Phase 1: three horizontal bar rows, cols 34..7 --- ; x-ref: $3104
$3342a9 7eb_3342lda#>playfield_row_23_page_base; a59 = $7E (bottom decoration row) ; x-ref: $335B
$334485 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3346a9 falda#$fa; $FA: bottom decoration bar char
$334891 58sta(zp_ptr_wipe_buf_lo),y; write to page $7E, col Y
$334Ae6 59inczp_ptr_wipe_buf_hi; a59 → $7F (near-bottom bar row); TEMPF2 Temporary storage for FLPT value.
$334Ca9 c2lda#$c2; $C2: near-bottom bar char
$334E91 58sta(zp_ptr_wipe_buf_lo),y; write to page $7F, col Y
$3350a9 75lda#>playfield_row_14_page_base; a59 → $75 (top border row)
$335285 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3354a9 63lda#$63; $63: top border bar char
$335691 58sta(zp_ptr_wipe_buf_lo),y; write to page $75, col Y
$335888dey; next column (right→left)
$3359c0 06cpy#$06; done when col 6 reached (cols 34..7 = 28 columns)
$335Bd0 e5bneb_3342
$335Da9 74lda#>playfield_row_13_page_base; --- Phase 2: vertical pillar segments, pages $74–$7D (10 rows) ---
$335F85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3361a0 07b_3361ldy#$07; left pillar starts at col 7 ; x-ref: $3387
$3363a9 67lda#$67; $67: left pillar char 1
$336591 58sta(zp_ptr_wipe_buf_lo),y
$3367c8iny
$3368a9 43lda#$43; $43: left pillar char 2
$336A91 58sta(zp_ptr_wipe_buf_lo),y
$336Cc8iny
$336Da9 65lda#$65; $65: left pillar char 3 (col 9)
$336F91 58sta(zp_ptr_wipe_buf_lo),y
$3371a0 20ldy#$20; right pillar starts at col 32 ($20)
$3373a9 67lda#$67
$337591 58sta(zp_ptr_wipe_buf_lo),y; $67: right pillar char 1 (col 32)
$3377c8iny
$3378a9 43lda#$43
$337A91 58sta(zp_ptr_wipe_buf_lo),y; $43: right pillar char 2 (col 33)
$337Cc8iny
$337Da9 65lda#$65
$337F91 58sta(zp_ptr_wipe_buf_lo),y; $65: right pillar char 3 (col 34)
$3381e6 59inczp_ptr_wipe_buf_hi; advance to next row page; TEMPF2 Temporary storage for FLPT value.
$3383a5 59ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3385c9 7ecmp#$7e; stop before page $7E (bottom bars)
$3387d0 d8bneb_3361
$3389a9 4flda#$4f; --- Phase 3: corner pieces ---
$338B8d 09 75staplayfield_row_14_col_8; $4F: top-right corner at page $75, col 8
$338Ea9 50lda#$50; $50: top-left divider corner
$33908d 20 75staplayfield_row_14_col_31; at page $75, col 31
$339360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the first line of the intro/title screen text into the playfield buffer.
; Sets the high byte of the buffer pointer to $67 (Row 0) and starts at column 7.
; Copies characters from $303E until a #$FF terminator is reached.
;
; Inputs: String at $303E.
; Outputs: Populates row 0 of the intro playfield buffer.
; Side Effects: Modifies $59 (pointer high byte), X (string index), Y (data index).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_intro_text_lode_runner
$3394a9 67lda#>playfield_row_buffer; x-ref: $3121
$339685 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$3398a0 07ldy#$07
$339Aa2 00ldx#$00
$339Cbd 3e 30j_339Cldatxt_title_lode_runner,x; x-ref: $33A7
$339Fc9 ffcmp#$ff
$33A1f0 07beqr_33AA
$33A391 58sta(zp_ptr_wipe_buf_lo),y
$33A5e8inx
$33A6c8iny
$33A74c 9c 33jmpj_339C
$33AA60r_33AArts; x-ref: $33A1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the second line of the intro/title screen text into the playfield buffer.
; Sets the high byte of the buffer pointer to $68 (Row 1) and starts at column 7.
; Copies characters from $305B until a #$FF terminator is reached.
;
; Inputs: String at $305B.
; Outputs: Populates row 1 of the intro playfield buffer.
; Side Effects: Modifies $59 (pointer high byte), X (string index), Y (data index).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_intro_text_underline
$33ABa9 68lda#>playfield_row_1_page_base; x-ref: $3124
$33AD85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$33AFa0 07ldy#$07
$33B1a2 00ldx#$00
$33B3bd 5b 30j_33B3ldatxt_title_underline,x; x-ref: $33BE
$33B6c9 ffcmp#$ff
$33B8f0 07beqr_33C1
$33BA91 58sta(zp_ptr_wipe_buf_lo),y
$33BCe8inx
$33BDc8iny
$33BE4c b3 33jmpj_33B3
$33C160r_33C1rts; x-ref: $33B8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the third line of the intro/title screen text into the playfield buffer.
; Sets the high byte of the buffer pointer to $6A (Row 3) and starts at column 13.
; Copies characters from $3078 until a #$FF terminator is reached.
;
; Inputs: String at $3078.
; Outputs: Populates row 3 of the intro playfield buffer.
; Side Effects: Modifies $59 (pointer high byte), X (string index), Y (data index).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_intro_text_high_score
$33C2a9 6alda#>playfield_row_3_page_base; x-ref: $3127
$33C485 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$33C6a0 0dldy#$0d
$33C8a2 00ldx#$00
$33CAbd 78 30j_33CAldatxt_title_high_score,x; x-ref: $33D5
$33CDc9 ffcmp#$ff
$33CFf0 07beqr_33D8
$33D191 58sta(zp_ptr_wipe_buf_lo),y
$33D3e8inx
$33D4c8iny
$33D54c ca 33jmpj_33CA
$33D860r_33D8rts; x-ref: $33CF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the fourth line of the intro/title screen text into the playfield buffer.
; Sets the high byte of the buffer pointer to $6D (Row 6) and starts at column 10.
; Copies characters from $3084 until a #$FF terminator is reached.
;
; Inputs: String at $3084.
; Outputs: Populates row 6 of the intro playfield buffer.
; Side Effects: Modifies $59 (pointer high byte), X (string index), Y (data index).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_intro_text_select_level
$33D9a9 6dlda#>playfield_row_6_page_base; x-ref: $312A
$33DB85 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$33DDa0 0aldy#$0a
$33DFa2 00ldx#$00
$33E1bd 84 30j_33E1ldatxt_title_select_level,x; x-ref: $33EC
$33E4c9 ffcmp#$ff
$33E6f0 07beqr_33EF
$33E891 58sta(zp_ptr_wipe_buf_lo),y
$33EAe8inx
$33EBc8iny
$33EC4c e1 33jmpj_33E1
$33EF60r_33EFrts; x-ref: $33E6
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the fifth line of the intro/title screen text into the playfield buffer.
; Sets the high byte of the buffer pointer to $6E (Row 7) and starts at column 12.
; Copies characters from $3096 until a #$FF terminator is reached.
;
; Inputs: String at $3096.
; Outputs: Populates row 7 of the intro playfield buffer.
; Side Effects: Modifies $59 (pointer high byte), X (string index), Y (data index).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_intro_text_dig_to_select
$33F0a9 6elda#>playfield_row_7_page_base; x-ref: $312D
$33F285 59stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value.
$33F4a0 05ldy#$05
$33F6a2 00ldx#$00
$33F8bd 9b 30j_33F8ldatxt_title_dig_to_select,x; x-ref: $3403
$33FBc9 ffcmp#$ff
$33FDf0 07beqr_3406
$33FF91 58sta(zp_ptr_wipe_buf_lo),y
$3401e8inx
$3402c8iny
$34034c f8 33jmpj_33F8
$340660r_3406rts; x-ref: $33FD
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Converts the BCD high score at $2943 and $2944 into four numeric PETSCII characters.
; Writes the resulting characters to the playfield buffer at $6A19 to $6A1C (Row 3, Columns 25-28).
; Used to display the high score on the intro/title screen.
;
; Inputs: BCD high score at $2943-$2944.
; Outputs: Writes numerical characters to $6A19 to $6A1C.
; Side Effects: None.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3407ad 43 29draw_intro_hi_scoreldahi_score_high_bcd; x-ref: $3130
$340A29 f0and#$f0
$340C4alsra
$340D4alsra
$340E4alsra
$340F4alsra
$341018clc
$341169 30adc#$30
$34138d 19 6astaplayfield_intro_hi_score_thousands; High score character digit 1 (high BCD nibble of hi_score_high_bcd); High Score thousands digit (Title Screen)
$3416ad 43 29ldahi_score_high_bcd
$341929 0fand#$0f
$341B18clc
$341C69 30adc#$30
$341E8d 1a 6astaplayfield_intro_hi_score_hundreds; High score character digit 2 (low BCD nibble of hi_score_high_bcd); High Score hundreds digit (Title Screen)
$3421ad 44 29ldahi_score_low_bcd
$342429 f0and#$f0
$34264alsra
$34274alsra
$34284alsra
$34294alsra
$342A18clc
$342B69 30adc#$30
$342D8d 1b 6astaplayfield_intro_hi_score_tens; High score character digit 3 (high BCD nibble of hi_score_low_bcd); High Score tens digit (Title Screen)
$3430ad 44 29ldahi_score_low_bcd
$343329 0fand#$0f
$343518clc
$343669 30adc#$30
$34388d 1c 6astaplayfield_intro_hi_score_ones; High score character digit 4 (low BCD nibble of hi_score_low_bcd); High Score ones digit (Title Screen)
$343B60rts
$343Cmenu_cursor_page.byte$72; x-ref: $363A, $370C, $371F, $374D, $37B0
$343Dmenu_nav_delta.byte$00; x-ref: $36DE, $36ED, $36FC, $36FF, $3710, $3714
$343Emenu_key_lockout.byte$00; x-ref: $363F, $36A1, $36AF, $3709
.encode
.enc"screen"
txt_game_paused_table
$343F.text" ", $ff; x-ref: $3652, $367B, $3792
$3451txt_game_paused_l1.text" uccccccccccccci ", $ff
$3463txt_game_paused_l2.text" b h ", $ff
$3475txt_game_paused_l3.text" b GAME PAUSED h ", $ff
$3487txt_game_paused_l4.text" b ", $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, " h ", $ff
$3499txt_game_paused_l5.text" b h ", $ff
$34ABtxt_game_paused_l6.text" b CONTINUE h ", $ff
$34BDtxt_game_paused_l7.text" b h ", $ff
$34CFtxt_game_paused_l8.text" bRESTART LEVELh ", $ff
$34E1txt_game_paused_l9.text" b h ", $ff
$34F3txt_game_paused_la.text" b END GAME h ", $ff
$3505txt_game_paused_lb.text" b h ", $ff
$3517txt_game_paused_lc.text" jfffffffffffffk ", $ff
$3529txt_game_paused_ld.text" ", $ff, $fe
.endencode
; Pause/game-menu banner render buffer (251 bytes). Parallel to banner_game_paused_table ($343F).
; Both tables are scanned together using the same X index by menu_draw_banner ($3786):
; - banner_game_paused_table drives control: $FF=newline, $FE=end-of-table.
; - For every other position, THIS table supplies the character written to the screen buffer.
;
; The routine at $364C pre-populates this table from level/config data before drawing.
; $FF bytes here align with the $FF newline sentinels in the structure table (never rendered).
; Default fill is $01; dynamic slots are overwritten with actual screen chars at runtime.
;
; The banner displays the pause overlay:
; top/bottom borders (u/c.../i, j/f.../k chars), sides (b/h),
; and menu rows: CONTINUE / RESTART LEVEL / END GAME.
$353Cbanner_data_table.fill17, $01; x-ref: $365F, $379D
$354D.byte$ff, $02
$354F.fill15, $01
$355E.byte$02, $ff, $03
$3561.fill15, $01
$3570.byte$03, $ff, $04
$3573.fill15, $01
$3582.byte$04, $ff, $05
$3585.fill15, $01
$3594.byte$05, $ff, $06
$3597.fill15, $01
$35A6.byte$06, $ff, $07
$35A9.fill15, $01
$35B8.byte$07, $ff, $08
$35BB.fill15, $01
$35CA.byte$08, $ff, $09
$35CD.fill15, $01
$35DC.byte$09, $ff, $0a
$35DF.fill15, $01
$35EE.byte$0a, $ff, $0b
$35F1.fill15, $01
$3600.byte$0b, $ff, $0c
$3603.fill15, $01
$3612.byte$0c, $ff, $0d
$3615.fill15, $01
$3624.byte$0d, $ff, $0e
$3627.fill15, $01
$3636.byte$0e, $ff
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Opens the Pause Menu overlay. Called directly from the gameplay IRQ when
; the PAUSE key is detected.
;
; Two-pass operation over txt_game_paused_table (layout: $FF=newline, $FE=end):
;
; Pass 1 (j3652): walk the layout table; for each non-control position,
; read the current offscreen buffer char and save it to banner_data_table.
; This snapshots the game background so menu_draw_banner can restore it
; on every subsequent tick.
;
; Pass 2 (j367B): walk the same layout table again; for each non-control
; position, write the layout table char directly into the offscreen buffer.
; This paints the full GAME PAUSED box (borders + CONTINUE / RESTART
; LEVEL / END GAME rows) into the buffer in one sweep.
;
; After both passes: installs pause_menu_irq into the IRQ vector (ZP $90/$91)
; so the next interrupt tick transfers control to the menu handler.
;
; Inputs: None
; Outputs: banner_data_table (background snapshot); offscreen buffer (menu text);
; ZP $90/$91 (IRQ vector → pause_menu_irq); game_state_flag=4
; Side Effects: a343C=$72 (CONTINUE pre-selected); a343E=1 (debounce lockout,
; pause key still held); ZP $79/$7A clobbered
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3638a9 72open_pause_menulda#$72; pre-select CONTINUE (first row, page $72) ; x-ref: $2919
$363A8d 3c 34stamenu_cursor_page
$363Da9 01lda#$01; key-release lockout: pause key is still held on entry
$363F8d 3e 34stamenu_key_lockout
$3642a9 04lda#$04; game_state_flag = 4 (pause/menu state)
$364485 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$3646a9 6clda#$6c; ZP pointer = $6C09: offscreen buffer row 5, col 8 (banner top-left)
$364885 7astazp_ptr_menu_buf_hi
$364Aa9 09lda#$09
$364C85 79stazp_ptr_menu_buf_lo
$364Ea2 00ldx#$00; X = layout table index; Y = column within current row
$3650a0 00ldy#$00
$3652bd 3f 34j_3652ldatxt_game_paused_table,x; --- Pass 1: snapshot offscreen buffer → banner_data_table --- ; x-ref: $3664, $366C
$3655c9 ffcmp#$ff
$3657f0 0ebeqb_3667; $FF = newline sentinel
$3659c9 fecmp#$fe; $FE = end-of-table → begin pass 2
$365Bf0 12beqb_366F
$365Db1 79lda(zp_ptr_menu_buf_lo),y; read background char from offscreen buffer
$365F9d 3c 35stabanner_data_table,x; save to parallel snapshot buffer at same index
$3662e8inx
$3663c8iny
$36644c 52 36jmpj_3652
$3667a0 00b_3667ldy#$00; newline: reset column to 0, advance to next buffer row (page++) ; x-ref: $3657
$3669e6 7ainczp_ptr_menu_buf_hi
$366Be8inx
$366C4c 52 36jmpj_3652
$366Fa9 6cb_366Flda#$6c; reset ZP pointer back to $6C09 for second pass ; x-ref: $365B
$367185 7astazp_ptr_menu_buf_hi
$3673a9 09lda#$09
$367585 79stazp_ptr_menu_buf_lo
$3677a2 00ldx#$00; restart both indices from zero
$3679a0 00ldy#$00
$367Bbd 3f 34j_367Bldatxt_game_paused_table,x; read menu text char (or $FF/$FE control code) ; x-ref: $368A, $3692
$367Ec9 ffcmp#$ff
$3680f0 0bbeqb_368D; $FF = newline sentinel
$3682c9 fecmp#$fe; $FE = end-of-table → install IRQ vector and return
$3684f0 0fbeqb_3695
$368691 79sta(zp_ptr_menu_buf_lo),y; write menu text char directly to offscreen buffer
$3688e8inx
$3689c8iny
$368A4c 7b 36jmpj_367B
$368Da0 00b_368Dldy#$00; newline: reset column to 0, advance to next buffer row ; x-ref: $3680
$368Fe6 7ainczp_ptr_menu_buf_hi
$3691e8inx
$36924c 7b 36jmpj_367B
$3695a9 9eb_3695lda#<pause_menu_irq; redirect IRQ vector to pause_menu_irq (low byte) ; x-ref: $3684
$369785 90stazp_irq_vector_lo; CINV Vector: Hardware Interrupt
$3699a9 36lda#>pause_menu_irq; redirect IRQ vector to pause_menu_irq (high byte)
$369B85 91stazp_irq_vector_hi
$369D60rts; next interrupt will fire pause_menu_irq
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; IRQ handler for the Pause Menu. Runs every interrupt tick while the menu
; is active. Three distinct code paths depending on state:
;
; 1. KEY-RELEASE LOCKOUT (a343E=1): a move was just made; wait until all
; keys are released before accepting input again (debounce). Once clear,
; reset a343E and fall through to re-highlight + blit.
;
; 2. CONFIRM (DIG1 or DIG2 pressed): blink the selected row twice, redraw
; the banner base layer, then call menu_dispatch_selection to act.
;
; 3. NAVIGATE (no DIG key; LEFT or RIGHT held): compute direction delta
; into a343D (-1/0/+1), advance a343C by 2*a343D (rows are 2 pages
; apart: $72/$74/$76), clamp if result would leave valid range ($70 or
; $78), set a343E=1 to debounce.
;
; All paths converge at b3722: re-apply highlight to selected row, blit
; offscreen buffer to screen, then RTI (registers restored from IRQ stack).
;
; Inputs: a343C (selected row page), a343E (key-release lockout flag),
; hardware keyboard matrix via PIA1 ($E810/$E812)
; Outputs: a343C updated on navigation; screen updated every tick
; Side Effects: Calls menu_dispatch_selection which may change game_state_flag
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$369E20 31 37pause_menu_irqjsrmenu_clear_highlights; strip reverse-video from all rows; leaves ZP $79=$0B for highlight calls ; x-ref: $3695, $3699
$36A1ad 3e 34ldamenu_key_lockout; key-release lockout flag (1=waiting for keys up)
$36A4f0 0fbeqb_36B5; no lockout → proceed to key scanning
$36A620 65 1ajsrwait_any_key; lockout active: poll until all keys released ($FF = no key)
$36A9c9 ffcmp#$ff; key still held → re-highlight + blit, keep waiting
$36ABd0 75bneb_3722
$36ADa9 00lda#$00; all keys released: clear lockout flag
$36AF8d 3e 34stamenu_key_lockout
$36B24c 22 37jmpb_3722
$36B5a5 2cb_36B5ldazp_key_row_dig1; --- path 2: CONFIRM check (DIG1 key) --- ; x-ref: $36A4 ARYTAB Pointer: Start of BASIC Arrays
$36B78d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$36BAad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$36BD25 35andzp_key_col_dig1
$36BFf0 0cbeqb_36CD; DIG1 not pressed → check DIG2
$36C1a5 2dldazp_key_row_dig2
$36C38d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$36C6ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$36C925 36andzp_key_col_dig2; CURLIN Current BASIC Line Number
$36CBd0 0fbneb_36DC
$36CD20 60 37b_36CDjsrmenu_blink_cursor; --- confirm path: blink row, redraw banner, dispatch --- ; x-ref: $36BF
$36D020 86 37jsrmenu_draw_banner
$36D320 86 08jsrblit_screen
$36D620 b0 37jsrmenu_dispatch_selection
$36D94c 25 37jmpj_3725
$36DCa9 00b_36DClda#$00; x-ref: $36CB
$36DE8d 3d 34stamenu_nav_delta
$36E1a5 2aldazp_key_row_left; VARTAB Pointer: Start of BASIC Variables
$36E38d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$36E6ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$36E925 33andzp_key_col_left
$36EBd0 03bneb_36F0
$36EDce 3d 34decmenu_nav_delta
$36F0a5 2bb_36F0ldazp_key_row_right; x-ref: $36EB
$36F28d 10 e8sta$e810; PORT A or DDR A: Data Direction Register A
$36F5ad 12 e8lda$e812; PORT B or DDR B: Data Direction Register B
$36F825 34andzp_key_col_right; MEMSIZ Pointer: Highest Address Used by BASIC
$36FAd0 03bneb_36FF
$36FCee 3d 34incmenu_nav_delta
$36FFad 3d 34b_36FFldamenu_nav_delta; delta = 0: no key held → skip to re-highlight + blit ; x-ref: $36FA
$3702d0 03bneb_3707
$37044c 22 37jmpb_3722
$3707a9 01b_3707lda#$01; set lockout: don't accept another move until key released ; x-ref: $3702
$37098d 3e 34stamenu_key_lockout
$370Cad 3c 34ldamenu_cursor_page; a343C + 2*a343D (rows are 2 pages apart: $72→$74→$76)
$370F18clc
$37106d 3d 34adcmenu_nav_delta
$371318clc
$37146d 3d 34adcmenu_nav_delta
$3717c9 78cmp#$78; $78 = one past END GAME row → clamp (don't move past bottom)
$3719f0 07beqb_3722
$371Bc9 70cmp#$70; $70 = one before CONTINUE row → clamp (don't move past top)
$371Df0 03beqb_3722
$371F8d 3c 34stamenu_cursor_page; in-range: commit new selected row
$372220 4d 37b_3722jsrmenu_highlight_selected; --- epilogue: re-highlight selected row, blit, RTI --- ; x-ref: $36AB, $36B2, $3704, $3719, $371D
$372520 86 08j_3725jsrblit_screen; x-ref: $36D9
$3728ad 12 e8lda$e812; flush offscreen buffer to screen RAM; PORT B or DDR B: Data Direction Register B
$372B68pla; restore Y, X, A saved by IRQ trampoline
$372Ca8tay
$372D68pla
$372Eaatax
$372F68pla
$373040rti
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Strips reverse-video (bit 7) from the 13-char text span on all three menu rows.
;
; The three selectable rows sit on offscreen-buffer pages $72, $74, $76.
; The highlight region within each row starts at byte offset $0B (2 bytes past
; the banner's $09 base — skipping the leading space and 'b' border char) and
; runs for 13 chars, covering the inner menu text area.
;
; Called at the start of every IRQ tick and at the top of each blink cycle to
; give menu_highlight_selected a clean base to re-apply a single lit row.
;
; Inputs: None (page addresses $72/$74/$76 are hardcoded)
; Outputs: 13-byte spans at $720B, $740B, $760B have bit 7 cleared
; Side Effects: ZP $79 = $0B (left set; menu_highlight_selected relies on this)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
menu_clear_highlights
$3731a9 0blda#$0b; low byte of ZP pointer: offset $0B within each page (inner text area) ; x-ref: $369E, $3764
$373385 79stazp_ptr_menu_buf_lo
$3735a2 72ldx#$72; start at page $72 (CONTINUE row)
$373786 7ab_3737stxzp_ptr_menu_buf_hi; set high byte of ZP pointer to current row page ; x-ref: $374A
$3739a0 00ldy#$00
$373Bb1 79b_373Blda(zp_ptr_menu_buf_lo),y; read char, strip bit 7 (clear reverse video), write back ; x-ref: $3744
$373D29 7fand#$7f
$373F91 79sta(zp_ptr_menu_buf_lo),y
$3741c8iny
$3742c0 0dcpy#$0d; 13 chars per row ($00–$0C)
$3744d0 f5bneb_373B
$3746e8inx; step by 2: $72→$74→$76 (skipping spacer rows)
$3747e8inx
$3748e0 78cpx#$78; stop after page $76 (END GAME row)
$374Ad0 ebbneb_3737
$374C60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets reverse-video (bit 7) on the 13-char span of the currently selected row.
;
; Relies on menu_clear_highlights having been called first (ZP $79 = $0B).
; Sets $7A to the selected row's page (from a343C: $72/$74/$76), then ORs
; bit 7 into 13 consecutive chars — matching the exact span that clear strips.
;
; Inputs: a343C (page of selected row: $72/$74/$76); ZP $79 must = $0B
; Outputs: 13-byte span at (a343C)$0B has bit 7 set (reverse video on)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
menu_highlight_selected
$374Dae 3c 34ldxmenu_cursor_page; page of selected row ($72=CONTINUE, $74=RESTART, $76=END GAME) ; x-ref: $3722, $3772
$375086 7astxzp_ptr_menu_buf_hi; set high byte of ZP pointer to selected row's page
$3752a0 00ldy#$00
$3754b1 79b_3754lda(zp_ptr_menu_buf_lo),y; read char, set bit 7 (reverse video on), write back ; x-ref: $375D
$375609 80ora#$80
$375891 79sta(zp_ptr_menu_buf_lo),y
$375Ac8iny
$375Bc0 0dcpy#$0d; 13 chars ($00–$0C), same span as menu_clear_highlights
$375Dd0 f5bneb_3754
$375F60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Animates the selected menu row with 2 blink cycles before returning.
;
; Each cycle:
; 1. Clear all highlights + blit (dark phase, 4 busy-waits)
; 2. Highlight selected row + blit (lit phase, 8 busy-waits)
;
; The 1:2 dark/lit ratio keeps the row visibly highlighted most of the time,
; giving a brief "flash" rather than a full 50/50 blink.
;
; Inputs: a343C (selected row page, passed through to menu_highlight_selected)
; Outputs: Screen updated twice per cycle via blit_screen
; Side Effects: ZP $79 = $0B (left by menu_clear_highlights)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3760a0 02menu_blink_cursorldy#$02; Y = 2 blink cycles to run ; x-ref: $36CD
$376298b_3762tya; save cycle counter (Y clobbered by blit/highlight calls) ; x-ref: $3783
$376348pha
$376420 31 37jsrmenu_clear_highlights; --- dark phase: all rows un-highlighted ---
$376720 86 08jsrblit_screen
$376Aa2 04ldx#$04
$376C20 c8 09b_376Cjsrbusy_wait_pause; x-ref: $3770
$376Fcadex
$3770d0 fabneb_376C
$377220 4d 37jsrmenu_highlight_selected; --- lit phase: selected row reverse-video on ---
$377520 86 08jsrblit_screen
$3778a2 08ldx#$08
$377A20 c8 09b_377Ajsrbusy_wait_pause; x-ref: $377E
$377Dcadex
$377Ed0 fabneb_377A
$378068pla; restore cycle counter
$3781a8tay
$378288dey; repeat for 2 cycles total
$3783d0 ddbneb_3762
$378560rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Redraws the banner base layer into the offscreen buffer each IRQ tick.
;
; Uses two parallel tables indexed by X:
; txt_game_paused_table : layout guide — $FF=advance row, $FE=end, else=copy slot
; banner_data_table : char data — background chars saved by open_pause_menu
;
; Restores the saved pre-menu background into the banner region of the offscreen
; buffer so that menu_highlight_selected can re-apply reverse-video highlights on
; top of a clean base each tick.
;
; Inputs: banner_data_table (pre-populated by open_pause_menu from offscreen buffer)
; Outputs: Offscreen buffer rows starting at $6C09 overwritten with banner chars
; Side Effects: Modifies ZP $79/$7A (page pointer); clobbers A, X, Y
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3786a9 6cmenu_draw_bannerlda#>playfield_row_5_offset_09_banner; point $7A/$79 to offscreen buffer start: row 5, col 8 ($6C09) ; x-ref: $36D0
$378885 7astazp_ptr_menu_buf_hi
$378Aa9 09lda#<playfield_row_5_offset_09_banner
$378C85 79stazp_ptr_menu_buf_lo
$378Ea2 00ldx#$00; X = layout/data table index; Y = column offset within current row
$3790a0 00ldy#$00
$3792bd 3f 34j_3792ldatxt_game_paused_table,x; read layout byte: $FF=newline, $FE=end, else=copy slot ; x-ref: $37A4, $37AC
$3795c9 ffcmp#$ff
$3797f0 0ebeqb_37A7; $FF = newline sentinel
$3799c9 fecmp#$fe; $FE = end-of-table sentinel → done
$379Bf0 12beqr_37AF
$379Dbd 3c 35ldabanner_data_table,x; read background char from parallel data table
$37A091 79sta(zp_ptr_menu_buf_lo),y; write to offscreen buffer at current row+col
$37A2e8inx; advance both table index and column
$37A3c8iny
$37A44c 92 37jmpj_3792
$37A7a0 00b_37A7ldy#$00; newline: reset column to 0, advance to next buffer page (next screen row) ; x-ref: $3797
$37A9e6 7ainczp_ptr_menu_buf_hi
$37ABe8inx
$37AC4c 92 37jmpj_3792
$37AF60r_37AFrts; x-ref: $379B
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Executes the action for the currently highlighted pause-menu row.
;
; a343C holds the offscreen-buffer page address of the selected row:
; $72 → row 1 (CONTINUE) : resume gameplay
; $74 → row 2 (RESTART LEVEL) : trigger death/respawn sequence
; $76 → row 3 (QUIT TO TITLE) : return to title screen
;
; Inputs: a343C (offscreen-buffer page of highlighted row: $72/$74/$76)
; Outputs: game_state_flag (set to 1 on CONTINUE); ZP $90/$91 (gameplay IRQ vector)
; Side Effects: May call handle_player_death or switch_to_title_screen
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
menu_dispatch_selection
$37B0ad 3c 34ldamenu_cursor_page; offscreen-buffer page of highlighted row ($72/$74/$76) ; x-ref: $36D6
$37B3c9 72cmp#$72; row 1 selected? (CONTINUE / START GAME)
$37B5d0 0dbneb_37C4; no → try next option
$37B7a9 01lda#$01; game_state_flag = 1 (gameplay active)
$37B985 67stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa
$37BBa9 4alda#<gameplay_irq; install gameplay IRQ handler low byte into ZP $90
$37BD85 90stazp_irq_vector_lo; CINV Vector: Hardware Interrupt
$37BFa9 06lda#>gameplay_irq; install gameplay IRQ handler high byte into ZP $91
$37C185 91stazp_irq_vector_hi
$37C360rts
$37C4c9 74b_37C4cmp#$74; row 2 selected? (RESTART LEVEL) ; x-ref: $37B5
$37C6d0 07bneb_37CF; no → try next option
$37C820 dd 09jsrtransition_from_full_to_empty; flash the selected row before acting
$37CB20 11 07jsrhandle_player_death; restart: trigger death/respawn sequence at current level
$37CE60rts
$37CFc9 76b_37CFcmp#$76; row 3 selected? (QUIT TO TITLE) ; x-ref: $37C6
$37D1d0 07bner_37DA; no → unrecognized row, do nothing
$37D320 dd 09jsrtransition_from_full_to_empty; flash the selected row before acting
$37D620 bf 30jsrswitch_to_title_screen; return to title screen
$37D960rts
$37DA60r_37DArts; unrecognized row (should not occur) ; x-ref: $37D1
$37DB.fill8501, $00
; Compressed level map data — 10 levels stored in reverse order (level 10 first).
; Loaded by unpack_level_data ($11E8) via pointer (a75:a74), where:
; a75 = $63 - ui_level_num (set at $0709; level 1 -> $62, level 10 -> $59)
; a74 = $10 (fixed low-byte; each level starts at $xx10)
;
; Level start addresses:
; level_data_10 $5910 (a75=$59)
; level_data_09 $5A10 (a75=$5A)
; level_data_08 $5B10 (a75=$5B)
; level_data_07 $5C10 (a75=$5C)
; level_data_06 $5D10 (a75=$5D)
; level_data_05 $5E10 (a75=$5E)
; level_data_04 $5F10 (a75=$5F)
; level_data_03 $6010 (a75=$60)
; level_data_02 $6110 (a75=$61)
; level_data_01 $6210 (a75=$62)
;
; Each level block (256 bytes max) contains:
; 1. MAP ROWS: nibble-packed, 2 tiles per byte, 16 bytes per row (= 32 tiles wide).
; Rows continue until playfield page register a57 reaches $80 (starts at $67).
; Bit 7 of each row's last byte controls page advance: 0 -> +2, 1 -> +1.
; Tile IDs (4-bit nibbles):
; $0 = empty/space
; $1 = ladder
; $2 = border wall (left/right edge)
; $3 = rope (horizontal bar)
; $8 = solid brick (non-diggable)
; $A = diggable brick
; 2. METADATA (8 bytes, read sequentially after map rows):
; draw_offset player start screen column
; draw_page player start screen page (row)
; bonus_walk_page bonus-phase walk start page
; bonus_walk_offset bonus-phase walk start column
; exit_offset level exit column
; exit_page level exit page
; guard_spawn_offset guard respawn column (after freed from hole)
; guard_spawn_page guard respawn page
; 3. ENTITY LIST (variable length, 2 bytes each, $FF terminated):
; Gold: byte < $41 -> column = byte - $20, next byte = page
; Guard: byte >= $41 -> column = byte - $40, next byte = page
; $FF = end of entity list
; 4. PADDING: zero bytes to align next level to $xx10 boundary.
$5910level_data_10.byte$21, $11
$5912.fill12, $00
$591E.byte$11, $12, $20, $00, $11, $00, $11, $11
$5926.byte$11, $02, $11, $11, $11, $20, $00, $11
$592E.byte$00, $02, $a8, $88, $88, $88, $88, $88
$5936.byte$88, $8a, $88, $88, $88, $a8, $88, $88
$593E.byte$88, $8a, $20, $00, $00, $00, $00, $00
$5946.byte$00, $02, $20, $00, $00, $20, $00, $00
$594E.byte$00, $02, $20, $00, $00, $00, $00, $00
$5956.byte$00, $22, $00, $00, $00, $20, $00, $00
$595E.byte$00, $02, $20, $00, $00, $00, $00, $00
$5966.byte$00, $02, $20, $00, $00, $00, $00, $00
$596E.byte$00, $02, $20, $00, $00, $00, $00, $00
$5976.byte$00, $22, $00, $00, $00, $00, $00, $00
$597E.byte$00, $02, $20, $00, $00, $00, $00, $00
$5986.byte$00, $02, $20, $00, $00, $00, $00, $00
$598E.byte$00, $02, $20, $00, $00, $00, $00, $00
$5996.byte$00, $22, $00, $00, $00, $00, $00, $00
$599E.byte$00, $02, $20, $00, $00, $00, $00, $00
$59A6.byte$00, $02, $20, $00, $00, $00, $00, $00
$59AE.byte$00, $02, $20, $00, $00, $00, $00, $00
$59B6.byte$00, $22, $00, $00, $00, $00, $00, $00
$59BE.byte$00, $02, $20, $00, $00, $21
$59C4.fill9, $11
$59CD.byte$20, $00, $02, $0b, $7c, $0f, $6b, $0f
$59D5.byte$67, $13, $67, $31, $7d, $31, $79, $31
$59DD.byte$75, $31, $71, $31, $6d, $2f, $7b, $2f
$59E5.byte$77, $2f, $73, $2f, $6f, $24, $68, $3d
$59ED.byte$68, $26, $6a, $3b, $6a, $5f, $67, $42
$59F5.byte$67, $5c, $69, $45, $69, $ff
$59FB.fill21, $00
$5A10level_data_09.byte$00, $00, $00, $00, $00, $00, $21, $11
$5A18.byte$11, $11, $11, $00, $00, $00, $00, $00
$5A20.byte$11, $11, $11, $11, $11, $11, $20, $00
$5A28.byte$00, $00, $01, $11, $11, $10
$5A2E.fill8, $00
$5A36.byte$20
$5A37.fill9, $00
$5A40.byte$11, $11, $11, $11, $11, $11, $21, $11
$5A48.byte$11, $11, $11, $01, $11, $11, $21, $10
$5A50.byte$11, $11, $11, $11, $11, $11, $20, $00
$5A58.byte$00, $00, $00, $00, $00, $00, $20, $00
$5A60.byte$11, $11, $11, $11, $11, $11, $21, $11
$5A68.byte$11, $11, $11, $14, $44, $00, $21, $11
$5A70.byte$11, $11, $11, $11, $11, $11, $20, $00
$5A78.byte$00, $00, $00, $00, $03, $33, $20, $00
$5A80.byte$11, $00, $00, $00, $00, $11, $20, $00
$5A88.byte$00, $00, $00, $00, $00, $00, $20, $00
$5A90.byte$11, $11, $11, $11, $11, $11, $12
$5A97.fill8, $11
$5A9F.byte$12, $88, $88, $88, $88, $88, $88, $8a
$5AA7.fill8, $88
$5AAF.byte$8a, $00, $00, $00, $00, $00, $00, $02
$5AB7.fill8, $00
$5ABF.byte$02
$5AC0.fill16, $11
$5AD0.byte$0b, $7c, $20, $72, $20, $67, $1d, $67
$5AD8.byte$30, $68, $39, $6a, $25, $6a, $39, $6e
$5AE0.byte$40, $72, $3a, $77, $34, $78, $26, $78
$5AE8.byte$27, $78, $26, $7d, $41, $6d, $59, $71
$5AF0.byte$ff
$5AF1.fill15, $00
$5B00.fill16, $88
$5B10level_data_08.byte$00, $00, $00, $00, $33, $00, $00, $00
$5B18.byte$00, $00, $00, $33, $00, $00, $00, $00
$5B20.byte$21, $11, $11, $12, $00, $21, $00, $00
$5B28.byte$00, $00, $12, $00, $21, $11, $11, $12
$5B30.byte$21, $00, $00, $12, $00, $01, $00, $00
$5B38.byte$00, $00, $10, $00, $21, $00, $00, $12
$5B40.byte$21, $00, $00, $12, $00, $01, $00, $00
$5B48.byte$00, $00, $10, $00, $21, $00, $00, $12
$5B50.byte$21, $11, $11, $12, $00, $01, $00, $00
$5B58.byte$00, $00, $10, $00, $21, $11, $11, $12
$5B60.byte$21, $00, $00, $12, $33, $31, $21, $11
$5B68.byte$11, $12, $13, $33, $21, $00, $00, $12
$5B70.byte$21, $00, $00, $10, $00, $21, $20, $00
$5B78.byte$00, $02, $12, $00, $01, $00, $00, $12
$5B80.byte$21, $00, $00, $10, $00, $21, $20, $00
$5B88.byte$00, $02, $12, $00, $01, $00, $00, $12
$5B90.byte$21, $11, $11, $10, $00, $21, $44, $44
$5B98.byte$44, $44, $12, $00, $01, $11, $11, $12
$5BA0.byte$20, $00, $00, $01, $00, $20, $00, $00
$5BA8.byte$00, $00, $02, $00, $10, $00, $00, $02
$5BB0.byte$20, $00, $00, $00, $10, $20, $00, $00
$5BB8.byte$00, $00, $02, $01, $00, $00, $00, $02
$5BC0.fill17, $11
$5BD1.byte$7c, $0d, $71, $0d, $67, $1d, $67, $31
$5BD9.byte$77, $30, $77, $25, $77, $3c, $77, $25
$5BE1.byte$6f, $3c, $6f, $25, $69, $3c, $69, $5a
$5BE9.byte$68, $5e, $76, $52, $76, $43, $76, $4a
$5BF1.byte$7c, $ff
$5BF3.fill23, $00
$5C0A.byte$03, $33, $33, $33, $33, $33
$5C10level_data_07.byte$11, $11, $12, $10, $00, $01, $21, $11
$5C18.byte$10, $00, $02, $00, $00, $00, $00, $02
$5C20.byte$00, $00, $02, $00, $00, $00, $20, $00
$5C28.byte$00, $11, $12, $11, $11, $11, $11, $12
$5C30.byte$00, $00, $02, $00, $00, $00, $20, $00
$5C38.byte$00, $00, $02, $00, $00, $00, $00, $02
$5C40.byte$11, $11, $12, $11, $11, $00, $20, $00
$5C48.byte$00, $00, $02, $00, $00, $00, $00, $02
$5C50.byte$00, $00, $02, $00, $00, $00, $20, $00
$5C58.byte$00, $00, $02, $00, $00, $00, $00, $02
$5C60.byte$00, $00, $02, $00, $00, $00, $20, $00
$5C68.byte$00, $00, $02, $00, $11, $11, $11, $11
$5C70.byte$11, $21, $11, $11, $11, $11, $11, $12
$5C78.byte$00, $00, $02, $00, $11, $11, $10, $00
$5C80.byte$00, $20, $00, $00, $00, $00, $00, $02
$5C88.byte$33, $33, $32, $00, $10, $00, $10, $00
$5C90.byte$11, $11, $11, $12, $00, $00, $00, $00
$5C98.byte$00, $00, $02, $00, $11, $11, $11, $11
$5CA0.byte$88, $88, $88, $8a, $88, $88, $88, $88
$5CA8.byte$88, $88, $8a, $88, $88, $88, $88, $88
$5CB0.byte$00, $00, $00, $02, $00, $00, $00, $00
$5CB8.byte$00, $00, $02, $00, $00, $00, $00, $00
$5CC0.fill16, $11
$5CD0.byte$0b, $7c, $03, $68, $03, $67, $13, $67
$5CD8.byte$26, $78, $3c, $78, $33, $7d, $33, $77
$5CE0.byte$23, $74, $2a, $74, $2a, $6e, $3d, $6a
$5CE8.byte$5b, $7c, $4f, $67, $54, $69, $43, $6d
$5CF0.byte$ff
$5CF1.fill15, $00
$5D00.fill8, $99
$5D08.byte$88, $89, $99, $99, $99, $99, $99, $99
$5D10level_data_06.byte$10
$5D11.fill9, $00
$5D1A.byte$01, $00, $00, $00, $00, $01, $11, $11
$5D22.byte$12
$5D23.fill12, $11
$5D2F.byte$21, $10, $00, $02, $00, $00, $00, $00
$5D37.byte$00, $00, $11, $11, $10, $11, $10, $00
$5D3F.byte$21, $12, $11, $11, $11, $11, $12, $11
$5D47.byte$11, $11, $10, $01, $10, $11, $10, $00
$5D4F.byte$21, $12, $00, $01, $11, $11, $12
$5D56.fill9, $11
$5D5F.byte$21, $12, $00, $01, $00, $00, $02
$5D66.fill8, $00
$5D6E.byte$01, $21, $12, $00, $01, $11, $21, $11
$5D76.byte$11, $11, $21, $11, $11, $11, $11, $11
$5D7E.byte$21, $21, $12, $00, $01, $11, $20, $00
$5D86.byte$00, $00, $21, $11, $11, $11, $00, $00
$5D8E.byte$21, $21, $12, $11, $11, $00, $20, $00
$5D96.byte$00, $00, $20, $00, $00, $11, $00, $00
$5D9E.byte$21, $21, $12, $11, $11, $21, $11, $11
$5DA6.byte$21, $11, $11, $11, $11, $01, $11, $11
$5DAE.byte$11, $21, $12, $00, $00, $21, $10, $11
$5DB6.byte$20
$5DB7.fill8, $00
$5DBF.byte$21
$5DC0.fill16, $11
$5DD0.byte$0e, $78, $12, $69, $12, $67, $1d, $67
$5DD8.byte$23, $69, $2f, $69, $3b, $69, $25, $77
$5DE0.byte$2a, $7d, $3c, $73, $31, $73, $2b, $73
$5DE8.byte$3a, $79, $39, $79, $37, $7b, $34, $7d
$5DF0.byte$35, $6f, $34, $6f, $3c, $6f, $28, $6d
$5DF8.byte$4a, $6c, $58, $68, $43, $7c, $57, $7c
$5E00.fill16, $00
$5E10level_data_05.byte$11, $11, $10, $00, $00, $00, $00, $00
$5E18.byte$11, $11, $11, $21, $11, $11, $11, $11
$5E20.byte$00, $01, $11, $00, $00, $00, $00, $01
$5E28.byte$11, $00, $00, $20, $00, $00, $00, $00
$5E30.byte$00, $00, $11, $10, $00, $00, $00, $11
$5E38.byte$10, $00, $00, $20, $00, $00, $00, $00
$5E40.byte$11, $12, $11, $11, $00, $00, $01, $11
$5E48.byte$12, $11, $11, $21, $11, $11, $11, $11
$5E50.byte$00, $02, $00, $01, $10, $00, $11, $00
$5E58.byte$02
$5E59.fill8, $00
$5E61.byte$02, $00, $00, $11, $21, $10, $00, $02
$5E69.byte$00, $00, $00, $00, $00, $00, $00, $21
$5E71.byte$11, $12, $00, $00, $20, $00, $00, $12
$5E79.byte$11, $12, $11, $11, $00, $00, $00, $a8
$5E81.byte$88, $8a, $88, $88, $88, $88, $88, $88
$5E89.byte$88, $8a, $88, $88, $88, $88, $88, $20
$5E91.byte$00, $02, $00, $00, $00, $00, $00, $00
$5E99.byte$00, $02, $00, $00, $00, $00, $00, $20
$5EA1.byte$00, $02, $11, $11, $11, $21, $11, $11
$5EA9.byte$11, $12, $11, $11, $11, $12, $11, $20
$5EB1.byte$00, $00, $00, $00, $00, $20, $00, $00
$5EB9.byte$00, $00, $00, $00, $00, $02, $00
$5EC0.fill16, $11
$5ED0.byte$12, $7c, $0b, $72, $0b, $67, $1d, $67
$5ED8.byte$30, $79, $25, $74, $3a, $74, $21, $6e
$5EE0.byte$2e, $6e, $3c, $6e, $32, $68, $52, $78
$5EE8.byte$46, $73, $42, $6d, $5c, $67, $ff
$5EEF.fill17, $00
$5F00.byte$03, $33, $33, $33, $33, $33, $30
$5F07.fill9, $00
$5F10level_data_04.byte$20, $00, $00, $02, $00, $00, $01, $00
$5F18.byte$00, $00, $00, $00, $02, $00, $00, $00
$5F20.byte$20, $00, $02, $00, $02, $00, $01, $11
$5F28.byte$11, $11, $00, $02, $00, $02, $00, $00
$5F30.byte$20, $00, $20, $02, $00, $20, $00, $00
$5F38.byte$00, $00, $00, $20, $02, $00, $20, $00
$5F40.byte$20, $02, $00, $02, $00, $02, $00, $00
$5F48.byte$20, $00, $02, $00, $02, $00, $02, $00
$5F50.byte$20, $02, $00, $00, $00, $02, $00, $02
$5F58.byte$00, $00, $02, $00, $00, $00, $02, $00
$5F60.byte$20, $00, $21, $11, $11, $20, $00, $00
$5F68.byte$20, $00, $00, $21, $11, $11, $20, $00
$5F70.byte$20, $00, $02, $00, $20, $02, $00, $00
$5F78.byte$02, $00, $02, $00, $20, $02, $00, $00
$5F80.byte$20, $00, $00, $00, $00, $02, $00, $00
$5F88.byte$20, $00, $02, $00, $00, $00, $00, $00
$5F90.byte$21, $11, $11, $11, $20, $00, $21, $11
$5F98.byte$11, $11, $20, $00, $21, $11, $11, $11
$5FA0.byte$a8, $88, $88, $88, $a8, $88, $8a, $88
$5FA8.byte$a8, $8a, $88, $88, $a8, $88, $88, $88
$5FB0.byte$20, $00, $00, $00, $20, $00, $00, $00
$5FB8.byte$00, $00, $00, $00, $20, $00, $00, $00
$5FC0.fill16, $11
$5FD0.byte$15, $7c, $20, $78, $20, $67, $12, $67
$5FD8.byte$31, $6a, $24, $6e, $2c, $6e, $31, $6e
$5FE0.byte$36, $6e, $3e, $6e, $27, $72, $29, $72
$5FE8.byte$39, $72, $3b, $72, $27, $78, $2f, $78
$5FF0.byte$33, $78, $3e, $78, $32, $7d, $48, $71
$5FF8.byte$5a, $71, $52, $77, $ff, $00, $00, $00
$6000.byte$33, $33, $33, $33, $33, $33
$6006.fill10, $00
$6010level_data_03.byte$20, $00, $00, $00, $00, $02, $11, $11
$6018.byte$11, $11, $11, $11, $20, $00, $00, $00
$6020.byte$11, $11, $11, $20, $00, $02, $00, $00
$6028.byte$00, $00, $00, $00, $24, $44, $44, $44
$6030.byte$88, $88, $88, $a8, $88, $8a, $88, $88
$6038.byte$88, $88, $88, $88, $a8, $88, $88, $88
$6040.byte$00, $00, $00, $21, $11, $11, $11, $12
$6048.byte$11, $11, $11, $21, $10, $00, $00, $00
$6050.byte$00, $00, $00, $20, $00, $00, $00, $02
$6058.byte$00, $00, $00, $20, $03, $30, $00, $00
$6060.byte$11, $11, $21, $10, $00, $00, $00, $02
$6068.byte$00, $00, $00, $20, $00, $03, $30, $00
$6070.byte$00, $00, $20, $00, $00, $02, $11, $11
$6078.byte$11, $12, $11, $10, $00, $00, $03, $30
$6080.byte$00, $00, $23, $33, $33, $32, $00, $00
$6088.byte$00, $02, $00, $00, $00, $00, $00, $01
$6090.byte$00, $00, $20, $00, $00, $00, $00, $02
$6098.byte$11, $11, $11, $12, $00, $00, $00, $00
$60A0.byte$11, $21, $11, $11, $11, $11, $11, $11
$60A8.byte$00, $00, $00, $01, $11, $11, $12, $11
$60B0.byte$00, $20, $00, $00, $00, $00, $00, $00
$60B8.byte$02, $11, $12, $00, $00, $00, $02, $00
$60C0.fill16, $11
$60D0.byte$0b, $7c, $20, $6a, $20, $67, $1d, $67
$60D8.byte$32, $68, $34, $7b, $3b, $7d, $24, $71
$60E0.byte$33, $6d, $40, $75, $24, $6a, $4a, $6c
$60E8.byte$52, $72, $56, $76, $ff
$60ED.fill35, $00
$6110level_data_02.byte$24, $41, $44, $20, $00, $02, $11, $11
$6118.byte$11, $11, $11, $11, $12, $00, $00, $00
$6120.byte$20, $00, $00, $20, $00, $02, $00, $00
$6128.byte$00, $00, $00, $00, $02, $11, $11, $10
$6130.byte$21, $41, $41, $23, $33, $32, $33, $33
$6138.byte$33, $33, $30, $00, $02, $00, $00, $00
$6140.byte$20, $00, $00, $20, $00, $02, $00, $00
$6148.byte$00, $00, $21, $11, $14, $44, $44, $42
$6150.byte$a8, $88, $88, $a8, $88, $8a, $88, $88
$6158.byte$88, $88, $a8, $88, $88, $88, $88, $8a
$6160.byte$20, $00, $00, $20, $00, $02, $11, $11
$6168.byte$11, $11, $20, $00, $00, $00, $00, $02
$6170.byte$11, $11, $11, $14, $11, $42
$6176.fill9, $00
$617F.byte$02, $11, $11, $11, $00, $00, $02, $00
$6187.byte$00, $00, $00, $00, $00, $02, $11, $12
$618F.byte$11, $10, $00, $01, $00, $00, $02, $00
$6197.byte$00, $33, $33, $33, $33, $32, $00, $02
$619F.byte$00, $11, $11, $11, $12, $11, $11, $44
$61A7.byte$44, $44, $00, $00, $00, $02, $00, $11
$61AF.byte$11, $00, $00, $00, $02
$61B4.fill8, $00
$61BC.byte$02, $00, $00, $00
$61C0.fill16, $11
$61D0.byte$0b, $7c, $20, $6e, $20, $67, $1d, $67
$61D8.byte$24, $68, $23, $6c, $22, $79, $29, $73
$61E0.byte$2f, $71, $40, $79, $34, $68, $3c, $6a
$61E8.byte$58, $6d, $44, $72, $ff
$61ED.fill35, $00
$6210level_data_01.byte$11, $11, $11, $11, $21, $11, $11, $11
$6218.fill12, $00
$6224.byte$23, $33, $33, $33, $33, $33
$622A.fill10, $00
$6234.byte$20, $00, $00, $11, $20, $00, $11, $11
$623C.byte$11, $11, $21, $11, $00, $00, $00, $00
$6244.byte$20, $00, $00, $11, $20, $00, $00, $00
$624C.byte$00, $00, $20, $00, $11, $12, $11, $11
$6254.byte$10, $00, $00, $11, $11, $11, $11, $21
$625C.byte$11, $11, $11, $11, $00, $02
$6262.fill9, $00
$626B.byte$20, $00, $00, $00, $00, $11, $11, $11
$6273.byte$11, $11, $21, $11, $11, $11, $11, $11
$627B.byte$20, $00, $00, $00, $00, $88, $88, $88
$6283.byte$88, $88, $a8, $88, $88, $88, $88, $88
$628B.byte$a8, $88, $88, $88, $88, $00, $00, $00
$6293.byte$00, $00, $23, $33, $33, $33, $33, $33
$629B.byte$20, $00, $00, $00, $00, $00, $00, $02
$62A3.byte$11, $11, $11, $00, $00, $00, $00, $00
$62AB.byte$11, $11, $11, $11, $12, $00, $00, $02
$62B3.fill12, $00
$62BF.byte$02
$62C0.fill16, $11
$62D0.byte$10, $7c, $15, $6c, $15, $67, $1b, $67
$62D8.byte$34, $7d, $29, $79, $3c, $79, $26, $68
$62E0.byte$3a, $6c, $39, $70, $4d, $73, $5a, $6f
$62E8.byte$44, $6f, $ff
$62EB.fill21, $00
; Wipe animation path — ROW offset table.
; Parallel to wipe_path_col_table ($6400); both indexed by the same X register.
; Consumed by transition_from_full_to_empty ($09DD) and
; transition_from_empty_to_full ($0B33) to animate a concentric rectangular wipe
; that progressively erases / restores the playfield.
;
; Each byte is a row offset from the playfield centre row ($73):
; page = $73 + row_off (bottom half)
; page = $73 - row_off (top half)
;
; X starts at $FC (outermost ring) and decrements toward 0 (centre).
; The path encodes every column of each concentric ring so the full
; 25-row × 32-col playfield is covered step by step.
;
; Sentinels:
; $FF = end-of-frame: blit buffer to screen RAM and insert a frame delay
; $FF,$FF pair at end = animation complete
$6300wipe_path_row_table.byte$ff, $ff, $00, $ff, $00, $01, $01, $ff; x-ref: $09E3, $09F0, $0A0E, $0A2C, $0A5E, $0A95, $0B33, $0B40, ...
$6308.byte$00, $01, $02, $02, $02, $ff, $00, $01
$6310.byte$02, $03, $03, $03, $ff, $00, $01, $02
$6318.byte$03, $04, $04, $04, $ff, $00, $01, $02
$6320.byte$03, $03, $04, $04, $05, $05, $05, $05
$6328.byte$ff, $00, $01, $02, $03, $04, $05, $06
$6330.byte$06, $06, $06, $ff, $00, $01, $02, $03
$6338.byte$04, $04, $05, $05, $06, $07, $07, $07
$6340.byte$07, $ff, $00, $01, $02, $03, $04, $05
$6348.byte$06, $06, $07, $07, $08, $08, $08, $08
$6350.byte$08, $ff, $00, $01, $02, $03, $04, $05
$6358.byte$06, $07, $08, $09, $09, $09, $09, $09
$6360.byte$07, $ff, $00, $01, $02, $03, $05, $06
$6368.byte$07, $08, $09, $0a, $0a, $0a, $0a, $ff
$6370.byte$04, $06, $07, $08, $09, $0a, $ff, $00
$6378.byte$01, $02, $03, $05, $07, $08, $09, $0a
$6380.byte$0b, $0b, $0b, $0b, $ff, $04, $06, $0a
$6388.byte$0b, $0b, $0b, $ff, $00, $01, $02, $03
$6390.byte$05, $07, $08, $09, $0a, $0c, $0c, $0c
$6398.byte$0c, $ff, $04, $06, $08, $09, $0a, $0c
$63A0.byte$ff, $00, $01, $02, $03, $05, $06, $07
$63A8.byte$09, $0a, $0b, $0c, $0c, $ff, $04, $05
$63B0.byte$07, $08, $0b, $0c, $ff, $00, $01, $02
$63B8.byte$03, $06, $08, $09, $0a, $0b, $0c, $ff
$63C0.byte$04, $05, $07, $0a, $0b, $ff, $00, $01
$63C8.byte$02, $03, $06, $08, $09, $0c, $ff, $04
$63D0.byte$05, $07, $09, $0a, $0b, $0c, $ff, $00
$63D8.byte$01, $02, $03, $04, $06, $08, $0a, $0b
$63E0.byte$0c, $ff, $05, $06, $07, $09, $0c, $ff
$63E8.byte$07, $08, $09, $0a, $0b, $ff, $08, $0b
$63F0.byte$0c, $ff, $09, $0a, $0c, $ff, $0a, $0b
$63F8.byte$ff, $0b, $0c, $ff, $0c, $ff, $ff, $00
; Wipe animation path — COLUMN offset table.
; Parallel to wipe_path_row_table ($6300); same X index, same sentinels.
; Consumed by transition_from_full_to_empty ($09DD) and
; transition_from_empty_to_full ($0B33).
;
; Each byte is a column offset from the playfield centre column ($11):
; col = $11 + col_off (right half)
; col = $11 - col_off (left half)
;
; For every non-sentinel entry in wipe_path_row_table, the matching byte
; here gives the column offset for the same wipe step, so together the
; two tables define (row_off, col_off) pairs that trace each concentric
; rectangular ring of the playfield from the outside inward.
;
; Sentinels:
; $FF = end-of-frame (mirrors wipe_path_row_table sentinel positions)
; $FF,$FF pair at end = animation complete
$6400wipe_path_col_table.byte$ff, $ff, $00, $ff, $01, $01, $00, $ff; x-ref: $09F8, $0A16, $0A34, $0A66, $0B65, $0B96, $0BC7, $0BF8
$6408.byte$02, $02, $02, $01, $00, $ff, $03, $03
$6410.byte$03, $02, $01, $00, $ff, $04, $04, $04
$6418.byte$03, $02, $01, $00, $ff, $05, $05, $05
$6420.byte$05, $04, $03, $04, $03, $02, $01, $00
$6428.byte$ff, $06, $06, $06, $06, $05, $04, $03
$6430.byte$02, $01, $00, $ff, $07, $07, $07, $07
$6438.byte$06, $07, $05, $06, $05, $03, $02, $01
$6440.byte$00, $ff, $08, $08, $08, $08, $08, $07
$6448.byte$06, $04, $04, $05, $04, $03, $02, $01
$6450.byte$00, $ff, $09, $09, $09, $09, $09, $08
$6458.byte$07, $06, $05, $04, $03, $02, $01, $00
$6460.byte$07, $ff, $0a, $0a, $0a, $0a, $09, $08
$6468.byte$07, $06, $05, $03, $02, $01, $00, $ff
$6470.byte$0a, $09, $08, $07, $06, $04, $ff, $0b
$6478.byte$0b, $0b, $0b, $0a, $09, $08, $07, $05
$6480.byte$03, $02, $01, $00, $ff, $0b, $0a, $06
$6488.byte$04, $05, $06, $ff, $0c, $0c, $0c, $0c
$6490.byte$0b, $0a, $09, $08, $07, $03, $02, $01
$6498.byte$00, $ff, $0c, $0b, $0a, $09, $08, $04
$64A0.byte$ff, $0d, $0d, $0d, $0d, $0c, $0c, $0b
$64A8.byte$0a, $09, $07, $06, $05, $ff, $0d, $0d
$64B0.byte$0c, $0b, $08, $07, $ff, $0e, $0e, $0e
$64B8.byte$0e, $0d, $0c, $0b, $0a, $09, $08, $ff
$64C0.byte$0e, $0e, $0d, $0b, $0a, $ff, $0f, $0f
$64C8.byte$0f, $0f, $0e, $0d, $0c, $09, $ff, $0f
$64D0.byte$0f, $0e, $0d, $0c, $0b, $0a, $ff, $10
$64D8.byte$10, $10, $10, $10, $0f, $0e, $0d, $0c
$64E0.byte$0b, $ff, $10, $10, $0f, $0e, $0c, $ff
$64E8.byte$10, $0f, $0f, $0e, $0d, $ff, $10, $0e
$64F0.byte$0d, $ff, $10, $0f, $0e, $ff, $10, $0f
$64F8.byte$ff, $10, $0f, $ff, $10, $ff, $ff, $00
$6500queue_array_x.fill256, $00; x-ref: $1C80, $1C91, $1C9D, $1CA0, $1D54
$6600queue_array_y.fill256, $00; x-ref: $1C84, $1C8E, $1CA3, $1CA6, $1D51
; Playfield tile buffer — one 256-byte page per screen row.
; Each row occupies a dedicated memory page for multiplication-free Y-coordinate access:
; $6700 = row 0, $6800 = row 1, ..., $7F00 = row 24 (25 rows total)
; Within each page, column indices 0-$2A (42 wide) hold the tile chars.
; Filled by unpack_level_data ($11E8) via page pointer a57 (starting at $67).
; Cleared to $20 (space) by clear_gameplay_grid ($079D) and clear_sub_grid ($07F0).
; Border chars ($65/$63) written at columns 1 and $21 by the blit routines.
$6700playfield_row_buffer.byte$00; x-ref: $07A1, $07F4, $169A, $2E79, $2E93, $3394