(back to project page)

Sabotage Disassembly

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; Sabotage for the Apple II, by Mark Allen                                     ;
                   ; Copyright 1981 Mark Allen, On-Line Systems                                   ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; Disassembly by Andy McFadden, using 6502bench SourceGen v1.9.0               ;
                   ; Last updated 2024/08/18                                                      ;
                   ;                                                                              ;
                   ; Hat tip to pixjuan and hfeiges for the information at                        ;
                   ; https://gist.github.com/pixjuan/e3e26a070b958bd415e467a2e93dbdda.            ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

                   ; 
                   ; The game uses the full height of the hi-res screen, but limits the horizontal
                   ; positions of most objects to 7-258 horizontally (bytes 1-36, inclusive).  X
                   ; coordinates are generally stored as 16-bit values; some are stored as 8.8
                   ; fixed point values, more often they're stored as a hi-res byte column value
                   ; (position div 7, 0-39) and a position-within-byte value (position mod 7, 0-6).
                   ; 
                   ; Some objects, like paratroopers, are column-aligned.  As a result, several
                   ; data arrays have 40 elements.  The white base of the gun turret spans six
                   ; columns, 16-21 inclusive.  The middle 4 are a "no landing zone" for
                   ; paratroopers.
                   ; 
                   ; Notes on the player's gun turret:
                   ;  - There are 55 possible angles, 1-55.  1 is straight right, 28 is straight
                   ; up, 55 is straight left.
                   ;  - The input mechanism limits the turret angle to the range [4,52], so it's
                   ; not possible to fire flat horizontally to either side.
                   ;  - There are 19 turret bitmaps; each bitmap can be used for up to 3 angles.
                   ;  - Angle 0 isn't used.  The bitmap for angle 0 is blank, shown when the gun is
                   ; exploding.
                   ;  - The keyboard D/F keys change the angle by 6 steps.
                   ; 
                   ; Basic testing / cheating:
                   ;   40fc: 01  # progress through helicopter difficulty levels more quickly
                   ;   58f8: 60  # disable sabotage
                   ;   6a78: 00  # ignore bomb strikes
                   ; 

                   MON_BRKV        .eq     $03f0  {addr/2}   ;address of BRK handler
                   MON_SOFTEVEC    .eq     $03f2  {addr/2}   ;address of RESET handler
                   MON_PWREDUP     .eq     $03f4             ;power-up RESET checksum
                   BAS_AMPERV      .eq     $03f5  {addr/3}   ;Applesoft ampersand vector
                   MON_USRADDR     .eq     $03f8  {addr/3}   ;jump to function that handles monitor Ctrl-Y
                   MON_NMIVEC      .eq     $03fb  {addr/3}   ;jump to function that handles NMI
                   MON_IRQADDR     .eq     $03fe  {addr/2}   ;address of IRQ handler
                   TEXT_SCREEN     .eq     $0400  {addr/1024}
                   HIRES_SCREEN    .eq     $2000  {addr/8192} ;hi-res page 1
                   KBD             .eq     $c000             ;R last key pressed + 128
                   KBDSTRB         .eq     $c010             ;RW keyboard strobe
                   SPKR            .eq     $c030             ;RW toggle speaker
                   TXTCLR          .eq     $c050             ;RW display graphics
                   TXTSET          .eq     $c051             ;RW display text
                   MIXCLR          .eq     $c052             ;RW display full screen
                   TXTPAGE1        .eq     $c054             ;RW display page 1
                   HIRES           .eq     $c057             ;RW display hi-res graphics
                   BUTN0           .eq     $c061             ;R switch input 0 / open-apple
                   PADDL0          .eq     $c064             ;R analog input 0
                   PTRIG           .eq     $c070             ;RW analog input reset

                                   .addrs  $1d00
1d00: 4c 1f 1d     Start           jmp     FillHoles

1d03: 7a 22 95 20+                 .junk   28

                   ; Fill all the text page 1 screen holes with a pattern.  This is verified later
                   ; as part of the copy protection (see $428b).  These would be altered by any I/O
                   ; activity, so this helps prevent a scenario where somebody halts the game on
                   ; the title screen and saves a memory image to disk.
                   ; 
                   ; 0478: 04 17 e0 2b fc 0f 18 e3
                   ; 04f8: 54 a7 70 bb 4c df 28 f3
                   ;  ...
                   ]hole_ptr       .var    $00    {addr/2}

1d1f: a9 04        FillHoles       lda     #>TEXT_SCREEN     ;start at $0478
1d21: 85 01                        sta     ]hole_ptr+1
1d23: a9 78                        lda     #<TEXT_SCREEN+120
1d25: 85 00                        sta     ]hole_ptr
1d27: a2 08                        ldx     #$08              ;do fancy math on 8 screen holes
1d29: a9 34                        lda     #$34
1d2b: a0 07        @Loop1          ldy     #$07              ;8 entries per hole
1d2d: 49 65        @Loop2          eor     #$65
1d2f: 18                           clc
1d30: 69 92                        adc     #$92
1d32: 91 00                        sta     (]hole_ptr),y
1d34: 88                           dey
1d35: 10 f6                        bpl     @Loop2
1d37: ca                           dex
1d38: f0 0f                        beq     @FillDone
1d3a: 48                           pha
1d3b: a5 00                        lda     ]hole_ptr         ;advance to next hole
1d3d: 18                           clc
1d3e: 69 80                        adc     #$80
1d40: 85 00                        sta     ]hole_ptr
1d42: 68                           pla
1d43: 90 e6                        bcc     @Loop1
1d45: e6 01                        inc     ]hole_ptr+1
1d47: b0 e2                        bcs     @Loop1

1d49: 18           @FillDone       clc
1d4a: 90 04                        bcc     @EraseStart       ;(always)

1d4c: ff 1d 1e 1d                  .junk   4

                   ; Erase $1d00-1d4f.
1d50: a9 ea        @EraseStart     lda     #$ea              ;NOP opcode
1d52: a2 50                        ldx     #$50
1d54: 9d 00 1d     @EraseLoop      sta     Start,x
1d57: ca                           dex
1d58: d0 fa                        bne     @EraseLoop

                   ; Relocate $2000-5fff to $4000-7fff.
                   ]src_ptr        .var    $78    {addr/2}
                   ]dst_ptr        .var    $7a    {addr/2}

1d5a: a9 5f                        lda     #$5f              ;end of source range
1d5c: 85 79                        sta     ]src_ptr+1
1d5e: a9 7f                        lda     #$7f              ;end of destination range
1d60: 85 7b                        sta     ]dst_ptr+1
1d62: a0 00                        ldy     #$00
1d64: 84 78                        sty     ]src_ptr
1d66: 84 7a                        sty     ]dst_ptr
1d68: a2 40                        ldx     #$40              ;40 pages
1d6a: b1 78        @CopyLoop       lda     (]src_ptr),y      ;copy
1d6c: 91 7a                        sta     (]dst_ptr),y
1d6e: c8                           iny
1d6f: d0 f9                        bne     @CopyLoop
1d71: c6 79                        dec     ]src_ptr+1        ;advance to previous page
1d73: c6 7b                        dec     ]dst_ptr+1
1d75: ca                           dex
1d76: d0 f2                        bne     @CopyLoop

                   ; Do some copy-protection-ish things.
1d78: ee 63 42                     inc     set_to_one        ;set this to one, so game works
1d7b: a2 20                        ldx     #$20
1d7d: 8e f0 42                     stx     set_to_20
1d80: ad 61 42                     lda     stop_vector       ;get address of self-destruct code
1d83: ae 62 42                     ldx     stop_vector+1
1d86: 8d f0 03                     sta     MON_BRKV          ;set BRK handler
1d89: 8e f1 03                     stx     MON_BRKV+1
1d8c: 8d f2 03                     sta     MON_SOFTEVEC      ;set RESET handler
1d8f: 8e f3 03                     stx     MON_SOFTEVEC+1
1d92: 8d f6 03                     sta     BAS_AMPERV+1      ;set Applesoft ampersand handler
1d95: 8e f7 03                     stx     BAS_AMPERV+2
1d98: 8d f9 03                     sta     MON_USRADDR+1     ;set Ctrl+Y handler
1d9b: 8e fa 03                     stx     MON_USRADDR+2
1d9e: 8d fc 03                     sta     MON_NMIVEC+1      ;set NMI handler
1da1: 8e fd 03                     stx     MON_NMIVEC+2
1da4: 8d fe 03                     sta     MON_IRQADDR       ;set IRQ handler
1da7: 8e ff 03                     stx     MON_IRQADDR+1
1daa: ad f3 03                     lda     MON_SOFTEVEC+1
1dad: 49 a5                        eor     #$a5
1daf: 8d f4 03                     sta     MON_PWREDUP
1db2: 4c bc 42                     jmp     EraseAndStart     ;erase this code and start the game

1db5: 00 00 00 00+                 .junk   587
                                   .adrend ↑ ~$1d00

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; Game "cold" initialization entry point, called after all of the relocating   ;
                   ; and zeroing-out has finished.                                                ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                   .addrs  $4000
4000: a9 00        ColdInit        lda     #$00              ;init scores
4002: 8d eb 42                     sta     cur_score
4005: 8d ec 42                     sta     cur_score+1
4008: 8d e6 42                     sta     high_score
400b: 8d e7 42                     sta     high_score+1
                   ; 
                   ; Game "warm" initialization.  Come back here every time time the game ends.
                   ; 
400e: ad ec 42     WarmInit        lda     cur_score+1       ;see if score beat old high score
4011: cd e7 42                     cmp     high_score+1
4014: 90 16                        bcc     @NotHigh
4016: d0 08                        bne     @NewHigh
4018: ad eb 42                     lda     cur_score
401b: cd e6 42                     cmp     high_score
401e: 90 0c                        bcc     @NotHigh
4020: ad ec 42     @NewHigh        lda     cur_score+1       ;overwrite high score with current score
4023: 8d e7 42                     sta     high_score+1
4026: ad eb 42                     lda     cur_score
4029: 8d e6 42                     sta     high_score
402c: 20 8c 6d     @NotHigh        jsr     PrintTitleAndInstr ;print title screen
402f: ad 51 c0                     lda     TXTSET            ;turn off graphics, show text
4032: ad 54 c0                     lda     TXTPAGE1          ;text page 1
                   ; Pause for 4+((256*5)-1+5)*256-1=328707 cycles, presumably to ensure they get
                   ; to see the game-over screen even if they were frantically hitting keys.
4035: a0 00                        ldy     #$00
4037: a2 00                        ldx     #$00
4039: ca           @PauseLoop      dex
403a: d0 fd                        bne     @PauseLoop
403c: 88                           dey
403d: d0 fa                        bne     @PauseLoop
                   ; Get the "steerable shells?" response.
403f: a2 00                        ldx     #$00
4041: 8e 10 c0                     stx     KBDSTRB           ;clear pending key
4044: ad 00 c0     @KeyLoop        lda     KBD
4047: 10 fb                        bpl     @KeyLoop          ;wait for input
4049: a2 00                        ldx     #$00
404b: 8e 10 c0                     stx     KBDSTRB
404e: 8e ad 60                     stx     is_steerable
4051: c9 ce                        cmp     #“N”
4053: f0 07                        beq     @GotSteer         ;branch with steerable set to zero
4055: c9 d9                        cmp     #“Y”
4057: d0 eb                        bne     @KeyLoop          ;didn't recognize key, loop
4059: ee ad 60                     inc     is_steerable      ;set steerable to one
                   ; 
                   ; Initialize game state.
                   ; 
405c: 20 ec 5c     @GotSteer       jsr     InitGame
405f: ad 50 c0                     lda     TXTCLR            ;enable display of hi-res screen
4062: ad 52 c0                     lda     MIXCLR
4065: ad 57 c0                     lda     HIRES
4068: ad 54 c0                     lda     TXTPAGE1
406b: a9 1e                        lda     #30
406d: 8d d7 42                     sta     flier_create_ctr
4070: a9 1e                        lda     #30               ;init drop counter to 30 frames
4072: 8d d6 42                     sta     drop_event_ctr
4075: a9 04                        lda     #4                ;init turret to far right
4077: 8d ea 42                     sta     cur_gun_angle
407a: 8d e1 42                     sta     desired_gun_angle
407d: a9 00                        lda     #$00
407f: 8d d8 42                     sta     btn_fire_ctr      ;init gun cooldown counter
4082: 8d ef 42                     sta     flier_height_cap  ;reset flier height cap
4085: 8d d9 42                     sta     delay_ctr
4088: 8d da 42                     sta     bomber_mode_flag
408b: 8d e2 42                     sta     heli_mode_ctr
408e: 8d e3 42                     sta     heli_mode_ctr+1
4091: 8d e0 42                     sta     difficulty        ;reset difficulty level
4094: 8d de 42                     sta     paddle_num        ;start with paddle 0
4097: 8d df 42                     sta     paddle_sel_flag   ;unlock paddle choice
                   ; Get initial values for the paddle positions.
409a: 20 12 42                     jsr     ReadPaddle        ;read pdl0
409d: 9d dc 42                     sta     paddle0_prev,x    ;save result (X-reg == 0)
40a0: ee de 42                     inc     paddle_num        ;select pdl1
40a3: 20 07 42                     jsr     Delay41K          ;wait for recharge
40a6: 20 12 42                     jsr     ReadPaddle        ;read pdl1
40a9: 9d dc 42                     sta     paddle0_prev,x    ;save result (X-reg == 1)
40ac: 20 07 42                     jsr     Delay41K          ;pause briefly
                   ; Jump into main loop.
40af: a2 ff                        ldx     #$ff
40b1: 4c dd 40                     jmp     LateEntry

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; Main game loop.                                                              ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40b4: ad d9 42     MainLoop        lda     delay_ctr         ;are we waiting for stuff to clear?
40b7: f0 06                        beq     @NotDelay         ;no, do usual things
40b9: ce d9 42                     dec     delay_ctr         ;yes, decrement delay counter and skip stuff
40bc: 4c 2a 41                     jmp     @UpdateGame

                   ; Update counters and check for a mode change.  The helicopter phases run for 6
                   ; rounds of 768 frames, with the fliers getting lower each round.  The bomber
                   ; phases run until (N+1)*4 bombs have been dropped, where N starts at zero and
                   ; increases with each bombing wave, to a maximum of 5.
40bf: ad da 42     @NotDelay       lda     bomber_mode_flag  ;are we in bomber mode?
40c2: f0 2c                        beq     @UpdModeCtr       ;no, branch
40c4: ad db 42                     lda     bomb_drop_ctr     ;do we have more bombs to drop?
40c7: d0 61                        bne     @UpdateGame       ;yes, skip this
                   ; All bombs have been dropped, switch back to helicopters.
40c9: a9 64                        lda     #100              ;stall for 100 frames to let bombers clear
40cb: 8d d9 42                     sta     delay_ctr
40ce: a9 00                        lda     #$00              ;reset height so fliers are at top
40d0: 8d ef 42                     sta     flier_height_cap  ;fliers at very top
40d3: 8d da 42                     sta     bomber_mode_flag  ;back to helicopters
                   ; Update bomb parameters for future round.
40d6: ae e0 42                     ldx     difficulty        ;get overall difficulty level
40d9: e0 05                        cpx     #5                ;at the limit?  (We drop (n+1)*4)
40db: f0 4d                        beq     @UpdateGame       ;yes, don't change the quantity
40dd: e8           LateEntry       inx                       ;no, make it harder
40de: 8e e0 42                     stx     difficulty        ;update difficulty
40e1: 8d db 42                     sta     bomb_drop_ctr     ;? A-reg is usually 0, could be initial paddle read
40e4: bd 0d 44                     lda     para_thresholds,x ;configure paratrooper drop threshold
40e7: 8d ed 42                     sta     para_drop_thresh
40ea: bd 13 44                     lda     flier_thresholds,x ;configure flier creation threshold
40ed: 8d ee 42                     sta     flier_create_thresh
                   ; Update the counter that advances the game mode / difficulty.
40f0: ee e2 42     @UpdModeCtr     inc     heli_mode_ctr     ;update the game mode counter
40f3: d0 35                        bne     @UpdateGame
40f5: ee e3 42                     inc     heli_mode_ctr+1
40f8: ad e3 42                     lda     heli_mode_ctr+1
40fb: c9 03                        cmp     #$03              ;has the counter hit $300 (768)?
40fd: 90 2b                        bcc     @UpdateGame       ;no, keep playing
                   ; Limit reached, time for a height change or switch to bombing.
40ff: a9 00                        lda     #$00
4101: 8d e3 42                     sta     heli_mode_ctr+1   ;reset mode change counter
4104: a9 06                        lda     #6                ;delay for 6 frames
4106: 8d d9 42                     sta     delay_ctr
4109: ee ef 42                     inc     flier_height_cap  ;move fliers closer to the bottom
410c: ad ef 42                     lda     flier_height_cap
410f: c9 06                        cmp     #6                ;reached limit?
4111: 90 17                        bcc     @UpdateGame       ;not yet, branch
4113: a9 01                        lda     #1                ;yes, reset height to level 1
4115: 8d ef 42                     sta     flier_height_cap
4118: a9 64                        lda     #100              ;run for 100 frames to let fliers get clear
411a: 8d d9 42                     sta     delay_ctr
411d: 8d da 42                     sta     bomber_mode_flag  ;switch to bombing mode
4120: ae e0 42                     ldx     difficulty        ;get difficulty (0-5)
4123: e8                           inx                       ;add 1 (1-6)
4124: 8a                           txa
4125: 0a                           asl     A                 ;multiply by 4 (4-24)
4126: 0a                           asl     A
4127: 8d db 42                     sta     bomb_drop_ctr     ;set bomb drop counter
                   ; Perform game updates.
412a: ad 63 42     @UpdateGame     lda     set_to_one        ;copy protection?
412d: f0 22                        beq     @SkipMost         ;if it got zeroed, skip a bunch of stuff
412f: 20 f8 58                     jsr     CheckSabotage     ;handle sabotage in progress
4132: 20 be 4a                     jsr     UpdateParas       ;update falling paratroopers
4135: 20 3f 42                     jsr     CheckButton       ;check the fire button
4138: 20 5c 6c                     jsr     MakeSounds        ;generate sound effects
413b: 20 ac 5e                     jsr     UpdateShells      ;update shell movement
413e: 20 d6 69                     jsr     UpdateBombs       ;update bomb movement
4141: ce d6 42                     dec     drop_event_ctr    ;time to check for bomb/para drops?
4144: d0 0b                        bne     @SkipMost         ;not yet, branch
4146: a9 15                        lda     #21               ;reset counter to 21 frames
4148: 8d d6 42                     sta     drop_event_ctr
414b: 20 1c 44                     jsr     TryDropPara       ;try to drop paratroopers
414e: 20 28 69                     jsr     TryDropBomb       ;try to drop bombs
4151: 20 7a 42     @SkipMost       jsr     DoUpdates         ;do more updates (fliers, shrapnel, sounds)
4154: ad e8 42                     lda     exploding_gun_ctr ;is the gun exploding?
4157: d0 03                        bne     @SkipDraw         ;yes, skip redraw
4159: 20 24 46                     jsr     RedrawGun         ;erase and redraw the gun, if it has moved
415c: 20 3f 42     @SkipDraw       jsr     CheckButton       ;check paddle button if cooldown has expired
415f: ce d7 42                     dec     flier_create_ctr  ;decrement cooldown counter
4162: d0 08                        bne     @ReadKeyboard     ;not ready yet, branch
4164: a9 0a                        lda     #10               ;reset cooldown to 10 frames
4166: 8d d7 42                     sta     flier_create_ctr
4169: 20 9a 61                     jsr     TryCreateFlier    ;try to create a new flier
                   ; Handle keyboard input.
416c: ad 00 c0     @ReadKeyboard   lda     KBD               ;key pending?
416f: 10 30                        bpl     @NoKeyHit         ;no, branch
4171: a2 00                        ldx     #$00
4173: 8e 10 c0                     stx     KBDSTRB           ;reset input
4176: c9 c4                        cmp     #“D”              ;was it 'D' (rotate left)?
4178: d0 0d                        bne     @NotLfKey         ;no, branch
417a: ad e1 42                     lda     desired_gun_angle ;yes, update posn (C will be set)
417d: 69 05                        adc     #5                ;add 6
417f: c9 35                        cmp     #53               ;>= 53?
4181: 90 15                        bcc     @SetNewPosn       ;no, keep it
4183: a9 34                        lda     #52               ;yes, clamp upper value to 52
4185: b0 11                        bcs     @SetNewPosn

4187: c9 c6        @NotLfKey       cmp     #“F”              ;was it 'F' (rotate right)?
4189: d0 13                        bne     @NotRtKey         ;no, branch
418b: ad e1 42                     lda     desired_gun_angle ;yes, update posn (C will be set)
418e: e9 06                        sbc     #6                ;subtract 6
4190: 30 04                        bmi     @IsNeg            ;if negative, branch
4192: c9 04                        cmp     #4                ;less than 4?
4194: b0 02                        bcs     @SetNewPosn       ;no, keep it
4196: a9 04        @IsNeg          lda     #4                ;clamp lower value to 4
4198: 8d e1 42     @SetNewPosn     sta     desired_gun_angle
419b: 4c a1 41                     jmp     @NoKeyHit

419e: 20 ea 5d     @NotRtKey       jsr     FireShell         ;keys other than 'F' and 'D' fire the gun

                   ; Handle game paddle / joystick input.  We watch both paddles until one shows
                   ; non-trivial movement.  We then focus on that one paddle.
41a1: 20 12 42     @NoKeyHit       jsr     ReadPaddle        ;read current paddle (leaves pdl index in X-reg)
41a4: a8                           tay                       ;stash a copy in Y-reg
41a5: 38                           sec
41a6: fd dc 42                     sbc     paddle0_prev,x    ;subtract previous value
41a9: c9 ff                        cmp     #$ff              ;is it the same or nearly the same value?
41ab: b0 10                        bcs     @SmallMove        ;yes, branch
41ad: c9 02                        cmp     #$02
41af: 90 0c                        bcc     @SmallMove        ;yes, branch
41b1: 98                           tya                       ;non-trivial move, get angle back out of Y-reg
41b2: 9d dc 42                     sta     paddle0_prev,x    ;save for next time
41b5: 8d e1 42                     sta     desired_gun_angle ;set new gun angle
41b8: a9 01                        lda     #$01
41ba: 8d df 42                     sta     paddle_sel_flag   ;set flag indicating paddle has been chosen
41bd: ad df 42     @SmallMove      lda     paddle_sel_flag   ;has paddle been chosen?
41c0: d0 08                        bne     @NoFlip           ;yes, branch
41c2: ad de 42                     lda     paddle_num        ;switch to the other paddle
41c5: 49 01                        eor     #%00000001        ; so we check that next frame
41c7: 8d de 42                     sta     paddle_num

41ca: ad e1 42     @NoFlip         lda     desired_gun_angle ;get angle specified by paddle / keyboard input
41cd: 38                           sec
41ce: ed ea 42                     sbc     cur_gun_angle     ;subtract current angle
41d1: 10 08                        bpl     @AnglePos         ;if >= 0, branch
41d3: c9 fc                        cmp     #$fc              ;is angle change < -4?
41d5: b0 0a                        bcs     @DeltaOkay        ;no, branch
41d7: a9 fc                        lda     #$fc              ;clamp to -4
41d9: 30 06                        bmi     @DeltaOkay

41db: c9 05        @AnglePos       cmp     #$05              ;is angle change >= 5?
41dd: 90 02                        bcc     @DeltaOkay        ;no, branch
41df: a9 04                        lda     #$04              ;clamp to 4
41e1: 18           @DeltaOkay      clc
41e2: 6d ea 42                     adc     cur_gun_angle     ;add delta to new gun angle
41e5: 8d ea 42                     sta     cur_gun_angle
                   ; Check to see if we're exploding.
41e8: ad e8 42                     lda     exploding_gun_ctr ;are we exploding?
41eb: d0 03                        bne     @Explode          ;yes, handle that
41ed: 4c b4 40                     jmp     MainLoop          ;no, business as usual

41f0: c9 ff        @Explode        cmp     #$ff              ;new explosion?
41f2: d0 08                        bne     @NotNew           ;no, branch
41f4: 20 06 6c                     jsr     ExplodeGun        ;yes, start gun explosion
41f7: a9 64                        lda     #100              ;run explosion for 100 frames
41f9: 8d e8 42                     sta     exploding_gun_ctr
41fc: ce e8 42     @NotNew         dec     exploding_gun_ctr ;have we watched our demise for long enough?
41ff: f0 03                        beq     @JumpWarmInit     ;yes, restart game
4201: 4c b4 40                     jmp     MainLoop          ;no, keep running the main loop

4204: 4c 0e 40     @JumpWarmInit   jmp     WarmInit          ;restart game

                   ; 
                   ; Delay for 4+((256*5)-1+5)*32-1+6=41097 cycles.
                   ; 
4207: a0 20        Delay41K        ldy     #$20
4209: a2 00                        ldx     #$00
420b: ca           @Loop           dex
420c: d0 fd                        bne     @Loop
420e: 88                           dey
420f: d0 fa                        bne     @Loop
4211: 60                           rts

                   ; 
                   ; Standard analog paddle read routine, copied from monitor PREAD.
                   ; 
                   ; Reads the paddle specified by $42de (0 or 1).  Returns a gun angle in A-reg.
                   ; 
                   ; On exit:
                   ;   X-reg: paddle number
                   ;   A-reg: gun angle, range [4,52]
                   ; 
                   ]pdl_posn       .var    $74    {addr/1}

4212: ae de 42     ReadPaddle      ldx     paddle_num        ;0 or 1
4215: ad 70 c0                     lda     PTRIG             ;trigger paddle discharge
4218: a0 00                        ldy     #$00              ;init count
421a: ea                           nop
421b: ea                           nop
421c: bd 64 c0     @PdlLoop        lda     PADDL0,x          ;4 done yet?
421f: 10 04                        bpl     @PdlDone          ;2 yes, branch
4221: c8                           iny                       ;2
4222: d0 f8                        bne     @PdlLoop          ;3 (11 cycles total in loop)
4224: 88                           dey                       ;if we overflowed Y-reg over, dec to 255
4225: 98           @PdlDone        tya
                   ; trim result to central range of motion
4226: 4a                           lsr     A                 ;Y-reg is [0,255]
4227: 4a                           lsr     A                 ;divide by 4 to get [0,63]
4228: 38                           sec
4229: e9 08                        sbc     #$08              ;subtract 8 (now [-8,55])
422b: 10 02                        bpl     @NonNeg           ;value < 0?
422d: a9 00                        lda     #$00              ;yes, clamp to zero (now [0,55])
422f: c9 31        @NonNeg         cmp     #49               ;value < 49?
4231: 90 02                        bcc     @NotBig           ;yes, branch
4233: a9 30                        lda     #48               ;no, trim to [0,48]
4235: 85 74        @NotBig         sta     ]pdl_posn
                   ; flip it and map it to a gun angle: [0,48] --> [52,4]
4237: a9 30                        lda     #48
4239: 38                           sec
423a: e5 74                        sbc     ]pdl_posn
423c: 69 03                        adc     #$03              ;carry is still set, so this is +4
423e: 60                           rts

                   ; 
                   ; Checks to see if the button on the selected paddle is pressed.  If so, a shell
                   ; is fired.
                   ; 
423f: ad df 42     CheckButton     lda     paddle_sel_flag   ;have we selected a paddle?
4242: f0 1c                        beq     @Return           ;no, bail
4244: ae d8 42                     ldx     btn_fire_ctr      ;did we fire recently?
4247: f0 07                        beq     @CheckBtn         ;no, go check the button
4249: ca                           dex                       ;yes, decrement the counter
424a: 8e d8 42                     stx     btn_fire_ctr
424d: 4c 60 42                     jmp     @Return

4250: ae de 42     @CheckBtn       ldx     paddle_num        ;get selected paddle
4253: bd 61 c0                     lda     BUTN0,x           ;read matching button
4256: 10 08                        bpl     @Return           ;not pressed, bail
4258: 20 ea 5d                     jsr     FireShell         ;fire a shell
425b: a9 0c                        lda     #12               ;wait 12 frames before firing again
425d: 8d d8 42                     sta     btn_fire_ctr
4260: 60           @Return         rts

                   ; 
                   ; Address of self-destruct function.  Reset key and interrupts are directed
                   ; here.
4261: 66 42        stop_vector     .dd2    SelfDestruct
                   ; 
                   ; Value set to one during initialization and never changed.  Part of the copy
                   ; protection?
4263: 00           set_to_one      .dd1    $00
4264: bb 42                        .junk   2

                   ; 
                   ; Erases memory and crashes.
                   ; 
                   • Clear variables
                   ]erase_ptr      .var    $74    {addr/2}

4266: a9 7f        SelfDestruct    lda     #$7f
4268: 85 75                        sta     ]erase_ptr+1
426a: a9 00                        lda     #$00
426c: 85 74                        sta     ]erase_ptr
426e: a0 00                        ldy     #$00
4270: 91 74        @Loop           sta     (]erase_ptr),y
4272: c8                           iny
4273: d0 fb                        bne     @Loop
4275: c6 75                        dec     ]erase_ptr+1
4277: 4c 70 42                     jmp     @Loop

                   ; 
                   ; Updates fliers, checks the game paddle fire button, makes sounds... and
                   ; periodically checks the screen holes for copy protection reasons.
                   ; 
427a: 20 8a 62     DoUpdates       jsr     UpdateFliers      ;advance helicopters and bombers
427d: 20 3f 42                     jsr     CheckButton       ;check the fire button
4280: 20 5c 6c                     jsr     MakeSounds        ;click the speaker
4283: 20 ee 52                     jsr     UpdateShrapnel    ;update shrapnel movement
4286: ee bb 42                     inc     cp_check_ctr      ;time to check the screen holes?
4289: d0 2f                        bne     @Return           ;nonzero, don't check this time

                   ; Verify contents of screen holes.  (Copy protection.)
                   ]hole_ptr       .var    $00    {addr/2}

428b: a9 04                        lda     #>TEXT_SCREEN     ;start at $0478
428d: 85 01                        sta     ]hole_ptr+1
428f: a9 78                        lda     #<TEXT_SCREEN+120
4291: 85 00                        sta     ]hole_ptr
4293: a2 08                        ldx     #$08              ;check 8 screen holes
4295: a9 34                        lda     #$34
4297: a0 07        @HolesLoop      ldy     #$07              ;8 entries per hole
4299: 49 65        @HoleLoop       eor     #$65
429b: 18                           clc
429c: 69 92                        adc     #$92
429e: d1 00                        cmp     (]hole_ptr),y     ;does it match?
42a0: f0 03                        beq     @Okay             ;yes, continue
42a2: 20 66 42                     jsr     SelfDestruct      ;no, die horribly
42a5: 88           @Okay           dey
42a6: 10 f1                        bpl     @HoleLoop
42a8: ca                           dex
42a9: f0 0f                        beq     @Return
42ab: 48                           pha
42ac: a5 00                        lda     ]hole_ptr         ;move to next hole
42ae: 18                           clc
42af: 69 80                        adc     #$80
42b1: 85 00                        sta     ]hole_ptr
42b3: 68                           pla
42b4: 90 e1                        bcc     @HolesLoop
42b6: e6 01                        inc     ]hole_ptr+1
42b8: b0 dd                        bcs     @HolesLoop

42ba: 60           @Return         rts

42bb: 00           cp_check_ctr    .dd1    $00               ;screen hole (copy protection) test counter

                   ; 
                   ; Zero out memory from $1d00-1fff, then jump to game init code.
                   ; 
42bc: a2 03        EraseAndStart   ldx     #$03              ;3 pages
42be: a9 1d                        lda     #$1d              ;starting at $1d00
42c0: 85 75                        sta     ]erase_ptr+1
42c2: a9 00                        lda     #$00
42c4: 85 74                        sta     ]erase_ptr
42c6: a0 00                        ldy     #$00
42c8: 91 74        @Loop           sta     (]erase_ptr),y
42ca: c8                           iny
42cb: d0 fb                        bne     @Loop
42cd: e6 75                        inc     ]erase_ptr+1
42cf: ca                           dex
42d0: d0 f6                        bne     @Loop
42d2: 4c 00 40                     jmp     ColdInit          ;start the game

                   ; 
                   ; Main variable storage.
                   ; 
                   active_bomber_count
42d5: 00                           .dd1    $00               ;nonzero if a flier is active (for sound)
42d6: 00           drop_event_ctr  .dd1    $00               ;counter for bomb/para drop cooldown
                   flier_create_ctr
42d7: 00                           .dd1    $00               ;counter for flier creation cooldown
42d8: 00           btn_fire_ctr    .dd1    $00               ;counter for shot cooldown (pdl button only)
42d9: 00           delay_ctr       .dd1    $00               ;used to run frames with no new bad guys
                   bomber_mode_flag
42da: 00                           .dd1    $00               ;0=heli, nonzero=bombers
42db: 00           bomb_drop_ctr   .dd1    $00               ;something to do with bomb availability
42dc: 00           paddle0_prev    .dd1    $00               ;previous gun angle read from paddle 0
42dd: 00           paddle1_prev    .dd1    $00               ;previous gun angle read from paddle 1
42de: 00           paddle_num      .dd1    $00               ;selected paddle (0 or 1)
42df: 00           paddle_sel_flag .dd1    $00               ;bool 0/1: have we decided which paddle to use?
42e0: 00           difficulty      .dd1    $00               ;game difficulty level, 0-5
                   desired_gun_angle
42e1: 00                           .dd1    $00               ;[4,52] desired gun angle; gun traverses slowly
42e2: 00 00        heli_mode_ctr   .dd2    $0000             ;game mode change counter
42e4: 00 00        rng_state       .dd2    $0000             ;state for crude random number generator
42e6: 00 00        high_score      .dd2    $0000
                   exploding_gun_ctr
42e8: 00                           .dd1    $00               ;$00=normal, $ff=explosion start, other=countdown
42e9: 00           old_gun_angle   .dd1    $00               ;previous angle of gun turret (for erase)
42ea: 00           cur_gun_angle   .dd1    $00               ;current angle of gun turret
42eb: 00 00        cur_score       .dd2    $0000
                   para_drop_thresh
42ed: 00                           .dd1    $00               ;likelihood of dropping para (higher=more likely)
                   flier_create_thresh
42ee: 00                           .dd1    $00               ;likelihood of creating flier (higher=more likely)
                   flier_height_cap
42ef: 00                           .dd1    $00               ;cap on index into flier height array, 0-5
42f0: 00           set_to_20       .dd1    $00               ;set to $20 during init, not used?
42f1: 00 00 00                     .junk   3
                   ; 
                   ; Shell state.  Maximum of 8 shells, 9 bytes each.
                   ; 
42f4: 01           shell_base      .dd1    $01               ;used during init
                   shell_active_flag
42f5: 00                           .dd1    $00               ;bool 0/1: is this shell active?
42f6: 00           shell_x_hi      .dd1    $00               ;shell 8.8 X position, high byte
42f7: 00           shell_x_lo      .dd1    $00               ;shell 8.8 X position, low byte
42f8: 00           shell_y_hi      .dd1    $00               ;shell 8.8 Y position, high byte
42f9: 00           shell_y_lo      .dd1    $00               ;shell 8.8 Y position, low byte
42fa: 00           shell_dx_hi     .dd1    $00               ;shell delta X, low byte
42fb: 00           shell_dx_lo     .dd1    $00               ;shell delta X, high byte
42fc: 00           shell_dy_hi     .dd1    $00               ;shell delta Y, low byte
42fd: 00           shell_dy_lo     .dd1    $00               ;shell delta Y, high byte
42fe: 00 00 00 00+                 .ds     63
                   ; (end of shell state)
433d: 00 00 00 00+                 .junk   7
                   ; 
                   ; Flier (helicopter/bomber) state.  There can be four on screen at a time, 10
                   ; bytes each.
                   ; 
                   ; The flier status specifies the type and direction:
                   ;   0 - inactive
                   ;   2 - helicopter, moving right
                   ;   4 - helicopter, moving left
                   ;   6 - bomber, moving right
                   ;   8 - bomber, moving left
                   ; 
4344: 00           flier_base      .dd1    $00               ;used during init
                   flier_old_status
4345: 00                           .dd1    $00               ;0/2/4/6/8
4346: 00           flier_old_y     .dd1    $00
4347: 00           flier_old_x_hi  .dd1    $00               ;horizontal position, high part (div 7)
4348: 00           flier_old_x_lo  .dd1    $00               ;horizontal position, low part (mod 7)
4349: 00           flier_status    .dd1    $00               ;0/2/4/6/8
434a: 00           flier_y         .dd1    $00
434b: 00           flier_x_hi      .dd1    $00
434c: 00           flier_x_lo      .dd1    $00
434d: 00           flier_x_vel     .dd1    $00
434e: 00           flier_y_vel     .dd1    $00               ;velocity in Y axis, always zero
434f: 00 00 00 00+                 .ds     30
                   ; 
                   ; Falling paratrooper state.  8 entries, 5 bytes each.
                   ; 
                   ; Stationary paratroopers on the ground are not represented here.
                   ; 
                   ; The meaning of the "status" fields is:
                   ;   $00 - inactive
                   ;   $ff - in free fall, no parachute
                   ;   > 0 - falling with parachute open
                   ;   < 0 - in free fall, before parachute opens
                   ; 
                   ; The fall speed is constant: one row per frame with chute open, three throws
                   ; per frame with chute closed or shot off.  When a paratrooper is dropped, it's
                   ; given a negative status (e.g. $8a) that is decremented.  When it reaches $7f,
                   ; the chute opens.
                   ; 
436d: 00           para_status     .dd1    $00
436e: 00           para_old_status .dd1    $00               ;status (0=inactive, ff=exploding, $80=falling)
436f: 00           para_xpos       .dd1    $00               ;X position, as column (0-39)
4370: 00           para_ypos       .dd1    $00               ;new Y position
4371: 00           para_old_ypos   .dd1    $00               ;Y position (0-191)
4372: 00 00 00 00+                 .ds     35
                   ; 
4395: 00 00 00 00+                 .junk   40
                   ; 
                   ; Unused?
                   ; 
                   ; Entries 1-36 are initialized to zero.
                   ; 
43bd: 00 00 00 00+ unused_43bd?    .fill   40,$00
                   ; 
                   ; Ground height table.  This is the height at which a falling paratrooper will
                   ; stop.  The values in the table increase as paratroopers land.
                   ; 
                   ; There's room for 40 entries, one per column on the hi-res screen, but only
                   ; entries 1-36 are used.
                   ; 
                   ; The turret base is in columns [16,21].  The cannon base is in columns [18,19],
                   ; but the cannon barrel can extend past that.
                   ; 
