back to project page

ROCK2 Disassembly

                   ********************************************************************************
                   * 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

                   vis
71b4: 01 02 04 08+ hires_pixel     .bulk   $01,$02,$04,$08,$10,$20,$40 ;single-pixel mask

                   vis
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.
                   ; 
                   vis
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.
                   ; 
                   vis vis
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

                   vis
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

                   vis
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

                   vis
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)

Symbol Table

ClearViewport$7400
DrawMountains$7000
DrawStars$70b0
GameEntry$a440
StartMission$8750
UpdateSound$a040