********************************************************************************
* ABM for the Apple II, by Silas Warner *
* Copyright 1980 Muse Software *
* *
* This is the main game code. It was extracted from a .NIB image of the 13- *
* sector original disk. *
********************************************************************************
* Disassembly by Andy McFadden, using 6502bench SourceGen v1.7.1. *
* Last updated 2020/08/13 *
* *
* This program makes heavy use of inline data following JSRs. These are *
* automatically formatted by an extension script based on the label prefix: *
* *
* InA_* : JSR is followed by a two-byte address *
* InXY_*: JSR is followed by a two-byte text screen position *
* InS_* : JSR is followed by a null-terminated high-ASCII string *
* *
* If you rename one of these labels, do not change the prefix, or the inline *
* formatting will vanish. *
********************************************************************************
* The state of inbound missiles are recorded from $4200-4cff. Each missile *
* gets its own page, so there are a maximum of 11 inbound missiles. Inline *
* references to addresses at $42xx are transparently remapped to the *
* appropriate page. *
* *
* The first 150-ish bytes of each page record the column of the missile trail *
* at the corresponding row, making it easy to erase the trail when the missile *
* is destroyed. The last 30 bytes of each page holds the missile state *
* (position, explosion progress, etc). *
* *
* $4d00-4dff holds the trail data and state for the ABM. Only one ABM may be *
* launched at a time. *
********************************************************************************
* The Applesoft floating-point math routines are used heavily. The basic *
* format of a float is 40 bits: *
* *
* EEEEEEEE SMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM *
* *
* The exponent uses a bias of 129 (IEEE 754 uses 127). For example, the *
* number +25.0 would be: *
* *
* Applesoft: 0x8548000000: [10000101] [0][1001000 00000000 ...] *
* IEEE 754 : 0x41c80000: [0][1000001 1][1001000 00000000 ...] *
********************************************************************************
TOK_CALL .eq $8c {const}
O_DET_SIZE .eq $f4 {const} ;missile detonation size (96-176)
O_DET_STAGE1 .eq $f5 {const} ;tracks detonation progress (white)
O_DET_STAGE2 .eq $f6 {const} ;tracks detonation progress (black)
O_ROW .eq $fa {const} ;current row, starts at $00; $ff=inactive
O_DET_ROW .eq $fb {const} ;row at which missile detonates
O_TARGET_CITY .eq $fc {const} ;targeted city (0-5)
O_MIRV_ROW .eq $fd {const} ;row at which missile MIRVs ($00=never)
missile_ptr .eq $17 {addr/2} ;points to page with missile state
BAS_HCOLOR1 .eq $1c ;hi-res color mask
MON_WNDTOP .eq $22 ;top of scroll window
MON_CH .eq $24 ;cursor horizontal displacement
MON_CV .eq $25 ;cursor vertical displacement
BAS_HBASL .eq $26 ;base address for hi-res drawing (lo part)
MON_BASL .eq $28 ;base address for text output (lo)
BAS_HMASK .eq $30 ;hi-res graphics on-the-fly bit mask
MON_INVFLAG .eq $32 ;text mask (255=normal, 127=flash, 63=inv)
sfx_counter .eq $4a {addr/2} ;speaker click delay
BAS_FAC .eq $9d {addr/6} ;floating point accumulator (6b)
strobe_thing? .eq $cb ;not initialized, never used
BAS_SCALE .eq $e7 ;hi-res graphics scale factor
STACK .eq $0100 {addr/256} ;hardware stack
ReadPdlX .eq $0300 ;reads paddle for cursor X position
ReadPdlY .eq $0303 ;reads paddle for cursor Y position
GetOuterBtn .eq $0306 ;checks button that fires outer/center launchers
GetInnerBtn .eq $0309 ;checks button that fires inner launchers
missile_cur_xc .eq $42e2 {addr/5} ;fp: current hi-res column of missile (0-139)
missile_target_xc .eq $42e8 {addr/5} ;fp: target hi-res column of missile (10-135)
missile_xmove .eq $42ee {addr/5} ;fp: per-frame horizontal movement
abm_trail .eq $4d00 {addr/160} ;pixel columns for ABM trail
abm_cur_xc .eq $4de2 {addr/5} ;fp: current ABM column (0-139)
abm_target_xc .eq $4de8 {addr/5} ;fp: temp storage for ABM target column (0-139)
abm_xmove .eq $4dee {addr/5} ;fp: ABM per-frame horizontal movement
abm_max_size .eq $4df4 ;max size of exploding ABM ($50/$70)
abm_det_size1 .eq $4df5 ;current size of ABM explosion (white)
abm_det_size2 .eq $4df6 ;current size of ABM explosion (black)
abm_row .eq $4dfa ;current row of ABM, or $ff if none fired
abm_target_row .eq $4dfb ;row at which ABM will detonate
RWTS_BUFFER .eq $73ef {addr/256} ;RWTS sector buffer
HIGH_SCORE .eq $746f {addr/5} ;fp: high score
RWTS_PARM .eq $77e8 {addr/17} ;RWTS parameter table
DOS_32K_RWTS .eq $7d00 ;RWTS entry point
KBD .eq $c000 ;R last key pressed + 128
KBDSTRB .eq $c010 ;RW keyboard strobe
SPKR .eq $c030 ;RW toggle speaker
STROBE .eq $c040 ;R game I/O strobe
TXTSET .eq $c051 ;RW display text
TXTPAGE1 .eq $c054 ;RW display page 1
LORES .eq $c056 ;RW display lo-res graphics
BAS_GIVAYF .eq $e2f2 ;convert 16-bit (A,Y) to float, store in FAC
BAS_SNGFLT .eq $e301 ;convert 8-bit int (Y) to FLOAT
BAS_FSUB .eq $e7a7 ;FAC = (Y,A) - FAC
BAS_FADD .eq $e7be ;FAC = (Y,A) + FAC
BAS_CON_ONE .eq $e913 ;constant value 1.0
BAS_FMULT .eq $e97f ;FAC = (Y,A) * FAC
BAS_LOAD_ARG_FROM_YA .eq $e9e3 ;unpack 5-byte val at (Y,A) into ARG
BAS_MUL10 .eq $ea39 ;multiply FAC by 10
BAS_CON_TEN .eq $ea50 ;constant value 10.0
BAS_FDIV .eq $ea66 ;FAC = (Y,A) / FAC
BAS_LOAD_FAC_FROM_YA .eq $eaf9 ;unpack 5-byte val at (Y,A) into FAC
BAS_STORE_FAC_AT_YX_ROUNDED .eq $eb2b ;round FAC, store at (Y,X)
BAS_SIGN .eq $eb82 ;test FAC sign; <,=,> 0 -> A={-1,0,1}
BAS_FLOAT .eq $eb93 ;convert value in A to float in FAC
BAS_ABS .eq $ebaf ;changes sign of FAC to +
BAS_FCOMP .eq $ebb2 ;cmp (Y,A) with FAC; <,=,> -> A={1,0,-1}
BAS_QINT .eq $ebf2 ;convert FAC to big-endian int in FAC+1..FAC+4
BAS_INT .eq $ec23 ;FAC = floor(FAC)
BAS_LINPRT .eq $ed24 ;print float at (A,X) as decimal integer
BAS_PRINT_FAC .eq $ed2e ;print FAC to screen
BAS_CON_HALF .eq $ee64 ;constant value 0.5
BAS_FPWRT .eq $ee97 ;compute exponentiation
BAS_NEGOP .eq $eed0 ;negate value in FAC
BAS_RND .eq $efae ;generate random number
BAS_CON_PI_HALF .eq $f066 ;constant value PI/2
BAS_CON_PI_DOUB .eq $f06b ;constant value PI*2
BAS_QUARTER .eq $f070 ;constant value 0.25
BAS_HGR .eq $f3e2 ;switch to hi-res mixed-mode and clear
BAS_HPOSN .eq $f411 ;set hi-res position; horiz=(Y,X) vert=A
BAS_HPLOT0 .eq $f457 ;plot point; horiz=(Y,X), vert=A
BAS_HGLIN .eq $f53a ;draw line from last point to (A,X),Y
BAS_DRAW0 .eq $f601 ;draw a shape from addr (Y,X)
BAS_HCOLOR .eq $f6e9 {addr/4} ;set hi-res color to value in X
MON_SETTXT .eq $fb39 ;set screen to text mode
MON_VTAB .eq $fc22 ;tab to row specified in Acc
MON_HOME .eq $fc58 ;clear screen and reset text output to top-left
MON_WAIT .eq $fca8 ;delay for (26 + 27*Acc + 5*(Acc*Acc))/2 cycles
MON_CROUT .eq $fd8e ;print a carriage return
MON_COUT .eq $fded ;print Acc to output device via $36-37
MON_COUT1 .eq $fdf0 ;print Acc to screen
.org $0801
********************************************************************************
* Applesoft BASIC header. When run, calls assembly-language program at $810. *
********************************************************************************
0801: 0b 08 .dd2 $080b ;pointer to start of next line
0803: 01 00 .dd2 $0001 ;line 1
0805: 8c .dd1 TOK_CALL ;"CALL 2064"
0806: 32 30 36 34 .str ‘2064’
080a: 00 .dd1 $00
080b: 00 08 .dd2 $0800 ;(garbage pointer)
080d: 02 00 .dd2 $0002 ;line 2
080f: 8d .dd1 $8d ;junk
********************************************************************************
* Program entry point. *
********************************************************************************
0810: 20 8c 09 Start jsr ReadHighScore ;read high score from disk
0813: 8d 56 c0 Restart sta LORES ;switch to lo-res graphics (why?)
0816: 8d 51 c0 sta TXTSET ;text mode
0819: 8d 54 c0 sta TXTPAGE1 ;text page 1
; Draw the scroll-up ABM logo.
081c: a9 05 lda #$05 ;draw the logo animation 5x
081e: 8d 44 08 sta :_count+1
0821: 20 39 fb :LogoLoop jsr MON_SETTXT ;text mode, full-screen window
0824: 20 58 fc jsr MON_HOME ;clear screen
0827: 20 42 15 jsr InXY_SetTextPos
082a: 17 .dd1 23 ;row 23 (bottom of screen)
082b: 00 .dd1 0 ;col 0
082c: a2 ff ldx #$ff
082e: e8 :LogoLineLoop inx
082f: bd 05 09 lda abm_logo,x ;get horizontal offset
0832: 10 06 bpl :DrawSpc ;if not negative, draw a space
0834: 20 8e fd jsr MON_CROUT ;print CR to scroll text up one line
0837: 4c 2e 08 jmp :LogoLineLoop
083a: f0 07 :DrawSpc beq :_count ;if zero, we're all done
083c: a8 tay
083d: a9 20 lda #‘ ’ ;inverse space
083f: 91 28 sta (MON_BASL),y ;store on screen
0841: d0 eb bne :LogoLineLoop
0843: a9 00 :_count lda #$00 ;count is stored in self-mod operand
0845: f0 06 beq :LogoDone ;we're done, branch
0847: ce 44 08 dec :_count+1
084a: 4c 21 08 jmp :LogoLoop
084d: 20 42 15 :LogoDone jsr InXY_SetTextPos
0850: 05 .dd1 5 ;row 5 (6th row)
0851: 0a .dd1 10 ;col 10 (11th position)
0852: 20 2b 15 jsr InS_PrintString ;print high score notice
0855: c8 c9 c7 c8+ .zstr “HIGH SCORE IS ”
0864: 20 73 15 jsr InA_LoadFloat ;copy high score into FAC
0867: 31 0c .dd2 high_score
0869: 20 2e ed jsr BAS_PRINT_FAC ;print FAC
086c: 20 42 15 jsr InXY_SetTextPos
086f: 17 .dd1 23 ;row 23 (bottom of screen)
0870: 00 .dd1 0
0871: 20 2b 15 jsr InS_PrintString ;print multi-line string (scrolls 3x)
0874: a0 a0 a0 a0+ .zstr “ COPYRIGHT 1980, MUSE SOFTWARE”,$8d,“ ALL RIGHTS”
+ “ RESERVED”,$8d,$8d,“ PRESS A KEY TO START.”
; Wait for a key to be hit. If enough time passes, go into attract mode.
08d6: 8d 10 c0 sta KBDSTRB ;clear keyboard strobe
08d9: a2 f0 ldx #$f0
08db: 8e ef 0b stx temp+1 ;init delay counter to $f0f0
08de: 8e ee 0b stx temp
08e1: ee ee 0b :WaitForStart inc temp
08e4: d0 09 bne :ChkStart
08e6: ee ef 0b inc temp+1
08e9: d0 04 bne :ChkStart
08eb: a9 ff lda #$ff ;demo is playing
08ed: d0 10 bne :SetMode ;(always)
08ef: ad 00 c0 :ChkStart lda KBD ;key hit?
08f2: 30 06 bmi :KeyHit ;yes, branch
08f4: ca dex
08f5: d0 f8 bne :ChkStart
08f7: 4c e1 08 jmp :WaitForStart
08fa: 8d 10 c0 :KeyHit sta KBDSTRB
08fd: a9 00 lda #$00 ;player is playing
08ff: 8d f0 0b :SetMode sta auto_play_flag
0902: 4c d4 09 jmp StartGame
;
; Text "ABM" logo. Values are horizontal offsets where an inverse ' ' should be
; drawn. The result looks like this:
;
; ### ##### ## ##
; ## ## ## ## ### ###
; ## ## ## ## #######
; ## ## ## ## ## # ##
; ## ## ##### ## ##
; ####### ## ## ## ##
; ## ## ## ## ## ##
; ## ## ## ## ## ##
; ## ## ##### ## ##
0905: 09 0a 0b 11+ abm_logo .bulk $09,$0a,$0b,$11,$12,$13,$14,$15,$1b,$1c,$20,$21,$c8
0912: 08 09 0b 0c+ .bulk $08,$09,$0b,$0c,$11,$12,$15,$16,$1b,$1c,$1d,$1f,$20,$21,$c8
0921: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$16,$17,$1b,$1c,$1d,$1e,$1f,$20,$21,$c8
0931: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$15,$16,$1b,$1c,$1e,$20,$21,$c8
093f: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$13,$14,$15,$1b,$1c,$20,$21,$c8
094d: 07 08 09 0a+ .bulk $07,$08,$09,$0a,$0b,$0c,$0d,$11,$12,$15,$16,$1b,$1c,$20,$21,$c8
095d: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$16,$17,$1b,$1c,$20,$21,$c8
096a: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$15,$16,$1b,$1c,$20,$21,$c8
0977: 07 08 0c 0d+ .bulk $07,$08,$0c,$0d,$11,$12,$13,$14,$15,$1b,$1c,$20,$21,$c8
0985: c8 c8 c8 c8+ .bulk $c8,$c8,$c8,$c8,$c8,$c8
098b: 00 .dd1 $00 ;zero indicates end of list
;
; Reads the high score from disk.
;
098c: a9 01 ReadHighScore lda #$01 ;cmd=read
098e: 20 b1 09 jsr AccessHighScore ;read high score sector
0991: a0 74 ldy #>HIGH_SCORE
0993: a9 6f lda #<HIGH_SCORE
0995: 20 f9 ea jsr BAS_LOAD_FAC_FROM_YA ;copy score from sector buffer to FAC
0998: 20 ac 15 jsr InA_StoreFloat ;store rounded FAC at address below
099b: 31 0c .dd2 high_score
099d: 60 rts
;
; Writes the high score to disk.
;
099e: a9 01 WriteHighScore lda #$01 ;cmd=read
09a0: 20 b1 09 jsr AccessHighScore ;read high score sector
09a3: 20 73 15 jsr InA_LoadFloat ;copy score into FAC
09a6: 31 0c .dd2 high_score
09a8: a0 74 ldy #>HIGH_SCORE
09aa: a2 6f ldx #<HIGH_SCORE
09ac: 20 2b eb jsr BAS_STORE_FAC_AT_YX_ROUNDED ;copy score from FAC to sector buffer
09af: a9 02 lda #$02 ;cmd=write; fall through
;
; Common code for high score I/O.
;
09b1: 8d f4 77 AccessHighScore sta RWTS_PARM+12
09b4: a9 02 lda #$02 ;track 2
09b6: 8d ec 77 sta RWTS_PARM+4
09b9: a9 09 lda #$09 ;sector 9
09bb: 8d ed 77 sta RWTS_PARM+5
09be: a9 00 lda #$00 ;volume 0 (matches any)
09c0: 8d eb 77 sta RWTS_PARM+3
09c3: a9 ef lda #<RWTS_BUFFER ;set buffer address
09c5: 8d f0 77 sta RWTS_PARM+8
09c8: a9 73 lda #>RWTS_BUFFER
09ca: 8d f1 77 sta RWTS_PARM+9
09cd: a0 e8 ldy #<RWTS_PARM ;use DOS RWTS parameter list
09cf: a9 77 lda #>RWTS_PARM
09d1: 4c 00 7d jmp DOS_32K_RWTS ;write the sector
********************************************************************************
* Initializes state for a new game. *
********************************************************************************
09d4: 20 39 fb StartGame jsr MON_SETTXT ;set text mode, reset window
09d7: 20 58 fc jsr MON_HOME ;clear the screen
09da: 20 e2 f3 jsr BAS_HGR ;switch to HGR w/window, and clear graphics
09dd: 8d 10 c0 sta KBDSTRB ;clear keyboard strobe
09e0: a2 03 ldx #$03
09e2: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
; Init game state.
09e5: a9 46 lda #70
09e7: 8d 79 13 sta cursor_x ;init cursor position
09ea: 8d 7a 13 sta cursor_y ;roughly middle of screen (X gets doubled)
09ed: 8d 81 12 sta auto_curs_x
09f0: 8d 82 12 sta auto_curs_y
09f3: a2 01 ldx #$01
09f5: 86 e7 stx BAS_SCALE ;set scale for shape drawing
09f7: a9 00 lda #$00
09f9: 8d ee 0b sta temp
09fc: 85 17 sta missile_ptr
09fe: 8d f3 0b sta abm_fired_cnt ;init score counters
0a01: 8d f4 0b sta abm_fired_cnt+1
0a04: 8d f1 0b sta mssl_destroy_cnt
0a07: 8d f2 0b sta mssl_destroy_cnt+1
0a0a: 8d 43 18 sta sound_fx_mod ;init sound effects
; Draw cities and launchers.
0a0d: ae ee 0b :DrawLoop ldx temp ;get start position
0a10: a0 00 ldy #$00
0a12: a9 9f lda #159 ;row 159 (just above text area)
0a14: 20 57 f4 jsr BAS_HPLOT0 ;plot a point
0a17: 20 7d 13 jsr InA_DrawShape ;draw a city at that position
0a1a: 4e 14 .dd2 shape_city
0a1c: ad ee 0b lda temp
0a1f: c9 fa cmp #250 ;did we draw the rightmost city?
0a21: b0 31 bcs :DrawText ;yes, done drawing
0a23: 69 1e adc #30 ;move right 30 pixels
0a25: 8d ee 0b sta temp
0a28: aa tax
0a29: a0 00 ldy #$00
0a2b: a9 9d lda #157
0a2d: 20 57 f4 jsr BAS_HPLOT0 ;plot a point
0a30: 20 7d 13 jsr InA_DrawShape ;draw the bottom of the launcher
0a33: b9 14 .dd2 shape_laun_bot
0a35: ad ee 0b lda temp
0a38: 18 clc
0a39: 69 06 adc #6 ;move right six pixels
0a3b: aa tax
0a3c: a0 00 ldy #$00
0a3e: a9 98 lda #152 ;come up 7 lines
0a40: 20 57 f4 jsr BAS_HPLOT0
0a43: 20 7d 13 jsr InA_DrawShape ;draw the top of the launcher
0a46: dd 14 .dd2 shape_laun_top
0a48: ad ee 0b lda temp
0a4b: 18 clc
0a4c: 69 14 adc #20 ;move right 20 pixels
0a4e: 8d ee 0b sta temp
0a51: 4c 0d 0a jmp :DrawLoop ;draw the next set
; Draw text area: city names, missile ratings, score.
0a54: 20 42 15 :DrawText jsr InXY_SetTextPos
0a57: 14 .dd1 20
0a58: 00 .dd1 0
0a59: 20 2b 15 jsr InS_PrintString
0a5c: a0 c2 cf d3+ .zstr “ BOS NYC PHL BAL WSH RCH”,$8d,“ 1 5 ”
+ “ 1 5 1”,$8d,“ ENEMY 0 0 ABM'S”
0ac3: a9 16 lda #22 ;set top of text window
0ac5: 85 22 sta MON_WNDTOP
0ac7: ad f0 0b lda auto_play_flag ;are we in attract mode?
0aca: f0 09 beq :NotAuto ;no, branch
0acc: a9 64 lda #$64 ;replace speaker click with harmless store
0ace: 8d 76 13 sta _Click+1 ; (attract mode is silent)
0ad1: a0 14 ldy #20 ;start with high spawn probability
0ad3: d0 07 bne :SetProb ;(always)
0ad5: a9 30 :NotAuto lda #$30
0ad7: 8d 76 13 sta _Click+1
0ada: a0 78 ldy #120 ;start with low spawn probability
0adc: 20 01 e3 :SetProb jsr BAS_SNGFLT ;convert Y-reg to float
0adf: 20 ac 15 jsr InA_StoreFloat ;save it
0ae2: f5 0b .dd2 spawn_prob
0ae4: a2 02 ldx #$02
0ae6: 20 ec f6 jsr BAS_HCOLOR+3 ;color=purple
0ae9: a2 00 ldx #$00
0aeb: a0 00 ldy #$00
0aed: a9 9f lda #159
0aef: 20 57 f4 jsr BAS_HPLOT0 ;plot at (0,159)
0af2: a2 01 ldx #1
0af4: a9 16 lda #22
0af6: a0 9f ldy #159
0af8: 20 3a f5 jsr BAS_HGLIN ;draw line to (278,159)
0afb: a2 03 ldx #$03
0afd: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
; Make a copy of the original row 155, so that as the game goes on we can
; compare the screen contents to the original state to evaluate damage.
0b00: a2 00 ldx #$00
0b02: a0 00 ldy #$00
0b04: a9 9b lda #155
0b06: 20 11 f4 jsr BAS_HPOSN ;get address of (0,155)
0b09: a0 00 ldy #$00
0b0b: b1 26 :Copy155Loop lda (BAS_HBASL),y
0b0d: 99 52 0b sta orig_row_155,y
0b10: c8 iny
0b11: c0 28 cpy #40 ;copy full 40-byte line
0b13: 90 f6 bcc :Copy155Loop
; Init launcher status to undamaged.
0b15: a0 00 ldy #$00
0b17: a9 98 lda #152
0b19: 99 7a 0b :InitLauLoop sta launcher_status,y
0b1c: c8 iny
0b1d: c0 05 cpy #$05 ;done with all five?
0b1f: d0 f8 bne :InitLauLoop
; Init city status to undamaged.
0b21: a9 ff lda #$ff
0b23: a0 00 ldy #$00
0b25: 99 4a 18 :InitCitLoop sta city_status,y
0b28: c8 iny
0b29: c0 06 cpy #$06 ;done with all six?
0b2b: d0 f8 bne :InitCitLoop
; Initialize missile data area from $4100-4dff. This is (mostly) zeroed out.
0b2d: a9 41 lda #$41
0b2f: 85 18 sta missile_ptr+1
0b31: e6 18 :InitMslLoop inc missile_ptr+1
0b33: a5 18 lda missile_ptr+1
0b35: c9 4e cmp #$4e ;reached last page?
0b37: 90 03 bcc :NotDone ;not yet, keep going
0b39: 4c 38 0c jmp GameLoop ;start the game
0b3c: 20 41 0b :NotDone jsr :InitPage
0b3f: d0 f0 bne :InitMslLoop ;(always)
; Zero the page pointed to by $17-18 to zeroes, then store $FF at +$FA.
0b41: a9 00 :InitPage lda #$00
0b43: a8 tay
0b44: 85 17 sta missile_ptr ;set low byte of pointer to zero
0b46: 91 17 :ZeroLoop sta (missile_ptr),y ;set 256 bytes to zero
0b48: c8 iny
0b49: d0 fb bne :ZeroLoop
0b4b: a0 fa ldy #O_ROW ;set missile status to inactive
0b4d: a9 ff lda #$ff
0b4f: 91 17 sta (missile_ptr),y
0b51: 60 rts
;
; Contents of hi-res row 155, which is 4 lines above the text window. The
; values here are compared against the current state to determine whether a city
; has been damaged. (When the on-screen value of the city center is zero, the
; city is considered destroyed.)
0b52: 00 00 00 00+ orig_row_155 .fill 40,$00
;
; Launcher status.
;
; All values are initialized to 152, possibly to indicate the hi-res row from
; which ABMs launch, but in practice this is treated as a bool (zero/nonzero)
; indicating whether the launcher is alive. It's also set to zero when the
; launcher fires.
0b7a: 00 00 00 00+ launcher_status .bulk $00,$00,$00,$00,$00
;
; Appears to be hi-res pixel column (0-255 of 279) from which ABMs launch from
; the five launchers.
0b7f: 24 56 88 ba+ launcher_posns .bulk $24,$56,$88,$ba,$ec
;
; After an ABM has finished exploding, check the state of all launchers of the
; same class (inner vs. outer). Redraw it if necessary, or mark it as disabled
; if it has been destroyed. The state of the launcher we just used will be $00
; (inactive), so the goal here is to decide if we want to re-enable it.
;
; We do this *after* the ABM has exploded, which means a "dead" launcher can
; fire one last missile before it's marked as inactive. (BUG?)
;
0b84: a0 fe CheckOuterLau ldy #$fe ;start with even-numbered launchers
0b86: d0 02 bne :ChkLauLoop ;(always)
0b88: a0 ff CheckInnerLau ldy #$ff ;start with odd-numbered launchers
0b8a: c8 :ChkLauLoop iny ;increment twice so we only check odds or evens
0b8b: c8 iny
0b8c: 8c e7 0b sty :_NextLauncher+1 ;save index
0b8f: b9 7a 0b lda launcher_status,y ;check status
0b92: d0 52 bne :_NextLauncher ;launcher is enabled, skip
0b94: a2 03 ldx #$03
0b96: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
0b99: ae e7 0b ldx :_NextLauncher+1 ;get index
0b9c: bd 7f 0b lda launcher_posns,x ;get pixel column
0b9f: aa tax
0ba0: a0 00 ldy #$00
0ba2: a9 9b lda #155
0ba4: 20 11 f4 jsr BAS_HPOSN ;get address for (xc,155)
0ba7: b1 26 lda (BAS_HBASL),y ;grab a byte at that address
0ba9: d0 0a bne :StillAlive ;nonzero, launcher is still alive
0bab: ac e7 0b ldy :_NextLauncher+1 ;get index
0bae: a9 00 lda #$00
0bb0: 99 7a 0b sta launcher_status,y ;mark launcher as dead (redundant)
0bb3: f0 31 beq :_NextLauncher ;(always)
0bb5: d9 52 0b :StillAlive cmp orig_row_155,y ;is launcher mid-line undamaged?
0bb8: f0 16 beq :NoDamage ;yes, no need to redraw bottom
0bba: ac e7 0b ldy :_NextLauncher+1
0bbd: b9 7f 0b lda launcher_posns,y ;get the horizontal offset
0bc0: 38 sec
0bc1: e9 06 sbc #$06 ;slide back 6 pixels
0bc3: aa tax
0bc4: a0 00 ldy #$00
0bc6: a9 9d lda #157
0bc8: 20 57 f4 jsr BAS_HPLOT0 ;plot a point at xc,157
0bcb: 20 7d 13 jsr InA_DrawShape ;draw the launcher bottom
0bce: b9 14 .dd2 shape_laun_bot
0bd0: ae e7 0b :NoDamage ldx :_NextLauncher+1
0bd3: bd 7f 0b lda launcher_posns,x ;get horizontal position
0bd6: 9d 7a 0b sta launcher_status,x ;mark launcher as alive (posn is always nonzer)
0bd9: aa tax ;copy horiz position to X-reg
0bda: a0 00 ldy #$00
0bdc: a9 98 lda #152
0bde: 20 57 f4 jsr BAS_HPLOT0 ;plot a point at xc,152
0be1: 20 7d 13 jsr InA_DrawShape ;draw the launcher top
0be4: dd 14 .dd2 shape_laun_top
0be6: a0 00 :_NextLauncher ldy #$00 ;get current value of counter (self-mod)
0be8: c0 05 cpy #$05 ;processed all appropriate launchers?
0bea: 90 9e bcc :ChkLauLoop ;not yet, branch
0bec: 60 rts
0bed: 00 page_counter .dd1 $00 ;game loop counts 0-10 for $4200-4c00
0bee: 00 00 temp .dd2 $0000 ;general-purpose temporary storage
0bf0: 00 auto_play_flag .dd1 $00 ;bool 00/ff: are we in auto-play (attract) mode?
mssl_destroy_cnt
0bf1: 00 00 .dd2 $0000 ;number of missiles destroyed
0bf3: 00 00 abm_fired_cnt .dd2 $0000 ;number of ABMs fired
0bf5: 88 48 00 00+ spawn_prob .bulk $88,$48,$00,$00,$00 ;fp: probability of missile spawn (lower=more likely)
0bfa: 88 00 00 00+ const_128 .bulk $88,$00,$00,$00,$00 ;fp: 128.0
0bff: 85 48 00 00+ const_25 .bulk $85,$48,$00,$00,$00 ;fp: 25.0
0c04: 87 48 00 00+ const_100 .bulk $87,$48,$00,$00,$00 ;fp: 100.0
0c09: 83 40 00 00+ const_6 .bulk $83,$40,$00,$00,$00 ;fp: 6.0
0c0e: 00 00 00 00+ .junk 10
0c18: 00 00 00 00+ xc_delta_temp .fill 5,$00 ;fp: temp storage for missile/abm delta X
0c1d: 00 00 00 00+ score_m_temp .fill 5,$00 ;fp: temporary storage while computing score
0c22: 00 00 00 00+ .junk 10
0c2c: 00 00 00 00+ score_temp .fill 5,$00 ;fp: temporary storage while computing score
0c31: 00 00 00 00+ high_score .fill 5,$00 ;fp: high score
0c36: 00 00 .junk 2
********************************************************************************
* Main game loop. *
* *
* Each iteration, the page counter is incremented, cycling through 0-10. On *
* cycles 0 through 5 we check to see if we want to spawn a missile. 6 through *
* 10 are reserved for MIRV spawns. (When a missile splits, the initial slot *
* plus 5 additional slots means all 6 cities can be targeted if no other MIRVs *
* are active.) *
* *
* Missiles are only updated when it's their turn, so each missile is advanced *
* once every 11 frames. *
********************************************************************************
0c38: a5 cb GameLoop lda strobe_thing? ;(no idea what the point of this is)
0c3a: 4d 40 c0 eor STROBE
0c3d: 85 cb sta strobe_thing?
0c3f: ee ed 0b inc page_counter
0c42: ad ed 0b lda page_counter
0c45: c9 0b cmp #11 ;did counter hit 11?
0c47: 90 09 bcc :SkipSound ;not yet, branch
0c49: 20 5c 13 jsr PlaySounds ;update sound effects
0c4c: a9 00 lda #$00 ;reset counter
0c4e: 8d ed 0b sta page_counter
0c51: 18 clc
0c52: 69 42 :SkipSound adc #$42 ;set pointer to $4200, $4300, ... $4C00
0c54: 85 18 sta missile_ptr+1
0c56: a9 00 lda #$00 ;set pointer low byte
0c58: 85 17 sta missile_ptr
0c5a: a0 fa ldy #O_ROW
0c5c: b1 17 lda (missile_ptr),y ;check current row
0c5e: c9 ff cmp #$ff ;is it $FF?
0c60: f0 03 beq :SkipUpdate ;yes, missile is inactive; branch
0c62: 4c fe 0c jmp :UpdateMissile ;go update this missile
0c65: ad ed 0b :SkipUpdate lda page_counter
0c68: c9 06 cmp #$06 ;are we on page 0-5?
0c6a: b0 0f bcs :NoSpawn ;no, don't spawn new missile here
0c6c: 20 ae ef jsr BAS_RND ;generate random [0,1)
0c6f: 20 91 15 jsr InA_MulFloat ;multiply by spawn probability
0c72: f5 0b .dd2 spawn_prob
0c74: 20 f2 eb jsr BAS_QINT ;convert to int in FAC1..FAC4
0c77: a5 a1 lda BAS_FAC+4 ;check least-significant byte
0c79: f0 03 beq :DoSpawn ;if it's zero, spawn a new missile
0c7b: 4c 56 13 :NoSpawn jmp JmpUpdateAbm
; Create a new missile in this slot. Start by increasing the probably of
; missile and MIRV spawns. This reaches its maximum after 110 missiles.
0c7e: 20 73 15 :DoSpawn jsr InA_LoadFloat ;get spawn probability in FAC
0c81: f5 0b .dd2 spawn_prob
0c83: 20 a6 15 jsr InA_CmpFloat ;compare to 10.0
0c86: 50 ea .dd2 BAS_CON_TEN
0c88: 30 0d bmi :NoDec ;FAC < 10, don't reduce further; branch
0c8a: 20 8b 15 jsr InA_SubFloat ;FAC = 1 - FAC
0c8d: 13 e9 .dd2 BAS_CON_ONE
0c8f: 20 d0 ee jsr BAS_NEGOP ;negate value
0c92: 20 ac 15 jsr InA_StoreFloat ;store it (essentially "FAC = FAC - 1")
0c95: f5 0b .dd2 spawn_prob
0c97: 20 ae ef :NoDec jsr BAS_RND ;random number [0,1)
0c9a: 20 91 15 jsr InA_MulFloat ;multiply by 128.0 [0,128)
0c9d: fa 0b .dd2 const_128
0c9f: 20 85 15 jsr InA_AddFloat ;add 6.0 (6,134)
0ca2: 09 0c .dd2 const_6
0ca4: 20 ac 15 jsr InA_StoreFloat ;save it as the horizontal start position
0ca7: e2 42 .dd2 missile_cur_xc
0ca9: 20 ae ef jsr BAS_RND ;random number [0,1)
0cac: 20 91 15 jsr InA_MulFloat ;multiply by spawn probability
0caf: f5 0b .dd2 spawn_prob
0cb1: 20 91 15 jsr InA_MulFloat ;multiply by 0.25
0cb4: 70 f0 .dd2 BAS_QUARTER
0cb6: 20 f2 eb jsr BAS_QINT ;convert to int
0cb9: a4 a1 ldy BAS_FAC+4 ;get value as int (yields 3.3% - 33% chance)
0cbb: d0 16 bne :SkipMirv ;not zero, don't make it a MIRV
; This missile is a MIRV. Determine the height at which it will split.
0cbd: 20 ae ef jsr BAS_RND ;random number [0,1)
0cc0: 20 91 15 jsr InA_MulFloat ;multiply by 100.0 [0,100)
0cc3: 04 0c .dd2 const_100
0cc5: 20 91 15 jsr InA_MulFloat ;multiply by 0.5 [0,50)
0cc8: 64 ee .dd2 BAS_CON_HALF
0cca: 20 f2 eb jsr BAS_QINT ;convert to int [0,49]
0ccd: a5 a1 lda BAS_FAC+4 ;get least-significant byte
0ccf: 69 19 adc #25 ;add 25 [25,74]
0cd1: d0 02 bne :SkipLoad ;(always)
; Set MIRV row and missile status.
0cd3: a9 00 :SkipMirv lda #$00
0cd5: a0 fd :SkipLoad ldy #O_MIRV_ROW
0cd7: 91 17 sta (missile_ptr),y
0cd9: a9 00 lda #$00
0cdb: a0 fa ldy #O_ROW
0cdd: 91 17 sta (missile_ptr),y ;mark missile as alive
; Target a city. The game ends when all cities are dead, so we shouldn't be
; here unless there's something to target.
0cdf: 20 ae ef jsr BAS_RND ;random number [0,1)
0ce2: 20 91 15 jsr InA_MulFloat ;multiply by 6.0 [0,6)
0ce5: 09 0c .dd2 const_6
0ce7: 20 f2 eb jsr BAS_QINT ;convert to int [0,5]
0cea: a4 a1 ldy BAS_FAC+4 ;start with this city
0cec: b9 4a 18 :ScanCities lda city_status,y ;is city alive?
0cef: d0 09 bne :CityPicked ;yes, branch
0cf1: c8 iny
0cf2: c0 06 cpy #$06 ;end of list?
0cf4: d0 f6 bne :ScanCities ;not yet, loop
0cf6: a0 00 ldy #$00 ;start over at start of list
0cf8: f0 f2 beq :ScanCities ;loop (always)
0cfa: 98 :CityPicked tya
0cfb: 20 a0 0e jsr TargetCity ;configure missile to fly toward city
;
; Update the missile. Start by seeing if we've reached terminal height.
;
0cfe: a0 fa :UpdateMissile ldy #O_ROW
0d00: b1 17 lda (missile_ptr),y ;get current row
0d02: 8d ee 0b sta temp ;save for later
0d05: a0 fb ldy #O_DET_ROW
0d07: b1 17 lda (missile_ptr),y ;get detonation row (why not CMP here?)
0d09: cd ee 0b cmp temp ;have we reached it?
0d0c: f0 02 beq :Explode ;yes, explode
0d0e: b0 03 bcs :MoveDown ;no, keep moving
0d10: 4c c4 0d :Explode jmp MissileExploding ;update explosion animation
; Advance the missile. We move twice each frame.
0d13: ee ee 0b :MoveDown inc temp ;move to next row
0d16: 20 5c 13 jsr PlaySounds
; Start by overwriting the white missile head with green trail.
0d19: a2 01 ldx #$01
0d1b: 20 ec f6 jsr BAS_HCOLOR+3 ;color=green
0d1e: 20 73 15 jsr InA_LoadFloat ;get current column
0d21: e2 42 .dd2 missile_cur_xc
0d23: 20 f2 eb jsr BAS_QINT ;convert to int
0d26: ac ee 0b ldy temp ;get row
0d29: 88 dey ;move up so we're at current row
0d2a: 98 tya
0d2b: a6 a1 ldx BAS_FAC+4 ;get current column as int
0d2d: 20 07 14 jsr DrawPixel ;overwrite white head with green
0d30: 20 73 15 jsr InA_LoadFloat ;get current column
0d33: e2 42 .dd2 missile_cur_xc
0d35: 20 85 15 jsr InA_AddFloat ;add movement delta
0d38: ee 42 .dd2 missile_xmove
0d3a: 20 ac 15 jsr InA_StoreFloat ;save it
0d3d: e2 42 .dd2 missile_cur_xc
0d3f: 20 f2 eb jsr BAS_QINT ;convert to int
0d42: ac ee 0b ldy temp ;get next row
0d45: a5 a1 lda BAS_FAC+4 ;get next column
0d47: 91 17 sta (missile_ptr),y ;save in pixel list
0d49: 98 tya
0d4a: a0 fa ldy #O_ROW
0d4c: 91 17 sta (missile_ptr),y ;update current row
0d4e: a6 a1 ldx BAS_FAC+4 ;get column
0d50: a8 tay
0d51: 20 07 14 jsr DrawPixel ;draw green pixel
0d54: c9 03 cmp #$03 ;was screen previously white?
0d56: f0 45 beq :Collision ;yes, ran into something
0d58: ee ee 0b inc temp ;move to next row (+2)
0d5b: ad ee 0b lda temp
0d5e: a0 fa ldy #O_ROW
0d60: 91 17 sta (missile_ptr),y ;update current row (again)
0d62: a2 03 ldx #$03
0d64: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
0d67: 20 73 15 jsr InA_LoadFloat ;get current column
0d6a: e2 42 .dd2 missile_cur_xc
0d6c: 20 85 15 jsr InA_AddFloat ;add movement delta
0d6f: ee 42 .dd2 missile_xmove
0d71: 20 ac 15 jsr InA_StoreFloat ;save it
0d74: e2 42 .dd2 missile_cur_xc
0d76: 20 f2 eb jsr BAS_QINT ;convert to int
0d79: ac ee 0b ldy temp ;get row
0d7c: a5 a1 lda BAS_FAC+4 ;get column
0d7e: 91 17 sta (missile_ptr),y ;save in pixel list
0d80: a6 a1 ldx BAS_FAC+4 ;get column (TAX?)
0d82: ad ee 0b lda temp ;get row (TYA?)
0d85: 20 07 14 jsr DrawPixel ;draw white pixel
0d88: c9 03 cmp #$03 ;was screen previously white?
0d8a: f0 11 beq :Collision ;yes, ran into something
0d8c: a0 fd ldy #O_MIRV_ROW
0d8e: b1 17 lda (missile_ptr),y ;get MIRV row
0d90: f0 05 beq :NotMirv ;zero, not a MIRV
0d92: cd ee 0b cmp temp ;have we reached the split point?
0d95: 90 03 bcc :SplitMirv ;yes, handle that
0d97: 4c 56 13 :NotMirv jmp JmpUpdateAbm
0d9a: 4c c5 11 :SplitMirv jmp SplitMirv
; Missile has collided with something. Disable or detonate.
0d9d: ad ee 0b :Collision lda temp ;get current row
0da0: a0 fb ldy #O_DET_ROW
0da2: 91 17 sta (missile_ptr),y ;set this as the detonation row
0da4: a0 fa ldy #O_ROW
0da6: 91 17 sta (missile_ptr),y ;and the current row
0da8: 20 ae ef jsr BAS_RND ;get randon number [0,1)
0dab: 20 91 15 jsr InA_MulFloat ;multiply by 10.0 [0,10)
0dae: 50 ea .dd2 BAS_CON_TEN
0db0: 20 f2 eb jsr BAS_QINT ;convert to int [0,9]
0db3: a5 a1 lda BAS_FAC+4 ;get least significant byte
0db5: f0 0d beq MissileExploding ;if zero, detonate (10% chance)
0db7: a0 f4 ldy #O_DET_SIZE ;otherwise, mark as exploded for recycling
0db9: b1 17 lda (missile_ptr),y ;get detonation size
0dbb: c8 iny ;O_DET_STAGE1
0dbc: 91 17 sta (missile_ptr),y ;init stage 1 size to "done"
0dbe: c8 iny ;O_DET_STAGE2
0dbf: 91 17 sta (missile_ptr),y ;init stage 2 size to "done"
0dc1: 4c 56 13 jmp JmpUpdateAbm ;done with missile
;
; Update missile explosion.
;
MissileExploding
0dc4: a0 f4 ldy #O_DET_SIZE
0dc6: b1 17 lda (missile_ptr),y ;get detonation size
0dc8: 8d ee 0b sta temp
0dcb: a0 f5 ldy #O_DET_STAGE1
0dcd: b1 17 lda (missile_ptr),y ;get stage 1 progress
0dcf: 8d c5 15 sta _DrawCircIndex+1
0dd2: cd ee 0b cmp temp ;are we finished?
0dd5: f0 37 beq :Stage2 ;yes, check stage 2
0dd7: c9 00 cmp #$00 ;have we started yet?
0dd9: d0 0c bne :InProgress1 ;yes, branch
0ddb: ad ee 0b lda temp ;no, tweak sound effects with det size
0dde: 4a lsr A ;divide by 8
0ddf: 4a lsr A
0de0: 4a lsr A
0de1: 6d 43 18 adc sound_fx_mod ;note carry set with last shifted bit
0de4: 8d 43 18 sta sound_fx_mod
0de7: a2 03 :InProgress1 ldx #$03
0de9: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
0dec: 20 73 15 jsr InA_LoadFloat ;get missile's horizontal position
0def: e2 42 .dd2 missile_cur_xc
0df1: 20 f2 eb jsr BAS_QINT ;convert to int
0df4: a0 fa ldy #O_ROW
0df6: b1 17 lda (missile_ptr),y ;get current row
0df8: a8 tay ;use as index
0df9: a5 a1 lda BAS_FAC+4 ;get column as int
0dfb: 91 17 sta (missile_ptr),y ;store in missile trail pixel list
0dfd: aa tax ;center column in X-reg
0dfe: 98 tya ;center row in A-reg
0dff: a0 10 ldy #$10 ;draw 16 more pixels
0e01: 20 b8 15 jsr DrawExplosion
0e04: a0 f5 ldy #O_DET_STAGE1
0e06: ad c5 15 lda _DrawCircIndex+1 ;get updated count
0e09: 91 17 sta (missile_ptr),y ;save it
0e0b: 4c 56 13 jmp JmpUpdateAbm ;done with missile
0e0e: a0 f6 :Stage2 ldy #O_DET_STAGE2
0e10: b1 17 lda (missile_ptr),y ;get stage 2 progress
0e12: 8d c5 15 sta _DrawCircIndex+1
0e15: cd ee 0b cmp temp ;are we finished?
0e18: f0 28 beq :DetComplete ;yes, go evaluate damage
0e1a: a2 00 ldx #$00
0e1c: 20 ec f6 jsr BAS_HCOLOR+3 ;color=black
0e1f: 20 73 15 jsr InA_LoadFloat ;get missile's horizontal position
0e22: e2 42 .dd2 missile_cur_xc
0e24: 20 f2 eb jsr BAS_QINT ;convert to int
0e27: a0 fa ldy #O_ROW
0e29: b1 17 lda (missile_ptr),y ;get current row
0e2b: a6 a1 ldx BAS_FAC+4 ;get column as int
0e2d: a0 10 ldy #$10 ;draw 16 more pixels
0e2f: 20 b8 15 jsr DrawExplosion
0e32: ce 43 18 dec sound_fx_mod ;update sound FX
0e35: ce 43 18 dec sound_fx_mod
0e38: a0 f6 ldy #O_DET_STAGE2
0e3a: ad c5 15 lda _DrawCircIndex+1 ;get updated count
0e3d: 91 17 sta (missile_ptr),y ;save it
0e3f: 4c 56 13 jmp JmpUpdateAbm ;done with missile
; Detonation complete. Redraw base line, update stats, and check effects.
0e42: a2 02 :DetComplete ldx #$02
0e44: 20 ec f6 jsr BAS_HCOLOR+3 ;color=purple
0e47: a2 00 ldx #$00
0e49: a0 00 ldy #$00
0e4b: a9 9f lda #159
0e4d: 20 57 f4 jsr BAS_HPLOT0 ;plot point at 0,159
0e50: a2 01 ldx #1
0e52: a9 16 lda #22
0e54: a0 9f ldy #159
0e56: 20 3a f5 jsr BAS_HGLIN ;draw line to 278,159
0e59: a2 00 ldx #$00
0e5b: 20 ec f6 jsr BAS_HCOLOR+3 ;set color to black
0e5e: ee f1 0b inc mssl_destroy_cnt ;increment the number of missiles destroyed
0e61: d0 03 bne :NoInc
0e63: ee f2 0b inc mssl_destroy_cnt+1
0e66: 20 5c 13 :NoInc jsr PlaySounds ;update sounds
0e69: 20 42 15 jsr InXY_SetTextPos ;set text position to score area
0e6c: 16 .dd1 22
0e6d: 0f .dd1 15
0e6e: ad f2 0b lda mssl_destroy_cnt+1 ;get destroyed count
0e71: ae f1 0b ldx mssl_destroy_cnt
0e74: 20 24 ed jsr BAS_LINPRT ;print it
0e77: 20 56 18 jsr EvalCityDamage
; Erase the missile trail.
0e7a: a0 fa ldy #O_ROW
0e7c: b1 17 lda (missile_ptr),y ;get final row
0e7e: 8d ef 0b sta temp+1 ;save it
0e81: a9 ff lda #$ff
0e83: 91 17 sta (missile_ptr),y ;mark missile slot as inactive
0e85: 8d ee 0b sta temp ;init counter to $ff so first inc makes it zero
0e88: ee ee 0b :EraseLoop inc temp
0e8b: ac ee 0b ldy temp ;get row
0e8e: cc ef 0b cpy temp+1 ;reached the bottom?
0e91: d0 03 bne :ErasePixel ;not yet
0e93: 4c 50 13 jmp GameLoopBottom ;yes, we're done
0e96: b1 17 :ErasePixel lda (missile_ptr),y ;get column we drew on for this row
0e98: aa tax
0e99: 98 tya
0e9a: 20 07 14 jsr DrawPixel ;draw black pixel here
0e9d: 4c 88 0e jmp :EraseLoop
;
; Configures a missile to target a specific city.
;
; On entry:
; A-reg: city to target (0-5)
;
0ea0: a0 fc TargetCity ldy #O_TARGET_CITY
0ea2: 91 17 sta (missile_ptr),y ;record the city being targeted
; Compute column of target city.
0ea4: 20 93 eb jsr BAS_FLOAT ;convert A-reg to float in FAC
0ea7: 20 91 15 jsr InA_MulFloat ;multiply by 25 (0-125)
0eaa: ff 0b .dd2 const_25
0eac: 20 85 15 jsr InA_AddFloat ;add 10 (10-135)
0eaf: 50 ea .dd2 BAS_CON_TEN
0eb1: 20 ac 15 jsr InA_StoreFloat
0eb4: e8 42 .dd2 missile_target_xc
; Compute size of missile explosion.
0eb6: 20 ae ef jsr BAS_RND ;generate random number [0,1)
0eb9: 20 91 15 jsr InA_MulFloat ;multiply by 6 [0,6)
0ebc: 09 0c .dd2 const_6
0ebe: 20 85 15 jsr InA_AddFloat ;add 6 [6,12)
0ec1: 09 0c .dd2 const_6
0ec3: 20 f2 eb jsr BAS_QINT ;convert to int [6,11]
0ec6: a5 a1 lda BAS_FAC+4 ;get least significant byte
0ec8: 18 clc
0ec9: 0a asl A ;multiply by 16 [96,176]
0eca: 0a asl A
0ecb: 0a asl A
0ecc: 0a asl A
0ecd: a0 f4 ldy #O_DET_SIZE
0ecf: 91 17 sta (missile_ptr),y
; Pick the detonation height. We must detonate above the level of the city, or
; the collision with a lit pixel will kill the missile.
;
; Note we pick our numbers in such a way that deltaX is < 1.0, so we're never in
; a position where we have to draw two pixels on the same row.
0ed1: 20 ae ef jsr BAS_RND ;generate random number [0,1)
0ed4: 20 91 15 jsr InA_MulFloat ;multiply by 10 [0,10)
0ed7: 50 ea .dd2 BAS_CON_TEN
0ed9: 20 f2 eb jsr BAS_QINT ;convert to int [0,9]
0edc: a5 a1 lda BAS_FAC+4 ;get least significant byte
0ede: 49 ff eor #$ff ;invert [$f6,$ff]
0ee0: 69 96 adc #150 ;add 150 [140,149]
0ee2: a0 fb ldy #O_DET_ROW
0ee4: 91 17 sta (missile_ptr),y
; Init explosion trackers.
0ee6: a9 00 lda #$00
0ee8: a0 f5 ldy #O_DET_STAGE1
0eea: 91 17 sta (missile_ptr),y
0eec: c8 iny
0eed: 91 17 sta (missile_ptr),y
; Compute how far the missile moves horizontally each frame.
0eef: 20 73 15 jsr InA_LoadFloat ;get starting X coordinate
0ef2: e2 42 .dd2 missile_cur_xc
0ef4: 20 8b 15 jsr InA_SubFloat ;subtract target X coordinate
0ef7: e8 42 .dd2 missile_target_xc
0ef9: 20 ac 15 jsr InA_StoreFloat ;save delta X
0efc: 18 0c .dd2 xc_delta_temp
0efe: a0 fa ldy #O_ROW
0f00: b1 17 lda (missile_ptr),y ;get missile's row (will be nonzero for MIRV)
0f02: 49 ff eor #$ff ;invert
0f04: 18 clc
0f05: 69 a0 adc #160 ;add 160 to get approximate delta Y
0f07: a8 tay
0f08: 20 01 e3 jsr BAS_SNGFLT ;convert to float in FAC
0f0b: 20 97 15 jsr InA_DivFloat ;FAC = deltaX / deltaY
0f0e: 18 0c .dd2 xc_delta_temp
0f10: 20 ac 15 jsr InA_StoreFloat ;save as per-frame horizontal movement
0f13: ee 42 .dd2 missile_xmove
; Record initial position.
0f15: 20 73 15 jsr InA_LoadFloat
0f18: e2 42 .dd2 missile_cur_xc ;get start column
0f1a: 20 f2 eb jsr BAS_QINT ;convert to int
0f1d: a0 fa ldy #O_ROW
0f1f: b1 17 lda (missile_ptr),y ;get missile's row
0f21: a8 tay ;use as index
0f22: a5 a1 lda BAS_FAC+4 ;get column as int
0f24: 91 17 sta (missile_ptr),y ;save it so we can erase it later
0f26: 60 rts
;
; Update the ABM, or check user input if none is in flight.
;
0f27: ad fa 4d UpdateAbmMaybe lda abm_row ;is an ABM in flight?
0f2a: c9 ff cmp #$ff
0f2c: f0 03 beq :GetInput ;no, get user input
0f2e: 4c 6f 10 jmp UpdateAbm ;yes, update it
0f31: ad f0 0b :GetInput lda auto_play_flag ;are we in attract mode?
0f34: f0 03 beq ReadUserInput ;no, get user input
0f36: 4c 85 12 jmp AutoPlay ;yes, auto-play
;
; Reads paddles and buttons.
;
; To avoid slowing the game down with excessive controller reads, and also to
; mitigate paddle cross-talk, the paddle reads are distributed across frames.
; We do two frames with no reads, one frame for paddle X, and one frame for
; paddle Y.
;
; The cursor is only on-screen while we are reading the paddles, which is why
; it's a flickery mess.
;
; This is not called while an ABM is in flight.
;
0f39: ee 3d 0f ReadUserInput inc :_IgnorePaddle+1
0f3c: a2 00 :_IgnorePaddle ldx #$00 ;do we want to check the paddles this time?
0f3e: 30 2d bmi :SkipPaddles ;no, skip it
0f40: d0 11 bne :ReadY ;if it's #$01, read the Y axis
0f42: 20 86 13 jsr DrawCursor ;draw the cursor
0f45: 20 5c 13 jsr PlaySounds ;play sound effects
0f48: 20 00 03 jsr ReadPdlX ;read X-axis paddle
0f4b: 4a lsr A ;halve it; note LSB is now in carry
0f4c: 69 06 adc #$06 ;add 6 to get [6,134]
0f4e: 8d 79 13 sta cursor_x
0f51: d0 14 bne :Cont ;(always)
0f53: 20 86 13 :ReadY jsr DrawCursor ;draw the cursor
0f56: 20 5c 13 jsr PlaySounds ;play sound effects
0f59: 20 03 03 jsr ReadPdlY ;read Y-axis paddle
0f5c: 4a lsr A ;halve it; note LSB is now in carry
0f5d: 69 03 adc #$03 ;add 3 to get [3,131]
0f5f: 8d 7a 13 sta cursor_y
0f62: a9 fd lda #$fd ;reset counter to -3
0f64: 8d 3d 0f sta :_IgnorePaddle+1
0f67: 20 5c 13 :Cont jsr PlaySounds ;play sound effects
0f6a: 20 be 13 jsr EraseCursor ;erase the cursor
0f6d: 20 06 03 :SkipPaddles jsr GetOuterBtn ;button for outer/center launchers pressed?
0f70: 90 09 bcc :NotOuter ;no, branch
0f72: a9 50 FireOuter lda #$50 ;explosion with 80 pixels
0f74: 8d f4 4d sta abm_max_size
0f77: a0 fe ldy #$fe ;check even-numbered launchers
0f79: d0 0f bne :FireCommon ;(always)
0f7b: 20 09 03 :NotOuter jsr GetInnerBtn ;button for inner launchers pressed?
0f7e: b0 03 bcs FireInner ;yes, fire
0f80: 4c 50 13 jmp GameLoopBottom
0f83: a9 70 FireInner lda #$70 ;explosion with 112 pixels
0f85: 8d f4 4d sta abm_max_size
0f88: a0 ff ldy #$ff ;check odd-numbered launchers
0f8a: 8c bc 0f :FireCommon sty :_Closest+1
0f8d: a9 64 lda #100 ;max horizontal delta for launcher
0f8f: 8d af 0f sta :_Val+1 ; (can't fire farther away than that)
0f92: c8 :NextLauncher iny ;incr twice so we check odd or even
0f93: c8 iny
0f94: c0 05 cpy #$05
0f96: b0 23 bcs :_Closest
0f98: b9 7a 0b lda launcher_status,y ;get launcher status
0f9b: f0 f5 beq :NextLauncher ;not available, branch
0f9d: b9 7f 0b lda launcher_posns,y ;get launcher position
0fa0: 4a lsr A ;halve it to get color coordinates (0-139)
0fa1: 38 sec
0fa2: ed 79 13 sbc cursor_x ;subtract cursor X position
0fa5: c9 00 cmp #$00 ;(?)
0fa7: 10 05 bpl :_Val ;positive, branch
0fa9: 49 ff eor #$ff ;negative, invert to get absolute value
0fab: 38 sec
0fac: 69 00 adc #$00 ;add 1
0fae: c9 00 :_Val cmp #$00 ;is it closer than previous value?
0fb0: b0 e0 bcs :NextLauncher ;no, check next
0fb2: 8d af 0f sta :_Val+1 ;yes, this is closest; update
0fb5: 8c bc 0f sty :_Closest+1 ;save launcher index
0fb8: 4c 92 0f jmp :NextLauncher ;check next
0fbb: a0 00 :_Closest ldy #$00 ;did we find a launcher?
0fbd: 10 03 bpl :FireAbm ;yes, fire ABM
0fbf: 4c 50 13 jmp GameLoopBottom ;no, nothing was in range
0fc2: a9 00 :FireAbm lda #$00
0fc4: 99 7a 0b sta launcher_status,y ;mark launcher as unavailable
0fc7: b9 7f 0b lda launcher_posns,y ;get launcher position
0fca: 8d f0 0f sta :_LaunchCol+1 ;set as horizontal coordinate for HPOSN
0fcd: 4a lsr A ;divide by two
0fce: a8 tay
0fcf: c8 iny ;add two
0fd0: c8 iny
0fd1: 20 01 e3 jsr BAS_SNGFLT ;convert to float
0fd4: 20 ac 15 jsr InA_StoreFloat
0fd7: e2 4d .dd2 abm_cur_xc
0fd9: ad 7a 13 lda cursor_y ;get target row
0fdc: 8d fb 4d sta abm_target_row ;save it
0fdf: ac 79 13 ldy cursor_x ;get target column
0fe2: 20 01 e3 jsr BAS_SNGFLT ;convert to float
0fe5: 20 ac 15 jsr InA_StoreFloat ;save it
0fe8: e8 4d .dd2 abm_target_xc
0fea: a2 00 ldx #$00
0fec: 20 ec f6 jsr BAS_HCOLOR+3 ;color=black
0fef: a2 00 :_LaunchCol ldx #$00 ;starting pixel column
0ff1: a9 98 lda #152 ;starting row
0ff3: 8d fa 4d sta abm_row ;save it
0ff6: ce fa 4d dec abm_row ;move up one row
0ff9: a0 00 ldy #$00
0ffb: 20 11 f4 jsr BAS_HPOSN ;get address of position
0ffe: 20 7d 13 jsr InA_DrawShape ;erase launcher top
1001: dd 14 .dd2 shape_laun_top
1003: a9 00 lda #$00
1005: 8d f5 4d sta abm_det_size1 ;init detonation counters
1008: 8d f6 4d sta abm_det_size2
100b: 20 73 15 jsr InA_LoadFloat ;get start column
100e: e2 4d .dd2 abm_cur_xc
1010: 20 8b 15 jsr InA_SubFloat ;subtract from target column
1013: e8 4d .dd2 abm_target_xc
1015: 20 ac 15 jsr InA_StoreFloat ;save delta X
1018: 18 0c .dd2 xc_delta_temp
101a: ad fa 4d lda abm_row ;get start row
101d: 38 sec
101e: ed fb 4d sbc abm_target_row ;subtract target row (delta Y)
1021: a8 tay
1022: 20 01 e3 jsr BAS_SNGFLT ;convert to float in FAC
1025: 20 97 15 jsr InA_DivFloat ;FAC = deltaX / deltaY
1028: 18 0c .dd2 xc_delta_temp
102a: 20 ac 15 jsr InA_StoreFloat ;save X movement
102d: ee 4d .dd2 abm_xmove
102f: 20 af eb jsr BAS_ABS ;compute absolute value
1032: 20 a6 15 jsr InA_CmpFloat ;compare to 1.0
1035: 13 e9 .dd2 BAS_CON_ONE
1037: 30 03 bmi :GoodAngle ;less than 1.0, keep going
1039: 4c a1 11 jmp StopAbm ;angle too shallow, won't draw right, disallow
103c: 20 73 15 :GoodAngle jsr InA_LoadFloat ;get initial column
103f: e2 4d .dd2 abm_cur_xc
1041: 20 f2 eb jsr BAS_QINT ;convert to int
1044: ac fa 4d ldy abm_row ;get current row
1047: a5 a1 lda BAS_FAC+4 ;get initial column as int
1049: 99 00 4d sta abm_trail,y ;record position in trail record
104c: ee f3 0b inc abm_fired_cnt ;increment number of ABMs fired
104f: d0 03 bne :NoInc
1051: ee f4 0b inc abm_fired_cnt+1
1054: 20 42 15 :NoInc jsr InXY_SetTextPos
1057: 16 .dd1 22 ;status row
1058: 14 .dd1 20
1059: ae f3 0b ldx abm_fired_cnt ;get number of ABMs fired
105c: ad f4 0b lda abm_fired_cnt+1
105f: 20 24 ed jsr BAS_LINPRT ;print value
1062: 20 2b 15 jsr InS_PrintString
1065: a0 c1 c2 cd+ .zstr “ ABM'S”
106c: 4c 50 13 jmp GameLoopBottom
;
; Updates ABM movement.
;
106f: 20 5c 13 UpdateAbm jsr PlaySounds
1072: ad fb 4d lda abm_target_row ;get target row
1075: cd fa 4d cmp abm_row ;compare to current row
1078: 90 03 bcc :StillFlying ;not there yet, branch
107a: 4c ef 10 jmp AbmExploding ;detonate!
; Move the ABM up two rows.
107d: ce fa 4d :StillFlying dec abm_row ;reduce row (move toward top of screen)
1080: a2 02 ldx #$02
1082: 20 ec f6 jsr BAS_HCOLOR+3 ;color=purple
1085: ac fa 4d ldy abm_row ;get next row
1088: c8 iny ;inc to get current row
1089: 98 tya
108a: be 00 4d ldx abm_trail,y ;get pixel position
108d: 20 07 14 jsr DrawPixel ;draw purple pixel on top of white head
1090: 20 73 15 jsr InA_LoadFloat ;get current column
1093: e2 4d .dd2 abm_cur_xc
1095: 20 85 15 jsr InA_AddFloat ;add movement delta
1098: ee 4d .dd2 abm_xmove
109a: 20 ac 15 jsr InA_StoreFloat ;save updated position
109d: e2 4d .dd2 abm_cur_xc
109f: 20 f2 eb jsr BAS_QINT ;convert to integer
10a2: ac fa 4d ldy abm_row ;get current row
10a5: a5 a1 lda BAS_FAC+4 ;get column as integer
10a7: 99 00 4d sta abm_trail,y ;save in ABM trail list
10aa: aa tax
10ab: 98 tya
10ac: 20 07 14 jsr DrawPixel ;draw trail
10af: c9 03 cmp #$03 ;did we hit something?
10b1: f0 36 beq :AbmCollision ;yes, branch
10b3: ce fa 4d dec abm_row ;move down a row
10b6: a2 03 ldx #$03
10b8: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
10bb: 20 73 15 jsr InA_LoadFloat ;get current column
10be: e2 4d .dd2 abm_cur_xc
10c0: 20 85 15 jsr InA_AddFloat ;add movement delta
10c3: ee 4d .dd2 abm_xmove
10c5: 20 ac 15 jsr InA_StoreFloat ;save updated position
10c8: e2 4d .dd2 abm_cur_xc
10ca: 20 f2 eb jsr BAS_QINT ;convert to integer
10cd: ac fa 4d ldy abm_row ;get current row
10d0: a5 a1 lda BAS_FAC+4 ;get column as integer
10d2: 99 00 4d sta abm_trail,y ;save in ABM trail list
10d5: aa tax
10d6: 98 tya
10d7: 20 07 14 jsr DrawPixel ;draw missile head
10da: c9 03 cmp #$03 ;did we hit something?
10dc: f0 0b beq :AbmCollision ;yes, branch
10de: 20 5c 13 jsr PlaySounds
10e1: a9 28 lda #$28 ;sleep for ~4.5 msec (feels a little smoother
10e3: 20 a8 fc jsr MON_WAIT ; when things aren't too busy)
10e6: 4c 50 13 jmp GameLoopBottom
10e9: ad fa 4d :AbmCollision lda abm_row ;set current = target so we know ABM is exploding
10ec: 8d fb 4d sta abm_target_row
10ef: 20 5c 13 AbmExploding jsr PlaySounds
10f2: ad f5 4d lda abm_det_size1 ;check stage 1 detonation size
10f5: 8d c5 15 sta _DrawCircIndex+1
10f8: cd f4 4d cmp abm_max_size ;done yet?
10fb: f0 34 beq :Stage2 ;yes, branch
10fd: c9 00 cmp #$00 ;initial detonation?
10ff: d0 0b bne :NotFirst ;no, skip sound adjust
1101: ad f4 4d lda abm_max_size ;get max size
1104: 4a lsr A ;divide by 2
1105: 18 clc
1106: 6d 43 18 adc sound_fx_mod ;tweak sound effects
1109: 8d 43 18 sta sound_fx_mod
110c: a2 03 :NotFirst ldx #$03
110e: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
1111: 20 73 15 jsr InA_LoadFloat ;get current column
1114: e2 4d .dd2 abm_cur_xc
1116: 20 f2 eb jsr BAS_QINT ;convert to int
1119: ac fa 4d ldy abm_row ;get current row
111c: a5 a1 lda BAS_FAC+4 ;get current col as int
111e: 99 00 4d sta abm_trail,y ;save in trail list
1121: aa tax
1122: 98 tya
1123: a0 08 ldy #$08 ;expand circle by 8 pixels
1125: 20 b8 15 jsr DrawExplosion ;draw it
1128: ad c5 15 lda _DrawCircIndex+1 ;get updated count
112b: 8d f5 4d sta abm_det_size1 ;save it
112e: 4c 50 13 jmp GameLoopBottom
1131: ad f6 4d :Stage2 lda abm_det_size2 ;get stage 2 detonation size
1134: 8d c5 15 sta _DrawCircIndex+1
1137: cd f4 4d cmp abm_max_size ;done yet?
113a: f0 2c beq :ExplodeDone ;yes, go clean up
113c: a2 00 ldx #$00
113e: 20 ec f6 jsr BAS_HCOLOR+3 ;color=black
1141: 20 73 15 jsr InA_LoadFloat ;get current column
1144: e2 4d .dd2 abm_cur_xc
1146: 20 f2 eb jsr BAS_QINT ;convert to int
1149: ad fa 4d lda abm_row ;get current row
114c: a6 a1 ldx BAS_FAC+4 ;get current col as int
114e: a0 08 ldy #$08 ;expand circle by 8 pixels
1150: 20 b8 15 jsr DrawExplosion ;draw it
1153: ce 43 18 dec sound_fx_mod ;update sound effects
1156: ce 43 18 dec sound_fx_mod
1159: ce 43 18 dec sound_fx_mod
115c: ce 43 18 dec sound_fx_mod
115f: ad c5 15 lda _DrawCircIndex+1 ;get updated count
1162: 8d f6 4d sta abm_det_size2 ;save it
1165: 4c 50 13 jmp GameLoopBottom
1168: 20 5c 13 :ExplodeDone jsr PlaySounds
; Redraw base line, then erase trail.
116b: a2 02 ldx #$02
116d: 20 ec f6 jsr BAS_HCOLOR+3 ;color=purple
1170: a2 00 ldx #$00
1172: a0 00 ldy #$00
1174: a9 9f lda #159
1176: 20 57 f4 jsr BAS_HPLOT0 ;plot point at 0,159
1179: a2 01 ldx #1
117b: a9 16 lda #22
117d: a0 9f ldy #159
117f: 20 3a f5 jsr BAS_HGLIN ;draw line to 278,159
1182: a2 00 ldx #$00
1184: 20 ec f6 jsr BAS_HCOLOR+3 ;color=black
1187: a9 99 lda #153 ;start row is 152, add 1 to compensate for
1189: 8d ee 0b sta temp ; initial DEC
118c: ce ee 0b :Loop dec temp
118f: ac ee 0b ldy temp ;get row
1192: cc fa 4d cpy abm_row ;reached end of trail?
1195: 90 0a bcc StopAbm ;yes, branch
1197: be 00 4d ldx abm_trail,y ;get pixel column
119a: 98 tya
119b: 20 07 14 jsr DrawPixel ;draw black pixel
119e: 4c 8c 11 jmp :Loop
; ABM is done exploding, reset state and check launcher.
11a1: a9 ff StopAbm lda #$ff
11a3: 8d fa 4d sta abm_row ;mark ABM as inactive
11a6: a2 03 ldx #$03
11a8: 20 ec f6 jsr BAS_HCOLOR+3 ;color=white
11ab: ad f4 4d lda abm_max_size ;get size of ABM we just fired
11ae: c9 50 cmp #$50 ;from outer/center launcher?
11b0: d0 06 bne :InnerLauncher ;no, branch
11b2: 20 84 0b jsr CheckOuterLau
11b5: 4c 50 13 jmp GameLoopBottom
11b8: 20 88 0b :InnerLauncher jsr CheckInnerLau
11bb: 4c 50 13 jmp GameLoopBottom
temp_missile_col
11be: 00 00 00 00+ .fill 5,$00 ;fp: temp storage for missile column
temp_missile_row
11c3: 00 .dd1 $00
11c4: 00 temp_city_off .dd1 $00
;
; Split one missile into up to six, one per city.
;
; Note this does NOT zero out the MIRV split row, so the original missile will
; try to split continuously, limited only by slot availability. (Bug?)
;
11c5: 20 73 15 SplitMirv jsr InA_LoadFloat ;get missile's column
11c8: e2 42 .dd2 missile_cur_xc
11ca: 20 ac 15 jsr InA_StoreFloat ;save in temp
11cd: be 11 .dd2 temp_missile_col
11cf: a0 fa ldy #O_ROW
11d1: b1 17 lda (missile_ptr),y ;get missile's row
11d3: 18 clc
11d4: e9 00 sbc #$00 ;subtract 1
11d6: 8d c3 11 sta temp_missile_row ;save in temp
11d9: a9 02 lda #$02
11db: a0 fc ldy #O_TARGET_CITY ;set target city (redundant; target city
11dd: 91 17 sta (missile_ptr),y ; subroutine does this too)
11df: 20 a0 0e jsr TargetCity ;configure original missile to target city #2
11e2: a5 17 lda missile_ptr ;get current page pointer
11e4: 8d 7b 12 sta :_RestorePtr+1 ;save it off
11e7: a9 fd lda #$fd ;init city offset to -3
11e9: 8d c4 11 sta temp_city_off
11ec: a9 47 lda #$47 ;start at first page reserved for MIRVs ($48xx)
11ee: 85 18 sta missile_ptr+1
11f0: e6 18 :MirvLoop inc missile_ptr+1 ;advance to next page
11f2: a5 18 lda missile_ptr+1
11f4: c9 4d cmp #$4d ;did we reach end of slots?
11f6: 90 03 bcc :Cont ;no, branch
11f8: 4c 7a 12 jmp :_RestorePtr ;yes, jump to end
11fb: ee c4 11 :Cont inc temp_city_off ;increment the count
11fe: ad c4 11 lda temp_city_off
1201: d0 05 bne :NotZero ;not zero, branch
1203: a9 01 lda #$01 ;skip zero, go straight to +1
1205: 8d c4 11 sta temp_city_off
1208: 20 93 eb :NotZero jsr BAS_FLOAT ;convert to float in FAC
120b: 20 85 15 jsr InA_AddFloat ;add to missile column
120e: be 11 .dd2 temp_missile_col
1210: a0 fa ldy #O_ROW
1212: b1 17 lda (missile_ptr),y ;get row for this missile
1214: c9 ff cmp #$ff ;is it available?
1216: d0 d8 bne :MirvLoop ;no, move to next slot
1218: 20 ac 15 jsr InA_StoreFloat ;set column for this missile
121b: e2 42 .dd2 missile_cur_xc
121d: 20 f2 eb jsr BAS_QINT ;convert to integer
1220: a0 fa ldy #O_ROW
1222: ad c3 11 lda temp_missile_row ;get row - 1
1225: 91 17 sta (missile_ptr),y ;set missile's row
1227: a8 tay ;use row as index
1228: a5 a1 lda BAS_FAC+4 ;get column as int
122a: 91 17 sta (missile_ptr),y ;save column in trail list
122c: a6 a1 ldx BAS_FAC+4 ;get column as int (TAX?)
122e: ad c3 11 lda temp_missile_row ;get row - 1
1231: 8e 59 12 stx _Column+1 ;set column
1234: 20 07 14 jsr DrawPixel ;draw white pixel
1237: ad c4 11 lda temp_city_off ;get offset
123a: 18 clc
123b: 69 02 adc #$02 ;add 2
123d: a0 fc ldy #O_TARGET_CITY
123f: 91 17 sta (missile_ptr),y ;set as target city
1241: 20 a0 0e jsr TargetCity ;configure missile to hit city
1244: 20 73 15 jsr InA_LoadFloat ;get X movement delta
1247: ee 42 .dd2 missile_xmove
1249: 20 af eb jsr BAS_ABS ;compute absolute value
124c: 20 a6 15 jsr InA_CmpFloat ;compare to 1.0
124f: 13 e9 .dd2 BAS_CON_ONE
1251: 30 21 bmi :PathGood ;it's less, keep it
; MIRV path is too shallow, kill it, and give up.
1253: a2 00 ldx #$00
1255: 20 ec f6 jsr BAS_HCOLOR+3 ;color=black
1258: a2 00 _Column ldx #$00 ;missile column
125a: ad c3 11 lda temp_missile_row ;missile row
125d: 20 07 14 jsr DrawPixel ;erase head
1260: a9 ff lda #$ff
1262: a0 fa ldy #O_ROW
1264: 91 17 sta (missile_ptr),y ;mark missile as inactive
1266: ad 7b 12 lda :_RestorePtr+1 ;restore previous pointer value
1269: 85 18 sta missile_ptr+1 ;(redundant with code below)
126b: a9 00 lda #$00
126d: a0 fd ldy #O_MIRV_ROW
126f: 91 17 sta (missile_ptr),y ;set MIRV row to zero
1271: 4c 7a 12 jmp :_RestorePtr
1274: 20 5c 13 :PathGood jsr PlaySounds
1277: 4c f0 11 jmp :MirvLoop ;go make another
127a: a9 00 :_RestorePtr lda #$00 ;get previous pointer value
127c: 85 18 sta missile_ptr+1 ;restore
127e: 4c 38 0c jmp GameLoop
1281: 00 auto_curs_x .dd1 $00 ;random value, from 6 to 133
1282: 00 auto_curs_y .dd1 $00 ;random value, from 6 to 133
1283: 00 00 .junk 2
;
; Controls the player in "attract mode", moving the cursor and firing ABMs.
;
1285: ad 00 c0 AutoPlay lda KBD ;check keyboard
1288: 10 03 bpl :DoPlay ;no key hit, keep going
128a: 4c 13 08 jmp Restart ;key hit, jump back to title screen
128d: 20 86 13 :DoPlay jsr DrawCursor ;draw cursor
1290: 20 ae ef jsr BAS_RND ;get random [0,1)
1293: 20 91 15 jsr InA_MulFloat ;multiply by 128 [0,128)
1296: fa 0b .dd2 const_128
1298: 20 f2 eb jsr BAS_QINT ;convert to integer [0,127]
129b: a5 a1 lda BAS_FAC+4 ;get value as int
129d: f0 7a beq :MoveFireOuter ;if zero, fire outer, move to new position
129f: c9 01 cmp #$01
12a1: f0 7f beq :MoveFireInner ;if 1, fire inner, move to new position
12a3: c9 08 cmp #$08
12a5: 90 32 bcc :AddWobble ;if 2-7, ?
12a7: 29 01 and #$01 ;odd or even?
12a9: f0 14 beq :MoveY ;even, move Y only
12ab: ad 79 13 lda cursor_x ;get current cursor X position
12ae: 38 sec
12af: ed 81 12 sbc auto_curs_x ;subtract desired position
12b2: f0 1f beq :MoveDone ;already there, bail
12b4: 30 06 bmi :MoveRight ;which way do we need to go?
12b6: ce 79 13 dec cursor_x ;move left
12b9: 4c bf 12 jmp :MoveY
12bc: ee 79 13 :MoveRight inc cursor_x ;move right
12bf: ad 7a 13 :MoveY lda cursor_y ;get current cursor Y position
12c2: 38 sec
12c3: ed 82 12 sbc auto_curs_y ;subtract desired position
12c6: f0 0b beq :MoveDone ;already there, bail
12c8: 30 06 bmi :MoveDown
12ca: ce 7a 13 dec cursor_y ;move up
12cd: 4c d3 12 jmp :MoveDone
12d0: ee 7a 13 :MoveDown inc cursor_y
12d3: 20 be 13 :MoveDone jsr EraseCursor ;erase the cursor
12d6: 4c 38 0c jmp GameLoop
;
; Add a bit of wobble to the cursor position.
;
; The computation seems wrong. It's calculating:
; (PI/2) - (PI/2) - (2*PI*random)
; which is equivalent to:
; -2*PI*random
;
; I think the author probably wanted PI - (2*PI*random), to get an integer value
; in the range [-3,3]. But he only had 2*PI and 0.5*PI, and tried to double-up
; the latter.
;
; There doesn't seem to be a point in using PI other than as a convenient
; constant.
;
12d9: 20 ae ef :AddWobble jsr BAS_RND ;get random number [0,1)
12dc: 20 91 15 jsr InA_MulFloat ;multiply by PI*2 [0,~6.28)
12df: 6b f0 .dd2 BAS_CON_PI_DOUB
12e1: 20 8b 15 jsr InA_SubFloat ;subtract from PI/2
12e4: 66 f0 .dd2 BAS_CON_PI_HALF
12e6: 20 8b 15 jsr InA_SubFloat ;again
12e9: 66 f0 .dd2 BAS_CON_PI_HALF
12eb: 20 f2 eb jsr BAS_QINT ;convert to integer [0,6]
12ee: a5 a1 lda BAS_FAC+4 ;get value as int
12f0: 6d 79 13 adc cursor_x ;add to cursor X position
12f3: 8d 79 13 sta cursor_x
12f6: 20 ae ef jsr BAS_RND ;repeat for Y position
12f9: 20 91 15 jsr InA_MulFloat ; ...
12fc: 6b f0 .dd2 BAS_CON_PI_DOUB
12fe: 20 8b 15 jsr InA_SubFloat
1301: 66 f0 .dd2 BAS_CON_PI_HALF
1303: 20 85 15 jsr InA_AddFloat
1306: 66 f0 .dd2 BAS_CON_PI_HALF
1308: 20 d0 ee jsr BAS_NEGOP
130b: 20 f2 eb jsr BAS_QINT
130e: a5 a1 lda BAS_FAC+4
1310: 6d 7a 13 adc cursor_y
1313: 8d 7a 13 sta cursor_y
1316: 4c d3 12 jmp :MoveDone
1319: 20 2b 13 :MoveFireOuter jsr RandomCursorPos ;pick new position
131c: 20 be 13 jsr EraseCursor ;erase cursor
131f: 4c 72 0f jmp FireOuter ;fire ABM from outer/center silo
1322: 20 2b 13 :MoveFireInner jsr RandomCursorPos ;pick new position
1325: 20 be 13 jsr EraseCursor ;erase cursor
1328: 4c 83 0f jmp FireInner ;fire ABM from inner silo
; Pick a random X,Y position for the cursor.
132b: 20 ae ef RandomCursorPos jsr BAS_RND ;get random [0,1)
132e: 20 91 15 jsr InA_MulFloat ;multiply by 128.0 [0,128)
1331: fa 0b .dd2 const_128
1333: 20 f2 eb jsr BAS_QINT ;convert to integer [0,127]
1336: a5 a1 lda BAS_FAC+4
1338: 69 06 adc #$06 ;add 6 [6,133]
133a: 8d 81 12 sta auto_curs_x ;save it
133d: 20 ae ef jsr BAS_RND ;do it again
1340: 20 91 15 jsr InA_MulFloat
1343: fa 0b .dd2 const_128
1345: 20 f2 eb jsr BAS_QINT
1348: a5 a1 lda BAS_FAC+4
134a: 69 06 adc #$06 ;(should be 3?)
134c: 8d 82 12 sta auto_curs_y ;save that too
134f: 60 rts
;
; Play sounds, jump to top of game loop.
;
1350: 20 5c 13 GameLoopBottom jsr PlaySounds
1353: 4c 38 0c jmp GameLoop
;
; Play sounds, jump to ABM/player update.
;
1356: 20 5c 13 JmpUpdateAbm jsr PlaySounds
1359: 4c 27 0f jmp UpdateAbmMaybe
;
; Plays sound effects.
;
135c: a5 4a PlaySounds lda sfx_counter
135e: 26 4b rol sfx_counter+1
1360: 2a rol A
1361: 90 02 bcc :NoInc
1363: e6 4b inc sfx_counter+1
1365: 45 4b :NoInc eor sfx_counter+1
1367: 4d 40 c0 eor STROBE ;(?)
136a: 85 4a sta sfx_counter
136c: 29 7f and #$7f
136e: cd 43 18 cmp sound_fx_mod
1371: f0 05 beq :Return
1373: b0 03 bcs :Return
1375: 8d 30 c0 _Click sta SPKR ;$c030 or (for attract mode) $c064
1378: 60 :Return rts
1379: 00 cursor_x .dd1 $00 ;ranges from 6 to 134 ($86)
137a: 00 cursor_y .dd1 $00 ;ranges from 3 to 131 ($83)
137b: 00 prev_cursor_x .dd1 $00 ;X posn where cursor was last drawn
137c: 00 prev_cursor_y .dd1 $00 ;Y posn where cursor was last drawn
;
; Draws an Applesoft shape, from an inline data pointer, at the position
; determined by the last HPOSN or HPLOT.
;
137d: 20 4c 15 InA_DrawShape jsr GetInlineWord
1380: aa tax ;get address in (Y,X)
1381: a9 00 lda #$00 ;rotation=0
1383: 4c 01 f6 jmp BAS_DRAW0 ;draw shape
;
; Draws the cursor, one pixel at a time. The previous screen contents are
; saved.
;
1386: a0 ff DrawCursor ldy #$ff ;init index (first inc will make it zero)
1388: 8c 94 13 sty :_Index+1
138b: a2 03 ldx #$03 ;color=3 (white)
138d: 20 ec f6 jsr BAS_HCOLOR+3
1390: ee 94 13 :Loop inc :_Index+1
1393: a0 00 :_Index ldy #$00
1395: ad 79 13 lda cursor_x ;get cursor X position
1398: 18 clc
1399: 79 e9 13 adc cursor_xoff,y ;add the X offset
139c: aa tax
139d: ad 7a 13 lda cursor_y ;get cursor Y position
13a0: 18 clc
13a1: 79 f3 13 adc cursor_yoff,y ;add the Y offset
13a4: 20 07 14 jsr DrawPixel ;draw the pixel
13a7: ac 94 13 ldy :_Index+1
13aa: 99 fd 13 sta saved_curs_pix,y ;save previous value
13ad: c0 09 cpy #$09 ;did we finish entry #9?
13af: 90 df bcc :Loop ;not yet, loop
13b1: ad 79 13 lda cursor_x ;remember where we drew it
13b4: 8d 7b 13 sta prev_cursor_x
13b7: ad 7a 13 lda cursor_y
13ba: 8d 7c 13 sta prev_cursor_y
13bd: 60 rts
;
; Erases the cursor, one pixel at a time. The previous screen contents are
; restored.
;
13be: a0 ff EraseCursor ldy #$ff ;init index (first inc will make it zero)
13c0: 8c c7 13 sty :_Index+1
13c3: ee c7 13 :Loop inc :_Index+1
13c6: a0 00 :_Index ldy #$00
13c8: b9 fd 13 lda saved_curs_pix,y ;get old color
13cb: aa tax
13cc: 20 ec f6 jsr BAS_HCOLOR+3 ;set color
13cf: ad 7b 13 lda prev_cursor_x ;get cursor's previous X position
13d2: 18 clc
13d3: 79 e9 13 adc cursor_xoff,y ;add the X offset
13d6: aa tax
13d7: ad 7c 13 lda prev_cursor_y ;get cursor's previous Y position
13da: 18 clc
13db: 79 f3 13 adc cursor_yoff,y ;add the Y offset
13de: 20 07 14 jsr DrawPixel ;draw the pixel
13e1: ac c7 13 ldy :_Index+1
13e4: c0 09 cpy #$09 ;did we finish entry #9?
13e6: 90 db bcc :Loop ;not yet, loop
13e8: 60 rts
;
; X/Y offsets for drawing cursor pixels. These are 8-bit signed values.
;
; (0,-2)
; (0,-1)
; (-1, 0) (0, 0) (1, 0)
; (-1, 1) (0, 1) (1, 1)
; (0, 2)
; (0, 3)
13e9: 00 00 ff ff+ cursor_xoff .bulk $00,$00,$ff,$ff,$00,$00,$01,$01,$00,$00
13f3: fe ff 00 01+ cursor_yoff .bulk $fe,$ff,$00,$01,$00,$01,$00,$01,$02,$03
;
; Saved pixel colors. These hold the color value (0-3) of the pixel under the
; cursor pixel. Note these are color pixels (two basic pixels).
13fd: 00 00 00 00+ saved_curs_pix .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
;
; Draws a color pixel (two B&W pixels) at the specified position. The previous
; color value is returned.
;
; On entry:
; X-reg: X offset (0-139)
; A-reg: Y offset (0-191)
;
; On exit:
; A-reg: previous color pixel value (0-3)
;
1407: 48 DrawPixel pha ;preserve Y offset
1408: a9 00 lda #$00
140a: 8d 4c 14 sta :_ScreenColor+1 ;init return value
140d: 8a txa ;get X offset
140e: 0a asl A ;double it
140f: aa tax ;save low part in X-reg
1410: a9 00 lda #$00
1412: 69 00 adc #$00
1414: a8 tay ;save high part in Y-reg
1415: 68 pla ;restore Y offset (why?)
1416: 48 pha ;save it some more
1417: 8a txa ;get low part
1418: 29 fe and #%11111110 ;ensure it's even
141a: aa tax ;put that back in X-reg
141b: 49 01 eor #$01 ;ensure it's odd
141d: 8d 2e 14 sta :_XCoordLo+1 ;store odd X coord for second pass
1420: 68 pla ;restore Y offset
1421: 8d 30 14 sta :_YCoord+1 ;set as Y coord
1424: 8c 32 14 sty :_XCoordHi+1 ;set X coord (high)
1427: 20 33 14 jsr :DoDraw ;draw one pixel
142a: 0e 4c 14 asl :_ScreenColor+1 ;shift previous screen value
142d: a2 00 :_XCoordLo ldx #$00 ;now set up for the even pixel
142f: a9 00 :_YCoord lda #$00
1431: a0 00 :_XCoordHi ldy #$00
1433: 20 11 f4 :DoDraw jsr BAS_HPOSN ;set up DP for hi-res X/Y position
1436: b1 26 lda (BAS_HBASL),y ;get current value
1438: 25 30 and BAS_HMASK ;apply masks
143a: 29 7f and #$7f ;(probably not needed, nothing draws with hi set)
143c: f0 03 beq L1441 ;pixel not set, branch
143e: ee 4c 14 inc :_ScreenColor+1 ;bump return value
1441: a5 1c L1441 lda BAS_HCOLOR1 ;merge color pixels into hi-res screen
1443: 51 26 eor (BAS_HBASL),y
1445: 25 30 and BAS_HMASK ;don't touch the adjacent pixels
1447: 51 26 eor (BAS_HBASL),y
1449: 91 26 sta (BAS_HBASL),y
144b: a9 00 :_ScreenColor lda #$00 ;return previous color
144d: 60 rts
;
; Shapes for the cities and missile launchers.
;
144e: 24 24 24 35+ shape_city .bulk $24,$24,$24,$35,$36,$2e,$25,$24,$24,$2c,$2c,$24,$36,$35,$35,$36
+ $36,$26,$24,$25,$24,$24,$25,$24,$2c,$24,$24,$2d,$36,$36,$35,$36
+ $2e,$36,$36,$36,$3f,$37,$2e,$2d,$2d,$25,$3c,$3f,$24,$24,$2c,$2d
+ $36,$36,$26,$24,$24,$24,$24,$24,$2c,$2d,$35,$36,$36,$36,$36,$36
+ $36,$26,$24,$24,$2d,$24,$24,$36,$36,$2d,$35,$36,$36,$3e,$3f,$3f
+ $3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$2d,$2d,$2d,$2d,$2d
+ $2d,$2d,$2d,$2d,$2d,$2d,$2d,$2d,$2d,$05,$00
14b9: 1b 2e 21 24+ shape_laun_bot .bulk $1b,$2e,$21,$24,$09,$36,$2e,$21,$24,$24,$09,$36,$36,$2d,$24,$24
+ $0d,$36,$36,$2f,$2e,$21,$24,$2c,$11,$36,$2e,$21,$2c,$11,$36,$1b
+ $1b,$08,$20,$00
14dd: 2d 2d 3f 27+ shape_laun_top .bulk $2d,$2d,$3f,$27,$24,$2c,$24,$34,$36,$35,$36,$06,$00
;
; Returns a pointer stored as inline data following the caller's caller's JSR.
;
; On exit:
; $10-11: pointer
; A-reg/X-reg/Y-reg preserved
;
]ptr .var $10 {addr/2}
14ea: 48 GetInlinePtr pha ;preserve A-reg
14eb: 8a txa
14ec: 48 pha ;preserve X-reg
14ed: ba tsx
14ee: bd 05 01 lda STACK+5,x ;extract the pointer
14f1: 85 10 sta ]ptr
14f3: bd 06 01 lda STACK+6,x
14f6: 85 11 sta ]ptr+1
14f8: 68 pla
14f9: aa tax ;restore X-reg
14fa: 68 pla ;restore A-reg
14fb: 60 rts
;
; Adjusts the return address on the stack to move past the inline string. The
; caller must jump here, not JSR.
;
; On entry:
; Y-reg: string length
;
]tmp .var $10 {addr/1}
14fc: 98 AdjustRetAddr tya ;move length to A-reg (why?)
14fd: 85 10 sta ]tmp ;store in DP
14ff: 68 pla ;pull low byte of return addr
1500: 18 clc
1501: 65 10 adc ]tmp ;add the length
1503: 85 10 sta ]tmp ;store result in DP
1505: 90 04 bcc :NoInc ;if we didn't cross a page, branch
1507: 68 pla ;need to update high byte, so pull that
1508: 69 00 adc #$00
150a: 48 pha
150b: a5 10 :NoInc lda ]tmp ;get low byte
150d: 48 pha ;push it back on
150e: 60 rts ;return to updated address
;
; Unused function, probably used for issuing DOS commands.
;
]ptr .var $10 {addr/2}
150f: a9 84 InS_unref_150f? lda #$84 ;Ctrl+D
1511: 20 ed fd jsr MON_COUT
1514: 20 ea 14 jsr GetInlinePtr ;get pointer to string
1517: a0 00 ldy #$00
1519: 8c 20 15 sty :_Index+1
151c: ee 20 15 :Loop inc :_Index+1
151f: a0 00 :_Index ldy #$00 ;track index with self-mod code
1521: b1 10 lda (]ptr),y
1523: f0 d7 beq AdjustRetAddr ;done, move return addr to end of string
1525: 20 ed fd jsr MON_COUT ;use COUT to allow interception
1528: 4c 1c 15 jmp :Loop
;
; Prints a high-ASCII string that immediately follows the JSR to this function.
;
152b: 20 ea 14 InS_PrintString jsr GetInlinePtr ;get pointer to string
152e: a0 00 ldy #$00
1530: 8c 37 15 sty :_Index+1
1533: ee 37 15 :Loop inc :_Index+1
1536: a0 00 :_Index ldy #$00 ;track index with self-mod code
1538: b1 10 lda (]ptr),y
153a: f0 c0 beq AdjustRetAddr ;done, move return addr to end of string
153c: 20 f0 fd jsr MON_COUT1 ;use COUT1 to bypass I/O hooks
153f: 4c 33 15 jmp :Loop
;
; Sets the text position to a row and column specified by inline data following
; the call to this function. First byte is row (0-23), second byte is column
; (0-39).
;
1542: 20 4c 15 InXY_SetTextPos jsr GetInlineWord
1545: 85 25 sta MON_CV ;set text vertical position
1547: 84 24 sty MON_CH ;set text horizontal position
1549: 4c 22 fc jmp MON_VTAB ;update pointer
;
; Gets an inline word from the caller's caller. Update's the return address
; from the calling function to walk past it.
;
; If the retrieved value is $42xx, the high byte is replaced with the value at
; $18 (high byte of pointer at $17-18).
;
; On exit:
; A-reg: low byte
; Y-reg: high byte
;
154c: ba GetInlineWord tsx ;get stack pointer
154d: bd 03 01 lda STACK+3,x ;read from stack+3 to get caller's caller
1550: 85 10 sta ]ptr ;(address is last byte of JSR, e.g. $0829)
1552: bd 04 01 lda STACK+4,x
1555: 85 11 sta ]ptr+1
1557: a0 01 ldy #$01 ;add one to get byte following JSR
1559: b1 10 lda (]ptr),y ;get first (low) byte
155b: 48 pha ;save it
155c: c8 iny
155d: b1 10 lda (]ptr),y ;get second (high) byte
155f: c9 42 cmp #$42 ;$42xx?
1561: d0 02 bne :Not42
1563: a5 18 lda missile_ptr+1 ;substitute value in $18
1565: a8 :Not42 tay ;put high byte in Y-reg
1566: 68 pla ;put low byte in A-reg
1567: 20 6a 15 jsr :IncVal ;increment twice to get past value
156a: fe 03 01 :IncVal inc STACK+3,x
156d: d0 03 bne :NoInc
156f: fe 04 01 inc STACK+4,x
1572: 60 :NoInc rts
;
; Gets a 5-byte "packed" floating-point value, and loads it into Applesoft's
; FAC. The address is specified by inline data following the JSR to this
; function.
;
; On entry:
; A-reg: destination address (low)
; Y-reg: destination address (high)
;
1573: 20 4c 15 InA_LoadFloat jsr GetInlineWord ;get word from caller's caller
1576: 4c f9 ea jmp BAS_LOAD_FAC_FROM_YA ;load it into Applesoft FP register
;
; Converts an inline integer value to a float in FAC.
;
1579: 20 4c 15 unref_1579? jsr GetInlineWord
157c: 8c 81 15 sty :_Aval+1 ;Y-reg has high byte, but func wants it as low
157f: a8 tay ;move low byte to Y-reg
1580: a9 00 :_Aval lda #$00 ;high byte in A-reg
1582: 4c f2 e2 jmp BAS_GIVAYF ;convert value to float
;
; Computes FAC = (inline_ref) + FAC.
;
1585: 20 4c 15 InA_AddFloat jsr GetInlineWord
1588: 4c be e7 jmp BAS_FADD
;
; Computes FAC = (inline_ref) - FAC.
;
158b: 20 4c 15 InA_SubFloat jsr GetInlineWord
158e: 4c a7 e7 jmp BAS_FSUB
;
; Computes FAC = (inline_ref) * FAC.
;
1591: 20 4c 15 InA_MulFloat jsr GetInlineWord
1594: 4c 7f e9 jmp BAS_FMULT
;
; Computes FAC = (inline_ref) / FAC.
;
1597: 20 4c 15 InA_DivFloat jsr GetInlineWord
159a: 4c 66 ea jmp BAS_FDIV
;
; Computes FAC = (inline_ref) ^ FAC.
;
InA_unref_ExpFloat
159d: 20 4c 15 jsr GetInlineWord
15a0: 20 e3 e9 jsr BAS_LOAD_ARG_FROM_YA
15a3: 4c 97 ee jmp BAS_FPWRT
;
; Compares float referenced by inline value to FAC.
;
; On exit:
; A-reg: {1,0,-1} as (Y,A) <,=,> FAC
; Status flags set according to A-reg
;
15a6: 20 4c 15 InA_CmpFloat jsr GetInlineWord
15a9: 4c b2 eb jmp BAS_FCOMP
;
; Rounds the floating point value in FAC, then stores the value at the address
; specified inline after the caller's JSR.
;
; On entry:
; X-reg: destination address (low)
; Y-reg: destination address (high)
;
15ac: 20 4c 15 InA_StoreFloat jsr GetInlineWord ;get address in (Y,A)
15af: aa tax
15b0: 4c 2b eb jmp BAS_STORE_FAC_AT_YX_ROUNDED ;round FAC, and store at (Y,X)
;
; Unused: print a float as an integer.
;
unref_PrintFloat
15b3: aa tax
15b4: 98 tya
15b5: 4c 24 ed jmp BAS_LINPRT ;print float at (A,X) as integer
;
; Draws expanding circle for explosions.
;
; The tables define a circle as a series of pixels drawn from the center
; outward, sort of like drawing a spiral. The code starts at index M and stops
; at index N, drawing part of the circle on each call, expanding outward. By
; remembering the stop index and passing it in on subsequent iterations, the
; circle can be drawn incrementally.
;
; The number of iterations we do per visit determines how quickly the circle
; expands per frame. The total pixel count determines the radius. Each entry
; in the table determines 4 pixels, one per quadrant, and we're drawing "color
; pixels" which are twice as wide as they are high. So a 256-entry table covers
; 2048 pixels, which math says is enough for a circle of radius 25. The largest
; displacement in the table is 24, to which we add 1 for the center pixel.
;
; At max radius we offset +/- 24 pixels, so the maximum Y coordinate value
; (assuming a valid center point) is [-24,215], which we can check with a simple
; test. There's no risk of wrapping around the screen.
;
; ABMs use radius $50 (80) for outer/center, $70 (112) for inner. Missiles use
; a randomized radius between 96 and 176.
;
; Note to hackers: don't use a size larger than ($100 - iterations), or the code
; can get stuck. If you're using an iteration count of 8, don't exceed $f8.
; The integer rolls over and the game doesn't realize the explosion should stop.
; Make the size a multiple of the count.
;
; Set the HCOLOR before calling.
;
; On entry:
; X-reg: center X position (0-139)
; A-reg: center Y position (0-191)
; Y-reg: iterations (8 or 16)
; (set the initial index directly, starting from zero)
;
; On exit:
; Index updated (+= iteration count)
;
15b8: 8d d2 15 DrawExplosion sta :_DrawCircY+1 ;set center Y
15bb: 8e c7 15 stx :_DrawCircX+1 ;set center X
15be: 8c 3c 16 sty :_CircleCount+1 ;init iterations
; Note the DrawPixel function takes the column (0-139) in X-reg, and the row (0-
; 191) in A-reg. Y-reg doesn't matter.
15c1: ce 3c 16 :DrawCircLoop dec :_CircleCount+1 ;count down interations
; Start with bottom-right quadrant.
15c4: a0 00 _DrawCircIndex ldy #$00
15c6: a9 00 :_DrawCircX lda #$00 ;get center X
15c8: 18 clc
15c9: 79 43 16 adc circle_x_data,y ;add horizontal offset
15cc: aa tax ;copy to X-reg
15cd: c9 8c cmp #140 ;range check 0 <= X <= 140
15cf: b0 0b bcs :SkipBR ;went past, don't draw pixel
15d1: a9 00 :_DrawCircY lda #$00 ;get center Y
15d3: 18 clc
15d4: 79 43 17 adc circle_y_data,y ;add vertical offset
15d7: a0 00 ldy #$00 ;(?)
15d9: 20 07 14 jsr DrawPixel ;draw bottom-right pixel
; Bottom-Left quadrant.
15dc: ac c5 15 :SkipBR ldy _DrawCircIndex+1
15df: ad c7 15 lda :_DrawCircX+1
15e2: 38 sec
15e3: f9 43 16 sbc circle_x_data,y
15e6: aa tax
15e7: c9 8c cmp #140 ;check vs. left edge (negative)
15e9: b0 0c bcs :SkipBL
15eb: ad d2 15 lda :_DrawCircY+1
15ee: 18 clc
15ef: 79 43 17 adc circle_y_data,y ;not range-checked: we don't draw circles that low
15f2: a0 00 ldy #$00 ;(?)
15f4: 20 07 14 jsr DrawPixel ;draw bottom-left pixel
; Upper-right quadrant.
15f7: ac c5 15 :SkipBL ldy _DrawCircIndex+1
15fa: ad c7 15 lda :_DrawCircX+1
15fd: 18 clc
15fe: 79 43 16 adc circle_x_data,y
1601: aa tax
1602: c9 8c cmp #140 ;check vs. right edge
1604: b0 10 bcs :SkipUR
1606: ad d2 15 lda :_DrawCircY+1
1609: 38 sec
160a: f9 43 17 sbc circle_y_data,y
160d: c9 be cmp #190 ;check vs. top (negative)
160f: b0 05 bcs :SkipUR
1611: a0 00 ldy #$00 ;(?)
1613: 20 07 14 jsr DrawPixel ;draw upper-right pixel
; Upper-left quadrant.
1616: ac c5 15 :SkipUR ldy _DrawCircIndex+1
1619: ad c7 15 lda :_DrawCircX+1
161c: 38 sec
161d: f9 43 16 sbc circle_x_data,y
1620: aa tax
1621: c9 8c cmp #140 ;check vs. left edge (negative)
1623: b0 10 bcs :SkipUL
1625: ad d2 15 lda :_DrawCircY+1
1628: 38 sec
1629: f9 43 17 sbc circle_y_data,y
162c: c9 be cmp #190 ;check vs. top (negative)
162e: b0 05 bcs :SkipUL
1630: a0 00 ldy #$00 ;(?)
1632: 20 07 14 jsr DrawPixel ;draw upper-left pixel
; All pixels are drawn; update state.
1635: ee c5 15 :SkipUL inc _DrawCircIndex+1
1638: 20 5c 13 jsr PlaySounds ;update sound effects once per iteration
163b: a2 00 :_CircleCount ldx #$00 ;done yet?
163d: f0 03 beq :Return ;yes, bail
163f: 4c c1 15 jmp :DrawCircLoop
1642: 60 :Return rts
;
; Pixel offsets for incrementally-drawn circle. The X offsets are for double-
; wide color pixels, so represent twice the distance of the Y offsets.
;
; The values are all +X/+Y, representing the first quadrant.
;
1643: 00 00 00 01+ circle_x_data .bulk $00,$00,$00,$01,$01,$01,$00,$01,$00,$02,$02,$01,$02,$00,$02,$01
+ $02,$00,$03,$03,$01,$03,$02,$03,$00,$02,$03,$01,$03,$00,$04,$02
+ $04,$01,$04,$03,$04,$02,$00,$04,$01,$02,$03,$04,$02,$00,$03,$04
+ $05,$05,$01,$05,$05,$04,$02,$05,$03,$00,$01,$05,$04,$03,$05,$02
+ $00,$06,$04,$06,$01,$06,$05,$06,$03,$02,$06,$04,$05,$00,$06,$01
+ $03,$06,$05,$02,$04,$06,$00,$07,$07,$01,$05,$07,$03,$07,$04,$06
+ $02,$07,$05,$07,$00,$06,$01,$03,$07,$04,$02,$05,$06,$07,$00,$08
+ $08,$01,$04,$07,$08,$03,$06,$08,$05,$02,$08,$07,$08,$06,$00,$04
+ $03,$08,$01,$05,$07,$02,$08,$06,$07,$04,$08,$00,$09,$03,$05,$09
+ $01,$09,$09,$08,$02,$06,$07,$09,$09,$04,$05,$08,$03,$09,$00,$01
+ $07,$06,$09,$02,$08,$04,$09,$05,$07,$03,$00,$06,$08,$0a,$0a,$01
+ $0a,$09,$0a,$02,$0a,$07,$05,$09,$04,$08,$0a,$06,$03,$0a,$00,$01
+ $09,$0a,$07,$08,$02,$05,$04,$0a,$06,$09,$03,$08,$0a,$00,$0b,$07
+ $0b,$01,$0b,$09,$0b,$02,$05,$0a,$0b,$04,$06,$0b,$08,$03,$07,$09
+ $0b,$0a,$00,$01,$0b,$05,$06,$0a,$02,$08,$04,$0b,$09,$07,$03,$0b
+ $0a,$00,$0c,$0c,$01,$08,$09,$0c,$05,$0b,$06,$0c,$02,$0c,$04,$07
1743: 00 01 02 00+ circle_y_data .bulk $00,$01,$02,$00,$01,$02,$03,$03,$04,$00,$01,$04,$02,$05,$03,$05
+ $04,$06,$00,$01,$06,$02,$05,$03,$07,$06,$04,$07,$05,$08,$00,$07
+ $01,$08,$02,$06,$03,$08,$09,$04,$09,$09,$07,$05,$09,$0a,$08,$06
+ $00,$01,$0a,$02,$03,$07,$0a,$04,$09,$0b,$0b,$05,$08,$0a,$06,$0b
+ $0c,$00,$09,$01,$0c,$02,$07,$03,$0b,$0c,$04,$0a,$08,$0d,$05,$0d
+ $0c,$06,$09,$0d,$0b,$07,$0e,$00,$01,$0e,$0a,$02,$0d,$03,$0c,$08
+ $0e,$04,$0b,$05,$0f,$09,$0f,$0e,$06,$0d,$0f,$0c,$0a,$07,$10,$00
+ $01,$10,$0e,$08,$02,$0f,$0b,$03,$0d,$10,$04,$09,$05,$0c,$11,$0f
+ $10,$06,$11,$0e,$0a,$11,$07,$0d,$0b,$10,$08,$12,$00,$11,$0f,$01
+ $12,$02,$03,$09,$12,$0e,$0c,$04,$05,$11,$10,$0a,$12,$06,$13,$13
+ $0d,$0f,$07,$13,$0b,$12,$08,$11,$0e,$13,$14,$10,$0c,$00,$01,$14
+ $02,$09,$03,$14,$04,$0f,$12,$0a,$13,$0d,$05,$11,$14,$06,$15,$15
+ $0b,$07,$10,$0e,$15,$13,$14,$08,$12,$0c,$15,$0f,$09,$16,$00,$11
+ $01,$16,$02,$0d,$03,$16,$14,$0a,$04,$15,$13,$05,$10,$16,$12,$0e
+ $06,$0b,$17,$17,$07,$15,$14,$0c,$17,$11,$16,$08,$0f,$13,$17,$09
+ $0d,$18,$00,$01,$18,$12,$10,$02,$16,$0a,$15,$03,$18,$04,$17,$14
;
; Sound effect modifier.
1843: 00 sound_fx_mod .dd1 $00
1844: 00 00 00 00+ .junk 6
;
; Status of the six cities:
; $ff - undamaged
; $80 - damaged
; $00 - destroyed
184a: ff ff ff ff+ city_status .bulk $ff,$ff,$ff,$ff,$ff,$ff
;
; Horizontal column of city center (0-39).
1850: 02 09 10 17+ city_cols .bulk $02,$09,$10,$17,$1e,$25
;
; Evaluate damage done to cities.
;
1856: a2 ff EvalCityDamage ldx #$ff
1858: 8e 6c 18 stx :_Loop+1
185b: e8 inx ;X-reg=0
185c: a0 00 ldy #$00 ;init surviving city count to zero
185e: 8e be 18 stx :_CityLoopDone+1
1861: a9 9b lda #155 ;line 155 (five above text window), left column
1863: 20 11 f4 jsr BAS_HPOSN
1866: 20 42 15 jsr InXY_SetTextPos
1869: 14 .dd1 20 ;top row of text window
186a: 00 .dd1 0 ;left edge
; Loop over all six cities, checking for damage.
186b: a2 00 :_Loop ldx #$00
186d: e8 inx
186e: 8e 6c 18 stx :_Loop+1
1871: e0 06 cpx #$06 ;done yet?
1873: f0 48 beq :_CityLoopDone ;yes, branch
1875: bd 50 18 lda city_cols,x ;get horizontal offset of city center (0-39)
1878: a8 tay ;use as index into hi-res page
1879: b1 26 lda (BAS_HBASL),y ;read hi-res byte at (offset*7, 155)
187b: d0 1a bne :CityNotDead ;still some pixels here, city not destroyed
187d: bd 4a 18 lda city_status,x ;check status
1880: f0 e9 beq :_Loop ;already destroyed, nothing to do
; Destroy city.
1882: a9 00 lda #$00
1884: 9d 4a 18 sta city_status,x ;set status to destroyed
1887: 88 dey ;back up one space
1888: a2 00 ldx #$00
188a: a9 ad lda #“-”
188c: 91 28 :DashLoop sta (MON_BASL),y ;ovewrite city initials with hyphens
188e: c8 iny
188f: e8 inx
1890: e0 03 cpx #$03
1892: 90 f8 bcc :DashLoop
1894: 4c 6b 18 jmp :_Loop ;check next
1897: ee be 18 :CityNotDead inc :_CityLoopDone+1 ;increment number of surviving cities
189a: d9 52 0b cmp orig_row_155,y ;check for damage
189d: f0 cc beq :_Loop ;no damage, move on to next
189f: bd 4a 18 lda city_status,x ;check city status
18a2: c9 80 cmp #$80 ;already marked as damaged?
18a4: f0 c5 beq :_Loop ;yes, move on to next
18a6: a9 80 lda #$80
18a8: 9d 4a 18 sta city_status,x ;set status to damaged
18ab: 88 dey ;back up one space from center
18ac: a2 00 ldx #$00
18ae: b1 28 :InvertLoop lda (MON_BASL),y ;invert the city abbrev (or whatever is there)
18b0: 29 3f and #$3f
18b2: 91 28 sta (MON_BASL),y
18b4: c8 iny
18b5: e8 inx
18b6: e0 03 cpx #$03
18b8: 90 f4 bcc :InvertLoop
18ba: 4c 6b 18 jmp :_Loop
18bd: a9 00 :_CityLoopDone lda #$00 ;check surviving city count
18bf: f0 01 beq :NoCitiesLeft ;all destroyed, game is over
18c1: 60 rts
18c2: 68 :NoCitiesLeft pla ;pop return address
18c3: 68 pla
18c4: ad f0 0b lda auto_play_flag ;attract mode?
18c7: f0 03 beq ShowFinalScore ;no, show player their score
18c9: 4c 13 08 jmp Restart ;yes, just go back to title screen
;
; Shows the final score after the game ends.
;
; base_score = ((missiles_destroyed - 6) * 10) / (ABMs_fired + 100)
; score = floor(base_score * 100) * 10
;
18cc: 8d 56 c0 ShowFinalScore sta LORES ;lo-res graphics (why?)
18cf: 8d 51 c0 sta TXTSET ;text mode (redundant with next call)
18d2: 20 39 fb jsr MON_SETTXT ;text mode, reset window
18d5: ad f2 0b lda mssl_destroy_cnt+1 ;get number of missiles destroyed
18d8: ac f1 0b ldy mssl_destroy_cnt
18db: 20 f2 e2 jsr BAS_GIVAYF ;convert to float
18de: 20 42 15 jsr InXY_SetTextPos
18e1: 0a .dd1 10
18e2: 0a .dd1 10
18e3: 20 2b 15 jsr InS_PrintString
18e6: d9 cf d5 a0+ .zstr “YOU SCORED ”
18f2: 20 8b 15 jsr InA_SubFloat ;subtract 6, because missiles "destroyed" by blowing
18f5: 09 0c .dd2 const_6 ; up a city don't count toward your score
18f7: 20 d0 ee jsr BAS_NEGOP
18fa: 20 82 eb jsr BAS_SIGN ;did it go negative?
18fd: 10 0c bpl :NotNeg ;no, branch
18ff: a9 00 lda #$00 ;yes, set it to zero
1901: 85 9d sta BAS_FAC
1903: 85 9e sta BAS_FAC+1
1905: 85 9f sta BAS_FAC+2
1907: 85 a0 sta BAS_FAC+3
1909: 85 a1 sta BAS_FAC+4
190b: 20 39 ea :NotNeg jsr BAS_MUL10 ;multiply by 10
190e: 20 ac 15 jsr InA_StoreFloat ;save it
1911: 1d 0c .dd2 score_m_temp
1913: ad f4 0b lda abm_fired_cnt+1 ;get number of ABMs launched
1916: ac f3 0b ldy abm_fired_cnt
1919: 20 f2 e2 jsr BAS_GIVAYF ;convert to float
191c: 20 85 15 jsr InA_AddFloat ;add 100
191f: 04 0c .dd2 const_100
1921: 20 97 15 jsr InA_DivFloat ;divide (missiles-6) by (ABM+100)
1924: 1d 0c .dd2 score_m_temp
1926: 20 39 ea jsr BAS_MUL10 ;multiply by 10
1929: 20 39 ea jsr BAS_MUL10 ;again (* 100)
192c: 20 23 ec jsr BAS_INT ;truncate fractional portion
192f: 20 39 ea jsr BAS_MUL10 ;multiply by 10 (* 1000)
1932: 20 ac 15 jsr InA_StoreFloat ;save it
1935: 2c 0c .dd2 score_temp
1937: 20 2e ed jsr BAS_PRINT_FAC ;print score
193a: 20 42 15 jsr InXY_SetTextPos ;move down a line
193d: 0b .dd1 11
193e: 0a .dd1 10
193f: 20 2b 15 jsr InS_PrintString
1942: c6 cf d2 a0+ .zstr “FOR DOWNING ”
194f: ad f2 0b lda mssl_destroy_cnt+1 ;get count of missiles destroyed
1952: ae f1 0b ldx mssl_destroy_cnt
1955: 20 24 ed jsr BAS_LINPRT ;print it
1958: 20 42 15 jsr InXY_SetTextPos ;move down a line
195b: 0c .dd1 12
195c: 0a .dd1 10
195d: 20 2b 15 jsr InS_PrintString
1960: cd c9 d3 d3+ .zstr “MISSILES WITH ”
196f: ad f4 0b lda abm_fired_cnt+1 ;get count of ABMs fired
1972: ae f3 0b ldx abm_fired_cnt
1975: 20 24 ed jsr BAS_LINPRT ;print it
1978: 20 42 15 jsr InXY_SetTextPos ;move down a line
197b: 0d .dd1 13
197c: 0a .dd1 10
197d: 20 2b 15 jsr InS_PrintString
1980: c1 c2 cd a7+ .zstr “ABM'S.”
1987: 20 42 15 jsr InXY_SetTextPos ;move up several rows
198a: 05 .dd1 5
198b: 0a .dd1 10
198c: 20 2b 15 jsr InS_PrintString
198f: c8 c9 c7 c8+ .zstr “HIGH SCORE IS ”
199e: 20 73 15 jsr InA_LoadFloat ;get high score
19a1: 31 0c .dd2 high_score
19a3: 20 2e ed jsr BAS_PRINT_FAC ;print it
19a6: 20 73 15 jsr InA_LoadFloat ;get current score in FAC
19a9: 2c 0c .dd2 score_temp
19ab: 20 a6 15 jsr InA_CmpFloat ;compare to high score
19ae: 31 0c .dd2 high_score
19b0: 30 2a bmi :NotHigh ;not new high score, branch
19b2: 20 ac 15 jsr InA_StoreFloat ;store new high score
19b5: 31 0c .dd2 high_score
19b7: a9 3f lda #$3f
19b9: 85 32 sta MON_INVFLAG ;inverse text
19bb: 20 42 15 jsr InXY_SetTextPos ;move down one row
19be: 06 .dd1 6
19bf: 0c .dd1 12
19c0: 20 2b 15 jsr InS_PrintString
19c3: c2 c5 c6 cf+ .zstr “BEFORE THIS GAME!”
19d5: a9 ff lda #$ff
19d7: 85 32 sta MON_INVFLAG ;normal text
19d9: 20 9e 09 jsr WriteHighScore ;save score to disk
19dc: 20 42 15 :NotHigh jsr InXY_SetTextPos ;move to bottom of screen
19df: 17 .dd1 23
19e0: 05 .dd1 5
19e1: 20 2b 15 jsr InS_PrintString
19e4: d0 d2 c5 d3+ .zstr “PRESS RETURN TO PLAY AGAIN.”
; Wait until key hit or timer expires.
1a00: 8d 10 c0 sta KBDSTRB ;clear keyboard strobe
1a03: a2 d0 ldx #$d0
1a05: 8e ef 0b stx temp+1 ;set timer
1a08: 8e ee 0b stx temp
1a0b: ee ee 0b :WaitLoop inc temp
1a0e: d0 08 bne :NotExp
1a10: ee ef 0b inc temp+1
1a13: d0 03 bne :NotExp
1a15: 4c 13 08 jmp Restart ;timer expired, restart at title
1a18: ad 00 c0 :NotExp lda KBD ;key hit?
1a1b: 30 06 bmi :KeyHit
1a1d: ca dex
1a1e: d0 f8 bne :NotExp
1a20: 4c 0b 1a jmp :WaitLoop
1a23: 8d 10 c0 :KeyHit sta KBDSTRB
1a26: a9 00 lda #$00
1a28: 8d f0 0b sta auto_play_flag ;clear auto-play flag (unnecessary?)
1a2b: 4c d4 09 jmp StartGame ;start a new game
;
; Unused junk saved with file.
;
1a2e: d2 d3 cf d2+ .junk 1489
No exported symbols found.