43e5: 00           ground_height_0 .dd1    $00
43e6: 00 00 00 00+ ground_height_1 .fill   14,$00            ;set to 0
                   ground_height_15
43f4: 00                           .dd1    $00               ;set to 0
                   ground_height_16
43f5: 00                           .dd1    $00               ;set to 3
                   ground_height_17
43f6: 00                           .dd1    $00               ;set to 4
                   ground_height_18
43f7: 00                           .dd1    $00               ;set to 4
                   ground_height_19
43f8: 00                           .dd1    $00               ;set to 4
                   ground_height_20
43f9: 00                           .dd1    $00               ;set to 4
                   ground_height_21
43fa: 00                           .dd1    $00               ;set to 3
                   ground_height_22
43fb: 00 00 00 00+                 .fill   18,$00            ;set to 0
                   ; 
                   ; Event thresholds, indexed by difficulty (0-5).  This is the chance (out of
                   ; 256) that a flier will be created or drop a paratrooper.  At higher difficulty
                   ; levels it's nearly guaranteed to happen.
                   ; 
440d: 50 78 a0 c8+ para_thresholds .bulk   $50,$78,$a0,$c8,$e6,$fe
                   flier_thresholds
4413: 40 64 90 ae+                 .bulk   $40,$64,$90,$ae,$d6,$fe
4419: 6c f2 42                     .junk   3

                   ; 
                   ; Attempts to drop paratroopers from all active helicopters.
                   ; 
                   ; This is called every 21 frames.
                   ; 
                   • Clear variables
                   ]drop_col_lbound .var   $76    {addr/1}
                   ]drop_col_rbound .var   $77    {addr/1}
                   ]flier_idx      .var    $78    {addr/1}
                   ]flier_dir      .var    $79    {addr/1}
                   ]flier_num      .var    $7a    {addr/1}
                   ]flier_xpos     .var    $7b    {addr/1}
                   ]drop_lanes     .var    $7d    {addr/1}
                   ]drop_lane_diff_x2 .var $7e    {addr/1}

441c: ad 28 5b     TryDropPara     lda     sabotage_side     ;sabotage in progress?
441f: 0d d9 42                     ora     delay_ctr         ;are we in a no-action delay mode?
4422: 0d da 42                     ora     bomber_mode_flag  ;are we in bomber mode?
4425: f0 01                        beq     @DoTry            ;none of these are true, branch
4427: 60                           rts

4428: a2 27        @DoTry          ldx     #39               ;40 elements, one per hi-res screen column
442a: a9 00                        lda     #$00              ;initialize array to zeroes
442c: 9d a1 45     @Loop           sta     para_falling_arr,x
442f: ca                           dex
4430: 10 fa                        bpl     @Loop
                   ; For each falling paratrooper, increment the value in the per-column array.
4432: a0 00                        ldy     #$00
4434: b9 6d 43     @Loop           lda     para_status,y     ;currently alive?
4437: 19 6e 43                     ora     para_old_status,y ;previously alive?
443a: f0 06                        beq     @NotAlive         ;no, branch
443c: be 6f 43                     ldx     para_xpos,y       ;get X position (column 0-39)
443f: fe a1 45                     inc     para_falling_arr,x ;increment entry in array
4442: 98           @NotAlive       tya
4443: 18                           clc
4444: 69 05                        adc     #5                ;5 bytes per struct
4446: a8                           tay
4447: c0 28                        cpy     #40               ;done all 8?
4449: 90 e9                        bcc     @Loop             ;no, loop
                   ; 
444b: a2 04                        ldx     #4                ;4 fliers
444d: a9 ff                        lda     #$ff              ;initialize chute parameter array to $ff
444f: 9d d4 45     @Loop           sta     flier_drop_lanes-1,x
4452: ca                           dex
4453: d0 fa                        bne     @Loop
                   ; 
4455: a2 00                        ldx     #$00
4457: 86 7a                        stx     ]flier_num
4459: 86 78        @FlierLoop      stx     ]flier_idx        ;index into flier table (flier_num * 10)
445b: bd 45 43                     lda     flier_old_status,x ;get flier status
445e: f0 56                        beq     @Next             ;inactive, move on
4460: bc 47 43                     ldy     flier_old_x_hi,x  ;get X position
4463: 84 7b                        sty     ]flier_xpos
4465: 4a                           lsr     A                 ;divide flier status (2/4/6/8) by 2
4466: 29 01                        and     #$01              ;get low bit (0=moving left, 1=moving right)
4468: 85 79                        sta     ]flier_dir
446a: f0 02                        beq     @Leftward         ;if moving left, branch
446c: 88                           dey                       ;adjust edge
446d: 88                           dey
446e: c0 00        @Leftward       cpy     #$00              ;is edge off left side of screen?
4470: 10 02                        bpl     @OnLeft           ;no, branch
4472: a0 00                        ldy     #$00              ;yes, clamp to zero
4474: 84 76        @OnLeft         sty     ]drop_col_lbound  ;set left edge of allowed drop area
4476: 98                           tya
4477: 18                           clc
4478: 69 05                        adc     #5                ;add 5 to column
447a: c9 28                        cmp     #40               ;would we end up off right edge of screen?
447c: 90 02                        bcc     @OnRight
447e: a9 27                        lda     #39               ;clamp to right edge
4480: 85 77        @OnRight        sta     ]drop_col_rbound  ;set right edge of allowed drop area
                   ; Set Y-reg to the number of flier "lanes" above the flier we're looking at.  If
                   ; the flier is at the lowest possible height, Y-reg will be zero.
4482: ac ef 42                     ldy     flier_height_cap  ;get height index cap
4485: b9 77 62                     lda     flier_rows,y      ;get lowest flier row allowed by current difficulty
4488: a0 00                        ldy     #$00
448a: 38                           sec
448b: fd 46 43                     sbc     flier_old_y,x     ;subtract flier's height
448e: 4c 95 44                     jmp     @DidSub

4491: c8           @Iter           iny                       ;inc lane diff
4492: 38                           sec
4493: e9 0e                        sbc     #14               ;each flier "lane" is 14 pixels high
4495: d0 fa        @DidSub         bne     @Iter             ;didn't match, loop
                   ; Copy computed values to arrays.
4497: a6 7a                        ldx     ]flier_num        ;get flier index
4499: 98                           tya
449a: 9d d5 45                     sta     flier_drop_lanes,x ;save lane diff
449d: a5 76                        lda     ]drop_col_lbound  ;copy left/right column bracket
449f: 9d cd 45                     sta     flier_drop_left,x
44a2: a5 77                        lda     ]drop_col_rbound
44a4: 9d d1 45                     sta     flier_drop_right,x
44a7: a5 79                        lda     ]flier_dir        ;0=left, 1=right
44a9: 9d c9 45                     sta     flier_drop_dir,x
44ac: d0 02                        bne     @Rightward        ;branch with A-reg=1
44ae: a9 02                        lda     #$02
44b0: 18           @Rightward      clc
44b1: 65 7b                        adc     ]flier_xpos       ;add to old X position
44b3: 9d d9 45                     sta     flier_drop_col,x  ;set as drop column
44b6: e6 7a        @Next           inc     ]flier_num        ;advance flier number and struct index
44b8: a5 78                        lda     ]flier_idx
44ba: 18                           clc
44bb: 69 0a                        adc     #10               ;10 bytes per struct
44bd: aa                           tax
44be: e0 28                        cpx     #40               ;have we processed all 4?
44c0: 90 97                        bcc     @FlierLoop        ;not yet, loop

                   ; We now have values for all 4 possible fliers stored in arrays.  Next, we want
                   ; to walk through them, starting at a random entry, and try to drop from each
                   ; active flier.
                   ; 
                   ; Note $7a (flier_num) now holds the total number of fliers (4).

                   ; Pick a random flier index (0-3).
44c2: ad e4 42                     lda     rng_state         ;get random value
44c5: 29 1f                        and     #$1f              ;reduce to 0-31
44c7: 38                           sec
44c8: e9 04        @RngLoop        sbc     #$04              ;subtract 4 until it goes negative
44ca: b0 fc                        bcs     @RngLoop
44cc: 69 04                        adc     #$04              ;undo the last subtraction (now 0-3)
44ce: aa                           tax                       ;copy to X-reg
                   ; 
44cf: 86 78        @Loop           stx     ]flier_idx        ;save index (0-3)
44d1: ad e5 42                     lda     rng_state+1       ;(update RNG)
44d4: 48                           pha
44d5: 2a                           rol     A
44d6: 68                           pla
44d7: 2a                           rol     A
44d8: 8d e5 42                     sta     rng_state+1
                   ; 
44db: bd d9 45                     lda     flier_drop_col,x  ;copy drop column to ZP
44de: 85 7b                        sta     ]flier_xpos
44e0: bc d5 45                     ldy     flier_drop_lanes,x ;copy lane diff count to ZP
44e3: 84 7d                        sty     ]drop_lanes
44e5: c0 08                        cpy     #$08              ;huge difference (8*14=112)?
44e7: b0 56                        bcs     @Next             ;yes, off end of array, move on
44e9: d9 14 46                     cmp     para_drop_lim_lf,y ;too far left for this height?
44ec: 90 51                        bcc     @Next             ;yes, move on
44ee: d9 1c 46                     cmp     para_drop_lim_rt,y ;too far right for this height?
44f1: f0 02                        beq     @LROkay           ;no, branch
44f3: b0 4a                        bcs     @Next             ;yes, move on
                   ; Check for a reason not to drop.  We want to avoid dropping a paratrooper on
                   ; top of a helicopter flying below us.  We use the left/right bounds computed
                   ; earlier, combined with the difference in height lanes and direction of
                   ; movement of the other flier, to define an exclusion zone.  This isn't very
                   ; precise, but it prevents the problem (so long as we also ensure that chutes
                   ; open later for high-flying helicopters).
44f5: a2 03        @LROkay         ldx     #$03              ;loop through all 4 fliers
44f7: a5 7d        @Loop1          lda     ]drop_lanes       ;get our drop lane count (0=lowest)
44f9: 38                           sec
44fa: fd d5 45                     sbc     flier_drop_lanes,x ;subtract drop lane count for this flier
44fd: f0 3a                        beq     @Next1            ;same lane as us, move on
44ff: 90 38                        bcc     @Next1            ;more than us (above us), move on
4501: 0a                           asl     A                 ;double it
4502: 85 7e                        sta     ]drop_lane_diff_x2 ;save the diff
4504: bc c9 45                     ldy     flier_drop_dir,x  ;get drop direction (0=lf, 1=rt)
4507: f0 16                        beq     @Left1            ;moving left, branch
4509: 18                           clc
450a: 7d d1 45                     adc     flier_drop_right,x ;add lane diff to right edge limit
450d: c5 7b                        cmp     ]flier_xpos       ;are we in that zone?
450f: 90 28                        bcc     @Next1            ;no, move on
4511: a5 7e                        lda     ]drop_lane_diff_x2
4513: 18                           clc
4514: 7d cd 45                     adc     flier_drop_left,x ;now check the left edge
4517: c5 7b                        cmp     ]flier_xpos
4519: f0 24                        beq     @Next             ;too close, bail
451b: 90 22                        bcc     @Next             ;too close, bail
451d: b0 1a                        bcs     @Next1            ;safe, move on

451f: bd d1 45     @Left1          lda     flier_drop_right,x ;same calculation, but they're moving leftward
4522: 38                           sec
4523: e5 7e                        sbc     ]drop_lane_diff_x2
4525: 30 12                        bmi     @Next1            ;looks safe, move on
4527: c5 7b                        cmp     ]flier_xpos
4529: 90 0e                        bcc     @Next1
452b: bd cd 45                     lda     flier_drop_left,x
452e: 38                           sec
452f: e5 7e                        sbc     ]drop_lane_diff_x2
4531: 30 06                        bmi     @Next1
4533: c5 7b                        cmp     ]flier_xpos
4535: f0 08                        beq     @Next             ;too close, bail
4537: 90 06                        bcc     @Next             ;too close, bail
4539: ca           @Next1          dex
453a: 10 bb                        bpl     @Loop1
                   ; Looks good, drop a paratrooper if a slot is open and RNG agrees.
453c: 20 4d 45                     jsr     DropPara
                   ; 
453f: a6 78        @Next           ldx     ]flier_idx        ;move to the next flier
4541: e8                           inx
4542: e0 04                        cpx     #$04              ;wrap around at end
4544: 90 02                        bcc     @Lt4
4546: a2 00                        ldx     #$00
4548: c6 7a        @Lt4            dec     ]flier_num        ;have we processed all four?
454a: d0 83                        bne     @Loop             ;no, loop
454c: 60                           rts

                   ; 
                   ; Drops a paratrooper if RNG agrees and we have an empty slot.
                   ; 
                   ; On entry:
                   ;   $78: flier index (0-3)
                   ;   $7b: horizontal column offset (0-39)
                   ;   $7d: flier lane diff (0 for low fliers, more for high)
                   ; 
                   ]mul_tmp        .var    $74    {addr/1}

454d: ad e4 42     DropPara        lda     rng_state         ;get random number
4550: 69 57                        adc     #$57              ;(update RNG)
4552: 8d e4 42                     sta     rng_state
4555: 4d e5 42                     eor     rng_state+1
4558: cd ed 42                     cmp     para_drop_thresh  ;is it below the para drop threshold?
455b: b0 13                        bcs     @Return           ;no, bail
                   ; Find an empty slot.
455d: a0 00                        ldy     #$00
455f: b9 6d 43     @Loop           lda     para_status,y     ;currently active?
4562: 19 6e 43                     ora     para_old_status,y ;previously active?
4565: f0 0a                        beq     @Cont             ;neither, use this one
4567: 98                           tya
4568: 18                           clc
4569: 69 05                        adc     #5                ;para struct is 5 bytes
456b: a8                           tay
456c: c0 28                        cpy     #40               ;have we checked all 8?
456e: 90 ef                        bcc     @Loop             ;not yet, loop
4570: 60           @Return         rts                       ;no empty slots, bail

4571: a6 7b        @Cont           ldx     ]flier_xpos       ;get flier X position
4573: 18                           clc
4574: bd e5 43                     lda     ground_height_0,x ;get height of stuff on ground (landed paras, gun)
4577: 7d a1 45                     adc     para_falling_arr,x ;add falling paratroopers
457a: c9 04                        cmp     #$04              ;will this result in height >= 4?
457c: b0 f2                        bcs     @Return           ;yes, don't drop here
457e: 8a                           txa
457f: 99 6f 43                     sta     para_xpos,y       ;set para X position
4582: fe a1 45                     inc     para_falling_arr,x ;increment number of falling paras in this column
4585: a6 7d                        ldx     ]drop_lanes       ;get flier height diff (0=lowest)
4587: bd 0c 46                     lda     para_fall_dist,x  ;get pre-chute fall distance (negative value)
458a: 99 6d 43                     sta     para_status,y     ;set as para status
458d: a5 78                        lda     ]flier_idx        ;get flier index (0-3)
458f: 0a                           asl     A                 ;multiply by 10 to get flier struct offset
4590: 85 74                        sta     ]mul_tmp
4592: 0a                           asl     A
4593: 0a                           asl     A
4594: 65 74                        adc     ]mul_tmp
4596: aa                           tax
4597: bd 46 43                     lda     flier_old_y,x     ;get flier Y position
459a: 69 0a                        adc     #10               ;add 10 (height of helicopter bitmap)
459c: 99 70 43                     sta     para_ypos,y       ;set as initial position of para
459f: 60                           rts

45a0: 00                           .junk   1
                   ; 
                   ; Count of the number of paratroopers currently falling in each hi-res column.
                   ; 
                   para_falling_arr
45a1: 00 00 00 00+                 .ds     40
                   ; 
                   ; Flier drop parameter arrays.  When considering paratrooper drops, values are
                   ; computed for all 4 possible fliers, and stored here.  These are then evaluated
                   ; by the drop code.
                   ; 
45c9: 00 00 00 00  flier_drop_dir  .ds     4                 ;flier direction (0=left, 1=right)
45cd: 00 00 00 00  flier_drop_left .ds     4                 ;left drop bound
                   flier_drop_right
45d1: 00 00 00 00                  .ds     4                 ;right drop bound
                   flier_drop_lanes
45d5: 00 00 00 00                  .ds     4                 ;number of lanes above lowest lane (0-7)
45d9: 00 00 00 00  flier_drop_col  .ds     4                 ;column para will be dropped in
                   ; 
                   ; Embedded copyright notice, not shown in-game.
                   ; 
45dd: 43 4f 50 59+                 .str    ‘COPYRIGHT (C) 1981  MARK ALLEN, ON LINE SYSTEMS’
                   ; 
                   ; Paratrooper fall distance.  The value determines how many frames the para
                   ; remains in free fall before the chute opens.
                   ; 
460c: 85 89 8e 93+ para_fall_dist  .bulk   $85,$89,$8e,$93,$97,$9c,$a1,$a5
                   ; 
                   ; Left/right column limitations on paratrooper drops.  The table is indexed with
                   ; the lane height difference between the flier and the lowest possible lane, so
                   ; low-flying helicopters use lower indices.
                   ; 
                   ; The net effect, once the difficulty increases and helicopters appear at
                   ; multiple levels, is to restrict high-up helicopters to dropping very near the
                   ; gun turret, while lower-down helicopters can drop across most of the field.
                   ; 
                   para_drop_lim_lf
4614: 05 05 07 09+                 .bulk   $05,$05,$07,$09,$0b,$0d,$0f,$11
                   para_drop_lim_rt
461c: 22 22 20 1e+                 .bulk   $22,$22,$20,$1e,$1c,$1a,$18,$16

                   ; 
                   ; Draws the gun.  The old gun is erased as the new gun is drawn.  Does nothing
                   ; if the old and new position are the same.
                   ; 
                   ; Copies the "new" angle into the "current" angle.
                   ; 
                   • Clear variables
                   ]hires_ptr      .var    $76    {addr/2}
                   ]cur_gun_ptr    .var    $78    {addr/2}
                   ]new_gun_ptr    .var    $7a    {addr/2}
                   ]src_counter    .var    $7c    {addr/1}

4624: ae e9 42     RedrawGun       ldx     old_gun_angle     ;get current gun position
4627: ec ea 42                     cpx     cur_gun_angle     ;has it moved?
462a: f0 70                        beq     @Return           ;no, bail
462c: bc 9d 46                     ldy     gun_posn_to_index,x ;get index into bitmap table
462f: b9 d5 46                     lda     gun_addrs,y       ;copy address to ZP
4632: 85 78                        sta     ]cur_gun_ptr
4634: b9 d6 46                     lda     gun_addrs+1,y
4637: 85 79                        sta     ]cur_gun_ptr+1
4639: ae ea 42                     ldx     cur_gun_angle     ;get new gun position
463c: bc 9d 46                     ldy     gun_posn_to_index,x ;copy address to ZP
463f: b9 d5 46                     lda     gun_addrs,y
4642: 85 7a                        sta     ]new_gun_ptr
4644: b9 d6 46                     lda     gun_addrs+1,y
4647: 85 7b                        sta     ]new_gun_ptr+1

                   ; Draw and erase the 4x12 bitmaps with a single operation.  The gun is always at
                   ; the same screen position, so the rows and columns are hard-coded.
4649: a0 00                        ldy     #$00
464b: 84 7c                        sty     ]src_counter      ;offset into bitmap data
464d: a2 9d                        ldx     #157              ;start on line 157
464f: bd 5e 4d     @DrawLoop       lda     hires_addr_hi,x   ;get hi-res line base address
4652: 85 77                        sta     ]hires_ptr+1      ;copy to ZP
4654: bd 1e 4e                     lda     hires_addr_lo,x
4657: 85 76                        sta     ]hires_ptr
4659: a4 7c                        ldy     ]src_counter      ;get current offset into bitmap
465b: b1 78                        lda     (]cur_gun_ptr),y  ;get byte from bitmap for current gun posn
465d: 51 7a                        eor     (]new_gun_ptr),y  ;combine with byte from bitmap for new gun posn
465f: e6 7c                        inc     ]src_counter      ;advance source index
4661: a0 11                        ldy     #17               ;hi-res column 17
4663: 51 76                        eor     (]hires_ptr),y    ;combine with byte on hi-res screen (erase + draw)
4665: 91 76                        sta     (]hires_ptr),y    ;write to screen
4667: a4 7c                        ldy     ]src_counter      ;repeat for column 18
4669: b1 78                        lda     (]cur_gun_ptr),y
466b: 51 7a                        eor     (]new_gun_ptr),y
466d: e6 7c                        inc     ]src_counter
466f: a0 12                        ldy     #18
4671: 51 76                        eor     (]hires_ptr),y
4673: 91 76                        sta     (]hires_ptr),y
4675: a4 7c                        ldy     ]src_counter      ;repeat for column 19
4677: b1 78                        lda     (]cur_gun_ptr),y
4679: 51 7a                        eor     (]new_gun_ptr),y
467b: e6 7c                        inc     ]src_counter
467d: a0 13                        ldy     #19
467f: 51 76                        eor     (]hires_ptr),y
4681: 91 76                        sta     (]hires_ptr),y
4683: a4 7c                        ldy     ]src_counter      ;repeat for column 20
4685: b1 78                        lda     (]cur_gun_ptr),y
4687: 51 7a                        eor     (]new_gun_ptr),y
4689: e6 7c                        inc     ]src_counter
468b: a0 14                        ldy     #20
468d: 51 76                        eor     (]hires_ptr),y
468f: 91 76                        sta     (]hires_ptr),y
4691: e8                           inx                       ;advance to next line
4692: e0 a9                        cpx     #169              ;reached line 169?
4694: 90 b9                        bcc     @DrawLoop         ;not yet, loop
                   ; Update the "old" angle for erase next time.
4696: ad ea 42                     lda     cur_gun_angle
4699: 8d e9 42                     sta     old_gun_angle
469c: 60           @Return         rts

                   ; 
                   ; Map a gun position [0,55] to a (doubled) index into the 20 gun images [0,38].
                   ; Each image is used for up to three positions.  Note angle 0 maps to the last
                   ; image (index 38).
                   ; 
                   ; There are 56 entries here, but the input routine limits the gun angles to
                   ; [4,52], which means the flat horizontal angles to the left and right can't be
                   ; reached.  Angle 0 is used for the blank image, which is only used when the gun
                   ; is exploding and shouldn't appear.
                   ; 
                   ; Take the gun angle, get the byte from this table, and use it as an index into
                   ; the gun address table to get the bitmap address.
                   ; 
                   gun_posn_to_index
469d: 26 00 00 02+                 .bulk   $26,$00,$00,$02,$02,$02,$04,$04,$04,$06,$06,$06,$08,$08,$08,$0a
                                    +      $0a,$0a,$0c,$0c,$0c,$0e,$0e,$0e,$10,$10,$10,$12,$12,$12,$14,$14
                                    +      $14,$16,$16,$16,$18,$18,$18,$1a,$1a,$1a,$1c,$1c,$1c,$1e,$1e,$1e
                                    +      $20,$20,$20,$22,$22,$22,$24,$24
                   ; Pointers to gun image bitmaps.
46d5: 2d 47        gun_addrs       .dd2    gun_img_00
46d7: 5d 47                        .dd2    gun_img_01
46d9: 8d 47                        .dd2    gun_img_02
46db: bd 47                        .dd2    gun_img_03
46dd: ed 47                        .dd2    gun_img_04
46df: 1d 48                        .dd2    gun_img_05
46e1: 4d 48                        .dd2    gun_img_06
46e3: 7d 48                        .dd2    gun_img_07
46e5: ad 48                        .dd2    gun_img_08
46e7: dd 48                        .dd2    gun_img_09
46e9: 0d 49                        .dd2    gun_img_10
46eb: 3d 49                        .dd2    gun_img_11
46ed: 6d 49                        .dd2    gun_img_12
46ef: 9d 49                        .dd2    gun_img_13
46f1: cd 49                        .dd2    gun_img_14
46f3: fd 49                        .dd2    gun_img_15
46f5: 2d 4a                        .dd2    gun_img_16
46f7: 5d 4a                        .dd2    gun_img_17
46f9: 8d 4a                        .dd2    gun_img_18
46fb: fd 46                        .dd2    gun_erase

                   vis
46fd: 00 00 00 00+ gun_erase       .fill   48,$00

                   vis vis
472d: 80 80 80 80+ gun_img_00      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$a0,$85,$80
                                    +      $80,$a8,$f5,$bf,$80,$a8,$d5,$ff,$80,$aa,$d5,$ff,$80,$aa,$d5,$bf

                   vis
475d: 80 80 80 80+ gun_img_01      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$be,$80,$a0,$f5,$ff
                                    +      $80,$a8,$f5,$ff,$80,$a8,$d5,$bf,$80,$aa,$d5,$83,$80,$aa,$d5,$80

                   vis
478d: 80 80 80 80+ gun_img_02      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$80,$a0,$80,$80,$80,$fc,$80,$80,$c0,$ff,$80,$a0,$f5,$ff
                                    +      $80,$a8,$f5,$9f,$80,$a8,$d5,$83,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
47bd: 80 80 80 80+ gun_img_03      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$98
                                    +      $80,$80,$80,$be,$80,$80,$c0,$bf,$80,$80,$f0,$9f,$80,$a0,$f5,$87
                                    +      $80,$a8,$f5,$81,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
47ed: 80 80 80 80+ gun_img_04      .bulk   $80,$80,$80,$80,$80,$80,$80,$8c,$80,$80,$80,$9e,$80,$80,$80,$9f
                                    +      $80,$80,$e0,$9f,$80,$80,$f0,$8f,$80,$80,$f8,$83,$80,$a0,$f5,$81
                                    +      $80,$a8,$b5,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
481d: 80 80 80 80+ gun_img_05      .bulk   $80,$80,$80,$80,$80,$80,$80,$87,$80,$80,$c0,$8f,$80,$80,$e0,$87
                                    +      $80,$80,$f0,$83,$80,$80,$f8,$83,$80,$80,$fc,$81,$80,$a0,$f5,$80
                                    +      $80,$a8,$b5,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
484d: 80 80 80 80+ gun_img_06      .bulk   $80,$80,$80,$80,$80,$80,$f0,$81,$80,$80,$f0,$83,$80,$80,$f8,$81
                                    +      $80,$80,$f8,$81,$80,$80,$fc,$80,$80,$80,$fe,$80,$80,$a0,$b5,$80
                                    +      $80,$a8,$b5,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
487d: 80 80 b8 80+ gun_img_07      .bulk   $80,$80,$b8,$80,$80,$80,$fc,$80,$80,$80,$fc,$80,$80,$80,$be,$80
                                    +      $80,$80,$be,$80,$80,$80,$be,$80,$80,$80,$9f,$80,$80,$a0,$95,$80
                                    +      $80,$a8,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
48ad: 80 80 8e 80+ gun_img_08      .bulk   $80,$80,$8e,$80,$80,$80,$9f,$80,$80,$80,$9f,$80,$80,$80,$9f,$80
                                    +      $80,$c0,$8f,$80,$80,$c0,$8f,$80,$80,$c0,$8f,$80,$80,$a0,$85,$80
                                    +      $80,$a8,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
48dd: 80 c0 83 80+ gun_img_09      .bulk   $80,$c0,$83,$80,$80,$e0,$87,$80,$80,$e0,$87,$80,$80,$e0,$87,$80
                                    +      $80,$e0,$87,$80,$80,$e0,$87,$80,$80,$e0,$87,$80,$80,$a0,$85,$80
                                    +      $80,$a8,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
490d: 80 f0 80 80+ gun_img_10      .bulk   $80,$f0,$80,$80,$80,$f8,$81,$80,$80,$f8,$81,$80,$80,$f8,$81,$80
                                    +      $80,$f0,$83,$80,$80,$f0,$83,$80,$80,$f0,$83,$80,$80,$a0,$85,$80
                                    +      $80,$a8,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
493d: 80 9c 80 80+ gun_img_11      .bulk   $80,$9c,$80,$80,$80,$be,$80,$80,$80,$be,$80,$80,$80,$fc,$80,$80
                                    +      $80,$fc,$80,$80,$80,$fc,$80,$80,$80,$f8,$81,$80,$80,$a8,$85,$80
                                    +      $80,$a8,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
496d: 80 80 80 80+ gun_img_12      .bulk   $80,$80,$80,$80,$80,$8f,$80,$80,$c0,$8f,$80,$80,$80,$9f,$80,$80
                                    +      $80,$9f,$80,$80,$80,$be,$80,$80,$80,$fe,$80,$80,$80,$ac,$85,$80
                                    +      $80,$ac,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
499d: 80 80 80 80+ gun_img_13      .bulk   $80,$80,$80,$80,$e0,$81,$80,$80,$f0,$83,$80,$80,$e0,$87,$80,$80
                                    +      $c0,$8f,$80,$80,$c0,$9f,$80,$80,$80,$bf,$80,$80,$80,$ae,$85,$80
                                    +      $80,$ac,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
49cd: 80 80 80 80+ gun_img_14      .bulk   $80,$80,$80,$80,$b0,$80,$80,$80,$f8,$80,$80,$80,$f8,$81,$80,$80
                                    +      $f8,$87,$80,$80,$f0,$8f,$80,$80,$c0,$9f,$80,$80,$80,$af,$85,$80
                                    +      $80,$ac,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
49fd: 80 80 80 80+ gun_img_15      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$98,$80,$80,$80
                                    +      $fc,$80,$80,$80,$fc,$83,$80,$80,$f8,$8f,$80,$80,$e0,$af,$85,$80
                                    +      $80,$af,$95,$80,$80,$a8,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
4a2d: 80 80 80 80+ gun_img_16      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $84,$80,$80,$80,$be,$80,$80,$80,$fe,$83,$80,$80,$fe,$af,$85,$80
                                    +      $f8,$af,$95,$80,$c0,$ab,$95,$80,$80,$aa,$d5,$80,$80,$aa,$d5,$80

                   vis
4a5d: 80 80 80 80+ gun_img_17      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$80,$fc,$80,$80,$80,$fe,$af,$85,$80
                                    +      $fe,$af,$95,$80,$fc,$ab,$95,$80,$c0,$ab,$d5,$80,$80,$aa,$d5,$80

                   vis
4a8d: 80 80 80 80+ gun_img_18      .bulk   $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$a0,$85,$80
                                    +      $fc,$af,$95,$80,$fe,$ab,$95,$80,$fe,$ab,$d5,$80,$fc,$ab,$d5,$80
4abd: 00                           .junk   1

                   ; 
                   ; Updates falling paratroopers.
                   ; 
                   • Clear variables
                   ]array_index    .var    $78    {addr/1}
                   ]status         .var    $79    {addr/1}
                   ]old_status     .var    $7a    {addr/1}
                   ]x_hi           .var    $7b    {addr/1}
                   ]old_ypos       .var    $7c    {addr/1}
                   ]ypos           .var    $7d    {addr/1}
                   ]coll_check_flag .var   $7e    {addr/1}

4abe: a2 00        UpdateParas     ldx     #$00
4ac0: 86 78                        stx     ]array_index      ;init index into para array
4ac2: a6 78        @ParaLoop       ldx     ]array_index
4ac4: bd 6e 43                     lda     para_old_status,x ;copy position values to ZP
4ac7: 85 7a                        sta     ]old_status
4ac9: bd 6f 43                     lda     para_xpos,x
4acc: 85 7b                        sta     ]x_hi
4ace: bd 70 43                     lda     para_ypos,x
4ad1: 85 7d                        sta     ]ypos
4ad3: bd 71 43                     lda     para_old_ypos,x
4ad6: 85 7c                        sta     ]old_ypos
4ad8: bd 70 43                     lda     para_ypos,x
4adb: 9d 71 43                     sta     para_old_ypos,x   ;copy current Y posn to old
4ade: bd 6d 43                     lda     para_status,x
4ae1: 85 79                        sta     ]status
4ae3: 9d 6e 43                     sta     para_old_status,x ;copy current status to old
4ae6: d0 03                        bne     @DoUpdate         ;if status was nonzero, update position
4ae8: 4c 7a 4b                     jmp     @EraseOld

4aeb: 30 08        @DoUpdate       bmi     @FreeFall         ;(flags set by para status) if in free fall, branch
4aed: 18                           clc
4aee: a5 7d                        lda     ]ypos             ;chute is open, move down one row
4af0: 69 01                        adc     #1
4af2: 4c 01 4b                     jmp     @Comm

4af5: c9 ff        @FreeFall       cmp     #$ff              ;was chute removed?
4af7: f0 03                        beq     @NoDec            ;yes, branch
4af9: de 6d 43                     dec     para_status,x     ;decrement negative status, counting to chute open
4afc: 18           @NoDec          clc
4afd: a5 7d                        lda     ]ypos
4aff: 69 03                        adc     #3                ;in free fall, move down three rows
4b01: 9d 70 43     @Comm           sta     para_ypos,x       ;save updated value
                   ; 
4b04: a4 7b                        ldy     ]x_hi             ;get the X position (hi-res column, 0-39)
4b06: b9 e5 43                     lda     ground_height_0,y ;get height of ground, incl landed paras
4b09: a8                           tay
4b0a: b9 4a 4d                     lda     para_stop_row,y   ;get row at which para stops falling
4b0d: dd 70 43                     cmp     para_ypos,x       ;have we reached it?
4b10: b0 68                        bcs     @EraseOld         ;not yet, branch
                   ; The para has hit bottom.
4b12: 85 7d                        sta     ]ypos             ;save row
4b14: a9 00                        lda     #$00
4b16: 9d 6d 43                     sta     para_status,x     ;mark para as inactive
4b19: 9d 6e 43                     sta     para_old_status,x
4b1c: a6 7b                        ldx     ]x_hi             ;get column
4b1e: a5 79                        lda     ]status           ;check status: were we in free fall?
4b20: 10 49                        bpl     @SafeLanding      ;yes, touch down
                   ; Hard landing, not survivable.  See if we landed on someone.
4b22: bd e5 43                     lda     ground_height_0,x ;get current ground height
4b25: f0 27                        beq     @NoCollateral     ;if no landed paras here, branch
4b27: e0 10                        cpx     #16               ;did we land on left edge of gun platform?
4b29: f0 23                        beq     @NoCollateral     ;if yes, collateral damage not possible, because the
4b2b: e0 15                        cpx     #21               ; first trooper who lands there destroys the gun
4b2d: f0 1f                        beq     @NoCollateral     ;if landed on right edge, branch
                   ; Kill the topmost landed para.
4b2f: de e5 43                     dec     ground_height_0,x ;decrement ground height
4b32: a8                           tay                       ;put height in Y-reg
4b33: 88                           dey                       ;decrement it there, too
4b34: a9 01                        lda     #$01              ;do additional collision test vs. other falling
4b36: 85 7e                        sta     ]coll_check_flag  ; paras (should not be needed?)
4b38: be 4a 4d                     ldx     para_stop_row,y   ;get row
4b3b: a4 7b                        ldy     ]x_hi             ;get column
4b3d: 20 cb 4b                     jsr     DrawParaNoChute   ;erase para
4b40: a2 00                        ldx     #$00              ;add 2 points
4b42: a9 02                        lda     #$02
4b44: 20 b8 5b                     jsr     IncreaseScore
4b47: a6 78                        ldx     ]array_index      ;get index of para
4b49: a9 00                        lda     #$00
4b4b: 20 36 4d                     jsr     ExplodePara       ;create explosion for landed para
                   ; 
4b4e: a9 07        @NoCollateral   lda     #$07              ;make some noise
4b50: 8d d5 6c                     sta     sound_event_num
4b53: a6 78                        ldx     ]array_index      ;get index of para
4b55: a9 00                        lda     #$00
4b57: 8d d6 6c                     sta     sound_event_ctr
4b5a: 20 36 4d                     jsr     ExplodePara       ;create explosion for falling para
4b5d: a2 00                        ldx     #$00              ;add 2 points
4b5f: a9 02                        lda     #$02
4b61: 20 b8 5b                     jsr     IncreaseScore
4b64: a9 00                        lda     #$00
4b66: 85 79                        sta     ]status           ;set new status to zero
4b68: 4c 7a 4b                     jmp     @EraseOld

4b6b: a9 00        @SafeLanding    lda     #$00              ;safe landing sound
4b6d: 8d d5 6c                     sta     sound_event_num
4b70: 8d d6 6c                     sta     sound_event_ctr
4b73: fe e5 43                     inc     ground_height_0,x ;increase "ground" height one level
4b76: a9 ff                        lda     #$ff
4b78: 85 79                        sta     ]status           ;set status to free-fall so we draw w/o chute
                   ; 
4b7a: a9 01        @EraseOld       lda     #$01              ;check for mid-air collisions
4b7c: 85 7e                        sta     ]coll_check_flag
4b7e: a4 7b                        ldy     ]x_hi
4b80: a6 7c                        ldx     ]old_ypos
4b82: a5 7a                        lda     ]old_status       ;check previous state
4b84: f0 0e                        beq     @NoErase          ;if inactive previously, skip erase
4b86: 10 06                        bpl     @HasChute         ;had chute, branch
4b88: 20 cb 4b                     jsr     DrawParaNoChute   ;erase, without chute
4b8b: 4c 97 4b                     jmp     @Cont

4b8e: 20 e0 4b     @HasChute       jsr     DrawParaWithChute ;erase, with chute
4b91: 4c 97 4b                     jmp     @Cont

4b94: 20 c3 4b     @NoErase        jsr     Delay168          ;delay briefly to keep frame rate consistent
4b97: a9 00        @Cont           lda     #$00              ;skip collision detection
4b99: 85 7e                        sta     ]coll_check_flag
4b9b: a4 7b                        ldy     ]x_hi             ;get position
4b9d: a6 7d                        ldx     ]ypos
4b9f: a5 79                        lda     ]status           ;check status
4ba1: f0 0e                        beq     @Inactive         ;inactive, delay briefly and continue without drawing
4ba3: 10 06                        bpl     @HasChute         ;has chute, branch
4ba5: 20 cb 4b                     jsr     DrawParaNoChute   ;draw, no chute
4ba8: 4c b4 4b                     jmp     @Next

4bab: 20 e0 4b     @HasChute       jsr     DrawParaWithChute ;draw, with chute
4bae: 4c b4 4b                     jmp     @Next

4bb1: 20 c3 4b     @Inactive       jsr     Delay168          ;delay briefly to keep frame rate consistent
                   ; 
4bb4: a5 78        @Next           lda     ]array_index      ;advance to next paratrooper entry
4bb6: 18                           clc
4bb7: 69 05                        adc     #5                ;5 bytes per struct
4bb9: 85 78                        sta     ]array_index
4bbb: c9 28                        cmp     #40               ;have we processed all 8?
4bbd: b0 03                        bcs     @Return           ;yes, bail
4bbf: 4c c2 4a                     jmp     @ParaLoop         ;no, loop

