mads 2.1.7 build 21 (20 May 24)
Source: main.asm
     1 				;==============================================
     2 				; DOOM2D for Atari XE/XL + VBXE
     3 				; main.asm - Entry point
     4 				;
     5 				; Build: mads main.asm -o:doom2d.xex
     6 				;==============================================
     7
     8 				        opt h+
     9 				        opt o+
    10
    11 				        icl 'constants.asm'
Source: constants.asm
     1 				;==============================================
     2 				; DOOM2D - Constants & equates
     3 				; constants.asm
     4 				;==============================================
     5
     6 				; --- Atari OS ---
     7 = 0014			RTCLOK3         = $14
     8 = 022F			SDMCTL          = $22F
     9 = 02C8			COLOR4          = $2C8
    10 = E456			CIOV            = $E456
    11 = D300			PORTA           = $D300
    12 = D010			TRIG0           = $D010
    13 = D200			AUDF1           = $D200
    14 = D201			AUDC1           = $D201
    15 = D202			AUDF2           = $D202
    16 = D203			AUDC2           = $D203
    17 = D207			AUDC4           = $D207
    18 = D208			AUDCTL          = $D208
    19 = D209			STIMER          = $D209
    20 = D20E			IRQEN           = $D20E
    21 = D20F			SKCTL           = $D20F
    22 = D40B			VCOUNT          = $D40B
    23
    24 				; --- OS vectors/shadow ---
    25 = 0010			POKMSK          = $0010
    26 = 0210			VTIMR1          = $0210
    27
    28 				; --- Sound ---
    29 = 0000			SFX_PISTOL      = 0
    30 = 0001			SFX_ITEMUP      = 1
    31 = 0002			SFX_ROCKET      = 2
    32 = 0003			SFX_PODEATH     = 3
    33 = 0004			SFX_POSIGHT     = 4
    34 = 0005			SFX_IMPSIGHT    = 5
    35 = 0006			SFX_IMPDEATH    = 6
    36 = 0007			SFX_SHOTGUN     = 7
    37 = 0008			SFX_WPNUP      = 8
    38 = 0009			SFX_PUNCH      = 9
    39 = 000A			SFX_BAREXP     = 10
    40 = 000B			SFX_SLOP       = 11
    41 = 000C			SFX_BRSSIT     = 12
    42 = 000D			SFX_BRSDTH     = 13
    43 = 000E			SFX_SGTSIT     = 14
    44 = 000F			SFX_SGTDTH     = 15
    45 = 0010			SFX_CACSIT     = 16
    46 = 0011			SFX_CACDTH     = 17
    47 = 0012			SFX_PLASMA     = 18
    48 = 0013			SFX_BFG        = 19
    49 = 0014			SFX_BFGXPL     = 20
    50 = 0015			SFX_PLDEATH    = 21
    51 = 0016			SFX_DOOROPN    = 22
    52 = 0017			SFX_DOORCLS    = 23
    53 = 0018			SFX_OOF        = 24
    54 = 0019			SFX_POSIGHT2   = 25
    55 = 001A			SFX_POSIGHT3   = 26
    56 = 001B			SFX_FIRSHT     = 27
    57 = 001C			SFX_SWTCHN     = 28
    58 = 001D			SFX_SWTCHX     = 29
    59 = 001E			SFX_CLAW       = 30
    60 = 001F			SFX_SGTATK     = 31
    61
    62 = 001C			TILE_SWITCH_OFF = 28
    63 = 001D			TILE_SWITCH_ON  = 29
    64
    65 				; --- Switch target system ---
    66 = 0004			MAX_SWITCHES    = 4
    67 = 0000			SW_ACT_DOOR     = 0             ; switch opens door (spacebar locked)
    68 = 0001			SW_ACT_WALL     = 1             ; remove wall (hidden area)
    69 = 0002			SW_ACT_ELEV     = 2             ; call elevator (future)
    70 = 0003			SW_ACT_DOOR_LOCK = 3            ; pickup opens door (spacebar locked)
    71 = 0004			SW_ACT_FLOOR    = 4             ; floor trigger opens door (step on tile)
    72
    73 				; --- VBXE registers ---
    74 = D640			VBXE_VCTL       = $D640
    75 = D641			VBXE_XDL0       = $D641
    76 = D642			VBXE_XDL1       = $D642
    77 = D643			VBXE_XDL2       = $D643
    78 = D644			VBXE_CSEL       = $D644
    79 = D645			VBXE_PSEL       = $D645
    80 = D646			VBXE_CR         = $D646
    81 = D647			VBXE_CG         = $D647
    82 = D648			VBXE_CB         = $D648
    83 = D650			VBXE_BL_ADR0    = $D650
    84 = D651			VBXE_BL_ADR1    = $D651
    85 = D652			VBXE_BL_ADR2    = $D652
    86 = D653			VBXE_BLITTER    = $D653
    87 = D65E			VBXE_MEMAC_CTRL = $D65E
    88 = D65F			VBXE_BANK_SEL   = $D65F
    89
    90 				; --- VBXE constants ---
    91 = 0001			VC_XDL_ON       = $01
    92 = 0004			VC_NO_TRANS     = $04
    93 = 0008			MC_CPU          = $08
    94 = 0080			BANK_EN         = $80
    95 = 0001			BLT_TRANS       = $01
    96
    97 				; --- VRAM layout (double buffered) ---
    98 				; Screen 0: $000000 (64000 bytes)
    99 				; Tiles:    $010000 (bank $10, 4KB)
   100 				; Sprites:  $011000 (banks $11-$12, 7KB)
   101 				; Screen 1: $020000 (64000 bytes)
   102 				; XDL:      $07F000 (bank $7F)
   103 				; BCB:      $07F100 (bank $7F)
   104 = 0002			SCR1_HI         = $02           ; Screen 1 high byte for buffer swap
   105
   106 = 07F000			VRAM_XDL        = $07F000
   107 = 07F100			VRAM_BCB        = $07F100
   108
   109 				; Explicit MEMAC-A bank numbers
   110 = 0055			BANK_TILES      = $55           ; VRAM $055000 (moved from $52, sky now extends to $053FFF)
   111 = 0011			BANK_SPR0       = $11           ; VRAM $011000 (first 4KB of sprites)
   112 = 0012			BANK_SPR1       = $12           ; VRAM $012000 (remaining sprites)
   113 = 001D			BANK_HUD        = $1D           ; VRAM $01D000 (HUD font, 768 bytes)
   114 = 001E			BANK_MAP        = $1E           ; VRAM $01E000 (map data, 2048 bytes)
   115 = 0034			BANK_BG         = $34           ; VRAM $034000 (background sky image, 512x256)
   116
   117 				; MEMAC-B ($D65D): 16KB window at $4000-$7FFF for sound data
   118 				; Sound VRAM starts at $061000 = MEMAC-B bank $18 (each bank = 16KB)
   119 				; Control byte: $C0 = enable+CPU, + bank number
   120 = D65D			VBXE_MEMAC_B    = $D65D
   121 = 4000			MEMB            = $4000         ; MEMAC-B window base
   122 = 00D8			MEMB_SND_BANK0  = $D8           ; first sound bank ($C0 enable + bank $18 = VRAM $060000)
   123 = 007F			BANK_XDL        = $7F           ; VRAM $07F000
   124 = 007F			BANK_BCB        = $7F           ; VRAM $07F100
   125
   126 				; MEMAC-A window (at $9000 to avoid overlap with program data!)
   127 = 9000			MEMW            = $9000
   128
   129 				; --- Screen ---
   130 = 0140			SCR_W           = 320
   131 = 00C8			SCR_H           = 200
   132 = 0150			SCR_PITCH       = 336           ; 21 tiles * 16px, buffer row pitch for game
   133 = 0200			SKY_W           = 512           ; sky image width (wider than screen for parallax)
   134
   135 				; --- Tiles/map ---
   136 = 0010			TW              = 16
   137 = 0010			TH              = 16
   138 = 0040			MAP_W           = 64
   139 = 0020			MAP_H           = 32
   140 = 0014			TILES_X         = 20            ; dirty-tracked columns (screen-relative)
   141 = 000C			TILES_Y         = 12
   142 = 0015			RENDER_COLS     = 21            ; rendered columns (includes padding for XDL scroll)
   143
   144 				; --- Game ---
   145 = 0002			PL_MAXSPD       = 2             ; max horizontal speed (px/frame)
   146 = 0001			GRAV            = 1
   147 = 0008			JUMPF           = 8
   148 = 0003			MAXFALL         = 3
   149 = 0003			COYOTE          = 3             ; coyote time (frames after leaving edge)
   150 = 0004			MAX_PROJ        = 4
   151 = 0006			MAX_ENEMIES     = 6
   152 = 000C			MAX_PICKUPS     = 12
   153
   154 				; --- Pickup types ---
   155 = 0000			PK_HEALTH       = 0
   156 = 0001			PK_AMMO         = 1
   157 = 0002			PK_MEDIKIT      = 2
   158 = 0003			PK_GREENARMOR   = 3
   159 = 0004			PK_BLUEARMOR    = 4
   160 = 0005			PK_SOULSPHERE   = 5
   161 = 0006			PK_KEYRED       = 6
   162 = 0007			PK_KEYBLUE      = 7
   163 = 0008			PK_KEYYELLOW    = 8
   164 = 0009			PK_SHOTGUN      = 9
   165 = 000A			PK_SHELLS       = 10
   166 = 000B			PK_PISTOL       = 11
   167 = 000C			PK_CHAINGUN     = 12
   168 = 000D			PK_ROCKETL      = 13
   169 = 000E			PK_ROCKETBOX    = 14
   170 = 000F			PK_PLASMAGUN    = 15
   171 = 0010			PK_CELLS        = 16
   172 = 0011			PK_BFG          = 17
   173 = 0012			PK_ROCKET1      = 18
   174 = 0013			PK_HEALTHBONUS  = 19
   175 = 0014			PK_ARMORBONUS   = 20
   176 = 0015			NUM_PK_TYPES    = 21
   177
   178 				; --- Decoration ---
   179 = 0008			MAX_DECOR       = 8
   180 = 0000			DC_BARREL       = 0
   181 = 0001			DC_TORCH        = 1
   182 = 0002			DC_PILLAR       = 2
   183 = 0003			DC_LAMP         = 3
   184 = 0004			DC_DEADGUY      = 4
   185 = 0005			DC_TECHTHING    = 5
   186 = 0006			NUM_DC_TYPES    = 6
   187
   188 				; Barrel
   189 = 0003			BARREL_HP       = 3             ; hits to explode
   190 = 0014			BARREL_DMG      = 20            ; explosion damage to enemies (fixed)
   191 = 0020			BARREL_RADIUS   = 32            ; explosion radius (pixels)
   192 = 0064			BARREL_PLR_MAX  = 100           ; max barrel damage to player (point blank)
   193 = 0003			BARREL_PLR_SCALE = 3            ; damage reduction per pixel distance (100-32*3=4 at edge)
   194 = 0006			BARREL_KB_X     = 6             ; barrel knockback horizontal (stronger than rocket)
   195 = 0008			BARREL_KB_Y     = 8             ; barrel knockback vertical
   196
   197 				; Rocket splash
   198 = 0020			ROCKET_SPLASH_RADIUS = 32       ; splash radius (pixels)
   199 = 0032			ROCKET_SPLASH_MAX    = 50       ; max self-damage (point blank)
   200 = 0002			ROCKET_SPLASH_SCALE  = 2        ; damage reduction per pixel distance
   201 = 0003			ROCKET_KB_X          = 3        ; horizontal knockback velocity
   202 = 0006			ROCKET_KB_Y          = 6        ; upward knockback velocity
   203
   204 				; --- Player ---
   205 = 000A			PL_W            = 10            ; player collision width
   206 = 001C			PL_H            = 28            ; player collision height
   207
   208 				; --- Weapons ---
   209 = 0000			WP_FIST         = 0             ; key 1 (fist/chainsaw share slot)
   210 = 0001			WP_PISTOL       = 1             ; key 2
   211 = 0002			WP_SHOTGUN      = 2             ; key 3
   212 = 0003			WP_CHAINGUN     = 3             ; key 4
   213 = 0004			WP_ROCKET       = 4             ; key 5
   214 = 0005			WP_PLASMA       = 5             ; key 6
   215 = 0006			WP_BFG          = 6             ; key 7
   216 = 0007			NUM_WEAPONS     = 7
   217 = 00FF			AMMO_NONE       = $FF           ; melee weapons
   218
   219 				; --- HUD ---
   220 = D000			HUD_FONT_ADDR   = $D000         ; Font offset within bank $1D (low 12 bits)
   221 = 0008			HUD_CHAR_W      = 8
   222 = 0008			HUD_CHAR_H      = 8
   223 = 00C0			HUD_Y           = 192           ; Y position (bottom 8px of screen)
   224 = 0008			HUD_HP_IX       = 8             ; X for heart icon
   225 = 0018			HUD_HP_DX       = 24            ; X for HP first digit
   226 = 0050			HUD_AM_IX       = 80            ; X for bullet icon
   227 = 0060			HUD_AM_DX       = 96            ; X for ammo first digit
   228 = 000A			HUD_CHR_HEART   = 10
   229 = 000B			HUD_CHR_BULLET  = 11
   230 = 00A0			HUD_WP_DX       = 160           ; X for weapon number display
   231 = 00DC			HUD_KEY_DX      = 220           ; X for key icons (3x16px, max 220+48=268 < 320)
   232
   233 				; --- Game states ---
   234 = 0000			STATE_TITLE     = 0
   235 = 0001			STATE_PLAYING   = 1
   236
   237 				; --- Joystick (inverted) ---
   238 = 0001			J_UP            = $01
   239 = 0002			J_DOWN          = $02
   240 = 0004			J_LEFT          = $04
   241 = 0008			J_RIGHT         = $08
   242
   243 				; --- Sprite indices (all defined in sprite_defs.asm) ---
    12 				        icl 'zeropage.asm'
Source: zeropage.asm
     1 				;==============================================
     2 				; DOOM2D - Zero page variables
     3 				; zeropage.asm
     4 				;==============================================
     5
     6 = 0080			zt              = $80       ; temp
     7 = 0081			zt2             = $81
     8 = 0082			zsrc            = $82       ; 2b source pointer
     9 = 0086			zva             = $86       ; 3b VRAM address (24-bit)
    10
    11 = 0090			zfr             = $90       ; frame counter
    12 = 0091			zpx             = $91       ; player X pixel
    13 = 0092			zpy             = $92       ; player Y pixel
    14 = 0093			zpvx            = $93       ; player vel X (signed)
    15 = 0094			zpvy            = $94       ; player vel Y (signed)
    16 = 0095			zpdir           = $95       ; 0=right 1=left
    17 = 0096			zpst            = $96       ; player state
    18 = 0097			zpan            = $97       ; anim timer
    19 = 0098			zphp            = $98       ; health
    20 = 0099			zpammo          = $99       ; ammo
    21 = 009A			zpgnd           = $9A       ; on ground
    22 = 009B			zpcoy           = $9B       ; coyote timer
    23 = 009C			zpx_hi          = $9C       ; player X high byte (for positions > 255)
    24 = 009D			zpy_hi          = $9D       ; player Y high byte (for positions > 255)
    25
    26 = 00A0			zparmor         = $A0       ; armor points (0-200)
    27 = 00A1			zpkeys          = $A1       ; key flags: bit0=red, bit1=blue, bit2=yellow
    28 = 00A2			zpweap          = $A2       ; bitfield: owned weapons (bit0=fist..bit7=chainsaw)
    29 = 00A3			zpshells        = $A3       ; shells ammo (shotgun)
    30 = 00A4			zprockets       = $A4       ; rockets ammo (rocket launcher)
    31 = 00A5			zpcells         = $A5       ; cells ammo (plasma, BFG)
    32 = 00A6			zpwcur          = $A6       ; current weapon (0-7)
    33
    34 = 00A8			zjoy            = $A8       ; joystick state
    35 = 00A9			ztrig           = $A9       ; trigger state
    36 = 00AA			zjoyp           = $AA       ; previous joystick
    37 = 00AB			ztrigp          = $AB       ; previous trigger
    38
    39 				; Current enemy temp vars
    40 = 00B5			zzidx           = $B5       ; current enemy index
    41
    42 = 00C3			zpwcool         = $C3       ; weapon cooldown timer
    43 = 00C4			snd_ptr         = $C4       ; 2b: current sample address (sound IRQ)
    44 = 00C6			snd_end         = $C6       ; 2b: end of sample address
    45 = 00C8			snd_phase       = $C8       ; 0=hi nibble, 1=lo nibble
    46
    47 = 00C9			snd_active      = $C9       ; 0=silent, 1=playing
    48 = 00CA			snd_cur_byte    = $CA       ; current packed byte (for lo nibble)
    49 = 00CB			snd_lock        = $CB       ; >0 = priority sound playing, don't override
    50 = 00CC			snd_bank        = $CC       ; current MEMAC-B bank
    51 = 00CD			snd_save_y      = $CD       ; Y register save for sound IRQ
    52
    53 = 00B8			game_state      = $B8       ; 0=title, 1=playing
    54 = 00B9			dirty_ptr       = $B9       ; 2b pointer to current dirty array
    55 = 00BB			uc_bank         = $BB       ; upload: start bank (moved from code area to ZP)
    56 = 00BC			uc_cnt          = $BC       ; upload: bank count
    57 = 00BD			uc_lastpg       = $BD       ; upload: pages in last bank
    58
    59 = 00D0			zdx             = $D0       ; draw X lo
    60 = 00D1			zdxh            = $D1       ; draw X hi
    61 = 00D2			zdy             = $D2       ; draw Y
    62 = 00D4			ztptr           = $D4       ; 2b tile map pointer
    63
    64 				; Camera / scroll
    65 = 00D6			cam_tx          = $D6       ; tile column of left camera edge (0..44)
    66 = 00D7			cam_x_lo        = $D7       ; camera X lo (tile-aligned = cam_tx*16)
    67 = 00D8			cam_x_hi        = $D8       ; camera X hi
    68 = 00D9			cam_sub         = $D9       ; sub-tile pixel offset (0-15, XDL smooth scroll)
    69 = 00DA			scroll_redraw   = $DA       ; >0: full redraw pending (countdown for both buffers)
    70 = 00DB			cam_tx_prev     = $DB       ; previous cam_tx (to detect tile-column change)
    71 = 00DC			sky_x_lo        = $DC       ; parallax sky pixel offset lo (0-192)
    72 = 00DD			sky_x_sub       = $DD       ; parallax XDL fine scroll (0-4)
    73 = 00DE			cam_ty          = $DE       ; tile row of top camera edge (0..20)
    74 = 00DF			cam_y           = $DF       ; camera Y in pixels (tile-aligned = cam_ty*16)
    75 = 00E0			cam_ty_prev     = $E0       ; previous cam_ty (detect row change → full redraw)
    76 = 00E1			cam_y_hi        = $E1       ; camera Y high byte (for Y > 255)
    77 = 00E2			cam_sub_prev    = $E2       ; previous cam_sub (detect pixel-level horizontal change)
    13
    14 				;==============================================
    15 				; INIT segment: runs during XEX load, before main data
    16 				; Turns off screen to prevent garbage display
    17 				;==============================================
    18 				        org $0600
    19 				.proc early_init
    20 FFFF> 0600-0613> A9 00	        lda #0
    21 0602 8D 2F 02		        sta SDMCTL          ; disable ANTIC DMA
    22 0605 8D 00 D4		        sta $D400           ; DMACTL shadow
    23 0608 8D C8 02		        sta COLOR4          ; black background
    24 				        ; Disable BASIC ROM so $A000 area is RAM (for LUTs, spawn data)
    25 060B AD 01 D3		        lda $D301
    26 060E 29 FD		        and #$FD            ; clear bit 1 = BASIC OFF
    27 0610 8D 01 D3		        sta $D301
    28 0613 60			        rts
    29 				.endp
    30 02E2-02E3> 00 06		        ini early_init
    31
    32 				;==============================================
    33 				; MEMAC-B read trampoline (must be below $4000!)
    34 				; MEMAC-B maps $4000-$7FFF to VRAM when enabled.
    35 				; snd_irq code may be at $4000+, so enable/read/disable
    36 				; must execute from here ($0610) to avoid crash.
    37 				;==============================================
    38 0614			        org $0610
    39 0610			snd_memac_read
    40 0610-061E> A5 CC		        lda snd_bank
    41 0612 8D 5D D6		        sta VBXE_MEMAC_B
    42 0615 A0 00		        ldy #0
    43 0617 B1 C4		        lda (snd_ptr),y
    44 0619 8C 5D D6		        sty VBXE_MEMAC_B
    45 061C 85 CA		        sta snd_cur_byte
    46 061E 60			        rts
    47
    48 				;==============================================
    49 061F			        org $2000
    50 				;==============================================
    51
    52 2000			.proc main
    53 2000-2D19> 78		        sei
    54
    55 				        ; Black screen during init (no ANTIC DMA, black background)
    56 2001 A9 00		        lda #0
    57 2003 8D 2F 02		        sta SDMCTL
    58 2006 8D C8 02		        sta COLOR4
    59
    60 				        ; --- VBXE detection ---
    61 				        ; Try $D600 first
    62 2009 AD 40 D6		        lda $D640
    63 200C C9 10		        cmp #$10            ; FX core?
    64 200E F0 59		        beq ?vbxe_ok
    65 				        ; Try $D700
    66 2010 AD 40 D7		        lda $D740
    67 2013 C9 10		        cmp #$10
    68 2015 F0 52		        beq ?vbxe_ok
    69
    70 				        ; VBXE not found: restore default Atari screen
    71 2017 A9 22		        lda #$22
    72 2019 8D 2F 02		        sta SDMCTL          ; enable ANTIC DMA
    73 201C A9 00		        lda #0
    74 201E 8D C8 02		        sta COLOR4          ; black border (COLBK)
    75 2021 A9 94		        lda #$94
    76 2023 8D C6 02		        sta $02C6           ; COLOR2 = blue text bg
    77 2026 58			        cli
    78 				        ; Open E: on IOCB #0
    79 2027 A2 00		        ldx #0
    80 2029 A9 03		        lda #3              ; OPEN
    81 202B 8D 42 03		        sta $0342           ; ICCOM
    82 202E A9 66		        lda #<s_edev
    83 2030 8D 44 03		        sta $0344           ; ICBAL
    84 2033 A9 20		        lda #>s_edev
    85 2035 8D 45 03		        sta $0345           ; ICBAH
    86 2038 A9 0C		        lda #$0C            ; read+write
    87 203A 8D 4A 03		        sta $034A           ; ICAX1
    88 203D A9 00		        lda #0
    89 203F 8D 4B 03		        sta $034B           ; ICAX2
    90 2042 20 56 E4		        jsr CIOV
    91 				        ; Print message
    92 2045 A2 00		        ldx #0
    93 2047 A9 09		        lda #$09            ; PUT RECORD
    94 2049 8D 42 03		        sta $0342           ; ICCOM
    95 204C A9 A6		        lda #<no_vbxe_msg
    96 204E 8D 44 03		        sta $0344           ; ICBAL
    97 2051 A9 21		        lda #>no_vbxe_msg
    98 2053 8D 45 03		        sta $0345           ; ICBAH
    99 2056 A9 13		        lda #<no_vbxe_len
   100 2058 8D 48 03		        sta $0348           ; ICBLL
   101 205B A9 00		        lda #>no_vbxe_len
   102 205D 8D 49 03		        sta $0349           ; ICBLH
   103 2060 20 56 E4		        jsr CIOV
   104 2063 4C 63 20		        jmp *               ; halt
   105
   106 2066 45 3A 9B		s_edev  dta c'E:',$9B
   107
   108 2069			?vbxe_ok
   109 				        ; MEMAC-A: 4KB at $9000, CPU access
   110 2069 A9 98		        lda #$90+MC_CPU
   111 206B 8D 5E D6		        sta VBXE_MEMAC_CTRL
   112
   113 				        ; Keep VBXE display OFF during init (black screen)
   114 206E A9 00		        lda #0
   115 2070 8D 40 D6		        sta VBXE_VCTL
   116
   117 				        ; Palette, tiles, sprites, HUD font, map, title all uploaded via INI segments
   118 				        ; reformat_title_336 runs as INI segment (after last title upload)
   119 2073 20 B9 21		        jsr setup_xdl
   120
   121 				        ; Everything ready: NOW enable XDL display
   122 2076 A9 05		        lda #VC_XDL_ON+VC_NO_TRANS
   123 2078 8D 40 D6		        sta VBXE_VCTL
   124 207B A9 00		        lda #$00
   125 207D 8D 41 D6		        sta VBXE_XDL0
   126 2080 A9 F0		        lda #$F0
   127 2082 8D 42 D6		        sta VBXE_XDL1
   128 2085 A9 07		        lda #$07
   129 2087 8D 43 D6		        sta VBXE_XDL2
   130
   131 				        ; Detect PAL/NTSC
   132 208A AD 14 D0		        lda $D014           ; PAL register
   133 208D 29 0E		        and #$0E            ; bits 1-3
   134 208F 8D 00 23		        sta is_pal          ; 0=NTSC, non-zero=PAL
   135
   136 				        ; Digital sound engine init (Timer 1 IRQ + AUDC4)
   137 2092 20 53 5A		        jsr snd_init
   138
   139 				        ; Init double buffer: draw to screen 1, display screen 0
   140 2095 A9 02		        lda #SCR1_HI            ; $02
   141 2097 8D FF 22		        sta zbuf_hi
   142
   143 				        ; Init camera (no scroll for title)
   144 209A A9 00		        lda #0
   145 209C 85 D9		        sta cam_sub
   146 209E 85 D6		        sta cam_tx
   147 20A0 85 DB		        sta cam_tx_prev
   148 20A2 85 D7		        sta cam_x_lo
   149 20A4 85 D8		        sta cam_x_hi
   150 20A6 85 DA		        sta scroll_redraw
   151 20A8 85 DC		        sta sky_x_lo
   152 20AA 85 DD		        sta sky_x_sub
   153 20AC 85 DE		        sta cam_ty
   154 20AE 85 DF		        sta cam_y
   155 20B0 85 E0		        sta cam_ty_prev
   156
   157 				        ; Start in title state
   158 20B2 A9 00		        lda #STATE_TITLE
   159 20B4 85 B8		        sta game_state
   160
   161 20B6 58			        cli
   162
   163 				; =============================================
   164 				; TITLE SCREEN (menu logic in menu.asm)
   165 				; =============================================
   166 20B7 20 B7 4D		        jsr menu_title_screen
   167
   168 20BA			?start_game
   169 20BA A9 01		        lda #STATE_PLAYING
   170 20BC 85 B8		        sta game_state
   171 20BE 20 21 25		        jsr init_game
   172 20C1 20 B9 61		        jsr init_render
   173 				        ; Force HUD full redraw
   174 20C4 A9 FF		        lda #$FF
   175 20C6 8D 6C 49		        sta hud_prev_hp
   176 20C9 8D 6D 49		        sta hud_prev_ammo
   177 20CC 8D 6E 49		        sta hud_prev_weap
   178 20CF 8D 6F 49		        sta hud_prev_keys
   179 20D2 8D 70 49		        sta hud_prev_armor
   180 20D5 A9 02		        lda #2
   181 20D7 8D 71 49		        sta hud_frames
   182 20DA 8D 72 49		        sta hud_full_clear
   183
   184 				; --- Game loop (double buffered) ---
   185 20DD			?loop
   186 				        ; ESC = pause + menu
   187 20DD AD FC 02		        lda $02FC
   188 20E0 C9 1C		        cmp #$1C
   189 20E2 D0 0C		        bne ?no_esc
   190 20E4 A9 FF		        lda #$FF
   191 20E6 8D FC 02		        sta $02FC
   192 20E9 20 A0 4E		        jsr menu_pause
   193 20EC C9 01		        cmp #1
   194 20EE F0 CA		        beq ?start_game         ; new game requested
   195 20F0			?no_esc
   196 20F0 E6 90		        inc zfr
   197
   198 				        ; PROFILING: uncomment STA $D01A lines to see border color timing
   199 				        ; Colors: $10=gray(logic) $30=red(dirty) $C0=green(render)
   200 				        ;         $90=blue(HUD) $50=purple(blit wait) $00=black(idle)
   201 				        ; lda #$10
   202 				        ; sta $D01A
   203
   204 20F2 20 9C 29		        jsr read_input
   205 				        ; Check player death
   206 20F5 A5 98		        lda zphp
   207 20F7 D0 12		        bne ?alive
   208 20F9 20 5F 2E		        jsr player_dead
   209 20FC 20 E0 2E		        jsr camera_update
   210 20FF 20 05 25		        jsr calc_sky_offset
   211 2102 20 97 3A		        jsr update_enemies
   212 2105 20 D1 36		        jsr update_decorations
   213 2108 4C 2F 21		        jmp ?skip_logic
   214 210B			?alive
   215 210B 20 B7 29		        jsr player_update
   216 210E 20 E0 2E		        jsr camera_update
   217 2111 20 05 25		        jsr calc_sky_offset
   218 2114 20 97 3A		        jsr update_enemies
   219 2117 20 84 2D		        jsr proj_update
   220 211A 20 AC 45		        jsr eproj_update
   221 211D 20 5E 31		        jsr update_pickups
   222 2120 20 D1 36		        jsr update_decorations
   223 2123 20 A2 55		        jsr update_doors
   224 2126 20 A4 59		        jsr check_floor_triggers
   225 2129 20 E7 5A		        jsr sound_update
   226 212C 20 C0 4B		        jsr fps_update
   227 212F			?skip_logic
   228
   229 				        ; lda #$30
   230 				        ; sta $D01A
   231
   232 				        ; Scroll redraw: full clear+render when cam_tx changed
   233 212F A5 DA		        lda scroll_redraw
   234 2131 F0 2E		        beq ?no_scroll_rd
   235 2133 C6 DA		        dec scroll_redraw
   236 				        ; Clear dirty flags for current buffer
   237 2135 20 71 53		        jsr setup_dirty_ptr
   238 2138 A0 EF		        ldy #TILES_X*TILES_Y-1
   239 213A A9 00		        lda #0
   240 213C 91 B9		?clrd   sta (dirty_ptr),y
   241 213E 88			        dey
   242 213F 10 FB		        bpl ?clrd
   243 				        ; Full redraw with parallax sky
   244 2141 20 76 22		        jsr clear_screen
   245 2144 20 E3 46		        jsr render_tiles
   246 				        ; Force all pickups/decor to redraw (full-screen dirty bbox)
   247 2147 A9 01		        lda #1
   248 2149 8D E6 53		        sta dirty_any
   249 214C A9 00		        lda #0
   250 214E 8D E2 53		        sta dirty_min_col
   251 2151 8D E4 53		        sta dirty_min_row
   252 2154 A9 13		        lda #TILES_X-1
   253 2156 8D E3 53		        sta dirty_max_col
   254 2159 A9 0B		        lda #TILES_Y-1
   255 215B 8D E5 53		        sta dirty_max_row
   256 215E 4C 64 21		        jmp ?after_dirty
   257 2161			?no_scroll_rd
   258 2161 20 E7 53		        jsr restore_dirty
   259 2164			?after_dirty
   260
   261 				        ; lda #$C0
   262 				        ; sta $D01A
   263 2164 20 3B 34		        jsr render_pickups_nodirty
   264 2167 20 8E 35		        jsr render_decor_nodirty
   265 216A 20 2F 36		        jsr render_exploding
   266 216D 20 22 42		        jsr render_enemies
   267 2170 20 24 48		        jsr render_projs
   268 2173 20 96 46		        jsr render_eproj
   269 2176 20 37 47		        jsr render_player
   270
   271 				        ; lda #$90
   272 				        ; sta $D01A
   273 2179 20 73 49		        jsr render_hud
   274 217C 20 E9 4B		        jsr fps_render
   275
   276 				        ; lda #$50
   277 				        ; sta $D01A
   278 217F 20 DF 22		        jsr wait_blit
   279
   280 				        ; lda #$00
   281 				        ; sta $D01A
   282
   283 				        ; Wait VBLANK
   284 2182 A5 14		        lda RTCLOK3
   285 2184 C5 14		?vsync  cmp RTCLOK3
   286 2186 F0 FC		        beq ?vsync
   287
   288 				        ; Swap: update XDL overlay addresses to show back buffer
   289 2188 A9 FF		        lda #BANK_EN+BANK_XDL
   290 218A 8D 5F D6		        sta VBXE_BANK_SEL
   291 218D A5 D9		        lda cam_sub
   292 218F 8D 06 90		        sta MEMW+[VRAM_XDL&$FFF]+XDL_GAME_LO
   293 2192 AD FF 22		        lda zbuf_hi
   294 2195 8D 08 90		        sta MEMW+[VRAM_XDL&$FFF]+XDL_GAME_HI
   295 2198 8D 12 90		        sta MEMW+[VRAM_XDL&$FFF]+XDL_HUD_HI
   296
   297 				        ; Flip: toggle zbuf_hi between $00 and $02
   298 219B AD FF 22		        lda zbuf_hi
   299 219E 49 02		        eor #SCR1_HI
   300 21A0 8D FF 22		        sta zbuf_hi
   301
   302 21A3 4C DD 20		        jmp ?loop
   303 				.endp
   304
   305 				; --- VBXE not detected message ---
   306 21A6			no_vbxe_msg
   307 21A6 56 42 58 45 20 4E +         dta c'VBXE NOT DETECTED!'
   308 21B8 9B			        dta $9B                    ; EOL
   309 = 0013			no_vbxe_len = *-no_vbxe_msg
   310
   311 				; --- Modules ---
   312 21B9			        icl 'vbxe.asm'
Source: vbxe.asm
     1 				;==============================================
     2 				; DOOM2D - VBXE init, palette, XDL, blitter
     3 				; vbxe.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; XDL
     8 				; ============================================
     9 21B9			.proc setup_xdl
    10 21B9 A9 FF		        lda #BANK_EN+BANK_XDL
    11 21BB 8D 5F D6		        sta VBXE_BANK_SEL
    12 21BE A2 16		        ldx #xdl_len-1
    13 21C0 BD F1 21		?lp     lda xdl_data,x
    14 21C3 9D 00 90		        sta MEMW+[VRAM_XDL&$FFF],x
    15 21C6 CA			        dex
    16 21C7 10 F7		        bpl ?lp
    17 				        ; Pre-fill constant BCB fields (never change during gameplay)
    18 21C9 A9 FF		        lda #BANK_EN+BANK_BCB
    19 21CB 8D 5F D6		        sta VBXE_BANK_SEL
    20 21CE A9 50		        lda #<SCR_PITCH
    21 21D0 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9      ; dst step lo = 336
    22 21D3 A9 01		        lda #>SCR_PITCH
    23 21D5 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10     ; dst step hi
    24 21D8 A9 01		        lda #1
    25 21DA 8D 0B 91		        sta MEMW+[VRAM_BCB&$FFF]+11     ; dst step mode
    26 21DD A9 FF		        lda #$FF
    27 21DF 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15     ; AND mask
    28 21E2 A9 00		        lda #0
    29 21E4 8D 10 91		        sta MEMW+[VRAM_BCB&$FFF]+16     ; XOR lo
    30 21E7 8D 11 91		        sta MEMW+[VRAM_BCB&$FFF]+17     ; XOR hi
    31 21EA 8D 12 91		        sta MEMW+[VRAM_BCB&$FFF]+18     ; collision
    32 21ED 8D 13 91		        sta MEMW+[VRAM_BCB&$FFF]+19     ; pattern
    33 21F0 60			        rts
    34 				.endp
    35
    36 				; ============================================
    37 				; XDL DATA: 3 blocks (border + game + HUD)
    38 				; Single game block with cam_sub scroll.
    39 				; Parallax achieved by adjusting sky blit source offset
    40 				; in calc_sky_offset (compensates for cam_sub XDL scroll).
    41 				; ============================================
    42 21F1			xdl_data
    43 				        ; Block 1: border (20 lines, overlay off)
    44 21F1 24 00		        dta $24,$00
    45 21F3 13			        dta 19
    46 				        ; Block 2: game area (192 lines, scrollable)
    47 21F4 62 08		        dta $62,$08
    48 21F6 BF			        dta 191
    49 21F7 00 00 00		        dta $00,$00,$00         ; OVADR: lo=cam_sub, mid=$00, hi=buffer
    50 21FA 50 01		        dta <SCR_PITCH,>SCR_PITCH
    51 21FC 11 FF		        dta $11,$FF
    52 				        ; Block 3: HUD (8 lines, no scroll)
    53 21FE 62 88		        dta $62,$88
    54 2200 07			        dta 7
    55 2201 00 FC 00		        dta $00,$FC,$00
    56 2204 50 01		        dta <SCR_PITCH,>SCR_PITCH
    57 2206 11 FF		        dta $11,$FF
    58 = 0017			xdl_len = * - xdl_data
    59
    60 				; XDL byte offsets for runtime updates
    61 = 0006			XDL_GAME_LO     = 6            ; game block: OVADR lo (cam_sub)
    62 = 0008			XDL_GAME_HI     = 8            ; game block: buffer select
    63 = 0012			XDL_HUD_HI      = 18           ; HUD block: buffer select
    64
    65 				; ============================================
    66 				; INIT CLEAR (both screen buffers: banks 0-15 + $20-$2F)
    67 				; ============================================
    68 2208			.proc init_clear
    69 				        ; Clear screen 0 (banks 0-15)
    70 2208 A9 00		        lda #0
    71 220A 8D 75 22		        sta ic_bank
    72 220D AD 75 22		?bank1  lda ic_bank
    73 2210 09 80		        ora #BANK_EN
    74 2212 8D 5F D6		        sta VBXE_BANK_SEL
    75 2215 20 3D 22		        jsr ?clear_page
    76 2218 EE 75 22		        inc ic_bank
    77 221B AD 75 22		        lda ic_bank
    78 221E C9 10		        cmp #16
    79 2220 D0 EB		        bne ?bank1
    80 				        ; Clear screen 1 (banks $20-$2F)
    81 2222 A9 20		        lda #$20
    82 2224 8D 75 22		        sta ic_bank
    83 2227 AD 75 22		?bank2  lda ic_bank
    84 222A 09 80		        ora #BANK_EN
    85 222C 8D 5F D6		        sta VBXE_BANK_SEL
    86 222F 20 3D 22		        jsr ?clear_page
    87 2232 EE 75 22		        inc ic_bank
    88 2235 AD 75 22		        lda ic_bank
    89 2238 C9 30		        cmp #$30
    90 223A D0 EB		        bne ?bank2
    91 223C 60			        rts
    92
    93 223D			?clear_page
    94 223D A9 00		        lda #0
    95 223F A0 00		        ldy #0
    96 2241 99 00 90		?p      sta MEMW,y
    97 2244 99 00 91		        sta MEMW+$100,y
    98 2247 99 00 92		        sta MEMW+$200,y
    99 224A 99 00 93		        sta MEMW+$300,y
   100 224D 99 00 94		        sta MEMW+$400,y
   101 2250 99 00 95		        sta MEMW+$500,y
   102 2253 99 00 96		        sta MEMW+$600,y
   103 2256 99 00 97		        sta MEMW+$700,y
   104 2259 99 00 98		        sta MEMW+$800,y
   105 225C 99 00 99		        sta MEMW+$900,y
   106 225F 99 00 9A		        sta MEMW+$A00,y
   107 2262 99 00 9B		        sta MEMW+$B00,y
   108 2265 99 00 9C		        sta MEMW+$C00,y
   109 2268 99 00 9D		        sta MEMW+$D00,y
   110 226B 99 00 9E		        sta MEMW+$E00,y
   111 226E 99 00 9F		        sta MEMW+$F00,y
   112 2271 C8			        iny
   113 2272 D0 CD		        bne ?p
   114 2274 60			        rts
   115 2275 00			ic_bank dta 0
   116 				.endp
   117
   118 				; upload_tiles, upload_hud_font, setup_palette - all moved to INI segments in main.asm
   119
   120 				; ============================================
   121 				; CLEAR SCREEN (blit sky to full game area, 192 lines)
   122 				; Vertical parallax: sky source row = cam_ty * 3 (via sky_pv_mid).
   123 				; Max source end: cam_ty=20 → row 60+192=252 < 256. Always fits.
   124 				; ============================================
   125 2276			.proc clear_screen
   126 2276 A9 FF		        lda #BANK_EN+BANK_BCB
   127 2278 8D 5F D6		        sta VBXE_BANK_SEL
   128 				        ; Source: sky at (sky_x_lo, cam_ty*3)
   129 227B A5 DC		        lda sky_x_lo
   130 227D 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0     ; src lo
   131 2280 A9 40		        lda #$40                        ; sky base mid ($034000)
   132 2282 18			        clc
   133 2283 6D 20 25		        adc sky_pv_mid                  ; + cam_ty*6 (vertical parallax)
   134 2286 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1     ; src mid
   135 2289 A9 03		        lda #$03                        ; sky base hi
   136 228B 69 00		        adc #0
   137 228D 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2     ; src hi
   138 2290 A9 00		        lda #<SKY_W
   139 2292 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3     ; src step = 512
   140 2295 A9 02		        lda #>SKY_W
   141 2297 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   142 229A A9 01		        lda #1
   143 229C 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   144 				        ; Destination: buffer at (0,0)
   145 229F A9 00		        lda #0
   146 22A1 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   147 22A4 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   148 22A7 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20    ; mode = 0
   149 22AA AD FF 22		        lda zbuf_hi
   150 22AD 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   151 				        ; Size: 336 x 192 (full buffer width, covers padding column)
   152 22B0 A9 4F		        lda #<[SCR_PITCH-1]
   153 22B2 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   154 22B5 A9 01		        lda #>[SCR_PITCH-1]
   155 22B7 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   156 22BA A9 FF		        lda #$FF
   157 22BC 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15    ; AND = $FF
   158 22BF A9 BF		        lda #HUD_Y-1
   159 22C1 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14    ; height = 192 (no HUD overlap)
   160 22C4 4C C7 22		        jmp run_blit
   161 				.endp
   162
   163 				; ============================================
   164 				; BLITTER
   165 				; ============================================
   166 22C7			.proc run_blit
   167 22C7 20 DF 22		        jsr wait_blit
   168 22CA A9 00		        lda #$00
   169 22CC 8D 50 D6		        sta VBXE_BL_ADR0
   170 22CF A9 F1		        lda #$F1
   171 22D1 8D 51 D6		        sta VBXE_BL_ADR1
   172 22D4 A9 07		        lda #$07
   173 22D6 8D 52 D6		        sta VBXE_BL_ADR2
   174 22D9 A9 01		        lda #1
   175 22DB 8D 53 D6		        sta VBXE_BLITTER
   176 22DE 60			        rts                     ; return immediately, CPU works while blitter runs
   177 				.endp
   178
   179 22DF			.proc wait_blit
   180 22DF AD 53 D6		?w      lda VBXE_BLITTER
   181 22E2 D0 FB		        bne ?w
   182 22E4 60			        rts
   183 				.endp
   184
   185 				; ============================================
   186 				; CALC DST: zva = zdy * 320 + zdx + buffer_base
   187 				; buffer 0 = $000000, buffer 1 = $020000
   188 				; ============================================
   189 22E5			.proc calc_dst
   190 				        ; LUT version: zva = y_addr[zdy] + zdx + buffer_base
   191 				        ; y_addr is 17-bit (336*199 > 65535), xhi handles overflow
   192 22E5 A4 D2		        ldy zdy
   193 22E7 B9 00 A0		        lda y_addr_lo,y
   194 22EA 18			        clc
   195 22EB 65 D0		        adc zdx
   196 22ED 85 86		        sta zva
   197 22EF B9 C8 A0		        lda y_addr_hi,y
   198 22F2 65 D1		        adc zdxh
   199 22F4 85 87		        sta zva+1
   200 22F6 B9 90 A1		        lda y_addr_xhi,y
   201 22F9 6D FF 22		        adc zbuf_hi
   202 22FC 85 88		        sta zva+2
   203 22FE 60			        rts
   204 				.endp
   205
   206 				; Buffer high byte: $00 for screen 0, $02 for screen 1
   207 22FF 00			zbuf_hi dta 0
   208
   209 				; PAL/NTSC flag: 0=NTSC, non-zero=PAL
   210 2300 00			is_pal  dta 0
   211
   212 				; ============================================
   213 				; BLIT TILE 16x16
   214 				; src = $01:(r_tile):$00
   215 				; ============================================
   216 2301			.proc blit_tile
   217 				        ; Check if one-way tile or switch (needs bg + transparent blit)
   218 2301 AD 36 47		        lda r_tile
   219 2304 AA			        tax
   220 2305 BD B9 A5		        lda tile_oneway,x
   221 2308 D0 0E		        bne ?draw_bg
   222 230A AD 36 47		        lda r_tile
   223 230D C9 1C		        cmp #TILE_SWITCH_OFF
   224 230F F0 07		        beq ?draw_bg
   225 2311 C9 1D		        cmp #TILE_SWITCH_ON
   226 2313 F0 03		        beq ?draw_bg
   227 2315 4C 23 23		        jmp ?normal
   228 2318			?draw_bg
   229 				        ; First: draw sky background section at this tile position
   230 2318 AD 36 47		        lda r_tile
   231 231B 48			        pha                 ; save original tile index
   232 231C 20 79 24		        jsr blit_bg         ; copies sky from VRAM $034000+ at r_col,r_row
   233 231F 68			        pla
   234 2320 8D 36 47		        sta r_tile          ; restore original tile
   235 				        ; Then: draw platform with BLT_TRANS (below, mode set at end)
   236 2323			?normal
   237 2323 A9 FF		        lda #BANK_EN+BANK_BCB
   238 2325 8D 5F D6		        sta VBXE_BANK_SEL
   239 2328 A9 00		        lda #0
   240 232A 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   241 232D AD 36 47		        lda r_tile
   242 2330 C9 10		        cmp #16
   243 2332 B0 0E		        bcs ?newtile
   244 				        ; Old tiles 0-15: VRAM $05:($50+tile):$00 (moved to $055000)
   245 2334 18			        clc
   246 2335 69 50		        adc #$50            ; tile 0 → $50, tile 1 → $51...
   247 2337 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   248 233A A9 05		        lda #$05
   249 233C 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   250 233F 4C 5F 23		        jmp ?tileok
   251 2342			?newtile
   252 				        ; Switch tiles 28-29: VRAM $06:(tile-28):$00
   253 2342 C9 1C		        cmp #TILE_SWITCH_OFF
   254 2344 90 0E		        bcc ?oldnew
   255 2346 38			        sec
   256 2347 E9 1C		        sbc #TILE_SWITCH_OFF ; 28→0, 29→1
   257 2349 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   258 234C A9 06		        lda #$06
   259 234E 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   260 2351 4C 5F 23		        jmp ?tileok
   261 2354			?oldnew
   262 				        ; New tiles 16-27: VRAM $03:(tile+$10):$00
   263 2354 18			        clc
   264 2355 69 10		        adc #$10            ; tile 16 → $20, tile 17 → $21...
   265 2357 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   266 235A A9 03		        lda #$03
   267 235C 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   268 235F			?tileok
   269 235F A9 10		        lda #TW
   270 2361 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   271 2364 A9 00		        lda #0
   272 2366 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   273 2369 85 D1		        sta zdxh
   274 236B A9 01		        lda #1
   275 236D 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   276 2370 AD 35 47		        lda r_col
   277 2373 0A			        asl
   278 2374 0A			        asl
   279 2375 0A			        asl
   280 2376 0A			        asl
   281 2377 26 D1		        rol zdxh            ; capture carry for cols 16-19
   282 2379 85 D0		        sta zdx
   283 237B AD 34 47		        lda r_row
   284 237E 0A			        asl
   285 237F 0A			        asl
   286 2380 0A			        asl
   287 2381 0A			        asl
   288 2382 85 D2		        sta zdy
   289 2384 20 E5 22		        jsr calc_dst
   290 2387 A5 86		        lda zva
   291 2389 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   292 238C A5 87		        lda zva+1
   293 238E 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   294 2391 A5 88		        lda zva+2
   295 2393 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   296 				        ; dst step [9-11], AND [15], XOR [16-19] pre-filled
   297 2396 A9 0F		        lda #TW-1
   298 2398 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   299 239B A9 00		        lda #0
   300 239D 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   301 23A0 AD C7 23		        lda bt_height           ; default TH-1, or clipped for extra row
   302 23A3 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   303 				        ; Blitter mode: transparent for one-way/switch tiles, opaque for rest
   304 23A6 AD 36 47		        lda r_tile
   305 23A9 AA			        tax
   306 23AA BD B9 A5		        lda tile_oneway,x
   307 23AD D0 0F		        bne ?trans
   308 23AF AD 36 47		        lda r_tile
   309 23B2 C9 1C		        cmp #TILE_SWITCH_OFF
   310 23B4 F0 08		        beq ?trans
   311 23B6 C9 1D		        cmp #TILE_SWITCH_ON
   312 23B8 F0 04		        beq ?trans
   313 23BA A9 00		        lda #0              ; BLT_COPY (opaque)
   314 23BC F0 02		        beq ?setm
   315 23BE A9 01		?trans  lda #BLT_TRANS      ; transparent (skip index 0)
   316 23C0 8D 14 91		?setm   sta MEMW+[VRAM_BCB&$FFF]+20
   317 23C3 20 C7 22		        jsr run_blit
   318 23C6 60			?skip   rts
   319 				.endp
   320
   321 23C7 0F			bt_height dta TH-1              ; tile blit height (default 15, set to clip_h-1 for extra row)
   322
   323 				; ============================================
   324 				; BLIT SPRITE (transparent mode 1)
   325 				; src = $01:(spr_off_hi+$10):spr_off_lo
   326 				; ============================================
   327 23C8			.proc blit_sprite
   328 23C8 AA			        tax
   329 				        ; --- Right-edge clipping ---
   330 23C9 A5 D1		        lda zdxh
   331 23CB C9 02		        cmp #2
   332 23CD 90 03		        bcc ?xlt2
   333 23CF 4C 76 24		        jmp ?skip               ; X >= 512: entirely off screen
   334 23D2 C9 01		?xlt2   cmp #1
   335 23D4 F0 09		        beq ?clip_r             ; zdxh=1: may need right clip
   336 				        ; zdxh=0: max right edge = 255+16=271 < 320, no clip
   337 23D6 BD 13 A4		        lda spr_w,x
   338 23D9 8D 77 24		        sta bs_w
   339 23DC 4C FC 23		        jmp ?chk_bot
   340 23DF			?clip_r ; zdxh=1: available width = 320-256-zdx = 64-zdx
   341 23DF A9 40		        lda #64
   342 23E1 38			        sec
   343 23E2 E5 D0		        sbc zdx
   344 23E4 F0 0D		        beq ?cr_off
   345 23E6 90 0B		        bcc ?cr_off             ; zdx >= 64: off screen
   346 23E8 DD 13 A4		        cmp spr_w,x
   347 23EB B0 09		        bcs ?clip_rok           ; available >= sprite width
   348 23ED 8D 77 24		        sta bs_w                ; clipped width
   349 23F0 4C FC 23		        jmp ?chk_bot
   350 23F3 4C 76 24		?cr_off jmp ?skip
   351 23F6			?clip_rok
   352 23F6 BD 13 A4		        lda spr_w,x
   353 23F9 8D 77 24		        sta bs_w
   354
   355 23FC			?chk_bot
   356 				        ; --- Bottom-edge clipping ---
   357 23FC BD A5 A4		        lda spr_h,x
   358 23FF 8D 78 24		        sta bs_h
   359 2402 A5 D2		        lda zdy
   360 2404 18			        clc
   361 2405 6D 78 24		        adc bs_h
   362 2408 C9 C9		        cmp #SCR_H+1            ; zdy + h > 200?
   363 240A 90 0D		        bcc ?no_bclip
   364 240C A9 C8		        lda #SCR_H              ; clip: new height = 200 - zdy
   365 240E 38			        sec
   366 240F E5 D2		        sbc zdy
   367 2411 D0 03		        bne ?bclip_ok
   368 2413 4C 76 24		        jmp ?skip
   369 2416			?bclip_ok
   370 2416 8D 78 24		        sta bs_h
   371 2419			?no_bclip
   372 				        ; --- Blit ---
   373 2419 A9 FF		        lda #BANK_EN+BANK_BCB
   374 241B 8D 5F D6		        sta VBXE_BANK_SEL
   375 241E BD CB 5C		        lda spr_off_lo,x
   376 2421 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   377 2424 BD 5D 5D		        lda spr_off_hi,x
   378 2427 18			        clc
   379 2428 69 10		        adc #$10
   380 242A 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   381 242D BD EF 5D		        lda spr_off_bank,x
   382 2430 69 00		        adc #0
   383 2432 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   384 				        ; src step = original sprite width (not clipped!)
   385 2435 BD 13 A4		        lda spr_w,x
   386 2438 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   387 243B A9 00		        lda #0
   388 243D 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   389 2440 A9 01		        lda #1
   390 2442 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   391 2445 20 E5 22		        jsr calc_dst
   392 2448 A5 86		        lda zva
   393 244A 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   394 244D A5 87		        lda zva+1
   395 244F 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   396 2452 A5 88		        lda zva+2
   397 2454 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   398 				        ; dst step [9-11] pre-filled by setup_xdl
   399 				        ; Blit size = clipped width x clipped height
   400 2457 AD 77 24		        lda bs_w
   401 245A 38			        sec
   402 245B E9 01		        sbc #1
   403 245D 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   404 2460 A9 00		        lda #0
   405 2462 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   406 2465 AD 78 24		        lda bs_h
   407 2468 38			        sec
   408 2469 E9 01		        sbc #1
   409 246B 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   410 				        ; AND [15], XOR [16-19] pre-filled by setup_xdl
   411 246E A9 01		        lda #BLT_TRANS
   412 2470 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   413 2473 20 C7 22		        jsr run_blit
   414 2476 60			?skip   rts
   415 				.endp
   416
   417 2477 00			bs_w    dta 0
   418 2478 00			bs_h    dta 0
   419
   420 				; ============================================
   421 				; BLIT BG 16x16 (restore sky background for empty tiles)
   422 				; Source = bg_row[r_row] + sky_pv_mid + sky_x_lo + sky_col*16
   423 				; Matches clear_screen vertical parallax addressing.
   424 				; Sky is 512px wide, src step = 512. Col 20 wraps to col 0.
   425 				; ============================================
   426 2479			.proc blit_bg
   427 2479 A9 FF		        lda #BANK_EN+BANK_BCB
   428 247B 8D 5F D6		        sta VBXE_BANK_SEL
   429 				        ; Source column: r_col*16 + sky_x_lo
   430 				        ; No wrapping: sky_x_lo max=176, col 20*16=320, sum max=496+16=512
   431 247E AD 35 47		        lda r_col
   432 2481 0A			        asl
   433 2482 0A			        asl
   434 2483 0A			        asl
   435 2484 0A			        asl                         ; sky_col*16 (0-304), C=1 if sky_col>=16
   436 2485 85 80		        sta zt                      ; save col offset lo
   437 2487 A9 00		        lda #0
   438 2489 69 00		        adc #0                      ; carry from shift = col offset hi (0 or 1)
   439 248B 85 81		        sta zt2
   440 				        ; Add sky_x_lo (0-192). sum max = 304+192 = 496, fits 9 bits
   441 248D A5 80		        lda zt
   442 248F 18			        clc
   443 2490 65 DC		        adc sky_x_lo
   444 2492 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0  ; src lo
   445 2495 A5 81		        lda zt2
   446 2497 69 00		        adc #0                      ; propagate carry
   447 2499 85 81		        sta zt2                     ; col+sky offset hi (0 or 1)
   448 				        ; Source row: bg_row[r_row] + sky_pv_mid
   449 				        ; Combine sky_pv_mid + zt2 first (max 120+1=121, no overflow)
   450 				        ; then add to bg_row_mid with correct carry propagation to hi
   451 249B AD 20 25		        lda sky_pv_mid
   452 249E 18			        clc
   453 249F 65 81		        adc zt2                     ; 0-121, never overflows
   454 24A1 85 80		        sta zt                      ; combined offset
   455 24A3 AC 34 47		        ldy r_row
   456 24A6 B9 58 A2		        lda bg_row_mid,y
   457 24A9 18			        clc
   458 24AA 65 80		        adc zt
   459 24AC 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1  ; src mid
   460 24AF B9 78 A2		        lda bg_row_hi,y
   461 24B2 69 00		        adc #0                      ; propagate carry
   462 24B4 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2  ; src hi
   463 				        ; src step = 512 (SKY_W)
   464 24B7 A9 00		        lda #<SKY_W
   465 24B9 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   466 24BC A9 02		        lda #>SKY_W
   467 24BE 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   468 24C1 A9 01		        lda #1
   469 24C3 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   470 				        ; Destination: screen at (r_col*16, r_row*16)
   471 24C6 A9 00		        lda #0
   472 24C8 85 D1		        sta zdxh
   473 24CA AD 35 47		        lda r_col
   474 24CD 0A			        asl
   475 24CE 0A			        asl
   476 24CF 0A			        asl
   477 24D0 0A			        asl
   478 24D1 26 D1		        rol zdxh
   479 24D3 85 D0		        sta zdx
   480 24D5 AD 34 47		        lda r_row
   481 24D8 0A			        asl
   482 24D9 0A			        asl
   483 24DA 0A			        asl
   484 24DB 0A			        asl
   485 24DC 85 D2		        sta zdy
   486 24DE 20 E5 22		        jsr calc_dst
   487 24E1 A5 86		        lda zva
   488 24E3 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   489 24E6 A5 87		        lda zva+1
   490 24E8 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   491 24EB A5 88		        lda zva+2
   492 24ED 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   493 				        ; Size 16x16, mode=0
   494 24F0 A9 0F		        lda #TW-1
   495 24F2 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   496 24F5 A9 00		        lda #0
   497 24F7 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   498 24FA 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   499 24FD A9 0F		        lda #TH-1
   500 24FF 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   501 2502 4C C7 22		        jmp run_blit
   502 				.endp
   503
   504 				; bg_row_mid, bg_row_hi moved to $A000 segment (see main.asm)
   505
   506 				; ============================================
   507 				; CALC SKY OFFSET (proportional parallax, horizontal + vertical)
   508 				; Horizontal: sky_x_lo = sky_lut[cam_tx] (0-192)
   509 				;   Same as yesterday's working version. Changes only with cam_tx
   510 				;   (triggers scroll_redraw), so dirty restore is always consistent.
   511 				; Vertical: sky_pv_mid = cam_ty * 6
   512 				;   Maps cam_ty (0-20) to sky pixel row cam_ty*3 (0-60).
   513 				;   Changes only with cam_ty (triggers scroll_redraw).
   514 				; Called once per frame after camera_update.
   515 				; ============================================
   516 2505			.proc calc_sky_offset
   517 				        ; --- Vertical parallax: sky_pv_mid = cam_ty * 6 ---
   518 2505 A5 DE		        lda cam_ty              ; 0-20
   519 2507 0A			        asl                     ; *2 (0-40)
   520 2508 85 80		        sta zt
   521 250A 0A			        asl                     ; *4 (0-80)
   522 250B 18			        clc
   523 250C 65 80		        adc zt                  ; *6 (0-120)
   524 250E 8D 20 25		        sta sky_pv_mid
   525
   526 				        ; --- Horizontal parallax ---
   527 				        ; sky_x_lo = sky_lut[cam_tx] - cam_sub
   528 				        ; The -cam_sub cancels the XDL fine scroll for the sky:
   529 				        ;   screen sees sky[sky_x_lo + cam_sub] = sky[sky_lut - cam_sub + cam_sub] = sky[sky_lut]
   530 				        ; Sky stays at sky_lut[cam_tx] on screen, no 1:1 drift.
   531 				        ; At tile boundary: sky jumps forward by ~4px (delta) — smooth.
   532 				        ; Dirty restore: sky_x_lo changes with cam_sub but blit_bg
   533 				        ;   restores the SAME screen position (cam_sub offset cancels).
   534 2511 A6 D6		        ldx cam_tx
   535 2513 BD 98 A2		        lda sky_lut,x
   536 2516 38			        sec
   537 2517 E5 D9		        sbc cam_sub             ; cancel XDL fine scroll
   538 2519 B0 02		        bcs ?store
   539 251B A9 00		        lda #0                  ; clamp at left edge
   540 251D 85 DC		?store  sta sky_x_lo
   541 251F 60			        rts
   542 				.endp
   543
   544 2520 00			sky_pv_mid dta 0               ; vertical parallax mid byte offset
   545
   546 				; sky_lut moved to $A000 segment (see main.asm)
   547
   313 2521			        icl 'game.asm'
Source: game.asm
     1 				;==============================================
     2 				; DOOM2D - Game utilities (init, tile collision, sound)
     3 				; game.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; INIT GAME
     8 				; ============================================
     9 2521			.proc init_game
    10 2521 A9 00		        lda #0
    11 2523 85 90		        sta zfr
    12 2525 8D BC 4B		        sta fps_count
    13 2528 8D C8 57		        sta level_complete
    14 252B 85 D6		        sta cam_tx
    15 252D 85 DB		        sta cam_tx_prev
    16 252F 85 D9		        sta cam_sub
    17 2531 85 D7		        sta cam_x_lo
    18 2533 85 D8		        sta cam_x_hi
    19 2535 85 DA		        sta scroll_redraw
    20 2537 85 DC		        sta sky_x_lo
    21 2539 85 DD		        sta sky_x_sub
    22 253B 85 DE		        sta cam_ty
    23 253D 85 DF		        sta cam_y
    24 253F 85 E1		        sta cam_y_hi
    25 2541 85 E0		        sta cam_ty_prev
    26 2543 A5 14		        lda RTCLOK3
    27 2545 8D BE 4B		        sta fps_rtclk
    28 2548 20 50 29		        jsr init_player
    29 254B 20 00 60		        jsr init_enemies
    30 254E 20 86 60		        jsr init_pickups
    31 2551 20 CC 60		        jsr init_decorations
    32 2554 20 23 61		        jsr init_doors
    33 2557 20 1B 45		        jsr init_eproj
    34 255A A9 FF		        lda #$FF
    35 255C 8D A2 59		        sta ft_prev_col
    36 255F 8D A3 59		        sta ft_prev_row
    37 2562 60			        rts
    38 				.endp
    39
    40 				; ============================================
    41 				; GET TILE AT PIXEL POSITION
    42 				; Input: gt_px/gt_px_hi, gt_py/gt_py_hi (16-bit pixel coords)
    43 				; Output: A = tile index from map
    44 				; ============================================
    45 2563			.proc get_tile_at
    46 				        ; tile_col = (gt_px_hi:gt_px) / 16
    47 2563 AD B1 25		        lda gt_px_hi
    48 2566 0A			        asl
    49 2567 0A			        asl
    50 2568 0A			        asl
    51 2569 0A			        asl                 ; hi * 16 (0 or 16)
    52 256A 8D B4 25		        sta gt_col
    53 256D AE B0 25		        ldx gt_px
    54 2570 BD C6 A2		        lda pix_to_tile,x  ; lo / 16
    55 2573 18			        clc
    56 2574 6D B4 25		        adc gt_col
    57 2577 8D B4 25		        sta gt_col
    58 				        ; tile_row = (gt_py_hi:gt_py) / 16
    59 257A AD B3 25		        lda gt_py_hi
    60 257D 0A			        asl
    61 257E 0A			        asl
    62 257F 0A			        asl
    63 2580 0A			        asl                 ; hi * 16
    64 2581 8D B5 25		        sta gt_row
    65 2584 AE B2 25		        ldx gt_py
    66 2587 BD C6 A2		        lda pix_to_tile,x  ; lo / 16
    67 258A 18			        clc
    68 258B 6D B5 25		        adc gt_row
    69 258E 8D B5 25		        sta gt_row
    70 				        ; bounds check
    71 2591 AD B4 25		        lda gt_col
    72 2594 C9 40		        cmp #MAP_W
    73 2596 B0 15		        bcs ?oob
    74 2598 8D 35 47		        sta r_col
    75 259B AD B5 25		        lda gt_row
    76 259E C9 20		        cmp #MAP_H
    77 25A0 B0 0B		        bcs ?oob
    78 25A2 8D 34 47		        sta r_row
    79 25A5 20 B6 25		        jsr calc_map_ptr
    80 25A8 A0 00		        ldy #0
    81 25AA B1 D4		        lda (ztptr),y
    82 25AC 60			        rts
    83 25AD A9 01		?oob    lda #1             ; out of bounds = solid (wall)
    84 25AF 60			        rts
    85 				.endp
    86 25B0 00			gt_px   dta 0
    87 25B1 00			gt_px_hi dta 0
    88 25B2 00			gt_py   dta 0
    89 25B3 00			gt_py_hi dta 0
    90 25B4 00			gt_col  dta 0
    91 25B5 00			gt_row  dta 0
    92
    93 				; ============================================
    94 				; CALC MAP POINTER
    95 				; Input: r_row, r_col (tile coords)
    96 				; Output: ztptr = address in map_data
    97 				; ============================================
    98 25B6			.proc calc_map_ptr
    99 25B6 A9 9E		        lda #BANK_EN+BANK_MAP
   100 25B8 8D 5F D6		        sta VBXE_BANK_SEL
   101 25BB AC 34 47		        ldy r_row
   102 25BE B9 C6 A3		        lda map_row_lo,y
   103 25C1 18			        clc
   104 25C2 6D 35 47		        adc r_col
   105 25C5 85 D4		        sta ztptr
   106 25C7 B9 E6 A3		        lda map_row_hi,y
   107 25CA 69 00		        adc #0
   108 25CC 85 D5		        sta ztptr+1
   109 25CE 60			        rts
   110 				.endp
   111
   112 				; ============================================
   113 				; TILE COL HELPERS (shared by LOS, can_hear, pickups, doors)
   114 				; ============================================
   115
   116 				; Enemy tile col from en_x[X], enxhi[X] → A
   117 				; (does NOT add +8 center offset — caller does if needed)
   118 25CF			.proc get_enemy_tile_col
   119 25CF BD 37 3A		        lda en_x,x
   120 25D2 4A			        lsr
   121 25D3 4A			        lsr
   122 25D4 4A			        lsr
   123 25D5 4A			        lsr
   124 25D6 8D E4 25		        sta gt_tmp
   125 25D9 BD 3D 3A		        lda enxhi,x
   126 25DC 0A			        asl
   127 25DD 0A			        asl
   128 25DE 0A			        asl
   129 25DF 0A			        asl
   130 25E0 0D E4 25		        ora gt_tmp
   131 25E3 60			        rts
   132 25E4 00			gt_tmp  dta 0
   133 				.endp
   134
   135 				; Player tile col from zpx, zpx_hi → A
   136 25E5			.proc get_player_tile_col
   137 25E5 A5 91		        lda zpx
   138 25E7 4A			        lsr
   139 25E8 4A			        lsr
   140 25E9 4A			        lsr
   141 25EA 4A			        lsr
   142 25EB 8D F8 25		        sta gt_tmp
   143 25EE A5 9C		        lda zpx_hi
   144 25F0 0A			        asl
   145 25F1 0A			        asl
   146 25F2 0A			        asl
   147 25F3 0A			        asl
   148 25F4 0D F8 25		        ora gt_tmp
   149 25F7 60			        rts
   150 25F8 00			gt_tmp  dta 0
   151 				.endp
   152
   153 				; map_row_lo/hi + row_x20 moved to $A000 segment in main.asm
   154
   155 				; ============================================
   156 				; TILE SOLID TABLE
   157 				; 0=passable, 1=solid
   158 				; Index: 0=empty 1=wall 2=platform 3=ceil 4=door 5=sky 6=darkbg(wall) 7=tech 8=metal 9=support 10=stone
   159 				; ============================================
   160 				; tile_solid, tile_oneway, tile_halfh moved to $A000 segment (main.asm)
   161
   162 				; ============================================
   163 				; CHECK IF TILE AT (gt_px, gt_py) IS SOLID
   164 				; Output: Z=0 if solid, Z=1 if passable
   165 				; ============================================
   166 25F9			.proc check_solid
   167 25F9 20 63 25		        jsr get_tile_at
   168 25FC AA			        tax
   169 25FD BD 9B A5		        lda tile_solid,x
   170 2600 60			        rts                 ; A!=0 means solid
   171 				.endp
   172
   173 				; ============================================
   174 				; CHECK IF TILE AT (gt_px, gt_py) IS SOLID OR PLATFORM
   175 				; For use during falling - platforms count as solid
   176 				; Output: Z=0 if solid/platform, Z=1 if passable
   177 				; ============================================
   178 2601			.proc check_solid_or_platform
   179 2601 20 63 25		        jsr get_tile_at
   180 2604 AA			        tax
   181 2605 BD B9 A5		        lda tile_oneway,x
   182 2608 D0 04		        bne ?plat           ; one-way tile = solid when falling
   183 260A BD 9B A5		        lda tile_solid,x
   184 260D 60			        rts
   185 260E A9 01		?plat   lda #1              ; one-way = solid when falling
   186 2610 60			        rts
   187 				.endp
   188
   189 				; sound_update is in sound.asm
   314 2611			        icl 'weapons.asm'
Source: weapons.asm
     1 				;==============================================
     2 				; DOOM2D - Weapon system
     3 				; weapons.asm
     4 				;
     5 				; 8 weapons like DOOM 1 (1993)
     6 				; Keyboard 1-7 to switch
     7 				;==============================================
     8
     9 = 02FC			CH              = $02FC         ; OS keyboard shadow register ($FF = no key)
    10
    11 				; Weapon property tables (indexed by weapon 0-6)
    12 				; Key 1 = fist (upgrades to chainsaw when picked up)
    13 2611			weap_dmg
    14 2611 02			        dta 2                   ; WP_FIST (3 with chainsaw)
    15 2612 01			        dta 1                   ; WP_PISTOL
    16 2613 05			        dta 5                   ; WP_SHOTGUN (DOOM: 7x pistol)
    17 2614 01			        dta 1                   ; WP_CHAINGUN (fast fire)
    18 2615 0A			        dta 10                  ; WP_ROCKET (+splash)
    19 2616 02			        dta 2                   ; WP_PLASMA (very fast)
    20 2617 14			        dta 20                  ; WP_BFG (one-shots baron)
    21
    22 2618			weap_cooldown
    23 2618 0C			        dta 12                  ; WP_FIST (6 with chainsaw)
    24 2619 0F			        dta 15                  ; WP_PISTOL
    25 261A 1E			        dta 30                  ; WP_SHOTGUN
    26 261B 04			        dta 4                   ; WP_CHAINGUN
    27 261C 23			        dta 35                  ; WP_ROCKET
    28 261D 03			        dta 3                   ; WP_PLASMA
    29 261E 3C			        dta 60                  ; WP_BFG
    30
    31 				; Ammo type: 0=bullets, 1=shells, 2=rockets, 3=cells, $FF=melee
    32 261F			weap_ammotype
    33 261F FF			        dta AMMO_NONE           ; WP_FIST
    34 2620 00			        dta 0                   ; WP_PISTOL   (bullets)
    35 2621 01			        dta 1                   ; WP_SHOTGUN  (shells)
    36 2622 00			        dta 0                   ; WP_CHAINGUN (bullets)
    37 2623 02			        dta 2                   ; WP_ROCKET   (rockets)
    38 2624 03			        dta 3                   ; WP_PLASMA   (cells)
    39 2625 03			        dta 3                   ; WP_BFG      (cells)
    40
    41 2626			weap_ammocost
    42 2626 00			        dta 0                   ; WP_FIST
    43 2627 01			        dta 1                   ; WP_PISTOL
    44 2628 01			        dta 1                   ; WP_SHOTGUN
    45 2629 01			        dta 1                   ; WP_CHAINGUN
    46 262A 01			        dta 1                   ; WP_ROCKET
    47 262B 01			        dta 1                   ; WP_PLASMA
    48 262C 28			        dta 40                  ; WP_BFG (DOOM: 40 cells/shot)
    49
    50 				; Melee range (0 = projectile weapon)
    51 262D			weap_range
    52 262D 10			        dta 16                  ; WP_FIST
    53 262E 00			        dta 0                   ; WP_PISTOL
    54 262F 00			        dta 0                   ; WP_SHOTGUN
    55 2630 00			        dta 0                   ; WP_CHAINGUN
    56 2631 00			        dta 0                   ; WP_ROCKET
    57 2632 00			        dta 0                   ; WP_PLASMA
    58 2633 00			        dta 0                   ; WP_BFG
    59
    60 				; Ammo ZP address table (indexed by ammo type 0-3)
    61 2634			ammo_addr
    62 2634 99			        dta zpammo              ; 0 = bullets
    63 2635 A3			        dta zpshells            ; 1 = shells
    64 2636 A4			        dta zprockets           ; 2 = rockets
    65 2637 A5			        dta zpcells             ; 3 = cells
    66
    67 				; Weapon -> projectile sprite (base frame, animates +1)
    68 2638			weap_proj_spr
    69 2638 00			        dta 0                   ; WP_FIST (melee, unused)
    70 2639 2A			        dta 42                  ; WP_PISTOL -> SPR_PROJ1
    71 263A 2A			        dta 42                  ; WP_SHOTGUN -> SPR_PROJ1
    72 263B 2A			        dta 42                  ; WP_CHAINGUN -> SPR_PROJ1
    73 263C 68			        dta 104                 ; WP_ROCKET -> SPR_ROCKET_PROJ
    74 263D 81			        dta SPR_PLASMA_PROJ1    ; WP_PLASMA
    75 263E 83			        dta SPR_BFG_PROJ1       ; WP_BFG
    76
    77 				; Weapon -> pickup sprite index for HUD display
    78 263F			weap_hud_spr
    79 263F 00			        dta 0                   ; WP_FIST (no pickup sprite, use 0)
    80 2640 2F			        dta 47                  ; WP_PISTOL -> SPR_AMMO_CLIP
    81 2641 36			        dta 54                  ; WP_SHOTGUN -> SPR_SHOTGUNPK
    82 2642 66			        dta 102                 ; WP_CHAINGUN -> SPR_CHAINGUNPK
    83 2643 67			        dta 103                 ; WP_ROCKET -> SPR_ROCKETPK
    84 2644 7F			        dta SPR_PLASMAGUNPK     ; WP_PLASMA
    85 2645 85			        dta SPR_BFGPK           ; WP_BFG
    86
    87 				; Weapon -> sound effect ($FF = no sound yet)
    88 2646			weap_sfx
    89 2646 09			        dta SFX_PUNCH           ; WP_FIST
    90 2647 00			        dta SFX_PISTOL          ; WP_PISTOL
    91 2648 07			        dta SFX_SHOTGUN         ; WP_SHOTGUN
    92 2649 00			        dta SFX_PISTOL          ; WP_CHAINGUN (same as pistol)
    93 264A 02			        dta SFX_ROCKET          ; WP_ROCKET
    94 264B 12			        dta SFX_PLASMA          ; WP_PLASMA
    95 264C 13			        dta SFX_BFG             ; WP_BFG
    96
    97 				; Keyboard scancode -> weapon mapping
    98 				; Atari scancodes: 1=$1F, 2=$1E, 3=$1A, 4=$18, 5=$1D, 6=$1B, 7=$33
    99 264D			key_to_weap_sc
   100 264D 1F 1E 1A 18 1D 1B +         dta $1F, $1E, $1A, $18, $1D, $1B, $33
   101 2654			key_to_weap_id
   102 2654 00 01 02 03 04 05 +         dta 0, 1, 2, 3, 4, 5, 6
   103
   104 				; ============================================
   105 				; CHECK WEAPON SWITCH (called from read_input)
   106 				; Uses OS shadow CH ($02FC), cleared to $FF after read
   107 				; ============================================
   108 265B			.proc check_weapon_switch
   109 265B AD FC 02		        lda CH
   110 265E C9 FF		        cmp #$FF
   111 2660 F0 3A		        beq ?done               ; no key pressed
   112 				        ; F key ($38) = toggle FPS counter
   113 2662 C9 38		        cmp #$38
   114 2664 D0 15		        bne ?not_f
   115 2666 A9 FF		        lda #$FF
   116 2668 8D FC 02		        sta CH
   117 266B AD BB 4B		        lda fps_show
   118 266E 49 01		        eor #1
   119 2670 8D BB 4B		        sta fps_show
   120 2673 D0 27		        bne ?done               ; turned on, no clear needed
   121 2675 A9 02		        lda #2
   122 2677 8D BF 4B		        sta fps_clear           ; clear both buffers
   123 267A 60			        rts
   124 267B			?not_f  ; SPACE ($21) = USE key (open doors)
   125 267B C9 21		        cmp #$21
   126 267D D0 08		        bne ?not_space
   127 267F A9 FF		        lda #$FF
   128 2681 8D FC 02		        sta CH
   129 2684 4C DA 55		        jmp try_open_door
   130 2687			?not_space
   131 2687 8D AB 26		        sta wk_key              ; save scancode
   132 268A A9 FF		        lda #$FF
   133 268C 8D FC 02		        sta CH                  ; acknowledge key (clear for next press)
   134 268F AD AB 26		        lda wk_key
   135 				        ; Check against weapon keys 1-7
   136 2692 A2 06		        ldx #6
   137 2694 DD 4D 26		?lp     cmp key_to_weap_sc,x
   138 2697 F0 04		        beq ?found
   139 2699 CA			        dex
   140 269A 10 F8		        bpl ?lp
   141 269C 60			?done   rts
   142 269D			?found  ; X = index, get weapon id
   143 269D BD 54 26		        lda key_to_weap_id,x
   144 26A0 AA			        tax
   145 				        ; Check if player owns this weapon (bit X in zpweap)
   146 26A1 A5 A2		        lda zpweap
   147 26A3 3D 27 27		        and weap_bitmask,x
   148 26A6 F0 F4		        beq ?done               ; don't own it
   149 26A8 86 A6		        stx zpwcur
   150 26AA 60			        rts
   151 26AB 00			wk_key  dta 0
   152 				.endp
   153
   154 				; ============================================
   155 				; MELEE ATTACK - check proximity to enemies
   156 				; ============================================
   157 26AC			.proc melee_attack
   158 26AC A6 A6		        ldx zpwcur
   159 26AE BD 11 26		        lda weap_dmg,x
   160 26B1 8D 25 27		        sta ml_dmg
   161 26B4 BD 2D 26		        lda weap_range,x
   162 26B7 8D 26 27		        sta ml_range
   163 26BA A2 00		        ldx #0
   164 26BC BD 55 3A		?lp     lda en_act,x
   165 26BF C9 01		        cmp #1
   166 26C1 D0 59		        bne ?nx
   167 				        ; Check same hi byte
   168 26C3 BD 3D 3A		        lda enxhi,x
   169 26C6 C5 9C		        cmp zpx_hi
   170 26C8 D0 52		        bne ?nx
   171 				        ; Check X distance
   172 26CA A5 91		        lda zpx
   173 26CC 38			        sec
   174 26CD FD 37 3A		        sbc en_x,x
   175 26D0 10 05		        bpl ?xa
   176 26D2 49 FF		        eor #$FF
   177 26D4 18			        clc
   178 26D5 69 01		        adc #1
   179 26D7 CD 26 27		?xa     cmp ml_range
   180 26DA B0 40		        bcs ?nx
   181 				        ; Check Y distance (hi byte first)
   182 26DC A5 9D		        lda zpy_hi
   183 26DE DD 49 3A		        cmp en_yhi,x
   184 26E1 D0 39		        bne ?nx                 ; different hi bytes = too far
   185 26E3 A5 92		        lda zpy
   186 26E5 38			        sec
   187 26E6 FD 43 3A		        sbc en_y,x
   188 26E9 10 05		        bpl ?ya
   189 26EB 49 FF		        eor #$FF
   190 26ED 18			        clc
   191 26EE 69 01		        adc #1
   192 26F0 C9 14		?ya     cmp #20                 ; vertical range
   193 26F2 B0 28		        bcs ?nx
   194 				        ; Hit! Apply melee damage
   195 26F4 BD 5B 3A		        lda en_hp,x
   196 26F7 38			        sec
   197 26F8 ED 25 27		        sbc ml_dmg
   198 26FB B0 02		        bcs ?hok
   199 26FD A9 00		        lda #0
   200 26FF 9D 5B 3A		?hok    sta en_hp,x
   201 2702 A9 08		        lda #8
   202 2704 9D FD 44		        sta en_pain_tmr,x
   203 2707 BD 5B 3A		        lda en_hp,x
   204 270A D0 0D		        bne ?alive
   205 270C A9 02		        lda #2
   206 270E 9D 55 3A		        sta en_act,x           ; dying
   207 2711 A9 14		        lda #20
   208 2713 9D 79 3A		        sta en_dtimer,x
   209 				        ; Death sound by enemy type
   210 2716 20 91 5B		        jsr play_enemy_death
   211 2719			?alive  ; Face enemy
   212 2719 4C 51 41		        jmp alert_enemies_sound
   213 271C E8			?nx     inx
   214 271D E0 06		        cpx #MAX_ENEMIES
   215 271F F0 03		        beq ?done
   216 2721 4C BC 26		        jmp ?lp
   217 2724 60			?done   rts
   218 2725 00			ml_dmg  dta 0
   219 2726 00			ml_range dta 0
   220 				.endp
   221
   222 				; Bitmask table for weapon ownership
   223 2727			weap_bitmask
   224 2727 01			        dta $01                 ; bit0 = fist/chainsaw
   225 2728 02			        dta $02                 ; bit1 = pistol
   226 2729 04			        dta $04                 ; bit2 = shotgun
   227 272A 08			        dta $08                 ; bit3 = chaingun
   228 272B 10			        dta $10                 ; bit4 = rocket
   229 272C 20			        dta $20                 ; bit5 = plasma
   230 272D 40			        dta $40                 ; bit6 = BFG
   231
   232 				; ============================================
   233 				; GET CURRENT AMMO COUNT
   234 				; Returns: A = current ammo for active weapon
   235 				;          (99 for melee = always can fire)
   236 				; ============================================
   237 272E			.proc get_cur_ammo
   238 272E A6 A6		        ldx zpwcur
   239 2730 BD 1F 26		        lda weap_ammotype,x
   240 2733 C9 FF		        cmp #AMMO_NONE
   241 2735 F0 18		        beq ?melee
   242 2737 C9 01		        cmp #1
   243 2739 F0 0B		        beq ?shells
   244 273B C9 02		        cmp #2
   245 273D F0 0A		        beq ?rockets
   246 273F C9 03		        cmp #3
   247 2741 F0 09		        beq ?cells
   248 				        ; ammo type 0 = bullets
   249 2743 A5 99		        lda zpammo
   250 2745 60			        rts
   251 2746 A5 A3		?shells lda zpshells
   252 2748 60			        rts
   253 2749 A5 A4		?rockets lda zprockets
   254 274B 60			        rts
   255 274C A5 A5		?cells  lda zpcells
   256 274E 60			        rts
   257 274F A9 63		?melee  lda #99
   258 2751 60			        rts
   259 				.endp
   260
   261 				; ============================================
   262 				; DEDUCT AMMO for current weapon
   263 				; ============================================
   264 2752			.proc deduct_ammo
   265 2752 A6 A6		        ldx zpwcur
   266 2754 BD 1F 26		        lda weap_ammotype,x
   267 2757 C9 FF		        cmp #AMMO_NONE
   268 2759 F0 6E		        beq ?done               ; melee = no ammo
   269 275B BC 26 26		        ldy weap_ammocost,x     ; cost to deduct
   270 275E C9 01		        cmp #1
   271 2760 F0 15		        beq ?shells
   272 2762 C9 02		        cmp #2
   273 2764 F0 1E		        beq ?rockets
   274 2766 C9 03		        cmp #3
   275 2768 F0 27		        beq ?cells
   276 				        ; ammo type 0 = bullets
   277 276A 98			        tya
   278 276B 85 81		        sta zt2
   279 276D A5 99		        lda zpammo
   280 276F 38			        sec
   281 2770 E5 81		        sbc zt2
   282 2772 85 99		        sta zpammo
   283 2774 F0 28		        beq ?auto_switch
   284 2776 60			        rts
   285 2777 98			?shells tya
   286 2778 85 81		        sta zt2
   287 277A A5 A3		        lda zpshells
   288 277C 38			        sec
   289 277D E5 81		        sbc zt2
   290 277F 85 A3		        sta zpshells
   291 2781 F0 1B		        beq ?auto_switch
   292 2783 60			        rts
   293 2784 98			?rockets tya
   294 2785 85 81		        sta zt2
   295 2787 A5 A4		        lda zprockets
   296 2789 38			        sec
   297 278A E5 81		        sbc zt2
   298 278C 85 A4		        sta zprockets
   299 278E F0 0E		        beq ?auto_switch
   300 2790 60			        rts
   301 2791 98			?cells  tya
   302 2792 85 81		        sta zt2
   303 2794 A5 A5		        lda zpcells
   304 2796 38			        sec
   305 2797 E5 81		        sbc zt2
   306 2799 85 A5		        sta zpcells
   307 279B F0 01		        beq ?auto_switch
   308 279D 60			        rts
   309 279E			?auto_switch
   310 				        ; Ammo ran out — find best weapon with ammo
   311 				        ; Priority: chaingun, shotgun, pistol, fist
   312 279E A5 A2		        lda zpweap
   313 27A0 29 08		        and #$08                ; chaingun
   314 27A2 F0 04		        beq ?no_cg
   315 27A4 A5 99		        lda zpammo
   316 27A6 D0 13		        bne ?sw_cg
   317 27A8 A5 A2		?no_cg  lda zpweap
   318 27AA 29 04		        and #$04                ; shotgun
   319 27AC F0 04		        beq ?no_sg
   320 27AE A5 A3		        lda zpshells
   321 27B0 D0 0E		        bne ?sw_sg
   322 27B2 A5 99		?no_sg  lda zpammo
   323 27B4 D0 0F		        bne ?sw_pi              ; pistol always owned, check bullets
   324 27B6 A9 00		        lda #WP_FIST            ; fallback to fist
   325 27B8 85 A6		        sta zpwcur
   326 27BA 60			        rts
   327 27BB A9 03		?sw_cg  lda #WP_CHAINGUN
   328 27BD 85 A6		        sta zpwcur
   329 27BF 60			        rts
   330 27C0 A9 02		?sw_sg  lda #WP_SHOTGUN
   331 27C2 85 A6		        sta zpwcur
   332 27C4 60			        rts
   333 27C5 A9 01		?sw_pi  lda #WP_PISTOL
   334 27C7 85 A6		        sta zpwcur
   335 27C9 60			?done   rts
   336 				.endp
   337
   338 				; ============================================
   339 				; HITSCAN FIRE - instant hit for pistol/shotgun/chaingun
   340 				; Scans tile columns from player in firing direction,
   341 				; stops at first wall or enemy hit.
   342 				; ============================================
   343 27CA			.proc hitscan_fire
   344 				        ; Pre-compute weapon damage
   345 27CA A6 A6		        ldx zpwcur
   346 27CC BD 11 26		        lda weap_dmg,x
   347 27CF 8D 89 28		        sta hs_dmg
   348
   349 				        ; Map row pointer for gun height (16-bit)
   350 27D2 A5 92		        lda zpy
   351 27D4 38			        sec
   352 27D5 E9 0E		        sbc #14
   353 27D7 8D 86 28		        sta hs_gy
   354 27DA A5 9D		        lda zpy_hi
   355 27DC E9 00		        sbc #0                  ; propagate borrow from sbc #14
   356 27DE 0A			        asl
   357 27DF 0A			        asl
   358 27E0 0A			        asl
   359 27E1 0A			        asl                     ; hi * 16
   360 27E2 85 80		        sta zt                  ; hi contribution to tile row
   361 27E4 AD 86 28		        lda hs_gy
   362 27E7 4A			        lsr
   363 27E8 4A			        lsr
   364 27E9 4A			        lsr
   365 27EA 4A			        lsr                     ; lo / 16
   366 27EB 05 80		        ora zt
   367 27ED A8			        tay                     ; Y = tile row
   368 				        ; ztptr = map_data + row*64 (LUT)
   369 27EE B9 C6 A3		        lda map_row_lo,y
   370 27F1 85 D4		        sta ztptr
   371 27F3 B9 E6 A3		        lda map_row_hi,y
   372 27F6 85 D5		        sta ztptr+1
   373 27F8 A9 9E		        lda #BANK_EN+BANK_MAP
   374 27FA 8D 5F D6		        sta VBXE_BANK_SEL
   375
   376 				        ; Player tile column = (zpx_hi:zpx+5) / 16
   377 27FD A5 91		        lda zpx
   378 27FF 18			        clc
   379 2800 69 05		        adc #5
   380 2802 85 80		        sta zt
   381 2804 A5 9C		        lda zpx_hi
   382 2806 69 00		        adc #0
   383 2808 0A			        asl
   384 2809 0A			        asl
   385 280A 0A			        asl
   386 280B 0A			        asl
   387 280C 8D 87 28		        sta hs_col
   388 280F A5 80		        lda zt
   389 2811 4A			        lsr
   390 2812 4A			        lsr
   391 2813 4A			        lsr
   392 2814 4A			        lsr
   393 2815 0D 87 28		        ora hs_col
   394 2818 8D 87 28		        sta hs_col
   395
   396 				        ; Scan direction
   397 281B A5 95		        lda zpdir
   398 281D D0 2B		        bne ?left
   399
   400 				        ; --- Scan RIGHT ---
   401 281F AD 87 28		        lda hs_col
   402 2822 18			?rlp    clc
   403 2823 69 01		        adc #1
   404 2825 C9 40		        cmp #MAP_W
   405 2827 B0 5C		        bcs ?miss
   406 2829 8D 88 28		        sta hs_cur
   407 282C A8			        tay
   408 282D B1 D4		        lda (ztptr),y
   409 282F AA			        tax
   410 2830 BD 9B A5		        lda tile_solid,x
   411 2833 F0 0A		        beq ?r_open
   412 2835 E0 0F		        cpx #15                 ; barrel invisible solid?
   413 2837 D0 4C		        bne ?miss               ; real wall
   414 2839 20 08 29		        jsr hs_chk_barrel
   415 283C 4C 85 28		        jmp ?miss               ; barrel or wall, stop either way
   416 283F 20 8A 28		?r_open jsr hs_chk_col
   417 2842 B0 2F		        bcs ?hit
   418 2844 AD 88 28		        lda hs_cur
   419 2847 4C 22 28		        jmp ?rlp
   420
   421 				        ; --- Scan LEFT ---
   422 284A AD 87 28		?left   lda hs_col
   423 284D 38			?llp    sec
   424 284E E9 01		        sbc #1
   425 2850 90 33		        bcc ?miss               ; past column 0
   426 2852 8D 88 28		        sta hs_cur
   427 2855 A8			        tay
   428 2856 B1 D4		        lda (ztptr),y
   429 2858 AA			        tax
   430 2859 BD 9B A5		        lda tile_solid,x
   431 285C F0 0A		        beq ?l_open
   432 285E E0 0F		        cpx #15
   433 2860 D0 23		        bne ?miss               ; real wall
   434 2862 20 08 29		        jsr hs_chk_barrel
   435 2865 4C 85 28		        jmp ?miss
   436 2868 20 8A 28		?l_open jsr hs_chk_col
   437 286B B0 06		        bcs ?hit
   438 286D AD 88 28		        lda hs_cur
   439 2870 4C 4D 28		        jmp ?llp
   440
   441 2873			?hit    ; X = enemy index (from hs_chk_col)
   442 2873 20 8F 28		        jsr hs_damage_enemy
   443 				        ; Shotgun pierce: check for second enemy in same column
   444 2876 A5 A6		        lda zpwcur
   445 2878 C9 02		        cmp #WP_SHOTGUN
   446 287A D0 09		        bne ?miss
   447 				        ; Continue from next enemy index
   448 287C E8			        inx
   449 287D 20 BD 28		        jsr hs_chk_col_from
   450 2880 90 03		        bcc ?miss
   451 2882 20 8F 28		        jsr hs_damage_enemy
   452 2885 60			?miss   rts
   453
   454 2886 00			hs_gy   dta 0
   455 2887 00			hs_col  dta 0
   456 2888 00			hs_cur  dta 0
   457 2889 00			hs_dmg  dta 0
   458 				.endp
   459
   460 				; ============================================
   461 				; HS_CHK_COL - check if any enemy is in tile column hs_cur
   462 				; Returns: C=1 hit (X=enemy index), C=0 no hit
   463 				; ============================================
   464 288A			.proc hs_chk_col
   465 288A A2 00		        ldx #0
   466 288C 4C BD 28		        jmp hs_chk_col_from
   467 				.endp
   468
   469 				; ============================================
   470 				; HS_DAMAGE_ENEMY - apply hitscan damage to enemy X
   471 				; ============================================
   472 288F			.proc hs_damage_enemy
   473 288F BD 5B 3A		        lda en_hp,x
   474 2892 38			        sec
   475 2893 ED 89 28		        sbc hitscan_fire.hs_dmg
   476 2896 B0 02		        bcs ?hok
   477 2898 A9 00		        lda #0
   478 289A 9D 5B 3A		?hok    sta en_hp,x
   479 289D A9 08		        lda #8
   480 289F 9D FD 44		        sta en_pain_tmr,x
   481 28A2 BD 5B 3A		        lda en_hp,x
   482 28A5 D0 15		        bne ?done
   483 				        ; Killed!
   484 28A7 A9 02		        lda #2
   485 28A9 9D 55 3A		        sta en_act,x
   486 28AC A9 14		        lda #20
   487 28AE 9D 79 3A		        sta en_dtimer,x
   488 28B1 A9 00		        lda #0
   489 28B3 9D 85 3A		        sta en_gib,x
   490 28B6 9D 8B 3A		        sta envelx,x
   491 28B9 20 91 5B		        jsr play_enemy_death
   492 28BC 60			?done   rts
   493 				.endp
   494
   495 				; ============================================
   496 				; HS_CHK_COL_FROM - like hs_chk_col but starts from X
   497 				; Returns: C=1 hit (X=enemy), C=0 no hit
   498 				; ============================================
   499 28BD			.proc hs_chk_col_from
   500 28BD E0 06		?lp     cpx #MAX_ENEMIES
   501 28BF B0 45		        bcs ?miss
   502 28C1 BD 55 3A		        lda en_act,x
   503 28C4 C9 01		        cmp #1
   504 28C6 D0 3A		        bne ?nx
   505 				        ; Enemy tile column
   506 28C8 BD 37 3A		        lda en_x,x
   507 28CB 18			        clc
   508 28CC 69 08		        adc #8
   509 28CE 85 80		        sta zt
   510 28D0 BD 3D 3A		        lda enxhi,x
   511 28D3 69 00		        adc #0
   512 28D5 0A			        asl
   513 28D6 0A			        asl
   514 28D7 0A			        asl
   515 28D8 0A			        asl
   516 28D9 85 81		        sta zt2
   517 28DB A5 80		        lda zt
   518 28DD 4A			        lsr
   519 28DE 4A			        lsr
   520 28DF 4A			        lsr
   521 28E0 4A			        lsr
   522 28E1 05 81		        ora zt2
   523 28E3 CD 88 28		        cmp hitscan_fire.hs_cur
   524 28E6 D0 1A		        bne ?nx
   525 				        ; Same column! Check Y (hi byte first)
   526 28E8 BD 49 3A		        lda en_yhi,x
   527 28EB C5 9D		        cmp zpy_hi
   528 28ED D0 13		        bne ?nx             ; different hi bytes = too far
   529 28EF BD 43 3A		        lda en_y,x
   530 28F2 38			        sec
   531 28F3 E5 92		        sbc zpy
   532 28F5 10 05		        bpl ?yp
   533 28F7 49 FF		        eor #$FF
   534 28F9 18			        clc
   535 28FA 69 01		        adc #1
   536 28FC C9 12		?yp     cmp #18
   537 28FE B0 02		        bcs ?nx
   538 2900 38			        sec
   539 2901 60			        rts
   540 2902 E8			?nx     inx
   541 2903 4C BD 28		        jmp ?lp
   542 2906 18			?miss   clc
   543 2907 60			        rts
   544 				.endp
   545
   546 				; ============================================
   547 				; HS_CHK_BARREL - damage barrel at tile column hs_cur
   548 				; ============================================
   549 2908			.proc hs_chk_barrel
   550 2908 A2 00		        ldx #0
   551 290A BD CD 34		?lp     lda dc_act,x
   552 290D C9 01		        cmp #1
   553 290F D0 39		        bne ?nx
   554 2911 BD FD 34		        lda dc_type,x
   555 2914 C9 00		        cmp #DC_BARREL
   556 2916 D0 32		        bne ?nx
   557 				        ; Barrel tile column = dc_xhi:dc_x / 16
   558 2918 BD DD 34		        lda dc_xhi,x
   559 291B 0A			        asl
   560 291C 0A			        asl
   561 291D 0A			        asl
   562 291E 0A			        asl
   563 291F 85 80		        sta zt
   564 2921 BD D5 34		        lda dc_x,x
   565 2924 4A			        lsr
   566 2925 4A			        lsr
   567 2926 4A			        lsr
   568 2927 4A			        lsr
   569 2928 05 80		        ora zt
   570 292A CD 88 28		        cmp hitscan_fire.hs_cur
   571 292D D0 1B		        bne ?nx
   572 				        ; Same column! Damage barrel
   573 292F BD 05 35		        lda dc_hp,x
   574 2932 38			        sec
   575 2933 ED 89 28		        sbc hitscan_fire.hs_dmg
   576 2936 B0 02		        bcs ?hok
   577 2938 A9 00		        lda #0
   578 293A 9D 05 35		?hok    sta dc_hp,x
   579 293D D0 0A		        bne ?done
   580 				        ; Barrel killed → start explosion
   581 293F A9 02		        lda #2
   582 2941 9D CD 34		        sta dc_act,x
   583 2944 A9 1F		        lda #31
   584 2946 9D 0D 35		        sta dc_timer,x
   585 2949 60			?done   rts
   586 294A E8			?nx     inx
   587 294B E0 08		        cpx #MAX_DECOR
   588 294D D0 BB		        bne ?lp
   589 294F 60			        rts
   590 				.endp
   315 2950			        icl 'player.asm'
Source: player.asm
     1 				;==============================================
     2 				; DOOM2D - Player logic (input, movement, projectiles)
     3 				; player.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; INIT PLAYER
     8 				; ============================================
     9 2950			.proc init_player
    10 2950 A9 00		        lda #0
    11 2952 85 95		        sta zpdir
    12 2954 85 96		        sta zpst
    13 2956 85 97		        sta zpan
    14 2958 85 93		        sta zpvx
    15 295A 85 94		        sta zpvy
    16 295C 85 9A		        sta zpgnd
    17 295E 85 9B		        sta zpcoy
    18 2960 85 9C		        sta zpx_hi
    19 2962 85 9D		        sta zpy_hi
    20 2964 85 C3		        sta zpwcool
    21 2966 85 99		        sta zpammo
    22 2968 85 A3		        sta zpshells
    23 296A 85 A4		        sta zprockets
    24 296C 85 A5		        sta zpcells
    25 296E 85 A0		        sta zparmor
    26 2970 85 A1		        sta zpkeys
    27 2972 A9 30		        lda #SPAWN_X
    28 2974 85 91		        sta zpx
    29 2976 A9 90		        lda #<SPAWN_Y
    30 2978 85 92		        sta zpy
    31 297A A9 00		        lda #>SPAWN_Y
    32 297C 85 9D		        sta zpy_hi
    33 297E A9 64		        lda #100
    34 2980 85 98		        sta zphp
    35 				        ; Start with fist + pistol + 50 bullets (like DOOM)
    36 2982 A9 03		        lda #$03                ; bit0=fist, bit1=pistol
    37 2984 85 A2		        sta zpweap
    38 2986 A9 01		        lda #WP_PISTOL
    39 2988 85 A6		        sta zpwcur
    40 298A A9 32		        lda #50
    41 298C 85 99		        sta zpammo
    42 298E A9 00		        lda #0
    43 2990 8D 5E 2E		        sta pl_pain_timer
    44 				        ; Clear projectiles
    45 2993 A2 03		        ldx #MAX_PROJ-1
    46 2995 9D 1A 2D		?cp     sta proj_a,x
    47 2998 CA			        dex
    48 2999 10 FA		        bpl ?cp
    49 299B 60			        rts
    50 				.endp
    51
    52 				; ============================================
    53 				; INPUT
    54 				; ============================================
    55 299C			.proc read_input
    56 299C A5 A8		        lda zjoy
    57 299E 85 AA		        sta zjoyp
    58 29A0 A5 A9		        lda ztrig
    59 29A2 85 AB		        sta ztrigp
    60 29A4 AD 78 02		        lda $0278           ; STICK0 (OS shadow, updated by VBI)
    61 29A7 49 FF		        eor #$FF
    62 29A9 29 0F		        and #$0F
    63 29AB 85 A8		        sta zjoy
    64 29AD AD 84 02		        lda $0284           ; STRIG0 (OS shadow)
    65 29B0 49 01		        eor #$01
    66 29B2 85 A9		        sta ztrig
    67 29B4 4C 5B 26		        jmp check_weapon_switch
    68 				.endp
    69
    70 				; ============================================
    71 				; PLAYER UPDATE (with tile collision)
    72 				; ============================================
    73 29B7			.proc player_update
    74 				        ; --- Pain timer ---
    75 29B7 AD 5E 2E		        lda pl_pain_timer
    76 29BA F0 03		        beq ?no_pain_dec
    77 29BC CE 5E 2E		        dec pl_pain_timer
    78 29BF			?no_pain_dec
    79 				        ; --- Coyote timer ---
    80 29BF A5 9A		        lda zpgnd
    81 29C1 F0 07		        beq ?coy_dec
    82 29C3 A9 03		        lda #COYOTE
    83 29C5 85 9B		        sta zpcoy
    84 29C7 4C D0 29		        jmp ?horiz
    85 29CA			?coy_dec
    86 29CA A5 9B		        lda zpcoy
    87 29CC F0 02		        beq ?horiz
    88 29CE C6 9B		        dec zpcoy
    89
    90 29D0			?horiz
    91 				        ; --- Horizontal acceleration ---
    92 29D0 A5 A8		        lda zjoy
    93 29D2 29 04		        and #J_LEFT
    94 29D4 F0 19		        beq ?not_l
    95
    96 				        ; Accelerate left (air = half rate)
    97 29D6 A5 9A		        lda zpgnd
    98 29D8 D0 06		        bne ?al_do
    99 29DA A5 90		        lda zfr
   100 29DC 29 01		        and #1
   101 29DE D0 08		        bne ?dir_l          ; skip accel every other frame in air
   102 29E0 A5 93		?al_do  lda zpvx
   103 29E2 C9 FE		        cmp #256-PL_MAXSPD  ; already at -3?
   104 29E4 F0 02		        beq ?dir_l
   105 29E6 C6 93		        dec zpvx
   106 29E8 A9 01		?dir_l  lda #1
   107 29EA 85 95		        sta zpdir
   108 29EC 4C 2C 2A		        jmp ?apply_vx
   109
   110 29EF A5 A8		?not_l  lda zjoy
   111 29F1 29 08		        and #J_RIGHT
   112 29F3 F0 19		        beq ?no_h
   113
   114 				        ; Accelerate right (air = half rate)
   115 29F5 A5 9A		        lda zpgnd
   116 29F7 D0 06		        bne ?ar_do
   117 29F9 A5 90		        lda zfr
   118 29FB 29 01		        and #1
   119 29FD D0 08		        bne ?dir_r
   120 29FF A5 93		?ar_do  lda zpvx
   121 2A01 C9 02		        cmp #PL_MAXSPD      ; already at +3?
   122 2A03 F0 02		        beq ?dir_r
   123 2A05 E6 93		        inc zpvx
   124 2A07 A9 00		?dir_r  lda #0
   125 2A09 85 95		        sta zpdir
   126 2A0B 4C 2C 2A		        jmp ?apply_vx
   127
   128 2A0E			?no_h   ; No input: decelerate toward 0
   129 2A0E A5 93		        lda zpvx
   130 2A10 F0 0C		        beq ?stopped
   131 2A12 30 05		        bmi ?dec_n
   132 2A14 C6 93		        dec zpvx
   133 2A16 4C 2C 2A		        jmp ?apply_vx
   134 2A19 E6 93		?dec_n  inc zpvx
   135 2A1B 4C 2C 2A		        jmp ?apply_vx
   136 2A1E			?stopped
   137 2A1E A5 9A		        lda zpgnd
   138 2A20 D0 03		        bne ?st2
   139 2A22 4C 3C 2B		        jmp ?hd
   140 2A25 A9 00		?st2    lda #0
   141 2A27 85 96		        sta zpst
   142 2A29 4C 3C 2B		        jmp ?hd
   143
   144 2A2C			?apply_vx
   145 				        ; --- Apply horizontal velocity with collision ---
   146 2A2C A5 93		        lda zpvx
   147 2A2E D0 03		        bne ?vx_go
   148 2A30 4C 15 2B		        jmp ?set_st
   149 2A33 30 6B		?vx_go  bmi ?vx_l
   150
   151 				        ; Moving right: collision check (16-bit X)
   152 2A35 A5 91		        lda zpx
   153 2A37 18			        clc
   154 2A38 65 93		        adc zpvx
   155 2A3A 85 80		        sta zt              ; new X low
   156 2A3C A5 9C		        lda zpx_hi
   157 2A3E 69 00		        adc #0
   158 2A40 85 81		        sta zt2             ; new X high
   159 				        ; Check tile at (newX + PL_W, y-14)
   160 2A42 A5 80		        lda zt
   161 2A44 18			        clc
   162 2A45 69 0A		        adc #PL_W
   163 2A47 8D B0 25		        sta gt_px
   164 2A4A A5 81		        lda zt2
   165 2A4C 69 00		        adc #0
   166 2A4E 8D B1 25		        sta gt_px_hi
   167 2A51 A5 92		        lda zpy
   168 2A53 38			        sec
   169 2A54 E9 0E		        sbc #14
   170 2A56 8D B2 25		        sta gt_py
   171 2A59 A5 9D		        lda zpy_hi
   172 2A5B E9 00		        sbc #0
   173 2A5D 8D B3 25		        sta gt_py_hi
   174 2A60 20 F9 25		        jsr check_solid
   175 2A63 F0 30		        beq ?vx_rok
   176 				        ; Blocked: try 1px
   177 2A65 A5 91		        lda zpx
   178 2A67 18			        clc
   179 2A68 69 01		        adc #1
   180 2A6A 85 80		        sta zt
   181 2A6C A5 9C		        lda zpx_hi
   182 2A6E 69 00		        adc #0
   183 2A70 85 81		        sta zt2
   184 2A72 A5 80		        lda zt
   185 2A74 18			        clc
   186 2A75 69 0A		        adc #PL_W
   187 2A77 8D B0 25		        sta gt_px
   188 2A7A A5 81		        lda zt2
   189 2A7C 69 00		        adc #0
   190 2A7E 8D B1 25		        sta gt_px_hi
   191 2A81 A5 92		        lda zpy
   192 2A83 38			        sec
   193 2A84 E9 0E		        sbc #14
   194 2A86 8D B2 25		        sta gt_py
   195 2A89 A5 9D		        lda zpy_hi
   196 2A8B E9 00		        sbc #0
   197 2A8D 8D B3 25		        sta gt_py_hi
   198 2A90 20 F9 25		        jsr check_solid
   199 2A93 D0 7C		        bne ?vx_stop
   200 2A95 A5 80		?vx_rok lda zt
   201 2A97 85 91		        sta zpx
   202 2A99 A5 81		        lda zt2
   203 2A9B 85 9C		        sta zpx_hi
   204 2A9D 4C 15 2B		        jmp ?set_st
   205
   206 2AA0			?vx_l   ; Moving left: collision check
   207 2AA0 A5 91		        lda zpx
   208 2AA2 18			        clc
   209 2AA3 65 93		        adc zpvx            ; add negative = subtract
   210 2AA5 85 80		        sta zt
   211 2AA7 A5 9C		        lda zpx_hi
   212 2AA9 69 FF		        adc #$FF            ; add -1 if borrow (sign extend)
   213 2AAB 85 81		        sta zt2
   214 2AAD 30 23		        bmi ?vx_l1          ; went negative = past left edge
   215 				        ; Check tile at (newX + 3, y-14)
   216 2AAF A5 80		        lda zt
   217 2AB1 18			        clc
   218 2AB2 69 03		        adc #3
   219 2AB4 8D B0 25		        sta gt_px
   220 2AB7 A5 81		        lda zt2
   221 2AB9 69 00		        adc #0
   222 2ABB 8D B1 25		        sta gt_px_hi
   223 2ABE A5 92		        lda zpy
   224 2AC0 38			        sec
   225 2AC1 E9 0E		        sbc #14
   226 2AC3 8D B2 25		        sta gt_py
   227 2AC6 A5 9D		        lda zpy_hi
   228 2AC8 E9 00		        sbc #0
   229 2ACA 8D B3 25		        sta gt_py_hi
   230 2ACD 20 F9 25		        jsr check_solid
   231 2AD0 F0 34		        beq ?vx_lok
   232 				        ; Blocked: try 1px
   233 2AD2 A5 91		?vx_l1  lda zpx
   234 2AD4 38			        sec
   235 2AD5 E9 01		        sbc #1
   236 2AD7 85 80		        sta zt
   237 2AD9 A5 9C		        lda zpx_hi
   238 2ADB E9 00		        sbc #0
   239 2ADD 85 81		        sta zt2
   240 2ADF 30 30		        bmi ?vx_stop        ; past left edge
   241 2AE1 A5 80		        lda zt
   242 2AE3 18			        clc
   243 2AE4 69 03		        adc #3
   244 2AE6 8D B0 25		        sta gt_px
   245 2AE9 A5 81		        lda zt2
   246 2AEB 69 00		        adc #0
   247 2AED 8D B1 25		        sta gt_px_hi
   248 2AF0 A5 92		        lda zpy
   249 2AF2 38			        sec
   250 2AF3 E9 0E		        sbc #14
   251 2AF5 8D B2 25		        sta gt_py
   252 2AF8 A5 9D		        lda zpy_hi
   253 2AFA E9 00		        sbc #0
   254 2AFC 8D B3 25		        sta gt_py_hi
   255 2AFF 20 F9 25		        jsr check_solid
   256 2B02 D0 0D		        bne ?vx_stop
   257 2B04 B0 0B		        bcs ?vx_stop
   258 2B06 A5 80		?vx_lok lda zt
   259 2B08 85 91		        sta zpx
   260 2B0A A5 81		        lda zt2
   261 2B0C 85 9C		        sta zpx_hi
   262 2B0E 4C 15 2B		        jmp ?set_st
   263
   264 2B11			?vx_stop
   265 2B11 A9 00		        lda #0
   266 2B13 85 93		        sta zpvx
   267
   268 2B15			?set_st ; Clamp player X to map bounds (0 to MAP_W*16-16)
   269 2B15 A5 9C		        lda zpx_hi
   270 2B17 30 13		        bmi ?clamp_l           ; xhi < 0 → past left edge
   271 2B19 C9 04		        cmp #(MAP_W*16/256)
   272 2B1B 90 17		        bcc ?xok
   273 				        ; Past right edge
   274 2B1D A9 F0		        lda #<(MAP_W*16-16)
   275 2B1F 85 91		        sta zpx
   276 2B21 A9 03		        lda #>(MAP_W*16-16)
   277 2B23 85 9C		        sta zpx_hi
   278 2B25 A9 00		        lda #0
   279 2B27 85 93		        sta zpvx
   280 2B29 4C 34 2B		        jmp ?xok
   281 2B2C			?clamp_l
   282 2B2C A9 00		        lda #0
   283 2B2E 85 91		        sta zpx
   284 2B30 85 9C		        sta zpx_hi
   285 2B32 85 93		        sta zpvx
   286 2B34			?xok    ; Update animation state
   287 2B34 A5 93		        lda zpvx
   288 2B36 F0 04		        beq ?hd
   289 2B38 A9 01		        lda #1
   290 2B3A 85 96		        sta zpst
   291
   292 2B3C			?hd
   293 				        ; --- Jump (with coyote time) ---
   294 2B3C A5 A8		        lda zjoy
   295 2B3E 29 01		        and #J_UP
   296 2B40 F0 19		        beq ?noj
   297 2B42 A5 9A		        lda zpgnd
   298 2B44 D0 04		        bne ?do_j
   299 2B46 A5 9B		        lda zpcoy
   300 2B48 F0 11		        beq ?noj            ; no ground, no coyote
   301 2B4A A9 00		?do_j   lda #0
   302 2B4C 38			        sec
   303 2B4D E9 08		        sbc #JUMPF
   304 2B4F 85 94		        sta zpvy
   305 2B51 A9 00		        lda #0
   306 2B53 85 9A		        sta zpgnd
   307 2B55 85 9B		        sta zpcoy           ; consume coyote
   308 2B57 A9 02		        lda #2
   309 2B59 85 96		        sta zpst
   310 				        ; sound disabled (will add custom sounds later)
   311 2B5B			?noj
   312 				        ; --- Variable jump height ---
   313 2B5B A5 94		        lda zpvy
   314 2B5D 10 16		        bpl ?noj2
   315 2B5F A5 AA		        lda zjoyp
   316 2B61 29 01		        and #J_UP
   317 2B63 F0 10		        beq ?noj2           ; wasn't holding UP
   318 2B65 A5 A8		        lda zjoy
   319 2B67 29 01		        and #J_UP
   320 2B69 D0 0A		        bne ?noj2           ; still holding UP
   321 				        ; UP just released during ascent: cut upward velocity
   322 2B6B A5 94		        lda zpvy
   323 2B6D C9 FE		        cmp #$FE            ; already -2 or -1?
   324 2B6F B0 04		        bcs ?noj2
   325 2B71 A9 FE		        lda #$FE            ; reduce to gentle coast
   326 2B73 85 94		        sta zpvy
   327 2B75			?noj2
   328 				        ; --- Gravity ---
   329 2B75 A5 9A		        lda zpgnd
   330 2B77 D0 0F		        bne ?nog
   331 2B79 A5 94		        lda zpvy
   332 2B7B 18			        clc
   333 2B7C 69 01		        adc #GRAV
   334 2B7E 30 06		        bmi ?gok            ; still ascending (negative) = skip cap
   335 2B80 C9 03		        cmp #MAXFALL
   336 2B82 90 02		        bcc ?gok
   337 2B84 A9 03		        lda #MAXFALL
   338 2B86 85 94		?gok    sta zpvy
   339 2B88			?nog
   340 				        ; --- Apply vel Y + vertical collision (16-bit) ---
   341 2B88 A5 92		        lda zpy
   342 2B8A 18			        clc
   343 2B8B 65 94		        adc zpvy
   344 2B8D 85 92		        sta zpy
   345 2B8F A5 9D		        lda zpy_hi
   346 2B91 A6 94		        ldx zpvy
   347 2B93 10 05		        bpl ?vypos
   348 2B95 69 FF		        adc #$FF        ; zpvy negative: sign extend $FF + carry
   349 2B97 4C 9C 2B		        jmp ?vydone
   350 2B9A 69 00		?vypos  adc #0          ; zpvy positive: $00 + carry
   351 2B9C 85 9D		?vydone sta zpy_hi
   352
   353 				        ; Check if falling (zpvy >= 0): floor collision
   354 2B9E A5 94		        lda zpvy
   355 2BA0 10 11		        bpl ?chk_floor
   356 				        ; Ascending: check if Y went negative (high byte bit 7 set)
   357 2BA2 A5 9D		        lda zpy_hi
   358 2BA4 10 5F		        bpl ?chkup              ; high byte positive → no underflow, check ceiling
   359 				        ; Underflow: Y wrapped negative, clamp to top
   360 2BA6 A9 01		        lda #1
   361 2BA8 85 92		        sta zpy
   362 2BAA A9 00		        lda #0
   363 2BAC 85 9D		        sta zpy_hi
   364 2BAE 85 94		        sta zpvy
   365 2BB0 4C 72 2C		        jmp ?cd
   366 2BB3			?chk_floor
   367
   368 				        ; Floor check: tile at feet (x+5, y)
   369 				        ; Use check_solid_or_platform so player lands on platforms
   370 2BB3 A5 91		        lda zpx
   371 2BB5 18			        clc
   372 2BB6 69 05		        adc #5
   373 2BB8 8D B0 25		        sta gt_px
   374 2BBB A5 9C		        lda zpx_hi
   375 2BBD 69 00		        adc #0
   376 2BBF 8D B1 25		        sta gt_px_hi
   377 2BC2 A5 92		        lda zpy
   378 2BC4 8D B2 25		        sta gt_py
   379 2BC7 A5 9D		        lda zpy_hi
   380 2BC9 8D B3 25		        sta gt_py_hi
   381 2BCC 20 01 26		        jsr check_solid_or_platform
   382 2BCF F0 11		        beq ?nofloor
   383 				        ; Landed! Snap to top of tile
   384 2BD1 A5 92		        lda zpy
   385 2BD3 29 F0		        and #$F0
   386 2BD5 85 92		        sta zpy
   387 				        ; zpy_hi unchanged (tile snap only affects low nibble)
   388 2BD7 A9 00		        lda #0
   389 2BD9 85 94		        sta zpvy
   390 2BDB A9 01		        lda #1
   391 2BDD 85 9A		        sta zpgnd
   392 2BDF 4C 72 2C		        jmp ?cd
   393 2BE2			?nofloor
   394 				        ; Bottom boundary: max Y = (MAP_H-1)*16 = 496 = $01F0
   395 2BE2 A5 9D		        lda zpy_hi
   396 2BE4 C9 02		        cmp #2
   397 2BE6 B0 0A		        bcs ?hitbot         ; Y >= 512, past bottom
   398 2BE8 C9 01		        cmp #1
   399 2BEA 90 60		        bcc ?chkgnd         ; Y < 256, not near bottom
   400 				        ; zpy_hi = 1: check if zpy >= $F0 (Y >= 496)
   401 2BEC A5 92		        lda zpy
   402 2BEE C9 F0		        cmp #$F0
   403 2BF0 90 5A		        bcc ?chkgnd
   404 2BF2 A9 F0		?hitbot lda #$F0
   405 2BF4 85 92		        sta zpy
   406 2BF6 A9 01		        lda #$01
   407 2BF8 85 9D		        sta zpy_hi
   408 2BFA A9 00		        lda #0
   409 2BFC 85 94		        sta zpvy
   410 2BFE A9 01		        lda #1
   411 2C00 85 9A		        sta zpgnd
   412 2C02 4C 72 2C		        jmp ?cd
   413
   414 2C05			?chkup  ; Rising: ceiling check at (x+5, y-PL_H)
   415 2C05 A5 91		        lda zpx
   416 2C07 18			        clc
   417 2C08 69 05		        adc #5
   418 2C0A 8D B0 25		        sta gt_px
   419 2C0D A5 9C		        lda zpx_hi
   420 2C0F 69 00		        adc #0
   421 2C11 8D B1 25		        sta gt_px_hi
   422 2C14 A5 92		        lda zpy
   423 2C16 38			        sec
   424 2C17 E9 1C		        sbc #PL_H
   425 2C19 8D B2 25		        sta gt_py
   426 2C1C A5 9D		        lda zpy_hi
   427 2C1E E9 00		        sbc #0
   428 2C20 8D B3 25		        sta gt_py_hi
   429 2C23 B0 08		        bcs ?ceil_ok
   430 				        ; Underflow: clamp to row 0
   431 2C25 A9 00		        lda #0
   432 2C27 8D B2 25		        sta gt_py
   433 2C2A 8D B3 25		        sta gt_py_hi
   434 2C2D 20 F9 25		?ceil_ok jsr check_solid
   435 2C30 F0 40		        beq ?cd
   436 				        ; Hit ceiling: snap below ceiling tile
   437 2C32 AD B2 25		        lda gt_py
   438 2C35 29 F0		        and #$F0
   439 2C37 18			        clc
   440 2C38 69 10		        adc #16
   441 2C3A 69 1C		        adc #PL_H
   442 2C3C 85 92		        sta zpy
   443 2C3E AD B3 25		        lda gt_py_hi
   444 2C41 69 00		        adc #0
   445 2C43 85 9D		        sta zpy_hi
   446 2C45 A9 00		        lda #0
   447 2C47 85 94		        sta zpvy
   448 2C49 4C 72 2C		        jmp ?cd
   449
   450 2C4C A5 9A		?chkgnd lda zpgnd
   451 2C4E F0 22		        beq ?cd
   452 2C50 A5 91		        lda zpx
   453 2C52 18			        clc
   454 2C53 69 05		        adc #5
   455 2C55 8D B0 25		        sta gt_px
   456 2C58 A5 9C		        lda zpx_hi
   457 2C5A 69 00		        adc #0
   458 2C5C 8D B1 25		        sta gt_px_hi
   459 2C5F A5 92		        lda zpy
   460 2C61 8D B2 25		        sta gt_py
   461 2C64 A5 9D		        lda zpy_hi
   462 2C66 8D B3 25		        sta gt_py_hi
   463 2C69 20 01 26		        jsr check_solid_or_platform
   464 2C6C D0 04		        bne ?cd
   465 2C6E A9 00		        lda #0
   466 2C70 85 9A		        sta zpgnd
   467 2C72			?cd
   468 				        ; --- Shoot (with cooldown, weapon-dependent) ---
   469 2C72 A5 C3		        lda zpwcool
   470 2C74 F0 05		        beq ?can_fire
   471 2C76 C6 C3		        dec zpwcool
   472 2C78 4C 16 2D		        jmp ?nos
   473 2C7B			?can_fire
   474 2C7B A5 A9		        lda ztrig
   475 2C7D D0 03		        bne ?chk_fire
   476 2C7F 4C 16 2D		        jmp ?nos
   477 2C82			?chk_fire
   478 				        ; --- Auto-fire detection ---
   479 				        ; Chaingun and Plasma fire continuously while trigger held.
   480 				        ; Other weapons require trigger release between shots (edge detect).
   481 2C82 A6 A6		        ldx zpwcur
   482 2C84 E0 03		        cpx #WP_CHAINGUN
   483 2C86 F0 0B		        beq ?autofire           ; chaingun: hold trigger = keep firing
   484 2C88 E0 05		        cpx #WP_PLASMA
   485 2C8A F0 07		        beq ?autofire           ; plasma: hold trigger = keep firing
   486 2C8C A5 AB		        lda ztrigp              ; other weapons: check trigger was released
   487 2C8E F0 03		        beq ?autofire
   488 2C90 4C 16 2D		        jmp ?nos                ; still held from last frame = don't fire
   489 2C93			?autofire
   490 				        ; --- Ammo check ---
   491 2C93 20 2E 27		        jsr get_cur_ammo
   492 2C96 F0 7E		        beq ?nos                ; no ammo left = can't fire
   493 				        ; --- Dispatch by weapon type ---
   494 2C98 A6 A6		        ldx zpwcur
   495 2C9A BD 2D 26		        lda weap_range,x
   496 2C9D D0 60		        bne ?melee              ; melee range > 0 = fist/chainsaw
   497 2C9F E0 04		        cpx #WP_ROCKET
   498 2CA1 B0 35		        bcs ?projectile         ; rocket/plasma/BFG = spawn projectile
   499 				        ; --- Hitscan weapon (pistol, shotgun, chaingun) ---
   500 				        ; Save weapon before deduct (auto_switch may change zpwcur)
   501 2CA3 A5 A6		        lda zpwcur
   502 2CA5 8D 19 2D		        sta fire_wcur
   503 2CA8 20 52 27		        jsr deduct_ammo         ; subtract ammo (may auto-switch if empty)
   504 				        ; Play weapon sound BEFORE hit (so enemy death sound can queue)
   505 2CAB AE 19 2D		        ldx fire_wcur           ; use saved weapon
   506 2CAE BD 46 26		        lda weap_sfx,x
   507 2CB1 C9 FF		        cmp #$FF
   508 2CB3 F0 12		        beq ?no_hsfx            ; no sound defined = skip
   509 2CB5 AA			        tax
   510 2CB6 20 A2 5A		        jsr snd_play
   511 				        ; Set sound lock duration based on weapon fire rate.
   512 2CB9 AE 19 2D		        ldx fire_wcur
   513 2CBC BD 18 26		        lda weap_cooldown,x     ; cooldown = frames between shots
   514 2CBF C9 08		        cmp #8
   515 2CC1 90 02		        bcc ?short_lock         ; cooldown < 8 → use cooldown as lock
   516 2CC3 A9 08		        lda #8                  ; cooldown >= 8 → cap lock at 8 frames
   517 2CC5			?short_lock
   518 2CC5 85 CB		        sta snd_lock
   519 2CC7			?no_hsfx
   520 2CC7 20 CA 27		        jsr hitscan_fire
   521 2CCA 20 51 41		        jsr alert_enemies_sound
   522 2CCD AE 19 2D		        ldx fire_wcur           ; use saved weapon for cooldown
   523 2CD0 BD 18 26		        lda weap_cooldown,x
   524 2CD3 85 C3		        sta zpwcool
   525 2CD5 4C 16 2D		        jmp ?nos
   526 2CD8			?projectile
   527 				        ; Projectile weapon (rocket, plasma, BFG)
   528 				        ; Save current weapon before deduct (auto_switch may change zpwcur)
   529 2CD8 A5 A6		        lda zpwcur
   530 2CDA 8D 19 2D		        sta fire_wcur
   531 2CDD 20 52 27		        jsr deduct_ammo
   532 2CE0 20 3A 2D		        jsr spawn_proj
   533 2CE3 20 51 41		        jsr alert_enemies_sound
   534 2CE6 AE 19 2D		        ldx fire_wcur          ; use saved weapon, not auto-switched
   535 2CE9 BD 46 26		        lda weap_sfx,x
   536 2CEC C9 FF		        cmp #$FF
   537 2CEE F0 04		        beq ?no_sfx
   538 2CF0 AA			        tax
   539 2CF1 20 A2 5A		        jsr snd_play
   540 2CF4 AE 19 2D		?no_sfx ldx fire_wcur
   541 2CF7 BD 18 26		        lda weap_cooldown,x
   542 2CFA 85 C3		        sta zpwcool
   543 2CFC 4C 16 2D		        jmp ?nos
   544 2CFF			?melee  ; Melee attack
   545 2CFF 20 AC 26		        jsr melee_attack
   546 2D02 A6 A6		        ldx zpwcur
   547 2D04 BD 46 26		        lda weap_sfx,x
   548 2D07 C9 FF		        cmp #$FF
   549 2D09 F0 04		        beq ?no_msfx
   550 2D0B AA			        tax
   551 2D0C 20 A2 5A		        jsr snd_play
   552 2D0F A6 A6		?no_msfx ldx zpwcur
   553 2D11 BD 18 26		        lda weap_cooldown,x
   554 2D14 85 C3		        sta zpwcool
   555 2D16			?nos
   556 2D16 E6 97		        inc zpan
   557 2D18 60			        rts
   558 2D19 00			fire_wcur dta 0                 ; saved weapon index during fire (before auto-switch)
   559 				.endp
   560
   561 				; ============================================
   562 				; PROJECTILES
   563 				; ============================================
   564 = 2D1A			proj_a  .ds MAX_PROJ
   565 = 2D1E			proj_x  .ds MAX_PROJ
   566 = 2D22			proj_xh .ds MAX_PROJ            ; X high byte (for positions > 255)
   567 = 2D26			proj_y  .ds MAX_PROJ
   568 = 2D2A			proj_yhi .ds MAX_PROJ
   569 = 2D2E			proj_vx .ds MAX_PROJ
   570 = 2D32			proj_dmg .ds MAX_PROJ           ; damage per projectile (from weapon)
   571 = 2D36			proj_spr .ds MAX_PROJ           ; sprite index per projectile
   572
   573 2D3A			.proc spawn_proj
   574 2D3A-30EB> A2 00		        ldx #0
   575 2D3C BD 1A 2D		?f      lda proj_a,x
   576 2D3F F0 06		        beq ?ok
   577 2D41 E8			        inx
   578 2D42 E0 04		        cpx #MAX_PROJ
   579 2D44 D0 F6		        bne ?f
   580 2D46 60			        rts
   581 2D47 A9 01		?ok     lda #1
   582 2D49 9D 1A 2D		        sta proj_a,x
   583 2D4C A5 91		        lda zpx
   584 2D4E 9D 1E 2D		        sta proj_x,x
   585 2D51 A5 9C		        lda zpx_hi
   586 2D53 9D 22 2D		        sta proj_xh,x
   587 2D56 A5 92		        lda zpy
   588 2D58 38			        sec
   589 2D59 E9 0E		        sbc #14             ; gun height (chest level)
   590 2D5B 9D 26 2D		        sta proj_y,x
   591 2D5E A5 9D		        lda zpy_hi
   592 2D60 E9 00		        sbc #0
   593 2D62 9D 2A 2D		        sta proj_yhi,x
   594 				        ; Set damage and sprite from saved weapon (auto-switch may have changed zpwcur)
   595 2D65 AC 19 2D		        ldy player_update.fire_wcur
   596 2D68 B9 11 26		        lda weap_dmg,y
   597 2D6B 9D 32 2D		        sta proj_dmg,x
   598 2D6E B9 38 26		        lda weap_proj_spr,y
   599 2D71 9D 36 2D		        sta proj_spr,x
   600 2D74 A5 95		        lda zpdir
   601 2D76 D0 06		        bne ?pl
   602 2D78 A9 03		        lda #3
   603 2D7A 9D 2E 2D		        sta proj_vx,x
   604 2D7D 60			        rts
   605 2D7E A9 FD		?pl     lda #$FD
   606 2D80 9D 2E 2D		        sta proj_vx,x
   607 2D83 60			        rts
   608 				.endp
   609
   610 2D84			.proc proj_update
   611 2D84 A2 00		        ldx #0
   612 2D86 BD 1A 2D		?lp     lda proj_a,x
   613 2D89 D0 03		        bne ?upd
   614 2D8B 4C 49 2E		        jmp ?nx
   615 2D8E			?upd    ; Update 16-bit X: proj_xh:proj_x += proj_vx (signed)
   616 2D8E BD 1E 2D		        lda proj_x,x
   617 2D91 18			        clc
   618 2D92 7D 2E 2D		        adc proj_vx,x
   619 2D95 9D 1E 2D		        sta proj_x,x
   620 				        ; Sign-extend velocity to high byte
   621 2D98 BD 2E 2D		        lda proj_vx,x
   622 2D9B 30 0B		        bmi ?neg
   623 				        ; Positive velocity: xh += carry
   624 2D9D BD 22 2D		        lda proj_xh,x
   625 2DA0 69 00		        adc #0
   626 2DA2 9D 22 2D		        sta proj_xh,x
   627 2DA5 4C B0 2D		        jmp ?bounds
   628 2DA8			?neg    ; Negative velocity: xh += $FF + carry
   629 2DA8 BD 22 2D		        lda proj_xh,x
   630 2DAB 69 FF		        adc #$FF
   631 2DAD 9D 22 2D		        sta proj_xh,x
   632 2DB0			?bounds ; Kill if off screen
   633 2DB0 BD 22 2D		        lda proj_xh,x
   634 2DB3 30 69		        bmi ?kill            ; xh < 0: past left edge
   635 2DB5 C9 02		        cmp #2
   636 2DB7 B0 65		        bcs ?kill            ; xh >= 2: past 512
   637 2DB9 C9 01		        cmp #1
   638 2DBB D0 07		        bne ?chk_wall        ; xh = 0: check wall
   639 				        ; xh = 1: kill if past right edge (X > 320 = xh=1,lo=64)
   640 2DBD BD 1E 2D		        lda proj_x,x
   641 2DC0 C9 40		        cmp #64
   642 2DC2 B0 5A		        bcs ?kill
   643 2DC4			?chk_wall
   644 				        ; Check hit against barrels FIRST (before tile check,
   645 				        ; because barrel uses invisible solid tile 15)
   646 2DC4 86 80		        stx zt
   647 2DC6 20 2D 39		        jsr check_proj_barrels
   648 2DC9 90 03		        bcc ?no_barrel_hit
   649 2DCB 4C 52 2E		        jmp ?nx3            ; hit barrel
   650 2DCE			?no_barrel_hit
   651 				        ; Check if projectile center hit a solid tile
   652 2DCE A6 80		        ldx zt
   653 2DD0 BD 1E 2D		        lda proj_x,x
   654 2DD3 18			        clc
   655 2DD4 69 04		        adc #4              ; center of 8x8 sprite
   656 2DD6 8D B0 25		        sta gt_px
   657 2DD9 BD 22 2D		        lda proj_xh,x
   658 2DDC 69 00		        adc #0
   659 2DDE 8D B1 25		        sta gt_px_hi
   660 2DE1 BD 26 2D		        lda proj_y,x
   661 2DE4 8D B2 25		        sta gt_py
   662 2DE7 BD 2A 2D		        lda proj_yhi,x
   663 2DEA 8D B3 25		        sta gt_py_hi
   664 2DED 86 81		        stx zt2
   665 2DEF 20 F9 25		        jsr check_solid
   666 2DF2 48			        pha
   667 2DF3 A6 81		        ldx zt2
   668 2DF5 68			        pla
   669 2DF6 D0 26		        bne ?kill
   670 				        ; Check hit against all enemies
   671 2DF8 A6 80		        ldx zt
   672 2DFA 86 80		        stx zt
   673 2DFC 20 98 43		        jsr check_proj_enemies
   674 2DFF 90 18		        bcc ?no_ehit
   675 				        ; Enemy hit — check if BFG for radius blast
   676 2E01 A6 80		        ldx zt
   677 2E03 BD 36 2D		        lda proj_spr,x
   678 2E06 C9 83		        cmp #SPR_BFG_PROJ1
   679 2E08 D0 48		        bne ?nx3            ; not BFG, done
   680 2E0A 20 9D 2F		        jsr bfg_blast
   681 2E0D 8A			        txa
   682 2E0E 48			        pha
   683 2E0F A2 14		        ldx #SFX_BFGXPL
   684 2E11 20 A2 5A		        jsr snd_play
   685 2E14 68			        pla
   686 2E15 AA			        tax
   687 2E16 4C 52 2E		        jmp ?nx3
   688 2E19			?no_ehit
   689 2E19 A6 80		        ldx zt
   690 2E1B 4C 49 2E		        jmp ?nx
   691 2E1E BD 36 2D		?kill   lda proj_spr,x
   692 2E21 C9 68		        cmp #SPR_ROCKET_PROJ
   693 2E23 D0 0F		        bne ?chk_bfg
   694 2E25 20 20 30		        jsr rocket_splash_player
   695 2E28 8A			        txa
   696 2E29 48			        pha
   697 2E2A A2 0A		        ldx #SFX_BAREXP
   698 2E2C 20 A2 5A		        jsr snd_play
   699 2E2F 68			        pla
   700 2E30 AA			        tax
   701 2E31 4C 44 2E		        jmp ?no_ksnd
   702 2E34			?chk_bfg
   703 2E34 C9 83		        cmp #SPR_BFG_PROJ1
   704 2E36 D0 0C		        bne ?no_ksnd
   705 2E38 20 9D 2F		        jsr bfg_blast
   706 2E3B 8A			        txa
   707 2E3C 48			        pha
   708 2E3D A2 14		        ldx #SFX_BFGXPL
   709 2E3F 20 A2 5A		        jsr snd_play
   710 2E42 68			        pla
   711 2E43 AA			        tax
   712 2E44			?no_ksnd
   713 2E44 A9 00		        lda #0
   714 2E46 9D 1A 2D		        sta proj_a,x
   715 2E49 E8			?nx     inx
   716 2E4A E0 04		        cpx #MAX_PROJ
   717 2E4C F0 03		        beq ?end
   718 2E4E 4C 86 2D		        jmp ?lp
   719 2E51 60			?end    rts
   720 2E52 A6 80		?nx3    ldx zt
   721 2E54 E8			        inx
   722 2E55 E0 04		        cpx #MAX_PROJ
   723 2E57 F0 03		        beq ?end2
   724 2E59 4C 86 2D		        jmp ?lp
   725 2E5C 60			?end2   rts
   726 				.endp
   727
   728 				; ============================================
   729 				; PLAYER DEAD - death animation + wait for R restart
   730 				; ============================================
   731 2E5D 00			pl_dead_timer dta 0
   732 2E5E 00			pl_pain_timer dta 0
   733
   734 2E5F			.proc player_dead
   735 				        ; Mark dirty at player position (ensure sprite cleanup between frames)
   736 2E5F A5 91		        lda zpx
   737 2E61 38			        sec
   738 2E62 E5 D7		        sbc cam_x_lo
   739 2E64 85 D0		        sta zdx
   740 2E66 A5 9C		        lda zpx_hi
   741 2E68 E5 D8		        sbc cam_x_hi
   742 2E6A 85 D1		        sta zdxh
   743 2E6C A5 92		        lda zpy
   744 2E6E 38			        sec
   745 2E6F E9 20		        sbc #32
   746 2E71 B0 02		        bcs ?dy_ok
   747 2E73 A9 00		        lda #0
   748 2E75 85 D2		?dy_ok  sta zdy
   749 2E77 A9 10		        lda #16
   750 2E79 8D 5D 55		        sta md_w
   751 2E7C A9 20		        lda #32
   752 2E7E 8D 5E 55		        sta md_h
   753 2E81 20 90 54		        jsr mark_dirty_sprite
   754
   755 2E84 AD 5D 2E		        lda pl_dead_timer
   756 2E87 D0 0E		        bne ?counting
   757 				        ; First frame of death: play sound, init timer
   758 2E89 A9 00		        lda #0
   759 2E8B 85 CB		        sta snd_lock
   760 2E8D A2 15		        ldx #SFX_PLDEATH
   761 2E8F 20 A2 5A		        jsr snd_play
   762 2E92 A9 01		        lda #1
   763 2E94 8D 5D 2E		        sta pl_dead_timer
   764 2E97			?counting
   765 				        ; Increment death timer (for animation)
   766 2E97 AD 5D 2E		        lda pl_dead_timer
   767 2E9A C9 FF		        cmp #255
   768 2E9C F0 03		        beq ?wait
   769 2E9E EE 5D 2E		        inc pl_dead_timer
   770 2EA1			?wait
   771 				        ; Auto-restart after ~3s (timer=180)
   772 2EA1 AD 5D 2E		        lda pl_dead_timer
   773 2EA4 C9 B4		        cmp #180
   774 2EA6 90 37		        bcc ?done
   775 2EA8 A9 FF		        lda #$FF
   776 2EAA 8D FC 02		        sta $02FC
   777 2EAD A9 00		        lda #0
   778 2EAF 85 C9		        sta snd_active
   779 2EB1 8D 07 D2		        sta AUDC4
   780 2EB4 8D 5D 2E		        sta pl_dead_timer
   781 2EB7 A5 10		        lda POKMSK
   782 2EB9 29 FE		        and #$FE
   783 2EBB 85 10		        sta POKMSK
   784 2EBD 8D 0E D2		        sta IRQEN
   785 2EC0 20 21 25		        jsr init_game
   786 2EC3 20 B9 61		        jsr init_render
   787 2EC6 A9 FF		        lda #$FF
   788 2EC8 8D 6C 49		        sta hud_prev_hp
   789 2ECB 8D 6D 49		        sta hud_prev_ammo
   790 2ECE 8D 6E 49		        sta hud_prev_weap
   791 2ED1 8D 6F 49		        sta hud_prev_keys
   792 2ED4 8D 70 49		        sta hud_prev_armor
   793 2ED7 A9 02		        lda #2
   794 2ED9 8D 71 49		        sta hud_frames
   795 2EDC 8D 72 49		        sta hud_full_clear
   796 2EDF 60			?done   rts
   797 				.endp
   798
   799 				; ============================================
   800 				; CAMERA UPDATE - pixel-precise camera tracking
   801 				; cam_x = clamp(player_x - 160, 0, 704)
   802 				; cam_tx = cam_x >> 4, cam_sub = cam_x & 15
   803 				; cam_x_lo/hi = cam_tx * 16 (tile-aligned, for sprite offset)
   804 				; ============================================
   805 2EE0			.proc camera_update
   806 				        ; cam_x = player_x - 160 ($A0)
   807 2EE0 A5 91		        lda zpx
   808 2EE2 38			        sec
   809 2EE3 E9 A0		        sbc #160
   810 2EE5 85 80		        sta zt
   811 2EE7 A5 9C		        lda zpx_hi
   812 2EE9 E9 00		        sbc #0
   813 2EEB 85 81		        sta zt2
   814 				        ; Clamp low: if negative, clamp to 0
   815 2EED B0 09		        bcs ?not_neg
   816 2EEF A9 00		        lda #0
   817 2EF1 85 80		        sta zt
   818 2EF3 85 81		        sta zt2
   819 2EF5 4C 10 2F		        jmp ?store
   820 2EF8			?not_neg
   821 				        ; Clamp high: if > 704 ($02C0)
   822 2EF8 A5 81		        lda zt2
   823 2EFA C9 03		        cmp #3
   824 2EFC B0 0A		        bcs ?clamp_max
   825 2EFE C9 02		        cmp #2
   826 2F00 90 0E		        bcc ?store
   827 				        ; zt2=2: check lo byte
   828 2F02 A5 80		        lda zt
   829 2F04 C9 C0		        cmp #$C0
   830 2F06 90 08		        bcc ?store
   831 2F08			?clamp_max
   832 2F08 A9 C0		        lda #$C0
   833 2F0A 85 80		        sta zt
   834 2F0C A9 02		        lda #$02
   835 2F0E 85 81		        sta zt2
   836 2F10			?store
   837 				        ; cam_tx = cam_x >> 4
   838 2F10 A5 81		        lda zt2
   839 2F12 0A			        asl
   840 2F13 0A			        asl
   841 2F14 0A			        asl
   842 2F15 0A			        asl
   843 2F16 85 D6		        sta cam_tx
   844 2F18 A5 80		        lda zt
   845 2F1A 4A			        lsr
   846 2F1B 4A			        lsr
   847 2F1C 4A			        lsr
   848 2F1D 4A			        lsr
   849 2F1E 05 D6		        ora cam_tx
   850 2F20 85 D6		        sta cam_tx
   851 				        ; cam_sub = cam_x & 15
   852 2F22 A5 80		        lda zt
   853 2F24 29 0F		        and #$0F
   854 2F26 85 D9		        sta cam_sub
   855 				        ; cam_x_lo/hi = zt & $F0 / zt2 (tile-aligned, same as cam_tx*16)
   856 2F28 A5 80		        lda zt
   857 2F2A 29 F0		        and #$F0
   858 2F2C 85 D7		        sta cam_x_lo
   859 2F2E A5 81		        lda zt2
   860 2F30 85 D8		        sta cam_x_hi
   861 				        ; Detect tile column change
   862 2F32 A5 D6		        lda cam_tx
   863 2F34 C5 DB		        cmp cam_tx_prev
   864 2F36 F0 06		        beq ?no_x_chg
   865 2F38 85 DB		        sta cam_tx_prev
   866 2F3A A9 02		        lda #2
   867 2F3C 85 DA		        sta scroll_redraw
   868 2F3E			?no_x_chg
   869 				        ; --- Vertical camera (16-bit) ---
   870 				        ; Update when grounded OR falling (not jumping = zpvy >= 0)
   871 				        ; cam_y = clamp(zpy_hi:zpy - 96, 0, 64)
   872 				        ; max 64 = sky is 256px, 256-192=64px safe scroll range
   873 2F3E A5 9A		        lda zpgnd
   874 2F40 D0 04		        bne ?do_ycam
   875 2F42 A5 94		        lda zpvy
   876 2F44 30 56		        bmi ?no_ychg            ; jumping (ascending) → don't move camera
   877 2F46			?do_ycam
   878 2F46 A5 92		        lda zpy
   879 2F48 38			        sec
   880 2F49 E9 60		        sbc #96
   881 2F4B 85 80		        sta zt
   882 2F4D A5 9D		        lda zpy_hi
   883 2F4F E9 00		        sbc #0
   884 2F51 85 81		        sta zt2
   885 				        ; If negative, clamp to 0
   886 2F53 B0 09		        bcs ?y_not_neg
   887 2F55 A9 00		        lda #0
   888 2F57 85 80		        sta zt
   889 2F59 85 81		        sta zt2
   890 2F5B 4C 74 2F		        jmp ?y_clamp
   891 2F5E			?y_not_neg
   892 				        ; Clamp to max 320 ($0140) = (MAP_H - TILES_Y) * 16
   893 2F5E A5 81		        lda zt2
   894 2F60 C9 01		        cmp #1
   895 2F62 90 10		        bcc ?y_clamp            ; hi=0: < 256, OK
   896 2F64 D0 06		        bne ?y_max              ; hi>=2: > 511, clamp
   897 2F66 A5 80		        lda zt
   898 2F68 C9 41		        cmp #$41
   899 2F6A 90 08		        bcc ?y_clamp            ; < 321, OK
   900 2F6C A9 40		?y_max  lda #$40
   901 2F6E 85 80		        sta zt
   902 2F70 A9 01		        lda #$01
   903 2F72 85 81		        sta zt2
   904 2F74			?y_clamp
   905 				        ; cam_ty = zt2:zt >> 4
   906 2F74 A5 81		        lda zt2
   907 2F76 0A			        asl
   908 2F77 0A			        asl
   909 2F78 0A			        asl
   910 2F79 0A			        asl
   911 2F7A 85 DE		        sta cam_ty
   912 2F7C A5 80		        lda zt
   913 2F7E 4A			        lsr
   914 2F7F 4A			        lsr
   915 2F80 4A			        lsr
   916 2F81 4A			        lsr
   917 2F82 05 DE		        ora cam_ty
   918 2F84 85 DE		        sta cam_ty              ; cam_ty = 0..20
   919 				        ; cam_y_hi:cam_y = zt & $F0 (tile-aligned = cam_ty*16)
   920 2F86 A5 80		        lda zt
   921 2F88 29 F0		        and #$F0
   922 2F8A 85 DF		        sta cam_y
   923 2F8C A5 81		        lda zt2
   924 2F8E 85 E1		        sta cam_y_hi
   925 				        ; Detect tile row change
   926 2F90 A5 DE		        lda cam_ty
   927 2F92 C5 E0		        cmp cam_ty_prev
   928 2F94 F0 06		        beq ?no_ychg
   929 2F96 85 E0		        sta cam_ty_prev
   930 2F98 A9 02		        lda #2
   931 2F9A 85 DA		        sta scroll_redraw
   932 2F9C			?no_ychg
   933 2F9C 60			        rts
   934 				.endp
   935
   936 				; ============================================
   937 				; BFG BLAST - radius damage to all enemies in range
   938 				; Input: X = projectile index
   939 				; Radius: 64px, Damage: 20 per enemy
   940 				; ============================================
   941 = 0040			BFG_RADIUS = 64
   942 = 0014			BFG_BLAST_DMG = 20
   943
   944 2F9D			.proc bfg_blast
   945 2F9D 8E 1E 30		        stx bf_proj
   946 2FA0 A0 00		        ldy #0
   947 2FA2 8C 1F 30		?lp     sty bf_eidx
   948 2FA5 B9 55 3A		        lda en_act,y
   949 2FA8 C9 01		        cmp #1
   950 2FAA D0 66		        bne ?nx
   951 				        ; Check same hi byte
   952 2FAC AE 1E 30		        ldx bf_proj
   953 2FAF BD 22 2D		        lda proj_xh,x
   954 2FB2 D9 3D 3A		        cmp enxhi,y
   955 2FB5 D0 5B		        bne ?nx
   956 				        ; X distance
   957 2FB7 BD 1E 2D		        lda proj_x,x
   958 2FBA 38			        sec
   959 2FBB F9 37 3A		        sbc en_x,y
   960 2FBE 10 05		        bpl ?xa
   961 2FC0 49 FF		        eor #$FF
   962 2FC2 18			        clc
   963 2FC3 69 01		        adc #1
   964 2FC5 C9 40		?xa     cmp #BFG_RADIUS
   965 2FC7 B0 49		        bcs ?nx
   966 				        ; Y distance (hi byte check first)
   967 2FC9 AE 1E 30		        ldx bf_proj
   968 2FCC BD 2A 2D		        lda proj_yhi,x
   969 2FCF D9 49 3A		        cmp en_yhi,y
   970 2FD2 D0 3E		        bne ?nx             ; different hi bytes = too far
   971 2FD4 BD 26 2D		        lda proj_y,x
   972 2FD7 38			        sec
   973 2FD8 F9 43 3A		        sbc en_y,y
   974 2FDB 10 05		        bpl ?ya
   975 2FDD 49 FF		        eor #$FF
   976 2FDF 18			        clc
   977 2FE0 69 01		        adc #1
   978 2FE2 C9 40		?ya     cmp #BFG_RADIUS
   979 2FE4 B0 2C		        bcs ?nx
   980 				        ; Hit! Apply BFG damage
   981 2FE6 B9 5B 3A		        lda en_hp,y
   982 2FE9 38			        sec
   983 2FEA E9 14		        sbc #BFG_BLAST_DMG
   984 2FEC B0 02		        bcs ?hok
   985 2FEE A9 00		        lda #0
   986 2FF0 99 5B 3A		?hok    sta en_hp,y
   987 2FF3 D0 1D		        bne ?nx
   988 				        ; Killed
   989 2FF5 A9 02		        lda #2
   990 2FF7 99 55 3A		        sta en_act,y
   991 2FFA A9 14		        lda #20
   992 2FFC 99 79 3A		        sta en_dtimer,y
   993 2FFF A9 00		        lda #0
   994 3001 99 85 3A		        sta en_gib,y
   995 3004 99 8B 3A		        sta envelx,y
   996 3007 8C 1F 30		        sty bf_eidx
   997 300A 98			        tya
   998 300B AA			        tax
   999 300C 20 91 5B		        jsr play_enemy_death
  1000 300F AC 1F 30		        ldy bf_eidx
  1001 3012 AC 1F 30		?nx     ldy bf_eidx
  1002 3015 C8			        iny
  1003 3016 C0 06		        cpy #MAX_ENEMIES
  1004 3018 D0 88		        bne ?lp
  1005 301A AE 1E 30		        ldx bf_proj
  1006 301D 60			        rts
  1007 301E 00			bf_proj dta 0
  1008 301F 00			bf_eidx dta 0
  1009 				.endp
  1010
  1011 				; ============================================
  1012 				; ROCKET SPLASH DAMAGE + KNOCKBACK TO PLAYER
  1013 				; Input: X = projectile index
  1014 				; ============================================
  1015 3020			.proc rocket_splash_player
  1016 3020 8E 9C 30		        stx rs_proj
  1017 3023 BD 36 2D		        lda proj_spr,x
  1018 3026 C9 68		        cmp #SPR_ROCKET_PROJ
  1019 3028 D0 71		        bne ?ret
  1020 302A BD 22 2D		        lda proj_xh,x
  1021 302D C5 9C		        cmp zpx_hi
  1022 302F D0 6A		        bne ?ret
  1023 3031 BD 1E 2D		        lda proj_x,x
  1024 3034 38			        sec
  1025 3035 E5 91		        sbc zpx
  1026 3037 10 05		        bpl ?xa
  1027 3039 49 FF		        eor #$FF
  1028 303B 18			        clc
  1029 303C 69 01		        adc #1
  1030 303E C9 20		?xa     cmp #ROCKET_SPLASH_RADIUS
  1031 3040 B0 59		        bcs ?ret
  1032 3042 8D 9D 30		        sta rs_dist
  1033 3045 AE 9C 30		        ldx rs_proj
  1034 3048 BD 2A 2D		        lda proj_yhi,x
  1035 304B C5 9D		        cmp zpy_hi
  1036 304D D0 4C		        bne ?ret            ; different hi bytes = too far
  1037 304F BD 26 2D		        lda proj_y,x
  1038 3052 38			        sec
  1039 3053 E5 92		        sbc zpy
  1040 3055 10 05		        bpl ?ya
  1041 3057 49 FF		        eor #$FF
  1042 3059 18			        clc
  1043 305A 69 01		        adc #1
  1044 305C C9 20		?ya     cmp #ROCKET_SPLASH_RADIUS
  1045 305E B0 3B		        bcs ?ret
  1046 3060 CD 9D 30		        cmp rs_dist
  1047 3063 B0 03		        bcs ?dok
  1048 3065 AD 9D 30		        lda rs_dist
  1049 3068 0A			?dok    asl
  1050 3069 8D 9D 30		        sta rs_dist
  1051 306C A9 32		        lda #ROCKET_SPLASH_MAX
  1052 306E 38			        sec
  1053 306F ED 9D 30		        sbc rs_dist
  1054 3072 90 04		        bcc ?pmin
  1055 3074 C9 01		        cmp #1
  1056 3076 B0 02		        bcs ?apply
  1057 3078 A9 01		?pmin   lda #1
  1058 307A 8D 9D 30		?apply  sta rs_dist
  1059 307D AD 9D 30		        lda rs_dist
  1060 3080 20 9E 30		        jsr player_take_damage
  1061 				        ; --- Horizontal knockback: push player away from explosion ---
  1062 3083 AE 9C 30		        ldx rs_proj
  1063 3086 A5 91		        lda zpx
  1064 3088 DD 1E 2D		        cmp proj_x,x
  1065 308B B0 0A		        bcs ?kb_r
  1066 				        ; Player left of explosion → push left
  1067 308D A9 00		        lda #0
  1068 308F 38			        sec
  1069 3090 E9 06		        sbc #BARREL_KB_X        ; use barrel KB strength (stronger)
  1070 3092 85 93		        sta zpvx
  1071 3094 4C 9B 30		        jmp ?ret
  1072 3097			?kb_r   ; Player right → push right
  1073 3097 A9 06		        lda #BARREL_KB_X
  1074 3099 85 93		        sta zpvx
  1075 309B 60			?ret    rts
  1076 309C 00			rs_proj dta 0
  1077 309D 00			rs_dist dta 0
  1078 				.endp
  1079
  1080 				; ============================================
  1081 				; PLAYER TAKE DAMAGE (with armor absorption)
  1082 				; Input: A = damage amount
  1083 				; Armor absorbs 75% damage, HP takes 25%
  1084 				; ============================================
  1085 309E			.proc player_take_damage
  1086 309E 8D E9 30		        sta ptd_dmg
  1087 30A1 A5 A0		        lda zparmor
  1088 30A3 F0 32		        beq ?no_armor
  1089 				        ; armor_hit = damage * 3/4
  1090 30A5 AD E9 30		        lda ptd_dmg
  1091 30A8 4A			        lsr
  1092 30A9 4A			        lsr                     ; damage/4 (HP portion)
  1093 30AA F0 03		        beq ?min_hp
  1094 30AC 4C B1 30		        jmp ?calc
  1095 30AF A9 01		?min_hp lda #1                  ; minimum 1 HP damage
  1096 30B1 8D EB 30		?calc   sta ptd_hp
  1097 30B4 AD E9 30		        lda ptd_dmg
  1098 30B7 38			        sec
  1099 30B8 ED EB 30		        sbc ptd_hp              ; armor_hit = damage - damage/4
  1100 30BB 8D EA 30		        sta ptd_arm
  1101 				        ; Reduce armor
  1102 30BE A5 A0		        lda zparmor
  1103 30C0 38			        sec
  1104 30C1 ED EA 30		        sbc ptd_arm
  1105 30C4 B0 02		        bcs ?ar_ok
  1106 30C6 A9 00		        lda #0
  1107 30C8 85 A0		?ar_ok  sta zparmor
  1108 				        ; Apply HP portion
  1109 30CA A5 98		        lda zphp
  1110 30CC 38			        sec
  1111 30CD ED EB 30		        sbc ptd_hp
  1112 30D0 B0 0F		        bcs ?hp_ok
  1113 30D2 A9 00		        lda #0
  1114 30D4 4C E1 30		        jmp ?hp_ok
  1115 30D7			?no_armor
  1116 				        ; No armor: full damage to HP
  1117 30D7 A5 98		        lda zphp
  1118 30D9 38			        sec
  1119 30DA ED E9 30		        sbc ptd_dmg
  1120 30DD B0 02		        bcs ?hp_ok
  1121 30DF A9 00		        lda #0
  1122 30E1 85 98		?hp_ok  sta zphp
  1123 30E3 A9 0C		        lda #12                ; pain sprite for 12 frames (~0.2s)
  1124 30E5 8D 5E 2E		        sta pl_pain_timer
  1125 30E8 60			        rts
  1126 30E9 00			ptd_dmg dta 0
  1127 30EA 00			ptd_arm dta 0
  1128 30EB 00			ptd_hp  dta 0
  1129 				.endp
   316 30EC			        icl 'pickups.asm'
Source: pickups.asm
     1 				;==============================================
     2 				; DOOM2D - Pickups (health, ammo, armor, keys, weapons, powerups)
     3 				; pickups.asm
     4 				;==============================================
     5
     6 				; Pickup arrays
     7 = 30EC			pk_act  .ds MAX_PICKUPS         ; active flag
     8 = 30F8			pk_x    .ds MAX_PICKUPS         ; X position (lo)
     9 = 3104			pk_xhi  .ds MAX_PICKUPS         ; X position (hi)
    10 = 3110			pk_y    .ds MAX_PICKUPS         ; Y position (lo)
    11 = 311C			pk_yhi  .ds MAX_PICKUPS         ; Y position (hi)
    12 = 3128			pk_type .ds MAX_PICKUPS         ; type (PK_xxx constants)
    13
    14 				; pk_spr_tab is in sprite_defs.asm (auto-generated)
    15
    16 				; HP/ammo amounts per pickup type
    17 3134			pk_amount
    18 3134-34CC> 0A		        dta 10                  ; PK_HEALTH:     +10 HP
    19 3135 01			        dta 1                   ; PK_AMMO:       +1 ammo
    20 3136 19			        dta 25                  ; PK_MEDIKIT:     +25 HP
    21 3137 64			        dta 100                 ; PK_GREENARMOR:  100 armor (DOOM: green=100)
    22 3138 C8			        dta 200                 ; PK_BLUEARMOR:   200 armor (DOOM: blue=200)
    23 3139 64			        dta 100                 ; PK_SOULSPHERE:  +100 HP (over max)
    24 313A 01			        dta 1                   ; PK_KEYRED:      key bit 0
    25 313B 02			        dta 2                   ; PK_KEYBLUE:     key bit 1
    26 313C 04			        dta 4                   ; PK_KEYYELLOW:   key bit 2
    27 313D 04			        dta $04                 ; PK_SHOTGUN:     weapon bit2 (WP_SHOTGUN)
    28 313E 04			        dta 4                   ; PK_SHELLS:      +4 shells
    29 313F 02			        dta $02                 ; PK_PISTOL:      weapon bit1 (WP_PISTOL)
    30 3140 08			        dta $08                 ; PK_CHAINGUN:    weapon bit3 (WP_CHAINGUN)
    31 3141 10			        dta $10                 ; PK_ROCKETL:     weapon bit4 (WP_ROCKET)
    32 3142 05			        dta 5                   ; PK_ROCKETBOX:   +5 rockets
    33 3143 20			        dta $20                 ; PK_PLASMAGUN:   weapon bit5 (WP_PLASMA)
    34 3144 14			        dta 20                  ; PK_CELLS:       +20 cells
    35 3145 40			        dta $40                 ; PK_BFG:         weapon bit6 (WP_BFG)
    36 3146 01			        dta 1                   ; PK_ROCKET1:     +1 rocket
    37 3147 01			        dta 1                   ; PK_HEALTHBONUS: +1 HP (max 200)
    38 3148 01			        dta 1                   ; PK_ARMORBONUS:  +1 armor (max 200)
    39
    40 				; Effect class per type: 0=health, 1=ammo, 2=armor, 3=key, 4=soul, 5=weapon
    41 3149			pk_class
    42 3149 00			        dta 0                   ; PK_HEALTH
    43 314A 01			        dta 1                   ; PK_AMMO
    44 314B 00			        dta 0                   ; PK_MEDIKIT
    45 314C 02			        dta 2                   ; PK_GREENARMOR
    46 314D 02			        dta 2                   ; PK_BLUEARMOR
    47 314E 04			        dta 4                   ; PK_SOULSPHERE
    48 314F 03			        dta 3                   ; PK_KEYRED
    49 3150 03			        dta 3                   ; PK_KEYBLUE
    50 3151 03			        dta 3                   ; PK_KEYYELLOW
    51 3152 05			        dta 5                   ; PK_SHOTGUN
    52 3153 01			        dta 1                   ; PK_SHELLS
    53 3154 05			        dta 5                   ; PK_PISTOL (weapon class)
    54 3155 05			        dta 5                   ; PK_CHAINGUN (weapon class)
    55 3156 05			        dta 5                   ; PK_ROCKETL (weapon class)
    56 3157 06			        dta 6                   ; PK_ROCKETBOX (rocket ammo class)
    57 3158 05			        dta 5                   ; PK_PLASMAGUN (weapon class)
    58 3159 07			        dta 7                   ; PK_CELLS (cells ammo class)
    59 315A 05			        dta 5                   ; PK_BFG (weapon class)
    60 315B 06			        dta 6                   ; PK_ROCKET1 (rocket ammo class)
    61 315C 04			        dta 4                   ; PK_HEALTHBONUS (soul class - goes over 100)
    62 315D 08			        dta 8                   ; PK_ARMORBONUS (armor bonus class)
    63
    64 				; init_pickups is in the overlay segment (end of main.asm)
    65
    66 				; ============================================
    67 				; UPDATE PICKUPS (collision with player)
    68 				; ============================================
    69 315E			.proc update_pickups
    70 				        ; Skip all pickup checks if player is dead
    71 315E A5 98		        lda zphp
    72 3160 D0 01		        bne ?go
    73 3162 60			?ret    rts
    74 3163 A2 00		?go     ldx #0
    75 3165 BD EC 30		?lp     lda pk_act,x
    76 3168 D0 03		        bne ?chk
    77 316A 4C A4 33		        jmp ?nx
    78 316D			?chk    ; Check X hi byte match
    79 316D BD 04 31		        lda pk_xhi,x
    80 3170 C5 9C		        cmp zpx_hi
    81 3172 D0 11		        bne ?nxj
    82 				        ; Check X distance: |pk_x - zpx| < 12
    83 3174 BD F8 30		        lda pk_x,x
    84 3177 38			        sec
    85 3178 E5 91		        sbc zpx
    86 317A 10 05		        bpl ?ax
    87 317C 49 FF		        eor #$FF
    88 317E 18			        clc
    89 317F 69 01		        adc #1
    90 3181 C9 0C		?ax     cmp #12
    91 3183 90 03		        bcc ?xok
    92 3185 4C A4 33		?nxj    jmp ?nx
    93 3188			?xok    ; Check Y distance: |pk_yhi:pk_y - zpy_hi:zpy| < 16
    94 3188 BD 1C 31		        lda pk_yhi,x
    95 318B C5 9D		        cmp zpy_hi
    96 318D D0 11		        bne ?nxj2
    97 318F BD 10 31		        lda pk_y,x
    98 3192 38			        sec
    99 3193 E5 92		        sbc zpy
   100 3195 10 05		        bpl ?ay
   101 3197 49 FF		        eor #$FF
   102 3199 18			        clc
   103 319A 69 01		        adc #1
   104 319C C9 10		?ay     cmp #16
   105 319E 90 03		        bcc ?hit
   106 31A0 4C A4 33		?nxj2   jmp ?nx
   107 31A3			?hit    ; Pickup collected! Route by class
   108 31A3 8E AD 33		        stx pk_tmp
   109 31A6 BC 28 31		        ldy pk_type,x
   110 31A9 B9 49 31		        lda pk_class,y
   111 31AC C9 01		        cmp #1
   112 31AE F0 39		        beq ?ammo
   113 31B0 C9 02		        cmp #2
   114 31B2 F0 6C		        beq ?armor
   115 31B4 C9 03		        cmp #3
   116 31B6 F0 79		        beq ?key
   117 31B8 C9 04		        cmp #4
   118 31BA F0 7F		        beq ?soul
   119 31BC C9 05		        cmp #5
   120 31BE D0 03		        bne ?chk6
   121 31C0 4C 70 32		        jmp ?weapon
   122 31C3 C9 06		?chk6   cmp #6
   123 31C5 D0 03		        bne ?chk7
   124 31C7 4C 4C 32		        jmp ?rockets
   125 31CA C9 07		?chk7   cmp #7
   126 31CC D0 03		        bne ?chk8
   127 31CE 4C 5D 32		        jmp ?cells
   128 31D1 C9 08		?chk8   cmp #8
   129 31D3 D0 03		        bne ?chk_hp
   130 31D5 4C 12 32		        jmp ?armbonus
   131 31D8			?chk_hp
   132 				        ; class 0: health
   133 31D8 A5 98		        lda zphp
   134 31DA 18			        clc
   135 31DB 79 34 31		        adc pk_amount,y
   136 31DE C9 64		        cmp #100
   137 31E0 90 02		        bcc ?hp_ok
   138 31E2 A9 64		        lda #100
   139 31E4 85 98		?hp_ok  sta zphp
   140 31E6 4C 63 33		        jmp ?sfx_ammo
   141 31E9			?ammo   ; Check if shells (PK_SHELLS) or bullets (PK_AMMO)
   142 31E9 BD 28 31		        lda pk_type,x
   143 31EC C9 0A		        cmp #PK_SHELLS
   144 31EE F0 11		        beq ?ashell
   145 				        ; Bullets
   146 31F0 A5 99		        lda zpammo
   147 31F2 18			        clc
   148 31F3 79 34 31		        adc pk_amount,y
   149 31F6 C9 C8		        cmp #200
   150 31F8 90 02		        bcc ?am_ok
   151 31FA A9 C8		        lda #200
   152 31FC 85 99		?am_ok  sta zpammo
   153 31FE 4C 63 33		        jmp ?sfx_ammo
   154 3201 A5 A3		?ashell lda zpshells
   155 3203 18			        clc
   156 3204 79 34 31		        adc pk_amount,y
   157 3207 C9 32		        cmp #50
   158 3209 90 02		        bcc ?sh_ok
   159 320B A9 32		        lda #50
   160 320D 85 A3		?sh_ok  sta zpshells
   161 320F 4C 63 33		        jmp ?sfx_ammo
   162 3212			?armbonus ; +1 armor (max 200, always picks up)
   163 3212 A5 A0		        lda zparmor
   164 3214 C9 C8		        cmp #200
   165 3216 B0 05		        bcs ?ab_skip
   166 3218 E6 A0		        inc zparmor
   167 321A 4C 63 33		        jmp ?sfx_ammo
   168 321D 4C A4 33		?ab_skip jmp ?nx
   169 3220			?armor  ; Set armor to value if current is less (DOOM behavior)
   170 3220 B9 34 31		        lda pk_amount,y
   171 3223 C5 A0		        cmp zparmor
   172 3225 90 07		        bcc ?ar_skip            ; already have more armor
   173 3227 F0 05		        beq ?ar_skip
   174 3229 85 A0		        sta zparmor
   175 322B 4C 63 33		        jmp ?sfx_ammo
   176 322E 4C A4 33		?ar_skip jmp ?nx                ; don't pick up if already have enough
   177 3231 B9 34 31		?key    lda pk_amount,y
   178 3234 05 A1		        ora zpkeys
   179 3236 85 A1		        sta zpkeys
   180 3238 4C 63 33		        jmp ?sfx_ammo
   181 323B A5 98		?soul   lda zphp
   182 323D 18			        clc
   183 323E 79 34 31		        adc pk_amount,y
   184 3241 C9 C8		        cmp #200
   185 3243 90 02		        bcc ?so_ok
   186 3245 A9 C8		        lda #200
   187 3247 85 98		?so_ok  sta zphp
   188 3249 4C 63 33		        jmp ?sfx_ammo
   189 324C A5 A4		?rockets lda zprockets
   190 324E 18			        clc
   191 324F 79 34 31		        adc pk_amount,y
   192 3252 C9 32		        cmp #50
   193 3254 90 02		        bcc ?rk_ok
   194 3256 A9 32		        lda #50
   195 3258 85 A4		?rk_ok  sta zprockets
   196 325A 4C 63 33		        jmp ?sfx_ammo
   197 325D A5 A5		?cells  lda zpcells
   198 325F 18			        clc
   199 3260 79 34 31		        adc pk_amount,y
   200 3263 B0 04		        bcs ?cl_max
   201 3265 C9 C8		        cmp #200
   202 3267 90 02		        bcc ?cl_ok
   203 3269 A9 C8		?cl_max lda #200
   204 326B 85 A5		?cl_ok  sta zpcells
   205 326D 4C 63 33		        jmp ?sfx_ammo
   206 3270 B9 34 31		?weapon lda pk_amount,y         ; weapon bitmask
   207 3273 8D AE 33		        sta wp_mask             ; save for switch check
   208 3276 A5 A2		        lda zpweap
   209 3278 2D AE 33		        and wp_mask
   210 327B 8D AF 33		        sta wp_had              ; non-zero = already had this weapon
   211 327E A5 A2		        lda zpweap
   212 3280 0D AE 33		        ora wp_mask             ; add to owned weapons
   213 3283 85 A2		        sta zpweap
   214 				        ; Auto-switch ONLY if weapon is NEW
   215 3285 BD 28 31		        lda pk_type,x
   216 3288 C9 0B		        cmp #PK_PISTOL
   217 328A D0 21		        bne ?wshotg
   218 328C AD AF 33		        lda wp_had
   219 328F D0 04		        bne ?wpok2
   220 3291 A9 01		        lda #WP_PISTOL
   221 3293 85 A6		        sta zpwcur
   222 3295 A5 99		?wpok2  lda zpammo
   223 3297 18			        clc
   224 3298 69 14		        adc #20
   225 329A C9 C8		        cmp #200
   226 329C 90 02		        bcc ?wpok
   227 329E A9 C8		        lda #200
   228 32A0 85 99		?wpok   sta zpammo
   229 32A2 AD AF 33		        lda wp_had
   230 32A5 D0 03		        bne ?wp_ammo1
   231 32A7 4C 5B 33		        jmp ?sfx_wpn
   232 32AA 4C 63 33		?wp_ammo1 jmp ?sfx_ammo
   233 32AD C9 09		?wshotg cmp #PK_SHOTGUN
   234 32AF D0 21		        bne ?wchain
   235 32B1 AD AF 33		        lda wp_had
   236 32B4 D0 04		        bne ?wsok2
   237 32B6 A9 02		        lda #WP_SHOTGUN
   238 32B8 85 A6		        sta zpwcur
   239 32BA A5 A3		?wsok2  lda zpshells
   240 32BC 18			        clc
   241 32BD 69 08		        adc #8
   242 32BF C9 32		        cmp #50
   243 32C1 90 02		        bcc ?wsok
   244 32C3 A9 32		        lda #50
   245 32C5 85 A3		?wsok   sta zpshells
   246 32C7 AD AF 33		        lda wp_had
   247 32CA D0 03		        bne ?jsfx_ammo2
   248 32CC 4C 5B 33		        jmp ?sfx_wpn
   249 32CF 4C 63 33		?jsfx_ammo2 jmp ?sfx_ammo
   250 32D2 C9 0C		?wchain cmp #PK_CHAINGUN
   251 32D4 D0 1E		        bne ?wrocket
   252 32D6 AD AF 33		        lda wp_had
   253 32D9 D0 04		        bne ?wcok2
   254 32DB A9 03		        lda #WP_CHAINGUN
   255 32DD 85 A6		        sta zpwcur
   256 32DF A5 99		?wcok2  lda zpammo
   257 32E1 18			        clc
   258 32E2 69 14		        adc #20
   259 32E4 C9 C8		        cmp #200
   260 32E6 90 02		        bcc ?wcok
   261 32E8 A9 C8		        lda #200
   262 32EA 85 99		?wcok   sta zpammo
   263 32EC AD AF 33		        lda wp_had
   264 32EF D0 72		        bne ?sfx_ammo
   265 32F1 4C 5B 33		        jmp ?sfx_wpn
   266 32F4 C9 0D		?wrocket cmp #PK_ROCKETL
   267 32F6 D0 1E		        bne ?wplasma
   268 32F8 AD AF 33		        lda wp_had
   269 32FB D0 04		        bne ?wrok2
   270 32FD A9 04		        lda #WP_ROCKET
   271 32FF 85 A6		        sta zpwcur
   272 3301 A5 A4		?wrok2  lda zprockets
   273 3303 18			        clc
   274 3304 69 02		        adc #2
   275 3306 C9 32		        cmp #50
   276 3308 90 02		        bcc ?wrok
   277 330A A9 32		        lda #50
   278 330C 85 A4		?wrok   sta zprockets
   279 330E AD AF 33		        lda wp_had
   280 3311 D0 50		        bne ?sfx_ammo
   281 3313 4C 5B 33		        jmp ?sfx_wpn
   282 3316 C9 0F		?wplasma cmp #PK_PLASMAGUN
   283 3318 D0 20		        bne ?wbfg
   284 331A AD AF 33		        lda wp_had
   285 331D D0 04		        bne ?wplok2
   286 331F A9 05		        lda #WP_PLASMA
   287 3321 85 A6		        sta zpwcur
   288 3323 A5 A5		?wplok2 lda zpcells
   289 3325 18			        clc
   290 3326 69 28		        adc #40
   291 3328 B0 04		        bcs ?wpl_max
   292 332A C9 C8		        cmp #200
   293 332C 90 02		        bcc ?wplok
   294 332E A9 C8		?wpl_max lda #200
   295 3330 85 A5		?wplok  sta zpcells
   296 3332 AD AF 33		        lda wp_had
   297 3335 D0 22		        bne ?jsfx_ammo
   298 3337 4C 5B 33		        jmp ?sfx_wpn
   299 333A C9 11		?wbfg   cmp #PK_BFG
   300 333C D0 2A		        bne ?collect
   301 333E AD AF 33		        lda wp_had
   302 3341 D0 04		        bne ?wbok2
   303 3343 A9 06		        lda #WP_BFG
   304 3345 85 A6		        sta zpwcur
   305 3347 A5 A5		?wbok2  lda zpcells
   306 3349 18			        clc
   307 334A 69 28		        adc #40
   308 334C B0 04		        bcs ?wb_max
   309 334E C9 C8		        cmp #200
   310 3350 90 02		        bcc ?wbok
   311 3352 A9 C8		?wb_max lda #200
   312 3354 85 A5		?wbok   sta zpcells
   313 3356 AD AF 33		        lda wp_had
   314 3359			?jsfx_ammo
   315 3359 D0 08		        bne ?sfx_ammo
   316 335B			?sfx_wpn
   317 335B A2 08		        ldx #SFX_WPNUP
   318 335D 20 A2 5A		        jsr snd_play
   319 3360 4C 68 33		        jmp ?collect
   320 3363			?sfx_ammo
   321 3363 A2 01		        ldx #SFX_ITEMUP
   322 3365 20 A2 5A		        jsr snd_play
   323 3368			?collect
   324 				        ; Mark dirty tiles under pickup so background restores
   325 3368 AE AD 33		        ldx pk_tmp
   326 336B BD F8 30		        lda pk_x,x
   327 336E 85 D0		        sta zdx
   328 3370 BD 04 31		        lda pk_xhi,x
   329 3373 85 D1		        sta zdxh
   330 3375 BD 10 31		        lda pk_y,x
   331 3378 38			        sec
   332 3379 E9 10		        sbc #16
   333 337B 85 80		        sta zt
   334 337D BD 1C 31		        lda pk_yhi,x
   335 3380 E9 00		        sbc #0
   336 3382 90 15		        bcc ?skipmark
   337 3384 AA			        tax                 ; X = Y high byte
   338 3385 A5 80		        lda zt              ; A = Y low byte
   339 3387 20 85 A5		        jsr calc_scr_y
   340 338A 90 0D		        bcc ?skipmark
   341 338C 85 D2		        sta zdy
   342 338E A9 10		        lda #16
   343 3390 8D 5D 55		        sta md_w
   344 3393 8D 5E 55		        sta md_h
   345 3396 20 90 54		        jsr mark_dirty_sprite
   346 3399			?skipmark
   347 3399 AE AD 33		        ldx pk_tmp
   348 339C A9 00		        lda #0
   349 339E 9D EC 30		        sta pk_act,x
   350 				        ; Check if this pickup triggers a switch action
   351 33A1 20 B0 33		        jsr pickup_check_trigger
   352 33A4 E8			?nx     inx
   353 33A5 E0 0C		        cpx #MAX_PICKUPS
   354 33A7 D0 01		        bne ?lp2
   355 33A9 60			        rts
   356 33AA 4C 65 31		?lp2    jmp ?lp
   357 33AD 00			pk_tmp  dta 0
   358 33AE 00			wp_mask dta 0
   359 33AF 00			wp_had  dta 0
   360 				.endp
   361
   362 				; ============================================
   363 				; PICKUP TRIGGER - check if collected pickup has a linked action
   364 				; Converts pickup pixel pos to tile col/row, looks up in sw_col/sw_row
   365 				; ============================================
   366 33B0			.proc pickup_check_trigger
   367 33B0 AE AD 33		        ldx update_pickups.pk_tmp
   368 				        ; tile col = pk_xhi * 16 + pk_x / 16
   369 33B3 BD 04 31		        lda pk_xhi,x
   370 33B6 0A			        asl
   371 33B7 0A			        asl
   372 33B8 0A			        asl
   373 33B9 0A			        asl                     ; xhi * 16
   374 33BA 8D ED 33		        sta pct_col
   375 33BD BD F8 30		        lda pk_x,x
   376 33C0 4A			        lsr
   377 33C1 4A			        lsr
   378 33C2 4A			        lsr
   379 33C3 4A			        lsr                     ; x / 16
   380 33C4 0D ED 33		        ora pct_col
   381 33C7 8D C9 57		        sta sw_found_col
   382 				        ; tile row = (pk_yhi:pk_y - 16) / 16
   383 33CA BD 10 31		        lda pk_y,x
   384 33CD 38			        sec
   385 33CE E9 10		        sbc #16
   386 33D0 85 80		        sta zt
   387 33D2 BD 1C 31		        lda pk_yhi,x
   388 33D5 E9 00		        sbc #0              ; propagate borrow
   389 				        ; A = adjusted hi, zt = adjusted lo
   390 				        ; row = (A << 4) | (zt >> 4)
   391 33D7 0A			        asl
   392 33D8 0A			        asl
   393 33D9 0A			        asl
   394 33DA 0A			        asl
   395 33DB 8D CA 57		        sta sw_found_row
   396 33DE A5 80		        lda zt
   397 33E0 4A			        lsr
   398 33E1 4A			        lsr
   399 33E2 4A			        lsr
   400 33E3 4A			        lsr
   401 33E4 0D CA 57		        ora sw_found_row
   402 33E7 8D CA 57		        sta sw_found_row
   403 				        ; Look up in switch table and execute
   404 33EA 4C 30 59		        jmp switch_do_target
   405 33ED 00			pct_col dta 0
   406 				.endp
   407
   408 				; ============================================
   409 				; RENDER PICKUPS (16x16 sprites)
   410 				; ============================================
   411 33EE			.proc render_pickups
   412 33EE A2 00		        ldx #0
   413 33F0 BD EC 30		?lp     lda pk_act,x
   414 33F3 F0 3F		        beq ?nx
   415 33F5 8E 3A 34		        stx rp_idx
   416 				        ; Screen position
   417 33F8 BD F8 30		        lda pk_x,x
   418 33FB 85 D0		        sta zdx
   419 33FD BD 04 31		        lda pk_xhi,x
   420 3400 85 D1		        sta zdxh
   421 3402 BD 10 31		        lda pk_y,x
   422 3405 38			        sec
   423 3406 E9 10		        sbc #16             ; sprite height offset (16x16)
   424 3408 85 80		        sta zt
   425 340A BD 1C 31		        lda pk_yhi,x
   426 340D E9 00		        sbc #0              ; propagate borrow
   427 340F AA			        tax                 ; X = Y high byte
   428 3410 A5 80		        lda zt              ; A = Y low byte
   429 3412 20 85 A5		        jsr calc_scr_y
   430 3415 90 1A		        bcc ?nx2
   431 3417 85 D2		        sta zdy
   432 				        ; Mark dirty
   433 3419 A9 10		        lda #16
   434 341B 8D 5D 55		        sta md_w
   435 341E 8D 5E 55		        sta md_h
   436 3421 20 90 54		        jsr mark_dirty_sprite
   437 				        ; Get sprite index from type
   438 3424 AE 3A 34		        ldx rp_idx
   439 3427 BD 28 31		        lda pk_type,x
   440 342A AA			        tax
   441 342B BD BD 5E		        lda pk_spr_tab,x
   442 				        ; Draw sprite
   443 342E 20 C8 23		        jsr blit_sprite
   444 3431 AE 3A 34		?nx2    ldx rp_idx
   445 3434 E8			?nx     inx
   446 3435 E0 0C		        cpx #MAX_PICKUPS
   447 3437 D0 B7		        bne ?lp
   448 3439 60			        rts
   449 343A 00			rp_idx  dta 0
   450 				.endp
   451
   452 				; ============================================
   453 				; RENDER PICKUPS - NO DIRTY (for per-frame static redraw)
   454 				; Same as render_pickups but skips mark_dirty_sprite
   455 				; ============================================
   456 343B			.proc render_pickups_nodirty
   457 343B AD E6 53		        lda dirty_any
   458 343E D0 01		        bne ?go
   459 3440 60			        rts                     ; no dirty tiles = no statics to redraw
   460 3441 A2 00		?go     ldx #0
   461 3443 BD EC 30		?lp     lda pk_act,x
   462 3446 F0 7A		        beq ?nx
   463 				        ; Check if pickup overlaps dirty bbox (screen-relative)
   464 3448 8E CB 34		        stx rn_idx
   465 				        ; Screen tile col = world_tile_col - cam_tx
   466 344B BD 04 31		        lda pk_xhi,x
   467 344E 0A			        asl
   468 344F 0A			        asl
   469 3450 0A			        asl
   470 3451 0A			        asl
   471 3452 8D CC 34		        sta rn_tc
   472 3455 BD F8 30		        lda pk_x,x
   473 3458 4A			        lsr
   474 3459 4A			        lsr
   475 345A 4A			        lsr
   476 345B 4A			        lsr
   477 345C 0D CC 34		        ora rn_tc
   478 345F 38			        sec
   479 3460 E5 D6		        sbc cam_tx              ; convert to screen-relative
   480 3462 CD E2 53		        cmp dirty_min_col
   481 3465 90 58		        bcc ?nx2                ; left of dirty area
   482 3467 CD E3 53		        cmp dirty_max_col
   483 346A F0 02		        beq ?col_ok
   484 346C B0 51		        bcs ?nx2                ; right of dirty area
   485 346E			?col_ok ; Tile row = (pk_y - 16) >> 4, screen-relative
   486 346E BD 10 31		        lda pk_y,x
   487 3471 38			        sec
   488 3472 E9 10		        sbc #16
   489 3474 90 49		        bcc ?nx2
   490 3476 4A			        lsr
   491 3477 4A			        lsr
   492 3478 4A			        lsr
   493 3479 4A			        lsr
   494 347A 38			        sec
   495 347B E5 DE		        sbc cam_ty
   496 347D CD E4 53		        cmp dirty_min_row
   497 3480 90 3D		        bcc ?nx2                ; above dirty area
   498 3482 CD E5 53		        cmp dirty_max_row
   499 3485 F0 02		        beq ?row_ok
   500 3487 B0 36		        bcs ?nx2                ; below dirty area
   501 3489			?row_ok ; Inside dirty bbox - redraw (screen coords)
   502 3489 AE CB 34		        ldx rn_idx
   503 348C BD F8 30		        lda pk_x,x
   504 348F 38			        sec
   505 3490 E5 D7		        sbc cam_x_lo
   506 3492 85 D0		        sta zdx
   507 3494 BD 04 31		        lda pk_xhi,x
   508 3497 E5 D8		        sbc cam_x_hi
   509 3499 85 D1		        sta zdxh
   510 349B BD 10 31		        lda pk_y,x
   511 349E 38			        sec
   512 349F E9 10		        sbc #16
   513 34A1 85 80		        sta zt
   514 34A3 BD 1C 31		        lda pk_yhi,x
   515 34A6 E9 00		        sbc #0
   516 34A8 AA			        tax                 ; X = Y high byte
   517 34A9 A5 80		        lda zt              ; A = Y low byte
   518 34AB 20 85 A5		        jsr calc_scr_y
   519 34AE 90 0F		        bcc ?nx2
   520 34B0 85 D2		        sta zdy
   521 34B2 AE CB 34		        ldx rn_idx
   522 34B5 BD 28 31		        lda pk_type,x
   523 34B8 AA			        tax
   524 34B9 BD BD 5E		        lda pk_spr_tab,x
   525 34BC 20 C8 23		        jsr blit_sprite
   526 34BF AE CB 34		?nx2    ldx rn_idx
   527 34C2 E8			?nx     inx
   528 34C3 E0 0C		        cpx #MAX_PICKUPS
   529 34C5 F0 03		        beq ?skip
   530 34C7 4C 43 34		        jmp ?lp
   531 34CA 60			?skip   rts
   532 34CB 00			rn_idx  dta 0
   533 34CC 00			rn_tc   dta 0
   534 				.endp
   317 34CD			        icl 'decorations.asm'
Source: decorations.asm
     1 				;==============================================
     2 				; DOOM2D - Decorations (static map objects)
     3 				; decorations.asm
     4 				;
     5 				; Torches, pillars, lamps, gore
     6 				; Barrel logic is in barrel.asm
     7 				;==============================================
     8
     9 				; Decoration arrays
    10 = 34CD			dc_act  .ds MAX_DECOR            ; 0=dead, 1=active, 2=exploding
    11 = 34D5			dc_x    .ds MAX_DECOR            ; X position (lo)
    12 = 34DD			dc_xhi  .ds MAX_DECOR            ; X position (hi)
    13 = 34E5			dc_orig_tile .ds MAX_DECOR       ; original tile under barrel (for restore)
    14 = 34ED			dc_y    .ds MAX_DECOR            ; Y position (lo)
    15 = 34F5			dc_yhi  .ds MAX_DECOR            ; Y position (hi)
    16 = 34FD			dc_type .ds MAX_DECOR            ; type (DC_xxx)
    17 = 3505			dc_hp   .ds MAX_DECOR            ; hit points (barrels only)
    18 = 350D			dc_timer .ds MAX_DECOR           ; explosion timer
    19
    20 				; dc_wtab, dc_htab, dc_solid moved to $A000 segment (main.asm)
    21
    22 				; Decoration type -> sprite index
    23 3515			dc_spr_tab
    24 3515-3A36> 38		        dta 56                   ; DC_BARREL     -> barrel
    25 3516 3A			        dta 58                   ; DC_TORCH      -> torch
    26 3517 39			        dta 57                   ; DC_PILLAR     -> pillar
    27 3518 3B			        dta 59                   ; DC_LAMP       -> lamp
    28 3519 00			        dta 0                    ; DC_DEADGUY    -> TODO
    29 351A 00			        dta 0                    ; DC_TECHTHING  -> TODO
    30
    31 				; init_decorations is in the overlay segment (end of main.asm)
    32
    33 				; ============================================
    34 				; RENDER DECORATIONS (initial full render)
    35 				; ============================================
    36 351B			.proc render_decorations
    37 351B A2 00		        ldx #0
    38 351D BD CD 34		?lp     lda dc_act,x
    39 3520 F0 65		        beq ?nx
    40 3522 8E 8D 35		        stx rd_idx
    41 3525 BD D5 34		        lda dc_x,x
    42 3528 38			        sec
    43 3529 E5 D7		        sbc cam_x_lo
    44 352B 85 D0		        sta zdx
    45 352D BD DD 34		        lda dc_xhi,x
    46 3530 E5 D8		        sbc cam_x_hi
    47 3532 85 D1		        sta zdxh
    48 3534 BD FD 34		        lda dc_type,x
    49 3537 A8			        tay
    50 3538 BD ED 34		        lda dc_y,x
    51 353B 38			        sec
    52 353C F9 3D A5		        sbc dc_htab,y
    53 353F 85 80		        sta zt
    54 3541 BD F5 34		        lda dc_yhi,x
    55 3544 E9 00		        sbc #0              ; propagate borrow
    56 3546 AA			        tax                 ; X = Y high byte
    57 3547 A5 80		        lda zt              ; A = Y low byte
    58 3549 20 85 A5		        jsr calc_scr_y
    59 354C 90 36		        bcc ?nx2
    60 354E 85 D2		        sta zdy
    61 3550 AE 8D 35		        ldx rd_idx
    62 3553 BD FD 34		        lda dc_type,x
    63 3556 A8			        tay
    64 3557 B9 37 A5		        lda dc_wtab,y
    65 355A 8D 5D 55		        sta md_w
    66 355D B9 3D A5		        lda dc_htab,y
    67 3560 8D 5E 55		        sta md_h
    68 3563 20 90 54		        jsr mark_dirty_sprite
    69 3566 AE 8D 35		        ldx rd_idx
    70 3569 BD CD 34		        lda dc_act,x
    71 356C C9 02		        cmp #2
    72 356E D0 07		        bne ?draw
    73 3570 BD 0D 35		        lda dc_timer,x
    74 3573 29 04		        and #4
    75 3575 F0 0D		        beq ?nx2
    76 3577 AE 8D 35		?draw   ldx rd_idx
    77 357A BD FD 34		        lda dc_type,x
    78 357D AA			        tax
    79 357E BD 15 35		        lda dc_spr_tab,x
    80 3581 20 C8 23		        jsr blit_sprite
    81 3584 AE 8D 35		?nx2    ldx rd_idx
    82 3587 E8			?nx     inx
    83 3588 E0 08		        cpx #MAX_DECOR
    84 358A D0 91		        bne ?lp
    85 358C 60			        rts
    86 358D 00			rd_idx  dta 0
    87 				.endp
    88
    89 				; ============================================
    90 				; RENDER DECORATIONS - NO DIRTY (per-frame static redraw)
    91 				; ============================================
    92 358E			.proc render_decor_nodirty
    93 358E AD E6 53		        lda dirty_any
    94 3591 D0 01		        bne ?go
    95 3593 60			        rts
    96 3594 A2 00		?go     ldx #0
    97 3596 BD CD 34		?lp     lda dc_act,x
    98 3599 C9 01		        cmp #1
    99 359B F0 03		        beq ?act
   100 359D 4C 24 36		        jmp ?nx
   101 35A0 8E 2D 36		?act    stx rn_idx
   102 				        ; Screen tile col = world_tile_col - cam_tx
   103 35A3 BD DD 34		        lda dc_xhi,x
   104 35A6 0A			        asl
   105 35A7 0A			        asl
   106 35A8 0A			        asl
   107 35A9 0A			        asl
   108 35AA 8D 2E 36		        sta rn_tc
   109 35AD BD D5 34		        lda dc_x,x
   110 35B0 4A			        lsr
   111 35B1 4A			        lsr
   112 35B2 4A			        lsr
   113 35B3 4A			        lsr
   114 35B4 0D 2E 36		        ora rn_tc
   115 35B7 38			        sec
   116 35B8 E5 D6		        sbc cam_tx
   117 35BA CD E2 53		        cmp dirty_min_col
   118 35BD 90 62		        bcc ?nx2
   119 35BF CD E3 53		        cmp dirty_max_col
   120 35C2 F0 02		        beq ?col_ok
   121 35C4 B0 5B		        bcs ?nx2
   122 35C6 BD FD 34		?col_ok lda dc_type,x
   123 35C9 A8			        tay
   124 35CA BD ED 34		        lda dc_y,x
   125 35CD 38			        sec
   126 35CE F9 3D A5		        sbc dc_htab,y
   127 35D1 90 4E		        bcc ?nx2
   128 35D3 4A			        lsr
   129 35D4 4A			        lsr
   130 35D5 4A			        lsr
   131 35D6 4A			        lsr
   132 35D7 38			        sec
   133 35D8 E5 DE		        sbc cam_ty
   134 35DA CD E4 53		        cmp dirty_min_row
   135 35DD 90 42		        bcc ?nx2
   136 35DF CD E5 53		        cmp dirty_max_row
   137 35E2 F0 02		        beq ?row_ok
   138 35E4 B0 3B		        bcs ?nx2
   139 35E6 AE 2D 36		?row_ok ldx rn_idx
   140 35E9 BD D5 34		        lda dc_x,x
   141 35EC 38			        sec
   142 35ED E5 D7		        sbc cam_x_lo
   143 35EF 85 D0		        sta zdx
   144 35F1 BD DD 34		        lda dc_xhi,x
   145 35F4 E5 D8		        sbc cam_x_hi
   146 35F6 85 D1		        sta zdxh
   147 35F8 BD FD 34		        lda dc_type,x
   148 35FB A8			        tay
   149 35FC BD ED 34		        lda dc_y,x
   150 35FF 38			        sec
   151 3600 F9 3D A5		        sbc dc_htab,y
   152 3603 85 80		        sta zt
   153 3605 BD F5 34		        lda dc_yhi,x
   154 3608 E9 00		        sbc #0              ; propagate borrow
   155 360A AA			        tax                 ; X = Y high byte
   156 360B A5 80		        lda zt              ; A = Y low byte
   157 360D 20 85 A5		        jsr calc_scr_y
   158 3610 90 0F		        bcc ?nx2
   159 3612 85 D2		        sta zdy
   160 3614 AE 2D 36		        ldx rn_idx
   161 3617 BD FD 34		        lda dc_type,x
   162 361A AA			        tax
   163 361B BD 15 35		        lda dc_spr_tab,x
   164 361E 20 C8 23		        jsr blit_sprite
   165 3621 AE 2D 36		?nx2    ldx rn_idx
   166 3624 E8			?nx     inx
   167 3625 E0 08		        cpx #MAX_DECOR
   168 3627 F0 03		        beq ?skip
   169 3629 4C 96 35		        jmp ?lp
   170 362C 60			?skip   rts
   171 362D 00			rn_idx  dta 0
   172 362E 00			rn_tc   dta 0
   173 				.endp
   174
   175 				; ============================================
   176 				; RENDER EXPLODING BARRELS (game loop)
   177 				; ============================================
   178 362F			.proc render_exploding
   179 362F A2 00		        ldx #0
   180 3631 BD CD 34		?lp     lda dc_act,x
   181 3634 C9 02		        cmp #2
   182 3636 D0 03		        bne ?nxj
   183 3638 4C 3E 36		        jmp ?draw
   184 363B 4C C7 36		?nxj    jmp ?nx
   185 363E			?draw
   186 363E 8E D0 36		        stx rx_idx
   187 3641 BD D5 34		        lda dc_x,x
   188 3644 38			        sec
   189 3645 E5 D7		        sbc cam_x_lo
   190 3647 85 D0		        sta zdx
   191 3649 BD DD 34		        lda dc_xhi,x
   192 364C E5 D8		        sbc cam_x_hi
   193 364E 85 D1		        sta zdxh
   194 3650 BD FD 34		        lda dc_type,x
   195 3653 A8			        tay
   196 3654 BD ED 34		        lda dc_y,x
   197 3657 38			        sec
   198 3658 F9 3D A5		        sbc dc_htab,y
   199 365B 85 80		        sta zt
   200 365D BD F5 34		        lda dc_yhi,x
   201 3660 E9 00		        sbc #0              ; propagate borrow
   202 3662 AA			        tax                 ; X = Y high byte
   203 3663 A5 80		        lda zt              ; A = Y low byte
   204 3665 20 85 A5		        jsr calc_scr_y
   205 3668 90 5A		        bcc ?nx2
   206 366A 85 D2		        sta zdy
   207 366C AE D0 36		        ldx rx_idx
   208 366F BD FD 34		        lda dc_type,x
   209 3672 A8			        tay
   210 3673 B9 37 A5		        lda dc_wtab,y
   211 3676 8D 5D 55		        sta md_w
   212 3679 B9 3D A5		        lda dc_htab,y
   213 367C 8D 5E 55		        sta md_h
   214 367F 20 90 54		        jsr mark_dirty_sprite
   215 3682 AE D0 36		        ldx rx_idx
   216 3685 BD CD 34		        lda dc_act,x
   217 3688 C9 03		        cmp #3
   218 368A F0 38		        beq ?nx2
   219 368C BD FD 34		        lda dc_type,x
   220 368F C9 00		        cmp #DC_BARREL
   221 3691 D0 20		        bne ?blink
   222 				        ; Barrel explosion: timer 30-21=exp1, 20-11=exp2, 10-1=exp3+blink
   223 3693 BD 0D 35		        lda dc_timer,x
   224 3696 C9 15		        cmp #21
   225 3698 B0 0C		        bcs ?exp1
   226 369A C9 0B		        cmp #11
   227 369C B0 0D		        bcs ?exp2
   228 369E 4A			        lsr
   229 369F B0 23		        bcs ?nx2
   230 36A1 A9 7E		        lda #SPR_BARREL_EXP3
   231 36A3 4C AD 36		        jmp ?draw_exp
   232 36A6 A9 7C		?exp1   lda #SPR_BARREL_EXP1
   233 36A8 4C AD 36		        jmp ?draw_exp
   234 36AB A9 7D		?exp2   lda #SPR_BARREL_EXP2
   235 36AD			?draw_exp
   236 36AD 20 C8 23		        jsr blit_sprite
   237 36B0 4C C4 36		        jmp ?nx2
   238 36B3 BD 0D 35		?blink  lda dc_timer,x
   239 36B6 29 04		        and #4
   240 36B8 F0 0A		        beq ?nx2
   241 36BA BD FD 34		        lda dc_type,x
   242 36BD AA			        tax
   243 36BE BD 15 35		        lda dc_spr_tab,x
   244 36C1 20 C8 23		        jsr blit_sprite
   245 36C4 AE D0 36		?nx2    ldx rx_idx
   246 36C7 E8			?nx     inx
   247 36C8 E0 08		        cpx #MAX_DECOR
   248 36CA F0 03		        beq ?ret
   249 36CC 4C 31 36		        jmp ?lp
   250 36CF 60			?ret    rts
   251 36D0 00			rx_idx  dta 0
   252 				.endp
   318 36D1			        icl 'barrel.asm'
Source: barrel.asm
     1 				;==============================================
     2 				; DOOM2D - Barrel logic
     3 				; barrel.asm
     4 				;
     5 				; Barrel explosion, chain reactions, damage,
     6 				; projectile collision, map solid tile management.
     7 				; Uses decoration arrays from decorations.asm.
     8 				;==============================================
     9
    10 				; ============================================
    11 				; UPDATE DECORATIONS (barrel explosion logic)
    12 				; ============================================
    13 36D1			.proc update_decorations
    14 36D1 A2 00		        ldx #0
    15 36D3 BD CD 34		?lp     lda dc_act,x
    16 36D6 C9 02		        cmp #2
    17 36D8 D0 03		        bne ?not_exp
    18 36DA 4C E0 36		        jmp ?exploding
    19 36DD			?not_exp
    20 36DD 4C B0 37		        jmp ?nx
    21 36E0			?exploding
    22 36E0 BD 0D 35		        lda dc_timer,x
    23 36E3 C9 1E		        cmp #30
    24 36E5 D0 79		        bne ?chk_dmg
    25 				        ; Timer=30: explosion visual + sound + chain (no damage yet)
    26 36E7 8E B9 37		        stx ud_idx
    27 36EA 20 22 3A		        jsr barrel_clear_solid
    28 36ED A9 00		        lda #0
    29 36EF 85 CB		        sta snd_lock
    30 36F1 A2 0A		        ldx #SFX_BAREXP
    31 36F3 20 A2 5A		        jsr snd_play
    32 				        ; Chain: set nearby live barrels to exploding
    33 36F6 AE B9 37		        ldx ud_idx
    34 36F9 A0 00		        ldy #0
    35 36FB CC B9 37		?clp    cpy ud_idx
    36 36FE F0 55		        beq ?cnx
    37 3700 B9 CD 34		        lda dc_act,y
    38 3703 C9 01		        cmp #1
    39 3705 D0 4E		        bne ?cnx
    40 3707 B9 FD 34		        lda dc_type,y
    41 370A C9 00		        cmp #DC_BARREL
    42 370C D0 47		        bne ?cnx
    43 				        ; 16-bit X distance
    44 370E BD D5 34		        lda dc_x,x
    45 3711 38			        sec
    46 3712 F9 D5 34		        sbc dc_x,y
    47 3715 8D BA 37		        sta ud_tmp
    48 3718 BD DD 34		        lda dc_xhi,x
    49 371B F9 DD 34		        sbc dc_xhi,y
    50 371E F0 12		        beq ?cxchk
    51 3720 C9 FF		        cmp #$FF
    52 3722 D0 31		        bne ?cnx
    53 3724 AD BA 37		        lda ud_tmp
    54 3727 F0 2C		        beq ?cnx               ; lo=0 → distance=256, too far
    55 3729 A9 00		        lda #0
    56 372B 38			        sec
    57 372C ED BA 37		        sbc ud_tmp
    58 372F 8D BA 37		        sta ud_tmp
    59 3732 AD BA 37		?cxchk  lda ud_tmp
    60 3735 C9 20		        cmp #BARREL_RADIUS
    61 3737 B0 1C		        bcs ?cnx
    62 3739 BD ED 34		        lda dc_y,x
    63 373C 38			        sec
    64 373D F9 ED 34		        sbc dc_y,y
    65 3740 10 05		        bpl ?cya
    66 3742 49 FF		        eor #$FF
    67 3744 18			        clc
    68 3745 69 01		        adc #1
    69 3747 C9 20		?cya    cmp #BARREL_RADIUS
    70 3749 B0 0A		        bcs ?cnx
    71 374B A9 02		        lda #2
    72 374D 99 CD 34		        sta dc_act,y
    73 3750 A9 20		        lda #32
    74 3752 99 0D 35		        sta dc_timer,y
    75 3755 C8			?cnx    iny
    76 3756 C0 08		        cpy #MAX_DECOR
    77 3758 D0 A1		        bne ?clp
    78 375A AE B9 37		        ldx ud_idx
    79 375D 4C 6F 37		        jmp ?no_fire
    80 3760			?chk_dmg
    81 3760 C9 14		        cmp #20
    82 3762 D0 0B		        bne ?no_fire
    83 				        ; Timer=20: apply radius damage (~0.2s after explosion)
    84 3764 8E B9 37		        stx ud_idx
    85 3767 8A			        txa
    86 3768 A8			        tay
    87 3769 20 BB 37		        jsr barrel_explode
    88 376C AE B9 37		        ldx ud_idx
    89 376F			?no_fire
    90 376F DE 0D 35		        dec dc_timer,x
    91 3772 D0 39		        bne ?nx2
    92 				        ; Timer=0: barrel disappears
    93 3774 BD D5 34		        lda dc_x,x
    94 3777 85 D0		        sta zdx
    95 3779 BD DD 34		        lda dc_xhi,x
    96 377C 85 D1		        sta zdxh
    97 377E 8E B9 37		        stx ud_idx
    98 3781 BD ED 34		        lda dc_y,x
    99 3784 38			        sec
   100 3785 E9 10		        sbc #16
   101 3787 85 80		        sta zt
   102 3789 BD F5 34		        lda dc_yhi,x
   103 378C E9 00		        sbc #0
   104 378E 90 15		        bcc ?skipmark
   105 3790 AA			        tax                 ; X = Y high byte
   106 3791 A5 80		        lda zt              ; A = Y low byte
   107 3793 20 85 A5		        jsr calc_scr_y
   108 3796 90 0D		        bcc ?skipmark
   109 3798 85 D2		        sta zdy
   110 379A A9 10		        lda #16
   111 379C 8D 5D 55		        sta md_w
   112 379F 8D 5E 55		        sta md_h
   113 37A2 20 90 54		        jsr mark_dirty_sprite
   114 37A5			?skipmark
   115 37A5 AE B9 37		        ldx ud_idx
   116 37A8 A9 00		        lda #0
   117 37AA 9D CD 34		        sta dc_act,x
   118 37AD 4C B0 37		?nx2    jmp ?nx
   119 37B0 E8			?nx     inx
   120 37B1 E0 08		        cpx #MAX_DECOR
   121 37B3 F0 03		        beq ?done
   122 37B5 4C D3 36		        jmp ?lp
   123 37B8 60			?done   rts
   124 37B9 00			ud_idx  dta 0
   125 37BA 00			ud_tmp  dta 0
   126 				.endp
   127
   128 				; ============================================
   129 				; BARREL EXPLODE - apply radius damage
   130 				; Input: Y = barrel decoration index
   131 				; ============================================
   132 37BB			.proc barrel_explode
   133 37BB 8C 29 39		        sty bx_idx
   134 37BE AE 29 39		        ldx bx_idx
   135 				        ; --- Radius damage to enemies ---
   136 37C1 A0 00		        ldy #0
   137 37C3 8C 2A 39		?elp    sty bx_eidx
   138 37C6 B9 55 3A		        lda en_act,y
   139 37C9 C9 01		        cmp #1
   140 37CB F0 03		        beq ?eact
   141 37CD 4C A5 38		        jmp ?enx
   142 37D0 AE 29 39		?eact   ldx bx_idx
   143 37D3 BD DD 34		        lda dc_xhi,x
   144 37D6 D9 3D 3A		        cmp enxhi,y
   145 37D9 F0 03		        beq ?exhi_ok
   146 37DB 4C A5 38		        jmp ?enx
   147 37DE			?exhi_ok
   148 37DE BD D5 34		        lda dc_x,x
   149 37E1 38			        sec
   150 37E2 F9 37 3A		        sbc en_x,y
   151 37E5 10 05		        bpl ?exa
   152 37E7 49 FF		        eor #$FF
   153 37E9 18			        clc
   154 37EA 69 01		        adc #1
   155 37EC C9 20		?exa    cmp #BARREL_RADIUS
   156 37EE 90 03		        bcc ?exr_ok
   157 37F0 4C A5 38		        jmp ?enx
   158 37F3 AE 29 39		?exr_ok ldx bx_idx
   159 37F6 BD ED 34		        lda dc_y,x
   160 37F9 38			        sec
   161 37FA F9 43 3A		        sbc en_y,y
   162 37FD 10 05		        bpl ?eya
   163 37FF 49 FF		        eor #$FF
   164 3801 18			        clc
   165 3802 69 01		        adc #1
   166 3804 C9 20		?eya    cmp #BARREL_RADIUS
   167 3806 90 03		        bcc ?eyr_ok
   168 3808 4C A5 38		        jmp ?enx
   169 380B			?eyr_ok ; Enemy in range! Apply damage
   170 380B AC 2A 39		        ldy bx_eidx
   171 380E B9 5B 3A		        lda en_hp,y
   172 3811 8D 2C 39		        sta bx_oldhp
   173 3814 38			        sec
   174 3815 E9 14		        sbc #BARREL_DMG
   175 3817 B0 02		        bcs ?ehok
   176 3819 A9 00		        lda #0
   177 381B 99 5B 3A		?ehok   sta en_hp,y
   178 381E F0 03		        beq ?ekill
   179 3820 4C A5 38		        jmp ?enx
   180 3823			?ekill
   181 				        ; Enemy killed by barrel!
   182 3823 A9 02		        lda #2
   183 3825 99 55 3A		        sta en_act,y
   184 3828 A9 14		        lda #20
   185 382A 99 79 3A		        sta en_dtimer,y
   186 382D A9 00		        lda #0
   187 382F 99 85 3A		        sta en_gib,y
   188 3832 99 8B 3A		        sta envelx,y
   189 				        ; Gib check: only zombie, imp, shotgun
   190 3835 B9 6D 3A		        lda en_type,y
   191 3838 C9 00		        cmp #EN_ZOMBIE
   192 383A F0 0B		        beq ?gchk
   193 383C C9 01		        cmp #EN_IMP
   194 383E F0 07		        beq ?gchk
   195 3840 C9 04		        cmp #EN_SHOTGUN
   196 3842 F0 03		        beq ?gchk
   197 3844 4C 99 38		        jmp ?enx_snd
   198 3847			?gchk   ; overkill = BARREL_DMG - old_hp
   199 3847 A9 14		        lda #BARREL_DMG
   200 3849 38			        sec
   201 384A ED 2C 39		        sbc bx_oldhp
   202 				        ; threshold = overkill * 3 + 10
   203 384D 8D 2C 39		        sta bx_oldhp
   204 3850 0A			        asl
   205 3851 18			        clc
   206 3852 6D 2C 39		        adc bx_oldhp           ; *3
   207 3855 69 0A		        adc #10                ; +10 base
   208 3857 8D 2C 39		        sta bx_oldhp
   209 385A A5 90		        lda zfr
   210 385C 45 14		        eor RTCLOK3
   211 385E 29 3F		        and #$3F
   212 3860 CD 2C 39		        cmp bx_oldhp
   213 3863 B0 34		        bcs ?enx_snd
   214 				        ; Gib! Set flag + knockback away from barrel
   215 3865 A9 01		        lda #1
   216 3867 99 85 3A		        sta en_gib,y
   217 386A A9 FE		        lda #$FE
   218 386C 99 7F 3A		        sta envely,y
   219 386F AE 29 39		        ldx bx_idx
   220 3872 B9 37 3A		        lda en_x,y
   221 3875 DD D5 34		        cmp dc_x,x
   222 3878 B0 04		        bcs ?bkr
   223 387A A9 FE		        lda #$FE
   224 387C D0 02		        bne ?bkset
   225 387E A9 02		?bkr    lda #2
   226 3880 99 8B 3A		?bkset  sta envelx,y
   227 				        ; Gib sound
   228 3883 8C 2A 39		        sty bx_eidx
   229 3886 A9 00		        lda #0
   230 3888 85 CB		        sta snd_lock
   231 388A A2 0B		        ldx #SFX_SLOP
   232 388C 20 A2 5A		        jsr snd_play
   233 388F A9 08		        lda #8
   234 3891 85 CB		        sta snd_lock
   235 3893 AC 2A 39		        ldy bx_eidx
   236 3896 4C A5 38		        jmp ?enx
   237 3899			?enx_snd
   238 3899 8C 2A 39		        sty bx_eidx
   239 389C AE 2A 39		        ldx bx_eidx
   240 389F 20 91 5B		        jsr play_enemy_death
   241 38A2 AC 2A 39		        ldy bx_eidx
   242 38A5 AC 2A 39		?enx    ldy bx_eidx
   243 38A8 C8			        iny
   244 38A9 C0 06		        cpy #MAX_ENEMIES
   245 38AB F0 03		        beq ?edone
   246 38AD 4C C3 37		        jmp ?elp
   247 38B0			?edone
   248 				        ; --- Radius damage to player ---
   249 				        ; --- Player splash damage from barrel explosion ---
   250 				        ; Skip if barrel and player on different X hi pages
   251 38B0 AE 29 39		        ldx bx_idx
   252 38B3 BD DD 34		        lda dc_xhi,x
   253 38B6 C5 9C		        cmp zpx_hi
   254 38B8 D0 6E		        bne ?ret
   255 				        ; X distance: |barrel_x - player_x|
   256 38BA BD D5 34		        lda dc_x,x
   257 38BD 38			        sec
   258 38BE E5 91		        sbc zpx
   259 38C0 10 05		        bpl ?pxa
   260 38C2 49 FF		        eor #$FF                ; negate (absolute value)
   261 38C4 18			        clc
   262 38C5 69 01		        adc #1
   263 38C7 C9 20		?pxa    cmp #BARREL_RADIUS      ; too far horizontally?
   264 38C9 B0 5D		        bcs ?ret
   265 38CB 8D 2B 39		        sta bx_dist
   266 				        ; Y distance: |barrel_y - player_y| (16-bit hi check)
   267 38CE BD F5 34		        lda dc_yhi,x
   268 38D1 C5 9D		        cmp zpy_hi
   269 38D3 D0 53		        bne ?ret
   270 38D5 BD ED 34		        lda dc_y,x
   271 38D8 38			        sec
   272 38D9 E5 92		        sbc zpy
   273 38DB 10 05		        bpl ?pya
   274 38DD 49 FF		        eor #$FF
   275 38DF 18			        clc
   276 38E0 69 01		        adc #1
   277 38E2 C9 20		?pya    cmp #BARREL_RADIUS      ; too far vertically?
   278 38E4 B0 42		        bcs ?ret
   279 				        ; Use max(dx, dy) as distance
   280 38E6 CD 2B 39		        cmp bx_dist
   281 38E9 B0 03		        bcs ?dok
   282 38EB AD 2B 39		        lda bx_dist
   283 38EE 8D 2B 39		?dok    sta bx_dist
   284 				        ; Damage = BARREL_PLR_MAX - dist*3 (farther = less damage)
   285 38F1 0A			        asl                     ; dist * 2
   286 38F2 18			        clc
   287 38F3 6D 2B 39		        adc bx_dist             ; dist * 3
   288 38F6 8D 2B 39		        sta bx_dist
   289 38F9 A9 64		        lda #BARREL_PLR_MAX     ; max damage at point blank
   290 38FB 38			        sec
   291 38FC ED 2B 39		        sbc bx_dist             ; reduce by distance
   292 38FF 90 04		        bcc ?pmin               ; underflow = minimum damage
   293 3901 C9 01		        cmp #1
   294 3903 B0 02		        bcs ?papply
   295 3905 A9 01		?pmin   lda #1                  ; minimum 1 damage
   296 3907 8D 2B 39		?papply sta bx_dist
   297 390A AD 2B 39		        lda bx_dist
   298 390D 20 9E 30		        jsr player_take_damage
   299 				        ; --- Horizontal knockback: push player away from barrel ---
   300 				        ; Direction based on player position relative to barrel
   301 3910 AE 29 39		        ldx bx_idx
   302 3913 A5 91		        lda zpx
   303 3915 DD D5 34		        cmp dc_x,x
   304 3918 B0 0A		        bcs ?kb_r
   305 				        ; Player is left of barrel → push left (negative velocity)
   306 391A A9 00		        lda #0
   307 391C 38			        sec
   308 391D E9 06		        sbc #BARREL_KB_X
   309 391F 85 93		        sta zpvx
   310 3921 4C 28 39		        jmp ?ret
   311 3924			?kb_r   ; Player is right of barrel → push right (positive velocity)
   312 3924 A9 06		        lda #BARREL_KB_X
   313 3926 85 93		        sta zpvx
   314 3928 60			?ret    rts
   315 3929 00			bx_idx  dta 0
   316 392A 00			bx_eidx dta 0
   317 392B 00			bx_dist dta 0
   318 392C 00			bx_oldhp dta 0
   319 				.endp
   320
   321 				; ============================================
   322 				; CHECK PROJECTILE HIT ON BARRELS
   323 				; Input: X = projectile index
   324 				; Output: C=1 if hit a barrel
   325 				; ============================================
   326 392D			.proc check_proj_barrels
   327 392D 8E C5 39		        stx cb_proj
   328 3930 A0 00		        ldy #0
   329 3932 B9 CD 34		?lp     lda dc_act,y
   330 3935 C9 01		        cmp #1
   331 3937 F0 03		        beq ?act
   332 3939 4C BB 39		        jmp ?nx
   333 393C			?act
   334 393C B9 FD 34		        lda dc_type,y
   335 393F C9 00		        cmp #DC_BARREL
   336 3941 F0 03		        beq ?isbar
   337 3943 4C BB 39		        jmp ?nx
   338 3946			?isbar
   339 				        ; Bounding box X overlap: proj(8px) vs barrel(16px)
   340 				        ; delta = proj_x - dc_x (16-bit)
   341 				        ; hit if: hi=0 and lo<16 (proj right of barrel left)
   342 				        ;     or: hi=$FF and lo>=248 (proj left edge within 8px of barrel left)
   343 3946 AE C5 39		        ldx cb_proj
   344 3949 BD 1E 2D		        lda proj_x,x
   345 394C 38			        sec
   346 394D F9 D5 34		        sbc dc_x,y
   347 3950 8D C6 39		        sta cb_tmp
   348 3953 BD 22 2D		        lda proj_xh,x
   349 3956 F9 DD 34		        sbc dc_xhi,y
   350 3959 D0 0A		        bne ?xneg
   351 				        ; hi=0: proj_x >= dc_x, check if proj_x < dc_x+16
   352 395B AD C6 39		        lda cb_tmp
   353 395E C9 10		        cmp #16
   354 3960 B0 59		        bcs ?nx
   355 3962 4C 70 39		        jmp ?xhit
   356 3965 C9 FF		?xneg   cmp #$FF
   357 3967 D0 52		        bne ?nx                ; too far
   358 				        ; hi=$FF: proj_x < dc_x, check if proj_x+8 > dc_x
   359 3969 AD C6 39		        lda cb_tmp             ; 256 - |delta|
   360 396C C9 F9		        cmp #249               ; 256 - 7: proj right edge touches barrel left
   361 396E 90 4B		        bcc ?nx
   362 3970			?xhit
   363 3970 BD 2A 2D		        lda proj_yhi,x
   364 3973 D9 F5 34		        cmp dc_yhi,y
   365 3976 D0 43		        bne ?nx             ; different hi bytes = too far
   366 3978 BD 26 2D		        lda proj_y,x
   367 397B 38			        sec
   368 397C F9 ED 34		        sbc dc_y,y
   369 397F 10 05		        bpl ?ya
   370 3981 49 FF		        eor #$FF
   371 3983 18			        clc
   372 3984 69 01		        adc #1
   373 3986 C9 10		?ya     cmp #16
   374 3988 B0 31		        bcs ?nx
   375 				        ; Hit! Damage barrel
   376 398A AE C5 39		        ldx cb_proj
   377 398D BD 32 2D		        lda proj_dmg,x
   378 3990 85 81		        sta zt2
   379 3992 B9 05 35		        lda dc_hp,y
   380 3995 38			        sec
   381 3996 E5 81		        sbc zt2
   382 3998 B0 02		        bcs ?hok
   383 399A A9 00		        lda #0
   384 399C 99 05 35		?hok    sta dc_hp,y
   385 399F D0 0A		        bne ?hit
   386 39A1 A9 02		        lda #2
   387 39A3 99 CD 34		        sta dc_act,y
   388 39A6 A9 1F		        lda #31
   389 39A8 99 0D 35		        sta dc_timer,y
   390 39AB AE C5 39		?hit    ldx cb_proj
   391 39AE 20 20 30		        jsr rocket_splash_player
   392 39B1 AE C5 39		        ldx cb_proj
   393 39B4 A9 00		        lda #0
   394 39B6 9D 1A 2D		        sta proj_a,x
   395 39B9 38			        sec
   396 39BA 60			        rts
   397 39BB C8			?nx     iny
   398 39BC C0 08		        cpy #MAX_DECOR
   399 39BE F0 03		        beq ?miss
   400 39C0 4C 32 39		        jmp ?lp
   401 39C3			?miss
   402 39C3 18			        clc
   403 39C4 60			        rts
   404 39C5 00			cb_proj dta 0
   405 39C6 00			cb_tmp  dta 0
   406 				.endp
   407
   408 				; ============================================
   409 				; BARREL MAP SOLID: write/clear solid tile
   410 				; X = decoration index
   411 				; ============================================
   412 				; Shared: barrel tile col/row → r_col/r_row + calc_map_ptr
   413 39C7			.proc barrel_calc_pos
   414 39C7 BD D5 34		        lda dc_x,x
   415 39CA 4A			        lsr
   416 39CB 4A			        lsr
   417 39CC 4A			        lsr
   418 39CD 4A			        lsr
   419 39CE 8D 35 47		        sta r_col
   420 39D1 BD DD 34		        lda dc_xhi,x
   421 39D4 0A			        asl
   422 39D5 0A			        asl
   423 39D6 0A			        asl
   424 39D7 0A			        asl
   425 39D8 0D 35 47		        ora r_col
   426 39DB 8D 35 47		        sta r_col
   427 				        ; row = (dc_yhi:dc_y - 16) / 16  (undo +16 snap, then tile)
   428 39DE BD ED 34		        lda dc_y,x
   429 39E1 38			        sec
   430 39E2 E9 10		        sbc #16
   431 39E4 8D 34 47		        sta r_row
   432 39E7 BD F5 34		        lda dc_yhi,x
   433 39EA E9 00		        sbc #0              ; propagate borrow
   434 				        ; A = adjusted hi, r_row = adjusted lo
   435 				        ; row = (A << 4) | (r_row >> 4)
   436 39EC 0A			        asl
   437 39ED 0A			        asl
   438 39EE 0A			        asl
   439 39EF 0A			        asl
   440 39F0 85 80		        sta zt
   441 39F2 AD 34 47		        lda r_row
   442 39F5 4A			        lsr
   443 39F6 4A			        lsr
   444 39F7 4A			        lsr
   445 39F8 4A			        lsr
   446 39F9 05 80		        ora zt
   447 39FB 8D 34 47		        sta r_row
   448 39FE 4C B6 25		        jmp calc_map_ptr
   449 				.endp
   450
   451 3A01			.proc barrel_set_solid
   452 3A01 8E 21 3A		        stx bs_idx
   453 3A04 20 C7 39		        jsr barrel_calc_pos
   454 3A07 A0 00		        ldy #0
   455 3A09 B1 D4		        lda (ztptr),y
   456 3A0B C9 0F		        cmp #15
   457 3A0D D0 02		        bne ?orig_ok
   458 3A0F A9 00		        lda #0
   459 3A11			?orig_ok
   460 3A11 AE 21 3A		        ldx bs_idx
   461 3A14 9D E5 34		        sta dc_orig_tile,x
   462 3A17 A9 0F		        lda #15
   463 3A19 A0 00		        ldy #0
   464 3A1B 91 D4		        sta (ztptr),y
   465 3A1D AE 21 3A		        ldx bs_idx
   466 3A20 60			        rts
   467 3A21 00			bs_idx  dta 0
   468 				.endp
   469
   470 3A22			.proc barrel_clear_solid
   471 3A22 8E 36 3A		        stx bc_idx
   472 3A25 20 C7 39		        jsr barrel_calc_pos
   473 3A28 A0 00		        ldy #0
   474 3A2A AE 36 3A		        ldx bc_idx
   475 3A2D BD E5 34		        lda dc_orig_tile,x
   476 3A30 91 D4		        sta (ztptr),y
   477 3A32 AE 36 3A		        ldx bc_idx
   478 3A35 60			        rts
   479 3A36 00			bc_idx  dta 0
   480 				.endp
   319 3A37			        icl 'enemies.asm'
Source: enemies.asm
     1 				;==============================================
     2 				; DOOM2D - Enemies (arrays, init, update, render)
     3 				; enemies.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; ENEMY ARRAYS (indexed by X = 0..MAX_ENEMIES-1)
     8 				; ============================================
     9 = 3A37			en_x    .ds MAX_ENEMIES        ; X position (low byte)
    10 = 3A3D			enxhi   .ds MAX_ENEMIES        ; X position (high byte)
    11 = 3A43			en_y    .ds MAX_ENEMIES        ; Y position (lo)
    12 = 3A49			en_yhi  .ds MAX_ENEMIES        ; Y position (hi)
    13 = 3A4F			en_dir  .ds MAX_ENEMIES        ; direction 0=right, 1=left
    14 = 3A55			en_act  .ds MAX_ENEMIES        ; active flag
    15 = 3A5B			en_hp   .ds MAX_ENEMIES        ; hit points
    16 = 3A61			en_xmin .ds MAX_ENEMIES        ; patrol min X
    17 = 3A67			en_xmax .ds MAX_ENEMIES        ; patrol max X
    18 = 3A6D			en_type .ds MAX_ENEMIES        ; 0=zombie, 1=imp, 2=pinky, 3=caco
    19 = 3A73			en_cooldown .ds MAX_ENEMIES    ; attack cooldown timer
    20 = 3A79			en_dtimer .ds MAX_ENEMIES      ; death animation timer (0=not dying)
    21 = 3A7F			envely   .ds MAX_ENEMIES        ; vertical velocity (signed, for Imp jump)
    22 = 3A85			en_gib   .ds MAX_ENEMIES        ; 1=gibbed (rocket kill), 0=normal death
    23 = 3A8B			envelx   .ds MAX_ENEMIES        ; horizontal velocity (signed, for knockback)
    24 = 3A91			en_atk   .ds MAX_ENEMIES        ; attack timer (0=ready to fire)
    25
    26 				; en_act values: 0=inactive, 1=alive, 2=dying
    27
    28 				; en_base_spr and en_hp_tab are in sprite_defs.asm (auto-generated)
    29
    30 				; ============================================
    31 				; init_enemies is in the overlay segment (end of main.asm)
    32
    33 				; ============================================
    34 				; UPDATE ALL ENEMIES
    35 				; ============================================
    36 3A97			.proc update_enemies
    37 3A97-4502> A2 00		        ldx #0
    38 3A99 BD 55 3A		?lp     lda en_act,x
    39 3A9C D0 03		        bne ?active
    40 3A9E 4C 44 41		        jmp ?nx
    41 3AA1 C9 02		?active cmp #2
    42 3AA3 D0 67		        bne ?alive
    43 				        ; --- Dying: count down death timer + knockback ---
    44 3AA5 DE 79 3A		        dec en_dtimer,x
    45 3AA8 F0 5A		        beq ?dead
    46 				        ; Apply knockback velocity if gibbed
    47 3AAA BD 85 3A		        lda en_gib,x
    48 3AAD F0 52		        beq ?nx_jmp         ; not gibbed, skip movement
    49 				        ; Horizontal knockback (16-bit, signed velocity)
    50 3AAF BD 37 3A		        lda en_x,x
    51 3AB2 18			        clc
    52 3AB3 7D 8B 3A		        adc envelx,x
    53 3AB6 9D 37 3A		        sta en_x,x
    54 3AB9 BD 8B 3A		        lda envelx,x
    55 3ABC 30 0B		        bmi ?kb_neg
    56 3ABE BD 3D 3A		        lda enxhi,x
    57 3AC1 69 00		        adc #0              ; positive: add carry
    58 3AC3 9D 3D 3A		        sta enxhi,x
    59 3AC6 4C D1 3A		        jmp ?kb_vy
    60 3AC9 BD 3D 3A		?kb_neg lda enxhi,x
    61 3ACC 69 FF		        adc #$FF            ; negative: sign extend ($FF + carry)
    62 3ACE 9D 3D 3A		        sta enxhi,x
    63 3AD1			?kb_vy
    64 				        ; Vertical knockback (gravity applied, 16-bit)
    65 3AD1 BD 7F 3A		        lda envely,x
    66 3AD4 30 12		        bmi ?kb_vy_neg
    67 3AD6 18			        clc
    68 3AD7 7D 43 3A		        adc en_y,x
    69 3ADA 9D 43 3A		        sta en_y,x
    70 3ADD BD 49 3A		        lda en_yhi,x
    71 3AE0 69 00		        adc #0
    72 3AE2 9D 49 3A		        sta en_yhi,x
    73 3AE5 4C F7 3A		        jmp ?kb_vy_grav
    74 3AE8			?kb_vy_neg
    75 3AE8 18			        clc
    76 3AE9 7D 43 3A		        adc en_y,x
    77 3AEC 9D 43 3A		        sta en_y,x
    78 3AEF BD 49 3A		        lda en_yhi,x
    79 3AF2 69 FF		        adc #$FF            ; sign-extend negative
    80 3AF4 9D 49 3A		        sta en_yhi,x
    81 3AF7			?kb_vy_grav
    82 				        ; Add gravity to knockback
    83 3AF7 BD 7F 3A		        lda envely,x
    84 3AFA C9 03		        cmp #MAXFALL
    85 3AFC B0 03		        bcs ?nx_jmp
    86 3AFE FE 7F 3A		        inc envely,x
    87 3B01 4C 44 41		?nx_jmp jmp ?nx
    88 3B04			?dead
    89 3B04 A9 00		        lda #0
    90 3B06 9D 55 3A		        sta en_act,x        ; fully dead, deactivate
    91 3B09 4C 44 41		        jmp ?nx
    92 3B0C 86 B5		?alive  stx zzidx
    93 				        ; --- Off-screen skip: far from camera, only do gravity ---
    94 				        ; Check if |enemy_x - cam_x| > 512 (about 1.5 screens away)
    95 3B0E BD 3D 3A		        lda enxhi,x
    96 3B11 38			        sec
    97 3B12 E5 D8		        sbc cam_x_hi
    98 3B14 10 02		        bpl ?osr_pos
    99 3B16 49 FF		        eor #$FF                ; negate
   100 3B18 C9 02		?osr_pos cmp #2
   101 3B1A 90 03		        bcc ?on_screen
   102 3B1C 4C B0 40		        jmp ?grav               ; far from camera, skip AI
   103 3B1F			?on_screen
   104 				        ; --- Pain timer decrement ---
   105 3B1F BD FD 44		        lda en_pain_tmr,x
   106 3B22 F0 03		        beq ?no_pdec
   107 3B24 DE FD 44		        dec en_pain_tmr,x
   108 3B27			?no_pdec
   109 				        ; --- LOS check: skip every 2nd frame per enemy ---
   110 3B27 A5 90		        lda zfr
   111 3B29 45 B5		        eor zzidx              ; stagger: different enemies check different frames
   112 3B2B 29 01		        and #$01
   113 3B2D F0 03		        beq ?do_los
   114 3B2F 4C B0 40		        jmp ?grav              ; skip frame: just gravity, no movement
   115 3B32			?do_los
   116 				        ; If alerted and falling, skip LOS and keep chasing
   117 				        ; (prevents cycling on stairs when chest-height row changes)
   118 3B32 BD 73 3A		        lda en_cooldown,x
   119 3B35 F0 08		        beq ?normal_los
   120 3B37 BD 7F 3A		        lda envely,x
   121 3B3A F0 03		        beq ?normal_los
   122 3B3C 4C 3E 3E		        jmp ?no_attack          ; alerted + falling = keep chasing
   123 3B3F			?normal_los
   124 				        ; --- LOS check: can enemy see player? ---
   125 				        ; Y distance must be within 48px; hi bytes must match
   126 3B3F BD 49 3A		        lda en_yhi,x
   127 3B42 C5 9D		        cmp zpy_hi
   128 3B44 F0 03		        beq ?los_yhi_ok
   129 3B46 4C A3 40		        jmp ?idle           ; different hi bytes = too far
   130 3B49			?los_yhi_ok
   131 3B49 BD 43 3A		        lda en_y,x
   132 3B4C 38			        sec
   133 3B4D E5 92		        sbc zpy
   134 3B4F 10 05		        bpl ?yp
   135 3B51 49 FF		        eor #$FF
   136 3B53 18			        clc
   137 3B54 69 01		        adc #1
   138 3B56 C9 30		?yp     cmp #48             ; detect only on same/adjacent platform
   139 3B58 90 03		        bcc ?los_check
   140 3B5A 4C A3 40		        jmp ?idle
   141 3B5D			?los_check
   142 				        ; Proximity check: |enxhi:en_x - zpx_hi:zpx| < 48 = always detect
   143 3B5D BD 37 3A		        lda en_x,x
   144 3B60 38			        sec
   145 3B61 E5 91		        sbc zpx
   146 3B63 8D 4F 41		        sta los_cur         ; temp dist low
   147 3B66 BD 3D 3A		        lda enxhi,x
   148 3B69 E5 9C		        sbc zpx_hi
   149 3B6B 10 09		        bpl ?ppos
   150 				        ; Negate 16-bit
   151 3B6D A9 00		        lda #0
   152 3B6F 38			        sec
   153 3B70 ED 4F 41		        sbc los_cur
   154 3B73 8D 4F 41		        sta los_cur
   155 3B76			?ppos   ; A=dist high, los_cur=dist low
   156 3B76 D0 07		        bne ?no_prox        ; high != 0 → distance > 255
   157 3B78 AD 4F 41		        lda los_cur
   158 3B7B C9 30		        cmp #48
   159 3B7D 90 30		        bcc ?dir_ok         ; within 48px = proximity alert!
   160 3B7F			?no_prox
   161 				        ; If already alerted, skip direction check (360° awareness)
   162 3B7F BD 73 3A		        lda en_cooldown,x
   163 3B82 D0 2B		        bne ?dir_ok
   164 				        ; Direction check: enemy only sees forward (180° cone)
   165 3B84 BD 4F 3A		        lda en_dir,x
   166 3B87 D0 13		        bne ?chk_left
   167 				        ; Facing right (dir=0): player must be right of enemy
   168 3B89 BD 3D 3A		        lda enxhi,x
   169 3B8C C5 9C		        cmp zpx_hi
   170 3B8E 90 1F		        bcc ?dir_ok         ; enxhi < zpx_hi → right ✓
   171 3B90 D0 07		        bne ?not_right      ; enxhi > zpx_hi → left
   172 3B92 BD 37 3A		        lda en_x,x
   173 3B95 C5 91		        cmp zpx
   174 3B97 90 16		        bcc ?dir_ok         ; en_x < zpx → right ✓
   175 3B99			?not_right
   176 3B99 4C A3 40		        jmp ?idle
   177 3B9C			?chk_left
   178 				        ; Facing left (dir=1): player must be left of enemy
   179 3B9C A5 9C		        lda zpx_hi
   180 3B9E DD 3D 3A		        cmp enxhi,x
   181 3BA1 90 0C		        bcc ?dir_ok         ; zpx_hi < enxhi → left ✓
   182 3BA3 D0 07		        bne ?not_left       ; zpx_hi > enxhi → right
   183 3BA5 A5 91		        lda zpx
   184 3BA7 DD 37 3A		        cmp en_x,x
   185 3BAA 90 03		        bcc ?dir_ok         ; zpx < en_x → left ✓
   186 3BAC			?not_left
   187 3BAC 4C A3 40		        jmp ?idle
   188 3BAF			?dir_ok
   189
   190 				        ; Compute map row pointer for LOS tile scan (16-bit Y)
   191 3BAF A9 9E		        lda #BANK_EN+BANK_MAP
   192 3BB1 8D 5F D6		        sta VBXE_BANK_SEL
   193 3BB4 BD 43 3A		        lda en_y,x
   194 3BB7 38			        sec
   195 3BB8 E9 08		        sbc #8              ; chest height
   196 3BBA 85 80		        sta zt
   197 3BBC BD 49 3A		        lda en_yhi,x
   198 3BBF E9 00		        sbc #0              ; propagate borrow
   199 3BC1 0A			        asl
   200 3BC2 0A			        asl
   201 3BC3 0A			        asl
   202 3BC4 0A			        asl                 ; hi * 16
   203 3BC5 85 81		        sta zt2
   204 3BC7 A5 80		        lda zt
   205 3BC9 4A			        lsr
   206 3BCA 4A			        lsr
   207 3BCB 4A			        lsr
   208 3BCC 4A			        lsr                 ; lo / 16
   209 3BCD 05 81		        ora zt2             ; A = row
   210 3BCF A8			        tay
   211 				        ; ztptr = map_data + row*64 (LUT)
   212 3BD0 B9 C6 A3		        lda map_row_lo,y
   213 3BD3 85 D4		        sta ztptr
   214 3BD5 B9 E6 A3		        lda map_row_hi,y
   215 3BD8 85 D5		        sta ztptr+1
   216 				        ; Enemy tile column = (enxhi:en_x + 8) / 16
   217 3BDA A6 B5		        ldx zzidx
   218 3BDC BD 37 3A		        lda en_x,x
   219 3BDF 18			        clc
   220 3BE0 69 08		        adc #8
   221 3BE2 48			        pha                 ; save low byte
   222 3BE3 BD 3D 3A		        lda enxhi,x
   223 3BE6 69 00		        adc #0              ; add carry
   224 3BE8 0A			        asl
   225 3BE9 0A			        asl
   226 3BEA 0A			        asl
   227 3BEB 0A			        asl                 ; hi * 16
   228 3BEC 8D 4D 41		        sta los_ecol
   229 3BEF 68			        pla
   230 3BF0 4A			        lsr
   231 3BF1 4A			        lsr
   232 3BF2 4A			        lsr
   233 3BF3 4A			        lsr                 ; low / 16
   234 3BF4 0D 4D 41		        ora los_ecol
   235 3BF7 8D 4D 41		        sta los_ecol
   236
   237 				        ; Player tile column = (zpx_hi:zpx + 8) / 16
   238 3BFA A5 91		        lda zpx
   239 3BFC 18			        clc
   240 3BFD 69 08		        adc #8
   241 3BFF 8D 4E 41		        sta los_pcol        ; temp
   242 3C02 A5 9C		        lda zpx_hi
   243 3C04 69 00		        adc #0              ; carry from zpx+8
   244 3C06 0A			        asl
   245 3C07 0A			        asl
   246 3C08 0A			        asl
   247 3C09 0A			        asl                 ; hi * 16
   248 3C0A 8D 50 41		        sta los_end         ; temp
   249 3C0D AD 4E 41		        lda los_pcol
   250 3C10 4A			        lsr
   251 3C11 4A			        lsr
   252 3C12 4A			        lsr
   253 3C13 4A			        lsr                 ; (zpx+8) / 16
   254 3C14 18			        clc
   255 3C15 6D 50 41		        adc los_end
   256 3C18 8D 4E 41		        sta los_pcol
   257
   258 				        ; Scan ALL tiles between player and enemy (inclusive endpoints)
   259 3C1B AD 4D 41		        lda los_ecol
   260 3C1E CD 4E 41		        cmp los_pcol
   261 3C21 F0 3A		        beq ?h_clear        ; same column = visible
   262 3C23 90 0F		        bcc ?scan_r         ; enemy left of player
   263
   264 				        ; Enemy right of player: scan from pcol to ecol
   265 3C25 AD 4E 41		        lda los_pcol
   266 3C28 8D 4F 41		        sta los_cur
   267 3C2B AD 4D 41		        lda los_ecol
   268 3C2E 18			        clc
   269 3C2F 69 01		        adc #1
   270 3C31 4C 40 3C		        jmp ?do_scan
   271
   272 3C34			?scan_r ; Enemy left of player: scan from ecol to pcol
   273 3C34 AD 4D 41		        lda los_ecol
   274 3C37 8D 4F 41		        sta los_cur
   275 3C3A AD 4E 41		        lda los_pcol
   276 3C3D 18			        clc
   277 3C3E 69 01		        adc #1
   278
   279 3C40			?do_scan
   280 3C40 8D 50 41		        sta los_end
   281 3C43 AD 4F 41		?scan   lda los_cur
   282 3C46 CD 50 41		        cmp los_end
   283 3C49 B0 12		        bcs ?h_clear        ; no wall found horizontally, check vertical
   284 3C4B A8			        tay
   285 3C4C B1 D4		        lda (ztptr),y       ; tile at (column, row)
   286 3C4E AA			        tax
   287 3C4F BD 9B A5		        lda tile_solid,x
   288 3C52 F0 03		        beq ?scan_nx
   289 3C54 4C A3 40		        jmp ?idle           ; solid tile = wall blocks LOS
   290 3C57			?scan_nx
   291 3C57 EE 4F 41		        inc los_cur
   292 3C5A 4C 43 3C		        jmp ?scan
   293
   294 3C5D			?h_clear
   295 				        ; --- Vertical LOS check: solid tiles between enemy row and player row? ---
   296 3C5D A6 B5		        ldx zzidx
   297 3C5F BD 43 3A		        lda en_y,x
   298 3C62 38			        sec
   299 3C63 E9 08		        sbc #8              ; enemy chest
   300 3C65 85 80		        sta zt
   301 3C67 BD 49 3A		        lda en_yhi,x
   302 3C6A E9 00		        sbc #0
   303 3C6C 0A			        asl
   304 3C6D 0A			        asl
   305 3C6E 0A			        asl
   306 3C6F 0A			        asl                 ; hi * 16
   307 3C70 85 81		        sta zt2
   308 3C72 A5 80		        lda zt
   309 3C74 4A			        lsr
   310 3C75 4A			        lsr
   311 3C76 4A			        lsr
   312 3C77 4A			        lsr
   313 3C78 05 81		        ora zt2
   314 3C7A 8D 4F 41		        sta los_cur         ; enemy row
   315 3C7D A5 92		        lda zpy
   316 3C7F 38			        sec
   317 3C80 E9 08		        sbc #8              ; player chest
   318 3C82 85 80		        sta zt
   319 3C84 A5 9D		        lda zpy_hi
   320 3C86 E9 00		        sbc #0
   321 3C88 0A			        asl
   322 3C89 0A			        asl
   323 3C8A 0A			        asl
   324 3C8B 0A			        asl                 ; hi * 16
   325 3C8C 85 81		        sta zt2
   326 3C8E A5 80		        lda zt
   327 3C90 4A			        lsr
   328 3C91 4A			        lsr
   329 3C92 4A			        lsr
   330 3C93 4A			        lsr
   331 3C94 05 81		        ora zt2
   332 3C96 8D 50 41		        sta los_end         ; player row
   333 3C99 CD 4F 41		        cmp los_cur
   334 3C9C F0 3A		        beq ?detected       ; same row = no vertical wall possible
   335 3C9E 90 03		        bcc ?v_up
   336 				        ; Player below enemy: scan from enemy row to player row
   337 3CA0 4C B1 3C		        jmp ?v_scan
   338 3CA3			?v_up   ; Player above enemy: swap so los_cur < los_end
   339 3CA3 AD 50 41		        lda los_end
   340 3CA6 48			        pha
   341 3CA7 AD 4F 41		        lda los_cur
   342 3CAA 8D 50 41		        sta los_end
   343 3CAD 68			        pla
   344 3CAE 8D 4F 41		        sta los_cur
   345 3CB1 AD 4F 41		?v_scan lda los_cur
   346 3CB4 CD 50 41		        cmp los_end
   347 3CB7 B0 1F		        bcs ?detected       ; reached end = no wall found
   348 3CB9 A8			        tay
   349 3CBA B9 C6 A3		        lda map_row_lo,y
   350 3CBD 85 D4		        sta ztptr
   351 3CBF B9 E6 A3		        lda map_row_hi,y
   352 3CC2 85 D5		        sta ztptr+1
   353 3CC4 AC 4D 41		        ldy los_ecol        ; check at enemy's column
   354 3CC7 B1 D4		        lda (ztptr),y
   355 3CC9 AA			        tax
   356 3CCA BD 9B A5		        lda tile_solid,x
   357 3CCD D0 06		        bne ?v_wall         ; solid tile blocks vertical LOS
   358 3CCF EE 4F 41		        inc los_cur
   359 3CD2 4C B1 3C		        jmp ?v_scan
   360 3CD5 4C A3 40		?v_wall jmp ?idle           ; wall between floors = can't see
   361
   362 3CD8			?detected
   363 				        ; Mark as alerted (stays alerted permanently)
   364 3CD8 A6 B5		        ldx zzidx
   365 3CDA BD 73 3A		        lda en_cooldown,x
   366 3CDD D0 03		        bne ?already_alert      ; already alerted = no sight sound
   367 				        ; First alert! Play sight sound by enemy type
   368 3CDF 20 BE 5B		        jsr play_enemy_sight
   369 3CE2			?already_alert
   370 3CE2 A6 B5		        ldx zzidx
   371 3CE4 A9 01		        lda #1
   372 3CE6 9D 73 3A		        sta en_cooldown,x
   373 				        ; --- Attack logic ---
   374 3CE9 BD 6D 3A		        lda en_type,x
   375 3CEC C9 00		        cmp #EN_ZOMBIE
   376 3CEE F0 13		        beq ?atk_check
   377 3CF0 C9 04		        cmp #EN_SHOTGUN
   378 3CF2 F0 0F		        beq ?atk_check
   379 3CF4 C9 01		        cmp #EN_IMP
   380 3CF6 F0 0B		        beq ?atk_check
   381 3CF8 C9 02		        cmp #EN_PINKY
   382 3CFA F0 07		        beq ?atk_check
   383 3CFC C9 03		        cmp #EN_CACO
   384 3CFE F0 03		        beq ?atk_check
   385 3D00 4C 3E 3E		        jmp ?no_attack
   386 3D03			?atk_check
   387 3D03 BD 91 3A		        lda en_atk,x
   388 3D06 F0 06		        beq ?do_attack
   389 3D08 DE 91 3A		        dec en_atk,x
   390 3D0B 4C 3E 3E		        jmp ?no_attack
   391 3D0E			?do_attack
   392 				        ; Check Y distance first (must be on similar level)
   393 3D0E BD 49 3A		        lda en_yhi,x
   394 3D11 C5 9D		        cmp zpy_hi
   395 3D13 D0 11		        bne ?no_atk_far     ; different hi bytes = too far
   396 3D15 BD 43 3A		        lda en_y,x
   397 3D18 38			        sec
   398 3D19 E5 92		        sbc zpy
   399 3D1B 10 05		        bpl ?atk_yp
   400 3D1D 49 FF		        eor #$FF
   401 3D1F 18			        clc
   402 3D20 69 01		        adc #1
   403 3D22 C9 18		?atk_yp cmp #24               ; within 24px vertical = can shoot
   404 3D24 90 03		        bcc ?in_range
   405 3D26			?no_atk_far
   406 3D26 4C 3E 3E		        jmp ?no_attack         ; too far vertically, skip
   407 3D29			?in_range
   408 				        ; Pinky: melee only (must be close)
   409 3D29 BD 6D 3A		        lda en_type,x
   410 3D2C C9 02		        cmp #EN_PINKY
   411 3D2E D0 35		        bne ?not_pinky
   412 				        ; Check X distance for melee range (|en_x - zpx| < 20)
   413 3D30 BD 37 3A		        lda en_x,x
   414 3D33 38			        sec
   415 3D34 E5 91		        sbc zpx
   416 3D36 10 05		        bpl ?pk_xp
   417 3D38 49 FF		        eor #$FF
   418 3D3A 18			        clc
   419 3D3B 69 01		        adc #1
   420 3D3D C9 14		?pk_xp  cmp #20
   421 3D3F B0 21		        bcs ?no_atk2            ; too far for melee, skip
   422 				        ; Pinky bite! 15 damage, aggressive cooldown
   423 3D41 A9 0F		        lda #15
   424 3D43 20 9E 30		        jsr player_take_damage
   425 3D46 86 81		        stx zt2
   426 3D48 A9 00		        lda #0
   427 3D4A 85 CB		        sta snd_lock
   428 3D4C A2 1F		        ldx #SFX_SGTATK
   429 3D4E 20 A2 5A		        jsr snd_play
   430 3D51 A6 81		        ldx zt2
   431 				        ; Short cooldown: 20-35 frames (~0.3-0.6s)
   432 3D53 A5 90		        lda zfr
   433 3D55 45 14		        eor RTCLOK3
   434 3D57 29 0F		        and #$0F
   435 3D59 18			        clc
   436 3D5A 69 14		        adc #20
   437 3D5C 9D 91 3A		        sta en_atk,x
   438 3D5F 4C 3E 3E		        jmp ?no_attack
   439 3D62 4C 3E 3E		?no_atk2 jmp ?no_attack
   440 3D65			?not_pinky
   441 				        ; Cacodemon: fireball (same projectile as imp, different damage)
   442 3D65 BD 6D 3A		        lda en_type,x
   443 3D68 C9 03		        cmp #EN_CACO
   444 3D6A D0 21		        bne ?not_caco
   445 				        ; Spawn caco fireball (reuses imp fireball system)
   446 3D6C 20 26 45		        jsr spawn_eproj
   447 3D6F A6 B5		        ldx zzidx
   448 				        ; Override damage: caco fireball = 15 (imp = 20)
   449 				        ; (spawn_eproj sets damage to 20, patch it)
   450 				        ; Fire sound (same as imp)
   451 3D71 86 81		        stx zt2
   452 3D73 A9 00		        lda #0
   453 3D75 85 CB		        sta snd_lock
   454 3D77 A2 1B		        ldx #SFX_FIRSHT
   455 3D79 20 A2 5A		        jsr snd_play
   456 3D7C A6 81		        ldx zt2
   457 				        ; Caco cooldown: 50-80 frames (~0.8-1.3s)
   458 3D7E A5 90		        lda zfr
   459 3D80 45 14		        eor RTCLOK3
   460 3D82 29 1F		        and #$1F
   461 3D84 18			        clc
   462 3D85 69 32		        adc #50
   463 3D87 9D 91 3A		        sta en_atk,x
   464 3D8A 4C 3E 3E		        jmp ?no_attack
   465 3D8D			?not_caco
   466 				        ; Imp: melee if close, fireball if far
   467 3D8D BD 6D 3A		        lda en_type,x
   468 3D90 C9 01		        cmp #EN_IMP
   469 3D92 D0 47		        bne ?hitscan
   470 				        ; Check X distance for melee range (|en_x - zpx| < 20)
   471 3D94 BD 37 3A		        lda en_x,x
   472 3D97 38			        sec
   473 3D98 E5 91		        sbc zpx
   474 3D9A 10 05		        bpl ?imp_xp
   475 3D9C 49 FF		        eor #$FF
   476 3D9E 18			        clc
   477 3D9F 69 01		        adc #1
   478 3DA1 C9 14		?imp_xp cmp #20
   479 3DA3 B0 21		        bcs ?imp_far
   480 				        ; Imp melee attack — claw damage 10
   481 3DA5 A9 0A		        lda #10
   482 3DA7 20 9E 30		        jsr player_take_damage
   483 				        ; Imp claw melee sound
   484 3DAA 86 81		        stx zt2
   485 3DAC A9 00		        lda #0
   486 3DAE 85 CB		        sta snd_lock
   487 3DB0 A2 1E		        ldx #SFX_CLAW
   488 3DB2 20 A2 5A		        jsr snd_play
   489 3DB5 A6 81		        ldx zt2
   490 				        ; Melee has shorter cooldown (15-30 frames = aggressive!)
   491 3DB7 A5 90		        lda zfr
   492 3DB9 45 14		        eor RTCLOK3
   493 3DBB 29 0F		        and #$0F              ; random 0-15
   494 3DBD 18			        clc
   495 3DBE 69 0F		        adc #15               ; 15-30 frames (~0.25-0.5s)
   496 3DC0 9D 91 3A		        sta en_atk,x
   497 3DC3 4C 3E 3E		        jmp ?no_attack
   498 3DC6			?imp_far
   499 				        ; Spawn imp fireball (ranged attack)
   500 3DC6 20 26 45		        jsr spawn_eproj
   501 				        ; Restore enemy index (spawn_eproj clobbers X)
   502 3DC9 A6 B5		        ldx zzidx
   503 				        ; Fire sound
   504 3DCB 86 81		        stx zt2
   505 3DCD A9 00		        lda #0
   506 3DCF 85 CB		        sta snd_lock
   507 3DD1 A2 1B		        ldx #SFX_FIRSHT
   508 3DD3 20 A2 5A		        jsr snd_play
   509 3DD6 A6 81		        ldx zt2
   510 3DD8 4C 06 3E		        jmp ?set_cd
   511 3DDB			?hitscan
   512 				        ; Hitscan damage to player (zombie=3, shotgun=15)
   513 3DDB BD 6D 3A		        lda en_type,x
   514 3DDE C9 04		        cmp #EN_SHOTGUN
   515 3DE0 F0 04		        beq ?sg_dmg
   516 3DE2 A9 03		        lda #3                 ; zombie damage
   517 3DE4 D0 02		        bne ?do_dmg
   518 3DE6 A9 0F		?sg_dmg lda #15                ; shotgun guy damage (3 pellets)
   519 3DE8 20 9E 30		?do_dmg jsr player_take_damage
   520 				        ; Fire sound (zombie=pistol, shotgun=shotgun)
   521 3DEB 86 81		        stx zt2
   522 3DED A9 00		        lda #0
   523 3DEF 85 CB		        sta snd_lock
   524 3DF1 A6 B5		        ldx zzidx
   525 3DF3 BD 6D 3A		        lda en_type,x
   526 3DF6 C9 04		        cmp #EN_SHOTGUN
   527 3DF8 F0 05		        beq ?sg_snd
   528 3DFA A2 00		        ldx #SFX_PISTOL
   529 3DFC 4C 01 3E		        jmp ?pl_snd
   530 3DFF A2 07		?sg_snd ldx #SFX_SHOTGUN
   531 3E01 20 A2 5A		?pl_snd jsr snd_play
   532 3E04 A6 81		        ldx zt2
   533 3E06			?set_cd ; Reset timer by enemy type
   534 3E06 BD 6D 3A		        lda en_type,x
   535 3E09 C9 01		        cmp #EN_IMP
   536 3E0B F0 13		        beq ?imp_cd
   537 3E0D C9 04		        cmp #EN_SHOTGUN
   538 3E0F F0 1E		        beq ?sg_cd
   539 				        ; Zombie: 30 + random 0-63 = 30-93 frames
   540 3E11 A5 90		        lda zfr
   541 3E13 45 14		        eor RTCLOK3
   542 3E15 5D 37 3A		        eor en_x,x
   543 3E18 29 3F		        and #$3F
   544 3E1A 18			        clc
   545 3E1B 69 1E		        adc #30
   546 3E1D 4C 3B 3E		        jmp ?st_cd
   547 3E20			?imp_cd ; Imp: 45 + random 0-63 = 45-108 frames (~0.75-1.8s)
   548 3E20 A5 90		        lda zfr
   549 3E22 45 14		        eor RTCLOK3
   550 3E24 5D 37 3A		        eor en_x,x
   551 3E27 29 3F		        and #$3F
   552 3E29 18			        clc
   553 3E2A 69 2D		        adc #45
   554 3E2C 4C 3B 3E		        jmp ?st_cd
   555 3E2F			?sg_cd  ; Shotgun guy: 40 + random 0-63 = 40-103 frames
   556 3E2F A5 90		        lda zfr
   557 3E31 45 14		        eor RTCLOK3
   558 3E33 5D 37 3A		        eor en_x,x
   559 3E36 29 3F		        and #$3F
   560 3E38 18			        clc
   561 3E39 69 28		        adc #40
   562 3E3B 9D 91 3A		?st_cd  sta en_atk,x
   563 3E3E			?no_attack
   564 3E3E A6 B5		        ldx zzidx
   565 				        ; Player visible! Chase: face player and move toward
   566 				        ; 16-bit absolute distance: |enxhi:en_x - zpx_hi:zpx|
   567 3E40 BD 37 3A		        lda en_x,x
   568 3E43 38			        sec
   569 3E44 E5 91		        sbc zpx
   570 3E46 8D 4F 41		        sta los_cur         ; dist low
   571 3E49 BD 3D 3A		        lda enxhi,x
   572 3E4C E5 9C		        sbc zpx_hi
   573 3E4E 8D 50 41		        sta los_end         ; dist high (signed)
   574 3E51 10 15		        bpl ?dpos
   575 				        ; Negate 16-bit (ones complement + 1)
   576 3E53 AD 4F 41		        lda los_cur
   577 3E56 49 FF		        eor #$FF
   578 3E58 18			        clc
   579 3E59 69 01		        adc #1
   580 3E5B 8D 4F 41		        sta los_cur
   581 3E5E AD 50 41		        lda los_end
   582 3E61 49 FF		        eor #$FF
   583 3E63 69 00		        adc #0
   584 3E65 8D 50 41		        sta los_end
   585 3E68			?dpos   ; los_end:los_cur = absolute distance
   586 3E68 AD 50 41		        lda los_end
   587 3E6B D0 3C		        bne ?jchase1        ; high != 0 → far, chase
   588 3E6D AD 4F 41		        lda los_cur
   589 3E70 C9 10		        cmp #16
   590 3E72 B0 35		        bcs ?jchase1        ; > 16px, chase directly
   591 				        ; Within 16px horizontally - check Y distance
   592 				        ; If player is on different level, patrol instead of standing
   593 3E74 BD 49 3A		        lda en_yhi,x
   594 3E77 C5 9D		        cmp zpy_hi
   595 3E79 D0 31		        bne ?patrol         ; different hi bytes = different level
   596 3E7B BD 43 3A		        lda en_y,x
   597 3E7E 38			        sec
   598 3E7F E5 92		        sbc zpy
   599 3E81 10 05		        bpl ?yd
   600 3E83 49 FF		        eor #$FF
   601 3E85 18			        clc
   602 3E86 69 01		        adc #1
   603 3E88 C9 20		?yd     cmp #32
   604 3E8A B0 20		        bcs ?patrol         ; different level (>=32px Y) = patrol
   605 				        ; Same level, within 16px: update patrol bounds to current pos
   606 3E8C BD 37 3A		        lda en_x,x
   607 3E8F 38			        sec
   608 3E90 E9 28		        sbc #40
   609 3E92 B0 02		        bcs ?nb_min
   610 3E94 A9 00		        lda #0
   611 3E96 9D 61 3A		?nb_min sta en_xmin,x
   612 3E99 BD 37 3A		        lda en_x,x
   613 3E9C 18			        clc
   614 3E9D 69 28		        adc #40
   615 3E9F 90 02		        bcc ?nb_max
   616 3EA1 A9 FF		        lda #255
   617 3EA3 9D 67 3A		?nb_max sta en_xmax,x
   618 3EA6 4C AC 3E		        jmp ?patrol         ; patrol near player with updated bounds
   619 3EA9 4C BA 3F		?jchase1 jmp ?do_chase
   620 				        ; Different level - patrol back and forth
   621 3EAC BD 4F 3A		?patrol lda en_dir,x
   622 3EAF D0 77		        bne ?pat_l
   623 				        ; Patrol right: wall check first
   624 3EB1 BD 37 3A		        lda en_x,x
   625 3EB4 18			        clc
   626 3EB5 69 10		        adc #16             ; right edge of enemy
   627 3EB7 8D B0 25		        sta gt_px
   628 3EBA BD 3D 3A		        lda enxhi,x
   629 3EBD 69 00		        adc #0
   630 3EBF 8D B1 25		        sta gt_px_hi
   631 3EC2 BD 43 3A		        lda en_y,x
   632 3EC5 38			        sec
   633 3EC6 E9 08		        sbc #8              ; chest height
   634 3EC8 8D B2 25		        sta gt_py
   635 3ECB BD 49 3A		        lda en_yhi,x
   636 3ECE E9 00		        sbc #0
   637 3ED0 8D B3 25		        sta gt_py_hi
   638 3ED3 20 F9 25		        jsr check_solid
   639 3ED6 48			        pha
   640 3ED7 A6 B5		        ldx zzidx
   641 3ED9 68			        pla
   642 3EDA D0 35		        bne ?pat_turn_l     ; wall → turn around
   643 3EDC A9 00		        lda #0
   644 3EDE 9D F7 44		        sta en_tcnt,x       ; moved → reset turn counter
   645 3EE1 FE 37 3A		        inc en_x,x
   646 3EE4 D0 03		        bne ?pr1
   647 3EE6 FE 3D 3A		        inc enxhi,x
   648 3EE9 FE 37 3A		?pr1    inc en_x,x
   649 3EEC D0 03		        bne ?pat_nw1
   650 3EEE FE 3D 3A		        inc enxhi,x
   651 3EF1			?pat_nw1
   652 3EF1 BD 6D 3A		        lda en_type,x
   653 3EF4 A8			        tay
   654 3EF5 B9 8D 5E		        lda en_speed_tab,y
   655 3EF8 C9 03		        cmp #3
   656 3EFA 90 08		        bcc ?pr_nospd
   657 3EFC FE 37 3A		        inc en_x,x
   658 3EFF D0 03		        bne ?pr_nospd
   659 3F01 FE 3D 3A		        inc enxhi,x
   660 3F04			?pr_nospd
   661 3F04 BD 37 3A		        lda en_x,x
   662 3F07 DD 67 3A		        cmp en_xmax,x
   663 3F0A 90 02		        bcc ?jgrav1
   664 3F0C B0 03		        bcs ?pat_turn_l
   665 3F0E 4C B0 40		?jgrav1 jmp ?grav
   666 3F11			?pat_turn_l
   667 3F11 A6 B5		        ldx zzidx
   668 3F13 FE F7 44		        inc en_tcnt,x
   669 3F16 BD F7 44		        lda en_tcnt,x
   670 3F19 C9 02		        cmp #2
   671 3F1B B0 08		        bcs ?grav_jmp       ; stuck: don't flip, just stand
   672 3F1D A9 01		        lda #1
   673 3F1F 9D 4F 3A		        sta en_dir,x
   674 3F22 4C B0 40		        jmp ?grav
   675 3F25 4C B0 40		?grav_jmp jmp ?grav
   676 3F28			?pat_l  ; Patrol left: wall check first
   677 3F28 BD 37 3A		        lda en_x,x
   678 3F2B 38			        sec
   679 3F2C E9 01		        sbc #1              ; left edge
   680 3F2E 8D B0 25		        sta gt_px
   681 3F31 BD 3D 3A		        lda enxhi,x
   682 3F34 E9 00		        sbc #0
   683 3F36 30 6B		        bmi ?pat_turn_r     ; underflow past 0 → turn
   684 3F38 8D B1 25		        sta gt_px_hi
   685 3F3B BD 43 3A		        lda en_y,x
   686 3F3E 38			        sec
   687 3F3F E9 08		        sbc #8
   688 3F41 8D B2 25		        sta gt_py
   689 3F44 BD 49 3A		        lda en_yhi,x
   690 3F47 E9 00		        sbc #0
   691 3F49 8D B3 25		        sta gt_py_hi
   692 3F4C 20 F9 25		        jsr check_solid
   693 3F4F 48			        pha
   694 3F50 A6 B5		        ldx zzidx
   695 3F52 68			        pla
   696 3F53 D0 4E		        bne ?pat_turn_r     ; wall → turn around
   697 3F55 BD 37 3A		        lda en_x,x
   698 3F58 D0 08		        bne ?pl_ok
   699 3F5A BD 3D 3A		        lda enxhi,x
   700 3F5D F0 44		        beq ?pat_turn_r     ; at 0 → turn
   701 3F5F DE 3D 3A		        dec enxhi,x
   702 3F62 A9 00		?pl_ok  lda #0
   703 3F64 9D F7 44		        sta en_tcnt,x       ; moved → reset turn counter
   704 3F67 DE 37 3A		        dec en_x,x
   705 3F6A BD 37 3A		        lda en_x,x
   706 3F6D D0 08		        bne ?pl2
   707 3F6F BD 3D 3A		        lda enxhi,x
   708 3F72 F0 2C		        beq ?jgrav2
   709 3F74 DE 3D 3A		        dec enxhi,x
   710 3F77 DE 37 3A		?pl2    dec en_x,x
   711 3F7A BD 6D 3A		        lda en_type,x
   712 3F7D A8			        tay
   713 3F7E B9 8D 5E		        lda en_speed_tab,y
   714 3F81 C9 03		        cmp #3
   715 3F83 90 10		        bcc ?pl_nospd
   716 3F85 BD 37 3A		        lda en_x,x
   717 3F88 D0 08		        bne ?pl3
   718 3F8A BD 3D 3A		        lda enxhi,x
   719 3F8D F0 06		        beq ?pl_nospd
   720 3F8F DE 3D 3A		        dec enxhi,x
   721 3F92 DE 37 3A		?pl3    dec en_x,x
   722 3F95			?pl_nospd
   723 3F95 BD 37 3A		        lda en_x,x
   724 3F98 DD 61 3A		        cmp en_xmin,x
   725 3F9B B0 03		        bcs ?jgrav2
   726 3F9D 4C A3 3F		        jmp ?pat_turn_r
   727 3FA0 4C B0 40		?jgrav2 jmp ?grav
   728 3FA3			?pat_turn_r
   729 3FA3 A6 B5		        ldx zzidx
   730 3FA5 FE F7 44		        inc en_tcnt,x
   731 3FA8 BD F7 44		        lda en_tcnt,x
   732 3FAB C9 02		        cmp #2
   733 3FAD B0 08		        bcs ?grav_jmp2      ; stuck: don't flip, just stand
   734 3FAF A9 00		        lda #0
   735 3FB1 9D 4F 3A		        sta en_dir,x
   736 3FB4 4C B0 40		        jmp ?grav
   737 3FB7 4C B0 40		?grav_jmp2 jmp ?grav
   738 3FBA			?do_chase
   739 				        ; Determine direction: compare 16-bit enxhi:en_x vs zpx_hi:zpx
   740 3FBA A6 B5		        ldx zzidx
   741 3FBC BD 3D 3A		        lda enxhi,x
   742 3FBF C5 9C		        cmp zpx_hi
   743 3FC1 B0 03		        bcs ?not_chase_r1   ; enxhi >= zpx_hi
   744 3FC3 4C 4D 40		        jmp ?chase_r        ; enxhi < zpx_hi → go right
   745 3FC6			?not_chase_r1
   746 3FC6 D0 0A		        bne ?chase_l        ; enxhi > zpx_hi → go left
   747 3FC8 BD 37 3A		        lda en_x,x
   748 3FCB C5 91		        cmp zpx
   749 3FCD B0 03		        bcs ?chase_l_or_eq  ; en_x >= zpx
   750 3FCF 4C 4D 40		        jmp ?chase_r        ; en_x < zpx → go right
   751 3FD2			?chase_l_or_eq
   752 				        ; Enemy right of or equal to player: face left, move left
   753 3FD2			?chase_l
   754 3FD2 A9 01		        lda #1
   755 3FD4 9D 4F 3A		        sta en_dir,x
   756 				        ; Wall check left (en_x - 1, chest height)
   757 3FD7 BD 37 3A		        lda en_x,x
   758 3FDA 38			        sec
   759 3FDB E9 01		        sbc #1
   760 3FDD 8D B0 25		        sta gt_px
   761 3FE0 BD 3D 3A		        lda enxhi,x
   762 3FE3 E9 00		        sbc #0
   763 3FE5 10 03		        bpl ?cl_edge_ok
   764 3FE7 4C B0 40		        jmp ?grav           ; past left edge
   765 3FEA			?cl_edge_ok
   766 3FEA 8D B1 25		        sta gt_px_hi
   767 3FED BD 43 3A		        lda en_y,x
   768 3FF0 38			        sec
   769 3FF1 E9 08		        sbc #8
   770 3FF3 8D B2 25		        sta gt_py
   771 3FF6 BD 49 3A		        lda en_yhi,x
   772 3FF9 E9 00		        sbc #0
   773 3FFB 8D B3 25		        sta gt_py_hi
   774 3FFE 20 F9 25		        jsr check_solid
   775 4001 48			        pha
   776 4002 A6 B5		        ldx zzidx
   777 4004 68			        pla
   778 4005 D0 0C		        bne ?cl_wall        ; wall → don't move
   779 4007 BD 37 3A		        lda en_x,x
   780 400A D0 0D		        bne ?cl_ok
   781 400C BD 3D 3A		        lda enxhi,x
   782 400F F0 02		        beq ?cl_wall        ; at 0
   783 4011 D0 03		        bne ?cl_borrow
   784 4013 4C B0 40		?cl_wall jmp ?grav
   785 4016			?cl_borrow
   786 4016 DE 3D 3A		        dec enxhi,x
   787 4019 DE 37 3A		?cl_ok  dec en_x,x
   788 401C BD 37 3A		        lda en_x,x
   789 401F D0 0B		        bne ?cl_ok2
   790 4021 BD 3D 3A		        lda enxhi,x
   791 4024 D0 03		        bne ?cl_borrow2
   792 4026 4C B0 40		        jmp ?grav
   793 4029			?cl_borrow2
   794 4029 DE 3D 3A		        dec enxhi,x
   795 402C DE 37 3A		?cl_ok2 dec en_x,x
   796 402F BD 6D 3A		        lda en_type,x
   797 4032 A8			        tay
   798 4033 B9 8D 5E		        lda en_speed_tab,y
   799 4036 C9 03		        cmp #3
   800 4038 90 10		        bcc ?cl_done
   801 403A BD 37 3A		        lda en_x,x
   802 403D D0 08		        bne ?cl_ok3
   803 403F BD 3D 3A		        lda enxhi,x
   804 4042 F0 06		        beq ?cl_done
   805 4044 DE 3D 3A		        dec enxhi,x
   806 4047 DE 37 3A		?cl_ok3 dec en_x,x
   807 404A 4C B0 40		?cl_done jmp ?grav
   808 404D			?chase_r
   809 404D A9 00		        lda #0
   810 404F 9D 4F 3A		        sta en_dir,x
   811 				        ; Wall check right (en_x + 16, chest height)
   812 4052 BD 37 3A		        lda en_x,x
   813 4055 18			        clc
   814 4056 69 10		        adc #16
   815 4058 8D B0 25		        sta gt_px
   816 405B BD 3D 3A		        lda enxhi,x
   817 405E 69 00		        adc #0
   818 4060 8D B1 25		        sta gt_px_hi
   819 4063 BD 43 3A		        lda en_y,x
   820 4066 38			        sec
   821 4067 E9 08		        sbc #8
   822 4069 8D B2 25		        sta gt_py
   823 406C BD 49 3A		        lda en_yhi,x
   824 406F E9 00		        sbc #0
   825 4071 8D B3 25		        sta gt_py_hi
   826 4074 20 F9 25		        jsr check_solid
   827 4077 48			        pha
   828 4078 A6 B5		        ldx zzidx
   829 407A 68			        pla
   830 407B D0 23		        bne ?cr_wall        ; wall → don't move
   831 407D FE 37 3A		        inc en_x,x
   832 4080 D0 03		        bne ?cr2
   833 4082 FE 3D 3A		        inc enxhi,x
   834 4085 FE 37 3A		?cr2    inc en_x,x
   835 4088 D0 03		        bne ?cr3
   836 408A FE 3D 3A		        inc enxhi,x
   837 408D BD 6D 3A		?cr3    lda en_type,x
   838 4090 A8			        tay
   839 4091 B9 8D 5E		        lda en_speed_tab,y
   840 4094 C9 03		        cmp #3
   841 4096 90 08		        bcc ?cr_wall
   842 4098 FE 37 3A		        inc en_x,x
   843 409B D0 03		        bne ?cr_wall
   844 409D FE 3D 3A		        inc enxhi,x
   845 40A0 4C B0 40		?cr_wall jmp ?grav
   846
   847 40A3 A6 B5		?idle   ldx zzidx
   848 				        ; If alerted but lost LOS, patrol instead of standing
   849 40A5 BD 73 3A		        lda en_cooldown,x
   850 40A8 D0 03		        bne ?do_patrol
   851 40AA 4C B0 40		        jmp ?grav               ; not alerted = truly idle
   852 40AD			?do_patrol
   853 40AD 4C AC 3E		        jmp ?patrol             ; alerted = keep moving
   854 				        ; --- Enemy gravity + vertical movement ---
   855 40B0			?grav
   856 				        ; Apply vertical velocity (16-bit en_yhi:en_y)
   857 40B0 BD 7F 3A		        lda envely,x
   858 40B3 F0 44		        beq ?no_vy
   859 40B5 30 16		        bmi ?jump_up        ; negative velocity = jumping up
   860 				        ; Falling down: add positive velocity
   861 40B7 18			        clc
   862 40B8 7D 43 3A		        adc en_y,x
   863 40BB 9D 43 3A		        sta en_y,x
   864 40BE BD 49 3A		        lda en_yhi,x
   865 40C1 69 00		        adc #0              ; propagate carry
   866 40C3 9D 49 3A		        sta en_yhi,x
   867 40C6 C9 02		        cmp #2              ; Y >= 512 = fell off map
   868 40C8 B0 15		        bcs ?kill_en
   869 40CA 4C E7 40		        jmp ?vy_grav
   870 40CD			?jump_up
   871 				        ; Jumping up: add negative velocity
   872 40CD 18			        clc
   873 40CE 7D 43 3A		        adc en_y,x
   874 40D1 9D 43 3A		        sta en_y,x
   875 40D4 BD 49 3A		        lda en_yhi,x
   876 40D7 69 FF		        adc #$FF            ; sign-extend negative velocity
   877 40D9 9D 49 3A		        sta en_yhi,x
   878 40DC 4C E7 40		        jmp ?vy_grav
   879 40DF			?kill_en
   880 40DF A9 00		        lda #0
   881 40E1 9D 55 3A		        sta en_act,x        ; deactivate
   882 40E4 4C 44 41		        jmp ?nx
   883 40E7			?vy_grav
   884 				        ; Add gravity to velocity
   885 40E7 BD 7F 3A		        lda envely,x
   886 40EA C9 03		        cmp #MAXFALL
   887 40EC B0 06		        bcs ?cap_vy
   888 40EE FE 7F 3A		        inc envely,x
   889 40F1 4C F9 40		        jmp ?no_vy
   890 40F4 A9 03		?cap_vy lda #MAXFALL
   891 40F6 9D 7F 3A		        sta envely,x
   892 40F9			?no_vy
   893 				        ; Skip floor check if on ground and no velocity (every 4th frame still checks)
   894 40F9 BD 7F 3A		        lda envely,x
   895 40FC D0 06		        bne ?do_floor          ; has velocity → must check
   896 40FE A5 90		        lda zfr
   897 4100 29 03		        and #$03
   898 4102 D0 40		        bne ?nx                ; on ground, no velocity → skip 3 of 4 frames
   899 4104			?do_floor
   900 				        ; Floor check: tile at (center X, feet Y)
   901 4104 BD 37 3A		        lda en_x,x
   902 4107 18			        clc
   903 4108 69 08		        adc #8
   904 410A 8D B0 25		        sta gt_px
   905 410D BD 3D 3A		        lda enxhi,x
   906 4110 69 00		        adc #0
   907 4112 8D B1 25		        sta gt_px_hi
   908 4115 BD 43 3A		        lda en_y,x
   909 4118 8D B2 25		        sta gt_py
   910 411B BD 49 3A		        lda en_yhi,x
   911 411E 8D B3 25		        sta gt_py_hi
   912 4121 20 01 26		        jsr check_solid_or_platform
   913 4124 48			        pha                 ; save result (A=solid flag)
   914 4125 A6 B5		        ldx zzidx           ; restore X (clobbered by check_solid)
   915 4127 68			        pla                 ; restore A + Z flag
   916 4128 D0 0D		        bne ?on_floor       ; solid below = on floor
   917 				        ; No floor: apply gravity if not already falling
   918 412A BD 7F 3A		        lda envely,x
   919 412D D0 15		        bne ?nx             ; already has velocity
   920 412F A9 01		        lda #1
   921 4131 9D 7F 3A		        sta envely,x         ; start falling
   922 4134 4C 44 41		        jmp ?nx
   923 4137			?on_floor
   924 				        ; Snap to tile top + stop falling
   925 4137 BD 43 3A		        lda en_y,x
   926 413A 29 F0		        and #$F0
   927 413C 9D 43 3A		        sta en_y,x
   928 413F A9 00		        lda #0
   929 4141 9D 7F 3A		        sta envely,x
   930 				        ; (Imp jump removed - not practical with current level design)
   931 4144 E8			?nx     inx
   932 4145 E0 06		        cpx #MAX_ENEMIES
   933 4147 B0 03		        bcs ?done
   934 4149 4C 99 3A		        jmp ?lp
   935 414C 60			?done   rts
   936
   937 414D 00			los_ecol dta 0
   938 414E 00			los_pcol dta 0
   939 414F 00			los_cur  dta 0
   940 4150 00			los_end  dta 0
   941 				.endp
   942
   943 				; ============================================
   944 				; ALERT ENEMIES BY SOUND (player fired weapon)
   945 				; All alive enemies within earshot turn to face player
   946 				; ============================================
   947 4151			.proc alert_enemies_sound
   948 4151 A2 00		        ldx #0
   949 4153 BD 55 3A		?lp     lda en_act,x
   950 4156 C9 01		        cmp #1              ; only alive enemies
   951 4158 D0 2B		        bne ?nx
   952 				        ; Check if sound can reach enemy (no solid wall between)
   953 415A 8E 8B 41		        stx aes_idx
   954 415D 20 8C 41		        jsr can_hear
   955 4160 48			        pha                 ; save result
   956 4161 AE 8B 41		        ldx aes_idx
   957 4164 68			        pla                 ; restore A (sets Z flag)
   958 4165 F0 1E		        beq ?nx             ; Z=1 → blocked by wall
   959 				        ; Turn to face player (16-bit compare)
   960 4167 BD 3D 3A		        lda enxhi,x
   961 416A C5 9C		        cmp zpx_hi
   962 416C 90 0D		        bcc ?fr
   963 416E D0 07		        bne ?fl
   964 4170 BD 37 3A		        lda en_x,x
   965 4173 C5 91		        cmp zpx
   966 4175 90 04		        bcc ?fr
   967 4177 A9 01		?fl     lda #1              ; player is left
   968 4179 D0 02		        bne ?sd
   969 417B A9 00		?fr     lda #0              ; player is right
   970 417D 9D 4F 3A		?sd     sta en_dir,x
   971 4180 A9 01		        lda #1
   972 4182 9D 73 3A		        sta en_cooldown,x   ; mark as alerted
   973 4185 E8			?nx     inx
   974 4186 E0 06		        cpx #MAX_ENEMIES
   975 4188 90 C9		        bcc ?lp
   976 418A 60			        rts
   977 418B 00			aes_idx dta 0
   978 				.endp
   979
   980 				; ============================================
   981 				; CAN HEAR - check if sound travels from player to enemy
   982 				; Walk tile-by-tile horizontally at enemy's row.
   983 				; If any solid tile blocks the path → can't hear.
   984 				; Input: X = enemy index
   985 				; Output: Z=0 (can hear), Z=1 (blocked)
   986 				; ============================================
   987 418C			.proc can_hear
   988 418C 20 CF 25		        jsr get_enemy_tile_col
   989 418F 8D 1D 42		        sta ch_ecol
   990 4192 20 E5 25		        jsr get_player_tile_col
   991 4195 8D 1E 42		        sta ch_pcol
   992 				        ; Rows (16-bit en_yhi:en_y / 16)
   993 4198 BD 49 3A		        lda en_yhi,x
   994 419B 0A			        asl
   995 419C 0A			        asl
   996 419D 0A			        asl
   997 419E 0A			        asl                 ; hi * 16
   998 419F 8D 1F 42		        sta ch_erow
   999 41A2 BD 43 3A		        lda en_y,x
  1000 41A5 4A			        lsr
  1001 41A6 4A			        lsr
  1002 41A7 4A			        lsr
  1003 41A8 4A			        lsr                 ; lo / 16
  1004 41A9 0D 1F 42		        ora ch_erow
  1005 41AC 8D 1F 42		        sta ch_erow
  1006 				        ; Enable map access
  1007 41AF A9 9E		        lda #BANK_EN+BANK_MAP
  1008 41B1 8D 5F D6		        sta VBXE_BANK_SEL
  1009 				        ; --- Horizontal scan (on enemy's FEET row) ---
  1010 41B4 AC 1F 42		        ldy ch_erow
  1011 41B7 20 D8 41		        jsr ?hscan_row
  1012 41BA F0 03		        beq ?h2
  1013 41BC 4C 17 42		        jmp ?blocked
  1014 41BF			?h2     ; --- Horizontal scan (on enemy's CHEST row = feet-1) ---
  1015 41BF AD 1F 42		        lda ch_erow
  1016 41C2 F0 0C		        beq ?hok            ; row 0 = can't go higher
  1017 41C4 38			        sec
  1018 41C5 E9 01		        sbc #1
  1019 41C7 A8			        tay
  1020 41C8 20 D8 41		        jsr ?hscan_row
  1021 41CB F0 03		        beq ?hok
  1022 41CD 4C 17 42		        jmp ?blocked
  1023 41D0			?hok    ; Can hear — restore bank and return
  1024 41D0 A9 00		        lda #0
  1025 41D2 8D 5F D6		        sta VBXE_BANK_SEL
  1026 41D5 A9 01		        lda #1              ; Z=0 → can hear
  1027 41D7 60			        rts
  1028 				; Scan horizontal row Y for solid tiles between ecol and pcol
  1029 				; Input: Y = row to scan
  1030 				; Output: Z=1 (clear), Z=0 (blocked)
  1031 41D8			?hscan_row
  1032 41D8 B9 C6 A3		        lda map_row_lo,y
  1033 41DB 85 D4		        sta ztptr
  1034 41DD B9 E6 A3		        lda map_row_hi,y
  1035 41E0 85 D5		        sta ztptr+1
  1036 				        ; Sort: ch_cur = min(ecol,pcol)+1, ch_end = max(ecol,pcol)
  1037 41E2 AD 1D 42		        lda ch_ecol
  1038 41E5 AC 1E 42		        ldy ch_pcol
  1039 41E8 CC 1D 42		        cpy ch_ecol
  1040 41EB B0 04		        bcs ?hr_s
  1041 41ED 98			        tya                 ; swap: A=smaller
  1042 41EE AC 1D 42		        ldy ch_ecol         ; Y=larger
  1043 41F1 18			?hr_s   clc
  1044 41F2 69 01		        adc #1
  1045 41F4 8D 20 42		        sta ch_cur
  1046 41F7 8C 21 42		        sty ch_end
  1047 41FA AD 20 42		?hr_lp  lda ch_cur
  1048 41FD CD 21 42		        cmp ch_end
  1049 4200 B0 0F		        bcs ?hr_ok
  1050 4202 A8			        tay
  1051 4203 B1 D4		        lda (ztptr),y
  1052 4205 A8			        tay
  1053 4206 B9 9B A5		        lda tile_solid,y
  1054 4209 D0 09		        bne ?hr_blk
  1055 420B EE 20 42		        inc ch_cur
  1056 420E 4C FA 41		        jmp ?hr_lp
  1057 4211 A9 00		?hr_ok  lda #0              ; Z=1 → clear
  1058 4213 60			        rts
  1059 4214 A9 01		?hr_blk lda #1              ; Z=0 → blocked
  1060 4216 60			        rts
  1061
  1062 4217			?blocked
  1063 4217 A9 00		        lda #0
  1064 4219 8D 5F D6		        sta VBXE_BANK_SEL
  1065 				        ; A already 0 → Z=1 → blocked
  1066 421C 60			        rts
  1067 421D 00			ch_ecol dta 0
  1068 421E 00			ch_pcol dta 0
  1069 421F 00			ch_erow dta 0
  1070 4220 00			ch_cur  dta 0
  1071 4221 00			ch_end  dta 0
  1072 				.endp
  1073
  1074 				; ============================================
  1075 				; RENDER ALL ENEMIES
  1076 				; ============================================
  1077 4222			.proc render_enemies
  1078 4222 A2 00		        ldx #0
  1079 4224 BD 55 3A		?lp     lda en_act,x
  1080 4227 D0 03		        bne ?vis
  1081 4229 4C 8E 43		        jmp ?nx
  1082 422C 86 B5		?vis    stx zzidx
  1083 				        ; Screen position (world - camera)
  1084 422E BD 37 3A		        lda en_x,x
  1085 4231 38			        sec
  1086 4232 E5 D7		        sbc cam_x_lo
  1087 4234 85 D0		        sta zdx
  1088 4236 BD 3D 3A		        lda enxhi,x
  1089 4239 E5 D8		        sbc cam_x_hi
  1090 423B 85 D1		        sta zdxh
  1091 423D 10 03		        bpl ?xok
  1092 423F 4C 8C 43		        jmp ?nx2
  1093 4242 BD 43 3A		?xok    lda en_y,x
  1094 4245 38			        sec
  1095 4246 E9 20		        sbc #32
  1096 4248 48			        pha                 ; save lo
  1097 4249 BD 49 3A		        lda en_yhi,x
  1098 424C E9 00		        sbc #0
  1099 424E AA			        tax                 ; X = hi byte for calc_scr_y
  1100 424F 68			        pla                 ; A = lo byte
  1101 4250 20 85 A5		        jsr calc_scr_y
  1102 4253 90 07		        bcc ?jnx2
  1103 4255 85 D2		        sta zdy
  1104 4257 A6 B5		        ldx zzidx
  1105 4259 4C 5F 42		        jmp ?yok
  1106 425C 4C 8C 43		?jnx2   jmp ?nx2
  1107 425F			?yok
  1108 				        ; Check if dying -> use death sprite
  1109 425F BD 55 3A		        lda en_act,x
  1110 4262 C9 02		        cmp #2
  1111 4264 F0 03		        beq ?dying
  1112 4266 4C F4 42		        jmp ?walkanim
  1113 4269			?dying
  1114 				        ; Death animation: select sprite by timer phase and gib flag
  1115 				        ; Gib: timer 20-10 = gib1, 9-1 = gib2 + blink
  1116 				        ; Normal: timer 20-14 = death1, 13-7 = death2, 6-1 = death3 + blink
  1117 4269 BD 85 3A		        lda en_gib,x
  1118 426C D0 20		        bne ?gib
  1119 				        ; --- Normal death ---
  1120 426E BD 79 3A		        lda en_dtimer,x
  1121 4271 C9 0E		        cmp #14
  1122 4273 B0 0D		        bcs ?d_fr1
  1123 4275 C9 07		        cmp #7
  1124 4277 B0 0F		        bcs ?d_fr2
  1125 				        ; Blink in last phase
  1126 4279 4A			        lsr
  1127 427A B0 46		        bcs ?skip_draw
  1128 				        ; Frame 3: death3 from table (0 = fallback to death1)
  1129 427C 20 E3 42		        jsr ?get_death3
  1130 427F 4C AD 42		        jmp ?deathdraw
  1131 4282			?d_fr1  ; Frame 1: base death sprite (base_spr + 5)
  1132 4282 20 C5 42		        jsr ?get_death1
  1133 4285 4C AD 42		        jmp ?deathdraw
  1134 4288			?d_fr2  ; Frame 2: death2 from table (0 = fallback to death1)
  1135 4288 20 D2 42		        jsr ?get_death2
  1136 428B 4C AD 42		        jmp ?deathdraw
  1137 428E			?gib    ; --- Gib death ---
  1138 428E BD 79 3A		        lda en_dtimer,x
  1139 4291 C9 0A		        cmp #10
  1140 4293 B0 0F		        bcs ?g_fr1
  1141 				        ; Blink in last phase
  1142 4295 4A			        lsr
  1143 4296 B0 2A		        bcs ?skip_draw
  1144 				        ; Frame 2: gib2 from table
  1145 4298 BD 6D 3A		        lda en_type,x
  1146 429B AA			        tax
  1147 429C BD A5 5E		        lda en_gib2_spr,x
  1148 429F A6 B5		        ldx zzidx
  1149 42A1 4C AD 42		        jmp ?deathdraw
  1150 42A4			?g_fr1  ; Frame 1: gib1 from table
  1151 42A4 BD 6D 3A		        lda en_type,x
  1152 42A7 AA			        tax
  1153 42A8 BD 9F 5E		        lda en_gib1_spr,x
  1154 42AB A6 B5		        ldx zzidx
  1155 42AD			?deathdraw
  1156 				        ; Mark dirty + draw WITHOUT mirror
  1157 42AD 48			        pha
  1158 42AE A9 10		        lda #16
  1159 42B0 8D 5D 55		        sta md_w
  1160 42B3 A9 20		        lda #32
  1161 42B5 8D 5E 55		        sta md_h
  1162 42B8 20 90 54		        jsr mark_dirty_sprite
  1163 42BB 68			        pla
  1164 42BC 20 C8 23		        jsr blit_sprite
  1165 42BF 4C 8C 43		        jmp ?nx2
  1166 42C2			?skip_draw
  1167 42C2 4C 8C 43		        jmp ?nx2
  1168 				        ; --- Helper: get death sprite by type ---
  1169 42C5			?get_death1
  1170 42C5 BD 6D 3A		        lda en_type,x
  1171 42C8 AA			        tax
  1172 42C9 BD 81 5E		        lda en_base_spr,x
  1173 42CC 18			        clc
  1174 42CD 69 05		        adc #5
  1175 42CF A6 B5		        ldx zzidx
  1176 42D1 60			        rts
  1177 42D2			?get_death2
  1178 42D2 BD 6D 3A		        lda en_type,x
  1179 42D5 AA			        tax
  1180 42D6 BD 93 5E		        lda en_death2_spr,x
  1181 42D9 F0 03		        beq ?gd2_fb         ; 0 = no death2, use death1
  1182 42DB A6 B5		        ldx zzidx
  1183 42DD 60			        rts
  1184 42DE A6 B5		?gd2_fb ldx zzidx
  1185 42E0 4C C5 42		        jmp ?get_death1
  1186 42E3			?get_death3
  1187 42E3 BD 6D 3A		        lda en_type,x
  1188 42E6 AA			        tax
  1189 42E7 BD 99 5E		        lda en_death3_spr,x
  1190 42EA F0 03		        beq ?gd3_fb         ; 0 = no death3, use death1
  1191 42EC A6 B5		        ldx zzidx
  1192 42EE 60			        rts
  1193 42EF A6 B5		?gd3_fb ldx zzidx
  1194 42F1 4C C5 42		        jmp ?get_death1
  1195 42F4			?walkanim
  1196 				        ; Calculate sprite index: base + walk frame
  1197 42F4 BD 6D 3A		        lda en_type,x
  1198 42F7 AA			        tax
  1199 42F8 BD 81 5E		        lda en_base_spr,x
  1200 42FB 8D 97 43		        sta re_base
  1201 42FE A6 B5		        ldx zzidx
  1202 				        ; Check pain first
  1203 4300 BD FD 44		        lda en_pain_tmr,x
  1204 4303 F0 26		        beq ?no_epain
  1205 				        ; Show pain sprite
  1206 4305 BD 6D 3A		        lda en_type,x
  1207 4308 A8			        tay
  1208 4309 B9 B1 5E		        lda en_pain_spr,y
  1209 430C F0 1D		        beq ?no_epain          ; 0 = no pain sprite
  1210 430E BC 4F 3A		        ldy en_dir,x
  1211 4311 F0 03		        beq ?epain_r
  1212 4313 4C 19 43		        jmp ?dodraw_pain       ; dir=1 (left) = base variant
  1213 4316			?epain_r
  1214 4316 18			        clc
  1215 4317 69 01		        adc #1                 ; +1 = _L variant
  1216 4319			?dodraw_pain
  1217 4319 48			        pha
  1218 431A A9 10		        lda #16
  1219 431C 8D 5D 55		        sta md_w
  1220 431F A9 20		        lda #32
  1221 4321 8D 5E 55		        sta md_h
  1222 4324 20 90 54		        jsr mark_dirty_sprite
  1223 4327 68			        pla
  1224 4328 4C 89 43		        jmp ?dopain
  1225 432B			?no_epain
  1226 				        ; Check if shooting (just fired: alerted + timer in shoot range)
  1227 432B BD 6D 3A		        lda en_type,x
  1228 432E C9 00		        cmp #EN_ZOMBIE
  1229 4330 F0 13		        beq ?chk_shoot
  1230 4332 C9 04		        cmp #EN_SHOTGUN
  1231 4334 F0 0F		        beq ?chk_shoot
  1232 4336 C9 01		        cmp #EN_IMP
  1233 4338 F0 0B		        beq ?chk_shoot
  1234 433A C9 02		        cmp #EN_PINKY
  1235 433C F0 07		        beq ?chk_shoot
  1236 433E C9 03		        cmp #EN_CACO
  1237 4340 F0 03		        beq ?chk_shoot
  1238 4342 4C 5C 43		        jmp ?walk
  1239 4345			?chk_shoot
  1240 4345 BD 73 3A		        lda en_cooldown,x
  1241 4348 F0 12		        beq ?walk              ; not alerted = no shoot sprite
  1242 434A BD 91 3A		        lda en_atk,x
  1243 434D C9 0F		        cmp #15
  1244 434F B0 0B		        bcs ?walk
  1245 4351 F0 09		        beq ?walk
  1246 4353 AD 97 43		        lda re_base
  1247 4356 18			        clc
  1248 4357 69 04		        adc #4                 ; shoot = base + 4
  1249 4359 4C 70 43		        jmp ?dodraw
  1250 435C			?walk   ; Walk animation: frame = (zfr >> 3) & 3, cycle 0,1,2,1
  1251 435C A5 90		        lda zfr
  1252 435E 4A			        lsr
  1253 435F 4A			        lsr
  1254 4360 4A			        lsr
  1255 4361 29 03		        and #$03
  1256 4363 C9 03		        cmp #3
  1257 4365 D0 02		        bne ?fok
  1258 4367 A9 01		        lda #1
  1259 4369 18			?fok    clc
  1260 436A 69 01		        adc #1             ; +1 because walk frames start at base+1
  1261 436C 18			        clc
  1262 436D 6D 97 43		        adc re_base
  1263 4370			?dodraw ; Mark dirty tiles before drawing
  1264 4370 48			        pha
  1265 4371 A9 10		        lda #16
  1266 4373 8D 5D 55		        sta md_w
  1267 4376 A9 20		        lda #32
  1268 4378 8D 5E 55		        sta md_h
  1269 437B 20 90 54		        jsr mark_dirty_sprite
  1270 437E 68			        pla
  1271 				        ; Base sprites face LEFT, _L sprites face RIGHT
  1272 				        ; dir=0 (right) -> +MIRROR_OFFSET, dir=1 (left) -> base
  1273 437F A6 B5		        ldx zzidx
  1274 4381 BC 4F 3A		        ldy en_dir,x
  1275 4384 D0 03		        bne ?noflip
  1276 4386 18			        clc
  1277 4387 69 3C		        adc #MIRROR_OFFSET
  1278 4389			?noflip
  1279 4389 20 C8 23		?dopain jsr blit_sprite
  1280 438C A6 B5		?nx2    ldx zzidx
  1281 438E E8			?nx     inx
  1282 438F E0 06		        cpx #MAX_ENEMIES
  1283 4391 B0 03		        bcs ?ret
  1284 4393 4C 24 42		        jmp ?lp
  1285 4396 60			?ret    rts
  1286 4397 00			re_base dta 0
  1287 				.endp
  1288
  1289 				; ============================================
  1290 				; CHECK PROJECTILE HIT ON ALL ENEMIES
  1291 				; Returns: C=1 if hit (enemy killed/damaged), C=0 if no hit
  1292 				; Input: proj_x[zt], proj_y[zt] via X index in zt
  1293 				; ============================================
  1294 4398			.proc check_proj_enemies
  1295 4398 A5 80		        lda zt
  1296 439A 8D F3 44		        sta cp_proj
  1297 439D A9 00		        lda #0
  1298 439F 8D F4 44		        sta cp_eidx
  1299 43A2 AE F4 44		?lp     ldx cp_eidx
  1300 43A5 BD 55 3A		        lda en_act,x
  1301 43A8 C9 01		        cmp #1              ; only alive enemies (skip dead=0 and dying=2)
  1302 43AA F0 03		        beq ?chk
  1303 43AC 4C E4 44		        jmp ?nxe
  1304 43AF			?chk    ; 16-bit X distance: |proj_x16 - (en_x16 + 8)| < 14
  1305 43AF AE F3 44		        ldx cp_proj
  1306 43B2 AC F4 44		        ldy cp_eidx
  1307 43B5 B9 37 3A		        lda en_x,y
  1308 43B8 18			        clc
  1309 43B9 69 08		        adc #8              ; enemy center X low
  1310 43BB 8D F5 44		        sta cp_tmp
  1311 43BE B9 3D 3A		        lda enxhi,y
  1312 43C1 69 00		        adc #0              ; propagate carry from adc #8
  1313 43C3 8D F6 44		        sta cp_tmph
  1314 43C6 BD 1E 2D		        lda proj_x,x
  1315 43C9 38			        sec
  1316 43CA ED F5 44		        sbc cp_tmp          ; distance low byte
  1317 43CD 48			        pha                 ; save on stack
  1318 43CE BD 22 2D		        lda proj_xh,x
  1319 43D1 ED F6 44		        sbc cp_tmph         ; distance high byte (carry-corrected)
  1320 43D4 F0 13		        beq ?pa             ; high=0: positive small distance
  1321 43D6 C9 FF		        cmp #$FF
  1322 43D8 D0 0B		        bne ?nxe_pla        ; high not 0 or $FF: too far
  1323 				        ; Negative distance: negate low byte
  1324 43DA 68			        pla
  1325 43DB 49 FF		        eor #$FF
  1326 43DD 18			        clc
  1327 43DE 69 01		        adc #1
  1328 43E0 90 08		        bcc ?pa2            ; normal negation, check distance
  1329 43E2 4C E4 44		        jmp ?nxe            ; low was 0 → distance = 256, too far
  1330 43E5 68			?nxe_pla pla
  1331 43E6 4C E4 44		        jmp ?nxe
  1332 43E9 68			?pa     pla                 ; restore low byte
  1333 43EA C9 0E		?pa2    cmp #14             ; hit box ~14px wide
  1334 43EC 90 03		        bcc ?xhit
  1335 43EE 4C E4 44		        jmp ?nxe
  1336 43F1			?xhit
  1337 				        ; Check Y distance (compare with enemy center: en_y - 14)
  1338 				        ; Hi byte check first
  1339 43F1 B9 49 3A		        lda en_yhi,y
  1340 43F4 DD 2A 2D		        cmp proj_yhi,x
  1341 43F7 D0 19		        bne ?jnxe           ; different hi bytes = too far
  1342 43F9 B9 43 3A		        lda en_y,y
  1343 43FC 38			        sec
  1344 43FD E9 0E		        sbc #14             ; enemy center Y
  1345 43FF 85 81		        sta zt2
  1346 4401 BD 26 2D		        lda proj_y,x
  1347 4404 38			        sec
  1348 4405 E5 81		        sbc zt2
  1349 4407 10 05		        bpl ?pb
  1350 4409 49 FF		        eor #$FF
  1351 440B 18			        clc
  1352 440C 69 01		        adc #1
  1353 440E C9 12		?pb     cmp #18
  1354 4410 90 03		        bcc ?yhit
  1355 4412 4C E4 44		?jnxe   jmp ?nxe
  1356 4415			?yhit
  1357 				        ; Hit! Decrement HP by projectile damage
  1358 4415 AE F3 44		        ldx cp_proj
  1359 4418 BD 32 2D		        lda proj_dmg,x
  1360 441B 85 81		        sta zt2
  1361 441D AE F4 44		        ldx cp_eidx
  1362 4420 BD 5B 3A		        lda en_hp,x
  1363 4423 38			        sec
  1364 4424 E5 81		        sbc zt2
  1365 4426 B0 02		        bcs ?hp_ok
  1366 4428 A9 00		        lda #0
  1367 442A 9D 5B 3A		?hp_ok  sta en_hp,x
  1368 442D A9 08		        lda #8                 ; pain sprite for 8 frames
  1369 442F 9D FD 44		        sta en_pain_tmr,x
  1370 				        ; Alert: face player when hit (16-bit compare)
  1371 4432 BD 3D 3A		        lda enxhi,x
  1372 4435 C5 9C		        cmp zpx_hi
  1373 4437 90 0D		        bcc ?face_r
  1374 4439 D0 07		        bne ?face_l
  1375 443B BD 37 3A		        lda en_x,x
  1376 443E C5 91		        cmp zpx
  1377 4440 90 04		        bcc ?face_r
  1378 4442			?face_l
  1379 4442 A9 01		        lda #1              ; player is left, face left
  1380 4444 D0 02		        bne ?set_dir
  1381 4446 A9 00		?face_r lda #0              ; player is right, face right
  1382 4448			?set_dir
  1383 4448 9D 4F 3A		        sta en_dir,x
  1384 				        ; Check if dead
  1385 444B BD 5B 3A		        lda en_hp,x
  1386 444E F0 03		        beq ?dead_chk
  1387 4450 4C C1 44		        jmp ?alive
  1388 4453			?dead_chk
  1389 				        ; Enemy died - start death animation
  1390 4453 A9 02		        lda #2              ; dying state
  1391 4455 9D 55 3A		        sta en_act,x
  1392 4458 A9 14		        lda #20             ; death anim duration (frames)
  1393 445A 9D 79 3A		        sta en_dtimer,x
  1394 445D A9 00		        lda #0
  1395 445F 9D 85 3A		        sta en_gib,x
  1396 4462 9D 8B 3A		        sta envelx,x
  1397 				        ; Check if killed by rocket -> gib + knockback
  1398 				        ; Only zombie(0), imp(1), shotgun(4) can be gibbed
  1399 4465 BD 6D 3A		        lda en_type,x
  1400 4468 C9 00		        cmp #EN_ZOMBIE
  1401 446A F0 0B		        beq ?chk_rocket
  1402 446C C9 01		        cmp #EN_IMP
  1403 446E F0 07		        beq ?chk_rocket
  1404 4470 C9 04		        cmp #EN_SHOTGUN
  1405 4472 F0 03		        beq ?chk_rocket
  1406 4474 4C BB 44		        jmp ?no_gib         ; pinky, caco, baron = normal death only
  1407 4477			?chk_rocket
  1408 4477 AE F3 44		        ldx cp_proj
  1409 447A BD 36 2D		        lda proj_spr,x
  1410 447D AE F4 44		        ldx cp_eidx
  1411 4480 C9 68		        cmp #SPR_ROCKET_PROJ
  1412 4482 D0 37		        bne ?no_gib
  1413 				        ; Rocket kill! Set gib flag + knockback
  1414 4484 A9 01		        lda #1
  1415 4486 9D 85 3A		        sta en_gib,x
  1416 4489 A9 FE		        lda #$FE            ; upward velocity (-2, gentle arc)
  1417 448B 9D 7F 3A		        sta envely,x
  1418 				        ; Knockback direction: push away from player
  1419 448E BD 3D 3A		        lda enxhi,x
  1420 4491 C5 9C		        cmp zpx_hi
  1421 4493 90 0D		        bcc ?kb_l           ; enemy left of player
  1422 4495 D0 07		        bne ?kb_r
  1423 4497 BD 37 3A		        lda en_x,x
  1424 449A C5 91		        cmp zpx
  1425 449C 90 04		        bcc ?kb_l
  1426 449E A9 02		?kb_r   lda #2              ; push right
  1427 44A0 D0 02		        bne ?kb_set
  1428 44A2 A9 FE		?kb_l   lda #$FE            ; push left (-2)
  1429 44A4 9D 8B 3A		?kb_set sta envelx,x
  1430 				        ; Gib sound (DSSLOP) with priority lock
  1431 44A7 86 81		        stx zt2
  1432 44A9 A9 00		        lda #0
  1433 44AB 85 CB		        sta snd_lock
  1434 44AD A2 0B		        ldx #SFX_SLOP
  1435 44AF 20 A2 5A		        jsr snd_play
  1436 44B2 A9 08		        lda #8
  1437 44B4 85 CB		        sta snd_lock
  1438 44B6 A6 81		        ldx zt2
  1439 44B8 4C D4 44		        jmp ?deact
  1440 44BB			?no_gib ; Normal death sound by enemy type
  1441 44BB 20 91 5B		        jsr play_enemy_death
  1442 44BE 4C D4 44		        jmp ?deact
  1443 				        ; --- Enemy survived (HP > 0) ---
  1444 44C1 AE F3 44		?alive  ldx cp_proj
  1445 44C4 BD 36 2D		        lda proj_spr,x
  1446 44C7 C9 68		        cmp #SPR_ROCKET_PROJ
  1447 44C9 D0 09		        bne ?deact
  1448 44CB 8A			        txa
  1449 44CC 48			        pha
  1450 44CD A2 0A		        ldx #SFX_BAREXP
  1451 44CF 20 A2 5A		        jsr snd_play
  1452 44D2 68			        pla
  1453 44D3 AA			        tax
  1454 				        ; --- Deactivate projectile and return ---
  1455 44D4 AE F3 44		?deact  ldx cp_proj
  1456 44D7 20 20 30		        jsr rocket_splash_player
  1457 44DA AE F3 44		        ldx cp_proj
  1458 44DD A9 00		        lda #0
  1459 44DF 9D 1A 2D		        sta proj_a,x
  1460 44E2 38			        sec              ; hit
  1461 44E3 60			        rts
  1462 44E4 EE F4 44		?nxe    inc cp_eidx
  1463 44E7 AD F4 44		        lda cp_eidx
  1464 44EA C9 06		        cmp #MAX_ENEMIES
  1465 44EC B0 03		        bcs ?miss
  1466 44EE 4C A2 43		        jmp ?lp
  1467 44F1 18			?miss   clc              ; no hit
  1468 44F2 60			        rts
  1469 44F3 00			cp_proj dta 0
  1470 44F4 00			cp_eidx dta 0
  1471 44F5 00			cp_tmp  dta 0
  1472 44F6 00			cp_tmph dta 0
  1473 				.endp
  1474
  1475 				; Turn counter for stuck detection (placed here to avoid shifting code layout)
  1476 44F7 00 00 00 00 00 00	en_tcnt      dta 0,0,0,0,0,0    ; MAX_ENEMIES = 6
  1477 44FD 00 00 00 00 00 00	en_pain_tmr  dta 0,0,0,0,0,0    ; pain sprite timer
  1478
  1479 				; ============================================
  1480 				; ENEMY PROJECTILES (imp fireball etc.)
  1481 				; ============================================
  1482 = 0003			MAX_EPROJ = 3
  1483
  1484 = 4503			eproj_a   .ds MAX_EPROJ       ; 0=inactive, 1=active
  1485 = 4506			eproj_x   .ds MAX_EPROJ       ; X lo
  1486 = 4509			eproj_xh  .ds MAX_EPROJ       ; X hi
  1487 = 450C			eproj_y   .ds MAX_EPROJ       ; Y lo
  1488 = 450F			eproj_yhi .ds MAX_EPROJ       ; Y hi
  1489 = 4512			eproj_vx  .ds MAX_EPROJ       ; velocity X (signed)
  1490 = 4515			eproj_dmg .ds MAX_EPROJ       ; damage
  1491 = 4518			eproj_spr .ds MAX_EPROJ       ; sprite index
  1492
  1493 451B			.proc init_eproj
  1494 451B-5190> A2 02		        ldx #MAX_EPROJ-1
  1495 451D A9 00		?lp     lda #0
  1496 451F 9D 03 45		        sta eproj_a,x
  1497 4522 CA			        dex
  1498 4523 10 F8		        bpl ?lp
  1499 4525 60			        rts
  1500 				.endp
  1501
  1502 				; Spawn enemy projectile
  1503 				; Input: X = enemy index (en_x/enxhi/en_y/en_dir used for position)
  1504 4526			.proc spawn_eproj
  1505 4526 8E AB 45		        stx ep_eidx
  1506 				        ; Find free slot
  1507 4529 A2 00		        ldx #0
  1508 452B BD 03 45		?f      lda eproj_a,x
  1509 452E F0 06		        beq ?ok
  1510 4530 E8			        inx
  1511 4531 E0 03		        cpx #MAX_EPROJ
  1512 4533 D0 F6		        bne ?f
  1513 4535 60			        rts                    ; no free slot
  1514 4536 A9 01		?ok     lda #1
  1515 4538 9D 03 45		        sta eproj_a,x
  1516 				        ; Position from enemy + offset in fire direction
  1517 453B AC AB 45		        ldy ep_eidx
  1518 453E B9 4F 3A		        lda en_dir,y
  1519 4541 D0 14		        bne ?sp_left
  1520 				        ; Facing right: X + 12
  1521 4543 B9 37 3A		        lda en_x,y
  1522 4546 18			        clc
  1523 4547 69 0C		        adc #12
  1524 4549 9D 06 45		        sta eproj_x,x
  1525 454C B9 3D 3A		        lda enxhi,y
  1526 454F 69 00		        adc #0
  1527 4551 9D 09 45		        sta eproj_xh,x
  1528 4554 4C 68 45		        jmp ?sp_y
  1529 4557			?sp_left
  1530 				        ; Facing left: X - 4
  1531 4557 B9 37 3A		        lda en_x,y
  1532 455A 38			        sec
  1533 455B E9 04		        sbc #4
  1534 455D 9D 06 45		        sta eproj_x,x
  1535 4560 B9 3D 3A		        lda enxhi,y
  1536 4563 E9 00		        sbc #0
  1537 4565 9D 09 45		        sta eproj_xh,x
  1538 4568 B9 43 3A		?sp_y   lda en_y,y
  1539 456B 38			        sec
  1540 456C E9 0E		        sbc #14                ; chest height
  1541 456E 9D 0C 45		        sta eproj_y,x
  1542 4571 B9 49 3A		        lda en_yhi,y
  1543 4574 E9 00		        sbc #0
  1544 4576 9D 0F 45		        sta eproj_yhi,x
  1545 				        ; Velocity: toward player direction
  1546 4579 B9 4F 3A		        lda en_dir,y
  1547 457C D0 08		        bne ?left
  1548 457E A9 03		        lda #3                 ; right
  1549 4580 9D 12 45		        sta eproj_vx,x
  1550 4583 4C 8B 45		        jmp ?setdmg
  1551 4586 A9 FD		?left   lda #$FD               ; left (-3)
  1552 4588 9D 12 45		        sta eproj_vx,x
  1553 458B			?setdmg ; Set damage and sprite based on enemy type
  1554 458B AC AB 45		        ldy ep_eidx
  1555 458E B9 6D 3A		        lda en_type,y
  1556 4591 C9 03		        cmp #EN_CACO
  1557 4593 F0 0B		        beq ?caco_fb
  1558 				        ; Imp fireball: 20 damage, red sprite
  1559 4595 A9 14		        lda #20
  1560 4597 9D 15 45		        sta eproj_dmg,x
  1561 459A A9 8A		        lda #SPR_IMP_FIRE1
  1562 459C 9D 18 45		        sta eproj_spr,x
  1563 459F 60			        rts
  1564 45A0			?caco_fb ; Caco fireball: 15 damage, different sprite
  1565 45A0 A9 0F		        lda #15
  1566 45A2 9D 15 45		        sta eproj_dmg,x
  1567 45A5 A9 90		        lda #SPR_CACO_FIRE1
  1568 45A7 9D 18 45		        sta eproj_spr,x
  1569 45AA 60			        rts
  1570 45AB 00			ep_eidx dta 0
  1571 				.endp
  1572
  1573 				; Update enemy projectiles
  1574 45AC			.proc eproj_update
  1575 45AC A2 00		        ldx #0
  1576 45AE BD 03 45		?lp     lda eproj_a,x
  1577 45B1 D0 03		        bne ?upd
  1578 45B3 4C 85 46		        jmp ?nx
  1579 45B6			?upd    ; Animate sprite (toggle frame every 4 frames)
  1580 				        ; Determine base sprite from current sprite
  1581 45B6 BD 18 45		        lda eproj_spr,x
  1582 45B9 C9 90		        cmp #SPR_CACO_FIRE1
  1583 45BB B0 16		        bcs ?caco_anim
  1584 				        ; Imp fireball animation
  1585 45BD A5 90		        lda zfr
  1586 45BF 29 04		        and #$04
  1587 45C1 F0 08		        beq ?f1
  1588 45C3 A9 8B		        lda #SPR_IMP_FIRE2
  1589 45C5 9D 18 45		        sta eproj_spr,x
  1590 45C8 4C E6 45		        jmp ?move
  1591 45CB A9 8A		?f1     lda #SPR_IMP_FIRE1
  1592 45CD 9D 18 45		        sta eproj_spr,x
  1593 45D0 4C E6 45		        jmp ?move
  1594 45D3			?caco_anim
  1595 45D3 A5 90		        lda zfr
  1596 45D5 29 04		        and #$04
  1597 45D7 F0 08		        beq ?cf1
  1598 45D9 A9 91		        lda #SPR_CACO_FIRE2
  1599 45DB 9D 18 45		        sta eproj_spr,x
  1600 45DE 4C E6 45		        jmp ?move
  1601 45E1 A9 90		?cf1    lda #SPR_CACO_FIRE1
  1602 45E3 9D 18 45		        sta eproj_spr,x
  1603 45E6			?move   ; Update 16-bit X
  1604 45E6 BD 06 45		        lda eproj_x,x
  1605 45E9 18			        clc
  1606 45EA 7D 12 45		        adc eproj_vx,x
  1607 45ED 9D 06 45		        sta eproj_x,x
  1608 45F0 BD 12 45		        lda eproj_vx,x
  1609 45F3 30 0B		        bmi ?negv
  1610 45F5 BD 09 45		        lda eproj_xh,x
  1611 45F8 69 00		        adc #0
  1612 45FA 9D 09 45		        sta eproj_xh,x
  1613 45FD 4C 08 46		        jmp ?bounds
  1614 4600 BD 09 45		?negv   lda eproj_xh,x
  1615 4603 69 FF		        adc #$FF
  1616 4605 9D 09 45		        sta eproj_xh,x
  1617 4608			?bounds ; Kill if off screen
  1618 4608 BD 09 45		        lda eproj_xh,x
  1619 460B 30 73		        bmi ?kill
  1620 460D C9 02		        cmp #2
  1621 460F B0 6F		        bcs ?kill
  1622 				        ; Check wall collision
  1623 4611 8E 94 46		        stx ep_idx
  1624 4614 BD 06 45		        lda eproj_x,x
  1625 4617 18			        clc
  1626 4618 69 04		        adc #4
  1627 461A 8D B0 25		        sta gt_px
  1628 461D BD 09 45		        lda eproj_xh,x
  1629 4620 69 00		        adc #0
  1630 4622 8D B1 25		        sta gt_px_hi
  1631 4625 BD 0C 45		        lda eproj_y,x
  1632 4628 8D B2 25		        sta gt_py
  1633 462B BD 0F 45		        lda eproj_yhi,x
  1634 462E 8D B3 25		        sta gt_py_hi
  1635 4631 20 F9 25		        jsr check_solid
  1636 4634 D0 47		        bne ?kill2
  1637 				        ; Check player collision (8x8 vs player 10x28)
  1638 4636 AE 94 46		        ldx ep_idx
  1639 				        ; X distance
  1640 4639 BD 06 45		        lda eproj_x,x
  1641 463C 38			        sec
  1642 463D E5 91		        sbc zpx
  1643 463F 8D 95 46		        sta ep_dx
  1644 4642 BD 09 45		        lda eproj_xh,x
  1645 4645 E5 9C		        sbc zpx_hi
  1646 4647 D0 44		        bne ?nx2               ; hi byte diff = too far
  1647 4649 AD 95 46		        lda ep_dx
  1648 464C 10 05		        bpl ?xpos
  1649 464E 49 FF		        eor #$FF
  1650 4650 18			        clc
  1651 4651 69 01		        adc #1
  1652 4653 C9 0C		?xpos   cmp #12                ; within 12px X
  1653 4655 B0 36		        bcs ?nx2
  1654 				        ; Y distance (hi byte check first)
  1655 4657 BD 0F 45		        lda eproj_yhi,x
  1656 465A C5 9D		        cmp zpy_hi
  1657 465C D0 2F		        bne ?nx2               ; different hi bytes = too far
  1658 465E BD 0C 45		        lda eproj_y,x
  1659 4661 38			        sec
  1660 4662 E5 92		        sbc zpy
  1661 4664 10 05		        bpl ?ypos
  1662 4666 49 FF		        eor #$FF
  1663 4668 18			        clc
  1664 4669 69 01		        adc #1
  1665 466B C9 18		?ypos   cmp #24                ; within 24px Y
  1666 466D B0 1E		        bcs ?nx2
  1667 				        ; Hit player!
  1668 466F BD 15 45		        lda eproj_dmg,x
  1669 4672 20 9E 30		        jsr player_take_damage
  1670 4675 A9 00		        lda #0
  1671 4677 9D 03 45		        sta eproj_a,x
  1672 467A 4C 85 46		        jmp ?nx
  1673 467D AE 94 46		?kill2  ldx ep_idx
  1674 4680 A9 00		?kill   lda #0
  1675 4682 9D 03 45		        sta eproj_a,x
  1676 4685 E8			?nx     inx
  1677 4686 E0 03		        cpx #MAX_EPROJ
  1678 4688 F0 09		        beq ?done
  1679 468A 4C AE 45		        jmp ?lp
  1680 468D AE 94 46		?nx2    ldx ep_idx
  1681 4690 4C 85 46		        jmp ?nx
  1682 4693 60			?done   rts
  1683 4694 00			ep_idx  dta 0
  1684 4695 00			ep_dx   dta 0
  1685 				.endp
  1686
  1687 				; Render enemy projectiles
  1688 4696			.proc render_eproj
  1689 4696 A2 00		        ldx #0
  1690 4698 BD 03 45		?lp     lda eproj_a,x
  1691 469B F0 3C		        beq ?nx
  1692 469D 8E E2 46		        stx ep_ridx
  1693 				        ; Set draw position (world - camera)
  1694 46A0 BD 06 45		        lda eproj_x,x
  1695 46A3 38			        sec
  1696 46A4 E5 D7		        sbc cam_x_lo
  1697 46A6 85 D0		        sta zdx
  1698 46A8 BD 09 45		        lda eproj_xh,x
  1699 46AB E5 D8		        sbc cam_x_hi
  1700 46AD 85 D1		        sta zdxh
  1701 46AF BD 0C 45		        lda eproj_y,x
  1702 46B2 48			        pha                 ; save lo
  1703 46B3 BD 0F 45		        lda eproj_yhi,x
  1704 46B6 AA			        tax                 ; X = hi byte for calc_scr_y
  1705 46B7 68			        pla                 ; A = lo byte
  1706 46B8 20 85 A5		        jsr calc_scr_y
  1707 46BB 90 19		        bcc ?epskip
  1708 46BD 85 D2		        sta zdy
  1709 46BF AE E2 46		        ldx ep_ridx
  1710 				        ; Mark dirty
  1711 46C2 A9 08		        lda #8
  1712 46C4 8D 5D 55		        sta md_w
  1713 46C7 8D 5E 55		        sta md_h
  1714 46CA 20 90 54		        jsr mark_dirty_sprite
  1715 				        ; Blit sprite
  1716 46CD AE E2 46		        ldx ep_ridx
  1717 46D0 BD 18 45		        lda eproj_spr,x
  1718 46D3 20 C8 23		        jsr blit_sprite
  1719 46D6 AE E2 46		?epskip ldx ep_ridx
  1720 46D9 E8			?nx     inx
  1721 46DA E0 03		        cpx #MAX_EPROJ
  1722 46DC F0 03		        beq ?done
  1723 46DE 4C 98 46		        jmp ?lp
  1724 46E1 60			?done   rts
  1725 46E2 00			ep_ridx dta 0
  1726 				.endp
   320 46E3			        icl 'renderer.asm'
Source: renderer.asm
     1 				;==============================================
     2 				; DOOM2D - Rendering (tiles, sprites)
     3 				; renderer.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; RENDER TILES
     8 				; ============================================
     9 46E3			.proc render_tiles
    10 46E3 A9 00		        lda #0
    11 46E5 8D 34 47		        sta r_row
    12 46E8 A9 00		?rrow   lda #0
    13 46EA 8D 35 47		        sta r_col
    14 46ED			?rcol
    15 				        ; Map lookup at (cam_tx + r_col, cam_ty + r_row)
    16 46ED A9 9E		        lda #BANK_EN+BANK_MAP
    17 46EF 8D 5F D6		        sta VBXE_BANK_SEL
    18 46F2 AD 34 47		        lda r_row
    19 46F5 18			        clc
    20 46F6 65 DE		        adc cam_ty
    21 46F8 A8			        tay
    22 46F9 B9 C6 A3		        lda map_row_lo,y
    23 46FC 18			        clc
    24 46FD 65 D6		        adc cam_tx
    25 46FF 6D 35 47		        adc r_col
    26 4702 85 D4		        sta ztptr
    27 4704 B9 E6 A3		        lda map_row_hi,y
    28 4707 69 00		        adc #0
    29 4709 85 D5		        sta ztptr+1
    30
    31 470B A0 00		        ldy #0
    32 470D B1 D4		        lda (ztptr),y
    33 470F F0 0D		        beq ?empty
    34 4711 C9 0F		        cmp #15
    35 4713 F0 09		        beq ?empty
    36 4715 8D 36 47		        sta r_tile
    37 4718 20 01 23		        jsr blit_tile
    38 471B 4C 1E 47		        jmp ?next
    39 471E			?empty  ; Empty tile: sky already filled by clear_screen (336px wide)
    40 471E EE 35 47		?next   inc r_col
    41 4721 AD 35 47		        lda r_col
    42 4724 C9 15		        cmp #RENDER_COLS       ; 21 columns (includes padding for XDL scroll)
    43 4726 90 C5		        bcc ?rcol
    44 4728 EE 34 47		        inc r_row
    45 472B AD 34 47		        lda r_row
    46 472E C9 0C		        cmp #TILES_Y
    47 4730 90 B6		        bcc ?rrow
    48 4732 60			        rts
    49 				.endp
    50
    51 4733 00			rt_clip_h dta 0                  ; clip height for extra row (1-3)
    52
    53 4734 00			r_row   dta 0
    54 4735 00			r_col   dta 0
    55 4736 00			r_tile  dta 0
    56
    57 				; ============================================
    58 				; RENDER PLAYER
    59 				; ============================================
    60 4737			.proc render_player
    61 4737 A5 91		        lda zpx
    62 4739 38			        sec
    63 473A E5 D7		        sbc cam_x_lo
    64 473C 85 D0		        sta zdx
    65 473E A5 9C		        lda zpx_hi
    66 4740 E5 D8		        sbc cam_x_hi
    67 4742 85 D1		        sta zdxh
    68 4744 10 03		        bpl ?xok
    69 4746 4C 23 48		        jmp ?done
    70 4749			?xok
    71 				        ; screen Y = (zpy_hi:zpy - 32) - cam_y_hi:cam_y
    72 4749 A5 92		        lda zpy
    73 474B 38			        sec
    74 474C E9 20		        sbc #32
    75 474E 85 80		        sta zt
    76 4750 A5 9D		        lda zpy_hi
    77 4752 E9 00		        sbc #0
    78 4754 85 81		        sta zt2                 ; zt2:zt = zpy - 32
    79 4756 A5 80		        lda zt
    80 4758 38			        sec
    81 4759 E5 DF		        sbc cam_y
    82 475B 85 80		        sta zt
    83 475D A5 81		        lda zt2
    84 475F E5 E1		        sbc cam_y_hi
    85 4761 90 0D		        bcc ?done_jmp           ; off screen top (negative)
    86 4763 D0 0B		        bne ?done_jmp           ; high byte > 0 → off screen bottom (> 255)
    87 4765 A5 80		        lda zt
    88 4767 C9 C1		        cmp #193
    89 4769 B0 05		        bcs ?done_jmp           ; > 192 → off screen bottom
    90 476B 85 D2		        sta zdy
    91 476D 4C 73 47		        jmp ?y2ok
    92 4770 4C 23 48		?done_jmp jmp ?done
    93 4773			?y2ok
    94 				        ; Dead? Show death animation (3 frames + blink)
    95 4773 A5 98		        lda zphp
    96 4775 D0 3B		        bne ?not_dead
    97 4777 AD 5D 2E		        lda pl_dead_timer
    98 477A C9 50		        cmp #80
    99 477C B0 17		        bcs ?d_blink           ; 80+: blink
   100 477E C9 28		        cmp #40
   101 4780 B0 0E		        bcs ?d_fr3             ; 40-79: death3
   102 4782 C9 0F		        cmp #15
   103 4784 B0 05		        bcs ?d_fr2             ; 15-39: death2
   104 4786 A9 05		        lda #SPR_PL_DEATH      ; 0-14: death1
   105 4788 4C 9D 47		        jmp ?draw_death
   106 478B A9 88		?d_fr2  lda #SPR_PL_DEATH2
   107 478D 4C 9D 47		        jmp ?draw_death
   108 4790 A9 89		?d_fr3  lda #SPR_PL_DEATH3
   109 4792 4C 9D 47		        jmp ?draw_death
   110 4795 4A			?d_blink lsr
   111 4796 90 03		        bcc ?d_show
   112 4798 4C 23 48		        jmp ?done              ; blink: odd = hidden
   113 479B			?d_show
   114 479B A9 89		        lda #SPR_PL_DEATH3
   115 479D			?draw_death
   116 				        ; Death sprites: no mirror, direct blit
   117 479D 48			        pha
   118 479E A9 10		        lda #16
   119 47A0 8D 5D 55		        sta md_w
   120 47A3 A9 20		        lda #32
   121 47A5 8D 5E 55		        sta md_h
   122 47A8 20 90 54		        jsr mark_dirty_sprite
   123 47AB 68			        pla
   124 47AC 20 C8 23		        jsr blit_sprite
   125 47AF 4C 23 48		        jmp ?done
   126 47B2			?not_dead
   127 				        ; Check pain (hit reaction)
   128 47B2 AD 5E 2E		        lda pl_pain_timer
   129 47B5 F0 20		        beq ?no_pain
   130 				        ; Pain sprite from chunk 7 (VRAM $05xxxx)
   131 47B7 A5 95		        lda zpdir
   132 47B9 D0 05		        bne ?pain_l
   133 47BB A9 8D		        lda #SPR_PL_PAIN_L    ; dir=0 (right) -> _L variant
   134 47BD 4C C2 47		        jmp ?draw_pain
   135 47C0 A9 8C		?pain_l lda #SPR_PL_PAIN      ; dir=1 (left) -> base variant
   136 47C2			?draw_pain
   137 47C2 48			        pha
   138 47C3 A9 10		        lda #16
   139 47C5 8D 5D 55		        sta md_w
   140 47C8 A9 20		        lda #32
   141 47CA 8D 5E 55		        sta md_h
   142 47CD 20 90 54		        jsr mark_dirty_sprite
   143 47D0 68			        pla
   144 47D1 20 C8 23		        jsr blit_sprite
   145 47D4 4C 23 48		        jmp ?done
   146 47D7			?no_pain
   147 				        ; Check if shooting (show shoot sprite while cooldown active)
   148 				        ; Skip shoot sprite for melee weapons
   149 47D7 A5 C3		        lda zpwcool
   150 47D9 F0 0C		        beq ?no_shoot
   151 47DB A6 A6		        ldx zpwcur
   152 47DD BD 2D 26		        lda weap_range,x
   153 47E0 D0 05		        bne ?no_shoot           ; melee = no shoot sprite
   154 47E2 A9 04		        lda #SPR_PL_SHOOT
   155 47E4 4C 0A 48		        jmp ?draw
   156 47E7			?no_shoot
   157 47E7 A5 96		        lda zpst
   158 47E9 C9 01		        cmp #1
   159 47EB F0 09		        beq ?walk
   160 47ED C9 02		        cmp #2
   161 47EF F0 17		        beq ?jump
   162 47F1 A9 00		        lda #SPR_PL_IDLE
   163 47F3 4C 0A 48		        jmp ?draw
   164 47F6 A5 97		?walk   lda zpan
   165 47F8 4A			        lsr
   166 47F9 4A			        lsr
   167 47FA 29 03		        and #$03
   168 47FC C9 03		        cmp #3
   169 47FE D0 02		        bne ?wok
   170 4800 A9 01		        lda #1              ; cycle: 0,1,2,1 (walk1,walk2,walk3,walk2)
   171 4802 18			?wok    clc
   172 4803 69 01		        adc #SPR_PL_W1
   173 4805 4C 0A 48		        jmp ?draw
   174 4808 A9 02		?jump   lda #SPR_PL_W2
   175 480A			?draw   ; Mark dirty tiles before drawing
   176 480A 48			        pha                     ; save sprite index
   177 480B A9 10		        lda #16
   178 480D 8D 5D 55		        sta md_w
   179 4810 A9 20		        lda #32
   180 4812 8D 5E 55		        sta md_h
   181 4815 20 90 54		        jsr mark_dirty_sprite
   182 4818 68			        pla                     ; restore sprite index
   183 				        ; Base sprites face LEFT, _L sprites face RIGHT
   184 				        ; dir=0 (right) -> +MIRROR_OFFSET, dir=1 (left) -> base
   185 4819 A4 95		        ldy zpdir
   186 481B D0 03		        bne ?noflip
   187 481D 18			        clc
   188 481E 69 3C		        adc #MIRROR_OFFSET
   189 4820 20 C8 23		?noflip jsr blit_sprite
   190 4823 60			?done   rts
   191 				.endp
   192
   193 				; ============================================
   194 				; RENDER PROJECTILES
   195 				; ============================================
   196 4824			.proc render_projs
   197 4824 A2 00		        ldx #0
   198 4826 BD 1A 2D		?lp     lda proj_a,x
   199 4829 D0 03		        bne ?active
   200 482B 4C E0 48		        jmp ?nx
   201 482E			?active ; Only render rocket and plasma projectiles (hitscan = invisible)
   202 482E BD 36 2D		        lda proj_spr,x
   203 4831 C9 68		        cmp #SPR_ROCKET_PROJ
   204 4833 F0 6A		        beq ?draw
   205 4835 C9 81		        cmp #SPR_PLASMA_PROJ1
   206 4837 F0 07		        beq ?draw_plasma
   207 4839 C9 83		        cmp #SPR_BFG_PROJ1
   208 483B F0 13		        beq ?draw_bfg
   209 483D 4C E0 48		        jmp ?nx
   210 4840			?draw_plasma
   211 				        ; Animate plasma: alternate proj1/proj2 every 4 frames
   212 4840 A5 90		        lda zfr
   213 4842 29 04		        and #$04
   214 4844 F0 05		        beq ?pp1
   215 4846 A9 82		        lda #SPR_PLASMA_PROJ2
   216 4848 4C A1 48		        jmp ?draw_spr
   217 484B A9 81		?pp1    lda #SPR_PLASMA_PROJ1
   218 484D 4C A1 48		        jmp ?draw_spr
   219 4850			?draw_bfg
   220 4850 A5 90		        lda zfr
   221 4852 29 04		        and #$04
   222 4854 F0 05		        beq ?bp1
   223 4856 A9 84		        lda #SPR_BFG_PROJ2
   224 4858 4C 5D 48		        jmp ?draw_bfg2
   225 485B A9 83		?bp1    lda #SPR_BFG_PROJ1
   226 485D			?draw_bfg2
   227 485D 8E E9 48		        stx rj_idx
   228 4860 48			        pha
   229 4861 BD 1E 2D		        lda proj_x,x
   230 4864 38			        sec
   231 4865 E5 D7		        sbc cam_x_lo
   232 4867 85 D0		        sta zdx
   233 4869 BD 22 2D		        lda proj_xh,x
   234 486C E5 D8		        sbc cam_x_hi
   235 486E 85 D1		        sta zdxh
   236 4870 BD 26 2D		        lda proj_y,x
   237 4873 48			        pha
   238 4874 BD 2A 2D		        lda proj_yhi,x
   239 4877 AA			        tax                 ; X = hi byte
   240 4878 68			        pla                 ; A = lo byte
   241 4879 20 85 A5		        jsr calc_scr_y
   242 487C 90 1A		        bcc ?bfgskip
   243 487E 85 D2		        sta zdy
   244 4880 AE E9 48		        ldx rj_idx
   245 4883 A9 10		        lda #16
   246 4885 8D 5D 55		        sta md_w
   247 4888 8D 5E 55		        sta md_h
   248 488B 20 90 54		        jsr mark_dirty_sprite
   249 488E 68			        pla
   250 488F 20 C8 23		        jsr blit_sprite
   251 4892 AE E9 48		        ldx rj_idx
   252 4895 4C E0 48		        jmp ?nx
   253 4898 68			?bfgskip pla
   254 4899 AE E9 48		        ldx rj_idx
   255 489C 4C E0 48		        jmp ?nx
   256 489F A9 68		?draw   lda #SPR_ROCKET_PROJ
   257 48A1			?draw_spr
   258 48A1 8E E9 48		        stx rj_idx
   259 48A4 48			        pha
   260 48A5 BD 1E 2D		        lda proj_x,x
   261 48A8 38			        sec
   262 48A9 E5 D7		        sbc cam_x_lo
   263 48AB 85 D0		        sta zdx
   264 48AD BD 22 2D		        lda proj_xh,x
   265 48B0 E5 D8		        sbc cam_x_hi
   266 48B2 85 D1		        sta zdxh
   267 48B4 BD 26 2D		        lda proj_y,x
   268 48B7 48			        pha
   269 48B8 BD 2A 2D		        lda proj_yhi,x
   270 48BB AA			        tax                 ; X = hi byte
   271 48BC 68			        pla                 ; A = lo byte
   272 48BD 20 85 A5		        jsr calc_scr_y
   273 48C0 90 1A		        bcc ?sprskip
   274 48C2 85 D2		        sta zdy
   275 48C4 AE E9 48		        ldx rj_idx
   276 48C7 A9 08		        lda #8
   277 48C9 8D 5D 55		        sta md_w
   278 48CC 8D 5E 55		        sta md_h
   279 48CF 20 90 54		        jsr mark_dirty_sprite
   280 48D2 68			        pla
   281 48D3 20 C8 23		        jsr blit_sprite
   282 48D6 AE E9 48		        ldx rj_idx
   283 48D9 4C E0 48		        jmp ?nx
   284 48DC 68			?sprskip pla
   285 48DD AE E9 48		        ldx rj_idx
   286 48E0 E8			?nx     inx
   287 48E1 E0 04		        cpx #MAX_PROJ
   288 48E3 F0 03		        beq ?done
   289 48E5 4C 26 48		        jmp ?lp
   290 48E8 60			?done   rts
   291 48E9 00			rj_idx  dta 0
   292 				.endp
   321 48EA			        icl 'hud.asm'
Source: hud.asm
     1 				;==============================================
     2 				; DOOM2D - HUD rendering (HP, Ammo)
     3 				; hud.asm
     4 				;==============================================
     5
     6 				; HUD digit storage (NOT in zero page - zt/zt2 get clobbered by calc_dst)
     7 48EA 00			hd_d0   dta 0                   ; ones digit
     8 48EB 00			hd_d1   dta 0                   ; tens digit
     9 48EC 00			hd_d2   dta 0                   ; hundreds digit
    10
    11 				; ============================================
    12 				; NUM TO DIGITS: A (0-255) -> hd_d2/hd_d1/hd_d0
    13 				; ============================================
    14 48ED			.proc num_to_digits
    15 48ED A2 00		        ldx #0
    16 48EF C9 64		?s      cmp #100
    17 48F1 90 05		        bcc ?t
    18 48F3 E9 64		        sbc #100                ; carry already set from bcc
    19 48F5 E8			        inx
    20 48F6 D0 F7		        bne ?s
    21 48F8 8E EC 48		?t      stx hd_d2
    22 48FB A2 00		        ldx #0
    23 48FD C9 0A		?d      cmp #10
    24 48FF 90 05		        bcc ?j
    25 4901 E9 0A		        sbc #10
    26 4903 E8			        inx
    27 4904 D0 F7		        bne ?d
    28 4906 8E EB 48		?j      stx hd_d1
    29 4909 8D EA 48		        sta hd_d0
    30 490C 60			        rts
    31 				.endp
    32
    33 				; ============================================
    34 				; BLIT HUD CHAR 8x8 (transparent)
    35 				; Input: A = char index (0-11), zdx/zdxh = X, zdy = Y
    36 				; Source: VRAM $019000 + char*64
    37 				; ============================================
    38 490D			.proc blit_hud_char
    39 				        ; Calculate source: $019000 + A*64
    40 				        ; A*64: shift left 6 = high nybble is A>>2, low byte is (A&3)<<6
    41 490D 8D 6B 49		        sta hc_idx
    42 4910 A9 FF		        lda #BANK_EN+BANK_BCB
    43 4912 8D 5F D6		        sta VBXE_BANK_SEL
    44
    45 				        ; src_lo = (idx & 3) << 6
    46 4915 AD 6B 49		        lda hc_idx
    47 4918 29 03		        and #$03
    48 491A 0A			        asl
    49 491B 0A			        asl
    50 491C 0A			        asl
    51 491D 0A			        asl
    52 491E 0A			        asl
    53 491F 0A			        asl
    54 4920 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0     ; src addr low
    55
    56 				        ; src_mid = (HUD_FONT_ADDR>>8) + (idx >> 2)
    57 4923 AD 6B 49		        lda hc_idx
    58 4926 4A			        lsr
    59 4927 4A			        lsr
    60 4928 18			        clc
    61 4929 69 D0		        adc #>HUD_FONT_ADDR             ; base within bank
    62 492B 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1      ; src addr mid
    63
    64 492E A9 01		        lda #$01                        ; bank $01 ($01xxxx)
    65 4930 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2      ; src addr high
    66
    67 				        ; src step = 8 (width)
    68 4933 A9 08		        lda #HUD_CHAR_W
    69 4935 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
    70 4938 A9 00		        lda #0
    71 493A 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
    72 493D A9 01		        lda #1
    73 493F 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
    74
    75 				        ; dst = calc_dst (uses zdx, zdxh, zdy, zbuf_hi)
    76 4942 20 E5 22		        jsr calc_dst
    77 4945 A5 86		        lda zva
    78 4947 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
    79 494A A5 87		        lda zva+1
    80 494C 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
    81 494F A5 88		        lda zva+2
    82 4951 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
    83 				        ; dst step [9-11], AND [15], XOR [16-19] pre-filled
    84 				        ; size 8x8
    85 4954 A9 07		        lda #HUD_CHAR_W-1
    86 4956 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
    87 4959 A9 00		        lda #0
    88 495B 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
    89 495E A9 07		        lda #HUD_CHAR_H-1
    90 4960 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
    91 4963 A9 00		        lda #0
    92 4965 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20     ; mode=0 (opaque)
    93 4968 4C C7 22		        jmp run_blit
    94 496B 00			hc_idx  dta 0
    95 				.endp
    96
    97 				; HUD cache (previous frame values)
    98 496C FF			hud_prev_hp   dta $FF         ; force redraw on first frame
    99 496D FF			hud_prev_ammo dta $FF
   100 496E FF			hud_prev_weap dta $FF
   101 496F FF			hud_prev_keys dta $FF
   102 4970 FF			hud_prev_armor dta $FF
   103 4971 02			hud_frames    dta 2           ; frames left to draw (2 = both buffers)
   104 4972 02			hud_full_clear dta 2          ; >0 = clear armor/keys areas (2 = both buffers) [RESTART-TEMP]
   105
   106 				; ============================================
   107 				; RENDER HUD (only redraws changed elements)
   108 				; Layout: [heart]HP  [bullet]AMMO
   109 				; ============================================
   110 4973			.proc render_hud
   111 				        ; Check if anything changed
   112 4973 A5 98		        lda zphp
   113 4975 CD 6C 49		        cmp hud_prev_hp
   114 4978 D0 29		        bne ?changed
   115 497A 20 2E 27		        jsr get_cur_ammo
   116 497D CD 6D 49		        cmp hud_prev_ammo
   117 4980 D0 21		        bne ?changed
   118 4982 A5 A6		        lda zpwcur
   119 4984 CD 6E 49		        cmp hud_prev_weap
   120 4987 D0 1A		        bne ?changed
   121 4989 A5 A1		        lda zpkeys
   122 498B CD 6F 49		        cmp hud_prev_keys
   123 498E D0 13		        bne ?changed
   124 4990 A5 A0		        lda zparmor
   125 4992 CD 70 49		        cmp hud_prev_armor
   126 4995 D0 0C		        bne ?changed
   127 				        ; Nothing changed - still need to draw for both buffers?
   128 4997 AD 71 49		        lda hud_frames
   129 499A F0 06		        beq ?skip               ; already drawn to both buffers
   130 499C CE 71 49		        dec hud_frames
   131 499F 4C C2 49		        jmp ?draw
   132 49A2 60			?skip   rts
   133 49A3			?changed
   134 				        ; Update cache + reset counter for both buffers
   135 49A3 A5 98		        lda zphp
   136 49A5 8D 6C 49		        sta hud_prev_hp
   137 49A8 20 2E 27		        jsr get_cur_ammo
   138 49AB 8D 6D 49		        sta hud_prev_ammo
   139 49AE A5 A6		        lda zpwcur
   140 49B0 8D 6E 49		        sta hud_prev_weap
   141 49B3 A5 A1		        lda zpkeys
   142 49B5 8D 6F 49		        sta hud_prev_keys
   143 49B8 A5 A0		        lda zparmor
   144 49BA 8D 70 49		        sta hud_prev_armor
   145 49BD A9 01		        lda #1                  ; draw this frame + 1 more (both buffers)
   146 49BF 8D 71 49		        sta hud_frames
   147
   148 49C2 A9 C0		?draw   lda #HUD_Y
   149 49C4 85 D2		        sta zdy
   150
   151 				        ; --- Heart icon ---
   152 49C6 A9 08		        lda #HUD_HP_IX
   153 49C8 85 D0		        sta zdx
   154 49CA A9 00		        lda #0
   155 49CC 85 D1		        sta zdxh
   156 49CE A9 0A		        lda #HUD_CHR_HEART
   157 49D0 20 0D 49		        jsr blit_hud_char
   158
   159 				        ; --- HP digits ---
   160 49D3 A5 98		        lda zphp
   161 49D5 20 ED 48		        jsr num_to_digits
   162
   163 				        ; Hundreds
   164 49D8 A9 18		        lda #HUD_HP_DX
   165 49DA 85 D0		        sta zdx
   166 49DC A9 00		        lda #0
   167 49DE 85 D1		        sta zdxh
   168 49E0 AD EC 48		        lda hd_d2
   169 49E3 20 0D 49		        jsr blit_hud_char
   170
   171 				        ; Tens
   172 49E6 A9 20		        lda #[HUD_HP_DX+8]
   173 49E8 85 D0		        sta zdx
   174 49EA A9 00		        lda #0
   175 49EC 85 D1		        sta zdxh
   176 49EE AD EB 48		        lda hd_d1
   177 49F1 20 0D 49		        jsr blit_hud_char
   178
   179 				        ; Ones
   180 49F4 A9 28		        lda #[HUD_HP_DX+16]
   181 49F6 85 D0		        sta zdx
   182 49F8 A9 00		        lda #0
   183 49FA 85 D1		        sta zdxh
   184 49FC AD EA 48		        lda hd_d0
   185 49FF 20 0D 49		        jsr blit_hud_char
   186
   187 				        ; --- Armor digits ---
   188 				        ; Always clear armor area first (prevents stale digits when armor=0)
   189 4A02 A9 38		        lda #56
   190 4A04 85 D0		        sta zdx
   191 4A06 A9 00		        lda #0
   192 4A08 85 D1		        sta zdxh
   193 4A0A A9 C0		        lda #HUD_Y
   194 4A0C 85 D2		        sta zdy
   195 4A0E 20 70 4B		        jsr hud_clear_icon
   196 4A11 A9 40		        lda #64
   197 4A13 85 D0		        sta zdx
   198 4A15 A9 00		        lda #0
   199 4A17 85 D1		        sta zdxh
   200 4A19 A9 C0		        lda #HUD_Y
   201 4A1B 85 D2		        sta zdy
   202 4A1D 20 70 4B		        jsr hud_clear_icon
   203 4A20 A9 48		        lda #72
   204 4A22 85 D0		        sta zdx
   205 4A24 A9 00		        lda #0
   206 4A26 85 D1		        sta zdxh
   207 4A28 A9 C0		        lda #HUD_Y
   208 4A2A 85 D2		        sta zdy
   209 4A2C 20 70 4B		        jsr hud_clear_icon
   210 				        ; Draw armor digits only if > 0
   211 4A2F A5 A0		        lda zparmor
   212 4A31 F0 33		        beq ?no_armor
   213 4A33 A9 C0		        lda #HUD_Y
   214 4A35 85 D2		        sta zdy
   215 4A37 A5 A0		        lda zparmor
   216 4A39 20 ED 48		        jsr num_to_digits
   217 4A3C A9 38		        lda #56
   218 4A3E 85 D0		        sta zdx
   219 4A40 A9 00		        lda #0
   220 4A42 85 D1		        sta zdxh
   221 4A44 AD EC 48		        lda hd_d2
   222 4A47 20 0D 49		        jsr blit_hud_char
   223 4A4A A9 40		        lda #64
   224 4A4C 85 D0		        sta zdx
   225 4A4E A9 00		        lda #0
   226 4A50 85 D1		        sta zdxh
   227 4A52 AD EB 48		        lda hd_d1
   228 4A55 20 0D 49		        jsr blit_hud_char
   229 4A58 A9 48		        lda #72
   230 4A5A 85 D0		        sta zdx
   231 4A5C A9 00		        lda #0
   232 4A5E 85 D1		        sta zdxh
   233 4A60 AD EA 48		        lda hd_d0
   234 4A63 20 0D 49		        jsr blit_hud_char
   235 4A66			?no_armor
   236 4A66 A9 C0		        lda #HUD_Y
   237 4A68 85 D2		        sta zdy
   238
   239 				        ; --- Bullet icon ---
   240 4A6A A9 50		        lda #HUD_AM_IX
   241 4A6C 85 D0		        sta zdx
   242 4A6E A9 00		        lda #0
   243 4A70 85 D1		        sta zdxh
   244 4A72 A9 0B		        lda #HUD_CHR_BULLET
   245 4A74 20 0D 49		        jsr blit_hud_char
   246
   247 				        ; --- Weapon number ---
   248 4A77 A9 A0		        lda #HUD_WP_DX
   249 4A79 85 D0		        sta zdx
   250 4A7B A9 00		        lda #0
   251 4A7D 85 D1		        sta zdxh
   252 4A7F A5 A6		        lda zpwcur
   253 4A81 18			        clc
   254 4A82 69 01		        adc #1                  ; display 1-7 (not 0-6)
   255 4A84 20 0D 49		        jsr blit_hud_char
   256
   257 				        ; --- Weapon pickup sprite (16x16, above HUD) ---
   258 4A87 A9 AA		        lda #[HUD_WP_DX+10]
   259 4A89 85 D0		        sta zdx
   260 4A8B A9 00		        lda #0
   261 4A8D 85 D1		        sta zdxh
   262 4A8F A9 B8		        lda #[HUD_Y-8]
   263 4A91 85 D2		        sta zdy
   264 				        ; Mark dirty for cleanup
   265 4A93 A9 10		        lda #16
   266 4A95 8D 5D 55		        sta md_w
   267 4A98 8D 5E 55		        sta md_h
   268 4A9B 20 90 54		        jsr mark_dirty_sprite
   269 				        ; Clear HUD icon area (only HUD row, 16x8)
   270 4A9E A9 AA		        lda #[HUD_WP_DX+10]
   271 4AA0 85 D0		        sta zdx
   272 4AA2 A9 00		        lda #0
   273 4AA4 85 D1		        sta zdxh
   274 4AA6 A9 C0		        lda #HUD_Y
   275 4AA8 85 D2		        sta zdy
   276 4AAA 20 70 4B		        jsr hud_clear_icon
   277 				        ; Draw weapon sprite
   278 4AAD A9 AA		        lda #[HUD_WP_DX+10]
   279 4AAF 85 D0		        sta zdx
   280 4AB1 A9 00		        lda #0
   281 4AB3 85 D1		        sta zdxh
   282 4AB5 A9 B8		        lda #[HUD_Y-8]
   283 4AB7 85 D2		        sta zdy
   284 4AB9 A6 A6		        ldx zpwcur
   285 4ABB BD 3F 26		        lda weap_hud_spr,x
   286 4ABE 20 C8 23		        jsr blit_sprite
   287
   288 				        ; --- Ammo digits (weapon-dependent) ---
   289 4AC1 A9 C0		        lda #HUD_Y
   290 4AC3 85 D2		        sta zdy
   291 4AC5 20 2E 27		        jsr get_cur_ammo
   292 4AC8 20 ED 48		        jsr num_to_digits
   293
   294 				        ; Hundreds
   295 4ACB A9 60		        lda #HUD_AM_DX
   296 4ACD 85 D0		        sta zdx
   297 4ACF A9 00		        lda #0
   298 4AD1 85 D1		        sta zdxh
   299 4AD3 AD EC 48		        lda hd_d2
   300 4AD6 20 0D 49		        jsr blit_hud_char
   301
   302 				        ; Tens
   303 4AD9 A9 68		        lda #[HUD_AM_DX+8]
   304 4ADB 85 D0		        sta zdx
   305 4ADD A9 00		        lda #0
   306 4ADF 85 D1		        sta zdxh
   307 4AE1 AD EB 48		        lda hd_d1
   308 4AE4 20 0D 49		        jsr blit_hud_char
   309
   310 				        ; Ones
   311 4AE7 A9 70		        lda #[HUD_AM_DX+16]
   312 4AE9 85 D0		        sta zdx
   313 4AEB A9 00		        lda #0
   314 4AED 85 D1		        sta zdxh
   315 4AEF AD EA 48		        lda hd_d0
   316 4AF2 20 0D 49		        jsr blit_hud_char
   317
   318 				        ; --- Key icons (clear on restart, then draw collected) ---
   319 4AF5 AD 72 49		        lda hud_full_clear      ; [RESTART-TEMP] clear key slots on restart
   320 4AF8 F0 30		        beq ?no_kclear
   321 4AFA A9 C0		        lda #HUD_Y
   322 4AFC 85 D2		        sta zdy
   323 4AFE A9 DC		        lda #HUD_KEY_DX
   324 4B00 85 D0		        sta zdx
   325 4B02 A9 00		        lda #0
   326 4B04 85 D1		        sta zdxh
   327 4B06 20 70 4B		        jsr hud_clear_icon
   328 4B09 A9 C0		        lda #HUD_Y
   329 4B0B 85 D2		        sta zdy
   330 4B0D A9 EC		        lda #[HUD_KEY_DX+16]
   331 4B0F 85 D0		        sta zdx
   332 4B11 A9 00		        lda #0
   333 4B13 85 D1		        sta zdxh
   334 4B15 20 70 4B		        jsr hud_clear_icon
   335 4B18 A9 C0		        lda #HUD_Y
   336 4B1A 85 D2		        sta zdy
   337 4B1C A9 FC		        lda #[HUD_KEY_DX+32]
   338 4B1E 85 D0		        sta zdx
   339 4B20 A9 00		        lda #0
   340 4B22 85 D1		        sta zdxh
   341 4B24 20 70 4B		        jsr hud_clear_icon
   342 4B27 CE 72 49		        dec hud_full_clear      ; 2→1→0 (both buffers)
   343 4B2A			?no_kclear
   344
   345 				        ; Red key
   346 4B2A A5 A1		        lda zpkeys
   347 4B2C 29 01		        and #$01                ; bit 0 = red
   348 4B2E F0 11		        beq ?no_red
   349 4B30 A9 DC		        lda #HUD_KEY_DX
   350 4B32 85 D0		        sta zdx
   351 4B34 A9 00		        lda #0
   352 4B36 85 D1		        sta zdxh
   353 4B38 A9 B8		        lda #[HUD_Y-8]
   354 4B3A 85 D2		        sta zdy
   355 4B3C A9 33		        lda #SPR_KEYRED
   356 4B3E 20 C8 23		        jsr blit_sprite
   357 4B41			?no_red
   358 				        ; Blue key
   359 4B41 A5 A1		        lda zpkeys
   360 4B43 29 02		        and #$02                ; bit 1 = blue
   361 4B45 F0 11		        beq ?no_blue
   362 4B47 A9 EC		        lda #[HUD_KEY_DX+16]
   363 4B49 85 D0		        sta zdx
   364 4B4B A9 00		        lda #0
   365 4B4D 85 D1		        sta zdxh
   366 4B4F A9 B8		        lda #[HUD_Y-8]
   367 4B51 85 D2		        sta zdy
   368 4B53 A9 34		        lda #SPR_KEYBLUE
   369 4B55 20 C8 23		        jsr blit_sprite
   370 4B58			?no_blue
   371 				        ; Yellow key
   372 4B58 A5 A1		        lda zpkeys
   373 4B5A 29 04		        and #$04                ; bit 2 = yellow
   374 4B5C F0 11		        beq ?no_yellow
   375 4B5E A9 FC		        lda #[HUD_KEY_DX+32]
   376 4B60 85 D0		        sta zdx
   377 4B62 A9 00		        lda #0
   378 4B64 85 D1		        sta zdxh
   379 4B66 A9 B8		        lda #[HUD_Y-8]
   380 4B68 85 D2		        sta zdy
   381 4B6A A9 35		        lda #SPR_KEYYELLOW
   382 4B6C 20 C8 23		        jsr blit_sprite
   383 4B6F			?no_yellow
   384 4B6F 60			        rts
   385 				.endp
   386
   387 				; ============================================
   388 				; CLEAR 16x16 AREA (black fill at zdx/zdy)
   389 				; Uses blitter AND=0 to force all pixels black
   390 				; ============================================
   391 4B70			.proc hud_clear_icon
   392 4B70 A9 FF		        lda #BANK_EN+BANK_BCB
   393 4B72 8D 5F D6		        sta VBXE_BANK_SEL
   394 				        ; src = $00FA00 (known zeros), src step = 0
   395 4B75 A9 00		        lda #0
   396 4B77 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   397 4B7A 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   398 4B7D 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   399 4B80 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   400 4B83 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   401 4B86 A9 FA		        lda #$FA
   402 4B88 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   403 				        ; dst
   404 4B8B 20 E5 22		        jsr calc_dst
   405 4B8E A5 86		        lda zva
   406 4B90 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   407 4B93 A5 87		        lda zva+1
   408 4B95 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   409 4B98 A5 88		        lda zva+2
   410 4B9A 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   411 				        ; dst step [9-11] pre-filled
   412 				        ; size 16x8
   413 4B9D A9 0F		        lda #15
   414 4B9F 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   415 4BA2 A9 00		        lda #0
   416 4BA4 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   417 4BA7 A9 07		        lda #7
   418 4BA9 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   419 				        ; AND=0 forces black (override pre-filled $FF)
   420 4BAC 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15
   421 4BAF 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   422 4BB2 20 C7 22		        jsr run_blit
   423 				        ; Restore AND=$FF for next blit
   424 4BB5 A9 FF		        lda #$FF
   425 4BB7 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15
   426 4BBA 60			        rts
   427 				.endp
   428
   429 				; ============================================
   430 				; FPS COUNTER
   431 				; ============================================
   432 4BBB 00			fps_show    dta 0           ; 0=hidden, 1=shown
   433 4BBC 00			fps_count   dta 0           ; frames this period
   434 4BBD 00			fps_display dta 0           ; last calculated FPS
   435 4BBE 00			fps_rtclk   dta 0           ; RTCLOK3 at period start
   436 4BBF 00			fps_clear   dta 0           ; >0 = clear both buffers on toggle off
   437
   438 = 0128			FPS_X       = 296           ; X position (right edge of HUD)
   439
   440 				; ============================================
   441 				; FPS_UPDATE - count frames, calc every second
   442 				; ============================================
   443 4BC0			.proc fps_update
   444 4BC0 EE BC 4B		        inc fps_count
   445 4BC3 A5 14		        lda RTCLOK3
   446 4BC5 38			        sec
   447 4BC6 ED BE 4B		        sbc fps_rtclk
   448 4BC9 AE 00 23		        ldx is_pal
   449 4BCC D0 06		        bne ?pal
   450 4BCE C9 3C		        cmp #60
   451 4BD0 90 16		        bcc ?done
   452 4BD2 B0 04		        bcs ?calc
   453 4BD4 C9 32		?pal    cmp #50
   454 4BD6 90 10		        bcc ?done
   455 4BD8 AD BC 4B		?calc   lda fps_count
   456 4BDB 8D BD 4B		        sta fps_display
   457 4BDE A9 00		        lda #0
   458 4BE0 8D BC 4B		        sta fps_count
   459 4BE3 A5 14		        lda RTCLOK3
   460 4BE5 8D BE 4B		        sta fps_rtclk
   461 4BE8 60			?done   rts
   462 				.endp
   463
   464 				; ============================================
   465 				; FPS_RENDER - draw FPS on HUD (2 digits)
   466 				; ============================================
   467 4BE9			.proc fps_render
   468 				        ; Clear area if toggled off
   469 4BE9 AD BF 4B		        lda fps_clear
   470 4BEC F0 12		        beq ?chk_show
   471 4BEE CE BF 4B		        dec fps_clear
   472 4BF1 A9 28		        lda #<FPS_X
   473 4BF3 85 D0		        sta zdx
   474 4BF5 A9 01		        lda #>FPS_X
   475 4BF7 85 D1		        sta zdxh
   476 4BF9 A9 C0		        lda #HUD_Y
   477 4BFB 85 D2		        sta zdy
   478 4BFD 4C 70 4B		        jmp hud_clear_icon
   479 4C00			?chk_show
   480 4C00 AD BB 4B		        lda fps_show
   481 4C03 F0 26		        beq ?skip
   482 4C05 AD BD 4B		        lda fps_display
   483 4C08 20 ED 48		        jsr num_to_digits
   484 4C0B A9 C0		        lda #HUD_Y
   485 4C0D 85 D2		        sta zdy
   486 				        ; Tens digit
   487 4C0F A9 28		        lda #<FPS_X
   488 4C11 85 D0		        sta zdx
   489 4C13 A9 01		        lda #>FPS_X
   490 4C15 85 D1		        sta zdxh
   491 4C17 AD EB 48		        lda hd_d1
   492 4C1A 20 0D 49		        jsr blit_hud_char
   493 				        ; Ones digit
   494 4C1D A9 30		        lda #<[FPS_X+8]
   495 4C1F 85 D0		        sta zdx
   496 4C21 A9 01		        lda #>[FPS_X+8]
   497 4C23 85 D1		        sta zdxh
   498 4C25 AD EA 48		        lda hd_d0
   499 4C28 20 0D 49		        jsr blit_hud_char
   500 4C2B 60			?skip   rts
   501 				.endp
   502
   503 				; ============================================
   504 				; CLEAR HUD AREA (320x8 at Y=192, fill black)
   505 				; Uses current zbuf_hi for buffer selection
   506 				; ============================================
   507 4C2C			.proc clear_hud_area
   508 4C2C A9 FF		        lda #BANK_EN+BANK_BCB
   509 4C2E 8D 5F D6		        sta VBXE_BANK_SEL
   510 4C31 A9 00		        lda #0
   511 4C33 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   512 4C36 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   513 4C39 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   514 4C3C 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3   ; src step = 0
   515 4C3F 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   516 4C42 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   517 4C45 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15  ; AND = 0
   518 4C48 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20  ; mode 0 (fill)
   519 4C4B 85 D0		        sta zdx
   520 4C4D 85 D1		        sta zdxh
   521 				        ; XOR [16-19] already 0 from setup_xdl
   522 				        ; dst step = 336 (must set BEFORE blit!)
   523 4C4F A9 50		        lda #<SCR_PITCH
   524 4C51 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9
   525 4C54 A9 01		        lda #>SCR_PITCH
   526 4C56 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10
   527 4C59 A9 01		        lda #1
   528 4C5B 8D 0B 91		        sta MEMW+[VRAM_BCB&$FFF]+11
   529 4C5E A9 C0		        lda #HUD_Y
   530 4C60 85 D2		        sta zdy
   531 4C62 20 E5 22		        jsr calc_dst
   532 4C65 A5 86		        lda zva
   533 4C67 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   534 4C6A A5 87		        lda zva+1
   535 4C6C 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   536 4C6F A5 88		        lda zva+2
   537 4C71 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   538 4C74 A9 3F		        lda #<[SCR_W-1]
   539 4C76 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   540 4C79 A9 01		        lda #>[SCR_W-1]
   541 4C7B 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   542 4C7E A9 07		        lda #7                  ; 8 rows
   543 4C80 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   544 4C83 20 C7 22		        jsr run_blit
   545 4C86 20 DF 22		        jsr wait_blit
   546 				        ; Restore AND and mode (dst step + XOR unchanged)
   547 4C89 A9 FF		        lda #BANK_EN+BANK_BCB
   548 4C8B 8D 5F D6		        sta VBXE_BANK_SEL
   549 4C8E A9 FF		        lda #$FF
   550 4C90 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15
   551 4C93 A9 01		        lda #BLT_TRANS
   552 4C95 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   553 4C98 60			        rts
   554 				.endp
   322 4C99			        icl 'menu.asm'
Source: menu.asm
     1 				;==============================================
     2 				; DOOM2D - Title screen & menu system
     3 				; menu.asm
     4 				;==============================================
     5
     6 = 0084			txt_ptr = $84               ; 2b pointer for text rendering
     7
     8 				; ============================================
     9 				; CONSTANTS & STATE
    10 				; ============================================
    11 = 070000			MENU_BKP_VRAM = $070000        ; backup area for title menu background
    12 = 0070			MENU_X      = 112
    13 = 0050			MENU_Y      = 80
    14 = 0010			MENU_SPACE  = 16
    15 = 0003			MENU_ITEMS  = 3
    16
    17 4C99 00			menu_sel    dta 0
    18 4C9A 00			menu_prev   dta 0
    19 4C9B 00			draw_mode   dta 0           ; 0=draw_menu, 1=draw_settings
    20
    21 				; ============================================
    22 				; BACKUP MENU AREA (184x64 from screen to VRAM $070000)
    23 				; ============================================
    24 4C9C			.proc backup_menu_area
    25 4C9C A9 FF		        lda #BANK_EN+BANK_BCB
    26 4C9E 8D 5F D6		        sta VBXE_BANK_SEL
    27 4CA1 A9 50		        lda #80
    28 4CA3 85 D0		        sta zdx
    29 4CA5 A9 00		        lda #0
    30 4CA7 85 D1		        sta zdxh
    31 4CA9 A9 48		        lda #72
    32 4CAB 85 D2		        sta zdy
    33 4CAD 20 E5 22		        jsr calc_dst
    34 4CB0 A5 86		        lda zva
    35 4CB2 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
    36 4CB5 A5 87		        lda zva+1
    37 4CB7 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
    38 4CBA A5 88		        lda zva+2
    39 4CBC 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
    40 4CBF A9 50		        lda #<SCR_PITCH
    41 4CC1 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
    42 4CC4 A9 01		        lda #>SCR_PITCH
    43 4CC6 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
    44 4CC9 A9 01		        lda #1
    45 4CCB 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5   ; step mode 1 = use explicit step
    46 				        ; Dest = VRAM $070000
    47 4CCE A9 00		        lda #0
    48 4CD0 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
    49 4CD3 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
    50 4CD6 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10
    51 4CD9 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
    52 4CDC 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
    53 4CDF A9 07		        lda #$07
    54 4CE1 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
    55 4CE4 A9 B8		        lda #184
    56 4CE6 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9
    57 4CE9 A9 01		        lda #1
    58 4CEB 8D 0B 91		        sta MEMW+[VRAM_BCB&$FFF]+11
    59 4CEE A9 B7		        lda #183
    60 4CF0 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
    61 4CF3 A9 3F		        lda #63
    62 4CF5 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
    63 4CF8 20 C7 22		        jsr run_blit
    64 4CFB 20 DF 22		        jsr wait_blit
    65 4CFE A9 FF		        lda #BANK_EN+BANK_BCB
    66 4D00 8D 5F D6		        sta VBXE_BANK_SEL
    67 4D03 A9 50		        lda #<SCR_PITCH
    68 4D05 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9
    69 4D08 A9 01		        lda #>SCR_PITCH
    70 4D0A 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10
    71 4D0D A9 01		        lda #BLT_TRANS
    72 4D0F 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
    73 4D12 60			        rts
    74 				.endp
    75
    76 				; ============================================
    77 				; RESTORE MENU AREA (VRAM $070000 back to screen)
    78 				; ============================================
    79 4D13			.proc restore_menu_area
    80 4D13 A9 FF		        lda #BANK_EN+BANK_BCB
    81 4D15 8D 5F D6		        sta VBXE_BANK_SEL
    82 				        ; Source = VRAM $070000, step=184
    83 4D18 A9 00		        lda #0
    84 4D1A 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
    85 4D1D 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
    86 4D20 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
    87 4D23 85 D1		        sta zdxh
    88 4D25 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
    89 4D28 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20  ; mode 0 (copy)
    90 4D2B A9 07		        lda #$07
    91 4D2D 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
    92 4D30 A9 B8		        lda #184
    93 4D32 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
    94 4D35 A9 01		        lda #1
    95 4D37 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5   ; src step mode
    96 				        ; Dest = screen (80,72), step=336
    97 4D3A A9 50		        lda #80
    98 4D3C 85 D0		        sta zdx
    99 4D3E A9 48		        lda #72
   100 4D40 85 D2		        sta zdy
   101 4D42 20 E5 22		        jsr calc_dst
   102 4D45 A5 86		        lda zva
   103 4D47 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   104 4D4A A5 87		        lda zva+1
   105 4D4C 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   106 4D4F A5 88		        lda zva+2
   107 4D51 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   108 4D54 A9 50		        lda #<SCR_PITCH
   109 4D56 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9   ; dst step = 336
   110 4D59 A9 01		        lda #>SCR_PITCH
   111 4D5B 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10
   112 4D5E A9 01		        lda #1
   113 4D60 8D 0B 91		        sta MEMW+[VRAM_BCB&$FFF]+11  ; dst step mode
   114 				        ; Size 184x64
   115 4D63 A9 B7		        lda #183
   116 4D65 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   117 4D68 A9 3F		        lda #63
   118 4D6A 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   119 4D6D 20 C7 22		        jsr run_blit
   120 4D70 20 DF 22		        jsr wait_blit
   121 				        ; Restore normal BCB
   122 4D73 A9 FF		        lda #BANK_EN+BANK_BCB
   123 4D75 8D 5F D6		        sta VBXE_BANK_SEL
   124 4D78 A9 01		        lda #BLT_TRANS
   125 4D7A 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   126 4D7D 60			        rts
   127 				.endp
   128
   129 				; ============================================
   130 				; REDRAW BOTH BUFFERS (clear + draw on both screens)
   131 				; Set draw_mode before calling: 0=menu, 1=settings
   132 				; ============================================
   133 4D7E			.proc redraw_both
   134 4D7E 20 F4 50		        jsr clear_menu_area
   135 4D81 AD 9B 4C		        lda draw_mode
   136 4D84 D0 06		        bne ?sett
   137 4D86 20 BE 4F		        jsr draw_menu
   138 4D89 4C 8F 4D		        jmp ?buf2
   139 4D8C 20 1C 50		?sett   jsr draw_settings
   140 4D8F 20 DF 22		?buf2   jsr wait_blit
   141 4D92 AD FF 22		        lda zbuf_hi
   142 4D95 49 02		        eor #SCR1_HI
   143 4D97 8D FF 22		        sta zbuf_hi
   144 4D9A 20 F4 50		        jsr clear_menu_area
   145 4D9D AD 9B 4C		        lda draw_mode
   146 4DA0 D0 06		        bne ?sett2
   147 4DA2 20 BE 4F		        jsr draw_menu
   148 4DA5 4C AB 4D		        jmp ?done
   149 4DA8 20 1C 50		?sett2  jsr draw_settings
   150 4DAB 20 DF 22		?done   jsr wait_blit
   151 4DAE AD FF 22		        lda zbuf_hi
   152 4DB1 49 02		        eor #SCR1_HI
   153 4DB3 8D FF 22		        sta zbuf_hi
   154 4DB6 60			        rts
   155 				.endp
   156
   157 				; ============================================
   158 				; TITLE SCREEN (called from main, returns when NEW GAME selected)
   159 				; ============================================
   160 4DB7			.proc menu_title_screen
   161 4DB7			?wait_release
   162 				        ; Wait until joystick and fire are released
   163 4DB7 AD 00 D3		        lda PORTA
   164 4DBA 49 FF		        eor #$FF
   165 4DBC 29 0F		        and #$0F
   166 4DBE D0 F7		        bne ?wait_release
   167 4DC0 AD 10 D0		        lda TRIG0
   168 4DC3 F0 F2		        beq ?wait_release
   169 4DC5 A9 FF		        lda #$FF
   170 4DC7 8D FC 02		        sta $02FC               ; clear keyboard
   171 				        ; Small delay to debounce
   172 4DCA A2 0A		        ldx #10
   173 4DCC A5 14		?dly    lda RTCLOK3
   174 4DCE C5 14		?dly2   cmp RTCLOK3
   175 4DD0 F0 FC		        beq ?dly2
   176 4DD2 CA			        dex
   177 4DD3 D0 F7		        bne ?dly
   178 4DD5 A9 FF		        lda #$FF
   179 4DD7 8D FC 02		        sta $02FC               ; clear again after delay
   180 				        ; Wait for any input
   181 4DDA			?wait_key
   182 4DDA A5 14		        lda RTCLOK3
   183 4DDC C5 14		?wk_vs  cmp RTCLOK3
   184 4DDE F0 FC		        beq ?wk_vs
   185 				        ; Check keyboard
   186 4DE0 AD FC 02		        lda $02FC
   187 4DE3 C9 FF		        cmp #$FF
   188 4DE5 F0 03		        beq ?chk_joy
   189 4DE7 4C FB 4D		        jmp ?got_input
   190 4DEA			?chk_joy
   191 				        ; Check joystick
   192 4DEA AD 00 D3		        lda PORTA
   193 4DED 49 FF		        eor #$FF
   194 4DEF 29 0F		        and #$0F
   195 4DF1 D0 08		        bne ?got_input
   196 				        ; Check fire
   197 4DF3 AD 10 D0		        lda TRIG0
   198 4DF6 F0 03		        beq ?got_input
   199 4DF8 4C DA 4D		        jmp ?wait_key
   200 4DFB			?got_input
   201 4DFB A9 FF		        lda #$FF
   202 4DFD 8D FC 02		        sta $02FC
   203
   204 				        ; Play switch sound
   205 4E00 A2 1C		        ldx #SFX_SWTCHN
   206 4E02 20 A2 5A		        jsr snd_play
   207
   208 				        ; Init menu
   209 4E05 A9 00		        lda #0
   210 4E07 8D 99 4C		        sta menu_sel
   211 4E0A 8D 9A 4C		        sta menu_prev
   212 				        ; Backup title area before drawing menu (one buffer is enough,
   213 				        ; both have same title graphic)
   214 4E0D 20 9C 4C		        jsr backup_menu_area
   215 				        ; Clear + draw menu on both buffers
   216 4E10 A9 00		        lda #0
   217 4E12 8D 9B 4C		        sta draw_mode
   218 4E15 20 7E 4D		        jsr redraw_both
   219
   220 				        ; --- Menu loop ---
   221 4E18			?menu_loop
   222 4E18 20 6F 50		        jsr update_menu
   223 				        ; Redraw cursor if changed
   224 4E1B AD 99 4C		        lda menu_sel
   225 4E1E CD 9A 4C		        cmp menu_prev
   226 4E21 F0 0E		        beq ?no_redraw
   227 4E23 A2 00		        ldx #SFX_PISTOL
   228 4E25 20 A2 5A		        jsr snd_play
   229 4E28 20 7E 4D		        jsr redraw_both
   230 4E2B AD 99 4C		        lda menu_sel
   231 4E2E 8D 9A 4C		        sta menu_prev
   232 4E31			?no_redraw
   233 4E31 A5 14		        lda RTCLOK3
   234 4E33 C5 14		?vs     cmp RTCLOK3
   235 4E35 F0 FC		        beq ?vs
   236 				        ; ESC = close menu, restore title graphic on both buffers
   237 4E37 AD FC 02		        lda $02FC
   238 4E3A C9 1C		        cmp #$1C
   239 4E3C D0 23		        bne ?no_esc
   240 4E3E A9 FF		        lda #$FF
   241 4E40 8D FC 02		        sta $02FC
   242 4E43 A2 1D		        ldx #SFX_SWTCHX
   243 4E45 20 A2 5A		        jsr snd_play
   244 4E48 20 13 4D		        jsr restore_menu_area
   245 4E4B AD FF 22		        lda zbuf_hi
   246 4E4E 49 02		        eor #SCR1_HI
   247 4E50 8D FF 22		        sta zbuf_hi
   248 4E53 20 13 4D		        jsr restore_menu_area
   249 4E56 AD FF 22		        lda zbuf_hi
   250 4E59 49 02		        eor #SCR1_HI
   251 4E5B 8D FF 22		        sta zbuf_hi
   252 4E5E 4C B7 4D		        jmp ?wait_release
   253 4E61			?no_esc
   254 				        ; RETURN = confirm
   255 4E61 C9 0C		        cmp #$0C
   256 4E63 D0 08		        bne ?chk_fire
   257 4E65 A9 FF		        lda #$FF
   258 4E67 8D FC 02		        sta $02FC
   259 4E6A 4C 7D 4E		        jmp ?handle_sel
   260 4E6D			?chk_fire
   261 				        ; FIRE button = confirm
   262 4E6D AD 10 D0		        lda TRIG0
   263 4E70 F0 03		        beq ?fire_pressed
   264 4E72 4C 18 4E		        jmp ?menu_loop
   265 4E75			?fire_pressed
   266 				        ; Wait for release
   267 4E75 AD 10 D0		?fr_rel lda TRIG0
   268 4E78 F0 FB		        beq ?fr_rel
   269 4E7A 4C 7D 4E		        jmp ?handle_sel
   270 4E7D			?handle_sel
   271 4E7D A9 FF		        lda #$FF
   272 4E7F 8D FC 02		        sta $02FC
   273 4E82 A2 00		        ldx #SFX_PISTOL
   274 4E84 20 A2 5A		        jsr snd_play
   275 4E87 AD 99 4C		        lda menu_sel
   276 4E8A D0 01		        bne ?not_ng
   277 4E8C 60			        rts                     ; NEW GAME = return to main
   278 4E8D C9 01		?not_ng cmp #1
   279 4E8F D0 06		        bne ?not_set
   280 4E91 20 38 4F		        jsr menu_settings
   281 4E94 4C 9A 4E		        jmp ?redraw_menu
   282 4E97			?not_set
   283 				        ; CREDITS - TODO
   284 4E97 4C 18 4E		        jmp ?menu_loop
   285
   286 4E9A			?redraw_menu
   287 4E9A 20 7E 4D		        jsr redraw_both
   288 4E9D 4C 18 4E		        jmp ?menu_loop
   289 				.endp
   290
   291 				; ============================================
   292 				; PAUSE MENU (called from game loop, returns when resume)
   293 				; ============================================
   294 4EA0			.proc menu_pause
   295 				        ; Stop current sound, reset lock, play menu open sound
   296 4EA0 A9 00		        lda #0
   297 4EA2 85 C9		        sta snd_active
   298 4EA4 85 CB		        sta snd_lock
   299 4EA6 8D 07 D2		        sta AUDC4
   300 4EA9 A2 1C		        ldx #SFX_SWTCHN
   301 4EAB 20 A2 5A		        jsr snd_play
   302 				        ; Init
   303 4EAE A9 00		        lda #0
   304 4EB0 8D 99 4C		        sta menu_sel
   305 4EB3 8D 9A 4C		        sta menu_prev
   306 				        ; Backup game graphics under menu area
   307 4EB6 20 9C 4C		        jsr backup_menu_area
   308 				        ; Draw on both buffers
   309 4EB9 A9 00		        lda #0
   310 4EBB 8D 9B 4C		        sta draw_mode
   311 4EBE 20 7E 4D		        jsr redraw_both
   312
   313 4EC1 20 6F 50		?loop   jsr update_menu
   314 4EC4 AD 99 4C		        lda menu_sel
   315 4EC7 CD 9A 4C		        cmp menu_prev
   316 4ECA F0 0E		        beq ?no_redraw
   317 4ECC A2 00		        ldx #SFX_PISTOL
   318 4ECE 20 A2 5A		        jsr snd_play
   319 4ED1 20 7E 4D		        jsr redraw_both
   320 4ED4 AD 99 4C		        lda menu_sel
   321 4ED7 8D 9A 4C		        sta menu_prev
   322 4EDA			?no_redraw
   323 4EDA A5 14		        lda RTCLOK3
   324 4EDC C5 14		?vs     cmp RTCLOK3
   325 4EDE F0 FC		        beq ?vs
   326 				        ; ESC = resume
   327 4EE0 AD FC 02		        lda $02FC
   328 4EE3 C9 1C		        cmp #$1C
   329 4EE5 F0 28		        beq ?resume
   330 				        ; RETURN
   331 4EE7 C9 0C		        cmp #$0C
   332 4EE9 D0 D6		        bne ?loop
   333 4EEB A9 FF		        lda #$FF
   334 4EED 8D FC 02		        sta $02FC
   335 4EF0 A2 00		        ldx #SFX_PISTOL
   336 4EF2 20 A2 5A		        jsr snd_play
   337 4EF5 AD 99 4C		        lda menu_sel
   338 4EF8 F0 0D		        beq ?new_game           ; NEW GAME
   339 4EFA C9 01		        cmp #1
   340 4EFC D0 06		        bne ?not_set
   341 4EFE 20 38 4F		        jsr menu_settings
   342 4F01 4C 32 4F		        jmp ?redraw
   343 4F04			?not_set
   344 4F04 4C C1 4E		        jmp ?loop
   345 4F07			?new_game
   346 4F07 A9 FF		        lda #$FF
   347 4F09 8D FC 02		        sta $02FC
   348 4F0C A9 01		        lda #1                  ; A=1 = new game requested
   349 4F0E 60			        rts
   350 4F0F A9 FF		?resume lda #$FF
   351 4F11 8D FC 02		        sta $02FC
   352 4F14 A2 1D		        ldx #SFX_SWTCHX
   353 4F16 20 A2 5A		        jsr snd_play
   354 				        ; Restore game graphics on both buffers
   355 4F19 20 13 4D		        jsr restore_menu_area
   356 4F1C AD FF 22		        lda zbuf_hi
   357 4F1F 49 02		        eor #SCR1_HI
   358 4F21 8D FF 22		        sta zbuf_hi
   359 4F24 20 13 4D		        jsr restore_menu_area
   360 4F27 AD FF 22		        lda zbuf_hi
   361 4F2A 49 02		        eor #SCR1_HI
   362 4F2C 8D FF 22		        sta zbuf_hi
   363 4F2F A9 00		        lda #0                  ; A=0 = resume
   364 4F31 60			        rts                     ; return to game loop
   365 4F32 20 7E 4D		?redraw jsr redraw_both
   366 4F35 4C C1 4E		        jmp ?loop
   367 				.endp
   368
   369 				; ============================================
   370 				; SETTINGS (from title - preserves background)
   371 				; ============================================
   372 4F38			.proc menu_settings
   373 4F38 A9 01		        lda #1
   374 4F3A 8D 9B 4C		        sta draw_mode
   375 4F3D 20 7E 4D		        jsr redraw_both
   376 4F40 A5 14		?loop   lda RTCLOK3
   377 4F42 C5 14		?vs     cmp RTCLOK3
   378 4F44 F0 FC		        beq ?vs
   379 4F46 AD FC 02		        lda $02FC
   380 4F49 C9 0C		        cmp #$0C
   381 4F4B D0 0B		        bne ?chk_esc
   382 4F4D A9 FF		        lda #$FF
   383 4F4F 8D FC 02		        sta $02FC
   384 4F52 20 6F 4F		        jsr toggle_sound
   385 4F55 20 7E 4D		        jsr redraw_both
   386 4F58			?chk_esc
   387 4F58 AD FC 02		        lda $02FC
   388 4F5B C9 1C		        cmp #$1C
   389 4F5D D0 E1		        bne ?loop
   390 4F5F A9 FF		        lda #$FF
   391 4F61 8D FC 02		        sta $02FC
   392 4F64 A2 1D		        ldx #SFX_SWTCHX
   393 4F66 20 A2 5A		        jsr snd_play
   394 4F69 A9 00		        lda #0
   395 4F6B 8D 9B 4C		        sta draw_mode
   396 4F6E 60			        rts
   397 				.endp
   398
   399
   400 				; ============================================
   401 				; TOGGLE SOUND ON/OFF
   402 				; ============================================
   403 4F6F			.proc toggle_sound
   404 4F6F AD 52 5A		        lda snd_enabled
   405 4F72 49 01		        eor #1
   406 4F74 8D 52 5A		        sta snd_enabled
   407 4F77 D0 07		        bne ?done
   408 4F79 A9 00		        lda #0
   409 4F7B 85 C9		        sta snd_active
   410 4F7D 8D 07 D2		        sta AUDC4
   411 4F80 60			?done   rts
   412 				.endp
   413
   414 				; ============================================
   415 				; DRAW TEXT STRING
   416 				; ============================================
   417 4F81			.proc draw_text
   418 4F81 A0 00		        ldy #0
   419 4F83 B1 84		?lp     lda (txt_ptr),y
   420 4F85 F0 35		        beq ?done
   421 4F87 8C BD 4F		        sty dt_y
   422 4F8A C9 20		        cmp #' '
   423 4F8C F0 14		        beq ?space
   424 4F8E C9 30		        cmp #'0'
   425 4F90 90 10		        bcc ?space
   426 4F92 C9 3A		        cmp #':'
   427 4F94 90 06		        bcc ?digit
   428 4F96 38			        sec
   429 4F97 E9 35		        sbc #'A'-12
   430 4F99 4C A5 4F		        jmp ?draw
   431 4F9C 38			?digit  sec
   432 4F9D E9 30		        sbc #'0'
   433 4F9F 4C A5 4F		        jmp ?draw
   434 4FA2 4C A8 4F		?space  jmp ?adv                ; space = skip (transparent)
   435 4FA5 20 0D 49		?draw   jsr blit_hud_char
   436 4FA8 A5 D0		?adv    lda zdx
   437 4FAA 18			        clc
   438 4FAB 69 08		        adc #8
   439 4FAD 85 D0		        sta zdx
   440 4FAF A5 D1		        lda zdxh
   441 4FB1 69 00		        adc #0
   442 4FB3 85 D1		        sta zdxh
   443 4FB5 AC BD 4F		        ldy dt_y
   444 4FB8 C8			        iny
   445 4FB9 4C 83 4F		        jmp ?lp
   446 4FBC 60			?done   rts
   447 4FBD 00			dt_y    dta 0
   448 				.endp
   449
   450 				; ============================================
   451 				; DRAW MENU (cursor + 3 items)
   452 				; ============================================
   453 4FBE			.proc draw_menu
   454 				        ; Cursor
   455 4FBE AD 99 4C		        lda menu_sel
   456 4FC1 0A			        asl
   457 4FC2 0A			        asl
   458 4FC3 0A			        asl
   459 4FC4 0A			        asl
   460 4FC5 18			        clc
   461 4FC6 69 50		        adc #MENU_Y
   462 4FC8 85 D2		        sta zdy
   463 4FCA A9 60		        lda #MENU_X-16
   464 4FCC 85 D0		        sta zdx
   465 4FCE A9 00		        lda #0
   466 4FD0 85 D1		        sta zdxh
   467 4FD2 A9 0B		        lda #11
   468 4FD4 20 0D 49		        jsr blit_hud_char
   469 				        ; NEW GAME
   470 4FD7 A9 52		        lda #<txt_newgame
   471 4FD9 85 84		        sta txt_ptr
   472 4FDB A9 51		        lda #>txt_newgame
   473 4FDD 85 85		        sta txt_ptr+1
   474 4FDF A9 70		        lda #MENU_X
   475 4FE1 85 D0		        sta zdx
   476 4FE3 A9 00		        lda #0
   477 4FE5 85 D1		        sta zdxh
   478 4FE7 A9 50		        lda #MENU_Y
   479 4FE9 85 D2		        sta zdy
   480 4FEB 20 81 4F		        jsr draw_text
   481 				        ; SETTINGS
   482 4FEE A9 5B		        lda #<txt_settings
   483 4FF0 85 84		        sta txt_ptr
   484 4FF2 A9 51		        lda #>txt_settings
   485 4FF4 85 85		        sta txt_ptr+1
   486 4FF6 A9 70		        lda #MENU_X
   487 4FF8 85 D0		        sta zdx
   488 4FFA A9 00		        lda #0
   489 4FFC 85 D1		        sta zdxh
   490 4FFE A9 60		        lda #MENU_Y+MENU_SPACE
   491 5000 85 D2		        sta zdy
   492 5002 20 81 4F		        jsr draw_text
   493 				        ; CREDITS
   494 5005 A9 64		        lda #<txt_credits
   495 5007 85 84		        sta txt_ptr
   496 5009 A9 51		        lda #>txt_credits
   497 500B 85 85		        sta txt_ptr+1
   498 500D A9 70		        lda #MENU_X
   499 500F 85 D0		        sta zdx
   500 5011 A9 00		        lda #0
   501 5013 85 D1		        sta zdxh
   502 5015 A9 70		        lda #MENU_Y+MENU_SPACE*2
   503 5017 85 D2		        sta zdy
   504 5019 4C 81 4F		        jmp draw_text
   505 				.endp
   506
   507 				; ============================================
   508 				; DRAW SETTINGS
   509 				; ============================================
   510 501C			.proc draw_settings
   511 501C A9 6D		        lda #<txt_snd_lbl
   512 501E 85 84		        sta txt_ptr
   513 5020 A9 51		        lda #>txt_snd_lbl
   514 5022 85 85		        sta txt_ptr+1
   515 5024 A9 70		        lda #MENU_X
   516 5026 85 D0		        sta zdx
   517 5028 A9 00		        lda #0
   518 502A 85 D1		        sta zdxh
   519 502C A9 50		        lda #MENU_Y
   520 502E 85 D2		        sta zdy
   521 5030 20 81 4F		        jsr draw_text
   522 5033 AD 52 5A		        lda snd_enabled
   523 5036 D0 09		        bne ?on
   524 5038 A9 7F		        lda #<txt_off
   525 503A 85 84		        sta txt_ptr
   526 503C A9 51		        lda #>txt_off
   527 503E 4C 47 50		        jmp ?dr
   528 5041 A9 76		?on     lda #<txt_on
   529 5043 85 84		        sta txt_ptr
   530 5045 A9 51		        lda #>txt_on
   531 5047 85 85		?dr     sta txt_ptr+1
   532 5049 A9 A8		        lda #MENU_X+56
   533 504B 85 D0		        sta zdx
   534 504D A9 00		        lda #0
   535 504F 85 D1		        sta zdxh
   536 5051 A9 50		        lda #MENU_Y
   537 5053 85 D2		        sta zdy
   538 5055 20 81 4F		        jsr draw_text
   539 				        ;-- Row 2: empty (clear old SETTINGS text)
   540 5058 A9 88		        lda #<txt_empty
   541 505A 85 84		        sta txt_ptr
   542 505C A9 51		        lda #>txt_empty
   543 505E 85 85		        sta txt_ptr+1
   544 5060 A9 70		        lda #MENU_X
   545 5062 85 D0		        sta zdx
   546 5064 A9 00		        lda #0
   547 5066 85 D1		        sta zdxh
   548 5068 A9 60		        lda #MENU_Y+MENU_SPACE
   549 506A 85 D2		        sta zdy
   550 506C 4C 81 4F		        jmp draw_text
   551 				.endp
   552
   553 				; ============================================
   554 				; UPDATE MENU (joystick + keyboard)
   555 				; ============================================
   556 506F			.proc update_menu
   557 506F AD 00 D3		        lda PORTA
   558 5072 49 FF		        eor #$FF
   559 5074 8D F2 50		        sta um_joy
   560 5077 29 01		        and #J_UP
   561 5079 F0 0F		        beq ?no_jup
   562 507B AD F3 50		        lda um_prev
   563 507E 29 01		        and #J_UP
   564 5080 D0 08		        bne ?no_jup
   565 5082 AD 99 4C		        lda menu_sel
   566 5085 F0 03		        beq ?no_jup
   567 5087 CE 99 4C		        dec menu_sel
   568 508A AD F2 50		?no_jup lda um_joy
   569 508D 29 02		        and #J_DOWN
   570 508F F0 11		        beq ?no_jdn
   571 5091 AD F3 50		        lda um_prev
   572 5094 29 02		        and #J_DOWN
   573 5096 D0 0A		        bne ?no_jdn
   574 5098 AD 99 4C		        lda menu_sel
   575 509B C9 02		        cmp #MENU_ITEMS-1
   576 509D B0 03		        bcs ?no_jdn
   577 509F EE 99 4C		        inc menu_sel
   578 50A2 AD F2 50		?no_jdn lda um_joy
   579 50A5 8D F3 50		        sta um_prev
   580 50A8 AD FC 02		        lda $02FC
   581 50AB C9 FF		        cmp #$FF
   582 50AD F0 42		        beq ?done
   583 50AF C9 06		        cmp #$06
   584 50B1 F0 1F		        beq ?kup
   585 50B3 C9 0E		        cmp #$0E
   586 50B5 F0 1B		        beq ?kup
   587 50B7 C9 86		        cmp #$86
   588 50B9 F0 17		        beq ?kup
   589 50BB C9 8E		        cmp #$8E
   590 50BD F0 13		        beq ?kup
   591 50BF C9 07		        cmp #$07
   592 50C1 F0 1F		        beq ?kdn
   593 50C3 C9 0F		        cmp #$0F
   594 50C5 F0 1B		        beq ?kdn
   595 50C7 C9 87		        cmp #$87
   596 50C9 F0 17		        beq ?kdn
   597 50CB C9 8F		        cmp #$8F
   598 50CD F0 13		        beq ?kdn
   599 50CF 4C F1 50		        jmp ?done
   600 50D2 A9 FF		?kup    lda #$FF
   601 50D4 8D FC 02		        sta $02FC
   602 50D7 AD 99 4C		        lda menu_sel
   603 50DA F0 15		        beq ?done
   604 50DC CE 99 4C		        dec menu_sel
   605 50DF 4C F1 50		        jmp ?done
   606 50E2 A9 FF		?kdn    lda #$FF
   607 50E4 8D FC 02		        sta $02FC
   608 50E7 AD 99 4C		        lda menu_sel
   609 50EA C9 02		        cmp #MENU_ITEMS-1
   610 50EC B0 03		        bcs ?done
   611 50EE EE 99 4C		        inc menu_sel
   612 50F1 60			?done   rts
   613 50F2 00			um_joy  dta 0
   614 50F3 00			um_prev dta 0
   615 				.endp
   616
   617 				; Clear menu area with black (VBXE constant fill: AND=0, XOR=0)
   618 50F4			.proc clear_menu_area
   619 50F4 A9 FF		        lda #BANK_EN+BANK_BCB
   620 50F6 8D 5F D6		        sta VBXE_BANK_SEL
   621 50F9 A9 00		        lda #0
   622 50FB 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   623 50FE 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   624 5101 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   625 5104 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   626 5107 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   627 510A 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   628 510D 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15  ; AND = 0
   629 5110 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20  ; mode 0
   630 5113 85 D1		        sta zdxh
   631 5115 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   632 				        ; XOR [16-19] already 0 from setup_xdl
   633 5118 A9 50		        lda #80
   634 511A 85 D0		        sta zdx
   635 511C A9 48		        lda #72
   636 511E 85 D2		        sta zdy
   637 5120 20 E5 22		        jsr calc_dst
   638 5123 A5 86		        lda zva
   639 5125 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   640 5128 A5 87		        lda zva+1
   641 512A 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   642 512D A5 88		        lda zva+2
   643 512F 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   644 5132 A9 B7		        lda #183
   645 5134 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   646 5137 A9 3F		        lda #63
   647 5139 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   648 513C 20 C7 22		        jsr run_blit
   649 513F 20 DF 22		        jsr wait_blit
   650 5142 A9 FF		        lda #BANK_EN+BANK_BCB
   651 5144 8D 5F D6		        sta VBXE_BANK_SEL
   652 5147 A9 FF		        lda #$FF
   653 5149 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15
   654 514C A9 01		        lda #BLT_TRANS
   655 514E 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   656 5151 60			        rts
   657 				.endp
   658
   659 				; ============================================
   660 				; TEXT DATA
   661 				; ============================================
   662 5152 4E 45 57 20 47 41 + txt_newgame dta c'NEW GAME',0
   663 515B 53 45 54 54 49 4E + txt_settings dta c'SETTINGS',0
   664 5164 43 52 45 44 49 54 + txt_credits dta c'CREDITS ',0
   665 516D 53 4F 55 4E 44 20 + txt_snd_lbl dta c'SOUND   ',0
   666 5176 4F 4E 20 20 20 20 + txt_on  dta c'ON      ',0
   667 517F 4F 46 46 20 20 20 + txt_off dta c'OFF     ',0
   668 5188 20 20 20 20 20 20 + txt_empty dta c'        ',0
   323 5191			        icl 'dirty.asm'
Source: dirty.asm
     1 				;==============================================
     2 				; DOOM2D - Dirty tile tracking for optimized rendering
     3 				; dirty.asm
     4 				;
     5 				; Instead of redrawing all 240 tiles every frame,
     6 				; only restore tiles that had sprites on them.
     7 				;==============================================
     8
     9 				; Dirty flags: 1 byte per tile, 0=clean, 1=dirty
    10 				; Separate array per double-buffer
    11 = 5191			dirty_0 .ds TILES_X*TILES_Y    ; 240 bytes for buffer 0
    12 = 5281			dirty_1 .ds TILES_X*TILES_Y    ; 240 bytes for buffer 1
    13
    14
    15 				; ============================================
    16 				; SETUP DIRTY POINTER for current back buffer
    17 				; ============================================
    18 5371			.proc setup_dirty_ptr
    19 5371-555E> AD FF 22	        lda zbuf_hi
    20 5374 D0 09		        bne ?buf1
    21 5376 A9 91		        lda #<dirty_0
    22 5378 85 B9		        sta dirty_ptr
    23 537A A9 51		        lda #>dirty_0
    24 537C 85 BA		        sta dirty_ptr+1
    25 537E 60			        rts
    26 537F A9 81		?buf1   lda #<dirty_1
    27 5381 85 B9		        sta dirty_ptr
    28 5383 A9 52		        lda #>dirty_1
    29 5385 85 BA		        sta dirty_ptr+1
    30 5387 60			        rts
    31 				.endp
    32
    33
    34 				; ============================================
    35 				; CLEAR ALL DIRTY FLAGS (both buffers, for init)
    36 				; ============================================
    37 5388			.proc clear_dirty_all
    38 5388 A9 00		        lda #0
    39 538A A0 EF		        ldy #TILES_X*TILES_Y-1
    40 538C 99 91 51		?lp     sta dirty_0,y
    41 538F 99 81 52		        sta dirty_1,y
    42 5392 88			        dey
    43 5393 10 F7		        bpl ?lp
    44 5395 60			        rts
    45 				.endp
    46
    47 				; init_render is in the overlay segment (end of main.asm)
    48
    49 				; ============================================
    50 				; RENDER STATIC: bake pickups + decorations into background
    51 				; Called once per buffer during init_render.
    52 				; No mark_dirty - these are part of the background.
    53 				; ============================================
    54 5396			.proc render_static
    55 				        ; --- Pickups ---
    56 5396 A2 00		        ldx #0
    57 5398 BD EC 30		?pk     lda pk_act,x
    58 539B F0 3C		        beq ?pnx
    59 539D 8E E1 53		        stx rs_idx
    60 53A0 BD F8 30		        lda pk_x,x
    61 53A3 38			        sec
    62 53A4 E5 D7		        sbc cam_x_lo
    63 53A6 85 D0		        sta zdx
    64 53A8 BD 04 31		        lda pk_xhi,x
    65 53AB E5 D8		        sbc cam_x_hi
    66 53AD 85 D1		        sta zdxh
    67 53AF BD 10 31		        lda pk_y,x
    68 53B2 38			        sec
    69 53B3 E9 10		        sbc #16
    70 53B5 85 80		        sta zt
    71 53B7 BD 1C 31		        lda pk_yhi,x
    72 53BA E9 00		        sbc #0
    73 53BC AA			        tax                 ; X = Y high byte
    74 53BD A5 80		        lda zt              ; A = Y low byte
    75 53BF 20 85 A5		        jsr calc_scr_y
    76 53C2 90 12		        bcc ?pnx2
    77 53C4 85 D2		        sta zdy
    78 53C6 AE E1 53		        ldx rs_idx
    79 53C9 AE E1 53		        ldx rs_idx
    80 53CC BD 28 31		        lda pk_type,x
    81 53CF AA			        tax
    82 53D0 BD BD 5E		        lda pk_spr_tab,x
    83 53D3 20 C8 23		        jsr blit_sprite
    84 53D6 AE E1 53		?pnx2   ldx rs_idx
    85 53D9 E8			?pnx    inx
    86 53DA E0 0C		        cpx #MAX_PICKUPS
    87 53DC D0 BA		        bne ?pk
    88
    89 				        ; --- Decorations ---
    90 53DE 4C 1B 35		        jmp render_decorations
    91 53E1 00			rs_idx  dta 0
    92 				.endp
    93
    94 				; ============================================
    95 				; RESTORE DIRTY TILES
    96 				; Re-blits only tiles marked dirty, then clears flags.
    97 				; Call at start of render phase (before drawing sprites).
    98 				; ============================================
    99 				; Dirty bounding box (set by restore_dirty, used by static redraw)
   100 53E2 14			dirty_min_col dta TILES_X
   101 53E3 00			dirty_max_col dta 0
   102 53E4 0C			dirty_min_row dta TILES_Y
   103 53E5 00			dirty_max_row dta 0
   104 53E6 00			dirty_any     dta 0             ; 0 = no dirty tiles this frame
   105
   106 53E7			.proc restore_dirty
   107 53E7 20 71 53		        jsr setup_dirty_ptr
   108 				        ; Reset bounding box
   109 53EA A9 14		        lda #TILES_X
   110 53EC 8D E2 53		        sta dirty_min_col
   111 53EF A9 0C		        lda #TILES_Y
   112 53F1 8D E4 53		        sta dirty_min_row
   113 53F4 A9 00		        lda #0
   114 53F6 8D E3 53		        sta dirty_max_col
   115 53F9 8D E5 53		        sta dirty_max_row
   116 53FC 8D E6 53		        sta dirty_any
   117 53FF 8D 8F 54		        sta rd_idx
   118 5402 8D 34 47		        sta r_row
   119 5405 A9 00		?rrow   lda #0
   120 5407 8D 35 47		        sta r_col
   121 540A AC 8F 54		?rcol   ldy rd_idx
   122 540D B1 B9		        lda (dirty_ptr),y
   123 540F F0 63		        beq ?clean
   124
   125 				        ; --- Dirty tile: restore it ---
   126 				        ; Clear flag
   127 5411 A9 00		        lda #0
   128 5413 91 B9		        sta (dirty_ptr),y
   129
   130 				        ; Update bounding box
   131 5415 A9 01		        lda #1
   132 5417 8D E6 53		        sta dirty_any
   133 541A AD 35 47		        lda r_col
   134 541D CD E2 53		        cmp dirty_min_col
   135 5420 B0 03		        bcs ?nc1
   136 5422 8D E2 53		        sta dirty_min_col
   137 5425 CD E3 53		?nc1    cmp dirty_max_col
   138 5428 90 03		        bcc ?nc2
   139 542A 8D E3 53		        sta dirty_max_col
   140 542D AD 34 47		?nc2    lda r_row
   141 5430 CD E4 53		        cmp dirty_min_row
   142 5433 B0 03		        bcs ?nc3
   143 5435 8D E4 53		        sta dirty_min_row
   144 5438 CD E5 53		?nc3    cmp dirty_max_row
   145 543B 90 03		        bcc ?nc4
   146 543D 8D E5 53		        sta dirty_max_row
   147 5440			?nc4
   148 				        ; Look up map tile at (cam_tx + r_col, cam_ty + r_row)
   149 5440 A9 9E		        lda #BANK_EN+BANK_MAP
   150 5442 8D 5F D6		        sta VBXE_BANK_SEL
   151 5445 AD 34 47		        lda r_row
   152 5448 18			        clc
   153 5449 65 DE		        adc cam_ty
   154 544B A8			        tay
   155 544C B9 C6 A3		        lda map_row_lo,y
   156 544F 18			        clc
   157 5450 65 D6		        adc cam_tx
   158 5452 6D 35 47		        adc r_col
   159 5455 85 D4		        sta ztptr
   160 5457 B9 E6 A3		        lda map_row_hi,y
   161 545A 69 00		        adc #0
   162 545C 85 D5		        sta ztptr+1
   163
   164 545E A0 00		        ldy #0
   165 5460 B1 D4		        lda (ztptr),y
   166 5462 F0 0D		        beq ?empty
   167 5464 C9 0F		        cmp #15
   168 5466 F0 09		        beq ?empty             ; tile 15 = invisible solid (barrel), draw sky
   169 				        ; Non-empty tile: blit it
   170 5468 8D 36 47		        sta r_tile
   171 546B 20 01 23		        jsr blit_tile
   172 546E 4C 74 54		        jmp ?clean
   173 5471			?empty  ; Empty/invisible tile: restore sky background
   174 5471 20 79 24		        jsr blit_bg
   175
   176 5474 EE 8F 54		?clean  inc rd_idx
   177 5477 EE 35 47		        inc r_col
   178 547A AD 35 47		        lda r_col
   179 547D C9 14		        cmp #TILES_X
   180 547F 90 89		        bcc ?rcol
   181 5481 EE 34 47		        inc r_row
   182 5484 AD 34 47		        lda r_row
   183 5487 C9 0C		        cmp #TILES_Y
   184 5489 B0 03		        bcs ?done
   185 548B 4C 05 54		        jmp ?rrow
   186 548E 60			?done   rts
   187 548F 00			rd_idx  dta 0
   188 				.endp
   189
   190 				; ============================================
   191 				; MARK DIRTY SPRITE
   192 				; Marks tiles covered by sprite at (zdx:zdxh, zdy)
   193 				; with size md_w x md_h as dirty.
   194 				; Call BEFORE blit_sprite for each sprite.
   195 				; ============================================
   196 5490			.proc mark_dirty_sprite
   197 				        ; --- Left column ---
   198 				        ; col = (zdxh << 4) | (zdx >> 4)
   199 5490 A5 D1		        lda zdxh
   200 5492 C9 02		        cmp #2
   201 5494 90 01		        bcc ?onscr
   202 5496 60			        rts                     ; off screen right, skip
   203 5497			?onscr
   204 5497 0A			        asl
   205 5498 0A			        asl
   206 5499 0A			        asl
   207 549A 0A			        asl
   208 549B 8D 56 55		        sta md_cl
   209 549E A6 D0		        ldx zdx
   210 54A0 BD C6 A2		        lda pix_to_tile,x
   211 54A3 0D 56 55		        ora md_cl
   212 54A6 8D 56 55		        sta md_cl               ; left column
   213
   214 				        ; --- Right column ---
   215 				        ; right_x = zdx + md_w - 1 (16-bit)
   216 54A9 A5 D0		        lda zdx
   217 54AB 18			        clc
   218 54AC 6D 5D 55		        adc md_w
   219 54AF 85 80		        sta zt
   220 54B1 A5 D1		        lda zdxh
   221 54B3 69 00		        adc #0
   222 54B5 85 81		        sta zt2                 ; zt2:zt = zdx + md_w
   223 				        ; Subtract 1
   224 54B7 A5 80		        lda zt
   225 54B9 38			        sec
   226 54BA E9 01		        sbc #1
   227 54BC 85 80		        sta zt
   228 54BE A5 81		        lda zt2
   229 54C0 E9 00		        sbc #0
   230 54C2 85 81		        sta zt2                 ; zt2:zt = right pixel
   231 				        ; col = (zt2 << 4) | (zt >> 4)
   232 54C4 A5 81		        lda zt2
   233 54C6 0A			        asl
   234 54C7 0A			        asl
   235 54C8 0A			        asl
   236 54C9 0A			        asl
   237 54CA 8D 57 55		        sta md_cr
   238 54CD A6 80		        ldx zt
   239 54CF BD C6 A2		        lda pix_to_tile,x
   240 54D2 0D 57 55		        ora md_cr
   241 54D5 8D 57 55		        sta md_cr               ; right column
   242
   243 				        ; --- Top row ---
   244 54D8 A6 D2		        ldx zdy
   245 54DA BD C6 A2		        lda pix_to_tile,x
   246 54DD 8D 58 55		        sta md_rt               ; top row
   247
   248 				        ; --- Bottom row ---
   249 54E0 A5 D2		        lda zdy
   250 54E2 18			        clc
   251 54E3 6D 5E 55		        adc md_h
   252 54E6 38			        sec
   253 54E7 E9 01		        sbc #1                  ; bottom pixel
   254 54E9 AA			        tax
   255 54EA BD C6 A2		        lda pix_to_tile,x
   256 54ED 8D 59 55		        sta md_rb               ; bottom row
   257
   258 				        ; --- Clamp to screen bounds ---
   259 54F0 AD 56 55		        lda md_cl
   260 54F3 C9 14		        cmp #TILES_X
   261 54F5 90 01		        bcc ?cl_ok
   262 54F7 60			        rts                     ; left col >= 20: entirely off screen
   263 54F8			?cl_ok
   264 54F8 AD 57 55		        lda md_cr
   265 54FB C9 14		        cmp #TILES_X
   266 54FD 90 05		        bcc ?cr_ok
   267 54FF A9 13		        lda #TILES_X-1
   268 5501 8D 57 55		        sta md_cr
   269 5504 AD 58 55		?cr_ok  lda md_rt
   270 5507 C9 0C		        cmp #TILES_Y
   271 5509 90 01		        bcc ?rt_ok
   272 550B 60			        rts                     ; top row >= 12: below screen
   273 550C			?rt_ok
   274 550C AD 59 55		        lda md_rb
   275 550F C9 0C		        cmp #TILES_Y
   276 5511 90 05		        bcc ?rb_ok
   277 5513 A9 0B		        lda #TILES_Y-1
   278 5515 8D 59 55		        sta md_rb
   279 5518			?rb_ok
   280 				        ; --- Mark tiles in rectangle [md_cl..md_cr] x [md_rt..md_rb] ---
   281 5518 AD 58 55		        lda md_rt
   282 551B 8D 5A 55		        sta md_r
   283 551E			?rlp    ; dirty_idx = md_r * 20 + md_c
   284 551E AE 5A 55		        ldx md_r
   285 5521 BD 06 A4		        lda row_x20,x
   286 5524 8D 5C 55		        sta md_base             ; row * 20
   287
   288 5527 AD 56 55		        lda md_cl
   289 552A 8D 5B 55		        sta md_c
   290 552D AD 5C 55		?clp    lda md_base
   291 5530 18			        clc
   292 5531 6D 5B 55		        adc md_c
   293 5534 A8			        tay
   294 5535 A9 01		        lda #1
   295 5537 91 B9		        sta (dirty_ptr),y
   296
   297 5539 AD 5B 55		        lda md_c
   298 553C CD 57 55		        cmp md_cr
   299 553F B0 06		        bcs ?rnx                ; done with this row
   300 5541 EE 5B 55		        inc md_c
   301 5544 4C 2D 55		        jmp ?clp
   302
   303 5547 AD 5A 55		?rnx    lda md_r
   304 554A CD 59 55		        cmp md_rb
   305 554D B0 06		        bcs ?done               ; done with all rows
   306 554F EE 5A 55		        inc md_r
   307 5552 4C 1E 55		        jmp ?rlp
   308
   309 5555 60			?done   rts
   310
   311 5556 00			md_cl   dta 0                   ; left column
   312 5557 00			md_cr   dta 0                   ; right column
   313 5558 00			md_rt   dta 0                   ; top row
   314 5559 00			md_rb   dta 0                   ; bottom row
   315 555A 00			md_r    dta 0                   ; current row
   316 555B 00			md_c    dta 0                   ; current column
   317 555C 00			md_base dta 0                   ; row * 20
   318 				.endp
   319
   320 				; Sprite size params (set before calling mark_dirty_sprite)
   321 555D 00			md_w    dta 0
   322 555E 00			md_h    dta 0
   324 555F			        icl 'door.asm'
Source: door.asm
     1 				;==============================================
     2 				; DOOM2D - Door system
     3 				; door.asm
     4 				;
     5 				; Doors are tile-based (TILE_DOOR=4).
     6 				; Open = tile becomes empty (0), Close = tile becomes TILE_DOOR (4).
     7 				; Like D2DF: instant open/close, no animation.
     8 				; Auto-close after DOOR_TIMER frames.
     9 				; Key-colored doors require matching key in zpkeys.
    10 				;==============================================
    11
    12 = 0008			MAX_DOORS       = 8
    13 = 0096			DOOR_TIMER_INIT = 150           ; ~2.5s at 60fps before auto-close
    14 = 0004			TILE_DOOR_ID    = 4
    15 = 0019			TILE_DOOR_RED   = 25
    16 = 001A			TILE_DOOR_BLUE  = 26
    17 = 001B			TILE_DOOR_YEL   = 27
    18
    19 				; Door arrays
    20 = 555F			door_col    .ds MAX_DOORS       ; tile column (0-63)
    21 = 5567			door_row    .ds MAX_DOORS       ; tile row (0-31)
    22 = 556F			door_state  .ds MAX_DOORS       ; 0=closed, 1=open
    23 = 5577			door_timer  .ds MAX_DOORS       ; countdown to auto-close (0=inactive)
    24 = 557F			door_key    .ds MAX_DOORS       ; required key: 0=none, 1=red, 2=blue, 4=yellow
    25 = 5587			door_tile   .ds MAX_DOORS       ; original tile type (for close_door restore)
    26 558F-5ED1> 00		num_doors   dta 0               ; actual door count
    27
    28 				; init_doors is in the overlay segment (end of main.asm)
    29
    30 				; ============================================
    31 				; ASSIGN DOOR KEYS from entity spawn data
    32 				; Called after init_doors with door key spawn info
    33 				; Input: door_key_data (from map2bin.py)
    34 				; ============================================
    35 5590			.proc init_door_keys
    36 5590 A2 00		        ldx #0
    37 5592 EC 8F 55		?lp     cpx num_doors
    38 5595 B0 0A		        bcs ?done
    39 5597 BD C0 57		        lda door_key_data,x
    40 559A 9D 7F 55		        sta door_key,x
    41 559D E8			        inx
    42 559E 4C 92 55		        jmp ?lp
    43 55A1 60			?done   rts
    44 				.endp
    45
    46 				; ============================================
    47 				; UPDATE DOORS - auto-close timer + player USE
    48 				; Called every frame from main loop
    49 				; ============================================
    50 55A2			.proc update_doors
    51 55A2 A2 00		        ldx #0
    52 55A4 EC 8F 55		?lp     cpx num_doors
    53 55A7 B0 30		        bcs ?done
    54
    55 55A9 BD 6F 55		        lda door_state,x
    56 55AC F0 23		        beq ?next               ; closed, skip timer
    57
    58 				        ; Door is open — countdown
    59 55AE BD 77 55		        lda door_timer,x
    60 55B1 F0 1E		        beq ?next               ; timer already 0
    61 55B3 DE 77 55		        dec door_timer,x
    62 55B6 D0 19		        bne ?next               ; not yet
    63
    64 				        ; Timer expired — try to close
    65 				        ; Check if player is in door tile
    66 55B8 8E BF 57		        stx door_tmp_idx
    67 55BB 20 49 57		        jsr check_player_in_door
    68 55BE D0 09		        bne ?keep_open          ; player inside, don't close
    69 55C0 AE BF 57		        ldx door_tmp_idx
    70 55C3 20 9D 56		        jsr close_door
    71 55C6 4C D5 55		        jmp ?next2
    72
    73 55C9			?keep_open
    74 55C9 AE BF 57		        ldx door_tmp_idx
    75 55CC A9 0A		        lda #10                 ; retry in 10 frames
    76 55CE 9D 77 55		        sta door_timer,x
    77
    78 55D1 E8			?next   inx
    79 55D2 4C A4 55		        jmp ?lp
    80 55D5 E8			?next2  inx
    81 55D6 4C A4 55		        jmp ?lp
    82 55D9 60			?done   rts
    83 				.endp
    84
    85 				; ============================================
    86 				; TRY OPEN DOOR - Player pressed USE near a door
    87 				; Called from player_update when USE is pressed
    88 				; ============================================
    89 55DA			.proc try_open_door
    90 				        ; Find nearest closed door within range
    91 				        ; Player tile col = (zpx_hi:zpx + 8) / 16
    92 				        ; Player tile row = (zpy) / 16 and (zpy-16)/16 (head)
    93 55DA A2 00		        ldx #0
    94 55DC EC 8F 55		?lp     cpx num_doors
    95 55DF B0 40		        bcs ?none
    96
    97 55E1 BD 6F 55		        lda door_state,x
    98 55E4 D0 29		        bne ?nx                 ; already open, skip
    99
   100 				        ; Check if player is adjacent to this door
   101 55E6 8E BF 57		        stx door_tmp_idx
   102 55E9 20 DB 56		        jsr check_player_near_door
   103 55EC F0 25		        beq ?nx2                ; not near
   104
   105 				        ; Found a closed door near player!
   106 55EE AE BF 57		        ldx door_tmp_idx
   107 				        ; Key check
   108 55F1 BD 7F 55		        lda door_key,x
   109 55F4 F0 11		        beq ?open_it            ; no key needed
   110
   111 				        ; Check if player has required key
   112 55F6 25 A1		        and zpkeys
   113 55F8 DD 7F 55		        cmp door_key,x
   114 55FB F0 0A		        beq ?open_it            ; has the key!
   115
   116 				        ; No key — play denied sound
   117 55FD 86 81		        stx zt2
   118 55FF A2 18		        ldx #SFX_OOF
   119 5601 20 A2 5A		        jsr snd_play
   120 5604 A6 81		        ldx zt2
   121 5606 60			        rts                     ; can't open
   122
   123 5607			?open_it
   124 				        ; Check if this door is switch-only (linked as SW_ACT_DOOR target)
   125 5607 20 F0 58		        jsr is_door_sw_only
   126 560A D0 0E		        bne ?nx3                ; switch-only door, skip
   127 560C 4C 24 56		        jmp open_door
   128
   129 560F E8			?nx     inx
   130 5610 4C DC 55		        jmp ?lp
   131 5613 AE BF 57		?nx2    ldx door_tmp_idx
   132 5616 E8			        inx
   133 5617 4C DC 55		        jmp ?lp
   134 561A AE BF 57		?nx3    ldx door_tmp_idx
   135 561D E8			        inx
   136 561E 4C DC 55		        jmp ?lp
   137 5621			?none   ; No door found — check for switch
   138 5621 4C CB 57		        jmp try_use_switch
   139 				.endp
   140
   141 				; ============================================
   142 				; OPEN DOOR - Set door to open state
   143 				; Input: X = door index
   144 				; ============================================
   145 5624			.proc open_door
   146 5624 A9 01		        lda #1
   147 5626 9D 6F 55		        sta door_state,x
   148 5629 A9 96		        lda #DOOR_TIMER_INIT
   149 562B 9D 77 55		        sta door_timer,x
   150
   151 				        ; Change map tile to empty (0)
   152 562E 8E 62 56		        stx od_idx
   153 5631 A9 9E		        lda #BANK_EN+BANK_MAP
   154 5633 8D 5F D6		        sta VBXE_BANK_SEL
   155
   156 5636 BC 67 55		        ldy door_row,x
   157 5639 B9 C6 A3		        lda map_row_lo,y
   158 563C 85 D4		        sta ztptr
   159 563E B9 E6 A3		        lda map_row_hi,y
   160 5641 85 D5		        sta ztptr+1
   161 5643 AE 62 56		        ldx od_idx
   162 5646 BC 5F 55		        ldy door_col,x
   163 5649 A9 00		        lda #0                  ; empty tile
   164 564B 91 D4		        sta (ztptr),y
   165
   166 564D A9 00		        lda #0
   167 564F 8D 5F D6		        sta VBXE_BANK_SEL
   168
   169 				        ; Mark dirty for redraw
   170 5652 AE 62 56		        ldx od_idx
   171 5655 20 A1 57		        jsr mark_door_dirty
   172
   173 				        ; Play open sound
   174 5658 86 81		        stx zt2
   175 565A A2 16		        ldx #SFX_DOOROPN
   176 565C 20 A2 5A		        jsr snd_play
   177 565F A6 81		        ldx zt2
   178 5661 60			        rts
   179 5662 00			od_idx  dta 0
   180 				.endp
   181
   182 				; ============================================
   183 				; [RESTART-TEMP] RESTART CLOSE DOORS - close all open doors (for level restart)
   184 				; Restores map tiles without sound/dirty marking.
   185 				; ============================================
   186 5663			.proc restart_close_doors
   187 5663 A2 00		        ldx #0
   188 5665 EC 8F 55		?lp     cpx num_doors
   189 5668 B0 31		        bcs ?done
   190 566A BD 6F 55		        lda door_state,x
   191 566D F0 28		        beq ?nx
   192 				        ; Door is open: restore map tile
   193 566F 8E 9C 56		        stx rcd_idx
   194 5672 A9 9E		        lda #BANK_EN+BANK_MAP
   195 5674 8D 5F D6		        sta VBXE_BANK_SEL
   196 5677 BC 67 55		        ldy door_row,x
   197 567A B9 C6 A3		        lda map_row_lo,y
   198 567D 85 D4		        sta ztptr
   199 567F B9 E6 A3		        lda map_row_hi,y
   200 5682 85 D5		        sta ztptr+1
   201 5684 AE 9C 56		        ldx rcd_idx
   202 5687 BC 5F 55		        ldy door_col,x
   203 568A BD 87 55		        lda door_tile,x
   204 568D 91 D4		        sta (ztptr),y
   205 568F A9 00		        lda #0
   206 5691 8D 5F D6		        sta VBXE_BANK_SEL
   207 5694 AE 9C 56		        ldx rcd_idx
   208 5697 E8			?nx     inx
   209 5698 4C 65 56		        jmp ?lp
   210 569B 60			?done   rts
   211 569C 00			rcd_idx dta 0
   212 				.endp
   213
   214 				; ============================================
   215 				; CLOSE DOOR
   216 				; Input: X = door index
   217 				; ============================================
   218 569D			.proc close_door
   219 569D A9 00		        lda #0
   220 569F 9D 6F 55		        sta door_state,x
   221 56A2 9D 77 55		        sta door_timer,x
   222
   223 				        ; Change map tile back to door
   224 56A5 8E DA 56		        stx cd_idx
   225 56A8 A9 9E		        lda #BANK_EN+BANK_MAP
   226 56AA 8D 5F D6		        sta VBXE_BANK_SEL
   227
   228 56AD BC 67 55		        ldy door_row,x
   229 56B0 B9 C6 A3		        lda map_row_lo,y
   230 56B3 85 D4		        sta ztptr
   231 56B5 B9 E6 A3		        lda map_row_hi,y
   232 56B8 85 D5		        sta ztptr+1
   233 56BA AE DA 56		        ldx cd_idx
   234 56BD BC 5F 55		        ldy door_col,x
   235 56C0 BD 87 55		        lda door_tile,x         ; restore original tile type (colored)
   236 56C3 91 D4		        sta (ztptr),y
   237
   238 56C5 A9 00		        lda #0
   239 56C7 8D 5F D6		        sta VBXE_BANK_SEL
   240
   241 				        ; Mark dirty for redraw
   242 56CA AE DA 56		        ldx cd_idx
   243 56CD 20 A1 57		        jsr mark_door_dirty
   244
   245 				        ; Play close sound
   246 56D0 86 81		        stx zt2
   247 56D2 A2 17		        ldx #SFX_DOORCLS
   248 56D4 20 A2 5A		        jsr snd_play
   249 56D7 A6 81		        ldx zt2
   250 56D9 60			        rts
   251 56DA 00			cd_idx  dta 0
   252 				.endp
   253
   254 				; ============================================
   255 				; CHECK PLAYER NEAR DOOR
   256 				; Input: door_tmp_idx = door index
   257 				; Output: A != 0 if player is adjacent
   258 				; ============================================
   259 56DB			.proc check_player_near_door
   260 				        ; Player feet tile
   261 56DB A6 92		        ldx zpy
   262 56DD BD C6 A2		        lda pix_to_tile,x
   263 56E0 8D 47 57		        sta cpn_prow            ; player row (feet)
   264
   265 				        ; Player center col (16-bit)
   266 56E3 A5 91		        lda zpx
   267 56E5 18			        clc
   268 56E6 69 08		        adc #8
   269 56E8 85 80		        sta zt
   270 56EA A5 9C		        lda zpx_hi
   271 56EC 69 00		        adc #0
   272 56EE 0A			        asl
   273 56EF 0A			        asl
   274 56F0 0A			        asl
   275 56F1 0A			        asl
   276 56F2 8D 46 57		        sta cpn_pcol
   277 56F5 A5 80		        lda zt
   278 56F7 4A			        lsr
   279 56F8 4A			        lsr
   280 56F9 4A			        lsr
   281 56FA 4A			        lsr
   282 56FB 0D 46 57		        ora cpn_pcol
   283 56FE 8D 46 57		        sta cpn_pcol            ; player column
   284
   285 5701 AE BF 57		        ldx door_tmp_idx
   286 				        ; Check column: player must be in same col or adjacent (±1)
   287 5704 AD 46 57		        lda cpn_pcol
   288 5707 38			        sec
   289 5708 FD 5F 55		        sbc door_col,x
   290 570B 10 05		        bpl ?cp1
   291 570D 49 FF		        eor #$FF
   292 570F 18			        clc
   293 5710 69 01		        adc #1
   294 5712 C9 02		?cp1    cmp #2                  ; within 2 columns
   295 5714 B0 2A		        bcs ?no
   296
   297 				        ; Check row: player feet or head must be at door row
   298 				        ; Head row = (zpy - 16) / 16
   299 5716 A5 92		        lda zpy
   300 5718 38			        sec
   301 5719 E9 10		        sbc #16
   302 571B AA			        tax
   303 571C BD C6 A2		        lda pix_to_tile,x
   304 571F 8D 48 57		        sta cpn_hrow            ; player head row
   305
   306 5722 AE BF 57		        ldx door_tmp_idx
   307 5725 AD 47 57		        lda cpn_prow
   308 5728 DD 67 55		        cmp door_row,x
   309 572B F0 16		        beq ?yes
   310 572D AD 48 57		        lda cpn_hrow
   311 5730 DD 67 55		        cmp door_row,x
   312 5733 F0 0E		        beq ?yes
   313 				        ; Also check row+1 (door might be above player)
   314 5735 BD 67 55		        lda door_row,x
   315 5738 18			        clc
   316 5739 69 01		        adc #1
   317 573B CD 47 57		        cmp cpn_prow
   318 573E F0 03		        beq ?yes
   319 5740 A9 00		?no     lda #0
   320 5742 60			        rts
   321 5743 A9 01		?yes    lda #1
   322 5745 60			        rts
   323 5746 00			cpn_pcol dta 0
   324 5747 00			cpn_prow dta 0
   325 5748 00			cpn_hrow dta 0
   326 				.endp
   327
   328 				; ============================================
   329 				; CHECK PLAYER IN DOOR (for close safety)
   330 				; Input: door_tmp_idx = door index
   331 				; Output: A != 0 if player occupies door tile
   332 				; ============================================
   333 5749			.proc check_player_in_door
   334 				        ; Player center col
   335 5749 A5 91		        lda zpx
   336 574B 18			        clc
   337 574C 69 08		        adc #8
   338 574E 85 80		        sta zt
   339 5750 A5 9C		        lda zpx_hi
   340 5752 69 00		        adc #0
   341 5754 0A			        asl
   342 5755 0A			        asl
   343 5756 0A			        asl
   344 5757 0A			        asl
   345 5758 8D 9E 57		        sta cpid_pcol
   346 575B A5 80		        lda zt
   347 575D 4A			        lsr
   348 575E 4A			        lsr
   349 575F 4A			        lsr
   350 5760 4A			        lsr
   351 5761 0D 9E 57		        ora cpid_pcol
   352 5764 8D 9E 57		        sta cpid_pcol
   353
   354 5767 AE BF 57		        ldx door_tmp_idx
   355 576A AD 9E 57		        lda cpid_pcol
   356 576D DD 5F 55		        cmp door_col,x
   357 5770 D0 26		        bne ?no
   358
   359 				        ; Same column — check Y overlap
   360 				        ; Player occupies rows from (zpy-24)/16 to zpy/16
   361 5772 A6 92		        ldx zpy
   362 5774 BD C6 A2		        lda pix_to_tile,x
   363 5777 8D 9F 57		        sta cpid_frow
   364 577A A5 92		        lda zpy
   365 577C 38			        sec
   366 577D E9 18		        sbc #24
   367 577F AA			        tax
   368 5780 BD C6 A2		        lda pix_to_tile,x
   369 5783 8D A0 57		        sta cpid_hrow
   370
   371 5786 AE BF 57		        ldx door_tmp_idx
   372 5789 BD 67 55		        lda door_row,x
   373 578C CD A0 57		        cmp cpid_hrow
   374 578F 90 07		        bcc ?no                 ; door above player head
   375 5791 CD 9F 57		        cmp cpid_frow
   376 5794 F0 05		        beq ?yes
   377 5796 90 03		        bcc ?yes                ; door row between head and feet
   378 5798 A9 00		?no     lda #0
   379 579A 60			        rts
   380 579B A9 01		?yes    lda #1
   381 579D 60			        rts
   382 579E 00			cpid_pcol dta 0
   383 579F 00			cpid_frow dta 0
   384 57A0 00			cpid_hrow dta 0
   385 				.endp
   386
   387 				; ============================================
   388 				; MARK DOOR TILE AS DIRTY
   389 				; Input: X = door index (preserved)
   390 				; ============================================
   391 57A1			.proc mark_door_dirty
   392 57A1 8E BE 57		        stx mdd_idx
   393 				        ; Mark dirty in BOTH buffers (double buffering)
   394 57A4 AE BE 57		        ldx mdd_idx
   395 57A7 BC 67 55		        ldy door_row,x
   396 57AA B9 06 A4		        lda row_x20,y
   397 57AD 18			        clc
   398 57AE 7D 5F 55		        adc door_col,x
   399 57B1 A8			        tay
   400 57B2 A9 01		        lda #1
   401 57B4 99 91 51		        sta dirty_0,y
   402 57B7 99 81 52		        sta dirty_1,y
   403 57BA AE BE 57		        ldx mdd_idx
   404 57BD 60			        rts
   405 57BE 00			mdd_idx dta 0
   406 				.endp
   407
   408 				; Shared temp vars for door routines
   409 57BF 00			door_tmp_idx    dta 0
   410
   411 				; Door key data (filled by map2bin.py, MAX_DOORS entries)
   412 57C0			door_key_data
   413 57C0 00 00 00 00 00 00 +         :MAX_DOORS dta 0
   414
   415 				; ============================================
   416 				; SWITCH SYSTEM
   417 				; ============================================
   418 57C8 00			level_complete  dta 0           ; 1 = switch activated, level done
   419
   420 				; Switch found position (saved by search routine)
   421 57C9 00			sw_found_col    dta 0
   422 57CA 00			sw_found_row    dta 0
   423
   424 				; TRY USE SWITCH - check tiles around player for switch (OFF or ON)
   425 				; Called when USE pressed and no door found
   426 57CB			.proc try_use_switch
   427 				        ; Check right side: player center X + 16
   428 57CB A5 91		        lda zpx
   429 57CD 18			        clc
   430 57CE 69 10		        adc #16
   431 57D0 8D B0 25		        sta gt_px
   432 57D3 A5 9C		        lda zpx_hi
   433 57D5 69 00		        adc #0
   434 57D7 8D B1 25		        sta gt_px_hi
   435 57DA A5 92		        lda zpy
   436 57DC 8D B2 25		        sta gt_py
   437 57DF 20 63 25		        jsr get_tile_at
   438 57E2 C9 1C		        cmp #TILE_SWITCH_OFF
   439 57E4 D0 03		        bne ?r1
   440 57E6 4C AD 58		        jmp ?found_off
   441 57E9 C9 1D		?r1     cmp #TILE_SWITCH_ON
   442 57EB D0 03		        bne ?r2
   443 57ED 4C B8 58		        jmp ?found_on
   444 57F0			?r2     ; Check left side: player center X - 8
   445 57F0 A5 91		        lda zpx
   446 57F2 38			        sec
   447 57F3 E9 08		        sbc #8
   448 57F5 8D B0 25		        sta gt_px
   449 57F8 A5 9C		        lda zpx_hi
   450 57FA E9 00		        sbc #0
   451 57FC 8D B1 25		        sta gt_px_hi
   452 57FF A5 92		        lda zpy
   453 5801 8D B2 25		        sta gt_py
   454 5804 20 63 25		        jsr get_tile_at
   455 5807 C9 1C		        cmp #TILE_SWITCH_OFF
   456 5809 D0 03		        bne ?l1
   457 580B 4C AD 58		        jmp ?found_off
   458 580E C9 1D		?l1     cmp #TILE_SWITCH_ON
   459 5810 D0 03		        bne ?l2
   460 5812 4C B8 58		        jmp ?found_on
   461 5815			?l2     ; Check head height right
   462 5815 A5 91		        lda zpx
   463 5817 18			        clc
   464 5818 69 10		        adc #16
   465 581A 8D B0 25		        sta gt_px
   466 581D A5 9C		        lda zpx_hi
   467 581F 69 00		        adc #0
   468 5821 8D B1 25		        sta gt_px_hi
   469 5824 A5 92		        lda zpy
   470 5826 38			        sec
   471 5827 E9 10		        sbc #16
   472 5829 8D B2 25		        sta gt_py
   473 582C 20 63 25		        jsr get_tile_at
   474 582F C9 1C		        cmp #TILE_SWITCH_OFF
   475 5831 D0 03		        bne ?hr1
   476 5833 4C AD 58		        jmp ?found_off
   477 5836 C9 1D		?hr1    cmp #TILE_SWITCH_ON
   478 5838 D0 03		        bne ?hr2
   479 583A 4C B8 58		        jmp ?found_on
   480 583D			?hr2    ; Check head height left
   481 583D A5 91		        lda zpx
   482 583F 38			        sec
   483 5840 E9 08		        sbc #8
   484 5842 8D B0 25		        sta gt_px
   485 5845 A5 9C		        lda zpx_hi
   486 5847 E9 00		        sbc #0
   487 5849 8D B1 25		        sta gt_px_hi
   488 584C A5 92		        lda zpy
   489 584E 38			        sec
   490 584F E9 10		        sbc #16
   491 5851 8D B2 25		        sta gt_py
   492 5854 20 63 25		        jsr get_tile_at
   493 5857 C9 1C		        cmp #TILE_SWITCH_OFF
   494 5859 D0 03		        bne ?hl1
   495 585B 4C AD 58		        jmp ?found_off
   496 585E C9 1D		?hl1    cmp #TILE_SWITCH_ON
   497 5860 D0 03		        bne ?hl2
   498 5862 4C B8 58		        jmp ?found_on
   499 5865			?hl2    ; Check at player feet (standing on switch)
   500 5865 A5 91		        lda zpx
   501 5867 8D B0 25		        sta gt_px
   502 586A A5 9C		        lda zpx_hi
   503 586C 8D B1 25		        sta gt_px_hi
   504 586F A5 92		        lda zpy
   505 5871 8D B2 25		        sta gt_py
   506 5874 20 63 25		        jsr get_tile_at
   507 5877 C9 1C		        cmp #TILE_SWITCH_OFF
   508 5879 D0 03		        bne ?f1
   509 587B 4C AD 58		        jmp ?found_off
   510 587E C9 1D		?f1     cmp #TILE_SWITCH_ON
   511 5880 D0 03		        bne ?f2
   512 5882 4C B8 58		        jmp ?found_on
   513 5885			?f2     ; Check at player head (overlapping switch above)
   514 5885 A5 91		        lda zpx
   515 5887 8D B0 25		        sta gt_px
   516 588A A5 9C		        lda zpx_hi
   517 588C 8D B1 25		        sta gt_px_hi
   518 588F A5 92		        lda zpy
   519 5891 38			        sec
   520 5892 E9 10		        sbc #16
   521 5894 8D B2 25		        sta gt_py
   522 5897 20 63 25		        jsr get_tile_at
   523 589A C9 1C		        cmp #TILE_SWITCH_OFF
   524 589C D0 03		        bne ?nope
   525 589E 4C AD 58		        jmp ?found_off
   526 58A1 C9 1D		?nope   cmp #TILE_SWITCH_ON
   527 58A3 D0 03		        bne ?oof
   528 58A5 4C B8 58		        jmp ?found_on
   529 58A8			?oof    ; No switch found — play OOF
   530 58A8 A2 18		        ldx #SFX_OOF
   531 58AA 4C A2 5A		        jmp snd_play
   532 58AD			?found_off
   533 				        ; Toggle switch OFF -> ON
   534 58AD A0 00		        ldy #0
   535 58AF A9 1D		        lda #TILE_SWITCH_ON
   536 58B1 91 D4		        sta (ztptr),y
   537 58B3 A2 1C		        ldx #SFX_SWTCHN        ; switch ON sound
   538 58B5 4C C0 58		        jmp ?do_action
   539 58B8			?found_on
   540 				        ; Toggle switch ON -> OFF
   541 58B8 A0 00		        ldy #0
   542 58BA A9 1C		        lda #TILE_SWITCH_OFF
   543 58BC 91 D4		        sta (ztptr),y
   544 58BE A2 1D		        ldx #SFX_SWTCHX        ; switch OFF sound
   545 58C0			?do_action
   546 				        ; Save switch tile position
   547 58C0 AD B4 25		        lda gt_col
   548 58C3 8D C9 57		        sta sw_found_col
   549 58C6 AD B5 25		        lda gt_row
   550 58C9 8D CA 57		        sta sw_found_row
   551 				        ; Mark switch tile dirty
   552 58CC 8E DB 58		        stx sw_sfx_tmp
   553 58CF 20 DC 58		        jsr mark_pos_dirty
   554 				        ; Play switch sound
   555 58D2 AE DB 58		        ldx sw_sfx_tmp
   556 58D5 20 A2 5A		        jsr snd_play
   557 				        ; Look up switch in table and execute action
   558 58D8 4C 30 59		        jmp switch_do_target
   559 58DB 00			sw_sfx_tmp dta 0
   560 				.endp
   561
   562 				; ============================================
   563 				; MARK TILE AT (gt_col, gt_row) DIRTY
   564 				; ============================================
   565 58DC			.proc mark_pos_dirty
   566 58DC AC B5 25		        ldy gt_row
   567 58DF B9 06 A4		        lda row_x20,y
   568 58E2 18			        clc
   569 58E3 6D B4 25		        adc gt_col
   570 58E6 A8			        tay
   571 58E7 A9 01		        lda #1
   572 58E9 99 91 51		        sta dirty_0,y
   573 58EC 99 81 52		        sta dirty_1,y
   574 58EF 60			        rts
   575 				.endp
   576
   577 				; ============================================
   578 				; IS DOOR SWITCH-ONLY?
   579 				; Input: X = door index (door_col[X], door_row[X])
   580 				; Output: Z=1 if normal door, Z=0 if switch-only
   581 				; Preserves X (door index)
   582 				; ============================================
   583 58F0			.proc is_door_sw_only
   584 58F0 8E 2F 59		        stx idsw_door
   585 58F3 A0 00		        ldy #0
   586 58F5 CC 70 A5		?lp     cpy num_switches
   587 58F8 B0 2F		        bcs ?no
   588 58FA B9 81 A5		        lda sw_action,y
   589 58FD C9 00		        cmp #SW_ACT_DOOR
   590 58FF F0 0B		        beq ?chk
   591 5901 C9 03		        cmp #SW_ACT_DOOR_LOCK
   592 5903 F0 07		        beq ?chk
   593 5905 C9 04		        cmp #SW_ACT_FLOOR
   594 5907 F0 03		        beq ?chk
   595 5909 4C 25 59		        jmp ?nx
   596 590C AE 2F 59		?chk    ldx idsw_door
   597 590F B9 79 A5		        lda sw_tgt_col,y
   598 5912 DD 5F 55		        cmp door_col,x
   599 5915 D0 0E		        bne ?nx
   600 5917 B9 7D A5		        lda sw_tgt_row,y
   601 591A DD 67 55		        cmp door_row,x
   602 591D D0 06		        bne ?nx
   603 				        ; Match — always locked
   604 591F AE 2F 59		        ldx idsw_door
   605 5922 A9 01		        lda #1              ; Z=0 locked
   606 5924 60			        rts
   607 5925 C8			?nx     iny
   608 5926 4C F5 58		        jmp ?lp
   609 5929 AE 2F 59		?no     ldx idsw_door
   610 592C A9 00		        lda #0              ; Z=1 can open
   611 592E 60			        rts
   612 592F 00			idsw_door dta 0
   613 				.endp
   614
   615 				; ============================================
   616 				; SWITCH TARGET LOOKUP + EXECUTE
   617 				; Find switch at (sw_found_col, sw_found_row) in table,
   618 				; then execute action at target tile.
   619 				; ============================================
   620 5930			.proc switch_do_target
   621 5930 A2 00		        ldx #0
   622 5932 EC 70 A5		?lp     cpx num_switches
   623 5935 B0 16		        bcs ?no_match
   624 5937 BD 71 A5		        lda sw_col,x
   625 593A CD C9 57		        cmp sw_found_col
   626 593D D0 0B		        bne ?next
   627 593F BD 75 A5		        lda sw_row,x
   628 5942 CD CA 57		        cmp sw_found_row
   629 5945 D0 03		        bne ?next
   630 				        ; Found match at index X
   631 5947 4C 4E 59		        jmp ?exec
   632 594A E8			?next   inx
   633 594B D0 E5		        bne ?lp             ; always branches (max 4)
   634 594D			?no_match
   635 				        ; No linked target — just toggle (legacy behavior)
   636 594D 60			        rts
   637 594E			?exec
   638 				        ; Load target position into r_col/r_row
   639 594E BD 79 A5		        lda sw_tgt_col,x
   640 5951 8D 35 47		        sta r_col
   641 5954 8D B4 25		        sta gt_col
   642 5957 BD 7D A5		        lda sw_tgt_row,x
   643 595A 8D 34 47		        sta r_row
   644 595D 8D B5 25		        sta gt_row
   645 				        ; Save action type
   646 5960 BD 81 A5		        lda sw_action,x
   647 5963 48			        pha
   648 				        ; Calculate map pointer to target tile
   649 5964 20 B6 25		        jsr calc_map_ptr
   650 				        ; Get action type
   651 5967 68			        pla
   652 				        ; Dispatch action
   653 5968 C9 00		        cmp #SW_ACT_DOOR
   654 596A F0 09		        beq ?act_door
   655 596C C9 03		        cmp #SW_ACT_DOOR_LOCK
   656 596E F0 05		        beq ?act_door
   657 5970 C9 01		        cmp #SW_ACT_WALL
   658 5972 F0 20		        beq ?act_wall
   659 				        ; Default / SW_ACT_ELEV: future
   660 5974 60			        rts
   661 5975			?act_door
   662 				        ; Find door at target position in door table and open it
   663 5975 A2 00		        ldx #0
   664 5977 EC 8F 55		?dlp    cpx num_doors
   665 597A B0 17		        bcs ?dno        ; door not found, bail
   666 597C BD 5F 55		        lda door_col,x
   667 597F CD B4 25		        cmp gt_col
   668 5982 D0 0B		        bne ?dnx
   669 5984 BD 67 55		        lda door_row,x
   670 5987 CD B5 25		        cmp gt_row
   671 598A D0 03		        bne ?dnx
   672 				        ; Found door at index X — open via door system (timer + auto-close)
   673 598C 4C 24 56		        jmp open_door
   674 598F E8			?dnx    inx
   675 5990 4C 77 59		        jmp ?dlp
   676 5993 60			?dno    rts
   677 5994			?act_wall
   678 				        ; Remove wall: set target tile to empty (0)
   679 5994 A0 00		        ldy #0
   680 5996 A9 00		        lda #0
   681 5998 91 D4		        sta (ztptr),y
   682 				        ; Mark target dirty
   683 599A 20 DC 58		        jsr mark_pos_dirty
   684 				        ; Play switch sound
   685 599D A2 1D		        ldx #SFX_SWTCHX
   686 599F 4C A2 5A		        jmp snd_play
   687 				.endp
   688
   689 				; ============================================
   690 				; CHECK FLOOR TRIGGERS
   691 				; Called every frame. If player moved to a new tile,
   692 				; check if that tile is a trigger (linked empty tile).
   693 				; ============================================
   694 59A2 FF			ft_prev_col dta $FF             ; previous player tile col
   695 59A3 FF			ft_prev_row dta $FF             ; previous player tile row
   696
   697 59A4			.proc check_floor_triggers
   698 				        ; Compute current player tile col
   699 59A4 20 E5 25		        jsr get_player_tile_col
   700 59A7 8D F3 59		        sta ft_cur_col
   701 				        ; Player feet row
   702 59AA A5 92		        lda zpy
   703 59AC 4A			        lsr
   704 59AD 4A			        lsr
   705 59AE 4A			        lsr
   706 59AF 4A			        lsr
   707 59B0 8D F4 59		        sta ft_cur_row
   708 				        ; Did player move to a new tile?
   709 59B3 AD F3 59		        lda ft_cur_col
   710 59B6 CD A2 59		        cmp ft_prev_col
   711 59B9 D0 08		        bne ?moved
   712 59BB AD F4 59		        lda ft_cur_row
   713 59BE CD A3 59		        cmp ft_prev_row
   714 59C1 F0 2F		        beq ?done           ; same tile = skip
   715 59C3			?moved  ; Update previous position
   716 59C3 AD F3 59		        lda ft_cur_col
   717 59C6 8D A2 59		        sta ft_prev_col
   718 59C9 AD F4 59		        lda ft_cur_row
   719 59CC 8D A3 59		        sta ft_prev_row
   720 				        ; Check feet row
   721 59CF AD F3 59		        lda ft_cur_col
   722 59D2 8D C9 57		        sta sw_found_col
   723 59D5 AD F4 59		        lda ft_cur_row
   724 59D8 8D CA 57		        sta sw_found_row
   725 59DB 20 F5 59		        jsr ft_check_table
   726 				        ; Also check body row (feet - 1)
   727 59DE AD F4 59		        lda ft_cur_row
   728 59E1 F0 0F		        beq ?done
   729 59E3 38			        sec
   730 59E4 E9 01		        sbc #1
   731 59E6 8D CA 57		        sta sw_found_row
   732 59E9 AD F3 59		        lda ft_cur_col
   733 59EC 8D C9 57		        sta sw_found_col
   734 59EF 20 F5 59		        jsr ft_check_table
   735 59F2 60			?done   rts
   736 59F3 00			ft_cur_col dta 0
   737 59F4 00			ft_cur_row dta 0
   738 				.endp
   739
   740 				; Find floor trigger at (sw_found_col, sw_found_row) — only SW_ACT_FLOOR
   741 59F5			.proc ft_check_table
   742 59F5 A2 00		        ldx #0
   743 59F7 EC 70 A5		?lp     cpx num_switches
   744 59FA B0 53		        bcs ?no
   745 59FC BD 81 A5		        lda sw_action,x
   746 59FF C9 04		        cmp #SW_ACT_FLOOR
   747 5A01 D0 48		        bne ?nx
   748 5A03 BD 71 A5		        lda sw_col,x
   749 5A06 CD C9 57		        cmp sw_found_col
   750 5A09 D0 40		        bne ?nx
   751 5A0B BD 75 A5		        lda sw_row,x
   752 5A0E CD CA 57		        cmp sw_found_row
   753 5A11 D0 38		        bne ?nx
   754 				        ; Match — execute target action (open door)
   755 5A13 BD 79 A5		        lda sw_tgt_col,x
   756 5A16 8D 35 47		        sta r_col
   757 5A19 8D B4 25		        sta gt_col
   758 5A1C BD 7D A5		        lda sw_tgt_row,x
   759 5A1F 8D 34 47		        sta r_row
   760 5A22 8D B5 25		        sta gt_row
   761 5A25 20 B6 25		        jsr calc_map_ptr
   762 				        ; Find door and open it
   763 5A28 A2 00		        ldx #0
   764 5A2A EC 8F 55		?dlp    cpx num_doors
   765 5A2D B0 20		        bcs ?no
   766 5A2F BD 5F 55		        lda door_col,x
   767 5A32 CD B4 25		        cmp gt_col
   768 5A35 D0 10		        bne ?dnx
   769 5A37 BD 67 55		        lda door_row,x
   770 5A3A CD B5 25		        cmp gt_row
   771 5A3D D0 08		        bne ?dnx
   772 				        ; Only open if closed
   773 5A3F BD 6F 55		        lda door_state,x
   774 5A42 D0 0B		        bne ?no             ; already open, skip
   775 5A44 4C 24 56		        jmp open_door
   776 5A47 E8			?dnx    inx
   777 5A48 4C 2A 5A		        jmp ?dlp
   778 5A4B E8			?nx     inx
   779 5A4C 4C F7 59		        jmp ?lp
   780 5A4F 60			?no     rts
   781 				.endp
   325 5A50			        icl 'sound.asm'
Source: sound.asm
     1 				;==============================================
     2 				; DOOM2D - Digital sample playback engine (VRAM via MEMAC-B)
     3 				; sound.asm
     4 				;
     5 				; Sounds in VRAM, read via MEMAC-B ($4000-$7FFF window).
     6 				; MEMAC-B is INDEPENDENT from MEMAC-A (map/blitter).
     7 				; No BANK_SEL conflicts, no buffer needed.
     8 				;==============================================
     9
    10 				; Sound state: snd_active/snd_cur_byte/snd_lock/snd_bank moved to ZP ($C9-$CC)
    11
    12 				; Saved original VIMIRQ
    13 5A50 00 00		old_iir         dta a(0)
    14 5A52 01			snd_enabled     dta 1               ; 1=sounds on, 0=sounds off
    15
    16 				; ============================================
    17 				; SND_INIT
    18 				; ============================================
    19 5A53			.proc snd_init
    20 5A53 A9 00		        lda #0
    21 5A55 8D 0F D2		        sta SKCTL
    22 5A58 EA			        nop
    23 5A59 EA			        nop
    24 5A5A A9 03		        lda #3
    25 5A5C 8D 0F D2		        sta SKCTL
    26
    27 5A5F A9 00		        lda #0
    28 5A61 8D 08 D2		        sta AUDCTL
    29 5A64 8D 01 D2		        sta AUDC1
    30 5A67 8D 03 D2		        sta AUDC2
    31 5A6A 8D 05 D2		        sta $D205
    32 5A6D 8D 07 D2		        sta AUDC4
    33
    34 5A70 A9 0F		        lda #15
    35 5A72 8D 00 D2		        sta AUDF1
    36
    37 5A75 A9 00		        lda #0
    38 5A77 85 C9		        sta snd_active
    39 5A79 85 CA		        sta snd_cur_byte
    40 5A7B 85 CB		        sta snd_lock
    41 5A7D 85 CC		        sta snd_bank
    42 5A7F 85 C8		        sta snd_phase
    43 5A81 8D 5D D6		        sta VBXE_MEMAC_B
    44 5A84 A9 FF		        lda #$FF
    45 5A86 8D E5 5A		        sta snd_queue
    46
    47 5A89 78			        sei
    48 5A8A AD 16 02		        lda $0216
    49 5A8D 8D 50 5A		        sta old_iir
    50 5A90 AD 17 02		        lda $0217
    51 5A93 8D 51 5A		        sta old_iir+1
    52 5A96 A9 09		        lda #<snd_irq
    53 5A98 8D 16 02		        sta $0216
    54 5A9B A9 5B		        lda #>snd_irq
    55 5A9D 8D 17 02		        sta $0217
    56 5AA0 58			        cli
    57 5AA1 60			        rts
    58 				.endp
    59
    60 				; ============================================
    61 				; SND_PLAY - Start playing a sound from VRAM
    62 				; Input: X = SFX index (0-13)
    63 				; ============================================
    64 5AA2			.proc snd_play
    65 5AA2 AD 52 5A		        lda snd_enabled
    66 5AA5 F0 3C		        beq ?skip
    67 5AA7 A5 CB		        lda snd_lock
    68 5AA9 D0 38		        bne ?skip
    69
    70 5AAB 78			        sei
    71 5AAC BD 2B 5C		        lda sfx_vptr_lo,x
    72 5AAF 85 C4		        sta snd_ptr
    73 5AB1 BD 4B 5C		        lda sfx_vptr_hi,x
    74 5AB4 85 C5		        sta snd_ptr+1
    75 5AB6 BD 0B 5C		        lda sfx_vbank,x
    76 5AB9 85 CC		        sta snd_bank
    77
    78 5ABB BD 8B 5C		        lda sfx_vend_lo,x
    79 5ABE 85 C6		        sta snd_end
    80 5AC0 BD AB 5C		        lda sfx_vend_hi,x
    81 5AC3 85 C7		        sta snd_end+1
    82 5AC5 BD 6B 5C		        lda sfx_vbank_end,x
    83 5AC8 8D E4 5A		        sta snd_end_bank
    84
    85 5ACB A9 00		        lda #0
    86 5ACD 85 C8		        sta snd_phase
    87 				        ; Pre-read first byte (hi nibble IRQ won't read)
    88 5ACF 20 10 06		        jsr snd_memac_read
    89 5AD2 A9 01		        lda #1
    90 5AD4 85 C9		        sta snd_active
    91
    92 5AD6 A5 10		        lda POKMSK
    93 5AD8 09 01		        ora #$01
    94 5ADA 85 10		        sta POKMSK
    95 5ADC 8D 0E D2		        sta IRQEN
    96 5ADF 8D 09 D2		        sta STIMER
    97 5AE2 58			        cli
    98 5AE3 60			?skip   rts
    99
   100 5AE4 00			snd_end_bank dta 0
   101 				.endp
   102
   103 				; Sound queue: plays next sound when lock expires
   104 5AE5 FF			snd_queue       dta $FF         ; $FF = empty, else SFX index
   105 5AE6 00			snd_queue_lock  dta 0           ; lock value for queued sound
   106
   107 				; ============================================
   108 				; SOUND UPDATE - decrement priority lock + play queue
   109 				; ============================================
   110 5AE7			.proc sound_update
   111 5AE7 A5 CB		        lda snd_lock
   112 5AE9 F0 04		        beq ?chk_queue
   113 5AEB C6 CB		        dec snd_lock
   114 5AED D0 19		        bne ?done
   115 5AEF			?chk_queue
   116 				        ; Lock expired — check queue
   117 5AEF AD E5 5A		        lda snd_queue
   118 5AF2 C9 FF		        cmp #$FF
   119 5AF4 F0 12		        beq ?done
   120 				        ; Play queued sound
   121 5AF6 AA			        tax
   122 5AF7 A9 FF		        lda #$FF
   123 5AF9 8D E5 5A		        sta snd_queue           ; clear queue
   124 5AFC A9 00		        lda #0
   125 5AFE 85 CB		        sta snd_lock
   126 5B00 20 A2 5A		        jsr snd_play
   127 5B03 AD E6 5A		        lda snd_queue_lock
   128 5B06 85 CB		        sta snd_lock
   129 5B08 60			?done   rts
   130 				.endp
   131
   132 				; ============================================
   133 				; IRQ HANDLER (MEMAC-B)
   134 				; ~65 cycles, no BANK_SEL conflict with graphics
   135 				; ============================================
   136 5B09			.proc snd_irq
   137 5B09 48			        pha
   138
   139 5B0A AD 0E D2		        lda IRQEN
   140 5B0D 29 01		        and #$01
   141 5B0F F0 03		        beq ?is_ours
   142 5B11 4C 8D 5B		        jmp ?not_ours
   143 5B14			?is_ours
   144 5B14 A5 10		        lda POKMSK
   145 5B16 29 FE		        and #$FE
   146 5B18 8D 0E D2		        sta IRQEN
   147 5B1B A5 10		        lda POKMSK
   148 5B1D 8D 0E D2		        sta IRQEN
   149
   150 5B20 A5 C9		        lda snd_active
   151 5B22 F0 5E		        beq ?silent
   152
   153 5B24 A5 C8		        lda snd_phase
   154 5B26 D0 0F		        bne ?lo
   155
   156 				        ; --- Hi nibble: just output (no VRAM read = fast!) ---
   157 5B28 A5 CA		        lda snd_cur_byte
   158 5B2A 4A			        lsr
   159 5B2B 4A			        lsr
   160 5B2C 4A			        lsr
   161 5B2D 4A			        lsr
   162 5B2E 09 10		        ora #$10
   163 5B30 8D 07 D2		        sta AUDC4
   164 5B33 E6 C8		        inc snd_phase          ; 0 → 1
   165 5B35 68			        pla
   166 5B36 40			        rti
   167
   168 5B37			?lo     ; --- Lo nibble: output + advance + read next byte ---
   169 5B37 A5 CA		        lda snd_cur_byte
   170 5B39 29 0F		        and #$0F
   171 5B3B 09 10		        ora #$10
   172 5B3D 8D 07 D2		        sta AUDC4
   173 5B40 C6 C8		        dec snd_phase          ; 1 → 0
   174
   175 5B42 E6 C4		        inc snd_ptr
   176 5B44 D0 0E		        bne ?nc
   177 5B46 E6 C5		        inc snd_ptr+1
   178 5B48 A5 C5		        lda snd_ptr+1
   179 5B4A C9 80		        cmp #$80
   180 5B4C D0 06		        bne ?nc
   181 5B4E A9 40		        lda #$40
   182 5B50 85 C5		        sta snd_ptr+1
   183 5B52 E6 CC		        inc snd_bank
   184 5B54			?nc
   185 				        ; End check
   186 5B54 A5 CC		        lda snd_bank
   187 5B56 CD E4 5A		        cmp snd_play.snd_end_bank
   188 5B59 D0 1E		        bne ?read
   189 5B5B A5 C5		        lda snd_ptr+1
   190 5B5D C5 C7		        cmp snd_end+1
   191 5B5F D0 18		        bne ?read
   192 5B61 A5 C4		        lda snd_ptr
   193 5B63 C5 C6		        cmp snd_end
   194 5B65 D0 12		        bne ?read
   195
   196 				        ; Sound finished
   197 5B67 A9 00		        lda #0
   198 5B69 85 C9		        sta snd_active
   199 5B6B 8D 07 D2		        sta AUDC4
   200 5B6E A5 10		        lda POKMSK
   201 5B70 29 FE		        and #$FE
   202 5B72 85 10		        sta POKMSK
   203 5B74 8D 0E D2		        sta IRQEN
   204 5B77 68			        pla
   205 5B78 40			        rti
   206
   207 5B79			?read   ; Pre-read next byte via trampoline (for next hi nibble)
   208 5B79 84 CD		        sty snd_save_y
   209 5B7B 20 10 06		        jsr snd_memac_read
   210 5B7E A4 CD		        ldy snd_save_y
   211 5B80 68			        pla
   212 5B81 40			        rti
   213
   214 5B82 A5 10		?silent lda POKMSK
   215 5B84 29 FE		        and #$FE
   216 5B86 85 10		        sta POKMSK
   217 5B88 8D 0E D2		        sta IRQEN
   218 5B8B 68			        pla
   219 5B8C 40			        rti
   220
   221 5B8D			?not_ours
   222 5B8D 68			        pla
   223 5B8E 6C 50 5A		        jmp (old_iir)
   224 				.endp
   225
   226 				; ============================================
   227 				; PLAY ENEMY DEATH SOUND
   228 				; ============================================
   229 5B91			.proc play_enemy_death
   230 5B91 BD 6D 3A		        lda en_type,x
   231 5B94 A8			        tay
   232 5B95 B9 AB 5E		        lda en_death_sfx,y
   233 5B98 C9 FF		        cmp #$FF
   234 5B9A F0 21		        beq ?skip
   235 				        ; If sound locked, queue instead of dropping
   236 5B9C 86 81		        stx zt2
   237 5B9E A5 CB		        lda snd_lock
   238 5BA0 F0 0E		        beq ?play_now
   239 				        ; Queue death sound for later
   240 5BA2 B9 AB 5E		        lda en_death_sfx,y
   241 5BA5 8D E5 5A		        sta snd_queue
   242 5BA8 A9 08		        lda #8
   243 5BAA 8D E6 5A		        sta snd_queue_lock
   244 5BAD A6 81		        ldx zt2
   245 5BAF 60			        rts
   246 5BB0			?play_now
   247 5BB0 B9 AB 5E		        lda en_death_sfx,y
   248 5BB3 AA			        tax
   249 5BB4 20 A2 5A		        jsr snd_play
   250 5BB7 A9 08		        lda #8
   251 5BB9 85 CB		        sta snd_lock
   252 5BBB A6 81		        ldx zt2
   253 5BBD 60			?skip   rts
   254 				.endp
   255
   256 				; ============================================
   257 				; PLAY ENEMY SIGHT SOUND
   258 				; ============================================
   259 5BBE			.proc play_enemy_sight
   260 5BBE BD 6D 3A		        lda en_type,x
   261 5BC1 A8			        tay
   262 5BC2 B9 B7 5E		        lda en_sight_sfx,y
   263 5BC5 C9 FF		        cmp #$FF
   264 5BC7 F0 41		        beq ?skip
   265 5BC9 86 81		        stx zt2
   266 5BCB A9 00		        lda #0
   267 5BCD 85 CB		        sta snd_lock
   268 				        ; Zombie/shotgun: randomly pick 1 of 3 sight sounds
   269 5BCF BD 6D 3A		        lda en_type,x
   270 5BD2 C9 00		        cmp #EN_ZOMBIE
   271 5BD4 F0 0A		        beq ?rnd_posit
   272 5BD6 C9 04		        cmp #EN_SHOTGUN
   273 5BD8 F0 06		        beq ?rnd_posit
   274 				        ; Other enemies: use table directly
   275 5BDA B9 B7 5E		        lda en_sight_sfx,y
   276 5BDD 4C 00 5C		        jmp ?play
   277 5BE0			?rnd_posit
   278 				        ; Random 0-2 from frame counter
   279 5BE0 A5 90		        lda zfr
   280 5BE2 45 14		        eor RTCLOK3
   281 5BE4 29 03		        and #$03
   282 5BE6 C9 03		        cmp #3
   283 5BE8 D0 02		        bne ?rok
   284 5BEA A9 00		        lda #0             ; 3 -> 0 (keep range 0-2)
   285 5BEC C9 00		?rok    cmp #0
   286 5BEE D0 05		        bne ?p2
   287 5BF0 A9 04		        lda #SFX_POSIGHT
   288 5BF2 4C 00 5C		        jmp ?play
   289 5BF5 C9 01		?p2     cmp #1
   290 5BF7 D0 05		        bne ?p3
   291 5BF9 A9 19		        lda #SFX_POSIGHT2
   292 5BFB 4C 00 5C		        jmp ?play
   293 5BFE A9 1A		?p3     lda #SFX_POSIGHT3
   294 5C00 AA			?play   tax
   295 5C01 20 A2 5A		        jsr snd_play
   296 5C04 A9 08		        lda #8
   297 5C06 85 CB		        sta snd_lock
   298 5C08 A6 81		        ldx zt2
   299 5C0A 60			?skip   rts
   300 				.endp
   301
   302 				; ============================================
   303 				; VRAM SOUND ADDRESS TABLES (auto-generated)
   304 				; ============================================
   305 5C0B			        icl '../data/sound_tables.asm'
Source: sound_tables.asm
     1 				; Auto-generated sound VRAM address tables
     2 				; 32 sounds, 50318 bytes total
     3 				; VRAM $061000-$06D48D
     4
     5 				; Start bank (with BANK_EN)
     6 5C0B			sfx_vbank
     7 5C0B D8			        dta $D8  ; SFX_PISTOL (pistol)
     8 5C0C D8			        dta $D8  ; SFX_ITEMUP (itemup)
     9 5C0D D8			        dta $D8  ; SFX_ROCKET (rocket)
    10 5C0E D8			        dta $D8  ; SFX_PODEATH (podeath)
    11 5C0F D8			        dta $D8  ; SFX_POSIGHT (posit)
    12 5C10 D8			        dta $D8  ; SFX_IMPSIGHT (impsit)
    13 5C11 D8			        dta $D8  ; SFX_IMPDEATH (impdeath)
    14 5C12 D8			        dta $D8  ; SFX_SHOTGUN (shotgun)
    15 5C13 D8			        dta $D8  ; SFX_WPNUP (wpnup)
    16 5C14 D8			        dta $D8  ; SFX_PUNCH (punch)
    17 5C15 D8			        dta $D8  ; SFX_BAREXP (barexp)
    18 5C16 D9			        dta $D9  ; SFX_SLOP (slop)
    19 5C17 D9			        dta $D9  ; SFX_BRSSIT (brssit)
    20 5C18 D9			        dta $D9  ; SFX_BRSDTH (brsdth)
    21 5C19 D9			        dta $D9  ; SFX_SGTSIT (sgtsit)
    22 5C1A D9			        dta $D9  ; SFX_SGTDTH (sgtdth)
    23 5C1B D9			        dta $D9  ; SFX_CACSIT (cacsit)
    24 5C1C D9			        dta $D9  ; SFX_CACDTH (cacdth)
    25 5C1D D9			        dta $D9  ; SFX_PLASMA (plasma)
    26 5C1E D9			        dta $D9  ; SFX_BFG (bfg)
    27 5C1F DA			        dta $DA  ; SFX_BFGXPL (bfgxpl)
    28 5C20 DA			        dta $DA  ; SFX_PLDEATH (pldeath)
    29 5C21 DA			        dta $DA  ; SFX_DOOROPN (dooropn)
    30 5C22 DA			        dta $DA  ; SFX_DOORCLS (doorcls)
    31 5C23 DA			        dta $DA  ; SFX_OOF (oof)
    32 5C24 DA			        dta $DA  ; SFX_POSIGHT2 (posit2)
    33 5C25 DA			        dta $DA  ; SFX_POSIGHT3 (posit3)
    34 5C26 DA			        dta $DA  ; SFX_FIRSHT (firsht)
    35 5C27 DB			        dta $DB  ; SFX_SWTCHN (swtchn)
    36 5C28 DB			        dta $DB  ; SFX_SWTCHX (swtchx)
    37 5C29 DB			        dta $DB  ; SFX_CLAW (claw)
    38 5C2A DB			        dta $DB  ; SFX_SGTATK (sgtatk)
    39
    40 				; Start pointer lo (within MEMAC $9000 window)
    41 5C2B			sfx_vptr_lo
    42 5C2B 00			        dta $00  ; SFX_PISTOL
    43 5C2C 1E			        dta $1E  ; SFX_ITEMUP
    44 5C2D B8			        dta $B8  ; SFX_ROCKET
    45 5C2E 66			        dta $66  ; SFX_PODEATH
    46 5C2F 96			        dta $96  ; SFX_POSIGHT
    47 5C30 96			        dta $96  ; SFX_IMPSIGHT
    48 5C31 0B			        dta $0B  ; SFX_IMPDEATH
    49 5C32 AA			        dta $AA  ; SFX_SHOTGUN
    50 5C33 16			        dta $16  ; SFX_WPNUP
    51 5C34 BD			        dta $BD  ; SFX_PUNCH
    52 5C35 81			        dta $81  ; SFX_BAREXP
    53 5C36 00			        dta $00  ; SFX_SLOP
    54 5C37 D6			        dta $D6  ; SFX_BRSSIT
    55 5C38 B6			        dta $B6  ; SFX_BRSDTH
    56 5C39 BB			        dta $BB  ; SFX_SGTSIT
    57 5C3A A2			        dta $A2  ; SFX_SGTDTH
    58 5C3B D5			        dta $D5  ; SFX_CACSIT
    59 5C3C 00			        dta $00  ; SFX_CACDTH
    60 5C3D 50			        dta $50  ; SFX_PLASMA
    61 5C3E 58			        dta $58  ; SFX_BFG
    62 5C3F 43			        dta $43  ; SFX_BFGXPL
    63 5C40 82			        dta $82  ; SFX_PLDEATH
    64 5C41 4D			        dta $4D  ; SFX_DOOROPN
    65 5C42 28			        dta $28  ; SFX_DOORCLS
    66 5C43 20			        dta $20  ; SFX_OOF
    67 5C44 E4			        dta $E4  ; SFX_POSIGHT2
    68 5C45 DF			        dta $DF  ; SFX_POSIGHT3
    69 5C46 B9			        dta $B9  ; SFX_FIRSHT
    70 5C47 28			        dta $28  ; SFX_SWTCHN
    71 5C48 95			        dta $95  ; SFX_SWTCHX
    72 5C49 68			        dta $68  ; SFX_CLAW
    73 5C4A F0			        dta $F0  ; SFX_SGTATK
    74
    75 				; Start pointer hi
    76 5C4B			sfx_vptr_hi
    77 5C4B 50			        dta $50  ; SFX_PISTOL
    78 5C4C 52			        dta $52  ; SFX_ITEMUP
    79 5C4D 53			        dta $53  ; SFX_ROCKET
    80 5C4E 5A			        dta $5A  ; SFX_PODEATH
    81 5C4F 61			        dta $61  ; SFX_POSIGHT
    82 5C50 64			        dta $64  ; SFX_IMPSIGHT
    83 5C51 6C			        dta $6C  ; SFX_IMPDEATH
    84 5C52 6F			        dta $6F  ; SFX_SHOTGUN
    85 5C53 76			        dta $76  ; SFX_WPNUP
    86 5C54 79			        dta $79  ; SFX_PUNCH
    87 5C55 7B			        dta $7B  ; SFX_BAREXP
    88 5C56 43			        dta $43  ; SFX_SLOP
    89 5C57 49			        dta $49  ; SFX_BRSSIT
    90 5C58 51			        dta $51  ; SFX_BRSDTH
    91 5C59 58			        dta $58  ; SFX_SGTSIT
    92 5C5A 60			        dta $60  ; SFX_SGTDTH
    93 5C5B 67			        dta $67  ; SFX_CACSIT
    94 5C5C 74			        dta $74  ; SFX_CACDTH
    95 5C5D 7B			        dta $7B  ; SFX_PLASMA
    96 5C5E 7F			        dta $7F  ; SFX_BFG
    97 5C5F 4C			        dta $4C  ; SFX_BFGXPL
    98 5C60 56			        dta $56  ; SFX_PLDEATH
    99 5C61 5E			        dta $5E  ; SFX_DOOROPN
   100 5C62 61			        dta $61  ; SFX_DOORCLS
   101 5C63 64			        dta $64  ; SFX_OOF
   102 5C64 66			        dta $66  ; SFX_POSIGHT2
   103 5C65 6E			        dta $6E  ; SFX_POSIGHT3
   104 5C66 76			        dta $76  ; SFX_FIRSHT
   105 5C67 41			        dta $41  ; SFX_SWTCHN
   106 5C68 45			        dta $45  ; SFX_SWTCHX
   107 5C69 49			        dta $49  ; SFX_CLAW
   108 5C6A 4D			        dta $4D  ; SFX_SGTATK
   109
   110 				; End bank (with BANK_EN)
   111 5C6B			sfx_vbank_end
   112 5C6B D8			        dta $D8  ; SFX_PISTOL
   113 5C6C D8			        dta $D8  ; SFX_ITEMUP
   114 5C6D D8			        dta $D8  ; SFX_ROCKET
   115 5C6E D8			        dta $D8  ; SFX_PODEATH
   116 5C6F D8			        dta $D8  ; SFX_POSIGHT
   117 5C70 D8			        dta $D8  ; SFX_IMPSIGHT
   118 5C71 D8			        dta $D8  ; SFX_IMPDEATH
   119 5C72 D8			        dta $D8  ; SFX_SHOTGUN
   120 5C73 D8			        dta $D8  ; SFX_WPNUP
   121 5C74 D8			        dta $D8  ; SFX_PUNCH
   122 5C75 D9			        dta $D9  ; SFX_BAREXP
   123 5C76 D9			        dta $D9  ; SFX_SLOP
   124 5C77 D9			        dta $D9  ; SFX_BRSSIT
   125 5C78 D9			        dta $D9  ; SFX_BRSDTH
   126 5C79 D9			        dta $D9  ; SFX_SGTSIT
   127 5C7A D9			        dta $D9  ; SFX_SGTDTH
   128 5C7B D9			        dta $D9  ; SFX_CACSIT
   129 5C7C D9			        dta $D9  ; SFX_CACDTH
   130 5C7D D9			        dta $D9  ; SFX_PLASMA
   131 5C7E DA			        dta $DA  ; SFX_BFG
   132 5C7F DA			        dta $DA  ; SFX_BFGXPL
   133 5C80 DA			        dta $DA  ; SFX_PLDEATH
   134 5C81 DA			        dta $DA  ; SFX_DOOROPN
   135 5C82 DA			        dta $DA  ; SFX_DOORCLS
   136 5C83 DA			        dta $DA  ; SFX_OOF
   137 5C84 DA			        dta $DA  ; SFX_POSIGHT2
   138 5C85 DA			        dta $DA  ; SFX_POSIGHT3
   139 5C86 DB			        dta $DB  ; SFX_FIRSHT
   140 5C87 DB			        dta $DB  ; SFX_SWTCHN
   141 5C88 DB			        dta $DB  ; SFX_SWTCHX
   142 5C89 DB			        dta $DB  ; SFX_CLAW
   143 5C8A DB			        dta $DB  ; SFX_SGTATK
   144
   145 				; End pointer lo
   146 5C8B			sfx_vend_lo
   147 5C8B 1E			        dta $1E  ; SFX_PISTOL
   148 5C8C B8			        dta $B8  ; SFX_ITEMUP
   149 5C8D 66			        dta $66  ; SFX_ROCKET
   150 5C8E 96			        dta $96  ; SFX_PODEATH
   151 5C8F 96			        dta $96  ; SFX_POSIGHT
   152 5C90 0B			        dta $0B  ; SFX_IMPSIGHT
   153 5C91 AA			        dta $AA  ; SFX_IMPDEATH
   154 5C92 16			        dta $16  ; SFX_SHOTGUN
   155 5C93 BD			        dta $BD  ; SFX_WPNUP
   156 5C94 81			        dta $81  ; SFX_PUNCH
   157 5C95 00			        dta $00  ; SFX_BAREXP
   158 5C96 D6			        dta $D6  ; SFX_SLOP
   159 5C97 B6			        dta $B6  ; SFX_BRSSIT
   160 5C98 BB			        dta $BB  ; SFX_BRSDTH
   161 5C99 A2			        dta $A2  ; SFX_SGTSIT
   162 5C9A D5			        dta $D5  ; SFX_SGTDTH
   163 5C9B 00			        dta $00  ; SFX_CACSIT
   164 5C9C 50			        dta $50  ; SFX_CACDTH
   165 5C9D 58			        dta $58  ; SFX_PLASMA
   166 5C9E 43			        dta $43  ; SFX_BFG
   167 5C9F 82			        dta $82  ; SFX_BFGXPL
   168 5CA0 4D			        dta $4D  ; SFX_PLDEATH
   169 5CA1 28			        dta $28  ; SFX_DOOROPN
   170 5CA2 20			        dta $20  ; SFX_DOORCLS
   171 5CA3 E4			        dta $E4  ; SFX_OOF
   172 5CA4 DF			        dta $DF  ; SFX_POSIGHT2
   173 5CA5 B9			        dta $B9  ; SFX_POSIGHT3
   174 5CA6 28			        dta $28  ; SFX_FIRSHT
   175 5CA7 95			        dta $95  ; SFX_SWTCHN
   176 5CA8 68			        dta $68  ; SFX_SWTCHX
   177 5CA9 F0			        dta $F0  ; SFX_CLAW
   178 5CAA 8E			        dta $8E  ; SFX_SGTATK
   179
   180 				; End pointer hi
   181 5CAB			sfx_vend_hi
   182 5CAB 52			        dta $52  ; SFX_PISTOL
   183 5CAC 53			        dta $53  ; SFX_ITEMUP
   184 5CAD 5A			        dta $5A  ; SFX_ROCKET
   185 5CAE 61			        dta $61  ; SFX_PODEATH
   186 5CAF 64			        dta $64  ; SFX_POSIGHT
   187 5CB0 6C			        dta $6C  ; SFX_IMPSIGHT
   188 5CB1 6F			        dta $6F  ; SFX_IMPDEATH
   189 5CB2 76			        dta $76  ; SFX_SHOTGUN
   190 5CB3 79			        dta $79  ; SFX_WPNUP
   191 5CB4 7B			        dta $7B  ; SFX_PUNCH
   192 5CB5 43			        dta $43  ; SFX_BAREXP
   193 5CB6 49			        dta $49  ; SFX_SLOP
   194 5CB7 51			        dta $51  ; SFX_BRSSIT
   195 5CB8 58			        dta $58  ; SFX_BRSDTH
   196 5CB9 60			        dta $60  ; SFX_SGTSIT
   197 5CBA 67			        dta $67  ; SFX_SGTDTH
   198 5CBB 74			        dta $74  ; SFX_CACSIT
   199 5CBC 7B			        dta $7B  ; SFX_CACDTH
   200 5CBD 7F			        dta $7F  ; SFX_PLASMA
   201 5CBE 4C			        dta $4C  ; SFX_BFG
   202 5CBF 56			        dta $56  ; SFX_BFGXPL
   203 5CC0 5E			        dta $5E  ; SFX_PLDEATH
   204 5CC1 61			        dta $61  ; SFX_DOOROPN
   205 5CC2 64			        dta $64  ; SFX_DOORCLS
   206 5CC3 66			        dta $66  ; SFX_OOF
   207 5CC4 6E			        dta $6E  ; SFX_POSIGHT2
   208 5CC5 76			        dta $76  ; SFX_POSIGHT3
   209 5CC6 41			        dta $41  ; SFX_FIRSHT
   210 5CC7 45			        dta $45  ; SFX_SWTCHN
   211 5CC8 49			        dta $49  ; SFX_SWTCHX
   212 5CC9 4D			        dta $4D  ; SFX_CLAW
   213 5CCA 54			        dta $54  ; SFX_SGTATK
   214
   215 = 0005			SND_NUM_CHUNKS = 5
   216 = 00E1			SND_VRAM_BASE_BANK = $E1
   326 5CCB			        icl 'data.asm'
Source: data.asm
     1 				;==============================================
     2 				; DOOM2D - Data tables and binary includes
     3 				; data.asm
     4 				;==============================================
     5
     6 				; ============================================
     7 				; SPRITE TABLES (auto-generated in sprite_defs.asm)
     8 				; ============================================
     9 5CCB			        icl 'sprite_defs.asm'
Source: sprite_defs.asm
     1 				;==============================================
     2 				; Auto-generated sprite/tile definitions
     3 				; DO NOT EDIT - generated by prepare_sprites.py
     4 				;==============================================
     5
     6 				; Sprite indices
     7 = 0000			SPR_PLAYER_IDLE              equ   0  ; 16x32, offset $0000
     8 = 0001			SPR_PLAYER_WALK1             equ   1  ; 16x32, offset $0200
     9 = 0002			SPR_PLAYER_WALK2             equ   2  ; 16x32, offset $0400
    10 = 0003			SPR_PLAYER_WALK3             equ   3  ; 16x32, offset $0600
    11 = 0004			SPR_PLAYER_SHOOT             equ   4  ; 16x32, offset $0800
    12 = 0005			SPR_PLAYER_DEATH1            equ   5  ; 16x32, offset $0A00
    13 = 0006			SPR_ZOMBIE_IDLE              equ   6  ; 16x32, offset $0C00
    14 = 0007			SPR_ZOMBIE_WALK1             equ   7  ; 16x32, offset $0E00
    15 = 0008			SPR_ZOMBIE_WALK2             equ   8  ; 16x32, offset $1000
    16 = 0009			SPR_ZOMBIE_WALK3             equ   9  ; 16x32, offset $1200
    17 = 000A			SPR_ZOMBIE_SHOOT             equ  10  ; 16x32, offset $1400
    18 = 000B			SPR_ZOMBIE_DEATH1            equ  11  ; 16x32, offset $1600
    19 = 000C			SPR_IMP_IDLE                 equ  12  ; 16x32, offset $1800
    20 = 000D			SPR_IMP_WALK1                equ  13  ; 16x32, offset $1A00
    21 = 000E			SPR_IMP_WALK2                equ  14  ; 16x32, offset $1C00
    22 = 000F			SPR_IMP_WALK3                equ  15  ; 16x32, offset $1E00
    23 = 0010			SPR_IMP_SHOOT                equ  16  ; 16x32, offset $2000
    24 = 0011			SPR_IMP_DEATH1               equ  17  ; 16x32, offset $2200
    25 = 0012			SPR_PINKY_IDLE               equ  18  ; 16x32, offset $2400
    26 = 0013			SPR_PINKY_WALK1              equ  19  ; 16x32, offset $2600
    27 = 0014			SPR_PINKY_WALK2              equ  20  ; 16x32, offset $2800
    28 = 0015			SPR_PINKY_WALK3              equ  21  ; 16x32, offset $2A00
    29 = 0016			SPR_PINKY_ATTACK             equ  22  ; 16x32, offset $2C00
    30 = 0017			SPR_PINKY_DEATH1             equ  23  ; 16x32, offset $2E00
    31 = 0018			SPR_CACO_IDLE                equ  24  ; 16x32, offset $3000
    32 = 0019			SPR_CACO_WALK1               equ  25  ; 16x32, offset $3200
    33 = 001A			SPR_CACO_WALK2               equ  26  ; 16x32, offset $3400
    34 = 001B			SPR_CACO_WALK3               equ  27  ; 16x32, offset $3600
    35 = 001C			SPR_CACO_SHOOT               equ  28  ; 16x32, offset $3800
    36 = 001D			SPR_CACO_DEATH1              equ  29  ; 16x32, offset $3A00
    37 = 001E			SPR_SHOTGUN_IDLE             equ  30  ; 16x32, offset $3C00
    38 = 001F			SPR_SHOTGUN_WALK1            equ  31  ; 16x32, offset $3E00
    39 = 0020			SPR_SHOTGUN_WALK2            equ  32  ; 16x32, offset $4000
    40 = 0021			SPR_SHOTGUN_WALK3            equ  33  ; 16x32, offset $4200
    41 = 0022			SPR_SHOTGUN_SHOOT            equ  34  ; 16x32, offset $4400
    42 = 0023			SPR_SHOTGUN_DEATH1           equ  35  ; 16x32, offset $4600
    43 = 0024			SPR_BARON_IDLE               equ  36  ; 16x32, offset $4800
    44 = 0025			SPR_BARON_WALK1              equ  37  ; 16x32, offset $4A00
    45 = 0026			SPR_BARON_WALK2              equ  38  ; 16x32, offset $4C00
    46 = 0027			SPR_BARON_WALK3              equ  39  ; 16x32, offset $4E00
    47 = 0028			SPR_BARON_SHOOT              equ  40  ; 16x32, offset $5000
    48 = 0029			SPR_BARON_DEATH1             equ  41  ; 16x32, offset $5200
    49 = 002A			SPR_PROJECTILE1              equ  42  ; 8x8, offset $5400
    50 = 002B			SPR_PROJECTILE2              equ  43  ; 8x8, offset $5440
    51 = 002C			SPR_MEDIKIT                  equ  44  ; 16x16, offset $5500
    52 = 002D			SPR_STIMPACK                 equ  45  ; 16x16, offset $5600
    53 = 002E			SPR_HEALTH_BONUS             equ  46  ; 16x16, offset $5700
    54 = 002F			SPR_AMMO_CLIP                equ  47  ; 16x16, offset $5800
    55 = 0030			SPR_GREENARMOR               equ  48  ; 16x16, offset $AD00
    56 = 0031			SPR_BLUEARMOR                equ  49  ; 16x16, offset $AE00
    57 = 0032			SPR_SOULSPHERE               equ  50  ; 16x16, offset $AF00
    58 = 0033			SPR_KEYRED                   equ  51  ; 16x16, offset $B000
    59 = 0034			SPR_KEYBLUE                  equ  52  ; 16x16, offset $B100
    60 = 0035			SPR_KEYYELLOW                equ  53  ; 16x16, offset $B200
    61 = 0036			SPR_SHOTGUNPK                equ  54  ; 16x16, offset $B300
    62 = 0037			SPR_SHELLS                   equ  55  ; 16x16, offset $B400
    63 = 0038			SPR_BARREL                   equ  56  ; 16x16, offset $B500
    64 = 0039			SPR_PILLAR                   equ  57  ; 16x32, offset $B600
    65 = 003A			SPR_TORCH                    equ  58  ; 16x32, offset $B800
    66 = 003B			SPR_LAMP                     equ  59  ; 16x32, offset $BA00
    67 = 003C			SPR_PLAYER_IDLE_L            equ  60  ; 16x32, offset $5900
    68 = 003D			SPR_PLAYER_WALK1_L           equ  61  ; 16x32, offset $5B00
    69 = 003E			SPR_PLAYER_WALK2_L           equ  62  ; 16x32, offset $5D00
    70 = 003F			SPR_PLAYER_WALK3_L           equ  63  ; 16x32, offset $5F00
    71 = 0040			SPR_PLAYER_SHOOT_L           equ  64  ; 16x32, offset $6100
    72 = 0041			SPR_PLAYER_DEATH1_L          equ  65  ; 16x32, offset $6300
    73 = 0042			SPR_ZOMBIE_IDLE_L            equ  66  ; 16x32, offset $6500
    74 = 0043			SPR_ZOMBIE_WALK1_L           equ  67  ; 16x32, offset $6700
    75 = 0044			SPR_ZOMBIE_WALK2_L           equ  68  ; 16x32, offset $6900
    76 = 0045			SPR_ZOMBIE_WALK3_L           equ  69  ; 16x32, offset $6B00
    77 = 0046			SPR_ZOMBIE_SHOOT_L           equ  70  ; 16x32, offset $6D00
    78 = 0047			SPR_ZOMBIE_DEATH1_L          equ  71  ; 16x32, offset $6F00
    79 = 0048			SPR_IMP_IDLE_L               equ  72  ; 16x32, offset $7100
    80 = 0049			SPR_IMP_WALK1_L              equ  73  ; 16x32, offset $7300
    81 = 004A			SPR_IMP_WALK2_L              equ  74  ; 16x32, offset $7500
    82 = 004B			SPR_IMP_WALK3_L              equ  75  ; 16x32, offset $7700
    83 = 004C			SPR_IMP_SHOOT_L              equ  76  ; 16x32, offset $7900
    84 = 004D			SPR_IMP_DEATH1_L             equ  77  ; 16x32, offset $7B00
    85 = 004E			SPR_PINKY_IDLE_L             equ  78  ; 16x32, offset $7D00
    86 = 004F			SPR_PINKY_WALK1_L            equ  79  ; 16x32, offset $7F00
    87 = 0050			SPR_PINKY_WALK2_L            equ  80  ; 16x32, offset $8100
    88 = 0051			SPR_PINKY_WALK3_L            equ  81  ; 16x32, offset $8300
    89 = 0052			SPR_PINKY_ATTACK_L           equ  82  ; 16x32, offset $8500
    90 = 0053			SPR_PINKY_DEATH1_L           equ  83  ; 16x32, offset $8700
    91 = 0054			SPR_CACO_IDLE_L              equ  84  ; 16x32, offset $8900
    92 = 0055			SPR_CACO_WALK1_L             equ  85  ; 16x32, offset $8B00
    93 = 0056			SPR_CACO_WALK2_L             equ  86  ; 16x32, offset $8D00
    94 = 0057			SPR_CACO_WALK3_L             equ  87  ; 16x32, offset $8F00
    95 = 0058			SPR_CACO_SHOOT_L             equ  88  ; 16x32, offset $9100
    96 = 0059			SPR_CACO_DEATH1_L            equ  89  ; 16x32, offset $9300
    97 = 005A			SPR_SHOTGUN_IDLE_L           equ  90  ; 16x32, offset $9500
    98 = 005B			SPR_SHOTGUN_WALK1_L          equ  91  ; 16x32, offset $9700
    99 = 005C			SPR_SHOTGUN_WALK2_L          equ  92  ; 16x32, offset $9900
   100 = 005D			SPR_SHOTGUN_WALK3_L          equ  93  ; 16x32, offset $9B00
   101 = 005E			SPR_SHOTGUN_SHOOT_L          equ  94  ; 16x32, offset $9D00
   102 = 005F			SPR_SHOTGUN_DEATH1_L         equ  95  ; 16x32, offset $9F00
   103 = 0060			SPR_BARON_IDLE_L             equ  96  ; 16x32, offset $A100
   104 = 0061			SPR_BARON_WALK1_L            equ  97  ; 16x32, offset $A300
   105 = 0062			SPR_BARON_WALK2_L            equ  98  ; 16x32, offset $A500
   106 = 0063			SPR_BARON_WALK3_L            equ  99  ; 16x32, offset $A700
   107 = 0064			SPR_BARON_SHOOT_L            equ 100  ; 16x32, offset $A900
   108 = 0065			SPR_BARON_DEATH1_L           equ 101  ; 16x32, offset $AB00
   109 = 0066			SPR_CHAINGUNPK               equ 102  ; 16x16, offset $BC00
   110 = 0067			SPR_ROCKETPK                 equ 103  ; 16x16, offset $BD00
   111 = 0068			SPR_ROCKET_PROJ              equ 104  ; 8x8, offset $BE00
   112 = 0069			SPR_ROCKETBOX                equ 105  ; 16x16, offset $BF00
   113 				; --- New sprites in VRAM $031000+ (chunk5, bank $31) ---
   114 = 006A			SPR_ZOMBIE_DEATH2            equ 106  ; 16x32, VRAM $031000
   115 = 006B			SPR_ZOMBIE_DEATH3            equ 107  ; 16x32, VRAM $031200
   116 = 006C			SPR_ZOMBIE_GIB1              equ 108  ; 16x32, VRAM $031400
   117 = 006D			SPR_ZOMBIE_GIB2              equ 109  ; 16x32, VRAM $031600
   118 = 006E			SPR_IMP_GIB1                 equ 110  ; 16x32, VRAM $031800
   119 = 006F			SPR_IMP_GIB2                 equ 111  ; 16x32, VRAM $031A00
   120 = 0070			SPR_SHOTGUN_GIB1             equ 112  ; 16x32, VRAM $031C00
   121 = 0071			SPR_SHOTGUN_GIB2             equ 113  ; 16x32, VRAM $031E00
   122 				; --- Death2/3 for all types (chunk5 offset $1900+, VRAM $032900+) ---
   123 = 0072			SPR_IMP_DEATH2               equ 114  ; 16x32
   124 = 0073			SPR_IMP_DEATH3               equ 115  ; 16x32
   125 = 0074			SPR_SHOTGUN_DEATH2           equ 116  ; 16x32
   126 = 0075			SPR_SHOTGUN_DEATH3           equ 117  ; 16x32
   127 = 0076			SPR_PINKY_DEATH2             equ 118  ; 16x32
   128 = 0077			SPR_PINKY_DEATH3             equ 119  ; 16x32
   129 = 0078			SPR_CACO_DEATH2              equ 120  ; 16x32
   130 = 0079			SPR_CACO_DEATH3              equ 121  ; 16x32
   131 = 007A			SPR_BARON_DEATH2             equ 122  ; 16x32
   132 = 007B			SPR_BARON_DEATH3             equ 123  ; 16x32
   133 = 007C			SPR_BARREL_EXP1              equ 124  ; 16x32 (flash)
   134 = 007D			SPR_BARREL_EXP2              equ 125  ; 16x32 (fire)
   135 = 007E			SPR_BARREL_EXP3              equ 126  ; 16x32 (smoke)
   136 = 007F			SPR_PLASMAGUNPK              equ 127  ; 16x16, VRAM $01F000
   137 = 0080			SPR_CELLSPK                  equ 128  ; 16x16, VRAM $01F100
   138 = 0081			SPR_PLASMA_PROJ1             equ 129  ; 8x8,   VRAM $01F200
   139 = 0082			SPR_PLASMA_PROJ2             equ 130  ; 8x8,   VRAM $01F240
   140 = 0083			SPR_BFG_PROJ1                equ 131  ; 16x16, VRAM $01F280
   141 = 0084			SPR_BFG_PROJ2                equ 132  ; 16x16, VRAM $01F380
   142 = 0085			SPR_BFGPK                   equ 133  ; 16x16, VRAM $01F480
   143 = 0086			SPR_ROCKET1PK                equ 134  ; 16x16, VRAM $01F580
   144 = 0087			SPR_ARMORBONUS               equ 135  ; 16x16, VRAM $01F680
   145 = 0088			SPR_PL_DEATH2                equ 136  ; 16x32, VRAM $01F780
   146 = 0089			SPR_PL_DEATH3                equ 137  ; 16x32, VRAM $01F980
   147 = 008A			SPR_IMP_FIRE1                equ 138  ; 8x8,   VRAM $01E800
   148 = 008B			SPR_IMP_FIRE2                equ 139  ; 8x8,   VRAM $01E840
   149 = 008C			SPR_PL_PAIN                  equ 140  ; 16x32, VRAM $054000
   150 = 008D			SPR_PL_PAIN_L                equ 141  ; 16x32, VRAM $054200
   151 = 008E			SPR_ZOMBIE_PAIN              equ 142  ; 16x32, VRAM $054400
   152 = 008F			SPR_ZOMBIE_PAIN_L            equ 143  ; 16x32, VRAM $054600
   153 = 0090			SPR_CACO_FIRE1               equ 144  ; 8x8,   VRAM $01E880
   154 = 0091			SPR_CACO_FIRE2               equ 145  ; 8x8,   VRAM $01E8C0
   155 = C000			SPRITESHEET_SIZE         equ $C000      ; chunks 1-4 (49152 bytes)
   156
   157 				; Player sprite aliases
   158 = 0000			SPR_PL_IDLE              equ 0
   159 = 0001			SPR_PL_W1                equ 1
   160 = 0002			SPR_PL_W2                equ 2
   161 = 0003			SPR_PL_W3                equ 3
   162 = 0004			SPR_PL_SHOOT             equ 4
   163 = 0005			SPR_PL_DEATH             equ 5
   164
   165 = 002A			SPR_PROJ1                         equ  42
   166 = 002B			SPR_PROJ2                         equ  43
   167
   168
   169 = 003C			MIRROR_OFFSET            equ  60
   170
   171 				; Enemy types
   172 = 0000			EN_ZOMBIE                       equ 0
   173 = 0001			EN_IMP                          equ 1
   174 = 0002			EN_PINKY                        equ 2
   175 = 0003			EN_CACO                         equ 3
   176 = 0004			EN_SHOTGUN                      equ 4
   177 = 0005			EN_BARON                        equ 5
   178
   179 				; Sprite VRAM offset tables
   180 5CCB			spr_off_lo
   181 5CCB 00			        dta <$0000  ; player_idle
   182 5CCC 00			        dta <$0200  ; player_walk1
   183 5CCD 00			        dta <$0400  ; player_walk2
   184 5CCE 00			        dta <$0600  ; player_walk3
   185 5CCF 00			        dta <$0800  ; player_shoot
   186 5CD0 00			        dta <$0A00  ; player_death1
   187 5CD1 00			        dta <$0C00  ; zombie_idle
   188 5CD2 00			        dta <$0E00  ; zombie_walk1
   189 5CD3 00			        dta <$1000  ; zombie_walk2
   190 5CD4 00			        dta <$1200  ; zombie_walk3
   191 5CD5 00			        dta <$1400  ; zombie_shoot
   192 5CD6 00			        dta <$1600  ; zombie_death1
   193 5CD7 00			        dta <$1800  ; imp_idle
   194 5CD8 00			        dta <$1A00  ; imp_walk1
   195 5CD9 00			        dta <$1C00  ; imp_walk2
   196 5CDA 00			        dta <$1E00  ; imp_walk3
   197 5CDB 00			        dta <$2000  ; imp_shoot
   198 5CDC 00			        dta <$2200  ; imp_death1
   199 5CDD 00			        dta <$2400  ; pinky_idle
   200 5CDE 00			        dta <$2600  ; pinky_walk1
   201 5CDF 00			        dta <$2800  ; pinky_walk2
   202 5CE0 00			        dta <$2A00  ; pinky_walk3
   203 5CE1 00			        dta <$2C00  ; pinky_attack
   204 5CE2 00			        dta <$2E00  ; pinky_death1
   205 5CE3 00			        dta <$3000  ; caco_idle
   206 5CE4 00			        dta <$3200  ; caco_walk1
   207 5CE5 00			        dta <$3400  ; caco_walk2
   208 5CE6 00			        dta <$3600  ; caco_walk3
   209 5CE7 00			        dta <$3800  ; caco_shoot
   210 5CE8 00			        dta <$3A00  ; caco_death1
   211 5CE9 00			        dta <$3C00  ; shotgun_idle
   212 5CEA 00			        dta <$3E00  ; shotgun_walk1
   213 5CEB 00			        dta <$4000  ; shotgun_walk2
   214 5CEC 00			        dta <$4200  ; shotgun_walk3
   215 5CED 00			        dta <$4400  ; shotgun_shoot
   216 5CEE 00			        dta <$4600  ; shotgun_death1
   217 5CEF 00			        dta <$4800  ; baron_idle
   218 5CF0 00			        dta <$4A00  ; baron_walk1
   219 5CF1 00			        dta <$4C00  ; baron_walk2
   220 5CF2 00			        dta <$4E00  ; baron_walk3
   221 5CF3 00			        dta <$5000  ; baron_shoot
   222 5CF4 00			        dta <$5200  ; baron_death1
   223 5CF5 00			        dta <$5400  ; projectile1
   224 5CF6 40			        dta <$5440  ; projectile2
   225 5CF7 00			        dta <$5500  ; medikit
   226 5CF8 00			        dta <$5600  ; stimpack
   227 5CF9 00			        dta <$5700  ; health_bonus
   228 5CFA 00			        dta <$5800  ; ammo_clip
   229 5CFB 00			        dta <$AD00  ; greenarmor
   230 5CFC 00			        dta <$AE00  ; bluearmor
   231 5CFD 00			        dta <$AF00  ; soulsphere
   232 5CFE 00			        dta <$B000  ; keyred
   233 5CFF 00			        dta <$B100  ; keyblue
   234 5D00 00			        dta <$B200  ; keyyellow
   235 5D01 00			        dta <$B300  ; shotgunpk
   236 5D02 00			        dta <$B400  ; shells
   237 5D03 00			        dta <$B500  ; barrel
   238 5D04 00			        dta <$B600  ; pillar
   239 5D05 00			        dta <$B800  ; torch
   240 5D06 00			        dta <$BA00  ; lamp
   241 5D07 00			        dta <$5900  ; player_idle_L
   242 5D08 00			        dta <$5B00  ; player_walk1_L
   243 5D09 00			        dta <$5D00  ; player_walk2_L
   244 5D0A 00			        dta <$5F00  ; player_walk3_L
   245 5D0B 00			        dta <$6100  ; player_shoot_L
   246 5D0C 00			        dta <$6300  ; player_death1_L
   247 5D0D 00			        dta <$6500  ; zombie_idle_L
   248 5D0E 00			        dta <$6700  ; zombie_walk1_L
   249 5D0F 00			        dta <$6900  ; zombie_walk2_L
   250 5D10 00			        dta <$6B00  ; zombie_walk3_L
   251 5D11 00			        dta <$6D00  ; zombie_shoot_L
   252 5D12 00			        dta <$6F00  ; zombie_death1_L
   253 5D13 00			        dta <$7100  ; imp_idle_L
   254 5D14 00			        dta <$7300  ; imp_walk1_L
   255 5D15 00			        dta <$7500  ; imp_walk2_L
   256 5D16 00			        dta <$7700  ; imp_walk3_L
   257 5D17 00			        dta <$7900  ; imp_shoot_L
   258 5D18 00			        dta <$7B00  ; imp_death1_L
   259 5D19 00			        dta <$7D00  ; pinky_idle_L
   260 5D1A 00			        dta <$7F00  ; pinky_walk1_L
   261 5D1B 00			        dta <$8100  ; pinky_walk2_L
   262 5D1C 00			        dta <$8300  ; pinky_walk3_L
   263 5D1D 00			        dta <$8500  ; pinky_attack_L
   264 5D1E 00			        dta <$8700  ; pinky_death1_L
   265 5D1F 00			        dta <$8900  ; caco_idle_L
   266 5D20 00			        dta <$8B00  ; caco_walk1_L
   267 5D21 00			        dta <$8D00  ; caco_walk2_L
   268 5D22 00			        dta <$8F00  ; caco_walk3_L
   269 5D23 00			        dta <$9100  ; caco_shoot_L
   270 5D24 00			        dta <$9300  ; caco_death1_L
   271 5D25 00			        dta <$9500  ; shotgun_idle_L
   272 5D26 00			        dta <$9700  ; shotgun_walk1_L
   273 5D27 00			        dta <$9900  ; shotgun_walk2_L
   274 5D28 00			        dta <$9B00  ; shotgun_walk3_L
   275 5D29 00			        dta <$9D00  ; shotgun_shoot_L
   276 5D2A 00			        dta <$9F00  ; shotgun_death1_L
   277 5D2B 00			        dta <$A100  ; baron_idle_L
   278 5D2C 00			        dta <$A300  ; baron_walk1_L
   279 5D2D 00			        dta <$A500  ; baron_walk2_L
   280 5D2E 00			        dta <$A700  ; baron_walk3_L
   281 5D2F 00			        dta <$A900  ; baron_shoot_L
   282 5D30 00			        dta <$AB00  ; baron_death1_L
   283 5D31 00			        dta <$BC00  ; chaingunpk
   284 5D32 00			        dta <$BD00  ; rocketpk
   285 5D33 00			        dta <$BE00  ; rocket_proj
   286 5D34 00			        dta <$BF00  ; rocketbox
   287 5D35 00			        dta <$0000  ; zombie_death2 (VRAM $031000)
   288 5D36 00			        dta <$0000  ; zombie_death3 (VRAM $031200)
   289 5D37 00			        dta <$0000  ; zombie_gib1 (VRAM $031400)
   290 5D38 00			        dta <$0000  ; zombie_gib2 (VRAM $031600)
   291 5D39 00			        dta <$0000  ; imp_gib1 (VRAM $031800)
   292 5D3A 00			        dta <$0000  ; imp_gib2 (VRAM $031A00)
   293 5D3B 00			        dta <$0000  ; shotgun_gib1 (VRAM $031C00)
   294 5D3C 00			        dta <$0000  ; shotgun_gib2 (VRAM $031E00)
   295 5D3D 00			        dta <$0000  ; imp_death2
   296 5D3E 00			        dta <$0000  ; imp_death3
   297 5D3F 00			        dta <$0000  ; shotgun_death2
   298 5D40 00			        dta <$0000  ; shotgun_death3
   299 5D41 00			        dta <$0000  ; pinky_death2
   300 5D42 00			        dta <$0000  ; pinky_death3
   301 5D43 00			        dta <$0000  ; caco_death2
   302 5D44 00			        dta <$0000  ; caco_death3
   303 5D45 00			        dta <$0000  ; baron_death2
   304 5D46 00			        dta <$0000  ; baron_death3
   305 5D47 00			        dta <$0000  ; barrel_exp1
   306 5D48 00			        dta <$0000  ; barrel_exp2
   307 5D49 00			        dta <$0000  ; barrel_exp3
   308 5D4A 00			        dta <$0000  ; plasmagunpk (VRAM $01F000)
   309 5D4B 00			        dta <$0000  ; cellspk (VRAM $01F100)
   310 5D4C 00			        dta <$0000  ; plasma_proj1 (VRAM $01F200)
   311 5D4D 40			        dta <$0040  ; plasma_proj2 (VRAM $01F240)
   312 5D4E 80			        dta <$0080  ; bfg_proj1 (VRAM $01F280)
   313 5D4F 80			        dta <$0080  ; bfg_proj2 (VRAM $01F380)
   314 5D50 80			        dta <$0080  ; bfgpk (VRAM $01F480)
   315 5D51 80			        dta <$0080  ; rocket1pk (VRAM $01F580)
   316 5D52 80			        dta <$0080  ; armorbonus (VRAM $01F680)
   317 5D53 80			        dta <$0080  ; pl_death2 (VRAM $01F780)
   318 5D54 80			        dta <$0080  ; pl_death3 (VRAM $01F980)
   319 5D55 00			        dta <$0000  ; imp_fire1 (VRAM $01E800)
   320 5D56 40			        dta <$0040  ; imp_fire2 (VRAM $01E840)
   321 5D57 00			        dta <$0000  ; pl_pain (VRAM $054000)
   322 5D58 00			        dta <$0000  ; pl_pain_L (VRAM $054200)
   323 5D59 00			        dta <$0000  ; zombie_pain (VRAM $054400)
   324 5D5A 00			        dta <$0000  ; zombie_pain_L (VRAM $054600)
   325 5D5B 80			        dta <$0080  ; caco_fire1 (VRAM $01E880)
   326 5D5C C0			        dta <$00C0  ; caco_fire2 (VRAM $01E8C0)
   327
   328 5D5D			spr_off_hi
   329 5D5D 00			        dta >$0000  ; player_idle
   330 5D5E 02			        dta >$0200  ; player_walk1
   331 5D5F 04			        dta >$0400  ; player_walk2
   332 5D60 06			        dta >$0600  ; player_walk3
   333 5D61 08			        dta >$0800  ; player_shoot
   334 5D62 0A			        dta >$0A00  ; player_death1
   335 5D63 0C			        dta >$0C00  ; zombie_idle
   336 5D64 0E			        dta >$0E00  ; zombie_walk1
   337 5D65 10			        dta >$1000  ; zombie_walk2
   338 5D66 12			        dta >$1200  ; zombie_walk3
   339 5D67 14			        dta >$1400  ; zombie_shoot
   340 5D68 16			        dta >$1600  ; zombie_death1
   341 5D69 18			        dta >$1800  ; imp_idle
   342 5D6A 1A			        dta >$1A00  ; imp_walk1
   343 5D6B 1C			        dta >$1C00  ; imp_walk2
   344 5D6C 1E			        dta >$1E00  ; imp_walk3
   345 5D6D 20			        dta >$2000  ; imp_shoot
   346 5D6E 22			        dta >$2200  ; imp_death1
   347 5D6F 24			        dta >$2400  ; pinky_idle
   348 5D70 26			        dta >$2600  ; pinky_walk1
   349 5D71 28			        dta >$2800  ; pinky_walk2
   350 5D72 2A			        dta >$2A00  ; pinky_walk3
   351 5D73 2C			        dta >$2C00  ; pinky_attack
   352 5D74 2E			        dta >$2E00  ; pinky_death1
   353 5D75 30			        dta >$3000  ; caco_idle
   354 5D76 32			        dta >$3200  ; caco_walk1
   355 5D77 34			        dta >$3400  ; caco_walk2
   356 5D78 36			        dta >$3600  ; caco_walk3
   357 5D79 38			        dta >$3800  ; caco_shoot
   358 5D7A 3A			        dta >$3A00  ; caco_death1
   359 5D7B 3C			        dta >$3C00  ; shotgun_idle
   360 5D7C 3E			        dta >$3E00  ; shotgun_walk1
   361 5D7D 40			        dta >$4000  ; shotgun_walk2
   362 5D7E 42			        dta >$4200  ; shotgun_walk3
   363 5D7F 44			        dta >$4400  ; shotgun_shoot
   364 5D80 46			        dta >$4600  ; shotgun_death1
   365 5D81 48			        dta >$4800  ; baron_idle
   366 5D82 4A			        dta >$4A00  ; baron_walk1
   367 5D83 4C			        dta >$4C00  ; baron_walk2
   368 5D84 4E			        dta >$4E00  ; baron_walk3
   369 5D85 50			        dta >$5000  ; baron_shoot
   370 5D86 52			        dta >$5200  ; baron_death1
   371 5D87 54			        dta >$5400  ; projectile1
   372 5D88 54			        dta >$5440  ; projectile2
   373 5D89 55			        dta >$5500  ; medikit
   374 5D8A 56			        dta >$5600  ; stimpack
   375 5D8B 57			        dta >$5700  ; health_bonus
   376 5D8C 58			        dta >$5800  ; ammo_clip
   377 5D8D AD			        dta >$AD00  ; greenarmor
   378 5D8E AE			        dta >$AE00  ; bluearmor
   379 5D8F AF			        dta >$AF00  ; soulsphere
   380 5D90 B0			        dta >$B000  ; keyred
   381 5D91 B1			        dta >$B100  ; keyblue
   382 5D92 B2			        dta >$B200  ; keyyellow
   383 5D93 B3			        dta >$B300  ; shotgunpk
   384 5D94 B4			        dta >$B400  ; shells
   385 5D95 B5			        dta >$B500  ; barrel
   386 5D96 B6			        dta >$B600  ; pillar
   387 5D97 B8			        dta >$B800  ; torch
   388 5D98 BA			        dta >$BA00  ; lamp
   389 5D99 59			        dta >$5900  ; player_idle_L
   390 5D9A 5B			        dta >$5B00  ; player_walk1_L
   391 5D9B 5D			        dta >$5D00  ; player_walk2_L
   392 5D9C 5F			        dta >$5F00  ; player_walk3_L
   393 5D9D 61			        dta >$6100  ; player_shoot_L
   394 5D9E 63			        dta >$6300  ; player_death1_L
   395 5D9F 65			        dta >$6500  ; zombie_idle_L
   396 5DA0 67			        dta >$6700  ; zombie_walk1_L
   397 5DA1 69			        dta >$6900  ; zombie_walk2_L
   398 5DA2 6B			        dta >$6B00  ; zombie_walk3_L
   399 5DA3 6D			        dta >$6D00  ; zombie_shoot_L
   400 5DA4 6F			        dta >$6F00  ; zombie_death1_L
   401 5DA5 71			        dta >$7100  ; imp_idle_L
   402 5DA6 73			        dta >$7300  ; imp_walk1_L
   403 5DA7 75			        dta >$7500  ; imp_walk2_L
   404 5DA8 77			        dta >$7700  ; imp_walk3_L
   405 5DA9 79			        dta >$7900  ; imp_shoot_L
   406 5DAA 7B			        dta >$7B00  ; imp_death1_L
   407 5DAB 7D			        dta >$7D00  ; pinky_idle_L
   408 5DAC 7F			        dta >$7F00  ; pinky_walk1_L
   409 5DAD 81			        dta >$8100  ; pinky_walk2_L
   410 5DAE 83			        dta >$8300  ; pinky_walk3_L
   411 5DAF 85			        dta >$8500  ; pinky_attack_L
   412 5DB0 87			        dta >$8700  ; pinky_death1_L
   413 5DB1 89			        dta >$8900  ; caco_idle_L
   414 5DB2 8B			        dta >$8B00  ; caco_walk1_L
   415 5DB3 8D			        dta >$8D00  ; caco_walk2_L
   416 5DB4 8F			        dta >$8F00  ; caco_walk3_L
   417 5DB5 91			        dta >$9100  ; caco_shoot_L
   418 5DB6 93			        dta >$9300  ; caco_death1_L
   419 5DB7 95			        dta >$9500  ; shotgun_idle_L
   420 5DB8 97			        dta >$9700  ; shotgun_walk1_L
   421 5DB9 99			        dta >$9900  ; shotgun_walk2_L
   422 5DBA 9B			        dta >$9B00  ; shotgun_walk3_L
   423 5DBB 9D			        dta >$9D00  ; shotgun_shoot_L
   424 5DBC 9F			        dta >$9F00  ; shotgun_death1_L
   425 5DBD A1			        dta >$A100  ; baron_idle_L
   426 5DBE A3			        dta >$A300  ; baron_walk1_L
   427 5DBF A5			        dta >$A500  ; baron_walk2_L
   428 5DC0 A7			        dta >$A700  ; baron_walk3_L
   429 5DC1 A9			        dta >$A900  ; baron_shoot_L
   430 5DC2 AB			        dta >$AB00  ; baron_death1_L
   431 5DC3 BC			        dta >$BC00  ; chaingunpk
   432 5DC4 BD			        dta >$BD00  ; rocketpk
   433 5DC5 BE			        dta >$BE00  ; rocket_proj
   434 5DC6 BF			        dta >$BF00  ; rocketbox
   435 5DC7 00			        dta >$0000  ; zombie_death2 (VRAM $031000)
   436 5DC8 02			        dta >$0200  ; zombie_death3 (VRAM $031200)
   437 5DC9 04			        dta >$0400  ; zombie_gib1 (VRAM $031400)
   438 5DCA 06			        dta >$0600  ; zombie_gib2 (VRAM $031600)
   439 5DCB 08			        dta >$0800  ; imp_gib1 (VRAM $031800)
   440 5DCC 0A			        dta >$0A00  ; imp_gib2 (VRAM $031A00)
   441 5DCD 0C			        dta >$0C00  ; shotgun_gib1 (VRAM $031C00)
   442 5DCE 0E			        dta >$0E00  ; shotgun_gib2 (VRAM $031E00)
   443 5DCF EC			        dta >$EC00  ; imp_death2 (VRAM $01FC00, in c6)
   444 5DD0 EE			        dta >$EE00  ; imp_death3 (VRAM $01FE00, in c6)
   445 5DD1 1D			        dta >$1D00  ; shotgun_death2
   446 5DD2 1F			        dta >$1F00  ; shotgun_death3
   447 5DD3 21			        dta >$2100  ; pinky_death2
   448 5DD4 23			        dta >$2300  ; pinky_death3
   449 5DD5 25			        dta >$2500  ; caco_death2
   450 5DD6 27			        dta >$2700  ; caco_death3
   451 5DD7 29			        dta >$2900  ; baron_death2
   452 5DD8 2B			        dta >$2B00  ; baron_death3
   453 5DD9 2D			        dta >$2D00  ; barrel_exp1 (16x16, 256B each)
   454 5DDA 2E			        dta >$2E00  ; barrel_exp2
   455 5DDB 2F			        dta >$2F00  ; barrel_exp3
   456 5DDC E0			        dta >$E000  ; plasmagunpk ($01F000: hi=$E0, +$10=$F0)
   457 5DDD E1			        dta >$E100  ; cellspk ($01F100: hi=$E1, +$10=$F1)
   458 5DDE E2			        dta >$E200  ; plasma_proj1 ($01F200)
   459 5DDF E2			        dta >$E200  ; plasma_proj2 ($01F240, same hi byte)
   460 5DE0 E2			        dta >$E200  ; bfg_proj1 ($01F280)
   461 5DE1 E3			        dta >$E300  ; bfg_proj2 ($01F380)
   462 5DE2 E4			        dta >$E400  ; bfgpk ($01F480)
   463 5DE3 E5			        dta >$E500  ; rocket1pk ($01F580)
   464 5DE4 E6			        dta >$E600  ; armorbonus ($01F680)
   465 5DE5 E7			        dta >$E700  ; pl_death2 ($01F780)
   466 5DE6 E9			        dta >$E900  ; pl_death3 ($01F980)
   467 5DE7 D8			        dta >$D800  ; imp_fire1 ($01E800, +$10=$E8)
   468 5DE8 D8			        dta >$D840  ; imp_fire2 ($01E840, +$10=$E8)
   469 5DE9 30			        dta >$3000  ; pl_pain ($054000, +$10=$40)
   470 5DEA 32			        dta >$3200  ; pl_pain_L ($054200, +$10=$42)
   471 5DEB 34			        dta >$3400  ; zombie_pain ($054400, +$10=$44)
   472 5DEC 36			        dta >$3600  ; zombie_pain_L ($054600, +$10=$46)
   473 5DED D8			        dta >$D880  ; caco_fire1 ($01E880, +$10=$E8)
   474 5DEE D8			        dta >$D8C0  ; caco_fire2 ($01E8C0, +$10=$E8)
   475
   476 				; Sprite VRAM bank table (high byte of 24-bit VRAM address)
   477 				; $01 = banks $10-$1F (VRAM $01xxxx), $03 = banks $30+ (VRAM $03xxxx)
   478 5DEF			spr_off_bank
   479 5DEF 01			        dta $01  ; player_idle
   480 5DF0 01			        dta $01  ; player_walk1
   481 5DF1 01			        dta $01  ; player_walk2
   482 5DF2 01			        dta $01  ; player_walk3
   483 5DF3 01			        dta $01  ; player_shoot
   484 5DF4 01			        dta $01  ; player_death1
   485 5DF5 01			        dta $01  ; zombie_idle
   486 5DF6 01			        dta $01  ; zombie_walk1
   487 5DF7 01			        dta $01  ; zombie_walk2
   488 5DF8 01			        dta $01  ; zombie_walk3
   489 5DF9 01			        dta $01  ; zombie_shoot
   490 5DFA 01			        dta $01  ; zombie_death1
   491 5DFB 01			        dta $01  ; imp_idle
   492 5DFC 01			        dta $01  ; imp_walk1
   493 5DFD 01			        dta $01  ; imp_walk2
   494 5DFE 01			        dta $01  ; imp_walk3
   495 5DFF 01			        dta $01  ; imp_shoot
   496 5E00 01			        dta $01  ; imp_death1
   497 5E01 01			        dta $01  ; pinky_idle
   498 5E02 01			        dta $01  ; pinky_walk1
   499 5E03 01			        dta $01  ; pinky_walk2
   500 5E04 01			        dta $01  ; pinky_walk3
   501 5E05 01			        dta $01  ; pinky_attack
   502 5E06 01			        dta $01  ; pinky_death1
   503 5E07 01			        dta $01  ; caco_idle
   504 5E08 01			        dta $01  ; caco_walk1
   505 5E09 01			        dta $01  ; caco_walk2
   506 5E0A 01			        dta $01  ; caco_walk3
   507 5E0B 01			        dta $01  ; caco_shoot
   508 5E0C 01			        dta $01  ; caco_death1
   509 5E0D 01			        dta $01  ; shotgun_idle
   510 5E0E 01			        dta $01  ; shotgun_walk1
   511 5E0F 01			        dta $01  ; shotgun_walk2
   512 5E10 01			        dta $01  ; shotgun_walk3
   513 5E11 01			        dta $01  ; shotgun_shoot
   514 5E12 01			        dta $01  ; shotgun_death1
   515 5E13 01			        dta $01  ; baron_idle
   516 5E14 01			        dta $01  ; baron_walk1
   517 5E15 01			        dta $01  ; baron_walk2
   518 5E16 01			        dta $01  ; baron_walk3
   519 5E17 01			        dta $01  ; baron_shoot
   520 5E18 01			        dta $01  ; baron_death1
   521 5E19 01			        dta $01  ; projectile1
   522 5E1A 01			        dta $01  ; projectile2
   523 5E1B 01			        dta $01  ; medikit
   524 5E1C 01			        dta $01  ; stimpack
   525 5E1D 01			        dta $01  ; health_bonus
   526 5E1E 01			        dta $01  ; ammo_clip
   527 5E1F 01			        dta $01  ; greenarmor
   528 5E20 01			        dta $01  ; bluearmor
   529 5E21 01			        dta $01  ; soulsphere
   530 5E22 01			        dta $01  ; keyred
   531 5E23 01			        dta $01  ; keyblue
   532 5E24 01			        dta $01  ; keyyellow
   533 5E25 01			        dta $01  ; shotgunpk
   534 5E26 01			        dta $01  ; shells
   535 5E27 01			        dta $01  ; barrel
   536 5E28 01			        dta $01  ; pillar
   537 5E29 01			        dta $01  ; torch
   538 5E2A 01			        dta $01  ; lamp
   539 5E2B 01			        dta $01  ; player_idle_L
   540 5E2C 01			        dta $01  ; player_walk1_L
   541 5E2D 01			        dta $01  ; player_walk2_L
   542 5E2E 01			        dta $01  ; player_walk3_L
   543 5E2F 01			        dta $01  ; player_shoot_L
   544 5E30 01			        dta $01  ; player_death1_L
   545 5E31 01			        dta $01  ; zombie_idle_L
   546 5E32 01			        dta $01  ; zombie_walk1_L
   547 5E33 01			        dta $01  ; zombie_walk2_L
   548 5E34 01			        dta $01  ; zombie_walk3_L
   549 5E35 01			        dta $01  ; zombie_shoot_L
   550 5E36 01			        dta $01  ; zombie_death1_L
   551 5E37 01			        dta $01  ; imp_idle_L
   552 5E38 01			        dta $01  ; imp_walk1_L
   553 5E39 01			        dta $01  ; imp_walk2_L
   554 5E3A 01			        dta $01  ; imp_walk3_L
   555 5E3B 01			        dta $01  ; imp_shoot_L
   556 5E3C 01			        dta $01  ; imp_death1_L
   557 5E3D 01			        dta $01  ; pinky_idle_L
   558 5E3E 01			        dta $01  ; pinky_walk1_L
   559 5E3F 01			        dta $01  ; pinky_walk2_L
   560 5E40 01			        dta $01  ; pinky_walk3_L
   561 5E41 01			        dta $01  ; pinky_attack_L
   562 5E42 01			        dta $01  ; pinky_death1_L
   563 5E43 01			        dta $01  ; caco_idle_L
   564 5E44 01			        dta $01  ; caco_walk1_L
   565 5E45 01			        dta $01  ; caco_walk2_L
   566 5E46 01			        dta $01  ; caco_walk3_L
   567 5E47 01			        dta $01  ; caco_shoot_L
   568 5E48 01			        dta $01  ; caco_death1_L
   569 5E49 01			        dta $01  ; shotgun_idle_L
   570 5E4A 01			        dta $01  ; shotgun_walk1_L
   571 5E4B 01			        dta $01  ; shotgun_walk2_L
   572 5E4C 01			        dta $01  ; shotgun_walk3_L
   573 5E4D 01			        dta $01  ; shotgun_shoot_L
   574 5E4E 01			        dta $01  ; shotgun_death1_L
   575 5E4F 01			        dta $01  ; baron_idle_L
   576 5E50 01			        dta $01  ; baron_walk1_L
   577 5E51 01			        dta $01  ; baron_walk2_L
   578 5E52 01			        dta $01  ; baron_walk3_L
   579 5E53 01			        dta $01  ; baron_shoot_L
   580 5E54 01			        dta $01  ; baron_death1_L
   581 5E55 01			        dta $01  ; chaingunpk
   582 5E56 01			        dta $01  ; rocketpk
   583 5E57 01			        dta $01  ; rocket_proj
   584 5E58 01			        dta $01  ; rocketbox
   585 5E59 03			        dta $03  ; zombie_death2 (VRAM $03xxxx)
   586 5E5A 03			        dta $03  ; zombie_death3 (VRAM $03xxxx)
   587 5E5B 03			        dta $03  ; zombie_gib1
   588 5E5C 03			        dta $03  ; zombie_gib2
   589 5E5D 03			        dta $03  ; imp_gib1
   590 5E5E 03			        dta $03  ; imp_gib2
   591 5E5F 03			        dta $03  ; shotgun_gib1
   592 5E60 03			        dta $03  ; shotgun_gib2
   593 5E61 01			        dta $01  ; imp_death2 (moved to c6, VRAM $01FC00)
   594 5E62 01			        dta $01  ; imp_death3 (moved to c6, VRAM $01FE00)
   595 5E63 03			        dta $03  ; shotgun_death2
   596 5E64 03			        dta $03  ; shotgun_death3
   597 5E65 03			        dta $03  ; pinky_death2
   598 5E66 03			        dta $03  ; pinky_death3
   599 5E67 03			        dta $03  ; caco_death2
   600 5E68 03			        dta $03  ; caco_death3
   601 5E69 03			        dta $03  ; baron_death2
   602 5E6A 03			        dta $03  ; baron_death3
   603 5E6B 03			        dta $03  ; barrel_exp1
   604 5E6C 03			        dta $03  ; barrel_exp2
   605 5E6D 03			        dta $03  ; barrel_exp3
   606 5E6E 01			        dta $01  ; plasmagunpk (VRAM $01xxxx)
   607 5E6F 01			        dta $01  ; cellspk (VRAM $01xxxx)
   608 5E70 01			        dta $01  ; plasma_proj1
   609 5E71 01			        dta $01  ; plasma_proj2
   610 5E72 01			        dta $01  ; bfg_proj1
   611 5E73 01			        dta $01  ; bfg_proj2
   612 5E74 01			        dta $01  ; bfgpk
   613 5E75 01			        dta $01  ; rocket1pk
   614 5E76 01			        dta $01  ; armorbonus
   615 5E77 01			        dta $01  ; pl_death2
   616 5E78 01			        dta $01  ; pl_death3
   617 5E79 01			        dta $01  ; imp_fire1
   618 5E7A 01			        dta $01  ; imp_fire2
   619 5E7B 05			        dta $05  ; pl_pain (VRAM $05xxxx)
   620 5E7C 05			        dta $05  ; pl_pain_L (VRAM $05xxxx)
   621 5E7D 05			        dta $05  ; zombie_pain
   622 5E7E 05			        dta $05  ; zombie_pain_L
   623 5E7F 01			        dta $01  ; caco_fire1 (VRAM $01xxxx)
   624 5E80 01			        dta $01  ; caco_fire2 (VRAM $01xxxx)
   625
   626 				; spr_w/spr_h moved to $A000 segment in main.asm
   627
   628 				; Enemy base sprite + HP tables
   629 5E81			en_base_spr
   630 5E81 06			        dta 6  ; zombie
   631 5E82 0C			        dta 12  ; imp
   632 5E83 12			        dta 18  ; pinky
   633 5E84 18			        dta 24  ; caco
   634 5E85 1E			        dta 30  ; shotgun
   635 5E86 24			        dta 36  ; baron
   636
   637 5E87			en_hp_tab
   638 5E87 03			        dta 3  ; zombie
   639 5E88 05			        dta 5  ; imp
   640 5E89 08			        dta 8  ; pinky
   641 5E8A 0C			        dta 12  ; caco
   642 5E8B 06			        dta 6  ; shotgun
   643 5E8C 14			        dta 20  ; baron
   644
   645 5E8D			en_speed_tab
   646 5E8D 02			        dta 2  ; zombie
   647 5E8E 02			        dta 2  ; imp
   648 5E8F 03			        dta 3  ; pinky (melee - must catch player)
   649 5E90 02			        dta 2  ; caco
   650 5E91 02			        dta 2  ; shotgun
   651 5E92 02			        dta 2  ; baron
   652
   653 				; Gib sprite lookup (indexed by enemy type, 2 frames per type)
   654 				; Death frame 2 lookup (indexed by enemy type, 0=use death1)
   655 5E93			en_death2_spr
   656 5E93 6A			        dta SPR_ZOMBIE_DEATH2  ; zombie
   657 5E94 72			        dta SPR_IMP_DEATH2     ; imp
   658 5E95 76			        dta SPR_PINKY_DEATH2   ; pinky
   659 5E96 78			        dta SPR_CACO_DEATH2    ; caco
   660 5E97 74			        dta SPR_SHOTGUN_DEATH2 ; shotgun
   661 5E98 7A			        dta SPR_BARON_DEATH2   ; baron
   662
   663 				; Death frame 3 lookup (indexed by enemy type, 0=use death1)
   664 5E99			en_death3_spr
   665 5E99 6B			        dta SPR_ZOMBIE_DEATH3  ; zombie
   666 5E9A 73			        dta SPR_IMP_DEATH3     ; imp
   667 5E9B 77			        dta SPR_PINKY_DEATH3   ; pinky
   668 5E9C 79			        dta SPR_CACO_DEATH3    ; caco
   669 5E9D 75			        dta SPR_SHOTGUN_DEATH3 ; shotgun
   670 5E9E 7B			        dta SPR_BARON_DEATH3   ; baron
   671
   672 				; Gib sprite lookup (indexed by enemy type)
   673 5E9F			en_gib1_spr
   674 5E9F 6C			        dta SPR_ZOMBIE_GIB1    ; zombie
   675 5EA0 6E			        dta SPR_IMP_GIB1       ; imp
   676 5EA1 6C			        dta SPR_ZOMBIE_GIB1    ; pinky (reuse zombie)
   677 5EA2 6C			        dta SPR_ZOMBIE_GIB1    ; caco (reuse zombie)
   678 5EA3 70			        dta SPR_SHOTGUN_GIB1   ; shotgun
   679 5EA4 6C			        dta SPR_ZOMBIE_GIB1    ; baron (reuse zombie)
   680
   681 5EA5			en_gib2_spr
   682 5EA5 6D			        dta SPR_ZOMBIE_GIB2    ; zombie
   683 5EA6 6F			        dta SPR_IMP_GIB2       ; imp
   684 5EA7 6D			        dta SPR_ZOMBIE_GIB2    ; pinky (reuse zombie)
   685 5EA8 6D			        dta SPR_ZOMBIE_GIB2    ; caco (reuse zombie)
   686 5EA9 71			        dta SPR_SHOTGUN_GIB2   ; shotgun
   687 5EAA 6D			        dta SPR_ZOMBIE_GIB2    ; baron (reuse zombie)
   688
   689 				; Enemy sound lookup tables (indexed by type, $FF = no sound)
   690 5EAB			en_death_sfx
   691 5EAB 03			        dta SFX_PODEATH    ; zombie
   692 5EAC 06			        dta SFX_IMPDEATH   ; imp
   693 5EAD 0F			        dta SFX_SGTDTH     ; pinky
   694 5EAE 11			        dta SFX_CACDTH     ; caco
   695 5EAF 03			        dta SFX_PODEATH    ; shotgun (same as zombie)
   696 5EB0 0D			        dta SFX_BRSDTH     ; baron
   697
   698 				; Pain sprite lookup (indexed by enemy type, R variant, add 1 for L)
   699 5EB1			en_pain_spr
   700 5EB1 8E			        dta SPR_ZOMBIE_PAIN    ; zombie
   701 5EB2 00			        dta 0                  ; imp (TODO)
   702 5EB3 00			        dta 0                  ; pinky (TODO)
   703 5EB4 00			        dta 0                  ; caco (TODO)
   704 5EB5 00			        dta 0                  ; shotgun (TODO - same as zombie)
   705 5EB6 00			        dta 0                  ; baron (TODO)
   706
   707 5EB7			en_sight_sfx
   708 5EB7 04			        dta SFX_POSIGHT    ; zombie
   709 5EB8 05			        dta SFX_IMPSIGHT   ; imp
   710 5EB9 0E			        dta SFX_SGTSIT     ; pinky
   711 5EBA 10			        dta SFX_CACSIT     ; caco
   712 5EBB 04			        dta SFX_POSIGHT    ; shotgun (same as zombie)
   713 5EBC 0C			        dta SFX_BRSSIT     ; baron
   714
   715 				; Pickup type -> sprite index
   716 5EBD			pk_spr_tab
   717 5EBD 2D			        dta 45  ; PK_HEALTH -> stimpack
   718 5EBE 2F			        dta 47  ; PK_AMMO -> ammo_clip
   719 5EBF 2C			        dta 44  ; PK_MEDIKIT -> medikit
   720 5EC0 30			        dta 48  ; PK_GREENARMOR -> greenarmor
   721 5EC1 31			        dta 49  ; PK_BLUEARMOR  -> bluearmor
   722 5EC2 32			        dta 50  ; PK_SOULSPHERE -> soulsphere
   723 5EC3 33			        dta 51  ; PK_KEYRED     -> keyred
   724 5EC4 34			        dta 52  ; PK_KEYBLUE    -> keyblue
   725 5EC5 35			        dta 53  ; PK_KEYYELLOW  -> keyyellow
   726 5EC6 36			        dta 54  ; PK_SHOTGUNPK  -> shotgunpk
   727 5EC7 37			        dta 55  ; PK_SHELLS     -> shells
   728 5EC8 2F			        dta 47  ; PK_PISTOL     -> ammo_clip (temp, reuse)
   729 5EC9 66			        dta 102 ; PK_CHAINGUN   -> chaingunpk
   730 5ECA 67			        dta 103 ; PK_ROCKETL    -> rocketpk
   731 5ECB 69			        dta 105 ; PK_ROCKETBOX  -> rocketbox
   732 5ECC 7F			        dta SPR_PLASMAGUNPK  ; PK_PLASMAGUN
   733 5ECD 80			        dta SPR_CELLSPK      ; PK_CELLS
   734 5ECE 85			        dta SPR_BFGPK        ; PK_BFG
   735 5ECF 86			        dta SPR_ROCKET1PK   ; PK_ROCKET1
   736 5ED0 2E			        dta 46              ; PK_HEALTHBONUS -> health_bonus
   737 5ED1 87			        dta SPR_ARMORBONUS  ; PK_ARMORBONUS
   738
   739 				; Tile indices
   740 = 0000			TILE_EMPTY                equ   0  ; offset $0000
   741 = 0001			TILE_WALL                 equ   1  ; offset $0100
   742 = 0002			TILE_FLOOR                equ   2  ; offset $0200
   743 = 0003			TILE_CEILING              equ   3  ; offset $0300
   744 = 0004			TILE_DOOR                 equ   4  ; offset $0400
   745 = 0005			TILE_SKY                  equ   5  ; offset $0500
   746 = 0006			TILE_DARKBG               equ   6  ; offset $0600
   747 = 0007			TILE_TECHWALL             equ   7  ; offset $0700
   748 = 0008			TILE_METALWALL            equ   8  ; offset $0800
   749 = 0009			TILE_SUPPORT              equ   9  ; offset $0900
   750 = 000A			TILE_STONEWALL            equ  10  ; offset $0A00
   751 = 000B			TILE_RESERVED_11          equ  11  ; offset $0B00
   752 = 000C			TILE_RESERVED_12          equ  12  ; offset $0C00
   753 = 000D			TILE_RESERVED_13          equ  13  ; offset $0D00
   754 = 000E			TILE_RESERVED_14          equ  14  ; offset $0E00
   755 = 000F			TILE_RESERVED_15          equ  15  ; offset $0F00
   756 = 1000			TILESHEET_SIZE           equ $1000
    10
    11 				; ============================================
    12 				; MAP DATA - stored in VRAM bank $1E, accessed via MEMAC-A at $9000
    13 				; ============================================
    14 = 9000			map_data = MEMW              ; $9000 (MEMAC window = VRAM $01E000+)
    15
    16 				; Entity spawn data moved to $A000 segment in main.asm
    17
    18 				; ============================================
    19 				; BINARY DATA
    20 				; ============================================
    21 				; palette_data moved to INI segment in main.asm (saves 768B RAM)
    22 				; tilesheet_data moved to INI segment in main.asm (saves 4KB RAM)
    23 				; hud_font_data moved to INI segment in main.asm (saves 768B RAM)
    24
    25 				; Y-ADDRESS LUT is at $A000 in a separate segment (see end of main.asm)
    26
    27 				; Spritesheet is loaded via multi-segment (see main.asm)
    28 				; NOT included here - would exceed $C000 RAM limit
   327
   328 				; --- Asset uploads, init code, LUT tables ---
   329 5ED2			        icl 'uploads.asm'
Source: uploads.asm
     1 				;==============================================
     2 				; DOOM2D - Asset uploads (VRAM: tiles, sprites, sky, sounds, title)
     3 				; uploads.asm
     4 				;==============================================
     5
     6 				;==============================================
     7 				; GENERIC CHUNK UPLOAD (own segment at $0480, persists during all INI uploads)
     8 				; Params: zsrc=source, uc_bank=start bank, uc_cnt=bank count, uc_lastpg=pages in last bank
     9 				;==============================================
    10 5ED2			        org $0480
    11 0480			.proc generic_upload
    12 0480-04D0> A9 98		        lda #$90+MC_CPU
    13 0482 8D 5E D6		        sta VBXE_MEMAC_CTRL
    14 0485 A9 00		        lda #0
    15 0487 8D 0E D4		        sta $D40E
    16 048A AD 01 D3		        lda $D301
    17 048D 29 FC		        and #$FC
    18 048F 8D 01 D3		        sta $D301
    19
    20 0492 A5 BB		?nxbank lda uc_bank
    21 0494 8D 5F D6		        sta VBXE_BANK_SEL
    22 0497 A9 90		        lda #>MEMW
    23 0499 8D AC 04		        sta ?wr+2
    24 049C A2 10		        ldx #16             ; default: full 16 pages per bank
    25 049E A5 BC		        lda uc_cnt
    26 04A0 C9 01		        cmp #1
    27 04A2 D0 02		        bne ?full
    28 04A4 A6 BD		        ldx uc_lastpg       ; last bank may be partial
    29 04A6			?full
    30 04A6 A0 00		?page   ldy #0
    31 04A8 B1 82		?lp     lda (zsrc),y
    32 04AA 99 00 90		?wr     sta MEMW,y
    33 04AD C8			        iny
    34 04AE D0 F8		        bne ?lp
    35 04B0 E6 83		        inc zsrc+1
    36 04B2 EE AC 04		        inc ?wr+2
    37 04B5 CA			        dex
    38 04B6 D0 EE		        bne ?page
    39 04B8 E6 BB		        inc uc_bank
    40 04BA C6 BC		        dec uc_cnt
    41 04BC D0 D4		        bne ?nxbank
    42
    43 				        ; Disable MEMAC bank before returning
    44 04BE A9 00		        lda #0
    45 04C0 8D 5F D6		        sta VBXE_BANK_SEL
    46
    47 04C3 AD 01 D3		        lda $D301
    48 04C6 09 03		        ora #$03
    49 04C8 8D 01 D3		        sta $D301
    50 04CB A9 40		        lda #$40
    51 04CD 8D 0E D4		        sta $D40E
    52 04D0 60			        rts
    53 				.endp
    54
    55 				;==============================================
    56 				; MAP + FIREBALL UPLOAD (bank $1E: map 2048B + imp 128B + caco 128B)
    57 				;==============================================
    58 04D1			        org $6000
    59 6000			map_bin_data
    60 6000-68FF> 18 18 18 18 +         ins '../data/test_map.bin'
    61 6800 00 00 B9 B6 B8 B6 +         ins '../data/imp_fireball.bin'
    62 6880 00 00 00 00 FE 00 +         ins '../data/caco_fireball.bin'
    63
    64 6900			        org $0580
    65 0580			.proc upload_map
    66 0580-0596> A9 00		        lda #<map_bin_data
    67 0582 85 82		        sta zsrc
    68 0584 A9 60		        lda #>map_bin_data
    69 0586 85 83		        sta zsrc+1
    70 0588 A9 9E		        lda #BANK_EN+BANK_MAP
    71 058A 85 BB		        sta uc_bank
    72 058C A9 01		        lda #1
    73 058E 85 BC		        sta uc_cnt
    74 0590 A9 0A		        lda #10                 ; 2304 bytes (map 2048 + imp 128 + caco 128) = 9 full + partial = 10 pages
    75 0592 85 BD		        sta uc_lastpg
    76 0594 4C 80 04		        jmp generic_upload
    77 				.endp
    78 02E2-02E3> 80 05		        ini upload_map
    79
    80 				;==============================================
    81 				; PALETTE SETUP (768 bytes, loaded at $6000, written to VBXE palette regs)
    82 				;==============================================
    83 0597			        org $6000
    84 6000			palette_data
    85 6000-62FF> 00 0F 0B 25 +         ins '../data/palette.bin'
    86
    87 6300			        org $0580
    88 0580			.proc upload_palette
    89 0580-05A4> A9 00		        lda #0
    90 0582 8D 44 D6		        sta VBXE_CSEL
    91 0585 A9 01		        lda #1
    92 0587 8D 45 D6		        sta VBXE_PSEL
    93 058A A2 00		        ldx #0
    94 058C BD 00 60		?lp     lda palette_data,x
    95 058F 0A			        asl                     ; *2: expand 7-bit (0-127) to 8-bit (0-254)
    96 0590 8D 46 D6		        sta VBXE_CR
    97 0593 BD 00 61		        lda palette_data+256,x
    98 0596 0A			        asl
    99 0597 8D 47 D6		        sta VBXE_CG
   100 059A BD 00 62		        lda palette_data+512,x
   101 059D 0A			        asl
   102 059E 8D 48 D6		        sta VBXE_CB
   103 05A1 E8			        inx
   104 05A2 D0 E8		        bne ?lp
   105 05A4 60			        rts
   106 				.endp
   107 02E2-02E3> 80 05		        ini upload_palette
   108
   109 				;==============================================
   110 				; TILESHEET UPLOAD (4096 bytes, bank $10)
   111 				; Loaded at $6000, uploaded to VRAM, RAM freed
   112 				;==============================================
   113 05A5			        org $6000
   114 6000			tilesheet_data
   115 6000-6FFF> 00 00 00 00 +         ins '../data/tilesheet.bin'
   116
   117 7000			        org $0580
   118 0580			.proc upload_tilesheet
   119 0580-0597> A9 00		        lda #<tilesheet_data
   120 0582 85 82		        sta zsrc
   121 0584 A9 60		        lda #>tilesheet_data
   122 0586 85 83		        sta zsrc+1
   123 0588 A9 D5		        lda #BANK_EN+BANK_TILES
   124 058A 85 BB		        sta uc_bank
   125 058C A9 01		        lda #1
   126 058E 85 BC		        sta uc_cnt
   127 0590 A9 10		        lda #16
   128 0592 85 BD		        sta uc_lastpg
   129 0594 20 80 04		        jsr generic_upload
   130 0597 60			        rts
   131 				.endp
   132 02E2-02E3> 80 05		        ini upload_tilesheet
   133
   134 				;==============================================
   135 				; Door tiles 25-27 are embedded in spritesheet_c5.bin at VRAM $032900-$032BFF
   136 				;==============================================
   137
   138 				;==============================================
   139 				; HUD FONT UPLOAD (2432 bytes = 10 pages, bank $1D)
   140 				; Chars: 0-9, heart, bullet, A-Z
   141 				;==============================================
   142 0598			        org $6000
   143 6000			hud_font_data
   144 6000-69BF> 00 B0 B0 B0 +         ins '../data/hud_font.bin'
   145
   146 69C0			        org $0580
   147 0580			.proc upload_hud_font_ini
   148 0580-0597> A9 00		        lda #<hud_font_data
   149 0582 85 82		        sta zsrc
   150 0584 A9 60		        lda #>hud_font_data
   151 0586 85 83		        sta zsrc+1
   152 0588 A9 9D		        lda #BANK_EN+BANK_HUD
   153 058A 85 BB		        sta uc_bank
   154 058C A9 01		        lda #1
   155 058E 85 BC		        sta uc_cnt
   156 0590 A9 0A		        lda #10
   157 0592 85 BD		        sta uc_lastpg
   158 0594 20 80 04		        jsr generic_upload
   159 0597 60			        rts
   160 				.endp
   161 02E2-02E3> 80 05		        ini upload_hud_font_ini
   162
   163 				;==============================================
   164 				; MULTI-SEGMENT SPRITESHEET LOADING
   165 				; 4 chunks of max 12KB each (loaded at $6000-$8FFF, never hits $9000 MEMAC)
   166 				;==============================================
   167
   168 				; --- Chunk 1: 12288 bytes (3 banks: $11-$13) ---
   169 0598			        org $6000
   170 6000			spritesheet_c1
   171 6000-8FFF> 00 00 00 00 +         ins '../data/spritesheet_c1.bin'
   172
   173 9000			        org $0580
   174 0580			.proc upload_chunk1
   175 0580-0597> A9 00		        lda #<spritesheet_c1
   176 0582 85 82		        sta zsrc
   177 0584 A9 60		        lda #>spritesheet_c1
   178 0586 85 83		        sta zsrc+1
   179 0588 A9 91		        lda #BANK_EN+BANK_SPR0
   180 058A 85 BB		        sta uc_bank
   181 058C A9 03		        lda #3
   182 058E 85 BC		        sta uc_cnt
   183 0590 A9 10		        lda #16
   184 0592 85 BD		        sta uc_lastpg
   185 0594 20 80 04		        jsr generic_upload
   186 0597 60			        rts
   187 				.endp
   188 02E2-02E3> 80 05		        ini upload_chunk1
   189
   190 				; --- Chunk 2: 12288 bytes (3 banks: $14-$16) ---
   191 0598			        org $6000
   192 6000			spritesheet_c2
   193 6000-8FFF> 00 00 00 00 +         ins '../data/spritesheet_c2.bin'
   194
   195 9000			        org $0580
   196 0580			.proc upload_chunk2
   197 0580-0597> A9 00		        lda #<spritesheet_c2
   198 0582 85 82		        sta zsrc
   199 0584 A9 60		        lda #>spritesheet_c2
   200 0586 85 83		        sta zsrc+1
   201 0588 A9 94		        lda #BANK_EN+$14
   202 058A 85 BB		        sta uc_bank
   203 058C A9 03		        lda #3
   204 058E 85 BC		        sta uc_cnt
   205 0590 A9 10		        lda #16
   206 0592 85 BD		        sta uc_lastpg
   207 0594 20 80 04		        jsr generic_upload
   208 0597 60			        rts
   209 				.endp
   210 02E2-02E3> 80 05		        ini upload_chunk2
   211
   212 				; --- Chunk 3: 12288 bytes (3 banks: $17-$19) ---
   213 0598			        org $6000
   214 6000			spritesheet_c3
   215 6000-8FFF> 00 00 00 00 +         ins '../data/spritesheet_c3.bin'
   216
   217 9000			        org $0580
   218 0580			.proc upload_chunk3
   219 0580-0597> A9 00		        lda #<spritesheet_c3
   220 0582 85 82		        sta zsrc
   221 0584 A9 60		        lda #>spritesheet_c3
   222 0586 85 83		        sta zsrc+1
   223 0588 A9 97		        lda #BANK_EN+$17
   224 058A 85 BB		        sta uc_bank
   225 058C A9 03		        lda #3
   226 058E 85 BC		        sta uc_cnt
   227 0590 A9 10		        lda #16
   228 0592 85 BD		        sta uc_lastpg
   229 0594 20 80 04		        jsr generic_upload
   230 0597 60			        rts
   231 				.endp
   232 02E2-02E3> 80 05		        ini upload_chunk3
   233
   234 				; --- Chunk 4: 11264 bytes (2 full banks $1A-$1B + 12 pages in $1C) ---
   235 0598			        org $6000
   236 6000			spritesheet_c4
   237 6000-8FFF> 00 00 00 5F +         ins '../data/spritesheet_c4.bin'
   238
   239 9000			        org $0580
   240 0580			.proc upload_chunk4
   241 0580-0597> A9 00		        lda #<spritesheet_c4
   242 0582 85 82		        sta zsrc
   243 0584 A9 60		        lda #>spritesheet_c4
   244 0586 85 83		        sta zsrc+1
   245 0588 A9 9A		        lda #BANK_EN+$1A
   246 058A 85 BB		        sta uc_bank
   247 058C A9 03		        lda #3
   248 058E 85 BC		        sta uc_cnt
   249 0590 A9 10		        lda #16             ; last bank: 16 pages (12288 bytes, chunk4 full)
   250 0592 85 BD		        sta uc_lastpg
   251 0594 20 80 04		        jsr generic_upload
   252 0597 60			        rts
   253 				.endp
   254 02E2-02E3> 80 05		        ini upload_chunk4
   255
   256 				; --- Chunk 5: new death sprites (bank $31 = VRAM $031000+) ---
   257 0598			        org $6000
   258 6000			spritesheet_c5
   259 6000-8FFF> 00 00 00 00 +         ins '../data/spritesheet_c5.bin'
   260
   261 9000			        org $0580
   262 0580			.proc upload_chunk5
   263 0580-0597> A9 00		        lda #<spritesheet_c5
   264 0582 85 82		        sta zsrc
   265 0584 A9 60		        lda #>spritesheet_c5
   266 0586 85 83		        sta zsrc+1
   267 0588 A9 B1		        lda #BANK_EN+$31
   268 058A 85 BB		        sta uc_bank
   269 058C A9 03		        lda #3              ; 3 banks: $31 + $32 + $33
   270 058E 85 BC		        sta uc_cnt
   271 0590 A9 10		        lda #16             ; last bank: full 16 pages
   272 0592 85 BD		        sta uc_lastpg
   273 0594 20 80 04		        jsr generic_upload
   274 0597 60			        rts
   275 				.endp
   276 02E2-02E3> 80 05		        ini upload_chunk5
   277
   278 				;==============================================
   279 				; SWITCH TILES 28-29 at VRAM $060000 (bank $60)
   280 				; Own chunk, no conflicts
   281 				;==============================================
   282 0598			        org $6000
   283 6000			switch_tiles
   284 6000-61FF> 00 00 00 00 +         ins '../data/switch_off.bin'
   285 6100 00 00 00 00 00 00 +         ins '../data/switch_on.bin'
   286 6200			        org $0580
   287 0580			.proc upload_switch_tiles
   288 0580-0597> A9 00		        lda #<switch_tiles
   289 0582 85 82		        sta zsrc
   290 0584 A9 60		        lda #>switch_tiles
   291 0586 85 83		        sta zsrc+1
   292 0588 A9 E0		        lda #BANK_EN+$60
   293 058A 85 BB		        sta uc_bank
   294 058C A9 01		        lda #1
   295 058E 85 BC		        sta uc_cnt
   296 0590 A9 02		        lda #2
   297 0592 85 BD		        sta uc_lastpg
   298 0594 20 80 04		        jsr generic_upload
   299 0597 60			        rts
   300 				.endp
   301 02E2-02E3> 80 05		        ini upload_switch_tiles
   302
   303 				; --- Chunk 6: plasma/cells pickup sprites (bank $1F, 2 pages) ---
   304 0598			        org $6000
   305 6000			spritesheet_c6
   306 6000-6FFF> 00 00 00 00 +         ins '../data/spritesheet_c6.bin'
   307
   308 7000			        org $0580
   309 0580			.proc upload_chunk6
   310 0580-0597> A9 00		        lda #<spritesheet_c6
   311 0582 85 82		        sta zsrc
   312 0584 A9 60		        lda #>spritesheet_c6
   313 0586 85 83		        sta zsrc+1
   314 0588 A9 9F		        lda #BANK_EN+$1F
   315 058A 85 BB		        sta uc_bank
   316 058C A9 01		        lda #1
   317 058E 85 BC		        sta uc_cnt
   318 0590 A9 10		        lda #16
   319 0592 85 BD		        sta uc_lastpg
   320 0594 20 80 04		        jsr generic_upload
   321 0597 60			        rts
   322 				.endp
   323 02E2-02E3> 80 05		        ini upload_chunk6
   324
   325 				; --- Chunk 7: player pain sprites (bank $54 = VRAM $054000) ---
   326 0598			        org $6000
   327 6000			spritesheet_c7
   328 6000-67FF> 00 00 00 00 +         ins '../data/pain_sprites.bin'
   329
   330 6800			        org $0580
   331 0580			.proc upload_chunk7
   332 0580-0597> A9 00		        lda #<spritesheet_c7
   333 0582 85 82		        sta zsrc
   334 0584 A9 60		        lda #>spritesheet_c7
   335 0586 85 83		        sta zsrc+1
   336 0588 A9 D4		        lda #BANK_EN+$54
   337 058A 85 BB		        sta uc_bank
   338 058C A9 01		        lda #1
   339 058E 85 BC		        sta uc_cnt
   340 0590 A9 08		        lda #8                  ; 2048 bytes = 8 pages
   341 0592 85 BD		        sta uc_lastpg
   342 0594 20 80 04		        jsr generic_upload
   343 0597 60			        rts
   344 				.endp
   345 02E2-02E3> 80 05		        ini upload_chunk7
   346
   347 				;==============================================
   348 				; BACKGROUND SKY UPLOAD (512x200 = 102400 bytes, VRAM $034000+)
   349 				; 9 chunks: 8x12KB + 1x4KB
   350 				;==============================================
   351
   352 				; --- Sky chunk 1: 12288 bytes (banks $34-$36) ---
   353 0598			        org $6000
   354 6000-8FFF> A4 A4 A4 A4 + sky_c1  ins '../data/sky_c1.bin'
   355 9000			        org $0580
   356 0580			.proc upload_sky1
   357 0580-0597> A9 00		        lda #<sky_c1
   358 0582 85 82		        sta zsrc
   359 0584 A9 60		        lda #>sky_c1
   360 0586 85 83		        sta zsrc+1
   361 0588 A9 B4		        lda #BANK_EN+BANK_BG
   362 058A 85 BB		        sta uc_bank
   363 058C A9 03		        lda #3
   364 058E 85 BC		        sta uc_cnt
   365 0590 A9 10		        lda #16
   366 0592 85 BD		        sta uc_lastpg
   367 0594 20 80 04		        jsr generic_upload
   368 0597 60			        rts
   369 				.endp
   370 02E2-02E3> 80 05		        ini upload_sky1
   371
   372 				; --- Sky chunk 2: 12288 bytes (banks $37-$39) ---
   373 0598			        org $6000
   374 6000-8FFF> 4F EE EE 4E + sky_c2  ins '../data/sky_c2.bin'
   375 9000			        org $0580
   376 0580			.proc upload_sky2
   377 0580-0597> A9 00		        lda #<sky_c2
   378 0582 85 82		        sta zsrc
   379 0584 A9 60		        lda #>sky_c2
   380 0586 85 83		        sta zsrc+1
   381 0588 A9 B7		        lda #BANK_EN+$37
   382 058A 85 BB		        sta uc_bank
   383 058C A9 03		        lda #3
   384 058E 85 BC		        sta uc_cnt
   385 0590 A9 10		        lda #16
   386 0592 85 BD		        sta uc_lastpg
   387 0594 20 80 04		        jsr generic_upload
   388 0597 60			        rts
   389 				.endp
   390 02E2-02E3> 80 05		        ini upload_sky2
   391
   392 				; --- Sky chunk 3: 12288 bytes (banks $3A-$3C) ---
   393 0598			        org $6000
   394 6000-8FFF> A5 43 44 44 + sky_c3  ins '../data/sky_c3.bin'
   395 9000			        org $0580
   396 0580			.proc upload_sky3
   397 0580-0597> A9 00		        lda #<sky_c3
   398 0582 85 82		        sta zsrc
   399 0584 A9 60		        lda #>sky_c3
   400 0586 85 83		        sta zsrc+1
   401 0588 A9 BA		        lda #BANK_EN+$3A
   402 058A 85 BB		        sta uc_bank
   403 058C A9 03		        lda #3
   404 058E 85 BC		        sta uc_cnt
   405 0590 A9 10		        lda #16
   406 0592 85 BD		        sta uc_lastpg
   407 0594 20 80 04		        jsr generic_upload
   408 0597 60			        rts
   409 				.endp
   410 02E2-02E3> 80 05		        ini upload_sky3
   411
   412 				; --- Sky chunk 4: 12288 bytes (banks $3D-$3F) ---
   413 0598			        org $6000
   414 6000-8FFF> D6 D6 D6 F8 + sky_c4  ins '../data/sky_c4.bin'
   415 9000			        org $0580
   416 0580			.proc upload_sky4
   417 0580-0597> A9 00		        lda #<sky_c4
   418 0582 85 82		        sta zsrc
   419 0584 A9 60		        lda #>sky_c4
   420 0586 85 83		        sta zsrc+1
   421 0588 A9 BD		        lda #BANK_EN+$3D
   422 058A 85 BB		        sta uc_bank
   423 058C A9 03		        lda #3
   424 058E 85 BC		        sta uc_cnt
   425 0590 A9 10		        lda #16
   426 0592 85 BD		        sta uc_lastpg
   427 0594 20 80 04		        jsr generic_upload
   428 0597 60			        rts
   429 				.endp
   430 02E2-02E3> 80 05		        ini upload_sky4
   431
   432 				; --- Sky chunk 5: 12288 bytes (banks $40-$42) ---
   433 0598			        org $6000
   434 6000-8FFF> F8 F8 F8 F8 + sky_c5  ins '../data/sky_c5.bin'
   435 9000			        org $0580
   436 0580			.proc upload_sky5
   437 0580-0597> A9 00		        lda #<sky_c5
   438 0582 85 82		        sta zsrc
   439 0584 A9 60		        lda #>sky_c5
   440 0586 85 83		        sta zsrc+1
   441 0588 A9 C0		        lda #BANK_EN+$40
   442 058A 85 BB		        sta uc_bank
   443 058C A9 03		        lda #3
   444 058E 85 BC		        sta uc_cnt
   445 0590 A9 10		        lda #16
   446 0592 85 BD		        sta uc_lastpg
   447 0594 20 80 04		        jsr generic_upload
   448 0597 60			        rts
   449 				.endp
   450 02E2-02E3> 80 05		        ini upload_sky5
   451
   452 				; --- Sky chunk 6: 12288 bytes (banks $43-$45) ---
   453 0598			        org $6000
   454 6000-8FFF> A4 A4 A4 44 + sky_c6  ins '../data/sky_c6.bin'
   455 9000			        org $0580
   456 0580			.proc upload_sky6
   457 0580-0597> A9 00		        lda #<sky_c6
   458 0582 85 82		        sta zsrc
   459 0584 A9 60		        lda #>sky_c6
   460 0586 85 83		        sta zsrc+1
   461 0588 A9 C3		        lda #BANK_EN+$43
   462 058A 85 BB		        sta uc_bank
   463 058C A9 03		        lda #3
   464 058E 85 BC		        sta uc_cnt
   465 0590 A9 10		        lda #16
   466 0592 85 BD		        sta uc_lastpg
   467 0594 20 80 04		        jsr generic_upload
   468 0597 60			        rts
   469 				.endp
   470 02E2-02E3> 80 05		        ini upload_sky6
   471
   472 				; --- Sky chunk 7: 12288 bytes (banks $46-$48) ---
   473 0598			        org $6000
   474 6000-8FFF> A4 A4 A4 A4 + sky_c7  ins '../data/sky_c7.bin'
   475 9000			        org $0580
   476 0580			.proc upload_sky7
   477 0580-0597> A9 00		        lda #<sky_c7
   478 0582 85 82		        sta zsrc
   479 0584 A9 60		        lda #>sky_c7
   480 0586 85 83		        sta zsrc+1
   481 0588 A9 C6		        lda #BANK_EN+$46
   482 058A 85 BB		        sta uc_bank
   483 058C A9 03		        lda #3
   484 058E 85 BC		        sta uc_cnt
   485 0590 A9 10		        lda #16
   486 0592 85 BD		        sta uc_lastpg
   487 0594 20 80 04		        jsr generic_upload
   488 0597 60			        rts
   489 				.endp
   490 02E2-02E3> 80 05		        ini upload_sky7
   491
   492 				; --- Sky chunk 8: 12288 bytes (banks $49-$4B) ---
   493 0598			        org $6000
   494 6000-8FFF> F8 F8 F8 F8 + sky_c8  ins '../data/sky_c8.bin'
   495 9000			        org $0580
   496 0580			.proc upload_sky8
   497 0580-0597> A9 00		        lda #<sky_c8
   498 0582 85 82		        sta zsrc
   499 0584 A9 60		        lda #>sky_c8
   500 0586 85 83		        sta zsrc+1
   501 0588 A9 C9		        lda #BANK_EN+$49
   502 058A 85 BB		        sta uc_bank
   503 058C A9 03		        lda #3
   504 058E 85 BC		        sta uc_cnt
   505 0590 A9 10		        lda #16
   506 0592 85 BD		        sta uc_lastpg
   507 0594 20 80 04		        jsr generic_upload
   508 0597 60			        rts
   509 				.endp
   510 02E2-02E3> 80 05		        ini upload_sky8
   511
   512 				; --- Sky chunk 9: 12288 bytes (banks $4C-$4E) ---
   513 0598			        org $6000
   514 6000-8FFF> A4 DA A4 3F + sky_c9  ins '../data/sky_c9.bin'
   515 9000			        org $0580
   516 0580			.proc upload_sky9
   517 0580-0597> A9 00		        lda #<sky_c9
   518 0582 85 82		        sta zsrc
   519 0584 A9 60		        lda #>sky_c9
   520 0586 85 83		        sta zsrc+1
   521 0588 A9 CC		        lda #BANK_EN+$4C
   522 058A 85 BB		        sta uc_bank
   523 058C A9 03		        lda #3
   524 058E 85 BC		        sta uc_cnt
   525 0590 A9 10		        lda #16
   526 0592 85 BD		        sta uc_lastpg
   527 0594 20 80 04		        jsr generic_upload
   528 0597 60			        rts
   529 				.endp
   530 02E2-02E3> 80 05		        ini upload_sky9
   531
   532 				; --- Sky chunk 10: 12288 bytes (banks $4F-$51) ---
   533 0598			        org $6000
   534 6000-8FFF> D6 D6 D6 D6 + sky_c10 ins '../data/sky_c10.bin'
   535 9000			        org $0580
   536 0580			.proc upload_sky10
   537 0580-0597> A9 00		        lda #<sky_c10
   538 0582 85 82		        sta zsrc
   539 0584 A9 60		        lda #>sky_c10
   540 0586 85 83		        sta zsrc+1
   541 0588 A9 CF		        lda #BANK_EN+$4F
   542 058A 85 BB		        sta uc_bank
   543 058C A9 03		        lda #3
   544 058E 85 BC		        sta uc_cnt
   545 0590 A9 10		        lda #16
   546 0592 85 BD		        sta uc_lastpg
   547 0594 20 80 04		        jsr generic_upload
   548 0597 60			        rts
   549 				.endp
   550 02E2-02E3> 80 05		        ini upload_sky10
   551
   552 				; --- Sky chunk 11: 8192 bytes (banks $52-$53) ---
   553 0598			        org $6000
   554 6000-7FFF> F8 F8 F8 F8 + sky_c11 ins '../data/sky_c11.bin'
   555 8000			        org $0580
   556 0580			.proc upload_sky11
   557 0580-0597> A9 00		        lda #<sky_c11
   558 0582 85 82		        sta zsrc
   559 0584 A9 60		        lda #>sky_c11
   560 0586 85 83		        sta zsrc+1
   561 0588 A9 D2		        lda #BANK_EN+$52
   562 058A 85 BB		        sta uc_bank
   563 058C A9 02		        lda #2
   564 058E 85 BC		        sta uc_cnt
   565 0590 A9 10		        lda #16
   566 0592 85 BD		        sta uc_lastpg
   567 0594 20 80 04		        jsr generic_upload
   568 0597 60			        rts
   569 				.endp
   570 02E2-02E3> 80 05		        ini upload_sky11
   571
   572 				;==============================================
   573 				; SOUND DATA UPLOAD TO VRAM ($061000+)
   574 				; Original .bin files concatenated, read via MEMAC-B
   575 				;==============================================
   576
   577 				; --- Sound chunk 1: 12288 bytes (banks $61-$63) ---
   578 0598			        org $6000
   579 6000-8FFF> 75 00 6E BA + snd_c1  ins '../data/snd_c1.bin'
   580 9000			        org $0580
   581 0580			.proc upload_snd1
   582 0580-0597> A9 00		        lda #<snd_c1
   583 0582 85 82		        sta zsrc
   584 0584 A9 60		        lda #>snd_c1
   585 0586 85 83		        sta zsrc+1
   586 0588 A9 E1		        lda #BANK_EN+$61
   587 058A 85 BB		        sta uc_bank
   588 058C A9 03		        lda #3
   589 058E 85 BC		        sta uc_cnt
   590 0590 A9 10		        lda #16
   591 0592 85 BD		        sta uc_lastpg
   592 0594 20 80 04		        jsr generic_upload
   593 0597 60			        rts
   594 				.endp
   595 02E2-02E3> 80 05		        ini upload_snd1
   596
   597 				; --- Sound chunk 2: 12288 bytes (banks $64-$66) ---
   598 0598			        org $6000
   599 6000-8FFF> 77 78 87 86 + snd_c2  ins '../data/snd_c2.bin'
   600 9000			        org $0580
   601 0580			.proc upload_snd2
   602 0580-0597> A9 00		        lda #<snd_c2
   603 0582 85 82		        sta zsrc
   604 0584 A9 60		        lda #>snd_c2
   605 0586 85 83		        sta zsrc+1
   606 0588 A9 E4		        lda #BANK_EN+$64
   607 058A 85 BB		        sta uc_bank
   608 058C A9 03		        lda #3
   609 058E 85 BC		        sta uc_cnt
   610 0590 A9 10		        lda #16
   611 0592 85 BD		        sta uc_lastpg
   612 0594 20 80 04		        jsr generic_upload
   613 0597 60			        rts
   614 				.endp
   615 02E2-02E3> 80 05		        ini upload_snd2
   616
   617 				; --- Sound chunk 3: 12288 bytes (banks $67-$69) ---
   618 0598			        org $6000
   619 6000-8FFF> 45 7A 75 77 + snd_c3  ins '../data/snd_c3.bin'
   620 9000			        org $0580
   621 0580			.proc upload_snd3
   622 0580-0597> A9 00		        lda #<snd_c3
   623 0582 85 82		        sta zsrc
   624 0584 A9 60		        lda #>snd_c3
   625 0586 85 83		        sta zsrc+1
   626 0588 A9 E7		        lda #BANK_EN+$67
   627 058A 85 BB		        sta uc_bank
   628 058C A9 03		        lda #3
   629 058E 85 BC		        sta uc_cnt
   630 0590 A9 10		        lda #16
   631 0592 85 BD		        sta uc_lastpg
   632 0594 20 80 04		        jsr generic_upload
   633 0597 60			        rts
   634 				.endp
   635 02E2-02E3> 80 05		        ini upload_snd3
   636
   637 				; --- Sound chunk 4: 12288 bytes (banks $6A-$6C) ---
   638 0598			        org $6000
   639 6000-8FFF> 67 77 77 87 + snd_c4  ins '../data/snd_c4.bin'
   640 9000			        org $0580
   641 0580			.proc upload_snd4
   642 0580-0597> A9 00		        lda #<snd_c4
   643 0582 85 82		        sta zsrc
   644 0584 A9 60		        lda #>snd_c4
   645 0586 85 83		        sta zsrc+1
   646 0588 A9 EA		        lda #BANK_EN+$6A
   647 058A 85 BB		        sta uc_bank
   648 058C A9 03		        lda #3
   649 058E 85 BC		        sta uc_cnt
   650 0590 A9 10		        lda #16
   651 0592 85 BD		        sta uc_lastpg
   652 0594 20 80 04		        jsr generic_upload
   653 0597 60			        rts
   654 				.endp
   655 02E2-02E3> 80 05		        ini upload_snd4
   656
   657 				; --- Sound chunk 5: overflow sounds (bank $6D) ---
   658 				; IMPORTANT: update uc_lastpg when adding more sounds!
   659 0598			        org $6000
   660 6000-648D> 76 65 66 86 + snd_c5  ins '../data/snd_c5.bin'
   661 648E			        org $0580
   662 0580			.proc upload_snd5
   663 0580-0597> A9 00		        lda #<snd_c5
   664 0582 85 82		        sta zsrc
   665 0584 A9 60		        lda #>snd_c5
   666 0586 85 83		        sta zsrc+1
   667 0588 A9 ED		        lda #BANK_EN+$6D
   668 058A 85 BB		        sta uc_bank
   669 058C A9 01		        lda #1
   670 058E 85 BC		        sta uc_cnt
   671 0590 A9 05		        lda #5                  ; 1166 bytes: ceil(1166/256) = 5 pages
   672 0592 85 BD		        sta uc_lastpg
   673 0594 20 80 04		        jsr generic_upload
   674 0597 60			        rts
   675 				.endp
   676 02E2-02E3> 80 05		        ini upload_snd5
   677
   678 				; =============================================
   679 				; TITLE SCREEN UPLOAD (320x200 = 64000 bytes)
   680 				; Upload to Screen 0 ($000000) and Screen 1 ($020000)
   681 				; 6 chunks, same layout as sky upload
   682 				; =============================================
   683 				; --- Title chunk 1 (banks $00-$02) ---
   684 0598			        org $6000
   685 6000-8FFF> BA B9 B8 6D + title_c1 ins '../data/title_c1.bin'
   686 9000			        org $0580
   687 0580			.proc upload_title1
   688 0580-05AE> A9 00		        lda #<title_c1
   689 0582 85 82		        sta zsrc
   690 0584 A9 60		        lda #>title_c1
   691 0586 85 83		        sta zsrc+1
   692 0588 A9 80		        lda #BANK_EN+$00
   693 058A 85 BB		        sta uc_bank
   694 058C A9 03		        lda #3
   695 058E 85 BC		        sta uc_cnt
   696 0590 A9 10		        lda #16
   697 0592 85 BD		        sta uc_lastpg
   698 0594 20 80 04		        jsr generic_upload
   699 				        ; Also upload to Screen 1
   700 0597 A9 00		        lda #<title_c1
   701 0599 85 82		        sta zsrc
   702 059B A9 60		        lda #>title_c1
   703 059D 85 83		        sta zsrc+1
   704 059F A9 A0		        lda #BANK_EN+$20
   705 05A1 85 BB		        sta uc_bank
   706 05A3 A9 03		        lda #3
   707 05A5 85 BC		        sta uc_cnt
   708 05A7 A9 10		        lda #16
   709 05A9 85 BD		        sta uc_lastpg
   710 05AB 20 80 04		        jsr generic_upload
   711 05AE 60			        rts
   712 				.endp
   713 02E2-02E3> 80 05		        ini upload_title1
   714
   715 				; --- Title chunk 2 (banks $03-$05) ---
   716 05AF			        org $6000
   717 6000-8FFF> 4A 4B EE 02 + title_c2 ins '../data/title_c2.bin'
   718 9000			        org $0580
   719 0580			.proc upload_title2
   720 0580-05AE> A9 00		        lda #<title_c2
   721 0582 85 82		        sta zsrc
   722 0584 A9 60		        lda #>title_c2
   723 0586 85 83		        sta zsrc+1
   724 0588 A9 83		        lda #BANK_EN+$03
   725 058A 85 BB		        sta uc_bank
   726 058C A9 03		        lda #3
   727 058E 85 BC		        sta uc_cnt
   728 0590 A9 10		        lda #16
   729 0592 85 BD		        sta uc_lastpg
   730 0594 20 80 04		        jsr generic_upload
   731 0597 A9 00		        lda #<title_c2
   732 0599 85 82		        sta zsrc
   733 059B A9 60		        lda #>title_c2
   734 059D 85 83		        sta zsrc+1
   735 059F A9 A3		        lda #BANK_EN+$23
   736 05A1 85 BB		        sta uc_bank
   737 05A3 A9 03		        lda #3
   738 05A5 85 BC		        sta uc_cnt
   739 05A7 A9 10		        lda #16
   740 05A9 85 BD		        sta uc_lastpg
   741 05AB 20 80 04		        jsr generic_upload
   742 05AE 60			        rts
   743 				.endp
   744 02E2-02E3> 80 05		        ini upload_title2
   745
   746 				; --- Title chunk 3 (banks $06-$08) ---
   747 05AF			        org $6000
   748 6000-8FFF> E2 E4 A2 F7 + title_c3 ins '../data/title_c3.bin'
   749 9000			        org $0580
   750 0580			.proc upload_title3
   751 0580-05AE> A9 00		        lda #<title_c3
   752 0582 85 82		        sta zsrc
   753 0584 A9 60		        lda #>title_c3
   754 0586 85 83		        sta zsrc+1
   755 0588 A9 86		        lda #BANK_EN+$06
   756 058A 85 BB		        sta uc_bank
   757 058C A9 03		        lda #3
   758 058E 85 BC		        sta uc_cnt
   759 0590 A9 10		        lda #16
   760 0592 85 BD		        sta uc_lastpg
   761 0594 20 80 04		        jsr generic_upload
   762 0597 A9 00		        lda #<title_c3
   763 0599 85 82		        sta zsrc
   764 059B A9 60		        lda #>title_c3
   765 059D 85 83		        sta zsrc+1
   766 059F A9 A6		        lda #BANK_EN+$26
   767 05A1 85 BB		        sta uc_bank
   768 05A3 A9 03		        lda #3
   769 05A5 85 BC		        sta uc_cnt
   770 05A7 A9 10		        lda #16
   771 05A9 85 BD		        sta uc_lastpg
   772 05AB 20 80 04		        jsr generic_upload
   773 05AE 60			        rts
   774 				.endp
   775 02E2-02E3> 80 05		        ini upload_title3
   776
   777 				; --- Title chunk 4 (banks $09-$0B) ---
   778 05AF			        org $6000
   779 6000-8FFF> 7D 7E 7E 7F + title_c4 ins '../data/title_c4.bin'
   780 9000			        org $0580
   781 0580			.proc upload_title4
   782 0580-05AE> A9 00		        lda #<title_c4
   783 0582 85 82		        sta zsrc
   784 0584 A9 60		        lda #>title_c4
   785 0586 85 83		        sta zsrc+1
   786 0588 A9 89		        lda #BANK_EN+$09
   787 058A 85 BB		        sta uc_bank
   788 058C A9 03		        lda #3
   789 058E 85 BC		        sta uc_cnt
   790 0590 A9 10		        lda #16
   791 0592 85 BD		        sta uc_lastpg
   792 0594 20 80 04		        jsr generic_upload
   793 0597 A9 00		        lda #<title_c4
   794 0599 85 82		        sta zsrc
   795 059B A9 60		        lda #>title_c4
   796 059D 85 83		        sta zsrc+1
   797 059F A9 A9		        lda #BANK_EN+$29
   798 05A1 85 BB		        sta uc_bank
   799 05A3 A9 03		        lda #3
   800 05A5 85 BC		        sta uc_cnt
   801 05A7 A9 10		        lda #16
   802 05A9 85 BD		        sta uc_lastpg
   803 05AB 20 80 04		        jsr generic_upload
   804 05AE 60			        rts
   805 				.endp
   806 02E2-02E3> 80 05		        ini upload_title4
   807
   808 				; --- Title chunk 5 (banks $0C-$0E) ---
   809 05AF			        org $6000
   810 6000-8FFF> 09 09 7D 7D + title_c5t ins '../data/title_c5.bin'
   811 9000			        org $0580
   812 0580			.proc upload_title5
   813 0580-05AE> A9 00		        lda #<title_c5t
   814 0582 85 82		        sta zsrc
   815 0584 A9 60		        lda #>title_c5t
   816 0586 85 83		        sta zsrc+1
   817 0588 A9 8C		        lda #BANK_EN+$0C
   818 058A 85 BB		        sta uc_bank
   819 058C A9 03		        lda #3
   820 058E 85 BC		        sta uc_cnt
   821 0590 A9 10		        lda #16
   822 0592 85 BD		        sta uc_lastpg
   823 0594 20 80 04		        jsr generic_upload
   824 0597 A9 00		        lda #<title_c5t
   825 0599 85 82		        sta zsrc
   826 059B A9 60		        lda #>title_c5t
   827 059D 85 83		        sta zsrc+1
   828 059F A9 AC		        lda #BANK_EN+$2C
   829 05A1 85 BB		        sta uc_bank
   830 05A3 A9 03		        lda #3
   831 05A5 85 BC		        sta uc_cnt
   832 05A7 A9 10		        lda #16
   833 05A9 85 BD		        sta uc_lastpg
   834 05AB 20 80 04		        jsr generic_upload
   835 05AE 60			        rts
   836 				.endp
   837 02E2-02E3> 80 05		        ini upload_title5
   838
   839 				; --- Title chunk 6 (bank $0F, partial) ---
   840 05AF			        org $6000
   841 6000-69FF> 2E 01 2E 2F + title_c6t ins '../data/title_c6.bin'
   842 6A00			        org $0580
   843 0580			.proc upload_title6
   844 0580-05AE> A9 00		        lda #<title_c6t
   845 0582 85 82		        sta zsrc
   846 0584 A9 60		        lda #>title_c6t
   847 0586 85 83		        sta zsrc+1
   848 0588 A9 8F		        lda #BANK_EN+$0F
   849 058A 85 BB		        sta uc_bank
   850 058C A9 01		        lda #1
   851 058E 85 BC		        sta uc_cnt
   852 0590 A9 0A		        lda #10
   853 0592 85 BD		        sta uc_lastpg
   854 0594 20 80 04		        jsr generic_upload
   855 0597 A9 00		        lda #<title_c6t
   856 0599 85 82		        sta zsrc
   857 059B A9 60		        lda #>title_c6t
   858 059D 85 83		        sta zsrc+1
   859 059F A9 AF		        lda #BANK_EN+$2F
   860 05A1 85 BB		        sta uc_bank
   861 05A3 A9 01		        lda #1
   862 05A5 85 BC		        sta uc_cnt
   863 05A7 A9 0A		        lda #10
   864 05A9 85 BD		        sta uc_lastpg
   865 05AB 20 80 04		        jsr generic_upload
   866 05AE 60			        rts
   867 				.endp
   868 02E2-02E3> 80 05		        ini upload_title6
   869
   870 				;==============================================
   871 				; REFORMAT TITLE FROM 320 TO 336 PITCH (INI segment)
   872 				; Runs after all title uploads. Blits buffer 1 (320) → buffer 0 (336),
   873 				; then buffer 0 (336) → buffer 1 (336).
   874 				;==============================================
   875 05AF			        org $6000
   876 6000			.proc reformat_title_336
   877 6000-60BE> A9 98		        lda #$90+MC_CPU
   878 6002 8D 5E D6		        sta VBXE_MEMAC_CTRL
   879 6005 A9 FF		        lda #BANK_EN+BANK_BCB
   880 6007 8D 5F D6		        sta VBXE_BANK_SEL
   881 				        ; Step 1: buffer 1 (src 320) → buffer 0 (dst 336)
   882 600A A9 00		        lda #$00
   883 600C 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   884 600F 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   885 6012 A9 02		        lda #$02
   886 6014 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   887 6017 A9 40		        lda #<SCR_W
   888 6019 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   889 601C A9 01		        lda #>SCR_W
   890 601E 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   891 6021 A9 01		        lda #1
   892 6023 8D 05 91		        sta MEMW+[VRAM_BCB&$FFF]+5
   893 6026 A9 00		        lda #$00
   894 6028 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   895 602B 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   896 602E 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   897 6031 A9 50		        lda #<SCR_PITCH
   898 6033 8D 09 91		        sta MEMW+[VRAM_BCB&$FFF]+9
   899 6036 A9 01		        lda #>SCR_PITCH
   900 6038 8D 0A 91		        sta MEMW+[VRAM_BCB&$FFF]+10
   901 603B A9 01		        lda #1
   902 603D 8D 0B 91		        sta MEMW+[VRAM_BCB&$FFF]+11
   903 6040 A9 3F		        lda #<[SCR_W-1]
   904 6042 8D 0C 91		        sta MEMW+[VRAM_BCB&$FFF]+12
   905 6045 A9 01		        lda #>[SCR_W-1]
   906 6047 8D 0D 91		        sta MEMW+[VRAM_BCB&$FFF]+13
   907 604A A9 C7		        lda #SCR_H-1
   908 604C 8D 0E 91		        sta MEMW+[VRAM_BCB&$FFF]+14
   909 604F A9 FF		        lda #$FF
   910 6051 8D 0F 91		        sta MEMW+[VRAM_BCB&$FFF]+15
   911 6054 A9 00		        lda #0
   912 6056 8D 10 91		        sta MEMW+[VRAM_BCB&$FFF]+16
   913 6059 8D 11 91		        sta MEMW+[VRAM_BCB&$FFF]+17
   914 605C 8D 12 91		        sta MEMW+[VRAM_BCB&$FFF]+18
   915 605F 8D 13 91		        sta MEMW+[VRAM_BCB&$FFF]+19
   916 6062 8D 14 91		        sta MEMW+[VRAM_BCB&$FFF]+20
   917 				        ; Point blitter to BCB and start
   918 6065 A9 00		        lda #$00
   919 6067 8D 50 D6		        sta $D650
   920 606A A9 F1		        lda #$F1
   921 606C 8D 51 D6		        sta $D651
   922 606F A9 07		        lda #$07
   923 6071 8D 52 D6		        sta $D652
   924 6074 A9 01		        lda #1
   925 6076 8D 53 D6		        sta $D653
   926 6079 AD 53 D6		?w1     lda $D653
   927 607C D0 FB		        bne ?w1
   928 				        ; Step 2: buffer 0 (src 336) → buffer 1 (dst 336)
   929 607E A9 FF		        lda #BANK_EN+BANK_BCB
   930 6080 8D 5F D6		        sta VBXE_BANK_SEL
   931 6083 A9 00		        lda #$00
   932 6085 8D 00 91		        sta MEMW+[VRAM_BCB&$FFF]+0
   933 6088 8D 01 91		        sta MEMW+[VRAM_BCB&$FFF]+1
   934 608B 8D 02 91		        sta MEMW+[VRAM_BCB&$FFF]+2
   935 608E A9 50		        lda #<SCR_PITCH
   936 6090 8D 03 91		        sta MEMW+[VRAM_BCB&$FFF]+3
   937 6093 A9 01		        lda #>SCR_PITCH
   938 6095 8D 04 91		        sta MEMW+[VRAM_BCB&$FFF]+4
   939 6098 A9 00		        lda #$00
   940 609A 8D 06 91		        sta MEMW+[VRAM_BCB&$FFF]+6
   941 609D 8D 07 91		        sta MEMW+[VRAM_BCB&$FFF]+7
   942 60A0 A9 02		        lda #$02
   943 60A2 8D 08 91		        sta MEMW+[VRAM_BCB&$FFF]+8
   944 60A5 A9 00		        lda #$00
   945 60A7 8D 50 D6		        sta $D650
   946 60AA A9 F1		        lda #$F1
   947 60AC 8D 51 D6		        sta $D651
   948 60AF A9 07		        lda #$07
   949 60B1 8D 52 D6		        sta $D652
   950 60B4 A9 01		        lda #1
   951 60B6 8D 53 D6		        sta $D653
   952 60B9 AD 53 D6		?w2     lda $D653
   953 60BC D0 FB		        bne ?w2
   954 60BE 60			        rts
   955 				.endp
   956 02E2-02E3> 00 60		        ini reformat_title_336
   330 60BF			        icl 'init.asm'
Source: init.asm
     1 				;==============================================
     2 				; DOOM2D - Initialization code (overlay at $6000, runs once)
     3 				; init.asm
     4 				;==============================================
     5
     6 				;==============================================
     7 				; INIT OVERLAY SEGMENT ($6000)
     8 				; These procs run once during init_game, then memory is free.
     9 				; Must be the LAST org $6000 segment (after all uploads).
    10 				;==============================================
    11 60BF			        org $6000
    12
    13 6000			.proc init_enemies
    14 6000-61F9> A2 00		        ldx #0
    15 6002 E0 04		?lp     cpx #NUM_ENEMIES
    16 6004 B0 72		        bcs ?done
    17 6006 A9 01		        lda #1
    18 6008 9D 55 3A		        sta en_act,x
    19 600B BD 59 A5		        lda enemy_spawn_type,x
    20 600E 9D 6D 3A		        sta en_type,x
    21 6011 8A			        txa
    22 6012 0A			        asl
    23 6013 A8			        tay
    24 6014 B9 49 A5		        lda enemy_spawn_x,y
    25 6017 9D 37 3A		        sta en_x,x
    26 601A B9 4A A5		        lda enemy_spawn_x+1,y
    27 601D 9D 3D 3A		        sta enxhi,x
    28 6020 BD 51 A5		        lda enemy_spawn_y,x
    29 6023 18			        clc
    30 6024 69 10		        adc #16
    31 6026 9D 43 3A		        sta en_y,x
    32 6029 BD 55 A5		        lda enemy_spawn_yhi,x
    33 602C 69 00		        adc #0
    34 602E 9D 49 3A		        sta en_yhi,x
    35 6031 A9 00		        lda #0
    36 6033 9D 73 3A		        sta en_cooldown,x
    37 6036 9D 7F 3A		        sta envely,x
    38 6039 9D 85 3A		        sta en_gib,x
    39 603C 9D F7 44		        sta en_tcnt,x
    40 603F 9D 8B 3A		        sta envelx,x
    41 6042 9D FD 44		        sta en_pain_tmr,x
    42 6045 A9 3C		        lda #60
    43 6047 9D 91 3A		        sta en_atk,x
    44 604A BD 5D A5		        lda enemy_spawn_dir,x
    45 604D 9D 4F 3A		        sta en_dir,x
    46 6050 BD 59 A5		        lda enemy_spawn_type,x
    47 6053 A8			        tay
    48 6054 B9 87 5E		        lda en_hp_tab,y
    49 6057 9D 5B 3A		        sta en_hp,x
    50 605A BD 37 3A		        lda en_x,x
    51 605D 38			        sec
    52 605E E9 28		        sbc #40
    53 6060 B0 02		        bcs ?min_ok
    54 6062 A9 00		        lda #0
    55 6064 9D 61 3A		?min_ok sta en_xmin,x
    56 6067 BD 37 3A		        lda en_x,x
    57 606A 18			        clc
    58 606B 69 28		        adc #40
    59 606D 90 02		        bcc ?max_ok
    60 606F A9 FF		        lda #255
    61 6071 9D 67 3A		?max_ok sta en_xmax,x
    62 6074 E8			        inx
    63 6075 4C 02 60		        jmp ?lp
    64 6078			?done
    65 6078 E0 06		?clr    cpx #MAX_ENEMIES
    66 607A B0 09		        bcs ?ret
    67 607C A9 00		        lda #0
    68 607E 9D 55 3A		        sta en_act,x
    69 6081 E8			        inx
    70 6082 4C 78 60		        jmp ?clr
    71 6085 60			?ret    rts
    72 				.endp
    73
    74 6086			.proc init_pickups
    75 6086 A9 00		        lda #0
    76 6088 85 A0		        sta zparmor
    77 608A 85 A1		        sta zpkeys
    78 608C A2 00		        ldx #0
    79 608E E0 02		?lp     cpx #NUM_PICKUPS
    80 6090 B0 2C		        bcs ?done
    81 6092 A9 01		        lda #1
    82 6094 9D EC 30		        sta pk_act,x
    83 6097 BD 61 A5		        lda pickup_spawn_x,x
    84 609A 9D F8 30		        sta pk_x,x
    85 609D BD 63 A5		        lda pickup_spawn_xhi,x
    86 60A0 9D 04 31		        sta pk_xhi,x
    87 60A3 BD 65 A5		        lda pickup_spawn_y,x
    88 60A6 18			        clc
    89 60A7 69 10		        adc #16
    90 60A9 9D 10 31		        sta pk_y,x
    91 60AC BD 67 A5		        lda pickup_spawn_yhi,x
    92 60AF 69 00		        adc #0
    93 60B1 9D 1C 31		        sta pk_yhi,x
    94 60B4 BD 69 A5		        lda pickup_spawn_type,x
    95 60B7 9D 28 31		        sta pk_type,x
    96 60BA E8			        inx
    97 60BB 4C 8E 60		        jmp ?lp
    98 60BE			?done
    99 60BE E0 0C		?clr    cpx #MAX_PICKUPS
   100 60C0 B0 09		        bcs ?ret
   101 60C2 A9 00		        lda #0
   102 60C4 9D EC 30		        sta pk_act,x
   103 60C7 E8			        inx
   104 60C8 4C BE 60		        jmp ?clr
   105 60CB 60			?ret    rts
   106 				.endp
   107
   108 60CC			.proc init_decorations
   109 60CC A2 00		        ldx #0
   110 60CE E0 00		?lp     cpx #NUM_DECOR
   111 60D0 B0 43		        bcs ?done
   112 60D2 A9 01		        lda #1
   113 60D4 9D CD 34		        sta dc_act,x
   114 60D7 BD 6B A5		        lda decor_spawn_x,x
   115 60DA 9D D5 34		        sta dc_x,x
   116 60DD BD 6C A5		        lda decor_spawn_xhi,x
   117 60E0 9D DD 34		        sta dc_xhi,x
   118 60E3 BD 6D A5		        lda decor_spawn_y,x
   119 60E6 18			        clc
   120 60E7 69 10		        adc #16
   121 60E9 9D ED 34		        sta dc_y,x
   122 60EC BD 6E A5		        lda decor_spawn_yhi,x
   123 60EF 69 00		        adc #0
   124 60F1 9D F5 34		        sta dc_yhi,x
   125 60F4 BD 6F A5		        lda decor_spawn_type,x
   126 60F7 9D FD 34		        sta dc_type,x
   127 60FA A9 00		        lda #0
   128 60FC 9D 05 35		        sta dc_hp,x
   129 60FF 9D 0D 35		        sta dc_timer,x
   130 6102 BD 6F A5		        lda decor_spawn_type,x
   131 6105 C9 00		        cmp #DC_BARREL
   132 6107 D0 08		        bne ?nob
   133 6109 A9 03		        lda #BARREL_HP
   134 610B 9D 05 35		        sta dc_hp,x
   135 610E 20 01 3A		        jsr barrel_set_solid
   136 6111 E8			?nob    inx
   137 6112 4C CE 60		        jmp ?lp
   138 6115			?done
   139 6115 E0 08		?clr    cpx #MAX_DECOR
   140 6117 B0 09		        bcs ?ret
   141 6119 A9 00		        lda #0
   142 611B 9D CD 34		        sta dc_act,x
   143 611E E8			        inx
   144 611F 4C 15 61		        jmp ?clr
   145 6122 60			?ret    rts
   146 				.endp
   147
   148 6123			.proc init_doors
   149 6123 A9 00		        lda #0
   150 6125 8D 8F 55		        sta num_doors
   151 6128 A9 9E		        lda #BANK_EN+BANK_MAP
   152 612A 8D 5F D6		        sta VBXE_BANK_SEL
   153 612D A9 00		        lda #0
   154 612F 8D B7 61		        sta id_row
   155 6132 A9 00		?rlp    lda #0
   156 6134 8D B6 61		        sta id_col
   157 6137 AC B7 61		?clp    ldy id_row
   158 613A B9 C6 A3		        lda map_row_lo,y
   159 613D 85 D4		        sta ztptr
   160 613F B9 E6 A3		        lda map_row_hi,y
   161 6142 85 D5		        sta ztptr+1
   162 6144 AC B6 61		        ldy id_col
   163 6147 B1 D4		        lda (ztptr),y
   164 6149 C9 04		        cmp #TILE_DOOR_ID
   165 614B F0 1E		        beq ?found_door
   166 614D C9 19		        cmp #TILE_DOOR_RED
   167 614F F0 0B		        beq ?found_red
   168 6151 C9 1A		        cmp #TILE_DOOR_BLUE
   169 6153 F0 0C		        beq ?found_blue
   170 6155 C9 1B		        cmp #TILE_DOOR_YEL
   171 6157 F0 0D		        beq ?found_yel
   172 6159 4C 9C 61		        jmp ?next
   173 615C			?found_red
   174 615C A9 01		        lda #1
   175 615E 4C 6D 61		        jmp ?add_door
   176 6161			?found_blue
   177 6161 A9 02		        lda #2
   178 6163 4C 6D 61		        jmp ?add_door
   179 6166			?found_yel
   180 6166 A9 04		        lda #4
   181 6168 4C 6D 61		        jmp ?add_door
   182 616B			?found_door
   183 616B A9 00		        lda #0
   184 616D			?add_door
   185 616D 8D B8 61		        sta id_key
   186 6170 AE 8F 55		        ldx num_doors
   187 6173 E0 08		        cpx #MAX_DOORS
   188 6175 B0 25		        bcs ?next
   189 6177 AD B6 61		        lda id_col
   190 617A 9D 5F 55		        sta door_col,x
   191 617D AD B7 61		        lda id_row
   192 6180 9D 67 55		        sta door_row,x
   193 6183 A9 00		        lda #0
   194 6185 9D 6F 55		        sta door_state,x
   195 6188 9D 77 55		        sta door_timer,x
   196 618B AD B8 61		        lda id_key
   197 618E 9D 7F 55		        sta door_key,x
   198 6191 AC B6 61		        ldy id_col
   199 6194 B1 D4		        lda (ztptr),y
   200 6196 9D 87 55		        sta door_tile,x
   201 6199 EE 8F 55		        inc num_doors
   202 619C EE B6 61		?next   inc id_col
   203 619F AD B6 61		        lda id_col
   204 61A2 C9 40		        cmp #MAP_W
   205 61A4 90 91		        bcc ?clp
   206 61A6 EE B7 61		        inc id_row
   207 61A9 AD B7 61		        lda id_row
   208 61AC C9 20		        cmp #MAP_H
   209 61AE 90 82		        bcc ?rlp
   210 61B0 A9 00		        lda #0
   211 61B2 8D 5F D6		        sta VBXE_BANK_SEL
   212 61B5 60			        rts
   213 61B6 00			id_col  dta 0
   214 61B7 00			id_row  dta 0
   215 61B8 00			id_key  dta 0
   216 				.endp
   217
   218 61B9			.proc init_render
   219 61B9 20 88 53		        jsr clear_dirty_all
   220 61BC A9 00		        lda #$00
   221 61BE 8D FF 22		        sta zbuf_hi
   222 61C1 20 2C 4C		        jsr clear_hud_area
   223 61C4 A9 02		        lda #SCR1_HI
   224 61C6 8D FF 22		        sta zbuf_hi
   225 61C9 20 2C 4C		        jsr clear_hud_area
   226 61CC A9 00		        lda #$00
   227 61CE 8D FF 22		        sta zbuf_hi
   228 61D1 20 71 53		        jsr setup_dirty_ptr
   229 61D4 20 76 22		        jsr clear_screen
   230 61D7 20 E3 46		        jsr render_tiles
   231 61DA 20 96 53		        jsr render_static
   232 61DD 20 DF 22		        jsr wait_blit
   233 61E0 A9 02		        lda #SCR1_HI
   234 61E2 8D FF 22		        sta zbuf_hi
   235 61E5 20 71 53		        jsr setup_dirty_ptr
   236 61E8 20 76 22		        jsr clear_screen
   237 61EB 20 E3 46		        jsr render_tiles
   238 61EE 20 96 53		        jsr render_static
   239 61F1 20 DF 22		        jsr wait_blit
   240 61F4 A9 02		        lda #SCR1_HI
   241 61F6 8D FF 22		        sta zbuf_hi
   242 61F9 60			        rts
   243 				.endp
   331 61FA			        icl 'luts.asm'
Source: luts.asm
     1 				;==============================================
     2 				; DOOM2D - Lookup tables at $A000 (under BASIC ROM)
     3 				; luts.asm
     4 				;
     5 				; BASIC ROM is disabled by generic_upload, stays off during gameplay.
     6 				; Contains: y_addr, bg_row, sky_lut, pix_to_tile, map_row,
     7 				;           dirty row, sprite dims, decoration props, spawn data,
     8 				;           calc_scr_y, tile properties.
     9 				;==============================================
    10
    11 61FA			        org $A000
    12 A000			y_addr_lo
    13 A000-A5F4> 00 50 A0 F0 + :200    dta <[#*SCR_PITCH]
    14 A0C8			y_addr_hi
    15 A0C8 00 01 02 03 05 06 + :200    dta >[#*SCR_PITCH]
    16 A190			y_addr_xhi
    17 A190 00 00 00 00 00 00 + :200    dta [[#*SCR_PITCH]/65536]
    18
    19 				; Background row address lookup (32 entries, moved from vbxe.asm)
    20 A258			bg_row_mid
    21 A258 40 60 80 A0 C0 E0 +         dta $40,$60,$80,$A0,$C0,$E0,$00,$20,$40,$60,$80,$A0,$C0,$E0,$00,$20
    22 A268 20 20 20 20 20 20 +         dta $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
    23 A278			bg_row_hi
    24 A278 03 03 03 03 03 03 +         dta $03,$03,$03,$03,$03,$03,$04,$04,$04,$04,$04,$04,$04,$04,$05,$05
    25 A288 05 05 05 05 05 05 +         dta $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
    26
    27 				; Sky parallax LUT (46 entries, moved from vbxe.asm)
    28 A298 00 04 08 0C 10 14 + sky_lut :45 dta [176 * # / 44]
    29 A2C5 B0			        dta 176                 ; entry 45: guard (max 176 so sky_x_lo+336 <= 512)
    30
    31 				; Pixel-to-tile LUT (256 bytes, moved from game.asm to free main segment)
    32 A2C6			pix_to_tile
    33 A2C6 00 00 00 00 00 00 +         :256 dta [#/16]
    34
    35 				; Map row address LUT (64 bytes, moved from game.asm to free main segment)
    36 A3C6			map_row_lo
    37 A3C6 00 40 80 C0 00 40 +         :32 dta <[map_data + #*64]
    38 A3E6			map_row_hi
    39 A3E6 90 90 90 90 91 91 +         :32 dta >[map_data + #*64]
    40
    41 				; Dirty row base offset LUT (13 bytes, moved from game.asm)
    42 A406			row_x20
    43 A406 00 14 28 3C 50 64 +         :13 dta [#*20]
    44
    45 				; Sprite width/height tables (moved from sprite_defs.asm to free main segment)
    46 A413			spr_w
    47 A413 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; player: idle,walk1-3,shoot,death1
    48 A419 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; zombie: idle,walk1-3,shoot,death1
    49 A41F 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; imp: idle,walk1-3,shoot,death1
    50 A425 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; pinky: idle,walk1-3,attack,death1
    51 A42B 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; caco: idle,walk1-3,shoot,death1
    52 A431 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; shotgun: idle,walk1-3,shoot,death1
    53 A437 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; baron: idle,walk1-3,shoot,death1
    54 A43D 08 08		        dta 8,8                ; projectile1,2
    55 A43F 10 10 10 10		        dta 16,16,16,16        ; medikit,stimpack,health_bonus,ammo_clip
    56 A443 10 10 10		        dta 16,16,16           ; greenarmor,bluearmor,soulsphere
    57 A446 10 10 10		        dta 16,16,16           ; keyred,keyblue,keyyellow
    58 A449 10 10 10		        dta 16,16,16           ; shotgunpk,shells,barrel
    59 A44C 10 10 10		        dta 16,16,16           ; pillar,torch,lamp
    60 A44F 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; player_L: idle,walk1-3,shoot,death1
    61 A455 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; zombie_L
    62 A45B 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; imp_L
    63 A461 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; pinky_L
    64 A467 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; caco_L
    65 A46D 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; shotgun_L
    66 A473 10 10 10 10 10 10	        dta 16,16,16,16,16,16  ; baron_L
    67 A479 10 10		        dta 16,16              ; chaingunpk,rocketpk
    68 A47B 08			        dta 8                  ; rocket_proj
    69 A47C 10			        dta 16                 ; rocketbox
    70 A47D 10 10 10 10		        dta 16,16,16,16        ; zombie_death2,death3,gib1,gib2
    71 A481 10 10		        dta 16,16              ; imp_gib1,gib2
    72 A483 10 10		        dta 16,16              ; shotgun_gib1,gib2
    73 A485 10 10		        dta 16,16              ; imp_death2,death3
    74 A487 10 10		        dta 16,16              ; shotgun_death2,death3
    75 A489 10 10		        dta 16,16              ; pinky_death2,death3
    76 A48B 10 10		        dta 16,16              ; caco_death2,death3
    77 A48D 10 10		        dta 16,16              ; baron_death2,death3
    78 A48F 10 10 10		        dta 16,16,16           ; barrel_exp1-3
    79 A492 10 10		        dta 16,16              ; plasmagunpk,cellspk
    80 A494 08 08		        dta 8,8                ; plasma_proj1,2
    81 A496 10 10		        dta 16,16              ; bfg_proj1,2
    82 A498 10 10 10		        dta 16,16,16           ; bfgpk,rocket1pk,armorbonus
    83 A49B 10 10		        dta 16,16              ; pl_death2,death3
    84 A49D 08 08		        dta 8,8                ; imp_fire1,fire2
    85 A49F 10 10		        dta 16,16              ; pl_pain,pl_pain_L
    86 A4A1 10 10		        dta 16,16              ; zombie_pain,zombie_pain_L
    87 A4A3 08 08		        dta 8,8                ; caco_fire1,fire2
    88
    89 A4A5			spr_h
    90 A4A5 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; player: idle,walk1-3,shoot,death1
    91 A4AB 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; zombie
    92 A4B1 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; imp
    93 A4B7 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; pinky
    94 A4BD 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; caco
    95 A4C3 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; shotgun
    96 A4C9 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; baron
    97 A4CF 08 08		        dta 8,8                ; projectile1,2
    98 A4D1 10 10 10 10		        dta 16,16,16,16        ; medikit,stimpack,health_bonus,ammo_clip
    99 A4D5 10 10 10		        dta 16,16,16           ; greenarmor,bluearmor,soulsphere
   100 A4D8 10 10 10		        dta 16,16,16           ; keyred,keyblue,keyyellow
   101 A4DB 10 10 10		        dta 16,16,16           ; shotgunpk,shells,barrel
   102 A4DE 20 20 20		        dta 32,32,32           ; pillar,torch,lamp
   103 A4E1 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; player_L
   104 A4E7 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; zombie_L
   105 A4ED 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; imp_L
   106 A4F3 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; pinky_L
   107 A4F9 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; caco_L
   108 A4FF 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; shotgun_L
   109 A505 20 20 20 20 20 20	        dta 32,32,32,32,32,32  ; baron_L
   110 A50B 10 10		        dta 16,16              ; chaingunpk,rocketpk
   111 A50D 08			        dta 8                  ; rocket_proj
   112 A50E 10			        dta 16                 ; rocketbox
   113 A50F 20 20 20 20		        dta 32,32,32,32        ; zombie_death2,death3,gib1,gib2
   114 A513 20 20		        dta 32,32              ; imp_gib1,gib2
   115 A515 20 20		        dta 32,32              ; shotgun_gib1,gib2
   116 A517 20 20		        dta 32,32              ; imp_death2,death3
   117 A519 20 20		        dta 32,32              ; shotgun_death2,death3
   118 A51B 20 20		        dta 32,32              ; pinky_death2,death3
   119 A51D 20 20		        dta 32,32              ; caco_death2,death3
   120 A51F 20 20		        dta 32,32              ; baron_death2,death3
   121 A521 10 10 10		        dta 16,16,16           ; barrel_exp1-3
   122 A524 10 10		        dta 16,16              ; plasmagunpk,cellspk
   123 A526 08 08		        dta 8,8                ; plasma_proj1,2
   124 A528 10 10		        dta 16,16              ; bfg_proj1,2
   125 A52A 10 10 10		        dta 16,16,16           ; bfgpk,rocket1pk,armorbonus
   126 A52D 20 20		        dta 32,32              ; pl_death2,death3
   127 A52F 08 08		        dta 8,8                ; imp_fire1,fire2
   128 A531 20 20		        dta 32,32              ; pl_pain,pl_pain_L
   129 A533 20 20		        dta 32,32              ; zombie_pain,zombie_pain_L
   130 A535 08 08		        dta 8,8                ; caco_fire1,fire2
   131
   132 				; Decoration property tables (moved from decorations.asm)
   133 A537 10 10 10 10 10 10	dc_wtab dta 16,16,16,16,16,16     ; width per type
   134 A53D 10 20 20 10 10 10	dc_htab dta 16,32,32,16,16,16     ; height per type
   135 A543 01 00 01 00 00 00	dc_solid dta 1,0,1,0,0,0          ; solid flag per type
   136
   137 				; Entity spawn data (moved from data.asm to free main segment)
   138 A549			        icl '../data/test_map_ent.asm'
Source: test_map_ent.asm
     1 				; Auto-generated entity spawn data
     2 				; Source: mapa-velka.map
     3
     4 = 0030			SPAWN_X = 48
     5 = 0090			SPAWN_Y = 144
     6
     7 = 0004			NUM_ENEMIES = 4
     8
     9 A549			enemy_spawn_x
    10 A549 C0 01		        dta a(448)  ; zombie
    11 A54B 20 02		        dta a(544)  ; zombie
    12 A54D 40 02		        dta a(576)  ; zombie
    13 A54F 60 01		        dta a(352)  ; zombie
    14
    15 A551			enemy_spawn_y
    16 A551 90			        dta <144  ; zombie
    17 A552 90			        dta <144  ; zombie
    18 A553 90			        dta <144  ; zombie
    19 A554 70			        dta <368  ; zombie
    20
    21 A555			enemy_spawn_yhi
    22 A555 00			        dta >144  ; zombie
    23 A556 00			        dta >144  ; zombie
    24 A557 00			        dta >144  ; zombie
    25 A558 01			        dta >368  ; zombie
    26
    27 A559			enemy_spawn_type
    28 A559 00			        dta 0  ; zombie
    29 A55A 00			        dta 0  ; zombie
    30 A55B 00			        dta 0  ; zombie
    31 A55C 00			        dta 0  ; zombie
    32
    33 A55D			enemy_spawn_dir
    34 A55D 00			        dta 0  ; zombie right
    35 A55E 00			        dta 0  ; zombie right
    36 A55F 00			        dta 0  ; zombie right
    37 A560 01			        dta 1  ; zombie left
    38
    39 = 0002			NUM_PICKUPS = 2
    40
    41 A561			pickup_spawn_x
    42 A561 B0			        dta 176  ; plasmagun
    43 A562 E0			        dta 224  ; plasmagun
    44
    45 A563			pickup_spawn_xhi
    46 A563 00			        dta 0  ; plasmagun
    47 A564 00			        dta 0  ; plasmagun
    48
    49 A565			pickup_spawn_y
    50 A565 90			        dta <144  ; plasmagun
    51 A566 70			        dta <368  ; plasmagun
    52
    53 A567			pickup_spawn_yhi
    54 A567 00			        dta >144  ; plasmagun
    55 A568 01			        dta >368  ; plasmagun
    56
    57 A569			pickup_spawn_type
    58 A569 0F			        dta 15  ; plasmagun
    59 A56A 0F			        dta 15  ; plasmagun
    60
    61 = 0000			NUM_DECOR = 0
    62
    63 A56B			decor_spawn_x
    64 A56B 00			        dta 0  ; placeholder
    65
    66 A56C			decor_spawn_xhi
    67 A56C 00			        dta 0  ; placeholder
    68
    69 A56D			decor_spawn_y
    70 A56D 00			        dta 0  ; placeholder
    71
    72 A56E			decor_spawn_yhi
    73 A56E 00			        dta 0  ; placeholder
    74
    75 A56F			decor_spawn_type
    76 A56F 00			        dta 0  ; placeholder
    77
    78 				; Switch target links (auto-generated)
    79 A570			num_switches
    80 A570 01			        dta 1
    81 A571			sw_col
    82 A571 1D			        dta 29  ; switch 0
    83 A572 00			        dta 0
    84 A573 00			        dta 0
    85 A574 00			        dta 0
    86 A575			sw_row
    87 A575 09			        dta 9  ; switch 0
    88 A576 00			        dta 0
    89 A577 00			        dta 0
    90 A578 00			        dta 0
    91 A579			sw_tgt_col
    92 A579 0F			        dta 15  ; -> door
    93 A57A 00			        dta 0
    94 A57B 00			        dta 0
    95 A57C 00			        dta 0
    96 A57D			sw_tgt_row
    97 A57D 09			        dta 9  ; -> door
    98 A57E 00			        dta 0
    99 A57F 00			        dta 0
   100 A580 00			        dta 0
   101 A581			sw_action
   102 A581 00			        dta 0  ; door
   103 A582 00			        dta 0
   104 A583 00			        dta 0
   105 A584 00			        dta 0
   139
   140 				; ============================================
   141 				; CALC SCREEN Y - convert world Y to screen Y
   142 				; Input: A = world Y lo, X = world Y hi (or 0)
   143 				;        Subtracts cam_y_hi:cam_y
   144 				; Output: A = screen Y (0-191), C=1 if valid
   145 				;         C=0 if off-screen (top or bottom)
   146 				; Clobbers: zt, zt2
   147 				; ============================================
   148 A585			.proc calc_scr_y
   149 A585 38			        sec
   150 A586 E5 DF		        sbc cam_y
   151 A588 85 80		        sta zt
   152 A58A 8A			        txa
   153 A58B E5 E1		        sbc cam_y_hi
   154 A58D 90 0A		        bcc ?off                ; negative = above camera
   155 A58F D0 08		        bne ?off                ; hi > 0 = below screen (> 255)
   156 A591 A5 80		        lda zt
   157 A593 C9 C0		        cmp #192
   158 A595 B0 02		        bcs ?off                ; >= 192 = below game area
   159 A597 38			        sec                     ; C=1 = valid
   160 A598 60			        rts
   161 A599 18			?off    clc                     ; C=0 = off-screen
   162 A59A 60			        rts
   163 				.endp
   164
   165 				; Tile property tables (moved from game.asm to save main segment space)
   166 A59B			tile_solid
   167 A59B 00 01 00 01 01 00 +         dta 0,1,0,1,1,0,1,1,1,1,1,0,0,0,0,1
   168 A5AB 01 01 01 01 01 00 +         dta 1,1,1,1,1,0,1,0,1,1,1,1,0,0
   169 A5B9			tile_oneway
   170 A5B9 00 00 01 00 00 00 +         dta 0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,0
   171 A5C9 00 00 00 00 00 01 +         dta 0,0,0,0,0,1,0,1,0,0,0,0,0,0
   172 A5D7			tile_halfh
   173 A5D7 00 00 00 00 00 00 +         dta 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0
   174 A5E7 00 00 00 00 00 00 +         dta 0,0,0,0,0,0,0,0,0,0,0,0,0,0
   332
   333 02E0-02E1> 00 20		        run main
   334
   335 				; (end of main.asm — uploads, init, and LUTs are in separate files)
