;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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
46fd: 00 00 00 00+ gun_erase .fill 48,$00
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
;
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
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
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
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
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
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
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 .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).
;
5b64: 08 00 3e 00+ trooper_0 .bulk $08,$00,$3e,$00,$5d,$00,$1c,$00,$14,$00,$22,$00
5b70: 10 00 7c 00+ trooper_1 .bulk $10,$00,$7c,$00,$3a,$01,$38,$00,$28,$00,$48,$00
5b7c: 20 00 78 01+ trooper_2 .bulk $20,$00,$78,$01,$74,$02,$70,$00,$50,$00,$48,$00
5b88: 40 00 70 03+ trooper_3 .bulk $40,$00,$70,$03,$68,$05,$60,$01,$20,$01,$20,$02
5b94: 00 01 60 07+ trooper_4 .bulk $00,$01,$60,$07,$50,$0b,$40,$03,$40,$02,$20,$02
5ba0: 00 02 40 0f+ trooper_5 .bulk $00,$02,$40,$0f,$20,$17,$00,$07,$00,$05,$00,$09
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.
;
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.
;
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.
;
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
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
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
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).
;
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