4bc2: 60           @Return         rts

                   ; 
                   ; Delays for 2+9*18-1+5 = 168 cycles.
                   ; 
4bc3: a2 12        Delay168        ldx     #18
4bc5: ca           @Loop           dex
4bc6: e8                           inx
4bc7: ca                           dex
4bc8: d0 fb                        bne     @Loop
4bca: 60                           rts

                   ; 
                   ; Draws or erases a paratrooper without a chute.
                   ; 
                   ; On entry:
                   ;   X-reg: top row
                   ;   Y-reg: hi-res column (0-39)
                   ; 
                   ]hires_ptr      .var    $76    {addr/2}

4bcb: e8           DrawParaNoChute inx                       ;add 4 to row number for missing chute
4bcc: e8                           inx
4bcd: e8                           inx
4bce: e8                           inx
4bcf: bd 5e 4d                     lda     hires_addr_hi,x   ;configure first hi-res row
4bd2: 85 77                        sta     ]hires_ptr+1
4bd4: bd 1e 4e                     lda     hires_addr_lo,x
4bd7: 85 76                        sta     ]hires_ptr
4bd9: b1 76                        lda     (]hires_ptr),y    ;draw the top row (head, as a single pixel)
4bdb: 49 08                        eor     #%00001000
4bdd: 4c 32 4c                     jmp     DrawParaBody

                   ; 
                   ; Draws or erases a falling paratrooper with a parachute, using hard-coded
                   ; bitmap data.  The image is 10 rows high, byte-aligned.  Optionally tests for
                   ; collisions with other falling paratroopers.
                   ; 
                   ;  ..###..
                   ;  .#####.
                   ;  #######
                   ;  #.....#
                   ;  .#.@.#.
                   ;  .@@@@@.
                   ;  @.@@@.@
                   ;  ..@@@..
                   ;  ..@.@..
                   ;  .@...@.
                   ; 
                   ; On entry:
                   ;   X-reg: top row
                   ;   Y-reg: hi-res column (0-39)
                   ;   $7e: bool (0/1): if true, test for collisions with other paras
                   ; 
                   DrawParaWithChute
4be0: bd 5e 4d                     lda     hires_addr_hi,x   ;draw parachute (4 rows)
4be3: 85 77                        sta     ]hires_ptr+1
4be5: bd 1e 4e                     lda     hires_addr_lo,x
4be8: 85 76                        sta     ]hires_ptr
4bea: b1 76                        lda     (]hires_ptr),y
4bec: 49 1c                        eor     #%00011100
4bee: 91 76                        sta     (]hires_ptr),y
4bf0: e8                           inx
4bf1: bd 5e 4d                     lda     hires_addr_hi,x
4bf4: 85 77                        sta     ]hires_ptr+1
4bf6: bd 1e 4e                     lda     hires_addr_lo,x
4bf9: 85 76                        sta     ]hires_ptr
4bfb: b1 76                        lda     (]hires_ptr),y
4bfd: 49 3e                        eor     #%00111110
4bff: 91 76                        sta     (]hires_ptr),y
4c01: e8                           inx
4c02: bd 5e 4d                     lda     hires_addr_hi,x
4c05: 85 77                        sta     ]hires_ptr+1
4c07: bd 1e 4e                     lda     hires_addr_lo,x
4c0a: 85 76                        sta     ]hires_ptr
4c0c: b1 76                        lda     (]hires_ptr),y
4c0e: 49 7f                        eor     #%01111111
4c10: 91 76                        sta     (]hires_ptr),y
4c12: e8                           inx
4c13: bd 5e 4d                     lda     hires_addr_hi,x
4c16: 85 77                        sta     ]hires_ptr+1
4c18: bd 1e 4e                     lda     hires_addr_lo,x
4c1b: 85 76                        sta     ]hires_ptr
4c1d: b1 76                        lda     (]hires_ptr),y
4c1f: 49 41                        eor     #%01000001
4c21: 91 76                        sta     (]hires_ptr),y
                   ; 
4c23: e8                           inx                       ;draw head, with parachute cords
4c24: bd 5e 4d                     lda     hires_addr_hi,x
4c27: 85 77                        sta     ]hires_ptr+1
4c29: bd 1e 4e                     lda     hires_addr_lo,x
4c2c: 85 76                        sta     ]hires_ptr
4c2e: b1 76                        lda     (]hires_ptr),y
4c30: 49 2a                        eor     #%00101010
                   ; Draw body.
4c32: 91 76        DrawParaBody    sta     (]hires_ptr),y    ;write head row to screen
4c34: e8                           inx
4c35: bd 5e 4d                     lda     hires_addr_hi,x   ;draw body (5 rows)
4c38: 85 77                        sta     ]hires_ptr+1
4c3a: bd 1e 4e                     lda     hires_addr_lo,x
4c3d: 85 76                        sta     ]hires_ptr
4c3f: b1 76                        lda     (]hires_ptr),y
4c41: 49 3e                        eor     #%00111110
4c43: 91 76                        sta     (]hires_ptr),y
4c45: e8                           inx
4c46: a5 7e                        lda     ]coll_check_flag
4c48: f0 44                        beq     @DrawAndCheck
4c4a: bd 5e 4d                     lda     hires_addr_hi,x
4c4d: 85 77                        sta     ]hires_ptr+1
4c4f: bd 1e 4e                     lda     hires_addr_lo,x
4c52: 85 76                        sta     ]hires_ptr
4c54: b1 76                        lda     (]hires_ptr),y
4c56: 49 5d                        eor     #%01011101
4c58: 91 76                        sta     (]hires_ptr),y
4c5a: e8                           inx
4c5b: bd 5e 4d                     lda     hires_addr_hi,x
4c5e: 85 77                        sta     ]hires_ptr+1
4c60: bd 1e 4e                     lda     hires_addr_lo,x
4c63: 85 76                        sta     ]hires_ptr
4c65: b1 76                        lda     (]hires_ptr),y
4c67: 49 1c                        eor     #%00011100
4c69: 91 76                        sta     (]hires_ptr),y
4c6b: e8                           inx
4c6c: bd 5e 4d                     lda     hires_addr_hi,x
4c6f: 85 77                        sta     ]hires_ptr+1
4c71: bd 1e 4e                     lda     hires_addr_lo,x
4c74: 85 76                        sta     ]hires_ptr
4c76: b1 76                        lda     (]hires_ptr),y
4c78: 49 14                        eor     #%00010100
4c7a: 91 76                        sta     (]hires_ptr),y
4c7c: e8                           inx
4c7d: bd 5e 4d                     lda     hires_addr_hi,x
4c80: 85 77                        sta     ]hires_ptr+1
4c82: bd 1e 4e                     lda     hires_addr_lo,x
4c85: 85 76                        sta     ]hires_ptr
4c87: b1 76                        lda     (]hires_ptr),y
4c89: 49 22                        eor     #%00100010
4c8b: 91 76                        sta     (]hires_ptr),y
4c8d: 60                           rts

                   ; Draw the last four rows of the paratrooper, checking for collisions.  A para
                   ; in freefall moves 3 rows per frame, so we don't need to check more.
4c8e: bd 5e 4d     @DrawAndCheck   lda     hires_addr_hi,x
4c91: 85 77                        sta     ]hires_ptr+1
4c93: bd 1e 4e                     lda     hires_addr_lo,x
4c96: 85 76                        sta     ]hires_ptr
4c98: b1 76                        lda     (]hires_ptr),y    ;see if there's something drawn here
4c9a: f0 03                        beq     @NoColl1          ;nope, keep going
4c9c: 20 e6 4c                     jsr     CheckFallHit      ;yes, see if we hit something
4c9f: 49 5d        @NoColl1        eor     #%01011101        ;draw this part
4ca1: 91 76                        sta     (]hires_ptr),y    ;save it to the screen
4ca3: e8                           inx                       ;repeat for remaining rows
4ca4: bd 5e 4d                     lda     hires_addr_hi,x
4ca7: 85 77                        sta     ]hires_ptr+1
4ca9: bd 1e 4e                     lda     hires_addr_lo,x
4cac: 85 76                        sta     ]hires_ptr
4cae: b1 76                        lda     (]hires_ptr),y
4cb0: f0 03                        beq     @NoColl2
4cb2: 20 e6 4c                     jsr     CheckFallHit
4cb5: 49 1c        @NoColl2        eor     #%00011100
4cb7: 91 76                        sta     (]hires_ptr),y
4cb9: e8                           inx
4cba: bd 5e 4d                     lda     hires_addr_hi,x
4cbd: 85 77                        sta     ]hires_ptr+1
4cbf: bd 1e 4e                     lda     hires_addr_lo,x
4cc2: 85 76                        sta     ]hires_ptr
4cc4: b1 76                        lda     (]hires_ptr),y
4cc6: f0 03                        beq     @NoColl3
4cc8: 20 e6 4c                     jsr     CheckFallHit
4ccb: 49 14        @NoColl3        eor     #%00010100
4ccd: 91 76                        sta     (]hires_ptr),y
4ccf: e8                           inx
4cd0: bd 5e 4d                     lda     hires_addr_hi,x
4cd3: 85 77                        sta     ]hires_ptr+1
4cd5: bd 1e 4e                     lda     hires_addr_lo,x
4cd8: 85 76                        sta     ]hires_ptr
4cda: b1 76                        lda     (]hires_ptr),y
4cdc: f0 03                        beq     @NoColl4
4cde: 20 e6 4c                     jsr     CheckFallHit
4ce1: 49 22        @NoColl4        eor     #%00100010
4ce3: 91 76                        sta     (]hires_ptr),y
4ce5: 60                           rts

                   ; 
                   ; Checks to see if a falling paratrooper has hit another falling paratrooper.
                   ; This is only possible if a para in free-fall lands on one with a chute, since
                   ; they all fall at the same rates.
                   ; 
                   ; In the event of a collision, remove the chute from the para that got hit.
                   ; 
                   ; On entry:
                   ;   $78: array index of para we're processing
                   ;   $7b: hi-res column of para (0-39)
                   ;   $7d: row of para
                   ; 
                   ; On exit:
                   ;   A-reg: entry value preserved
                   ;   X-reg: entry value preserved
                   ; 
                   ]this_para_index .var   $78    {addr/1}
                   ]this_para_col  .var    $7b    {addr/1}
                   ]this_para_row  .var    $7d    {addr/1}
                   ]arr_index      .var    $7f    {addr/1}

4ce6: 48           CheckFallHit    pha                       ;preserve A-reg
4ce7: 8a                           txa                       ; and X-reg
4ce8: 48                           pha
4ce9: a2 00                        ldx     #$00
4ceb: 86 7f                        stx     ]arr_index        ;init index into para array
4ced: a6 7f        @Loop           ldx     ]arr_index
4cef: bd 6d 43                     lda     para_status,x     ;see if it is or was alive
4cf2: 1d 6e 43                     ora     para_old_status,x
4cf5: f0 30                        beq     @Next             ;no, move on
4cf7: bd 6f 43                     lda     para_xpos,x       ;is it in the same column?
4cfa: c5 7b                        cmp     ]this_para_col
4cfc: d0 29                        bne     @Next             ;no, move on
4cfe: e4 78                        cpx     ]this_para_index  ;is this the para we're processing?
4d00: f0 25                        beq     @Next             ;yes, move on
                   ; 
4d02: a5 7d                        lda     ]this_para_row    ;get our row
4d04: 18                           clc
4d05: 69 09                        adc     #9                ;add 9 (paratrooper images are 10 rows high)
4d07: dd 70 43                     cmp     para_ypos,x       ;are we above the top of the other para?
4d0a: 90 1b                        bcc     @Next             ;yes, move on
4d0c: bd 70 43                     lda     para_ypos,x       ;get row of other para
4d0f: 18                           clc
4d10: 69 04                        adc     #4                ;add 4 (plummeting para has no chute)
4d12: c5 7d                        cmp     ]this_para_row    ;is other para above us?
4d14: 90 11                        bcc     @Next             ;yes, move on
                   ; Process collision.  The para we landed on loses its chute and goes into free-
                   ; fall.
4d16: bd 6d 43                     lda     para_status,x
4d19: c9 ff                        cmp     #$ff              ;has it already lost its chute?
4d1b: f0 0a                        beq     @Next             ;yes, move on
4d1d: a9 ff                        lda     #$ff
4d1f: 9d 6d 43                     sta     para_status,x     ;remove chute
4d22: a9 01                        lda     #$01              ;small shrapnel
4d24: 20 36 4d                     jsr     ExplodePara
4d27: a5 7f        @Next           lda     ]arr_index
4d29: 18                           clc
4d2a: 69 05                        adc     #5                ;para structs are 5 bytes
4d2c: 85 7f                        sta     ]arr_index
4d2e: c9 28                        cmp     #40               ;have we checked all 8?
4d30: 90 bb                        bcc     @Loop             ;not yet, loop
4d32: 68                           pla                       ;restore A-reg and X-reg
4d33: aa                           tax
4d34: 68                           pla
4d35: 60                           rts

                   ; 
                   ; Add explosion effect when paratrooper is shot or collides with another.
                   ; 
                   ; On entry:
                   ;   A-reg: shrapnel type (0/1)
                   ;   X-reg: para struct index
                   ; 
4d36: 48           ExplodePara     pha
4d37: bd 6f 43                     lda     para_xpos,x
4d3a: 48                           pha
4d3b: a9 00                        lda     #$00
4d3d: 48                           pha
4d3e: bd 70 43                     lda     para_ypos,x
4d41: 48                           pha
4d42: a9 00                        lda     #$00
4d44: 48                           pha
4d45: 48                           pha
4d46: 20 0e 51                     jsr     AddShrapnel
4d49: 60                           rts

                   ; 
                   ; Values that determine the row at which a falling paratrooper becomes a landed
                   ; paratrooper.  The values are (180 - ground_height * 5).
                   ; 
                   ; For example, if there is a landed para underneath the descending para, the
                   ; "ground" will have a height of 1.  The green ground line is two pixels high,
                   ; and each chute-less paratrooper is 6 pixels high, but for stacking purposes we
                   ; ignore the top pixel (their head and the next paratrooper's legs are on the
                   ; same line), so they're actually five high.  We want to stop when the falling
                   ; paratrooper, which is 10 rows high with the chute, is being drawn on line
                   ; (192-2-5-10=) 175.
                   ; 
                   ; We don't drop paratroopers when the ground height is 4 or more, so most of
                   ; this table is unused.
                   ; 
4d4a: b4           para_stop_row   .dd1    180
4d4b: af                           .dd1    175
4d4c: aa                           .dd1    170
4d4d: a5                           .dd1    165
4d4e: a0                           .dd1    160
4d4f: 9b                           .dd1    155
4d50: 96                           .dd1    150
4d51: 91                           .dd1    145
4d52: 8c                           .dd1    140
4d53: 87                           .dd1    135
4d54: 82                           .dd1    130
4d55: 7d                           .dd1    125
                   ; 
4d56: 01 02 04 08+                 .bulk   $01,$02,$04,$08,$10,$20,$40,$80 ;unused?

                   ; Hi-res page 1 screen memory addresses, lines 0-191, high byte.
4d5e: 20 24 28 2c+ hires_addr_hi   .bulk   $20,$24,$28,$2c,$30,$34,$38,$3c,$20,$24,$28,$2c,$30,$34,$38,$3c
                                    +      $21,$25,$29,$2d,$31,$35,$39,$3d,$21,$25,$29,$2d,$31,$35,$39,$3d
                                    +      $22,$26,$2a,$2e,$32,$36,$3a,$3e,$22,$26,$2a,$2e,$32,$36,$3a,$3e
                                    +      $23,$27,$2b,$2f,$33,$37,$3b,$3f,$23,$27,$2b,$2f,$33,$37,$3b,$3f
                                    +      $20,$24,$28,$2c,$30,$34,$38,$3c,$20,$24,$28,$2c,$30,$34,$38,$3c
                                    +      $21,$25,$29,$2d,$31,$35,$39,$3d,$21,$25,$29,$2d,$31,$35,$39,$3d
                                    +      $22,$26,$2a,$2e,$32,$36,$3a,$3e,$22,$26,$2a,$2e,$32,$36,$3a,$3e
                                    +      $23,$27,$2b,$2f,$33,$37,$3b,$3f,$23,$27,$2b,$2f,$33,$37,$3b,$3f
                                    +      $20,$24,$28,$2c,$30,$34,$38,$3c,$20,$24,$28,$2c,$30,$34,$38,$3c
                                    +      $21,$25,$29,$2d,$31,$35,$39,$3d,$21,$25,$29,$2d,$31,$35,$39,$3d
                                    +      $22,$26,$2a,$2e,$32,$36,$3a,$3e,$22,$26,$2a,$2e,$32,$36,$3a,$3e
                                    +      $23,$27,$2b,$2f,$33,$37,$3b,$3f,$23,$27,$2b,$2f,$33,$37,$3b,$3f
                   ; Hi-res page 1 screen memory addresses, lines 0-191, low byte.
4e1e: 00 00 00 00+ hires_addr_lo   .bulk   $00,$00,$00,$00,$00,$00,$00,$00,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $28,$28,$28,$28,$28,$28,$28,$28,$a8,$a8,$a8,$a8,$a8,$a8,$a8,$a8
                                    +      $28,$28,$28,$28,$28,$28,$28,$28,$a8,$a8,$a8,$a8,$a8,$a8,$a8,$a8
                                    +      $28,$28,$28,$28,$28,$28,$28,$28,$a8,$a8,$a8,$a8,$a8,$a8,$a8,$a8
                                    +      $28,$28,$28,$28,$28,$28,$28,$28,$a8,$a8,$a8,$a8,$a8,$a8,$a8,$a8
                                    +      $50,$50,$50,$50,$50,$50,$50,$50,$d0,$d0,$d0,$d0,$d0,$d0,$d0,$d0
                                    +      $50,$50,$50,$50,$50,$50,$50,$50,$d0,$d0,$d0,$d0,$d0,$d0,$d0,$d0
                                    +      $50,$50,$50,$50,$50,$50,$50,$50,$d0,$d0,$d0,$d0,$d0,$d0,$d0,$d0
                                    +      $50,$50,$50,$50,$50,$50,$50,$50,$d0,$d0,$d0,$d0,$d0,$d0,$d0,$d0
                   ; This is ((index / 7) + 1), used to select a byte on the hi-res screen based on
                   ; the X coordinate.  I suspect the "+ 1" is present to make the display closer
                   ; to centered.
4ede: 01 01 01 01+ xc_to_byte      .bulk   $01,$01,$01,$01,$01,$01,$01,$02,$02,$02,$02,$02,$02,$02,$03,$03
                                    +      $03,$03,$03,$03,$03,$04,$04,$04,$04,$04,$04,$04,$05,$05,$05,$05
                                    +      $05,$05,$05,$06,$06,$06,$06,$06,$06,$06,$07,$07,$07,$07,$07,$07
                                    +      $07,$08,$08,$08,$08,$08,$08,$08,$09,$09,$09,$09,$09,$09,$09,$0a
                                    +      $0a,$0a,$0a,$0a,$0a,$0a,$0b,$0b,$0b,$0b,$0b,$0b,$0b,$0c,$0c,$0c
                                    +      $0c,$0c,$0c,$0c,$0d,$0d,$0d,$0d,$0d,$0d,$0d,$0e,$0e,$0e,$0e,$0e
                                    +      $0e,$0e,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$10,$10,$10,$10,$10,$10,$10
                                    +      $11,$11,$11,$11,$11,$11,$11,$12,$12,$12,$12,$12,$12,$12,$13,$13
                                    +      $13,$13,$13,$13,$13,$14,$14,$14,$14,$14,$14,$14,$15,$15,$15,$15
                                    +      $15,$15,$15,$16,$16,$16,$16,$16,$16,$16,$17,$17,$17,$17,$17,$17
                                    +      $17,$18,$18,$18,$18,$18,$18,$18,$19,$19,$19,$19,$19,$19,$19,$1a
                                    +      $1a,$1a,$1a,$1a,$1a,$1a,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1c,$1c,$1c
                                    +      $1c,$1c,$1c,$1c,$1d,$1d,$1d,$1d,$1d,$1d,$1d,$1e,$1e,$1e,$1e,$1e
                                    +      $1e,$1e,$1f,$1f,$1f,$1f,$1f,$1f,$1f,$20,$20,$20,$20,$20,$20,$20
                                    +      $21,$21,$21,$21,$21,$21,$21,$22,$22,$22,$22,$22,$22,$22,$23,$23
                                    +      $23,$23,$23,$23,$23,$24,$24,$24,$24,$24,$24,$24,$25,$25,$25,$25
                                    +      $25,$25,$25,$26,$26,$26,$26,$26,$26,$26,$27,$27,$27,$27,$27,$27
                                    +      $27,$28,$28,$28,$28,$28,$28,$28
                   ; This is (2^(index % 7)), used to select a pixel on the hi-res screen based on
                   ; the X coordinate.
4ff6: 01 02 04 08+ xc_to_pixel     .bulk   $01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02
                                    +      $04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08
                                    +      $10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20
                                    +      $40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01
                                    +      $02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04
                                    +      $08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10
                                    +      $20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40
                                    +      $01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02
                                    +      $04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08
                                    +      $10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20
                                    +      $40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01
                                    +      $02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04
                                    +      $08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10
                                    +      $20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40
                                    +      $01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02
                                    +      $04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08
                                    +      $10,$20,$40,$01,$02,$04,$08,$10,$20,$40,$01,$02,$04,$08,$10,$20
                                    +      $40,$01,$02,$04,$08,$10,$20,$40

                   ; 
                   ; Adds shrapnel to the set.  Six arguments are pushed onto the stack, in this
                   ; order:
                   ; 
                   ;   shrapnel type (0-4)
                   ;   X position high (div 7, 0-39)
                   ;   X position low (mod 7, 0-6)
                   ;   Y position (0-191)
                   ;   X velocity of exploding thing
                   ;   Y velocity of exploding thing
                   ; 
                   ; The shrapnel type indicates what is exploding:
                   ;   0: paratrooper
                   ;   1: paratrooper that got landed on or shot in chute
                   ;   2: helicopter or bomber
                   ;   3: bomb
                   ;   4: player gun turret
                   ; 
                   ; There can be up to 14 pieces of shrapnel on screen.  Slots are assigned in a
                   ; strict rotation, so if a lot of things are exploding, the earlier explosions
                   ; will start to thin out as new stuff blows up.
                   ; 
                   ; On exit:
                   ;   X-reg: preserved
                   ;   Y-reg: preserved
                   ; 
                   • Clear variables
                   ]tmp_ctr        .var    $74    {addr/1}

510e: 8e e3 52     AddShrapnel     stx     shrp_save_x       ;preserve X-reg/Y-reg
5111: 8c e4 52                     sty     shrp_save_y
5114: 68                           pla                       ;pop return addr
5115: aa                           tax
5116: 68                           pla                       ;pop return addr
5117: a8                           tay
5118: 68                           pla                       ;get vertical velocity
5119: 0a                           asl     A                 ;multiply by 8
511a: 0a                           asl     A
511b: 0a                           asl     A
511c: 8d ec 52                     sta     shrp_obj_yvel_x8
511f: 68                           pla                       ;get horizontal velocity
5120: 0a                           asl     A                 ;multiply by 8
5121: 0a                           asl     A
5122: 0a                           asl     A
5123: 8d eb 52                     sta     shrp_obj_xvel_x8
5126: 68                           pla                       ;get row (0-191)
5127: 8d ea 52                     sta     shrp_row
512a: 68                           pla                       ;get position within byte (0-6)
512b: 8d e8 52                     sta     shrp_col_lo
512e: 68                           pla                       ;get horizontal byte position (0-39)
512f: 8d e9 52                     sta     shrp_col_hi
5132: 68                           pla                       ;shrapnel type (0-4)
5133: 8d e2 52                     sta     shrp_type
5136: 0a                           asl     A                 ;multiply by 16
5137: 0a                           asl     A
5138: 0a                           asl     A
5139: 0a                           asl     A
513a: 8d e7 52                     sta     shrp_type_x16
513d: 98                           tya                       ;restore return address
513e: 48                           pha
513f: 8a                           txa
5140: 48                           pha
                   ; 
5141: a9 01                        lda     #$01              ;default to +1
5143: 8d e0 52                     sta     shrp_pos75
5146: ad e5 42                     lda     rng_state+1       ;get random number
5149: 4a                           lsr     A
514a: 29 03                        and     #$03              ;reduce to 0-3
514c: d0 05                        bne     @Pos              ;branch if nonzero, for 75% chance of +1
514e: a9 ff                        lda     #$ff              ; and 25% chance of -1
5150: 8d e0 52                     sta     shrp_pos75
                   ; 
5153: ac e2 52     @Pos            ldy     shrp_type         ;get shrapnel type (0-4)
5156: ad e4 42                     lda     rng_state         ;get random value
5159: 29 03                        and     #$03              ;reduce value to 0-3
515b: 79 d0 52                     adc     shrp_count_tab,y  ;add shrapnel count based on explosion type
515e: 8d e6 52                     sta     shrp_create_count ;set number of pieces to create
                   ; Loop until we have created all fragments.
5161: ae e5 52                     ldx     shrp_prev_slot    ;get index of last slot we used
5164: ad e4 42     @CreateLoop     lda     rng_state         ;update RNG state
5167: 69 7b                        adc     #$7b
5169: 8d e4 42                     sta     rng_state
516c: ad e5 42                     lda     rng_state+1
516f: 69 cf                        adc     #$cf
5171: 8d e5 42                     sta     rng_state+1
                   ; 
5174: ad e4 42                     lda     rng_state         ;get a random number
5177: 29 0f                        and     #$0f              ;reduce to 0-15
5179: 18                           clc
517a: 6d e7 52                     adc     shrp_type_x16     ;add shrapnel type in high nibble
517d: a8                           tay                       ;value now $00-4f
517e: b9 6c 52                     lda     shrp_frag_type,y  ;get the fragment type (0-6)
5181: a8                           tay
                   ; Find a slot to use, starting with the one after the slot we used most
                   ; recently.  If the slot is inactive (status==0) or is a smaller fragment (1/2),
                   ; use that slot.  Otherwise, walk through the array.  If we can't find a slot,
                   ; we either overwrite the next slot (if this is a big fragment), or just skip
                   ; the fragment (if this is a small one).
5182: a9 0f                        lda     #15               ;iterate 15 times, so we end up past at initial slot
5184: 85 74                        sta     ]tmp_ctr          ; if none are empty
5186: e8           @Loop           inx                       ;increment slot number
5187: e0 0e                        cpx     #14               ;reached end of list?
5189: 90 02                        bcc     @NoWrap           ;no, branch
518b: a2 00                        ldx     #$00              ;yes, wrap around to start
518d: bd 96 58     @NoWrap         lda     shrp_status,x     ;get status value (shrapnel type, 0-6)
5190: c9 03                        cmp     #3                ;is it a big piece?
5192: 90 0b                        bcc     @UseSlot          ;no, use this slot
5194: c6 74                        dec     ]tmp_ctr          ;have we checked all slots?
5196: d0 ee                        bne     @Loop             ;not yet, loop
5198: c0 03                        cpy     #3                ;is this a big piece?
519a: b0 03                        bcs     @UseSlot          ;yes, overwrite this slot
519c: 4c 5a 52                     jmp     @Next             ;no, give up on this fragment

519f: 98           @UseSlot        tya
51a0: 9d 96 58                     sta     shrp_status,x     ;set fragment type
                   ; Compute fragment velocity (relative to source object).
51a3: a8                           tay                       ;(redundant)
51a4: ad e5 42                     lda     rng_state+1       ;get random value
51a7: 39 d5 52                     and     shrp_vel_mask,y   ;apply bit mask
51aa: 9d 6c 58                     sta     shrp_xvel,x       ;set as X velocity component
51ad: ad e5 42                     lda     rng_state+1       ;get random value
51b0: 2a                           rol     A
51b1: ad e4 42                     lda     rng_state
51b4: 6a                           ror     A
51b5: 6a                           ror     A
51b6: 6a                           ror     A
51b7: 6a                           ror     A
51b8: 39 d5 52                     and     shrp_vel_mask,y   ;apply velocity mask
51bb: 9d 88 58                     sta     shrp_yvel,x       ;set Y velocity component
                   ; 
51be: ad e2 52                     lda     shrp_type         ;check explosion type
51c1: c9 03                        cmp     #$03              ;is it a bomb or the gun turret?
51c3: 90 06                        bcc     @NotBombGun       ;no, branch
51c5: 1e 88 58                     asl     shrp_yvel,x       ;yes, double the velocity
51c8: 1e 6c 58                     asl     shrp_xvel,x
                   ; Compute initial position.  We want fragments to start a little bit away from
                   ; the exploding object.  Also, there's a 50% chance of inverting each relative
                   ; velocity component.
51cb: ad e9 52     @NotBombGun     lda     shrp_col_hi       ;copy X position to array
51ce: 9d 5e 58                     sta     shrp_xhi,x
51d1: ad e4 42                     lda     rng_state         ;get random value
51d4: 4d e5 42                     eor     rng_state+1
51d7: 48                           pha                       ;push for later
51d8: ac e2 52                     ldy     shrp_type         ;get shrapnel type index
51db: 39 c6 52                     and     shrp_xpos_mask,y  ;mask off some bits for this type
51de: d9 cb 52                     cmp     shrp_xpos_half,y  ;compare to half value
51e1: b0 0c                        bcs     @DontNeg          ;50% chance of leaving velocity as-is
51e3: 48                           pha                       ;save masked value
51e4: bd 6c 58                     lda     shrp_xvel,x       ;set xvel = -xvel
51e7: 49 ff                        eor     #$ff              ;do 2s complement negation
51e9: 69 01                        adc     #$01
51eb: 9d 6c 58                     sta     shrp_xvel,x
51ee: 68                           pla                       ;restore masked value
51ef: 18           @DontNeg        clc
51f0: 6d e8 52                     adc     shrp_col_lo       ;add to initial X position
51f3: 4c fb 51                     jmp     @Div7             ;divide X coordinate by 7, save both parts

51f6: e9 07        @DoDiv7         sbc     #7                ;division is done with repeated subtraction
51f8: fe 5e 58                     inc     shrp_xhi,x
51fb: c9 07        @Div7           cmp     #7                ;is value >= 7?
51fd: b0 f7                        bcs     @DoDiv7           ;yes, loop
51ff: 0a                           asl     A                 ;multiply by 8
5200: 0a                           asl     A
5201: 0a                           asl     A
5202: 9d 50 58                     sta     shrp_xlo_x8,x     ;store low part of X position, * 8
                   ; 
5205: 68                           pla                       ;pull random value we pushed earlier
5206: 4a                           lsr     A                 ;shift to use the bits in the top half
5207: 4a                           lsr     A
5208: 4a                           lsr     A
5209: 4a                           lsr     A
520a: 39 bc 52                     and     shrp_ypos_mask,y  ;mask off some bits (Y-reg has shrapnel type, 0-4)
520d: c0 04                        cpy     #$04              ;type 4 (gun turret explosion)?
520f: f0 05                        beq     @NegY             ;yes, those always explode upward; branch
5211: d9 c1 52                     cmp     shrp_ypos_half,y  ;is it >= half?
5214: b0 0c                        bcs     @PosY             ;yes, branch
5216: 48           @NegY           pha                       ;stash value
5217: bd 88 58                     lda     shrp_yvel,x       ;set yvel = -yvel
521a: 49 ff                        eor     #$ff              ;do 2s complement negation
521c: 69 01                        adc     #$01
521e: 9d 88 58                     sta     shrp_yvel,x
5221: 68                           pla                       ;restore value
5222: 18           @PosY           clc
5223: 6d ea 52                     adc     shrp_row          ;add random value to starting Y position
5226: 9d 7a 58                     sta     shrp_y,x          ;set as Y position
                   ; 
5229: ad eb 52                     lda     shrp_obj_xvel_x8  ;add relative X velocity to object velocity
522c: 18                           clc
522d: 7d 6c 58                     adc     shrp_xvel,x
5230: 9d 6c 58                     sta     shrp_xvel,x       ;store in shrapnel array
5233: ad ec 52                     lda     shrp_obj_yvel_x8  ;add relative Y velocity to object velocity
5236: 18                           clc
5237: 7d 88 58                     adc     shrp_yvel,x
523a: 9d 88 58                     sta     shrp_yvel,x       ;store in shrapnel array
                   ; Pick a color set.  Tends to favor a single color.
523d: ad e0 52                     lda     shrp_pos75        ;get randomly-chosen value
5240: f0 12                        beq     @ReUseColor       ;if zero, use previous color
5242: ce e0 52                     dec     shrp_pos75        ;decrement to zero, or more negative
5245: ad e4 42                     lda     rng_state         ;get random value
5248: 4d e5 42                     eor     rng_state+1
524b: 29 03                        and     #$03              ;reduce to 0-3
524d: a8                           tay                       ;use as index
524e: b9 dc 52                     lda     shrp_color_sets,y ;pick a color set
5251: 8d e1 52                     sta     shrp_color_tmp    ;save for next time
5254: ad e1 52     @ReUseColor     lda     shrp_color_tmp    ;get previous color set
5257: 9d a4 58                     sta     shrp_color,x      ;set in array
                   ; 
525a: ce e6 52     @Next           dec     shrp_create_count ;have we created everything?
525d: f0 03                        beq     @Done             ;yes, bail
525f: 4c 64 51                     jmp     @CreateLoop

5262: 8e e5 52     @Done           stx     shrp_prev_slot
5265: ae e3 52                     ldx     shrp_save_x
5268: ac e4 52                     ldy     shrp_save_y
526b: 60                           rts

                   ; 
                   ; Shrapnel creation data.
                   ; 

                   ; Fragment types used for a given explosion type.  The index is formed by
                   ; putting the shrapnel type (0-4) in the high nibble, and a random value in the
                   ; low nibble.  The values are indices into the shrapnel image sets (0-6).  The
                   ; purpose is to use small fragments for small explosions (like shooting a
                   ; paratrooper), and a mix of big and small fragments for bigger explosions (like
                   ; the gun).
526c: 01 01 01 01+ shrp_frag_type  .bulk   $01,$01,$01,$01,$02,$02,$02,$02,$01,$01,$01,$01,$02,$02,$02,$02 ;explosion type 0 (para)
527c: 01 01 01 01+                 .bulk   $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 ;explosion type 1 (para squish)
528c: 01 01 01 02+                 .bulk   $01,$01,$01,$02,$02,$02,$03,$03,$03,$04,$04,$04,$05,$05,$06,$06 ;explosion type 2 (flier)
529c: 01 01 01 01+                 .bulk   $01,$01,$01,$01,$02,$02,$02,$02,$01,$01,$01,$01,$02,$02,$02,$02 ;explosion type 3 (bomb)
52ac: 01 02 03 04+                 .bulk   $01,$02,$03,$04,$05,$06,$01,$02,$03,$04,$05,$06,$01,$02,$03,$04 ;explosion type 4 (gun turret)
                   ; Initial position mask, indexed by shrapnel explosion type.  This is applied to
                   ; a random number, which is added to the explosion position to form the initial
                   ; position of the fragment.  If the value is at least half, the fragment's
                   ; velocity (relative to the velocity of the source object) is inverted.  The net
                   ; effect is a cloud of fragments that starts at small distance from the start
                   ; point, and expands outward at different speeds and directions.
52bc: 07 03 07 03+ shrp_ypos_mask  .bulk   $07,$03,$07,$03,$07 ;bit mask, indexed by shrapnel type
52c1: 04 02 04 02+ shrp_ypos_half  .bulk   $04,$02,$04,$02,$04 ;half the mask value, rounded up
52c6: 07 07 0f 03+ shrp_xpos_mask  .bulk   $07,$07,$0f,$03,$07 ;bit mask, indexed by shrapnel type
52cb: 04 04 08 02+ shrp_xpos_half  .bulk   $04,$04,$08,$02,$04 ;half of the mask value, rounded up
                   ; Determines how many pieces of shrapnel a given explosion has.  Indexed by
                   ; explosion type (0-4).
52d0: 06 03 0b 0e+ shrp_count_tab  .bulk   $06,$03,$0b,$0e,$0e
                   ; Shrapnel velocity mask.  This is applied to a random number to get the
                   ; shrapnel initial velocity.  Indexed by shrapnel set number (0-6).
52d5: 00 0f 0f 07+ shrp_vel_mask   .bulk   $00,$0f,$0f,$07,$07,$07,$07
                   ; Shrapnel color set.  This is an index into the table of colors at $5646.  The
                   ; value is picked at random.
52dc: 07 03 08 04  shrp_color_sets .bulk   $07,$03,$08,$04
                   ; 
                   ; Temporary storage, used when creating shrapnel fragments.
                   ; 
52e0: 00           shrp_pos75      .dd1    $00               ;color cycler; initially 1 (75%) or -1 (25%)
52e1: 00           shrp_color_tmp  .dd1    $00
52e2: 00           shrp_type       .dd1    $00
52e3: 00           shrp_save_x     .dd1    $00
52e4: 00           shrp_save_y     .dd1    $00
52e5: 00           shrp_prev_slot  .dd1    $00               ;slot previously used
                   shrp_create_count
52e6: 00                           .dd1    $00
52e7: 00           shrp_type_x16   .dd1    $00
52e8: 00           shrp_col_lo     .dd1    $00
52e9: 00           shrp_col_hi     .dd1    $00
52ea: 00           shrp_row        .dd1    $00
                   shrp_obj_xvel_x8
52eb: 00                           .dd1    $00               ;horizontal velocity of exploding object, x8
                   shrp_obj_yvel_x8
52ec: 00                           .dd1    $00               ;vertical velocity of exploding object, x8
52ed: 00                           .junk   1

                   ; 
                   ; Updates and redraws all shrapnel fragments, testing for collisions.
                   ; 
                   • Clear variables
                   ]xposn_lo       .var    $83    {addr/1}
                   ]shrp_index     .var    $85    {addr/1}
                   ]frag_set       .var    $86    {addr/1}
                   ]color_set      .var    $8f    {addr/1}

52ee: a2 00        UpdateShrapnel  ldx     #$00              ;init index into shrapnel table
52f0: 86 85                        stx     ]shrp_index
                   ; Erase all fragments.  We do this even when the fragment is blank, to maintain
                   ; constant frame rate.
