;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
;
; Auto-generated by Regenerator 2000 v0.9.17
; https://github.com/ricardoquesada/regenerator2000
;
; Exported from: plus4_kikstart_8192.regen2000proj
;
; Assemble with 64tass:
; 64tass -o plus4_kikstart_8192.prg plus4_kikstart_8192.asm
;
;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; EXTERNAL LABELS
; zpf_ = Zero Page Field
; zpa_ = Zero Page Absolute Address
; zpp_ = Zero Page Pointer
; f_ = Field
; a_ = Absolute Address
zp_game_mode = $02 ; x-ref: $21C0, $2304, $2410, $25D0, $2635, $267C, $268C, $2696, ...
zp_frame_phase = $03 ; x-ref: $20BE, $20D2, $20F6, $2128, $212A, $2132, $2134
zp_raster_split_a = $04 ; x-ref: $214D, $21BC, $246C, $24F4, $24FB, $250D, $2514, $262F, ...
zp_raster_split_b = $05 ; x-ref: $2158, $2477, $24E1, $24E8, $2631, $264F
zp_raster_target = $06 ; x-ref: $20C2, $2166, $2171
zp_obstacle_flags = $10 ; x-ref: $223E, $225A, $2286, $2299, $22C0, $2526, $2572, $257A, ...
zp_obstacle_life = $14 ; x-ref: $2273, $22A6, $22CC, $2804, $2806, $2CA0, $2CE8
zp_obstacle_pos_hi = $18 ; x-ref: $224E, $2254, $2534, $253D, $2541, $254B, $254F, $255B, ...
zp_obstacle_pos_lo = $1c ; x-ref: $2536, $253B, $2558, $255D, $2CC5, $2CCB
zp_obstacle_vel = $20 ; x-ref: $228F, $2530, $2539, $255F, $2563, $293C, $2943, $2CC1, ...
zp_ptr_obj_record_lo = $24 ; x-ref: $231A, $239E, $23EE, $2616, $294A, $2E1E, $2E28, $2E2E, ...
zp_ptr_obj_record_hi = $25 ; x-ref: $261A, $2E23, $2E2C, $2E34, $2E40
zp_track_scroll_frac = $26 ; x-ref: $24D8, $24DD, $2633, $2651
zp_bike_pos_x_hi = $27 ; x-ref: $23FB, $2482, $2497, $2690, $26B2, $2777, $27AD, $27B3, ...
zp_bike_pos_x_lo = $28 ; x-ref: $2403, $2686, $277B, $2911, $2916, $2956
zp_bike_vel = $29 ; x-ref: $2688, $26F0, $28FD, $2907, $290B, $2914, $2958, $2E6F
zp_bike_throttle = $2a ; x-ref: $24DB, $2639, $2649, $26C5, $26FA, $277F, $278A, $2790, ...
zp_spawn_cooldown = $2b ; x-ref: $2680, $2813, $2C96, $2CA4
zp_rng_state_lo = $2c ; x-ref: $27E0, $27E5, $27EB
zp_rng_state_mid = $2d ; x-ref: $27E7, $27ED, $27F3
zp_rng_state_hi = $2e ; x-ref: $27EF, $27F5
zp_course_index = $2f ; x-ref: $20B0, $2592, $29B4, $2A1A, $2A1C, $2A3D, $2A50, $2A85, ...
zp_lives_counter = $30 ; x-ref: $20B4, $21AD, $25A7, $25AB, $2645, $28A3, $28B3, $299C, ...
zp_scroll_dir_flag = $31 ; x-ref: $232C, $232E, $2653, $26A2
zp_sprite_queue_count = $32 ; x-ref: $20B6, $21D8, $21FB, $2206, $23D0, $23E6
zp_joystick_state = $33 ; x-ref: $2743, $2749, $274D, $2754, $2784, $2797, $27CF, $2DBD, ...
zp_music_step = $34 ; x-ref: $261E, $2AA0, $2AA2, $2AAA
zp_music_pos = $35 ; x-ref: $2622, $2AAC, $2AAE, $2AB6, $2AB8
zp_track_progress = $36 ; x-ref: $26C1, $26DE, $2834, $289F, $28AB, $28AD, $28C4, $28C6, ...
zp_track_left_edge = $37 ; x-ref: $268E, $27B1, $2927, $295A, $2EBA
zp_track_flags = $38 ; x-ref: $27C1, $292D, $2E80
zp_track_right_edge = $39 ; x-ref: $27AB, $2921, $2E76
zp_prev_track_flags = $3a ; x-ref: $27C3
zp_player_active = $3b ; x-ref: $21A3, $21A9, $2322, $233A, $233F, $2657
zp_anim_frame_count = $3c ; x-ref: $2372, $2376, $2A42, $2BA8, $2D8B, $2DA8
zp_raster_color = $3d ; x-ref: $2146, $21B4, $21D4
zp_scroll_timer = $3e ; x-ref: $2504, $2509
zp_frame_counter = $3f ; x-ref: $20D8, $236C, $256C, $2B33, $2BC4, $2DDE
zp_crash_debris_x = $40 ; x-ref: $2445, $244B, $2453, $2675, $26B0, $2764
zp_crash_debris_y = $48 ; x-ref: $242C, $2436, $2441, $2457, $26B6, $276A, $2896
zp_snd_pitch = $50 ; x-ref: $28E6, $2AFA, $2AFF, $2B01, $2B65, $2B7D, $2B8E, $2BAE, ...
zp_snd_pitch_delta = $51 ; x-ref: $28EA, $2AFD, $2B69, $2B81, $2B92, $2BB2, $2BD1, $2BE2, ...
zp_spawn_type = $52 ; x-ref: $2C5F, $2C9A, $2CA6
zp_scroll_speed = $53 ; x-ref: $2507, $2A47, $2DAD
zp_snd_duration = $54 ; x-ref: $28EE, $2AF4, $2AF8, $2B61, $2B79, $2B96, $2BA0, $2BA6, ...
zp_snd_volume = $55 ; x-ref: $28F2, $2B1B, $2B6D, $2B85, $2B9A, $2BB6, $2BD5, $2BEA, ...
zp_title_row_anim = $56 ; x-ref: $2DB7, $2DC5, $2DCB, $2E0E
zp_title_char_idx = $57 ; x-ref: $2DEA, $2DF2, $2DF4, $2DF8, $2E10, $2E17
zp_blit_scratch = $f7 ; x-ref: $2CF1, $2CF9, $2D18, $2D70, $2D75, $2D7B, $2D84
zp_ted_ctrl_staging = $f8 ; x-ref: $2475, $248B, $248D, $24AF, $24C8, $28D9, $28DF, $2B03, ...
zp_pitch_hi = $f9 ; x-ref: $2480, $2493, $2495, $24AB, $24CA, $2B07, $2B0B, $2B0F, ...
zp_blit_col = $fc ; x-ref: $2210, $2229, $2233, $2252, $228D, $22C6, $22E6, $22E8, ...
zp_blit_row = $fd ; x-ref: $2215, $222B, $222F, $2275, $22A8, $22D1, $22FF, $23C8, ...
zp_blit_color_ptr = $fe ; x-ref: $21E2, $21EF, $21F9, $2221, $2231, $224C, $2297, $22D5, ...
zp_blit_char_idx = $ff ; x-ref: $21E7, $21F1, $21F5, $221C, $2227, $2258, $2260, $2264, ...
vec_irq_lo = $0314 ; x-ref: $2060
vec_irq_hi = $0315 ; x-ref: $2065
sprite_q_row_m1 = $03ff ; x-ref: $21DC
sprite_q_row = $0400 ; x-ref: $220A, $23D4
sprite_q_col_m1 = $042f ; x-ref: $21E9
sprite_q_col = $0430 ; x-ref: $2223, $23D9
sprite_q_char = $0460 ; x-ref: $2219, $23DE
sprite_q_color = $0490 ; x-ref: $221E, $23E3
COLOR_RAM_R0C0 = $0800 ; x-ref: $204F, $20A2
COLOR_RAM_R1C0 = $0828 ; x-ref: $20A5
COLOR_RAM_R6C8 = $08f8 ; x-ref: $2974
COLOR_RAM_R6C16 = $0900 ; x-ref: $2052
COLOR_RAM_R7C33 = $0939 ; x-ref: $2A95
COLOR_RAM_R7C34 = $093a ; x-ref: $2A98
COLOR_RAM_R8C8 = $0948 ; x-ref: $2979
COLOR_RAM_R8C14 = $094e ; x-ref: $26D6
COLOR_RAM_R10C8 = $0998 ; x-ref: $2A68, $2A7A
COLOR_RAM_R12C16 = $09f0 ; x-ref: $2DD8
COLOR_RAM_R12C32 = $0a00 ; x-ref: $2055
COLOR_RAM_R16C0 = $0a80 ; x-ref: $2080
COLOR_RAM_R17C0 = $0aa8 ; x-ref: $207D
COLOR_RAM_R18C0 = $0ad0 ; x-ref: $207A
COLOR_RAM_R19C0 = $0af8 ; x-ref: $2077
COLOR_RAM_R19C8 = $0b00 ; x-ref: $2058
COLOR_RAM_R20C0 = $0b20 ; x-ref: $2074
COLOR_RAM_R21C0 = $0b48 ; x-ref: $2071
COLOR_RAM_R22C39 = $0b97 ; x-ref: $25B5
COLOR_RAM_R23C0 = $0b98 ; x-ref: $258C, $25B8, $25C5
COLOR_RAM_R23C1 = $0b99 ; x-ref: $25BB
COLOR_RAM_R23C23 = $0baf ; x-ref: $25A1
COLOR_RAM_R24C0 = $0bc0 ; x-ref: $25C8
SCREEN_RAM_R0C0 = $0c00 ; x-ref: $2041, $2094, $2D95
SCREEN_RAM_R0C7 = $0c07 ; x-ref: $2199, $2349, $234F, $2358, $237A, $2380, $2389, $2981, ...
SCREEN_RAM_R0C8 = $0c08 ; x-ref: $298A, $2E9A, $2EA6
SCREEN_RAM_R0C10 = $0c0a ; x-ref: $2993
SCREEN_RAM_R0C11 = $0c0b ; x-ref: $2363
SCREEN_RAM_R0C19 = $0c13 ; x-ref: $29CA, $29D0, $29D9, $29E9, $29FB
SCREEN_RAM_R0C32 = $0c20 ; x-ref: $29EC, $29FE
SCREEN_RAM_R1C0 = $0c28 ; x-ref: $209C, $2D9B
SCREEN_RAM_R1C7 = $0c2f ; x-ref: $234C, $235D, $237D, $238E, $2EAB
SCREEN_RAM_R1C8 = $0c30 ; x-ref: $2EAE
SCREEN_RAM_R1C9 = $0c31 ; x-ref: $20B8, $2DA3
SCREEN_RAM_R1C11 = $0c33 ; x-ref: $2366
SCREEN_RAM_R1C19 = $0c3b ; x-ref: $29CD, $29DE, $2A01
SCREEN_RAM_R1C32 = $0c48 ; x-ref: $2A04
SCREEN_RAM_R2C0 = $0c50 ; x-ref: $2A30
SCREEN_RAM_R6C8 = $0cf8 ; x-ref: $2969
SCREEN_RAM_R6C16 = $0d00 ; x-ref: $2044
SCREEN_RAM_R6C29 = $0d0d ; x-ref: $284F, $2852, $285B, $2863, $2987
SCREEN_RAM_R6C30 = $0d0e ; x-ref: $2866, $2990
SCREEN_RAM_R6C31 = $0d0f ; x-ref: $2869, $2999
SCREEN_RAM_R7C33 = $0d39 ; x-ref: $2A82
SCREEN_RAM_R7C34 = $0d3a ; x-ref: $2A90
SCREEN_RAM_R8C8 = $0d48 ; x-ref: $296F
SCREEN_RAM_R8C14 = $0d4e ; x-ref: $26D1
SCREEN_RAM_R8C16 = $0d50 ; x-ref: $2A33
SCREEN_RAM_R8C29 = $0d5d ; x-ref: $286E, $2871, $287A, $2882
SCREEN_RAM_R8C30 = $0d5e ; x-ref: $2885, $29A1
SCREEN_RAM_R8C31 = $0d5f ; x-ref: $2888, $29A7
SCREEN_RAM_R10C8 = $0d98 ; x-ref: $2A63, $2A75
SCREEN_RAM_R12C16 = $0df0 ; x-ref: $2DD3
SCREEN_RAM_R12C22 = $0df6 ; x-ref: $2E04
SCREEN_RAM_R12C23 = $0df7 ; x-ref: $2DFE
SCREEN_RAM_R12C32 = $0e00 ; x-ref: $2047
SCREEN_RAM_R16C0 = $0e80 ; x-ref: $23A6
SCREEN_RAM_R17C0 = $0ea8 ; x-ref: $23AC
SCREEN_RAM_R18C0 = $0ed0 ; x-ref: $23B2
SCREEN_RAM_R19C0 = $0ef8 ; x-ref: $23B8
SCREEN_RAM_R19C8 = $0f00 ; x-ref: $204A
SCREEN_RAM_R20C0 = $0f20 ; x-ref: $23BE
SCREEN_RAM_R21C0 = $0f48 ; x-ref: $206C
SCREEN_RAM_R23C0 = $0f98 ; x-ref: $2086
SCREEN_RAM_R24C0 = $0fc0 ; x-ref: $208E
; ENUMS
; Enum: GameMode
GameMode = {
NORMAL_RIDE: $00,
CRASH_DEBRIS: $01,
ACTIVE_RIDE: $02,
STARTING_GATE: $03,
TRACK_SCROLL: $04,
DEBRIS_WAIT: $05,
RESULTS_SCREEN: $06,
VELOCITY_INTEGRATE: $07,
NEXT_LAP_INIT: $08
}
; charset
$1000.fill8, $00
$1008.byte$ee, $b7, $be, $eb, $ac, $ea, $9a, $aa
$1010.byte$55, $55, $ff, $ff, $f9, $bf, $3a, $eb
$1018.byte$55, $55, $ff, $cf, $fb, $ef, $e6, $eb
$1020.byte$ff, $df, $ff, $fc, $ff, $3f, $ff, $fd
$1028.byte$ff, $cf, $ff, $fd, $ff, $7b, $ff, $ff
$1030.byte$00, $00, $00, $80, $40, $b0, $98, $2e
$1038.byte$00, $00, $00, $00, $00, $00, $00, $82
$1040.byte$00, $00, $00, $03, $02, $0e, $29, $ea
$1048.byte$55, $55, $02, $0a, $28, $28, $0a, $3f
$1050.byte$55, $55, $82, $00, $00, $00, $ff, $3c
$1058.byte$55, $55, $80, $a0, $28, $28, $a0, $fc
$1060.byte$03, $0f, $3c, $35, $32, $f6, $ca, $da
$1068.byte$c0, $e0, $68, $48, $98, $9a, $f2, $f6
$1070.byte$da, $ca, $f7, $33, $35, $3c, $0e, $02
$1078.byte$f6, $f2, $da, $d8, $48, $68, $a0, $80
$1080.byte$fd, $fd, $fd, $55, $df, $df, $df, $55
$1088.byte$ff, $de, $ff, $ef, $f3, $7f, $ee, $bb
$1090.byte$7f, $1f, $07, $01, $00, $00, $00, $00
$1098.byte$f4, $d0, $40, $00, $00, $00, $00, $00
$10A0.byte$0f, $3f, $35, $f5, $d5, $d5, $d5, $d5
$10A8.byte$ff, $ff, $75, $75, $75, $75, $75, $fd
$10B0.byte$c0, $f0, $70, $7c, $5c, $5c, $5c, $5c
$10B8.byte$ff, $ff, $d5, $d5, $d5, $d5, $d5, $ff
$10C0.byte$ff, $57, $55, $55, $55, $55, $55, $ff
$10C8.byte$fc, $fc, $5c, $5c, $5c, $5c, $5c, $fc
$10D0.byte$ff, $fc, $f3, $cf, $cd, $cf, $fd, $ff
$10D8.byte$ff, $fc, $ff, $ff, $55, $ff, $55, $ff
$10E0.byte$fc, $fc, $3c, $cc, $cc, $cc, $fc, $fc
$10E8.byte$df, $df, $ff, $eb, $eb, $2b, $28, $28
$10F0.byte$57, $ff, $ff, $03, $ff, $ff, $00, $00
$10F8.byte$dc, $dc, $fc, $ac, $ac, $a0, $a0, $a0
$1100.byte$00, $03, $0f, $0e, $0f, $37, $3f, $fb
$1108.byte$cc, $3c, $fb, $ef, $bc, $ef, $ef, $bf
$1110.byte$00, $00, $c0, $c0, $c0, $f0, $70, $fc
$1118.byte$fb, $cb, $3e, $fe, $ee, $fb, $2f, $fb
$1120.byte$bf, $ee, $ee, $ee, $fb, $7b, $fb, $fe
$1128.byte$bc, $f0, $fc, $cc, $fc, $e0, $bc, $fc
$1130.byte$fe, $3e, $df, $fb, $cb, $3e, $3f, $0f
$1138.byte$fe, $cb, $bb, $bb, $ef, $ef, $ff, $ef
$1140.byte$cc, $fc, $e0, $ec, $bc, $b0, $c0, $f0
$1148.byte$0c, $03, $00, $00, $00, $00, $02, $eb
$1150.byte$ed, $ef, $20, $20, $a8, $a8, $ee, $ab
$1158.byte$80, $00, $00, $00, $00, $00, $00, $ac
$1160.byte$c0, $55, $55, $0f, $33, $55, $55, $00
$1168.byte$00, $55, $55, $00, $c0, $55, $55, $03
$1170.byte$00, $55, $55, $00, $03, $5f, $7d, $c0
$1178.byte$03, $5f, $7d, $f0, $c0, $55, $55, $00
$1180.byte$00, $55, $55, $03, $0f, $7d, $f5, $c0
$1188.byte$0f, $7d, $f5, $c0, $00, $55, $55, $00
$1190.byte$c0, $55, $55, $03, $00, $55, $55, $00
$1198.byte$00, $55, $55, $c0, $f0, $55, $55, $03
$11A0.byte$18, $18, $18, $18, $30, $30, $30, $30
$11A8.byte$60, $60, $60, $60, $c3, $cf, $ff, $f3
$11B0.byte$c6, $06, $06, $06, $0c, $0c, $0c, $0c
$11B8.byte$00, $3c, $7e, $7e, $7e, $7e, $3c, $00
$11C0.byte$0f, $3f, $3f, $f7, $f7, $ff, $ff, $ff
$11C8.byte$ff, $ff, $3f, $3f, $0f, $01, $04, $01
$11D0.byte$00, $01, $00, $00, $00, $00, $00, $00
$11D8.byte$c0, $f0, $f0, $fc, $fc, $fc, $fc, $fc
$11E0.byte$fc, $cc, $30, $f0, $c0, $00, $00, $00
$11E8.byte$40
$11E9.fill9, $00
$11F2.byte$0f, $3f, $3f, $f7, $f7, $ff, $ff, $ff
$11FA.byte$ff, $ff, $3f, $3f, $0f, $01, $04, $01
$1202.byte$00, $01, $00, $00, $00, $00, $00, $00
$120A.byte$c0, $f0, $f0, $fc, $fc, $fc, $fc, $fc
$1212.byte$fc, $cc, $30, $f0, $c0, $00, $00, $00
$121A.byte$40
$121B.fill9, $00
$1224.byte$0f, $3f, $3f, $f7, $f7, $ff, $ff, $ff
$122C.byte$ff, $ff, $3f, $3f, $0f, $01, $04, $01
$1234.byte$00, $01, $00, $00, $00, $00, $00, $00
$123C.byte$c0, $f0, $f0, $fc, $fc, $fc, $fc, $fc
$1244.byte$fc, $cc, $30, $f0, $c0, $00, $00, $00
$124C.byte$40
$124D.fill9, $00
$1256.byte$0f, $3f, $3f, $f7, $f7, $ff, $ff, $ff
$125E.byte$ff, $ff, $3f, $3f, $0f, $01, $04, $01
$1266.byte$00, $01, $00, $00, $00, $00, $00, $00
$126E.byte$c0, $f0, $f0, $fc, $fc, $fc, $fc, $fc
$1276.byte$fc, $cc, $30, $f0, $c0, $00, $00, $00
$127E.byte$40, $00, $0f, $0f, $03, $03, $03, $03
$1286.byte$fd, $fd, $c3, $c3, $03, $03, $03, $03
$128E.fill14, $00
$129C.byte$0c, $0c, $fc, $fc, $00, $00, $00, $00
$12A4.byte$c0, $c0
$12A6.fill12, $00
$12B2.byte$3c, $3c, $0f, $0f, $03, $03, $01, $0d
$12BA.byte$3f, $f3, $c3, $f3, $30
$12BF.fill12, $00
$12CB.byte$30, $3c, $0c, $3c, $f0, $c0, $00, $00
$12D3.byte$00, $c0, $c0, $f0, $f0
$12D8.fill14, $00
$12E6.byte$30, $f0, $cc, $0f, $01, $01, $0f, $0c
$12EE.byte$30, $30, $3c, $0c
$12F2.fill10, $00
$12FC.byte$c0, $f0, $30, $30, $c0, $c0, $00, $00
$1304.byte$c0, $cc, $3c, $30
$1308.fill14, $00
$1316.byte$03, $03, $00, $03, $33, $ff, $cd, $c1
$131E.byte$03, $03, $0f, $0c, $0f, $03
$1324.fill11, $00
$132F.byte$c0, $c0, $c0, $00, $00, $0c, $cc, $fc
$1337.byte$30
$1338.fill12, $00
$1344.byte$3c, $f3, $3f, $03, $00, $00, $03, $3f
$134C.byte$cf, $ff, $f3, $00, $00, $00, $c3, $ff
$1354.byte$3c, $ff, $fc, $f0, $00, $00, $00, $f0
$135C.byte$ff, $3c, $f0, $00, $00, $0c, $3f, $cf
$1364.byte$ff, $ff, $f3, $3f, $00, $00, $0c, $3f
$136C.byte$f3, $3f, $fc, $cf, $00, $00, $00, $0c
$1374.byte$fc, $fc, $ff, $f3, $00, $00, $30, $fc
$137C.byte$3c, $ff, $cf, $fc, $00, $00, $00, $02
$1384.byte$02, $02, $02, $01, $2d, $2a, $ba, $ce
$138C.byte$32, $fe, $de, $dc, $fc, $30, $00, $00
$1394.byte$00, $00, $00, $00, $30, $d0, $90, $80
$139C.byte$80, $20, $04, $41, $5a, $9a, $9e, $6c
$13A4.byte$63, $cf, $0d, $0d, $0f, $03
$13AA.fill15, $00
$13B9.byte$80, $80, $c0, $00, $c0, $c0, $c0, $c0
$13C1.fill15, $00
$13D0.byte$08, $0a, $2e, $33, $0c, $3f, $37, $37
$13D8.byte$3f, $0c, $00, $00, $00, $00, $00, $00
$13E0.byte$0c, $34, $24, $a0, $a0, $88, $81, $50
$13E8.byte$56, $a6, $a6, $91, $98, $b3, $83, $03
$13F0.byte$03
$13F1.fill14, $00
$13FF.byte$40, $80, $a0, $e0, $30, $c0, $f0, $70
$1407.byte$70, $f0, $c0
$140A.fill14, $00
$1418.byte$02, $02, $0b, $0c, $03, $0f, $0d, $0d
$1420.byte$0f, $03, $00, $00, $00, $00, $00, $00
$1428.byte$03, $0d, $09, $28, $28, $22, $20, $14
$1430.byte$d5, $a9, $a9, $e6, $26, $ec, $e0, $c0
$1438.byte$c0
$1439.fill13, $00
$1446.byte$40, $10, $a0, $a8, $e8, $cc, $30, $fc
$144E.byte$dc, $dc, $fc, $30
$1452.fill16, $00
$1462.byte$02, $03, $00, $03, $03, $03, $03
$1469.fill8, $00
$1471.byte$03, $02, $0a, $0a, $08, $08, $05, $85
$1479.byte$aa, $ea, $39, $c9, $fb, $78, $70, $f0
$1481.byte$c0, $00, $00, $00, $00, $00, $00, $c0
$1489.byte$40, $40, $00, $00, $80, $10, $04, $68
$1491.byte$6a, $6e, $b3, $8c, $3f, $37, $37, $3f
$1499.byte$0c
$149A.fill11, $00
$14A5.byte$02, $02, $02, $02, $01, $2d, $2a, $ba
$14AD.byte$ce, $32, $fe, $de, $dc, $fc, $30, $00
$14B5.byte$00, $00, $00, $00, $00, $30, $d0, $90
$14BD.byte$80, $80, $20, $04, $41, $5a, $9a, $9e
$14C5.byte$6c, $63, $cf, $0d, $0d, $0f, $03
$14CC.fill15, $00
$14DB.byte$80, $80, $c0, $00, $c0, $c0, $c0, $c0
$14E3.fill15, $00
$14F2.byte$08, $0a, $2e, $33, $0c, $3f, $37, $37
$14FA.byte$3f, $0c, $00, $00, $00, $00, $00, $00
$1502.byte$0c, $34, $24, $a0, $a0, $88, $81, $50
$150A.byte$56, $a6, $a6, $91, $98, $b3, $83, $03
$1512.byte$03
$1513.fill14, $00
$1521.byte$40, $80, $a0, $e0, $30, $c0, $f0, $70
$1529.byte$70, $f0, $c0
$152C.fill14, $00
$153A.byte$02, $02, $0b, $0c, $03, $0f, $0d, $0d
$1542.byte$0f, $03, $00, $00, $00, $00, $00, $00
$154A.byte$03, $0d, $09, $28, $28, $22, $20, $14
$1552.byte$d5, $a9, $a9, $e6, $26, $ec, $e0, $c0
$155A.byte$c0
$155B.fill13, $00
$1568.byte$40, $10, $a0, $a8, $e8, $cc, $30, $fc
$1570.byte$dc, $dc, $fc, $30
$1574.fill16, $00
$1584.byte$02, $03, $00, $03, $03, $03, $03
$158B.fill8, $00
$1593.byte$03, $02, $0a, $0a, $08, $08, $05, $85
$159B.byte$aa, $ea, $39, $c9, $fb, $78, $70, $f0
$15A3.byte$c0, $00, $00, $00, $00, $00, $00, $c0
$15AB.byte$40, $40, $00, $00, $80, $10, $04, $68
$15B3.byte$6a, $6e, $b3, $8c, $3f, $37, $37, $3f
$15BB.byte$0c
$15BC.fill11, $00
$15C7.byte$02, $02, $02, $02, $01, $2d, $2a, $ba
$15CF.byte$ce, $32, $fe, $de, $dc, $fc, $30, $00
$15D7.byte$00, $00, $00, $00, $00, $30, $d0, $90
$15DF.byte$80, $80, $20, $04, $41, $5a, $9a, $9e
$15E7.byte$6c, $63, $cf, $0d, $0d, $0f, $03
$15EE.fill15, $00
$15FD.byte$80, $80, $c0, $00, $c0, $c0, $c0, $c0
$1605.fill15, $00
$1614.byte$08, $0a, $2e, $33, $0c, $3f, $37, $37
$161C.byte$3f, $0c, $00, $00, $00, $00, $00, $00
$1624.byte$0c, $34, $24, $a0, $a0, $88, $81, $50
$162C.byte$56, $a6, $a6, $91, $98, $b3, $83, $03
$1634.byte$03
$1635.fill14, $00
$1643.byte$40, $80, $a0, $e0, $30, $c0, $f0, $70
$164B.byte$70, $f0, $c0
$164E.fill14, $00
$165C.byte$02, $02, $0b, $0c, $03, $0f, $0d, $0d
$1664.byte$0f, $03, $00, $00, $00, $00, $00, $00
$166C.byte$03, $0d, $09, $28, $28, $22, $20, $14
$1674.byte$d5, $a9, $a9, $e6, $26, $ec, $e0, $c0
$167C.byte$c0
$167D.fill13, $00
$168A.byte$40, $10, $a0, $a8, $e8, $cc, $30, $fc
$1692.byte$dc, $dc, $fc, $30
$1696.fill16, $00
$16A6.byte$02, $03, $00, $03, $03, $03, $03
$16AD.fill8, $00
$16B5.byte$03, $02, $0a, $0a, $08, $08, $05, $85
$16BD.byte$aa, $ea, $39, $c9, $fb, $78, $70, $f0
$16C5.byte$c0, $00, $00, $00, $00, $00, $00, $c0
$16CD.byte$40, $40, $00, $00, $80, $10, $04, $68
$16D5.byte$6a, $6e, $b3, $8c, $3f, $37, $37, $3f
$16DD.byte$0c
$16DE.fill11, $00
$16E9.byte$02, $02, $02, $02, $01, $2d, $2a, $ba
$16F1.byte$ce, $32, $fe, $de, $dc, $fc, $30, $00
$16F9.byte$00, $00, $00, $00, $00, $30, $d0, $90
$1701.byte$80, $80, $20, $04, $41, $5a, $9a, $9e
$1709.byte$6c, $63, $cf, $0d, $0d, $0f, $03
$1710.fill15, $00
$171F.byte$80, $80, $c0, $00, $c0, $c0, $c0, $c0
$1727.fill15, $00
$1736.byte$08, $0a, $2e, $33, $0c, $3f, $37, $37
$173E.byte$3f, $0c, $00, $00, $00, $00, $00, $00
$1746.byte$0c, $34, $24, $a0, $a0, $88, $81, $50
$174E.byte$56, $a6, $a6, $91, $98, $b3, $83, $03
$1756.byte$03
$1757.fill14, $00
$1765.byte$40, $80, $a0, $e0, $30, $c0, $f0, $70
$176D.byte$70, $f0, $c0
$1770.fill14, $00
$177E.byte$02, $02, $0b, $0c, $03, $0f, $0d, $0d
$1786.byte$0f, $03, $00, $00, $00, $00, $00, $00
$178E.byte$03, $0d, $09, $28, $28, $22, $20, $14
$1796.byte$d5, $a9, $a9, $e6, $26, $ec, $e0, $c0
$179E.byte$c0
$179F.fill13, $00
$17AC.byte$40, $10, $a0, $a8, $e8, $cc, $30, $fc
$17B4.byte$dc, $dc, $fc, $30
$17B8.fill16, $00
$17C8.byte$02, $03, $00, $03, $03, $03, $03
$17CF.fill8, $00
$17D7.byte$03, $02, $0a, $0a, $08, $08, $05, $85
$17DF.byte$aa, $ea, $39, $c9, $fb, $78, $70, $f0
$17E7.byte$c0, $00, $00, $00, $00, $00, $00, $c0
$17EF.byte$40, $40, $00, $00, $80, $10, $04, $68
$17F7.byte$6a, $6e, $b3, $8c, $3f, $37, $37, $3f
$17FF.byte$0c
; double-height characters charst
$1800.fill8, $00
$1808.byte$3c, $3c, $66, $66, $c0, $c0, $c0, $c0
$1810.byte$7e, $7e, $60, $60, $60, $60, $7c, $7c
$1818.byte$3e, $3e, $60, $60, $c0, $c0, $ce, $ce
$1820.byte$c6, $c6, $c6, $c6, $c6, $c6, $fe, $fe
$1828.byte$7e, $7e, $18, $18, $18, $18, $18, $18
$1830.byte$c6, $c6, $ee, $ee, $fe, $fe, $fe, $fe
$1838.byte$7c, $7c, $c6, $c6, $c6, $c6, $c6, $c6
$1840.byte$fc, $fc, $e6, $e6, $c6, $c6, $cc, $cc
$1848.byte$78, $78, $cc, $cc, $c0, $c0, $7c, $7c
$1850.byte$7e, $7e, $18, $18, $18, $18, $18, $18
$1858.byte$38, $38, $4c, $4c, $c6, $c6, $c6, $c6
$1860.byte$18, $18, $38, $38, $18, $18, $18, $18
$1868.byte$7c, $7c, $c6, $c6, $0e, $0e, $3c, $3c
$1870.byte$7e, $7e, $0c, $0c, $18, $18, $3c, $3c
$1878.byte$1c, $1c, $3c, $3c, $6c, $6c, $cc, $cc
$1880.byte$fc, $fc, $c0, $c0, $fc, $fc, $06, $06
$1888.byte$3c, $3c, $60, $60, $c0, $c0, $fc, $fc
$1890.byte$fe, $fe, $06, $06, $0c, $0c, $18, $18
$1898.byte$78, $78, $c4, $c4, $e4, $e4, $78, $78
$18A0.byte$7c, $7c, $c6, $c6, $c6, $c6, $7e, $7e
$18A8.byte$c0, $c0, $66, $66, $3c, $3c, $00, $00
$18B0.byte$60, $60, $60, $60, $7e, $7e, $00, $00
$18B8.byte$c6, $c6, $66, $66, $3e, $3e, $00, $00
$18C0.byte$c6, $c6, $c6, $c6, $c6, $c6, $00, $00
$18C8.byte$18, $18, $18, $18, $7e, $7e, $00, $00
$18D0.byte$d6, $d6, $c6, $c6, $c6, $c6, $00, $00
$18D8.byte$c6, $c6, $c6, $c6, $7c, $7c, $00, $00
$18E0.byte$f8, $f8, $dc, $dc, $ce, $ce, $00, $00
$18E8.byte$06, $06, $c6, $c6, $7c, $7c, $00, $00
$18F0.byte$18, $18, $18, $18, $18, $18, $00, $00
$18F8.byte$c6, $c6, $64, $64, $38, $38, $00, $00
$1900.byte$18, $18, $18, $18, $7e, $7e, $00, $00
$1908.byte$78, $78, $e0, $e0, $fe, $fe, $00, $00
$1910.byte$06, $06, $c6, $c6, $7c, $7c, $00, $00
$1918.byte$fe, $fe, $0c, $0c, $0c, $0c, $00, $00
$1920.byte$06, $06, $c6, $c6, $7c, $7c, $00, $00
$1928.byte$c6, $c6, $c6, $c6, $7c, $7c, $00, $00
$1930.byte$30, $30, $30, $30, $30, $30, $00, $00
$1938.byte$9e, $9e, $86, $86, $7c, $7c, $00, $00
$1940.byte$06, $06, $0c, $0c, $78, $78, $00, $00
; Packed music notes. High nibble=Voice 2, Low nibble=Voice 1
$1948music_packed_notes.byte$91, $91, $91, $94, $94, $94, $72, $72; x-ref: $2ABA, $2AD0
$1950.byte$72, $74, $74, $74, $41, $41, $41, $44
$1958.byte$44, $44, $72, $72, $92, $d4, $d4, $94
$1960.byte$c4, $c4, $c4, $b8, $b8, $b8, $a6, $a6
$1968.byte$86, $88, $88, $88, $64, $64, $64, $68
$1970.byte$68, $68, $66, $66, $66, $68, $68, $68
$1978.byte$84, $84, $84, $88, $88, $88, $66, $66
$1980.byte$66, $68, $68, $68, $44, $44, $44, $48
$1988.byte$48, $48, $66, $66, $86, $d8, $d8, $e8
$1990.byte$d3, $d3, $d3, $c7, $c7, $c7, $a5, $a5
$1998.byte$95, $97, $97, $97, $c4, $c4, $c4, $a8
$19A0.byte$a8, $a8, $96, $96, $86, $88, $88, $88
$19A8.byte$91, $91, $91, $94, $94, $94, $72, $72
$19B0.byte$72, $74, $74, $74, $41, $41, $41, $44
$19B8.byte$44, $44, $72, $72, $92, $d4, $d4, $94
$19C0.byte$c4, $c4, $c4, $b8, $b8, $b8, $a6, $a6
$19C8.byte$86, $88, $88, $88, $64, $64, $64, $68
$19D0.byte$68, $68, $66, $66, $66, $68, $68, $68
$19D8.byte$c4, $c4, $c4, $b8, $b8, $b8, $a6, $a6
$19E0.byte$86, $88, $88, $88, $64, $64, $64, $68
$19E8.byte$68, $68, $46, $46, $46, $48, $48, $48
$19F0.byte$71, $71, $71, $74, $74, $74, $72, $72
$19F8.byte$72, $74, $74, $74, $71, $71, $71, $74
$1A00.byte$74, $74, $72, $72, $72, $74, $74, $74
; Voice 2 frequency low bytes for music
$1A08voice_2_freq_lo.byte<$0000, <$0000, <$0000, <$0000, <$0304; x-ref: $2AC4
$1A0D.byte<$0320, <$0338, <$0343, <$0358, <$036a
$1A12.byte<$0373, <$037b, <$0382, <$0390, <$039c
$1A17.byte<$0000
$1A18voice_2_freq_hi.byte>$0000, >$0000, >$0000, >$0000, >$0304; x-ref: $2ACA
$1A1D.byte>$0320, >$0338, >$0343, >$0358, >$036a
$1A22.byte>$0373, >$037b, >$0382, >$0390, >$039c
$1A27.byte>$0000
; Voice 1 frequency low bytes for music
$1A28voice_1_freq_lo.byte<$0000, <$010d, <$01a9, <$01ca, <$0208; x-ref: $2AD6
$1A2D.byte<$023f, <$0270, <$0287, <$02b0, <$0000
$1A32.byte<$0000, <$0000, <$0000, <$0000, <$0000
$1A37.byte<$0000
$1A38voice_1_freq_hi.byte>$0000, >$010d, >$01a9, >$01ca, >$0208; x-ref: $2ADC
$1A3D.byte>$023f, >$0270, >$0287, >$02b0, >$0000
$1A42.byte>$0000, >$0000, >$0000, >$0000, >$0000
$1A47.byte>$0000
$1A48.byte$00, $00, $00, $00, $2f, $2a, $ba, $ce
$1A50.byte$32, $fe, $de, $dc, $fc, $30, $00, $00
$1A58.byte$00, $00, $04, $01, $2a, $aa, $ae, $ac
$1A60.byte$a3, $cf, $0d, $0d, $0f, $03, $00, $00
$1A68.byte$00, $00, $00, $00, $00, $80, $80, $c0
$1A70.byte$00, $c0, $c0, $c0, $c0, $00, $00, $00
$1A78.byte$b0, $bc, $bf, $bf, $bc, $b0, $80, $80; flag upper
$1A80.byte$80, $80, $80, $80, $80, $80, $00, $00; flag bottom
$1A88.byte$18, $18, $08, $08, $10, $10, $00, $00
$1A90.byte$38, $4c, $c6, $c6, $c6, $64, $38, $00; 0
$1A98.byte$18, $38, $18, $18, $18, $18, $7e, $00
$1AA0.byte$7c, $c6, $0e, $3c, $78, $e0, $fe, $00
$1AA8.byte$7e, $0c, $18, $3c, $06, $c6, $7c, $00
$1AB0.byte$1c, $3c, $6c, $cc, $fe, $0c, $0c, $00
$1AB8.byte$fc, $c0, $fc, $06, $06, $c6, $7c, $00
$1AC0.byte$3c, $60, $c0, $fc, $c6, $c6, $7c, $00
$1AC8.byte$fe, $c6, $0c, $18, $30, $30, $30, $00
$1AD0.byte$78, $c4, $e4, $78, $9e, $86, $7c, $00
$1AD8.byte$7c, $c6, $c6, $7e, $06, $0c, $78, $00
$1AE0.byte$00, $00, $00, $00, $00, $18, $18, $00
$1AE8.byte$00, $00, $00, $00, $00, $18, $08, $10
$1AF0.byte$1c, $1c, $1c, $18, $00, $18, $18, $00
$1AF8.byte$18, $08, $10, $00, $00, $00, $00, $00; '
$1B00.byte$00, $00, $00, $3c, $3c, $00, $00, $00; -
$1B08.byte$38, $6c, $c6, $c6, $fe, $c6, $c6, $00
$1B10.byte$fc, $c6, $c6, $fc, $c6, $c6, $fc, $00
$1B18.byte$3c, $66, $c0, $c0, $c0, $66, $3c, $00
$1B20.byte$f8, $cc, $c6, $c6, $c6, $cc, $f8, $00
$1B28.byte$7e, $60, $60, $7c, $60, $60, $7e, $00
$1B30.byte$7e, $60, $60, $7c, $60, $60, $60, $00
$1B38.byte$3e, $60, $c0, $ce, $c6, $66, $3e, $00
$1B40.byte$c6, $c6, $c6, $fe, $c6, $c6, $c6, $00
$1B48.byte$7e, $18, $18, $18, $18, $18, $7e, $00
$1B50.byte$06, $06, $06, $06, $06, $c6, $7c, $00
$1B58.byte$c6, $cc, $d8, $f0, $f8, $dc, $ce, $00
$1B60.byte$60, $60, $60, $60, $60, $60, $7e, $00
$1B68.byte$c6, $ee, $fe, $fe, $d6, $c6, $c6, $00
$1B70.byte$c6, $e6, $f6, $fe, $de, $ce, $c6, $00
$1B78.byte$7c, $c6, $c6, $c6, $c6, $c6, $7c, $00
$1B80.byte$fc, $c6, $c6, $c6, $fc, $c0, $c0, $00
$1B88.byte$7c, $c6, $c6, $c6, $ce, $cc, $7a, $00
$1B90.byte$fc, $e6, $c6, $cc, $f8, $dc, $ce, $00
$1B98.byte$78, $cc, $c0, $7c, $06, $c6, $7c, $00
$1BA0.byte$7e, $18, $18, $18, $18, $18, $18, $00
$1BA8.byte$c6, $c6, $c6, $c6, $c6, $c6, $7c, $00
$1BB0.byte$c6, $c6, $c6, $ee, $7c, $38, $10, $00
$1BB8.byte$c6, $c6, $d6, $fe, $fe, $6c, $44, $00
$1BC0.byte$c6, $ee, $7c, $38, $7c, $ee, $c6, $00; x
$1BC8.byte$66, $66, $66, $3c, $18, $18, $18; y
$1BCF.fill49, $00
$1C00track_column_colors.byte$00, $49, $4b, $4d, $49, $4b, $4d, $49; x-ref: $2083
$1C08.byte$4b, $4d, $49, $4b, $4d, $49, $4b, $4d
$1C10.byte$00, $00, $00, $00, $00, $00, $00
$1C17.fill16, $4f
$1C27.byte$00
$1C28track_tile_indices.byte$00, $00, $0a, $05, $06, $02, $00, $0b; x-ref: $2091, $2D92
$1C30.byte$0b, $51, $0b, $0b, $00, $09, $01, $07
$1C38.byte$08, $02, $00, $0b, $0b, $0b, $0b, $0b
$1C40.byte$0b, $0b, $00, $04, $05, $03, $04, $00
$1C48.byte$0b, $0b, $0b, $0c, $0b, $0b, $0b, $00
$1C50screen_row_addr_lo.byte<$0c00, <$0c28, <$0c50, <$0c78, <$0ca0; x-ref: $21DF, $220D
$1C55.byte<$0cc8, <$0cf0, <$0d18, <$0d40, <$0d68
$1C5A.byte<$0d90, <$0db8, <$0de0, <$0e08, <$0e30
$1C5F.byte<$0e58, <$0e80, <$0ea8, <$0ed0, <$0ef8
$1C64.byte<$0f20, <$0f48, <$0f70, <$0f98
$1C68screen_row_addr_hi.byte>$0c00, >$0c28, >$0c50, >$0c78, >$0ca0; x-ref: $21E4, $2212
$1C6D.byte>$0cc8, >$0cf0, >$0d18, >$0d40, >$0d68
$1C72.byte>$0d90, >$0db8, >$0de0, >$0e08, >$0e30
$1C77.byte>$0e58, >$0e80, >$0ea8, >$0ed0, >$0ef8
$1C7C.byte>$0f20, >$0f48, >$0f70, >$0f98
$1C80debris_vel_x.byte$ff, $ff, $ff, $00, $01, $01, $01, $00; x-ref: $2448
$1C88debris_vel_y.byte$ff, $00, $01, $01, $01, $00, $ff, $ff; x-ref: $2433
obstacle_color_normal
$1C90.byte$3a, $4d, $5f, $2e, $3a, $4d, $5f, $2e; x-ref: $2249
.encode
.enc"none"
$1C98level_select_text.text"level", $00, "RR"; x-ref: $2DD0
.endencode
$1CA0obstacle_col_deltas.byte$00, $01, $01, $fe, $01, $01, $00, $00; x-ref: $22F3
$1CA8obstacle_color_wide.fill8, $79; x-ref: $2294
$1CB0.byte$69, $59, $49, $39, $29, $19, $09, $09
$1CB8.byte$09, $09, $19, $29, $39, $49, $59, $69
$1CC0.fill8, $79
$1CC8bike_char_base.byte$70, $79, $82, $8b; x-ref: $2472, $247D
$1CCCbike_subpos_offset.byte$00, $24, $48, $6c; x-ref: $2487, $248F
debris_start_x_positions
$1CD0.byte$18, $18, $18, $10, $08, $08, $08, $10; x-ref: $2672
$1CD8.byte$13, $0b, $03, $03, $03, $0b, $13, $13
.encode
.enc"none"
; Text for 'game over' or course end
$1CE0course_end_text.text"game", $00, "over", $00, "^^^^", $00, $00; x-ref: $26CE
; Text for 'bonus for time left'
$1CF0time_bonus_text.text"bonus", $00, "for", $00, "time", $00, "left", $00, $00, "RRR"; x-ref: $2966
; Text for 'bikes left bonus'
bikes_left_bonus_text
$1D08.text"bikes", $00, "left", $00, "bonus", $00, $00, $00, $00, $00; x-ref: $296C
.endencode
$1D1D.byte$52, $52, $52
$1D20.fill24, $00
; First character of the bonus score amount
$1D38bonus_value_char1.byte$00, $53, $54, $56, $59, $5b, $00, $00; x-ref: $299E
; Second character of the bonus score amount
$1D40bonus_value_char2.byte$00, $52, $57, $52, $57, $5b, $00, $00; x-ref: $29A4
.encode
.enc"none"
; Text for 'extra bike awarded'
extra_bike_awarded_text
$1D48.text"extra", $00, "bike", $00, "awarded\\\\\\"; x-ref: $2A60
; Text for 'five bikes left still'
$1D60five_bikes_left_text.text"five", $00, "bikes", $00, "left", $00, "still\\\"; x-ref: $2A72
.endencode
; Color attributes for obstacles/characters
$1D78obstacle_attributes.byte$00, $00, $67, $67, $67, $67, $00, $67; x-ref: $209F
$1D80.byte$67, $67, $67, $67, $00, $35, $35, $35
$1D88.byte$35, $35, $00, $35, $35, $35, $35, $35
$1D90.byte$35, $35, $00, $44, $44, $44, $44, $00
$1D98.fill8, $44
; Offsets into spawn tables for different types
$1DA0spawn_type_offsets.byte$10, $10, $10, $10, $00, $00, $00, $00; x-ref: $2C67
$1DA8.fill8, $20
; Bitmasks to check if spawning an obstacle is allowed
$1DB0spawn_allowed_masks.byte$80, $40, $20, $10, $80, $40, $20; x-ref: $2C6E
$1DB7.fill9, $10
; Course number string digits (units). Used for the title selection screen. Values are screen character codes ($52='0', $53='1', etc.)
$1DC0course_digits_units.byte$53, $54, $55, $56, $57, $58, $59, $5a; x-ref: $2DFB
$1DC8.byte$5b, $52, $53, $54, $55, $56, $57, $58
; Course number string digits (tens). Used for the title selection screen. Values are screen character codes ($52='0', $53='1', etc.)
$1DD0course_digits_tens.fill9, $52; x-ref: $2E01
$1DD9.byte$53, $53, $53, $53, $53, $53, $53
$1DE0course_scroll_speeds.byte$00, $40, $20, $20, $20, $20, $40, $20; x-ref: $2A44, $2DAA
$1DE8.byte$80, $40, $80, $40, $40, $80, $c0, $40
; Per-course random threshold for obstacle spawning. A PRNG roll must be < this threshold to spawn. $00 means no spawns, $FF means max spawns.
spawn_prob_thresholds
$1DF0.byte$00, $ff, $10, $10, $10, $18, $10, $08; x-ref: $2C7B
$1DF8.byte$ff, $08, $08, $10, $10, $04, $40, $10
$1E00.fill16, $00
; Number of track scroll tile steps per frame per course (1-5). Used by update_scroll_tiles to control track speed, and displayed in the speed footer.
$1E10course_scroll_steps.byte$01, $01, $02, $02, $02, $03, $03, $03; x-ref: $29B9, $2A8A, $2D6D
$1E18.byte$04, $04, $04, $04, $05, $05, $05, $05
; Obstacle spawn configuration table. Indexed by course_index + f1DA0[spawn_type]. Specifies if a spawn is allowed (when ANDed with mask) and the cooldown timer (lower 4 bits + 6).
obstacle_spawn_params
$1E20.byte$00, $00, $1f, $00, $00, $1c, $7c, $fc; x-ref: $2C6B, $2C8E, $2CD2
$1E28.byte$86, $f6, $00, $f4, $00, $c0, $80, $f4
$1E30.byte$00, $70, $70, $70, $00, $70, $70, $70
$1E38.byte$80, $f0, $00, $70, $00, $00, $80, $f0
$1E40.byte$00, $00, $00, $00, $1c, $00, $00, $3c
$1E48.byte$00, $00, $5f, $00, $7c, $78, $f0, $74
; Low bytes of 16-bit pointers to the level data for each course.
$1E50levels_ptr_lo.byte<level_0, <level_1, <level_2, <level_3, <level_4; x-ref: $2E1B
$1E55.byte<level_5, <level_6, <level_7, <level_8, <level_9
$1E5A.byte<level_10, <level_11, <level_12, <level_13, <level_14
$1E5F.byte<level_15
$1E60levels_ptr_hi.byte>level_0, >level_1, >level_2, >level_3, >level_4; x-ref: $2E20
$1E65.byte>level_5, >level_6, >level_7, >level_8, >level_9
$1E6A.byte>level_10, >level_11, >level_12, >level_13, >level_14
$1E6F.byte>level_15
$1E70course_anim_frames.byte$14, $1e, $1e, $1e, $20, $1e, $1e, $20; x-ref: $2A21, $2A3F
$1E78.byte$10, $24, $1e, $24, $2c, $2c, $1c, $30
; ROM tile color descriptor table (64 entries).
; Bits 0-1: used for sprite shape selection
; Bits 2-5: foreground TED color index offset by $33.
; Bits 6-7: background color field.
tile_color_descriptors
$1E80.byte$18, $88, $88, $88, $84, $84, $84, $88; x-ref: $23F3, $23FE
$1E88.byte$88, $88, $84, $84, $84, $14, $10, $0c
$1E90.byte$08, $04, $10, $10, $10, $10, $0c, $0c
$1E98.byte$0c, $0c, $54, $54, $54, $50, $50, $50
$1EA0.byte$4c, $4c, $4c, $48, $48, $48, $94, $94
$1EA8.byte$94, $90, $90, $90, $14, $10, $0c, $08
$1EB0.byte$04, $14, $10, $0c, $08, $04, $90, $88
$1EB8.byte$88, $90, $00, $00, $00, $18, $18, $18
$1EC0tile_chars_row0.byte$00, $00, $00, $00, $14, $15, $16, $00; x-ref: $23A3
$1EC8.byte$00, $00, $20, $21, $22, $00, $00, $00
$1ED0.byte$00, $03
$1ED2.fill30, $00
$1EF0.byte$10, $00, $00, $00, $00, $05, $00, $00
$1EF8.byte$00, $00, $00, $00, $00, $12, $12, $13
$1F00tile_chars_row1.byte$00, $14, $15, $16, $17, $18, $19, $20; x-ref: $23A9
$1F08.byte$21, $22, $23, $24, $25, $00, $00, $00
$1F10.byte$03, $04
$1F12.fill17, $00
$1F23.byte$09, $0a, $0b
$1F26.fill9, $00
$1F2F.byte$10, $10, $00, $00, $00, $05, $04, $00
$1F37.byte$0c, $0d, $00, $00, $00, $00, $00, $00
$1F3F.byte$00
$1F40tile_chars_row2.byte$00, $17, $18, $19, $1a, $1b, $1c, $23; x-ref: $23AF
$1F48.byte$24, $25, $26, $27, $28, $00, $00, $03
$1F50.byte$04, $05, $00, $00, $00, $00, $2c, $2d
$1F58.byte$2e, $2f, $00, $00, $00, $00, $00, $00
$1F60.byte$09, $0a, $0b, $04, $05, $04
$1F66.fill8, $00
$1F6E.byte$10, $10, $10, $00, $00, $05, $04, $05
$1F76.byte$00, $0e, $0f, $00, $00, $00, $00, $00
$1F7E.byte$00, $00
$1F80tile_chars_row3.byte$00, $1a, $1b, $1c, $1d, $1e, $1f, $26; x-ref: $23B5
$1F88.byte$27, $28, $29, $2a, $2b, $00, $03, $04
$1F90.byte$05, $04, $2c, $2d, $2e, $2f, $30, $31
$1F98.byte$32, $33, $00, $00, $00, $09, $0a, $0b
$1FA0.byte$04, $05, $04, $05, $04, $05, $00, $00
$1FA8.byte$00, $06, $07, $08, $00, $10, $10, $10
$1FB0.byte$10, $00, $05, $04, $05, $04, $0c, $0d
$1FB8.byte$0c, $0d, $00, $00, $00, $00, $00, $00
$1FC0tile_chars_row4.byte$00, $1d, $1e, $1f, $11, $11, $11, $29; x-ref: $23BB
$1FC8.byte$2a, $2b, $11, $11, $11, $02, $11, $11
$1FD0.byte$11, $11, $30, $31, $32, $33, $11, $11
$1FD8.byte$11, $11, $09, $0a, $0b
$1FDD.fill9, $11
$1FE6.byte$06, $07, $08, $11, $11, $11, $10, $10
$1FEE.byte$10, $10, $10, $11, $11, $11, $11, $11
$1FF6.byte$0e, $0f, $0e, $0f, $00, $00, $00, $00
$1FFE.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main entry point for Kikstart (Commodore Plus/4).
; Performs a full cold-start initialization:
; 1. TED chip reset: colors set, audio muted, video matrix configured.
; 2. Screen RAM (4 pages) cleared; attribute RAM filled with color $39.
; 3. Game IRQ handler installed at $0314/$0315 (KERNAL shadow vector).
; 4. Track/tile color tables initialized from ROM data tables.
; 5. TED Timer 1 configured and started to drive per-frame IRQs.
; 6. IRQs enabled; falls into the raster-synced main game loop.
;
; The main loop is 3-phase, synchronized to the TED Timer 1 IRQ (handled
; by the routine at p2118). The IRQ increments RAM_ZPVEC1 each tick:
; Phase 1 ($01): 8 game-logic subsystem update subroutines.
; Phase 2 ($02): 7 more update subroutines, then repeats from phase 1.
; Phase 0 ($00): Internal raster task dispatched inside the IRQ handler.
;
; Inputs: None (cold start)
; Outputs: None
; Side Effects: TED chip fully configured, IRQ vector patched to p2118,
; TED Timer 1 running, game loop active indefinitely.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$200078startsei; Disable IRQs during hardware init
$2001a2 ffldx#$ff
$20039atxs; Init stack pointer to top of page $01
$2004a9 00lda#$00
$20068d 15 ffsta$ff15; TED: background color = black
$2009a9 71lda#$71
$200B8d 16 ffsta$ff16; TED: color register #1 = $71
$200Ea9 39lda#$39
$20108d 17 ffsta$ff17; TED: color register #2 = $39
$2013a9 00lda#$00
$20158d 19 ffsta$ff19; TED: color reg #4 = $00 (also used as IRQ frame-sync flag)
$2018a9 00lda#$00
$201A8d 11 ffsta$ff11; TED: mute audio output (volume = 0)
$201D8d 0e ffsta$ff0e; TED: silence voice 1 frequency (low byte)
$20208d 12 ffsta$ff12; TED: silence voice 1 frequency (high bits)
$20238d 0f ffsta$ff0f; TED: silence voice 2 frequency (low byte)
$20268d 10 ffsta$ff10; TED: silence voice 2 frequency (high bits)
$2029a9 10lda#$10
$202B8d 13 ffsta$ff13; TED: clock status = $10
$202Ea9 08lda#$08
$20308d 14 ffsta$ff14; TED: video matrix/color memory base address = $08
$2033a9 1blda#$1b
$20358d 06 ffsta$ff06; TED control reg 1: $1B (standard bitmap/text mode)
$2038a9 98lda#$98
$203A8d 07 ffsta$ff07; TED control reg 2: $98
$203Da2 00ldx#$00
$203Fa9 00b_203Flda#$00; Clear screen RAM ; x-ref: $205C
$20419d 00 0cstaSCREEN_RAM_R0C0,x
$20449d 00 0dstaSCREEN_RAM_R6C16,x
$20479d 00 0estaSCREEN_RAM_R12C32,x
$204A9d 00 0fstaSCREEN_RAM_R19C8,x
$204Da9 39lda#$39; Color RAM with attribute $39
$204F9d 00 08staCOLOR_RAM_R0C0,x
$20529d 00 09staCOLOR_RAM_R6C16,x
$20559d 00 0astaCOLOR_RAM_R12C32,x
$20589d 00 0bstaCOLOR_RAM_R19C8,x
$205Be8inx
$205Cd0 e1bneb_203F
$205Ea9 18lda#<irq_handler; Install game IRQ handler: low byte of p2118 → $0314
$20608d 14 03stavec_irq_lo
$2063a9 21lda#>irq_handler; High byte of p2118 → $0315 (KERNAL IRQ shadow vector)
$20658d 15 03stavec_irq_hi
$2068a2 27ldx#$27; Init 40 entries ($27...$00) of track/tile color & char pointer tables
$206Aa9 01b_206Alda#$01; x-ref: $20A9
$206C9d 48 0fstaSCREEN_RAM_R21C0,x; Track data init: store $01
$206Fa9 3dlda#$3d
$20719d 48 0bstaCOLOR_RAM_R21C0,x
$20749d 20 0bstaCOLOR_RAM_R20C0,x
$20779d f8 0astaCOLOR_RAM_R19C0,x
$207A9d d0 0astaCOLOR_RAM_R18C0,x
$207D9d a8 0astaCOLOR_RAM_R17C0,x
$20809d 80 0astaCOLOR_RAM_R16C0,x
$2083bd 00 1cldatrack_column_colors,x; Load bitmap color for this column from BMCOLR table
$20869d 98 0fstaSCREEN_RAM_R23C0,x; Store to display color table
$2089f0 03beqb_208E
$208B18clc; Non-zero: add 1 to color for alternate-row shading
$208C69 01adc#$01
$208E9d c0 0fb_208EstaSCREEN_RAM_R24C0,x; x-ref: $2089
$2091bd 28 1cldatrack_tile_indices,x; Load character pointer from ROM table at $1C28
$20949d 00 0cstaSCREEN_RAM_R0C0,x; Set TED screen character pointer
$2097f0 03beqb_209C
$209918clc; Non-zero: add $14 (20) — row-offset into char/tile data
$209A69 14adc#$14
$209C9d 28 0cb_209CstaSCREEN_RAM_R1C0,x; x-ref: $2097
$209Fbd 78 1dldaobstacle_attributes,x; Load attribute byte from ROM table at $1D78
$20A29d 00 08staCOLOR_RAM_R0C0,x; Set TED attribute byte for this column
$20A59d 28 08staCOLOR_RAM_R1C0,x
$20A8cadex
$20A910 bfbplb_206A
$20AB20 14 26jsrreset_basic_runtime_state; Additional game state initialization (called once)
$20AEa9 fflda#$ff
$20B085 2fstazp_course_index
$20B2a9 00lda#$00
$20B485 30stazp_lives_counter
$20B685 32stazp_sprite_queue_count
$20B88d 31 0cstaSCREEN_RAM_R1C9
$20BBeanop
$20BCa9 fflda#$ff
$20BE85 03stazp_frame_phase; Frame phase counter = $FF (wraps to 0 on first IRQ increment)
$20C0a9 14lda#$14
$20C285 06stazp_raster_target
$20C4a9 12lda#$12; TED Timer 1 latch low byte = $12 (controls IRQ rate)
$20C68d 0b ffsta$ff0b; TED Timer 1 control: enable + start — IRQs will fire each frame
$20C9a9 02lda#$02
$20CB8d 0a ffsta$ff0a
$20CE58cli; Enable IRQs — game loop begins
$20CFad 19 ffb_20CFlda$ff19; Spin-wait: read $FF19 to acknowledge TED IRQ, then check phase ; x-ref: $20D6, $2112
$20D2a5 03ldazp_frame_phase
$20D4c9 01cmp#$01; Wait until IRQ advances frame phase to 1
$20D6d0 f7bneb_20CF
$20D8e6 3finczp_frame_counter; Increment global frame counter
$20DA20 04 23jsrupdate_obstacle_positions; --- Phase 1 subsystem updates begin ---
$20DD20 24 25jsrupdate_obstacle_physics
$20E020 ec 23jsrupdate_tile_colors
$20E320 04 27jsrread_joystick
$20E620 88 25jsrdraw_hud_progress_bar
$20E920 d0 25jsrhandle_game_mode
$20EC20 ec 2ajsrsound_engine_tick
$20EF20 4c 2cjsrobstacle_spawn_and_collide
$20F2eanop
$20F3ad 19 ffb_20F3lda$ff19; Spin-wait: acknowledge TED IRQ, wait until phase advances to 2 ; x-ref: $20FA
$20F6a5 03ldazp_frame_phase
$20F8c9 02cmp#$02; Wait until IRQ advances frame phase to 2
$20FAd0 f7bneb_20F3
$20FC20 d8 21jsrsprite_erase; --- Phase 2 subsystem updates begin ---
$20FF20 c0 21jsrselect_raster_color
$210220 d8 24jsrtrack_scroll_engine
$210520 10 24jsrrender_debris_or_bike
$210820 9c 23jsrrender_track_tiles
$210B20 3c 22jsrrender_obstacles
$210E20 04 22jsrsprite_draw
$211118clc
$211290 bbbccb_20CF; Unconditional loop back to phase-1 wait (BCC always taken)
$2114.byte$00, $00, $00, $00
; TED Timer 1 IRQ handler.
; Fires each raster frame. Acknowledges the TED timer IRQ, increments the
; 3-phase frame counter in RAM_ZPVEC1 (wraps 0→1→2→0), then dispatches
; raster state machine tasks for phase 0. Phases 1 and 2 are handled by
; the spin-waits in the main loop (main_init). Non-timer IRQs are chained
; to the KERNAL handler at $FCC3.
$2118ad 09 ffirq_handlerlda$ff09; Read $FF09: check which IRQ source fired ; x-ref: $205E, $2063
$211B29 02and#$02; Bit 1 = TED Timer 1 IRQ
$211Dd0 03bneb_2122
$211F4c c3 fcjmp$fcc3; Not our IRQ — chain to KERNAL handler at $FCC3
$2122a9 02b_2122lda#$02; x-ref: $211D
$21248d 09 ffsta$ff09; Acknowledge TED Timer 1 IRQ (write $02 to $FF09)
$2127eanop
$2128e6 03inczp_frame_phase; Advance frame phase counter: 0→1→2→0
$212Aa5 03ldazp_frame_phase
$212Cc9 03cmp#$03; Wrap counter at 3
$212E90 04bccb_2134
$2130a9 00lda#$00
$213285 03stazp_frame_phase
$2134a5 03b_2134ldazp_frame_phase; x-ref: $212E
$2136c9 00cmp#$00; Phase 0: dispatch raster task A
$2138f0 0cbeqb_2146
$213Ac9 01cmp#$01; Phase 1: dispatch raster task B
$213Cf0 13beqb_2151
$213Ec9 02cmp#$02; Phase 2: dispatch raster task C
$2140f0 1abeqb_215C
$21424c c3 fcjmp$fcc3
$2145eanop
$2146a5 3db_2146ldazp_raster_color; x-ref: $2138
$214848pha
$2149a0 83ldy#$83
$214Ba9 90lda#$90
$214D05 04orazp_raster_split_a
$214Fd0 12bneb_2163
$2151a9 10b_2151lda#$10; x-ref: $213C
$215348pha
$2154a0 b3ldy#$b3
$2156a9 90lda#$90
$215805 05orazp_raster_split_b
$215Ad0 07bneb_2163
$215Ca9 18b_215Clda#$18; x-ref: $2140
$215E48pha
$215Fa0 14ldy#$14
$2161a9 90lda#$90
$2163ae 1d ffb_2163ldx$ff1d; x-ref: $214F, $215A, $2168
$2166e4 06cpxzp_raster_target
$2168d0 f9bneb_2163
$216A8d 07 ffsta$ff07
$216D68pla
$216E8d 13 ffsta$ff13
$217184 06styzp_raster_target
$217398tya
$217438sec
$2175e9 02sbc#$02
$21778d 0b ffsta$ff0b
$217Aa9 02lda#$02
$217C8d 0a ffsta$ff0a
$217F4c c3 fcjmp$fcc3
$2182.fill14, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Title/attract-mode collision detector. Checks whether any scrolling
; obstacle (slots 4,3,1,0; slot 2 skipped as the player slot) has reached
; the left-edge sentinel position ($0B in $0C07,x). If any slot hits the
; sentinel, exits early (obstacle still in transit). Once all slots are
; checked with none at $0B AND player_active is clear, arms the crash:
; sets player_active=$FF, lives_counter=1, and calls trigger_crash (debris
; explosion + mode 5). Called every frame from the title scroll loop.
;
; Inputs: $0C07,x (screen X positions for 5 slots), player_active
; Outputs: player_active=$FF, lives_counter=1 (only when triggered)
; Side Effects: Calls trigger_crash (debris, sound, game_mode=5)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
check_title_obstacle_collision
$2190eanop; x-ref: $2369
$2191eanop
$2192eanop
$2193a2 04ldx#$04; check slots 4,3,1,0 (skip 2 = player slot)
$2195e0 02b_2195cpx#$02; x-ref: $21A1
$2197f0 07beqb_21A0
$2199bd 07 0cldaSCREEN_RAM_R0C7,x; has this obstacle reached the left-edge sentinel ($0B)?
$219Cc9 0bcmp#$0b
$219Ed0 12bner_21B2
$21A0cab_21A0dex; x-ref: $2197
$21A110 f2bplb_2195
$21A3a5 3bldazp_player_active; all slots clear: is player already active? -> skip if so
$21A5d0 0bbner_21B2
$21A7a9 fflda#$ff; arm crash: mark player active, set 1 lap, trigger debris + mode 5
$21A985 3bstazp_player_active
$21ABa9 01lda#$01
$21AD85 30stazp_lives_counter
$21AF20 ac 26jsrtrigger_crash
$21B260r_21B2rts; x-ref: $219E, $21A5
$21B3.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Conditionally clears raster_split_a when the game is in a stationary mode.
; If raster_colour==$18 (dark orange, set by select_raster_colour for modes
; 0/4/6 — but specifically when stationary), zeroes raster_split_a to
; prevent a spurious raster split from leaking through during non-riding
; modes. Called after the scroll timer updates raster_split_a; this
; guards against that value being applied when no split is wanted.
;
; Inputs: raster_colour (set by select_raster_colour), raster_split_a
; Outputs: raster_split_a=0 (only if raster_colour==$18)
; Side Effects: None (split value read later by IRQ handler)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clear_split_if_stationary
$21B4a5 3dldazp_raster_color; is raster colour $18 (stationary mode)? ; x-ref: $251D
$21B6c9 18cmp#$18
$21B8d0 04bner_21BE
$21BAa9 00lda#$00; yes -> zero the raster split (no split during stationary modes)
$21BC85 04stazp_raster_split_a
$21BE60r_21BErts; x-ref: $21B8
$21BF.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Selects the playfield background colour for the raster split.
; Modes 0 (normal ride), 4 (track scroll), 6 (results screen) use $18
; (TED: luminance 1, colour 8 = dark orange/brown).
; All other modes use $10 (TED: luminance 1, colour 0 = black).
; The value is read by irq_handler and written to $FF13 (TED background
; colour register) during raster phase 0.
;
; Inputs: game_mode
; Outputs: raster_colour ($10 or $18)
; Side Effects: None (colour applied later by IRQ handler)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21C0a5 02select_raster_colorldazp_game_mode; x-ref: $20FF
$21C2c9 00cmp#GameMode.NORMAL_RIDE; mode 0 (normal ride) -> dark orange
$21C4f0 0cbeqb_21D2
$21C6c9 04cmp#GameMode.TRACK_SCROLL; mode 4 (track scroll) -> dark orange
$21C8f0 08beqb_21D2
$21CAc9 06cmp#GameMode.RESULTS_SCREEN; mode 6 (results screen) -> dark orange
$21CCf0 04beqb_21D2
$21CEa9 10lda#$10; $10 = black background (active gameplay)
$21D0d0 02bneb_21D4
$21D2a9 18b_21D2lda#$18; $18 = dark orange/brown background (stationary modes) ; x-ref: $21C4, $21C8, $21CC
$21D485 3db_21D4stazp_raster_color; store for IRQ handler to apply during raster phase 0 ; x-ref: $21D0
$21D660rts
$21D7.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sprite erase - clears previous frame's character sprites from screen.
; Iterates the sprite queue backwards (sprite_queue_count..1). For each entry:
; 1. Looks up screen row address from f1C50/f1C68 (24-entry row table)
; using the row index stored in f03FF[Y].
; 2. Writes $00 (blank) to screen RAM at (ptr),column.
; 3. Toggles hi byte bit 2 (EOR #$04) to switch from screen RAM ($0Cxx)
; to colour RAM ($08xx) and writes $3D (default colour attribute).
; 4. Decrements sprite_queue_count and repeats until zero.
;
; First step of the double-buffer sprite technique:
; erase old (this) -> run game logic -> draw new (sprite_draw).
;
; Inputs: sprite_queue_count, f03FF[1..N] (row indices), f042F[1..N] (col)
; Outputs: Screen + colour RAM cleared at previous sprite positions,
; sprite_queue_count = 0
; Side Effects: Direct writes to screen RAM ($0C00+) and colour RAM ($0800+)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$21D8a4 32sprite_eraseldyzp_sprite_queue_count; any sprites in the erase queue? ; x-ref: $20FC, $21FD
$21DAf0 23beqr_21FF; queue empty -> nothing to erase
$21DCbe ff 03ldxsprite_q_row_m1,y; X = screen row index from sprite queue
$21DFbd 50 1cldascreen_row_addr_lo,x; screen row address lo (40-col row table)
$21E285 festazp_blit_color_ptr
$21E4bd 68 1cldascreen_row_addr_hi,x; screen row address hi ($0C-$0F pages)
$21E785 ffstazp_blit_char_idx
$21E9b9 2f 04ldasprite_q_col_m1,y; Y = column offset within the row
$21ECa8tay
$21EDa9 00lda#$00
$21EF91 festa(zp_blit_color_ptr),y; write $00 (blank char) to screen RAM
$21F1a5 ffldazp_blit_char_idx
$21F349 04eor#$04; toggle bit 2: screen RAM -> colour RAM ($0Cxx<->$08xx)
$21F585 ffstazp_blit_char_idx
$21F7a9 3dlda#$3d; $3D = default colour attribute
$21F991 festa(zp_blit_color_ptr),y; restore default colour in colour RAM
$21FBc6 32deczp_sprite_queue_count; pop entry, loop until all sprites erased
$21FDd0 d9bnesprite_erase
$21FF60r_21FFrts; x-ref: $21DA
$2200.byte$00, $00, $00, $00
$2204a0 00sprite_drawldy#$00; x-ref: $210E
$2206c4 32b_2206cpyzp_sprite_queue_count; x-ref: $2238
$2208b0 2dbcsb_2237
$220Abe 00 04ldxsprite_q_row,y
$220Dbd 50 1cldascreen_row_addr_lo,x
$221085 fcstazp_blit_col
$2212bd 68 1cldascreen_row_addr_hi,x
$221585 fdstazp_blit_row
$221798tya
$221848pha
$2219b9 60 04ldasprite_q_char,y
$221C85 ffstazp_blit_char_idx
$221Eb9 90 04ldasprite_q_color,y
$222185 festazp_blit_color_ptr
$2223b9 30 04ldasprite_q_col,y
$2226a8tay
$2227a5 ffldazp_blit_char_idx
$222991 fcsta(zp_blit_col),y
$222Ba5 fdldazp_blit_row
$222D49 04eor#$04
$222F85 fdstazp_blit_row
$2231a5 feldazp_blit_color_ptr
$223391 fcsta(zp_blit_col),y
$223568pla
$2236a8tay
$2237c8b_2237iny; x-ref: $2208
$223810 ccbplb_2206
$223A60rts
$223B.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Obstacle sprite renderer - draws all active obstacles as character sprites.
; Iterates 4 slots (X=3..0), dispatching to normal or wide renderers.
;
; Normal obstacles (type 0-7): 2x3 character grid
; Colour from f1C90[type]. Screen col = pos_hi >> 2.
; Char base = ((sub_pos | type_hi_bit) * 6) + $38.
; Blits 6 cells via sprite_queue_push + obstacle_blit_advance.
; Column deltas from f1CA0: (0,+1,+1,-2,+1,+1) = two 3-wide rows.
;
; Wide obstacles (type >= 8): 4-tall vertical column
; Screen col = (flags & $03) + 3.
; Colour from f1CA8[vel & $1F] (32-entry pulsating gradient).
; Char base = (flags & $04) + $68 (two animation variants).
; Blits 4 vertical cells, each at obstacle_life + Y.
; If bit 4 set: render_wide_flash_bar adds 3-char horizontal bar.
;
; Inputs: obstacle_flags/pos_hi/life/vel[0..3], f1C90, f1CA0, f1CA8
; Outputs: Sprite queue entries via sprite_queue_push
; Side Effects: None (deferred rendering)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$223Ca2 03render_obstaclesldx#$03; x-ref: $210B
$223Eb5 10b_223Eldazp_obstacle_flags,x; x-ref: $22B9
$2240c9 ffcmp#$ff; $FF = dead slot, skip to next
$2242f0 74beqb_22B8
$2244c9 08cmp#$08; type >= 8? -> wide obstacle renderer
$2246b0 3ebcsb_2286
$2248a8tay
$2249b9 90 1cldaobstacle_color_normal,y; --- Normal: colour from f1C90[type] ---
$224C85 festazp_blit_color_ptr
$224Eb5 18ldazp_obstacle_pos_hi,x
$22504alsra; screen col = pos_hi >> 2
$22514alsra
$225285 fcstazp_blit_col
$2254b5 18ldazp_obstacle_pos_hi,x
$225629 03and#$03; sub-pixel X = pos_hi & $03 (4 animation phases)
$225885 ffstazp_blit_char_idx
$225Ab5 10ldazp_obstacle_flags,x
$225Cc9 04cmp#$04; type >= 4? -> use alternate animation set (bit 2)
$225E90 06bccb_2266
$2260a5 ffldazp_blit_char_idx
$226209 04ora#$04
$226485 ffstazp_blit_char_idx
$226606 ffb_2266aslzp_blit_char_idx; char_idx = idx * 6 + $38 (via ASL + ADC trick) ; x-ref: $225E
$2268a5 ffldazp_blit_char_idx
$226A0aasla
$226B18clc
$226C65 ffadczp_blit_char_idx
$226E18clc
$226F69 38adc#$38
$227185 ffstazp_blit_char_idx
$2273b5 14ldazp_obstacle_life,x; vertical position = obstacle lifetime counter
$227585 fdstazp_blit_row
$2277a0 00ldy#$00
$227920 c8 23b_2279jsrsprite_queue_push; blit 6 cells: 2x3 grid (3 cols x 2 rows) ; x-ref: $2281
$227C20 f0 22jsrobstacle_blit_advance
$227Fc0 06cpy#$06
$2281d0 f6bneb_2279
$228318clc
$228490 32bccb_22B8
$2286b5 10b_2286ldazp_obstacle_flags,x; --- Wide: screen col = (flags & $03) + 3 --- ; x-ref: $2246
$228829 03and#$03
$228A18clc
$228B69 03adc#$03
$228D85 fcstazp_blit_col
$228Fb5 20ldazp_obstacle_vel,x
$229129 1fand#$1f; colour from f1CA8[vel & $1F] (pulsating gradient)
$2293a8tay
$2294b9 a8 1cldaobstacle_color_wide,y
$229785 festazp_blit_color_ptr
$2299b5 10ldazp_obstacle_flags,x
$229B29 04and#$04
$229D18clc
$229E69 68adc#$68; char base = (flags & $04) + $68 (2 anim variants)
$22A085 ffstazp_blit_char_idx
$22A2a0 00ldy#$00
$22A498b_22A4tya; blit 4 vertical cells at life+0, life+1, life+2, life+3 ; x-ref: $22B2
$22A518clc
$22A675 14adczp_obstacle_life,x
$22A885 fdstazp_blit_row
$22AA20 c8 23jsrsprite_queue_push
$22ADc8iny
$22AEe6 ffinczp_blit_char_idx
$22B0c0 04cpy#$04
$22B2d0 f0bneb_22A4
$22B420 c0 22jsrrender_wide_flash_bar; render flash bar if bit 4 set (sound-triggered)
$22B7eanop
$22B8cab_22B8dex; x-ref: $2242, $2284
$22B910 83bplb_223E
$22BB60rts
$22BC.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Wide obstacle flash bar - renders a 3-char horizontal bar when bit 4 of
; obstacle_flags is set (sound-triggered visual effect).
; Chars $34-$36 (cycling), colour $71, at blit_col+1, row = life+1.
; Extends rightward until column >= $10.
;
; Inputs: obstacle_flags[X] (bit 4), obstacle_life[X], blit_col
; Outputs: Sprite queue entries
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
render_wide_flash_bar
$22C0b5 10ldazp_obstacle_flags,x; x-ref: $22B4
$22C229 10and#$10; bit 4 clear? -> no flash bar
$22C4f0 28beqr_22EE
$22C6e6 fcinczp_blit_col
$22C8a9 34lda#$34; chars $34-$36 (cycling), colour $71
$22CA85 ffstazp_blit_char_idx
$22CCb5 14ldazp_obstacle_life,x
$22CE18clc
$22CF69 01adc#$01
$22D185 fdstazp_blit_row
$22D3a9 71lda#$71
$22D585 festazp_blit_color_ptr
$22D720 c8 23b_22D7jsrsprite_queue_push; blit bar chars rightward until col >= $10 ; x-ref: $22EC
$22DAe6 ffinczp_blit_char_idx
$22DCa5 ffldazp_blit_char_idx
$22DEc9 37cmp#$37
$22E0d0 04bneb_22E6
$22E2a9 34lda#$34
$22E485 ffstazp_blit_char_idx
$22E6e6 fcb_22E6inczp_blit_col; x-ref: $22E0
$22E8a5 fcldazp_blit_col
$22EAc9 10cmp#$10
$22EC90 e9bccb_22D7
$22EE60r_22EErts; x-ref: $22C4
$22EF.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Normal obstacle blit advance helper.
; Increments Y and blit_char_idx. Applies column delta from f1CA0[Y]
; (pattern: 0,+1,+1,-2,+1,+1 = two 3-wide rows). Increments blit_row
; when Y reaches 3 (start of second row).
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
obstacle_blit_advance
$22F0c8iny; advance Y, char_idx; apply col delta from f1CA0[Y] ; x-ref: $227C
$22F1e6 ffinczp_blit_char_idx
$22F3b9 a0 1cldaobstacle_col_deltas,y
$22F618clc
$22F765 fcadczp_blit_col
$22F985 fcstazp_blit_col
$22FBc0 03cpy#$03; at cell 3? -> inc row (start 2nd row of 2x3 grid)
$22FDd0 02bner_2301
$22FFe6 fdinczp_blit_row
$230160r_2301rts; x-ref: $22FD
$2302.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame update of obstacle and player bike screen positions.
; Called every game frame (phase 1 of the IRQ-driven 3-phase main loop).
;
; Guards on a02 (game mode): only active for modes 1, 2, 5, or >=7;
; returns immediately for menus, attract, or paused states.
;
; Reads the obstacle type byte at offset $10 of the object record pointed
; to by p24 ($24/$25):
; $FE -> clears a3B (player-active flag), calls s2694 (crash/reset)
; $FD -> sets a31=1 (state-change signal)
;
; Decrement path (a3B=0, obstacles scrolling right->left):
; Loops over slots X=4,3,1 (slot 2 skipped): decrements f0C07,x and
; f0C2F,x by 1 each frame. Wraps at $0A back to $14/$28 (track boundary).
; Calls s2190 to check if any slot crossed the finish threshold.
;
; Increment path (a3B!=0, player in motion, even frames only):
; Loops over slots X=1,0: increments f0C07,x and f0C2F,x. Wraps at
; $15 back down to $0B/$1F. Calls s2E90 to check for a finish-line
; or bike collision.
;
; Inputs: a02 (game mode), a3B (player-active flag), a31, a3C, a3F
; (frame counter), (p24)+$10 (obstacle type), f0C07/f0C2F tables
; Outputs: f0C07,x / f0C2F,x (updated screen positions), a3B, a31
; Side Effects: May call s2694 (crash/reset), s2190 (lap check),
; s2E90 (finish-line/collision check)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_obstacle_positions
$2304a5 02ldazp_game_mode; Load game mode byte ; x-ref: $20DA
$2306c9 01cmp#GameMode.CRASH_DEBRIS; Mode 1 -> active
$2308f0 0ebeqb_2318
$230Ac9 02cmp#GameMode.ACTIVE_RIDE; Mode 2 -> active
$230Cf0 0abeqb_2318
$230Ec9 05cmp#GameMode.DEBRIS_WAIT; Mode 5 -> active
$2310f0 06beqb_2318
$2312c9 07cmp#GameMode.VELOCITY_INTEGRATE; Mode >=7 -> active; all others fall through to RTS
$2314b0 02bcsb_2318; Inactive mode — skip all updates
$231660rts
$2317eanop
$2318a0 10b_2318ldy#$10; Y=$10: offset to obstacle type byte in object record ; x-ref: $2308, $230C, $2310, $2314
$231Ab1 24lda(zp_ptr_obj_record_lo),y; Read obstacle type via p24 pointer (indirect indexed)
$231Cc9 fecmp#$fe; $FE = sentinel: clear player-active flag and call crash/reset handler
$231Ed0 06bneb_2326
$2320a9 00lda#$00; Clear a3B: mark player as inactive (obstacle-scroll mode)
$232285 3bstazp_player_active
$2324f0 08beqb_232E
$2326c9 fdb_2326cmp#$fd; $FD = sentinel: signal state-change (set a31=1) ; x-ref: $231E
$2328d0 04bneb_232E
$232Aa9 01lda#$01; Set state-change flag
$232C85 31stazp_scroll_dir_flag
$232Ea5 31b_232Eldazp_scroll_dir_flag; a31=0 -> decrement path (obstacles scrolling); else increment path ; x-ref: $2324, $2328
$2330f0 0dbeqb_233F
$2332eanop
$2333eanop
$2334eanop
$2335eanop
$2336eanop
$2337eanop
$2338a9 fflda#$ff; Set a3B=$FF (player active) and call crash/reset handler
$233A85 3bstazp_player_active
$233C20 94 26jsrenter_mode_riding
$233Fa5 3bb_233Fldazp_player_active; x-ref: $2330
$2341d0 26bneb_2369
$2343a2 04ldx#$04; Decrement path: start at slot X=4, work down to X=0 (skip slot 2)
$2345e0 02b_2345cpx#$02; Skip slot 2 (player slot) ; x-ref: $2361
$2347f0 17beqb_2360
$2349de 07 0cdecSCREEN_RAM_R0C7,x; Scroll obstacle left (decrement screen X position)
$234Cde 2f 0cdecSCREEN_RAM_R1C7,x; Scroll second position table in step
$234Fbd 07 0cldaSCREEN_RAM_R0C7,x
$2352c9 0acmp#$0a; At left edge ($0A)? Wrap obstacle back to right side ($14/$28)
$2354d0 0dbneb_2363
$2356a9 14lda#$14
$23589d 07 0cstaSCREEN_RAM_R0C7,x
$235Ba9 28lda#$28
$235D9d 2f 0cstaSCREEN_RAM_R1C7,x
$2360cab_2360dex; x-ref: $2347
$236110 e2bplb_2345
$2363ce 0b 0cb_2363decSCREEN_RAM_R0C11; x-ref: $2354
$2366ce 33 0cdecSCREEN_RAM_R1C11
$236920 90 21b_2369jsrcheck_title_obstacle_collision; x-ref: $2341
$236Ca5 3fldazp_frame_counter
$236E29 01and#$01
$2370d0 25bner_2397
$2372a5 3cldazp_anim_frame_count
$2374f0 21beqr_2397
$2376c6 3cdeczp_anim_frame_count
$2378a2 01ldx#$01
$237Afe 07 0cb_237AincSCREEN_RAM_R0C7,x; x-ref: $2392
$237Dfe 2f 0cincSCREEN_RAM_R1C7,x
$2380bd 07 0cldaSCREEN_RAM_R0C7,x
$2383c9 15cmp#$15
$2385d0 0dbneb_2394
$2387a9 0blda#$0b
$23899d 07 0cstaSCREEN_RAM_R0C7,x
$238Ca9 1flda#$1f
$238E9d 2f 0cstaSCREEN_RAM_R1C7,x; Only run increment path on even frames (bit 0 of frame counter)
$2391cadex
$239210 e6bplb_237A; Secondary counter zero -> skip increment this frame
$239420 90 2eb_2394jsrcheck_finish_line; Decrement secondary counter ; x-ref: $2385
$239760r_2397rts; x-ref: $2370, $2374
$2398.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Track tile renderer - expands a 40-byte track row definition into a
; 40x5 character tile grid at the bottom of the screen.
;
; For each of 40 columns (Y=0..39):
; 1. Reads tile type from (ptr_obj_record_lo),Y
; 2. Masks to 6-bit index (0-63 tile types)
; 3. Looks up 5 ROM tables (f1EC0/f1F00/f1F40/f1F80/f1FC0) to get
; the 5 characters that form the tile column vertically
; 4. Writes them to 5 consecutive screen rows at the bottom:
; $0E80+Y (row 19), $0EA8+Y (row 20), $0ED0+Y (row 21),
; $0EF8+Y (row 22), $0F20+Y (row 23)
;
; Inputs: (ptr_obj_record_lo) -> 40-byte track row data,
; f1EC0-f1FC0 (5 ROM tile expansion tables, 64 entries each)
; Outputs: Screen RAM $0E80-$0F47 (5 rows x 40 cols at bottom of screen)
; Side Effects: None (pure screen write)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$239Ca0 00render_track_tilesldy#$00; x-ref: $2108
$239Eb1 24b_239Elda(zp_ptr_obj_record_lo),y; read tile type from track data row ; x-ref: $23C4
$23A029 3fand#$3f; mask to 6-bit tile type (0-63)
$23A2aatax
$23A3bd c0 1eldatile_chars_row0,x; tile row 0 -> screen row 19 ($0E80)
$23A699 80 0estaSCREEN_RAM_R16C0,y
$23A9bd 00 1fldatile_chars_row1,x; tile row 1 -> screen row 20 ($0EA8)
$23AC99 a8 0estaSCREEN_RAM_R17C0,y
$23AFbd 40 1fldatile_chars_row2,x; tile row 2 -> screen row 21 ($0ED0)
$23B299 d0 0estaSCREEN_RAM_R18C0,y
$23B5bd 80 1fldatile_chars_row3,x; tile row 3 -> screen row 22 ($0EF8)
$23B899 f8 0estaSCREEN_RAM_R19C0,y
$23BBbd c0 1fldatile_chars_row4,x; tile row 4 -> screen row 23 ($0F20)
$23BE99 20 0fstaSCREEN_RAM_R20C0,y
$23C1c8iny
$23C2c0 28cpy#$28; 40 columns (full screen width)
$23C4d0 d8bneb_239E
$23C660rts
$23C7.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Sprite queue push - adds one sprite entry to the draw queue.
; Clips sprites with column >= 40 (off-screen right).
;
; Stores (blit_col, blit_row, blit_char_idx, blit_colour_ptr) into
; four parallel arrays at f0400/f0430/f0460/f0490 indexed by
; sprite_queue_count, then increments the count.
;
; The queue is processed by sprite_draw ($2204) to write chars to screen,
; and by sprite_erase ($21D8) to clear them next frame.
;
; Inputs: blit_col (row idx), blit_row (col), blit_char_idx (char code),
; blit_colour_ptr (colour attr), sprite_queue_count
; Outputs: sprite_queue_count (incremented), queue arrays updated
; Side Effects: None (deferred rendering)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$23C8a5 fdsprite_queue_pushldazp_blit_row; x-ref: $2279, $22AA, $22D7, $2463, $24B7
$23CAc9 28cmp#$28; column >= 40? -> off-screen right, clip
$23CCb0 1cbcsr_23EA
$23CE8atxa
$23CF48pha
$23D0a6 32ldxzp_sprite_queue_count; save X, use sprite_queue_count as queue index
$23D2a5 fcldazp_blit_col
$23D49d 00 04stasprite_q_row,x; store row index into queue
$23D7a5 fdldazp_blit_row
$23D99d 30 04stasprite_q_col,x; store column into queue
$23DCa5 ffldazp_blit_char_idx
$23DE9d 60 04stasprite_q_char,x; store character code into queue
$23E1a5 feldazp_blit_color_ptr
$23E39d 90 04stasprite_q_color,x; store colour attribute into queue
$23E6e6 32inczp_sprite_queue_count; advance queue pointer for next entry
$23E868pla
$23E9aatax
$23EA60r_23EArts; x-ref: $23CC
$23EB.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame track tile colour attribute update.
; Loops over 10 entries of the current object record (Y=$10,$12,...,$12;
; word-stride, 10 iterations until Y=$14). For each entry:
; 1. Reads the tile type byte from (p24)+Y and masks to 6 bits (type 0-63).
; 2. Looks up a colour descriptor byte from the ROM table at f1E80[type].
; 3. Splits the descriptor into two TED colour fields:
; bits 2-5 (& $3C) + $33 -> foreground/ink colour -> $27+Y
; bits 6-7 (& $C0) -> background/paper colour -> $28+Y
; 4. Writes both fields back into the object record via absolute indexed
; addressing (@w a27,Y / @w a28,Y).
;
; The ROM table f1E80 encodes up to 64 tile type descriptors:
; bits 2-5: foreground colour index (offset by $33 into TED palette)
; bits 6-7: background colour field
; bits 0-1: used by other routines (e.g. sprite shape selection in s2410)
;
; Inputs: p24 ($24/$25) -- pointer to current object record
; f1E80 -- ROM tile colour descriptor table (64 entries)
; Outputs: Object record bytes at $27+Y / $28+Y (Y=$10..$12) updated
; with foreground and background TED colour attributes
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$23ECa0 10update_tile_colorsldy#$10; Y=$10: start at first tile slot in object record (word-stride loop) ; x-ref: $20E0
$23EEb1 24b_23EElda(zp_ptr_obj_record_lo),y; Read tile type byte from object record via p24 pointer ; x-ref: $240A
$23F029 3fand#$3f; Mask to 6-bit type index (0-63 tile types)
$23F2aatax
$23F3bd 80 1eldatile_color_descriptors,x; Fetch color descriptor from ROM table f1E80[type]
$23F629 3cand#$3c; Extract foreground colour field (bits 2-5), offset by $33 into TED palette
$23F818clc
$23F969 33adc#$33
$23FB99 27 00sta@w zp_bike_pos_x_hi,y; Write foreground colour into object record at $27+Y
$23FEbd 80 1eldatile_color_descriptors,x; Re-fetch same descriptor for background field
$240129 c0and#$c0; Extract background colour field (bits 6-7)
$240399 28 00sta@w zp_bike_pos_x_lo,y; Write background colour into object record at $28+Y
$2406c8iny; Advance by 2 (word stride through object record)
$2407c8iny
$2408c0 14cpy#$14; 10 entries done when Y=$14 (10 x 2-byte steps from $10)
$240Ad0 e2bneb_23EE
$240C60rts
$240D.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Mode-dependent screen update: crash debris animation or bike renderer.
;
; Mode dispatch:
; Modes 0, 6, 8 -> RTS (nothing to render)
; Modes 1, 5 -> Crash debris animation (8 particles)
; Modes 2,3,4,7 -> Bike renderer (3x3 character grid)
;
; Debris animation (modes 1, 5):
; 8 particles (X=7..0) with independent X/Y velocities from f1C80/f1C88.
; Each frame: pos += velocity, bounds check (Y >= 19 or X >= 40 -> kill),
; blit char $52 / colour $37 via s23C8. Dead particles marked $FF.
;
; Bike renderer (modes 2, 3, 4, 7):
; Renders bike as a 3x3 character grid. Animation frame derived from
; raster_split_a/b sub-pixel positions (XOR $06, >> 1 -> f1CC8 lookup).
; Horizontal sub-position from bike_pos_hi & $03 -> f1CCC offset.
; Screen column from bike_pos_hi >> 2. Each cell blitted via s23C8
; with colour $39. Char index += 3 per row for sequential animation.
;
; Inputs: game_mode, crash_debris_x/y[0..7], raster_split_a/b,
; bike_pos_hi, f1C80/f1C88, f1CC8/f1CCC
; Outputs: Screen RAM (debris or bike chars), crash_debris_x/y (updated)
; Side Effects: Calls s23C8 (character blit) for each visible element
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
render_debris_or_bike
$2410a5 02ldazp_game_mode; x-ref: $2105
$2412c9 00cmp#GameMode.NORMAL_RIDE; modes 0, 6, 8 -> nothing to render, return
$2414f0 12beqr_2428
$2416c9 06cmp#GameMode.RESULTS_SCREEN
$2418f0 0ebeqr_2428
$241Ac9 08cmp#GameMode.NEXT_LAP_INIT
$241Cf0 0abeqr_2428
$241Ec9 01cmp#GameMode.CRASH_DEBRIS; modes 1, 5 -> crash debris animation
$2420f0 08beqb_242A
$2422c9 05cmp#GameMode.DEBRIS_WAIT
$2424f0 04beqb_242A
$2426d0 44bneb_246C; all other modes -> bike renderer
$242860r_2428rts; x-ref: $2414, $2418, $241C
$2429eanop
$242Aa2 07b_242Aldx#$07; --- Debris animation: 8 particles (X=7..0) --- ; x-ref: $2420, $2424
$242Cb5 48b_242Cldazp_crash_debris_y,x; x-ref: $2467
$242Ec9 ffcmp#$ff; $FF = dead particle, skip
$2430f0 34beqb_2466
$243218clc
$24337d 88 1cadcdebris_vel_y,x; Y pos += velocity from f1C88 (signed: up/down)
$243695 48stazp_crash_debris_y,x
$243838sec
$2439e9 02sbc#$02
$243Bc9 13cmp#$13; Y >= 19 (off bottom)? -> kill particle
$243D90 06bccb_2445
$243Fa9 ffb_243Flda#$ff; x-ref: $2451
$244195 48stazp_crash_debris_y,x
$2443d0 21bneb_2466
$2445b5 40b_2445ldazp_crash_debris_x,x; x-ref: $243D
$244718clc
$24487d 80 1cadcdebris_vel_x,x; X pos += velocity from f1C80 (signed: left/right)
$244B95 40stazp_crash_debris_x,x
$244Dc9 28cmp#$28; X >= 40 (off sides)? -> kill particle
$244F90 04bccb_2455
$2451b0 ecbcsb_243F
$2453b5 40ldazp_crash_debris_x,x
$245585 fdb_2455stazp_blit_row; x-ref: $244F
$2457b5 48ldazp_crash_debris_y,x
$245985 fcstazp_blit_col
$245Ba9 52lda#$52; $52 = debris character, $37 = debris colour
$245D85 festazp_blit_color_ptr
$245Fa9 37lda#$37
$246185 ffstazp_blit_char_idx
$246320 c8 23jsrsprite_queue_push; blit debris particle to screen
$2466cab_2466dex; x-ref: $2430, $2443
$246710 c3bplb_242C
$246960rts
$246A.byte$00, $00
$246Ca5 04b_246Cldazp_raster_split_a; --- Bike renderer: 3x3 character grid --- ; x-ref: $2426
$246E49 06eor#$06; (split_a XOR $06) >> 1 -> sub-pixel frame index
$24704alsra
$2471aatax
$2472bd c8 1cldabike_char_base,x; f1CC8[frame] -> base char for top half of bike
$247585 f8stazp_ted_ctrl_staging
$2477a5 05ldazp_raster_split_b
$247949 06eor#$06
$247B4alsra
$247Caatax
$247Dbd c8 1cldabike_char_base,x; f1CC8[frame] -> base char for bottom half of bike
$248085 f9stazp_pitch_hi
$2482a5 27ldazp_bike_pos_x_hi
$248429 03and#$03; bike_pos_hi & $03 -> sub-position column offset
$2486aatax
$2487bd cc 1cldabike_subpos_offset,x; f1CCC[sub-pos] -> char offset for smooth horizontal motion
$248A18clc
$248B65 f8adczp_ted_ctrl_staging
$248D85 f8stazp_ted_ctrl_staging
$248Fbd cc 1cldabike_subpos_offset,x
$249218clc
$249365 f9adczp_pitch_hi
$249585 f9stazp_pitch_hi
$2497a5 27ldazp_bike_pos_x_hi
$24994alsra; screen column = bike_pos_hi >> 2
$249A4alsra
$249B85 fcstazp_blit_col
$249Da0 00ldy#$00
$249Fa9 10b_249Flda#$10; outer loop: 3 columns (Y=0..2) ; x-ref: $24D1
$24A185 fdstazp_blit_row
$24A3a2 00ldx#$00
$24A5a5 fcldazp_blit_col
$24A7c9 10cmp#$10; select top or bottom half char base
$24A990 04bccb_24AF
$24ABa5 f9ldazp_pitch_hi
$24ADd0 02bneb_24B1
$24AFa5 f8b_24AFldazp_ted_ctrl_staging; x-ref: $24A9
$24B185 ffb_24B1stazp_blit_char_idx; x-ref: $24AD
$24B3a9 39lda#$39; $39 = bike colour attribute
$24B585 festazp_blit_color_ptr
$24B720 c8 23b_24B7jsrsprite_queue_push; inner loop: blit 3 rows per column via s23C8 ; x-ref: $24C6
$24BAe8inx
$24BBa5 ffldazp_blit_char_idx
$24BD18clc
$24BE69 03adc#$03; char_idx += 3 (next animation frame in sequence)
$24C085 ffstazp_blit_char_idx
$24C2e6 fdinczp_blit_row
$24C4e0 03cpx#$03
$24C6d0 efbneb_24B7
$24C8e6 f8inczp_ted_ctrl_staging; advance both char bases and column for next column
$24CAe6 f9inczp_pitch_hi
$24CCe6 fcinczp_blit_col
$24CEc8iny
$24CFc0 03cpy#$03
$24D1d0 ccbneb_249F
$24D360rts
$24D4.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Track scroll engine - converts throttle + course speed into smooth sub-pixel
; scrolling via TED fine scroll registers.
;
; Uses a fractional accumulator technique: track_scroll_frac is decremented
; by bike_throttle each frame. On underflow, a scroll step occurs (faster
; throttle = more frequent steps = faster scroll).
;
; Two raster splits (raster_split_a/b) cycle through 4 sub-pixel positions
; (0,2,4,6). On wrap past 6->0, a full character row scroll triggers:
; raster_split_b wrap -> s2A0C (track screen update) + s2E28 (advance ptr)
; raster_split_a wrap -> s27FC (obstacle position update)
;
; Dual scroll sources:
; Phase 1: Throttle-driven (bike_throttle) - player speed
; Phase 2: Timer-driven (scroll_speed) - base course rate, independent
;
; Inputs: track_scroll_frac, bike_throttle, raster_split_a/b,
; scroll_timer, scroll_speed
; Outputs: track_scroll_frac, raster_split_a/b, scroll_timer
; Side Effects: s2A0C (track update), s2E28 (ptr advance), s27FC (obstacles),
; s21B4 (raster colour)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$24D8a5 26track_scroll_engineldazp_track_scroll_frac; x-ref: $2102
$24DA38sec
$24DBe5 2asbczp_bike_throttle; frac -= throttle (higher throttle -> faster underflow)
$24DD85 26stazp_track_scroll_frac
$24DFb0 23bcsb_2504; no underflow -> skip throttle-driven scroll
$24E1a5 05ldazp_raster_split_b
$24E338sec
$24E4e9 02sbc#$02; step split_b by 2 sub-pixels (cycles 0,2,4,6)
$24E629 06and#$06
$24E885 05stazp_raster_split_b
$24EAc9 06cmp#$06; wrapped past 6? -> full row scroll needed
$24ECd0 06bneb_24F4
$24EE20 0c 2ajsrtick_scroll_tiles_if_active; full row scroll: update track screen content
$24F120 28 2ejsradvance_course_data_ptr; advance track data pointer to next row
$24F4a5 04b_24F4ldazp_raster_split_a; x-ref: $24EC
$24F638sec
$24F7e9 02sbc#$02; step split_a by 2 sub-pixels (cycles 0,2,4,6)
$24F929 06and#$06
$24FB85 04stazp_raster_split_a
$24FDc9 06cmp#$06
$24FFd0 03bneb_2504
$250120 fc 27jsrobstacle_life_tick; split_a wrapped: update obstacle positions
$2504a5 3eb_2504ldazp_scroll_timer; x-ref: $24DF, $24FF
$250638sec
$2507e5 53sbczp_scroll_speed; timer -= scroll_speed (base course rate)
$250985 3estazp_scroll_timer
$250Bb0 10bcsb_251D; no underflow -> skip timer-driven scroll
$250Da5 04ldazp_raster_split_a
$250F38sec
$2510e9 02sbc#$02; timer scroll: step split_a sub-pixels
$251229 06and#$06
$251485 04stazp_raster_split_a
$2516c9 06cmp#$06
$2518d0 03bneb_251D
$251A20 fc 27jsrobstacle_life_tick; timer split_a wrapped: update obstacle positions
$251D20 b4 21b_251Djsrclear_split_if_stationary; update raster colour state for IRQ handler ; x-ref: $250B, $2518
$252060rts
$2521.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame obstacle physics update. Loops over 4 obstacle slots (X=3..0)
; and integrates velocity into position, applies boundary bounce/wrap, and
; triggers a collision sound when a height threshold is crossed.
;
; Per-slot zero-page layout (base + X):
; f10,x ($10+X) -- type/flags: $FF=inactive; bits 0-2=type;
; bit3=high-speed; bit4=sound-triggered
; f14,x ($14+X) -- lifetime countdown
; f18,x ($18+X) -- position high byte (integer Y)
; f1C,x ($1C+X) -- position low byte (fractional / sub-pixel)
; f20,x ($20+X) -- velocity (signed)
;
; Low-speed path (f10,x < $08, e.g. barrels):
; Integrates f20 into f1C/f18 with carry. Boundary checks on f18:
; $10-$2F -> negate velocity (bounce via s293C)
; >= $30 -> nudge position down then bounce
; < $10 -> nudge position up then bounce
;
; High-speed path (f10,x >= $08, e.g. tyres):
; Adds f18 into f1C; carry propagates into f20. When (f20 & $1F)==$10
; AND ((X XOR frame_counter) & $03)==0: arms collision sound via s2C20
; and sets bit 4 of f10,x. Otherwise clears bit 4.
; The frame-counter gate staggers triggers across 4 frames to prevent
; multiple sounds firing simultaneously.
;
; Inputs: f10,x-f20,x (4 slots), a3F (frame counter)
; Outputs: f18,x (position), f1C,x (sub-pixel), f20,x (velocity),
; f10,x (flags)
; Side Effects: May arm obstacle sound via s2C20 (JMPER, DSCPNT, a51, SIZE)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_obstacle_physics
$2524a2 03ldx#$03; Loop over 4 obstacle slots (X=3..0) ; x-ref: $20DD
$2526b5 10b_2526ldazp_obstacle_flags,x; Load obstacle type/flags for this slot ; x-ref: $2581
$2528c9 ffcmp#$ff
$252Af0 54beqb_2580; Slot inactive — skip to next
$252Cc9 08cmp#$08; type >= $08 -> high-speed path (tyres etc.)
$252Eb0 28bcsb_2558
$2530b5 20ldazp_obstacle_vel,x; Load velocity; negative -> pre-decrement integer position for signed carry
$253210 02bplb_2536
$2534d6 18deczp_obstacle_pos_hi,x; Pre-decrement integer pos to handle negative velocity carry correctly
$2536b5 1cb_2536ldazp_obstacle_pos_lo,x; Integrate: fractional pos += velocity ; x-ref: $2532
$253818clc
$253975 20adczp_obstacle_vel,x
$253B95 1cstazp_obstacle_pos_lo,x
$253Db5 18ldazp_obstacle_pos_hi,x; Propagate fractional carry into integer position
$253F69 00adc#$00
$254195 18stazp_obstacle_pos_hi,x
$2543c9 10cmp#$10; Below upper boundary ($10)? -> nudge up path
$254590 08bccb_254F
$2547c9 30cmp#$30; Between $10-$2F -> bounce (negate velocity)
$254990 09bccb_2554
$254Bd6 18deczp_obstacle_pos_hi,x; Above ceiling ($30): nudge position down, then bounce
$254Dd0 02bneb_2551
$254Ff6 18b_254Finczp_obstacle_pos_hi,x; Below floor ($10): nudge position up ; x-ref: $2545
$255120 3c 29b_2551jsrnegate_obstacle_vel; Negate velocity (bounce) ; x-ref: $254D
$255418b_2554clc; x-ref: $2549
$255590 29bccb_2580
$2557eanop
$2558b5 1cb_2558ldazp_obstacle_pos_lo,x; --- High-speed path (f10,x >= $08, e.g. tyres) --- ; x-ref: $252E
$255A18clc
$255B75 18adczp_obstacle_pos_hi,x; Accumulator += integer position as velocity step; carry into f20
$255D95 1cstazp_obstacle_pos_lo,x
$255Fb5 20ldazp_obstacle_vel,x
$256169 00adc#$00
$256395 20stazp_obstacle_vel,x; Check low 5 bits of velocity == $10 (mid-range threshold)
$256529 1fand#$1f
$2567c9 10cmp#$10
$2569d0 0fbneb_257A; Gate: (slot XOR frame_counter) & $03 == 0 -> only 1 in 4 frames per slot
$256B8atxa
$256C45 3feorzp_frame_counter
$256E29 03and#$03
$2570d0 08bneb_257A
$2572b5 10ldazp_obstacle_flags,x; Arm collision sound + set bit 4 in f10,x (sound-triggered flag)
$257420 20 2cjsrsfx_obstacle_collision_arm
$2577eanop
$2578d0 06bneb_2580
$257Ab5 10b_257Aldazp_obstacle_flags,x; Not at threshold or wrong frame: clear bit 4 (reset sound-triggered flag) ; x-ref: $2569, $2570
$257C29 efand#$ef
$257E95 10stazp_obstacle_flags,x
$2580cab_2580dex; Next slot; loop until X < 0 ; x-ref: $252A, $2555, $2578
$258110 a3bplb_2526
$258360rts
$2584.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Draws the progress/lap HUD bar on two consecutive screen rows.
; Phase 1: Clear 40-char row at $0B98 with blanks ($00).
; Phase 2: Draw course progress markers ($3A) at $0BAF+X, one per completed
; course (clamped to max 15), filling right-to-left.
; Phase 3: Draw lap pips ($29) at $0B97+X, 3 chars per remaining lap,
; filling right-to-left.
; Phase 4: Copy the completed row to a second row at $0BC0 (shadow/duplicate).
;
; Inputs: course_index, lives_counter
; Outputs: Screen RAM $0B98-$0BBF (row 1), $0BC0-$0BE7 (row 2)
; Side Effects: None (pure screen write)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
draw_hud_progress_bar
$2588a2 27ldx#$27; 40 chars = one full screen row ; x-ref: $20E6
$258Aa9 00lda#$00
$258C9d 98 0bb_258CstaCOLOR_RAM_R23C0,x; clear HUD row with blanks ; x-ref: $2590
$258Fcadex
$259010 fabplb_258C
$2592a5 2fldazp_course_index
$2594c9 0fcmp#$0f; clamp course_index to max 15
$259690 02bccb_259A
$2598a9 0flda#$0f
$259Aaab_259Atax; x-ref: $2596
$259Be0 ffb_259Bcpx#$ff; x-ref: $25A5
$259Df0 08beqb_25A7
$259Fa9 3alda#$3a; $3A = course progress marker char
$25A19d af 0bstaCOLOR_RAM_R23C23,x; draw one marker per completed course (right side of row)
$25A4cadex
$25A510 f4bplb_259B
$25A7a5 30b_25A7ldazp_lives_counter; X = lives_counter * 3 (3 chars per lap pip group) ; x-ref: $259D
$25A90aasla
$25AA18clc
$25AB65 30adczp_lives_counter
$25ADaatax
$25AEcadex
$25AFe0 00b_25AFcpx#$00; x-ref: $25C1
$25B130 10bmib_25C3
$25B3a9 29lda#$29; $29 = lap pip char (3 consecutive per lap)
$25B59d 97 0bstaCOLOR_RAM_R22C39,x; draw 3-char pip group (left side of row)
$25B89d 98 0bstaCOLOR_RAM_R23C0,x
$25BB9d 99 0bstaCOLOR_RAM_R23C1,x
$25BEcadex; advance 3 positions for next pip group
$25BFcadex
$25C0cadex
$25C110 ecbplb_25AF
$25C3a2 27b_25C3ldx#$27; copy completed row to second row at $0BC0 (shadow) ; x-ref: $25B1
$25C5bd 98 0bb_25C5ldaCOLOR_RAM_R23C0,x; x-ref: $25CC
$25C89d c0 0bstaCOLOR_RAM_R24C0,x
$25CBcadex
$25CC10 f7bplb_25C5
$25CE60rts
$25CF.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Main game state machine dispatcher - called once per frame from the main loop.
; Reads game_mode and dispatches to the handler for the current game phase:
;
; Mode 0: handle_mode_normal_ride - Fire/jump button -> movement dispatch
; Mode 1: handle_mode_crash_debris - Wait for debris landing -> respawn
; Mode 2: handle_mode_active_ride - Throttle input, bounds, physics, FX
; Mode 3: handle_mode_starting_gate - Left-edge enforce + throttle decay
; Mode 4: handle_mode_track_scroll - Track scrolling + finish animation
; Mode 5: handle_mode_debris_wait - Wait debris clear -> lap/course end
; Mode 6: handle_mode_results_screen - Descending jingle -> return to menu
; Mode 7: apply_velocity_and_integrate - Velocity + 4x position integration
; Mode 8: handle_mode_next_lap_init - Poll readiness -> start next lap
;
; Game flow: 0->2 (ride), crash->1->0 (respawn), bounds->7 (correct),
; 3 (decay)->4 (scroll)->5 (debris wait)->8 (next lap)->1 or 6 (results).
;
; Inputs: game_mode
; Outputs: Delegates entirely to the target handler
; Side Effects: All side effects come from the dispatched handler
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$25D0a5 02handle_game_modeldazp_game_mode; read current game state ; x-ref: $20E9
$25D2c9 00cmp#GameMode.NORMAL_RIDE
$25D4f0 21beqhandle_game_mode_0; mode 0: normal riding
$25D6c9 01cmp#GameMode.CRASH_DEBRIS
$25D8f0 20beqhandle_game_mode_1; mode 1: crash debris settle
$25DAc9 02cmp#GameMode.ACTIVE_RIDE
$25DCf0 1fbeqhandle_game_mode_2; mode 2: active riding
$25DEc9 03cmp#GameMode.STARTING_GATE
$25E0f0 1ebeqhandle_game_mode_3; mode 3: starting gate
$25E2c9 04cmp#GameMode.TRACK_SCROLL
$25E4f0 1dbeqhandle_game_mode_4; mode 4: track scrolling
$25E6c9 05cmp#GameMode.DEBRIS_WAIT
$25E8f0 1cbeqhandle_game_mode_5; mode 5: debris wait / lap transition
$25EAc9 06cmp#GameMode.RESULTS_SCREEN
$25ECf0 1bbeqhandle_game_mode_6; mode 6: results screen
$25EEc9 07cmp#GameMode.VELOCITY_INTEGRATE
$25F0f0 1abeqhandle_game_mode_7; mode 7: velocity correction
$25F2c9 08cmp#GameMode.NEXT_LAP_INIT
$25F4f0 19beqhandle_game_mode_8; mode 8: next lap init
$25F660rts; unknown mode: no-op
$25F74c 54 27handle_game_mode_0jmphandle_mode_normal_ride; x-ref: $25D4
$25FA4c 64 27handle_game_mode_1jmphandle_mode_crash_debris; x-ref: $25D8
$25FD4c 84 27handle_game_mode_2jmphandle_mode_active_ride; x-ref: $25DC
$26004c b8 2ehandle_game_mode_3jmphandle_mode_starting_gate; x-ref: $25E0
$26034c 34 28handle_game_mode_4jmphandle_mode_track_scroll; x-ref: $25E4
$26064c 94 28handle_game_mode_5jmphandle_mode_debris_wait; x-ref: $25E8
$26094c c4 28handle_game_mode_6jmphandle_mode_results_screen; x-ref: $25EC
$260C4c f8 28handle_game_mode_7jmpapply_velocity_and_integrate; x-ref: $25F0
$260F4c 48 29handle_game_mode_8jmphandle_mode_next_lap_init; x-ref: $25F4
$2612.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the CBM BASIC 3.5 interpreter runtime state to a clean baseline.
; Zeroes or re-initialises a suite of zero-page BASIC variables (INDEX2,
; FRESPC, SUBFLG[0-3], ZPVEC2, RESHO, SRCHTK, OLDOV, TEMPF1) and sets
; several unlabelled scratch bytes to fixed sentinel values. Called during
; level initialisation (after TED screen setup) to ensure no stale BASIC
; engine state leaks between levels.
;
; Inputs: None
; Outputs: None
; Side Effects: Modifies RAM_INDEX2, RAM_FRESPC, RAM_SUBFLG[0-3], RAM_ZPVEC2,
; RAM_RESHO, RAM_SRCHTK, RAM_OLDOV, RAM_TEMPF1; sets $25=$2F,
; $34=$05, $2A=$80
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
reset_basic_runtime_state
$2614a9 00lda#$00; Clear INDEX2 (utility pointer used by BASIC string/array ops) ; x-ref: $20AB, $28CC
$261685 24stazp_ptr_obj_record_lo
$2618a9 2flda#$2f
$261A85 25stazp_ptr_obj_record_hi; ZP scratch $25 = $2F (BASIC engine internal field)
$261Ca9 05lda#$05
$261E85 34stazp_music_step; ZP scratch $34 = 5 (BASIC engine internal field)
$2620a9 fflda#$ff
$262285 35stazp_music_pos; FRESPC=$FF: mark string space as invalid/unallocated
$2624a2 03ldx#$03; X=3: fill SUBFLG[0..3] with $FF (disable subscript/user-fn flags)
$2626a9 fflda#$ff
$262895 10b_2628stazp_obstacle_flags,x; SUBFLG[X] = $FF; repeat for X=3,2,1,0 ; x-ref: $262B
$262Acadex
$262B10 fbbplb_2628
$262Da9 00lda#0
$262F85 04stazp_raster_split_a
$263185 05stazp_raster_split_b
$263385 26stazp_track_scroll_frac
$263585 02stazp_game_mode; Zero ZPVEC2 (temp for renumber), RESHO (expression result high byte), SRCHTK (token search key)
$2637a9 80lda#$80
$263985 2astazp_bike_throttle; $2A=$80: set high-bit state flag (signals BASIC engine not inside a loop)
$263B20 0c 2ejsrreset_display_counters; Zero OLDOV (overflow flag) and TEMPF1 (FP temporary)
$263E60rts
$263F.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes all per-course game state for the start of a ride.
; Resets lives_counter to 5, sets neutral throttle ($60), clears raster
; split lines, scroll fraction, and scroll direction, marks player active
; ($FF), then calls s2E14 (load course object-record pointer from
; title_char_idx) and s2A3A (clear scroll tables, load anim_frame_count
; and scroll_speed for the current course).
; Called from handle_mode_normal_ride on the no-jump path.
;
; Inputs: title_char_idx = selected course/character index
; Outputs: lives_counter=5, bike_throttle=$60, raster_split_a/b=0,
; track_scroll_frac=0, scroll_dir_flag=0, player_active=$FF,
; course_index, ptr_obj_record_lo/hi, anim_frame_count, scroll_speed
; Side Effects: Scroll position tables cleared (via s2A2C inside s2A3A)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$264020 14 2einit_course_statejsrload_course_layout; load course object-record pointer from title_char_idx ; x-ref: $275A
$2643a9 05lda#$05; start with 5 laps
$264585 30stazp_lives_counter
$2647a9 60lda#$60; neutral/mid throttle
$264985 2astazp_bike_throttle
$264Ba9 00lda#$00; clear raster splits, scroll fraction, and scroll direction
$264D85 04stazp_raster_split_a
$264F85 05stazp_raster_split_b
$265185 26stazp_track_scroll_frac
$265385 31stazp_scroll_dir_flag
$2655a9 fflda#$ff; mark player as active
$265785 3bstazp_player_active
$265920 3a 2ajsrclear_track_and_load_course; clear scroll tables, load anim_frame_count + scroll_speed for course
$265C20 64 26jsrinit_lap_start_anim
$265F60rts
$2660.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Kills all 4 obstacle slots then initializes the lap-start debris animation.
; Sets all obstacle_flags to $FF (inactive), then falls through into
; init_lap_start_anim_no_kill to trigger the sound effect, load 16 debris
; X-positions from f1CD0, enter game_mode=1, and clear spawn_cooldown.
; Called from init_course_state at course start (full reset path).
;
; Inputs: None
; Outputs: obstacle_flags[0..3]=$FF, crash_debris_x[0..15] from f1CD0,
; game_mode=1, spawn_cooldown=0
; Side Effects: Sound triggered via s2BDC
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2664a2 03init_lap_start_animldx#$03; kill all 4 obstacle slots (flags=$FF = inactive) ; x-ref: $265C
$2666a9 fflda#$ff
$266895 10b_2668stazp_obstacle_flags,x; x-ref: $266B
$266Acadex
$266B10 fbbplb_2668
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the lap-start debris animation without killing obstacle slots.
; Partial entry point: skips the obstacle_flags reset, jumps straight to
; triggering the lap-start sound (s2BDC), loading 16 debris X-positions
; from the f1CD0 table into crash_debris_x, setting game_mode=1, and
; clearing spawn_cooldown.
; Called from handle_mode_next_lap_init at lap transitions where obstacles
; have already been cleared.
;
; Inputs: None
; Outputs: crash_debris_x[0..15] from f1CD0, game_mode=1, spawn_cooldown=0
; Side Effects: Sound triggered via s2BDC
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
init_lap_start_anim_no_kill
$266D20 dc 2bjsrsfx_lap_start; trigger lap-start sound (pitch=$C0, delta=$04, dur=$0F, vol=$50) ; x-ref: $295E
$2670a2 0fldx#$0f; load 16 debris X-positions from f1CD0 into crash_debris_x
$2672bd d0 1cb_2672ldadebris_start_x_positions,x; x-ref: $2678
$267595 40stazp_crash_debris_x,x
$2677cadex
$267810 f8bplb_2672
$267Aa9 01lda#GameMode.CRASH_DEBRIS; game_mode = 1 (debris / lap-start animation), no obstacle spawning yet
$267C85 02stazp_game_mode
$267Ea9 00lda#$00
$268085 2bstazp_spawn_cooldown
$268260rts
$2683.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Stops the bike and resets it to the left track edge, entering mode 2
; (starting-gate / stopped state). Zeroes fractional position and velocity,
; pins bike_pos_hi to track_left_edge, and sets game_mode=2.
; Called on boundary violations and soft mode-transition recoveries.
;
; Inputs: track_left_edge = left boundary to pin bike to
; Outputs: bike_pos_lo=0, bike_vel=0, game_mode=2, bike_pos_hi=track_left_edge
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2684a9 00stop_bike_and_resetlda#$00; zero fractional position and velocity ; x-ref: $2842, $2937
$268685 28stazp_bike_pos_x_lo
$268885 29stazp_bike_vel
$268Aa9 02lda#GameMode.ACTIVE_RIDE; game_mode = 2 (starting gate / stopped)
$268C85 02stazp_game_mode
$268Ea5 37ldazp_track_left_edge; pin bike to left track boundary
$269085 27stazp_bike_pos_x_hi
$269260rts
$2693.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Transition: enter riding mode (mode 3).
; Sets game_mode=3 (player active / in-play state). Called after setting
; player_active=$FF, signaling the player has engaged from the starting gate
; and normal bike-riding logic should take over.
;
; Inputs: None
; Outputs: game_mode=3
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2694a9 03enter_mode_ridinglda#GameMode.STARTING_GATE; game_mode = 3 (player active / riding) ; x-ref: $233C
$269685 02stazp_game_mode
$269860rts
$2699.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Transition: enter track scroll mode (mode 4).
; Sets game_mode=4, clears scroll_dir_flag, calls s2964 to init scroll state.
; Called when starting gate throttle reaches zero.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enter_mode_track_scroll
$269Ca9 04lda#GameMode.TRACK_SCROLL; game_mode = 4 (track scroll) ; x-ref: $282C
$269E85 02stazp_game_mode
$26A0a9 00lda#$00
$26A285 31stazp_scroll_dir_flag
$26A420 64 29jsrinit_score_screen; initialize score screen (results display)
$26A760rts
$26A8.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Trigger crash - initializes debris explosion and enters mode 5.
; Sets all 8 debris particles to X=$10, Y=bike_pos_hi/4 (bike screen col).
; Zeroes track_progress and bike_throttle, triggers crash sound (s2B8C).
; Called on boundary violations and obstacle collisions.
;
; Inputs: bike_pos_hi
; Outputs: crash_debris_x/y[0..7] (initialized), game_mode=5,
; track_progress=0, bike_throttle=0
; Side Effects: Crash sound (s2B8C)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$26ACa2 07trigger_crashldx#$07; --- Init 8 debris particles at bike position --- ; x-ref: $21AF, $27B7, $2933, $2D5D
$26AEa9 10b_26AElda#$10; all particles start at X=$10 (center) ; x-ref: $26B9
$26B095 40stazp_crash_debris_x,x
$26B2a5 27ldazp_bike_pos_x_hi
$26B44alsra; Y = bike screen column (pos_hi >> 2)
$26B54alsra
$26B695 48stazp_crash_debris_y,x
$26B8cadex
$26B910 f3bplb_26AE
$26BBa9 05lda#GameMode.DEBRIS_WAIT; game_mode = 5 (debris wait)
$26BD85 02stazp_game_mode
$26BFa9 00lda#$00
$26C185 36stazp_track_progress
$26C3a9 00lda#$00
$26C585 2astazp_bike_throttle
$26C720 8c 2bjsrsfx_crash_explosion; trigger crash sound effect
$26CA60rts
$26CB.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Setup course-end screen - copies 16-char text from f1CE0 to screen $0D4E
; with colour $71, kills all 4 obstacle slots, resets track_progress,
; and enters mode 6 (results screen).
; Called when no laps remain after debris clears.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
setup_course_end_screen
$26CCa2 0fldx#$0f; x-ref: $28BD
$26CEbd e0 1cb_26CEldacourse_end_text,x; copy 16-char course-end text from f1CE0 to screen ; x-ref: $26DA
$26D19d 4e 0dstaSCREEN_RAM_R8C14,x
$26D4a9 71lda#$71; $71 = course-end text colour
$26D69d 4e 09staCOLOR_RAM_R8C14,x
$26D9cadex
$26DA10 f2bplb_26CE
$26DCa9 00lda#$00
$26DE85 36stazp_track_progress
$26E0a9 06lda#GameMode.RESULTS_SCREEN; game_mode = 6 (results screen)
$26E285 02stazp_game_mode
$26E4a2 03ldx#$03
$26E6a9 fflda#$ff; kill all 4 obstacle slots ($FF)
$26E895 10b_26E8stazp_obstacle_flags,x; x-ref: $26EB
$26EAcadex
$26EB10 fbbplb_26E8
$26ED60rts
$26EE.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Transition: enter velocity correction mode (mode 7).
; Stores A into bike_vel, sets game_mode=7.
; Called from physics boundary checks and throttle handlers.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enter_mode_vel_correct
$26F085 29stazp_bike_vel; store velocity (A) and set game_mode=7 ; x-ref: $2772, $2B5C, $2B74, $2E7C, $2E88
$26F2a9 07lda#GameMode.VELOCITY_INTEGRATE
$26F485 02stazp_game_mode
$26F660rts
$26F7.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Transition: enter next lap mode (mode 8).
; Sets bike_throttle=$60 (mid), game_mode=8.
; Called when laps remain after debris clears.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$26F8a9 60enter_mode_next_laplda#$60; throttle=$60, game_mode=8 (next lap) ; x-ref: $28B9
$26FA85 2astazp_bike_throttle
$26FCa9 08lda#GameMode.NEXT_LAP_INIT
$26FE85 02stazp_game_mode
$270060rts
$2701.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Joystick input scanner for the Plus/4.
; Reads the joystick port by scanning two keyboard matrix rows via $FD30
; (keyboard latch / row select) and $FF08 (TED keyboard input register).
; Joystick signals are active-low on the hardware; this routine extracts
; individual direction and fire bits from two matrix scans, remaps them
; into a single byte, and inverts the direction nibble to active-high.
;
; Scan 1: $FD30=$FA -> $FF08 read -> Y (directions A + fire bit 6)
; Scan 2: $FD30=$FD -> $FF08 read -> X (directions B)
; Bit remap: X bits are merged into Y by conditionally clearing Y bits.
; Final: a33 = ((Y & $0F) | ((Y & $40) << 1)) XOR $0F
;
; Output bit layout (a33):
; Bit 0: Direction 1 Bit 4-6: Unused (0)
; Bit 1: Direction 2 Bit 7: Fire button
; Bit 2: Tilt back (subtract) (directions are active-high;
; Bit 3: Tilt forward (add) fire is active-high)
;
; Inputs: $FD30, $FF08 (hardware keyboard matrix registers)
; Outputs: a33 ($33) -- normalized joystick state byte
; Side Effects: Briefly disables IRQs (SEI/CLI) during hardware scan
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$270478read_joysticksei; Disable IRQs during keyboard matrix scan ; x-ref: $20E3
$2705a9 falda#$fa; Scan row 1: select keyboard matrix row $FA via $FD30
$27078d 30 fdsta$fd30
$270A8d 08 ffsta$ff08
$270Dac 08 ffldy$ff08; Y = row 1 result (directions A + fire in bit 6)
$2710a9 fdlda#$fd; Scan row 2: select keyboard matrix row $FD via $FD30
$27128d 30 fdsta$fd30
$27158d 08 ffsta$ff08
$2718ae 08 ffldx$ff08; X = row 2 result (directions B)
$271B58cli; Re-enable IRQs -- scan complete
$271C8atxa; --- Bit remap: merge X direction bits into Y ---
$271D29 10and#$10; X bit 4 -> if clear, clear Y bit 1
$271Fd0 04bneb_2725
$272198tya
$272229 fdand#$fd
$2724a8tay
$27258ab_2725txa; X bit 2 -> if clear, clear Y bit 0 ; x-ref: $271F
$272629 04and#$04
$2728d0 04bneb_272E
$272A98tya
$272B29 feand#$fe
$272Da8tay
$272E8ab_272Etxa; X bit 0 -> if clear, clear Y bit 2 ; x-ref: $2728
$272F29 01and#$01
$2731d0 04bneb_2737
$273398tya
$273429 fband#$fb
$2736a8tay
$27378ab_2737txa; X bit 3 -> if clear, clear Y bit 3 ; x-ref: $2731
$273829 08and#$08
$273Ad0 04bneb_2740
$273C98tya
$273D29 f7and#$f7
$273Fa8tay
$274098b_2740tya; --- Final assembly: combine directions + fire into a33 --- ; x-ref: $273A
$274129 0fand#$0f
$274385 33stazp_joystick_state; Store direction nibble (bits 0-3)
$274598tya
$274629 40and#$40; Extract fire bit: Y bit 6 -> ASL -> bit 7 -> OR into a33
$27480aasla
$274905 33orazp_joystick_state
$274B49 0feor#$0f; Invert direction nibble: active-low -> active-high (fire bit 7 unchanged)
$274D85 33stazp_joystick_state; Final joystick state stored in a33
$274F60rts
$2750.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 0: Normal riding state - dispatches movement based on fire/jump button.
; Reads joystick bit 7 (fire/jump); if held, calls the jump movement routine,
; otherwise calls the standard forward movement routine.
;
; Inputs: joystick_state (bit 7 = fire/jump button)
; Outputs: None (delegates to movement subroutines)
; Side Effects: Calls s2640 (normal move) or s2DB4 (jump move)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_normal_ride
$2754a5 33ldazp_joystick_state; x-ref: $25F7
$275629 80and#$80; isolate fire/jump button (bit 7)
$2758d0 04bneb_275E; button held -> jump movement
$275A20 40 26jsrinit_course_state; normal movement (no jump)
$275D60rts
$275E20 b4 2db_275Ejsrhandle_jump_and_level_select; jump movement (fire held) ; x-ref: $2758
$276160rts
$2762.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 1: Crash debris flight - waits for debris to reach landing spot,
; then respawns the bike at its starting position with neutral throttle.
; The debris landing position is ($10, $0B). Once reached, bike_pos is reset
; to $2A00 and throttle is set to $80 (neutral mid-point).
;
; Inputs: crash_debris_x, crash_debris_y (debris position)
; Outputs: bike_pos_hi, bike_pos_lo (reset to $2A, $00), bike_throttle (reset to $80)
; Side Effects: Calls s26F0 (likely sprite/screen reset at #$30)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_crash_debris
$2764a5 40ldazp_crash_debris_x; x-ref: $25FA
$2766c9 10cmp#$10; debris landed at x=$10?
$2768d0 17bner_2781
$276Aa5 48ldazp_crash_debris_y
$276Cc9 0bcmp#$0b; debris landed at y=$0B?
$276Ed0 11bner_2781
$2770a9 30lda#$30; trigger respawn display/sprite reset
$277220 f0 26jsrenter_mode_vel_correct
$2775a9 2alda#$2a
$277785 27stazp_bike_pos_x_hi; reset bike position hi = $2A
$2779a9 00lda#$00
$277B85 28stazp_bike_pos_x_lo; reset bike position lo = $00
$277Da9 80lda#$80
$277F85 2astazp_bike_throttle; reset throttle to neutral ($80)
$278160r_2781rts; x-ref: $2768, $276E
$2782.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 2: Active riding - processes throttle input, enforces track
; boundaries, updates bike physics, and triggers surface audio/visual effects.
; - Joystick bit 3: throttle up (+3, capped at $F9)
; - Joystick bit 2: throttle down (-3, floored at $63)
; - If bike_pos_hi > track_right_edge or < track_left_edge: crash (s26AC)
; - Otherwise: run bike physics update (s2E74)
; - If track_flags & prev_track_flags & $40: trigger surface FX sound (s2B74)
; - If fire not held: trigger idle sound/state (s2B5C with #$CC)
;
; Inputs: joystick_state, bike_throttle, bike_pos_hi, track_right_edge, track_left_edge,
; track_flags, prev_track_flags
; Outputs: bike_throttle (adjusted), delegates physics to s2E74
; Side Effects: May trigger crash (s26AC), physics (s2E74), sounds (s2B74, s2B5C)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_active_ride
$2784a5 33ldazp_joystick_state; x-ref: $25FD
$278629 08and#$08; isolate throttle-up input (bit 3)
$2788f0 0dbeqb_2797; no throttle-up input -> skip
$278Aa5 2aldazp_bike_throttle
$278Cc9 f9cmp#$f9; already at max throttle ($F9)?
$278Eb0 07bcsb_2797; at cap -> skip increase
$2790a5 2aldazp_bike_throttle
$279218clc
$279369 03adc#$03; throttle += 3
$279585 2astazp_bike_throttle
$2797a5 33b_2797ldazp_joystick_state; x-ref: $2788, $278E
$279929 04and#$04; isolate throttle-down input (bit 2)
$279Bf0 0dbeqb_27AA; no throttle-down input -> skip
$279Da5 2aldazp_bike_throttle
$279Fc9 63cmp#$63; already at min throttle ($63)?
$27A190 07bccb_27AA; at floor -> skip decrease
$27A3a5 2aldazp_bike_throttle
$27A538sec
$27A6e9 03sbc#$03; throttle -= 3
$27A885 2astazp_bike_throttle
$27AAeab_27AAnop; x-ref: $279B, $27A1
$27ABa5 39ldazp_track_right_edge; right track edge limit
$27ADc5 27cmpzp_bike_pos_x_hi
$27AF90 06bccb_27B7; bike past right edge -> crash
$27B1a5 37ldazp_track_left_edge; left track edge limit
$27B3c5 27cmpzp_bike_pos_x_hi
$27B5b0 04bcsb_27BB; bike within bounds -> normal physics
$27B720 ac 26b_27B7jsrtrigger_crash; out of bounds: trigger crash ; x-ref: $27AF
$27BA60rts
$27BB20 74 2eb_27BBjsrcheck_right_boundary; in bounds: run bike physics update ; x-ref: $27B5
$27BEeanop
$27BFeanop
$27C0eanop
$27C1a5 38ldazp_track_flags
$27C325 3aandzp_prev_track_flags; both current & previous frame have bit 6 set
$27C529 40and#$40; bit 6: special surface flag (ramp/mud?)
$27C7f0 06beqb_27CF; no surface effect -> check fire button
$27C9a9 a0lda#$a0; surface effect sound/action ($A0)
$27CB20 74 2bjsrsfx_surface_impact
$27CE60rts
$27CFa5 33b_27CFldazp_joystick_state; x-ref: $27C7
$27D129 80and#$80; fire/jump button held? (bit 7)
$27D3d0 06bner_27DB; fire held -> skip idle sound
$27D5a9 cclda#$cc; idle engine sound/state ($CC)
$27D720 5c 2bjsrsfx_idle_landing
$27DA60rts
$27DB60r_27DBrts; x-ref: $27D3
$27DC.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; 24-bit linear congruential PRNG.
; Advances a 3-byte state (rng_state_lo/mid/hi) using additive constants
; $45, $72, $BD with inter-byte feedback. Returns result in A (= rng_state_hi).
;
; Inputs: rng_state_lo, rng_state_mid, rng_state_hi
; Outputs: A = new rng_state_hi, all three state bytes updated
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$27E0a5 2cprng_nextldazp_rng_state_lo; x-ref: $2C5A, $2C78, $2CAC, $2CB6, $2CCD
$27E218clc
$27E369 45adc#$45
$27E585 2cstazp_rng_state_lo
$27E7a5 2dldazp_rng_state_mid
$27E969 72adc#$72
$27EB65 2cadczp_rng_state_lo
$27ED85 2dstazp_rng_state_mid
$27EFa5 2eldazp_rng_state_hi
$27F169 bdadc#$bd
$27F365 2dadczp_rng_state_mid
$27F585 2estazp_rng_state_hi
$27F760rts
$27F8.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Obstacle life ticker - per-frame lifetime countdown for all 4 obstacle slots.
; Decrements obstacle_life[X] for each active slot. When life underflows
; to $FC, marks the slot dead (obstacle_flags = $FF).
; Also increments spawn_cooldown each call, pacing new obstacle spawns.
;
; Always called in pairs during non-riding phases (gate, debris wait, scroll)
; to age obstacles at double speed, clearing the screen faster.
;
; Inputs: obstacle_flags[0..3], obstacle_life[0..3], spawn_cooldown
; Outputs: obstacle_life (decremented), obstacle_flags ($FF on expiry),
; spawn_cooldown (incremented)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$27FCa2 03obstacle_life_tickldx#$03; 4 obstacle slots (X=3..0) ; x-ref: $2501, $251A, $2818, $281B, $28A5, $28A8
$27FEb5 10b_27FEldazp_obstacle_flags,x; x-ref: $2811
$2800c9 ffcmp#$ff; dead slot ($FF)? -> skip
$2802f0 0cbeqb_2810
$2804d6 14deczp_obstacle_life,x; decrement lifetime counter
$2806b5 14ldazp_obstacle_life,x
$2808c9 fccmp#$fc; underflowed to $FC? -> expired
$280Ad0 04bneb_2810
$280Ca9 fflda#$ff; kill slot: mark dead ($FF)
$280E95 10stazp_obstacle_flags,x
$2810cab_2810dex; x-ref: $2802, $280A
$281110 ebbplb_27FE
$2813e6 2binczp_spawn_cooldown; advance spawn cooldown timer
$281560rts
$2816.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Starting gate throttle decay - per-frame handler during gate phase (mode 3).
; Ages obstacle timers at double speed (s27FC x2), then decays bike_throttle
; by 4 per frame (rounded to multiples of 4 via AND #$FC).
; When throttle reaches zero, transitions to track scroll mode (mode 4)
; via s269C.
;
; Inputs: bike_throttle, obstacle timers (via s27FC)
; Outputs: bike_throttle (decreased by 4, clamped to mult of 4)
; Side Effects: s27FC x2 (obstacle aging), s269C (mode 4 transition on zero)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
starting_gate_throttle_decay
$281820 fc 27jsrobstacle_life_tick; age obstacle timers at double speed ; x-ref: $2EC1
$281B20 fc 27jsrobstacle_life_tick
$281Ea5 2aldazp_bike_throttle
$2820c9 00cmp#$00; throttle already zero?
$2822f0 08beqb_282C; yes -> transition to track scroll mode
$282438sec
$2825e9 04sbc#$04; decay throttle by 4 per frame
$282729 fcand#$fc; round down to nearest 4 (clear low 2 bits)
$282985 2astazp_bike_throttle
$282B60rts
$282C20 9c 26b_282Cjsrenter_mode_track_scroll; throttle zero: set game_mode=4 (track scroll) ; x-ref: $2822
$282F60rts
$2830.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 4: Track scrolling phase - advances track_progress each tick with
; a matching engine pitch sound, handles the end-of-scroll stop, and runs the
; finish-line digit countdown animation.
;
; Three phases based on track_progress:
; != $A0 (scrolling): call s2BF4 to increment progress and update engine sound.
; When progress hits $E0: stop bike (game_mode=2), reset position, clear
; track display buffers (s2A2C).
; == $A0 (finish anim): animate digit counters on screen RAM at $0D0D-$0D0F
; and $0D5D-$0D5F (PETSCII cycling). When done, advance course_index
; and load next course layout (s2A18).
;
; Inputs: track_progress, snd_pitch, course_index,
; screen RAM $0D0D-$0D0F and $0D5D-$0D5F
; Outputs: track_progress, course_index, game_mode, bike_throttle,
; bike_pos_hi/lo, bike_vel, snd_pitch/delta/duration/volume,
; screen RAM (digit counters and track buffers)
; Side Effects: Screen buffer clear (s2A2C), next-course load (s2A18),
; scroll tile update (s29B4)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_track_scroll
$2834a5 36ldazp_track_progress; x-ref: $2603
$2836c9 a0cmp#$a0; finish-line animation phase?
$2838f0 13beqb_284D; yes -> run digit countdown animation
$283A20 f4 2bjsradvance_progress_and_engine_sfx; advance track_progress + update engine pitch sound
$283Deanop
$283Ec9 e0cmp#$e0; has scroll reached end-of-course?
$2840d0 0abner_284C; no -> keep scrolling
$284220 84 26jsrstop_bike_and_reset; stop bike: zero pos/vel, set game_mode=2, pin bike to left edge
$2845a9 60lda#$60
$284785 2astazp_bike_throttle; set throttle to $60 (mid)
$284920 2c 2ajsrclear_track_buffers; clear track display buffers ($0C50 and $0D50, 256 bytes each)
$284C60r_284Crts; x-ref: $2840
$284Da2 02b_284Dldx#$02; animate 3 digit chars at $0D0D-$0D0F (top row) ; x-ref: $2838
$284Fde 0d 0db_284FdecSCREEN_RAM_R6C29,x; decrement digit char value ; x-ref: $285F
$2852bd 0d 0dldaSCREEN_RAM_R6C29,x
$2855c9 51cmp#$51; wrapped past $51? (digit cycle point)
$2857d0 36bneb_288F; not wrapped -> scroll update and return
$2859a9 5blda#$5b; wrap char back to $5B (animation cycle)
$285B9d 0d 0dstaSCREEN_RAM_R6C29,x
$285Ecadex
$285F10 eebplb_284F
$2861a9 52lda#$52; reset top row digits to $52 when animation completes
$28638d 0d 0dstaSCREEN_RAM_R6C29
$28668d 0e 0dstaSCREEN_RAM_R6C30
$28698d 0f 0dstaSCREEN_RAM_R6C31
$286Ca2 02ldx#$02
$286Ede 5d 0db_286EdecSCREEN_RAM_R8C29,x; decrement digit char value (bottom row) ; x-ref: $287E
$2871bd 5d 0dldaSCREEN_RAM_R8C29,x
$2874c9 51cmp#$51; wrapped past $51? (digit cycle point)
$2876d0 17bneb_288F; not wrapped -> scroll update and return
$2878a9 5blda#$5b; wrap char back to $5B (animation cycle)
$287A9d 5d 0dstaSCREEN_RAM_R8C29,x
$287Dcadex
$287E10 eebplb_286E
$2880a9 52lda#$52; reset bottom row digits to $52 when animation completes
$28828d 5d 0dstaSCREEN_RAM_R8C29
$28858d 5e 0dstaSCREEN_RAM_R8C30
$28888d 5f 0dstaSCREEN_RAM_R8C31
$288B20 18 2ajsradvance_course_layout; both rows done: inc progress, advance course, load next layout
$288E60rts
$288F20 b4 29b_288Fjsrupdate_scroll_tiles; animation still running: update scroll tile positions ; x-ref: $2857, $2876
$289260rts
$289300.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 5: Debris wait / lap-end transition.
; Waits for all 8 crash_debris_y slots to read $FF (inactive). While waiting,
; returns immediately each tick. Once all debris is clear, advances
; track_progress each tick (aging obstacles via s27FC x2). When progress
; reaches $A0 the lap outcome is resolved:
; - Laps remain (lives_counter > 0): throttle=$60, game_mode=8 (next lap).
; - No laps left (lives_counter = 0): set up course-end screen, game_mode=6.
; Special case: if track_progress == 0 when debris clears, lives_counter is
; decremented first (counts the completed lap before deciding the outcome).
;
; Inputs: crash_debris_y[0..7], track_progress, lives_counter,
; obstacle_life[], obstacle_flags[]
; Outputs: track_progress, lives_counter, game_mode, bike_throttle,
; obstacle_flags[], spawn_cooldown, screen RAM $0D4E, color RAM $094E
; Side Effects: Course-end screen setup (s26CC), next-lap start (s26F8)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_debris_wait
$2894a2 07ldx#$07; x-ref: $2606
$2896b5 48b_2896ldazp_crash_debris_y,x; check debris slot X: $FF = inactive ; x-ref: $289D
$2898c9 ffcmp#$ff
$289Ad0 20bner_28BC; slot still active -> wait, exit immediately
$289Ccadex
$289D10 f7bplb_2896
$289Fa5 36ldazp_track_progress; all 8 slots clear; check if this is a fresh lap (progress==0)
$28A1d0 02bneb_28A5; progress != 0 -> skip lap count decrement
$28A3c6 30deczp_lives_counter; progress==0: count this lap as done
$28A520 fc 27b_28A5jsrobstacle_life_tick; age obstacle timers (run twice for double speed decay) ; x-ref: $28A1
$28A820 fc 27jsrobstacle_life_tick; second obstacle aging pass
$28ABe6 36inczp_track_progress; advance progress toward lap-end threshold ($A0)
$28ADa5 36ldazp_track_progress
$28AFc9 a0cmp#$a0; reached lap-end threshold?
$28B1d0 09bner_28BC; not yet -> keep counting
$28B3a5 30ldazp_lives_counter; progress=$A0: any laps left?
$28B5c9 00cmp#$00
$28B7f0 04beqb_28BD; no laps left -> course end
$28B920 f8 26jsrenter_mode_next_lap; laps remain: throttle=$60, game_mode=8 (start next lap)
$28BC60r_28BCrts; x-ref: $289A, $28B1
$28BD20 cc 26b_28BDjsrsetup_course_end_screen; no laps: load course-end screen, game_mode=6, clear obstacles ; x-ref: $28B7
$28C060rts
$28C1.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 6: Course-end results screen - plays a descending pitch sweep
; sound over ~$D0 ticks as track_progress counts from 0 to $F0.
; Each tick: progress is incremented, a sound note is computed from the
; current progress value (lower 5 bits set pitch offset, bits 5-6 set
; octave), combined, offset by $20, then inverted (EOR $FF) so the pitch
; descends as progress increases. Duration=1 tick, volume=$30.
; First ~32 ticks ($00-$20): BMI skips sound (silent lead-in).
; When track_progress reaches $F0: calls reset_basic_runtime_state and
; returns, ending the results screen and handing control back to menu/title.
;
; Inputs: track_progress (set to 0 by s26CC on entry to this mode)
; Outputs: track_progress, snd_pitch, snd_pitch_delta, snd_duration,
; snd_volume, ted_ctrl_staging
; Side Effects: Calls reset_basic_runtime_state when sequence completes
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_results_screen
$28C4e6 36inczp_track_progress; advance results sequence timer ; x-ref: $2609
$28C6a5 36ldazp_track_progress
$28C8c9 f0cmp#$f0; sequence complete ($F0 ticks)?
$28CAd0 04bneb_28D0; not done -> compute sound pitch
$28CC20 14 26jsrreset_basic_runtime_state; done: wipe BASIC ZP state, return to menu
$28CF60rts
$28D038b_28D0sec; x-ref: $28CA
$28D1e9 20sbc#$20; offset from $20 baseline
$28D330 1fbmir_28F4; progress < $21 -> silent lead-in, skip sound
$28D548pha
$28D629 1fand#$1f; lower 5 bits -> pitch offset
$28D80aasla; double for note spacing
$28D985 f8stazp_ted_ctrl_staging
$28DB68pla
$28DC29 60and#$60; bits 5-6 -> octave/range selector
$28DE18clc
$28DF65 f8adczp_ted_ctrl_staging
$28E118clc
$28E269 20adc#$20
$28E449 ffeor#$ff; invert -> descending pitch as progress increases
$28E685 50stazp_snd_pitch
$28E8a9 00lda#$00
$28EA85 51stazp_snd_pitch_delta; no pitch slide between notes
$28ECa9 01lda#$01; single-tick note duration
$28EE85 54stazp_snd_duration
$28F0a9 30lda#$30; fixed volume for results jingle
$28F285 55stazp_snd_volume
$28F460r_28F4rts; x-ref: $28D3
$28F5.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Core bike physics: velocity correction + 16-bit position integration.
; Reads throttle input (via s2E6C -> s2E44), biases velocity rightward (+2),
; clamps positive velocity to $60, then integrates velocity into bike_pos
; four times (effective displacement = 4 * bike_vel per call).
; After integration, checks track boundaries:
; - Within bounds: return cleanly.
; - Out of bounds + track_flags bit 7 set: full crash (s26AC, debris).
; - Out of bounds + track_flags bit 7 clear: soft recovery (s2684,
; zero vel, game_mode=2, pin to left edge).
;
; Inputs: joystick_state (via s2E44), bike_vel, bike_pos_hi/lo,
; bike_throttle, track_right_edge, track_left_edge, track_flags
; Outputs: bike_vel (biased + clamped), bike_pos_hi/lo (integrated),
; bike_throttle (via s2E44)
; Side Effects: May trigger crash (s26AC) or soft recovery (s2684)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
apply_velocity_and_integrate
$28F820 6c 2ejsrupdate_throttle_and_load_vel; read throttle input + load bike_vel with CLC ; x-ref: $260C, $2EBE
$28FB69 02adc#$02; bias velocity rightward (+2 correction)
$28FD85 29stazp_bike_vel
$28FF30 08bmib_2909; negative velocity -> skip cap (allow leftward)
$2901c9 60cmp#$60; positive vel > $60?
$290390 04bccb_2909
$2905a9 60lda#$60; clamp to max rightward velocity $60
$290785 29stazp_bike_vel
$2909a2 03b_2909ldx#$03; integrate velocity into position 4 times (X=3..0) ; x-ref: $28FF, $2903
$290Ba5 29b_290Bldazp_bike_vel; x-ref: $291F
$290D10 02bplb_2911; positive vel -> skip hi pre-decrement
$290Fc6 27deczp_bike_pos_x_hi; negative vel: sign-extend by pre-decrementing hi byte
$2911a5 28b_2911ldazp_bike_pos_x_lo; x-ref: $290D
$291318clc
$291465 29adczp_bike_vel; pos_lo += vel (16-bit add: carry propagates to hi)
$291685 28stazp_bike_pos_x_lo
$2918a5 27ldazp_bike_pos_x_hi
$291A69 00adc#$00
$291C85 27stazp_bike_pos_x_hi
$291Ecadex
$291F10 eabplb_290B; repeat 4x -> effective displacement = 4 * vel
$2921a5 39ldazp_track_right_edge; bounds check: is bike past right edge?
$2923c5 27cmpzp_bike_pos_x_hi
$292590 06bccb_292D; past right -> out of bounds
$2927a5 37ldazp_track_left_edge; bounds check: is bike past left edge?
$2929c5 27cmpzp_bike_pos_x_hi
$292Bb0 0dbcsr_293A; within bounds -> return cleanly
$292Da5 38b_292Dldazp_track_flags; x-ref: $2925
$292F29 80and#$80; bit 7 = crash-enabled for this track segment?
$2931f0 04beqb_2937; crash disabled -> soft recovery
$293320 ac 26jsrtrigger_crash; crash enabled: full crash with debris explosion
$293660rts
$293720 84 26b_2937jsrstop_bike_and_reset; soft recovery: zero vel, game_mode=2, pin to left edge ; x-ref: $2931
$293A60r_293Arts; x-ref: $292B
$293B.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Negates the velocity of obstacle slot X (two's-complement bounce).
; Called when an obstacle crosses the upper ($30) or lower ($10) track
; boundary - flips its direction so it bounces back inside the playfield.
;
; Inputs: X = obstacle slot index; obstacle_vel,x = current velocity
; Outputs: obstacle_vel,x = negated velocity (direction reversed)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$293Cb5 20negate_obstacle_velldazp_obstacle_vel,x; x-ref: $2551
$293E49 ffeor#$ff; one's complement (invert all bits)
$294018clc
$294169 01adc#$01; +1 -> two's complement negation complete
$294395 20stazp_obstacle_vel,x
$294560rts
$2946.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 8: Next-lap initialization wait.
; Polls the object record status byte at offset $10 each tick. When bits 6-7
; are set (signaling screen/scroll readiness), resets the bike to the left
; track edge with neutral throttle ($60), zeroes velocity and position lo,
; initializes the lap-start debris animation from f1CD0, and transitions
; to game_mode=1 (crash debris / lap-start animation).
; Until the readiness bits are set, returns immediately (wait state).
;
; Inputs: (ptr_obj_record_lo)+$10 (object record status), track_left_edge
; Outputs: bike_throttle ($60), bike_pos_hi (left edge), bike_pos_lo (0),
; bike_vel (0), game_mode (-> 1), spawn_cooldown (0),
; crash_debris_x[0..15] (from f1CD0)
; Side Effects: Calls s2BDC (sound/visual trigger), starts lap animation
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_next_lap_init
$2948a0 10ldy#$10; offset $10 into object record = status byte ; x-ref: $260F
$294Ab1 24lda(zp_ptr_obj_record_lo),y; read status byte from object record
$294C29 c0and#$c0; isolate readiness bits 6-7
$294Ef0 11beqr_2961; not ready yet -> wait (return immediately)
$2950a9 60lda#$60; ready: set neutral throttle
$295285 2astazp_bike_throttle
$2954a9 00lda#$00
$295685 28stazp_bike_pos_x_lo; zero position lo + velocity
$295885 29stazp_bike_vel
$295Aa5 37ldazp_track_left_edge; pin bike to left track edge
$295C85 27stazp_bike_pos_x_hi
$295E20 6d 26jsrinit_lap_start_anim_no_kill; init debris from f1CD0, game_mode=1, spawn_cooldown=0
$296160r_2961rts; x-ref: $294E
$2962.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the end-of-course results/score screen.
; 1. Copies 24-char score headings from f1CF0 ("bonus for time left") and
; f1D08 ("bikes left bonus") to screen RAM $0CF8/$0D48; fills colour
; RAM $08F8/$0948 with $71 (standard score colour).
; 2. Converts position values $0C07/$0C08/$0C0A into score display chars
; (+$47) and writes to screen cells $0D0D/$0D0E/$0D0F.
; 3. Looks up two per-lap bonus digit chars from f1D38/f1D40[lives_counter]
; and writes to $0D5E/$0D5F.
; 4. Calls s2A4A: awards an extra bike (increments lives_counter, shows
; f1D48 "extra bike awarded" banner at $0D98) if course_index < $10
; and lives_counter != 5; otherwise shows f1D60 text.
; 5. Clears track_progress.
;
; Inputs: $0C07/$0C08/$0C0A (score values), lives_counter, course_index
; Outputs: Screen/colour RAM score display populated, track_progress=0,
; lives_counter possibly incremented (via s2A4A)
; Side Effects: Calls s2A4A (extra bike award + banner display)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2964a2 17init_score_screenldx#$17; copy 24-char score headings from f1CF0/f1D08, colour $71 ; x-ref: $26A4
$2966bd f0 1cb_2966ldatime_bonus_text,x; "bonus for time left" heading -> screen RAM $0CF8 ; x-ref: $297D
$29699d f8 0cstaSCREEN_RAM_R6C8,x
$296Cbd 08 1dldabikes_left_bonus_text,x; "bikes left bonus" heading -> screen RAM $0D48
$296F9d 48 0dstaSCREEN_RAM_R8C8,x
$2972a9 71lda#$71; $71 = standard score-screen colour -> colour RAM $08F8 and $0948
$29749d f8 08staCOLOR_RAM_R6C8,x
$2977a9 71lda#$71
$29799d 48 09staCOLOR_RAM_R8C8,x
$297Ccadex
$297D10 e7bplb_2966
$297Feanop
$2980eanop
$2981ad 07 0cldaSCREEN_RAM_R0C7; convert position values to score chars (+$47) -> $0D0D/$0D0E/$0D0F
$298418clc
$298569 47adc#$47
$29878d 0d 0dstaSCREEN_RAM_R6C29
$298Aad 08 0cldaSCREEN_RAM_R0C8
$298D18clc
$298E69 47adc#$47
$29908d 0e 0dstaSCREEN_RAM_R6C30
$2993ad 0a 0cldaSCREEN_RAM_R0C10
$299618clc
$299769 47adc#$47
$29998d 0f 0dstaSCREEN_RAM_R6C31
$299Ca6 30ldxzp_lives_counter; per-lap bonus digits from f1D38/f1D40[lives_counter] -> $0D5E/$0D5F
$299Ebd 38 1dldabonus_value_char1,x
$29A18d 5e 0dstaSCREEN_RAM_R8C30
$29A4bd 40 1dldabonus_value_char2,x
$29A78d 5f 0dstaSCREEN_RAM_R8C31
$29AA20 4a 2ajsraward_extra_bike; award extra bike + show banner if eligible, fill $0D98 score row
$29ADa9 00lda#$00
$29AF85 36stazp_track_progress
$29B160rts
$29B2.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame track scroll tile position updater.
; Looks up the number of increment steps for the current course from
; f1E10[course_index & $0F] (1-5 steps; higher courses scroll faster) and
; calls s29C8 that many times. Each call increments 7 tile position pairs
; ($0C13,x / $0C3B,x for X=6..0), wrapping at $15 back to $0B/$1F.
; Then calls s2BBC to tick the scroll sound effect: if snd_duration==0,
; picks a pitch from (frame_counter & $07)*4 + $A0, sets duration=3,
; volume=$10 (pitch delta $30).
; Called every frame from handle_mode_debris_wait while animation runs.
;
; Inputs: course_index (scroll speed), $0C13,x/$0C3B,x (7 tile pairs),
; snd_duration, frame_counter
; Outputs: $0C13,x/$0C3B,x updated (wrapped at $15->$0B/$1F),
; snd_pitch/snd_duration/snd_volume possibly updated
; Side Effects: Scroll sound tick via s2BBC
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$29B4a5 2fupdate_scroll_tilesldazp_course_index; speed = f1E10[course & $0F]: 1-5 steps/frame (faster on later courses) ; x-ref: $288F
$29B629 0fand#$0f
$29B8aatax
$29B9bd 10 1eldacourse_scroll_steps,x; Y = step count for this course
$29BCa8tay
$29BD20 c8 29jsrtick_scroll_tiles; increment tile positions Y times (s29C8 loops X=6..0, wraps at $15->$0B)
$29C020 bc 2bjsrsfx_scroll_rumble; tick scroll sound effect
$29C360rts
$29C4.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Core single-step scroll tile engine. Advances 7 tile position pairs by
; one step (called Y times by update_scroll_tiles for variable speed).
;
; Phase 1 - Increment (X=6..0):
; Increments $0C13,x (tile A) and $0C3B,x (tile B) for each of 7 pairs.
; When $0C13,x reaches $15 (wrap point): resets A to $0B, B to $1F, then
; exits the inner DEX loop early (staggered-wrap design: only the wrapping
; slot is reset on that step). DEY re-enters from the top for remaining Y
; steps.
;
; Phase 2 - Best-position update (X=0..6):
; Scans all 7 pairs: if any current position > stored best ($0C20,x /
; $0C48,x) then copies all 7 current pairs to the best tables. If all
; are equal, returns without update. If any is below best, returns.
; Tracks the furthest scroll position reached (high-scroll record).
;
; Inputs: Y = step count; $0C13,x/$0C3B,x (7 current tile pairs);
; $0C20,x/$0C48,x (7 best-position pairs)
; Outputs: $0C13,x/$0C3B,x incremented/wrapped; $0C20,x/$0C48,x updated
; if new positions set a new best
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$29C8a2 06tick_scroll_tilesldx#$06; --- Phase 1: increment 7 tile pairs (X=6..0) --- ; x-ref: $29BD, $29E5, $2A14
$29CAfe 13 0cb_29CAincSCREEN_RAM_R0C19,x; increment tile A and tile B for pair X ; x-ref: $29E2, $2D81
$29CDfe 3b 0cincSCREEN_RAM_R1C19,x
$29D0bd 13 0cldaSCREEN_RAM_R0C19,x
$29D3c9 15cmp#$15; reached wrap point ($15)?
$29D5d0 0dbneb_29E4
$29D7a9 0blda#$0b; wrap: reset A=$0B, B=$1F; exit inner loop (staggered wrap)
$29D99d 13 0cstaSCREEN_RAM_R0C19,x
$29DCa9 1flda#$1f
$29DE9d 3b 0cstaSCREEN_RAM_R1C19,x
$29E1cadex
$29E210 e6bplb_29CA
$29E488b_29E4dey; decrement Y step counter; loop back if more steps remain ; x-ref: $29D5
$29E5d0 e1bnetick_scroll_tiles
$29E7a2 00ldx#$00; --- Phase 2: check if new positions beat the stored best ---
$29E9bd 13 0cb_29E9ldaSCREEN_RAM_R0C19,x; compare current vs best; below -> no update, above -> copy all ; x-ref: $29F6
$29ECdd 20 0ccmpSCREEN_RAM_R0C32,x
$29EF90 07bccr_29F8
$29F1d0 06bneb_29F9
$29F3e8inx
$29F4e0 07cpx#$07
$29F6d0 f1bneb_29E9
$29F860r_29F8rts; x-ref: $29EF
$29F9a2 06b_29F9ldx#$06; new best: copy all 7 current pairs to best tables $0C20/$0C48 ; x-ref: $29F1
$29FBbd 13 0cb_29FBldaSCREEN_RAM_R0C19,x; x-ref: $2A08
$29FE9d 20 0cstaSCREEN_RAM_R0C32,x
$2A01bd 3b 0cldaSCREEN_RAM_R1C19,x
$2A049d 48 0cstaSCREEN_RAM_R1C32,x
$2A07cadex
$2A0810 f1bplb_29FB
$2A0A60rts
$2A0B.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Single-step scroll tile tick, gated on game mode.
; If game_mode != 0 (not normal-ride mode), calls tick_scroll_tiles with
; Y=1 to advance all 7 tile pairs by one step. If game_mode == 0, returns
; immediately without updating tiles.
;
; Inputs: game_mode, $0C13,x/$0C3B,x (7 tile pairs)
; Outputs: $0C13,x/$0C3B,x possibly incremented (if game_mode != 0)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
tick_scroll_tiles_if_active
$2A0Ca5 02ldazp_game_mode; skip tile tick if game_mode == 0 (normal-ride mode) ; x-ref: $24EE
$2A0Ec9 00cmp#GameMode.NORMAL_RIDE
$2A10f0 05beqr_2A17
$2A12a0 01ldy#$01; Y=1: single-step tick
$2A1420 c8 29jsrtick_scroll_tiles
$2A1760r_2A17rts; x-ref: $2A10
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the game's track progress and loads parameters for the next layout.
; Increments the overall track_progress and the course_index (wrapping at 16).
; Looks up the animation frame count from course_anim_frames and delegates to
; store_course_params to update the scroll and animation state.
;
; Inputs: track_progress, course_index
; Outputs: track_progress, course_index, anim_frame_count, scroll_speed
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
advance_course_layout
$2A18e6 36inczp_track_progress; x-ref: $288B
$2A1Ae6 2finczp_course_index
$2A1Ca5 2fldazp_course_index; wrap to 0-15 (16 courses total)
$2A1E29 0fand#$0f
$2A20aatax
$2A21bd 70 1eldacourse_anim_frames,x; lookup anim_frame_count for new course
$2A2420 a8 2djsrstore_course_params; update anim_frame_count and scroll_speed
$2A2760rts
$2A28.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears two 256-byte track display buffers by filling them with $00.
; Buffer 1: $0C50-$0D4F. Buffer 2: $0D50-$0E4F.
; Used to wipe the track tile/scroll state before loading a new course.
;
; Inputs: None
; Outputs: $0C50-$0D4F = 0, $0D50-$0E4F = 0
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2A2Ca2 00clear_track_buffersldx#$00; zero-fill $0C50-$0D4F and $0D50-$0E4F (256 bytes each) ; x-ref: $2849, $2A3A
$2A2Ea9 00lda#$00
$2A309d 50 0cb_2A30staSCREEN_RAM_R2C0,x; x-ref: $2A37
$2A339d 50 0dstaSCREEN_RAM_R8C16,x
$2A36e8inx
$2A37d0 f7bneb_2A30
$2A3960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Clears track display buffers (via clear_track_buffers), then loads
; per-course animation and scroll parameters from lookup tables:
; anim_frame_count = f1E70[course_index]
; scroll_speed = f1DE0[course_index]
; Called from init_course_state to prepare a new course for play.
;
; Inputs: course_index
; Outputs: $0C50-$0E4F cleared, anim_frame_count, scroll_speed loaded
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clear_track_and_load_course
$2A3A20 2c 2ajsrclear_track_buffers; clear both track display buffers ; x-ref: $2659
$2A3Da6 2fldxzp_course_index; load anim_frame_count and scroll_speed for current course
$2A3Fbd 70 1eldacourse_anim_frames,x
$2A4285 3cstazp_anim_frame_count
$2A44bd e0 1dldacourse_scroll_speeds,x
$2A4785 53stazp_scroll_speed
$2A4960rts
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Awards an extra bike and displays the appropriate score-screen banner.
; Three paths:
; 1. lives_counter == 5 (max bikes): shows f1D60 "five bikes left still"
; 2. course_index >= $10 (past last course): no banner, footer only
; 3. Otherwise: increments lives_counter (extra bike!) and shows f1D48
; "extra bike awarded"
; All paths converge to fill the course speed footer: $78 at $0D39,
; speed digit (f1E10[course & $0F] + $52) at $0D3A, colour $71.
; Called from init_score_screen.
;
; Inputs: lives_counter (current bikes, max 5), course_index
; Outputs: lives_counter possibly incremented, screen $0D98+ banner text,
; colour $0998+ = $71, $0D39/$0D3A speed footer, $0939/$093A = $71
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2A4Aa5 30award_extra_bikeldazp_lives_counter; max bikes (5)? -> show "five bikes left still" banner ; x-ref: $29AA
$2A4Cc9 05cmp#$05
$2A4Ef0 20beqb_2A70
$2A50a5 2fldazp_course_index; past last course ($10)? -> no extra bike, skip to footer
$2A52c9 10cmp#$10
$2A54b0 2abcsb_2A80
$2A56aatax
$2A57eanop
$2A58eanop
$2A59eanop
$2A5Aeanop
$2A5Beanop
$2A5Ce6 30inczp_lives_counter; award extra bike! show "extra bike awarded" banner
$2A5Ea2 17ldx#$17
$2A60bd 48 1db_2A60ldaextra_bike_awarded_text,x; x-ref: $2A6C
$2A639d 98 0dstaSCREEN_RAM_R10C8,x
$2A66a9 71lda#$71
$2A689d 98 09staCOLOR_RAM_R10C8,x
$2A6Bcadex
$2A6C10 f2bplb_2A60
$2A6E30 10bmib_2A80
$2A70a2 17b_2A70ldx#$17; max-bikes path: show "five bikes left still" ; x-ref: $2A4E
$2A72bd 60 1db_2A72ldafive_bikes_left_text,x; x-ref: $2A7E
$2A759d 98 0dstaSCREEN_RAM_R10C8,x
$2A78a9 71lda#$71
$2A7A9d 98 09staCOLOR_RAM_R10C8,x
$2A7Dcadex
$2A7E10 f2bplb_2A72
$2A80a9 78b_2A80lda#$78; --- Footer: course speed label + digit --- ; x-ref: $2A54, $2A6E
$2A828d 39 0dstaSCREEN_RAM_R7C33
$2A85a5 2fldazp_course_index; speed digit = f1E10[course & $0F] + $52 -> $0D3A
$2A8729 0fand#$0f
$2A89aatax
$2A8Abd 10 1eldacourse_scroll_steps,x
$2A8D18clc
$2A8E69 52adc#$52
$2A908d 3a 0dstaSCREEN_RAM_R7C34
$2A93a9 71lda#$71
$2A958d 39 09staCOLOR_RAM_R7C33
$2A988d 3a 09staCOLOR_RAM_R7C34
$2A9B60rts
$2A9C.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Music sequencer tick - advances the two-voice music playback each frame.
; Each note lasts 6 frames (music_step counts 0-5). The full sequence is
; 192 notes ($C0 entries in f1948) and loops endlessly.
;
; Each byte in f1948[music_pos] encodes two notes packed in nibbles:
; High nibble -> Voice 2 (TED $FF0E/$FF12) via f1A08/f1A18 freq tables
; Low nibble -> Voice 1 (TED $FF0F/$FF10) via f1A28/f1A38 freq tables
; Both tables hold 16-entry frequency lo/hi pairs.
; Control register $FF11 is set to $38 (both voices on, square wave).
;
; Called from the jump/airborne handler - this is the stunt/jump theme.
;
; Inputs: music_step (0-5 sub-step), music_pos (0-$BF sequence position)
; Outputs: TED $FF0E-$FF12 (note frequencies + control),
; music_step/music_pos advanced (loop at $C0)
; Side Effects: Direct writes to Plus/4 TED hardware sound registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2AA0e6 34music_sequencer_tickinczp_music_step; advance sub-step (6 frames per note) ; x-ref: $2DB4
$2AA2a5 34ldazp_music_step
$2AA4c9 06cmp#$06
$2AA6d0 10bneb_2AB8
$2AA8a9 00lda#$00
$2AAA85 34stazp_music_step
$2AACe6 35inczp_music_pos; next note; loop at $C0 (192-note sequence)
$2AAEa5 35ldazp_music_pos
$2AB0c9 c0cmp#$c0
$2AB2d0 04bneb_2AB8
$2AB4a9 00lda#$00
$2AB685 35stazp_music_pos
$2AB8a6 35b_2AB8ldxzp_music_pos; --- Decode packed note byte from f1948[music_pos] --- ; x-ref: $2AA6, $2AB2
$2ABAbd 48 19ldamusic_packed_notes,x
$2ABD29 f0and#$f0; high nibble -> Voice 2 note index
$2ABF4alsra
$2AC04alsra
$2AC14alsra
$2AC24alsra
$2AC3a8tay
$2AC4b9 08 1aldavoice_2_freq_lo,y; Voice 2 freq lo/hi -> TED $FF0E/$FF12
$2AC78d 0e ffsta$ff0e
$2ACAb9 18 1aldavoice_2_freq_hi,y
$2ACD8d 12 ffsta$ff12
$2AD0bd 48 19ldamusic_packed_notes,x
$2AD329 0fand#$0f; low nibble -> Voice 1 note index
$2AD5a8tay
$2AD6b9 28 1aldavoice_1_freq_lo,y; Voice 1 freq lo/hi -> TED $FF0F/$FF10
$2AD98d 0f ffsta$ff0f
$2ADCb9 38 1aldavoice_1_freq_hi,y
$2ADF8d 10 ffsta$ff10
$2AE2a9 38lda#$38; $38 = both voices on, square wave -> TED control $FF11
$2AE48d 11 ffsta$ff11
$2AE760rts
$2AE8.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Per-frame sound engine driver for the TED chip. Manages two sound channels:
;
; Voice 1 ($FF0F/$FF10): Pitched SFX notes. Frequency = snd_pitch << 2.
; snd_pitch slides by snd_pitch_delta each frame. Duration-limited by
; snd_duration (decremented each frame; voice silenced when it hits 0).
;
; Voice 2 ($FF0E/$FF12): Continuous engine noise. Frequency directly tracks
; bike_throttle (faster throttle = higher pitch).
;
; Control register ($FF11) is assembled from:
; - snd_volume (or $10 when silent)
; - Bit 4: engine channel enable (cleared when throttle=0 or mode=8)
; - Bits 2-3: alternate each frame (vibrato/distortion effect)
; - Bit 3: set when throttle=0 (idle noise mode)
;
; Special: entire routine is skipped during game_mode=0 (silent init phase).
;
; Inputs: game_mode, snd_duration, snd_pitch, snd_pitch_delta, snd_volume,
; bike_throttle, frame_counter
; Outputs: TED $FF0E-$FF12, snd_duration (dec), snd_pitch (slid),
; ted_ctrl_staging, pitch_hi
; Side Effects: Directly writes TED hardware sound registers
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2AECa5 02sound_engine_tickldazp_game_mode; x-ref: $20EC
$2AEEc9 00cmp#GameMode.NORMAL_RIDE; mode 0 = silent (skip all sound)
$2AF0d0 02bneb_2AF4
$2AF260rts
$2AF3eanop
$2AF4a5 54b_2AF4ldazp_snd_duration; duration > 0? (active note playing) ; x-ref: $2AF0
$2AF6f0 27beqb_2B1F; expired -> silence Voice 1
$2AF8c6 54deczp_snd_duration
$2AFAa5 50ldazp_snd_pitch
$2AFC18clc
$2AFD65 51adczp_snd_pitch_delta; pitch slide: pitch += delta each frame
$2AFF85 50stazp_snd_pitch
$2B01a5 50ldazp_snd_pitch
$2B0385 f8stazp_ted_ctrl_staging
$2B05a9 00lda#$00
$2B0785 f9stazp_pitch_hi
$2B0906 f8aslzp_ted_ctrl_staging; shift pitch left 2x -> 10-bit TED frequency
$2B0B26 f9rolzp_pitch_hi
$2B0D06 f8aslzp_ted_ctrl_staging
$2B0F26 f9rolzp_pitch_hi
$2B11a5 f8ldazp_ted_ctrl_staging
$2B138d 0f ffsta$ff0f; TED Voice 1 frequency lo
$2B16a5 f9ldazp_pitch_hi
$2B188d 10 ffsta$ff10; TED Voice 1 frequency hi (2 bits)
$2B1Ba5 55ldazp_snd_volume
$2B1Dd0 02bneb_2B21
$2B1Fa9 10b_2B1Flda#$10; $10 = base control (sound off, bit 4 set as default) ; x-ref: $2AF6
$2B2185 f8b_2B21stazp_ted_ctrl_staging; x-ref: $2B1D
$2B23a5 2aldazp_bike_throttle
$2B25f0 06beqb_2B2D; throttle=0? -> mute engine channel
$2B27a5 02ldazp_game_mode
$2B29c9 08cmp#GameMode.NEXT_LAP_INIT; mode 8 (next lap init)? -> also mute engine
$2B2Bd0 06bneb_2B33
$2B2Da5 f8b_2B2Dldazp_ted_ctrl_staging; x-ref: $2B25
$2B2F29 efand#$ef; clear bit 4: disable engine channel
$2B3185 f8stazp_ted_ctrl_staging
$2B33a5 3fb_2B33ldazp_frame_counter; frame alternation: bits 2-3 toggle each frame (vibrato) ; x-ref: $2B2B
$2B3529 01and#$01
$2B370aasla
$2B380aasla
$2B3918clc
$2B3A69 04adc#$04
$2B3C05 f8orazp_ted_ctrl_staging
$2B3E85 f8stazp_ted_ctrl_staging
$2B40a9 00lda#$00
$2B428d 12 ffsta$ff12; TED Voice 2 frequency hi = 0
$2B45a5 2aldazp_bike_throttle
$2B478d 0e ffsta$ff0e; Voice 2 freq lo = throttle (engine pitch tracks speed)
$2B4Ad0 06bneb_2B52
$2B4Ca5 f8ldazp_ted_ctrl_staging
$2B4E09 08ora#$08; throttle=0: set bit 3 (noise/distortion for idle)
$2B5085 f8stazp_ted_ctrl_staging
$2B52a5 f8b_2B52ldazp_ted_ctrl_staging; x-ref: $2B4A
$2B548d 11 ffsta$ff11; write assembled control byte to TED sound control
$2B5760rts
$2B58.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Idle landing sound + velocity correction trigger.
; Called when the bike lands without the fire button held (no jump).
; Sets bike_vel to A (caller passes $CC), enters game_mode=7 (velocity
; correction), and arms a high-pitched descending sound effect:
; pitch=$E0, delta=$02 (slow slide), duration=15 frames, volume=$30.
;
; Inputs: A = landing velocity ($CC from caller)
; Outputs: bike_vel=A, game_mode=7, snd_pitch=$E0, snd_pitch_delta=$02,
; snd_duration=$0F, snd_volume=$30
; Side Effects: None (sound applied by sound_engine_tick on next frames)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2B5C20 f0 26sfx_idle_landingjsrenter_mode_vel_correct; set vel=A, game_mode=7; arm idle landing sound ; x-ref: $27D7
$2B5Fa9 0flda#$0f; dur=15, pitch=$E0, delta=$02 (slow descending), vol=$30
$2B6185 54stazp_snd_duration
$2B63a9 e0lda#$e0
$2B6585 50stazp_snd_pitch
$2B67a9 02lda#$02
$2B6985 51stazp_snd_pitch_delta
$2B6Ba9 30lda#$30
$2B6D85 55stazp_snd_volume
$2B6F60rts
$2B70.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Surface impact sound + velocity correction trigger.
; Called when the bike hits a special surface (ramp/mud, track_flags bit 6).
; Sets bike_vel to A (caller passes $A0), enters game_mode=7 (velocity
; correction), and arms a lower-pitched fast-descending sound effect:
; pitch=$A0, delta=$06 (fast slide), duration=15 frames, volume=$30.
;
; Inputs: A = surface impact velocity ($A0 from caller)
; Outputs: bike_vel=A, game_mode=7, snd_pitch=$A0, snd_pitch_delta=$06,
; snd_duration=$0F, snd_volume=$30
; Side Effects: None (sound applied by sound_engine_tick on next frames)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2B7420 f0 26sfx_surface_impactjsrenter_mode_vel_correct; set vel=A, game_mode=7; arm surface impact sound ; x-ref: $27CB
$2B77a9 0flda#$0f; dur=15, pitch=$A0, delta=$06 (fast descending), vol=$30
$2B7985 54stazp_snd_duration
$2B7Ba9 a0lda#$a0
$2B7D85 50stazp_snd_pitch
$2B7Fa9 06lda#$06
$2B8185 51stazp_snd_pitch_delta
$2B83a9 30lda#$30
$2B8585 55stazp_snd_volume
$2B8760rts
$2B88.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Arms the crash explosion sound effect.
; Sets up the loudest, longest, most dramatic sound in the game:
; max pitch ($FF) sweeping down rapidly (delta=$FC) at high volume ($50)
; for 48 frames ($30). Called from trigger_crash when the bike crashes.
;
; Inputs: None
; Outputs: snd_pitch=$FF, snd_pitch_delta=$FC, snd_duration=$30, snd_volume=$50
; Side Effects: None (sound played by sound_engine_tick on subsequent frames)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2B8Ca9 ffsfx_crash_explosionlda#$ff; max pitch, fast sweep-down, long duration, loud - crash boom! ; x-ref: $26C7
$2B8E85 50stazp_snd_pitch
$2B90a9 fclda#$fc
$2B9285 51stazp_snd_pitch_delta
$2B94a9 30lda#$30
$2B9685 54stazp_snd_duration
$2B98a9 50lda#$50
$2B9A85 55stazp_snd_volume
$2B9C60rts
$2B9D.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Finish-line sound effect ticker (non-preemptive).
; Only fires when snd_duration==0 (no sound already playing). Arms a short
; 4-frame blip whose pitch is derived from anim_frame_count + $C0,
; creating a dynamic tone that varies each lap. Called every frame from
; check_finish_line at the end of the bike movement path.
;
; Inputs: snd_duration (guard), anim_frame_count (pitch source)
; Outputs: snd_pitch = anim_frame_count + $C0, snd_pitch_delta = $08,
; snd_duration = $04, snd_volume = $30 (when triggered)
; Side Effects: None (sound rendered by sound_engine_tick)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2BA0a5 54sfx_finish_lineldazp_snd_duration; guard: skip if a sound is already playing ; x-ref: $2E90
$2BA2d0 14bner_2BB8
$2BA4a9 04lda#$04; 4-frame blip; pitch = anim_frame_count + $C0 (dynamic)
$2BA685 54stazp_snd_duration
$2BA8a5 3cldazp_anim_frame_count
$2BAAeanop
$2BAB18clc
$2BAC69 c0adc#$c0
$2BAE85 50stazp_snd_pitch
$2BB0a9 08lda#$08; delta=$08 (moderate slide), vol=$30
$2BB285 51stazp_snd_pitch_delta
$2BB4a9 30lda#$30
$2BB685 55stazp_snd_volume
$2BB860r_2BB8rts; x-ref: $2BA2
$2BB9.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Scroll rumble sound effect ticker (non-preemptive).
; Only fires when snd_duration==0. Arms a 3-frame pulse whose pitch
; cycles through 8 tones: (frame_counter & $07) * 4 + $A0, ranging
; $A0-$BC. The cycling creates a pulsating warble - the signature
; track scroll / engine rumble sound. Fast descending slide ($10).
; Called every frame from update_scroll_tiles.
;
; Inputs: snd_duration (guard), frame_counter (pitch cycling)
; Outputs: snd_pitch = (frame&7)*4 + $A0, snd_pitch_delta = $10,
; snd_duration = $03, snd_volume = $30 (when triggered)
; Side Effects: None (sound rendered by sound_engine_tick)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2BBCa5 54sfx_scroll_rumbleldazp_snd_duration; guard: skip if a sound is already playing ; x-ref: $29C0
$2BBEd0 17bner_2BD7
$2BC0a9 03lda#$03; 3-frame pulse; pitch = (frame&7)*4 + $A0 (8-tone warble cycle)
$2BC285 54stazp_snd_duration
$2BC4a5 3fldazp_frame_counter
$2BC629 07and#$07
$2BC80aasla
$2BC90aasla
$2BCA18clc
$2BCB69 a0adc#$a0
$2BCD85 50stazp_snd_pitch
$2BCFa9 10lda#$10; delta=$10 (fast slide), vol=$30
$2BD185 51stazp_snd_pitch_delta
$2BD3a9 30lda#$30
$2BD585 55stazp_snd_volume
$2BD760r_2BD7rts; x-ref: $2BBE
$2BD8.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Lap-start sound effect + display counter reset.
; Arms a bright ascending tone at loud volume - the lap-start fanfare.
; Then calls reset_display_counters to prepare the display for the new
; lap animation. Called from init_lap_start_anim_no_kill.
;
; Inputs: None
; Outputs: snd_pitch=$C0, snd_pitch_delta=$04, snd_duration=$0F,
; snd_volume=$50; display counters reset
; Side Effects: Calls reset_display_counters
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2BDCa9 c0sfx_lap_startlda#$c0; pitch=$C0 (mid-high), delta=$04 (slow ascending), loud ; x-ref: $266D
$2BDE85 50stazp_snd_pitch
$2BE0a9 04lda#$04
$2BE285 51stazp_snd_pitch_delta
$2BE4a9 0flda#$0f
$2BE685 54stazp_snd_duration
$2BE8a9 50lda#$50
$2BEA85 55stazp_snd_volume
$2BEC20 0c 2ejsrreset_display_counters; prepare display for new lap animation
$2BEF60rts
$2BF0.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances track_progress by one tick and dynamically updates the engine
; pitch sound, creating an accelerating tone as the track scrolls.
;
; Three phases based on track_progress:
; == 0 (start): compute initial pitch from progress, arm sound
; $01-$8F (scrolling): re-arm sound only if snd_pitch < $60;
; otherwise let existing pitch slide naturally
; >= $90 (near end): silent - skip all sound setup, only inc progress
;
; Pitch computation:
; delta = (progress >> 3) | $F0 -> range $F0-$FF (increasingly fast)
; pitch = $F0 - delta -> range $00-$0F (rises as progress increases)
; 1-frame duration = continuous retrigger for smooth gliding tone.
;
; Returns new track_progress in A for caller to check end-of-course.
; Called from handle_mode_track_scroll.
;
; Inputs: track_progress, snd_pitch
; Outputs: track_progress (incremented), A = new progress,
; snd_pitch/delta/duration/volume updated (when applicable)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
advance_progress_and_engine_sfx
$2BF4a5 36ldazp_track_progress; progress==0? -> compute initial pitch; >=90? -> silent (near end) ; x-ref: $283A
$2BF6f0 0abeqb_2C02
$2BF8c9 90cmp#$90
$2BFAb0 1ebcsb_2C1A
$2BFCa5 50ldazp_snd_pitch; pitch already >= $60? -> skip recalc, let it slide naturally
$2BFEc9 60cmp#$60
$2C00b0 10bcsb_2C12
$2C02a5 36b_2C02ldazp_track_progress; delta = (progress>>3)|$F0; pitch = $F0 - delta (rising with progress) ; x-ref: $2BF6
$2C044alsra
$2C054alsra
$2C064alsra
$2C0709 f0ora#$f0
$2C0985 51stazp_snd_pitch_delta
$2C0Ba9 f0lda#$f0
$2C0D38sec
$2C0Ee5 51sbczp_snd_pitch_delta
$2C1085 50stazp_snd_pitch
$2C12a9 01b_2C12lda#$01; 1-frame duration = continuous retrigger for smooth tone ; x-ref: $2C00
$2C1485 54stazp_snd_duration
$2C16a9 30lda#$30
$2C1885 55stazp_snd_volume
$2C1Ae6 36b_2C1Ainczp_track_progress; always: advance progress, return new value in A ; x-ref: $2BFA
$2C1Ca5 36ldazp_track_progress
$2C1E60rts
$2C1F.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Arms the obstacle collision sound and sets the sound-triggered flag.
; Called from the high-speed obstacle physics path when (vel & $1F)==$10
; and the frame-counter gate passes. Sets bit 4 of obstacle_flags,x
; (marking the slot as sound-triggered) and arms a near-max-pitch
; rapidly-descending sound: pitch=$FC, delta=$FE (-2), dur=12, vol=$50.
;
; Inputs: A = obstacle_flags,x (current flags), X = obstacle slot
; Outputs: obstacle_flags,x with bit 4 set, snd_pitch=$FC,
; snd_pitch_delta=$FE, snd_duration=$0C, snd_volume=$50
; Side Effects: None (sound rendered by sound_engine_tick)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sfx_obstacle_collision_arm
$2C2009 10ora#$10; set bit 4 (sound-triggered flag) in obstacle_flags,x ; x-ref: $2574
$2C2295 10stazp_obstacle_flags,x
$2C24a9 fclda#$fc; pitch=$FC, delta=$FE (-2), dur=12, vol=$50 (near-max descending)
$2C2685 50stazp_snd_pitch
$2C28a9 felda#$fe
$2C2A85 51stazp_snd_pitch_delta
$2C2Ca9 0clda#$0c
$2C2E85 54stazp_snd_duration
$2C30a9 50lda#$50
$2C3285 55stazp_snd_volume
$2C3460rts
$2C35.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Bonus collect sound effect. Arms a max-pitch fast-descending chirp when
; the bike hits a safe obstacle (type < 5). Short 8-frame burst.
; Called from the collision handler on the non-crash (bonus) path.
;
; Inputs: None
; Outputs: snd_pitch=$FF, snd_pitch_delta=$F0, snd_duration=$08, snd_volume=$30
; Side Effects: None (sound rendered by sound_engine_tick)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2C38a9 ffsfx_bonus_collectlda#$ff; pitch=$FF, delta=$F0, dur=8, vol=$30 (short max-pitch chirp) ; x-ref: $2D61
$2C3A85 50stazp_snd_pitch
$2C3Ca9 f0lda#$f0
$2C3E85 51stazp_snd_pitch_delta
$2C40a9 08lda#$08
$2C4285 54stazp_snd_duration
$2C44a9 30lda#$30
$2C4685 55stazp_snd_volume
$2C4860rts
$2C49.byte$00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Obstacle management system: spawner + collision detector.
; Called every frame from the main loop. Two phases:
;
; Phase 1 - Spawning ($2C5A-$2CDF): Only during game_mode 2 or 7.
; Uses PRNG (s27E0) to generate spawn_type (lower nibble).
; Looks up course-specific spawn tables (f1DA0, f1DB0, f1DF0, f1E20)
; to decide if a spawn is allowed. Finds an empty slot (obstacle_flags=$FF)
; and checks spawn_cooldown. If all checks pass, spawns an obstacle with:
; - Types 0-7 (normal): random position and velocity
; - Types 8+ (wide/special): table-driven position, random velocity
; - All spawns get obstacle_life=$28 (40-tick lifetime).
;
; Phase 2 - Collision ($2CE0-$2D43): Checks all 4 obstacle slots.
; Computes vertical screen distance from raster position.
; - Normal obstacles: vertical [$39,$49), horizontal |bike-obs| < 8
; - Wide obstacles: vertical [$3D,$49), flag bit 4 set, horizontal range check
; On collision: type >= 5 -> crash (s26AC); type < 5 -> bonus (scroll advance).
;
; Inputs: game_mode, course_index, spawn_cooldown, obstacle_flags/life/pos/vel[],
; bike_pos_hi, raster_split_a, rng_state_*, spawn tables f1DA0-f1E20
; Outputs: obstacle_flags/life/pos_hi/pos_lo/vel[], spawn_cooldown, spawn_type
; Side Effects: May crash (s26AC), add score (s2C38), advance scroll (b29CA)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
obstacle_spawn_and_collide
$2C4Ca5 02ldazp_game_mode; x-ref: $20EF
$2C4Ec9 02cmp#GameMode.ACTIVE_RIDE; only active during active riding (mode 2)
$2C50f0 08beqb_2C5A
$2C52c9 07cmp#GameMode.VELOCITY_INTEGRATE; or velocity correction (mode 7)
$2C54f0 04beqb_2C5A
$2C5660rts
$2C57eanop
$2C58eanop
$2C59eanop
$2C5A20 e0 27b_2C5Ajsrprng_next; PRNG -> lower nibble = spawn_type ; x-ref: $2C50, $2C54
$2C5D29 0fand#$0f
$2C5F85 52stazp_spawn_type
$2C61aatax
$2C62a5 2fldazp_course_index
$2C6429 0fand#$0f
$2C6618clc
$2C677d a0 1dadcspawn_type_offsets,x; Y = course_index + f1DA0[spawn_type] (spawn table offset)
$2C6Aa8tay
$2C6Bb9 20 1eldaobstacle_spawn_params,y
$2C6E3d b0 1dandspawn_allowed_masks,x; spawn allowed? (table AND mask must be nonzero)
$2C71f0 18beqb_2C8B; zero = spawn blocked for this course/type
$2C73a5 2fldazp_course_index
$2C7529 0fand#$0f
$2C77aatax
$2C7820 e0 27jsrprng_next; 2nd PRNG roll vs difficulty threshold
$2C7Bdd f0 1dcmpspawn_prob_thresholds,x; roll >= threshold? -> spawn fails (probability gate)
$2C7Eb0 0bbcsb_2C8B
$2C80a2 03ldx#$03
$2C82b5 10b_2C82ldazp_obstacle_flags,x; find empty obstacle slot ($FF = dead) ; x-ref: $2C89
$2C84c9 ffcmp#$ff
$2C86f0 06beqb_2C8E; found empty slot X -> spawn here
$2C88cadex
$2C8910 f7bplb_2C82
$2C8B18b_2C8Bclc; x-ref: $2C71, $2C7E, $2C98
$2C8C90 52bccb_2CE0
$2C8Eb9 20 1eb_2C8Eldaobstacle_spawn_params,y; x-ref: $2C86
$2C9129 0fand#$0f
$2C9318clc
$2C9469 06adc#$06
$2C96c5 2bcmpzp_spawn_cooldown; cooldown check: too soon since last spawn?
$2C98b0 f1bcsb_2C8B
$2C9Aa5 52ldazp_spawn_type
$2C9C95 10stazp_obstacle_flags,x; write spawn_type into slot flags
$2C9Ea9 28lda#$28; obstacle_life = $28 (40 ticks)
$2CA095 14stazp_obstacle_life,x
$2CA2a9 00lda#$00
$2CA485 2bstazp_spawn_cooldown
$2CA6a5 52ldazp_spawn_type
$2CA8c9 08cmp#$08; type >= 8? -> wide/special obstacle spawn path
$2CAAb0 1dbcsb_2CC9
$2CAC20 e0 27jsrprng_next; normal: random horizontal pos = (PRNG & $1C) + $0C
$2CAF29 1cand#$1c
$2CB118clc
$2CB269 0cadc#$0c
$2CB495 18stazp_obstacle_pos_hi,x
$2CB620 e0 27jsrprng_next; normal: random velocity = (PRNG & $BF) + $20
$2CB929 bfand#$bf
$2CBB18clc
$2CBC69 20adc#$20
$2CBEeanop
$2CBFeanop
$2CC0eanop
$2CC195 20stazp_obstacle_vel,x
$2CC3a9 00lda#$00
$2CC595 1cstazp_obstacle_pos_lo,x
$2CC7f0 17beqb_2CE0
$2CC9a9 00b_2CC9lda#$00; x-ref: $2CAA
$2CCB95 1cstazp_obstacle_pos_lo,x
$2CCD20 e0 27jsrprng_next; wide: random velocity from PRNG
$2CD095 20stazp_obstacle_vel,x
$2CD2b9 20 1eldaobstacle_spawn_params,y
$2CD529 e0and#$e0; wide: pos = (f1E20[Y] & $E0) >> 1 + $10 (table-driven)
$2CD74alsra
$2CD8eanop
$2CD9eanop
$2CDAeanop
$2CDB18clc
$2CDC69 10adc#$10
$2CDE95 18stazp_obstacle_pos_hi,x
$2CE0a2 03b_2CE0ldx#$03; --- Phase 2: Collision detection (4 slots) --- ; x-ref: $2C8C, $2CC7
$2CE2b5 10b_2CE2ldazp_obstacle_flags,x; x-ref: $2D41
$2CE4c9 ffcmp#$ff; slot dead ($FF)? -> skip
$2CE6f0 58beqb_2D40
$2CE8b5 14ldazp_obstacle_life,x
$2CEA0aasla; vert screen dist = life*8 - raster_split / 2
$2CEB0aasla
$2CEC0aasla
$2CED38sec
$2CEEe5 04sbczp_raster_split_a
$2CF04alsra
$2CF185 f7stazp_blit_scratch
$2CF3b5 10ldazp_obstacle_flags,x
$2CF5c9 08cmp#$08; type >= 8? -> wide obstacle collision path
$2CF7b0 1fbcsb_2D18
$2CF9a5 f7ldazp_blit_scratch
$2CFBc9 39cmp#$39; normal: vertical hit zone lower bound ($39)
$2CFD90 41bccb_2D40
$2CFFc9 49cmp#$49; normal: vertical hit zone upper bound ($49)
$2D01b0 3dbcsb_2D40
$2D03a5 27ldazp_bike_pos_x_hi
$2D0538sec
$2D06f5 18sbczp_obstacle_pos_hi,x; horizontal distance = |bike_pos_hi - obstacle_pos_hi|
$2D0810 05bplb_2D0F
$2D0A49 ffeor#$ff
$2D0C18clc
$2D0D69 01adc#$01
$2D0Fc9 08b_2D0Fcmp#$08; within 8 pixels horizontally? -> HIT! ; x-ref: $2D08
$2D11b0 2dbcsb_2D40
$2D1320 48 2djsrhandle_obstacle_collision; COLLISION: handle hit on normal obstacle
$2D1660rts
$2D17eanop
$2D18a5 f7b_2D18ldazp_blit_scratch; x-ref: $2CF7
$2D1Ac9 3dcmp#$3d; wide: vertical hit zone lower bound ($3D)
$2D1C90 22bccb_2D40
$2D1Ec9 49cmp#$49
$2D20b0 1ebcsb_2D40
$2D22b5 10ldazp_obstacle_flags,x
$2D2429 10and#$10; bit 4 = active/visible flag (must be set)
$2D26f0 18beqb_2D40
$2D28b5 10ldazp_obstacle_flags,x
$2D2A29 03and#$03; wide: horizontal range from (flags & $03)*4 + $0C
$2D2C0aasla
$2D2D0aasla
$2D2E18clc
$2D2F69 0cadc#$0c
$2D31c5 27cmpzp_bike_pos_x_hi
$2D33b0 0bbcsb_2D40
$2D35a9 3flda#$3f
$2D37c5 27cmpzp_bike_pos_x_hi; wide: horizontal range upper limit ($3F)
$2D3990 05bccb_2D40
$2D3B20 48 2djsrhandle_obstacle_collision; COLLISION: handle hit on wide obstacle
$2D3E60rts
$2D3Feanop
$2D40cab_2D40dex; x-ref: $2CE6, $2CFD, $2D01, $2D11, $2D1C, $2D20, $2D26, $2D33, ...
$2D4110 9fbplb_2CE2
$2D4360rts
$2D44.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Collision resolution handler. Called when an obstacle hits the bike.
; - Wide obstacles (type >= 8): stop obstacle (vel=0), keep in slot
; - Normal obstacles (type < 8): mark slot dead ($FF)
; - Type >= 5: full crash (s26AC - debris explosion)
; - Type < 5, nonzero: score bonus + scroll advance (s2C38 + b29CA)
; - Type == 0: set anim_frame_count = 5 (visual flash)
;
; Inputs: X = obstacle slot index, obstacle_flags[X]
; Outputs: obstacle_flags[X] (cleared or kept), obstacle_vel[X] (zeroed)
; Side Effects: Crash (s26AC) or bonus scoring (s2C38) + scroll advance
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_obstacle_collision
$2D48b4 10ldyzp_obstacle_flags,x; x-ref: $2D13, $2D3B
$2D4Ac0 08cpy#$08; type >= 8 (wide)? -> stop obstacle, keep in slot
$2D4C90 06bccb_2D54
$2D4Ea9 00lda#$00; wide: zero velocity (obstacle stops)
$2D5095 20stazp_obstacle_vel,x
$2D52f0 04beqb_2D58
$2D54a9 ffb_2D54lda#$ff; normal: mark slot dead ($FF) ; x-ref: $2D4C
$2D5695 10stazp_obstacle_flags,x
$2D5898b_2D58tya; x-ref: $2D52
$2D59c9 05cmp#$05; type >= 5? -> crash!
$2D5B90 04bccb_2D61
$2D5D20 ac 26jsrtrigger_crash; dangerous obstacle: full crash + debris
$2D6060rts
$2D6120 38 2cb_2D61jsrsfx_bonus_collect; safe obstacle: score bonus + scroll advance ; x-ref: $2D5B
$2D64c0 00cpy#$00; type == 0? -> visual flash only
$2D66f0 21beqb_2D89
$2D68a5 2fldazp_course_index
$2D6A29 0fand#$0f
$2D6Caatax
$2D6Dbd 10 1eldacourse_scroll_steps,x
$2D7085 f7stazp_blit_scratch
$2D72a9 00lda#$00
$2D7418b_2D74clc; x-ref: $2D78
$2D7565 f7adczp_blit_scratch
$2D7788dey
$2D7810 fabplb_2D74
$2D7Aeanop
$2D7B85 f7stazp_blit_scratch
$2D7Da0 01b_2D7Dldy#$01; x-ref: $2D86
$2D7Fa2 05ldx#$05
$2D8120 ca 29jsrb_29CA
$2D84c6 f7deczp_blit_scratch
$2D86d0 f5bneb_2D7D
$2D8860rts
$2D89a9 05b_2D89lda#$05; type 0 hit: set anim flash = 5 frames ; x-ref: $2D66
$2D8B85 3cstazp_anim_frame_count
$2D8D60rts
$2D8E.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Initializes the two parallel track tile base tables.
; Copies 32 bytes from ROM f1C28 into $0C00-$0C1F (raw base tile indices),
; then stores each value + $14 (20) into $0C28-$0C47 (second-half tile
; mapping for alternating playfield rows). Clears sentinel byte $0C31.
;
; Inputs: None
; Outputs: $0C00-$0C1F = f1C28 base tiles, $0C28-$0C47 = base + $14,
; $0C31 = 0
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
init_tile_base_tables
$2D90a2 1fldx#$1f; copy 32 base tiles from f1C28 -> $0C00; offset +$14 -> $0C28 ; x-ref: $2E14
$2D92bd 28 1cb_2D92ldatrack_tile_indices,x; x-ref: $2D9F
$2D959d 00 0cstaSCREEN_RAM_R0C0,x
$2D9818clc
$2D9969 14adc#$14
$2D9B9d 28 0cstaSCREEN_RAM_R1C0,x
$2D9Ecadex
$2D9F10 f1bplb_2D92
$2DA1a9 00lda#$00; clear sentinel byte $0C31
$2DA38d 31 0cstaSCREEN_RAM_R1C9
$2DA660rts
$2DA7.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Stores per-course animation and scroll parameters.
; Saves A into anim_frame_count, loads scroll_speed from course_scroll_speeds[X].
; Called from the course-advance routine (advance_course_layout) after incrementing
; course_index and looking up the new frame count from course_anim_frames.
;
; Inputs: A = anim_frame_count value, X = course_index
; Outputs: anim_frame_count = A, scroll_speed = course_scroll_speeds[X]
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2DA885 3cstore_course_paramsstazp_anim_frame_count; save anim_frame_count and load scroll_speed for this course ; x-ref: $2A24
$2DAAbd e0 1dldacourse_scroll_speeds,x
$2DAD85 53stazp_scroll_speed
$2DAF60rts
$2DB0.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Jump movement handler + level select UI.
; Called from handle_mode_normal_ride when fire button is held (mode 0).
;
; 1. Ticks the music sequencer (music_sequencer_tick) each frame.
; 2. If title_row_anim < $64 (100): monitors joystick tilt bits 2+3.
; Both held simultaneously -> inc title_row_anim (charging to unlock).
; Either released -> reset title_row_anim to 0.
; 3. When title_row_anim reaches $64 (100 frames held): enters the
; level-select mode. Copies "level RR" text from f1C98 to screen
; $0DF0 with colour $71. Every 8th frame, reads joystick bits 2/3
; to dec/inc title_char_idx (course selection, wrapped to 0-15).
; Updates course display digits from f1DC0/f1DD0 lookup tables
; and syncs course_index to the selection.
;
; Inputs: joystick_state (bits 2-3, bit 7), title_row_anim, title_char_idx,
; frame_counter
; Outputs: title_row_anim, title_char_idx, course_index,
; screen $0DF0-$0DF7 (level text), $09F0-$09F7 (colour)
; Side Effects: Music sequencer tick, screen RAM writes
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_jump_and_level_select
$2DB420 a0 2ajsrmusic_sequencer_tick; tick music each frame while fire is held ; x-ref: $275E
$2DB7a5 56ldazp_title_row_anim; charging counter: needs 100 frames with both tilt bits held
$2DB9c9 64cmp#$64
$2DBBf0 11beqb_2DCE
$2DBDa5 33ldazp_joystick_state; both tilt bits (2+3) held? -> charge; else reset to 0
$2DBF29 0cand#$0c
$2DC1c9 0ccmp#$0c
$2DC3d0 04bneb_2DC9
$2DC5e6 56inczp_title_row_anim
$2DC7d0 04bner_2DCD
$2DC9a9 00b_2DC9lda#$00; x-ref: $2DC3
$2DCB85 56stazp_title_row_anim
$2DCD60r_2DCDrts; x-ref: $2DC7
$2DCEa2 07b_2DCEldx#$07; --- Level select active: copy "level RR" to screen, pick course --- ; x-ref: $2DBB
$2DD0bd 98 1cb_2DD0ldalevel_select_text,x; x-ref: $2DDC
$2DD39d f0 0dstaSCREEN_RAM_R12C16,x
$2DD6a9 71lda#$71
$2DD89d f0 09staCOLOR_RAM_R12C16,x
$2DDBcadex
$2DDC10 f2bplb_2DD0
$2DDEa5 3fldazp_frame_counter; every 8th frame: joystick left/right adjusts title_char_idx
$2DE029 07and#$07
$2DE2d0 10bneb_2DF4
$2DE4a5 33ldazp_joystick_state
$2DE629 08and#$08
$2DE8d0 02bneb_2DEC
$2DEAc6 57deczp_title_char_idx
$2DECa5 33b_2DECldazp_joystick_state; x-ref: $2DE8
$2DEE29 04and#$04
$2DF0d0 02bneb_2DF4
$2DF2e6 57inczp_title_char_idx
$2DF4a5 57b_2DF4ldazp_title_char_idx; wrap to 0-15; update display digits from f1DC0/f1DD0, sync course_index ; x-ref: $2DE2, $2DF0
$2DF629 0fand#$0f
$2DF885 57stazp_title_char_idx
$2DFAaatax
$2DFBbd c0 1dldacourse_digits_units,x
$2DFE8d f7 0dstaSCREEN_RAM_R12C23
$2E01bd d0 1dldacourse_digits_tens,x
$2E048d f6 0dstaSCREEN_RAM_R12C22
$2E0786 2fstxzp_course_index
$2E0960rts
$2E0A.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Resets the tile/character animation counters to zero.
; Despite their BASIC-flavoured names, RAM_OLDOV and RAM_TEMPF1 are both
; display-engine state variables: OLDOV is a row animation counter (0-99)
; that drives ROM tile-row selection from $1C98, and TEMPF1 is a 4-bit
; column/character index (inc/dec'd via RAM_FRFTOP bits 2-3) that indexes
; the ROM character table at $1DC0. Clearing both restarts the animation
; from frame zero. Called during level initialisation and object resets.
;
; Inputs: None
; Outputs: None
; Side Effects:($56) <- 0 (row animation counter reset)
; ($57) <- 0 (column/character index reset)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
reset_display_counters
$2E0Ca9 00lda#$00; A=0; same value stored to both counters below ; x-ref: $263B, $2BEC
$2E0E85 56stazp_title_row_anim; Row animation counter (0-99); drives ROM tile-row selection at $1C98
$2E1085 57stazp_title_char_idx; Column/character index (4-bit); indexes ROM char table at $1DC0
$2E1260rts
$2E13.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Loads the course layout pointer and initializes track tile base tables.
; First calls init_tile_base_tables to set up $0C00 (raw base tiles from
; f1C28) and $0C28 (offset +$14 for alternating playfield rows). Then
; syncs course_index to title_char_idx and loads the 16-bit course layout
; pointer from f1E50/f1E60[course] into ptr_obj_record_lo/hi.
; Called from init_course_state to prepare a new course for play.
;
; Inputs: title_char_idx (course selection 0-15)
; Outputs: course_index synced, ptr_obj_record_lo/hi = course layout ptr,
; $0C00-$0C1F base tiles, $0C28-$0C47 offset tiles, $0C31=0
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2E1420 90 2dload_course_layoutjsrinit_tile_base_tables; init tile base tables ($0C00/$0C28 from f1C28) ; x-ref: $2640
$2E17a6 57ldxzp_title_char_idx; sync course_index; load layout ptr from f1E50/f1E60[course]
$2E1986 2fstxzp_course_index
$2E1Bbd 50 1eldalevels_ptr_lo,x
$2E1E85 24stazp_ptr_obj_record_lo
$2E20bd 60 1eldalevels_ptr_hi,x
$2E2385 25stazp_ptr_obj_record_hi
$2E2560rts
$2E26.byte$00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Advances the course layout data pointer by one byte, with wrapping.
; Increments the 16-bit pointer ptr_obj_record_lo/hi. If it reaches
; the end-of-data sentinel $3FD8, wraps back to $2F00 (start of course
; data area). Implements a circular course data buffer so the track
; loops endlessly. Called from the per-frame track rendering loop.
;
; Inputs: ptr_obj_record_lo/hi (current position in course data)
; Outputs: ptr_obj_record_lo/hi incremented; wrapped to $2F00 at $3FD8
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
advance_course_data_ptr
$2E28e6 24inczp_ptr_obj_record_lo; inc 16-bit pointer; wrap $3FD8 -> $2F00 (circular course buffer) ; x-ref: $24F1
$2E2Ad0 02bneb_2E2E
$2E2Ce6 25inczp_ptr_obj_record_hi
$2E2Ea5 24b_2E2Eldazp_ptr_obj_record_lo; reached end sentinel $3FD8? ; x-ref: $2E2A
$2E30c9 d8cmp#$d8
$2E32d0 0ebner_2E42
$2E34a5 25ldazp_ptr_obj_record_hi
$2E36c9 3fcmp#$3f
$2E38d0 08bner_2E42
$2E3Aa9 00lda#<course_data_base; yes: wrap pointer back to $2F00 (course data start)
$2E3C85 24stazp_ptr_obj_record_lo
$2E3Ea9 2flda#>course_data_base
$2E4085 25stazp_ptr_obj_record_hi
$2E4260r_2E42rts; x-ref: $2E32, $2E38
$2E43.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Joystick throttle input handler.
; Reads joystick tilt bits and adjusts bike_throttle:
; Bit 3 (right): accelerate -> throttle += 2, capped at $FA
; Bit 2 (left): decelerate -> throttle -= 2, floored at $62
; Step size is $02 per frame. Throttle range: $62 (min) to $FA (max).
;
; Inputs: joystick_state (bits 2 and 3), bike_throttle
; Outputs: bike_throttle adjusted within [$62, $FA]
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2E44a5 33update_throttleldazp_joystick_state; bit 3 (right): accelerate -> throttle += 2, cap at $FA ; x-ref: $2E6C
$2E4629 08and#$08
$2E48f0 0dbeqb_2E57
$2E4Aa5 2aldazp_bike_throttle
$2E4Cc9 facmp#$fa
$2E4Eb0 07bcsb_2E57
$2E50a5 2aldazp_bike_throttle
$2E5218clc
$2E5369 02adc#$02
$2E5585 2astazp_bike_throttle
$2E57a5 33b_2E57ldazp_joystick_state; bit 2 (left): decelerate -> throttle -= 2, floor at $62 ; x-ref: $2E48, $2E4E
$2E5929 04and#$04
$2E5Bf0 0dbeqr_2E6A
$2E5Da5 2aldazp_bike_throttle
$2E5Fc9 62cmp#$62
$2E6190 07bccr_2E6A
$2E63a5 2aldazp_bike_throttle
$2E6538sec
$2E66e9 02sbc#$02
$2E6885 2astazp_bike_throttle
$2E6A60r_2E6Arts; x-ref: $2E5B, $2E61
$2E6B.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Convenience wrapper: updates throttle from joystick input, then loads
; bike_vel into A with carry clear — ready for the caller's ADC bias.
; Called from apply_velocity_and_integrate.
;
; Inputs: joystick_state, bike_throttle, bike_vel
; Outputs: bike_throttle adjusted, A = bike_vel, C = 0
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
update_throttle_and_load_vel
$2E6C20 44 2ejsrupdate_throttle; update throttle, then return bike_vel in A with C=0 ; x-ref: $28F8
$2E6Fa5 29ldazp_bike_vel
$2E7118clc
$2E7260rts
$2E73.byte$00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Right-boundary check with conditional velocity correction.
; Compares bike_pos_hi against track_right_edge:
; - Below right edge: bike past boundary -> vel=0, mode=7 (soft wall)
; - At/above edge + track_flags bit 7 set: prevent overshoot -> vel=0, mode=7
; - At/above edge + bit 7 clear: normal, no action
; Provides a "soft wall" that stops the bike without crashing.
; Called from the physics integration path after position update.
;
; Inputs: bike_pos_hi, track_right_edge, track_flags (bit 7)
; Outputs: Possibly bike_vel=0, game_mode=7 (via enter_mode_vel_correct)
; Side Effects: None
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2E74a5 27check_right_boundaryldazp_bike_pos_x_hi; bike past right edge? -> vel=0, mode=7 (soft wall) ; x-ref: $27BB
$2E76c5 39cmpzp_track_right_edge
$2E78b0 06bcsb_2E80
$2E7Aa9 00lda#$00
$2E7C20 f0 26jsrenter_mode_vel_correct
$2E7F60rts
$2E80a5 38b_2E80ldazp_track_flags; at boundary: track_flags bit 7 set? -> also stop (prevent overshoot) ; x-ref: $2E78
$2E8229 80and#$80
$2E84f0 05beqr_2E8B
$2E86a9 00lda#$00
$2E8820 f0 26jsrenter_mode_vel_correct
$2E8B60r_2E8Brts; x-ref: $2E84
$2E8C.byte$00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Finish-line / bike-coincidence detector.
; Called every frame at the end of the bike increment path (both bikes moving).
;
; First arms the finish-line sound effect via s2BA0 (which only triggers if
; no sound is already playing — JMPER=$00). Then checks whether both bike
; screen-position slots have simultaneously reached the left-edge wrap value
; $0B (bike 0 at f0C07, bike 1 at $0C08). If either bike has not yet
; arrived, the routine returns without further action.
;
; When both bikes coincide at $0B:
; - Both position slots are reset to the right-edge start value $14
; (character table) and $28 (secondary position table), restarting
; the next lap from the beginning of the track.
; - The finish-line sound (already armed above) continues to play.
;
; Inputs: f0C07 (bike 0 screen pos), $0C08 (bike 1 screen pos)
; Outputs: f0C07/$0C08 <- $14, f0C2F/$0C30 <- $28 (on coincidence only)
; Side Effects: Arms sound via s2BA0 (sets JMPER, DSCPNT, a51, SIZE)
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
$2E9020 a0 2bcheck_finish_linejsrsfx_finish_line; Arm finish-line sound (no-op if JMPER already non-zero) ; x-ref: $2394
$2E93ad 07 0cldaSCREEN_RAM_R0C7; Load bike 0 screen position (slot 0 of position table)
$2E96c9 0bcmp#$0b; Has bike 0 reached the left edge?
$2E98d0 17bner_2EB1; Bike 0 not at edge — no coincidence, return
$2E9Aad 08 0cldaSCREEN_RAM_R0C8; Load bike 1 screen position (slot 1 of position table)
$2E9Dc9 0bcmp#$0b; Has bike 1 also reached the left edge?
$2E9Fd0 10bner_2EB1; Bike 1 not at edge — no coincidence, return
$2EA1a9 14lda#$14; Both bikes at $0B — reset both to right-edge start ($14)
$2EA38d 07 0cstaSCREEN_RAM_R0C7
$2EA68d 08 0cstaSCREEN_RAM_R0C8
$2EA9a9 28lda#$28; Reset both secondary position slots to $28 (right-edge start)
$2EAB8d 2f 0cstaSCREEN_RAM_R1C7
$2EAE8d 30 0cstaSCREEN_RAM_R1C8
$2EB160r_2EB1rts; x-ref: $2E98, $2E9F
$2EB2.byte$00, $00, $00, $00, $00, $00
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Game mode 3: Starting gate phase - enforces the left track boundary and
; decays throttle until the bike comes to a stop.
; If the bike has gone left of the track edge, a rightward correction force
; is applied (j28F8). Obstacles are aged every tick. When throttle reaches
; zero, transitions to game_mode = 4 (course complete / result screen).
;
; Inputs: bike_pos_hi, track_left_edge, bike_throttle,
; obstacle_life[], obstacle_flags[]
; Outputs: bike_pos_hi/lo, bike_vel, bike_throttle, game_mode,
; obstacle_life[], obstacle_flags[], spawn_cooldown
; Side Effects: May trigger crash (s26AC); advances to game_mode 4 via s269C
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
handle_mode_starting_gate
$2EB8a5 27ldazp_bike_pos_x_hi; x-ref: $2600
$2EBAc5 37cmpzp_track_left_edge; has bike gone left of the track edge?
$2EBCb0 03bcsb_2EC1; bike >= left edge -> no correction needed
$2EBE20 f8 28jsrapply_velocity_and_integrate; bike past left edge: apply rightward correction + bounds check
$2EC120 18 28b_2EC1jsrstarting_gate_throttle_decay; age obstacles + decay throttle (-> game_mode 4 when throttle hits 0) ; x-ref: $2EBC
$2EC460rts
$2EC5.fill59, $00
; Start of course layout data area. The 16-bit layout pointer wraps back here when it reaches the $3FD8 end sentinel, making the track circular.
$2F00course_data_base.byte$00, $00; x-ref: $2E3A, $2E3E
$2F02level_0.fill14, $00; x-ref: $1E50
$2F10.byte$fd, $ff
$2F12.fill14, $00
$2F20.byte$fe, $ff
$2F22.fill14, $00
$2F30.byte$b1
$2F31.fill9, $31
$2F3A.byte$b2
$2F3B.fill9, $32
$2F44.byte$b3
$2F45.fill9, $33
$2F4E.byte$20, $21, $22, $00, $00, $01, $02, $03
$2F56.byte$01, $02, $03, $01, $02, $03, $01, $02
$2F5E.byte$03, $01, $02, $03, $80
$2F63.fill17, $00
$2F74.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$2F7C.byte$8e, $0e, $0e, $0e, $0e, $0e, $0e, $0e
$2F84.byte$96, $17, $18, $19, $16, $17, $18, $19
$2F8C.byte$8e
$2F8D.fill9, $0e
$2F96.byte$29, $2a, $2b, $8e, $0e, $0e, $0e, $0e
$2F9E.byte$0e, $0e, $29, $2a, $2b, $29, $2a, $2b
$2FA6.byte$8e, $0e, $0e, $0e, $0e, $0e
$2FAC.fill8, $0d
$2FB4.byte$26, $27, $28, $26, $27, $28, $26, $27
$2FBC.byte$28, $8d, $0d, $0d, $80, $00
$2FC2level_1.fill14, $00; x-ref: $1E51
$2FD0.byte$fd, $ff
$2FD2.fill14, $00
$2FE0.byte$fe, $ff
$2FE2.fill22, $00
$2FF8.byte$36, $39, $80
$2FFB.fill13, $00
$3008.byte$36, $39, $80
$300B.fill17, $00
$301C.byte$ad, $2d, $2d, $2d, $2d, $2d, $2d, $2d
$3024.fill8, $2f
$302C.byte$90, $10, $10, $10, $10, $10, $10, $10
$3034.byte$23, $24, $25, $90
$3038.fill10, $10
$3042.byte$23, $24, $25, $90
$3046.fill10, $10
$3050.byte$23, $24, $25, $90
$3054.fill10, $10
$305E.byte$23, $24, $25, $90
$3062.fill22, $10
$3078.byte$0a, $0b, $0c, $90
$307C.fill9, $10
$3085.byte$0a, $0b, $0c, $90
$3089.fill9, $10
$3092.byte$0a, $0b, $0c, $90
$3096.fill9, $10
$309F.byte$0a, $0b, $0c, $90
$30A3.fill13, $10
$30B0.byte$b4, $34, $33, $33, $32, $32, $b1
$30B7.fill9, $31
$30C0.byte$26, $27, $28, $b1
$30C4.fill8, $31
$30CC.byte$26, $27, $28, $26, $27, $28, $b1, $31
$30D4.byte$31, $31, $31, $31, $31, $31, $26, $27
$30DC.byte$28, $26, $27, $28, $26, $27, $28, $b1
$30E4.byte$31, $31, $31, $31, $80, $00
$30EAlevel_2.fill14, $00; x-ref: $1E52
$30F8.byte$fd, $ff
$30FA.fill14, $00
$3108.byte$fe, $ff
$310A.fill14, $00
$3118.byte$31, $32, $32, $32, $32, $32, $32, $32
$3120.byte$33, $34, $34, $34, $34, $34, $34, $34
$3128.byte$91, $11, $11, $11, $11, $11, $11, $11
$3130.byte$29, $2a, $2b, $91
$3134.fill8, $11
$313C.byte$29, $2a, $2b, $91
$3140.fill12, $11
$314C.byte$90
$314D.fill15, $10
$315C.byte$04, $05, $06, $04, $05, $06, $90, $10
$3164.byte$10, $10, $10, $10, $10, $10, $01, $02
$316C.byte$03, $01, $02, $03, $01, $02, $03, $90
$3174.byte$10, $10, $10, $10, $23, $24, $25, $00
$317C.byte$36, $37, $38, $37, $38, $37, $38, $37
$3184.byte$38, $37, $38, $37, $38, $37, $38, $37
$318C.byte$38, $37, $38, $39, $b1, $31, $31, $31
$3194.byte$31, $31, $31, $31, $1a, $1b, $1c, $b1
$319C.byte$31, $31, $31, $31, $1a, $1b, $1c, $31
$31A4.byte$31, $36, $39, $31, $1a, $1b, $1c, $b1
$31AC.byte$31, $31, $31, $31, $1a, $1b, $1c, $b1
$31B4.byte$31, $31, $31, $31, $1a, $1b, $1c, $31
$31BC.byte$31, $36, $39, $31, $1a, $1b, $1c, $b1
$31C4.byte$31, $31, $31, $31, $1a, $1b, $1c, $b1
$31CC.byte$31, $31, $31, $31, $1a, $1b, $1c, $31
$31D4.byte$31, $36, $39, $31, $1a, $1b, $1c, $b1
$31DC.byte$31, $31, $31, $31, $8d, $0d, $0d, $0d
$31E4.byte$0d, $0d, $26, $27, $28, $0d, $26, $27
$31EC.byte$28, $0d, $26, $27, $28, $8d
$31F2level_3.byte$0d, $0d, $0d, $0d; x-ref: $1E53
$31F6.fill10, $00
$3200.byte$fd, $ff
$3202.fill14, $00
$3210.byte$fe, $ff
$3212.fill8, $00
$321A.byte$ac
$321B.fill8, $2c
$3223.byte$2d, $2d, $2d, $2d, $2d, $2e, $2e, $2e
$322B.byte$2e, $2e, $2f, $2f, $2f, $2f, $2f, $b0
$3233.byte$30, $30, $30, $30, $30, $00, $07, $08
$323B.byte$09, $07, $08, $09, $00, $b0, $30, $30
$3243.byte$30, $30, $30, $30, $00, $07, $08, $09
$324B.byte$00, $07, $08, $09, $00, $b0, $30, $30
$3253.byte$30, $30, $30, $30, $30, $00, $01, $02
$325B.byte$03, $01, $02, $03, $01, $02, $03, $8d
$3263.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$326B.byte$26, $27, $28, $8e
$326F.fill8, $0e
$3277.byte$29, $2a, $2b, $8f
$327B.fill9, $0f
$3284.byte$20, $21, $22, $00, $04, $05, $06, $04
$328C.byte$05, $06, $04, $05, $06, $00, $b5, $35
$3294.byte$35, $35, $35, $35, $b4, $34, $34, $34
$329C.byte$b3, $33, $33, $33, $b2, $32, $32, $32
$32A4.byte$b1, $31, $31, $31, $80
$32A9.fill11, $00
$32B4.byte$36, $39, $80
$32B7.fill9, $00
$32C0.byte$36, $39, $80
$32C3.fill9, $00
$32CC.byte$36, $39, $80
$32CF.fill9, $00
$32D8.byte$36, $39, $80
$32DB.fill11, $00
$32E6.byte$31, $31, $1a, $1b, $1c, $00, $36, $37
$32EE.byte$38, $37, $38, $37
$32F2level_4.byte$38, $37, $38, $37, $38, $39; x-ref: $1E54
$32F8.fill8, $00
$3300.byte$fd, $ff
$3302.fill14, $00
$3310.byte$fe, $ff
$3312.fill14, $00
$3320.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$3328.byte$8e, $0e, $0e, $0e, $0e, $0e, $0e, $0e
$3330.byte$8f, $0f, $0f, $0f, $0f, $0f, $0f, $0f
$3338.byte$90, $10, $10, $10, $10, $10, $10, $10
$3340.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3348.byte$23, $24, $25, $34, $04, $05, $06, $b4
$3350.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3358.byte$34, $34, $34, $34, $23, $24, $25, $b4
$3360.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3368.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3370.byte$23, $24, $25, $34, $0a, $0b, $0c, $b4
$3378.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3380.byte$34, $34, $34, $34, $23, $24, $25, $b4
$3388.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3390.byte$34, $34, $34, $34, $34, $34, $34, $b4
$3398.byte$23, $24, $25, $34, $29, $2a, $2b, $b4
$33A0.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33A8.byte$34, $34, $34, $34, $23, $24, $25, $b4
$33B0.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33B8.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33C0.byte$23, $24, $25, $34, $0a, $0b, $0c, $b4
$33C8.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33D0.byte$34, $34, $34, $34, $23, $24, $25, $b4
$33D8.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33E0.byte$23, $24, $25, $34, $04, $05, $06, $b4
$33E8.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33F0.byte$34, $34, $34, $34, $34, $34, $34, $b4
$33F8.byte$90, $10, $10, $10, $10, $10, $10, $10
$3400.byte$8f, $0f, $0f, $0f, $0f, $0f, $0f, $0f
$3408.byte$8e, $0e, $0e, $0e, $0e, $0e, $0e, $0e
$3410.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$3418.byte$80, $00
$341Alevel_5.fill14, $00; x-ref: $1E55
$3428.byte$fd, $ff
$342A.fill14, $00
$3438.byte$fe, $ff
$343A.fill14, $00
$3448.byte$36, $39, $80
$344B.fill9, $00
$3454.byte$36, $39, $80
$3457.fill9, $00
$3460.byte$36, $39, $80, $00, $00, $00, $00, $00
$3468.byte$00, $00, $80, $00, $31, $31, $1a, $1b
$3470.byte$1c, $00, $36, $37, $38, $37, $38, $37
$3478.byte$38, $b9, $00, $31, $31, $1a, $1b, $1c
$3480.byte$00, $36, $37, $38, $37, $38, $37, $38
$3488.byte$b9, $00, $31, $31, $1a, $1b, $1c, $00
$3490.byte$36, $37, $38, $37, $38, $37, $38, $39
$3498.byte$80
$3499.fill15, $00
$34A8.byte$36, $39, $00, $31, $1a, $1b, $1c, $00
$34B0.byte$36, $37, $38, $37, $38, $37, $b8, $39
$34B8.byte$00, $32, $1d, $1e, $1f, $00, $36, $37
$34C0.byte$38, $37, $38, $37, $38, $39, $80
$34C7.fill13, $00
$34D4.byte$80, $00, $31, $31, $1a, $1b, $1c, $00
$34DC.byte$36, $37, $38, $39, $07, $08, $09, $36
$34E4.byte$37, $38, $b9, $00, $31, $31, $1a, $1b
$34EC.byte$1c, $00, $07, $08, $09, $07
$34F2level_6.byte$08, $09, $07, $08, $09, $00, $80, $00; x-ref: $1E56
$34FA.byte$00, $00, $00, $00, $00, $00, $fd, $ff
$3502.fill14, $00
$3510.byte$fe, $ff
$3512.fill14, $00
$3520.byte$0d, $0d, $0d, $0d, $0d, $36, $39, $8d
$3528.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$3530.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$3538.byte$34, $34, $23, $24, $24, $25, $34, $34
$3540.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$3548.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$3550.byte$0d, $0d, $0d, $0d, $0d, $36, $39, $8d
$3558.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$3560.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$3568.byte$34, $34, $23, $24, $24, $25, $34, $34
$3570.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$3578.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$3580.byte$0d, $0d, $0d, $0d, $0d, $36, $39, $8d
$3588.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$3590.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$3598.byte$34, $34, $23, $24, $24, $25, $34, $34
$35A0.byte$0f, $0f, $0f, $0f, $0f, $0f, $0f, $8f
$35A8.byte$32, $32, $1d, $1e, $1e, $1f, $32, $32
$35B0.byte$0d, $0d, $0d, $0d, $0d, $36, $39, $8d
$35B8.byte$00, $00
$35BAlevel_7.fill14, $00; x-ref: $1E57
$35C8.byte$fd, $ff
$35CA.fill14, $00
$35D8.byte$fe, $ff
$35DA.fill18, $00
$35EC.byte$b1, $31, $31, $31, $31, $31, $31, $31
$35F4.byte$b2, $32, $32, $32, $32, $32, $32, $32
$35FC.byte$1d, $1e, $1f, $00, $01, $02, $03, $01
$3604.byte$02, $03, $01, $02, $03, $01, $02, $83
$360C.byte$00, $20, $21, $21, $22, $00, $01, $02
$3614.byte$03, $01, $02, $03, $01, $02, $03, $01
$361C.byte$02, $03, $01, $82, $03, $00, $20, $21
$3624.byte$21, $22, $00, $07, $08, $09, $00, $00
$362C.byte$07, $08, $09, $00, $07, $08, $09, $00
$3634.byte$00, $07, $08, $09, $80
$3639.fill15, $00
$3648.byte$36, $39, $80
$364B.fill8, $00
$3653.byte$80, $36, $39, $00, $1a, $1b, $1b, $1c
$365B.byte$00, $36, $39, $36, $37, $38, $37, $38
$3663.byte$37, $38, $39, $80
$3667.fill13, $00
$3674.byte$1a, $1b, $1b, $1c, $00, $00, $36, $39
$367C.byte$36, $39, $b6, $39, $00, $00, $1d, $1e
$3684.byte$1e, $1f, $00, $00, $36, $39, $36, $39
$368C.byte$36, $b9, $00, $00, $20, $21, $21, $22
$3694.byte$00, $00, $01, $02, $03, $01, $02, $03
$369C.byte$00, $00, $af
$369F.fill9, $2f
$36A8.byte$31, $0a, $0b, $0c, $31, $00, $07, $08
$36B0.byte$09, $00
$36B2level_8.fill14, $00; x-ref: $1E58
$36C0.byte$fd, $ff
$36C2.fill14, $00
$36D0.byte$fe, $ff
$36D2.fill14, $00
$36E0.byte$b1, $31, $31, $31, $31, $31, $31, $31
$36E8.byte$b2, $32, $32, $32, $32, $32, $32, $32
$36F0.byte$b3, $33, $33, $33, $33, $33, $33, $33
$36F8.byte$b4, $34, $34, $34, $34, $34, $34, $34
$3700.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$3708.byte$34, $23, $24, $24, $24, $24, $25, $34
$3710.byte$af, $2f, $2f, $2f, $2f, $2f, $29, $2a
$3718.byte$2a, $2b, $2f, $2f, $2f, $2f, $33, $33
$3720.byte$20, $21, $22, $36, $39, $20, $21, $22
$3728.byte$33, $33, $2f, $2f, $2f, $2f, $2f, $2f
$3730.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$3738.byte$31, $0a, $0b, $0c, $0a, $0b, $0c, $31
$3740.byte$af, $2f, $2f, $2f, $2f, $2f, $29, $2a
$3748.byte$2a, $2b, $2f, $2f, $2f, $2f, $33, $33
$3750.byte$33, $20, $21, $21, $21, $21, $22, $33
$3758.byte$33, $33, $2f, $2f, $2f, $2f, $2f, $2f
$3760.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$3768.byte$34, $23, $24, $24, $24, $24, $25, $34
$3770.byte$af, $2f, $2f, $2f, $2f, $2f, $29, $2a
$3778.byte$2a, $2b, $2f, $2f, $2f, $2f, $33, $33
$3780.byte$20, $21, $22, $36, $39, $20, $21, $22
$3788.byte$33, $33, $2f, $2f, $2f, $2f, $2f, $2f
$3790.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$3798.byte$34, $23, $24, $24, $24, $24, $25, $34
$37A0.byte$af, $2f, $2f, $2f, $2f, $2f, $29, $2a
$37A8.byte$2a, $2b, $2f, $2f, $2f, $2f, $2f, $2f
$37B0.byte$33, $20, $21, $21, $21, $21, $22, $33
$37B8.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$37C0.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$37C8.byte$31, $0a, $0b, $0c, $0a, $0b, $0c, $31
$37D0.byte$af, $2f, $2f, $2f, $2f, $2f, $29, $2a
$37D8.byte$2a, $2b, $2f, $2f, $2f, $2f, $33, $33
$37E0.byte$20, $21, $22, $36, $39, $20, $21, $22
$37E8.byte$33, $33, $2f, $2f, $2f, $2f, $2f, $2f
$37F0.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$37F8.byte$34, $23, $24, $24, $24, $24, $25, $34
$3800.byte$af, $2f, $2f, $2f, $2f, $2f, $2f, $2f
$3808.byte$ae, $2e, $2e, $2e, $2e, $2e, $2e, $2e
$3810.byte$ad, $2d, $2d, $2d, $2d, $2d, $2d, $2d
$3818.byte$ac, $2c, $2c, $2c, $2c, $2c, $2c, $2c
$3820.byte$80, $00
$3822level_9.fill14, $00; x-ref: $1E59
$3830.byte$fd, $ff
$3832.fill14, $00
$3840.byte$fe, $ff
$3842.fill14, $00
$3850.byte$1a, $1b, $1c, $00, $01, $02, $03, $80
$3858.fill12, $00
$3864.byte$1a, $1b, $1c, $00, $07, $08, $09, $80
$386C.fill12, $00
$3878.byte$1a, $1b, $1c, $00, $01, $02, $03, $80
$3880.fill12, $00
$388C.byte$b1, $31, $31, $31, $31, $31, $31, $31
$3894.byte$1a, $1b, $1c, $00, $01, $02, $03, $01
$389C.byte$02, $03, $01, $02, $03, $01, $02, $03
$38A4.byte$00, $b2
$38A6.fill8, $32
$38AE.byte$b3
$38AF.fill9, $33
$38B8.byte$b4
$38B9.fill8, $34
$38C1.byte$23, $24, $25, $00, $04, $05, $06, $04
$38C9.byte$05, $06, $04, $05, $06, $04, $05, $06
$38D1.byte$04, $05, $06, $04, $85, $06, $00, $23
$38D9.byte$24, $24, $25, $00, $0a, $0b, $0c, $0a
$38E1.byte$0b, $0c, $0a, $0b, $0c, $0a, $0b, $0c
$38E9.byte$0a, $0b, $0c, $0a, $8b, $0c, $00, $23
$38F1.byte$24, $24, $25, $00, $04, $05, $06, $04
$38F9.byte$05, $06, $00, $04, $05, $06, $00, $04
$3901.byte$05, $06, $04, $85, $06, $00, $23, $24
$3909.byte$24, $25, $00, $36, $37, $38, $37, $38
$3911.byte$37, $38, $37, $38, $37, $38, $b7, $38
$3919.byte$39, $00, $1a, $1b, $1b, $1c, $00, $36
$3921.byte$37, $38, $37, $38, $37, $38, $37, $38
$3929.byte$37, $38, $b7, $38, $39, $00, $1a, $1b
$3931.byte$1b, $1c, $00, $36, $37, $38, $37, $38
$3939.byte$37, $38, $37, $38, $37, $38, $37, $38
$3941.byte$39
$3942level_10.byte$80; x-ref: $1E5A
$3943.fill13, $00
$3950.byte$fd, $ff
$3952.fill14, $00
$3960.byte$fe, $ff
$3962.fill14, $00
$3970.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$3978.byte$8e, $0e, $0e, $0e, $0e, $0e, $0e, $0e
$3980.byte$8f, $0f, $0f, $0f, $0f, $0f, $0f, $0f
$3988.byte$90, $10, $10, $10, $10, $10, $10, $10
$3990.byte$23, $24, $25, $07, $08, $09, $07, $08
$3998.byte$09, $07, $08, $09, $07, $08, $09, $07
$39A0.byte$08, $09, $07, $08, $09, $80, $00, $00
$39A8.byte$00, $00, $00, $00, $80, $00, $00, $00
$39B0.byte$1a, $1b, $1c, $00, $00, $01, $02, $03
$39B8.byte$00, $00, $00, $00, $80, $00, $00, $00
$39C0.byte$1a, $1b, $1b, $1c, $00, $07, $08, $09
$39C8.byte$00, $00, $00, $00, $80, $00, $00, $00
$39D0.byte$1a, $1b, $1b, $1c, $00, $01, $02, $03
$39D8.byte$80
$39D9.fill14, $00
$39E7.byte$1a, $1b, $1b, $1c, $00, $36, $37, $38
$39EF.byte$37, $38, $37, $38, $37, $38, $b7, $38
$39F7.byte$39, $00, $1a, $1b, $1c, $00, $36, $37
$39FF.byte$38, $37, $38, $37, $38, $37, $38, $37
$3A07.byte$38, $39, $80
$3A0A.fill16, $00
$3A1A.byte$1a, $1b, $1b, $1c, $00, $36, $39, $36
$3A22.byte$b7, $38, $39, $00, $1a, $1b, $1c, $00
$3A2A.byte$36, $39, $36, $37, $38, $37, $38, $39
$3A32level_11.byte$80; x-ref: $1E5B
$3A33.fill13, $00
$3A40.byte$fd, $ff
$3A42.fill14, $00
$3A50.byte$fe, $ff
$3A52.fill14, $00
$3A60.fill8, $0e
$3A68.fill8, $10
$3A70.byte$0d, $0a, $0b, $0c, $0d, $0f, $0f, $0f
$3A78.byte$23, $24, $24, $24, $25, $81, $02, $03
$3A80.byte$23, $24, $24, $25, $36, $b7, $38, $39
$3A88.byte$23, $24, $25, $87, $08, $09, $23, $24
$3A90.byte$24, $24, $24, $24, $25, $a9, $2a, $2b
$3A98.byte$23, $24, $24, $25, $36, $b7, $38, $39
$3AA0.byte$23, $24, $24, $25, $8f, $0f, $0f, $23
$3AA8.byte$24, $24, $24, $24, $25, $a9, $2a, $2b
$3AB0.byte$23, $24, $24, $25, $36, $b7, $38, $39
$3AB8.byte$23
$3AB9.fill11, $24
$3AC4.byte$25, $81, $02, $03, $23, $24, $24, $25
$3ACC.byte$36, $b7, $38, $39, $23, $24, $24, $25
$3AD4.byte$29, $aa, $2b, $23, $24, $25, $07, $08
$3ADC.byte$09, $8f, $0f, $0f, $23, $24, $24, $25
$3AE4.byte$36, $b7, $38, $39, $23, $24, $24, $25
$3AEC.byte$81, $02, $03, $23, $24, $24, $24, $24
$3AF4.byte$25, $8f, $0f, $0f, $23, $24, $24, $25
$3AFC.byte$36, $b7, $38, $39, $23, $24, $24, $25
$3B04.byte$29, $2a, $2b, $0f, $0f, $0f, $0f, $0f
$3B0C.byte$0f, $0f, $23, $24, $24, $25, $00, $04
$3B14.byte$05, $06, $04, $05, $06, $04, $05, $06
$3B1C.byte$04, $05, $06, $0d, $00, $00
$3B22level_12.fill14, $00; x-ref: $1E5C
$3B30.byte$fd, $ff
$3B32.fill14, $00
$3B40.byte$fe, $ff
$3B42.fill14, $00
$3B50.byte$2c, $2c, $2c, $2c, $2c, $ac, $2c, $2c
$3B58.byte$2d, $2d, $2d, $2d, $2d, $ad, $2d, $2d
$3B60.byte$2e, $2e, $2e, $2e, $2e, $2e, $ae, $2e
$3B68.byte$2f, $2f, $2f, $2e, $2d, $ae, $2f, $2f
$3B70.byte$2f, $2f, $2f, $2e, $2d, $ae, $2f, $2f
$3B78.byte$2f, $2f, $2f, $2f, $af, $2f, $30, $30
$3B80.byte$30, $30, $30, $b0, $2f, $2f, $2e, $2e
$3B88.byte$2e, $2e, $2e, $ae, $2f, $30, $30, $30
$3B90.byte$30, $30, $30, $2f, $2e, $2d, $2d, $ae
$3B98.byte$2f, $30, $30, $30, $30, $2f, $2e, $2d
$3BA0.byte$2c, $2c, $2d, $ae, $2f, $30, $30, $30
$3BA8.byte$30, $30, $2f, $2e, $2d, $ae, $2f, $30
$3BB0.byte$30, $b0, $30, $2f, $2e, $2e, $2e, $2e
$3BB8.byte$ae, $2e, $2f, $2e, $2e, $ae, $2e, $2d
$3BC0.byte$2e, $2e, $2e, $2e, $2e, $ae, $2f, $30
$3BC8.byte$30, $30, $30, $b0, $30, $2f, $2e, $2e
$3BD0.byte$2e, $2e, $2e, $ae, $2f, $30, $30, $30
$3BD8.byte$30, $30, $b0, $2f, $30, $30, $30, $30
$3BE0.byte$2f, $ae, $2f, $30, $30, $30, $b0, $30
$3BE8.byte$2f, $2f, $2f, $2f, $2f, $2e, $2e, $2e
$3BF0.byte$ad, $2e, $2d, $2c, $2c, $ac, $2c, $2c
$3BF8.byte$2e, $2e, $2e, $2e, $ae, $2e, $30, $30
$3C00.byte$30, $30, $30, $30, $30, $2f, $30, $30
$3C08.byte$30, $30, $af, $2e, $30, $30, $30, $30
$3C10.byte$2f, $2e, $2d, $2c, $2d, $2e, $2d, $2e
$3C18.byte$2f, $2f, $2f, $2e, $2d, $ae, $2f, $30
$3C20.byte$30, $30, $b0, $30, $2f, $2f, $2f, $2f
$3C28.byte$2e, $2f, $2e, $2d, $2e, $2f, $2f, $2f
$3C30.byte$2f, $2e, $af, $2f, $2f, $2e, $2e, $ae
$3C38.byte$2e, $2e, $2d, $2d, $2d, $2d, $ad, $2d
$3C40.byte$2c, $2d, $2d, $2d, $2d, $ad, $2d, $2d
$3C48.fill8, $2c
$3C50.byte$80, $00
$3C52level_13.fill14, $00; x-ref: $1E5D
$3C60.byte$fd, $ff
$3C62.fill14, $00
$3C70.byte$fe, $ff
$3C72.fill14, $00
$3C80.byte$1a, $1b, $1b, $1c, $00, $00, $36, $37
$3C88.byte$38, $37, $38, $37, $38, $b7, $38, $39
$3C90.byte$00, $1a, $1b, $1c, $00, $36, $37, $38
$3C98.byte$37, $38, $37, $38, $b7, $38, $39, $00
$3CA0.byte$1a, $1b, $1c, $00, $36, $37, $38, $37
$3CA8.byte$38, $37, $38, $37, $38, $b7, $38, $39
$3CB0.byte$00, $1a, $1b, $1c, $00, $36, $37, $38
$3CB8.byte$37, $38, $37, $38, $39
$3CBD.fill11, $00
$3CC8.byte$12, $13, $14, $15, $b1, $31, $16, $17
$3CD0.byte$18, $19, $b1, $31, $12, $13, $14, $15
$3CD8.byte$b1, $31, $16, $17, $18, $19, $31, $31
$3CE0.byte$80, $00, $00, $00, $00, $00, $00, $00
$3CE8.fill10, $0d
$3CF2.byte$1d, $1e, $1e, $1e, $1f, $0d, $0d, $36
$3CFA.byte$37, $38, $37, $38, $37, $38, $39, $36
$3D02.byte$37, $38, $39, $36, $37, $38, $39, $80
$3D0A.fill10, $00
$3D14.byte$80, $00, $00, $00, $1a, $1b, $1b, $1c
$3D1C.byte$00, $36, $37, $38, $37, $38, $37, $38
$3D24.byte$37, $b8, $39, $00, $1d, $1e, $1e, $1f
$3D2C.byte$00, $36, $39, $36, $37, $38, $39, $36
$3D34.byte$37, $38, $37, $38, $39, $80
$3D3A.fill10, $00
$3D44.byte$80, $00, $00, $00, $1a, $1b, $1c, $00
$3D4C.byte$04, $05, $06, $00, $00, $00, $00, $00
$3D54.byte$80, $00, $00, $00, $1a, $1b, $1c, $00
$3D5C.byte$0a, $0b, $0c, $00, $00, $00, $00, $00
$3D64.byte$80, $00, $00, $00, $1a, $1b
$3D6Alevel_14.byte$1c, $00, $04, $05, $06; x-ref: $1E5E
$3D6F.fill9, $00
$3D78.byte$fd, $ff
$3D7A.fill14, $00
$3D88.byte$fe, $ff
$3D8A.fill14, $00
$3D98.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$3DA0.byte$8e, $0e, $0e, $0e, $0e, $0e, $0e, $0e
$3DA8.byte$8f, $0f, $0f, $0f, $0f, $0f, $0f, $0f
$3DB0.byte$90, $10, $10, $10, $10, $10, $10, $10
$3DB8.byte$91, $11, $11, $11, $11, $11, $11, $11
$3DC0.byte$23
$3DC1.fill138, $a4
$3E4B.byte$24, $24, $24, $24, $25, $91, $11, $11
$3E53.byte$11, $11, $11, $11, $11, $90, $10, $10
$3E5B.byte$10, $10, $10, $10, $10, $8f, $0f, $0f
$3E63.byte$0f, $0f, $0f, $0f, $0f, $8e, $0e, $0e
$3E6B.byte$0e, $0e, $0e, $0e, $0e, $8d, $0d, $0d
$3E73.byte$0d, $0d, $0d, $0d, $0d, $80, $00
$3E7Alevel_15.fill14, $00; x-ref: $1E5F
$3E88.byte$fd, $ff
$3E8A.fill14, $00
$3E98.byte$fe, $ff
$3E9A.fill11, $00
$3EA5.byte$80, $00, $00, $32, $32, $32, $32, $32
$3EAD.byte$b2, $32, $32, $33, $33, $33, $33, $34
$3EB5.byte$34, $34, $b3, $34, $34, $33, $33, $33
$3EBD.byte$33, $32, $33, $34, $35, $35, $34, $35
$3EC5.byte$b4, $33, $32, $33, $33, $33, $33, $33
$3ECD.byte$b2, $33, $34, $34, $34, $b4, $34, $35
$3ED5.byte$35, $35, $34, $33, $32, $b1, $32, $33
$3EDD.byte$33, $33, $33, $20, $21, $22, $00, $04
$3EE5.byte$05, $06, $04, $05, $06, $04, $05, $06
$3EED.byte$04, $05, $06, $80
$3EF1.fill15, $00
$3F00.byte$b1, $31, $31, $31, $31, $31, $31, $31
$3F08.byte$1a, $1b, $1c, $31, $31, $31, $36, $39
$3F10.byte$b1, $31, $31, $31, $1a, $1b, $1c, $31
$3F18.byte$36, $37, $38, $39, $b1, $31, $31, $31
$3F20.byte$1a, $1b, $1c, $31, $31, $36, $37, $38
$3F28.byte$39, $31, $31, $31, $1a, $1b, $1c, $31
$3F30.byte$36, $37, $38, $37, $38, $39, $b1, $31
$3F38.byte$1a, $1b, $1c, $b1
$3F3C.fill8, $31
$3F44.byte$1a, $1b, $1c, $31, $31, $36, $39, $b1
$3F4C.byte$31, $31, $31, $31, $1a, $1b, $1c, $31
$3F54.byte$31, $31, $31, $31, $80
$3F59.fill15, $00
$3F68.byte$36, $39, $80
$3F6B.fill13, $00
$3F78.byte$36, $b9, $00, $1a, $1b, $1b, $1c, $00
$3F80.byte$36, $37, $38, $37, $38, $37, $38, $39
$3F88.byte$80, $00, $00, $00, $00, $00, $00, $00
$3F90.byte$8d, $0d, $0d, $0d, $0d, $0d, $0d, $0d
$3F98.byte$1d, $1e, $1e, $1f, $00, $36, $37, $38
$3FA0.byte$37, $38, $37, $38, $37, $38, $37, $38
$3FA8.byte$37, $38, $39, $80
$3FAC.fill12, $00
$3FB8.byte$1d, $1e, $1e, $1f, $00, $07, $08, $09
$3FC0.byte$01, $02, $03, $07, $08, $09, $01, $02
$3FC8.byte$03, $07, $08, $09, $80
$3FCD.fill11, $00
$3FD8padding.fill16, $00
$3FE8.byte$fd, $ff
$3FEA.fill14, $00
$3FF8.byte$fe, $ff, $00, $00, $00, $00, $e1, $06