| ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; |
| ; 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 |
| $0424 | | basic_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 |
| $0440 | | basic_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 |
| $045F | | basic_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 |
| $0475 | | basic_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 |
| $0481 | | basic_line_60.wordbasic_end; 60 SYS 1536 ; x-ref: $0475 |
| $0483 | | .word$003c |
| $0485 | | .byte$9e, $20, $31, $35, $33, $36, $00 |
| $048C | | basic_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0600 | a9 0c | startlda#$0c; VIA PCR $E84C = $0C: CB2 low output — enables speaker transistor |
| $0602 | 8d 4c e8 | sta$e84c; PCR Peripheral Control Register; set to $0C or $0E at power on |
| $0605 | d8 | cld |
| $0606 | 20 7c 0c | jsrinit_audio; configure VIA Timer 2 for square-wave audio |
| $0609 | a9 10 | lda#$10 |
| $060B | 85 50 | stazp_random_seed; seed LFSR with $10 |
| $060D | 78 | sei; disable IRQ during hardware probe |
| $060E | 20 25 16 | jsrdetect_environment; probe screen width / PET model; patches blit routines if 80-col |
| $0611 | 58 | cli; re-enable IRQ — IRQ handler now active via ZP $90/$91 |
| $0612 | 20 72 07 | main_loopjsrget_random; advance LFSR every iteration — continuous entropy source ; x-ref: $061C, $062C, $062F |
| $0615 | a5 67 | ldazp_game_state_flag; 0=idle, 1=gameplay, 2+=modal dialog; ARGHO Floating Accum. #2: Mantissa |
| $0617 | d0 06 | bneb_061F |
| $0619 | 20 27 09 | jsrblit_sidebar; state 0: keep sidebar visible while title not yet started |
| $061C | 4c 12 06 | jmpmain_loop |
| $061F | c9 01 | b_061Fcmp#$01; x-ref: $0617 |
| $0621 | d0 0c | bneb_062F |
| $0623 | 20 e0 1c | jsrupdate_guard_pathfinding; state 1: background pathfinding recalc (gameplay_irq handles all else) |
| $0626 | 20 ac 29 | jsrupdate_sidebar_status |
| $0629 | 20 27 09 | jsrblit_sidebar |
| $062C | 4c 12 06 | jmpmain_loop |
| $062F | 4c 12 06 | b_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0632 | 20 3c 11 | menu_irqjsrrestore_sprite_bg; erase player icon; restore tile beneath it in offscreen buffer ; x-ref: $1636, $163A |
| $0635 | 20 6c 16 | jsrtitle_screen_state_machine; advance attract mode, blink cursor, handle title input |
| $0638 | 20 66 10 | jsrprocess_player_state; move player icon based on joystick / key input |
| $063B | 20 5f 11 | jsrsave_bg_and_draw_sprite; save tile under icon; draw player icon into offscreen buffer |
| $063E | 20 86 08 | jsrblit_screen; copy offscreen buffer → screen RAM |
| $0641 | ad 12 e8 | lda$e812; acknowledge VIA timer interrupt (clears interrupt flag); PORT B or DDR B: Data Direction Register B |
| $0644 | 68 | pla; restore Y (pushed last by IRQ prologue) |
| $0645 | a8 | tay |
| $0646 | 68 | pla; restore X |
| $0647 | aa | tax |
| $0648 | 68 | pla; restore A |
| $0649 | 40 | rti |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $064A | 20 98 0c | gameplay_irqjsrplay_sound_tick; --- Phase 1: Audio & Timers --- ; x-ref: $0769, $076D, $37BB, $37BF |
| $064D | 20 c2 2a | jsrupdate_game_timer |
| $0650 | 20 59 26 | jsrcheck_level_complete_bonus |
| $0653 | 20 3c 11 | jsrrestore_sprite_bg; --- Phase 2: Background Restore (erase previous frame's sprites) --- |
| $0656 | 20 38 25 | jsrrestore_gold_backgrounds |
| $0659 | 20 47 24 | jsrrestore_guard_backgrounds |
| $065C | 20 02 2c | jsrrestore_hole_backgrounds |
| $065F | 20 7b 25 | jsrerase_floating_score |
| $0662 | 20 09 2b | jsrplay_bonus_audio_tick; --- Phase 3: Game Logic --- |
| $0665 | 20 27 1c | jsrapply_pathfinding_to_guards |
| $0668 | 20 b3 23 | jsrprocess_guard_movement |
| $066B | 20 f7 0d | jsrscan_keyboard; read joystick / keyboard input |
| $066E | 20 66 10 | jsrprocess_player_state; move player, apply gravity |
| $0671 | 20 b3 1e | jsrprocess_dig_animations |
| $0674 | 20 8e 2c | jsrtick_hole_state_machine |
| $0677 | 20 de 25 | jsrcheck_gold_collection |
| $067A | 20 e2 26 | jsrcheck_guard_picks_up_gold |
| $067D | 20 79 27 | jsrcheck_hole_healing_crush |
| $0680 | 20 25 27 | jsrcheck_guard_drops_gold |
| $0683 | 20 9a 25 | jsrtick_floating_score |
| $0686 | 20 21 2c | jsrsave_hole_bg_and_draw; --- Phase 4: Draw (render new frame into back-buffer) --- |
| $0689 | 20 78 24 | jsrdraw_guards |
| $068C | 20 52 25 | jsrdraw_gold |
| $068F | 20 5f 11 | jsrsave_bg_and_draw_sprite |
| $0692 | 20 86 08 | jsrblit_screen; flush back-buffer → Screen RAM |
| $0695 | 20 5f 28 | jsrcheck_player_guard_death; --- Phase 5: Death Checks & State Transitions --- |
| $0698 | 20 e8 28 | jsrcheck_player_wall_crush_death |
| $069B | 20 8c 28 | jsrcheck_player_hole_death |
| $069E | 20 b9 28 | jsrcheck_player_hazard_death |
| $06A1 | 20 c3 26 | jsrcheck_level_complete |
| $06A4 | 20 0d 29 | jsrcheck_pause_key |
| $06A7 | ad 12 e8 | lda$e812; acknowledge VIA interrupt (clear IFR at $E812); PORT B or DDR B: Data Direction Register B |
| $06AA | 68 | pla |
| $06AB | a8 | tay |
| $06AC | 68 | pla |
| $06AD | aa | tax |
| $06AE | 68 | pla |
| $06AF | 40 | rti; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $06B0 | 20 92 0c | init_new_gamejsrstop_audio; x-ref: $322F |
| $06B3 | 20 7c 0c | jsrinit_audio |
| $06B6 | a9 00 | lda#$00 |
| $06B8 | 8d 45 29 | stascore_high_bcd |
| $06BB | 8d 46 29 | stascore_low_bcd |
| $06BE | a9 00 | lda#$00 |
| $06C0 | 85 74 | stazp_ptr_level_data |
| $06C2 | 20 80 07 | jsrclear_physical_screen |
| $06C5 | 4c f6 06 | jmpj_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $06C8 | 20 92 0c | advance_leveljsrstop_audio; audio reset before level transition ; x-ref: $26DE |
| $06CB | 20 7c 0c | jsrinit_audio |
| $06CE | ad df 2a | ldaui_level_num |
| $06D1 | cd e0 2a | cmplevel_select_num; full circuit? (ui_level_num == player's chosen start level) |
| $06D4 | d0 11 | bneb_06E7; no — advance normally |
| $06D6 | ce df 2a | decui_level_num; yes — full circuit complete: undo the wrap |
| $06D9 | ad df 2a | ldaui_level_num |
| $06DC | d0 05 | bneb_06E3 |
| $06DE | a9 0a | lda#10; clamped to 0 — set level 10 instead |
| $06E0 | 8d df 2a | staui_level_num |
| $06E3 | 20 49 2e | b_06E3jsrshow_game_over_screen; all levels completed — show game over / high score screen ; x-ref: $06DC |
| $06E6 | 60 | rts; early exit — no level reload |
| $06E7 | ee df 2a | b_06E7incui_level_num; normal advance: next level ; x-ref: $06D4 |
| $06EA | ad df 2a | ldaui_level_num |
| $06ED | c9 0b | cmp#11; past level 10? |
| $06EF | d0 05 | bnej_06F6 |
| $06F1 | a9 01 | lda#$01; yes — wrap back to level 1 |
| $06F3 | 8d df 2a | staui_level_num |
| $06F6 | a9 00 | j_06F6lda#$00; --- reset per-level state --- ; x-ref: $06C5, $06EF |
| $06F8 | 8d df 24 | stagold_count |
| $06FB | 8d 47 29 | stascore_penalty_high_bcd |
| $06FE | 8d 48 29 | stascore_penalty_low_bcd |
| $0701 | a9 51 | lda#$51 |
| $0703 | 8d 49 29 | stagame_timer_bcd; reset countdown timer to BCD 51 seconds |
| $0706 | a9 63 | lda#$63 |
| $0708 | 38 | sec |
| $0709 | ed df 2a | sbcui_level_num |
| $070C | 85 75 | stazp_difficulty_scalar; $63 - level_num: difficulty scalar (smaller value = higher level = harder) |
| $070E | 20 21 25 | jsrinit_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0711 | 20 92 0c | handle_player_deathjsrstop_audio; --- Phase 1: audio reset --- ; x-ref: $2884, $28B1, $28E0, $2909, $37CB |
| $0714 | 20 7c 0c | jsrinit_audio |
| $0717 | f8 | sed; --- Phase 2: BCD score penalty --- enable BCD arithmetic |
| $0718 | ad 46 29 | ldascore_low_bcd |
| $071B | 38 | sec |
| $071C | ed 48 29 | sbcscore_penalty_low_bcd; score_low -= penalty_low (BCD) |
| $071F | 8d 46 29 | stascore_low_bcd |
| $0722 | ad 45 29 | ldascore_high_bcd |
| $0725 | ed 47 29 | sbcscore_penalty_high_bcd; score_high -= penalty_high with borrow (BCD) |
| $0728 | 8d 45 29 | stascore_high_bcd |
| $072B | d8 | cld; back to binary mode |
| $072C | a9 00 | lda#$00 |
| $072E | 8d 47 29 | stascore_penalty_high_bcd; --- Phase 3: clear transient state --- zero out penalty |
| $0731 | 8d 48 29 | stascore_penalty_low_bcd |
| $0734 | a9 00 | lda#$00 |
| $0736 | 8d df 24 | stagold_count; gold back to 0 (level restart) |
| $0739 | 20 9d 07 | jsrclear_gameplay_grid; --- Phase 4: full level reinit --- |
| $073C | 20 9d 1e | jsrinit_dig_slots |
| $073F | 20 ba 0d | jsrinit_game_variables |
| $0742 | 20 21 25 | jsrinit_gold_arrays |
| $0745 | 20 2b 22 | jsrinit_guard_arrays |
| $0748 | 20 ee 2b | jsrinit_hole_slots |
| $074B | 20 75 25 | jsrinit_floating_score |
| $074E | 20 e9 2a | jsrinit_bonus_sequencer |
| $0751 | 20 e8 11 | jsrunpack_level_data; reload compressed tile data into back-buffer |
| $0754 | 20 b8 1c | jsrinit_ai_pathfinding_grid |
| $0757 | 20 e0 1c | jsrupdate_guard_pathfinding |
| $075A | 20 4b 29 | jsrdraw_ui_text_labels |
| $075D | 20 b8 0a | jsrtransition_from_empty_to_full; animated wipe-in reveals freshly drawn level |
| $0760 | a9 ff | lda#$ff; --- Phase 5: resume gameplay --- |
| $0762 | 8d b1 0d | staplayer_state; mark player alive/ready; |
| $0765 | a9 01 | lda#$01 |
| $0767 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $0769 | a9 4a | lda#<gameplay_irq |
| $076B | 85 90 | stazp_irq_vector_lo; redirect IRQ lo → gameplay_irq; CINV Vector: Hardware Interrupt |
| $076D | a9 06 | lda#>gameplay_irq |
| $076F | 85 91 | stazp_irq_vector_hi; redirect IRQ hi → next tick resumes gameplay |
| $0771 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0772 | a5 50 | get_randomldazp_random_seed; x-ref: $0612, $2735, $32D3, $3321 |
| $0774 | f0 05 | beqb_077B |
| $0776 | 0a | asla |
| $0777 | f0 04 | beqb_077D |
| $0779 | 90 02 | bccb_077D |
| $077B | 49 1d | b_077Beor#$1d; x-ref: $0774 |
| $077D | 85 50 | b_077Dstazp_random_seed; x-ref: $0777, $0779 |
| $077F | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0780 | a9 20 | lda#$20; x-ref: $06C2, $1681 |
| $0782 | a2 00 | ldx#$00 |
| $0784 | 9d 00 80 | b_0784staSCREEN_RAM_R0C0,x; x-ref: $079A |
| $0787 | 9d 00 81 | staSCREEN_RAM_R6C16,x |
| $078A | 9d 00 82 | staSCREEN_RAM_R12C32,x |
| $078D | 9d 00 83 | staSCREEN_RAM_R19C8,x |
| $0790 | 9d 00 84 | staSCREEN_RAM_R25C24,x |
| $0793 | 9d 00 85 | staSCREEN_RAM_R32C0,x |
| $0796 | 9d 00 86 | staSCREEN_RAM_R38C16,x |
| $0799 | ca | dex |
| $079A | d0 e8 | bneb_0784 |
| $079C | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $079D | a9 20 | clear_gameplay_gridlda#$20; space char — fills every tile position ; x-ref: $0739, $1684, $30E0, $30F1 |
| $079F | a2 2a | ldx#42; X = 42..0 inclusive (43 column positions, covers full 40-col display + 3 guard bytes) |
| $07A1 | 9d 00 67 | b_07A1staplayfield_row_buffer,x; row 0 ($6700) ; x-ref: $07ED |
| $07A4 | 9d 00 68 | staplayfield_row_1_page_base,x; Playfield Row 1 page base |
| $07A7 | 9d 00 69 | staplayfield_row_2_page_base,x; Playfield Row 2 page base |
| $07AA | 9d 00 6a | staplayfield_row_3_page_base,x; Playfield Row 3 page base |
| $07AD | 9d 00 6b | staplayfield_row_4_page_base,x; Playfield Row 4 page base |
| $07B0 | 9d 00 6c | staplayfield_row_5_page_base,x; Playfield Row 5 page base |
| $07B3 | 9d 00 6d | staplayfield_row_6_page_base,x; Playfield Row 6 page base |
| $07B6 | 9d 00 6e | staplayfield_row_7_page_base,x; Playfield Row 7 page base |
| $07B9 | 9d 00 6f | staplayfield_row_8_page_base,x; Playfield Row 8 page base |
| $07BC | 9d 00 70 | staplayfield_row_9_page_base,x; Playfield Row 9 page base |
| $07BF | 9d 00 71 | staplayfield_row_10_page_base,x; Playfield Row 10 page base |
| $07C2 | 9d 00 72 | staplayfield_row_11_page_base,x; Playfield Row 11 page base |
| $07C5 | 9d 00 73 | staplayfield_row_12_page_base,x; Playfield Row 12 page base |
| $07C8 | 9d 00 74 | staplayfield_row_13_page_base,x; Playfield Row 13 page base |
| $07CB | 9d 00 75 | staplayfield_row_14_page_base,x; Playfield Row 14 page base |
| $07CE | 9d 00 76 | staplayfield_row_15_page_base,x; Playfield Row 15 page base |
| $07D1 | 9d 00 77 | staplayfield_row_16_page_base,x; Playfield Row 16 page base |
| $07D4 | 9d 00 78 | staplayfield_row_17_page_base,x; Playfield Row 17 page base |
| $07D7 | 9d 00 79 | staplayfield_row_18_page_base,x; Playfield Row 18 page base |
| $07DA | 9d 00 7a | staplayfield_row_19_page_base,x; Playfield Row 19 page base |
| $07DD | 9d 00 7b | staplayfield_row_20_page_base,x; Playfield Row 20 page base |
| $07E0 | 9d 00 7c | staplayfield_row_21_page_base,x; Playfield Row 21 page base |
| $07E3 | 9d 00 7d | staplayfield_row_22_page_base,x; Playfield Row 22 page base |
| $07E6 | 9d 00 7e | staplayfield_row_23_page_base,x; Playfield Row 23 page base |
| $07E9 | 9d 00 7f | staplayfield_row_24_page_base,x; Playfield Row 24 page base |
| $07EC | ca | dex; next column position |
| $07ED | 10 b2 | bplb_07A1; repeat until X wraps below 0 (covers cols 42..0) |
| $07EF | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $07F0 | a9 20 | clear_sub_gridlda#$20; space char — fills every tile position ; x-ref: $2E51 |
| $07F2 | a2 20 | ldx#32; X = 32..0 inclusive (33 column positions) |
| $07F4 | 9d 00 67 | b_07F4staplayfield_row_buffer,x; row 0 ($6700) ; x-ref: $0840 |
| $07F7 | 9d 00 68 | staplayfield_row_1_page_base,x; Playfield Row 1 page base |
| $07FA | 9d 00 69 | staplayfield_row_2_page_base,x; Playfield Row 2 page base |
| $07FD | 9d 00 6a | staplayfield_row_3_page_base,x; Playfield Row 3 page base |
| $0800 | 9d 00 6b | staplayfield_row_4_page_base,x; Playfield Row 4 page base |
| $0803 | 9d 00 6c | staplayfield_row_5_page_base,x; Playfield Row 5 page base |
| $0806 | 9d 00 6d | staplayfield_row_6_page_base,x; Playfield Row 6 page base |
| $0809 | 9d 00 6e | staplayfield_row_7_page_base,x; Playfield Row 7 page base |
| $080C | 9d 00 6f | staplayfield_row_8_page_base,x; Playfield Row 8 page base |
| $080F | 9d 00 70 | staplayfield_row_9_page_base,x; Playfield Row 9 page base |
| $0812 | 9d 00 71 | staplayfield_row_10_page_base,x; Playfield Row 10 page base |
| $0815 | 9d 00 72 | staplayfield_row_11_page_base,x; Playfield Row 11 page base |
| $0818 | 9d 00 73 | staplayfield_row_12_page_base,x; Playfield Row 12 page base |
| $081B | 9d 00 74 | staplayfield_row_13_page_base,x; Playfield Row 13 page base |
| $081E | 9d 00 75 | staplayfield_row_14_page_base,x; Playfield Row 14 page base |
| $0821 | 9d 00 76 | staplayfield_row_15_page_base,x; Playfield Row 15 page base |
| $0824 | 9d 00 77 | staplayfield_row_16_page_base,x; Playfield Row 16 page base |
| $0827 | 9d 00 78 | staplayfield_row_17_page_base,x; Playfield Row 17 page base |
| $082A | 9d 00 79 | staplayfield_row_18_page_base,x; Playfield Row 18 page base |
| $082D | 9d 00 7a | staplayfield_row_19_page_base,x; Playfield Row 19 page base |
| $0830 | 9d 00 7b | staplayfield_row_20_page_base,x; Playfield Row 20 page base |
| $0833 | 9d 00 7c | staplayfield_row_21_page_base,x; Playfield Row 21 page base |
| $0836 | 9d 00 7d | staplayfield_row_22_page_base,x; Playfield Row 22 page base |
| $0839 | 9d 00 7e | staplayfield_row_23_page_base,x; Playfield Row 23 page base |
| $083C | 9d 00 7f | staplayfield_row_24_page_base,x; Playfield Row 24 page base |
| $083F | ca | dex; next column position |
| $0840 | 10 b2 | bplb_07F4; repeat until X wraps below 0 (covers cols 32..0) |
| $0842 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0843 | a5 51 | ldazp_screen_width; x-ref: $1668 |
| $0845 | c9 50 | cmp#80 |
| $0847 | f0 01 | beqb_084A |
| $0849 | 60 | rts |
| $084A | a0 0c | b_084Aldy#$0c; x-ref: $0847 |
| $084C | a9 00 | lda#$00 |
| $084E | 8d 8c 08 | stasmc_blit_screen_r0_lo |
| $0851 | 8d 2d 09 | stasmc_blit_sidebar_r0_lo |
| $0854 | a2 80 | ldx#$80 |
| $0856 | 8e 8d 08 | stxsmc_blit_screen_r0_hi |
| $0859 | 8e 2e 09 | stxsmc_blit_sidebar_r0_hi |
| $085C | 18 | b_085Cclc; x-ref: $0879 |
| $085D | 69 50 | adc#$50 |
| $085F | 99 86 08 | stablit_screen,y |
| $0862 | 99 27 09 | stablit_sidebar,y |
| $0865 | 90 01 | bccb_0868 |
| $0867 | e8 | inx |
| $0868 | c8 | b_0868iny; x-ref: $0865 |
| $0869 | 48 | pha |
| $086A | 8a | txa |
| $086B | 99 86 08 | stablit_screen,y |
| $086E | 99 27 09 | stablit_sidebar,y |
| $0871 | 68 | pla |
| $0872 | c8 | iny |
| $0873 | c8 | iny |
| $0874 | c8 | iny |
| $0875 | c8 | iny |
| $0876 | c8 | iny |
| $0877 | c0 9c | cpy#$9c |
| $0879 | d0 e1 | bneb_085C |
| $087B | a9 83 | lda#$83 |
| $087D | 8d b6 0a | stasmc_screen_addr_hi |
| $0880 | a9 bf | lda#$bf |
| $0882 | 8d b7 0a | stasmc_screen_addr_lo |
| $0885 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0886 | a2 00 | blit_screenldx#$00; X = column index (0–$1F), copied across all 25 rows each pass ; x-ref: $063E, $0692, $085F, $086B, $0AA4, $3002, $3030, $31F8, ... |
| $0888 | bd 01 67 | j_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 |
| $088B | 9d 00 80 | staSCREEN_RAM_R0C0,x; Screen RAM row 0 ($8000 + X) |
| $088E | bd 01 68 | ldaplayfield_row_1,x; Playfield Row 1 buffer |
| $0891 | 9d 28 80 | staSCREEN_RAM_R1C0,x |
| $0894 | bd 01 69 | ldaplayfield_row_2,x; Playfield Row 2 buffer |
| $0897 | 9d 50 80 | staSCREEN_RAM_R2C0,x |
| $089A | bd 01 6a | ldaplayfield_row_3,x; Playfield Row 3 buffer |
| $089D | 9d 78 80 | staSCREEN_RAM_R3C0,x |
| $08A0 | bd 01 6b | ldaplayfield_row_4,x; Playfield Row 4 buffer |
| $08A3 | 9d a0 80 | staSCREEN_RAM_R4C0,x |
| $08A6 | bd 01 6c | ldaplayfield_row_5,x; Playfield Row 5 buffer |
| $08A9 | 9d c8 80 | staSCREEN_RAM_R5C0,x |
| $08AC | bd 01 6d | ldaplayfield_row_6,x; Playfield Row 6 buffer |
| $08AF | 9d f0 80 | staSCREEN_RAM_R6C0,x |
| $08B2 | bd 01 6e | ldaplayfield_row_7,x; Playfield Row 7 buffer |
| $08B5 | 9d 18 81 | staSCREEN_RAM_R7C0,x |
| $08B8 | bd 01 6f | ldaplayfield_row_8,x; Playfield Row 8 buffer |
| $08BB | 9d 40 81 | staSCREEN_RAM_R8C0,x |
| $08BE | bd 01 70 | ldaplayfield_row_9,x; Playfield Row 9 buffer |
| $08C1 | 9d 68 81 | staSCREEN_RAM_R9C0,x |
| $08C4 | bd 01 71 | ldaplayfield_row_10,x; Playfield Row 10 buffer |
| $08C7 | 9d 90 81 | staSCREEN_RAM_R10C0,x |
| $08CA | bd 01 72 | ldaplayfield_row_11,x; Playfield Row 11 buffer |
| $08CD | 9d b8 81 | staSCREEN_RAM_R11C0,x |
| $08D0 | bd 01 73 | ldaplayfield_row_12,x; Playfield Row 12 buffer |
| $08D3 | 9d e0 81 | staSCREEN_RAM_R12C0,x |
| $08D6 | bd 01 74 | ldaplayfield_row_13,x; Playfield Row 13 buffer |
| $08D9 | 9d 08 82 | staSCREEN_RAM_R13C0,x |
| $08DC | bd 01 75 | ldaplayfield_row_14,x; Playfield Row 14 buffer |
| $08DF | 9d 30 82 | staSCREEN_RAM_R14C0,x |
| $08E2 | bd 01 76 | ldaplayfield_row_15,x; Playfield Row 15 buffer |
| $08E5 | 9d 58 82 | staSCREEN_RAM_R15C0,x |
| $08E8 | bd 01 77 | ldaplayfield_row_16,x; Playfield Row 16 buffer |
| $08EB | 9d 80 82 | staSCREEN_RAM_R16C0,x |
| $08EE | bd 01 78 | ldaplayfield_row_17,x; Playfield Row 17 buffer |
| $08F1 | 9d a8 82 | staSCREEN_RAM_R17C0,x |
| $08F4 | bd 01 79 | ldaplayfield_row_18,x; Playfield Row 18 buffer |
| $08F7 | 9d d0 82 | staSCREEN_RAM_R18C0,x |
| $08FA | bd 01 7a | ldaplayfield_row_19,x; Playfield Row 19 buffer |
| $08FD | 9d f8 82 | staSCREEN_RAM_R19C0,x |
| $0900 | bd 01 7b | ldaplayfield_row_20,x; Playfield Row 20 buffer |
| $0903 | 9d 20 83 | staSCREEN_RAM_R20C0,x |
| $0906 | bd 01 7c | ldaplayfield_row_21,x; Playfield Row 21 buffer |
| $0909 | 9d 48 83 | staSCREEN_RAM_R21C0,x |
| $090C | bd 01 7d | ldaplayfield_row_22,x; Playfield Row 22 buffer |
| $090F | 9d 70 83 | staSCREEN_RAM_R22C0,x |
| $0912 | bd 01 7e | ldaplayfield_row_23,x; Playfield Row 23 buffer |
| $0915 | 9d 98 83 | staSCREEN_RAM_R23C0,x |
| $0918 | bd 01 7f | ldaplayfield_row_24,x; Playfield Row 24 buffer |
| $091B | 9d c0 83 | staSCREEN_RAM_R24C0,x |
| $091E | e8 | inx; advance to next column |
| $091F | e0 20 | cpx#32; all 32 play-area columns done? |
| $0921 | f0 03 | beqr_0926; yes — return |
| $0923 | 4c 88 08 | jmpj_0888; no — next column |
| $0926 | 60 | r_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0927 | a2 20 | blit_sidebarldx#$20; x-ref: $0619, $0629, $0862, $086E, $26D0, $31FB, $320C, $3241 |
| $0929 | bd 01 67 | j_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 |
| $092C | 9d 00 80 | staSCREEN_RAM_R0C0,x |
| $092F | bd 01 68 | ldaplayfield_row_1,x; Playfield Row 1 buffer |
| $0932 | 9d 28 80 | staSCREEN_RAM_R1C0,x |
| $0935 | bd 01 69 | ldaplayfield_row_2,x; Playfield Row 2 buffer |
| $0938 | 9d 50 80 | staSCREEN_RAM_R2C0,x |
| $093B | bd 01 6a | ldaplayfield_row_3,x; Playfield Row 3 buffer |
| $093E | 9d 78 80 | staSCREEN_RAM_R3C0,x |
| $0941 | bd 01 6b | ldaplayfield_row_4,x; Playfield Row 4 buffer |
| $0944 | 9d a0 80 | staSCREEN_RAM_R4C0,x |
| $0947 | bd 01 6c | ldaplayfield_row_5,x; Playfield Row 5 buffer |
| $094A | 9d c8 80 | staSCREEN_RAM_R5C0,x |
| $094D | bd 01 6d | ldaplayfield_row_6,x; Playfield Row 6 buffer |
| $0950 | 9d f0 80 | staSCREEN_RAM_R6C0,x |
| $0953 | bd 01 6e | ldaplayfield_row_7,x; Playfield Row 7 buffer |
| $0956 | 9d 18 81 | staSCREEN_RAM_R7C0,x |
| $0959 | bd 01 6f | ldaplayfield_row_8,x; Playfield Row 8 buffer |
| $095C | 9d 40 81 | staSCREEN_RAM_R8C0,x |
| $095F | bd 01 70 | ldaplayfield_row_9,x; Playfield Row 9 buffer |
| $0962 | 9d 68 81 | staSCREEN_RAM_R9C0,x |
| $0965 | bd 01 71 | ldaplayfield_row_10,x; Playfield Row 10 buffer |
| $0968 | 9d 90 81 | staSCREEN_RAM_R10C0,x |
| $096B | bd 01 72 | ldaplayfield_row_11,x; Playfield Row 11 buffer |
| $096E | 9d b8 81 | staSCREEN_RAM_R11C0,x |
| $0971 | bd 01 73 | ldaplayfield_row_12,x; Playfield Row 12 buffer |
| $0974 | 9d e0 81 | staSCREEN_RAM_R12C0,x |
| $0977 | bd 01 74 | ldaplayfield_row_13,x; Playfield Row 13 buffer |
| $097A | 9d 08 82 | staSCREEN_RAM_R13C0,x |
| $097D | bd 01 75 | ldaplayfield_row_14,x; Playfield Row 14 buffer |
| $0980 | 9d 30 82 | staSCREEN_RAM_R14C0,x |
| $0983 | bd 01 76 | ldaplayfield_row_15,x; Playfield Row 15 buffer |
| $0986 | 9d 58 82 | staSCREEN_RAM_R15C0,x |
| $0989 | bd 01 77 | ldaplayfield_row_16,x; Playfield Row 16 buffer |
| $098C | 9d 80 82 | staSCREEN_RAM_R16C0,x |
| $098F | bd 01 78 | ldaplayfield_row_17,x; Playfield Row 17 buffer |
| $0992 | 9d a8 82 | staSCREEN_RAM_R17C0,x |
| $0995 | bd 01 79 | ldaplayfield_row_18,x; Playfield Row 18 buffer |
| $0998 | 9d d0 82 | staSCREEN_RAM_R18C0,x |
| $099B | bd 01 7a | ldaplayfield_row_19,x; Playfield Row 19 buffer |
| $099E | 9d f8 82 | staSCREEN_RAM_R19C0,x |
| $09A1 | bd 01 7b | ldaplayfield_row_20,x; Playfield Row 20 buffer |
| $09A4 | 9d 20 83 | staSCREEN_RAM_R20C0,x |
| $09A7 | bd 01 7c | ldaplayfield_row_21,x; Playfield Row 21 buffer |
| $09AA | 9d 48 83 | staSCREEN_RAM_R21C0,x |
| $09AD | bd 01 7d | ldaplayfield_row_22,x; Playfield Row 22 buffer |
| $09B0 | 9d 70 83 | staSCREEN_RAM_R22C0,x |
| $09B3 | bd 01 7e | ldaplayfield_row_23,x; Playfield Row 23 buffer |
| $09B6 | 9d 98 83 | staSCREEN_RAM_R23C0,x |
| $09B9 | bd 01 7f | ldaplayfield_row_24,x; Playfield Row 24 buffer |
| $09BC | 9d c0 83 | staSCREEN_RAM_R24C0,x |
| $09BF | e8 | inx |
| $09C0 | e0 28 | cpx#$28 |
| $09C2 | f0 03 | beqr_09C7 |
| $09C4 | 4c 29 09 | jmpj_0929 |
| $09C7 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $09C8 | 48 | busy_wait_pausepha; save A ; x-ref: $0C05, $0D03, $0D15, $26D5, $287B, $28A8, $28D7, $2900, ... |
| $09C9 | 8a | txa; save X |
| $09CA | 48 | pha |
| $09CB | 98 | tya; save Y |
| $09CC | 48 | pha |
| $09CD | a0 10 | ldy#$10; outer loop: 16 passes |
| $09CF | a2 ff | b_09CFldx#$ff; inner loop: 255 iterations each pass ; x-ref: $09D5 |
| $09D1 | ca | b_09D1dex; burn cycles ; x-ref: $09D2 |
| $09D2 | d0 fd | bneb_09D1 |
| $09D4 | 88 | dey; outer step; loop until Y=0 |
| $09D5 | d0 f8 | bneb_09CF |
| $09D7 | 68 | pla; restore Y |
| $09D8 | a8 | tay |
| $09D9 | 68 | pla; restore X |
| $09DA | aa | tax |
| $09DB | 68 | pla; restore A |
| $09DC | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $09DD | a9 00 | lda#$00; ZP pointer low byte = 0 (constant; a59 carries the row page) ; x-ref: $26DB, $2881, $28AE, $28DD, $2906, $37C8, $37D3 |
| $09DF | 85 58 | stazp_ptr_wipe_buf_lo |
| $09E1 | a2 fc | ldx#$fc; start at outermost ring ($FC); contract inward via dex |
| $09E3 | bd 00 63 | j_09E3ldawipe_path_row_table,x; read row offset for this wipe step ; x-ref: $0A8E, $0A9C |
| $09E6 | c9 ff | cmp#$ff; end-of-frame sentinel? |
| $09E8 | d0 03 | bneb_09ED; no: erase/mark this ring step |
| $09EA | 4c 91 0a | jmpj_0A91; yes: blit frame to screen and delay |
| $09ED | a9 73 | b_09EDlda#>playfield_row_12_page_base; --- bottom-right: page=$73+row_off (toward bottom row $7F), col=$11+col_off --- ; x-ref: $09E8 |
| $09EF | 18 | clc |
| $09F0 | 7d 00 63 | adcwipe_path_row_table,x; bottom-half row page = $73 + row_offset |
| $09F3 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $09F5 | a9 11 | lda#$11 |
| $09F7 | 18 | clc |
| $09F8 | 7d 00 64 | adcwipe_path_col_table,x; right col = $11 + col_offset |
| $09FB | a8 | tay |
| $09FC | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $09FE | c9 7f | cmp#$7f; reached bottom playfield edge ($7F)? |
| $0A00 | d0 05 | bneb_0A07; no: erase level tile |
| $0A02 | a9 64 | lda#$64; yes: bottom-right sweep boundary — bottom-edge char ($64) |
| $0A04 | 4c 09 0a | jmpj_0A09 |
| $0A07 | a9 20 | b_0A07lda#$20; erase level tile at this position ; x-ref: $0A00 |
| $0A09 | 91 58 | j_0A09sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A04 |
| $0A0B | a9 73 | lda#$73; --- top-right: page=$73-row_off (toward top row $67), col=$11+col_off --- |
| $0A0D | 38 | sec |
| $0A0E | fd 00 63 | sbcwipe_path_row_table,x; top-half row page = $73 - row_offset |
| $0A11 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A13 | a9 11 | lda#$11 |
| $0A15 | 18 | clc |
| $0A16 | 7d 00 64 | adcwipe_path_col_table,x; right col = $11 + col_offset |
| $0A19 | a8 | tay |
| $0A1A | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A1C | c9 67 | cmp#$67; reached top playfield edge ($67)? |
| $0A1E | d0 05 | bneb_0A25; no: erase level tile |
| $0A20 | a9 63 | lda#$63; yes: top-right sweep boundary — top-edge char ($63) |
| $0A22 | 4c 27 0a | jmpj_0A27 |
| $0A25 | a9 20 | b_0A25lda#$20; erase level tile at this position ; x-ref: $0A1E |
| $0A27 | 91 58 | j_0A27sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A22 |
| $0A29 | a9 73 | lda#$73; --- bottom-left: page=$73+row_off (toward bottom row $7F), col=$11-col_off --- |
| $0A2B | 18 | clc |
| $0A2C | 7d 00 63 | adcwipe_path_row_table,x; bottom-half row page = $73 + row_offset |
| $0A2F | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A31 | a9 11 | lda#$11 |
| $0A33 | 38 | sec |
| $0A34 | fd 00 64 | sbcwipe_path_col_table,x; left col = $11 - col_offset |
| $0A37 | a8 | tay |
| $0A38 | c0 01 | cpy#$01; reached left playfield edge (col 1)? |
| $0A3A | d0 10 | bneb_0A4C; no: check row boundary only |
| $0A3C | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A3E | c9 7f | cmp#$7f; col=1: also at bottom edge ($7F)? |
| $0A40 | d0 05 | bneb_0A47 |
| $0A42 | a9 4c | lda#$4c; col=1 + row=$7F: bottom-left corner char ($4C) |
| $0A44 | 4c 59 0a | jmpj_0A59 |
| $0A47 | a9 65 | b_0A47lda#$65; col=1 only: left-edge char ($65) ; x-ref: $0A40 |
| $0A49 | 4c 59 0a | jmpj_0A59 |
| $0A4C | a5 59 | b_0A4Cldazp_ptr_wipe_buf_hi; col≠1: check bottom edge ; x-ref: $0A3A TEMPF2 Temporary storage for FLPT value. |
| $0A4E | c9 7f | cmp#$7f |
| $0A50 | d0 05 | bneb_0A57 |
| $0A52 | a9 64 | lda#$64; col≠1 + row=$7F: bottom-edge char ($64) |
| $0A54 | 4c 59 0a | jmpj_0A59 |
| $0A57 | a9 20 | b_0A57lda#$20; inside playfield: erase level tile ; x-ref: $0A50 |
| $0A59 | 91 58 | j_0A59sta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A44, $0A49, $0A54 |
| $0A5B | a9 73 | lda#$73; --- top-left: page=$73-row_off (toward top row $67), col=$11-col_off --- |
| $0A5D | 38 | sec |
| $0A5E | fd 00 63 | sbcwipe_path_row_table,x; top-half row page = $73 - row_offset |
| $0A61 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A63 | a9 11 | lda#$11 |
| $0A65 | 38 | sec |
| $0A66 | fd 00 64 | sbcwipe_path_col_table,x; left col = $11 - col_offset |
| $0A69 | a8 | tay |
| $0A6A | c0 01 | cpy#$01; reached left playfield edge (col 1)? |
| $0A6C | d0 10 | bneb_0A7E; no: check row boundary only |
| $0A6E | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0A70 | c9 67 | cmp#$67; col=1: also at top edge ($67)? |
| $0A72 | d0 05 | bneb_0A79 |
| $0A74 | a9 4f | lda#$4f; col=1 + row=$67: top-left corner char ($4F) |
| $0A76 | 4c 8b 0a | jmpj_0A8B |
| $0A79 | a9 65 | b_0A79lda#$65; col=1 only: left-edge char ($65) ; x-ref: $0A72 |
| $0A7B | 4c 8b 0a | jmpj_0A8B |
| $0A7E | a5 59 | b_0A7Eldazp_ptr_wipe_buf_hi; col≠1: check top edge ; x-ref: $0A6C TEMPF2 Temporary storage for FLPT value. |
| $0A80 | c9 67 | cmp#$67 |
| $0A82 | d0 05 | bneb_0A89 |
| $0A84 | a9 63 | lda#$63; col≠1 + row=$67: top-edge char ($63) |
| $0A86 | 4c 8b 0a | jmpj_0A8B |
| $0A89 | a9 20 | b_0A89lda#$20; inside playfield: erase level tile ; x-ref: $0A82 |
| $0A8B | 91 58 | j_0A8Bsta(zp_ptr_wipe_buf_lo),y; write to offscreen buffer (erase tile or mark boundary) ; x-ref: $0A76, $0A7B, $0A86 |
| $0A8D | ca | dex; contract: next (inner) wipe step |
| $0A8E | 4c e3 09 | jmpj_09E3 |
| $0A91 | 20 a0 0a | j_0A91jsrblit_screen_and_pause; $FF hit: blit frame to screen and delay one frame ; x-ref: $09EA |
| $0A94 | ca | dex; skip past the $FF sentinel |
| $0A95 | bd 00 63 | ldawipe_path_row_table,x; read next entry |
| $0A98 | c9 ff | cmp#$ff; second $FF = all rings erased, animation complete |
| $0A9A | f0 03 | beqr_0A9F; yes: done |
| $0A9C | 4c e3 09 | jmpj_09E3; no: continue next frame |
| $0A9F | 60 | r_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 |
| $0AA0 | 8a | txa; x-ref: $0A91 |
| $0AA1 | 48 | pha |
| $0AA2 | 98 | tya |
| $0AA3 | 48 | pha |
| $0AA4 | 20 86 08 | jsrblit_screen |
| $0AA7 | a0 10 | ldy#$10 |
| $0AA9 | a2 ff | b_0AA9ldx#$ff; x-ref: $0AAF |
| $0AAB | ca | b_0AABdex; x-ref: $0AAC |
| $0AAC | d0 fd | bneb_0AAB |
| $0AAE | 88 | dey |
| $0AAF | d0 f8 | bneb_0AA9 |
| $0AB1 | 68 | pla |
| $0AB2 | a8 | tay |
| $0AB3 | 68 | pla |
| $0AB4 | aa | tax |
| $0AB5 | 60 | rts |
| ; 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. |
| $0AB6 | | smc_screen_addr_hi.byte>SCREEN_RAM_R11C39; x-ref: $087D, $0B45, $0B76, $0BA7, $0BD8 |
| $0AB7 | | smc_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 |
| $0AB8 | a2 00 | ldx#$00; Phase 1: draw static border frame (25 rows × 33 cols) to screen RAM ; x-ref: $075D, $2F9D |
| $0ABA | a0 00 | ldy#$00 |
| $0ABC | a9 00 | lda#$00 |
| $0ABE | 8d 11 0b | stasmc_draw_border_addr_lo; screen RAM pointer lo = 0 |
| $0AC1 | a9 80 | lda#$80; screen RAM pointer hi = $80 → base $8000 |
| $0AC3 | 8d 12 0b | stasmc_draw_border_addr_hi |
| $0AC6 | e0 00 | b_0AC6cpx#$00; first row (row 0)? ; x-ref: $0B16, $0B2B |
| $0AC8 | d0 17 | bneb_0AE1 |
| $0ACA | c0 00 | cpy#$00; left col (col 0)? |
| $0ACC | d0 05 | bneb_0AD3 |
| $0ACE | a9 4f | lda#$4f; top-left corner ($4F) |
| $0AD0 | 4c 10 0b | jmpj_0B10 |
| $0AD3 | c0 20 | b_0AD3cpy#$20; right col (col $20)? ; x-ref: $0ACC |
| $0AD5 | d0 05 | bneb_0ADC |
| $0AD7 | a9 65 | lda#$65; top-right corner ($65) |
| $0AD9 | 4c 10 0b | jmpj_0B10 |
| $0ADC | a9 63 | b_0ADClda#$63; top-edge fill ($63) ; x-ref: $0AD5 |
| $0ADE | 4c 10 0b | jmpj_0B10 |
| $0AE1 | e0 18 | b_0AE1cpx#$18; last row (row $18 = 24)? ; x-ref: $0AC8 |
| $0AE3 | d0 17 | bneb_0AFC |
| $0AE5 | c0 00 | cpy#$00; left col? |
| $0AE7 | d0 05 | bneb_0AEE |
| $0AE9 | a9 4c | lda#$4c; bottom-left corner ($4C) |
| $0AEB | 4c 10 0b | jmpj_0B10 |
| $0AEE | c0 20 | b_0AEEcpy#$20; right col? ; x-ref: $0AE7 |
| $0AF0 | d0 05 | bneb_0AF7 |
| $0AF2 | a9 65 | lda#$65; bottom-right corner ($65) |
| $0AF4 | 4c 10 0b | jmpj_0B10 |
| $0AF7 | a9 64 | b_0AF7lda#$64; bottom-edge fill ($64) ; x-ref: $0AF0 |
| $0AF9 | 4c 10 0b | jmpj_0B10 |
| $0AFC | c0 00 | b_0AFCcpy#$00; middle rows: left col? ; x-ref: $0AE3 |
| $0AFE | d0 05 | bneb_0B05 |
| $0B00 | a9 65 | lda#$65; left-edge fill ($65) |
| $0B02 | 4c 10 0b | jmpj_0B10 |
| $0B05 | c0 20 | b_0B05cpy#$20; right col? ; x-ref: $0AFE |
| $0B07 | d0 05 | bneb_0B0E |
| $0B09 | a9 65 | lda#$65; right-edge fill ($65) |
| $0B0B | 4c 10 0b | jmpj_0B10 |
| $0B0E | a9 20 | b_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 |
| $0B10 | 99 00 80 | j_0B10staSCREEN_RAM_R0C0,y; write char directly to screen RAM ; x-ref: $0AD0, $0AD9, $0ADE, $0AEB, $0AF4, $0AF9, $0B02, $0B0B |
| $0B13 | c8 | iny |
| $0B14 | c0 21 | cpy#$21; all 33 cols done? |
| $0B16 | d0 ae | bneb_0AC6 |
| $0B18 | a0 00 | ldy#$00 |
| $0B1A | ad 11 0b | ldasmc_draw_border_addr_lo; advance screen pointer by screen_width (carry-propagated) |
| $0B1D | 18 | clc |
| $0B1E | 65 51 | adczp_screen_width |
| $0B20 | 8d 11 0b | stasmc_draw_border_addr_lo |
| $0B23 | 90 03 | bccb_0B28 |
| $0B25 | ee 12 0b | incsmc_draw_border_addr_hi |
| $0B28 | e8 | b_0B28inx; x-ref: $0B23 |
| $0B29 | e0 19 | cpx#$19; all 25 rows done? |
| $0B2B | d0 99 | bneb_0AC6 |
| ; Phase 2: draw initial cursor highlight box at the first path-table entry. |
| $0B2D | a9 00 | lda#$00; Phase 2: reveal level tiles from centre outward; a58=0 (ZP low byte) |
| $0B2F | 85 58 | stazp_ptr_wipe_buf_lo |
| $0B31 | a2 02 | ldx#$02; start at innermost ring (index 2); expand outward via inx |
| $0B33 | bd 00 63 | j_0B33ldawipe_path_row_table,x; read row offset for this reveal step ; x-ref: $0C02, $0C10 |
| $0B36 | c9 ff | cmp#$ff; end-of-frame sentinel? |
| $0B38 | d0 03 | bneb_0B3D |
| $0B3A | 4c 05 0c | jmpj_0C05; yes: delay this frame then check for end |
| $0B3D | a9 73 | b_0B3Dlda#$73; --- bottom-right: page=$73+row_off, col=$11+col_off; SMC adds screen_width --- ; x-ref: $0B38 |
| $0B3F | 18 | clc |
| $0B40 | 7d 00 63 | adcwipe_path_row_table,x; offscreen buffer page = $73 + row_offset (bottom half) |
| $0B43 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0B45 | ad b6 0a | ldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0B6B |
| $0B48 | 8d 6d 0b | stasmc_wipe_br_addr_hi |
| $0B4B | ad b7 0a | ldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset |
| $0B4E | bc 00 63 | ldywipe_path_row_table,x |
| $0B51 | 88 | b_0B51dey; add screen_width × row_offset to reach target screen RAM row (carry → hi byte patch) ; x-ref: $0B57, $0B5C |
| $0B52 | 30 0b | bmib_0B5F |
| $0B54 | 18 | clc |
| $0B55 | 65 51 | adczp_screen_width |
| $0B57 | 90 f8 | bccb_0B51 |
| $0B59 | ee 6d 0b | incsmc_wipe_br_addr_hi |
| $0B5C | 4c 51 0b | jmpb_0B51 |
| $0B5F | 8d 6c 0b | b_0B5Fstasmc_wipe_br_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0B52 |
| $0B62 | a9 11 | lda#$11 |
| $0B64 | 18 | clc |
| $0B65 | 7d 00 64 | adcwipe_path_col_table,x; right col = $11 + col_offset |
| $0B68 | a8 | tay |
| $0B69 | b1 58 | lda(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 |
| $0B6B | 99 00 80 | staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col |
| $0B6E | a9 73 | lda#$73; --- top-left: page=$73-row_off, col=$11-col_off; SMC subtracts screen_width --- |
| $0B70 | 38 | sec |
| $0B71 | fd 00 63 | sbcwipe_path_row_table,x; offscreen buffer page = $73 - row_offset (top half) |
| $0B74 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0B76 | ad b6 0a | ldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0B9C |
| $0B79 | 8d 9e 0b | stasmc_wipe_tl_addr_hi |
| $0B7C | ad b7 0a | ldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset |
| $0B7F | bc 00 63 | ldywipe_path_row_table,x |
| $0B82 | 88 | b_0B82dey; subtract screen_width × row_offset to reach target screen RAM row (borrow → hi byte patch) ; x-ref: $0B88, $0B8D |
| $0B83 | 30 0b | bmib_0B90 |
| $0B85 | 38 | sec |
| $0B86 | e5 51 | sbczp_screen_width |
| $0B88 | b0 f8 | bcsb_0B82 |
| $0B8A | ce 9e 0b | decsmc_wipe_tl_addr_hi |
| $0B8D | 4c 82 0b | jmpb_0B82 |
| $0B90 | 8d 9d 0b | b_0B90stasmc_wipe_tl_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0B83 |
| $0B93 | a9 11 | lda#$11 |
| $0B95 | 38 | sec |
| $0B96 | fd 00 64 | sbcwipe_path_col_table,x; left col = $11 - col_offset |
| $0B99 | a8 | tay |
| $0B9A | b1 58 | lda(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 |
| $0B9C | 99 00 80 | staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col |
| $0B9F | a9 73 | lda#$73; --- bottom-left: page=$73+row_off, col=$11-col_off; SMC adds screen_width --- |
| $0BA1 | 18 | clc |
| $0BA2 | 7d 00 63 | adcwipe_path_row_table,x; offscreen buffer page = $73 + row_offset (bottom half) |
| $0BA5 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0BA7 | ad b6 0a | ldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0BCD |
| $0BAA | 8d cf 0b | stasmc_wipe_bl_addr_hi |
| $0BAD | ad b7 0a | ldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset |
| $0BB0 | bc 00 63 | ldywipe_path_row_table,x |
| $0BB3 | 88 | b_0BB3dey; add screen_width × row_offset to reach target screen RAM row (carry → hi byte patch) ; x-ref: $0BB9, $0BBE |
| $0BB4 | 30 0b | bmib_0BC1 |
| $0BB6 | 18 | clc |
| $0BB7 | 65 51 | adczp_screen_width |
| $0BB9 | 90 f8 | bccb_0BB3 |
| $0BBB | ee cf 0b | incsmc_wipe_bl_addr_hi |
| $0BBE | 4c b3 0b | jmpb_0BB3 |
| $0BC1 | 8d ce 0b | b_0BC1stasmc_wipe_bl_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0BB4 |
| $0BC4 | a9 11 | lda#$11 |
| $0BC6 | 38 | sec |
| $0BC7 | fd 00 64 | sbcwipe_path_col_table,x; left col = $11 - col_offset |
| $0BCA | a8 | tay |
| $0BCB | b1 58 | lda(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 |
| $0BCD | 99 00 80 | staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col |
| $0BD0 | a9 73 | lda#$73; --- top-right: page=$73-row_off, col=$11+col_off; SMC subtracts screen_width --- |
| $0BD2 | 38 | sec |
| $0BD3 | fd 00 63 | sbcwipe_path_row_table,x; offscreen buffer page = $73 - row_offset (top half) |
| $0BD6 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $0BD8 | ad b6 0a | ldasmc_screen_addr_hi; SMC base screen RAM hi byte → patch STA at $0BFE |
| $0BDB | 8d 00 0c | stasmc_wipe_tr_addr_hi |
| $0BDE | ad b7 0a | ldasmc_screen_addr_lo; SMC base screen RAM lo byte; loop counter = row_offset |
| $0BE1 | bc 00 63 | ldywipe_path_row_table,x |
| $0BE4 | 88 | b_0BE4dey; subtract screen_width × row_offset to reach target screen RAM row (borrow → hi byte patch) ; x-ref: $0BEA, $0BEF |
| $0BE5 | 30 0b | bmib_0BF2 |
| $0BE7 | 38 | sec |
| $0BE8 | e5 51 | sbczp_screen_width |
| $0BEA | b0 f8 | bcsb_0BE4 |
| $0BEC | ce 00 0c | decsmc_wipe_tr_addr_hi |
| $0BEF | 4c e4 0b | jmpb_0BE4 |
| $0BF2 | 8d ff 0b | b_0BF2stasmc_wipe_tr_addr_lo; patch STA lo byte — screen RAM row address now complete ; x-ref: $0BE5 |
| $0BF5 | a9 11 | lda#$11 |
| $0BF7 | 18 | clc |
| $0BF8 | 7d 00 64 | adcwipe_path_col_table,x; right col = $11 + col_offset |
| $0BFB | a8 | tay |
| $0BFC | b1 58 | lda(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 |
| $0BFE | 99 00 80 | staSCREEN_RAM_R0C0,y; write tile directly to screen RAM at patched row + col |
| $0C01 | e8 | inx; expand: next (outer) reveal step |
| $0C02 | 4c 33 0b | jmpj_0B33 |
| $0C05 | 20 c8 09 | j_0C05jsrbusy_wait_pause; $FF sentinel: delay one frame ; x-ref: $0B3A |
| $0C08 | e8 | inx; skip past the $FF sentinel |
| $0C09 | bd 00 63 | ldawipe_path_row_table,x |
| $0C0C | c9 ff | cmp#$ff; second $FF = all rings revealed, animation complete |
| $0C0E | f0 03 | beqr_0C13 |
| $0C10 | 4c 33 0b | jmpj_0B33 |
| $0C13 | 60 | r_0C13rts; x-ref: $0C0E |
| $0C14 | | sound_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 |
| $0C7A | | sound_active_flag.byte$00; x-ref: $0C8B, $0C98, $0CAD, $0CB8, $0CC3, $0CCE, $2815 |
| $0C7B | | sound_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0C7C | a9 10 | init_audiolda#$10; x-ref: $0606, $06B3, $06CB, $0714 |
| $0C7E | 8d 4b e8 | sta$e84b; ACR Aux. control register; set to $00 at power on |
| $0C81 | a9 0f | lda#$0f |
| $0C83 | 8d 4a e8 | sta$e84a; Shift register |
| $0C86 | a9 00 | lda#$00 |
| $0C88 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0C8B | 8d 7a 0c | stasound_active_flag |
| $0C8E | 8d 7b 0c | stasound_table_index |
| $0C91 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0C92 | a9 00 | stop_audiolda#$00; x-ref: $06B0, $06C8, $0711, $2E49 |
| $0C94 | 8d 4a e8 | sta$e84a; Shift register |
| $0C97 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0C98 | ae 7a 0c | play_sound_tickldxsound_active_flag; x-ref: $064A |
| $0C9B | d0 01 | bneb_0C9E |
| $0C9D | 60 | rts |
| $0C9E | ae 7b 0c | b_0C9Eldxsound_table_index; x-ref: $0C9B |
| $0CA1 | bd 14 0c | ldasound_data_table,x |
| $0CA4 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0CA7 | f0 04 | beqb_0CAD |
| $0CA9 | ee 7b 0c | incsound_table_index |
| $0CAC | 60 | rts |
| $0CAD | ce 7a 0c | b_0CADdecsound_active_flag; x-ref: $0CA7 |
| $0CB0 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0CB1 | a9 25 | lda#$25; x-ref: $0FB8, $1034 |
| $0CB3 | 8d 7b 0c | stasound_table_index |
| $0CB6 | a9 01 | lda#$01 |
| $0CB8 | 8d 7a 0c | stasound_active_flag |
| $0CBB | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0CBC | a9 06 | lda#$06; x-ref: $2B15 |
| $0CBE | 8d 7b 0c | stasound_table_index |
| $0CC1 | a9 01 | lda#$01 |
| $0CC3 | 8d 7a 0c | stasound_active_flag |
| $0CC6 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0CC7 | a9 4e | lda#$4e; x-ref: $2601 |
| $0CC9 | 8d 7b 0c | stasound_table_index |
| $0CCC | a9 01 | lda#$01 |
| $0CCE | 8d 7a 0c | stasound_active_flag |
| $0CD1 | 60 | rts |
| ; One-shot latch for the dynamic digging pitch tone. |
| ; $FF = Armed / ready to trigger |
| ; $00 = Fired / currently playing |
| $0CD2 | | dig_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0CD3 | ad d2 0c | play_dynamic_pitchldadig_pitch_latch; x-ref: $0E75 |
| $0CD6 | f0 05 | beqb_0CDD |
| $0CD8 | a9 00 | lda#$00 |
| $0CDA | 8d d2 0c | stadig_pitch_latch |
| $0CDD | ad b5 0d | b_0CDDldadraw_page; x-ref: $0CD6 |
| $0CE0 | 38 | sec |
| $0CE1 | e9 55 | sbc#$55 |
| $0CE3 | 0a | asla |
| $0CE4 | 0a | asla |
| $0CE5 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0CE8 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $0CE9 | ad d2 0c | ldadig_pitch_latch; x-ref: $10BF |
| $0CEC | d0 0a | bner_0CF8 |
| $0CEE | a9 ff | lda#$ff |
| $0CF0 | 8d d2 0c | stadig_pitch_latch |
| $0CF3 | a9 00 | lda#$00 |
| $0CF5 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0CF8 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0CF9 | a2 18 | play_victory_jingleldx#$18; x-ref: $26CA |
| $0CFB | bd 14 0c | j_0CFBldasound_data_table,x; x-ref: $0D07 |
| $0CFE | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0D01 | f0 07 | beqr_0D0A |
| $0D03 | 20 c8 09 | jsrbusy_wait_pause |
| $0D06 | e8 | inx |
| $0D07 | 4c fb 0c | jmpj_0CFB |
| $0D0A | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0D0B | a2 57 | play_death_soundldx#$57; x-ref: $2876, $28A3, $28D2, $28FB |
| $0D0D | bd 14 0c | j_0D0Dldasound_data_table,x; x-ref: $0D19 |
| $0D10 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $0D13 | f0 07 | beqr_0D1C |
| $0D15 | 20 c8 09 | jsrbusy_wait_pause |
| $0D18 | e8 | inx |
| $0D19 | 4c 0d 0d | jmpj_0D0D |
| $0D1C | 60 | r_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 |
| $0D1D | | sprite_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 |
| $0DAD | | tile_buf_0.byte$00; x-ref: $0DD5, $1147, $1179 |
| $0DAE | | tile_buf_1.byte$00; x-ref: $0DD8, $114D, $117F |
| $0DAF | | tile_buf_2.byte$00; x-ref: $0DDB, $1153, $1185 |
| $0DB0 | | tile_buf_3.byte$00; x-ref: $0DDE, $1159, $118B |
| $0DB1 | | player_state.byte$ff; ; x-ref: $0762, $0DCB, $0FDC, $1058, $1066, $107A, $10A9, $115F, ... |
| $0DB2 | | respawn_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. |
| $0DB3 | | anim_frame.byte$00; x-ref: $0DE8, $0E7D, $0E85, $0E9B, $0EA5, $0EAE, $0EB6, $0ECC, ... |
| $0DB4 | | draw_offset.byte$00; x-ref: $0F27, $0F6A, $0F87, $0FC5, $1003, $1041, $10B3, $1144, ... |
| $0DB5 | | draw_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. |
| $0DB6 | | anim_strip_base.byte$00; x-ref: $0DE3, $0E7A, $0E94, $0EAB, $0EC5, $0EDE, $0EF8, $0F17, ... |
| $0DB7 | | input_dx.byte$00; x-ref: $0DF0, $0DFC, $0E50, $0E5F, $0E62, $0E6E, $106D, $10CF, ... |
| $0DB8 | | input_dy.byte$00; x-ref: $0DED, $0DF9, $0E32, $0E41, $0E67, $0E71, $1070, $10DC, ... |
| $0DB9 | | input_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0DBA | a9 00 | init_game_variableslda#$00; x-ref: $073F, $2FBC, $3107 |
| $0DBC | 85 52 | stazp_ptr_screen_lo |
| $0DBE | 85 54 | stazp_ptr_attr_lo; TEMPF1 Temporary storage for FLPT value. |
| $0DC0 | a9 2b | lda#$2b |
| $0DC2 | 85 5a | stazp_ptr_playfield_lo |
| $0DC4 | a9 ff | lda#$ff |
| $0DC6 | 8d d2 0c | stadig_pitch_latch |
| $0DC9 | a9 ff | lda#$ff |
| $0DCB | 8d b1 0d | staplayer_state |
| $0DCE | a9 80 | lda#$80 |
| $0DD0 | 8d b2 0d | starespawn_timer |
| $0DD3 | a9 20 | lda#$20 |
| $0DD5 | 8d ad 0d | statile_buf_0 |
| $0DD8 | 8d ae 0d | statile_buf_1 |
| $0DDB | 8d af 0d | statile_buf_2 |
| $0DDE | 8d b0 0d | statile_buf_3 |
| $0DE1 | a9 00 | lda#$00 |
| $0DE3 | 8d b6 0d | staanim_strip_base |
| $0DE6 | a9 08 | lda#$08 |
| $0DE8 | 8d b3 0d | staanim_frame |
| $0DEB | a9 00 | lda#$00 |
| $0DED | 8d b8 0d | stainput_dy |
| $0DF0 | 8d b7 0d | stainput_dx |
| $0DF3 | 8d b9 0d | stainput_state |
| $0DF6 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $0DF7 | a9 00 | scan_keyboardlda#$00; x-ref: $066B |
| $0DF9 | 8d b8 0d | stainput_dy |
| $0DFC | 8d b7 0d | stainput_dx |
| $0DFF | 8d b9 0d | stainput_state |
| $0E02 | a5 2c | ldazp_key_row_dig1; ARYTAB Pointer: Start of BASIC Arrays |
| $0E04 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E07 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E0A | 25 35 | andzp_key_col_dig1 |
| $0E0C | d0 06 | bneb_0E14 |
| $0E0E | a9 01 | lda#$01 |
| $0E10 | 8d b9 0d | stainput_state |
| $0E13 | 60 | rts |
| $0E14 | a5 2d | b_0E14ldazp_key_row_dig2; x-ref: $0E0C |
| $0E16 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E19 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E1C | 25 36 | andzp_key_col_dig2; CURLIN Current BASIC Line Number |
| $0E1E | d0 06 | bneb_0E26 |
| $0E20 | a9 ff | lda#$ff |
| $0E22 | 8d b9 0d | stainput_state |
| $0E25 | 60 | rts |
| $0E26 | a5 29 | b_0E26ldazp_key_row_up; x-ref: $0E1E |
| $0E28 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E2B | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E2E | 25 32 | andzp_key_col_up; FRESPC Utility String Pointer |
| $0E30 | d0 03 | bneb_0E35 |
| $0E32 | ce b8 0d | decinput_dy |
| $0E35 | a5 28 | b_0E35ldazp_key_row_down; x-ref: $0E30 TXTTAB Pointer: Start of BASIC Text |
| $0E37 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E3A | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E3D | 25 31 | andzp_key_col_down |
| $0E3F | d0 03 | bneb_0E44 |
| $0E41 | ee b8 0d | incinput_dy |
| $0E44 | a5 2a | b_0E44ldazp_key_row_left; x-ref: $0E3F VARTAB Pointer: Start of BASIC Variables |
| $0E46 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E49 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E4C | 25 33 | andzp_key_col_left |
| $0E4E | d0 03 | bneb_0E53 |
| $0E50 | ce b7 0d | decinput_dx |
| $0E53 | a5 2b | b_0E53ldazp_key_row_right; x-ref: $0E4E |
| $0E55 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $0E58 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $0E5B | 25 34 | andzp_key_col_right; MEMSIZ Pointer: Highest Address Used by BASIC |
| $0E5D | d0 03 | bneb_0E62 |
| $0E5F | ee b7 0d | incinput_dx |
| $0E62 | ad b7 0d | b_0E62ldainput_dx; x-ref: $0E5D |
| $0E65 | f0 0d | beqr_0E74 |
| $0E67 | ad b8 0d | ldainput_dy |
| $0E6A | f0 08 | beqr_0E74 |
| $0E6C | a9 00 | lda#$00 |
| $0E6E | 8d b7 0d | stainput_dx |
| $0E71 | 8d b8 0d | stainput_dy |
| $0E74 | 60 | r_0E74rts; x-ref: $0E65, $0E6A |
| $0E75 | 20 d3 0c | j_0E75jsrplay_dynamic_pitch; x-ref: $10BC |
| $0E78 | a9 30 | lda#$30 |
| $0E7A | 8d b6 0d | staanim_strip_base |
| $0E7D | ad b3 0d | ldaanim_frame |
| $0E80 | 18 | clc |
| $0E81 | 69 04 | adc#$04 |
| $0E83 | 29 0c | and#$0c |
| $0E85 | 8d b3 0d | staanim_frame |
| $0E88 | d0 03 | bner_0E8D |
| $0E8A | ee b5 0d | incdraw_page |
| $0E8D | 60 | r_0E8Drts; x-ref: $0E88 |
| $0E8E | b1 5a | j_0E8Elda(zp_ptr_playfield_lo),y; x-ref: $10D6 |
| $0E90 | 29 18 | and#$18 |
| $0E92 | d0 15 | bneb_0EA9 |
| $0E94 | ad b6 0d | ldaanim_strip_base |
| $0E97 | c9 20 | cmp#$20 |
| $0E99 | d0 23 | bner_0EBE |
| $0E9B | ad b3 0d | ldaanim_frame |
| $0E9E | 18 | clc |
| $0E9F | 69 04 | adc#$04 |
| $0EA1 | 29 0c | and#$0c |
| $0EA3 | f0 19 | beqr_0EBE |
| $0EA5 | 8d b3 0d | staanim_frame |
| $0EA8 | 60 | rts |
| $0EA9 | a9 20 | b_0EA9lda#$20; x-ref: $0E92 |
| $0EAB | 8d b6 0d | staanim_strip_base |
| $0EAE | ad b3 0d | ldaanim_frame |
| $0EB1 | 18 | clc |
| $0EB2 | 69 04 | adc#$04 |
| $0EB4 | 29 0c | and#$0c |
| $0EB6 | 8d b3 0d | staanim_frame |
| $0EB9 | d0 03 | bner_0EBE |
| $0EBB | ee b5 0d | incdraw_page |
| $0EBE | 60 | r_0EBErts; x-ref: $0E99, $0EA3, $0EB9 |
| $0EBF | b1 5a | j_0EBFlda(zp_ptr_playfield_lo),y; x-ref: $10D9 |
| $0EC1 | 29 04 | and#$04 |
| $0EC3 | d0 17 | bneb_0EDC |
| $0EC5 | ad b6 0d | ldaanim_strip_base |
| $0EC8 | c9 20 | cmp#$20 |
| $0ECA | d0 25 | bner_0EF1 |
| $0ECC | ad b3 0d | ldaanim_frame |
| $0ECF | 38 | sec |
| $0ED0 | e9 04 | sbc#$04 |
| $0ED2 | 29 0c | and#$0c |
| $0ED4 | c9 0c | cmp#$0c |
| $0ED6 | f0 19 | beqr_0EF1 |
| $0ED8 | 8d b3 0d | staanim_frame |
| $0EDB | 60 | rts |
| $0EDC | a9 20 | b_0EDClda#$20; x-ref: $0EC3 |
| $0EDE | 8d b6 0d | staanim_strip_base |
| $0EE1 | ad b3 0d | ldaanim_frame |
| $0EE4 | 38 | sec |
| $0EE5 | e9 04 | sbc#$04 |
| $0EE7 | 29 0c | and#$0c |
| $0EE9 | 8d b3 0d | staanim_frame |
| $0EEC | d0 03 | bner_0EF1 |
| $0EEE | ce b5 0d | decdraw_page |
| $0EF1 | 60 | r_0EF1rts; x-ref: $0ECA, $0ED6, $0EEC |
| $0EF2 | b1 5a | j_0EF2lda(zp_ptr_playfield_lo),y; x-ref: $10E3 |
| $0EF4 | 29 02 | and#$02 |
| $0EF6 | d0 17 | bneb_0F0F |
| $0EF8 | ad b6 0d | ldaanim_strip_base |
| $0EFB | f0 04 | beqb_0F01 |
| $0EFD | c9 40 | cmp#$40 |
| $0EFF | d0 29 | bner_0F2A |
| $0F01 | ad b3 0d | b_0F01ldaanim_frame; x-ref: $0EFB |
| $0F04 | 18 | clc |
| $0F05 | 69 04 | adc#$04 |
| $0F07 | 29 0c | and#$0c |
| $0F09 | f0 1f | beqr_0F2A |
| $0F0B | 8d b3 0d | staanim_frame |
| $0F0E | 60 | rts |
| $0F0F | b1 5a | b_0F0Flda(zp_ptr_playfield_lo),y; x-ref: $0EF6 |
| $0F11 | 29 20 | and#$20 |
| $0F13 | f0 02 | beqb_0F17 |
| $0F15 | a9 40 | lda#$40 |
| $0F17 | 8d b6 0d | b_0F17staanim_strip_base; x-ref: $0F13 |
| $0F1A | ad b3 0d | ldaanim_frame |
| $0F1D | 18 | clc |
| $0F1E | 69 04 | adc#$04 |
| $0F20 | 29 0c | and#$0c |
| $0F22 | 8d b3 0d | staanim_frame |
| $0F25 | d0 03 | bner_0F2A |
| $0F27 | ee b4 0d | incdraw_offset |
| $0F2A | 60 | r_0F2Arts; x-ref: $0EFF, $0F09, $0F25 |
| $0F2B | b1 5a | j_0F2Blda(zp_ptr_playfield_lo),y; x-ref: $10E6 |
| $0F2D | 29 01 | and#$01 |
| $0F2F | d0 1b | bneb_0F4C |
| $0F31 | ad b6 0d | ldaanim_strip_base |
| $0F34 | c9 10 | cmp#$10 |
| $0F36 | f0 04 | beqb_0F3C |
| $0F38 | c9 50 | cmp#$50 |
| $0F3A | d0 31 | bner_0F6D |
| $0F3C | ad b3 0d | b_0F3Cldaanim_frame; x-ref: $0F36 |
| $0F3F | 38 | sec |
| $0F40 | e9 04 | sbc#$04 |
| $0F42 | 29 0c | and#$0c |
| $0F44 | c9 0c | cmp#$0c |
| $0F46 | f0 25 | beqr_0F6D |
| $0F48 | 8d b3 0d | staanim_frame |
| $0F4B | 60 | rts |
| $0F4C | b1 5a | b_0F4Clda(zp_ptr_playfield_lo),y; x-ref: $0F2F |
| $0F4E | 29 20 | and#$20 |
| $0F50 | f0 04 | beqb_0F56 |
| $0F52 | a9 50 | lda#$50 |
| $0F54 | d0 02 | bneb_0F58 |
| $0F56 | a9 10 | b_0F56lda#$10; x-ref: $0F50 |
| $0F58 | 8d b6 0d | b_0F58staanim_strip_base; x-ref: $0F54 |
| $0F5B | ad b3 0d | ldaanim_frame |
| $0F5E | 38 | sec |
| $0F5F | e9 04 | sbc#$04 |
| $0F61 | 29 0c | and#$0c |
| $0F63 | 8d b3 0d | staanim_frame |
| $0F66 | c9 0c | cmp#$0c |
| $0F68 | d0 03 | bner_0F6D |
| $0F6A | ce b4 0d | decdraw_offset |
| $0F6D | 60 | r_0F6Drts; x-ref: $0F3A, $0F46, $0F68 |
| $0F6E | c8 | j_0F6Einy; x-ref: $10C9 |
| $0F6F | e6 5b | inczp_ptr_playfield_hi |
| $0F71 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $0F73 | c6 5b | deczp_ptr_playfield_hi |
| $0F75 | 88 | dey |
| $0F76 | 29 40 | and#$40 |
| $0F78 | f0 6f | beqr_0FE9 |
| $0F7A | a2 09 | ldx#$09 |
| $0F7C | bd 43 1e | b_0F7Cldadig_slot_status,x; x-ref: $0F99 |
| $0F7F | 30 17 | bmib_0F98 |
| $0F81 | bd 57 1e | ldadig_slot_col,x |
| $0F84 | 38 | sec |
| $0F85 | e9 01 | sbc#$01 |
| $0F87 | cd b4 0d | cmpdraw_offset |
| $0F8A | d0 0c | bneb_0F98 |
| $0F8C | bd 4d 1e | ldadig_slot_page,x |
| $0F8F | 38 | sec |
| $0F90 | e9 02 | sbc#$02 |
| $0F92 | cd b5 0d | cmpdraw_page |
| $0F95 | d0 01 | bneb_0F98 |
| $0F97 | 60 | rts |
| $0F98 | ca | b_0F98dex; x-ref: $0F7F, $0F8A, $0F95 |
| $0F99 | 10 e1 | bplb_0F7C |
| $0F9B | c8 | iny |
| $0F9C | b1 5a | lda(zp_ptr_playfield_lo),y |
| $0F9E | 88 | dey |
| $0F9F | 29 03 | and#$03 |
| $0FA1 | d0 01 | bneb_0FA4 |
| $0FA3 | 60 | rts |
| $0FA4 | c8 | b_0FA4iny; x-ref: $0FA1 |
| $0FA5 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $0FA7 | 88 | dey |
| $0FA8 | 29 0c | and#$0c |
| $0FAA | f0 01 | beqb_0FAD |
| $0FAC | 60 | rts |
| $0FAD | a2 09 | b_0FADldx#$09; x-ref: $0FAA |
| $0FAF | bd 43 1e | b_0FAFldadig_slot_status,x; x-ref: $0FB5 |
| $0FB2 | 30 04 | bmib_0FB8 |
| $0FB4 | ca | dex |
| $0FB5 | 10 f8 | bplb_0FAF |
| $0FB7 | 60 | rts |
| $0FB8 | 20 b1 0c | b_0FB8jsrtrigger_guard_captured_sound; x-ref: $0FB2 |
| $0FBB | a9 00 | lda#$00 |
| $0FBD | 9d 43 1e | stadig_slot_status,x |
| $0FC0 | a9 ff | lda#$ff |
| $0FC2 | 9d 93 1e | stadig_slot_hole_idx,x |
| $0FC5 | ad b4 0d | ldadraw_offset |
| $0FC8 | 9d 57 1e | stadig_slot_col,x |
| $0FCB | fe 57 1e | incdig_slot_col,x |
| $0FCE | ad b5 0d | ldadraw_page |
| $0FD1 | 9d 4d 1e | stadig_slot_page,x |
| $0FD4 | fe 4d 1e | incdig_slot_page,x |
| $0FD7 | fe 4d 1e | incdig_slot_page,x |
| $0FDA | a9 01 | lda#$01 |
| $0FDC | 8d b1 0d | staplayer_state |
| $0FDF | a9 70 | lda#$70 |
| $0FE1 | 8d b6 0d | staanim_strip_base |
| $0FE4 | a9 00 | lda#$00 |
| $0FE6 | 8d b3 0d | staanim_frame |
| $0FE9 | 60 | r_0FE9rts; x-ref: $0F78 |
| $0FEA | 88 | j_0FEAdey; x-ref: $10CC |
| $0FEB | e6 5b | inczp_ptr_playfield_hi |
| $0FED | b1 5a | lda(zp_ptr_playfield_lo),y |
| $0FEF | c6 5b | deczp_ptr_playfield_hi |
| $0FF1 | c8 | iny |
| $0FF2 | 29 40 | and#$40 |
| $0FF4 | f0 6f | beqr_1065 |
| $0FF6 | a2 09 | ldx#$09 |
| $0FF8 | bd 43 1e | b_0FF8ldadig_slot_status,x; x-ref: $1015 |
| $0FFB | 30 17 | bmib_1014 |
| $0FFD | bd 57 1e | ldadig_slot_col,x |
| $1000 | 18 | clc |
| $1001 | 69 01 | adc#$01 |
| $1003 | cd b4 0d | cmpdraw_offset |
| $1006 | d0 0c | bneb_1014 |
| $1008 | bd 4d 1e | ldadig_slot_page,x |
| $100B | 38 | sec |
| $100C | e9 02 | sbc#$02 |
| $100E | cd b5 0d | cmpdraw_page |
| $1011 | d0 01 | bneb_1014 |
| $1013 | 60 | rts |
| $1014 | ca | b_1014dex; x-ref: $0FFB, $1006, $1011 |
| $1015 | 10 e1 | bplb_0FF8 |
| $1017 | 88 | dey |
| $1018 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $101A | c8 | iny |
| $101B | 29 03 | and#$03 |
| $101D | d0 01 | bneb_1020 |
| $101F | 60 | rts |
| $1020 | 88 | b_1020dey; x-ref: $101D |
| $1021 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $1023 | c8 | iny |
| $1024 | 29 0c | and#$0c |
| $1026 | f0 01 | beqb_1029 |
| $1028 | 60 | rts |
| $1029 | a2 09 | b_1029ldx#$09; x-ref: $1026 |
| $102B | bd 43 1e | b_102Bldadig_slot_status,x; x-ref: $1031 |
| $102E | 30 04 | bmib_1034 |
| $1030 | ca | dex |
| $1031 | 10 f8 | bplb_102B |
| $1033 | 60 | rts |
| $1034 | 20 b1 0c | b_1034jsrtrigger_guard_captured_sound; x-ref: $102E |
| $1037 | a9 00 | lda#$00 |
| $1039 | 9d 43 1e | stadig_slot_status,x |
| $103C | a9 ff | lda#$ff |
| $103E | 9d 93 1e | stadig_slot_hole_idx,x |
| $1041 | ad b4 0d | ldadraw_offset |
| $1044 | 9d 57 1e | stadig_slot_col,x |
| $1047 | de 57 1e | decdig_slot_col,x |
| $104A | ad b5 0d | ldadraw_page |
| $104D | 9d 4d 1e | stadig_slot_page,x |
| $1050 | fe 4d 1e | incdig_slot_page,x |
| $1053 | fe 4d 1e | incdig_slot_page,x |
| $1056 | a9 01 | lda#$01 |
| $1058 | 8d b1 0d | staplayer_state |
| $105B | a9 80 | lda#$80 |
| $105D | 8d b6 0d | staanim_strip_base |
| $1060 | a9 00 | lda#$00 |
| $1062 | 8d b3 0d | staanim_frame |
| $1065 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1066 | ad b1 0d | process_player_stateldaplayer_state; x-ref: $0638, $066E, $3238 |
| $1069 | f0 42 | beqb_10AD |
| $106B | 10 21 | bplb_108E |
| $106D | ad b7 0d | ldainput_dx |
| $1070 | 0d b8 0d | orainput_dy |
| $1073 | 0d b9 0d | orainput_state |
| $1076 | f0 10 | beqb_1088 |
| $1078 | a9 00 | b_1078lda#$00; x-ref: $108B |
| $107A | 8d b1 0d | staplayer_state |
| $107D | a2 04 | ldx#$04 |
| $107F | a9 02 | lda#$02 |
| $1081 | 9d 1c 22 | b_1081staguard_move_rate,x; x-ref: $1085 |
| $1084 | ca | dex |
| $1085 | 10 fa | bplb_1081 |
| $1087 | 60 | rts |
| $1088 | ce b2 0d | b_1088decrespawn_timer; x-ref: $1076 |
| $108B | f0 eb | beqb_1078 |
| $108D | 60 | rts |
| $108E | ad b3 0d | b_108Eldaanim_frame; x-ref: $106B |
| $1091 | 18 | clc |
| $1092 | 69 04 | adc#$04 |
| $1094 | 29 0c | and#$0c |
| $1096 | 8d b3 0d | staanim_frame |
| $1099 | a0 09 | ldy#$09 |
| $109B | b9 43 1e | b_109Bldadig_slot_status,y; x-ref: $10A5 |
| $109E | f0 0c | beqr_10AC |
| $10A0 | c9 01 | cmp#$01 |
| $10A2 | f0 08 | beqr_10AC |
| $10A4 | 88 | dey |
| $10A5 | 10 f4 | bplb_109B |
| $10A7 | a9 00 | lda#$00 |
| $10A9 | 8d b1 0d | staplayer_state |
| $10AC | 60 | r_10ACrts; x-ref: $109E, $10A2 |
| $10AD | ac b5 0d | b_10ADldydraw_page; x-ref: $1069 |
| $10B0 | c8 | iny |
| $10B1 | 84 5b | styzp_ptr_playfield_hi |
| $10B3 | ac b4 0d | ldydraw_offset |
| $10B6 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $10B8 | 29 10 | and#$10 |
| $10BA | f0 03 | beqb_10BF |
| $10BC | 4c 75 0e | jmpj_0E75 |
| $10BF | 20 e9 0c | b_10BFjsrsilence_dynamic_pitch; x-ref: $10BA |
| $10C2 | ad b9 0d | ldainput_state |
| $10C5 | f0 08 | beqb_10CF |
| $10C7 | 30 03 | bmib_10CC |
| $10C9 | 4c 6e 0f | jmpj_0F6E |
| $10CC | 4c ea 0f | b_10CCjmpj_0FEA; x-ref: $10C7 |
| $10CF | ad b7 0d | b_10CFldainput_dx; x-ref: $10C5 |
| $10D2 | f0 08 | beqb_10DC |
| $10D4 | 30 03 | bmib_10D9 |
| $10D6 | 4c 8e 0e | jmpj_0E8E |
| $10D9 | 4c bf 0e | b_10D9jmpj_0EBF; x-ref: $10D4 |
| $10DC | ad b8 0d | b_10DCldainput_dy; x-ref: $10D2 |
| $10DF | f0 08 | beqb_10E9 |
| $10E1 | 30 03 | bmib_10E6 |
| $10E3 | 4c f2 0e | jmpj_0EF2 |
| $10E6 | 4c 2b 0f | b_10E6jmpj_0F2B; x-ref: $10E1 |
| $10E9 | ad b6 0d | b_10E9ldaanim_strip_base; x-ref: $10DF |
| $10EC | d0 09 | bneb_10F7 |
| $10EE | ad b3 0d | ldaanim_frame |
| $10F1 | 09 04 | ora#$04 |
| $10F3 | 8d b3 0d | staanim_frame |
| $10F6 | 60 | rts |
| $10F7 | c9 10 | b_10F7cmp#$10; x-ref: $10EC |
| $10F9 | d0 09 | bneb_1104 |
| $10FB | ad b3 0d | ldaanim_frame |
| $10FE | 29 08 | and#$08 |
| $1100 | 8d b3 0d | staanim_frame |
| $1103 | 60 | rts |
| $1104 | c9 20 | b_1104cmp#$20; x-ref: $10F9 |
| $1106 | d0 06 | bneb_110E |
| $1108 | a9 00 | lda#$00 |
| $110A | 8d b3 0d | staanim_frame |
| $110D | 60 | rts |
| $110E | c9 30 | b_110Ecmp#$30; x-ref: $1106 |
| $1110 | d0 06 | bneb_1118 |
| $1112 | a9 00 | lda#$00 |
| $1114 | 8d b3 0d | staanim_frame |
| $1117 | 60 | rts |
| $1118 | c9 70 | b_1118cmp#$70; x-ref: $1110 |
| $111A | d0 0b | bneb_1127 |
| $111C | a9 00 | lda#$00 |
| $111E | 8d b6 0d | staanim_strip_base |
| $1121 | a9 08 | lda#$08 |
| $1123 | 8d b3 0d | staanim_frame |
| $1126 | 60 | rts |
| $1127 | c9 80 | b_1127cmp#$80; x-ref: $111A |
| $1129 | d0 0b | bneb_1136 |
| $112B | a9 10 | lda#$10 |
| $112D | 8d b6 0d | staanim_strip_base |
| $1130 | a9 04 | lda#$04 |
| $1132 | 8d b3 0d | staanim_frame |
| $1135 | 60 | rts |
| $1136 | a9 60 | b_1136lda#$60; x-ref: $1129 |
| $1138 | 8d b6 0d | staanim_strip_base |
| $113B | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $113C | ac b5 0d | restore_sprite_bgldydraw_page; x-ref: $0632, $0653, $18D1, $1A0E, $2FDD, $3009, $3232 |
| $113F | 84 53 | styzp_ptr_screen_hi |
| $1141 | c8 | iny |
| $1142 | 84 55 | styzp_ptr_attr_hi |
| $1144 | ac b4 0d | ldydraw_offset |
| $1147 | ad ad 0d | ldatile_buf_0 |
| $114A | 91 52 | sta(zp_ptr_screen_lo),y |
| $114C | 88 | dey |
| $114D | ad ae 0d | ldatile_buf_1 |
| $1150 | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $1152 | c8 | iny |
| $1153 | ad af 0d | ldatile_buf_2 |
| $1156 | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $1158 | c8 | iny |
| $1159 | ad b0 0d | ldatile_buf_3 |
| $115C | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $115E | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $115F | ad b1 0d | ldaplayer_state; x-ref: $063B, $068F, $2FD3, $2FFF, $302D, $311E, $323B |
| $1162 | 10 08 | bplb_116C |
| $1164 | ad b2 0d | ldarespawn_timer |
| $1167 | 29 10 | and#$10 |
| $1169 | f0 01 | beqb_116C |
| $116B | 60 | rts |
| $116C | ac b5 0d | b_116Cldydraw_page; x-ref: $1162, $1169 |
| $116F | 84 53 | styzp_ptr_screen_hi |
| $1171 | c8 | iny |
| $1172 | 84 55 | styzp_ptr_attr_hi |
| $1174 | ac b4 0d | ldydraw_offset |
| $1177 | b1 52 | lda(zp_ptr_screen_lo),y |
| $1179 | 8d ad 0d | statile_buf_0 |
| $117C | 88 | dey |
| $117D | b1 54 | lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $117F | 8d ae 0d | statile_buf_1 |
| $1182 | c8 | iny |
| $1183 | b1 54 | lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $1185 | 8d af 0d | statile_buf_2 |
| $1188 | c8 | iny |
| $1189 | b1 54 | lda(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $118B | 8d b0 0d | statile_buf_3 |
| $118E | 88 | dey |
| $118F | a2 09 | ldx#$09 |
| $1191 | bd 43 1e | b_1191ldadig_slot_status,x; x-ref: $11B3 |
| $1194 | 30 1c | bmib_11B2 |
| $1196 | c9 03 | cmp#$03 |
| $1198 | d0 1b | bneb_11B5 |
| $119A | bd 4d 1e | ldadig_slot_page,x |
| $119D | cd b5 0d | cmpdraw_page |
| $11A0 | d0 10 | bneb_11B2 |
| $11A2 | bd 57 1e | ldadig_slot_col,x |
| $11A5 | cd b4 0d | cmpdraw_offset |
| $11A8 | d0 08 | bneb_11B2 |
| $11AA | bd 61 1e | ldadig_slot_anim_step,x |
| $11AD | 29 01 | and#$01 |
| $11AF | f0 04 | beqb_11B5 |
| $11B1 | 60 | rts |
| $11B2 | ca | b_11B2dex; x-ref: $1194, $11A0, $11A8 |
| $11B3 | 10 dc | bplb_1191 |
| $11B5 | ad b6 0d | b_11B5ldaanim_strip_base; x-ref: $1198, $11AF |
| $11B8 | 18 | clc |
| $11B9 | 6d b3 0d | adcanim_frame |
| $11BC | aa | tax |
| $11BD | bd 1d 0d | ldasprite_anim_table,x |
| $11C0 | c9 20 | cmp#$20 |
| $11C2 | f0 02 | beqb_11C6 |
| $11C4 | 91 52 | sta(zp_ptr_screen_lo),y |
| $11C6 | e8 | b_11C6inx; x-ref: $11C2 |
| $11C7 | 88 | dey |
| $11C8 | bd 1d 0d | ldasprite_anim_table,x |
| $11CB | c9 20 | cmp#$20 |
| $11CD | f0 02 | beqb_11D1 |
| $11CF | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $11D1 | e8 | b_11D1inx; x-ref: $11CD |
| $11D2 | c8 | iny |
| $11D3 | bd 1d 0d | ldasprite_anim_table,x |
| $11D6 | c9 20 | cmp#$20 |
| $11D8 | f0 02 | beqb_11DC |
| $11DA | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $11DC | e8 | b_11DCinx; x-ref: $11D8 |
| $11DD | c8 | iny |
| $11DE | bd 1d 0d | ldasprite_anim_table,x |
| $11E1 | c9 20 | cmp#$20 |
| $11E3 | f0 02 | beqr_11E7 |
| $11E5 | 91 54 | sta(zp_ptr_attr_lo),y; TEMPF1 Temporary storage for FLPT value. |
| $11E7 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $11E8 | a9 01 | unpack_level_datalda#$01; --- phase 1: tile render --- ; x-ref: $0751 |
| $11EA | 85 56 | stazp_ptr_render_buf_lo; a56=$01/a57=$67: buffer start at $6701 (row 0, col 0) |
| $11EC | a9 67 | lda#$67 |
| $11EE | 85 57 | stazp_ptr_render_buf_hi |
| $11F0 | a0 00 | ldy#$00; Y = level data byte index; X = column (0..31) |
| $11F2 | a2 00 | b_11F2ldx#$00; x-ref: $122A |
| $11F4 | b1 74 | b_11F4lda(zp_ptr_level_data),y; high nibble (>>4) = left tile of pair → render at column X ; x-ref: $120B |
| $11F6 | 29 f0 | and#$f0 |
| $11F8 | 4a | lsra |
| $11F9 | 4a | lsra |
| $11FA | 4a | lsra |
| $11FB | 4a | lsra |
| $11FC | 20 ff 13 | jsrrender_actor_sprite |
| $11FF | e8 | inx; low nibble = right tile of pair → render at column X+1 |
| $1200 | b1 74 | lda(zp_ptr_level_data),y |
| $1202 | 29 0f | and#$0f |
| $1204 | 20 ff 13 | jsrrender_actor_sprite |
| $1207 | c8 | iny; advance to next byte; X+=2; loop until 32 tiles (X=$20) |
| $1208 | e8 | inx |
| $1209 | e0 20 | cpx#$20 |
| $120B | d0 e7 | bneb_11F4 |
| $120D | 88 | dey; re-read last row byte; bit 7: 0=2-page row, 1=1-page row |
| $120E | b1 74 | lda(zp_ptr_level_data),y |
| $1210 | c8 | iny |
| $1211 | 29 80 | and#$80 |
| $1213 | d0 0a | bneb_121F |
| $1215 | a5 57 | ldazp_ptr_render_buf_hi; normal row: advance 2 buffer pages |
| $1217 | 18 | clc |
| $1218 | 69 02 | adc#$02 |
| $121A | 85 57 | stazp_ptr_render_buf_hi |
| $121C | 4c 26 12 | jmpj_1226 |
| $121F | a5 57 | b_121Fldazp_ptr_render_buf_hi; thin row: advance 1 buffer page ; x-ref: $1213 |
| $1221 | 18 | clc |
| $1222 | 69 01 | adc#$01 |
| $1224 | 85 57 | stazp_ptr_render_buf_hi |
| $1226 | a5 57 | j_1226ldazp_ptr_render_buf_hi; stop when a57 reaches $80 (past last screen row) ; x-ref: $121C |
| $1228 | c9 80 | cmp#$80 |
| $122A | 90 c6 | bccb_11F2 |
| $122C | b1 74 | lda(zp_ptr_level_data),y; --- phase 2: level metadata --- |
| $122E | 8d b4 0d | stadraw_offset; player start column |
| $1231 | c8 | iny |
| $1232 | b1 74 | lda(zp_ptr_level_data),y; player start row |
| $1234 | 8d b5 0d | stadraw_page |
| $1237 | c8 | iny |
| $1238 | b1 74 | lda(zp_ptr_level_data),y; exit walk start column+1 |
| $123A | 8d e7 2a | stabonus_walk_offset |
| $123D | c8 | iny |
| $123E | b1 74 | lda(zp_ptr_level_data),y; exit walk start row page |
| $1240 | 8d e8 2a | stabonus_walk_page |
| $1243 | c8 | iny |
| $1244 | b1 74 | lda(zp_ptr_level_data),y; level exit tile column |
| $1246 | 8d e2 2a | staexit_offset |
| $1249 | c8 | iny |
| $124A | b1 74 | lda(zp_ptr_level_data),y; level exit tile row |
| $124C | 8d e3 2a | staexit_page |
| $124F | c8 | iny |
| $1250 | b1 74 | lda(zp_ptr_level_data),y; guard respawn column |
| $1252 | 8d e4 2a | staguard_spawn_offset |
| $1255 | c8 | iny |
| $1256 | b1 74 | lda(zp_ptr_level_data),y; guard respawn row |
| $1258 | 8d e5 2a | staguard_spawn_page |
| $125B | a2 00 | ldx#$00; --- phase 3: gold positions (byte < $41) --- |
| $125D | c8 | j_125Diny; x-ref: $127D |
| $125E | b1 74 | lda(zp_ptr_level_data),y |
| $1260 | c5 ff | cmpzp_kernal_ff_sentinel; $FF = end of entity list |
| $1262 | f0 4e | beqb_12B2 |
| $1264 | c9 41 | cmp#$41; byte >= $41 → guard entry |
| $1266 | b0 18 | bcsb_1280 |
| $1268 | 38 | sec; gold column = byte - $20 |
| $1269 | e9 20 | sbc#$20 |
| $126B | 9d 01 25 | stagold_offset,x |
| $126E | ee e0 24 | incgold_total; one more gold piece on this level |
| $1271 | a9 00 | lda#$00; mark gold as uncollected |
| $1273 | 9d e1 24 | stagold_active_flag,x |
| $1276 | c8 | iny |
| $1277 | b1 74 | lda(zp_ptr_level_data),y; next byte = gold row page |
| $1279 | 9d f1 24 | stagold_page,x |
| $127C | e8 | inx; --- phase 4: guard positions (byte $41–$FE) --- |
| $127D | 4c 5d 12 | jmpj_125D |
| $1280 | b1 74 | b_1280lda(zp_ptr_level_data),y; guard column = byte - $40; push to stack while scanning for free slot ; x-ref: $1266, $12A5, $12AF |
| $1282 | c9 ff | cmp#$ff |
| $1284 | f0 2c | beqb_12B2 |
| $1286 | 38 | sec |
| $1287 | e9 40 | sbc#$40 |
| $1289 | 48 | pha |
| $128A | a2 04 | ldx#$04; scan slots 4..0 for a free entry ($FF = free) |
| $128C | bd f9 21 | b_128Cldaguard_active,x; x-ref: $12A9 |
| $128F | c9 ff | cmp#$ff |
| $1291 | d0 15 | bneb_12A8 |
| $1293 | a9 00 | lda#$00; slot taken → try next |
| $1295 | 9d f9 21 | staguard_active,x |
| $1298 | 68 | pla |
| $1299 | 9d 03 22 | staguard_offset,x |
| $129C | c8 | iny; next byte = guard row page |
| $129D | b1 74 | lda(zp_ptr_level_data),y |
| $129F | 9d 08 22 | staguard_page,x |
| $12A2 | c8 | iny |
| $12A3 | f0 0d | beqb_12B2 |
| $12A5 | 4c 80 12 | jmpb_1280 |
| $12A8 | ca | b_12A8dex; x-ref: $1291 |
| $12A9 | 10 e1 | bplb_128C |
| $12AB | 68 | pla |
| $12AC | c8 | iny |
| $12AD | f0 03 | beqb_12B2 |
| $12AF | 4c 80 12 | jmpb_1280 |
| $12B2 | 20 8c 1a | b_12B2jsrbuild_movement_grid; --- phase 5: build movement grid, then autotile fixup --- ; x-ref: $1262, $1284, $12A3, $12AD |
| $12B5 | a9 00 | lda#$00 |
| $12B7 | 85 56 | stazp_ptr_render_buf_lo; --- fixup pass 1: ladder left/right edge joins (scan all rows, Y $01..$20) --- |
| $12B9 | a9 67 | lda#$67 |
| $12BB | 85 57 | stazp_ptr_render_buf_hi |
| $12BD | a0 01 | b_12BDldy#$01; x-ref: $1309 |
| $12BF | b1 56 | b_12BFlda(zp_ptr_render_buf_lo),y; found ladder char ($43): fix its left and right neighbours ; x-ref: $1301 |
| $12C1 | c9 43 | cmp#$43 |
| $12C3 | d0 39 | bneb_12FE |
| $12C5 | 88 | dey; left neighbour (Y-1): $20→$67, $63→$50 |
| $12C6 | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $12C8 | c9 20 | cmp#$20 |
| $12CA | d0 07 | bneb_12D3 |
| $12CC | a9 67 | lda#$67 |
| $12CE | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $12D0 | 4c db 12 | jmpj_12DB |
| $12D3 | c9 63 | b_12D3cmp#$63; x-ref: $12CA |
| $12D5 | d0 04 | bnej_12DB |
| $12D7 | a9 50 | lda#$50 |
| $12D9 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $12DB | c8 | j_12DBiny; right neighbour (Y+1): $20→$65, $63→$4F, $CC→$E4 ; x-ref: $12D0, $12D5 |
| $12DC | c8 | iny |
| $12DD | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $12DF | c9 20 | cmp#$20 |
| $12E1 | d0 07 | bneb_12EA |
| $12E3 | a9 65 | lda#$65 |
| $12E5 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $12E7 | 4c f5 12 | jmpj_12F5 |
| $12EA | c9 63 | b_12EAcmp#$63; x-ref: $12E1 |
| $12EC | d0 07 | bnej_12F5 |
| $12EE | a9 4f | lda#$4f |
| $12F0 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $12F2 | 4c fd 12 | jmpj_12FD |
| $12F5 | c9 cc | j_12F5cmp#$cc; x-ref: $12E7, $12EC |
| $12F7 | d0 04 | bnej_12FD |
| $12F9 | a9 e4 | lda#$e4 |
| $12FB | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $12FD | 88 | j_12FDdey; x-ref: $12F2, $12F7 |
| $12FE | c8 | b_12FEiny; x-ref: $12C3 |
| $12FF | c0 21 | cpy#$21; advance to next row; stop at page $80 |
| $1301 | d0 bc | bneb_12BF |
| $1303 | e6 57 | inczp_ptr_render_buf_hi |
| $1305 | a5 57 | ldazp_ptr_render_buf_hi |
| $1307 | c9 80 | cmp#$80 |
| $1309 | d0 b2 | bneb_12BD |
| $130B | a9 00 | lda#$00; --- fixup pass 2: bottom-row char substitution (page $7F only) --- |
| $130D | 85 56 | stazp_ptr_render_buf_lo |
| $130F | a9 7f | lda#$7f |
| $1311 | 85 57 | stazp_ptr_render_buf_hi |
| $1313 | a0 01 | ldy#$01 |
| $1315 | b1 56 | b_1315lda(zp_ptr_render_buf_lo),y; $20→$64, $67→$7A, $65→$4C (bottom-row visual variants) ; x-ref: $1338 |
| $1317 | c9 20 | cmp#$20 |
| $1319 | d0 07 | bneb_1322 |
| $131B | a9 64 | lda#$64 |
| $131D | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $131F | 4c 35 13 | jmpj_1335 |
| $1322 | c9 67 | b_1322cmp#$67; x-ref: $1319 |
| $1324 | d0 07 | bneb_132D |
| $1326 | a9 7a | lda#$7a |
| $1328 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $132A | 4c 35 13 | jmpj_1335 |
| $132D | c9 65 | b_132Dcmp#$65; x-ref: $1324 |
| $132F | d0 04 | bnej_1335 |
| $1331 | a9 4c | lda#$4c |
| $1333 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1335 | c8 | j_1335iny; x-ref: $131F, $132A, $132F |
| $1336 | c0 21 | cpy#$21 |
| $1338 | d0 db | bneb_1315 |
| $133A | a9 00 | lda#$00; --- fixup pass 3: entity top→brick (scan all rows, one col per row) --- |
| $133C | 85 56 | stazp_ptr_render_buf_lo |
| $133E | a9 67 | lda#$67 |
| $1340 | 85 57 | stazp_ptr_render_buf_hi |
| $1342 | a0 01 | ldy#$01 |
| $1344 | b1 56 | b_1344lda(zp_ptr_render_buf_lo),y; $CC (entity top) → $E4 (brick bottom char) ; x-ref: $1354 |
| $1346 | c9 cc | cmp#$cc |
| $1348 | d0 04 | bneb_134E |
| $134A | a9 e4 | lda#$e4 |
| $134C | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $134E | e6 57 | b_134Einczp_ptr_render_buf_hi; x-ref: $1348 |
| $1350 | a5 57 | ldazp_ptr_render_buf_hi |
| $1352 | c9 80 | cmp#$80 |
| $1354 | d0 ee | bneb_1344 |
| $1356 | a9 00 | lda#$00; --- fixup pass 4: space+entity→brick (if Y is space and Y+1 is $CC → $E4) --- |
| $1358 | 85 56 | stazp_ptr_render_buf_lo |
| $135A | a9 67 | lda#$67 |
| $135C | 85 57 | stazp_ptr_render_buf_hi |
| $135E | a0 01 | b_135Eldy#$01; x-ref: $137D |
| $1360 | b1 56 | b_1360lda(zp_ptr_render_buf_lo),y; space at Y: check right neighbour (Y+1) ; x-ref: $1375 |
| $1362 | c9 20 | cmp#$20 |
| $1364 | d0 0c | bneb_1372 |
| $1366 | c8 | iny |
| $1367 | b1 56 | lda(zp_ptr_render_buf_lo),y; $CC immediately right of space → $E4 |
| $1369 | c9 cc | cmp#$cc |
| $136B | d0 04 | bneb_1371 |
| $136D | a9 e4 | lda#$e4 |
| $136F | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1371 | 88 | b_1371dey; x-ref: $136B |
| $1372 | c8 | b_1372iny; x-ref: $1364 |
| $1373 | c0 21 | cpy#$21 |
| $1375 | d0 e9 | bneb_1360 |
| $1377 | e6 57 | inczp_ptr_render_buf_hi |
| $1379 | a5 57 | ldazp_ptr_render_buf_hi |
| $137B | c9 80 | cmp#$80 |
| $137D | d0 df | bneb_135E |
| $137F | a9 00 | lda#$00; --- fixup pass 5: floor edge substitution (all rows) --- |
| $1381 | 85 56 | stazp_ptr_render_buf_lo |
| $1383 | a9 67 | lda#$67 |
| $1385 | 85 57 | stazp_ptr_render_buf_hi |
| $1387 | a0 01 | ldy#$01 |
| $1389 | b1 56 | b_1389lda(zp_ptr_render_buf_lo),y; $20→$63, $67→$50, $65→$4F ; x-ref: $13AC |
| $138B | c9 20 | cmp#$20 |
| $138D | d0 07 | bneb_1396 |
| $138F | a9 63 | lda#$63 |
| $1391 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1393 | 4c a9 13 | jmpj_13A9 |
| $1396 | c9 67 | b_1396cmp#$67; x-ref: $138D |
| $1398 | d0 07 | bneb_13A1 |
| $139A | a9 50 | lda#$50 |
| $139C | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $139E | 4c a9 13 | jmpj_13A9 |
| $13A1 | c9 65 | b_13A1cmp#$65; x-ref: $1398 |
| $13A3 | d0 04 | bnej_13A9 |
| $13A5 | a9 4f | lda#$4f |
| $13A7 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $13A9 | c8 | j_13A9iny; x-ref: $1393, $139E, $13A3 |
| $13AA | c0 21 | cpy#$21 |
| $13AC | d0 db | bneb_1389 |
| $13AE | a9 00 | lda#$00; --- fixup pass 6: final char substitution (all rows) --- |
| $13B0 | 85 56 | stazp_ptr_render_buf_lo |
| $13B2 | a9 67 | lda#$67 |
| $13B4 | 85 57 | stazp_ptr_render_buf_hi |
| $13B6 | a0 01 | ldy#$01 |
| $13B8 | b1 56 | b_13B8lda(zp_ptr_render_buf_lo),y; $20→$65, $63→$4F, $64→$4C, $43→$4F ; x-ref: $13E0 |
| $13BA | c9 20 | cmp#$20 |
| $13BC | d0 04 | bneb_13C2 |
| $13BE | a9 65 | lda#$65 |
| $13C0 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $13C2 | c9 63 | b_13C2cmp#$63; x-ref: $13BC |
| $13C4 | d0 04 | bneb_13CA |
| $13C6 | a9 4f | lda#$4f |
| $13C8 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $13CA | c9 64 | b_13CAcmp#$64; x-ref: $13C4 |
| $13CC | d0 04 | bneb_13D2 |
| $13CE | a9 4c | lda#$4c |
| $13D0 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $13D2 | c9 43 | b_13D2cmp#$43; x-ref: $13CC |
| $13D4 | d0 04 | bneb_13DA |
| $13D6 | a9 4f | lda#$4f |
| $13D8 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $13DA | e6 57 | b_13DAinczp_ptr_render_buf_hi; x-ref: $13D4 |
| $13DC | a5 57 | ldazp_ptr_render_buf_hi |
| $13DE | c9 80 | cmp#$80 |
| $13E0 | d0 d6 | bneb_13B8 |
| $13E2 | a9 00 | lda#$00; --- finalise: fill col $21 with $65, then draw guards and gold --- |
| $13E4 | 85 56 | stazp_ptr_render_buf_lo |
| $13E6 | a9 67 | lda#$67 |
| $13E8 | 85 57 | stazp_ptr_render_buf_hi |
| $13EA | a0 21 | ldy#$21 |
| $13EC | a9 65 | b_13EClda#$65; x-ref: $13F6 |
| $13EE | 91 56 | sta(zp_ptr_render_buf_lo),y; write $65 at col $21 (right sidebar separator) on every row |
| $13F0 | e6 57 | inczp_ptr_render_buf_hi |
| $13F2 | a5 57 | ldazp_ptr_render_buf_hi |
| $13F4 | c9 80 | cmp#$80 |
| $13F6 | d0 f4 | bneb_13EC |
| $13F8 | 20 78 24 | jsrdraw_guards; draw all guard sprites into buffer |
| $13FB | 20 52 25 | jsrdraw_gold; draw all gold pieces into buffer |
| $13FE | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $13FF | 85 64 | render_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 |
| $1401 | 98 | tya |
| $1402 | 48 | pha |
| $1403 | 8a | txa; X (column index) → Y for (a56),Y screen writes |
| $1404 | a8 | tay |
| $1405 | a5 64 | ldazp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer |
| $1407 | 29 08 | and#$08; bit 3 set → 1-row tile path |
| $1409 | d0 50 | bneb_145B |
| $140B | a5 64 | ldazp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer |
| $140D | c9 00 | cmp#$00; --- 2-row tiles (bit 3 clear) --- |
| $140F | d0 03 | bneb_1414 |
| $1411 | 4c 8c 14 | jmpj_148C |
| $1414 | c9 01 | b_1414cmp#$01; $01 → entity sprite: $CC top row, $DD bottom row ; x-ref: $140F |
| $1416 | d0 0f | bneb_1427 |
| $1418 | a9 cc | lda#$cc; write top char to current row |
| $141A | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $141C | e6 57 | inczp_ptr_render_buf_hi; inc a57 → next page (row below); write bottom char; dec a57 → restore |
| $141E | a9 dd | lda#$dd |
| $1420 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1422 | c6 57 | deczp_ptr_render_buf_hi |
| $1424 | 4c 8c 14 | jmpj_148C |
| $1427 | c9 02 | b_1427cmp#$02; $02 → ladder: $43 both rows ; x-ref: $1416 |
| $1429 | d0 0d | bneb_1438 |
| $142B | a9 43 | lda#$43 |
| $142D | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $142F | e6 57 | inczp_ptr_render_buf_hi |
| $1431 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1433 | c6 57 | deczp_ptr_render_buf_hi |
| $1435 | 4c 8c 14 | jmpj_148C |
| $1438 | c9 03 | b_1438cmp#$03; $03 → open top / floor bottom: $20 top row, $63 bottom row ; x-ref: $1429 |
| $143A | d0 0c | bneb_1448 |
| $143C | a9 20 | lda#$20 |
| $143E | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1440 | e6 57 | inczp_ptr_render_buf_hi |
| $1442 | a9 63 | lda#$63 |
| $1444 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1446 | c6 57 | deczp_ptr_render_buf_hi |
| $1448 | c9 04 | b_1448cmp#$04; $04 → brick tile: $A0 top row, $E4 bottom row ; x-ref: $143A |
| $144A | d0 0c | bneb_1458 |
| $144C | a9 a0 | lda#$a0 |
| $144E | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1450 | e6 57 | inczp_ptr_render_buf_hi |
| $1452 | a9 e4 | lda#$e4 |
| $1454 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1456 | c6 57 | deczp_ptr_render_buf_hi |
| $1458 | 4c 8c 14 | b_1458jmpj_148C; x-ref: $144A |
| $145B | a5 64 | b_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 |
| $145D | 29 07 | and#$07 |
| $145F | c9 00 | cmp#$00 |
| $1461 | d0 03 | bneb_1466; $08 ($00 after mask) → empty: no write |
| $1463 | 4c 8c 14 | jmpj_148C |
| $1466 | c9 01 | b_1466cmp#$01; $09 ($01 after mask) → rope: write $DB ; x-ref: $1461 |
| $1468 | d0 07 | bneb_1471 |
| $146A | a9 db | lda#$db |
| $146C | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $146E | 4c 8c 14 | jmpj_148C |
| $1471 | c9 02 | b_1471cmp#$02; $0A ($02 after mask) → ladder half: write $43 ; x-ref: $1468 |
| $1473 | d0 07 | bneb_147C |
| $1475 | a9 43 | lda#$43 |
| $1477 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1479 | 4c 8c 14 | jmpj_148C |
| $147C | c9 03 | b_147Ccmp#$03; $0B ($03 after mask) → floor half: write $63 ; x-ref: $1473 |
| $147E | d0 04 | bneb_1484 |
| $1480 | a9 63 | lda#$63 |
| $1482 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1484 | c9 04 | b_1484cmp#$04; $0C ($04 after mask) → brick half: write $A0 ; x-ref: $147E |
| $1486 | d0 04 | bnej_148C |
| $1488 | a9 a0 | lda#$a0 |
| $148A | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $148C | 68 | j_148Cpla; restore Y (level data index) and return ; x-ref: $1411, $1424, $1435, $1458, $1463, $146E, $1479, $1486 |
| $148D | a8 | tay |
| $148E | 60 | rts |
| | .encode |
| | .enc"screen" |
| $148F | | txt_lode_runner_pet.text"LODE RUNNER ON COMMODORE PET", $ff; x-ref: $1693 |
| | .endencode |
| $14AC | | txt_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 |
| $14DF | | txt_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 |
| $150B | | f_150B.fill40, $fa; x-ref: $1715 |
| $1533 | | .byte$ff |
| $1534 | | f_1534.fill40, $c2; x-ref: $1728 |
| $155C | | .byte$ff |
| | .encode |
| | .enc"screen" |
| $155D | | txt_select_move_left.text" SELECT KEY TO MOVE LEFT (EG: J)", $ff; x-ref: $17AD |
| $157E | | txt_select_move_up.text"SELECT KEY TO MOVE UP (EG: I) ", $ff; x-ref: $181C |
| $159F | | txt_select_move_down.text" SELECT KEY TO MOVE DOWN (EG: K)", $ff; x-ref: $188D |
| $15C0 | | txt_select_dig_right.text"SELECT KEY TO DIG RIGHT (EG: F) ", $ff; x-ref: $1905 |
| $15E1 | | txt_select_dig_left.text" SELECT KEY TO DIG LEFT (EG: D) ", $ff; x-ref: $1958 |
| $1602 | | txt_select_pause.text"SELECT KEY TO PAUSE GAME (EG: Q)", $ff; x-ref: $19BC |
| | .endencode |
| $1623 | | irq_counter.byte$00; x-ref: $1650, $166C, $1673 |
| $1624 | | title_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1625 | a9 2b | detect_environmentlda#$2b; x-ref: $060E |
| $1627 | 85 5a | stazp_ptr_playfield_lo |
| $1629 | a9 00 | lda#$00 |
| $162B | 85 52 | stazp_ptr_screen_lo |
| $162D | 85 54 | stazp_ptr_attr_lo; TEMPF1 Temporary storage for FLPT value. |
| $162F | a9 00 | lda#$00 |
| $1631 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $1633 | 20 9d 1e | jsrinit_dig_slots |
| $1636 | a9 32 | lda#<menu_irq |
| $1638 | 85 90 | stazp_irq_vector_lo; CINV Vector: Hardware Interrupt |
| $163A | a9 06 | lda#>menu_irq |
| $163C | 85 91 | stazp_irq_vector_hi |
| $163E | 20 0d 1c | jsrclear_playfield_buffer |
| $1641 | a2 00 | ldx#$00 |
| $1643 | a9 ff | lda#$ff |
| $1645 | 95 31 | b_1645stazp_key_col_down,x; x-ref: $164C |
| $1647 | 95 28 | stazp_key_row_down,x; TXTTAB Pointer: Start of BASIC Text |
| $1649 | e8 | inx |
| $164A | e0 10 | cpx#$10 |
| $164C | d0 f7 | bneb_1645 |
| $164E | a9 10 | lda#$10 |
| $1650 | 8d 23 16 | stairq_counter |
| $1653 | a9 00 | lda#$00 |
| $1655 | 8d 24 16 | statitle_state |
| $1658 | ad 28 80 | ldaSCREEN_RAM_R1C0 |
| $165B | c9 2a | cmp#$2a |
| $165D | f0 05 | beqb_1664 |
| $165F | a9 50 | lda#80; 40 columns |
| $1661 | 4c 66 16 | jmpj_1666 |
| $1664 | a9 28 | b_1664lda#40; 80 columns ; x-ref: $165D |
| $1666 | 85 51 | j_1666stazp_screen_width; x-ref: $1661 |
| $1668 | 20 43 08 | jsrpatch_80_column_compatibility |
| $166B | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $166C | ce 23 16 | decirq_counter; x-ref: $0635 |
| $166F | d0 16 | bner_1687 |
| $1671 | a9 01 | lda#$01 |
| $1673 | 8d 23 16 | stairq_counter |
| $1676 | 20 b3 1e | jsrprocess_dig_animations |
| $1679 | ad 24 16 | ldatitle_state |
| $167C | d0 0a | bneb_1688 |
| $167E | ee 24 16 | inctitle_state |
| $1681 | 20 80 07 | jsrclear_physical_screen |
| $1684 | 20 9d 07 | jsrclear_gameplay_grid |
| $1687 | 60 | r_1687rts; x-ref: $166F |
| $1688 | c9 01 | b_1688cmp#$01; x-ref: $167C |
| $168A | d0 2a | bneb_16B6 |
| $168C | ee 24 16 | inctitle_state |
| $168F | a2 00 | ldx#$00 |
| $1691 | a0 07 | ldy#$07 |
| $1693 | bd 8f 14 | j_1693ldatxt_lode_runner_pet,x; x-ref: $169F |
| $1696 | c9 ff | cmp#$ff |
| $1698 | f0 08 | beqb_16A2 |
| $169A | 99 00 67 | staplayfield_row_buffer,y |
| $169D | c8 | iny |
| $169E | e8 | inx |
| $169F | 4c 93 16 | jmpj_1693 |
| $16A2 | a2 00 | b_16A2ldx#$00; x-ref: $1698 |
| $16A4 | a0 07 | ldy#$07 |
| $16A6 | bd ac 14 | j_16A6ldatxt_horizontal_bar,x; x-ref: $16B2 |
| $16A9 | c9 ff | cmp#$ff |
| $16AB | f0 08 | beqr_16B5 |
| $16AD | 99 00 68 | staplayfield_row_1_page_base,y; Playfield Row 1 page base |
| $16B0 | c8 | iny |
| $16B1 | e8 | inx |
| $16B2 | 4c a6 16 | jmpj_16A6 |
| $16B5 | 60 | r_16B5rts; x-ref: $16AB, $16EF |
| $16B6 | c9 02 | b_16B6cmp#$02; x-ref: $168A |
| $16B8 | d0 17 | bneb_16D1 |
| $16BA | ee 24 16 | inctitle_state |
| $16BD | a2 00 | ldx#$00 |
| $16BF | a0 0a | ldy#$0a |
| $16C1 | bd c9 14 | j_16C1ldatxt_jim_orlando_gmail,x; x-ref: $16CD |
| $16C4 | c9 ff | cmp#$ff |
| $16C6 | f0 08 | beqr_16D0 |
| $16C8 | 99 00 6a | staplayfield_row_3_page_base,y; Playfield Row 3 page base |
| $16CB | c8 | iny |
| $16CC | e8 | inx |
| $16CD | 4c c1 16 | jmpj_16C1 |
| $16D0 | 60 | r_16D0rts; x-ref: $16C6 |
| $16D1 | c9 03 | b_16D1cmp#$03; x-ref: $16B8 |
| $16D3 | d0 22 | bneb_16F7 |
| $16D5 | ee 24 16 | inctitle_state |
| $16D8 | a2 00 | ldx#$00 |
| $16DA | a0 10 | ldy#$10 |
| $16DC | bd df 14 | j_16DCldatxt_40_columns,x; x-ref: $16E8 |
| $16DF | c9 ff | cmp#$ff |
| $16E1 | f0 08 | beqb_16EB |
| $16E3 | 99 00 6d | staplayfield_row_6_page_base,y; Playfield Row 6 page base |
| $16E6 | c8 | iny |
| $16E7 | e8 | inx |
| $16E8 | 4c dc 16 | jmpj_16DC |
| $16EB | a5 51 | b_16EBldazp_screen_width; x-ref: $16E1 |
| $16ED | c9 28 | cmp#$28 |
| $16EF | f0 c4 | beqr_16B5 |
| $16F1 | a9 38 | lda#$38 |
| $16F3 | 8d 10 6d | staplayfield_row_6_col_16; Set columns digit to '8' for 80-column mode |
| $16F6 | 60 | rts |
| $16F7 | c9 04 | b_16F7cmp#$04; x-ref: $16D3 |
| $16F9 | d0 56 | bneb_1751 |
| $16FB | ee 24 16 | inctitle_state |
| $16FE | a2 00 | ldx#$00 |
| $1700 | a0 02 | ldy#$02 |
| $1702 | bd ea 14 | j_1702ldatxt_select_move_right,x; x-ref: $170E |
| $1705 | c9 ff | cmp#$ff |
| $1707 | f0 08 | beqb_1711 |
| $1709 | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $170C | c8 | iny |
| $170D | e8 | inx |
| $170E | 4c 02 17 | jmpj_1702 |
| $1711 | a2 00 | b_1711ldx#$00; x-ref: $1707 |
| $1713 | a0 01 | ldy#$01 |
| $1715 | bd 0b 15 | j_1715ldaf_150B,x; x-ref: $1721 |
| $1718 | c9 ff | cmp#$ff |
| $171A | f0 08 | beqb_1724 |
| $171C | 99 00 7d | staplayfield_row_22_page_base,y; Playfield Row 22 page base |
| $171F | c8 | iny |
| $1720 | e8 | inx |
| $1721 | 4c 15 17 | jmpj_1715 |
| $1724 | a2 00 | b_1724ldx#$00; x-ref: $171A |
| $1726 | a0 01 | ldy#$01 |
| $1728 | bd 34 15 | j_1728ldaf_1534,x; x-ref: $1734 |
| $172B | c9 ff | cmp#$ff |
| $172D | f0 08 | beqb_1737 |
| $172F | 99 00 7e | staplayfield_row_23_page_base,y; Playfield Row 23 page base |
| $1732 | c8 | iny |
| $1733 | e8 | inx |
| $1734 | 4c 28 17 | jmpj_1728 |
| $1737 | a9 08 | b_1737lda#$08; x-ref: $172D |
| $1739 | 8d b3 0d | staanim_frame |
| $173C | a9 09 | lda#$09 |
| $173E | 8d b4 0d | stadraw_offset |
| $1741 | a9 00 | lda#$00 |
| $1743 | 8d b6 0d | staanim_strip_base |
| $1746 | a9 7b | lda#$7b |
| $1748 | 8d b5 0d | stadraw_page |
| $174B | a9 00 | lda#$00 |
| $174D | 8d b1 0d | staplayer_state |
| $1750 | 60 | rts |
| $1751 | c9 05 | b_1751cmp#$05; x-ref: $16F9 |
| $1753 | d0 13 | bneb_1768 |
| $1755 | 20 1b 1a | jsrmap_new_key |
| $1758 | d0 0d | bner_1767 |
| $175A | ee 24 16 | inctitle_state |
| $175D | a9 01 | lda#$01 |
| $175F | 8d b8 0d | stainput_dy |
| $1762 | a9 00 | lda#$00 |
| $1764 | 8d b6 0d | staanim_strip_base |
| $1767 | 60 | r_1767rts; x-ref: $1758 |
| $1768 | c9 06 | b_1768cmp#$06; x-ref: $1753 |
| $176A | d0 1f | bneb_178B |
| $176C | ad b4 0d | ldadraw_offset |
| $176F | c9 18 | cmp#$18 |
| $1771 | 90 17 | bccr_178A |
| $1773 | a9 18 | lda#$18 |
| $1775 | 8d b4 0d | stadraw_offset |
| $1778 | a9 00 | lda#$00 |
| $177A | 8d b6 0d | staanim_strip_base |
| $177D | a9 04 | lda#$04 |
| $177F | 8d b3 0d | staanim_frame |
| $1782 | a9 00 | lda#$00 |
| $1784 | 8d b8 0d | stainput_dy |
| $1787 | ee 24 16 | inctitle_state |
| $178A | 60 | r_178Arts; x-ref: $1771 |
| $178B | c9 07 | b_178Bcmp#$07; x-ref: $176A |
| $178D | d0 13 | bneb_17A2 |
| $178F | 20 65 1a | jsrwait_any_key |
| $1792 | f0 0d | beqr_17A1 |
| $1794 | ee 24 16 | inctitle_state |
| $1797 | a9 00 | lda#$00 |
| $1799 | 8d b3 0d | staanim_frame |
| $179C | a9 10 | lda#$10 |
| $179E | 8d b6 0d | staanim_strip_base |
| $17A1 | 60 | r_17A1rts; x-ref: $1792 |
| $17A2 | c9 08 | b_17A2cmp#$08; x-ref: $178D |
| $17A4 | d0 17 | bneb_17BD |
| $17A6 | ee 24 16 | inctitle_state |
| $17A9 | a2 00 | ldx#$00 |
| $17AB | a0 02 | ldy#$02 |
| $17AD | bd 5d 15 | j_17ADldatxt_select_move_left,x; x-ref: $17B9 |
| $17B0 | c9 ff | cmp#$ff |
| $17B2 | f0 08 | beqr_17BC |
| $17B4 | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $17B7 | c8 | iny |
| $17B8 | e8 | inx |
| $17B9 | 4c ad 17 | jmpj_17AD |
| $17BC | 60 | r_17BCrts; x-ref: $17B2 |
| $17BD | c9 09 | b_17BDcmp#$09; x-ref: $17A4 |
| $17BF | d0 13 | bneb_17D4 |
| $17C1 | 20 1b 1a | jsrmap_new_key |
| $17C4 | d0 0d | bner_17D3 |
| $17C6 | ee 24 16 | inctitle_state |
| $17C9 | a9 10 | lda#$10 |
| $17CB | 8d b6 0d | staanim_strip_base |
| $17CE | a9 ff | lda#$ff |
| $17D0 | 8d b8 0d | stainput_dy |
| $17D3 | 60 | r_17D3rts; x-ref: $17C4 |
| $17D4 | c9 0a | b_17D4cmp#$0a; x-ref: $17BF |
| $17D6 | d0 1a | bneb_17F2 |
| $17D8 | ad b4 0d | ldadraw_offset |
| $17DB | c9 08 | cmp#$08 |
| $17DD | b0 12 | bcsr_17F1 |
| $17DF | a9 07 | lda#$07 |
| $17E1 | 8d b4 0d | stadraw_offset |
| $17E4 | a9 04 | lda#$04 |
| $17E6 | 8d b3 0d | staanim_frame |
| $17E9 | a9 00 | lda#$00 |
| $17EB | 8d b8 0d | stainput_dy |
| $17EE | ee 24 16 | inctitle_state |
| $17F1 | 60 | r_17F1rts; x-ref: $17DD |
| $17F2 | c9 0b | b_17F2cmp#$0b; x-ref: $17D6 |
| $17F4 | d0 1b | bneb_1811 |
| $17F6 | 20 65 1a | jsrwait_any_key |
| $17F9 | f0 15 | beqr_1810 |
| $17FB | ee 24 16 | inctitle_state |
| $17FE | a9 08 | lda#$08 |
| $1800 | 8d b3 0d | staanim_frame |
| $1803 | a9 10 | lda#$10 |
| $1805 | 8d b6 0d | staanim_strip_base |
| $1808 | a9 01 | lda#$01 |
| $180A | 85 56 | stazp_ptr_render_buf_lo |
| $180C | a9 7c | lda#$7c |
| $180E | 85 57 | stazp_ptr_render_buf_hi |
| $1810 | 60 | r_1810rts; x-ref: $17F9 |
| $1811 | c9 0c | b_1811cmp#$0c; x-ref: $17F4 |
| $1813 | d0 30 | bneb_1845 |
| $1815 | ee 24 16 | inctitle_state |
| $1818 | a2 00 | ldx#$00 |
| $181A | a0 02 | ldy#$02 |
| $181C | bd 7e 15 | j_181Cldatxt_select_move_up,x; x-ref: $1828 |
| $181F | c9 ff | cmp#$ff |
| $1821 | f0 08 | beqb_182B |
| $1823 | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $1826 | c8 | iny |
| $1827 | e8 | inx |
| $1828 | 4c 1c 18 | jmpj_181C |
| $182B | a2 07 | b_182Bldx#$07; x-ref: $1821 |
| $182D | a0 05 | b_182Dldy#$05; x-ref: $1842 |
| $182F | a9 67 | lda#$67 |
| $1831 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1833 | c8 | iny |
| $1834 | a9 40 | lda#$40 |
| $1836 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $1838 | c8 | iny |
| $1839 | a9 65 | lda#$65 |
| $183B | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $183D | 88 | dey |
| $183E | 88 | dey |
| $183F | c6 57 | deczp_ptr_render_buf_hi |
| $1841 | ca | dex |
| $1842 | 10 e9 | bplb_182D |
| $1844 | 60 | rts |
| $1845 | c9 0d | b_1845cmp#$0d; x-ref: $1813 |
| $1847 | d0 13 | bneb_185C |
| $1849 | 20 1b 1a | jsrmap_new_key |
| $184C | d0 0d | bner_185B |
| $184E | ee 24 16 | inctitle_state |
| $1851 | a9 ff | lda#$ff |
| $1853 | 8d b7 0d | stainput_dx |
| $1856 | a9 20 | lda#$20 |
| $1858 | 8d b6 0d | staanim_strip_base |
| $185B | 60 | r_185Brts; x-ref: $184C |
| $185C | c9 0e | b_185Ccmp#$0e; x-ref: $1847 |
| $185E | d0 10 | bneb_1870 |
| $1860 | ad b5 0d | ldadraw_page |
| $1863 | c9 74 | cmp#$74 |
| $1865 | b0 08 | bcsr_186F |
| $1867 | a9 00 | lda#$00 |
| $1869 | 8d b7 0d | stainput_dx |
| $186C | ee 24 16 | inctitle_state |
| $186F | 60 | r_186Frts; x-ref: $1865 |
| $1870 | c9 0f | b_1870cmp#$0f; x-ref: $185E |
| $1872 | d0 0e | bneb_1882 |
| $1874 | 20 65 1a | jsrwait_any_key |
| $1877 | f0 08 | beqr_1881 |
| $1879 | a9 04 | lda#$04 |
| $187B | 8d b3 0d | staanim_frame |
| $187E | ee 24 16 | inctitle_state |
| $1881 | 60 | r_1881rts; x-ref: $1877 |
| $1882 | c9 10 | b_1882cmp#$10; x-ref: $1872 |
| $1884 | d0 17 | bneb_189D |
| $1886 | ee 24 16 | inctitle_state |
| $1889 | a2 00 | ldx#$00 |
| $188B | a0 02 | ldy#$02 |
| $188D | bd 9f 15 | j_188Dldatxt_select_move_down,x; x-ref: $1899 |
| $1890 | c9 ff | cmp#$ff |
| $1892 | f0 08 | beqr_189C |
| $1894 | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $1897 | c8 | iny |
| $1898 | e8 | inx |
| $1899 | 4c 8d 18 | jmpj_188D |
| $189C | 60 | r_189Crts; x-ref: $1892 |
| $189D | c9 11 | b_189Dcmp#$11; x-ref: $1884 |
| $189F | d0 13 | bneb_18B4 |
| $18A1 | 20 1b 1a | jsrmap_new_key |
| $18A4 | d0 0d | bner_18B3 |
| $18A6 | ee 24 16 | inctitle_state |
| $18A9 | a9 01 | lda#$01 |
| $18AB | 8d b7 0d | stainput_dx |
| $18AE | a9 20 | lda#$20 |
| $18B0 | 8d b6 0d | staanim_strip_base |
| $18B3 | 60 | r_18B3rts; x-ref: $18A4 |
| $18B4 | c9 12 | b_18B4cmp#$12; x-ref: $189F |
| $18B6 | d0 10 | bneb_18C8 |
| $18B8 | ad b5 0d | ldadraw_page |
| $18BB | c9 7b | cmp#$7b |
| $18BD | 90 08 | bccr_18C7 |
| $18BF | a9 00 | lda#$00 |
| $18C1 | 8d b7 0d | stainput_dx |
| $18C4 | ee 24 16 | inctitle_state |
| $18C7 | 60 | r_18C7rts; x-ref: $18BD |
| $18C8 | c9 13 | b_18C8cmp#$13; x-ref: $18B6 |
| $18CA | d0 29 | bneb_18F5 |
| $18CC | 20 65 1a | jsrwait_any_key |
| $18CF | f0 23 | beqr_18F4 |
| $18D1 | 20 3c 11 | jsrrestore_sprite_bg |
| $18D4 | a9 01 | lda#$01 |
| $18D6 | 85 56 | stazp_ptr_render_buf_lo |
| $18D8 | a9 7c | lda#$7c |
| $18DA | 85 57 | stazp_ptr_render_buf_hi |
| $18DC | a2 07 | ldx#$07 |
| $18DE | a0 05 | b_18DEldy#$05; x-ref: $18EF |
| $18E0 | a9 20 | lda#$20 |
| $18E2 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $18E4 | c8 | iny |
| $18E5 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $18E7 | c8 | iny |
| $18E8 | 91 56 | sta(zp_ptr_render_buf_lo),y |
| $18EA | 88 | dey |
| $18EB | 88 | dey |
| $18EC | c6 57 | deczp_ptr_render_buf_hi |
| $18EE | ca | dex |
| $18EF | 10 ed | bplb_18DE |
| $18F1 | ee 24 16 | inctitle_state |
| $18F4 | 60 | r_18F4rts; x-ref: $18CF |
| $18F5 | c9 14 | b_18F5cmp#$14; x-ref: $18CA |
| $18F7 | d0 04 | bneb_18FD |
| $18F9 | ee 24 16 | inctitle_state |
| $18FC | 60 | rts |
| $18FD | c9 15 | b_18FDcmp#$15; x-ref: $18F7 |
| $18FF | d0 17 | bneb_1918 |
| $1901 | a2 00 | ldx#$00 |
| $1903 | a0 02 | ldy#$02 |
| $1905 | bd c0 15 | j_1905ldatxt_select_dig_right,x; x-ref: $1911 |
| $1908 | c9 ff | cmp#$ff |
| $190A | f0 08 | beqb_1914 |
| $190C | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $190F | c8 | iny |
| $1910 | e8 | inx |
| $1911 | 4c 05 19 | jmpj_1905 |
| $1914 | ee 24 16 | b_1914inctitle_state; x-ref: $190A |
| $1917 | 60 | rts |
| $1918 | c9 16 | b_1918cmp#$16; x-ref: $18FF |
| $191A | d0 27 | bneb_1943 |
| $191C | 20 1b 1a | jsrmap_new_key |
| $191F | d0 21 | bner_1942 |
| $1921 | a9 00 | lda#$00 |
| $1923 | 8d 43 1e | stadig_slot_status |
| $1926 | a9 7d | lda#$7d |
| $1928 | 8d 4d 1e | stadig_slot_page |
| $192B | a9 08 | lda#$08 |
| $192D | 8d 57 1e | stadig_slot_col |
| $1930 | a9 00 | lda#$00 |
| $1932 | 8d b3 0d | staanim_frame |
| $1935 | a9 01 | lda#$01 |
| $1937 | 8d b1 0d | staplayer_state |
| $193A | a9 70 | lda#$70 |
| $193C | 8d b6 0d | staanim_strip_base |
| $193F | ee 24 16 | inctitle_state |
| $1942 | 60 | r_1942rts; x-ref: $191F |
| $1943 | c9 17 | b_1943cmp#$17; x-ref: $191A |
| $1945 | d0 09 | bneb_1950 |
| $1947 | 20 65 1a | jsrwait_any_key |
| $194A | f0 03 | beqr_194F |
| $194C | ee 24 16 | inctitle_state |
| $194F | 60 | r_194Frts; x-ref: $194A |
| $1950 | c9 18 | b_1950cmp#$18; x-ref: $1945 |
| $1952 | d0 17 | bneb_196B |
| $1954 | a2 00 | ldx#$00 |
| $1956 | a0 02 | ldy#$02 |
| $1958 | bd e1 15 | j_1958ldatxt_select_dig_left,x; x-ref: $1964 |
| $195B | c9 ff | cmp#$ff |
| $195D | f0 08 | beqb_1967 |
| $195F | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $1962 | c8 | iny |
| $1963 | e8 | inx |
| $1964 | 4c 58 19 | jmpj_1958 |
| $1967 | ee 24 16 | b_1967inctitle_state; x-ref: $195D |
| $196A | 60 | rts |
| $196B | c9 19 | b_196Bcmp#$19; x-ref: $1952 |
| $196D | d0 31 | bneb_19A0 |
| $196F | 20 1b 1a | jsrmap_new_key |
| $1972 | d0 2b | bner_199F |
| $1974 | a9 00 | lda#$00 |
| $1976 | 8d 44 1e | stakbind_anim_step; overlaps dig_slot_status[1]; reused as wizard animation step counter (0..2) during key binding |
| $1979 | a9 7d | lda#$7d |
| $197B | 8d 4e 1e | stakbind_sprite_page; overlaps dig_slot_page[1]; reused as player sprite page ($7D) during key binding |
| $197E | a9 06 | lda#$06 |
| $1980 | 8d 58 1e | stakbind_sprite_col; overlaps dig_slot_col[1]; reused as player sprite column ($06) during key binding |
| $1983 | a9 00 | lda#$00 |
| $1985 | 8d b3 0d | staanim_frame |
| $1988 | a9 01 | lda#$01 |
| $198A | 8d b1 0d | staplayer_state |
| $198D | a9 80 | lda#$80 |
| $198F | 8d b6 0d | staanim_strip_base |
| $1992 | a9 01 | lda#$01 |
| $1994 | 8d 6b 1e | stadig_slot_delay_inner |
| $1997 | a9 00 | lda#$00 |
| $1999 | 8d 75 1e | stadig_slot_delay_outer |
| $199C | ee 24 16 | inctitle_state |
| $199F | 60 | r_199Frts; x-ref: $1972 |
| $19A0 | c9 1a | b_19A0cmp#$1a; x-ref: $196D |
| $19A2 | d0 10 | bneb_19B4 |
| $19A4 | 20 65 1a | jsrwait_any_key |
| $19A7 | f0 0a | beqr_19B3 |
| $19A9 | ad 44 1e | ldakbind_anim_step; overlaps dig_slot_status[1]; reused as wizard animation step counter (0..2) during key binding |
| $19AC | c9 02 | cmp#$02 |
| $19AE | d0 03 | bner_19B3 |
| $19B0 | ee 24 16 | inctitle_state |
| $19B3 | 60 | r_19B3rts; x-ref: $19A7, $19AE |
| $19B4 | c9 1b | b_19B4cmp#$1b; x-ref: $19A2 |
| $19B6 | d0 21 | bneb_19D9 |
| $19B8 | a2 00 | ldx#$00 |
| $19BA | a0 02 | ldy#$02 |
| $19BC | bd 02 16 | j_19BCldatxt_select_pause,x; x-ref: $19C8 |
| $19BF | c9 ff | cmp#$ff |
| $19C1 | f0 08 | beqb_19CB |
| $19C3 | 99 00 71 | staplayfield_row_10_page_base,y; Playfield Row 10 page base |
| $19C6 | c8 | iny |
| $19C7 | e8 | inx |
| $19C8 | 4c bc 19 | jmpj_19BC |
| $19CB | a9 01 | b_19CBlda#$01; x-ref: $19C1 |
| $19CD | 8d b8 0d | stainput_dy |
| $19D0 | a9 00 | lda#$00 |
| $19D2 | 8d b6 0d | staanim_strip_base |
| $19D5 | ee 24 16 | inctitle_state |
| $19D8 | 60 | rts |
| $19D9 | c9 1c | b_19D9cmp#$1c; x-ref: $19B6 |
| $19DB | d0 26 | bneb_1A03 |
| $19DD | ad b4 0d | ldadraw_offset |
| $19E0 | c9 20 | cmp#$20 |
| $19E2 | f0 0c | beqb_19F0 |
| $19E4 | c9 07 | cmp#$07 |
| $19E6 | d0 0d | bneb_19F5 |
| $19E8 | a9 01 | lda#$01 |
| $19EA | 8d b8 0d | stainput_dy |
| $19ED | 4c f5 19 | jmpb_19F5 |
| $19F0 | a9 ff | b_19F0lda#$ff; x-ref: $19E2 |
| $19F2 | 8d b8 0d | stainput_dy |
| $19F5 | 20 1b 1a | b_19F5jsrmap_new_key; x-ref: $19E6, $19ED |
| $19F8 | d0 08 | bner_1A02 |
| $19FA | a9 00 | lda#$00 |
| $19FC | 8d b8 0d | stainput_dy |
| $19FF | ee 24 16 | inctitle_state |
| $1A02 | 60 | r_1A02rts; x-ref: $19F8 |
| $1A03 | c9 30 | b_1A03cmp#$30; x-ref: $19DB |
| $1A05 | f0 04 | beqb_1A0B |
| $1A07 | ee 24 16 | inctitle_state |
| $1A0A | 60 | rts |
| $1A0B | 20 7c 1a | b_1A0Bjsrfinalize_key_bindings; x-ref: $1A05 |
| $1A0E | 20 3c 11 | jsrrestore_sprite_bg |
| $1A11 | a9 ff | lda#$ff |
| $1A13 | 8d b1 0d | staplayer_state |
| $1A16 | 20 bf 30 | jsrswitch_to_title_screen |
| $1A19 | 60 | rts |
| $1A1A | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1A1B | a2 00 | map_new_keyldx#$00; x-ref: $1755, $17C1, $1849, $18A1, $191C, $196F, $19F5 |
| $1A1D | 8e 10 e8 | b_1A1Dstx$e810; x-ref: $1A2A PORT A or DDR A: Data Direction Register A |
| $1A20 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $1A23 | c9 ff | cmp#$ff |
| $1A25 | d0 08 | bneb_1A2F |
| $1A27 | e8 | inx |
| $1A28 | e0 0a | cpx#$0a |
| $1A2A | d0 f1 | bneb_1A1D |
| $1A2C | a9 ff | lda#$ff |
| $1A2E | 60 | rts |
| $1A2F | a0 00 | b_1A2Fldy#$00; x-ref: $1A25 |
| $1A31 | d9 31 00 | b_1A31cmp@w zp_key_col_down,y; x-ref: $1A45 |
| $1A34 | d0 0c | bneb_1A42 |
| $1A36 | 48 | pha |
| $1A37 | 8a | txa |
| $1A38 | d9 28 00 | cmp@w zp_key_row_down,y; TXTTAB Pointer: Start of BASIC Text |
| $1A3B | d0 04 | bneb_1A41 |
| $1A3D | 68 | pla |
| $1A3E | a9 ff | lda#$ff |
| $1A40 | 60 | rts |
| $1A41 | 68 | b_1A41pla; x-ref: $1A3B |
| $1A42 | c8 | b_1A42iny; x-ref: $1A34 |
| $1A43 | c0 08 | cpy#$08 |
| $1A45 | d0 ea | bneb_1A31 |
| $1A47 | 48 | pha |
| $1A48 | a0 00 | ldy#$00 |
| $1A4A | b9 31 00 | b_1A4Alda@w zp_key_col_down,y; x-ref: $1A54 |
| $1A4D | c9 ff | cmp#$ff |
| $1A4F | f0 09 | beqb_1A5A |
| $1A51 | c8 | iny |
| $1A52 | c0 08 | cpy#$08 |
| $1A54 | d0 f4 | bneb_1A4A |
| $1A56 | 68 | pla |
| $1A57 | a9 ff | lda#$ff |
| $1A59 | 60 | rts |
| $1A5A | 68 | b_1A5Apla; x-ref: $1A4F |
| $1A5B | 99 31 00 | sta@w zp_key_col_down,y |
| $1A5E | 8a | txa |
| $1A5F | 99 28 00 | sta@w zp_key_row_down,y; TXTTAB Pointer: Start of BASIC Text |
| $1A62 | a9 00 | lda#$00 |
| $1A64 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1A65 | a2 00 | wait_any_keyldx#$00; x-ref: $178F, $17F6, $1874, $18CC, $1947, $19A4, $2FAD, $3173, ... |
| $1A67 | 8e 10 e8 | b_1A67stx$e810; x-ref: $1A74 PORT A or DDR A: Data Direction Register A |
| $1A6A | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $1A6D | c9 ff | cmp#$ff |
| $1A6F | d0 08 | bneb_1A79 |
| $1A71 | e8 | inx |
| $1A72 | e0 0a | cpx#$0a |
| $1A74 | d0 f1 | bneb_1A67 |
| $1A76 | a9 ff | lda#$ff |
| $1A78 | 60 | rts |
| $1A79 | a9 00 | b_1A79lda#$00; x-ref: $1A6F |
| $1A7B | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $1A7C | a0 00 | ldy#$00; x-ref: $1A0B |
| $1A7E | b9 31 00 | b_1A7Elda@w zp_key_col_down,y; x-ref: $1A89 |
| $1A81 | 49 ff | eor#$ff |
| $1A83 | 99 31 00 | sta@w zp_key_col_down,y |
| $1A86 | c8 | iny |
| $1A87 | c0 09 | cpy#$09 |
| $1A89 | d0 f3 | bneb_1A7E |
| $1A8B | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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) |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1A8C | a9 2b | build_movement_gridlda#<playfield_row_0_offset_2b; --- PHASE 1: zero the entire movement grid --- ; x-ref: $12B2 |
| $1A8E | 85 58 | stazp_ptr_wipe_buf_lo; grid pointer low = $2B |
| $1A90 | a9 67 | lda#>playfield_row_0_offset_2b; grid pointer high = $67 (first row) |
| $1A92 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $1A94 | a9 00 | b_1A94lda#$00; fill value = $00 ; x-ref: $1AA3 |
| $1A96 | a0 29 | ldy#$29; 42 bytes per row (cols 0..$29) |
| $1A98 | 91 58 | b_1A98sta(zp_ptr_wipe_buf_lo),y; x-ref: $1A9B |
| $1A9A | 88 | dey |
| $1A9B | 10 fb | bplb_1A98 |
| $1A9D | e6 59 | inczp_ptr_wipe_buf_hi; advance to next row; TEMPF2 Temporary storage for FLPT value. |
| $1A9F | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $1AA1 | c9 80 | cmp#$80; stop when high byte reaches $80 (end of grid) |
| $1AA3 | d0 ef | bneb_1A94 |
| $1AA5 | a9 2b | lda#<playfield_row_1_offset_2b; --- PHASE 2: build property bits from visual tiles --- |
| $1AA7 | 85 58 | stazp_ptr_wipe_buf_lo |
| $1AA9 | a9 68 | lda#>playfield_row_1_offset_2b |
| $1AAB | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $1AAD | a9 00 | lda#<playfield_row_1_page_base; screen tile buffer low = $00 |
| $1AAF | 85 56 | stazp_ptr_render_buf_lo; screen tile buffer high = $68 (row 1) |
| $1AB1 | a9 68 | lda#>playfield_row_1_page_base |
| $1AB3 | 85 57 | stazp_ptr_render_buf_hi |
| $1AB5 | a9 00 | lda#$00 |
| $1AB7 | a0 01 | j_1AB7ldy#$01; start at col 1 (skip left border) ; x-ref: $1C09 |
| $1AB9 | a5 57 | j_1AB9ldazp_ptr_render_buf_hi; --- per-tile loop (rows $68-$7F, cols 1-$20) --- ; x-ref: $1BFC |
| $1ABB | c9 7f | cmp#$7f; last row ($7F)? walk bits don't apply |
| $1ABD | f0 27 | beqb_1AE6 |
| $1ABF | b1 56 | lda(zp_ptr_render_buf_lo),y; rope/ladder tile? can't walk from it |
| $1AC1 | c9 63 | cmp#$63 |
| $1AC3 | f0 21 | beqb_1AE6 |
| $1AC5 | c9 43 | cmp#$43 |
| $1AC7 | f0 1d | beqb_1AE6 |
| $1AC9 | e6 57 | inczp_ptr_render_buf_hi; peek at tile one row below |
| $1ACB | b1 56 | lda(zp_ptr_render_buf_lo),y; restore row pointer |
| $1ACD | c6 57 | deczp_ptr_render_buf_hi; wall/solid/ladder below? floor confirmed → check walk bits |
| $1ACF | c9 dd | cmp#$dd |
| $1AD1 | f0 13 | beqb_1AE6 |
| $1AD3 | c9 cc | cmp#$cc |
| $1AD5 | f0 0f | beqb_1AE6 |
| $1AD7 | c9 db | cmp#$db |
| $1AD9 | f0 0b | beqb_1AE6 |
| $1ADB | c9 a0 | cmp#$a0 |
| $1ADD | f0 07 | beqb_1AE6 |
| $1ADF | c9 43 | cmp#$43 |
| $1AE1 | f0 03 | beqb_1AE6 |
| $1AE3 | 4c 22 1b | jmpj_1B22 |
| $1AE6 | c0 01 | b_1AE6cpy#$01; --- bit $01: walk-left --- floor confirmed; at col 1 (left border)? skip ; x-ref: $1ABD, $1AC3, $1AC7, $1AD1, $1AD5, $1AD9, $1ADD, $1AE1 |
| $1AE8 | f0 1a | beqb_1B04 |
| $1AEA | 88 | dey; peek at left neighbour |
| $1AEB | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1AED | c8 | iny; restore Y |
| $1AEE | c9 dd | cmp#$dd |
| $1AF0 | f0 12 | beqb_1B04 |
| $1AF2 | c9 cc | cmp#$cc |
| $1AF4 | f0 0e | beqb_1B04 |
| $1AF6 | c9 db | cmp#$db; left neighbour is wall/solid → can't walk left |
| $1AF8 | f0 0a | beqb_1B04 |
| $1AFA | c9 a0 | cmp#$a0 |
| $1AFC | f0 06 | beqb_1B04 |
| $1AFE | b1 58 | lda(zp_ptr_wipe_buf_lo),y; set bit $01 (walk-left accessible) |
| $1B00 | 09 01 | ora#$01 |
| $1B02 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1B04 | c0 20 | b_1B04cpy#$20; --- bit $02: walk-right --- at col $20 (right border)? skip ; x-ref: $1AE8, $1AF0, $1AF4, $1AF8, $1AFC |
| $1B06 | f0 1a | beqj_1B22 |
| $1B08 | c8 | iny; peek at right neighbour |
| $1B09 | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1B0B | 88 | dey; restore Y |
| $1B0C | c9 dd | cmp#$dd |
| $1B0E | f0 12 | beqj_1B22 |
| $1B10 | c9 cc | cmp#$cc |
| $1B12 | f0 0e | beqj_1B22 |
| $1B14 | c9 db | cmp#$db; set bit $02 (walk-right accessible) |
| $1B16 | f0 0a | beqj_1B22 |
| $1B18 | c9 a0 | cmp#$a0 |
| $1B1A | f0 06 | beqj_1B22 |
| $1B1C | b1 58 | lda(zp_ptr_wipe_buf_lo),y |
| $1B1E | 09 02 | ora#$02 |
| $1B20 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1B22 | b1 56 | j_1B22lda(zp_ptr_render_buf_lo),y; --- bit $04: climb-up --- tile is $43 (ladder)? ; x-ref: $1AE3, $1B06, $1B0E, $1B12, $1B16, $1B1A |
| $1B24 | c9 43 | cmp#$43 |
| $1B26 | d0 43 | bneb_1B6B |
| $1B28 | a5 57 | ldazp_ptr_render_buf_hi; first data row ($68)? can't climb higher → skip |
| $1B2A | c9 68 | cmp#$68 |
| $1B2C | f0 1c | beqb_1B4A |
| $1B2E | c6 57 | deczp_ptr_render_buf_hi; peek 2 rows above (entity is 2 tiles tall) |
| $1B30 | c6 57 | deczp_ptr_render_buf_hi |
| $1B32 | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1B34 | e6 57 | inczp_ptr_render_buf_hi; restore row pointer |
| $1B36 | e6 57 | inczp_ptr_render_buf_hi |
| $1B38 | c9 cc | cmp#$cc; restore row pointer |
| $1B3A | f0 0e | beqb_1B4A |
| $1B3C | c9 a0 | cmp#$a0; wall/solid 2 rows above? head would hit → no climb-up |
| $1B3E | f0 0a | beqb_1B4A |
| $1B40 | c9 db | cmp#$db |
| $1B42 | f0 06 | beqb_1B4A |
| $1B44 | b1 58 | lda(zp_ptr_wipe_buf_lo),y; set bit $04 (can climb up) |
| $1B46 | 09 04 | ora#$04 |
| $1B48 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1B4A | a5 57 | b_1B4Aldazp_ptr_render_buf_hi; --- bit $08: climb-down (ladder) --- last row? can't descend → skip ; x-ref: $1B2C, $1B3A, $1B3E, $1B42 |
| $1B4C | c9 7f | cmp#$7f |
| $1B4E | f0 44 | beqb_1B94 |
| $1B50 | e6 57 | inczp_ptr_render_buf_hi; peek one row below |
| $1B52 | b1 56 | lda(zp_ptr_render_buf_lo),y; restore row pointer |
| $1B54 | c6 57 | deczp_ptr_render_buf_hi |
| $1B56 | c9 cc | cmp#$cc; wall/solid below? ladder descend blocked |
| $1B58 | f0 3a | beqb_1B94 |
| $1B5A | c9 a0 | cmp#$a0 |
| $1B5C | f0 36 | beqb_1B94 |
| $1B5E | c9 db | cmp#$db |
| $1B60 | f0 32 | beqb_1B94 |
| $1B62 | b1 58 | lda(zp_ptr_wipe_buf_lo),y; set bit $08 (can climb down) |
| $1B64 | 09 08 | ora#$08 |
| $1B66 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1B68 | 4c 94 1b | jmpb_1B94 |
| $1B6B | c9 63 | b_1B6Bcmp#$63; --- bit $08: climb-down (rope/ladder-below) --- tile is rope ($63)? ; x-ref: $1B26 |
| $1B6D | f0 0d | beqb_1B7C |
| $1B6F | e6 57 | inczp_ptr_render_buf_hi; not rope: peek row below for ladder ($43) |
| $1B71 | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1B73 | c6 57 | deczp_ptr_render_buf_hi |
| $1B75 | c9 43 | cmp#$43; ladder directly below → can step down onto it |
| $1B77 | f0 15 | beqb_1B8E |
| $1B79 | 4c 94 1b | jmpb_1B94 |
| $1B7C | e6 57 | b_1B7Cinczp_ptr_render_buf_hi; rope tile: peek row below; wall/solid → rope descent blocked ; x-ref: $1B6D |
| $1B7E | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1B80 | c6 57 | deczp_ptr_render_buf_hi |
| $1B82 | c9 cc | cmp#$cc |
| $1B84 | f0 0e | beqb_1B94 |
| $1B86 | c9 a0 | cmp#$a0 |
| $1B88 | f0 0a | beqb_1B94 |
| $1B8A | c9 db | cmp#$db |
| $1B8C | f0 06 | beqb_1B94 |
| $1B8E | b1 58 | b_1B8Elda(zp_ptr_wipe_buf_lo),y; set bit $08 (can climb down from rope, or step onto ladder below) ; x-ref: $1B77 |
| $1B90 | 09 08 | ora#$08 |
| $1B92 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1B94 | b1 56 | b_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, ... |
| $1B96 | c9 63 | cmp#$63 |
| $1B98 | f0 21 | beqb_1BBB |
| $1B9A | c9 43 | cmp#$43 |
| $1B9C | f0 1d | beqb_1BBB |
| $1B9E | a5 57 | ldazp_ptr_render_buf_hi; peek one row below |
| $1BA0 | c9 7f | cmp#$7f |
| $1BA2 | f0 17 | beqb_1BBB |
| $1BA4 | e6 57 | inczp_ptr_render_buf_hi |
| $1BA6 | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1BA8 | c6 57 | deczp_ptr_render_buf_hi; space ($20) or rope ($63) below → entity will fall / descend |
| $1BAA | c9 20 | cmp#$20 |
| $1BAC | f0 07 | beqb_1BB5 |
| $1BAE | c9 63 | cmp#$63 |
| $1BB0 | f0 03 | beqb_1BB5 |
| $1BB2 | 4c bb 1b | jmpb_1BBB |
| $1BB5 | b1 58 | b_1BB5lda(zp_ptr_wipe_buf_lo),y; set bit $10 (can fall through) ; x-ref: $1BAC, $1BB0 |
| $1BB7 | 09 10 | ora#$10 |
| $1BB9 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1BBB | b1 56 | b_1BBBlda(zp_ptr_render_buf_lo),y; --- bit $20: rope --- tile is rope ($63)? ; x-ref: $1B98, $1B9C, $1BA2, $1BB2 |
| $1BBD | c9 63 | cmp#$63 |
| $1BBF | d0 06 | bneb_1BC7 |
| $1BC1 | b1 58 | lda(zp_ptr_wipe_buf_lo),y; set bit $20 (rope tile) |
| $1BC3 | 09 20 | ora#$20 |
| $1BC5 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1BC7 | b1 56 | b_1BC7lda(zp_ptr_render_buf_lo),y; --- bits $40/$80: solid wall --- tile is $CC/$DB/$DD (brick variants)? ; x-ref: $1BBF |
| $1BC9 | c9 cc | cmp#$cc |
| $1BCB | f0 0b | beqb_1BD8 |
| $1BCD | c9 db | cmp#$db |
| $1BCF | f0 07 | beqb_1BD8 |
| $1BD1 | c9 dd | cmp#$dd |
| $1BD3 | f0 03 | beqb_1BD8 |
| $1BD5 | 4c f7 1b | jmpj_1BF7 |
| $1BD8 | a9 40 | b_1BD8lda#$40; STA (not ORA): overwrites all previous bits; wall always wins ; x-ref: $1BCB, $1BCF, $1BD3 |
| $1BDA | 91 58 | sta(zp_ptr_wipe_buf_lo),y; re-read tile to check for full-solid ($CC/$DB/$A0/$DD) |
| $1BDC | b1 56 | lda(zp_ptr_render_buf_lo),y |
| $1BDE | c9 cc | cmp#$cc |
| $1BE0 | f0 0f | beqb_1BF1 |
| $1BE2 | c9 db | cmp#$db |
| $1BE4 | f0 0b | beqb_1BF1 |
| $1BE6 | c9 a0 | cmp#$a0 |
| $1BE8 | f0 07 | beqb_1BF1 |
| $1BEA | c9 dd | cmp#$dd |
| $1BEC | f0 03 | beqb_1BF1 |
| $1BEE | 4c f7 1b | jmpj_1BF7 |
| $1BF1 | b1 58 | b_1BF1lda(zp_ptr_wipe_buf_lo),y; set bit $80 (fully solid wall) ; x-ref: $1BE0, $1BE4, $1BE8, $1BEC |
| $1BF3 | 09 80 | ora#$80 |
| $1BF5 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $1BF7 | c8 | j_1BF7iny; advance to next column ; x-ref: $1BD5, $1BEE |
| $1BF8 | c0 21 | cpy#$21; col $21 = past last data col → end of row |
| $1BFA | f0 03 | beqb_1BFF |
| $1BFC | 4c b9 1a | jmpj_1AB9 |
| $1BFF | e6 59 | b_1BFFinczp_ptr_wipe_buf_hi; advance grid pointer to next row ; x-ref: $1BFA TEMPF2 Temporary storage for FLPT value. |
| $1C01 | e6 57 | inczp_ptr_render_buf_hi |
| $1C03 | a5 57 | ldazp_ptr_render_buf_hi; all rows processed ($80)? done |
| $1C05 | c9 80 | cmp#$80 |
| $1C07 | f0 03 | beqr_1C0C |
| $1C09 | 4c b7 1a | jmpj_1AB7 |
| $1C0C | 60 | r_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 |
| $1C0D | a9 2b | lda#<playfield_row_0_offset_2b; x-ref: $163E, $30F4 |
| $1C0F | 85 5a | stazp_ptr_playfield_lo |
| $1C11 | a9 67 | lda#>playfield_row_0_offset_2b |
| $1C13 | 85 5b | stazp_ptr_playfield_hi |
| $1C15 | a9 0f | b_1C15lda#$0f; x-ref: $1C24 |
| $1C17 | a0 29 | ldy#$29 |
| $1C19 | 91 5a | b_1C19sta(zp_ptr_playfield_lo),y; x-ref: $1C1C |
| $1C1B | 88 | dey |
| $1C1C | 10 fb | bplb_1C19 |
| $1C1E | e6 5b | inczp_ptr_playfield_hi |
| $1C20 | a5 5b | ldazp_ptr_playfield_hi |
| $1C22 | c9 80 | cmp#$80 |
| $1C24 | d0 ef | bneb_1C15 |
| $1C26 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $1C27 | a2 04 | ldx#$04; x-ref: $0665 |
| $1C29 | bd f9 21 | b_1C29ldaguard_active,x; x-ref: $1C6D, $1C76 |
| $1C2C | 30 42 | bmib_1C70 |
| $1C2E | bc 08 22 | ldyguard_page,x |
| $1C31 | c8 | iny |
| $1C32 | 84 5b | styzp_ptr_playfield_hi |
| $1C34 | 84 61 | styzp_ptr_pathfinding_hi |
| $1C36 | bc 03 22 | ldyguard_offset,x |
| $1C39 | b1 60 | lda(zp_ptr_pathfinding_lo),y |
| $1C3B | 29 7f | and#$7f |
| $1C3D | 8d 41 1c | staa_1C41 |
| a_1C41 =*+$01 ; x-ref: $1C3D |
| $1C40 | 10 fe | b_1C40bplb_1C40; x-ref: $1C40 |
| $1C42 | ea | nop |
| $1C43 | ea | nop |
| $1C44 | ea | nop |
| $1C45 | ea | nop |
| $1C46 | ea | nop |
| $1C47 | ea | nop |
| $1C48 | ea | nop |
| $1C49 | ea | nop |
| $1C4A | a9 01 | lda#$01 |
| $1C4C | 9d 12 22 | staguard_vspeed,x |
| $1C4F | 4c 70 1c | jmpb_1C70 |
| $1C52 | a9 ff | lda#$ff |
| $1C54 | 9d 17 22 | staguard_hspeed,x |
| $1C57 | 4c 67 1c | jmpj_1C67 |
| $1C5A | a9 ff | lda#$ff |
| $1C5C | 9d 12 22 | staguard_vspeed,x |
| $1C5F | 4c 70 1c | jmpb_1C70 |
| $1C62 | a9 01 | lda#$01 |
| $1C64 | 9d 17 22 | staguard_hspeed,x |
| $1C67 | a9 00 | j_1C67lda#$00; x-ref: $1C57 |
| $1C69 | 9d 12 22 | staguard_vspeed,x |
| $1C6C | ca | dex |
| $1C6D | 10 ba | bplb_1C29 |
| $1C6F | 60 | rts |
| $1C70 | a9 00 | b_1C70lda#$00; x-ref: $1C2C, $1C4F, $1C5F |
| $1C72 | 9d 17 22 | staguard_hspeed,x |
| $1C75 | ca | dex |
| $1C76 | 10 b1 | bplb_1C29 |
| $1C78 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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) |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1C79 | 86 65 | queue_push_xystxzp_scratch_reg_x; x-ref: $1D10, $1D41, $1D6F, $1D8A, $1DA5, $1DBC BITS Floating -accum. #1: Overflow Digit |
| $1C7B | 8a | txa |
| $1C7C | e6 63 | inczp_queue_tail; FACSGN Floating Accum. #1: Sign |
| $1C7E | a6 63 | ldxzp_queue_tail; FACSGN Floating Accum. #1: Sign |
| $1C80 | 9d 00 65 | staqueue_array_x,x |
| $1C83 | 98 | tya |
| $1C84 | 9d 00 66 | staqueue_array_y,x |
| $1C87 | a6 65 | ldxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit |
| $1C89 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1C8A | e6 62 | queue_shift_pop_xyinczp_queue_head_temp |
| $1C8C | a4 62 | ldyzp_queue_head_temp |
| $1C8E | b9 00 66 | ldaqueue_array_y,y |
| $1C91 | be 00 65 | ldxqueue_array_x,y |
| $1C94 | a8 | tay |
| $1C95 | 86 65 | stxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit |
| $1C97 | 84 66 | styzp_scratch_reg_y; ARGEXP Floating-Point Accumulator #2: Exponent |
| $1C99 | a0 01 | ldy#$01 |
| $1C9B | a2 00 | ldx#$00 |
| $1C9D | b9 00 65 | b_1C9Dldaqueue_array_x,y; x-ref: $1CAD |
| $1CA0 | 9d 00 65 | staqueue_array_x,x |
| $1CA3 | b9 00 66 | ldaqueue_array_y,y |
| $1CA6 | 9d 00 66 | staqueue_array_y,x |
| $1CA9 | e8 | inx |
| $1CAA | c8 | iny |
| $1CAB | e4 63 | cpxzp_queue_tail; FACSGN Floating Accum. #1: Sign |
| $1CAD | d0 ee | bneb_1C9D |
| $1CAF | c6 62 | deczp_queue_head_temp |
| $1CB1 | c6 63 | deczp_queue_tail; FACSGN Floating Accum. #1: Sign |
| $1CB3 | a6 65 | ldxzp_scratch_reg_x; BITS Floating -accum. #1: Overflow Digit |
| $1CB5 | a4 66 | ldyzp_scratch_reg_y; ARGEXP Floating-Point Accumulator #2: Exponent |
| $1CB7 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $1CB8 | a9 55 | lda#$55; x-ref: $0754 |
| $1CBA | 85 5c | stazp_ptr_attr_ram_lo |
| $1CBC | a9 2b | lda#$2b |
| $1CBE | 85 5e | stazp_ptr_move_grid_lo; FACEXP Floating-Point Accumulator #1: Exponent |
| $1CC0 | a9 67 | lda#$67 |
| $1CC2 | 85 5d | stazp_ptr_attr_ram_hi |
| $1CC4 | 85 5f | stazp_ptr_move_grid_hi; FACHO Floating Accum. #1: Mantissa |
| $1CC6 | a9 01 | b_1CC6lda#$01; x-ref: $1CD7 |
| $1CC8 | a0 01 | ldy#$01 |
| $1CCA | 91 5c | b_1CCAsta(zp_ptr_attr_ram_lo),y; x-ref: $1CCF |
| $1CCC | c8 | iny |
| $1CCD | c0 21 | cpy#$21 |
| $1CCF | d0 f9 | bneb_1CCA |
| $1CD1 | e6 5d | inczp_ptr_attr_ram_hi |
| $1CD3 | a5 5d | ldazp_ptr_attr_ram_hi |
| $1CD5 | c9 80 | cmp#$80 |
| $1CD7 | d0 ed | bneb_1CC6 |
| $1CD9 | a9 00 | lda#$00 |
| $1CDB | 85 62 | stazp_queue_head_temp |
| $1CDD | 85 63 | stazp_queue_tail; FACSGN Floating Accum. #1: Sign |
| $1CDF | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $1CE0 | ad 61 1d | ldaa_1D61; read branch opcode at $1D61 to detect current generation ; x-ref: $0623, $0757 |
| $1CE3 | c9 10 | cmp#$10; $10 = BPL = gen A currently active? |
| $1CE5 | d0 31 | bneb_1D18; yes (gen A active) -> switch to gen B |
| $1CE7 | a9 30 | lda#$30; --- switch to gen B: patch branch opcodes $10->$30 (BPL->BMI) --- |
| $1CE9 | 8d 61 1d | staa_1D61; patch BMI opcode at visited-check for above neighbour |
| $1CEC | 8d 7c 1d | staa_1D7C; patch BMI opcode at visited-check for below neighbour |
| $1CEF | 8d 99 1d | staa_1D99; patch BMI opcode at visited-check for right neighbour |
| $1CF2 | 8d b0 1d | staa_1DB0; patch BMI opcode at visited-check for left neighbour |
| $1CF5 | a9 88 | lda#$88; --- patch gen B direction codes (bit 7 set = visited marker) --- |
| $1CF7 | 8d 6c 1d | staa_1D6C; above: $88 = move-down + gen-B visited bit |
| $1CFA | a9 98 | lda#$98; below: $98 = move-up + gen-B visited bit |
| $1CFC | 8d 87 1d | staa_1D87 |
| $1CFF | a9 90 | lda#$90; right: $90 = move-left + gen-B visited bit |
| $1D01 | 8d a2 1d | staa_1DA2 |
| $1D04 | a9 a0 | lda#$a0; left: $A0 = move-right + gen-B visited bit |
| $1D06 | 8d b9 1d | staa_1DB9 |
| $1D09 | ac b4 0d | ldydraw_offset; --- seed BFS at player position --- |
| $1D0C | ae b5 0d | ldxdraw_page; X = draw_page + 1 (attr RAM row for player) |
| $1D0F | e8 | inx |
| $1D10 | 20 79 1c | jsrqueue_push_xy; init attr pointer high byte for BFS loop |
| $1D13 | 86 5d | stxzp_ptr_attr_ram_hi |
| $1D15 | 4c 46 1d | jmpj_1D46 |
| $1D18 | a9 10 | b_1D18lda#$10; --- switch to gen A: patch branch opcodes $30->$10 (BMI->BPL) --- ; x-ref: $1CE5 |
| $1D1A | 8d 61 1d | staa_1D61 |
| $1D1D | 8d 7c 1d | staa_1D7C |
| $1D20 | 8d 99 1d | staa_1D99 |
| $1D23 | 8d b0 1d | staa_1DB0 |
| $1D26 | a9 08 | lda#$08; --- patch gen A direction codes (bit 7 clear = visited marker) --- |
| $1D28 | 8d 6c 1d | staa_1D6C; above: $08 = move-down (bit 7 clear) |
| $1D2B | a9 18 | lda#$18 |
| $1D2D | 8d 87 1d | staa_1D87; below: $18 = move-up |
| $1D30 | a9 10 | lda#$10; right: $10 = move-left |
| $1D32 | 8d a2 1d | staa_1DA2 |
| $1D35 | a9 20 | lda#$20; left: $20 = move-right |
| $1D37 | 8d b9 1d | staa_1DB9 |
| $1D3A | ac b4 0d | ldydraw_offset |
| $1D3D | ae b5 0d | ldxdraw_page |
| $1D40 | e8 | inx |
| $1D41 | 20 79 1c | jsrqueue_push_xy |
| $1D44 | 86 5d | stxzp_ptr_attr_ram_hi |
| $1D46 | a5 63 | j_1D46ldazp_queue_tail; --- BFS loop: empty? (tail == head) -> done --- ; x-ref: $1D15, $1DC0 FACSGN Floating Accum. #1: Sign |
| $1D48 | c5 62 | cmpzp_queue_head_temp |
| $1D4A | d0 01 | bneb_1D4D |
| $1D4C | 60 | rts |
| $1D4D | e6 62 | b_1D4Dinczp_queue_head_temp; dequeue: advance head, read (col, page) from queue arrays ; x-ref: $1D4A |
| $1D4F | a4 62 | ldyzp_queue_head_temp |
| $1D51 | b9 00 66 | ldaqueue_array_y,y |
| $1D54 | be 00 65 | ldxqueue_array_x,y |
| $1D57 | a8 | tay; Y = col (column offset), X = page (row) |
| $1D58 | e0 68 | cpx#$68; first data row ($68)? no row above -> skip above-check |
| $1D5A | f0 17 | beqb_1D73 |
| $1D5C | ca | dex; --- above neighbour (page-1) --- |
| $1D5D | 86 5d | stxzp_ptr_attr_ram_hi |
| $1D5F | b1 5c | lda(zp_ptr_attr_ram_lo),y; read attr at (page-1, col); BPL/BMI skips if already visited this gen |
| $1D61 | 10 0f | a_1D61bplb_1D72; x-ref: $1CE0, $1CE9, $1D1A |
| $1D63 | 86 5f | stxzp_ptr_move_grid_hi; update movement grid pointer to page-1; FACHO Floating Accum. #1: Mantissa |
| $1D65 | b1 5e | lda(zp_ptr_move_grid_lo),y; FACEXP Floating-Point Accumulator #1: Exponent |
| $1D67 | 29 18 | and#$18; bits $08+$10: can guard fall/climb-down from (page-1) to reach us? |
| $1D69 | f0 07 | beqb_1D72 |
| a_1D6C =*+$01 ; x-ref: $1CF7, $1D28 |
| $1D6B | a9 08 | lda#$08; write "move DOWN" direction code (+ gen visited bit) |
| $1D6D | 91 5c | sta(zp_ptr_attr_ram_lo),y |
| $1D6F | 20 79 1c | jsrqueue_push_xy |
| $1D72 | e8 | b_1D72inx; --- below neighbour (page+1) --- restore page; last row ($7F)? skip ; x-ref: $1D61, $1D69 |
| $1D73 | e0 7f | b_1D73cpx#$7f; x-ref: $1D5A |
| $1D75 | f0 17 | beqb_1D8E |
| $1D77 | e8 | inx; read attr at (page+1, col); BPL/BMI skips if already visited this gen |
| $1D78 | 86 5d | stxzp_ptr_attr_ram_hi |
| $1D7A | b1 5c | lda(zp_ptr_attr_ram_lo),y |
| $1D7C | 10 0f | a_1D7Cbplb_1D8D; x-ref: $1CEC, $1D1D |
| $1D7E | 86 5f | stxzp_ptr_move_grid_hi; bit $04: can guard climb-up from (page+1) to reach us?; FACHO Floating Accum. #1: Mantissa |
| $1D80 | b1 5e | lda(zp_ptr_move_grid_lo),y; FACEXP Floating-Point Accumulator #1: Exponent |
| $1D82 | 29 04 | and#$04 |
| $1D84 | f0 07 | beqb_1D8D |
| a_1D87 =*+$01 ; x-ref: $1CFC, $1D2D |
| $1D86 | a9 18 | lda#$18; write "move UP" direction code |
| $1D88 | 91 5c | sta(zp_ptr_attr_ram_lo),y |
| $1D8A | 20 79 1c | jsrqueue_push_xy |
| $1D8D | ca | b_1D8Ddex; restore page; set both attr+grid pointers back to current row ; x-ref: $1D7C, $1D84 |
| $1D8E | 86 5d | b_1D8Estxzp_ptr_attr_ram_hi; x-ref: $1D75 |
| $1D90 | 86 5f | stxzp_ptr_move_grid_hi; FACHO Floating Accum. #1: Mantissa |
| $1D92 | c0 20 | cpy#$20; --- right neighbour (col+1) --- rightmost col ($20)? skip |
| $1D94 | f0 13 | beqb_1DA9 |
| $1D96 | c8 | iny; read attr at (page, col+1); BPL/BMI skips if already visited this gen |
| $1D97 | b1 5c | lda(zp_ptr_attr_ram_lo),y |
| $1D99 | 10 0d | a_1D99bplb_1DA8; x-ref: $1CEF, $1D20 |
| $1D9B | b1 5e | lda(zp_ptr_move_grid_lo),y; bit $01: can guard walk-left from (col+1) to reach us?; FACEXP Floating-Point Accumulator #1: Exponent |
| $1D9D | 29 01 | and#$01 |
| $1D9F | f0 07 | beqb_1DA8 |
| a_1DA2 =*+$01 ; x-ref: $1D01, $1D32 |
| $1DA1 | a9 10 | lda#$10; write "move LEFT" direction code |
| $1DA3 | 91 5c | sta(zp_ptr_attr_ram_lo),y |
| $1DA5 | 20 79 1c | jsrqueue_push_xy |
| $1DA8 | 88 | b_1DA8dey; --- left neighbour (col-1) --- restore col; leftmost col ($01)? skip ; x-ref: $1D99, $1D9F |
| $1DA9 | c0 01 | b_1DA9cpy#$01; x-ref: $1D94 |
| $1DAB | f0 13 | beqb_1DC0 |
| $1DAD | 88 | dey; read attr at (page, col-1); BPL/BMI skips if already visited this gen |
| $1DAE | b1 5c | lda(zp_ptr_attr_ram_lo),y |
| $1DB0 | 10 0d | a_1DB0bplb_1DBF; x-ref: $1CF2, $1D23 |
| $1DB2 | b1 5e | lda(zp_ptr_move_grid_lo),y; bit $02: can guard walk-right from (col-1) to reach us?; FACEXP Floating-Point Accumulator #1: Exponent |
| $1DB4 | 29 02 | and#$02 |
| $1DB6 | f0 07 | beqb_1DBF |
| a_1DB9 =*+$01 ; x-ref: $1D06, $1D37 |
| $1DB8 | a9 20 | lda#$20; write "move RIGHT" direction code |
| $1DBA | 91 5c | sta(zp_ptr_attr_ram_lo),y |
| $1DBC | 20 79 1c | jsrqueue_push_xy |
| $1DBF | c8 | b_1DBFiny; restore col; loop back for next queued cell ; x-ref: $1DB0, $1DB6 |
| $1DC0 | 4c 46 1d | b_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) |
| $1DC3 | | dig_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. |
| $1DE3 | | dig_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). |
| $1E03 | | dig_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. |
| $1E23 | | dig_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. |
| $1E43 | | dig_slot_status.byte$ff; x-ref: $0F7C, $0FAF, $0FBD, $0FF8, $102B, $1039, $109B, $1191, ... |
| $1E44 | | kbind_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). |
| $1E4D | | dig_slot_page.byte$ff; x-ref: $0F8C, $0FD1, $0FD4, $0FD7, $1008, $104D, $1050, $1053, ... |
| $1E4E | | kbind_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. |
| $1E57 | | dig_slot_col.byte$ff; x-ref: $0F81, $0FC8, $0FCB, $0FFD, $1044, $1047, $11A2, $192D, ... |
| $1E58 | | kbind_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. |
| $1E61 | | dig_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. |
| $1E6B | | dig_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). |
| $1E75 | | dig_slot_delay_outer.fill10, $ff; x-ref: $1999, $1F18, $1F30 |
| ; Background char saved from top cell before digging. Restored at status 4. |
| $1E7F | | dig_slot_saved_top.fill10, $ff; x-ref: $1EC6, $1F7F |
| ; Background char saved from bottom cell before digging. Restored at status 4. |
| $1E89 | | dig_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. |
| $1E93 | | dig_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $1E9D | a2 09 | init_dig_slotsldx#$09; x-ref: $073C, $1633 |
| $1E9F | a9 ff | lda#$ff |
| $1EA1 | 9d 43 1e | b_1EA1stadig_slot_status,x; x-ref: $1EA8 |
| $1EA4 | 9d 93 1e | stadig_slot_hole_idx,x |
| $1EA7 | ca | dex |
| $1EA8 | 10 f7 | bplb_1EA1 |
| $1EAA | a9 00 | lda#$00 |
| $1EAC | 85 68 | stazp_ptr_dig_screen_lo |
| $1EAE | a9 55 | lda#$55 |
| $1EB0 | 85 77 | stazp_ptr_ai_grid_lo; TXTPTR Pointer: Current Byte of BASIC Text |
| $1EB2 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $1EB3 | a2 09 | ldx#$09; iterate all 10 dig slots ; x-ref: $0671, $1676 |
| $1EB5 | bd 43 1e | b_1EB5ldadig_slot_status,x; $FF = inactive → skip ; x-ref: $1F1F |
| $1EB8 | 30 64 | bmib_1F1E |
| $1EBA | d0 21 | bneb_1EDD; non-zero → not phase 0; dispatch further |
| $1EBC | bd 4d 1e | ldadig_slot_page,x; --- phase 0: save background tiles --- |
| $1EBF | 85 69 | stazp_ptr_dig_screen_hi |
| $1EC1 | bc 57 1e | ldydig_slot_col,x; set screen pointer to dig row |
| $1EC4 | b1 68 | lda(zp_ptr_dig_screen_lo),y; save top tile (row) |
| $1EC6 | 9d 7f 1e | stadig_slot_saved_top,x |
| $1EC9 | e6 69 | inczp_ptr_dig_screen_hi; advance pointer to row+1 (bottom tile) |
| $1ECB | b1 68 | lda(zp_ptr_dig_screen_lo),y; save bottom tile (row+1) |
| $1ECD | 9d 89 1e | stadig_slot_saved_bot,x |
| $1ED0 | c6 69 | deczp_ptr_dig_screen_hi; restore pointer |
| $1ED2 | a9 00 | lda#$00 |
| $1ED4 | 9d 61 1e | stadig_slot_anim_step,x; reset anim step counter to 0 |
| $1ED7 | fe 43 1e | incdig_slot_status,x; advance to phase 1 |
| $1EDA | 4c 1e 1f | jmpb_1F1E |
| $1EDD | c9 01 | b_1EDDcmp#$01; x-ref: $1EBA |
| $1EDF | d0 41 | bneb_1F22 |
| $1EE1 | bd 4d 1e | ldadig_slot_page,x; --- phase 1: open animation (32 steps) --- |
| $1EE4 | 85 69 | stazp_ptr_dig_screen_hi |
| $1EE6 | bc 61 1e | ldydig_slot_anim_step,x; set screen pointer to dig row |
| $1EE9 | b9 c3 1d | ldadig_open_anim_top,y |
| $1EEC | bc 57 1e | ldydig_slot_col,x |
| $1EEF | 20 2e 21 | jsrget_adjacent_tile_border; clip to border tile if at edge |
| $1EF2 | 91 68 | sta(zp_ptr_dig_screen_lo),y; write top char to screen (row) |
| $1EF4 | e6 69 | inczp_ptr_dig_screen_hi; advance pointer to row+1 (bottom tile) |
| $1EF6 | bc 61 1e | ldydig_slot_anim_step,x |
| $1EF9 | b9 e3 1d | ldadig_open_anim_bot,y; look up bottom char for this anim step |
| $1EFC | bc 57 1e | ldydig_slot_col,x |
| $1EFF | 20 2e 21 | jsrget_adjacent_tile_border; write bottom char to screen (row+1) |
| $1F02 | 91 68 | sta(zp_ptr_dig_screen_lo),y |
| $1F04 | fe 61 1e | incdig_slot_anim_step,x; all 32 steps done? |
| $1F07 | bd 61 1e | ldadig_slot_anim_step,x |
| $1F0A | c9 20 | cmp#$20 |
| $1F0C | d0 10 | bneb_1F1E |
| $1F0E | fe 43 1e | incdig_slot_status,x; arm hold-open counters: inner=8, outer=$2B (=43) → ~352 frame ticks |
| $1F11 | a9 08 | lda#$08 |
| $1F13 | 9d 6b 1e | stadig_slot_delay_inner,x |
| $1F16 | a9 2b | lda#$2b |
| $1F18 | 9d 75 1e | stadig_slot_delay_outer,x |
| $1F1B | 20 cb 1f | jsrupdate_hole_collision; update movement grid for open hole; advance to phase 2 |
| $1F1E | ca | b_1F1Edex; x-ref: $1EB8, $1EDA, $1F0C, $1F29, $1F33, $1F3D, $1F6B, $1F70, ... |
| $1F1F | 10 94 | bplb_1EB5 |
| $1F21 | 60 | rts |
| $1F22 | c9 02 | b_1F22cmp#$02; --- phase 2: hold open (nested countdown) --- ; x-ref: $1EDF |
| $1F24 | d0 1a | bneb_1F40 |
| $1F26 | de 6b 1e | decdig_slot_delay_inner,x; inner still counting → wait |
| $1F29 | 10 f3 | bplb_1F1E |
| $1F2B | a9 08 | lda#$08; reload inner = 8 |
| $1F2D | 9d 6b 1e | stadig_slot_delay_inner,x |
| $1F30 | de 75 1e | decdig_slot_delay_outer,x; outer still counting → wait another inner cycle |
| $1F33 | 10 e9 | bplb_1F1E |
| $1F35 | a9 00 | lda#$00; both expired: reset anim step, advance to phase 3 |
| $1F37 | 9d 61 1e | stadig_slot_anim_step,x |
| $1F3A | fe 43 1e | incdig_slot_status,x |
| $1F3D | 4c 1e 1f | jmpb_1F1E |
| $1F40 | c9 03 | b_1F40cmp#$03; x-ref: $1F24 |
| $1F42 | d0 2f | bneb_1F73 |
| $1F44 | bd 4d 1e | ldadig_slot_page,x; look up top close char; $00 = sentinel: skip writing top cell |
| $1F47 | 85 69 | stazp_ptr_dig_screen_hi |
| $1F49 | bc 61 1e | ldydig_slot_anim_step,x |
| $1F4C | b9 03 1e | ldadig_close_anim_top,y |
| $1F4F | f0 05 | beqb_1F56 |
| $1F51 | bc 57 1e | ldydig_slot_col,x |
| $1F54 | 91 68 | sta(zp_ptr_dig_screen_lo),y |
| $1F56 | e6 69 | b_1F56inczp_ptr_dig_screen_hi; x-ref: $1F4F |
| $1F58 | bc 61 1e | ldydig_slot_anim_step,x |
| $1F5B | b9 23 1e | ldadig_close_anim_bot,y |
| $1F5E | bc 57 1e | ldydig_slot_col,x |
| $1F61 | 91 68 | sta(zp_ptr_dig_screen_lo),y |
| $1F63 | fe 61 1e | incdig_slot_anim_step,x; all 32 steps done → advance to phase 4 |
| $1F66 | bd 61 1e | ldadig_slot_anim_step,x |
| $1F69 | c9 20 | cmp#$20 |
| $1F6B | d0 b1 | bneb_1F1E |
| $1F6D | fe 43 1e | incdig_slot_status,x |
| $1F70 | 4c 1e 1f | jmpb_1F1E |
| $1F73 | c9 04 | b_1F73cmp#$04; --- phase 4: restore background tiles --- ; x-ref: $1F42 |
| $1F75 | d0 a7 | bneb_1F1E |
| $1F77 | bd 4d 1e | ldadig_slot_page,x |
| $1F7A | 85 69 | stazp_ptr_dig_screen_hi |
| $1F7C | bc 57 1e | ldydig_slot_col,x |
| $1F7F | bd 7f 1e | ldadig_slot_saved_top,x; restore top bg tile to screen |
| $1F82 | 91 68 | sta(zp_ptr_dig_screen_lo),y |
| $1F84 | e6 69 | inczp_ptr_dig_screen_hi; restore bottom bg tile to screen (row+1) |
| $1F86 | bd 89 1e | ldadig_slot_saved_bot,x |
| $1F89 | 91 68 | sta(zp_ptr_dig_screen_lo),y |
| $1F8B | a9 ff | lda#$ff; mark slot idle ($FF) |
| $1F8D | 9d 43 1e | stadig_slot_status,x |
| $1F90 | 20 aa 20 | jsrupdate_filled_hole_collision; update movement grid for closed hole |
| $1F93 | bd 93 1e | ldadig_slot_hole_idx,x; valid hole slot index? (>= 0) → respawn trapped guard |
| $1F96 | 10 03 | bplb_1F9B |
| $1F98 | 4c 1e 1f | jmpb_1F1E |
| $1F9B | a8 | b_1F9Btay; Y = hole slot index → index into guard arrays ; x-ref: $1F96 |
| $1F9C | a9 00 | lda#$00; deactivate hole slot |
| $1F9E | 99 d0 2b | stahole_slot_state,y |
| $1FA1 | a9 00 | lda#$00 |
| $1FA3 | 99 fe 21 | staguard_anim_frame,y; reset: anim_frame=0, vspeed=0, hspeed=0 |
| $1FA6 | 99 12 22 | staguard_vspeed,y |
| $1FA9 | 99 17 22 | staguard_hspeed,y |
| $1FAC | 99 f9 21 | staguard_active,y; guard_active=0 (active), ai_bias=0 |
| $1FAF | 99 21 22 | staguard_ai_bias,y |
| $1FB2 | a9 02 | lda#$02; move_rate=2 |
| $1FB4 | 99 1c 22 | staguard_move_rate,y |
| $1FB7 | a9 30 | lda#$30; sprite_strip=$30 (standing/walking) |
| $1FB9 | 99 0d 22 | staguard_sprite_strip,y |
| $1FBC | ad e4 2a | ldaguard_spawn_offset; teleport guard back to spawn position |
| $1FBF | 99 03 22 | staguard_offset,y |
| $1FC2 | ad e5 2a | ldaguard_spawn_page |
| $1FC5 | 99 08 22 | staguard_page,y |
| $1FC8 | 4c 1e 1f | jmpb_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 |
| $1FCB | a5 69 | ldazp_ptr_dig_screen_hi; dig row page ; x-ref: $1F1B |
| $1FCD | 85 5b | stazp_ptr_playfield_hi; a5B = dig row |
| $1FCF | c6 5b | deczp_ptr_playfield_hi; a5B = dig row − 1 |
| $1FD1 | c6 5b | deczp_ptr_playfield_hi; start 2 rows above dig row |
| $1FD3 | bc 57 1e | ldydig_slot_col,x; Y = hole column |
| $1FD6 | b1 5a | lda(zp_ptr_playfield_lo),y; read movement flags at row−2 |
| $1FD8 | 29 20 | and#$20; rope present? |
| $1FDA | f0 09 | beqb_1FE5; no rope → fall-through path |
| $1FDC | b1 5a | lda(zp_ptr_playfield_lo),y |
| $1FDE | 09 08 | ora#$08; rope: add climb-down ($08) — gap below is reachable |
| $1FE0 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $1FE2 | 4c eb 1f | jmpj_1FEB |
| $1FE5 | b1 5a | b_1FE5lda(zp_ptr_playfield_lo),y; no rope: add fall-through ($10) ; x-ref: $1FDA |
| $1FE7 | 09 10 | ora#$10 |
| $1FE9 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $1FEB | e6 5b | j_1FEBinczp_ptr_playfield_hi; advance to row−1 (top edge of hole) ; x-ref: $1FE2 |
| $1FED | a9 10 | lda#$10; force fall-through at row−1 |
| $1FEF | 91 5a | sta(zp_ptr_playfield_lo),y |
| $1FF1 | e6 5b | inczp_ptr_playfield_hi; advance to dig row |
| $1FF3 | a5 5b | ldazp_ptr_playfield_hi; at bottom of playfield ($7F)? |
| $1FF5 | c9 7f | cmp#$7f |
| $1FF7 | f0 15 | beqb_200E; yes: skip below-check, use open-floor path |
| $1FF9 | e6 5b | inczp_ptr_playfield_hi; peek at row below |
| $1FFB | b1 5a | lda(zp_ptr_playfield_lo),y; read flags of cell directly below hole |
| $1FFD | c6 5b | deczp_ptr_playfield_hi; back to dig row |
| $1FFF | 29 10 | and#$10; row below has fall-through? |
| $2001 | f0 0b | beqb_200E; no → open-floor path |
| $2003 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2005 | 29 3f | and#$3f; clear solid bits ($40/$80) |
| $2007 | 09 10 | ora#$10; hole over void/fall: propagate fall-through |
| $2009 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $200B | 4c 59 20 | jmpj_2059 |
| $200E | b1 5a | b_200Elda(zp_ptr_playfield_lo),y; clear solid bits ; x-ref: $1FF7, $2001 |
| $2010 | 29 3f | and#$3f; set walk-left + walk-right + climb-down ($01|$02|$08) |
| $2012 | 09 0b | ora#$0b |
| $2014 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2016 | c0 01 | cpy#$01; at left edge (col 1)? |
| $2018 | f0 0b | beqb_2025; yes: prune walk-left |
| $201A | 88 | dey; check left neighbor |
| $201B | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor flags |
| $201D | c8 | iny; restore Y |
| $201E | 29 80 | and#$80; left neighbor solid? |
| $2020 | d0 03 | bneb_2025; yes: prune walk-left |
| $2022 | 4c 2b 20 | jmpj_202B |
| $2025 | b1 5a | b_2025lda(zp_ptr_playfield_lo),y; clear walk-left ($01) ; x-ref: $2018, $2020 |
| $2027 | 29 fe | and#$fe |
| $2029 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $202B | c0 20 | j_202Bcpy#$20; at right edge (col $20)? ; x-ref: $2022 |
| $202D | f0 0b | beqb_203A; yes: prune walk-right |
| $202F | c8 | iny; check right neighbor |
| $2030 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2032 | 88 | dey; restore Y |
| $2033 | 29 80 | and#$80; right neighbor solid? |
| $2035 | d0 03 | bneb_203A; yes: prune walk-right |
| $2037 | 4c 40 20 | jmpj_2040 |
| $203A | b1 5a | b_203Alda(zp_ptr_playfield_lo),y; clear walk-right ($02) ; x-ref: $202D, $2035 |
| $203C | 29 fd | and#$fd |
| $203E | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2040 | a5 5b | j_2040ldazp_ptr_playfield_hi; at last row? ; x-ref: $2037 |
| $2042 | c9 7f | cmp#$7f |
| $2044 | f0 0d | beqb_2053; yes: prune climb-down |
| $2046 | e6 5b | inczp_ptr_playfield_hi; peek at row below |
| $2048 | b1 5a | lda(zp_ptr_playfield_lo),y; read flags of cell below |
| $204A | c6 5b | deczp_ptr_playfield_hi; restore to dig row |
| $204C | 29 08 | and#$08; cell below has climb-down? |
| $204E | f0 03 | beqb_2053; no: prune climb-down |
| $2050 | 4c 59 20 | jmpj_2059 |
| $2053 | b1 5a | b_2053lda(zp_ptr_playfield_lo),y; clear climb-down ($08) ; x-ref: $2044, $204E |
| $2055 | 29 f7 | and#$f7 |
| $2057 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2059 | 88 | j_2059dey; left neighbor column ; x-ref: $200B, $2050 |
| $205A | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor at dig row |
| $205C | 29 80 | and#$80; solid? skip |
| $205E | d0 0c | bneb_206C |
| $2060 | b1 5a | lda(zp_ptr_playfield_lo),y; fall-through? skip |
| $2062 | 29 10 | and#$10 |
| $2064 | d0 06 | bneb_206C; left neighbor can now walk right into open hole |
| $2066 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2068 | 09 02 | ora#$02 |
| $206A | 91 5a | sta(zp_ptr_playfield_lo),y |
| $206C | c6 5b | b_206Cdeczp_ptr_playfield_hi; advance to row−1 ; x-ref: $205E, $2064 |
| $206E | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor at row−1 |
| $2070 | 29 80 | and#$80; solid? skip |
| $2072 | d0 0c | bneb_2080 |
| $2074 | b1 5a | lda(zp_ptr_playfield_lo),y; fall-through? skip |
| $2076 | 29 10 | and#$10 |
| $2078 | d0 06 | bneb_2080; left neighbor at row−1 can now walk right into hole |
| $207A | b1 5a | lda(zp_ptr_playfield_lo),y |
| $207C | 09 02 | ora#$02 |
| $207E | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2080 | c8 | b_2080iny; col−1 → col → col+1 (right neighbor) ; x-ref: $2072, $2078 |
| $2081 | c8 | iny |
| $2082 | b1 5a | lda(zp_ptr_playfield_lo),y; read right neighbor at row−1 |
| $2084 | 29 80 | and#$80; solid? skip |
| $2086 | d0 0c | bneb_2094 |
| $2088 | b1 5a | lda(zp_ptr_playfield_lo),y; fall-through? skip |
| $208A | 29 10 | and#$10 |
| $208C | d0 06 | bneb_2094; right neighbor at row−1 can now walk left into hole |
| $208E | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2090 | 09 01 | ora#$01 |
| $2092 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2094 | e6 5b | b_2094inczp_ptr_playfield_hi; advance to dig row ; x-ref: $2086, $208C |
| $2096 | b1 5a | lda(zp_ptr_playfield_lo),y; read right neighbor at dig row |
| $2098 | 29 80 | and#$80; solid? skip |
| $209A | d0 0c | bneb_20A8 |
| $209C | b1 5a | lda(zp_ptr_playfield_lo),y; fall-through? skip |
| $209E | 29 10 | and#$10 |
| $20A0 | d0 06 | bneb_20A8; right neighbor at dig row can now walk left into hole |
| $20A2 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $20A4 | 09 01 | ora#$01 |
| $20A6 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $20A8 | 88 | b_20A8dey; restore Y to hole column ; x-ref: $209A, $20A0 |
| $20A9 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $20AA | a5 69 | ldazp_ptr_dig_screen_hi; dig row page ; x-ref: $1F90 |
| $20AC | 85 5b | stazp_ptr_playfield_hi; a5B = dig row |
| $20AE | c6 5b | deczp_ptr_playfield_hi; a5B = dig row − 1 |
| $20B0 | c6 5b | deczp_ptr_playfield_hi; start 2 rows above dig row |
| $20B2 | bc 57 1e | ldydig_slot_col,x; Y = hole column |
| $20B5 | b1 5a | lda(zp_ptr_playfield_lo),y; read movement flags at row−2 |
| $20B7 | 29 c0 | and#$c0; solid ($40|$80)? |
| $20B9 | f0 03 | beqb_20BE; not solid: restore walk bits |
| $20BB | 4c f0 20 | jmpj_20F0; already solid: skip to hole-rows |
| $20BE | b1 5a | b_20BElda(zp_ptr_playfield_lo),y; keep rope bit ($20) only ; x-ref: $20B9 |
| $20C0 | 29 20 | and#$20 |
| $20C2 | 09 03 | ora#$03; restore walk-left ($01) + walk-right ($02) |
| $20C4 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $20C6 | c8 | iny; check right neighbor |
| $20C7 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $20C9 | 88 | dey; restore Y |
| $20CA | 29 80 | and#$80; right neighbor solid? |
| $20CC | d0 07 | bneb_20D5; yes: prune walk-right |
| $20CE | c0 20 | cpy#$20; at right edge (col $20)? |
| $20D0 | f0 03 | beqb_20D5; yes: prune walk-right |
| $20D2 | 4c db 20 | jmpj_20DB |
| $20D5 | b1 5a | b_20D5lda(zp_ptr_playfield_lo),y; clear walk-right ($02) ; x-ref: $20CC, $20D0 |
| $20D7 | 29 fd | and#$fd |
| $20D9 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $20DB | c0 01 | j_20DBcpy#$01; at left edge (col 1)? ; x-ref: $20D2 |
| $20DD | f0 0b | beqb_20EA; yes: prune walk-left |
| $20DF | 88 | dey; check left neighbor |
| $20E0 | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor flags |
| $20E2 | c8 | iny; restore Y |
| $20E3 | 29 80 | and#$80; left neighbor solid? |
| $20E5 | d0 03 | bneb_20EA; yes: prune walk-left |
| $20E7 | 4c f0 20 | jmpj_20F0 |
| $20EA | b1 5a | b_20EAlda(zp_ptr_playfield_lo),y; clear walk-left ($01) ; x-ref: $20DD, $20E5 |
| $20EC | 29 fe | and#$fe |
| $20EE | 91 5a | sta(zp_ptr_playfield_lo),y |
| $20F0 | a9 c0 | j_20F0lda#$c0; solid wall value ; x-ref: $20BB, $20E7 |
| $20F2 | e6 5b | inczp_ptr_playfield_hi; advance to row−1 |
| $20F4 | 91 5a | sta(zp_ptr_playfield_lo),y; seal row−1 as solid |
| $20F6 | e6 5b | inczp_ptr_playfield_hi; advance to dig row |
| $20F8 | 91 5a | sta(zp_ptr_playfield_lo),y; seal dig row as solid |
| $20FA | 88 | dey; left neighbor column |
| $20FB | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor at dig row |
| $20FD | 29 fd | and#$fd; clear walk-right ($02) — hole is sealed, can't exit right |
| $20FF | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2101 | c6 5b | deczp_ptr_playfield_hi; move to row−1 |
| $2103 | b1 5a | lda(zp_ptr_playfield_lo),y; read left neighbor at row−1 |
| $2105 | 29 fd | and#$fd; clear walk-right ($02) |
| $2107 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2109 | c8 | iny; col−1 → col → col+1 (right neighbor) |
| $210A | c8 | iny |
| $210B | b1 5a | lda(zp_ptr_playfield_lo),y; read right neighbor at row−1 |
| $210D | 29 fe | and#$fe; clear walk-left ($01) — hole is sealed, can't enter from right |
| $210F | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2111 | e6 5b | inczp_ptr_playfield_hi; advance to dig row |
| $2113 | b1 5a | lda(zp_ptr_playfield_lo),y; read right neighbor at dig row |
| $2115 | 29 fe | and#$fe; clear walk-left ($01) |
| $2117 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2119 | a5 5b | ldazp_ptr_playfield_hi; a78 = dig row (AI grid high byte) |
| $211B | 85 78 | stazp_ptr_ai_grid_hi |
| $211D | a9 00 | lda#$00; zero = clear BFS direction code |
| $211F | 91 77 | sta(zp_ptr_ai_grid_lo),y; zero right neighbor at dig row in AI grid; TXTPTR Pointer: Current Byte of BASIC Text |
| $2121 | c6 78 | deczp_ptr_ai_grid_hi; move to row−1 |
| $2123 | 91 77 | sta(zp_ptr_ai_grid_lo),y; zero right neighbor at row−1 in AI grid; TXTPTR Pointer: Current Byte of BASIC Text |
| $2125 | 88 | dey; col+1 → col → col−1 (left neighbor) |
| $2126 | 88 | dey |
| $2127 | 91 77 | sta(zp_ptr_ai_grid_lo),y; zero left neighbor at row−1 in AI grid; TXTPTR Pointer: Current Byte of BASIC Text |
| $2129 | e6 78 | inczp_ptr_ai_grid_hi; advance to dig row |
| $212B | 91 77 | sta(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 |
| $212D | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $212E | c9 20 | cmp#$20; A != $20 (space)? pass through unchanged — only autotiles over empty cells ; x-ref: $1EEF, $1EFF |
| $2130 | d0 42 | bner_2174 |
| $2132 | a5 69 | ldazp_ptr_dig_screen_hi; on last row ($7F)? use floor-border chars instead |
| $2134 | c9 7f | cmp#$7f |
| $2136 | d0 0a | bneb_2142 |
| $2138 | c0 01 | cpy#$01; col 1 (leftmost)? return $4C (left floor-border) |
| $213A | d0 03 | bneb_213F |
| $213C | a9 4c | lda#$4c |
| $213E | 60 | rts |
| $213F | a9 64 | b_213Flda#$64; any other col on last row: return $64 (floor-border) ; x-ref: $213A |
| $2141 | 60 | rts |
| $2142 | c8 | b_2142iny; --- check right neighbour (col+1) --- ; x-ref: $2136 |
| $2143 | b1 68 | lda(zp_ptr_dig_screen_lo),y; peek tile at col+1 |
| $2145 | 88 | dey; restore Y |
| $2146 | c9 43 | cmp#$43; solid tile to the right ($43/$63/$50)? |
| $2148 | f0 0b | beqb_2155 |
| $214A | c9 63 | cmp#$63 |
| $214C | f0 07 | beqb_2155 |
| $214E | c9 50 | cmp#$50 |
| $2150 | f0 03 | beqb_2155 |
| $2152 | 4c 58 21 | jmpj_2158 |
| $2155 | a9 67 | b_2155lda#$67; return $67: right-edge shadow char ; x-ref: $2148, $214C, $2150 |
| $2157 | 60 | rts |
| $2158 | c0 01 | j_2158cpy#$01; --- check left neighbour (col-1) --- col 1? no left neighbour → still return left-edge char ; x-ref: $2152 |
| $215A | f0 13 | beqb_216F |
| $215C | 88 | dey; peek tile at col-1 |
| $215D | b1 68 | lda(zp_ptr_dig_screen_lo),y |
| $215F | c8 | iny; restore Y |
| $2160 | c9 43 | cmp#$43; solid tile to the left ($43/$63/$4F)? |
| $2162 | f0 0b | beqb_216F |
| $2164 | c9 63 | cmp#$63 |
| $2166 | f0 07 | beqb_216F |
| $2168 | c9 4f | cmp#$4f |
| $216A | f0 03 | beqb_216F |
| $216C | 4c 72 21 | jmpj_2172 |
| $216F | a9 65 | b_216Flda#$65; return $65: left-edge shadow char ; x-ref: $215A, $2162, $2166, $216A |
| $2171 | 60 | rts |
| $2172 | a9 20 | j_2172lda#$20; no solid neighbours: return $20 (plain space, no edge effect) ; x-ref: $216C |
| $2174 | 60 | r_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) |
| $2175 | | guard_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) |
| $21E5 | | guard_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. |
| $21EA | | guard_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. |
| $21EF | | guard_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. |
| $21F9 | | guard_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. |
| $21FE | | guard_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. |
| $2203 | | guard_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. |
| $2208 | | guard_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. |
| $220D | | guard_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. |
| $2212 | | guard_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. |
| $2217 | | guard_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. |
| $221C | | guard_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. |
| $2221 | | guard_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). |
| $2226 | | guard_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $222B | a9 00 | init_guard_arrayslda#$00; --- ZP pointer setup --- ; x-ref: $0745 |
| $222D | 85 6e | stazp_ptr_guard_top_lo; a6E = $00 (screen row ptr low); FBUFPT Pointer: Cassette Buffer |
| $222F | 85 70 | stazp_ptr_guard_bot_lo; a70 = $00 (same); CHRGET Subroutine: Get Next Byte of BASIC Text |
| $2231 | a9 2b | lda#$2b; a5A = $2B (movement grid column base offset) |
| $2233 | 85 5a | stazp_ptr_playfield_lo |
| $2235 | a9 55 | lda#$55; p60 = $55 (pathfinding attr RAM pointer low byte) |
| $2237 | 85 60 | stazp_ptr_pathfinding_lo |
| $2239 | a2 04 | ldx#$04; --- init 5 guard slots (X: 4..0) --- |
| $223B | a9 ff | b_223Blda#$ff; guard_active = $FF (inactive), guard_hole_status = $FF (not trapped) ; x-ref: $2260 |
| $223D | 9d f9 21 | staguard_active,x |
| $2240 | 9d 26 22 | staguard_hole_status,x |
| $2243 | a9 00 | lda#$00; guard_ai_bias = 0 |
| $2245 | 9d 21 22 | staguard_ai_bias,x |
| $2248 | a9 00 | lda#$00; guard_sprite_strip = 0 (default) |
| $224A | 9d 0d 22 | staguard_sprite_strip,x |
| $224D | a9 08 | lda#$08; guard_anim_frame = $08 |
| $224F | 9d fe 21 | staguard_anim_frame,x |
| $2252 | a9 00 | lda#$00; guard_hspeed = 0, guard_vspeed = 0 (stationary) |
| $2254 | 9d 17 22 | staguard_hspeed,x |
| $2257 | 9d 12 22 | staguard_vspeed,x |
| $225A | a9 80 | lda#$80; guard_move_rate = $80 (128-tick head-start delay) |
| $225C | 9d 1c 22 | staguard_move_rate,x |
| $225F | ca | dex; next guard |
| $2260 | 10 d9 | bplb_223B |
| $2262 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2263 | a9 30 | guard_gravity_falllda#$30; strip=$30 (falling) ; x-ref: $23F0 |
| $2265 | 9d 0d 22 | staguard_sprite_strip,x |
| $2268 | bd fe 21 | ldaguard_anim_frame,x; advance frame +4, wrap within 0-$C |
| $226B | 18 | clc |
| $226C | 69 04 | adc#$04 |
| $226E | 29 0c | and#$0c |
| $2270 | 9d fe 21 | staguard_anim_frame,x |
| $2273 | d0 0d | bneb_2282; frame wrapped to 0 → move one row down |
| $2275 | fe 08 22 | incguard_page,x; collision check; roll back if hit |
| $2278 | 20 1b 28 | jsrcheck_guard_guard_collision |
| $227B | f0 08 | beqb_2285 |
| $227D | 20 3c 28 | jsrcheck_guard_hole_collision |
| $2280 | f0 03 | beqb_2285 |
| $2282 | 4c dd 23 | b_2282jmpguard_move_epilogue; x-ref: $2273 |
| $2285 | de 08 22 | b_2285decguard_page,x; rollback: undo page inc ; x-ref: $227B, $2280 |
| $2288 | 4c dd 23 | jmpguard_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $228B | b1 5a | guard_move_downlda(zp_ptr_playfield_lo),y; bits $08+$10: climb-down or fall-through accessible? ; x-ref: $23FA |
| $228D | 29 18 | and#$18 |
| $228F | d0 15 | bneb_22A6; no: animate only if strip already $20, stop on wrap |
| $2291 | bd 0d 22 | ldaguard_sprite_strip,x |
| $2294 | c9 20 | cmp#$20 |
| $2296 | d0 2d | bneb_22C5 |
| $2298 | bd fe 21 | ldaguard_anim_frame,x |
| $229B | 18 | clc |
| $229C | 69 04 | adc#$04 |
| $229E | 29 0c | and#$0c |
| $22A0 | f0 23 | beqb_22C5 |
| $22A2 | 9d fe 21 | staguard_anim_frame,x |
| $22A5 | 60 | rts; rts early: animate only, don't advance position |
| $22A6 | a9 20 | b_22A6lda#$20; yes: strip=$20, advance frame +4 ; x-ref: $228F |
| $22A8 | 9d 0d 22 | staguard_sprite_strip,x |
| $22AB | bd fe 21 | ldaguard_anim_frame,x |
| $22AE | 18 | clc |
| $22AF | 69 04 | adc#$04 |
| $22B1 | 29 0c | and#$0c |
| $22B3 | 9d fe 21 | staguard_anim_frame,x |
| $22B6 | d0 0d | bneb_22C5; frame wrapped to 0 → move one row down |
| $22B8 | fe 08 22 | incguard_page,x |
| $22BB | 20 1b 28 | jsrcheck_guard_guard_collision |
| $22BE | f0 08 | beqb_22C8 |
| $22C0 | 20 3c 28 | jsrcheck_guard_hole_collision |
| $22C3 | f0 03 | beqb_22C8 |
| $22C5 | 4c dd 23 | b_22C5jmpguard_move_epilogue; x-ref: $2296, $22A0, $22B6 |
| $22C8 | de 08 22 | b_22C8decguard_page,x; rollback: undo page inc ; x-ref: $22BE, $22C3 |
| $22CB | 4c dd 23 | jmpguard_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $22CE | b1 5a | guard_move_uplda(zp_ptr_playfield_lo),y; bit $04: climb-up accessible? ; x-ref: $23FD |
| $22D0 | 29 04 | and#$04 |
| $22D2 | d0 17 | bneb_22EB; no: animate only if strip already $20, stop when frame reaches $0C |
| $22D4 | bd 0d 22 | ldaguard_sprite_strip,x |
| $22D7 | c9 20 | cmp#$20 |
| $22D9 | d0 2f | bneb_230A |
| $22DB | bd fe 21 | ldaguard_anim_frame,x |
| $22DE | 38 | sec |
| $22DF | e9 04 | sbc#$04 |
| $22E1 | 29 0c | and#$0c |
| $22E3 | c9 0c | cmp#$0c |
| $22E5 | f0 23 | beqb_230A |
| $22E7 | 9d fe 21 | staguard_anim_frame,x |
| $22EA | 60 | rts; rts early: animate only, don't advance position |
| $22EB | a9 20 | b_22EBlda#$20; yes: strip=$20, advance frame -4 ; x-ref: $22D2 |
| $22ED | 9d 0d 22 | staguard_sprite_strip,x |
| $22F0 | bd fe 21 | ldaguard_anim_frame,x |
| $22F3 | 38 | sec |
| $22F4 | e9 04 | sbc#$04 |
| $22F6 | 29 0c | and#$0c |
| $22F8 | 9d fe 21 | staguard_anim_frame,x |
| $22FB | d0 0d | bneb_230A; frame reached 0 → move one row up |
| $22FD | de 08 22 | decguard_page,x |
| $2300 | 20 1b 28 | jsrcheck_guard_guard_collision |
| $2303 | f0 08 | beqb_230D |
| $2305 | 20 3c 28 | jsrcheck_guard_hole_collision |
| $2308 | f0 03 | beqb_230D |
| $230A | 4c dd 23 | b_230Ajmpguard_move_epilogue; x-ref: $22D9, $22E5, $22FB |
| $230D | fe 08 22 | b_230Dincguard_page,x; rollback: undo page dec ; x-ref: $2303, $2308 |
| $2310 | 4c dd 23 | jmpguard_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2313 | b1 5a | guard_move_rightlda(zp_ptr_playfield_lo),y; bit $02: walk-right accessible? ; x-ref: $2407 |
| $2315 | 29 02 | and#$02 |
| $2317 | d0 17 | bneb_2330; no: animate only if strip $00 or $40, stop on wrap to 0 |
| $2319 | bd 0d 22 | ldaguard_sprite_strip,x |
| $231C | f0 04 | beqb_2322 |
| $231E | c9 40 | cmp#$40 |
| $2320 | d0 33 | bneb_2355 |
| $2322 | bd fe 21 | b_2322ldaguard_anim_frame,x; x-ref: $231C |
| $2325 | 18 | clc |
| $2326 | 69 04 | adc#$04 |
| $2328 | 29 0c | and#$0c |
| $232A | f0 29 | beqb_2355 |
| $232C | 9d fe 21 | staguard_anim_frame,x |
| $232F | 60 | rts; rts early: animate only |
| $2330 | b1 5a | b_2330lda(zp_ptr_playfield_lo),y; yes: bit $20 set → on rope → strip=$40; else strip=$00 (floor) ; x-ref: $2317 |
| $2332 | 29 20 | and#$20 |
| $2334 | f0 02 | beqb_2338 |
| $2336 | a9 40 | lda#$40 |
| $2338 | 9d 0d 22 | b_2338staguard_sprite_strip,x; x-ref: $2334 |
| $233B | bd fe 21 | ldaguard_anim_frame,x |
| $233E | 18 | clc |
| $233F | 69 04 | adc#$04 |
| $2341 | 29 0c | and#$0c |
| $2343 | 9d fe 21 | staguard_anim_frame,x |
| $2346 | d0 0d | bneb_2355; frame wrapped to 0 → move one col right |
| $2348 | fe 03 22 | incguard_offset,x |
| $234B | 20 1b 28 | jsrcheck_guard_guard_collision |
| $234E | f0 08 | beqb_2358 |
| $2350 | 20 3c 28 | jsrcheck_guard_hole_collision |
| $2353 | f0 03 | beqb_2358 |
| $2355 | 4c dd 23 | b_2355jmpguard_move_epilogue; x-ref: $2320, $232A, $2346 |
| $2358 | de 03 22 | b_2358decguard_offset,x; rollback: undo offset inc ; x-ref: $234E, $2353 |
| $235B | 4c dd 23 | jmpguard_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $235E | b1 5a | guard_move_leftlda(zp_ptr_playfield_lo),y; bit $01: walk-left accessible? ; x-ref: $240A |
| $2360 | 29 01 | and#$01 |
| $2362 | d0 1b | bneb_237F; no: animate only if strip $10 or $50, stop when frame reaches $0C |
| $2364 | bd 0d 22 | ldaguard_sprite_strip,x |
| $2367 | c9 10 | cmp#$10 |
| $2369 | f0 04 | beqb_236F |
| $236B | c9 50 | cmp#$50 |
| $236D | d0 3b | bneb_23AA |
| $236F | bd fe 21 | b_236Fldaguard_anim_frame,x; x-ref: $2369 |
| $2372 | 38 | sec |
| $2373 | e9 04 | sbc#$04 |
| $2375 | 29 0c | and#$0c |
| $2377 | c9 0c | cmp#$0c |
| $2379 | f0 2f | beqb_23AA |
| $237B | 9d fe 21 | staguard_anim_frame,x |
| $237E | 60 | rts; rts early: animate only |
| $237F | b1 5a | b_237Flda(zp_ptr_playfield_lo),y; yes: bit $20 set → on rope → strip=$50; else strip=$10 (floor) ; x-ref: $2362 |
| $2381 | 29 20 | and#$20 |
| $2383 | f0 04 | beqb_2389 |
| $2385 | a9 50 | lda#$50 |
| $2387 | d0 02 | bneb_238B |
| $2389 | a9 10 | b_2389lda#$10; x-ref: $2383 |
| $238B | 9d 0d 22 | b_238Bstaguard_sprite_strip,x; x-ref: $2387 |
| $238E | bd fe 21 | ldaguard_anim_frame,x |
| $2391 | 38 | sec |
| $2392 | e9 04 | sbc#$04 |
| $2394 | 29 0c | and#$0c |
| $2396 | 9d fe 21 | staguard_anim_frame,x |
| $2399 | c9 0c | cmp#$0c |
| $239B | d0 0d | bneb_23AA; frame underflowed to $0C → move one col left |
| $239D | de 03 22 | decguard_offset,x |
| $23A0 | 20 1b 28 | jsrcheck_guard_guard_collision |
| $23A3 | f0 08 | beqb_23AD |
| $23A5 | 20 3c 28 | jsrcheck_guard_hole_collision |
| $23A8 | f0 03 | beqb_23AD |
| $23AA | 4c dd 23 | b_23AAjmpguard_move_epilogue; x-ref: $236D, $2379, $239B |
| $23AD | fe 03 22 | b_23ADincguard_offset,x; rollback: undo offset dec ; x-ref: $23A3, $23A8 |
| $23B0 | 4c dd 23 | jmpguard_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 |
| $23B3 | a2 04 | ldx#$04; process 5 guards ; x-ref: $0668 |
| $23B5 | bd f9 21 | b_23B5ldaguard_active,x; skip inactive guards ($FF) ; x-ref: $23DE |
| $23B8 | c9 ff | cmp#$ff |
| $23BA | f0 21 | beqguard_move_epilogue |
| $23BC | bd 21 22 | ldaguard_ai_bias,x; --- AI bias decay: drift toward 0 each tick --- |
| $23BF | f0 0f | beqb_23D0 |
| $23C1 | 30 06 | bmib_23C9 |
| $23C3 | de 21 22 | decguard_ai_bias,x; positive bias: decrement |
| $23C6 | 4c d0 23 | jmpb_23D0 |
| $23C9 | c9 ff | b_23C9cmp#$ff; negative bias: $FF is clamped floor, else increment ; x-ref: $23C1 |
| $23CB | f0 03 | beqb_23D0 |
| $23CD | fe 21 22 | incguard_ai_bias,x |
| $23D0 | de 1c 22 | b_23D0decguard_move_rate,x; --- movement timer: decrement; skip movement if not expired --- ; x-ref: $23BF, $23C6, $23CB |
| $23D3 | d0 08 | bneguard_move_epilogue |
| $23D5 | a9 02 | lda#$02; reload timer = 2 (move every 2 frames) |
| $23D7 | 9d 1c 22 | staguard_move_rate,x |
| $23DA | 4c e1 23 | jmpguard_movement_dispatch |
| $23DD | ca | guard_move_epiloguedex; x-ref: $2282, $2288, $22C5, $22CB, $230A, $2310, $2355, $235B, ... |
| $23DE | 10 d5 | bplb_23B5 |
| $23E0 | 60 | rts |
| guard_movement_dispatch |
| $23E1 | bc 08 22 | ldyguard_page,x; set grid pointer to guard_page+1 ; x-ref: $23DA |
| $23E4 | c8 | iny |
| $23E5 | 84 5b | styzp_ptr_playfield_hi |
| $23E7 | bc 03 22 | ldyguard_offset,x |
| $23EA | b1 5a | lda(zp_ptr_playfield_lo),y; bit $10 set → gravity fall |
| $23EC | 29 10 | and#$10 |
| $23EE | f0 03 | beqb_23F3 |
| $23F0 | 4c 63 22 | jmpguard_gravity_fall |
| $23F3 | bd 12 22 | b_23F3ldaguard_vspeed,x; vspeed > 0 → move down; vspeed < 0 → move up ; x-ref: $23EE |
| $23F6 | f0 08 | beqb_2400 |
| $23F8 | 30 03 | bmib_23FD |
| $23FA | 4c 8b 22 | jmpguard_move_down |
| $23FD | 4c ce 22 | b_23FDjmpguard_move_up; x-ref: $23F8 |
| $2400 | bd 17 22 | b_2400ldaguard_hspeed,x; hspeed > 0 → move right; hspeed < 0 → move left ; x-ref: $23F6 |
| $2403 | f0 08 | beqb_240D |
| $2405 | 30 03 | bmib_240A |
| $2407 | 4c 13 23 | jmpguard_move_right |
| $240A | 4c 5e 23 | b_240Ajmpguard_move_left; x-ref: $2405 |
| $240D | bd 0d 22 | b_240Dldaguard_sprite_strip,x; --- idle: vspeed=hspeed=0; animate based on strip --- ; x-ref: $2403 |
| $2410 | d0 0b | bneb_241D |
| $2412 | bd fe 21 | ldaguard_anim_frame,x; strip=$00: set bit 2 of frame (idle breathing) |
| $2415 | 09 04 | ora#$04 |
| $2417 | 9d fe 21 | staguard_anim_frame,x |
| $241A | 4c dd 23 | jmpguard_move_epilogue |
| $241D | c9 10 | b_241Dcmp#$10; strip=$10: keep only bit 3 of frame ; x-ref: $2410 |
| $241F | d0 0b | bneb_242C |
| $2421 | bd fe 21 | ldaguard_anim_frame,x |
| $2424 | 29 08 | and#$08 |
| $2426 | 9d fe 21 | staguard_anim_frame,x |
| $2429 | 4c dd 23 | jmpguard_move_epilogue |
| $242C | c9 20 | b_242Ccmp#$20; strip=$20: no idle animation ; x-ref: $241F |
| $242E | d0 03 | bneb_2433 |
| $2430 | 4c dd 23 | jmpguard_move_epilogue |
| $2433 | c9 30 | b_2433cmp#$30; strip=$30: reset frame to 0 ; x-ref: $242E |
| $2435 | d0 08 | bneb_243F |
| $2437 | a9 00 | lda#$00 |
| $2439 | 9d fe 21 | staguard_anim_frame,x |
| $243C | 4c dd 23 | jmpguard_move_epilogue |
| $243F | a9 60 | b_243Flda#$60; any other strip → switch to $60 (dead/captured) ; x-ref: $2435 |
| $2441 | 9d 0d 22 | staguard_sprite_strip,x |
| $2444 | 4c dd 23 | jmpguard_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 |
| $2447 | a2 00 | ldx#$00; x-ref: $0659 |
| $2449 | bd f9 21 | b_2449ldaguard_active,x; x-ref: $2475 |
| $244C | c9 ff | cmp#$ff |
| $244E | f0 22 | beqb_2472 |
| $2450 | bc 08 22 | ldyguard_page,x |
| $2453 | 84 6f | styzp_ptr_guard_top_hi |
| $2455 | c8 | iny |
| $2456 | 84 71 | styzp_ptr_guard_bot_hi |
| $2458 | bc 03 22 | ldyguard_offset,x |
| $245B | bd e5 21 | ldaguard_saved_top,x |
| $245E | 91 6e | sta(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer |
| $2460 | 88 | dey |
| $2461 | bd ea 21 | ldaguard_saved_bot_left,x |
| $2464 | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $2466 | c8 | iny |
| $2467 | bd ef 21 | ldaguard_saved_bot_mid,x |
| $246A | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $246C | c8 | iny |
| $246D | bd f4 21 | ldaguard_saved_bot_right,x |
| $2470 | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $2472 | e8 | b_2472inx; x-ref: $244E |
| $2473 | e0 05 | cpx#$05 |
| $2475 | d0 d2 | bneb_2449 |
| $2477 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2478 | a2 04 | draw_guardsldx#$04; x-ref: $0689, $13F8 |
| $247A | 86 76 | stxzp_guard_draw_idx; CHRGOT Entry to Get Same Byte of Text Again |
| $247C | a6 76 | b_247Cldxzp_guard_draw_idx; x-ref: $24DC CHRGOT Entry to Get Same Byte of Text Again |
| $247E | bd f9 21 | ldaguard_active,x |
| $2481 | c9 ff | cmp#$ff |
| $2483 | f0 55 | beqb_24DA |
| $2485 | bc 08 22 | ldyguard_page,x |
| $2488 | 84 6f | styzp_ptr_guard_top_hi |
| $248A | c8 | iny |
| $248B | 84 71 | styzp_ptr_guard_bot_hi |
| $248D | bc 03 22 | ldyguard_offset,x |
| $2490 | b1 6e | lda(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer |
| $2492 | 9d e5 21 | staguard_saved_top,x |
| $2495 | 88 | dey |
| $2496 | b1 70 | lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $2498 | 9d ea 21 | staguard_saved_bot_left,x |
| $249B | c8 | iny |
| $249C | b1 70 | lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $249E | 9d ef 21 | staguard_saved_bot_mid,x |
| $24A1 | c8 | iny |
| $24A2 | b1 70 | lda(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $24A4 | 9d f4 21 | staguard_saved_bot_right,x |
| $24A7 | 88 | dey |
| $24A8 | bd 0d 22 | ldaguard_sprite_strip,x |
| $24AB | 18 | clc |
| $24AC | 7d fe 21 | adcguard_anim_frame,x |
| $24AF | aa | tax |
| $24B0 | bd 75 21 | ldaguard_sprite_table,x |
| $24B3 | c9 20 | cmp#$20 |
| $24B5 | f0 02 | beqb_24B9 |
| $24B7 | 91 6e | sta(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer |
| $24B9 | e8 | b_24B9inx; x-ref: $24B5 |
| $24BA | 88 | dey |
| $24BB | bd 75 21 | ldaguard_sprite_table,x |
| $24BE | c9 20 | cmp#$20 |
| $24C0 | f0 02 | beqb_24C4 |
| $24C2 | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $24C4 | e8 | b_24C4inx; x-ref: $24C0 |
| $24C5 | c8 | iny |
| $24C6 | bd 75 21 | ldaguard_sprite_table,x |
| $24C9 | c9 20 | cmp#$20 |
| $24CB | f0 02 | beqb_24CF |
| $24CD | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $24CF | e8 | b_24CFinx; x-ref: $24CB |
| $24D0 | c8 | iny |
| $24D1 | bd 75 21 | ldaguard_sprite_table,x |
| $24D4 | c9 20 | cmp#$20 |
| $24D6 | f0 02 | beqb_24DA |
| $24D8 | 91 70 | sta(zp_ptr_guard_bot_lo),y; CHRGET Subroutine: Get Next Byte of BASIC Text |
| $24DA | c6 76 | b_24DAdeczp_guard_draw_idx; x-ref: $2483, $24D6 CHRGOT Entry to Get Same Byte of Text Again |
| $24DC | 10 9e | bplb_247C |
| $24DE | 60 | rts |
| ; 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). |
| $24DF | | gold_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. |
| $24E0 | | gold_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) |
| $24E1 | | gold_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. |
| $24F1 | | gold_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. |
| $2501 | | gold_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. |
| $2511 | | gold_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2521 | a2 0f | init_gold_arraysldx#$0f; x-ref: $070E, $0742 |
| $2523 | a9 ff | b_2523lda#$ff; x-ref: $252E |
| $2525 | 9d e1 24 | stagold_active_flag,x |
| $2528 | a9 20 | lda#$20 |
| $252A | 9d 11 25 | stagold_saved_bg,x |
| $252D | ca | dex |
| $252E | 10 f3 | bplb_2523 |
| $2530 | a9 00 | lda#$00 |
| $2532 | 85 6a | stazp_ptr_gold_screen_lo |
| $2534 | 8d e0 24 | stagold_total |
| $2537 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2538 | a2 00 | ldx#$00; x-ref: $0656 |
| $253A | bd e1 24 | b_253Aldagold_active_flag,x; x-ref: $254F |
| $253D | 30 0d | bmib_254C |
| $253F | bd f1 24 | ldagold_page,x |
| $2542 | 85 6b | stazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign |
| $2544 | bc 01 25 | ldygold_offset,x |
| $2547 | bd 11 25 | ldagold_saved_bg,x |
| $254A | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $254C | e8 | b_254Cinx; x-ref: $253D |
| $254D | e0 10 | cpx#$10 |
| $254F | d0 e9 | bneb_253A |
| $2551 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2552 | a2 0f | draw_goldldx#$0f; x-ref: $068C, $13FB |
| $2554 | bd e1 24 | b_2554ldagold_active_flag,x; x-ref: $256B |
| $2557 | 30 11 | bmib_256A |
| $2559 | bd f1 24 | ldagold_page,x |
| $255C | 85 6b | stazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign |
| $255E | bc 01 25 | ldygold_offset,x |
| $2561 | b1 6a | lda(zp_ptr_gold_screen_lo),y |
| $2563 | 9d 11 25 | stagold_saved_bg,x |
| $2566 | a9 68 | lda#$68 |
| $2568 | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $256A | ca | b_256Adex; x-ref: $2557 |
| $256B | 10 e7 | bplb_2554 |
| $256D | 60 | rts |
| ; 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 |
| $256E | | float_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. |
| $256F | | float_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). |
| $2570 | | float_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). |
| $2571 | | float_score_char0.byte$00; x-ref: $2588, $25AE |
| ; Char buffer at col+1 of floating score (BCD tens digit: (float_score_bcd >> 4) + $30). |
| $2572 | | float_score_char1.byte$00; x-ref: $258E, $25B4 |
| ; Char buffer at col+2 of floating score (BCD units digit: (float_score_bcd & $0F) + $30). |
| $2573 | | float_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. |
| $2574 | | float_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2575 | a9 ff | init_floating_scorelda#$ff; x-ref: $074B |
| $2577 | 8d 6e 25 | stafloat_score_timer |
| $257A | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $257B | ad 6e 25 | erase_floating_scoreldafloat_score_timer; x-ref: $065F |
| $257E | 30 19 | bmir_2599 |
| $2580 | ad 6f 25 | ldafloat_score_page |
| $2583 | 85 6b | stazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign |
| $2585 | ac 70 25 | ldyfloat_score_offset |
| $2588 | ad 71 25 | ldafloat_score_char0 |
| $258B | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $258D | c8 | iny |
| $258E | ad 72 25 | ldafloat_score_char1 |
| $2591 | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $2593 | c8 | iny |
| $2594 | ad 73 25 | ldafloat_score_char2 |
| $2597 | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $2599 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $259A | ad 6e 25 | tick_floating_scoreldafloat_score_timer; x-ref: $0683 |
| $259D | 30 3e | bmir_25DD |
| $259F | ce 6e 25 | decfloat_score_timer |
| $25A2 | 30 39 | bmir_25DD |
| $25A4 | ad 6f 25 | ldafloat_score_page |
| $25A7 | 85 6b | stazp_ptr_gold_screen_hi; ARGSGN Floating Accum. #2: Sign |
| $25A9 | ac 70 25 | ldyfloat_score_offset |
| $25AC | b1 6a | lda(zp_ptr_gold_screen_lo),y |
| $25AE | 8d 71 25 | stafloat_score_char0 |
| $25B1 | c8 | iny |
| $25B2 | b1 6a | lda(zp_ptr_gold_screen_lo),y |
| $25B4 | 8d 72 25 | stafloat_score_char1 |
| $25B7 | c8 | iny |
| $25B8 | b1 6a | lda(zp_ptr_gold_screen_lo),y |
| $25BA | 8d 73 25 | stafloat_score_char2 |
| $25BD | 88 | dey |
| $25BE | 88 | dey |
| $25BF | a9 2b | lda#$2b |
| $25C1 | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $25C3 | c8 | iny |
| $25C4 | ad 74 25 | ldafloat_score_bcd |
| $25C7 | 29 f0 | and#$f0 |
| $25C9 | 4a | lsra |
| $25CA | 4a | lsra |
| $25CB | 4a | lsra |
| $25CC | 4a | lsra |
| $25CD | 18 | clc |
| $25CE | 69 30 | adc#$30 |
| $25D0 | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $25D2 | c8 | iny |
| $25D3 | ad 74 25 | ldafloat_score_bcd |
| $25D6 | 29 0f | and#$0f |
| $25D8 | 18 | clc |
| $25D9 | 69 30 | adc#$30 |
| $25DB | 91 6a | sta(zp_ptr_gold_screen_lo),y |
| $25DD | 60 | r_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 |
| $25DE | a2 0f | ldx#$0f; x-ref: $0677 |
| $25E0 | bd e1 24 | b_25E0ldagold_active_flag,x; x-ref: $264B |
| $25E3 | 30 65 | bmib_264A |
| $25E5 | bd 01 25 | ldagold_offset,x |
| $25E8 | cd b4 0d | cmpdraw_offset |
| $25EB | d0 5d | bneb_264A |
| $25ED | bd f1 24 | ldagold_page,x |
| $25F0 | 38 | sec |
| $25F1 | e9 01 | sbc#$01 |
| $25F3 | cd b5 0d | cmpdraw_page |
| $25F6 | d0 52 | bneb_264A |
| $25F8 | de e1 24 | decgold_active_flag,x |
| $25FB | de e1 24 | decgold_active_flag,x |
| $25FE | ee df 24 | incgold_count |
| $2601 | 20 c7 0c | jsrtrigger_collection_sound |
| $2604 | a9 50 | lda#$50 |
| $2606 | 8d 6e 25 | stafloat_score_timer |
| $2609 | ad 49 29 | ldagame_timer_bcd |
| $260C | 8d 74 25 | stafloat_score_bcd |
| $260F | bd f1 24 | ldagold_page,x |
| $2612 | 8d 6f 25 | stafloat_score_page |
| $2615 | bd 01 25 | ldagold_offset,x |
| $2618 | 8d 70 25 | stafloat_score_offset |
| $261B | ce 70 25 | decfloat_score_offset |
| $261E | f8 | sed |
| $261F | ad 49 29 | ldagame_timer_bcd |
| $2622 | 18 | clc |
| $2623 | 6d 48 29 | adcscore_penalty_low_bcd |
| $2626 | 8d 48 29 | stascore_penalty_low_bcd |
| $2629 | 90 09 | bccb_2634 |
| $262B | ad 47 29 | ldascore_penalty_high_bcd |
| $262E | 18 | clc |
| $262F | 69 01 | adc#$01 |
| $2631 | 8d 47 29 | stascore_penalty_high_bcd |
| $2634 | ad 49 29 | b_2634ldagame_timer_bcd; x-ref: $2629 |
| $2637 | 18 | clc |
| $2638 | 6d 46 29 | adcscore_low_bcd |
| $263B | 8d 46 29 | stascore_low_bcd |
| $263E | 90 09 | bccb_2649 |
| $2640 | ad 45 29 | ldascore_high_bcd |
| $2643 | 18 | clc |
| $2644 | 69 01 | adc#$01 |
| $2646 | 8d 45 29 | stascore_high_bcd |
| $2649 | d8 | b_2649cld; x-ref: $263E |
| $264A | ca | b_264Adex; x-ref: $25E3, $25EB, $25F6 |
| $264B | 10 93 | bplb_25E0 |
| $264D | ad df 24 | ldagold_count |
| $2650 | cd e0 24 | cmpgold_total |
| $2653 | d0 03 | bner_2658 |
| $2655 | 20 fc 2a | jsrreset_bonus_timer |
| $2658 | 60 | r_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 |
| $2659 | ad e6 2a | ldabonus_timer; x-ref: $0650 |
| $265C | c9 ff | cmp#$ff |
| $265E | f0 62 | beqr_26C2 |
| $2660 | ad b5 0d | ldadraw_page |
| $2663 | cd e3 2a | cmpexit_page |
| $2666 | d0 5a | bner_26C2 |
| $2668 | ad b4 0d | ldadraw_offset |
| $266B | cd e2 2a | cmpexit_offset |
| $266E | d0 52 | bner_26C2 |
| $2670 | a9 50 | lda#$50 |
| $2672 | 8d 6e 25 | stafloat_score_timer |
| $2675 | ad 49 29 | ldagame_timer_bcd |
| $2678 | 8d 74 25 | stafloat_score_bcd |
| $267B | ad b5 0d | ldadraw_page |
| $267E | 8d 6f 25 | stafloat_score_page |
| $2681 | ee 6f 25 | incfloat_score_page |
| $2684 | ad b4 0d | ldadraw_offset |
| $2687 | 8d 70 25 | stafloat_score_offset |
| $268A | ce 70 25 | decfloat_score_offset |
| $268D | ce 70 25 | decfloat_score_offset |
| $2690 | ce 70 25 | decfloat_score_offset |
| $2693 | f8 | sed |
| $2694 | ad 49 29 | ldagame_timer_bcd |
| $2697 | 18 | clc |
| $2698 | 6d 46 29 | adcscore_low_bcd |
| $269B | 8d 46 29 | stascore_low_bcd |
| $269E | 90 09 | bccb_26A9 |
| $26A0 | ad 45 29 | ldascore_high_bcd |
| $26A3 | 18 | clc |
| $26A4 | 69 01 | adc#$01 |
| $26A6 | 8d 45 29 | stascore_high_bcd |
| $26A9 | ad 49 29 | b_26A9ldagame_timer_bcd; x-ref: $269E |
| $26AC | 18 | clc |
| $26AD | 6d 48 29 | adcscore_penalty_low_bcd |
| $26B0 | 8d 48 29 | stascore_penalty_low_bcd |
| $26B3 | 90 09 | bccb_26BE |
| $26B5 | ad 47 29 | ldascore_penalty_high_bcd |
| $26B8 | 18 | clc |
| $26B9 | 69 01 | adc#$01 |
| $26BB | 8d 47 29 | stascore_penalty_high_bcd |
| $26BE | d8 | b_26BEcld; x-ref: $26B3 |
| $26BF | ee e1 2a | inclevel_complete_flag |
| $26C2 | 60 | r_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $26C3 | ad e1 2a | check_level_completeldalevel_complete_flag; x-ref: $06A1 |
| $26C6 | c9 01 | cmp#$01 |
| $26C8 | d0 17 | bner_26E1 |
| $26CA | 20 f9 0c | jsrplay_victory_jingle |
| $26CD | 20 ac 29 | jsrupdate_sidebar_status |
| $26D0 | 20 27 09 | jsrblit_sidebar |
| $26D3 | a2 20 | ldx#$20 |
| $26D5 | 20 c8 09 | b_26D5jsrbusy_wait_pause; x-ref: $26D9 |
| $26D8 | ca | dex |
| $26D9 | 10 fa | bplb_26D5 |
| $26DB | 20 dd 09 | jsrtransition_from_full_to_empty |
| $26DE | 20 c8 06 | jsradvance_level |
| $26E1 | 60 | r_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 |
| $26E2 | a2 0f | ldx#$0f; x-ref: $067A |
| $26E4 | bd e1 24 | b_26E4ldagold_active_flag,x; x-ref: $2722 |
| $26E7 | 30 38 | bmib_2721 |
| $26E9 | a0 04 | ldy#$04 |
| $26EB | b9 f9 21 | b_26EBldaguard_active,y; x-ref: $271F |
| $26EE | c9 ff | cmp#$ff |
| $26F0 | f0 2c | beqb_271E |
| $26F2 | b9 21 22 | ldaguard_ai_bias,y |
| $26F5 | f0 03 | beqb_26FA |
| $26F7 | 4c 1e 27 | jmpb_271E |
| $26FA | bd f1 24 | b_26FAldagold_page,x; x-ref: $26F5 |
| $26FD | 38 | sec |
| $26FE | e9 01 | sbc#$01 |
| $2700 | d9 08 22 | cmpguard_page,y |
| $2703 | d0 19 | bneb_271E |
| $2705 | bd 01 25 | ldagold_offset,x |
| $2708 | d9 03 22 | cmpguard_offset,y |
| $270B | d0 11 | bneb_271E |
| $270D | a9 ff | lda#$ff |
| $270F | 9d e1 24 | stagold_active_flag,x |
| $2712 | a9 90 | lda#$90 |
| $2714 | 99 21 22 | staguard_ai_bias,y |
| $2717 | 8a | txa |
| $2718 | 99 26 22 | staguard_hole_status,y |
| $271B | 4c 21 27 | jmpb_2721 |
| $271E | 88 | b_271Edey; x-ref: $26F0, $26F7, $2703, $270B |
| $271F | 10 ca | bplb_26EB |
| $2721 | ca | b_2721dex; x-ref: $26E7, $271B |
| $2722 | 10 c0 | bplb_26E4 |
| $2724 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2725 | a2 04 | ldx#$04; x-ref: $0680 |
| $2727 | bd f9 21 | b_2727ldaguard_active,x; x-ref: $2776 |
| $272A | c9 ff | cmp#$ff |
| $272C | f0 47 | beqb_2775 |
| $272E | bd 21 22 | ldaguard_ai_bias,x |
| $2731 | c9 ff | cmp#$ff |
| $2733 | d0 40 | bneb_2775 |
| $2735 | 20 72 07 | jsrget_random |
| $2738 | c9 04 | cmp#$04 |
| $273A | b0 39 | bcsb_2775 |
| $273C | bd 08 22 | ldaguard_page,x |
| $273F | 85 5b | stazp_ptr_playfield_hi |
| $2741 | e6 5b | inczp_ptr_playfield_hi |
| $2743 | bd 03 22 | ldaguard_offset,x |
| $2746 | a8 | tay |
| $2747 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2749 | 29 03 | and#$03 |
| $274B | f0 28 | beqb_2775 |
| $274D | a5 5b | ldazp_ptr_playfield_hi |
| $274F | 85 6f | stazp_ptr_guard_top_hi |
| $2751 | b1 6e | lda(zp_ptr_guard_top_lo),y; FBUFPT Pointer: Cassette Buffer |
| $2753 | c9 20 | cmp#$20 |
| $2755 | d0 1e | bneb_2775 |
| $2757 | bd 26 22 | ldaguard_hole_status,x |
| $275A | a8 | tay |
| $275B | a9 00 | lda#$00 |
| $275D | 99 e1 24 | stagold_active_flag,y |
| $2760 | bd 03 22 | ldaguard_offset,x |
| $2763 | 99 01 25 | stagold_offset,y |
| $2766 | bd 08 22 | ldaguard_page,x |
| $2769 | 18 | clc |
| $276A | 69 01 | adc#$01 |
| $276C | 99 f1 24 | stagold_page,y |
| $276F | a9 7f | lda#$7f |
| $2771 | 9d 21 22 | staguard_ai_bias,x |
| $2774 | 60 | rts |
| $2775 | 88 | b_2775dey; x-ref: $272C, $2733, $273A, $274B, $2755 |
| $2776 | 10 af | bplb_2727 |
| $2778 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2779 | a2 04 | ldx#$04; x-ref: $067D |
| $277B | bd f9 21 | b_277Bldaguard_active,x; x-ref: $2781 |
| $277E | f0 04 | beqb_2784 |
| $2780 | ca | b_2780dex; x-ref: $2792, $27B4, $27BA, $2808, $2818 |
| $2781 | 10 f8 | bplb_277B |
| $2783 | 60 | rts |
| $2784 | bd 08 22 | b_2784ldaguard_page,x; x-ref: $277E |
| $2787 | 85 5b | stazp_ptr_playfield_hi |
| $2789 | e6 5b | inczp_ptr_playfield_hi |
| $278B | bc 03 22 | ldyguard_offset,x |
| $278E | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2790 | 29 20 | and#$20 |
| $2792 | d0 ec | bneb_2780 |
| $2794 | a0 09 | ldy#$09 |
| $2796 | b9 43 1e | b_2796ldadig_slot_status,y; x-ref: $27B2 |
| $2799 | 30 16 | bmib_27B1 |
| $279B | b9 57 1e | ldadig_slot_col,y |
| $279E | dd 03 22 | cmpguard_offset,x |
| $27A1 | d0 0e | bneb_27B1 |
| $27A3 | b9 4d 1e | ldadig_slot_page,y |
| $27A6 | 38 | sec |
| $27A7 | e9 02 | sbc#$02 |
| $27A9 | dd 08 22 | cmpguard_page,x |
| $27AC | d0 03 | bneb_27B1 |
| $27AE | 4c b7 27 | jmpj_27B7 |
| $27B1 | 88 | b_27B1dey; x-ref: $2799, $27A1, $27AC |
| $27B2 | 10 e2 | bplb_2796 |
| $27B4 | 4c 80 27 | jmpb_2780 |
| $27B7 | b9 93 1e | j_27B7ldadig_slot_hole_idx,y; x-ref: $27AE |
| $27BA | 10 c4 | bplb_2780 |
| $27BC | b9 43 1e | ldadig_slot_status,y |
| $27BF | c9 02 | cmp#$02 |
| $27C1 | 90 48 | bccb_280B |
| $27C3 | 86 64 | stxzp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer |
| $27C5 | bd 21 22 | ldaguard_ai_bias,x |
| $27C8 | 10 18 | bplb_27E2 |
| $27CA | bd 26 22 | ldaguard_hole_status,x |
| $27CD | aa | tax |
| $27CE | a9 00 | lda#$00 |
| $27D0 | 9d e1 24 | stagold_active_flag,x |
| $27D3 | b9 57 1e | ldadig_slot_col,y |
| $27D6 | 9d 01 25 | stagold_offset,x |
| $27D9 | b9 4d 1e | ldadig_slot_page,y |
| $27DC | 9d f1 24 | stagold_page,x |
| $27DF | de f1 24 | decgold_page,x |
| $27E2 | a6 64 | b_27E2ldxzp_scratch_reg_a; x-ref: $27C8 SGNFLG Pointer: Series Evaluation Constant Pointer |
| $27E4 | b9 57 1e | ldadig_slot_col,y |
| $27E7 | 9d da 2b | stahole_slot_offset,x |
| $27EA | b9 4d 1e | ldadig_slot_page,y |
| $27ED | 9d d5 2b | stahole_slot_page,x |
| $27F0 | de d5 2b | dechole_slot_page,x |
| $27F3 | de d5 2b | dechole_slot_page,x |
| $27F6 | 98 | tya |
| $27F7 | 9d e9 2b | stahole_slot_actor,x |
| $27FA | 8a | txa |
| $27FB | 99 93 1e | stadig_slot_hole_idx,y |
| $27FE | a9 ff | lda#$ff |
| $2800 | 9d d0 2b | stahole_slot_state,x |
| $2803 | a9 ff | lda#$ff |
| $2805 | 9d f9 21 | staguard_active,x |
| $2808 | 4c 80 27 | jmpb_2780 |
| $280B | a9 04 | b_280Blda#$04; x-ref: $27C1 |
| $280D | 99 43 1e | stadig_slot_status,y |
| $2810 | a9 00 | lda#$00 |
| $2812 | 8d 48 e8 | sta$e848; Timer 2 LO |
| $2815 | 8d 7a 0c | stasound_active_flag |
| $2818 | 4c 80 27 | jmpb_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 |
| $281B | a0 04 | ldy#$04; x-ref: $2278, $22BB, $2300, $234B, $23A0 |
| $281D | 84 64 | b_281Dstyzp_scratch_reg_a; x-ref: $2839 SGNFLG Pointer: Series Evaluation Constant Pointer |
| $281F | e4 64 | cpxzp_scratch_reg_a; SGNFLG Pointer: Series Evaluation Constant Pointer |
| $2821 | f0 15 | beqb_2838 |
| $2823 | b9 f9 21 | ldaguard_active,y |
| $2826 | 30 10 | bmib_2838 |
| $2828 | bd 03 22 | ldaguard_offset,x |
| $282B | d9 03 22 | cmpguard_offset,y |
| $282E | d0 08 | bneb_2838 |
| $2830 | bd 08 22 | ldaguard_page,x |
| $2833 | d9 08 22 | cmpguard_page,y |
| $2836 | f0 03 | beqr_283B |
| $2838 | 88 | b_2838dey; x-ref: $2821, $2826, $282E |
| $2839 | 10 e2 | bplb_281D |
| $283B | 60 | r_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 |
| $283C | a0 04 | ldy#$04; x-ref: $227D, $22C0, $2305, $2350, $23A5 |
| $283E | b9 d0 2b | b_283Eldahole_slot_state,y; x-ref: $285C |
| $2841 | f0 18 | beqb_285B |
| $2843 | bd 03 22 | ldaguard_offset,x |
| $2846 | d9 da 2b | cmphole_slot_offset,y |
| $2849 | d0 10 | bneb_285B |
| $284B | bd 08 22 | ldaguard_page,x |
| $284E | d9 d5 2b | cmphole_slot_page,y |
| $2851 | f0 0b | beqr_285E |
| $2853 | 18 | clc |
| $2854 | 69 01 | adc#$01 |
| $2856 | d9 d5 2b | cmphole_slot_page,y |
| $2859 | f0 03 | beqr_285E |
| $285B | 88 | b_285Bdey; x-ref: $2841, $2849 |
| $285C | 10 e0 | bplb_283E |
| $285E | 60 | r_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 |
| $285F | a0 04 | ldy#$04; x-ref: $0695 |
| $2861 | b9 f9 21 | b_2861ldaguard_active,y; x-ref: $2889 |
| $2864 | 30 22 | bmib_2888 |
| $2866 | ad b4 0d | ldadraw_offset |
| $2869 | d9 03 22 | cmpguard_offset,y |
| $286C | d0 1a | bneb_2888 |
| $286E | ad b5 0d | ldadraw_page |
| $2871 | d9 08 22 | cmpguard_page,y |
| $2874 | d0 12 | bneb_2888 |
| $2876 | 20 0b 0d | jsrplay_death_sound |
| $2879 | a2 40 | ldx#$40 |
| $287B | 20 c8 09 | b_287Bjsrbusy_wait_pause; x-ref: $287F |
| $287E | ca | dex |
| $287F | 10 fa | bplb_287B |
| $2881 | 20 dd 09 | jsrtransition_from_full_to_empty |
| $2884 | 20 11 07 | jsrhandle_player_death |
| $2887 | 60 | rts |
| $2888 | 88 | b_2888dey; x-ref: $2864, $286C, $2874 |
| $2889 | 10 d6 | bplb_2861 |
| $288B | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $288C | a0 04 | ldy#$04; x-ref: $069B |
| $288E | b9 d0 2b | b_288Eldahole_slot_state,y; x-ref: $28B6 |
| $2891 | f0 22 | beqb_28B5 |
| $2893 | ad b4 0d | ldadraw_offset |
| $2896 | d9 da 2b | cmphole_slot_offset,y |
| $2899 | d0 1a | bneb_28B5 |
| $289B | ad b5 0d | ldadraw_page |
| $289E | d9 d5 2b | cmphole_slot_page,y |
| $28A1 | d0 12 | bneb_28B5 |
| $28A3 | 20 0b 0d | jsrplay_death_sound |
| $28A6 | a2 10 | ldx#$10 |
| $28A8 | 20 c8 09 | b_28A8jsrbusy_wait_pause; x-ref: $28AC |
| $28AB | ca | dex |
| $28AC | 10 fa | bplb_28A8 |
| $28AE | 20 dd 09 | jsrtransition_from_full_to_empty |
| $28B1 | 20 11 07 | jsrhandle_player_death |
| $28B4 | 60 | rts |
| $28B5 | 88 | b_28B5dey; x-ref: $2891, $2899, $28A1 |
| $28B6 | 10 d6 | bplb_288E |
| $28B8 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $28B9 | a2 09 | ldx#$09; x-ref: $069E |
| $28BB | bd 43 1e | b_28BBldadig_slot_status,x; x-ref: $28E5 |
| $28BE | c9 04 | cmp#$04 |
| $28C0 | d0 22 | bneb_28E4 |
| $28C2 | bd 4d 1e | ldadig_slot_page,x |
| $28C5 | cd b5 0d | cmpdraw_page |
| $28C8 | d0 1a | bneb_28E4 |
| $28CA | bd 57 1e | ldadig_slot_col,x |
| $28CD | cd b4 0d | cmpdraw_offset |
| $28D0 | d0 12 | bneb_28E4 |
| $28D2 | 20 0b 0d | jsrplay_death_sound |
| $28D5 | a0 08 | ldy#$08 |
| $28D7 | 20 c8 09 | b_28D7jsrbusy_wait_pause; x-ref: $28DB |
| $28DA | 88 | dey |
| $28DB | 10 fa | bplb_28D7 |
| $28DD | 20 dd 09 | jsrtransition_from_full_to_empty |
| $28E0 | 20 11 07 | jsrhandle_player_death |
| $28E3 | 60 | rts |
| $28E4 | ca | b_28E4dex; x-ref: $28C0, $28C8, $28D0 |
| $28E5 | 10 d4 | bplb_28BB |
| $28E7 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $28E8 | ad b5 0d | ldadraw_page; x-ref: $0698 |
| $28EB | 85 5b | stazp_ptr_playfield_hi |
| $28ED | e6 5b | inczp_ptr_playfield_hi |
| $28EF | ac b4 0d | ldydraw_offset |
| $28F2 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $28F4 | f0 05 | beqb_28FB |
| $28F6 | 29 c0 | and#$c0 |
| $28F8 | d0 01 | bneb_28FB |
| $28FA | 60 | rts |
| $28FB | 20 0b 0d | b_28FBjsrplay_death_sound; x-ref: $28F4, $28F8 |
| $28FE | a0 08 | ldy#$08 |
| $2900 | 20 c8 09 | b_2900jsrbusy_wait_pause; x-ref: $2904 |
| $2903 | 88 | dey |
| $2904 | 10 fa | bplb_2900 |
| $2906 | 20 dd 09 | jsrtransition_from_full_to_empty |
| $2909 | 20 11 07 | jsrhandle_player_death |
| $290C | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $290D | a5 2e | check_pause_keyldazp_key_row_pause; x-ref: $06A4 STREND Pointer End of BASIC Arrays (+1) |
| $290F | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $2912 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $2915 | 25 37 | andzp_key_col_pause |
| $2917 | d0 03 | bner_291C |
| $2919 | 20 38 36 | jsropen_pause_menu |
| $291C | 60 | r_291Crts; x-ref: $2917 |
| | .encode |
| | .enc"screen" |
| $291D | | txt_level.text"LEVEL", $ff; x-ref: $294D |
| $2923 | | txt_gold.text"GOLD", $ff; x-ref: $295D |
| $2928 | | txt_timer.text"TIMER", $ff; x-ref: $296D |
| $292E | | txt_points.text"POINTS", $ff; x-ref: $297D |
| $2935 | | txt_level_points.text"-LEVEL", $ff; x-ref: $298D |
| $293C | | txt_total_points.text"-TOTAL", $ff; x-ref: $299D |
| | .endencode |
| $2943 | | hi_score_high_bcd.byte$20; x-ref: $2EF9, $2F39, $2F69, $2F78, $3407, $3416 |
| $2944 | | hi_score_low_bcd.byte$21; x-ref: $2F06, $2F3F, $2F83, $2F92, $3421, $3430 |
| $2945 | | score_high_bcd.byte$00; x-ref: $06B8, $0722, $0728, $2640, $2646, $26A0, $26A6, $2A7E, ... |
| $2946 | | score_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, ... |
| $2949 | | game_timer_bcd.byte$00; x-ref: $0703, $2609, $261F, $2634, $2675, $2694, $26A9, $2A1D, ... |
| $294A | | timer_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $294B | a2 00 | draw_ui_text_labelsldx#$00; x-ref: $075A |
| $294D | bd 1d 29 | j_294Dldatxt_level,x; x-ref: $2958 |
| $2950 | c9 ff | cmp#$ff |
| $2952 | f0 07 | beqb_295B |
| $2954 | 9d 23 69 | staplayfield_row_2_col_34,x; Write 'LEVEL' static text to sidebar (row 2, column 34) |
| $2957 | e8 | inx |
| $2958 | 4c 4d 29 | jmpj_294D |
| $295B | a2 00 | b_295Bldx#$00; x-ref: $2952 |
| $295D | bd 23 29 | j_295Dldatxt_gold,x; x-ref: $2968 |
| $2960 | c9 ff | cmp#$ff |
| $2962 | f0 07 | beqb_296B |
| $2964 | 9d 23 6d | staplayfield_row_6_col_34,x; Write 'GOLD' static text to sidebar (row 6, column 34) |
| $2967 | e8 | inx |
| $2968 | 4c 5d 29 | jmpj_295D |
| $296B | a2 00 | b_296Bldx#$00; x-ref: $2962 |
| $296D | bd 28 29 | j_296Dldatxt_timer,x; x-ref: $2978 |
| $2970 | c9 ff | cmp#$ff |
| $2972 | f0 07 | beqb_297B |
| $2974 | 9d 23 71 | staplayfield_row_10_col_34,x; Write 'TIME' static text to sidebar (row 10, column 34) |
| $2977 | e8 | inx |
| $2978 | 4c 6d 29 | jmpj_296D |
| $297B | a2 00 | b_297Bldx#$00; x-ref: $2972 |
| $297D | bd 2e 29 | j_297Dldatxt_points,x; x-ref: $2988 |
| $2980 | c9 ff | cmp#$ff |
| $2982 | f0 07 | beqb_298B |
| $2984 | 9d 23 75 | staplayfield_row_14_col_34,x; Write 'POINTS' static text to sidebar (row 14, column 34) |
| $2987 | e8 | inx |
| $2988 | 4c 7d 29 | jmpj_297D |
| $298B | a2 00 | b_298Bldx#$00; x-ref: $2982 |
| $298D | bd 35 29 | j_298Dldatxt_level_points,x; x-ref: $2998 |
| $2990 | c9 ff | cmp#$ff |
| $2992 | f0 07 | beqb_299B |
| $2994 | 9d 23 77 | staplayfield_row_16_col_34,x; Write '-LEVEL' static text to sidebar (row 16, column 34) |
| $2997 | e8 | inx |
| $2998 | 4c 8d 29 | jmpj_298D |
| $299B | a2 00 | b_299Bldx#$00; x-ref: $2992 |
| $299D | bd 3c 29 | j_299Dldatxt_total_points,x; x-ref: $29A8 |
| $29A0 | c9 ff | cmp#$ff |
| $29A2 | f0 07 | beqr_29AB |
| $29A4 | 9d 23 7a | staplayfield_row_19_col_34,x; Write '-TOTAL' static text to sidebar (row 19, column 34) |
| $29A7 | e8 | inx |
| $29A8 | 4c 9d 29 | jmpj_299D |
| $29AB | 60 | r_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 |
| $29AC | ad df 2a | ldaui_level_num; --- section 1: level number --- ; x-ref: $0626, $26CD |
| $29AF | c9 0a | cmp#$0a |
| $29B1 | f0 0b | beqb_29BE; level == 10? use two-digit path |
| $29B3 | 29 0f | and#$0f; levels 1-9: low nibble + $30 -> PETSCII digit |
| $29B5 | 18 | clc |
| $29B6 | 69 30 | adc#$30 |
| $29B8 | 8d 26 6a | stahud_level_units; store units digit |
| $29BB | 4c c8 29 | jmpj_29C8 |
| $29BE | a9 31 | b_29BElda#$31; level 10: '1' tens, '0' units ; x-ref: $29B1 |
| $29C0 | 8d 25 6a | stahud_level_tens |
| $29C3 | a9 30 | lda#$30 |
| $29C5 | 8d 26 6a | stahud_level_units |
| $29C8 | ad e0 24 | j_29C8ldagold_total; --- section 2: gold progress "count/total" --- total >= 10? two-digit path ; x-ref: $29BB |
| $29CB | c9 0a | cmp#$0a |
| $29CD | b0 1a | bcsb_29E9 |
| $29CF | ad df 24 | ldagold_count; total < 10: count digit at a6E25 |
| $29D2 | 18 | clc |
| $29D3 | 69 30 | adc#$30 |
| $29D5 | 8d 25 6e | stahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00) |
| $29D8 | a9 2f | lda#$2f; total digit at a6E27 |
| $29DA | 8d 26 6e | stahud_gold_slash; Gold HUD display separator '/' (ASCII $2F) |
| $29DD | ad e0 24 | ldagold_total |
| $29E0 | 18 | clc |
| $29E1 | 69 30 | adc#$30 |
| $29E3 | 8d 27 6e | stahud_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) |
| $29E6 | 4c 18 2a | jmpj_2A18 |
| $29E9 | a9 31 | b_29E9lda#$31; total >= 10: tens='1' at a6E27, units=total-10 at a6E28 ; x-ref: $29CD |
| $29EB | 8d 27 6e | stahud_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) |
| $29EE | ad e0 24 | ldagold_total |
| $29F1 | 38 | sec |
| $29F2 | e9 0a | sbc#$0a |
| $29F4 | 18 | clc |
| $29F5 | 69 30 | adc#$30 |
| $29F7 | 8d 28 6e | stahud_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) |
| $29FA | ad df 24 | ldagold_count; count >= 10? tens='1' at hud_gold_count_tens, units at hud_gold_count_units |
| $29FD | c9 0a | cmp#$0a |
| $29FF | b0 09 | bcsb_2A0A |
| $2A01 | 18 | clc |
| $2A02 | 69 30 | adc#$30 |
| $2A04 | 8d 25 6e | stahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00) |
| $2A07 | 4c 18 2a | jmpj_2A18 |
| $2A0A | 38 | b_2A0Asec; x-ref: $29FF |
| $2A0B | e9 0a | sbc#$0a |
| $2A0D | 18 | clc |
| $2A0E | 69 30 | adc#$30 |
| $2A10 | 8d 25 6e | stahud_gold_count_units; HUD gold progress: count units digit (offset $25 from Row 7 base $6E00) |
| $2A13 | a9 31 | lda#$31 |
| $2A15 | 8d 24 6e | stahud_gold_count_tens; HUD gold progress: count tens digit (offset $24 from Row 7 base $6E00) |
| $2A18 | a9 2f | j_2A18lda#$2f; Write '/' separator to hud_gold_sep ; x-ref: $29E6, $2A07 |
| $2A1A | 8d 26 6e | stahud_gold_slash; HUD gold progress: '/' separator (offset $26 from Row 7 base $6E00); Gold HUD display separator '/' (ASCII $2F) |
| $2A1D | ad 49 29 | ldagame_timer_bcd; --- section 3: game timer (2-digit BCD) --- clamp: $51 displayed as $50 |
| $2A20 | c9 51 | cmp#$51 |
| $2A22 | d0 02 | bneb_2A26 |
| $2A24 | a9 50 | lda#$50 |
| $2A26 | 29 0f | b_2A26and#$0f; low nibble -> units digit at a7226 ; x-ref: $2A22 |
| $2A28 | 18 | clc |
| $2A29 | 69 30 | adc#$30 |
| $2A2B | 8d 26 72 | staplayfield_row_11_col_37; Store converted units digit to HUD timer |
| $2A2E | ad 49 29 | ldagame_timer_bcd; high nibble (>>4) -> tens digit at a7225 |
| $2A31 | 4a | lsra |
| $2A32 | 4a | lsra |
| $2A33 | 4a | lsra |
| $2A34 | 4a | lsra |
| $2A35 | 18 | clc |
| $2A36 | 69 30 | adc#$30 |
| $2A38 | 8d 25 72 | stahud_timer_tens; Store converted tens digit to HUD timer |
| $2A3B | ad 47 29 | ldascore_penalty_high_bcd; --- Section 4: death-penalty score (score_penalty_high/low_bcd → a7824-27) --- |
| $2A3E | 4a | lsra; 4x LSR = extract high nibble (tens-thousands digit) |
| $2A3F | 4a | lsra |
| $2A40 | 4a | lsra |
| $2A41 | 4a | lsra |
| $2A42 | 18 | clc |
| $2A43 | 69 30 | adc#$30; +$30 = convert BCD nibble to PETSCII digit |
| $2A45 | 8d 24 78 | stahud_penalty_ten_thousands; HUD penalty: ten-thousands digit |
| $2A48 | ad 47 29 | ldascore_penalty_high_bcd |
| $2A4B | 29 0f | and#$0f; mask low nibble (thousands digit) |
| $2A4D | 18 | clc |
| $2A4E | 69 30 | adc#$30 |
| $2A50 | 8d 25 78 | stahud_penalty_thousands; HUD penalty: thousands digit |
| $2A53 | ad 48 29 | ldascore_penalty_low_bcd; low byte of penalty (hundreds + tens digits) |
| $2A56 | 4a | lsra |
| $2A57 | 4a | lsra |
| $2A58 | 4a | lsra |
| $2A59 | 4a | lsra |
| $2A5A | 18 | clc |
| $2A5B | 69 30 | adc#$30 |
| $2A5D | 8d 26 78 | stahud_penalty_hundreds; HUD penalty: hundreds digit |
| $2A60 | ad 48 29 | ldascore_penalty_low_bcd |
| $2A63 | 29 0f | and#$0f; tens digit → sidebar penalty display |
| $2A65 | 18 | clc |
| $2A66 | 69 30 | adc#$30 |
| $2A68 | 8d 27 78 | stahud_penalty_tens; HUD penalty: tens digit (lowest active digit, units is static '0') |
| $2A6B | a2 00 | ldx#$00; leading-zero suppression: scan left to right |
| $2A6D | bd 24 78 | b_2A6Dldahud_penalty_ten_thousands,x; check penalty digit[x] ; x-ref: $2A7C |
| $2A70 | c9 30 | cmp#$30; is it PETSCII '0'? |
| $2A72 | d0 0a | bneb_2A7E; non-zero digit: stop suppressing |
| $2A74 | a9 20 | lda#$20; replace leading '0' with space |
| $2A76 | 9d 24 78 | stahud_penalty_ten_thousands,x |
| $2A79 | e8 | inx |
| $2A7A | e0 03 | cpx#$03; stop before last digit (always show at least the units digit) |
| $2A7C | d0 ef | bneb_2A6D |
| $2A7E | ad 45 29 | b_2A7Eldascore_high_bcd; --- Section 5: current score (score_high/low_bcd → a7B24-27) --- ; x-ref: $2A72 |
| $2A81 | 4a | lsra; 4x LSR = extract high nibble (ten-thousands digit) |
| $2A82 | 4a | lsra |
| $2A83 | 4a | lsra |
| $2A84 | 4a | lsra |
| $2A85 | 18 | clc |
| $2A86 | 69 30 | adc#$30; +$30 = convert BCD nibble to PETSCII digit |
| $2A88 | 8d 24 7b | stahud_score_ten_thousands; ten-thousands digit → sidebar score display |
| $2A8B | ad 45 29 | ldascore_high_bcd |
| $2A8E | 29 0f | and#$0f; mask low nibble (thousands digit) |
| $2A90 | 18 | clc |
| $2A91 | 69 30 | adc#$30 |
| $2A93 | 8d 25 7b | stahud_score_thousands; thousands digit → sidebar score display |
| $2A96 | ad 46 29 | ldascore_low_bcd; low byte of score (hundreds + tens digits) |
| $2A99 | 4a | lsra |
| $2A9A | 4a | lsra |
| $2A9B | 4a | lsra |
| $2A9C | 4a | lsra |
| $2A9D | 18 | clc |
| $2A9E | 69 30 | adc#$30 |
| $2AA0 | 8d 26 7b | stahud_score_hundreds; hundreds digit → sidebar score display |
| $2AA3 | ad 46 29 | ldascore_low_bcd |
| $2AA6 | 29 0f | and#$0f; tens digit → sidebar score display |
| $2AA8 | 18 | clc |
| $2AA9 | 69 30 | adc#$30 |
| $2AAB | 8d 27 7b | staplayfield_row_20_col_39; units digit → sidebar score display |
| $2AAE | a2 00 | ldx#$00; leading-zero suppression: scan left to right |
| $2AB0 | bd 24 7b | b_2AB0ldahud_score_ten_thousands,x; check score digit[x] ; x-ref: $2ABF |
| $2AB3 | c9 30 | cmp#$30; is it PETSCII '0'? |
| $2AB5 | d0 0a | bner_2AC1; non-zero digit: stop suppressing |
| $2AB7 | a9 20 | lda#$20; replace leading '0' with space |
| $2AB9 | 9d 24 7b | stahud_score_ten_thousands,x; Replace leading zero with a space |
| $2ABC | e8 | inx |
| $2ABD | e0 03 | cpx#$03; stop before last digit (always show at least the units digit) |
| $2ABF | d0 ef | bneb_2AB0 |
| $2AC1 | 60 | r_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). |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2AC2 | ad 49 29 | update_game_timerldagame_timer_bcd; x-ref: $064D |
| $2AC5 | c9 10 | cmp#$10 |
| $2AC7 | f0 15 | beqr_2ADE |
| $2AC9 | ce 4a 29 | dectimer_frame_counter |
| $2ACC | 10 10 | bplr_2ADE |
| $2ACE | a9 32 | lda#$32 |
| $2AD0 | 8d 4a 29 | statimer_frame_counter |
| $2AD3 | f8 | sed |
| $2AD4 | ad 49 29 | ldagame_timer_bcd |
| $2AD7 | 38 | sec |
| $2AD8 | e9 01 | sbc#$01 |
| $2ADA | 8d 49 29 | stagame_timer_bcd |
| $2ADD | d8 | cld |
| $2ADE | 60 | r_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. |
| $2ADF | | ui_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. |
| $2AE0 | | level_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. |
| $2AE1 | | level_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. |
| $2AE2 | | exit_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. |
| $2AE3 | | exit_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). |
| $2AE4 | | guard_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. |
| $2AE5 | | guard_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. |
| $2AE6 | | bonus_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. |
| $2AE7 | | bonus_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. |
| $2AE8 | | bonus_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2AE9 | a9 ff | init_bonus_sequencerlda#$ff; $FF = disarmed sentinel for all bonus fields ; x-ref: $074E |
| $2AEB | 8d e6 2a | stabonus_timer; play_bonus_audio_tick bails on bit-7 set |
| $2AEE | 8d e7 2a | stabonus_walk_offset; no walk column set yet |
| $2AF1 | 8d e8 2a | stabonus_walk_page; no walk row set yet |
| $2AF4 | 8d e1 2a | stalevel_complete_flag; needs $FF→$00→$01 before check_level_complete triggers |
| $2AF7 | a9 00 | lda#$00; a6C = $00: ZP pointer low byte for play_bonus_audio_tick walk animation |
| $2AF9 | 85 6c | stazp_ptr_bonus_walk_lo; ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2AFB | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2AFC | ad e6 2a | reset_bonus_timerldabonus_timer; x-ref: $2655 |
| $2AFF | c9 ff | cmp#$ff |
| $2B01 | d0 05 | bner_2B08 |
| $2B03 | a9 08 | lda#$08 |
| $2B05 | 8d e6 2a | stabonus_timer |
| $2B08 | 60 | r_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 |
| $2B09 | ad e6 2a | ldabonus_timer; load bonus_timer; bit 7 set ($FE/$FF) → inactive/done, bail out ; x-ref: $0662 |
| $2B0C | 10 01 | bplb_2B0F |
| $2B0E | 60 | rts |
| $2B0F | f0 04 | b_2B0Fbeqb_2B15; not yet zero → decrement and wait ; x-ref: $2B0C |
| $2B11 | ce e6 2a | decbonus_timer |
| $2B14 | 60 | rts |
| $2B15 | 20 bc 0c | b_2B15jsrtrigger_countdown_sound; fire: play tick beep, reload 8-frame countdown ; x-ref: $2B0F |
| $2B18 | a9 08 | lda#$08 |
| $2B1A | 8d e6 2a | stabonus_timer |
| $2B1D | ad e8 2a | ldabonus_walk_page; a6D (ZP ptr high byte) = bonus_walk_page (screen page/row) |
| $2B20 | 85 6d | stazp_ptr_bonus_walk_hi; FACOV Floating Accum. #1. Low-Order (Rounding) |
| $2B22 | ac e7 2a | ldybonus_walk_offset; Y = bonus_walk_offset - 1 (column index) |
| $2B25 | 88 | dey |
| $2B26 | b1 6c | lda(zp_ptr_bonus_walk_lo),y; read char at col-1; substitute walk animation frame; ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2B28 | c9 63 | cmp#$63; $63 (border) → $50 |
| $2B2A | d0 05 | bneb_2B31 |
| $2B2C | a9 50 | lda#$50 |
| $2B2E | 4c 40 2b | jmpj_2B40 |
| $2B31 | c9 20 | b_2B31cmp#$20; $20 (space) → $67 ; x-ref: $2B2A |
| $2B33 | d0 05 | bneb_2B3A |
| $2B35 | a9 67 | lda#$67 |
| $2B37 | 4c 40 2b | jmpj_2B40 |
| $2B3A | c9 65 | b_2B3Acmp#$65; $65 (trailing frame) → $43 (body char) ; x-ref: $2B33 |
| $2B3C | d0 04 | bneb_2B42 |
| $2B3E | a9 43 | lda#$43 |
| $2B40 | 91 6c | j_2B40sta(zp_ptr_bonus_walk_lo),y; x-ref: $2B2E, $2B37 ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2B42 | c8 | b_2B42iny; Y++ → col; write $43 (body char) unconditionally ; x-ref: $2B3C |
| $2B43 | a9 43 | lda#$43 |
| $2B45 | 91 6c | sta(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2B47 | c8 | iny; Y++ → col+1; read char |
| $2B48 | b1 6c | lda(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2B4A | c9 63 | cmp#$63 |
| $2B4C | d0 05 | bneb_2B53 |
| $2B4E | a9 4f | lda#$4f |
| $2B50 | 4c 55 2b | jmpj_2B55 |
| $2B53 | a9 65 | b_2B53lda#$65; x-ref: $2B4C |
| $2B55 | 91 6c | j_2B55sta(zp_ptr_bonus_walk_lo),y; x-ref: $2B50 ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2B57 | 88 | dey; Y-- back to col; a5B = current row page (movement grid) |
| $2B58 | a5 6d | ldazp_ptr_bonus_walk_hi; FACOV Floating Accum. #1. Low-Order (Rounding) |
| $2B5A | 85 5b | stazp_ptr_playfield_hi |
| $2B5C | a9 04 | lda#$04 |
| $2B5E | 91 5a | sta(zp_ptr_playfield_lo),y; write $04 (climb-up) at current movement grid position |
| $2B60 | e6 5b | inczp_ptr_playfield_hi; check row above for wall bits ($C0) |
| $2B62 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B64 | c6 5b | deczp_ptr_playfield_hi |
| $2B66 | 29 c0 | and#$c0 |
| $2B68 | d0 1a | bneb_2B84; above is open: set $08 (climb-down) at current row |
| $2B6A | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B6C | 09 08 | ora#$08 |
| $2B6E | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2B70 | e6 5b | inczp_ptr_playfield_hi; check if row below already has climb-down bit ($08) |
| $2B72 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B74 | c6 5b | deczp_ptr_playfield_hi |
| $2B76 | 29 08 | and#$08 |
| $2B78 | f0 0a | beqb_2B84 |
| $2B7A | e6 5b | inczp_ptr_playfield_hi; row below has $08: set $04 (climb-up) in row below too |
| $2B7C | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B7E | 09 04 | ora#$04 |
| $2B80 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2B82 | c6 5b | deczp_ptr_playfield_hi |
| $2B84 | c0 01 | b_2B84cpy#$01; at left edge (col 1): skip walk-left bit ; x-ref: $2B68, $2B78 |
| $2B86 | f0 0e | beqb_2B96 |
| $2B88 | 88 | dey; left neighbor has no wall bits → set $01 (walk-left) at current pos |
| $2B89 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B8B | c8 | iny |
| $2B8C | 29 c0 | and#$c0 |
| $2B8E | d0 06 | bneb_2B96 |
| $2B90 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B92 | 09 01 | ora#$01 |
| $2B94 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2B96 | c0 20 | b_2B96cpy#$20; at right edge (col $20): skip walk-right bit ; x-ref: $2B86, $2B8E |
| $2B98 | f0 0e | beqb_2BA8 |
| $2B9A | c8 | iny; right neighbor has no wall bits → set $02 (walk-right) at current pos |
| $2B9B | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2B9D | 88 | dey |
| $2B9E | 29 c0 | and#$c0 |
| $2BA0 | d0 06 | bneb_2BA8 |
| $2BA2 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2BA4 | 09 02 | ora#$02 |
| $2BA6 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2BA8 | 88 | b_2BA8dey; Y-- → col-1; if still holds $43 body char: write $0F (all-dirs open) to movement grid ; x-ref: $2B98, $2BA0 |
| $2BA9 | b1 6c | lda(zp_ptr_bonus_walk_lo),y; ARISGN Sign Comparison Result: Accum. # 1 vs #2 |
| $2BAB | c9 43 | cmp#$43 |
| $2BAD | d0 04 | bneb_2BB3 |
| $2BAF | a9 0f | lda#$0f |
| $2BB1 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2BB3 | c8 | b_2BB3iny; Y++ back to col; advance walker: dec bonus_walk_page (move up one row) ; x-ref: $2BAD |
| $2BB4 | ce e8 2a | decbonus_walk_page |
| $2BB7 | ad e8 2a | ldabonus_walk_page |
| $2BBA | c9 66 | cmp#$66; page $66 = past top of screen → animation complete |
| $2BBC | f0 0c | beqb_2BCA |
| $2BBE | c6 6d | deczp_ptr_bonus_walk_hi; also dec a6D (ZP ptr high byte) to match new bonus_walk_page; FACOV Floating Accum. #1. Low-Order (Rounding) |
| $2BC0 | b1 6c | lda(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 |
| $2BC2 | c9 20 | cmp#$20 |
| $2BC4 | f0 09 | beqr_2BCF |
| $2BC6 | c9 63 | cmp#$63 |
| $2BC8 | f0 05 | beqr_2BCF |
| $2BCA | a9 fe | b_2BCAlda#$fe; $FE sentinel: animation done, play_bonus_audio_tick will bail on next call ; x-ref: $2BBC |
| $2BCC | 8d e6 2a | stabonus_timer |
| $2BCF | 60 | r_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). |
| $2BD0 | | hole_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. |
| $2BD5 | | hole_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. |
| $2BDA | | hole_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. |
| $2BDF | | hole_slot_bg_top.byte$00, $00, $00, $00, $00; x-ref: $2C11, $2C32 |
| ; Saved background char under bottom cell; restored when the hole closes. |
| $2BE4 | | hole_slot_bg_bot.byte$00, $00, $00, $00, $00; x-ref: $2C18, $2C39 |
| ; Actor index that owns this slot. $FF = free / no owner. |
| $2BE9 | | hole_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2BEE | a2 04 | init_hole_slotsldx#$04; x-ref: $0748 |
| $2BF0 | a9 00 | b_2BF0lda#$00; x-ref: $2BFB |
| $2BF2 | 9d d0 2b | stahole_slot_state,x |
| $2BF5 | a9 ff | lda#$ff |
| $2BF7 | 9d e9 2b | stahole_slot_actor,x |
| $2BFA | ca | dex |
| $2BFB | 10 f3 | bplb_2BF0 |
| $2BFD | a9 00 | lda#$00 |
| $2BFF | 85 72 | stazp_ptr_hole_screen_lo |
| $2C01 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2C02 | a2 04 | ldx#$04; x-ref: $065C |
| $2C04 | bd d0 2b | b_2C04ldahole_slot_state,x; x-ref: $2C1E |
| $2C07 | f0 14 | beqb_2C1D |
| $2C09 | bd d5 2b | ldahole_slot_page,x |
| $2C0C | 85 73 | stazp_ptr_hole_screen_hi |
| $2C0E | bc da 2b | ldyhole_slot_offset,x |
| $2C11 | bd df 2b | ldahole_slot_bg_top,x |
| $2C14 | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C16 | e6 73 | inczp_ptr_hole_screen_hi |
| $2C18 | bd e4 2b | ldahole_slot_bg_bot,x |
| $2C1B | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C1D | ca | b_2C1Ddex; x-ref: $2C07 |
| $2C1E | 10 e4 | bplb_2C04 |
| $2C20 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2C21 | a2 00 | ldx#$00; X = 0: iterate slots 0..4 ; x-ref: $0686 |
| $2C23 | bd d0 2b | b_2C23ldahole_slot_state,x; x-ref: $2C8B |
| $2C26 | f0 60 | beqb_2C88; skip inactive slot (state=0) |
| $2C28 | bd d5 2b | ldahole_slot_page,x; --- step 1: save background tiles under hole sprite --- |
| $2C2B | 85 73 | stazp_ptr_hole_screen_hi; a73 = hole row page (top half) |
| $2C2D | bc da 2b | ldyhole_slot_offset,x; Y = hole column |
| $2C30 | b1 72 | lda(zp_ptr_hole_screen_lo),y; read tile at top row, col Y |
| $2C32 | 9d df 2b | stahole_slot_bg_top,x; save top-row tile for restore next frame |
| $2C35 | e6 73 | inczp_ptr_hole_screen_hi; advance to bottom row page |
| $2C37 | b1 72 | lda(zp_ptr_hole_screen_lo),y; read tile at bottom row, col Y |
| $2C39 | 9d e4 2b | stahole_slot_bg_bot,x; save bottom-row tile for restore next frame |
| $2C3C | c6 73 | deczp_ptr_hole_screen_hi; restore a73 to top row page for draw phase |
| $2C3E | bd e9 2b | ldahole_slot_actor,x; --- step 2: guard-occupying-hole skip check --- |
| $2C41 | a8 | tay; Y = linked dig slot index |
| $2C42 | b9 43 1e | ldadig_slot_status,y; read dig slot status |
| $2C45 | c9 03 | cmp#$03; status $03 = guard physically inside hole |
| $2C47 | d0 0a | bneb_2C53; no → proceed to draw |
| $2C49 | bd d0 2b | ldahole_slot_state,x; yes: also check state parity |
| $2C4C | 29 01 | and#$01; state & $01: odd tick gate |
| $2C4E | f0 03 | beqb_2C53; bit clear → proceed to draw anyway |
| $2C50 | 4c 88 2c | jmpb_2C88; guard inside + odd tick → skip draw entirely |
| $2C53 | bc da 2b | b_2C53ldyhole_slot_offset,x; --- step 3: draw hole tiles based on state --- ; x-ref: $2C47, $2C4E |
| $2C56 | bd d0 2b | ldahole_slot_state,x; reload state for threshold checks |
| $2C59 | c9 80 | cmp#$80; state >= $80 → nearly healed |
| $2C5B | 90 0d | bccb_2C6A; no → check mid-stage bit |
| $2C5D | a9 2e | lda#$2e; state >= $80: top=$2E (nearly-healed top) |
| $2C5F | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C61 | e6 73 | inczp_ptr_hole_screen_hi; advance to bottom row |
| $2C63 | a9 56 | lda#$56; bot=$56 (nearly-healed bottom) |
| $2C65 | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C67 | 4c 88 2c | jmpb_2C88 |
| $2C6A | 29 10 | b_2C6Aand#$10; state & $10 set → mid-stage hole ; x-ref: $2C5B |
| $2C6C | f0 0d | beqb_2C7B; no → fully open hole |
| $2C6E | a9 2e | lda#$2e; mid-stage: top=$2E |
| $2C70 | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C72 | e6 73 | inczp_ptr_hole_screen_hi; advance to bottom row |
| $2C74 | a9 5b | lda#$5b; bot=$5B (half-open hole) |
| $2C76 | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C78 | 4c 88 2c | jmpb_2C88 |
| $2C7B | a9 2e | b_2C7Blda#$2e; fully open: top=$2E ; x-ref: $2C6C |
| $2C7D | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C7F | e6 73 | inczp_ptr_hole_screen_hi; advance to bottom row |
| $2C81 | a9 19 | lda#$19; bot=$19 (fully open hole) |
| $2C83 | 91 72 | sta(zp_ptr_hole_screen_lo),y |
| $2C85 | 4c 88 2c | jmpb_2C88 |
| $2C88 | e8 | b_2C88inx; next slot ; x-ref: $2C26, $2C50, $2C67, $2C78, $2C85 |
| $2C89 | e0 05 | cpx#$05; all 5 slots done? |
| $2C8B | d0 96 | bneb_2C23 |
| $2C8D | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $2C8E | a2 04 | ldx#$04; iterate all 5 hole slots, high to low ; x-ref: $0674 |
| $2C90 | bd d0 2b | b_2C90ldahole_slot_state,x; skip inactive slot (state=0) ; x-ref: $2C96 |
| $2C93 | d0 04 | bneb_2C99 |
| $2C95 | ca | j_2C95dex; next slot (DEX + BPL = loop over 4..0) ; x-ref: $2D56, $2DA7 |
| $2C96 | 10 f8 | bplb_2C90 |
| $2C98 | 60 | rts |
| $2C99 | c9 ff | b_2C99cmp#$ff; --- state $FF: new hole — write movement-grid property bits --- ; x-ref: $2C93 |
| $2C9B | d0 3f | bneb_2CDC |
| $2C9D | bd d5 2b | ldahole_slot_page,x; a5B = hole row page + 1 (points to property grid row) |
| $2CA0 | 85 5b | stazp_ptr_playfield_hi |
| $2CA2 | e6 5b | inczp_ptr_playfield_hi |
| $2CA4 | bc da 2b | ldyhole_slot_offset,x |
| $2CA7 | b1 5a | lda(zp_ptr_playfield_lo),y; keep rope bit ($20), set walk-left+right bits ($03) → hole is passable both ways |
| $2CA9 | 29 20 | and#$20 |
| $2CAB | 09 03 | ora#$03 |
| $2CAD | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2CAF | c8 | iny; check right neighbor (Y+1) for wall bits ($C0) |
| $2CB0 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2CB2 | 88 | dey |
| $2CB3 | 29 c0 | and#$c0 |
| $2CB5 | d0 07 | bneb_2CBE; right is wall → clear walk-right bit ($02) |
| $2CB7 | c0 20 | cpy#$20; at right edge (col $20) → also clear walk-right bit |
| $2CB9 | f0 03 | beqb_2CBE |
| $2CBB | 4c c4 2c | jmpj_2CC4 |
| $2CBE | b1 5a | b_2CBElda(zp_ptr_playfield_lo),y; right neighbor is open: keep walk-right bit, fall through to left check ; x-ref: $2CB5, $2CB9 |
| $2CC0 | 29 fd | and#$fd |
| $2CC2 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2CC4 | c0 01 | j_2CC4cpy#$01; at left edge (col $01) → clear walk-left bit ($01) ; x-ref: $2CBB |
| $2CC6 | f0 0b | beqb_2CD3 |
| $2CC8 | 88 | dey; check left neighbor (Y-1) for wall bits ($C0) |
| $2CC9 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2CCB | c8 | iny |
| $2CCC | 29 c0 | and#$c0 |
| $2CCE | d0 03 | bneb_2CD3; left neighbor is open: skip clearing walk-left bit → hole open both sides |
| $2CD0 | 4c 51 2d | jmpj_2D51 |
| $2CD3 | b1 5a | b_2CD3lda(zp_ptr_playfield_lo),y; left is wall or edge: clear walk-left bit ($01) ; x-ref: $2CC6, $2CCE |
| $2CD5 | 29 fe | and#$fe |
| $2CD7 | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2CD9 | 4c 51 2d | jmpj_2D51 |
| $2CDC | c9 fa | b_2CDCcmp#$fa; --- state $FA: shift hole visual down one row --- ; x-ref: $2C9B |
| $2CDE | d0 06 | bneb_2CE6 |
| $2CE0 | fe d5 2b | inchole_slot_page,x; hole_slot_page++ (visual sinks one row into the floor) |
| $2CE3 | 4c 51 2d | jmpj_2D51 |
| $2CE6 | c9 f5 | b_2CE6cmp#$f5; --- state $F5: shift hole visual down a second row --- ; x-ref: $2CDE |
| $2CE8 | d0 06 | bneb_2CF0 |
| $2CEA | fe d5 2b | inchole_slot_page,x; hole_slot_page++ (sinks another row) |
| $2CED | 4c 51 2d | jmpj_2D51 |
| $2CF0 | c9 30 | b_2CF0cmp#$30; --- state $30: shift hole visual back up one row --- ; x-ref: $2CE8 |
| $2CF2 | d0 06 | bneb_2CFA |
| $2CF4 | de d5 2b | dechole_slot_page,x; hole_slot_page-- (hole rises back toward original row) |
| $2CF7 | 4c 51 2d | jmpj_2D51 |
| $2CFA | c9 10 | b_2CFAcmp#$10; --- state $10: shift hole visual back up a second row --- ; x-ref: $2CF2 |
| $2CFC | d0 06 | bneb_2D04 |
| $2CFE | de d5 2b | dechole_slot_page,x; hole_slot_page-- (returns to original row) |
| $2D01 | 4c 51 2d | jmpj_2D51 |
| $2D04 | c9 05 | b_2D04cmp#$05; --- state $05: nudge hole offset toward draw_offset (player column) --- ; x-ref: $2CFC |
| $2D06 | d0 49 | bnej_2D51 |
| $2D08 | bd d5 2b | ldahole_slot_page,x; a5B = hole row page + 1 (property grid) |
| $2D0B | 85 5b | stazp_ptr_playfield_hi |
| $2D0D | e6 5b | inczp_ptr_playfield_hi |
| $2D0F | bc da 2b | ldyhole_slot_offset,x |
| $2D12 | bd da 2b | ldahole_slot_offset,x |
| $2D15 | cd b4 0d | cmpdraw_offset |
| $2D18 | b0 1d | bcsb_2D37 |
| $2D1A | c0 32 | cpy#$32; at right edge → can't go right, try nudge left instead |
| $2D1C | f0 08 | beqb_2D26 |
| $2D1E | c8 | iny |
| $2D1F | b1 5a | lda(zp_ptr_playfield_lo),y; right neighbor has no wall bits → nudge right is safe |
| $2D21 | 88 | dey |
| $2D22 | 29 c0 | and#$c0 |
| $2D24 | f0 0b | beqb_2D31 |
| $2D26 | 88 | b_2D26dey; right blocked or at edge: check left neighbor instead ; x-ref: $2D1C |
| $2D27 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2D29 | c8 | iny |
| $2D2A | 29 c0 | and#$c0 |
| $2D2C | f0 20 | beqb_2D4E; left also blocked → neither direction free, leave offset unchanged |
| $2D2E | 4c 51 2d | jmpj_2D51 |
| $2D31 | fe da 2b | b_2D31inchole_slot_offset,x; nudge right: inc hole_slot_offset ; x-ref: $2D24, $2D49 |
| $2D34 | 4c 51 2d | jmpj_2D51 |
| $2D37 | c0 01 | b_2D37cpy#$01; hole_offset >= draw_offset: hole is right of player → try nudge left ; x-ref: $2D18 |
| $2D39 | f0 08 | beqb_2D43; at left edge → can't go left, try nudge right instead |
| $2D3B | 88 | dey |
| $2D3C | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2D3E | c8 | iny |
| $2D3F | 29 c0 | and#$c0 |
| $2D41 | f0 0b | beqb_2D4E |
| $2D43 | c8 | b_2D43iny; left blocked or at edge: check right neighbor instead ; x-ref: $2D39 |
| $2D44 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2D46 | 88 | dey |
| $2D47 | 29 c0 | and#$c0 |
| $2D49 | f0 e6 | beqb_2D31; right also blocked → leave offset unchanged |
| $2D4B | 4c 51 2d | jmpj_2D51 |
| $2D4E | de da 2b | b_2D4Edechole_slot_offset,x; x-ref: $2D2C, $2D41 |
| $2D51 | de d0 2b | j_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, ... |
| $2D54 | f0 03 | beqb_2D59 |
| $2D56 | 4c 95 2c | jmpj_2C95 |
| $2D59 | bd d5 2b | b_2D59ldahole_slot_page,x; --- state $00: seal hole and release trapped guard --- ; x-ref: $2D54 |
| $2D5C | 85 5b | stazp_ptr_playfield_hi |
| $2D5E | e6 5b | inczp_ptr_playfield_hi |
| $2D60 | bd e9 2b | ldahole_slot_actor,x; get actor index that dug this hole |
| $2D63 | a8 | tay |
| $2D64 | b9 57 1e | ldadig_slot_col,y; read dig column → Y; write healed-brick property bits: keep rope ($20), set solid ($10) |
| $2D67 | a8 | tay |
| $2D68 | b1 5a | lda(zp_ptr_playfield_lo),y |
| $2D6A | 29 20 | and#$20 |
| $2D6C | 09 10 | ora#$10 |
| $2D6E | 91 5a | sta(zp_ptr_playfield_lo),y |
| $2D70 | bd e9 2b | ldahole_slot_actor,x |
| $2D73 | a8 | tay |
| $2D74 | a9 ff | lda#$ff |
| $2D76 | 99 93 1e | stadig_slot_hole_idx,y; unlink actor from hole slot ($FF = none) |
| $2D79 | a9 ff | lda#$ff |
| $2D7B | 9d e9 2b | stahole_slot_actor,x; unlink slot from actor ($FF = free) |
| $2D7E | bd d5 2b | ldahole_slot_page,x; teleport guard to sealed hole position |
| $2D81 | 9d 08 22 | staguard_page,x |
| $2D84 | bd da 2b | ldahole_slot_offset,x |
| $2D87 | 9d 03 22 | staguard_offset,x |
| $2D8A | a9 00 | lda#$00; reset guard to normal patrol state: active, no animation, no velocity |
| $2D8C | 9d f9 21 | staguard_active,x |
| $2D8F | a9 00 | lda#$00 |
| $2D91 | 9d fe 21 | staguard_anim_frame,x |
| $2D94 | 9d 0d 22 | staguard_sprite_strip,x |
| $2D97 | 9d 12 22 | staguard_vspeed,x |
| $2D9A | 9d 17 22 | staguard_hspeed,x |
| $2D9D | a9 02 | lda#$02; move_rate = 2 (normal patrol speed) |
| $2D9F | 9d 1c 22 | staguard_move_rate,x |
| $2DA2 | a9 7f | lda#$7f; ai_bias = $7F (neutral pathfinding bias) |
| $2DA4 | 9d 21 22 | staguard_ai_bias,x |
| $2DA7 | 4c 95 2c | jmpj_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. |
| $2DAA | | death_anim_state.byte$00; x-ref: $2E4E, $2FB7, $2FD6, $2FFC, $302A |
| | .encode |
| | .enc"screen" |
| $2DAB | | txt_game_over.text" GAME OVER", $ff; x-ref: $2E60 |
| $2DBD | | txt_game_over_l1.text" ", $63, $63, $63, $63, $63, $63, $63, $63, $63, $ff |
| $2DCF | | txt_game_over_l2.text" ", $ff |
| $2DD1 | | txt_game_over_l3.text" ", $ff |
| $2DD3 | | txt_game_over_l4.text" SCORE:", $ff, $fe |
| $2DE2 | | txt_hi_score.text" ", $ff; x-ref: $2F18 |
| $2DE4 | | txt_hi_score_l1.text" HIGH SCORE:", $ff |
| $2DF5 | | txt_hi_score_l2.text" ", $ff |
| $2DF7 | | txt_hi_score_l3.text" ", $ff |
| $2DF9 | | txt_hi_score_l4.text" ", $ff |
| $2DFB | | txt_hi_score_l5.text" ", $ff |
| $2DFD | | txt_hi_score_l6.text" PRESS ANY KEY", $ff |
| $2E11 | | txt_hi_score_l7.text" ", $ff, $fe |
| $2E14 | | txt_new_hi_score.text" ", $ff; x-ref: $2F4A |
| $2E16 | | txt_new_hi_score_l1.text" NEW HIGH SCORE:", $ff |
| $2E28 | | txt_new_hi_score_l2.text" ", $ff |
| $2E2A | | txt_new_hi_score_l3.text" ", $ff |
| $2E2C | | txt_new_hi_score_l4.text" ", $ff |
| $2E2E | | txt_new_hi_score_l5.text" EMAIL ME YOUR SCORE ", $ff |
| $2E46 | | txt_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 |
| $2E49 | 20 92 0c | jsrstop_audio; --- setup --- ; x-ref: $06E3 |
| $2E4C | a9 00 | lda#$00; death_anim_state = 0 (reset walk animation) |
| $2E4E | 8d aa 2d | stadeath_anim_state |
| $2E51 | 20 f0 07 | jsrclear_sub_grid; wipe sub-grid (clears any leftover tile state) |
| $2E54 | a9 6c | lda#>playfield_row_5_page_base; --- section 1: render txt_game_over into offscreen buffer from $6C00 --- |
| $2E56 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E58 | a9 00 | lda#<playfield_row_5_page_base; ZP pointer ($58/$59) = $6C00 (buffer start) |
| $2E5A | 85 58 | stazp_ptr_wipe_buf_lo |
| $2E5C | a2 00 | ldx#$00 |
| $2E5E | a0 05 | j_2E5Eldy#$05; Y = col 5 (starting column of each text row) ; x-ref: $2E76 |
| $2E60 | bd ab 2d | j_2E60ldatxt_game_over,x; read layout byte: $FF=newline, $FE=end, else=char to write ; x-ref: $2E6F |
| $2E63 | c9 ff | cmp#$ff |
| $2E65 | f0 0b | beqb_2E72 |
| $2E67 | c9 fe | cmp#$fe |
| $2E69 | f0 0e | beqb_2E79 |
| $2E6B | 91 58 | sta(zp_ptr_wipe_buf_lo),y; write char to offscreen buffer at current row+col |
| $2E6D | c8 | iny |
| $2E6E | e8 | inx |
| $2E6F | 4c 60 2e | jmpj_2E60 |
| $2E72 | e8 | b_2E72inx; $FF newline: advance to next buffer page (next row), skip one col ; x-ref: $2E65 |
| $2E73 | c8 | iny |
| $2E74 | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E76 | 4c 5e 2e | jmpj_2E5E |
| $2E79 | a9 67 | b_2E79lda#>playfield_row_buffer; --- section 2: draw left/right border columns ($65) on every row --- ; x-ref: $2E69 |
| $2E7B | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E7D | a0 01 | b_2E7Dldy#$01; reset page to playfield_row_buffer start ; x-ref: $2E8F |
| $2E7F | a9 65 | lda#$65; left border at col 1 |
| $2E81 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2E83 | a0 21 | ldy#$21; right border at col $21 |
| $2E85 | a9 65 | lda#$65 |
| $2E87 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2E89 | e6 59 | inczp_ptr_wipe_buf_hi; stop at page $80 (past last screen row); TEMPF2 Temporary storage for FLPT value. |
| $2E8B | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E8D | c9 80 | cmp#$80 |
| $2E8F | d0 ec | bneb_2E7D |
| $2E91 | a0 01 | ldy#$01; --- section 3: draw top/bottom decoration rows across cols 1..$20 --- |
| $2E93 | a9 67 | b_2E93lda#>playfield_row_buffer; $63 = top border char → playfield top row ; x-ref: $2EAE |
| $2E95 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E97 | a9 63 | lda#$63 |
| $2E99 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2E9B | a9 7f | lda#>playfield_row_24_page_base; $C2 = bottom decoration char → page $7F (near-bottom row) |
| $2E9D | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2E9F | a9 c2 | lda#$c2 |
| $2EA1 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2EA3 | a9 7e | lda#>playfield_row_23_page_base; $FA = bottom border char → page $7E (bottom row) |
| $2EA5 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2EA7 | a9 fa | lda#$fa |
| $2EA9 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2EAB | c8 | iny; stop after col $20 (cols 1..$20 = 32 columns) |
| $2EAC | c0 21 | cpy#$21 |
| $2EAE | d0 e3 | bneb_2E93 |
| $2EB0 | a9 4f | lda#$4f; top-left corner piece at $6701 |
| $2EB2 | 8d 01 67 | staplayfield_row_0; Playfield Row 0 buffer |
| $2EB5 | a9 e4 | lda#$e4; bottom-right corner piece at $7E20 |
| $2EB7 | 8d 20 7e | staplayfield_row_23_col_32; Playfield Row 23, column 32 (bottom-right corner of the border) |
| $2EBA | a9 70 | lda#>playfield_row_9_page_base; --- section 4: render current score at page $7000, col $13 --- |
| $2EBC | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2EBE | a0 13 | ldy#$13; Y = $13: starting column for score digits |
| $2EC0 | ad 45 29 | ldascore_high_bcd; high nibble of score_high_bcd = ten-thousands digit |
| $2EC3 | 29 f0 | and#$f0 |
| $2EC5 | f0 09 | beqb_2ED0; skip ten-thousands digit if zero (leading-zero suppression) |
| $2EC7 | 4a | lsra; 4x LSR + ADC #$30 → ten-thousands digit as PETSCII |
| $2EC8 | 4a | lsra |
| $2EC9 | 4a | lsra |
| $2ECA | 4a | lsra |
| $2ECB | 18 | clc |
| $2ECC | 69 30 | adc#$30 |
| $2ECE | 91 58 | sta(zp_ptr_wipe_buf_lo),y; write ten-thousands digit to screen |
| $2ED0 | c8 | b_2ED0iny; low nibble → thousands digit (no suppression: always shown) ; x-ref: $2EC5 |
| $2ED1 | ad 45 29 | ldascore_high_bcd |
| $2ED4 | 29 0f | and#$0f |
| $2ED6 | 18 | clc |
| $2ED7 | 69 30 | adc#$30 |
| $2ED9 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2EDB | c8 | iny |
| $2EDC | ad 46 29 | ldascore_low_bcd; high nibble of score_low_bcd → hundreds digit |
| $2EDF | 29 f0 | and#$f0 |
| $2EE1 | 4a | lsra |
| $2EE2 | 4a | lsra |
| $2EE3 | 4a | lsra |
| $2EE4 | 4a | lsra |
| $2EE5 | 18 | clc |
| $2EE6 | 69 30 | adc#$30 |
| $2EE8 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2EEA | c8 | iny |
| $2EEB | ad 46 29 | ldascore_low_bcd; low nibble of score_low_bcd → tens digit |
| $2EEE | 29 0f | and#$0f |
| $2EF0 | 18 | clc |
| $2EF1 | 69 30 | adc#$30 |
| $2EF3 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2EF5 | c8 | iny; --- section 5: hi-score comparison --- |
| $2EF6 | ad 45 29 | ldascore_high_bcd |
| $2EF9 | cd 43 29 | cmphi_score_high_bcd |
| $2EFC | f0 05 | beqb_2F03; high bytes equal → compare low bytes |
| $2EFE | 90 10 | bccb_2F10; score_high < hi_score_high → not a new record |
| $2F00 | 4c 36 2f | jmpj_2F36; score_high > hi_score_high → new record |
| $2F03 | ad 46 29 | b_2F03ldascore_low_bcd; x-ref: $2EFC |
| $2F06 | cd 44 29 | cmphi_score_low_bcd |
| $2F09 | f0 05 | beqb_2F10; score_low <= hi_score_low → not a new record (equal counts as no new record) |
| $2F0B | 90 03 | bccb_2F10 |
| $2F0D | 4c 36 2f | jmpj_2F36 |
| $2F10 | a9 71 | b_2F10lda#>playfield_row_10_page_base; --- section 6a: not a new record — render txt_hi_score at page $7100 --- ; x-ref: $2EFE, $2F09, $2F0B |
| $2F12 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2F14 | a2 00 | ldx#$00 |
| $2F16 | a0 05 | j_2F16ldy#$05; x-ref: $2F2E |
| $2F18 | bd e2 2d | j_2F18ldatxt_hi_score,x; x-ref: $2F27 |
| $2F1B | c9 ff | cmp#$ff |
| $2F1D | f0 0b | beqb_2F2A |
| $2F1F | c9 fe | cmp#$fe |
| $2F21 | f0 0e | beqb_2F31 |
| $2F23 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F25 | c8 | iny |
| $2F26 | e8 | inx |
| $2F27 | 4c 18 2f | jmpj_2F18 |
| $2F2A | e8 | b_2F2Ainx; x-ref: $2F1D |
| $2F2B | c8 | iny |
| $2F2C | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2F2E | 4c 16 2f | jmpj_2F16 |
| $2F31 | a0 15 | b_2F31ldy#$15; $FF newline: advance page, skip one col ; x-ref: $2F21 |
| $2F33 | 4c 65 2f | jmpj_2F65; hi-score digits start at col $15 → join shared digit-render path |
| $2F36 | ad 45 29 | j_2F36ldascore_high_bcd; --- section 6b: new record — save score, render txt_new_hi_score at $7100 --- ; x-ref: $2F00, $2F0D |
| $2F39 | 8d 43 29 | stahi_score_high_bcd; persist new hi-score high byte |
| $2F3C | ad 46 29 | ldascore_low_bcd; persist new hi-score low byte |
| $2F3F | 8d 44 29 | stahi_score_low_bcd |
| $2F42 | a9 71 | lda#>playfield_row_10_page_base |
| $2F44 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2F46 | a2 00 | ldx#$00 |
| $2F48 | a0 05 | j_2F48ldy#$05; x-ref: $2F60 |
| $2F4A | bd 14 2e | j_2F4Aldatxt_new_hi_score,x; x-ref: $2F59 |
| $2F4D | c9 ff | cmp#$ff |
| $2F4F | f0 0b | beqb_2F5C |
| $2F51 | c9 fe | cmp#$fe |
| $2F53 | f0 0e | beqb_2F63 |
| $2F55 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F57 | c8 | iny |
| $2F58 | e8 | inx |
| $2F59 | 4c 4a 2f | jmpj_2F4A |
| $2F5C | e8 | b_2F5Cinx; x-ref: $2F4F |
| $2F5D | c8 | iny |
| $2F5E | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2F60 | 4c 48 2f | jmpj_2F48 |
| $2F63 | a0 17 | b_2F63ldy#$17; new hi-score digits start at col $17 (2 cols right of existing record position) ; x-ref: $2F53 |
| $2F65 | a9 72 | j_2F65lda#>playfield_row_11_page_base; --- section 7: render hi-score digits at page $7200 (shared by both paths) --- ; x-ref: $2F33 |
| $2F67 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $2F69 | ad 43 29 | ldahi_score_high_bcd; high nibble → ten-thousands digit (no leading-zero suppression) |
| $2F6C | 29 f0 | and#$f0 |
| $2F6E | 4a | lsra |
| $2F6F | 4a | lsra |
| $2F70 | 4a | lsra |
| $2F71 | 4a | lsra |
| $2F72 | 18 | clc |
| $2F73 | 69 30 | adc#$30 |
| $2F75 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F77 | c8 | iny; low nibble → thousands digit |
| $2F78 | ad 43 29 | ldahi_score_high_bcd |
| $2F7B | 29 0f | and#$0f |
| $2F7D | 18 | clc |
| $2F7E | 69 30 | adc#$30 |
| $2F80 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F82 | c8 | iny |
| $2F83 | ad 44 29 | ldahi_score_low_bcd; high nibble of low byte → hundreds digit |
| $2F86 | 29 f0 | and#$f0 |
| $2F88 | 4a | lsra |
| $2F89 | 4a | lsra |
| $2F8A | 4a | lsra |
| $2F8B | 4a | lsra |
| $2F8C | 18 | clc |
| $2F8D | 69 30 | adc#$30 |
| $2F8F | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F91 | c8 | iny |
| $2F92 | ad 44 29 | ldahi_score_low_bcd; low nibble of low byte → tens digit |
| $2F95 | 29 0f | and#$0f |
| $2F97 | 18 | clc |
| $2F98 | 69 30 | adc#$30 |
| $2F9A | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $2F9C | c8 | iny |
| $2F9D | 20 b8 0a | jsrtransition_from_empty_to_full; --- section 8: finalise and hand off to IRQ --- |
| $2FA0 | a9 02 | lda#$02; game_state_flag = 2 (game-over / modal state) |
| $2FA2 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $2FA4 | a9 ad | lda#<game_over_screen_irq; redirect IRQ → game_over_screen_irq (walk animation + keypress wait) |
| $2FA6 | 85 90 | stazp_irq_vector_lo; CINV Vector: Hardware Interrupt |
| $2FA8 | a9 2f | lda#>game_over_screen_irq |
| $2FAA | 85 91 | stazp_irq_vector_hi |
| $2FAC | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $2FAD | 20 65 1a | game_over_screen_irqjsrwait_any_key; x-ref: $2FA4, $2FA8 |
| $2FB0 | c9 ff | cmp#$ff |
| $2FB2 | f0 03 | beqb_2FB7 |
| $2FB4 | 20 bf 30 | jsrswitch_to_title_screen |
| $2FB7 | ad aa 2d | b_2FB7ldadeath_anim_state; x-ref: $2FB2 |
| $2FBA | d0 1d | bneb_2FD9 |
| $2FBC | 20 ba 0d | jsrinit_game_variables |
| $2FBF | a9 7c | lda#$7c |
| $2FC1 | 8d b5 0d | stadraw_page |
| $2FC4 | a9 04 | lda#$04 |
| $2FC6 | 8d b4 0d | stadraw_offset |
| $2FC9 | a9 00 | lda#$00 |
| $2FCB | 8d b3 0d | staanim_frame |
| $2FCE | a9 00 | lda#$00 |
| $2FD0 | 8d b6 0d | staanim_strip_base |
| $2FD3 | 20 5f 11 | jsrsave_bg_and_draw_sprite |
| $2FD6 | ee aa 2d | incdeath_anim_state |
| $2FD9 | c9 01 | b_2FD9cmp#$01; x-ref: $2FBA |
| $2FDB | d0 28 | bneb_3005 |
| $2FDD | 20 3c 11 | jsrrestore_sprite_bg |
| $2FE0 | a9 00 | lda#$00 |
| $2FE2 | 8d b6 0d | staanim_strip_base |
| $2FE5 | ad b3 0d | ldaanim_frame |
| $2FE8 | 18 | clc |
| $2FE9 | 69 04 | adc#$04 |
| $2FEB | 29 0c | and#$0c |
| $2FED | 8d b3 0d | staanim_frame |
| $2FF0 | d0 03 | bneb_2FF5 |
| $2FF2 | ee b4 0d | incdraw_offset |
| $2FF5 | ad b4 0d | b_2FF5ldadraw_offset; x-ref: $2FF0 |
| $2FF8 | c9 1c | cmp#$1c |
| $2FFA | d0 03 | bneb_2FFF |
| $2FFC | ee aa 2d | incdeath_anim_state |
| $2FFF | 20 5f 11 | b_2FFFjsrsave_bg_and_draw_sprite; x-ref: $2FFA |
| $3002 | 20 86 08 | jsrblit_screen |
| $3005 | c9 02 | b_3005cmp#$02; x-ref: $2FDB |
| $3007 | d0 2a | bneb_3033 |
| $3009 | 20 3c 11 | jsrrestore_sprite_bg |
| $300C | a9 10 | lda#$10 |
| $300E | 8d b6 0d | staanim_strip_base |
| $3011 | ad b3 0d | ldaanim_frame |
| $3014 | 38 | sec |
| $3015 | e9 04 | sbc#$04 |
| $3017 | 29 0c | and#$0c |
| $3019 | 8d b3 0d | staanim_frame |
| $301C | c9 0c | cmp#$0c |
| $301E | d0 03 | bneb_3023 |
| $3020 | ce b4 0d | decdraw_offset |
| $3023 | ad b4 0d | b_3023ldadraw_offset; x-ref: $301E |
| $3026 | c9 05 | cmp#$05 |
| $3028 | d0 03 | bneb_302D |
| $302A | ce aa 2d | decdeath_anim_state |
| $302D | 20 5f 11 | b_302Djsrsave_bg_and_draw_sprite; x-ref: $3028 |
| $3030 | 20 86 08 | jsrblit_screen |
| $3033 | ad 12 e8 | b_3033lda$e812; x-ref: $3007 PORT B or DDR B: Data Direction Register B |
| $3036 | 68 | pla |
| $3037 | a8 | tay |
| $3038 | 68 | pla |
| $3039 | aa | tax |
| $303A | 68 | pla |
| $303B | 40 | rti |
| ; 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) |
| $303C | | title_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. |
| $303D | | selected_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 |
| $305B | | txt_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 |
| $3078 | | txt_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. |
| $30BC | | level_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. |
| $30BD | | select_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. |
| $30BE | | key_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 |
| $30BF | a9 03 | lda#$03; x-ref: $1A16, $2FB4, $37D6 |
| $30C1 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $30C3 | a9 00 | lda#$00 |
| $30C5 | 85 58 | stazp_ptr_wipe_buf_lo |
| $30C7 | a9 00 | lda#$00 |
| $30C9 | 8d 3c 30 | statitle_screen_state |
| $30CC | a9 00 | lda#$00 |
| $30CE | 8d bc 30 | stalevel_select_delta |
| $30D1 | a9 00 | lda#$00 |
| $30D3 | 8d be 30 | stakey_debounce_flag |
| $30D6 | a9 00 | lda#$00 |
| $30D8 | 8d bd 30 | staselect_confirm_flag |
| $30DB | a9 01 | lda#$01 |
| $30DD | 8d 3d 30 | staselected_level |
| $30E0 | 20 9d 07 | jsrclear_gameplay_grid |
| $30E3 | a9 ec | lda#<title_screen_irq |
| $30E5 | 85 90 | stazp_irq_vector_lo; CINV Vector: Hardware Interrupt |
| $30E7 | a9 30 | lda#>title_screen_irq |
| $30E9 | 85 91 | stazp_irq_vector_hi |
| $30EB | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $30EC | ad 3c 30 | title_screen_irqldatitle_screen_state; --- State 0: one-time init --- check title_screen_state (0=init, 1=select, 2=launch) ; x-ref: $30E3, $30E7 |
| $30EF | d0 76 | bneb_3167; non-zero → skip init, go to state 1 or 2 dispatch |
| $30F1 | 20 9d 07 | jsrclear_gameplay_grid; clear back-buffer tile data |
| $30F4 | 20 0d 1c | jsrclear_playfield_buffer; clear playfield collision/tile buffer |
| $30F7 | a9 75 | lda#>playfield_row_14_page_base; a5B = page $75 (border row target) |
| $30F9 | 85 5b | stazp_ptr_playfield_hi |
| $30FB | a9 2f | lda#$2f; $2F = PETSCII horizontal line char |
| $30FD | a0 29 | ldy#$29; Y = 41 ($29) → 0, fill 42 columns with border char |
| $30FF | 91 5a | b_30FFsta(zp_ptr_playfield_lo),y; x-ref: $3102 |
| $3101 | 88 | dey |
| $3102 | 10 fb | bplb_30FF |
| $3104 | 20 40 33 | jsrdraw_static_ui_decorations |
| $3107 | 20 ba 0d | jsrinit_game_variables |
| $310A | a9 10 | lda#$10 |
| $310C | 8d b4 0d | stadraw_offset |
| $310F | a9 7c | lda#$7c; draw_page = $7C: player icon row |
| $3111 | 8d b5 0d | stadraw_page |
| $3114 | a9 01 | lda#$01; input_dy = 1: player walks downward |
| $3116 | 8d b8 0d | stainput_dy |
| $3119 | a9 00 | lda#$00; player_state = 0: start walking animation |
| $311B | 8d b1 0d | staplayer_state |
| $311E | 20 5f 11 | jsrsave_bg_and_draw_sprite; place player icon at draw_page/draw_offset |
| $3121 | 20 94 33 | jsrdraw_intro_text_lode_runner; render all intro text strings |
| $3124 | 20 ab 33 | jsrdraw_intro_text_underline |
| $3127 | 20 c2 33 | jsrdraw_intro_text_high_score |
| $312A | 20 d9 33 | jsrdraw_intro_text_select_level |
| $312D | 20 f0 33 | jsrdraw_intro_text_dig_to_select |
| $3130 | 20 07 34 | jsrdraw_intro_hi_score |
| $3133 | a2 01 | ldx#$01; X = BCD level counter, starts at $01 |
| $3135 | a9 71 | lda#>playfield_row_10_page_base |
| $3137 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3139 | a0 02 | ldy#$02; Y = column, starts at 2 (4 cols apart per level) |
| $313B | 8a | b_313Btxa; high nibble of X → tens digit ; x-ref: $315C |
| $313C | 29 f0 | and#$f0 |
| $313E | 4a | lsra |
| $313F | 4a | lsra |
| $3140 | 4a | lsra |
| $3141 | 4a | lsra |
| $3142 | 18 | clc |
| $3143 | 69 30 | adc#$30; +$30 → PETSCII tens digit, write to buffer |
| $3145 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3147 | c8 | iny |
| $3148 | 8a | txa; low nibble of X → units digit |
| $3149 | 29 0f | and#$0f |
| $314B | 18 | clc |
| $314C | 69 30 | adc#$30; +$30 → PETSCII units digit, write to buffer |
| $314E | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3150 | c8 | iny |
| $3151 | c8 | iny; skip 2 cols (4 total per level) |
| $3152 | c8 | iny |
| $3153 | f8 | sed; BCD: X += 1 (01→02→03→...→10) |
| $3154 | 8a | txa |
| $3155 | 18 | clc |
| $3156 | 69 01 | adc#$01 |
| $3158 | aa | tax |
| $3159 | d8 | cld |
| $315A | e0 11 | cpx#$11; loop until all 10 level numbers drawn |
| $315C | d0 dd | bneb_313B |
| $315E | 20 4d 32 | jsrdraw_level_select_cursor; draw selection box around default level |
| $3161 | ee 3c 30 | inctitle_screen_state; advance to state 1 (level selection) |
| $3164 | 4c 32 32 | jmpj_3232 |
| $3167 | c9 01 | b_3167cmp#$01; --- State 1: level selection --- ; x-ref: $30EF |
| $3169 | f0 03 | beqb_316E |
| $316B | 4c f1 31 | jmpj_31F1 |
| $316E | ad be 30 | b_316Eldakey_debounce_flag; key_debounce_flag < 0 means debounced → skip wait ; x-ref: $3169 |
| $3171 | 30 0d | bmib_3180 |
| $3173 | 20 65 1a | jsrwait_any_key; wait for any key release before accepting input |
| $3176 | 30 03 | bmib_317B |
| $3178 | 4c 32 32 | jmpj_3232 |
| $317B | a9 ff | b_317Blda#$ff; armed: debounce_flag = $FF ; x-ref: $3176 |
| $317D | 8d be 30 | stakey_debounce_flag |
| $3180 | 20 92 32 | b_3180jsrerase_level_select_cursor; clear delta and confirm each frame ; x-ref: $3171 |
| $3183 | a9 00 | lda#$00 |
| $3185 | 8d bc 30 | stalevel_select_delta |
| $3188 | 8d bd 30 | staselect_confirm_flag |
| $318B | a5 29 | ldazp_key_row_up; scan up key via PIA row/col |
| $318D | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $3190 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $3193 | 25 32 | andzp_key_col_up; FRESPC Utility String Pointer |
| $3195 | d0 03 | bneb_319A |
| $3197 | ce bc 30 | declevel_select_delta; up pressed: delta = -1 |
| $319A | a5 28 | b_319Aldazp_key_row_down; scan down key via PIA row/col ; x-ref: $3195 TXTTAB Pointer: Start of BASIC Text |
| $319C | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $319F | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $31A2 | 25 31 | andzp_key_col_down |
| $31A4 | d0 03 | bneb_31A9 |
| $31A6 | ee bc 30 | inclevel_select_delta; down pressed: delta = +1 |
| $31A9 | a5 2c | b_31A9ldazp_key_row_dig1; scan dig key (two key combos checked) ; x-ref: $31A4 ARYTAB Pointer: Start of BASIC Arrays |
| $31AB | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $31AE | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $31B1 | 25 35 | andzp_key_col_dig1 |
| $31B3 | f0 0c | beqb_31C1 |
| $31B5 | a5 2d | ldazp_key_row_dig2 |
| $31B7 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $31BA | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $31BD | 25 36 | andzp_key_col_dig2; CURLIN Current BASIC Line Number |
| $31BF | d0 05 | bneb_31C6 |
| $31C1 | a9 01 | b_31C1lda#$01; dig pressed: set confirm flag ; x-ref: $31B3 |
| $31C3 | 8d bd 30 | staselect_confirm_flag |
| $31C6 | ad bd 30 | b_31C6ldaselect_confirm_flag; dig confirmed → advance to state 2 ; x-ref: $31BF |
| $31C9 | f0 06 | beqb_31D1 |
| $31CB | ee 3c 30 | inctitle_screen_state |
| $31CE | 4c eb 31 | jmpj_31EB |
| $31D1 | ad bc 30 | b_31D1ldalevel_select_delta; no delta → nothing to do ; x-ref: $31C9 |
| $31D4 | f0 15 | beqj_31EB |
| $31D6 | a9 00 | lda#$00 |
| $31D8 | 8d be 30 | stakey_debounce_flag; clear debounce for next move |
| $31DB | ad 3d 30 | ldaselected_level; selected_level += delta |
| $31DE | 18 | clc |
| $31DF | 6d bc 30 | adclevel_select_delta |
| $31E2 | f0 4e | beqj_3232; clamp: skip update if result == 0 (underflow) |
| $31E4 | c9 0b | cmp#$0b; clamp: skip update if result == 11 (overflow) |
| $31E6 | f0 4a | beqj_3232 |
| $31E8 | 8d 3d 30 | staselected_level; valid: store new level selection |
| $31EB | 20 4d 32 | j_31EBjsrdraw_level_select_cursor; redraw cursor at new position ; x-ref: $31CE, $31D4 |
| $31EE | 4c 32 32 | jmpj_3232 |
| $31F1 | a0 01 | j_31F1ldy#$01; --- State 2: flash cursor and launch --- Y counts 2 flash cycles ; x-ref: $316B |
| $31F3 | 98 | b_31F3tya; erase cursor for flash-off frame ; x-ref: $321A |
| $31F4 | 48 | pha |
| $31F5 | 20 92 32 | jsrerase_level_select_cursor |
| $31F8 | 20 86 08 | jsrblit_screen |
| $31FB | 20 27 09 | jsrblit_sidebar |
| $31FE | a2 05 | ldx#$05 |
| $3200 | 20 c8 09 | b_3200jsrbusy_wait_pause; busy-wait x5 → flash-off hold ; x-ref: $3204 |
| $3203 | ca | dex |
| $3204 | 10 fa | bplb_3200 |
| $3206 | 20 4d 32 | jsrdraw_level_select_cursor; draw cursor for flash-on frame |
| $3209 | 20 86 08 | jsrblit_screen |
| $320C | 20 27 09 | jsrblit_sidebar |
| $320F | a2 05 | ldx#$05; busy-wait x5 → flash-on hold |
| $3211 | 20 c8 09 | b_3211jsrbusy_wait_pause; x-ref: $3215 |
| $3214 | ca | dex; loop: DEY → repeat for second flash cycle |
| $3215 | 10 fa | bplb_3211 |
| $3217 | 68 | pla |
| $3218 | a8 | tay |
| $3219 | 88 | dey |
| $321A | 10 d7 | bplb_31F3 |
| $321C | ad 3d 30 | ldaselected_level; selected_level → ui_level_num (active level) |
| $321F | 8d df 2a | staui_level_num |
| $3222 | 18 | clc; (selected_level + 9) mod 10 → level_select_num |
| $3223 | 69 09 | adc#$09 |
| $3225 | c9 0b | cmp#$0b |
| $3227 | 90 03 | bccb_322C |
| $3229 | 38 | sec; remap overflow: >=11 → subtract 10 |
| $322A | e9 0a | sbc#$0a |
| $322C | 8d e0 2a | b_322Cstalevel_select_num; store full-circuit sentinel for advance_level ; x-ref: $3227 |
| $322F | 20 b0 06 | jsrinit_new_game; launch game from this level |
| $3232 | 20 3c 11 | j_3232jsrrestore_sprite_bg; --- shared epilogue (all states) --- ; x-ref: $3164, $3178, $31E2, $31E6, $31EE |
| $3235 | 20 c5 32 | jsrai_boundary_collision; animate player icon walking on title |
| $3238 | 20 66 10 | jsrprocess_player_state |
| $323B | 20 5f 11 | jsrsave_bg_and_draw_sprite |
| $323E | 20 86 08 | jsrblit_screen |
| $3241 | 20 27 09 | jsrblit_sidebar |
| $3244 | ad 12 e8 | lda$e812; acknowledge VIA interrupt; PORT B or DDR B: Data Direction Register B |
| $3247 | 68 | pla |
| $3248 | a8 | tay |
| $3249 | 68 | pla |
| $324A | aa | tax |
| $324B | 68 | pla |
| $324C | 40 | rti |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $324D | a9 71 | lda#>playfield_row_10_page_base; x-ref: $315E, $31EB, $3206 |
| $324F | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3251 | ad 3d 30 | ldaselected_level |
| $3254 | 38 | sec |
| $3255 | e9 01 | sbc#$01 |
| $3257 | 0a | asla |
| $3258 | 0a | asla |
| $3259 | 18 | clc |
| $325A | 69 01 | adc#$01; A = (selected_level - 1) * 4 + 1 |
| $325C | a8 | tay |
| $325D | a9 5d | lda#$5d; | |
| $325F | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3261 | c6 59 | deczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3263 | a9 70 | lda#$70; top-left corner |
| $3265 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3267 | c8 | iny |
| $3268 | a9 40 | lda#$40; - |
| $326A | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $326C | c8 | iny |
| $326D | a9 40 | lda#$40; - |
| $326F | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3271 | c8 | iny |
| $3272 | a9 6e | lda#$6e; top-right corner |
| $3274 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3276 | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3278 | a9 5d | lda#$5d; | |
| $327A | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $327C | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $327E | a9 7d | lda#$7d; bottom-right corner |
| $3280 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3282 | 88 | dey |
| $3283 | a9 40 | lda#$40; - |
| $3285 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3287 | 88 | dey |
| $3288 | a9 40 | lda#$40; - |
| $328A | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $328C | 88 | dey |
| $328D | a9 6d | lda#$6d; bottom-left corner |
| $328F | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3291 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $3292 | a9 71 | lda#>playfield_row_10_page_base; x-ref: $3180, $31F5 |
| $3294 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3296 | ad 3d 30 | ldaselected_level |
| $3299 | 38 | sec |
| $329A | e9 01 | sbc#$01 |
| $329C | 0a | asla |
| $329D | 0a | asla |
| $329E | 18 | clc |
| $329F | 69 01 | adc#$01 |
| $32A1 | a8 | tay |
| $32A2 | a9 20 | lda#$20 |
| $32A4 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32A6 | c6 59 | deczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $32A8 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32AA | c8 | iny |
| $32AB | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32AD | c8 | iny |
| $32AE | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32B0 | c8 | iny |
| $32B1 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32B3 | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $32B5 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32B7 | e6 59 | inczp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $32B9 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32BB | 88 | dey |
| $32BC | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32BE | 88 | dey |
| $32BF | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32C1 | 88 | dey |
| $32C2 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $32C4 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $32C5 | ad b4 0d | ldadraw_offset; sprite column check ; x-ref: $3235 |
| $32C8 | c9 21 | cmp#$21; right wall (col 33)? |
| $32CA | d0 39 | bneb_3305; no → check left wall |
| $32CC | ad b5 0d | ldadraw_page; yes — check which row |
| $32CF | c9 7c | cmp#$7c; bottom-right corner? |
| $32D1 | d0 21 | bneb_32F4; no → check top-right corner |
| $32D3 | 20 72 07 | jsrget_random; yes: bottom-right corner — flip a coin |
| $32D6 | 29 01 | and#$01 |
| $32D8 | f0 0d | beqb_32E7; rand bit 0 = 0 → turn up |
| $32DA | a9 00 | lda#$00; rand bit 0 = 1 → turn left: dx=0, dy=-1 |
| $32DC | 8d b7 0d | stainput_dx |
| $32DF | a9 ff | lda#$ff |
| $32E1 | 8d b8 0d | stainput_dy; dy = -1 (move up) |
| $32E4 | 4c 3f 33 | jmpr_333F |
| $32E7 | a9 ff | b_32E7lda#$ff; turn up: dx=0 (already 0), dy=-1 ($FF) ; x-ref: $32D8 |
| $32E9 | 8d b7 0d | stainput_dx |
| $32EC | a9 00 | lda#$00 |
| $32EE | 8d b8 0d | stainput_dy; dy = 0 (move right/left only) |
| $32F1 | 4c 3f 33 | jmpr_333F |
| $32F4 | c9 74 | b_32F4cmp#$74; top-right corner (row $74)? ; x-ref: $32D1 |
| $32F6 | d0 47 | bner_333F; no (mid-wall) → no change, return |
| $32F8 | a9 01 | lda#$01; yes: top-right corner — turn down: dx=1, dy=0 |
| $32FA | 8d b7 0d | stainput_dx |
| $32FD | a9 00 | lda#$00 |
| $32FF | 8d b8 0d | stainput_dy |
| $3302 | 4c 3f 33 | jmpr_333F |
| $3305 | c9 08 | b_3305cmp#$08; left wall (col 8)? ; x-ref: $32CA |
| $3307 | d0 36 | bner_333F; no (mid-field) → no change, return |
| $3309 | ad b5 0d | ldadraw_page; yes — check which row |
| $330C | c9 7c | cmp#$7c; bottom-left corner? |
| $330E | d0 0d | bneb_331D; no → check top-left corner |
| $3310 | a9 ff | lda#$ff; yes: bottom-left corner — turn left: dx=-1, dy=0 |
| $3312 | 8d b7 0d | stainput_dx |
| $3315 | a9 00 | lda#$00 |
| $3317 | 8d b8 0d | stainput_dy |
| $331A | 4c 3f 33 | jmpr_333F |
| $331D | c9 74 | b_331Dcmp#$74; top-left corner (row $74)? ; x-ref: $330E |
| $331F | d0 1e | bner_333F; no (mid-wall) → no change, return |
| $3321 | 20 72 07 | jsrget_random; yes: top-left corner — flip a coin |
| $3324 | 29 01 | and#$01 |
| $3326 | f0 0d | beqb_3335; rand bit 0 = 0 → turn down |
| $3328 | a9 01 | lda#$01; rand bit 0 = 1 → turn right: dx=1, dy=0 |
| $332A | 8d b7 0d | stainput_dx |
| $332D | a9 00 | lda#$00 |
| $332F | 8d b8 0d | stainput_dy |
| $3332 | 4c 3f 33 | jmpr_333F |
| $3335 | a9 00 | b_3335lda#$00; turn down: dx=0, dy=1 ; x-ref: $3326 |
| $3337 | 8d b7 0d | stainput_dx |
| $333A | a9 01 | lda#$01 |
| $333C | 8d b8 0d | stainput_dy |
| $333F | 60 | r_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 |
| $3340 | a0 22 | ldy#$22; --- Phase 1: three horizontal bar rows, cols 34..7 --- ; x-ref: $3104 |
| $3342 | a9 7e | b_3342lda#>playfield_row_23_page_base; a59 = $7E (bottom decoration row) ; x-ref: $335B |
| $3344 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3346 | a9 fa | lda#$fa; $FA: bottom decoration bar char |
| $3348 | 91 58 | sta(zp_ptr_wipe_buf_lo),y; write to page $7E, col Y |
| $334A | e6 59 | inczp_ptr_wipe_buf_hi; a59 → $7F (near-bottom bar row); TEMPF2 Temporary storage for FLPT value. |
| $334C | a9 c2 | lda#$c2; $C2: near-bottom bar char |
| $334E | 91 58 | sta(zp_ptr_wipe_buf_lo),y; write to page $7F, col Y |
| $3350 | a9 75 | lda#>playfield_row_14_page_base; a59 → $75 (top border row) |
| $3352 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3354 | a9 63 | lda#$63; $63: top border bar char |
| $3356 | 91 58 | sta(zp_ptr_wipe_buf_lo),y; write to page $75, col Y |
| $3358 | 88 | dey; next column (right→left) |
| $3359 | c0 06 | cpy#$06; done when col 6 reached (cols 34..7 = 28 columns) |
| $335B | d0 e5 | bneb_3342 |
| $335D | a9 74 | lda#>playfield_row_13_page_base; --- Phase 2: vertical pillar segments, pages $74–$7D (10 rows) --- |
| $335F | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3361 | a0 07 | b_3361ldy#$07; left pillar starts at col 7 ; x-ref: $3387 |
| $3363 | a9 67 | lda#$67; $67: left pillar char 1 |
| $3365 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3367 | c8 | iny |
| $3368 | a9 43 | lda#$43; $43: left pillar char 2 |
| $336A | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $336C | c8 | iny |
| $336D | a9 65 | lda#$65; $65: left pillar char 3 (col 9) |
| $336F | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3371 | a0 20 | ldy#$20; right pillar starts at col 32 ($20) |
| $3373 | a9 67 | lda#$67 |
| $3375 | 91 58 | sta(zp_ptr_wipe_buf_lo),y; $67: right pillar char 1 (col 32) |
| $3377 | c8 | iny |
| $3378 | a9 43 | lda#$43 |
| $337A | 91 58 | sta(zp_ptr_wipe_buf_lo),y; $43: right pillar char 2 (col 33) |
| $337C | c8 | iny |
| $337D | a9 65 | lda#$65 |
| $337F | 91 58 | sta(zp_ptr_wipe_buf_lo),y; $65: right pillar char 3 (col 34) |
| $3381 | e6 59 | inczp_ptr_wipe_buf_hi; advance to next row page; TEMPF2 Temporary storage for FLPT value. |
| $3383 | a5 59 | ldazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3385 | c9 7e | cmp#$7e; stop before page $7E (bottom bars) |
| $3387 | d0 d8 | bneb_3361 |
| $3389 | a9 4f | lda#$4f; --- Phase 3: corner pieces --- |
| $338B | 8d 09 75 | staplayfield_row_14_col_8; $4F: top-right corner at page $75, col 8 |
| $338E | a9 50 | lda#$50; $50: top-left divider corner |
| $3390 | 8d 20 75 | staplayfield_row_14_col_31; at page $75, col 31 |
| $3393 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $3394 | a9 67 | lda#>playfield_row_buffer; x-ref: $3121 |
| $3396 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $3398 | a0 07 | ldy#$07 |
| $339A | a2 00 | ldx#$00 |
| $339C | bd 3e 30 | j_339Cldatxt_title_lode_runner,x; x-ref: $33A7 |
| $339F | c9 ff | cmp#$ff |
| $33A1 | f0 07 | beqr_33AA |
| $33A3 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $33A5 | e8 | inx |
| $33A6 | c8 | iny |
| $33A7 | 4c 9c 33 | jmpj_339C |
| $33AA | 60 | r_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 |
| $33AB | a9 68 | lda#>playfield_row_1_page_base; x-ref: $3124 |
| $33AD | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $33AF | a0 07 | ldy#$07 |
| $33B1 | a2 00 | ldx#$00 |
| $33B3 | bd 5b 30 | j_33B3ldatxt_title_underline,x; x-ref: $33BE |
| $33B6 | c9 ff | cmp#$ff |
| $33B8 | f0 07 | beqr_33C1 |
| $33BA | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $33BC | e8 | inx |
| $33BD | c8 | iny |
| $33BE | 4c b3 33 | jmpj_33B3 |
| $33C1 | 60 | r_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 |
| $33C2 | a9 6a | lda#>playfield_row_3_page_base; x-ref: $3127 |
| $33C4 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $33C6 | a0 0d | ldy#$0d |
| $33C8 | a2 00 | ldx#$00 |
| $33CA | bd 78 30 | j_33CAldatxt_title_high_score,x; x-ref: $33D5 |
| $33CD | c9 ff | cmp#$ff |
| $33CF | f0 07 | beqr_33D8 |
| $33D1 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $33D3 | e8 | inx |
| $33D4 | c8 | iny |
| $33D5 | 4c ca 33 | jmpj_33CA |
| $33D8 | 60 | r_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 |
| $33D9 | a9 6d | lda#>playfield_row_6_page_base; x-ref: $312A |
| $33DB | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $33DD | a0 0a | ldy#$0a |
| $33DF | a2 00 | ldx#$00 |
| $33E1 | bd 84 30 | j_33E1ldatxt_title_select_level,x; x-ref: $33EC |
| $33E4 | c9 ff | cmp#$ff |
| $33E6 | f0 07 | beqr_33EF |
| $33E8 | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $33EA | e8 | inx |
| $33EB | c8 | iny |
| $33EC | 4c e1 33 | jmpj_33E1 |
| $33EF | 60 | r_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 |
| $33F0 | a9 6e | lda#>playfield_row_7_page_base; x-ref: $312D |
| $33F2 | 85 59 | stazp_ptr_wipe_buf_hi; TEMPF2 Temporary storage for FLPT value. |
| $33F4 | a0 05 | ldy#$05 |
| $33F6 | a2 00 | ldx#$00 |
| $33F8 | bd 9b 30 | j_33F8ldatxt_title_dig_to_select,x; x-ref: $3403 |
| $33FB | c9 ff | cmp#$ff |
| $33FD | f0 07 | beqr_3406 |
| $33FF | 91 58 | sta(zp_ptr_wipe_buf_lo),y |
| $3401 | e8 | inx |
| $3402 | c8 | iny |
| $3403 | 4c f8 33 | jmpj_33F8 |
| $3406 | 60 | r_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. |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $3407 | ad 43 29 | draw_intro_hi_scoreldahi_score_high_bcd; x-ref: $3130 |
| $340A | 29 f0 | and#$f0 |
| $340C | 4a | lsra |
| $340D | 4a | lsra |
| $340E | 4a | lsra |
| $340F | 4a | lsra |
| $3410 | 18 | clc |
| $3411 | 69 30 | adc#$30 |
| $3413 | 8d 19 6a | staplayfield_intro_hi_score_thousands; High score character digit 1 (high BCD nibble of hi_score_high_bcd); High Score thousands digit (Title Screen) |
| $3416 | ad 43 29 | ldahi_score_high_bcd |
| $3419 | 29 0f | and#$0f |
| $341B | 18 | clc |
| $341C | 69 30 | adc#$30 |
| $341E | 8d 1a 6a | staplayfield_intro_hi_score_hundreds; High score character digit 2 (low BCD nibble of hi_score_high_bcd); High Score hundreds digit (Title Screen) |
| $3421 | ad 44 29 | ldahi_score_low_bcd |
| $3424 | 29 f0 | and#$f0 |
| $3426 | 4a | lsra |
| $3427 | 4a | lsra |
| $3428 | 4a | lsra |
| $3429 | 4a | lsra |
| $342A | 18 | clc |
| $342B | 69 30 | adc#$30 |
| $342D | 8d 1b 6a | staplayfield_intro_hi_score_tens; High score character digit 3 (high BCD nibble of hi_score_low_bcd); High Score tens digit (Title Screen) |
| $3430 | ad 44 29 | ldahi_score_low_bcd |
| $3433 | 29 0f | and#$0f |
| $3435 | 18 | clc |
| $3436 | 69 30 | adc#$30 |
| $3438 | 8d 1c 6a | staplayfield_intro_hi_score_ones; High score character digit 4 (low BCD nibble of hi_score_low_bcd); High Score ones digit (Title Screen) |
| $343B | 60 | rts |
| $343C | | menu_cursor_page.byte$72; x-ref: $363A, $370C, $371F, $374D, $37B0 |
| $343D | | menu_nav_delta.byte$00; x-ref: $36DE, $36ED, $36FC, $36FF, $3710, $3714 |
| $343E | | menu_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 |
| $3451 | | txt_game_paused_l1.text" uccccccccccccci ", $ff |
| $3463 | | txt_game_paused_l2.text" b h ", $ff |
| $3475 | | txt_game_paused_l3.text" b GAME PAUSED h ", $ff |
| $3487 | | txt_game_paused_l4.text" b ", $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, $63, " h ", $ff |
| $3499 | | txt_game_paused_l5.text" b h ", $ff |
| $34AB | | txt_game_paused_l6.text" b CONTINUE h ", $ff |
| $34BD | | txt_game_paused_l7.text" b h ", $ff |
| $34CF | | txt_game_paused_l8.text" bRESTART LEVELh ", $ff |
| $34E1 | | txt_game_paused_l9.text" b h ", $ff |
| $34F3 | | txt_game_paused_la.text" b END GAME h ", $ff |
| $3505 | | txt_game_paused_lb.text" b h ", $ff |
| $3517 | | txt_game_paused_lc.text" jfffffffffffffk ", $ff |
| $3529 | | txt_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. |
| $353C | | banner_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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $3638 | a9 72 | open_pause_menulda#$72; pre-select CONTINUE (first row, page $72) ; x-ref: $2919 |
| $363A | 8d 3c 34 | stamenu_cursor_page |
| $363D | a9 01 | lda#$01; key-release lockout: pause key is still held on entry |
| $363F | 8d 3e 34 | stamenu_key_lockout |
| $3642 | a9 04 | lda#$04; game_state_flag = 4 (pause/menu state) |
| $3644 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $3646 | a9 6c | lda#$6c; ZP pointer = $6C09: offscreen buffer row 5, col 8 (banner top-left) |
| $3648 | 85 7a | stazp_ptr_menu_buf_hi |
| $364A | a9 09 | lda#$09 |
| $364C | 85 79 | stazp_ptr_menu_buf_lo |
| $364E | a2 00 | ldx#$00; X = layout table index; Y = column within current row |
| $3650 | a0 00 | ldy#$00 |
| $3652 | bd 3f 34 | j_3652ldatxt_game_paused_table,x; --- Pass 1: snapshot offscreen buffer → banner_data_table --- ; x-ref: $3664, $366C |
| $3655 | c9 ff | cmp#$ff |
| $3657 | f0 0e | beqb_3667; $FF = newline sentinel |
| $3659 | c9 fe | cmp#$fe; $FE = end-of-table → begin pass 2 |
| $365B | f0 12 | beqb_366F |
| $365D | b1 79 | lda(zp_ptr_menu_buf_lo),y; read background char from offscreen buffer |
| $365F | 9d 3c 35 | stabanner_data_table,x; save to parallel snapshot buffer at same index |
| $3662 | e8 | inx |
| $3663 | c8 | iny |
| $3664 | 4c 52 36 | jmpj_3652 |
| $3667 | a0 00 | b_3667ldy#$00; newline: reset column to 0, advance to next buffer row (page++) ; x-ref: $3657 |
| $3669 | e6 7a | inczp_ptr_menu_buf_hi |
| $366B | e8 | inx |
| $366C | 4c 52 36 | jmpj_3652 |
| $366F | a9 6c | b_366Flda#$6c; reset ZP pointer back to $6C09 for second pass ; x-ref: $365B |
| $3671 | 85 7a | stazp_ptr_menu_buf_hi |
| $3673 | a9 09 | lda#$09 |
| $3675 | 85 79 | stazp_ptr_menu_buf_lo |
| $3677 | a2 00 | ldx#$00; restart both indices from zero |
| $3679 | a0 00 | ldy#$00 |
| $367B | bd 3f 34 | j_367Bldatxt_game_paused_table,x; read menu text char (or $FF/$FE control code) ; x-ref: $368A, $3692 |
| $367E | c9 ff | cmp#$ff |
| $3680 | f0 0b | beqb_368D; $FF = newline sentinel |
| $3682 | c9 fe | cmp#$fe; $FE = end-of-table → install IRQ vector and return |
| $3684 | f0 0f | beqb_3695 |
| $3686 | 91 79 | sta(zp_ptr_menu_buf_lo),y; write menu text char directly to offscreen buffer |
| $3688 | e8 | inx |
| $3689 | c8 | iny |
| $368A | 4c 7b 36 | jmpj_367B |
| $368D | a0 00 | b_368Dldy#$00; newline: reset column to 0, advance to next buffer row ; x-ref: $3680 |
| $368F | e6 7a | inczp_ptr_menu_buf_hi |
| $3691 | e8 | inx |
| $3692 | 4c 7b 36 | jmpj_367B |
| $3695 | a9 9e | b_3695lda#<pause_menu_irq; redirect IRQ vector to pause_menu_irq (low byte) ; x-ref: $3684 |
| $3697 | 85 90 | stazp_irq_vector_lo; CINV Vector: Hardware Interrupt |
| $3699 | a9 36 | lda#>pause_menu_irq; redirect IRQ vector to pause_menu_irq (high byte) |
| $369B | 85 91 | stazp_irq_vector_hi |
| $369D | 60 | rts; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $369E | 20 31 37 | pause_menu_irqjsrmenu_clear_highlights; strip reverse-video from all rows; leaves ZP $79=$0B for highlight calls ; x-ref: $3695, $3699 |
| $36A1 | ad 3e 34 | ldamenu_key_lockout; key-release lockout flag (1=waiting for keys up) |
| $36A4 | f0 0f | beqb_36B5; no lockout → proceed to key scanning |
| $36A6 | 20 65 1a | jsrwait_any_key; lockout active: poll until all keys released ($FF = no key) |
| $36A9 | c9 ff | cmp#$ff; key still held → re-highlight + blit, keep waiting |
| $36AB | d0 75 | bneb_3722 |
| $36AD | a9 00 | lda#$00; all keys released: clear lockout flag |
| $36AF | 8d 3e 34 | stamenu_key_lockout |
| $36B2 | 4c 22 37 | jmpb_3722 |
| $36B5 | a5 2c | b_36B5ldazp_key_row_dig1; --- path 2: CONFIRM check (DIG1 key) --- ; x-ref: $36A4 ARYTAB Pointer: Start of BASIC Arrays |
| $36B7 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $36BA | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $36BD | 25 35 | andzp_key_col_dig1 |
| $36BF | f0 0c | beqb_36CD; DIG1 not pressed → check DIG2 |
| $36C1 | a5 2d | ldazp_key_row_dig2 |
| $36C3 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $36C6 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $36C9 | 25 36 | andzp_key_col_dig2; CURLIN Current BASIC Line Number |
| $36CB | d0 0f | bneb_36DC |
| $36CD | 20 60 37 | b_36CDjsrmenu_blink_cursor; --- confirm path: blink row, redraw banner, dispatch --- ; x-ref: $36BF |
| $36D0 | 20 86 37 | jsrmenu_draw_banner |
| $36D3 | 20 86 08 | jsrblit_screen |
| $36D6 | 20 b0 37 | jsrmenu_dispatch_selection |
| $36D9 | 4c 25 37 | jmpj_3725 |
| $36DC | a9 00 | b_36DClda#$00; x-ref: $36CB |
| $36DE | 8d 3d 34 | stamenu_nav_delta |
| $36E1 | a5 2a | ldazp_key_row_left; VARTAB Pointer: Start of BASIC Variables |
| $36E3 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $36E6 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $36E9 | 25 33 | andzp_key_col_left |
| $36EB | d0 03 | bneb_36F0 |
| $36ED | ce 3d 34 | decmenu_nav_delta |
| $36F0 | a5 2b | b_36F0ldazp_key_row_right; x-ref: $36EB |
| $36F2 | 8d 10 e8 | sta$e810; PORT A or DDR A: Data Direction Register A |
| $36F5 | ad 12 e8 | lda$e812; PORT B or DDR B: Data Direction Register B |
| $36F8 | 25 34 | andzp_key_col_right; MEMSIZ Pointer: Highest Address Used by BASIC |
| $36FA | d0 03 | bneb_36FF |
| $36FC | ee 3d 34 | incmenu_nav_delta |
| $36FF | ad 3d 34 | b_36FFldamenu_nav_delta; delta = 0: no key held → skip to re-highlight + blit ; x-ref: $36FA |
| $3702 | d0 03 | bneb_3707 |
| $3704 | 4c 22 37 | jmpb_3722 |
| $3707 | a9 01 | b_3707lda#$01; set lockout: don't accept another move until key released ; x-ref: $3702 |
| $3709 | 8d 3e 34 | stamenu_key_lockout |
| $370C | ad 3c 34 | ldamenu_cursor_page; a343C + 2*a343D (rows are 2 pages apart: $72→$74→$76) |
| $370F | 18 | clc |
| $3710 | 6d 3d 34 | adcmenu_nav_delta |
| $3713 | 18 | clc |
| $3714 | 6d 3d 34 | adcmenu_nav_delta |
| $3717 | c9 78 | cmp#$78; $78 = one past END GAME row → clamp (don't move past bottom) |
| $3719 | f0 07 | beqb_3722 |
| $371B | c9 70 | cmp#$70; $70 = one before CONTINUE row → clamp (don't move past top) |
| $371D | f0 03 | beqb_3722 |
| $371F | 8d 3c 34 | stamenu_cursor_page; in-range: commit new selected row |
| $3722 | 20 4d 37 | b_3722jsrmenu_highlight_selected; --- epilogue: re-highlight selected row, blit, RTI --- ; x-ref: $36AB, $36B2, $3704, $3719, $371D |
| $3725 | 20 86 08 | j_3725jsrblit_screen; x-ref: $36D9 |
| $3728 | ad 12 e8 | lda$e812; flush offscreen buffer to screen RAM; PORT B or DDR B: Data Direction Register B |
| $372B | 68 | pla; restore Y, X, A saved by IRQ trampoline |
| $372C | a8 | tay |
| $372D | 68 | pla |
| $372E | aa | tax |
| $372F | 68 | pla |
| $3730 | 40 | rti |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $3731 | a9 0b | lda#$0b; low byte of ZP pointer: offset $0B within each page (inner text area) ; x-ref: $369E, $3764 |
| $3733 | 85 79 | stazp_ptr_menu_buf_lo |
| $3735 | a2 72 | ldx#$72; start at page $72 (CONTINUE row) |
| $3737 | 86 7a | b_3737stxzp_ptr_menu_buf_hi; set high byte of ZP pointer to current row page ; x-ref: $374A |
| $3739 | a0 00 | ldy#$00 |
| $373B | b1 79 | b_373Blda(zp_ptr_menu_buf_lo),y; read char, strip bit 7 (clear reverse video), write back ; x-ref: $3744 |
| $373D | 29 7f | and#$7f |
| $373F | 91 79 | sta(zp_ptr_menu_buf_lo),y |
| $3741 | c8 | iny |
| $3742 | c0 0d | cpy#$0d; 13 chars per row ($00–$0C) |
| $3744 | d0 f5 | bneb_373B |
| $3746 | e8 | inx; step by 2: $72→$74→$76 (skipping spacer rows) |
| $3747 | e8 | inx |
| $3748 | e0 78 | cpx#$78; stop after page $76 (END GAME row) |
| $374A | d0 eb | bneb_3737 |
| $374C | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| $374D | ae 3c 34 | ldxmenu_cursor_page; page of selected row ($72=CONTINUE, $74=RESTART, $76=END GAME) ; x-ref: $3722, $3772 |
| $3750 | 86 7a | stxzp_ptr_menu_buf_hi; set high byte of ZP pointer to selected row's page |
| $3752 | a0 00 | ldy#$00 |
| $3754 | b1 79 | b_3754lda(zp_ptr_menu_buf_lo),y; read char, set bit 7 (reverse video on), write back ; x-ref: $375D |
| $3756 | 09 80 | ora#$80 |
| $3758 | 91 79 | sta(zp_ptr_menu_buf_lo),y |
| $375A | c8 | iny |
| $375B | c0 0d | cpy#$0d; 13 chars ($00–$0C), same span as menu_clear_highlights |
| $375D | d0 f5 | bneb_3754 |
| $375F | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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) |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $3760 | a0 02 | menu_blink_cursorldy#$02; Y = 2 blink cycles to run ; x-ref: $36CD |
| $3762 | 98 | b_3762tya; save cycle counter (Y clobbered by blit/highlight calls) ; x-ref: $3783 |
| $3763 | 48 | pha |
| $3764 | 20 31 37 | jsrmenu_clear_highlights; --- dark phase: all rows un-highlighted --- |
| $3767 | 20 86 08 | jsrblit_screen |
| $376A | a2 04 | ldx#$04 |
| $376C | 20 c8 09 | b_376Cjsrbusy_wait_pause; x-ref: $3770 |
| $376F | ca | dex |
| $3770 | d0 fa | bneb_376C |
| $3772 | 20 4d 37 | jsrmenu_highlight_selected; --- lit phase: selected row reverse-video on --- |
| $3775 | 20 86 08 | jsrblit_screen |
| $3778 | a2 08 | ldx#$08 |
| $377A | 20 c8 09 | b_377Ajsrbusy_wait_pause; x-ref: $377E |
| $377D | ca | dex |
| $377E | d0 fa | bneb_377A |
| $3780 | 68 | pla; restore cycle counter |
| $3781 | a8 | tay |
| $3782 | 88 | dey; repeat for 2 cycles total |
| $3783 | d0 dd | bneb_3762 |
| $3785 | 60 | rts |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| ; 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 |
| ; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- |
| $3786 | a9 6c | menu_draw_bannerlda#>playfield_row_5_offset_09_banner; point $7A/$79 to offscreen buffer start: row 5, col 8 ($6C09) ; x-ref: $36D0 |
| $3788 | 85 7a | stazp_ptr_menu_buf_hi |
| $378A | a9 09 | lda#<playfield_row_5_offset_09_banner |
| $378C | 85 79 | stazp_ptr_menu_buf_lo |
| $378E | a2 00 | ldx#$00; X = layout/data table index; Y = column offset within current row |
| $3790 | a0 00 | ldy#$00 |
| $3792 | bd 3f 34 | j_3792ldatxt_game_paused_table,x; read layout byte: $FF=newline, $FE=end, else=copy slot ; x-ref: $37A4, $37AC |
| $3795 | c9 ff | cmp#$ff |
| $3797 | f0 0e | beqb_37A7; $FF = newline sentinel |
| $3799 | c9 fe | cmp#$fe; $FE = end-of-table sentinel → done |
| $379B | f0 12 | beqr_37AF |
| $379D | bd 3c 35 | ldabanner_data_table,x; read background char from parallel data table |
| $37A0 | 91 79 | sta(zp_ptr_menu_buf_lo),y; write to offscreen buffer at current row+col |
| $37A2 | e8 | inx; advance both table index and column |
| $37A3 | c8 | iny |
| $37A4 | 4c 92 37 | jmpj_3792 |
| $37A7 | a0 00 | b_37A7ldy#$00; newline: reset column to 0, advance to next buffer page (next screen row) ; x-ref: $3797 |
| $37A9 | e6 7a | inczp_ptr_menu_buf_hi |
| $37AB | e8 | inx |
| $37AC | 4c 92 37 | jmpj_3792 |
| $37AF | 60 | r_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 |
| $37B0 | ad 3c 34 | ldamenu_cursor_page; offscreen-buffer page of highlighted row ($72/$74/$76) ; x-ref: $36D6 |
| $37B3 | c9 72 | cmp#$72; row 1 selected? (CONTINUE / START GAME) |
| $37B5 | d0 0d | bneb_37C4; no → try next option |
| $37B7 | a9 01 | lda#$01; game_state_flag = 1 (gameplay active) |
| $37B9 | 85 67 | stazp_game_state_flag; ARGHO Floating Accum. #2: Mantissa |
| $37BB | a9 4a | lda#<gameplay_irq; install gameplay IRQ handler low byte into ZP $90 |
| $37BD | 85 90 | stazp_irq_vector_lo; CINV Vector: Hardware Interrupt |
| $37BF | a9 06 | lda#>gameplay_irq; install gameplay IRQ handler high byte into ZP $91 |
| $37C1 | 85 91 | stazp_irq_vector_hi |
| $37C3 | 60 | rts |
| $37C4 | c9 74 | b_37C4cmp#$74; row 2 selected? (RESTART LEVEL) ; x-ref: $37B5 |
| $37C6 | d0 07 | bneb_37CF; no → try next option |
| $37C8 | 20 dd 09 | jsrtransition_from_full_to_empty; flash the selected row before acting |
| $37CB | 20 11 07 | jsrhandle_player_death; restart: trigger death/respawn sequence at current level |
| $37CE | 60 | rts |
| $37CF | c9 76 | b_37CFcmp#$76; row 3 selected? (QUIT TO TITLE) ; x-ref: $37C6 |
| $37D1 | d0 07 | bner_37DA; no → unrecognized row, do nothing |
| $37D3 | 20 dd 09 | jsrtransition_from_full_to_empty; flash the selected row before acting |
| $37D6 | 20 bf 30 | jsrswitch_to_title_screen; return to title screen |
| $37D9 | 60 | rts |
| $37DA | 60 | r_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. |
| $5910 | | level_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 |
| $5A10 | | level_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 |
| $5B10 | | level_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 |
| $5C10 | | level_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 |
| $5D10 | | level_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 |
| $5E10 | | level_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 |
| $5F10 | | level_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 |
| $6010 | | level_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 |
| $6110 | | level_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 |
| $6210 | | level_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 |
| $6300 | | wipe_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 |
| $6400 | | wipe_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 |
| $6500 | | queue_array_x.fill256, $00; x-ref: $1C80, $1C91, $1C9D, $1CA0, $1D54 |
| $6600 | | queue_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. |
| $6700 | | playfield_row_buffer.byte$00; x-ref: $07A1, $07F4, $169A, $2E79, $2E93, $3394 |