52f2: bd b2 58     @Loop           lda     shrp_old_xlo_x8,x ;get low part of X position (* 8)
52f5: 4a                           lsr     A                 ;divide by 8 to get mod 7 remainder (0-6)
52f6: 4a                           lsr     A
52f7: 4a                           lsr     A
52f8: 85 83                        sta     ]xposn_lo         ;horizontal position
52fa: bd dc 58                     lda     shrp_old_status,x ;get fragment image set
52fd: 85 86                        sta     ]frag_set
52ff: 20 02 54                     jsr     GetShrapnelBitmap ;copy bitmap into $76-7f
5302: a6 85                        ldx     ]shrp_index
5304: bc c0 58                     ldy     shrp_old_xhi,x    ;get high part of horizontal position
5307: bd ea 58                     lda     shrp_old_color,x  ;get color mask
530a: 85 8f                        sta     ]color_set
530c: bd ce 58                     lda     shrp_old_y,x      ;get vertical position
530f: aa                           tax
5310: 20 20 54                     jsr     DrawShrapFrag     ;erase fragment
5313: e6 85                        inc     ]shrp_index       ;move to next fragment
5315: a6 85                        ldx     ]shrp_index
5317: e0 0e                        cpx     #14               ;finished with all entries?
5319: 90 d7                        bcc     @Loop             ;no, loop
                   ; 
531b: a2 00                        ldx     #$00              ;init shrapnel index
531d: 86 85        @Loop           stx     ]shrp_index
531f: ad e4 42                     lda     rng_state         ;(update RNG)
5322: 69 cb                        adc     #$cb
5324: 8d e4 42                     sta     rng_state
5327: ad e5 42                     lda     rng_state+1
532a: 69 39                        adc     #$39
532c: 8d e5 42                     sta     rng_state+1
                   ; 
532f: a6 85                        ldx     ]shrp_index
5331: bd 50 58                     lda     shrp_xlo_x8,x     ;get low part of horizontal position, * 8
5334: 4a                           lsr     A                 ;divide by 8 (now 0-6)
5335: 4a                           lsr     A
5336: 4a                           lsr     A
5337: 85 83                        sta     ]xposn_lo
5339: bd 96 58                     lda     shrp_status,x     ;get shrapnel set
533c: 85 86                        sta     ]frag_set
533e: 20 02 54                     jsr     GetShrapnelBitmap ;load bitmap into $76-7f
5341: a6 85                        ldx     ]shrp_index
5343: bc 5e 58                     ldy     shrp_xhi,x        ;get high part of horizontal position
5346: bd 7a 58                     lda     shrp_y,x          ;get vertical position
5349: aa                           tax
534a: 20 60 54                     jsr     TestShrapnelColl  ;test for collision
534d: b0 13                        bcs     @NoColl           ;no clashes, branch
                   ; 
534f: a6 85                        ldx     ]shrp_index
5351: bd 96 58                     lda     shrp_status,x
5354: c9 03                        cmp     #$03              ;is this shrapnel set 0-2 (blank or small pieces)?
5356: 90 0a                        bcc     @NoColl           ;yes, skip tests
5358: 20 91 54                     jsr     TestShrpHitPara   ;did we hit a paratrooper?
535b: 90 05                        bcc     @NoColl           ;no, branch
535d: 20 52 55                     jsr     TestShrpHitFlier  ;did we hit a flier?
5360: 90 00                        bcc     @NoColl           ;no, branch (for no reason)
                   ; 
5362: a6 85        @NoColl         ldx     ]shrp_index       ;get position and color
5364: bc 5e 58                     ldy     shrp_xhi,x
5367: bd a4 58                     lda     shrp_color,x
536a: 85 8f                        sta     ]color_set
536c: bd 7a 58                     lda     shrp_y,x
536f: aa                           tax
5370: 20 20 54                     jsr     DrawShrapFrag     ;draw fragment
                   ; 
5373: a6 85                        ldx     ]shrp_index       ;copy values to "old" values
5375: bd a4 58                     lda     shrp_color,x
5378: 9d ea 58                     sta     shrp_old_color,x
537b: bd 50 58                     lda     shrp_xlo_x8,x
537e: 9d b2 58                     sta     shrp_old_xlo_x8,x
5381: bd 5e 58                     lda     shrp_xhi,x
5384: 9d c0 58                     sta     shrp_old_xhi,x
5387: bd 7a 58                     lda     shrp_y,x
538a: 9d ce 58                     sta     shrp_old_y,x
538d: bd 96 58                     lda     shrp_status,x
5390: 9d dc 58                     sta     shrp_old_status,x
5393: f0 64                        beq     @Next             ;if this is a blank piece, skip update
                   ; Update shrapnel fragment position.
5395: bd 50 58                     lda     shrp_xlo_x8,x     ;get low part of horizontal position (0-6), * 8
5398: 18                           clc
5399: 7d 6c 58                     adc     shrp_xvel,x       ;add horizontal velocity (signed quantity)
539c: 10 09                        bpl     @NotNeg           ;if it didn't go negative, branch
539e: 18                           clc
539f: 69 38                        adc     #56               ;add 7 * 8 to make it positive
53a1: de 5e 58                     dec     shrp_xhi,x        ;decrement high part
53a4: 4c b0 53                     jmp     @Comm

53a7: c9 38        @NotNeg         cmp     #56               ;did we exceed 7 * 8?
53a9: 90 05                        bcc     @Comm             ;no, keep going
53ab: e9 38                        sbc     #56               ;yes, reduce to (0-6)*8
53ad: fe 5e 58                     inc     shrp_xhi,x        ;increment the high part
53b0: 9d 50 58     @Comm           sta     shrp_xlo_x8,x     ;save updated value
                   ; 
53b3: bd 5e 58                     lda     shrp_xhi,x        ;get the high part of the horizontal position
53b6: 30 3c                        bmi     @Disable          ;if negative, disable
53b8: f0 3a                        beq     @Disable          ;if zero, disable
53ba: c9 26                        cmp     #38               ;if approaching right edge, disable
53bc: b0 36                        bcs     @Disable
53be: ad e4 42                     lda     rng_state         ;get random value
53c1: 29 1f                        and     #$1f              ;reduce to 0-31
53c3: f0 2f                        beq     @Disable          ;if zero, disable (3% chance of spontaneous end)
                   ; 
53c5: 38                           sec                       ;set carry for sign-extension
53c6: fe 88 58                     inc     shrp_yvel,x       ;increase Y velocity, to simulate gravity
53c9: fe 88 58                     inc     shrp_yvel,x
53cc: 30 01                        bmi     @NegY             ;if still moving upward, branch
53ce: 18                           clc                       ;positive value, clear carry for sign-extension
53cf: 08           @NegY           php                       ;stash carry flag
53d0: bd 88 58                     lda     shrp_yvel,x       ;get Y velocity
53d3: 6a                           ror     A                 ;perform a signed division by 8, rolling
53d4: 28                           plp                       ; the carry flag into the high bit
53d5: 08                           php
53d6: 6a                           ror     A
53d7: 28                           plp
53d8: 6a                           ror     A
53d9: 18                           clc
53da: 7d 7a 58                     adc     shrp_y,x          ;add velocity/8 to Y position
53dd: 9d 7a 58                     sta     shrp_y,x          ;save it
                   ; Disable fragments if they're off screen, or if they're large fragments below
                   ; line 150 (just above the level of the gun).  This prevents big chunks from
                   ; striking the gun or landed paratroopers.
53e0: c9 be                        cmp     #190              ;have we reached the bottom (or rolled over the top)?
53e2: b0 10                        bcs     @Disable          ;yes, disable fragment
53e4: ac e8 42                     ldy     exploding_gun_ctr ;is the gun exploding?
53e7: d0 10                        bne     @Next             ;yes, skip next part
53e9: bc 96 58                     ldy     shrp_status,x     ;get shrapnel set
53ec: c0 03                        cpy     #$03              ;is it >= 3 (big chunks)?
53ee: 90 09                        bcc     @Next             ;no, branch
53f0: c9 96                        cmp     #150              ;are we below line 150?
53f2: 90 05                        bcc     @Next             ;no, branch
53f4: a9 00        @Disable        lda     #$00              ;disable the fragment
53f6: 9d 96 58                     sta     shrp_status,x
                   ; 
53f9: e8           @Next           inx
53fa: e0 0e                        cpx     #$0e
53fc: b0 03                        bcs     @Return
53fe: 4c 1d 53                     jmp     @Loop

5401: 60           @Return         rts

                   ; 
                   ; Copies the bitmap for a specific shrapnel image into zero page.  Each image is
                   ; 2 bytes by 5 rows, so we need 10 bytes.
                   ; 
                   ; On entry:
                   ;   $83: horizontal position mod 7 (0-6), determines image within set
                   ;   $86: shrapnel fragment set (0-6)
                   ; 
                   ; On exit:
                   ;   $80: pointer to shrapnel image set
                   ;   $76-7f: 10 bytes of image data
                   ]shrp_bits      .var    $76    {addr/10}
                   ]shrp_set_ptr   .var    $80    {addr/2}
                   ]x_lo           .var    $83    {addr/1}
                   ]frag_set       .var    $86    {addr/1}

                   GetShrapnelBitmap
5402: a5 86                        lda     ]frag_set         ;get size (0-6)
5404: 0a                           asl     A                 ;double it to use as index
5405: aa                           tax
5406: bd 58 56                     lda     shrapnel_addrs,x  ;get base address of set
5409: 85 80                        sta     ]shrp_set_ptr
540b: bd 59 56                     lda     shrapnel_addrs+1,x
540e: 85 81                        sta     ]shrp_set_ptr+1
5410: a6 83                        ldx     ]x_lo             ;get horizontal pixel offset within byte
5412: bc 51 56                     ldy     shrapnel_offsets,x ;get offset to image within set
5415: a2 09                        ldx     #9                ;each image is 2x5, 10 bytes
5417: b1 80        @Loop           lda     (]shrp_set_ptr),y ;copy to ZP
5419: 95 76                        sta     ]shrp_bits,x
541b: 88                           dey
541c: ca                           dex
541d: 10 f8                        bpl     @Loop
541f: 60                           rts

                   ; 
                   ; Draw (or erase) one fragment of shrapnel.
                   ; 
                   ; On entry:
                   ;   X-reg: screen row (0-191)
                   ;   Y-reg: screen column (0-255)
                   ;   $76-7f: 10-byte shrapnel bitmap
                   ;   $8f: color set (3, 4, 7, or 8)
                   ; 
                   • Clear variables
                   ]shrp_bitmap    .var    $76    {addr/10}
                   ]hires_ptr      .var    $80    {addr/2}
                   ]index_tmp      .var    $82    {addr/1}
                   ]screen_row     .var    $84    {addr/1}
                   ]colormask_0    .var    $8d    {addr/1}
                   ]colormask_1    .var    $8e    {addr/1}
                   ]color_set      .var    $8f    {addr/1}

5420: 86 84        DrawShrapFrag   stx     ]screen_row       ;stash top row
5422: 98                           tya                       ;copy column into A-reg
5423: 29 01                        and     #$01              ;mask low bit to check column: 0=even, 1=odd
5425: 18                           clc
5426: 65 8f                        adc     ]color_set        ;add to the color argument, so the colors come out
5428: aa                           tax                       ; right if we're starting in an odd column
5429: bd 46 56                     lda     colors_even,x     ;configure the color mask for the first column
542c: 85 8d                        sta     ]colormask_0
542e: bd 47 56                     lda     colors_odd,x      ;configure the color mask for the second column
5431: 85 8e                        sta     ]colormask_1
                   ; 
5433: a2 00                        ldx     #$00              ;init index into bitmap
5435: 86 82        @Loop           stx     ]index_tmp
5437: a6 84                        ldx     ]screen_row       ;set up the hi-res row pointer
5439: bd 5e 4d                     lda     hires_addr_hi,x
543c: 85 81                        sta     ]hires_ptr+1
543e: bd 1e 4e                     lda     hires_addr_lo,x
5441: 85 80                        sta     ]hires_ptr
5443: e6 84                        inc     ]screen_row       ;advance to next row
                   ; 
5445: a6 82                        ldx     ]index_tmp        ;get index into bitmap
5447: b5 76                        lda     ]shrp_bitmap,x    ;get byte from bitmap
5449: 25 8d                        and     ]colormask_0      ;apply color mask
544b: 51 80                        eor     (]hires_ptr),y    ;merge with screen contents
544d: 91 80                        sta     (]hires_ptr),y    ;write to screen
544f: c8                           iny                       ;advance to next byte on screen
5450: e8                           inx                       ; and in bitmap
5451: b5 76                        lda     ]shrp_bitmap,x    ;repeat for second column
5453: 25 8e                        and     ]colormask_1
5455: 51 80                        eor     (]hires_ptr),y
5457: 91 80                        sta     (]hires_ptr),y
5459: 88                           dey
545a: e8                           inx
545b: e0 0a                        cpx     #10               ;done 2 columns * 5 rows?
545d: 90 d6                        bcc     @Loop             ;not yet, loop
545f: 60                           rts

                   ; 
                   ; Tests to see if a shrapnel bitmap overlaps with something else on screen.
                   ; 
                   ; Shrapnel bitmaps are 2 bytes by 5 rows.
                   ; 
                   ; On entry:
                   ;   $76-7f: shrapnel bitmap data
                   ;   X-reg: top screen row (0-191)
                   ;   Y-reg: screen column (0-39)
                   ; 
                   ; On exit:
                   ;   Carry clear if collision detected
                   ; 
                   TestShrapnelColl
5460: 86 84                        stx     ]screen_row
5462: a2 00                        ldx     #$00
5464: 86 82        @Loop           stx     ]index_tmp        ;index into bitmap
5466: a6 84                        ldx     ]screen_row       ;get hi-res line address
5468: bd 5e 4d                     lda     hires_addr_hi,x
546b: 85 81                        sta     ]hires_ptr+1
546d: bd 1e 4e                     lda     hires_addr_lo,x
5470: 85 80                        sta     ]hires_ptr
5472: e6 84                        inc     ]screen_row       ;advance row number to next line
                   ; 
5474: a6 82                        ldx     ]index_tmp        ;get index into bitmap
5476: b5 76                        lda     ]shrp_bitmap,x    ;get bitmap data
5478: 29 7f                        and     #$7f              ;ignore high bit
547a: 31 80                        and     (]hires_ptr),y    ;mask off contents of hi-res screen
547c: d0 11                        bne     @Collided         ;found a collision, bail
547e: c8                           iny                       ;move to next column
547f: e8                           inx                       ;move to next byte in bitmap
5480: b5 76                        lda     ]shrp_bitmap,x    ;get bitmap data
5482: 29 7f                        and     #$7f              ;ignore high bit
5484: 31 80                        and     (]hires_ptr),y    ;mask off contents of hi-res screen
5486: d0 07                        bne     @Collided         ;found a collision, bail
5488: 88                           dey                       ;back to left column
5489: e8                           inx                       ;advance to next byte in bitmap
548a: e0 0a                        cpx     #10               ;done 2 bytes * 5 rows?
548c: 90 d6                        bcc     @Loop             ;not yet, loop
548e: 60                           rts                       ;done, return with carry set

548f: 18           @Collided       clc
5490: 60                           rts

                   ; 
                   ; Tests to see if shrapnel hit a paratrooper.
                   ; 
                   ; On entry:
                   ;   $76-7f: shrapnel bitmap data
                   ;   $85: index into shrapnel table
                   ; 
                   ; On exit:
                   ;   Carry flag clear if collision detected
                   ; 
                   • Clear variables
                   ]shrp_bitmap    .var    $76    {addr/10}
                   ]shrp_ypos      .var    $84    {addr/1}
                   ]shrp_index     .var    $85    {addr/1}
                   ]shrp_xpos      .var    $87    {addr/1}
                   ]para_index     .var    $88    {addr/1}

5491: a6 85        TestShrpHitPara ldx     ]shrp_index       ;get index into shrapnel table
5493: bd 7a 58                     lda     shrp_y,x          ;copy position to ZP
5496: 85 84                        sta     ]shrp_ypos
5498: bd 5e 58                     lda     shrp_xhi,x
549b: 85 87                        sta     ]shrp_xpos
                   ; Loop over all falling paratroopers.
549d: a2 00                        ldx     #$00
549f: 86 88        @Loop           stx     ]para_index
54a1: bc 6d 43                     ldy     para_status,x     ;check status
54a4: f0 53                        beq     @Next             ;inactive, move on
54a6: bc 6e 43                     ldy     para_old_status,x ;check previous status
54a9: f0 4e                        beq     @Next             ;inactive, move on
54ab: 38                           sec
54ac: bd 6f 43                     lda     para_xpos,x       ;compare X coordinates
54af: e5 87                        sbc     ]shrp_xpos
54b1: c9 02                        cmp     #$02              ;are the values equal, or para is 1 column to right?
54b3: b0 44                        bcs     @Next             ;no, move on
54b5: 85 87                        sta     ]shrp_xpos        ;save diff (0 or 1)
54b7: a5 84                        lda     ]shrp_ypos        ;get shrapnel Y position
54b9: 38                           sec
54ba: fd 71 43                     sbc     para_old_ypos,x   ;subtract para Y coordinate
54bd: c0 00                        cpy     #$00              ;test para status: pos=chute open, neg=freefall
54bf: 10 06                        bpl     @HasChute
54c1: c9 0a                        cmp     #10               ;is para above shrapnel, by < 10 rows?
54c3: 90 0a                        bcc     @Overlap          ;yes, branch
54c5: b0 32                        bcs     @Next             ;no, move on

54c7: c9 fc        @HasChute       cmp     #$fc              ;is para below shrapnel, by < 4 rows?
54c9: b0 04                        bcs     @Overlap          ;yes, branch
54cb: c9 0a                        cmp     #10               ;is para above shrapnel, by < 10 rows?
54cd: b0 2a                        bcs     @Next             ;no, move on
54cf: c9 00        @Overlap        cmp     #$00              ;above or below?
54d1: 30 06                        bmi     @ParaBelow        ;para below, branch
54d3: a8                           tay                       ;put first row of paratrooper in Y-reg
54d4: a2 00                        ldx     #$00              ;start at first row of shrapnel bitmap
54d6: 4c e1 54                     jmp     @Comm

54d9: 49 ff        @ParaBelow      eor     #$ff              ;get absolute value
54db: 18                           clc
54dc: 69 01                        adc     #$01
54de: aa                           tax                       ;start at this row in shrapnel bitmap
54df: a0 00                        ldy     #$00              ;start at top row of paratrooper
54e1: 8a           @Comm           txa
54e2: 0a                           asl     A                 ;double row number (2 bytes/row)
54e3: 18                           clc
54e4: 65 87                        adc     ]shrp_xpos        ;add 0/1 to select shrapnel bitmap column
54e6: aa                           tax
54e7: b5 76        @Loop           lda     ]shrp_bitmap,x    ;get byte from shrapnel bitmap
54e9: 39 ae 60                     and     para_img,y        ;bitwise AND it with paratrooper bitmap
54ec: d0 16                        bne     @Clash            ;if a bit is set, we have a clash
54ee: c8                           iny                       ;advance to next row of paratrooper bitmap
54ef: c0 0a                        cpy     #10               ;reached the end?
54f1: b0 06                        bcs     @Next             ;no, loop
54f3: e8                           inx                       ;move to next row of shrapnel bitmap (2 bytes/row)
54f4: e8                           inx
54f5: e0 0a                        cpx     #10               ;reached end of shrapnel bitmap?
54f7: 90 ee                        bcc     @Loop             ;not yet, loop
                   ; 
54f9: a5 88        @Next           lda     ]para_index       ;advance to next paratrooper entry
54fb: 18                           clc
54fc: 69 05                        adc     #5                ;para struct is 5 bytes
54fe: aa                           tax
54ff: e0 28                        cpx     #40               ;have we processed all 8?
5501: 90 9c                        bcc     @Loop             ;not yet, loop
5503: 60                           rts                       ;return with carry set

                   ; Overlapping pixels detected.  Y-reg holds the paratrooper bitmap row.
5504: a6 88        @Clash          ldx     ]para_index
5506: c0 04                        cpy     #4                ;were we shot in the top 4 rows (chute)?
5508: b0 0a                        bcs     @BodyShot         ;no, branch
550a: a9 ff                        lda     #$ff              ;set status to falling without a chute
550c: 9d 6d 43                     sta     para_status,x
550f: a9 01                        lda     #$01              ;set explosion type to 1
5511: 4c 27 55                     jmp     @Explode

5514: bd 6d 43     @BodyShot       lda     para_status,x     ;get status
5517: f0 05                        beq     @SkipZ            ;if already dead, skip redundant store (why?)
5519: a9 00                        lda     #$00
551b: 9d 6d 43                     sta     para_status,x     ;mark slot as inactive
551e: a9 02        @SkipZ          lda     #$02              ;add 2 points
5520: a2 00                        ldx     #$00
5522: 20 b8 5b                     jsr     IncreaseScore
5525: a9 00                        lda     #$00              ;set explosion type to 0
5527: a6 88        @Explode        ldx     ]para_index       ;create explosion effect
5529: 48                           pha
552a: bd 6f 43                     lda     para_xpos,x
552d: 48                           pha
552e: a9 00                        lda     #$00
5530: 48                           pha
5531: bd 71 43                     lda     para_old_ypos,x
5534: 48                           pha
5535: a9 00                        lda     #$00
5537: 48                           pha
5538: a9 01                        lda     #$01
553a: 48                           pha
553b: 20 0e 51                     jsr     AddShrapnel
                   ; Copy the shrapnel bitmap into ZP.  (Why?)
                   ]x_lo           .var    $83    {addr/1}
                   ]frag_set       .var    $86    {addr/1}

553e: a6 85                        ldx     ]shrp_index       ;get current parameters
5540: bd 50 58                     lda     shrp_xlo_x8,x
5543: 4a                           lsr     A
5544: 4a                           lsr     A
5545: 4a                           lsr     A
5546: 85 83                        sta     ]x_lo
5548: bd 96 58                     lda     shrp_status,x
554b: 85 86                        sta     ]frag_set
554d: 20 02 54                     jsr     GetShrapnelBitmap ;copy bitmap into ZP
5550: 18                           clc                       ;indicate collision detected
5551: 60                           rts

                   ; 
                   ; Tests to see if shrapnel struck a flier.
                   ; 
                   ; On entry:
                   ;   $76-7f: shrapnel bitmap data
                   ;   $85: index into shrapnel table
                   ; 
                   ; On exit:
                   ;   Carry flag clear if collision detected
                   ; 
                   • Clear variables
                   ]shrp_bitmap    .var    $76    {addr/10}
                   ]mul_tmp        .var    $82    {addr/1}
                   ]shrp_ypos      .var    $84    {addr/1}
                   ]shrp_index     .var    $85    {addr/1}
                   ]shrp_xpos      .var    $87    {addr/1}
                   ]flier_index    .var    $88    {addr/1}
                   ]flier_bmp_ptr  .var    $89    {addr/2}
                   ]xdiff          .var    $8b    {addr/1}
                   ]shrp_bit_off   .var    $8c    {addr/1}

                   TestShrpHitFlier
5552: a6 85                        ldx     ]shrp_index
5554: bd 7a 58                     lda     shrp_y,x
5557: 85 84                        sta     ]shrp_ypos
5559: bd 5e 58                     lda     shrp_xhi,x
555c: 85 87                        sta     ]shrp_xpos
                   ; Loop over all fliers.
555e: a2 00                        ldx     #$00
5560: 86 88        @FlierLoop      stx     ]flier_index      ;get index into flier array
5562: bd 45 43                     lda     flier_old_status,x ;get flier old status
5565: f0 05                        beq     @NotActive        ;if inactive, skip
5567: bd 49 43                     lda     flier_status,x    ;get flier current status
556a: d0 03                        bne     @Active           ;if active, do work
556c: 4c fd 55     @NotActive      jmp     @NextFlier        ;inactive, move on

556f: 38           @Active         sec
5570: a5 87                        lda     ]shrp_xpos        ;compute difference in position of left edge
5572: fd 47 43                     sbc     flier_old_x_hi,x
5575: c9 04                        cmp     #$04              ;< 4 (width of flier in bytes)?
5577: 90 07                        bcc     @XOverlap         ;yes, keep testing
5579: c9 ff                        cmp     #$ff              ;== -1 (shrapnel just to left of flier)?
557b: f0 03                        beq     @XOverlap         ;yes, keep testing
557d: 4c fd 55                     jmp     @NextFlier        ;no overlap, move on

5580: 85 8b        @XOverlap       sta     ]xdiff            ;col diff, [-1,3]
5582: a5 84                        lda     ]shrp_ypos        ;compute difference in position of top
5584: 38                           sec
5585: fd 46 43                     sbc     flier_old_y,x
5588: c9 0a                        cmp     #10               ;< 10 (height of flier)?
558a: 90 04                        bcc     @Overlap          ;yes, shrapnel is below but nearby
558c: c9 fc                        cmp     #$fc              ;>= -4 (shrapnel above)?
558e: 90 6d                        bcc     @NextFlier        ;no overlap, move on
                   ; 
5590: c9 00        @Overlap        cmp     #$00              ;test sign
5592: 30 06                        bmi     @FlierBelow
5594: a8                           tay
5595: a2 00                        ldx     #$00              ;start in top row of shrapnel
5597: 4c a2 55                     jmp     @Comm

559a: 49 ff        @FlierBelow     eor     #$ff              ;get absolute value of height diff
559c: 18                           clc
559d: 69 01                        adc     #$01
559f: aa                           tax
55a0: a0 00                        ldy     #$00              ;start in top row of flier
55a2: 8a           @Comm           txa                       ;get top row of shrapnel
55a3: 0a                           asl     A                 ;double it (2 bytes / row)
55a4: 85 8c                        sta     ]shrp_bit_off     ;set as shrapnel bitmap offset
55a6: 98                           tya                       ;get top row of flier
55a7: 0a                           asl     A                 ;multiply by 10 (bug? should be 4?)
55a8: 85 82                        sta     ]mul_tmp
55aa: 0a                           asl     A
55ab: 0a                           asl     A
55ac: 65 82                        adc     ]mul_tmp
55ae: 65 8b                        adc     ]xdiff            ;add to X difference, which is [-1,3]
55b0: 85 8b                        sta     ]xdiff            ;save flier bitmap offset (may be negative)
                   ; Get pointer to flier bitmap.
55b2: a6 88                        ldx     ]flier_index
55b4: bd 45 43                     lda     flier_old_status,x ;get flier status (2/4/6/8)
55b7: a8                           tay
55b8: b9 1f 64                     lda     flier_addrs,y     ;get address of flier bitmap set
55bb: 85 89                        sta     ]flier_bmp_ptr
55bd: b9 20 64                     lda     flier_addrs+1,y
55c0: 85 8a                        sta     ]flier_bmp_ptr+1
55c2: bc 48 43                     ldy     flier_old_x_lo,x  ;get flier position, low part (0-6)
55c5: b9 18 64                     lda     flier_bmp_off,y   ;get offset into flier bitmap set
55c8: 18                           clc
55c9: 65 89                        adc     ]flier_bmp_ptr    ;add to pointer
55cb: 85 89                        sta     ]flier_bmp_ptr
55cd: 90 02                        bcc     @NoInc
55cf: e6 8a                        inc     ]flier_bmp_ptr+1
                   ; 
55d1: a6 8c        @NoInc          ldx     ]shrp_bit_off     ;get shrapnel bitmap offset
55d3: a4 8b                        ldy     ]xdiff            ;get flier bitmap offset
55d5: a5 8b        @Loop           lda     ]xdiff            ;check bitmap offset for negative value
55d7: 30 08                        bmi     @SkipNeg          ;if neg, skip this one
55d9: b5 76                        lda     ]shrp_bitmap,x    ;get value from shrapnel bitmap
55db: 29 7f                        and     #$7f              ;ignore high bit
55dd: 31 89                        and     (]flier_bmp_ptr),y ;bitwise AND with flier bitmap
55df: d0 2a                        bne     @Clash            ;if bits are nonzero, we collided; branch
55e1: c8           @SkipNeg        iny                       ;advance to next byte in flier
55e2: e8                           inx                       ;advance to next byte in shrapnel bitmap
55e3: a5 8b                        lda     ]xdiff            ;check initial flier offset
55e5: c9 03                        cmp     #3                ;was it at right edge of bitmap?
55e7: f0 08                        beq     @NoChk2           ;yes, don't look at this column
55e9: b5 76                        lda     ]shrp_bitmap,x    ;get value from shrapnel bitmap
55eb: 29 7f                        and     #$7f              ;ignore high bit
55ed: 31 89                        and     (]flier_bmp_ptr),y ;bitwise AND with flier bitmap
55ef: d0 1a                        bne     @Clash            ;if bits are nonzero, we collided; branch
55f1: c8           @NoChk2         iny                       ;move to next row of flier (+3)
55f2: c8                           iny
55f3: c8                           iny
55f4: e8                           inx                       ;move to next row of shrapnel bitmap (+1)
55f5: c0 28                        cpy     #40               ;did we walk past end of the flier bitmap (4*10)?
55f7: b0 04                        bcs     @NextFlier        ;yes, move on
55f9: e0 0a                        cpx     #10               ;did we walk past end of the shrapnel bitmap (2*5)?
55fb: 90 d8                        bcc     @Loop             ;not yet, loop
                   ; 
55fd: a5 88        @NextFlier      lda     ]flier_index      ;get index into flier structs
55ff: 18                           clc
5600: 69 0a                        adc     #10               ;10 bytes per flier
5602: aa                           tax
5603: e0 28                        cpx     #40               ;have we processed all 4?
5605: b0 03                        bcs     @Return           ;yes, bail
5607: 4c 60 55                     jmp     @FlierLoop        ;no, loop

560a: 60           @Return         rts                       ;return with carry set

                   ; Flier hit by shrapnel.
                   ]x_lo           .var    $83    {addr/1}
                   ]shrp_size      .var    $86    {addr/1}

560b: a6 88        @Clash          ldx     ]flier_index
560d: a9 00                        lda     #$00              ;mark flier as inactive
560f: 9d 49 43                     sta     flier_status,x
5612: a9 02                        lda     #$02              ;explosion type 2 (flier)
5614: 48                           pha
5615: bd 47 43                     lda     flier_old_x_hi,x  ;copy position and velocity
5618: 48                           pha
5619: bd 48 43                     lda     flier_old_x_lo,x
561c: 48                           pha
561d: bd 46 43                     lda     flier_old_y,x
5620: 48                           pha
5621: bd 4d 43                     lda     flier_x_vel,x
5624: 48                           pha
5625: a9 00                        lda     #$00              ;vertical velocity is always zero
5627: 48                           pha
5628: 20 0e 51                     jsr     AddShrapnel
                   ; 
562b: a9 05                        lda     #$05              ;add 5 points
562d: a2 00                        ldx     #$00
562f: 20 b8 5b                     jsr     IncreaseScore
                   ; 
5632: a6 85                        ldx     ]shrp_index       ;load shrapnel bitmap (why?)
5634: bd 50 58                     lda     shrp_xlo_x8,x
5637: 4a                           lsr     A
5638: 4a                           lsr     A
5639: 4a                           lsr     A
563a: 85 83                        sta     ]x_lo
563c: bd 96 58                     lda     shrp_status,x
563f: 85 86                        sta     ]shrp_size
5641: 20 02 54                     jsr     GetShrapnelBitmap
5644: 18                           clc                       ;return with carry clear to indicate collision
5645: 60                           rts

                   ; 
                   ; Shrapnel color table.  Indices are generated at random from the set {3,4,7,8}
                   ; (see table at $52dc).  Two adjacent entries are used for odd/even.
                   ; 
5646: ff           colors_even     .dd1    $ff
5647: ff           colors_odd      .dd1    $ff
5648: ff                           .dd1    $ff
5649: 2a           ground_color_e  .dd1    $2a               ;3: green
564a: 55           ground_color_o  .dd1    $55               ;4: purple
564b: 2a                           .dd1    $2a
564c: 55                           .dd1    $55
564d: aa                           .dd1    $aa               ;7: orange
564e: d5                           .dd1    $d5               ;8: blue
564f: aa                           .dd1    $aa
5650: d5                           .dd1    $d5

                   ; Offsets to shrapnel graphics within a set.  Each set has 7 images, 2 bytes * 5
                   ; lines each, so we need to advance by 10 each time.  The value here is the
                   ; offset of the *last* byte within the set.
                   shrapnel_offsets
5651: 09                           .dd1    9
5652: 13                           .dd1    19
5653: 1d                           .dd1    29
5654: 27                           .dd1    39
5655: 31                           .dd1    49
5656: 3b                           .dd1    59
5657: 45                           .dd1    69

                   ; Pointers to shrapnel image sets.  Each of the 7 sets has 7 shrapnel pieces.
                   ; The pieces are bit-shifted to a pixel offset but also slightly different, so
                   ; as they move horizontally they change shape as well as position.
5658: 66 56        shrapnel_addrs  .dd2    shrapnel_blank
565a: ac 56                        .dd2    shrapnel_1
565c: f2 56                        .dd2    shrapnel_2
565e: 38 57                        .dd2    shrapnel_3
5660: 7e 57                        .dd2    shrapnel_4
5662: c4 57                        .dd2    shrapnel_5
5664: 0a 58                        .dd2    shrapnel_6

                   ; 
                   ; Shrapnel fragment images.  There are 6 sizes.
                   ; 
                   vis
5666: 00 00 00 00+ shrapnel_blank  .bulk   $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00

                   vis
56ac: 80 80 80 80+ shrapnel_1      .bulk   $80,$80,$80,$80,$88,$80,$80,$80,$80,$80,$80,$80,$80,$80,$90,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$80,$a0,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$c0,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$81,$80,$80
                                    +      $80,$80,$80,$80,$80,$80,$80,$82,$80,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$84,$80,$80,$80,$80

                   vis
56f2: 80 80 88 80+ shrapnel_2      .bulk   $80,$80,$88,$80,$8c,$80,$80,$80,$80,$80,$80,$80,$80,$80,$b0,$80
                                    +      $90,$80,$80,$80,$80,$80,$80,$80,$f0,$80,$80,$80,$80,$80,$80,$80
                                    +      $80,$80,$e0,$80,$c0,$80,$80,$80,$80,$80,$80,$81,$80,$81,$80,$81
                                    +      $80,$80,$80,$80,$80,$80,$80,$83,$80,$82,$80,$80,$80,$80,$80,$84
                                    +      $80,$8c,$80,$80,$80,$80

                   vis
5738: 80 80 88 80+ shrapnel_3      .bulk   $80,$80,$88,$80,$bc,$80,$98,$80,$80,$80,$80,$80,$90,$80,$f8,$80
                                    +      $b0,$80,$80,$80,$80,$80,$a0,$80,$f0,$81,$e0,$80,$80,$80,$80,$80
                                    +      $c0,$80,$e0,$83,$c0,$81,$80,$80,$80,$80,$80,$81,$c0,$87,$80,$83
                                    +      $80,$80,$80,$80,$80,$82,$80,$8f,$80,$86,$80,$80,$80,$80,$80,$84
                                    +      $80,$9e,$80,$8c,$80,$80

                   vis
577e: 88 80 88 80+ shrapnel_4      .bulk   $88,$80,$88,$80,$9c,$80,$9c,$80,$80,$80,$c0,$80,$a0,$80,$b8,$80
                                    +      $9c,$80,$80,$80,$80,$80,$f0,$80,$f0,$81,$f0,$82,$80,$80,$80,$80
                                    +      $80,$80,$e0,$80,$f0,$81,$c0,$81,$80,$80,$80,$87,$c0,$83,$c0,$83
                                    +      $80,$81,$80,$80,$80,$80,$80,$8e,$80,$8e,$c0,$81,$80,$80,$80,$8d
                                    +      $80,$8e,$80,$8c,$80,$80

                   vis
57c4: bc 80 bc 80+ shrapnel_5      .bulk   $bc,$80,$bc,$80,$f8,$80,$be,$80,$98,$80,$b8,$80,$f8,$80,$f8,$80
                                    +      $be,$80,$8c,$80,$a0,$80,$f0,$80,$f0,$81,$f0,$81,$f8,$81,$80,$80
                                    +      $e0,$83,$e0,$87,$e0,$87,$f0,$87,$80,$83,$c0,$87,$e0,$87,$c0,$87
                                    +      $80,$81,$80,$80,$80,$8e,$80,$9f,$c0,$8f,$80,$87,$80,$80,$80,$9c
                                    +      $80,$bd,$80,$9f,$80,$8f

                   vis
580a: bc 80 f0 80+ shrapnel_6      .bulk   $bc,$80,$f0,$80,$f0,$80,$be,$80,$9e,$80,$f8,$80,$fc,$80,$e0,$80
                                    +      $e0,$80,$f0,$80,$f8,$80,$fc,$81,$cc,$81,$c0,$81,$c0,$80,$f0,$81
                                    +      $f8,$83,$f8,$87,$98,$86,$90,$82,$c0,$87,$e0,$8f,$e0,$89,$e0,$81
                                    +      $c0,$83,$80,$87,$c0,$83,$c0,$93,$c0,$9f,$80,$87,$80,$81,$80,$b3
                                    +      $80,$bb,$80,$9f,$80,$8e
                   ; 
                   ; Shrapnel state.  There can be up to 14 pieces of shrapnel on screen.
                   ; 
                   ; The shrapnel status field holds 0-6, used to select the fragment set.  Set 0
                   ; is blank, but is drawn and erased anyway to maintain a constant speed.  A
                   ; status of zero is only meaningful when considering collisions.
                   ; 
                   ; Values are stored in parallel arrays.
                   ; 
5850: 00 00 00 00+ shrp_xlo_x8     .fill   14,$00            ;X position, mod 7 (0-6), * 8
585e: 00 00 00 00+ shrp_xhi        .fill   14,$00            ;X position, div 7
586c: 00 00 00 00+ shrp_xvel       .fill   14,$00            ;horizontal velocity (signed)
587a: 00 00 00 00+ shrp_y          .fill   14,$00
5888: 00 00 00 00+ shrp_yvel       .fill   14,$00            ;vertical velocity (signed)
5896: 00 00 00 00+ shrp_status     .fill   14,$00
58a4: 00 00 00 00+ shrp_color      .fill   14,$00
58b2: 00 00 00 00+ shrp_old_xlo_x8 .fill   14,$00
58c0: 00 00 00 00+ shrp_old_xhi    .fill   14,$00
58ce: 00 00 00 00+ shrp_old_y      .fill   14,$00
58dc: 00 00 00 00+ shrp_old_status .fill   14,$00
58ea: 00 00 00 00+ shrp_old_color  .fill   14,$00

                   ; 
                   ; Handles sabotage.
                   ; 
                   ; Triggers sabotage when certain conditions are met.  Performs the animation
                   ; steps when sabotage is in progress.
                   ; 
58f8: ad e8 42     CheckSabotage   lda     exploding_gun_ctr ;is gun exploding?
58fb: f0 01                        beq     @NoExplod         ;no, branch
58fd: 60                           rts

