********************************************************************************
* Stellar 7 for the Apple II, by Damon Slye *
* Copyright 1983 Dynamix *
* *
* Disassembly of "ROCK2". *
********************************************************************************
* This contains half of the graphics engine and, because it includes PLAYGAME, *
* all of the game logic. *
********************************************************************************
* Disassembly by Andy McFadden, using 6502bench SourceGen v1.6. *
* Last updated 2020/03/30 *
********************************************************************************
c_num_objects .eq 24 {const} ;max number of objects
target_angle .eq $0a ;0-255
view_vert_index .eq $2c ;index into viewport-transformed vertices
hpage .eq $32 ;hi-res page ($20/$40)
mesh_ptr .eq $3a {addr/2} ;pointer to object in mesh data table
mesh_table_ptr .eq $3c {addr/2} ;pointer to mesh data table
VIEW_X_ADJ .eq $50 {addr/2} ;translate viewport to screen coords
VIEW_Y_ADJ .eq $52 {addr/2} ;translate viewport to screen coords
VIEW_LEFT .eq $54 {addr/2} ;left edge of current viewport
VIEW_RIGHT .eq $56 {addr/2} ;right edge of current viewport
VIEW_TOP .eq $58 {addr/2} ;top edge of current viewport
VIEW_BOTTOM .eq $5a {addr/2} ;bottom edge of current viewport
g_obj_index .eq $82 ;frequently-used index to object (0-23)
jmp_tmp .eq $87 {addr/2} ;zero-page addr used for indirect JMP
HPAGE_EE .eq $ee ;hi-res page ($20/$40)
sound00 .eq $0300 {addr/4}
sysnm_flash_flag .eq $030b ;modifies sound during shutter open
sound10 .eq $0310 {addr/4}
sound18 .eq $0318
warp_sound_thing? .eq $031c
sound20 .eq $0320 {addr/4}
sound30 .eq $0330 {addr/4}
sound38 .eq $0338
sound40 .eq $0340 {addr/2}
warp_sound_ctr? .eq $034c
sound80 .eq $0380
DIVIDE_16 .eq $0810 ;performs 16-bit division; numerator may be signed
ROTATE_COORDS .eq $0b00 ;rotate X,Z about Y (ccw) by angle in A-reg
RADAR_XCOORDS .eq $0cd0 ;scaled map position for radar
RADAR_YCOORDS .eq $0ce8 ;scaled map position for radar
ROTATE_INT8 .eq $0d00 ;rotate X,0 about Y (ccw) by angle in A-reg
CLIP_COORD_XLO .eq $0e00 {addr/64} ;low byte of vertex X-coord (viewport)
CLIP_COORD_XHI .eq $0e40 {addr/64} ;high byte of vertex X-coord (viewport)
CLIP_COORD_YLO .eq $0e80 {addr/64} ;low byte of vertex Y-coord (viewport)
CLIP_COORD_YHI .eq $0ec0 {addr/64} ;high byte of vertex Y-coord (viewport)
DRAW_LINE .eq $1000 ;draw from $00,$01 to $02,$03 (device coords)
HIRES_ADDR_LO .eq $1500 ;hi-res row address, low byte
HIRES_ADDR_HI .eq $1600 ;hi-res row address, high byte
DIV7_TAB .eq $1700 ;result of N / 7
MOD7_TAB .eq $1800 ;result of N % 7
CLIP_DRAW_LINE .eq $1900 ;clip line to viewport and call DrawLine
UPD_DRAW_OBJ_HUD .eq $1c00 ;updates & draws objects and the HUD items
COPY_HIRES_2TO1 .eq $1c10 ;copies hi-res page 2 to page 1
INIT_WARP_VIEW .eq $1c60 ;init viewport for warp-out
DRAW_WARP_BKGND .eq $1ca0 ;draws stars, horizon line, mountains
MARK_ALL_INACTIVE .eq $1e0b ;zeroes $6600-6617
COPY_OBJ_STATE .eq $1e16 ;copies object state; X-reg=src slot, Y-reg=dest
DETECT_COLLISION .eq $1e83 ;detects collision; objects in X-reg and Y-reg
CLEAR_OBJ_MOVE .eq $1efd ;zeroes out the movement for Y-reg=slot
INIT_VIEWPORT_SLOT .eq $1f12 ;inits a viewport, Y-reg=index (0-3)
INIT_GFX_ENGINE .eq $1f69 ;inits viewports, obj slots
HIGH_SCORES .eq $ad00 {addr/128} ;high score table
score_eight .eq $ad80 {addr/16} ;past end of high score file
LOAD_FILE .eq $af60 ;load file specified by index in X-reg
LOAD_LEVEL_FILES .eq $af88 ;loads DYN#, LEV#, OBJ#
DISK_WRITE_FILE .eq $b006 ;used to save high scores
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
MIXCLR .eq $c052 ;RW display full screen
TXTPAGE1 .eq $c054 ;RW display page 1
TXTPAGE2 .eq $c055 ;RW display page 2 (or read/write aux mem)
HIRES .eq $c057 ;RW display hi-res graphics
SETAN0 .eq $c058 ;RW annunciator 0 off
SETAN1 .eq $c05a ;RW annunciator 1 off
CLRAN1 .eq $c05b ;RW annunciator 1 on
BUTN0 .eq $c061 ;R switch input 0 / open-apple
BUTN1 .eq $c062 ;R switch input 1 / closed-apple
BUTN2 .eq $c063 ;R switch input 2 / shift key
PADDL0 .eq $c064 ;R analog input 0
PADDL1 .eq $c065 ;R analog input 1
PTRIG .eq $c070 ;RW analog input reset
MON_BELL1 .eq $fbdd ;sound bell regardless of output device
.org $6000
;
; Updates all objects, draws them, and flips pages.
;
6000: 20 10 60 UpdateDrawSwap jsr UpdateDrawObjects ;update object positions, then draw them
6003: 4c d0 65 jmp SwapBuffers ;flip to other page
6006: 00 00 ff ff+ .junk 10
;
; Updates all objects and draws them.
;
UpdateDrawObjects
6010: ad f0 65 lda render_page ;make sure render page is 0/1
6013: 29 01 and #$01
6015: 8d f0 65 sta render_page
6018: aa tax
6019: bd 21 60 lda :hpage_addr,x ;convert to page address ($20/$40)
601c: 85 32 sta hpage
601e: 4c 23 60 jmp :Cont1
6021: 20 40 :hpage_addr .bulk $20,$40
6023: ae f0 65 :Cont1 ldx render_page
6026: bd f2 65 lda block_movement0,x
6029: f0 03 beq :Cont2
602b: 4c eb 60 jmp MovementDone
602e: 20 00 a0 :Cont2 jsr SoundStop
;
; Iterate over all active objects, updating their positions.
;
]obj_index .var $21 {addr/1}
]rotated_xc .var $22 {addr/2} ;result of ROTATE_INT8
]rotated_zc .var $24 {addr/2} ;result of ROTATE_INT8
6031: a0 17 ldy #c_num_objects-1
6033: 84 21 ObjUpdateLoop sty ]obj_index
6035: b9 00 66 lda obj_alive_flag,y ;active object in this slot?
6038: f0 05 beq :Inactive ;no, skip
603a: b9 80 67 lda obj_immob_flag,y
603d: f0 03 beq :ProcessObject
603f: 4c e5 60 :Inactive jmp :NextObject
6042: 18 :ProcessObject clc
6043: b9 d8 66 lda obj_facing,y ;update unit facing
6046: 79 68 67 adc obj_rotation_speed,y
6049: 99 d8 66 sta obj_facing,y
604c: b9 00 66 lda obj_alive_flag,y ;check bit 6
604f: c9 c0 cmp #$c0 ;also set?
6051: f0 ec beq :Inactive ;yes, skip update
; Move object's center point according to its direction and speed. Start by
; computing the movement vector.
6053: b9 38 67 lda obj_speed,y ;get current speed
6056: 85 00 sta $00 ;?
6058: 18 clc
6059: b9 d8 66 lda obj_facing,y ;get direction obj is facing
605c: 79 50 67 adc obj_facing_adj,y ;flip it if we're in reverse
605f: 18 clc
6060: 69 40 adc #64 ;convert (0,Z) to (X,0) with 90 deg ccw rot
6062: 20 00 0d jsr ROTATE_INT8 ;rotate (X,0) by angle in A-reg
; Add the vector, plus the grid-aligned movement, to the object's X coordinate.
6065: a4 21 ldy ]obj_index
6067: a2 00 ldx #$00 ;sign-extend positive
6069: b9 f0 66 lda obj_xc_movement,y ;grid-aligned move distance
606c: 10 01 bpl :IsPos
606e: ca dex ;sign-extend negative
606f: 18 :IsPos clc
6070: 65 22 adc ]rotated_xc ;add vector
6072: 90 02 bcc :NoInc
6074: e8 inx
6075: 18 clc
6076: 79 48 66 :NoInc adc obj_xc_lo,y ;add to previous position
6079: 99 48 66 sta obj_xc_lo,y ;save low part
607c: 8a txa
607d: 65 23 adc ]rotated_xc+1 ;repeat for high part
607f: 18 clc
6080: 79 60 66 adc obj_xc_hi,y
6083: aa tax ;save value
6084: 29 10 and #$10 ;check bit 4
6086: f0 05 beq :MakePos ;zero, so we're [0000,0fff] or [e000,efff]
6088: 8a txa ;one, so we're [1000,7fff] or [f000,ffff]
6089: 09 e0 ora #$e0 ;if was positive, make it negative
608b: d0 03 bne :Cont ;(always)
608d: 8a :MakePos txa
608e: 29 1f and #$1f ;if it was negative, make it positive (could be #$0f)
6090: 99 60 66 :Cont sta obj_xc_hi,y ;save high part
; Repeat for the Z coordinate.
6093: a2 00 ldx #$00
6095: b9 08 67 lda obj_zc_movement,y
6098: 10 01 bpl L609B
609a: ca dex
609b: 18 L609B clc
609c: 65 24 adc ]rotated_zc
609e: 90 02 bcc L60A2
60a0: e8 inx
60a1: 18 clc
60a2: 79 78 66 L60A2 adc obj_zc_lo,y
60a5: 99 78 66 sta obj_zc_lo,y
60a8: 8a txa
60a9: 65 25 adc ]rotated_zc+1
60ab: 18 clc
60ac: 79 90 66 adc obj_zc_hi,y
60af: aa tax
60b0: 29 10 and #$10
60b2: f0 05 beq L60B9
60b4: 8a txa
60b5: 09 e0 ora #$e0
60b7: d0 03 bne L60BC
60b9: 8a L60B9 txa
60ba: 29 1f and #$1f
60bc: 99 90 66 L60BC sta obj_zc_hi,y
; Repeat for the Y coordinate, which only adds the grid-aligned movement.
60bf: a2 00 ldx #$00
60c1: b9 20 67 lda obj_yc_movement,y
60c4: 10 01 bpl L60C7
60c6: ca dex
60c7: 18 L60C7 clc
60c8: 79 a8 66 adc obj_yc_lo,y
60cb: 99 a8 66 sta obj_yc_lo,y
60ce: 8a txa
60cf: 79 c0 66 adc obj_yc_high,y
60d2: aa tax
60d3: 29 10 and #$10
60d5: f0 05 beq L60DC
60d7: 8a txa
60d8: 09 e0 ora #$e0
60da: d0 03 bne L60DF
60dc: 8a L60DC txa
60dd: 29 1f and #$1f
60df: 99 c0 66 L60DF sta obj_yc_high,y
60e2: 20 10 a0 jsr SoundHalf
60e5: 88 :NextObject dey
60e6: 30 03 bmi MovementDone
60e8: 4c 33 60 jmp ObjUpdateLoop
; All objects have been moved. Next step is to draw stuff.
60eb: ad f1 65 MovementDone lda erase_view_flag ;erase viewport? (this is always $80)
60ee: f0 0c beq :NoClear ;no, branch
60f0: ad f0 65 lda render_page
60f3: 20 f9 60 jsr ClearViewport_jmp ;clear the viewport
60f6: 4c fc 60 jmp :NoClear
ClearViewport_jmp
60f9: 6c f6 65 jmp (jmp_addr_clear) ;clear viewport, page (0/1) in A-reg
60fc: ad f4 65 :NoClear lda mesh_data_ptr ;get pointer to mesh data table
60ff: 85 3c sta mesh_table_ptr
6101: ad f5 65 lda mesh_data_ptr+1
6104: 85 3d sta mesh_table_ptr+1
;
; Loop over all known viewports. (In practice, only viewport 0 is used.)
;
; Draw all visible objects in each viewport.
;
]viewport_index .var $20 {addr/1}
]obj_index .var $21 {addr/1}
]flag_2f? .var $2f {addr/1}
]viewer_xc .var $40 {addr/2}
]viewer_zc .var $42 {addr/2}
]viewer_yc .var $44 {addr/2}
]viewer_angle .var $46 {addr/1}
]viewer_zoom .var $47 {addr/1}
]near_plane_forv .var $48 {addr/2}
]near_plane .var $4a {addr/2}
]far_plane .var $4c {addr/2}
]skip_obj_index .var $4e {addr/1}
6106: a0 03 ldy #$03 ;start at 3, count down to 0
6108: 84 20 ViewPortLoop sty ]viewport_index
610a: b9 c0 67 lda view_actv_flags,y ;is this an active viewport?
610d: d0 03 bne :DoView ;yes
610f: 4c 1c 65 jmp :NextView ;no, move on to the next
6112: be c4 67 :DoView ldx view_skip_objs,y ;get viewer object index
6115: 86 4e stx ]skip_obj_index ;save for later
; Copy viewer position to zero-page variables.
6117: bd 48 66 lda obj_xc_lo,x
611a: 85 40 sta ]viewer_xc
611c: bd 60 66 lda obj_xc_hi,x
611f: 85 41 sta ]viewer_xc+1
6121: bd 78 66 lda obj_zc_lo,x
6124: 85 42 sta ]viewer_zc
6126: bd 90 66 lda obj_zc_hi,x
6129: 85 43 sta ]viewer_zc+1
612b: bd a8 66 lda obj_yc_lo,x
612e: 85 44 sta ]viewer_yc
6130: bd c0 66 lda obj_yc_high,x
6133: 85 45 sta ]viewer_yc+1
6135: 18 clc
6136: bd d8 66 lda obj_facing,x ;get viewer object facing
6139: 79 c8 67 adc view_angle_adjs,y ;add adjustment (for left/right/rear view)
613c: 49 ff eor #$ff ;invert it (convert to CW rotation)
613e: 18 clc
613f: 69 01 adc #$01
6141: 85 46 sta ]viewer_angle ;save for later
6143: b9 cc 67 lda view_zooms,y ;get zoom factor (7 or 9)
6146: 85 47 sta ]viewer_zoom
; Copy the near/far planes to zero page.
6148: b9 d0 67 lda view_near_vert,y ;near plane for vertices
614b: 85 48 sta ]near_plane_forv
614d: a9 00 lda #$00 ;high byte always zero
614f: 85 49 sta ]near_plane_forv+1
6151: b9 d4 67 lda view_near_lo,y
6154: 85 4a sta ]near_plane
6156: b9 d8 67 lda view_near_hi,y
6159: 85 4b sta ]near_plane+1
615b: b9 dc 67 lda view_far_lo,y
615e: 85 4c sta ]far_plane
6160: b9 e0 67 lda view_far_hi,y
6163: 85 4d sta ]far_plane+1
; Copy viewport center and edge parameters to zero page.
6165: a2 00 ldx #$00 ;assume positive
6167: b9 e4 67 lda view_x_adjs,y
616a: 10 01 bpl L616D
616c: ca dex ;sign-extend negative
616d: 85 50 L616D sta VIEW_X_ADJ
616f: 86 51 stx VIEW_X_ADJ+1
6171: a2 00 ldx #$00
6173: b9 e8 67 lda view_y_adjs,y
6176: 10 01 bpl L6179
6178: ca dex
6179: 85 52 L6179 sta VIEW_Y_ADJ
617b: 86 53 stx VIEW_Y_ADJ+1
617d: a2 00 ldx #$00
617f: b9 ec 67 lda view_left_vals,y
6182: 10 01 bpl L6185
6184: ca dex
6185: 85 54 L6185 sta VIEW_LEFT
6187: 86 55 stx VIEW_LEFT+1
6189: a2 00 ldx #$00
618b: b9 f0 67 lda view_right_vals,y
618e: 10 01 bpl L6191
6190: ca dex
6191: 85 56 L6191 sta VIEW_RIGHT
6193: 86 57 stx VIEW_RIGHT+1
6195: a2 00 ldx #$00
6197: b9 f4 67 lda view_top_vals,y
619a: 10 01 bpl L619D
619c: ca dex
619d: 85 58 L619D sta VIEW_TOP
619f: 86 59 stx VIEW_TOP+1
61a1: a2 00 ldx #$00
61a3: b9 f8 67 lda view_bottom_vals,y
61a6: 10 01 bpl L61A9
61a8: ca dex
61a9: 85 5a L61A9 sta VIEW_BOTTOM
61ab: 86 5b stx VIEW_BOTTOM+1
61ad: 20 40 a0 jsr UpdateSound
;
; Loop through all objects. If alive and visible, transform the center point to
; get its relative position for visibility testing and the radar.
;
]thing_2e? .var $2e {addr/1}
]obj_angle .var $33 {addr/1}
]obj_center_xc .var $34 {addr/2}
]obj_center_zc .var $36 {addr/2}
]obj_center_yc .var $38 {addr/2}
61b0: a0 00 ldy #$00
61b2: a9 80 lda #$80
61b4: 85 2f sta ]flag_2f?
:ObjectRenderLoop
61b6: 84 21 sty ]obj_index
61b8: b9 00 66 lda obj_alive_flag,y ;get the "is alive" bits
61bb: c9 c0 cmp #$c0 ;bits 6 and 7 both set?
61bd: d0 0f bne :NotC0 ;no, branch
61bf: a5 2f lda ]flag_2f? ;NOTE: not sure what this is --
61c1: d0 24 bne :SkipObj ; doesn't seem to be used by Stellar 7
61c3: 18 clc
61c4: a5 2e lda ]thing_2e?
61c6: 79 d8 66 adc obj_facing,y
61c9: 85 33 sta ]obj_angle
61cb: 4c 2b 63 jmp :DoDraw2
61ce: a9 80 :NotC0 lda #$80
61d0: 85 2f sta ]flag_2f?
61d2: b9 00 66 lda obj_alive_flag,y ;alive?
61d5: f0 10 beq :SkipObj ;no, skip it
61d7: b9 18 66 lda obj_type,y ;check object type
61da: f0 0b beq :SkipObj ;type zero, skip
61dc: c4 4e cpy ]skip_obj_index ;is it the viewer camera object?
61de: f0 07 beq :SkipObj ;yes, skip it
61e0: 0a asl A ;double it to get addr of offset
61e1: a8 tay
61e2: c8 iny
61e3: b1 3c lda (mesh_table_ptr),y ;do we have a mesh for this?
61e5: 10 09 bpl :DoObj ;yes, go do it
61e7: a9 00 :SkipObj lda #$00 ;nothing to draw, nothing to show on radar
61e9: 85 35 sta ]obj_center_xc+1
61eb: 85 37 sta ]obj_center_zc+1
61ed: 4c da 64 jmp :FinishObj
;
; The object is alive and can be rendered.
;
; Compute obj_center_xc = obj_xc - viewer_xc.
;
61f0: a4 21 :DoObj ldy ]obj_index
61f2: 38 sec
61f3: b9 48 66 lda obj_xc_lo,y
61f6: e5 40 sbc ]viewer_xc
61f8: 85 34 sta ]obj_center_xc
61fa: b9 60 66 lda obj_xc_hi,y
61fd: e5 41 sbc ]viewer_xc+1
61ff: aa tax
6200: 29 10 and #$10 ;clamp to map boundaries
6202: f0 05 beq L6209
6204: 8a txa
6205: 09 e0 ora #$e0
6207: d0 03 bne L620C
6209: 8a L6209 txa
620a: 29 1f and #$1f
620c: 85 35 L620C sta ]obj_center_xc+1
; Compute obj_center_zc = obj_zc - viewer_zc
620e: 38 sec
620f: b9 78 66 lda obj_zc_lo,y
6212: e5 42 sbc ]viewer_zc
6214: 85 36 sta ]obj_center_zc
6216: b9 90 66 lda obj_zc_hi,y
6219: e5 43 sbc ]viewer_zc+1
621b: aa tax
621c: 29 10 and #$10 ;clamp
621e: f0 05 beq L6225
6220: 8a txa
6221: 09 e0 ora #$e0
6223: d0 03 bne L6228
6225: 8a L6225 txa
6226: 29 1f and #$1f
6228: 85 37 L6228 sta ]obj_center_zc+1
; Compute obj_center_yc = obj_yc - viewer_yc
622a: 38 sec
622b: b9 a8 66 lda obj_yc_lo,y
622e: e5 44 sbc ]viewer_yc
6230: 85 38 sta ]obj_center_yc
6232: b9 c0 66 lda obj_yc_high,y
6235: e5 45 sbc ]viewer_yc+1
6237: aa tax
6238: 29 10 and #$10 ;clamp
623a: f0 05 beq L6241
623c: 8a txa
623d: 09 e0 ora #$e0
623f: d0 03 bne L6244
6241: 8a L6241 txa
6242: 29 1f and #$1f
6244: 85 39 L6244 sta ]obj_center_yc+1
; Rotate obj_center_xc/zc about the Y axis by the viewer angle.
6246: a5 46 lda ]viewer_angle ;get the viewer angle
6248: aa tax ;(could've just loaded it last?)
6249: a5 34 lda ]obj_center_xc ;copy XC/ZC to DP
624b: 85 00 sta $00
624d: a5 35 lda ]obj_center_xc+1
624f: 85 01 sta $01
6251: a5 36 lda ]obj_center_zc
6253: 85 02 sta $02
6255: a5 37 lda ]obj_center_zc+1
6257: 85 03 sta $03
6259: 8a txa
625a: 20 00 0b jsr ROTATE_COORDS ;rotate
625d: a5 04 lda $04 ;copy XC/ZC back out
625f: 85 34 sta ]obj_center_xc
6261: a5 05 lda $05
6263: 85 35 sta ]obj_center_xc+1
6265: a5 06 lda $06
6267: 85 36 sta ]obj_center_zc
6269: a5 07 lda $07
626b: 85 37 sta ]obj_center_zc+1
626d: 20 20 a0 jsr SoundTwice
; We now have the object's center position relative to the viewer. We want to
; do some visibility tests so we don't transform all the points if it's not
; visible.
;
; Start with a simple test on the near/far planes, to cull objects that are
; behind us or too far away. Because this is based on the object center, the
; near-plane test wouldn't be correct for very large objects, but Stellar 7
; doesn't have anything big.
;
; The far plane test is actually irrelevant, because it's set to $1600, which is
; more than half the size of the arena. Objects disappear when their
; transformed Z coordinates become negative. If you set the far plane to $0e00
; you can see the usual clipping effects at the edge, e.g. if you back up until
; the object just disappears, it will reappear if you rotate a few degress left
; or right. This doesn't happen with the negative-Z clip because the rotation
; results aren't wrapped at the map edge.
6270: a4 21 ldy ]obj_index
6272: b9 98 67 lda obj_visible_flag,y ;object invisible?
6275: f0 20 beq :NoDraw ;yes, don't draw (except maybe on radar)
; Compare center with near/far planes.
6277: a5 36 lda ]obj_center_zc ;compare ZC with near plane
6279: c5 4a cmp ]near_plane
627b: a5 37 lda ]obj_center_zc+1
627d: e5 4b sbc ]near_plane+1
627f: 6a ror A ;adjust sign
6280: 45 37 eor ]obj_center_zc+1
6282: 45 4b eor ]near_plane+1
6284: 0a asl A
6285: 90 10 bcc :NoDraw ;too close, don't draw
6287: a5 4c lda ]far_plane ;compare ZC with far plane
6289: c5 36 cmp ]obj_center_zc
628b: a5 4d lda ]far_plane+1
628d: e5 37 sbc ]obj_center_zc+1
628f: 6a ror A
6290: 45 4d eor ]far_plane+1 ;adjust sign
6292: 45 37 eor ]obj_center_zc+1
6294: 0a asl A
6295: b0 03 bcs :SideVis ;too far, don't draw
6297: 4c da 64 :NoDraw jmp :FinishObj
; Now we want to check the sides. We do that by taking the center point,
; offsetting it by the object's largest dimension, and projecting that point.
; If the point falls inside the view frustum, we know at least part of the
; object may be visible, and we need to process the vertices.
;
; We only check the X coordinate vs. left/right edges. The fliers don't get
; high enough to make culling based on the Y coordinate useful all that often.
]obj_span_x .var $22 {addr/2}
]obj_span_z .var $24 {addr/2}
]outer_xc .var $26 {addr/2}
629a: a4 21 :SideVis ldy ]obj_index
629c: b9 30 66 lda obj_max_dim,y ;get the maximum dimension for this object
629f: 85 22 sta ]obj_span_x
62a1: a9 00 lda #$00 ;sign-extend
62a3: 85 23 sta ]obj_span_x+1
62a5: 18 clc
62a6: a5 36 lda ]obj_center_zc ;add max dimension to the Z coord
62a8: 65 22 adc ]obj_span_x
62aa: 85 24 sta ]obj_span_z
62ac: a5 37 lda ]obj_center_zc+1
62ae: 65 23 adc ]obj_span_x+1
62b0: 85 25 sta ]obj_span_z+1
62b2: a5 35 lda ]obj_center_xc+1 ;check the object's position
62b4: 10 10 bpl :PosX ;XC negative (to the left)
62b6: 18 clc
62b7: a5 34 lda ]obj_center_xc ;take the center position
62b9: 65 22 adc ]obj_span_x ;add the span
62bb: 85 26 sta ]outer_xc
62bd: a5 35 lda ]obj_center_xc+1
62bf: 65 23 adc ]obj_span_x+1
62c1: 85 27 sta ]outer_xc+1
62c3: 4c d3 62 jmp :DoDiv
62c6: 38 :PosX sec ;XC positive (to the right)
62c7: a5 34 lda ]obj_center_xc
62c9: e5 22 sbc ]obj_span_x ;subtract the span
62cb: 85 26 sta ]outer_xc
62cd: a5 35 lda ]obj_center_xc+1
62cf: e5 23 sbc ]obj_span_x+1
62d1: 85 27 sta ]outer_xc+1
62d3: a5 47 :DoDiv lda ]viewer_zoom ;zoom level (2^N multiplier)
62d5: 85 06 sta $06
62d7: a5 26 lda ]outer_xc ;divide XC...
62d9: 85 00 sta $00
62db: a5 27 lda ]outer_xc+1
62dd: 85 01 sta $01
62df: a5 24 lda ]obj_span_z ;...by ZC
62e1: 85 02 sta $02
62e3: a5 25 lda ]obj_span_z+1
62e5: 85 03 sta $03
62e7: 20 10 08 jsr DIVIDE_16 ;divides $00/01 by $02/03; result in $00/01
62ea: a5 00 lda $00
62ec: 85 26 sta ]outer_xc ;save it in $26/27
62ee: a5 01 lda $01
62f0: 85 27 sta ]outer_xc+1
62f2: 20 40 a0 jsr UpdateSound
62f5: a5 35 lda ]obj_center_xc+1 ;get object center position
62f7: 10 13 bpl :PosX ;positive, branch
62f9: a5 26 lda ]outer_xc ;XC is negative, check left side
62fb: c5 54 cmp VIEW_LEFT
62fd: a5 27 lda ]outer_xc+1
62ff: e5 55 sbc VIEW_LEFT+1
6301: 6a ror A
6302: 45 27 eor ]outer_xc+1
6304: 45 55 eor VIEW_LEFT+1
6306: 0a asl A
6307: b0 16 bcs :DoDraw ;outer XC >= left edge, it fits in frame
6309: 4c da 64 jmp :FinishObj ;not visible, skip it
630c: a5 56 :PosX lda VIEW_RIGHT ;XC is positive, check right side
630e: c5 26 cmp ]outer_xc
6310: a5 57 lda VIEW_RIGHT+1
6312: e5 27 sbc ]outer_xc+1
6314: 6a ror A
6315: 45 57 eor VIEW_RIGHT+1
6317: 45 27 eor ]outer_xc+1
6319: 0a asl A
631a: b0 03 bcs :DoDraw ;right edge > outer XC, it fits in frame
631c: 4c da 64 jmp :FinishObj ;not visible, skip it
;
; Draw the transformed object.
;
631f: a4 21 :DoDraw ldy ]obj_index
6321: 18 clc
6322: b9 d8 66 lda obj_facing,y ;get object facing
6325: 65 46 adc ]viewer_angle ;add view angle
6327: 85 33 sta ]obj_angle ;save combined angle
6329: 85 2e sta ]thing_2e? ;also save here
632b: a9 00 :DoDraw2 lda #$00
632d: 85 2f sta ]flag_2f?
; Get a pointer to the mesh data (initially points at vertices).
632f: b9 18 66 lda obj_type,y ;get object type
6332: 0a asl A ;double it
6333: a8 tay
6334: 18 clc
6335: a5 3c lda mesh_table_ptr ;get table ptr, low
6337: 71 3c adc (mesh_table_ptr),y ;add offset, low
6339: 85 3a sta mesh_ptr ;save as pointer, low
633b: c8 iny
633c: 18 clc ;repeat with high byte
633d: a5 3d lda mesh_table_ptr+1
633f: 71 3c adc (mesh_table_ptr),y
6341: 85 3b sta mesh_ptr+1
;
; Generate list of transformed vertices.
;
; This performs a perpsective projection. For each point (x,y,z):
; P(x) = x/z
; P(y) = y/z
;
; Where P(x) and P(y) are clip-space coordinates.
;
]rot_xc .var $22 {addr/2}
]rot_zc .var $24 {addr/2}
]vertex_yc .var $26 {addr/2}
]p_xc .var $28 {addr/2}
]p_yc .var $2a {addr/2}
]prev1_angle .var $30 {addr/1}
]prev0_dist .var $31 {addr/1}
6343: a9 00 lda #$00 ;clear list of transformed vertices
6345: 85 2c sta view_vert_index
6347: a9 80 lda #$80 ;init to invalid value
6349: 85 31 sta ]prev0_dist
634b: a0 00 VertexXformLoop ldy #$00
634d: b1 3a lda (mesh_ptr),y ;get 0th byte of vertex data (distance)
634f: 10 03 bpl :Cont ;positive, not end of list
6351: 4c 7e 64 jmp :VerticesDone ;end of list found
6354: c5 31 :Cont cmp ]prev0_dist ;match previous?
6356: d0 5e bne :Match31 ;no, branch
6358: 85 00 sta $00
635a: a0 01 ldy #$01
635c: b1 3a lda (mesh_ptr),y ;get 1st byte of vertex data (angle)
635e: c5 30 cmp ]prev1_angle ;match previous?
6360: d0 5c bne :Match30 ;no, branch
; Angle and distance are the same as the previous entry, so we can skip the
; rotation and translation operations. The Y coordinate will be different, so
; we need to adjust for that and project the point. (This is only true for
; vertices that form vertical edges, and requires that the vertices be stored
; adjacent. It lets us skip translation/rotation of X/Z and projection of X.)
6362: a6 2c ldx view_vert_index
6364: a5 29 lda ]p_xc+1 ;get high byte of previous transformed XC
6366: 9d 40 0e sta CLIP_COORD_XHI,x ;save it
6369: c9 80 cmp #$80 ;was it invalid?
636b: f0 46 beq :NextVertex_jmp ;yup, this one will be too; skip it
636d: a5 28 lda ]p_xc ;copy the low byte
636f: 9d 00 0e sta CLIP_COORD_XLO,x
6372: a2 00 ldx #$00 ;assume positive
6374: a0 02 ldy #$02
6376: b1 3a lda (mesh_ptr),y ;get 3rd byte
6378: 10 01 bpl :IsPos
637a: ca dex ;negate high byte
637b: 18 :IsPos clc ;perform translation on YC
637c: 65 38 adc ]obj_center_yc
637e: 85 26 sta ]vertex_yc
6380: 8a txa
6381: 65 39 adc ]obj_center_yc+1
6383: 85 27 sta ]vertex_yc+1
; Compute perspective projection on Y.
6385: a5 47 lda ]viewer_zoom
6387: 85 06 sta $06
6389: a5 26 lda ]vertex_yc
638b: 85 00 sta $00
638d: a5 27 lda ]vertex_yc+1
638f: 85 01 sta $01
6391: a5 24 lda ]rot_zc
6393: 85 02 sta $02
6395: a5 25 lda ]rot_zc+1
6397: 85 03 sta $03
6399: 20 10 08 jsr DIVIDE_16 ;compute yc/zc
639c: a5 00 lda $00
639e: 85 2a sta ]p_yc
63a0: a5 01 lda $01
63a2: 85 2b sta ]p_yc+1
63a4: a6 2c ldx view_vert_index
63a6: a5 2a lda ]p_yc
63a8: 9d 80 0e sta CLIP_COORD_YLO,x
63ab: a5 2b lda ]p_yc+1 ;(reorder above to skip this load)
63ad: 9d c0 0e sta CLIP_COORD_YHI,x
63b0: 20 40 a0 jsr UpdateSound
63b3: 4c 6e 64 :NextVertex_jmp jmp :NextVertex
63b6: 85 31 :Match31 sta ]prev0_dist ;save the 1st byte
63b8: 85 00 sta $00 ;set as arg to rotate func
63ba: a0 01 ldy #$01
63bc: b1 3a lda (mesh_ptr),y ;get 1st byte of vertex data (angle)
63be: 85 30 :Match30 sta ]prev1_angle
63c0: 18 clc
63c1: 65 33 adc ]obj_angle ;form rotation angle
63c3: 20 00 0d jsr ROTATE_INT8 ;rotate $00, result in $22-25
63c6: 18 clc
63c7: a5 22 lda ]rot_xc ;perform translation on XC
63c9: 65 34 adc ]obj_center_xc
63cb: 85 22 sta ]rot_xc
63cd: a5 23 lda ]rot_xc+1
63cf: 65 35 adc ]obj_center_xc+1
63d1: 85 23 sta ]rot_xc+1
63d3: 18 clc ;perform translation on ZC
63d4: a5 24 lda ]rot_zc
63d6: 65 36 adc ]obj_center_zc
63d8: 85 24 sta ]rot_zc
63da: a5 25 lda ]rot_zc+1
63dc: 65 37 adc ]obj_center_zc+1
63de: 85 25 sta ]rot_zc+1
63e0: a6 2c ldx view_vert_index
; Check Z coord against near plane ($0001). The idea is to eliminate vertices
; that are behind us.
;
; This isn't quite correct -- ideally it would project the vertex and then clip
; the line to the viewport, rather than discarding all connected edges -- which
; is why if you drive into a sandsled you'll see parts of the nearest skid
; disappear. (This might be unavoidable because of the numeric resolution; not
; sure.)
63e2: a5 24 lda ]rot_zc ;get post-translate/rotate Z coordinate
63e4: c5 48 cmp ]near_plane_forv ;compare to the vertex-test near plane
63e6: a5 25 lda ]rot_zc+1
63e8: e5 49 sbc ]near_plane_forv+1
63ea: 6a ror A
63eb: 45 25 eor ]rot_zc+1 ;fix the sign bit
63ed: 45 49 eor ]near_plane_forv+1
63ef: 0a asl A
63f0: b0 0d bcs :NoCull ;ZC is greater, keep it
63f2: a9 80 lda #$80 ;mark vertex invalid
63f4: 9d 40 0e sta CLIP_COORD_XHI,x ;in the list we're generating
63f7: 85 29 sta ]p_xc+1 ; and for next time
63f9: 20 40 a0 jsr UpdateSound
63fc: 4c 6e 64 jmp :NextVertex
; Perform perpsective projection for X and Y.
63ff: a5 47 :NoCull lda ]viewer_zoom
6401: 85 06 sta $06
6403: a5 22 lda ]rot_xc
6405: 85 00 sta $00
6407: a5 23 lda ]rot_xc+1
6409: 85 01 sta $01
640b: a5 24 lda ]rot_zc
640d: 85 02 sta $02
640f: a5 25 lda ]rot_zc+1
6411: 85 03 sta $03
6413: 20 10 08 jsr DIVIDE_16 ;compute xc/zc
6416: a5 00 lda $00
6418: 85 28 sta ]p_xc
641a: a5 01 lda $01
641c: 85 29 sta ]p_xc+1
641e: a6 2c ldx view_vert_index
6420: a5 28 lda ]p_xc ;(reorder above to skip this load)
6422: 9d 00 0e sta CLIP_COORD_XLO,x
6425: a5 29 lda ]p_xc+1
6427: 9d 40 0e sta CLIP_COORD_XHI,x
642a: 20 40 a0 jsr UpdateSound
642d: a2 00 ldx #$00 ;assume positive
642f: a0 02 ldy #$02
6431: b1 3a lda (mesh_ptr),y ;get 2nd vertex byte (height)
6433: 10 01 bpl :IsPos
6435: ca dex ;make negative
6436: 18 :IsPos clc
6437: 65 38 adc ]obj_center_yc
6439: 85 26 sta ]vertex_yc
643b: 8a txa
643c: 65 39 adc ]obj_center_yc+1
643e: 85 27 sta ]vertex_yc+1
6440: a5 47 lda ]viewer_zoom
6442: 85 06 sta $06
6444: a5 26 lda ]vertex_yc
6446: 85 00 sta $00
6448: a5 27 lda ]vertex_yc+1
644a: 85 01 sta $01
644c: a5 24 lda ]rot_zc
644e: 85 02 sta $02
6450: a5 25 lda ]rot_zc+1
6452: 85 03 sta $03
6454: 20 10 08 jsr DIVIDE_16 ;compute yc/zc
6457: a5 00 lda $00
6459: 85 2a sta ]p_yc
645b: a5 01 lda $01
645d: 85 2b sta ]p_yc+1
645f: a6 2c ldx view_vert_index
6461: a5 2a lda ]p_yc ;(reorder above to skip this load)
6463: 9d 80 0e sta CLIP_COORD_YLO,x
6466: a5 2b lda ]p_yc+1
6468: 9d c0 0e sta CLIP_COORD_YHI,x
646b: 20 40 a0 jsr UpdateSound
; Advance to next vertex.
646e: 18 :NextVertex clc
646f: a5 3a lda mesh_ptr
6471: 69 03 adc #$03 ;mesh pointer += 3
6473: 85 3a sta mesh_ptr
6475: 90 02 bcc :NoInc
6477: e6 3b inc mesh_ptr+1
6479: e6 2c :NoInc inc view_vert_index
647b: 4c 4b 63 jmp VertexXformLoop
647e: e6 3a :VerticesDone inc mesh_ptr ;advance past end-of-list marker
6480: d0 02 bne DrawEdges
6482: e6 3b inc mesh_ptr+1
;
; For each item in the edge list, get the transformed vertices and draw the
; edge.
;
]tmp_index .var $2d {addr/1}
6484: a0 00 DrawEdges ldy #$00
6486: b1 3a lda (mesh_ptr),y ;get first index
6488: 30 50 bmi :FinishObj ;branch if end of list
648a: 85 2d sta ]tmp_index ;save index for later
648c: aa tax
648d: bd 40 0e lda CLIP_COORD_XHI,x
6490: c9 80 cmp #$80 ;invalid?
6492: f0 38 beq :Skip ;skip this edge
6494: c8 iny
6495: b1 3a lda (mesh_ptr),y ;get second index
6497: aa tax
6498: bd 40 0e lda CLIP_COORD_XHI,x
649b: c9 80 cmp #$80 ;invalid?
649d: f0 2d beq :Skip ;skip this edge
649f: a4 2d ldy ]tmp_index ;get first index
64a1: b9 00 0e lda CLIP_COORD_XLO,y ;get x0,y0 (viewport coords)
64a4: 85 10 sta $10
64a6: b9 40 0e lda CLIP_COORD_XHI,y
64a9: 85 11 sta $11
64ab: b9 80 0e lda CLIP_COORD_YLO,y
64ae: 85 12 sta $12
64b0: b9 c0 0e lda CLIP_COORD_YHI,y
64b3: 85 13 sta $13
64b5: bd 00 0e lda CLIP_COORD_XLO,x ;get x1,y1 (viewport coords)
64b8: 85 14 sta $14
64ba: bd 40 0e lda CLIP_COORD_XHI,x
64bd: 85 15 sta $15
64bf: bd 80 0e lda CLIP_COORD_YLO,x
64c2: 85 16 sta $16
64c4: bd c0 0e lda CLIP_COORD_YHI,x
64c7: 85 17 sta $17
64c9: 20 00 19 jsr CLIP_DRAW_LINE ;clip to viewport and draw line
64cc: 18 :Skip clc
64cd: a5 3a lda mesh_ptr ;advance to next pair
64cf: 69 02 adc #$02 ;mesh pointer += 2
64d1: 85 3a sta mesh_ptr
64d3: 90 af bcc DrawEdges
64d5: e6 3b inc mesh_ptr+1
64d7: 4c 84 64 jmp DrawEdges
; Finish by setting the radar coordinates. Things that aren't shown on the
; radar display are removed later. We just use the high byte [$f0,$0f], giving
; a 32x32 range for our 28x28 radar.
64da: a4 21 :FinishObj ldy ]obj_index
64dc: a5 35 lda ]obj_center_xc+1 ;just use the high byte of the transformed position
64de: 99 d0 0c sta RADAR_XCOORDS,y
64e1: a5 37 lda ]obj_center_zc+1
64e3: 99 e8 0c sta RADAR_YCOORDS,y
64e6: c8 iny
64e7: c0 18 cpy #c_num_objects
64e9: b0 03 bcs :DoneDrawing
64eb: 4c b6 61 jmp :ObjectRenderLoop
64ee: ad fc 65 :DoneDrawing lda show_bkgnd_flag ;show stars/mountains?
64f1: f0 29 beq :NextView ;no, skip this bit
64f3: a6 45 ldx ]viewer_yc+1 ;check high byte of viewer Y coord
64f5: e8 inx ;?
64f6: e8 inx ;?
64f7: e0 04 cpx #$04
64f9: b0 21 bcs :NextView ;height >= $0200, no background
]bk_horiz_pos .var $22 {addr/2}
]bk_vert_pos .var $24 {addr/2}
]horizon_adj .var $26 {addr/1}
64fb: a5 44 lda ]viewer_yc ;configure the viewer height
64fd: 85 24 sta ]bk_vert_pos
64ff: a5 45 lda ]viewer_yc+1
6501: 85 25 sta ]bk_vert_pos+1
6503: a9 00 lda #$00 ;horizontal position is viewer angle * 2
6505: 85 23 sta ]bk_horiz_pos+1
6507: a5 46 lda ]viewer_angle
6509: 0a asl A
650a: 26 23 rol ]bk_horiz_pos+1
650c: 85 22 sta ]bk_horiz_pos
650e: ad fd 65 lda horizon_baseline ;copy horizon offset
6511: 85 26 sta ]horizon_adj
6513: 20 00 70 jsr DrawMountains ;draw mountains
6516: 20 b0 70 jsr DrawStars ;draw stars
6519: 20 30 a0 jsr SoundThrice
651c: a4 20 :NextView ldy ]viewport_index ;move to next viewport
651e: 88 dey
651f: 30 03 bmi :Done
6521: 4c 08 61 jmp ViewPortLoop
6524: 60 :Done rts
6525: ff 00 00 ff+ .junk 171
; Switches the display to show the page we just rendered, then switches the
; rendering output to the other page.
65d0: 2c 57 c0 SwapBuffers bit HIRES ;enable hi-res graphics
65d3: 2c 50 c0 bit TXTCLR ; (probably not needed by this point?)
65d6: ad f0 65 lda render_page ;check which page we're rendering on
65d9: d0 06 bne :Show2 ;rendering on page 2, show it
65db: 2c 54 c0 bit TXTPAGE1 ;show page 1
65de: 4c e4 65 jmp :Flip
65e1: 2c 55 c0 :Show2 bit TXTPAGE2
65e4: 49 01 :Flip eor #$01
65e6: 8d f0 65 sta render_page
65e9: 60 rts
65ea: 00 00 ff ff+ .junk 6
;
; Additional graphics engine state.
;
65f0: ff render_page .dd1 $ff ;page we're rendering on (0/1)
65f1: ff erase_view_flag .dd1 $ff ;bool 00/80: erase viewport before drawing objects?
65f2: 00 block_movement0 .dd1 $00 ;bool 00/80: don't move units when rendering page 0
65f3: 00 block_movement1 .dd1 $00 ;bool 00/80: don't move units when rendering page 1
65f4: ff ff mesh_data_ptr .dd2 $ffff ;set to e.g. $6800 (mesh data)
65f6: 00 00 jmp_addr_clear .dd2 $0000 ;ClearScreen or ClearViewport
65f8: ff ff jmp_addr2? .dd2 $ffff ;not used
65fa: 00 00 jmp_addr3? .dd2 $0000 ;not used
65fc: ff show_bkgnd_flag .dd1 $ff ;bool 00/80: show stars/mountains?
horizon_baseline
65fd: ff .dd1 $ff ;star/mountain baseline (e.g. $f0 is below hrz line)
65fe: 00 00 .align $0100 (2 bytes)
;
; Object state for 3D engine. Room for 24 active objects.
;
; IMPORTANT: $6600-66EF is overwritten by OBJ# files. It looks like much of the
; file is junk, but the values from $6648-66a7 (the XC/ZC values) determine the
; initial positions of the units on each level. (The unit types are determined
; by DYN#, $8400-8417.)
;
; $66B0-66CF is checked with a rolling XOR by the OBJ# file loader in ROCK3.
;
; ---
;
; The high bit is set on all active objects, i.e. $80=active, $00=slot unused.
; Additionally it appears that bit 6 may be set ($c0).
6600: ff ff 00 00+ obj_alive_flag .junk 24
;
; Object type (0-63). Type 0 is the player, $01-0E are active units, $0F-15 are
; projectiles, $16-18 are stationary objects (obstacle, fuelbay, warplink), $19+
; are things like explosion animations.
6618: ff ff 00 00+ obj_type .junk 24
;
; Maximum object dimension. Used by the gfx engine for visibility and collision
; calculations.
6630: ff ff 00 00+ obj_max_dim .junk 24
;
; Object X coordinate [-4096,4095]. For a viewer at the origin looking at angle
; 0, +X is to right.
6648: ff ff 00 00+ obj_xc_lo .junk 24
6660: ff ff 00 00+ obj_xc_hi .junk 24
;
; Object Z coordinate [-4096,4095]. For a viewer at the origin looking at angle
; 0, +Z is out into the distance.
6678: ff ff 00 00+ obj_zc_lo .junk 24
6690: ff ff 00 00+ obj_zc_hi .junk 24
;
; Object Y coordinate [-4096,4095]. +Y is upward.
66a8: ff ff 00 00+ obj_yc_lo .junk 24
66c0: ff ff 00 00+ obj_yc_high .junk 24
;
; Object's facing angle (0-255).
66d8: ff ff 00 00+ obj_facing .junk 24
;
; Grid-aligned movement. The object moves this distance every frame. The
; object's facing has no bearing on the movement. (These don't appear to be
; used by Stellar 7.)
66f0: ff ff 00 00+ obj_xc_movement .junk 24
6708: ff ff 00 00+ obj_zc_movement .junk 24
6720: ff ff 00 00+ obj_yc_movement .junk 24
;
; Object speed. Every frame, the object moves this distance toward its facing
; angle.
6738: ff ff 00 00+ obj_speed .junk 24
;
; Object facing adjustment, applied when updating the object's position. For
; the player this is $00 when moving forward and $80 in reverse. For Seekers
; this is used to counteract the constant rotation, so that it homes in on the
; player while spinning.
6750: ff ff 00 00+ obj_facing_adj .junk 24
;
; The object rotates this much (in 360/256ths degree angle units) each frame.
; Used for Pulsars and Seekers.
obj_rotation_speed
6768: ff ff 00 00+ .junk 24
;
; Flag (00/80) indicating whether the object is immobile. This seems to be
; applied to things that explode, e.g. projectiles, as an alternative to setting
; all of the speed fields to zero.
6780: ff ff 00 00+ obj_immob_flag .junk 24
;
; Flag (00/80) indicating whether the object is visible. Set to zero for
; cloaked units. (Not used for player, which is never visible in the viewport.)
obj_visible_flag
6798: ff ff 00 00+ .junk 24
67b0: ff ff 00 00+ .junk 16 ;junk?
;
; Viewport parameters.
;
; The code supports up to 4 active viewports, presumably for things like the
; missile camera view that was used in Arcticfox. I've only ever seen viewport
; 0 active.
;
; ---
;
; The "active" flag is $80 for active viewports, $00 for inactive.
67c0: ff ff 00 00 view_actv_flags .junk 4 ;bool 00/ff: is viewport N active
;
; Object number (0-23) to skip on this viewport. A first-person view doesn't
; show the person, but we need to know which object that is. For Stellar 7 the
; player is always object 0.
67c4: ff ff 00 00 view_skip_objs .junk 4
;
; Viewport angle adjustment. Used for looking in a direction other than
; straight ahead, e.g. for implementing left/right/rear views. 64 would be a
; 90-degree CCW turn (left view). Always 0 in Stellar 7.
67c8: ff ff 00 00 view_angle_adjs .junk 4
;
; Camera zoom. Normally 7, increases to 9 when the 'Z' key is hit.
67cc: ff ff 00 00 view_zooms .junk 4
;
; Near clip plane applied to individual vertices. This is used to skip vertices
; in objects whose center is in front of the viewer, but have vertices that are
; projected behind. For 16-bit comparisons the high byte is always zero.
; Stellar 7 uses a value of $0001.
67d0: ff ff 00 00 view_near_vert .junk 4
;
; Near/far clip planes, used to cull entire objects based on the object's center
; point. Stellar 7 uses near=$0000, far=$1600.
;
; This doesn't work correctly for large objects, which could be centered behind
; the viewer but extend into past it. The game doesn't have any of those, and a
; quick cull is beneficial for performance.
67d4: ff ff 00 00 view_near_lo .junk 4
67d8: ff ff 00 00 view_near_hi .junk 4
67dc: ff ff 00 00 view_far_lo .junk 4
67e0: ff ff 00 00 view_far_hi .junk 4
;
; Viewport screen adjustments. These values are added to clipped coordinates to
; get screen coordinates.
67e4: ff ff 00 00 view_x_adjs .junk 4
67e8: ff ff 00 00 view_y_adjs .junk 4
;
; Viewport bounds (signed 8-bit values). These are used to clip lines.
;
; Top/right are positive, left/bottom are negative.
67ec: ff ff 00 00 view_left_vals .junk 4 ;viewport left edges
67f0: ff ff 00 00 view_right_vals .junk 4 ;viewport right edges
67f4: ff ff 00 00 view_top_vals .junk 4 ;viewport top edges
view_bottom_vals
67f8: ff ff 00 00 .junk 4 ;viewport bottom edges
67fc: ff ff 00 00 .junk 4 ;unused?
;
; $6800-6E75 overwritten by BRIEF.ST
; $6800-73FF overwritten by LEV#
;
; The object mesh data is loaded at $6800. To reduce the memory footprint, only
; the meshes used on a given level are loaded at any given time.
6800: fe ff 00 00+ mesh_data .junk 2048
;
; Draws the background mountains.
;
; On entry:
; $22/23: 2x player facing angle ($0000-$01fe)
; $24/25: vertical position
; $26: horizon adjustment
;
; IMPORTANT: this implementation is overwritten by the LEV# file.
;
• Clear variables
]view_x0 .var $10 {addr/2}
]view_y0 .var $12 {addr/2}
]view_x1 .var $14 {addr/2}
]view_y1 .var $16 {addr/2}
]bk_horiz_pos .var $22 {addr/2}
]bk_vert_pos .var $24 {addr/2}
]horizon_adj .var $26 {addr/1}
]vertical_pos .var $28 {addr/2}
]data_ptr .var $2a {addr/2}
]tmp .var $2c {addr/1}
7000: 38 DrawMountains sec ;compute vertical baseline
7001: a5 26 lda ]horizon_adj
7003: e5 24 sbc ]bk_vert_pos
7005: 85 28 sta ]vertical_pos
7007: a9 00 lda #$00
7009: e5 25 sbc ]bk_vert_pos+1
700b: 85 29 sta ]vertical_pos+1
700d: a9 00 lda #<mountain_data ;configure pointer to mountain data
700f: 85 2a sta ]data_ptr
7011: a9 72 lda #>mountain_data
7013: 85 2b sta ]data_ptr+1
; Extract list of vertices for mountain data.
7015: a2 00 ldx #$00
7017: a0 00 :ExtractLoop ldy #$00
7019: b1 2a lda (]data_ptr),y ;get X position, high byte
701b: 30 3a bmi :EndOfVerts ;negative value means end of list; branch
701d: 85 2c sta ]tmp
701f: a0 01 ldy #$01
7021: 38 sec
7022: b1 2a lda (]data_ptr),y ;get X position, low byte
7024: e5 22 sbc ]bk_horiz_pos
7026: 9d 00 0e sta CLIP_COORD_XLO,x ;xc low
7029: a5 2c lda ]tmp
702b: e5 23 sbc ]bk_horiz_pos+1
702d: 29 01 and #$01
702f: 49 ff eor #$ff
7031: 18 clc
7032: 69 01 adc #$01
7034: 9d 40 0e sta CLIP_COORD_XHI,x ;xc high
7037: a0 02 ldy #$02
7039: 38 sec
703a: a5 28 lda ]vertical_pos
703c: f1 2a sbc (]data_ptr),y ;get Y position (unsigned)
703e: 9d 80 0e sta CLIP_COORD_YLO,x ;yc low
7041: a5 29 lda ]vertical_pos+1
7043: e9 00 sbc #$00
7045: 9d c0 0e sta CLIP_COORD_YHI,x ;yc high
7048: e8 inx
7049: 18 clc ;data pointer += 3
704a: a5 2a lda ]data_ptr
704c: 69 03 adc #$03
704e: 85 2a sta ]data_ptr
7050: 90 c5 bcc :ExtractLoop
7052: e6 2b inc ]data_ptr+1
7054: 4c 17 70 jmp :ExtractLoop
7057: e6 2a :EndOfVerts inc ]data_ptr ;data pointer ++
7059: d0 02 bne :DrawLoop
705b: e6 2b inc ]data_ptr+1
; Extract the list of line indexes and draw them.
705d: a0 00 :DrawLoop ldy #$00
705f: b1 2a lda (]data_ptr),y ;start vertex
7061: 30 3f bmi :Done ;found the $80, bail
7063: aa tax
7064: a0 01 ldy #$01
7066: b1 2a lda (]data_ptr),y ;end vertex
7068: a8 tay
7069: b9 00 0e lda CLIP_COORD_XLO,y ;copy X/Y to line clip input
706c: 85 10 sta ]view_x0
706e: b9 40 0e lda CLIP_COORD_XHI,y
7071: 85 11 sta ]view_x0+1
7073: b9 80 0e lda CLIP_COORD_YLO,y
7076: 85 12 sta ]view_y0
7078: b9 c0 0e lda CLIP_COORD_YHI,y
707b: 85 13 sta ]view_y0+1
707d: bd 00 0e lda CLIP_COORD_XLO,x
7080: 85 14 sta ]view_x1
7082: bd 40 0e lda CLIP_COORD_XHI,x
7085: 85 15 sta ]view_x1+1
7087: bd 80 0e lda CLIP_COORD_YLO,x
708a: 85 16 sta ]view_y1
708c: bd c0 0e lda CLIP_COORD_YHI,x
708f: 85 17 sta ]view_y1+1
7091: 20 00 19 jsr CLIP_DRAW_LINE ;clip line and draw
7094: 18 clc ;data pointer += 2
7095: a5 2a lda ]data_ptr
7097: 69 02 adc #$02 ;2 bytes/entry
7099: 85 2a sta ]data_ptr
709b: 90 c0 bcc :DrawLoop
709d: e6 2b inc ]data_ptr+1 ;(not possible - data is page-aligned)
709f: 4c 5d 70 jmp :DrawLoop
70a2: 60 :Done rts
70a3: 20 44 41 4d+ .str ‘ DAMON SLYE’,$0a,$0a
;
; Draws the stars and the horizon line.
;
; IMPORTANT: this implementation is overwritten by the LEV# file. Some
; implementations may do things differently, e.g. LEV1 has a feature that makes
; the stars twinkle.
;
• Clear variables
]base_x .var $00 {addr/2}
]base_y .var $02 {addr/2}
]data_ptr .var $04 {addr/2}
]data_item .var $06 {addr/1}
]xc .var $08 {addr/1}
]hptr .var $0a {addr/2}
]row1_ptr .var $0c {addr/2}
]row2_ptr .var $0e {addr/2}
]left_edge .var $10 {addr/1}
]right_edge .var $11 {addr/1}
]top_edge .var $12 {addr/1}
]bottom_edge .var $13 {addr/1}
]end_col .var $14 {addr/1}
]bk_horiz_pos .var $22 {addr/2}
]bk_vert_pos .var $24 {addr/2}
]horizon_adj .var $26 {addr/1}
70b0: 38 DrawStars sec ;take the horizontal offset (facing angle * 2)
70b1: a5 22 lda ]bk_horiz_pos
70b3: e5 50 sbc VIEW_X_ADJ ;subtract the viewport adjustment
70b5: 85 00 sta ]base_x ;save
70b7: a5 23 lda ]bk_horiz_pos+1
70b9: e9 00 sbc #$00
70bb: 85 01 sta ]base_x+1
70bd: 38 sec ;take the viewport adjustment
70be: a5 52 lda VIEW_Y_ADJ
70c0: e5 26 sbc ]horizon_adj ;subtract the horizon adjustment
70c2: 85 02 sta ]base_y ;save
70c4: a9 00 lda #$00 ;extend to 16 bits
70c6: e9 00 sbc #$00
70c8: 85 03 sta ]base_y+1
70ca: 18 clc ;add in the vertical offset
70cb: a5 02 lda ]base_y
70cd: 65 24 adc ]bk_vert_pos
70cf: 85 02 sta ]base_y
70d1: a5 03 lda ]base_y+1
70d3: 65 25 adc ]bk_vert_pos+1
70d5: 85 03 sta ]base_y+1
70d7: 38 sec ;+1
70d8: a5 50 lda VIEW_X_ADJ ;compute leftmost viewport position in screen coords
70da: 65 54 adc VIEW_LEFT
70dc: 85 10 sta ]left_edge
70de: 18 clc
70df: a5 50 lda VIEW_X_ADJ ;same for right edge
70e1: 65 56 adc VIEW_RIGHT
70e3: 85 11 sta ]right_edge
70e5: 38 sec
70e6: a5 52 lda VIEW_Y_ADJ ;topmost viewport position in screen coords
70e8: e5 58 sbc VIEW_TOP
70ea: 85 12 sta ]top_edge
70ec: e6 12 inc ]top_edge ;+1
70ee: 38 sec ;same for bottom edge
70ef: a5 52 lda VIEW_Y_ADJ
70f1: e5 5a sbc VIEW_BOTTOM
70f3: 85 13 sta ]bottom_edge
70f5: a9 00 lda #<star_data ;get pointer to star data
70f7: 85 04 sta ]data_ptr
70f9: a9 73 lda #>star_data
70fb: 85 05 sta ]data_ptr+1
70fd: a0 00 :ItemLoop ldy #$00
70ff: b1 04 lda (]data_ptr),y ;get first byte
7101: 10 03 bpl :DrawStar ;if positive, draw it
7103: 4c c2 71 jmp :DrawHorizon ;otherwise, end of list; finish with horizon line
7106: 85 06 :DrawStar sta ]data_item
7108: a0 01 ldy #$01 ;get second byte (X low)
710a: 38 sec
710b: b1 04 lda (]data_ptr),y
710d: e5 00 sbc ]base_x ;subtract from base X
710f: 85 08 sta ]xc
7111: a5 06 lda ]data_item ;get first byte again
7113: e5 01 sbc ]base_x+1 ;subtract high byte
7115: 29 01 and #$01 ;mask of the optional $40
7117: d0 40 bne :AdvancePtr ;if not zero, it's not on screen
7119: a5 08 lda ]xc ;see if we fit in viewport
711b: c5 10 cmp ]left_edge
711d: 90 3a bcc :AdvancePtr ;too far left
711f: c5 11 cmp ]right_edge
7121: b0 36 bcs :AdvancePtr ;too far right
7123: a0 02 ldy #$02 ;get third byte (Y coord)
7125: 18 clc
7126: b1 04 lda (]data_ptr),y
7128: 65 02 adc ]base_y ;add to base
712a: aa tax
712b: a9 00 lda #$00 ;high byte
712d: 65 03 adc ]base_y+1
712f: d0 28 bne :AdvancePtr ;too big, off screen
7131: e4 12 cpx ]top_edge
7133: 90 24 bcc :AdvancePtr ;too far up
7135: e4 13 cpx ]bottom_edge
7137: b0 20 bcs :AdvancePtr ;too far down
7139: bd 00 15 lda HIRES_ADDR_LO,x
713c: 85 0a sta ]hptr
713e: bd 00 16 lda HIRES_ADDR_HI,x
7141: 05 32 ora hpage
7143: 85 0b sta ]hptr+1
7145: 24 06 bit ]data_item ;check first byte for $40
7147: 70 1e bvs :DrawCross ;found it, draw a cross instead
; Draw single pixel.
7149: a6 08 ldx ]xc
714b: bc 00 17 ldy DIV7_TAB,x
714e: bd 00 18 lda MOD7_TAB,x
7151: aa tax
7152: bd b4 71 lda hires_pixel,x
7155: 11 0a :DrawOne ora (]hptr),y
7157: 91 0a sta (]hptr),y
7159: 18 :AdvancePtr clc ;data pointer += 3
715a: a5 04 lda ]data_ptr
715c: 69 03 adc #$03
715e: 85 04 sta ]data_ptr
7160: 90 9b bcc :ItemLoop
7162: e6 05 inc ]data_ptr+1 ;(not possible - data is page-aligned)
7164: 4c fd 70 jmp :ItemLoop
7167: ca :DrawCross dex ;up one
7168: bd 00 15 lda HIRES_ADDR_LO,x ;get hi-res line address
716b: 85 0c sta ]row1_ptr
716d: bd 00 16 lda HIRES_ADDR_HI,x
7170: 05 32 ora hpage
7172: 85 0d sta ]row1_ptr+1
7174: e8 inx ;down two
7175: e8 inx
7176: bd 00 15 lda HIRES_ADDR_LO,x ;get hi-res line address
7179: 85 0e sta ]row2_ptr
717b: bd 00 16 lda HIRES_ADDR_HI,x
717e: 05 32 ora hpage
7180: 85 0f sta ]row2_ptr+1
; Draw a '+' shape.
7182: a6 08 ldx ]xc
7184: bc 00 17 ldy DIV7_TAB,x
7187: bd 00 18 lda MOD7_TAB,x
718a: aa tax
718b: bd b4 71 lda hires_pixel,x
718e: 11 0c ora (]row1_ptr),y ;draw single pixel on line above
7190: 91 0c sta (]row1_ptr),y
7192: bd b4 71 lda hires_pixel,x
7195: 11 0e ora (]row2_ptr),y ;draw single pixel on line below
7197: 91 0e sta (]row2_ptr),y
7199: bd bb 71 lda hires_3_pixel,x
719c: 11 0a ora (]hptr),y ;draw triple pixel on middle line
719e: 91 0a sta (]hptr),y
71a0: e0 00 cpx #$00 ;at left edge of byte?
71a2: d0 06 bne :NotByteLeft ;no
71a4: 88 dey ;yes, back up a byte and plot a single pixel
71a5: a9 40 lda #$40
71a7: 4c 55 71 jmp :DrawOne
71aa: e0 06 :NotByteLeft cpx #$06 ;at right edge of byte?
71ac: d0 ab bne :AdvancePtr ;no, we're done with this
71ae: c8 iny ;yes, move a byte to the right
71af: a9 01 lda #$01 ;light up the leftmost pixel
71b1: 4c 55 71 jmp :DrawOne
71b4: 01 02 04 08+ hires_pixel .bulk $01,$02,$04,$08,$10,$20,$40 ;single-pixel mask
71bb: 03 07 0e 1c+ hires_3_pixel .bulk $03,$07,$0e,$1c,$38,$70,$60 ;triple-pixel mask (double at edges)
; Draw the horizon line. If we're warping out it could be off screen.
71c2: a5 26 :DrawHorizon lda ]horizon_adj
71c4: f0 36 beq :Bail
71c6: 18 clc
71c7: 65 02 adc ]base_y
71c9: aa tax ;this is the row where we draw the horizon
71ca: a9 00 lda #$00
71cc: 65 03 adc ]base_y+1
71ce: d0 2c bne :Bail
71d0: e4 12 cpx ]top_edge ;off the top?
71d2: 90 28 bcc :Bail ;yes, bail
71d4: e4 13 cpx ]bottom_edge ;off the bottom?
71d6: b0 24 bcs :Bail ;yes, bail
71d8: bd 00 15 lda HIRES_ADDR_LO,x ;set up hi-res pointer
71db: 85 0a sta ]hptr
71dd: bd 00 16 lda HIRES_ADDR_HI,x
71e0: 05 32 ora hpage
71e2: 85 0b sta ]hptr+1
71e4: a6 10 ldx ]left_edge ;start at left edge of viewport
71e6: bd 00 17 lda DIV7_TAB,x
71e9: a8 tay
71ea: a6 11 ldx ]right_edge ;stop at right edge
71ec: bd 00 17 lda DIV7_TAB,x
71ef: 85 14 sta ]end_col
71f1: a9 7f lda #$7f ;solid white
71f3: 91 0a :HorizonLoop sta (]hptr),y ;draw
71f5: c8 iny
71f6: c4 14 cpy ]end_col ;reached the end?
71f8: 90 f9 bcc :HorizonLoop ;not yet
71fa: 91 0a sta (]hptr),y ;draw last column
71fc: 60 :Bail rts
71fd: 24 d0 e7 .align $0100 (3 bytes)
;
; Data for background mountains, loaded from LEV#.
;
; This is a list of vertices terminated by $80, followed by a list of lines,
; also terminated with $80. The vertices are 3 bytes each, with a 16-bit big-
; endian X coordinate ($0000-01ff) and an 8-bit unsigned Y coordinate ($00-ff).
; The Y coordinate puts $00 at the top (well off screen) and $fe at the horizon
; line. A mountain peak could, for example, be at $d5.
;
; This code generates clip-space lines.
;
7200: 28 37 29 3a+ mountain_data .junk 256
;
; Star data, in 3-byte groups. Part of the LEV# file.
;
; As with the mountain data, this has a 16-bit big-endian X coordinate and an 8-
; bit unsigned Y coordinate. Values are interpreted the same way, so you can
; position stars around the mountains. Some of the star data will only be
; visible when warping out.
;
; If bit 6 of the first byte of the X coordinate is set ($40), the star is drawn
; as a cross instead of a single dot.
;
7300: 0a 00 8a 73+ star_data .junk 256
;
; Clears the 224x157 viewport on the hi-res screen. Called indirectly through a
; viewport-clear vector every frame, before objects are drawn.
;
; The viewport spans columns 1-32, lines 33 - 189.
;
; This requires ((5*157 + 9) * 32) = 25,408 cycles, plus 32 sound updates, for a
; total of about 26,950.
;
; On entry:
; A-reg: 0 for page 1, nonzero for page 2
;
7400: a8 ClearViewport tay ;0 for page 1, nonzero for page 2
7401: a9 00 lda #$00
7403: a2 1f ldx #$1f
7405: c0 00 cpy #$00
7407: f0 03 beq :Page1
7409: 4c ef 75 jmp :Page2
740c: 9d 01 26 :Page1 sta $2601,x
740f: 9d 01 2a sta $2a01,x
7412: 9d 01 2e sta $2e01,x
7415: 9d 01 32 sta $3201,x
7418: 9d 01 36 sta $3601,x
741b: 9d 01 3a sta $3a01,x
741e: 9d 01 3e sta $3e01,x
7421: 9d 81 22 sta $2281,x
7424: 9d 81 26 sta $2681,x
7427: 9d 81 2a sta $2a81,x
742a: 9d 81 2e sta $2e81,x
742d: 9d 81 32 sta $3281,x
7430: 9d 81 36 sta $3681,x
7433: 9d 81 3a sta $3a81,x
7436: 9d 81 3e sta $3e81,x
7439: 9d 01 23 sta $2301,x
743c: 9d 01 27 sta $2701,x
743f: 9d 01 2b sta $2b01,x
7442: 9d 01 2f sta $2f01,x
7445: 9d 01 33 sta $3301,x
7448: 9d 01 37 sta $3701,x
744b: 9d 01 3b sta $3b01,x
744e: 9d 01 3f sta $3f01,x
7451: 9d 81 23 sta $2381,x
7454: 9d 81 27 sta $2781,x
7457: 9d 81 2b sta $2b81,x
745a: 9d 81 2f sta $2f81,x
745d: 9d 81 33 sta $3381,x
7460: 9d 81 37 sta $3781,x
7463: 9d 81 3b sta $3b81,x
7466: 9d 81 3f sta $3f81,x
7469: 9d 29 20 sta $2029,x
746c: 9d 29 24 sta $2429,x
746f: 9d 29 28 sta $2829,x
7472: 9d 29 2c sta $2c29,x
7475: 9d 29 30 sta $3029,x
7478: 9d 29 34 sta $3429,x
747b: 9d 29 38 sta $3829,x
747e: 9d 29 3c sta $3c29,x
7481: 9d a9 20 sta $20a9,x
7484: 9d a9 24 sta $24a9,x
7487: 9d a9 28 sta $28a9,x
748a: 9d a9 2c sta $2ca9,x
748d: 9d a9 30 sta $30a9,x
7490: 9d a9 34 sta $34a9,x
7493: 9d a9 38 sta $38a9,x
7496: 9d a9 3c sta $3ca9,x
7499: 9d 29 21 sta $2129,x
749c: 9d 29 25 sta $2529,x
749f: 9d 29 29 sta $2929,x
74a2: 9d 29 2d sta $2d29,x
74a5: 9d 29 31 sta $3129,x
74a8: 9d 29 35 sta $3529,x
74ab: 9d 29 39 sta $3929,x
74ae: 9d 29 3d sta $3d29,x
74b1: 9d a9 21 sta $21a9,x
74b4: 9d a9 25 sta $25a9,x
74b7: 9d a9 29 sta $29a9,x
74ba: 9d a9 2d sta $2da9,x
74bd: 9d a9 31 sta $31a9,x
74c0: 9d a9 35 sta $35a9,x
74c3: 9d a9 39 sta $39a9,x
74c6: 9d a9 3d sta $3da9,x
74c9: 9d 29 22 sta $2229,x
74cc: 9d 29 26 sta $2629,x
74cf: 9d 29 2a sta $2a29,x
74d2: 9d 29 2e sta $2e29,x
74d5: 9d 29 32 sta $3229,x
74d8: 9d 29 36 sta $3629,x
74db: 9d 29 3a sta $3a29,x
74de: 9d 29 3e sta $3e29,x
74e1: 9d a9 22 sta $22a9,x
74e4: 9d a9 26 sta $26a9,x
74e7: 9d a9 2a sta $2aa9,x
74ea: 9d a9 2e sta $2ea9,x
74ed: 9d a9 32 sta $32a9,x
74f0: 9d a9 36 sta $36a9,x
74f3: 9d a9 3a sta $3aa9,x
74f6: 9d a9 3e sta $3ea9,x
74f9: 9d 29 23 sta $2329,x
74fc: 9d 29 27 sta $2729,x
74ff: 9d 29 2b sta $2b29,x
7502: 9d 29 2f sta $2f29,x
7505: 9d 29 33 sta $3329,x
7508: 9d 29 37 sta $3729,x
750b: 9d 29 3b sta $3b29,x
750e: 9d 29 3f sta $3f29,x
7511: 9d a9 23 sta $23a9,x
7514: 9d a9 27 sta $27a9,x
7517: 9d a9 2b sta $2ba9,x
751a: 9d a9 2f sta $2fa9,x
751d: 9d a9 33 sta $33a9,x
7520: 9d a9 37 sta $37a9,x
7523: 9d a9 3b sta $3ba9,x
7526: 9d a9 3f sta $3fa9,x
7529: 9d 51 20 sta $2051,x
752c: 9d 51 24 sta $2451,x
752f: 9d 51 28 sta $2851,x
7532: 9d 51 2c sta $2c51,x
7535: 9d 51 30 sta $3051,x
7538: 9d 51 34 sta $3451,x
753b: 9d 51 38 sta $3851,x
753e: 9d 51 3c sta $3c51,x
7541: 9d d1 20 sta $20d1,x
7544: 9d d1 24 sta $24d1,x
7547: 9d d1 28 sta $28d1,x
754a: 9d d1 2c sta $2cd1,x
754d: 9d d1 30 sta $30d1,x
7550: 9d d1 34 sta $34d1,x
7553: 9d d1 38 sta $38d1,x
7556: 9d d1 3c sta $3cd1,x
7559: 9d 51 21 sta $2151,x
755c: 9d 51 25 sta $2551,x
755f: 9d 51 29 sta $2951,x
7562: 9d 51 2d sta $2d51,x
7565: 9d 51 31 sta $3151,x
7568: 9d 51 35 sta $3551,x
756b: 9d 51 39 sta $3951,x
756e: 9d 51 3d sta $3d51,x
7571: 9d d1 21 sta $21d1,x
7574: 9d d1 25 sta $25d1,x
7577: 9d d1 29 sta $29d1,x
757a: 9d d1 2d sta $2dd1,x
757d: 9d d1 31 sta $31d1,x
7580: 9d d1 35 sta $35d1,x
7583: 9d d1 39 sta $39d1,x
7586: 9d d1 3d sta $3dd1,x
7589: 9d 51 22 sta $2251,x
758c: 9d 51 26 sta $2651,x
758f: 9d 51 2a sta $2a51,x
7592: 9d 51 2e sta $2e51,x
7595: 9d 51 32 sta $3251,x
7598: 9d 51 36 sta $3651,x
759b: 9d 51 3a sta $3a51,x
759e: 9d 51 3e sta $3e51,x
75a1: 9d d1 22 sta $22d1,x
75a4: 9d d1 26 sta $26d1,x
75a7: 9d d1 2a sta $2ad1,x
75aa: 9d d1 2e sta $2ed1,x
75ad: 9d d1 32 sta $32d1,x
75b0: 9d d1 36 sta $36d1,x
75b3: 9d d1 3a sta $3ad1,x
75b6: 9d d1 3e sta $3ed1,x
75b9: 9d 51 23 sta $2351,x
75bc: 9d 51 27 sta $2751,x
75bf: 9d 51 2b sta $2b51,x
75c2: 9d 51 2f sta $2f51,x
75c5: 9d 51 33 sta $3351,x
75c8: 9d 51 37 sta $3751,x
75cb: 9d 51 3b sta $3b51,x
75ce: 9d 51 3f sta $3f51,x
75d1: 9d d1 23 sta $23d1,x
75d4: 9d d1 27 sta $27d1,x
75d7: 9d d1 2b sta $2bd1,x
75da: 9d d1 2f sta $2fd1,x
75dd: 9d d1 33 sta $33d1,x
75e0: 9d d1 37 sta $37d1,x
75e3: 20 40 a0 jsr UpdateSound
75e6: a9 00 lda #$00
75e8: ca dex
75e9: 30 03 bmi :Done1
75eb: 4c 0c 74 jmp :Page1
75ee: 60 :Done1 rts
75ef: 9d 01 46 :Page2 sta $4601,x
75f2: 9d 01 4a sta $4a01,x
75f5: 9d 01 4e sta $4e01,x
75f8: 9d 01 52 sta $5201,x
75fb: 9d 01 56 sta $5601,x
75fe: 9d 01 5a sta $5a01,x
7601: 9d 01 5e sta $5e01,x
7604: 9d 81 42 sta $4281,x
7607: 9d 81 46 sta $4681,x
760a: 9d 81 4a sta $4a81,x
760d: 9d 81 4e sta $4e81,x
7610: 9d 81 52 sta $5281,x
7613: 9d 81 56 sta $5681,x
7616: 9d 81 5a sta $5a81,x
7619: 9d 81 5e sta $5e81,x
761c: 9d 01 43 sta $4301,x
761f: 9d 01 47 sta $4701,x
7622: 9d 01 4b sta $4b01,x
7625: 9d 01 4f sta $4f01,x
7628: 9d 01 53 sta $5301,x
762b: 9d 01 57 sta $5701,x
762e: 9d 01 5b sta $5b01,x
7631: 9d 01 5f sta $5f01,x
7634: 9d 81 43 sta $4381,x
7637: 9d 81 47 sta $4781,x
763a: 9d 81 4b sta $4b81,x
763d: 9d 81 4f sta $4f81,x
7640: 9d 81 53 sta $5381,x
7643: 9d 81 57 sta $5781,x
7646: 9d 81 5b sta $5b81,x
7649: 9d 81 5f sta $5f81,x
764c: 9d 29 40 sta $4029,x
764f: 9d 29 44 sta $4429,x
7652: 9d 29 48 sta $4829,x
7655: 9d 29 4c sta $4c29,x
7658: 9d 29 50 sta $5029,x
765b: 9d 29 54 sta $5429,x
765e: 9d 29 58 sta $5829,x
7661: 9d 29 5c sta $5c29,x
7664: 9d a9 40 sta $40a9,x
7667: 9d a9 44 sta $44a9,x
766a: 9d a9 48 sta $48a9,x
766d: 9d a9 4c sta $4ca9,x
7670: 9d a9 50 sta $50a9,x
7673: 9d a9 54 sta $54a9,x
7676: 9d a9 58 sta $58a9,x
7679: 9d a9 5c sta $5ca9,x
767c: 9d 29 41 sta $4129,x
767f: 9d 29 45 sta $4529,x
7682: 9d 29 49 sta $4929,x
7685: 9d 29 4d sta $4d29,x
7688: 9d 29 51 sta $5129,x
768b: 9d 29 55 sta $5529,x
768e: 9d 29 59 sta $5929,x
7691: 9d 29 5d sta $5d29,x
7694: 9d a9 41 sta $41a9,x
7697: 9d a9 45 sta $45a9,x
769a: 9d a9 49 sta $49a9,x
769d: 9d a9 4d sta $4da9,x
76a0: 9d a9 51 sta $51a9,x
76a3: 9d a9 55 sta $55a9,x
76a6: 9d a9 59 sta $59a9,x
76a9: 9d a9 5d sta $5da9,x
76ac: 9d 29 42 sta $4229,x
76af: 9d 29 46 sta $4629,x
76b2: 9d 29 4a sta $4a29,x
76b5: 9d 29 4e sta $4e29,x
76b8: 9d 29 52 sta $5229,x
76bb: 9d 29 56 sta $5629,x
76be: 9d 29 5a sta $5a29,x
76c1: 9d 29 5e sta $5e29,x
76c4: 9d a9 42 sta $42a9,x
76c7: 9d a9 46 sta $46a9,x
76ca: 9d a9 4a sta $4aa9,x
76cd: 9d a9 4e sta $4ea9,x
76d0: 9d a9 52 sta $52a9,x
76d3: 9d a9 56 sta $56a9,x
76d6: 9d a9 5a sta $5aa9,x
76d9: 9d a9 5e sta $5ea9,x
76dc: 9d 29 43 sta $4329,x
76df: 9d 29 47 sta $4729,x
76e2: 9d 29 4b sta $4b29,x
76e5: 9d 29 4f sta $4f29,x
76e8: 9d 29 53 sta $5329,x
76eb: 9d 29 57 sta $5729,x
76ee: 9d 29 5b sta $5b29,x
76f1: 9d 29 5f sta $5f29,x
76f4: 9d a9 43 sta $43a9,x
76f7: 9d a9 47 sta $47a9,x
76fa: 9d a9 4b sta $4ba9,x
76fd: 9d a9 4f sta $4fa9,x
7700: 9d a9 53 sta $53a9,x
7703: 9d a9 57 sta $57a9,x
7706: 9d a9 5b sta $5ba9,x
7709: 9d a9 5f sta $5fa9,x
770c: 9d 51 40 sta $4051,x
770f: 9d 51 44 sta $4451,x
7712: 9d 51 48 sta $4851,x
7715: 9d 51 4c sta $4c51,x
7718: 9d 51 50 sta $5051,x
771b: 9d 51 54 sta $5451,x
771e: 9d 51 58 sta $5851,x
7721: 9d 51 5c sta $5c51,x
7724: 9d d1 40 sta $40d1,x
7727: 9d d1 44 sta $44d1,x
772a: 9d d1 48 sta $48d1,x
772d: 9d d1 4c sta $4cd1,x
7730: 9d d1 50 sta $50d1,x
7733: 9d d1 54 sta $54d1,x
7736: 9d d1 58 sta $58d1,x
7739: 9d d1 5c sta $5cd1,x
773c: 9d 51 41 sta $4151,x
773f: 9d 51 45 sta $4551,x
7742: 9d 51 49 sta $4951,x
7745: 9d 51 4d sta $4d51,x
7748: 9d 51 51 sta $5151,x
774b: 9d 51 55 sta $5551,x
774e: 9d 51 59 sta $5951,x
7751: 9d 51 5d sta $5d51,x
7754: 9d d1 41 sta $41d1,x
7757: 9d d1 45 sta $45d1,x
775a: 9d d1 49 sta $49d1,x
775d: 9d d1 4d sta $4dd1,x
7760: 9d d1 51 sta $51d1,x
7763: 9d d1 55 sta $55d1,x
7766: 9d d1 59 sta $59d1,x
7769: 9d d1 5d sta $5dd1,x
776c: 9d 51 42 sta $4251,x
776f: 9d 51 46 sta $4651,x
7772: 9d 51 4a sta $4a51,x
7775: 9d 51 4e sta $4e51,x
7778: 9d 51 52 sta $5251,x
777b: 9d 51 56 sta $5651,x
777e: 9d 51 5a sta $5a51,x
7781: 9d 51 5e sta $5e51,x
7784: 9d d1 42 sta $42d1,x
7787: 9d d1 46 sta $46d1,x
778a: 9d d1 4a sta $4ad1,x
778d: 9d d1 4e sta $4ed1,x
7790: 9d d1 52 sta $52d1,x
7793: 9d d1 56 sta $56d1,x
7796: 9d d1 5a sta $5ad1,x
7799: 9d d1 5e sta $5ed1,x
779c: 9d 51 43 sta $4351,x
779f: 9d 51 47 sta $4751,x
77a2: 9d 51 4b sta $4b51,x
77a5: 9d 51 4f sta $4f51,x
77a8: 9d 51 53 sta $5351,x
77ab: 9d 51 57 sta $5751,x
77ae: 9d 51 5b sta $5b51,x
77b1: 9d 51 5f sta $5f51,x
77b4: 9d d1 43 sta $43d1,x
77b7: 9d d1 47 sta $47d1,x
77ba: 9d d1 4b sta $4bd1,x
77bd: 9d d1 4f sta $4fd1,x
77c0: 9d d1 53 sta $53d1,x
77c3: 9d d1 57 sta $57d1,x
77c6: 20 40 a0 jsr UpdateSound
77c9: a9 00 lda #$00
77cb: ca dex
77cc: 30 03 bmi :Done2
77ce: 4c ef 75 jmp :Page2
77d1: 60 :Done2 rts
77d2: ba 3a 84 22+ .align $0100 (46 bytes)
;
; Prints a character on the hi-res screen.
;
; Also called from BRIEFING.
;
; On entry:
; A-reg: char to print
;
• Clear variables
]tmp .var $f9 {addr/1}
]end_row .var $fa {addr/1}
]index .var $fb {addr/1}
]src_ptr .var $fc {addr/2}
]dst_ptr .var $fe {addr/2}
7800: 29 7f PrintChar and #$7f
7802: 85 f9 sta ]tmp
7804: ad fb 78 lda text_win_right
7807: cd f8 78 cmp text_col ;are we off the right edge?
780a: b0 0e bcs :ColOkay ;no, good
780c: ad fa 78 lda text_win_left ;move us to the left edge
780f: 8d f8 78 sta text_col
7812: ad f9 78 lda text_row ;move us down 8 rows (font height)
7815: 69 08 adc #$08
7817: 8d f9 78 sta text_row
781a: ad fd 78 :ColOkay lda text_win_bttm ;check the bottom
781d: cd f9 78 cmp text_row ;off the edge?
7820: b0 06 bcs :RowOkay ;no, keep going
7822: ad fc 78 lda text_win_top
7825: 8d f9 78 sta text_row
7828: a5 f9 :RowOkay lda ]tmp ;get the char
782a: c9 20 cmp #‘ ’ ;is it a control char?
782c: b0 43 bcs :NotCtrl
782e: c9 0a cmp #$0a ;linefeed?
7830: f0 0a beq :LfOrCr ;yes (note: sets carry)
7832: c9 0d cmp #$0d ;carriage return?
7834: d0 0f bne L7845 ;no, don't know what this is
7836: ad fa 78 lda text_win_left ;yes (note: carry is set)
7839: 8d f8 78 sta text_col ;move to left edge of window
783c: ad f9 78 :LfOrCr lda text_row ;move down 8 rows (font height)
783f: 69 07 adc #$07
7841: 8d f9 78 sta text_row
7844: 60 rts
7845: c9 07 L7845 cmp #$07 ;Ctrl+G?
7847: d0 03 bne :NotBell
7849: 4c dd fb jmp MON_BELL1 ;let the bells ring
784c: c9 00 :NotBell cmp #$00 ;Ctrl+@?
784e: d0 20 bne :Bail ;no, bail
7850: a9 00 lda #$00 ;yes, reset text parameters
7852: 8d fa 78 sta text_win_left
7855: 8d fc 78 sta text_win_top
7858: 8d f8 78 sta text_col
785b: 8d f9 78 sta text_row
785e: 8d ff 78 sta text_eor_mask
7861: a9 27 lda #39
7863: 8d fb 78 sta text_win_right
7866: a9 b8 lda #184
7868: 8d fd 78 sta text_win_bttm
786b: a9 79 lda #>font_glyphs
786d: 8d fe 78 sta text_font_addr
7870: 60 :Bail rts
7871: e9 20 :NotCtrl sbc #$20 ;adjust for control char range
7873: a2 00 ldx #$00
7875: 86 fd stx ]src_ptr+1 ;find the address of the character
7877: 0a asl A
7878: 0a asl A
7879: 26 fd rol ]src_ptr+1
787b: 0a asl A
787c: 85 fc sta ]src_ptr
787e: a5 fd lda ]src_ptr+1
7880: 2a rol A
7881: 6d fe 78 adc text_font_addr
7884: 85 fd sta ]src_ptr+1
7886: a9 00 lda #$00 ;start at byte 0
7888: 85 fb sta ]index
788a: ae f9 78 ldx text_row ;get top row
788d: 18 clc
788e: 8a txa
788f: 69 08 adc #$08
7891: 85 fa sta ]end_row ;bottom row
7893: bd 00 15 :DrawLoop lda HIRES_ADDR_LO,x ;get hi-res row address
7896: 85 fe sta ]dst_ptr
7898: bd 00 16 lda HIRES_ADDR_HI,x
789b: 05 ee ora HPAGE_EE
789d: 85 ff sta ]dst_ptr+1
789f: a4 fb ldy ]index
78a1: b1 fc lda (]src_ptr),y ;get byte from font data
78a3: 4d ff 78 eor text_eor_mask
78a6: ac f8 78 ldy text_col ;get output column
78a9: 91 fe sta (]dst_ptr),y ;write byte
78ab: e6 fb inc ]index ;update font data index
78ad: e8 inx ;move to next row
78ae: e4 fa cpx ]end_row ;done?
78b0: 90 e1 bcc :DrawLoop ;not yet
78b2: ee f8 78 inc text_col ;increment current column number
78b5: 60 rts
78b6: 00 00 ff ff+ .junk 42
;
; Prints a DCI string (last character has its high bit set).
;
; Also called from BRIEFING.
;
; On entry:
; $06/07: pointer to string
;
• Clear variables
]str_ptr .var $06 {addr/2}
]index .var $08 {addr/1}
78e0: a0 00 PrintDciString ldy #$00
78e2: 84 08 sty ]index
78e4: b1 06 lda (]str_ptr),y
78e6: 30 0b bmi :LastChar
78e8: 20 00 78 :Loop jsr PrintChar
78eb: e6 08 inc ]index
78ed: a4 08 ldy ]index
78ef: b1 06 lda (]str_ptr),y
78f1: 10 f5 bpl :Loop
78f3: 4c 00 78 :LastChar jmp PrintChar
78f6: 22 3a .junk 2
78f8: b2 text_col .dd1 $b2 ;current column, 0-39
78f9: 53 text_row .dd1 $53 ;current row, 0-191 (multiples of 8)
78fa: 49 text_win_left .dd1 $49 ;leftmost column, 0-39
78fb: 5a text_win_right .dd1 $5a ;rightmost column, 0-39
78fc: 45 text_win_top .dd1 $45 ;0-191
78fd: 53 text_win_bttm .dd1 $53 ;0-191
78fe: 00 text_font_addr .dd1 $00 ;high byte of address of font
78ff: 34 text_eor_mask .dd1 $34 ;$00/$7f/$80 ($7f inverts colors)
;
; Font; looks like a variant of the classic "byte" font from DOS Toolkit. This
; is a bit easier to read on a color monitor.
;
7900: 00 00 00 00+ font_glyphs .bulk $00,$00,$00,$00,$00,$00,$00,$00,$1c,$1c,$1c,$1c,$00,$1c,$1c,$00
+ $36,$36,$36,$00,$00,$00,$00,$00,$24,$7e,$24,$24,$7e,$24,$00,$00
+ $18,$7e,$1e,$7e,$78,$7e,$18,$00,$06,$66,$30,$18,$0c,$66,$60,$00
+ $3e,$36,$36,$1c,$76,$36,$7e,$00,$18,$18,$18,$00,$00,$00,$00,$00
+ $30,$18,$0c,$0c,$0c,$18,$30,$00,$0c,$18,$30,$30,$30,$18,$0c,$00
+ $00,$08,$2a,$1c,$2a,$08,$00,$00,$00,$18,$18,$7e,$18,$18,$00,$00
+ $00,$00,$00,$00,$1c,$1c,$18,$1c,$00,$00,$00,$3e,$3e,$00,$00,$00
+ $00,$00,$00,$00,$1c,$1c,$1c,$00,$00,$60,$30,$18,$0c,$06,$00,$00
+ $7e,$66,$66,$66,$66,$66,$7e,$00,$1c,$1c,$18,$18,$3c,$3c,$3c,$00
+ $7e,$60,$60,$7e,$06,$06,$7e,$00,$3e,$30,$30,$7e,$70,$70,$7e,$00
+ $06,$06,$66,$66,$7e,$60,$60,$00,$7e,$06,$06,$7e,$60,$60,$7e,$00
+ $7e,$66,$06,$7e,$66,$66,$7e,$00,$7e,$66,$30,$18,$18,$18,$18,$00
+ $7e,$66,$66,$7e,$66,$66,$7e,$00,$7e,$66,$66,$7e,$60,$60,$60,$00
+ $00,$1c,$1c,$00,$1c,$1c,$00,$00,$00,$1c,$1c,$00,$1c,$1c,$18,$1c
+ $30,$18,$0c,$06,$0c,$18,$30,$00,$00,$00,$7e,$00,$7e,$00,$00,$00
+ $06,$0c,$18,$30,$18,$0c,$06,$00,$7e,$66,$30,$18,$00,$18,$18,$00
+ $7e,$66,$76,$76,$76,$06,$7e,$00,$7e,$66,$66,$7e,$66,$66,$66,$00
+ $3e,$66,$66,$3e,$66,$66,$3e,$00,$7e,$66,$66,$06,$06,$66,$7e,$00
+ $3e,$66,$66,$66,$66,$66,$3e,$00,$7e,$06,$06,$7e,$0e,$0e,$7e,$00
+ $7e,$0e,$0e,$7e,$0c,$0c,$0c,$00,$7e,$66,$06,$06,$76,$66,$7e,$00
+ $66,$66,$66,$7e,$66,$66,$66,$00,$18,$18,$18,$1c,$1c,$1c,$1c,$00
+ $60,$60,$60,$60,$60,$66,$7e,$00,$66,$36,$1e,$7e,$66,$66,$66,$00
+ $0c,$0c,$0c,$0e,$0e,$0e,$7e,$00,$62,$76,$6a,$62,$62,$62,$62,$00
+ $66,$6e,$7e,$76,$66,$66,$66,$00,$7e,$66,$66,$66,$66,$66,$7e,$00
+ $7e,$66,$66,$7e,$06,$06,$06,$00,$7e,$66,$66,$66,$7e,$7e,$7e,$18
+ $3e,$66,$66,$3e,$66,$66,$66,$00,$7e,$66,$06,$7e,$60,$66,$7e,$00
+ $7e,$18,$18,$18,$18,$18,$18,$00,$66,$66,$66,$66,$66,$66,$7e,$00
+ $66,$66,$66,$66,$66,$3c,$18,$00,$46,$46,$46,$46,$56,$6e,$46,$00
+ $66,$66,$3c,$18,$3c,$66,$66,$00,$66,$66,$3c,$18,$18,$18,$18,$00
+ $7e,$66,$30,$18,$0c,$66,$7e,$00,$3e,$06,$06,$06,$06,$06,$3e,$00
+ $00,$06,$0c,$18,$30,$60,$00,$00,$3e,$30,$30,$30,$30,$30,$3e,$00
+ $00,$18,$3c,$66,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7f,$7f
+ $1c,$08,$49,$7f,$49,$08,$1c,$00,$00,$00,$7e,$60,$7e,$66,$7e,$00
+ $06,$06,$7e,$66,$66,$66,$7e,$00,$00,$00,$7e,$66,$06,$06,$7e,$00
+ $60,$60,$7e,$66,$66,$66,$7e,$00,$00,$00,$7e,$66,$7e,$06,$7e,$00
+ $3c,$0c,$0c,$3e,$0c,$0c,$0c,$00,$00,$00,$7e,$66,$66,$7e,$60,$7e
+ $06,$06,$7e,$66,$66,$66,$66,$00,$18,$00,$18,$18,$18,$18,$18,$00
+ $60,$00,$60,$60,$60,$66,$7e,$00,$06,$06,$66,$36,$7e,$66,$66,$00
+ $1c,$18,$18,$18,$18,$18,$3c,$00,$00,$00,$7e,$5a,$5a,$42,$42,$00
+ $00,$00,$7e,$66,$66,$66,$66,$00,$00,$00,$7e,$66,$66,$66,$7e,$00
+ $00,$00,$7e,$66,$66,$7e,$06,$06,$00,$00,$7e,$66,$66,$7e,$60,$60
+ $00,$00,$7e,$66,$06,$06,$06,$00,$00,$00,$7e,$06,$7e,$60,$7e,$00
+ $18,$18,$7e,$18,$18,$18,$18,$00,$00,$00,$66,$66,$66,$66,$7e,$00
+ $00,$00,$66,$66,$66,$3c,$18,$00,$00,$00,$42,$42,$5a,$5a,$7e,$00
+ $00,$00,$66,$3c,$18,$3c,$66,$00,$00,$00,$66,$66,$66,$7e,$60,$7e
+ $00,$00,$7e,$30,$18,$0c,$7e,$00,$08,$2a,$1c,$7f,$1c,$2a,$08,$00
+ $1c,$1e,$73,$33,$73,$1e,$1c,$00,$20,$10,$36,$7f,$7f,$3e,$3e,$14
+ $08,$14,$22,$7f,$22,$2a,$36,$7f,$55,$55,$55,$55,$01,$01,$01,$01
;
; Read from the controller (keyboard / joystick / Joyport).
;
; On exit:
; $00: movement direction (0-9)
; $01: button 0 value
; $02: button 1 value
; $03: last key pressed (high bit set)
;
]move_dir .var $00 {addr/1} ;0-9
]button0 .var $01 {addr/1}
]button1 .var $02 {addr/1}
]last_key .var $03 {addr/1}
7c00: a9 00 ReadController lda #$00
7c02: 85 01 sta ]button0
7c04: 85 02 sta ]button1
7c06: ad 00 c0 lda KBD ;check keyboard
7c09: 2c 10 c0 bit KBDSTRB ;clear it
7c0c: 85 03 sta ]last_key ;save it
7c0e: ad f4 7c lda control_mode ;joystick?
7c11: d0 0f bne :Joystick ;yes
; Handle keyboard movement.
7c13: a5 03 lda ]last_key
7c15: a2 09 ldx #$09
7c17: dd f6 7c :Loop cmp movement_keys,x
7c1a: f0 03 beq :FoundKey
7c1c: ca dex
7c1d: d0 f8 bne :Loop
7c1f: 86 00 :FoundKey stx ]move_dir
7c21: 60 rts
7c22: 30 31 :Joystick bmi ReadJoyport
7c24: ad 61 c0 lda BUTN0
7c27: 85 01 sta ]button0
7c29: ad 62 c0 lda BUTN1
7c2c: 85 02 sta ]button1
7c2e: 20 87 7c jsr ReadJoystick
7c31: c0 0f cpy #15 ;joystick north?
7c33: b0 07 bcs :NotNorth ;no, branch
7c35: a9 01 lda #$01
7c37: 85 00 sta ]move_dir
7c39: 4c 48 7c jmp :CheckX
7c3c: a9 04 :NotNorth lda #$04 ;possibly stationary
7c3e: 85 00 sta ]move_dir
7c40: c0 72 cpy #114 ;joystick south?
7c42: 90 04 bcc :CheckX ;nope
7c44: a9 07 lda #$07 ;yes, moving backward
7c46: 85 00 sta ]move_dir
7c48: e0 0f :CheckX cpx #15 ;joystick west?
7c4a: 90 08 bcc :Done ;yes, done
7c4c: e6 00 inc ]move_dir ;no, assume centered
7c4e: e0 72 cpx #114 ;joystick east?
7c50: 90 02 bcc :Done ;no, done
7c52: e6 00 inc ]move_dir ;yes, inc one more
7c54: 60 :Done rts
7c55: 2c 58 c0 ReadJoyport bit SETAN0
7c58: ad 61 c0 lda BUTN0
7c5b: 49 80 eor #$80
7c5d: 85 01 sta ]button0
7c5f: 2c 5b c0 bit CLRAN1
7c62: ad 62 c0 lda BUTN1
7c65: 30 05 bmi L7C6C
7c67: a2 01 ldx #$01
7c69: 4c 75 7c jmp L7C75
7c6c: a2 04 L7C6C ldx #$04
7c6e: ad 63 c0 lda BUTN2
7c71: 30 02 bmi L7C75
7c73: a2 07 ldx #$07
7c75: 2c 5a c0 L7C75 bit SETAN1
7c78: ad 62 c0 lda BUTN1
7c7b: 10 07 bpl L7C84
7c7d: e8 inx
7c7e: ad 63 c0 lda BUTN2
7c81: 30 01 bmi L7C84
7c83: e8 inx
7c84: 86 00 L7C84 stx ]move_dir
7c86: 60 rts
;
; Reads both axes of the joystick.
;
; On exit:
; X-reg: paddle 0
; Y-reg: paddle 1
;
7c87: 2c 70 c0 ReadJoystick bit PTRIG ;reset counters
7c8a: a2 00 ldx #$00
7c8c: a0 00 ldy #$00
7c8e: 2c 64 c0 :BothLoop bit PADDL0
7c91: 10 12 bpl :Paddle0Done
7c93: 2c 65 c0 bit PADDL1
7c96: 10 23 bpl :Paddle1Done
7c98: 4c 9b 7c jmp :Next
7c9b: e8 :Next inx
7c9c: c8 iny
7c9d: 10 ef bpl :BothLoop
7c9f: ca dex
7ca0: 88 dey
7ca1: 60 :Done rts
7ca2: ea :Paddle1Loop nop
7ca3: ea nop
7ca4: ea nop
7ca5: 2c 65 c0 :Paddle0Done bit PADDL1
7ca8: 10 f7 bpl :Done
7caa: 4c ad 7c jmp :Next
7cad: ea :Next nop
7cae: c8 iny
7caf: 10 f1 bpl :Paddle1Loop
7cb1: 88 dey
7cb2: 60 rts
7cb3: 2c 64 c0 :Paddle0Loop bit PADDL0
7cb6: 10 e9 bpl :Done
7cb8: ea nop
7cb9: ea nop
7cba: ea nop
7cbb: 4c be 7c :Paddle1Done jmp :Next
7cbe: e8 :Next inx
7cbf: ea nop
7cc0: 10 f1 bpl :Paddle0Loop
7cc2: ca dex
7cc3: 60 rts
7cc4: ff ff 00 00+ .junk 48
7cf4: 00 control_mode .dd1 $00 ;0=keyboard, 1=joystick, $80=joyport
7cf5: 00 .dd1 $00
7cf6: 00 movement_keys .dd1 $00
7cf7: d5 .dd1 “U” ;1=fwd/left
7cf8: c9 .dd1 “I” ;2=fwd
7cf9: cf .dd1 “O” ;3=fwd/right
7cfa: ca .dd1 “J” ;4=rotate left
7cfb: cb .dd1 “K” ;5=stationary
7cfc: cc .dd1 “L” ;6=rotate right
7cfd: cd .dd1 “M” ;7=back/left
7cfe: ac .dd1 “,” ;8=back
7cff: ae .dd1 “.” ;9=back/right
;
; Erase the 28x28 radar in the top right.
;
; On entry:
; Y-reg: $00 for page 1, nonzero for page 2
;
• Clear variables
7d00: a8 EraseRadar tay
7d01: a9 00 lda #$00
7d03: a2 03 ldx #$03
7d05: c0 00 cpy #$00
7d07: f0 03 beq :Page1
7d09: 4c 67 7d jmp :Page2
7d0c: 9d 23 28 :Page1 sta $2823,x
7d0f: 9d 23 2c sta $2c23,x
7d12: 9d 23 30 sta $3023,x
7d15: 9d 23 34 sta $3423,x
7d18: 9d 23 38 sta $3823,x
7d1b: 9d 23 3c sta $3c23,x
7d1e: 9d a3 20 sta $20a3,x
7d21: 9d a3 24 sta $24a3,x
7d24: 9d a3 28 sta $28a3,x
7d27: 9d a3 2c sta $2ca3,x
7d2a: 9d a3 30 sta $30a3,x
7d2d: 9d a3 34 sta $34a3,x
7d30: 9d a3 38 sta $38a3,x
7d33: 9d a3 3c sta $3ca3,x
7d36: 9d 23 21 sta $2123,x
7d39: 9d 23 25 sta $2523,x
7d3c: 9d 23 29 sta $2923,x
7d3f: 9d 23 2d sta $2d23,x
7d42: 9d 23 31 sta $3123,x
7d45: 9d 23 35 sta $3523,x
7d48: 9d 23 39 sta $3923,x
7d4b: 9d 23 3d sta $3d23,x
7d4e: 9d a3 21 sta $21a3,x
7d51: 9d a3 25 sta $25a3,x
7d54: 9d a3 29 sta $29a3,x
7d57: 9d a3 2d sta $2da3,x
7d5a: 9d a3 31 sta $31a3,x
7d5d: 9d a3 35 sta $35a3,x
7d60: ca dex
7d61: 30 03 bmi :Done1
7d63: 4c 0c 7d jmp :Page1
7d66: 60 :Done1 rts
7d67: 9d 23 48 :Page2 sta $4823,x
7d6a: 9d 23 4c sta $4c23,x
7d6d: 9d 23 50 sta $5023,x
7d70: 9d 23 54 sta $5423,x
7d73: 9d 23 58 sta $5823,x
7d76: 9d 23 5c sta $5c23,x
7d79: 9d a3 40 sta $40a3,x
7d7c: 9d a3 44 sta $44a3,x
7d7f: 9d a3 48 sta $48a3,x
7d82: 9d a3 4c sta $4ca3,x
7d85: 9d a3 50 sta $50a3,x
7d88: 9d a3 54 sta $54a3,x
7d8b: 9d a3 58 sta $58a3,x
7d8e: 9d a3 5c sta $5ca3,x
7d91: 9d 23 41 sta $4123,x
7d94: 9d 23 45 sta $4523,x
7d97: 9d 23 49 sta $4923,x
7d9a: 9d 23 4d sta $4d23,x
7d9d: 9d 23 51 sta $5123,x
7da0: 9d 23 55 sta $5523,x
7da3: 9d 23 59 sta $5923,x
7da6: 9d 23 5d sta $5d23,x
7da9: 9d a3 41 sta $41a3,x
7dac: 9d a3 45 sta $45a3,x
7daf: 9d a3 49 sta $49a3,x
7db2: 9d a3 4d sta $4da3,x
7db5: 9d a3 51 sta $51a3,x
7db8: 9d a3 55 sta $55a3,x
7dbb: ca dex
7dbc: 30 03 bmi :Done2
7dbe: 4c 67 7d jmp :Page2
7dc1: 60 :Done2 rts
7dc2: 7d f3 02 ad+ .align $0100 (62 bytes)
;
; Draws reticle, score, radar, and fuel/shield gauges.
;
; On entry:
; A-reg: bool 00/80: should we draw reticle?
;
7e00: a8 DrawHudItems tay
7e01: ad f0 65 lda render_page ;get render page (0/1)
7e04: 29 01 and #$01
7e06: aa tax
7e07: bd 1d 7e lda :hires_addr_hi,x
7e0a: 85 ee sta HPAGE_EE ;set page addr ($20/$40)
7e0c: c0 00 cpy #$00 ;was 0 passed in?
7e0e: f0 03 beq :SkipReticle ;yes, skip reticle
7e10: 20 5d 81 jsr DrawReticle
7e13: 20 1f 7e :SkipReticle jsr DrawScore
7e16: 20 6c 7e jsr DrawRadar
7e19: 20 05 7f jsr DrawSFGauges
7e1c: 60 rts
7e1d: 20 40 :hires_addr_hi .bulk $20,$40
;
; Prints the 4-digit score. (The actual score is 6 BCD digits, but we only
; display four while playing.)
;
• Clear variables
]tmp .var $ef {addr/1}
7e1f: a9 00 DrawScore lda #$00
7e21: 8d ff 78 sta text_eor_mask
7e24: a9 03 lda #$03
7e26: 8d f8 78 sta text_col
7e29: a9 10 lda #$10
7e2b: 8d f9 78 sta text_row
7e2e: ad f9 81 lda score+1 ;get high two digits
7e31: 20 37 7e jsr :PrintScoreByte ;print it
7e34: ad f8 81 lda score ;get low two digits
7e37: 85 ef :PrintScoreByte sta ]tmp ;save value
7e39: 4a lsr A ;get high nibble
7e3a: 4a lsr A
7e3b: 4a lsr A
7e3c: 4a lsr A
7e3d: 18 clc
7e3e: 69 30 adc #‘0’ ;turn into ASCII digit
7e40: 20 00 78 jsr PrintChar ;print it
7e43: 20 40 a0 jsr UpdateSound
7e46: a5 ef lda ]tmp
7e48: 29 0f and #$0f ;get low nibble
7e4a: 18 clc
7e4b: 69 30 adc #‘0’
7e4d: 20 00 78 jsr PrintChar ;print it
7e50: 4c 40 a0 jmp UpdateSound
;
; Updates the player's score.
;
; On entry:
; A-reg: score low byte (BCD)
; X-reg: score high byte (BCD)
;
7e53: f8 UpdateScore sed
7e54: 18 clc
7e55: 6d f8 81 adc score
7e58: 8d f8 81 sta score
7e5b: 8a txa
7e5c: 6d f9 81 adc score+1
7e5f: 8d f9 81 sta score+1
7e62: a9 00 lda #$00
7e64: 6d fa 81 adc score+2
7e67: 8d fa 81 sta score+2
7e6a: d8 cld
7e6b: 60 rts
;
; Redraws the radar. The radar area is 28x28.
;
; The radar is drawn with the high byte of the object X,Z position. The arena
; is 8192x8192, giving a range of 32x32. So there's a dead area the radar
; doesn't cover, but it's not very large.
;
]hptr .var $fa {addr/2}
7e6c: ad f0 65 DrawRadar lda render_page
7e6f: 20 00 7d jsr EraseRadar ;clear radar area
7e72: 20 40 a0 jsr UpdateSound
; Loop over all objects.
7e75: a0 17 ldy #c_num_objects-1 ;loop over all objects
7e77: b9 d0 0c L7E77 lda RADAR_XCOORDS,y
7e7a: 19 e8 0c ora RADAR_YCOORDS,y ;is the coordinate (0,0)?
7e7d: f0 32 beq :Skip ;yes, nothing to do for this one
7e7f: 38 sec
7e80: a9 0e lda #14
7e82: f9 e8 0c sbc RADAR_YCOORDS,y ;center Y-coord (and flip)
7e85: c9 1c cmp #28 ;off the edge?
7e87: b0 28 bcs :Skip ;yes, skip it
7e89: aa tax
7e8a: e8 inx
7e8b: e8 inx
7e8c: bd 00 15 lda HIRES_ADDR_LO,x
7e8f: 85 fa sta ]hptr
7e91: bd 00 16 lda HIRES_ADDR_HI,x
7e94: 05 ee ora HPAGE_EE
7e96: 85 fb sta ]hptr+1
7e98: b9 d0 0c lda RADAR_XCOORDS,y
7e9b: 69 0e adc #14 ;center
7e9d: c9 1c cmp #28
7e9f: b0 10 bcs :Skip
7ea1: 84 ef sty ]tmp
7ea3: aa tax
7ea4: bd cd 7e lda :radar_col_byte,x
7ea7: a8 tay
7ea8: bd e9 7e lda :radar_col_bit,x
7eab: 11 fa ora (]hptr),y
7ead: 91 fa sta (]hptr),y
7eaf: a4 ef ldy ]tmp
7eb1: 88 :Skip dey
7eb2: 10 c3 bpl L7E77
; Draw the center dot that represents the player.
7eb4: a2 10 ldx #$10
7eb6: bd 00 15 lda HIRES_ADDR_LO,x
7eb9: 85 fa sta ]hptr
7ebb: bd 00 16 lda HIRES_ADDR_HI,x
7ebe: 05 ee ora HPAGE_EE
7ec0: 85 fb sta ]hptr+1
7ec2: a0 25 ldy #$25
7ec4: a9 01 lda #$01
7ec6: 11 fa ora (]hptr),y
7ec8: 91 fa sta (]hptr),y
7eca: 4c 40 a0 jmp UpdateSound
7ecd: 23 23 23 23+ :radar_col_byte .bulk $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
7ee9: 01 02 04 08+ :radar_col_bit .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
;
; Draws the shield (blue) and fuel (purple) gauges.
;
; Gauges have alternating lines of white and color. There are 37 colored
; regions, 36 white lines.
;
; This draws the gauges in their entirety on every frame, and takes about 7800
; cycles (the exact amount depending on how full the gauges are).
;
• Clear variables
]level .var $ec {addr/1}
]level_div4 .var $ed {addr/1}
]num_empty .var $ef {addr/1}
]hptr0 .var $fa {addr/2}
]hptr1 .var $fc {addr/2}
]hptr2 .var $fe {addr/2}
7f05: ad fe 81 DrawSFGauges lda fuel_level+1 ;$00-92
7f08: 85 ec sta ]level
7f0a: 4a lsr A ;divide by 4
7f0b: 4a lsr A
7f0c: 85 ed sta ]level_div4
7f0e: 38 sec
7f0f: a9 25 lda #37
7f11: e5 ed sbc ]level_div4
7f13: 85 ef sta ]num_empty ;37 - (level / 4)
; Clear the fully-empty sections.
7f15: a2 27 ldx #39 ;start at the top
7f17: a0 26 ldy #38 ;column 38
7f19: bd 00 15 :DrawBlack lda HIRES_ADDR_LO,x ;set up three rows to erase
7f1c: 85 fa sta ]hptr0
7f1e: bd 00 16 lda HIRES_ADDR_HI,x
7f21: 05 ee ora HPAGE_EE
7f23: 85 fb sta ]hptr0+1
7f25: e8 inx
7f26: bd 00 15 lda HIRES_ADDR_LO,x
7f29: 85 fc sta ]hptr1
7f2b: bd 00 16 lda HIRES_ADDR_HI,x
7f2e: 05 ee ora HPAGE_EE
7f30: 85 fd sta ]hptr1+1
7f32: e8 inx
7f33: bd 00 15 lda HIRES_ADDR_LO,x
7f36: 85 fe sta ]hptr2
7f38: bd 00 16 lda HIRES_ADDR_HI,x
7f3b: 05 ee ora HPAGE_EE
7f3d: 85 ff sta ]hptr2+1
7f3f: e8 inx
7f40: a9 00 lda #$00 ;zero all three
7f42: 91 fa sta (]hptr0),y
7f44: 91 fc sta (]hptr1),y
7f46: 91 fe sta (]hptr2),y
7f48: e8 inx ;skip the white line
7f49: c6 ef dec ]num_empty
7f4b: d0 cc bne :DrawBlack
7f4d: 20 20 a0 jsr SoundTwice
; Now draw the purple, which may fill only one or two lines of a section.
7f50: 38 sec
7f51: a9 ba lda #186 ;bottom row
7f53: e5 ec sbc ]level ;compute topmost row
7f55: aa tax
7f56: a9 00 lda #<CLIP_COORD_XLO ;instead of skipping unwanted stores, we change the
7f58: 85 fa sta ]hptr0 ; pointers to scratch space
7f5a: 85 fc sta ]hptr1
7f5c: a9 0e lda #>CLIP_COORD_XLO
7f5e: 85 fb sta ]hptr0+1
7f60: 85 fd sta ]hptr1+1
7f62: a5 ec lda ]level ;check row mod 4
7f64: 29 03 and #$03 ;is this a white separator line? (mod == 0)
7f66: f0 37 beq :NextPurple ;yes, skip it
7f68: c9 01 cmp #$01 ;is only 1 bar filled?
7f6a: f0 1e beq :OnePurple ;yes, branch
7f6c: c9 02 cmp #$02 ;two bars?
7f6e: f0 0d beq :TwoPurple ;yes, branch
7f70: bd 00 15 :ThreePurple lda HIRES_ADDR_LO,x ;draw all three bars in this region
7f73: 85 fa sta ]hptr0
7f75: bd 00 16 lda HIRES_ADDR_HI,x
7f78: 05 ee ora HPAGE_EE
7f7a: 85 fb sta ]hptr0+1
7f7c: e8 inx
7f7d: bd 00 15 :TwoPurple lda HIRES_ADDR_LO,x
7f80: 85 fc sta ]hptr1
7f82: bd 00 16 lda HIRES_ADDR_HI,x
7f85: 05 ee ora HPAGE_EE
7f87: 85 fd sta ]hptr1+1
7f89: e8 inx
7f8a: bd 00 15 :OnePurple lda HIRES_ADDR_LO,x
7f8d: 85 fe sta ]hptr2
7f8f: bd 00 16 lda HIRES_ADDR_HI,x
7f92: 05 ee ora HPAGE_EE
7f94: 85 ff sta ]hptr2+1
7f96: e8 inx
7f97: a9 55 lda #$55 ;purple
7f99: 91 fa sta (]hptr0),y ;draw
7f9b: 91 fc sta (]hptr1),y
7f9d: 91 fe sta (]hptr2),y
7f9f: e8 :NextPurple inx ;skip the separator
7fa0: c6 ed dec ]level_div4 ;moving 4 rows at a time
7fa2: 10 cc bpl :ThreePurple ;keep drawing full sections
7fa4: 20 20 a0 jsr SoundTwice
; Now repeat the process for the shield.
7fa7: ad fc 81 lda shield_level+1 ;... see above ...
7faa: 85 ec sta ]level
7fac: 4a lsr A
7fad: 4a lsr A
7fae: 85 ed sta ]level_div4
7fb0: 38 sec
7fb1: a9 25 lda #$25
7fb3: e5 ed sbc ]level_div4
7fb5: 85 ef sta ]num_empty
7fb7: a2 27 ldx #$27
7fb9: bd 00 15 L7FB9 lda HIRES_ADDR_LO,x
7fbc: 85 fa sta ]hptr0
7fbe: bd 00 16 lda HIRES_ADDR_HI,x
7fc1: 05 ee ora HPAGE_EE
7fc3: 85 fb sta ]hptr0+1
7fc5: e8 inx
7fc6: bd 00 15 lda HIRES_ADDR_LO,x
7fc9: 85 fc sta ]hptr1
7fcb: bd 00 16 lda HIRES_ADDR_HI,x
7fce: 05 ee ora HPAGE_EE
7fd0: 85 fd sta ]hptr1+1
7fd2: e8 inx
7fd3: bd 00 15 lda HIRES_ADDR_LO,x
7fd6: 85 fe sta ]hptr2
7fd8: bd 00 16 lda HIRES_ADDR_HI,x
7fdb: 05 ee ora HPAGE_EE
7fdd: 85 ff sta ]hptr2+1
7fdf: e8 inx
7fe0: a0 22 ldy #$22 ;note this straddles two columns
7fe2: a9 84 lda #$84 ;(aesthetics > optimization)
7fe4: 91 fa sta (]hptr0),y
7fe6: 91 fc sta (]hptr1),y
7fe8: 91 fe sta (]hptr2),y
7fea: a0 23 ldy #$23
7fec: a9 00 lda #$00
7fee: 91 fa sta (]hptr0),y
7ff0: 91 fc sta (]hptr1),y
7ff2: 91 fe sta (]hptr2),y
7ff4: e8 inx
7ff5: c6 ef dec ]num_empty
7ff7: d0 c0 bne L7FB9
7ff9: 20 30 a0 jsr SoundThrice
; Draw blue (see previous).
7ffc: 38 sec
7ffd: a9 ba lda #$ba
7fff: e5 ec sbc ]level
8001: aa tax
8002: a9 00 lda #$00
8004: 85 fa sta ]hptr0
8006: 85 fc sta ]hptr1
8008: a9 0e lda #$0e
800a: 85 fb sta ]hptr0+1
800c: 85 fd sta ]hptr1+1
800e: a5 ec lda ]level
8010: 29 03 and #$03
8012: f0 43 beq L8057
8014: c9 01 cmp #$01
8016: f0 1e beq L8036
8018: c9 02 cmp #$02
801a: f0 0d beq L8029
801c: bd 00 15 :ThreeBlue lda HIRES_ADDR_LO,x
801f: 85 fa sta ]hptr0
8021: bd 00 16 lda HIRES_ADDR_HI,x
8024: 05 ee ora HPAGE_EE
8026: 85 fb sta ]hptr0+1
8028: e8 inx
8029: bd 00 15 L8029 lda HIRES_ADDR_LO,x
802c: 85 fc sta ]hptr1
802e: bd 00 16 lda HIRES_ADDR_HI,x
8031: 05 ee ora HPAGE_EE
8033: 85 fd sta ]hptr1+1
8035: e8 inx
8036: bd 00 15 L8036 lda HIRES_ADDR_LO,x
8039: 85 fe sta ]hptr2
803b: bd 00 16 lda HIRES_ADDR_HI,x
803e: 05 ee ora HPAGE_EE
8040: 85 ff sta ]hptr2+1
8042: e8 inx
8043: a0 22 ldy #$22 ;again, straddles two columns
8045: a9 c4 lda #$c4
8047: 91 fa sta (]hptr0),y
8049: 91 fc sta (]hptr1),y
804b: 91 fe sta (]hptr2),y
804d: a0 23 ldy #$23
804f: a9 aa lda #$aa
8051: 91 fa sta (]hptr0),y
8053: 91 fc sta (]hptr1),y
8055: 91 fe sta (]hptr2),y
8057: e8 L8057 inx
8058: c6 ed dec ]level_div4
805a: 10 c0 bpl :ThreeBlue
805c: 4c 30 a0 jmp SoundThrice
;
; Increases the shield level. Shields cannot be increased above $92ff.
;
; On entry:
; A-reg: increase, low byte
; X-reg: increase, high byte
;
; On exit:
; Y-reg preserved
;
805f: 18 IncreaseShields clc
8060: 6d fb 81 adc shield_level
8063: 8d fb 81 sta shield_level
8066: 8a txa
8067: 6d fc 81 adc shield_level+1
806a: b0 04 bcs L8070
806c: c9 93 cmp #$93
806e: 90 07 bcc L8077
8070: a9 ff L8070 lda #$ff
8072: 8d fb 81 sta shield_level
8075: a9 92 lda #$92 ;max shields
8077: 8d fc 81 L8077 sta shield_level+1
807a: 60 rts
;
; Reduces the shield level. Shields cannot be reduced below zero.
;
; On entry:
; A-reg: reduction, low byte
; X-reg: reduction, high byte
;
; On exit:
; Y-reg preserved
;
]tmp_hi .var $ed {addr/1}
]tmp_lo .var $ef {addr/1}
807b: 38 ReduceShields sec
807c: ed fb 81 sbc shield_level
807f: 85 ef sta ]tmp_lo
8081: 8a txa
8082: ed fc 81 sbc shield_level+1
8085: b0 12 bcs :NoShield
8087: 85 ed sta ]tmp_hi
8089: 38 sec
808a: a9 00 lda #$00
808c: e5 ef sbc ]tmp_lo
808e: 8d fb 81 sta shield_level
8091: a9 00 lda #$00
8093: e5 ed sbc ]tmp_hi
8095: 8d fc 81 sta shield_level+1
8098: 60 rts
8099: a9 00 :NoShield lda #$00
809b: 8d fb 81 sta shield_level
809e: 8d fc 81 sta shield_level+1
80a1: 60 rts
;
; Increases the fuel level. Fuel cannot be increased above $92ff.
;
; On entry:
; A-reg: increase, low byte
; X-reg: increase, high byte
;
; On exit:
; Y-reg preserved
;
80a2: 18 IncreaseFuel clc
80a3: 6d fd 81 adc fuel_level
80a6: 8d fd 81 sta fuel_level
80a9: 8a txa
80aa: 6d fe 81 adc fuel_level+1
80ad: b0 04 bcs L80B3
80af: c9 93 cmp #$93
80b1: 90 07 bcc L80BA
80b3: a9 ff L80B3 lda #$ff
80b5: 8d fd 81 sta fuel_level
80b8: a9 92 lda #$92
80ba: 8d fe 81 L80BA sta fuel_level+1
80bd: 60 rts
;
; Reduces the fuel level by a specified amount. Fuel cannot be reduced below
; zero.
;
; On entry:
; A-reg: increase, low byte
; X-reg: increase, high byte
;
; On exit:
; Y-reg preserved
;
• Clear variables
]new_fuel_hi .var $ed {addr/1}
]new_fuel_lo .var $ef {addr/1}
80be: 38 ReduceFuel sec ;compute negval = (A/X - fuel_level)
80bf: ed fd 81 sbc fuel_level ;(would be nicer to compute fuel_level - A/X, but
80c2: 85 ef sta ]new_fuel_lo ; the 6502 doesn't really do that)
80c4: 8a txa ;(also, max fuel is $92ff, a negative value, so the
80c5: ed fe 81 sbc fuel_level+1 ; comparison here won't be trivial)
80c8: b0 12 bcs :NoFuel
80ca: 85 ed sta ]new_fuel_hi
80cc: 38 sec
80cd: a9 00 lda #$00 ;compute fuel_level = 0 - negval
80cf: e5 ef sbc ]new_fuel_lo
80d1: 8d fd 81 sta fuel_level
80d4: a9 00 lda #$00
80d6: e5 ed sbc ]new_fuel_hi
80d8: 8d fe 81 sta fuel_level+1
80db: 60 rts
80dc: a9 00 :NoFuel lda #$00 ;uh oh
80de: 8d fd 81 sta fuel_level
80e1: 8d fe 81 sta fuel_level+1
80e4: 60 rts
;
; Prints the system name in the box at the top right of the screen.
;
; On entry:
; A-reg: system number (1-7), 0 for blank, 8 for warplink
;
]string_ptr .var $06 {addr/2}
80e5: 0a PrintSystemName asl A
80e6: 0a asl A
80e7: 0a asl A
80e8: 18 clc
80e9: 69 15 adc #<system_names
80eb: 85 06 sta ]string_ptr
80ed: a9 00 lda #$00
80ef: 69 81 adc #>system_names
80f1: 85 07 sta ]string_ptr+1
80f3: a9 80 lda #$80
80f5: 8d ff 78 sta text_eor_mask ;set high bits so color fringe matches blue frame
80f8: a9 10 lda #16
80fa: 8d f9 78 sta text_row
80fd: a9 20 lda #$20 ;draw on page 1
80ff: 85 ee sta HPAGE_EE
8101: a9 18 lda #24
8103: 8d f8 78 sta text_col
8106: 20 e0 78 jsr PrintDciString
8109: a9 40 lda #$40 ;and again on page 2
810b: 85 ee sta HPAGE_EE
810d: a9 18 lda #24
810f: 8d f8 78 sta text_col
8112: 4c e0 78 jmp PrintDciString
; System names, 8 bytes each, centered for display.
8115: 20 20 20 20+ system_names .dstr ‘ ’
811d: 20 20 53 4f+ .dstr ‘ SOL ’
8125: 41 4e 54 41+ .dstr ‘ANTARES ’
812d: 20 52 49 47+ .dstr ‘ RIGEL ’
8135: 20 44 45 4e+ .dstr ‘ DENEB ’
813d: 20 53 49 52+ .dstr ‘ SIRIUS ’
8145: 52 45 47 55+ .dstr ‘REGULUS ’
814d: 41 52 43 54+ .dstr ‘ARCTURUS’
8155: 57 41 52 50+ .dstr ‘WARPLINK’
;
; Draws the target reticle brackets.
;
• Clear variables
]x0 .var $00 {addr/1}
]y0 .var $01 {addr/1}
]x1 .var $02 {addr/1}
]y1 .var $03 {addr/1}
]saved_x .var $ef {addr/1}
815d: a5 32 DrawReticle lda hpage ;preserve $32
815f: 48 pha
8160: a5 ee lda HPAGE_EE ;draw here instead
8162: 85 32 sta hpage
8164: a2 0f ldx #$0f
8166: b5 00 L8166 lda ]x0,x ;preserve zero-page $00-0f
8168: 48 pha
8169: ca dex
816a: 10 fa bpl L8166
; Get signed 8-bit values from the list and adjust them by the viewport center
; point to form screen coords. (This would be much faster as a series of
; ORA/STA statements, but wouldn't move if the viewport changed position.)
816c: a2 00 ldx #$00
816e: bd d6 81 :Loop lda :line_list,x
8171: c9 80 cmp #$80
8173: f0 2f beq :Done
8175: 18 clc
8176: 6d e4 67 adc view_x_adjs
8179: 85 00 sta ]x0
817b: e8 inx
817c: 38 sec
817d: ad e8 67 lda view_y_adjs
8180: fd d6 81 sbc :line_list,x
8183: 85 01 sta ]y0
8185: e8 inx
8186: 18 clc
8187: ad e4 67 lda view_x_adjs
818a: 7d d6 81 adc :line_list,x
818d: 85 02 sta ]x1
818f: e8 inx
8190: 38 sec
8191: ad e8 67 lda view_y_adjs
8194: fd d6 81 sbc :line_list,x
8197: 85 03 sta ]y1
8199: 86 ef stx ]saved_x
819b: 20 00 10 jsr DRAW_LINE
819e: a6 ef ldx ]saved_x
81a0: e8 inx
81a1: 4c 6e 81 jmp :Loop
81a4: a2 00 :Done ldx #$00 ;restore zero-page $00-0f
81a6: 68 :Loop pla
81a7: 95 00 sta ]x0,x
81a9: e8 inx
81aa: e0 10 cpx #$10
81ac: 90 f8 bcc :Loop
81ae: 68 pla
81af: 85 32 sta hpage
81b1: 60 rts
81b2: 41 52 44 20+ .junk 36
; Target reticle definition.
81d6: f6 08 fb 08 :line_list .bulk $f6,$08,$fb,$08 ;-10,8 to -5,8
81da: 0a 08 05 08 .bulk $0a,$08,$05,$08 ;10,8 to 5,8
81de: f6 f8 fb f8 .bulk $f6,$f8,$fb,$f8 ;-10,-8 to -5,-8
81e2: 0a f8 05 f8 .bulk $0a,$f8,$05,$f8 ;10,-8 to 5,-8
81e6: f6 08 f6 05 .bulk $f6,$08,$f6,$05 ;-10,8 to -10,5
81ea: 0a 08 0a 05 .bulk $0a,$08,$0a,$05 ;10,8 to 10,5
81ee: f6 f8 f6 fb .bulk $f6,$f8,$f6,$fb ;-10,-8 to -10,-5
81f2: 0a f8 0a fb .bulk $0a,$f8,$0a,$fb ;10,-8 to 10,-5
81f6: 80 .dd1 $80
81f7: 4c .junk 1
;
; Player state.
81f8: 41 52 20 score .dd3 $205241 ;score, in BCD digits
81fb: 37 20 shield_level .dd2 $2037 ;max $92ff
81fd: 4d 49 fuel_level .dd2 $494d ;max $92ff
81ff: 53 .dd1 $53
;
; Tables for computation of arctangent.
8200: 00 06 0d 13+ arctan_tab_0 .bulk $00,$06,$0d,$13,$19,$20,$26,$2c,$33,$39,$40,$47,$4e,$55,$5c,$63
+ $6a,$71,$79,$81,$89,$91,$99,$a2,$ab,$b4,$be,$c8,$d2,$dd,$e8,$f4
+ $00,$0d,$1a,$29,$38,$48,$59,$6b,$7f,$94,$ab,$c4,$df,$fd,$1d,$42
+ $6a,$98,$cb,$07,$4c,$9d,$fe,$74,$07,$c3,$be,$1c,$27,$8f,$5b,$bc
8240: 00 00 00 00+ arctan_tab_1 .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
+ $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02,$02
+ $02,$02,$02,$03,$03,$03,$03,$04,$05,$05,$06,$08,$0a,$0d,$14,$28
;
; Compute the angle from a unit to the player.
;
; On entry:
; $09: index of unit targeting the player
;
; On exit:
; $0a: angle to player
;
• Clear variables
]delta_x .var $00 {addr/2}
]delta_z .var $02 {addr/2}
]delta_x_sgn .var $04 {addr/1}
]delta_z_sgn .var $05 {addr/1}
]val06 .var $06 {addr/1}
]val07 .var $07 {addr/1}
]tmp .var $08 {addr/1}
]src_unit_index .var $09 {addr/1}
8280: a4 09 AngleToPlayer ldy ]src_unit_index
; Compute abs(player_xc - unit_xc), wrapping at arena edge.
8282: 38 sec
8283: ad 40 87 lda last_known_xc ;get player location
8286: f9 48 66 sbc obj_xc_lo,y ;subtract unit location
8289: 09 01 ora #$01
828b: 85 00 sta ]delta_x
828d: ad 41 87 lda last_known_xc+1
8290: f9 60 66 sbc obj_xc_hi,y
8293: 85 01 sta ]delta_x+1
8295: 29 10 and #$10 ;check mod 4096 sign bit
8297: 85 04 sta ]delta_x_sgn ;save it
8299: f0 0d beq :XPos
829b: 38 sec ;invert it (modulo arithmetic)
829c: a9 00 lda #$00
829e: e5 00 sbc ]delta_x
82a0: 85 00 sta ]delta_x
82a2: a9 00 lda #$00
82a4: e5 01 sbc ]delta_x+1
82a6: 85 01 sta ]delta_x+1
82a8: a5 01 :XPos lda ]delta_x+1 ;delta now positive; strip high bits
82aa: 29 0f and #$0f
82ac: 85 01 sta ]delta_x+1
; Repeat for ZC.
82ae: 38 sec
82af: ad 42 87 lda last_known_zc
82b2: f9 78 66 sbc obj_zc_lo,y
82b5: 09 01 ora #$01
82b7: 85 02 sta ]delta_z
82b9: ad 43 87 lda last_known_zc+1
82bc: f9 90 66 sbc obj_zc_hi,y
82bf: 85 03 sta ]delta_z+1
82c1: 29 10 and #$10
82c3: 85 05 sta ]delta_z_sgn
82c5: f0 0d beq :ZPos
82c7: 38 sec
82c8: a9 00 lda #$00
82ca: e5 02 sbc ]delta_z
82cc: 85 02 sta ]delta_z
82ce: a9 00 lda #$00
82d0: e5 03 sbc ]delta_z+1
82d2: 85 03 sta ]delta_z+1
82d4: a5 03 :ZPos lda ]delta_z+1
82d6: 29 0f and #$0f
82d8: 85 03 sta ]delta_z+1
; Shift delta_x and delta_z to the left until one of them gets a 1 in the high
; bit of the high byte.
82da: 06 00 :ShiftLoop asl ]delta_x ;delta_x *= 2
82dc: 26 01 rol ]delta_x+1
82de: 30 09 bmi L82E9 ;stop if the high bit is set
82e0: 06 02 asl ]delta_z ;delta_z *= 2
82e2: 26 03 rol ]delta_z+1
82e4: 10 f4 bpl :ShiftLoop ;continue if the high bit is clear
82e6: 4c ed 82 jmp :CheckX0
82e9: 06 02 L82E9 asl ]delta_z ;delta_z *= 2 to match
82eb: 26 03 rol ]delta_z+1
; Special-case target at same X coordinate. Mathematically, the tangent is Y/X,
; so X==0 doesn't end well.
82ed: a2 40 :CheckX0 ldx #$40 ;check for delta_x ~= 0
82ef: a5 01 lda ]delta_x+1 ;get high byte of delta_x
82f1: d0 03 bne L82F6 ;not zero, delta_x isn't tiny
82f3: 4c 78 83 jmp :AbsAngInX ;zero, delta_x is tiny
; Compute Z/X (?)
82f6: a9 00 L82F6 lda #$00
82f8: a2 08 ldx #$08
82fa: 06 03 asl ]delta_z+1
82fc: 2a L82FC rol A
82fd: c5 01 cmp ]delta_x+1
82ff: 90 02 bcc L8303
8301: e5 01 sbc ]delta_x+1
8303: 26 03 L8303 rol ]delta_z+1
8305: ca dex
8306: d0 f4 bne L82FC
8308: a2 08 ldx #$08
830a: 0a L830A asl A
830b: b0 04 bcs L8311
830d: c5 01 cmp ]delta_x+1
830f: 90 03 bcc L8314
8311: e5 01 L8311 sbc ]delta_x+1
8313: 38 sec
8314: 26 02 L8314 rol ]delta_z
8316: ca dex
8317: d0 f1 bne L830A
; Check for very large result.
8319: a2 40 ldx #$40
831b: a5 03 lda ]delta_z+1
831d: c9 51 cmp #$51
831f: b0 57 bcs :AbsAngInX
; Initial values.
8321: a9 00 lda #$00
8323: 85 06 sta ]val06
8325: a9 3f lda #$3f
8327: 85 07 sta ]val07
; Iterate for the result.
8329: a2 06 ldx #$06
832b: 18 :Loop clc
832c: a5 06 lda ]val06
832e: 65 07 adc ]val07
8330: 4a lsr A
8331: a8 tay
8332: a5 02 lda ]delta_z
8334: d9 00 82 cmp arctan_tab_0,y
8337: a5 03 lda ]delta_z+1
8339: f9 40 82 sbc arctan_tab_1,y
833c: b0 05 bcs L8343
833e: 84 07 sty ]val07
8340: 4c 45 83 jmp L8345
8343: 84 06 L8343 sty ]val06
8345: ca L8345 dex
8346: d0 e3 bne :Loop
;
8348: a4 06 ldy ]val06
834a: 38 sec
834b: a5 02 lda ]delta_z
834d: f9 00 82 sbc arctan_tab_0,y
8350: 85 00 sta ]delta_x
8352: a5 03 lda ]delta_z+1
8354: f9 40 82 sbc arctan_tab_1,y
8357: 85 01 sta ]delta_x+1
8359: a4 07 ldy ]val07
835b: 38 sec
835c: b9 00 82 lda arctan_tab_0,y
835f: e5 02 sbc ]delta_z
8361: 85 02 sta ]delta_z
8363: b9 40 82 lda arctan_tab_1,y
8366: e5 03 sbc ]delta_z+1
8368: 85 03 sta ]delta_z+1
836a: a6 07 ldx ]val07
836c: a5 00 lda ]delta_x
836e: c5 02 cmp ]delta_z
8370: a5 01 lda ]delta_x+1
8372: e5 03 sbc ]delta_z+1
8374: b0 02 bcs :AbsAngInX
8376: a6 06 ldx ]val06
; Angle 0-63 is in X-reg; use the X/Z delta sign bits to put it in the correct
; quadrant.
8378: 86 08 :AbsAngInX stx ]tmp
837a: a5 04 lda ]delta_x_sgn
837c: f0 07 beq L8385
837e: 38 sec
837f: a9 80 lda #$80
8381: e5 08 sbc ]tmp
8383: 85 08 sta ]tmp
8385: a5 05 L8385 lda ]delta_z_sgn
8387: f0 07 beq L8390
8389: 38 sec
838a: a9 00 lda #$00
838c: e5 08 sbc ]tmp
838e: 85 08 sta ]tmp
8390: 38 L8390 sec
8391: a5 08 lda ]tmp
8393: e9 40 sbc #$40
8395: 85 0a sta target_angle
8397: 60 rts
8398: 61 72 20 74+ .align $0100 (104 bytes)
;
; Game object state. The $6600 area is for 3D engine stuff (position,
; velocity). This area is for things that are only relevant while playing the
; game. The indices are common to both.
;
; As with the $6600 area, there are up to 24 active objects, including
; projectiles. The player is object #0.
;
; IMPORTANT: $8400-856C is overwritten by DYN# files during the game. For the
; mission briefing, $8400-9157 holds the code. $8400 is the entry pont. Most
; of DYN looks like uninitialized filler, except for the initial unit types at
; $8400-8417 and some spawning stuff and parameters starting at $8550.
;
; ---
;
; $8400-8417 in the DYN# files holds the initial set of enemy unit types. Empty
; slots are set to $80. While the level is being played, this is updated as
; objects are created and destroyed.
;
; Slot 0 is reserved for the player, slots 1-2 are reserved for player shots,
; and slot 3 is reserved for the Warplink.
8400: ff ff 00 00+ gobj_types .junk 24 ;initial set of objects
;
; Movement class, copied from the unit move class table for units, or set to $09
; for projectiles.
8418: ff ff 00 00+ gobj_move_class .junk 24 ;also read by ROCK1
;
; Flash pattern index. Newly-spawned units flash a few times when they first
; appear. The pattern is defined at $8666.
8430: ff ff 00 00+ gobj_vis_flash .junk 24
;
; Layers of armor remaining. For the player (entry #0) this holds the damage
; sustained in the current frame.
unit_armor_remaining
8448: ff ff 00 00+ .junk 24
;
; Projectile time-to-live, decremented every frame until it reaches zero.
;
; The shots/round value determines how many projectiles may be in flight
; simultaneously. The field is set to $80 if the shot is unavailable to this
; unit.
8460: ff ff 00 00+ gobj_shot0_ttl .junk 24
8478: ff ff 00 00+ gobj_shot1_ttl .junk 24
8490: ef ef 00 00+ gobj_shot2_ttl .junk 24
;
; Shot cooldown. This limits how quickly a unit can fire even if it has a shot
; available.
gobj_shot_cooldn
84a8: ef ef 00 00+ .junk 24
;
; Unit collision flag.
;
; For objects in general, this is bool 00/80 indicating whether the unit
; collided with another object and was forced to stop moving. For an enemy
; ground unit, this may also be $81, indicating that the unit collided with the
; player.
84c0: ef ef 00 00+ gobj_coll_flags .junk 24
;
; Movement pattern. Initially $80, this is set to a small integer, the meaning
; of which depends on the unit's movement class.
gobj_move_pattern
84d8: ef ef 00 00+ .junk 24
;
; Number of frames to continue with the current movement pattern. Set to $80
; when the pattern is set, and changed to a random positive value by the
; pattern-specific code.
gobj_mvpat_cooldn
84f0: ef ef 00 00+ .junk 24
;
; Unit elevation, from 0-3, used during collision testing.
;
; The collision test only looks at X and Z. This value takes Y into account, so
; that shots correctly miss a Skimmer in flight. It also seems to apply to
; movement class 4 (Seekers and Guisers).
;
; This is overloaded by move class 1 pattern 3 as a counter.
gobj_coll_height
8508: ff ff 00 00+ .junk 24
;
; Movement-related counter. Typically this determines how long we do a
; particular thing, e.g. rotate randomly.
gobj_move_counter
8520: ff ff 00 00+ .junk 24
;
; For the player, this is set nonzero when the cloak is engaged, and counts
; down. For the Stalker, this determines when the unit cloaks or decloaks (when
; it reaches zero the cloak toggles).
gobj_cloak_cooldn
8538: ff ff 00 00+ .junk 24
;
; Each entry represents the chance of spawning a new enemy when N units are in
; the arena. For example, LEV1 uses $46,$1e,$00. If 2 units are on the field,
; there is no chance of spawning an additional unit. Besides capping the max
; unit count, this randomizes the pace of reinforcements.
spawn_likelihood
8550: ff ff 00 00+ .junk 9
;
; This holds the odds of spawning a unit of type N. Each entry represents the
; top of a range from 1-255. If a randomly-rolled value is <= this, we spawn
; that unit.
unit_spawn_chance
8559: ff 00 00 ff+ .junk 15
;
; After killing this many units no more will spawn (so you can't stay on a lower
; level forever).
nospawn_kill_count
8568: ff .dd1 $ff
;
; The warplink spawns if this many frames have elapsed.
frames_before_warp
8569: ff 00 .dd2 $00ff
;
; The warplink spawns after the player has killed this many enemies.
kills_before_warp
856b: 00 .dd1 $00
warplink_rot_speed
856c: ff .dd1 $ff
856d: ff 00 00 ff+ .align $80 (19 bytes)
;
; Game state -- flags and counters.
8580: 80 sound_enab_flag .dd1 $80 ;bool 00/80: is sound enabled?
8581: 00 shots_vis_flag .dd1 $00 ;bool 00/80: are shots visible on radar?
8582: 00 00 rng_state .dd2 $0000 ;state for the random number generator
8584: ef system_num .dd1 $ef ;current system (1-7)
player_destruct_flag
8585: ef .dd1 $ef ;bool 00/80: set on self destruct
warplink_spawn_flag
8586: 00 .dd1 $00 ;bool 00/80: true if warplink has been spawned
8587: 00 level_tour_flag .dd1 $00 ;every 200 frames, auto-jump to next level
8588: ef num_kills .dd1 $ef ;number of kills on this level
8589: ef 00 frame_count .dd2 $00ef ;frames elapsed since start of level
warplink_touch_flag
858b: 00 .dd1 $00 ;bool 00/80: did player touch warplink?
collide_last_frame
858c: ef .dd1 $ef ;bool 00/80: did player collide with unit last frame?
858d: ef player_coll_ctr .dd1 $ef ;player constant-collision counter
858e: 00 raven_gfx_stage .dd1 $00 ;0-3, with 0=full (no cloak)
cloak_trans_state
858f: 00 .dd1 $00 ;0=steady, 1=turning on, 2=turning off
8590: ef button1_up_flag .dd1 $ef ;$80 = joystick button 1 was pressed
;
; Unit capabilities. There are 15 entries, with entry #0 for the player. This
; covers all "active" units (e.g. not obstacles, fuelbays, or projectiles).
;
8591: 15 0f 10 12+ unit_weap_type .bulk $15,$0f,$10,$12,$12,$13,$10,$14,$11,$14,$10,$12,$0f,$00,$00 ;weapon type
85a0: 00 01 02 01+ unit_armor_thck .bulk $00,$01,$02,$01,$02,$03,$02,$05,$02,$04,$01,$01,$01,$01,$01 ;armor thickness
85af: 2d 32 20 30+ unit_fwd_speed .bulk $2d,$32,$20,$30,$28,$1e,$28,$32,$00,$00,$00,$3c,$50,$00,$20 ;forward speed, in units/frame (KPH / 4)
85be: 19 0a 10 10+ unit_rev_speed .bulk $19,$0a,$10,$10,$14,$0f,$19,$1e,$00,$00,$00,$19,$28,$00,$0a ;reverse speed, in units/frame (KPH / 4)
85cd: 02 01 02 03+ unit_turn_rate .bulk $02,$01,$02,$03,$02,$02,$03,$03,$01,$01,$00,$02,$02,$00,$00 ;maximum turn rate
85dc: 02 02 01 01+ unit_shots_rnd .bulk $02,$02,$01,$01,$01,$02,$01,$03,$02,$01,$03,$01,$01,$00,$00 ;shots/round
unit_shot_cooldn
85eb: 03 08 00 00+ .bulk $03,$08,$00,$00,$00,$08,$00,$06,$0c,$00,$00,$00,$00,$00,$00 ;shot cooldown for units with > 1 shot/rnd
unit_weapon_yadj
85fa: 00 ea 03 f8+ .bulk $00,$ea,$03,$f8,$fd,$04,$06,$03,$fb,$05,$ec,$f2,$f1,$00,$00 ;weapon hardpoint height adjustment
8609: 4b 3c 46 46+ unit_max_dim .bulk $4b,$3c,$46,$46,$4b,$5a,$50,$50,$50,$50,$5a,$46,$50,$4b,$32 ;maximum dimension, for visibility (+ collisions?)
8618: 00 20 35 20+ unit_score_bcd .bulk $00,$20,$35,$20,$30,$75,$90,$00,$25,$30,$40,$50,$60,$05,$10 ;points received for kill (BCD)
;
; Movement class table, indexed by object type.
;
8627: 00 unit_move_class .dd1 $00 ;0 (player)
8628: 01 01 01 01+ .fill 7,$01 ;1 (Sandsled)
862f: 02 .dd1 $02 ;8 (Laser Battery)
8630: 02 .dd1 $02
8631: 02 .dd1 $02
8632: 03 .dd1 $03 ;11 (Skimmer)
8633: 03 .dd1 $03
8634: 04 .dd1 $04 ;13 (Guiser)
8635: 04 .dd1 $04
8636: 05 .dd1 $05 ;15 (Projectile - Low Laser)
8637: 05 .dd1 $05
8638: 05 .dd1 $05
8639: 06 .dd1 $06 ;18 (Projectile - Light Cannon)
863a: 06 .dd1 $06
863b: 06 .dd1 $06
863c: 07 .dd1 $07 ;21 (Projectile - Thunder Cannon)
863d: 08 .dd1 $08 ;22 (Obstacle)
863e: 08 .dd1 $08 ;23 (Fuelbay)
863f: 08 .dd1 $08 ;24 (Warplink)
8640: 09 .dd1 $09 ;25 (explosion meta 0)
8641: 09 .dd1 $09
8642: 09 .dd1 $09
;
; Number of frames in explosion animations plus one, for meta types $19/1a/1b.
explos_frame_cnt
8643: 09 09 09 .bulk $09,$09,$09
;
; Base type for explosion animations, for meta types $19/1a/1b. $1e is the
; first explosion type minus one, $28 is the first impact type minus one.
explos_base_type
8646: 1e 28 28 .bulk $1e,$28,$28
;
; Damage caused by projectiles and bombs. Indexed by type, $0d-14.
8649: 20 20 0a 10+ projectile_dmgs .bulk $20,$20,$0a,$10,$14,$0e,$12,$1a
;
; Projectile time-to-live. Indexed by type, $0f-15.
8651: 12 16 1c 14+ projectile_ttls .bulk $12,$16,$1c,$14,$18,$1c,$18
;
; Projectile range, high byte. Used when determining whether the player is in
; range of a unit's guns. (The actual range is speed * TTL.)
projectile_range_hi
8658: 08 0a 0e 06+ .bulk $08,$0a,$0e,$06,$08,$0a,$09
;
; Projectile speed, in units/frame. Indexed by type, $0f-15.
projectile_speeds
865f: 7d 7d 7d 5f+ .bulk $7d,$7d,$7d,$5f,$5f,$5f,$69
;
; Boolean values, used to make a newly-spawned unit flash. Initial index is
; $13, decremented before being used, counted down to zero.
;
; Looks like: #...#..#.#.##.###.#
new_unit_flash_pat
8666: 80 00 80 80+ .bulk $80,$00,$80,$80,$80,$00,$80,$80,$00,$80,$00,$80,$00,$00,$80,$00
+ $00,$00,$80
; Player rotation speed, in 256th angles per frame.
;
; This is indexed by movement direction - 1:
; 1 - fwd/left
; 2 - fwd
; 3 - fwd/right
; 4 - rotate left
; 5 - stationary
; 6 - rotate right
; 7 - back/left
; 8 - back
; 9 - back/right
8679: 02 00 fe 02+ plyr_rot_speed .bulk $02,$00,$fe,$02,$00,$fe,$fe,$00,$02 ;+/- 2
;
; Player movement speed, in units per frame.
8682: 2d 2d 2d 00+ plyr_move_speed .bulk $2d,$2d,$2d,$00,$00,$00,$19,$19,$19 ;45 forward, 25 reverse
;
; Pulsar behavior modifier.
868b: 0e 09 06 05+ pulsar_p0_count .bulk $0e,$09,$06,$05,$04,$03,$02,$02
8693: 0f 0a 07 05+ pulsar_p1_count .bulk $0f,$0a,$07,$05,$04,$04,$03,$02
;
; Unit collision handler index. When two units collide, we have to decide how
; to move them. If one is immobile it's easy, if both are mobile they may both
; need new instructions.
;
; The unit movement classes are combined using bit patterns at $9698/96a1, and
; used as an index into this table. The value here is an index into the handler
; function. Zero entries are either uninteresting or impossible.
unit_coll_hnd_index
869b: 00 01 01 00+ .bulk $00,$01,$01,$00,$03,$03,$00,$00
86a3: 09 01 01 00+ .bulk $09,$01,$01,$00,$02,$00,$04,$01
86ab: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$04,$00
86b3: 01 01 01 00+ .bulk $01,$01,$01,$00,$02,$00,$04,$01
86bb: 05 01 01 00+ .bulk $05,$01,$01,$00,$05,$00,$06,$01
86c3: 0a 0a 0a 0a+ .bulk $0a,$0a,$0a,$0a,$08,$00,$00,$05
86cb: 07 00 00 00+ .bulk $07,$00,$00,$00,$00,$00,$00,$00
;
; This affects when movement class 1 units break out of patterns 1 and 2,
; respectively.
86d3: 09 0c 06 0a+ mc1_p1_chance .bulk $09,$0c,$06,$0a,$08,$04,$04,$06
86db: 06 09 02 08+ mc1_p2_chance .bulk $06,$09,$02,$08,$06,$04,$03,$03
;
; Movement class 1 movement pattern chances. This is used to select a movement
; pattern at random. The tables are indexed by object type (0-7). The first
; table holds the chance of selecting pattern 0, the second table holds the
; chance of selecting pattern 1. If the random value is >= the pattern 1
; chance, pattern 2 is used.
mc1_choice0_chances
86e3: 4c 40 20 30+ .bulk $4c,$40,$20,$30,$40,$10,$28,$20
mc1_choice1_chances
86eb: 99 84 60 80+ .bulk $99,$84,$60,$80,$60,$6e,$64,$70
;
; Move class 1, pattern 3, move count.
86f3: 0a 01 08 06+ mc1_p3_count .bulk $0a,$01,$08,$06,$01,$01,$01,$06,$14,$08,$18,$0c,$0a,$06,$10,$06
+ $14,$18,$0c,$0a,$18,$26,$28,$14
;
; Move class 1, pattern 3, move direction: 0=stop, 1=reverse, 2=forward.
870b: 01 01 01 01+ mc1_p3_movedir .bulk $01,$01,$01,$01,$01,$01,$01,$01,$02,$01,$00,$00,$01,$01,$01,$01
+ $02,$02,$02,$02,$02,$00,$00,$02
;
; Move class 1, pattern 3, rotation: 0=none, 1=random, 2=left, 3=right.
8723: 02 00 00 00+ mc1_p3_rotation .bulk $02,$00,$00,$00,$00,$00,$00,$03,$03,$00,$03,$01,$01,$00,$03,$01
+ $01,$01,$01,$00,$00,$01,$01,$00
;
; Hovercraft bounce height. Hovercraft Y coordinates are set to one of these
; values on alternating frames, to give it a bit of bounce.
hover_bounce_height
873b: 00 04 .bulk $00,$04
873d: ff 00 00 .junk 3
;
; Player's last known X/Z coordinate. When unloaked this is updated every
; frame, when cloaked it's only updated when the player fires.
8740: 00 00 last_known_xc .dd2 $0000
8742: 00 00 last_known_zc .dd2 $0000
8744: ff ff 00 00+ .junk 12
********************************************************************************
* Main high-level loop for playing the game. Loads the current level, opens *
* the shutter, and starts play. *
********************************************************************************
* IMPORTANT: this is overwritten by BRIEFING and PLAYGAME. The data in ROCK2 *
* that overlaps with PLAYGAME ($8400-9B42) matches exactly. *
********************************************************************************
• Clear variables
]thing_8d? .var $8d {addr/1}
8750: 20 a7 87 StartMission jsr InitGame
8753: 20 3a 88 :StartLevel jsr PrepareLevel
8756: 20 70 8a jsr OpenShutter
8759: ad 00 c0 lda KBD
875c: c5 8d cmp ]thing_8d? ;? ($8d is usually zero; used by unit move code)
875e: d0 03 bne :GameLoop
8760: 2c 10 c0 bit KBDSTRB ;clear pending key
; Main play loop. Calls the update function until we're dead or hit a warplink.
8763: 20 e3 8b :GameLoop jsr UpdateGameState ;do one frame
8766: ad 8b 85 lda warplink_touch_flag ;did we bump a warplink?
8769: 30 05 bmi :NextLevel ;yes, move to next level
876b: ad 85 85 lda player_destruct_flag ;are we dead?
876e: 10 f3 bpl :GameLoop ;no, keep going
; Dead or warping out.
8770: 20 e0 a1 :NextLevel jsr InitSound00
8773: ad 85 85 lda player_destruct_flag ;are we out of the loop because we died?
8776: 30 19 bmi :GameOver ;yes, go deal with that
8778: ad 84 85 lda system_num ;check system number
877b: c9 07 cmp #$07 ;are we on Arcturus?
877d: f0 12 beq :GameOver ;yes, game is over
877f: 20 ad 88 jsr DrawHudPage12
8782: 20 d5 88 jsr PrepPage12
8785: 20 5d 89 jsr WarpOutPart1
8788: 20 df 89 jsr WarpOutPart2
878b: ee 84 85 inc system_num ;move to next system
878e: 4c 53 87 jmp :StartLevel
8791: 20 ad 88 :GameOver jsr DrawHudPage12
8794: ad 87 85 lda level_tour_flag
8797: 10 03 bpl :NotTour
8799: ee 84 85 inc system_num ;?
879c: ad 85 85 :NotTour lda player_destruct_flag ;dead?
879f: 10 03 bpl :Winner ;no, must be victorious
87a1: 4c 21 89 jmp PlayerDeadAnim
87a4: 4c 00 a3 :Winner jmp Victory
87a7: 20 e0 a1 InitGame jsr InitSound00 ;reset sound effects
87aa: a9 01 lda #$01 ;initial system is Sol
87ac: 8d 84 85 sta system_num
87af: a9 00 lda #$00 ;initialize flags and score
87b1: 8d 85 85 sta player_destruct_flag
87b4: 8d 87 85 sta level_tour_flag
87b7: a9 00 lda #$00
87b9: 8d f8 81 sta score
87bc: 8d f9 81 sta score+1
87bf: 8d fa 81 sta score+2
87c2: a9 ff lda #$ff ;set shields and fuel to max
87c4: 8d fb 81 sta shield_level
87c7: 8d fd 81 sta fuel_level
87ca: a9 92 lda #$92
87cc: 8d fc 81 sta shield_level+1
87cf: 8d fe 81 sta fuel_level+1
87d2: a9 00 lda #$00 ;reset text parameters
87d4: 20 00 78 jsr PrintChar
87d7: 4c 12 88 jmp InitGraphics ;init the graphics engine
;
; Initializes viewport 0 (which is the only one Stellar 7 uses).
;
87da: a0 00 InitViewport0 ldy #$00
87dc: 20 12 1f jsr INIT_VIEWPORT_SLOT ;init viewport 0
87df: a9 01 lda #$01 ;overwrite some values
87e1: 8d d0 67 sta view_near_vert
87e4: a9 00 lda #$00
87e6: 8d d4 67 sta view_near_lo
87e9: a9 00 lda #$00
87eb: 8d d8 67 sta view_near_hi
87ee: a9 16 lda #$16
87f0: 8d e0 67 sta view_far_hi
87f3: a9 77 lda #$77 ;+119
87f5: 8d e4 67 sta view_x_adjs
87f8: a9 6b lda #$6b ;+107
87fa: 8d e8 67 sta view_y_adjs
87fd: a9 90 lda #$90 ;-112
87ff: 8d ec 67 sta view_left_vals
8802: a9 6f lda #$6f ;+111
8804: 8d f0 67 sta view_right_vals
8807: a9 4a lda #$4a ;+74
8809: 8d f4 67 sta view_top_vals
880c: a9 ae lda #$ae ;-82
880e: 8d f8 67 sta view_bottom_vals
8811: 60 rts
;
; Top-level graphics init function. Called at the start of each level.
;
8812: 20 69 1f InitGraphics jsr INIT_GFX_ENGINE
8815: 20 da 87 jsr InitViewport0
8818: a9 80 lda #$80
881a: 8d fc 65 sta show_bkgnd_flag ;enable stars/mountains
881d: a9 00 lda #<ClearViewport
881f: 8d f6 65 sta jmp_addr_clear
8822: a9 74 lda #>ClearViewport
8824: 8d f7 65 sta jmp_addr_clear+1
8827: 2c 52 c0 bit MIXCLR
882a: 2c 57 c0 bit HIRES
882d: 2c 50 c0 bit TXTCLR
8830: 2c 55 c0 bit TXTPAGE2
8833: 20 10 1c jsr COPY_HIRES_2TO1
8836: 2c 54 c0 bit TXTPAGE1
8839: 60 rts
;
; Prepares for a new level by loading the three per-level files (OBJ#, DYN#,
; LEV#) and initializing game state.
;
883a: 20 88 af PrepareLevel jsr LOAD_LEVEL_FILES
883d: a9 17 lda #c_num_objects-1 ;objects start at end of array
883f: 85 82 sta g_obj_index ; (start of DYN#, $8400-8417)
8841: a4 82 :ObjLoop ldy g_obj_index
8843: b9 00 84 lda gobj_types,y ;object defined?
8846: 30 0a bmi :Skip ;no, skip entry
8848: 20 78 97 jsr SetObjectDefaults
884b: a4 82 ldy g_obj_index
884d: a9 00 lda #$00 ;zero this out (was set to $13?)
884f: 99 30 84 sta gobj_vis_flash,y
8852: c6 82 :Skip dec g_obj_index ;move to next entry
8854: 10 eb bpl :ObjLoop
; Reset some player state.
8856: a9 00 lda #$00
8858: 8d 88 85 sta num_kills
885b: 8d 89 85 sta frame_count
885e: 8d 8a 85 sta frame_count+1
8861: 8d 8b 85 sta warplink_touch_flag
8864: 8d 86 85 sta warplink_spawn_flag
8867: 8d 8c 85 sta collide_last_frame
886a: 8d a8 84 sta gobj_shot_cooldn
886d: 8d 8d 85 sta player_coll_ctr
8870: 8d 90 85 sta button1_up_flag
8873: 8d 8e 85 sta raven_gfx_stage
8876: 8d 8f 85 sta cloak_trans_state
8879: 8d 38 85 sta gobj_cloak_cooldn
887c: 20 60 99 jsr SetLastKnownPos
887f: ad 84 85 lda system_num
8882: 20 e5 80 jsr PrintSystemName
8885: a9 01 lda #$01
8887: 8d f0 65 sta render_page
888a: a9 80 lda #$80
888c: 8d f3 65 sta block_movement1
888f: 20 00 1c jsr UPD_DRAW_OBJ_HUD
8892: a9 00 lda #$00
8894: 8d f3 65 sta block_movement1
8897: 20 79 99 jsr DrawRaven
889a: a9 00 lda #$00
889c: 8d f0 65 sta render_page
889f: a9 00 lda #$00
88a1: 20 00 7e jsr DrawHudItems
88a4: 20 79 99 jsr DrawRaven
88a7: a9 01 lda #$01
88a9: 8d f0 65 sta render_page
88ac: 60 rts
;
; Draws the heads-up display on both hi-res page.
;
88ad: a9 80 DrawHudPage12 lda #$80
88af: 8d f2 65 sta block_movement0 ;prevent unit movement; we want to draw twice without
88b2: 8d f3 65 sta block_movement1 ; updating anything
88b5: 20 00 1c jsr UPD_DRAW_OBJ_HUD ;draw on current page
88b8: 20 79 99 jsr DrawRaven
88bb: 20 d0 65 jsr SwapBuffers ;switch to the other page
88be: 20 00 1c jsr UPD_DRAW_OBJ_HUD ;draw same again
88c1: 20 79 99 jsr DrawRaven
88c4: a9 00 lda #$00
88c6: 8d f2 65 sta block_movement0 ;allow unit movement
88c9: 8d f3 65 sta block_movement1
88cc: a9 01 lda #$01 ;draw on hi-res page 2
88ce: 8d f0 65 sta render_page
88d1: 2c 54 c0 bit TXTPAGE1 ;show hi-res page 1
88d4: 60 rts
88d5: 20 0b 1e PrepPage12 jsr MARK_ALL_INACTIVE
88d8: a9 80 lda #$80
88da: 8d 00 66 sta obj_alive_flag
88dd: a9 00 lda #$00 ;full raven
88df: 8d 8e 85 sta raven_gfx_stage
88e2: 4c ad 88 jmp DrawHudPage12
;
; Handles control keys.
;
88e5: ad 00 c0 CheckCtrlKeys lda KBD ;get key
88e8: 2c 10 c0 bit KBDSTRB ;clear input
88eb: c9 9b CheckCtrlKeys1 cmp #$9b ;ESC?
88ed: d0 0d bne :NotEsc ;no, branch
88ef: ad 00 c0 L88EF lda KBD ;game is paused; wait for another ESC
88f2: 10 fb bpl L88EF
88f4: 2c 10 c0 bit KBDSTRB
88f7: c9 9b cmp #$9b
88f9: d0 f4 bne L88EF
88fb: 60 rts
88fc: c9 93 :NotEsc cmp #$93 ;Ctrl+S (sound toggle)?
88fe: d0 0b bne :CheckControlMode ;no, branch
8900: ad 80 85 lda sound_enab_flag ;yes, toggle sound
8903: 49 80 eor #$80
8905: 8d 80 85 sta sound_enab_flag
8908: 4c e0 a1 jmp InitSound00
:CheckControlMode
890b: a0 00 ldy #$00
890d: c9 8b cmp #$8b ;Ctrl+K (keyboard)?
890f: f0 0c beq :GotIt ;yes, branch
8911: a0 01 ldy #$01
8913: c9 8a cmp #$8a ;Ctrl+J (joystick)?
8915: f0 06 beq :GotIt ;yes, branch
8917: a0 80 ldy #$80
8919: c9 81 cmp #$81 ;Ctrl+A (joyport)?
891b: d0 03 bne :Bail ;no, bail
891d: 8c f4 7c :GotIt sty control_mode ;update control mode
8920: 60 :Bail rts
;
; Flash some stuff when the player dies.
;
• Clear variables
]tmp .var $8d {addr/1}
8921: a9 00 PlayerDeadAnim lda #$00 ;filled-in raven graphic
8923: 8d 8e 85 sta raven_gfx_stage
8926: 20 79 99 jsr DrawRaven
8929: 20 d0 65 jsr SwapBuffers
892c: a9 03 lda #$03 ;empty raven graphic
892e: 8d 8e 85 sta raven_gfx_stage
8931: 20 79 99 jsr DrawRaven
8934: a9 20 lda #$20 ;set to page 1
8936: 85 32 sta hpage
8938: 20 43 a4 jsr CallInvertViewport ;invert the viewport on page 1
893b: 20 06 a2 jsr PlayDeadSound_jmp ;plays sound while flipping hi-res pages
893e: 2c 54 c0 bit TXTPAGE1 ;show page 1
8941: a9 01 lda #$01 ;render on page 2
8943: 8d f0 65 sta render_page
8946: a9 00 lda #$00
8948: 8d c0 67 sta view_actv_flags ;disable the viewport
894b: 20 00 60 jsr UpdateDrawSwap
894e: a2 3c ldx #$3c
8950: a0 1e L8950 ldy #$1e ;delay
8952: c6 8d L8952 dec ]tmp
8954: d0 fc bne L8952
8956: 88 dey
8957: d0 f9 bne L8952
8959: ca dex
895a: d0 f4 bne L8950
895c: 60 rts
;
; Animates the first part of the warp-out sequence, in which we rise spinning
; above the level of the mountains.
;
; We want to draw the background (stars, mountains, horizon) but no units or
; target reticle.
;
• Clear variables
]bk_horiz_pos .var $22 {addr/2}
]bk_vert_pos .var $24 {addr/2}
]sys_flash_ctr .var $80 {addr/1} ;counter for name flash
]counter .var $86 {addr/1}
]system_num_zp .var $8b {addr/1}
]sys_display_flag .var $8c {addr/1} ;bool 00/80: draw system name
]rotate_frac .var $c0 {addr/1}
]rotate_amt .var $c1 {addr/1}
]unused_c2? .var $c2 {addr/1}
]climb_rate .var $c3 {addr/1}
895d: 20 c0 a1 WarpOutPart1 jsr SwapSound
8960: a0 fd ldy #$fd ;start at -3
8962: c8 :SkipLoop iny ;advance until we find the end (first value >= $80)
8963: c8 iny
8964: c8 iny
8965: b9 00 73 lda star_data,y
8968: 10 f8 bpl :SkipLoop
896a: a9 00 lda #$00 ;replace the list-end marker with $00
896c: 99 00 73 sta star_data,y ;(unlocking data for stars normally above viewport?)
896f: a9 08 lda #$08 ;init params for flashing system name
8971: 85 8b sta ]system_num_zp
8973: a9 00 lda #$00
8975: 85 80 sta ]sys_flash_ctr
8977: a9 80 lda #$80
8979: 85 8c sta ]sys_display_flag
897b: 20 60 1c jsr INIT_WARP_VIEW ;init viewport and bkgnd vars ($22-26)
; Configure per-system warp-out animation parameters.
897e: a9 00 lda #$00
8980: 85 c0 sta ]rotate_frac
8982: 85 c2 sta ]unused_c2?
8984: ae 84 85 ldx system_num
8987: ca dex
8988: bd cd 89 lda warp_rot_rate,x
898b: 85 c1 sta ]rotate_amt
898d: a9 01 lda #$01
898f: 85 c3 sta ]climb_rate
8991: ae 84 85 :Loop ldx system_num ;per-system parameters for warp out
8994: ca dex
8995: 18 clc
8996: a5 c0 lda ]rotate_frac ;update the fractional value with the acceleration
8998: 7d d3 89 adc warp_rot_accel,x ;this sets the carry when it rolls over
899b: 85 c0 sta ]rotate_frac
899d: a5 c1 lda ]rotate_amt ;update the amount we rotate each frame
899f: 7d d9 89 adc warp_rot_sign,x ;this adds 0/+1 or -1/0 based on table and carry flag
89a2: 85 c1 sta ]rotate_amt
89a4: 20 a0 1c jsr DRAW_WARP_BKGND ;draw with current parameters
89a7: a5 22 lda ]bk_horiz_pos ;tweak the sound based on height
89a9: 4a lsr A
89aa: 69 60 adc #$60
89ac: 8d 1c 03 sta warp_sound_thing?
89af: 20 0c a2 jsr PlaySound1_jmp
89b2: 20 d0 65 jsr SwapBuffers ;switch to the other hi-res page
89b5: 20 0c a2 jsr PlaySound1_jmp
89b8: 20 ae 8b jsr FlashSystemName
89bb: a5 24 lda ]bk_vert_pos ;done yet?
89bd: c9 80 cmp #$80
89bf: 90 d0 bcc :Loop ;no, keep going
; If we ended with hi-res page 1 being displayed (which means we're set to
; render on hi-res page 2, which is indicated by render_page==1), we're set.
; Otherwise, draw and flip one more time so we can always start rendering on
; page 2.
89c1: ad f0 65 lda render_page ;are we set to render on hi-res page 2?
89c4: d0 06 bne :Done ;yes, we're done
89c6: 20 a0 1c jsr DRAW_WARP_BKGND ;no, draw and flip
89c9: 4c d0 65 jmp SwapBuffers
89cc: 60 :Done rts
; Warp-out parameters, indexed by system_num (1-6) - 1.
;
; We don't need parameters for system #7 because we don't warp out of Arcturus.
89cd: 01 fe fd 03+ warp_rot_rate .bulk $01,$fe,$fd,$03,$02,$fc ;initial rotation rate
89d3: 50 f4 ce 3c+ warp_rot_accel .bulk $50,$f4,$ce,$3c,$28,$ec ;rate of acceleration
89d9: 00 ff ff 00+ warp_rot_sign .bulk $00,$ff,$ff,$00,$00,$ff ;sign adjustment for rotation accel
;
; Animates the second part of the warp-out sequence, in which the shutter closes
; while stars spin by.
;
89df: a9 80 WarpOutPart2 lda #$80 ;don't try to draw mountains
89e1: 8d 00 72 sta mountain_data
89e4: a9 21 lda #$21
89e6: 85 86 sta ]counter
89e8: 20 a0 1c :ShutterLoop jsr DRAW_WARP_BKGND
89eb: a5 22 lda ]bk_horiz_pos
89ed: 4a lsr A
89ee: 69 60 adc #$60
89f0: 8d 1c 03 sta warp_sound_thing?
89f3: 20 0c a2 jsr PlaySound1_jmp
89f6: 20 0c a2 jsr PlaySound1_jmp
89f9: 20 d9 8a jsr DrawShutter
89fc: a5 86 lda ]counter
89fe: c9 21 cmp #$21
8a00: f0 0d beq L8A0F
8a02: e6 86 inc ]counter
8a04: 20 d9 8a jsr DrawShutter
8a07: a5 58 lda VIEW_TOP ;reduce the viewport to match
8a09: d0 02 bne :NoDec1
8a0b: c6 59 dec VIEW_TOP+1
8a0d: c6 58 :NoDec1 dec VIEW_TOP
8a0f: a5 58 L8A0F lda VIEW_TOP ;repeat
8a11: d0 02 bne :NoDec2
8a13: c6 59 dec VIEW_TOP+1
8a15: c6 58 :NoDec2 dec VIEW_TOP
8a17: a5 24 lda ]bk_vert_pos ;check position
8a19: c9 b4 cmp #180
8a1b: 90 06 bcc L8A23
8a1d: a9 00 lda #$00
8a1f: 85 c2 sta ]unused_c2?
8a21: 85 c3 sta ]climb_rate ;stop climbing
8a23: a2 bd L8A23 ldx #189 ;bottom of viewport
8a25: 4c 59 8a jmp :StartCopy
; Copy rows from page 2 to page 1.
]dst_ptr .var $87 {addr/2}
]src_ptr .var $89 {addr/2}
8a28: bd 00 15 :CopyLines lda HIRES_ADDR_LO,x
8a2b: 85 89 sta ]src_ptr
8a2d: 85 87 sta ]dst_ptr
8a2f: bd 00 16 lda HIRES_ADDR_HI,x ;copy from $4000 ...
8a32: 09 40 ora #$40
8a34: 85 8a sta ]src_ptr+1
8a36: bd 00 16 lda HIRES_ADDR_HI,x ;...to $2000
8a39: 09 20 ora #$20
8a3b: 85 88 sta ]dst_ptr+1
8a3d: a0 20 ldy #32 ;start at byte 32
8a3f: b1 89 :Loop lda (]src_ptr),y ;do 4 at a time to reduce loop overhead
8a41: 91 87 sta (]dst_ptr),y
8a43: 88 dey
8a44: b1 89 lda (]src_ptr),y
8a46: 91 87 sta (]dst_ptr),y
8a48: 88 dey
8a49: b1 89 lda (]src_ptr),y
8a4b: 91 87 sta (]dst_ptr),y
8a4d: 88 dey
8a4e: b1 89 lda (]src_ptr),y
8a50: 91 87 sta (]dst_ptr),y
8a52: 88 dey
8a53: d0 ea bne :Loop ;loop until byte 0
8a55: 20 0c a2 jsr PlaySound1_jmp
8a58: ca dex
8a59: e4 86 :StartCopy cpx ]counter
8a5b: d0 cb bne :CopyLines
8a5d: 20 ae 8b jsr FlashSystemName
8a60: e6 86 inc ]counter
8a62: a5 86 lda ]counter
8a64: c9 be cmp #190 ;reached bottom of viewport?
8a66: 90 80 bcc :ShutterLoop ;not yet
8a68: 20 c0 a1 jsr SwapSound
8a6b: a9 08 lda #$08 ;"WARPLINK"
8a6d: 4c e5 80 jmp PrintSystemName
;
; Shutter-open animation for warp-in. Hi-res page 1 shows the shutter, hi-res
; page 2 has a rendered scene. We scroll the shutter up on page 1, copying
; lines from page 2 as we go.
;
]src_ptr .var $87 {addr/2}
]dst_ptr .var $89 {addr/2}
8a70: ad 84 85 OpenShutter lda system_num ;prep system name for flashing
8a73: 85 8b sta ]system_num_zp
8a75: a9 00 lda #$00
8a77: 85 80 sta ]sys_flash_ctr
8a79: 85 8c sta ]sys_display_flag
8a7b: a9 bd lda #189 ;bottom line of viewport
8a7d: 85 86 sta ]counter
8a7f: a2 21 :CopyShutter ldx #33 ;top line of viewport
8a81: bd 00 15 lda HIRES_ADDR_LO,x ;set src pointer to top row
8a84: 85 87 sta ]src_ptr ;(this becomes dst_ptr; next line down will be src)
8a86: bd 00 16 lda HIRES_ADDR_HI,x
8a89: 09 20 ora #$20
8a8b: 85 88 sta ]src_ptr+1
8a8d: 4c a9 8a jmp :UpdatePtr
8a90: e8 :CopyRows inx ;advance to next line down
8a91: bd 00 15 lda HIRES_ADDR_LO,x ;set up hi-res row address
8a94: 85 87 sta ]src_ptr
8a96: bd 00 16 lda HIRES_ADDR_HI,x
8a99: 09 20 ora #$20 ;always hi-res page 1
8a9b: 85 88 sta ]src_ptr+1
8a9d: a0 20 ldy #32 ;viewport is 32 columns wide
8a9f: b1 87 :CopyCols lda (]src_ptr),y
8aa1: 91 89 sta (]dst_ptr),y
8aa3: 88 dey
8aa4: d0 f9 bne :CopyCols
8aa6: 20 09 a2 jsr PlayShutOpBeep_jmp
8aa9: a5 87 :UpdatePtr lda ]src_ptr ;dst_ptr = src_ptr
8aab: 85 89 sta ]dst_ptr
8aad: a5 88 lda ]src_ptr+1
8aaf: 85 8a sta ]dst_ptr+1
8ab1: e4 86 cpx ]counter ;done yet?
8ab3: 90 db bcc :CopyRows ;no, keep going
; Copy a row of non-shutter graphics from page 2.
8ab5: bd 00 16 lda HIRES_ADDR_HI,x
8ab8: 09 40 ora #$40
8aba: 85 8a sta ]dst_ptr+1
8abc: a0 20 ldy #$20
8abe: b1 89 :CopyLoop lda (]dst_ptr),y
8ac0: 91 87 sta (]src_ptr),y
8ac2: 88 dey
8ac3: d0 f9 bne :CopyLoop
8ac5: 20 cd 8b jsr ShutterOpenSound
8ac8: 20 ae 8b jsr FlashSystemName
8acb: c6 86 dec ]counter
8acd: a5 86 lda ]counter
8acf: c9 21 cmp #33 ;reached the top of the viewport?
8ad1: b0 ac bcs :CopyShutter ;not yet
8ad3: ad 84 85 lda system_num
8ad6: 4c e5 80 jmp PrintSystemName
;
; Draws the closing shutter while warping out.
;
; On entry:
; $86: shutter position
;
]hptr .var $87 {addr/2}
8ad9: a6 86 DrawShutter ldx ]counter
8adb: 20 55 8b jsr :ClearLineHi
8ade: 20 0c a2 jsr PlaySound1_jmp
8ae1: ca dex
8ae2: e0 21 cpx #$21
8ae4: b0 01 bcs L8AE7
8ae6: 60 rts
8ae7: 20 7f 8b L8AE7 jsr :DrawSolidEdge
8aea: 20 0c a2 jsr PlaySound1_jmp
8aed: 38 sec
8aee: a5 86 lda ]counter
8af0: e9 0f sbc #$0f
8af2: 4c 00 8b jmp L8B00
8af5: aa L8AF5 tax
8af6: 20 8d 8b jsr :DrawSolidBar
8af9: 20 0c a2 jsr PlaySound1_jmp
8afc: 38 sec
8afd: 8a txa
8afe: e9 0c sbc #$0c
8b00: c9 21 L8B00 cmp #$21
8b02: b0 f1 bcs L8AF5
8b04: 38 sec
8b05: a5 86 lda ]counter
8b07: e9 09 sbc #$09
8b09: 4c 17 8b jmp L8B17
8b0c: aa L8B0C tax
8b0d: 20 62 8b jsr :DrawEmptyBar
8b10: 20 0c a2 jsr PlaySound1_jmp
8b13: 38 sec
8b14: 8a txa
8b15: e9 0c sbc #$0c
8b17: c9 21 L8B17 cmp #$21
8b19: b0 f1 bcs L8B0C
8b1b: 38 sec
8b1c: a5 86 lda ]counter
8b1e: e9 0b sbc #$0b
8b20: 4c 2c 8b jmp L8B2C
8b23: a0 00 L8B23 ldy #$00
8b25: 20 3d 8b jsr :DrawPerfEdge
8b28: 38 sec
8b29: 8a txa
8b2a: e9 0a sbc #$0a
8b2c: aa L8B2C tax
8b2d: e0 21 cpx #$21
8b2f: 90 0b bcc L8B3C
8b31: a0 01 ldy #$01
8b33: 20 3d 8b jsr :DrawPerfEdge
8b36: ca dex
8b37: ca dex
8b38: e0 21 cpx #$21
8b3a: b0 e7 bcs L8B23
8b3c: 60 L8B3C rts
; Draw perforated shutter edge.
8b3d: 20 a1 8b :DrawPerfEdge jsr :GetHiResAddr
8b40: b9 51 8b lda :left_edge,y
8b43: 48 pha
8b44: b9 53 8b lda :right_edge,y
8b47: a0 20 ldy #$20
8b49: 91 87 sta (]hptr),y
8b4b: 68 pla
8b4c: a0 01 ldy #$01
8b4e: 91 87 sta (]hptr),y
8b50: 60 rts
8b51: aa :left_edge .dd1 $aa
8b52: 8a .dd1 $8a
8b53: 95 :right_edge .dd1 $95
8b54: 94 .dd1 $94
; Clears a viewport line to $80 (black with hi bit set). Row in X-reg.
8b55: 20 a1 8b :ClearLineHi jsr :GetHiResAddr
8b58: a9 80 lda #$80
8b5a: a0 20 ldy #$20
8b5c: 91 87 :Loop sta (]hptr),y
8b5e: 88 dey
8b5f: d0 fb bne :Loop
8b61: 60 rts
8b62: 20 a1 8b :DrawEmptyBar jsr :GetHiResAddr
8b65: e6 87 inc ]hptr
8b67: e6 87 inc ]hptr
8b69: a0 1d ldy #$1d
8b6b: a9 a0 lda #$a0
8b6d: 91 87 sta (]hptr),y
8b6f: a9 80 lda #$80
8b71: 88 dey
8b72: 91 87 :Loop sta (]hptr),y
8b74: 88 dey
8b75: 91 87 sta (]hptr),y
8b77: 88 dey
8b78: d0 f8 bne :Loop
8b7a: a9 81 lda #$81
8b7c: 91 87 sta (]hptr),y
8b7e: 60 rts
8b7f: 20 8d 8b :DrawSolidEdge jsr :DrawSolidBar
8b82: a9 aa lda #$aa
8b84: 91 87 sta (]hptr),y
8b86: a0 1f ldy #$1f
8b88: a9 95 lda #$95
8b8a: 91 87 sta (]hptr),y
8b8c: 60 rts
8b8d: 20 a1 8b :DrawSolidBar jsr :GetHiResAddr
8b90: e6 87 inc ]hptr
8b92: a0 1e ldy #$1e
8b94: a9 aa :Loop lda #$aa
8b96: 91 87 sta (]hptr),y
8b98: 88 dey
8b99: a9 d5 lda #$d5
8b9b: 91 87 sta (]hptr),y
8b9d: 88 dey
8b9e: d0 f4 bne :Loop
8ba0: 60 rts
8ba1: bd 00 15 :GetHiResAddr lda HIRES_ADDR_LO,x
8ba4: 85 87 sta ]hptr
8ba6: bd 00 16 lda HIRES_ADDR_HI,x
8ba9: 09 20 ora #$20
8bab: 85 88 sta ]hptr+1
8bad: 60 rts
;
; Flash the system name.
;
; On entry:
; $80: frame counter for flash
; $8b: system number (1-7, 0/8)
; $8c: display flag ($00/$80)
;
8bae: 20 e5 88 FlashSystemName jsr CheckCtrlKeys
8bb1: c6 80 dec ]sys_flash_ctr ;time to change state?
8bb3: 10 17 bpl :Skip ;not yet
8bb5: a9 05 lda #$05 ;reset
8bb7: 85 80 sta ]sys_flash_ctr
8bb9: a4 8b ldy ]system_num_zp ;system number
8bbb: a5 8c lda ]sys_display_flag ;name draw flag
8bbd: 49 80 eor #$80 ;flip high bit
8bbf: 85 8c sta ]sys_display_flag
8bc1: 8d 0b 03 sta sysnm_flash_flag ;toggle beeping as well
8bc4: 30 02 bmi :NegFlag ;flag is negative, draw system name
8bc6: a0 00 ldy #$00 ;blank out the system name
8bc8: 98 :NegFlag tya
8bc9: 20 e5 80 jsr PrintSystemName
8bcc: 60 :Skip rts
]shutter_posn .var $86 {addr/1}
ShutterOpenSound
8bcd: 38 sec
8bce: a9 bf lda #191 ;small at the bottom, large at the top, to keep the
8bd0: e5 86 sbc ]shutter_posn ; shutter open speed relatively steady
8bd2: 4a lsr A
8bd3: a8 tay ;Y-reg = # of iterations
8bd4: 20 09 a2 jsr PlayShutOpBeep_jmp
8bd7: a2 6c :DelayLoop1 ldx #108
8bd9: ca :DelayLoop2 dex
8bda: d0 fd bne :DelayLoop2
8bdc: 20 09 a2 jsr PlayShutOpBeep_jmp
8bdf: 88 dey
8be0: d0 f5 bne :DelayLoop1
8be2: 60 rts
********************************************************************************
* Do the next frame. This is the primary game update function. *
********************************************************************************
• Clear variables
]move_dir_req .var $00 {addr/1}
]button0 .var $01 {addr/1}
]button1 .var $02 {addr/1}
]last_key .var $03 {addr/1}
]obj_index2 .var $83 {addr/1}
8be3: 20 00 7c UpdateGameState jsr ReadController ;read joystick, check keyboard
8be6: 20 40 a0 jsr UpdateSound
8be9: a5 03 lda ]last_key ;check last key pressed
8beb: 10 47 bpl :NoKey ;no key hit
8bed: c9 c6 cmp #“F” ;was it 'F'?
8bef: d0 0b bne :NotF
8bf1: ad 81 85 lda shots_vis_flag ;yes, flip the shot-visibility flag
8bf4: 49 80 eor #$80
8bf6: 8d 81 85 sta shots_vis_flag
8bf9: 4c 34 8c jmp :NoKey
8bfc: c9 da :NotF cmp #“Z” ;was it 'Z'?
8bfe: d0 0b bne :NotZ
8c00: 38 sec
8c01: a9 10 lda #16 ;zoom = 16 - zoom (toggle)
8c03: ed cc 67 sbc view_zooms
8c06: 8d cc 67 sta view_zooms
8c09: 10 29 bpl :NoKey
8c0b: ae f4 7c :NotZ ldx control_mode
8c0e: f0 04 beq :KbdMode
8c10: c9 a0 cmp #“ ” ;was it spacebar (stealth)?
8c12: f0 04 beq :WasSpace
8c14: c9 8d :KbdMode cmp #$8d ;was it enter? (stealth)
8c16: d0 0d bne :NotEnter
8c18: a9 e1 :WasSpace lda #225 ;engage cloak
8c1a: 8d 38 85 sta gobj_cloak_cooldn
8c1d: a9 01 lda #$01
8c1f: 8d 8f 85 sta cloak_trans_state
8c22: 4c 34 8c jmp :NoKey
8c25: c9 92 :NotEnter cmp #$92 ;Ctrl+R?
8c27: d0 08 bne :NotCtrlR
8c29: a9 80 lda #$80 ;set flag
8c2b: 8d 85 85 sta player_destruct_flag ;to blow ourselves up
8c2e: 4c 34 8c jmp :NoKey
8c31: 20 eb 88 :NotCtrlR jsr CheckCtrlKeys1
; Deal with movement and colliding with other units.
8c34: ad c0 84 :NoKey lda gobj_coll_flags ;did the player run into something?
8c37: 10 15 bpl :NoColl ;no, branch
8c39: ad 38 67 lda obj_speed ;were we moving?
8c3c: f0 10 beq :NoColl ;no, continue
8c3e: ad 50 67 lda obj_facing_adj ;reverse direction
8c41: 49 80 eor #$80
8c43: 8d 50 67 sta obj_facing_adj
8c46: a9 00 lda #$00 ;stop turning
8c48: 8d 68 67 sta obj_rotation_speed
8c4b: 4c 62 8c jmp :SkipMove
8c4e: a6 00 :NoColl ldx ]move_dir_req ;get player's desired move direction
8c50: 20 44 99 jsr SetPlayerMove ;set speed and rotation
8c53: ad c0 84 lda gobj_coll_flags ;check collision
8c56: 30 05 bmi :DidColl ;we did collide, branch
8c58: ad 8c 85 lda collide_last_frame ;did we collide last time?
8c5b: 10 05 bpl :SkipMove ;no, skip speed cut
8c5d: a9 00 :DidColl lda #$00 ;set speed to zero after collision
8c5f: 8d 38 67 sta obj_speed
8c62: ad c0 84 :SkipMove lda gobj_coll_flags ;get collision flag
8c65: 8d 8c 85 sta collide_last_frame ;remember it for next frame
; Check to see if we're trying to cloak or fire the cannon.
8c68: ad f4 7c lda control_mode ;are we in keyboard mode?
8c6b: d0 09 bne :NotKbd ;no, branch
8c6d: a5 03 lda ]last_key
8c6f: c9 a0 cmp #“ ” ;spacebar? (fire)
8c71: f0 21 beq :DoFire ;(if not kbd, spacebar is cloak)
8c73: 4c a6 8c jmp :NotFiring
8c76: a5 02 :NotKbd lda ]button1 ;button 1 pressed? (cloak)
8c78: 10 0f bpl :NotCloak ;no
8c7a: ad 90 85 lda button1_up_flag ;was button1 down last time?
8c7d: 10 0a bpl :NotCloak ;yes, don't refresh cloak continuously
8c7f: a9 e1 lda #225 ;init cloaking duration
8c81: 8d 38 85 sta gobj_cloak_cooldn
8c84: a9 01 lda #$01 ;state = cloak engaging
8c86: 8d 8f 85 sta cloak_trans_state
8c89: a5 02 :NotCloak lda ]button1 ;get button1 state
8c8b: 49 80 eor #$80 ;flip it
8c8d: 8d 90 85 sta button1_up_flag ;true if button1 not pressed
8c90: a5 01 lda ]button0 ;button 0 pressed? (fire)
8c92: 10 12 bpl :NotFiring
8c94: a0 01 :DoFire ldy #$01 ;check if object slot #1 is free
8c96: b9 00 84 lda gobj_types,y
8c99: 30 06 bmi :GotShotSlot ;yup, use it
8c9b: c8 iny ;check object slot #2
8c9c: b9 00 84 lda gobj_types,y
8c9f: 10 05 bpl :NotFiring ;both full, can't fire yet
8ca1: a2 00 :GotShotSlot ldx #$00 ;player is unit index 0
8ca3: 20 64 94 jsr FireWeapon
; Handle flashing of newly-spawned units. We can ignore the player and the
; player's shots (slots 0/1/2).
8ca6: a2 03 :NotFiring ldx #$03 ;start with slot 3
8ca8: bd 00 84 :FlashLoop lda gobj_types,x ;get the type
8cab: c9 01 cmp #$01 ;ignore < 1
8cad: 90 13 bcc :NoFlash
8caf: c9 19 cmp #$19 ;ignore explosions
8cb1: b0 0f bcs :NoFlash
8cb3: bc 30 84 ldy gobj_vis_flash,x ;check for flash on new-spawned units
8cb6: f0 0a beq :NoFlash ;nope
8cb8: de 30 84 dec gobj_vis_flash,x ;update counter
8cbb: 88 dey ;update Y-reg to match
8cbc: b9 66 86 lda new_unit_flash_pat,y ;get on/off pattern
8cbf: 9d 98 67 sta obj_visible_flag,x ;set visibility
8cc2: e8 :NoFlash inx
8cc3: e0 18 cpx #c_num_objects
8cc5: 90 e1 bcc :FlashLoop
; Update the state of the cloaking device, and redraw some items on the screen.
8cc7: 20 c9 9a jsr UpdateCloaking
8cca: 20 40 a0 jsr UpdateSound
8ccd: 20 00 1c jsr UPD_DRAW_OBJ_HUD
8cd0: ad 48 84 lda unit_armor_remaining ;hit by a shell or laser?
8cd3: f0 03 beq :NoInvert
8cd5: 20 43 a4 jsr CallInvertViewport ;yes, invert the viewport
8cd8: 20 d0 65 :NoInvert jsr SwapBuffers
8cdb: a9 00 lda #$00 ;reset collision flag
8cdd: 8d c0 84 sta gobj_coll_flags
8ce0: 8d 48 84 sta unit_armor_remaining ;reset damage taken this frame
; Convert the unit's altitude (0-200) to a small number (0-3). This is done for
; fliers and bombs like the Seeker (which, as far as I know, does not fly).
8ce3: a2 17 ldx #c_num_objects-1 ;start with the highest-numbered object
8ce5: bd 00 84 :HeightLoop lda gobj_types,x ;get the type
8ce8: 30 28 bmi :Cont ;skip empty slots
8cea: a9 00 lda #$00
8cec: 9d c0 84 sta gobj_coll_flags,x ;clear the collision flags
8cef: bd 18 84 lda gobj_move_class,x ;get the movement class
8cf2: c9 03 cmp #$03 ;< 3 (player / ground vehicle / turret)?
8cf4: 90 1c bcc :Cont ;yes, branch
8cf6: c9 05 cmp #$05 ;>= 5 (projectile / immobile / explosion)?
8cf8: b0 18 bcs :Cont ;yes, branch
8cfa: bd a8 66 lda obj_yc_lo,x ;get unit's altitude
8cfd: a0 00 ldy #$00 ;use 0 for ground level
8cff: c9 00 cmp #$00
8d01: f0 0b beq L8D0E
8d03: c8 iny
8d04: c9 4b cmp #75 ;1 for height < 75
8d06: 90 06 bcc L8D0E
8d08: c8 iny
8d09: c9 c8 cmp #200 ;2 for height < 200
8d0b: 90 01 bcc L8D0E
8d0d: c8 iny ;3 for height >= 200 (fliers are at 200)
8d0e: 98 L8D0E tya
8d0f: 9d 08 85 sta gobj_coll_height,x ;save it off
8d12: ca :Cont dex
8d13: d0 d0 bne :HeightLoop
8d15: 20 40 a0 jsr UpdateSound
; Check for collisions between pairs of objects. A handler will be called that
; processes projectile impacts and such.
]coll_obj0 .var $8e {addr/1}
]coll_obj1 .var $8f {addr/1}
8d18: a9 00 lda #$00 ;start with the player
8d1a: 85 82 sta g_obj_index
8d1c: a6 82 :CollOtrLoop ldx g_obj_index
8d1e: 20 53 96 jsr CheckCollIntr ;is this object interesting?
8d21: 10 32 bpl :SkipCollChk ;no, branch
8d23: e8 inx ;start by comparing to N+1
8d24: 86 83 stx ]obj_index2 ;(we compared with 0..N-1 on previous iterations)
8d26: a6 83 :CollInrLoop ldx ]obj_index2
8d28: 20 53 96 jsr CheckCollIntr ;is this object interesting?
8d2b: 10 1d bpl :SkipCollChk1 ;no, branch
8d2d: a4 82 ldy g_obj_index ;put first index into Y-reg
8d2f: 20 83 1e jsr DETECT_COLLISION ;check to see if they collide
8d32: 90 16 bcc :SkipCollChk1 ;no collision, move on to the next
8d34: a5 82 lda g_obj_index
8d36: 85 8e sta ]coll_obj0 ;set up args 0/1
8d38: a5 83 lda ]obj_index2
8d3a: 85 8f sta ]coll_obj1
8d3c: 20 6e 96 jsr HandleCollision ;call the handler
8d3f: a5 83 lda ]obj_index2 ;flip args
8d41: 85 8e sta ]coll_obj0
8d43: a5 82 lda g_obj_index
8d45: 85 8f sta ]coll_obj1
8d47: 20 6e 96 jsr HandleCollision ;call the handler again
8d4a: e6 83 :SkipCollChk1 inc ]obj_index2 ;advance to next object
8d4c: a5 83 lda ]obj_index2
8d4e: c9 18 cmp #c_num_objects ;done?
8d50: 90 d4 bcc :CollInrLoop ;no, keep going
8d52: 20 40 a0 jsr UpdateSound
8d55: e6 82 :SkipCollChk inc g_obj_index ;advance to next object
8d57: a5 82 lda g_obj_index
8d59: c9 17 cmp #c_num_objects-1 ;done? (note we stop one short)
8d5b: 90 bf bcc :CollOtrLoop ;no, keep going
; This appears to destroy objects that the player has collided with 5x in a row.
; I tried to ram a Sandsled but the collision code causes the player to bounce
; off, so you're not constantly colliding. Perhaps this is a hack to free the
; player if they somehow get stuck?
8d5d: ad c0 84 lda gobj_coll_flags ;did the player collide with something?
8d60: 10 28 bpl :ResetCounter ;no, reset counter
8d62: ad 38 67 lda obj_speed ;is the player in motion?
8d65: f0 23 beq :ResetCounter ;no, reset counter
8d67: ee 8d 85 inc player_coll_ctr
8d6a: ad 8d 85 lda player_coll_ctr ;check counter
8d6d: c9 05 cmp #$05 ;collided 5x in a row?
8d6f: 90 1e bcc FindDead ;not yet
8d71: a0 04 ldy #$04
8d73: b9 c0 84 :Loop lda gobj_coll_flags,y ;check all enemy units
8d76: c9 81 cmp #$81 ;is this a ground unit that collided with player?
8d78: d0 05 bne L8D7F ;no, branch
8d7a: a9 00 lda #$00 ;yes, destroy the unit
8d7c: 99 48 84 sta unit_armor_remaining,y
8d7f: c8 L8D7F iny
8d80: c0 18 cpy #c_num_objects
8d82: 90 ef bcc :Loop
8d84: ce 8d 85 dec player_coll_ctr ;decr so we can fire again next update
8d87: 4c 8f 8d jmp FindDead
• Clear variables
]kill_points .var $8e {addr/2}
8d8a: a9 00 :ResetCounter lda #$00 ;reset player continuous-collision counter
8d8c: 8d 8d 85 sta player_coll_ctr
; Identify any units that have been killed.
8d8f: a9 17 FindDead lda #c_num_objects-1 ;start with the highest-numbered object
8d91: 85 82 sta g_obj_index
8d93: a6 82 :UnitLoop ldx g_obj_index
8d95: bd 00 84 lda gobj_types,x ;get type
8d98: 10 03 bpl :IsActive
8d9a: 4c 76 8e jmp :UnitLoopBttm
8d9d: bd 18 84 :IsActive lda gobj_move_class,x ;get the movement class
8da0: c9 01 cmp #$01 ;interested in 1-7 (enemy units and projectiles)
8da2: 90 0b bcc :NotDead
8da4: c9 08 cmp #$08
8da6: b0 07 bcs :NotDead
8da8: bd 48 84 lda unit_armor_remaining,x ;check armor (hit points)
8dab: 30 05 bmi :IsDead ;if <= 0, it's dead
8dad: f0 03 beq :IsDead
8daf: 4c 4b 8e :NotDead jmp :UnitAlive
8db2: bd 18 84 :IsDead lda gobj_move_class,x ;check to see if this unit is worth points
8db5: c9 05 cmp #$05 ;projectile?
8db7: b0 72 bcs :NoScore
8db9: ee 88 85 inc num_kills ;bump kill count
8dbc: 20 60 a1 jsr EnemyKillSound
; Update score.
8dbf: bc 00 84 ldy gobj_types,x ;get unit type
8dc2: a9 00 lda #$00 ;init 16-bit kill score to zero
8dc4: 85 8e sta ]kill_points
8dc6: 85 8f sta ]kill_points+1
8dc8: 18 clc
8dc9: f8 sed ;BCD addition
8dca: ae 84 85 ldx system_num ;score values are multiplied by the system number
8dcd: a5 8e :MultScore lda ]kill_points
8dcf: 79 18 86 adc unit_score_bcd,y ;add unit score
8dd2: 85 8e sta ]kill_points
8dd4: a5 8f lda ]kill_points+1
8dd6: 69 00 adc #$00
8dd8: 85 8f sta ]kill_points+1
8dda: ca dex
8ddb: d0 f0 bne :MultScore
8ddd: d8 cld
8dde: a5 8e lda ]kill_points ;load into A-reg/X-reg
8de0: a6 8f ldx ]kill_points+1
8de2: 20 53 7e jsr UpdateScore ;add kill score to total score
8de5: a6 82 ldx g_obj_index
8de7: bd 00 84 lda gobj_types,x ;get the type of the unit that was killed
8dea: c9 07 cmp #$07 ;Gir Draxon?
8dec: d0 39 bne :NotGir ;no, skip
; Gir Draxon is dead. This awards 2000 points and sets the "level tour" flag,
; which ends the level after a certain number of frames.
8dee: a2 04 ldx #$04
8df0: bc 00 84 :Loop ldy gobj_types,x
8df3: c0 0f cpy #$0f
8df5: b0 17 bcs L8E0E
8df7: a9 19 lda #$19
8df9: 9d 00 84 sta gobj_types,x
8dfc: a9 09 lda #$09
8dfe: 9d 18 84 sta gobj_move_class,x
8e01: a9 00 lda #$00
8e03: 9d 48 84 sta unit_armor_remaining,x
8e06: a9 80 lda #$80
8e08: 9d 80 67 sta obj_immob_flag,x
8e0b: 9d 98 67 sta obj_visible_flag,x
8e0e: e8 L8E0E inx
8e0f: e0 18 cpx #c_num_objects
8e11: 90 dd bcc :Loop
8e13: a9 00 lda #$00
8e15: 8d 68 85 sta nospawn_kill_count
8e18: 8d 89 85 sta frame_count ;zero the frame count
8e1b: a9 80 lda #$80 ;enable level tour
8e1d: 8d 87 85 sta level_tour_flag
8e20: a9 00 lda #$00 ;add 2000 points
8e22: a2 20 ldx #$20
8e24: 20 53 7e jsr UpdateScore
; Create an appropriate impact or explosion animation.
8e27: a0 19 :NotGir ldy #$19 ;meta-type for explosion
8e29: d0 08 bne :GotMetaType ;(always)
8e2b: a0 1a :NoScore ldy #$1a ;meta type for impact
8e2d: c9 05 cmp #$05 ;was it a projectile?
8e2f: f0 02 beq :GotMetaType ;yes, branch
8e31: a0 1b ldy #$1b ;meta type for impact (same as $1a)
8e33: 98 :GotMetaType tya
8e34: a6 82 ldx g_obj_index
8e36: 9d 00 84 sta gobj_types,x ;set type to $19/1a/1b
8e39: a9 80 lda #$80 ;mark immobile and visible
8e3b: 9d 80 67 sta obj_immob_flag,x
8e3e: 9d 98 67 sta obj_visible_flag,x
8e41: a9 09 lda #$09 ;move class 9 (explosion)
8e43: 9d 18 84 sta gobj_move_class,x
8e46: a9 00 lda #$00 ;set animation counter to zero
8e48: 9d 48 84 sta unit_armor_remaining,x
8e4b: bd 18 84 :UnitAlive lda gobj_move_class,x ;get move class
8e4e: c9 09 cmp #$09 ;is it an explosion?
8e50: d0 24 bne :UnitLoopBttm ;no, skip the next bit
; Update explosion/impact animations. Each is 8 frames. We use the "armor
; remaining" field to store the frame number.
8e52: fe 48 84 inc unit_armor_remaining,x ;set armor to 1, for use as counter
8e55: bc 00 84 ldy gobj_types,x ;get object type (19/1a/1b)
8e58: bd 48 84 lda unit_armor_remaining,x ;get counter
8e5b: d9 2a 86 cmp explos_frame_cnt-25,y ;animation done?
8e5e: 90 0c bcc :StillExploding ;not yet
8e60: a9 80 lda #$80 ;yes, clear the slot
8e62: 9d 00 84 sta gobj_types,x
8e65: a9 00 lda #$00
8e67: 9d 00 66 sta obj_alive_flag,x
8e6a: 10 0a bpl :UnitLoopBttm ;(always)
8e6c: 18 :StillExploding clc ;advance explosion animation
8e6d: bd 48 84 lda unit_armor_remaining,x ;get animation frame # (1-8)
8e70: 79 2d 86 adc explos_base_type-25,y ;add the base type
8e73: 9d 18 66 sta obj_type,x ;store as object type
; Bottom of unit death / explosion update loop.
8e76: c6 82 :UnitLoopBttm dec g_obj_index
8e78: f0 03 beq :Projectiles
8e7a: 4c 93 8d jmp :UnitLoop
8e7d: 20 40 a0 :Projectiles jsr UpdateSound
; Update projectile lifetimes (move class 5/6/7).
8e80: a2 17 ldx #c_num_objects-1
8e82: bc 00 84 :ProjLoop ldy gobj_types,x ;get object type
8e85: 30 20 bmi :Next ;object slot not active, branch
8e87: bd 18 84 lda gobj_move_class,x ;get movement class
8e8a: c9 05 cmp #$05 ;classes 5/6/7 are projectiles
8e8c: 90 19 bcc :Next
8e8e: c9 08 cmp #$08
8e90: b0 15 bcs :Next
8e92: b9 50 86 lda projectile_speeds-15,y ;look up speed by unit type
8e95: 9d 38 67 sta obj_speed,x ;replace speed
8e98: de 78 84 dec gobj_shot1_ttl,x
8e9b: d0 0a bne :Next
8e9d: a9 80 lda #$80
8e9f: 9d 00 84 sta gobj_types,x
8ea2: a9 00 lda #$00
8ea4: 9d 00 66 sta obj_alive_flag,x
8ea7: ca :Next dex
8ea8: d0 d8 bne :ProjLoop
; Decrement shot TTLs for move classes 0/1/2/3. Note these are decoupled from
; projectile lifetimes. This doesn't really matter except in rare situations,
; e.g. all projectiles are removed when the player touches a Fuelbay, but the
; unit TTLs are not reset.
8eaa: a2 17 ldx #c_num_objects-1
8eac: bd 18 84 :ShotLoop lda gobj_move_class,x
8eaf: c9 04 cmp #$04 ;units w/class >= 4 don't shoot projectiles
8eb1: b0 26 bcs :NoProj ; so skip the next bit
8eb3: bd 60 84 lda gobj_shot0_ttl,x ;shot 0 in flight?
8eb6: f0 05 beq :NoShot0 ;no, check next one
8eb8: 30 17 bmi :NoShot2 ;no, and not available to this unit, so we're done
8eba: de 60 84 dec gobj_shot0_ttl,x ;decrement time-to-live
8ebd: bd 78 84 :NoShot0 lda gobj_shot1_ttl,x ;repeat for shot 1
8ec0: f0 05 beq :NoShot1
8ec2: 30 0d bmi :NoShot2
8ec4: de 78 84 dec gobj_shot1_ttl,x
8ec7: bd 90 84 :NoShot1 lda gobj_shot2_ttl,x ;repeat for shot 2
8eca: f0 05 beq :NoShot2
8ecc: 30 03 bmi :NoShot2
8ece: de 90 84 dec gobj_shot2_ttl,x
8ed1: bd a8 84 :NoShot2 lda gobj_shot_cooldn,x ;check shot cooldown
8ed4: f0 03 beq :NoProj
8ed6: de a8 84 dec gobj_shot_cooldn,x
8ed9: ca :NoProj dex
8eda: 10 d0 bpl :ShotLoop
8edc: 20 40 a0 jsr UpdateSound
; Spawn a new enemy, if needed.
8edf: ad 88 85 lda num_kills ;check how many things we've killed
8ee2: cd 68 85 cmp nospawn_kill_count ;reached the limit?
8ee5: b0 4a bcs :NoGen ;yes, stop running up the score
8ee7: ad 89 85 lda frame_count
8eea: 29 0f and #$0f ;only try to generate every 16th frame
8eec: d0 43 bne :NoGen
; Set Y-reg to the number of active enemy units.
8eee: a0 00 ldy #$00
8ef0: a2 04 ldx #$04
8ef2: bd 00 84 :FindUnitLoop lda gobj_types,x ;scan list of active units
8ef5: c9 01 cmp #$01 ;< sandsled?
8ef7: 90 05 bcc :NotEnemy
8ef9: c9 0f cmp #$0f ;>= projectile?
8efb: b0 01 bcs :NotEnemy
8efd: c8 iny ;increase enemy unit count
8efe: e8 :NotEnemy inx
8eff: e0 18 cpx #c_num_objects
8f01: 90 ef bcc :FindUnitLoop
; Get a random number, and compare it to the value from the spawn likelihood
; table in DYN#. If it's >=, don't spawn this frame.
8f03: 20 18 99 jsr GetRandom ;get random number
8f06: d9 50 85 cmp spawn_likelihood,y ;how likely are we to spawn something this frame?
8f09: b0 26 bcs :NoGen
; We've decided to spawn something. The table at $8559 has one nonzero entry
; for each type of unit that can spawn on this level. We generate a random
; number between 1 and 255, and if the table value is greater or equal we spawn
; that unit.
8f0b: 20 18 99 :ReRand jsr GetRandom ;get random number
8f0e: c9 00 cmp #$00 ;if zero, re-roll
8f10: f0 f9 beq :ReRand
8f12: a2 01 ldx #$01 ;start at entry #1
8f14: dd 59 85 :ChkLoop cmp unit_spawn_chance,x ;check if number is <= chance
8f17: 90 06 bcc :SpawnX
8f19: f0 04 beq :SpawnX
8f1b: e8 inx ;no, check next entry
8f1c: 4c 14 8f jmp :ChkLoop
8f1f: 20 39 97 :SpawnX jsr FindEmptySlot ;find an empty slot for this unit
8f22: 8a txa
8f23: 99 00 84 sta gobj_types,y ;set the object type
8f26: 84 82 sty g_obj_index
8f28: 20 40 a0 jsr UpdateSound
8f2b: 20 78 97 jsr SetObjectDefaults ;configure default values
8f2e: 20 46 97 jsr PlaceObjectOnMap ;add it to the map
8f31: 20 00 a0 :NoGen jsr SoundStop
; Move units. The primary determinant of unit action is the movement class.
8f34: a9 04 lda #$04 ;skip player / shot0 / shot1 / Warplink slots
8f36: 85 82 sta g_obj_index
8f38: a6 82 :Loop ldx g_obj_index
8f3a: bd 00 84 lda gobj_types,x ;get type
8f3d: 30 21 bmi :DoNext ;$80 is no unit
8f3f: bd 30 84 lda gobj_vis_flash,x ;is it just arriving?
8f42: d0 1c bne :DoNext ;yes, don't move while flashing in
8f44: bc 18 84 ldy gobj_move_class,x ;get movement class
8f47: c0 01 cpy #$01
8f49: 90 15 bcc :DoNext ;< 1 is player, do nothing
8f4b: c0 05 cpy #$05
8f4d: b0 11 bcs :DoNext ;>= 5 is projectile or immobile
8f4f: 88 dey ;Y-reg now 0-3
8f50: b9 6e 8f lda :jmp_addr_lo,y ;get subroutine address
8f53: 85 87 sta jmp_tmp
8f55: b9 72 8f lda :jmp_addr_hi,y
8f58: 85 88 sta jmp_tmp+1
8f5a: 20 6b 8f jsr :CallFunc ;call it
8f5d: 20 10 a0 jsr SoundHalf
8f60: e6 82 :DoNext inc g_obj_index
8f62: a5 82 lda g_obj_index
8f64: c9 18 cmp #c_num_objects
8f66: 90 d0 bcc :Loop
8f68: 4c 14 93 jmp UpdatePlayerStuff
8f6b: 6c 87 00 :CallFunc jmp (jmp_tmp)
8f6e: 76 :jmp_addr_lo .dd1 <Move1_Ground
8f6f: 15 .dd1 <Move2_Turret
8f70: f3 .dd1 <Move3_Flier
8f71: e2 .dd1 <Move4_Special
8f72: 8f :jmp_addr_hi .dd1 >Move1_Ground
8f73: 91 .dd1 >Move2_Turret
8f74: 91 .dd1 >Move3_Flier
8f75: 92 .dd1 >Move4_Special
;
; Movement logic for class 1 (ground units).
;
; On entry:
; X-reg: unit index
;
8f76: bd 00 84 Move1_Ground lda gobj_types,x ;get object type
8f79: c9 03 cmp #$03 ;Hovercraft?
8f7b: d0 10 bne :NotHover
8f7d: 8a txa ;start with the index, so they bounce out of sync
8f7e: 4d 89 85 eor frame_count ;xor the frame count
8f81: 29 01 and #$01 ;keep only the low bit
8f83: a8 tay
8f84: b9 3b 87 lda hover_bounce_height,y ;get height
8f87: 9d a8 66 sta obj_yc_lo,x ;set the object Y coordinate
8f8a: 4c a8 8f jmp :SkipCloak
8f8d: c9 06 :NotHover cmp #$06 ;Stalker or Gir Draxon?
8f8f: 90 17 bcc :SkipCloak ;no, branch
8f91: de 38 85 dec gobj_cloak_cooldn,x ;update the cooldown
8f94: d0 12 bne :SkipCloak ;not zero, no change this frame
8f96: a0 32 ldy #50 ;cloak for 50 frames
8f98: bd 98 67 lda obj_visible_flag,x ;toggle cloak status
8f9b: 49 80 eor #$80
8f9d: 9d 98 67 sta obj_visible_flag,x
8fa0: 10 02 bpl :NotVis ;not visible, branch
8fa2: a0 64 ldy #100 ;de-cloaked; change cooldown to 100 frames
8fa4: 98 :NotVis tya
8fa5: 9d 38 85 sta gobj_cloak_cooldn,x ;update the cooldown
;
8fa8: bd c0 84 :SkipCloak lda gobj_coll_flags,x ;did we have a movement-stopping collision?
8fab: 10 09 bpl :NoColl ;no
8fad: c9 80 cmp #$80 ;was it something other than the player?
8faf: d0 1f bne :DidColl ;no, handle player collision
8fb1: bd 38 67 lda obj_speed,x ;were we moving?
8fb4: d0 1a bne :DidColl ;yes, branch
; No collision, or something ran into us while we were parked.
8fb6: bd d8 84 :NoColl lda gobj_move_pattern,x ;get the movement pattern
8fb9: 10 03 bpl :GotPat ;we have a pattern, implement it
8fbb: 20 2f 96 jsr GetRandMove1Pat ;pick a pattern at random
; Jump to pattern-specific movement code.
8fbe: a6 82 :GotPat ldx g_obj_index
8fc0: bc d8 84 ldy gobj_move_pattern,x
8fc3: b9 0b 90 lda :jmp_addr_lo,y
8fc6: 85 87 sta jmp_tmp
8fc8: b9 10 90 lda :jmp_addr_hi,y
8fcb: 85 88 sta jmp_tmp+1
8fcd: 6c 87 00 jmp (jmp_tmp)
; Collided with player, or something drove into us.
8fd0: bd 38 67 :DidColl lda obj_speed,x ;were we moving?
8fd3: d0 0b bne :WasMove ;yes, branch
8fd5: a9 01 lda #$01 ;no, player drove into us
8fd7: 9d d8 84 sta gobj_move_pattern,x ;switch to pattern 1
8fda: 20 ea 93 jsr RotateUnitToPlayer
8fdd: 4c 3d 94 jmp CheckFireWeapon
8fe0: a9 80 :WasMove lda #$80
8fe2: 9d f0 84 sta gobj_mvpat_cooldn,x
8fe5: a0 04 ldy #$04 ;switch to pattern 4
8fe7: bd 50 67 lda obj_facing_adj,x ;reverse direction
8fea: 49 80 eor #$80
8fec: 9d 50 67 sta obj_facing_adj,x
8fef: f0 02 beq L8FF3 ;now moving forward
8ff1: a0 03 ldy #$03 ;now moving backward, use pattern 3
8ff3: 98 L8FF3 tya
8ff4: 9d d8 84 sta gobj_move_pattern,x ;update movement pattern
8ff7: bd c0 84 lda gobj_coll_flags,x ;check the flags
8ffa: c9 81 cmp #$81 ;did we collide with player?
8ffc: d0 0c bne :Return ;no, done
8ffe: 20 18 99 jsr GetRandom
9001: c9 aa cmp #$aa ;1 in 3 chance of sticking with pattern 3/4
9003: b0 05 bcs :Return
9005: a9 01 lda #$01 ;switch to pattern 1
9007: 9d d8 84 sta gobj_move_pattern,x
900a: 60 :Return rts
900b: 15 :jmp_addr_lo .dd1 <Move1_p0
900c: 35 .dd1 <Move1_p1
900d: 4e .dd1 <Move1_p2
900e: 6a .dd1 <Move1_p3
900f: 02 .dd1 <Move1_p4
9010: 90 :jmp_addr_hi .dd1 >Move1_p0
9011: 90 .dd1 >Move1_p1
9012: 90 .dd1 >Move1_p2
9013: 90 .dd1 >Move1_p3
9014: 91 .dd1 >Move1_p4
; Pattern 0: drive forward while turning in a random direction for 0-63 frames.
9015: bd f0 84 Move1_p0 lda gobj_mvpat_cooldn,x ;check the cooldown
9018: 10 10 bpl :Update ;not newly set
901a: a9 00 lda #$00
901c: 9d f0 84 sta gobj_mvpat_cooldn,x ;set cooldown to zero
901f: 9d 50 67 sta obj_facing_adj,x ;drive forward
9022: 20 cb 93 jsr MaxFwdSpeed ;max speed
9025: a9 3f lda #$3f ;rotate in random direction for 0-63 frames
9027: 20 f7 93 jsr SetRandomRotation
902a: a6 82 :Update ldx g_obj_index ;get object index
902c: de 20 85 dec gobj_move_counter,x ;done with rotation?
902f: d0 03 bne :Return ;not yet
9031: 4c 2f 96 jmp GetRandMove1Pat ;yes, pick a new pattern
9034: 60 :Return rts
; Pattern 1: sit in place, rotate toward player, shoot.
9035: a9 00 Move1_p1 lda #$00 ;set speed to zero
9037: 9d 38 67 sta obj_speed,x
903a: 20 d5 93 jsr RotToPlyrShoot ;turn toward player
903d: a6 82 ldx g_obj_index ;get object index
903f: 20 18 99 jsr GetRandom
9042: bc 00 84 ldy gobj_types,x ;get object type
9045: d9 d3 86 cmp mc1_p1_chance,y ;check chance of breaking out of pattern
9048: b0 03 bcs :Return ;random is >= chance, keep going
904a: 4c 2f 96 jmp GetRandMove1Pat ;pick a new pattern
904d: 60 :Return rts
; Pattern 2: aggressively pursue player.
904e: a9 00 Move1_p2 lda #$00
9050: 9d 50 67 sta obj_facing_adj,x ;drive forward
9053: 20 cb 93 jsr MaxFwdSpeed ;at max speed
9056: 20 d5 93 jsr RotToPlyrShoot ;turn toward player
9059: a6 82 ldx g_obj_index
905b: 20 18 99 jsr GetRandom
905e: bc 00 84 ldy gobj_types,x
9061: d9 db 86 cmp mc1_p2_chance,y ;check chance of breaking out of pattern
9064: b0 03 bcs :Return
9066: 4c 2f 96 jmp GetRandMove1Pat ;pick a new pattern
9069: 60 :Return rts
; Pattern 3: table-driven behavior with some randomness.
;
; This is not assigned by the "choose random pattern" code.
906a: bd f0 84 Move1_p3 lda gobj_mvpat_cooldn,x ;check cooldown
906d: 10 12 bpl :Update ;not new
906f: 20 18 99 jsr GetRandom
9072: 29 07 and #$07
9074: 9d f0 84 sta gobj_mvpat_cooldn,x ;set the cooldown to 0-7
9077: a9 03 lda #$03
9079: 9d 08 85 sta gobj_coll_height,x ;use height field as high bits
907c: a9 01 lda #$01
907e: 9d 20 85 sta gobj_move_counter,x
9081: de 20 85 :Update dec gobj_move_counter,x
9084: f0 01 beq :CheckDone
9086: 60 rts
9087: de 08 85 :CheckDone dec gobj_coll_height,x ;decrement the counter
908a: 10 03 bpl :Update ;still going
908c: 4c 2f 96 jmp GetRandMove1Pat ;pick a new pattern
908f: bd 08 85 :Update lda gobj_coll_height,x ;value is 0-2
9092: 0a asl A
9093: 0a asl A
9094: 0a asl A ;now 0-16
9095: 7d f0 84 adc gobj_mvpat_cooldn,x ;random 0-7, yielding 0-23
9098: a8 tay
9099: b9 f3 86 lda mc1_p3_count,y ;number of frames
909c: 9d 20 85 sta gobj_move_counter,x
909f: a9 00 lda #$00 ;drive forward
90a1: 9d 50 67 sta obj_facing_adj,x
90a4: be 0b 87 ldx mc1_p3_movedir,y ;0/1/2
90a7: f0 1e beq :SetSpeed ;0 == don't move
90a9: e0 01 cpx #$01
90ab: d0 0c bne :Rev ;1 == reverse
90ad: a6 82 ldx g_obj_index ;2 == forward
90af: bd 00 84 lda gobj_types,x
90b2: aa tax
90b3: bd af 85 lda unit_fwd_speed,x ;get speed
90b6: 4c c7 90 jmp :SetSpeed
90b9: a6 82 :Rev ldx g_obj_index
90bb: a9 80 lda #$80 ;reverse
90bd: 9d 50 67 sta obj_facing_adj,x
90c0: bd 00 84 lda gobj_types,x
90c3: aa tax
90c4: bd be 85 lda unit_rev_speed,x ;get speed
90c7: a6 82 :SetSpeed ldx g_obj_index
90c9: 9d 38 67 sta obj_speed,x
90cc: a9 00 lda #$00
90ce: be 23 87 ldx mc1_p3_rotation,y ;0/1/2/3
90d1: f0 29 beq :SetRotation ;0 == no rotation
90d3: e0 01 cpx #$01
90d5: d0 17 bne :DoTurn
90d7: a6 82 ldx g_obj_index ;1 == random rotation
90d9: bc 00 84 ldy gobj_types,x
90dc: be cd 85 ldx unit_turn_rate,y
90df: 20 18 99 jsr GetRandom
90e2: 0a asl A
90e3: 8a txa
90e4: 90 16 bcc :SetRotation
90e6: 49 ff eor #$ff
90e8: 18 clc
90e9: 69 01 adc #$01
90eb: 4c fc 90 jmp :SetRotation
90ee: a4 82 :DoTurn ldy g_obj_index
90f0: b9 68 67 lda obj_rotation_speed,y
90f3: e0 02 cpx #$02
90f5: f0 05 beq :SetRotation ;2 == turn left
90f7: 49 ff eor #$ff ;3 == turn right
90f9: 18 clc
90fa: 69 01 adc #$01
90fc: a6 82 :SetRotation ldx g_obj_index
90fe: 9d 68 67 sta obj_rotation_speed,x
9101: 60 rts
; Pattern 4: move forward, random rotation for 0-15 frames.
9102: a9 00 Move1_p4 lda #$00
9104: 9d f0 84 sta gobj_mvpat_cooldn,x ;set pattern and cooldown to zero
9107: 9d d8 84 sta gobj_move_pattern,x
910a: 9d 50 67 sta obj_facing_adj,x ;move forward
910d: 20 cb 93 jsr MaxFwdSpeed ;at max speed
9110: a9 0f lda #$0f ;mask for random number 0-15
9112: 4c f7 93 jmp SetRandomRotation
;
; Movement logic for class 2 (turrets).
;
; On entry:
; X-reg: unit index
;
9115: bd 00 84 Move2_Turret lda gobj_types,x ;get object type
9118: c9 08 cmp #$08 ;is it a Laser Battery?
911a: d0 03 bne :NotLaser
911c: 4c d5 93 jmp RotToPlyrShoot
911f: c9 09 :NotLaser cmp #$09 ;is it a Gun Battery?
9121: d0 0c bne MovePulsar ;no, must be a Pulsar
9123: 20 48 95 jsr CheckRngToPlayer ;are they in range?
9126: 10 06 bpl :Return ;no, nothing to do
9128: 20 ea 93 jsr RotateUnitToPlayer ;rotate toward player...
912b: 4c 3d 94 jmp CheckFireWeapon ;...and fire when the shot is lined up
912e: 60 :Return rts
; Update a Pulsar.
;
; Pulsars, like all spinners, rotate at a randomly-chosen speed (see $97f6),
; which can be +/- 1 to 8.
]obj_index .var $09 {addr/1}
912f: bd d8 84 MovePulsar lda gobj_move_pattern,x ;do we have a pattern?
9132: 10 05 bpl :HavePat ;yes, do it
9134: a9 00 lda #$00 ;set to pattern 0
9136: 9d d8 84 sta gobj_move_pattern,x
9139: c9 00 :HavePat cmp #$00 ;using pattern 0?
913b: d0 46 bne :Not0 ;no, branch
;
; Pulsar pattern 1: fire at the player if in range and lined up.
913d: a5 82 lda g_obj_index
913f: 85 09 sta ]obj_index
9141: 20 80 82 jsr AngleToPlayer ;get angle to player
9144: 20 10 a0 jsr SoundHalf
; Find the closest 90-degree facing. For example, if the target is at 200
; degrees, the closest would be 180.
9147: a6 82 ldx g_obj_index ;get object index
9149: bd d8 66 lda obj_facing,x ;get current facing
914c: 18 :FindAngle clc
914d: 69 40 adc #$40 ;rotate 90 degrees
914f: a8 tay ;preserve angle
9150: 38 sec
9151: e5 0a sbc target_angle ;compare to target angle
9153: 20 33 94 jsr ComputeAbs ;get absolute value
9156: c9 21 cmp #$21 ;is this the closest 90-degree facing?
9158: 98 tya ;restore angle
9159: b0 f1 bcs :FindAngle
915b: 9d d8 66 sta obj_facing,x ;set the facing to that angle
; Fire at the player if possible.
915e: 20 4b 94 jsr CheckTargetAngle ;see if we're lined up
9161: 10 1f bpl :Return ;no, bail
9163: 20 48 95 jsr CheckRngToPlayer ;check range
9166: 10 1a bpl :Return ;too far, bail
9168: a6 82 ldx g_obj_index
916a: a9 01 lda #$01 ;switch to pattern 1
916c: 9d d8 84 sta gobj_move_pattern,x
916f: bd 68 67 lda obj_rotation_speed,x ;+/- 1-8
9172: 20 33 94 jsr ComputeAbs ;get absolute value
9175: a8 tay
9176: b9 8a 86 lda pulsar_p0_count-1,y
9179: 9d 20 85 sta gobj_move_counter,x ;set delay before next shot
917c: 20 39 97 jsr FindEmptySlot
917f: 4c 64 94 jmp FireWeapon
9182: 60 :Return rts
9183: c9 01 :Not0 cmp #$01
9185: d0 2f bne :Not1
; Pulsar pattern 1: fire at current facing.
9187: de 20 85 dec gobj_move_counter,x ;decrement the counter
918a: d0 29 bne :Return ;until it hits zero, do nothing
918c: a9 03 lda #$03 ;switch to pattern 3
918e: 9d d8 84 sta gobj_move_pattern,x
9191: a9 1e lda #30 ;do it for 30 frames
9193: 9d 20 85 sta gobj_move_counter,x
9196: 20 18 99 jsr GetRandom
9199: c9 d2 cmp #$d2 ;18% chance of not doing next part
919b: b0 18 bcs :Return ; (i.e. doing the pattern 3 sit & spin thing)
919d: a9 02 lda #$02 ;switch to pattern 2
919f: 9d d8 84 sta gobj_move_pattern,x
91a2: bd 68 67 lda obj_rotation_speed,x
91a5: 20 33 94 jsr ComputeAbs
91a8: a8 tay
91a9: b9 92 86 lda pulsar_p1_count-1,y
91ac: 9d 20 85 sta gobj_move_counter,x ;set delay before next shot
91af: 20 39 97 jsr FindEmptySlot
91b2: 4c 64 94 jmp FireWeapon ;fire, with or without a target
91b5: 60 :Return rts
91b6: c9 02 :Not1 cmp #$02
91b8: d0 2e bne :Not2
; Pulsar pattern 2: fire 90 degrees off target.
91ba: de 20 85 dec gobj_move_counter,x ;decrement the counter
91bd: d0 28 bne :Return ;until it hits zero, do nothing
91bf: a9 03 lda #$03 ;switch to pattern 3
91c1: 9d d8 84 sta gobj_move_pattern,x
91c4: a9 1e lda #30 ;do it for 30 frames
91c6: 9d 20 85 sta gobj_move_counter,x
91c9: 20 18 99 jsr GetRandom
91cc: c9 a0 cmp #$a0 ;37.5% chance of not doing next part
91ce: b0 17 bcs :Return ; (i.e. doing the pattern 3 sit & spin thing)
91d0: bd 68 67 lda obj_rotation_speed,x
91d3: 0a asl A ;check sign of rotation speed (0=ccw)
91d4: a9 40 lda #$40 ;90 degrees ccw if rotating cw
91d6: b0 02 bcs L91DA
91d8: a9 c0 lda #$c0 ;90 degrees cw if rotating ccw
91da: 18 L91DA clc
91db: 7d d8 66 adc obj_facing,x ;modify facing
91de: 9d d8 66 sta obj_facing,x
91e1: 20 39 97 jsr FindEmptySlot
91e4: 4c 64 94 jmp FireWeapon ;fire, with or without a target
91e7: 60 :Return rts
; Pulsar pattern 3: sit and spin.
91e8: de 20 85 :Not2 dec gobj_move_counter,x
91eb: d0 05 bne :Return
91ed: a9 00 lda #$00 ;switch to pattern 0
91ef: 9d d8 84 sta gobj_move_pattern,x
91f2: 60 :Return rts
;
; Movement logic for class 3 (fliers).
;
; On entry:
; X-reg: unit index
;
91f3: bd d8 84 Move3_Flier lda gobj_move_pattern,x ;do we have a pattern?
91f6: 30 05 bmi L91FD ;not yet
91f8: bd c0 84 lda gobj_coll_flags,x ;did we collide with something?
91fb: 10 0d bpl L920A ;nope
91fd: 20 cb 93 L91FD jsr MaxFwdSpeed ;full speed ahead
9200: a9 05 lda #$05 ;use pattern 5
9202: 9d d8 84 sta gobj_move_pattern,x
9205: a9 00 lda #$00 ;no rotation
9207: 9d 68 67 sta obj_rotation_speed,x
920a: bc d8 84 L920A ldy gobj_move_pattern,x ;get the current movement pattern
920d: b9 1a 92 lda :jmp_addr_lo,y
9210: 85 87 sta jmp_tmp
9212: b9 20 92 lda :jmp_addr_hi,y
9215: 85 88 sta jmp_tmp+1
9217: 6c 87 00 jmp (jmp_tmp) ;jump to the appropriate handler
921a: 26 :jmp_addr_lo .dd1 <Move3_p0
921b: 50 .dd1 <Move3_p1
921c: 62 .dd1 <Move3_p2
921d: 6f .dd1 <Move3_p3
921e: 93 .dd1 <Move3_p4
921f: c3 .dd1 <Move3_p5
9220: 92 :jmp_addr_hi .dd1 >Move3_p0
9221: 92 .dd1 >Move3_p1
9222: 92 .dd1 >Move3_p2
9223: 92 .dd1 >Move3_p3
9224: 92 .dd1 >Move3_p4
9225: 92 .dd1 >Move3_p5
; Pattern 0: fly around, making occasional random turns.
;
; Only used for Stinger, which seems to spend more time airborne than Skimmer.
9226: bd f0 84 Move3_p0 lda gobj_mvpat_cooldn,x ;check cooldown
9229: 10 10 bpl :NotCool
922b: 20 18 99 jsr GetRandom
922e: 29 03 and #$03 ;random 0-3
9230: 18 clc
9231: 69 01 adc #$01 ;1-4
9233: 9d f0 84 sta gobj_mvpat_cooldn,x ;set cooldown
9236: a9 01 lda #$01 ;set counter to 1 so we fall through
9238: 9d 20 85 sta gobj_move_counter,x
923b: de 20 85 :NotCool dec gobj_move_counter,x
923e: d0 0a bne :Return
9240: de f0 84 dec gobj_mvpat_cooldn,x
9243: d0 06 bne :RandomTurn
9245: a9 01 lda #$01 ;switch to pattern 1
9247: 9d d8 84 sta gobj_move_pattern,x
924a: 60 :Return rts
924b: a9 3f :RandomTurn lda #$3f ;0-63 frames
924d: 4c f7 93 jmp SetRandomRotation
; Pattern 1: fly toward player.
9250: 20 ea 93 Move3_p1 jsr RotateUnitToPlayer ;rotate toward player
9253: 20 18 99 jsr GetRandom
9256: c9 08 cmp #$08 ;97% chance of continuing to do this
9258: b0 07 bcs :Return
925a: a6 82 ldx g_obj_index
925c: a9 02 lda #$02 ;switch to pattern 2
925e: 9d d8 84 sta gobj_move_pattern,x
9261: 60 :Return rts
; Pattern 2: descend to ground level.
9262: 20 1a 94 Move3_p2 jsr DiveSeeker ;move toward ground
9265: c9 00 cmp #$00 ;are we there yet?
9267: d0 05 bne :Return ;no, bail
9269: a9 03 lda #$03 ;switch to pattern 3
926b: 9d d8 84 sta gobj_move_pattern,x
926e: 60 :Return rts
; Pattern 3: attack.
926f: 20 ea 93 Move3_p3 jsr RotateUnitToPlayer ;turn toward player
9272: a6 82 ldx g_obj_index
9274: bd 60 84 lda gobj_shot0_ttl,x ;is our projectile still alive?
9277: d0 19 bne :Return ;yes, bail
9279: 20 48 95 jsr CheckRngToPlayer ;player in range?
927c: 10 14 bpl :Return
927e: 20 3d 94 jsr CheckFireWeapon ;if shot is lined up, fire
9281: a6 82 ldx g_obj_index
9283: bd 60 84 lda gobj_shot0_ttl,x ;did we fire?
9286: f0 0a beq :Return ;no, bail
9288: a9 04 lda #$04 ;yes, switch to pattern 4
928a: 9d d8 84 sta gobj_move_pattern,x
928d: a9 80 lda #128 ;keep current movement for 128 frames
928f: 9d f0 84 sta gobj_mvpat_cooldn,x
9292: 60 :Return rts
; Pattern 4: continue moving at ground level after firing, turning occasionally.
9293: bd f0 84 Move3_p4 lda gobj_mvpat_cooldn,x ;check cooldown
9296: 10 16 bpl :NotCool
9298: 20 18 99 jsr GetRandom
929b: 29 03 and #$03 ;random number 0-3
929d: 18 clc
929e: 69 01 adc #$01 ;1-4
92a0: c9 04 cmp #$04 ;is it 4?
92a2: d0 02 bne :Not4
92a4: a9 02 lda #$02 ;set to 2 (distribution 1-2-2-3)
92a6: 9d f0 84 :Not4 sta gobj_mvpat_cooldn,x
92a9: a9 01 lda #$01 ;init counter to 1 so it trips on first iteration
92ab: 9d 20 85 sta gobj_move_counter,x
92ae: de 20 85 :NotCool dec gobj_move_counter,x ;dec the counter
92b1: d0 0a bne :Return
92b3: de f0 84 dec gobj_mvpat_cooldn,x ;dec the cooldown
92b6: d0 06 bne :RandomTurn
92b8: a9 05 lda #$05 ;switch to pattern 5
92ba: 9d d8 84 sta gobj_move_pattern,x
92bd: 60 :Return rts
92be: a9 3f :RandomTurn lda #$3f ;pick random direction and set
92c0: 4c f7 93 jmp SetRandomRotation ; gobj_move_counter
; Pattern 5: climb. Always used after collision with a ground unit.
;
; Collisions don't stop the flier, but do force it to climb.
92c3: 20 01 94 Move3_p5 jsr HandleSeekerColl ;climb
92c6: c9 c8 cmp #200 ;at ceiling?
92c8: d0 17 bne :Return ;not yet
92ca: bc 00 84 ldy gobj_types,x ;get object type
92cd: c0 0b cpy #$0b ;Skimmer?
92cf: d0 06 bne :Stinger ;no, must be a Stinger
92d1: a9 01 lda #$01 ;switch to pattern 1
92d3: 9d d8 84 sta gobj_move_pattern,x
92d6: 60 rts
92d7: a9 00 :Stinger lda #$00 ;switch to pattern 0
92d9: 9d d8 84 sta gobj_move_pattern,x
92dc: a9 80 lda #$80 ;reset cooldown
92de: 9d f0 84 sta gobj_mvpat_cooldn,x
92e1: 60 :Return rts
;
; Movement logic for class 4 (bombs). Guisers don't move, so this is all
; Seeker.
;
; On entry:
; X-reg: unit index
;
92e2: bd 00 84 Move4_Special lda gobj_types,x ;check type
92e5: c9 0e cmp #$0e ;Seeker?
92e7: d0 2a bne :Return ;no, Guiser, nothing to do
92e9: bd c0 84 lda gobj_coll_flags,x ;collided with something?
92ec: 10 0d bpl :NoColl ;no, branch
92ee: bd 08 85 lda gobj_coll_height,x ;airborne?
92f1: c9 03 cmp #$03
92f3: f0 03 beq :Flying ;yes, ignore X/Z collision
92f5: 4c 01 94 jmp HandleSeekerColl
92f8: 4c cb 95 :Flying jmp MoveSeeker
92fb: 20 cb 95 :NoColl jsr MoveSeeker ;set direction and speed to intercept player
92fe: a6 82 ldx g_obj_index
9300: bd 08 85 lda gobj_coll_height,x ;check the object's height
9303: f0 0e beq :Return ;on the ground, nothing to do
9305: c9 03 cmp #$03 ;are we at the ceiling?
9307: d0 07 bne :DoDive ;no, take us down
9309: 20 18 99 jsr GetRandom
930c: c9 f5 cmp #$f5 ;96% chance to fly a little longer
930e: 90 03 bcc :Return ; (unlikely -- need to be at max height)
9310: 4c 1a 94 :DoDive jmp DiveSeeker ;take us down
9313: 60 :Return rts
;
; Updates player stuff (every frame):
; - Checks to see if we're ready to spawn the Warplink or Gir Draxon. Create
; it if so (with some random delay).
; - Reduces fuel according to player movement.
; - Reduces shields according to damage taken.
; - Sets "destroyed" flag if fuel or shields hit zero.
;
UpdatePlayerStuff
9314: ad 86 85 lda warplink_spawn_flag ;already spawned warplink?
9317: 30 58 bmi :CheckTour ;yes, don't do it again
9319: ad 88 85 lda num_kills
931c: cd 6b 85 cmp kills_before_warp ;killed enough?
931f: b0 0f bcs :DoSpawn ;yes, create it
9321: ad 89 85 lda frame_count
9324: cd 69 85 cmp frames_before_warp
9327: ad 8a 85 lda frame_count+1
932a: ed 6a 85 sbc frames_before_warp+1
932d: 4c 71 93 jmp :CheckTour
9330: 20 18 99 :DoSpawn jsr GetRandom ;6.25% chance of warplink spawn per frame
9333: c9 10 cmp #$10
9335: b0 3a bcs :CheckTour
9337: a9 80 lda #$80 ;set the flag so we don't spawn it again
9339: 8d 86 85 sta warplink_spawn_flag
933c: ad 84 85 lda system_num ;are we in Arcturus?
933f: c9 07 cmp #$07
9341: f0 12 beq :InArcturus ;yes, special handling
9343: a9 03 lda #$03
9345: 85 82 sta g_obj_index
9347: a9 18 lda #$18 ;Warplink
9349: 8d 03 84 sta gobj_types+3 ;use reserved slot
934c: 20 78 97 jsr SetObjectDefaults
934f: 20 46 97 jsr PlaceObjectOnMap ;add to map
9352: 4c 71 93 jmp :CheckTour
; In Arcturus, instead of spawning a Warplink, we spawn Gir Draxon.
9355: 20 39 97 :InArcturus jsr FindEmptySlot ;find a slot
9358: c0 17 cpy #c_num_objects-1
935a: f0 10 beq :GirFail
935c: 84 82 sty g_obj_index
935e: a9 07 lda #$07 ;Gir Draxon
9360: 99 00 84 sta gobj_types,y
9363: 20 78 97 jsr SetObjectDefaults
9366: 20 46 97 jsr PlaceObjectOnMap ;add to map
9369: 4c 71 93 jmp :CheckTour
936c: a9 00 :GirFail lda #$00 ;clear the flag, try again later
936e: 8d 86 85 sta warplink_spawn_flag
;
; This happens every frame.
;
9371: ad 87 85 :CheckTour lda level_tour_flag ;are we taking the tour?
9374: 10 13 bpl :NoTour ;no, branch
9376: ad 89 85 lda frame_count ;auto-warplink after 200 frames?
9379: c9 c8 cmp #200
937b: 90 0c bcc :NoTour
937d: a9 80 lda #$80
937f: 8d 8b 85 sta warplink_touch_flag
9382: a9 00 lda #$00 ;add 1000 points
9384: a2 10 ldx #$10
9386: 20 53 7e jsr UpdateScore
9389: a9 02 :NoTour lda #$02 ;default is to reduce fuel by 2
938b: ac 38 67 ldy obj_speed ;get current speed
938e: f0 09 beq L9399 ;not moving, 2 is good
9390: a9 03 lda #$03
9392: cc be 85 cpy unit_rev_speed ;are we moving in reverse?
9395: f0 02 beq L9399 ;yes, reduce fuel by 3
9397: a9 04 lda #$04 ;must be moving forward, reduce fuel by 4
9399: a2 00 L9399 ldx #$00
939b: 20 be 80 jsr ReduceFuel
939e: ad 48 84 lda unit_armor_remaining ;did we take damage this frame?
93a1: f0 03 beq :NoDam ;nope
93a3: 20 40 a1 jsr PlayerHitSound ;yes, sound off
93a6: a9 00 :NoDam lda #$00
93a8: ae 48 84 ldx unit_armor_remaining ;get total damage taken this frame
93ab: 20 7b 80 jsr ReduceShields ;remove it from the shields
93ae: ad fe 81 lda fuel_level+1 ;out of fuel?
93b1: f0 05 beq :Dead ;yes
93b3: ad fc 81 lda shield_level+1 ;out of shields?
93b6: d0 05 bne :Skip ;not yet
93b8: a9 80 :Dead lda #$80 ;set "player destroyed" flag
93ba: 8d 85 85 sta player_destruct_flag
93bd: ee 89 85 :Skip inc frame_count
93c0: d0 03 bne :NoInc
93c2: ee 8a 85 inc frame_count+1
93c5: 20 37 99 :NoInc jsr UpdateRngState
93c8: 4c 40 a0 jmp UpdateSound
;
; Sets the unit's speed to the object's maximum forward speed.
;
93cb: bc 00 84 MaxFwdSpeed ldy gobj_types,x
93ce: b9 af 85 lda unit_fwd_speed,y
93d1: 9d 38 67 sta obj_speed,x
93d4: 60 rts
;
; Rotates a unit toward the player, and fires if lined up and in range.
;
; On entry:
; $82: object index
;
• Clear variables
]obj_index .var $09 {addr/1}
]tmp_8d .var $8d {addr/1}
93d5: a5 82 RotToPlyrShoot lda g_obj_index
93d7: 85 09 sta ]obj_index
93d9: 20 80 82 jsr AngleToPlayer
93dc: 20 10 a0 jsr SoundHalf
93df: 20 8f 95 jsr RotateObjToTarget
93e2: 20 48 95 jsr CheckRngToPlayer
93e5: 10 32 bpl :Return
93e7: 4c 3d 94 jmp CheckFireWeapon
;
; Rotates a unit toward the player.
;
; On entry:
; $82: object index
;
RotateUnitToPlayer
93ea: a5 82 lda g_obj_index
93ec: 85 09 sta ]obj_index
93ee: 20 80 82 jsr AngleToPlayer
93f1: 20 10 a0 jsr SoundHalf
93f4: 4c 8f 95 jmp RotateObjToTarget
;
; Gives the object a random rotation (random speed and direction). It will
; rotate in that direction for a random number of frames, limited by the mask
; value in the A-reg (this sets the move counter).
;
; On entry:
; A-reg: bit mask that limits the number of frames
;
SetRandomRotation
93f7: 85 8d sta ]tmp_8d ;save the mask
93f9: 20 18 99 jsr GetRandom ;get random number
93fc: 25 8d and ]tmp_8d ;mask it
93fe: 4c eb 95 jmp SetRandomRotation1
;
; Handles a collision between a Seeker and another unit. We don't want to get
; hung up on Obstacles, turrets, or immobile units, so we levitate over them.
;
; Also used for fliers that collide with a ground unit.
;
; On exit:
; A-reg: new Y coordinate
;
HandleSeekerColl
9401: bc 00 84 ldy gobj_types,x ;get object type
9404: 18 clc
9405: bd a8 66 lda obj_yc_lo,x ;get the Y coord
9408: 79 be 85 adc unit_rev_speed,y ;use reverse speed as climb rate
940b: 9d a8 66 sta obj_yc_lo,x ;update Y coord
940e: b0 04 bcs L9414 ;on overflow, set to 200
9410: c9 c8 cmp #200 ;did we hit 200 (flight ceiling)?
9412: 90 05 bcc :Return ;not yet
9414: a9 c8 L9414 lda #200 ;cap at 200
9416: 9d a8 66 sta obj_yc_lo,x
9419: 60 :Return rts
;
; Moves the Seeker toward the ground.
;
; On exit:
; A-reg: new Y coordinate
;
941a: bc 00 84 DiveSeeker ldy gobj_types,x ;get object type
941d: 38 sec
941e: bd a8 66 lda obj_yc_lo,x
9421: f9 be 85 sbc unit_rev_speed,y ;use Seeker reverse speed (10) as dive rate
9424: 9d a8 66 sta obj_yc_lo,x
9427: 90 04 bcc L942D
9429: c9 00 cmp #$00
942b: b0 ec bcs :Return
942d: a9 00 L942D lda #$00 ;bottom out at zero
942f: 9d a8 66 sta obj_yc_lo,x
9432: 60 rts
;
; Computes the absolute value of A-reg.
;
9433: c9 80 ComputeAbs cmp #$80 ;value negative?
9435: 90 e2 bcc :Return
9437: 49 ff eor #$ff ;negate
9439: 18 clc
943a: 69 01 adc #$01
943c: 60 rts
;
; Checks whether an enemy unit is pointed at the player and has an object slot
; for the projectile. If all is ready, fire the weapon.
;
; On entry:
; $0a: angle to target
; $82: unit slot number
;
943d: 20 4b 94 CheckFireWeapon jsr CheckTargetAngle ;are we lined up?
9440: 10 08 bpl :Return ;no, don't shot
9442: 20 39 97 jsr FindEmptySlot ;yes, find a slot for projectile, put in Y-reg
9445: a6 82 ldx g_obj_index
9447: 4c 64 94 jmp FireWeapon
944a: 60 :Return rts
;
; Determines whether the difference between two angles is less than 2 angle-
; units.
;
; On entry:
; $0a: target object
; $82: unit index of source object
;
; On exit:
; A-reg/X-reg: $80 if lined up, $00 if not
; Processor flags set for X-reg value
;
CheckTargetAngle
944b: a4 82 ldy g_obj_index
944d: 38 sec
944e: a5 0a lda target_angle ;compare target angle to unit facing
9450: f9 d8 66 sbc obj_facing,y
9453: 10 05 bpl :IsPos
9455: 49 ff eor #$ff
9457: 18 clc
9458: 69 01 adc #$01
945a: a2 80 :IsPos ldx #$80 ;flag set (lined up)
945c: c9 02 cmp #$02 ;are we within 2 rotation units?
945e: 90 02 bcc :SetAndRet ;yes, good to go
9460: a2 00 ldx #$00 ;no, not lined up
9462: 8a :SetAndRet txa ;set flags
9463: 60 rts
;
; Fires a weapon. Used for the player and enemy units.
;
; On entry:
; X-reg: index of firing unit
; Y-reg: index of new slot for projectile
;
9464: bd a8 84 FireWeapon lda gobj_shot_cooldn,x ;ready to fire again?
9467: d0 0f bne :Return ;not yet, even if shot slot is open
9469: bd 60 84 lda gobj_shot0_ttl,x ;check for available projectile
946c: f0 0b beq :ShotAvail
946e: bd 78 84 lda gobj_shot1_ttl,x
9471: f0 06 beq :ShotAvail
9473: bd 90 84 lda gobj_shot2_ttl,x
9476: f0 01 beq :ShotAvail
9478: 60 :Return rts ;can't fire
9479: e0 00 :ShotAvail cpx #$00 ;is this the player?
947b: f0 10 beq :IsPlayer ;yes, branch
947d: 20 18 99 jsr GetRandom ;roll the dice
9480: c9 30 cmp #$30 ;18.75% chance of resetting cooldown
9482: b0 18 bcs :DoFire
9484: 20 18 99 jsr GetRandom ;add 0-31 frames to cooldown
9487: 29 1f and #$1f
9489: 9d a8 84 sta gobj_shot_cooldn,x
948c: 60 rts
; Do some extra stuff when it's the player firing. None of these calls alter
; the contents of Y-reg.
948d: 20 80 a1 :IsPlayer jsr PlayerFireSound ;initiate the noise
9490: a9 19 lda #25
9492: a2 00 ldx #$00
9494: 20 be 80 jsr ReduceFuel ;reduce fuel by 25 units
9497: 20 60 99 jsr SetLastKnownPos
949a: a2 00 ldx #$00 ;set X-reg=0 (index of player obj)
;
]saved_unit_index .var $8d {addr/1}
]y_hi .var $8e {addr/1}
949c: 86 8d :DoFire stx ]saved_unit_index
949e: 20 16 1e jsr COPY_OBJ_STATE ;copy all obj data from X-reg slot to Y-reg slot
94a1: bd 00 84 lda gobj_types,x ;get object type
94a4: aa tax
94a5: bd 91 85 lda unit_weap_type,x ;get type of projectile fired by this unit
94a8: 99 00 84 sta gobj_types,y ;set the projectile object type to that
94ab: aa tax
94ac: bd 27 86 lda unit_move_class,x ;get projectile movement class
94af: 99 18 84 sta gobj_move_class,y ;set in new object
94b2: bd 42 86 lda projectile_ttls-15,x ;first projectile type is $0f
94b5: 99 78 84 sta gobj_shot1_ttl,y ;store TTL in shot1
94b8: a6 8d ldx ]saved_unit_index
94ba: bd 00 84 lda gobj_types,x ;get unit type
94bd: aa tax
; Adjust the projectile's Y coordinate so it comes out of the turret, rather
; than slightly above or below.
94be: bd fa 85 lda unit_weapon_yadj,x ;get turret height adjustment value
94c1: 0a asl A ;sign bit in carry
94c2: a9 ff lda #$ff ;compute sign extension
94c4: 69 00 adc #$00 ;A=$ff for positive, $00 for negative
94c6: 49 ff eor #$ff ;A=$00 for positive, $ff for negative
94c8: 85 8e sta ]y_hi ;save it
94ca: 18 clc
94cb: bd fa 85 lda unit_weapon_yadj,x ;get height adjustment
94ce: a6 8d ldx ]saved_unit_index
94d0: 7d a8 66 adc obj_yc_lo,x ;add to unit Y coord
94d3: 99 a8 66 sta obj_yc_lo,y ;save as projectile Y coord
94d6: a5 8e lda ]y_hi ;repeat for high byte
94d8: 7d c0 66 adc obj_yc_high,x
94db: 99 c0 66 sta obj_yc_high,y
; Set up the rest of the object fields. X-reg is firing unit index, Y-reg is
; projectile index.
94de: a9 00 lda #$00 ;don't need to flash this one into existence
94e0: 99 30 84 sta gobj_vis_flash,y
94e3: a9 01 lda #$01 ;projectiles have 1 HP
94e5: 99 48 84 sta unit_armor_remaining,y
94e8: 8a txa
94e9: 99 60 84 sta gobj_shot0_ttl,y ;store firing unit index in shot0
94ec: 20 fd 1e jsr CLEAR_OBJ_MOVE ;clear speed / facing fields
94ef: a9 7f lda #$7f ;set speed to 127
94f1: 99 38 67 sta obj_speed,y
94f4: b9 00 84 lda gobj_types,y
94f7: 99 18 66 sta obj_type,y
94fa: a9 0f lda #$0f
94fc: 99 30 66 sta obj_max_dim,y ;projectiles always have size=15
94ff: a9 80 lda #$80
9501: 99 98 67 sta obj_visible_flag,y ;mark visible
9504: b9 78 84 lda gobj_shot1_ttl,y ;get TTL (set by $94b5)
9507: bc 60 84 ldy gobj_shot0_ttl,x ;is shot #0 in flight?
950a: d0 05 bne :Shot0Active ;yes, branch
950c: 9d 60 84 sta gobj_shot0_ttl,x ;no, store projectile TTL in unit shot0
950f: f0 0d beq :Cont ;shot #1 wasn't moving, so done
9511: bc 78 84 :Shot0Active ldy gobj_shot1_ttl,x ;is shot #1 in flight?
9514: d0 05 bne :Use2 ;yes, use shot #2
9516: 9d 78 84 sta gobj_shot1_ttl,x ;no, store TTL in shot1
9519: f0 03 beq :Cont
951b: 9d 90 84 :Use2 sta gobj_shot2_ttl,x ;store TTL in shot2
; Set the cooldown so they can't fire again immediately.
951e: bd 00 84 :Cont lda gobj_types,x ;get object type
9521: a8 tay
9522: b9 eb 85 lda unit_shot_cooldn,y ;get cooldown for this type
9525: 9d a8 84 sta gobj_shot_cooldn,x ;store in object
9528: bd 00 84 lda gobj_types,x ;get type
952b: c9 06 cmp #$06 ;Stalker?
952d: f0 04 beq :StealthUnit
952f: c9 07 cmp #$07 ;Gir Draxon?
9531: d0 14 bne :Return
; Stealthed enemies de-cloak when they fire.
9533: bd 98 67 :StealthUnit lda obj_visible_flag,x ;currently visible?
9536: 30 0f bmi :Return ;yes, we're good
9538: a9 80 lda #$80 ;make visible
953a: 9d 98 67 sta obj_visible_flag,x
953d: 38 sec
953e: a9 40 lda #64 ;update cloaking cooldown
9540: fd 38 85 sbc gobj_cloak_cooldn,x ;new = (64 - old) / 2
9543: 4a lsr A
9544: 9d 38 85 sta gobj_cloak_cooldn,x
9547: 60 :Return rts
;
; Checks to see if the player is in range of the unit's weapon. The calculation
; here is pretty sloppy, but perfect accuracy is not important.
;
; On entry:
; $82: unit index
;
; On exit:
; A-reg: bool 00/80 player is in range (flags set)
;
• Clear variables
]tmp_8e .var $8e {addr/1}
]tmp_8f .var $8f {addr/1}
CheckRngToPlayer
9548: a4 82 ldy g_obj_index ;get object index
; Compute distance from player. We simplify the calculation by just computing
; Max(deltaX, deltaZ).
954a: 38 sec
954b: b9 60 66 lda obj_xc_hi,y ;compute difference between player and object
954e: ed 60 66 sbc obj_xc_hi
9551: 85 8f sta ]tmp_8f
9553: 20 7f 95 jsr :Mod4096
9556: 85 8e sta ]tmp_8e ;abs(xc_obj - xc_player), modulo arithmetic
9558: 38 sec ;repeat for Z coord
9559: b9 90 66 lda obj_zc_hi,y
955c: ed 90 66 sbc obj_zc_hi
955f: 85 8f sta ]tmp_8f
9561: 20 7f 95 jsr :Mod4096 ;abs(zc_obj - zc_player), modulo arithmetic
9564: c5 8e cmp ]tmp_8e ;is deltaZ >= deltaX?
9566: b0 02 bcs L956A ;yes, use deltaZ
9568: a5 8e lda ]tmp_8e ;switch to deltaX
956a: 85 8e L956A sta ]tmp_8e
; Compare distance to weapon range.
956c: be 00 84 ldx gobj_types,y ;get object type
956f: bc 91 85 ldy unit_weap_type,x ;get weapon type
9572: b9 49 86 lda projectile_range_hi-15,y ;get weapon range
9575: a2 80 ldx #$80
9577: c5 8e cmp ]tmp_8e ;is range >= distance?
9579: b0 02 bcs :InRange ;yes
957b: a2 00 ldx #$00
957d: 8a :InRange txa ;A-reg = $00 or $80
957e: 60 rts
957f: 29 10 :Mod4096 and #$10 ;sign bit
9581: f0 07 beq L958A
9583: 38 sec ;negative, invert
9584: a9 00 lda #$00
9586: e5 8f sbc ]tmp_8f
9588: 85 8f sta ]tmp_8f
958a: a5 8f L958A lda ]tmp_8f
958c: 29 0f and #$0f
958e: 60 rts
;
; Rotates an object toward a target angle. The amount of rotation is limited by
; the unit's maximum turn rate.
;
; On entry:
; $0a: target angle
; $82: object index
;
• Clear variables
]max_turn_rate .var $90 {addr/1}
RotateObjToTarget
958f: a4 82 ldy g_obj_index ;get object index
9591: be 00 84 ldx gobj_types,y
9594: bd cd 85 lda unit_turn_rate,x ;get max turn rate
9597: 85 90 sta ]max_turn_rate ;save it
9599: 38 sec
959a: a5 0a lda target_angle ;compute (target angle) - (object facing)
959c: f9 d8 66 sbc obj_facing,y
959f: 10 05 bpl :PosDiff
95a1: 49 ff eor #$ff ;result negative, get abs value
95a3: 18 clc
95a4: 69 01 adc #$01
95a6: c5 90 :PosDiff cmp ]max_turn_rate ;compare to max turn rate
95a8: 90 02 bcc :SmallTurn ;small rotation
95aa: a5 90 lda ]max_turn_rate ;limit to max turn rate
95ac: 85 90 :SmallTurn sta ]max_turn_rate ;save Min(required_turn, max_turn)
95ae: a5 0a lda target_angle ;check (target angle) - (object facing) again
95b0: d9 d8 66 cmp obj_facing,y
95b3: 10 07 bpl :IsPos
95b5: 38 sec
95b6: a9 00 lda #$00 ;negate the turn amount
95b8: e5 90 sbc ]max_turn_rate
95ba: 85 90 sta ]max_turn_rate
95bc: 18 :IsPos clc ;update the object's facing
95bd: b9 d8 66 lda obj_facing,y
95c0: 65 90 adc ]max_turn_rate
95c2: 99 d8 66 sta obj_facing,y
95c5: a9 00 lda #$00 ;do rotation here, not in the graphics engine
95c7: 99 68 67 sta obj_rotation_speed,y
95ca: 60 rts
;
; Sets a Seeker's direction and speed to move toward the player.
;
95cb: a5 82 MoveSeeker lda g_obj_index ;get object index
95cd: 85 09 sta $09
95cf: 20 80 82 jsr AngleToPlayer ;compute angle to player
95d2: 20 10 a0 jsr SoundHalf
95d5: a6 82 ldx g_obj_index ;get object index
95d7: 38 sec
95d8: a5 0a lda target_angle ;get angle to target
95da: fd d8 66 sbc obj_facing,x ;subtract current facing
95dd: 38 sec
95de: fd 68 67 sbc obj_rotation_speed,x ;adjust by spin speed
95e1: 9d 50 67 sta obj_facing_adj,x ;set the facing adjustment
95e4: ad bd 85 lda unit_fwd_speed+14 ;Seeker speed
95e7: 9d 38 67 sta obj_speed,x ;move toward player
95ea: 60 rts
;
; Sets the object's rotation to a random value, between 0 and its maximum
; rotation speed.
;
; On entry:
; A-reg: random value
;
SetRandomRotation1
95eb: a4 82 ldy g_obj_index ;get object index
95ed: 18 clc
95ee: 69 01 adc #$01 ;increment A-reg (so it's at least 1)
95f0: 69 00 adc #$00 ;if we rolled over to zero, inc again
95f2: 99 20 85 sta gobj_move_counter,y ;save it
95f5: 20 18 99 jsr GetRandom
95f8: c9 5a cmp #$5a ;35% chance of staying
95fa: b0 06 bcs L9602 ;no, branch
95fc: a9 00 lda #$00 ;set rotation to zero
95fe: 99 68 67 sta obj_rotation_speed,y
9601: 60 rts
9602: be 00 84 L9602 ldx gobj_types,y ;get object type
9605: bd cd 85 lda unit_turn_rate,x ;get turn rate
9608: 85 90 sta ]max_turn_rate
960a: 20 18 99 jsr GetRandom ;get random number
960d: 29 07 and #$07 ; between 0 and 7
960f: c5 90 :Loop cmp ]max_turn_rate ;is it < turn rate?
9611: 90 05 bcc L9618 ;yes, use it
9613: e5 90 sbc ]max_turn_rate ;no, use (random - turn_rate)
9615: 4c 0f 96 jmp :Loop ;loop until < turn rate
9618: 18 L9618 clc
9619: 69 01 adc #$01 ;add 1 (so we're not standing still)
961b: 99 68 67 sta obj_rotation_speed,y ;set the rotation speed
961e: 20 18 99 jsr GetRandom
9621: c9 80 cmp #$80 ;50% chance of being done
9623: 90 09 bcc :Return
9625: 38 sec ;rotate the opposite direction
9626: a9 00 lda #$00
9628: f9 68 67 sbc obj_rotation_speed,y
962b: 99 68 67 sta obj_rotation_speed,y
962e: 60 :Return rts
;
; Picks a random movement pattern for a unit in movement class 1 (ground units).
; The object's movement pattern will be set to {0,1,2}, and the pattern cooldown
; set to $80.
;
; On entry:
; $82: object index
;
962f: a4 82 GetRandMove1Pat ldy g_obj_index ;get object index
9631: be 00 84 ldx gobj_types,y ;get object type
9634: 20 18 99 jsr GetRandom ;generate random number
9637: a0 00 ldy #$00
9639: dd e3 86 cmp mc1_choice0_chances,x ;compare to table value
963c: 90 09 bcc L9647 ;use pattern 0
963e: a0 01 ldy #$01
9640: dd eb 86 cmp mc1_choice1_chances,x
9643: 90 02 bcc L9647 ;use pattern 1
9645: a0 02 ldy #$02 ;use pattern 2
9647: a6 82 L9647 ldx g_obj_index
9649: 98 tya
964a: 9d d8 84 sta gobj_move_pattern,x
964d: a9 80 lda #$80 ;init the cooldown
964f: 9d f0 84 sta gobj_mvpat_cooldn,x
9652: 60 rts
;
; Checks to see if a collision with this object is "interesting". Explosions
; and fliers at or above height 2 are not interesting.
;
; On entry:
; X-reg: object index
;
; On exit:
; A-reg: bool 00/80: is interesting
; (flags set according to A-reg)
;
9653: bd 00 84 CheckCollIntr lda gobj_types,x ;get the object type
9656: c9 19 cmp #$19 ;is it one of the explosion meta-types?
9658: b0 11 bcs :ReturnSkip
965a: bd 18 84 lda gobj_move_class,x ;get the movement class
965d: c9 03 cmp #$03 ;is it a flier?
965f: d0 07 bne :ReturnDo ;no, it's interesting
9661: bd 08 85 lda gobj_coll_height,x ;yes, check current altitude
9664: c9 02 cmp #$02
9666: b0 03 bcs :ReturnSkip ;too high, skip it
9668: a9 80 :ReturnDo lda #$80
966a: 60 rts
966b: a9 00 :ReturnSkip lda #$00
966d: 60 rts
;
; Handles a collision between objects. One or both objects may be in motion.
;
; The arguments are not symmetric. However, the function is always called
; twice, with the arguments swapped on the second call.
;
; This function does not prevent objects from moving through each other. It's
; more about handling projectiles.
;
; On entry:
; $8e: index of first object
; $8f: index of second object
;
• Clear variables
]tmp .var $8d {addr/1}
]unit_index0 .var $8e {addr/1}
]unit_index1 .var $8f {addr/1}
966e: a4 8f HandleCollision ldy ]unit_index1
9670: be 18 84 ldx gobj_move_class,y ;get move class for second unit
9673: bd 98 96 lda magic_bits1,x ;get some bits
9676: 85 8d sta ]tmp
9678: a4 8e ldy ]unit_index0
967a: be 18 84 ldx gobj_move_class,y ;get move class for first unit
967d: bd a1 96 lda magic_bits0,x ;get some bits
9680: 18 clc
9681: 65 8d adc ]tmp ;combine them (could ORA?)
9683: aa tax
9684: bc 9b 86 ldy unit_coll_hnd_index,x ;get index of handler function
9687: b9 aa 96 lda unit_coll_hnd_lo,y ;set up jump to address of handler function
968a: 85 87 sta jmp_tmp
968c: b9 b5 96 lda unit_coll_hnd_hi,y
968f: 85 88 sta jmp_tmp+1
9691: a4 8f ldy ]unit_index1 ;load unit indices into X-reg/Y-reg
9693: a6 8e ldx ]unit_index0
9695: 6c 87 00 jmp (jmp_tmp)
;
; Bit patterns for collision handler lookup, indexed by movement class (0-8).
;
; The tables aren't fully NxM because some collisions are impossible. Two
; Obstacles cannot collide, and while a Sandsled can drive into an Obstacle, the
; opposite is not true. Enemy projectiles don't hurt enemies.
;
; Movement classes:
; 0 - player
; 1 - ground vehicle
; 2 - turret
; 3 - flier
; 4 - bomb
; 5 - laser projectile
; 6 - cannon projectile
; 7 - player cannon projectile
; 8 - immobile (Obstacle / Fuelbay / Warplink)
; 9 - explosion and impact animations (not included in table)
;
; Map:
;
; Y0 1 2 3 4 5 6 7 8
; X -----------------
; 0| 0 9 0 1 5 a a a 7
; 1| 1 1 0 1 1 a a a 0
; 2| 1 1 0 1 1 a a a 0
; 3| 0 0 0 0 0 a a a 0
; 4| 3 2 0 2 5 8 8 8 0
; 5| 3 0 0 0 0 0 0 0 0
; 6| 3 0 0 0 0 0 0 0 0
; 7| 0 4 4 4 6 0 0 0 0
; 8| 0 1 0 1 1 5 5 5 0
9698: 00 08 10 18+ magic_bits1 .bulk $00,$08,$10,$18,$20,$28,$28,$28,$30
96a1: 00 01 02 03+ magic_bits0 .bulk $00,$01,$02,$03,$04,$05,$05,$06,$07
; Collision handlers.
unit_coll_hnd_lo
96aa: c8 .dd1 <Coll00_None
96ab: c0 .dd1 <Coll01
96ac: c9 .dd1 <Coll02
96ad: d1 .dd1 <Coll03
96ae: e4 .dd1 <Coll04
96af: ef .dd1 <Coll05
96b0: f5 .dd1 <Coll06
96b1: fd .dd1 <Coll07
96b2: 23 .dd1 <Coll08
96b3: 2b .dd1 <Coll09
96b4: 31 .dd1 <Coll0a
unit_coll_hnd_hi
96b5: 96 .dd1 >Coll00_None
96b6: 96 .dd1 >Coll01
96b7: 96 .dd1 >Coll02
96b8: 96 .dd1 >Coll03
96b9: 96 .dd1 >Coll04
96ba: 96 .dd1 >Coll05
96bb: 96 .dd1 >Coll06
96bc: 96 .dd1 >Coll07
96bd: 97 .dd1 >Coll08
96be: 97 .dd1 >Coll09
96bf: 97 .dd1 >Coll0a
; Handles:
; - Various enemy units running into various other enemy units.
;
; Sets a flag for the movement code to see.
96c0: b9 c0 84 Coll01 lda gobj_coll_flags,y ;set the "collided" flag
96c3: 09 80 ora #$80
96c5: 99 c0 84 sta gobj_coll_flags,y
96c8: 60 Coll00_None rts
; Handles:
; - Ground unit or flier collided with Seeker.
; (X-reg=Seeker, Y-reg=other unit)
96c9: bd 08 85 Coll02 lda gobj_coll_height,x ;check seeker's height
96cc: c9 02 cmp #$02 ;airborne?
96ce: 90 f0 bcc Coll01 ;no, they bumped
96d0: 60 rts
; Handles:
; - Player struck by projectile or bomb.
; (X-reg=projectile/bomb, Y-reg=0)
;
; Adds damage from impact/explosion to amount sustained this frame.
96d1: bd 00 84 Coll03 lda gobj_types,x ;get projectile type
96d4: aa tax
96d5: bd 3c 86 lda projectile_dmgs-13,x ;get damage caused
96d8: 18 clc
96d9: 79 48 84 adc unit_armor_remaining,y ;add to damage sustained this frame
96dc: 90 02 bcc :NoRoll
96de: a9 ff lda #$ff ;rolled over, max out at 255
96e0: 99 48 84 :NoRoll sta unit_armor_remaining,y
96e3: 60 rts
; Handles:
; - Ground unit, turret, flier hit by player cannon.
; (X-reg=0, Y-reg=unit hit)
96e4: 98 Coll04 tya ;transfer unit index to X-reg
96e5: aa tax
96e6: de 48 84 dec unit_armor_remaining,x ;reduce armor on target
96e9: 10 03 bpl :NotNeg
96eb: fe 48 84 inc unit_armor_remaining,x ;don't let it go negative (is this possible?)
96ee: 60 :NotNeg rts
; Handles:
; - Projectile collision with stationary object.
; (X-reg=stationary object, Y-reg=projectile)
; - Seeker/Guiser collision with player or Seeker/Guiser.
; (X-reg=player or seeker/guiser, Y-reg=seeker/guiser)
;
; Sets Y-reg's armor to zero to indicate projectile/bomb has been destroyed.
96ef: a9 00 Coll05 lda #$00
96f1: 99 48 84 sta unit_armor_remaining,y
96f4: 60 rts
; Handles:
; - Seeker or Guiser struck by shot from player's cannon.
; (X-reg=0, Y-reg=unit hit)
96f5: b9 08 85 Coll06 lda gobj_coll_height,y ;check the Seeker's height
96f8: c9 02 cmp #$02 ;is it sufficiently airborne?
96fa: 90 e8 bcc Coll04 ;no, it's a hit
96fc: 60 rts
; Handles:
; - Player running into stationary object (Obstacle, Fuelbay, Warplink).
; (X-reg=0, Y-reg=object)
96fd: b9 00 84 Coll07 lda gobj_types,y
9700: c9 17 cmp #$17 ;Fuelbay
9702: d0 03 bne :NotFuelbay
9704: 4c 3b 98 jmp HandleFuelbay
9707: c9 18 :NotFuelbay cmp #$18 ;Warplink
9709: d0 12 bne :NotWarplink
; The player drove into a warplink.
970b: a9 80 lda #$80
970d: 8d 8b 85 sta warplink_touch_flag
9710: 20 03 a2 jsr PlaySound3_jmp
9713: a2 1e ldx #30
9715: 20 5f 80 jsr IncreaseShields ;A-reg is $00 or $80, but it's the low byte
9718: a2 1e ldx #30 ; so not too important
971a: 4c a2 80 jmp IncreaseFuel
971d: a9 80 :NotWarplink lda #$80
971f: 8d c0 84 sta gobj_coll_flags
9722: 60 rts
; Handles:
; - Seeker/Guiser collision with projectile.
; (X-reg=Seeker/Guiser, Y-reg=projectile)
9723: bd 08 85 Coll08 lda gobj_coll_height,x ;check the Seeker's height
9726: c9 02 cmp #$02 ;is it 0/1?
9728: 90 c5 bcc Coll05 ;yes, collision with projectile is possible
972a: 60 rts ;no, projectile passed under us
; Handles:
; - Player and ground unit colliding.
; (X-reg=player, Y-reg=ground unit)
972b: a9 81 Coll09 lda #$81 ;collision ($80) with player ($01)
972d: 99 c0 84 sta gobj_coll_flags,y
9730: 60 rts
; Handles:
; - Projectile colliding with player or ground/turret/flier.
; (X-reg=unit, Y-reg=projectile)
9731: b9 60 84 Coll0a lda gobj_shot0_ttl,y ;in a projectile, the shot 0 TTL counter holds the
9734: c5 8e cmp ]unit_index0 ; index of the unit that fired it
9736: d0 b7 bne Coll05 ;collided with different unit, handle collision
9738: 60 rts
;
; Finds an empty slot in which to place a new object.
;
; Slots 0-3 are reserved (0 is player, 1-2 are player shots, 3 is warplink).
;
; On exit:
; Y-reg: slot index
; X-reg preserved
;
9739: a0 03 FindEmptySlot ldy #$03 ;start in slot 4
973b: c8 :Loop iny
973c: c0 17 cpy #c_num_objects-1
973e: f0 05 beq :Return
9740: b9 00 84 lda gobj_types,y ;check unit type
9743: 10 f6 bpl :Loop ;$80 is an empty slot, so keep looking if >= 0
9745: 60 :Return rts
;
; When a new unit is spawned, pick a random location and facing.
;
; On entry:
; $82: object index
;
PlaceObjectOnMap
9746: a4 82 ldy g_obj_index
9748: a9 00 lda #$00
974a: 99 48 66 sta obj_xc_lo,y
974d: 99 78 66 sta obj_zc_lo,y
9750: 20 18 99 jsr GetRandom ;face a random direction
9753: 99 d8 66 sta obj_facing,y
9756: 20 06 99 :PickPosn jsr GetRndMapCoord ;pick a random location
9759: 99 60 66 sta obj_xc_hi,y
975c: 20 06 99 jsr GetRndMapCoord
975f: 99 90 66 sta obj_zc_hi,y
9762: a2 17 ldx #c_num_objects-1
9764: e4 82 :CheckLoop cpx g_obj_index ;is this entry us?
9766: f0 0a beq :Skip ;yes, don't check for collision with ourselves
9768: bd 00 84 lda gobj_types,x ;object here?
976b: 30 05 bmi :Skip
976d: 20 83 1e jsr DETECT_COLLISION ;check object in X-reg vs. object in Y-reg
9770: b0 e4 bcs :PickPosn
9772: ca :Skip dex
9773: 10 ef bpl :CheckLoop
9775: 4c 40 a0 jmp UpdateSound
;
; Set default values for a newly-created game object.
;
; On entry:
; $82: object index
;
SetObjectDefaults
9778: a4 82 ldy g_obj_index
977a: be 00 84 ldx gobj_types,y ;get object type
977d: 8a txa
977e: 99 18 66 sta obj_type,y ;save in obj state
9781: a9 80 lda #$80
9783: 99 00 66 sta obj_alive_flag,y ;mark as alive
9786: 99 98 67 sta obj_visible_flag,y ;mark as visible
9789: 20 fd 1e jsr CLEAR_OBJ_MOVE ;init movement state
978c: a9 00 lda #$00
978e: 99 80 67 sta obj_immob_flag,y ;not immobile
9791: 99 a8 66 sta obj_yc_lo,y ;resting on the ground
9794: 99 c0 66 sta obj_yc_high,y
9797: bd 27 86 lda unit_move_class,x
979a: 99 18 84 sta gobj_move_class,y ;set movement class
979d: a9 13 lda #$13 ;flash units when they appear
979f: 99 30 84 sta gobj_vis_flash,y
97a2: a9 00 lda #$00 ;clear collision flags
97a4: 99 c0 84 sta gobj_coll_flags,y
97a7: a9 64 lda #100 ;don't toggle cloak for first 100 frames
97a9: 99 38 85 sta gobj_cloak_cooldn,y
97ac: a9 80 lda #$80 ;initial movement pattern
97ae: 99 d8 84 sta gobj_move_pattern,y
97b1: e0 16 cpx #$16 ;< Obstacle?
97b3: 90 20 bcc :UnitDef ;yes, branch
97b5: e0 19 cpx #$19 ;> WarpLink?
97b7: b0 1c bcs :UnitDef ;yes, branch
97b9: a9 55 lda #$55 ;obstacle / fuelbay / warplink
97bb: 99 30 66 sta obj_max_dim,y ;set size
97be: a9 01 lda #$01 ;give them nonzero armor
97c0: 99 48 84 sta unit_armor_remaining,y ;(exact value irrelevant, they don't take damage)
97c3: e0 18 cpx #$18 ;warplink?
97c5: d0 0b bne :Done ;no, we're done
97c7: a9 50 lda #$50 ;shrink the dimension slightly
97c9: 99 30 66 sta obj_max_dim,y
97cc: ad 6c 85 lda warplink_rot_speed ;set the rotation speed with per-level value
97cf: 99 68 67 sta obj_rotation_speed,y
97d2: 4c 20 a0 :Done jmp SoundTwice
97d5: bd 09 86 :UnitDef lda unit_max_dim,x
97d8: 99 30 66 sta obj_max_dim,y ;set max dimension
97db: bd a0 85 lda unit_armor_thck,x
97de: 99 48 84 sta unit_armor_remaining,y ;set armor remaining
97e1: e0 0b cpx #$0b ;Skimmer?
97e3: f0 04 beq :Airborne
97e5: e0 0c cpx #$0c ;Stinger?
97e7: d0 05 bne :Cont1
97e9: a9 c8 :Airborne lda #200
97eb: 99 a8 66 sta obj_yc_lo,y ;airborne units start in the air
97ee: e0 0a :Cont1 cpx #$0a ;Pulsar?
97f0: f0 04 beq :Spinner
97f2: e0 0e cpx #$0e ;Seeker?
97f4: d0 19 bne :Cont2
97f6: 20 18 99 :Spinner jsr GetRandom ;spin unit at random speed
97f9: 29 07 and #$07 ;0-7
97fb: 18 clc
97fc: 69 01 adc #$01 ;1-8
97fe: 99 68 67 sta obj_rotation_speed,y
9801: 20 18 99 jsr GetRandom ;50/50 chance to spin in opposite direction
9804: 10 09 bpl :Cont2
9806: 38 sec ;invert rotation speed
9807: a9 00 lda #$00
9809: f9 68 67 sbc obj_rotation_speed,y
980c: 99 68 67 sta obj_rotation_speed,y
980f: a9 80 :Cont2 lda #$80 ;init all 3 shots to $80 (unavailable)
9811: 99 60 84 sta gobj_shot0_ttl,y
9814: 99 78 84 sta gobj_shot1_ttl,y
9817: 99 90 84 sta gobj_shot2_ttl,y
981a: bd dc 85 lda unit_shots_rnd,x ;get max simultaneous shots
981d: aa tax
981e: a9 00 lda #$00 ;zero out first N entries to mark them as available
9820: e0 00 cpx #$00
9822: f0 11 beq L9835
9824: 99 60 84 sta gobj_shot0_ttl,y
9827: e0 01 cpx #$01
9829: f0 0a beq L9835
982b: 99 78 84 sta gobj_shot1_ttl,y
982e: e0 02 cpx #$02
9830: f0 03 beq L9835
9832: 99 90 84 sta gobj_shot2_ttl,y
9835: a9 12 L9835 lda #$12
9837: 99 a8 84 sta gobj_shot_cooldn,y
983a: 60 rts
;
; Player bumped into a Fuelbay.
;
; On entry:
; $8f: index of Fuelbay object
;
983b: 20 e0 a1 HandleFuelbay jsr InitSound00 ;stop sounds
983e: a0 17 ldy #c_num_objects-1
9840: b9 18 84 :RemoveProj lda gobj_move_class,y ;for each object, get move class
9843: c9 05 cmp #$05 ;vehicle or bomb?
9845: 90 0e bcc :Next ;ignore
9847: c9 08 cmp #$08 ;immobile object or explosion?
9849: b0 0a bcs :Next ;ignore
984b: a9 00 lda #$00 ;that leaves projectiles (laser, cannon, player)
984d: 99 00 66 sta obj_alive_flag,y ;remove them all from the board
9850: a9 80 lda #$80
9852: 99 00 84 sta gobj_types,y
9855: 88 :Next dey
9856: d0 e8 bne :RemoveProj
; Remove the Fuelbay as well.
9858: a4 8f ldy ]unit_index1
985a: a9 00 lda #$00
985c: 99 00 66 sta obj_alive_flag,y
985f: a9 80 lda #$80
9861: 99 00 84 sta gobj_types,y
; Draw a grid, and animate by page-flipping while raising the fuel/shield
; gauges.
9864: 20 ad 88 jsr DrawHudPage12 ;make sure both pages show the same HUD gfx
9867: 20 b8 98 jsr DrawRefuelGrid ;draw the grid on one page
986a: 20 d0 65 jsr SwapBuffers ;flip to it
986d: 20 e5 88 :RefuelLoop jsr CheckCtrlKeys
9870: a2 01 ldx #$01 ;1.25 units at a time
9872: a9 40 lda #$40
9874: 20 5f 80 jsr IncreaseShields
9877: a2 01 ldx #$01 ;add $140 units until we're full
9879: a9 40 lda #$40
987b: 20 a2 80 jsr IncreaseFuel
987e: a9 00 lda #$00 ;don't draw target reticle
9880: 20 00 7e jsr DrawHudItems ;draw fuel/shield gauges (+ other stuff)
9883: 20 a0 98 jsr PlayRisingSound
9886: 20 d0 65 jsr SwapBuffers
9889: ad fc 81 lda shield_level+1 ;max shields?
988c: c9 92 cmp #$92
988e: 90 dd bcc :RefuelLoop ;not yet
9890: ad fe 81 lda fuel_level+1 ;max fuel?
9893: c9 92 cmp #$92
9895: 90 d6 bcc :RefuelLoop ;not yet
9897: a9 ff lda #$ff ;full tank is $92ff
9899: 8d fb 81 sta shield_level ;make sure they're topped off
989c: 8d fd 81 sta fuel_level
989f: 60 rts
98a0: ad fe 81 PlayRisingSound lda fuel_level+1 ;compare levels
98a3: cd fc 81 cmp shield_level+1 ;fuel < shield?
98a6: 90 03 bcc :UseFuel ;yes, use fuel value
98a8: ad fc 81 lda shield_level+1 ;no, use shield value
98ab: 85 8d :UseFuel sta ]tmp ;multiply by 5
98ad: 4a lsr A
98ae: 4a lsr A
98af: 65 8d adc ]tmp
98b1: e9 dc sbc #$dc ;do this other thing
98b3: 49 ff eor #$ff ;value changes the sound pitch
98b5: 4c 00 a2 jmp PlayRefuelSound_jmp
• Clear variables
]x0 .var $00 {addr/1}
]y0 .var $01 {addr/1}
]x1 .var $02 {addr/1}
]y1 .var $03 {addr/1}
]index .var $85 {addr/1}
98b8: ad f0 65 DrawRefuelGrid lda render_page ;configure render page
98bb: 29 01 and #$01
98bd: aa tax
98be: bd 04 99 lda :hires_page_hi,x
98c1: 85 32 sta hpage
; Draw vertical lines.
98c3: a9 0e lda #14
98c5: 85 85 sta ]index
98c7: a5 85 :VertLines lda ]index
98c9: 85 00 sta ]x0
98cb: 85 02 sta ]x1
98cd: a9 21 lda #33 ;viewport top
98cf: 85 01 sta ]y0
98d1: a9 bd lda #189 ;viewport bottom
98d3: 85 03 sta ]y1
98d5: 20 00 10 jsr DRAW_LINE
98d8: 18 clc
98d9: a5 85 lda ]index
98db: 69 10 adc #16
98dd: 85 85 sta ]index
98df: c9 e6 cmp #230
98e1: 90 e4 bcc :VertLines
; Draw horizontal lines.
98e3: a9 25 lda #37
98e5: 85 85 sta ]index
98e7: a5 85 :HorizLines lda ]index
98e9: 85 01 sta ]y0
98eb: 85 03 sta ]y1
98ed: a9 07 lda #7
98ef: 85 00 sta ]x0
98f1: a9 e6 lda #230
98f3: 85 02 sta ]x1
98f5: 20 00 10 jsr DRAW_LINE
98f8: 18 clc
98f9: a5 85 lda ]index
98fb: 69 0e adc #14
98fd: 85 85 sta ]index
98ff: c9 bd cmp #189
9901: 90 e4 bcc :HorizLines
9903: 60 rts
9904: 20 40 :hires_page_hi .bulk $20,$40
;
; Returns a random value in the range [$00,$1f] or [$e0,$ff]. This is the high
; byte of a map coordinate.
;
9906: 20 18 99 GetRndMapCoord jsr GetRandom
9909: 29 fe and #$fe ;don't be odd
990b: 48 pha ;preserve
990c: 29 10 and #$10 ;test bit 4 (BIT?)
990e: f0 04 beq :IsPos
9910: 68 pla
9911: 09 e0 ora #$e0 ;sign-extend
9913: 60 rts
9914: 68 :IsPos pla
9915: 29 1f and #$1f
9917: 60 rts
;
; Gets a random number, and updates the RNG state.
;
; On exit:
; A-reg: random value (0-255)
;
9918: ad 82 85 GetRandom lda rng_state
991b: 0e 82 85 asl rng_state
991e: 4d 82 85 eor rng_state
9921: 8d 82 85 sta rng_state
9924: 6e 82 85 ror rng_state
9927: 6a ror A
9928: ee 83 85 inc rng_state+1
992b: 6d 83 85 adc rng_state+1
992e: 50 03 bvc :NoInc
9930: ee 83 85 inc rng_state+1
9933: 8d 82 85 :NoInc sta rng_state
9936: 60 rts
;
; Perturbs the random number generator state using the player's current X/Z
; position.
;
9937: ad 48 66 UpdateRngState lda obj_xc_lo
993a: 4d 78 66 eor obj_zc_lo
993d: 6d 83 85 adc rng_state+1
9940: 8d 83 85 sta rng_state+1
9943: 60 rts
;
; Sets the player vehicle's movement and rotation speed based on the movement
; direction specified by the player (via joystick or keyboard).
;
; On entry:
; X-reg: movement direction (1-9)
;
9944: e0 00 SetPlayerMove cpx #$00 ;invalid
9946: f0 17 beq :Return
9948: bd 78 86 lda plyr_rot_speed-1,x
994b: 8d 68 67 sta obj_rotation_speed
994e: bd 81 86 lda plyr_move_speed-1,x
9951: 8d 38 67 sta obj_speed
9954: a9 00 lda #$00
9956: e0 07 cpx #$07 ;moving in reverse (7/8/9)?
9958: 90 02 bcc :Fwd
995a: a9 80 lda #$80 ;yes, set the facing adjustment
995c: 8d 50 67 :Fwd sta obj_facing_adj
995f: 60 :Return rts
;
; Sets the player's last-known position. When uncloaked this is updated every
; frame. When cloaked it's only updated when the player fires.
;
9960: ad 48 66 SetLastKnownPos lda obj_xc_lo ;player is 0th entry
9963: 8d 40 87 sta last_known_xc
9966: ad 60 66 lda obj_xc_hi
9969: 8d 41 87 sta last_known_xc+1
996c: ad 78 66 lda obj_zc_lo
996f: 8d 42 87 sta last_known_zc
9972: ad 90 66 lda obj_zc_hi
9975: 8d 43 87 sta last_known_zc+1
9978: 60 rts
;
; Draws the raven graphic at the top center of the screen. The outline is part
; of the background, but the middle has 4 stages that animate when the cloak is
; enabled or disabled.
;
• Clear variables
]base_ptr .var $92 {addr/2}
]raven_line_ptrs .var $a0 {addr/50}
9979: ae 8e 85 DrawRaven ldx raven_gfx_stage ;get index to raven (4 stages, 0-3)
997c: bd c1 9a lda raven_gfx_lo,x ;get base address of raven image
997f: 85 92 sta ]base_ptr
9981: bd c5 9a lda raven_gfx_hi,x
9984: 85 93 sta ]base_ptr+1
9986: a2 00 ldx #$00 ;populate zp with pointers to source lines
9988: a5 92 :Loop lda ]base_ptr
998a: 95 a0 sta ]raven_line_ptrs,x
998c: e8 inx
998d: a5 93 lda ]base_ptr+1
998f: 95 a0 sta ]raven_line_ptrs,x
9991: 18 clc
9992: a5 92 lda ]base_ptr ;advance pointer by 12 bytes
9994: 69 0c adc #12
9996: 85 92 sta ]base_ptr
9998: 90 02 bcc :NoInc
999a: e6 93 inc ]base_ptr+1
999c: e8 :NoInc inx
999d: e0 32 cpx #50 ;height=25
999f: 90 e7 bcc :Loop
99a1: 20 40 a0 jsr UpdateSound
99a4: 20 00 a0 jsr SoundStop
99a7: ad f0 65 lda render_page ;which page are we on?
99aa: f0 03 beq DrawRavenPage1 ;call appropriate routine
99ac: 4c 38 9a jmp DrawRavenPage2
99af: a0 0b DrawRavenPage1 ldy #11 ;width=12
99b1: b1 a0 :Loop lda (]raven_line_ptrs),y
99b3: 99 0a 2c sta $2c0a,y
99b6: b1 a2 lda (]raven_line_ptrs+2),y
99b8: 99 0a 30 sta $300a,y
99bb: b1 a4 lda (]raven_line_ptrs+4),y
99bd: 99 0a 34 sta $340a,y
99c0: b1 a6 lda (]raven_line_ptrs+6),y
99c2: 99 0a 38 sta $380a,y
99c5: b1 a8 lda (]raven_line_ptrs+8),y
99c7: 99 0a 3c sta $3c0a,y
99ca: b1 aa lda (]raven_line_ptrs+10),y
99cc: 99 8a 20 sta $208a,y
99cf: b1 ac lda (]raven_line_ptrs+12),y
99d1: 99 8a 24 sta $248a,y
99d4: b1 ae lda (]raven_line_ptrs+14),y
99d6: 99 8a 28 sta $288a,y
99d9: b1 b0 lda (]raven_line_ptrs+16),y
99db: 99 8a 2c sta $2c8a,y
99de: b1 b2 lda (]raven_line_ptrs+18),y
99e0: 99 8a 30 sta $308a,y
99e3: b1 b4 lda (]raven_line_ptrs+20),y
99e5: 99 8a 34 sta $348a,y
99e8: b1 b6 lda (]raven_line_ptrs+22),y
99ea: 99 8a 38 sta $388a,y
99ed: b1 b8 lda (]raven_line_ptrs+24),y
99ef: 99 8a 3c sta $3c8a,y
99f2: b1 ba lda (]raven_line_ptrs+26),y
99f4: 99 0a 21 sta $210a,y
99f7: b1 bc lda (]raven_line_ptrs+28),y
99f9: 99 0a 25 sta $250a,y
99fc: b1 be lda (]raven_line_ptrs+30),y
99fe: 99 0a 29 sta $290a,y
9a01: b1 c0 lda (]raven_line_ptrs+32),y
9a03: 99 0a 2d sta $2d0a,y
9a06: b1 c2 lda (]raven_line_ptrs+34),y
9a08: 99 0a 31 sta $310a,y
9a0b: b1 c4 lda (]raven_line_ptrs+36),y
9a0d: 99 0a 35 sta $350a,y
9a10: b1 c6 lda (]raven_line_ptrs+38),y
9a12: 99 0a 39 sta $390a,y
9a15: b1 c8 lda (]raven_line_ptrs+40),y
9a17: 99 0a 3d sta $3d0a,y
9a1a: b1 ca lda (]raven_line_ptrs+42),y
9a1c: 99 8a 21 sta $218a,y
9a1f: b1 cc lda (]raven_line_ptrs+44),y
9a21: 99 8a 25 sta $258a,y
9a24: b1 ce lda (]raven_line_ptrs+46),y
9a26: 99 8a 29 sta $298a,y
9a29: b1 d0 lda (]raven_line_ptrs+48),y
9a2b: 99 8a 2d sta $2d8a,y
9a2e: 20 10 a0 jsr SoundHalf
9a31: 88 dey
9a32: 30 03 bmi :Done
9a34: 4c b1 99 jmp :Loop
9a37: 60 :Done rts
9a38: a0 0b DrawRavenPage2 ldy #$0b
9a3a: b1 a0 :Loop lda (]raven_line_ptrs),y
9a3c: 99 0a 4c sta $4c0a,y
9a3f: b1 a2 lda (]raven_line_ptrs+2),y
9a41: 99 0a 50 sta $500a,y
9a44: b1 a4 lda (]raven_line_ptrs+4),y
9a46: 99 0a 54 sta $540a,y
9a49: b1 a6 lda (]raven_line_ptrs+6),y
9a4b: 99 0a 58 sta $580a,y
9a4e: b1 a8 lda (]raven_line_ptrs+8),y
9a50: 99 0a 5c sta $5c0a,y
9a53: b1 aa lda (]raven_line_ptrs+10),y
9a55: 99 8a 40 sta $408a,y
9a58: b1 ac lda (]raven_line_ptrs+12),y
9a5a: 99 8a 44 sta $448a,y
9a5d: b1 ae lda (]raven_line_ptrs+14),y
9a5f: 99 8a 48 sta $488a,y
9a62: b1 b0 lda (]raven_line_ptrs+16),y
9a64: 99 8a 4c sta $4c8a,y
9a67: b1 b2 lda (]raven_line_ptrs+18),y
9a69: 99 8a 50 sta $508a,y
9a6c: b1 b4 lda (]raven_line_ptrs+20),y
9a6e: 99 8a 54 sta $548a,y
9a71: b1 b6 lda (]raven_line_ptrs+22),y
9a73: 99 8a 58 sta $588a,y
9a76: b1 b8 lda (]raven_line_ptrs+24),y
9a78: 99 8a 5c sta $5c8a,y
9a7b: b1 ba lda (]raven_line_ptrs+26),y
9a7d: 99 0a 41 sta $410a,y
9a80: b1 bc lda (]raven_line_ptrs+28),y
9a82: 99 0a 45 sta $450a,y
9a85: b1 be lda (]raven_line_ptrs+30),y
9a87: 99 0a 49 sta $490a,y
9a8a: b1 c0 lda (]raven_line_ptrs+32),y
9a8c: 99 0a 4d sta $4d0a,y
9a8f: b1 c2 lda (]raven_line_ptrs+34),y
9a91: 99 0a 51 sta $510a,y
9a94: b1 c4 lda (]raven_line_ptrs+36),y
9a96: 99 0a 55 sta $550a,y
9a99: b1 c6 lda (]raven_line_ptrs+38),y
9a9b: 99 0a 59 sta $590a,y
9a9e: b1 c8 lda (]raven_line_ptrs+40),y
9aa0: 99 0a 5d sta $5d0a,y
9aa3: b1 ca lda (]raven_line_ptrs+42),y
9aa5: 99 8a 41 sta $418a,y
9aa8: b1 cc lda (]raven_line_ptrs+44),y
9aaa: 99 8a 45 sta $458a,y
9aad: b1 ce lda (]raven_line_ptrs+46),y
9aaf: 99 8a 49 sta $498a,y
9ab2: b1 d0 lda (]raven_line_ptrs+48),y
9ab4: 99 8a 4d sta $4d8a,y
9ab7: 20 10 a0 jsr SoundHalf
9aba: 88 dey
9abb: 30 03 bmi :Done
9abd: 4c 3a 9a jmp :Loop
9ac0: 60 :Done rts
; Addresses of the four-part raven animation.
9ac1: 50 raven_gfx_lo .dd1 <raven0
9ac2: 7c .dd1 <raven1
9ac3: a8 .dd1 <raven2
9ac4: d4 .dd1 <raven3
9ac5: 9b raven_gfx_hi .dd1 >raven0
9ac6: 9c .dd1 >raven1
9ac7: 9d .dd1 >raven2
9ac8: 9e .dd1 >raven3
;
; Updates the state of the player's cloaking device.
;
; There are three important bits of state:
; $858f: 0 when nothing is changing
; 1 when the cloak is activating
; 2 when the cloak is deactivating
; $858e: 0 when the raven image is full (no cloak)
; 1 when mostly full
; 2 when mostly empty
; 3 when empty
; $65f0: 0 when drawing on hi-res page 1
; 1 when drawing on hi-res page 2
;
; The first value just indicates whether something is changing. The raven
; graphics index (0-3) is what actually determines whether we're cloaked; any
; non-zero value means we're cloaked.
;
; The cloak activation code doesn't check the fuel level, so we actually start
; the transition and then, at the bottom of the function, reverse it. This
; provides a nice audio/visual failure indication.
;
9ac9: ad 38 85 UpdateCloaking lda gobj_cloak_cooldn ;countdown in progress?
9acc: f0 0a beq :NoChange ;no, not cloaked
9ace: ce 38 85 dec gobj_cloak_cooldn ;decrement cloak counter
9ad1: d0 05 bne :NoChange ;not zero, keep going
9ad3: a9 02 lda #$02 ;set state to "decloaking"
9ad5: 8d 8f 85 sta cloak_trans_state
9ad8: ad 8f 85 :NoChange lda cloak_trans_state ;check cloak transition state
9adb: f0 46 beq :NoChange ;not changing
9add: ad 8f 85 lda cloak_trans_state ;load this again
9ae0: c9 01 cmp #$01 ;cloak turning on?
9ae2: d0 1a bne :TurningOff
9ae4: ad 8e 85 lda raven_gfx_stage ;check the stage
9ae7: c9 03 cmp #$03 ;fully on?
9ae9: f0 08 beq L9AF3 ;yes
9aeb: ad f0 65 lda render_page ;incr every-other frame
9aee: d0 03 bne L9AF3 ; so we draw same thing on both pages
9af0: ee 8e 85 inc raven_gfx_stage
9af3: 20 79 99 L9AF3 jsr DrawRaven ;draw graphic
9af6: ad 8e 85 lda raven_gfx_stage ;check stage
9af9: c9 03 cmp #$03 ;fully cloaked?
9afb: 4c 11 9b jmp :Cont
9afe: ad 8e 85 :TurningOff lda raven_gfx_stage ;fully off?
9b01: f0 08 beq L9B0B ;yes
9b03: ad f0 65 lda render_page ;got both pages?
9b06: d0 03 bne L9B0B
9b08: ce 8e 85 dec raven_gfx_stage ;advance to next stage
9b0b: 20 79 99 L9B0B jsr DrawRaven ;raw graphic
9b0e: ad 8e 85 lda raven_gfx_stage ;check stage
9b11: d0 0a :Cont bne :StillTrans ;not fully on/off, branch
9b13: ad f0 65 lda render_page ;make sure we get both hi-res pages
9b16: f0 05 beq :StillTrans
9b18: a9 00 lda #$00 ;transition completed
9b1a: 8d 8f 85 sta cloak_trans_state
9b1d: ac 8e 85 :StillTrans ldy raven_gfx_stage
9b20: 20 a0 a1 jsr PlayCloakSound
9b23: ad 8e 85 :NoChange lda raven_gfx_stage ;if raven gfx is nonzero, we're cloaked
9b26: d0 03 bne :Cloaked
9b28: 4c 60 99 jmp SetLastKnownPos ;not cloaked, update "last known" position
9b2b: ad fe 81 :Cloaked lda fuel_level+1 ;check fuel level
9b2e: c9 08 cmp #$08 ;getting low?
9b30: b0 0a bcs :FuelOk ;no, still good
9b32: a9 02 lda #$02 ;fuel low, uncloak
9b34: 8d 8f 85 sta cloak_trans_state
9b37: a9 00 lda #$00 ;reset cooldown
9b39: 8d 38 85 sta gobj_cloak_cooldn
9b3c: a9 28 :FuelOk lda #40 ;reduce fuel by 40 units
9b3e: a2 00 ldx #0
9b40: 4c be 80 jmp ReduceFuel
9b43: 20 41 4e 59+ .junk 13
;
; This is the (12*7)x25 raven image that appears at the top of the HUD. It's
; missing the very top and bottom because those parts aren't changed by the
; animation.
;
9b50: 60 7f 01 00+ raven0 .bulk $60,$7f,$01,$00,$00,$00,$00,$00,$00,$00,$7f,$07,$40,$5f,$47,$01
+ $00,$00,$00,$00,$00,$63,$7b,$03,$00,$67,$1e,$03,$00,$00,$00,$00
+ $40,$79,$66,$01,$40,$1b,$7e,$07,$00,$00,$00,$00,$60,$7f,$58,$03
+ $40,$73,$7f,$1f,$00,$00,$00,$00,$78,$7f,$4f,$03,$00,$07,$7c,$7f
+ $03,$40,$03,$40,$7f,$3f,$60,$01,$00,$7e,$7f,$1f,$7f,$73,$4f,$7f
+ $79,$7f,$7f,$00,$00,$0e,$70,$3f,$7c,$3f,$7c,$3f,$7c,$1f,$70,$00
+ $00,$1c,$0f,$7f,$00,$1e,$78,$00,$7e,$71,$39,$00,$00,$7c,$71,$7e
+ $19,$36,$6c,$18,$7f,$0e,$3f,$00,$00,$38,$18,$7b,$3f,$60,$06,$7c
+ $5f,$19,$1c,$00,$00,$70,$4c,$4d,$7f,$40,$03,$7e,$33,$33,$0e,$00
+ $00,$60,$67,$4c,$7f,$01,$01,$7f,$33,$66,$07,$00,$00,$40,$33,$66
+ $5c,$07,$60,$3f,$66,$4c,$03,$00,$00,$00,$1f,$66,$6c,$0e,$70,$36
+ $66,$78,$01,$00,$00,$00,$3c,$37,$66,$3c,$3c,$66,$6c,$3d,$00,$00
+ $00,$00,$70,$3f,$6e,$7f,$7f,$77,$7c,$0f,$00,$00,$00,$00,$60,$79
+ $7f,$59,$1b,$7f,$1f,$07,$00,$00,$00,$00,$00,$60,$77,$7c,$3f,$6e
+ $07,$00,$00,$00,$00,$00,$00,$00,$38,$76,$6f,$1c,$00,$00,$00,$00
+ $00,$00,$00,$00,$1c,$5b,$5b,$39,$00,$00,$00,$00,$00,$00,$00,$00
+ $5c,$59,$1b,$3b,$00,$00,$00,$00,$00,$00,$00,$00,$78,$4c,$33,$1e
+ $00,$00,$00,$00,$00,$00,$00,$00,$70,$4d,$33,$0f,$00,$00,$00,$00
+ $00,$00,$00,$00,$60,$7f,$7f,$07,$00,$00,$00,$00
9c7c: 60 7f 01 00+ raven1 .bulk $60,$7f,$01,$00,$00,$00,$00,$00,$00,$00,$7f,$07,$40,$5f,$47,$01
+ $00,$00,$00,$00,$00,$63,$7b,$03,$00,$67,$1e,$03,$00,$00,$00,$00
+ $40,$79,$66,$01,$40,$1b,$7e,$07,$00,$00,$00,$00,$60,$7f,$58,$03
+ $40,$63,$7f,$1f,$00,$00,$00,$00,$78,$7f,$47,$03,$00,$07,$7c,$7f
+ $03,$40,$03,$40,$7f,$3f,$60,$01,$00,$7e,$00,$1f,$7f,$73,$4f,$7f
+ $79,$01,$7e,$00,$00,$0e,$00,$38,$7c,$3f,$7c,$3f,$1c,$00,$70,$00
+ $00,$1c,$0f,$70,$00,$1e,$78,$00,$0e,$70,$39,$00,$00,$7c,$01,$60
+ $19,$36,$6c,$18,$07,$00,$3f,$00,$00,$38,$00,$40,$3f,$00,$00,$7c
+ $03,$00,$1c,$00,$00,$70,$0c,$00,$00,$00,$00,$00,$00,$30,$0e,$00
+ $00,$60,$67,$00,$00,$00,$00,$00,$00,$66,$07,$00,$00,$40,$33,$66
+ $1c,$00,$00,$38,$66,$4c,$03,$00,$00,$00,$1f,$66,$0c,$03,$40,$31
+ $66,$78,$01,$00,$00,$00,$3c,$37,$66,$1c,$38,$66,$6c,$3d,$00,$00
+ $00,$00,$70,$3f,$6e,$0f,$70,$77,$7c,$0f,$00,$00,$00,$00,$60,$79
+ $7f,$19,$18,$7f,$1f,$07,$00,$00,$00,$00,$00,$60,$77,$00,$00,$6e
+ $07,$00,$00,$00,$00,$00,$00,$00,$38,$02,$40,$1c,$00,$00,$00,$00
+ $00,$00,$00,$00,$1c,$03,$40,$39,$00,$00,$00,$00,$00,$00,$00,$00
+ $5c,$19,$18,$3b,$00,$00,$00,$00,$00,$00,$00,$00,$78,$4c,$33,$1e
+ $00,$00,$00,$00,$00,$00,$00,$00,$70,$4d,$33,$0f,$00,$00,$00,$00
+ $00,$00,$00,$00,$60,$7f,$7f,$07,$00,$00,$00,$00
9da8: 60 7f 01 00+ raven2 .bulk $60,$7f,$01,$00,$00,$00,$00,$00,$00,$00,$7f,$07,$40,$5f,$47,$01
+ $00,$00,$00,$00,$00,$63,$7b,$03,$00,$67,$1e,$03,$00,$00,$00,$00
+ $40,$79,$66,$01,$40,$1b,$7c,$07,$00,$00,$00,$00,$60,$3f,$58,$03
+ $40,$03,$70,$1f,$00,$00,$00,$00,$78,$07,$40,$03,$00,$07,$00,$7c
+ $03,$40,$03,$40,$3f,$00,$60,$01,$00,$1e,$00,$78,$7f,$73,$4f,$7f
+ $1f,$00,$78,$00,$00,$0e,$00,$30,$7c,$3f,$7c,$3f,$0c,$00,$70,$00
+ $00,$1c,$00,$00,$00,$1c,$38,$00,$00,$00,$38,$00,$00,$7c,$00,$00
+ $00,$00,$00,$00,$00,$00,$3e,$00,$00,$38,$00,$00,$00,$00,$00,$00
+ $00,$00,$1c,$00,$00,$70,$00,$00,$00,$00,$00,$00,$00,$00,$0e,$00
+ $00,$60,$07,$00,$00,$00,$00,$00,$00,$60,$07,$00,$00,$40,$33,$00
+ $00,$00,$00,$00,$00,$4c,$03,$00,$00,$00,$1f,$66,$00,$00,$00,$00
+ $66,$78,$01,$00,$00,$00,$3c,$37,$66,$1c,$38,$66,$6c,$3d,$00,$00
+ $00,$00,$70,$3f,$6e,$07,$60,$77,$7c,$0f,$00,$00,$00,$00,$60,$79
+ $7f,$01,$00,$7f,$1f,$07,$00,$00,$00,$00,$00,$60,$77,$00,$00,$6e
+ $07,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00,$1c,$00,$00,$00,$00
+ $00,$00,$00,$00,$1c,$00,$00,$38,$00,$00,$00,$00,$00,$00,$00,$00
+ $5c,$01,$00,$3b,$00,$00,$00,$00,$00,$00,$00,$00,$78,$0c,$30,$1e
+ $00,$00,$00,$00,$00,$00,$00,$00,$70,$4d,$33,$0f,$00,$00,$00,$00
+ $00,$00,$00,$00,$60,$7f,$7f,$07,$00,$00,$00,$00
9ed4: 60 7f 01 00+ raven3 .bulk $60,$7f,$01,$00,$00,$00,$00,$00,$00,$00,$7f,$07,$40,$43,$47,$01
+ $00,$00,$00,$00,$00,$63,$43,$03,$00,$07,$1e,$03,$00,$00,$00,$00
+ $40,$79,$60,$01,$40,$03,$78,$07,$00,$00,$00,$00,$60,$1f,$40,$03
+ $40,$03,$60,$1f,$00,$00,$00,$00,$78,$07,$40,$03,$00,$07,$00,$78
+ $03,$40,$03,$40,$1f,$00,$60,$01,$00,$0e,$00,$60,$7f,$73,$4f,$7f
+ $07,$00,$70,$00,$00,$0e,$00,$00,$7c,$3f,$7c,$3f,$00,$00,$70,$00
+ $00,$1c,$00,$00,$00,$0c,$30,$00,$00,$00,$38,$00,$00,$1c,$00,$00
+ $00,$00,$00,$00,$00,$00,$38,$00,$00,$38,$00,$00,$00,$00,$00,$00
+ $00,$00,$1c,$00,$00,$70,$00,$00,$00,$00,$00,$00,$00,$00,$0e,$00
+ $00,$60,$01,$00,$00,$00,$00,$00,$00,$00,$07,$00,$00,$40,$03,$00
+ $00,$00,$00,$00,$00,$40,$03,$00,$00,$00,$0f,$00,$00,$00,$00,$00
+ $00,$70,$01,$00,$00,$00,$3c,$06,$00,$00,$00,$00,$60,$3c,$00,$00
+ $00,$00,$70,$3f,$08,$00,$00,$10,$7c,$0f,$00,$00,$00,$00,$60,$79
+ $7f,$01,$00,$7f,$1f,$07,$00,$00,$00,$00,$00,$60,$77,$00,$00,$6e
+ $07,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00,$1c,$00,$00,$00,$00
+ $00,$00,$00,$00,$1c,$00,$00,$38,$00,$00,$00,$00,$00,$00,$00,$00
+ $1c,$00,$00,$38,$00,$00,$00,$00,$00,$00,$00,$00,$38,$00,$00,$1c
+ $00,$00,$00,$00,$00,$00,$00,$00,$70,$08,$10,$0e,$00,$00,$00,$00
+ $00,$00,$00,$00,$60,$3f,$7c,$07,$00,$00,$00,$00
a000: a9 00 SoundStop lda #$00
a002: 8d 80 03 sta sound80
a005: 60 rts
a006: aa bd 1d 7e+ .align $10 (10 bytes)
a010: ee 80 03 SoundHalf inc sound80
a013: ad 80 03 lda sound80
a016: 4a lsr A
a017: 90 27 bcc UpdateSound
a019: 60 rts
a01a: 05 7f 60 20+ .align $10 (6 bytes)
a020: 20 40 a0 SoundTwice jsr UpdateSound
a023: 4c 40 a0 jmp UpdateSound
a026: 8d f8 78 a9+ .align $10 (10 bytes)
a030: 20 40 a0 SoundThrice jsr UpdateSound
a033: 20 40 a0 jsr UpdateSound
a036: 4c 40 a0 jmp UpdateSound
a039: 4a 4a 4a 4a+ .align $10 (7 bytes)
********************************************************************************
* Update sound effects. *
* *
* Called from various places in ROCK1 and ROCK2. *
* *
* On exit: *
* X-reg and Y-reg preserved *
********************************************************************************
• Clear variables
]tmp .var $ff {addr/1}
a040: ad 80 85 UpdateSound lda sound_enab_flag ;rewritten by SwapSound ($A1C0)
a043: 30 01 bmi LA046
a045: 60 rts
a046: ad 00 03 LA046 lda sound00
a049: 10 37 bpl LA082
a04b: ee 40 03 inc sound40
a04e: d0 0a bne LA05A
a050: ee 61 a0 inc LA05F+2
a053: d0 05 bne LA05A
a055: a9 e0 lda #$e0
a057: 8d 61 a0 sta LA05F+2
a05a: 84 ff LA05A sty ]tmp
a05c: ac 40 03 ldy sound40
a05f: b9 00 e0 LA05F lda $e000,y
a062: a4 ff ldy ]tmp
a064: c9 d0 cmp #$d0
a066: b0 04 bcs LA06C
a068: ad 30 c0 lda SPKR ;click
a06b: 60 rts
a06c: ce 30 03 LA06C dec sound30
a06f: d0 10 bne LA081
a071: a9 00 lda #$00
a073: 8d 00 03 sta sound00
a076: 8d 01 03 sta sound00+1
a079: 8d 02 03 sta sound00+2
a07c: a9 20 lda #$20
a07e: 8d 33 03 sta sound30+3
a081: 60 LA081 rts
a082: ad 01 03 LA082 lda sound00+1
a085: 10 43 bpl LA0CA
a087: ee 41 03 inc sound40+1
a08a: d0 0a bne LA096
a08c: ee 9d a0 inc :_addr+2
a08f: d0 05 bne LA096
a091: a9 e0 lda #$e0
a093: 8d 9d a0 sta :_addr+2
a096: 84 ff LA096 sty ]tmp
a098: ac 41 03 ldy sound40+1
a09b: b9 00 e0 :_addr lda $e000,y
a09e: a4 ff ldy ]tmp
a0a0: cd 11 03 cmp sound10+1
a0a3: b0 03 bcs LA0A8
a0a5: ad 30 c0 lda SPKR ;click
a0a8: ce 31 03 LA0A8 dec sound30+1
a0ab: d0 1c bne LA0C9
a0ad: a9 0d lda #$0d
a0af: 8d 31 03 sta sound30+1
a0b2: ce 11 03 dec sound10+1
a0b5: ad 11 03 lda sound10+1
a0b8: c9 01 cmp #$01
a0ba: b0 0d bcs LA0C9
a0bc: a9 00 lda #$00
a0be: 8d 01 03 sta sound00+1
a0c1: 8d 02 03 sta sound00+2
a0c4: a9 20 lda #$20
a0c6: 8d 33 03 sta sound30+3
a0c9: 60 LA0C9 rts
a0ca: ad 02 03 LA0CA lda sound00+2
a0cd: 10 24 bpl LA0F3
a0cf: ce 22 03 dec sound20+2
a0d2: d0 1e bne LA0F2
a0d4: ad 30 c0 lda SPKR ;click
a0d7: ee 12 03 inc sound10+2
a0da: ad 12 03 lda sound10+2
a0dd: c9 78 cmp #$78
a0df: 90 0b bcc LA0EC
a0e1: a9 00 lda #$00
a0e3: 8d 02 03 sta sound00+2
a0e6: a9 20 lda #$20
a0e8: 8d 33 03 sta sound30+3
a0eb: 60 rts
a0ec: 4a LA0EC lsr A
a0ed: 4a lsr A
a0ee: 4a lsr A
a0ef: 8d 22 03 sta sound20+2
a0f2: 60 LA0F2 rts
a0f3: ad 03 03 LA0F3 lda sound00+3
a0f6: 10 18 bpl LA110
a0f8: ce 23 03 dec sound20+3
a0fb: d0 13 bne LA110
a0fd: ad 30 c0 lda SPKR ;click
a100: ad 13 03 lda sound10+3
a103: 8d 23 03 sta sound20+3
a106: ce 33 03 dec sound30+3
a109: d0 05 bne LA110
a10b: a9 00 lda #$00
a10d: 8d 03 03 sta sound00+3
a110: 60 LA110 rts
a111: e5 ed 85 ef+ .align $40 (47 bytes)
a140: a9 50 PlayerHitSound lda #$50
a142: 8d 30 03 sta sound30
a145: a9 80 lda #$80
a147: 8d 00 03 sta sound00
a14a: 60 rts
a14b: d0 cc 20 20+ .align $20 (21 bytes)
a160: a9 0d EnemyKillSound lda #$0d
a162: 8d 31 03 sta sound30+1
a165: a9 a0 lda #$a0
a167: 8d 11 03 sta sound10+1
a16a: a9 80 lda #$80
a16c: 8d 01 03 sta sound00+1
a16f: 60 rts
a170: bd 00 15 85+ .align $20 (16 bytes)
a180: a9 01 PlayerFireSound lda #$01
a182: 8d 22 03 sta sound20+2
a185: a9 08 lda #$08
a187: 8d 12 03 sta sound10+2
a18a: a9 80 lda #$80
a18c: 8d 02 03 sta sound00+2
a18f: 60 rts
a190: 00 16 05 ee+ .align $20 (16 bytes)
;
; Plays the cloak/uncloak sound.
;
; On entry:
; Y-reg: cloak stage (0-3)
;
a1a0: b9 b3 a1 PlayCloakSound lda :cloak_sound_tbl,y
a1a3: 8d 33 03 sta sound30+3
a1a6: c8 iny
a1a7: 8c 13 03 sty sound10+3
a1aa: 8c 23 03 sty sound20+3
a1ad: a9 80 lda #$80
a1af: 8d 03 03 sta sound00+3
a1b2: 60 rts
:cloak_sound_tbl
a1b3: 00 00 00 40 .bulk $00,$00,$00,$40
a1b7: a2 27 bd 00+ .align $20 (9 bytes)
;
; The warp-out code alters the UpdateSound code to jump to the warp-sound code
; instead. When warp-out completes the code is restored.
;
a1c0: 86 ff SwapSound stx ]tmp ;preserve X-reg
a1c2: a2 02 ldx #$02
a1c4: bd 40 a0 :Loop lda UpdateSound,x
a1c7: 48 pha
a1c8: bd d8 a1 lda :sound_jmp,x
a1cb: 9d 40 a0 sta UpdateSound,x
a1ce: 68 pla
a1cf: 9d d8 a1 sta :sound_jmp,x
a1d2: ca dex
a1d3: 10 ef bpl :Loop
a1d5: a6 ff ldx ]tmp ;restore
a1d7: 60 rts
a1d8: 4c b8 a2 :sound_jmp jmp PlayWarpSounds
a1db: 05 ee 85 ff+ .align $20 (5 bytes)
;
; Initializes sound generation. Any existing sounds cease.
;
a1e0: a9 00 InitSound00 lda #$00
a1e2: 8d 00 03 sta sound00
a1e5: 8d 01 03 sta sound00+1
a1e8: 8d 02 03 sta sound00+2
a1eb: 8d 03 03 sta sound00+3
a1ee: 60 rts
a1ef: fa 91 fc 91+ .align $0100 (17 bytes)
PlayRefuelSound_jmp
a200: 4c 12 a2 jmp PlayRefuelSound
a203: 4c 3a a2 PlaySound3_jmp jmp PlaySound3?
PlayDeadSound_jmp
a206: 4c 56 a2 jmp PlayDeadSound
PlayShutOpBeep_jmp
a209: 4c a0 a2 jmp ShutterOpenBeep?
a20c: 4c b8 a2 PlaySound1_jmp jmp PlayWarpSounds
a20f: 4c .dd1 $4c ;unreferenced JMP to RTS
a210: df .dd1 $df
a211: a2 .dd1 $a2
a212: 8d 18 03 PlayRefuelSound sta sound18 ;sound pitch parameter
a215: e9 e0 sbc #$e0
a217: 49 ff eor #$ff
a219: 4a lsr A
a21a: 4a lsr A
a21b: 4a lsr A
a21c: 4a lsr A
a21d: 69 10 adc #$10
a21f: 8d 38 03 sta sound38
a222: ae 18 03 LA222 ldx sound18
a225: 66 00 LA225 ror $00
a227: 26 00 rol $00
a229: ca dex
a22a: d0 f9 bne LA225
a22c: ad 80 85 lda sound_enab_flag
a22f: 10 03 bpl LA234
a231: ad 30 c0 lda SPKR ;click
a234: ce 38 03 LA234 dec sound38
a237: d0 e9 bne LA222
a239: 60 rts
a23a: ad 80 85 PlaySound3? lda sound_enab_flag
a23d: 10 16 bpl LA255
a23f: a9 80 lda #$80
a241: 8d 39 03 sta $0339
a244: a2 60 LA244 ldx #$60
a246: 66 00 LA246 ror $00
a248: 26 00 rol $00
a24a: ca dex
a24b: d0 f9 bne LA246
a24d: ad 30 c0 lda SPKR ;click
a250: ce 39 03 dec $0339
a253: d0 ef bne LA244
a255: 60 LA255 rts
; Player has died. Make sounds and flicker the hi-res pages.
a256: a9 dc PlayDeadSound lda #$dc
a258: 8d 1a 03 sta $031a
a25b: a9 10 LA25B lda #$10
a25d: 8d 3a 03 sta $033a
a260: ee 4a 03 LA260 inc $034a
a263: d0 0a bne LA26F
a265: ee 79 a2 inc LA277+2
a268: d0 05 bne LA26F
a26a: a9 e0 lda #$e0
a26c: 8d 79 a2 sta LA277+2
a26f: a2 8c LA26F ldx #$8c
a271: ca LA271 dex
a272: d0 fd bne LA271
a274: ae 4a 03 ldx $034a
a277: bd 00 e0 LA277 lda $e000,x
a27a: cd 1a 03 cmp $031a
a27d: b0 08 bcs LA287
a27f: ad 80 85 lda sound_enab_flag
a282: 10 03 bpl LA287
a284: ad 30 c0 lda SPKR ;click
a287: ce 3a 03 LA287 dec $033a
a28a: d0 d4 bne LA260
a28c: ad 1a 03 lda $031a
a28f: 4a lsr A
a290: 4a lsr A
a291: 4a lsr A
a292: 2c 54 c0 bit TXTPAGE1
a295: 90 03 bcc LA29A
a297: 2c 55 c0 bit TXTPAGE2
a29a: ce 1a 03 LA29A dec $031a
a29d: d0 bc bne LA25B
a29f: 60 rts
ShutterOpenBeep?
a2a0: ad 80 85 lda sound_enab_flag
a2a3: 10 12 bpl :Skip
a2a5: ad 0b 03 lda sysnm_flash_flag ;toggle beep as system name flashes
a2a8: 10 0d bpl :Skip
a2aa: ce 2b 03 dec $032b
a2ad: d0 08 bne :Skip
a2af: ad 30 c0 lda SPKR ;click
a2b2: a9 02 lda #$02
a2b4: 8d 2b 03 sta $032b
a2b7: 60 :Skip rts
]tmp .var $ff {addr/1}
a2b8: ad 80 85 PlayWarpSounds lda sound_enab_flag
a2bb: 10 21 bpl :Done
a2bd: ee 4c 03 inc warp_sound_ctr?
a2c0: d0 0a bne :NoWrap
a2c2: ee d3 a2 inc :_Entropy+2
a2c5: d0 05 bne :NoWrap
a2c7: a9 e0 lda #$e0
a2c9: 8d d3 a2 sta :_Entropy+2
a2cc: 84 ff :NoWrap sty ]tmp
a2ce: ac 4c 03 ldy warp_sound_ctr?
a2d1: b9 00 e0 :_Entropy lda $e000,y ;use ROM as entropy source
a2d4: a4 ff ldy ]tmp
a2d6: cd 1c 03 cmp warp_sound_thing?
a2d9: b0 03 bcs :Done
a2db: ad 30 c0 lda SPKR ;click
a2de: 60 :Done rts
a2df: 60 81 8d fe+ .align $0100 (33 bytes)
;
; Tell the player they've won.
;
]msg_num .var $00 {addr/1}
]txt_ptr .var $01 {addr/2}
]string_index .var $03 {addr/1}
]counter .var $04 {addr/1}
]src_ptr .var $05 {addr/2}
]hcol .var $07 {addr/1}
]hline .var $08 {addr/1}
]hptr .var $09 {addr/2}
]vhpage .var $0b {addr/1} ;$20/$40
a300: 2c 55 c0 Victory bit TXTPAGE2
a303: a9 48 lda #$48
a305: 85 08 sta ]hline
a307: a9 40 lda #$40
a309: 85 0b sta ]vhpage
a30b: a0 03 ldy #$03 ;msg #3
a30d: 20 57 a3 jsr :PrintStringN
a310: 20 3a a2 jsr PlaySound3?
a313: a0 09 ldy #$09
a315: 20 3e a3 jsr LA33E
a318: a9 78 lda #$78
a31a: 85 08 sta ]hline
a31c: a9 02 lda #$02 ;msg #2
a31e: 85 00 sta ]msg_num
a320: a9 40 :MsgLoop lda #$40
a322: 85 0b sta ]vhpage
a324: a4 00 ldy ]msg_num
a326: 20 57 a3 jsr :PrintStringN
a329: a9 20 lda #$20
a32b: 85 0b sta ]vhpage
a32d: a4 00 ldy ]msg_num
a32f: 20 57 a3 jsr :PrintStringN
a332: 20 69 a3 jsr :MoveDownLine
a335: 20 69 a3 jsr :MoveDownLine
a338: c6 00 dec ]msg_num
a33a: 10 e4 bpl :MsgLoop
; Slowly flip hi-res pages for a bit.
a33c: a0 3d ldy #$3d
a33e: a2 00 LA33E ldx #$00
a340: 86 ff stx ]tmp
a342: ca LA342 dex
a343: d0 fd bne LA342
a345: c6 ff dec ]tmp
a347: d0 f9 bne LA342
a349: 98 tya
a34a: 4a lsr A
a34b: 49 01 eor #$01
a34d: 29 01 and #$01
a34f: aa tax
a350: bd 54 c0 lda TXTPAGE1,x ;flip hi-res page
a353: 88 dey
a354: d0 e8 bne LA33E
a356: 60 rts
a357: b9 c8 a3 :PrintStringN lda :col_tab,y
a35a: 85 07 sta ]hcol
a35c: b9 c0 a3 lda :text_addr_lo,y
a35f: 85 01 sta ]txt_ptr
a361: b9 c4 a3 lda :text_addr_hi,y
a364: 85 02 sta ]txt_ptr+1
a366: 4c 71 a3 jmp :PrintString
a369: 18 :MoveDownLine clc
a36a: a5 08 lda ]hline
a36c: 69 08 adc #$08
a36e: 85 08 sta ]hline
a370: 60 rts
; Simple HRCG that blends with the screen contents instead of overwriting them.
a371: a0 00 :PrintString ldy #$00
a373: 84 03 sty ]string_index
a375: b1 01 lda (]txt_ptr),y
a377: 20 82 a3 :StringLoop jsr :PrintChar
a37a: e6 03 inc ]string_index
a37c: a4 03 ldy ]string_index
a37e: b1 01 lda (]txt_ptr),y
a380: 10 f5 bpl :StringLoop ;fall through for last one (which has hi set)
a382: 29 7f :PrintChar and #$7f ;strip hi bit
a384: 38 sec
a385: e9 20 sbc #$20 ;no control chars in font
a387: 0a asl A ;multiply * 8
a388: 0a asl A
a389: 26 06 rol ]src_ptr+1 ;(not initialized)
a38b: 0a asl A
a38c: 85 05 sta ]src_ptr ;set src ptr
a38e: a5 06 lda ]src_ptr+1
a390: 2a rol A ;do last part of * 8 in high byte
a391: 29 03 and #$03 ;strip the uninitialized bits
a393: 18 clc
a394: 69 79 adc #>font_glyphs ;add the base address
a396: 85 06 sta ]src_ptr+1 ;ready
a398: a9 07 lda #$07 ;glyph height (8)
a39a: 85 04 sta ]counter
a39c: 18 clc
a39d: a5 08 lda ]hline ;top line
a39f: 69 07 adc #$07 ;drawing from bottom up
a3a1: aa tax
a3a2: bd 00 15 :DrawLoop lda HIRES_ADDR_LO,x ;get hi-res row address
a3a5: 85 09 sta ]hptr
a3a7: bd 00 16 lda HIRES_ADDR_HI,x
a3aa: 05 0b ora ]vhpage
a3ac: 85 0a sta ]hptr+1
a3ae: a4 04 ldy ]counter ;copy one byte
a3b0: b1 05 lda (]src_ptr),y
a3b2: a4 07 ldy ]hcol
a3b4: 11 09 ora (]hptr),y ;blend with screen contents
a3b6: 91 09 sta (]hptr),y
a3b8: ca dex ;move up a line
a3b9: c6 04 dec ]counter ;done yet?
a3bb: 10 e5 bpl :DrawLoop ;nope
a3bd: e6 07 inc ]hcol ;increment column number
a3bf: 60 rts
a3c0: fb :text_addr_lo .dd1 <TA3FB
a3c1: dc .dd1 <TA3DC
a3c2: cc .dd1 <TA3CC
a3c3: 1a .dd1 <TA41A
a3c4: a3 :text_addr_hi .dd1 >TA3FB
a3c5: a3 .dd1 >TA3DC
a3c6: a3 .dd1 >TA3CC
a3c7: a4 .dd1 >TA41A
a3c8: 01 01 09 03 :col_tab .bulk $01,$01,$09,$03
a3cc: 43 4f 4e 47+ TA3CC .dstr ‘CONGRATULATIONS!’
a3dc: 59 4f 55 20+ TA3DC .dstr ‘YOU HAVE VANQUISHED GIR DRAXON!’
a3fb: 54 52 55 4c+ TA3FB .dstr ‘TRULY A BRILLIANT PERFORMANCE!’
a41a: 52 41 56 45+ TA41A .dstr ‘RAVEN COMMANDER, COME IN--’
a435: 60 48 4a 4a+ .junk 11
********************************************************************************
* OS/ROCK3 entry point *
********************************************************************************
a440: 4c 46 a4 GameEntry jmp :GameEntry1 ;ISO 16
CallInvertViewport
a443: 4c 78 ab jmp InvertViewport
]counter .var $02 {addr/1}
]str_ptr .var $06 {addr/2}
a446: a9 00 :GameEntry1 lda #$00 ;Ctrl+@
a448: 20 00 78 jsr PrintChar ;reset text window and other options
a44b: a9 00 lda #$00
a44d: 8d bb a5 sta briefing_loaded_flag
a450: a9 01 lda #$01 ;joystick mode
a452: 8d f4 7c sta control_mode
a455: 20 58 ab MainMenuLoop jsr ClearHiresPage1
a458: 20 e0 a1 jsr InitSound00
a45b: 20 bd a5 :MenuLoop jsr PrintMenuText
a45e: 2c 54 c0 bit TXTPAGE1
; Wait until a key is pressed or the timeout expires.
a461: 2c 10 c0 bit KBDSTRB
a464: a9 1e lda #$1e
a466: 85 02 sta ]counter
a468: a2 00 ldx #$00
a46a: a0 00 ldy #$00
a46c: ad 00 c0 :WaitLoop lda KBD
a46f: 30 0c bmi :KeyHit
a471: 88 dey
a472: d0 f8 bne :WaitLoop
a474: ca dex
a475: d0 f5 bne :WaitLoop
a477: c6 02 dec ]counter
a479: d0 f1 bne :WaitLoop
a47b: a9 cd lda #“M” ;act like the user hit 'M'
a47d: 2c 10 c0 :KeyHit bit KBDSTRB
a480: c9 a0 cmp #“ ” ;check spacebar (starts mission)
a482: d0 70 bne :CheckM ;nope
; Start the mission.
a484: 20 5f ab jsr ClearHiresPage2
a487: a9 40 lda #$40
a489: 85 ee sta HPAGE_EE
a48b: a9 50 lda #80 ;row 10
a48d: 8d f9 78 sta text_row
a490: a9 02 lda #2 ;centered
a492: 8d f8 78 sta text_col
a495: a9 a3 lda #<good_luck
a497: 85 06 sta ]str_ptr
a499: a9 a4 lda #>good_luck
a49b: 85 07 sta ]str_ptr+1
a49d: 20 e0 78 jsr PrintDciString
a4a0: 4c c7 a4 jmp :Cont
a4a3: 47 6f 6f 64+ good_luck .dstr ‘Good luck on your mission, Commander’
a4c7: 2c 55 c0 :Cont bit TXTPAGE2 ;switch to page 2 to show message
a4ca: 20 10 1c jsr COPY_HIRES_2TO1 ;copy to page 1
a4cd: 2c 54 c0 bit TXTPAGE1 ;switch to page 1
a4d0: a2 03 ldx #$03 ;load PANEL on page 2
a4d2: 20 60 af jsr LOAD_FILE
a4d5: a2 07 ldx #$07 ;load PLAYGAME at $8400-9b42
a4d7: 20 60 af jsr LOAD_FILE
a4da: ad bc a5 lda sound_enab_menu_flag ;configure sound based on menu option
a4dd: 8d 80 85 sta sound_enab_flag
a4e0: 20 50 87 jsr StartMission ;start the mission
a4e3: ad 80 85 lda sound_enab_flag ;copy current state of sound
a4e6: 8d bc a5 sta sound_enab_menu_flag
a4e9: 20 19 a9 jsr ShowPostGameScore ;game over, show scores
a4ec: a9 00 lda #$00 ;mark briefing code as unloaded
a4ee: 8d bb a5 sta briefing_loaded_flag
a4f1: 4c 55 a4 jmp MainMenuLoop
a4f4: c9 cd :CheckM cmp #“M” ;'M' (show briefing)?
a4f6: f0 03 beq :Briefing ;yes, branch just ahead
a4f8: 4c 7c a5 jmp :CheckS
; Prepare for mission briefing.
a4fb: ad bb a5 :Briefing lda briefing_loaded_flag ;are briefing files already loaded?
a4fe: 30 71 bmi :AlreadyLoaded ;yes, branch
a500: 20 5f ab jsr ClearHiresPage2 ;no, put up a "loading message"
a503: a9 40 lda #$40
a505: 85 ee sta HPAGE_EE
a507: a9 50 lda #80 ;line 10
a509: 8d f9 78 sta text_row
a50c: a9 0b lda #11
a50e: 8d f8 78 sta text_col
a511: a9 1f lda #<text_b00
a513: 85 06 sta ]str_ptr
a515: a9 a5 lda #>text_b00
a517: 85 07 sta ]str_ptr+1
a519: 20 e0 78 jsr PrintDciString
a51c: 4c 2f a5 jmp :Next
a51f: 4d 49 53 53+ text_b00 .dstr ‘MISSION BRIEFING’
a52f: a9 5c :Next lda #92 ;line 11.5
a531: 8d f9 78 sta text_row
a534: a9 0a lda #10
a536: 8d f8 78 sta text_col
a539: a9 47 lda #<text_b01
a53b: 85 06 sta ]str_ptr
a53d: a9 a5 lda #>text_b01
a53f: 85 07 sta ]str_ptr+1
a541: 20 e0 78 jsr PrintDciString
a544: 4c 59 a5 jmp :Next
a547: 28 72 65 74+ text_b01 .dstr ‘(retrieving files)’
a559: 2c 55 c0 :Next bit TXTPAGE2 ;show the text we just drew
a55c: 20 10 1c jsr COPY_HIRES_2TO1 ;clone it on page 1
a55f: 2c 54 c0 bit TXTPAGE1 ;show page 1
a562: a2 04 ldx #$04 ;BRIEF (background image, loads onto page 2)
a564: 20 60 af jsr LOAD_FILE
a567: a2 05 ldx #$05 ;BRIEF.ST ($6800-6E75)
a569: 20 60 af jsr LOAD_FILE
a56c: a2 08 ldx #$08 ;BRIEFING ($8400-9157)
a56e: 20 60 af jsr LOAD_FILE
a571: 20 00 84 :AlreadyLoaded jsr gobj_types ;call into BRIEFING code to show briefing
a574: a9 80 lda #$80
a576: 8d bb a5 sta briefing_loaded_flag ;files are loaded if we want to show it again
a579: 4c 55 a4 jmp MainMenuLoop
a57c: c9 d3 :CheckS cmp #“S” ;'S' (show scores)?
a57e: d0 14 bne :CheckCtrlS
; Show high scores.
a580: 20 5f ab jsr ClearHiresPage2
a583: 20 1d a8 jsr ShowHighScores
a586: 2c 55 c0 bit TXTPAGE2
a589: 20 4f ab jsr WaitForAnyKey
a58c: a9 00 lda #$00 ;we wiped hi-res page 2, so we need to re-load the
a58e: 8d bb a5 sta briefing_loaded_flag ; background graphic to show the briefing again
a591: 4c 5b a4 jmp :MenuLoop
a594: c9 93 :CheckCtrlS cmp #$93 ;Ctrl+S?
a596: d0 0b bne :CheckCtrlAJK
a598: ad bc a5 lda sound_enab_menu_flag
a59b: 49 80 eor #$80
a59d: 8d bc a5 sta sound_enab_menu_flag
a5a0: 4c 5b a4 jmp :MenuLoop
a5a3: a0 00 :CheckCtrlAJK ldy #$00 ;keyboard
a5a5: c9 8b cmp #$8b ;Ctrl+K?
a5a7: f0 0c beq :SetControls
a5a9: a0 01 ldy #$01 ;joystick
a5ab: c9 8a cmp #$8a ;Ctrl+J?
a5ad: f0 06 beq :SetControls
a5af: a0 80 ldy #$80 ;atari joyport
a5b1: c9 81 cmp #$81 ;Ctrl+A?
a5b3: d0 03 bne :Bail ;none of these, give up
a5b5: 8c f4 7c :SetControls sty control_mode
a5b8: 4c 5b a4 :Bail jmp :MenuLoop
briefing_loaded_flag
a5bb: 52 .dd1 $52 ;bool $00/$80: are briefing files loaded?
sound_enab_menu_flag
a5bc: 80 .dd1 $80 ;bool $00/$80: is sound enabled (for menu display)
• Clear variables
]txt_ptr .var $06 {addr/2}
a5bd: a9 20 PrintMenuText lda #$20 ;draw on hi-res page 1
a5bf: 85 ee sta HPAGE_EE
a5c1: a9 08 lda #8 ;line 2
a5c3: 8d f9 78 sta text_row
a5c6: a9 0f lda #15 ;centered
a5c8: 8d f8 78 sta text_col
a5cb: a9 d9 lda #<text00
a5cd: 85 06 sta ]txt_ptr
a5cf: a9 a5 lda #>text00
a5d1: 85 07 sta ]txt_ptr+1
a5d3: 20 e0 78 jsr PrintDciString
a5d6: 4c e2 a5 jmp :Next
a5d9: 53 54 45 4c+ text00 .dstr ‘STELLAR 7’
a5e2: a9 28 :Next lda #40 ;line 5
a5e4: 8d f9 78 sta text_row
a5e7: a9 02 lda #2 ;column 2
a5e9: 8d f8 78 sta text_col
a5ec: a9 fa lda #<text01
a5ee: 85 06 sta ]txt_ptr
a5f0: a9 a5 lda #>text01
a5f2: 85 07 sta ]txt_ptr+1
a5f4: 20 e0 78 jsr PrintDciString
a5f7: 4c 19 a6 jmp :Next
a5fa: 53 50 41 43+ text01 .dstr ‘SPACE BAR to begin the mission’
a619: a9 0d :Next lda #$0d ;print carriage return
a61b: 20 00 78 jsr PrintChar ;(next line, column 0)
a61e: a9 2c lda #<text02
a620: 85 06 sta ]txt_ptr
a622: a9 a6 lda #>text02
a624: 85 07 sta ]txt_ptr+1
a626: 20 e0 78 jsr PrintDciString
a629: 4c 51 a6 jmp :Next
a62c: 20 20 20 20+ text02 .dstr ‘ M for the mission briefing’
a651: a9 0d :Next lda #$0d ;CR
a653: 20 00 78 jsr PrintChar
a656: a9 64 lda #<text03
a658: 85 06 sta ]txt_ptr
a65a: a9 a6 lda #>text03
a65c: 85 07 sta ]txt_ptr+1
a65e: 20 e0 78 jsr PrintDciString
a661: 4c 87 a6 jmp :Next
a664: 20 20 20 20+ text03 .dstr ‘ S to display high scores’
a687: a9 58 :Next lda #88 ;row 11
a689: 8d f9 78 sta text_row
a68c: a9 02 lda #2 ;column 2
a68e: 8d f8 78 sta text_col
a691: a9 9f lda #<text04
a693: 85 06 sta ]txt_ptr
a695: a9 a6 lda #>text04
a697: 85 07 sta ]txt_ptr+1
a699: 20 e0 78 jsr PrintDciString
a69c: 4c c2 a6 jmp :Next
a69f: 43 54 52 4c+ text04 .dstr ‘CTRL-K to select KEYBOARD control’
a6c2: a9 0d :Next lda #$0d ;CR
a6c4: 20 00 78 jsr PrintChar
a6c7: a9 d5 lda #<text05
a6c9: 85 06 sta ]txt_ptr
a6cb: a9 a6 lda #>text05
a6cd: 85 07 sta ]txt_ptr+1
a6cf: 20 e0 78 jsr PrintDciString
a6d2: 4c fa a6 jmp :Next
a6d5: 20 20 43 54+ text05 .dstr ‘ CTRL-J to select JOYSTICK control’
a6fa: a9 0d :Next lda #$0d ;CR
a6fc: 20 00 78 jsr PrintChar
a6ff: a9 0d lda #<text06
a701: 85 06 sta ]txt_ptr
a703: a9 a7 lda #>text06
a705: 85 07 sta ]txt_ptr+1
a707: 20 e0 78 jsr PrintDciString
a70a: 4c 32 a7 jmp :Next
a70d: 20 20 43 54+ text06 .dstr ‘ CTRL-A to select ATARI JOYSTICK’
a732: a9 0d :Next lda #$0d ;CR
a734: 20 00 78 jsr PrintChar
a737: a9 45 lda #<text07
a739: 85 06 sta ]txt_ptr
a73b: a9 a7 lda #>text07
a73d: 85 07 sta ]txt_ptr+1
a73f: 20 e0 78 jsr PrintDciString
a742: 4c 6a a7 jmp :Next
a745: 20 20 43 54+ text07 .dstr ‘ CTRL-S to toggle the sound on/off’
a76a: a9 98 :Next lda #152 ;row 13
a76c: 8d f9 78 sta text_row
a76f: a9 0a lda #10 ;col 10
a771: 8d f8 78 sta text_col
a774: a9 82 lda #<text08
a776: 85 06 sta ]txt_ptr
a778: a9 a7 lda #>text08
a77a: 85 07 sta ]txt_ptr+1
a77c: 20 e0 78 jsr PrintDciString
a77f: 4c 8c a7 jmp :Next
a782: 43 4f 4e 54+ text08 .dstr ‘CONTROL: ’
a78c: ae f4 7c :Next ldx control_mode ;0=keyboard, 1=joystick, $80=joyport
a78f: f0 06 beq :AsIndex
a791: e0 01 cpx #$01
a793: f0 02 beq :AsIndex
a795: a2 02 ldx #$02 ;convert $80 -> 2
a797: bd ed a7 :AsIndex lda :ctrl_lookup_lo,x
a79a: 85 06 sta ]txt_ptr
a79c: bd f0 a7 lda :ctrl_lookup_hi,x
a79f: 85 07 sta ]txt_ptr+1
a7a1: 20 e0 78 jsr PrintDciString
a7a4: a9 0d lda #$0d ;CR
a7a6: 20 00 78 jsr PrintChar
a7a9: a9 0e lda #14 ;col 14
a7ab: 8d f8 78 sta text_col
a7ae: a9 bc lda #<text09
a7b0: 85 06 sta ]txt_ptr
a7b2: a9 a7 lda #>text09
a7b4: 85 07 sta ]txt_ptr+1
a7b6: 20 e0 78 jsr PrintDciString
a7b9: 4c c4 a7 jmp :Next
a7bc: 53 4f 55 4e+ text09 .dstr ‘SOUND: ’
a7c4: ad bc a5 :Next lda sound_enab_menu_flag
a7c7: 30 12 bmi LA7DB
a7c9: a9 d7 lda #$d7
a7cb: 85 06 sta ]txt_ptr
a7cd: a9 a7 lda #$a7
a7cf: 85 07 sta ]txt_ptr+1
a7d1: 20 e0 78 jsr PrintDciString
a7d4: 4c da a7 jmp LA7DA
a7d7: 4f 46 c6 .dstr ‘OFF’
a7da: 60 LA7DA rts
a7db: a9 e9 LA7DB lda #$e9
a7dd: 85 06 sta ]txt_ptr
a7df: a9 a7 lda #$a7
a7e1: 85 07 sta ]txt_ptr+1
a7e3: 20 e0 78 jsr PrintDciString
a7e6: 4c ec a7 jmp LA7EC
a7e9: 4f 4e a0 .dstr ‘ON ’
a7ec: 60 LA7EC rts
a7ed: f3 :ctrl_lookup_lo .dd1 <:text_kbd
a7ee: 01 .dd1 <:text_joystick
a7ef: 0f .dd1 <:text_atari
a7f0: a7 :ctrl_lookup_hi .dd1 >:text_kbd
a7f1: a8 .dd1 >:text_joystick
a7f2: a8 .dd1 >:text_atari
a7f3: 4b 45 59 42+ :text_kbd .dstr ‘KEYBOARD ’
a801: 4a 4f 59 53+ :text_joystick .dstr ‘JOYSTICK ’
a80f: 41 54 41 52+ :text_atari .dstr ‘ATARI JOYSTICK’
;
; Shows the high scores. Called from the main menu, and by the post-game score
; display code.
;
• Clear variables
]score_index .var $02 {addr/1}
]text_ptr .var $06 {addr/2}
a81d: a9 40 ShowHighScores lda #$40
a81f: 85 ee sta HPAGE_EE
a821: a9 08 lda #8 ;line 1
a823: 8d f9 78 sta text_row
a826: a9 0a lda #10 ;centered
a828: 8d f8 78 sta text_col
a82b: a9 39 lda #<text_hs0
a82d: 85 06 sta ]text_ptr
a82f: a9 a8 lda #>text_hs0
a831: 85 07 sta ]text_ptr+1
a833: 20 e0 78 jsr PrintDciString
a836: 4c 4b a8 jmp :Next
a839: 53 54 45 4c+ text_hs0 .dstr ‘STELLAR 7 MISSIONS’
a84b: a9 28 :Next lda #40 ;line 5
a84d: 8d f9 78 sta text_row
a850: a9 01 lda #$01
a852: 85 02 sta ]score_index
a854: 20 96 a8 jsr PrintOneScore ;print highest score
a857: a9 48 lda #72 ;line 9
a859: 8d f9 78 sta text_row
a85c: a9 02 lda #$02 ;continue with next-highest score
a85e: 85 02 sta ]score_index
a860: 20 96 a8 :Loop jsr PrintOneScore
a863: a9 0d lda #$0d ;CR
a865: 20 00 78 jsr PrintChar
a868: a9 0d lda #$0d ;CR
a86a: 20 00 78 jsr PrintChar
a86d: e6 02 inc ]score_index ;next score
a86f: a5 02 lda ]score_index
a871: c9 08 cmp #$08 ;scores 1-7 are kept
a873: 90 eb bcc :Loop
a875: 60 rts
;
; Prints a two-digit BCD value.
;
; For something like "0306", we want to print " 306", so the way we handle a
; zero nibble is context-sensitive.
;
; On entry:
; A-reg: value to print
; $03: 0 to print ' ', $80 to print '0' for zero nibbles
;
; On exit:
; $03: set to $80 if we output anything but ' ', so subsequent calls print '0'
;
]data_ptr .var $00 {addr/2}
]zero_flag .var $03 {addr/1}
]index_x16 .var $04 {addr/1}
a876: 48 PrintBcdByte pha
a877: 4a lsr A ;get the high nibble
a878: 4a lsr A
a879: 4a lsr A
a87a: 4a lsr A
a87b: 20 7f a8 jsr :PrintBcdNibble ;print it
a87e: 68 pla ;restore to get the low nibble
a87f: 29 0f :PrintBcdNibble and #$0f
a881: d0 04 bne :PrintDigit ;nonzero, print digit
a883: 24 03 bit ]zero_flag ;zero, print ' ' or '0'?
a885: 10 0a bpl :PrintSpc
a887: 09 b0 :PrintDigit ora #“0”
a889: 20 00 78 jsr PrintChar
a88c: a9 80 lda #$80 ;configure to print zeroes
a88e: 85 03 sta ]zero_flag
a890: 60 rts
a891: a9 a0 :PrintSpc lda #“ ”
a893: 4c 00 78 jmp PrintChar
;
; Print "#: <name> <score> <system>"
;
a896: a9 04 PrintOneScore lda #4 ;col 4
a898: 8d f8 78 sta text_col
a89b: a5 02 lda ]score_index
a89d: 09 b0 ora #“0” ;add '0' to score number
a89f: 20 00 78 jsr PrintChar
a8a2: a9 ba lda #“:”
a8a4: 20 00 78 jsr PrintChar
a8a7: a9 08 lda #8 ;col 8
a8a9: 8d f8 78 sta text_col
a8ac: a9 ad lda #>HIGH_SCORES
a8ae: 85 07 sta ]text_ptr+1
a8b0: a5 02 lda ]score_index ;set address to $ADx0, where x is 1-7
a8b2: 0a asl A
a8b3: 0a asl A
a8b4: 0a asl A
a8b5: 0a asl A
a8b6: 85 06 sta ]text_ptr
a8b8: 20 e0 78 jsr PrintDciString ;print string, which always ends in $A0
a8bb: a9 15 lda #21 ;col 21
a8bd: 8d f8 78 sta text_col
a8c0: a9 00 lda #$00
a8c2: 85 03 sta ]zero_flag
a8c4: a0 0e ldy #$0e
a8c6: b1 06 lda (]text_ptr),y ;get high byte of BCD high score
a8c8: 20 76 a8 jsr PrintBcdByte ; (comes last -- little-endian order)
a8cb: a0 0d ldy #$0d
a8cd: b1 06 lda (]text_ptr),y ;middle byte
a8cf: 20 76 a8 jsr PrintBcdByte
a8d2: a9 80 lda #$80 ;always print zeroes -- want "00" for no score
a8d4: 85 03 sta ]zero_flag
a8d6: a0 0c ldy #$0c
a8d8: b1 06 lda (]text_ptr),y ;low byte
a8da: 20 76 a8 jsr PrintBcdByte
a8dd: a9 1c lda #28 ;col 28
a8df: 8d f8 78 sta text_col
a8e2: a0 0f ldy #$0f
a8e4: b1 06 lda (]text_ptr),y ;get system number
a8e6: c9 08 cmp #$08 ;did we finish?
a8e8: b0 11 bcs :DraxonScore ;yes, show that specially
a8ea: 0a asl A ;multiply by 8 (the length of system name strings)
a8eb: 0a asl A
a8ec: 0a asl A
a8ed: 18 clc
a8ee: 69 15 adc #<system_names ;set up pointer into system name table
a8f0: 85 06 sta ]text_ptr
a8f2: a9 00 lda #$00
a8f4: 69 81 adc #>system_names
a8f6: 85 07 sta ]text_ptr+1
a8f8: 4c e0 78 jmp PrintDciString ;print it
a8fb: a9 11 :DraxonScore lda #<draxon_str ;print "DRAXON" instead
a8fd: 85 06 sta ]text_ptr
a8ff: a9 a9 lda #>draxon_str
a901: 85 07 sta ]text_ptr+1
a903: a9 80 lda #$80 ;make the text look slightly different
a905: 8d ff 78 sta text_eor_mask ; (affects the color on the flag glyph)
a908: 20 e0 78 jsr PrintDciString
a90b: a9 00 lda #$00
a90d: 8d ff 78 sta text_eor_mask
a910: 60 rts
a911: 44 52 41 58+ draxon_str .str ‘DRAXON ’
a918: ff .dd1 $ff ;glyph $7F is a colorful flag
;
; Shows the high scores along with the score of the game that just ended. If
; it's higher than one or more scores in the list, the player is prompted to
; enter their name, and the list is updated.
;
ShowPostGameScore
a919: 20 10 1c jsr COPY_HIRES_2TO1
a91c: 2c 54 c0 bit TXTPAGE1
a91f: 20 5f ab jsr ClearHiresPage2
a922: a9 ad lda #>HIGH_SCORES
a924: 85 01 sta ]data_ptr+1
a926: a9 07 lda #$07 ;slot #7 is lowest high score
a928: 20 cb aa jsr CompareScores ;check how ours compares
a92b: b0 48 bcs EnterHiScName ;our is greater, add it to the list
a92d: a9 08 lda #$08 ;we didn't make the list
a92f: 20 e5 aa jsr GenerateScoreLine
a932: a0 0b ldy #$0b
a934: b9 69 a9 :Loop lda your_score,y ;copy "YOUR SCORE" where the player name would be
a937: 99 80 ad sta score_eight,y ;temp area?
a93a: 88 dey
a93b: 10 f7 bpl :Loop
a93d: 20 1d a8 jsr ShowHighScores ;show the 7 saved high scores
a940: a9 b4 lda #$b4 ;move down a bit
a942: 8d f9 78 sta text_row
a945: a9 08 lda #$08
a947: 85 02 sta ]score_index
a949: 20 96 a8 jsr PrintOneScore ;show ours as the 8th score
a94c: a9 04 lda #$04
a94e: 8d f8 78 sta text_col
a951: a9 a0 lda #“ ”
a953: 20 00 78 jsr PrintChar
a956: a9 a0 lda #“ ”
a958: 20 00 78 jsr PrintChar
a95b: a9 b4 lda #$b4
a95d: 20 14 ab jsr DrawScoreBox
a960: 2c 55 c0 bit TXTPAGE2
a963: 2c 10 c0 bit KBDSTRB
a966: 4c 4f ab jmp WaitForAnyKey
a969: 59 4f 55 52+ your_score .dstr ‘YOUR SCORE: ’
a975: a9 06 EnterHiScName lda #$06 ;start at the 7th entry
a977: 85 02 sta ]score_index
a979: a5 02 :CmpLoop lda ]score_index
a97b: 20 cb aa jsr CompareScores ;compare current score to high score N
a97e: 90 04 bcc :FoundSlot
a980: c6 02 dec ]score_index ;try the next one
a982: d0 f5 bne :CmpLoop
a984: e6 02 :FoundSlot inc ]score_index
a986: a5 02 lda ]score_index
a988: 0a asl A ;multiply index by 16
a989: 0a asl A
a98a: 0a asl A
a98b: 0a asl A
a98c: 85 04 sta ]index_x16
; Move lower scores down.
a98e: a0 6f ldy #$6f
a990: b9 00 ad :CopyLoop lda HIGH_SCORES,y ;16 bytes per entry
a993: 99 10 ad sta HIGH_SCORES+16,y
a996: 88 dey
a997: c4 04 cpy ]index_x16
a999: b0 f5 bcs :CopyLoop
a99b: a5 02 lda ]score_index
a99d: 48 pha ;save for later
a99e: 20 e5 aa jsr GenerateScoreLine
a9a1: 20 1d a8 jsr ShowHighScores
a9a4: a9 b4 lda #$b4
a9a6: 8d f9 78 sta text_row
a9a9: a9 04 lda #$04
a9ab: 8d f8 78 sta text_col
a9ae: a9 bc lda #<enter_name ;show "enter name" prompt
a9b0: 85 06 sta ]text_ptr
a9b2: a9 a9 lda #>enter_name
a9b4: 85 07 sta ]text_ptr+1
a9b6: 20 e0 78 jsr PrintDciString
a9b9: 4c dc a9 jmp :SaveScores
a9bc: 45 6e 74 65+ enter_name .dstr ‘Enter your name and press RETURN’
]filename_ptr .var $02 {addr/2}
a9dc: 68 :SaveScores pla ;get score index
a9dd: 48 pha ;save it again
a9de: aa tax
a9df: bd c3 aa lda score_rows,x ;get hi-res row for this score
a9e2: 20 14 ab jsr DrawScoreBox ;draw a box around it
a9e5: a9 ad lda #>HIGH_SCORES
a9e7: 85 01 sta ]data_ptr+1
a9e9: a9 40 lda #$40
a9eb: 85 ee sta HPAGE_EE
a9ed: 68 pla ;get score index
a9ee: 2c 55 c0 bit TXTPAGE2
a9f1: 20 50 aa jsr InputHiScName ;input name
; Write the high score data to disk. The file write routine always copies 252
; bytes, starting 4 bytes into the buffer ($04-ff). The first 4 bytes are a
; place-holder for the 'B' file header.
a9f4: a9 4b lda #<high_dot
a9f6: 85 02 sta ]filename_ptr
a9f8: a9 aa lda #>high_dot
a9fa: 85 03 sta ]filename_ptr+1
a9fc: a9 00 lda #<HIGH_SCORES
a9fe: 85 00 sta ]data_ptr
aa00: a9 ad lda #>HIGH_SCORES
aa02: 85 01 sta ]data_ptr+1
aa04: 20 06 b0 jsr DISK_WRITE_FILE ;disk write
aa07: 20 10 1c jsr COPY_HIRES_2TO1
aa0a: 2c 54 c0 bit TXTPAGE1
aa0d: a9 b4 lda #$b4
aa0f: 8d f9 78 sta text_row
aa12: a9 04 lda #$04
aa14: 8d f8 78 sta text_col
aa17: a9 25 lda #<spaces32
aa19: 85 06 sta ]text_ptr
aa1b: a9 aa lda #>spaces32
aa1d: 85 07 sta ]text_ptr+1
aa1f: 20 e0 78 jsr PrintDciString
aa22: 4c 45 aa jmp :WaitForKey
aa25: 20 20 20 20+ spaces32 .dstr ‘ ’
aa45: 2c 55 c0 :WaitForKey bit TXTPAGE2
aa48: 4c 4f ab jmp WaitForAnyKey
aa4b: c8 c9 c7 c8+ high_dot .dstr “HIGH.”
;
; Input a name for the high score list.
;
; On entry:
; A-reg: high score index (1-7)
;
]index .var $02 {addr/1}
aa50: 2c 10 c0 InputHiScName bit KBDSTRB
aa53: aa tax
aa54: bd c3 aa lda score_rows,x ;get hi-res row
aa57: 8d f9 78 sta text_row
aa5a: 8a txa
aa5b: 0a asl A ;multiply by 16
aa5c: 0a asl A
aa5d: 0a asl A
aa5e: 0a asl A
aa5f: 85 00 sta ]data_ptr ;pointer into HIGH_SCORES table
aa61: a9 00 :StartOfLine lda #$00 ;start at the start
aa63: 85 02 sta ]index
aa65: a9 7f :InputLoop lda #$7f ;inverse text for cursor block
aa67: 8d ff 78 sta text_eor_mask
aa6a: 20 b4 aa jsr :PrintCurChar ;print char under cursor
aa6d: a9 00 lda #$00 ;normal text
aa6f: 8d ff 78 sta text_eor_mask
aa72: 20 4f ab jsr WaitForAnyKey
aa75: c9 8d cmp #$8d ;enter? (done)
aa77: f0 2f beq :PrintAndBail
aa79: c9 88 cmp #$88 ;Ctrl+H? (backspace)
aa7b: d0 0c bne :NotBacksp
aa7d: a5 02 lda ]index
aa7f: f0 e4 beq :InputLoop
aa81: 20 b4 aa jsr :PrintCurChar
aa84: c6 02 dec ]index
aa86: 4c 65 aa jmp :InputLoop
aa89: c9 95 :NotBacksp cmp #$95 ;Ctrl+U? (forward arrow)
aa8b: d0 0e bne :NotFwd
aa8d: 20 b4 aa :AdvanceIndex jsr :PrintCurChar
aa90: a4 02 ldy ]index ;on last char?
aa92: c0 0b cpy #$0b
aa94: f0 cb beq :StartOfLine ;yes, don't advance
aa96: e6 02 inc ]index ;no, bump index
aa98: 4c 65 aa jmp :InputLoop
aa9b: c9 a0 :NotFwd cmp #$a0 ;some other control character?
aa9d: 90 c6 bcc :InputLoop ;yes, ignore it
aa9f: 29 7f and #$7f ;no, keep it, high bit clear
aaa1: a4 02 ldy ]index
aaa3: 91 00 sta (]data_ptr),y
aaa5: 4c 8d aa jmp :AdvanceIndex
aaa8: 20 b4 aa :PrintAndBail jsr :PrintCurChar ;print the last char again, in normal text
aaab: a0 0b ldy #$0b ;flip the high bit on the last character
aaad: b1 00 lda (]data_ptr),y ; to make it a DCI string
aaaf: 09 80 ora #$80
aab1: 91 00 sta (]data_ptr),y
aab3: 60 rts
aab4: 18 :PrintCurChar clc
aab5: a5 02 lda ]index
aab7: 69 08 adc #$08
aab9: 8d f8 78 sta text_col
aabc: a4 02 ldy ]index
aabe: b1 00 lda (]data_ptr),y
aac0: 4c 00 78 jmp PrintChar
aac3: 00 28 48 58+ score_rows .bulk $00,$28,$48,$58,$68,$78,$88,$98
;
; Compare current score against a stored high score.
;
; On entry:
; A-reg: high score slot (1-7)
;
; On exit:
; Carry set if stored score is less than current score
;
aacb: 0a CompareScores asl A ;calc low byte of address
aacc: 0a asl A ;(page-aligned, 16 bytes per entry)
aacd: 0a asl A
aace: 0a asl A
aacf: 85 00 sta ]data_ptr
aad1: a0 0c ldy #$0c
aad3: ad f8 81 lda score
aad6: d1 00 cmp (]data_ptr),y
aad8: c8 iny
aad9: ad f9 81 lda score+1
aadc: f1 00 sbc (]data_ptr),y
aade: c8 iny
aadf: ad fa 81 lda score+2
aae2: f1 00 sbc (]data_ptr),y
aae4: 60 rts
;
; Writes a new high score entry, with the score and blank spaces where the name
; will go.
;
; On entry:
; A-reg: score slot (1-7)
;
GenerateScoreLine
aae5: 0a asl A ;calc low byte of address
aae6: 0a asl A
aae7: 0a asl A
aae8: 0a asl A
aae9: 85 00 sta ]data_ptr
aaeb: a0 0b ldy #11 ;init to spaces
aaed: a9 a0 lda #“ ” ;last one is high ASCII
aaef: 91 00 sta (]data_ptr),y
aaf1: a0 0a ldy #10
aaf3: a9 20 lda #‘ ’
aaf5: 91 00 :SpcLoop sta (]data_ptr),y
aaf7: 88 dey
aaf8: 10 fb bpl :SpcLoop
aafa: a0 0c ldy #$0c
aafc: ad f8 81 lda score ;copy the score
aaff: 91 00 sta (]data_ptr),y
ab01: c8 iny
ab02: ad f9 81 lda score+1
ab05: 91 00 sta (]data_ptr),y
ab07: c8 iny
ab08: ad fa 81 lda score+2
ab0b: 91 00 sta (]data_ptr),y
ab0d: c8 iny
ab0e: ad 84 85 lda system_num ;set highest system reached
ab11: 91 00 sta (]data_ptr),y
ab13: 60 rts
;
; Draws a box around a high score entry.
;
; On entry:
; A-reg: score slot * 16
;
• Clear variables
]x0 .var $00 {addr/1}
]y0 .var $01 {addr/1}
]x1 .var $02 {addr/1}
]y1 .var $03 {addr/1}
]minus4 .var $e0 {addr/1}
]plus10 .var $e1 {addr/1}
ab14: aa DrawScoreBox tax
ab15: 38 sec
ab16: e9 04 sbc #4
ab18: 85 e0 sta ]minus4
ab1a: 8a txa
ab1b: 18 clc
ab1c: 69 0a adc #10
ab1e: 85 e1 sta ]plus10
ab20: a5 e0 lda ]minus4
ab22: 20 40 ab jsr :DrawHoriz
ab25: a5 e1 lda ]plus10
ab27: 20 40 ab jsr :DrawHoriz
ab2a: a9 18 lda #24
ab2c: 20 31 ab jsr :DrawVert
ab2f: a9 fe lda #254
ab31: 85 00 :DrawVert sta ]x0
ab33: 85 02 sta ]x1
ab35: a5 e0 lda ]minus4
ab37: 85 01 sta ]y0
ab39: a5 e1 lda ]plus10
ab3b: 85 03 sta ]y1
ab3d: 4c 00 10 jmp DRAW_LINE
ab40: 85 01 :DrawHoriz sta ]y0
ab42: 85 03 sta ]y1
ab44: a9 18 lda #24
ab46: 85 00 sta ]x0
ab48: a9 fe lda #254
ab4a: 85 02 sta ]x1
ab4c: 4c 00 10 jmp DRAW_LINE
;
; Waits for a key to be hit, then clears the keyboard strobe. Use for a "press
; any key to continue" situation.
;
ab4f: ad 00 c0 WaitForAnyKey lda KBD
ab52: 10 fb bpl WaitForAnyKey
ab54: 2c 10 c0 bit KBDSTRB
ab57: 60 rts
ab58: a9 40 ClearHiresPage1 lda #$40 ;erase from $2000-3FFF
ab5a: a2 20 ldx #$20
ab5c: 4c 63 ab jmp ClearMemory
;
; Clears hi-res page 2.
;
ab5f: a9 60 ClearHiresPage2 lda #$60 ;erase from $4000-5FFF
ab61: a2 40 ldx #$40
;
; Zeroes out 256-byte pages of memory.
;
; On entry:
; X-reg: start page
; A-reg: end page
;
]ptr .var $00 {addr/2}
]end_page .var $02 {addr/1}
ab63: 85 02 ClearMemory sta ]end_page
ab65: a0 00 ldy #$00
ab67: 84 00 sty ]ptr
ab69: a9 00 lda #$00
ab6b: 86 01 :OuterLoop stx ]ptr+1
ab6d: 91 00 :InnerLoop sta (]ptr),y
ab6f: c8 iny
ab70: d0 fb bne :InnerLoop
ab72: e8 inx
ab73: e4 02 cpx ]end_page
ab75: 90 f4 bcc :OuterLoop
ab77: 60 :Done rts
;
; Inverts the colors in the viewport. This is done to flash the screen white
; after being hit.
;
; The viewport is 157 lines high, spanning lines [33,189], and 32 bytes across.
;
; The top and bottom lines are not inverted.
;
ab78: a2 22 InvertViewport ldx #34 ;start on line 34, so we only do 156 lines
ab7a: a9 7f lda #$7f ; (156 divides evenly by 6)
ab7c: 8d 36 ac sta :_EorVal5+1
ab7f: e0 b8 :OuterLoop cpx #184 ;line 184 is the first line of the last set of 6
ab81: 90 07 bcc :Configure6
ab83: d0 f2 bne :Done
ab85: a9 00 lda #$00 ;don't invert very last line
ab87: 8d 36 ac sta :_EorVal5+1
; Configure 6 load/eor/store statements.
ab8a: bd 00 15 :Configure6 lda HIRES_ADDR_LO,x
ab8d: 8d 0b ac sta :_ClearLoop+1
ab90: 8d 10 ac sta :_Store0+1
ab93: bd 00 16 lda HIRES_ADDR_HI,x
ab96: 05 32 ora hpage
ab98: 8d 0c ac sta :_ClearLoop+2
ab9b: 8d 11 ac sta :_Store0+2
ab9e: e8 inx
ab9f: bd 00 15 lda HIRES_ADDR_LO,x
aba2: 8d 13 ac sta :_Load1+1
aba5: 8d 18 ac sta :_Store1+1
aba8: bd 00 16 lda HIRES_ADDR_HI,x
abab: 05 32 ora hpage
abad: 8d 14 ac sta :_Load1+2
abb0: 8d 19 ac sta :_Store1+2
abb3: e8 inx
abb4: bd 00 15 lda HIRES_ADDR_LO,x
abb7: 8d 1b ac sta :_Load2+1
abba: 8d 20 ac sta :_Store2+1
abbd: bd 00 16 lda HIRES_ADDR_HI,x
abc0: 05 32 ora hpage
abc2: 8d 1c ac sta :_Load2+2
abc5: 8d 21 ac sta :_Store2+2
abc8: e8 inx
abc9: bd 00 15 lda HIRES_ADDR_LO,x
abcc: 8d 23 ac sta :_Load3+1
abcf: 8d 28 ac sta :_Store3+1
abd2: bd 00 16 lda HIRES_ADDR_HI,x
abd5: 05 32 ora hpage
abd7: 8d 24 ac sta :_Load3+2
abda: 8d 29 ac sta :_Store3+2
abdd: e8 inx
abde: bd 00 15 lda HIRES_ADDR_LO,x
abe1: 8d 2b ac sta :_Load4+1
abe4: 8d 30 ac sta :_Store4+1
abe7: bd 00 16 lda HIRES_ADDR_HI,x
abea: 05 32 ora hpage
abec: 8d 2c ac sta :_Load4+2
abef: 8d 31 ac sta :_Store4+2
abf2: e8 inx
abf3: bd 00 15 lda HIRES_ADDR_LO,x
abf6: 8d 33 ac sta :_Load5+1
abf9: 8d 38 ac sta :_Store5+1
abfc: bd 00 16 lda HIRES_ADDR_HI,x
abff: 05 32 ora hpage
ac01: 8d 34 ac sta :_Load5+2
ac04: 8d 39 ac sta :_Store5+2
ac07: e8 inx
; Drawing loop.
ac08: a0 20 ldy #32 ;start on the right edge (column 32)
ac0a: b9 00 20 :_ClearLoop lda $2000,y
ac0d: 49 7f eor #$7f
ac0f: 99 00 20 :_Store0 sta $2000,y
ac12: b9 00 20 :_Load1 lda $2000,y
ac15: 49 7f eor #$7f
ac17: 99 00 20 :_Store1 sta $2000,y
ac1a: b9 00 20 :_Load2 lda $2000,y
ac1d: 49 7f eor #$7f
ac1f: 99 00 20 :_Store2 sta $2000,y
ac22: b9 00 20 :_Load3 lda $2000,y
ac25: 49 7f eor #$7f
ac27: 99 00 20 :_Store3 sta $2000,y
ac2a: b9 00 20 :_Load4 lda $2000,y
ac2d: 49 7f eor #$7f
ac2f: 99 00 20 :_Store4 sta $2000,y
ac32: b9 00 20 :_Load5 lda $2000,y
ac35: 49 7f :_EorVal5 eor #$7f
ac37: 99 00 20 :_Store5 sta $2000,y
ac3a: 88 dey ;next column
ac3b: 98 tya
ac3c: 29 07 and #$07 ;every 8 iterations...
ac3e: d0 ca bne :_ClearLoop
ac40: 20 40 a0 jsr UpdateSound ; ...update the sound effects
ac43: c0 00 cpy #$00 ;reached column 0?
ac45: d0 c3 bne :_ClearLoop ;not yet
ac47: 4c 7f ab jmp :OuterLoop ;go do another set of 6 lines
ac4a: 00 00 ff ff+ .align $0100 (182 bytes)