;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
;
; Auto-generated by Regenerator 2000 v0.9.17
; https://github.com/ricardoquesada/regenerator2000
;
; Exported from: c128_hero_is_back.regen2000proj
;
; Assemble with 64tass:
; 64tass -o c128_hero_is_back.prg c128_hero_is_back.asm
;
;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; EXTERNAL LABELS
; zpf_ = Zero Page Field
; zpa_ = Zero Page Absolute Address
; zpp_ = Zero Page Pointer
; f_ = Field
; a_ = Absolute Address
; p_ = Pointer
; L_ = Other / User-defined
zp_work0 = $02 ; x-ref: $1CB5, $1CE8, $1F37, $1FDF, $2031, ...
zp_work1 = $03 ; x-ref: $1CBA, $1CE2, $1F3B, $1FE3, $2035, ...
zp_work2 = $04 ; x-ref: $1F4F, $1FF7, $4A1E, $4A2C, $4A2E, ...
zp_work3 = $05 ; x-ref: $1F5A, $2002, $4A22, $4A32, $4A3C, ...
zp_ptr_aux1_lo = $06 ; x-ref: $4B78, $4BAC, $4BF2, $4BF7, $4D3A, ...
zp_ptr_aux1_hi = $07 ; x-ref: $4B7C, $4BF9, $4BFD, $4D3F, $69F4, ...
zp_ptr_aux2_lo = $08 ; x-ref: $4B80, $4BB9, $4BC7, $4BFF, $4C04, ...
zp_ptr_aux2_hi = $09 ; x-ref: $4B84, $4C06, $4C0A, $69FC, $6A3B, ...
zp_ptr_aux3_lo = $0a ; x-ref: $4B88, $4BCC, $4C0C, $4C11
zp_ptr_aux3_hi = $0b ; x-ref: $4B8C, $4C13, $4C17
zp_ptr_aux4_lo = $0c ; x-ref: $4B90, $4BB7, $4C19, $4C1E
zp_ptr_aux4_hi = $0d ; x-ref: $4B94, $4C20, $4C24
zp_temp_pixel_y = $1b ; x-ref: $7258, $727F, $72B3, $72F5, $7361, ...
zp_ptr_map_lo = $1c ; x-ref: $722E, $7237, $7251, $7270, $72A4, ...
zp_ptr_map_hi = $1d ; x-ref: $4A26, $4A5A, $4A5C, $4AE8, $7233, ...
zp_temp_x = $1e ; x-ref: $4A4F, $4A54, $4A78, $4A88, $4A8A, ...
zp_temp_y = $1f ; x-ref: $4AA2, $4AAD, $4ABE, $4AC0, $4AC7, ...
zpf_sort_order = $20 ; x-ref: $53F5, $5411, $541D, $542B, $5444, ...
zpf_sort_order_1 = $21 ; x-ref: $540C, $541B, $541F, $5426, $547E
zp_sort_order_2 = $22 ; x-ref: $54B8
zp_sort_order_3 = $23 ; x-ref: $54F2
zp_sort_order_4 = $24 ; x-ref: $552C
zp_sort_order_5 = $25 ; x-ref: $5566
zp_sort_order_6 = $26 ; x-ref: $55A0
zp_sort_order_7 = $27 ; x-ref: $55DA
zp_spr_y_pos = $0033 ; x-ref: $4C2D, $53F9, $540E, $5413, $5428, ...
zp_mux_y_bomb = $34 ; x-ref: $81B1, $81F4
zp_mux_y_fire = $35 ; x-ref: $792E, $7954
zp_mux_y_explode = $36 ; x-ref: $7076, $708E
zp_mux_y_entities = $37 ; x-ref: $7793, $77B2
zp_screen_dirty = $0045 ; x-ref: $4C38, $4C6A, $781C, $7AEE, $8397
zp_spr_x_lo = $46 ; x-ref: $544B, $5485, $54BF, $54F9, $5533, ...
zp_mux_x_lo_bomb = $47 ; x-ref: $81D6, $81E3
zp_mux_x_lo_fire = $48 ; x-ref: $7946
zp_mux_x_lo_explode = $49 ; x-ref: $707F
zp_mux_x_lo_entities = $4a ; x-ref: $77A3
zp_mux_x_lo_hazard = $58 ; x-ref: $780D, $7ADF, $8388
zp_spr_x_hi = $59 ; x-ref: $545A, $5494, $54CE, $5508, $5542, ...
zp_mux_x_hi_bomb = $5a ; x-ref: $81DC, $81E7
zp_mux_x_hi_fire = $5b ; x-ref: $794D
zp_mux_x_hi_explode = $5c ; x-ref: $7086
zp_mux_x_hi_entities = $5d ; x-ref: $77AA
zp_mux_x_hi_hazard = $6b ; x-ref: $7814, $7AE6, $838F
zp_spr_frame = $6c ; x-ref: $5450, $548A, $54C4, $54FE, $5538, ...
zp_mux_frame_bomb = $6d ; x-ref: $820F, $8218
zp_mux_frame_fire = $6e ; x-ref: $7967
zp_mux_frame_explode = $6f ; x-ref: $7093
zp_mux_frame_entities = $70 ; x-ref: $77B7
zp_mux_frame_hazard = $7e ; x-ref: $7820, $7AF3, $839B
zp_spr_color = $007f ; x-ref: $4C79, $50AC, $5455, $548F, $54C9, ...
zp_spr_color_p2 = $0080 ; x-ref: $4C7E, $50AF
zp_dynamite_timer = $81 ; x-ref: $7844
zp_screen_color = $82 ; x-ref: $6F16
zp_mux_color_entities = $83 ; x-ref: $77BC
zp_spr_color_extra = $0091 ; x-ref: $50B2, $7824, $7AF7, $839F
zp_spr_expand_x = $92 ; x-ref: $53FD, $5466, $54A0, $54DA, $5514, ...
zp_mux_flip_fire = $94 ; x-ref: $7972
zp_spr_priority = $a5 ; x-ref: $53FF, $5472, $54AC, $54E6, $5520, ...
zp_dynamite_active = $a7 ; x-ref: $7848
zp_current_device = $ba ; x-ref: $20C8
zp_init_flag = $d8 ; x-ref: $20D7
zp_math_temp = $ed ; x-ref: $21A5, $21A9, $21AC, $21B2, $21B5, ...
zp_ptr_text_lo = $f5 ; x-ref: $3777, $3785, $37A4, $37B4, $37CE, ...
zp_ptr_text_hi = $f6 ; x-ref: $3789, $37A8, $37B8, $37D2, $60A1
zp_ptr_screen_lo = $f7 ; x-ref: $378B, $3790, $37C3, $60A5
zp_ptr_screen_hi = $f8 ; x-ref: $3792, $3796, $60A9
zp_ptr_src_lo = $00fb ; x-ref: $1C59, $1C69, $1C86, $1C94, $1CAB, ...
zp_ptr_src_hi = $00fc ; x-ref: $1C5E, $1C6F, $1C89, $1C9A, $1CB0, ...
zp_ptr_dst_lo = $fd ; x-ref: $1CBE, $1CD2, $1CDA, $2083, $36B6, ...
zp_ptr_dst_hi = $fe ; x-ref: $1CC2, $1CDE, $2085, $36BC, $36D3, ...
zp_temp = $ff ; x-ref: $1C63, $1C65, $1C75, $1C8C, $1C8E, ...
IRQ_VECTOR_LO = $0314 ; x-ref: $2150, $2179, $217F, $2196
IRQ_VECTOR_HI = $0315 ; x-ref: $2153, $2182, $219B
SCREEN_RAM = $0400 ; x-ref: $3648, $4B3E, $4C93, $8C17
SCREEN_RAM_R1C0 = $0428 ; x-ref: $8C18
SCREEN_RAM_R1C8 = $0430 ; x-ref: $69EE, $69F2
SCREEN_RAM_R1C15 = $0437 ; x-ref: $6D70, $6D74
SCREEN_RAM_R2C0 = $0450 ; x-ref: $8C19
SCREEN_RAM_R3C0 = $0478 ; x-ref: $8C1A
SCREEN_RAM_R3C8 = $0480 ; x-ref: $6D85, $6D89
SCREEN_RAM_R4C0 = $04a0 ; x-ref: $8C1B
SCREEN_RAM_R4C10 = $04aa ; x-ref: $4B41, $4C9F
SCREEN_RAM_R5C0 = $04c8 ; x-ref: $8C1C
SCREEN_RAM_R6C0 = $04f0 ; x-ref: $8C1D
SCREEN_RAM_R6C4 = $04f4 ; x-ref: $6613, $6617
SCREEN_RAM_R6C10 = $04fa ; x-ref: $364B
SCREEN_RAM_R7C0 = $0518 ; x-ref: $8C1E
SCREEN_RAM_R7C6 = $051e ; x-ref: $5ED6, $5EDA
SCREEN_RAM_R7C32 = $0538 ; x-ref: $6977, $697B
SCREEN_RAM_R8C0 = $0540 ; x-ref: $8C1F
SCREEN_RAM_R8C6 = $0546 ; x-ref: $5EEB, $5EEF
SCREEN_RAM_R8C20 = $0554 ; x-ref: $4B44, $4CAB
SCREEN_RAM_R9C0 = $0568 ; x-ref: $8C20
SCREEN_RAM_R10C0 = $0590 ; x-ref: $8C21
SCREEN_RAM_R11C0 = $05b8 ; x-ref: $8C22
SCREEN_RAM_R11C11 = $05c3 ; x-ref: $698C, $6990
SCREEN_RAM_R12C0 = $05e0 ; x-ref: $8C23
SCREEN_RAM_R12C6 = $05e6 ; x-ref: $5F00, $5F04
SCREEN_RAM_R12C10 = $05ea ; x-ref: $6EC1, $6EC5
SCREEN_RAM_R12C13 = $05ed ; x-ref: $6680, $6684, $6EEB, $6EEF
SCREEN_RAM_R12C20 = $05f4 ; x-ref: $364E
SCREEN_RAM_R12C30 = $05fe ; x-ref: $4B47, $4CB7
SCREEN_RAM_R13C0 = $0608 ; x-ref: $8C24
SCREEN_RAM_R13C5 = $060d ; x-ref: $6B64
SCREEN_RAM_R13C6 = $060e ; x-ref: $5F15, $5F19
SCREEN_RAM_R14C0 = $0630 ; x-ref: $8C25
SCREEN_RAM_R15C0 = $0658 ; x-ref: $8C26
SCREEN_RAM_R16C0 = $0680 ; x-ref: $7A4B, $7A55, $7A5F, $7A69, $8C27
SCREEN_RAM_R17C23 = $06bf ; x-ref: $6B7A
SCREEN_RAM_R18C1 = $06d1 ; x-ref: $62BF, $62C3
SCREEN_RAM_R18C7 = $06d7 ; x-ref: $829C, $82A4
SCREEN_RAM_R18C9 = $06d9 ; x-ref: $6628, $662C
SCREEN_RAM_R18C30 = $06ee ; x-ref: $3651
SCREEN_RAM_R19C4 = $06fc ; x-ref: $520D, $5227
SCREEN_RAM_R19C5 = $06fd ; x-ref: $5210, $522C
SCREEN_RAM_R19C25 = $0711 ; x-ref: $6352, $6368
SCREEN_RAM_R20C5 = $0725 ; x-ref: $5213, $5231
SCREEN_RAM_R20C25 = $0739 ; x-ref: $6357, $636B
SCREEN_RAM_R22C4 = $0774 ; x-ref: $62E7, $62EB
SCREEN_RAM_R22C12 = $077c ; x-ref: $636F, $6373
SCREEN_RAM_R23C21 = $07ad ; x-ref: $69BA, $69BE
SCREEN_RAM_R24C1 = $07c1 ; x-ref: $630C, $6310
SCREEN_RAM_R24C16 = $07d0 ; x-ref: $6321, $6325
SCREEN_RAM_R24C35 = $07e3 ; x-ref: $6336, $633A
SPRITE_PTR_0 = $07f8 ; x-ref: $5452, $565A, $64DB, $66C9, $6BB2, ...
SPRITE_PTR_1 = $07f9 ; x-ref: $548C, $56EC, $6BB6, $6C98
SPRITE_PTR_2 = $07fa ; x-ref: $54C6, $577E, $6BBA, $6C9C
SPRITE_PTR_3 = $07fb ; x-ref: $5500, $5810, $6BBE, $6CA0
SPRITE_PTR_4 = $07fc ; x-ref: $553A, $58A2, $6BC2, $6CA4
SPRITE_PTR_5 = $07fd ; x-ref: $5574, $5934, $6CA8
SPRITE_PTR_6 = $07fe ; x-ref: $55AE, $59C6
SPRITE_PTR_7 = $07ff ; x-ref: $55E8, $5A58
game_flags = $0a04 ; x-ref: $20D9, $20DE
cave_data_buffer = $1300 ; x-ref: $1CBC, $1CC0
VIC_RAM_CONFIG = $d506 ; C128: RAM Configuration Register (RCR) ; x-ref: $1CC6, $1CF3
COLOR_RAM = $d800 ; x-ref: $3655, $4C99
COLOR_RAM_R1C0 = $d828 ; x-ref: $5071, $507D
COLOR_RAM_R1C8 = $d830 ; x-ref: $69F6, $69FA
COLOR_RAM_R4C10 = $d8aa ; x-ref: $4CA5
COLOR_RAM_R5C0 = $d8c8 ; x-ref: $6048
COLOR_RAM_R6C0 = $d8f0 ; x-ref: $5080, $508C
COLOR_RAM_R6C10 = $d8fa ; x-ref: $3658
COLOR_RAM_R8C20 = $d954 ; x-ref: $4CB1
COLOR_RAM_R11C0 = $d9b8 ; x-ref: $508F, $509B, $6A45, $6A49
COLOR_RAM_R12C20 = $d9f4 ; x-ref: $365B
COLOR_RAM_R12C30 = $d9fe ; x-ref: $4CBD
COLOR_RAM_R13C0 = $da08 ; x-ref: $6052
COLOR_RAM_R17C0 = $daa8 ; x-ref: $605C
COLOR_RAM_R18C30 = $daee ; x-ref: $365E
COLOR_RAM_R20C5 = $db25 ; x-ref: $62D6
COLOR_RAM_R22C24 = $db88 ; x-ref: $62FE
COLOR_RAM_R24C1 = $dbc1 ; x-ref: $6345
room_data_ptrs_lo = $e000 ; $E000 Reset Code ; x-ref: $4B23
room_data_ptrs_hi = $e001 ; x-ref: $4B28
color_attr_row = $e318 ; x-ref: $4CE9
color_attr_remapped = $e340 ; x-ref: $4CF7
screen_buf_q1 = $e368 ; x-ref: $4C90
screen_buf_q2 = $e412 ; x-ref: $4C9C
screen_buf_q3 = $e4bc ; x-ref: $4CA8
screen_buf_q4 = $e566 ; x-ref: $4CB4
color_buf_q1 = $e610 ; x-ref: $4C96, $4E32, $4E53
color_buf_0x28 = $e638 ; x-ref: $4E68
color_buf_q2 = $e6ba ; x-ref: $4CA2
color_buf_0x100 = $e710 ; x-ref: $4E38
color_buf_mid_clear = $e738 ; x-ref: $4E6B
color_buf_q3 = $e764 ; x-ref: $4CAE
color_buf_0x1b8 = $e7c8 ; x-ref: $4E45
color_buf_q4 = $e80e ; x-ref: $4CBA
color_buf_0x228 = $e838 ; x-ref: $4E71
color_buf_last_row = $e890 ; x-ref: $4E59
KERNAL_IRQ_DEFAULT = $ea31 ; x-ref: $2194, $2199
screen_slot_table = $ef78 ; x-ref: $49F0, $4E1A, $505E
MMU_CONFIG = $ff00 ; C128: MMU Configuration Register ; x-ref: $1CCB, $1CEE, $20E3, $2160, $21A0, ...
KERNAL_SETBNK = $ff68 ; $FF68 (jmp) setbnk ; x-ref: $1C54, $52E0, $5307
KERNAL_READSS = $ffb7 ; $FFB7 (jmp) readss Read I/O Status Word ; x-ref: $20B3
KERNAL_SETLFS = $ffba ; $FFBA (jmp) setlfs Set Logical File Parameters ; x-ref: $20D0
KERNAL_SETNAM = $ffbd ; $FFBD (jmp) setnam Set Filename ; x-ref: $206A, $207B, $208C, $20A3
KERNAL_OPENi = $ffc0 ; $FFC0 (ind) iopen Open Vector [efbd] ; x-ref: $2094, $20AB
KERNAL_CLOSEi = $ffc3 ; $FFC3 (ind) iclose Close Vector [f188] ; x-ref: $2099, $20C0
KERNAL_CHKINi = $ffc6 ; $FFC6 (ind) ichkin Set Input [f106] ; x-ref: $20B0
KERNAL_CLRCHi = $ffcc ; $FFCC (ind) iclrch Restore I/O Vector [f226] ; x-ref: $209C, $20C3
KERNAL_BASINi = $ffcf ; $FFCF (ind) ibasin Input Vector, chrin [ef06] ; x-ref: $20B8
KERNAL_LOADSP = $ffd5 ; $FFD5 (jmp) loadsp Load RAM From Device ; x-ref: $2078
KERNAL_SAVESP = $ffd8 ; $FFD8 (jmp) savesp Save RAM To Device ; x-ref: $2089
; ENUMS
; Enum: GameState
GameState = {
GAMEPLAY: $00,
INTRO: $01,
CURTAIN_ANIMATION: $02,
MAIN_MENU: $03,
GAME_OVER: $04,
CREDITS: $05,
GAME_COMPLETE: $06
}
; Enum: MenuOptions
MenuOptions = {
START: $00,
DYNAMITE: $01,
BEST_HEROES: $02,
PLAY_LEVELS: $03,
CREDITS: $04
}
; Enum: VicIIColors
VicIIColors = {
BLACK: $00,
WHITE: $01,
RED: $02,
CYAN: $03,
PURPLE: $04,
GREEN: $05,
BLUE: $06,
YELLOW: $07,
ORANGE: $08,
BROWN: $09,
LIGHT_RED: $0a,
DARK_GREY: $0b,
GREY: $0c,
LIGHT_GREEN: $0d,
LIGHT_BLUE: $0e,
LIGHT_GREY: $0f
}
$1C01.worda_1C0B; 10 SYS 7181
$1C03.word$000a
$1C05.byte$9e, $37, $31, $38, $31
a_1C0B =*+$01 ; x-ref: $1C01
$1C0A.word$0000; End of BASIC
$1C0C.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main game loop. Runs initialization per frame, waits for the raster beam
; to reach line $FB (bottom of visible screen), then dispatches the current
; game state handler. Loops forever.
;
; Inputs: None
; Outputs: None
; Side Effects: Drives the entire game; calls frame setup, raster sync,
; and game-state dispatch each iteration.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1C0D20 d5 20startjsrinit_game; frame setup (MMU bank, disable CIAs, etc.)
main_loop_raster_wait
$1C10a9 fblda#$fb; target raster line = $FB (251) ; x-ref: $1C1A
$1C12cd 12 d0b_1C12cmp$d012; wait until raster beam reaches line $FB ; x-ref: $1C15 Raster Position
$1C15d0 fbbneb_1C12; not there yet, keep polling
$1C1720 f5 20jsrgame_state_dispatch; game state dispatch (input, logic, rendering)
$1C1A4c 10 1cjmpmain_loop_raster_wait; loop forever
; Table of 20 word-sized entries (lo/hi interleaved). Each pair holds the RAM bank 1 start address where CAVEnn was loaded. Indexed by cave_index * 2.
$1C1Dcave_addr_table.byte$00; x-ref: $1C6C, $1CA8
; High byte counterpart of cave_addr_table (same table, offset +1).
$1C1Ecave_addr_table_hi.byte$00; x-ref: $1C72, $1CAD
; End address lo for cave data. Actually cave_addr_table+2, i.e. the next cave's start address serves as the current cave's end.
$1C1Fcave_end_table.byte$00; x-ref: $1CB2
; End address hi for cave data (cave_addr_table+3). 37 bytes of fill complete the 40-byte table (20 entries x 2 bytes).
$1C20cave_end_table_hi.fill37, $00; x-ref: $1CB7
; Low byte of the final end address after all 20 cave files are loaded into bank 1 RAM.
$1C45cave_data_end_lo.byte$00; x-ref: $1C97
; High byte of the final end address. Followed by the "CAVE" filename template string.
$1C46cave_data_end_hi.byte$00; x-ref: $1C9D
.encode
.enc"none"
$1C47.text"CAVE"
.endencode
; Tens digit ASCII char in the "CAVExx" filename string. Written by convert_byte_to_two_ascii_digits.
$1C4Bcave_filename_tens.byte$58; x-ref: $1D04
; Units digit ASCII char in the "CAVExx" filename string. Written by convert_byte_to_two_ascii_digits.
$1C4Ccave_filename_units.byte$58; x-ref: $1D01
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Loads all 20 cave data files (CAVE01–CAVE20) from disk into RAM bank 1,
; starting at $2000. Records each file's load address in the cave_load_addrs
; table at $1C1D for later lookup. Saves/restores zero page around the
; operation.
;
; Inputs: None
; Outputs: f_1C1D table filled with 20 address entries, a_1C45/a_1C46 = end addr
; Side Effects: Disk I/O; loads 20 files into bank 1 RAM; zero page temporarily modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1C4D20 c4 5bload_all_cave_datajsrrestore_zero_page; Save zero page to buffer ; x-ref: $6693
$1C50a9 01lda#$01; Load from bank 0
$1C52a2 00ldx#$00; Save to bank 1
$1C5420 68 ffjsrKERNAL_SETBNK; Set I/O banks for LOAD; $FF68 (jmp) setbnk
$1C57a9 00lda#<cave_data_load_addr; Load address = $2000 (lo)
$1C598d fb 00sta@w zp_ptr_src_lo
$1C5Ca9 20lda#>cave_data_load_addr; Load address = $2000 (hi)
$1C5E8d fc 00sta@w zp_ptr_src_hi
$1C61a9 00lda#$00; Cave index = 0
$1C6385 ffstazp_temp
$1C65a5 ffb_1C65ldazp_temp; --- Loop: load each cave file --- ; x-ref: $1C92
$1C670aasla; Index * 2 for word table
$1C68aatax
$1C69ad fb 00lda@w zp_ptr_src_lo; Store load addr lo in table
$1C6C9d 1d 1cstacave_addr_table,x
$1C6Fad fc 00lda@w zp_ptr_src_hi; Store load addr hi in table
$1C729d 1e 1cstacave_addr_table_hi,x
$1C75a5 ffldazp_temp; Cave number (0-based)
$1C7718clc
$1C7869 01adc#$01; Convert to 1-based for filename
$1C7A20 f7 1cjsrconvert_byte_to_two_ascii_digits; Convert to 2-digit decimal -> "CAVExx"
$1C7Da9 06lda#$06; Filename length = 6 ("CAVExx")
$1C7Fa2 47ldx#$47; Filename addr = $1C47
$1C81a0 1cldy#$1c
$1C8320 6a 20jsrload_file_to_addr; SETNAM + LOAD file from disk
$1C868e fb 00stx@w zp_ptr_src_lo; Update load addr with end-of-file (lo)
$1C898c fc 00sty@w zp_ptr_src_hi; Update load addr with end-of-file (hi)
$1C8Ce6 ffinczp_temp; Next cave index
$1C8Ea5 ffldazp_temp
$1C90c9 14cmp#$14; All 20 caves loaded?
$1C92d0 d1bneb_1C65
$1C94ad fb 00lda@w zp_ptr_src_lo; Store final end address (lo)
$1C978d 45 1cstacave_data_end_lo
$1C9Aad fc 00lda@w zp_ptr_src_hi; Store final end address (hi)
$1C9D8d 46 1cstacave_data_end_hi
$1CA04c b7 5bjmpsave_zero_page; Restore zero page and return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Copies cave/level data from banked RAM into the working buffer at $1300.
; Uses the current cave index (a_60D9) to look up source address and end
; address from tables at f_1C1D-f_1C20, then performs a byte-by-byte copy
; with C128 bank switching (RAM bank via $D506, MMU config via $FF00).
;
; Inputs: a_60D9 = cave index (0-based)
; Outputs: Cave data copied to $1300+
; Side Effects: Temporarily switches C128 RAM bank configuration
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1CA3ad d9 60load_cave_dataldacurrent_level; A = cave index ; x-ref: $6152
$1CA60aasla; index * 2 for word-sized table entries
$1CA7aatax
$1CA8bd 1d 1cldacave_addr_table,x; src addr lo from table
$1CAB85 fbstazp_ptr_src_lo; store in ZP ptr $FB
$1CADbd 1e 1cldacave_addr_table_hi,x; src addr hi from table
$1CB085 fcstazp_ptr_src_hi; store in ZP ptr $FC
$1CB2bd 1f 1cldacave_end_table,x; end addr lo
$1CB585 02stazp_work0; store in ZP $02
$1CB7bd 20 1cldacave_end_table_hi,x; end addr hi
$1CBA85 03stazp_work1; store in ZP $03
$1CBCa9 00lda#<cave_data_buffer; dest addr lo = $00
$1CBE85 fdstazp_ptr_dst_lo; store in ZP ptr $FD
$1CC0a9 13lda#>cave_data_buffer; dest addr hi = $13 (dest=$1300)
$1CC285 festazp_ptr_dst_hi; store in ZP ptr $FE
$1CC4a9 06lda#$06; select RAM bank for source data
$1CC68d 06 d5staVIC_RAM_CONFIG; Select RAM bank 6 for banked read; C128: RAM Configuration Register (RCR)
$1CC9a9 4elda#$4e; MMU: map banked RAM for reading
$1CCB8d 00 ffstaMMU_CONFIG; C128: MMU Configuration Register
$1CCEa0 00ldy#$00
$1CD0b1 fbb_1CD0lda(zp_ptr_src_lo),y; read byte from banked source ; x-ref: $1CE4, $1CEA
$1CD291 fdsta(zp_ptr_dst_lo),y; write byte to dest buffer
$1CD4e6 fbinczp_ptr_src_lo; advance source ptr lo
$1CD6d0 02bneb_1CDA
$1CD8e6 fcinczp_ptr_src_hi; advance source ptr hi on carry
$1CDAe6 fdb_1CDAinczp_ptr_dst_lo; advance dest ptr lo ; x-ref: $1CD6
$1CDCd0 02bneb_1CE0
$1CDEe6 feinczp_ptr_dst_hi; advance dest ptr hi on carry
$1CE0a5 fcb_1CE0ldazp_ptr_src_hi; check if src hi == end hi ; x-ref: $1CDC
$1CE2c5 03cmpzp_work1
$1CE4d0 eabneb_1CD0; not done yet, keep copying
$1CE6a5 fbldazp_ptr_src_lo; check if src lo == end lo
$1CE8c5 02cmpzp_work0
$1CEAd0 e4bneb_1CD0; not done yet, keep copying
$1CECa9 0elda#$0e; MMU: restore normal config
$1CEE8d 00 ffstaMMU_CONFIG; C128: MMU Configuration Register
$1CF1a9 04lda#$04; restore normal RAM config
$1CF38d 06 d5staVIC_RAM_CONFIG; Restore normal RAM config after bank copy; C128: RAM Configuration Register (RCR)
$1CF660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Converts a byte value (0-99) in A to two ASCII digit characters.
; The tens digit is stored at a_1C4B and the units digit at a_1C4C,
; which are the digit positions in the "CAVEXX" display string.
;
; Inputs: A = value to convert (0-99)
; Outputs: a_1C4B = tens digit ASCII char, a_1C4C = units digit ASCII char
; Side Effects: X is modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
convert_byte_to_two_ascii_digits
$1CF7a2 2fldx#$2f; X = '0'-1 (ASCII $30 - 1) ; x-ref: $1C7A
$1CF938sec; Prepare for repeated subtraction
$1CFAe8b_1CFAinx; Increment tens digit char ; x-ref: $1CFD
$1CFBe9 0asbc#$0a; Subtract 10 from value
$1CFDb0 fbbcsb_1CFA; Loop while value >= 10
$1CFF69 3aadc#$3a; Adjust remainder to ASCII units digit (+$30+$0A)
$1D018d 4c 1cstacave_filename_units; Store units digit character
$1D048e 4b 1cstxcave_filename_tens; Store tens digit character
$1D0760rts
; Transition active flag: $00 = inactive (skip update), nonzero = active, $FF = finished/aborted.
$1D08transition_active.byte$00; x-ref: $1D44, $1E01, $1E11, $802A
; Current pattern byte from the sequence table. Used as joystick override during state 0 transitions.
$1D09transition_pattern.byte$00; x-ref: $1D77, $1DAD, $1DF5
; 16-bit transition countdown timer (low byte). Decremented each frame; when both bytes reach zero, the next sequence step triggers.
$1D0Atransition_timer_lo.byte$00; x-ref: $1D80, $1D88, $1D8B, $1D9D, $1DC4, ...
; 16-bit transition countdown timer (high byte).
$1D0Btransition_timer_hi.byte$00; x-ref: $1D85, $1D8E, $1DA2, $1DC9, $1DDC, ...
; Index into the transition sequence table (transition_seq_table). Advances by 2 for each (pattern, duration) pair.
$1D0Ctransition_seq_idx.byte$00; x-ref: $1DA7, $1DEA, $1DEF
; Duration counter for the current sequence step. Decremented each frame; when zero, advances to next (pattern, duration) pair.
$1D0Dtransition_duration.byte$00; x-ref: $1DB3, $1DE5, $1DFB
; Sequence table of (pattern, duration) byte pairs used during state 3 (cave init) transitions. Indexed by transition_seq_idx.
$1D0Etransition_seq_table.byte$e7; x-ref: $1DAA, $1DF2
; Duration byte counterpart of transition_seq_table (same table, offset +1). Each pair: [pattern, duration].
transition_seq_table_dur
$1D0F.byte$64, $e9, $1e, $e7, $9b, $e9, $1e, $e7; x-ref: $1DB0, $1DF8
$1D17.byte$e6, $eb, $2d, $ef, $0f, $eb, $32, $ff
$1D1F.byte$ff, $eb, $6e, $e5, $1e, $eb, $b4, $e7
$1D27.byte$ff, $e7, $b4, $e9, $1e, $e7, $3c, $ef
$1D2F.byte$19, $e7, $32, $e6, $3c, $e7, $78, $eb
$1D37.byte$4b, $ef, $19, $eb, $9b, $ff, $ff, $e7
$1D3F.byte$c8, $e7, $73, $eb, $ff
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Manages timed transition sequences during game state changes (death, level
; complete, etc.). Uses a 16-bit countdown timer (a_1D0A/a_1D0B) and a
; sequence table at a_1D0E containing (pattern, duration) pairs. Each frame,
; decrements the timer; when it expires, reloads with state-dependent values
; and dispatches to the appropriate visual effect handler.
;
; Inputs: a_1D08 (active flag), a_20D3 (game state), a_3E80 (joystick)
; Outputs: a_1D0A/a_1D0B (timer), a_1D09 (current pattern), a_1D0C (index)
; Side Effects: Dispatches to state-specific effect routines; may reset SID
; voices; advances sequence table pointer
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_transition_sequence
$1D44ad 08 1dldatransition_active; Check if transition is active ; x-ref: $2104
$1D47d0 01bneb_1D4A; Exit if no active transition
$1D4960rts
$1D4A20 89 3fb_1D4Ajsrreset_keyboard_state; Reset sprite/collision flags ; x-ref: $1D47
$1D4Dad d3 20ldagame_state; Check game state for transition dispatch
$1D50c9 03cmp#GameState.MAIN_MENU; State 3 = special transition
$1D52d0 0cbneb_1D60
$1D54ad 80 3eldajoystick_state; Read joystick/input register
$1D5729 1fand#$1f; Check all direction + fire bits
$1D59c9 1fcmp#$1f
$1D5Bf0 23beqb_1D80; All pressed? Skip to timer
$1D5D4c ff 1djmpabort_transition; Abort: mark transition finished
$1D60ad 80 3eb_1D60ldajoystick_state; Read joystick/input register ; x-ref: $1D52
$1D6329 10and#$10; Check fire button (bit 4)
$1D65d0 06bneb_1D6D; Fire pressed: skip ahead
$1D6720 55 1ejsrsilence_all_channels; Reset SID voice state
$1D6A4c 5e 69jmpshow_main_menu; Jump to default handler
$1D6Dad d3 20b_1D6Dldagame_state; Check game state again ; x-ref: $1D65
$1D70c9 00cmp#GameState.GAMEPLAY
$1D72d0 0cbneb_1D80; State 0: advance sequence
$1D7420 e5 1djsradvance_sequence_step
$1D77ad 09 1dldatransition_pattern; Load current pattern from table
$1D7A8d 80 3estajoystick_state; Store as joystick override
$1D7D20 55 1ejsrsilence_all_channels; Reset SID voice state
$1D80ad 0a 1db_1D80ldatransition_timer_lo; Check timer high byte ; x-ref: $1D5B, $1D72
$1D83d0 03bneb_1D88; If nonzero, skip low-byte dec
$1D85ce 0b 1ddectransition_timer_hi; Decrement timer high byte
$1D88ce 0a 1db_1D88dectransition_timer_lo; Decrement timer low byte ; x-ref: $1D83
$1D8Bad 0a 1dldatransition_timer_lo; Load timer low byte
$1D8E0d 0b 1doratransition_timer_hi; OR with high byte
$1D91f0 01beqb_1D94; Timer expired? Handle it
$1D9360rts; Timer still running, return
$1D94ad d3 20b_1D94ldagame_state; Timer expired — dispatch by state ; x-ref: $1D91
$1D97c9 03cmp#GameState.MAIN_MENU; State 3?
$1D99d0 23bneb_1DBE; No, check other states
$1D9Ba9 40lda#$40; Reload timer = $0B40 (2880 frames)
$1D9D8d 0a 1dstatransition_timer_lo
$1DA0a9 0blda#$0b
$1DA28d 0b 1dstatransition_timer_hi
$1DA5a9 00lda#$00; Reset sequence index to 0
$1DA78d 0c 1dstatransition_seq_idx
$1DAAad 0e 1dldatransition_seq_table; Load initial pattern from table
$1DAD8d 09 1dstatransition_pattern
$1DB0ad 0f 1dldatransition_seq_table_dur; Load initial duration from table
$1DB38d 0d 1dstatransition_duration
$1DB6a9 00lda#$00; Clear state variable
$1DB88d d8 60stadifficulty_mode
$1DBB4c ff 60jmpinit_cave_level; Jump to state 3 init handler
$1DBEc9 00b_1DBEcmp#GameState.GAMEPLAY; State 0? ; x-ref: $1D99
$1DC0d0 0fbneb_1DD1
$1DC2a9 e0lda#$e0; Reload timer = $01E0 (480 frames)
$1DC48d 0a 1dstatransition_timer_lo
$1DC7a9 01lda#$01
$1DC98d 0b 1dstatransition_timer_hi
$1DCCa9 fflda#$ff; Load $FF (flag value)
$1DCE4c 3e 6djmpinit_high_score_screen; Jump to state 0 effect handler
$1DD1c9 04b_1DD1cmp#$04; State 4? ; x-ref: $1DC0
$1DD3d0 0dbneb_1DE2
$1DD5a9 2clda#$2c; Reload timer = $012C (300 frames)
$1DD78d 0a 1dstatransition_timer_lo
$1DDAa9 01lda#$01
$1DDC8d 0b 1dstatransition_timer_hi
$1DDF4c c3 5ejmpshow_credits_screen; Jump to state 4 effect handler
$1DE24c 5e 69b_1DE2jmpshow_main_menu; Fallback: jump to default handler ; x-ref: $1DD3
; Advances to the next (pattern, duration) pair in the sequence table
; at a_1D0E, indexed by a_1D0C. Called each frame during state 0.
advance_sequence_step
$1DE5ce 0d 1ddectransition_duration; Decrement duration counter ; x-ref: $1D74
$1DE8d0 14bner_1DFE; Duration not expired, return
$1DEAae 0c 1dldxtransition_seq_idx; Load sequence table index
$1DEDe8inx; Advance by 2 (next pair)
$1DEEe8inx
$1DEF8e 0c 1dstxtransition_seq_idx; Store updated index
$1DF2bd 0e 1dldatransition_seq_table,x; Load next pattern byte
$1DF58d 09 1dstatransition_pattern; Store as current pattern
$1DF8bd 0f 1dldatransition_seq_table_dur,x; Load next duration byte
$1DFB8d 0d 1dstatransition_duration; Store as current duration counter
$1DFE60r_1DFErts; x-ref: $1DE8
; Aborts the current transition by marking it finished and reloading
; the timer with $021C (540 frames).
$1DFFa9 ffabort_transitionlda#$ff; Mark transition as finished ($FF) ; x-ref: $1D5D, $69CF
$1E018d 08 1dstatransition_active; Store finish flag
$1E04a9 1clda#$1c; Reload timer = $021C (540 frames)
$1E068d 0a 1dstatransition_timer_lo
$1E09a9 02lda#$02
$1E0B8d 0b 1dstatransition_timer_hi
$1E0E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables sound playback by clearing the sound-active flag.
; Called during game state transitions (death, level change, menu) to
; silence the SID before entering a new state.
;
; Inputs: None
; Outputs: a_1D08 = $00 (sound disabled)
; Side Effects: Sound playback stops on the next tick (s_1D44 returns early)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E0Fa9 00stop_soundlda#$00; Clear flag: disable sound ; x-ref: $6A7D, $6AA2, $6AAE, $6ABE
$1E118d 08 1dstatransition_active; sound_active_flag = 0 (off)
$1E1460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the SID chip for music playback. Silences all three voices by
; clearing their control registers and pulse widths, sets all frequency
; high-bytes to $FF, and sets the master volume to maximum ($0F) with no
; filter enabled.
;
; Inputs: None
; Outputs: None
; Side Effects: Resets SID registers $D401-$D418; silences all audio, sets
; volume to max.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E15a9 00sid_initlda#$00; Clear value for control/pulse registers ; x-ref: $1E52, $20EF, $60CC
$1E178d 04 d4sta$d404; Silence voice 1 (gate off, no waveform); Voice 1: Control Register
$1E1A8d 0b d4sta$d40b; Silence voice 2; Voice 2: Control Register
$1E1D8d 12 d4sta$d412; Silence voice 3; Voice 3: Control Register
$1E208d 02 d4sta$d402; Reset voice 1 pulse width; Voice 1: Pulse Waveform Width - Low-Byte
$1E238d 09 d4sta$d409; Reset voice 2 pulse width; Voice 2: Pulse Waveform Width - Low-Byte
$1E268d 10 d4sta$d410; Reset voice 3 pulse width; Voice 3: Pulse Waveform Width - Low-Byte
$1E29a9 fflda#$ff; Max frequency high-byte value
$1E2B8d 01 d4sta$d401; Voice 1 freq hi = $FF; Voice 1: Frequency Control - High-Byte
$1E2E8d 08 d4sta$d408; Voice 2 freq hi = $FF; Voice 2: Frequency Control - High-Byte
$1E318d 0f d4sta$d40f; Voice 3 freq hi = $FF; Voice 3: Frequency Control - High-Byte
$1E34a9 0flda#$0f; Max volume, no filter
$1E368d 18 d4sta$d418; Set master volume to 15; Select Filter Mode and Volume
$1E3960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Update all 8 sound effect channels. Each channel has an independent state
; machine that controls SID voice parameters (frequency, waveform, ADSR).
; Channels 1-2 use Voice 3, channel 3 uses Voice 2, channels 4-8 use Voice 1.
; Called once per frame from the main game loop.
;
; Inputs: None (reads SFX state variables a_88EC..a_8BCF)
; Outputs: None
; Side Effects: Writes to SID registers $D400-$D414 (all three voices)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E3A20 ee 88update_all_sfxjsrtick_sfx_hit; Tick SFX channel 1: hit/destroy noise (Voice 3) ; x-ref: $60B6, $64D0
$1E3D20 42 89jsrtick_sfx_explosion; Tick SFX channel 2 (Voice 3)
$1E4020 92 89jsrtick_sfx_fire; Tick SFX channel 3 (Voice 2)
$1E4320 25 8ajsrtick_sfx_ch4_voice1; Tick SFX channel 4 (Voice 1)
$1E4620 9f 8ajsrtick_sfx_ch5_voice1; Tick SFX channel 5 (Voice 1)
$1E4920 f2 8ajsrtick_sfx6_voice1; Tick SFX channel 6 (Voice 1)
$1E4C20 78 8bjsrtick_sfx7_voice1; Tick SFX channel 7 (Voice 1)
$1E4F4c d1 8bjmptick_sfx_ch8; Tick SFX channel 8 (Voice 3), tail call
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Stops all music and sound effects by resetting the SID chip hardware
; registers and clearing all 8 music channel enable flags.
;
; Inputs: None
; Outputs: None
; Side Effects: SID chip voices silenced, all channel flags cleared
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E5220 15 1estop_all_musicjsrsid_init; Reset SID chip registers ; x-ref: $618E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Silences all 8 sound channels by clearing each channel's enable flag.
; Called during state transitions and when fire is released on title screen.
;
; Inputs: None
; Outputs: None
; Side Effects: Clears enable byte for all 8 sound channels
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1E55a9 00silence_all_channelslda#$00; Clear accumulator (disable value) ; x-ref: $1D67, $1D7D
$1E578d ec 88stasfx_hit_state; Silence channel 1
$1E5A8d 40 89stasfx_explosion_state; Silence channel 2
$1E5D8d 88 89stasfx_fire_phase; Silence channel 3
$1E608d 1a 8astasfx_ch4_state; Silence channel 4
$1E638d 9d 8astasfx_ch5_state; Silence channel 5
$1E668d f0 8astasfx6_enable; Silence channel 6
$1E698d 76 8bstasfx7_enable; Silence channel 7
$1E6C8d cf 8bstasfx_ch8_state; Silence channel 8
$1E6F60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Hero collision detection dispatcher.
; Checks if the hero collides with walls, laser beams, enemies, or the raft.
; Skips all checks if hero is invulnerable (a_7B11 != 0).
; Dispatches based on game state (a_7AFA): state 0 = normal play,
; state 4 = dynamite placed (checks dynamite vs enemies instead).
;
; Inputs: a_7AFA (game state), a_7B11 (hero invulnerability flag)
; Outputs: None (dispatches to sub-routines)
; Side Effects: May trigger hero death (j_5056), enemy death (j_75F5/j_8185),
; or raft collision (j_8185)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_hero_collisions
$1E70ad 11 7bldahero_invulnerable; hero invulnerable? skip all collision checks ; x-ref: $61EC
$1E73f0 01beqhero_collision_dispatch
$1E7560rts
hero_collision_dispatch
$1E76ad fa 7aldahero_state; check game state ; x-ref: $1E73
$1E79c9 04cmp#$04
$1E7Bd0 03bneb_1E80; state 4 = dynamite placed, check dynamite collisions
$1E7D4c 0d 20jmpcheck_dynamite_vs_enemies
$1E80ad fa 7ab_1E80ldahero_state; state 0 = normal play ; x-ref: $1E7B
$1E83d0 14bner_1E99
$1E8520 9a 1ejsrcheck_hazard_collision; check hero vs wall/obstacle
$1E8820 d6 1ejsrcheck_creature2_collision; check hero vs laser beam
$1E8B20 bd 1fjsrcheck_player_enemy_collision; check hero vs enemies (laser hitbox)
$1E8E20 21 1fjsrcheck_enemy_collision; check hero vs enemies (normal hitbox)
$1E91ad fa 7aldahero_state
$1E94d0 03bner_1E99
$1E964c 65 1fjmpcheck_raft_collision; check hero vs raft/vehicle
$1E9960r_1E99rts; x-ref: $1E83, $1E94
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Check hero collision with wall/obstacle.
; Compares hero Y position (a_7AFD) against obstacle Y (a_77C5) and hero X
; position (a_7AFB/a_7AFC) against obstacle X (a_77C3/a_77C4).
; If within bounding box, triggers hero death via j_5056.
;
; Inputs: a_77C2 (obstacle active flag), a_3FA4 (hero death flag),
; hero pos (a_7AFB-a_7AFD), obstacle pos (a_77C3-a_77C5)
; Outputs: None
; Side Effects: May trigger hero death (j_5056)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_hazard_collision
$1E9Aad c2 77ldahazard_active; obstacle active? ; x-ref: $1E85
$1E9Df0 36beqr_1ED5; No hazard -> skip
$1E9Fad a4 3fldascreen_slot_status; hero already dead?
$1EA2d0 31bner_1ED5; Death already triggered -> skip
$1EA4ad fd 7aldahero_y_pos; delta_y = hero_y - obstacle_y
$1EA738sec
$1EA8ed c5 77sbchazard_y; delta_y = player_y - hazard_y
$1EABc9 0dcmp#$0d; delta_y >= 13? too far below
$1EAD10 26bplr_1ED5
$1EAFc9 f4cmp#$f4; delta_y < -12? too far above
$1EB130 22bmir_1ED5
$1EB3ad fb 7aldahero_x_lo; delta_x_lo = hero_x_lo - obstacle_x_lo
$1EB638sec
$1EB7ed c3 77sbchazard_x_lo; delta_x lo = player_x_lo - hazard_x_lo
$1EBAa8tay; Y = delta_x lo
$1EBBad fc 7aldahero_x_hi; delta_x_hi = hero_x_hi - obstacle_x_hi
$1EBEed c4 77sbchazard_x_hi; A = delta_x hi = player_x_hi - hazard_x_hi
$1EC1d0 07bneb_1ECA; Hi byte nonzero? Check negative case
$1EC3c0 0acpy#$0a; delta_x_lo < 10? collision!
$1EC5b0 0ebcsr_1ED5
$1EC74c 56 50jmpj_5056; kill hero
$1ECAc9 ffb_1ECAcmp#$ff; Hi byte == $FF? (negative delta) ; x-ref: $1EC1
$1ECCd0 07bner_1ED5
$1ECEc0 f6cpy#$f6; delta_x lo < $F6 (-10)? Too far left
$1ED090 03bccr_1ED5
$1ED24c 56 50jmpj_5056; Collision! Trigger death
$1ED560r_1ED5rts; No collision ; x-ref: $1E9D, $1EA2, $1EAD, $1EB1, $1EC5, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks collision between the player and the secondary creature (a_7827).
; Compares player position (a_7AFB/C/D) against creature position (a_77C3/C4/C5)
; with a vertical proximity range of [5, 11) pixels and a direction-dependent
; horizontal range of ~50 pixels in the player's facing direction.
; On hit, jumps to j_5056 to trigger player death.
;
; Inputs: a_7827 (creature2 active flag), a_77C2 (enemy active),
; a_3FA4 (player death flag), a_7AFB/C (player X 16-bit),
; a_7AFD (player Y), a_77C3/C4 (enemy X 16-bit),
; a_77C5 (enemy Y), a_7B01 (player facing direction)
; Outputs: None (exits early if no collision)
; Side Effects: Sets a_3FA4=$FF and triggers death screen flash via j_5056
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_creature2_collision
$1ED6ad 27 78ldafire_active; creature2 active? ; x-ref: $1E88
$1ED9f0 45beqr_1F20; no creature2, skip
$1EDBad c2 77ldahazard_active; enemy active?
$1EDEf0 40beqr_1F20; no enemy, skip
$1EE0ad a4 3fldascreen_slot_status; already dead?
$1EE3d0 3bbner_1F20; already dead, skip
$1EE5ad fd 7aldahero_y_pos; delta_y = player_y - enemy_y
$1EE838sec
$1EE9ed c5 77sbchazard_y
$1EECc9 0bcmp#$0b; delta_y >= 11? too far below
$1EEE10 30bplr_1F20
$1EF0c9 05cmp#$05; delta_y < 5? too far above
$1EF230 2cbmir_1F20
$1EF4ad fb 7aldahero_x_lo; delta_x lo = player_x_lo - enemy_x_lo
$1EF738sec
$1EF8ed c3 77sbchazard_x_lo
$1EFBa8tay; Y = delta_x lo
$1EFCad fc 7aldahero_x_hi; delta_x hi = player_x_hi - enemy_x_hi
$1EFFed c4 77sbchazard_x_hi
$1F02aatax; X = delta_x hi
$1F03ad 01 7bldahero_scroll_dir; player facing direction
$1F06c9 01cmp#$01; facing right?
$1F08d0 0bbneb_1F15; no, check left-facing range
$1F0Ae0 00cpx#$00; hi byte must be 0 (positive delta)
$1F0Cd0 12bner_1F20; hi != 0, out of range
$1F0Ec0 33cpy#$33; lo < $33 (51 px)? within range
$1F10b0 0ebcsr_1F20; lo >= $33, out of range
$1F124c 56 50jmpj_5056; collision! trigger player death
$1F15e0 ffb_1F15cpx#$ff; hi byte must be $FF (negative delta) ; x-ref: $1F08
$1F17d0 07bner_1F20; hi != $FF, out of range
$1F19c0 cecpy#$ce; lo >= $CE (-50 px)? within range
$1F1B90 03bccr_1F20; lo < $CE, out of range
$1F1D4c 56 50jmpj_5056; collision! trigger player death
$1F2060r_1F20rts; x-ref: $1ED9, $1EDE, $1EE3, $1EEE, $1EF2, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Check hero collision with enemies (normal hitbox).
; Iterates the enemy table (f_7096) from index a_71E0-1 down to 0.
; Skips dead enemies (f_7096,x != 0). Calls s_761B to set hitbox
; dimensions based on enemy type. If hero is within bounding box,
; triggers enemy-hit-hero via j_8185.
;
; Inputs: a_71E0 (enemy count), enemy tables (f_7096, f_70C3, etc.)
; Outputs: None
; Side Effects: May trigger enemy hit via j_8185
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_enemy_collision
$1F21ae e0 71ldxtile_count; enemy_count ; x-ref: $1E8E
$1F24d0 01bneb_1F27; no enemies? return
$1F2660rts
$1F27cab_1F27dex; adjust to 0-based index ; x-ref: $1F24
$1F28bd 96 70b_1F28ldaentity_status,x; enemy dead/inactive? skip ; x-ref: $1F62
$1F2Bd0 34bneb_1F61
$1F2D20 1b 76jsrget_enemy_hitbox; set hitbox params for enemy type
$1F30ad fd 7aldahero_y_pos; delta_y = hero_y - enemy_y
$1F3338sec
$1F34fd f0 70sbcentity_y,x; A = player_Y - enemy_Y
$1F37c5 02cmpzp_work0; delta >= upper Y bound? skip
$1F3910 26bplb_1F61
$1F3Bc5 03cmpzp_work1; delta < lower Y bound? skip
$1F3D30 22bmib_1F61
$1F3Fad fb 7aldahero_x_lo; delta_x_lo = hero_x_lo - enemy_x_lo
$1F4238sec
$1F43fd c3 70sbcentity_x_lo,x; 16-bit: player_X - enemy_X
$1F46a8tay; Y = horiz delta lo byte
$1F47ad fc 7aldahero_x_hi; player X hi
$1F4Afd d2 70sbcentity_x_hi,x; A = horiz delta hi byte
$1F4Dd0 07bneb_1F56; hi=0? positive delta path
$1F4Fc4 04cpyzp_work2; lo >= upper X bound? skip
$1F51b0 0ebcsb_1F61
$1F534c 85 81jmpj_8185; hero hit by enemy!
$1F56c9 ffb_1F56cmp#$ff; hi=$FF? negative delta path ; x-ref: $1F4D
$1F58d0 07bneb_1F61
$1F5Ac4 05cpyzp_work3; lo < lower X bound? skip
$1F5C90 03bccb_1F61
$1F5E4c 85 81jmpj_8185; hero hit by enemy (neg delta)!
$1F61cab_1F61dex; next enemy ; x-ref: $1F2B, $1F39, $1F3D, $1F51, $1F58, ...
$1F6210 c4bplb_1F28; loop until all checked
$1F6460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Check hero collision with raft/vehicle (a_83A2).
; Compares hero position against raft position (a_83A3-a_83A5).
; Uses raft width (a_83A8) to calculate dynamic horizontal hitbox.
; If within range, triggers raft collision via j_8185.
;
; Inputs: a_83A2 (raft active flag), raft pos (a_83A3-a_83A5),
; a_83A8 (raft width), hero pos (a_7AFB-a_7AFD)
; Outputs: None
; Side Effects: May trigger raft collision via j_8185
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$1F65ad a2 83check_raft_collisionldaraft_active; raft/vehicle active? ; x-ref: $1E96
$1F68f0 52beqr_1FBC
$1F6Aad fd 7aldahero_y_pos; delta_y = hero_y - raft_y
$1F6D38sec
$1F6Eed a5 83sbcraft_pixel_x
$1F71c9 21cmp#$21; delta_y >= $21? too far below
$1F7310 47bplr_1FBC
$1F75c9 e2cmp#$e2; delta_y < $E2? too far above
$1F7730 43bmir_1FBC
$1F79ad fb 7aldahero_x_lo; delta_x = hero_x - raft_x
$1F7C38sec
$1F7Ded a3 83sbcraft_pixel_y_lo
$1F80a8tay
$1F81ad fc 7aldahero_x_hi
$1F84ed a4 83sbcraft_pixel_y_hi
$1F87d0 28bneb_1FB1
$1F89ad a8 83ldaraft_anim_frame; left_edge = raft_width * 8
$1F8C0aasla
$1F8D0aasla
$1F8E0aasla
$1F8F85 ffstazp_temp
$1F91c4 ffcpyzp_temp
$1F93b0 03bcsb_1F98
$1F954c 85 81jmpj_8185; raft collision (from left)!
$1F98c0 38b_1F98cpy#$38; x-ref: $1F93
$1F9Ab0 20bcsr_1FBC
$1F9Ca9 04lda#$04
$1F9E38sec
$1F9Fed a8 83sbcraft_anim_frame
$1FA20aasla
$1FA30aasla
$1FA40aasla
$1FA518clc
$1FA669 11adc#$11
$1FA885 ffstazp_temp
$1FAAc4 ffcpyzp_temp
$1FAC90 0ebccr_1FBC
$1FAE4c 85 81jmpj_8185; raft collision (from right)!
$1FB1c9 ffb_1FB1cmp#$ff; x-ref: $1F87
$1FB3d0 07bner_1FBC
$1FB5c0 f9cpy#$f9
$1FB790 03bccr_1FBC
$1FB94c 85 81jmpj_8185; raft collision (negative delta)!
$1FBC60r_1FBCrts; x-ref: $1F68, $1F73, $1F77, $1F9A, $1FAC, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Check hero collision with enemies (laser-active hitbox).
; Similar to check_enemy_collision_normal but uses s_76BD to calculate
; hitbox params based on enemy type and laser state.
; Skips enemies of type 4 and dead enemies. If hero is within bounding
; box, triggers laser-hit-enemy via j_75F5.
;
; Inputs: a_7827 (laser active), a_71E0 (enemy count),
; enemy tables, hero position
; Outputs: None
; Side Effects: May trigger enemy death via j_75F5
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_player_enemy_collision
$1FBDad 27 78ldafire_active; laser active? ; x-ref: $1E8B
$1FC0f0 05beqr_1FC7; No enemies → return
$1FC2ae e0 71ldxtile_count; enemy_count
$1FC5d0 01bneb_1FC8; No enemies → return
$1FC760r_1FC7rts; x-ref: $1FC0
$1FC8cab_1FC8dex; Start from last enemy index ; x-ref: $1FC5
$1FC9bd a5 70b_1FC9ldaentity_type,x; enemy type 4? skip (immune) ; x-ref: $200A
$1FCCc9 04cmp#$04; Type 4 = dead/exploding, skip
$1FCEf0 39beqb_2009
$1FD0bd 96 70ldaentity_status,x; enemy dead? skip
$1FD3d0 34bneb_2009
$1FD520 bd 76jsrget_laser_hitbox_params; set laser hitbox params for enemy type
$1FD8ad fd 7aldahero_y_pos; delta_y = hero_y - enemy_y
$1FDB38sec
$1FDCfd f0 70sbcentity_y,x; Subtract enemy Y position
$1FDFc5 02cmpzp_work0; Y delta >= upper bound? Skip
$1FE110 26bplb_2009
$1FE3c5 03cmpzp_work1; Y delta < lower bound? Skip
$1FE530 22bmib_2009
$1FE7ad fb 7aldahero_x_lo; delta_x = hero_x - enemy_x
$1FEA38sec
$1FEBfd c3 70sbcentity_x_lo,x; Subtract enemy X low
$1FEEa8tay; Y = X delta low byte
$1FEFad fc 7aldahero_x_hi; Player X high byte
$1FF2fd d2 70sbcentity_x_hi,x; Subtract enemy X high → A = X delta high
$1FF5d0 07bneb_1FFE; High byte = 0 → same page
$1FF7c4 04cpyzp_work2; X delta low >= right bound? Skip
$1FF9b0 0ebcsb_2009
$1FFB4c f5 75jmpj_75F5; laser kills enemy!
$1FFEc9 ffb_1FFEcmp#$ff; High byte = $FF → player is left of enemy ; x-ref: $1FF5
; Also used as cave_data_load_addr: initial RAM destination ($2000) for loading
; cave files from disk. After each file loads, this pointer advances past the
; loaded data. See $1C57-$1C5E.
$2000d0 07cave_data_load_addrbneb_2009; High byte ≠ $FF → delta out of range, skip ; x-ref: $1C57, $1C5C
$2002c4 05cpyzp_work3; X delta low < left bound? Skip
$200490 03bccb_2009
$20064c f5 75jmpj_75F5; Laser kills enemy (negative X delta in range)!
$2009cab_2009dex; next enemy ; x-ref: $1FCE, $1FD3, $1FE1, $1FE5, $1FF9, ...
$200A10 bdbplb_1FC9; Loop until all checked
$200C60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Check dynamite explosion vs enemies.
; When game state is 4 (dynamite placed), iterate all enemies and check
; if they are within the blast radius. Uses wider bounding box for
; type-4 enemies ($18/$E9) vs normal ($14/$ED). Marks hit enemies in
; f_71D1,x table.
;
; Inputs: a_71E0 (enemy count), enemy tables, hero position
; Outputs: f_71D1,x (enemy hit flags)
; Side Effects: May mark enemies as hit by dynamite
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_dynamite_vs_enemies
$200Dae e0 71ldxtile_count; enemy_count ; x-ref: $1E7D
$2010d0 01bneb_2013
$201260rts
$2013cab_2013dex; x-ref: $2010
$2014bd 96 70b_2014ldaentity_status,x; enemy dead? skip ; x-ref: $2067
$2017d0 4dbneb_2066
$2019ad fd 7aldahero_y_pos; delta_y = hero_y - enemy_y
$201C38sec
$201Dfd f0 70sbcentity_y,x
$2020c9 18cmp#$18; delta_y >= $18? too far below
$202210 42bplb_2066
$2024c9 e9cmp#$e9
$202630 3ebmib_2066
$2028bd a5 70ldaentity_type,x; enemy type 4? use wider hitbox
$202Bc9 04cmp#$04
$202Df0 0bbeqb_203A
$202Fa9 14lda#$14; normal blast radius: $14 wide
$203185 02stazp_work0
$2033a9 edlda#$ed
$203585 03stazp_work1
$20374c 42 20jmpj_2042
$203Aa9 18b_203Alda#$18; type-4 blast radius: $18 wide ; x-ref: $202D
$203C85 02stazp_work0
$203Ea9 e9lda#$e9
$204085 03stazp_work1
$2042ad fb 7aj_2042ldahero_x_lo; delta_x = hero_x - enemy_x ; x-ref: $2037
$204538sec
$2046fd c3 70sbcentity_x_lo,x
$2049a8tay
$204Aad fc 7aldahero_x_hi
$204Dfd d2 70sbcentity_x_hi,x
$2050d0 07bneb_2059
$2052c4 02cpyzp_work0
$2054b0 10bcsb_2066
$20564c 61 20jmpj_2061
$2059c9 ffb_2059cmp#$ff; x-ref: $2050
$205Bd0 09bneb_2066
$205Dc4 03cpyzp_work1
$205F90 05bccb_2066
$2061a9 ffj_2061lda#$ff; mark enemy as hit by dynamite ; x-ref: $2056
$20639d d1 71staentity_hit_flag,x
$2066cab_2066dex; next enemy ; x-ref: $2017, $2022, $2026, $2054, $205B, ...
$206710 abbplb_2014
$206960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Loads a file from disk into memory at the address stored in $FB/$FC.
; The caller must pre-load A=filename length, X/Y=filename address before calling.
; Uses secondary address 0 (load to specified address, not file header address).
;
; Inputs: A = filename length, X = filename addr lo, Y = filename addr hi,
; $FB/$FC = destination address (lo/hi)
; Outputs: X/Y = end-of-load address (lo/hi), A = error code (0=ok)
; Side Effects: Performs disk I/O; loads file data into RAM
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$206A20 bd ffload_file_to_addrjsrKERNAL_SETNAM; Set filename (A=len, X/Y=name addr) ; x-ref: $1C83, $52F1 $FFBD (jmp) setnam Set Filename
$206Da0 00ldy#$00; Secondary addr 0 = load to X/Y addr
$206F20 c6 20jsrsetup_file_params; Set logical file #1, device, SA=0
$2072a6 fbldxzp_ptr_src_lo; Load addr lo from $FB
$2074a4 fcldyzp_ptr_src_hi; Load addr hi from $FC
$2076a9 00lda#$00; A=0: LOAD (not VERIFY)
$20784c d5 ffjmpKERNAL_LOADSP; Tail-call KERNAL LOAD; $FFD5 (jmp) loadsp Load RAM From Device
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Saves a memory region to a storage device (disk).
; Caller must pre-set the filename in A/X/Y before calling.
; Uses ZP $FB-$FC as start address and ZP $FD-$FE as end address+1.
;
; Inputs: A = filename length, X/Y = pointer to filename string,
; $FB-$FC = start address of memory region,
; $FD-$FE = end address+1 of memory region
; Outputs: Carry set on error, A = KERNAL error code
; Side Effects: Saves memory block to disk via KERNAL SAVE
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
save_memory_to_device
$207B20 bd ffjsrKERNAL_SETNAM; Set filename from caller's A/X/Y ; x-ref: $5320 $FFBD (jmp) setnam Set Filename
$207Ea0 ffldy#$ff; Secondary address $FF = SAVE mode
$208020 c6 20jsrsetup_file_params; Set logical file #1, device (default 8)
$2083a6 fdldxzp_ptr_dst_lo; X = end address low byte
$2085a4 feldyzp_ptr_dst_hi; Y = end address high byte
$2087a9 fblda#$fb; A = ZP ptr to start addr ($FB-$FC)
$20894c d8 ffjmpKERNAL_SAVESP; Tail call: perform the SAVE; $FFD8 (jmp) savesp Save RAM To Device
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sends a command string to the disk drive via the command channel.
; The caller sets A=length, X/Y=pointer to command string before calling
; KERNAL_SETNAM, then this routine opens the command channel (SA#15),
; which implicitly sends the command, and immediately closes it.
;
; Inputs: Filename must be set via KERNAL_SETNAM before entry (A=len, X/Y=ptr)
; Outputs: None
; Side Effects: Sends a command to the disk drive (e.g., scratch, rename)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$208C20 bd ffsend_disk_commandjsrKERNAL_SETNAM; x-ref: $5300 $FFBD (jmp) setnam Set Filename
$208Fa0 0fldy#$0f; SA=15: disk command channel
$209120 c6 20jsrsetup_file_params; Set LF#1, device, SA=15
$209420 c0 ffjsrKERNAL_OPENi; Open command channel (sends the command); $FFC0 (ind) iopen Open Vector [efbd]
$2097a9 01lda#$01; LF#1
$209920 c3 ffjsrKERNAL_CLOSEi; Close the command channel; $FFC3 (ind) iclose Close Vector [f188]
$209C4c cc ffjmpKERNAL_CLRCHi; Restore default I/O and return; $FFCC (ind) iclrch Restore I/O Vector [f226]
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Reads and discards the disk drive error/status channel.
; Opens the command channel (secondary address 15), reads all status
; bytes until the I/O status indicates completion, then closes the channel.
; Commonly called after disk operations to clear any pending drive errors.
;
; Inputs: zpa_BA (current device number)
; Outputs: None (status bytes are read and discarded)
; Side Effects: Opens and closes logical file #1 on the disk drive
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
read_disk_status_channel
$209Fa9 00lda#$00; filename length = 0 (no filename needed for status channel) ; x-ref: $6696
$20A1aatax
$20A2a8tay
$20A320 bd ffjsrKERNAL_SETNAM; set empty filename; $FFBD (jmp) setnam Set Filename
$20A6a0 0fldy#$0f; secondary address 15 = command/status channel
$20A820 c6 20jsrsetup_file_params; set logical file params (file #1, device, SA #15)
$20AB20 c0 ffjsrKERNAL_OPENi; open the command channel; $FFC0 (ind) iopen Open Vector [efbd]
$20AEa2 01ldx#$01; channel #1
$20B020 c6 ffjsrKERNAL_CHKINi; set channel 1 as input source; $FFC6 (ind) ichkin Set Input [f106]
$20B320 b7 ffread_status_loopjsrKERNAL_READSS; check I/O status ; x-ref: $20BB $FFB7 (jmp) readss Read I/O Status Word
$20B6d0 06bnestatus_read_done; non-zero = error or end-of-input, stop reading
$20B820 cf ffjsrKERNAL_BASINi; read and discard one status byte; $FFCF (ind) ibasin Input Vector, chrin [ef06]
$20BB4c b3 20jmpread_status_loop
$20BEa9 01status_read_donelda#$01; logical file #1 ; x-ref: $20B6
$20C020 c3 ffjsrKERNAL_CLOSEi; close the command channel; $FFC3 (ind) iclose Close Vector [f188]
$20C34c cc ffjmpKERNAL_CLRCHi; restore default I/O channels; $FFCC (ind) iclrch Restore I/O Vector [f226]
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up logical file parameters for KERNAL I/O operations.
; Uses logical file #1, reads the current device number from $BA,
; and ensures it is at least 8 (disk drive). The secondary address
; is expected to be pre-loaded in Y by the caller.
;
; Inputs: Y = secondary address (set by caller before JSR)
; Outputs: A = 1 (logical file #), X = device # (>= 8), Y unchanged
; Side Effects: Calls KERNAL SETLFS ($FFBA) to apply file parameters
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$20C6a9 01setup_file_paramslda#$01; Logical file number = 1 ; x-ref: $206F, $2080, $2091, $20A8
$20C8a6 baldxzp_current_device; Current device number (KERNAL $BA)
$20CAe0 08cpx#$08; Is it >= 8 (disk drive)?
$20CCb0 02bcsb_20D0; Yes, keep the device number
$20CEa2 08ldx#$08; No, default to device 8
$20D04c ba ffb_20D0jmpKERNAL_SETLFS; Set logical file params (A=file#, X=dev#, Y=sec addr) ; x-ref: $20CC $FFBA (jmp) setlfs Set Logical File Parameters
; Game state machine variable.
; 0=playing cave, 1=title/intro, 2=high score entry, 3=main menu,
; 4=best heroes, 5=credits, 6=game complete
$20D3game_state.byte$00; x-ref: $1D4D, $1D6D, $1D94, $20FE, $2107, ...
; Snapshot of game_state taken before transition check.
; Used to detect mid-frame state changes and abort the current frame.
$20D4prev_game_state.byte$00; x-ref: $2101, $210A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the game hardware and state. Configures the C128 MMU bank,
; saves the zero-page working area, disables CIA interrupts, installs
; a custom NMI handler, resets the SID sound chip, and then jumps to
; the first game-state setup (state 1).
;
; Inputs: None
; Outputs: None
; Side Effects: MMU bank set to $0E (I/O visible, RAM0); CIA1/CIA2
; interrupts disabled; NMI vector redirected; SID silenced;
; zero-page $03–$FA saved to buffer; game state set to 1.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$20D5a9 ffinit_gamelda#$ff; sentinel: mark game as initializing ; x-ref: $1C0D
$20D785 d8stazp_init_flag; store init flag in zero-page
$20D9ad 04 0aldagame_flags; Read game flags for init
$20DC29 feand#$fe
$20DE8d 04 0astagame_flags; Clear bit 0 of game flags
$20E1a9 0elda#$0e; MMU bank $0E: I/O + KERNAL visible, RAM0
$20E38d 00 ffstaMMU_CONFIG; Set MMU: I/O + KERNAL visible, RAM0; C128: MMU Configuration Register
$20E620 b7 5bjsrsave_zero_page; save zero-page $03-$FA to buffer
$20E920 3d 21jsrdisable_cia_interrupts; disable CIA1 & CIA2 interrupts
$20EC20 ab 5bjsrdisable_nmi; set NMI vector to custom handler
$20EF20 15 1ejsrsid_init; silence all SID voices
$20F24c f3 65jmpinit_intro_screen; enter game state 1 (title/intro setup)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main game-state dispatcher. Called once per frame from the main loop after
; the raster beam reaches line $FB. Reads joystick input, scans the keyboard
; matrix, filters keyboard ghosting, then snapshots the current game state
; (a_20D3) and runs the screen transition sequencer. If the transition changed
; the state, returns immediately (to let the new state take effect next frame).
; Otherwise, dispatches to the handler for the current state:
;
; State 0 → j_6182 (title/intro screen)
; State 1 → j_663B (menu/option select)
; State 2 → j_66DE (game setup / level init)
; State 3 → j_6A6A (gameplay)
; State 4 → j_6E96 (game over / results)
; State 5 → j_5F28 (high-score entry)
; Default → j_60B6 (fallback / SFX update)
;
; Inputs: a_20D3 (current game state)
; Outputs: None (dispatches to appropriate handler)
; Side Effects: Joystick and keyboard state updated; transition sequencer runs
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$20F520 82 3egame_state_dispatchjsrread_joystick; poll joystick port ; x-ref: $1C17
$20F820 18 3fjsrscan_keyboard_matrix; scan keyboard matrix for key states
$20FB20 96 3ejsrfilter_joystick_from_keyboard; filter phantom joystick bits from keyboard ghosting
$20FEad d3 20ldagame_state; Snapshot current game_state
$21018d d4 20staprev_game_state; Save as prev_game_state for comparison
$210420 44 1djsrupdate_transition_sequence; run screen transition sequencer (may change state)
$2107ad d3 20ldagame_state; Re-read game_state (may have changed)
$210Acd d4 20cmpprev_game_state; Compare with pre-transition snapshot
$210Df0 01beqb_2110; if state unchanged, proceed to dispatch
$210F60rts; state changed mid-frame — abort, let new state run next frame
$2110c9 00b_2110cmp#GameState.GAMEPLAY; state == 0? → title/intro screen ; x-ref: $210D
$2112d0 03bneb_2117
$21144c 82 61jmpgame_engine_loop
$2117c9 01b_2117cmp#GameState.INTRO; state == 1? → menu/option select ; x-ref: $2112
$2119d0 03bneb_211E
$211B4c 3b 66jmpwait_for_intro_fire_click
$211Ec9 02b_211Ecmp#GameState.CURTAIN_ANIMATION; state == 2? → game setup / level init ; x-ref: $2119
$2120d0 03bneb_2125
$21224c de 66jmpupdate_title_curtain_animation
$2125c9 03b_2125cmp#GameState.MAIN_MENU; state == 3? → gameplay ; x-ref: $2120
$2127d0 03bneb_212C
$21294c 6a 6ajmpupdate_main_menu_selection
$212Cc9 04b_212Ccmp#GameState.GAME_OVER; state == 4? → game over / results ; x-ref: $2127
$212Ed0 03bneb_2133
$21304c 96 6ejmpupdate_high_score_screen
$2133c9 05b_2133cmp#GameState.CREDITS; state == 5? → high-score entry ; x-ref: $212E
$2135d0 03bneb_213A
$21374c 28 5fjmpwait_for_credits_fire_click
$213A4c b6 60b_213Ajmpupdate_typewriter_or_skip; default: fallback handler (update SFX, check fire) ; x-ref: $2135
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables all CIA1 and CIA2 interrupt sources and acknowledges any pending
; interrupts. Writes $7F to the ICR to clear all enable bits, then reads
; the ICR to clear the interrupt-pending flags.
;
; Inputs: None
; Outputs: A = value of CIA2 ICR (pending flags at time of read)
; Side Effects: All CIA1 and CIA2 interrupts are disabled and acknowledged
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
disable_cia_interrupts
$213Da9 7flda#$7f; $7F = %01111111, clears all CIA interrupt enable bits ; x-ref: $20E9
$213F8d 0d dcsta$dc0d; Disable all CIA1 interrupts (timer A/B, TOD, serial, flag); CIA1: CIA Interrupt Control Register
$21428d 0d ddsta$dd0d; Disable all CIA2 interrupts; CIA2: CIA Interrupt Control Register
$2145ad 0d dclda$dc0d; Acknowledge/clear any pending CIA1 interrupts; CIA1: CIA Interrupt Control Register
$2148ad 0d ddlda$dd0d; Acknowledge/clear any pending CIA2 interrupts; CIA2: CIA Interrupt Control Register
$214B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Configures a raster IRQ on a specified scanline with a custom handler.
; The raster line is passed in A, and the handler address in X (lo) / Y (hi).
; Sets the C128 MMU to $3E (RAM+I/O) and points the hardware IRQ vector
; at $FFFE/$FFFF to the local IRQ stub at $2174, which dispatches through
; the stored handler pointer at $0314/$0315.
;
; Inputs: A = raster line number, X = handler address lo, Y = handler address hi
; Outputs: None
; Side Effects: Configures VIC-II raster IRQ, sets hardware IRQ vector, sets MMU
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$214C78setup_raster_irqsei; Disable interrupts during setup ; x-ref: $6163, $69DB
$214D8d 12 d0sta$d012; Set raster line to trigger IRQ (from A); Raster Position
$21508e 14 03stxIRQ_VECTOR_LO; Store custom IRQ handler lo byte
$21538c 15 03styIRQ_VECTOR_HI; Store custom IRQ handler hi byte
$2156ad 11 d0lda$d011; Read VIC Control Register 1; VIC Control Register 1
$215929 7fand#$7f; Clear bit 7 (raster compare bit 8)
$215B8d 11 d0sta$d011; Restrict to raster lines 0-255; VIC Control Register 1
$215Ea9 3elda#$3e; MMU config $3E: RAM+I/O visible
$21608d 00 ffstaMMU_CONFIG; Set MMU: RAM+I/O visible for IRQ; C128: MMU Configuration Register
$2163a9 74lda#$74; Point HW IRQ vector lo to $2174 (IRQ stub)
$21658d fe ffsta$fffe; IRQ
$2168a9 21lda#$21; Point HW IRQ vector hi to $21 (IRQ stub)
$216A8d ff ffsta$ffff; IRQ
$216Da9 01lda#$01; Enable raster interrupt
$216F8d 1a d0sta$d01a; VIC Interrupt Mask Register (IMR)
$217258cli; Re-enable interrupts
$217360rts
$217448pha
$21758atxa
$217648pha
$217798tya
$217848pha
$21796c 14 03jmp(IRQ_VECTOR_LO); Dispatch through custom IRQ handler vector
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Raster IRQ epilogue / chain dispatcher.
; Sets the next raster line and IRQ handler, acknowledges the interrupt,
; restores registers from the stack, and returns via RTI.
;
; Inputs: A = next raster line, X = lo byte of next IRQ handler, Y = hi byte
; Outputs: None (returns via RTI)
; Side Effects: $D012 (raster line), $0314/$0315 (IRQ vector), $D019 (IRQ ack)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$217C8d 12 d0irq_chain_nextsta$d012; Set next raster trigger line ; x-ref: $563A, $56CC, $575E, $57F0, $5882, ...; Raster Position
$217F8e 14 03stxIRQ_VECTOR_LO; Set next IRQ handler (lo)
$21828c 15 03styIRQ_VECTOR_HI; Set next IRQ handler (hi)
$2185ee 19 d0inc$d019; Acknowledge VIC interrupt; VIC Interrupt Request Register (IRR)
$218868pla; Restore Y
$2189a8tay
$218A68pla; Restore X
$218Baatax
$218C68pla; Restore A
$218D40rti; Return from interrupt
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables raster IRQs and restores the KERNAL default IRQ vector ($EA31).
; Also restores the C128 MMU configuration to make KERNAL ROM and I/O visible.
;
; Inputs: None
; Outputs: None
; Side Effects: Disables VIC interrupts ($D01A), restores IRQ vector to $EA31,
; sets MMU config ($FF00) to $0E (KERNAL+I/O)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$218E78irq_raster_disablesei; Disable interrupts during setup ; x-ref: $3694
$218Fa9 00lda#$00
$21918d 1a d0sta$d01a; Disable all VIC interrupt sources; VIC Interrupt Mask Register (IMR)
$2194a9 31lda#<KERNAL_IRQ_DEFAULT; Restore IRQ vector lo = $31
$21968d 14 03staIRQ_VECTOR_LO; Restore default KERNAL IRQ ($EA31)
$2199a9 ealda#>KERNAL_IRQ_DEFAULT; Restore IRQ vector hi = $EA ($EA31 = KERNAL IRQ)
$219B8d 15 03staIRQ_VECTOR_HI; Restore default KERNAL IRQ ($EA31)
$219Ea9 0elda#$0e; MMU: KERNAL ROM + I/O visible
$21A08d 00 ffstaMMU_CONFIG; Restore MMU: KERNAL+I/O visible; C128: MMU Configuration Register
$21A358cli; Re-enable interrupts
$21A460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Multiplies the accumulator by 5.
; Uses shift-and-add: A*4 + A = A*5.
;
; Inputs: A = value to multiply
; Outputs: A = A * 5 (result undefined if overflow occurs)
; Side Effects: Clobbers zpa_ED (scratch), carry flag undefined
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21A585 edmultiply_by_5stazp_math_temp; Save original A in scratch ; x-ref: $6B71, $7A43
$21A70aasla; A = A * 2
$21A80aasla; A = A * 4
$21A965 edadczp_math_temp; A = A*4 + original = A*5
$21AB60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Multiplies the accumulator by 7 using shift-and-subtract.
; Computes A = A * 8 - A = A * 7. Uses zpa_ED as a temporary.
;
; Inputs: A = value to multiply
; Outputs: A = A * 7 (carry flag undefined)
; Side Effects: zpa_ED is overwritten
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21AC85 edmultiply_by_7stazp_math_temp; Save original A ; x-ref: $8157, $8168
$21AE0aasla
$21AF0aasla
$21B00aasla; A = original * 8
$21B138sec
$21B2e5 edsbczp_math_temp; A = A*8 - A = A*7
$21B460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Multiplies the accumulator by 30 using shift-and-subtract.
; Computes A = A * 32 - A * 2 = A * 30.
; Used to index into 30-byte record tables.
;
; Inputs: A = value to multiply
; Outputs: A = A * 30 (low byte, carry undefined)
; Side Effects: zpa_ED used as temp storage
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21B585 edmultiply_by_30stazp_math_temp; save original value (N) ; x-ref: $6B5B
$21B70aasla; A = N * 2
$21B80aasla; A = N * 4
$21B90aasla; A = N * 8
$21BA0aasla; A = N * 16
$21BB0aasla; A = N * 32
$21BC38sec; prepare for subtraction
$21BDe5 edsbczp_math_temp; A = N*32 - N = N*31
$21BFe5 edsbczp_math_temp; A = N*31 - N = N*30
$21C160rts; return A = N * 30
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Divides the unsigned value in A by 5 using fixed-point
; multiplication by the reciprocal (1/5 ≈ 0.001100110011... binary).
; Uses successive shift-and-add to approximate A * 51/256.
;
; Inputs: A = unsigned dividend (0-255)
; Outputs: A = quotient (A / 5, truncated)
; Side Effects: zpa_ED used as temporary storage
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21C24adivide_by_5lsra; A = input >> 1 ; x-ref: $7C6E
$21C385 edstazp_math_temp; temp = input/2
$21C54alsra; A = input/4
$21C665 edadczp_math_temp; A = input/4 + input/2 = 3*input/4
$21C86arora; A = 3*input/8 (with carry)
$21C94alsra; A = 3*input/16
$21CA4alsra; A = 3*input/32
$21CB65 edadczp_math_temp; A += input/2 (refine approximation)
$21CD6arora; rotate for next bit of 1/5
$21CE65 edadczp_math_temp; A += input/2 (final refinement)
$21D06arora; final shifts to complete A/5
$21D14alsra
$21D24alsra
$21D360rts; return A = input / 5
$21D4.fill171, $00
; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Gameplay Sprites
$227F.byte$05, $01, $41, $00, $00, $11, $00, $00
$2287.byte$11, $00, $01, $51, $00, $01, $41, $00
$228F.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$2297.byte$80, $02, $8a, $80, $02, $a2, $00, $02
$229F.byte$02, $00, $00, $fc, $00, $00, $f0, $00
$22A7.byte$00, $f0, $00, $00, $f0, $00, $00, $f0
$22AF.byte$00, $00, $f0, $00, $00, $f0, $00, $00
$22B7.byte$f0, $00, $00, $f0, $00, $03, $f0, $00
$22BF.byte$06, $01, $41, $00, $00, $11, $00, $00
$22C7.byte$11, $00, $01, $51, $00, $01, $41, $00
$22CF.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$22D7.byte$80, $02, $8a, $80, $02, $a2, $00, $02
$22DF.byte$02, $00, $00, $fc, $00, $00, $3c, $00
$22E7.byte$00, $fc, $00, $00, $fc, $00, $00, $cc
$22EF.byte$00, $00, $cf, $c0, $00, $c0, $c0, $0f
$22F7.byte$00, $c0, $03, $00, $00, $00, $00, $00
$22FF.byte$06, $01, $41, $00, $00, $11, $00, $00
$2307.byte$11, $00, $01, $51, $00, $01, $41, $00
$230F.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$2317.byte$80, $02, $8a, $80, $00, $8a, $00, $02
$231F.byte$0a, $00, $00, $fc, $00, $00, $3c, $00
$2327.byte$00, $fc, $00, $03, $f0, $00, $03, $30
$232F.byte$00, $03, $ff, $00, $00, $33, $00, $00
$2337.byte$33, $00, $00, $30, $00, $00, $f0, $00
$233F.byte$06, $01, $41, $00, $00, $11, $00, $00
$2347.byte$11, $00, $01, $51, $00, $01, $41, $00
$234F.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$2357.byte$80, $02, $8a, $80, $00, $8a, $00, $02
$235F.byte$0a, $00, $00, $fc, $00, $00, $fc, $00
$2367.byte$03, $fc, $00, $03, $cc, $00, $03, $0f
$236F.byte$00, $00, $cf, $00, $00, $c3, $00, $03
$2377.byte$03, $00, $00, $03, $00, $00, $0c, $00
$237F.byte$06, $01, $41, $00, $00, $11, $00, $00
$2387.byte$11, $00, $01, $51, $00, $01, $41, $00
$238F.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$2397.byte$80, $02, $8a, $80, $00, $8a, $00, $02
$239F.byte$0a, $00, $00, $fc, $00, $00, $fc, $00
$23A7.byte$03, $ff, $00, $03, $cf, $00, $03, $03
$23AF.byte$c0, $03, $03, $c0, $0f, $00, $f0, $00
$23B7.byte$00, $30, $00, $00, $30, $00, $00, $00
$23BF.byte$06, $01, $41, $00, $00, $11, $00, $00
$23C7.byte$11, $00, $01, $51, $00, $01, $41, $00
$23CF.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$23D7.byte$80, $02, $8a, $80, $02, $a2, $00, $02
$23DF.byte$02, $00, $00, $fc, $00, $03, $fc, $00
$23E7.byte$03, $3c, $f0, $03, $0f, $c0, $0f, $0f
$23EF.fill16, $00
$23FF.byte$06, $01, $41, $00, $00, $11, $00, $00
$2407.byte$11, $00, $01, $51, $00, $01, $41, $00
$240F.byte$00, $a2, $80, $02, $aa, $80, $02, $8a
$2417.byte$80, $02, $8a, $80, $00, $8a, $00, $02
$241F.byte$0a, $00, $00, $fc, $00, $00, $f0, $00
$2427.byte$00, $f0, $00, $00, $30, $00, $00, $30
$242F.byte$00, $00, $3c, $00, $00, $3c, $00, $00
$2437.byte$0c, $00, $00, $0c, $00, $00, $0c, $00
$243F.byte$06, $00, $41, $40, $00, $44, $00, $00
$2447.byte$44, $00, $00, $45, $40, $00, $41, $40
$244F.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$2457.byte$80, $02, $a2, $80, $00, $8a, $80, $00
$245F.byte$80, $80, $00, $3f, $00, $00, $0f, $00
$2467.byte$00, $0f, $00, $00, $0f, $00, $00, $0f
$246F.byte$00, $00, $0f, $00, $00, $0f, $00, $00
$2477.byte$0f, $00, $00, $0f, $00, $00, $0f, $c0
$247F.byte$06, $00, $41, $40, $00, $44, $00, $00
$2487.byte$44, $00, $00, $45, $40, $00, $41, $40
$248F.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$2497.byte$80, $02, $a2, $80, $00, $8a, $80, $00
$249F.byte$80, $80, $00, $3f, $00, $00, $3c, $00
$24A7.byte$00, $3f, $00, $00, $3f, $00, $00, $33
$24AF.byte$00, $03, $f3, $00, $03, $03, $00, $03
$24B7.byte$00, $f0, $00, $00, $c0, $00, $00, $00
$24BF.byte$06, $00, $41, $40, $00, $44, $00, $00
$24C7.byte$44, $00, $00, $45, $40, $00, $41, $40
$24CF.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$24D7.byte$80, $02, $a2, $80, $00, $a2, $00, $00
$24DF.byte$a0, $80, $00, $3f, $00, $00, $3c, $00
$24E7.byte$00, $3f, $00, $00, $0f, $c0, $00, $0c
$24EF.byte$c0, $00, $ff, $c0, $00, $cc, $00, $00
$24F7.byte$cc, $00, $00, $0c, $00, $00, $0f, $00
$24FF.byte$06, $00, $41, $40, $00, $44, $00, $00
$2507.byte$44, $00, $00, $45, $40, $00, $41, $40
$250F.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$2517.byte$80, $02, $a2, $80, $00, $a2, $00, $00
$251F.byte$a0, $80, $00, $3f, $00, $00, $3f, $00
$2527.byte$00, $3f, $c0, $00, $33, $c0, $00, $f0
$252F.byte$c0, $00, $f3, $00, $00, $c3, $00, $00
$2537.byte$c0, $c0, $00, $c0, $00, $00, $30, $00
$253F.byte$06, $00, $41, $40, $00, $44, $00, $00
$2547.byte$44, $00, $00, $45, $40, $00, $41, $40
$254F.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$2557.byte$80, $02, $a2, $80, $00, $a2, $00, $00
$255F.byte$a0, $80, $00, $3f, $00, $00, $3f, $00
$2567.byte$00, $ff, $c0, $00, $f3, $c0, $03, $c0
$256F.byte$c0, $03, $c0, $c0, $0f, $00, $f0, $0c
$2577.byte$00, $00, $0c, $00, $00, $00, $00, $00
$257F.byte$06, $00, $41, $40, $00, $44, $00, $00
$2587.byte$44, $00, $00, $45, $40, $00, $41, $40
$258F.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$2597.byte$80, $02, $a2, $80, $00, $8a, $80, $00
$259F.byte$80, $80, $00, $3f, $00, $00, $3f, $c0
$25A7.byte$0f, $3c, $c0, $03, $f0, $c0, $00, $f0
$25AF.byte$f0
$25B0.fill15, $00
$25BF.byte$06, $00, $41, $40, $00, $44, $00, $00
$25C7.byte$44, $00, $00, $45, $40, $00, $41, $40
$25CF.byte$02, $8a, $00, $02, $aa, $80, $02, $a2
$25D7.byte$80, $02, $a2, $80, $00, $a2, $00, $00
$25DF.byte$a0, $80, $00, $3f, $00, $00, $0f, $00
$25E7.byte$00, $0f, $00, $00, $0c, $00, $00, $0c
$25EF.byte$00, $00, $3c, $00, $00, $3c, $00, $00
$25F7.byte$30, $00, $00, $30, $00, $00, $30, $00
$25FF.byte$06, $00, $51, $40, $01, $04, $40, $01
$2607.byte$04, $40, $01, $54, $40, $00, $50, $40
$260F.byte$00, $a2, $80, $02, $08, $80, $02, $aa
$2617.byte$00, $08, $a2, $00, $08, $a2, $80, $08
$261F.byte$a8, $80, $0c, $fc, $c0, $00, $fc, $00
$2627.byte$00, $cc, $00, $00, $cc, $00, $00, $cc
$262F.byte$00, $00, $cc, $00, $00, $cc, $00, $00
$2637.byte$cc, $00, $00, $cc, $00, $03, $cf, $00
$263F.byte$06, $00, $02, $00, $00, $02, $00, $00
$2647.byte$02
$2648.fill55, $00
$267F.byte$07, $00, $0a, $80, $00, $02, $00, $00
$2687.byte$02
$2688.fill55, $00
$26BF.byte$07, $00, $2a, $a0, $00, $02, $00, $00
$26C7.byte$02
$26C8.fill55, $00
$26FF.byte$07, $02, $aa, $80, $02, $00, $80, $02
$2707.byte$00, $80, $02, $00, $80, $02, $00, $80
$270F.byte$02, $00, $80, $02, $00, $80, $02, $00
$2717.byte$80, $02, $00, $80, $02, $00, $80, $02
$271F.byte$00, $80, $02, $00, $80, $02, $00, $80
$2727.byte$02, $00, $80, $02, $00, $80, $02, $00
$272F.byte$80, $02, $00, $80, $02, $00, $80, $02
$2737.byte$00, $80, $02, $00, $80, $02, $aa, $80
$273F.byte$01
$2740.fill30, $00
$275E.byte$02, $a2, $88
$2761.fill30, $00
$277F.byte$0a
$2780.fill30, $00
$279E.byte$2a, $28, $88
$27A1.fill30, $00
$27BF.byte$0a
$27C0.fill30, $00
$27DE.byte$28, $aa, $a0
$27E1.fill30, $00
$27FF.byte$0a
$2800.fill30, $00
$281E.byte$02, $aa, $80
$2821.fill30, $00
$283F.byte$0a
$2840.fill31, $00
$285F.byte$20, $00, $00, $a0, $00, $00, $28, $00
$2867.byte$00, $20, $00, $00, $20, $00, $00, $54
$286F.byte$00, $00, $54, $00, $00, $54, $00, $00
$2877.byte$54, $00, $00, $54, $00, $00, $54, $00
$287F.byte$07
$2880.fill31, $00
$289F.byte$80, $00, $00, $08, $00, $00, $28, $00
$28A7.byte$00, $20, $00, $00, $a8, $00, $00, $54
$28AF.byte$00, $00, $54, $00, $00, $54, $00, $00
$28B7.byte$54, $00, $00, $54, $00, $00, $54, $00
$28BF.byte$07
$28C0.fill31, $00
$28DF.byte$08, $00, $00, $80, $00, $00, $a0, $00
$28E7.byte$00, $20, $00, $00, $20, $00, $00, $54
$28EF.byte$00, $00, $54, $00, $00, $54, $00, $00
$28F7.byte$54, $00, $00, $54, $00, $00, $54, $00
$28FF.byte$07
$2900.fill37, $00
$2925.byte$28, $00, $00, $aa, $00, $00, $aa, $00
$292D.byte$00, $28, $00, $00, $aa, $00, $00, $aa
$2935.byte$00, $00, $28, $00, $00, $00, $00, $00
$293D.byte$00, $00, $07
$2940.fill30, $00
$295E.byte$02, $20, $80, $00, $28, $00, $00, $aa
$2966.byte$00, $00, $aa, $00, $08, $2a, $20, $00
$296E.byte$a8, $00, $02, $aa, $00, $00, $aa, $80
$2976.byte$00, $28, $00, $00, $2a, $00, $02, $02
$297E.byte$20, $07
$2980.fill39, $00
$29A7.byte$0a, $8a, $80, $00, $88, $00, $00, $8a
$29AF.byte$80, $00, $80, $80, $00, $8a, $80
$29B6.fill9, $00
$29BF.byte$07, $00, $00, $00, $00, $30, $00, $00
$29C7.byte$30, $00, $00, $30, $00, $00, $30, $00
$29CF.byte$00, $30, $00, $00, $30, $00, $00, $20
$29D7.byte$00, $02, $22, $00, $08, $a8, $80, $08
$29DF.byte$a8, $80, $08, $20, $80, $08, $00, $80
$29E7.fill24, $00
$29FF.byte$07, $00, $00, $00, $00, $30, $00, $00
$2A07.byte$30, $00, $00, $30, $00, $00, $30, $00
$2A0F.byte$00, $30, $00, $00, $30, $00, $02, $22
$2A17.byte$00, $08, $a8, $80, $08, $a8, $80, $08
$2A1F.byte$20, $80, $08, $00, $80
$2A24.fill27, $00
$2A3F.byte$07
$2A40.fill18, $00
$2A52.byte$08, $00, $80, $08, $88, $80, $0a, $22
$2A5A.byte$80, $0a, $8a, $80, $0a, $aa, $80, $02
$2A62.byte$aa, $00, $00, $20
$2A66.fill25, $00
$2A7F.byte$08
$2A80.fill22, $00
$2A96.byte$88, $00, $00, $20, $00, $00, $a8, $00
$2A9E.byte$02, $aa, $00, $0a, $22, $80, $0a, $02
$2AA6.byte$80, $08, $00, $80, $02, $02
$2AAC.fill19, $00
$2ABF.byte$08
$2AC0.fill16, $00
$2AD0.byte$30, $00, $00, $fc, $00, $00, $cc, $00
$2AD8.byte$00, $44, $00, $00, $54, $00, $01, $55
$2AE0.byte$00, $00, $54, $00, $00, $44, $00, $00
$2AE8.byte$cc, $00, $00, $fc, $00, $00, $30
$2AEF.fill16, $00
$2AFF.byte$01
$2B00.fill16, $00
$2B10.byte$cc, $00, $00, $cc, $00, $00, $cc, $00
$2B18.byte$00, $44, $00, $00, $54, $00, $01, $55
$2B20.byte$00, $00, $54, $00, $00, $44, $00, $00
$2B28.byte$cc, $00, $00, $cc, $00, $00, $cc
$2B2F.fill16, $00
$2B3F.byte$01
$2B40.fill15, $00
$2B4F.byte$0c, $00, $c0, $0f, $03, $c0, $03, $cf
$2B57.byte$00, $00, $44, $00, $00, $54, $00, $01
$2B5F.byte$55, $00, $00, $54, $00, $00, $44, $00
$2B67.byte$03, $cf, $00, $0f, $03, $c0, $0c, $00
$2B6F.byte$c0
$2B70.fill15, $00
$2B7F.byte$01
$2B80.fill15, $00
$2B8F.byte$03, $03, $00, $03, $cf, $00, $00, $cc
$2B97.byte$00, $00, $44, $00, $00, $54, $00, $01
$2B9F.byte$55, $00, $00, $54, $00, $00, $44, $00
$2BA7.byte$00, $cc, $00, $03, $cf, $00, $03, $03
$2BAF.fill16, $00
$2BBF.byte$01
$2BC0.fill21, $00
$2BD5.byte$28, $00, $00, $22, $00, $00, $2a, $00
$2BDD.byte$00, $a2, $00, $00, $2a, $00, $00, $08
$2BE5.fill26, $00
$2BFF.byte$05
$2C00.fill21, $00
$2C15.byte$02, $80, $00, $0a, $20, $00, $2a, $a0
$2C1D.byte$00, $a8, $20, $00, $2a, $a0, $00, $00
$2C25.byte$80
$2C26.fill25, $00
$2C3F.byte$05
$2C40.fill22, $00
$2C56.byte$28, $00, $00, $a2, $00, $2a, $aa, $00
$2C5E.byte$aa, $a2, $00, $2a, $aa, $00, $00, $08
$2C66.fill25, $00
$2C7F.byte$05
$2C80.fill22, $00
$2C96.byte$02, $80, $00, $0a, $20, $2a, $aa, $a0
$2C9E.byte$aa, $a8, $20, $2a, $aa, $00, $00, $02
$2CA6.byte$a0, $00, $00, $80
$2CAA.fill21, $00
$2CBF.byte$05
$2CC0.fill23, $00
$2CD7.byte$28, $00, $00, $a2, $2a, $aa, $aa, $aa
$2CDF.byte$aa, $a2, $2a, $aa, $aa, $00, $00, $02
$2CE7.fill24, $00
$2CFF.byte$05
$2D00.fill37, $00
$2D25.byte$a0, $00, $02, $88, $00, $0a, $02
$2D2C.fill19, $00
$2D3F.byte$0e
$2D40.fill31, $00
$2D5F.byte$a8, $00, $02, $8a, $00, $0a, $02, $80
$2D67.byte$08, $00, $80, $0a, $02
$2D6C.fill19, $00
$2D7F.byte$0e
$2D80.fill19, $00
$2D93.byte$a8, $00, $02, $8a, $00, $02, $02, $00
$2D9B.byte$0a, $02, $80, $08, $00, $80, $08, $22
$2DA3.byte$80, $0a, $0a, $00, $02, $00, $00, $00
$2DAB.byte$8a
$2DAC.fill19, $00
$2DBF.byte$0e
$2DC0.fill19, $00
$2DD3.byte$a8, $00, $02, $8a, $00, $0a, $02, $80
$2DDB.byte$08, $20, $80, $08, $82, $80, $08, $82
$2DE3.byte$00, $08, $28, $00, $0a, $00, $00, $02
$2DEB.byte$8a
$2DEC.fill19, $00
$2DFF.byte$0e
$2E00.fill13, $00
$2E0D.byte$a8, $00, $02, $02, $00, $0a, $20, $80
$2E15.byte$08, $88, $80, $08, $80, $80, $08, $82
$2E1D.byte$00, $08, $28, $00, $0a, $00, $00, $02
$2E25.byte$0a, $00, $02, $0a, $00, $02, $2a, $80
$2E2D.fill18, $00
$2E3F.byte$0e
$2E40.fill24, $00
$2E58.byte$0a, $8a, $80, $08, $08, $80, $0a, $88
$2E60.byte$80, $00, $88, $80, $0a, $8a, $80
$2E67.fill24, $00
$2E7F.byte$01
$2E80.fill19, $00
$2E93.byte$2a, $80, $00, $20, $80, $00, $a8, $80
$2E9B.byte$02, $aa, $80, $00, $fc, $c0, $02, $aa
$2EA3.byte$80, $00, $fc, $c0, $00, $30, $c0
$2EAA.fill21, $00
$2EBF.byte$0b
$2EC0.fill57, $00
$2EF9.byte$2a, $aa, $a8, $2a, $aa, $a8, $07
$2F00.fill22, $00
$2F16.byte$80, $00, $03, $f0, $00, $02, $80, $00
$2F1E.byte$02, $80, $00, $02, $00, $00, $05, $40
$2F26.byte$00, $04, $40, $00, $04, $40, $00, $04
$2F2E.byte$40, $00, $04, $40, $00, $0c, $f3, $00
$2F36.byte$0f, $0f, $c0, $0f, $fc, $c0, $03, $f0
$2F3E.byte$f0, $08
$2F40.fill22, $00
$2F56.byte$80, $00, $03, $f0, $00, $02, $80, $00
$2F5E.byte$02, $82, $00, $02, $02, $00, $05, $41
$2F66.byte$00, $04, $15, $00, $04, $40, $00, $05
$2F6E.byte$40, $00, $05, $40, $00, $0f, $ff, $00
$2F76.byte$0f, $ff, $c0, $0f, $fc, $c0, $03, $f0
$2F7E.byte$f0, $08
$2F80.fill22, $00
$2F96.byte$02, $00, $00, $0f, $c0, $00, $02, $80
$2F9E.byte$00, $02, $80, $00, $00, $80, $00, $01
$2FA6.byte$50, $00, $01, $10, $00, $01, $10, $00
$2FAE.byte$01, $10, $00, $01, $10, $00, $cf, $30
$2FB6.byte$03, $f0, $f0, $03, $3f, $f0, $0f, $0f
$2FBE.byte$c0, $08
$2FC0.fill22, $00
$2FD6.byte$02, $00, $00, $0f, $c0, $00, $02, $80
$2FDE.byte$00, $82, $80, $00, $80, $80, $00, $41
$2FE6.byte$50, $00, $54, $10, $00, $01, $10, $00
$2FEE.byte$01, $50, $00, $01, $50, $00, $ff, $f0
$2FF6.byte$03, $ff, $f0, $03, $3f, $f0, $0f, $0f
$2FFE.byte$c0, $08
; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Charset
$3000.byte$00, $00, $3c, $3c, $3c, $3c, $00, $00
$3008.byte$3c, $66, $66, $7e, $66, $66, $66, $00
$3010.byte$7c, $66, $66, $7c, $66, $66, $7c, $00
$3018.byte$3c, $62, $60, $60, $60, $62, $3c, $00
$3020.byte$7c, $66, $66, $66, $66, $66, $7c, $00
$3028.byte$7e, $60, $60, $7c, $60, $60, $7e, $00
$3030.byte$7e, $60, $60, $7c, $60, $60, $60, $00
$3038.byte$3c, $62, $60, $6e, $66, $66, $3c, $00
$3040.byte$66, $66, $66, $7e, $66, $66, $66, $00
$3048.byte$3c, $18, $18, $18, $18, $18, $3c, $00
$3050.byte$0e, $06, $06, $06, $66, $66, $3c, $00
$3058.byte$66, $6c, $78, $70, $78, $6c, $66, $00
$3060.byte$60, $60, $60, $60, $60, $60, $7e, $00
$3068.byte$42, $66, $7e, $66, $66, $66, $66, $00
$3070.byte$66, $76, $7e, $6e, $66, $66, $66, $00
$3078.byte$3c, $66, $66, $66, $66, $66, $3c, $00
$3080.byte$7c, $66, $66, $7c, $60, $60, $60, $00
$3088.byte$3c, $66, $66, $66, $6e, $66, $3d, $00
$3090.byte$7c, $66, $66, $7c, $66, $66, $66, $00
$3098.byte$3c, $62, $60, $3c, $06, $46, $3c, $00
$30A0.byte$7e, $18, $18, $18, $18, $18, $18, $00
$30A8.byte$66, $66, $66, $66, $66, $66, $3c, $00
$30B0.byte$66, $66, $66, $66, $66, $3c, $18, $00
$30B8.byte$66, $66, $66, $66, $7e, $66, $42, $00
$30C0.byte$66, $66, $3c, $18, $3c, $66, $66, $00
$30C8.byte$66, $66, $66, $3c, $18, $18, $18, $00
$30D0.byte$7e, $06, $0c, $18, $30, $60, $7e, $00
$30D8.byte$00, $00, $00, $00, $00, $04, $02, $01
$30E0.byte$00, $00, $00, $02, $06, $42, $82, $27
$30E8.byte$00, $00, $00, $1c, $02, $0c, $10, $5e
$30F0.byte$00, $00, $00, $00, $00, $00, $00, $ff
$30F8.fill16, $00
$3108.byte$18, $18, $18, $18, $00, $18, $18, $00
$3110.byte$66, $66, $cc, $00, $00, $00, $00, $00
$3118.byte$3c, $42, $99, $a1, $a1, $99, $42, $3c
$3120.byte$1c, $22, $49, $55, $55, $4e, $20, $1c
$3128.fill16, $00
$3138.byte$18, $18, $30, $00, $00, $00, $00, $00
$3140.byte$18, $30, $30, $30, $30, $30, $18, $00
$3148.byte$18, $0c, $0c, $0c, $0c, $0c, $18
$314F.fill10, $00
$3159.byte$18, $18, $7e, $18, $18, $00, $00, $00
$3161.byte$00, $00, $00, $00, $18, $18, $30, $00
$3169.byte$00, $00, $7c
$316C.fill9, $00
$3175.byte$18, $18
$3177.fill9, $00
$3180.byte$3c, $66, $66, $66, $66, $66, $3c, $00
$3188.byte$18, $38, $18, $18, $18, $18, $3c, $00
$3190.byte$3c, $46, $06, $0c, $18, $30, $7e, $00
$3198.byte$7e, $04, $08, $1c, $06, $46, $3c, $00
$31A0.byte$0c, $1c, $2c, $4c, $7e, $0c, $0c, $00
$31A8.byte$7e, $60, $7c, $06, $06, $46, $3c, $00
$31B0.byte$3c, $62, $60, $7c, $66, $66, $3c, $00
$31B8.byte$7e, $06, $0c, $18, $30, $30, $30, $00
$31C0.byte$3c, $66, $66, $3c, $66, $66, $3c, $00
$31C8.byte$3c, $66, $66, $3e, $06, $46, $3c, $00
$31D0.byte$00, $00, $18, $18, $00, $18, $18
$31D7.fill41, $00
; Double width charset: 0-9
$3200.byte$0f, $3c, $3c, $3c, $3c, $3c, $3c, $0f
$3208.byte$03, $0f, $03, $03, $03, $03, $03, $0f
$3210.byte$0f, $0c, $00, $00, $0f, $3c, $3c, $3f
$3218.byte$0f, $0c, $00, $00, $00, $00, $0c, $0f
$3220.byte$00, $03, $0c, $30, $3f, $00, $00, $00
$3228.byte$3f, $3c, $3c, $3f, $00, $00, $30, $3f
$3230.byte$0f, $3c, $3c, $3f, $3c, $3c, $3c, $0f
$3238.byte$3f, $30, $00, $00, $03, $03, $03, $03
$3240.byte$0f, $3c, $3c, $0f, $0f, $3c, $3c, $0f
$3248.byte$0f, $3c, $3c, $3c, $0f, $00, $30, $0f
$3250.byte$f0, $3c, $3c, $3c, $3c, $3c, $3c, $f0
$3258.byte$c0, $c0, $c0, $c0, $c0, $c0, $c0, $f0
$3260.byte$f0, $3c, $3c, $3c, $f0, $00, $00, $fc
$3268.byte$f0, $3c, $3c, $f0, $f0, $3c, $3c, $f0
$3270.byte$f0, $f0, $f0, $f0, $fc, $f0, $f0, $f0
$3278.byte$fc, $00, $00, $f0, $3c, $3c, $3c, $f0
$3280.byte$f0, $0c, $00, $f0, $3c, $3c, $3c, $f0
$3288.byte$fc, $0c, $3c, $f0, $c0, $c0, $c0, $c0
$3290.byte$f0, $3c, $3c, $f0, $f0, $3c, $3c, $f0
$3298.byte$f0, $3c, $3c, $3c, $f0, $3c, $3c, $f0
; End of double-width charset
; Being of "power..." charset
$32A0.byte$0f, $0c, $0f, $0c, $0c, $00, $00, $00
$32A8.byte$cf, $cc, $cc, $0c, $0f, $00, $00, $00
$32B0.byte$cc, $cc, $cf, $cf, $cc, $00, $00, $00
$32B8.byte$0c, $cc, $fc, $3c, $0c, $00, $00, $00
$32C0.byte$fc, $c0, $f0, $c0, $f0, $00, $00, $00
$32C8.byte$fc, $cc, $f0, $cc, $cc, $00, $00, $00
$32D0.byte$55, $55, $55, $55, $55, $00, $00, $00
$32D8.byte$d5, $d5, $d5, $d5, $d5, $00, $00, $00
$32E0.byte$f5, $f5, $f5, $f5, $f5, $00, $00, $00
$32E8.byte$fd, $fd, $fd, $fd, $fd, $00, $00, $00
$32F0.byte$ff, $ff, $ff, $ff, $ff, $00, $00, $00
$32F8.byte$03, $00, $00, $00, $00, $00, $00, $00
$3300.byte$f0, $c0, $45, $45, $44, $a2, $22, $22
$3308.byte$0c, $0c, $3c, $30, $30, $00, $00, $00
$3310.byte$00, $0c, $3c, $0f, $0c, $0c, $15, $15
$3318.byte$15, $15, $15, $15, $00, $00, $00, $00
; "Level:" charset
$3320.fill8, $03
$3328.byte$c0, $c0, $c0, $c0, $c0, $c0, $fc, $fc
$3330.byte$ff, $ff, $f0, $fc, $fc, $f0, $ff, $ff
$3338.byte$3c, $3c, $3c, $3c, $3c, $0f, $0f, $03
$3340.byte$f3, $f3, $f3, $f3, $f3, $c3, $c3, $03
$3348.byte$fc, $fc, $c0, $f0, $f0, $c0, $fc, $fc
$3350.byte$f0, $f0, $f0, $f0, $f0, $f0, $ff, $ff
$3358.byte$3c, $3c, $3c, $00, $00, $3c, $3c, $3c
; LC Games charset
$3360.byte$df, $df, $d8, $df, $df, $c0, $ff, $ff
$3368.byte$3c, $7d, $61, $6d, $6d, $6d, $7d, $3d
$3370.byte$e6, $f7, $b7, $f7, $f6, $b6, $b6, $b6
$3378.byte$37, $77, $f6, $f7, $b7, $36, $37, $37
$3380.byte$cf, $df, $18, $9e, $8f, $03, $df, $de
; HERO double-width charset
$3388.byte$30, $fc, $fc, $ff, $ff, $fc, $fc, $30
$3390.byte$30, $fc, $fc, $fc, $fc, $fc, $fc, $33
$3398.byte$3f, $ff, $fc, $ff, $ff, $fc, $ff, $3f
$33A0.byte$f0, $fc, $00, $c0, $c0, $00, $fc, $f3
$33A8.byte$3f, $ff, $fc, $fc, $ff, $ff, $fc, $30
$33B0.byte$c0, $f0, $fc, $fc, $f0, $c0, $f0, $3c
$33B8.byte$0f, $3f, $fc, $fc, $fc, $fc, $3f, $cf
$33C0.byte$c0, $f0, $fc, $fc, $fc, $fc, $f0, $c3
; Logo charset
$33C8.byte$7c, $fe, $fe, $fe, $fa, $fe, $fa, $fa
$33D0.byte$07, $0f, $0f, $0f, $0f, $0f, $0f, $0f
$33D8.byte$c0, $e0, $e0, $e0, $a0, $e0, $a0, $a0
$33E0.byte$7f, $ff, $ff, $ff, $ff, $7f, $3f, $1f
$33E8.byte$ff, $ff, $ff, $ff, $ff, $80, $df, $e8
$33F0.byte$fc, $fe, $fe, $fe, $fa, $06, $fc, $00
$33F8.byte$7f, $ff, $ff, $ff, $ff, $7f, $7f, $3f
$3400.byte$ff, $ff, $ff, $ff, $ff, $00, $7f, $a0
$3408.byte$f0, $fc, $fe, $fb, $fd, $3e, $de, $3f
$3410.byte$00, $00, $00, $00, $80, $80, $c0, $40
$3418.byte$01, $0f, $3f, $7f, $ff, $fe, $f9, $f7
$3420.byte$ff, $ff, $ff, $ff, $ff, $00, $ff, $01
$3428.byte$00, $e0, $f8, $fc, $ff, $ff, $3f, $df
$3430.byte$00, $00, $00, $00, $00, $80, $c0, $e0
$3438.byte$fa, $fa, $fa, $fa, $fa, $fa, $e0, $df
$3440.byte$00, $00, $00, $00, $00, $00, $00, $ff
$3448.byte$0f, $0f, $0f, $0f, $0f, $0f, $00, $ff
$3450.byte$a0, $a0, $a0, $a0, $a0, $a0, $00, $f0
$3458.byte$0f, $07, $03, $01, $00, $00, $00, $7f
$3460.byte$f4, $fa, $fd, $fe, $ff, $7f, $3f, $ff
$3468.byte$00, $00, $00, $80, $40, $a0, $d0, $e8
$3470.byte$3f, $1f, $0f, $0f, $07, $07, $00, $7f
$3478.byte$a0, $d0, $d0, $e8, $f4, $f4, $00, $ff
$3480.byte$1f, $1f, $1f, $1f, $1f, $1f, $3f, $fe
$3488.byte$40, $40, $40, $40, $40, $40, $40, $c0
$3490.byte$0f, $1f, $3f, $3f, $7e, $7d, $7d, $fb
$3498.byte$4c, $b8, $40, $40, $c0, $80, $00, $00
$34A0.byte$7f, $1f, $0f, $07, $03, $03, $01, $01
$34A8.byte$e0, $f0, $d8, $e8, $ec, $f4, $f4, $f6
$34B0.byte$bf, $bf, $bf, $bf, $df, $ef, $f7, $fb
$34B8.byte$ff, $ff, $ff, $ff, $e0, $f7, $fa, $fd
$34C0.byte$ff, $ff, $ff, $ff, $00, $ff, $00, $0f
$34C8.byte$f8, $f8, $f8, $e8, $18, $f0, $00, $a0
$34D0.byte$ff, $ff, $ff, $ff, $fc, $fb, $fa, $fa
$34D8.byte$ff, $ff, $ff, $ff, $00, $ff, $00, $00
$34E0.byte$f4, $fa, $fa, $fa, $06, $fc, $00, $00
$34E8.byte$ff, $ff, $ff, $ff, $00, $ff, $00, $1f
$34F0.byte$fe, $fd, $fb, $e6, $1c, $f0, $00, $d0
$34F8.byte$80, $80, $00, $00, $00, $00, $00, $00
$3500.byte$fa, $fa, $fa, $fa, $fa, $fa, $fa, $fb
$3508.byte$00, $00, $00, $00, $00, $00, $00, $01
$3510.byte$fa, $fa, $fa, $fa, $fa, $fa, $fa, $f6
$3518.byte$f9, $fa, $fa, $fa, $fa, $fa, $fa, $fa
$3520.byte$fe, $ff, $7f, $3f, $1f, $0f, $07, $03
$3528.byte$8f, $4f, $af, $df, $ef, $f7, $fb, $ff
$3530.fill8, $a0
$3538.fill8, $fa
$3540.byte$0f, $0f, $07, $07, $03, $01, $01, $00
$3548.byte$d0, $e8, $f4, $f4, $fa, $fa, $fd, $fd
$3550.byte$00, $00, $00, $00, $00, $00, $00, $80
$3558.byte$7d, $7d, $7e, $3f, $3f, $1f, $0f, $0f
$3560.byte$00, $80, $80, $c0, $e0, $f0, $fc, $ff
$3568.byte$01, $03, $03, $07, $0f, $1f, $7f, $ff
$3570.byte$f4, $f4, $ec, $e8, $d8, $b0, $a0, $60
$3578.byte$fa, $fa, $fa, $fa, $fa, $f6, $7c, $00
$3580.byte$01, $00, $00, $00, $00, $00, $00, $00
$3588.byte$ff, $ff, $7f, $3f, $1f, $0f, $07, $00
$3590.byte$a0, $a0, $a0, $a0, $a0, $60, $c0, $00
$3598.byte$fb, $ff, $ff, $ff, $ff, $c0, $7f, $00
$35A0.byte$ff, $ff, $ff, $ff, $ff, $00, $ff, $00
$35A8.byte$c0, $e0, $e0, $e0, $a0, $60, $c0, $00
$35B0.byte$fe, $7f, $3f, $3f, $1f, $1f, $0f, $00
$35B8.byte$80, $40, $40, $a0, $a0, $60, $c0, $00
$35C0.byte$07, $03, $01, $00, $00, $00, $00, $00
$35C8.byte$ff, $ff, $df, $67, $38, $0f, $01, $00
$35D0.byte$ff, $ff, $ff, $ff, $fe, $01, $ff, $00
$35D8.byte$fe, $f9, $f7, $cc, $38, $e0, $00, $00
$35E0.byte$c0, $80, $00, $00, $00, $00, $00, $00
$35E8.byte$38, $7c, $fe, $fe, $fa, $74, $38, $00
; is back --- charset
$35F0.fill8, $06
$35F8.byte$7e, $fe, $c0, $fc, $7e, $06, $fe, $fc
$3600.byte$fc, $fe, $c6, $fc, $fe, $c6, $fe, $fc
$3608.byte$38, $38, $7c, $6c, $7c, $fe, $c6, $c6
$3610.byte$7c, $fe, $c6, $c0, $c0, $c6, $fe, $7c
$3618.byte$c6, $ce, $dc, $f8, $f8, $dc, $ce, $c6
$3620.byte$00, $00, $00, $ff, $00, $00, $00, $00
$3628.byte$00, $00, $00, $f0, $00, $00, $00, $00
; Temporary buffer for BCD-to-ASCII digit conversion.
; Holds individual ASCII digit characters (units at [0], tens at [1], etc.).
; Also used as an indexed array: bcd_digit_buffer,x
$3630bcd_digit_buffer.byte$00; x-ref: $3702, $371A, $372A, $373F, $374E, ...
$3631bcd_digit_tens.byte$00; Tens digit of BCD conversion result (ASCII) ; x-ref: $3705, $6429
$3632bcd_digit_hundreds.byte$00, $00, $00, $00, $00, $00; Hundreds digit of BCD conversion result (ASCII) ; x-ref: $3708, $642C
; Leading-zero suppression disable flag.
; When non-zero, all digits are printed including leading zeros.
; When zero, leading '0' chars are replaced with spaces.
zero_suppress_disable
$3638.byte$00; x-ref: $373A, $6109, $6D4E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets VIC-II vertical scroll offset to 3 and enables 25-row display mode.
; Clears bitmap mode (bit 5) while preserving DEN, ECM, and RST8 bits.
;
; Inputs: None
; Outputs: A (modified)
; Side Effects: Modifies VIC-II Control Register 1 ($D011)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
set_vic_scroll_and_rows
$3639ad 11 d0lda$d011; Read current VIC-II control register 1 ; x-ref: $6966, $6D49 VIC Control Register 1
$363C29 d0and#$d0; Keep DEN, ECM, RST8; clear YSCROLL, RSEL, BMM
$363E09 0bora#$0b; Set YSCROLL=3, RSEL=1 (25 rows)
$36408d 11 d0sta$d011; Apply updated scroll/row settings; VIC Control Register 1
$364360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears the entire screen memory ($0400-$07E7) with spaces ($20)
; and fills color RAM ($D800-$DBE7) with the color value in Y.
;
; Inputs: Y = color value to fill color RAM with
; Outputs: X = 0 (loop counter exhausted), A = Y (color value)
; Side Effects: Screen memory and color RAM are overwritten
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3644a2 00clear_screenldx#$00; Start index at 0 ; x-ref: $5ECB, $6041, $614C, $6608, $6675, ...
$3646a9 20b_3646lda#$20; Space character (screen clear) ; x-ref: $3664
$36489d 00 04staSCREEN_RAM,x; Screen RAM rows 0-5 ($0400+X)
$364B9d fa 04staSCREEN_RAM_R6C10,x; Screen RAM rows 6-12 ($04FA+X)
$364E9d f4 05staSCREEN_RAM_R12C20,x; Screen RAM rows 12-18 ($05F4+X)
$36519d ee 06staSCREEN_RAM_R18C30,x; Screen RAM rows 18-24 ($06EE+X)
$365498tya; Use Y as color value
$36559d 00 d8staCOLOR_RAM,x; Fill color RAM rows 0-5
$36589d fa d8staCOLOR_RAM_R6C10,x; Fill color RAM rows 6-12
$365B9d f4 d9staCOLOR_RAM_R12C20,x; Fill color RAM rows 12-18
$365E9d ee dastaCOLOR_RAM_R18C30,x; Fill color RAM rows 18-24
$3661e8inx; Next byte
$3662e0 facpx#$fa; Loop 250 times (4x250 = 1000 bytes)
$3664d0 e0bneb_3646
$366660rts
$3667ad 11 d0enable_screenlda$d011; x-ref: $5F25, $60B3, $6166, $6638, $668D, ...; VIC Control Register 1
$366A29 10and#$10
$366Cd0 0fbner_367D
$366Ea9 3clda#$3c
$3670cd 12 d0b_3670cmp$d012; x-ref: $3673 Raster Position
$3673b0 fbbcsb_3670
$3675ad 11 d0lda$d011; VIC Control Register 1
$367809 10ora#$10
$367A8d 11 d0sta$d011; VIC Control Register 1
$367D60r_367Drts; x-ref: $366C
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Blanks the screen by clearing the VIC-II DEN (Display Enable) bit.
; Waits for the raster to reach line 251 (bottom border) before disabling
; the display to avoid visual glitches. If the display is already blanked,
; returns immediately. After blanking, disables VIC interrupts and restores
; the default KERNAL IRQ vector via j_218E.
;
; Inputs: None
; Outputs: None
; Side Effects: Display is turned off, VIC interrupts disabled, IRQ vector restored
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$367Ead 11 d0blank_screenlda$d011; Read VIC-II control register 1 ; x-ref: $5EC8, $603C, $6104, $6122, $65F8, ...; VIC Control Register 1
$368129 10and#$10; Isolate DEN bit (bit 4 = display enable)
$3683f0 12beqr_3697; Display already off? Skip
$3685a9 fblda#$fb; Target raster line 251 (bottom border)
$3687cd 12 d0b_3687cmp$d012; Wait for raster to reach line 251 ; x-ref: $368A Raster Position
$368Ab0 fbbcsb_3687
$368Cad 11 d0lda$d011; Read control register again; VIC Control Register 1
$368F29 efand#$ef; Clear DEN bit to blank display
$36918d 11 d0sta$d011; Store back: display now off; VIC Control Register 1
$36944c 8e 21jmpirq_raster_disable; Disable VIC IRQs, restore default KERNAL IRQ
$369760r_3697rts; x-ref: $3683
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Waits for a specified number of video frames by polling the VIC-II raster
; register ($D012). Loops until raster line $FB is reached, repeating X times.
; Used as a frame-accurate delay (e.g., X=$3C ≈ 60 frames ≈ ~1 second on PAL).
;
; Inputs: X = number of frames to wait
; Outputs: X = 0, A = #$FB (clobbered)
; Side Effects: Blocks execution until the raster condition is met X times
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3698a9 fbwait_raster_frameslda#$fb; Target raster line = 251 (near bottom of screen) ; x-ref: $60B0
$369Acd 12 d0b_369Acmp$d012; Wait while raster hasn't reached target line yet ; x-ref: $369D, $36A5 Raster Position
$369Db0 fbbcsb_369A
$369Fcd 12 d0b_369Fcmp$d012; Wait while raster is still at/past target line ; x-ref: $36A2 Raster Position
$36A290 fbbccb_369F
$36A4cadex; Decrement frame counter
$36A5d0 f3bneb_369A; Loop until X frames have elapsed
$36A760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws encoded text to screen RAM and sets color RAM for each character.
; Reads from source pointer ($FB/$FC), writes to screen pointer ($FD/$FE).
; $00 in source = end of string, $FF = skip to next row.
; Each row processes up to 40 columns (screen width).
;
; Inputs: A = color value, ($FB/$FC) = source text pointer, ($FD/$FE) = screen RAM destination
; Outputs: None (screen and color RAM updated in place)
; Side Effects: Writes to screen RAM and color RAM ($D800-$DBFF)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$36A885 ffdraw_text_with_colorstazp_temp; Store color value ; x-ref: $5EE0, $5EF5, $5F0A, $5F1F, $62C9, ...
$36AAa0 00ldy#$00; Y = source/dest index
$36ACa2 28j_36ACldx#$28; X = 40 columns per row ; x-ref: $36EB
$36AEb1 fbj_36AElda(zp_ptr_src_lo),y; Load next source byte ; x-ref: $36D6
$36B0f0 3cbeqr_36EE; $00 = end of string → return
$36B2c9 ffcmp#$ff; Check for $FF marker
$36B4f0 23beqb_36D9; $FF = skip to next row
$36B691 fdsta(zp_ptr_dst_lo),y; Write char to screen RAM
$36B8a5 fdldazp_ptr_dst_lo; Copy screen ptr low to $02
$36BA85 02stazp_work0
$36BCa5 feldazp_ptr_dst_hi; Mask high byte to 2 bits
$36BE29 03and#$03
$36C018clc; Add $D8 → color RAM base
$36C169 d8adc#$d8
$36C385 03stazp_work1
$36C5a5 ffldazp_temp; Write color to color RAM
$36C791 02sta(zp_work0),y; Advance source pointer
$36C9e6 fbinczp_ptr_src_lo
$36CBd0 02bneb_36CF
$36CDe6 fcinczp_ptr_src_hi
$36CFe6 fdb_36CFinczp_ptr_dst_lo; Advance dest pointer ; x-ref: $36CB
$36D1d0 02bneb_36D5
$36D3e6 feinczp_ptr_dst_hi
$36D5cab_36D5dex; Columns remaining-- ; x-ref: $36D1
$36D64c ae 36jmpj_36AE; Continue row
$36D9e6 fbb_36D9inczp_ptr_src_lo; Skip $FF marker byte ; x-ref: $36B4
$36DBd0 02bneb_36DF
$36DDe6 fcinczp_ptr_src_hi
$36DF8ab_36DFtxa; X = remaining cols in row ; x-ref: $36DB
$36E018clc
$36E165 fdadczp_ptr_dst_lo; Add remaining cols to dest ptr
$36E385 fdstazp_ptr_dst_lo
$36E5a9 00lda#$00; Carry into high byte
$36E765 feadczp_ptr_dst_hi
$36E985 festazp_ptr_dst_hi
$36EB4c ac 36jmpj_36AC; Start next row
$36EE60r_36EErts; x-ref: $36B0
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Converts the binary value in A to three ASCII decimal digit characters
; (hundreds, tens, units) and prints them to the screen at ($FD) with
; leading zero suppression. Uses repeated subtraction for the conversion.
;
; Inputs: A = binary value (0-255), X = digit count for display,
; ($FD/$FE) = pointer to screen destination
; Outputs: Screen memory at ($FD) is updated with decimal ASCII digits
; Side Effects: Digit buffer a_3630-a_3632 is overwritten, zpa_FF is modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
print_byte_as_decimal
$36EF86 ffstxzp_temp; Save digit count for print epilogue ; x-ref: $6E13
$36F1a0 2fldy#$2f; Y = $2F ('0'-1), will count hundreds
$36F3a2 3aldx#$3a; X = $3A ('9'+1), will count tens
$36F538sec; Prepare for subtraction
$36F6c8b_36F6iny; Next hundreds digit ; x-ref: $36F9
$36F7e9 64sbc#$64; Subtract 100
$36F9b0 fbbcsb_36F6; Loop while A >= 0 (hundreds)
$36FBcab_36FBdex; Next tens digit ; x-ref: $36FE
$36FC69 0aadc#$0a; Add 10 (undo overshoot)
$36FE30 fbbmib_36FB; Loop while A < 0 (tens)
$370069 2fadc#$2f; Convert units remainder to ASCII
$37028d 30 36stabcd_digit_buffer; Store units digit
$37058e 31 36stxbcd_digit_tens; Store tens digit (ASCII in X); Tens digit of BCD conversion result (ASCII)
$37088c 32 36stybcd_digit_hundreds; Store hundreds digit (ASCII in Y); Hundreds digit of BCD conversion result (ASCII)
$370B4c 33 37jmpj_3733; Jump to shared print/suppress routine
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Unpacks BCD-encoded bytes from ($FB) into individual ASCII digit characters
; and prints them to the screen at ($FD). Leading zeros are suppressed (replaced
; with spaces) unless the flag at a_3638 is non-zero.
; The digit count in X specifies the total number of screen characters to produce.
;
; Inputs: X = digit count, ($FB/$FC) = pointer to packed BCD source,
; ($FD/$FE) = pointer to screen destination
; Outputs: Screen memory at ($FD) is updated with ASCII digits
; Side Effects: Temporary buffer a_3630 is overwritten, zpa_FF is modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$370E86 ffprint_bcd_numberstxzp_temp; Save digit count ; x-ref: $6098, $6DDA
$3710a0 00ldy#$00; Y = source byte index
$3712a2 00ldx#$00; X = destination digit index
$3714b1 fbb_3714lda(zp_ptr_src_lo),y; Load packed BCD byte from source ; x-ref: $3731
$371629 0fand#$0f; Isolate low nibble (ones digit)
$371809 30ora#$30; Convert to ASCII '0'-'9' (screen code)
$371A9d 30 36stabcd_digit_buffer,x; Store digit in temp buffer
$371De8inx
$371Ee4 ffcpxzp_temp; All digits extracted? Done
$3720f0 11beqj_3733; Reload same BCD byte for high nibble
$3722b1 fblda(zp_ptr_src_lo),y
$37244alsra; Shift high nibble down (4x LSR)
$37254alsra
$37264alsra
$37274alsra
$372809 30ora#$30; Convert to ASCII '0'-'9'
$372A9d 30 36stabcd_digit_buffer,x; Store tens digit in temp buffer
$372Dc8iny; Next source byte
$372Ee8inx
$372Fe4 ffcpxzp_temp; All digits done? Loop if not
$3731d0 e1bneb_3714
$3733a0 00j_3733ldy#$00; Y = output screen index ; x-ref: $370B, $3720
$3735a6 ffldxzp_temp; X = last digit index
$3737cadex
$3738f0 14beqb_374E; Only 1 digit? Skip zero suppression
$373Aad 38 36ldazero_suppress_disable; Check zero-suppression disable flag
$373Dd0 0fbneb_374E; Flag set? Skip suppression
$373Fbd 30 36b_373Fldabcd_digit_buffer,x; Read digit from buffer (MSB first) ; x-ref: $374C
$3742c9 30cmp#$30; Is it '0'?
$3744d0 0bbneb_3751; Non-zero found, start printing
$3746a9 20lda#$20; Replace leading zero with space
$374891 fdsta(zp_ptr_dst_lo),y; Write space to screen
$374Ac8iny
$374Bcadex; Next most-significant digit
$374Cd0 f1bneb_373F
$374Ebd 30 36b_374Eldabcd_digit_buffer,x; Load digit from buffer ; x-ref: $3738, $373D, $3755
$375191 fdb_3751sta(zp_ptr_dst_lo),y; Write digit/char to screen ; x-ref: $3744
$3753c8iny
$3754cadex; Previous digit position
$375510 f7bplb_374E; More digits? Continue output
$375760rts
$3758typewriter_done_flag.byte$00; x-ref: $375D, $3769, $377D, $60B9
$3759typewriter_col_index.byte$00; x-ref: $3760, $379A, $37BA, $37C0, $37D4
typewriter_delay_counter
$375A.byte$00; x-ref: $3765, $376F, $37AC, $37D9
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the typewriter text effect.
; Resets the done flag, column index, and delay counter so the
; update_typewriter routine can begin printing characters one at a time.
;
; Inputs: zpp_F5/F6 = pointer to encoded text data
; zpa_F7/F8 = pointer to screen memory destination row
; Outputs: typewriter_done_flag, typewriter_col_index, typewriter_delay_counter
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$375Ba9 00init_typewriterlda#$00; Clear accumulator ; x-ref: $60AB
$375D8d 58 37statypewriter_done_flag; Reset done flag = not done
$37608d 59 37statypewriter_col_index; Reset column index to 0
$3763a9 01lda#$01; Initial delay = 1 (start immediately)
$37658d 5a 37statypewriter_delay_counter
$376860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the typewriter text effect by one tick.
; Reads encoded text from (zpp_F5),Y and writes characters to screen memory
; at (zpa_F7),Y. Supports control codes:
; $00 = end of text (sets done flag to $FF)
; $FF = advance to next screen row (+40 cols), reset column
; $FE = pause (sets delay counter to 60 frames)
; $1E = blank separator (increments column without writing)
; other = screen code character, written to screen
;
; Inputs: zpp_F5/F6 = pointer to current position in text data
; zpa_F7/F8 = pointer to current screen row
; Outputs: typewriter_done_flag = $FF when complete
; Side Effects: Writes characters to screen memory; sets a_8B76 flag for
; non-space characters
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3769ad 58 37update_typewriterldatypewriter_done_flag; Check if typewriter already finished ; x-ref: $60D3
$376Cf0 01beqb_376F; If done ($FF), exit early
$376E60rts
$376Fce 5a 37b_376Fdectypewriter_delay_counter; Decrement frame delay counter ; x-ref: $376C
$3772f0 01beqb_3775
$377460rts; Not yet zero, wait more frames
$3775a0 00b_3775ldy#$00; x-ref: $3772
$3777b1 f5typewriter_read_looplda(zp_ptr_text_lo),y; Y = 0, index into text data ; x-ref: $379D, $37BD
$3779d0 06bneb_3781; Read next encoded byte from text data
$377Ba9 fflda#$ff; If non-zero, check control codes
$377D8d 58 37statypewriter_done_flag; $00 = end of text, mark done
$378060rts
$3781c9 ffb_3781cmp#$ff; Is it $FF? (next row marker) ; x-ref: $3779
$3783d0 1bbneb_37A0
$3785e6 f5inczp_ptr_text_lo; Advance text data pointer
$3787d0 02bneb_378B
$3789e6 f6inczp_ptr_text_hi
$378Ba5 f7b_378Bldazp_ptr_screen_lo; Advance screen pointer by 40 columns (next row) ; x-ref: $3787
$378D18clc
$378E69 28adc#$28
$379085 f7stazp_ptr_screen_lo
$3792a5 f8ldazp_ptr_screen_hi
$379469 00adc#$00
$379685 f8stazp_ptr_screen_hi
$3798a9 00lda#$00
$379A8d 59 37statypewriter_col_index; Reset column index for new row
$379D4c 77 37jmptypewriter_read_loop; Loop back to read next byte
$37A0c9 feb_37A0cmp#$fe; Is it $FE? (pause marker) ; x-ref: $3783
$37A2d0 0cbneb_37B0
$37A4e6 f5inczp_ptr_text_lo; Advance text data pointer past marker
$37A6d0 02bneb_37AA
$37A8e6 f6inczp_ptr_text_hi
$37AAa9 3cb_37AAlda#$3c; Set delay to 60 frames (~1 second pause) ; x-ref: $37A6
$37AC8d 5a 37statypewriter_delay_counter
$37AF60rts
$37B0c9 1eb_37B0cmp#$1e; Is it $1E? (blank separator) ; x-ref: $37A2
$37B2d0 0cbneb_37C0
$37B4e6 f5inczp_ptr_text_lo; Advance text pointer (skip separator)
$37B6d0 02bneb_37BA
$37B8e6 f6inczp_ptr_text_hi
$37BAee 59 37b_37BAinctypewriter_col_index; Increment column without writing to screen ; x-ref: $37B6
$37BD4c 77 37jmptypewriter_read_loop; Loop back to read next byte
$37C0ac 59 37b_37C0ldytypewriter_col_index; Regular char: Y = current column index ; x-ref: $37B2
$37C391 f7sta(zp_ptr_screen_lo),y; Write screen code to screen memory
$37C5c9 20cmp#$20; Is it a space ($20)?
$37C7f0 05beqb_37CE
$37C9a9 01lda#$01; Non-space: set flag (e.g., trigger SFX)
$37CB8d 76 8bstasfx7_enable
$37CEe6 f5b_37CEinczp_ptr_text_lo; Advance text data pointer ; x-ref: $37C7
$37D0d0 02bneb_37D4
$37D2e6 f6inczp_ptr_text_hi
$37D4ee 59 37b_37D4inctypewriter_col_index; Advance column index ; x-ref: $37D0
$37D7a9 05lda#$05; Delay 5 frames between characters
$37D98d 5a 37statypewriter_delay_counter
$37DC60rts
$37DD.fill35, $00
; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Charset (???)
$3800.fill11, $00
$380B.byte$04, $00, $00, $10, $00, $00, $40, $00
$3813.byte$00, $20, $00, $02, $00, $a0, $c8, $22
$381B.byte$40, $e4, $ed, $df, $ff, $09, $46, $0a
$3823.byte$9f, $6e, $fd, $df, $3b, $02, $10, $00
$382B.byte$44, $a0, $ec, $f1, $7b, $1c, $34, $24
$3833.byte$2c, $ea, $a2, $56, $55, $38, $11, $12
$383B.byte$02, $24, $1a, $08, $11, $1c, $08, $0c
$3843.byte$08, $08, $10, $1c, $14, $ef, $f5, $b7
$384B.byte$fd, $bc, $59, $30, $11, $fe, $ed, $7d
$3853.byte$7f, $26, $43, $0a, $09, $ff, $db, $f5
$385B.byte$60, $10, $00, $08, $08, $95, $55, $4a
$3863.byte$32, $2a, $24, $2c, $14, $7b, $3b, $d6
$386B.byte$ee, $de, $37, $77, $7b, $ec, $de, $df
$3873.byte$ee, $ed, $6b, $b5, $dc, $bc, $7e, $bf
$387B.byte$b7, $ab, $dd, $bc, $7b, $74, $ba, $f7
$3883.byte$af, $dc, $bb, $7d, $be, $73, $3b, $d6
$388B.byte$e8, $be, $37, $74, $6b, $ec, $1c, $d3
$3893.byte$ac, $6d, $6b, $95, $d6, $9c, $66, $b9
$389B.byte$37, $ab, $dd, $a4, $7b, $3c, $78, $e2
$38A3.byte$f6, $77, $f3, $dd, $0d, $3c, $f8, $f0
$38AB.byte$ac, $84, $be, $e8, $cc, $82, $ff, $59
$38B3.byte$51, $87, $bc, $ff, $76, $1e, $3f, $79
$38BB.byte$37, $06, $3c, $1f, $1f, $04, $0b, $1b
$38C3.byte$0e, $06, $1b, $0d, $0d, $d0, $b0, $60
$38CB.byte$b0, $f8, $78, $b0, $90, $00, $18, $1d
$38D3.byte$87, $fb, $bd, $d7, $ff, $06, $0f, $19
$38DB.byte$b6, $ef, $5b, $bd, $ff, $00, $00, $81
$38E3.byte$c3, $e2, $b1, $df, $ff, $ff, $d7, $bd
$38EB.byte$fb, $87, $1d, $18, $00, $ff, $bd, $5b
$38F3.byte$ef, $b6, $19, $0f, $06, $ff, $df, $b1
$38FB.byte$e2, $c3, $81, $00, $00, $20, $6c, $f7
$3903.byte$df, $fd, $fe, $ff, $7b, $fb, $7f, $ff
$390B.byte$f7, $ff, $fd, $d8, $08, $f7, $df, $fe
$3913.byte$b7, $cf, $85, $00, $00, $6f, $f6, $1b
$391B.byte$df, $7b, $f2, $db, $be, $00, $40, $d9
$3923.byte$fb, $ff, $fe, $ef, $df, $be, $fb, $df
$392B.byte$e7, $ef, $bc, $fb, $6f, $37, $3f, $1f
$3933.byte$2e, $3f, $f7, $7d, $77, $7f, $dd, $ef
$393B.byte$3f, $f7, $ce, $7f, $77, $1d, $0f, $0b
$3943.byte$0f, $1f, $37, $3f, $1e, $db, $f6, $fc
$394B.byte$be, $fc, $b8, $f1, $f8, $7e, $fe, $db
$3953.byte$f6, $fc, $be, $7b, $fe, $f8, $ec, $fc
$395B.byte$b8, $f1, $f8, $78, $f4, $09, $26, $1f
$3963.byte$16, $6d, $1f, $6f, $b3, $48, $d0, $64
$396B.byte$f8, $ec, $f5, $fe, $bd, $af, $7b, $77
$3973.byte$17, $4d, $1b, $17, $02, $fd, $dd, $ea
$397B.byte$fe, $aa, $d4, $e8, $40, $ff, $ff, $ff
$3983.byte$ff, $00, $00, $00, $00, $ff, $ff, $ff
$398B.byte$00, $00, $00, $00, $00, $ff, $ff, $00
$3993.byte$00, $00, $00, $00, $00, $ff, $00, $00
$399B.byte$00, $00, $00, $00, $00, $5e, $ed, $f3
$39A3.byte$f7, $ef, $6f, $17, $79, $7c, $9a, $e7
$39AB.byte$ef, $ef, $e6, $d9, $3b, $fe, $7c, $3d
$39B3.byte$41, $ee, $ef, $5f, $1e, $3b, $3a, $b4
$39BB.byte$cf, $c7, $3a, $7c, $fe, $34, $76, $26
$39C3.byte$02, $68, $ed, $6d, $01, $5c, $9a, $67
$39CB.byte$73, $22, $9c, $d8, $8a, $06, $36, $74
$39D3.byte$30, $21, $4d, $6e, $48, $37, $7a, $b0
$39DB.byte$c7, $d6, $b8, $10, $cc
$39E0.fill32, $00
; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
; Intro Sprites
; sprite #E8: L
$3A00.byte$00, $00, $00, $f8, $00, $00, $f8, $00
$3A08.byte$00, $f8, $00, $00, $f8, $00, $00, $f8
$3A10.byte$00, $00, $f8, $00, $00, $f8, $00, $00
$3A18.byte$f8, $00, $00, $f8, $00, $00, $f8, $00
$3A20.byte$00, $f8, $00, $00, $f8, $00, $00, $f8
$3A28.byte$00, $00, $f8, $00, $00, $ff, $ff, $f0
$3A30.byte$ff, $ff, $f0, $ff, $ff, $f0, $ff, $ff
$3A38.byte$f0, $ff, $ff, $f0, $00, $00, $00, $01
; Srite #$E9: C
$3A40.byte$00, $00, $00, $01, $ff, $f0, $01, $ff
$3A48.byte$f0, $01, $ff, $f0, $01, $ff, $f0, $01
$3A50.byte$ff, $f0, $01, $f0, $00, $01, $f0, $00
$3A58.byte$01, $ff, $f0, $01, $ff, $f0, $01, $ff
$3A60.byte$f0, $01, $ff, $f0, $01, $ff, $f0
$3A67.fill24, $00
$3A7F.byte$01
; Sprite #$EA: Vertical MES
$3A80.byte$00, $00, $00, $70, $f8, $00, $f1, $fc
$3A88.byte$00, $c3, $8c, $00, $c7, $0c, $00, $ce
$3A90.byte$0c, $00, $fc, $3c, $00, $78, $38, $00
$3A98.byte$00, $00, $00, $c0, $0c, $00, $c3, $0c
$3AA0.byte$00, $c3, $0c, $00, $c3, $0c, $00, $c3
$3AA8.byte$0c, $00, $ff, $fc, $00, $ff, $fc, $00
$3AB0.byte$00, $00, $00, $ff, $fc, $00, $ff, $fc
$3AB8.byte$00, $70, $00, $00, $38, $00, $00, $01
; Sprite #$EB: Vertical GAM
$3AC0.byte$70, $00, $00, $ff, $fc, $00, $ff, $fc
$3AC8.byte$00, $00, $00, $00, $7f, $fc, $00, $ff
$3AD0.byte$fc, $00, $c3, $00, $00, $c3, $00, $00
$3AD8.byte$c3, $00, $00, $ff, $fc, $00, $7f, $fc
$3AE0.byte$00, $00, $00, $00, $73, $f8, $00, $f3
$3AE8.byte$fc, $00, $c3, $0c, $00, $c3, $0c, $00
$3AF0.byte$c0, $0c, $00, $ff, $fc, $00, $7f, $f8
$3AF8.byte$00, $00, $00, $00, $00, $00, $00, $01
; Sprite #$EC: PRE
$3B00.byte$0f, $cf, $cf, $0f, $ef, $ef, $0c, $6c
$3B08.byte$6c, $0c, $6c, $6c, $0c, $6c, $6c, $0c
$3B10.byte$6c, $6c, $0c, $6c, $6f, $0c, $6c, $ef
$3B18.byte$0f, $ef, $cc, $0f, $cf, $8c, $0c, $0c
$3B20.byte$cc, $0c, $0c, $cc, $0c, $0c, $6f, $0c
$3B28.byte$0c, $6f
$3B2A.fill21, $00
$3B3F.byte$01
; Sprite #$ED: ESEN
$3B40.byte$cf, $9f, $b1, $df, $df, $b1, $18, $d8
$3B48.byte$39, $18, $d8, $39, $1c, $18, $3d, $0e
$3B50.byte$18, $3d, $87, $1f, $3f, $83, $9f, $3f
$3B58.byte$01, $d8, $37, $00, $d8, $37, $18, $d8
$3B60.byte$33, $18, $d8, $33, $df, $df, $b1, $cf
$3B68.byte$9f, $b1
$3B6A.fill21, $00
$3B7F.byte$01
; Sprite #$EE: NTS
$3B80.byte$bf, $3e, $00, $bf, $7f, $00, $8c, $63
$3B88.byte$00, $8c, $63, $00, $8c, $70, $00, $8c
$3B90.byte$38, $00, $8c, $1c, $00, $8c, $0e, $00
$3B98.byte$8c, $07, $00, $8c, $03, $00, $8c, $63
$3BA0.byte$00, $8c, $63, $00, $8c, $7f, $00, $8c
$3BA8.byte$3e
$3BA9.fill22, $00
$3BBF.byte$01
; Sprite #$EF: LC GA
$3BC0.byte$00, $00, $00, $00, $00, $00, $ef, $e1
$3BC8.byte$e7, $ef, $e3, $ef, $ef, $e3, $ef, $ee
$3BD0.byte$03, $0d, $ef, $e3, $6f, $ef, $e3, $6f
$3BD8.byte$ef, $e3, $6f, $e0, $03, $6d, $ff, $e3
$3BE0.byte$ed, $ff, $e3, $ed, $ff, $e1, $cd
$3BE7.fill24, $00
$3BFF.byte$01
; Sprite #$F0: AMES
$3C00.byte$00, $00, $00, $00, $00, $00, $20, $be
$3C08.byte$78, $b1, $be, $f8, $bb, $be, $f8, $bf
$3C10.byte$b0, $c0, $bf, $bc, $f0, $bf, $bc, $f8
$3C18.byte$b5, $bc, $78, $b1, $b0, $18, $b1, $be
$3C20.byte$f8, $b1, $be, $f8, $b1, $be, $f0
$3C27.fill24, $00
$3C3F.byte$01, $00, $00, $00, $77, $f1, $f7, $10
$3C47.byte$12, $18, $10, $10, $10, $10, $00, $00
$3C4F.byte$11, $f0, $b2, $10, $10, $90, $10, $10
$3C57.byte$90, $10, $00, $90, $1f, $f0, $90, $00
$3C5F.byte$10, $10, $00, $10
$3C63.fill28, $00
$3C7F.byte$0e, $00, $00, $00, $21, $7c, $f0, $93
$3C87.byte$41, $80, $0e, $41, $00, $04, $41, $00
$3C8F.byte$00, $49, $20, $00, $41, $00, $00, $41
$3C97.byte$00, $00, $40, $80, $00, $4d, $e0, $00
$3C9F.byte$41, $00, $00, $41
$3CA3.fill28, $00
$3CBF.byte$0e, $3b, $f8, $f7, $08, $08, $08, $00
$3CC7.byte$08, $00, $00, $00, $00, $00, $18, $10
$3CCF.byte$00, $08, $00, $00, $08, $00, $00, $00
$3CD7.byte$00, $00, $18, $00, $00, $08, $00, $00
$3CDF.byte$08
$3CE0.fill31, $00
$3CFF.byte$06, $22, $f9, $e0, $96, $83, $00, $0c
$3D07.fill9, $00
$3D10.byte$02, $00, $00, $02, $00, $00, $00, $00
$3D18.byte$00, $03, $00, $00, $02
$3D1D.fill34, $00
$3D3F.byte$06, $60, $00, $06, $80, $00, $08
$3D46.fill39, $00
$3D6D.byte$1f, $ff, $ff, $20
$3D71.fill14, $00
$3D7F.byte$01, $7f, $ff, $e0, $80
$3D84.fill41, $00
$3DAD.byte$7f, $e0, $00, $80
$3DB1.fill14, $00
$3DBF.byte$01, $7f, $ff, $80, $80
$3DC4.fill22, $00
$3DDA.byte$10, $00, $00, $10, $00, $00, $10, $00
$3DE2.byte$00, $10, $00, $00, $10, $00, $00, $10
$3DEA.byte$00, $00, $20, $7f, $ff, $c0, $80
$3DF1.fill14, $00
$3DFF.byte$01, $00, $03, $f0, $00, $1c, $00, $00
$3E07.byte$60, $00, $00, $80, $00, $01
$3E0D.fill11, $00
$3E18.byte$10, $00, $00, $20, $00, $00, $40, $00
$3E20.byte$00, $40, $00, $00, $80, $00, $00, $80
$3E28.fill23, $00
$3E3F.byte$01, $ff, $ff, $80
$3E43.fill60, $00
$3E7F.byte$01
$3E80joystick_state.byte$00; x-ref: $1D54, $1D60, $1D7A, $3E82, $3E92, ...
$3E81joystick_prev_state.byte$00; x-ref: $3E85, $5D5E, $5D78, $5DB2, $5DD0, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Reads joystick port 2 and updates the current joystick state.
; Saves the previous state for edge detection before reading new input.
; If the SID paddle register ($D419) reads zero, clears bit 5 of the
; joystick value (masks out a secondary fire button or paddle input).
;
; Inputs: None
; Outputs: joystick_state = current joystick bits, joystick_prev_state = previous frame
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3E82ad 80 3eread_joystickldajoystick_state; save current state as previous ; x-ref: $20F5, $5D54
$3E858d 81 3estajoystick_prev_state
$3E88ad 00 dclda$dc00; read joystick port 2 (active low); CIA1: Data Port Register A
$3E8Bae 19 d4ldx$d419; read SID paddle register; Analog/Digital Converter: Game Paddle 1
$3E8Ed0 02bneb_3E92; paddle != 0? skip masking
$3E9029 dfand#$df; clear bit 5 (secondary fire) if paddle=0
$3E928d 80 3eb_3E92stajoystick_state; store new joystick state ; x-ref: $3E8E
$3E9560rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Filters phantom joystick inputs caused by keyboard matrix ghosting.
; Reads the current joystick state (a_3E80) and for each active direction/fire
; bit, checks if the corresponding keyboard row shows a key pressed. If the
; key is NOT pressed, the joystick bit is cleared as a ghost artifact.
;
; Bit mapping:
; Bit 0 (Up) -> keyboard row a_3F10, mask $02
; Bit 1 (Down) -> keyboard row a_3F10, mask $20
; Bit 2 (Left) -> keyboard row a_3F10, mask $04
; Bit 3 (Right) -> keyboard row a_3F12, mask $04
; Bit 4 (Fire) -> keyboard row a_3F0E, mask $80 OR a_3F14, mask $10
;
; Inputs: a_3E80 (raw joystick state), a_3F0C-a_3F16 (keyboard scan results)
; Outputs: a_3E80 (filtered joystick state)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
filter_joystick_from_keyboard
$3E96ad 80 3eldajoystick_state; check joystick Up bit ; x-ref: $20FB
$3E9929 01and#$01; bit 0 = Up
$3E9Bf0 0fbeqb_3EAC; skip if Up not active
$3E9Dad 10 3fldakb_row4_cur; check keyboard row for Up ghost
$3EA029 02and#$02; key pressed? (active low)
$3EA2d0 08bneb_3EAC; skip clear if key is pressed
$3EA4ad 80 3eldajoystick_state
$3EA729 feand#$fe; clear bit 0 (Up) - ghost input
$3EA98d 80 3estajoystick_state
$3EACad 80 3eb_3EACldajoystick_state; check joystick Down bit ; x-ref: $3E9B, $3EA2
$3EAF29 02and#$02; bit 1 = Down
$3EB1f0 0fbeqb_3EC2; skip if Down not active
$3EB3ad 10 3fldakb_row4_cur; check keyboard row for Down ghost
$3EB629 20and#$20; key pressed?
$3EB8d0 08bneb_3EC2; skip clear if key is pressed
$3EBAad 80 3eldajoystick_state
$3EBD29 fdand#$fd; clear bit 1 (Down) - ghost input
$3EBF8d 80 3estajoystick_state
$3EC2ad 80 3eb_3EC2ldajoystick_state; check joystick Left bit ; x-ref: $3EB1, $3EB8
$3EC529 04and#$04; bit 2 = Left
$3EC7f0 0fbeqb_3ED8; skip if Left not active
$3EC9ad 10 3fldakb_row4_cur; check keyboard row for Left ghost
$3ECC29 04and#$04; key pressed?
$3ECEd0 08bneb_3ED8; skip clear if key is pressed
$3ED0ad 80 3eldajoystick_state
$3ED329 fband#$fb; clear bit 2 (Left) - ghost input
$3ED58d 80 3estajoystick_state
$3ED8ad 80 3eb_3ED8ldajoystick_state; check joystick Right bit ; x-ref: $3EC7, $3ECE
$3EDB29 08and#$08; bit 3 = Right
$3EDDf0 0fbeqb_3EEE; skip if Right not active
$3EDFad 12 3fldakb_row5_cur; check keyboard row for Right ghost
$3EE229 04and#$04; key pressed?
$3EE4d0 08bneb_3EEE; skip clear if key is pressed
$3EE6ad 80 3eldajoystick_state
$3EE929 f7and#$f7; clear bit 3 (Right) - ghost input
$3EEB8d 80 3estajoystick_state
$3EEEad 80 3eb_3EEEldajoystick_state; check joystick Fire bit ; x-ref: $3EDD, $3EE4
$3EF129 10and#$10; bit 4 = Fire
$3EF3f0 16beqr_3F0B; skip if Fire not active
$3EF5ad 0e 3fldakb_row1_cur; check 1st keyboard row for Fire ghost
$3EF829 80and#$80; key pressed?
$3EFAf0 07beqb_3F03; no ghost from this row, check 2nd row
$3EFCad 14 3fldakb_row6_cur; check 2nd keyboard row for Fire ghost
$3EFF29 10and#$10; key pressed?
$3F01d0 08bner_3F0B; key confirms Fire - keep it
$3F03ad 80 3eb_3F03ldajoystick_state; x-ref: $3EFA
$3F0629 efand#$ef; clear bit 4 (Fire) - ghost input
$3F088d 80 3estajoystick_state
$3F0B60r_3F0Brts; x-ref: $3EF3, $3F01
$3F0Ckb_row0_cur.byte$00; x-ref: $3F18, $3F49, $3F8B, $6182, $6EA7
$3F0Dkb_row0_prev.byte$00; x-ref: $3F1B, $6185, $6EAA
$3F0Ekb_row1_cur.byte$00; x-ref: $3EF5, $3F1E, $3F54, $3F8E
$3F0Fkb_row1_prev.byte$00; x-ref: $3F21
$3F10kb_row4_cur.byte$00; x-ref: $3E9D, $3EB3, $3EC9, $3F24, $3F5F, ...
$3F11kb_row4_prev.byte$00; x-ref: $3F27
$3F12kb_row5_cur.byte$00; x-ref: $3EDF, $3F2A, $3F6A, $3F94
$3F13kb_row5_prev.byte$00; x-ref: $3F2D
$3F14kb_row6_cur.byte$00; x-ref: $3EFC, $3F30, $3F75, $3F97
$3F15kb_row6_prev.byte$00; x-ref: $3F33
$3F16kb_row7_cur.byte$00; x-ref: $3F36, $3F80, $3F9A, $8093
$3F17kb_row7_prev.byte$00; x-ref: $3F39, $8096
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans 6 rows of the C128 keyboard matrix via CIA1 ($DC00-$DC02).
; First saves the previous scan results into shadow variables (a_3F0D,
; a_3F0F, a_3F11, a_3F13, a_3F15, a_3F17), then selects each keyboard
; row by writing the row-select mask to CIA1 Port A and reading the
; column state from CIA1 Port B.
;
; Row mapping:
; $FE -> row 0 -> a_3F0C $FD -> row 1 -> a_3F0E
; $EF -> row 4 -> a_3F10 $DF -> row 5 -> a_3F12
; $BF -> row 6 -> a_3F14 $7F -> row 7 -> a_3F16
;
; Inputs: None
; Outputs: a_3F0C-a_3F16 (current scan), a_3F0D-a_3F17 (previous scan)
; Side Effects: Temporarily sets CIA1 DDRA to output ($FF), restores to $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3F18ad 0c 3fscan_keyboard_matrixldakb_row0_cur; save previous row 0 scan ; x-ref: $20F8
$3F1B8d 0d 3fstakb_row0_prev; previous row 0 result
$3F1Ead 0e 3fldakb_row1_cur; save previous row 1 scan
$3F218d 0f 3fstakb_row1_prev; previous row 1 result
$3F24ad 10 3fldakb_row4_cur; save previous row 4 scan
$3F278d 11 3fstakb_row4_prev; previous row 4 result
$3F2Aad 12 3fldakb_row5_cur; save previous row 5 scan
$3F2D8d 13 3fstakb_row5_prev; previous row 5 result
$3F30ad 14 3fldakb_row6_cur; save previous row 6 scan
$3F338d 15 3fstakb_row6_prev; previous row 6 result
$3F36ad 16 3fldakb_row7_cur; save previous row 7 scan
$3F398d 17 3fstakb_row7_prev; previous row 7 result
$3F3Ca9 fflda#$ff; set all Port A bits to output
$3F3E8d 02 dcsta$dc02; CIA1: Data Direction Register A
$3F41a9 felda#$fe; select keyboard row 0
$3F438d 00 dcsta$dc00; CIA1: Data Port Register A
$3F46ad 01 dclda$dc01; read column state for row 0; CIA1: Data Port Register B
$3F498d 0c 3fstakb_row0_cur; store row 0 result
$3F4Ca9 fdlda#$fd; select keyboard row 1
$3F4E8d 00 dcsta$dc00; CIA1: Data Port Register A
$3F51ad 01 dclda$dc01; read column state for row 1; CIA1: Data Port Register B
$3F548d 0e 3fstakb_row1_cur; store row 1 result
$3F57a9 eflda#$ef; select keyboard row 4
$3F598d 00 dcsta$dc00; CIA1: Data Port Register A
$3F5Cad 01 dclda$dc01; read column state for row 4; CIA1: Data Port Register B
$3F5F8d 10 3fstakb_row4_cur; store row 4 result
$3F62a9 dflda#$df; select keyboard row 5
$3F648d 00 dcsta$dc00; CIA1: Data Port Register A
$3F67ad 01 dclda$dc01; read column state for row 5; CIA1: Data Port Register B
$3F6A8d 12 3fstakb_row5_cur; store row 5 result
$3F6Da9 bflda#$bf; select keyboard row 6
$3F6F8d 00 dcsta$dc00; CIA1: Data Port Register A
$3F72ad 01 dclda$dc01; read column state for row 6; CIA1: Data Port Register B
$3F758d 14 3fstakb_row6_cur; store row 6 result
$3F78a9 7flda#$7f; select keyboard row 7
$3F7A8d 00 dcsta$dc00; CIA1: Data Port Register A
$3F7Dad 01 dclda$dc01; read column state for row 7; CIA1: Data Port Register B
$3F808d 16 3fstakb_row7_cur; store row 7 result
$3F83a9 00lda#$00; restore Port A to input mode
$3F858d 02 dcsta$dc02; CIA1: Data Direction Register A
$3F8860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets all keyboard matrix current-scan variables to $FF (no keys pressed).
; Used during state transitions to prevent stale key presses from being
; processed. The CIA keyboard matrix is active-low, so $FF means all bits
; high = no keys held.
;
; Inputs: None
; Outputs: kb_row0_cur..kb_row7_cur all set to $FF
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$3F89a9 ffreset_keyboard_statelda#$ff; $FF = no keys pressed (active-low) ; x-ref: $1D4A
$3F8B8d 0c 3fstakb_row0_cur; Clear row 0
$3F8E8d 0e 3fstakb_row1_cur; Clear row 1
$3F918d 10 3fstakb_row4_cur; Clear row 4
$3F948d 12 3fstakb_row5_cur; Clear row 5
$3F978d 14 3fstakb_row6_cur; Clear row 6
$3F9A8d 16 3fstakb_row7_cur; Clear row 7
$3F9D60rts
; Current room/screen index within the level map.
; Bits 2-0 = column (0-7), bits 5-3 = row (0-11).
; Used to index the screen data pointer table at $E000.
$3F9Ecurrent_room_index.byte$00; x-ref: $4AEA, $4B1E, $4E17, $5011, $5018, ...
; Color value for the top section of the playfield.
; Loaded from the level palette table f_48F9 based on world index.
$3F9Flevel_color_top.byte$00; x-ref: $49FF, $4E2F, $4E50
$3FA0level_color_bottom.byte$00; Color value for the bottom section of the playfield ; x-ref: $4A05, $4E42, $4E56
$3FA1level_color_border.byte$00; Border/background color 3 for the level ; x-ref: $4A0B, $4CD0, $4E22
level_color_multicolor
$3FA2.byte$00; VIC-II ECM multicolor value ($D023) for the level ; x-ref: $4A11, $64B0
; Current active background color applied to color RAM ($D828+) and VIC-II
; background color 3 ($D024). Updated via set_screen_color routine.
; Skips cells with color value 2 (used for special tiles).
$3FA3active_bg_color.byte$00; x-ref: $4E25, $4E62, $4F41, $4F50, $4F91, ...
; Screen slot occupancy flag from f_EF78 lookup.
; Non-zero = slot occupied (partial color fill, bg3 active).
; Zero = empty slot (full color fill). Also used as hero death trigger
; in collision routines — $FF signals death.
$3FA4screen_slot_status.byte$00; x-ref: $1E9F, $1EE0, $4C81, $4CCB, $4E1D, ...
; Map decompression in-progress flag.
; $FF = decompression active (tile 16/hero entity init enabled).
; $00 = decompression complete.
$3FA5map_decompressing.byte$00; x-ref: $4A64, $4A6C, $4FE8
; Set to $FF when screen content has changed and needs redraw.
; Checked by animation subsystems to trigger re-initialization.
; Cleared to $00 at the start of render_level_screen.
$3FA6screen_changed_flag.byte$00; x-ref: $4B1B, $4D02, $7A20
; Rendering lock flag.
; Set to $FF at start of render_level_screen, cleared to $00 at end.
; Prevents re-entrant rendering during IRQ-driven updates.
rendering_in_progress
$3FA7.byte$00; x-ref: $4AF2, $4CDD, $7798, $7802, $7AD4, ...
; Snapshot of combined animation subsystem flags (OR of 3 subsystems).
; Saved at start of render_level_screen to preserve animation state
; across the re-initialization of all tile subsystems.
saved_animation_flags
$3FA8.byte$00; x-ref: $4AFE, $4C33
; Lo-byte pointer table for predefined screen row patterns.
; 17 entries ($3FA9-$3FB9), paired with row_pattern_ptrs_hi.
; Used by screen decompressor to copy 40-byte row templates.
$3FA9row_pattern_ptrs_lo.byte$c0, $e8, $10, $38, $60, $88, $b0, $d8; x-ref: $4D37
$3FB1.byte$00, $28, $50, $78, $a0, $c8, $f0, $18
$3FB9.byte$40
; Hi-byte pointer table for predefined screen row patterns.
; 17 entries ($3FBA-$3FCA), paired with row_pattern_ptrs_lo.
$3FBArow_pattern_ptrs_hi.byte$e0, $e0, $e1, $e1, $e1, $e1, $e1, $e1; x-ref: $4D3C
$3FC2.byte$e2, $e2, $e2, $e2, $e2, $e2, $e2, $e3
$3FCA.byte$e3
$3FCB.fill40, $00
$3FF3.byte$01, $02, $00, $01, $00, $02, $00, $02
$3FFB.byte$01, $02, $00, $01, $00, $02, $00, $02
$4003.byte$01, $02, $00, $01, $00, $02, $00, $02
$400B.byte$01, $02, $00, $01, $00, $02, $00, $02
$4013.byte$01, $02, $00, $01, $00, $02, $00, $02
$401B.byte$00, $01, $02, $00, $02, $01, $01, $00
$4023.byte$00, $01, $02, $00, $02, $01, $01, $00
$402B.byte$00, $01, $02, $00, $02, $01, $01, $00
$4033.byte$00, $01, $02, $00, $02, $01, $01, $00
$403B.byte$00, $01, $02, $00, $02, $01, $01, $00
$4043.byte$01, $00, $00, $02, $00, $01, $00, $02
$404B.byte$01, $00, $00, $02, $00, $01, $00, $02
$4053.byte$01, $00, $00, $02, $00, $01, $00, $02
$405B.byte$01, $00, $00, $02, $00, $01, $00, $02
$4063.byte$01, $00, $00, $02, $00, $01, $00, $02
$406B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4073.byte$00, $02, $01, $00, $01, $00, $02, $01
$407B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4083.byte$00, $02, $01, $00, $01, $00, $02, $01
$408B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4093.byte$02, $00, $00, $01, $02, $00, $02, $00
$409B.byte$02, $00, $00, $01, $02, $00, $02, $00
$40A3.byte$02, $00, $00, $01, $02, $00, $02, $00
$40AB.byte$02, $00, $00, $01, $02, $00, $02, $00
$40B3.byte$02, $00, $00, $01, $02, $00, $02, $00
$40BB.byte$01, $02, $00, $01, $00, $02, $00, $02
$40C3.byte$01, $02, $00, $01, $00, $02, $00, $02
$40CB.byte$01, $02, $00, $01, $00, $02, $00, $02
$40D3.byte$01, $02, $00, $01, $00, $02, $00, $02
$40DB.byte$01, $02, $00, $01, $00, $02, $00, $02
$40E3.byte$00, $01, $02, $00, $02, $01, $01, $00
$40EB.byte$00, $01, $02, $00, $02, $01, $01, $00
$40F3.byte$00, $01, $02, $00, $02, $01, $01, $00
$40FB.byte$00, $01, $02, $00, $02, $01, $01, $00
$4103.byte$00, $01, $02, $00, $02, $01, $01, $00
$410B.byte$01, $00, $00, $02, $00, $01, $00, $02
$4113.byte$01, $00, $00, $02, $00, $01, $00, $02
$411B.byte$01, $00, $00, $02, $00, $01, $00, $02
$4123.byte$01, $00, $00, $02, $00, $01, $00, $02
$412B.byte$01, $00, $00, $02, $00, $01, $00, $02
$4133.byte$00, $02, $01, $00, $01, $00, $02, $01
$413B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4143.byte$00, $02, $01, $00, $01, $00, $02, $01
$414B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4153.byte$00, $02, $01, $00, $01, $00, $02, $01
$415B.byte$02, $00, $00, $01, $02, $00, $02, $00
$4163.byte$02, $00, $00, $01, $02, $00, $02, $00
$416B.byte$02, $00, $00, $01, $02, $00, $02, $00
$4173.byte$02, $00, $00, $01, $02, $00, $02, $00
$417B.byte$02, $00, $00, $01, $02, $00, $02, $00
$4183.byte$01, $02, $00, $01, $00, $02, $00, $02
$418B.byte$01, $02, $00, $01, $00, $02, $00, $02
$4193.byte$01, $02, $00, $01, $00, $02, $00, $02
$419B.byte$01, $02, $00, $01, $00, $02, $00, $02
$41A3.byte$01, $02, $00, $01, $00, $02, $00, $02
$41AB.byte$00, $01, $02, $00, $02, $01, $01, $00
$41B3.byte$00, $01, $02, $00, $02, $01, $01, $00
$41BB.byte$00, $01, $02, $00, $02, $01, $01, $00
$41C3.byte$00, $01, $02, $00, $02, $01, $01, $00
$41CB.byte$00, $01, $02, $00, $02, $01, $01, $00
$41D3.byte$01, $00, $00, $02, $00, $01, $00, $02
$41DB.byte$01, $00, $00, $02, $00, $01, $00, $02
$41E3.byte$01, $00, $00, $02, $00, $01, $00, $02
$41EB.byte$01, $00, $00, $02, $00, $01, $00, $02
$41F3.byte$01, $00, $00, $02, $00, $01, $00, $02
$41FB.byte$00, $02, $01, $00, $01, $00, $02, $01
$4203.byte$00, $02, $01, $00, $01, $00, $02, $01
$420B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4213.byte$00, $02, $01, $00, $01, $00, $02, $01
$421B.byte$00, $02, $01, $00, $01, $00, $02, $01
$4223.byte$02, $00, $00, $01, $02, $00, $02, $00
$422B.byte$02, $00, $00, $01, $02, $00, $02, $00
$4233.byte$02, $00, $00, $01, $02, $00, $02, $00
$423B.byte$02, $00, $00, $01, $02, $00, $02, $00
$4243.byte$02, $00, $00, $01, $02, $00, $02
$424A.fill41, $00
$4273.byte$1a, $1b, $1c, $1a, $1b, $1c, $1a, $1b
$427B.byte$1c, $1a, $1b, $1c, $1a, $1b, $1c, $1a
$4283.byte$1b, $1c, $1a, $1b, $1c, $1a, $1b, $1c
$428B.byte$1a, $1b, $1c, $1a, $1b, $1c, $1a, $1b
$4293.byte$1c, $1a, $1b, $1c, $1a, $1b, $1c, $1a
$429B.byte$21, $23, $24, $21, $25, $24, $20, $25
$42A3.byte$21, $23, $24, $21, $25, $24, $20, $25
$42AB.byte$21, $23, $24, $21, $25, $24, $20, $25
$42B3.byte$21, $23, $24, $21, $25, $24, $20, $25
$42BB.byte$21, $23, $24, $21, $25, $24, $20, $25
$42C3.byte$20, $23, $20, $21, $22, $21, $22, $21
$42CB.byte$20, $23, $20, $21, $22, $21, $22, $21
$42D3.byte$20, $23, $20, $21, $22, $21, $22, $21
$42DB.byte$20, $23, $20, $21, $22, $21, $22, $21
$42E3.byte$20, $23, $20, $21, $22, $21, $22, $21
$42EB.byte$20, $22, $21, $24, $24, $20, $20, $23
$42F3.byte$20, $22, $21, $24, $24, $20, $20, $23
$42FB.byte$20, $22, $21, $24, $24, $20, $20, $23
$4303.byte$20, $22, $21, $24, $24, $20, $20, $23
$430B.byte$20, $22, $21, $24, $24, $20, $20, $23
$4313.byte$23, $24, $24, $20, $22, $21, $25, $24
$431B.byte$23, $24, $24, $20, $22, $21, $25, $24
$4323.byte$23, $24, $24, $20, $22, $21, $25, $24
$432B.byte$23, $24, $24, $20, $22, $21, $25, $24
$4333.byte$23, $24, $24, $20, $22, $21, $25, $24
$433B.byte$25, $21, $23, $21, $20, $25, $21, $25
$4343.byte$25, $21, $23, $21, $20, $25, $21, $25
$434B.byte$25, $21, $23, $21, $20, $25, $21, $25
$4353.byte$25, $21, $23, $21, $20, $25, $21, $25
$435B.byte$25, $21, $23, $21, $20, $25, $21, $25
$4363.byte$21, $23, $24, $21, $25, $24, $20, $25
$436B.byte$21, $23, $24, $21, $25, $24, $20, $25
$4373.byte$21, $23, $24, $21, $25, $24, $20, $25
$437B.byte$21, $23, $24, $21, $25, $24, $20, $25
$4383.byte$21, $23, $24, $21, $25, $24, $20, $25
$438B.byte$20, $23, $20, $21, $22, $21, $22, $21
$4393.byte$20, $23, $20, $21, $22, $21, $22, $21
$439B.byte$20, $23, $20, $21, $22, $21, $22, $21
$43A3.byte$20, $23, $20, $21, $22, $21, $22, $21
$43AB.byte$20, $23, $20, $21, $22, $21, $22, $21
$43B3.byte$20, $22, $21, $24, $24, $20, $20, $23
$43BB.byte$20, $22, $21, $24, $24, $20, $20, $23
$43C3.byte$20, $22, $21, $24, $24, $20, $20, $23
$43CB.byte$20, $22, $21, $24, $24, $20, $20, $23
$43D3.byte$20, $22, $21, $24, $24, $20, $20, $23
$43DB.byte$23, $24, $24, $20, $22, $21, $25, $24
$43E3.byte$23, $24, $24, $20, $22, $21, $25, $24
$43EB.byte$23, $24, $24, $20, $22, $21, $25, $24
$43F3.byte$23, $24, $24, $20, $22, $21, $25, $24
$43FB.byte$23, $24, $24, $20, $22, $21, $25, $24
$4403.byte$25, $21, $23, $21, $20, $25, $21, $25
$440B.byte$25, $21, $23, $21, $20, $25, $21, $25
$4413.byte$25, $21, $23, $21, $20, $25, $21, $25
$441B.byte$25, $21, $23, $21, $20, $25, $21, $25
$4423.byte$25, $21, $23, $21, $20, $25, $21, $25
$442B.byte$21, $23, $24, $21, $25, $24, $20, $25
$4433.byte$21, $23, $24, $21, $25, $24, $20, $25
$443B.byte$21, $23, $24, $21, $25, $24, $20, $25
$4443.byte$21, $23, $24, $21, $25, $24, $20, $25
$444B.byte$21, $23, $24, $21, $25, $24, $20, $25
$4453.byte$20, $23, $20, $21, $22, $21, $22, $21
$445B.byte$20, $23, $20, $21, $22, $21, $22, $21
$4463.byte$20, $23, $20, $21, $22, $21, $22, $21
$446B.byte$20, $23, $20, $21, $22, $21, $22, $21
$4473.byte$20, $23, $20, $21, $22, $21, $22, $21
$447B.byte$20, $22, $21, $24, $24, $20, $20, $23
$4483.byte$20, $22, $21, $24, $24, $20, $20, $23
$448B.byte$20, $22, $21, $24, $24, $20, $20, $23
$4493.byte$20, $22, $21, $24, $24, $20, $20, $23
$449B.byte$20, $22, $21, $24, $24, $20, $20, $23
$44A3.byte$23, $24, $24, $20, $22, $21, $25, $24
$44AB.byte$23, $24, $24, $20, $22, $21, $25, $24
$44B3.byte$23, $24, $24, $20, $22, $21, $25, $24
$44BB.byte$23, $24, $24, $20, $22, $21, $25, $24
$44C3.byte$23, $24, $24, $20, $22, $21, $25, $24
$44CB.byte$25, $21, $23, $21, $20, $25, $21, $25
$44D3.byte$25, $21, $23, $21, $20, $25, $21, $25
$44DB.byte$25, $21, $23, $21, $20, $25, $21, $25
$44E3.byte$25, $21, $23, $21, $20, $25, $21, $25
$44EB.byte$25, $21, $23, $21, $20, $25, $21, $25
$44F3.byte$30, $31, $32, $33, $33, $32, $31, $30
$44FB.byte$30, $31, $32, $33, $33, $32, $31, $30
$4503.byte$30, $31, $32, $33, $33, $32, $31, $30
$450B.byte$30, $31, $32, $33, $33, $32, $31, $30
$4513.byte$30, $31, $32, $33, $33, $32, $31, $30
$451B.byte$16, $17, $14, $14, $16, $17, $14, $14
$4523.byte$16, $17, $14, $14, $16, $17, $14, $14
$452B.byte$16, $17, $14, $14, $16, $17, $14, $14
$4533.byte$16, $17, $14, $14, $16, $17, $14, $14
$453B.byte$16, $17, $14, $14, $16, $17, $14, $14
$4543.byte$14, $15, $16, $14, $14, $15, $16, $14
$454B.byte$14, $15, $16, $14, $14, $15, $16, $14
$4553.byte$14, $15, $16, $14, $14, $15, $16, $14
$455B.byte$14, $15, $16, $14, $14, $15, $16, $14
$4563.byte$14, $15, $16, $14, $14, $15, $16, $14
$456B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4573.byte$17, $18, $14, $15, $17, $18, $14, $15
$457B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4583.byte$17, $18, $14, $15, $17, $18, $14, $15
$458B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4593.byte$15, $15, $16, $15, $15, $15, $16, $15
$459B.byte$15, $15, $16, $15, $15, $15, $16, $15
$45A3.byte$15, $15, $16, $15, $15, $15, $16, $15
$45AB.byte$15, $15, $16, $15, $15, $15, $16, $15
$45B3.byte$15, $15, $16, $15, $15, $15, $16, $15
$45BB.byte$16, $18, $17, $19, $16, $18, $17, $19
$45C3.byte$16, $18, $17, $19, $16, $18, $17, $19
$45CB.byte$16, $18, $17, $19, $16, $18, $17, $19
$45D3.byte$16, $18, $17, $19, $16, $18, $17, $19
$45DB.byte$16, $18, $17, $19, $16, $18, $17, $19
$45E3.byte$16, $17, $14, $14, $16, $17, $14, $14
$45EB.byte$16, $17, $14, $14, $16, $17, $14, $14
$45F3.byte$16, $17, $14, $14, $16, $17, $14, $14
$45FB.byte$16, $17, $14, $14, $16, $17, $14, $14
$4603.byte$16, $17, $14, $14, $16, $17, $14, $14
$460B.byte$14, $15, $16, $14, $14, $15, $16, $14
$4613.byte$14, $15, $16, $14, $14, $15, $16, $14
$461B.byte$14, $15, $16, $14, $14, $15, $16, $14
$4623.byte$14, $15, $16, $14, $14, $15, $16, $14
$462B.byte$14, $15, $16, $14, $14, $15, $16, $14
$4633.byte$17, $18, $14, $15, $17, $18, $14, $15
$463B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4643.byte$17, $18, $14, $15, $17, $18, $14, $15
$464B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4653.byte$17, $18, $14, $15, $17, $18, $14, $15
$465B.byte$15, $15, $16, $15, $15, $15, $16, $15
$4663.byte$15, $15, $16, $15, $15, $15, $16, $15
$466B.byte$15, $15, $16, $15, $15, $15, $16, $15
$4673.byte$15, $15, $16, $15, $15, $15, $16, $15
$467B.byte$15, $15, $16, $15, $15, $15, $16, $15
$4683.byte$16, $18, $17, $19, $16, $18, $17, $19
$468B.byte$16, $18, $17, $19, $16, $18, $17, $19
$4693.byte$16, $18, $17, $19, $16, $18, $17, $19
$469B.byte$16, $18, $17, $19, $16, $18, $17, $19
$46A3.byte$16, $18, $17, $19, $16, $18, $17, $19
$46AB.byte$16, $17, $14, $14, $16, $17, $14, $14
$46B3.byte$16, $17, $14, $14, $16, $17, $14, $14
$46BB.byte$16, $17, $14, $14, $16, $17, $14, $14
$46C3.byte$16, $17, $14, $14, $16, $17, $14, $14
$46CB.byte$16, $17, $14, $14, $16, $17, $14, $14
$46D3.byte$14, $15, $16, $14, $14, $15, $16, $14
$46DB.byte$14, $15, $16, $14, $14, $15, $16, $14
$46E3.byte$14, $15, $16, $14, $14, $15, $16, $14
$46EB.byte$14, $15, $16, $14, $14, $15, $16, $14
$46F3.byte$14, $15, $16, $14, $14, $15, $16, $14
$46FB.byte$17, $18, $14, $15, $17, $18, $14, $15
$4703.byte$17, $18, $14, $15, $17, $18, $14, $15
$470B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4713.byte$17, $18, $14, $15, $17, $18, $14, $15
$471B.byte$17, $18, $14, $15, $17, $18, $14, $15
$4723.byte$15, $15, $16, $15, $15, $15, $16, $15
$472B.byte$15, $15, $16, $15, $15, $15, $16, $15
$4733.byte$15, $15, $16, $15, $15, $15, $16, $15
$473B.byte$15, $15, $16, $15, $15, $15, $16, $15
$4743.byte$15, $15, $16, $15, $15, $15, $16, $15
$474B.byte$16, $18, $17, $19, $16, $18, $17, $19
$4753.byte$16, $18, $17, $19, $16, $18, $17, $19
$475B.byte$16, $18, $17, $19, $16, $18, $17, $19
$4763.byte$16, $18, $17, $19, $16, $18, $17, $19
$476B.byte$16, $18, $17, $19, $16, $18, $17, $19
$4773.byte$16, $17, $14, $14, $16, $17, $14, $14
$477B.byte$16, $17, $14, $14, $16, $17, $14, $14
$4783.byte$16, $17, $14, $14, $16, $17, $14, $14
$478B.byte$16, $17, $14, $14, $16, $17, $14, $14
$4793.byte$16, $17, $14, $14, $16, $17, $14, $14
$479B.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
$47A3.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
$47AB.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
$47B3.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
$47BB.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
; Background/ceiling tile indices, 120 entries (3 rows × 40 cols). Used when no wall above to fill background.
$47C3bg_tile_table.byte$00, $07, $01, $06, $01, $00, $06, $01; x-ref: $4F3C, $4F4B, $4F5F
$47CB.byte$00, $07, $02, $00, $08, $02, $06, $00
$47D3.byte$02, $06, $07, $02, $01, $00, $01, $02
$47DB.byte$06, $02, $07, $02, $00, $06, $02, $00
$47E3.byte$02, $06, $00, $00, $08, $01, $00, $06
$47EB.byte$03, $05, $04, $03, $03, $04, $04, $05
$47F3.byte$03, $04, $03, $05, $04, $05, $04, $05
$47FB.byte$04, $03, $03, $03, $05, $04, $05, $04
$4803.byte$05, $03, $04, $05, $04, $03, $05, $05
$480B.byte$03, $03, $04, $05, $04, $03, $05, $04
$4813.byte$da, $db, $dc, $db, $dc, $db, $dc, $dc
$481B.byte$da, $dc, $da, $da, $dc, $db, $da, $da
$4823.byte$dc, $da, $db, $dc, $dc, $db, $db, $dc
$482B.byte$da, $da, $da, $da, $dc, $db, $db, $da
$4833.byte$dc, $db, $db, $da, $db, $db, $da, $dc
; Wall decoration tile indices, 120 entries (3 rows × 40 cols). Drawn below wall edges.
$483Bwall_tile_table.byte$dd, $df, $dd, $df, $dd, $df, $dd, $dd; x-ref: $4F82, $4F8C, $4F9A, $4FA8
$4843.byte$de, $dd, $de, $de, $dd, $df, $de, $de
$484B.byte$dd, $de, $df, $dd, $dd, $df, $df, $dd
$4853.byte$de, $de, $de, $de, $dd, $df, $df, $de
$485B.byte$dd, $df, $df, $de, $df, $df, $de, $dd
$4863.byte$0a, $0b, $09, $09, $0b, $0a, $0b, $0b
$486B.byte$0a, $0b, $09, $0b, $09, $0a, $0b, $09
$4873.byte$0a, $0b, $0b, $09, $0a, $0a, $0b, $0a
$487B.byte$09, $0b, $09, $09, $0b, $0a, $09, $09
$4883.byte$0b, $0a, $0b, $09, $09, $0b, $0a, $0b
$488B.byte$00, $01, $0c, $02, $00, $01, $07, $00
$4893.byte$01, $0c, $00, $02, $00, $01, $0c, $01
$489B.byte$00, $01, $00, $08, $01, $0c, $00, $00
$48A3.byte$0c, $01, $07, $08, $00, $01, $0c, $00
$48AB.byte$00, $0c, $00, $0c, $01, $0c, $02, $07
$48B3.byte$02, $00, $08, $00, $02, $00, $08, $02
$48BB.byte$00, $08, $00, $00, $02, $00, $08, $02
$48C3.byte$00, $08, $01, $00, $00, $02, $00, $00
$48CB.byte$08, $00, $08, $01, $00, $07, $08, $01
$48D3.byte$00, $08, $00, $00, $00, $01, $00
; Outer corner tile indices (15 entries). Values $26-$28 for top-right wall corners.
$48DAouter_corner_tiles.byte$08, $27, $26, $26, $28, $27, $26, $26; x-ref: $4F06
$48E2.byte$28, $28, $26, $27, $26, $28, $26
; Inner corner tile indices (16 entries). Values $29-$2B for wall inner corners.
$48E9inner_corner_tiles.byte$27, $2a, $29, $2b, $2a, $29, $29, $2a; x-ref: $4F14
$48F1.byte$2a, $2b, $29, $2b, $2a, $29, $2b, $2a
; World color palette (4 entries, one per world). Stored to $3F9F during level decompression.
$48F9world_color_0.byteVicIIColors.BROWN; brown ; x-ref: $49FC
$48FA.byteVicIIColors.GREEN; green
$48FB.byteVicIIColors.BLUE; blue
$48FC.byteVicIIColors.GREY; grey
; World color palette (4 entries, one per world). Stored to $3FA0 during level decompression.
$48FDworld_color_1.byteVicIIColors.ORANGE; orange ; x-ref: $4A02
$48FE.byteVicIIColors.LIGHT_GREEN; light green
$48FF.byteVicIIColors.LIGHT_BLUE; light blue
$4900.byteVicIIColors.LIGHT_GREY; grey
; World color palette (4 entries, one per world). Stored to $3FA1 during level decompression.
$4901world_color_2.byteVicIIColors.GREEN; green ; x-ref: $4A08
$4902.byteVicIIColors.YELLOW; yellow
$4903.byteVicIIColors.GREY; grey
$4904.byteVicIIColors.LIGHT_GREEN; light green
; World color palette (4 entries, one per world). Stored to $3FA2 during level decompression.
$4905world_color_3.byteVicIIColors.BLUE; blue ; x-ref: $4A0E
$4906.byteVicIIColors.RED; red
$4907.byteVicIIColors.PURPLE; purple
$4908.byteVicIIColors.GREEN; green
; Level map row pointer table (low bytes). 96 entries indexed by level row. Points into $E800-$EF00 tile map buffer. Each row offset = 18 ($12) bytes.
$4909level_map_ptr_lo.byte$b8, $ca, $dc, $ee, $00, $12, $24, $36; x-ref: $722B, $7772, $85F9, $875D
$4911.byte$48, $5a, $6c, $7e, $90, $a2, $b4, $c6
$4919.byte$d8, $ea, $fc, $0e, $20, $32, $44, $56
$4921.byte$68, $7a, $8c, $9e, $b0, $c2, $d4, $e6
$4929.byte$f8, $0a, $1c, $2e, $40, $52, $64, $76
$4931.byte$88, $9a, $ac, $be, $d0, $e2, $f4, $06
$4939.byte$18, $2a, $3c, $4e, $60, $72, $84, $96
$4941.byte$a8, $ba, $cc, $de, $f0, $02, $14, $26
$4949.byte$38, $4a, $5c, $6e, $80, $92, $a4, $b6
$4951.byte$c8, $da, $ec, $fe, $10, $22, $34, $46
$4959.byte$58, $6a, $7c, $8e, $a0, $b2, $c4, $d6
$4961.byte$e8, $fa, $0c, $1e, $30, $42, $54, $66
; Level map row pointer table (high bytes). 96 entries paired with level_map_ptr_lo. Points into $E800-$EF00 tile map buffer.
$4969level_map_ptr_hi.byte$e8, $e8, $e8, $e8; x-ref: $7230, $7777, $85FE, $8762
$496D.fill15, $e9
$497C.fill14, $ea
$498A.fill14, $eb
$4998.fill14, $ec
$49A6.fill15, $ed
$49B5.fill14, $ee
$49C3.byte$ef, $ef, $ef, $ef, $ef, $ef
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Decompresses the cave/level map data into the tile map buffer.
; Reads compressed tile data from $1300, decodes run-length and skip markers,
; and writes 96 rows x 18 columns of tile indices into the map buffer at $E000.
; Also initializes the row-offset table at $EF78 and sets the level color
; palette from the current world index ($60D9).
;
; Inputs: a_60D9 (current world index, bits 0-1 select color palette)
; Compressed map data at $1300
; Outputs: Tile map at $E000, row offsets at $EF78, colors at $3F9F-$3FA2
; Side Effects: Modifies zero-page $02-$05, $1D-$1F, $FB-$FC
; Calls s_4AF0 to post-process the decompressed map
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$49C9a2 5fdecompress_cave_mapldx#$5f; 96 rows to process ; x-ref: $615A
$49CBa9 b8lda#$b8; ptr $FB/$FC = $E8B8 (tile map buffer)
$49CD85 fbstazp_ptr_src_lo
$49CFa9 e8lda#$e8
$49D185 fcstazp_ptr_src_hi
$49D3a0 11b_49D3ldy#$11; 18 columns per row ; x-ref: $49EA
$49D5a9 fflda#$ff; $FF = empty/default tile
$49D791 fbb_49D7sta(zp_ptr_src_lo),y; fill row with $FF ; x-ref: $49DA
$49D988dey
$49DA10 fbbplb_49D7
$49DCa5 fbldazp_ptr_src_lo; advance ptr by 18 to next row
$49DE18clc
$49DF69 12adc#$12
$49E185 fbstazp_ptr_src_lo
$49E3a5 fcldazp_ptr_src_hi
$49E569 00adc#$00
$49E785 fcstazp_ptr_src_hi
$49E9cadex; loop until all 96 rows cleared
$49EA10 e7bplb_49D3
$49ECa2 5fldx#$5f; clear 96 row-offset entries
$49EEa9 00lda#$00; $00 = initial offset
$49F09d 78 efb_49F0stascreen_slot_table,x; Clear all 96 room slots to 0 (unvisited) ; x-ref: $49F4
$49F3cadex
$49F410 fabplb_49F0
$49F6ad d9 60ldacurrent_level; world index (0-3)
$49F929 03and#$03; mask to 2 bits (4 worlds)
$49FBaatax
$49FCbd f9 48ldaworld_color_0,x; Load world color 0 by world index; brown
$49FF8d 9f 3fstalevel_color_top; store as level colors $3F9F-$3FA2
$4A02bd fd 48ldaworld_color_1,x; Load world color 1 by world index; orange
$4A058d a0 3fstalevel_color_bottom; Color value for the bottom section of the playfield
$4A08bd 01 49ldaworld_color_2,x; Load world color 2 by world index; green
$4A0B8d a1 3fstalevel_color_border; Border/background color 3 for the level
$4A0Ebd 05 49ldaworld_color_3,x; Load world color 3 by world index; blue
$4A118d a2 3fstalevel_color_multicolor; VIC-II ECM multicolor value ($D023) for the level
$4A14a9 00lda#$00; source ptr $02/$03 = $1300 (compressed data)
$4A1685 02stazp_work0
$4A18a9 13lda#$13
$4A1A85 03stazp_work1
$4A1Ca9 00lda#$00; dest ptr $04/$05 = $E000 (tile map)
$4A1E85 04stazp_work2
$4A20a9 e0lda#$e0
$4A2285 05stazp_work3
$4A24a9 00lda#$00; row counter = 0
$4A2685 1dstazp_ptr_map_hi
$4A28a0 00ldy#$00
$4A2Aa5 02b_4A2Aldazp_work0; -- begin row decompression loop -- ; x-ref: $4A60
$4A2C91 04sta(zp_work2),y; write low byte of source to dest (tile index)
$4A2Ee6 04inczp_work2; advance dest pointer
$4A30d0 02bneb_4A34
$4A32e6 05inczp_work3
$4A34a5 03b_4A34ldazp_work1; write high byte of source to dest ; x-ref: $4A30
$4A3691 04sta(zp_work2),y
$4A38e6 04inczp_work2
$4A3Ad0 02bneb_4A3E
$4A3Ce6 05inczp_work3
$4A3Eb1 02b_4A3Elda(zp_work0),y; check for $FF = end-of-row marker ; x-ref: $4A3A
$4A40c9 ffcmp#$ff
$4A42d0 09bneb_4A4D
$4A44e6 02inczp_work0
$4A46d0 02bneb_4A4A
$4A48e6 03inczp_work1
$4A4A4c 5a 4ab_4A4Ajmpj_4A5A; x-ref: $4A46
$4A4Da9 00b_4A4Dlda#$00; column counter = 0 ; x-ref: $4A42
$4A4F85 1estazp_temp_x
$4A5120 70 4ab_4A51jsrdecode_tile_entry; decode one tile entry ; x-ref: $4A58
$4A54a5 1eldazp_temp_x; check if 18 columns done
$4A56c9 11cmp#$11
$4A58d0 f7bneb_4A51
$4A5Ae6 1dj_4A5Ainczp_ptr_map_hi; next row ; x-ref: $4A4A
$4A5Ca5 1dldazp_ptr_map_hi; all 96 rows done?
$4A5Ec9 60cmp#$60
$4A60d0 c8bneb_4A2A
$4A62a9 fflda#$ff; signal: decompression active
$4A648d a5 3fstamap_decompressing
$4A6720 f0 4ajsrrender_level_screen; post-process decompressed map
$4A6Aa9 00lda#$00; signal: decompression done
$4A6C8d a5 3fstamap_decompressing
$4A6F60rts
; Decodes one compressed tile entry from the source stream.
; $F5 = end-of-row, $E4+ = multi-skip, $D4+ = single-skip with column advance,
; < $D4 = raw tile data processed by s_4AB6.
$4A70b1 02decode_tile_entrylda(zp_work0),y; Read next byte from map data stream ; x-ref: $4A51
$4A72c9 f5cmp#$f5; $F5 = end-of-row marker?
$4A74d0 0bbneb_4A81
$4A76a9 11lda#$11; set column = 18 (force row end)
$4A7885 1estazp_temp_x; Store end-of-row flag in column counter
$4A7Ae6 02inczp_work0; Advance data pointer low byte
$4A7Cd0 02bner_4A80; Skip high byte increment if no carry
$4A7Ee6 03inczp_work1; Advance data pointer high byte
$4A8060r_4A80rts; x-ref: $4A7C
$4A81c9 e4b_4A81cmp#$e4; >= $E4? multi-column skip ; x-ref: $4A74
$4A8390 0ebccb_4A93
$4A85e9 e3sbc#$e3; skip count = value - $E3
$4A8718clc
$4A8865 1eadczp_temp_x; add to column counter
$4A8A85 1estazp_temp_x; Update column counter
$4A8Ce6 02inczp_work0
$4A8Ed0 02bner_4A92
$4A90e6 03inczp_work1
$4A9260r_4A92rts; x-ref: $4A8E
$4A93c9 d4b_4A93cmp#$d4; >= $D4? single-column skip ; x-ref: $4A83
$4A9590 09bccb_4AA0
$4A97e6 1einczp_temp_x; advance column by 1
$4A99e6 02inczp_work0
$4A9Bd0 02bner_4A9F
$4A9De6 03inczp_work1
$4A9F60r_4A9Frts; x-ref: $4A9B
$4AA0a9 00b_4AA0lda#$00; raw tile data: decode via s_4AB6 ; x-ref: $4A95
$4AA285 1fstazp_temp_y
$4AA420 b6 4ab_4AA4jsrdecode_raw_tile_byte; Process tile through width accumulator ; x-ref: $4AB1
$4AA7e6 02inczp_work0; Advance data pointer past tile byte
$4AA9d0 02bneb_4AAD
$4AABe6 03inczp_work1
$4AADa5 1fb_4AADldazp_temp_y; 40 bytes per tile row? ; x-ref: $4AA9
$4AAFc9 28cmp#$28
$4AB1d0 f1bneb_4AA4; Loop until row width filled
$4AB3e6 1einczp_temp_x; column done
$4AB560rts
; Decodes a raw tile byte from the compressed stream.
; Values $00-$27: add 1 + accumulate into pixel offset.
; Values $28-$4F: add with wrap (subtract $27) into pixel offset.
; Values $50-$77: add with wrap (subtract $4F) into pixel offset.
; Values $78-$C7: set pixel offset = 40 (full row width).
; Value $D3: store current row as special row marker at $3F9E.
; Other >= $C8: increment pixel offset by 1.
$4AB6b1 02decode_raw_tile_bytelda(zp_work0),y; Read byte from RLE data stream ; x-ref: $4AA4
$4AB8c9 28cmp#$28; < $28? range 0: small offset
$4ABAb0 07bcsb_4AC3
$4ABC69 01adc#$01; offset += value + 1
$4ABE65 1fadczp_temp_y; Add to running column count
$4AC085 1fstazp_temp_y; Store updated column count
$4AC260rts
$4AC3c9 50b_4AC3cmp#$50; < $50? range 1: medium offset ; x-ref: $4ABA
$4AC5b0 08bcsb_4ACF
$4AC765 1fadczp_temp_y; offset += value - $27
$4AC938sec
$4ACAe9 27sbc#$27
$4ACC85 1fstazp_temp_y
$4ACE60rts
$4ACFc9 78b_4ACFcmp#$78; < $78? range 2: large offset ; x-ref: $4AC5
$4AD1b0 08bcsb_4ADB
$4AD365 1fadczp_temp_y; offset += value - $4F
$4AD538sec
$4AD6e9 4fsbc#$4f
$4AD885 1fstazp_temp_y
$4ADA60rts
$4ADBc9 c8b_4ADBcmp#$c8; < $C8? set offset = 40 (full width) ; x-ref: $4AD1
$4ADDb0 05bcsb_4AE4
$4ADFa9 28lda#$28; offset = 40 (full row)
$4AE185 1fstazp_temp_y
$4AE360rts
$4AE4c9 d3b_4AE4cmp#$d3; == $D3? special row marker ; x-ref: $4ADD
$4AE6d0 05bneb_4AED
$4AE8a5 1dldazp_ptr_map_hi; store row index as special marker
$4AEA8d 9e 3fstacurrent_room_index; Store room index from decompressor
$4AEDe6 1fb_4AEDinczp_temp_y; default: offset += 1 ; x-ref: $4AE6
$4AEF60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Renders the current level/room screen.
; Uses a_3F9E as the room index, looks up compressed screen data
; from the table at $E000, decompresses RLE tiles into screen RAM
; ($0400-$06A7), copies color data to color RAM ($D800-$DBFE),
; sets up sprite multicolor registers, and enables all sprites.
; If screen data starts with $FF, clears the screen and disables sprites.
;
; Inputs: a_3F9E (room/level index)
; Outputs: Screen RAM ($0400), Color RAM ($D800), sprite config
; Side Effects: Disables then re-enables sprites, calls 8 subsystem
; reset routines, modifies VIC-II registers ($D015,
; $D024, $D025, $D026)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$4AF0a9 ffrender_level_screenlda#$ff; set rendering-in-progress flag ; x-ref: $4A67, $501B, $502E, $5042, $5052
$4AF28d a7 3fstarendering_in_progress; Set rendering_in_progress = $FF (lock)
$4AF5ad c2 77ldahazard_active; OR animation flags from 3 subsystems
$4AF80d 73 7aoratile14_active_flag
$4AFB0d ab 82oratile13_active_flag
$4AFE8d a8 3fstasaved_animation_flags; Save combined animation flags snapshot
$4B0120 0f 6fjsrreset_color_flash; reset subsystem 1
$4B0420 0d 72jsrreset_animated_tiles; reset subsystem 2
$4B0720 c6 77jsrreset_hazard; reset subsystem 3
$4B0A20 17 7ajsrreset_water_animation; reset subsystem 4
$4B0D20 78 7ajsrreset_tile14_state; reset subsystem 5
$4B1020 b0 82jsrreset_tile13_scroll; reset subsystem 6
$4B1320 7b 84jsrreset_raft; reset subsystem 7
$4B1620 e6 85jsrreset_dynamic_tiles; reset subsystem 8
$4B19a9 00lda#$00; clear the screen-changed flag
$4B1B8d a6 3fstascreen_changed_flag
$4B1Ead 9e 3fldacurrent_room_index; Clear screen_changed_flag before render
$4B210aasla
$4B22aatax
$4B23bd 00 e0ldaroom_data_ptrs_lo,x; Load room screen data pointer lo from table; $E000 Reset Code
$4B2685 02stazp_work0
$4B28bd 01 e0ldaroom_data_ptrs_hi,x; Load room screen data pointer hi from table
$4B2B85 03stazp_work1
$4B2Da0 00ldy#$00; read first byte of screen data
$4B2Fb1 02lda(zp_work0),y
$4B31c9 ffcmp#$ff; $FF = empty room marker
$4B33d0 1bbneb_4B50
$4B35a9 00lda#$00; disable all sprites for empty room
$4B378d 15 d0sta$d015; Sprite display Enable
$4B3Aa2 00ldx#$00
$4B3Ca9 00lda#$00
$4B3E9d 00 04b_4B3EstaSCREEN_RAM,x; clear 4 pages of screen RAM ($0400-$06A7) ; x-ref: $4B4D
$4B419d aa 04staSCREEN_RAM_R4C10,x
$4B449d 54 05staSCREEN_RAM_R8C20,x
$4B479d fe 05staSCREEN_RAM_R12C30,x
$4B4Ae8inx
$4B4Be0 aacpx#$aa; loop 170 ($AA) bytes per page
$4B4Dd0 efbneb_4B3E
$4B4F60rts
$4B50a9 c0b_4B50lda#$c0; set dest ptr = $E0C0 (decompressed tile buffer) ; x-ref: $4B33
$4B5285 04stazp_work2
$4B54a9 e0lda#$e0
$4B5685 05stazp_work3
$4B58a9 11lda#$11; 17 rows to decompress
$4B5A85 1estazp_temp_x
$4B5C20 e1 4cb_4B5Cjsrdecode_level_row; decompress one row of RLE data ; x-ref: $4B61
$4B5Fa5 1eldazp_temp_x
$4B61d0 f9bneb_4B5C
$4B6320 17 4ejsrfill_screen_colors; set up level tile attribute lookups
$4B66a9 c0lda#$c0
$4B6885 02stazp_work0
$4B6Aa9 e0lda#$e0
$4B6C85 03stazp_work1
$4B6Ea9 68lda#$68
$4B7085 04stazp_work2
$4B72a9 e3lda#$e3
$4B7485 05stazp_work3
$4B76a9 73lda#$73
$4B7885 06stazp_ptr_aux1_lo
$4B7Aa9 42lda#$42
$4B7C85 07stazp_ptr_aux1_hi
$4B7Ea9 1blda#$1b
$4B8085 08stazp_ptr_aux2_lo
$4B82a9 45lda#$45
$4B8485 09stazp_ptr_aux2_hi
$4B86a9 cblda#$cb
$4B8885 0astazp_ptr_aux3_lo
$4B8Aa9 3flda#$3f
$4B8C85 0bstazp_ptr_aux3_hi
$4B8Ea9 10lda#$10
$4B9085 0cstazp_ptr_aux4_lo
$4B92a9 e6lda#$e6
$4B9485 0dstazp_ptr_aux4_hi
$4B96a2 00ldx#$00; process column right-to-left (39 to 0)
$4B98a0 27j_4B98ldy#$27; x-ref: $4C26
$4B9Ab1 02b_4B9Alda(zp_work0),y; x-ref: $4BD1
$4B9Cf0 2ebeqb_4BCC; tile=0 → use background char
$4B9Ec9 05cmp#$05; tile >= 5 → complex tile handler
$4BA090 06bccb_4BA8
$4BA220 b5 4fjsrdispatch_complex_tile
$4BA54c cc 4bjmpb_4BCC
$4BA8c9 01b_4BA8cmp#$01; tile=1 → use primary char lookup ; x-ref: $4BA0
$4BAAd0 05bneb_4BB1
$4BACb1 06lda(zp_ptr_aux1_lo),y
$4BAE4c ce 4bjmpj_4BCE
$4BB1c9 02b_4BB1cmp#$02; x-ref: $4BAA
$4BB3d0 09bneb_4BBE
$4BB5a9 02lda#$02
$4BB791 0csta(zp_ptr_aux4_lo),y
$4BB9b1 08lda(zp_ptr_aux2_lo),y
$4BBB4c ce 4bjmpj_4BCE
$4BBEc9 03b_4BBEcmp#$03; tile=3 → solid block ($80) ; x-ref: $4BB3
$4BC0d0 05bneb_4BC7
$4BC2a9 80lda#$80
$4BC44c ce 4bjmpj_4BCE
$4BC7b1 08b_4BC7lda(zp_ptr_aux2_lo),y; x-ref: $4BC0
$4BC94c ce 4bjmpj_4BCE
$4BCCb1 0ab_4BCClda(zp_ptr_aux3_lo),y; tile=0: use background fill char ; x-ref: $4B9C, $4BA5
$4BCE91 04j_4BCEsta(zp_work2),y; store char to dest screen buffer ; x-ref: $4BAE, $4BBB, $4BC4, $4BC9
$4BD088dey
$4BD110 c7bplb_4B9A
$4BD3e8inx; advance all 6 source pointers by 40 cols
$4BD4e0 11cpx#$11
$4BD6f0 51beqb_4C29
$4BD8a5 02ldazp_work0
$4BDA18clc
$4BDB69 28adc#$28
$4BDD85 02stazp_work0
$4BDFa5 03ldazp_work1
$4BE169 00adc#$00
$4BE385 03stazp_work1
$4BE5a5 04ldazp_work2
$4BE718clc
$4BE869 28adc#$28
$4BEA85 04stazp_work2
$4BECa5 05ldazp_work3
$4BEE69 00adc#$00
$4BF085 05stazp_work3
$4BF2a5 06ldazp_ptr_aux1_lo
$4BF418clc
$4BF569 28adc#$28
$4BF785 06stazp_ptr_aux1_lo
$4BF9a5 07ldazp_ptr_aux1_hi
$4BFB69 00adc#$00
$4BFD85 07stazp_ptr_aux1_hi
$4BFFa5 08ldazp_ptr_aux2_lo
$4C0118clc
$4C0269 28adc#$28
$4C0485 08stazp_ptr_aux2_lo
$4C06a5 09ldazp_ptr_aux2_hi
$4C0869 00adc#$00
$4C0A85 09stazp_ptr_aux2_hi
$4C0Ca5 0aldazp_ptr_aux3_lo
$4C0E18clc
$4C0F69 28adc#$28
$4C1185 0astazp_ptr_aux3_lo
$4C13a5 0bldazp_ptr_aux3_hi
$4C1569 00adc#$00
$4C1785 0bstazp_ptr_aux3_hi
$4C19a5 0cldazp_ptr_aux4_lo
$4C1B18clc
$4C1C69 28adc#$28
$4C1E85 0cstazp_ptr_aux4_lo
$4C20a5 0dldazp_ptr_aux4_hi
$4C2269 00adc#$00
$4C2485 0dstazp_ptr_aux4_hi
$4C264c 98 4bjmpj_4B98
$4C29a2 11b_4C29ldx#$11; x-ref: $4BD6
$4C2Ba9 fflda#$ff
$4C2D9d 33 00b_4C2Dsta@w zp_spr_y_pos,x; x-ref: $4C31
$4C30cadex
$4C3110 fabplb_4C2D
$4C33ae a8 3fldxsaved_animation_flags; Restore saved_animation_flags after reset
$4C36d0 03bneb_4C3B; jump back for next row
$4C388d 45 00sta@w zp_screen_dirty; conditional: mark screen dirty if no animation
$4C3Ba9 90b_4C3Blda#$90; x-ref: $4C36
$4C3D85 02stazp_work0
$4C3Fa9 e3lda#$e3
$4C4185 03stazp_work1
$4C43a2 0fldx#$0f; if no animation active, mark all rows dirty
$4C45a0 27b_4C45ldy#$27; x-ref: $4C61
$4C47b1 02b_4C47lda(zp_work0),y; x-ref: $4C51
$4C49c9 0dcmp#$0d
$4C4B90 03bccb_4C50; set color source ptr = $E390
$4C4D20 7a 4ejsrdraw_wall_autotile
$4C5088b_4C50dey; x-ref: $4C4B
$4C5110 f4bplb_4C47; 16 rows of color data to process
$4C53a5 02ldazp_work0
$4C5518clc
$4C5669 28adc#$28
$4C5885 02stazp_work0
$4C5Aa5 03ldazp_work1
$4C5C69 00adc#$00
$4C5E85 03stazp_work1
$4C60cadex
$4C61d0 e2bneb_4C45
$4C63a9 00lda#$00; disable all sprites during screen copy
$4C658d 15 d0sta$d015; Sprite display Enable
$4C68a9 fflda#$ff
$4C6A8d 45 00sta@w zp_screen_dirty; mark all rows dirty ($FF)
$4C6Da9 02lda#VicIIColors.RED
$4C6F8d 25 d0sta$d025; sprite multicolor 0 = dark red; Sprite Multi-Color Register 0
$4C72a9 0flda#VicIIColors.LIGHT_GREY
$4C748d 26 d0sta$d026; sprite multicolor 1 = light gray; Sprite Multi-Color Register 1
$4C77a9 06lda#VicIIColors.BLUE
$4C798d 7f 00sta@w zp_spr_color; set player 1 sprite color = blue
$4C7Ca9 07lda#VicIIColors.YELLOW
$4C7E8d 80 00sta@w zp_spr_color_p2; set player 2 sprite color = yellow
$4C81ad a4 3fldascreen_slot_status; check if special bg color mode active
$4C84f0 08beqb_4C8E
$4C86a9 00lda#VicIIColors.BLACK; set background color 3 = black
$4C888d 24 d0sta$d024; Background Color 3
$4C8B20 a4 50jsrreset_sprite_colors
$4C8Ea2 00b_4C8Eldx#$00; x-ref: $4C84
$4C90bd 68 e3b_4C90ldascreen_buf_q1,x; Copy screen char Q1 to $0400 ; x-ref: $4CC3
$4C939d 00 04staSCREEN_RAM,x
$4C96bd 10 e6ldacolor_buf_q1,x; Copy color Q1 to $D800
$4C999d 00 d8staCOLOR_RAM,x; Copy decompressed color to color RAM (1st quarter)
$4C9Cbd 12 e4ldascreen_buf_q2,x; Copy screen char Q2 to $04AA
$4C9F9d aa 04staSCREEN_RAM_R4C10,x
$4CA2bd ba e6ldacolor_buf_q2,x; Copy color Q2 to $D8AA
$4CA59d aa d8staCOLOR_RAM_R4C10,x; Copy decompressed color (2nd quarter)
$4CA8bd bc e4ldascreen_buf_q3,x; Copy screen char Q3 to $0554
$4CAB9d 54 05staSCREEN_RAM_R8C20,x
$4CAEbd 64 e7ldacolor_buf_q3,x; Copy color Q3 to $D954
$4CB19d 54 d9staCOLOR_RAM_R8C20,x; Copy decompressed color (3rd quarter)
$4CB4bd 66 e5ldascreen_buf_q4,x; Copy screen char Q4 to $05FE
$4CB79d fe 05staSCREEN_RAM_R12C30,x
$4CBAbd 0e e8ldacolor_buf_q4,x; Copy color Q4 to $D9FE
$4CBD9d fe d9staCOLOR_RAM_R12C30,x; Copy decompressed color (4th quarter)
$4CC0e8inx; loop 170 bytes (one screen quarter)
$4CC1e0 aacpx#$aa
$4CC3d0 cbbneb_4C90
$4CC520 f4 84jsrdraw_raft_tiles; init sprite positions and params
$4CC820 4c 86jsrdraw_all_dynamic_tiles; init sprite animation state
$4CCBad a4 3fldascreen_slot_status; restore bg color 3 if not special mode
$4CCEd0 06bneb_4CD6
$4CD0ad a1 3fldalevel_color_border; Border/background color 3 for the level
$4CD38d 24 d0sta$d024; Background Color 3
$4CD6a9 ffb_4CD6lda#$ff; enable all 8 sprites ($FF) ; x-ref: $4CCE
$4CD88d 15 d0sta$d015; Sprite display Enable
$4CDBa9 00lda#$00; clear rendering-in-progress flag
$4CDD8d a7 3fstarendering_in_progress; Clear rendering_in_progress (unlock)
$4CE060rts; done
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Decodes one row of RLE-compressed level map data from the source stream
; (zpa_02/03) and writes tile values to the screen buffer (zpa_04/05).
; Handles several command byte ranges:
; $F5 = Clear/init: sets color attributes for row, flags a_3FA6
; $E4..$F4 = Fill N full rows with tile value $01 (solid)
; $D4..$E3 = Copy a predefined row pattern from the lookup table (f_3FA9/f_3FBA)
; < $D4 = Decode individual tile runs via s_4D75 (RLE sub-decoder)
; Decrements zpa_1E (remaining row counter) for each row consumed.
;
; Inputs: zpa_02/03 = source data pointer, zpa_04/05 = screen dest pointer,
; zpa_1E = remaining rows counter, Y = 0
; Outputs: zpa_02/03 advanced past consumed bytes, zpa_04/05 advanced by
; row(s), zpa_1E decremented
; Side Effects: Writes tile data to screen buffer at $E0C0+,
; may modify color attributes at f_E318/f_E340
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$4CE1b1 02decode_level_rowlda(zp_work0),y; load command byte from source stream ; x-ref: $4B5C
$4CE3c9 f5cmp#$f5; is it $F5 (clear/init row)?
$4CE5d0 1fbneb_4D06; no -> check other commands
$4CE7a2 27ldx#$27; X = 39 (40 columns per row)
$4CE9bd 18 e3b_4CE9ldacolor_attr_row,x; Read color attribute for current column ; x-ref: $4CFB
$4CECc9 01cmp#$01; is it color 1?
$4CEEf0 05beqb_4CF5; yes -> remap to color $04
$4CF0a9 03lda#$03; no -> remap to color $03
$4CF24c f7 4cjmpj_4CF7
$4CF5a9 04b_4CF5lda#$04; x-ref: $4CEE
$4CF79d 40 e3j_4CF7stacolor_attr_remapped,x; Write remapped color to attribute buffer ; x-ref: $4CF2
$4CFAcadex
$4CFB10 ecbplb_4CE9
$4CFDe8inx; X = 0 after loop
$4CFE86 1estxzp_temp_x; clear remaining rows counter
$4D00a9 fflda#$ff; set init flag = $FF
$4D028d a6 3fstascreen_changed_flag
$4D0560rts
$4D06c9 e4b_4D06cmp#$e4; cmd >= $E4? (fill N solid rows) ; x-ref: $4CE5
$4D0890 26bccb_4D30
$4D0Ae9 e4sbc#$e4; X = number of rows to fill - 1
$4D0Caatax
$4D0Da0 27b_4D0Dldy#$27; -- fill one row loop -- ; x-ref: $4D27
$4D0Fa9 01lda#$01; fill value = $01 (solid tile)
$4D1191 04b_4D11sta(zp_work2),y; write to 40 screen columns ; x-ref: $4D14
$4D1388dey
$4D1410 fbbplb_4D11
$4D16c8iny
$4D17a5 04ldazp_work2; advance dest ptr by 40 cols ($28)
$4D1918clc
$4D1A69 28adc#$28
$4D1C85 04stazp_work2
$4D1Ea5 05ldazp_work3
$4D2069 00adc#$00
$4D2285 05stazp_work3
$4D24c6 1edeczp_temp_x; decrement remaining rows
$4D26cadex; next row to fill
$4D2710 e4bplb_4D0D
$4D29e6 02inczp_work0; advance source data pointer
$4D2Bd0 02bner_4D2F
$4D2De6 03inczp_work1
$4D2F60r_4D2Frts; x-ref: $4D2B
$4D30c9 d4b_4D30cmp#$d4; cmd >= $D4? (copy predefined row pattern) ; x-ref: $4D08
$4D3290 2dbccb_4D61
$4D34e9 d4sbc#$d4; X = row pattern index
$4D36aatax
$4D37bd a9 3fldarow_pattern_ptrs_lo,x; Load row pattern pointer lo
$4D3A85 06stazp_ptr_aux1_lo
$4D3Cbd ba 3fldarow_pattern_ptrs_hi,x; Load row pattern pointer hi
$4D3F85 07stazp_ptr_aux1_hi
$4D41a0 27ldy#$27; Y = 39 (copy 40 bytes)
$4D43b1 06b_4D43lda(zp_ptr_aux1_lo),y; copy row pattern byte to screen ; x-ref: $4D48
$4D4591 04sta(zp_work2),y
$4D4788dey
$4D4810 f9bplb_4D43
$4D4Ac8iny
$4D4Ba5 04ldazp_work2; advance dest ptr by 40 cols ($28)
$4D4D18clc
$4D4E69 28adc#$28
$4D5085 04stazp_work2
$4D52a5 05ldazp_work3
$4D5469 00adc#$00
$4D5685 05stazp_work3
$4D58c6 1edeczp_temp_x; decrement remaining rows
$4D5Ae6 02inczp_work0; advance source data pointer
$4D5Cd0 02bner_4D60
$4D5Ee6 03inczp_work1
$4D6060r_4D60rts; x-ref: $4D5C
$4D61a9 28b_4D61lda#$28; default: decode tile runs within row ; x-ref: $4D32
$4D6385 1fstazp_temp_y; zpa_1F = 40 columns remaining in row
$4D6520 75 4db_4D65jsrdecode_rle_byte; decode next tile run via s_4D75 ; x-ref: $4D70
$4D68e6 02inczp_work0; advance source data pointer
$4D6Ad0 02bneb_4D6E
$4D6Ce6 03inczp_work1
$4D6Ea5 1fb_4D6Eldazp_temp_y; x-ref: $4D6A
$4D70d0 f3bneb_4D65
$4D72c6 1edeczp_temp_x; decrement remaining rows
$4D7460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Decodes a single RLE-compressed byte from the source stream and writes
; the expanded output to the destination buffer.
;
; Encoding scheme (value ranges of the compressed byte):
; $00-$27: Write $00 repeated (value+1) times
; $28-$4F: Write $01 repeated (value-$27) times
; $50-$77: Write $02 repeated (value-$4F) times
; $78-$9F: Write $00 (value-$77) times, then fill rest with $01
; $A0-$C7: Write $00 (value-$9F) times, then fill rest with $02
; $C8+: Write literal (value-$C3) once
;
; Inputs: (zpa_02),Y = source data pointer, (zpa_04),Y = dest pointer,
; zpa_1F = remaining columns in row
; Outputs: Destination buffer filled, zpa_04/05 advanced, zpa_1F decremented
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$4D75b1 02decode_rle_bytelda(zp_work0),y; Read compressed byte from source ; x-ref: $4D65
$4D77c9 28cmp#$28; < $28? Range $00-$27: fill with $00
$4D79b0 11bcsb_4D8C
$4D7Baatax; Count = value + 1
$4D7Ca9 00lda#$00; Fill value = $00
$4D7E91 04b_4D7Esta(zp_work2),y; Write fill value to dest ; x-ref: $4D89
$4D80e6 04inczp_work2; Advance dest pointer (lo)
$4D82d0 02bneb_4D86
$4D84e6 05inczp_work3; Advance dest pointer (hi)
$4D86c6 1fb_4D86deczp_temp_y; Decrement remaining columns ; x-ref: $4D82
$4D88cadex
$4D8910 f3bplb_4D7E
$4D8B60rts
$4D8Cc9 50b_4D8Ccmp#$50; < $50? Range $28-$4F: fill with $01 ; x-ref: $4D79
$4D8Eb0 13bcsb_4DA3
$4D90e9 27sbc#$27; Count = value - $27 (carry clear)
$4D92aatax
$4D93a9 01lda#$01; Fill value = $01
$4D9591 04b_4D95sta(zp_work2),y; Write fill value to dest ; x-ref: $4DA0
$4D97e6 04inczp_work2
$4D99d0 02bneb_4D9D
$4D9Be6 05inczp_work3
$4D9Dc6 1fb_4D9Ddeczp_temp_y; x-ref: $4D99
$4D9Fcadex
$4DA010 f3bplb_4D95
$4DA260rts
$4DA3c9 78b_4DA3cmp#$78; < $78? Range $50-$77: fill with $02 ; x-ref: $4D8E
$4DA5b0 13bcsb_4DBA
$4DA7e9 4fsbc#$4f; Count = value - $4F (carry clear)
$4DA9aatax
$4DAAa9 02lda#$02; Fill value = $02
$4DAC91 04b_4DACsta(zp_work2),y; x-ref: $4DB7
$4DAEe6 04inczp_work2
$4DB0d0 02bneb_4DB4
$4DB2e6 05inczp_work3
$4DB4c6 1fb_4DB4deczp_temp_y; x-ref: $4DB0
$4DB6cadex
$4DB710 f3bplb_4DAC
$4DB960rts
$4DBAc9 a0b_4DBAcmp#$a0; < $A0? Range $78-$9F: $00 then $01 ; x-ref: $4DA5
$4DBCb0 24bcsb_4DE2
$4DBEe9 77sbc#$77; Count of $00s = value - $77 (carry clear)
$4DC0aatax
$4DC1a9 00lda#$00; First fill value = $00
$4DC391 04b_4DC3sta(zp_work2),y; x-ref: $4DCE
$4DC5e6 04inczp_work2
$4DC7d0 02bneb_4DCB
$4DC9e6 05inczp_work3
$4DCBc6 1fb_4DCBdeczp_temp_y; x-ref: $4DC7
$4DCDcadex
$4DCE10 f3bplb_4DC3
$4DD0a6 1fldxzp_temp_y; Remaining cols for second fill
$4DD2a9 01lda#$01; Second fill value = $01
$4DD491 04b_4DD4sta(zp_work2),y; x-ref: $4DDD
$4DD6e6 04inczp_work2
$4DD8d0 02bneb_4DDC
$4DDAe6 05inczp_work3
$4DDCcab_4DDCdex; x-ref: $4DD8
$4DDDd0 f5bneb_4DD4
$4DDF86 1fstxzp_temp_y; Clear remaining columns (done)
$4DE160rts
$4DE2c9 c8b_4DE2cmp#$c8; < $C8? Range $A0-$C7: $00 then $02 ; x-ref: $4DBC
$4DE4b0 24bcsb_4E0A
$4DE6e9 9fsbc#$9f; Count of $00s = value - $9F (carry clear)
$4DE8aatax
$4DE9a9 00lda#$00; First fill value = $00
$4DEB91 04b_4DEBsta(zp_work2),y; x-ref: $4DF6
$4DEDe6 04inczp_work2
$4DEFd0 02bneb_4DF3
$4DF1e6 05inczp_work3
$4DF3c6 1fb_4DF3deczp_temp_y; x-ref: $4DEF
$4DF5cadex
$4DF610 f3bplb_4DEB
$4DF8a6 1fldxzp_temp_y; Remaining cols for second fill
$4DFAa9 02lda#$02; Second fill value = $02
$4DFC91 04b_4DFCsta(zp_work2),y; x-ref: $4E05
$4DFEe6 04inczp_work2
$4E00d0 02bneb_4E04
$4E02e6 05inczp_work3
$4E04cab_4E04dex; x-ref: $4E00
$4E05d0 f5bneb_4DFC
$4E0786 1fstxzp_temp_y; Clear remaining columns (done)
$4E0960rts
$4E0Ae9 c3b_4E0Asbc#$c3; Range $C8+: literal value - $C3 ; x-ref: $4DE4
$4E0C91 04sta(zp_work2),y; Write literal to dest
$4E0Ee6 04inczp_work2
$4E10d0 02bneb_4E14
$4E12e6 05inczp_work3
$4E14c6 1fb_4E14deczp_temp_y; Decrement remaining columns ; x-ref: $4E10
$4E1660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Fills the screen color buffer based on the current screen index.
; If the screen slot in f_EF78 is empty (zero), fills the entire playfield
; color buffer with the top/bottom color values from a_3F9F/a_3FA0.
; If the slot is non-empty, fills only the first and last rows with color,
; and clears the middle portion of the buffer.
;
; Inputs: a_3F9E (screen index), a_3F9F (top color), a_3FA0 (bottom color), a_3FA1 (border color)
; Outputs: a_3FA3, a_3FA4, a_85E5 (updated as side effects)
; Side Effects: Fills color buffer at $E610-$E88F
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$4E17ae 9e 3ffill_screen_colorsldxcurrent_room_index; Look up screen data using room index ; x-ref: $4B63
$4E1Abd 78 efldascreen_slot_table,x; Load slot status for current room (0=empty, $FF=occupied)
$4E1D8d a4 3fstascreen_slot_status; Store slot status
$4E20d0 2cbneb_4E4E; Branch if slot is occupied
$4E22ad a1 3fldalevel_color_border; --- Empty slot: full fill ---; Border/background color 3 for the level
$4E258d a3 3fstaactive_bg_color; Copy border color to a_3FA3
$4E28a9 0flda#VicIIColors.LIGHT_GREY; light gray
$4E2A8d e5 85stadtile_color; Set tile color
$4E2Da2 00ldx#$00
$4E2Fad 9f 3fldalevel_color_top; A = top color value
$4E329d 10 e6b_4E32stacolor_buf_q1,x; Fill color buf Q1 (256 bytes) with top color ; x-ref: $4E36
$4E35e8inx
$4E36d0 fabneb_4E32
$4E389d 10 e7b_4E38stacolor_buf_0x100,x; Fill color buf mid (184 bytes) with top color ; x-ref: $4E3E
$4E3Be8inx
$4E3Ce0 b8cpx#$b8
$4E3Ed0 f8bneb_4E38
$4E40a2 00ldx#$00
$4E42ad a0 3fldalevel_color_bottom; A = bottom color value; Color value for the bottom section of the playfield
$4E459d c8 e7b_4E45stacolor_buf_0x1b8,x; Fill color buf bottom (240 bytes) with bottom color ; x-ref: $4E4B
$4E48e8inx
$4E49e0 f0cpx#$f0
$4E4Bd0 f8bneb_4E45
$4E4D60rts
$4E4Ea2 27b_4E4Eldx#$27; --- Occupied slot: partial fill --- ; x-ref: $4E20
$4E50ad 9f 3fb_4E50ldalevel_color_top; Fill first 40 cols with top color ; x-ref: $4E5D
$4E539d 10 e6stacolor_buf_q1,x; Fill first 40 cols with top color (occupied slot)
$4E56ad a0 3fldalevel_color_bottom; Fill last 40 cols with bottom color; Color value for the bottom section of the playfield
$4E599d 90 e8stacolor_buf_last_row,x; Fill last 40 cols with bottom color (occupied slot)
$4E5Ccadex
$4E5D10 f1bplb_4E50
$4E5Fe8inx
$4E60a9 00lda#VicIIColors.BLACK
$4E628d a3 3fstaactive_bg_color; Clear a_3FA3
$4E658d e5 85stadtile_color; Clear tile color
$4E689d 38 e6b_4E68stacolor_buf_0x28,x; Clear mid-screen color buf (256 bytes) ; x-ref: $4E6F
$4E6B9d 38 e7stacolor_buf_mid_clear,x; Clear mid-screen color buf cont. (256 bytes)
$4E6Ee8inx
$4E6Fd0 f7bneb_4E68
$4E719d 38 e8b_4E71stacolor_buf_0x228,x; Clear bottom color buf (88 bytes) ; x-ref: $4E77
$4E74e8inx
$4E75e0 58cpx#$58
$4E77d0 f8bneb_4E71
$4E7960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Selects and draws the correct wall tile at the current screen position
; based on neighboring cells. Builds a 4-bit adjacency mask by sampling
; four diagonal neighbors (above-left, above-right, below-left, below-right)
; and uses the mask to pick the appropriate edge/corner/join tile from
; lookup tables. Also draws background or wall decoration tiles into
; adjacent rows when the top or bottom neighbor is absent.
;
; Inputs: zpa_02/03 = pointer to current screen row, X = tile index, Y = column offset
; Outputs: Tile written to screen via (zpa_02),Y; Y may be adjusted
; Side Effects: Writes to screen memory, modifies color RAM via a_3FA3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$4E7A86 fddraw_wall_autotilestxzp_ptr_dst_lo; Save tile index and column ; x-ref: $4C4D
$4E7C84 festyzp_ptr_dst_hi
$4E7Ea5 02ldazp_work0; Compute ptr to row above: (zpa_02) - 40
$4E8038sec
$4E81e9 28sbc#$28
$4E8385 fbstazp_ptr_src_lo; Store row-above pointer in a_00FB/FC
$4E85a5 03ldazp_work1
$4E87e9 00sbc#$00
$4E8985 fcstazp_ptr_src_hi
$4E8Ba2 00ldx#$00; Init neighbor mask = 0
$4E8Db1 fblda(zp_ptr_src_lo),y; Read cell above-left
$4E8Fc9 0dcmp#$0d; Is it a wall tile (>= $0D)?
$4E9190 02bccb_4E95
$4E93a2 08ldx#$08; Set bit 3: above-left is wall
$4E9598b_4E95tya; Y += 39 to reach above-right cell ; x-ref: $4E91
$4E9618clc
$4E9769 27adc#$27
$4E99a8tay
$4E9Ab1 fblda(zp_ptr_src_lo),y; Read cell above-right
$4E9Cc9 0dcmp#$0d
$4E9E90 04bccb_4EA4; Set bit 2: above-right is wall
$4EA08atxa
$4EA109 04ora#$04
$4EA3aatax
$4EA4c8b_4EA4iny; Y += 2 to reach below-left cell ; x-ref: $4E9E
$4EA5c8iny
$4EA6b1 fblda(zp_ptr_src_lo),y; Read cell below-left
$4EA8c9 0dcmp#$0d
$4EAA90 04bccb_4EB0; Set bit 1: below-left is wall
$4EAC8atxa
$4EAD09 02ora#$02
$4EAFaatax
$4EB098b_4EB0tya; Y += 39 to reach below-right cell ; x-ref: $4EAA
$4EB118clc
$4EB269 27adc#$27
$4EB4a8tay
$4EB5b1 fblda(zp_ptr_src_lo),y; Read cell below-right
$4EB7c9 0dcmp#$0d
$4EB990 01bccb_4EBC; Set bit 0: below-right is wall
$4EBBe8inx
$4EBC86 ffb_4EBCstxzp_temp; Store completed 4-bit neighbor mask ; x-ref: $4EB9
$4EBEa6 fdldxzp_ptr_dst_lo; Restore tile index and column
$4EC0a4 feldyzp_ptr_dst_hi
$4EC2c0 00cpy#$00; Skip if at left edge (col 0)
$4EC4f0 54beqb_4F1A
$4EC6c0 27cpy#$27; Skip if at right edge (col 39)
$4EC8f0 50beqb_4F1A
$4ECAb1 02lda(zp_work0),y; Read current cell value
$4ECCc9 1acmp#$1a
$4ECE90 4abccb_4F1A; Skip if cell < $1A (not a processable wall)
$4ED0a5 ffldazp_temp; Test bits 2-3 (above neighbors)
$4ED229 0cand#$0c
$4ED4d0 07bneb_4EDD
$4ED6a9 2clda#$2c; No above neighbors: horizontal edge tile $2C
$4ED891 02sta(zp_work0),y; Write tile and adjust Y left by 2
$4EDA88dey
$4EDB88dey
$4EDC60rts
$4EDDa5 ffb_4EDDldazp_temp; Test bits 1,3 (left-side neighbors) ; x-ref: $4ED4
$4EDF29 0aand#$0a
$4EE1d0 05bneb_4EE8
$4EE3a9 2dlda#$2d; Left-only neighbors: vertical edge tile $2D
$4EE591 02sta(zp_work0),y
$4EE760rts
$4EE8a5 ffb_4EE8ldazp_temp; Test bits 0,2 (right-side neighbors) ; x-ref: $4EE1
$4EEA29 05and#$05
$4EECd0 07bneb_4EF5
$4EEEa9 2elda#$2e; Right-only neighbors: vertical edge tile $2E
$4EF091 02sta(zp_work0),y
$4EF288dey
$4EF388dey; Test bits 0-1 (below neighbors)
$4EF460rts
$4EF5a5 ffb_4EF5ldazp_temp; x-ref: $4EEC
$4EF729 03and#$03
$4EF9d0 05bneb_4F00
$4EFBa9 2flda#$2f; Bottom-only: use inner corner tile from f_48DA,X
$4EFD91 02sta(zp_work0),y
$4EFF60rts
$4F00a5 ffb_4F00ldazp_temp; x-ref: $4EF9
$4F0229 04and#$04
$4F04d0 08bneb_4F0E
$4F06bd da 48ldaouter_corner_tiles,x; Load outer corner tile for top-right
$4F0991 02sta(zp_work0),y
$4F0B88dey
$4F0C88dey
$4F0D60rts
$4F0Ea5 ffb_4F0Eldazp_temp; x-ref: $4F04
$4F1029 02and#$02
$4F12d0 06bneb_4F1A
$4F14bd e9 48ldainner_corner_tiles,x; Load inner corner tile
$4F1791 02sta(zp_work0),y
$4F1960rts
$4F1Aa5 ffb_4F1Aldazp_temp; Bit 3 clear: no wall above — draw background above ; x-ref: $4EC4, $4EC8, $4ECE, $4F12
$4F1C29 08and#$08
$4F1Ed0 47bneb_4F67
$4F2084 ffstyzp_temp
$4F22a5 02ldazp_work0; Compute ptr 2 rows above current row
$4F2438sec
$4F25e9 50sbc#$50
$4F2785 fbstazp_ptr_src_lo
$4F29a5 03ldazp_work1
$4F2Be9 00sbc#$00
$4F2D85 fcstazp_ptr_src_hi
$4F2Fa5 fbldazp_ptr_src_lo
$4F3118clc
$4F3269 a8adc#$a8; Compute color RAM ptr (+$02A8 offset)
$4F3485 fdstazp_ptr_dst_lo
$4F36a5 fcldazp_ptr_src_hi
$4F3869 02adc#$02
$4F3A85 festazp_ptr_dst_hi
$4F3Cb9 c3 47ldabg_tile_table,y; Fill background tile from ceiling table
$4F3F91 fbsta(zp_ptr_src_lo),y
$4F41ad a3 3fldaactive_bg_color; Set color from a_3FA3
$4F4491 fdsta(zp_ptr_dst_lo),y
$4F4698tya
$4F4718clc
$4F4869 28adc#$28; Advance Y by 40 to next row
$4F4Aa8tay
$4F4Bb9 c3 47ldabg_tile_table,y; Draw background tile in row above
$4F4E91 fbsta(zp_ptr_src_lo),y
$4F50ad a3 3fldaactive_bg_color
$4F5391 fdsta(zp_ptr_dst_lo),y
$4F5598tya
$4F5669 28adc#$28
$4F58a8tay
$4F59b1 fblda(zp_ptr_src_lo),y; Check if 3rd row above has wall tile
$4F5Bc9 1acmp#$1a
$4F5D90 05bccb_4F64
$4F5Fb9 c3 47ldabg_tile_table,y; If not wall, also draw background there
$4F6291 fbsta(zp_ptr_src_lo),y
$4F64a4 ffb_4F64ldyzp_temp; x-ref: $4F5D
$4F6660rts
$4F67a5 ffb_4F67ldazp_temp; Bit 0 clear: no wall below — draw wall below ; x-ref: $4F1E
$4F6929 01and#$01
$4F6Bd0 47bner_4FB4
$4F6D84 ffstyzp_temp
$4F6Fa5 02ldazp_work0; Compute color RAM ptr for rows below
$4F7118clc
$4F7269 a8adc#$a8
$4F7485 fdstazp_ptr_dst_lo
$4F76a5 03ldazp_work1
$4F7869 02adc#$02
$4F7A85 festazp_ptr_dst_hi
$4F7Cb1 02lda(zp_work0),y; Check if current cell is wall (>= $1A)
$4F7Ec9 1acmp#$1a
$4F8090 05bccb_4F87
$4F82b9 3b 48ldawall_tile_table,y; Draw wall decoration tile below edge
$4F8591 02sta(zp_work0),y
$4F8798b_4F87tya; Advance to next row (+40) ; x-ref: $4F80
$4F8818clc
$4F8969 28adc#$28
$4F8Ba8tay
$4F8Cb9 3b 48ldawall_tile_table,y; Draw wall tile in 2nd row below
$4F8F91 02sta(zp_work0),y
$4F91ad a3 3fldaactive_bg_color; Set color for row below
$4F9491 fdsta(zp_ptr_dst_lo),y
$4F9698tya
$4F9769 28adc#$28
$4F99a8tay
$4F9Ab9 3b 48ldawall_tile_table,y; Draw wall tile in 3rd row below
$4F9D91 02sta(zp_work0),y
$4F9Fad a3 3fldaactive_bg_color
$4FA291 fdsta(zp_ptr_dst_lo),y
$4FA498tya
$4FA569 28adc#$28
$4FA7a8tay
$4FA8b9 3b 48ldawall_tile_table,y; Draw wall tile in 4th row below
$4FAB91 02sta(zp_work0),y
$4FADad a3 3fldaactive_bg_color
$4FB091 fdsta(zp_ptr_dst_lo),y
$4FB2a4 ffldyzp_temp; Restore column offset and return
$4FB460r_4FB4rts; x-ref: $4F6B
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Dispatches complex tile types (ID >= 5) to their rendering handlers.
; Subtracts 5 from the tile ID and branches to the appropriate handler:
; 0 (tile 5) -> j_85EC with A=0
; 1 (tile 6) -> j_85EC with A=1
; 7 (tile 12) -> j_77CC
; 8 (tile 13) -> j_82B6
; 9 (tile 14) -> j_7A7E
; 10 (tile 15) -> j_8481
; 11 (tile 16) -> j_7B1B (only if a_3FA5 != 0)
; others -> j_7213 (with adjusted tile ID)
;
; Inputs: A = tile ID (>= 5), X/Y = position context
; Outputs: None (tail-calls into specific handlers)
; Side Effects: Jumps to tile-specific rendering routines
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
dispatch_complex_tile
$4FB538sec; normalize tile ID: subtract 5 ; x-ref: $4BA2
$4FB6e9 05sbc#$05
$4FB8d0 05bneb_4FBF; result 0 → tile 5
$4FBAa9 00lda#$00; handler param A=0 for tile 5
$4FBC4c ec 85jmpj_85EC; dispatch tile 5 handler
$4FBFc9 01b_4FBFcmp#$01; result 1 → tile 6 ; x-ref: $4FB8
$4FC1d0 05bneb_4FC8
$4FC3a9 01lda#$01; handler param A=1 for tile 6
$4FC54c ec 85jmpj_85EC; dispatch tile 6 handler
$4FC8c9 07b_4FC8cmp#$07; result 7 → tile 12 ; x-ref: $4FC1
$4FCAd0 03bneb_4FCF
$4FCC4c cc 77jmpsetup_hazard_position; dispatch tile 12 handler
$4FCFc9 08b_4FCFcmp#$08; result 8 → tile 13 ; x-ref: $4FCA
$4FD1d0 03bneb_4FD6
$4FD34c b6 82jmpinit_tile13_scroll; dispatch tile 13 handler
$4FD6c9 09b_4FD6cmp#$09; result 9 → tile 14 ; x-ref: $4FD1
$4FD8d0 03bneb_4FDD
$4FDA4c 7e 7ajmpinit_tile14_params; dispatch tile 14 handler
$4FDDc9 0ab_4FDDcmp#$0a; result 10 → tile 15 ; x-ref: $4FD8
$4FDFd0 03bneb_4FE4
$4FE14c 81 84jmpinit_tile15_anim; dispatch tile 15 handler
$4FE4c9 0bb_4FE4cmp#$0b; result 11 → tile 16 ; x-ref: $4FDF
$4FE6d0 09bneb_4FF1
$4FE8ad a5 3fldamap_decompressing; check if tile 16 handler enabled
$4FEBf0 03beqr_4FF0; skip if flag is zero
$4FED4c 1b 7bjmpinit_hero_entity; dispatch tile 16 handler
$4FF060r_4FF0rts; tile 16 disabled: return ; x-ref: $4FEB
$4FF138b_4FF1sec; all other tiles: subtract 2 more ; x-ref: $4FE6
$4FF2e9 02sbc#$02
$4FF44c 13 72jmpj_7213; dispatch generic animated tile handler
$4FF748j_4FF7pha; x-ref: $7DFF, $7E21, $7EC6, $7ED0
$4FF8ad 01 6fldaexplosion_state
$4FFBc9 01cmp#$01
$4FFDd0 06bneb_5005
$4FFF20 87 6fjsrinit_explosion
$50024c 09 50jmpj_5009
$5005c9 02b_5005cmp#$02; x-ref: $4FFD
$5007d0 03bneb_500C
$500920 a2 6fj_5009jsrcheck_entity_explosion_hit; x-ref: $5002
$500C68b_500Cpla; x-ref: $5007
$500Dc9 01cmp#$01
$500Fd0 0dbneb_501E
$5011ad 9e 3fldacurrent_room_index
$501429 07and#$07
$5016f0 3dbeqr_5055
$5018ce 9e 3fdeccurrent_room_index
$501B4c f0 4ajmprender_level_screen
$501Ec9 02b_501Ecmp#$02; x-ref: $500F
$5020d0 0fbneb_5031
$5022ad 9e 3fldacurrent_room_index
$502529 07and#$07
$5027c9 07cmp#$07
$5029f0 2abeqr_5055
$502Bee 9e 3finccurrent_room_index
$502E4c f0 4ajmprender_level_screen
$5031c9 03b_5031cmp#$03; x-ref: $5020
$5033d0 10bneb_5045
$5035ad 9e 3fldacurrent_room_index
$5038c9 08cmp#$08
$503A90 19bccr_5055
$503C38sec
$503De9 08sbc#$08
$503F8d 9e 3fstacurrent_room_index
$50424c f0 4ajmprender_level_screen
$5045ad 9e 3fb_5045ldacurrent_room_index; x-ref: $5033
$5048c9 58cmp#$58
$504Ab0 09bcsr_5055
$504C18clc
$504D69 08adc#$08
$504F8d 9e 3fstacurrent_room_index
$50524c f0 4ajmprender_level_screen
$505560r_5055rts; x-ref: $5016, $5029, $503A, $504A
$5056a9 ffj_5056lda#$ff; x-ref: $1EC7, $1ED2, $1F12, $1F1D
$50588d a4 3fstascreen_slot_status
$505Bae 9e 3fldxcurrent_room_index
$505E9d 78 efstascreen_slot_table,x; Mark current room slot as occupied ($FF)
$5061a9 00lda#VicIIColors.BLACK
$50638d a3 3fstaactive_bg_color
$506620 a4 50jsrreset_sprite_colors
$5069ad a3 3fj_5069ldaactive_bg_color; x-ref: $50C6
$506C8d 24 d0sta$d024; Background Color 3
$506Fa2 00ldx#$00
$5071bd 28 d8b_5071ldaCOLOR_RAM_R1C0,x; Read color RAM row 1 for color filter ; x-ref: $50A1
$507429 0fand#$0f
$5076c9 02cmp#$02
$5078f0 06beqb_5080
$507Aad a3 3fldaactive_bg_color
$507D9d 28 d8staCOLOR_RAM_R1C0,x; Overwrite non-red colors in row 1
$5080bd f0 d8b_5080ldaCOLOR_RAM_R6C0,x; Read color RAM row 6 for color filter ; x-ref: $5078
$508329 0fand#$0f
$5085c9 02cmp#$02
$5087f0 06beqb_508F
$5089ad a3 3fldaactive_bg_color
$508C9d f0 d8staCOLOR_RAM_R6C0,x; Overwrite non-red colors in row 6
$508Fbd b8 d9b_508FldaCOLOR_RAM_R11C0,x; Read color RAM row 11 for color filter ; x-ref: $5087
$509229 0fand#$0f
$5094c9 02cmp#$02
$5096f0 06beqb_509E
$5098ad a3 3fldaactive_bg_color
$509B9d b8 d9staCOLOR_RAM_R11C0,x; Overwrite non-red colors in row 11
$509Ee8b_509Einx; x-ref: $5096
$509Fe0 c8cpx#$c8
$50A1d0 cebneb_5071
$50A360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets all sprite colors to dark gray (#$0B).
; Sets both VIC-II sprite multicolor registers, individual sprite color
; variables ($7F, $80, $91), and the 15-entry sprite color table at $710E
; to a uniform value, effectively neutralizing sprite colors.
;
; Inputs: None
; Outputs: None
; Side Effects: VIC-II $D025/$D026 set to #$0B; ZP $7F/$80/$91 and
; f_710E[0..14] filled with #$0B
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$50A4a9 0breset_sprite_colorslda#VicIIColors.DARK_GREY; dark gray ; x-ref: $4C8B, $5066
$50A68d 25 d0sta$d025; sprite multicolor 0 = dark gray; Sprite Multi-Color Register 0
$50A98d 26 d0sta$d026; sprite multicolor 1 = dark gray; Sprite Multi-Color Register 1
$50AC8d 7f 00sta@w zp_spr_color; player 1 sprite color = dark gray
$50AF8d 80 00sta@w zp_spr_color_p2; player 2 sprite color = dark gray
$50B28d 91 00sta@w zp_spr_color_extra; extra sprite color = dark gray
$50B5a2 0eldx#$0e; 15 entries (0..14)
$50B79d 0e 71b_50B7staentity_anim_speed,x; fill sprite color table with dark gray ; x-ref: $50BB
$50BAcadex
$50BB10 fabplb_50B7; loop until all 15 entries filled
$50BD60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets the active screen color and applies it to color RAM and background
; color 3. Only takes effect if the color update flag (a_3FA4) is non-zero,
; indicating an active screen slot. When active, stores the color passed in A
; into a_3FA3, then fills color RAM ($D828+) with that color (skipping cells
; with color 2) and sets VIC-II background color 3 ($D024).
;
; Inputs: A = new color value to apply
; Outputs: a_3FA3 updated with the new color
; Side Effects: Background color 3 ($D024) and color RAM ($D828-$DA7F) updated
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$50BEae a4 3fset_screen_colorldxscreen_slot_status; Check if color updates are active ; x-ref: $6F43, $7066
$50C1f0 06beqr_50C9; If flag is zero, skip (no active screen)
$50C38d a3 3fstaactive_bg_color; Store new color value
$50C64c 69 50jmpj_5069; Apply color to color RAM and $D024
$50C960r_50C9rts; Nothing to do, return ; x-ref: $50C1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Reads a tile value from the level map at the given coordinates.
; A = X-position (column * 8 + sub-column), $FD/$FE = Y-position (16-bit).
; Divides A by 8 to select a row pointer from the map_row_ptr tables,
; then combines remaining bits with $FD/$FE to form a column offset.
; Returns the tile ID in A, or $00 if coordinates are out of bounds.
;
; Inputs: A = x-position, zpa_FD/zpa_FE = y-position (lo/hi)
; Outputs: A = tile ID at the given map position (0 if out of bounds)
; Side Effects: Overwrites $FB/$FC (zero-page pointer)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$50CAc9 88get_map_tilecmp#$88; X-pos >= 136? Out of bounds ; x-ref: $74EA, $751D, $7D6E, $7D80, $7D92, ...
$50CCb0 23bcsb_50F1; Yes -> return 0
$50CEa4 feldyzp_ptr_dst_hi; Check Y-pos high byte
$50D0f0 06beqb_50D8; High byte = 0 -> skip Y check
$50D2a4 fdldyzp_ptr_dst_lo; Check Y-pos low byte
$50D4c0 40cpy#$40; Y-pos >= 64? Out of bounds
$50D6b0 19bcsb_50F1; Yes -> return 0
$50D84ab_50D8lsra; Divide x-pos by 8 to get row index ; x-ref: $50D0
$50D94alsra
$50DA4alsra
$50DBa8tay; Y = row index
$50DCb9 17 8cldarow_to_screen_lo,y; Row pointer low byte from table
$50DF85 fbstazp_ptr_src_lo; Store in ZP pointer
$50E1b9 28 8cldarow_to_screen_hi,y; Row pointer high byte from table
$50E485 fcstazp_ptr_src_hi; Store in ZP pointer
$50E6a5 fdldazp_ptr_dst_lo; Compute column offset from Y-pos and sub-bits
$50E846 felsrzp_ptr_dst_hi; Combine FE:FD >> 1 with low bits of A
$50EA6arora
$50EB4alsra
$50EC4alsra
$50EDa8tay; Y = column offset into row
$50EEb1 fblda(zp_ptr_src_lo),y; Read tile ID from map
$50F060rts
$50F1a9 00b_50F1lda#$00; Out-of-bounds: return tile 0 (empty) ; x-ref: $50CC, $50D6
$50F360rts
; Player score low byte (BCD). Part of 3-byte score $50F4-$50F6 (range 000000-999999)
$50F4score_lo.byte$00; x-ref: $5101, $513C, $5143, $515D, $5193, ...
; Player score mid byte (BCD). Part of 3-byte score $50F4-$50F6
$50F5score_mid.byte$00; x-ref: $5104, $5146, $514C, $5160, $5189, ...
; Player score high byte (BCD). Part of 3-byte score $50F4-$50F6
$50F6score_hi.byte$00; x-ref: $5107, $514F, $5155, $5163, $517F, ...
; Number of remaining lives/dynamite sticks (0-9). Starts at 4, capped at 9
$50F7lives_count.byte$00; x-ref: $510C, $51BD, $51C8, $51D1, $521A, ...
; Pending score delta low byte (BCD). Added to score on next update_score_and_lives call
$50F8score_delta_lo.byte$00; x-ref: $5111, $512F, $5140, $5168, $51D9, ...
; Pending score delta mid byte (BCD)
$50F9score_delta_mid.byte$00; x-ref: $5114, $5132, $5149, $516B, $51E2, ...
; Pending score delta high byte (BCD)
$50FAscore_delta_hi.byte$00; x-ref: $5117, $5135, $5152, $516E, $51EA, ...
; Extra-life threshold low byte (BCD). Lives awarded every 20000 pts
extra_life_threshold_lo
$50FB.byte$00; x-ref: $511C, $5190, $519C, $51A2
; Extra-life threshold mid byte (BCD)
extra_life_threshold_mid
$50FC.byte$00; x-ref: $5121, $5186, $51A5, $51AA
; Extra-life threshold high byte (BCD)
extra_life_threshold_hi
$50FD.byte$00; x-ref: $5126, $517C, $51AD, $51B2
; $FF = extra lives enabled, $00 = disabled (overflows past 999999 disables)
$50FEextra_life_enabled.byte$00; x-ref: $512B, $5174, $51BA
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the player's score, dynamite supply, pending score delta,
; and extra-life threshold for the start of a new game.
;
; Score ($50F4–$50F6) is zeroed (BCD 000000).
; Dynamite supply ($50F7) is set to 4.
; Pending score delta ($50F8–$50FA) is zeroed.
; Extra-life threshold ($50FB–$50FD) is set to 20000 (BCD $00/$00/$02).
; Extra-life enabled flag ($50FE) is set to $FF (enabled).
;
; Inputs: None
; Outputs: None (state stored in $50F4–$50FE)
; Side Effects: Resets all score and life-related game state
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$50FFa9 00init_score_and_liveslda#$00; Score low byte = 0 (BCD) ; x-ref: $611A
$51018d f4 50stascore_lo; Clear score low
$51048d f5 50stascore_mid; Clear score mid
$51078d f6 50stascore_hi; Clear score high
$510Aa9 04lda#$04; Start with 4 dynamite sticks
$510C8d f7 50stalives_count; Store dynamite count
$510Fa9 00lda#$00; Pending score delta = 0
$51118d f8 50stascore_delta_lo; Clear delta low
$51148d f9 50stascore_delta_mid; Clear delta mid
$51178d fa 50stascore_delta_hi; Clear delta high
$511Aa9 00lda#$00; Extra-life threshold low = 0
$511C8d fb 50staextra_life_threshold_lo
$511Fa9 00lda#$00; Extra-life threshold mid = 0
$51218d fc 50staextra_life_threshold_mid
$5124a9 02lda#$02; Threshold high = $02 → BCD 20000
$51268d fd 50staextra_life_threshold_hi
$5129a9 fflda#$ff; $FF = extra lives enabled
$512B8d fe 50staextra_life_enabled
$512E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Adds pending score delta to the current score (BCD), clamps at 999999,
; redraws the score display, then awards extra lives for every 20000 points
; (up to a maximum of 9 lives) and redraws the lives display.
;
; Inputs: a_50F8-a_50FA (score delta, 3-byte BCD)
; Outputs: a_50F4-a_50F6 (current score), a_50F7 (lives count)
; Side Effects: Redraws score digits and lives icons on screen
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_score_and_lives
$512Fad f8 50ldascore_delta_lo; Check if score delta is non-zero ; x-ref: $61D1
$51320d f9 50orascore_delta_mid
$51350d fa 50orascore_delta_hi
$5138d0 01bneb_513B
$513A60rts; Return if delta is zero (nothing to add)
$513Bf8b_513Bsed; Enter BCD mode for decimal addition ; x-ref: $5138
$513Cad f4 50ldascore_lo; Add delta low byte to score low byte
$513F18clc
$51406d f8 50adcscore_delta_lo
$51438d f4 50stascore_lo; Add delta mid byte to score mid byte
$5146ad f5 50ldascore_mid
$51496d f9 50adcscore_delta_mid
$514C8d f5 50stascore_mid
$514Fad f6 50ldascore_hi
$51526d fa 50adcscore_delta_hi
$51558d f6 50stascore_hi
$5158d8cld; Exit BCD mode
$515990 0bbccb_5166; No overflow? Skip clamping
$515Ba9 99lda#$99; Clamp score to 999999 (BCD $99 per byte)
$515D8d f4 50stascore_lo
$51608d f5 50stascore_mid
$51638d f6 50stascore_hi
$5166a9 00b_5166lda#$00; Clear score delta after adding ; x-ref: $5159
$51688d f8 50stascore_delta_lo
$516B8d f9 50stascore_delta_mid
$516E8d fa 50stascore_delta_hi
$517120 f4 51jsrdraw_score; Render score digits to screen
$5174ad fe 50ldaextra_life_enabled; Check extra life flag
$5177d0 01bneb_517A; Return if extra lives disabled
$517960rts
$517Aa2 00b_517Aldx#$00; X = number of lives to award ; x-ref: $5177
$517Cad fd 50b_517Cldaextra_life_threshold_hi; Compare threshold hi vs score hi ; x-ref: $51B6
$517Fcd f6 50cmpscore_hi
$518290 16bccb_519A; Score > threshold → award a life
$5184d0 37bneb_51BD; Score < threshold → done awarding
$5186ad fc 50ldaextra_life_threshold_mid; Compare threshold mid vs score mid
$5189cd f5 50cmpscore_mid
$518C90 0cbccb_519A
$518Ed0 2dbneb_51BD
$5190ad fb 50ldaextra_life_threshold_lo; Compare threshold lo vs score lo
$5193cd f4 50cmpscore_lo
$519690 02bccb_519A
$5198d0 23bneb_51BD
$519Ae8b_519Ainx; Score >= threshold: count one extra life ; x-ref: $5182, $518C, $5196
$519Bf8sed; Advance threshold by 20000 pts (BCD)
$519Cad fb 50ldaextra_life_threshold_lo
$519F18clc
$51A069 00adc#$00
$51A28d fb 50staextra_life_threshold_lo
$51A5ad fc 50ldaextra_life_threshold_mid
$51A869 00adc#$00
$51AA8d fc 50staextra_life_threshold_mid
$51ADad fd 50ldaextra_life_threshold_hi
$51B069 02adc#$02; Add $02 to threshold high byte (= +20000 BCD)
$51B28d fd 50staextra_life_threshold_hi
$51B5d8cld; Exit BCD mode
$51B690 c4bccb_517C; No overflow? Loop to check again
$51B8a9 00lda#$00; Overflow: disable further extra lives
$51BA8d fe 50staextra_life_enabled
$51BDad f7 50b_51BDldalives_count; Already at max 9 lives? ; x-ref: $5184, $518E, $5198
$51C0c9 09cmp#$09; Yes → return, no more lives to add
$51C2f0 13beqr_51D7
$51C48atxa; X = 0 means no lives earned this time
$51C5f0 10beqr_51D7; No lives earned → return
$51C718clc; Add earned lives to current count
$51C86d f7 50adclives_count
$51CBc9 0acmp#$0a; Cap lives at 9
$51CD90 02bccb_51D1
$51CFa9 09lda#$09; Clamp to max 9 lives
$51D18d f7 50b_51D1stalives_count; Store updated lives count ; x-ref: $51CD
$51D44c 09 52jmpj_5209; Tail-jump: redraw lives display on screen
$51D760r_51D7rts; x-ref: $51C2, $51C5
$51D8f8j_51D8sed; x-ref: $623F, $627A, $7618, $8009, $881B
$51D9ad f8 50ldascore_delta_lo
$51DC18clc
$51DD65 fbadczp_ptr_src_lo
$51DF8d f8 50stascore_delta_lo
$51E2ad f9 50ldascore_delta_mid
$51E565 fcadczp_ptr_src_hi
$51E78d f9 50stascore_delta_mid
$51EAad fa 50ldascore_delta_hi
$51ED65 fdadczp_ptr_dst_lo
$51EF8d fa 50stascore_delta_hi
$51F2d8cld
$51F360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the current score on screen by setting up pointers to the BCD score
; data at $50F4 and the screen destination at $0788, then tail-calling the
; BCD-to-screen rendering routine (j_6432) to display 6 score digits.
;
; Inputs: None (reads BCD score from a_50F4..a_50F6)
; Outputs: Screen memory at $0788 updated with score digits
; Side Effects: Modifies zp pointers $FB/$FC, $FD/$FE, $FF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$51F4a9 f4draw_scorelda#$f4; Source ptr lo = $F4 (-> a_50F4, BCD score data) ; x-ref: $5171, $62F7
$51F685 fbstazp_ptr_src_lo
$51F8a9 50lda#$50; Source ptr hi = $50
$51FA85 fcstazp_ptr_src_hi
$51FCa9 88lda#$88; Dest ptr lo = $88 (-> $0788, screen score area)
$51FE85 fdstazp_ptr_dst_lo
$5200a9 07lda#$07; Dest ptr hi = $07
$520285 festazp_ptr_dst_hi
$5204a2 06ldx#$06; 6 screen chars for 6-digit score
$52064c 32 64jmpj_6432; Tail-call BCD-to-screen renderer
$5209a2 0ej_5209ldx#$0e; x-ref: $51D4, $617C, $62CF
$520Ba9 20lda#$20
$520D9d fc 06b_520DstaSCREEN_RAM_R19C4,x; x-ref: $5218
$52109d fd 06staSCREEN_RAM_R19C5,x
$52139d 25 07staSCREEN_RAM_R20C5,x
$5216cadex
$5217cadex
$521810 f3bplb_520D
$521Aad f7 50ldalives_count
$521Dc9 02cmp#$02
$521F90 17bccr_5238
$5221e9 02sbc#$02
$52230aasla
$5224aatax
$5225a9 5fb_5225lda#$5f; x-ref: $5236
$52279d fc 06staSCREEN_RAM_R19C4,x
$522Aa9 60lda#$60
$522C9d fd 06staSCREEN_RAM_R19C5,x
$522Fa9 61lda#$61
$52319d 25 07staSCREEN_RAM_R20C5,x
$5234cadex
$5235cadex
$523610 edbplb_5225
$523860r_5238rts; x-ref: $521F
; Default high score table template: 10 entries x 7 bytes (3 score + 1 player ID + 3 padding). Copied to $527F buffer on init
$5239default_high_scores.byte$00, $00, $01, $02, $01, $0c, $10, $00; x-ref: $5330
$5241.byte$90, $00, $01, $02, $12, $01, $00, $80
$5249.byte$00, $01, $03, $08, $01, $00, $70, $00
$5251.byte$01, $04, $05, $0c, $00, $60, $00, $01
$5259.byte$05, $03, $08, $00, $50, $00, $01, $06
$5261.byte$0f, $18, $00, $40, $00, $00, $07, $0f
$5269.byte$0c, $00, $30, $00, $00, $08, $0f, $14
$5271.byte$00, $20, $00, $00, $09, $0e, $04, $00
$5279.byte$10, $00, $00, $0a, $15, $0c
$527F.fill70, $00
; Max level unlocked for level-select menu. $FF=none, 0=level 3, 1=level 7, 2=level 11, 3=level 15
$52C5max_unlocked_level.byte$ff, $53, $30, $3a, $48, $45, $52, $2d; x-ref: $63C7, $63D3, $63E1, $63EF, $63FB, ...
$52CD.byte$54, $4f, $50, $53, $43, $4f, $52, $45
$52D5.byte$53
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Loads high-score data from the disk file "HER-TOPSCORES" into the
; high-score buffer at $527F. First copies default scores from f_5239,
; then overwrites with saved data from disk (if available).
; Preserves zero page $03-$FA by saving/restoring via the ZP backup area.
;
; Inputs: None
; Outputs: High-score buffer at $527F populated from disk (or defaults)
; Side Effects: Disk I/O; zero page temporarily clobbered then restored
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$52D620 26 53load_high_scoresjsrinit_default_high_scores; Init high-score buffer with defaults from f_5239 ; x-ref: $6690
$52D920 c4 5bjsrrestore_zero_page; Save ZP $03-$FA to backup area
$52DCa9 00lda#$00; Bank 0 for both LOAD filename and data
$52DEa2 00ldx#$00
$52E020 68 ffjsrKERNAL_SETBNK; $FF68 (jmp) setbnk
$52E3a9 7flda#$7f; Load destination = $527F (high-score buffer)
$52E585 fbstazp_ptr_src_lo
$52E7a9 52lda#$52
$52E985 fcstazp_ptr_src_hi
$52EBa9 0dlda#$0d; Filename = "HER-TOPSCORES" (13 chars at $52C9)
$52EDa2 c9ldx#$c9
$52EFa0 52ldy#$52
$52F120 6a 20jsrload_file_to_addr; LOAD file into $527F
$52F44c b7 5bjmpsave_zero_page; Restore ZP $03-$FA from backup and return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Saves the high scores table from memory ($527F-$52C5) to disk as
; "HER-TOPSCORES". First scratches (deletes) the existing file, then
; saves the buffer. Preserves and restores zero page via backup buffer.
;
; Inputs: None (scores buffer at $527F-$52C5 must be populated)
; Outputs: None
; Side Effects: Writes "HER-TOPSCORES" file to disk; modifies zero page
; temporarily (restored via j_5BB7)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
save_high_scores_to_disk
$52F720 c4 5bjsrrestore_zero_page; Restore zero page from backup buffer ; x-ref: $6ED4, $6EFE
$52FAa9 10lda#$10; Filename len=16: "S0:HER-TOPSCORES"
$52FCa2 c6ldx#$c6
$52FEa0 52ldy#$52
$530020 8c 20jsrsend_disk_command; Scratch (delete) existing file on disk
$5303a9 00lda#$00; I/O bank=0, filename bank=0
$5305a2 00ldx#$00
$530720 68 ffjsrKERNAL_SETBNK; Set KERNAL banks for C128; $FF68 (jmp) setbnk
$530Aa9 7flda#$7f; Save start address = $527F (scores buffer)
$530C85 fbstazp_ptr_src_lo
$530Ea9 52lda#$52
$531085 fcstazp_ptr_src_hi
$5312a9 c6lda#$c6; Save end address+1 = $52C6 (end of buffer)
$531485 fdstazp_ptr_dst_lo
$5316a9 52lda#$52
$531885 festazp_ptr_dst_hi
$531Aa9 0dlda#$0d; Filename len=13: "HER-TOPSCORES"
$531Ca2 c9ldx#$c9
$531Ea0 52ldy#$52
$532020 7b 20jsrsave_memory_to_device; Save $527F-$52C5 to "HER-TOPSCORES"
$53234c b7 5bjmpsave_zero_page; Backup zero page and return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Copies the default high score table (10 entries x 7 bytes) from the
; read-only template at f_5239 into the working score buffer at $527F.
; Called before loading saved scores from disk and before saving them.
;
; Inputs: None
; Outputs: None
; Side Effects: Overwrites 70 bytes at $527F-$52C4 with default scores;
; clobbers ZP pointer $FB/$FC and Y register.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
init_default_high_scores
$5326a9 7flda#$7f; Set up ZP pointer to destination buffer at $527F ; x-ref: $52D6, $6ED1
$532885 fbstazp_ptr_src_lo
$532Aa9 52lda#$52
$532C85 fcstazp_ptr_src_hi
$532Ea0 00ldy#$00; Y = 0, start of copy loop
$5330b9 39 52b_5330ldadefault_high_scores,y; Load byte from default score template ; x-ref: $5338
$533391 fbsta(zp_ptr_src_lo),y; Store to working score buffer at ($FB),Y
$5335c8iny
$5336c0 46cpy#$46; 70 bytes = 10 entries x 7 bytes each
$5338d0 f6bneb_5330
$533A60rts; Return after copying all 70 bytes
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Inserts the current player's score into the high score table.
; Walks the 10-entry table from bottom to top, shifting entries down
; to make room, then writes the new score and player ID at the
; correct sorted position.
;
; Each table entry is 7 bytes: 3 bytes score (lo/mid/hi) + 1 byte
; player ID + 3 bytes padding (spaces $20).
;
; Inputs: $50F4-$50F6 = player score (3 bytes, lo/mid/hi)
; $60D9 = player identifier byte
; Outputs: A = table position (0-9) if inserted, or $FF if not qualified
; X = same as A
; Side Effects: Modifies high score buffer at $527F, clobbers $FB-$FE
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$533B20 cb 53insert_high_scorejsrset_score_buffer_ptr; Point $FB/$FC at score buffer base ($527F) ; x-ref: $62A4
$533Ea2 0aldx#$0a; X = 10 entries to check
$5340a5 fbldazp_ptr_src_lo; Advance $FB/$FC by $3F (63) to last entry
$534218clc
$534369 3fadc#$3f
$534585 fbstazp_ptr_src_lo
$5347a5 fcldazp_ptr_src_hi
$534969 00adc#$00
$534B85 fcstazp_ptr_src_hi
$534Da5 fbldazp_ptr_src_lo; $FD/$FE = $FB/$FC + 7 (next slot below)
$534F18clc
$535069 07adc#$07
$535285 fdstazp_ptr_dst_lo
$5354a5 fcldazp_ptr_src_hi
$535669 00adc#$00
$535885 festazp_ptr_dst_hi
$535Aa0 02b_535Aldy#$02; --- Compare loop: walk table bottom-to-top --- ; x-ref: $539C
$535Cad f6 50ldascore_hi; Compare high byte of score
$535Fd1 fbcmp(zp_ptr_src_lo),y
$536190 3bbccb_539E; Player score < entry: done, don't insert here
$5363d0 14bneb_5379
$536588dey
$5366ad f5 50ldascore_mid; Compare mid byte of score
$5369d1 fbcmp(zp_ptr_src_lo),y
$536B90 31bccb_539E
$536Dd0 0abneb_5379
$536F88dey
$5370ad f4 50ldascore_lo; Compare low byte of score
$5373d1 fbcmp(zp_ptr_src_lo),y
$537590 27bccb_539E
$5377f0 25beqb_539E; Score == entry: no insertion needed
$5379e0 0ab_5379cpx#$0a; First iteration? Skip copy (no slot below to shift into) ; x-ref: $5363, $536D
$537Bf0 09beqb_5386
$537Da0 06ldy#$06
$537Fb1 fbb_537Flda(zp_ptr_src_lo),y; Copy 7-byte entry from ($FB) down to ($FD) ; x-ref: $5384
$538191 fdsta(zp_ptr_dst_lo),y
$538388dey
$538410 f9bplb_537F
$5386a5 fbb_5386ldazp_ptr_src_lo; $FD/$FE = current slot (will become target for next shift) ; x-ref: $537B
$538885 fdstazp_ptr_dst_lo
$538Aa5 fcldazp_ptr_src_hi
$538C85 festazp_ptr_dst_hi
$538Ea5 fbldazp_ptr_src_lo; Move $FB/$FC up 7 bytes to previous entry
$539038sec
$5391e9 07sbc#$07
$539385 fbstazp_ptr_src_lo
$5395a5 fcldazp_ptr_src_hi
$5397e9 00sbc#$00
$539985 fcstazp_ptr_src_hi
$539Bcadex; Loop until all 10 entries checked
$539Cd0 bcbneb_535A
$539Ee0 0ab_539Ecpx#$0a; X still 10? Score didn't beat any entry ; x-ref: $5361, $536B, $5375, $5377
$53A0f0 26beqb_53C8
$53A2a0 00ldy#$00
$53A4ad f4 50ldascore_lo; Write player score low byte into slot
$53A791 fdsta(zp_ptr_dst_lo),y
$53A9c8iny
$53AAad f5 50ldascore_mid; Write player score mid byte
$53AD91 fdsta(zp_ptr_dst_lo),y
$53AFc8iny
$53B0ad f6 50ldascore_hi; Write player score high byte
$53B391 fdsta(zp_ptr_dst_lo),y
$53B5c8iny
$53B6ad d9 60ldacurrent_level; Write player ID byte
$53B991 fdsta(zp_ptr_dst_lo),y
$53BBc8iny
$53BCa9 20lda#$20; Pad remaining 3 bytes with spaces ($20)
$53BE91 fdsta(zp_ptr_dst_lo),y
$53C0c8iny
$53C191 fdsta(zp_ptr_dst_lo),y
$53C3c8iny
$53C491 fdsta(zp_ptr_dst_lo),y
$53C68atxa; Return X = insertion position (0-9)
$53C760rts
$53C8a9 ffb_53C8lda#$ff; Return $FF = score not qualified ; x-ref: $53A0
$53CA60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets the ZP pointer $FB/$FC to the start of the high score buffer ($527F).
; Called before any operation that needs to read/write score entries.
;
; Inputs: None
; Outputs: $FB/$FC = $527F (high score buffer base address)
; Side Effects: Clobbers A register.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$53CBa9 7fset_score_buffer_ptrlda#$7f; Low byte of $527F (score buffer) ; x-ref: $533B, $6D96, $6E70
$53CD85 fbstazp_ptr_src_lo; ZP pointer low byte
$53CFa9 52lda#$52; High byte of $527F (score buffer)
$53D185 fcstazp_ptr_src_hi; ZP pointer high byte
$53D360rts
; Current sprite slot index (0-18) during sprite multiplexer rendering pass
$53D4sprite_render_index.byte$00; x-ref: $5616, $563D, $569F, $56A2, $56CF, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets all VIC-II sprite registers to zero, effectively disabling all 8
; sprites and clearing their positions, expand flags, priority, and
; multicolor mode.
;
; Inputs: None
; Outputs: A = $00, X = $FF (from loop underflow)
; Side Effects: All sprite positions zeroed, sprites disabled, all
; sprite attribute registers cleared ($D010–$D01D).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$53D5a2 0freset_all_spritesldx#$0f; 16 sprite registers ($D000–$D00F) ; x-ref: $5F22, $6062, $613F, $6635, $66AF, ...
$53D7a9 00lda#$00; A=0 to clear all registers
$53D99d 00 d0b_53D9sta$d000,x; zero sprite X/Y positions ; x-ref: $53DD Sprite 0 X Pos
$53DCcadex
$53DD10 fabplb_53D9
$53DF8d 10 d0sta$d010; clear sprite X MSB bits; Sprites 0-7 MSB of X coordinate
$53E28d 15 d0sta$d015; disable all sprites; Sprite display Enable
$53E58d 17 d0sta$d017; no Y-expand; Sprites Expand 2x Vertical (Y)
$53E88d 1b d0sta$d01b; sprites behind background; Sprite to Background Display Priority
$53EB8d 1c d0sta$d01c; no multicolor; Sprites Multi-Color Mode Select
$53EE8d 1d d0sta$d01d; no X-expand; Sprites Expand 2x Horizontal (X)
$53F160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all 19 sprite/object slots to their default state.
; Sets the sort order table to identity (0..18), moves all sprites offscreen
; (Y = $FF), clears two attribute arrays, and enables multicolor mode for
; all hardware sprites.
;
; Inputs: None
; Outputs: zpf_20[$00..$12] = identity order, f_0033[$00..$12] = $FF,
; zpf_92[$00..$12] = $00, zpf_A5[$00..$12] = $00
; Side Effects: Sets $D01C (Sprite Multicolor Mode) to $FF (all sprites multicolor)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$53F2a2 12init_spritesldx#$12; 19 sprite slots (0-18) ; x-ref: $6147
$53F48ab_53F4txa; use index as initial sort order value ; x-ref: $5402
$53F595 20stazpf_sort_order,x; sort_order[x] = x (identity mapping)
$53F7a9 fflda#$ff
$53F995 33stazp_spr_y_pos,x; y_pos[x] = $FF (offscreen)
$53FBa9 00lda#$00
$53FD95 92stazp_spr_expand_x,x; clear attribute table 1
$53FF95 a5stazp_spr_priority,x; clear attribute table 2
$5401cadex
$540210 f0bplb_53F4
$5404a9 fflda#$ff; enable multicolor mode for all sprites
$54068d 1c d0sta$d01c; Sprites Multi-Color Mode Select
$540960rts
$540Aa2 00j_540Aldx#$00; x-ref: $648F
$540Cb4 21b_540Cldyzpf_sort_order_1,x; x-ref: $5437
$540Eb9 33 00lda@w zp_spr_y_pos,y
$5411b4 20ldyzpf_sort_order,x
$5413d9 33 00cmp@w zp_spr_y_pos,y
$5416b0 1cbcsb_5434
$54188e 33 54stxsprite_sort_resume_x
$541Bb5 21b_541Bldazpf_sort_order_1,x; x-ref: $5430
$541D95 20stazpf_sort_order,x
$541F94 21styzpf_sort_order_1,x
$5421e0 00cpx#$00
$5423f0 0dbeqb_5432
$5425cadex
$5426b4 21ldyzpf_sort_order_1,x
$5428b9 33 00lda@w zp_spr_y_pos,y
$542Bb4 20ldyzpf_sort_order,x
$542Dd9 33 00cmp@w zp_spr_y_pos,y
$543090 e9bccb_541B
sprite_sort_resume_x =*+$01 ; x-ref: $5418
$5432a2 ffb_5432ldx#$ff; x-ref: $5423
$5434e8b_5434inx; x-ref: $5416
$5435e0 12cpx#$12
$543790 d3bccb_540C
$5439a9 00lda#$00
$543B8d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$543E8d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$54418d 1b d0sta$d01b; Sprite to Background Display Priority
$5444a4 20ldyzpf_sort_order
$5446b6 33ldxzp_spr_y_pos,y
$54488e 01 d0stx$d001; Sprite 0 Y Pos
$544Bb6 46ldxzp_spr_x_lo,y
$544D8e 00 d0stx$d000; Sprite 0 X Pos
$5450b6 6cldxzp_spr_frame,y
$54528e f8 07stxSPRITE_PTR_0
$5455b6 7fldxzp_spr_color,y
$54578e 27 d0stx$d027; Sprite 0 Color
$545Ab6 59ldxzp_spr_x_hi,y
$545Cf0 08beqb_5466
$545Ead 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$546109 01ora#$01
$54638d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$5466b6 92b_5466ldxzp_spr_expand_x,y; x-ref: $545C
$5468f0 08beqb_5472
$546Aad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$546D09 01ora#$01
$546F8d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$5472b6 a5b_5472ldxzp_spr_priority,y; x-ref: $5468
$5474f0 08beqb_547E
$5476ad 1b d0lda$d01b; Sprite to Background Display Priority
$547909 01ora#$01
$547B8d 1b d0sta$d01b; Sprite to Background Display Priority
$547Ea4 21b_547Eldyzpf_sort_order_1; x-ref: $5474
$5480b6 33ldxzp_spr_y_pos,y
$54828e 03 d0stx$d003; Sprite 1 Y Pos
$5485b6 46ldxzp_spr_x_lo,y
$54878e 02 d0stx$d002; Sprite 1 X Pos
$548Ab6 6cldxzp_spr_frame,y
$548C8e f9 07stxSPRITE_PTR_1
$548Fb6 7fldxzp_spr_color,y
$54918e 28 d0stx$d028; Sprite 1 Color
$5494b6 59ldxzp_spr_x_hi,y
$5496f0 08beqb_54A0
$5498ad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$549B09 02ora#$02
$549D8d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$54A0b6 92b_54A0ldxzp_spr_expand_x,y; x-ref: $5496
$54A2f0 08beqb_54AC
$54A4ad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$54A709 02ora#$02
$54A98d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$54ACb6 a5b_54ACldxzp_spr_priority,y; x-ref: $54A2
$54AEf0 08beqb_54B8
$54B0ad 1b d0lda$d01b; Sprite to Background Display Priority
$54B309 02ora#$02
$54B58d 1b d0sta$d01b; Sprite to Background Display Priority
$54B8a4 22b_54B8ldyzp_sort_order_2; x-ref: $54AE
$54BAb6 33ldxzp_spr_y_pos,y
$54BC8e 05 d0stx$d005; Sprite 2 Y Pos
$54BFb6 46ldxzp_spr_x_lo,y
$54C18e 04 d0stx$d004; Sprite 2 X Pos
$54C4b6 6cldxzp_spr_frame,y
$54C68e fa 07stxSPRITE_PTR_2
$54C9b6 7fldxzp_spr_color,y
$54CB8e 29 d0stx$d029; Sprite 2 Color
$54CEb6 59ldxzp_spr_x_hi,y
$54D0f0 08beqb_54DA
$54D2ad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$54D509 04ora#$04
$54D78d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$54DAb6 92b_54DAldxzp_spr_expand_x,y; x-ref: $54D0
$54DCf0 08beqb_54E6
$54DEad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$54E109 04ora#$04
$54E38d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$54E6b6 a5b_54E6ldxzp_spr_priority,y; x-ref: $54DC
$54E8f0 08beqb_54F2
$54EAad 1b d0lda$d01b; Sprite to Background Display Priority
$54ED09 04ora#$04
$54EF8d 1b d0sta$d01b; Sprite to Background Display Priority
$54F2a4 23b_54F2ldyzp_sort_order_3; x-ref: $54E8
$54F4b6 33ldxzp_spr_y_pos,y
$54F68e 07 d0stx$d007; Sprite 3 Y Pos
$54F9b6 46ldxzp_spr_x_lo,y
$54FB8e 06 d0stx$d006; Sprite 3 X Pos
$54FEb6 6cldxzp_spr_frame,y
$55008e fb 07stxSPRITE_PTR_3
$5503b6 7fldxzp_spr_color,y
$55058e 2a d0stx$d02a; Sprite 3 Color
$5508b6 59ldxzp_spr_x_hi,y
$550Af0 08beqb_5514
$550Cad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$550F09 08ora#$08
$55118d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$5514b6 92b_5514ldxzp_spr_expand_x,y; x-ref: $550A
$5516f0 08beqb_5520
$5518ad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$551B09 08ora#$08
$551D8d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$5520b6 a5b_5520ldxzp_spr_priority,y; x-ref: $5516
$5522f0 08beqb_552C
$5524ad 1b d0lda$d01b; Sprite to Background Display Priority
$552709 08ora#$08
$55298d 1b d0sta$d01b; Sprite to Background Display Priority
$552Ca4 24b_552Cldyzp_sort_order_4; x-ref: $5522
$552Eb6 33ldxzp_spr_y_pos,y
$55308e 09 d0stx$d009; Sprite 4 Y Pos
$5533b6 46ldxzp_spr_x_lo,y
$55358e 08 d0stx$d008; Sprite 4 X Pos
$5538b6 6cldxzp_spr_frame,y
$553A8e fc 07stxSPRITE_PTR_4
$553Db6 7fldxzp_spr_color,y
$553F8e 2b d0stx$d02b; Sprite 4 Color
$5542b6 59ldxzp_spr_x_hi,y
$5544f0 08beqb_554E
$5546ad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$554909 10ora#$10
$554B8d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$554Eb6 92b_554Eldxzp_spr_expand_x,y; x-ref: $5544
$5550f0 08beqb_555A
$5552ad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$555509 10ora#$10
$55578d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$555Ab6 a5b_555Aldxzp_spr_priority,y; x-ref: $5550
$555Cf0 08beqb_5566
$555Ead 1b d0lda$d01b; Sprite to Background Display Priority
$556109 10ora#$10
$55638d 1b d0sta$d01b; Sprite to Background Display Priority
$5566a4 25b_5566ldyzp_sort_order_5; x-ref: $555C
$5568b6 33ldxzp_spr_y_pos,y
$556A8e 0b d0stx$d00b; Sprite 5 Y Pos
$556Db6 46ldxzp_spr_x_lo,y
$556F8e 0a d0stx$d00a; Sprite 5 X Pos
$5572b6 6cldxzp_spr_frame,y
$55748e fd 07stxSPRITE_PTR_5
$5577b6 7fldxzp_spr_color,y
$55798e 2c d0stx$d02c; Sprite 5 Color
$557Cb6 59ldxzp_spr_x_hi,y
$557Ef0 08beqb_5588
$5580ad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$558309 20ora#$20
$55858d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$5588b6 92b_5588ldxzp_spr_expand_x,y; x-ref: $557E
$558Af0 08beqb_5594
$558Cad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$558F09 20ora#$20
$55918d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$5594b6 a5b_5594ldxzp_spr_priority,y; x-ref: $558A
$5596f0 08beqb_55A0
$5598ad 1b d0lda$d01b; Sprite to Background Display Priority
$559B09 20ora#$20
$559D8d 1b d0sta$d01b; Sprite to Background Display Priority
$55A0a4 26b_55A0ldyzp_sort_order_6; x-ref: $5596
$55A2b6 33ldxzp_spr_y_pos,y
$55A48e 0d d0stx$d00d; Sprite 6 Y Pos
$55A7b6 46ldxzp_spr_x_lo,y
$55A98e 0c d0stx$d00c; Sprite 6 X Pos
$55ACb6 6cldxzp_spr_frame,y
$55AE8e fe 07stxSPRITE_PTR_6
$55B1b6 7fldxzp_spr_color,y
$55B38e 2d d0stx$d02d; Sprite 6 Color
$55B6b6 59ldxzp_spr_x_hi,y
$55B8f0 08beqb_55C2
$55BAad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$55BD09 40ora#$40
$55BF8d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$55C2b6 92b_55C2ldxzp_spr_expand_x,y; x-ref: $55B8
$55C4f0 08beqb_55CE
$55C6ad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$55C909 40ora#$40
$55CB8d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$55CEb6 a5b_55CEldxzp_spr_priority,y; x-ref: $55C4
$55D0f0 08beqb_55DA
$55D2ad 1b d0lda$d01b; Sprite to Background Display Priority
$55D509 40ora#$40
$55D78d 1b d0sta$d01b; Sprite to Background Display Priority
$55DAa4 27b_55DAldyzp_sort_order_7; x-ref: $55D0
$55DCb6 33ldxzp_spr_y_pos,y
$55DE8e 0f d0stx$d00f; Sprite 7 Y Pos
$55E1b6 46ldxzp_spr_x_lo,y
$55E38e 0e d0stx$d00e; Sprite 7 X Pos
$55E6b6 6cldxzp_spr_frame,y
$55E88e ff 07stxSPRITE_PTR_7
$55EBb6 7fldxzp_spr_color,y
$55ED8e 2e d0stx$d02e; Sprite 7 Color
$55F0b6 59ldxzp_spr_x_hi,y
$55F2f0 08beqb_55FC
$55F4ad 10 d0lda$d010; Sprites 0-7 MSB of X coordinate
$55F709 80ora#$80
$55F98d 10 d0sta$d010; Sprites 0-7 MSB of X coordinate
$55FCb6 92b_55FCldxzp_spr_expand_x,y; x-ref: $55F2
$55FEf0 08beqb_5608
$5600ad 1d d0lda$d01d; Sprites Expand 2x Horizontal (X)
$560309 80ora#$80
$56058d 1d d0sta$d01d; Sprites Expand 2x Horizontal (X)
$5608b6 a5b_5608ldxzp_spr_priority,y; x-ref: $55FE
$560Af0 08beqb_5614
$560Cad 1b d0lda$d01b; Sprite to Background Display Priority
$560F09 80ora#$80
$56118d 1b d0sta$d01b; Sprite to Background Display Priority
$5614a9 08b_5614lda#$08; x-ref: $560A
$56168d d4 53stasprite_render_index
$561960rts
$561Aad 01 d0j_561Alda$d001; x-ref: $5AA7, $64D3 Sprite 0 Y Pos
$561Dc9 8ccmp#$8c
$561F90 03bccb_5624
$56214c aa 5ajmpj_5AAA
$562418b_5624clc; x-ref: $561F
$562569 17adc#$17
$5627ed 12 d0sbc$d012; Raster Position
$562A90 11bccirq_sprite_mux_0
$562Cc9 03cmp#$03
$562Eb0 02bcsb_5632
$5630a9 03lda#$03
$563218b_5632clc; x-ref: $562E
$56336d 12 d0adc$d012; Raster Position
$5636a2 3dldx#<irq_sprite_mux_0
$5638a0 56ldy#>irq_sprite_mux_0
$563A4c 7c 21jmpirq_chain_next
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sprite multiplexer raster IRQ handler chain (hardware sprite 0).
;
; The VIC-II has only 8 hardware sprites, but this game displays up to 19
; logical sprites by reusing hardware sprites across different raster lines.
; Each handler in the chain (irq_sprite_mux_0 through irq_sprite_mux_7)
; configures one hardware sprite from the Y-sorted software sprite tables:
;
; 1. Load sprite_render_index to get the current logical sprite slot.
; 2. Use zpf_sort_order[slot] to get the Y-sorted sprite index.
; 3. If Y position == $FF → sprite is offscreen, skip to j_5AAA (done).
; 4. Write Y pos, X pos lo, X hi bit, frame ptr, color, expand-X,
; and priority to the VIC-II registers for this hardware sprite.
; 5. Increment sprite_render_index. If >= 19 ($13) → done.
; 6. Read the NEXT hardware sprite's current Y register to calculate
; when to fire the next raster split.
; 7. Chain to the next handler via JMP j_217C (set raster + IRQ vector).
;
; After all 8 hardware sprites are assigned, handler 7 loops back to
; irq_sprite_mux_0 (via j_561A) to reuse sprites on lower raster lines.
;
; Inputs: sprite_render_index, zpf_sort_order[], zp_spr_* tables
; Outputs: VIC-II sprite registers ($D000-$D00F, $D010, $D01B, $D01D,
; $D027-$D02E, sprite pointers)
; Side Effects: Configures VIC-II hardware sprites, chains next raster IRQ
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$563Dac d4 53irq_sprite_mux_0ldysprite_render_index; --- HW sprite 0: VIC $D000/$D001 --- ; x-ref: $562A, $5636, $5638
$5640b9 20 00lda@w zpf_sort_order,y
$5643a8tay; Get Y-sorted logical sprite index
$5644b9 33 00lda@w zp_spr_y_pos,y
$5647c9 ffcmp#$ff; Check if sprite is offscreen ($FF)
$5649d0 03bneb_564E
$564B4c aa 5ajmpj_5AAA; No more sprites to render
$564E8d 01 d0b_564Esta$d001; Set HW sprite 0 Y position ; x-ref: $5649 Sprite 0 Y Pos
$5651b9 46 00lda@w zp_spr_x_lo,y
$56548d 00 d0sta$d000; Set HW sprite 0 frame pointer; Sprite 0 X Pos
$5657b9 6c 00lda@w zp_spr_frame,y
$565A8d f8 07staSPRITE_PTR_0
$565Db9 7f 00lda@w zp_spr_color,y; Set X MSB bit 0
$56608d 27 d0sta$d027; Sprite 0 Color
$5663b9 59 00lda@w zp_spr_x_hi,y
$5666f0 07beqb_566F
$5668a9 01lda#$01
$566A0d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$566Dd0 05bneb_5674
$566Fa9 feb_566Flda#$fe; Set expand-X bit 0 ; x-ref: $5666
$56712d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$56748d 10 d0b_5674sta$d010; x-ref: $566D Sprites 0-7 MSB of X coordinate
$5677b9 92 00lda@w zp_spr_expand_x,y
$567Af0 07beqb_5683
$567Ca9 01lda#$01
$567E0d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$5681d0 05bneb_5688; Set priority bit 0
$5683a9 feb_5683lda#$fe; x-ref: $567A
$56852d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$56888d 1d d0b_5688sta$d01d; x-ref: $5681 Sprites Expand 2x Horizontal (X)
$568Bb9 a5 00lda@w zp_spr_priority,y
$568Ef0 07beqb_5697
$5690a9 01lda#$01
$56920d 1b d0ora$d01b; Sprite to Background Display Priority
$5695d0 05bneb_569C; All 19 sprites processed?
$5697a9 feb_5697lda#$fe; x-ref: $568E
$56992d 1b d0and$d01b; Sprite to Background Display Priority
$569C8d 1b d0b_569Csta$d01b; Read next HW sprite Y to calc raster split ; x-ref: $5695 Sprite to Background Display Priority
$569Fee d4 53incsprite_render_index; Past visible area? ($8C = rasterline 140)
$56A2ad d4 53ldasprite_render_index
$56A5c9 13cmp#$13
$56A7d0 03bneb_56AC
$56A94c aa 5ajmpj_5AAA
$56ACad 03 d0b_56AClda$d003; x-ref: $56A7 Sprite 1 Y Pos
$56AFc9 8ccmp#$8c
$56B190 03bccb_56B6
$56B34c aa 5ajmpj_5AAA
$56B618b_56B6clc; x-ref: $56B1
$56B769 17adc#$17
$56B9ed 12 d0sbc$d012; Raster Position
$56BC90 11bccirq_sprite_mux_1
$56BEc9 03cmp#$03
$56C0b0 02bcsb_56C4
$56C2a9 03lda#$03
$56C418b_56C4clc; x-ref: $56C0
$56C56d 12 d0adc$d012; Raster Position
$56C8a2 cfldx#<irq_sprite_mux_1
$56CAa0 56ldy#>irq_sprite_mux_1
$56CC4c 7c 21jmpirq_chain_next
$56CFac d4 53irq_sprite_mux_1ldysprite_render_index; --- HW sprite 1: VIC $D002/$D003 --- ; x-ref: $56BC, $56C8, $56CA
$56D2b9 20 00lda@w zpf_sort_order,y
$56D5a8tay
$56D6b9 33 00lda@w zp_spr_y_pos,y
$56D9c9 ffcmp#$ff
$56DBd0 03bneb_56E0
$56DD4c aa 5ajmpj_5AAA
$56E08d 03 d0b_56E0sta$d003; x-ref: $56DB Sprite 1 Y Pos
$56E3b9 46 00lda@w zp_spr_x_lo,y
$56E68d 02 d0sta$d002; Sprite 1 X Pos
$56E9b9 6c 00lda@w zp_spr_frame,y
$56EC8d f9 07staSPRITE_PTR_1
$56EFb9 7f 00lda@w zp_spr_color,y
$56F28d 28 d0sta$d028; Sprite 1 Color
$56F5b9 59 00lda@w zp_spr_x_hi,y
$56F8f0 07beqb_5701
$56FAa9 02lda#$02
$56FC0d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$56FFd0 05bneb_5706
$5701a9 fdb_5701lda#$fd; x-ref: $56F8
$57032d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$57068d 10 d0b_5706sta$d010; x-ref: $56FF Sprites 0-7 MSB of X coordinate
$5709b9 92 00lda@w zp_spr_expand_x,y
$570Cf0 07beqb_5715
$570Ea9 02lda#$02
$57100d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$5713d0 05bneb_571A
$5715a9 fdb_5715lda#$fd; x-ref: $570C
$57172d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$571A8d 1d d0b_571Asta$d01d; x-ref: $5713 Sprites Expand 2x Horizontal (X)
$571Db9 a5 00lda@w zp_spr_priority,y
$5720f0 07beqb_5729
$5722a9 02lda#$02
$57240d 1b d0ora$d01b; Sprite to Background Display Priority
$5727d0 05bneb_572E
$5729a9 fdb_5729lda#$fd; x-ref: $5720
$572B2d 1b d0and$d01b; Sprite to Background Display Priority
$572E8d 1b d0b_572Esta$d01b; x-ref: $5727 Sprite to Background Display Priority
$5731ee d4 53incsprite_render_index
$5734ad d4 53ldasprite_render_index
$5737c9 13cmp#$13
$5739d0 03bneb_573E
$573B4c aa 5ajmpj_5AAA
$573Ead 05 d0b_573Elda$d005; x-ref: $5739 Sprite 2 Y Pos
$5741c9 8ccmp#$8c
$574390 03bccb_5748
$57454c aa 5ajmpj_5AAA
$574818b_5748clc; x-ref: $5743
$574969 17adc#$17
$574Bed 12 d0sbc$d012; Raster Position
$574E90 11bccirq_sprite_mux_2
$5750c9 03cmp#$03
$5752b0 02bcsb_5756
$5754a9 03lda#$03
$575618b_5756clc; x-ref: $5752
$57576d 12 d0adc$d012; Raster Position
$575Aa2 61ldx#<irq_sprite_mux_2
$575Ca0 57ldy#>irq_sprite_mux_2
$575E4c 7c 21jmpirq_chain_next
$5761ac d4 53irq_sprite_mux_2ldysprite_render_index; --- HW sprite 2: VIC $D004/$D005 --- ; x-ref: $574E, $575A, $575C
$5764b9 20 00lda@w zpf_sort_order,y
$5767a8tay
$5768b9 33 00lda@w zp_spr_y_pos,y
$576Bc9 ffcmp#$ff
$576Dd0 03bneb_5772
$576F4c aa 5ajmpj_5AAA
$57728d 05 d0b_5772sta$d005; x-ref: $576D Sprite 2 Y Pos
$5775b9 46 00lda@w zp_spr_x_lo,y
$57788d 04 d0sta$d004; Sprite 2 X Pos
$577Bb9 6c 00lda@w zp_spr_frame,y
$577E8d fa 07staSPRITE_PTR_2
$5781b9 7f 00lda@w zp_spr_color,y
$57848d 29 d0sta$d029; Sprite 2 Color
$5787b9 59 00lda@w zp_spr_x_hi,y
$578Af0 07beqb_5793
$578Ca9 04lda#$04
$578E0d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$5791d0 05bneb_5798
$5793a9 fbb_5793lda#$fb; x-ref: $578A
$57952d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$57988d 10 d0b_5798sta$d010; x-ref: $5791 Sprites 0-7 MSB of X coordinate
$579Bb9 92 00lda@w zp_spr_expand_x,y
$579Ef0 07beqb_57A7
$57A0a9 04lda#$04
$57A20d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$57A5d0 05bneb_57AC
$57A7a9 fbb_57A7lda#$fb; x-ref: $579E
$57A92d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$57AC8d 1d d0b_57ACsta$d01d; x-ref: $57A5 Sprites Expand 2x Horizontal (X)
$57AFb9 a5 00lda@w zp_spr_priority,y
$57B2f0 07beqb_57BB
$57B4a9 04lda#$04
$57B60d 1b d0ora$d01b; Sprite to Background Display Priority
$57B9d0 05bneb_57C0
$57BBa9 fbb_57BBlda#$fb; x-ref: $57B2
$57BD2d 1b d0and$d01b; Sprite to Background Display Priority
$57C08d 1b d0b_57C0sta$d01b; x-ref: $57B9 Sprite to Background Display Priority
$57C3ee d4 53incsprite_render_index
$57C6ad d4 53ldasprite_render_index
$57C9c9 13cmp#$13
$57CBd0 03bneb_57D0
$57CD4c aa 5ajmpj_5AAA
$57D0ad 07 d0b_57D0lda$d007; x-ref: $57CB Sprite 3 Y Pos
$57D3c9 8ccmp#$8c
$57D590 03bccb_57DA
$57D74c aa 5ajmpj_5AAA
$57DA18b_57DAclc; x-ref: $57D5
$57DB69 17adc#$17
$57DDed 12 d0sbc$d012; Raster Position
$57E090 11bccirq_sprite_mux_3
$57E2c9 03cmp#$03
$57E4b0 02bcsb_57E8
$57E6a9 03lda#$03
$57E818b_57E8clc; x-ref: $57E4
$57E96d 12 d0adc$d012; Raster Position
$57ECa2 f3ldx#<irq_sprite_mux_3
$57EEa0 57ldy#>irq_sprite_mux_3
$57F04c 7c 21jmpirq_chain_next
$57F3ac d4 53irq_sprite_mux_3ldysprite_render_index; --- HW sprite 3: VIC $D006/$D007 --- ; x-ref: $57E0, $57EC, $57EE
$57F6b9 20 00lda@w zpf_sort_order,y
$57F9a8tay
$57FAb9 33 00lda@w zp_spr_y_pos,y
$57FDc9 ffcmp#$ff
$57FFd0 03bneb_5804
$58014c aa 5ajmpj_5AAA
$58048d 07 d0b_5804sta$d007; x-ref: $57FF Sprite 3 Y Pos
$5807b9 46 00lda@w zp_spr_x_lo,y
$580A8d 06 d0sta$d006; Sprite 3 X Pos
$580Db9 6c 00lda@w zp_spr_frame,y
$58108d fb 07staSPRITE_PTR_3
$5813b9 7f 00lda@w zp_spr_color,y
$58168d 2a d0sta$d02a; Sprite 3 Color
$5819b9 59 00lda@w zp_spr_x_hi,y
$581Cf0 07beqb_5825
$581Ea9 08lda#$08
$58200d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$5823d0 05bneb_582A
$5825a9 f7b_5825lda#$f7; x-ref: $581C
$58272d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$582A8d 10 d0b_582Asta$d010; x-ref: $5823 Sprites 0-7 MSB of X coordinate
$582Db9 92 00lda@w zp_spr_expand_x,y
$5830f0 07beqb_5839
$5832a9 08lda#$08
$58340d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$5837d0 05bneb_583E
$5839a9 f7b_5839lda#$f7; x-ref: $5830
$583B2d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$583E8d 1d d0b_583Esta$d01d; x-ref: $5837 Sprites Expand 2x Horizontal (X)
$5841b9 a5 00lda@w zp_spr_priority,y
$5844f0 07beqb_584D
$5846a9 08lda#$08
$58480d 1b d0ora$d01b; Sprite to Background Display Priority
$584Bd0 05bneb_5852
$584Da9 f7b_584Dlda#$f7; x-ref: $5844
$584F2d 1b d0and$d01b; Sprite to Background Display Priority
$58528d 1b d0b_5852sta$d01b; x-ref: $584B Sprite to Background Display Priority
$5855ee d4 53incsprite_render_index
$5858ad d4 53ldasprite_render_index
$585Bc9 13cmp#$13
$585Dd0 03bneb_5862
$585F4c aa 5ajmpj_5AAA
$5862ad 09 d0b_5862lda$d009; x-ref: $585D Sprite 4 Y Pos
$5865c9 8ccmp#$8c
$586790 03bccb_586C
$58694c aa 5ajmpj_5AAA
$586C18b_586Cclc; x-ref: $5867
$586D69 17adc#$17
$586Fed 12 d0sbc$d012; Raster Position
$587290 11bccirq_sprite_mux_4
$5874c9 03cmp#$03
$5876b0 02bcsb_587A
$5878a9 03lda#$03
$587A18b_587Aclc; x-ref: $5876
$587B6d 12 d0adc$d012; Raster Position
$587Ea2 85ldx#<irq_sprite_mux_4
$5880a0 58ldy#>irq_sprite_mux_4
$58824c 7c 21jmpirq_chain_next
$5885ac d4 53irq_sprite_mux_4ldysprite_render_index; --- HW sprite 4: VIC $D008/$D009 --- ; x-ref: $5872, $587E, $5880
$5888b9 20 00lda@w zpf_sort_order,y
$588Ba8tay
$588Cb9 33 00lda@w zp_spr_y_pos,y
$588Fc9 ffcmp#$ff
$5891d0 03bneb_5896
$58934c aa 5ajmpj_5AAA
$58968d 09 d0b_5896sta$d009; x-ref: $5891 Sprite 4 Y Pos
$5899b9 46 00lda@w zp_spr_x_lo,y
$589C8d 08 d0sta$d008; Sprite 4 X Pos
$589Fb9 6c 00lda@w zp_spr_frame,y
$58A28d fc 07staSPRITE_PTR_4
$58A5b9 7f 00lda@w zp_spr_color,y
$58A88d 2b d0sta$d02b; Sprite 4 Color
$58ABb9 59 00lda@w zp_spr_x_hi,y
$58AEf0 07beqb_58B7
$58B0a9 10lda#$10
$58B20d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$58B5d0 05bneb_58BC
$58B7a9 efb_58B7lda#$ef; x-ref: $58AE
$58B92d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$58BC8d 10 d0b_58BCsta$d010; x-ref: $58B5 Sprites 0-7 MSB of X coordinate
$58BFb9 92 00lda@w zp_spr_expand_x,y
$58C2f0 07beqb_58CB
$58C4a9 10lda#$10
$58C60d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$58C9d0 05bneb_58D0
$58CBa9 efb_58CBlda#$ef; x-ref: $58C2
$58CD2d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$58D08d 1d d0b_58D0sta$d01d; x-ref: $58C9 Sprites Expand 2x Horizontal (X)
$58D3b9 a5 00lda@w zp_spr_priority,y
$58D6f0 07beqb_58DF
$58D8a9 10lda#$10
$58DA0d 1b d0ora$d01b; Sprite to Background Display Priority
$58DDd0 05bneb_58E4
$58DFa9 efb_58DFlda#$ef; x-ref: $58D6
$58E12d 1b d0and$d01b; Sprite to Background Display Priority
$58E48d 1b d0b_58E4sta$d01b; x-ref: $58DD Sprite to Background Display Priority
$58E7ee d4 53incsprite_render_index
$58EAad d4 53ldasprite_render_index
$58EDc9 13cmp#$13
$58EFd0 03bneb_58F4
$58F14c aa 5ajmpj_5AAA
$58F4ad 0b d0b_58F4lda$d00b; x-ref: $58EF Sprite 5 Y Pos
$58F7c9 8ccmp#$8c
$58F990 03bccb_58FE
$58FB4c aa 5ajmpj_5AAA
$58FE18b_58FEclc; x-ref: $58F9
$58FF69 17adc#$17
$5901ed 12 d0sbc$d012; Raster Position
$590490 11bccirq_sprite_mux_5
$5906c9 03cmp#$03
$5908b0 02bcsb_590C
$590Aa9 03lda#$03
$590C18b_590Cclc; x-ref: $5908
$590D6d 12 d0adc$d012; Raster Position
$5910a2 17ldx#<irq_sprite_mux_5
$5912a0 59ldy#>irq_sprite_mux_5
$59144c 7c 21jmpirq_chain_next
$5917ac d4 53irq_sprite_mux_5ldysprite_render_index; --- HW sprite 5: VIC $D00A/$D00B --- ; x-ref: $5904, $5910, $5912
$591Ab9 20 00lda@w zpf_sort_order,y
$591Da8tay
$591Eb9 33 00lda@w zp_spr_y_pos,y
$5921c9 ffcmp#$ff
$5923d0 03bneb_5928
$59254c aa 5ajmpj_5AAA
$59288d 0b d0b_5928sta$d00b; x-ref: $5923 Sprite 5 Y Pos
$592Bb9 46 00lda@w zp_spr_x_lo,y
$592E8d 0a d0sta$d00a; Sprite 5 X Pos
$5931b9 6c 00lda@w zp_spr_frame,y
$59348d fd 07staSPRITE_PTR_5
$5937b9 7f 00lda@w zp_spr_color,y
$593A8d 2c d0sta$d02c; Sprite 5 Color
$593Db9 59 00lda@w zp_spr_x_hi,y
$5940f0 07beqb_5949
$5942a9 20lda#$20
$59440d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$5947d0 05bneb_594E
$5949a9 dfb_5949lda#$df; x-ref: $5940
$594B2d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$594E8d 10 d0b_594Esta$d010; x-ref: $5947 Sprites 0-7 MSB of X coordinate
$5951b9 92 00lda@w zp_spr_expand_x,y
$5954f0 07beqb_595D
$5956a9 20lda#$20
$59580d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$595Bd0 05bneb_5962
$595Da9 dfb_595Dlda#$df; x-ref: $5954
$595F2d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$59628d 1d d0b_5962sta$d01d; x-ref: $595B Sprites Expand 2x Horizontal (X)
$5965b9 a5 00lda@w zp_spr_priority,y
$5968f0 07beqb_5971
$596Aa9 20lda#$20
$596C0d 1b d0ora$d01b; Sprite to Background Display Priority
$596Fd0 05bneb_5976
$5971a9 dfb_5971lda#$df; x-ref: $5968
$59732d 1b d0and$d01b; Sprite to Background Display Priority
$59768d 1b d0b_5976sta$d01b; x-ref: $596F Sprite to Background Display Priority
$5979ee d4 53incsprite_render_index
$597Cad d4 53ldasprite_render_index
$597Fc9 13cmp#$13
$5981d0 03bneb_5986
$59834c aa 5ajmpj_5AAA
$5986ad 0d d0b_5986lda$d00d; x-ref: $5981 Sprite 6 Y Pos
$5989c9 8ccmp#$8c
$598B90 03bccb_5990
$598D4c aa 5ajmpj_5AAA
$599018b_5990clc; x-ref: $598B
$599169 17adc#$17
$5993ed 12 d0sbc$d012; Raster Position
$599690 11bccirq_sprite_mux_6
$5998c9 03cmp#$03
$599Ab0 02bcsb_599E
$599Ca9 03lda#$03
$599E18b_599Eclc; x-ref: $599A
$599F6d 12 d0adc$d012; Raster Position
$59A2a2 a9ldx#<irq_sprite_mux_6
$59A4a0 59ldy#>irq_sprite_mux_6
$59A64c 7c 21jmpirq_chain_next
$59A9ac d4 53irq_sprite_mux_6ldysprite_render_index; --- HW sprite 6: VIC $D00C/$D00D --- ; x-ref: $5996, $59A2, $59A4
$59ACb9 20 00lda@w zpf_sort_order,y
$59AFa8tay
$59B0b9 33 00lda@w zp_spr_y_pos,y
$59B3c9 ffcmp#$ff
$59B5d0 03bneb_59BA
$59B74c aa 5ajmpj_5AAA
$59BA8d 0d d0b_59BAsta$d00d; x-ref: $59B5 Sprite 6 Y Pos
$59BDb9 46 00lda@w zp_spr_x_lo,y
$59C08d 0c d0sta$d00c; Sprite 6 X Pos
$59C3b9 6c 00lda@w zp_spr_frame,y
$59C68d fe 07staSPRITE_PTR_6
$59C9b9 7f 00lda@w zp_spr_color,y
$59CC8d 2d d0sta$d02d; Sprite 6 Color
$59CFb9 59 00lda@w zp_spr_x_hi,y
$59D2f0 07beqb_59DB
$59D4a9 40lda#$40
$59D60d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$59D9d0 05bneb_59E0
$59DBa9 bfb_59DBlda#$bf; x-ref: $59D2
$59DD2d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$59E08d 10 d0b_59E0sta$d010; x-ref: $59D9 Sprites 0-7 MSB of X coordinate
$59E3b9 92 00lda@w zp_spr_expand_x,y
$59E6f0 07beqb_59EF
$59E8a9 40lda#$40
$59EA0d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$59EDd0 05bneb_59F4
$59EFa9 bfb_59EFlda#$bf; x-ref: $59E6
$59F12d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$59F48d 1d d0b_59F4sta$d01d; x-ref: $59ED Sprites Expand 2x Horizontal (X)
$59F7b9 a5 00lda@w zp_spr_priority,y
$59FAf0 07beqb_5A03
$59FCa9 40lda#$40
$59FE0d 1b d0ora$d01b; Sprite to Background Display Priority
$5A01d0 05bneb_5A08
$5A03a9 bfb_5A03lda#$bf; x-ref: $59FA
$5A052d 1b d0and$d01b; Sprite to Background Display Priority
$5A088d 1b d0b_5A08sta$d01b; x-ref: $5A01 Sprite to Background Display Priority
$5A0Bee d4 53incsprite_render_index
$5A0Ead d4 53ldasprite_render_index
$5A11c9 13cmp#$13
$5A13d0 03bneb_5A18
$5A154c aa 5ajmpj_5AAA
$5A18ad 0f d0b_5A18lda$d00f; x-ref: $5A13 Sprite 7 Y Pos
$5A1Bc9 8ccmp#$8c
$5A1D90 03bccb_5A22
$5A1F4c aa 5ajmpj_5AAA
$5A2218b_5A22clc; x-ref: $5A1D
$5A2369 17adc#$17
$5A25ed 12 d0sbc$d012; Raster Position
$5A2890 11bccirq_sprite_mux_7
$5A2Ac9 03cmp#$03
$5A2Cb0 02bcsb_5A30
$5A2Ea9 03lda#$03
$5A3018b_5A30clc; x-ref: $5A2C
$5A316d 12 d0adc$d012; Raster Position
$5A34a2 3bldx#<irq_sprite_mux_7
$5A36a0 5aldy#>irq_sprite_mux_7
$5A384c 7c 21jmpirq_chain_next
$5A3Bac d4 53irq_sprite_mux_7ldysprite_render_index; --- HW sprite 7: VIC $D00E/$D00F --- ; x-ref: $5A28, $5A34, $5A36
$5A3Eb9 20 00lda@w zpf_sort_order,y
$5A41a8tay
$5A42b9 33 00lda@w zp_spr_y_pos,y
$5A45c9 ffcmp#$ff
$5A47d0 03bneb_5A4C
$5A494c aa 5ajmpj_5AAA
$5A4C8d 0f d0b_5A4Csta$d00f; x-ref: $5A47 Sprite 7 Y Pos
$5A4Fb9 46 00lda@w zp_spr_x_lo,y
$5A528d 0e d0sta$d00e; Sprite 7 X Pos
$5A55b9 6c 00lda@w zp_spr_frame,y
$5A588d ff 07staSPRITE_PTR_7
$5A5Bb9 7f 00lda@w zp_spr_color,y
$5A5E8d 2e d0sta$d02e; Sprite 7 Color
$5A61b9 59 00lda@w zp_spr_x_hi,y
$5A64f0 07beqb_5A6D
$5A66a9 80lda#$80
$5A680d 10 d0ora$d010; Sprites 0-7 MSB of X coordinate
$5A6Bd0 05bneb_5A72
$5A6Da9 7fb_5A6Dlda#$7f; x-ref: $5A64
$5A6F2d 10 d0and$d010; Sprites 0-7 MSB of X coordinate
$5A728d 10 d0b_5A72sta$d010; x-ref: $5A6B Sprites 0-7 MSB of X coordinate
$5A75b9 92 00lda@w zp_spr_expand_x,y
$5A78f0 07beqb_5A81
$5A7Aa9 80lda#$80
$5A7C0d 1d d0ora$d01d; Sprites Expand 2x Horizontal (X)
$5A7Fd0 05bneb_5A86
$5A81a9 7fb_5A81lda#$7f; x-ref: $5A78
$5A832d 1d d0and$d01d; Sprites Expand 2x Horizontal (X)
$5A868d 1d d0b_5A86sta$d01d; x-ref: $5A7F Sprites Expand 2x Horizontal (X)
$5A89b9 a5 00lda@w zp_spr_priority,y
$5A8Cf0 07beqb_5A95
$5A8Ea9 80lda#$80
$5A900d 1b d0ora$d01b; Sprite to Background Display Priority
$5A93d0 05bneb_5A9A
$5A95a9 7fb_5A95lda#$7f; x-ref: $5A8C
$5A972d 1b d0and$d01b; Sprite to Background Display Priority
$5A9A8d 1b d0b_5A9Asta$d01b; x-ref: $5A93 Sprite to Background Display Priority
$5A9Dee d4 53incsprite_render_index
$5AA0ad d4 53ldasprite_render_index
$5AA3c9 13cmp#$13
$5AA5f0 03beqj_5AAA
$5AA74c 1a 56jmpj_561A
$5AAAa9 afj_5AAAlda#$af; x-ref: $5621, $564B, $56A9, $56B3, $56DD, ...
$5AACa2 d6ldx#<irq_end_sprite_mux
$5AAEa0 64ldy#>irq_end_sprite_mux
$5AB04c 7c 21jmpirq_chain_next
; 248-byte buffer for saving/restoring zero page locations $03–$FA.
; Used by save_zero_page / restore_zero_page around KERNAL calls.
$5AB3zp_save_buffer.fill248, $00; x-ref: $5BBB, $5BC6
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables NMI handling by pointing the NMI vector ($FFFA/$FFFB) to an RTI
; instruction at $5BB6. After this, any NMI (e.g., RESTORE key) immediately
; returns without executing any handler code.
;
; Inputs: None
; Outputs: None
; Side Effects: NMI vector ($FFFA/$FFFB) set to $5BB6 (RTI stub)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5BABa9 b6disable_nmilda#$b6; low byte of NMI handler address ($5BB6) ; x-ref: $20EC
$5BAD8d fa ffsta$fffa; set NMI vector low byte; NMI
$5BB0a9 5blda#$5b; high byte of NMI handler address ($5BB6)
$5BB28d fb ffsta$fffb; set NMI vector high byte → NMI now points to RTI; NMI
$5BB560rts
$5BB6.byte$40
$5BB7a2 00save_zero_pageldx#$00; x-ref: $1CA0, $20E6, $52F4, $5323
$5BB9b5 03b_5BB9ldazp_work1,x; x-ref: $5BC1
$5BBB9d b3 5astazp_save_buffer,x
$5BBEe8inx
$5BBFe0 f8cpx#$f8
$5BC1d0 f6bneb_5BB9
$5BC360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Restores 248 bytes of zero page ($03–$FA) from the save buffer at f_5AB3.
; This is the inverse of the save routine at j_5BB7 (save_zero_page).
; Used to restore zero page state after disk I/O or KERNAL calls that
; clobber it.
;
; Inputs: f_5AB3 buffer containing previously saved zero page data
; Outputs: Zero page $03–$FA restored from buffer
; Side Effects: Overwrites zero page locations $03–$FA
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5BC4a2 00restore_zero_pageldx#$00; Start index at 0 (first ZP byte = $03) ; x-ref: $1C4D, $52D9, $52F7
$5BC6bd b3 5ab_5BC6ldazp_save_buffer,x; Load saved byte from buffer ; x-ref: $5BCE
$5BC995 03stazp_work1,x; Write it back to zero page
$5BCBe8inx
$5BCCe0 f8cpx#$f8; 248 bytes: covers $03–$FA
$5BCEd0 f6bneb_5BC6
$5BD060rts
; Maximum number of characters allowed for text input (set on entry to input_text_string).
$5BD1input_max_chars.byte$00; x-ref: $5C72, $5CEA, $5D6B, $5D97
; Current number of characters entered in the input buffer. Incremented on each accepted char, decremented on backspace.
$5BD2input_char_count.byte$00; x-ref: $5C7D, $5CBB, $5CE7, $5CF0, $5CFC, ...
; Input completion flag. $00 = still accepting input, $FF = input complete (Enter pressed or max chars reached).
$5BD3input_done_flag.byte$00; x-ref: $5C80, $5C9C, $5CA4, $5CE3, $5D9E
; Last keyboard key state for debounce. Compared against current scan to detect new keypresses.
$5BD4input_last_key.byte$00; x-ref: $5C8C, $5CC9, $5CCD, $5CD3
; Screen column position of the text input cursor. Incremented after each character, decremented on backspace.
$5BD5input_cursor_col.byte$00; x-ref: $5C75, $5CF9, $5D8E, $5E1D, $5E2E
; Screen row position of the text input cursor. Set once on entry, used for screen address calculation.
$5BD6input_cursor_row.byte$00; x-ref: $5C78, $5E08
; Joystick character selector index (0–40). Indexes into char_selection_table to select the current character.
; 0 = cursor only (space), 1–26 = A-Z, 27–36 = 0-9, 37–40 = punctuation (+, -, ., ,).
$5BD7input_selector_idx.byte$00; x-ref: $5C83, $5D01, $5D7F, $5DA4, $5DB9, ...
; Cursor blink frame counter. Increments each frame, resets at 40 ($28). Phase < 20 shows cursor char, >= 20 shows blank.
$5BD8input_blink_counter.byte$00; x-ref: $5C86, $5CA9, $5CAC, $5CB5, $5D04, ...
; 40-byte text input buffer. Stores entered character codes, null-terminated. Used for high-score name entry (3 chars max).
$5BD9input_text_buffer.fill40, $00; x-ref: $5CC0, $5CF3, $5D88, $6E83
$5C01.byte$ff, $fe, $00, $00, $00, $00, $00, $00
$5C09.byte$33, $17, $01, $34, $1a, $13, $05, $00
$5C11.byte$35, $12, $04, $36, $03, $06, $14, $18
$5C19.byte$37, $19, $07, $38, $02, $08, $15, $16
$5C21.byte$39, $09, $0a, $30, $0d, $0b, $0f, $0e
$5C29.byte$2b, $10, $0c, $2d, $2e, $00, $00, $2c
$5C31.fill8, $00
$5C39.byte$31, $00, $00, $32, $20, $00, $11, $00
; CIA1 keyboard row select masks ($FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F).
; Each byte selects one row of the 8×8 keyboard matrix by pulling one bit low on Port A.
$5C41keyboard_row_masks.byte$fe, $fd, $fb, $f7, $ef, $df, $bf, $7f; x-ref: $5D17, $5D2F
; Character selection lookup table for joystick text input (41 entries).
; Index 0 = space ($20), 1-26 = A-Z, 27-36 = 0-9, 37-40 = +, -, ., comma.
$5C49char_selection_table.byte$20, $01, $02, $03, $04, $05, $06, $07; x-ref: $5D82, $5DFC
$5C51.byte$08, $09, $0a, $0b, $0c, $0d, $0e, $0f
$5C59.byte$10, $11, $12, $13, $14, $15, $16, $17
$5C61.byte$18, $19, $1a, $30, $31, $32, $33, $34
$5C69.byte$35, $36, $37, $38, $39, $2b, $2d, $2e
$5C71.byte$2c
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Text input routine. Reads characters from keyboard or joystick and writes
; them to screen memory. Supports keyboard scanning via CIA1, joystick-based
; character selection (up/down to cycle, fire to confirm), and backspace ($FF).
; Input terminates when max characters reached or Enter ($FE) is pressed.
;
; Inputs: A = max number of characters, X = screen column, Y = screen row
; Outputs: f_5BD9 = buffer with entered character codes
; Side Effects: Writes characters directly to screen memory at $0400
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5C728d d1 5binput_text_stringstainput_max_chars; Store max chars allowed ; x-ref: $6E6D
$5C758e d5 5bstxinput_cursor_col; Store initial screen column
$5C788c d6 5bstyinput_cursor_row; Store screen row
$5C7Ba9 00lda#$00; Clear current char count
$5C7D8d d2 5bstainput_char_count
$5C808d d3 5bstainput_done_flag; Clear done flag
$5C838d d7 5bstainput_selector_idx; Clear joystick char selector index
$5C868d d8 5bstainput_blink_counter; Clear blink counter
$5C8920 08 5djsrscan_keyboard; Scan keyboard, get initial key
$5C8C8d d4 5bstainput_last_key; Store last key state for debounce
$5C8F20 ec 5db_5C8Fjsrdraw_input_cursor; Draw blinking cursor on screen ; x-ref: $5CB1, $5CB8
$5C92a9 fblda#$fb
$5C94cd 12 d0b_5C94cmp$d012; Wait for raster line $FB (frame sync) ; x-ref: $5C97 Raster Position
$5C97d0 fbbneb_5C94
$5C9920 c4 5cjsrscan_keyboard_and_process_key; Scan keyboard for new keypress
$5C9Cad d3 5bldainput_done_flag; Key accepted? (done flag set)
$5C9Fd0 1abneb_5CBB
$5CA120 54 5djsrhandle_joystick_text_input; Check joystick for char selection
$5CA4ad d3 5bldainput_done_flag
$5CA7d0 12bneb_5CBB
$5CA9ee d8 5bincinput_blink_counter; Increment blink counter
$5CACad d8 5bldainput_blink_counter
$5CAFc9 28cmp#$28; Blinked 40 frames? (cursor toggle rate)
$5CB1d0 dcbneb_5C8F
$5CB3a9 00lda#$00; Reset blink counter
$5CB58d d8 5bstainput_blink_counter
$5CB84c 8f 5cjmpb_5C8F
$5CBBae d2 5bb_5CBBldxinput_char_count; Load char count index ; x-ref: $5C9F, $5CA7
$5CBEa9 00lda#$00; Clear char in buffer (null terminate)
$5CC09d d9 5bstainput_text_buffer,x
$5CC360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans the keyboard matrix via CIA1 and processes the resulting keypress.
; Debounces held keys, handles special keys ($FF = backspace, $FE = abort/done),
; and appends valid characters to the input buffer if not full.
;
; Inputs: None (reads keyboard hardware and internal state variables)
; Outputs: a_5BD4 (last key state), a_5BD3 (abort flag if $FE pressed)
; Side Effects: Writes character to screen via s_5DFF, updates input buffer
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
scan_keyboard_and_process_key
$5CC420 08 5djsrscan_keyboard; Scan keyboard matrix ; x-ref: $5C99
$5CC7d0 04bneb_5CCD; No key pressed?
$5CC98d d4 5bstainput_last_key; Clear last key state
$5CCC60rts
$5CCDcd d4 5bb_5CCDcmpinput_last_key; Same key still held? (debounce) ; x-ref: $5CC7
$5CD0d0 01bneb_5CD3
$5CD260rts; New key: store as current key state
$5CD38d d4 5bb_5CD3stainput_last_key; Store new key as last key ; x-ref: $5CD0
$5CD6c9 ffcmp#$ff; $FF = backspace key
$5CD8d0 03bneb_5CDD
$5CDA4c 24 5ejmpj_5E24; Handle backspace
$5CDDc9 feb_5CDDcmp#$fe; $FE = abort/done key ; x-ref: $5CD8
$5CDFd0 06bneb_5CE7
$5CE1a9 fflda#$ff; Signal input complete
$5CE38d d3 5bstainput_done_flag; Set abort/done flag
$5CE660rts
$5CE7ae d2 5bb_5CE7ldxinput_char_count; Check if buffer is full ; x-ref: $5CDF
$5CEAec d1 5bcpxinput_max_chars; Compare index vs max length
$5CEDd0 01bneb_5CF0
$5CEF60rts
$5CF0ae d2 5bb_5CF0ldxinput_char_count; Store char code in input buffer ; x-ref: $5CED
$5CF39d d9 5bstainput_text_buffer,x; Store char in input buffer
$5CF620 ff 5djsrdraw_char_at_cursor; Display char on screen
$5CF9ee d5 5bincinput_cursor_col; Advance screen cursor column
$5CFCee d2 5bincinput_char_count; Advance buffer write index
$5CFFa9 00lda#$00; Reset cursor blink counters
$5D018d d7 5bstainput_selector_idx
$5D048d d8 5bstainput_blink_counter
$5D0760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scans the keyboard matrix via CIA1 to detect a key press.
; Loops through all 8 rows of the keyboard matrix, outputting row-select
; masks to CIA1 Port A ($DC00) and reading column results from Port B ($DC01).
; If a key is detected (any column bit low), identifies the exact column and
; looks up the keycode from a decode table at ($FB/$FC).
;
; Inputs: None (uses hardcoded pointer $5C01 and row mask table f_5C41)
; Outputs: A = keycode from decode table (0 = no key pressed), Z flag set if no key
; Side Effects: Temporarily sets CIA1 DDRA ($DC02) to output then back to input
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5D08a9 01scan_keyboardlda#$01; Set up pointer to keyboard decode table (low byte) ; x-ref: $5C89, $5CC4
$5D0A85 fbstazp_ptr_src_lo
$5D0Ca9 5clda#$5c; Decode table pointer high byte = $5C
$5D0E85 fcstazp_ptr_src_hi
$5D10a2 00ldx#$00; X = keyboard row index (0-7)
$5D12a9 ffb_5D12lda#$ff; x-ref: $5D4F
$5D148d 02 dcsta$dc02; Set Port A as all outputs for row select; CIA1: Data Direction Register A
$5D17bd 41 5cldakeyboard_row_masks,x; Output row-select mask (one bit low per row)
$5D1A8d 00 dcsta$dc00; CIA1: Data Port Register A
$5D1Dad 01 dclda$dc01; Read column result from keyboard matrix; CIA1: Data Port Register B
$5D2085 ffstazp_temp; Save column result temporarily
$5D22a9 00lda#$00; Restore Port A as all inputs
$5D248d 02 dcsta$dc02; CIA1: Data Direction Register A
$5D27a5 ffldazp_temp
$5D29c9 ffcmp#$ff; No key pressed in this row?
$5D2Bf0 12beqb_5D3F; Yes, skip to next row
$5D2Da0 00ldy#$00; Y = column index
$5D2F19 41 5cj_5D2Forakeyboard_row_masks,y; OR with column mask to isolate pressed key ; x-ref: $5D39
$5D32c9 ffcmp#$ff; All bits high = this column not pressed
$5D34d0 06bneb_5D3C; Found the pressed column
$5D36a5 ffldazp_temp; Reload column result and try next column
$5D38c8iny
$5D394c 2f 5djmpj_5D2F
$5D3Cb1 fbb_5D3Clda(zp_ptr_src_lo),y; Look up keycode from decode table[Y] ; x-ref: $5D34
$5D3E60rts; Return with keycode in A
$5D3Fa5 fbb_5D3Fldazp_ptr_src_lo; Advance pointer by 8 (next row in table) ; x-ref: $5D2B
$5D4118clc
$5D4269 08adc#$08
$5D4485 fbstazp_ptr_src_lo
$5D46a5 fcldazp_ptr_src_hi; Handle carry into high byte
$5D4869 00adc#$00
$5D4A85 fcstazp_ptr_src_hi
$5D4Ce8inx
$5D4De0 08cpx#$08; All 8 rows scanned?
$5D4Fd0 c1bneb_5D12; No, scan next row
$5D51a9 00lda#$00; No key pressed, return 0
$5D5360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles joystick input for text entry. Reads joystick and processes:
; - Left: delete last entered character (backspace)
; - Fire: confirm currently selected character, store in buffer, advance cursor
; - Up: cycle character selection backward through allowed characters
; - Down: cycle character selection forward through allowed characters
; Uses f_5C49 as a character lookup table (space, A-Z, 0-9, punctuation).
; Sets a_5BD3 = $FF when input is complete (max length reached after fire).
;
; Inputs: joystick_state, joystick_prev_state, a_5BD1 (max length),
; a_5BD2 (current position), a_5BD7 (char selection index)
; Outputs: a_5BD2 (updated position), a_5BD3 (done flag), a_5BD7 (updated
; char index), a_5BD8 (blink counter reset), f_5BD9 (input buffer)
; Side Effects: Writes selected character to screen via s_5DFF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_joystick_text_input
$5D5420 82 3ejsrread_joystick; poll joystick ; x-ref: $5CA1
$5D57ad 80 3eldajoystick_state
$5D5A29 04and#$04; check left direction (bit 2)
$5D5Cd0 0abneb_5D68
$5D5Ead 81 3eldajoystick_prev_state; was left previously released?
$5D6129 04and#$04
$5D63f0 03beqb_5D68
$5D654c 24 5ejmpj_5E24; left pressed: backspace
$5D68ad d2 5bb_5D68ldainput_char_count; current input position ; x-ref: $5D5C, $5D63
$5D6Bcd d1 5bcmpinput_max_chars; compare with max length
$5D6Ed0 01bnehandle_joy_fire_confirm
$5D7060rts; input buffer full, exit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles joystick fire, up, and down inputs for character selection.
; Fire confirms the selected character and stores it in the buffer.
; Up/Down cycle through the character set (A-Z, 0-9, punctuation).
; Falls through from s_5D54 when buffer is not full.
;
; Inputs: joystick_state, joystick_prev_state, a_5BD7 (selector index)
; Outputs: f_5BD9 (char buffer), a_5BD2 (char count), a_5BD3 (done flag)
; Side Effects: Draws selected character to screen memory
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_joy_fire_confirm
$5D71ad 80 3eldajoystick_state; Fire button pressed? (bit 4) ; x-ref: $5D6E
$5D7429 10and#$10; check fire button (bit 4)
$5D76d0 33bnehandle_joy_up_prev_char; Fire not pressed, check up/down
$5D78ad 81 3eldajoystick_prev_state; Edge detect: was fire just released?
$5D7B29 10and#$10
$5D7Df0 2cbeqhandle_joy_up_prev_char; No edge, skip to up/down handler
$5D7Fae d7 5bldxinput_selector_idx; Load selector index
$5D82bd 49 5cldachar_selection_table,x; Get char code from selection table
$5D85ae d2 5bldxinput_char_count; Load current buffer position
$5D889d d9 5bstainput_text_buffer,x; Store confirmed char in buffer
$5D8B20 ff 5djsrdraw_char_at_cursor; Draw char on screen
$5D8Eee d5 5bincinput_cursor_col; Advance screen column
$5D91ee d2 5bincinput_char_count; Advance char count
$5D94ad d2 5bldainput_char_count; Check if buffer full
$5D97cd d1 5bcmpinput_max_chars; Compare count to max chars
$5D9Ad0 06bneb_5DA2
$5D9Ca9 fflda#$ff; Set done flag = $FF (input complete)
$5D9E8d d3 5bstainput_done_flag; set done flag
$5DA160rts
$5DA2a9 00b_5DA2lda#$00; Reset selector to first char (space) ; x-ref: $5D9A
$5DA48d d7 5bstainput_selector_idx
$5DA78d d8 5bstainput_blink_counter; Reset blink counter
$5DAA60rts
handle_joy_up_prev_char
$5DABad 80 3eldajoystick_state; Up pressed? (bit 0) ; x-ref: $5D76, $5D7D
$5DAE29 01and#$01; check up direction (bit 0)
$5DB0d0 17bnehandle_joy_down_next_char; Not pressed, check down
$5DB2ad 81 3eldajoystick_prev_state; Edge detect: was up just released?
$5DB529 01and#$01
$5DB7f0 10beqhandle_joy_down_next_char; No edge, check down
$5DB9ce d7 5bdecinput_selector_idx; Decrement selector index
$5DBC10 05bplb_5DC3
$5DBEa9 28lda#$28; Wrap: 0-1 → 40 ($28)
$5DC08d d7 5bstainput_selector_idx
$5DC3a9 00b_5DC3lda#$00; Reset blink counter ; x-ref: $5DBC
$5DC58d d8 5bstainput_blink_counter
$5DC860rts
handle_joy_down_next_char
$5DC9ad 80 3eldajoystick_state; Down pressed? (bit 1) ; x-ref: $5DB0, $5DB7
$5DCC29 02and#$02; check down direction (bit 1)
$5DCEd0 1bbner_5DEB; Not pressed, return
$5DD0ad 81 3eldajoystick_prev_state; Edge detect: was down just released?
$5DD329 02and#$02
$5DD5f0 14beqr_5DEB; No edge, return
$5DD7ee d7 5bincinput_selector_idx; Increment selector index
$5DDAad d7 5bldainput_selector_idx; Read back for range check
$5DDDc9 29cmp#$29; Wrap: 41 ($29) → 0
$5DDFd0 05bneb_5DE6
$5DE1a9 00lda#$00
$5DE38d d7 5bstainput_selector_idx
$5DE6a9 00b_5DE6lda#$00; Reset blink counter ; x-ref: $5DDF
$5DE88d d8 5bstainput_blink_counter
$5DEB60r_5DEBrts; x-ref: $5DCE, $5DD5
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the blinking cursor for the high score name entry.
; If the blink counter is >= 20, it draws a space (blink off).
; Otherwise, it draws the currently selected joystick character, or the default
; cursor character ($1E) if no character is selected (index 0).
; Falls through to `draw_char_at_cursor` to write to screen RAM.
;
; Inputs: input_blink_counter, input_selector_idx
; Outputs: None
; Side Effects: Writes one character to Screen RAM
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5DECa9 20draw_input_cursorlda#$20; Default to space (blink off) ; x-ref: $5C8F
$5DEEae d8 5bldxinput_blink_counter; Load blink counter
$5DF1e0 14cpx#$14; If >= 20, blink is off, draw space
$5DF3b0 0abcsdraw_char_at_cursor
$5DF5a9 1elda#$1e; Otherwise, set char to cursor ($1E)
$5DF7ae d7 5bldxinput_selector_idx; Load joystick selector index
$5DFAf0 03beqdraw_char_at_cursor; If 0, draw cursor char
$5DFCbd 49 5cldachar_selection_table,x; Else get selected letter
; Draws the character in A at screen position (row=a_5BD6, col=a_5BD5).
; Computes screen address: $0400 + row*40 + col, then stores the character.
$5DFF48draw_char_at_cursorpha; Save char to draw ; x-ref: $5CF6, $5D8B, $5DF3, $5DFA, $5E2B, ...
$5E00a9 00lda#$00; Screen base low = $00
$5E0285 fbstazp_ptr_src_lo
$5E04a9 04lda#$04; Screen base high = $04 ($0400)
$5E0685 fcstazp_ptr_src_hi
$5E08ac d6 5bldyinput_cursor_row; Y = row number
$5E0Bf0 10beqb_5E1D; Row 0? Skip multiply
$5E0Da5 fbb_5E0Dldazp_ptr_src_lo; Add 40 ($28) per row to pointer ; x-ref: $5E1B
$5E0F18clc; Add 40 ($28) per row to base address
$5E1069 28adc#$28
$5E1285 fbstazp_ptr_src_lo
$5E14a5 fcldazp_ptr_src_hi
$5E1669 00adc#$00
$5E1885 fcstazp_ptr_src_hi
$5E1A88dey; Loop for each row
$5E1Bd0 f0bneb_5E0D
$5E1Dac d5 5bb_5E1Dldyinput_cursor_col; Y = column offset ; x-ref: $5E0B
$5E2068pla; Restore char
$5E2191 fbsta(zp_ptr_src_lo),y; Write char to screen
$5E2360rts
$5E24ad d2 5bj_5E24ldainput_char_count; x-ref: $5CDA, $5D65
$5E27f0 16beqr_5E3F
$5E29a9 20lda#$20
$5E2B20 ff 5djsrdraw_char_at_cursor
$5E2Ece d5 5bdecinput_cursor_col
$5E3120 ff 5djsrdraw_char_at_cursor
$5E34ce d2 5bdecinput_char_count
$5E37a9 00lda#$00
$5E398d d7 5bstainput_selector_idx
$5E3C8d d8 5bstainput_blink_counter
$5E3F60r_5E3Frts; x-ref: $5E27
.encode
.enc"screen"
; Credits screen: role labels ("PROGRAMMING, GRAPHICS, SOUND" / "TESTING" / "EMAIL")
$5E40txt_credits_roles.text"PROGRAMMING, GRAPHICS, SOUND", $ff, $ff, $ff, $ff, "TESTING", $ff; x-ref: $5ECE, $5ED2
$5E68.text$ff, $ff, $ff, $ff, $ff, "EMAIL@"
; Credits screen: programmer/tester name "LUCA CARMINATI"
$5E73txt_credits_luca.text"LUCA CARMINATI@"; x-ref: $5EE3, $5EE7, $5EF8, $5EFC
; Credits screen: additional names "STEFANO CARMINATI" / "PAOLO CARMINATI"
$5E82txt_credits_testers.text"STEFANO CARMINATI", $ff, "PAOLO CARMINATI", $ff, $ff, $ff, $ff, "LUC"; x-ref: $5F0D, $5F11
$5EAA.text"ACARMINATI1967$GMAIL.COM@"
.endencode
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Displays the credits/about screen showing programmer, tester, and contact
; information for the Carminati family. Blanks the screen, clears it, then
; draws four text blocks with color: the role labels (cyan) and name strings
; (white). Resets all sprites and re-enables the display via j_3667.
;
; Inputs: None
; Outputs: None
; Side Effects: Screen blanked and cleared, credits text drawn, sprites reset, display re-enabled
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$5EC3a9 05show_credits_screenlda#GameState.CREDITS; Set game state = 5 (credits screen) ; x-ref: $1DDF, $6AC1
$5EC58d d3 20stagame_state; game_state = 5 (credits screen)
$5EC820 7e 36jsrblank_screen; Blank display before drawing
$5ECB20 44 36jsrclear_screen; Clear screen RAM
$5ECEa9 40lda#<txt_credits_roles; Pointer to role labels text at $5E40
$5ED085 fbstazp_ptr_src_lo
$5ED2a9 5elda#>txt_credits_roles; Text ptr hi = $5E
$5ED485 fcstazp_ptr_src_hi
$5ED6a9 1elda#<SCREEN_RAM_R7C6; Screen dest $051E (row 7, col 6)
$5ED885 fdstazp_ptr_dst_lo
$5EDAa9 05lda#>SCREEN_RAM_R7C6; Screen offset hi = $05 -> offset $051E (row 16, col 30)
$5EDC85 festazp_ptr_dst_hi
$5EDEa9 03lda#VicIIColors.CYAN; Color = cyan
$5EE020 a8 36jsrdraw_text_with_color; Draw "PROGRAMMING, GRAPHICS, SOUND / TESTING / EMAIL"
$5EE3a9 73lda#<txt_credits_luca; Pointer to "LUCA CARMINATI" text at $5E73
$5EE585 fbstazp_ptr_src_lo
$5EE7a9 5elda#>txt_credits_luca; text ptr hi = $5E
$5EE985 fcstazp_ptr_src_hi
$5EEBa9 46lda#<SCREEN_RAM_R8C6; Screen dest $0546 (row 8, col 6)
$5EED85 fdstazp_ptr_dst_lo
$5EEFa9 05lda#>SCREEN_RAM_R8C6; screen pos hi = $05 → $0546
$5EF185 festazp_ptr_dst_hi
$5EF3a9 01lda#VicIIColors.WHITE; Color = white
$5EF520 a8 36jsrdraw_text_with_color; Draw programmer name
$5EF8a9 73lda#<txt_credits_luca; Reuse "LUCA CARMINATI" pointer
$5EFA85 fbstazp_ptr_src_lo
$5EFCa9 5elda#>txt_credits_luca; text ptr hi = $5E
$5EFE85 fcstazp_ptr_src_hi
$5F00a9 e6lda#<SCREEN_RAM_R12C6; Screen dest $05E6 (row 12, col 6)
$5F0285 fdstazp_ptr_dst_lo
$5F04a9 05lda#>SCREEN_RAM_R12C6; screen pos hi = $05 → $05E6
$5F0685 festazp_ptr_dst_hi; Color = 1 (white)
$5F08a9 01lda#VicIIColors.WHITE; color = 1 (white)
$5F0A20 a8 36jsrdraw_text_with_color; Draw tester name (same person)
$5F0Da9 82lda#<txt_credits_testers; Pointer to testers/email text at $5E82
$5F0F85 fbstazp_ptr_src_lo
$5F11a9 5elda#>txt_credits_testers; text ptr hi = $5E
$5F1385 fcstazp_ptr_src_hi
$5F15a9 0elda#<SCREEN_RAM_R13C6; Screen dest $060E (row 13, col 6)
$5F1785 fdstazp_ptr_dst_lo
$5F19a9 06lda#>SCREEN_RAM_R13C6; screen pos hi = $06 → $060E
$5F1B85 festazp_ptr_dst_hi
$5F1Da9 01lda#VicIIColors.WHITE; color = 1 (white)
$5F1F20 a8 36jsrdraw_text_with_color; Draw "STEFANO/PAOLO CARMINATI" + email
$5F2220 d5 53jsrreset_all_sprites; Clear all sprite positions and enable bits
$5F254c 67 36jmpenable_screen; Re-enable screen display
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Polls the joystick port on the credits/game-complete screens to check for a new
; fire button click.
; Ensures the click is a fresh transition (active-low logic: currently pressed (0)
; and previously released ($10)) to filter out button-holding.
; When a new click is registered, transitions back to show_main_menu.
;
; Inputs: joystick_state ($3E80), joystick_prev_state ($3E81)
; Outputs: None
; Side Effects: Jumps to show_main_menu if new fire click is detected
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
wait_for_credits_fire_click
$5F28ad 80 3eldajoystick_state; Read current joystick state ; x-ref: $2137
$5F2B29 10and#$10; Isolate fire button bit (bit 4)
$5F2Dd0 0abner_5F39; Active-low: fire released -> abort tick (RTS)
$5F2Fad 81 3eldajoystick_prev_state; Read previous frame joystick state
$5F3229 10and#$10; Isolate previous fire button bit
$5F34f0 03beqr_5F39; Zero = already pressed last frame (hold check), abort
$5F364c 5e 69jmpshow_main_menu; New fire click! Return back to main menu
$5F3960r_5F39rts; Return from screen wait tick ; x-ref: $5F2D, $5F34
.encode
.enc"screen"
; Congratulations typewriter text: "GREAT JOB!" ... "YOUR FINAL SCORE IS" ... "000000"
$5F3Agame_complete_text.text"^^^^^^^^GREAT JOB!^^^^^^^^", $fe, $ff, $ff; Great job
$5F57.text"^WITH YOUR HELP, RODERICK^", $ff; with your help, roderick
$5F72.text"^HAS SAVED ALL THE MINERS^", $ff; has saved all the miners
$5F8D.text"^^^TRAPPED IN THE MOUNT^^^", $ff; trapped in the mount
$5FA8.text"^^^^^^^LEONE MINES.^^^^^^^", $fe, $ff, $ff, $ff; leone mines.
$5FC6.text"YOU GET 10000 EXTRA POINTS", $ff; you get 10000 extra points
$5FE1.text"^^^FOR EACH LIFE LEFT.^^^^", $fe, $ff, $ff, $ff; for each life left
$5FFF.text"^^^YOUR FINAL SCORE IS^^^^", $fe, $ff, $ff; your final score is
$601C.text"^^^^^^^^^^000000^^^^^^^^^^@"; 000000
.endencode
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Displays the game completion screen when all miners have been rescued.
; Adds 10000 bonus points (BCD) per remaining life to the player's score,
; shows a congratulatory typewriter-scrolled message, and prints the final
; score. The text congratulates the player for saving all miners in the
; mount Leone mines.
;
; Inputs: a_50F7 (remaining lives count), score at $50F4-$50F6 (BCD)
; Outputs: Updated score at $50F4-$50F6, screen display
; Side Effects: Blanks and redraws screen, sets color RAM, resets sprites,
; starts typewriter text animation, re-enables display
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
show_game_complete_screen
$6037a9 06lda#GameState.GAME_COMPLETE; game phase = 6 (game complete) ; x-ref: $640A
$60398d d3 20stagame_state; game_state = 6 (game complete)
$603C20 7e 36jsrblank_screen
$603Fa0 01ldy#$01; color index 1 (white)
$604120 44 36jsrclear_screen
$6044a2 27show_level_completeldx#$27; 40 columns (0-$27)
$6046a9 07lda#VicIIColors.YELLOW; yellow
$60489d c8 d8b_6048staCOLOR_RAM_R5C0,x; Color row 5 = yellow (level complete header) ; x-ref: $604C
$604Bcadex
$604C10 fabplb_6048
$604Ea2 4fldx#$4f; 80 chars (rows 13-14)
$6050a9 07lda#VicIIColors.YELLOW; Yellow
$60529d 08 dab_6052staCOLOR_RAM_R13C0,x; Color rows 13-14 = yellow (level complete body) ; x-ref: $6056
$6055cadex
$605610 fabplb_6052
$6058a2 27ldx#$27; Fill 40 chars (1 row)
$605Aa9 03lda#VicIIColors.CYAN; cyan
$605C9d a8 dab_605CstaCOLOR_RAM_R17C0,x; Color row 17 = cyan (score line) ; x-ref: $6060
$605Fcadex
$606010 fabplb_605C
$606220 d5 53jsrreset_all_sprites; Hide all sprites
$6065ae f7 50ldxlives_count; X = remaining lives count
$6068f8b_6068sed; enable BCD mode for score addition ; x-ref: $6084
$6069ad f4 50ldascore_lo; Score low byte
$606C18clc
$606D69 00adc#$00; Add 100 points (BCD) per dynamite
$606F8d f4 50stascore_lo
$6072ad f5 50ldascore_mid; Score mid byte
$607569 00adc#$00; Add 0 (carry propagation)
$60778d f5 50stascore_mid
$607Aad f6 50ldascore_hi; Score high byte
$607D69 01adc#$01; add $01 to high byte = +10000 BCD per life
$607F8d f6 50stascore_hi
$6082d8cld; back to binary mode
$6083cadex; Loop for each dynamite stick
$6084d0 e2bneb_6068; Loop done; now set up score print pointers
$6086a9 f4lda#$f4; source = score bytes at $50F4
$608885 fbstazp_ptr_src_lo
$608Aa9 50lda#$50; Source ptr hi = $50
$608C85 fcstazp_ptr_src_hi; Ptr to screen score position ($6026)
$608Ea9 26lda#$26; dest = screen at $6026 (score display area)
$609085 fdstazp_ptr_dst_lo
$6092a9 60lda#$60
$609485 festazp_ptr_dst_hi; Dest ptr hi = $60
$6096a2 06ldx#$06; 6 BCD digits to print
$609820 0e 37jsrprint_bcd_number; Render score to screen-code buffer at $6026
$609Ba9 3alda#$3a; text ptr = $5F3A (congratulations message)
$609D85 f5stazp_ptr_text_lo
$609Fa9 5flda#$5f; Text ptr hi = $5F
$60A185 f6stazp_ptr_text_hi
$60A3a9 cflda#$cf; screen dest = $04CF (row 5, centered)
$60A585 f7stazp_ptr_screen_lo
$60A7a9 04lda#$04; Screen dest hi = $04
$60A985 f8stazp_ptr_screen_hi
$60AB20 5b 37jsrinit_typewriter; Start typewriter text effect
$60AEa2 3cldx#$3c; wait 60 frames (~1 second) before starting
$60B020 98 36jsrwait_raster_frames
$60B34c 67 36jmpenable_screen; re-enable the display
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the typewriter text effect or skips it on fire-button press.
; While the typewriter animation is still running, delegates to update_typewriter.
; Once the typewriter is finished, polls for a fire-button press (rising edge);
; when detected, silences the SID and transitions to the next game phase via j_62A4.
;
; Inputs: joystick_state, joystick_prev_state, typewriter_done_flag
; Outputs: None
; Side Effects: Calls update_all_sfx every frame; may call sid_init and jump to j_62A4
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_typewriter_or_skip
$60B620 3a 1ejsrupdate_all_sfx; always update sound effects first ; x-ref: $213A
$60B9ad 58 37ldatypewriter_done_flag
$60BCf0 15beqb_60D3; 0 = typewriter still running
$60BEad 80 3eldajoystick_state; check joystick fire button (bit 4)
$60C129 10and#$10; isolate fire button bit
$60C3d0 0dbner_60D2; fire not pressed this frame → wait
$60C5ad 81 3eldajoystick_prev_state; check previous frame's fire state
$60C829 10and#$10
$60CAf0 06beqr_60D2; fire was already held → wait (need rising edge)
$60CC20 15 1ejsrsid_init; silence all SID voices
$60CF4c a4 62jmpj_62A4; transition to next game phase
$60D260r_60D2rts; x-ref: $60C3, $60CA
$60D34c 69 37b_60D3jmpupdate_typewriter; continue typewriter animation ; x-ref: $60BC
; Flag: 0 = setup/menu phase, 1 = gameplay active. Guards keyboard input handling.
$60D6game_active.byte$00; x-ref: $611F, $6173, $619C, $61B6
; Menu input mode index (0-3). Cycles through menu options; selects 30-byte option block from f_677D table.
$60D7input_mode.byte$00; x-ref: $6A8C, $6A8F, $6A98, $6B58, $802F
; Difficulty mode: 0 = normal (20 levels), 4 = easy (7), 8 = medium (11), 12 = hard (15). Set from menu, determines final level threshold.
$60D8difficulty_mode.byte$00; x-ref: $1DB8, $610C, $6169, $6387, $6A82, ...
; Current cave/level index (0-19). Used for cave data lookup, palette selection, enemy speed scaling, and score entries.
$60D9current_level.byte$00; x-ref: $1CA3, $49F6, $53B6, $610F, $6379, ...
; Flag: non-zero when a game session is active. Set to $FF on dynamite bonus award. Checked on RUN/STOP to decide whether to save high scores.
$60DAgame_in_progress.byte$00; x-ref: $6114, $6191, $62A9, $63F8
; Raster synchronization flag. Main loop clears to 0, then busy-waits until the IRQ handler sets it to $FF, ensuring one frame per game tick.
$60DBframe_sync_flag.byte$00; x-ref: $61C3, $61C6, $64BB
; Level-complete bonus sequence phase counter. 0 = normal play, 1 = energy bonus, 2 = life bonus, 3 = delay before next level.
$60DCbonus_phase.byte$00; x-ref: $6127, $61F2, $6218, $6221, $6242, ...
; Index (0-7) of the first free VIC-II sprite slot found during sprite sorting. Used to assign sprite pointers.
$60DDactive_sprite_slot.byte$00; x-ref: $64C8, $64D8
; Multi-purpose: hero Y start position during init ($78/$F0), frame counter during bonus phase 2, delay timer during bonus phase 3, death anim countdown.
$60DEhero_y_or_timer.byte$00; x-ref: $612C, $6178, $6247, $6259, $625C, ...
; SFX mute toggle. 0 = sound on, $FF = muted. Toggled by F7 key (EOR #$FF). When set, SID volume is zeroed and SFX updates are skipped.
$60DFsfx_muted.byte$00; x-ref: $6117, $61A7, $61AC, $61CB, $64CB
; HUD tile indices for fuel bar icons (7 bytes, null-terminated)
$60E0hud_tiles_fuel_bar.byte$54, $55, $56, $57, $58, $59, $00; x-ref: $62B7, $62BB
; HUD tile indices for status icons row (8 bytes, null-terminated)
hud_tiles_status_icons
$60E7.byte$64, $65, $66, $67, $68, $69, $6a, $6b; x-ref: $62DF, $62E3
$60EF.byte$00
; HUD tile indices for top status row (dynamite/lives icons)
$60F0hud_tiles_row1.byte$6c, $6d, $6e, $6f, $70, $00; x-ref: $6304, $6308
; HUD tile indices for bottom status row (fuel bar icons)
$60F6hud_tiles_row2.byte$71, $72, $73, $74, $75, $76, $77, $78; x-ref: $6319, $631D
$60FE.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes a new cave/level for gameplay. Blanks the screen, resets the
; score and lives display, loads and decompresses cave map data from banked
; RAM, sets up sprites, clears the screen, configures the raster IRQ for
; the gameplay handler, and re-enables the display.
; j_611D is a re-entry point used when restarting the same cave (e.g. after
; losing a life) — it skips the score/lives reset.
;
; Inputs: a_60D8 = cave/level index to initialize
; Outputs: None
; Side Effects: Screen blanked then re-enabled, sprites configured, cave map
; decompressed, raster IRQ set to gameplay handler at $6492,
; MMU set to $3E (RAM+I/O)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$60FFa9 00init_cave_levellda#GameState.GAMEPLAY; clear transition state ; x-ref: $1DBB, $6A85, $6ABB
$61018d d3 20stagame_state; game_state = 0 (playing cave)
$610420 7e 36jsrblank_screen; turn off VIC-II display
$6107a9 fflda#$ff; mark all cave blocks dirty
$61098d 38 36stazero_suppress_disable; Mark all blocks dirty (force full redraw)
$610Cad d8 60ldadifficulty_mode; Copy difficulty_mode to current_level as starting level
$610F8d d9 60stacurrent_level
$6112a9 00lda#$00; clear continue flag
$61148d da 60stagame_in_progress; Clear game_in_progress flag at level init
$61178d df 60stasfx_muted; Clear sfx_muted (sound enabled)
$611A20 ff 50jsrinit_score_and_lives; init score/lives display
$611Da9 00j_611Dlda#$00; clear game-active flag (setup phase) ; x-ref: $620B, $6410
$611F8d d6 60stagame_active; Clear game_active (setup phase)
$612220 7e 36jsrblank_screen; blank screen for level setup
$6125a9 00lda#$00; clear scroll counter
$61278d dc 60stabonus_phase; Clear bonus_phase (normal play)
$612Aa9 78lda#$78; init hero Y start position = 120
$612C8d de 60stahero_y_or_timer; Init hero_y_or_timer to 120 (hero start Y)
$612Fa9 06lda#$06; set initial fuel/energy = 6
$61318d 06 7bstahero_dynamite_count
$613420 3d 78jsrinit_dynamite_state; setup cave tilemap renderer
$613720 48 82jsrinit_energy; init animation tables
$613Aa9 02lda#VicIIColors.RED; set multicolor bg1 = red
$613C8d 22 d0sta$d022; Background Color 1, Multi-Color Register 0
$613F20 d5 53jsrreset_all_sprites; clear all sprite positions/data
$6142a9 fflda#$ff; enable all 8 sprites
$61448d 15 d0sta$d015; Sprite display Enable
$614720 f2 53jsrinit_sprites; configure sprite frames/colors
$614Aa0 0fldy#$0f; fill color = light grey
$614C20 44 36jsrclear_screen; clear screen RAM
$614F20 b7 62jsrdraw_hud; draw cave border/HUD layout
$615220 a3 1cjsrload_cave_data; load compressed cave data from banked RAM
$6155a9 3elda#$3e; MMU = $3E: RAM+I/O (restore after bank switch)
$61578d 00 ffstaMMU_CONFIG; Restore MMU after bank switch; C128: MMU Configuration Register
$615A20 c9 49jsrdecompress_cave_map; expand cave map into screen tiles
$615Da9 05lda#$05; raster line 5 for gameplay IRQ
$615Fa2 92ldx#<irq_gameplay_raster
$6161a0 64ldy#>irq_gameplay_raster; handler at $6492
$616320 4c 21jsrsetup_raster_irq; install gameplay raster IRQ
$61664c 67 36jmpenable_screen; enable VIC-II display and return
$6169ad d8 60j_6169ldadifficulty_mode; x-ref: $6211
$616Cf0 03beqb_6171
$616E4c 5e 69jmpshow_main_menu
$6171a9 01b_6171lda#$01; x-ref: $616C
$61738d d6 60stagame_active; Set game_active = 1 (gameplay begins)
$6176a9 f0lda#$f0
$61788d de 60stahero_y_or_timer
$617B60rts
$617C20 09 52j_617Cjsrj_5209; x-ref: $620E
$617F4c 5f 7bjmpj_7B5F
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; The primary game engine loop, executed once per frame. Coordinates keyboard
; checks (RUN/STOP to exit, F7 to toggle audio mute), busy-waits on the
; frame_sync_flag set by the raster IRQ for vertical sync, and updates all gameplay
; systems (physics, hero movement, collisions, energy drain, scrolling, animations).
; Manages transitions on hero death (lives decrement, level restart or game over
; transition) and triggers the step-by-step level complete bonus sequence when the
; hero successfully completes the cave.
;
; Inputs: None
; Outputs: None
; Side Effects: Performs BCD score additions, updates SID registers for SFX, reads
; keyboard row 0, processes transitions to restarts/gameover screens
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6182ad 0c 3fgame_engine_loopldakb_row0_cur; Read keyboard matrix row 0 ; x-ref: $2114
$6185cd 0d 3fcmpkb_row0_prev; Check if keys changed
$6188f0 2cbeqb_61B6
$618Ac9 efcmp#$ef; RUN/STOP key pressed?
$618Cd0 0ebneb_619C
$618E20 52 1ejsrstop_all_music; Silence SID voices before quitting
$6191ad da 60ldagame_in_progress; Check if a game is actively running
$6194f0 03beqjump_to_title_screen
$619620 dd 6ejsrshow_saving_and_save_scores
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Trampoline to title screen.
; Reached when RUN/STOP is pressed and no game is in progress (a_60DA == 0),
; skipping the high-score save performed by s_6EDD.
;
; Inputs: None
; Outputs: None (does not return)
; Side Effects: Transfers control to j_695E which blanks the screen and
; shows the title/menu.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$61994c 5e 69jump_to_title_screenjmpshow_main_menu; Go straight back to title screen menu ; x-ref: $6194
$619Cae d6 60b_619Cldxgame_active; Check if gameplay is active (vs menu/demo) ; x-ref: $618C
$619Fd0 15bneb_61B6
$61A1c9 f7cmp#$f7; F7 key pressed (toggle audio)?
$61A3d0 11bneb_61B6
$61A5a2 00ldx#$00
$61A7ad df 60ldasfx_muted; Toggle audio mute flag
$61AA49 ffeor#$ff
$61AC8d df 60stasfx_muted
$61AFd0 02bneb_61B3
$61B1a2 0fldx#$0f
$61B38e 18 d4b_61B3stx$d418; x-ref: $61AF Select Filter Mode and Volume
$61B6ad d6 60b_61B6ldagame_active; Check if gameplay is active ; x-ref: $6188, $619F, $61A3
$61B9d0 03bneb_61BE; Demo/Game-Over mode active -> JMP to animation
$61BB4c c1 61jmpj_61C1
$61BE4c 95 62b_61BEjmpupdate_game_over_or_demo; x-ref: $61B9
$61C1a9 00j_61C1lda#$00; Gameplay active: clear IRQ sync flag ; x-ref: $61BB
$61C38d db 60staframe_sync_flag; Clear frame_sync_flag (wait for next IRQ)
$61C6ad db 60b_61C6ldaframe_sync_flag; Busy-wait for raster IRQ to set frame_sync_flag ; x-ref: $61C9
$61C9f0 fbbeqb_61C6
$61CBad df 60ldasfx_muted
$61CEf0 01beqb_61D1; Update player scores and level stats
$61D060rts
$61D120 2f 51b_61D1jsrupdate_score_and_lives; Animate water characters ; x-ref: $61CE
$61D420 20 7ajsrupdate_water_animation; Tick player character physics & inputs
$61D720 9b 7bjsrupdate_hero_state_machine; Check player-placed dynamite collisions
$61DA20 4b 78jsrupdate_dynamite_collision; Process active explosion animations
$61DD20 19 6fjsrupdate_explosion; Tick global animated level elements
$61E020 a5 73jsrupdate_all_tiles; Scroll specific level backgrounds
$61E320 fa 82jsrupdate_tile13_scroll; Animate interactive raft structures
$61E620 c8 84jsrupdate_raft_animation; Drain player energy meter over time
$61E920 67 82jsrupdate_energy_drain; Check if player touched enemies/hazards
$61EC20 70 1ejsrcheck_hero_collisions; Assign updated player sprite pointers
$61EF20 42 81jsrselect_hero_sprite_ptr; Is the level complete bonus sequence active?
$61F2ad dc 60ldabonus_phase; Phase 0: evaluate player/game status
$61F5f0 03beqb_61FA; Phase 0: check game status
$61F74c 21 62jmpupdate_level_complete_bonus; Get hero current status flag
$61FAad fa 7ab_61FAldahero_state; Did the hero die? ; x-ref: $61F5
$61FDc9 05cmp#$05; Status $05 = hero died?
$61FFd0 13bneb_6214
$6201ce f7 50declives_count; No lives left -> trigger Game Over
$6204f0 0bbeqb_6211; No lives left -> game over
$6206ad 1b 82ldaenergy_level; Check remaining energy after death
$6209d0 03bneb_620E; Energy > 0: restart with score bonus
$620B4c 1d 61jmpj_611D; Restart cave with score compensation
$620E4c 7c 61b_620Ejmpj_617C; Trigger Game Over sequence ; x-ref: $6209
$62114c 69 61b_6211jmpj_6169; Is the cave successfully completed? ; x-ref: $6204
$6214c9 03b_6214cmp#$03; x-ref: $61FF
$6216d0 08bner_6220; Status $03 = level complete?
$6218ee dc 60incbonus_phase; Begin death/bonus phase 1
$621Ba9 01lda#$01
$621D8d 9d 8astasfx_ch5_state; Set SFX trigger flag
$622060r_6220rts; x-ref: $6216
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Processes the three-phase level completion bonus sequence.
; Phase 1 drains the remaining player energy, adding BCD score points per tick and
; playing a sound chime.
; Phase 2 drains the remaining dynamite stock, adding larger BCD score bonuses per
; unit.
; Phase 3 introduces a short delay timer before transitioning to the next cave layout
; via advance_level.
;
; Inputs: None
; Outputs: None
; Side Effects: Modifies energy_level, hero_dynamite_count, and adds BCD bonuses
; to score. Modifies SID voice registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_level_complete_bonus
$6221ad dc 60ldabonus_phase; Phase 1: check current phase ; x-ref: $61F7
$6224c9 01cmp#$01
$6226d0 25bneb_624D; Phase != 1: skip to phase 2 check
$6228ad 1b 82ldaenergy_level; Check energy for level-complete bonus
$622Bf0 15beqb_6242; Energy depleted -> advance phase
$622Dce 1b 82decenergy_level; Convert remaining energy to score bonus
$623020 8e 82jsrupdate_energy_bar_display
$6233a9 40lda#$40; Score += $0040 (energy bonus)
$623585 fbstazp_ptr_src_lo
$6237a9 00lda#$00
$623985 fcstazp_ptr_src_hi
$623Ba9 00lda#$00
$623D85 fdstazp_ptr_dst_lo
$623F4c d8 51jmpj_51D8; Add to BCD score
$6242ee dc 60b_6242incbonus_phase; Phase 2: advance phase counter ; x-ref: $622B
$6245a9 00lda#$00; Clear animation frame counter
$62478d de 60stahero_y_or_timer
$624A4c e7 8ajmpstop_sfx_ch5; Stop SFX (SID voice 1 off)
$624Dad dc 60b_624Dldabonus_phase; Phase 2: check current phase ; x-ref: $6226
$6250c9 02cmp#$02
$6252d0 38bneb_628C; Phase != 2: skip to phase 3
$6254ad 06 7bldahero_dynamite_count; Check remaining lives for bonus
$6257f0 2abeqb_6283; No lives left -> advance phase
$6259ee de 60inchero_y_or_timer; Increment frame counter
$625Cad de 60ldahero_y_or_timer
$625Fc9 0dcmp#$0d; Every 13 frames: award one life bonus
$6261d0 1fbner_6282
$6263a9 00lda#$00
$62658d de 60stahero_y_or_timer
$626820 5f 63jsrclear_last_dynamite_icon; Erase life icon from status bar
$626Bce 06 7bdechero_dynamite_count; Decrement remaining lives display
$626Ea9 50lda#$50; Score += $0050 (life bonus)
$627085 fbstazp_ptr_src_lo
$6272a9 00lda#$00
$627485 fcstazp_ptr_src_hi
$6276a9 00lda#$00
$627885 fdstazp_ptr_dst_lo
$627A20 d8 51jsrj_51D8; Add to BCD score
$627Da9 01lda#$01; Set score-changed flag
$627F8d ec 88stasfx_hit_state; Trigger hit SFX on Voice 3
$628260r_6282rts; x-ref: $6261
$6283ee dc 60b_6283incbonus_phase; Advance to phase 3 ; x-ref: $6257
$6286a9 5alda#$5a; Set phase 3 delay timer ($5A frames)
$62888d de 60stahero_y_or_timer
$628B60rts
$628Cce de 60b_628Cdechero_y_or_timer; Phase 3: decrement delay timer ; x-ref: $6252
$628Fd0 03bner_6294
$62914c 87 63jmpadvance_level; Timer done -> advance to next level
$629460r_6294rts; x-ref: $628F
update_game_over_or_demo
$629520 20 7ajsrupdate_water_animation; x-ref: $61BE
$629820 a5 73jsrupdate_all_tiles
$629B20 c8 84jsrupdate_raft_animation
$629Ece de 60dechero_y_or_timer
$62A1f0 01beqj_62A4
$62A360rts
$62A420 3b 53j_62A4jsrinsert_high_score; x-ref: $60CF, $62A1
$62A710 0bbplb_62B4
$62A9ad da 60ldagame_in_progress
$62ACf0 03beqb_62B1
$62AE20 dd 6ejsrshow_saving_and_save_scores
$62B14c 5e 69b_62B1jmpshow_main_menu; x-ref: $62AC
$62B44c 3e 6db_62B4jmpinit_high_score_screen; x-ref: $62A7
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the static HUD (Heads-Up Display) layout onto the screen.
; Sets up the text, icons (dynamite, fuel bar, year/logo), and their colors.
; Calls subroutines to draw dynamic HUD elements like score, lives, and dynamite counts.
;
; Inputs: None
; Outputs: None
; Side Effects: Extensively modifies Screen RAM and Color RAM
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$62B7a9 e0draw_hudlda#<hud_tiles_fuel_bar; Source = $60E0 ; x-ref: $614F
$62B985 fbstazp_ptr_src_lo
$62BBa9 60lda#>hud_tiles_fuel_bar
$62BD85 fcstazp_ptr_src_hi
$62BFa9 d1lda#<SCREEN_RAM_R18C1; Destination = $06D1
$62C185 fdstazp_ptr_dst_lo
$62C3a9 06lda#>SCREEN_RAM_R18C1
$62C585 festazp_ptr_dst_hi
$62C7a9 01lda#VicIIColors.WHITE; Draw text in White (1)
$62C920 a8 36jsrdraw_text_with_color
$62CC20 a0 82jsrfill_energy_bar; Draw player score
$62CF20 09 52jsrj_5209; Draw player lives
$62D2a2 0eldx#$0e; Set Color RAM for row 20 to White
$62D4a9 01lda#VicIIColors.WHITE
$62D69d 25 dbb_62D6staCOLOR_RAM_R20C5,x; Set HUD color row 20 = white ; x-ref: $62DA
$62D9cadex
$62DA10 fabplb_62D6
$62DC20 49 63jsrdraw_dynamite_stock; Draw dynamite stock
$62DFa9 e7lda#<hud_tiles_status_icons; Source = $60E7
$62E185 fbstazp_ptr_src_lo
$62E3a9 60lda#>hud_tiles_status_icons
$62E585 fcstazp_ptr_src_hi
$62E7a9 74lda#<SCREEN_RAM_R22C4; Destination = $0774
$62E985 fdstazp_ptr_dst_lo
$62EBa9 07lda#>SCREEN_RAM_R22C4
$62ED85 festazp_ptr_dst_hi
$62EFa9 07lda#VicIIColors.YELLOW; Draw text in Yellow (7)
$62F120 a8 36jsrdraw_text_with_color
$62F420 6f 63jsrdraw_level_number
$62F720 f4 51jsrdraw_score; Update score display
$62FAa2 0bldx#$0b
$62FCa9 01lda#VicIIColors.WHITE; Set Color RAM for row 22 to White
$62FE9d 88 dbb_62FEstaCOLOR_RAM_R22C24,x; Set HUD color row 22 = white ; x-ref: $6302
$6301cadex
$630210 fabplb_62FE
$6304a9 f0lda#<hud_tiles_row1; Draw dynamite icons (row 1)
$630685 fbstazp_ptr_src_lo
$6308a9 60lda#>hud_tiles_row1
$630A85 fcstazp_ptr_src_hi
$630Ca9 c1lda#<SCREEN_RAM_R24C1
$630E85 fdstazp_ptr_dst_lo
$6310a9 07lda#>SCREEN_RAM_R24C1
$631285 festazp_ptr_dst_hi
$6314a9 01lda#VicIIColors.WHITE
$631620 a8 36jsrdraw_text_with_color
$6319a9 f6lda#<hud_tiles_row2; Draw fuel bar (row 2)
$631B85 fbstazp_ptr_src_lo
$631Da9 60lda#>hud_tiles_row2
$631F85 fcstazp_ptr_src_hi
$6321a9 d0lda#<SCREEN_RAM_R24C16
$632385 fdstazp_ptr_dst_lo
$6325a9 07lda#>SCREEN_RAM_R24C16
$632785 festazp_ptr_dst_hi
$6329a9 01lda#VicIIColors.WHITE
$632B20 a8 36jsrdraw_text_with_color
$632Ea9 09lda#<hud_year_and_logo; Draw copyright year and logo
$633085 fbstazp_ptr_src_lo
$6332a9 68lda#>hud_year_and_logo
$633485 fcstazp_ptr_src_hi
$6336a9 e3lda#<SCREEN_RAM_R24C35
$633885 fdstazp_ptr_dst_lo
$633Aa9 07lda#>SCREEN_RAM_R24C35
$633C85 festazp_ptr_dst_hi
$633Ea9 01lda#VicIIColors.WHITE
$634020 a8 36jsrdraw_text_with_color
$6343a9 03lda#VicIIColors.CYAN; Set color of logo/year to Cyan (3)
$63458d c1 dbstaCOLOR_RAM_R24C1; Set single color cell row 24 col 1 = cyan
$634860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the player's current stock of dynamite onto the HUD.
; The icons are constructed using two characters ($62 top, $63 bottom).
; Loops from `hero_dynamite_count * 2 - 2` down to 0, writing to screen RAM.
;
; Inputs: hero_dynamite_count
; Outputs: None
; Side Effects: Modifies Screen RAM (dynamite icon area)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6349ad 06 7bdraw_dynamite_stockldahero_dynamite_count; Load number of dynamites ; x-ref: $62DC
$634C0aasla; Multiply by 2 (2 bytes per icon width)
$634Daatax
$634Ecadex; Adjust for 0-based index
$634Fcadex
$6350a9 62b_6350lda#$62; Top half character ($62) ; x-ref: $635C
$63529d 11 07staSCREEN_RAM_R19C25,x
$6355a9 63lda#$63; Bottom half character ($63)
$63579d 39 07staSCREEN_RAM_R20C25,x
$635Acadex; Move left to next icon slot
$635Bcadex
$635C10 f2bplb_6350; Loop until all icons drawn
$635E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Erases the right-most dynamite icon on the HUD by overwriting both its top
; and bottom halves with spaces ($20). Called when the player uses a dynamite.
;
; Inputs: hero_dynamite_count (before decrement)
; Outputs: None
; Side Effects: Modifies Screen RAM to clear one icon
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clear_last_dynamite_icon
$635Fad 06 7bldahero_dynamite_count; Load current dynamite count ; x-ref: $6268, $7C45
$63620aasla
$6363aatax
$6364cadex
$6365cadex
$6366a9 20lda#$20; Space character ($20)
$63689d 11 07staSCREEN_RAM_R19C25,x; Clear top half of icon
$636B9d 39 07staSCREEN_RAM_R20C25,x; Clear bottom half of icon
$636E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the current level number onto the HUD.
; The level is stored 0-based internally, so it adds 1 before displaying.
; Determines if the level is 1 or 2 digits and passes this count in X to
; the binary-to-decimal rendering routine at $6413.
;
; Inputs: current_level
; Outputs: None
; Side Effects: Modifies Screen RAM via the draw routine at $6413
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$636Fa9 7cdraw_level_numberlda#<SCREEN_RAM_R22C12; Set destination = $077C (after \"LEVEL \" text) ; x-ref: $62F4
$637185 fdstazp_ptr_dst_lo
$6373a9 07lda#>SCREEN_RAM_R22C12
$637585 festazp_ptr_dst_hi
$6377a2 01ldx#$01; Default to 1 digit
$6379ad d9 60ldacurrent_level; Load current level (0-based)
$637C18clc
$637D69 01adc#$01; Add 1 for display
$637Fc9 0acmp#$0a; Is it >= 10?
$638190 01bccb_6384; If < 10, skip to draw
$6383e8inx; Set to 2 digits
$63844c 13 64b_6384jmpj_6413; Jump to binary-to-decimal draw routine ; x-ref: $6381
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances to the next level after completing the current one.
; Checks difficulty mode (a_60D8) to determine the final level threshold:
; mode 4 → ends at level 7, mode 8 → level 11, mode 12 → level 15, default → level 19.
; If the threshold is reached, jumps to the game-over/completion screen (j_695E).
; In mode 0, awards an extra dynamite (a_52C5) at levels 3, 7, 11, and 15 if
; the player has not yet reached the corresponding bonus count.
; At level 19 in mode 0, wraps the level counter and jumps to bonus scoring (j_6037).
; Otherwise increments the level counter and continues to level initialization (j_611D).
;
; Inputs: a_60D8 (difficulty mode), a_60D9 (current level), a_52C5 (dynamite bonus count)
; Outputs: a_60D9 (updated level), a_60DA (extra-dynamite flag), a_52C5 (updated bonus count)
; Side Effects: May transition to completion screen, bonus screen, or next level init
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6387ad d8 60advance_levelldadifficulty_mode; load difficulty mode ; x-ref: $6291
$638Af0 34beqb_63C0; mode 0 → check dynamite bonuses
$638Cc9 04cmp#$04; mode == 4?
$638Ed0 0abneb_639A
$6390ad d9 60ldacurrent_level; load current level
$6393c9 07cmp#$07; reached level 7? (final for mode 4)
$6395d0 76bneb_640D
$63974c 5e 69jmpshow_main_menu; all levels done → completion screen
$639Ac9 08b_639Acmp#$08; mode == 8? ; x-ref: $638E
$639Cd0 0abneb_63A8
$639Ead d9 60ldacurrent_level; load current level
$63A1c9 0bcmp#$0b; reached level 11? (final for mode 8)
$63A3d0 68bneb_640D
$63A54c 5e 69jmpshow_main_menu
$63A8c9 0cb_63A8cmp#$0c; mode == 12? ; x-ref: $639C
$63AAd0 0abneb_63B6
$63ACad d9 60ldacurrent_level; load current level
$63AFc9 0fcmp#$0f; reached level 15? (final for mode 12)
$63B1d0 5abneb_640D
$63B34c 5e 69jmpshow_main_menu
$63B6ad d9 60b_63B6ldacurrent_level; default: load current level ; x-ref: $63AA
$63B9c9 13cmp#$13; reached level 19? (final for default)
$63BBd0 50bneb_640D
$63BD4c 5e 69jmpshow_main_menu
$63C0ad d9 60b_63C0ldacurrent_level; mode 0: load current level ; x-ref: $638A
$63C3c9 03cmp#$03; level 3?
$63C5d0 08bneb_63CF
$63C7ad c5 52ldamax_unlocked_level; check dynamite bonus count
$63CA30 2abmib_63F6; count < 0 → award extra dynamite
$63CC4c 0d 64jmpb_640D
$63CFc9 07b_63CFcmp#$07; level 7? ; x-ref: $63C5
$63D1d0 0abneb_63DD
$63D3ad c5 52ldamax_unlocked_level; check dynamite bonus count
$63D6c9 01cmp#$01; count < 1 → award extra
$63D830 1cbmib_63F6
$63DA4c 0d 64jmpb_640D
$63DDc9 0bb_63DDcmp#$0b; level 11? ; x-ref: $63D1
$63DFd0 0abneb_63EB
$63E1ad c5 52ldamax_unlocked_level; check dynamite bonus count
$63E4c9 02cmp#$02; count < 2 → award extra
$63E630 0ebmib_63F6
$63E84c 0d 64jmpb_640D
$63EBc9 0fb_63EBcmp#$0f; level 15? ; x-ref: $63DF
$63EDd0 12bneb_6401
$63EFad c5 52ldamax_unlocked_level; check dynamite bonus count
$63F2c9 03cmp#$03; count >= 3 → skip award
$63F410 17bplb_640D
$63F6a9 ffb_63F6lda#$ff; flag: extra dynamite awarded this level ; x-ref: $63CA, $63D8, $63E6
$63F88d da 60stagame_in_progress; set extra-dynamite flag
$63FBee c5 52incmax_unlocked_level; increment dynamite bonus count
$63FE4c 0d 64jmpb_640D
$6401c9 13b_6401cmp#$13; level 19? ; x-ref: $63ED
$6403d0 08bneb_640D
$6405a9 fflda#$ff; wrap level counter to $FF (→ 0 after inc)
$64078d d9 60stacurrent_level; store wrapped level
$640A4c 37 60jmpshow_game_complete_screen; go to bonus/score display
$640Dee d9 60b_640Dinccurrent_level; advance to next level number ; x-ref: $6395, $63A3, $63B1, $63BB, $63CC, ...
$64104c 1d 61jmpj_611D; continue to level initialization
$641386 ffj_6413stxzp_temp; x-ref: $6384
$6415a0 3fldy#$3f
$6417a2 4aldx#$4a
$641938sec
$641Ac8b_641Ainy; x-ref: $641D
$641Be9 64sbc#$64
$641Db0 fbbcsb_641A
$641Fcab_641Fdex; x-ref: $6422
$642069 0aadc#$0a
$642230 fbbmib_641F
$642469 3fadc#$3f
$64268d 30 36stabcd_digit_buffer
$64298e 31 36stxbcd_digit_tens; Tens digit of BCD conversion result (ASCII)
$642C8c 32 36stybcd_digit_hundreds; Hundreds digit of BCD conversion result (ASCII)
$642F4c 57 64jmpj_6457
$643286 ffj_6432stxzp_temp; x-ref: $5206
$6434a0 00ldy#$00
$6436a2 00ldx#$00
$6438b1 fbb_6438lda(zp_ptr_src_lo),y; x-ref: $6455
$643A29 0fand#$0f
$643C09 40ora#$40
$643E9d 30 36stabcd_digit_buffer,x
$6441e8inx
$6442e4 ffcpxzp_temp
$6444f0 11beqj_6457
$6446b1 fblda(zp_ptr_src_lo),y
$64484alsra
$64494alsra
$644A4alsra
$644B4alsra
$644C09 40ora#$40
$644E9d 30 36stabcd_digit_buffer,x
$6451c8iny
$6452e8inx
$6453e4 ffcpxzp_temp
$6455d0 e1bneb_6438
$6457a0 00j_6457ldy#$00; x-ref: $642F, $6444
$6459a6 ffldxzp_temp
$645Bcadex
$645Cf0 0cbeqb_646A
$645Ebd 30 36b_645Eldabcd_digit_buffer,x; x-ref: $6468
$6461c9 40cmp#$40
$6463d0 08bneb_646D
$6465c8iny
$6466c8iny
$6467cadex
$6468d0 f4bneb_645E
$646Abd 30 36b_646Aldabcd_digit_buffer,x; x-ref: $645C, $6477
$646D91 fdb_646Dsta(zp_ptr_dst_lo),y; x-ref: $6463
$646Fc8iny
$647018clc
$647169 0aadc#$0a
$647391 fdsta(zp_ptr_dst_lo),y
$6475c8iny
$6476cadex
$647710 f1bplb_646A
$647960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Collects sprite data for all game objects (hero, creatures, projectile,
; bomb, and hazard sprites) by calling each object's sprite setup routine,
; then jumps to the sprite sort-and-render multiplexer at j_540A.
;
; Each sub-call populates zero-page arrays ($33-$36 visibility flags,
; $46-$49 Y positions, $59-$5C Y hi-bytes, $6F-$70 sprite frames,
; $7E-$83 sprite colors, $91 sprite width) used by the multiplexer.
;
; Inputs: None (reads game state from each object module)
; Outputs: None (sprite data written to zero-page arrays)
; Side Effects: VIC-II sprite registers programmed via j_540A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_and_sort_sprites
$647A20 6f 70jsrsetup_explosion_sprite; Setup hero sprite position and frame ; x-ref: $64B6
$647D20 83 77jsrsetup_entity_sprites; Setup creature sprites (up to N enemies)
$648020 fd 77jsrsetup_hazard_sprite; Setup projectile/laser sprite
$648320 cf 7ajsrsetup_tile14_sprite; Setup hazard/obstacle sprite
$648620 a8 81jsrsetup_bomb_sprite; Setup bomb/dynamite sprite
$648920 20 79jsrsetup_fire_sprite; Setup secondary enemy sprite
$648C20 78 83jsrsetup_tile13_sprite; Setup extra game object sprite
$648F4c 0a 54jmpj_540A; Sort sprites by Y and program VIC-II
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Gameplay raster IRQ handler (triggered at raster line 5). Configures the
; VIC-II display for the game viewport: sets background/multicolor colors
; (with explosion flash override), selects character memory at $3800 and
; screen at $0400, enables ECM mode. Then updates all sprite data, finds
; a free sprite slot for the score overlay, conditionally ticks SFX, and
; jumps into the sprite multiplexer chain.
;
; Inputs: explosion_state, explosion_color, a_3FA2 (multicolor), a_60DF (sfx mute flag)
; Outputs: a_60DB (set to $FF), a_60DD (free sprite slot index)
; Side Effects: VIC-II registers ($D011, $D018, $D021, $D023) programmed;
; sprites updated; sound effects ticked
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6492a9 00irq_gameplay_rasterlda#$00; default bg = black ; x-ref: $615F, $6161, $654B, $654D
$6494ae 01 6fldxexplosion_state; check if explosion active
$6497e0 02cpx#$02; State 2 = exploding?
$6499d0 03bneb_649E; skip: use explosion flash color
$649Bad 06 6fldaexplosion_color; load explosion flash color
$649E8d 21 d0b_649Esta$d021; set screen background color ; x-ref: $6499 Background Color 0
$64A1a9 1elda#$1e; char mem=$3800, screen=$0400
$64A38d 18 d0sta$d018; VIC Memory Control Register
$64A6ad 11 d0lda$d011; read current VIC control; VIC Control Register 1
$64A929 f8and#$f8; clear YSCROLL bits 0-2
$64AB09 40ora#$40; enable ECM (bit 6)
$64AD8d 11 d0sta$d011; apply display mode; VIC Control Register 1
$64B0ad a2 3fldalevel_color_multicolor; load multicolor value; VIC-II ECM multicolor value ($D023) for the level
$64B38d 23 d0sta$d023; set multicolor register 1; Background Color 2, Multi-Color Register 1
$64B620 7a 64jsrupdate_and_sort_sprites; Collect & sort all game sprites
$64B9a9 fflda#$ff; mark all sprite slots as used
$64BB8d db 60staframe_sync_flag; IRQ: signal frame complete to main loop
$64BEa2 13ldx#$13; start from slot 19
$64C0cab_64C0dex; Scan zp array backward for active sprite ; x-ref: $64C3
$64C1b5 20ldazpf_sort_order,x; search downward for free slot
$64C3d0 fbbneb_64C0; Loop until non-zero (active) found
$64C58atxa; keep low 3 bits (VIC sprite index 0-7)
$64C629 07and#$07; Mask to 0-7 (sprite slot)
$64C88d dd 60staactive_sprite_slot; Load active_sprite_slot for pointer assignment
$64CBad df 60ldasfx_muted; check if SFX muted
$64CEd0 03bneb_64D3; skip SFX update if muted
$64D020 3a 1ejsrupdate_all_sfx; tick all sound effects
$64D34c 1a 56b_64D3jmpj_561A; continue to sprite multiplexer chain ; x-ref: $64CE
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; End-of-sprite-mux raster IRQ handler.
; Called when all multiplexed sprites have been displayed. Sets the HUD
; sprite pointer for the last active sprite slot, then chains to
; irq_raster_set_bg_colors at raster line $B6 to apply the gameplay
; color split.
;
; Inputs: active_sprite_slot = index of the current sprite slot
; Outputs: None
; Side Effects: Sets sprite pointer for active slot, chains IRQ to $64E7
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$64D6a9 89irq_end_sprite_muxlda#$89; HUD sprite pointer value ($89) ; x-ref: $5AAC, $5AAE
$64D8ae dd 60ldxactive_sprite_slot; Get current multiplexer slot index
$64DB9d f8 07staSPRITE_PTR_0,x; Set sprite pointer for this slot
$64DEa9 b6lda#$b6; Next raster at $B6
$64E0a2 e7ldx#<irq_raster_set_bg_colors; Load high byte of raster color-set handler
$64E2a0 64ldy#>irq_raster_set_bg_colors; Chain to next raster IRQ (color-set handler)
$64E44c 7c 21jmpirq_chain_next; Chain to irq_raster_set_bg_colors
; Raster IRQ handler: waits with NOPs for stable raster, then sets
; background color 0 and multicolor register 1 to dark grey ($0B).
; Chained from gameplay IRQ at raster line $B6.
irq_raster_set_bg_colors
$64E7eanop; x-ref: $64E0, $64E2
$64E8eanop
$64E9eanop
$64EAeanop
$64EBeanop
$64ECeanop
$64EDeanop
$64EEeanop
$64EFeanop
$64F0eanop
$64F1eanop
$64F2eanop
$64F3a9 0blda#VicIIColors.DARK_GREY
$64F58d 21 d0sta$d021; Background Color 0
$64F88d 23 d0sta$d023; Background Color 2, Multi-Color Register 1
$64FBeanop
$64FCeanop
$64FDeanop
$64FEeanop
$64FFeanop
$6500eanop
$6501eanop
$6502eanop
$6503eanop
$6504eanop
$6505eanop
$6506eanop
$6507eanop
$6508eanop
$6509eanop
$650Aeanop
$650Beanop
$650Ceanop
$650Da9 1clda#$1c
$650F8d 18 d0sta$d018; VIC Memory Control Register
$6512ad 11 d0lda$d011; VIC Control Register 1
$651529 bfand#$bf
$65178d 11 d0sta$d011; VIC Control Register 1
$651Aad 16 d0lda$d016; VIC Control Register 2
$651D09 10ora#$10
$651F8d 16 d0sta$d016; VIC Control Register 2
$6522a9 06lda#VicIIColors.BLUE
$65248d 23 d0sta$d023; Background Color 2, Multi-Color Register 1
$6527a9 edlda#$ed
$6529a2 30ldx#<irq_raster_clear_bg; Load low byte of raster bg-clear handler
$652Ba0 65ldy#>irq_raster_clear_bg; Load high byte of raster bg-clear handler
$652D4c 7c 21jmpirq_chain_next; Chain to next raster IRQ (bg-clear handler)
; Raster IRQ handler: waits with NOPs for stable raster, then clears
; background color 0 to black, disables multicolor mode in $D016,
; and chains to the gameplay raster IRQ.
$6530eairq_raster_clear_bgnop; x-ref: $6529, $652B
$6531eanop
$6532eanop
$6533eanop
$6534eanop
$6535eanop
$6536eanop
$6537eanop
$6538eanop
$6539eanop
$653Aeanop
$653Beanop
$653Ca9 00lda#VicIIColors.BLACK
$653E8d 21 d0sta$d021; Background Color 0
$6541ad 16 d0lda$d016; VIC Control Register 2
$654429 efand#$ef
$65468d 16 d0sta$d016; VIC Control Register 2
$6549a9 05lda#$05
$654Ba2 92ldx#<irq_gameplay_raster
$654Da0 64ldy#>irq_gameplay_raster
$654F4c 7c 21jmpirq_chain_next
.encode
.enc"screen"
; Homage screen: "THIS GAME IS A HOMAGE TO H.E.R.O. FROM JOHN VAN RYZIN..."
$6552txt_homage_message.text"THIS GAME IS A HOMAGE TO H.E.R.O.", $ff, $ff, "FROM "; x-ref: $660B, $660F
$657A.text"JOHN VAN RYZIN, PUBLISHED", $ff, $ff, "BY ACTIVISION"
$65A2.text" IN 1984.", $ff, $ff, $ff, "THIS VERSION CONTAINS COMPLE"
$65CA.text"TELY", $ff, $ff, "NEW LEVELS.@"
; Homage screen prompt: "PRESS FIRE TO CONTINUE"
$65DCtxt_press_fire.text"PRESS FIRE TO CONTINUE@"; x-ref: $6620, $6624
.endencode
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes and displays the intro/about screen. Sets the game state to INTRO,
; blanks the screen, sets border and background to black, configures VIC-II
; memory registers, clears the screen, draws the homage text (white) and the
; "press fire" prompt (green), resets all sprites, and re-enables the VIC-II display.
;
; Inputs: None
; Outputs: None
; Side Effects: Screen blanked then re-enabled, border/bg set to black, sprites
; disabled, screen RAM written with intro text
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$65F3a9 01init_intro_screenlda#GameState.INTRO; x-ref: $20F2
$65F58d d3 20stagame_state
$65F820 7e 36jsrblank_screen; Blank screen during setup
$65FBa9 00lda#VicIIColors.BLACK; Load black color index
$65FD8d 20 d0sta$d020; Set border color to black; Border Color
$66008d 21 d0sta$d021; Set background color to black; Background Color 0
$6603a9 1clda#$1c; VIC memory value for default charsets
$66058d 18 d0sta$d018; Set VIC-II screen and character pointers; VIC Memory Control Register
$660820 44 36jsrclear_screen; Wipe screen RAM clean
$660Ba9 52lda#<txt_homage_message
$660D85 fbstazp_ptr_src_lo
$660Fa9 65lda#>txt_homage_message
$661185 fcstazp_ptr_src_hi
$6613a9 f4lda#<SCREEN_RAM_R6C4
$661585 fdstazp_ptr_dst_lo
$6617a9 04lda#>SCREEN_RAM_R6C4
$661985 festazp_ptr_dst_hi
$661Ba9 01lda#VicIIColors.WHITE; Use white color for homage text
$661D20 a8 36jsrdraw_text_with_color
$6620a9 dclda#<txt_press_fire
$662285 fbstazp_ptr_src_lo
$6624a9 65lda#>txt_press_fire
$662685 fcstazp_ptr_src_hi
$6628a9 d9lda#<SCREEN_RAM_R18C9
$662A85 fdstazp_ptr_dst_lo
$662Ca9 06lda#>SCREEN_RAM_R18C9
$662E85 festazp_ptr_dst_hi
$6630a9 05lda#VicIIColors.GREEN; Use green color for fire prompt
$663220 a8 36jsrdraw_text_with_color
$663520 d5 53jsrreset_all_sprites; Disable all hardware sprites
$66384c 67 36jmpenable_screen; Enable VIC-II display and return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Polls the joystick port to check for a new fire button click on the intro screen.
; Ensures the button is newly pressed (active-low logic: currently pressed (0)
; and previously released ($10)) to prevent click-holding.
; If a new click is detected, transitions to load_assets_and_init_title.
;
; Inputs: joystick_state ($3E80), joystick_prev_state ($3E81)
; Outputs: None
; Side Effects: Jumps to load_assets_and_init_title if new click occurs
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
wait_for_intro_fire_click
$663Bad 80 3eldajoystick_state; Read current joystick state ; x-ref: $211B
$663E29 10and#$10; Isolate fire button bit (bit 4)
$6640d0 0abner_664C; Active-low: nonzero = released, abort tick
$6642ad 81 3eldajoystick_prev_state; Read previous frame joystick state
$664529 10and#$10; Isolate previous fire button bit
$6647f0 03beqr_664C; Zero = already pressed last frame (hold check), abort
$66494c 6d 66jmpload_assets_and_init_title; New fire click! Advance to loading sequence
$664C60r_664Crts; x-ref: $6640, $6647
$664Dtitle_sprite_y_pos.byte$00; x-ref: $66A1, $66FA, $6704, $6708, $671D
$664Etitle_anim_counter.byte$00; x-ref: $66A6, $66EC, $66F7
.encode
.enc"screen"
$664Ftxt_loading_data.text"LOADING DATA...@"; x-ref: $6678, $667C
.endencode
; X positions for 7 title screen sprites (indices 0-6).
title_sprite_x_positions
$665F.byte$9b, $9b, $c7, $c7, $96, $ae, $c6; x-ref: $66B9
; Color values for 7 title screen sprites (indices 0-6).
$6666title_sprite_colors.byteVicIIColors.BLUE; blue ; x-ref: $66CC
$6667.byteVicIIColors.LIGHT_BLUE; light blue
$6668.byteVicIIColors.BLUE; blue
$6669.byteVicIIColors.BLUE; blue
$666A.byteVicIIColors.ORANGE; orange
$666B.byteVicIIColors.ORANGE; orange
$666C.byteVicIIColors.ORANGE; orange
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Displays the "LOADING DATA..." message, then loads high scores and cave data
; from disk. Once assets are loaded, resets the title screen sprite animation Y
; position and frame counter, initializes the title screen sprites via
; init_title_sprites, and re-enables the display to begin the curtain animation.
;
; Inputs: None
; Outputs: None
; Side Effects: Displays loading text, loads disk data, initializes title sprites
; (VIC-II registers), resets animation variables, screen enabled
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
load_assets_and_init_title
$666Da9 02lda#GameState.CURTAIN_ANIMATION; x-ref: $6649
$666F8d d3 20stagame_state; game_state = GameState.CURTAIN_ANIMATION
$667220 7e 36jsrblank_screen; Blank display for loading message
$667520 44 36jsrclear_screen
$6678a9 4flda#<txt_loading_data; Source pointer to 'LOADING DATA...' text
$667A85 fbstazp_ptr_src_lo
$667Ca9 66lda#>txt_loading_data
$667E85 fcstazp_ptr_src_hi
$6680a9 edlda#<SCREEN_RAM_R12C13; Destination pointer to screen RAM ($05ED)
$668285 fdstazp_ptr_dst_lo
$6684a9 05lda#>SCREEN_RAM_R12C13
$668685 festazp_ptr_dst_hi
$6688a9 01lda#VicIIColors.WHITE; White color for loading text
$668A20 a8 36jsrdraw_text_with_color
$668D20 67 36jsrenable_screen; Enable display to show message
$669020 d6 52jsrload_high_scores; Load high scores from disk
$669320 4d 1cjsrload_all_cave_data; Load level/cave data from disk
$669620 9f 20jsrread_disk_status_channel; Clear drive error status channel
$669920 7e 36jsrblank_screen; Blank display to hide asset setup
$669C20 44 36jsrclear_screen; Wipe screen RAM
$669Fa9 00lda#$00
$66A18d 4d 66statitle_sprite_y_pos; Reset animation curtain Y offset to 0
$66A4a9 f0lda#$f0
$66A68d 4e 66statitle_anim_counter; Reset animation countdown (240 frames)
$66A920 af 66jsrinit_title_sprites; Build & scale title screen sprites
$66AC4c 67 36jmpenable_screen; Enable VIC display to start animation
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the 7 hardware sprites used for the title screen curtain animation.
; Sets their X positions, colors, and sprite data pointers (starting at $E8).
; Also expands sprites 0 and 1 by 2x both horizontally and vertically.
;
; Inputs: None
; Outputs: None
; Side Effects: Modifies VIC-II sprite registers ($D000-$D02D) and screen pointers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$66AF20 d5 53init_title_spritesjsrreset_all_sprites; x-ref: $66A9
$66B2a9 7flda#$7f; Enable sprites 0-6 (%01111111)
$66B48d 15 d0sta$d015; Sprite display Enable
$66B7a2 06ldx#$06; Loop for 7 sprites (X=6 down to 0)
$66B9bc 5f 66b_66B9ldytitle_sprite_x_positions,x; Load X position from table ; x-ref: $66D3
$66BC8atxa; Double X for VIC-II register offset
$66BD0aasla
$66BEaatax
$66BF98tya
$66C09d 00 d0sta$d000,x; Store X position; Sprite 0 X Pos
$66C38atxa; Restore original X index
$66C44alsra
$66C5aatax
$66C618clc; Base sprite pointer is $E8
$66C769 e8adc#$e8
$66C99d f8 07staSPRITE_PTR_0,x; Store sprite pointer
$66CCbd 66 66ldatitle_sprite_colors,x; Load color from table; blue
$66CF9d 27 d0sta$d027,x; Store sprite color; Sprite 0 Color
$66D2cadex; Next sprite
$66D310 e4bplb_66B9
$66D5a9 03lda#$03; Bitmask %00000011 (Sprites 0 and 1)
$66D78d 1d d0sta$d01d; Expand Sprites 0 & 1 Horizontally; Sprites Expand 2x Horizontal (X)
$66DA8d 17 d0sta$d017; Expand Sprites 0 & 1 Vertically; Sprites Expand 2x Vertical (Y)
$66DD60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks the title screen curtain opening animation once per frame.
; Checks the joystick port to allow skipping the animation with a fire button click
; (instantly transitioning to show_main_menu).
; If not skipped, decrements the frame counter title_anim_counter and increases the
; vertical offset title_sprite_y_pos by 2 pixels per frame (capping at 120 pixels).
; Triggers set_title_sprite_y_positions to write positions into VIC-II registers.
; Once the animation counter expires, automatically transitions to the main menu.
;
; Inputs: joystick_state ($3E80), joystick_prev_state ($3E81),
; title_anim_counter ($664E), title_sprite_y_pos ($664D)
; Outputs: title_anim_counter, title_sprite_y_pos
; Side Effects: Jumps to show_main_menu on skip or completion, modifies VIC-II
; sprite registers via set_title_sprite_y_positions
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_title_curtain_animation
$66DEad 80 3eldajoystick_state; Read current joystick state ; x-ref: $2122
$66E129 10and#$10; Isolate fire button bit (bit 4)
$66E3d0 07bneanimate_title_sprite_curtain; Active-low: fire released -> tick animation
$66E5ad 81 3eldajoystick_prev_state; Read previous frame joystick state
$66E829 10and#$10; Isolate previous fire button bit
$66EAd0 05bneb_66F1; Zero = already pressed (hold), continue anim. Nonzero = new click -> skip!
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Animates the title screen sprite curtain effect. Each frame, moves the
; top sprite group (0-2) downward and the bottom sprite group (4-6) upward,
; creating a "curtain opening" reveal. Runs until the animation counter
; reaches zero, then transitions to the score/gameplay screen.
;
; Inputs: title_anim_counter ($664E) — frames remaining
; title_sprite_y_pos ($664D) — current Y offset
; Outputs: title_anim_counter decremented, title_sprite_y_pos increased
; Side Effects: Updates VIC-II sprite Y position registers ($D001-$D00D)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
animate_title_sprite_curtain
$66ECad 4e 66ldatitle_anim_counter; Load remaining animation frames ; x-ref: $66E3
$66EFd0 03bneb_66F4; Counter > 0: continue animation tick
$66F14c 5e 69b_66F1jmpshow_main_menu; Skip or finish animation: transition to main menu ; x-ref: $66EA
$66F420 08 67b_66F4jsrset_title_sprite_y_positions; Update VIC-II sprite registers with new Y offsets ; x-ref: $66EF
$66F7ce 4e 66dectitle_anim_counter; Decrement animation frame counter
$66FAad 4d 66ldatitle_sprite_y_pos; Load current Y offset
$66FDc9 78cmp#$78; Reached max curtain offset (120 pixels)?
$66FFf0 06beqr_6707; Yes: stop scrolling curtain
$670118clc; move sprites 2 pixels per frame
$670269 02adc#$02; No: move curtain 2 pixels per frame
$67048d 4d 66statitle_sprite_y_pos; Store updated Y offset
$670760r_6707rts; Return from frame update tick ; x-ref: $66FF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets the Y positions of all 7 title sprites based on title_sprite_y_pos.
; Sprites 0-2 use the position directly (top group), sprite 3 is offset
; +$15 below them, and sprites 4-6 use $1C minus the position (bottom
; group moving upward as the curtain opens).
;
; Inputs: title_sprite_y_pos ($664D)
; Outputs: None
; Side Effects: Writes VIC-II registers $D001, $D003, $D005, $D007,
; $D009, $D00B, $D00D
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
set_title_sprite_y_positions
$6708ad 4d 66ldatitle_sprite_y_pos; load current Y offset ; x-ref: $66F4
$670B8d 01 d0sta$d001; sprite 0 Y = Y offset (top group); Sprite 0 Y Pos
$670E8d 03 d0sta$d003; sprite 1 Y = Y offset (top group); Sprite 1 Y Pos
$67118d 05 d0sta$d005; sprite 2 Y = Y offset (top group); Sprite 2 Y Pos
$671418clc
$671569 15adc#$15; add 21 pixels offset for sprite 3
$67178d 07 d0sta$d007; sprite 3 Y = Y offset + $15 (below top group); Sprite 3 Y Pos
$671Aa9 1clda#$1c; $1C is the base for the bottom group
$671C38sec
$671Ded 4d 66sbctitle_sprite_y_pos; bottom Y = $1C - Y offset (moves up as curtain opens)
$67208d 09 d0sta$d009; sprite 4 Y (bottom group); Sprite 4 Y Pos
$67238d 0b d0sta$d00b; sprite 5 Y (bottom group); Sprite 5 Y Pos
$67268d 0d d0sta$d00d; sprite 6 Y (bottom group); Sprite 6 Y Pos
$672960rts
; Current selected menu option on the title screen.
; 0=START, 1=DYNAMITE, 2=BEST HEROES, 3=PLAY LEVELS, 4=CREDITS.
$672Amenu_cursor_index.byte$00; x-ref: $6A78, $6AC4, $6B10, $6B13, $6B1F, ...
; Selected level index in the PLAY LEVELS menu option.
; Incremented/decremented with joystick up/down when menu_cursor_index=3.
; Used to index into level_select_strings.
menu_level_select_index
$672B.byte$00; x-ref: $6AB1, $6AD9, $6ADE, $6AF3, $6AFB, ...
; Title screen icon tile indices (3 bytes + null terminator)
menu_title_icon_tiles
$672C.byte$1b, $1c, $1d, $00; x-ref: $696F, $6973
.encode
.enc"screen"
; Menu option text strings: START, BEST HEROES, etc. (screen codes, $FF-terminated)
$6730menu_option_strings.text" START ", $ff, $ff, $ff, $ff; start ; x-ref: $6984, $6988
$6745.text" BEST HEROES ", $ff, $ff; best heroes
$6758.text"PLAY LEVELS XXXXX", $ff, $ff; play levels
$676B.text" CREDITS @"; credits
; Control/dynamite description strings for the menu, 30 bytes each.
; 4 entries indexed by a_60D7 via multiply_a_by_30.
; Displayed on the title screen below the menu options.
dynamite_desc_strings
$677D.text" DYNAMITE: JOYSTICK DOWN "; joystick down ; x-ref: $6B61
$679B.text"DYNAMITE: DOUBLE JOYSTICK DOWN"; double joy down
$67B9.text" DYNAMITE: SPACE BAR "; space
$67D7.text" DYNAMITE: 2ND FIRE BUTTON "; 2nd fire button
; Level selection display strings, 5 bytes each (null-terminated).
; E.g. "05-08", "09-12", etc. Indexed by menu_level_select_index.
$67F5level_select_strings.text"05-08"; 05-08 ; x-ref: $6B77
$67FA.text"09-12"; 09-12
$67FF.text"13-16"; 13-16
$6804.text"17-20"; 17-20
.endencode
; HUD: copyright year "2025" + H.E.R.O. logo tile indices for bottom screen rows
$6809hud_year_and_logo.byte$32, $30, $32, $35, $00; x-ref: $632E, $6332, $69B2, $69B6
; Title logo character data (24 columns x 8 rows)
$680Etitle_logo_chars.byte$79, $20, $7a, $7b, $20, $20, $7c, $7d; x-ref: $69DE, $69E2
$6816.byte$7e, $20, $20, $20, $7f, $80, $81, $82
$681E.byte$20, $20, $20, $83, $84, $85, $86, $20
$6826.byte$87, $88, $89, $8a, $20, $20, $8b, $8c
$682E.byte$8d, $20, $20, $20, $8e, $8f, $90, $91
$6836.byte$20, $20, $92, $93, $20, $94, $95, $20
$683E.byte$96, $97, $98, $99, $20, $20, $9a, $9b
$6846.byte$9c, $20, $20, $20, $9a, $9d, $9e, $9f
$684E.byte$20, $20, $a0, $20, $20, $a1, $a2, $20
$6856.byte$a3, $a4, $a5, $a6, $20, $20, $a7, $20
$685E.byte$20, $20, $20, $20, $a7, $a8, $a9, $20
$6866.byte$20, $20, $ab, $ac, $a1, $ad, $ae, $20
$686E.byte$af, $b0, $b1, $b2, $bd, $20, $b3, $b4
$6876.byte$b4, $b5, $bd, $20, $af, $20, $b6, $b7
$687E.byte$bd, $20, $b8, $b9, $ba, $bb, $bc, $bd
$6886.fill24, $20
$689E.byte$c4, $c4, $c4, $c4, $c4, $c4, $c4, $c5
$68A6.byte$be, $bf, $20, $c0, $c1, $c2, $c3, $20
$68AE.fill8, $c4
; Title logo color data (24 columns x 8 rows, matches title_logo_chars)
$68B6title_logo_colors.byte$05, $05, $05, $05, $05, $0d, $0a, $0a; x-ref: $69E6, $69EA
$68BE.byte$0a, $0a, $0a, $0d, $0e, $0e, $0e, $0e
$68C6.byte$0e, $0d, $04, $04, $04, $04, $04, $04
$68CE.byte$05, $05, $05, $05, $05, $0d, $0a, $0a
$68D6.byte$0a, $0a, $0a, $0d, $0e, $0e, $0e, $0e
$68DE.byte$0e, $0d, $04, $04, $04, $04, $04, $04
$68E6.byte$05, $05, $05, $05, $05, $05, $0a, $0a
$68EE.byte$0a, $0a, $0a, $05, $0e, $0e, $0e, $0e
$68F6.byte$0e, $05, $04, $04, $04, $04, $04, $04
$68FE.byte$05, $05, $05, $05, $05, $05, $0a, $0a
$6906.byte$0a, $0a, $0a, $05, $0e, $0e, $0e, $0e
$690E.byte$0e, $05, $04, $04, $04, $04, $04, $04
$6916.byte$05, $05, $05, $05, $05, $05, $0a, $0a
$691E.byte$0a, $0a, $0a, $05, $0e, $0e, $0e, $0e
$6926.byte$0e, $05, $04, $04, $04, $04, $04, $04
$692E.fill24, $01
$6946.byte$0b, $0b, $0c, $0c, $0c, $0f, $0f, $0f
$694E.fill8, $01
$6956.byte$0f, $0f, $0f, $0c, $0c, $0c, $0b, $0b
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up and displays the main menu screen.
; Clears the screen, draws the menu layout with title artwork, renders
; menu option text (START, BEST HEROES, PLAY LEVELS, CREDITS), loads
; the current dynamite/control description, highlights the selected
; menu item, enables menu sprites, and installs the menu raster IRQ.
;
; Inputs: None
; Outputs: None
; Side Effects: Blanks and redraws screen, sets a_20D3 to #$03 (menu state),
; enables 6 sprites ($D015=$3F), installs raster IRQ at $6B84
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$695Ea9 03show_main_menulda#GameState.MAIN_MENU; Set game state to menu mode ; x-ref: $1D6A, $1DE2, $5F36, $616E, $6199, ...
$69608d d3 20stagame_state; game_state = 3 (main menu)
$696320 7e 36jsrblank_screen; Clear screen and prepare for menu
$696620 39 36jsrset_vic_scroll_and_rows
$696920 44 36jsrclear_screen
$696C20 de 69jsrdraw_title_artwork; Copy title artwork to screen/color RAM
$696Fa9 2clda#<menu_title_icon_tiles
$697185 fbstazp_ptr_src_lo; Source: menu text at $672C
$6973a9 67lda#>menu_title_icon_tiles
$697585 fcstazp_ptr_src_hi; Dest: screen RAM $0538 (row 3, col 16)
$6977a9 38lda#<SCREEN_RAM_R7C32
$697985 fdstazp_ptr_dst_lo
$697Ba9 05lda#>SCREEN_RAM_R7C32
$697D85 festazp_ptr_dst_hi
$697Fa9 0blda#VicIIColors.DARK_GREY
$698120 a8 36jsrdraw_text_with_color; Color: dark grey
$6984a9 30lda#<menu_option_strings; Source: second text block at $6730
$698685 fbstazp_ptr_src_lo
$6988a9 67lda#>menu_option_strings; Dest: screen RAM $05C3 (row 6, col 3)
$698A85 fcstazp_ptr_src_hi
$698Ca9 c3lda#<SCREEN_RAM_R11C11
$698E85 fdstazp_ptr_dst_lo
$6990a9 05lda#>SCREEN_RAM_R11C11
$699285 festazp_ptr_dst_hi
$6994a9 01lda#VicIIColors.WHITE; Color: white
$699620 a8 36jsrdraw_text_with_color; Draw title text
$699920 58 6bjsrupdate_dynamite_desc; Load current dynamite description text
$699Ca2 01ldx#$01; X=row 1, Y=row 1: highlight selected option
$699Ea0 01ldy#$01
$69A020 45 6ajsrfill_color_row; Set color bar on selected item
$69A320 6e 6bjsrupdate_level_select_text; Load level selection string
$69A6ad c5 52ldamax_unlocked_level; Check if level select is available
$69A910 07bplb_69B2; Skip level bar if not unlocked
$69ABa2 0bldx#$0b
$69ADa0 03ldy#$03
$69AF20 45 6ajsrfill_color_row
$69B2a9 09b_69B2lda#<hud_year_and_logo; Source: control instructions at $6809 ; x-ref: $69A9
$69B485 fbstazp_ptr_src_lo
$69B6a9 68lda#>hud_year_and_logo; Dest: screen RAM $07AD (row 16, col 5)
$69B885 fcstazp_ptr_src_hi
$69BAa9 adlda#<SCREEN_RAM_R23C21
$69BC85 fdstazp_ptr_dst_lo
$69BEa9 07lda#>SCREEN_RAM_R23C21
$69C085 festazp_ptr_dst_hi
$69C2a9 03lda#VicIIColors.CYAN; Color: cyan
$69C420 a8 36jsrdraw_text_with_color; Draw control instructions text
$69C720 d5 53jsrreset_all_sprites; Deactivate all sprites first
$69CAa9 3flda#$3f; Enable sprites 0-5 for menu decorations
$69CC8d 15 d0sta$d015; Sprite display Enable
$69CF20 ff 1djsrabort_transition; Cancel any active screen transition
$69D220 67 36jsrenable_screen
$69D5a9 28lda#$28; A=$28 = raster line for menu IRQ
$69D7a2 84ldx#$84
$69D9a0 6bldy#$6b; X/Y=$6B84 = menu raster IRQ handler
$69DB4c 4c 21jmpsetup_raster_irq; Install raster IRQ and return
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the main menu's title artwork (logo) onto the screen.
; Copies a 24x8 block of character data and color data from ROM to
; Screen RAM ($0430) and Color RAM ($D830).
;
; Inputs: None
; Outputs: None
; Side Effects: Modifies Screen RAM and Color RAM
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$69DEa9 0edraw_title_artworklda#<title_logo_chars; Source 1 (Characters) = $680E ; x-ref: $696C
$69E085 02stazp_work0
$69E2a9 68lda#>title_logo_chars
$69E485 03stazp_work1
$69E6a9 b6lda#<title_logo_colors; Source 2 (Colors) = $68B6
$69E885 04stazp_work2
$69EAa9 68lda#>title_logo_colors
$69EC85 05stazp_work3
$69EEa9 30lda#<SCREEN_RAM_R1C8; Destination 1 (Screen) = $0430
$69F085 06stazp_ptr_aux1_lo
$69F2a9 04lda#>SCREEN_RAM_R1C8
$69F485 07stazp_ptr_aux1_hi
$69F6a9 30lda#<COLOR_RAM_R1C8; Destination 2 (Color) = $D830
$69F885 08stazp_ptr_aux2_lo
$69FAa9 d8lda#>COLOR_RAM_R1C8
$69FC85 09stazp_ptr_aux2_hi
$69FEa2 07ldx#$07; X = 8 rows (7 down to 0)
$6A00a0 17b_6A00ldy#$17; Y = 24 columns (23 down to 0) ; x-ref: $6A42
$6A02b1 02b_6A02lda(zp_work0),y; Copy character byte ; x-ref: $6A0B
$6A0491 06sta(zp_ptr_aux1_lo),y
$6A06b1 04lda(zp_work2),y; Copy color byte
$6A0891 08sta(zp_ptr_aux2_lo),y
$6A0A88dey; Inner loop (columns)
$6A0B10 f5bplb_6A02
$6A0Da5 02ldazp_work0; Add 24 to Source 1 pointer
$6A0F18clc
$6A1069 18adc#$18
$6A1285 02stazp_work0
$6A14a5 03ldazp_work1
$6A1669 00adc#$00
$6A1885 03stazp_work1
$6A1Aa5 04ldazp_work2; Add 24 to Source 2 pointer
$6A1C18clc
$6A1D69 18adc#$18
$6A1F85 04stazp_work2
$6A21a5 05ldazp_work3
$6A2369 00adc#$00
$6A2585 05stazp_work3
$6A27a5 06ldazp_ptr_aux1_lo; Add 40 to Dest 1 (next line)
$6A2918clc
$6A2A69 28adc#$28
$6A2C85 06stazp_ptr_aux1_lo
$6A2Ea5 07ldazp_ptr_aux1_hi
$6A3069 00adc#$00
$6A3285 07stazp_ptr_aux1_hi
$6A34a5 08ldazp_ptr_aux2_lo; Add 40 to Dest 2 (next line)
$6A3618clc
$6A3769 28adc#$28
$6A3985 08stazp_ptr_aux2_lo
$6A3Ba5 09ldazp_ptr_aux2_hi
$6A3D69 00adc#$00
$6A3F85 09stazp_ptr_aux2_hi
$6A41cadex; Outer loop (rows)
$6A42d0 bcbneb_6A00
$6A4460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Fills one 40-column row of Color RAM with a given color value.
; Used to highlight/unhighlight menu items by painting a full row.
; The base address is COLOR_RAM_ROW11; Y selects the row offset
; (each row is 80 bytes in the C128 80-col layout), and X holds
; the color value to fill.
;
; Inputs: X = color value to store, Y = row offset from COLOR_RAM_ROW11
; Outputs: None
; Side Effects: Overwrites 40 bytes of Color RAM at the target row
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6A45a9 b8fill_color_rowlda#<COLOR_RAM_R11C0; Point to COLOR_RAM_ROW11 as base ; x-ref: $69A0, $69AF
$6A4785 fbstazp_ptr_src_lo
$6A49a9 d9lda#>COLOR_RAM_R11C0
$6A4B85 fcstazp_ptr_src_hi
$6A4Dc0 00cpy#$00; Row offset = 0? Skip advance loop
$6A4Ff0 10beqb_6A61
$6A51a5 fbb_6A51ldazp_ptr_src_lo; x-ref: $6A5F
$6A5318clc
$6A5469 50adc#$50; Add 80 ($50) per row (C128 80-col layout)
$6A5685 fbstazp_ptr_src_lo
$6A58a5 fcldazp_ptr_src_hi
$6A5A69 00adc#$00
$6A5C85 fcstazp_ptr_src_hi
$6A5E88dey; Next row offset
$6A5Fd0 f0bneb_6A51
$6A618ab_6A61txa; A = color value (from X) ; x-ref: $6A4F
$6A62a0 27ldy#$27; Fill 40 columns ($00-$27)
$6A6491 fbb_6A64sta(zp_ptr_src_lo),y; Store color into Color RAM ; x-ref: $6A67
$6A6688dey
$6A6710 fbbplb_6A64
$6A6960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks the main menu input processing once per frame.
; Detects newly clicked fire button presses to activate menu choices:
; Option 0 (START) -> starts default game
; Option 1 (DYNAMITE) -> cycles control methods (input_mode)
; Option 2 (BEST HEROES) -> shows high score ranking screen
; Option 3 (PLAY LEVELS) -> launches game from selected starting level
; Option 4 (CREDITS) -> shows credits screen
;
; If fire is not pressed, processes directional joystick inputs:
; Left/Right -> shifts starting level group (Option 3 focused) up to max_unlocked_level
; Up/Down -> scrolls cursor, wrapping 0-4 and skipping Option 3 if locked
;
; Inputs: joystick_state ($3E80), joystick_prev_state ($3E81),
; menu_cursor_index ($672A), menu_level_select_index ($672B)
; Outputs: menu_cursor_index, menu_level_select_index, difficulty_mode, input_mode
; Side Effects: Stops audio, transitions to gameplay init, high scores, or credits
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_main_menu_selection
$6A6Aad 80 3eldajoystick_state; Read current joystick state ; x-ref: $2129
$6A6D29 10and#$10; Isolate fire button bit (bit 4)
$6A6Fd0 53bneb_6AC4; Active-low: fire released -> check movement
$6A71ad 81 3eldajoystick_prev_state; Read previous frame joystick state
$6A7429 10and#$10
$6A76f0 4cbeqb_6AC4; Zero = already pressed last frame (hold check), abort
$6A78ad 2a 67ldamenu_cursor_index; Load selected menu option cursor index
$6A7Bd0 0bbneb_6A88; Option > 0 -> check next selections
$6A7D20 0f 1ejsrstop_sound; Stop menu audio playback
$6A80a9 00lda#$00; Clear starting level difficulty
$6A828d d8 60stadifficulty_mode
$6A854c ff 60jmpinit_cave_level; Start gameplay from level 1
$6A88c9 01b_6A88cmp#MenuOptions.DYNAMITE; Option == 1 (DYNAMITE)? ; x-ref: $6A7B
$6A8Ad0 12bneb_6A9E
$6A8Cee d7 60incinput_mode; Cycle dynamite input control mode
$6A8Fad d7 60ldainput_mode
$6A92c9 04cmp#$04; Cycled past max mode 3?
$6A94d0 05bneb_6A9B
$6A96a9 00lda#$00
$6A988d d7 60stainput_mode; Wrap input mode back to 0
$6A9B4c 58 6bb_6A9Bjmpupdate_dynamite_desc; Update HUD text for selected control scheme ; x-ref: $6A94
$6A9Ec9 02b_6A9Ecmp#MenuOptions.BEST_HEROES; Option == 2 (BEST HEROES)? ; x-ref: $6A8A
$6AA0d0 08bneb_6AAA
$6AA220 0f 1ejsrstop_sound
$6AA5a9 fflda#$ff
$6AA74c 3e 6djmpinit_high_score_screen; Display high scores board
$6AAAc9 03b_6AAAcmp#MenuOptions.PLAY_LEVELS; Option == 3 (PLAY LEVELS)? ; x-ref: $6AA0
$6AACd0 10bneb_6ABE
$6AAE20 0f 1ejsrstop_sound
$6AB1ae 2b 67ldxmenu_level_select_index; Load selected starting level group index
$6AB4e8inx
$6AB58atxa; Shift group index to starting level index
$6AB60aasla
$6AB70aasla
$6AB88d d8 60stadifficulty_mode; Set starting level difficulty
$6ABB4c ff 60jmpinit_cave_level; Start gameplay from selected level
$6ABE20 0f 1eb_6ABEjsrstop_sound; Stop music, go to credits layout ; x-ref: $6AAC
$6AC14c c3 5ejmpshow_credits_screen
$6AC4ad 2a 67b_6AC4ldamenu_cursor_index; Check if cursor is on PLAY LEVELS ; x-ref: $6A6F, $6A76
$6AC7c9 03cmp#MenuOptions.PLAY_LEVELS
$6AC9d0 37bneb_6B02
$6ACBad 80 3eldajoystick_state; Read joystick state
$6ACE29 04and#$04; Isolate Left direction bit (bit 2)
$6AD0d0 13bneb_6AE5; Left released -> check Right direction
$6AD2ad 81 3eldajoystick_prev_state
$6AD529 04and#$04
$6AD7f0 0cbeqb_6AE5; Left held -> check Right direction
$6AD9ad 2b 67ldamenu_level_select_index; Check if starting level is at minimum
$6ADCf0 06beqr_6AE4
$6ADEce 2b 67decmenu_level_select_index; Decrement selected starting level group
$6AE14c 6e 6bjmpupdate_level_select_text; Refresh level range string on screen
$6AE460r_6AE4rts; x-ref: $6ADC
$6AE5ad 80 3eb_6AE5ldajoystick_state; Isolate Right direction bit (bit 3) ; x-ref: $6AD0, $6AD7
$6AE829 08and#$08
$6AEAd0 16bneb_6B02; Right released -> check Up/Down movement
$6AECad 81 3eldajoystick_prev_state
$6AEF29 08and#$08
$6AF1f0 0fbeqb_6B02; Right held -> check Up/Down movement
$6AF3ad 2b 67ldamenu_level_select_index; Check if level select reached max unlocked
$6AF6cd c5 52cmpmax_unlocked_level
$6AF9f0 06beqr_6B01
$6AFBee 2b 67incmenu_level_select_index; Check if at max level
$6AFE4c 6e 6bjmpupdate_level_select_text
$6B0160r_6B01rts; x-ref: $6AF9
$6B02ad 80 3eb_6B02ldajoystick_state; Refresh level range string on screen ; x-ref: $6AC9, $6AEA, $6AF1
$6B0529 01and#$01
$6B07d0 24bneb_6B2D
$6B09ad 81 3eldajoystick_prev_state
$6B0C29 01and#$01
$6B0Ef0 1dbeqb_6B2D
$6B10ce 2a 67decmenu_cursor_index; Move cursor up (decrement cursor)
$6B13ad 2a 67ldamenu_cursor_index; Read cursor index
$6B16c9 03cmp#MenuOptions.PLAY_LEVELS; Did we move onto Level Select?
$6B18d0 09bneb_6B23
$6B1Aad c5 52ldamax_unlocked_level; Check if Level Select is unlocked
$6B1D10 03bplr_6B22; If unlocked: skip skip-logic
$6B1Fce 2a 67decmenu_cursor_index; Level Select locked: skip it (move up again)
$6B2260r_6B22rts; x-ref: $6B1D
$6B23c9 ffb_6B23cmp#$ff; Did cursor wrap past 0? ; x-ref: $6B18
$6B25d0 05bner_6B2C
$6B27a9 04lda#MenuOptions.CREDITS; Wrap cursor around to CREDITS (4)
$6B298d 2a 67stamenu_cursor_index; Store updated cursor index
$6B2C60r_6B2Crts; x-ref: $6B25
$6B2Dad 80 3eb_6B2Dldajoystick_state; Read current joystick state ; x-ref: $6B07, $6B0E
$6B3029 02and#$02; Isolate Down direction bit (bit 1)
$6B32d0 23bner_6B57; Down released -> abort frame tick
$6B34ad 81 3eldajoystick_prev_state; Read previous frame joystick state
$6B3729 02and#$02; Isolate previous Down direction bit
$6B39f0 1cbeqr_6B57; Zero = already pressed last frame (hold check), abort
$6B3Bee 2a 67incmenu_cursor_index; Move cursor down (increment cursor)
$6B3Ead 2a 67ldamenu_cursor_index; Read cursor index
$6B41c9 03cmp#MenuOptions.PLAY_LEVELS; Did we move onto Level Select?
$6B43d0 09bneb_6B4E
$6B45ad c5 52ldamax_unlocked_level; Check if Level Select is unlocked
$6B4810 03bplr_6B4D; If unlocked: skip skip-logic
$6B4Aee 2a 67incmenu_cursor_index; Level Select locked: skip it (move down again)
$6B4D60r_6B4Drts; x-ref: $6B48
$6B4Ec9 05b_6B4Ecmp#$05; Did cursor wrap past CREDITS (4)? ; x-ref: $6B43
$6B50d0 05bner_6B57
$6B52a9 00lda#MenuOptions.START; Wrap cursor around to START (0)
$6B548d 2a 67stamenu_cursor_index; Store updated cursor index
$6B5760r_6B57rts; x-ref: $6B32, $6B39, $6B50
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the on-screen dynamite control description text based on input_mode.
; Looks up input_mode (0-3), multiplies by 30 to index into the
; dynamite_desc_strings table (4 entries × 30 screencodes each), and copies
; the 30-character string to scr_hud_row on screen RAM.
;
; Strings:
; 0 = " DYNAMITE: JOYSTICK DOWN "
; 1 = "DYNAMITE: DOUBLE JOYSTICK DOWN"
; 2 = " DYNAMITE: SPACE BAR "
; 3 = " DYNAMITE: 2ND FIRE BUTTON "
;
; Inputs: input_mode (0-3) — selected dynamite activation method
; Outputs: None
; Side Effects: Writes 30 bytes to screen RAM at scr_hud_row ($060D)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6B58ad d7 60update_dynamite_descldainput_mode; ; x-ref: $6999, $6A9B
$6B5B20 b5 21jsrmultiply_by_30; Offset = input_mode * 30
$6B5Eaatax; X = index into string table
$6B5Fa0 00ldy#$00; Y = screen write offset
$6B61bd 7d 67b_6B61ldadynamite_desc_strings,x; Read screencode from string table ; x-ref: $6B6B joystick down
$6B6499 0d 06staSCREEN_RAM_R13C5,y; Write to screen RAM HUD row
$6B67e8inx
$6B68c8iny
$6B69c0 1ecpy#$1e; 30 chars ($1E) per description
$6B6Bd0 f4bneb_6B61
$6B6D60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the level-select display on the menu screen.
; Reads menu_level_select_index (0-3), multiplies by 5 to index into
; level_select_strings (4 entries × 5 screencodes each), and copies the
; 5-char level range string to scr_lives_display on screen RAM.
;
; Strings:
; 0 = "05-08" 1 = "09-12" 2 = "13-16" 3 = "17-20"
;
; Inputs: menu_level_select_index ($672B) — selected starting level group
; Outputs: None
; Side Effects: Writes 5 bytes to screen RAM at scr_lives_display ($06BF)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_level_select_text
$6B6Ead 2b 67ldamenu_level_select_index; Load selected level group (0-3) ; x-ref: $69A3, $6AE1, $6AFE
$6B7120 a5 21jsrmultiply_by_5; Offset = index * 5 (5 chars per entry)
$6B74aatax; X = byte offset into string table
$6B75a0 00ldy#$00; Y = screen write position
$6B77bd f5 67b_6B77ldalevel_select_strings,x; Read level range char from table ; x-ref: $6B81 05-08
$6B7A99 bf 06staSCREEN_RAM_R17C23,y; Write to screen RAM (lives/level area)
$6B7De8inx
$6B7Ec8iny
$6B7Fc0 05cpy#$05; 5 chars per level string
$6B81d0 f4bneb_6B77
$6B8360rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Title screen raster IRQ handler — sprite setup.
; Positions 5 sprites to form the title/logo graphic:
; Sprites 0-3 in a row at Y=$3B (X=$59,$89,$B9,$EA)
; Sprite 4 centered at Y=$5B (X=$8F)
; Assigns sprite pointers $F5-$F9 and sets all 5 to white ($01).
; Computes next raster line from menu_cursor_index (each option is 16px
; apart, base $86) and chains to irq_menu_cursor_top.
;
; Inputs: menu_cursor_index = currently highlighted menu option (0-based)
; Outputs: None
; Side Effects: VIC-II sprite registers $D000-$D00B, sprite pointers,
; sprite colors, next IRQ chained to irq_menu_cursor_top
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
irq_title_screen_sprites
$6B84a9 59lda#$59; Sprite 0 X = $59 ; x-ref: $6CC5, $6CC7
$6B868d 00 d0sta$d000; Sprite 0 X Pos
$6B89a9 89lda#$89; Sprite 1 X = $89
$6B8B8d 02 d0sta$d002; Sprite 1 X Pos
$6B8Ea9 b9lda#$b9; Sprite 2 X = $B9
$6B908d 04 d0sta$d004; Sprite 2 X Pos
$6B93a9 ealda#$ea; Sprite 3 X = $EA
$6B958d 06 d0sta$d006; Sprite 3 X Pos
$6B98a9 8flda#$8f; Sprite 4 X = $8F (centered below)
$6B9A8d 08 d0sta$d008; Sprite 4 X Pos
$6B9Da9 3blda#$3b; Sprites 0-3 Y = $3B (top row)
$6B9F8d 01 d0sta$d001; Sprite 0 Y Pos
$6BA28d 03 d0sta$d003; Sprite 1 Y Pos
$6BA58d 05 d0sta$d005; Sprite 2 Y Pos
$6BA88d 07 d0sta$d007; Sprite 3 Y Pos
$6BABa9 5blda#$5b; Sprite 4 Y = $5B (second row)
$6BAD8d 09 d0sta$d009; Sprite 4 Y Pos
$6BB0a2 f5ldx#$f5; Sprite pointers $F5-$F9 (title logo frames)
$6BB28e f8 07stxSPRITE_PTR_0
$6BB5e8inx
$6BB68e f9 07stxSPRITE_PTR_1
$6BB9e8inx
$6BBA8e fa 07stxSPRITE_PTR_2
$6BBDe8inx
$6BBE8e fb 07stxSPRITE_PTR_3
$6BC1e8inx
$6BC28e fc 07stxSPRITE_PTR_4
$6BC5a9 01lda#VicIIColors.WHITE; All 5 title sprites = white
$6BC78d 27 d0sta$d027; Sprite 0 Color
$6BCA8d 28 d0sta$d028; Sprite 1 Color
$6BCD8d 29 d0sta$d029; Sprite 2 Color
$6BD08d 2a d0sta$d02a; Sprite 3 Color
$6BD38d 2b d0sta$d02b; Sprite 4 Color
$6BD6ad 2a 67ldamenu_cursor_index; Get current menu selection
$6BD90aasla; cursor_index * 16
$6BDA0aasla
$6BDB0aasla
$6BDC0aasla
$6BDD69 86adc#$86; + $86 = raster line for top highlight bar
$6BDFa2 e6ldx#<irq_menu_cursor_top
$6BE1a0 6bldy#>irq_menu_cursor_top
$6BE34c 7c 21jmpirq_chain_next; Chain to irq_menu_cursor_top
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Menu cursor highlight — top bar raster IRQ.
; NOP sled for stable raster timing, then sets $D021 to light blue ($0E)
; for one character row, waits, then resets $D021 to blue ($06).
; This creates a highlighted horizontal bar on the selected menu option.
; Computes the raster line for the bottom bar from menu_cursor_index
; (top + 8 lines = $94 base) and chains to irq_menu_cursor_bottom.
;
; Inputs: menu_cursor_index = currently highlighted menu option (0-based)
; Outputs: None
; Side Effects: $D021 (background color) flashed to light blue then blue
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6BE6eairq_menu_cursor_topnop; NOP sled — stable raster timing ; x-ref: $6BDF, $6BE1
$6BE7eanop
$6BE8eanop
$6BE9eanop
$6BEAeanop
$6BEBeanop
$6BECeanop
$6BEDeanop
$6BEEeanop
$6BEFeanop
$6BF0eanop
$6BF1eanop
$6BF2a9 0elda#VicIIColors.LIGHT_BLUE; Set background to light blue ($0E)
$6BF48d 21 d0sta$d021; Background Color 0
$6BF7eanop
$6BF8eanop
$6BF9eanop
$6BFAeanop
$6BFBeanop
$6BFCeanop
$6BFDeanop
$6BFEeanop
$6BFFeanop
$6C00eanop
$6C01eanop
$6C02eanop
$6C03eanop
$6C04eanop
$6C05eanop
$6C06eanop
$6C07eanop
$6C08eanop
$6C09eanop
$6C0Aeanop
$6C0Beanop
$6C0Ceanop
$6C0Deanop
$6C0Eeanop
$6C0Feanop
$6C10eanop
$6C11eanop
$6C12eanop
$6C13eanop
$6C14eanop
$6C15a9 06lda#VicIIColors.BLUE; Restore background to blue ($06)
$6C178d 21 d0sta$d021; Background Color 0
$6C1Aad 2a 67ldamenu_cursor_index; Get menu selection index
$6C1D0aasla; cursor_index * 16
$6C1E0aasla
$6C1F0aasla
$6C200aasla
$6C2169 94adc#$94; + $94 = raster line for bottom highlight bar
$6C23a2 2aldx#<irq_menu_cursor_bottom
$6C25a0 6cldy#>irq_menu_cursor_bottom
$6C274c 7c 21jmpirq_chain_next; Chain to irq_menu_cursor_bottom
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Menu cursor highlight — bottom bar raster IRQ.
; Same pattern as irq_menu_cursor_top: NOP sled for stable raster, then
; sets $D021 to light blue ($0E) for one character row, waits, then resets
; $D021 to black ($00) (the default menu screen background).
; Chains to irq_setup_status_bar_sprites at raster line $DC.
;
; Inputs: None (entered via IRQ vector)
; Outputs: None
; Side Effects: $D021 flashed to light blue then reset to black ($00)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
irq_menu_cursor_bottom
$6C2Aeanop; NOP sled — stable raster timing ; x-ref: $6C23, $6C25
$6C2Beanop
$6C2Ceanop
$6C2Deanop
$6C2Eeanop
$6C2Feanop
$6C30eanop
$6C31eanop
$6C32eanop
$6C33eanop
$6C34eanop
$6C35eanop
$6C36a9 0elda#VicIIColors.LIGHT_BLUE; Set background to light blue ($0E)
$6C388d 21 d0sta$d021; Background Color 0
$6C3Beanop
$6C3Ceanop
$6C3Deanop
$6C3Eeanop
$6C3Feanop
$6C40eanop
$6C41eanop
$6C42eanop
$6C43eanop
$6C44eanop
$6C45eanop
$6C46eanop
$6C47eanop
$6C48eanop
$6C49eanop
$6C4Aeanop
$6C4Beanop
$6C4Ceanop
$6C4Deanop
$6C4Eeanop
$6C4Feanop
$6C50eanop
$6C51eanop
$6C52eanop
$6C53eanop
$6C54eanop
$6C55eanop
$6C56eanop
$6C57eanop
$6C58eanop
$6C59a9 00lda#VicIIColors.BLACK; Reset background to black ($00)
$6C5B8d 21 d0sta$d021; Background Color 0
$6C5Ea9 dclda#$dc; Next raster at $DC for status bar sprites
$6C60a2 67ldx#<irq_setup_status_bar_sprites
$6C62a0 6cldy#>irq_setup_status_bar_sprites
$6C644c 7c 21jmpirq_chain_next; Chain to irq_setup_status_bar_sprites
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Raster IRQ handler that configures 6 sprites to form the bottom status bar.
; Positions 3 pairs of sprites in two columns (X=$8B/$A3) at Y=$E6,
; assigns consecutive sprite pointers $EF–$F4, and sets pair colors
; (white, light blue, blue). Chains to the next raster IRQ at line $28.
;
; Inputs: None (entered via IRQ vector)
; Outputs: None
; Side Effects: VIC-II sprite registers $D000–$D02C, sprite pointers $07F8–$07FD,
; next IRQ set at raster $28 with handler at $6B84
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
irq_setup_status_bar_sprites
$6C67a9 8blda#$8b; X pos for left column sprites ; x-ref: $6C60, $6C62
$6C698d 00 d0sta$d000; Sprite 0 X Pos
$6C6C8d 04 d0sta$d004; Sprite 2 X Pos
$6C6F8d 08 d0sta$d008; Sprite 4 X Pos
$6C7218clc; Offset right column by 24px
$6C7369 18adc#$18
$6C758d 02 d0sta$d002; Sprite 1 X Pos
$6C788d 06 d0sta$d006; Sprite 3 X Pos
$6C7B8d 0a d0sta$d00a; Sprite 5 X Pos
$6C7Ea9 e6lda#$e6; Y=$E6: near bottom of screen
$6C808d 01 d0sta$d001; Sprite 0 Y Pos
$6C838d 03 d0sta$d003; Sprite 1 Y Pos
$6C868d 05 d0sta$d005; Sprite 2 Y Pos
$6C898d 07 d0sta$d007; Sprite 3 Y Pos
$6C8C8d 09 d0sta$d009; Sprite 4 Y Pos
$6C8F8d 0b d0sta$d00b; Sprite 5 Y Pos
$6C92a2 efldx#$ef; Sprite pointers $EF-$F4 (blocks 239-244): LC GA
$6C948e f8 07stxSPRITE_PTR_0
$6C97e8inx; #$f0 = MES
$6C988e f9 07stxSPRITE_PTR_1
$6C9Be8inx; #$F1 = LC GAM (highlight)
$6C9C8e fa 07stxSPRITE_PTR_2
$6C9Fe8inx; #$F2 = MES (highlight)
$6CA08e fb 07stxSPRITE_PTR_3
$6CA3e8inx; #$F3 = LC GA (highlight 2)
$6CA48e fc 07stxSPRITE_PTR_4
$6CA7e8inx; #$F4 = MES (highlight 2)
$6CA88e fd 07stxSPRITE_PTR_5
$6CABa9 01lda#VicIIColors.WHITE; White for pair 0-1
$6CAD8d 27 d0sta$d027; Sprite 0 Color
$6CB08d 28 d0sta$d028; Sprite 1 Color
$6CB3a9 0elda#VicIIColors.LIGHT_BLUE; Light blue for pair 2-3
$6CB58d 29 d0sta$d029; Sprite 2 Color
$6CB88d 2a d0sta$d02a; Sprite 3 Color
$6CBBa9 06lda#VicIIColors.BLUE; Blue for pair 4-5
$6CBD8d 2b d0sta$d02b; Sprite 4 Color
$6CC08d 2c d0sta$d02c; Sprite 5 Color
$6CC3a9 28lda#$28; Next raster IRQ at line $28
$6CC5a2 84ldx#<irq_title_screen_sprites
$6CC7a0 6bldy#>irq_title_screen_sprites
$6CC94c 7c 21jmpirq_chain_next; Exit via IRQ epilogue
.encode
.enc"screen"
; Controls high score screen behavior.
; Bit 7 set ($FF) = show table only (no name entry).
; Bit 7 clear (0-9) = player ranking position for name input.
high_score_screen_flag
$6CCC.text"@"; x-ref: $6D3E, $6D5F, $6E63, $6E75
; High score table title: "BEST HEROES"
$6CCDtxt_best_heroes.text"BEST HEROES@"; x-ref: $6D68, $6D6C
; High score table column header: "RANK SCORE LVL NAME"
$6CD9txt_hiscore_header.text"RANK SCORE LVL NAME@"; x-ref: $6D7D, $6D81
; Rank label strings for the high score table, 4 chars each.
; 10 entries: " 1ST", " 2ND", " 3RD", " 4TH" ... " 9TH", "10TH".
; Followed by status messages ("SAVING DATA...", "RESETTING RANKING...").
$6CF2rank_label_strings.text" 1ST 2ND 3RD 4TH 5TH 6TH 7TH 8TH 9TH10TH"; x-ref: $6DB8
; Disk I/O status message: "SAVING DATA..."
$6D1Atxt_saving_data.text"SAVING DATA...@"; x-ref: $6EE3, $6EE7
; High score reset status message: "RESETTING RANKING..."
txt_resetting_ranking
$6D29.text"RESETTING RANKING...@"; x-ref: $6EB9, $6EBD
.endencode
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the High Scores / Scoreboard screen layout.
; Saves the player's ranking slot index in high_score_screen_flag, transitions
; game_state to GameState.GAME_OVER, blanks the display, configures VIC rows, clears
; the screen RAM, draws the scoreboard tables via draw_high_score_table, and resets
; active sprites.
; Finally, checks high_score_screen_flag; if negative ($FF), returns immediately
; (view-only mode). Otherwise, transitions to enter_high_score_name to let the
; player enter their initials.
;
; Inputs: A = player ranking slot index (0-9), or $FF for view-only
; Outputs: None
; Side Effects: Sets game_state to GameState.GAME_OVER, configures VIC registers,
; clears screen, draws high score table, resets sprites, enables screen
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
init_high_score_screen
$6D3E8d cc 6cstahigh_score_screen_flag; Store ranking slot index / view flag ; x-ref: $1DCE, $62B4, $6AA7, $6ED9
$6D41a9 04lda#GameState.GAME_OVER
$6D438d d3 20stagame_state; Store game state
$6D4620 7e 36jsrblank_screen; Blank VIC-II display to hide redraw
$6D4920 39 36jsrset_vic_scroll_and_rows; Set standard C128 scroll and 25-row mode
$6D4Ca9 00lda#$00; Disable dynamic tile zero-suppression
$6D4E8d 38 36stazero_suppress_disable
$6D51a0 01ldy#$01; Clear screen using character color index 1 (white)
$6D5320 44 36jsrclear_screen; Wipe screen RAM
$6D5620 68 6djsrdraw_high_score_table; Draw entire high score table layout
$6D5920 d5 53jsrreset_all_sprites; Clear all hardware sprites from view
$6D5C20 67 36jsrenable_screen; Re-enable VIC-II display
$6D5Fad cc 6cldahigh_score_screen_flag; Reload ranking slot index flag
$6D6230 03bmir_6D67; Active-high/negative flag ($FF) -> view-only, exit (RTS)
$6D644c 61 6ejmpenter_high_score_name; New high-score entry! Go to text input loop
$6D6760r_6D67rts; Return from high score setup ; x-ref: $6D62
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the complete high score table on the screen.
; First prints the title "BEST HEROES" (green, at SCR_HISCORE_TITLE) and the
; column header "RANK SCORE LVL NAME" (cyan, at SCR_HISCORE_HEADER).
; Then loops through 10 score entries (7 bytes each in the score buffer),
; rendering each row:
; - Rank label from rank_label_strings (" 1ST", " 2ND", etc.)
; - 6-digit BCD score at column +7
; - Level number at column +15 (or "---" if slot empty, $FF)
; - 3-char player name at column +21
; Rows advance by $50 (80 bytes, C128 80-col screen layout).
;
; Inputs: Score buffer pointed to by set_score_buffer_ptr
; Outputs: None
; Side Effects: Writes to screen RAM from $04D0 downward, 10 rows
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_high_score_table
$6D68a9 cdlda#<txt_best_heroes; Source: "BEST HEROES" title string ; x-ref: $6D56
$6D6A85 fbstazp_ptr_src_lo
$6D6Ca9 6clda#>txt_best_heroes
$6D6E85 fcstazp_ptr_src_hi
$6D70a9 37lda#<SCREEN_RAM_R1C15; Dest: screen row for title
$6D7285 fdstazp_ptr_dst_lo
$6D74a9 04lda#>SCREEN_RAM_R1C15
$6D7685 festazp_ptr_dst_hi
$6D78a9 05lda#VicIIColors.GREEN; Color = green
$6D7A20 a8 36jsrdraw_text_with_color
$6D7Da9 d9lda#<txt_hiscore_header; Source: "RANK SCORE LVL NAME"
$6D7F85 fbstazp_ptr_src_lo
$6D81a9 6clda#>txt_hiscore_header
$6D8385 fcstazp_ptr_src_hi
$6D85a9 80lda#<SCREEN_RAM_R3C8
$6D8785 fdstazp_ptr_dst_lo
$6D89a9 04lda#>SCREEN_RAM_R3C8
$6D8B85 festazp_ptr_dst_hi
$6D8Da9 03lda#VicIIColors.CYAN; Color = cyan
$6D8F20 a8 36jsrdraw_text_with_color; Draw high score header text
$6D92a9 00lda#$00; Entry counter = 0 (10 entries total)
$6D9485 06stazp_ptr_aux1_lo
$6D9620 cb 53jsrset_score_buffer_ptr; Init score buffer pointer ($FB/$FC)
$6D99a5 fbldazp_ptr_src_lo; Save score buffer ptr to zpa_02/03
$6D9B85 02stazp_work0
$6D9Da5 fcldazp_ptr_src_hi
$6D9F85 03stazp_work1
$6DA1a9 d0lda#$d0; Screen row dest starts at $04D0
$6DA385 04stazp_work2
$6DA5a9 04lda#$04
$6DA785 05stazp_work3
$6DA9a5 04j_6DA9ldazp_work2; Copy screen ptr to $FB/$FC ; x-ref: $6E5D
$6DAB85 fbstazp_ptr_src_lo
$6DADa5 05ldazp_work3
$6DAF85 fcstazp_ptr_src_hi
$6DB1a5 06ldazp_ptr_aux1_lo; Rank index * 4 for rank_label_strings
$6DB30aasla
$6DB40aasla
$6DB5aatax
$6DB6a0 00ldy#$00
$6DB8bd f2 6cb_6DB8ldarank_label_strings,x; Copy rank label chars to screen ; x-ref: $6DC1
$6DBB91 fbsta(zp_ptr_src_lo),y
$6DBDe8inx
$6DBEc8iny
$6DBFc0 04cpy#$04
$6DC1d0 f5bneb_6DB8
$6DC3a5 02ldazp_work0; Restore score buffer ptr
$6DC585 fbstazp_ptr_src_lo
$6DC7a5 03ldazp_work1
$6DC985 fcstazp_ptr_src_hi
$6DCBa5 04ldazp_work2; Screen ptr + 7 → score column dest
$6DCD18clc
$6DCE69 07adc#$07
$6DD085 fdstazp_ptr_dst_lo
$6DD2a5 05ldazp_work3
$6DD469 00adc#$00
$6DD685 festazp_ptr_dst_hi
$6DD8a2 06ldx#$06; Print 6-digit BCD score
$6DDA20 0e 37jsrprint_bcd_number; Print BCD score at screen + 7
$6DDDa5 02ldazp_work0
$6DDF18clc
$6DE069 03adc#$03; Score entry + 3 = level byte
$6DE285 fbstazp_ptr_src_lo
$6DE4a5 03ldazp_work1
$6DE669 00adc#$00
$6DE885 fcstazp_ptr_src_hi
$6DEAa5 04ldazp_work2; Screen ptr + 15 → level column dest
$6DEC18clc
$6DED69 0fadc#$0f
$6DEF85 fdstazp_ptr_dst_lo
$6DF1a5 05ldazp_work3
$6DF369 00adc#$00
$6DF585 festazp_ptr_dst_hi
$6DF7a0 00ldy#$00
$6DF9b1 fblda(zp_ptr_src_lo),y; Read level byte from score entry
$6DFBc9 ffcmp#$ff; $FF = empty slot
$6DFDd0 0fbneb_6E0E
$6DFFa9 01lda#$01; Display "---" for empty entry
$6E0191 fdsta(zp_ptr_dst_lo),y
$6E03c8iny
$6E04a9 0clda#$0c
$6E0691 fdsta(zp_ptr_dst_lo),y
$6E08c8iny
$6E0991 fdsta(zp_ptr_dst_lo),y
$6E0B4c 16 6ejmpj_6E16
$6E0E18b_6E0Eclc; Level is 0-based, display as 1-based ; x-ref: $6DFD
$6E0F69 01adc#$01
$6E11a2 03ldx#$03; 3 digits for level
$6E1320 ef 36jsrprint_byte_as_decimal; Print level number at screen + 15
$6E16a5 02j_6E16ldazp_work0; Score buffer + 4 → name ptr ; x-ref: $6E0B
$6E1818clc
$6E1969 04adc#$04; Score entry + 4 = 3-char name
$6E1B85 fbstazp_ptr_src_lo
$6E1Da5 03ldazp_work1
$6E1F69 00adc#$00
$6E2185 fcstazp_ptr_src_hi
$6E23a5 04ldazp_work2; Screen ptr + 21 → name column dest
$6E2518clc
$6E2669 15adc#$15
$6E2885 fdstazp_ptr_dst_lo
$6E2Aa5 05ldazp_work3
$6E2C69 00adc#$00
$6E2E85 festazp_ptr_dst_hi
$6E30a0 00ldy#$00
$6E32b1 fbb_6E32lda(zp_ptr_src_lo),y; Copy 3-char player name to screen ; x-ref: $6E39
$6E3491 fdsta(zp_ptr_dst_lo),y
$6E36c8iny
$6E37c0 03cpy#$03
$6E39d0 f7bneb_6E32
$6E3Be6 06inczp_ptr_aux1_lo; Next entry (index++)
$6E3Da5 06ldazp_ptr_aux1_lo
$6E3Fc9 0acmp#$0a; Done all 10 entries?
$6E41f0 1dbeqr_6E60
$6E43a5 02ldazp_work0; Score buffer ptr += 7 (entry size)
$6E4518clc
$6E4669 07adc#$07; Next score entry (+7 bytes each)
$6E4885 02stazp_work0
$6E4Aa5 03ldazp_work1
$6E4C69 00adc#$00
$6E4E85 03stazp_work1
$6E50a5 04ldazp_work2; Screen ptr += $50 (next row, 80 cols)
$6E5218clc
$6E5369 50adc#$50; Next screen row (+$50, 80 cols)
$6E5585 04stazp_work2
$6E57a5 05ldazp_work3
$6E5969 00adc#$00
$6E5B85 05stazp_work3
$6E5D4c a9 6djmpj_6DA9; Loop back for next entry
$6E6060r_6E60rts; x-ref: $6E41
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Prompts the player to enter their 3-character initials for the high score
; table, copies the entered name into the score buffer at the correct rank
; position, then displays the updated table and saves it to disk.
;
; Inputs: a_6CCC = player's ranking position (0-9)
; Outputs: Score buffer updated with player's initials
; Side Effects: Screen redrawn with high score table, scores saved to disk
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enter_high_score_name
$6E61a2 1dldx#$1d; X = screen column for name input ; x-ref: $6D64
$6E63ad cc 6cldahigh_score_screen_flag; Load player's ranking position
$6E660aasla; rank * 2
$6E6718clc
$6E6869 05adc#$05; Y = screen row (rank*2 + 5)
$6E6Aa8tay
$6E6Ba9 03lda#$03; A = max 3 chars for initials
$6E6D20 72 5cjsrinput_text_string; Prompt player for name entry
$6E7020 cb 53jsrset_score_buffer_ptr; Point $FB/$FC at score buffer
$6E73a9 04lda#$04; A = starting offset in buffer
$6E75ae cc 6cldxhigh_score_screen_flag; Get rank slot for name storage offset
$6E78f0 06beqb_6E80; Rank 0 → offset stays at 4
$6E7A18clc
$6E7B69 07b_6E7Badc#$07; Add 7 per rank to reach name field ; x-ref: $6E7E
$6E7Dcadex
$6E7Ed0 fbbneb_6E7B
$6E80a8b_6E80tay; Y = offset to name field in buffer ; x-ref: $6E78
$6E81a2 00ldx#$00
$6E83bd d9 5bb_6E83ldainput_text_buffer,x; Copy entered char from input buffer ; x-ref: $6E8E
$6E86f0 08beqb_6E90; Skip if null terminator
$6E8891 fbsta(zp_ptr_src_lo),y; Store char in score buffer
$6E8Ac8iny
$6E8Be8inx
$6E8Ce0 03cpx#$03; Copy up to 3 chars
$6E8Ed0 f3bneb_6E83
$6E9020 dd 6eb_6E90jsrshow_saving_and_save_scores; Display final table and save to disk ; x-ref: $6E86
$6E934c 5e 69jmpshow_main_menu; Return to title/game screen
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks the high score / scoreboard screen once per frame.
; Polls the joystick fire button; a new click instantly transitions back to the
; main menu.
; If fire is not pressed, polls keyboard row 0 for the RUN/STOP key. A new RUN/STOP
; click triggers a scoreboard factory reset:
; 1. Displays "RESETTING RANKING..." message
; 2. Restores default score list via init_default_high_scores
; 3. Flushes default high scores to the floppy disk via save_high_scores_to_disk
; 4. Reloads the high score table screen
;
; Inputs: joystick_state ($3E80), joystick_prev_state ($3E81),
; kb_row0_cur ($3F0C), kb_row0_prev ($3F0D)
; Outputs: None
; Side Effects: Clears screen, overwrites high score floppy file, reloads
; scoreboard view, or exits back to main menu
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_high_score_screen
$6E96ad 80 3eldajoystick_state; Read current joystick state ; x-ref: $2130
$6E9929 10and#$10; Isolate fire button bit (bit 4)
$6E9Bd0 0abneb_6EA7; Active-low: fire released -> check keyboard input
$6E9Dad 81 3eldajoystick_prev_state; Read previous frame joystick state
$6EA029 10and#$10
$6EA2f0 03beqb_6EA7; Zero = already pressed last frame (hold check), abort
$6EA44c 5e 69jmpshow_main_menu; New fire click! Return back to main menu
$6EA7ad 0c 3fb_6EA7ldakb_row0_cur; Read current keyboard row 0 state ; x-ref: $6E9B, $6EA2
$6EAAcd 0d 3fcmpkb_row0_prev; Check if keyboard state changed
$6EADf0 2dbeqr_6EDC; No key change: exit screen tick
$6EAFc9 efcmp#$ef; Check if RUN/STOP key is pressed
$6EB1d0 29bner_6EDC; Not RUN/STOP: exit screen tick
$6EB320 7e 36jsrblank_screen; Blank VIC-II display to hide layout draw
$6EB620 44 36jsrclear_screen; Wipe screen RAM clean
$6EB9a9 29lda#<txt_resetting_ranking; Source pointer = 'RESETTING RANKING...' text
$6EBB85 fbstazp_ptr_src_lo
$6EBDa9 6dlda#>txt_resetting_ranking
$6EBF85 fcstazp_ptr_src_hi
$6EC1a9 ealda#<SCREEN_RAM_R12C10; Destination pointer = screen RAM row 15
$6EC385 fdstazp_ptr_dst_lo
$6EC5a9 05lda#>SCREEN_RAM_R12C10
$6EC785 festazp_ptr_dst_hi
$6EC9a9 01lda#VicIIColors.WHITE; Draw message text in White
$6ECB20 a8 36jsrdraw_text_with_color
$6ECE20 67 36jsrenable_screen; Re-enable VIC-II display
$6ED120 26 53jsrinit_default_high_scores; Reset high score data table to defaults
$6ED420 f7 52jsrsave_high_scores_to_disk; Write default scoreboard to floppy disk
$6ED7a9 fflda#$ff; Load default ranking code ($FF)
$6ED94c 3e 6djmpinit_high_score_screen; Reload high score table screen
$6EDC60r_6EDCrts; Return from scoreboard screen update ; x-ref: $6EAD, $6EB1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Displays "SAVING DATA..." message on a blank screen, resets sprites, re-enables
; the display, then saves high scores to disk via save_high_scores_to_disk.
; Called when exiting gameplay (RUN/STOP or high-score entry complete) while a
; game was in progress.
;
; Inputs: None
; Outputs: None
; Side Effects: Blanks/clears screen, displays save message, resets sprites,
; re-enables VIC-II display, writes high scores to disk
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
show_saving_and_save_scores
$6EDD20 7e 36jsrblank_screen; Turn off display to avoid glitches ; x-ref: $6196, $62AE, $6E90
$6EE020 44 36jsrclear_screen; Wipe screen RAM
$6EE3a9 1alda#<txt_saving_data; src = "SAVING DATA..." text
$6EE585 fbstazp_ptr_src_lo
$6EE7a9 6dlda#>txt_saving_data
$6EE985 fcstazp_ptr_src_hi
$6EEBa9 edlda#<SCREEN_RAM_R12C13; dst = screen position $05ED
$6EED85 fdstazp_ptr_dst_lo
$6EEFa9 05lda#>SCREEN_RAM_R12C13
$6EF185 festazp_ptr_dst_hi
$6EF3a9 01lda#VicIIColors.WHITE; color 1 = white
$6EF520 a8 36jsrdraw_text_with_color; Render "SAVING DATA..." on screen
$6EF820 d5 53jsrreset_all_sprites; Remove all sprites from display
$6EFB20 67 36jsrenable_screen; Re-enable VIC-II display (set DEN bit)
$6EFE4c f7 52jmpsave_high_scores_to_disk; Tail call: save scores and return
$6F01explosion_state.byte$00; x-ref: $4FF8, $6494, $6F11, $6F19, $6F3E, ...
$6F02explosion_x_lo.byte$00; x-ref: $6FBD, $6FEC, $7021, $7046, $7079
$6F03explosion_x_hi.byte$00; x-ref: $6FC4, $6FF4, $7027, $704C, $7081
$6F04explosion_y.byte$00; x-ref: $6FB4, $6FE4, $7030, $7052, $7088
$6F05explosion_frame.byte$00; x-ref: $6F55, $6F77, $6F7A, $6F83, $6F8E, ...
$6F06explosion_color.byte$00; x-ref: $649B, $6F4C, $6F99
$6F07explosion_anim_tick.byte$00; x-ref: $6F68, $6F6B, $6F74, $705C
$6F08explosion_timer.byte$00; x-ref: $6F20, $6F32, $6F35, $6F5D, $6F93, ...
explosion_color_table
$6F09.byteVicIIColors.WHITE; white ; x-ref: $6F49, $6F96
$6F0A.byteVicIIColors.BLACK; black
$6F0B.byteVicIIColors.YELLOW; yellow
$6F0C.byteVicIIColors.ORANGE; orange
$6F0D.byteVicIIColors.RED; red
$6F0E.byteVicIIColors.BLACK; black
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the screen color flash subsystem to its idle state.
; Clears the state variable (a_6F01) so no flash animation runs,
; and restores the default screen color (yellow = 7) via zpa_82.
;
; Inputs: None
; Outputs: a_6F01 = 0, zpa_82 = 7
; Side Effects: Stops any active color flash animation
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6F0Fa9 00reset_color_flashlda#$00; idle state: no flash active ; x-ref: $4B01
$6F118d 01 6fstaexplosion_state; clear flash state machine
$6F14a9 07lda#VicIIColors.YELLOW; 7 = yellow (default color)
$6F1685 82stazp_screen_color; restore default screen color (yellow)
$6F1860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Explosion animation state machine. Called every frame from the main game loop.
; Dispatches on explosion_state ($6F01):
; State 0: Inactive — do nothing.
; State 1: Countdown delay, then init explosion (state 2) + check hero proximity.
; State 2: Animate explosion — cycle colors from table, advance sprite frames
; ($A1–$A4). After 6 color ticks, deactivate and check entity collisions.
; State 3: Fade-out — decrement timer, then deactivate.
;
; Inputs: explosion_state ($6F01), explosion_timer ($6F08)
; Outputs: explosion_state, explosion_frame, explosion_color, explosion_anim_tick
; Side Effects: May trigger entity destroy (j_875A) or hero death (j_8185)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6F19ad 01 6fupdate_explosionldaexplosion_state; Read current explosion state ; x-ref: $61DD
$6F1Cc9 01cmp#$01; State 1? (countdown delay)
$6F1Ed0 0ebneb_6F2E
$6F20ce 08 6fdecexplosion_timer; Decrement delay timer
$6F23d0 06bneb_6F2B
$6F2520 87 6fjsrinit_explosion; Timer expired → start explosion animation
$6F284c df 6fjmpcheck_hero_explosion_hit; Then check hero proximity
$6F2B4c 68 6fb_6F2Bjmptick_explosion_anim; Timer not expired → tick animation frame ; x-ref: $6F23
$6F2Ec9 02b_6F2Ecmp#$02; State 2? (active explosion) ; x-ref: $6F1E
$6F30d0 27bneb_6F59
$6F32ee 08 6fincexplosion_timer; Increment timer for color cycling
$6F35ae 08 6fldxexplosion_timer
$6F38e0 06cpx#$06; 6 ticks → explosion finished
$6F3Ad0 0dbneb_6F49
$6F3Ca9 00lda#$00; Deactivate explosion
$6F3E8d 01 6fstaexplosion_state
$6F41a9 00lda#VicIIColors.BLACK; Set screen color to black
$6F4320 be 50jsrset_screen_color
$6F464c a2 6fjmpcheck_entity_explosion_hit; Check entity collisions with explosion
$6F49bd 09 6fb_6F49ldaexplosion_color_table,x; Load color from cycling table ; x-ref: $6F3A white
$6F4C8d 06 6fstaexplosion_color; Set current explosion color
$6F4Fe0 03cpx#$03; At color index 3?
$6F51d0 05bner_6F58
$6F53a9 a5lda#$a5; Set special frame $A5 at midpoint
$6F558d 05 6fstaexplosion_frame
$6F5860r_6F58rts; x-ref: $6F51
$6F59c9 03b_6F59cmp#$03; State 3? (fade-out) ; x-ref: $6F30
$6F5Bd0 0abner_6F67
$6F5Dce 08 6fdecexplosion_timer; Decrement fade-out timer
$6F60d0 05bner_6F67
$6F62a9 00lda#$00; Timer expired → deactivate explosion
$6F648d 01 6fstaexplosion_state; Clear explosion state
$6F6760r_6F67rts; ; x-ref: $6F5B, $6F60
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the explosion animation by one tick. Increments anim_tick; every 4
; ticks advances to the next sprite frame, cycling through $A1-$A3.
;
; Inputs: explosion_anim_tick, explosion_frame
; Outputs: explosion_anim_tick (incremented, wraps at 4), explosion_frame
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6F68ee 07 6ftick_explosion_animincexplosion_anim_tick; Increment animation sub-tick counter ; x-ref: $6F2B
$6F6Bad 07 6fldaexplosion_anim_tick
$6F6Ec9 04cmp#$04; Every 4 sub-ticks?
$6F70d0 14bner_6F86
$6F72a9 00lda#$00; Reset sub-tick counter
$6F748d 07 6fstaexplosion_anim_tick
$6F77ee 05 6fincexplosion_frame; Advance to next sprite frame
$6F7Aad 05 6fldaexplosion_frame
$6F7Dc9 a4cmp#$a4; Past frame $A3?
$6F7Fd0 05bner_6F86
$6F81a9 a1lda#$a1; Wrap back to first explosion frame $A1
$6F838d 05 6fstaexplosion_frame
$6F8660r_6F86rts; x-ref: $6F70, $6F7F
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the explosion animation. Sets state to 2 (active), resets frame
; to $A4, loads initial color from explosion_color_table, and signals active.
;
; Inputs: explosion_color_table ($6F09)
; Outputs: explosion_state=2, explosion_frame=$A4, explosion_timer=0,
; explosion_color from table, a_88EC=1
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$6F87a9 02init_explosionlda#$02; State = 2 (active explosion) ; x-ref: $4FFF, $6F25
$6F898d 01 6fstaexplosion_state; Set explosion state to active
$6F8Ca9 a4lda#$a4; Start at sprite frame $A4
$6F8E8d 05 6fstaexplosion_frame; Store initial frame
$6F91a9 00lda#$00; Reset timer to 0
$6F938d 08 6fstaexplosion_timer; Clear explosion timer
$6F96ad 09 6fldaexplosion_color_table; Load initial color from table[0]; white
$6F998d 06 6fstaexplosion_color; Set explosion display color
$6F9Ca9 01lda#$01; Signal explosion active
$6F9E8d ec 88stasfx_hit_state; Flag explosion as active
$6FA160rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks all active entities for collision with the explosion. Iterates through
; entity slots; if an entity's Y matches explosion_y and X is within 25 pixels,
; calls j_875A to destroy it.
;
; Inputs: a_85E4 (entity count), f_85C9/D5/CF/D2 (entity tables),
; explosion_x_lo/hi, explosion_y
; Outputs: None (entities may be destroyed via j_875A)
; Side Effects: May destroy entities within blast radius
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_entity_explosion_hit
$6FA2ae e4 85ldxdynamic_tile_count; Load active entity count ; x-ref: $5009, $6F46
$6FA5d0 01bneb_6FA8; Any entities? Skip if none
$6FA760rts
$6FA8cab_6FA8dex; Convert count to 0-based index ; x-ref: $6FA5
$6FA9bd c9 85b_6FA9ldadtile_id,x; Tile active? ; x-ref: $6FDC
$6FACf0 2dbeqb_6FDB
$6FAEbd d5 85ldadtile_x,x; Tile pixel X
$6FB118clc
$6FB269 09adc#$09
$6FB4cd 04 6fcmpexplosion_y; Same row as explosion?
$6FB7d0 22bneb_6FDB
$6FB9bd cf 85ldadtile_y_lo,x; Tile pixel Y lo
$6FBC38sec
$6FBDed 02 6fsbcexplosion_x_lo
$6FC0a8tay; Entity X hi - explosion X hi
$6FC1bd d2 85ldadtile_y_hi,x; Tile pixel Y hi
$6FC4ed 03 6fsbcexplosion_x_hi
$6FC7d0 07bneb_6FD0
$6FC9c0 19cpy#$19; Within 25 pixels? ($19)
$6FCBb0 0ebcsb_6FDB
$6FCD4c 5a 87jmpj_875A; Yes → destroy entity
$6FD0c9 ffb_6FD0cmp#$ff; High byte = $FF? (negative offset) ; x-ref: $6FC7
$6FD2d0 07bneb_6FDB
$6FD4c0 e8cpy#$e8; Within -24 pixels? ($E8 = -24)
$6FD690 03bccb_6FDB
$6FD84c 5a 87jmpj_875A; Yes → destroy entity
$6FDBcab_6FDBdex; Next entity slot ; x-ref: $6FAC, $6FB7, $6FCB, $6FD2, $6FD6
$6FDC10 cbbplb_6FA9; More entities to check?
$6FDE60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the hero is within the explosion blast radius. If a_7AFA is nonzero
; (hero invulnerable/dead), skips the check. Compares explosion position against
; hero position; if within ±30 pixels on X and same Y row, calls j_8185 (hero death).
;
; Inputs: explosion_x_lo/hi, explosion_y, a_7AFB/7AFC/7AFD (hero position)
; Outputs: None
; Side Effects: May trigger hero death via j_8185
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_hero_explosion_hit
$6FDFad fa 7aldahero_state; Hero status (nonzero = invulnerable/dead) ; x-ref: $6F28
$6FE2d0 2abner_700E; Hero not idle → bail out
$6FE4ad 04 6fldaexplosion_y; Same Y row as explosion?
$6FE7cd fd 7acmphero_y_pos
$6FEAd0 22bner_700E; Y coords differ → no hit
$6FECad 02 6fldaexplosion_x_lo; Explosion X - hero X (lo byte)
$6FEF38sec
$6FF0ed fb 7asbchero_x_lo
$6FF3a8tay; Low byte of distance → Y reg
$6FF4ad 03 6fldaexplosion_x_hi; Explosion X - hero X (hi byte)
$6FF7ed fc 7asbchero_x_hi; Subtract hero X high byte (with borrow)
$6FFAd0 07bneb_7003; High byte nonzero → check negative case
$6FFCc0 1ecpy#$1e; Within 30 pixels? ($1E)
$6FFEb0 0ebcsr_700E; Distance >= 30 → no hit
$70004c 85 81jmpj_8185; Yes → hero death
$7003c9 ffb_7003cmp#$ff; High byte = $FF? (negative offset) ; x-ref: $6FFA
$7005d0 07bner_700E; High byte not $FF → too far
$7007c0 e3cpy#$e3; Within -29 pixels? ($E3 = -29)
$700990 03bccr_700E; Too far left → no hit
$700B4c 85 81jmpj_8185; Yes → hero death
$700E60r_700Erts; x-ref: $6FE2, $6FEA, $6FFE, $7005, $7009
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Triggered when an enemy collision occurs. Starts the explosion fade-out
; (state 3) if currently inactive, or continues the existing state 3.
;
; Inputs: explosion_state, entity position in X register
; Outputs: explosion_state=3, explosion position set from entity tables
; Side Effects: Sets explosion_timer to $1E (30 frames)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
start_explosion_from_hit
$700Fad 01 6fldaexplosion_state; Current explosion state ; x-ref: $880C
$7012f0 05beqb_7019; State 0 (inactive)? → start new
$7014c9 03cmp#$03; Already in state 3? → update position
$7016f0 06beqb_701E
$701860rts
$7019a9 03b_7019lda#$03; Set state to 3 (fade-out) ; x-ref: $7012
$701B8d 01 6fstaexplosion_state
$701Ebd cf 85b_701Eldadtile_y_lo,x; Copy entity X lo to explosion X ; x-ref: $7016
$70218d 02 6fstaexplosion_x_lo
$7024bd d2 85ldadtile_y_hi,x
$70278d 03 6fstaexplosion_x_hi; Copy entity X hi to explosion X
$702Abd d5 85ldadtile_x,x
$702D38sec
$702Ee9 05sbc#$05
$70308d 04 6fstaexplosion_y; Entity Y - 5 = explosion Y (center offset)
$7033a9 a6lda#$a6
$70358d 05 6fstaexplosion_frame; Set starting frame $A6
$7038a9 1elda#$1e
$703A8d 08 6fstaexplosion_timer; Set timer to 30 frames ($1E)
$703D60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes explosion at the hero's position. Sets state to 1 (countdown
; delay), copies hero coordinates, and sets screen color to yellow.
; Called when hero is hit by an enemy.
;
; Inputs: a_7AFB/7AFC/7AFD (hero position)
; Outputs: explosion_state=1, explosion position from hero, explosion_timer=$1E
; Side Effects: Sets screen color to $0B (dark grey), a_8940=1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$703Ea9 01init_hero_explosionlda#$01; State = 1 (countdown delay) ; x-ref: $7C4B
$70408d 01 6fstaexplosion_state; Activate blast effect state machine
$7043ad fb 7aldahero_x_lo; Copy hero X lo to explosion X
$70468d 02 6fstaexplosion_x_lo; Store as effect X position (low)
$7049ad fc 7aldahero_x_hi; Load blast origin X position (high)
$704C8d 03 6fstaexplosion_x_hi; Copy hero X hi to explosion X
$704Fad fd 7aldahero_y_pos; Copy hero Y to explosion Y
$70528d 04 6fstaexplosion_y; Store as effect Y position
$7055a9 a1lda#$a1; Starting frame $A1
$70578d 05 6fstaexplosion_frame; Set display character for effect
$705Aa9 00lda#$00; Clear anim tick counter
$705C8d 07 6fstaexplosion_anim_tick
$705Fa9 1elda#$1e; Set timer to 30 frames
$70618d 08 6fstaexplosion_timer; Set effect frame counter
$7064a9 0blda#VicIIColors.DARK_GREY; Set screen color to dark grey ($0B)
$706620 be 50jsrset_screen_color; Apply blast color to screen
$7069a9 01lda#$01; Signal hero explosion active
$706B8d 40 89stasfx_explosion_state; Enable blast processing
$706E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Prepares explosion sprite parameters for the multiplexer.
; If no explosion is active, hides the sprite by setting Y to $FF.
; Otherwise, converts explosion world position to screen coordinates
; (X += $0C, Y += $25), copies the animation frame, and stores all
; values into the zero-page sprite parameter slots.
;
; Inputs: explosion_state, explosion_x_lo/hi, explosion_y, explosion_frame
; Outputs: zpa_36 (Y pos), zpa_49 (X lo), zpa_5C (X hi), zpa_6F (frame)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
setup_explosion_sprite
$706Fad 01 6fldaexplosion_state; Is explosion active? ; x-ref: $647A
$7072d0 05bneb_7079; Yes -> set up sprite coords
$7074a9 fflda#$ff; $FF = offscreen Y (hide sprite)
$707685 36stazp_mux_y_explode; Store Y pos = offscreen
$707860rts
$7079ad 02 6fb_7079ldaexplosion_x_lo; Explosion world X (lo) ; x-ref: $7072
$707C18clc
$707D69 0cadc#$0c; Add $0C pixel offset to center sprite
$707F85 49stazp_mux_x_lo_explode; Store screen X lo for multiplexer
$7081ad 03 6fldaexplosion_x_hi; Explosion world X (hi)
$708469 00adc#$00; Propagate carry to hi byte
$708685 5cstazp_mux_x_hi_explode; Store screen X hi for multiplexer
$7088ad 04 6fldaexplosion_y; Explosion world Y
$708B18clc
$708C69 25adc#$25; Add $25 pixel offset to center sprite
$708E85 36stazp_mux_y_explode; Store screen Y for multiplexer
$7090ad 05 6fldaexplosion_frame; Current animation frame index
$709385 6fstazp_mux_frame_explode; Store sprite frame for multiplexer
$709560rts
; Entity status array (15 slots, indexed by X).
; Values: 0 = active/alive, 2 = inactive/dead.
; Used by both the animated-tile subsystem and the
; enemy collision routines to skip dead entities.
$7096entity_status.fill15, $00; x-ref: $1F28, $1FD0, $2014, $723D, $7243, ...
; Entity type array (15 slots, indexed by X).
; Values: 0-4 select tile behavior, hitbox, animation.
; Used to dispatch per-type update, collision, and init logic.
$70A5entity_type.fill15, $00; x-ref: $1FC9, $2028, $7225, $726B, $73C6, ...
; Entity X position fractional byte (15 slots, indexed by X).
; Sub-pixel precision for smooth 24-bit movement.
; Combined with entity_x_lo/entity_x_hi for full position.
$70B4entity_x_frac.fill15, $00; x-ref: $725C, $7419, $7420, $74F1, $74F7, ...
; Entity X position low byte (15 slots, indexed by X).
; Pixel-level X coordinate, used in collision checks.
; Mid byte of 24-bit position (frac/lo/hi).
$70C3entity_x_lo.fill15, $00; x-ref: $1F43, $1FEB, $2046, $7275, $72A9, ...
; Entity X position high byte (15 slots, indexed by X).
; Screen page for X coordinate, used in collision checks.
; High byte of 24-bit position (frac/lo/hi).
$70D2entity_x_hi.fill15, $00; x-ref: $1F4A, $1FF2, $204D, $727C, $72B0, ...
; Entity Y position fractional byte (15 slots, indexed by X).
; Sub-pixel precision for gravity/spring physics.
; Combined with entity_y for smooth vertical movement.
$70E1entity_y_frac.fill15, $00; x-ref: $725F, $747E, $7485
; Entity Y position in pixels (15 slots, indexed by X).
; Used in collision checks and rendering.
; For type 2 entities, updated by gravity physics.
$70F0entity_y.fill15, $00; x-ref: $1F34, $1FDC, $201D, $7284, $72B5, ...
; Entity current animation frame/tile index (15 slots, indexed by X).
; Holds the tile character code ($A7-$B8 range).
; Used by renderer and for sub-type hitbox refinement.
$70FFentity_frame.fill15, $00; x-ref: $728A, $72C6, $7310, $736C, $739C, ...
; Entity animation speed / color (15 slots, indexed by X).
; During init, set per entity type (5-14).
; Also bulk-initialized as sprite color at $50B7.
$710Eentity_anim_speed.fill15, $00; x-ref: $50B7, $728F, $72CB, $7371, $73A1, ...
; Entity X position target/limit low byte (15 slots, indexed by X).
; Used by type 2 gravity physics as the X convergence target.
; Compared against entity_x_lo to determine accel/decel.
$711Dentity_x_limit_lo.fill15, $00; x-ref: $72E8, $7442
; Entity X position target/limit high byte (15 slots, indexed by X).
; Used by type 2 gravity physics as the X convergence target.
; Compared against entity_x_hi to determine accel/decel.
$712Centity_x_limit_hi.fill15, $00; x-ref: $72F2, $7438
; Entity Y position target/limit (15 slots, indexed by X).
; Used by type 1 (vertical oscillation) and type 2 (gravity).
; Gravity physics accelerates toward this Y value.
$713Bentity_y_limit.fill15, $00; x-ref: $72B8, $72FD, $740E, $7494
; Entity X velocity low byte (15 slots, indexed by X).
; Type 2 gravity: fractional velocity, difficulty-scaled.
; Values: $00 (mid/hard) or $21 (easy).
$714Aentity_vel_x_lo.fill15, $00; x-ref: $731C, $7330, $7340, $741D, $7447, ...
; Entity X velocity mid byte (15 slots, indexed by X).
; Type 2 gravity: pixel-level velocity component.
; Values: $01 (easy), $02 (mid), $04 (hard).
$7159entity_vel_x_mid.fill15, $00; x-ref: $7321, $7335, $7345, $7426, $7451, ...
; Entity X velocity high byte (15 slots, indexed by X).
; Type 2 gravity: page-level velocity for fast-moving entities.
; Usually 0; carries from mid byte during acceleration.
$7168entity_vel_x_hi.fill15, $00; x-ref: $7302, $742F, $7459, $745E, $7476, ...
; Entity Y velocity low byte (15 slots, indexed by X).
; Type 2 gravity: fractional vertical velocity.
; Accelerated by +/- $0B per frame toward entity_y_limit.
$7177entity_vel_y_lo.fill15, $00; x-ref: $730A, $7482, $7499, $749F, $74AB, ...
; Entity Y velocity high byte (15 slots, indexed by X).
; Type 2 gravity: pixel-level vertical velocity.
; Carries from vel_y_lo during vertical acceleration.
$7186entity_vel_y_hi.fill15, $00; x-ref: $7305, $748B, $74A2, $74A7, $74B4, ...
; Entity movement phase index (15 slots, indexed by X).
; Type 1: vertical oscillation offset (0-15, wraps).
; Indexes into displacement table f_71E5 for Y offset.
$7195entity_move_phase.fill15, $00; x-ref: $72BD, $73FE, $7401, $740A
; Entity animation frame table index (15 slots, indexed by X).
; Indexes into per-type frame lookup tables (a_71F5, etc.).
; Wraps at type-specific limits (2, 4, or 8 frames).
$71A4entity_anim_index.fill15, $00; x-ref: $7262, $754E, $7559, $7573, $757E, ...
; Entity animation delay counter (15 slots, indexed by X).
; Counts up each frame; when reaching the delay threshold
; (a_71E2-a_71E4), advances entity_anim_index and resets to 0.
$71B3entity_anim_counter.fill15, $00; x-ref: $7265, $753E, $7541, $754B, $7563, ...
; Entity despawn/death timer (15 slots, indexed by X).
; Type 1: sub-counter for movement (wraps at 4).
; Dying entities ($75F5): set to $1E, counts down to 0 then status=2.
$71C2entity_timer.fill15, $00; x-ref: $72C0, $73BB, $73EF, $73F2, $73FB, ...
; Entity dynamite hit flag (15 slots, indexed by X).
; Set to $FF by check_dynamite_vs_enemies when entity is
; within blast radius. Death handler ($7758) sets status=2
; for flagged entities.
$71D1entity_hit_flag.fill15, $00; x-ref: $2063, $7268, $775E
; Number of active animated tiles. Used as loop bound for entity iteration.
$71E0tile_count.byte$00; x-ref: $1F21, $1FC2, $200D, $720F, $7217, ...
; Physics acceleration for type 2 tiles (gravity/spring). Difficulty-scaled: $11 (easy), $20 (mid), $80 (hard).
$71E1tile_accel.byte$00; x-ref: $7326, $733A, $734A, $744B, $7468
; Animation frame delay for type 0 tiles. Default 10, halved on level >= 10.
$71E2tile_delay_type0.byte$00; x-ref: $729C, $7544
; Animation frame delay for type 1 tiles. Default 10, halved on level >= 10.
$71E3tile_delay_type1.byte$00; x-ref: $72D8, $7569
; Animation frame delay for type 3 tiles. Default 8, halved on level >= 10.
$71E4tile_delay_type3.byte$00; x-ref: $737E, $75B2
; 16-byte signed Y-offset table for type 1 bounce animation: +1..+4..+1, 0, -1..-4..-1, 0.
$71E5tile_bounce_offsets.byte$01, $02, $03, $04, $03, $02, $01, $00; x-ref: $7412
$71ED.byte$ff, $fe, $fd, $fc, $fd, $fe, $ff, $00
; 2-frame animation sequence for type 0 tiles ($A7, $A8).
$71F5tile_frames_type0.byte$a7, $a8; x-ref: $7287, $755C
; 2-frame animation sequence for type 1 tiles ($A9, $AA).
$71F7tile_frames_type1.byte$a9, $aa; x-ref: $72C3, $7581
; 4-frame animation sequence for type 2 tiles ($AB-$AE).
$71F9tile_frames_type2.byte$ab, $ac, $ad, $ae; x-ref: $730D, $75A5
; 8-frame palindrome animation for type 3 tiles ($AF-$B3-$B0).
$71FDtile_frames_type3.byte$af, $b0, $b1, $b2, $b3, $b2, $b1, $b0; x-ref: $7369, $75CA
; 8-frame palindrome animation for type 4 tiles ($B4-$B8-$B5).
$7205tile_frames_type4.byte$b4, $b5, $b6, $b7, $b8, $b7, $b6, $b5; x-ref: $7399, $75EE
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the animated tile subsystem by clearing the tile count to zero.
; Called during level initialization to prepare for new tile spawning.
;
; Inputs: None
; Outputs: a_71E0 = 0 (animated tile count cleared)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$720Da9 00reset_animated_tileslda#$00; A = 0 ; x-ref: $4B04
$720F8d e0 71statile_count; clear animated tile count
$721260rts
$721386 1ej_7213stxzp_temp_x; x-ref: $4FF4
$721584 1fstyzp_temp_y
$7217ae e0 71ldxtile_count
$721A20 25 72jsrinit_animated_tile
$721Dee e0 71inctile_count
$7220a6 1eldxzp_temp_x
$7222a4 1fldyzp_temp_y
$722460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes an animated tile slot based on tile type.
; Looks up the level map to check if the tile cell is occupied, then
; configures position, animation frames, and speed based on the tile type
; (0-4). Difficulty scaling halves animation delay on level >= 10.
;
; Inputs: A = tile type (0-4), X = tile slot index
; Outputs: Tile slot arrays populated (position, frame, speed, status)
; Side Effects: Writes to a_71E1-a_71E4 (global animation counters)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$72259d a5 70init_animated_tilestaentity_type,x; store tile type in slot ; x-ref: $721A
$7228ac 9e 3fldycurrent_room_index; Y = current level index
$722Bb9 09 49ldalevel_map_ptr_lo,y; Get map row base addr (lo) for current level
$722E85 1cstazp_ptr_map_lo; zpa_1C = map pointer lo
$7230b9 69 49ldalevel_map_ptr_hi,y; Get map row base addr (hi) for current level
$723385 1dstazp_ptr_map_hi; zpa_1D = map pointer hi
$72358atxa; Y = tile slot index
$7236a8tay
$7237b1 1clda(zp_ptr_map_lo),y; read map cell for this tile
$7239d0 06bneb_7241; cell != 0? -> tile is active
$723Ba9 02lda#$02; cell is empty: mark tile inactive
$723D9d 96 70staentity_status,x; status = 2 (inactive)
$724060rts
$7241a9 00b_7241lda#$00; mark tile as active (status = 0) ; x-ref: $7239
$72439d 96 70staentity_status,x
$7246a9 00lda#$00
$724885 1dstazp_ptr_map_hi
$724Aa5 1fldazp_temp_y; zpa_1F (col) * 8 = column pixel X
$724C0aasla
$724D0aasla
$724E0aasla
$724F26 1drolzp_ptr_map_hi
$725185 1cstazp_ptr_map_lo; zpa_1C/1D = column pixel pos (16-bit)
$7253a5 1eldazp_temp_x; zpa_1E (row) * 8 = row pixel Y
$72550aasla
$72560aasla
$72570aasla
$725885 1bstazp_temp_pixel_y; zpa_1B = row pixel pos
$725Aa9 00lda#$00; clear state fields for this slot
$725C9d b4 70staentity_x_frac,x
$725F9d e1 70staentity_y_frac,x
$72629d a4 71staentity_anim_index,x
$72659d b3 71staentity_anim_counter,x
$72689d d1 71staentity_hit_flag,x
$726Bbd a5 70ldaentity_type,x; check tile type
$726Ed0 30bneb_72A0; type != 0? check next
$7270a5 1cldazp_ptr_map_lo; --- Type 0 handler ---
$727218clc
$727369 09adc#$09
$72759d c3 70staentity_x_lo,x; x_pos = col_px + 9
$7278a5 1dldazp_ptr_map_hi
$727A69 00adc#$00
$727C9d d2 70staentity_x_hi,x
$727Fa5 1bldazp_temp_pixel_y
$728118clc
$728269 0cadc#$0c
$72849d f0 70staentity_y,x
$7287ad f5 71ldatile_frames_type0; frame base = a_71F5 (tile $A7-$A8)
$728A9d ff 70staentity_frame,x
$728Da9 07lda#$07; speed = 7
$728F9d 0e 71staentity_anim_speed,x
$7292a9 0alda#$0a; anim delay = 10
$7294ac d9 60ldycurrent_level; level >= 10?
$7297c0 0acpy#$0a; no -> keep delay = 10
$729990 01bccb_729C; yes -> delay /= 2 (faster on harder levels)
$729B4alsra
$729C8d e2 71b_729Cstatile_delay_type0; store type 0 anim delay ; x-ref: $7299
$729F60rts
$72A0c9 01b_72A0cmp#$01; --- Type 1 handler --- ; x-ref: $726E
$72A2d0 38bneb_72DC
$72A4a5 1cldazp_ptr_map_lo; x_pos = col_px + 9
$72A618clc
$72A769 09adc#$09
$72A99d c3 70staentity_x_lo,x
$72ACa5 1dldazp_ptr_map_hi
$72AE69 00adc#$00
$72B09d d2 70staentity_x_hi,x
$72B3a5 1bldazp_temp_pixel_y
$72B59d f0 70staentity_y,x
$72B89d 3b 71staentity_y_limit,x
$72BBa9 00lda#$00
$72BD9d 95 71staentity_move_phase,x
$72C09d c2 71staentity_timer,x
$72C3ad f7 71ldatile_frames_type1; frame base = a_71F7 (tile $A9-$AA)
$72C69d ff 70staentity_frame,x
$72C9a9 08lda#$08; speed = 8
$72CB9d 0e 71staentity_anim_speed,x
$72CEa9 0alda#$0a; anim delay = 10
$72D0ac d9 60ldycurrent_level
$72D3c0 0acpy#$0a; no -> keep delay = 10
$72D590 01bccb_72D8; yes -> delay /= 2
$72D74alsra
$72D88d e3 71b_72D8statile_delay_type1; store type 1 anim delay ; x-ref: $72D5
$72DB60rts
$72DCc9 02b_72DCcmp#$02; --- Type 2 handler --- ; x-ref: $72A2
$72DEd0 6ebneb_734E
$72E0a5 1cldazp_ptr_map_lo; x_pos = col_px + 12
$72E218clc
$72E369 0cadc#$0c
$72E59d c3 70staentity_x_lo,x
$72E89d 1d 71staentity_x_limit_lo,x
$72EBa5 1dldazp_ptr_map_hi
$72ED69 00adc#$00
$72EF9d d2 70staentity_x_hi,x
$72F29d 2c 71staentity_x_limit_hi,x
$72F5a5 1bldazp_temp_pixel_y; y_pos = row_px + 8
$72F718clc
$72F869 08adc#$08
$72FA9d f0 70staentity_y,x
$72FD9d 3b 71staentity_y_limit,x
$7300a9 00lda#$00
$73029d 68 71staentity_vel_x_hi,x
$73059d 86 71staentity_vel_y_hi,x
$7308a9 c6lda#$c6
$730A9d 77 71staentity_vel_y_lo,x
$730Dad f9 71ldatile_frames_type2; frame base = a_71F9 (tile $AB-$AE)
$73109d ff 70staentity_frame,x
$7313ad d9 60ldacurrent_level; check difficulty tier
$7316c9 0acmp#$0a; level < 10? -> easy tier
$7318b0 10bcsb_732A; level >= 10: check mid tier
$731Aa9 21lda#$21; easy: horiz speed = $21
$731C9d 4a 71staentity_vel_x_lo,x
$731Fa9 01lda#$01; easy: vert speed = 1
$73219d 59 71staentity_vel_x_mid,x
$7324a9 11lda#$11; easy: combined speed = $11
$73268d e1 71statile_accel
$732960rts
$732Ac9 10b_732Acmp#$10; level >= 16? -> hard tier ; x-ref: $7318
$732Cb0 10bcsb_733E
$732Ea9 00lda#$00; mid: horiz speed = 0
$73309d 4a 71staentity_vel_x_lo,x
$7333a9 02lda#$02; mid: vert speed = 2
$73359d 59 71staentity_vel_x_mid,x
$7338a9 20lda#$20; mid: combined speed = $20
$733A8d e1 71statile_accel
$733D60rts
$733Ea9 00b_733Elda#$00; hard: horiz speed = 0 ; x-ref: $732C
$73409d 4a 71staentity_vel_x_lo,x
$7343a9 04lda#$04; hard: vert speed = 4
$73459d 59 71staentity_vel_x_mid,x
$7348a9 80lda#$80; hard: combined speed = $80
$734A8d e1 71statile_accel
$734D60rts
$734Ec9 03b_734Ecmp#$03; --- Type 3 handler --- ; x-ref: $72DE
$7350d0 30bneb_7382
$7352a5 1cldazp_ptr_map_lo
$735418clc; x_pos = col_px + 6
$735569 06adc#$06
$73579d c3 70staentity_x_lo,x
$735Aa5 1dldazp_ptr_map_hi
$735C69 00adc#$00
$735E9d d2 70staentity_x_hi,x
$7361a5 1bldazp_temp_pixel_y
$736318clc; y_pos = row_px + 6
$736469 06adc#$06
$73669d f0 70staentity_y,x
$7369ad fd 71ldatile_frames_type3; frame base = a_71FD (tile $AF-$B2 cycling)
$736C9d ff 70staentity_frame,x
$736Fa9 05lda#$05; speed = 5
$73719d 0e 71staentity_anim_speed,x
$7374a9 08lda#$08; anim delay = 8
$7376ac d9 60ldycurrent_level
$7379c0 0acpy#$0a; no -> keep delay = 8
$737B90 01bccb_737E; yes -> delay /= 2
$737D4alsra
$737E8d e4 71b_737Estatile_delay_type3; store type 3 anim delay ; x-ref: $737B
$738160rts
$7382a5 1cb_7382ldazp_ptr_map_lo; --- Type 4+ (default) handler --- ; x-ref: $7350
$738418clc; x_pos = col_px + 8
$738569 08adc#$08
$73879d c3 70staentity_x_lo,x
$738Aa5 1dldazp_ptr_map_hi
$738C69 00adc#$00
$738E9d d2 70staentity_x_hi,x
$7391a5 1bldazp_temp_pixel_y
$739318clc; y_pos = row_px + 3
$739469 03adc#$03
$73969d f0 70staentity_y,x
$7399ad 05 72ldatile_frames_type4; frame base = a_7205 (tile $B4-$B8 cycling)
$739C9d ff 70staentity_frame,x
$739Fa9 0elda#$0e; speed = 14
$73A19d 0e 71staentity_anim_speed,x
$73A460rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Iterates over all tile entities (tile_count-1 down to 0) and updates each one.
; For each tile, checks entity_status: if non-zero and not 2, decrements the timer
; and marks the tile as dying (status=2) when the timer expires. Active tiles
; (status=0) are dispatched by entity_type to the appropriate update handler:
; Type 0 → j_753E, Type 1 → bounce animation (s_73EF) + j_7563,
; Type 2 → gravity physics (update_tile_gravity) + j_7588,
; Type 3 → j_75AC, Default → s_74BD + j_75D1.
;
; Inputs: tile_count (number of tile entities to process)
; Outputs: None (updates entity arrays in-place)
; Side Effects: Modifies entity_status, entity_timer, positions, and velocities
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$73A5ae e0 71update_all_tilesldxtile_count; X = number of tile entities ; x-ref: $61E0, $6298
$73A8f0 07beqr_73B1; no tiles? skip
$73AAcadex; adjust to 0-based index
$73AB20 b2 73b_73ABjsrupdate_tile_entity; update tile X ; x-ref: $73AF
$73AEcadex; next tile
$73AF10 fabplb_73AB; loop until all processed
$73B160r_73B1rts; x-ref: $73A8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates a single tile entity: handles death timer countdown, dispatches
; type-specific movement (bounce, gravity, follow hero), and advances animation.
;
; Inputs: X = entity index into entity arrays
; Outputs: Updated entity_status, entity_timer, entity_frame, position arrays
; Side Effects: Entity position, animation frame, and status modified in-place
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$73B2bd 96 70update_tile_entityldaentity_status,x; status==0? tile is active ; x-ref: $73AB
$73B5f0 0fbeqb_73C6; status==2? already dying, skip
$73B7c9 02cmp#$02; status==2? → dead, skip
$73B9f0 0abeqr_73C5
$73BBde c2 71decentity_timer,x; count down entity timer
$73BEd0 05bner_73C5; timer not expired yet
$73C0a9 02lda#$02; mark as dying (status=2)
$73C29d 96 70staentity_status,x
$73C560r_73C5rts; x-ref: $73B9, $73BE
$73C6bd a5 70b_73C6ldaentity_type,x; get tile type for dispatch ; x-ref: $73B5
$73C9d0 03bneb_73CE; type==0?
$73CB4c 3e 75jmpj_753E; type 0: simple 2-frame animation
$73CEc9 01b_73CEcmp#$01; type==1? bounce animation ; x-ref: $73C9
$73D0d0 06bneb_73D8
$73D220 ef 73jsrupdate_tile_bounce; type 1: bounce movement + animate
$73D54c 63 75jmpj_7563
$73D8c9 02b_73D8cmp#$02; type==2? gravity physics ; x-ref: $73D0
$73DAd0 06bneb_73E2
$73DC20 19 74jsrupdate_tile_gravity; type 2: gravity physics + animate
$73DF4c 88 75jmpj_7588
$73E2c9 03b_73E2cmp#$03; type==3? ; x-ref: $73DA
$73E4d0 03bneb_73E9
$73E64c ac 75jmpj_75AC; type 3: 8-frame animation only
$73E920 bd 74b_73E9jsrchase_hero_horizontal; default: hero proximity + render ; x-ref: $73E4
$73EC4c d1 75jmpj_75D1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Animates a tile's vertical bounce by stepping through a 16-entry
; offset table (tile_bounce_offsets). Every 4 frames, the move_phase
; index advances. The phase wraps at 16, producing a smooth
; oscillation: +1..+4..+1, 0, -1..-4..-1, 0 pixels relative to
; entity_y_limit (the base Y position). Called as movement type 1
; in the entity movement dispatcher.
;
; Inputs: X = entity slot index
; Outputs: entity_y[x] = entity_y_limit[x] + tile_bounce_offsets[phase]
; Side Effects: entity_timer[x] and entity_move_phase[x] updated
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$73EFfe c2 71update_tile_bounceincentity_timer,x; tick frame delay counter ; x-ref: $73D2
$73F2bd c2 71ldaentity_timer,x
$73F5c9 04cmp#$04; trigger every 4 frames
$73F7d0 1fbner_7418; not yet → skip bounce update
$73F9a9 00lda#$00; reset timer to 0
$73FB9d c2 71staentity_timer,x
$73FEfe 95 71incentity_move_phase,x; advance to next phase in bounce table
$7401bd 95 71ldaentity_move_phase,x
$7404c9 10cmp#$10; 16 entries in table → wrap
$7406d0 05bneb_740D
$7408a9 00lda#$00; wrap phase back to 0
$740A9d 95 71staentity_move_phase,x
$740Da8b_740Dtay; Y = phase index ; x-ref: $7406
$740Ebd 3b 71ldaentity_y_limit,x; base Y position
$741118clc
$741279 e5 71adctile_bounce_offsets,y; add signed bounce offset
$74159d f0 70staentity_y,x; store final Y position
$741860r_7418rts; x-ref: $73F7
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates an animated tile's position using gravity/acceleration physics.
; Adds X velocity to X position, then adjusts velocity toward a target X limit.
; Adds Y velocity to Y position, then adjusts velocity toward a target Y limit.
; The tile accelerates toward its target position and decelerates past it,
; producing a gravity/spring effect. Called during tile animation phase 2.
;
; Inputs: X = tile index into entity arrays
; Outputs: Updated position (f_70B4/f_70C3/f_70D2,x) and Y pos (f_70E1/f_70F0,x)
; Side Effects: Velocity arrays (f_714A/f_7159/f_7168,x and f_7177/f_7186,x) modified
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7419bd b4 70update_tile_gravityldaentity_x_frac,x; pos_x_lo += vel_x_lo ; x-ref: $73DC
$741C18clc; clear carry for 24-bit add
$741D7d 4a 71adcentity_vel_x_lo,x; pos_lo += vel_lo
$74209d b4 70staentity_x_frac,x
$7423bd c3 70ldaentity_x_lo,x; pos_x_mid += vel_x_mid + carry
$74267d 59 71adcentity_vel_x_mid,x
$74299d c3 70staentity_x_lo,x
$742Cbd d2 70ldaentity_x_hi,x; pos_x_hi += vel_x_hi + carry
$742F7d 68 71adcentity_vel_x_hi,x
$74329d d2 70staentity_x_hi,x
$7435bd d2 70ldaentity_x_hi,x; compare pos_x_hi vs limit_hi
$7438dd 2c 71cmpentity_x_limit_hi,x; compare pos_hi with limit_hi
$743B90 27bccb_7464; pos_x_hi > limit_hi: decelerate
$743Dd0 08bneb_7447; above limit -> decelerate (bounce)
$743Fbd c3 70ldaentity_x_lo,x; pos_x_hi == limit_hi: check mid byte
$7442dd 1d 71cmpentity_x_limit_lo,x; compare pos_x_mid vs limit_mid
$744590 1dbccb_7464; pos < limit: accelerate
$7447bd 4a 71b_7447ldaentity_vel_x_lo,x; vel_x_lo -= accel (decelerate) ; x-ref: $743D
$744A38sec
$744Bed e1 71sbctile_accel; vel_lo -= a_71E1 (bounce decel)
$744E9d 4a 71staentity_vel_x_lo,x
$7451bd 59 71ldaentity_vel_x_mid,x; vel_x_mid -= borrow
$7454e9 00sbc#$00
$74569d 59 71staentity_vel_x_mid,x; vel_mid -= borrow
$7459bd 68 71ldaentity_vel_x_hi,x; vel_x_hi -= borrow
$745Ce9 00sbc#$00
$745E9d 68 71staentity_vel_x_hi,x; vel_hi -= borrow
$74614c 7e 74jmpj_747E; skip acceleration branch
$7464bd 4a 71b_7464ldaentity_vel_x_lo,x; vel_x_lo += accel (accelerate) ; x-ref: $743B, $7445
$746718clc
$74686d e1 71adctile_accel; vel_lo += a_71E1 (gravity accel)
$746B9d 4a 71staentity_vel_x_lo,x
$746Ebd 59 71ldaentity_vel_x_mid,x; vel_x_mid += carry
$747169 00adc#$00; vel_mid += carry
$74739d 59 71staentity_vel_x_mid,x
$7476bd 68 71ldaentity_vel_x_hi,x; vel_x_hi += carry
$747969 00adc#$00
$747B9d 68 71staentity_vel_x_hi,x
$747Ebd e1 70j_747Eldaentity_y_frac,x; pos_y_lo += vel_y_lo ; x-ref: $7461
$748118clc; h_pos_lo += h_vel_lo
$74827d 77 71adcentity_vel_y_lo,x
$74859d e1 70staentity_y_frac,x
$7488bd f0 70ldaentity_y,x; pos_y_hi += vel_y_hi + carry
$748B7d 86 71adcentity_vel_y_hi,x
$748E9d f0 70staentity_y,x
$7491bd f0 70ldaentity_y,x; compare pos_y_hi vs y_limit
$7494dd 3b 71cmpentity_y_limit,x
$749790 12bccb_74AB; below limit: increase vel_y
$7499bd 77 71ldaentity_vel_y_lo,x; vel_y_lo -= $0B (decelerate Y)
$749C38sec
$749De9 0bsbc#$0b; h_vel_lo -= $0B
$749F9d 77 71staentity_vel_y_lo,x
$74A2bd 86 71ldaentity_vel_y_hi,x; vel_y_hi -= borrow
$74A5e9 00sbc#$00
$74A79d 86 71staentity_vel_y_hi,x
$74AA60rts
$74ABbd 77 71b_74ABldaentity_vel_y_lo,x; vel_y_lo += $0B (accelerate Y) ; x-ref: $7497
$74AE18clc
$74AF69 0badc#$0b; h_vel_lo += $0B
$74B19d 77 71staentity_vel_y_lo,x
$74B4bd 86 71ldaentity_vel_y_hi,x; vel_y_hi += carry
$74B769 00adc#$00
$74B99d 86 71staentity_vel_y_hi,x
$74BC60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Chases the hero horizontally. Compares the entity's X position (indexed by X
; register) against the hero's X position, then moves the entity left or right
; by a half-pixel ($80 fractional) per call. Before moving, it checks the
; destination map tile — if the tile is >= $1A (solid/blocking), movement is
; skipped, preventing the entity from walking through walls.
;
; Inputs: X = entity index
; Outputs: entity_x_frac/lo/hi[X] updated
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
chase_hero_horizontal
$74BDad fa 7aldahero_state; Skip if hero is inactive/dead ; x-ref: $73E9
$74C0f0 01beqb_74C3
$74C260rts
$74C3ad fc 7ab_74C3ldahero_x_hi; Compare hero X vs entity X (hi byte) ; x-ref: $74C0
$74C6dd d2 70cmpentity_x_hi,x
$74C990 0dbccb_74D8; Entity is to the right of hero -> move left
$74CBd0 3ebneb_750B; Entity is to the left of hero -> move right
$74CDad fb 7aldahero_x_lo; Hi bytes equal, compare lo bytes
$74D0dd c3 70cmpentity_x_lo,x
$74D390 03bccb_74D8
$74D5d0 34bneb_750B
$74D760rts; Positions equal, nothing to do
$74D8bd c3 70b_74D8ldaentity_x_lo,x; --- Move entity LEFT toward hero --- ; x-ref: $74C9, $74D3
$74DB38sec
$74DCe9 09sbc#$09; Check tile 9 pixels to the left
$74DE85 fdstazp_ptr_dst_lo
$74E0bd d2 70ldaentity_x_hi,x
$74E3e9 00sbc#$00
$74E585 festazp_ptr_dst_hi
$74E7bd f0 70ldaentity_y,x
$74EA20 ca 50jsrget_map_tile
$74EDc9 1acmp#$1a; Tile >= $1A? Blocked, skip movement
$74EFb0 19bcsr_750A
$74F1bd b4 70ldaentity_x_frac,x
$74F438sec
$74F5e9 80sbc#$80; Subtract half-pixel (fractional step left)
$74F79d b4 70staentity_x_frac,x
$74FAbd c3 70ldaentity_x_lo,x
$74FDe9 00sbc#$00; Propagate borrow to lo byte
$74FF9d c3 70staentity_x_lo,x
$7502bd d2 70ldaentity_x_hi,x
$7505e9 00sbc#$00; Propagate borrow to hi byte
$75079d d2 70staentity_x_hi,x
$750A60r_750Arts; x-ref: $74EF
$750Bbd c3 70b_750Bldaentity_x_lo,x; --- Move entity RIGHT toward hero --- ; x-ref: $74CB, $74D5
$750E18clc
$750F69 06adc#$06; Check tile 6 pixels to the right
$751185 fdstazp_ptr_dst_lo
$7513bd d2 70ldaentity_x_hi,x
$751669 00adc#$00
$751885 festazp_ptr_dst_hi
$751Abd f0 70ldaentity_y,x
$751D20 ca 50jsrget_map_tile
$7520c9 1acmp#$1a; Tile >= $1A? Blocked, skip movement
$7522b0 19bcsr_753D
$7524bd b4 70ldaentity_x_frac,x
$752718clc
$752869 80adc#$80; Add half-pixel (fractional step right)
$752A9d b4 70staentity_x_frac,x
$752Dbd c3 70ldaentity_x_lo,x
$753069 00adc#$00; Propagate carry to lo byte
$75329d c3 70staentity_x_lo,x
$7535bd d2 70ldaentity_x_hi,x
$753869 00adc#$00; Propagate carry to hi byte
$753A9d d2 70staentity_x_hi,x
$753D60r_753Drts; x-ref: $7522
$753Efe b3 71j_753Eincentity_anim_counter,x; x-ref: $73CB
$7541bd b3 71ldaentity_anim_counter,x
$7544cd e2 71cmptile_delay_type0
$7547d0 19bner_7562
$7549a9 00lda#$00
$754B9d b3 71staentity_anim_counter,x
$754Ebc a4 71ldyentity_anim_index,x
$7551c8iny
$7552c0 02cpy#$02
$7554d0 02bneb_7558
$7556a0 00ldy#$00
$755898b_7558tya; x-ref: $7554
$75599d a4 71staentity_anim_index,x
$755Cb9 f5 71ldatile_frames_type0,y
$755F9d ff 70staentity_frame,x
$756260r_7562rts; x-ref: $7547
$7563fe b3 71j_7563incentity_anim_counter,x; x-ref: $73D5
$7566bd b3 71ldaentity_anim_counter,x
$7569cd e3 71cmptile_delay_type1
$756Cd0 19bner_7587
$756Ea9 00lda#$00
$75709d b3 71staentity_anim_counter,x
$7573bc a4 71ldyentity_anim_index,x
$7576c8iny
$7577c0 02cpy#$02
$7579d0 02bneb_757D
$757Ba0 00ldy#$00
$757D98b_757Dtya; x-ref: $7579
$757E9d a4 71staentity_anim_index,x
$7581b9 f7 71ldatile_frames_type1,y
$75849d ff 70staentity_frame,x
$758760r_7587rts; x-ref: $756C
$7588fe b3 71j_7588incentity_anim_counter,x; x-ref: $73DF
$758Bbd b3 71ldaentity_anim_counter,x
$758Ec9 04cmp#$04
$7590d0 19bner_75AB
$7592a9 00lda#$00
$75949d b3 71staentity_anim_counter,x
$7597bc a4 71ldyentity_anim_index,x
$759Ac8iny
$759Bc0 04cpy#$04
$759Dd0 02bneb_75A1
$759Fa0 00ldy#$00
$75A198b_75A1tya; x-ref: $759D
$75A29d a4 71staentity_anim_index,x
$75A5b9 f9 71ldatile_frames_type2,y
$75A89d ff 70staentity_frame,x
$75AB60r_75ABrts; x-ref: $7590
$75ACfe b3 71j_75ACincentity_anim_counter,x; x-ref: $73E6
$75AFbd b3 71ldaentity_anim_counter,x
$75B2cd e4 71cmptile_delay_type3
$75B5d0 19bner_75D0
$75B7a9 00lda#$00
$75B99d b3 71staentity_anim_counter,x
$75BCbc a4 71ldyentity_anim_index,x
$75BFc8iny
$75C0c0 08cpy#$08
$75C2d0 02bneb_75C6
$75C4a0 00ldy#$00
$75C698b_75C6tya; x-ref: $75C2
$75C79d a4 71staentity_anim_index,x
$75CAb9 fd 71ldatile_frames_type3,y
$75CD9d ff 70staentity_frame,x
$75D060r_75D0rts; x-ref: $75B5
$75D1fe b3 71j_75D1incentity_anim_counter,x; x-ref: $73EC
$75D4bd b3 71ldaentity_anim_counter,x
$75D7c9 10cmp#$10
$75D9d0 19bner_75F4
$75DBa9 00lda#$00
$75DD9d b3 71staentity_anim_counter,x
$75E0bc a4 71ldyentity_anim_index,x
$75E3c8iny
$75E4c0 08cpy#$08
$75E6d0 02bneb_75EA
$75E8a0 00ldy#$00
$75EA98b_75EAtya; x-ref: $75E6
$75EB9d a4 71staentity_anim_index,x
$75EEb9 05 72ldatile_frames_type4,y
$75F19d ff 70staentity_frame,x
$75F460r_75F4rts; x-ref: $75D9
$75F5a9 01j_75F5lda#$01; x-ref: $1FFB, $2006
$75F79d 96 70staentity_status,x
$75FAa9 b9lda#$b9
$75FC9d ff 70staentity_frame,x
$75FFa9 01lda#$01
$76019d 0e 71staentity_anim_speed,x
$7604a9 1elda#$1e
$76069d c2 71staentity_timer,x
$760920 6f 77jsrclear_map_tile
$760Ca9 50lda#$50
$760E85 fbstazp_ptr_src_lo
$7610a9 00lda#$00
$761285 fcstazp_ptr_src_hi
$7614a9 00lda#$00
$761685 fdstazp_ptr_dst_lo
$76184c d8 51jmpj_51D8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Looks up the enemy type from f_70A5[X] and sets the collision hitbox
; bounding-box parameters in zpa_02..zpa_05.
; zpa_02 = Y upper bound (positive threshold)
; zpa_03 = Y lower bound (negative threshold, signed)
; zpa_04 = X upper bound (positive threshold)
; zpa_05 = X lower bound (negative threshold, signed)
; The enemy type selects a preset hitbox size; for types 2–4 the
; sub-type in f_70FF[X] further refines the horizontal bounds.
;
; Inputs: X = enemy slot index, f_70A5[X] = enemy type,
; f_70FF[X] = enemy sub-type (used by types 2–4)
; Outputs: zpa_02..zpa_05 = hitbox bounding-box thresholds
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$761Bbd a5 70get_enemy_hitboxldaentity_type,x; A = enemy type ; x-ref: $1F2D
$761Ed0 11bneb_7631; type != 0? check next
$7620a9 0clda#$0c; --- Type 0: standard hitbox ---
$762285 02stazp_work0; Y upper = $0C
$7624a9 f4lda#$f4; Y lower = $F4 (-12 signed)
$762685 03stazp_work1
$7628a9 0blda#$0b; X upper = $0B
$762A85 04stazp_work2
$762Ca9 f4lda#$f4; X lower = $F4 (-12 signed)
$762E85 05stazp_work3
$763060rts
$7631c9 01b_7631cmp#$01; type == 1? ; x-ref: $761E
$7633d0 11bneb_7646; no? check next type
$7635a9 0elda#$0e; --- Type 1: slightly wider Y ---
$763785 02stazp_work0; Y upper = $0E
$7639a9 f4lda#$f4
$763B85 03stazp_work1; X upper = $0B (same as type 0)
$763Da9 0blda#$0b
$763F85 04stazp_work2
$7641a9 f4lda#$f4
$764385 05stazp_work3
$764560rts
$7646c9 02b_7646cmp#$02; type == 2? ; x-ref: $7633
$7648d0 2ebneb_7678; no? check type 3
$764Aa9 0flda#$0f; --- Type 2: Y=$0F/$F2, X varies by sub-type ---
$764C85 02stazp_work0; Y upper = $0F
$764Ea9 f2lda#$f2
$765085 03stazp_work1; Y lower = $F2 (-14 signed)
$7652bd ff 70ldaentity_frame,x; A = sub-type from f_70FF[X]
$7655c9 adcmp#$ad; sub-type == $AD?
$7657d0 09bneb_7662; no? check $AE
$7659a9 0blda#$0b; sub-type $AD: X = ($0B, $F4)
$765B85 04stazp_work2
$765Da9 f4lda#$f4
$765F85 05stazp_work3
$766160rts
$7662c9 aeb_7662cmp#$ae; sub-type == $AE? ; x-ref: $7657
$7664d0 09bneb_766F; no? use default X bounds
$7666a9 09lda#$09; sub-type $AE: X = ($09, $F6)
$766885 04stazp_work2
$766Aa9 f6lda#$f6
$766C85 05stazp_work3
$766E60rts
$766Fa9 07b_766Flda#$07; default sub-type: X = ($07, $F8) ; x-ref: $7664
$767185 04stazp_work2
$7673a9 f8lda#$f8
$767585 05stazp_work3
$767760rts
$7678c9 03b_7678cmp#$03; type == 3? ; x-ref: $7648
$767Ad0 19bneb_7695; no? must be type >= 4
$767Ca9 0clda#$0c; --- Type 3: Y=($0C,$F4), computed X ---
$767E85 02stazp_work0; Y upper = $0C
$7680a9 f4lda#$f4
$768285 03stazp_work1
$7684bd ff 70ldaentity_frame,x; A = sub-type from f_70FF[X]
$768738sec; A = sub-type - $AF
$7688e9 afsbc#$af
$768A0aasla; A = (sub-type - $AF) * 4
$768B0aasla
$768C69 01adc#$01; X upper = (sub-type - $AF) * 4 + 1
$768E85 04stazp_work2
$7690a9 fflda#$ff; X lower = $FF (-1: very wide)
$769285 05stazp_work3
$769460rts
$7695a9 00b_7695lda#$00; --- Type >= 4: computed Y, fixed X --- ; x-ref: $767A
$769785 02stazp_work0; Y upper = $00 (disabled)
$7699bd ff 70ldaentity_frame,x; A = sub-type from f_70FF[X]
$769C38sec; A = sub-type - $B4
$769De9 b4sbc#$b4
$769F0aasla; temp = (sub-type - $B4) * 2
$76A085 ffstazp_temp
$76A2a9 f8lda#$f8; Y lower = $F8 - temp
$76A438sec
$76A5e5 ffsbczp_temp
$76A785 03stazp_work1
$76A9bd ff 70ldaentity_frame,x; re-read sub-type for special case
$76ACc9 b6cmp#$b6; sub-type == $B6?
$76AEd0 04bneb_76B4; no? skip adjustment
$76B0c6 03deczp_work1; sub-type $B6: Y lower -= 2 (extra tall)
$76B2c6 03deczp_work1
$76B4a9 09b_76B4lda#$09; X upper = $09 ; x-ref: $76AE
$76B685 04stazp_work2
$76B8a9 f6lda#$f6; X lower = $F6 (-10 signed)
$76BA85 05stazp_work3
$76BC60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Returns the laser collision hitbox parameters for the current enemy.
; Branches on the enemy state (f_70A5,x) and sub-type (f_70FF,x) to select
; Y-range bounds (zpa_02/zpa_03) and X-range bounds (zpa_04/zpa_05).
; The direction flag a_7B01 and a_7828 adjust X bounds for facing.
;
; Inputs: X = enemy slot index
; Outputs: zpa_02 = Y upper bound, zpa_03 = Y lower bound,
; zpa_04 = X right bound, zpa_05 = X left bound
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
get_laser_hitbox_params
$76BDbd a5 70ldaentity_type,x; branch on enemy state ; x-ref: $1FD5
$76C0d0 3dbneb_76FF; state != 0 → check further
$76C2bd ff 70ldaentity_frame,x; load enemy sub-type
$76C5c9 a7cmp#$a7; sub-type == $A7?
$76C7d0 07bneb_76D0
$76C9a9 0blda#$0b; Y upper = 11 (tall hitbox)
$76CB85 02stazp_work0
$76CD4c d4 76jmpj_76D4
$76D0a9 0ab_76D0lda#$0a; Y upper = 10 (default for state 0) ; x-ref: $76C7
$76D285 02stazp_work0
$76D4a9 05j_76D4lda#$05; Y lower = 5 ; x-ref: $76CD
$76D685 03stazp_work1
$76D8ad 01 7bj_76D8ldahero_scroll_dir; check direction flag ; x-ref: $7712, $771D, $772C
$76DBc9 01cmp#$01; facing right?
$76DDd0 10bneb_76EF
$76DFa9 34lda#$34; X right = $34 (facing right)
$76E1ac 28 78ldyhit_strength; check secondary flag
$76E4f0 02beqb_76E8
$76E6a9 19lda#$19; X right = $19 (alt facing)
$76E885 04b_76E8stazp_work2; store X right bound ; x-ref: $76E4
$76EAa9 fflda#$ff; X left = $FF (no left bound)
$76EC85 05stazp_work3
$76EE60rts
$76EFa9 00b_76EFlda#$00; facing left path ; x-ref: $76DD
$76F185 04stazp_work2; X right = $00 (no right bound)
$76F3a9 cblda#$cb; X left = $CB (facing left)
$76F5ac 28 78ldyhit_strength; check secondary flag
$76F8f0 02beqb_76FC
$76FAa9 e6lda#$e6; X left = $E6 (alt facing)
$76FC85 05b_76FCstazp_work3; store X left bound ; x-ref: $76F8
$76FE60rts
$76FFc9 01b_76FFcmp#$01; --- State 1 --- ; x-ref: $76C0
$7701d0 1dbneb_7720
$7703bd ff 70ldaentity_frame,x; load enemy sub-type
$7706c9 a9cmp#$a9; sub-type == $A9?
$7708d0 0bbneb_7715
$770Aa9 0blda#$0b; Y upper = 11
$770C85 02stazp_work0
$770Ea9 04lda#$04; Y lower = 4
$771085 03stazp_work1
$77124c d8 76jmpj_76D8; → compute X bounds
$7715a9 0db_7715lda#$0d; Y upper = 13 (default for state 1) ; x-ref: $7708
$771785 02stazp_work0
$7719a9 05lda#$05; Y lower = 5
$771B85 03stazp_work1
$771D4c d8 76jmpj_76D8; → compute X bounds
$7720c9 02b_7720cmp#$02; --- State 2 --- ; x-ref: $7701
$7722d0 0bbneb_772F
$7724a9 0elda#$0e; Y upper = 14
$772685 02stazp_work0
$7728a9 03lda#$03; Y lower = 3
$772A85 03stazp_work1
$772C4c d8 76jmpj_76D8; → compute X bounds
$772Fa9 0bb_772Flda#$0b; --- Default state (>=3) --- ; x-ref: $7722
$773185 02stazp_work0; Y upper = 11
$7733a9 05lda#$05
$773585 03stazp_work1; Y lower = 5
$7737ad 01 7bldahero_scroll_dir; check direction flag
$773Ac9 01cmp#$01
$773Cd0 11bneb_774F; facing left? → fixed X bounds
$773Ebd ff 70ldaentity_frame,x; load sub-type
$774138sec; sub-type -= $AF
$7742e9 afsbc#$af
$77440aasla; (sub-type - $AF) * 4
$77450aasla
$774669 2aadc#$2a; X right = (sub-type-$AF)*4 + $2A
$774885 04stazp_work2; store X right bound
$774Aa9 fflda#$ff; X left = $FF
$774C85 05stazp_work3
$774E60rts
$774Fa9 00b_774Flda#$00; facing left: X right = $00, X left = $C7 ; x-ref: $773C
$775185 04stazp_work2
$7753a9 c7lda#$c7
$775585 05stazp_work3
$775760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Marks all hit entities as destroyed and clears them from the level map.
; Loops through entities 0..tile_count-1, checks entity_hit_flag for each.
; If hit, sets entity_status to $02 (destroyed) and zeroes the entity's
; slot in the current room's level map via clear_entity_from_map.
;
; Inputs: None (reads tile_count, entity_hit_flag)
; Outputs: None
; Side Effects: Sets entity_status=$02 for hit entities, clears map slots
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7758ae e0 71process_hit_entitiesldxtile_count; count of active entities ; x-ref: $7B62
$775Bf0 11beqr_776E; no entities? done
$775Dcadex; start from last entity
$775Ebd d1 71b_775Eldaentity_hit_flag,x; was this entity hit? ; x-ref: $776C
$7761f0 08beqb_776B; no → skip
$7763a9 02lda#$02; $02 = destroyed
$77659d 96 70staentity_status,x
$776820 6f 77jsrclear_map_tile; remove from level map
$776Bcab_776Bdex; next entity ; x-ref: $7761
$776C10 f0bplb_775E; loop until all checked
$776E60r_776Erts; x-ref: $775B
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears an entity's slot from the current room's level map.
; Looks up the level map pointer for the current room, then writes $00
; at offset X (entity index) to remove the entity from the map.
;
; Inputs: X = entity index
; Outputs: Y = entity index (copied from X)
; Side Effects: Zeroes one byte in the level map
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$776Fac 9e 3fclear_map_tileldycurrent_room_index; get current room ; x-ref: $7609, $7768
$7772b9 09 49ldalevel_map_ptr_lo,y; lo byte of map pointer
$777585 1cstazp_ptr_map_lo
$7777b9 69 49ldalevel_map_ptr_hi,y; hi byte of map pointer
$777A85 1dstazp_ptr_map_hi
$777C8atxa; copy entity index to Y
$777Da8tay
$777Ea9 00lda#$00; clear entity from map
$778091 1csta(zp_ptr_map_lo),y; clear tile at map[entity_index]
$778260rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Iterates through all active entities (enemies/creatures) and populates
; the zero-page sprite arrays used by the sprite sort-and-render multiplexer.
; For each entity: if status == 2 (dead), marks sprite Y as $FF (off-screen);
; if rendering is locked, skips entirely; otherwise computes screen-space
; coordinates from entity world position with fixed offsets (+12 X, +37 Y)
; and copies the animation frame and speed into the sprite arrays.
;
; Inputs: tile_count (number of entities), entity tables (status, x, y, frame, anim_speed)
; Outputs: zpf_37,x (Y pos), zpf_4A,x (X lo), zpf_5D,x (X hi), zpf_70,x (frame), zpf_83,x (anim speed)
; Side Effects: None (only writes to zero-page sprite arrays)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7783ae e0 71setup_entity_spritesldxtile_count; Load entity count as loop index ; x-ref: $647D
$7786d0 01bneb_7789; No entities? bail out
$778860rts
$7789cab_7789dex; Adjust to 0-based index (count-1..0) ; x-ref: $7786
$778Abd 96 70b_778Aldaentity_status,x; Check entity alive/dead status ; x-ref: $77BF
$778Dc9 02cmp#$02; Status == 2 means dead/inactive
$778Fd0 07bneb_7798
$7791a9 fflda#$ff; $FF = off-screen Y (hide sprite)
$779395 37stazp_mux_y_entities,x; Mark sprite Y as off-screen
$77954c be 77jmpj_77BE
$7798ad a7 3fb_7798ldarendering_in_progress; Skip sprite setup if render locked ; x-ref: $778F
$779Bd0 21bnej_77BE
$779Dbd c3 70ldaentity_x_lo,x; Get entity world X position (lo)
$77A018clc
$77A169 0cadc#$0c; Add horizontal sprite offset (+12 px)
$77A395 4astazp_mux_x_lo_entities,x; Store screen X lo for multiplexer
$77A5bd d2 70ldaentity_x_hi,x; Get entity world X position (hi)
$77A869 00adc#$00; Propagate carry to hi byte
$77AA95 5dstazp_mux_x_hi_entities,x; Store screen X hi for multiplexer
$77ACbd f0 70ldaentity_y,x; Get entity world Y position
$77AF18clc
$77B069 25adc#$25; Add vertical sprite offset (+37 px)
$77B295 37stazp_mux_y_entities,x; Store screen Y for multiplexer
$77B4bd ff 70ldaentity_frame,x; Copy current animation frame
$77B795 70stazp_mux_frame_entities,x
$77B9bd 0e 71ldaentity_anim_speed,x; Copy animation speed/delay
$77BC95 83stazp_mux_color_entities,x
$77BEcaj_77BEdex; Next entity (count down) ; x-ref: $7795, $779B
$77BF10 c9bplb_778A; Loop until all entities processed
$77C160rts
; Hazard active flag ($FF=active, $00=inactive). Set by setup_hazard_position, cleared by s_77C6.
$77C2hazard_active.byte$00; x-ref: $1E9A, $1EDB, $4AF5, $77C8, $77D2, ...
; Hazard pixel X position, low byte (column*8+4).
$77C3hazard_x_lo.byte$00; x-ref: $1EB7, $1EF8, $77E3, $7807
; Hazard pixel X position, high byte.
$77C4hazard_x_hi.byte$00; x-ref: $1EBE, $1EFF, $77EA, $780F
; Hazard pixel Y position (row*8+7).
$77C5hazard_y.byte$00; x-ref: $1EA8, $1EE9, $77F5, $7816
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Deactivates the hazard collision box by clearing the hazard_active flag.
; Called during subsystem reset to ensure no stale hazard state persists
; between screens or game states.
;
; Inputs: None
; Outputs: None
; Side Effects: Sets hazard_active ($77C2) to $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$77C6a9 00reset_hazardlda#$00; A = 0 (inactive) ; x-ref: $4B07
$77C88d c2 77stahazard_active; clear hazard collision box
$77CB60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Converts tile-grid coordinates (X=row, Y=column) to pixel coordinates
; and activates the hazard collision box at that position.
; Pixel X (16-bit) = column * 8 + 4 → stored in a_77C3/a_77C4
; Pixel Y = row * 8 + 7 → stored in a_77C5
; Sets a_77C2 = $FF to mark the hazard as active.
; Called as tile 12 handler from dispatch_complex_tile.
;
; Inputs: X = tile row, Y = tile column
; Outputs: X, Y preserved (restored before RTS)
; Side Effects: Sets a_77C2 (active flag), a_77C3/C4 (pixel X), a_77C5 (pixel Y)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
setup_hazard_position
$77CC86 1estxzp_temp_x; save tile row ; x-ref: $4FCC
$77CE84 1fstyzp_temp_y; save tile column
$77D0a9 fflda#$ff; mark hazard as active
$77D28d c2 77stahazard_active
$77D5a9 00lda#$00
$77D785 1dstazp_ptr_map_hi; clear hi byte for 16-bit result
$77D9a5 1fldazp_temp_y; column * 8 (shift left 3)
$77DB0aasla
$77DC0aasla
$77DD0aasla
$77DE26 1drolzp_ptr_map_hi; rotate carry into hi byte
$77E018clc
$77E169 04adc#$04; pixel_x_lo = column * 8 + 4
$77E38d c3 77stahazard_x_lo; store pixel X lo byte
$77E6a5 1dldazp_ptr_map_hi; add carry to hi byte
$77E869 00adc#$00
$77EA8d c4 77stahazard_x_hi; store pixel X hi byte
$77EDa5 1eldazp_temp_x; row * 8 (shift left 3)
$77EF0aasla
$77F00aasla
$77F10aasla
$77F218clc
$77F369 07adc#$07; pixel_y = row * 8 + 7
$77F58d c5 77stahazard_y; store pixel Y
$77F8a6 1eldxzp_temp_x; restore tile row in X
$77FAa4 1fldyzp_temp_y; restore tile column in Y
$77FC60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up the hazard sprite for the multiplexer. Reads the hazard object's
; position (hazard_x_lo/hi, hazard_y) and writes adjusted screen coordinates,
; sprite frame ($BA), and color (dark grey/$0B) into the zero-page sprite
; arrays. Skipped if hazard is inactive or rendering is in progress.
;
; Inputs: hazard_active, hazard_x_lo, hazard_x_hi, hazard_y
; Outputs: zpa_58 (X lo), zpa_6B (X hi), zp_screen_dirty (Y), zpa_7E (frame),
; zp_spr_color_extra (color)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$77FDad c2 77setup_hazard_spriteldahazard_active; Hazard active? ; x-ref: $6480
$7800f0 24beqr_7826; No → skip
$7802ad a7 3fldarendering_in_progress; Renderer busy?
$7805d0 1fbner_7826; Yes → skip to avoid conflict
$7807ad c3 77ldahazard_x_lo; Hazard X lo
$780A18clc
$780B69 0cadc#$0c; Add X offset (+12 pixels)
$780D85 58stazp_mux_x_lo_hazard; → sprite X lo
$780Fad c4 77ldahazard_x_hi; Hazard X hi
$781269 00adc#$00; Propagate carry
$781485 6bstazp_mux_x_hi_hazard; → sprite X hi
$7816ad c5 77ldahazard_y; Hazard Y
$781918clc
$781A69 25adc#$25; Add Y offset (+37 pixels)
$781C85 45stazp_screen_dirty; → sprite Y position
$781Ea9 balda#$ba; Sprite frame $BA = hazard shape
$782085 7estazp_mux_frame_hazard; → sprite frame
$7822a9 0blda#VicIIColors.DARK_GREY; $0B = dark grey
$782485 91stazp_spr_color_extra; → sprite color
$782660r_7826rts; x-ref: $7800, $7805
; Fire/dynamite active flag ($FF=active, $00=inactive). Set by handle_fire_pressed, cleared by s_783D/s_7912.
$7827fire_active.byte$00; x-ref: $1ED6, $1FBD, $783F, $784B, $78FA, ...
; Hit strength result: 0=miss, 1=partial/ceiling, 2=direct hit. Written by check_dynamite_enemy_collision.
$7828hit_strength.byte$00; x-ref: $76E1, $76F5, $7859, $7871, $78D1, ...
; Fire animation frame counter (0-2). Advances every 2 sub-frames, wraps at 3.
$7829fire_anim_frame.byte$00; x-ref: $78EA, $78ED, $78F6, $7906, $7956
; Fire animation sub-frame counter (0-1). Increments each update, resets at 2 to advance fire_anim_frame.
$782Afire_anim_tick.byte$00; x-ref: $78DB, $78DE, $78E7, $7909
; Fire/dynamite sprite X offset table (low bytes), indexed by direction*2
$782Bfire_offset_x_lo.byte$da; x-ref: $7943
; Fire/dynamite sprite X offset table (high bytes), indexed by direction*2
$782Cfire_offset_x_hi.byte$ff, $f0, $ff, $f4, $ff, $0e, $00, $10; x-ref: $794A
$7834.byte$00, $0c, $00
; Sprite pointer table for fire/dynamite animation (6 frames: 3 normal + 3 direct-hit)
$7837fire_sprite_frames.byte$9d, $9e, $9f, $a0, $a0, $89; x-ref: $7964
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the dynamite/projectile system for a new level. Clears any
; in-flight projectile, resets the countdown timer, and arms the dynamite
; so the player can throw it.
;
; Inputs: None
; Outputs: fire_active = 0, zp_dynamite_timer = 10, zp_dynamite_active = $FF
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$783Da9 00init_dynamite_statelda#$00; Deactivate any in-flight projectile ; x-ref: $6134
$783F8d 27 78stafire_active
$7842a9 0alda#$0a; Timer = 10 frames before detonation
$784485 81stazp_dynamite_timer; init dynamite timer to 10 frames
$7846a9 fflda#$ff; $FF = dynamite armed and ready to throw
$784885 a7stazp_dynamite_active; arm dynamite ($FF = active)
$784A60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the active dynamite/projectile collides with any enemy or hits
; the ceiling boundary. If a collision is found, sets the hit strength in
; a_7828 (1=partial, 2=direct) and decrements the enemy's HP/timer.
; Also advances the dynamite animation frame counter each call.
;
; Inputs: a_7827 = dynamite active flag (0 = inactive)
; Outputs: a_7828 = collision result (0=miss, 1=partial, 2=direct hit)
; Side Effects: Enemy HP decremented on hit via j_881E, animation frame
; counters a_7829/a_782A advanced
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_dynamite_collision
$784Bad 27 78ldafire_active; Dynamite active? ; x-ref: $61DA
$784Ed0 01bneb_7851; No → skip
$785060rts
$785120 57 78b_7851jsrcheck_dynamite_enemy_collision; Check collision with enemies ; x-ref: $784E
$78544c db 78jmpj_78DB; Advance animation frame
check_dynamite_enemy_collision
$7857a9 00lda#$00; Clear collision result ; x-ref: $7851
$78598d 28 78stahit_strength; a_7828 = 0 (no hit)
$785Cad 01 7bldahero_scroll_dir; Check projectile direction
$785Fc9 01cmp#$01; Direction == 1 (upward)?
$7861d0 12bneb_7875
$7863ad fc 7aldahero_x_hi; Y-pos high byte
$7866d0 0dbneb_7875; Not zero → skip ceiling check
$7868ad fb 7aldahero_x_lo; Y-pos low byte
$786Bc9 20cmp#$20; Y < $20 (near top)?
$786Db0 06bcsb_7875
$786Fa9 01lda#$01; Hit ceiling boundary
$78718d 28 78stahit_strength; collision_result = 1
$787460rts
$7875ae e4 85b_7875ldxdynamic_tile_count; Enemy count ; x-ref: $7861, $7866, $786D
$7878d0 01bneb_787B; No enemies → return
$787A60rts
$787Bcab_787Bdex; Loop index (count-1 → 0) ; x-ref: $7878
$787Cbd c9 85b_787Cldadtile_id,x; Enemy active? ; x-ref: $78D8
$787Ff0 56beqb_78D7; Inactive → next enemy
$7881ad fd 7aldahero_y_pos; Projectile X-pos
$788438sec
$7885fd d5 85sbcdtile_x,x; Minus enemy X-pos
$7888c9 1ccmp#$1c; X-distance >= 28?
$788A10 4bbplb_78D7
$788Cc9 f4cmp#$f4; X-distance <= -12?
$788E30 47bmib_78D7
$7890ad 01 7bldahero_scroll_dir; Check direction again
$7893c9 01cmp#$01; Direction == 1?
$7895d0 17bneb_78AE
$7897ad fb 7aldahero_x_lo; Y-dist = projectile_Y - enemy_Y
$789A38sec
$789Bfd cf 85sbcdtile_y_lo,x; Low byte subtract
$789Ea8tay
$789Fad fc 7aldahero_x_hi; High byte of Y-pos
$78A2fd d2 85sbcdtile_y_hi,x; High byte subtract
$78A5d0 30bneb_78D7; High byte != 0 → miss
$78A7c0 36cpy#$36; Y-distance >= $36?
$78A9b0 2cbcsb_78D7; Too far → miss
$78AB4c c2 78jmpj_78C2; Within range → classify hit
$78AEbd cf 85b_78AEldadtile_y_lo,x; Y-dist = enemy_Y - projectile_Y ; x-ref: $7895
$78B138sec
$78B2ed fb 7asbchero_x_lo
$78B5a8tay
$78B6bd d2 85ldadtile_y_hi,x; High byte subtract
$78B9ed fc 7asbchero_x_hi
$78BCd0 19bneb_78D7; High byte != 0 → miss
$78BEc0 36cpy#$36; Y-distance >= $36?
$78C0b0 15bcsb_78D7; Too far → miss
$78C2c0 28j_78C2cpy#$28; Y-dist >= $28 (40)? ; x-ref: $78AB
$78C4b0 0ebcsb_78D4; Far → no damage (graze)
$78C6c0 18cpy#$18; Y-dist >= $18 (24)?
$78C8b0 05bcsb_78CF; Partial hit → strength 1
$78CAa9 02lda#$02; Direct hit → strength 2
$78CC4c d1 78jmpj_78D1
$78CFa9 01b_78CFlda#$01; x-ref: $78C8
$78D18d 28 78j_78D1stahit_strength; Store hit strength ; x-ref: $78CC
$78D44c 1e 88b_78D4jmpj_881E; Decrement enemy HP/timer ; x-ref: $78C4
$78D7cab_78D7dex; Next enemy ; x-ref: $787F, $788A, $788E, $78A5, $78A9, ...
$78D810 a2bplb_787C; Loop until all checked
$78DA60rts
$78DBee 2a 78j_78DBincfire_anim_tick; Advance sub-frame counter ; x-ref: $7854
$78DEad 2a 78ldafire_anim_tick
$78E1c9 02cmp#$02; Every 2 sub-frames?
$78E3d0 14bner_78F9
$78E5a9 00lda#$00; Reset sub-frame counter
$78E78d 2a 78stafire_anim_tick
$78EAee 29 78incfire_anim_frame; Advance animation frame
$78EDad 29 78ldafire_anim_frame
$78F0c9 03cmp#$03; Wrap at frame 3
$78F2d0 05bner_78F9
$78F4a9 00lda#$00; Reset frame to 0
$78F68d 29 78stafire_anim_frame
$78F960r_78F9rts; x-ref: $78E3, $78F2
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles the fire button being pressed. If fire was already registered
; (a_7827 != 0), exits immediately to avoid re-triggering. Otherwise, sets
; the fire-active flag, resets the fire animation frame counters (a_7829
; and a_782A), and triggers the fire sound effect via a_8988.
;
; Inputs: a_7827 (fire-active flag)
; Outputs: a_7827 = $FF, a_7829 = 0, a_782A = 0, a_8988 = 1
; Side Effects: Triggers fire sound effect (SID voice 2)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$78FAad 27 78handle_fire_pressedldafire_active; check if fire already active ; x-ref: $7C15
$78FDd0 12bner_7911; already active? skip
$78FFa9 fflda#$ff; mark fire as active ($FF)
$79018d 27 78stafire_active
$7904a9 00lda#$00; reset animation frame counters
$79068d 29 78stafire_anim_frame
$79098d 2a 78stafire_anim_tick
$790Ca9 01lda#$01; trigger fire sound effect
$790E8d 88 89stasfx_fire_phase
$791160r_7911rts; x-ref: $78FD
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Deactivates the fire weapon. If fire_active is set, clears it and
; stops the fire sound effect by jumping to the SFX shutdown routine.
; If fire is already inactive, returns immediately.
;
; Inputs: fire_active ($7827)
; Outputs: fire_active cleared to 0
; Side Effects: Stops fire SFX on SID Voice 2 (via j_8A02)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7912ad 27 78deactivate_fireldafire_active; Is fire currently active? ; x-ref: $7C1B, $800C, $819A
$7915f0 08beqr_791F; No -> nothing to deactivate
$7917a9 00lda#$00; Clear fire_active flag
$79198d 27 78stafire_active
$791C4c 02 8ajmpj_8A02; Stop fire SFX (tail call)
$791F60r_791Frts; x-ref: $7915
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up the dynamite/fire explosion sprite. Calculates the sprite's screen
; position and animation frame based on the hero's position, facing direction,
; and hit strength. Returns early (with $FF = no sprite) if fire is inactive
; or the hero is below the ground boundary.
;
; Inputs: fire_active, hero_y_pos, hero_scroll_dir, hit_strength,
; fire_anim_frame, zp_spr_x_lo/hi, zp_spr_y_pos
; Outputs: zpa_48 = sprite X low, zpa_5B = sprite X high, zpa_35 = sprite Y,
; zpa_6E = sprite frame pointer, zpa_94 = horizontal flip flag
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7920ad 27 78setup_fire_spriteldafire_active; Is fire/dynamite active? ; x-ref: $6489
$7923f0 07beqb_792C; No → return $FF (no sprite)
$7925ad fd 7aldahero_y_pos; Get hero's Y screen position
$7928c9 87cmp#$87; Below ground boundary ($87)?
$792A90 05bccb_7931; No → calculate sprite position
$792Ca9 ffb_792Clda#$ff; $FF = no sprite to display ; x-ref: $7923
$792E85 35stazp_mux_y_fire
$793060rts
$7931ad 28 78b_7931ldahit_strength; Get hit type (0=punch, 1/2=dynamite) ; x-ref: $792A
$7934ae 01 7bldxhero_scroll_dir; Which direction is hero facing?
$7937e0 01cpx#$01; 1 = facing right, skip offset
$7939f0 03beqb_793E
$793B18clc
$793C69 03adc#$03; Add 3 for left-facing offset group
$793E0ab_793Easla; ×2 for word-sized table entries ; x-ref: $7939
$793Faatax
$7940a5 46ldazp_spr_x_lo; Hero sprite X low byte
$794218clc
$79437d 2b 78adcfire_offset_x_lo,x; Add X offset from table
$794685 48stazp_mux_x_lo_fire; Store fire sprite X low
$7948a5 59ldazp_spr_x_hi; Hero sprite X high byte
$794A7d 2c 78adcfire_offset_x_hi,x; Add X offset high from table
$794D85 5bstazp_mux_x_hi_fire; Store fire sprite X high
$794Fa5 33ldazp_spr_y_pos; Hero sprite Y position
$795138sec
$7952e9 08sbc#$08; Offset sprite 8px up from hero
$795485 35stazp_mux_y_fire; Store fire sprite Y position
$7956ad 29 78ldafire_anim_frame; Current animation frame index
$7959ae 28 78ldxhit_strength; Check hit strength
$795Ce0 02cpx#$02; 2 = direct hit → use alt frames
$795Ed0 03bneb_7963
$796018clc; Add 3 for direct-hit frame set
$796169 03adc#$03
$7963aab_7963tax; Use frame index into sprite table ; x-ref: $795E
$7964bd 37 78ldafire_sprite_frames,x; Look up sprite pointer from table
$796785 6estazp_mux_frame_fire; Store sprite frame pointer
$7969a9 fflda#$ff; $FF = default to flipped (punch)
$796Bae 28 78ldxhit_strength; hit_strength != 0 → dynamite
$796Ef0 02beqb_7972; Dynamite: no flip needed
$7970a9 00lda#$00
$797285 94b_7972stazp_mux_flip_fire; Store horizontal flip flag ; x-ref: $796E
$797460rts
; Water animation frame index (0-7). Advances every 5 ticks, indexes into water_anim_patterns.
$7975water_anim_frame.byte$00; x-ref: $7A19, $7A34, $7A37, $7A40
; Water animation tick divider (0-4). Increments each update, resets at 5 to advance water_anim_frame.
$7976water_anim_tick.byte$00; x-ref: $7A1C, $7A25, $7A28, $7A31
; Water animation pattern table: 8 frames x 20 columns of tile indices ($B0-$B3).
$7977water_anim_patterns.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0; x-ref: $7A52, $7A66
$797F.byte$b0, $b1, $b2, $b3, $b3, $b2, $b1, $b0
$7987.byte$b0, $b1, $b2, $b3, $b0, $b0, $b1, $b2
$798F.byte$b2, $b1, $b0, $b0, $b1, $b2, $b3, $b3
$7997.byte$b3, $b3, $b2, $b1, $b0, $b0, $b1, $b2
$799F.byte$b1, $b0, $b0, $b1, $b1, $b0, $b0, $b1
$79A7.byte$b2, $b3, $b3, $b2, $b2, $b3, $b3, $b2
$79AF.byte$b1, $b0, $b0, $b1, $b2, $b1, $b0, $b0
$79B7.byte$b0, $b0, $b1, $b2, $b3, $b3, $b2, $b1
$79BF.byte$b1, $b2, $b3, $b3, $b2, $b1, $b0, $b0
$79C7.byte$b3, $b2, $b1, $b0, $b0, $b1, $b2, $b3
$79CF.byte$b3, $b2, $b1, $b0, $b0, $b1, $b2, $b3
$79D7.byte$b3, $b2, $b1, $b0, $b3, $b3, $b2, $b1
$79DF.byte$b1, $b2, $b3, $b3, $b2, $b1, $b0, $b0
$79E7.byte$b0, $b0, $b1, $b2, $b3, $b3, $b2, $b1
$79EF.byte$b2, $b3, $b3, $b2, $b2, $b3, $b3, $b2
$79F7.byte$b1, $b0, $b0, $b1, $b1, $b0, $b0, $b1
$79FF.byte$b2, $b3, $b3, $b2, $b1, $b2, $b3, $b3
$7A07.byte$b3, $b3, $b2, $b1, $b0, $b0, $b1, $b2
$7A0F.byte$b2, $b1, $b0, $b0, $b1, $b2, $b3, $b3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the water/wave animation subsystem.
; Clears both the frame counter and the tick divider so the animation
; restarts from frame 0 on the next screen update.
;
; Inputs: None
; Outputs: None
; Side Effects: a_7975 (frame index) and a_7976 (tick divider) set to 0
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
reset_water_animation
$7A17a9 00lda#$00; A = 0 ; x-ref: $4B0A
$7A198d 75 79stawater_anim_frame; reset animation frame index (0-7)
$7A1C8d 76 79stawater_anim_tick; reset tick divider (0-4)
$7A1F60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Animates the water surface tiles on screen. Runs every 5 frames, cycling
; through 8 animation frames. Each frame selects 20 tile patterns from
; water_anim_patterns and writes them to scr_water_row. The first 20 tiles
; are written left-to-right; the second 20 are written with a reversed
; pattern index, creating a mirrored/reflected water effect. Cells marked
; $80 are treated as transparent and skipped.
;
; Inputs: screen_changed_flag (must be non-zero to proceed)
; Outputs: scr_water_row updated with current animation frame tiles
; Side Effects: Modifies water_anim_tick, water_anim_frame
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_water_animation
$7A20ad a6 3fldascreen_changed_flag; skip if no screen update needed ; x-ref: $61D4, $6295
$7A23f0 4dbeqr_7A72
$7A25ee 76 79incwater_anim_tick; advance tick counter
$7A28ad 76 79ldawater_anim_tick
$7A2Bc9 05cmp#$05; only update every 5 frames
$7A2Dd0 43bner_7A72
$7A2Fa9 00lda#$00; reset tick to 0
$7A318d 76 79stawater_anim_tick
$7A34ee 75 79incwater_anim_frame; advance to next anim frame
$7A37ad 75 79ldawater_anim_frame
$7A3Ac9 08cmp#$08; wrap frame at 8 (0-7)
$7A3Cd0 05bneb_7A43
$7A3Ea9 00lda#$00
$7A408d 75 79stawater_anim_frame
$7A4320 a5 21b_7A43jsrmultiply_by_5; frame * 5 * 4 = offset into patterns ; x-ref: $7A3C
$7A460aasla
$7A470aasla
$7A48aatax
$7A49a0 00ldy#$00; Y = tile index in water row
$7A4Bb9 80 06b_7A4BldaSCREEN_RAM_R16C0,y; x-ref: $7A5C
$7A4Ec9 80cmp#$80; $80 = masked/transparent tile, skip
$7A50f0 06beqb_7A58
$7A52bd 77 79ldawater_anim_patterns,x; copy pattern to screen row
$7A5599 80 06staSCREEN_RAM_R16C0,y
$7A58e8b_7A58inx; x-ref: $7A50
$7A59c8iny
$7A5Ac0 14cpy#$14; 20 tiles per half-row (left-to-right)
$7A5Cd0 edbneb_7A4B
$7A5Ecadex; back up X by 1 for mirror effect
$7A5Fb9 80 06b_7A5FldaSCREEN_RAM_R16C0,y; x-ref: $7A70
$7A62c9 80cmp#$80; $80 = masked/transparent, skip
$7A64f0 06beqb_7A6C
$7A66bd 77 79ldawater_anim_patterns,x; copy mirrored pattern to screen
$7A6999 80 06staSCREEN_RAM_R16C0,y
$7A6Ccab_7A6Cdex; reverse X for mirror effect ; x-ref: $7A64
$7A6Dc8iny
$7A6Ec0 28cpy#$28; 40 tiles total (second half right-to-left)
$7A70d0 edbneb_7A5F
$7A7260r_7A72rts; x-ref: $7A23, $7A2D
$7A73tile14_active_flag.byte$00; x-ref: $4AF8, $7A7A, $7A84, $7ACF, $7C32, ...
$7A74tile14_screen_y_lo.byte$00; x-ref: $7A9B, $7AB0, $7AD9, $7FD5
$7A75tile14_screen_y_hi.byte$00; x-ref: $7AA2, $7AB7, $7AE1, $7FDC
$7A76tile14_screen_x.byte$00; x-ref: $7AC7, $7AE8, $7FC6
$7A77tile14_render_param.byte$00; x-ref: $7AA7, $7ABC, $7AF0, $7FFA
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables tile-14 (special interactive tile) by clearing its active flag.
; Called during room reset to ensure no stale tile-14 state persists.
;
; Inputs: None
; Outputs: tile14_active_flag = $00
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7A78a9 00reset_tile14_statelda#$00; disable tile-14 ; x-ref: $4B0D
$7A7A8d 73 7astatile14_active_flag
$7A7D60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes tile-14 screen parameters from its tile-map coordinates.
; Converts tile column (X) and row (Y) to pixel screen positions for
; collision detection. Stores a render parameter ($BC or $BE) based on
; whether the tile falls in the upper or lower screen region.
;
; Inputs: X = tile column, Y = tile row
; Outputs: tile14_active_flag = $FF, tile14_screen_y_lo/hi, tile14_screen_x,
; tile14_render_param. X and Y preserved via zpa_1E/zpa_1F.
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7A7E86 1einit_tile14_paramsstxzp_temp_x; save tile column ; x-ref: $4FDA
$7A8084 1fstyzp_temp_y; save tile row
$7A82a9 fflda#$ff; mark tile-14 as active
$7A848d 73 7astatile14_active_flag
$7A87a9 00lda#$00; clear high byte for Y*8
$7A8985 1dstazp_ptr_map_hi
$7A8Ba5 1fldazp_temp_y; Y = tile_row * 8 (pixel Y)
$7A8D0aasla
$7A8E0aasla
$7A8F0aasla
$7A9026 1drolzp_ptr_map_hi; rotate carry into high byte
$7A92d0 19bneb_7AAD; if hi != 0 or Y >= 160: lower region
$7A94c9 a0cmp#$a0
$7A96b0 15bcsb_7AAD
$7A9818clc; upper region: offset by +10
$7A9969 0aadc#$0a
$7A9B8d 74 7astatile14_screen_y_lo
$7A9Ea5 1dldazp_ptr_map_hi
$7AA069 00adc#$00
$7AA28d 75 7astatile14_screen_y_hi
$7AA5a9 bclda#$bc; render param = $BC (upper)
$7AA78d 77 7astatile14_render_param
$7AAA4c bf 7ajmpj_7ABF; jump to X-coordinate calc
$7AAD38b_7AADsec; lower region: offset by -2 ; x-ref: $7A92, $7A96
$7AAEe9 02sbc#$02
$7AB08d 74 7astatile14_screen_y_lo
$7AB3a5 1dldazp_ptr_map_hi
$7AB5e9 00sbc#$00
$7AB78d 75 7astatile14_screen_y_hi
$7ABAa9 belda#$be; render param = $BE (lower)
$7ABC8d 77 7astatile14_render_param
$7ABFa5 1ej_7ABFldazp_temp_x; X = tile_col * 8 (pixel X) ; x-ref: $7AAA
$7AC10aasla
$7AC20aasla
$7AC30aasla
$7AC438sec; adjust X by -5 for centering
$7AC5e9 05sbc#$05
$7AC78d 76 7astatile14_screen_x
$7ACAa6 1eldxzp_temp_x; restore tile column to X
$7ACCa4 1fldyzp_temp_y; restore tile row to Y
$7ACE60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up sprite rendering parameters for a tile-14 hazard/obstacle entity.
; Checks if the tile is active and rendering is free, then copies the tile's
; screen position (with pixel offsets) and render parameters into the sprite
; multiplexer's ZP slot for the next frame.
;
; Inputs: None (reads tile14_* state variables)
; Outputs: None
; Side Effects: Populates sprite multiplexer ZP slot (Y, X, frame, color)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7ACFad 73 7asetup_tile14_spriteldatile14_active_flag; tile14 active? ; x-ref: $6483
$7AD2f0 25beqr_7AF9; skip if tile14 inactive
$7AD4ad a7 3fldarendering_in_progress; renderer busy?
$7AD7d0 20bner_7AF9; skip if render in progress
$7AD9ad 74 7aldatile14_screen_y_lo; tile14 screen Y lo
$7ADC18clc
$7ADD69 0cadc#$0c; add 12px vertical offset
$7ADF85 58stazp_mux_x_lo_hazard; -> sprite Y pos lo
$7AE1ad 75 7aldatile14_screen_y_hi; tile14 screen Y hi
$7AE469 00adc#$00; propagate carry
$7AE685 6bstazp_mux_x_hi_hazard; -> sprite Y pos hi
$7AE8ad 76 7aldatile14_screen_x; tile14 screen X
$7AEB18clc
$7AEC69 25adc#$25; add 37px horizontal offset
$7AEE85 45stazp_screen_dirty; -> sprite X pos
$7AF0ad 77 7aldatile14_render_param; tile14 sprite frame
$7AF385 7estazp_mux_frame_hazard; -> sprite frame ptr
$7AF5a9 08lda#VicIIColors.ORANGE; color = 8 (orange)
$7AF785 91stazp_spr_color_extra; -> sprite color
$7AF960r_7AF9rts; x-ref: $7AD2, $7AD7
; Hero state: 0=normal, 1=falling, 2=landed/hover, 3=hit, 4=dying, 5=dead
$7AFAhero_state.byte$00; x-ref: $1E76, $1E80, $1E91, $61FA, $6FDF, ...
; Hero X position, low byte (16-bit horizontal map coordinate)
$7AFBhero_x_lo.byte$00; x-ref: $1EB3, $1EF4, $1F3F, $1F79, $1FE7, ...
; Hero X position, high byte (16-bit horizontal map coordinate)
$7AFChero_x_hi.byte$00; x-ref: $1EBB, $1EFC, $1F47, $1F81, $1FEF, ...
; Hero Y screen position (vertical pixel row on screen)
$7AFDhero_y_pos.byte$00; x-ref: $1EA4, $1EE5, $1F30, $1F6A, $1FD8, ...
; Hero X target position (snapped to 8px grid for movement)
$7AFEhero_x_target.byte$00; x-ref: $7B30, $7CA6, $7CC7, $7CE4, $7DFA, ...
; Hero Y floor limit (lowest Y before death, set on death to snap pos)
$7AFFhero_y_floor_limit.byte$00; x-ref: $7B3C, $8101, $8192
; Hero facing direction: 0=none/left-facing, 1=left, 2=right
$7B00hero_direction.byte$00; x-ref: $7CAD, $7CB9, $7CD6, $7CE7, $7FA0, ...
; Hero scroll step size / movement direction (1=slow, 2=fast)
$7B01hero_scroll_dir.byte$00; x-ref: $1F03, $76D8, $7737, $785C, $7890, ...
; Hero Y velocity (signed: negative=up, positive=down, 0=hover)
$7B02hero_y_velocity.byte$00; x-ref: $7E68, $7E89, $7EBA, $7F54
; Hero airborne flag: $FF=vertical collision active, $00=grounded
$7B03hero_airborne_flag.byte$00; x-ref: $7B74, $7C23, $7C4E, $7ED6, $7F5B, ...
; Hero vertical acceleration counter (0-$2E, increases while UP held)
$7B04hero_accel_counter.byte$00; x-ref: $7B79, $7C28, $7C53, $7C6B, $7E2C, ...
; Hero entity collision flag: $FF=colliding with platform entity, $00=free
hero_entity_collision
$7B05.byte$00; x-ref: $7B86, $7C2D, $7C84, $7FA8, $7FAC, ...
; Hero dynamite count (decremented on use, displayed on HUD)
$7B06hero_dynamite_count.byte$00; x-ref: $6131, $6254, $626B, $6349, $635F, ...
; Hero VIC-II sprite pointer index (computed from state/direction/frame)
$7B07hero_sprite_ptr.byte$00; x-ref: $814B, $815C, $816D, $8181, $81FB
; Hero walk animation frame index (0-4, advances every 4 subframes)
$7B08hero_walk_frame.byte$00; x-ref: $7B7E, $7D56, $80C3, $80C6, $80CF, ...
; Hero walk animation sub-frame counter (3→0, ticks per frame)
$7B09hero_walk_subframe.byte$00; x-ref: $7B89, $7D5B, $80B9, $80C0
; Hero hover animation frame index (0-3, advances when tick overflows)
$7B0Ahero_hover_frame.byte$00; x-ref: $7B8C, $80E3, $80E6, $80EF, $8200
; Hero hover animation tick counter (0→delay, resets on overflow)
$7B0Bhero_hover_tick.byte$00; x-ref: $7B8F, $80D3, $80D6, $80E0
; Hero animation frame delay threshold ($0E=fast, slower when damaged)
$7B0Chero_anim_delay.byte$00; x-ref: $7B97, $7C5A, $7C74, $80D9
; Hero death sub-step counter (0-4 cycle, increments Y on overflow; $FF=frozen)
$7B0Dhero_death_substep.byte$00; x-ref: $7B81, $7BCB, $7BD0, $7BD3, $7BDC, ...
; Hero death timer (decremented each frame, state→5 when zero; init=$50)
$7B0Ehero_death_timer.byte$00; x-ref: $7BE2, $8197
; Fire button double-tap state machine (0=idle, 1=first tap, 2=confirmed)
$7B0Fhero_fire_tap_state.byte$00; x-ref: $7B92, $8049, $8055, $8066, $806B, ...
; Fire button double-tap timeout counter (init=$19, decrements to 0)
$7B10hero_fire_tap_timer.byte$00; x-ref: $804E, $8070
; Hero invulnerability flag (non-zero=skip all collision checks)
$7B11hero_invulnerable.byte$00; x-ref: $1E70, $7B54, $81F6, $826C
; Sprite pointer lookup table for hero walking animation frames
$7B12hero_sprite_table.byte$8b, $8c, $8d, $8e, $8f; x-ref: $8174
; Hero hover animation sprite frames (4 entries: $99, $9A, $9B, $9A - oscillating sequence)
$7B17hero_hover_frames.byte$99, $9a, $9b, $9a; x-ref: $8203
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the hero entity from a tile-16 spawn point on the level map.
; Converts the tile grid position (X=column, Y=row) into pixel coordinates,
; sets the movement step size based on vertical position, clears state flags,
; and calls the shared entity state initializer.
;
; Inputs: X = tile column, Y = tile row
; Outputs: X = tile column (preserved), Y = tile row (preserved)
; Side Effects: Populates the hero entity state block ($7AFA-$7B11)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7B1B86 1einit_hero_entitystxzp_temp_x; save tile column ; x-ref: $4FED
$7B1D84 1fstyzp_temp_y; save tile row
$7B1Fa9 00lda#$00; clear high byte for Y*8 calculation
$7B2185 1dstazp_ptr_map_hi
$7B23a5 1fldazp_temp_y; Y = tile row
$7B250aasla; row * 2
$7B260aasla; row * 4
$7B270aasla; row * 8
$7B2826 1drolzp_ptr_map_hi; carry into high byte
$7B2A18clc
$7B2B69 08adc#$08; pixel_y = row * 8 + 8
$7B2D8d fb 7astahero_x_lo; store as hero Y position low
$7B308d fe 7astahero_x_target; also set as Y target position
$7B33a5 1dldazp_ptr_map_hi; propagate carry to high byte
$7B3569 00adc#$00
$7B378d fc 7astahero_x_hi; store Y position high byte
$7B3Aa9 15lda#$15; hero X pixel position = 21
$7B3C8d ff 7astahero_y_floor_limit
$7B3Fa9 01lda#$01; default step = 1 (slow)
$7B41ae fc 7aldxhero_x_hi; check if Y high byte != 0
$7B44d0 09bneb_7B4F; if high byte set, stay slow
$7B46ae fb 7aldxhero_x_lo; check if Y low < 160
$7B49e0 a0cpx#$a0
$7B4Bb0 02bcsb_7B4F; near top of screen: step = 2 (fast)
$7B4Da9 02lda#$02
$7B4F8d 01 7bb_7B4Fstahero_scroll_dir; set hero movement step size ; x-ref: $7B44, $7B4B
$7B52a9 00lda#$00; clear hero state counter
$7B548d 11 7bstahero_invulnerable
$7B5720 68 7bjsrinit_hero_state; init remaining hero entity fields
$7B5Aa6 1eldxzp_temp_x; restore tile column
$7B5Ca4 1fldyzp_temp_y; restore tile row
$7B5E60rts
$7B5F20 13 80j_7B5Fjsrsnap_hero_x_to_tile; x-ref: $617F
$7B6220 58 77jsrprocess_hit_entities
$7B6520 b6 85jsrdeactivate_raft
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes hero entity fields to default values for a new life or respawn.
; Sets the hero to state 1 (active/falling), positions Y near top of screen,
; marks as airborne, and clears all animation, collision, and input counters.
;
; Inputs: None
; Outputs: hero_state, hero_y_pos, hero_airborne_flag, hero_accel_counter,
; hero_walk_frame, hero_death_substep, hero_entity_collision,
; hero_walk_subframe, hero_hover_frame, hero_hover_tick,
; hero_fire_tap_state, hero_anim_delay
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7B68a9 01init_hero_statelda#$01; state = 1 (active/falling) ; x-ref: $7B57
$7B6A8d fa 7astahero_state
$7B6Da9 f0lda#$f0; Y = $F0 (240), near top of screen
$7B6F8d fd 7astahero_y_pos
$7B72a9 fflda#$ff; mark hero as airborne
$7B748d 03 7bstahero_airborne_flag
$7B77a9 23lda#$23; acceleration counter = $23 (35)
$7B798d 04 7bstahero_accel_counter
$7B7Ca9 fflda#$ff; $FF = reset walk animation frame
$7B7E8d 08 7bstahero_walk_frame
$7B818d 0d 7bstahero_death_substep; $FF = reset death substep
$7B84a9 00lda#$00; clear collision, walk subframe, hover, fire-tap
$7B868d 05 7bstahero_entity_collision
$7B898d 09 7bstahero_walk_subframe
$7B8C8d 0a 7bstahero_hover_frame
$7B8F8d 0b 7bstahero_hover_tick
$7B928d 0f 7bstahero_fire_tap_state
$7B95a9 06lda#$06; animation delay = 6 frames
$7B978d 0c 7bstahero_anim_delay
$7B9A60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Hero state-machine dispatcher. Reads hero_state and branches to the
; appropriate handler:
; State 0: Normal movement - horizontal scroll, vertical update,
; tile collision check, then fire/animation processing.
; State 1: Falling - delegates to handle_falling_movement.
; State 2: Hover/landing - ticks animation, returns to state 0
; when joystick is released (no direction pressed).
; State 3: Animation-only - tail-calls the animation tick routine.
; State 4: Death sequence - increments death substep, nudges hero
; sprite downward every 5 frames, decrements death timer,
; transitions to state 5 when timer reaches zero.
; Other: Returns immediately (no-op).
;
; Called once per frame from the main game loop.
;
; Inputs: hero_state, joystick_state, hero_death_substep, hero_death_timer
; Outputs: hero_state (may transition), hero_y_pos (death nudge),
; hero_death_substep, hero_death_timer
; Side Effects: Delegates to movement, collision, fire, and animation routines
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_hero_state_machine
$7B9Bad fa 7aldahero_state; dispatch on hero_state ; x-ref: $61D7
$7B9Ed0 03bnestate1_falling; state == 0? -> normal movement
$7BA04c ed 7bjmpstate0_normal_movement; state 0: jump to normal movement path
$7BA3c9 01state1_fallingcmp#$01; state == 1? ; x-ref: $7B9E
$7BA5d0 03bnestate2_hover_landing; not state 1 -> check next
$7BA74c f3 80jmphandle_falling_movement; state 1: tail-call falling handler
$7BAAc9 02state2_hover_landingcmp#$02; state == 2? ; x-ref: $7BA5
$7BACd0 12bnestate3_anim_only; not state 2 -> check next
$7BAE20 d3 80jsradvance_hero_anim_frame; state 2: tick hover animation
$7BB1ad 80 3eldajoystick_state; read joystick direction bits
$7BB429 0fand#$0f; mask lower nibble (directions)
$7BB6c9 0fcmp#$0f; $0F = no direction pressed?
$7BB8f0 05beqr_7BBF; all released -> stay in hover, return
$7BBAa9 00lda#$00; direction pressed -> back to state 0
$7BBC8d fa 7astahero_state; hero_state = 0 (normal)
$7BBF60r_7BBFrts; x-ref: $7BB8
$7BC0c9 03state3_anim_onlycmp#$03; state == 3? ; x-ref: $7BAC
$7BC2d0 03bnestate4_death_sequence; not state 3 -> check next
$7BC44c d3 80jmpadvance_hero_anim_frame; state 3: tail-call animation tick
state4_death_sequence
$7BC7c9 04cmp#$04; state == 4? ; x-ref: $7BC2
$7BC9d0 21bner_7BEC; not state 4 -> return (unknown state)
$7BCBad 0d 7bldahero_death_substep; negative substep? -> skip nudge
$7BCE30 12bmistate_unknown_return
$7BD0ee 0d 7binchero_death_substep
$7BD3ad 0d 7bldahero_death_substep; substep == 5? (time to nudge)
$7BD6c9 05cmp#$05
$7BD8d0 08bnestate_unknown_return
$7BDAa9 00lda#$00; reset substep to 0
$7BDC8d 0d 7bstahero_death_substep
$7BDFee fd 7ainchero_y_pos; nudge hero sprite down 1 pixel
$7BE2ce 0e 7bstate_unknown_returndechero_death_timer; count down death timer ; x-ref: $7BCE, $7BD8
$7BE5d0 05bner_7BEC; timer not zero -> keep dying
$7BE7a9 05lda#$05; timer expired -> transition to state 5
$7BE98d fa 7astahero_state; hero_state = 5 (death complete)
$7BEC60r_7BECrts; x-ref: $7BC9, $7BE5
state0_normal_movement
$7BED20 84 7cjsrhandle_horizontal_scroll; state 0: process horizontal scrolling ; x-ref: $7BA0
$7BF0ad fa 7aldahero_state; re-check: death started mid-routine?
$7BF3c9 04cmp#$04; state == 4 -> abort, hero just died
$7BF5d0 01bneb_7BF8
$7BF760rts
$7BF820 25 7eb_7BF8jsrupdate_hero_vertical; process vertical movement/gravity ; x-ref: $7BF5
$7BFBad fa 7aldahero_state; re-check again after vertical update
$7BFEc9 04cmp#$04; state == 4 -> abort, hero just died
$7C00d0 01bneb_7C03
$7C0260rts
$7C0320 bd 7fb_7C03jsrcheck_tile14_collision; check tile14 collision, then fall through to fire/animation ; x-ref: $7C00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Processes the hero's fire/dynamite action and updates animation speed.
;
; First checks if the hero is dead (a_7AFA == 3). If so, returns immediately.
; Reads the joystick fire button: if pressed, calls s_78FA to process the fire
; action; if not pressed, calls s_7912 to reset pending fire state.
; Then calls s_802A to detect a valid fire-button press/release event.
; If fire is confirmed, checks multiple guard conditions (a_7B03, a_7B04,
; a_7B05, a_7A73 must all be zero, and explosion_state must be 0 or 3)
; before allowing dynamite usage.
; If all guards pass and dynamite count (a_7B06) > 0, clears the dynamite
; sprite from the HUD, decrements the count, and starts the hero explosion.
; Finally, computes the hero animation frame delay: $0E (fast) when undamaged,
; or a slower speed proportional to damage, and advances the animation counter.
;
; Inputs: a_7AFA (game state), joystick_state (fire button),
; a_7B03-a_7B05 (action flags), explosion_state, a_7B06 (dynamite count)
; Outputs: a_7B0C (animation frame delay), a_8AF0 (animation active flag)
; Side Effects: May trigger hero explosion, clears HUD dynamite sprite,
; advances animation frame counter via s_80D3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
process_fire_and_animation
$7C06ad fa 7aldahero_state; load game state
$7C09c9 03cmp#$03; state == 3 (dead)?
$7C0Bd0 01bneb_7C0E; not dead → continue
$7C0D60rts; dead → return early
$7C0Ead 80 3eb_7C0Eldajoystick_state; read joystick ; x-ref: $7C0B
$7C1129 10and#$10; fire button (bit 4)?
$7C13d0 06bneb_7C1B; fire NOT pressed → reset fire state
$7C1520 fa 78jsrhandle_fire_pressed; fire IS pressed → process fire action
$7C184c 1e 7cjmpj_7C1E; skip fire state reset
$7C1B20 12 79b_7C1Bjsrdeactivate_fire; reset pending fire state ; x-ref: $7C13
$7C1E20 2a 80j_7C1Ejsrcheck_hero_fire_input; check fire button press/release pattern ; x-ref: $7C18
$7C21f0 2bbeqb_7C4E; no valid fire event → skip to animation
$7C23ad 03 7bldahero_airborne_flag; guard: action flag 1 active?
$7C26d0 26bneb_7C4E; busy → skip dynamite
$7C28ad 04 7bldahero_accel_counter; guard: action flag 2 active?
$7C2Bd0 21bneb_7C4E; busy → skip dynamite
$7C2Dad 05 7bldahero_entity_collision; guard: action flag 3 active?
$7C30d0 1cbneb_7C4E; busy → skip dynamite
$7C32ad 73 7aldatile14_active_flag; guard: another action in progress?
$7C35d0 17bneb_7C4E; busy → skip dynamite
$7C37ad 01 6fldaexplosion_state; check explosion state
$7C3Af0 04beqb_7C40; no explosion → allow dynamite
$7C3Cc9 03cmp#$03; explosion finished (state 3)?
$7C3Ed0 0ebneb_7C4E; explosion still active → skip dynamite
$7C40ad 06 7bb_7C40ldahero_dynamite_count; any dynamite remaining? ; x-ref: $7C3A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles hero taking damage: if lives remain, clears hero tiles, decrements
; the lives counter, and triggers the explosion animation. Then computes
; the animation frame delay based on remaining damage timer, and manages
; the death sound effect state. Falls through from collision guard checks.
;
; Inputs: A (zero flag from a_7B06 lives check), a_7B03/a_7B04 (damage timers)
; Outputs: a_7B06 (decremented), a_7B0C (animation delay), a_8AF0 (sound state)
; Side Effects: Clears hero screen tiles, starts explosion animation, triggers death SFX
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7C43f0 09handle_hero_damagebeqb_7C4E; no dynamite → skip to animation
$7C4520 5f 63jsrclear_last_dynamite_icon; clear dynamite sprite from HUD
$7C48ce 06 7bdechero_dynamite_count; decrement dynamite count
$7C4B20 3e 70jsrinit_hero_explosion; start hero dynamite explosion
$7C4Ead 03 7bb_7C4Eldahero_airborne_flag; check damage flag 1 ; x-ref: $7C21, $7C26, $7C2B, $7C30, $7C35, ...
$7C51d0 15bneb_7C68; damaged → compute slower speed
$7C53ad 04 7bldahero_accel_counter; check damage flag 2
$7C56d0 10bneb_7C68; damaged → compute slower speed
$7C58a9 0elda#$0e; undamaged: fast frame delay ($0E)
$7C5A8d 0c 7bstahero_anim_delay; store animation frame delay
$7C5Dad f0 8aldasfx6_enable; check if animation active
$7C60f0 1fbeqb_7C81; not active yet → skip to frame advance
$7C6220 61 8bjsrcancel_sfx6; start animation playback
$7C654c 81 7cjmpb_7C81; skip to frame advance
$7C68a9 2eb_7C68lda#$2e; base delay = $2E (46) ; x-ref: $7C51, $7C56
$7C6A38sec; set carry for subtraction
$7C6Bed 04 7bsbchero_accel_counter; subtract damage amount
$7C6E20 c2 21jsrdivide_by_5; divide remaining ticks by 5
$7C7118clc; clear carry for addition
$7C7269 01adc#$01; add 1 → minimum delay of 1
$7C748d 0c 7bstahero_anim_delay; store slower frame delay
$7C77ad f0 8aldasfx6_enable; check if animation active
$7C7Ad0 05bneb_7C81; already active → skip to frame advance
$7C7Ca9 01lda#$01; mark animation as active
$7C7E8d f0 8astasfx6_enable; store animation active flag
$7C814c d3 80b_7C81jmpadvance_hero_anim_frame; tail-call: advance animation frame counter ; x-ref: $7C60, $7C65, $7C7A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Handles horizontal scrolling based on joystick left/right input.
; If vertical movement is active (a_7B05 != 0), only updates the scroll
; direction flag without actually scrolling. Otherwise, reads joystick,
; sets a target position snapped to an 8-pixel grid, moves 2 pixels per
; frame toward the target, checks map tiles for collisions, and handles
; wrap-around at screen edges.
;
; Inputs: joystick_state (bit 2 = left, bit 3 = right)
; Outputs: a_7AFB/a_7AFC (16-bit horizontal position),
; a_7AFE (target position), a_7B00 (direction),
; a_7B01 (scroll direction)
; Side Effects: Calls s_80B9 (animation tick), s_7D5F (tile collision
; check), j_7DE1/j_7E03 (edge wrap-around)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_horizontal_scroll
$7C84ad 05 7bldahero_entity_collision; check if vertical movement active ; x-ref: $7BED
$7C87f0 1abeqb_7CA3; if not, go to main horizontal logic
$7C89ad 80 3eldajoystick_state; vertical active: just read joystick direction
$7C8C29 04and#$04; joystick left?
$7C8Ed0 06bneb_7C96
$7C90a9 01lda#$01; set scroll direction = left
$7C928d 01 7bstahero_scroll_dir
$7C9560rts
$7C96ad 80 3eb_7C96ldajoystick_state; joystick right? ; x-ref: $7C8E
$7C9929 08and#$08
$7C9Bd0 05bner_7CA2
$7C9Da9 02lda#$02; set scroll direction = right
$7C9F8d 01 7bstahero_scroll_dir
$7CA260r_7CA2rts; x-ref: $7C9B
$7CA3ad fb 7ab_7CA3ldahero_x_lo; current pos == target pos? ; x-ref: $7C87
$7CA6cd fe 7acmphero_x_target
$7CA9d0 05bneb_7CB0; clear movement direction
$7CABa9 00lda#$00
$7CAD8d 00 7bstahero_direction
$7CB0ad 80 3eb_7CB0ldajoystick_state; joystick left? ; x-ref: $7CA9
$7CB329 04and#$04
$7CB5d0 16bneb_7CCD; direction = left
$7CB7a9 01lda#$01
$7CB98d 00 7bstahero_direction; scroll direction = left
$7CBC8d 01 7bstahero_scroll_dir
$7CBFad fb 7aldahero_x_lo; target = (pos - 1) AND $F8 (snap to 8px grid)
$7CC238sec
$7CC3e9 01sbc#$01
$7CC529 f8and#$f8
$7CC78d fe 7astahero_x_target
$7CCA4c e7 7cjmpj_7CE7
$7CCDad 80 3eb_7CCDldajoystick_state; joystick right? ; x-ref: $7CB5
$7CD029 08and#$08
$7CD2d0 13bnej_7CE7
$7CD4a9 02lda#$02; direction = right
$7CD68d 00 7bstahero_direction
$7CD98d 01 7bstahero_scroll_dir; scroll direction = right
$7CDCad fb 7aldahero_x_lo; target = (pos + 8) AND $F8 (snap to 8px grid)
$7CDF18clc
$7CE069 08adc#$08
$7CE229 f8and#$f8
$7CE48d fe 7astahero_x_target
$7CE7ad 00 7bj_7CE7ldahero_direction; check movement direction ; x-ref: $7CCA, $7CD2
$7CEAc9 01cmp#$01; moving left?
$7CECd0 31bnemove_hero_right
$7CEEad fb 7aldahero_x_lo; subtract 2 pixels from position (16-bit)
$7CF138sec
$7CF2e9 02sbc#$02
$7CF48d fb 7astahero_x_lo
$7CF7ad fc 7aldahero_x_hi
$7CFAe9 00sbc#$00
$7CFC8d fc 7astahero_x_hi
$7CFF20 b9 80jsradvance_walk_anim; tick animation frame counter
$7D02ad fb 7aldahero_x_lo; set tile check addr = pos - 8
$7D0538sec
$7D06e9 08sbc#$08
$7D0885 06stazp_ptr_aux1_lo
$7D0Aad fc 7aldahero_x_hi
$7D0De9 00sbc#$00
$7D0F85 07stazp_ptr_aux1_hi
$7D1120 5f 7djsrcheck_horiz_tile_collision; check map tiles for collision
$7D14ad fa 7aldahero_state; state == dead?
$7D17c9 04cmp#$04
$7D19f0 03beqr_7D1E
$7D1B4c e1 7djmpcheck_scroll_down; handle left-edge wrap-around
$7D1E60r_7D1Erts; x-ref: $7D19
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Moves hero 2 pixels to the right, advances the walk animation frame,
; then checks 4 map tiles at the right edge (pos+7) for wall or obstacle
; collision. If dying (a_7AFA == 4), returns. Otherwise, checks if the
; screen needs to scroll right.
;
; Inputs: a_7AFB/a_7AFC (hero X position), a_7AFD (hero Y row),
; a_7AFA (hero state)
; Outputs: a_7AFB/a_7AFC (updated X position), a_7B08/a_7B09 (anim frame)
; Side Effects: May trigger wall snap (s_8013), obstacle death (j_8185),
; or screen scroll (j_4FF7)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7D1Fc9 02move_hero_rightcmp#$02; direction != 1 (not left)? ; x-ref: $7CEC
$7D21d0 31bnereset_walk_anim; no → reset walk anim & return
$7D23ad fb 7aldahero_x_lo; hero_x_lo += 2 (move right)
$7D2618clc
$7D2769 02adc#$02
$7D298d fb 7astahero_x_lo; hero_x_hi += carry
$7D2Cad fc 7aldahero_x_hi
$7D2F69 00adc#$00
$7D318d fc 7astahero_x_hi
$7D3420 b9 80jsradvance_walk_anim; advance walk animation frame
$7D37ad fb 7aldahero_x_lo; right edge = hero_x_lo + 7
$7D3A18clc
$7D3B69 07adc#$07
$7D3D85 06stazp_ptr_aux1_lo
$7D3Fad fc 7aldahero_x_hi; right edge = hero_x_hi + carry
$7D4269 00adc#$00
$7D4485 07stazp_ptr_aux1_hi
$7D4620 5f 7djsrcheck_horiz_tile_collision; check 4 tiles at right edge column
$7D49ad fa 7aldahero_state; hero state
$7D4Cc9 04cmp#$04; state == 4? (dying)
$7D4Ef0 03beqr_7D53; yes → done, already dying
$7D504c 03 7ejmpcheck_scroll_up; check if screen should scroll right
$7D5360r_7D53rts; x-ref: $7D4E
$7D54a9 ffreset_walk_animlda#$ff; no movement: reset animation frame ; x-ref: $7D21
$7D568d 08 7bstahero_walk_frame
$7D59a9 00lda#$00; reset animation sub-counter
$7D5B8d 09 7bstahero_walk_subframe
$7D5E60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks 4 map tiles in a vertical column at the right edge of the hero.
; Samples tiles at vertical offsets: row-13, row-5, row+3, row+10 relative
; to hero Y position. If any tile >= $1A, snaps position to wall. If any
; tile >= $0D (but < $1A), triggers obstacle death.
;
; Inputs: zpa_06/zpa_07 (column X addr), a_7AFD (hero Y row)
; Outputs: zpa_02-zpa_05 (4 tile values)
; Side Effects: May jump to snap_pos_to_wall or handle_obstacle_hit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_horiz_tile_collision
$7D5Fa5 06ldazp_ptr_aux1_lo; copy column addr to ZP pointer ; x-ref: $7D11, $7D46
$7D6185 fdstazp_ptr_dst_lo
$7D63a5 07ldazp_ptr_aux1_hi; Copy Y-pos hi to ZP pointer
$7D6585 festazp_ptr_dst_hi
$7D67ad fd 7aldahero_y_pos; hero Y row
$7D6A38sec
$7D6Be9 0dsbc#$0d; row - 13 = top of hero sprite
$7D6Daatax
$7D6E20 ca 50jsrget_map_tile; get tile at top-right
$7D7185 02stazp_work0; tile_top → zpa_02
$7D73a5 06ldazp_ptr_aux1_lo; Restore Y-pos for next lookup
$7D7585 fdstazp_ptr_dst_lo
$7D77a5 07ldazp_ptr_aux1_hi
$7D7985 festazp_ptr_dst_hi
$7D7B8atxa; X-offset += 8 (next row down)
$7D7C18clc
$7D7D69 08adc#$08; Move down 8 rows
$7D7Faatax
$7D8020 ca 50jsrget_map_tile; get tile at upper-mid-right (+8 rows)
$7D8385 03stazp_work1; tile_upper_mid → zpa_03
$7D85a5 06ldazp_ptr_aux1_lo; Restore Y-pos for next lookup
$7D8785 fdstazp_ptr_dst_lo
$7D89a5 07ldazp_ptr_aux1_hi
$7D8B85 festazp_ptr_dst_hi
$7D8D8atxa; X-offset += 8 (another row down)
$7D8E18clc
$7D8F69 08adc#$08
$7D91aatax
$7D9220 ca 50jsrget_map_tile; get tile at lower-mid-right (+8 more rows)
$7D9585 04stazp_work2; tile_lower_mid → zpa_04
$7D97a5 06ldazp_ptr_aux1_lo; Restore Y-pos for next lookup
$7D9985 fdstazp_ptr_dst_lo
$7D9Ba5 07ldazp_ptr_aux1_hi
$7D9D85 festazp_ptr_dst_hi
$7D9Fad fd 7aldahero_y_pos; hero Y row
$7DA218clc
$7DA369 0aadc#$0a; row + 10 = bottom of hero sprite
$7DA520 ca 50jsrget_map_tile; get tile at bottom-right
$7DA885 05stazp_work3; tile_bottom → zpa_05
$7DAAa5 02ldazp_work0; --- Check all 4 tiles for walls ($1A+) ---
$7DACc9 1acmp#$1a; tile_top >= $1A? → wall
$7DAEb0 2bbcssnap_pos_to_wall; Wall hit -> snap to grid
$7DB0a5 03ldazp_work1; tile_upper_mid >= $1A? → wall
$7DB2c9 1acmp#$1a
$7DB4b0 25bcssnap_pos_to_wall; tile_lower_mid >= $1A? → wall
$7DB6a5 04ldazp_work2; Check tile 3 for wall
$7DB8c9 1acmp#$1a; tile_bottom >= $1A? → wall
$7DBAb0 1fbcssnap_pos_to_wall; Wall hit → snap X to grid
$7DBCa5 05ldazp_work3; Check tile 4 for wall
$7DBEc9 1acmp#$1a
$7DC0b0 19bcssnap_pos_to_wall; Wall hit → snap X to grid
$7DC2a5 02ldazp_work0; --- Check all 4 tiles for obstacles ($0D-$19) ---
$7DC4c9 0dcmp#$0d; tile_top >= $0D? → obstacle
$7DC6b0 16bcstrampoline_hero_death; Hazard hit -> player death
$7DC8a5 03ldazp_work1; tile_upper_mid >= $0D? → obstacle
$7DCAc9 0dcmp#$0d
$7DCCb0 10bcstrampoline_hero_death; tile_lower_mid >= $0D? → obstacle
$7DCEa5 04ldazp_work2; Check tile 3 for hazard
$7DD0c9 0dcmp#$0d; tile_bottom >= $0D? → obstacle
$7DD2b0 0abcstrampoline_hero_death; Hazard hit → trigger damage
$7DD4a5 05ldazp_work3; Check tile 4 for hazard
$7DD6c9 0dcmp#$0d
$7DD8b0 04bcstrampoline_hero_death; Hazard hit → trigger damage
$7DDA60rts; all tiles clear → safe to move
$7DDB4c 13 80snap_pos_to_walljmpsnap_hero_x_to_tile; wall hit: snap X to tile boundary ; x-ref: $7DAE, $7DB4, $7DBA, $7DC0
trampoline_hero_death
$7DDE4c 85 81jmpj_8185; wall collision → kill hero ; x-ref: $7DC6, $7DCC, $7DD2, $7DD8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the hero has reached the bottom screen boundary. If Y position
; is below row 0 (high byte = 0 and low byte < 2), repositions the hero
; to the top of the next screen section (Y = $0138) and triggers a downward
; screen scroll via j_4FF7 with scroll direction = 1.
;
; Inputs: None (reads hero Y position from a_7AFB/a_7AFC)
; Outputs: None
; Side Effects: May reposition hero and trigger screen scroll down
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7DE1ad fc 7acheck_scroll_downldahero_x_hi; hero Y position high byte ; x-ref: $7D1B
$7DE4d0 1cbner_7E02; high byte != 0 → not at bottom boundary
$7DE6ad fb 7aldahero_x_lo; hero Y position low byte
$7DE9c9 02cmp#$02; Y >= 2 → not at bottom boundary
$7DEBb0 15bcsr_7E02
$7DEDa9 38lda#$38; reposition hero Y low = $38 (top of next screen)
$7DEF8d fb 7astahero_x_lo
$7DF2a9 01lda#$01; set Y high byte = 1
$7DF48d fc 7astahero_x_hi
$7DF7ad fb 7aldahero_x_lo; sync Y target position with new Y
$7DFA8d fe 7astahero_x_target
$7DFDa9 01lda#$01; scroll direction = 1 (down)
$7DFF4c f7 4fjmpj_4FF7; trigger screen scroll transition
$7E0260r_7E02rts; x-ref: $7DE4, $7DEB
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the hero has passed the right scroll boundary. If hero_x >= $013F
; (page 1, offset $3F), resets position to $0008 and triggers a left-scroll
; of the map (scroll direction = 2).
;
; Inputs: a_7AFB/a_7AFC (hero X position)
; Outputs: a_7AFB/a_7AFC (possibly reset), a_7AFE (scroll target)
; Side Effects: May call j_4FF7 to scroll the screen right
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7E03ad fc 7acheck_scroll_upldahero_x_hi; hero_x_hi ; x-ref: $7D50
$7E06f0 1cbeqr_7E24; high byte == 0 → not at top boundary
$7E08ad fb 7aldahero_x_lo; hero Y position low byte
$7E0Bc9 3fcmp#$3f; Y < $3F → not at top boundary
$7E0D90 15bccr_7E24
$7E0Fa9 08lda#$08; reposition hero Y low = $08 (bottom of prev screen)
$7E118d fb 7astahero_x_lo
$7E14a9 00lda#$00; clear Y high byte
$7E168d fc 7astahero_x_hi
$7E19ad fb 7aldahero_x_lo; sync Y target position with new Y
$7E1C8d fe 7astahero_x_target
$7E1Fa9 02lda#$02; scroll direction = 2 (up)
$7E214c f7 4fjmpj_4FF7; trigger screen scroll transition
$7E2460r_7E24rts; x-ref: $7E06, $7E0D
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the hero's vertical (Y-axis) position based on joystick input.
; Reads the joystick UP button to build an acceleration counter (a_7B04),
; converts it to a velocity value (a_7B02), and applies it to the screen
; Y-position (a_7AFD). Checks for map tile collisions above and below
; the hero using get_map_tile. Clamps position at screen boundaries and
; triggers death if a solid or hazard tile is hit.
;
; Inputs: joystick_state (bit 0 = UP), a_7B04 (accel counter),
; a_7AFD (Y screen pos), a_7AFB/a_7AFC (Y map pos lo/hi)
; Outputs: a_7AFD (updated Y pos), a_7B02 (Y velocity),
; a_7B04 (updated accel counter)
; Side Effects: May trigger death (j_8185) on wall collision,
; may call j_4FF7 to scroll screen on boundary hit
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$7E25ad 80 3eupdate_hero_verticalldajoystick_state; Read joystick UP button (bit 0) ; x-ref: $7BF8
$7E2829 01and#$01; Isolate UP bit
$7E2Ad0 0dbneb_7E39; Branch if UP not pressed
$7E2Cad 04 7bldahero_accel_counter; Load vertical accel counter
$7E2Fc9 2ecmp#$2e; Max accel = $2E
$7E31f0 0ebeqb_7E41
$7E33ee 04 7binchero_accel_counter; Accelerate upward
$7E364c 41 7ejmpb_7E41
$7E39ad 04 7bb_7E39ldahero_accel_counter; UP not pressed: decelerate ; x-ref: $7E2A
$7E3Cf0 03beqb_7E41
$7E3Ece 04 7bdechero_accel_counter
$7E41ad 04 7bb_7E41ldahero_accel_counter; Convert accel counter to velocity ; x-ref: $7E31, $7E36, $7E3C
$7E44d0 05bneb_7E4B
$7E46a9 02lda#$02; Stopped: velocity = +2 (drift down)
$7E484c 68 7ejmpj_7E68
$7E4Bc9 2eb_7E4Bcmp#$2e; x-ref: $7E44
$7E4D90 05bccb_7E54
$7E4Fa9 felda#$fe; Max accel: velocity = -2 (fast up)
$7E514c 68 7ejmpj_7E68
$7E54c9 26b_7E54cmp#$26; x-ref: $7E4D
$7E5690 05bccb_7E5D
$7E58a9 fflda#$ff; High accel: velocity = -1 (medium up)
$7E5A4c 68 7ejmpj_7E68
$7E5Dc9 08b_7E5Dcmp#$08; x-ref: $7E56
$7E5Fb0 05bcsb_7E66
$7E61a9 01lda#$01; Low accel: velocity = +1 (slow down)
$7E634c 68 7ejmpj_7E68
$7E66a9 00b_7E66lda#$00; Medium accel: velocity = 0 (hover) ; x-ref: $7E5F
$7E688d 02 7bj_7E68stahero_y_velocity; Store Y velocity ; x-ref: $7E48, $7E51, $7E5A, $7E63
$7E6B18clc
$7E6C6d fd 7aadchero_y_pos; Apply velocity to Y screen position
$7E6F8d fd 7astahero_y_pos
$7E72c9 15cmp#$15; Min altitude threshold = $15
$7E74b0 0dbcsb_7E83
$7E76ad 9e 3fldacurrent_room_index; Check fuel/scroll level
$7E79c9 08cmp#$08; If fuel >= 8, skip clamp
$7E7Bb0 06bcsb_7E83
$7E7Da9 15lda#$15; Clamp Y pos to minimum altitude
$7E7F8d fd 7astahero_y_pos
$7E8260rts
$7E8320 62 7fb_7E83jsrcheck_entity_collision; Check platform/entity collision ; x-ref: $7E74, $7E7B
$7E86f0 01beqb_7E89; Z=1 means collision handled, skip movement
$7E8860rts
$7E89ad 02 7bb_7E89ldahero_y_velocity; Check velocity direction ; x-ref: $7E86
$7E8Cf0 19beqb_7EA7; Zero velocity: skip tile check
$7E8E10 0dbplb_7E9D; Negative = moving up
$7E90ad fd 7aldahero_y_pos; Moving up: check tiles 13px above
$7E9338sec
$7E94e9 0dsbc#$0d
$7E96aatax
$7E9720 d4 7ejsrcheck_vertical_collision
$7E9A4c a7 7ejmpb_7EA7
$7E9Dad fd 7ab_7E9Dldahero_y_pos; Moving down: check tiles 10px below ; x-ref: $7E8E
$7EA018clc
$7EA169 0aadc#$0a
$7EA3aatax
$7EA420 d4 7ejsrcheck_vertical_collision
$7EA7ad fa 7ab_7EA7ldahero_state; Check if hero already dead ; x-ref: $7E8C, $7E9A
$7EAAc9 04cmp#$04; State 4 = dead, exit early
$7EACd0 01bneb_7EAF
$7EAE60rts
$7EAFad fd 7ab_7EAFldahero_y_pos; Check Y pos against screen bounds ; x-ref: $7EAC
$7EB2c9 fccmp#$fc; Upper bound = $FC
$7EB4b0 1dbcsr_7ED3
$7EB6c9 8ccmp#$8c; Lower bound = $8C
$7EB890 19bccr_7ED3
$7EBAad 02 7bldahero_y_velocity; At boundary: check direction
$7EBD10 0abplb_7EC9
$7EBFa9 8blda#$8b; Moving up: clamp to top ($8B)
$7EC18d fd 7astahero_y_pos
$7EC4a9 03lda#$03; Scroll screen down (state 3)
$7EC64c f7 4fjmpj_4FF7
$7EC9a9 fcb_7EC9lda#$fc; Moving down: clamp to bottom ($FC) ; x-ref: $7EBD
$7ECB8d fd 7astahero_y_pos
$7ECEa9 04lda#$04; Scroll screen up (state 4)
$7ED04c f7 4fjmpj_4FF7
$7ED360r_7ED3rts; x-ref: $7EB4, $7EB8
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks for vertical collisions by probing three horizontal positions
; (left, right, far-right) at the current vertical offset. Reads map
; tiles at each probe point and branches based on tile type:
; - Tile >= $1A (solid wall): snap Y to grid boundary
; - Tile >= $0D (hazard): trigger player death
; - Otherwise: no collision, return
;
; Inputs: X = vertical pixel offset (adjusted Y position from caller)
; a_7AFB/a_7AFC = player horizontal position (16-bit)
; Outputs: a_7B03 = set to $FF on entry (vertical collision flag)
; a_7AFD = adjusted Y position on wall collision
; a_7B0D = cleared on tile $80 collision
; Side Effects: Triggers death via j_8185 on hazard tiles
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_vertical_collision
$7ED4a9 fflda#$ff; Set vertical collision flag ; x-ref: $7E97, $7EA4
$7ED68d 03 7bstahero_airborne_flag
$7ED9ad fb 7aldahero_x_lo; Player X position low byte
$7EDC38sec; Probe left: pos - 8
$7EDDe9 08sbc#$08
$7EDF85 fdstazp_ptr_dst_lo; Store probe address low byte
$7EE1ad fc 7aldahero_x_hi; Player X position high byte
$7EE4e9 00sbc#$00; Propagate borrow
$7EE685 festazp_ptr_dst_hi; Store probe address high byte
$7EE88atxa; Y offset to A for get_map_tile
$7EE920 ca 50jsrget_map_tile; Get tile ID at left probe
$7EEC85 02stazp_work0; Store left tile ID
$7EEEad fb 7aldahero_x_lo; Probe right: pos + 1
$7EF118clc
$7EF269 01adc#$01
$7EF485 fdstazp_ptr_dst_lo; Store probe address high byte
$7EF6ad fc 7aldahero_x_hi; Get tile ID at right probe
$7EF969 00adc#$00; Store right tile ID
$7EFB85 festazp_ptr_dst_hi
$7EFD8atxa
$7EFE20 ca 50jsrget_map_tile
$7F0185 03stazp_work1
$7F03ad fb 7aldahero_x_lo; Probe far-right: pos + 7
$7F0618clc
$7F0769 07adc#$07
$7F0985 fdstazp_ptr_dst_lo
$7F0Bad fc 7aldahero_x_hi
$7F0E69 00adc#$00
$7F1085 festazp_ptr_dst_hi
$7F128atxa
$7F1320 ca 50jsrget_map_tile; Get tile ID at far-right probe
$7F1685 04stazp_work2; Store far-right tile ID
$7F18a5 02ldazp_work0; Check left tile
$7F1Ac9 1acmp#$1a; Tile >= $1A? (solid wall)
$7F1Cb0 1fbcsb_7F3D; Yes → wall collision
$7F1Ea5 03ldazp_work1; Check right tile
$7F20c9 1acmp#$1a
$7F22b0 19bcsb_7F3D; Yes → wall collision
$7F24a5 04ldazp_work2; Check far-right tile
$7F26c9 1acmp#$1a
$7F28b0 13bcsb_7F3D; Yes → wall collision
$7F2Aa5 02ldazp_work0; Re-check left tile
$7F2Cc9 0dcmp#$0d; Tile >= $0D? (hazard)
$7F2Eb0 2fbcsb_7F5F; Yes → trigger death
$7F30a5 03ldazp_work1
$7F32c9 0dcmp#$0d
$7F34b0 29bcsb_7F5F; Yes → trigger death
$7F36a5 04ldazp_work2
$7F38c9 0dcmp#$0d; Check far-right hazard
$7F3Ab0 23bcsb_7F5F; Yes → trigger death
$7F3C60rts; No collision — return
$7F3Dc9 80b_7F3Dcmp#$80; Is tile $80? (special solid) ; x-ref: $7F1C, $7F22, $7F28
$7F3Fd0 08bneb_7F49
$7F41a9 00lda#$00; Clear collision counter
$7F438d 0d 7bstahero_death_substep
$7F464c 85 81jmpj_8185; Trigger death for special solid
$7F49ad fd 7ab_7F49ldahero_y_pos; Snap Y to 8-pixel grid boundary ; x-ref: $7F3F
$7F4C29 f8and#$f8
$7F4E18clc
$7F4F69 05adc#$05
$7F518d fd 7astahero_y_pos
$7F54ad 02 7bldahero_y_velocity; Moving up?
$7F5730 05bmir_7F5E; Yes → skip clearing
$7F59a9 00lda#$00; Clear vertical velocity
$7F5B8d 03 7bstahero_airborne_flag
$7F5E60r_7F5Erts; x-ref: $7F57
$7F5F4c 85 81b_7F5Fjmpj_8185; Trigger death for hazard tile ; x-ref: $7F2E, $7F34, $7F3A
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks whether the hero collides with the secondary entity ($82AB-$82AE).
; Compares X and Y pixel distances between the hero and the entity;
; if within range, snaps the hero X to the entity position, clears movement
; state, and sets the collision flag. Otherwise, restores hero Y from the
; saved position if the collision flag was previously active.
;
; Inputs: Hero position ($7AFB/$7AFC = Y lo/hi, $7AFD = X), entity position
; ($82AB = active flag, $82AC/$82AD = Y lo/hi, $82AE = X)
; Outputs: A = 0 if no collision / entity inactive; A != 0 if collision.
; Hero X ($7AFD), movement state ($7B00, $7B03, $7B05) modified on hit.
; Side Effects: On collision, hero snaps to entity X - 2 and movement is halted.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_entity_collision
$7F62ad ab 82ldatile13_active_flag; entity active? ; x-ref: $7E83
$7F65f0 45beqb_7FAC; inactive -> no collision
$7F67ad fd 7aldahero_y_pos; hero_x
$7F6A38sec
$7F6Bed ae 82sbctile13_column; hero_x - entity_x
$7F6Ec9 01cmp#$01; X dist >= 1? (too far right)
$7F7010 3abplb_7FAC
$7F72c9 fecmp#$fe; X dist < -2? (too far left)
$7F7430 36bmib_7FAC
$7F76ad fb 7aldahero_x_lo; hero_y_lo
$7F7938sec
$7F7Aed ac 82sbctile13_scroll_pos_lo; hero_y - entity_y (lo)
$7F7Da8tay; Y distance low byte -> Y reg
$7F7Ead fc 7aldahero_x_hi
$7F81ed ad 82sbctile13_scroll_pos_hi; hero_y - entity_y (hi, w/ borrow)
$7F84d0 07bneb_7F8D; hi != 0 -> check if negative
$7F86c0 0ecpy#$0e; Y dist < 14px? -> collision!
$7F88b0 22bcsb_7FAC; too far -> no collision
$7F8A4c 95 7fjmpj_7F95; collision: skip sign check
$7F8Dc9 ffb_7F8Dcmp#$ff; hi == $FF? (negative Y dist) ; x-ref: $7F84
$7F8Fd0 1bbneb_7FAC; not $FF -> too far, no collision
$7F91c0 f3cpy#$f3; lo >= $F3? (-13 signed)
$7F9390 17bccb_7FAC; in range -> fall to collision
$7F95ad ae 82j_7F95ldatile13_column; --- collision response --- ; x-ref: $7F8A
$7F9838sec
$7F99e9 02sbc#$02; hero_x = entity_x - 2
$7F9B8d fd 7astahero_y_pos; store new hero_x
$7F9Ea9 00lda#$00
$7FA08d 00 7bstahero_direction; clear horiz movement
$7FA38d 03 7bstahero_airborne_flag; clear vert movement
$7FA6a9 fflda#$ff; set collision active flag
$7FA88d 05 7bstahero_entity_collision
$7FAB60rts
$7FACad 05 7bb_7FACldahero_entity_collision; --- no collision path --- ; x-ref: $7F65, $7F70, $7F74, $7F88, $7F8F, ...
$7FAFf0 0bbeqr_7FBC; was collision flag clear? done
$7FB1ad fb 7aldahero_x_lo; restore hero_y from saved pos
$7FB48d fe 7astahero_x_target; -> hero_y_saved
$7FB7a9 00lda#$00
$7FB98d 05 7bstahero_entity_collision; clear collision flag
$7FBC60r_7FBCrts; x-ref: $7FAF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks collision between the player and tile-14 (destructible wall/object).
; Compares player X position (a_7AFD) against tile-14 X (tile14_screen_x) and
; player Y position (a_7AFB/a_7AFC) against tile-14 Y (tile14_screen_y_lo/hi).
; On hit: sets player state to 3 (hit), awards 1000 points, increments tile-14
; render counter, stops fire SFX, and silences SID voice 1.
;
; Inputs: tile14_active_flag, tile14_screen_x, tile14_screen_y_lo/hi,
; a_7AFB/a_7AFC (player Y 16-bit), a_7AFD (player X)
; Outputs: a_7AFA = #$03 on collision (hit state)
; Side Effects: Awards 1000 BCD points, stops SFX, silences SID voice 1
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_tile14_collision
$7FBDad 73 7aldatile14_active_flag; tile14 active? ; x-ref: $7C03
$7FC0f0 50beqr_8012; Not active -> exit
$7FC2ad fd 7aldahero_y_pos; delta_x = player_x - tile14_x
$7FC538sec
$7FC6ed 76 7asbctile14_screen_x
$7FC9c9 1ccmp#$1c; delta_x >= 28? too far right
$7FCB10 45bplr_8012
$7FCDc9 e5cmp#$e5; delta_x < -27? too far left
$7FCF30 41bmir_8012
$7FD1ad fb 7aldahero_x_lo; delta_y_lo = player_y_lo - tile14_y_lo
$7FD438sec
$7FD5ed 74 7asbctile14_screen_y_lo
$7FD8a8tay; Y = delta_y lo
$7FD9ad fc 7aldahero_x_hi; delta_y_hi = player_y_hi - tile14_y_hi
$7FDCed 75 7asbctile14_screen_y_hi
$7FDFd0 07bneb_7FE8; hi byte nonzero? check negative
$7FE1c0 16cpy#$16; delta_y_lo >= 22? too far below
$7FE3b0 2dbcsr_8012
$7FE54c f0 7fjmpj_7FF0
$7FE8c9 ffb_7FE8cmp#$ff; hi == $FF? (negative delta) ; x-ref: $7FDF
$7FEAd0 26bner_8012; delta_y_lo < $EB? too far above
$7FECc0 ebcpy#$eb
$7FEE90 22bccr_8012
$7FF0a9 03j_7FF0lda#$03; Collision! Set player state = hit ; x-ref: $7FE5
$7FF28d fa 7astahero_state
$7FF5a9 00lda#$00; Clear tile14 state
$7FF78d 00 7bstahero_direction
$7FFAee 77 7ainctile14_render_param; Increment tile14 render counter
$7FFDa9 00lda#$00; Y < $15? Above visible area
$7FFF85 fbstazp_ptr_src_lo; yes -> skip to anim update
$8001a9 10lda#$10; Y < floor limit?
$800385 fcstazp_ptr_src_hi
$8005a9 00lda#$00; Score hi = $00
$800785 fdstazp_ptr_dst_lo
$800920 d8 51jsrj_51D8; Add BCD score
$800C20 12 79jsrdeactivate_fire; Stop fire SFX if active
$800F4c 58 8bjmpsilence_sfx6_voice1; Silence SID voice 1 (tail call)
$801260r_8012rts; x-ref: $7FC0, $7FCB, $7FCF, $7FE3, $7FEA, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Snaps the hero's X position to the nearest 8-pixel tile boundary.
; Adds 4 (half a tile) and masks with $F8 to round to the nearest
; aligned position. Also updates the X movement target.
;
; Inputs: hero_x_lo, hero_x_hi
; Outputs: hero_x_lo, hero_x_hi, hero_x_target
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8013ad fb 7asnap_hero_x_to_tileldahero_x_lo; x-ref: $7B5F, $7DDB
$801618clc; prepare for addition
$801769 04adc#$04; add half a tile width (4 pixels)
$801929 f8and#$f8; mask to 8-pixel boundary (round to nearest tile)
$801B8d fb 7astahero_x_lo; store snapped X low byte
$801E8d fe 7astahero_x_target; also set as movement target
$8021ad fc 7aldahero_x_hi
$802469 00adc#$00; propagate carry to high byte
$80268d fc 7astahero_x_hi; store updated X high byte
$802960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Dispatches fire-button detection across input modes.
; Checks whether the fire button was newly pressed this frame.
; - If transition_active != 0: checks joystick port 1 fire (bit 1 falling edge).
; - If input_mode == 0: same joystick port 1 check.
; - If input_mode == 1: double-tap fire detection on joystick port 1,
; using a 25-frame ($19) timer and a 3-state tap state machine.
; - If input_mode == 2: keyboard row 7, key $EF ("2" key).
; - Otherwise: joystick port 2 fire (bit 5 falling edge).
;
; Inputs: None (reads transition_active, input_mode, joystick_state, joystick_prev_state)
; Outputs: A = $FF if fire just pressed, $00 otherwise
; Side Effects: Modifies hero_fire_tap_state and hero_fire_tap_timer (in double-tap mode)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_hero_fire_input
$802Aad 08 1dldatransition_active; is a screen transition active? ; x-ref: $7C1E
$802Dd0 05bneb_8034; yes → skip to joystick port 1 check
$802Fad d7 60ldainput_mode; 0=joy1, 1=double-tap, 2=keyboard, other=joy2
$8032d0 11bneb_8045; mode != 0 → branch to mode dispatch
$8034ad 80 3eb_8034ldajoystick_state; --- Mode 0 / transition: joystick port 1 fire --- ; x-ref: $802D
$803729 02and#$02; bit 1 = fire button
$8039d0 78bnefire_not_detected; fire held → not pressed (need release first)
$803Bad 81 3eldajoystick_prev_state
$803E29 02and#$02; bit 1 of prev state
$8040f0 71beqfire_not_detected; was also released → no edge → not pressed
$80424c b6 80jmpfire_detected; falling edge: was up, now down → fire!
$8045c9 01b_8045cmp#$01; --- Mode 1: double-tap fire detection --- ; x-ref: $8032
$8047d0 46bnecheck_fire_pressed; mode != 1 → check keyboard/joy2
$8049ad 0f 7bldahero_fire_tap_state; tap_state: 0=idle, 1=first tap, 2=second tap
$804Cf0 0abeqb_8058; idle → skip timer countdown
$804Ece 10 7bdechero_fire_tap_timer; count down 25-frame window
$8051d0 05bneb_8058; timer not expired → keep waiting
$8053a9 00lda#$00; timer expired → reset tap state to idle
$80558d 0f 7bstahero_fire_tap_state
$8058ad 80 3eb_8058ldajoystick_state; check current joystick fire button ; x-ref: $804C, $8051
$805B29 02and#$02; bit 1 = fire button
$805Dd0 23bneb_8082; fire held this frame → check if release needed
$805Fad 81 3eldajoystick_prev_state
$806229 02and#$02; check if fire was held last frame
$8064f0 4dbeqfire_not_detected; no falling edge → not pressed
$8066ad 0f 7bldahero_fire_tap_state; check current tap state
$8069d0 0bbneb_8076; not idle → check for 2nd tap
$806Bee 0f 7binchero_fire_tap_state; 1st tap detected → advance state
$806Ea9 19lda#$19; start 25-frame ($19) timer window
$80708d 10 7bstahero_fire_tap_timer
$80734c b3 80jmpfire_not_detected; 1st tap registered, don't fire yet
$8076c9 02b_8076cmp#$02; is this the 2nd tap (state == 2)? ; x-ref: $8069
$8078d0 39bnefire_not_detected; not 2nd tap → ignore
$807Aa9 00lda#$00; 2nd tap confirmed → reset state
$807C8d 0f 7bstahero_fire_tap_state
$807F4c b6 80jmpfire_detected; double-tap complete → fire!
$8082ad 0f 7bb_8082ldahero_fire_tap_state; fire held: if 1st tap, advance to state 2 ; x-ref: $805D
$8085c9 01cmp#$01
$8087d0 2abnefire_not_detected; not in 1st-tap state → no action
$8089ee 0f 7binchero_fire_tap_state; advance 1→2 (awaiting 2nd tap release)
$808C4c b3 80jmpfire_not_detected; still waiting, don't fire yet
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Checks if the fire button was newly pressed this frame.
; Branched to from the input dispatcher when input mode is not joystick port 1.
; If A=2, checks keyboard row 7 for the fire key ($EF = "2" key).
; Otherwise, checks joystick port 2 fire button (bit 5 falling edge).
; Returns A=$FF if fire was just pressed, A=$00 otherwise.
;
; Inputs: A = input mode (2 = keyboard, other = joystick port 2)
; Outputs: A = $FF (fire pressed) or $00 (not pressed)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$808Fc9 02check_fire_pressedcmp#$02; keyboard mode? ; x-ref: $8047
$8091d0 0fbnecheck_fire_joystick; not keyboard -> check joystick
$8093ad 16 3fldakb_row7_cur; read current keyboard row 7
$8096cd 17 3fcmpkb_row7_prev; changed since last frame?
$8099f0 18beqfire_not_detected; no change -> not pressed
$809Bc9 efcmp#$ef; $EF = fire key ("2") pressed
$809Dd0 14bnefire_not_detected; wrong key -> not pressed
$809F4c b6 80jmpfire_detected; fire key detected!
$80A2ad 80 3echeck_fire_joystickldajoystick_state; read joystick port 2 state ; x-ref: $8091
$80A529 20and#$20; bit 5 = fire button
$80A7d0 0abnefire_not_detected; fire not held -> no press
$80A9ad 81 3eldajoystick_prev_state; read previous joystick state
$80AC29 20and#$20
$80AEf0 03beqfire_not_detected; was also held last frame -> not new
$80B04c b6 80jmpfire_detected; new fire press detected!
$80B3a9 00fire_not_detectedlda#$00; return: fire NOT pressed ; x-ref: $8039, $8040, $8064, $8073, $8078, ...
$80B560rts
$80B6a9 fffire_detectedlda#$ff; return: fire WAS pressed ; x-ref: $8042, $807F, $809F, $80B0
$80B860rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the hero walk animation by one sub-frame tick.
; Decrements the sub-frame counter; when it wraps below zero, resets it
; to 3 and increments the frame index. Frame index wraps from 4 back to 0,
; giving a 5-frame walk cycle (0–4) with 4 sub-frame ticks per frame.
;
; Inputs: hero_walk_subframe (sub-frame counter), hero_walk_frame (frame index)
; Outputs: hero_walk_subframe (decremented/reset), hero_walk_frame (advanced/wrapped)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$80B9ce 09 7badvance_walk_animdechero_walk_subframe; sub-frame tick-- ; x-ref: $7CFF, $7D34
$80BC10 14bplr_80D2; still counting down? skip
$80BEa9 03lda#$03; reset sub-frame to 3 (4 ticks/frame)
$80C08d 09 7bstahero_walk_subframe
$80C3ee 08 7binchero_walk_frame; next animation frame
$80C6ad 08 7bldahero_walk_frame
$80C9c9 05cmp#$05; reached frame 5?
$80CBd0 05bner_80D2; no -> keep current frame
$80CDa9 00lda#$00; wrap frame back to 0
$80CF8d 08 7bstahero_walk_frame
$80D260r_80D2rts; x-ref: $80BC, $80CB
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the hero hover animation timer. Increments the tick counter
; and, when it reaches the animation delay threshold, resets the tick
; and advances to the next frame (cycling 0-3).
;
; Inputs: hero_hover_tick ($7B0B), hero_anim_delay ($7B09), hero_hover_frame ($7B0A)
; Outputs: hero_hover_tick (incremented/reset), hero_hover_frame (incremented/reset)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
advance_hero_anim_frame
$80D3ee 0b 7binchero_hover_tick; tick++ ; x-ref: $7BAE, $7BC4, $7C81, $813F
$80D6ad 0b 7bldahero_hover_tick; load current tick
$80D9cd 0c 7bcmphero_anim_delay; reached delay threshold?
$80DC90 14bccr_80F2; no -> return early
$80DEa9 00lda#$00; reset tick to 0
$80E08d 0b 7bstahero_hover_tick
$80E3ee 0a 7binchero_hover_frame; advance to next frame
$80E6ad 0a 7bldahero_hover_frame; load current frame
$80E9c9 04cmp#$04; frame >= 4? (max frames)
$80EBd0 05bner_80F2; no -> return
$80EDa9 00lda#$00; wrap frame back to 0
$80EF8d 0a 7bstahero_hover_frame
$80F260r_80F2rts; x-ref: $80DC, $80EB
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the hero downward by incrementing the vertical descent
; counter (a_7AFD). Checks vertical bounds ($F0 max, $15 min) and
; whether the hero has passed the floor limit (a_7AFF).
; If within bounds, probes the map tiles 8 pixels above and 7 pixels
; below the hero at the adjusted X offset (X-15) using get_map_tile.
; If both tiles are non-solid (tile ID < $0D), transitions the hero
; movement state (a_7AFA) to 2 (hovering). Otherwise exits to the
; animation frame update (s_80D3).
;
; Inputs: a_7AFB/a_7AFC = hero Y pos (lo/hi), a_7AFD = descent counter, a_7AFF = floor limit
; Outputs: a_7AFA = hero movement state (set to 2 if path clear)
; Side Effects: Calls get_map_tile (overwrites $FB/$FC), advances animation via s_80D3
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_falling_movement
$80F3ee fd 7ainchero_y_pos; descent counter++ ; x-ref: $7BA7
$80F6ad fd 7aldahero_y_pos; load updated descent pos
$80F9c9 f0cmp#$f0; Y >= $F0? out of bounds
$80FBb0 42bcsb_813F; yes -> skip to anim update
$80FDc9 15cmp#$15; Y < $15? above visible area
$80FF90 3ebccb_813F; yes -> skip to anim update
$8101cd ff 7acmphero_y_floor_limit; Y < floor limit?
$810490 39bccb_813F; yes -> skip to anim update
$8106ad fb 7aldahero_x_lo; hero Y position low byte
$810938sec; compute Y - 8 (8 px above)
$810Ae9 08sbc#$08
$810C85 fdstazp_ptr_dst_lo; store as map query Y-lo
$810Ead fc 7aldahero_x_hi; Y high byte
$8111e9 00sbc#$00; subtract borrow
$811385 festazp_ptr_dst_hi; store as map query Y-hi
$8115ad fd 7aldahero_y_pos; load X position for tile check
$811838sec
$8119e9 0fsbc#$0f; X - 15 (left edge offset)
$811Baatax; X = adjusted X for map query
$811C20 ca 50jsrget_map_tile; get tile at (X-15, Y-8)
$811Fc9 0dcmp#$0d; tile >= $0D? (solid)
$8121b0 1cbcsb_813F; solid above -> stop descent
$8123ad fb 7aldahero_x_lo; hero Y pos low byte again
$812618clc
$812769 07adc#$07; Y + 7 (7 px below)
$812985 fdstazp_ptr_dst_lo; store as map query Y-lo
$812Bad fc 7aldahero_x_hi; Y high byte
$812E69 00adc#$00; add carry
$813085 festazp_ptr_dst_hi; store as map query Y-hi
$81328atxa; restore adjusted X position
$813320 ca 50jsrget_map_tile; get tile at (X-15, Y+7)
$8136c9 0dcmp#$0d; tile >= $0D? (solid)
$8138b0 05bcsb_813F; solid below -> stop descent
$813Aa9 02lda#$02; both clear: set state = 2 (hovering)
$813C8d fa 7astahero_state; update hero movement state
$813F4c d3 80b_813Fjmpadvance_hero_anim_frame; advance animation frame and return ; x-ref: $80FB, $80FF, $8104, $8121, $8138
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Selects the hero's sprite pointer based on current state, direction,
; and animation frame. Computes a VIC-II sprite pointer index and
; stores it in a_7B07.
;
; a_7AFA >= 4 -> death/special sprite ($98)
; a_7B03 != 0 -> airborne/attack set: frame*7 + $88
; a_7B00 == 0 -> ground facing-left set: frame*7 + $82
; a_7B00 != 0 -> ground facing-right: lookup f_7B12[a_7B08],
; +7 if a_7B00 > 1 (multi-dir variant)
;
; Inputs: a_7AFA (entity state), a_7B00 (direction), a_7B01 (frame),
; a_7B03 (airborne flag), a_7B08 (table index)
; Outputs: a_7B07 (sprite pointer)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
select_hero_sprite_ptr
$8142ad fa 7aldahero_state; Get entity state ; x-ref: $61EF
$8145c9 04cmp#$04; State >= 4? (death/special)
$814790 06bccb_814F; Yes: skip to death sprite
$8149a9 98lda#$98; Death/special sprite pointer = $98
$814B8d 07 7bstahero_sprite_ptr; Store sprite pointer
$814E60rts
$814Fad 03 7bb_814Fldahero_airborne_flag; Airborne/attack flag set? ; x-ref: $8147
$8152f0 0cbeqb_8160; No: check direction
$8154ad 01 7bldahero_scroll_dir; Get animation frame index
$815720 ac 21jsrmultiply_by_7; A = frame * 7
$815A69 88adc#$88; Airborne sprite base = $88
$815C8d 07 7bstahero_sprite_ptr; Store sprite pointer
$815F60rts
$8160ad 00 7bb_8160ldahero_direction; Get direction (0=left) ; x-ref: $8152
$8163d0 0cbneb_8171; Non-zero: use lookup table
$8165ad 01 7bldahero_scroll_dir; Get animation frame index
$816820 ac 21jsrmultiply_by_7; A = frame * 7
$816B69 82adc#$82; Ground-left sprite base = $82
$816D8d 07 7bstahero_sprite_ptr; Store sprite pointer
$817060rts
$8171ae 08 7bb_8171ldxhero_walk_frame; Get frame table index ; x-ref: $8163
$8174bd 12 7bldahero_sprite_table,x; Lookup sprite ptr from table
$8177ae 00 7bldxhero_direction; Re-check direction
$817Ae0 01cpx#$01; Direction == 1? Use as-is
$817Cf0 03beqb_8181; Direction > 1: skip ahead
$817E18clc; Clear carry for addition
$817F69 07adc#$07; Add 7 for alt direction variant
$81818d 07 7bb_8181stahero_sprite_ptr; Store final sprite pointer ; x-ref: $817C
$818460rts
$8185a9 04j_8185lda#$04; x-ref: $1F53, $1F5E, $1F95, $1FAE, $1FB9, ...
$81878d fa 7astahero_state
$818Aad fd 7aldahero_y_pos
$818D29 f8and#$f8
$818F38sec
$8190e9 08sbc#$08
$81928d ff 7astahero_y_floor_limit
$8195a9 50lda#$50
$81978d 0e 7bstahero_death_timer
$819A20 12 79jsrdeactivate_fire
$819Da9 00lda#$00
$819F8d f0 8astasfx6_enable
$81A2a9 01lda#$01
$81A48d 1a 8astasfx_ch4_state
$81A760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Positions the bomb/dynamite sprite relative to the hero's current location.
; If rendering is in progress, hides the sprite by setting Y to $FF.
; Otherwise, copies the hero's X/Y position (with offsets) into the bomb's
; sprite slot ZP variables, selects the sprite frame based on hero state
; and hover animation, and handles the invulnerability override frame.
;
; Inputs: hero_x_lo/hi, hero_y_pos, hero_state, hero_scroll_dir,
; hero_sprite_ptr, hero_hover_frame, hero_invulnerable,
; rendering_in_progress
; Outputs: zp_spr_x_lo/hi, zp_spr_y_pos, zp_spr_frame,
; zpa_47, zpa_5A, zpa_34, zpa_6D
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$81A8ad a7 3fsetup_bomb_spriteldarendering_in_progress; Check if rendering is active ; x-ref: $6486
$81ABf0 07beqb_81B4; If not rendering, proceed to position
$81ADa9 fflda#$ff; Hide sprite: Y = $FF (offscreen)
$81AF85 33stazp_spr_y_pos
$81B185 34stazp_mux_y_bomb; Also hide collision Y
$81B360rts
$81B4ad fb 7ab_81B4ldahero_x_lo; Base X = hero_x_lo ; x-ref: $81AB
$81B718clc
$81B869 0cadc#$0c; Offset +12 pixels right
$81BA85 46stazp_spr_x_lo
$81BCad fc 7aldahero_x_hi
$81BF69 00adc#$00
$81C185 59stazp_spr_x_hi
$81C3ad fa 7aldahero_state; Check hero state
$81C6c9 04cmp#$04; State >= 4? (crouching/special)
$81C8b0 17bcsb_81E1; Yes: skip X offset adjustment
$81CAad 01 7bldahero_scroll_dir; Check scroll direction
$81CDc9 01cmp#$01; Facing right? (dir == 1)
$81CFf0 10beqb_81E1; Yes: use base X without nudge
$81D1a5 46ldazp_spr_x_lo; Facing left: nudge X back 6px
$81D338sec
$81D4e9 06sbc#$06; X - 6 for left-facing offset
$81D685 47stazp_mux_x_lo_bomb
$81D8a5 59ldazp_spr_x_hi
$81DAe9 00sbc#$00
$81DC85 5astazp_mux_x_hi_bomb
$81DE4c e9 81jmpj_81E9
$81E1a5 46b_81E1ldazp_spr_x_lo; x-ref: $81C8, $81CF
$81E385 47stazp_mux_x_lo_bomb
$81E5a5 59ldazp_spr_x_hi
$81E785 5astazp_mux_x_hi_bomb
$81E9ad fd 7aj_81E9ldahero_y_pos; Base Y = hero Y pos ; x-ref: $81DE
$81EC18clc
$81ED69 25adc#$25; Offset +37 pixels down (near feet)
$81EF85 33stazp_spr_y_pos
$81F138sec
$81F2e9 03sbc#$03; Collision Y = sprite Y - 3
$81F485 34stazp_mux_y_bomb
$81F6ad 11 7bldahero_invulnerable; Is hero invulnerable?
$81F9d0 17bneb_8212; Yes: use invulnerability frame
$81FBad 07 7bldahero_sprite_ptr; Normal: load hero sprite pointer
$81FE85 6cstazp_spr_frame
$8200ae 0a 7bldxhero_hover_frame; Index into hover animation table
$8203bd 17 7bldahero_hover_frames,x; Get hover frame from f_7B17 table
$8206ae fa 7aldxhero_state; Re-check hero state
$8209e0 04cpx#$04; State >= 4?
$820B90 02bccb_820F; No: use hover frame
$820Da9 9blda#$9b; Override: crouching frame = $9B
$820F85 6db_820Fstazp_mux_frame_bomb; Store secondary sprite frame ; x-ref: $820B
$821160rts
$8212a9 9cb_8212lda#$9c; Invulnerable: frame = $9C (flash) ; x-ref: $81F9
$821485 6cstazp_spr_frame
$8216a9 89lda#$89; Invulnerable secondary frame = $89
$821885 6dstazp_mux_frame_bomb
$821A60rts
; Current energy (0-128). Decremented by energy drain; used as SID freq for SFX
$821Benergy_level.byte$00; x-ref: $6206, $6228, $622D, $824A, $8286, ...
; Energy drain 16-bit fractional accumulator, lo-byte
$821Cenergy_drain_frac_lo.byte$00; x-ref: $824F, $8271, $8278
; Energy drain fractional accumulator, hi-byte. Carry triggers energy_level decrement
$821Denergy_drain_frac_hi.byte$00; x-ref: $8252, $827B, $8281
; Energy drain speed per frame, lo-byte (loaded from table by level)
energy_drain_speed_lo
$821E.byte$00; x-ref: $825D, $8275
; Energy drain speed per frame, hi-byte
energy_drain_speed_hi
$821F.byte$00; x-ref: $8263, $827E
; 16-bit drain rate table indexed by level (higher = faster drain)
energy_drain_rate_tbl
$8220.byte$70; x-ref: $825A
; Hi-bytes of energy drain rate table
energy_drain_rate_tbl_hi
$8221.byte$0d, $b8, $06, $d7, $03, $b0, $02, $3d; x-ref: $8260
$8229.byte$02, $eb, $01, $94, $01, $7e, $01, $73
$8231.byte$01, $60, $01, $58, $01, $58, $01, $58
$8239.byte$01, $2b, $01, $58, $01, $58, $01, $58
$8241.byte$01, $58, $01, $1e, $01, $24, $01
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes energy for the current level. Sets energy_level to the
; maximum value ($80 = 128) and configures the per-level drain speed
; from a lookup table indexed by current_level.
;
; Inputs: current_level (used to index the drain rate table)
; Outputs: energy_level = $80, energy_drain_frac_lo/hi = 0,
; energy_drain_speed_lo/hi = table value for current level
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8248a9 80init_energylda#$80; Energy starts at max (128) ; x-ref: $6137
$824A8d 1b 82staenergy_level; Store initial energy
$824Da9 00lda#$00; Zero the fractional drain accumulator
$824F8d 1c 82staenergy_drain_frac_lo
$82528d 1d 82staenergy_drain_frac_hi
$8255ad d9 60ldacurrent_level; current_level * 2 = word table index
$82580aasla
$8259aatax; Look up drain rate for current level
$825Abd 20 82ldaenergy_drain_rate_tbl,x; Load drain rate lo for this level
$825D8d 1e 82staenergy_drain_speed_lo
$8260bd 21 82ldaenergy_drain_rate_tbl_hi,x; Load drain rate hi for this level
$82638d 1f 82staenergy_drain_speed_hi
$826660rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Drains the hero's energy over time using a 16-bit fractional accumulator.
; When the accumulator overflows, energy_level is decremented. If energy
; reaches zero, triggers hero death via j_8185. After each drain tick,
; updates the on-screen energy bar by writing tile characters to scr_lava_row.
; Skipped entirely if hero is dead (hero_state != 0) or invulnerable.
;
; Inputs: energy_drain_speed_lo/hi (drain rate), energy_level
; Outputs: energy_drain_frac_lo/hi (updated), energy_level (decremented on overflow)
; Side Effects: Updates energy bar display on screen; may trigger hero death
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8267ad fa 7aupdate_energy_drainldahero_state; skip if hero is dead ; x-ref: $61E9
$826Ad0 33bner_829F
$826Cad 11 7bldahero_invulnerable; skip if hero is invulnerable
$826Fd0 2ebner_829F
$8271ad 1c 82ldaenergy_drain_frac_lo; add drain speed to 16-bit fractional counter
$827418clc; carry = overflow, drain 1 energy unit
$82756d 1e 82adcenergy_drain_speed_lo
$82788d 1c 82staenergy_drain_frac_lo
$827Bad 1d 82ldaenergy_drain_frac_hi; Accumulate drain hi-byte
$827E6d 1f 82adcenergy_drain_speed_hi
$82818d 1d 82staenergy_drain_frac_hi
$828490 19bccr_829F
$8286ce 1b 82decenergy_level; Drain overflow: decrement energy
$8289d0 03bneupdate_energy_bar_display
$828B20 85 81jsrj_8185; energy depleted: trigger hero death
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Updates the on-screen energy bar by writing the appropriate tile character
; at the correct column of the HUD lava row. The energy value (0-128) is
; divided by 4 to find the character column, and the 2-bit remainder selects
; one of 4 tile phases ($5A-$5D) to show a smooth depletion animation.
;
; Inputs: energy_level (current energy, 0-128)
; Outputs: scr_lava_row updated at column (energy_level / 4)
; Side Effects: Modifies one character cell on the HUD row
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_energy_bar_display
$828Ead 1b 82ldaenergy_level; A = energy level (0-128) ; x-ref: $6230, $8289
$82914alsra; energy / 4 (step 1)
$82924alsra; energy / 4 (step 2)
$8293aatax; X = column index in bar (0-32)
$8294ad 1b 82ldaenergy_level; reload energy for sub-cell phase
$829729 03and#$03; A = phase within cell (0-3)
$829918clc
$829A69 5aadc#$5a; $5A = base tile for energy bar
$829C9d d7 06staSCREEN_RAM_R18C7,x; draw tile at bar column X
$829F60r_829Frts; x-ref: $826A, $826F, $8284
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Fills the entire energy bar row on screen with the "full" tile character ($5E).
; Used during HUD initialization to display a full energy bar.
;
; Inputs: None
; Outputs: scr_lava_row[0..31] = $5E
; Side Effects: Updates the on-screen energy bar display row
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$82A0a2 1ffill_energy_barldx#$1f; loop counter: 31 down to 0 (32 bytes) ; x-ref: $62CC
$82A2a9 5elda#$5e; tile $5E = full energy bar character
$82A49d d7 06b_82A4staSCREEN_RAM_R18C7,x; fill energy bar row with full tile ; x-ref: $82A8
$82A7cadex; next column
$82A810 fabplb_82A4; loop until all 32 bytes filled
$82AA60rts
$82ABtile13_active_flag.byte$00; x-ref: $4AFB, $7F62, $82B2, $82BC, $82FA, ...
$82ACtile13_scroll_pos_lo.byte$00; x-ref: $7F7A, $82CD, $82E9, $830C, $8312, ...
$82ADtile13_scroll_pos_hi.byte$00; x-ref: $7F81, $82D4, $82E4, $8318, $831D, ...
$82AEtile13_column.byte$00; x-ref: $7F6B, $7F95, $82DF, $8332, $8368, ...
$82AFtile13_scroll_dir.byte$00; x-ref: $82F2, $8305, $833E, $8374
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Disables tile 13 scrolling by clearing the active flag.
; Called during room initialization to reset the scroll subsystem.
;
; Inputs: None
; Outputs: tile13_active_flag = 0
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$82B0a9 00reset_tile13_scrolllda#$00; disable tile 13 scroll ; x-ref: $4B10
$82B28d ab 82statile13_active_flag; clear active flag
$82B560rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes tile 13 horizontal scroll parameters.
; Converts tile grid position (X=row, Y=column) into a pixel-based scroll
; target and determines initial scroll direction based on viewport position.
;
; Inputs: X = tile row, Y = tile column
; Outputs: tile13_scroll_pos_lo/hi = target pixel position,
; tile13_column = pixel column offset,
; tile13_scroll_dir = $01 (left) or $02 (right)
; Side Effects: Sets tile13_active_flag to $FF (enabled)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$82B686 1einit_tile13_scrollstxzp_temp_x; save tile row ; x-ref: $4FD3
$82B884 1fstyzp_temp_y; save tile column
$82BAa9 fflda#$ff; mark scrolling as active
$82BC8d ab 82statile13_active_flag; enable active flag
$82BFa9 00lda#$00; clear high byte for multiply
$82C185 1dstazp_ptr_map_hi
$82C3a5 1fldazp_temp_y; column * 8 = pixel X position
$82C50aasla
$82C60aasla
$82C70aasla
$82C826 1drolzp_ptr_map_hi; carry from column*8 into hi byte
$82CA18clc
$82CB69 0cadc#$0c; add 12 pixel offset
$82CD8d ac 82statile13_scroll_pos_lo; store target scroll pos lo
$82D0a5 1dldazp_ptr_map_hi
$82D269 00adc#$00; add carry to hi byte
$82D48d ad 82statile13_scroll_pos_hi; store target scroll pos hi
$82D7a5 1eldazp_temp_x; row
$82D90aasla; row * 8 = pixel Y position
$82DA0aasla
$82DB0aasla
$82DC38sec; subtract 3 to center vertically
$82DDe9 03sbc#$03
$82DF8d ae 82statile13_column; store column pixel offset
$82E2a9 01lda#$01; assume scroll left (dir=$01)
$82E4ae ad 82ldxtile13_scroll_pos_hi; check if pos hi > 0
$82E7d0 09bneb_82F2
$82E9ae ac 82ldxtile13_scroll_pos_lo; check if pos lo >= $A0 (160)
$82ECe0 a0cpx#$a0
$82EEb0 02bcsb_82F2
$82F0a9 02lda#$02; below 160: scroll right (dir=$02)
$82F28d af 82b_82F2statile13_scroll_dir; store scroll direction ; x-ref: $82E7, $82EE
$82F5a6 1eldxzp_temp_x; restore tile row in X
$82F7a4 1fldyzp_temp_y; restore tile column in Y
$82F960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame update for tile 13 scrolling.
; Scrolls the viewport 2 pixels per frame toward the tile 13 target position.
; Checks map tile boundaries and reverses direction if blocked by a wall
; (tile ID >= $1A). Updates the shared scroll position at a_7AFB/a_7AFC.
;
; Inputs: tile13_active_flag, tile13_scroll_dir, a_7B05
; Outputs: tile13_scroll_pos_lo/hi updated, a_7AFB/a_7AFC updated
; Side Effects: May reverse scroll direction if a wall tile is encountered
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$82FAad ab 82update_tile13_scrollldatile13_active_flag; check if tile 13 scroll is active ; x-ref: $61E3
$82FDf0 05beqr_8304; inactive: skip update
$82FFad 05 7bldahero_entity_collision; check if scroll-needed flag is set
$8302d0 01bneb_8305; scroll needed: continue
$830460r_8304rts; x-ref: $82FD
$8305ad af 82b_8305ldatile13_scroll_dir; check direction: $01=left, $02=right ; x-ref: $8302
$8308c9 01cmp#$01
$830Ad0 36bneb_8342; not left: jump to scroll-right branch
$830Cad ac 82ldatile13_scroll_pos_lo; load current scroll pos lo
$830F38sec
$8310e9 02sbc#$02
$83128d ac 82statile13_scroll_pos_lo; store updated pos lo
$83158d fb 7astahero_x_lo; copy to shared scroll register
$8318ad ad 82ldatile13_scroll_pos_hi; borrow from hi byte
$831Be9 00sbc#$00
$831D8d ad 82statile13_scroll_pos_hi
$83208d fc 7astahero_x_hi
$8323ad ac 82ldatile13_scroll_pos_lo; store updated pos hi to shared register
$832638sec; compute left boundary: pos - 14
$8327e9 0esbc#$0e
$832985 fdstazp_ptr_dst_lo
$832Bad ad 82ldatile13_scroll_pos_hi
$832Ee9 00sbc#$00
$833085 festazp_ptr_dst_hi
$8332ad ae 82ldatile13_column; subtract 14 to get left edge for boundary check
$833520 ca 50jsrget_map_tile
$8338c9 1acmp#$1a
$833A90 05bccr_8341
$833Ca9 02lda#$02
$833E8d af 82statile13_scroll_dir; get tile at left boundary
$834160r_8341rts; x-ref: $833A
$8342ad ac 82b_8342ldatile13_scroll_pos_lo; no wall: keep scrolling left ; x-ref: $830A
$834518clc
$834669 02adc#$02; set direction = right
$83488d ac 82statile13_scroll_pos_lo; load current scroll pos lo
$834B8d fb 7astahero_x_lo
$834Ead ad 82ldatile13_scroll_pos_hi; add 2 pixels (scroll right)
$835169 00adc#$00
$83538d ad 82statile13_scroll_pos_hi; copy to shared scroll register lo
$83568d fc 7astahero_x_hi
$8359ad ac 82ldatile13_scroll_pos_lo; compute right boundary: pos + 12
$835C18clc
$835D69 0cadc#$0c
$835F85 fdstazp_ptr_dst_lo
$8361ad ad 82ldatile13_scroll_pos_hi
$836469 00adc#$00
$836685 festazp_ptr_dst_hi
$8368ad ae 82ldatile13_column
$836B20 ca 50jsrget_map_tile; get tile at right boundary
$836Ec9 1acmp#$1a; tile >= $1A is a wall?
$837090 05bccr_8377; no wall: keep scrolling right
$8372a9 01lda#$01; wall hit: reverse to scroll left
$83748d af 82statile13_scroll_dir; set direction = left
$837760r_8377rts; x-ref: $8370
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sets up the sprite parameters for tile 13 (scrolling tile entity).
; Checks if tile 13 is active and rendering is not locked, then writes
; the sprite X/Y position, frame pointer, and color into the shared
; ZP sprite slots for the multiplexer to pick up.
;
; Inputs: tile13_active_flag, tile13_scroll_pos_lo/hi, tile13_column
; Outputs: zpa_58/zpa_6B (sprite X lo/hi), zp_screen_dirty (sprite Y),
; zpa_7E (sprite frame), zp_spr_color_extra (sprite color)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8378ad ab 82setup_tile13_spriteldatile13_active_flag; tile 13 active? ; x-ref: $648C
$837Bf0 24beqr_83A1; no → skip
$837Dad a7 3fldarendering_in_progress; renderer busy?
$8380d0 1fbner_83A1; yes → skip to avoid conflict
$8382ad ac 82ldatile13_scroll_pos_lo; tile 13 X position lo
$838518clc
$838669 0cadc#$0c; add 12-pixel X offset
$838885 58stazp_mux_x_lo_hazard; → sprite X lo
$838Aad ad 82ldatile13_scroll_pos_hi; tile 13 X position hi
$838D69 00adc#$00; propagate carry
$838F85 6bstazp_mux_x_hi_hazard; → sprite X hi
$8391ad ae 82ldatile13_column; tile 13 Y position
$839418clc
$839569 25adc#$25; add 37-pixel Y offset
$839785 45stazp_screen_dirty; → sprite Y
$8399a9 bblda#$bb; sprite frame $BB = tile 13 shape
$839B85 7estazp_mux_frame_hazard; → sprite frame
$839Da9 07lda#VicIIColors.YELLOW; $07 = yellow
$839F85 91stazp_spr_color_extra; → sprite color
$83A160r_83A1rts; x-ref: $837B, $8380
; Raft/tile-15 active flag ($00=inactive, $FF=active)
$83A2raft_active.byte$00; x-ref: $1F65, $847D, $8487, $84C8, $84F4, ...
; Raft pixel Y position, lo-byte (row*8 + 8)
$83A3raft_pixel_y_lo.byte$00; x-ref: $1F7D, $849B
; Raft pixel Y position, hi-byte
$83A4raft_pixel_y_hi.byte$00; x-ref: $1F84, $84A2
; Raft pixel X position (col*8 + $14)
$83A5raft_pixel_x.byte$00; x-ref: $1F6E, $84B0
; Raft tile row index (used for screen address calculation)
$83A6raft_tile_row.byte$00; x-ref: $8490, $8501, $852F
; Raft tile column index (used for screen address calculation)
$83A7raft_tile_col.byte$00; x-ref: $84A7, $84FA, $8528
; Current raft animation frame (tile index from bounce table, 0-4)
$83A8raft_anim_frame.byte$00; x-ref: $1F89, $1F9F, $84BB, $84ED, $853B, ...
; Index into raft bounce sequence table (0-7, wraps around)
$83A9raft_anim_seq_idx.byte$00; x-ref: $84B5, $84DD, $84E7
; Raft animation tick counter (0-7, advances frame when reaching 8)
$83AAraft_anim_tick.byte$00; x-ref: $84C0, $84CE, $84D1, $84DA
; Ping-pong animation sequence: {0,1,2,3,4,3,2,1}
$83ABraft_anim_bounce_tbl.byte$00, $01, $02, $03, $04, $03, $02, $01; x-ref: $84B8, $84EA
$83B3.byte$01, $02, $00, $01, $14, $02, $00, $01
$83BB.byte$16, $14, $00, $01, $15, $16, $14, $01
$83C3.byte$14, $15, $16, $14, $00, $02, $00, $02
$83CB.byte$00, $02, $00, $14, $00, $02, $14, $15
$83D3.byte$00, $14, $15, $16, $14, $15, $16, $14
$83DB.byte$00, $01, $02, $00, $15, $01, $02, $00
$83E3.byte$14, $15, $02, $00, $18, $14, $15, $00
$83EB.byte$17, $18, $14, $15, $02, $01, $01, $00
$83F3.byte$02, $01, $01, $17, $02, $01, $17, $18
$83FB.byte$02, $17, $18, $14, $17, $18, $14, $15
$8403.byte$01, $00, $00, $02, $15, $00, $00, $02
$840B.byte$16, $15, $00, $02, $15, $16, $15, $02
$8413.byte$15, $15, $16, $15, $00, $01, $00, $02
$841B.byte$00, $01, $00, $15, $00, $01, $15, $15
$8423.byte$00, $15, $15, $16, $15, $15, $16, $15
$842B.byte$00, $02, $01, $00, $19, $02, $01, $00
$8433.byte$17, $19, $01, $00, $18, $17, $19, $00
$843B.byte$14, $18, $17, $19, $01, $00, $02, $01
$8443.byte$01, $00, $02, $14, $01, $00, $14, $18
$844B.byte$01, $14, $18, $17, $14, $18, $17, $19
$8453.byte$02, $00, $00, $01, $14, $00, $00, $01
$845B.byte$14, $14, $00, $01, $17, $14, $14, $01
$8463.byte$16, $17, $14, $14, $02, $00, $02, $00
$846B.byte$02, $00, $02, $16, $02, $00, $16, $17
$8473.byte$02, $16, $17, $14, $16, $17, $14, $14
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Deactivates the raft/vehicle by clearing its active flag.
; Called during level reset to ensure the raft starts inactive.
;
; Inputs: None
; Outputs: None
; Side Effects: Clears a_83A2 (raft active flag)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$847Ba9 00reset_raftlda#$00; A = 0 (inactive) ; x-ref: $4B13
$847D8d a2 83staraft_active; clear raft active flag
$848060rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes animation state for tile type 15. Computes pixel coordinates
; from the tile's column (X) and row (Y) positions, sets the initial
; animation frame from a ping-pong sequence table, and enables the
; animation flag so the per-frame updater (s_84C8) will animate it.
;
; Inputs: X = tile column, Y = tile row
; Outputs: X = tile column (preserved), Y = tile row (preserved)
; Side Effects: Writes animation state vars at $83A2-$83AA
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$848186 1einit_tile15_animstxzp_temp_x; save tile column ; x-ref: $4FE1
$848384 1fstyzp_temp_y; save tile row
$8485a9 fflda#$ff; $FF = animation enabled
$84878d a2 83staraft_active; $FF = enable raft animation
$848Aa9 00lda#$00; clear hi-byte for Y*8 calc
$848C85 1dstazp_ptr_map_hi
$848Ea5 1fldazp_temp_y; A = tile row
$84908d a6 83staraft_tile_row; Store tile row for screen addr calc
$84930aasla; row * 2
$84940aasla; row * 4
$84950aasla; row * 8
$849626 1drolzp_ptr_map_hi; capture carry into hi-byte
$849818clc
$849969 08adc#$08; row*8 + 8 = pixel Y offset
$849B8d a3 83staraft_pixel_y_lo; Pixel Y lo = row*8 + 8
$849Ea5 1dldazp_ptr_map_hi
$84A069 00adc#$00
$84A28d a4 83staraft_pixel_y_hi; Pixel Y hi (carry from lo)
$84A5a5 1eldazp_temp_x; A = tile column
$84A78d a7 83staraft_tile_col; Store column for screen addr calc
$84AA0aasla; col * 2
$84AB0aasla; col * 4
$84AC0aasla; col * 8
$84AD18clc
$84AE69 14adc#$14; col*8 + $14 = pixel X coord
$84B08d a5 83staraft_pixel_x; Pixel X = col*8 + $14
$84B3a2 02ldx#$02; start at frame index 2
$84B58e a9 83stxraft_anim_seq_idx; Start at sequence index 2
$84B8bd ab 83ldaraft_anim_bounce_tbl,x; Load initial frame from bounce table
$84BB8d a8 83staraft_anim_frame; Set initial animation frame
$84BEa9 00lda#$00; reset animation tick counter
$84C08d aa 83staraft_anim_tick; Reset tick counter
$84C3a6 1eldxzp_temp_x; restore tile column
$84C5a4 1fldyzp_temp_y; restore tile row
$84C760rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the raft's animation frame at a fixed rate. Every 8 ticks,
; the sequence index advances through an 8-entry bounce table to select
; the next animation frame, then tail-calls j_8528 to redraw the raft tiles.
; If the raft is inactive (raft_active == 0), returns immediately.
;
; Inputs: raft_active, raft_anim_tick, raft_anim_seq_idx, raft_anim_bounce_tbl
; Outputs: raft_anim_tick, raft_anim_seq_idx, raft_anim_frame
; Side Effects: Redraws raft tiles on screen via j_8528
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_raft_animation
$84C8ad a2 83ldaraft_active; Skip if raft is inactive ; x-ref: $61E6, $629B
$84CBd0 01bneb_84CE; Active: continue to animation update
$84CD60rts; Raft inactive: bail out
$84CEee aa 83b_84CEincraft_anim_tick; Increment animation tick ; x-ref: $84CB
$84D1ad aa 83ldaraft_anim_tick
$84D4c9 08cmp#$08; Every 8 ticks, advance frame
$84D6d0 1bbner_84F3; Not yet: return without frame change
$84D8a9 00lda#$00; Reset tick counter to 0
$84DA8d aa 83staraft_anim_tick
$84DDae a9 83ldxraft_anim_seq_idx; Advance to next sequence index
$84E0e8inx
$84E1e0 08cpx#$08; Wrap sequence index at 8 entries
$84E3d0 02bneb_84E7
$84E5a2 00ldx#$00; Wrap to first entry
$84E78e a9 83b_84E7stxraft_anim_seq_idx; Wrap index back to 0 ; x-ref: $84E3
$84EAbd ab 83ldaraft_anim_bounce_tbl,x; Look up frame from bounce pattern
$84ED8d a8 83staraft_anim_frame; Update current animation frame
$84F04c 28 85jmpj_8528; Tail-call: redraw raft tiles on screen
$84F360r_84F3rts; x-ref: $84D6
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the raft (tile 15) onto screen RAM and color RAM using the current
; animation frame. First clears the raft's 4x6 tile area in color RAM to
; red ($02), then copies the current animation frame's tile data from the
; raft tile table ($83B3+) to screen RAM, and finally copies corresponding
; attribute data to a secondary screen region.
;
; Inputs: raft_active, raft_tile_col, raft_tile_row, raft_anim_frame
; Outputs: None (screen updated in place)
; Side Effects: Writes 4x6 tile region to screen RAM and color RAM
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$84F4ad a2 83draw_raft_tilesldaraft_active; skip if raft not active ; x-ref: $4CC5
$84F7d0 01bneb_84FA
$84F960rts
$84FAac a7 83b_84FAldyraft_tile_col; Y = column index for screen row table ; x-ref: $84F7
$84FDb9 17 8cldarow_to_screen_lo,y; get screen row base addr lo-byte
$850018clc
$85016d a6 83adcraft_tile_row; add tile row offset
$850485 fbstazp_ptr_src_lo
$8506b9 28 8cldarow_to_screen_hi,y
$850969 d4adc#$d4; offset to color RAM ($D400)
$850B85 fcstazp_ptr_src_hi
$850Da2 05ldx#$05; 6 rows to fill
$850Fa0 07b_850Fldy#$07; 8 columns per row ; x-ref: $8526
$8511a9 02lda#VicIIColors.RED; color = red ($02)
$851391 fbb_8513sta(zp_ptr_src_lo),y; fill color RAM cell ; x-ref: $8516
$851588dey
$851610 fbbplb_8513
$8518a5 fbldazp_ptr_src_lo
$851A18clc
$851B69 28adc#$28; advance to next screen row (+40 cols)
$851D85 fbstazp_ptr_src_lo
$851Fa5 fcldazp_ptr_src_hi
$852169 00adc#$00
$852385 fcstazp_ptr_src_hi
$8525cadex
$8526d0 e7bneb_850F
$8528ac a7 83j_8528ldyraft_tile_col; --- part 2: draw raft tiles to screen --- ; x-ref: $84F0, $85C5
$852Bb9 17 8cldarow_to_screen_lo,y
$852E18clc
$852F6d a6 83adcraft_tile_row; add tile row offset to screen base
$853285 fbstazp_ptr_src_lo
$8534b9 28 8cldarow_to_screen_hi,y
$853769 00adc#$00
$853985 fcstazp_ptr_src_hi
$853Bad a8 83ldaraft_anim_frame; get current animation frame
$853E0aasla; frame * 4 (4 bytes per tile row)
$853F0aasla
$854018clc
$854169 b3adc#$b3; lo-byte of raft tile data ($83B3)
$854385 fdstazp_ptr_dst_lo
$8545a9 00lda#$00
$854769 83adc#$83; hi-byte of raft tile data ($83xx)
$854985 festazp_ptr_dst_hi
$854Ba2 05ldx#$05; 6 rows of tile data
$854Da0 03b_854Dldy#$03; 4 columns per tile row ; x-ref: $8571
$854Fb1 fdb_854Flda(zp_ptr_dst_lo),y; read tile data byte ; x-ref: $8554
$855191 fbsta(zp_ptr_src_lo),y; write to screen RAM
$855388dey
$855410 f9bplb_854F
$8556a5 fbldazp_ptr_src_lo
$855818clc
$855969 28adc#$28; next screen row (+40)
$855B85 fbstazp_ptr_src_lo
$855Da5 fcldazp_ptr_src_hi
$855F69 00adc#$00
$856185 fcstazp_ptr_src_hi
$8563a5 fdldazp_ptr_dst_lo
$856518clc
$856669 28adc#$28; next tile data row (+40)
$856885 fdstazp_ptr_dst_lo
$856Aa5 feldazp_ptr_dst_hi
$856C69 00adc#$00
$856E85 festazp_ptr_dst_hi
$8570cadex
$8571d0 dabneb_854D
$8573a5 fbldazp_ptr_src_lo; --- part 3: draw to secondary screen area ---
$857538sec
$8576e9 c4sbc#$c4; rewind src ptr by $C4 (back 5 rows-4)
$857885 fbstazp_ptr_src_lo
$857Aa5 fcldazp_ptr_src_hi
$857Ce9 00sbc#$00
$857E85 fcstazp_ptr_src_hi
$8580a5 fdldazp_ptr_dst_lo
$858238sec
$8583e9 b4sbc#$b4; rewind dst ptr by $B4 (back 4 rows+20)
$858585 fdstazp_ptr_dst_lo
$8587a5 feldazp_ptr_dst_hi
$8589e9 00sbc#$00
$858B85 festazp_ptr_dst_hi
$858Da2 05ldx#$05; 6 rows again
$858Fa0 03b_858Fldy#$03; 4 columns per row ; x-ref: $85B3
$8591b1 fdb_8591lda(zp_ptr_dst_lo),y; copy from secondary tile data ; x-ref: $8596
$859391 fbsta(zp_ptr_src_lo),y; write to secondary screen area
$859588dey
$859610 f9bplb_8591
$8598a5 fbldazp_ptr_src_lo
$859A18clc
$859B69 28adc#$28
$859D85 fbstazp_ptr_src_lo
$859Fa5 fcldazp_ptr_src_hi
$85A169 00adc#$00
$85A385 fcstazp_ptr_src_hi
$85A5a5 fdldazp_ptr_dst_lo
$85A718clc
$85A869 28adc#$28
$85AA85 fdstazp_ptr_dst_lo
$85ACa5 feldazp_ptr_dst_hi
$85AE69 00adc#$00
$85B085 festazp_ptr_dst_hi
$85B2cadex
$85B3d0 dabneb_858F
$85B560rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Deactivates the raft entity and redraws its screen area to remove it visually.
; Checks `raft_active`; if already inactive, returns immediately. Otherwise,
; clears the active flag and animation frame, then calls the raft tile redraw
; routine to restore the background tiles where the raft was displayed.
; Called during hero death/respawn to clean up the raft state.
;
; Inputs: None (reads raft_active global)
; Outputs: None
; Side Effects: Clears raft_active and raft_anim_frame; redraws raft screen area
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$85B6ad a2 83deactivate_raftldaraft_active; check if raft is currently active ; x-ref: $7B65
$85B9f0 0dbeqr_85C8; not active → nothing to do
$85BBa9 00lda#$00
$85BD8d a2 83staraft_active; deactivate the raft
$85C0a9 00lda#$00
$85C28d a8 83staraft_anim_frame; reset animation to frame 0
$85C54c 28 85jmpj_8528; redraw raft area to remove it from screen
$85C860r_85C8rts; x-ref: $85B9
; Dynamic tile ID / active flag (0=inactive). 3-byte array indexed by dynamic_tile_count.
$85C9dtile_id.byte$00, $00, $00; x-ref: $6FA9, $787C, $860C, $8652, $876E
; Dynamic tile type variant (0=normal 2x5 tile, nonzero=alternate rendering). 3-byte array.
$85CCdtile_type.byte$00, $00, $00; x-ref: $85F3, $8677, $8863
; Dynamic tile pixel Y position, low byte. Computed as (column * 8) + 8. 3-byte array.
$85CFdtile_y_lo.byte$00, $00, $00; x-ref: $6FB9, $701E, $789B, $78AE, $8622
; Dynamic tile pixel Y position, high byte. 3-byte array.
$85D2dtile_y_hi.byte$00, $00, $00; x-ref: $6FC1, $7024, $78A2, $78B6, $8629
; Dynamic tile pixel X position. Computed as (row * 8) + $14. 3-byte array.
$85D5dtile_x.byte$00, $00, $00; x-ref: $6FAE, $702A, $7885, $8637
; Dynamic tile screen column offset. Used as Y-index for indirect screen writes. 3-byte array.
$85D8dtile_scr_col.byte$00, $00, $00; x-ref: $8617, $8673, $8795, $885F
; Dynamic tile screen row index. Indexes into row_to_screen_lo/hi tables. 3-byte array.
$85DBdtile_scr_row.byte$00, $00, $00; x-ref: $862E, $865E, $8773, $8851
; Dynamic tile hit countdown timer, low byte. Init=$12. When timer reaches 0, tile is destroyed. 3-byte array.
$85DEdtile_timer_lo.byte$00, $00, $00; x-ref: $863C, $881E, $8826, $8829, $8841
; Dynamic tile hit countdown timer, high byte. Init=$02. Forms 16-bit timer with dtile_timer_lo ($0212=530 frames). 3-byte array.
$85E1dtile_timer_hi.byte$00, $00, $00; x-ref: $8641, $8823, $882C, $883A
$85E4dynamic_tile_count.byte$00; x-ref: $6FA2, $7875, $85E8, $85F0, $8603, ...
; Dynamic tile color attribute for color RAM. $0F=white (normal), $00=invisible/cleared.
$85E5dtile_color.byte$00; x-ref: $4E2A, $4E65, $8680, $868A, $8698, ...
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the dynamic tile count to zero, clearing all tile 5/tile 6 objects.
; Called during room initialization to prepare the dynamic tile subsystem
; for the new room. The count (a_85E4) is used as an index into parallel
; arrays (f_85C9–f_85E1) that track active dynamic tile state.
;
; Inputs: None
; Outputs: None
; Side Effects: Clears dynamic_tile_count (a_85E4) to 0
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$85E6a9 00reset_dynamic_tileslda#$00; clear accumulator ; x-ref: $4B16
$85E88d e4 85stadynamic_tile_count; reset dynamic tile count to 0
$85EB60rts
$85EC86 1ej_85ECstxzp_temp_x; x-ref: $4FBC, $4FC5
$85EE84 1fstyzp_temp_y
$85F0ae e4 85ldxdynamic_tile_count
$85F39d cc 85stadtile_type,x
$85F6ac 9e 3fldycurrent_room_index
$85F9b9 09 49ldalevel_map_ptr_lo,y
$85FC85 1cstazp_ptr_map_lo
$85FEb9 69 49ldalevel_map_ptr_hi,y
$860185 1dstazp_ptr_map_hi
$8603ad e4 85ldadynamic_tile_count
$860618clc
$860769 0fadc#$0f
$8609a8tay
$860Ab1 1clda(zp_ptr_map_lo),y
$860C9d c9 85stadtile_id,x; Load tile ID from map data
$860Ff0 33beqb_8644
$8611a9 00lda#$00
$861385 1dstazp_ptr_map_hi
$8615a5 1fldazp_temp_y
$86179d d8 85stadtile_scr_col,x; Store screen column
$861A0aasla
$861B0aasla
$861C0aasla
$861D26 1drolzp_ptr_map_hi
$861F18clc
$862069 08adc#$08
$86229d cf 85stadtile_y_lo,x; Store pixel Y lo (col*8+8)
$8625a5 1dldazp_ptr_map_hi
$862769 00adc#$00
$86299d d2 85stadtile_y_hi,x; Store pixel Y hi
$862Ca5 1eldazp_temp_x
$862E9d db 85stadtile_scr_row,x; Store screen row index
$86310aasla
$86320aasla
$86330aasla
$863418clc
$863569 14adc#$14
$86379d d5 85stadtile_x,x; Store pixel X (row*8+$14)
$863Aa9 12lda#$12
$863C9d de 85stadtile_timer_lo,x; Init timer lo = $12
$863Fa9 02lda#$02
$86419d e1 85stadtile_timer_hi,x; Init timer hi = $02
$8644ee e4 85b_8644incdynamic_tile_count; x-ref: $860F
$8647a6 1eldxzp_temp_x
$8649a4 1fldyzp_temp_y
$864B60rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Iterates over all dynamic tiles (from `dynamic_tile_count` down to 0).
; For each active tile (where `dtile_id` != 0), it calls `draw_dynamic_tile_2x5`
; to draw it onto the screen.
;
; Inputs: `dynamic_tile_count` (global)
; Outputs: None
; Side Effects: Modifies screen and color RAM by drawing the active dynamic tiles.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_all_dynamic_tiles
$864Cae e4 85ldxdynamic_tile_count; Load total number of dynamic tiles ; x-ref: $4CC8
$864Ff0 0cbeqr_865D; If zero, exit
$8651cadex; Adjust count to 0-based index for X
$8652bd c9 85b_8652ldadtile_id,x; Check if tile is active (id != 0) ; x-ref: $865B
$8655f0 03beqb_865A; If not active, skip drawing
$865720 5e 86jsrdraw_dynamic_tile_2x5
$865Acab_865Adex; Decrement index ; x-ref: $8655
$865B10 f5bplb_8652; Loop until all tiles processed (X < 0)
$865D60r_865Drts; x-ref: $864F
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws a 2x5 meta-tile on screen for a dynamic tile. Uses `dtile_type` to select
; the character set and color (either dynamic `dtile_color` or fixed red).
;
; Inputs: X = dynamic tile index
; Outputs: None
; Side Effects: Modifies screen and color RAM (10 bytes each)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_dynamic_tile_2x5
$865Ebd db 85ldadtile_scr_row,x; Get screen row for tile ; x-ref: $8657
$8661a8tay
$8662b9 17 8cldarow_to_screen_lo,y
$866585 fbstazp_ptr_src_lo; zp_ptr_src = screen RAM address
$866785 fdstazp_ptr_dst_lo
$8669b9 28 8cldarow_to_screen_hi,y
$866C85 fcstazp_ptr_src_hi
$866E18clc; Add $D4 to high byte for Color RAM
$866F69 d4adc#$d4
$867185 festazp_ptr_dst_hi
$8673bd d8 85ldadtile_scr_col,x; Get screen col for tile
$8676a8tay
$8677bd cc 85ldadtile_type,x
$867Ad0 74bnedraw_dtile_type_1; If type != 0, draw alternate tile
$867Ca9 34lda#$34; Row 1
$867E91 fbsta(zp_ptr_src_lo),y
$8680ad e5 85ldadtile_color
$868391 fdsta(zp_ptr_dst_lo),y
$8685c8iny
$8686a9 35lda#$35
$868891 fbsta(zp_ptr_src_lo),y
$868Aad e5 85ldadtile_color
$868D91 fdsta(zp_ptr_dst_lo),y
$868F98tya
$869018clc
$869169 27adc#$27
$8693a8tay
$8694a9 36lda#$36
$869691 fbsta(zp_ptr_src_lo),y
$8698ad e5 85ldadtile_color
$869B91 fdsta(zp_ptr_dst_lo),y
$869Dc8iny
$869Ea9 37lda#$37
$86A091 fbsta(zp_ptr_src_lo),y
$86A2ad e5 85ldadtile_color
$86A591 fdsta(zp_ptr_dst_lo),y
$86A798tya
$86A818clc
$86A969 27adc#$27
$86ABa8tay
$86ACa9 34lda#$34
$86AE91 fbsta(zp_ptr_src_lo),y
$86B0ad e5 85ldadtile_color
$86B391 fdsta(zp_ptr_dst_lo),y
$86B5c8iny
$86B6a9 35lda#$35
$86B891 fbsta(zp_ptr_src_lo),y
$86BAad e5 85ldadtile_color
$86BD91 fdsta(zp_ptr_dst_lo),y
$86BF98tya
$86C018clc
$86C169 27adc#$27
$86C3a8tay
$86C4a9 36lda#$36
$86C691 fbsta(zp_ptr_src_lo),y
$86C8ad e5 85ldadtile_color
$86CB91 fdsta(zp_ptr_dst_lo),y
$86CDc8iny
$86CEa9 37lda#$37
$86D091 fbsta(zp_ptr_src_lo),y
$86D2ad e5 85ldadtile_color
$86D591 fdsta(zp_ptr_dst_lo),y
$86D798tya
$86D818clc
$86D969 27adc#$27
$86DBa8tay
$86DCa9 34lda#$34
$86DE91 fbsta(zp_ptr_src_lo),y
$86E0ad e5 85ldadtile_color
$86E391 fdsta(zp_ptr_dst_lo),y
$86E5c8iny
$86E6a9 35lda#$35
$86E891 fbsta(zp_ptr_src_lo),y
$86EAad e5 85ldadtile_color
$86ED91 fdsta(zp_ptr_dst_lo),y
$86EF60rts
$86F0a9 0ddraw_dtile_type_1lda#$0d; Row 1, alternate tile ; x-ref: $867A
$86F291 fbsta(zp_ptr_src_lo),y
$86F4a9 02lda#$02
$86F691 fdsta(zp_ptr_dst_lo),y
$86F8c8iny
$86F9a9 0elda#$0e
$86FB91 fbsta(zp_ptr_src_lo),y
$86FDa9 02lda#$02
$86FF91 fdsta(zp_ptr_dst_lo),y
$870198tya
$870218clc
$870369 27adc#$27
$8705a8tay
$8706a9 0flda#$0f
$870891 fbsta(zp_ptr_src_lo),y
$870Aa9 02lda#$02
$870C91 fdsta(zp_ptr_dst_lo),y
$870Ec8iny
$870Fa9 10lda#$10
$871191 fbsta(zp_ptr_src_lo),y
$8713a9 02lda#$02
$871591 fdsta(zp_ptr_dst_lo),y
$871798tya
$871818clc
$871969 27adc#$27
$871Ba8tay
$871Ca9 0dlda#$0d
$871E91 fbsta(zp_ptr_src_lo),y
$8720a9 02lda#$02
$872291 fdsta(zp_ptr_dst_lo),y
$8724c8iny
$8725a9 0elda#$0e
$872791 fbsta(zp_ptr_src_lo),y
$8729a9 02lda#$02
$872B91 fdsta(zp_ptr_dst_lo),y
$872D98tya
$872E18clc
$872F69 27adc#$27
$8731a8tay
$8732a9 0flda#$0f
$873491 fbsta(zp_ptr_src_lo),y
$8736a9 02lda#$02
$873891 fdsta(zp_ptr_dst_lo),y
$873Ac8iny
$873Ba9 10lda#$10
$873D91 fbsta(zp_ptr_src_lo),y
$873Fa9 02lda#$02
$874191 fdsta(zp_ptr_dst_lo),y
$874398tya
$874418clc
$874569 27adc#$27
$8747a8tay
$8748a9 0dlda#$0d
$874A91 fbsta(zp_ptr_src_lo),y
$874Ca9 02lda#$02
$874E91 fdsta(zp_ptr_dst_lo),y
$8750c8iny
$8751a9 0elda#$0e
$875391 fbsta(zp_ptr_src_lo),y
$8755a9 02lda#$02
$875791 fdsta(zp_ptr_dst_lo),y
$875960rts
$875Aac 9e 3fj_875Aldycurrent_room_index; x-ref: $6FCD, $6FD8, $8831
$875Db9 09 49ldalevel_map_ptr_lo,y
$876085 1cstazp_ptr_map_lo
$8762b9 69 49ldalevel_map_ptr_hi,y
$876585 1dstazp_ptr_map_hi
$87678atxa
$876818clc
$876969 0fadc#$0f
$876Ba8tay
$876Ca9 00lda#$00
$876E9d c9 85stadtile_id,x; Tile active? (update loop)
$877191 1csta(zp_ptr_map_lo),y
$8773bd db 85ldadtile_scr_row,x; Get screen row for rendering
$8776a8tay
$8777b9 17 8cldarow_to_screen_lo,y
$877A85 fbstazp_ptr_src_lo
$877C85 fdstazp_ptr_dst_lo
$877Eb9 28 8cldarow_to_screen_hi,y
$878185 fcstazp_ptr_src_hi
$878318clc
$878469 d4adc#$d4
$878685 festazp_ptr_dst_hi
$8788a5 fbldazp_ptr_src_lo
$878A18clc
$878B69 68adc#$68
$878D85 02stazp_work0
$878Fa5 fcldazp_ptr_src_hi
$879169 dfadc#$df
$879385 03stazp_work1
$8795bd d8 85ldadtile_scr_col,x; Get screen column for rendering
$8798a8tay
$8799b1 02lda(zp_work0),y
$879B91 fbsta(zp_ptr_src_lo),y
$879Dad a3 3fldaactive_bg_color
$87A091 fdsta(zp_ptr_dst_lo),y
$87A2c8iny
$87A3b1 02lda(zp_work0),y
$87A591 fbsta(zp_ptr_src_lo),y
$87A7ad a3 3fldaactive_bg_color
$87AA91 fdsta(zp_ptr_dst_lo),y
$87AC98tya
$87AD18clc
$87AE69 27adc#$27
$87B0a8tay
$87B1b1 02lda(zp_work0),y
$87B391 fbsta(zp_ptr_src_lo),y
$87B5ad a3 3fldaactive_bg_color
$87B891 fdsta(zp_ptr_dst_lo),y
$87BAc8iny
$87BBb1 02lda(zp_work0),y
$87BD91 fbsta(zp_ptr_src_lo),y
$87BFad a3 3fldaactive_bg_color
$87C291 fdsta(zp_ptr_dst_lo),y
$87C498tya
$87C518clc
$87C669 27adc#$27
$87C8a8tay
$87C9b1 02lda(zp_work0),y
$87CB91 fbsta(zp_ptr_src_lo),y
$87CDad a3 3fldaactive_bg_color
$87D091 fdsta(zp_ptr_dst_lo),y
$87D2c8iny
$87D3b1 02lda(zp_work0),y
$87D591 fbsta(zp_ptr_src_lo),y
$87D7ad a3 3fldaactive_bg_color
$87DA91 fdsta(zp_ptr_dst_lo),y
$87DC98tya
$87DD18clc
$87DE69 27adc#$27
$87E0a8tay
$87E1b1 02lda(zp_work0),y
$87E391 fbsta(zp_ptr_src_lo),y
$87E5ad a3 3fldaactive_bg_color
$87E891 fdsta(zp_ptr_dst_lo),y
$87EAc8iny
$87EBb1 02lda(zp_work0),y
$87ED91 fbsta(zp_ptr_src_lo),y
$87EFad a3 3fldaactive_bg_color
$87F291 fdsta(zp_ptr_dst_lo),y
$87F498tya
$87F518clc
$87F669 27adc#$27
$87F8a8tay
$87F9b1 02lda(zp_work0),y
$87FB91 fbsta(zp_ptr_src_lo),y
$87FDad a3 3fldaactive_bg_color
$880091 fdsta(zp_ptr_dst_lo),y
$8802c8iny
$8803b1 02lda(zp_work0),y
$880591 fbsta(zp_ptr_src_lo),y
$8807ad a3 3fldaactive_bg_color
$880A91 fdsta(zp_ptr_dst_lo),y
$880C20 0f 70jsrstart_explosion_from_hit
$880Fa9 75lda#$75
$881185 fbstazp_ptr_src_lo
$8813a9 00lda#$00
$881585 fcstazp_ptr_src_hi
$8817a9 00lda#$00
$881985 fdstazp_ptr_dst_lo
$881B4c d8 51jmpj_51D8
$881Ebd de 85j_881Eldadtile_timer_lo,x; Check timer lo (hit countdown) ; x-ref: $78D4
$8821d0 03bneb_8826
$8823de e1 85decdtile_timer_hi,x
$8826de de 85b_8826decdtile_timer_lo,x; Decrement timer lo ; x-ref: $8821
$8829bd de 85ldadtile_timer_lo,x
$882C1d e1 85oradtile_timer_hi,x; Timer expired? Destroy tile
$882Fd0 09bneb_883A
$883120 5a 87jsrj_875A
$8834a9 01lda#$01
$88368d ec 88stasfx_hit_state
$883960rts
$883Abd e1 85b_883Aldadtile_timer_hi,x; x-ref: $882F
$883Dc9 01cmp#$01
$883Fd0 0fbner_8850
$8841bd de 85ldadtile_timer_lo,x
$8844c9 18cmp#$18
$8846d0 08bner_8850
$884820 51 88jsrdraw_dtile_anim_frame2
$884Ba9 01lda#$01
$884D8d cf 8bstasfx_ch8_state
$885060r_8850rts; x-ref: $883F, $8846
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the second animation frame for a dynamic tile onto the screen.
; Unlike the initial draw, this routine only updates Screen RAM (characters)
; and skips Color RAM updates to save cycles.
;
; Inputs: X = dynamic tile index
; Outputs: None
; Side Effects: Modifies screen RAM (10 bytes)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_dtile_anim_frame2
$8851bd db 85ldadtile_scr_row,x; Get screen row for tile ; x-ref: $8848
$8854a8tay
$8855b9 17 8cldarow_to_screen_lo,y
$885885 fbstazp_ptr_src_lo; zp_ptr_src = screen RAM address
$885Ab9 28 8cldarow_to_screen_hi,y
$885D85 fcstazp_ptr_src_hi
$885Fbd d8 85ldadtile_scr_col,x; Get screen col for tile
$8862a8tay
$8863bd cc 85ldadtile_type,x
$8866d0 42bnedraw_dtile_type_1_anim2; If type != 0, draw alternate tile frame 2
$8868a9 38lda#$38; Row 1, characters $38/$39
$886A91 fbsta(zp_ptr_src_lo),y
$886Cc8iny
$886Da9 39lda#$39
$886F91 fbsta(zp_ptr_src_lo),y
$887198tya
$887218clc
$887369 27adc#$27
$8875a8tay
$8876a9 3alda#$3a
$887891 fbsta(zp_ptr_src_lo),y
$887Ac8iny
$887Ba9 3blda#$3b
$887D91 fbsta(zp_ptr_src_lo),y
$887F98tya
$888018clc
$888169 27adc#$27
$8883a8tay
$8884a9 38lda#$38
$888691 fbsta(zp_ptr_src_lo),y
$8888c8iny
$8889a9 39lda#$39
$888B91 fbsta(zp_ptr_src_lo),y
$888D98tya
$888E18clc
$888F69 27adc#$27
$8891a8tay
$8892a9 3alda#$3a
$889491 fbsta(zp_ptr_src_lo),y
$8896c8iny
$8897a9 3blda#$3b
$889991 fbsta(zp_ptr_src_lo),y
$889B98tya
$889C18clc
$889D69 27adc#$27
$889Fa8tay
$88A0a9 38lda#$38
$88A291 fbsta(zp_ptr_src_lo),y
$88A4c8iny
$88A5a9 39lda#$39
$88A791 fbsta(zp_ptr_src_lo),y
$88A960rts
draw_dtile_type_1_anim2
$88AAa9 11lda#$11; Row 1, alternate tile frame 2 ; x-ref: $8866
$88AC91 fbsta(zp_ptr_src_lo),y
$88AEc8iny
$88AFa9 12lda#$12
$88B191 fbsta(zp_ptr_src_lo),y
$88B398tya
$88B418clc
$88B569 27adc#$27
$88B7a8tay
$88B8a9 13lda#$13
$88BA91 fbsta(zp_ptr_src_lo),y
$88BCc8iny
$88BDa9 12lda#$12
$88BF91 fbsta(zp_ptr_src_lo),y
$88C198tya
$88C218clc
$88C369 27adc#$27
$88C5a8tay
$88C6a9 11lda#$11
$88C891 fbsta(zp_ptr_src_lo),y
$88CAc8iny
$88CBa9 12lda#$12
$88CD91 fbsta(zp_ptr_src_lo),y
$88CF98tya
$88D018clc
$88D169 27adc#$27
$88D3a8tay
$88D4a9 13lda#$13
$88D691 fbsta(zp_ptr_src_lo),y
$88D8c8iny
$88D9a9 12lda#$12
$88DB91 fbsta(zp_ptr_src_lo),y
$88DD98tya
$88DE18clc
$88DF69 27adc#$27
$88E1a8tay
$88E2a9 11lda#$11
$88E491 fbsta(zp_ptr_src_lo),y
$88E6c8iny
$88E7a9 12lda#$12
$88E991 fbsta(zp_ptr_src_lo),y
$88EB60rts
$88ECsfx_hit_state.byte$00; x-ref: $1E57, $627F, $6F9E, $8836, $88EE, ...
$88EDsfx_hit_delay.byte$00; x-ref: $88FA, $890C, $892E
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks the hit/destroy noise sound effect on SID Voice 3.
; Uses a 3-state machine driven by sfx_hit_state:
; State 0: Idle — returns immediately.
; State 1: Init — sets delay counter to $FE, silences Voice 3, advances to state 2.
; State 2: Delay — increments counter until it reaches 0, then triggers a noise
; burst (freq=$0300, ADSR=$10/$F9, waveform=noise+gate) for 3 frames
; before gating off and resetting to idle.
; Triggered by entity destruction, explosion activation, and life bonus scoring.
;
; Inputs: sfx_hit_state ($88EC), sfx_hit_delay ($88ED)
; Outputs: sfx_hit_state, sfx_hit_delay
; Side Effects: Writes to SID Voice 3 registers ($D40E-$D414)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$88EEad ec 88tick_sfx_hitldasfx_hit_state; Load current state (0=idle, 1=init, 2=delay) ; x-ref: $1E3A
$88F1d0 01bneb_88F4; State 0 → nothing to do
$88F360rts
$88F4c9 01b_88F4cmp#$01; State 1? → initialize voice ; x-ref: $88F1
$88F6d0 14bneb_890C
$88F8a9 felda#$fe; $FE = -2, counts up to 0 (2-frame delay)
$88FA8d ed 88stasfx_hit_delay
$88FDa9 00lda#$00; Silence Voice 3: clear waveform
$88FF8d 12 d4sta$d412; Voice 3: Control Register
$89028d 13 d4sta$d413; Zero attack/decay; Voice 3: Attack / Decay Cycle Control
$89058d 14 d4sta$d414; Zero sustain/release; Voice 3: Sustain / Release Cycle Control
$8908ee ec 88incsfx_hit_state; Advance to state 2 (delay phase)
$890B60rts
$890Cee ed 88b_890Cincsfx_hit_delay; State 2: increment delay counter ; x-ref: $88F6
$890F10 01bplb_8912; Counter < 0 → still waiting
$891160rts
$8912d0 1ab_8912bneb_892E; Counter > 0 → check for gate-off ; x-ref: $890F
$8914a9 03lda#$03; Counter == 0: trigger noise burst
$89168d 0f d4sta$d40f; Freq hi=$03 → low-pitched noise; Voice 3: Frequency Control - High-Byte
$8919a9 00lda#$00; Freq lo=$00
$891B8d 0e d4sta$d40e; Voice 3: Frequency Control - Low-Byte
$891Ea9 10lda#$10; Attack=1, Decay=0 (fast attack)
$89208d 13 d4sta$d413; Voice 3: Attack / Decay Cycle Control
$8923a9 f9lda#$f9; Sustain=$F, Release=$9
$89258d 14 d4sta$d414; Voice 3: Sustain / Release Cycle Control
$8928a9 81lda#$81; $81 = noise waveform + gate on
$892A8d 12 d4sta$d412; Voice 3: Control Register
$892D60rts
$892Ead ed 88b_892Eldasfx_hit_delay; Counter > 0: check if time to gate off ; x-ref: $8912
$8931c9 03cmp#$03; Wait 3 frames after trigger
$8933d0 0abner_893F
$8935a9 80lda#$80; $80 = noise waveform, gate OFF
$89378d 12 d4sta$d412; Voice 3: Control Register
$893Aa9 00lda#$00; Reset to state 0 (idle)
$893C8d ec 88stasfx_hit_state
$893F60r_893Frts; x-ref: $8933
$8940sfx_explosion_state.byte$00; x-ref: $1E5A, $706B, $8942, $895C, $8984
$8941sfx_explosion_delay.byte$00; x-ref: $894E, $8960
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks the explosion/blast noise sound effect on SID Voice 3.
; Uses a 3-state machine driven by sfx_explosion_state:
; State 0: Idle — returns immediately.
; State 1: Init — sets delay counter to $FE, silences Voice 3, advances to state 2.
; State 2: Delay — increments counter until it reaches 0, then triggers a noise
; burst (freq=$3000, ADSR=$61/$10, waveform=noise+gate) and resets to idle.
; Triggered by init_hero_explosion when the hero's dynamite detonates.
;
; Inputs: sfx_explosion_state ($8940), sfx_explosion_delay ($8941)
; Outputs: sfx_explosion_state, sfx_explosion_delay
; Side Effects: Writes to SID Voice 3 registers ($D40E-$D414)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8942ad 40 89tick_sfx_explosionldasfx_explosion_state; Load current state (0=idle, 1=init, 2=delay) ; x-ref: $1E3D
$8945d0 01bneb_8948; State 0 → nothing to do
$894760rts
$8948c9 01b_8948cmp#$01; State 1? → initialize voice ; x-ref: $8945
$894Ad0 14bneb_8960
$894Ca9 felda#$fe; $FE = -2, counts up to 0 (2-frame delay)
$894E8d 41 89stasfx_explosion_delay
$8951a9 00lda#$00
$89538d 12 d4sta$d412; Clear waveform (silence Voice 3); Voice 3: Control Register
$89568d 13 d4sta$d413; Clear ADSR attack/decay; Voice 3: Attack / Decay Cycle Control
$89598d 14 d4sta$d414; Clear ADSR sustain/release; Voice 3: Sustain / Release Cycle Control
$895Cee 40 89incsfx_explosion_state; Advance to state 2 (delay phase)
$895F60rts
$8960ee 41 89b_8960incsfx_explosion_delay; Increment delay counter ; x-ref: $894A
$896310 01bplb_8966; Counter >= 0? → ready to proceed
$896560rts; Counter still negative → keep waiting
$8966d0 1ab_8966bneb_8982; Counter nonzero (overflow) → reset to idle ; x-ref: $8963
$8968a9 30lda#$30; Freq hi = $30 → frequency $3000
$896A8d 0f d4sta$d40f; Freq hi = $30 → frequency $3000; Voice 3: Frequency Control - High-Byte
$896Da9 00lda#$00
$896F8d 0e d4sta$d40e; Voice 3: Frequency Control - Low-Byte
$8972a9 61lda#$61; Attack=6, Decay=1
$89748d 13 d4sta$d413; Voice 3: Attack / Decay Cycle Control
$8977a9 10lda#$10
$89798d 14 d4sta$d414; Sustain=1, Release=0; Voice 3: Sustain / Release Cycle Control
$897Ca9 81lda#$81; $81 = Noise waveform + Gate on → trigger burst
$897E8d 12 d4sta$d412; Voice 3: Control Register
$898160rts
$8982a9 00b_8982lda#$00; Reset state to 0 (idle) — effect complete ; x-ref: $8966
$89848d 40 89stasfx_explosion_state
$898760rts
$8988sfx_fire_phase.byte$00; x-ref: $1E5D, $790E, $8992, $89AC, $89DF, ...
$8989sfx_fire_timer.byte$00; x-ref: $899E, $89B0, $89D8, $89F1
$898Asfx_fire_freq_table.byte$1b, $1c, $1e, $20, $22, $24, $26, $28; x-ref: $89B8, $89F4
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Tick SFX: Fire sound effect (SID Voice 2).
; Plays a rising-pitch pulse wave sweep to produce a "fire/shoot" sound.
;
; Phase 0: Idle, returns immediately.
; Phase 1: Init — clears Voice 2 registers, starts 2-tick delay.
; Phase 2: Plays the effect — sweeps through 8 frequency values with
; increasing pulse width. After one sweep, loops or silences.
; Phase 3: Final — silences Voice 2 and resets to idle.
;
; Inputs: sfx_fire_phase ($8988) = effect state (0=idle, 1=trigger, 2=playing, 3=stop)
; Outputs: SID Voice 2 registers ($D407–$D40D)
; Side Effects: Modifies SID Voice 2 output (frequency, pulse width, ADSR, gate)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8992ad 88 89tick_sfx_fireldasfx_fire_phase; Check SFX phase (0=idle) ; x-ref: $1E40
$8995d0 01bneb_8998; Phase 0? → return (no SFX active)
$899760rts
$8998c9 01b_8998cmp#$01; Phase 1? → initialize Voice 2 ; x-ref: $8995
$899Ad0 14bneb_89B0
$899Ca9 felda#$fe; Timer = -2 (2-tick init delay)
$899E8d 89 89stasfx_fire_timer
$89A1a9 00lda#$00; Clear Voice 2: gate off
$89A38d 0b d4sta$d40b; Voice 2: Control Register
$89A68d 0c d4sta$d40c; Voice 2: Attack / Decay Cycle Control
$89A98d 0d d4sta$d40d; Voice 2: Sustain / Release Cycle Control
$89ACee 88 89incsfx_fire_phase; Advance to phase 2 (playing)
$89AF60rts
$89B0ee 89 89b_89B0incsfx_fire_timer; Increment frame timer ; x-ref: $899A
$89B310 01bplb_89B6; Still negative? → wait (init delay)
$89B560rts
$89B6d0 20b_89B6bneb_89D8; Timer > 0? → sweep phase ; x-ref: $89B3
$89B8ad 8a 89ldasfx_fire_freq_table; Load first freq from table
$89BB8d 08 d4sta$d408; Voice 2: Frequency Control - High-Byte
$89BEa9 00lda#$00
$89C08d 07 d4sta$d407; Voice 2: Frequency Control - Low-Byte
$89C3a9 08lda#$08; Pulse width high nybble = $08
$89C58d 0a d4sta$d40a; Voice 2: Pulse Waveform Width - High-Nybble
$89C8a9 10lda#$10; Attack=1, Decay=0
$89CA8d 0c d4sta$d40c; Voice 2: Attack / Decay Cycle Control
$89CDa9 40lda#$40; Sustain=$4, Release=0
$89CF8d 0d d4sta$d40d; Voice 2: Sustain / Release Cycle Control
$89D2a9 41lda#$41; Pulse waveform + gate on
$89D48d 0b d4sta$d40b; Voice 2: Control Register
$89D760rts
$89D8ae 89 89b_89D8ldxsfx_fire_timer; X = current timer position ; x-ref: $89B6
$89DBe0 08cpx#$08; Reached end of sweep (8 steps)?
$89DDd0 15bneb_89F4
$89DFad 88 89ldasfx_fire_phase; Check if phase = 3 (stop requested)
$89E2c9 03cmp#$03
$89E4d0 09bneb_89EF
$89E6a9 00lda#$00; Silence Voice 2 and reset to idle
$89E88d 0b d4sta$d40b; Voice 2: Control Register
$89EB8d 88 89stasfx_fire_phase
$89EE60rts
$89EFa2 00b_89EFldx#$00; Reset timer for another sweep cycle ; x-ref: $89E4
$89F18e 89 89stxsfx_fire_timer
$89F4bd 8a 89b_89F4ldasfx_fire_freq_table,x; Load freq[X] from sweep table ; x-ref: $89DD
$89F78d 08 d4sta$d408; Voice 2: Frequency Control - High-Byte
$89FA8atxa; Pulse width = X * 256 + $800 (rising)
$89FB18clc
$89FC69 08adc#$08
$89FE8d 0a d4sta$d40a; Voice 2: Pulse Waveform Width - High-Nybble
$8A0160rts
$8A02ad 88 89j_8A02ldasfx_fire_phase; x-ref: $791C
$8A05c9 01cmp#$01
$8A07d0 09bneb_8A12
$8A09a9 00lda#$00
$8A0B8d 0b d4sta$d40b; Voice 2: Control Register
$8A0E8d 88 89stasfx_fire_phase
$8A1160rts
$8A12c9 02b_8A12cmp#$02; x-ref: $8A07
$8A14d0 03bner_8A19
$8A16ee 88 89incsfx_fire_phase
$8A1960r_8A19rts; x-ref: $8A14
$8A1Asfx_ch4_state.byte$00; x-ref: $1E60, $81A4, $8A25, $8A42, $8A8C
$8A1Bsfx_ch4_delay.byte$00; x-ref: $8A31, $8A46, $8A6F, $8A78
$8A1Csfx_ch4_freq_step.byte$00; x-ref: $8A3F, $8A7B, $8A7E
$8A1Dsfx_ch4_freq_tbl.byte$30, $20, $10, $00; x-ref: $8A4E, $8A90
$8A21sfx_ch4_pulse_tbl.byte$06, $04, $02, $00; x-ref: $8A59, $8A96
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Tick SFX channel 4 on SID Voice 1. State machine with two phases:
; State 1: Initialize voice — gate off, clear ADSR, reset frequency step.
; State 2+: Cycle through 4-entry frequency/pulse-width tables, producing a
; descending pitch sweep. Each sweep iteration decrements freq/pulse values;
; after 15 full sweeps the effect ends and the channel is silenced.
;
; Inputs: sfx_ch4_state ($8A1A), sfx_ch4_delay ($8A1B), sfx_ch4_freq_step ($8A1C)
; Outputs: None
; Side Effects: Writes SID Voice 1 registers $D400-$D406
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8A25ad 1a 8atick_sfx_ch4_voice1ldasfx_ch4_state; Check if channel is active ; x-ref: $1E43
$8A28d0 01bneb_8A2B; Inactive (state=0), exit
$8A2A60rts
$8A2Bc9 01b_8A2Bcmp#$01; State 1 = init phase? ; x-ref: $8A28
$8A2Dd0 17bneb_8A46
$8A2Fa9 felda#$fe; Delay counter = -2 (wait 2 frames before playing)
$8A318d 1b 8astasfx_ch4_delay
$8A34a9 00lda#$00; Gate off Voice 1, clear ADSR
$8A368d 04 d4sta$d404; Voice 1: Control Register
$8A398d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8A3C8d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8A3F8d 1c 8astasfx_ch4_freq_step; Reset frequency table index
$8A42ee 1a 8aincsfx_ch4_state; Advance to state 2 (playing)
$8A4560rts
$8A46ee 1b 8ab_8A46incsfx_ch4_delay; Increment delay counter ; x-ref: $8A2D
$8A4910 01bplb_8A4C; Still negative? Wait more frames
$8A4B60rts
$8A4Cd0 21b_8A4Cbneb_8A6F; Delay=0: start new sweep iteration ; x-ref: $8A49
$8A4Ead 1d 8aldasfx_ch4_freq_tbl; Load initial freq hi from table[0]=$30
$8A518d 01 d4sta$d401; Voice 1: Frequency Control - High-Byte
$8A54a9 00lda#$00
$8A568d 00 d4sta$d400; Voice 1: Frequency Control - Low-Byte
$8A59ad 21 8aldasfx_ch4_pulse_tbl; Load initial pulse width hi from table[0]=$06
$8A5C8d 03 d4sta$d403; Voice 1: Pulse Waveform Width - High-Nybble
$8A5Fa9 1alda#$1a; Attack=1, Decay=10
$8A618d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8A64a9 00lda#$00; Sustain=0, Release=0 (sharp decay)
$8A668d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8A69a9 41lda#$41; Pulse waveform + gate on
$8A6B8d 04 d4sta$d404; Voice 1: Control Register
$8A6E60rts
$8A6Fae 1b 8ab_8A6Fldxsfx_ch4_delay; Get current delay (also table index) ; x-ref: $8A4C
$8A72e0 04cpx#$04; Finished all 4 table entries?
$8A74d0 1abneb_8A90
$8A76a2 00ldx#$00; Reset table index to 0
$8A788e 1b 8astxsfx_ch4_delay
$8A7Bee 1c 8aincsfx_ch4_freq_step; Next sweep iteration
$8A7Ead 1c 8aldasfx_ch4_freq_step
$8A81c9 0fcmp#$0f; Done 15 sweeps? End effect
$8A83d0 0bbneb_8A90
$8A85a9 40lda#$40; Gate off (pulse waveform, no gate)
$8A878d 04 d4sta$d404; Voice 1: Control Register
$8A8Aa9 00lda#$00
$8A8C8d 1a 8astasfx_ch4_state; Deactivate channel (state=0)
$8A8F60rts
$8A90bd 1d 8ab_8A90ldasfx_ch4_freq_tbl,x; Update freq/pulse from table[X] ; x-ref: $8A74, $8A83
$8A938d 01 d4sta$d401; Voice 1: Frequency Control - High-Byte
$8A96bd 21 8aldasfx_ch4_pulse_tbl,x
$8A998d 03 d4sta$d403; Voice 1: Pulse Waveform Width - High-Nybble
$8A9C60rts
$8A9Dsfx_ch5_state.byte$00; x-ref: $1E63, $621D, $8A9F, $8AB9, $8AEC
$8A9Esfx_ch5_delay.byte$00; x-ref: $8AAB, $8ABD
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks SFX channel 5 (energy bonus countdown sound, Voice 1).
; Plays a noise waveform whose frequency tracks the remaining energy value
; while it is converted to score points during the level-complete sequence.
;
; Phase 0: Inactive (state = 0), returns immediately.
; Phase 1: Silence Voice 1, reset ADSR, set delay counter, advance to phase 2.
; Phase 2: Wait for delay, then gate noise waveform at energy-based frequency.
; Phase 3+: Update frequency each tick from current energy value.
;
; Inputs: sfx_ch5_state (trigger flag set to 1 to start)
; Outputs: None
; Side Effects: Writes to SID Voice 1 registers ($D400-$D406)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8A9Fad 9d 8atick_sfx_ch5_voice1ldasfx_ch5_state; Check channel state (0=off, 1=init, 2+=playing) ; x-ref: $1E46
$8AA2d0 01bneb_8AA5; State 0: channel inactive, skip
$8AA460rts; State 0: nothing to do
$8AA5c9 01b_8AA5cmp#$01; State 1: begin initialization? ; x-ref: $8AA2
$8AA7d0 14bneb_8ABD; Not phase 1 -> skip to playback
$8AA9a9 felda#$fe; Delay = -2 (count up to 0)
$8AAB8d 9e 8astasfx_ch5_delay; Store initial delay
$8AAEa9 00lda#$00; Silence Voice 1: gate off
$8AB08d 04 d4sta$d404; Voice 1: Control Register
$8AB38d 05 d4sta$d405; Clear Attack/Decay; Voice 1: Attack / Decay Cycle Control
$8AB68d 06 d4sta$d406; Clear Sustain/Release; Voice 1: Sustain / Release Cycle Control
$8AB9ee 9d 8aincsfx_ch5_state; Advance state to phase 2
$8ABC60rts
$8ABDee 9e 8ab_8ABDincsfx_ch5_delay; Increment delay counter toward 0 ; x-ref: $8AA7
$8AC010 01bplb_8AC3; Still negative: not ready yet
$8AC260rts
$8AC3d0 1bb_8AC3bneb_8AE0; Delay = 0: set up voice; >0: update freq ; x-ref: $8AC0
$8AC5ad 1b 82ldaenergy_level; Use energy level as SID voice 1 freq
$8AC88d 01 d4sta$d401; Set frequency high byte; Voice 1: Frequency Control - High-Byte
$8ACBa9 00lda#$00; Freq lo = 0
$8ACD8d 00 d4sta$d400; Voice 1: Frequency Control - Low-Byte
$8AD0a9 10lda#$10; Attack=1, Decay=0 (fast attack)
$8AD28d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8AD5a9 50lda#$50; Sustain=5, Release=0
$8AD78d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8ADAa9 81lda#$81; $81 = gate + noise waveform
$8ADC8d 04 d4sta$d404; Voice 1: Control Register
$8ADF60rts
$8AE0ad 1b 82b_8AE0ldaenergy_level; Update SID freq to current energy ; x-ref: $8AC3
$8AE38d 01 d4sta$d401; Refresh frequency high byte; Voice 1: Frequency Control - High-Byte
$8AE660rts
$8AE7a9 00stop_sfx_ch5lda#$00; Silence Voice 1 and reset channel ; x-ref: $624A
$8AE98d 04 d4sta$d404; Gate off, waveform off; Voice 1: Control Register
$8AEC8d 9d 8astasfx_ch5_state; Clear state -> channel inactive
$8AEF60rts
$8AF0sfx6_enable.byte$00; x-ref: $1E66, $7C5D, $7C77, $7C7E, $819F, ...
$8AF1sfx6_timer.byte$00; x-ref: $8AFE, $8B10, $8B32, $8B4F
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks SFX channel 6 state machine on SID Voice 1. Two-phase noise burst:
; Phase 1: Silence Voice 1, clear ADSR, start 2-frame delay.
; Phase 2: On timer=0, set noise waveform at freq $0500 with fast
; attack/decay. On timer=4, release gate. On timer=6, either
; restart the burst cycle or end if state was advanced to 3.
;
; Inputs: sfx6_enable — non-zero to activate, 0 = idle
; Outputs: sfx6_enable — cleared to 0 when effect finishes
; Side Effects: Writes SID Voice 1 registers ($D400–$D406)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8AF2ad f0 8atick_sfx6_voice1ldasfx6_enable; Check if channel is active ; x-ref: $1E49
$8AF5d0 01bneb_sfx6_active; State 0 = idle, skip
$8AF760rts; Idle — return immediately
$8AF8c9 01b_sfx6_activecmp#$01; State 1 = initialization phase? ; x-ref: $8AF5
$8AFAd0 14bneb_sfx6_advance_timer; Not state 1, skip to timer advance
$8AFCa9 felda#$fe; Initial delay = -2 (2 frames before trigger)
$8AFE8d f1 8astasfx6_timer
$8B01a9 00lda#$00; Gate off — silence voice
$8B038d 04 d4sta$d404; Voice 1: Control Register
$8B068d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8B098d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8B0Cee f0 8ab_sfx6_phase1_initincsfx6_enable
$8B0F60rts
$8B10ee f1 8ab_sfx6_advance_timerincsfx6_timer; Increment delay timer ; x-ref: $8AFA
$8B1310 01bplb_sfx6_set_freq; Timer < 0 — still in initial delay
$8B1560rts
$8B16d0 1ab_sfx6_set_freqbneb_sfx6_check_gate_off; Timer > 0 — check later phases ; x-ref: $8B13
$8B18a9 05lda#$05; Freq hi = $05 → freq $0500
$8B1A8d 01 d4sta$d401; Voice 1: Frequency Control - High-Byte
$8B1Da9 00lda#$00; Freq lo = $00
$8B1F8d 00 d4sta$d400; Voice 1: Frequency Control - Low-Byte
$8B22a9 52lda#$52; ADSR: Attack=$5, Decay=$2
$8B248d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8B27a9 00lda#$00; ADSR: Sustain=$0, Release=$0
$8B298d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8B2Ca9 81lda#$81; Noise waveform + gate on
$8B2E8d 04 d4sta$d404; Voice 1: Control Register
$8B3160rts
b_sfx6_check_gate_off
$8B32ad f1 8aldasfx6_timer; Check timer value ; x-ref: $8B16
$8B35c9 04cmp#$04; Timer == 4?
$8B37d0 06bneb_sfx6_check_end; Not 4, check for end phase
$8B39a9 80lda#$80; Noise waveform, gate OFF (release)
$8B3B8d 04 d4sta$d404; Voice 1: Control Register
$8B3E60rts
$8B3Fc9 06b_sfx6_check_endcmp#$06; Timer == 6? ; x-ref: $8B37
$8B41d0 14bner_8B57; Not 6 yet — wait
$8B43ad f0 8aldasfx6_enable; State == 3? (cancel requested)
$8B46c9 03cmp#$03; If state 3, silence and end
$8B48d0 03bneb_sfx6_restart_cycle; Otherwise jump to silence/end
$8B4A4c 58 8bjmpsilence_sfx6_voice1
$8B4Da9 00b_sfx6_restart_cyclelda#$00; Reset timer to 0 for next burst cycle ; x-ref: $8B48
$8B4F8d f1 8astasfx6_timer
$8B52a9 81lda#$81; Noise + gate on, restart burst
$8B548d 04 d4sta$d404; Voice 1: Control Register
$8B5760r_8B57rts; x-ref: $8B41
$8B58a9 00silence_sfx6_voice1lda#$00; Clear gate — silence voice ; x-ref: $800F, $8B4A
$8B5A8d 04 d4sta$d404; Voice 1: Control Register
$8B5D8d f0 8astasfx6_enable; Mark channel as idle
$8B6060rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Cancels SFX channel 6. If in state 1 (init), reverts to idle.
; If in state 2 (running), advances to state 3 so the tick routine
; will silence the voice on the next timer=6 check.
;
; Inputs: sfx6_enable — current channel state
; Outputs: sfx6_enable — modified to cancel the effect
; Side Effects: None (actual silencing deferred to tick_sfx6_voice1)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8B61ad f0 8acancel_sfx6ldasfx6_enable; Read current SFX6 state ; x-ref: $7C62
$8B64c9 01cmp#$01; State == 1 (init phase)?
$8B66d0 04bneb_8B6C; No, check if running
$8B68ce f0 8adecsfx6_enable; Revert to 0 (idle) — cancel init
$8B6B60rts
$8B6Cc9 02b_8B6Ccmp#$02; State == 2 (running)? ; x-ref: $8B66
$8B6Ed0 05bner_8B75; Not 1 or 2, ignore
$8B70a9 03lda#$03; Advance to state 3 (pending cancel)
$8B728d f0 8astasfx6_enable
$8B7560r_8B75rts; x-ref: $8B6E
$8B76sfx7_enable.byte$00; x-ref: $1E69, $37CB, $8B78, $8B92, $8BCB
$8B77sfx7_timer.byte$00; x-ref: $8B84, $8B96, $8BBD
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Ticks SFX channel 7 state machine on SID Voice 1. Three-phase sound effect:
; Phase 1: Silence Voice 1 and start a 2-frame delay.
; Phase 2 (timer=0): Configure pulse waveform at freq $0A00, gate on.
; Phase 2 (timer=1): Gate off, mark channel inactive.
;
; Inputs: sfx7_enable — non-zero to activate, 0 = idle
; Outputs: sfx7_enable — cleared to 0 when effect finishes
; Side Effects: Writes SID Voice 1 registers ($D400–$D406)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8B78ad 76 8btick_sfx7_voice1ldasfx7_enable; Check if channel is active ; x-ref: $1E4C
$8B7Bd0 01bneb_8B7E; Inactive (0) → return
$8B7D60rts
$8B7Ec9 01b_8B7Ecmp#$01; Phase 1? (init) ; x-ref: $8B7B
$8B80d0 14bneb_8B96
$8B82a9 felda#$fe; Timer = -2 (2-frame delay)
$8B848d 77 8bstasfx7_timer
$8B87a9 00lda#$00; Silence Voice 1
$8B898d 04 d4sta$d404; Voice 1: Control Register
$8B8C8d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8B8F8d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8B92ee 76 8bincsfx7_enable; Advance to phase 2
$8B9560rts
$8B96ee 77 8bb_8B96incsfx7_timer; Increment delay timer ; x-ref: $8B80
$8B9910 01bplb_8B9C; Timer < 0? Still waiting
$8B9B60rts
$8B9Cd0 1fb_8B9Cbneb_8BBD; Timer != 0 → check release ; x-ref: $8B99
$8B9Ea9 0alda#$0a; Freq hi = $0A
$8BA08d 01 d4sta$d401; Voice 1: Frequency Control - High-Byte
$8BA3a9 00lda#$00; Freq lo = $00 → freq $0A00
$8BA58d 00 d4sta$d400; Voice 1: Frequency Control - Low-Byte
$8BA8a9 08lda#$08; Pulse width hi-nybble = $08
$8BAA8d 03 d4sta$d403; Voice 1: Pulse Waveform Width - High-Nybble
$8BADa9 10lda#$10; A=$1, D=$0 → fast attack, instant decay
$8BAF8d 05 d4sta$d405; Voice 1: Attack / Decay Cycle Control
$8BB2a9 a1lda#$a1; S=$A, R=$1 → high sustain, fast release
$8BB48d 06 d4sta$d406; Voice 1: Sustain / Release Cycle Control
$8BB7a9 41lda#$41; $41 = pulse waveform + gate on
$8BB98d 04 d4sta$d404; Voice 1: Control Register
$8BBC60rts
$8BBDad 77 8bb_8BBDldasfx7_timer; x-ref: $8B9C
$8BC0c9 01cmp#$01; Timer == 1? (release frame)
$8BC2d0 0abner_8BCE
$8BC4a9 40lda#$40; $40 = pulse waveform, gate off
$8BC68d 04 d4sta$d404; Voice 1: Control Register
$8BC9a9 00lda#$00; Mark channel inactive
$8BCB8d 76 8bstasfx7_enable
$8BCE60r_8BCErts; x-ref: $8BC2
$8BCFsfx_ch8_state.byte$00; x-ref: $1E6C, $884D, $8BD1, $8BEB, $8C13
$8BD0sfx_ch8_delay.byte$00; x-ref: $8BDD, $8BEF
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Tick SFX channel 8 — delayed noise burst (Voice 3).
; State machine: 0=idle, 1=init (silence Voice 3, start delay),
; 2=wait for delay then trigger noise waveform, then reset to idle.
; Used for explosion-like sound effects.
;
; Inputs: sfx_ch8_state (channel state: 0=off, 1=init, 2=running)
; Outputs: sfx_ch8_state, sfx_ch8_delay (updated each tick)
; Side Effects: Programs SID Voice 3 registers ($D40E-$D414)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$8BD1ad cf 8btick_sfx_ch8ldasfx_ch8_state; Check channel state (0=idle) ; x-ref: $1E4F
$8BD4d0 01bneb_8BD7
$8BD660rts; Return if idle
$8BD7c9 01b_8BD7cmp#$01; State 1? (init phase) ; x-ref: $8BD4
$8BD9d0 14bneb_8BEF
$8BDBa9 felda#$fe; Init delay counter to -2 (2-tick delay)
$8BDD8d d0 8bstasfx_ch8_delay
$8BE0a9 00lda#$00; Silence Voice 3: gate off
$8BE28d 12 d4sta$d412; Voice 3: Control Register
$8BE58d 13 d4sta$d413; Zero attack/decay; Voice 3: Attack / Decay Cycle Control
$8BE88d 14 d4sta$d414; Zero sustain/release; Voice 3: Sustain / Release Cycle Control
$8BEBee cf 8bincsfx_ch8_state; Advance state to 2
$8BEE60rts
$8BEFee d0 8bb_8BEFincsfx_ch8_delay; Increment delay counter ; x-ref: $8BD9
$8BF210 01bplb_8BF5; Still negative? Not ready yet
$8BF460rts
$8BF5d0 1ab_8BF5bneb_8C11; Counter > 0? Go to release phase ; x-ref: $8BF2
$8BF7a9 05lda#$05; Counter == 0: trigger noise burst
$8BF98d 0f d4sta$d40f; Freq hi = $05 → ~328 Hz noise; Voice 3: Frequency Control - High-Byte
$8BFCa9 00lda#$00; Freq lo = $00
$8BFE8d 0e d4sta$d40e; Voice 3: Frequency Control - Low-Byte
$8C01a9 97lda#$97
$8C038d 13 d4sta$d413; A/D=$97: fast attack, medium decay; Voice 3: Attack / Decay Cycle Control
$8C06a9 00lda#$00
$8C088d 14 d4sta$d414; S/R=$00: no sustain, instant release; Voice 3: Sustain / Release Cycle Control
$8C0Ba9 81lda#$81; Ctrl=$81: noise waveform + gate on
$8C0D8d 12 d4sta$d412; Voice 3: Control Register
$8C1060rts
$8C11a9 00b_8C11lda#$00; Counter == 1: effect done, reset state to 0 ; x-ref: $8BF5
$8C138d cf 8bstasfx_ch8_state; Clear channel state (idle)
$8C1660rts
; Screen row-to-address lookup table, low bytes. Maps row index (0-16) to screen RAM offset. Each row is 40 ($28) chars wide.
$8C17row_to_screen_lo.byte<SCREEN_RAM, <SCREEN_RAM_R1C0, <SCREEN_RAM_R2C0, <SCREEN_RAM_R3C0, <SCREEN_RAM_R4C0; x-ref: $50DC, $84FD, $852B, $8662, $8777, ...
$8C1C.byte<SCREEN_RAM_R5C0, <SCREEN_RAM_R6C0, <SCREEN_RAM_R7C0, <SCREEN_RAM_R8C0, <SCREEN_RAM_R9C0
$8C21.byte<SCREEN_RAM_R10C0, <SCREEN_RAM_R11C0, <SCREEN_RAM_R12C0, <SCREEN_RAM_R13C0, <SCREEN_RAM_R14C0
$8C26.byte<SCREEN_RAM_R15C0, <SCREEN_RAM_R16C0
$8C28row_to_screen_hi.byte>SCREEN_RAM, >SCREEN_RAM_R1C0, >SCREEN_RAM_R2C0, >SCREEN_RAM_R3C0, >SCREEN_RAM_R4C0; x-ref: $50E1, $8506, $8534, $8669, $877E, ...
$8C2D.byte>SCREEN_RAM_R5C0, >SCREEN_RAM_R6C0, >SCREEN_RAM_R7C0, >SCREEN_RAM_R8C0, >SCREEN_RAM_R9C0
$8C32.byte>SCREEN_RAM_R10C0, >SCREEN_RAM_R11C0, >SCREEN_RAM_R12C0, >SCREEN_RAM_R13C0, >SCREEN_RAM_R14C0
$8C37.byte>SCREEN_RAM_R15C0, >SCREEN_RAM_R16C0