58fe: ad 27 5b     @NoExplod       lda     sabo_in_progress  ;is a sabotage animation in progress?
5901: d0 6f                        bne     @SaboAnim         ;yes, branch
5903: a9 00                        lda     #$00              ;init sabotage check
5905: 8d 28 5b                     sta     sabotage_side
5908: ad f5 43                     lda     ground_height_16  ;check left side of foundation
590b: c9 04                        cmp     #$04              ;does it have a para?
590d: b0 34                        bcs     @SaboLeft         ;yes, sabotage on left
590f: ad fa 43                     lda     ground_height_21  ;check right side of foundation
5912: c9 04                        cmp     #$04              ;does it have a para?
5914: b0 31                        bcs     @SaboRight        ;yes, sabotage on right
5916: ad f4 43                     lda     ground_height_15  ;check ground to left of foundation
5919: c9 03                        cmp     #$03              ;are three paras stacked up?
591b: b0 26                        bcs     @SaboLeft         ;yes, sabotage on left
591d: ad fb 43                     lda     ground_height_22  ;check ground to right of foundation
5920: c9 03                        cmp     #$03              ;are three paras stacked up?
5922: b0 23                        bcs     @SaboRight        ;yes, sabotage on right
                   ; Count up the landed paratroopers on the right (columns 22-36).
5924: a2 0f                        ldx     #15
5926: a9 00                        lda     #$00
5928: 18           @Loop           clc
5929: 7d fa 43                     adc     ground_height_21,x
592c: ca                           dex
592d: d0 f9                        bne     @Loop
592f: c9 04                        cmp     #$04              ;4 paratroopers on right?
5931: b0 14                        bcs     @SaboRight        ;yes, sabotage on right
                   ; Count up the landed paratroopers on the left (columns 1-15).
5933: a2 0f                        ldx     #15
5935: a9 00                        lda     #$00
5937: 18           @Loop           clc
5938: 7d e5 43                     adc     ground_height_0,x
593b: ca                           dex
593c: d0 f9                        bne     @Loop
593e: c9 04                        cmp     #$04              ;4 paratroopers on left?
5940: b0 01                        bcs     @SaboLeft         ;yes, sabotage on left
5942: 60           @Return         rts                       ;no, keep playing

5943: a9 01        @SaboLeft       lda     #$01
5945: 10 02                        bpl     @SaboComm         ;(always)

5947: a9 02        @SaboRight      lda     #$02
5949: 8d 28 5b     @SaboComm       sta     sabotage_side

                   ; Sabotage confirmed.  We want to let active elements clear before continuing.
                   ; Check if we have any falling paratroopers or active fliers.
594c: a2 00                        ldx     #$00              ;init para index
594e: bd 6d 43     @Loop           lda     para_status,x     ;currently active?
5951: 1d 6e 43                     ora     para_old_status,x ;previously active?
5954: 0d d5 42                     ora     active_bomber_count ;bombers in the air?
5957: 0d 29 64                     ora     active_heli_count ;helicopters in the air?
595a: d0 e6                        bne     @Return           ;something is still moving, bail
595c: 8a                           txa
595d: 18                           clc
595e: 69 05                        adc     #5                ;5 bytes per para struct
5960: aa                           tax
5961: e0 28                        cpx     #40               ;have we examined all 8 slots?
5963: 90 e9                        bcc     @Loop             ;not yet, loop
                   ; 
5965: a9 01                        lda     #$01
5967: 8d 27 5b                     sta     sabo_in_progress  ;set a flag to indicate we're end-stage
596a: a9 00                        lda     #$00              ;init animation variables
596c: 8d 26 5b                     sta     para_horiz_mvmt
596f: 8d 2d 5b                     sta     para_extra_drop
                   ; 
                   ; Start or advance a paratrooper sabotage animation.
                   ; 
5972: ad 2d 5b     @SaboAnim       lda     para_extra_drop   ;is an animation in progress?
5975: 0d 26 5b                     ora     para_horiz_mvmt
5978: d0 7f                        bne     @UpdateAnim       ;yes, branch
                   ; Find a paratrooper to move.
597a: ad 28 5b                     lda     sabotage_side     ;get sabotage side (1=left, 2=right)
597d: c9 01                        cmp     #$01              ;left?
597f: d0 1e                        bne     @OnRight          ;no, branch
                   ; 
5981: a2 11                        ldx     #17               ;start at left edge (16 is left side of foundation)
5983: ca           @Loop           dex
5984: bd e5 43                     lda     ground_height_0,x ;get height here
5987: f0 fa                        beq     @Loop             ;if empty, loop
5989: 38                           sec
598a: fd e6 43                     sbc     ground_height_0+1,x ;subtract height of ground to the right
598d: 90 f4                        bcc     @Loop             ;if right is higher, we can't move there; loop
598f: e0 10                        cpx     #16               ;are we at edge of foundation?
5991: d0 06                        bne     @NeedMove         ;no, need to move a para
5993: a9 ff        @ExplodeGun     lda     #$ff              ;para is on foundation, blow it up
5995: 8d e8 42                     sta     exploding_gun_ctr
5998: 60                           rts

5999: fe e6 43     @NeedMove       inc     ground_height_0+1,x ;increase height (add para) to the right
599c: 4c b4 59                     jmp     @PrepAnim         ;set up anim

599f: a2 14        @OnRight        ldx     #20               ;start at right edge (21 is right side of foundation)
59a1: e8           @Loop           inx
59a2: bd e5 43                     lda     ground_height_0,x ;get height here
59a5: f0 fa                        beq     @Loop             ;if empty, loop
59a7: 38                           sec
59a8: fd e4 43                     sbc     ground_height_0-1,x ;subtract height of ground to the left
59ab: 90 f4                        bcc     @Loop             ;if left is higher, we can't move there; loop
59ad: e0 15                        cpx     #21               ;are we at edge of foundation?
59af: f0 e2                        beq     @ExplodeGun       ;yes, para is on foundation, blow it up
59b1: fe e4 43                     inc     ground_height_0-1,x ;increase height (add para) to the left

                   ; Set up animation.  A-reg holds height diff, e.g. 1 if we're a solo para moving
                   ; into open space, 2 if we're a two-stack high para moving into open space. X-
                   ; reg holds start column.  We want to move the paratrooper in the current column
                   ; to the adjacent column.  This can involve sliding over, jumping down, or
                   ; jumping up on top of an existing trooper or gun foundation.
                   ; 
                   ; The only time we need to move more than one level is when jumping down.  Paras
                   ; can be stacked to a maximum height of four.  Two requires nothing special,
                   ; three and four require additional movement.
59b4: a0 00        @PrepAnim       ldy     #$00
59b6: 8c 2a 5b                     sty     parwalk_x_lo      ;start column-aligned
59b9: a0 07                        ldy     #$07
59bb: 8c 26 5b                     sty     para_horiz_mvmt   ;move 7 pixels (one column)
59be: 8e 29 5b                     stx     parwalk_x_hi      ;set start column
59c1: de e5 43                     dec     ground_height_0,x ;decrease height (remove para) from this spot
59c4: 8d 2c 5b                     sta     para_height_param ;save the height difference
59c7: f0 09                        beq     @GotHeight        ;DEC result was zero, para is not stacked, branch
59c9: c9 01                        cmp     #$01              ;are we 1 unit higher, moving laterally across paras?
59cb: d0 05                        bne     @GotHeight        ;no, branch
59cd: a0 03                        ldy     #$03              ;yes, set diff param to 3 (lateral jump)
59cf: 8c 2c 5b                     sty     para_height_param
59d2: 38           @GotHeight      sec
59d3: e9 02                        sbc     #$02              ;is height diff 0 or 1?
59d5: 30 15                        bmi     @SimpleMove       ;yes, nothing special required
59d7: 8d 2d 5b                     sta     para_extra_drop   ;store value
59da: 0e 2d 5b                     asl     para_extra_drop   ;multiply by 5 (pixel height of a stacked para)
59dd: 0e 2d 5b                     asl     para_extra_drop
59e0: 18                           clc
59e1: 6d 2d 5b                     adc     para_extra_drop
59e4: 8d 2d 5b                     sta     para_extra_drop
59e7: a9 02                        lda     #$02              ;set param to 2 (jump down)
59e9: 8d 2c 5b                     sta     para_height_param
59ec: bd e5 43     @SimpleMove     lda     ground_height_0,x ;get our height (which has already been decremented)
59ef: aa                           tax
59f0: bd 4a 4d                     lda     para_stop_row,x   ;convert to screen row
59f3: 18                           clc
59f4: 69 04                        adc     #$04              ;compensate for lack of parachute
59f6: 8d 2b 5b                     sta     parwalk_y         ;set current Y
                   ; 
59f9: 20 6b 5a     @UpdateAnim     jsr     DrawWalkingPara   ;erase paratrooper
59fc: ac 26 5b                     ldy     para_horiz_mvmt   ;more horizontal movement?
59ff: f0 58                        beq     @MoveDown         ;no, must be vertical drop; branch
5a01: ce 26 5b                     dec     para_horiz_mvmt   ;decrement counter
                   ; Find movement set.  0=jump up, 1=slide across, 2=jump down, 3=jump laterally.
5a04: ad 2c 5b                     lda     para_height_param ;height diff determines move set
5a07: c9 01                        cmp     #$01              ;is it < 1?
5a09: 90 12                        bcc     @Set0             ;yes, use set 0
5a0b: f0 16                        beq     @Set1             ;if == 1, use set 1
5a0d: c9 03                        cmp     #$03              ;== 3?
5a0f: d0 06                        bne     @Set2             ;no, use set 2
5a11: b9 3b 5b     @Set3           lda     para_ymove_3-1,y
5a14: 4c 26 5a                     jmp     @GotSet

5a17: b9 42 5b     @Set2           lda     para_ymove_2-1,y
5a1a: 4c 26 5a                     jmp     @GotSet

5a1d: b9 2d 5b     @Set0           lda     para_ymove_0-1,y
5a20: 4c 26 5a                     jmp     @GotSet

5a23: b9 34 5b     @Set1           lda     para_ymove_1-1,y
5a26: 18           @GotSet         clc                       ;the next Y coord change is in A-reg
5a27: 6d 2b 5b                     adc     parwalk_y         ;add to current Y position
5a2a: 8d 2b 5b                     sta     parwalk_y
5a2d: ad 28 5b                     lda     sabotage_side     ;which side are we on?
5a30: c9 01                        cmp     #$01
5a32: d0 15                        bne     @OnRight
5a34: ee 2a 5b                     inc     parwalk_x_lo      ;move para one pixel right
5a37: ad 2a 5b                     lda     parwalk_x_lo
5a3a: c9 07                        cmp     #7                ;reached byte boundary?
5a3c: d0 29                        bne     @DoDraw           ;no, branch
5a3e: ee 29 5b                     inc     parwalk_x_hi      ;update column number
5a41: a9 00                        lda     #$00
5a43: 8d 2a 5b                     sta     parwalk_x_lo      ;set pixel offset to zero
5a46: 4c 67 5a                     jmp     @DoDraw

5a49: ce 2a 5b     @OnRight        dec     parwalk_x_lo      ;more para one pixel left
5a4c: 10 19                        bpl     @DoDraw           ;if we didn't underflow, branch
5a4e: ce 29 5b                     dec     parwalk_x_hi      ;update column number
5a51: a9 06                        lda     #6
5a53: 8d 2a 5b                     sta     parwalk_x_lo      ;put us in rightmost pixel of byte
5a56: 4c 67 5a                     jmp     @DoDraw

                   ; After horizontal movement ends, if the destination column height is much less
                   ; than the source column height (e.g. we're jumping off a three-high para
                   ; stack), we need to do additional vertical movement.  Fall at 2 rows per frame.
5a59: ee 2b 5b     @MoveDown       inc     parwalk_y         ;move down one row
5a5c: ce 2d 5b                     dec     para_extra_drop   ;decrement movement
5a5f: f0 06                        beq     @DoDraw           ;if we're done, branch
5a61: ee 2b 5b                     inc     parwalk_y         ;move down another row
5a64: ce 2d 5b                     dec     para_extra_drop
5a67: 20 6b 5a     @DoDraw         jsr     DrawWalkingPara   ;draw paratrooper
5a6a: 60                           rts

                   ; 
                   ; Draws (erases) a paratrooper as it walks across the screen toward the gun.
                   ; 
                   ; On entry:
                   ;   $5b29: X coordinate, high part (0-39)
                   ;   $5b2a: X coordinate, low part (0-6)
                   ;   $5b2b: Y coordinate (0-191)
                   ; 
                   • Clear variables
                   ]bitmap_ptr     .var    $76    {addr/2}

5a6b: ad 2a 5b     DrawWalkingPara lda     parwalk_x_lo      ;get horizontal position low (mod 7)
5a6e: 0a                           asl     A                 ;double it
5a6f: aa                           tax
5a70: bd 4a 5b                     lda     para_addrs,x      ;get address of image
5a73: 85 76                        sta     ]bitmap_ptr
5a75: bd 4b 5b                     lda     para_addrs+1,x
5a78: 85 77                        sta     ]bitmap_ptr+1
5a7a: a0 0b                        ldy     #11               ;2 bytes * 6 lines per image
5a7c: b1 76        @Loop           lda     (]bitmap_ptr),y
5a7e: 99 58 5b                     sta     para_img_tmp,y    ;copy bitmap into temporary storage
5a81: 88                           dey
5a82: 10 f8                        bpl     @Loop

                   ; Draw the bitmap from temporary storage.
                   ]hires_ptr      .var    $76    {addr/2}

5a84: ae 2b 5b                     ldx     parwalk_y         ;get paratrooper Y coord
5a87: ac 29 5b                     ldy     parwalk_x_hi      ;get high part of X coord (0-39)
5a8a: bd 5e 4d                     lda     hires_addr_hi,x   ;calculate hi-res line offset
5a8d: 85 77                        sta     ]hires_ptr+1
5a8f: bd 1e 4e                     lda     hires_addr_lo,x
5a92: 85 76                        sta     ]hires_ptr
5a94: b1 76                        lda     (]hires_ptr),y    ;draw top-left byte
5a96: 4d 58 5b                     eor     para_img_tmp
5a99: 91 76                        sta     (]hires_ptr),y
5a9b: c8                           iny
5a9c: b1 76                        lda     (]hires_ptr),y    ;drop top-right byte
5a9e: 4d 59 5b                     eor     para_img_tmp+1
5aa1: 91 76                        sta     (]hires_ptr),y
5aa3: 88                           dey
5aa4: bd 5f 4d                     lda     hires_addr_hi+1,x ;...and so on for all 6 rows
5aa7: 85 77                        sta     ]hires_ptr+1
5aa9: bd 1f 4e                     lda     hires_addr_lo+1,x
5aac: 85 76                        sta     ]hires_ptr
5aae: b1 76                        lda     (]hires_ptr),y
5ab0: 4d 5a 5b                     eor     para_img_tmp+2
5ab3: 91 76                        sta     (]hires_ptr),y
5ab5: c8                           iny
5ab6: b1 76                        lda     (]hires_ptr),y
5ab8: 4d 5b 5b                     eor     para_img_tmp+3
5abb: 91 76                        sta     (]hires_ptr),y
5abd: 88                           dey
5abe: bd 60 4d                     lda     hires_addr_hi+2,x
5ac1: 85 77                        sta     ]hires_ptr+1
5ac3: bd 20 4e                     lda     hires_addr_lo+2,x
5ac6: 85 76                        sta     ]hires_ptr
5ac8: b1 76                        lda     (]hires_ptr),y
5aca: 4d 5c 5b                     eor     para_img_tmp+4
5acd: 91 76                        sta     (]hires_ptr),y
5acf: c8                           iny
5ad0: b1 76                        lda     (]hires_ptr),y
5ad2: 4d 5d 5b                     eor     para_img_tmp+5
5ad5: 91 76                        sta     (]hires_ptr),y
5ad7: 88                           dey
5ad8: bd 61 4d                     lda     hires_addr_hi+3,x
5adb: 85 77                        sta     ]hires_ptr+1
5add: bd 21 4e                     lda     hires_addr_lo+3,x
5ae0: 85 76                        sta     ]hires_ptr
5ae2: b1 76                        lda     (]hires_ptr),y
5ae4: 4d 5e 5b                     eor     para_img_tmp+6
5ae7: 91 76                        sta     (]hires_ptr),y
5ae9: c8                           iny
5aea: b1 76                        lda     (]hires_ptr),y
5aec: 4d 5f 5b                     eor     para_img_tmp+7
5aef: 91 76                        sta     (]hires_ptr),y
5af1: 88                           dey
5af2: bd 62 4d                     lda     hires_addr_hi+4,x
5af5: 85 77                        sta     ]hires_ptr+1
5af7: bd 22 4e                     lda     hires_addr_lo+4,x
5afa: 85 76                        sta     ]hires_ptr
5afc: b1 76                        lda     (]hires_ptr),y
5afe: 4d 60 5b                     eor     para_img_tmp+8
5b01: 91 76                        sta     (]hires_ptr),y
5b03: c8                           iny
5b04: b1 76                        lda     (]hires_ptr),y
5b06: 4d 61 5b                     eor     para_img_tmp+9
5b09: 91 76                        sta     (]hires_ptr),y
5b0b: 88                           dey
5b0c: bd 63 4d                     lda     hires_addr_hi+5,x
5b0f: 85 77                        sta     ]hires_ptr+1
5b11: bd 23 4e                     lda     hires_addr_lo+5,x
5b14: 85 76                        sta     ]hires_ptr
5b16: b1 76                        lda     (]hires_ptr),y
5b18: 4d 62 5b                     eor     para_img_tmp+10
5b1b: 91 76                        sta     (]hires_ptr),y
5b1d: c8                           iny
5b1e: b1 76                        lda     (]hires_ptr),y
5b20: 4d 63 5b                     eor     para_img_tmp+11
5b23: 91 76                        sta     (]hires_ptr),y    ;...bottom right done
5b25: 60                           rts

                   ; 
                   ; Paratrooper animation state.
                   ; 
5b26: 00           para_horiz_mvmt .dd1    $00               ;number of steps left in para horizontal move (1-7)
                   sabo_in_progress
5b27: 00                           .dd1    $00               ;bool (0/1): is sabotage in progress?
5b28: 00           sabotage_side   .dd1    $00               ;0=no sabotage, 1=left, 2=right
5b29: 00           parwalk_x_hi    .dd1    $00               ;animated paratrooper walk position
5b2a: 00           parwalk_x_lo    .dd1    $00
5b2b: 00           parwalk_y       .dd1    $00
                   para_height_param
5b2c: 00                           .dd1    $00               ;movement parameter, 0-3 (select ymove table)
5b2d: 00           para_extra_drop .dd1    $00               ;extra vertical drop, nonzero if para stack >= 3
5b2e: 01 01 00 ff+ para_ymove_0    .bulk   $01,$01,$00,$ff,$fe,$fe,$fe ;jump up (+2, -7)
5b35: 00 00 00 00+ para_ymove_1    .bulk   $00,$00,$00,$00,$00,$00,$00 ;slide across
5b3c: 02 02 01 00+ para_ymove_3    .bulk   $02,$02,$01,$00,$ff,$fe,$fe ;jump laterally (+5, -5)
5b43: 02 02 02 01+ para_ymove_2    .bulk   $02,$02,$02,$01,$00,$ff,$ff ;jump down (+7, -2)

                   ; Addresses of walking paratrooper images.
5b4a: 64 5b        para_addrs      .dd2    trooper_0
5b4c: 70 5b                        .dd2    trooper_1
5b4e: 7c 5b                        .dd2    trooper_2
5b50: 88 5b                        .dd2    trooper_3
5b52: 94 5b                        .dd2    trooper_4
5b54: a0 5b                        .dd2    trooper_5
5b56: ac 5b                        .dd2    trooper_6
5b58: 00 00 00 00+ para_img_tmp    .ds     12                ;holds copy of walking paratrooper image data

                   ; 
                   ; Walking paratrooper images.  These are not used when falling; the
                   ; trooper+parachute image is hard-coded for that.  Each images is 2 bytes by 6
                   ; lines (12 bytes).
                   ; 
                   vis vis
5b64: 08 00 3e 00+ trooper_0       .bulk   $08,$00,$3e,$00,$5d,$00,$1c,$00,$14,$00,$22,$00

                   vis
5b70: 10 00 7c 00+ trooper_1       .bulk   $10,$00,$7c,$00,$3a,$01,$38,$00,$28,$00,$48,$00

                   vis
5b7c: 20 00 78 01+ trooper_2       .bulk   $20,$00,$78,$01,$74,$02,$70,$00,$50,$00,$48,$00

                   vis
5b88: 40 00 70 03+ trooper_3       .bulk   $40,$00,$70,$03,$68,$05,$60,$01,$20,$01,$20,$02

                   vis
5b94: 00 01 60 07+ trooper_4       .bulk   $00,$01,$60,$07,$50,$0b,$40,$03,$40,$02,$20,$02

                   vis
5ba0: 00 02 40 0f+ trooper_5       .bulk   $00,$02,$40,$0f,$20,$17,$00,$07,$00,$05,$00,$09

                   vis
5bac: 00 04 00 1f+ trooper_6       .bulk   $00,$04,$00,$1f,$40,$2e,$00,$0e,$00,$0a,$00,$11

                   ; 
                   ; Increases the current score by a 16-bit BCD value.  Used to increment the
                   ; score when an enemy is hit.  Redraws the score.
                   ; 
                   ; On entry:
                   ;   A-reg: score diff low byte
                   ;   X-reg: score diff high byte
                   ; 
5bb8: 8d e4 5b     IncreaseScore   sta     score_change      ;save the arguments
5bbb: 8e e5 5b                     stx     score_change+1
5bbe: 4d e4 42                     eor     rng_state
5bc1: 8d e4 42                     sta     rng_state
5bc4: 20 12 5c                     jsr     DoDrawScore       ;erase the old score
5bc7: f8                           sed                       ;switch to BCD mode
5bc8: 18                           clc
5bc9: ad eb 42                     lda     cur_score
5bcc: 6d e4 5b                     adc     score_change      ;add the points
5bcf: 8d eb 42                     sta     cur_score         ;update the score
5bd2: ad ec 42                     lda     cur_score+1
5bd5: 6d e5 5b                     adc     score_change+1
5bd8: 8d ec 42                     sta     cur_score+1
5bdb: d8                           cld                       ;clear BCD mode
                   ; Draw the score on the hi-res screen.
5bdc: 20 12 5c     DrawScore       jsr     DoDrawScore
5bdf: 60                           rts

5be0: 00 00        score_tmp       .dd2    $0000             ;holds score while drawing it on hi-res screen
5be2: 00           score_lz_flag   .dd1    $00               ;leading-zero flag for score drawing
5be3: 00                           .dd1    $00
5be4: 00 00        score_change    .dd2    $0000             ;temporary storage when doing math

                   ; 
                   ; Reduces the current score by a 16-bit BCD value.  Used to decrement the score
                   ; when a shot is fired.  Redraws the score.
                   ; 
                   ; On entry:
                   ;   A-reg: score diff low byte
                   ;   X-reg: score diff high byte
                   ; 
5be6: 8d e4 5b     ReduceScore     sta     score_change      ;save the arguments
5be9: 8e e5 5b                     stx     score_change+1
5bec: 20 12 5c                     jsr     DoDrawScore       ;erase the current score
5bef: f8                           sed                       ;switch to decimal mode
5bf0: ad eb 42                     lda     cur_score
5bf3: 38                           sec
5bf4: ed e4 5b                     sbc     score_change      ;subtract the points
5bf7: 8d eb 42                     sta     cur_score         ;update the score
5bfa: ad ec 42                     lda     cur_score+1
5bfd: ed e5 5b                     sbc     score_change+1
5c00: 8d ec 42                     sta     cur_score+1
5c03: d8                           cld                       ;clear BCD mode
5c04: b0 08                        bcs     @NonNeg           ;if it's still positive, branch
5c06: a9 00                        lda     #$00              ;went negative, clamp to zero
5c08: 8d eb 42                     sta     cur_score
5c0b: 8d ec 42                     sta     cur_score+1
5c0e: 20 12 5c     @NonNeg         jsr     DoDrawScore       ;draw the new score
5c11: 60                           rts

                   ; 
                   ; Draws or erases the score as a 4-digit decimal number on the hi-res screen.
                   ; Drawing is done with XOR, so drawing the same thing twice erases it.
                   ; 
5c12: ad eb 42     DoDrawScore     lda     cur_score         ;copy score to temporary storage
5c15: 8d e0 5b                     sta     score_tmp
5c18: ad ec 42                     lda     cur_score+1
5c1b: 8d e1 5b                     sta     score_tmp+1
5c1e: a0 11                        ldy     #17               ;start at column 17
5c20: a9 00                        lda     #$00
5c22: 8d e2 5b                     sta     score_lz_flag     ;init leading-zero flag
5c25: a9 00        @DigitLoop      lda     #$00              ;shift the 16-bit BCD value 4 bits left,
5c27: 2e e0 5b                     rol     score_tmp         ; rolling the high 4 bits into the A-reg
5c2a: 2e e1 5b                     rol     score_tmp+1
5c2d: 2a                           rol     A
5c2e: 2e e0 5b                     rol     score_tmp
5c31: 2e e1 5b                     rol     score_tmp+1
5c34: 2a                           rol     A
5c35: 2e e0 5b                     rol     score_tmp
5c38: 2e e1 5b                     rol     score_tmp+1
5c3b: 2a                           rol     A
5c3c: 2e e0 5b                     rol     score_tmp
5c3f: 2e e1 5b                     rol     score_tmp+1
5c42: 2a                           rol     A
5c43: 0a                           asl     A                 ;multiply by 8 to get glyph address
5c44: 0a                           asl     A
5c45: 0a                           asl     A
5c46: aa                           tax
5c47: d0 09                        bne     @NonZero          ;if we found a nonzero value, draw it
5c49: c0 14                        cpy     #20               ;at column 20 (least-significant digit)?
5c4b: f0 0a                        beq     @DoDraw           ;yes, always draw
5c4d: ad e2 5b                     lda     score_lz_flag     ;have we found a nonzero digit?
5c50: f0 44                        beq     @Next             ;no, skip draw
5c52: a9 01        @NonZero        lda     #$01
5c54: 8d e2 5b                     sta     score_lz_flag     ;non-'0' seen, set flag
                   ; Draw the glyph, using fixed screen addresses.
5c57: bd 9d 5c     @DoDraw         lda     number_glyphs,x   ;get first (top) byte of glyph
5c5a: 59 50 33                     eor     HIRES_SCREEN+$1350,y ;merge it with existing contents
5c5d: 99 50 33                     sta     HIRES_SCREEN+$1350,y ;store merged result
5c60: bd 9e 5c                     lda     number_glyphs+1,x ; ... repeat ...
5c63: 59 50 37                     eor     HIRES_SCREEN+$1750,y
5c66: 99 50 37                     sta     HIRES_SCREEN+$1750,y
5c69: bd 9f 5c                     lda     number_glyphs+2,x
5c6c: 59 50 3b                     eor     HIRES_SCREEN+$1b50,y
5c6f: 99 50 3b                     sta     HIRES_SCREEN+$1b50,y
5c72: bd a0 5c                     lda     number_glyphs+3,x
5c75: 59 50 3f                     eor     HIRES_SCREEN+$1f50,y
5c78: 99 50 3f                     sta     HIRES_SCREEN+$1f50,y
5c7b: bd a1 5c                     lda     number_glyphs+4,x
5c7e: 59 d0 23                     eor     HIRES_SCREEN+$3d0,y
5c81: 99 d0 23                     sta     HIRES_SCREEN+$3d0,y
5c84: bd a2 5c                     lda     number_glyphs+5,x
5c87: 59 d0 27                     eor     HIRES_SCREEN+$7d0,y
5c8a: 99 d0 27                     sta     HIRES_SCREEN+$7d0,y
5c8d: bd a3 5c                     lda     number_glyphs+6,x
5c90: 59 d0 2b                     eor     HIRES_SCREEN+$bd0,y
5c93: 99 d0 2b                     sta     HIRES_SCREEN+$bd0,y
5c96: c8           @Next           iny
5c97: c0 15                        cpy     #21               ;done with all 4 digits?
5c99: 90 8a                        bcc     @DigitLoop        ;no, loop
5c9b: 60                           rts

5c9c: 7f                           .dd1    $7f

                   ; 
                   ; Number glyphs, used to display the score during the game.  The images are 8
                   ; bytes apart, but only the first 7 rows are used.
                   ; 
                   vis
5c9d: 1c 22 22 22+ number_glyphs   .bulk   $1c,$22,$22,$22,$22,$22,$1c,$7f
5ca5: 08 0c 08 08+                 .bulk   $08,$0c,$08,$08,$08,$08,$1c,$7f
5cad: 1c 22 20 10+                 .bulk   $1c,$22,$20,$10,$08,$04,$3e,$7f
5cb5: 1c 22 20 1c+                 .bulk   $1c,$22,$20,$1c,$20,$22,$1c,$7f
5cbd: 10 18 14 12+                 .bulk   $10,$18,$14,$12,$3e,$10,$10,$7f
5cc5: 3e 02 02 1e+                 .bulk   $3e,$02,$02,$1e,$20,$20,$1e,$7f
5ccd: 1c 22 02 1e+                 .bulk   $1c,$22,$02,$1e,$22,$22,$1c,$7f
5cd5: 3e 20 10 08+                 .bulk   $3e,$20,$10,$08,$04,$04,$04,$7f
5cdd: 1c 22 22 1c+                 .bulk   $1c,$22,$22,$1c,$22,$22,$1c,$7f
5ce5: 1c 22 22 3c+                 .bulk   $1c,$22,$22,$3c,$20,$22,$1c

                   ; 
                   ; Initializes game state, preparing all data structures.
                   ; 
                   ; Prepares the high-res screen, clearing it and drawing the fixed pieces
                   ; (ground, gun base, etc.)
                   ; 
                   ]hires_ptr      .var    $76    {addr/2}

5cec: a2 48        InitGame        ldx     #72               ;8 shells, 9 bytes each
5cee: a9 00                        lda     #$00
5cf0: 9d f4 42     @Loop           sta     shell_base,x      ;zero out all values
5cf3: ca                           dex
5cf4: d0 fa                        bne     @Loop
                   ; 
5cf6: a2 01                        ldx     #1                ;two slots for bombs
5cf8: 9d 49 6b     @Loop           sta     bomb_old_status,x ;zero out all values
5cfb: 9d 4b 6b                     sta     bomb_old_x_lo,x
5cfe: 9d 4d 6b                     sta     bomb_old_x_hi,x
5d01: 9d 4f 6b                     sta     bomb_old_y,x
5d04: 9d 51 6b                     sta     bomb_status,x
5d07: 9d 53 6b                     sta     bomb_x_lo,x
5d0a: 9d 55 6b                     sta     bomb_x_hi,x
5d0d: 9d 57 6b                     sta     bomb_y,x
5d10: ca                           dex
5d11: 10 e5                        bpl     @Loop
                   ; 
5d13: a2 28                        ldx     #40
5d15: a9 00                        lda     #$00
5d17: 9d 44 43     @Loop           sta     flier_base,x
5d1a: ca                           dex
5d1b: d0 fa                        bne     @Loop
                   ; 
5d1d: a2 0d                        ldx     #13               ;14 slots for shrapnel pieces
5d1f: a9 00        @Loop           lda     #$00
5d21: 9d a4 58                     sta     shrp_color,x
5d24: 9d ea 58                     sta     shrp_old_color,x
5d27: 9d 96 58                     sta     shrp_status,x
5d2a: 9d dc 58                     sta     shrp_old_status,x
5d2d: 9d 6c 58                     sta     shrp_xvel,x
5d30: 9d 88 58                     sta     shrp_yvel,x
5d33: a9 03                        lda     #$03
5d35: 9d 7a 58                     sta     shrp_y,x
5d38: 9d ce 58                     sta     shrp_old_y,x
5d3b: 9d 5e 58                     sta     shrp_xhi,x
5d3e: 9d 5e 58                     sta     shrp_xhi,x
5d41: 9d b2 58                     sta     shrp_old_xlo_x8,x
5d44: 9d c0 58                     sta     shrp_old_xhi,x
5d47: ca                           dex
5d48: 10 d5                        bpl     @Loop
                   ; 
5d4a: a9 34                        lda     #52               ;aim left, nearly horizontal
5d4c: 8d ea 42                     sta     cur_gun_angle
5d4f: a9 00                        lda     #$00
5d51: 8d e8 42                     sta     exploding_gun_ctr ;reset "gun is exploding" state
5d54: 8d 28 5b                     sta     sabotage_side
5d57: 8d 27 5b                     sta     sabo_in_progress
5d5a: 8d 5b 6c                     sta     gun_base_erased_flag
                   ; Init ground heights, for paratooper landings.
5d5d: a9 00                        lda     #$00              ;ground is level 0
5d5f: a2 24                        ldx     #36
5d61: 9d e5 43     @Loop           sta     ground_height_0,x
5d64: 9d bd 43                     sta     unused_43bd?,x
5d67: ca                           dex
5d68: d0 f7                        bne     @Loop
5d6a: a9 04                        lda     #4                ;set gun and adjacent columns to 4
5d6c: 8d f6 43                     sta     ground_height_17  ;this blocks paratroopers from landing here
5d6f: 8d f7 43                     sta     ground_height_18
5d72: 8d f8 43                     sta     ground_height_19
5d75: 8d f9 43                     sta     ground_height_20
5d78: a9 03                        lda     #3                ;set edge of gun base to height 3
5d7a: 8d f5 43                     sta     ground_height_16  ;this allows one paratrooper to drop here
5d7d: 8d fa 43                     sta     ground_height_21
                   ; Clear the hi-res screen ($2000-3fff).
5d80: a9 20                        lda     #>HIRES_SCREEN    ;set pointer to hi-res page 1
5d82: 85 77                        sta     ]hires_ptr+1
5d84: a9 00                        lda     #<HIRES_SCREEN
5d86: 85 76                        sta     ]hires_ptr
5d88: a2 20                        ldx     #$20              ;32 pages
5d8a: a8                           tay
5d8b: 91 76        @ClearLoop      sta     (]hires_ptr),y
5d8d: c8                           iny
5d8e: d0 fb                        bne     @ClearLoop
5d90: e6 77                        inc     ]hires_ptr+1
5d92: ca                           dex
5d93: d0 f6                        bne     @ClearLoop
                   ; Draw 2-pixel-high line of green at bottom of screen, convering bytes [1,36].
5d95: a2 bf                        ldx     #191              ;start at the bottom of the screen
5d97: bd 5e 4d     @GroundLoop     lda     hires_addr_hi,x
5d9a: 85 77                        sta     ]hires_ptr+1
5d9c: bd 1e 4e                     lda     hires_addr_lo,x
5d9f: 85 76                        sta     ]hires_ptr
5da1: a0 24                        ldy     #36               ;a few bytes from right edge
5da3: ad 49 56     @GroundLoop1    lda     ground_color_e    ;get ground color, even bytes
5da6: 91 76                        sta     (]hires_ptr),y
5da8: 88                           dey
5da9: ad 4a 56                     lda     ground_color_o    ;get ground color, odd bytes
5dac: 91 76                        sta     (]hires_ptr),y
5dae: 88                           dey
5daf: d0 f2                        bne     @GroundLoop1      ;loop until near (but not at) left edge
5db1: ca                           dex
5db2: e0 be                        cpx     #190              ;do on line 190 as well
5db4: f0 e1                        beq     @GroundLoop
                   ; Draw white box that forms gun foundation and hosts score display.
5db6: a2 bd                        ldx     #189
5db8: bd 5e 4d     @BoxLoop        lda     hires_addr_hi,x
5dbb: 85 77                        sta     ]hires_ptr+1
5dbd: bd 1e 4e                     lda     hires_addr_lo,x
5dc0: 85 76                        sta     ]hires_ptr
5dc2: a0 10                        ldy     #16               ;offset near center of screen
5dc4: a9 7e                        lda     #$7e              ;6 white pixels (black pixel on left)
5dc6: 91 76                        sta     (]hires_ptr),y    ;set screen value
5dc8: c8                           iny
5dc9: a9 7f                        lda     #$7f              ;all white pixels
5dcb: 91 76        @BoxLoop1       sta     (]hires_ptr),y    ;set screen value
5dcd: c8                           iny
5dce: c0 15                        cpy     #21               ;have we gone past row 21?
5dd0: 90 f9                        bcc     @BoxLoop1         ;not yet, branch
5dd2: a9 3f                        lda     #$3f              ;6 white pixels (black pixel on right)
5dd4: 91 76                        sta     (]hires_ptr),y    ;set screen value
5dd6: ca                           dex
5dd7: e0 af                        cpx     #175              ;have we gone above row 175?
5dd9: b0 dd                        bcs     @BoxLoop          ;not yet, branch
                   ; Draw orange block on top of white block; forms base of gun.
5ddb: 20 31 6c                     jsr     DrawGunBase
                   ; Zero out the score and draw it.
5dde: a9 00                        lda     #$00
5de0: 8d eb 42                     sta     cur_score
5de3: 8d ec 42                     sta     cur_score+1
5de6: 4c dc 5b                     jmp     DrawScore

5de9: 00                           .dd1    $00

                   ; 
                   ; Fires a shell from the gun if a slot is available.
                   ; 
5dea: ad e8 42     FireShell       lda     exploding_gun_ctr ;are we currently exploding?
5ded: d0 10                        bne     @Return           ;yes, bail
                   ; Find the first empty slot.
5def: a2 00                        ldx     #$00
5df1: bd f5 42     @FindLoop       lda     shell_active_flag,x
5df4: f0 0a                        beq     @FoundSlot
5df6: 8a                           txa
5df7: 18                           clc
5df8: 69 09                        adc     #9                ;9 bytes per struct
5dfa: aa                           tax
5dfb: e0 48                        cpx     #72               ;have we checked all 8?
5dfd: 90 f2                        bcc     @FindLoop         ;not yet, loop
5dff: 60           @Return         rts

5e00: a9 01        @FoundSlot      lda     #$01
5e02: 9d f5 42                     sta     shell_active_flag,x ;mark slot as in-use
5e05: a9 00                        lda     #$00
5e07: 9d f7 42                     sta     shell_x_lo,x      ;zero out the low parts of the position
5e0a: 9d f9 42                     sta     shell_y_lo,x
5e0d: ad ea 42                     lda     cur_gun_angle     ;get the gun's angle
5e10: 0a                           asl     A                 ;multiply by 4 to get a table index
5e11: 0a                           asl     A
5e12: a8                           tay
5e13: b9 b8 60                     lda     shell_deltay_lo,y ;copy velocity vector into shell struct
5e16: 9d fd 42                     sta     shell_dy_lo,x
5e19: b9 b9 60                     lda     shell_deltay_hi,y
5e1c: 9d fc 42                     sta     shell_dy_hi,x
5e1f: b9 ba 60                     lda     shell_deltax_lo,y
5e22: 9d fb 42                     sta     shell_dx_lo,x
5e25: b9 bb 60                     lda     shell_deltax_hi,y
5e28: 9d fa 42                     sta     shell_dx_hi,x
5e2b: ac ea 42                     ldy     cur_gun_angle     ;get gun angle [1,55]
5e2e: b9 9d 46                     lda     gun_posn_to_index,y ;map to doubled image index [0,36]
5e31: a8                           tay
5e32: b9 83 5e                     lda     shell_initial_posn,y ;get initial X position
5e35: 9d f6 42                     sta     shell_x_hi,x      ;set shell X position
5e38: 4d e4 42                     eor     rng_state         ;(update RNG)
5e3b: 8d e4 42                     sta     rng_state
5e3e: b9 84 5e                     lda     shell_initial_posn+1,y ;get initial Y position
5e41: 9d f8 42                     sta     shell_y_hi,x      ;set shell Y position
5e44: a8                           tay
5e45: b9 5e 4d                     lda     hires_addr_hi,y   ;set hi-res line address
5e48: 85 77                        sta     ]hires_ptr+1
5e4a: 4d e5 42                     eor     rng_state+1       ;(update RNG)
5e4d: 8d e5 42                     sta     rng_state+1
5e50: b9 1e 4e                     lda     hires_addr_lo,y
5e53: 85 76                        sta     ]hires_ptr
5e55: 4d e4 42                     eor     rng_state         ;(update RNG)
5e58: 8d e4 42                     sta     rng_state
                   ; Draw shell.
5e5b: bd f6 42                     lda     shell_x_hi,x      ;get high byte of 8.8 X coordinate
5e5e: aa                           tax
5e5f: bc de 4e                     ldy     xc_to_byte,x      ;convert to hi-res column
5e62: bd f6 4f                     lda     xc_to_pixel,x     ;get pixel within byte
5e65: 51 76                        eor     (]hires_ptr),y    ;merge with screen contents
5e67: 91 76                        sta     (]hires_ptr),y    ;write to screen
                   ; Configure sound.
5e69: ad d5 6c                     lda     sound_event_num
5e6c: 30 04                        bmi     @Neg
5e6e: c9 01                        cmp     #$01
5e70: d0 09                        bne     @SubPoint
5e72: a2 01        @Neg            ldx     #$01
5e74: 8e d5 6c                     stx     sound_event_num
5e77: ca                           dex
5e78: 8e d6 6c                     stx     sound_event_ctr
                   ; 
5e7b: a9 01        @SubPoint       lda     #$01              ;subtract one point
5e7d: a2 00                        ldx     #$00
5e7f: 20 e6 5b                     jsr     ReduceScore
5e82: 60                           rts

                   ; 
                   ; Initial shell positions, based on gun angle.
                   ; 
                   ; To use, take the angle [1,55] and look up the bitmap index from the table at
                   ; $469d to get a value [0,19].  Double that and use it as an index here to get
                   ; screen X/Y.
                   ; 
                   shell_initial_posn
5e83: 8b a6                        .bulk   $8b,$a6
5e85: 8b a4                        .bulk   $8b,$a4
5e87: 8b a2                        .bulk   $8b,$a2
5e89: 8a a1                        .bulk   $8a,$a1
5e8b: 8a 9f                        .bulk   $8a,$9f
5e8d: 89 9e                        .bulk   $89,$9e
5e8f: 86 9d                        .bulk   $86,$9d
5e91: 85 9d                        .bulk   $85,$9d
5e93: 83 9c                        .bulk   $83,$9c
5e95: 7f 9c                        .bulk   $7f,$9c
5e97: 7d 9c                        .bulk   $7d,$9c
5e99: 79 9c                        .bulk   $79,$9c
5e9b: 75 9c                        .bulk   $75,$9c
5e9d: 74 9d                        .bulk   $74,$9d
5e9f: 73 9e                        .bulk   $73,$9e
5ea1: 71 9f                        .bulk   $71,$9f
5ea3: 70 a0                        .bulk   $70,$a0
5ea5: 70 a4                        .bulk   $70,$a4
5ea7: 70 a6                        .bulk   $70,$a6
5ea9: 7e 9c                        .bulk   $7e,$9c
5eab: 00                           .junk   1

                   ; 
                   ; Updates the shells fired by the player.  Each shell is moved in multiple small
                   ; steps, with collision checks (vs. fliers, paratroopers, and bombs) at each
                   ; step.
                   ; 
                   • Clear variables
                   ]hires_ptr      .var    $76    {addr/2}
                   ]shell_index    .var    $78    {addr/1}
                   ]deltax_lo      .var    $79    {addr/1}
                   ]deltax_hi      .var    $7a    {addr/1}
                   ]deltay_lo      .var    $7b    {addr/1}
                   ]deltay_hi      .var    $7c    {addr/1}
                   ]xposn_hi       .var    $7d    {addr/1}
                   ]xposn_lo       .var    $7e    {addr/1}
                   ]yposn_hi       .var    $7f    {addr/1}
                   ]yposn_lo       .var    $80    {addr/1}
                   ]move_count     .var    $84    {addr/1}
                   ]saved_xreg     .var    $85    {addr/1}
                   ]saved_yreg     .var    $86    {addr/1}

5eac: a0 00        UpdateShells    ldy     #$00
5eae: 84 78        @ShellLoop      sty     ]shell_index      ;shell index, a multiple of 9
5eb0: b9 f5 42                     lda     shell_active_flag,y ;is this shell active?
5eb3: d0 15                        bne     @EraseShell       ;yes, redraw it
                   ; Delay for 2+((48*15)-1+7)*8-1=5809 cycles for each inactive shell.  Presumably
                   ; this is here to keep the game speed constant regardless of how many shells are
                   ; on screen.
5eb5: a0 08                        ldy     #8
5eb7: a2 00        @Loop1          ldx     #0
5eb9: e8           @Loop2          inx
5eba: ca                           dex
5ebb: e8                           inx
5ebc: ca                           dex
5ebd: e8                           inx
5ebe: e0 30                        cpx     #48
5ec0: f0 f7                        beq     @Loop2
5ec2: 88                           dey
5ec3: d0 f2                        bne     @Loop1
                   ; advance to next shell
5ec5: a4 78                        ldy     ]shell_index
5ec7: 4c c2 5f                     jmp     @NextShell

5eca: be f8 42     @EraseShell     ldx     shell_y_hi,y      ;get Y coordinate
5ecd: bd 5e 4d                     lda     hires_addr_hi,x   ;get hi-res line address
5ed0: 85 77                        sta     ]hires_ptr+1
5ed2: bd 1e 4e                     lda     hires_addr_lo,x
5ed5: 85 76                        sta     ]hires_ptr
5ed7: be f6 42                     ldx     shell_x_hi,y      ;get X coordinate
5eda: bc de 4e                     ldy     xc_to_byte,x      ;get byte index
5edd: bd f6 4f                     lda     xc_to_pixel,x     ;get single-pixel value
5ee0: 51 76                        eor     (]hires_ptr),y    ;merge with screen contents
5ee2: 91 76                        sta     (]hires_ptr),y    ;update screen
                   ; Get shell movement delta, based on whether steerable shells are enabled.
5ee4: ad ad 60                     lda     is_steerable      ;are steerable shells enabled?
5ee7: d0 19                        bne     @DoSteering       ;yes, branch
5ee9: a6 78                        ldx     ]shell_index      ;no steering, use movement delta from initial angle
5eeb: bd fa 42                     lda     shell_dx_hi,x     ;get velocity values from shell struct
5eee: 85 7a                        sta     ]deltax_hi        ;copy to ZP
5ef0: bd fb 42                     lda     shell_dx_lo,x
5ef3: 85 79                        sta     ]deltax_lo
5ef5: bd fc 42                     lda     shell_dy_hi,x
5ef8: 85 7c                        sta     ]deltay_hi
5efa: bd fd 42                     lda     shell_dy_lo,x
5efd: 85 7b                        sta     ]deltay_lo
5eff: 4c 1c 5f                     jmp     @Comm

5f02: ad ea 42     @DoSteering     lda     cur_gun_angle     ;update position with delta for current gun angle
5f05: 0a                           asl     A                 ;multiply by 4 to get index into movement table
5f06: 0a                           asl     A
5f07: aa                           tax
5f08: bd b8 60                     lda     shell_deltay_lo,x ;get velocity values from movement table
5f0b: 85 7b                        sta     ]deltay_lo        ;copy to ZP
5f0d: bd b9 60                     lda     shell_deltay_hi,x
5f10: 85 7c                        sta     ]deltay_hi
5f12: bd ba 60                     lda     shell_deltax_lo,x
5f15: 85 79                        sta     ]deltax_lo
5f17: bd bb 60                     lda     shell_deltax_hi,x
5f1a: 85 7a                        sta     ]deltax_hi
                   ; 
5f1c: a6 78        @Comm           ldx     ]shell_index
5f1e: bd f6 42                     lda     shell_x_hi,x      ;get position values from shell struct
5f21: 85 7d                        sta     ]xposn_hi         ;copy to ZP
5f23: bd f7 42                     lda     shell_x_lo,x
5f26: 85 7e                        sta     ]xposn_lo
5f28: bd f8 42                     lda     shell_y_hi,x
5f2b: 85 7f                        sta     ]yposn_hi
5f2d: bd f9 42                     lda     shell_y_lo,x
5f30: 85 80                        sta     ]yposn_lo
5f32: ad ad 60                     lda     is_steerable      ;get is-steerable flag
5f35: 49 01                        eor     #$01              ;invert, so 0=steerable 1=non-steerable
5f37: 0a                           asl     A                 ;multiply by 4
5f38: 0a                           asl     A
5f39: 69 08                        adc     #8                ;add 8 (carry is clear)
5f3b: 85 84                        sta     ]move_count       ;8 for steerable, 12 for non-steerable

                   ; Move the shell in multiple steps, checking for collisions at each step.  Non-
                   ; steerable shells move 50% faster than steerable shells.
5f3d: a5 7e        @MoveLoop       lda     ]xposn_lo         ;update X coordinate
5f3f: 18                           clc
5f40: 65 79                        adc     ]deltax_lo        ;positive velocity is rightward movement
5f42: 85 7e                        sta     ]xposn_lo
5f44: a5 7d                        lda     ]xposn_hi
5f46: 65 7a                        adc     ]deltax_hi
5f48: 85 7d                        sta     ]xposn_hi         ;if we're in column 0, by moving to the left edge or
5f4a: f0 6f                        beq     @DisableShell     ; wrapping around the right, disable the shell
                   ; 
5f4c: a5 80                        lda     ]yposn_lo         ;update Y coordinate
5f4e: 38                           sec
5f4f: e5 7b                        sbc     ]deltay_lo        ;positive velocity is upward movement, so subtract
5f51: 85 80                        sta     ]yposn_lo
5f53: a5 7f                        lda     ]yposn_hi
5f55: e5 7c                        sbc     ]deltay_hi
5f57: 85 7f                        sta     ]yposn_hi
5f59: c9 be                        cmp     #190              ;hit the ground (by rolling under at top)?
5f5b: b0 5e                        bcs     @DisableShell     ;yes, disable it
                   ; 
5f5d: aa                           tax
5f5e: bd 5e 4d                     lda     hires_addr_hi,x   ;get hi-res line address
5f61: 85 77                        sta     ]hires_ptr+1
5f63: bd 1e 4e                     lda     hires_addr_lo,x
5f66: 85 76                        sta     ]hires_ptr
5f68: a6 7d                        ldx     ]xposn_hi         ;get X position (0-255)
5f6a: bc de 4e                     ldy     xc_to_byte,x      ;get X position div 7 (column)
5f6d: bd f6 4f                     lda     xc_to_pixel,x     ;get X position mod 7 (bit pixel value)
5f70: 31 76                        and     (]hires_ptr),y    ;test for collision
5f72: f0 23                        beq     @NoColl           ;no collision, branch
                   ; Something is on the screen at this position.  Check for collision.
5f74: 86 85                        stx     ]saved_xreg       ;save X-reg / Y-reg
5f76: 84 86                        sty     ]saved_yreg
5f78: a6 78                        ldx     ]shell_index
5f7a: 20 18 60                     jsr     TestParaShot      ;did we hit a falling paratrooper?
5f7d: 90 3c                        bcc     @DisableShell     ;yes, disable the shell
5f7f: 20 cf 5f                     jsr     TestBombShot      ;did we hit a falling bomb?
5f82: 90 37                        bcc     @DisableShell     ;yes, disable the shell
5f84: a4 7d                        ldy     ]xposn_hi
5f86: be de 4e                     ldx     xc_to_byte,y
5f89: b9 f6 4f                     lda     xc_to_pixel,y
5f8c: a4 7f                        ldy     ]yposn_hi
5f8e: 20 8a 68                     jsr     TestFlierShot     ;did we hit a flier?
5f91: 90 28                        bcc     @DisableShell     ;yes, disable the shell
5f93: a6 85                        ldx     ]saved_xreg       ;restore X-reg / Y-reg
5f95: a4 86                        ldy     ]saved_yreg
5f97: c6 84        @NoColl         dec     ]move_count       ;have we performed all moves?
5f99: d0 a2                        bne     @MoveLoop         ;no, loop
                   ; Draw the shell at its new position.
5f9b: bd f6 4f                     lda     xc_to_pixel,x     ;get the pixel value
5f9e: 51 76                        eor     (]hires_ptr),y    ;merge it with screen contents
5fa0: 91 76                        sta     (]hires_ptr),y    ;write to screen

5fa2: a4 78                        ldy     ]shell_index      ;copy new position from ZP to shell struct
5fa4: a5 7d                        lda     ]xposn_hi
5fa6: 99 f6 42                     sta     shell_x_hi,y
5fa9: a5 7e                        lda     ]xposn_lo
5fab: 99 f7 42                     sta     shell_x_lo,y
5fae: a5 7f                        lda     ]yposn_hi
5fb0: 99 f8 42                     sta     shell_y_hi,y
5fb3: a5 80                        lda     ]yposn_lo
5fb5: 99 f9 42                     sta     shell_y_lo,y
5fb8: 4c c2 5f                     jmp     @NextShell        ;loop

5fbb: a4 78        @DisableShell   ldy     ]shell_index
5fbd: a9 00                        lda     #$00
5fbf: 99 f5 42                     sta     shell_active_flag,y ;clear the active flag

5fc2: 98           @NextShell      tya                       ;put shell index in A-reg
5fc3: 18                           clc
5fc4: 69 09                        adc     #9                ;each struct is 9 bytes
5fc6: a8                           tay
5fc7: c0 48                        cpy     #72               ;have we processed all 8?
5fc9: b0 03                        bcs     @Return           ;yes, bail
5fcb: 4c ae 5e                     jmp     @ShellLoop        ;no, loop

5fce: 60           @Return         rts

                   ; 
                   ; Tests to see if the player shot a bomb.
                   ; 
                   ; On entry:
                   ;   $7d: shell X coord (high part of 8.8 value, 0-255)
                   ;   $7f: shell Y coord (high part of 8.8 value, 0-191)
                   ; 
                   ; On exit:
                   ;   Carry clear if a collision was detected
                   ; 
                   ]deltay_x2      .var    $74    {addr/1}
                   ]shell_pix      .var    $87    {addr/1}

5fcf: a2 00        TestBombShot    ldx     #$00              ;init index into bomb arrays
5fd1: bd 49 6b     @Loop           lda     bomb_old_status,x ;get previous status
5fd4: 1d 51 6b                     ora     bomb_status,x     ;get current status
5fd7: f0 2f                        beq     @Next             ;inactive, move on
                   ; Test this entry for a collision.  We need to determine if the bomb overlaps
                   ; the shell, and if so, which byte in the bitmap is overlapping.
5fd9: a5 7f                        lda     ]yposn_hi         ;get shell Y coord
5fdb: 38                           sec
5fdc: fd 4f 6b                     sbc     bomb_old_y,x      ;compare to bomb top row Y coord
5fdf: c9 04                        cmp     #$04              ;is bomb at same line or slightly above?
5fe1: b0 25                        bcs     @Next             ;no, move on
5fe3: 0a                           asl     A                 ;double it
5fe4: 85 74                        sta     ]deltay_x2        ;this is the byte offset of the row in the bitmap
5fe6: a4 7d                        ldy     ]xposn_hi         ;get shell X coord
5fe8: b9 f6 4f                     lda     xc_to_pixel,y     ;get offset of pixel within byte (0-6)
5feb: 85 87                        sta     ]shell_pix
5fed: b9 de 4e                     lda     xc_to_byte,y      ;get byte offset (0-39)
5ff0: 38                           sec
5ff1: fd 4d 6b                     sbc     bomb_old_x_hi,x   ;compare to bomb left side X coord
5ff4: c9 02                        cmp     #$02              ;is bomb in some col or one to the left?
5ff6: b0 10                        bcs     @Next             ;no, move on
5ff8: 65 74                        adc     ]deltay_x2        ;add X offset (0/1) to row offset (0/2/4/6)
5ffa: bc 4b 6b                     ldy     bomb_old_x_lo,x   ;get bomb X low, which determines bomb image (0-6)
5ffd: 79 c7 6b                     adc     bomb_offsets,y    ;get offset of image, add it to row/col offset
6000: a8                           tay                       ;this is the offset of the colliding byte
6001: b9 ce 6b                     lda     bomb_images,y     ;get the bitmap value from that offset
6004: 25 87                        and     ]shell_pix        ;bitwise AND it with the shell pixel value
6006: d0 06                        bne     @Clash            ;if nonzero, the pixels clashed
                   ; 
6008: e8           @Next           inx
6009: e0 02                        cpx     #2                ;checked both bombs?
600b: 90 c4                        bcc     @Loop             ;not yet, loop
600d: 60                           rts                       ;return with carry set

600e: a9 00        @Clash          lda     #$00
6010: 9d 51 6b                     sta     bomb_status,x     ;mark bomb as inactive
6013: 20 1d 6b                     jsr     BombShotFx        ;create explosion
6016: 18                           clc
6017: 60                           rts                       ;return with carry clear

                   ; 
                   ; Tests to see if the player shot a falling paratrooper.
                   ; 
                   ; On entry:
                   ;   $7d: shell X coord (high part of 8.8 value, 0-255)
                   ;   $7f: shell Y coord (high part of 8.8 value, 0-191)
                   ; 
                   ; On exit:
                   ;   Carry clear if a collision was detected
                   ; 
                   ]shell_xcol     .var    $81    {addr/1}
                   ]index_tmp      .var    $82    {addr/1}
                   ]row_diff       .var    $83    {addr/1}

6018: a6 7d        TestParaShot    ldx     ]xposn_hi         ;get shell X coord (0-255)
601a: bd de 4e                     lda     xc_to_byte,x      ;get horizontal byte offset
601d: 85 81                        sta     ]shell_xcol
                   ; 
601f: a2 00                        ldx     #$00              ;init paratrooper struct index
6021: bd 6e 43     @Loop           lda     para_old_status,x ;check status
6024: f0 29                        beq     @Next             ;inactive, move on
6026: bd 6f 43                     lda     para_xpos,x       ;get paratrooper column
6029: c5 81                        cmp     ]shell_xcol       ;same as shell?
602b: d0 22                        bne     @Next             ;no, move on
602d: a5 7f                        lda     ]yposn_hi         ;get shell Y position
602f: 38                           sec
6030: fd 71 43                     sbc     para_old_ypos,x   ;compare to paratrooper Y position
6033: 30 1a                        bmi     @Next             ;if paratrooper is below shell, branch
6035: c9 0a                        cmp     #10               ;is the paratrooper's top < 10 lines above shell?
6037: b0 16                        bcs     @Next             ;no, move on
6039: a8                           tay                       ;copy row diff to Y-reg
603a: bd 6e 43                     lda     para_old_status,x ;get paratrooper status
603d: 10 04                        bpl     @WithChute        ;if falling with chute, branch
603f: c0 04                        cpy     #$04              ;is the paratrooper's top < 4 lines above shell?
6041: 90 0c                        bcc     @Next             ;no, and we have no chute to hit; move on
                   ; 
6043: 84 83        @WithChute      sty     ]row_diff
6045: b9 ae 60                     lda     para_img,y        ;get byte from paratrooper bitmap
6048: a4 7d                        ldy     ]xposn_hi         ;get shell X coord (0-255)
604a: 39 f6 4f                     and     xc_to_pixel,y     ;get pixel representation, then AND it with para
604d: d0 0a                        bne     @Clash            ;if bits are set, we collided
                   ; 
604f: 8a           @Next           txa
6050: 18                           clc
6051: 69 05                        adc     #5                ;paratrooper structs are 5 bytes
6053: aa                           tax
6054: c9 28                        cmp     #40               ;have we checked all 8?
6056: 90 c9                        bcc     @Loop             ;not yet, loop
6058: 60                           rts                       ;return with carry set

6059: 86 82        @Clash          stx     ]index_tmp
605b: a9 00                        lda     #$00              ;default to explosion type 0
605d: a4 83                        ldy     ]row_diff         ;check row diff
605f: c0 04                        cpy     #$04              ;hit in chute row?
6061: b0 02                        bcs     @HitBody          ;no, branch
6063: a9 01                        lda     #$01              ;hit chute, use explosion type 1
6065: 48           @HitBody        pha                       ;push AddShrapnel arg
6066: c9 00                        cmp     #$00              ;set sound effect... was body hit?
6068: f0 04                        beq     @HitBody1         ;yes, branch
606a: a9 03                        lda     #$03
606c: d0 02                        bne     @SetSound

606e: a9 04        @HitBody1       lda     #$04
6070: 8d d5 6c     @SetSound       sta     sound_event_num
6073: a9 00                        lda     #$00
6075: 8d d6 6c                     sta     sound_event_ctr
                   ; 
6078: bd 6f 43                     lda     para_xpos,x       ;push paratrooper coords
607b: 48                           pha
607c: a9 00                        lda     #$00
607e: 48                           pha
607f: bd 71 43                     lda     para_old_ypos,x
6082: 48                           pha
6083: a9 00                        lda     #$00              ;X velocity 0
6085: 48                           pha
6086: a9 02                        lda     #$02              ;Y velocity 2 (para was falling)
6088: 48                           pha
6089: 20 0e 51                     jsr     AddShrapnel
                   ; 
608c: a6 82                        ldx     ]index_tmp
608e: a5 83                        lda     ]row_diff         ;check which row was hit
6090: c9 04                        cmp     #$04              ;hit in the chute?
6092: b0 07                        bcs     @HitBody1         ;no, branch
6094: a9 ff                        lda     #$ff              ;mark para as free-fall without a chute
6096: 9d 6d 43                     sta     para_status,x
6099: 18                           clc
609a: 60                           rts                       ;return with carry clear

609b: a9 00        @HitBody1       lda     #$00
609d: 9d 6d 43                     sta     para_status,x     ;mark para as inactive
60a0: 86 83                        stx     ]row_diff         ;save X-reg
60a2: a9 02                        lda     #$02              ;add 2 points
60a4: a2 00                        ldx     #$00
60a6: 20 b8 5b                     jsr     IncreaseScore
60a9: a6 83                        ldx     ]row_diff         ;restore X-reg (why?)
60ab: 18                           clc
60ac: 60                           rts                       ;return with carry clear

60ad: 00           is_steerable    .dd1    $00               ;bool (0/1): are steerable shells enabled?

                   ; 
                   ; Paratrooper image.  This seems to be used exclusively for pixel-perfect
                   ; collision testing, not drawing.
                   ; 
                   vis
60ae: 1c 3e 7f 41+ para_img        .bulk   $1c,$3e,$7f,$41,$2a,$3e,$5d,$1c,$14,$22
                   ; 
                   ; Shell velocity table.  Take the gun angle, multiply by 4, and use it as an
                   ; index into the table.  Each 4-byte entry is an 8.8 fixed-point delta Y
                   ; followed by an 8.8 delta X.
                   ; 
                   ; Entries are defined for angles 1-55, but in practice only 4-52 are used.
                   ; 
                   ; The values represent a single step of about 1 pixel.  The movement routine
                   ; applies the vector 8 or 12 times per frame, testing for collisions each time.
                   ; 
60b8: 00           shell_deltay_lo .dd1    $00               ;0 (not used)
60b9: 00           shell_deltay_hi .dd1    $00
60ba: 00           shell_deltax_lo .dd1    $00
60bb: 00           shell_deltax_hi .dd1    $00
60bc: 00 00 00 01                  .bulk   $00,$00,$00,$01   ;1 (straight right)
60c0: 0f 00 00 01                  .bulk   $0f,$00,$00,$01
60c4: 1e 00 fe 00                  .bulk   $1e,$00,$fe,$00
60c8: 2c 00 fc 00                  .bulk   $2c,$00,$fc,$00
60cc: 3b 00 f9 00                  .bulk   $3b,$00,$f9,$00
60d0: 49 00 f5 00                  .bulk   $49,$00,$f5,$00
60d4: 58 00 f1 00                  .bulk   $58,$00,$f1,$00
60d8: 65 00 eb 00                  .bulk   $65,$00,$eb,$00
60dc: 73 00 e5 00                  .bulk   $73,$00,$e5,$00
60e0: 80 00 de 00                  .bulk   $80,$00,$de,$00
60e4: 8d 00 d6 00                  .bulk   $8d,$00,$d6,$00
60e8: 99 00 cd 00                  .bulk   $99,$00,$cd,$00
60ec: a5 00 c4 00                  .bulk   $a5,$00,$c4,$00
60f0: b0 00 ba 00                  .bulk   $b0,$00,$ba,$00
60f4: ba 00 b0 00                  .bulk   $ba,$00,$b0,$00
60f8: c4 00 a5 00                  .bulk   $c4,$00,$a5,$00
60fc: cd 00 99 00                  .bulk   $cd,$00,$99,$00
6100: d6 00 8d 00                  .bulk   $d6,$00,$8d,$00
6104: de 00 80 00                  .bulk   $de,$00,$80,$00
6108: e5 00 73 00                  .bulk   $e5,$00,$73,$00
610c: eb 00 65 00                  .bulk   $eb,$00,$65,$00
6110: f1 00 58 00                  .bulk   $f1,$00,$58,$00
6114: f5 00 49 00                  .bulk   $f5,$00,$49,$00   ;23
6118: f9 00 3b 00                  .bulk   $f9,$00,$3b,$00
611c: fc 00 2c 00                  .bulk   $fc,$00,$2c,$00
6120: fe 00 1e 00                  .bulk   $fe,$00,$1e,$00
6124: 00 01 0f 00                  .bulk   $00,$01,$0f,$00
6128: 00 01 00 00                  .bulk   $00,$01,$00,$00   ;28 (straight up)
612c: 00 01 f1 ff                  .bulk   $00,$01,$f1,$ff
6130: fe 00 e2 ff                  .bulk   $fe,$00,$e2,$ff
6134: fc 00 d4 ff                  .bulk   $fc,$00,$d4,$ff
6138: f9 00 c5 ff                  .bulk   $f9,$00,$c5,$ff
613c: f5 00 b7 ff                  .bulk   $f5,$00,$b7,$ff
6140: f1 00 a8 ff                  .bulk   $f1,$00,$a8,$ff
6144: eb 00 9b ff                  .bulk   $eb,$00,$9b,$ff
6148: e5 00 8d ff                  .bulk   $e5,$00,$8d,$ff
614c: de 00 80 ff                  .bulk   $de,$00,$80,$ff
6150: d6 00 73 ff                  .bulk   $d6,$00,$73,$ff
6154: cd 00 67 ff                  .bulk   $cd,$00,$67,$ff
6158: c4 00 5c ff                  .bulk   $c4,$00,$5c,$ff
615c: ba 00 50 ff                  .bulk   $ba,$00,$50,$ff
6160: b0 00 46 ff                  .bulk   $b0,$00,$46,$ff
6164: a5 00 3c ff                  .bulk   $a5,$00,$3c,$ff
6168: 99 00 33 ff                  .bulk   $99,$00,$33,$ff
616c: 8d 00 2a ff                  .bulk   $8d,$00,$2a,$ff
6170: 80 00 22 ff                  .bulk   $80,$00,$22,$ff
6174: 73 00 1b ff                  .bulk   $73,$00,$1b,$ff
6178: 65 00 15 ff                  .bulk   $65,$00,$15,$ff
617c: 58 00 0f ff                  .bulk   $58,$00,$0f,$ff
6180: 49 00 0b ff                  .bulk   $49,$00,$0b,$ff
6184: 3b 00 07 ff                  .bulk   $3b,$00,$07,$ff
6188: 2c 00 04 ff                  .bulk   $2c,$00,$04,$ff
618c: 1e 00 02 ff                  .bulk   $1e,$00,$02,$ff
6190: 0f 00 00 ff                  .bulk   $0f,$00,$00,$ff
6194: 00 00 00 ff                  .bulk   $00,$00,$00,$ff   ;55 (straight left)
6198: 00 ff                        .bulk   $00,$ff

                   ; 
                   ; Tries to create a flier.
                   ; 
                   ; This is called every 10 frames.  The flier is created if current conditions
                   ; allow it, and the RNG says it's time.
                   ; 
                   • Clear variables
                   ]tmp1           .var    $74    {addr/1}
                   ]tmp2           .var    $75    {addr/1}
                   ]rnd_direction  .var    $76    {addr/1}
                   ]lane_rnd       .var    $77    {addr/1}
                   ]flier_idx      .var    $78    {addr/1}
                   ]retry_count    .var    $79    {addr/1}

619a: ad 28 5b     TryCreateFlier  lda     sabotage_side     ;is sabotage in progress?
619d: 0d d9 42                     ora     delay_ctr         ;are we in a delay period?
61a0: f0 01                        beq     @CheckThing?      ;no, proceed
61a2: 60                           rts

61a3: ad e4 42     @CheckThing?    lda     rng_state         ;get random number (0-255)
61a6: 18                           clc                       ;(update RNG)
61a7: 69 43                        adc     #$43
61a9: 8d e4 42                     sta     rng_state
61ac: 90 03                        bcc     @NoInc
61ae: ee e5 42                     inc     rng_state+1
61b1: cd ee 42     @NoInc          cmp     flier_create_thresh ;is rnd below the threshold?
61b4: b0 13                        bcs     @Return           ;no, bail

                   ; All conditions green, look for a free slot.
61b6: a2 00                        ldx     #$00              ;init flier index
61b8: bd 45 43     @Loop           lda     flier_old_status,x ;check previous status
61bb: 1d 49 43                     ora     flier_status,x    ;check current status
61be: f0 0a                        beq     @EmptySlot        ;found empty slot, branch
61c0: 8a                           txa
61c1: 18                           clc
61c2: 69 0a                        adc     #10               ;advance to next flier (10 bytes per struct)
61c4: aa                           tax
61c5: e0 28                        cpx     #40               ;have we processed all four?
61c7: 90 ef                        bcc     @Loop             ;not yet, loop
61c9: 60           @Return         rts

61ca: 86 78        @EmptySlot      stx     ]flier_idx
61cc: ac ef 42                     ldy     flier_height_cap  ;get the height cap, 0-5
61cf: c8                           iny
61d0: 84 75                        sty     ]tmp2
61d2: a9 00                        lda     #$00
61d4: 85 74                        sta     ]tmp1             ;init to zero
61d6: ad e5 42                     lda     rng_state+1       ;get random value
61d9: a2 08                        ldx     #$08              ;repeat 8x
61db: 66 75        @Loop           ror     ]tmp2
61dd: 66 74                        ror     ]tmp1             ;...not entirely sure what this is doing, but it
61df: a8                           tay                       ;...seems to pick a random value between 0 and the
61e0: 38                           sec                       ;...height cap, inclusive
61e1: e5 74                        sbc     ]tmp1
61e3: b0 01                        bcs     @Gte
61e5: 98                           tya
61e6: ca           @Gte            dex
61e7: d0 f2                        bne     @Loop
61e9: 85 77                        sta     ]lane_rnd
                   ; 
61eb: ad e4 42                     lda     rng_state         ;get random number
61ee: 29 01                        and     #$01              ;keep only the low bit
61f0: 85 76                        sta     ]rnd_direction
61f2: ad ef 42                     lda     flier_height_cap  ;get the height cap (0-5)
61f5: 85 79                        sta     ]retry_count      ;try at most this many times to find a lane
                   ; Walk the list of fliers to see if there's already one in the lane we want to
                   ; fly in.  If there is, make sure it's moving the same direction.
61f7: a2 00        @Loop           ldx     #$00              ;init struct index
61f9: bd 49 43     @Loop1          lda     flier_status,x    ;get flier status
61fc: f0 1c                        beq     @Next             ;inactive, branch
61fe: a4 77                        ldy     ]lane_rnd         ;get our desired lane
6200: b9 77 62                     lda     flier_rows,y      ;get screen row for lane
6203: 38                           sec
6204: fd 4a 43                     sbc     flier_y,x         ;subtract flier's Y-coord
6207: c9 0b                        cmp     #11               ;is it same? (fliers are 10, lanes are 14 rows)
6209: 90 04                        bcc     @SameRow          ;yes, branch
620b: c9 f5                        cmp     #$f5              ;>= -11?
620d: 90 0b                        bcc     @Next             ;different row, move on
620f: bd 49 43     @SameRow        lda     flier_status,x    ;get flier status (which includes the direction)
6212: a8                           tay
6213: b9 81 62                     lda     flier_status_to_dir,y ;get right=$00, left=$01
6216: c5 76                        cmp     ]rnd_direction    ;does it match the direction we want?
6218: d0 46                        bne     @NextLane         ;no, try another lane
621a: 8a           @Next           txa
621b: 18                           clc
621c: 69 0a                        adc     #10               ;move to next flier (10 bytes each)
621e: aa                           tax
621f: e0 28                        cpx     #40               ;have we checked all 4?
6221: 90 d6                        bcc     @Loop1            ;not yet, loop
                   ; Configure the new flier.
6223: a6 78                        ldx     ]flier_idx
6225: a4 77                        ldy     ]lane_rnd         ;get the lane
6227: b9 77 62                     lda     flier_rows,y      ;convert to a row number
622a: 9d 4a 43                     sta     flier_y,x         ;set Y position
622d: a4 76                        ldy     ]rnd_direction    ;get desired direction
622f: f0 0f                        beq     @FlyRight
6231: a9 26                        lda     #38               ;flying left, start on right side of screen
6233: 9d 4b 43                     sta     flier_x_hi,x
6236: a9 fd                        lda     #$fd              ;set velocity to -3
6238: 9d 4d 43                     sta     flier_x_vel,x
623b: a9 04                        lda     #$04              ;set status to 4 (or 8)
623d: 4c 4c 62                     jmp     @Comm

6240: a9 fe        @FlyRight       lda     #$fe              ;flying right, start on left side of screen
6242: 9d 4b 43                     sta     flier_x_hi,x
6245: a9 03                        lda     #$03              ;set velocity to 3
6247: 9d 4d 43                     sta     flier_x_vel,x
624a: a9 02                        lda     #$02              ;set status to 2 (or 6)
624c: ac da 42     @Comm           ldy     bomber_mode_flag  ;is this a bomber?
624f: f0 03                        beq     @Comm1            ;no, branch
6251: 18                           clc
6252: 69 04                        adc     #$04              ;add 4 to status to make it a bomber
6254: 9d 49 43     @Comm1          sta     flier_status,x
6257: a9 00                        lda     #$00              ;set Y velocity and X low position to zero
6259: 9d 4e 43                     sta     flier_y_vel,x
625c: 9d 4c 43                     sta     flier_x_lo,x
625f: 60                           rts

                   ; Found a flier headed the wrong way in our desired lane.  Try the next lane.
6260: e6 77        @NextLane       inc     ]lane_rnd
6262: a5 77                        lda     ]lane_rnd
6264: cd ef 42                     cmp     flier_height_cap  ;too far down?
6267: 90 06                        bcc     @PickLane
6269: f0 04                        beq     @PickLane
626b: a9 00                        lda     #$00
626d: 85 77                        sta     ]lane_rnd         ;start over at the topmost lane
626f: c6 79        @PickLane       dec     ]retry_count      ;tried too many times?
6271: f0 03                        beq     @GiveUp           ;yes, give up
6273: 4c f7 61                     jmp     @Loop             ;no, try try again

6276: 60           @GiveUp         rts

                   ; 
                   ; Rows at which fliers can appear.  The index (0-5) is determined randomly,
                   ; clamped by the current difficulty level.  Each is 14 rows below the previous.
                   ; 
6277: 04           flier_rows      .dd1    4
6278: 12                           .dd1    18
6279: 20                           .dd1    32
627a: 2e                           .dd1    46
627b: 3c                           .dd1    60
627c: 4a                           .dd1    74
627d: 58                           .dd1    88                ;entries 6-9 not used?
627e: 66                           .dd1    102
627f: 74                           .dd1    116
6280: 82                           .dd1    130
                   ; 
                   ; This holds 0 or 1, and is indexed by flier status.  It seems to provide the
                   ; flier direction (which you could also get by shifting the status right):
                   ;   2 (heli right) = $00
                   ;   4 (heli left) = $01
                   ;   6 (bomber right) = $00
                   ;   8 (bomber left) = $01
                   ; 
                   flier_status_to_dir
6281: 01 01 00 01+                 .bulk   $01,$01,$00,$01,$01,$01,$00,$01,$01

                   ; 
                   ; Erases and redraws all fliers (helicopter or bomber), and advances their
                   ; positions.
                   ; 
                   • Clear variables
                   ]erase_bmp_ptr  .var    $76    {addr/2}
                   ]erase_flag     .var    $79    {addr/1}
                   ]draw_flag      .var    $7a    {addr/1}
                   ]draw_bmp_ptr   .var    $7b    {addr/2}
                   ]flier_index    .var    $7d    {addr/1}
                   ]erase_y        .var    $7e    {addr/1}
                   ]draw_y         .var    $7f    {addr/1}
                   ]erase_x_hi     .var    $80    {addr/1}
                   ]draw_x_hi      .var    $81    {addr/1}

628a: a2 00        UpdateFliers    ldx     #$00              ;init counts
628c: 8e 29 64                     stx     active_heli_count
628f: 8e d5 42                     stx     active_bomber_count
6292: 86 7d        @Loop           stx     ]flier_index
6294: bd 45 43                     lda     flier_old_status,x ;check old status
6297: 85 79                        sta     ]erase_flag       ;if flier existed before, erase it
6299: a8                           tay
629a: b9 1f 64                     lda     flier_addrs,y     ;get address of bitmap set
629d: 85 76                        sta     ]erase_bmp_ptr    ;set a pointer to the image to erase
629f: b9 20 64                     lda     flier_addrs+1,y
62a2: 85 77                        sta     ]erase_bmp_ptr+1
62a4: bd 49 43                     lda     flier_status,x    ;get new status
62a7: 9d 45 43                     sta     flier_old_status,x ;copy to old status
62aa: 85 7a                        sta     ]draw_flag        ;if flier still exists, draw it
62ac: f0 0c                        beq     @NowInactive      ;if it's now dead, branch
62ae: c9 06                        cmp     #$06              ;is it a bomber (status is 6 or 8)?
62b0: b0 05                        bcs     @IsBomber         ;yes, branch
62b2: ee 29 64                     inc     active_heli_count
62b5: 90 03                        bcc     @NowInactive

62b7: ee d5 42     @IsBomber       inc     active_bomber_count
62ba: a8           @NowInactive    tay                       ;copy status (0/2/4/6/8) to Y-reg
62bb: b9 1f 64                     lda     flier_addrs,y     ;get address of bitmap set
62be: 85 7b                        sta     ]draw_bmp_ptr     ;set a pointer to image to draw
62c0: b9 20 64                     lda     flier_addrs+1,y
62c3: 85 7c                        sta     ]draw_bmp_ptr+1
                   ; 
62c5: bc 48 43                     ldy     flier_old_x_lo,x  ;get old horizontal offset mod 7
62c8: b9 18 64                     lda     flier_bmp_off,y   ;get offset into bitmap set
62cb: 18                           clc
62cc: 65 76                        adc     ]erase_bmp_ptr    ;update the bitmap pointer
62ce: 85 76                        sta     ]erase_bmp_ptr
62d0: 90 02                        bcc     @NoInc
62d2: e6 77                        inc     ]erase_bmp_ptr+1
                   ; 
62d4: bc 4c 43     @NoInc          ldy     flier_x_lo,x      ;get new horizontal offset mod 7
62d7: 98                           tya
62d8: 9d 48 43                     sta     flier_old_x_lo,x  ;copy to old x lo
62db: b9 18 64                     lda     flier_bmp_off,y   ;get offset into bitmap set
62de: 18                           clc
62df: 65 7b                        adc     ]draw_bmp_ptr     ;update the bitmap pointer
62e1: 85 7b                        sta     ]draw_bmp_ptr
62e3: 90 02                        bcc     @NoInc
62e5: e6 7c                        inc     ]draw_bmp_ptr+1
                   ; 
62e7: bd 46 43     @NoInc          lda     flier_old_y,x     ;get old Y position
62ea: 85 7e                        sta     ]erase_y
62ec: bd 4a 43                     lda     flier_y,x         ;get new Y position
62ef: 9d 46 43                     sta     flier_old_y,x     ;copy to old position
62f2: 85 7f                        sta     ]draw_y
62f4: bd 47 43                     lda     flier_old_x_hi,x  ;get old X hi part (byte index, 0-39)
62f7: 85 80                        sta     ]erase_x_hi
62f9: bd 4b 43                     lda     flier_x_hi,x      ;get new X hi part
62fc: 9d 47 43                     sta     flier_old_x_hi,x  ;copy to old X hi part
62ff: 85 81                        sta     ]draw_x_hi
6301: 20 68 63                     jsr     EraseDrawFlier    ;erase and redraw the flier
                   ; Update the flier's position.
6304: a6 7d                        ldx     ]flier_index
6306: 18                           clc
6307: bd 4d 43                     lda     flier_x_vel,x     ;get X velocity
630a: 30 0f                        bmi     @Leftward         ;moving to left, branch
630c: 7d 4c 43                     adc     flier_x_lo,x      ;moving to right, add velocity
630f: c9 07                        cmp     #$07              ;did we move into a new byte?
6311: 90 13                        bcc     @Comm             ;no, branch
6313: fe 4b 43                     inc     flier_x_hi,x      ;yes, increment high part
6316: e9 07                        sbc     #$07              ;undo the addition
6318: 4c 26 63                     jmp     @Comm

631b: 7d 4c 43     @Leftward       adc     flier_x_lo,x      ;add the negative velocity value
631e: 10 06                        bpl     @Comm             ;if we didn't underflow, branch
6320: de 4b 43                     dec     flier_x_hi,x      ;decrement the high part
6323: 18                           clc
6324: 69 07                        adc     #$07              ;undo the subtraction
6326: 9d 4c 43     @Comm           sta     flier_x_lo,x      ;save updated low part
                   ; 
6329: bd 4b 43                     lda     flier_x_hi,x      ;get the high part
632c: c9 2b                        cmp     #43               ;have we moved off the right edge of the screen?
632e: 90 09                        bcc     @UpdateY          ;no, leave it be
6330: c9 fd                        cmp     #$fd              ;have we moved off the left edge of the screen?
6332: b0 05                        bcs     @UpdateY          ;no, leave it be
6334: a9 00                        lda     #$00              ;kill the flier
6336: 9d 49 43                     sta     flier_status,x
                   ; 
6339: bd 4a 43     @UpdateY        lda     flier_y,x         ;update the Y position
633c: 18                           clc                       ;(which seems to always be zero)
633d: 7d 4e 43                     adc     flier_y_vel,x
6340: 9d 4a 43                     sta     flier_y,x
                   ; Advance to next flier.
6343: 8a                           txa
6344: 18                           clc
6345: 69 0a                        adc     #10               ;flier structs are 10 bytes each
6347: aa                           tax
6348: c9 28                        cmp     #40               ;checked all 4?
634a: b0 03                        bcs     @HeliSound        ;yes, branch
634c: 4c 92 62                     jmp     @Loop             ;no, loop

                   ; Make some noise.
634f: ad 29 64     @HeliSound      lda     active_heli_count ;get number of active helicopters
6352: 0a                           asl     A                 ;multiply by 4
6353: 0a                           asl     A
6354: 8d 29 64                     sta     active_heli_count
6357: f0 0e                        beq     @Return           ;if no helicopters, bail
6359: a9 23                        lda     #35               ;make a number of clicks that changes slightly
635b: 38                           sec                       ; with the number of helicopters on-screen
635c: ed 29 64                     sbc     active_heli_count
635f: a8                           tay
6360: 2c 30 c0     @Loop           bit     SPKR              ;click speaker
6363: ea                           nop
6364: 88                           dey
6365: d0 f9                        bne     @Loop
6367: 60           @Return         rts

                   ; 
                   ; Draws and erases a single flier.  A single line is drawn and erased before
                   ; moving on to the next line.  Presumably this was done to reduce tearing.
                   ; 
                   ; Helicopters and bombers are both 4 bytes by 10 lines.
                   ; 
                   ; On entry:
                   ;   $76-77: pointer to bitmap to erase
                   ;   $79: nonzero if we need to erase
                   ;   $7a: nonzero if we need to draw
                   ;   $7b-7c: pointer to bitmap to draw
                   ;   $7e: Y-coord for erase
                   ;   $7f: Y-coord for draw
                   ;   $80: X-coord for erase (hi part)
                   ;   $81: X-coord for draw (hi part)
                   ; 
                   ]hires_ptr1     .var    $82    {addr/2}
                   ]bmp_index      .var    $84    {addr/1}
                   ]erase_data     .var    $85    {addr/4}
                   ]draw_data      .var    $89    {addr/4}
                   ]hires_ptr2     .var    $8d    {addr/2}

6368: a0 00        EraseDrawFlier  ldy     #$00
636a: b1 76        @DrawLoop       lda     (]erase_bmp_ptr),y ;get a row of data from both bitmaps
636c: 85 85                        sta     ]erase_data
636e: b1 7b                        lda     (]draw_bmp_ptr),y
6370: 85 89                        sta     ]draw_data
6372: c8                           iny
6373: b1 76                        lda     (]erase_bmp_ptr),y
6375: 85 86                        sta     ]erase_data+1
6377: b1 7b                        lda     (]draw_bmp_ptr),y
6379: 85 8a                        sta     ]draw_data+1
637b: c8                           iny
637c: b1 76                        lda     (]erase_bmp_ptr),y
637e: 85 87                        sta     ]erase_data+2
6380: b1 7b                        lda     (]draw_bmp_ptr),y
6382: 85 8b                        sta     ]draw_data+2
6384: c8                           iny
6385: b1 76                        lda     (]erase_bmp_ptr),y
6387: 85 88                        sta     ]erase_data+3
6389: b1 7b                        lda     (]draw_bmp_ptr),y
638b: 85 8c                        sta     ]draw_data+3
638d: c8                           iny
638e: 84 84                        sty     ]bmp_index        ;stash bitmap pointer
                   ; 
6390: a6 7e                        ldx     ]erase_y          ;set hi-res line pointer for erase row
6392: bd 5e 4d                     lda     hires_addr_hi,x
6395: 85 83                        sta     ]hires_ptr1+1
6397: bd 1e 4e                     lda     hires_addr_lo,x
639a: 85 82                        sta     ]hires_ptr1
639c: e6 7e                        inc     ]erase_y          ;advance for next row
639e: a6 7f                        ldx     ]draw_y           ;set hi-res line pointer for draw row
63a0: bd 5e 4d                     lda     hires_addr_hi,x   ; (which should be the same as erase unless the
63a3: 85 8e                        sta     ]hires_ptr2+1     ; flier is moving up or down)
63a5: bd 1e 4e                     lda     hires_addr_lo,x
63a8: 85 8d                        sta     ]hires_ptr2
63aa: e6 7f                        inc     ]draw_y           ;advance for next row
                   ; 
63ac: a5 79                        lda     ]erase_flag       ;do we need to erase?
63ae: f0 2d                        beq     @NoErase          ;no, branch
63b0: a4 80                        ldy     ]erase_x_hi       ;get offset into hi-res row
63b2: c0 26                        cpy     #38               ;are we at right edge?
63b4: b0 06                        bcs     @SkipE0           ;yes, clip (could skip rest of row)
63b6: a5 85                        lda     ]erase_data
63b8: 51 82                        eor     (]hires_ptr1),y
63ba: 91 82                        sta     (]hires_ptr1),y
63bc: c8           @SkipE0         iny
63bd: c0 26                        cpy     #38
63bf: b0 06                        bcs     @SkipE1
63c1: a5 86                        lda     ]erase_data+1
63c3: 51 82                        eor     (]hires_ptr1),y
63c5: 91 82                        sta     (]hires_ptr1),y
63c7: c8           @SkipE1         iny
63c8: c0 26                        cpy     #38
63ca: b0 06                        bcs     @SkipE2
63cc: a5 87                        lda     ]erase_data+2
63ce: 51 82                        eor     (]hires_ptr1),y
63d0: 91 82                        sta     (]hires_ptr1),y
63d2: c8           @SkipE2         iny
63d3: c0 26                        cpy     #38
63d5: b0 06                        bcs     @NoErase
63d7: a5 88                        lda     ]erase_data+3
63d9: 51 82                        eor     (]hires_ptr1),y
63db: 91 82                        sta     (]hires_ptr1),y
                   ; 
63dd: a5 7a        @NoErase        lda     ]draw_flag        ;do we need to draw?
63df: f0 2d                        beq     @NoDraw           ;no, branch
63e1: a4 81                        ldy     ]draw_x_hi        ;get offset into hi-res row
63e3: c0 26                        cpy     #38               ;are we at right edge?
63e5: b0 06                        bcs     @SkipDraw0        ;yes, clip (could skip rest of row)
63e7: a5 89                        lda     ]draw_data
63e9: 51 8d                        eor     (]hires_ptr2),y
63eb: 91 8d                        sta     (]hires_ptr2),y
63ed: c8           @SkipDraw0      iny
63ee: c0 26                        cpy     #38
63f0: b0 06                        bcs     @SkipDraw1
63f2: a5 8a                        lda     ]draw_data+1
63f4: 51 8d                        eor     (]hires_ptr2),y
63f6: 91 8d                        sta     (]hires_ptr2),y
63f8: c8           @SkipDraw1      iny
63f9: c0 26                        cpy     #38
63fb: b0 06                        bcs     @SkipDraw2
63fd: a5 8b                        lda     ]draw_data+2
63ff: 51 8d                        eor     (]hires_ptr2),y
6401: 91 8d                        sta     (]hires_ptr2),y
6403: c8           @SkipDraw2      iny
6404: c0 26                        cpy     #38
6406: b0 06                        bcs     @NoDraw
6408: a5 8c                        lda     ]draw_data+3
640a: 51 8d                        eor     (]hires_ptr2),y
640c: 91 8d                        sta     (]hires_ptr2),y
                   ; 
640e: a4 84        @NoDraw         ldy     ]bmp_index        ;get the index into the bitmap
6410: c0 26                        cpy     #38               ;are we at the end?
6412: b0 03                        bcs     @Return           ;yes, bail
6414: 4c 6a 63                     jmp     @DrawLoop         ;no, loop

6417: 60           @Return         rts

                   ; 
                   ; Offsets for the 7 bit-shifted images.  Each image is 4 bytes by 10 rows.
                   ; 
6418: 00 28 50 78+ flier_bmp_off   .bulk   $00,$28,$50,$78,$a0,$c8,$f0
                   ; 
                   ; Base addresses of the images for helicopters and bombers.  This table is
                   ; indexed directly by the flier status byte:
                   ;   0 - inactive
                   ;   2 - helicopter, moving right
                   ;   4 - helicopter, moving left
                   ;   6 - bomber, moving right
                   ;   8 - bomber, moving left
                   ; 
641f: 2a 64        flier_addrs     .dd2    heli_right        ;(unused)
6421: 2a 64                        .dd2    heli_right
6423: 42 65                        .dd2    heli_left
6425: 5a 66                        .dd2    bomber_right
6427: 72 67                        .dd2    bomber_left
                   active_heli_count
6429: 00                           .dd1    $00

                   ; 
                   ; Bitmaps of fliers (helicopters and bombers).  Both are the same size (4x10).
                   ; 
                   ; There are different sets for fliers moving from left to right and right to
                   ; left.  Each set has 7 shifted images.  Each of the helicopter images draws the
                   ; blades in a different position, so it impicitly animates as it moves across
                   ; the screen.
                   ; 
                   vis
642a: 00 60 7f 00+ heli_right      .bulk   $00,$60,$7f,$00,$00,$20,$00,$00,$04,$70,$00,$00,$06,$7c,$1f,$00
                                    +      $0c,$78,$30,$00,$78,$7f,$7f,$00,$78,$7f,$3f,$00,$00,$7f,$0f,$00
                                    +      $00,$10,$42,$00,$00,$7c,$3f,$00,$60,$7f,$00,$00,$00,$40,$00,$00
                                    +      $00,$60,$01,$00,$4c,$78,$3f,$00,$18,$70,$61,$00,$72,$7f,$7f,$01
                                    +      $60,$7f,$7f,$00,$00,$7e,$1f,$00,$00,$20,$04,$01,$00,$78,$7f,$00
                                    +      $00,$00,$7f,$03,$00,$00,$01,$00,$08,$40,$03,$00,$18,$70,$7f,$00
                                    +      $30,$60,$43,$01,$60,$7f,$7f,$03,$40,$7f,$7f,$01,$00,$7c,$3f,$00
                                    +      $00,$40,$08,$02,$00,$70,$7f,$01,$00,$7f,$03,$00,$00,$00,$02,$00
                                    +      $00,$01,$07,$00,$30,$60,$7f,$01,$60,$40,$07,$03,$40,$7f,$7f,$07
                                    +      $10,$7f,$7f,$03,$00,$78,$7f,$00,$00,$00,$11,$04,$00,$60,$7f,$03
                                    +      $00,$00,$7c,$0f,$00,$00,$04,$00,$00,$00,$0e,$00,$70,$40,$7f,$03
                                    +      $40,$01,$0f,$06,$00,$7f,$7f,$0f,$00,$7e,$7f,$07,$00,$70,$7f,$01
                                    +      $00,$00,$22,$08,$00,$40,$7f,$07,$00,$7c,$0f,$00,$00,$00,$08,$00
                                    +      $00,$02,$1c,$00,$40,$01,$7f,$07,$00,$03,$1e,$0c,$00,$7e,$7f,$1f
                                    +      $00,$7d,$7f,$0f,$00,$60,$7f,$03,$00,$00,$44,$10,$00,$00,$7f,$0f
                                    +      $00,$00,$70,$3f,$00,$00,$10,$00,$00,$00,$38,$00,$00,$03,$7e,$0f
                                    +      $40,$16,$3c,$18,$00,$7c,$7f,$3f,$00,$78,$7f,$1f,$00,$40,$7f,$07
                                    +      $00,$00,$08,$21,$00,$00,$7e,$1f

                   vis
6542: 7f 03 00 00+ heli_left       .bulk   $7f,$03,$00,$00,$00,$02,$00,$00,$00,$07,$10,$00,$7c,$1f,$30,$00
                                    +      $06,$0f,$18,$00,$7f,$7f,$0f,$00,$7e,$7f,$0f,$00,$78,$7f,$00,$00
                                    +      $21,$04,$00,$00,$7e,$1f,$00,$00,$00,$7c,$0f,$00,$00,$04,$00,$00
                                    +      $00,$0e,$00,$00,$78,$3f,$64,$00,$0c,$1e,$30,$00,$7e,$7f,$1f,$01
                                    +      $7c,$7f,$0f,$00,$70,$7f,$01,$00,$42,$08,$00,$00,$7c,$3f,$00,$00
                                    +      $7c,$0f,$00,$00,$00,$08,$00,$00,$00,$1c,$00,$01,$70,$7f,$40,$01
                                    +      $18,$3c,$60,$00,$7c,$7f,$3f,$00,$78,$7f,$1f,$00,$60,$7f,$03,$00
                                    +      $04,$11,$00,$00,$78,$7f,$00,$00,$00,$70,$3f,$00,$00,$10,$00,$00
                                    +      $00,$38,$20,$00,$60,$7f,$01,$03,$30,$78,$40,$01,$78,$7f,$7f,$00
                                    +      $70,$7f,$3f,$02,$40,$7f,$07,$00,$08,$22,$00,$00,$70,$7f,$01,$00
                                    +      $70,$3f,$00,$00,$00,$20,$00,$00,$00,$70,$00,$00,$40,$7f,$03,$0e
                                    +      $60,$70,$01,$03,$70,$7f,$7f,$01,$60,$7f,$7f,$00,$00,$7f,$0f,$00
                                    +      $10,$44,$00,$00,$60,$7f,$03,$00,$00,$40,$7f,$01,$00,$40,$00,$00
                                    +      $00,$60,$01,$02,$00,$7f,$07,$0c,$40,$61,$03,$06,$60,$7f,$7f,$03
                                    +      $40,$7f,$7f,$05,$00,$7e,$1f,$00,$20,$08,$01,$00,$40,$7f,$07,$00
                                    +      $40,$7f,$01,$00,$00,$00,$01,$00,$00,$40,$03,$00,$00,$7e,$0f,$18
                                    +      $00,$43,$07,$2d,$40,$7f,$7f,$07,$00,$7f,$7f,$03,$00,$7c,$3f,$00
                                    +      $40,$10,$02,$00,$00,$7f,$0f,$00

                   vis
665a: 00 00 00 00+ bomber_right    .bulk   $00,$00,$00,$00,$1c,$00,$00,$00,$3c,$00,$00,$00,$7c,$00,$00,$00
                                    +      $44,$7f,$0f,$00,$7c,$7f,$3b,$00,$60,$01,$7f,$00,$74,$7f,$3f,$00
                                    +      $70,$7f,$0f,$00,$00,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00,$00
                                    +      $78,$00,$00,$00,$78,$01,$00,$00,$08,$7f,$1f,$00,$78,$7f,$77,$00
                                    +      $60,$03,$7e,$01,$54,$7f,$7f,$00,$40,$7f,$1f,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$70,$00,$00,$00,$70,$01,$00,$00,$70,$03,$00,$00
                                    +      $10,$7e,$3f,$00,$70,$7f,$6b,$01,$10,$07,$7c,$03,$40,$7f,$7f,$01
                                    +      $10,$7f,$3f,$00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$01,$00,$00
                                    +      $60,$03,$00,$00,$60,$07,$00,$00,$20,$7c,$7f,$00,$60,$7f,$5f,$03
                                    +      $00,$0f,$78,$07,$08,$7e,$7f,$03,$40,$7e,$7f,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$40,$03,$00,$00,$40,$07,$00,$00,$40,$0f,$00,$00
                                    +      $40,$78,$7f,$01,$40,$7f,$3f,$07,$00,$1d,$70,$0f,$40,$7c,$7f,$07
                                    +      $00,$7d,$7f,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$07,$00,$00
                                    +      $00,$0f,$00,$00,$00,$1f,$00,$00,$00,$71,$7f,$03,$00,$7f,$7f,$0e
                                    +      $00,$38,$60,$1f,$40,$7a,$7f,$0f,$00,$7c,$7f,$03,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$0e,$00,$00,$00,$1e,$00,$00,$00,$3e,$00,$00
                                    +      $00,$62,$7f,$07,$00,$7e,$7f,$1d,$00,$74,$40,$3f,$40,$70,$7f,$1f
                                    +      $00,$72,$7f,$07,$00,$00,$00,$00

                   vis
6772: 00 00 00 00+ bomber_left     .bulk   $00,$00,$00,$00,$00,$00,$1c,$00,$00,$00,$1e,$00,$00,$00,$1f,$00
                                    +      $78,$7f,$11,$00,$6e,$7f,$1f,$00,$7f,$40,$03,$00,$7e,$7f,$17,$00
                                    +      $78,$7f,$07,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$38,$00
                                    +      $00,$00,$3c,$00,$00,$00,$3e,$00,$70,$7f,$23,$00,$5c,$7f,$3f,$00
                                    +      $7e,$01,$0f,$00,$7c,$7f,$57,$00,$70,$7f,$07,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$70,$00,$00,$00,$78,$00,$00,$00,$7c,$00
                                    +      $60,$7f,$47,$00,$38,$7f,$7f,$00,$7c,$03,$4e,$00,$78,$7f,$1f,$00
                                    +      $60,$7f,$4f,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$01
                                    +      $00,$00,$70,$01,$00,$00,$78,$01,$40,$7f,$0f,$01,$70,$7e,$7f,$01
                                    +      $78,$07,$3c,$00,$70,$7f,$1f,$04,$40,$7f,$5f,$00,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$40,$03,$00,$00,$60,$03,$00,$00,$70,$03
                                    +      $00,$7f,$1f,$02,$60,$7d,$7f,$03,$70,$0f,$38,$01,$60,$7f,$3f,$02
                                    +      $00,$7f,$3f,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$07
                                    +      $00,$00,$40,$07,$00,$00,$60,$07,$00,$7e,$3f,$04,$40,$7b,$7f,$07
                                    +      $60,$1f,$70,$00,$40,$7f,$7f,$0a,$00,$7e,$7f,$01,$00,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00,$00,$0e,$00,$00,$00,$0f,$00,$00,$40,$0f
                                    +      $00,$7c,$7f,$08,$00,$77,$7f,$0f,$40,$3f,$60,$05,$00,$7f,$7f,$21
                                    +      $00,$7c,$7f,$09,$00,$00,$00,$00

                   ; 
                   ; Tests for a collision between a shell and a flier.
                   ; 
                   ; On entry:
                   ;   A-reg: byte with screen pixel value (one bit set)
                   ;   X-reg: X coordinate, high part (div 7, 0-39)
                   ;   Y-reg: Y coordinate (0-191)
                   ; 
                   ; On exit:
                   ;   Carry clear if collision detected
                   ; 
                   • Clear variables
                   ]tmp            .var    $74    {addr/2}

688a: 8d 23 69     TestFlierShot   sta     @flier_c_pixel    ;save arguments
688d: 8e 24 69                     stx     @flier_c_x_hi
6890: 8c 25 69                     sty     @flier_c_y
6893: a2 00                        ldx     #$00              ;init flier index
6895: 8e 26 69     @Loop           stx     @flier_c_idx
6898: bd 45 43                     lda     flier_old_status,x ;check flier previous status
689b: f0 4f                        beq     @Next             ;inactive, move on
689d: bd 49 43                     lda     flier_status,x    ;check flier current status
68a0: f0 4a                        beq     @Next             ;inactive, move on
68a2: 38                           sec
68a3: ad 24 69                     lda     @flier_c_x_hi     ;get shell column
68a6: fd 47 43                     sbc     flier_old_x_hi,x  ;subtract leftmost flier column
68a9: c9 04                        cmp     #4                ;is flier in same column, or at most 3 to the left?
68ab: b0 3f                        bcs     @Next             ;no, move on
68ad: a8                           tay                       ;copy column diff to Y-reg
68ae: 38                           sec
68af: ad 25 69                     lda     @flier_c_y        ;get shell row
68b2: fd 46 43                     sbc     flier_old_y,x     ;subtract top row of flier
68b5: c9 0a                        cmp     #10               ;is flier at same row, or at most 9 above?
68b7: b0 33                        bcs     @Next             ;no, move on
68b9: 0a                           asl     A                 ;multiply row difference by 4
68ba: 0a                           asl     A
68bb: 84 74                        sty     ]tmp              ;store column diff
68bd: 65 74                        adc     ]tmp              ;add 4x row diff
68bf: 8d 27 69                     sta     @flier_c_off      ;save as offset into flier bitmap
                   ; 
                   ]bmp_ptr        .var    $74    {addr/2}

68c2: bc 48 43                     ldy     flier_old_x_lo,x  ;get low part of horizontal position (0-6)
68c5: b9 18 64                     lda     flier_bmp_off,y   ;get offset into flier bitmap set
68c8: bc 45 43                     ldy     flier_old_status,x ;use flier status (2/4/6/8) as index
68cb: 18                           clc
68cc: 79 1f 64                     adc     flier_addrs,y     ;add base address for this type of flier
68cf: 85 74                        sta     ]bmp_ptr          ;set pointer
68d1: b9 20 64                     lda     flier_addrs+1,y
68d4: 85 75                        sta     ]bmp_ptr+1
68d6: 90 02                        bcc     @NoInc
68d8: e6 75                        inc     ]bmp_ptr+1
                   ; 
68da: ac 27 69     @NoInc          ldy     @flier_c_off      ;get offset into flier bitmap
68dd: b1 74                        lda     (]bmp_ptr),y      ;get bitmap data for conflicting byte
68df: 2d 23 69                     and     @flier_c_pixel    ;bitwise AND it with the shell pixel
68e2: f0 08                        beq     @Next             ;if they didn't overlap, move on
68e4: a9 00                        lda     #$00
68e6: 9d 49 43                     sta     flier_status,x    ;mark flier as inactive
68e9: 4c f8 68                     jmp     @FlierHit         ;branch to blow it up

68ec: ad 26 69     @Next           lda     @flier_c_idx
68ef: 18                           clc
68f0: 69 0a                        adc     #10               ;advance to next flier entry (10 bytes each)
68f2: aa                           tax
68f3: c9 28                        cmp     #40               ;have we processed all 4 fliers?
68f5: 90 9e                        bcc     @Loop             ;not yet, loop
68f7: 60                           rts                       ;return with carry set

68f8: a9 02        @FlierHit       lda     #$02              ;flier explosion
68fa: 48                           pha
68fb: bd 47 43                     lda     flier_old_x_hi,x
68fe: 48                           pha
68ff: bd 48 43                     lda     flier_old_x_lo,x
6902: 48                           pha
6903: bd 46 43                     lda     flier_old_y,x
6906: 48                           pha
6907: bd 4d 43                     lda     flier_x_vel,x
690a: 48                           pha
690b: bd 4e 43                     lda     flier_y_vel,x
690e: 48                           pha
690f: 20 0e 51                     jsr     AddShrapnel
6912: a9 08                        lda     #$08              ;set sound effect
6914: 8d d5 6c                     sta     sound_event_num
6917: a9 05                        lda     #$05              ;add 5 points
6919: a2 00                        ldx     #$00
691b: 8e d6 6c                     stx     sound_event_ctr
691e: 20 b8 5b                     jsr     IncreaseScore
6921: 18                           clc
6922: 60                           rts                       ;return with carry clear

6923: 00           @flier_c_pixel  .dd1    $00
6924: 00           @flier_c_x_hi   .dd1    $00
6925: 00           @flier_c_y      .dd1    $00
6926: 00           @flier_c_idx    .dd1    $00
6927: 00           @flier_c_off    .dd1    $00

                   ; 
                   ; Try to drop a bomb.  If a bomb can be dropped, it will be.
                   ; 
                   ; This is called every 21 frames.  If a bomber doesn't drop a bomb, it's likely
                   ; because it was outside the zone when this was called.
                   ; 
                   • Clear variables
                   ]flier_index    .var    $74    {addr/1}
                   ]bomb_index     .var    $76    {addr/1}
                   ]x_hi_minus5    .var    $77    {addr/1}
                   ]x_hi_plus8     .var    $78    {addr/1}

6928: ad da 42     TryDropBomb     lda     bomber_mode_flag  ;are we in bomber mode?
692b: f0 0a                        beq     @Return           ;no, bail
692d: ad d9 42                     lda     delay_ctr         ;are we in a no-action delay mode?
6930: d0 05                        bne     @Return           ;yes, bail
6932: ad db 42                     lda     bomb_drop_ctr     ;do we need to drop more bombs?
6935: d0 01                        bne     @DoDrop           ;yes, branch
6937: 60           @Return         rts

                   ; Find an empty bomb slot.
6938: a2 02        @DoDrop         ldx     #$02
693a: ca           @Loop           dex
693b: 30 78                        bmi     @Return           ;no empty slots; bail
693d: bd 51 6b                     lda     bomb_status,x     ;is bomb currently or previously active?
6940: 1d 49 6b                     ora     bomb_old_status,x
6943: d0 f5                        bne     @Loop             ;yes, no good; check next slot
                   ; We have a slot.  See if the flier is ready to drop.
6945: 86 76                        stx     ]bomb_index       ;save the slot number
6947: a0 00                        ldy     #$00              ;flier index
6949: b9 45 43     @Loop           lda     flier_old_status,y ;is flier active?
694c: f0 5e                        beq     @Next             ;no, move on
694e: b9 4d 43                     lda     flier_x_vel,y     ;get velocity
6951: 30 0f                        bmi     @FlyLeft          ;moving to left, branch
6953: b9 47 43                     lda     flier_old_x_hi,y  ;check horizontal position
6956: c9 04                        cmp     #4                ;is the column >= 4 and < 8?
6958: 90 52                        bcc     @Next
695a: c9 08                        cmp     #8
695c: b0 4e                        bcs     @Next
695e: 69 03                        adc     #$03              ;in the zone; add 3 to column (why?)
6960: 90 0d                        bcc     @TryDrop          ;(always)
6962: b9 47 43     @FlyLeft        lda     flier_old_x_hi,y  ;check horizontal position
6965: c9 1c                        cmp     #28               ;is the column >= 28 and < 32?
6967: 90 43                        bcc     @Next
6969: c9 20                        cmp     #32
696b: b0 3f                        bcs     @Next
696d: e9 00                        sbc     #$00              ;in the zone; subtract 1 from column
                   ; We're in the right place, check for reasons not to drop.
696f: a6 76        @TryDrop        ldx     ]bomb_index       ;get the index of the slot we found
6971: 9d 55 6b                     sta     bomb_x_hi,x       ;set the bomb's horizontal position
6974: 18                           clc
6975: 69 08                        adc     #8                ;add 8
6977: 85 78                        sta     ]x_hi_plus8       ;stash it
6979: 38                           sec
697a: e9 0d                        sbc     #13               ;subtract 13
697c: 85 77                        sta     ]x_hi_minus5      ;stash it
697e: b9 46 43                     lda     flier_old_y,y     ;get Y position
6981: cd 77 62                     cmp     flier_rows        ;top row?
6984: d0 30                        bne     @DropBomb         ;no, nothing should be below us; drop bomb
                   ; Don't drop a bomb on a bomber.
6986: a2 00                        ldx     #$00              ;walk through list of fliers
6988: 86 74        @CheckLoop      stx     ]flier_index
698a: c4 74                        cpy     ]flier_index      ;is this ourselves?
698c: f0 13                        beq     @Next1            ;yes, move on
698e: bd 46 43                     lda     flier_old_y,x     ;get flier's Y position
6991: cd 77 62                     cmp     flier_rows        ;top row?
6994: f0 0b                        beq     @Next1            ;yes, no worries
6996: bd 47 43                     lda     flier_old_x_hi,x  ;get flier's X position
6999: c5 77                        cmp     ]x_hi_minus5      ;is it in the -5/+8 bracket?
699b: 90 04                        bcc     @Next1
699d: c5 78                        cmp     ]x_hi_plus8
699f: 90 0b                        bcc     @Next             ;in the bracket, DO NOT DROP
69a1: 8a           @Next1          txa
69a2: 18                           clc
69a3: 69 0a                        adc     #10               ;advance to next entry (10 bytes each)
69a5: aa                           tax
69a6: e0 28                        cpx     #40               ;have we processed all 4?
69a8: 90 de                        bcc     @CheckLoop        ;not yet, loop
69aa: b0 0a                        bcs     @DropBomb         ;(always)

69ac: 98           @Next           tya
69ad: 18                           clc
69ae: 69 0a                        adc     #10               ;10 bytes per flier
69b0: a8                           tay
69b1: c0 28                        cpy     #40               ;checked all 4?
69b3: 90 94                        bcc     @Loop             ;not yet, branch
69b5: 60           @Return         rts

                   ; Drop a bomb.  Flier index is in Y-reg.
69b6: a6 76        @DropBomb       ldx     ]bomb_index       ;get bomb index in X-reg
69b8: b9 4d 43                     lda     flier_x_vel,y     ;get the bomber speed; only care about left/right
69bb: 9d 51 6b                     sta     bomb_status,x     ;copy to bomb status
69be: b9 48 43                     lda     flier_old_x_lo,y  ;get bomber X position within byte
69c1: 9d 53 6b                     sta     bomb_x_lo,x       ;copy to bomb
69c4: b9 46 43                     lda     flier_old_y,y     ;get bomber Y position
69c7: 18                           clc
69c8: 69 0b                        adc     #11               ;bomber is 10 rows high, start below that
69ca: 9d 57 6b                     sta     bomb_y,x
69cd: a9 00                        lda     #$00              ;set initial velocity index
69cf: 9d 59 6b                     sta     bomb_vel_idx,x
69d2: ce db 42                     dec     bomb_drop_ctr     ;reduce count of available bomb slots
69d5: 60                           rts

                   ; 
                   ; Updates bomb movement.  Redraws the bomb, testing for collisions with shells
                   ; while doing so.  Tests for impact on gun.
                   ; 
                   ; Sets the active bomb count.
                   ; 
                   • Clear variables
                   ]bomb_index     .var    $76    {addr/1}
                   ]bomb_row       .var    $77    {addr/1}
                   ]draw_flag      .var    $7f    {addr/1}
                   ]draw_coll_flag .var    $81    {addr/1}

69d6: a2 00        UpdateBombs     ldx     #$00
69d8: 8e d7 6c                     stx     active_bomb_count ;reset count of falling bombs
69db: 86 7f                        stx     ]draw_flag
69dd: 86 76        @Loop           stx     ]bomb_index
69df: a9 00                        lda     #$00
69e1: 85 81                        sta     ]draw_coll_flag   ;clear gfx collision flag
69e3: bd 49 6b                     lda     bomb_old_status,x ;was this bomb active before?
69e6: f0 11                        beq     @SkipErase        ;no, skip erase
69e8: bd 4f 6b                     lda     bomb_old_y,x
69eb: 85 77                        sta     ]bomb_row
69ed: bc 4d 6b                     ldy     bomb_old_x_hi,x
69f0: bd 4b 6b                     lda     bomb_old_x_lo,x
69f3: aa                           tax
69f4: 20 87 6a                     jsr     DrawBomb          ;erase bomb
69f7: a6 76                        ldx     ]bomb_index
69f9: bd 51 6b     @SkipErase      lda     bomb_status,x     ;get current status
69fc: 9d 49 6b                     sta     bomb_old_status,x ;copy to old status
69ff: f0 7d                        beq     @Next             ;if inactive, move on
                   ; 
6a01: e6 7f                        inc     ]draw_flag        ;this will be a draw operation
6a03: bd 57 6b                     lda     bomb_y,x
6a06: 85 77                        sta     ]bomb_row
6a08: bc 55 6b                     ldy     bomb_x_hi,x
6a0b: bd 53 6b                     lda     bomb_x_lo,x
6a0e: aa                           tax
6a0f: 20 87 6a                     jsr     DrawBomb          ;draw bomb
6a12: ee d7 6c                     inc     active_bomb_count ;keep count of active bombs (for sound code)
6a15: c6 7f                        dec     ]draw_flag        ;reset draw flag to zero
                   ; Update the bomb's position for the next frame.
6a17: a6 76                        ldx     ]bomb_index
6a19: bc 59 6b                     ldy     bomb_vel_idx,x    ;get bomb velocity
6a1c: bd 55 6b                     lda     bomb_x_hi,x       ;copy current posn to old posn
6a1f: 9d 4d 6b                     sta     bomb_old_x_hi,x
6a22: bd 51 6b                     lda     bomb_status,x     ;check bomb direction
6a25: 30 16                        bmi     @ToLeft           ;moving to left, branch
6a27: bd 53 6b                     lda     bomb_x_lo,x       ;get current position
6a2a: 9d 4b 6b                     sta     bomb_old_x_lo,x   ;save as old position
6a2d: 18                           clc
6a2e: 79 5b 6b                     adc     bomb_dx,y         ;add X velocity value to position
6a31: c9 07                        cmp     #$07              ;did we move into the next byte?
6a33: 90 19                        bcc     @NoOvfl           ;no, branch
6a35: e9 07                        sbc     #$07              ;undo the addition
6a37: fe 55 6b                     inc     bomb_x_hi,x       ;increment the high part (hi-res byte)
6a3a: 4c 4e 6a                     jmp     @NoOvfl

6a3d: bd 53 6b     @ToLeft         lda     bomb_x_lo,x       ;get current position
6a40: 9d 4b 6b                     sta     bomb_old_x_lo,x   ;save as old position
6a43: 38                           sec
6a44: f9 5b 6b                     sbc     bomb_dx,y         ;add X velocity value to position
6a47: b0 05                        bcs     @NoOvfl           ;if we didn't underflow, branch
6a49: 69 07                        adc     #$07              ;undo subtraction
6a4b: de 55 6b                     dec     bomb_x_hi,x       ;decrement the high part (hi-res byte)
6a4e: 9d 53 6b     @NoOvfl         sta     bomb_x_lo,x
                   ; 
6a51: fe 59 6b                     inc     bomb_vel_idx,x    ;increase the velocity index (cur val still in Y-reg)
6a54: bd 57 6b                     lda     bomb_y,x          ;get current position
6a57: 9d 4f 6b                     sta     bomb_old_y,x      ;save as old position
6a5a: 18                           clc
6a5b: 79 91 6b                     adc     bomb_dy,y         ;add Y velocity value to position
6a5e: 9d 57 6b                     sta     bomb_y,x
6a61: c9 9b                        cmp     #155              ;have we reached the level of the gun turret?
6a63: 90 19                        bcc     @Next             ;no, branch
6a65: a4 81                        ldy     ]draw_coll_flag   ;yes; did we intersect with something while drawing?
6a67: d0 04                        bne     @HitGun           ;yes, blow up
6a69: c9 ac                        cmp     #172              ;have we reached the level of the gun base?
6a6b: 90 11                        bcc     @Next             ;not yet, branch
                   ; The bomb hit the player's gun.  Explode.
6a6d: a9 00        @HitGun         lda     #$00
6a6f: 9d 51 6b                     sta     bomb_status,x     ;disable the bomb
6a72: a6 76                        ldx     ]bomb_index
6a74: 20 2f 6b                     jsr     AddBombShrapnel   ;create the explosion
6a77: a9 ff                        lda     #$ff              ;signal the main loop that we've been destroyed
6a79: 8d e8 42                     sta     exploding_gun_ctr
6a7c: a6 76                        ldx     ]bomb_index       ;restore bomb index in X-reg
                   ; 
6a7e: e8           @Next           inx                       ;increment bomb index
6a7f: e0 02                        cpx     #$02              ;reached the end of the list?
6a81: b0 03                        bcs     @Return           ;yes, bail
6a83: 4c dd 69                     jmp     @Loop             ;no, loop

6a86: 60           @Return         rts

                   ; 
                   ; Draws (or erases) a falling bomb, and tests for a collision with shells when
                   ; drawing.  Each image is 2 bytes by 4 rows.
                   ; 
                   ; On entry:
                   ;   X-reg: horizontal screen offset mod 7 (0-6)
                   ;   Y-reg: horizontal screen offset / 7 (0-40)
                   ;   $76: bomb index (0-1)
                   ;   $77: top row (0-191)
                   ;   $7f: draw flag (0=erase, 1=draw)
                   ; 
                   ; On exit:
                   ;   $81: 0 if no collisions, 1 if an overlap was detected
                   ; 
                   ]bitmap_offset  .var    $78    {addr/1}
                   ]row_ctr        .var    $79    {addr/1}
                   ]tmp_saved_x    .var    $7a    {addr/1}
                   ]hires_row_off  .var    $7b    {addr/1}
                   ]img_byte       .var    $7c    {addr/1}
                   ]hires_ptr      .var    $7d    {addr/2}

6a87: a9 04        DrawBomb        lda     #4                ;number of rows in bomb bitmap
6a89: 85 79                        sta     ]row_ctr
6a8b: bd c7 6b                     lda     bomb_offsets,x    ;get offset to shifted bitmap
6a8e: 85 78                        sta     ]bitmap_offset
6a90: a6 77        @Loop           ldx     ]bomb_row         ;get top row
6a92: bd 1e 4e                     lda     hires_addr_lo,x   ;use it to get the hi-res line address
6a95: 85 7d                        sta     ]hires_ptr
6a97: bd 5e 4d                     lda     hires_addr_hi,x
6a9a: 85 7e                        sta     ]hires_ptr+1
6a9c: e6 77                        inc     ]bomb_row         ;move down one row
6a9e: a6 78                        ldx     ]bitmap_offset
6aa0: bd ce 6b                     lda     bomb_images,x     ;get first byte on this row from bitmap
6aa3: 51 7d                        eor     (]hires_ptr),y    ;draw on screen
6aa5: 91 7d                        sta     (]hires_ptr),y
6aa7: dd ce 6b                     cmp     bomb_images,x     ;did we draw on top of something?
6aaa: f0 03                        beq     @NoColl1          ;no, branch
6aac: 20 c9 6a                     jsr     @CheckColl1       ;yes, do detailed check
6aaf: c8           @NoColl1        iny                       ;move one byte right
6ab0: bd cf 6b                     lda     bomb_images+1,x   ;get second byte on this row from bitmap
6ab3: 51 7d                        eor     (]hires_ptr),y    ;draw on screen
6ab5: 91 7d                        sta     (]hires_ptr),y
6ab7: dd cf 6b                     cmp     bomb_images+1,x   ;did we draw on top of something?
6aba: f0 03                        beq     @NoColl2          ;no, branch
6abc: 20 cf 6a                     jsr     @CheckColl2       ;yes, do detailed check
6abf: 88           @NoColl2        dey                       ;move back, one byte left
6ac0: e8                           inx                       ;advance bitmap pointer to the next row
6ac1: e8                           inx
6ac2: 86 78                        stx     ]bitmap_offset
6ac4: c6 79                        dec     ]row_ctr          ;are we done?
6ac6: d0 c8                        bne     @Loop             ;no, loop
6ac8: 60                           rts

                   ; We noticed that the bomb wasn't being drawn on a black background, so we want
                   ; to do a more detailed test for a collision with a shell.  Shells are drawn as
                   ; a single pixel, so we test each shell to see if it's being drawn in the same
                   ; byte, and then we use the bomb bitmap as a mask to test for overlap.
                   ; 
                   ; At this point: X-reg holds index into bitmap row, Y-reg holds byte offset into
                   ; hi-res line.
6ac9: bd ce 6b     @CheckColl1     lda     bomb_images,x     ;get first byte from bitmap
6acc: 4c d2 6a                     jmp     @CheckComm

6acf: bd cf 6b     @CheckColl2     lda     bomb_images+1,x   ;get second byte from bitmap
6ad2: 85 7c        @CheckComm      sta     ]img_byte
6ad4: a5 7f                        lda     ]draw_flag        ;is this a draw operation?
6ad6: f0 44                        beq     @Return           ;no, this is an erase operation; bail
6ad8: a9 01                        lda     #$01
6ada: 85 81                        sta     ]draw_coll_flag
6adc: 86 7a                        stx     ]tmp_saved_x      ;preserve X-reg
6ade: 84 7b                        sty     ]hires_row_off    ;save offset into hi-res row
                   ; Walk through list of shells, looking for collision with our bomb.
                   ; 
                   ; This test appears to be broken, and doesn't seem to detect the strike in
                   ; practice.  The routine at $5fcf, called while updating the shells, is what
                   ; usually detects a shell hitting a bomb.
                   ; 
                   ; There is value in having both sets of tests, since the previous test was done
                   ; after moving the shell but before moving the bomb, and this test happens after
                   ; moving the bomb.
6ae0: a2 00                        ldx     #$00
6ae2: bc f5 42     @Loop           ldy     shell_active_flag,x ;check shell status
6ae5: f0 28                        beq     @NextShell        ;inactive, move on
6ae7: bc f8 42                     ldy     shell_y_hi,x      ;check shell Y position
6aea: c4 77                        cpy     ]bomb_row         ;is it on this row?
6aec: d0 21                        bne     @NextShell        ;no, move on
6aee: bc f6 42                     ldy     shell_x_hi,x      ;get the shell's X position
6af1: b9 de 4e                     lda     xc_to_byte,y      ;get byte within row (coord / 7)
6af4: c5 7b                        cmp     ]hires_row_off    ;is it in this byte?
6af6: d0 17                        bne     @NextShell        ;no, move on
6af8: b9 f6 4f                     lda     xc_to_pixel,y     ;get shell pixel value
6afb: 25 7c                        and     ]img_byte         ;mask off non-bomb bits
6afd: f0 10                        beq     @NextShell        ;doesn't overlap bomb image, move on
                   ; Collision.  Destroy the bomb and the shell.
6aff: a9 00                        lda     #$00
6b01: 9d 1d 43                     sta     shell_active_flag+40,x ;bug? writing into middle of shell state
6b04: a6 76                        ldx     ]bomb_index
6b06: 9d 51 6b                     sta     bomb_status,x     ;set bomb status to zero
6b09: 20 1d 6b                     jsr     BombShotFx
6b0c: 4c 18 6b                     jmp     @Bail

                   ; This appears to be treating shell structures as 5 bytes each, but the data
                   ; structure uses 9 bytes.  So this would seem to be broken for everything but
                   ; the first shell?
6b0f: 8a           @NextShell      txa
6b10: 18                           clc
6b11: 69 05                        adc     #5                ;bug? should be 9?
6b13: aa                           tax
6b14: e0 28                        cpx     #40               ;bug? should be 72?
6b16: 90 ca                        bcc     @Loop
6b18: a4 7b        @Bail           ldy     ]hires_row_off    ;restore X-reg/Y-reg
6b1a: a6 7a                        ldx     ]tmp_saved_x
6b1c: 60           @Return         rts

                   ; 
                   ; Handles collision between shell and bomb by generating an explosion effect and
                   ; increasing the score.
                   ; 
                   ; On entry:
                   ;   X-reg: bomb index (0-1)
                   ; 
6b1d: 20 2f 6b     BombShotFx      jsr     AddBombShrapnel   ;draw explosion effect
6b20: a9 06                        lda     #$06              ;set sound effect
6b22: 8d d5 6c                     sta     sound_event_num
6b25: a9 19                        lda     #25               ;increase score by 25 points
6b27: a2 00                        ldx     #0
6b29: 8e d6 6c                     stx     sound_event_ctr
6b2c: 4c b8 5b                     jmp     IncreaseScore

                   ; 
                   ; Adds shrapnel for an exploding bomb.
                   ; 
                   ; On entry:
                   ;   X-reg: bomb index (0-1)
                   ; 
6b2f: a9 03        AddBombShrapnel lda     #3                ;type 3
6b31: 48                           pha
6b32: bd 55 6b                     lda     bomb_x_hi,x
6b35: 48                           pha
6b36: bd 53 6b                     lda     bomb_x_lo,x
6b39: 18                           clc
6b3a: 69 03                        adc     #$03              ;add 3 to X position (why?)
6b3c: 48                           pha
6b3d: bd 57 6b                     lda     bomb_y,x
6b40: 48                           pha
6b41: a9 00                        lda     #$00
6b43: 48                           pha
6b44: 48                           pha
6b45: 20 0e 51                     jsr     AddShrapnel
6b48: 60                           rts

                   ; 
                   ; Bomb state.  There can be two active bombs.
                   ; 
                   ; This uses parallel arrays, rather than a multi-byte struct.
                   ; 
6b49: 00           bomb_old_status .dd1    $00               ;0=inactive, <0=moving left, >0=moving right
6b4a: 00                           .dd1    $00               ; (same for second bomb)
6b4b: 00           bomb_old_x_lo   .dd1    $00               ;previous X position, mod 7
6b4c: 00                           .dd1    $00
6b4d: 00           bomb_old_x_hi   .dd1    $00               ;previous X position, div 7 (byte offset)
6b4e: 00                           .dd1    $00
6b4f: 00           bomb_old_y      .dd1    $00               ;previous Y position
6b50: 00                           .dd1    $00
6b51: 00           bomb_status     .dd1    $00               ;0=inactive, <0=moving left, >0=moving right
6b52: 00                           .dd1    $00
6b53: 00           bomb_x_lo       .dd1    $00               ;current X position, mod 7
6b54: 00                           .dd1    $00
6b55: 00           bomb_x_hi       .dd1    $00               ;current X position, div 7
6b56: 00                           .dd1    $00
6b57: 00           bomb_y          .dd1    $00               ;current Y position
6b58: 00                           .dd1    $00
6b59: 00           bomb_vel_idx    .dd1    $00               ;bomb velocity index, into tables below
6b5a: 00                           .dd1    $00

                   ; Ballistic curve tables, for falling bombs.
6b5b: 03 02 03 03+ bomb_dx         .bulk   $03,$02,$03,$03,$03,$03,$02,$03,$02,$03,$02,$02,$03,$02,$02,$02
                                    +      $02,$02,$02,$02,$01,$02,$02,$01,$02,$01,$02,$01,$01,$01,$01,$02
                                    +      $01,$00,$01,$01,$01,$01,$00,$01,$00,$01,$00,$00,$01,$00,$00,$00
                                    +      $00,$00,$00,$00,$00,$00
6b91: 01 01 01 01+ bomb_dy         .bulk   $01,$01,$01,$01,$01,$02,$01,$02,$02,$01,$02,$02,$02,$02,$03,$02
                                    +      $02,$03,$02,$03,$03,$03,$03,$03,$03,$03,$03,$04,$03,$04,$03,$04
                                    +      $04,$04,$04,$04,$04,$04,$04,$05,$04,$05,$05,$04,$05,$05,$05,$05
                                    +      $05,$05,$05,$05,$05,$05
                   ; 
                   ; Offsets to bomb images for a given horizontal pixel position.
                   ; 
6bc7: 00 08 10 18+ bomb_offsets    .bulk   $00,$08,$10,$18,$20,$28,$30

                   ; 
                   ; Bomb images.  Each image is 2 bytes wide by 4 rows high (8 bytes).
                   ; 
                   vis
6bce: b8 80 fc 80+ bomb_images     .bulk   $b8,$80,$fc,$80,$fc,$80,$b8,$80
6bd6: f0 80 f8 81+                 .bulk   $f0,$80,$f8,$81,$f8,$81,$f0,$80
6bde: e0 81 f0 83+                 .bulk   $e0,$81,$f0,$83,$f0,$83,$e0,$81
6be6: c0 83 e0 87+                 .bulk   $c0,$83,$e0,$87,$e0,$87,$c0,$83
6bee: 80 87 c0 8f+                 .bulk   $80,$87,$c0,$8f,$c0,$8f,$80,$87
6bf6: 80 8e 80 9f+                 .bulk   $80,$8e,$80,$9f,$80,$9f,$80,$8e
6bfe: 80 9c 80 be+                 .bulk   $80,$9c,$80,$be,$80,$be,$80,$9c

                   ; 
                   ; Creates gun explosion effect, by erasing the orange gun base and adding
                   ; shrapnel.
                   ; 
6c06: a2 00        ExplodeGun      ldx     #$00
6c08: 8e ea 42                     stx     cur_gun_angle     ;set gun angle to zero (blank image)
6c0b: 20 24 46                     jsr     RedrawGun         ;erase gun
6c0e: ad 5b 6c                     lda     gun_base_erased_flag
6c11: d0 08                        bne     @SkipBaseErase
6c13: 20 31 6c                     jsr     DrawGunBase       ;erase the gun base
6c16: a9 01                        lda     #$01
6c18: 8d 5b 6c                     sta     gun_base_erased_flag ;set flag
                   ; Create explosion effect.
6c1b: a9 04        @SkipBaseErase  lda     #4
6c1d: 48                           pha
6c1e: a9 12                        lda     #18               ;column 18
6c20: 48                           pha
6c21: a9 02                        lda     #$02
6c23: 48                           pha
6c24: a9 a4                        lda     #164              ;row 164
6c26: 48                           pha
6c27: a9 00                        lda     #$00
6c29: 48                           pha
6c2a: a9 fc                        lda     #$fc
6c2c: 48                           pha
6c2d: 20 0e 51                     jsr     AddShrapnel
6c30: 60                           rts

                   ; 
                   ; Draws the base of the gun as an orange rectangle on top of the white base
                   ; block.  Call this a second time to erase it.
                   ; 
                   ]hires_ptr      .var    $74    {addr/2}

6c31: a2 ae        DrawGunBase     ldx     #174              ;start at row 174
6c33: a0 12                        ldy     #18               ;column 18
6c35: bd 5e 4d     @Loop           lda     hires_addr_hi,x
6c38: 85 75                        sta     ]hires_ptr+1
6c3a: bd 1e 4e                     lda     hires_addr_lo,x
6c3d: 85 74                        sta     ]hires_ptr
6c3f: a9 aa                        lda     #$aa              ;orange (even)
6c41: 51 74                        eor     (]hires_ptr),y
6c43: 91 74                        sta     (]hires_ptr),y
6c45: c8                           iny
6c46: a9 d5                        lda     #$d5              ;orange (odd)
6c48: 51 74                        eor     (]hires_ptr),y
6c4a: 91 74                        sta     (]hires_ptr),y
6c4c: c8                           iny
6c4d: a9 80                        lda     #$80              ;set high bit on next byte over
6c4f: 51 74                        eor     (]hires_ptr),y
6c51: 91 74                        sta     (]hires_ptr),y
6c53: 88                           dey                       ;move back to column 18
6c54: 88                           dey
6c55: ca                           dex                       ;move up one row
6c56: e0 a9                        cpx     #169              ;are we above row 169?
6c58: b0 db                        bcs     @Loop             ;not yet, loop
6c5a: 60                           rts

                   gun_base_erased_flag
6c5b: 00                           .dd1    $00               ;bool 0/1: have we erased gun base?

                   ; 
                   ; Make sounds.  This is called twice during the main loop.
                   ; 
                   ; Most sounds are based on patterns from a table.  Some are continuous but
                   ; oscillating, e.g. a falling bomb.  Some change pitch over time, e.g. the sound
                   ; of a firing shell.
                   ; 
                   ; The sound pattern is stored at $6cd5, and indexes a table at $6cd8:
                   ;   0 - paratrooper safe landing
                   ;   1 - shell fired
                   ;   2 - ?
                   ;   3 - paratrooper chute shot
                   ;   4 - paratrooper body shot
                   ;   5 - ?
                   ;   6 - falling bomb shot
                   ;   7 - paratrooper crashes into ground
                   ;   8 - flier shot
                   ;   9 - bomber flying
                   ; 
                   ; The counter value at $6cd6 tracks progress through the 16-byte pattern.  When
                   ; code wants to initiate a sound, e.g. because a flier was shot down, it sets
                   ; $6cd5 to the desired sound pattern, and $6cd6 to zero.
                   ; 
                   ; Note: this code doesn't produce sound for flying helicopters.  The flier
                   ; update code at $634f handles this, generating a pitch that depends on the
                   ; number of helicopters on screen.
                   ; 
                   • Clear variables
                   ]delay_count    .var    $76    {addr/1}
                   ]sound_ptr      .var    $77    {addr/2}
                   ]click_flag     .var    $79    {addr/1}

6c5c: 08           MakeSounds      php                       ;save A/X/Y/P
6c5d: 48                           pha
6c5e: 8a                           txa
6c5f: 48                           pha
6c60: 98                           tya
6c61: 48                           pha
6c62: a9 00                        lda     #$00              ;default to silence
6c64: 85 79                        sta     ]click_flag
6c66: a9 08                        lda     #$08              ;with a delay to maintain frame rate
6c68: 85 76                        sta     ]delay_count
                   ; 
6c6a: ad d7 6c                     lda     active_bomb_count ;are any bombs falling?
6c6d: f0 0f                        beq     @NoBombs          ;no, branch
6c6f: ad d5 6c                     lda     sound_event_num   ;check sound event number
6c72: c9 06                        cmp     #$06              ;is it 6 (bomb was shot)?
6c74: f0 17                        beq     @GotEvent         ;yes, branch so we don't override it
6c76: a9 05                        lda     #$05              ;make a droning sound when bomb is falling
6c78: 85 79                        sta     ]click_flag       ;(this is a priority, to alert player)
6c7a: 85 76                        sta     ]delay_count
6c7c: d0 3a                        bne     @PlaySound

6c7e: ad d5 6c     @NoBombs        lda     sound_event_num   ;do we have an active sound?
6c81: 10 0a                        bpl     @GotEvent         ;yes, branch
6c83: ad d5 42                     lda     active_bomber_count ;are there bombers on screen?
6c86: f0 30                        beq     @PlaySound        ;no, play background sound
6c88: a9 09                        lda     #$09              ;yes, play sound #9 (bomber flying)
6c8a: 8d d5 6c                     sta     sound_event_num
                   ; 
6c8d: ad d6 6c     @GotEvent       lda     sound_event_ctr   ;get counter
6c90: 29 0f                        and     #$0f              ;reduce to 0-15 (sound table entries have 16 bytes)
6c92: a8                           tay                       ;use as index
6c93: ad d5 6c                     lda     sound_event_num   ;get event number
6c96: ee d6 6c                     inc     sound_event_ctr   ;advance counter
6c99: c0 0f                        cpy     #$0f              ;at end of sound?
6c9b: d0 09                        bne     @NotDone          ;no, keep going
6c9d: a2 ff                        ldx     #$ff
6c9f: 8e d5 6c                     stx     sound_event_num   ;set event number to -1
6ca2: e8                           inx                       ;X-reg = 0
6ca3: 8e d6 6c                     stx     sound_event_ctr   ;zero out the counter
6ca6: 0a           @NotDone        asl     A                 ;double the event number
6ca7: aa                           tax                       ;use as index
6ca8: bd 78 6d                     lda     sound_addrs,x     ;get address into sound pattern tables
6cab: 85 77                        sta     ]sound_ptr
6cad: bd 79 6d                     lda     sound_addrs+1,x
6cb0: 85 78                        sta     ]sound_ptr+1
6cb2: b1 77                        lda     (]sound_ptr),y    ;get current value
6cb4: 85 79                        sta     ]click_flag       ;use as flag ($00 = silence)
6cb6: 85 76                        sta     ]delay_count      ; and as count
                   ; Make 9 clicks at a specific interval, unless silence requested, with a delay
                   ; configured by the sound table.
6cb8: a2 09        @PlaySound      ldx     #9                ;repeat 9x
6cba: a5 79        @SoundLoop      lda     ]click_flag       ;silence?
6cbc: f0 03                        beq     @NoClick          ;yes, branch over speaker click
6cbe: ad 30 c0                     lda     SPKR              ;click speaker
6cc1: a4 76        @NoClick        ldy     ]delay_count
                   ; Delay (15*(Y+1))-1 cycles.
6cc3: ea           @DelayLoop      nop
6cc4: ea                           nop
6cc5: ea                           nop
6cc6: ea                           nop
6cc7: ea                           nop
6cc8: 88                           dey
6cc9: 10 f8                        bpl     @DelayLoop
6ccb: ca                           dex                       ;done with clicks?
6ccc: d0 ec                        bne     @SoundLoop        ;not yet, loop
                   ; Restore A/X/Y/P and bail.
6cce: 68                           pla
6ccf: a8                           tay
6cd0: 68                           pla
6cd1: aa                           tax
6cd2: 68                           pla
6cd3: 28                           plp
6cd4: 60                           rts

6cd5: ff           sound_event_num .dd1    $ff               ;sound pattern index, 0-9
6cd6: 00           sound_event_ctr .dd1    $00               ;tracks progress in sound pattern
                   active_bomb_count
6cd7: 00                           .dd1    $00
                   ; 
                   ; Sound patterns, 16 bytes each.  Each value specifies the delay between clicks,
                   ; in units of approximately 15 cycles.  A value of zero indicates silence.
                   ; 
6cd8: 08 08 08 08+ sound0          .bulk   $08,$08,$08,$08,$0c,$0c,$0c,$04,$04,$08,$08,$08,$08,$0c,$0c,$0c
6ce8: 01 01 02 03+ sound1          .bulk   $01,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f
6cf8: 08 08 08 08+ sound2          .bulk   $08,$08,$08,$08,$00,$00,$08,$08,$08,$0a,$0a,$0a,$0c,$0d,$0e,$0f
6d08: 08 08 08 08+ sound3          .bulk   $08,$08,$08,$08,$08,$08,$08,$08,$01,$02,$03,$04,$05,$06,$07,$08
6d18: 01 01 04 07+ sound4          .bulk   $01,$01,$04,$07,$0a,$01,$01,$04,$07,$0a,$01,$01,$04,$07,$0a,$0a
6d28: 01 01 02 02+ sound5          .bulk   $01,$01,$02,$02,$02,$02,$03,$03,$03,$03,$02,$02,$02,$02,$01,$01
6d38: 01 06 0a 04+ sound6          .bulk   $01,$06,$0a,$04,$0f,$0c,$07,$0e,$02,$03,$08,$05,$0b,$0d,$09,$01
6d48: 0f 0d 0b 09+ sound7          .bulk   $0f,$0d,$0b,$09,$07,$05,$03,$01,$01,$03,$05,$07,$09,$0b,$0d,$0f
6d58: 0f 0f 0f 0f+ sound8          .bulk   $0f,$0f,$0f,$0f,$0a,$0a,$0a,$0a,$0d,$0d,$0d,$0d,$08,$08,$08,$08
6d68: 01 01 00 04+ sound9          .bulk   $01,$01,$00,$04,$04,$00,$01,$01,$01,$00,$02,$02,$00,$01,$01,$00
                   ; 
                   ; Addresses of sound patterns.
                   ; 
6d78: d8 6c        sound_addrs     .dd2    sound0
6d7a: e8 6c                        .dd2    sound1
6d7c: f8 6c                        .dd2    sound2
6d7e: 08 6d                        .dd2    sound3
6d80: 18 6d                        .dd2    sound4
6d82: 28 6d                        .dd2    sound5
6d84: 38 6d                        .dd2    sound6
6d86: 48 6d                        .dd2    sound7
6d88: 58 6d                        .dd2    sound8
6d8a: 68 6d                        .dd2    sound9

                   ; 
                   ; Prints the title and instructions on the text screen.
                   ; 
                   ]outptr         .var    $76    {addr/2}
                   ]int_val        .var    $7c    {addr/2}

                   PrintTitleAndInstr
6d8c: a2 2e                        ldx     #46               ;start on line 23
6d8e: bd a3 71     @ClearLoop      lda     text_line_addrs,x ;get address of start of text line
6d91: 85 76                        sta     ]outptr
6d93: bd a4 71                     lda     text_line_addrs+1,x
6d96: 85 77                        sta     ]outptr+1
6d98: a9 a0                        lda     #“ ”              ;set to high-ASCII space
6d9a: a0 27                        ldy     #39               ;40 chars per line
6d9c: 91 76        @LineLoop       sta     (]outptr),y       ;write to screen memory
6d9e: 88                           dey
6d9f: 10 fb                        bpl     @LineLoop
6da1: ca                           dex
6da2: ca                           dex
6da3: 10 e9                        bpl     @ClearLoop
                   ; 
6da5: a2 06                        ldx     #$06              ;col 6
6da7: a0 00                        ldy     #$00              ;row 0
6da9: 20 66 71                     jsr     InAZ_PrintInline
6dac: 2a 2a 2a 2a+                 .zstr   ‘**********SABOTAGE**********’
6dc9: a2 06                        ldx     #$06              ;col 6
6dcb: a0 01                        ldy     #$01              ;row 1
6dcd: 20 66 71                     jsr     InAZ_PrintInline
6dd0: 2a 2a 2a 2a+                 .zstr   ‘*******BY**MARK*ALLEN*******’
6ded: a2 05                        ldx     #$05
6def: a0 03                        ldy     #$03
6df1: 20 66 71                     jsr     InAZ_PrintInline
6df4: 53 43 4f 52+                 .zstr   ‘SCORE:’
6dfb: ad ec 42                     lda     cur_score+1       ;print most recent score
6dfe: 85 7d                        sta     ]int_val+1
6e00: ad eb 42                     lda     cur_score
6e03: 85 7c                        sta     ]int_val
6e05: 20 31 71                     jsr     PrintDec16
6e08: a2 15                        ldx     #$15
6e0a: a0 03                        ldy     #$03
6e0c: 20 66 71                     jsr     InAZ_PrintInline
6e0f: 48 49 47 48+                 .zstr   ‘HIGH SCORE:’
6e1b: ad e7 42                     lda     high_score+1      ;print high score
6e1e: 85 7d                        sta     ]int_val+1
6e20: ad e6 42                     lda     high_score
6e23: 85 7c                        sta     ]int_val
6e25: 20 31 71                     jsr     PrintDec16
6e28: a2 00                        ldx     #$00
6e2a: a0 05                        ldy     #$05
6e2c: 20 66 71                     jsr     InAZ_PrintInline
6e2f: 54 48 45 20+                 .zstr   ‘THE OBJECT OF  SABOTAGE  IS  TO SCORE AS’
6e58: a2 00                        ldx     #$00
6e5a: a0 06                        ldy     #$06
6e5c: 20 66 71                     jsr     InAZ_PrintInline
6e5f: 4d 41 4e 59+                 .zstr   ‘MANY POINTS AS POSSIBLE BEFORE THE ENEMY’
6e88: a2 00                        ldx     #$00
6e8a: a0 07                        ldy     #$07
6e8c: 20 66 71                     jsr     InAZ_PrintInline
6e8f: 44 45 53 54+                 .zstr   ‘DESTROYS YOUR GUN  EMPLACEMENT. YOUR GUN’
6eb8: a2 00                        ldx     #$00
6eba: a0 08                        ldy     #$08
6ebc: 20 66 71                     jsr     InAZ_PrintInline
6ebf: 4d 41 59 20+                 .zstr   ‘MAY BE DESTOYED EITHER  BY A HIT FROM AN’
6ee8: a2 00                        ldx     #$00
6eea: a0 09                        ldy     #$09
6eec: 20 66 71                     jsr     InAZ_PrintInline
6eef: 45 4e 45 4d+                 .zstr   ‘ENEMY BOMB  OR  BY  SABOTAGE WHEN ENOUGH’
6f18: a2 00                        ldx     #$00
6f1a: a0 0a                        ldy     #$0a
6f1c: 20 66 71                     jsr     InAZ_PrintInline
6f1f: 50 41 52 41+                 .zstr   ‘PARATROOPERS  HAVE  REACHED THE  GROUND.’
6f48: a2 00                        ldx     #$00
6f4a: a0 0b                        ldy     #$0b
6f4c: 20 66 71                     jsr     InAZ_PrintInline
6f4f: 54 48 45 20+                 .zstr   ‘THE GUN MAY BE CONTROLLED WITH A  PADDLE’
6f78: a2 00                        ldx     #$00
6f7a: a0 0c                        ldy     #$0c
6f7c: 20 66 71                     jsr     InAZ_PrintInline
6f7f: 4f 52 20 54+                 .zstr   ‘OR THE  KEYBOARD.  IF YOU ARE  USING THE’
6fa8: a2 00                        ldx     #$00
6faa: a0 0d                        ldy     #$0d
6fac: 20 66 71                     jsr     InAZ_PrintInline
6faf: 4b 45 59 42+                 .zstr   ‘KEYBOARD’
6fb8: a9 2c                        lda     #‘,’              ;some trouble with commas?
6fba: 20 9a 71                     jsr     OutputChar
6fbd: a2 09                        ldx     #$09
6fbf: a0 0d                        ldy     #$0d
6fc1: 20 66 71                     jsr     InAZ_PrintInline
6fc4: 20 20 50 52+                 .zstr   ‘  PRESSING  'D'  WILL MOVE  THE’
6fe4: a2 00                        ldx     #$00
6fe6: a0 0e                        ldy     #$0e
6fe8: 20 66 71                     jsr     InAZ_PrintInline
6feb: 47 55 4e 20+                 .zstr   ‘GUN COUNTER-CLOCKWISE AND  'F' WILL MOVE’
7014: a2 00                        ldx     #$00
7016: a0 0f                        ldy     #$0f
7018: 20 66 71                     jsr     InAZ_PrintInline
701b: 49 54 20 43+                 .zstr   ‘IT CLOCKWISE.  ANY OTHER KEY WILL FIRE A’
7044: a2 00                        ldx     #$00
7046: a0 10                        ldy     #$10
7048: 20 66 71                     jsr     InAZ_PrintInline
704b: 53 48 45 4c+                 .zstr   ‘SHELL.  FIRING A SHELL  COSTS ONE POINT.’
7074: a2 0c                        ldx     #$0c
7076: a0 11                        ldy     #$11
7078: 20 66 71                     jsr     InAZ_PrintInline
707b: 54 41 52 47+                 .zstr   ‘TARGET    POINTS’
708c: a2 0c                        ldx     #$0c
708e: a0 12                        ldy     #$12
7090: 20 66 71                     jsr     InAZ_PrintInline
7093: 3d 3d 3d 3d+                 .zstr   ‘======    ======’
70a4: a2 0c                        ldx     #$0c
70a6: a0 13                        ldy     #$13
70a8: 20 66 71                     jsr     InAZ_PrintInline
70ab: 42 4f 4d 42+                 .zstr   ‘BOMB          25’
70bc: a2 0c                        ldx     #$0c
70be: a0 14                        ldy     #$14
70c0: 20 66 71                     jsr     InAZ_PrintInline
70c3: 48 45 4c 49+                 .zstr   ‘HELICOPTER     5’
70d4: a2 0c                        ldx     #$0c
70d6: a0 15                        ldy     #$15
70d8: 20 66 71                     jsr     InAZ_PrintInline
70db: 4a 45 54 20+                 .zstr   ‘JET            5’
70ec: a2 0c                        ldx     #$0c
70ee: a0 16                        ldy     #$16
70f0: 20 66 71                     jsr     InAZ_PrintInline
70f3: 50 41 52 41+                 .zstr   ‘PARATROOPER    2’
7104: a2 02                        ldx     #$02
7106: a0 17                        ldy     #$17
7108: 20 66 71                     jsr     InAZ_PrintInline
710b: 44 4f 20 59+                 .zstr   ‘DO YOU WANT  STEERABLE SHELLS (Y/N)?’
7130: 60                           rts

                   ; 
                   ; Prints a 16-bit BCD integer in decimal form, suppressing leading zeroes.  Used
                   ; for scores.
                   ; 
                   ; On entry:
                   ;   $76-77: text screen line pointer
                   ;   $78: text screen column
                   ;   $7c-7d: BCD value to print
                   ; 
                   ]value          .var    $7c    {addr/2}
                   ]lzflag         .var    $7e    {addr/1}   ;leading-zero flag

7131: a2 00        PrintDec16      ldx     #$00
7133: 86 7e                        stx     ]lzflag
7135: a5 7d                        lda     ]value+1          ;get high byte of value
7137: 4a                           lsr     A                 ;shift to get high nibble
7138: 4a                           lsr     A
7139: 4a                           lsr     A
713a: 4a                           lsr     A
713b: 20 55 71                     jsr     @PrintMaybe       ;print if nonzero
713e: a5 7d                        lda     ]value+1
7140: 29 0f                        and     #$0f              ;get low nibble of high byte
7142: 20 55 71                     jsr     @PrintMaybe       ;print
7145: a5 7c                        lda     ]value            ;do same for low byte
7147: 4a                           lsr     A
7148: 4a                           lsr     A
7149: 4a                           lsr     A
714a: 4a                           lsr     A
714b: 20 55 71                     jsr     @PrintMaybe
714e: a5 7c                        lda     ]value
7150: 29 0f                        and     #$0f
7152: 4c 60 71                     jmp     @PrintChar        ;always print least-significant digit

7155: a6 7e        @PrintMaybe     ldx     ]lzflag           ;have we printed something yet?
7157: d0 07                        bne     @PrintChar        ;yes, print this
7159: 85 7e                        sta     ]lzflag           ;set lzflag nonzero if current value is nonzero
715b: c9 00                        cmp     #$00              ;is it zero?
715d: d0 01                        bne     @PrintChar        ;no, print it
715f: 60                           rts                       ;otherwise, we're still printing leading zeroes

7160: 18           @PrintChar      clc
7161: 69 30                        adc     #‘0’              ;convert [0,9] to ['0','9']
7163: 4c 9a 71                     jmp     OutputChar        ;print it

                   ; 
                   ; Prints an inline null-terminated ASCII string to the text screen.
                   ; 
                   ; The return address on the stack is adjusted to return to just after the
                   ; string.
                   ; 
                   ; On entry:
                   ;   X-reg: screen column [0,39]
                   ;   Y-reg: screen row [0,23]
                   ; 
                   ]out_ptr        .var    $76    {addr/2}
                   ]col_tmp        .var    $78    {addr/1}
                   ]text_ptr       .var    $7a    {addr/2}

                   InAZ_PrintInline
7166: 20 8a 71                     jsr     @SetTextPtr       ;save column, set text pointer to row start
7169: 68                           pla                       ;pull return address to get text pointer
716a: 85 7a                        sta     ]text_ptr
716c: 68                           pla
716d: 85 7b                        sta     ]text_ptr+1
716f: e6 7a        @Loop           inc     ]text_ptr
7171: d0 02                        bne     @NoInc
7173: e6 7b                        inc     ]text_ptr+1
7175: a0 00        @NoInc          ldy     #$00
7177: b1 7a                        lda     (]text_ptr),y
7179: f0 08                        beq     @Bail             ;found terminating NUL
717b: 09 80                        ora     #$80              ;set high bit (redundant)
717d: 20 9a 71                     jsr     OutputChar        ;print character to screen
7180: 4c 6f 71                     jmp     @Loop

7183: a5 7b        @Bail           lda     ]text_ptr+1       ;push return address
7185: 48                           pha
7186: a5 7a                        lda     ]text_ptr
7188: 48                           pha
7189: 60                           rts

718a: 86 78        @SetTextPtr     stx     ]col_tmp          ;save the column
718c: 98                           tya
718d: 0a                           asl     A                 ;multiply row by 2
718e: a8                           tay
718f: b9 a3 71                     lda     text_line_addrs,y ;get text line address
7192: 85 76                        sta     ]out_ptr          ;save as pointer
7194: b9 a4 71                     lda     text_line_addrs+1,y
7197: 85 77                        sta     ]out_ptr+1
7199: 60                           rts

                   ; 
                   ; Writes a character to the text screen at the current row/col, ORing it to high
                   ; ASCII first.  Advances the text column (but not the row).
                   ; 
                   ; On entry:
                   ;   $76-77: text screen line address
                   ;   $78: text screen column number
                   ; 
719a: a4 78        OutputChar      ldy     ]col_tmp
719c: 09 80                        ora     #$80              ;convert to high ASCII
719e: 91 76                        sta     (]out_ptr),y
71a0: e6 78                        inc     ]col_tmp
71a2: 60                           rts

                   ; Text screen page 1 line addresses.
71a3: 00 04        text_line_addrs .dd2    $0400
71a5: 80 04                        .dd2    $0480
71a7: 00 05                        .dd2    $0500
71a9: 80 05                        .dd2    $0580
71ab: 00 06                        .dd2    $0600
71ad: 80 06                        .dd2    $0680
71af: 00 07                        .dd2    $0700
71b1: 80 07                        .dd2    $0780
71b3: 28 04                        .dd2    $0428
71b5: a8 04                        .dd2    $04a8
71b7: 28 05                        .dd2    $0528
71b9: a8 05                        .dd2    $05a8
71bb: 28 06                        .dd2    $0628
71bd: a8 06                        .dd2    $06a8
71bf: 28 07                        .dd2    $0728
71c1: a8 07                        .dd2    $07a8
71c3: 50 04                        .dd2    $0450
71c5: d0 04                        .dd2    $04d0
71c7: 50 05                        .dd2    $0550
71c9: d0 05                        .dd2    $05d0
71cb: 50 06                        .dd2    $0650
71cd: d0 06                        .dd2    $06d0
71cf: 50 07                        .dd2    $0750
71d1: d0 07                        .dd2    $07d0

                   ; The rest appears to be vestigial junk.
71d3: 00 20 57 49+                 .junk   557
                                   .adrend ↑ ~$4000

Symbol Table

LabelValue
ColdInit$4000
Start$1d00