(back to project page)

Centipede_rev4 Disassembly

                   ********************************************************************************
                   * Centipede (rev 4)                                                            *
                   * By Dona Bailey and Ed Logg                                                   *
                   * Copyright 1980 Atari, Inc.                                                   *
                   ********************************************************************************
                   * Disassembly by Andy McFadden, using 6502bench SourceGen v1.8.2.              *
                   * Last updated 2022/03/10                                                      *
                   *                                                                              *
                   * The binary used is a concatenation of the four ROMs that are addressable by  *
                   * the 6502, followed by the two ROMs that hold the graphics data.              *
                   *                                                                              *
                   * For this project, I had access to the original Centipede source code when I  *
                   * started (https://github.com/historicalsource/centipede).  The assembler used *
                   * at Atari appears to require all upper case, with labels limited to 6         *
                   * characters, which can be a little terse.  I've renamed most labels for       *
                   * clarity, but retained the original subroutine names in comments to make it   *
                   * easy to match up with the source.  The project file also has Notes showing   *
                   * where each source file starts.                                               *
                   *                                                                              *
                   * The original sources refer to the spider as "bug" and the flea as "ant".     *
                   * Comments and labels here use spider/flea.                                    *
                   *                                                                              *
                   * Thanks to:                                                                   *
                   *  + The various MAME contributors.                                            *
                   *                                                                              *
                   * POKEY register descriptions from https://en.wikipedia.org/wiki/POKEY .       *
                   ********************************************************************************
                   * Memory map in a nutshell:                                                    *
                   *   $0000-01ff: RAM                                                            *
                   *   $0200-03ff: unused (MAME driver says RAM, but game doesn't use it)         *
                   *   $0400-07bf: playfield RAM                                                  *
                   *   $07c0-07cf: motion object picture                                          *
                   *   $07d0-07df: motion object vertical posn                                    *
                   *   $07e0-07ef: motion object horizontal position                              *
                   *   $07f0-07ff: motion object colors                                           *
                   *   $0800-2400: I/O ports (some write-only locations overlap with ROM)         *
                   *   $2000-3fff: game ROM (4x2KB)                                               *
                   *   (non-addressable): 2x2KB graphics ROM                                      *
                   *                                                                              *
                   * I/O port definitions:                                                        *
                   *                                                                              *
                   * DSW1 / N9 ($0800): CDBBHHLL                                                  *
                   *   C=minimum credits (1/2)                                                    *
                   *   D=difficulty (0=hard, 1=easy)                                              *
                   *   BB=bonus life (10/12/15/20K pts)                                           *
                   *   HH=lives per game (2/3/4/5)                                                *
                   *   LL=language (00=en, 01=de, 10=fr, 11=es)                                   *
                   *                                                                              *
                   * DSW2 / N8 ($0801): BBBLRRCC                                                  *
                   *   BBB=bonus coins for multiple coin drops                                    *
                   *   L=left coin multiplier                                                     *
                   *   RR=right coin multiplier                                                   *
                   *   CC=coins per credit (00=free play)                                         *
                   *   (rev4: LRR specifies the play time in minutes, 0-7)                        *
                   *                                                                              *
                   * IN0 ($0c00): BVTCBBBB                                                        *
                   *   B/BBBB=horizontal trackball inputs (hi bit direction, low bits magnitude)  *
                   *   V=VBLANK flag (1=in vblank)                                                *
                   *   T=self-test switch (0=on)                                                  *
                   *   C=cocktail cabinet (1=cocktail)                                            *
                   *                                                                              *
                   * IN1 ($0c01): CCCSGF21                                                        *
                   *   CCC=R,C,L coin switches (0=on)                                             *
                   *   S=slam (0=on)                                                              *
                   *   G=player 2 fire (0=on)                                                     *
                   *   F=player 1 fire (0=on)                                                     *
                   *   2=player 2 start (0=on)                                                    *
                   *   1=player 1 start (0=on)                                                    *
                   *                                                                              *
                   * IN2 ($0c02): B???BBBB                                                        *
                   *   B/BBBB=vertical trackball inputs                                           *
                   *                                                                              *
                   * IN3 ($0c03): JJJJKKKK                                                        *
                   *   JJJJ=player 1 joystick (R,L,U,D; 0=on)                                     *
                   *   KKKK=player 2 joystick                                                     *
                   *                                                                              *
                   * POKEY ($1000-100f)                                                           *
                   *                                                                              *
                   * Color palette ($1404-140f):                                                  *
                   *   $1404-1407: playfield colors 0-3                                           *
                   *   $140d-140f: motion object colors 1-3 (0 is transparent)                    *
                   *   Color is %xxxxDBGR, where 0=on.                                            *
                   *                                                                              *
                   * EAROM write ($1600-163f)                                                     *
                   * EAROM control ($1680): ????EOOC                                              *
                   *   E=chip enable (1=on)                                                       *
                   *   OO=operation (00=read, 01=write, 11=erase)                                 *
                   *   C=CK clock pulse (1=read enable)                                           *
                   * EAROM read ($1700-173f)                                                      *
                   *                                                                              *
                   * Coin counters and start LEDs ($1c00-1c07)                                    *
                   ********************************************************************************
                   * The display is generated from the contents of the playfield tiles and the    *
                   * sprites.  Playfield RAM starts at $400, which would be the top-left corner   *
                   * of the screen if the display weren't rotated 90 degrees counter-clockwise.   *
                   * Instead, $400 is the bottom-left corner, and $401 is one tile up.  For       *
                   * motion objects, (0,0) is at the bottom right, coordinates increasing as      *
                   * objects move upward and leftward.  Coordinates specify placement of the      *
                   * bottom-left corner of the motion object picture.                             *
                   *                                                                              *
                   * The playfield is 30x32, so each column is 32 bytes.  The top row is used for *
                   * scores, and the bottom row is unused, making the active playfield 30x30.     *
                   *                                                                              *
                   * For player 2 on a cocktail cabinet, the display must be rotated 180 degrees, *
                   * which can be done by flipping vertically and horizontally.  This is done in  *
                   * software, which must reposition all objects and set the flip flags on the    *
                   * tiles and sprites.                                                           *
                   *                                                                              *
                   * For both playfield tiles and motion objects, the picture is specified by the *
                   * low 6 bits ($00-$3f).  Setting bit 6 ($40) flips the image vertically,       *
                   * setting bit 7 ($80) flips it horizontally.                                   *
                   *                                                                              *
                   * Motion object indices:                                                       *
                   *   $00-0b: centipede segments                                                 *
                   *   $0c: flea / scorpion                                                       *
                   *   $0d: spider                                                                *
                   *   $0e: shot (from player)                                                    *
                   *   $0f: gun (player)                                                          *
                   *                                                                              *
                   * When stored in RAM, some of the bits in the picture number may be used for   *
                   * other purposes.  For example, the horizontal flip flags can be determined by *
                   * the direction of motion while copying the picture to the hardware, so bit 7  *
                   * can be given a different meaning.  For centipede segments, the source code   *
                   * sets bit 5 ($20) to indicate a poisoned head, and bit 6 ($40) to indicate    *
                   * body vs. head.                                                               *
                   ********************************************************************************
                   * Minor gripe about the code: the authors set the player number to 1 or 2,     *
                   * rather than 0 or 1, so instead of "LDA THING2,X" they had to do "LDA THING2- *
                   * 1,X" everywhere.  The disassembler tends to see this as an access to the     *
                   * second part of the previous two-byte item, rather than the first part of the *
                   * following two-byte item, and generates "LDA THING1+1,X" instead.  This       *
                   * required manually entering the correct symbol (as well as a lot of "-1" in   *
                   * the original sources).                                                       *
                   ********************************************************************************
                   NCENT           .eq     12     {const}    ;number of centipede segment slots
                   frame_ctr       .eq     $00    {addr/2}   ;frame counter
                   hs_scores       .eq     $02    {addr/24}  ;high scores (3 BCD digits, little-endian)
                   hs_initials     .eq     $1a    {addr/24}  ;high score initials
                   obst_ptr        .eq     $32    {addr/2}   ;addr of obstacle position
                   mobj_pict       .eq     $34    {addr/16}  ;motion object picture
                   mobj_pict_flea  .eq     $40
                   mobj_pict_spdr  .eq     $41
                   mobj_pict_shot  .eq     $42
                   mobj_pict_plyr  .eq     $43
                   mobj_hvel       .eq     $44    {addr/16}  ;motion obj horizontal velocity (+=left, -=right)
                   mobj_hvel_flea  .eq     $50               ;only used for scorpion
                   mobj_hvel_spdr  .eq     $51               ;reversed (+=right, -=left)
                   mobj_hvel_plyr  .eq     $53
                   mobj_horz       .eq     $54    {addr/16}  ;motion object horizontal position
                   mobj_horz_flea  .eq     $60
                   mobj_horz_spdr  .eq     $61
                   mobj_horz_shot  .eq     $62
                   mobj_horz_plyr  .eq     $63
                   mobj_vert       .eq     $64    {addr/16}  ;motion object vertical position
                   mobj_vert_flea  .eq     $70
                   mobj_vert_spdr  .eq     $71
                   mobj_vert_shot  .eq     $72
                   mobj_vert_plyr  .eq     $73
                   mobj_vvel       .eq     $74    {addr/16}  ;motion obj vertical velocity (rev: -=up, +=down)
                   mobj_vvel_flea  .eq     $80
                   mobj_vvel_spdr  .eq     $81
                   mobj_vvel_plyr  .eq     $83
                   plyr_hpos_frac  .eq     $84               ;player horz position fraction (00/80)
                   plyr_vpos_frac  .eq     $85               ;player vert position fraction (00/80)
                   attract_mode    .eq     $86               ;bool 00/ff: in attract mode?
                   delay_ctr       .eq     $87               ;action pause, in frames
                   cur_player      .eq     $88               ;current player (1/2)
                   num_players     .eq     $89               ;# of players in current / prev game (1/2)
                   irq_sync        .eq     $8a               ;incr by IRQ at VBLANK, reduced by main
                   temp1           .eq     $8b    {addr/2}   ;general use
                   temp2           .eq     $8d    {addr/2}   ;general use
                   temp3           .eq     $8f    {addr/2}   ;general use
                   out_ptr         .eq     $91    {addr/2}   ;tile output pointer, e.g. for messages
                   live_seg_count  .eq     $95    {addr/2}   ;(per plyr) # of live segments
                   new_head_flag   .eq     $97               ;bool 00/ff: need to add new head?
                   irqtmp_pict     .eq     $98               ;IRQ temporary
                   irqtmp_hflip    .eq     $99               ;IRQ temporary to hold horz flip
                   fire_debounce   .eq     $9a               ;fire switch debounce (entering initials)
                   plyr_cent_len   .eq     $9b    {addr/2}   ;(per plyr) initial length of centipede
                   plyr_cent_spd   .eq     $9d    {addr/2}   ;(per plyr) speed of centipede
                   spider_cd_ctr   .eq     $9f               ;cooldown for a new spider
                   new_head_ctr    .eq     $a0               ;countdown for a new head
                   spider_cooldown .eq     $a1               ;spider move / resurrect cooldown
                   plyr_head_ctr_init .eq  $a2    {addr/2}   ;(per plyr) initial value for new head counter
                   initial_lives   .eq     $a4               ;starting number of lives
                   plyr_lives      .eq     $a5    {addr/2}   ;(per plyr) # of lives left
                   game_over_flag  .eq     $a7               ;bool 00/01: are we showing "game over"
                   plyr_score0     .eq     $a8    {addr/2}   ;(per plyr) low byte of score
                   plyr_score1     .eq     $aa    {addr/2}   ;(per plyr) middle byte of score
                   plyr_score2     .eq     $ac    {addr/2}   ;(per plyr) high byte of score (rev4: pl2 is timer)
                   plyr_bonus_mid  .eq     $ae    {addr/2}   ;(per plyr) bonus score tracker, mid byte
                   plyr_bonus_hi   .eq     $b0    {addr/2}   ;(per plyr) bonus score tracker, hi byte
                   cn_expl_sound_idx .eq   $b2               ;centipede exploding sound index
                   cn_move_sound_idx .eq   $b3               ;centipede moving sound index
                   shot_sound_idx  .eq     $b4               ;shot sound index
                   spider_sound_idx .eq    $b5               ;spider sound index
                   bonus_sound_idx .eq     $b6               ;bonus life sound index
                   plexpl_sound_idx .eq    $b7               ;player explosion sound index
                   scorp_sound_idx .eq     $b8               ;scorpion sound index
                   tball_read_horz .eq     $b9               ;trackball horz reading (vert at +2)
                   tball_chng_horz .eq     $ba               ;trackball horz change (vert at +2)
                   tball_read_vert .eq     $bb               ;trackball vert reading
                   tball_prev_horz .eq     $bd               ;previous trackball horz reading (vert at +2)
                   spider_prev_hdir .eq    $be               ;previous horizontal dir for spider
                   tball_prev_vert .eq     $bf               ;previous trackball vertical reading
                   initial_entry_num .eq   $c0               ;which initial we're working on (0-2)
                   plyr_hs_init_slot .eq   $c1    {addr/2}   ;(per plyr) high score input slot index (or $ff)
                   leg_color       .eq     $c3    {addr/2}   ;current color of centipede legs
                   coin_drop_ctr   .eq     $c5    {addr/3}   ;coin drop management
                   credit_count    .eq     $c8               ;number of credits
                   coins_inserted  .eq     $c9               ;number of coins inserted
                   bonus_coin_state .eq    $ca               ;tracks coins being added, for bonus
                   bonus_coin_ctr  .eq     $cb               ;bonus coins awarded
                   coin_timer2     .eq     $cc    {addr/3}   ;coin slot timers, 1 per slot
                   coin_timer1     .eq     $cf    {addr/3}   ;coin slot timers, 1 per slot
                   slam_tone_ctr1  .eq     $d2               ;react to slam switch
                   n8_nomult       .eq     $d3               ;N8 switches, with slot mults masked out
                   irq_counter     .eq     $d4               ;counts every IRQ, used as time multiplier
                   test_col_irq_ctr .eq    $d5               ;color bar test IRQ counter
                   plyr_switch_flag .eq    $d6               ;bool 00/f9: flag for switching players
                   spdr_pts_mobj   .eq     $d7               ;motion obj index for spider pts (300/600/900)
                   plyr_low_mush   .eq     $d8    {addr/2}   ;(per plyr) # of mushrooms on lower part of screen
                   mush_ptr        .eq     $da    {addr/2}   ;pointer used when restoring shrooms (acts as flag)
                   two_cred_msg_flag .eq   $dc               ;bool 00/NZ: show "two credit min" msg?
                   test_input_arr  .eq     $dd    {addr/9}   ;holds inputs for self-test display
                   test_audio_chan .eq     $e6               ;self-test: which audio channel is used (0/2/4/6)
                   test_mobj       .eq     $e7               ;self-test: current motion object ($00-0f)
                   test_bk_color   .eq     $e8               ;self-test: background color
                   test_plf_color  .eq     $e9               ;self-test: playfield color
                   test_st1_debounce .eq   $ea               ;self-test: player 1 start switch debounce
                   test_st2_debounce .eq   $eb               ;self-test: player 2 start switch debounce
                   test_fire1_debounce .eq $ec               ;self-test: player 1 fire debounce
                   ckp2_indic      .eq     $ee               ;bool 00/80: cocktail player 2? (81/82)
                   ckp2_c0         .eq     $ef               ;cocktail player 2: %1100 0000
                   ckp2_f8         .eq     $f0               ;cocktail player 2: %1111 1000
                   ckp2_e0         .eq     $f1               ;cocktail player 2: %1110 0000
                   ckp2_40         .eq     $f2               ;cocktail player 2: %0100 0000
                   ckp2_ff         .eq     $f3               ;cocktail player 2: %1111 1111
                   ckp2_fe         .eq     $f4               ;cocktail player 2: %1111 1110
                   ckp2_bf         .eq     $f5               ;cocktail player 2: %1011 1111
                   ckp2_3f         .eq     $f6               ;cocktail player 2: %0011 1111
                   ckp2_03         .eq     $f7               ;cocktail player 2: %0000 0011
                   ckp2_fc         .eq     $f8               ;cocktail player 2: %1111 1100
                   earom_cur_addr  .eq     $f9               ;address of current EAROM op (0-63 or $ff)
                   earom_cur_op    .eq     $fa               ;current EAROM operation
                   game_time       .eq     $fb    {addr/2}   ;total game uptime, in (256/60Hz) units, BCD
                   dsw_n9_copy     .eq     $fd               ;copy of the N9 switches, read at reset
                   cksum_zero      .eq     $fe               ;code checksum, expected to be zero
                   one             .eq     $ff               ;holds 1 if copyright text is unmodified
                   screen_store_arr .eq    $0100  {addr/120} ;storage for screen swapping (30x32/8=120)
                   ea_copy         .eq     $0178  {addr/64}  ;RAM copy of EAROM data
                   ea_options      .eq     $018a             ;(+18) switch settings for high scores
                   ea_games        .eq     $018b  {addr/3}   ;(+19) number of games (BCD)
                   ea_time         .eq     $018e  {addr/4}   ;(+22) total play time (in ~4-sec units)
                   ea_checksum     .eq     $01b5             ;(+61) checksum for first 62 bytes
                   joy_last_horz   .eq     $01b8             ;previous horizontal reading
                   joy_last_vert   .eq     $01b9             ;previous vertical reading
                   PLAYFIELD       .eq     $0400  {addr/960} ;30x32 playfield tiles
                   MOBJ_PICT       .eq     $07c0  {addr/16}  ;motion object picture number
                   MOBJ_HORZ       .eq     $07d0  {addr/16}  ;motion object horizontal position
                   MOBJ_VERT       .eq     $07e0  {addr/16}  ;motion object vertical position
                   MOBJ_COLOR      .eq     $07f0  {addr/16}  ;motion object color ($00-3f)
                   DSW_N9          .eq     $0800             ;R N9 DIP switches
                   DSW_N8          .eq     $0801             ;R N8 DIP switches
                   IN0             .eq     $0c00             ;R inputs (trackball, config, switches)
                   IN1             .eq     $0c01             ;R inputs (coins, buttons)
                   IN2             .eq     $0c02             ;R inputs (vertical trackball)
                   IN3             .eq     $0c03             ;R inputs (joystick)
                   POKEY_AUDF1     .eq     $1000             ;W audio channel 1 frequency
                   POKEY_AUDC1     .eq     $1001             ;W audio channel 1 control
                   POKEY_AUDF2     .eq     $1002             ;W audio channel 2 frequency
                   POKEY_AUDC2     .eq     $1003             ;W audio channel 3 control
                   POKEY_AUDF3     .eq     $1004             ;W audio channel 3 frequency
                   POKEY_AUDC3     .eq     $1005             ;W audio channel 3 control
                   POKEY_AUDF4     .eq     $1006             ;W audio channel 4 frequency
                   POKEY_AUDC4     .eq     $1007             ;W audio channel 4 control
                   POKEY_AUDCTL    .eq     $1008             ;W audio control
                   POKEY_RANDOM    .eq     $100a             ;R random number
                   POKEY_SKCTL     .eq     $100f             ;W serial port control
                   COLOR_PAL       .eq     $1404  {addr/12}  ;W color: 0-3 playfield, 9-11 motion obj
                   EA_ADDR         .eq     $1600  {addr/64}  ;W base address of EAROM
                   EA_CTRL         .eq     $1680             ;W EAROM control
                   EA_READ         .eq     $1700  {addr/64}  ;R data for EAROM read
                   IRQ_ACK         .eq     $1800             ;W IRQ acknowledge
                   COIN_CTR        .eq     $1c00  {addr/3}   ;W left, center, right coin counters
                   ST_LED_1        .eq     $1c03             ;W player 1 start LED (0=on)
                   ST_LED_2        .eq     $1c04             ;W player 2 start LED (0=on)
                   FLIP            .eq     $1c07             ;W screen flip ($80=flipped)
                   WATCHDOG_RESET  .eq     $2000             ;W reset watchdog timer
                   CLR_TBALL       .eq     $2400             ;W clear trackball counters

                                   .addrs  $2000
2000: 4c 4b 3b     ENTRY           jmp     HandleReset       ;note: same address as watchdog reset

2003: 1b 31 39 38+ copyright       .dstr   $1b,‘1980 ATARI’  ;copyright notice; do not modify!

                   ; 
                   ; Jump here from reset handler.
                   ; 
200e: 20 76 28     Start           jsr     InitNewGame       ;init game state; create mushrooms
2011: 58                           cli                       ;enable IRQ
2012: 20 60 2d                     jsr     ShowScores        ;draw the high score table
                   ; 
2015: 46 8a        :MainLoop       lsr     irq_sync
2017: 90 fc                        bcc     :MainLoop         ;wait for IRQ to get to VBLANK
2019: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
201c: ad 00 0c                     lda     IN0
201f: 29 20                        and     #%00100000        ;is self-test switch pressed?
2021: f0 fe        :Spin           beq     :Spin             ;yes, spin until watchdog resets us
                   ; 
2023: 20 66 25                     jsr     ChkGameStart      ;check for game starting
2026: 20 79 30                     jsr     UpdateSound       ;update sound effects
2029: 20 45 27                     jsr     GetInitials       ;get player initials (if post-game)
202c: 10 e7                        bpl     :MainLoop         ;if entering initials, loop now
                   ; 
202e: 20 07 3b                     jsr     WriteEaromInc     ;continue write to EAROM (if in progress)
2031: 20 19 21                     jsr     AttractMove       ;do attract-mode movement (if in attract mode)
2034: 20 27 33                     jsr     DrawScores        ;draw the scores at the top of the screen
2037: 20 55 29                     jsr     MoveCentipede     ;move centipede
203a: 20 01 27                     jsr     UpdateExplosions  ;advance the state of explosions
203d: 20 d2 2a                     jsr     MovePlayer        ;move the player (if not in attract mode)
2040: 20 02 22                     jsr     MoveSpider        ;move the spider
2043: 20 d7 2e                     jsr     UpdateShot        ;check fire button, move shot
2046: 20 dd 2b                     jsr     CreateHead        ;create a new head, if it's time
2049: 20 1c 2e                     jsr     MoveScorpion      ;move the scorpion
204c: 20 59 20                     jsr     MoveFlea          ;move the flea
204f: 20 da 23                     jsr     CheckEnd          ;check if the game is over
2052: 20 f3 2c                     jsr     RestoreShroom     ;restore mushrooms (if player recently died)
2055: 4c 15 20                     jmp     :MainLoop         ;loop

2058: 55                           .dd1    $55               ;(checksum byte)

                   ; 
                   ; ANTMV: creates or updates flea (ant).
                   ; 
                   ; If the flea isn't already on screen, check to see if the number of mushrooms
                   ; in the lower part of the screen is less than a threshold determined by the
                   ; current score.  If there aren't enough mushrooms there, send the flea to
                   ; create more.
                   ; 
2059: a5 43        MoveFlea        lda     mobj_pict_plyr    ;get player picture
205b: 29 af                        and     #%10101111        ;is player alive (pict not $10 or $50)?
205d: d0 08                        bne     :Return           ;no, bail
205f: a5 40                        lda     mobj_pict_flea    ;get flea picture ($1c-1f when active)
2061: 45 ef                        eor     ckp2_c0           ;undo flip bits
2063: c9 20                        cmp     #$20              ;is it < $20?
2065: 90 01                        bcc     :DoMove           ;yes, flea is alive; branch
2067: 60           :Return         rts

2068: a5 70        :DoMove         lda     mobj_vert_flea    ;get vertical position
206a: 45 f0                        eor     ckp2_f8           ;undo flip bits
206c: c9 f8                        cmp     #$f8              ;are we active (not in score row)?
206e: 90 20                        bcc     :MoveFlea         ;yes, move flea
                   ; Decide if we want to spawn a flea.
2070: a6 88                        ldx     cur_player        ;get current player (1/2)
2072: b5 9a                        lda     plyr_cent_len-1,x ;get centipede length
2074: c9 0c                        cmp     #$0c              ;full segment count (e.g. first wave)?
2076: b0 6b                        bcs     :Return           ;yes, bail
2078: b5 ab                        lda     plyr_score2-1,x   ;get high byte of current score
207a: c9 02                        cmp     #$02              ;< 20000 points?
207c: a0 05                        ldy     #$05              ;if so, start with 5
207e: 90 02                        bcc     :Lt20
2080: a0 09                        ldy     #$09              ;>= 20K, start with 9
2082: c9 12        :Lt20           cmp     #$12              ;< 120K points?
2084: 90 05                        bcc     :CmpMush          ;yes, branch
2086: 4a                           lsr     A                 ;no, huge score; divide value by 2
2087: 18                           clc                       ;(e.g. 6 at 120K points)
2088: 69 06                        adc     #$06              ;add 6 (now 12 at 120K)
208a: a8                           tay                       ;use as value
                   ; 
208b: 98           :CmpMush        tya
208c: d5 d7                        cmp     plyr_low_mush-1,x ;compare to # of mushrooms in lower part of screen
208e: 90 53                        bcc     :Return           ;enough mushrooms, bail
                   ; 
2090: a5 00        :MoveFlea       lda     frame_ctr         ;rotate flea image every 4 frames
2092: 29 03                        and     #$03              ;are we there yet?
2094: d0 0f                        bne     :SkipPic          ;no, skip update
2096: e6 40                        inc     mobj_pict_flea    ;increment picture number (now $1d-20)
2098: a5 40                        lda     mobj_pict_flea    ;get picture number
209a: 18                           clc
209b: 69 01                        adc     #$01              ;increment again (now $1e-21)... bug?
209d: 29 03                        and     #%00000011        ;reduce to 0-3
209f: 09 1c                        ora     #$1c              ;now $1c-1f
20a1: 45 ef                        eor     ckp2_c0           ;set flip flags for cocktail player 2
20a3: 85 40                        sta     mobj_pict_flea    ;store it
                   ; 
20a5: a5 60        :SkipPic        lda     mobj_horz_flea    ;get horizontal position
20a7: 85 8b                        sta     temp1             ;save for later (when adding mushrooms)
20a9: a5 70                        lda     mobj_vert_flea    ;get vertical position
20ab: a4 ef                        ldy     ckp2_c0           ;are we flipped?
20ad: f0 06                        beq     :NotFlip          ;no, branch
20af: 18                           clc
20b0: 65 80                        adc     mobj_vvel_flea    ;yes, add vertical movement speed
20b2: 4c b8 20                     jmp     :GotNewVert

20b5: 38           :NotFlip        sec
20b6: e5 80                        sbc     mobj_vvel_flea    ;subtract vertical movement speed (reversed)
20b8: 85 70        :GotNewVert     sta     mobj_vert_flea    ;set new vertical position
20ba: 45 f0                        eor     ckp2_f8
20bc: c9 04                        cmp     #$04              ;reached bottom of screen?
20be: 90 24                        bcc     :FleaGone         ;yes, clean up
20c0: a2 0c                        ldx     #$0c
20c2: 20 9a 2c                     jsr     ChkPlyrColl       ;check for collision with player
20c5: 90 1c                        bcc     :Return           ;if collided, player dead; bail
                   ; 
20c7: a5 00                        lda     frame_ctr         ;check for mushrooms every 4 frames
20c9: 29 03                        and     #$03
20cb: d0 16                        bne     :Return           ;not ready, bail
20cd: ad 0a 10                     lda     POKEY_RANDOM      ;get a random number
20d0: 29 03                        and     #$03              ;reduce to 0-3
20d2: d0 0f                        bne     :Return           ;75% chance to bail without adding mushroom
                   ; Add a mushroom in space above flea.
20d4: a9 04                        lda     #$04
20d6: 45 f3                        eor     ckp2_ff           ;+4 or -5 if screen flipped
20d8: 18                           clc
20d9: 65 70                        adc     mobj_vert_flea    ;add vertical position
20db: a0 00                        ldy     #$00
20dd: 20 2f 2c                     jsr     CalcMushPosn      ;compute position of new mushroom
20e0: 20 ac 2b                     jsr     AddMushroom       ;add it
20e3: 60           :Return         rts

20e4: 20 e8 20     :FleaGone       jsr     InitFlea          ;reset flea position
20e7: 60                           rts

                   ; 
                   ; ANTPC: initializes flea (ant) picture and position.
                   ; 
                   ; The vertical position will be set to $f8 (or, if flipped, $00).  This
                   ; corresponds to the top line of the screen, where scores are shown.  Motion
                   ; objects aren't drawn here by the hardware -- it clips them to avoid
                   ; overwriting the scores -- so it effectively hides the object.
                   ; 
20e8: a9 1c        InitFlea        lda     #$1c              ;first flea picture ($1c-1f)
20ea: 45 ef                        eor     ckp2_c0           ;set flip flags for cocktail player 2
20ec: 85 40                        sta     mobj_pict_flea    ;save it
20ee: a9 f8                        lda     #$f8              ;set initial vertical position
20f0: 45 f0                        eor     ckp2_f8           ;$00 or $f8 (top / bottom = offscreen)
20f2: 85 70                        sta     mobj_vert_flea    ;save it
20f4: ad 0a 10     :Retry          lda     POKEY_RANDOM      ;get random number for horizontal position
20f7: 29 f8                        and     #%11111000        ;align with column
20f9: f0 f9                        beq     :Retry            ;col 0 is offscreen; branch to retry
20fb: c9 10                        cmp     #16               ;is it < column 2?
20fd: 90 f5                        bcc     :Retry            ;yes, retry
20ff: 38                           sec
2100: e9 04                        sbc     #$04              ;subtract 4 so it's in the middle of a playfield col
2102: 85 60                        sta     mobj_horz_flea    ;set horizontal position
                   ; 
2104: a0 03                        ldy     #$03              ;default to faster speed (3 pixels/frame)
2106: a6 88                        ldx     cur_player        ;get player number (1 or 2)
2108: b5 ab                        lda     plyr_score2-1,x   ;get high byte of score (##x,xxx)
210a: c9 06                        cmp     #$06              ;>= 60,000 points?
210c: b0 02                        bcs     :Ge60k            ;yes, branch
210e: a0 02                        ldy     #$02              ;no, use slower speed (2 pixels/frame)
2110: 84 80        :Ge60k          sty     mobj_vvel_flea    ;save vertical speed (reversed, so +2 = down)
2112: a9 00                        lda     #$00
2114: 85 50                        sta     mobj_hvel_flea    ;set horizontal speed to zero (move straight down)
2116: 85 b8                        sta     scorp_sound_idx   ;no sound
2118: 60                           rts

                   ; 
                   ; ATTRT: updates player position while in "attract" mode.
                   ; 
                   ]str_ptr        .var    $93    {addr/2}

2119: a5 86        AttractMove     lda     attract_mode      ;are we in "attract" mode?
211b: 10 6f                        bpl     :Return           ;no, bail
211d: 20 95 21                     jsr     DrawBonusText
                   ; Code from $2120-2133 is checksummed at the end of this routine ($2184).
2120: a9 03        :DrawCopyright  lda     #<copyright       ;get pointer to copyright notice
2122: 85 93                        sta     ]str_ptr          ;set as message text source
2124: a9 20                        lda     #>copyright
2126: 85 94                        sta     ]str_ptr+1
2128: a9 40                        lda     #$40              ;set playfield output pointer (X=10 Y=0 -> $0540)
212a: 85 91                        sta     out_ptr
212c: a9 05                        lda     #$05
212e: 85 92                        sta     out_ptr+1
2130: 20 74 38                     jsr     DrawText          ;draw text
                   ; 
2133: a5 00                        lda     frame_ctr         ;get frame counter
2135: d0 05                        bne     :SkipGO           ;do erase every 4 seconds; otherwise, branch
2137: a9 84                        lda     #$84              ;"game over"
2139: 20 24 38                     jsr     DrawMessage       ;erase message
                   ; 
213c: a5 00        :SkipGO         lda     frame_ctr
213e: ae 00 06                     ldx     PLAYFIELD+$200    ;read 1 ('A' in "ATARI") from the screen, so
2141: 86 ff                        stx     one               ; things break if the copyright notice changes
2143: 29 80                        and     #$80              ;do stuff every 128 frames (~0.5 sec)
2145: d0 45                        bne     :Return           ;not there yet, bail
2147: a5 43                        lda     mobj_pict_plyr    ;get player picture
2149: 29 af                        and     #%10101111        ;dead or exploding?
214b: d0 30                        bne     :Finish           ;yes, branch
                   ; 
214d: a5 63                        lda     mobj_horz_plyr    ;get horizontal position
214f: a0 01                        ldy     #$01              ;default to moving left
2151: c9 1c                        cmp     #$1c              ;nearing right edge?
2153: 90 08                        bcc     :SetHdir          ;yes, branch
2155: a0 ff                        ldy     #$ff              ;maybe move right
2157: c9 e4                        cmp     #$e4              ;nearing left edge?
2159: b0 02                        bcs     :SetHdir          ;yes, branch
215b: a4 53                        ldy     mobj_hvel_plyr    ;get current horizontal move direction
215d: 98           :SetHdir        tya                       ;copy move to A-reg for call arg
215e: 84 53                        sty     mobj_hvel_plyr    ;save new value
2160: 18                           clc
2161: 20 ef 2a                     jsr     MovePlyrHorz      ;move player
                   ; 
2164: a5 73                        lda     mobj_vert_plyr    ;get vertical position
2166: 85 8d                        sta     temp2             ;save for call
2168: a0 ff                        ldy     #$ff              ;default to moving down
216a: c9 30                        cmp     #$30              ;are we near top? (attract mode is never flipped)
216c: b0 08                        bcs     :SetVdir          ;yes, branch
216e: a0 01                        ldy     #$01              ;maybe move up
2170: c9 09                        cmp     #$09              ;near bottom edge?
2172: 90 02                        bcc     :SetVdir          ;yes, branch
2174: a4 83                        ldy     mobj_vvel_plyr    ;continue in current direction
2176: 98           :SetVdir        tya                       ;copy move to A-reg for call arg
2177: 84 83                        sty     mobj_vvel_plyr    ;save new value
2179: 18                           clc
217a: 20 28 2b                     jsr     MovePlayerVert    ;move player
                   ; 
217d: 20 64 2b     :Finish         jsr     MoveUnfiredShot   ;update position of unfired shot to match player
                   ; Compute checksum on copyright-drawing code.
2180: a2 13                        ldx     #$13
2182: a9 ab                        lda     #$ab              ;initial value
2184: 5d 20 21     :ChkLoop        eor     :DrawCopyright,x  ;rolling exclusive-OR
2187: ca                           dex
2188: 10 fa                        bpl     :ChkLoop
218a: 85 fe                        sta     cksum_zero        ;save checksum for use as always-zero value
218c: 60           :Return         rts

                   ; 
                   ; According to the sources, this is morse code for "COPYRIGHT 1980 ATARI".  It's
                   ; not actually used.  The idea is that, if somebody cloned the ROM but denied
                   ; doing so, the presence of these bytes would prove otherwise.
                   ; 
                   ;        -.-. --- .--. -.-- .-. .. --. .... - /
                   ; 000000 1010 111 0110 1011 010 00 110 0000 1
                   ; .---- ----. ---.. ----- / .- - .- .-. ..
                   ; 01111 11110 11100 11111   01 1 01 010 00
218d: 02                           .dd1    %00000010
218e: bb                           .dd1    %10111011
218f: 5a                           .dd1    %01011010
2190: 30                           .dd1    %00110000
2191: 5f                           .dd1    %01011111
2192: ee                           .dd1    %11101110
2193: 7d                           .dd1    %01111101
2194: a8                           .dd1    %10101000

                   ; 
                   ; BONUS: displays "bonus life every XXXXX" message.
                   ; 
2195: 20 b3 21     DrawBonusText   jsr     GetBonusScoreM    ;get low byte
2198: 85 ae                        sta     plyr_bonus_mid    ;set low byte
219a: b9 c0 21                     lda     bonus_score_tbl+1,y ;get high byte
219d: 85 b0                        sta     plyr_bonus_hi     ;set high byte
219f: a9 06                        lda     #$06              ;"bonus every "
21a1: 20 24 38                     jsr     DrawMessage       ;draw text
21a4: a5 b0                        lda     plyr_bonus_hi
21a6: 20 ab 38                     jsr     Draw1Digit        ;draw middle digit
21a9: a5 ae                        lda     plyr_bonus_mid
21ab: 20 9e 38                     jsr     Draw2Digits       ;draw low digits
21ae: a9 00                        lda     #$00
21b0: 4c 9e 38                     jmp     Draw2Digits       ;add "00" onto the end

                   ; 
                   ; BONUS1: retrieves lowest byte of bonus score setting, which corresponds to the
                   ; middle byte of the score.
                   ; 
                   ; On exit:
                   ;   A-reg: lowest byte of bonus score setting
                   ;   Y-reg: index into bonus score table
                   ; 
21b3: a5 fd        GetBonusScoreM  lda     dsw_n9_copy       ;get N9 switches
21b5: 29 30                        and     #%00110000        ;mask to get bonus life setting
21b7: 4a                           lsr     A                 ;shift right 3x, leaving value doubled
21b8: 4a                           lsr     A
21b9: 4a                           lsr     A
21ba: a8                           tay                       ;use as index
21bb: b9 bf 21                     lda     bonus_score_tbl,y ;get low byte of score
21be: 60                           rts

                   ; 
                   ; Number of points required to get a bonus life.  Stored as middle/high BCD
                   ; bytes, without the trailing "00".
                   ; 
21bf: 00 01        bonus_score_tbl .dd2    $0100             ;10,000 points
21c1: 20 01                        .dd2    $0120             ;12,000
21c3: 50 01                        .dd2    $0150             ;15,000
21c5: 00 02                        .dd2    $0200             ;20,000

                   ; 
                   ; BUGOFF: initializes spider ("bug").  Initially inactive.
                   ; 
                   • Clear variables

21c7: a6 88        InitSpider      ldx     cur_player        ;get current player
21c9: a0 02                        ldy     #$02              ;default to speed 2 (fast)
21cb: b5 ab                        lda     plyr_score2-1,x   ;get middle byte of score
21cd: d0 0c                        bne     :Ge10K            ;score > 9999, branch
21cf: a5 fd                        lda     dsw_n9_copy       ;get N9 switches
21d1: 29 40                        and     #%01000000        ;get difficulty flag
21d3: 09 10                        ora     #$10              ;make it $10 (easy) or $50 (hard)
21d5: d5 a9                        cmp     plyr_score1-1,x   ;has score exceeded threshold (1000/5000)?
21d7: 90 02                        bcc     :Ge10K            ;no, branch
21d9: a0 01                        ldy     #$01              ;use speed 1 (slow)
21db: 84 81        :Ge10K          sty     mobj_vvel_spdr    ;set vertical speed
21dd: ad 0a 10                     lda     POKEY_RANDOM      ;get random number
21e0: 29 04                        and     #%00000100        ;mask a bit
21e2: f0 05                        beq     :SetHMove         ;50% chance to invert horizontal dir
21e4: 98                           tya
21e5: 20 7c 38                     jsr     Calc2sComp        ;reverse direction
21e8: a8                           tay
21e9: 84 51        :SetHMove       sty     mobj_hvel_spdr    ;set horizontal speed (same magnitude as vert)
21eb: a9 60                        lda     #$60
21ed: 45 f0                        eor     ckp2_f8
21ef: 85 71                        sta     mobj_vert_spdr    ;set vertical position below middle of screen
21f1: a9 ff                        lda     #$ff
21f3: 85 61                        sta     mobj_horz_spdr    ;set horizontal position to right edge
21f5: a9 f8                        lda     #$f8
21f7: 85 41                        sta     mobj_pict_spdr    ;set picture to "600" with flip flags set
21f9: a9 60                        lda     #$60
21fb: 85 a1                        sta     spider_cooldown   ;init cooldown
21fd: a9 00                        lda     #$00
21ff: 85 b5                        sta     spider_sound_idx  ;turn off sound
2201: 60                           rts

                   ; 
                   ; BUGMV: updates position of spider ("bug").
                   ; 
2202: a5 43        MoveSpider      lda     mobj_pict_plyr    ;get player picture
2204: 29 af                        and     #%10101111        ;dead or exploding?
2206: f0 01                        beq     :DoUpdate         ;no, branch
2208: 60           :Return         rts

2209: a2 0d        :DoUpdate       ldx     #$0d              ;spider motion object index (not used?)
220b: a5 41                        lda     mobj_pict_spdr    ;get spider picture
220d: a8                           tay                       ;copy to Y-reg
220e: 29 20                        and     #$20              ;dead?
2210: f0 07                        beq     :NotDead          ;no, branch
2212: c0 f8                        cpy     #$f8              ;inactive?
2214: 90 f2                        bcc     :Return           ;no, branch
2216: 4c fa 22                     jmp     :DeadSpider       ;yes, go see if it's time to resurrect

2219: a5 00        :NotDead        lda     frame_ctr         ;get frame counter
221b: 29 03                        and     #$03              ;every 4 frames
221d: d0 10                        bne     :SkipPic          ;not yet, branch
                   ; 
221f: e6 41                        inc     mobj_pict_spdr    ;advance to next spider picture ($14-1b)
2221: a5 41                        lda     mobj_pict_spdr    ;get picture
2223: 45 f2                        eor     ckp2_40           ;undo vertical flip flag
2225: c9 1c                        cmp     #$1c              ;reached end of sequence?
2227: 90 06                        bcc     :SkipPic          ;not yet, branch
2229: a9 14                        lda     #$14              ;reset to start of sequence
222b: 45 f2                        eor     ckp2_40           ;apply vert flip flag
222d: 85 41                        sta     mobj_pict_spdr    ;set new value
                   ; 
222f: c6 a1        :SkipPic        dec     spider_cooldown   ;time to change direction?
2231: d0 35                        bne     :MoveSpider       ;no, branch
2233: ad 0a 10                     lda     POKEY_RANDOM      ;get random value
2236: 29 80                        and     #%10000000        ;keep 1 bit
2238: f0 18                        beq     :NoHChange        ;50% chance to keep moving same direction
                   ; Decide if we want to change the spider's direction.
223a: a5 51                        lda     mobj_hvel_spdr    ;get spider horizontal direction
223c: f0 10                        beq     :UpDown           ;just moving vertically; branch
223e: a4 61                        ldy     mobj_horz_spdr    ;get horizontal position
2240: c0 fb                        cpy     #$fb              ;near left edge?
2242: b0 0e                        bcs     :NoHChange        ;yes, keep going (entering or leaving)
2244: c0 05                        cpy     #$05              ;near right edge?
2246: 90 0a                        bcc     :NoHChange        ;yes, keep going
2248: 85 be                        sta     spider_prev_hdir  ;save previous direction
224a: a9 00                        lda     #$00              ;set hdir to zero
224c: f0 02                        beq     :SetHdir          ;(always)

224e: a5 be        :UpDown         lda     spider_prev_hdir  ;save previous direction
2250: 85 51        :SetHdir        sta     mobj_hvel_spdr    ;set new direction
                   ; 
2252: a5 fd        :NoHChange      lda     dsw_n9_copy       ;get N9 switches
2254: 29 40                        and     #%01000000        ;get difficulty flag
2256: 09 20                        ora     #$20              ;set to $20 or $40 (1 bit or 2 bits set)
2258: 2d 0a 10                     and     POKEY_RANDOM      ;mask with random value (50% or 25% chance all zero)
225b: f0 07                        beq     :NoChg            ;all zero, don't change direction
225d: a5 81                        lda     mobj_vvel_spdr    ;get current direction
225f: 20 7c 38                     jsr     Calc2sComp        ;reverse it
2262: 85 81                        sta     mobj_vvel_spdr    ;save new direction
2264: a9 30        :NoChg          lda     #48
2266: 85 a1                        sta     spider_cooldown   ;check again in 48 frames (~3/4 sec)
                   ; Move the spider.
2268: a5 61        :MoveSpider     lda     mobj_horz_spdr    ;get horizontal position
226a: 38                           sec                       ;subtract horizontal movement; note this means the
226b: e5 51                        sbc     mobj_hvel_spdr    ; hdir value for spiders is reversed
226d: 85 61                        sta     mobj_horz_spdr    ;save new position
226f: 85 8b                        sta     temp1             ;copy to arg
2271: a5 71                        lda     mobj_vert_spdr    ;get vertical position
2273: a4 ef                        ldy     ckp2_c0           ;check flip flag
2275: f0 06                        beq     :NoFlip           ;not flipped, branch
2277: 18                           clc
2278: 65 81                        adc     mobj_vvel_spdr    ;add vertical movement
227a: 4c 80 22                     jmp     :SetVpos

227d: 38           :NoFlip         sec
227e: e5 81                        sbc     mobj_vvel_spdr    ;subtract vertical movement; vdir is also reversed
2280: 85 71        :SetVpos        sta     mobj_vert_spdr    ;save new position
                   ; 
2282: a0 00                        ldy     #$00
2284: 20 2f 2c                     jsr     CalcMushPosn      ;check for collision with mushroom
2287: f0 13                        beq     :NoColl           ;no collision, branch
                   ; 
2289: a0 00                        ldy     #$00
228b: b1 32                        lda     (obst_ptr),y      ;get tile at that location
228d: 29 3f                        and     #$3f              ;strip flip flags
228f: c9 38                        cmp     #$38              ;is it a mushroom?
2291: 90 09                        bcc     :NoColl           ;no, branch (don't erase messages)
2293: a9 00                        lda     #$00
2295: 91 32                        sta     (obst_ptr),y      ;yes, remove mushroom
2297: 20 95 2b                     jsr     DecrLowMush       ;update low mushroom count
229a: a2 0d                        ldx     #$0d              ;spider motion object index (not used?)
                   ; 
229c: a5 61        :NoColl         lda     mobj_horz_spdr    ;get horizontal position
229e: c9 ff                        cmp     #$ff              ;are we off screen?
22a0: b0 54                        bcs     :Offscreen        ;yes, branch
22a2: a5 71                        lda     mobj_vert_spdr    ;get vertical position
22a4: 45 f0                        eor     ckp2_f8           ;apply flip
22a6: c9 09                        cmp     #$09              ;are we above bottom?
22a8: b0 06                        bcs     :ScoreAdj         ;yes, branch
22aa: a5 81                        lda     mobj_vvel_spdr    ;too close to bottom; get vertical direction
22ac: 10 3d                        bpl     :FlipVDir         ;branch to flip it if we're moving down
22ae: 30 32                        bmi     :ChkOverlap       ;(always)

                   ; Adjust max vertical position, based on score.
22b0: a6 88        :ScoreAdj       ldx     cur_player        ;get player number
22b2: b5 ab                        lda     plyr_score2-1,x   ;get high part of score
22b4: f8                           sed                       ;enable decimal mode
22b5: 38                           sec
22b6: e9 06                        sbc     #$06              ;subtract 6 (60,000 points)
22b8: d8                           cld                       ;disable decimal mode
22b9: 10 02                        bpl     :UseVal           ;branch if >= 60K and < 860K
22bb: a9 00                        lda     #$00              ;use zero
22bd: 4a           :UseVal         lsr     A                 ;divide by 2
22be: c9 06                        cmp     #$06              ;result < 6? (score was less than 120+60=180)
22c0: 90 02                        bcc     :Lt6              ;yes, use value
22c2: a9 05                        lda     #$05              ;no, cap at 5
22c4: 0a           :Lt6            asl     A                 ;multiply by 8
22c5: 0a                           asl     A
22c6: 0a                           asl     A
22c7: 85 8d                        sta     temp2             ;save in ZP
22c9: a9 60                        lda     #$60              ;max vertical position
22cb: 45 f0                        eor     ckp2_f8           ;adjust for flip
22cd: 38                           sec
22ce: e5 8d                        sbc     temp2             ;subtract adjustment
22d0: a6 ef                        ldx     ckp2_c0           ;are we flipped?
22d2: f0 06                        beq     :NoFlip           ;no, branch
22d4: c5 71                        cmp     mobj_vert_spdr    ;compare to spider's vertical position
22d6: 90 0a                        bcc     :ChkOverlap       ;we're fine, branch
22d8: b0 04                        bcs     :ChkVert          ;(always)

22da: c5 71        :NoFlip         cmp     mobj_vert_spdr    ;compare limit to current vertical position
22dc: b0 04                        bcs     :ChkOverlap       ;we're fine, branch
22de: a5 81        :ChkVert        lda     mobj_vvel_spdr    ;get vertical movement direction
22e0: 30 09                        bmi     :FlipVDir         ;if moving up, branch to flip it
                   ; 
22e2: a2 0d        :ChkOverlap     ldx     #$0d              ;spider motion object index
22e4: 20 6f 2c                     jsr     ChkSegOverlap     ;check for overlap with centipede segments
22e7: 90 07                        bcc     :NoOverlap        ;no overlap, branch
22e9: a5 81                        lda     mobj_vvel_spdr    ;get current vertical direction
22eb: 20 7c 38     :FlipVDir       jsr     Calc2sComp        ;reverse direction
22ee: 85 81                        sta     mobj_vvel_spdr    ;set new vertical direction
22f0: a2 0d        :NoOverlap      ldx     #$0d              ;spider motion object index
22f2: 20 9a 2c                     jsr     ChkPlyrColl       ;check for collision with player
22f5: 60                           rts

22f6: 20 c7 21     :Offscreen      jsr     InitSpider        ;re-init spider
22f9: 60                           rts

22fa: c6 a1        :DeadSpider     dec     spider_cooldown   ;decrement the cooldown
22fc: d0 11                        bne     :Return           ;still waiting; branch
22fe: ad 0a 10                     lda     POKEY_RANDOM      ;get random number
2301: 29 2f                        and     #%00101111
2303: 09 0f                        ora     #%00001111        ;$0f or $2f
2305: 85 a1                        sta     spider_cooldown   ;delay first direction change by 15 or 47 frames
2307: a9 14                        lda     #20
2309: 85 b5                        sta     spider_sound_idx  ;init spider sound sequence (20 elements)
230b: 45 f2                        eor     ckp2_40           ;set vertical flip flag if appropriate
230d: 85 41                        sta     mobj_pict_spdr    ;enable spider by setting picture to $14
230f: 60           :Return         rts

                   ; 
                   ; CALLS: sets up arguments for obstacle test routine.
                   ; 
                   ; On entry:
                   ;   X-reg: index of motion object
                   ; 
                   ; On exit:
                   ;   $8b: motion object horizontal position
                   ;   A-reg: motion object vertical position
                   ;   Y-reg: motion object horizontal direction ($01/ff)
                   ;   (X-reg unmodified)
                   ; 
2310: b5 54        SetObstacArgs   lda     mobj_horz,x       ;get horizontal position
2312: 85 8b                        sta     temp1             ;copy to ZP
2314: a0 ff                        ldy     #$ff
2316: b5 44                        lda     mobj_hvel,x       ;get horizontal direction
2318: 30 02                        bmi     :NegHdir          ;if moving left, branch
231a: a0 01                        ldy     #$01              ;otherwise return 1 if moving left or not at all
231c: b5 64        :NegHdir        lda     mobj_vert,x       ;get vertical position
231e: 60                           rts

                   ; 
                   ; CENTPC: initializes centipede.
                   ; 
                   ; A per-player value determines the length of the centipede.  This changes with
                   ; each wave.  All other segments are created as individual heads.
                   ; 
231f: a6 88        InitCentipede   ldx     cur_player        ;get current player number
2321: b5 94                        lda     live_seg_count-1,x ;are all the segments dead?
2323: d0 20                        bne     :ChangeDone       ;no, branch
                   ; Update color and speed.
2325: b5 c2                        lda     leg_color-1,x     ;time to change colors
2327: 09 80                        ora     #$80              ;set a flag to shift palette during IRQ
2329: 95 c2                        sta     leg_color-1,x
232b: b5 9c                        lda     plyr_cent_spd-1,x ;get centipede speed
232d: c9 03                        cmp     #$03              ;1 or 2?
232f: 90 14                        bcc     :ChangeDone       ;yes, branch
2331: d6 9a                        dec     plyr_cent_len-1,x ;decrement centipede length
2333: d0 04                        bne     :NoResetLen       ;not zero, branch
2335: a9 0c                        lda     #NCENT
2337: 95 9a                        sta     plyr_cent_len-1,x ;long centipede again
2339: a9 02        :NoResetLen     lda     #$02              ;set speed to 2 (fast)
233b: b4 ab                        ldy     plyr_score2-1,x   ;get high byte of score
233d: c0 04                        cpy     #$04              ;>= 40000?
233f: b0 02                        bcs     :SetSpeed         ;yes, keep fast speed
2341: a9 01                        lda     #$01              ;set speed to 1 (slow)
2343: 95 9c        :SetSpeed       sta     plyr_cent_spd-1,x ;set centipede speed
                   ; Configure head segment.
2345: a9 03        :ChangeDone     lda     #$03
2347: 85 34                        sta     mobj_pict         ;set picture on mobj #0 to centipede head
2349: b5 9c                        lda     plyr_cent_spd-1,x ;get speed
234b: 85 74                        sta     mobj_vvel         ;set as vertical speed
234d: a8                           tay                       ;preserve in Y-reg
234e: a5 00                        lda     frame_ctr         ;get frame counter
2350: 29 02                        and     #%00000010        ;alternate 2 frames on, 2 frames off
2352: d0 05                        bne     :NoNeg            ;(50/50 chance if we're not in sync with frames)
2354: 98                           tya
2355: 20 7c 38                     jsr     Calc2sComp        ;negate the value
2358: a8                           tay
2359: 84 44        :NoNeg          sty     mobj_hvel         ;set as horizontal speed
                   ; 
235b: a9 f8                        lda     #$f8              ;place at top of screen
235d: 45 f0                        eor     ckp2_f8           ;apply flip bits
235f: 85 64                        sta     mobj_vert         ;set vertical position
2361: a9 80                        lda     #$80
2363: 85 54                        sta     mobj_horz         ;set horizontal position to center of screen
2365: b5 9a                        lda     plyr_cent_len-1,x ;get centipede length
2367: 85 8b                        sta     temp1             ;stash in ZP
2369: c9 01                        cmp     #$01              ;is length 1 (just a head)?
236b: f0 35                        beq     :Len1             ;yes, branch
                   ; Configure body segments.
236d: a0 42                        ldy     #$42              ;picture $02 with "not head" flag ($40)
236f: a2 01                        ldx     #$01              ;set X=1
2371: 94 34        :SegLoop        sty     mobj_pict,x       ;set body picture
2373: a9 f8                        lda     #$f8              ;top of screen
2375: 45 f0                        eor     ckp2_f8           ;apply flip bits
2377: 95 64                        sta     mobj_vert,x       ;set vertical position
2379: b5 73                        lda     mobj_vvel-1,x     ;get vertical direction of head
237b: 95 74                        sta     mobj_vvel,x       ;set as vertical direction
237d: b5 43                        lda     mobj_hvel-1,x     ;get horizontal direction of head
237f: 95 44                        sta     mobj_hvel,x       ;set as horizontal direction
2381: 10 04                        bpl     :MoveLf           ;branch if moving to left
2383: a9 08                        lda     #$08              ;moving right, place segment one tile width to left
2385: d0 02                        bne     :SetHorz

2387: a9 f8        :MoveLf         lda     #$f8              ;moving left, place segment one tile width to right
2389: 18           :SetHorz        clc
238a: 75 53                        adc     mobj_horz-1,x     ;add to head's horizontal position
238c: 95 54                        sta     mobj_horz,x       ;set body horizontal position
238e: 88                           dey                       ;use next segment ($00-07 + $40)
238f: c0 3f                        cpy     #$3f              ;did we hit the end?
2391: d0 02                        bne     :Not3f            ;not yet, branch
2393: a0 47                        ldy     #$47              ;start back at picture $07 + $40
2395: e8           :Not3f          inx                       ;advance to next motion object slot
2396: e4 8b                        cpx     temp1             ;have we done all segments?
2398: 90 d7                        bcc     :SegLoop          ;no, loop
                   ; 
239a: a6 88                        ldx     cur_player        ;get current player
239c: b5 9a                        lda     plyr_cent_len-1,x ;get centipede length
239e: c9 0c                        cmp     #NCENT            ;full length?
23a0: f0 2d                        beq     :Finish
                   ; Move remaining segments to top of screen, where they become individual heads
                   ; with random position / direction.
23a2: a9 f8        :Len1           lda     #$f8              ;top of screen
23a4: 45 f0                        eor     ckp2_f8           ;apply flip bits
23a6: a6 8b                        ldx     temp1             ;get centipede length
23a8: 95 64        :InactLoop      sta     mobj_vert,x       ;set vertical position
23aa: a9 00                        lda     #$00
23ac: 95 34                        sta     mobj_pict,x       ;set picture to $00 (head)
23ae: a9 02                        lda     #$02              ;start with vertical speed = 2
23b0: a4 f4                        ldy     ckp2_fe           ;are we flipped?
23b2: f0 01                        beq     :NoFlip           ;no, branch
23b4: 98                           tya                       ;yes, set vertical speed = -2
23b5: 95 74        :NoFlip         sta     mobj_vvel,x       ;set vertical direction
23b7: 2c 0a 10                     bit     POKEY_RANDOM      ;get random; 50/50 chance
23ba: 10 03                        bpl     :NoInvert
23bc: 20 7c 38                     jsr     Calc2sComp        ;negate velocity
23bf: 95 44        :NoInvert       sta     mobj_hvel,x       ;set horizontal velocity
23c1: ad 0a 10                     lda     POKEY_RANDOM      ;get random value
23c4: 29 f8                        and     #%11111000        ;keep upper 5 bits
23c6: 95 54                        sta     mobj_horz,x       ;save as horizontal position
23c8: b5 64                        lda     mobj_vert,x       ;get vertical position so we don't have to recalc
23ca: e8                           inx                       ;move to next motion obj
23cb: e0 0c                        cpx     #NCENT            ;done with all centipede segments?
23cd: 90 d9                        bcc     :InactLoop        ;no, loop
                   ; 
23cf: a9 0c        :Finish         lda     #NCENT
23d1: a6 88                        ldx     cur_player        ;get current player
23d3: 95 94                        sta     live_seg_count-1,x ;set segment count to full 12
23d5: a5 fe                        lda     cksum_zero        ;should be $00
23d7: 85 97                        sta     new_head_flag     ;clear new-head flag
23d9: 60                           rts

                   ; 
                   ; CHKEND: checks for end of game, handles delays.
                   ; 
                   ; Various re-init calls are made from here when players die or waves end.  We
                   ; also handle the set-up for high score entry.
                   ; 
23da: a5 86        CheckEnd        lda     attract_mode      ;are we in "attract" mode?
23dc: 30 38                        bmi     :ChkDelay         ;yes, branch
23de: ad 01 08                     lda     DSW_N8            ;get N8 switches
23e1: 29 1c                        and     #%00011100        ;is a time limit configured?
23e3: f0 31                        beq     :ChkDelay         ;no, branch
23e5: a5 ab                        lda     plyr_score1+1
23e7: 05 ad                        ora     plyr_score2+1     ;are minutes / seconds both zero?
23e9: f0 1b                        beq     :TimesUp          ;yes, time has expired; branch
23eb: c6 a9                        dec     plyr_score0+1     ;decrement 1/60ths of a second
23ed: d0 27                        bne     :ChkDelay         ;not zero, branch
23ef: a9 3c                        lda     #60
23f1: 85 a9                        sta     plyr_score0+1     ;reset 1/60ths of a second to 60
23f3: f8                           sed                       ;switch to decimal mode
23f4: a5 ab                        lda     plyr_score1+1     ;decrement seconds
23f6: 38                           sec
23f7: e9 01                        sbc     #$01
23f9: d8                           cld                       ;clear decimal mode
23fa: 85 ab                        sta     plyr_score1+1     ;store result
23fc: 10 18                        bpl     :ChkDelay         ;not yet zero, branch
23fe: c6 ad                        dec     plyr_score2+1     ;decrement minutes
2400: a9 59                        lda     #$59
2402: 85 ab                        sta     plyr_score1+1     ;reset seconds to 59
2404: d0 10                        bne     :ChkDelay         ;(always)

2406: a9 00        :TimesUp        lda     #$00
2408: 85 a5                        sta     plyr_lives        ;kill the player
240a: 20 bc 26                     jsr     DrawLives         ;update display
240d: a5 43                        lda     mobj_pict_plyr
240f: 29 af                        and     #%10101111        ;mark as exploding / dead
2411: d0 03                        bne     :ChkDelay         ;if already dead, branch
2413: 20 c8 2c                     jsr     ExplodePlayer     ;blow up the player
                   ; 
2416: a5 87        :ChkDelay       lda     delay_ctr         ;is a delay pending?
2418: d0 01                        bne     :DelayMaybe       ;yes, branch
241a: 60           :Return         rts                       ;no, nothing further to do

                   ; A delay is set, which only happens on major events like the death of the
                   ; player or the end of the wave.  We do the necessary reinitialization here.
241b: a5 db        :DelayMaybe     lda     mush_ptr+1        ;are mushrooms being restored?
241d: d0 fb                        bne     :Return           ;yes, wait for that to finish
241f: c6 87                        dec     delay_ctr         ;still counting down a delay?
2421: d0 f7                        bne     :Return           ;yes, bail
2423: a5 d6                        lda     plyr_switch_flag  ;are we switching players?
2425: f0 0f                        beq     :ChkDead          ;no, branch
2427: a9 80                        lda     #$80              ;"player "
2429: 20 24 38                     jsr     DrawMessage       ;erase message
242c: a9 00                        lda     #$00
242e: a8                           tay
242f: 91 91                        sta     (out_ptr),y       ;erase player num digit
2431: 85 d6                        sta     plyr_switch_flag  ;clear switch flag
2433: 4c 36 29                     jmp     InitPlayer        ;initialize the player position

2436: a5 43        :ChkDead        lda     mobj_pict_plyr    ;get player mobj
2438: 29 af                        and     #%10101111        ;check deadness flags (anything other than $10/$50)
243a: d0 03                        bne     :InitPlayer       ;not alive, branch
243c: 4c 41 25                     jmp     :Init2            ;alive; init centipede for next wave and return

243f: 20 36 29     :InitPlayer     jsr     InitPlayer        ;init player position
2442: a5 86                        lda     attract_mode      ;are we in "attract" mode?
2444: 10 11                        bpl     :NotAttract       ;no, branch
                   ; In attract mode, alternate sets of mushrooms if more than 128*256/60=546
                   ; seconds have elapsed.  The source code notes "swap the screen to rewrite any
                   ; memory errors"; presumably the idea is that, if some sort of RAM issue has
                   ; placed random junk on screen, this will clear it.  Note we're only checking
                   ; this when the attract-mode player dies.
2446: a5 01                        lda     frame_ctr+1       ;check high byte of frame counter
2448: 10 0a                        bpl     :NoSwap           ;if < $80, branch
244a: 29 7f                        and     #$7f
244c: 85 01                        sta     frame_ctr+1       ;subtract $80 from counter
244e: 20 fe 31                     jsr     SwapShrooms       ;save/restore mushroom layout
2451: 20 60 2d                     jsr     ShowScores        ;redraw scores, which were erased by swap
2454: 4c 3b 25     :NoSwap         jmp     :Init1

2457: a5 a5        :NotAttract     lda     plyr_lives        ;get player 1 lives remaining
2459: 05 a6                        ora     plyr_lives+1      ;combine with player 2 lives
245b: d0 46                        bne     :StillAlive       ;some life left, keep going
245d: c6 86                        dec     attract_mode      ;decrement $00 -> $ff, switching to "attract"
                   ; 
245f: 20 67 32                     jsr     UpdateHS          ;update high score table; set index if need initials
2462: a5 ef                        lda     ckp2_c0           ;cocktail flip?
2464: f0 18                        beq     :NotFlip          ;no, branch
2466: a5 c2                        lda     plyr_hs_init_slot+1 ;do we need initials from player 2?
2468: 10 14                        bpl     :NotFlip          ;no, branch
246a: a9 80                        lda     #$80
246c: 85 ee                        sta     ckp2_indic        ;set cocktail player 2 flag
246e: 20 45 25                     jsr     InitBits          ;set up the flip flags
2471: 20 36 29                     jsr     InitPlayer        ;reset player position
2474: 20 fe 31                     jsr     SwapShrooms       ;clean up mushrooms
2477: a5 c1                        lda     plyr_hs_init_slot ;do we need initials from player 1?
2479: 10 03                        bpl     :NotFlip          ;no, branch
247b: 20 60 2d                     jsr     ShowScores        ;put the high scores up
                   ; 
247e: 20 c7 21     :NotFlip        jsr     InitSpider        ;reset the spider
2481: 20 1f 23                     jsr     InitCentipede     ;reset the centipede
2484: 20 e8 20                     jsr     InitFlea          ;reset the flea
2487: a9 01                        lda     #$01
2489: 85 00                        sta     frame_ctr         ;counter for clearing "game over" message
248b: a9 04                        lda     #$04              ;"game over"
248d: 20 24 38                     jsr     DrawMessage       ;draw message
                   ; 
2490: a6 89                        ldx     num_players       ;get player number
2492: a9 ff                        lda     #$ff
2494: 9d 02 1c                     sta     ST_LED_1-1,x      ;turn off start LED
2497: 20 4f 3a                     jsr     UpdateEaromSum    ;checksum the EAROM buffer
249a: a9 3d                        lda     #61               ;(EACKSM - EAROM)
249c: 85 f9                        sta     earom_cur_addr    ;starting address
249e: a9 00                        lda     #$00
24a0: 85 fa                        sta     earom_cur_op      ;compare / write EAROM
24a2: 60                           rts

24a3: a6 89        :StillAlive     ldx     num_players       ;get number of players
24a5: ca                           dex                       ;1 or 2?
24a6: d0 03                        bne     :HandleTwo        ;two players, branch
24a8: 4c 34 25                     jmp     :DecLives         ;one player, jump

24ab: a6 88        :HandleTwo      ldx     cur_player        ;get current player number
24ad: b5 a4                        lda     plyr_lives-1,x    ;get number of lives remaining
24af: d0 24                        bne     :StillAlive       ;still alive, branch
24b1: a5 a7                        lda     game_over_flag    ;are we showing "game over"?
24b3: d0 1e                        bne     :GameOverDone     ;yes, branch
24b5: e6 a7                        inc     game_over_flag    ;not yet; set flag
24b7: a9 80                        lda     #128
24b9: 85 87                        sta     delay_ctr         ;set a delay of 128 frames (~2 sec)
24bb: a9 f9                        lda     #$f9
24bd: 85 43                        sta     mobj_pict_plyr    ;set player / shot picture to $f9 ($39=blank + flags)
24bf: 85 42                        sta     mobj_pict_shot
24c1: a9 04                        lda     #$04              ;"game over"
24c3: 20 24 38                     jsr     DrawMessage       ;draw message
24c6: a9 00                        lda     #$00              ;"player "
24c8: 20 24 38                     jsr     DrawMessage       ;draw message
24cb: a5 88                        lda     cur_player        ;get player number
24cd: 09 20                        ora     #$20              ;convert to digit glyph index
24cf: 20 85 38                     jsr     DrawChar          ;draw it
24d2: 60                           rts

24d3: c6 a7        :GameOverDone   dec     game_over_flag    ;clear flag
24d5: a5 88        :StillAlive     lda     cur_player        ;get current player number (1/2)
24d7: 49 03                        eor     #$03              ;flip (now 2/1)
24d9: aa                           tax                       ;use as index
24da: b5 a4                        lda     plyr_lives-1,x    ;get lives of the other player
24dc: f0 56                        beq     :DecLives         ;no lives, branch
                   ; 
24de: 86 88                        stx     cur_player        ;switch players
24e0: a9 80                        lda     #$80
24e2: 25 ee                        and     ckp2_indic
24e4: 05 88                        ora     cur_player
24e6: 85 ee                        sta     ckp2_indic        ;correct cocktail indicator
24e8: c9 82                        cmp     #$82              ;are we cocktail player 2?
24ea: d0 03                        bne     :NotCkPl2         ;no, branch
24ec: 20 65 25                     jsr     InitBitsFlipped   ;yes, intialize bits for flip
                   ; 
24ef: b5 a1        :NotCkPl2       lda     plyr_head_ctr_init-1,x ;reset new head counter
24f1: 85 a0                        sta     new_head_ctr
24f3: a6 88                        ldx     cur_player        ;get player number
24f5: e0 01                        cpx     #$01              ;player 1?
24f7: d0 03                        bne     :NotPl1           ;no, branch
24f9: 20 45 25                     jsr     InitBits          ;init for non-flipped
24fc: 20 fe 31     :NotPl1         jsr     SwapShrooms       ;swap mushroom sets
24ff: a6 88                        ldx     cur_player        ;get player num
2501: e0 02                        cpx     #$02              ;player 2?
2503: d0 11                        bne     :NotPl2           ;no, branch
2505: b5 a4                        lda     plyr_lives-1,x    ;get lives remaining
2507: c5 a4                        cmp     initial_lives     ;on bonus life?
2509: d0 0b                        bne     :NotPl2           ;no, branch
250b: a5 ad                        lda     plyr_score2+1     ;check score to see if this is pl2's first round
250d: d0 07                        bne     :NotPl2           ;nope, branch [what if score rolls over?]
250f: a9 0c                        lda     #NCENT
2511: 95 94                        sta     live_seg_count-1,x ;init centipede to full size
2513: 20 c3 28                     jsr     InitPlay          ;initialize screen for player 2
                   ; 
2516: b5 c2        :NotPl2         lda     leg_color-1,x     ;indicate color change for next player
2518: 09 40                        ora     #$40
251a: 95 c2                        sta     leg_color-1,x
251c: a9 a0                        lda     #$a0              ;set brief delay before play starts
251e: 85 87                        sta     delay_ctr
2520: a9 00                        lda     #$00              ;"player "
2522: 20 24 38                     jsr     DrawMessage       ;draw message
2525: a5 88                        lda     cur_player        ;get current player number
2527: 09 20                        ora     #$20              ;convert to digit glyph index
2529: 20 85 38                     jsr     DrawChar          ;draw digit
252c: a9 f9                        lda     #$f9
252e: 85 43                        sta     mobj_pict_plyr    ;remove gun/shot
2530: 85 42                        sta     mobj_pict_shot
2532: 85 d6                        sta     plyr_switch_flag  ;raise the player-switch flag
2534: a6 88        :DecLives       ldx     cur_player        ;get current player number
2536: d6 a4                        dec     plyr_lives-1,x    ;decrement lives remaining
2538: 20 bc 26                     jsr     DrawLives         ;update display
253b: 20 c7 21     :Init1          jsr     InitSpider        ;reset spider
253e: 20 e8 20                     jsr     InitFlea          ;reset flea
2541: 20 1f 23     :Init2          jsr     InitCentipede     ;reset centipede
2544: 60                           rts

                   ; 
                   ; CLEAR: initializes some values.
                   ; 
                   ; Notably, initializes the cocktail player 2 flip flags to zero.  (For rev4,
                   ; these are always zero, because we don't support two players.)
                   ; 
2545: a5 fe        InitBits        lda     cksum_zero        ;expected to hold zero
2547: 85 bd                        sta     tball_prev_horz
2549: 85 bf                        sta     tball_prev_vert
254b: 8d 07 1c                     sta     FLIP
254e: 8d 00 24                     sta     CLR_TBALL
                   ; Init the screen rotation bit masks to zero for player 1.
2551: 85 f5                        sta     ckp2_bf
2553: 85 f7                        sta     ckp2_03
2555: 85 f6                        sta     ckp2_3f
2557: 85 f0                        sta     ckp2_f8
2559: 85 ef                        sta     ckp2_c0
255b: 85 f1                        sta     ckp2_e0
255d: 85 f2                        sta     ckp2_40
255f: 85 f3                        sta     ckp2_ff
2561: 85 f4                        sta     ckp2_fe
2563: 85 f8                        sta     ckp2_fc
                   ; 
                   ; CKSET: init screen rotation bit masks for player 2 on cocktail cabinet.
                   ; 
                   ; The rev4 ROM doesn't support two players, so this does nothing.
                   ; 
2565: 60           InitBitsFlipped rts

                   ; 
                   ; CHKST: checks for start of game.
                   ; 
                   ]blink_timer    .var    $8d    {addr/1}

2566: ad 01 08     ChkGameStart    lda     DSW_N8            ;get N8 switches
2569: 29 e3                        and     #%11100011        ;strip out coin multiplier (for rev4)
256b: 85 d3                        sta     n8_nomult         ;stash in ZP
256d: 29 03                        and     #%00000011        ;reduce to "coins per credit"
256f: 85 8d                        sta     ]blink_timer      ;stash in ZP
2571: d0 04                        bne     :NotFree          ;not free play, branch
2573: a9 02                        lda     #$02
2575: 85 c8                        sta     credit_count      ;for free play, just set credits to 2
2577: a5 fd        :NotFree        lda     dsw_n9_copy       ;get N9 switches
2579: 29 0c                        and     #%00001100        ;mask to get number of lives per game
257b: 4a                           lsr     A                 ;shift into low bits
257c: 4a                           lsr     A                 ;also clears carry flag
257d: 69 02                        adc     #$02              ;add 2 to get 2/3/4/5
257f: 85 a4                        sta     initial_lives     ;save in ZP
2581: a5 86                        lda     attract_mode      ;are we in "attract" mode?
2583: 30 01                        bmi     :Attracting       ;yes, branch
2585: 60                           rts

2586: a5 8d        :Attracting     lda     ]blink_timer      ;get coins-per-credit value
2588: f0 03                        beq     :SkipMsg          ;branch if free play
258a: 20 24 38                     jsr     DrawMessage       ;draw "X COINS Y PLAYS" message
                   ; 
258d: a5 00        :SkipMsg        lda     frame_ctr         ;get frame counter
258f: 29 20                        and     #%00100000        ;flip every 32 frames (~0.5 sec), giving us a
2591: 0a                           asl     A                 ; 1-second cycle with 50% duty
2592: 0a                           asl     A                 ;shift into high bit
2593: 85 8d                        sta     ]blink_timer      ;stash in ZP
2595: a5 c8                        lda     credit_count      ;get number of credits
2597: 05 c9                        ora     coins_inserted    ;combine with number of coins inserted
2599: f0 10                        beq     :ShowCredMin      ;both zero, branch
259b: a6 dc                        ldx     two_cred_msg_flag ;do we need to mention the 2-credit minimum?
259d: 10 29                        bpl     :ShowCredits      ;no, branch
259f: c9 02                        cmp     #$02              ;are there < 2 credits?
25a1: 90 1e                        bcc     :Lt2Cred          ;yes, branch
25a3: a9 00                        lda     #$00              ;no, at least 2 credits
25a5: 85 dc                        sta     two_cred_msg_flag ;clear the "need 2-credit min msg" flag
25a7: a9 8a                        lda     #$8a              ;erase the "2 credit minimum" message
25a9: d0 1a                        bne     :DoMinCred        ;(always)

25ab: a5 fd        :ShowCredMin    lda     dsw_n9_copy       ;get N9 settings
25ad: 29 80                        and     #$80              ;mask off all but the 2-credit-min setting
25af: 85 dc                        sta     two_cred_msg_flag ;set nonzero if we need to tell player about 2-cr min
25b1: 49 8a                        eor     #$8a              ;0->$8a->erase; 1->$0a->draw "2 CREDIT MINIMUM"
25b3: 20 24 38                     jsr     DrawMessage       ;draw (or erase) message
25b6: a2 ff                        ldx     #$ff
25b8: 8e 03 1c                     stx     ST_LED_1          ;turn off start LED #1
25bb: a2 ff        :Led2OffReturn  ldx     #$ff
25bd: 8e 04 1c                     stx     ST_LED_2          ;turn off start LED #2
25c0: 60                           rts

25c1: a9 0a        :Lt2Cred        lda     #$0a              ;"2 credit minimum"
25c3: 05 8d                        ora     ]blink_timer      ;draw or erase based on bit from frame counter
25c5: 20 24 38     :DoMinCred      jsr     DrawMessage       ;draw (or erase) message
                   ; 
25c8: a9 09        :ShowCredits    lda     #$09              ;"credits "
25ca: 20 24 38                     jsr     DrawMessage       ;draw message
25cd: a5 c8                        lda     credit_count      ;get number of credits paid for (0-16)
25cf: c9 0a                        cmp     #$0a              ;>= 10?
25d1: 90 0a                        bcc     :Lt10             ;no, branch
25d3: a9 21                        lda     #$21              ;'1'
25d5: 20 85 38                     jsr     DrawChar          ;draw digit
25d8: a5 c8                        lda     credit_count      ;get credits again
25da: 38                           sec
25db: e9 0a                        sbc     #$0a              ;reduce value by 10
25dd: 09 20        :Lt10           ora     #$20              ;convert to numeric glyph
25df: 20 85 38                     jsr     DrawChar          ;draw digit
25e2: a5 c9                        lda     coins_inserted    ;get number of coins not applied to credits
25e4: f0 02                        beq     :ZeroCoin         ;zero, branch
25e6: a9 1e                        lda     #$1e              ;'1/2'
25e8: 20 85 38     :ZeroCoin       jsr     DrawChar          ;draw blank tile or '1/2'
25eb: a6 c8                        ldx     credit_count      ;get number of credits
25ed: f0 cc                        beq     :Led2OffReturn    ;no credits, disable start LED #2 and bail
25ef: a5 dc                        lda     two_cred_msg_flag ;is the "2 credit min" flag set?
25f1: 30 c8                        bmi     :Led2OffReturn    ;yes, disable start LED #2 and bail
                   ; 
25f3: a5 8d                        lda     ]blink_timer      ;get the blink timer value ($00/$80)
25f5: 8d 03 1c                     sta     ST_LED_1          ;write to make start LED #1 blink
25f8: ad 01 0c                     lda     IN1               ;get switch inputs
25fb: a6 ff                        ldx     one               ;should be $01
25fd: 4a                           lsr     A                 ;roll player 1 start button into carry
25fe: b0 bb                        bcs     :Led2OffReturn    ;if not pressed, bail
                   ; Start a 1-player game (the only kind we have in rev4).
2600: c6 c8                        dec     credit_count      ;reduce number of credits
2602: a9 ff                        lda     #$ff
2604: 8d 03 1c                     sta     ST_LED_1          ;turn off start LEDs
2607: 8d 04 1c                     sta     ST_LED_2
260a: a9 00                        lda     #$00
260c: 85 fb                        sta     game_time         ;init game time
260e: 85 fc                        sta     game_time+1
2610: 85 9a                        sta     fire_debounce     ;init debounce register
2612: 85 cb                        sta     bonus_coin_ctr    ;reset bonus coin counters
2614: 85 ca                        sta     bonus_coin_state
2616: 86 89                        stx     num_players       ;set number of players to 1
2618: 9d 02 1c                     sta     ST_LED_1-1,x      ;light up start LED for current player
261b: a6 a4                        ldx     initial_lives     ;get max lives
261d: ca                           dex                       ;reduce by 1
261e: 86 a5                        stx     plyr_lives        ;set player #1's lives
2620: e6 86                        inc     attract_mode      ;switch to play mode ($ff -> $00)
2622: 20 a4 26                     jsr     CopyBestScores    ;copy highest scores to EAROM buffer
2625: 20 b3 21                     jsr     GetBonusScoreM    ;get low byte of bonus score setting
2628: 85 ae                        sta     plyr_bonus_mid    ;init bonus tracker for both players
262a: 85 af                        sta     plyr_bonus_mid+1
262c: b9 c0 21                     lda     bonus_score_tbl+1,y ;same for high byte
262f: 85 b0                        sta     plyr_bonus_hi
2631: 85 b1                        sta     plyr_bonus_hi+1
                   ; 
2633: ad 00 0c                     lda     IN0               ;get switch inputs
2636: 29 10                        and     #%00010000        ;check if this is a cocktail cabinet
2638: f0 07                        beq     :NotCocktail      ;not, branch
263a: a9 80                        lda     #$80
263c: 85 ee                        sta     ckp2_indic        ;set cocktail cabinet indicator
263e: 20 45 25                     jsr     InitBits          ;init bits for player 1
2641: 20 76 28     :NotCocktail    jsr     InitNewGame       ;prep for new game
2644: ad 01 08                     lda     DSW_N8            ;get N8 switches
2647: 29 1c                        and     #%00011100        ;is there a time limit?
2649: f0 0c                        beq     :JmpDLIVES        ;no, branch
264b: 4a                           lsr     A                 ;shift into low bits
264c: 4a                           lsr     A
264d: 85 ad                        sta     plyr_score2+1     ;save as minutes
264f: a9 00                        lda     #$00
2651: 85 ab                        sta     plyr_score1+1     ;set seconds to zero
2653: a9 3c                        lda     #60
2655: 85 a9                        sta     plyr_score0+1     ;set 1/60ths of a second to 60
2657: 4c bc 26     :JmpDLIVES      jmp     DrawLives         ;draw lives remaining

                   ; 
                   ; CLRCH: initializes color palette to the specified set.  There are 14 different
                   ; configurations.
                   ; 
                   ; On entry:
                   ;   X-reg: color set index (0-39 by 3)
                   ; 
265a: bd 7a 26     SetPalette      lda     :colors,x         ;get 1st value from table
265d: 48                           pha                       ;stash it
265e: bd 7b 26                     lda     :colors+1,x       ;get next value from table
2661: a8                           tay                       ;copy to Y-reg
2662: bd 7c 26                     lda     :colors+2,x       ;get final value from table
2665: aa                           tax                       ;copy to X-reg
2666: 68                           pla                       ;pop 1st value into A-reg
                   ; Set colors 1-3 for tiles and motion objects:
                   ;  1st color (A-reg) is used for tile 1 / sprite 3.
                   ;  2nd color (Y-reg) is used for tile 3 / sprite 1.
                   ;  3rd color (X-reg) is used for tile 2 / sprite 2.
                   ; 
                   ; So color #2 is the same for both, but colors 1/3 are swapped.  (This makes the
                   ; centipede body the same color as the regular mushroom.)  Color #0 is always
                   ; black/transparent.
2667: 8e 0e 14                     stx     COLOR_PAL+10      ;mobj2
266a: 8e 06 14                     stx     COLOR_PAL+2       ;tile2
266d: 8d 0f 14                     sta     COLOR_PAL+11      ;mobj3
2670: 8d 05 14                     sta     COLOR_PAL+1       ;tile1
2673: 8c 0d 14                     sty     COLOR_PAL+9       ;mobj1
2676: 8c 07 14                     sty     COLOR_PAL+3       ;tile3
2679: 60                           rts

267a: 0d 00 0e     :colors         .bulk   $0d,$00,$0e
267d: 02 04 01                     .bulk   $02,$04,$01
2680: 0e 01 0c                     .bulk   $0e,$01,$0c
2683: 04 01 0b                     .bulk   $04,$01,$0b
2686: 01 0c 0a                     .bulk   $01,$0c,$0a
2689: 09 0b 04                     .bulk   $09,$0b,$04
268c: 0c 0d 0a                     .bulk   $0c,$0d,$0a
268f: 09 0c 0e                     .bulk   $09,$0c,$0e
2692: 0a 0e 01                     .bulk   $0a,$0e,$01
2695: 0b 01 04                     .bulk   $0b,$01,$04
2698: 01 00 06                     .bulk   $01,$00,$06
269b: 0d 0e 0a                     .bulk   $0d,$0e,$0a
269e: 0e 0c 0b                     .bulk   $0e,$0c,$0b
26a1: 00 0d 02                     .bulk   $00,$0d,$02

                   ; 
                   ; COPYHS: copies three highest scores to EAROM buffer.
                   ; 
26a4: a9 ff        CopyBestScores  lda     #$ff              ;clear the "need initials" flags
26a6: 85 c1                        sta     plyr_hs_init_slot
26a8: 85 c2                        sta     plyr_hs_init_slot+1
26aa: a2 08                        ldx     #$08              ;3 sets of 3 bytes
26ac: b5 02        :Loop           lda     hs_scores,x       ;get scores
26ae: 9d 78 01                     sta     ea_copy,x         ;copy to EAROM buffer
26b1: b5 1a                        lda     hs_initials,x     ;get initials
26b3: 9d 81 01                     sta     ea_copy+9,x       ;write those too
26b6: ca                           dex
26b7: 10 f3                        bpl     :Loop             ;loop until done
26b9: 4c 4f 3a                     jmp     UpdateEaromSum    ;update the checksum

                   ; 
                   ; DLIVES: displays player lives remaining.
                   ; 
                   • Clear variables

26bc: a9 06        DrawLives       lda     #$06              ;max number of lives
26be: 85 8b                        sta     temp1
26c0: a9 04                        lda     #>PLAYFIELD
26c2: 45 f7                        eor     ckp2_03           ;factor in the flip
26c4: 29 06                        and     #$06              ;set pointer to $04xx or $06xx
26c6: 85 92                        sta     out_ptr+1         ;set high byte
26c8: a9 df                        lda     #$df
26ca: 45 f6                        eor     ckp2_3f
26cc: 85 91                        sta     out_ptr           ;set low byte of pointer
                   ; Draw 6 tiles, blanks or gun icon.
26ce: a6 a5                        ldx     plyr_lives        ;get lives remaining
26d0: a9 1f        :Loop1          lda     #$1f              ;gun image
26d2: ca                           dex                       ;drawn all?
26d3: 10 02                        bpl     :DoDraw1          ;not yet, branch
26d5: a9 00                        lda     #$00              ;draw blank instead
26d7: 20 85 38     :DoDraw1        jsr     DrawChar          ;draw the tile
26da: c6 8b                        dec     temp1             ;drawn 6 tiles?
26dc: d0 f2                        bne     :Loop1            ;not yet, loop
                   ; 
26de: a9 06                        lda     #$06
26e0: 45 f7                        eor     ckp2_03
26e2: 85 92                        sta     out_ptr+1         ;set pointer to $06xx or $05xx
26e4: a9 5f                        lda     #$5f
26e6: 45 f6                        eor     ckp2_3f
26e8: 85 91                        sta     out_ptr           ;set low byte of pointer
                   ; Repeat for player 2, but draw in opposite direction.
26ea: a9 06                        lda     #$06              ;max number of lives
26ec: 85 8b                        sta     temp1
26ee: 38                           sec
26ef: e5 a6                        sbc     plyr_lives+1      ;subtract player 2 lives
26f1: aa                           tax                       ;use as count
26f2: a9 00        :Loop2          lda     #$00              ;blank
26f4: ca                           dex                       ;drawn all blanks?
26f5: 10 02                        bpl     :DoDraw2          ;not yet, branch
26f7: a9 1f                        lda     #$1f              ;gun image
26f9: 20 85 38     :DoDraw2        jsr     DrawChar          ;draw tile
26fc: c6 8b                        dec     temp1             ;drawn 6 tiles?
26fe: d0 f2                        bne     :Loop2            ;not yet, loop
2700: 60                           rts

                   ; 
                   ; EXPLOD: explode centipede segments, spider, flea/scorpion, player.
                   ; 
                   ; If the player is exploding, and we've reached the last frame, this sets the
                   ; mushroom pointer at $da-db to point at the start of the playfield.
                   ; 
                   UpdateExplosions
2701: a2 0d                        ldx     #$0d              ;do spider/flea (everything but player/shot)
2703: b4 34        :Loop           ldy     mobj_pict,x       ;get picture
2705: c0 f9                        cpy     #$f9              ;is it alive? ($39 is blank)
2707: 90 18                        bcc     :Alive            ;yes, branch
2709: c0 fa                        cpy     #$fa              ;done exploding?
270b: 90 04                        bcc     :ExplDone         ;yes, branch
270d: d6 34                        dec     mobj_pict,x       ;advance to next explosion graphic
270f: d0 10                        bne     :Alive            ;(always)

2711: e0 0d        :ExplDone       cpx     #$0d              ;was it the spider?
2713: d0 0c                        bne     :Alive            ;no, branch
2715: a5 43                        lda     mobj_pict_plyr    ;get player picture
2717: 29 af                        and     #%10101111        ;is player alive?
2719: d0 06                        bne     :Alive            ;no, branch
271b: a5 d7                        lda     spdr_pts_mobj     ;get motion object with points for spider kill
271d: 45 ef                        eor     ckp2_c0           ;apply flip flags
271f: 85 41                        sta     mobj_pict_spdr    ;set as spider picture
                   ; 
2721: ca           :Alive          dex                       ;move to next motion object
2722: 10 df                        bpl     :Loop             ;loop until done
                   ; Now check the player.
2724: a5 43                        lda     mobj_pict_plyr    ;get player picture
2726: 29 af                        and     #%10101111        ;is player alive?
2728: f0 1a                        beq     :Return           ;yes, bail
                   ; 
272a: a5 00                        lda     frame_ctr         ;do next bit every 4 frames
272c: 29 03                        and     #$03
272e: d0 14                        bne     :Return
2730: a5 43                        lda     mobj_pict_plyr    ;get player picture
2732: c9 28                        cmp     #$28              ;have we reached end of explosion sequence?
2734: b0 0e                        bcs     :Return           ;yes, bail
2736: e6 43                        inc     mobj_pict_plyr    ;advance to next explosion image
2738: c9 27                        cmp     #$27              ;on last frame?
273a: d0 08                        bne     :Return           ;no return
273c: a9 00                        lda     #<PLAYFIELD       ;yes, configure mushroom pointer
273e: 85 da                        sta     mush_ptr
2740: a9 04                        lda     #>PLAYFIELD       ;(this acts as a flag)
2742: 85 db                        sta     mush_ptr+1
2744: 60           :Return         rts

                   ; 
                   ; GETINT: gets player initials for high score.
                   ; 
                   ; On exit:
                   ;   N-flag clear if we are entering initials
                   ; 
                   • Clear variables
                   ]initial_offset .var    $8d    {addr/1}
                   ]initial_index  .var    $8e    {addr/1}

2745: a5 c1        GetInitials     lda     plyr_hs_init_slot ;are we entering initials for either player?
2747: 25 c2                        and     plyr_hs_init_slot+1 ;(value is $ff if not entering initials)
2749: 10 01                        bpl     :GetInitials      ;need at least one, branch
274b: 60                           rts                       ;return with N-flag set

274c: a5 c2        :GetInitials    lda     plyr_hs_init_slot+1 ;do we need from player 2?
274e: 30 18                        bmi     :NotPl2           ;no, branch
2750: a5 ee                        lda     ckp2_indic        ;is this a cocktail machine?
2752: 10 14                        bpl     :NotPl2           ;no, branch
2754: a5 ef                        lda     ckp2_c0           ;are we in player 2 mode (flipped)?
2756: d0 10                        bne     :NotPl2           ;yes, already did swap; branch
2758: a9 82                        lda     #$82              ;set "player 2 and cocktail" value
275a: 85 ee                        sta     ckp2_indic
275c: 20 65 25                     jsr     InitBitsFlipped   ;set flip flags for player 2
275f: 20 fe 31                     jsr     SwapShrooms       ;switch to player 2's mushrooms
2762: 20 27 33                     jsr     DrawScores        ;redraw scores
2765: 20 36 29                     jsr     InitPlayer        ;init player position
                   ; 
2768: a5 89        :NotPl2         lda     num_players       ;get number of players (1/2)
276a: 4a                           lsr     A                 ;shift right
276b: f0 14                        beq     :Single           ;single-player game, branch
276d: a9 00                        lda     #$00              ;"player "
276f: 20 24 38                     jsr     DrawMessage       ;draw message
2772: a0 02                        ldy     #$02              ;assume player 2
2774: a6 c2                        ldx     plyr_hs_init_slot+1 ;do we need initials from player 2?
2776: 10 01                        bpl     :Pl2              ;yes, branch
2778: 88                           dey                       ;no, need player 1
2779: 84 88        :Pl2            sty     cur_player        ;set current player
277b: 98                           tya
277c: 09 20                        ora     #$20              ;convert to digit, '1' or '2'
277e: 20 85 38                     jsr     DrawChar          ;draw player number
                   ; 
2781: a9 08        :Single         lda     #$08              ;"great score"
2783: 20 24 38                     jsr     DrawMessage       ;draw message
2786: a9 05                        lda     #$05              ;"enter your initials"
2788: 20 24 38                     jsr     DrawMessage       ;draw message
278b: a9 89                        lda     #$89
278d: 45 f5                        eor     ckp2_bf           ;$89 / $36
278f: 85 91                        sta     out_ptr           ;set low byte of pointer
2791: a9 05                        lda     #$05
2793: 45 f7                        eor     ckp2_03           ;$03 / $06
2795: 85 92                        sta     out_ptr+1         ;set high byte of poitner
                   ; 
2797: a6 88                        ldx     cur_player        ;get current player
2799: b4 c0                        ldy     plyr_hs_init_slot-1,x ;get offset to high score slot
279b: 84 8d                        sty     ]initial_offset   ;save in ZP
279d: 98                           tya                       ;copy to A-reg
279e: 18                           clc
279f: 65 c0                        adc     initial_entry_num ;add number of initial we need next (0-2)
27a1: 85 8e                        sta     ]initial_index    ;save in ZP
27a3: 20 82 38                     jsr     DrawInitial       ;draw first initial
27a6: a4 8d                        ldy     ]initial_offset   ;get base offset of initials
27a8: c8                           iny                       ;add one
27a9: 20 82 38                     jsr     DrawInitial       ;draw second initial
27ac: a4 8d                        ldy     ]initial_offset   ;get base offset of initials
27ae: c8                           iny                       ;add two
27af: c8                           iny
27b0: 20 82 38                     jsr     DrawInitial       ;draw third initial
                   ; 
27b3: ad 01 0c                     lda     IN1               ;get input switches
27b6: a6 ef                        ldx     ckp2_c0           ;flipped?
27b8: f0 01                        beq     :Plyr1            ;no, do player 1
27ba: 4a                           lsr     A                 ;player 2, shift an extra bit
27bb: 4a           :Plyr1          lsr     A                 ;shift fire switch state into carry
27bc: 4a                           lsr     A
27bd: 4a                           lsr     A
27be: 26 9a                        rol     fire_debounce     ;roll it into debounce var
27c0: a5 9a                        lda     fire_debounce
27c2: 29 1f                        and     #%00011111        ;keep the low 5 bits
27c4: c9 18                        cmp     #%00011000        ;pressed for 3 frames? (off / off / on / on / on)
27c6: d0 46                        bne     :NotFire          ;no, branch
27c8: e6 c0                        inc     initial_entry_num ;yes, move to next initial
27ca: a5 c0                        lda     initial_entry_num ;get initial number
27cc: c9 03                        cmp     #$03              ;have we done all three?
27ce: 90 32                        bcc     :NextInitial      ;not yet, branch
                   ; All three initials have been entered, or we timed out.
27d0: a6 88        :Accept         ldx     cur_player        ;get current player
27d2: a9 ff                        lda     #$ff              ;reset the need-initials flag
27d4: 95 c0                        sta     plyr_hs_init_slot-1,x
27d6: a5 ef                        lda     ckp2_c0           ;are we flipped (handling player 2)?
27d8: f0 1d                        beq     :NotPl2           ;no, branch
27da: a9 80                        lda     #$80
27dc: 85 ee                        sta     ckp2_indic        ;reset cocktail machine indicator
27de: 20 45 25                     jsr     InitBits          ;set flip bits for player 1
27e1: 20 fe 31                     jsr     SwapShrooms       ;restore player 1 mushrooms
27e4: 20 36 29                     jsr     InitPlayer        ;init player
27e7: 20 27 33                     jsr     DrawScores        ;redraw scores
27ea: 20 1f 23                     jsr     InitCentipede     ;init the other goodies
27ed: 20 c7 21                     jsr     InitSpider
27f0: 20 e8 20                     jsr     InitFlea
27f3: a5 c1                        lda     plyr_hs_init_slot ;did player 1 get a high score?
27f5: 30 32                        bmi     :ShowScores       ;no, branch
27f7: a5 c1        :NotPl2         lda     plyr_hs_init_slot ;see if there's work left to do
27f9: 25 c2                        and     plyr_hs_init_slot+1
27fb: 30 17                        bmi     :EraseMsgs        ;no, branch
27fd: a2 00        :MoreLater      ldx     #$00
27ff: 86 c0                        stx     initial_entry_num ;reset initial entry num for next player
2801: 60                           rts

2802: e6 8e        :NextInitial    inc     ]initial_index    ;advance to the next initial
2804: a6 8e                        ldx     ]initial_index    ;get index in X-reg
2806: a9 f4                        lda     #$f4              ;12 * 256 / 60 = ~51 seconds
2808: 85 01                        sta     frame_ctr+1       ;set high byte of frame counter
280a: a9 01                        lda     #$01              ;'A'
280c: 95 1a                        sta     hs_initials,x     ;set initial value
280e: a5 01        :NotFire        lda     frame_ctr+1       ;check high byte of frame counter
2810: f0 be                        beq     :Accept           ;hit zero, timeout; branch
2812: d0 2e                        bne     :ReadTball        ;(always)

2814: a9 88        :EraseMsgs      lda     #$88              ;"great score"
2816: 20 24 38                     jsr     DrawMessage       ;erase message
2819: a9 85                        lda     #$85              ;"enter your initials"
281b: 20 24 38                     jsr     DrawMessage       ;erase message
281e: a9 00                        lda     #$00
2820: 8d 89 05                     sta     PLAYFIELD+$189    ;X=12 Y=9
2823: 8d a9 05                     sta     PLAYFIELD+$1a9    ;X=13 Y=9
2826: 8d c9 05                     sta     PLAYFIELD+$1c9    ;X=14 Y=9
                   ; 
2829: 20 a4 26     :ShowScores     jsr     CopyBestScores    ;copy high scores to EAROM buffer
282c: 20 60 2d                     jsr     ShowScores        ;display high score table
282f: a6 89                        ldx     num_players       ;get number of players
2831: 86 01                        stx     frame_ctr+1       ;"do not rewrite the screen for awhile"
2833: ca                           dex                       ;single-player game?
2834: f0 c7                        beq     :MoreLater        ;yes, branch
2836: a9 80                        lda     #$80              ;"player "
2838: 20 24 38                     jsr     DrawMessage       ;erase message
283b: a9 00                        lda     #$00
283d: a8                           tay
283e: 91 91                        sta     (out_ptr),y       ;clear player number
2840: f0 bb                        beq     :MoreLater
                   ; Read trackball.
2842: a5 00        :ReadTball      lda     frame_ctr
2844: 29 07                        and     #$07              ;every 8th frame
2846: d0 2b                        bne     :ZReturn          ;not yet, bail
2848: a2 ff                        ldx     #$ff
284a: a9 00                        lda     #$00
284c: a4 b9                        ldy     tball_read_horz   ;get horizontal movement
284e: 85 b9                        sta     tball_read_horz   ;reset value
2850: 10 07                        bpl     :PosMove
2852: a2 01                        ldx     #$01
2854: 98                           tya
2855: 20 7c 38                     jsr     Calc2sComp        ;invert movement
2858: a8                           tay
2859: c0 04        :PosMove        cpy     #$04              ;< 4?
285b: 90 16                        bcc     :ZReturn          ;yes, do nothing
285d: 8a                           txa
285e: 45 f4                        eor     ckp2_fe           ;1 for player 1, -1 if flipped
2860: a6 8e                        ldx     ]initial_index    ;get initial we're modifying
2862: 18                           clc
2863: 75 1a                        adc     hs_initials,x     ;add or subtract 1 from current value
2865: 30 08                        bmi     :SetZ             ;if we went past blank space, return to 'Z'
2867: c9 1b                        cmp     #$1b              ;did we pass 'Z'?
2869: 90 06                        bcc     :SetInitial       ;no, branch
286b: a9 00                        lda     #$00              ;set to blank space
286d: f0 02                        beq     :SetInitial

286f: a9 1a        :SetZ           lda     #$1a              ;'Z'
2871: 95 1a        :SetInitial     sta     hs_initials,x     ;set that initial
2873: a9 00        :ZReturn        lda     #$00              ;set N-flag (not entering initials)
2875: 60                           rts

                   ; 
                   ; INIT: initializes game state.
                   ; 
                   ; Called whenever a new game is about to start.
                   ; 
2876: a9 20        InitNewGame     lda     #$20
2878: 8d 08 10                     sta     POKEY_AUDCTL      ;init audio
287b: a9 0c                        lda     #NCENT
287d: 85 9b                        sta     plyr_cent_len     ;set centipede size
287f: 85 9c                        sta     plyr_cent_len+1
2881: a5 ff                        lda     one               ;get 1 (if all is well in system)
2883: 85 88                        sta     cur_player        ;set player number
2885: 85 53                        sta     mobj_hvel_plyr    ;direction for attract
2887: 85 83                        sta     mobj_vvel_plyr
2889: a9 02                        lda     #$02
288b: 85 9d                        sta     plyr_cent_spd     ;set centipede speed for both players
288d: 85 9e                        sta     plyr_cent_spd+1
                   ; 
288f: a2 06                        ldx     #$06
2891: a9 00                        lda     #$00
2893: 8d 0f 10                     sta     POKEY_SKCTL       ;disable POKEY
2896: 95 b2        :SnLoop         sta     cn_expl_sound_idx,x ;init sound channels
2898: ca                           dex
2899: 10 fb                        bpl     :SnLoop
                   ; 
289b: a2 05                        ldx     #$05
289d: 95 a8        :ScLoop         sta     plyr_score0,x     ;clear scores for both players
289f: ca                           dex
28a0: 10 fb                        bpl     :ScLoop
                   ; 
28a2: ad 0a 10                     lda     POKEY_RANDOM      ;POKEY is disabled, so random value should not
28a5: 4d 0a 10                     eor     POKEY_RANDOM      ; change; result here should be zero
28a8: 18                           clc
28a9: 65 c8                        adc     credit_count      ;"add credits if it's not a POKEY"
28ab: 85 c8                        sta     credit_count
28ad: a9 03                        lda     #$03
28af: 8d 0f 10                     sta     POKEY_SKCTL       ;enable POKEY
28b2: 20 1f 23                     jsr     InitCentipede     ;initialize centipede
28b5: a9 c0                        lda     #$c0              ;init counters for adding new heads
28b7: 85 a0                        sta     new_head_ctr
28b9: 85 a2                        sta     plyr_head_ctr_init
28bb: 85 a3                        sta     plyr_head_ctr_init+1
28bd: 20 c7 21                     jsr     InitSpider        ;init spider
28c0: 20 e8 20                     jsr     InitFlea          ;init flea
                   ; 
                   ; INITSC: performs partial initialization.
                   ; 
                   ; Normally we continue from the full init code above, but this is called
                   ; directly when switching to player 2.
                   ; 
                   ]row_ctr        .var    $8b    {addr/1}
                   ]tile_ptr       .var    $8d    {addr/2}

28c3: a9 0f        InitPlay        lda     #$0f              ;color=black
28c5: 8d 04 14                     sta     COLOR_PAL         ;set tile color 0 (background); does not change
28c8: a6 88                        ldx     cur_player        ;get player index
28ca: a9 00                        lda     #$00
28cc: 95 c2                        sta     leg_color-1,x     ;init per-wave palette index to zero
28ce: aa                           tax                       ;X-reg=0
28cf: 20 5a 26                     jsr     SetPalette        ;set colors to palette #0
                   ; 
28d2: a2 00                        ldx     #$00
28d4: 8a                           txa                       ;A-reg=0
28d5: 9d 00 04     :ZeroLoop       sta     PLAYFIELD,x       ;clear playfield and motion object state
28d8: 9d 00 05                     sta     PLAYFIELD+$100,x
28db: 9d 00 06                     sta     PLAYFIELD+$200,x
28de: 9d 00 07                     sta     PLAYFIELD+$300,x
28e1: e8                           inx
28e2: d0 f1                        bne     :ZeroLoop
                   ; Add mushrooms randomly.  We iterate 46 times, walking the screen from top to
                   ; bottom (row 27 to 2, inclusive).  The column is selected randomly.  The result
                   ; is a screen with at most 2 mushrooms per row, except for the bottom few lines
                   ; that will have 1 mushroom per row.  If the same spot is picked twice, the row
                   ; will have only one mushroom.
28e4: a6 88                        ldx     cur_player
28e6: 95 d7                        sta     plyr_low_mush-1,x ;set low-screen mushroom count to zero
28e8: a2 1b                        ldx     #27               ;start at row 27 (near top)
28ea: 86 8b                        stx     ]row_ctr
28ec: a2 2d                        ldx     #45               ;create up to 46 mushrooms
28ee: ad 0a 10     :Loop           lda     POKEY_RANDOM      ;get a random number for column
28f1: 29 e0                        and     #%11100000        ;keep the high 3 bits
28f3: 05 8b                        ora     ]row_ctr          ;merge row number
28f5: 85 8d                        sta     ]tile_ptr         ;use as low byte of pointer
28f7: ad 0a 10                     lda     POKEY_RANDOM      ;get another random number
28fa: 29 03                        and     #%00000011        ;need 2 more bits for the column number
28fc: 09 04                        ora     #$04              ;add 4 (for address $0400)
28fe: 85 8e                        sta     ]tile_ptr+1       ;set as high byte of pointer ($04xx-$07xx)
2900: 86 8f                        stx     temp3             ;stash counter
                   ; Update the low-screen mushroom count.
2902: a0 00                        ldy     #$00
2904: a5 8d                        lda     ]tile_ptr         ;get low byte of pointer
2906: 29 1f                        and     #%00011111        ;mask off low 5 bits, yielding the row number
2908: a6 ef                        ldx     ckp2_c0           ;check cocktail flip
290a: f0 06                        beq     :NotFlip          ;not flipped, branch
290c: c9 14                        cmp     #20               ;on upper part of flipped screen?
290e: 90 0e                        bcc     :SkipInc          ;no, branch
2910: b0 04                        bcs     :IncLow           ;(always)

2912: c9 0c        :NotFlip        cmp     #12               ;on lower part of screen?
2914: b0 08                        bcs     :SkipInc          ;no, branch
2916: b1 8d        :IncLow         lda     (]tile_ptr),y     ;is there already a mushroom here?
2918: d0 04                        bne     :SkipInc          ;yes, branch
291a: a6 88                        ldx     cur_player        ;get current player num
291c: f6 d7                        inc     plyr_low_mush-1,x ;add one to low-screen mushroom count
                   ; 
291e: a9 3f        :SkipInc        lda     #$3f              ;full un-poisoned mushroom
2920: 45 ef                        eor     ckp2_c0           ;apply cocktail flip
2922: 91 8d                        sta     (]tile_ptr),y     ;update playfield
                   ; 
2924: a5 8b                        lda     ]row_ctr          ;move down one row
2926: 38                           sec
2927: e9 01                        sbc     #$01
2929: c9 02                        cmp     #$02              ;are we near the bottom?
292b: b0 02                        bcs     :NotEdge          ;not yet, branch
292d: a9 1b                        lda     #27               ;yes, reset row
292f: 85 8b        :NotEdge        sta     ]row_ctr
2931: a6 8f                        ldx     temp3             ;get mushroom counter
2933: ca                           dex                       ;decrement
2934: 10 b8                        bpl     :Loop             ;if not done yet, loop
                   ; 
                   ; INIT1: initializes player position.
                   ; 
2936: a9 10        InitPlayer      lda     #$10              ;player gun motion object
2938: 45 f2                        eor     ckp2_40           ;flip gun vertically if screen flipped
293a: 85 43                        sta     mobj_pict_plyr    ;set picture
293c: a9 80                        lda     #$80
293e: 85 63                        sta     mobj_horz_plyr    ;set horizontal position to middle of screen
2940: 85 62                        sta     mobj_horz_shot
2942: a9 08                        lda     #$08
2944: 45 f0                        eor     ckp2_f8
2946: 85 73                        sta     mobj_vert_plyr    ;set vertical position one row up from bottom
2948: a9 0c                        lda     #$0c
294a: 45 f1                        eor     ckp2_e0
294c: 85 72                        sta     mobj_vert_shot    ;position shot up 4 pixels, so it sticks out of gun
294e: a9 11                        lda     #$11              ;player shot motion object
2950: 45 f2                        eor     ckp2_40           ;flip shot picture vertically if screen flipped
2952: 85 42                        sta     mobj_pict_shot    ;set picture
2954: 60                           rts

                   ; 
                   ; MOTION: updates position of centipede segments.
                   ; 
                   • Clear variables

2955: a5 87        MoveCentipede   lda     delay_ctr         ;are we pausing, e.g. between waves?
2957: f0 01                        beq     :NotPaused        ;no, branch
2959: 60                           rts

295a: a2 0b        :NotPaused      ldx     #NCENT-1          ;start with highest-numbered segment
295c: a5 00                        lda     frame_ctr         ;get the frame counter
295e: 29 0f                        and     #$0f              ;every 16 frames
2960: d0 04                        bne     :SegLoop          ;not yet, branch
2962: a9 07                        lda     #$07
2964: 85 b3                        sta     cn_move_sound_idx ;restart centipede movement sound
                   ; 
2966: b5 34        :SegLoop        lda     mobj_pict,x       ;get picture
2968: 10 03                        bpl     :SegAlive         ;not exploding or dead, branch
296a: 4c cb 2a                     jmp     :NextSeg

296d: a5 00        :SegAlive       lda     frame_ctr         ;get the frame counter
296f: 29 01                        and     #$01              ;every other frame
2971: d0 09                        bne     :SkipPicUpd       ;not this one
2973: b5 34                        lda     mobj_pict,x       ;get the picture number
2975: 18                           clc
2976: 69 01                        adc     #$01              ;advance one step (moves legs)
2978: 29 f7                        and     #%11110111        ;when it gets to 7, reset to 0
297a: 95 34                        sta     mobj_pict,x       ;save updated picture
                   ; 
297c: a0 01        :SkipPicUpd     ldy     #$01
297e: b5 64                        lda     mobj_vert,x       ;get vertical position
2980: 45 f0                        eor     ckp2_f8           ;adjust for flip
2982: c9 09                        cmp     #$09              ;above bottom row?
2984: b0 0a                        bcs     :NoNew1           ;yes, branch
                   ; Segment is on bottom row.  See if we want to spawn a new head.
2986: b5 34                        lda     mobj_pict,x       ;get picture number
2988: c9 10                        cmp     #$10              ;are body / poisoned-head flags set?
298a: b0 02                        bcs     :NoNew            ;yes, branch
298c: 84 97                        sty     new_head_flag     ;set "want new head" flag to $01
                   ; 
298e: b5 64        :NoNew          lda     mobj_vert,x       ;get vertical position
2990: 29 07        :NoNew1         and     #$07              ;see if we're aligned in row
2992: d0 73                        bne     :SetVert          ;not, branch
2994: 98                           tya                       ;A-reg=$01
2995: a4 88                        ldy     cur_player        ;get current player
2997: d9 94 00                     cmp     live_seg_count-1,y ;more than one live segment?
299a: d0 14                        bne     :ChkBody          ;yes, branch
                   ; Set speed to 2 if there's only one live segment.
299c: a9 02                        lda     #$02              ;default to move left
299e: b4 44                        ldy     mobj_hvel,x       ;get current move velocity
29a0: 10 02                        bpl     :MoveLeft
29a2: a9 fe                        lda     #$fe              ;-2
29a4: 95 44        :MoveLeft       sta     mobj_hvel,x       ;set new horizontal velocity
                   ; 
29a6: a9 02                        lda     #$02              ;default to move down (reverse of expectation)
29a8: b4 74                        ldy     mobj_vvel,x       ;get current move velocity
29aa: 10 02                        bpl     :MoveDown
29ac: a9 fe                        lda     #$fe              ;-2
29ae: 95 74        :MoveDown       sta     mobj_vvel,x       ;set new vertical velocity
                   ; 
29b0: b5 34        :ChkBody        lda     mobj_pict,x       ;get picture
29b2: 29 40                        and     #%01000000        ;head or body?
29b4: f0 0f                        beq     :ChkPoison        ;head, branch
29b6: b5 63                        lda     mobj_vert-1,x     ;get previous segment's vertical position
29b8: 38                           sec
29b9: f5 64                        sbc     mobj_vert,x       ;subtract current segment's vertical position
29bb: 20 7a 38                     jsr     CalcAbsVal        ;compute absolute value
29be: c9 08                        cmp     #$08              ;are they at least a full row apart?
29c0: b0 45                        bcs     :SetVert          ;yes, branch
29c2: 4c 96 2a     :Jmp_UpdatePos  jmp     :UpdateHorz

                   ; 
29c5: b5 34        :ChkPoison      lda     mobj_pict,x       ;get picture
29c7: 29 20                        and     #%00100000        ;check poison flag
29c9: d0 3c                        bne     :SetVert          ;poisoned, branch
                   ; Not poisoned, check screen edges.
29cb: b5 54                        lda     mobj_horz,x       ;get horizontal position
29cd: c9 f0                        cmp     #$f0              ;reached left edge?
29cf: 90 0a                        bcc     :NotAtLeft        ;not yet, branch
29d1: b4 74                        ldy     mobj_vvel,x       ;get vertical velocity
29d3: f0 0e                        beq     Jmp_ReverseHorz   ;velocity is zero, branch to reverse hvel
29d5: b4 44                        ldy     mobj_hvel,x       ;get horizontal velocity
29d7: 10 2e                        bpl     :SetVert          ;moving left, go change vertical
29d9: 30 0f                        bmi     :ChkShroom        ;moving right, don't change

29db: c9 10        :NotAtLeft      cmp     #$10              ;reached right edge?
29dd: b0 0b                        bcs     :ChkShroom        ;no, branch
29df: b4 74                        ldy     mobj_vvel,x       ;get vertical velocity
29e1: d0 03                        bne     :ChkHvel          ;nonzero, go check horizontal
29e3: 4c aa 2a     Jmp_ReverseHorz jmp     :ReverseHorz

29e6: b4 44        :ChkHvel        ldy     mobj_hvel,x       ;get horizontal velocity
29e8: 30 1d                        bmi     :SetVert          ;moving right, go change vertical
                   ; Check for collision with mushroom.
29ea: 20 10 23     :ChkShroom      jsr     SetObstacArgs     ;set up args
29ed: 20 2f 2c                     jsr     CalcMushPosn      ;check for collision
29f0: f0 10                        beq     :ChkOverlap       ;no collision, branch
29f2: c9 38                        cmp     #$38              ;was it a mushroom?
29f4: 90 11                        bcc     :SetVert          ;no, branch (alphanumeric tile, in attract mode)
29f6: c9 3c                        cmp     #$3c              ;was it a poisoned mushroom?
29f8: b0 0d                        bcs     :SetVert          ;no, branch
29fa: b5 34                        lda     mobj_pict,x       ;get segment picture
29fc: 09 20                        ora     #$20              ;set the "poisoned" flag
29fe: 95 34                        sta     mobj_pict,x       ;save it
2a00: 90 05                        bcc     :SetVert

2a02: 20 6f 2c     :ChkOverlap     jsr     ChkSegOverlap     ;check for overlapping segments
2a05: 90 bb                        bcc     :Jmp_UpdatePos    ;no overlap, branch
                   ; 
2a07: b5 64        :SetVert        lda     mobj_vert,x       ;get vertical position
2a09: 45 f0                        eor     ckp2_f8           ;adjust for flip
2a0b: b4 74                        ldy     mobj_vvel,x       ;get vertical velocity
2a0d: f0 d4                        beq     Jmp_ReverseHorz   ;if zero, reverse horizontal direction
2a0f: 10 12                        bpl     :Downward         ;branch if moving down
                   ; Centipede is moving upward.  Turn around at row 6.
2a11: a4 ef                        ldy     ckp2_c0           ;are we flipped?
2a13: f0 08                        beq     :NoFlip           ;no, branch
2a15: 45 f0                        eor     ckp2_f8           ;adjust for flip
2a17: c9 c9                        cmp     #$c9              ;near top of patrol area?
2a19: 90 63                        bcc     :ReverseVert      ;yes, start moving back down
2a1b: b0 68                        bcs     :UpdateVert

2a1d: c9 30        :NoFlip         cmp     #$30              ;near top of patrol area?
2a1f: b0 5d                        bcs     :ReverseVert      ;yes, start moving back down
2a21: 90 62                        bcc     :UpdateVert       ;(always)

2a23: c9 09        :Downward       cmp     #$09              ;are we at the bottom?
2a25: b0 5e                        bcs     :UpdateVert       ;not yet, branch
                   ; Centipede has reached the bottom.
2a27: b5 34                        lda     mobj_pict,x       ;get segment picture
2a29: 29 40                        and     #%01000000        ;is this a head?
2a2b: d0 51                        bne     :ReverseVert      ;no, branch
2a2d: b5 34                        lda     mobj_pict,x       ;get segment picture
2a2f: 29 df                        and     #%11011111        ;clear poisoned-head flag
2a31: 95 34                        sta     mobj_pict,x       ;save picture
2a33: e0 0b                        cpx     #NCENT-1          ;is this the last entry?
2a35: f0 47                        beq     :ReverseVert      ;yes, branch
                   ; 
2a37: 8a                           txa                       ;copy X-reg to Y-reg
2a38: a8                           tay
2a39: c8                           iny                       ;increment to index next segment
2a3a: b9 34 00                     lda     mobj_pict,y       ;get that picture
2a3d: 30 3f                        bmi     :ReverseVert      ;if dead, branch
2a3f: 29 40                        and     #%01000000        ;is this a head?
2a41: f0 3b                        beq     :ReverseVert      ;yes, branch
2a43: c0 0b        :SubSegLoop     cpy     #$0b              ;is this one the last segment?
2a45: f0 09                        beq     :Headify          ;yes, branch
2a47: b9 35 00                     lda     mobj_pict+1,y     ;get picture of next-next segment
2a4a: 30 04                        bmi     :Headify          ;branch if dead
2a4c: 29 40                        and     #%01000000        ;check for head
2a4e: d0 29                        bne     :SubSegNext       ;branch if body
                   ; 
2a50: b9 64 00     :Headify        lda     mobj_vert,y       ;get vertical position of next segment
2a53: 45 f0                        eor     ckp2_f8           ;adjust for flip
2a55: c9 09                        cmp     #$09              ;on bottom row?
2a57: b0 25                        bcs     :ReverseVert      ;"keep centipede whole if tail is not on bottom row"
2a59: b9 34 00                     lda     mobj_pict,y       ;get picture
2a5c: 29 07                        and     #%00000111        ;strip flags (making it a live, non-poisoned head)
2a5e: 99 34 00                     sta     mobj_pict,y       ;set picture
2a61: b9 44 00                     lda     mobj_hvel,y       ;get horizontal velocity
2a64: 20 7c 38                     jsr     Calc2sComp        ;negate it
2a67: 99 44 00                     sta     mobj_hvel,y       ;set velocity
2a6a: b9 64 00                     lda     mobj_vert,y       ;get vertical position
2a6d: 29 f8                        and     #%11111000        ;make it row-aligned
2a6f: 99 64 00                     sta     mobj_vert,y       ;set position
2a72: a9 00                        lda     #$00
2a74: 99 74 00                     sta     mobj_vvel,y       ;set vertical velocity to zero
2a77: f0 05                        beq     :ReverseVert

2a79: c8           :SubSegNext     iny                       ;go through all segments
2a7a: c0 0c                        cpy     #$0c              ;reached the end of the list?
2a7c: 90 c5                        bcc     :SubSegLoop       ;not yet, branch
                   ; Reverse vertical direction.
2a7e: b5 74        :ReverseVert    lda     mobj_vvel,x       ;get vertical velocity
2a80: 20 7c 38                     jsr     Calc2sComp        ;negate
2a83: 95 74                        sta     mobj_vvel,x       ;set new vertical velocity
                   ; 
2a85: b5 64        :UpdateVert     lda     mobj_vert,x       ;get vertical position
2a87: a4 ef                        ldy     ckp2_c0           ;are we flipped?
2a89: f0 06                        beq     :NoFlip           ;no, branch
2a8b: 18                           clc
2a8c: 75 74                        adc     mobj_vvel,x       ;yes, add velocity
2a8e: 4c 94 2a                     jmp     :SetVert

2a91: 38           :NoFlip         sec                       ;subtract vertical velocity (reverses value)
2a92: f5 74                        sbc     mobj_vvel,x
2a94: 95 64        :SetVert        sta     mobj_vert,x       ;set new vertical position
                   ; 
2a96: b5 44        :UpdateHorz     lda     mobj_hvel,x       ;add horizontal velocity
2a98: 18                           clc
2a99: 75 54                        adc     mobj_horz,x       ;update horizontal position
2a9b: 95 54                        sta     mobj_horz,x
                   ; 
2a9d: 20 9a 2c                     jsr     ChkPlyrColl       ;see if we collided with player
2aa0: 90 2f                        bcc     :Return           ;if so, bail
                   ; 
2aa2: b5 64                        lda     mobj_vert,x       ;get vertical position
2aa4: 29 07                        and     #%00000111        ;check position within row
2aa6: c9 04                        cmp     #$04              ;reached the halfway point?
2aa8: d0 21                        bne     :NextSeg          ;no, do nothing yet; branch
                   ; Reverse horizontal direction.
2aaa: b5 44        :ReverseHorz    lda     mobj_hvel,x       ;get horizontal velocity
2aac: 20 7c 38                     jsr     Calc2sComp        ;negate
2aaf: 95 44                        sta     mobj_hvel,x       ;save new velocity
2ab1: b4 74                        ldy     mobj_vvel,x       ;get vertical velocity
2ab3: d0 16                        bne     :NextSeg          ;if nonzero, branch
2ab5: 09 00                        ora     #$00              ;set flags for horizontal velocity
2ab7: 30 03                        bmi     :NegHV            ;branch if negative
2ab9: 20 7c 38                     jsr     Calc2sComp        ;negate horizontal velocity
2abc: 95 74        :NegHV          sta     mobj_vvel,x       ;set vertical velocity to negative horizontal speed
2abe: a9 04                        lda     #$04              ;shift 4 pixels left
2ac0: b4 44                        ldy     mobj_hvel,x       ;get horizontal velocity
2ac2: 10 02                        bpl     :PosHV            ;branch if positive
2ac4: a9 fc                        lda     #$fc              ;shift 4 pixels right
2ac6: 18           :PosHV          clc
2ac7: 75 54                        adc     mobj_horz,x       ;add to horizontal position
2ac9: 95 54                        sta     mobj_horz,x       ;save new position
                   ; 
2acb: ca           :NextSeg        dex                       ;advance to next segment
2acc: 30 03                        bmi     :Return           ;branch if all done
2ace: 4c 66 29                     jmp     :SegLoop          ;otherwise, loop

2ad1: 60           :Return         rts

                   ; 
                   ; MOVE: makes trackball calculations and moves player.
                   ; 
                   ]horz_posn      .var    $8b    {addr/1}
                   ]vert_posn      .var    $8d    {addr/1}

2ad2: a5 86        MovePlayer      lda     attract_mode      ;are we in "attract" mode?
2ad4: 10 01                        bpl     :DoMove           ;yes, branch
2ad6: 60           :Return         rts                       ;no, ignore controller

2ad7: a5 43        :DoMove         lda     mobj_pict_plyr    ;get player picture
2ad9: 29 af                        and     #%10101111        ;is player exploding or dead?
2adb: d0 f9                        bne     :Return           ;yes, bail
2add: a5 73                        lda     mobj_vert_plyr    ;get vertical position
2adf: 85 8d                        sta     ]vert_posn
                   ; 
2ae1: a4 fe                        ldy     cksum_zero        ;Y-reg=0, unless somebody has altered code
2ae3: a5 b9                        lda     tball_read_horz   ;get horizontal trackball movement
2ae5: 84 b9                        sty     tball_read_horz   ;reset value for next frame
2ae7: 20 4f 32                     jsr     ClampTrackball    ;clamp to [-4.5,4.5]
2aea: 65 84                        adc     plyr_hpos_frac
2aec: 85 84                        sta     plyr_hpos_frac    ;update fractional part
2aee: 98                           tya                       ;copy integer part to A-reg
                   ; 
                   ; MHOR: moves player horizontally.
                   ; 
                   ; While playing we fall through from the code above.  During attract mode, this
                   ; is called directly.
                   ; 
                   ; On entry:
                   ;   A-reg: distance to move
                   ;   C-flag: set or clear based on fractional movement
                   ; 
2aef: 65 63        MovePlyrHorz    adc     mobj_horz_plyr    ;add movement to position
2af1: aa                           tax                       ;copy new position to X-reg
2af2: 85 8b                        sta     ]horz_posn        ;set horizontal posn arg for collision check
2af4: a0 00                        ldy     #$00              ;use zero for horz move arg
2af6: a5 73                        lda     mobj_vert_plyr    ;get current vertical position
2af8: 20 2f 2c                     jsr     CalcMushPosn      ;check for collision
2afb: d0 11                        bne     :HitObstac        ;collided, branch
2afd: 8a                           txa                       ;get new position
2afe: c9 f4                        cmp     #$f4              ;too close to edge?
2b00: 90 04                        bcc     :CheckLft         ;no, branch
2b02: a9 f4                        lda     #$f4              ;clamp position
2b04: d0 0a                        bne     :SetHorzPosn      ;(always)

2b06: c9 0b        :CheckLft       cmp     #$0b              ;too close to edge?
2b08: b0 06                        bcs     :SetHorzPosn      ;no, branch
2b0a: a9 0b                        lda     #$0b              ;clamp position
2b0c: d0 02                        bne     :SetHorzPosn      ;(always)

2b0e: a5 63        :HitObstac      lda     mobj_horz_plyr    ;maintain previous horizontal position
2b10: 85 63        :SetHorzPosn    sta     mobj_horz_plyr    ;set new position
2b12: a4 86                        ldy     attract_mode      ;are we in "attract" mode?
2b14: 10 01                        bpl     :DoVert           ;no, branch to next part
2b16: 60                           rts

2b17: a0 00        :DoVert         ldy     #$00
2b19: a5 bb                        lda     tball_read_vert   ;get vertical trackball movement
2b1b: 84 bb                        sty     tball_read_vert   ;reset value for next frame
2b1d: 20 7c 38                     jsr     Calc2sComp        ;vertical axis is inverted
2b20: 20 4f 32                     jsr     ClampTrackball    ;clamp to [-4.5,4.5]
2b23: 65 85                        adc     plyr_vpos_frac
2b25: 85 85                        sta     plyr_vpos_frac    ;update fractional part
2b27: 98                           tya                       ;copy integer part to A-reg
                   ; 
                   ; MVER: moves player vertically.
                   ; 
                   ; While playing we fall through from the code above.  During attract mode, this
                   ; is called directly.
                   ; 
                   ;   A-reg: distance to move
                   ;   C-flag: set or clear based on fractional movement
                   ;   $8d: vertical position
                   ; 
2b28: 65 73        MovePlayerVert  adc     mobj_vert_plyr    ;add movement to position
2b2a: aa                           tax                       ;copy new position to X-reg
2b2b: a4 63                        ldy     mobj_horz_plyr    ;copy horizontal position to arg
2b2d: 84 8b                        sty     ]horz_posn
2b2f: a0 00                        ldy     #$00              ;use zero for horz move arg
2b31: 20 2f 2c                     jsr     CalcMushPosn      ;check for collision
2b34: d0 25                        bne     :HitObstac        ;collided, branch
2b36: 8a                           txa                       ;get new position
2b37: c9 08                        cmp     #$08              ;too close to bottom?
2b39: 90 1c                        bcc     :ClampNorm        ;yes, branch
2b3b: c9 f1                        cmp     #$f1              ;too close to bottom (flipped)?
2b3d: b0 14                        bcs     :ClampFlip        ;yes, branch
2b3f: c9 80                        cmp     #$80              ;on upper half of screen?
2b41: 90 08                        bcc     :NotFlip          ;no, not cocktail; branch
2b43: c9 c8                        cmp     #$c8              ;in acceptable range for flipped mode?
2b45: b0 16                        bcs     :SetVerPos        ;yes, branch
2b47: a9 c8                        lda     #$c8              ;no, clamp
2b49: d0 12                        bne     :SetVerPos        ;(always)

2b4b: c9 31        :NotFlip        cmp     #$31              ;are we too high?
2b4d: 90 0e                        bcc     :SetVerPos        ;no, branch
2b4f: a9 30                        lda     #$30              ;yes, clamp
2b51: d0 0a                        bne     :SetVerPos        ;(always)

2b53: a9 f0        :ClampFlip      lda     #$f0              ;clamp bottom when flipped
2b55: d0 06                        bne     :SetVerPos        ;(always)

2b57: a9 08        :ClampNorm      lda     #$08              ;clamp bottom
2b59: d0 02                        bne     :SetVerPos        ;(always)

2b5b: a5 73        :HitObstac      lda     mobj_vert_plyr    ;maintain previous vertical position
2b5d: 85 73        :SetVerPos      sta     mobj_vert_plyr    ;set new position
2b5f: a4 86                        ldy     attract_mode      ;are we in "attract" mode?
2b61: 10 01                        bpl     MoveUnfiredShot   ;no, branch
2b63: 60                           rts

                   ; 
                   ; RSHOT1: updates position of shot.
                   ; 
                   ; If the shot is unfired, we want to update it to track the player's position. 
                   ; If it's in flight, do nothing.
                   ; 
                   ; While playing we fall through from the code above.  During attract mode, this
                   ; is called directly.
                   ; 
                   ; On entry:
                   ;   $8d: vertical position of player
                   ; 
2b64: a5 72        MoveUnfiredShot lda     mobj_vert_shot    ;get vertical position
2b66: a6 ef                        ldx     ckp2_c0           ;check flip flags
2b68: f0 07                        beq     :NotFlipped       ;not flipped, branch
2b6a: 38                           sec
2b6b: e5 8d                        sbc     ]vert_posn        ;subtract the player's position
2b6d: b0 0e                        bcs     PlaceShot         ;below player (flipped); reposition shot
2b6f: 90 05                        bcc     :ChkFlight        ;(always)

2b71: 38           :NotFlipped     sec
2b72: e5 8d                        sbc     ]vert_posn        ;subtract the player's position
2b74: 90 07                        bcc     PlaceShot         ;below player; reposition shot
2b76: 20 7a 38     :ChkFlight      jsr     CalcAbsVal        ;get absolute value of difference
2b79: c9 05                        cmp     #$05              ;is shot in flight?
2b7b: b0 0d                        bcs     :ChkDead          ;yes, skip update
                   ; 
                   ; RSHOT: places the un-fired shot relative to the player's position.
                   ; 
2b7d: a9 04        PlaceShot       lda     #$04              ;set vertical position to player's position +4
2b7f: 45 f0                        eor     ckp2_f8
2b81: 18                           clc
2b82: 65 73                        adc     mobj_vert_plyr
2b84: 85 72                        sta     mobj_vert_shot
2b86: a5 63                        lda     mobj_horz_plyr    ;copy horizontal position
2b88: 85 62                        sta     mobj_horz_shot
                   ; 
2b8a: a5 43        :ChkDead        lda     mobj_pict_plyr    ;get player picture
2b8c: 29 af                        and     #%10101111        ;is player alive?
2b8e: f0 04                        beq     :Return           ;yes, bail
2b90: a9 28                        lda     #$28              ;no, replace shot with blank image
2b92: 85 42                        sta     mobj_pict_shot
2b94: 60           :Return         rts

                   ; 
                   ; MUSHDC: corrects count of mushrooms near bottom of screen.
                   ; 
                   ; When a mushroom is destroyed, if it's on the bottom pat of the screen (rows 2-
                   ; 11), the low-screen mushroom counter (which affects the flea) is decremented.
                   ; 
                   ; On entry:
                   ;   $32-33: pointer to mushroom tile that was destroyed
                   ; 
2b95: a5 32        DecrLowMush     lda     obst_ptr          ;get low byte of pointer
2b97: 29 1f                        and     #%00011111        ;mask to get row number
2b99: a6 ef                        ldx     ckp2_c0           ;are we flipped?
2b9b: f0 06                        beq     :NoFlip           ;no, branch
2b9d: c9 14                        cmp     #20               ;on bottom part of (flipped) screen?
2b9f: 90 0a                        bcc     :Return           ;no, bail
2ba1: b0 04                        bcs     :Decr             ;yes, branch to do decr

2ba3: c9 0c        :NoFlip         cmp     #12               ;on bottom part of screen?
2ba5: b0 04                        bcs     :Return           ;no, bail
2ba7: a6 88        :Decr           ldx     cur_player        ;get player number (1/2)
2ba9: d6 d7                        dec     plyr_low_mush-1,x ;decrement low-screen mushroom count
2bab: 60           :Return         rts

                   ; 
                   ; MUSHER: adds a new mushroom to the playfield.
                   ; 
                   ; Only adds a mushroom if one is not already present.  Will not add a mushroom
                   ; to the top row (where the scores are), the bottom row (which is always empty),
                   ; or one up from the bottom row (so the player is able to get beneath it to
                   ; shoot it).
                   ; 
                   ; If the mushroom is added on the lower part of the screen (rows 2-11), the low-
                   ; screen mushroom counter (which affects the flea) is incremented.
                   ; 
                   ; On entry:
                   ;   $32-33: pointer to playfield entry
                   ; 
2bac: a0 00        AddMushroom     ldy     #$00
2bae: b1 32                        lda     (obst_ptr),y      ;something already here?
2bb0: d0 2a                        bne     :Return           ;yes, bail
2bb2: a5 32                        lda     obst_ptr          ;get low byte of pointer
2bb4: 29 1f                        and     #%00011111        ;mask to get row number
2bb6: f0 24                        beq     :Return           ;yes, bail
2bb8: c9 1f                        cmp     #$1f              ;row 31 (top)?
2bba: f0 20                        beq     :Return           ;yes, bail
2bbc: a6 ef                        ldx     ckp2_c0           ;are we flipped?
2bbe: f0 0a                        beq     :NoFlip           ;no, branch
                   ; 
2bc0: c9 1e                        cmp     #$1e              ;one up from bottom?
2bc2: f0 18                        beq     :Return           ;yes, bail
2bc4: c9 14                        cmp     #20               ;on bottom part of (flipped) screen?
2bc6: 90 0e                        bcc     :Set              ;no, branch
2bc8: b0 08                        bcs     :LowerPart        ;yes, branch to do incr

2bca: c9 01        :NoFlip         cmp     #$01              ;one up from botton?
2bcc: f0 0e                        beq     :Return           ;yes, bail
2bce: c9 0c                        cmp     #12               ;on bottom part of screen?
2bd0: b0 04                        bcs     :Set              ;no, branch
                   ; 
2bd2: a6 88        :LowerPart      ldx     cur_player        ;get player number (1/2)
2bd4: f6 d7                        inc     plyr_low_mush-1,x ;increment low-screen mushroom count
2bd6: a9 3f        :Set            lda     #$3f              ;full mushroom
2bd8: 45 ef                        eor     ckp2_c0           ;add flip flags for cocktail player 2
2bda: 91 32                        sta     (obst_ptr),y      ;save to playfield
2bdc: 60           :Return         rts

                   ; 
                   ; NEWHD: adds new head.
                   ; 
2bdd: a5 97        CreateHead      lda     new_head_flag     ;anything to do?
2bdf: f0 4d                        beq     :Return           ;no, bail
2be1: a5 87                        lda     delay_ctr         ;are we on a break?
2be3: d0 49                        bne     :Return           ;yes, bail
2be5: a5 a0                        lda     new_head_ctr      ;is it time?
2be7: f0 03                        beq     :AddHead          ;yes, do it
2be9: c6 a0                        dec     new_head_ctr      ;no, update counter
2beb: 60                           rts

                   ; Find an empty slot.
2bec: a6 88        :AddHead        ldx     cur_player        ;get current player
2bee: a0 0b                        ldy     #NCENT-1          ;start in last centipede segment slot
2bf0: b9 34 00     :FindLoop       lda     mobj_pict,y       ;get picture
2bf3: 30 04                        bmi     :InitSlot         ;found a dead segment, branch
2bf5: 88                           dey                       ;try the next one
2bf6: 10 f8                        bpl     :FindLoop         ;loop until we've tried all
2bf8: 60                           rts

2bf9: a9 00        :InitSlot       lda     #$00
2bfb: 99 34 00                     sta     mobj_pict,y       ;set picture to live, non-poisoned, head
2bfe: a9 40                        lda     #$40              ;row 8
2c00: 45 f0                        eor     ckp2_f8           ;apply flip bits
2c02: 99 64 00                     sta     mobj_vert,y       ;set vertical position
2c05: a9 fc                        lda     #$fc
2c07: 99 54 00                     sta     mobj_horz,y       ;set horizontal position to left edge
2c0a: a9 02                        lda     #$02
2c0c: 99 74 00                     sta     mobj_vvel,y       ;set velocity to move downward
                   ; 
2c0f: b5 a1                        lda     plyr_head_ctr_init-1,x ;get new head counter initial value
2c11: c9 60                        cmp     #$60              ;~1.5 seconds
2c13: 90 04                        bcc     :DontReduce       ;don't reduce it any further
2c15: e9 08                        sbc     #$08              ;reduce the cooldown
2c17: 95 a1                        sta     plyr_head_ctr_init-1,x
2c19: 85 a0        :DontReduce     sta     new_head_ctr      ;reset the counter
                   ; 
2c1b: ad 0a 10                     lda     POKEY_RANDOM      ;get a random number
2c1e: 29 02                        and     #%00000010        ;0 or 2
2c20: d0 07                        bne     :SetHVel          ;if 2, branch (50%)
2c22: a9 04                        lda     #$04
2c24: 99 54 00                     sta     mobj_horz,y       ;set horizontal position to 4 (right edge)
2c27: a9 fe                        lda     #$fe              ;change velocity to -2 (move right?)
2c29: 99 44 00     :SetHVel        sta     mobj_hvel,y       ;set horizontal velocity
2c2c: f6 94                        inc     live_seg_count-1,x
2c2e: 60           :Return         rts

                   ; 
                   ; OBSTAC: calculates address of tile based on motion object coordinates.
                   ; 
                   ; This is used to determine whether a motion object and a mushroom are
                   ; attempting to occupy the same space at the same time.
                   ; 
                   ; The 16-bit pointer for a given row/column falls in the range $0400-07bf, and
                   ; has the form $0000BBcc cccrrrrr, where BB is the base address, ccccc is the
                   ; column (0-29), and rrrrrr is the row (0-31).  Because the display is
                   ; effectively rotated 90 degrees counter-clockwise, col 0 row 0 ($0400) is at
                   ; the bottom left.
                   ; 
                   ; To form the pointer, we take the motion object's vertical position, divide by
                   ; 8 (the height of a cell), and use that as the low 5 bits (000VVVVV).  We then
                   ; take the horizontal position, and mask off the low bits to get HHHHH00.  We
                   ; shift that left twice to get 000000HH HHH00000 and combine it with the low
                   ; bits to get the offset.  Instead of adding $0400, we just start with the low
                   ; bit of the high byte set, so it turns into a 4 when it's shifted twice.
                   ; 
                   ; On entry:
                   ;   A-reg: motion object vertical position
                   ;   Y-reg: motion object horizontal offset (usually 0, can be $01 / $ff)
                   ;   $8b: motion object horizontal position
                   ; 
                   ; On exit:
                   ;   A-reg: current playfield value (without flip flags)
                   ;   $32-33 (obst_ptr): playfield address
                   ;   (Z-flag set according to A-reg, i.e. Z=1 if nothing on field)
                   ;   Y-reg: $00
                   ;   (X-reg unmodified)
                   ; 
                   ]mobj_hpos_arg  .var    $8b    {addr/1}

2c2f: 4a           CalcMushPosn    lsr     A                 ;divide vertical position by 8
2c30: 4a                           lsr     A
2c31: 4a                           lsr     A
2c32: 69 00                        adc     #$00              ;round up
2c34: 85 32                        sta     obst_ptr          ;set low byte of pointer (000vvvvv)
2c36: a9 01                        lda     #$01              ;base addr of playfield ($0400) / 4
2c38: 85 33                        sta     obst_ptr+1        ;init high byte
                   ; 
2c3a: 98                           tya                       ;get horizontal velocity
2c3b: 0a                           asl     A                 ;multiply by 8
2c3c: 0a                           asl     A
2c3d: 0a                           asl     A
2c3e: 18                           clc
2c3f: 65 8b                        adc     ]mobj_hpos_arg    ;add to horizontal position
2c41: 85 8b                        sta     ]mobj_hpos_arg    ;save combined value
                   ; 
2c43: a9 f7                        lda     #$f7              ;compute (247 - hpos)
2c45: 38                           sec
2c46: e5 8b                        sbc     ]mobj_hpos_arg    ;watch for underflow
2c48: b0 02                        bcs     :NoWrap           ;didn't wrap around screen; branch
2c4a: a9 00                        lda     #$00              ;use left margin
2c4c: 29 f8        :NoWrap         and     #%11111000        ;clear low 3 bits
2c4e: 0a                           asl     A                 ;shift left twice; note bit for base addr already set
2c4f: 26 33                        rol     obst_ptr+1
2c51: 0a                           asl     A
2c52: 26 33                        rol     obst_ptr+1
2c54: 05 32                        ora     obst_ptr          ;merge horiz posn bits with vert posn bits
                   ; 
2c56: a4 33                        ldy     obst_ptr+1        ;get high byte of pointer
2c58: c0 07                        cpy     #$07              ;is it $07xx?
2c5a: d0 08                        bne     :PtrOkay          ;no, branch
2c5c: c9 c0                        cmp     #$c0              ;is it >= $07c0?
2c5e: 90 04                        bcc     :PtrOkay          ;no, branch
2c60: 29 1f                        and     #%00011111        ;off right side; strip column bits from low byte
2c62: 09 a0                        ora     #%10100000        ;set column to %11101 = 29
2c64: 85 32        :PtrOkay        sta     obst_ptr          ;set low byte of pointer
                   ; 
2c66: a0 00                        ldy     #$00
2c68: b1 32                        lda     (obst_ptr),y      ;get current value
2c6a: f0 02                        beq     :Return           ;zero, bail
2c6c: 45 ef                        eor     ckp2_c0           ;undo the flip bits for cocktail player 2
2c6e: 60           :Return         rts

                   ; 
                   ; OVRLAP: checks for overlapping segments.
                   ; 
                   ; The goal here is to see if the motion object specified by X-reg is overlapping
                   ; with any of the centipede segments.  It also tests whether the object is
                   ; overlapping with the flea/scorpion; not sure if that was intended.
                   ; 
                   ; On entry:
                   ;   X-reg: motion object to test ($00-0f)
                   ; 
                   ; On exit:
                   ;   C-flag: set if collision
                   ; 
                   • Clear variables
                   ]index          .var    $8b    {addr/1}
                   ]inc_index      .var    $8c    {addr/1}

2c6f: 86 8b        ChkSegOverlap   stx     ]index
2c71: 86 8c                        stx     ]inc_index        ;(?)
2c73: e6 8c                        inc     ]inc_index        ;(?)
2c75: a0 0c                        ldy     #NCENT            ;compare against flea and all centipede segments
2c77: b9 64 00     :Loop           lda     mobj_vert,y       ;get vertical position
2c7a: d5 64                        cmp     mobj_vert,x       ;compare to object of interest
2c7c: d0 17                        bne     :Next             ;not on same row, branch
2c7e: b9 34 00                     lda     mobj_pict,y       ;get picture
2c81: c9 f4                        cmp     #$f4              ;dead segment?
2c83: b0 10                        bcs     :Next             ;yes, branch
2c85: c4 8b                        cpy     ]index            ;are we comparing with ourselves?
2c87: f0 0c                        beq     :Next             ;yes, branch
                   ; 
2c89: b5 54                        lda     mobj_horz,x       ;get object's horizontal position
2c8b: 38                           sec
2c8c: f9 54 00                     sbc     mobj_horz,y       ;compute difference
2c8f: 55 44                        eor     mobj_hvel,x       ;"look only in from of us" [sic]
2c91: c9 f4                        cmp     #$f4              ;overlap?
2c93: b0 04                        bcs     :Return           ;collision; return with carry set
2c95: 88           :Next           dey
2c96: 10 df                        bpl     :Loop
2c98: 18                           clc                       ;indicate no collision
2c99: 60           :Return         rts

                   ; 
                   ; PLAY: checks for player collision with a motion object.
                   ; 
                   ; Pass in the object to test against, e.g. $0c for the flea.  In the event of a
                   ; collision, this will initiate the player explosion and death.
                   ; 
                   ; On entry:
                   ;   X-reg: index of motion object to check
                   ; 
                   ; On exit:
                   ;   C-flag: clear if a collision occurred
                   ;   (X-reg unmodified)
                   ; 
                   • Clear variables
                   ]horz_dist      .var    $8d    {addr/1}

2c9a: b5 54        ChkPlyrColl     lda     mobj_horz,x       ;get horizontal position
2c9c: 38                           sec
2c9d: e5 63                        sbc     mobj_horz_plyr    ;subtract player's horizontal position
2c9f: 20 7a 38                     jsr     CalcAbsVal        ;get absolute value
2ca2: e0 0d                        cpx     #$0d              ;is this the spider?
2ca4: d0 05                        bne     :NotSpider        ;no, branch
2ca6: c9 0a                        cmp     #10               ;is object < 10 units away? (spider is wide)
2ca8: 90 05                        bcc     :CheckVert        ;yes, check vertical distance
2caa: 60           :Return         rts                       ;return, with carry set (all paths)

2cab: c9 07        :NotSpider      cmp     #7                ;is object < 7 units away?
2cad: b0 fb                        bcs     :Return           ;no, bail
                   ; 
2caf: 85 8d        :CheckVert      sta     ]horz_dist        ;stash horizontal distance in ZP
2cb1: b5 64                        lda     mobj_vert,x       ;get vertical position
2cb3: 38                           sec
2cb4: e5 73                        sbc     mobj_vert_plyr    ;subtract player's vertical position
2cb6: 20 7a 38                     jsr     CalcAbsVal        ;get absolute value
2cb9: c9 07                        cmp     #7                ;is object >= 7 units away?
2cbb: b0 ed                        bcs     :Return           ;yes, bail
2cbd: 18                           clc
2cbe: 65 8d                        adc     ]horz_dist        ;combine vertical and horizontal
2cc0: e0 0d                        cpx     #$0d              ;is this the spider?
2cc2: f0 2a                        beq     :ChkSpider        ;yes, branch
2cc4: c9 0c                        cmp     #12               ;< 12 units?
2cc6: b0 e2        :BcsReturn      bcs     :Return           ;if not, bail
                   ; 
                   ; PLAYEX: makes the player explode.
                   ; 
                   ; Normally we get here by falling through from the code above, but this is also
                   ; called directly when the rev4 game timer expires.
                   ; 
2cc8: a9 30        ExplodePlayer   lda     #48
2cca: 85 87                        sta     delay_ctr         ;pause briefly
2ccc: a9 20                        lda     #$20
2cce: 85 43                        sta     mobj_pict_plyr    ;set picture to first explosion
2cd0: a9 ff                        lda     #$ff
2cd2: 95 34                        sta     mobj_pict,x       ;deactivate the thing we ran into
2cd4: a9 28                        lda     #$28
2cd6: 85 42                        sta     mobj_pict_shot    ;set shot to blank image
2cd8: a5 86                        lda     attract_mode      ;are we in "attract" mode?
2cda: 30 04                        bmi     :NoSound          ;yes, branch
2cdc: a9 13                        lda     #19
2cde: 85 b7                        sta     plexpl_sound_idx  ;init explosion sound sequence
2ce0: a9 00        :NoSound        lda     #$00
2ce2: 85 b2                        sta     cn_expl_sound_idx ;halt all other sounds
2ce4: 85 b3                        sta     cn_move_sound_idx
2ce6: 85 b4                        sta     shot_sound_idx
2ce8: 85 b5                        sta     spider_sound_idx
2cea: 85 b8                        sta     scorp_sound_idx
2cec: 18                           clc                       ;clear carry to indicate collision and death
2ced: 60                           rts

2cee: c9 0e        :ChkSpider      cmp     #14               ;< 14 units?
2cf0: 4c c6 2c                     jmp     :BcsReturn        ;return if not

                   ; 
                   ; RESTOR: restores mushrooms.  Called every frame.
                   ; 
                   ; If the mushroom pointer is zero, this does nothing.  The pointer is only set
                   ; when the player has been killed.
                   ; 
                   ; If we find a partial or poisoned mushroom, convert it to a full mushroom,
                   ; initiate an explosion, and return.  Future calls will find additional
                   ; mushrooms.
                   ; 
                   ; On entry:
                   ;   $dc-db: playfield pointer for start of search
                   ; 
                   • Clear variables

2cf3: a0 00        RestoreShroom   ldy     #$00              ;set Y-reg to zero, does not change
2cf5: a5 00                        lda     frame_ctr         ;get frame counter
2cf7: 29 07                        and     #%00000111        ;every 16 frames
2cf9: d0 14                        bne     :Return           ;not yet, bail
2cfb: a5 b7                        lda     plexpl_sound_idx  ;player busily exploding?
2cfd: d0 10                        bne     :Return           ;yes, bail
                   ; 
2cff: a5 db        :Loop           lda     mush_ptr+1        ;get high byte of pointer
2d01: f0 0c                        beq     :Return           ;if zero, bail
2d03: c9 07                        cmp     #$07              ;check for $07c0 (end of playfield)
2d05: d0 09                        bne     :NotEnd
2d07: a5 da                        lda     mush_ptr          ;test low byte
2d09: c9 c0                        cmp     #$c0
2d0b: 90 03                        bcc     :NotEnd
2d0d: 84 db                        sty     mush_ptr+1        ;all done, set pointer to zero
2d0f: 60           :Return         rts

2d10: b1 da        :NotEnd         lda     (mush_ptr),y      ;get tile from playfield
2d12: 29 3f                        and     #%00111111        ;strip flip flags
2d14: c9 38                        cmp     #$38              ;is it a mushroom?
2d16: 90 40                        bcc     :Next             ;no, move on to next
2d18: c9 3f                        cmp     #$3f              ;is it a full, non-poisoned mushroom?
2d1a: b0 3c                        bcs     :Next             ;yes, move on to next
                   ; 
2d1c: a9 3f                        lda     #$3f              ;full mushroom
2d1e: 45 ef                        eor     ckp2_c0           ;set flip flags
2d20: 91 da                        sta     (mush_ptr),y      ;set playfield entry
2d22: a9 00                        lda     #$00
2d24: 85 8b                        sta     temp1             ;high byte of score
2d26: a9 05                        lda     #$05              ;low byte of score
2d28: 20 ba 2d                     jsr     AddPoints         ;add 5 points
                   ; Put motion object explosion on mushroom, to give it a little zazz.  Use the
                   ; last centipede segment.
2d2b: a9 ff                        lda     #$ff
2d2d: 85 3f                        sta     mobj_pict+11      ;last centipede segment
2d2f: a5 db                        lda     mush_ptr+1        ;get high byte of pointer
2d31: 85 8b                        sta     temp1             ;store in ZP
2d33: a5 da                        lda     mush_ptr          ;get low byte of pointer
2d35: 0a                           asl     A                 ;multiply pointer by 8
2d36: 26 8b                        rol     temp1             ;so $0400-07bf --> $2000-3df8 by 8
2d38: 0a                           asl     A
2d39: 26 8b                        rol     temp1
2d3b: 0a                           asl     A
2d3c: 26 8b                        rol     temp1
2d3e: 85 6f                        sta     mobj_vert+11      ;use low byte as vertical position ($00-f8)
2d40: a5 8b                        lda     temp1             ;get high byte ($20-3d)
2d42: 29 1f                        and     #$1f              ;mask off high bit ($00-1d)
2d44: 49 1f                        eor     #$1f              ;invert ($1f-02)
2d46: 0a                           asl     A                 ;multiply by 8
2d47: 0a                           asl     A                 ;to get $f8-10 by 8
2d48: 0a                           asl     A                 ;(also clears carry)
2d49: e9 03                        sbc     #$03              ;subtract 4 to center explosion on tile
2d4b: 85 5f                        sta     mobj_horz+11      ;set horizontal position
                   ; 
2d4d: e6 da                        inc     mush_ptr          ;advance pointer
2d4f: d0 02                        bne     :NoInc
2d51: e6 db                        inc     mush_ptr+1        ;advance high byte of pointer
2d53: a9 13        :NoInc          lda     #19
2d55: 85 b2                        sta     cn_expl_sound_idx ;make a sound
2d57: 60                           rts

2d58: e6 da        :Next           inc     mush_ptr
2d5a: d0 a3                        bne     :Loop
2d5c: e6 db                        inc     mush_ptr+1
2d5e: d0 b0                        bne     :NotEnd           ;(always)

                   ; 
                   ; SCORES: displays high score table.
                   ; 
                   • Clear variables

2d60: a9 07        ShowScores      lda     #$07              ;"high scores"
2d62: 20 24 38                     jsr     DrawMessage       ;draw message
2d65: a0 00                        ldy     #$00              ;start at start of high score table
2d67: a2 5c                        ldx     #$5c              ;initial value for playfield pointer
2d69: 86 91        :Loop           stx     out_ptr           ;set low byte (determines row, part of col)
2d6b: a9 05                        lda     #$05
2d6d: 85 92                        sta     out_ptr+1         ;set high byte
                   ; 
2d6f: b9 04 00                     lda     hs_scores+2,y     ;get high byte of score
2d72: 84 8d                        sty     temp2             ;preserve index
2d74: 38                           sec                       ;enable zero suppression
2d75: 20 9e 38                     jsr     Draw2Digits       ;draw two digits
2d78: a4 8d                        ldy     temp2             ;restore index
2d7a: b9 03 00                     lda     hs_scores+1,y     ;get middle byte
2d7d: 20 9e 38                     jsr     Draw2Digits       ;draw
2d80: a4 8d                        ldy     temp2
2d82: b9 02 00                     lda     hs_scores,y       ;get low byte
2d85: 18                           clc                       ;disable zero suppression
2d86: 20 9e 38                     jsr     Draw2Digits       ;draw
2d89: a9 00                        lda     #$00
2d8b: 20 85 38                     jsr     DrawChar          ;draw blank space
2d8e: a4 8d                        ldy     temp2             ;get index into high score table
2d90: 20 82 38                     jsr     DrawInitial       ;draw first initial
2d93: e6 8d                        inc     temp2
2d95: a4 8d                        ldy     temp2
2d97: 20 82 38                     jsr     DrawInitial       ;draw second initial
2d9a: e6 8d                        inc     temp2
2d9c: a4 8d                        ldy     temp2
2d9e: 20 82 38                     jsr     DrawInitial       ;draw third initial
                   ; 
2da1: a5 91                        lda     out_ptr           ;move to next row
2da3: 29 1f                        and     #$1f
2da5: 09 40                        ora     #$40
2da7: aa                           tax
2da8: ca                           dex
2da9: a4 8d                        ldy     temp2             ;get score table index
2dab: c8                           iny                       ;advance to next set
2dac: c0 18                        cpy     #24               ;done yet?
2dae: 90 b9                        bcc     :Loop             ;no, loop
2db0: 60                           rts

2db1: c9                           .dd1    $c9               ;(checksum byte)

                   ; 
                   ; SCORNG: adds points to score after centipede segment hit.  Updates live
                   ; segment count.
                   ; 
                   ; On entry:
                   ;   A-reg: points to be added (1st digit)
                   ;   $8b: points to be added (2nd digit)
                   ;   X-reg: index of player (1/2)
                   ; 
                   ; On exit:
                   ;   (X-reg preserved)
                   ; 
2db2: 86 8d        AddPointsC      stx     temp2             ;preserve X-reg
2db4: a6 88                        ldx     cur_player        ;get current player number
2db6: d6 94                        dec     live_seg_count-1,x ;decrement segment count
2db8: a6 8d                        ldx     temp2             ;restore X-reg
                   ; 
                   ; SCORN1: adds points to score after non-centipede object hit.
                   ; 
                   ; Also checks if we need to award a bonus life, and updates new-head cooldown
                   ; every 10K points.
                   ; 
                   ; On entry:
                   ;   A-reg: points to be added (low byte)
                   ;   $8b: points to be added (high byte)
                   ;   X-reg: index of player (1/2)
                   ; 
2dba: a4 86        AddPoints       ldy     attract_mode      ;are we in "attract" mode?
2dbc: 30 5d                        bmi     :Return           ;yes, bail
2dbe: 86 8d                        stx     temp2             ;preserve X-reg
2dc0: f8                           sed                       ;switch to decimal mode
2dc1: a6 88                        ldx     cur_player        ;get current player
2dc3: 18                           clc
2dc4: 75 a7                        adc     plyr_score0-1,x   ;add points to low byte of score
2dc6: 95 a7                        sta     plyr_score0-1,x   ;save score
2dc8: b5 a9                        lda     plyr_score1-1,x
2dca: 65 8b                        adc     temp1             ;add upper byte of points to middle byte of score
2dcc: 95 a9                        sta     plyr_score1-1,x   ;save value
2dce: 90 0d                        bcc     :Not10k           ;if not on 10K boundary, branch
2dd0: b5 a1                        lda     plyr_head_ctr_init-1,x ;reduce cooldown on appearance of new heads
2dd2: e9 02                        sbc     #$02              ; (note carry is set)
2dd4: 95 a1                        sta     plyr_head_ctr_init-1,x
2dd6: b5 ab                        lda     plyr_score2-1,x   ;now increment the high byte of the score
2dd8: 18                           clc
2dd9: 69 01                        adc     #$01              ;must use ADC in decimal mode
2ddb: 95 ab                        sta     plyr_score2-1,x
                   ; 
2ddd: d8           :Not10k         cld                       ;clear decimal mode
2dde: a6 88                        ldx     cur_player        ;get current player
2de0: b5 a9                        lda     plyr_score1-1,x   ;get middle byte of score
2de2: d5 ad                        cmp     plyr_bonus_mid-1,x ;have we reached the bonus threshold?
2de4: b5 ab                        lda     plyr_score2-1,x
2de6: f5 af                        sbc     plyr_bonus_hi-1,x
2de8: 90 2f                        bcc     :XReturn          ;no bonus yet, bail
                   ; Update next bonus score for this player.
2dea: 20 b3 21                     jsr     GetBonusScoreM    ;get mid byte and set index
2ded: f8                           sed                       ;enable decimal mode
2dee: 18                           clc
2def: 75 ad                        adc     plyr_bonus_mid-1,x ;add to mid byte
2df1: 95 ad                        sta     plyr_bonus_mid-1,x
2df3: b9 c0 21                     lda     bonus_score_tbl+1,y ;get high byte from bonus table
2df6: 75 af                        adc     plyr_bonus_hi-1,x ;add to score tracker
2df8: 95 af                        sta     plyr_bonus_hi-1,x
2dfa: d8                           cld                       ;disable decimal mode
                   ; 
2dfb: ad 01 08                     lda     DSW_N8            ;get N8 switches
2dfe: 29 1c                        and     #%00011100        ;see if a timer is enabled
2e00: f0 06                        beq     :AddLife          ;no, branch
2e02: a5 ad                        lda     plyr_score2+1     ;see if the timer has run out
2e04: 05 ab                        ora     plyr_score1+1
2e06: f0 11                        beq     :XReturn          ;if timer hit zero, no bonus lives
                   ; 
2e08: b5 a4        :AddLife        lda     plyr_lives-1,x    ;get lives remaining for this player
2e0a: c9 06                        cmp     #$06              ;have we reached 6?
2e0c: f0 0b                        beq     :XReturn          ;yes, no bonus for you
2e0e: b0 fe        :SpinDeath      bcs     :SpinDeath        ;somehow we have more than 6... reset machine
2e10: f6 a4                        inc     plyr_lives-1,x    ;add one life
2e12: a9 11                        lda     #17               ;enable bonus sound, which has 17 steps
2e14: 85 b6                        sta     bonus_sound_idx
2e16: 20 bc 26                     jsr     DrawLives         ;update player life display
2e19: a6 8d        :XReturn        ldx     temp2             ;restore X-reg
2e1b: 60           :Return         rts

                   ; 
                   ; SCORP: starts and moves scorpion.
                   ; 
2e1c: a5 40        MoveScorpion    lda     mobj_pict_flea    ;get flea/scorpion picture
2e1e: 45 ef                        eor     ckp2_c0           ;undo flip bits
2e20: c9 34                        cmp     #$34              ;are we exploding?
2e22: 90 03                        bcc     :NoExplod         ;no, branch
2e24: 4c a5 2e                     jmp     :Exploding        ;yes, handle it

2e27: c9 30        :NoExplod       cmp     #$30              ;is it a scorpion?
2e29: b0 5d                        bcs     :MoveScorp        ;yes, go move it
2e2b: a5 70                        lda     mobj_vert_flea    ;get vertical position of flea
2e2d: 45 f0                        eor     ckp2_f8           ;undo flip bits
2e2f: c9 f8                        cmp     #$f8              ;at top of screen?
2e31: 90 04                        bcc     :Jmp_Return       ;no, it's a flea in motion; bail
2e33: a5 00                        lda     frame_ctr         ;no flea; good time for a scorpion?
2e35: f0 03                        beq     :CreateScorp      ;yes, do it
2e37: 4c d6 2e     :Jmp_Return     jmp     :Return           ;(always)

2e3a: a6 88        :CreateScorp    ldx     cur_player        ;get current player
2e3c: b5 9a                        lda     plyr_cent_len-1,x ;get current centipede length
2e3e: c9 0b                        cmp     #NCENT-1          ;full size?
2e40: b0 f5                        bcs     :Jmp_Return       ;bail ("wait until waves where centipede is small")
2e42: ad 0a 10                     lda     POKEY_RANDOM      ;get a random number
2e45: 29 03                        and     #$03
2e47: d0 ee                        bne     :Jmp_Return       ;75% chance of bailing
                   ; 
2e49: a9 14                        lda     #20
2e4b: 85 b8                        sta     scorp_sound_idx   ;initiate scorpion sound effect
2e4d: a9 30                        lda     #$30              ;first scorpion picture ($30-33)
2e4f: 45 ef                        eor     ckp2_c0           ;apply flip bits
2e51: 85 40                        sta     mobj_pict_flea    ;set picture
2e53: b5 ab                        lda     plyr_score2-1,x   ;get high byte of score
2e55: c9 02                        cmp     #$02              ;< 20K points?
2e57: 90 12                        bcc     :Lt20k            ;yes, branch
2e59: ad 0a 10                     lda     POKEY_RANDOM      ;get random value for speed
2e5c: 29 03                        and     #$03
2e5e: f0 0b                        beq     :Lt20k            ;25% chance for speed=1
2e60: a9 02                        lda     #$02              ;set speed=2
2e62: 2c 0a 10                     bit     POKEY_RANDOM      ;50/50 chance
2e65: 10 0d                        bpl     :SetMove
2e67: a9 fe                        lda     #$fe              ;set speed=-2
2e69: d0 09                        bne     :SetMove

2e6b: a9 01        :Lt20k          lda     #$01              ;set speed=1
2e6d: 2c 0a 10                     bit     POKEY_RANDOM      ;50/50 chance
2e70: 10 02                        bpl     :SetMove
2e72: a9 ff                        lda     #$ff              ;set speed=-1
2e74: 85 50        :SetMove        sta     mobj_hvel_flea
2e76: a9 00                        lda     #$00
2e78: 85 60                        sta     mobj_horz_flea    ;set horizontal position to zero
2e7a: 85 80                        sta     mobj_vvel_flea    ;set vertical velocity to zero (move horizontally)
2e7c: ad 0a 10                     lda     POKEY_RANDOM      ;get random value
2e7f: 29 78                        and     #%01111000        ;number from $08-78, in multiples of 8 (row 1-15)
2e81: 18                           clc
2e82: 69 70                        adc     #$70              ;add $70, now $78-e8 (row 15-29)
2e84: 45 f0                        eor     ckp2_f8           ;apply flip flags
2e86: 85 70                        sta     mobj_vert_flea    ;set vertical position
                   ; 
2e88: a5 43        :MoveScorp      lda     mobj_pict_plyr    ;check player
2e8a: 29 af                        and     #%10101111        ;alive?
2e8c: d0 1d                        bne     :RemoveScorp      ;no, branch to remove scorpion
2e8e: a5 60                        lda     mobj_horz_flea    ;get horizontal position
2e90: a4 ef                        ldy     ckp2_c0           ;flipped?
2e92: f0 06                        beq     :NoFlip           ;no, branch
2e94: 38                           sec
2e95: e5 50                        sbc     mobj_hvel_flea    ;move flipped scorp
2e97: 4c 9d 2e                     jmp     :SetHpos

2e9a: 18           :NoFlip         clc                       ;not flipped, add horizontal velocity
2e9b: 65 50                        adc     mobj_hvel_flea
2e9d: 85 60        :SetHpos        sta     mobj_horz_flea    ;set new horizontal position
2e9f: 85 8b                        sta     temp1             ;copy to ZP for mushroom collision calc later on
2ea1: d0 0b                        bne     :UpdateScorp      ;not at right edge, branch
2ea3: f0 06                        beq     :RemoveScorp      ;(always) reached edge of screen, remove it

2ea5: 45 ef        :Exploding      eor     ckp2_c0           ;undo flip bits
2ea7: c9 fa                        cmp     #$fa              ;still exploding (pic >= $3a)?
2ea9: b0 2b                        bcs     :Return           ;yes, bail
                   ; 
2eab: 4c e8 20     :RemoveScorp    jmp     InitFlea          ;reset flea / scorpion

2eae: a5 00        :UpdateScorp    lda     frame_ctr         ;get frame counter
2eb0: 29 03                        and     #$03              ;evey 4 frames
2eb2: d0 0d                        bne     :ChkShroom        ;not yet, branch
2eb4: a5 40                        lda     mobj_pict_flea    ;get picture
2eb6: 18                           clc
2eb7: 69 01                        adc     #$01              ;advance to next picture
2eb9: 29 03                        and     #%00000011        ;reduce to 0-3
2ebb: 09 30                        ora     #$30              ;add $30 ($30-33)
2ebd: 45 ef                        eor     ckp2_c0           ;apply flip flags
2ebf: 85 40                        sta     mobj_pict_flea    ;set picture
                   ; 
2ec1: a0 00        :ChkShroom      ldy     #$00              ;horizontal velocity
2ec3: a5 70                        lda     mobj_vert_flea    ;get vertical position
2ec5: 20 2f 2c                     jsr     CalcMushPosn      ;see if we ran into a mushroom
2ec8: c9 40                        cmp     #$40              ;value >= $40? (flip flags already removed)
2eca: b0 0a                        bcs     :Return           ;yes, bail
2ecc: c9 3c                        cmp     #$3c              ;is this an un-poisoned mushroom?
2ece: 90 06                        bcc     :Return           ;no, bail
2ed0: 29 fb                        and     #%11111011        ;change $3c-3f to $38-3b
2ed2: 45 ef                        eor     ckp2_c0           ;apply flip flags
2ed4: 91 32                        sta     (obst_ptr),y      ;write poisoned mushroom to playfield
2ed6: 60           :Return         rts

                   ; 
                   ; SHOOT: checks fire switch and fires shot.  Updates shot position, and checks
                   ; for collisions with motion objects and mushrooms.
                   ; 
2ed7: a5 72        UpdateShot      lda     mobj_vert_shot    ;get current vertical position
2ed9: a4 ef                        ldy     ckp2_c0           ;are we flipped?
2edb: f0 05                        beq     :NoFlip           ;no, branch
2edd: 18                           clc
2ede: 69 07                        adc     #$07              ;add 7 before inverting, so we're always calculating
2ee0: 49 ff                        eor     #$ff              ; from the same edge
2ee2: c9 f3        :NoFlip         cmp     #$f3              ;reached top of screen?
2ee4: b0 75                        bcs     Jmp_ResetShot     ;yes, reset position
                   ; 
2ee6: a9 04                        lda     #$04              ;offset by 4
2ee8: 45 f0                        eor     ckp2_f8           ;if flipped, -4
2eea: 18                           clc
2eeb: 65 73                        adc     mobj_vert_plyr    ;add player's vertical position
2eed: c5 72                        cmp     mobj_vert_shot    ;is it the same (i.e. shot has not been fired)?
2eef: d0 25                        bne     :MoveShot         ;no, move the shot
                   ; Shot still loaded.  Check the fire button.
2ef1: a5 43                        lda     mobj_pict_plyr    ;get player picture
2ef3: 29 af                        and     #%10101111        ;player dead or exploding?
2ef5: d0 18                        bne     Jmp_ChkAllDead    ;yes, branch
2ef7: ad 01 0c                     lda     IN1               ;get input switches
2efa: a6 86                        ldx     attract_mode      ;are we in "attract" mode?
2efc: 10 03                        bpl     :NotAttr          ;no, branch
2efe: ad 0a 10                     lda     POKEY_RANDOM      ;get random number
2f01: c0 c0        :NotAttr        cpy     #$c0              ;are we flipped?
2f03: d0 06                        bne     :Plyr1            ;no, player 1 is up
2f05: 29 08                        and     #%00001000        ;mask to get player 2 fire button
2f07: f0 09                        beq     :FireShot         ;value is zero, button is pressed
2f09: d0 04                        bne     Jmp_ChkAllDead

2f0b: 29 04        :Plyr1          and     #%00000100        ;mask to get player 1 fire button
2f0d: f0 03                        beq     :FireShot         ;value is zero, button is pressed
2f0f: 4c 5a 30     Jmp_ChkAllDead  jmp     :ChkAllDead

2f12: a9 0b        :FireShot       lda     #11
2f14: 85 b4                        sta     shot_sound_idx    ;initiate shot sound
                   ; 
2f16: a5 62        :MoveShot       lda     mobj_horz_shot    ;get horizontal position
2f18: 85 8b                        sta     temp1             ;copy to ZP for obstacle check
2f1a: a9 07                        lda     #$07              ;shot moves 7 pixels each time
2f1c: 45 f4                        eor     ckp2_fe           ;invert value if we're flipped
2f1e: a4 72                        ldy     mobj_vert_shot    ;get current vertical position in Y-reg
2f20: 18                           clc
2f21: 65 72                        adc     mobj_vert_shot    ;add the adjustment
2f23: 85 72                        sta     mobj_vert_shot    ;save new value
2f25: 84 8d                        sty     temp2             ;save original position in ZP
2f27: a9 01                        lda     #$01              ;adjust by +1
2f29: 45 f3                        eor     ckp2_ff           ;or -2 if flipped
2f2b: 18                           clc
2f2c: 65 8d                        adc     temp2             ;add to previous vertical position
2f2e: a0 00                        ldy     #$00              ;horizontal adj
2f30: 20 2f 2c                     jsr     CalcMushPosn      ;check for collision with mushroom
2f33: f0 29                        beq     :ChkMobjColl      ;no collision, branch
2f35: 29 3f                        and     #$3f              ;strip flip flags
2f37: c9 38                        cmp     #$38              ;was it some sort of mushroom?
2f39: 90 20                        bcc     Jmp_ResetShot     ;no, branch to reset shot
                   ; Subtract one from mushroom tile value.
2f3b: e9 01                        sbc     #$01              ;subtract one from tile value (carry is set)
2f3d: c9 3b                        cmp     #$3b              ;was it the last stage of an unpoisoned mushroom?
2f3f: f0 04                        beq     :KillShroom       ;yes, branch
2f41: c9 37                        cmp     #$37              ;last stage of a poisoned mushroom?
2f43: d0 0d                        bne     :NotGone          ;no, branch
2f45: a9 00        :KillShroom     lda     #$00
2f47: 85 8b                        sta     temp1             ;set high byte of score adjustment to zero
2f49: a9 01                        lda     #$01              ;low byte to 1
2f4b: 20 ba 2d                     jsr     AddPoints         ;add 1 point to score
2f4e: a0 00                        ldy     #$00              ;replace with blank
2f50: a5 ef                        lda     ckp2_c0           ;set flip flags
2f52: 45 ef        :NotGone        eor     ckp2_c0           ;apply flip flags (removes them from blank tile)
2f54: 91 32                        sta     (obst_ptr),y      ;store updated tile
2f56: d0 03                        bne     Jmp_ResetShot     ;branch if we didn't kill the mushroom
2f58: 20 95 2b                     jsr     DecrLowMush       ;update count if the mushroom was near bottom
2f5b: 4c 57 30     Jmp_ResetShot   jmp     :ResetShot        ;reload

                   ; Didn't hit a mushroom.  Check for collisions with other motion objects.
2f5e: a2 0d        :ChkMobjColl    ldx     #$0d              ;start with spider ($0e=shot, $0f=player)
2f60: b5 34        :MobjLoop       lda     mobj_pict,x       ;get picture
2f62: c9 76                        cmp     #$76              ;is it alive?
2f64: 90 08                        bcc     :Alive            ;yes, branch
2f66: c9 b9                        cmp     #$b9              ;is it dead?
2f68: 90 69                        bcc     :Jmp_MobjNext     ;yes, branch
2f6a: c9 f8                        cmp     #$f8              ;seriously, is it dead?
2f6c: b0 65                        bcs     :Jmp_MobjNext     ;yes, branch
2f6e: b5 64        :Alive          lda     mobj_vert,x       ;get vertical position
2f70: 45 f0                        eor     ckp2_f8           ;apply flip flags
2f72: c9 f8                        cmp     #$f8              ;top row?
2f74: b0 5d                        bcs     :Jmp_MobjNext     ;yes, ignore this one
                   ; Check for collision.  Start by computing vertical distance.
2f76: 45 f0                        eor     ckp2_f8           ;undo flip flags
2f78: 38                           sec
2f79: e5 72                        sbc     mobj_vert_shot    ;subtract shot position
2f7b: 20 7a 38                     jsr     CalcAbsVal        ;get absolute value
2f7e: a8                           tay                       ;copy distance to Y-reg
2f7f: e0 0c                        cpx     #$0c              ;is this flea/scorpion slot?
2f81: d0 16                        bne     :NotFastFlea      ;no, branch
2f83: b5 34                        lda     mobj_pict,x       ;get picture
2f85: 45 ef                        eor     ckp2_c0           ;apply flip flags
2f87: c9 20                        cmp     #$20              ;is it a flea?
2f89: b0 0e                        bcs     :NotFastFlea      ;no, branch
                   ; 
2f8b: a5 80                        lda     mobj_vvel_flea    ;get flea's vertical velocity
2f8d: 45 f0                        eor     ckp2_f8           ;apply flip flags
2f8f: c9 04                        cmp     #$04              ;is it a fast flea?
2f91: 90 06                        bcc     :NotFastFlea      ;no, branch
2f93: c0 07                        cpy     #$07              ;fast flea check: distance >= 7?
2f95: b0 3c                        bcs     :Jmp_MobjNext     ;yes, no collision; branch
2f97: 90 04                        bcc     :ChkHorz

2f99: c0 05        :NotFastFlea    cpy     #$05              ;general check: distance >= 5?
2f9b: b0 36                        bcs     :Jmp_MobjNext     ;yes, no collison; branch
                   ; 
2f9d: b5 54        :ChkHorz        lda     mobj_horz,x       ;get horizontal position
2f9f: 38                           sec
2fa0: e5 62                        sbc     mobj_horz_shot    ;subtract shot position
2fa2: 20 7a 38                     jsr     CalcAbsVal        ;get absolute value
2fa5: a8                           tay                       ;copy distance to Y-reg
2fa6: e0 0d                        cpx     #$0d              ;is this the spider?
2fa8: f0 25                        beq     :ChkSpider        ;yes, branch
2faa: e0 0c                        cpx     #$0c              ;is this a centipede segment?
2fac: 90 60                        bcc     :ChkSegment       ;yes, branch
2fae: b5 34                        lda     mobj_pict,x       ;get picture
2fb0: 45 ef                        eor     ckp2_c0           ;undo flip flags
2fb2: c9 20                        cmp     #$20              ;scorpion?
2fb4: b0 10                        bcs     :ChkScorp         ;yes, branch
2fb6: c0 06                        cpy     #6                ;flea check: distance >= 6?
2fb8: b0 19                        bcs     :Jmp_MobjNext     ;yes, no collision; branch
                   ; 
2fba: a0 02                        ldy     #$02              ;fleas are worth 200 pts
2fbc: a9 04                        lda     #$04              ;fast fleas move at 4 pixels/frame
2fbe: c5 80                        cmp     mobj_vvel_flea    ;is this a fast flea?
2fc0: f0 0a                        beq     :Jmp_UpdateScore  ;yes, kill it and update the score
2fc2: 85 80                        sta     mobj_vvel_flea    ;no, make it a fast flea
2fc4: d0 95                        bne     Jmp_ResetShot     ;reload

2fc6: c0 0a        :ChkScorp       cpy     #10               ;distance >= 10?
2fc8: b0 09                        bcs     :Jmp_MobjNext     ;yes, no collision; branch
2fca: a0 10                        ldy     #$10              ;1000 pts for scorpion
                   :Jmp_UpdateScore
2fcc: 4c 48 30                     jmp     :UpdateScore

2fcf: c0 0a        :ChkSpider      cpy     #10               ;distance >= 10?
2fd1: 90 03                        bcc     :CalcSpdrPts      ;yes, no collision; branch
2fd3: 4c 42 30     :Jmp_MobjNext   jmp     :MobjNext

                   ; Calculate points for killing the spider, based on vertical distance from
                   ; player.
2fd6: a0 b6        :CalcSpdrPts    ldy     #$b6              ;"300" = $36, ORed with $80 to flip horizontally
2fd8: 84 d7                        sty     spdr_pts_mobj
2fda: a0 03                        ldy     #$03              ;300 to start
2fdc: a5 71                        lda     mobj_vert_spdr    ;get spider vertical position
2fde: 38                           sec
2fdf: e5 73                        sbc     mobj_vert_plyr    ;compute difference from player position
2fe1: 20 7a 38                     jsr     CalcAbsVal        ;compute absolute value
2fe4: c9 40                        cmp     #64               ;>= 64?
2fe6: b0 0c                        bcs     :SetScore         ;yes, branch (300 pts)
2fe8: e6 d7                        inc     spdr_pts_mobj     ;incr to $37 ("900")
2fea: a0 09                        ldy     #$09              ;900 for scoring routine
2fec: c9 16                        cmp     #22               ;distance < 22?
2fee: 90 04                        bcc     :SetScore         ;yes, branch (900 pts)
2ff0: e6 d7                        inc     spdr_pts_mobj     ;incr to $38 ("600")
2ff2: a0 06                        ldy     #$06              ;600 for scoring routine
2ff4: a9 80        :SetScore       lda     #$80
2ff6: 85 9f                        sta     spider_cd_ctr     ;reset spider cooldown
2ff8: 85 a1                        sta     spider_cooldown
2ffa: a9 00                        lda     #$00
2ffc: 85 b5                        sta     spider_sound_idx  ;halt spider sound
2ffe: a9 f0                        lda     #$f0
3000: c5 61                        cmp     mobj_horz_spdr    ;is it off left edge?
3002: 90 06                        bcc     :ClampHpos        ;yes, branch
3004: a9 10                        lda     #$10
3006: c5 61                        cmp     mobj_horz_spdr    ;too close to right edge?
3008: 90 3e                        bcc     :UpdateScore      ;no, branch
300a: 85 61        :ClampHpos      sta     mobj_horz_spdr    ;set horizontal position to clamped value
300c: d0 3a                        bne     :UpdateScore      ;(always)

300e: c0 06        :ChkSegment     cpy     #6                ;horizontal delta >= 6?
3010: b0 30                        bcs     :MobjNext         ;yes, no collision; branch
3012: a9 00                        lda     #$00
3014: 85 8b                        sta     temp1             ;set high byte of score to zero
3016: a0 10                        ldy     #$10              ;set low byte to 10 (BCD)
3018: b5 34                        lda     mobj_pict,x       ;check object that was hit
301a: 29 40                        and     #%01000000        ;was it a head?
301c: d0 04                        bne     :NotHead          ;no, branch
301e: a0 00                        ldy     #$00              ;set low byte of score to zero
3020: e6 8b                        inc     temp1             ;set high byte to 1, yielding 100 (BCD)
3022: 98           :NotHead        tya                       ;put low byte of score in A-reg
3023: 20 b2 2d                     jsr     AddPointsC        ;update score, and decrement live segment count
                   ; 
3026: e0 0b                        cpx     #NCENT-1          ;was this the last segment in the array?
3028: f0 08                        beq     :NoPromote        ;yes, branch
302a: b5 35                        lda     mobj_pict+1,x     ;was this a tail (next seg is dead)?
302c: 30 04                        bmi     :NoPromote        ;yes, branch
302e: 29 bf                        and     #%10111111        ;clear "body" flag
3030: 95 35                        sta     mobj_pict+1,x     ;next segment becomes a head
3032: 20 10 23     :NoPromote      jsr     SetObstacArgs     ;set up args
3035: 20 2f 2c                     jsr     CalcMushPosn      ;identify tile overlapped by segment
3038: 86 8d                        stx     temp2             ;preserve X-reg
303a: 20 ac 2b                     jsr     AddMushroom       ;add a mushroom where the segment was hit
303d: a6 8d                        ldx     temp2             ;restore X-reg
303f: 4c 4f 30                     jmp     :StExplSnd        ;go make some noise

3042: ca           :MobjNext       dex                       ;advance to next motion object
3043: 30 15                        bmi     :ChkAllDead       ;if we're done, branch
3045: 4c 60 2f                     jmp     :MobjLoop         ;otherwise, loop

                   ; Add points to the score, and mark the slot as dead.
3048: 84 8b        :UpdateScore    sty     temp1             ;copy score value (/ 100) to ZP
304a: a9 00                        lda     #$00              ;set low byte to zero
304c: 20 ba 2d                     jsr     AddPoints         ;update score
                   ; 
304f: a9 13        :StExplSnd      lda     #19
3051: 85 b2                        sta     cn_expl_sound_idx ;start the centipede explosion sound
3053: a9 ff                        lda     #$ff
3055: 95 34                        sta     mobj_pict,x       ;mark this motion object as dead
                   ; 
3057: 20 7d 2b     :ResetShot      jsr     PlaceShot         ;place unfired shot relative to player
305a: a6 88        :ChkAllDead     ldx     cur_player        ;get player number
305c: b5 94                        lda     live_seg_count-1,x ;get number of live segments
305e: 05 87                        ora     delay_ctr         ;this is nonzero if we're pausing
3060: f0 10                        beq     :IncSpeed         ;centipede all dead, or waiting for next wave
                   ; 
3062: a5 41                        lda     mobj_pict_spdr    ;get spider picture
3064: 45 ef                        eor     ckp2_c0           ;clear flip flags
3066: c9 9c                        cmp     #$9c              ;is spider alive? (<=$1c)
3068: 90 07                        bcc     :Return           ;yes, bail
306a: c6 9f                        dec     spider_cd_ctr     ;reduce new spider cooldown
306c: d0 03                        bne     :Return
306e: 20 c7 21                     jsr     InitSpider        ;reset the spider
3071: 60           :Return         rts

3072: f6 9c        :IncSpeed       inc     plyr_cent_spd-1,x ;increase centipede speed
3074: a9 40                        lda     #64
3076: 85 87                        sta     delay_ctr         ;delay ~1 sec before next wave
3078: 60                           rts

                   ; 
                   ; SOUNDS: controls sounds for game.
                   ; 
                   ; The original sources numbered the channels 0-3, but the POKEY documentation
                   ; online labels them 1-4.  I'm following the POKEY doc convention.
                   ; 
                   ; Channel assignments:
                   ;   1: all explosions
                   ;   2: bonus, centipede, flea/scorpion sounds
                   ;   3: shot sound
                   ;   4: spider sound (changes every 2 frames)
                   ; 
3079: a6 86        UpdateSound     ldx     attract_mode      ;are we in "attract" mode?
307b: 10 0f                        bpl     :Playing          ;no, branch
307d: a2 00                        ldx     #$00              ;not playing game, silence all channels
307f: 8e 01 10                     stx     POKEY_AUDC1
3082: 8e 03 10                     stx     POKEY_AUDC2
3085: 8e 05 10                     stx     POKEY_AUDC3
3088: 8e 07 10                     stx     POKEY_AUDC4
308b: 60                           rts

                   ; Every other frame, change spider sound.
308c: a5 00        :Playing        lda     frame_ctr         ;get frame counter
308e: 4a                           lsr     A                 ;shift low bit into carry
308f: 90 19                        bcc     :ChkShot          ;bit clear, don't change sound this frame
3091: a4 b5                        ldy     spider_sound_idx  ;get spider sound index
3093: 98                           tya
3094: f0 11                        beq     :SetSpider        ;zero, spider is inactive; branch
3096: c6 b5                        dec     spider_sound_idx  ;decrement value
3098: d0 04                        bne     :UpdSpider        ;haven't hit zero yet, branch
309a: a9 14                        lda     #20               ;reset to 20
309c: 85 b5                        sta     spider_sound_idx  ;save
309e: b9 b0 31     :UpdSpider      lda     snd_freq3-1,y     ;copy new freq/ctrl values
30a1: 8d 06 10                     sta     POKEY_AUDF4
30a4: b9 c4 31                     lda     snd_ctrl3-1,y
30a7: 8d 07 10     :SetSpider      sta     POKEY_AUDC4
                   ; The shot sound plays for 11 frames, then stops.
30aa: a4 b4        :ChkShot        ldy     shot_sound_idx    ;get shot sound value
30ac: 98                           tya
30ad: f0 0a                        beq     :NoShot           ;not playing sound, branch
30af: c6 b4                        dec     shot_sound_idx
30b1: b9 a5 31                     lda     snd_freq2-1,y
30b4: 8d 04 10                     sta     POKEY_AUDF3       ;set frequency
30b7: a9 64                        lda     #%01100100        ;5-bit poly noise, volume=4
30b9: 8d 05 10     :NoShot         sta     POKEY_AUDC3
                   ; rev4: see if it's time to play a time-low warning.  We play the sound for two
                   ; seconds, when the time remaining is 0:15 or 0:14.
30bc: a5 ad                        lda     plyr_score2+1     ;get minutes left
30be: d0 14                        bne     :ChkBonus         ;nonzero, no alert
30c0: a5 ab                        lda     plyr_score1+1     ;get seconds left
30c2: c9 16                        cmp     #$16              ;>= 16 (BCD)?
30c4: b0 0e                        bcs     :ChkBonus         ;yes, no alert
30c6: c9 14                        cmp     #$14              ;< 14 (BCD)?
30c8: 90 0a                        bcc     :ChkBonus         ;yes, no alert
30ca: a5 00                        lda     frame_ctr         ;get frame counter
30cc: 29 04                        and     #%00000100        ;on for 4 frames, off for 4 frames
30ce: f0 16                        beq     :SetAudF2         ;set freq to zero
30d0: a9 10                        lda     #$10
30d2: d0 12                        bne     :SetAudF2         ;set freq to $10

                   ; Check for bonus life sound (has priority in channel 2).
30d4: a4 b6        :ChkBonus       ldy     bonus_sound_idx   ;playing the bonus sound?
30d6: f0 28                        beq     :ChkFSE           ;no, branch
30d8: a5 00                        lda     frame_ctr         ;get frame counter
30da: 29 07                        and     #%00000111        ;reduce to 0-7
30dc: d0 73                        bne     :ChkExplosion     ;skip update if 1-7 (update every 8 frames)
30de: c6 b6                        dec     bonus_sound_idx   ;update index
30e0: 88                           dey
30e1: f0 1d                        beq     :ChkFSE           ;sound done, assume something else will update chan 2
30e3: b9 d8 31                     lda     snd_freq4-1,y     ;get next freq from bonus sound sequence
30e6: 8d 02 10     :SetAudF2       sta     POKEY_AUDF2       ;set hardware
30e9: a9 a4                        lda     #%10100100        ;no poly, volume=4
30eb: d0 61                        bne     :SetAudC2         ;(always)

                   ; Update scorpion sound in channel 2, which has a 20-element cycle.
30ed: a4 b8        :ScorpSound     ldy     scorp_sound_idx   ;get scorpion / flea sound index
30ef: 88                           dey                       ;update
30f0: d0 02                        bne     :UpdFlea          ;not yet zero, branch
30f2: a0 14                        ldy     #20               ;reset to start
30f4: 84 b8        :UpdFlea        sty     scorp_sound_idx   ;save index
30f6: b9 e9 31                     lda     snd_freq6-1,y     ;get frequency value
30f9: 8d 02 10                     sta     POKEY_AUDF2       ;set hardware
30fc: a9 a4                        lda     #%10100100        ;noise=no poly, volume=4
30fe: d0 4e                        bne     :SetAudC2

3100: a5 70        :ChkFSE         lda     mobj_vert_flea    ;get flea vertical position
3102: 45 f0                        eor     ckp2_f8           ;adjust for flip
3104: c9 f8                        cmp     #$f8              ;is it off screen?
3106: b0 36                        bcs     :CentMove         ;yes, no flea
3108: a5 43                        lda     mobj_pict_plyr    ;get player
310a: 29 af                        and     #%10101111        ;exploding or dead?
310c: d0 30                        bne     :CentMove         ;no, branch
310e: a5 40                        lda     mobj_pict_flea    ;get flea picture
3110: 45 ef                        eor     ckp2_c0           ;adjust for flip
3112: c9 34                        cmp     #$34              ;is it >= flea/scorpion bitmaps (exploding)?
3114: b0 28                        bcs     :CentMove         ;yes, branch
3116: c9 20                        cmp     #$20              ;is it the scorpion?
3118: b0 d3                        bcs     :ScorpSound       ;yes, branch
                   ; Update flea sound, which is purely based on vertical position.
311a: a5 70                        lda     mobj_vert_flea    ;get flea vertical position
311c: 45 f4                        eor     ckp2_fe           ;if flipped, invert all but low bit
311e: 4a                           lsr     A                 ;divide by 2
311f: 49 ff                        eor     #$ff              ;invert
3121: 09 80                        ora     #$80              ;set high bit to use lower frequencies
3123: 8d 02 10                     sta     POKEY_AUDF2       ;set hardware
3126: a9 a4                        lda     #%10100100        ;noise=no poly, volume=4
3128: d0 24                        bne     :SetAudC2

312a: a4 b2        :CentExplode    ldy     cn_expl_sound_idx ;playing centipede explosion sound?
312c: 98                           tya
312d: f0 0b                        beq     :SetAudC1         ;if not, branch
312f: c6 b2                        dec     cn_expl_sound_idx ;update
3131: b9 71 31                     lda     snd_freq0-1,y     ;copy freq/ctrl to hardware
3134: 8d 00 10                     sta     POKEY_AUDF1
3137: b9 84 31                     lda     snd_ctrl0-1,y
313a: 8d 01 10     :SetAudC1       sta     POKEY_AUDC1
313d: 60                           rts

                   ; Play centipede movement sound if nothing else is on channel 2.
313e: a4 b3        :CentMove       ldy     cn_move_sound_idx ;get centipede movement sound index
3140: 98                           tya
3141: f0 0b                        beq     :SetAudC2         ;if inactive, silence the channel
3143: c6 b3                        dec     cn_move_sound_idx ;update the index
3145: b9 97 31                     lda     snd_freq1-1,y     ;copy freq/ctrl to hardware
3148: 8d 02 10                     sta     POKEY_AUDF2
314b: b9 9e 31                     lda     snd_ctrl1-1,y
314e: 8d 03 10     :SetAudC2       sta     POKEY_AUDC2
                   ; Update player explosion sound, which has 18 elements.
3151: a4 b7        :ChkExplosion   ldy     plexpl_sound_idx  ;get player explosion sound index
3153: f0 d5                        beq     :CentExplode      ;not exploding, branch
3155: a5 00                        lda     frame_ctr         ;get frame counter
3157: 29 03                        and     #%00000011        ;update every 4th frame
3159: d0 16                        bne     :Return
315b: c6 b7                        dec     plexpl_sound_idx  ;update counter in memory
315d: 88                           dey                       ;update counter in Y-reg
315e: f0 11                        beq     :Return           ;if reached end, bail w/o setting sound
3160: b9 71 31                     lda     snd_freq0-1,y     ;get current frequency
3163: 8d 00 10                     sta     POKEY_AUDF1       ;set hardware
3166: b9 84 31                     lda     snd_ctrl0-1,y     ;get current control byte
3169: f0 03                        beq     :SetAudC1_2       ;if zero, write as-is
316b: 18                           clc
316c: 69 02                        adc     #$02              ;otherwise, add 2 (increases volume)
316e: 8d 01 10     :SetAudC1_2     sta     POKEY_AUDC1       ;set hardware
3171: 60           :Return         rts

                   ; 
                   ; Sound 0: explosion.  There are 19 freq/ctrl pairs, which update every 4th
                   ; frame.
                   ; 
3172: 00 00 00 00+ snd_freq0       .bulk   $00,$00,$00,$00,$f0,$e0,$d0,$c0,$b0,$a0,$90,$80,$70,$60,$50,$40
                                    +      $30,$20,$10
3185: 00 00 00 00+ snd_ctrl0       .bulk   $00,$00,$00,$00,$81,$81,$81,$82,$82,$82,$82,$83,$83,$83,$83,$84
                                    +      $84,$84,$84
                   ; 
                   ; Sound 1: centipede moving.  7 elements, updated every frame.
                   ; 
3198: 70 00 00 a0+ snd_freq1       .bulk   $70,$00,$00,$a0,$00,$c0,$e0
319f: a1 00 00 a2+ snd_ctrl1       .bulk   $a1,$00,$00,$a2,$00,$a2,$a4
                   ; 
                   ; Sound 2: shot sound.
                   ; 
31a6: f0 e0 d0 c0+ snd_freq2       .bulk   $f0,$e0,$d0,$c0,$b0,$a0,$90,$80,$70,$60,$50
                   ; 
                   ; Sound 3: spider moving.  There are 20 freq/ctrl pairs, which change every
                   ; other frame.
                   ; 
31b1: 05 05 20 20+ snd_freq3       .bulk   $05,$05,$20,$20,$30,$30,$35,$35,$30,$30,$20,$20,$05,$05,$20,$20
                                    +      $30,$30,$35,$35
31c5: a1 00 a2 00+ snd_ctrl3       .bulk   $a1,$00,$a2,$00,$a3,$00,$a4,$00,$a3,$00,$a2,$00,$a1,$00,$a2,$00
                                    +      $a3,$00,$a2,$00
                   ; 
                   ; Sound 4: bonus life.
                   ; 
31d9: 28 28 30 28+ snd_freq4       .bulk   $28,$28,$30,$28,$28,$30,$3c,$51,$50,$50,$60,$50,$50,$60,$74,$a2
                                    +      $00
                   ; 
                   ; Sound 6: scorpion moving.  20 freq/ctrl pairs.
                   ; 
31ea: 60 60 70 70+ snd_freq6       .bulk   $60,$60,$70,$70,$60,$60,$60,$70,$70,$70,$50,$50,$80,$80,$50,$50
                                    +      $50,$80,$80,$80

                   ; 
                   ; SWAP: swaps mushroom layouts in two-player game.
                   ; 
                   ; The idea is to record the mushroom positions in compressed form, with 1 bit
                   ; per mushroom, in the low 120 bytes of the stack area.  We simultaneously
                   ; restore the previously-saved contents to the screen.  The net effect is to
                   ; completely rewrite the playfield contents.
                   ; 
                   • Clear variables
                   ]mush_acc       .var    $8b    {addr/1}
                   ]store_index    .var    $8d    {addr/1}
                   ]saved_index    .var    $8e    {addr/1}

31fe: a0 00        SwapShrooms     ldy     #$00              ;set playfield pointer
3200: 84 32                        sty     obst_ptr
3202: a9 04                        lda     #>PLAYFIELD
3204: 85 33                        sta     obst_ptr+1
3206: 84 8d                        sty     ]store_index      ;index into storage, init to zero
                   ; 
3208: a9 00        :OuterLoop      lda     #$00
320a: 85 8b                        sta     ]mush_acc
320c: 84 8e                        sty     ]saved_index      ;copy initial output index value to ZP
320e: a2 08                        ldx     #$08              ;get next 8 playfield values
3210: b1 32        :Loop1          lda     (obst_ptr),y      ;get playfield object picture num
3212: 29 3f                        and     #%00111111        ;strip off the flip bits
3214: c9 38                        cmp     #$38              ;is object >= $38 (i.e. is it a full/partial shroom)?
3216: 26 8b                        rol     ]mush_acc         ;if yet, carry is set; roll that into result
3218: c8                           iny                       ;advance to next address
3219: ca                           dex                       ;done with 8?
321a: d0 f4                        bne     :Loop1            ;not yet, loop
                   ; 
321c: a6 8d                        ldx     ]store_index      ;get storage index
321e: bd 00 01                     lda     screen_store_arr,x ;get current contents from storage
3221: e6 8d                        inc     ]store_index      ;update storage index
3223: a8                           tay                       ;copy current contents to Y-reg
3224: a5 8b                        lda     ]mush_acc         ;get bits we just generated
3226: 9d 00 01                     sta     screen_store_arr,x ;write those to storage
3229: 84 8b                        sty     ]mush_acc         ;store previous contents in our bit accumulator
                   ; 
322b: a4 8e                        ldy     ]saved_index      ;get output index
322d: a2 08                        ldx     #$08              ;set next 8 playfield values
322f: a9 00        :Loop2          lda     #$00              ;default to blank tile
3231: 26 8b                        rol     ]mush_acc         ;shift the high bit into the carry
3233: 90 04                        bcc     :Store            ;if clear, write the blank
3235: a9 3f                        lda     #$3f              ;healthy full mushroom tile
3237: 45 ef                        eor     ckp2_c0           ;apply flip flags
3239: 91 32        :Store          sta     (obst_ptr),y      ;write to playfield
323b: c8                           iny                       ;advance to next address
323c: ca                           dex                       ;done with 8?
323d: d0 f0                        bne     :Loop2            ;not yet, loop
                   ; 
323f: 98                           tya                       ;set flags for output index
3240: d0 02                        bne     :NoInc            ;if we haven't reached end of page, branch
3242: e6 33                        inc     obst_ptr+1        ;advance high byte of pointer
3244: c0 c0        :NoInc          cpy     #$c0              ;low byte of pointer at $c0?
3246: d0 c0                        bne     :OuterLoop        ;no, loop
3248: a5 33                        lda     obst_ptr+1        ;check high byte of pointer
324a: c9 07                        cmp     #$07              ;at $07?
324c: d0 ba                        bne     :OuterLoop        ;no, haven't hit $07c0 yet; loop
324e: 60                           rts

                   ; 
                   ; TBLMT: limits trackball movement.
                   ; 
                   ; The result is returned in two parts.  Y-reg holds the integer part, A-reg
                   ; holds the fractional part.
                   ; 
                   ; On exit:
                   ;   Y-reg: clamped reading [-4,4]
                   ;   A-reg: low bit of result, in high bit (00/80)
                   ; 
324f: c9 08        ClampTrackball  cmp     #$08              ;< 8?
3251: 90 0c                        bcc     :InRange          ;yes, branch
3253: c9 f8                        cmp     #$f8              ;>= -8?
3255: b0 08                        bcs     :InRange          ;yes, branch
3257: c9 80                        cmp     #$80              ;test for negative
3259: a9 08                        lda     #$08              ;clamp to 8
325b: 90 02                        bcc     :InRange          ;if positive, branch
325d: a9 f8                        lda     #$f8              ;was negative, clamp to -8
325f: c9 80        :InRange        cmp     #$80              ;set carry if negative
3261: 6a                           ror     A                 ;divide by 2, maintaining sign; LSB in carry
3262: a8                           tay                       ;copy that to Y-reg
3263: a9 00                        lda     #$00
3265: 6a                           ror     A                 ;roll low bit of result into high bit of A-reg
3266: 60                           rts

                   ; 
                   ; UPDATE: updates high score table after game ends.
                   ; 
                   ; Highest score appears first in the table.  Top 3 scores are copied to EAROM.
                   ; 
                   • Clear variables

3267: a9 ff        UpdateHS        lda     #$ff
3269: a2 00                        ldx     #$00              ;rev4: only need to check player 1
326b: 85 c1                        sta     plyr_hs_init_slot ;clear "need initials" flags
326d: 85 c2                        sta     plyr_hs_init_slot+1
326f: f8                           sed                       ;enable decimal mode
                   ; Update total play time, held in EAROM.
3270: a5 fb                        lda     game_time         ;play time of last game (both players)
3272: 18                           clc
3273: 6d 8e 01                     adc     ea_time           ;add to total time
3276: 8d 8e 01                     sta     ea_time
3279: a5 fc                        lda     game_time+1
327b: 6d 8f 01                     adc     ea_time+1
327e: 8d 8f 01                     sta     ea_time+1
3281: ad 90 01                     lda     ea_time+2
3284: 69 00                        adc     #$00
3286: 8d 90 01                     sta     ea_time+2
3289: ad 91 01                     lda     ea_time+3
328c: 69 00                        adc     #$00
328e: b0 1c                        bcs     :NoRoll           ;don't allow it to roll over
3290: 8d 91 01                     sta     ea_time+3
                   ; Update total number of games played, held in EAROM.
3293: ad 8b 01                     lda     ea_games          ;get total games played
3296: 18                           clc
3297: 65 89                        adc     num_players       ;2-player games count as 2
3299: 8d 8b 01                     sta     ea_games
329c: ad 8c 01                     lda     ea_games+1
329f: 69 00                        adc     #$00
32a1: 8d 8c 01                     sta     ea_games+1
32a4: ad 8d 01                     lda     ea_games+2
32a7: 69 00                        adc     #$00
32a9: 8d 8d 01                     sta     ea_games+2
32ac: d8           :NoRoll         cld                       ;disable decimal mode
                   ; 
32ad: a0 00        :FindLower      ldy     #$00              ;start at start of table (highest score)
32af: b9 02 00     :ChkLoop        lda     hs_scores,y       ;get low byte of score
32b2: d5 a8                        cmp     plyr_score0,x     ;compare to player's score (sets carry)
32b4: b9 03 00                     lda     hs_scores+1,y     ;subtract middle byte
32b7: f5 aa                        sbc     plyr_score1,x
32b9: b9 04 00                     lda     hs_scores+2,y     ;subtract high byte
32bc: f5 ac                        sbc     plyr_score2,x
32be: 90 2e                        bcc     :MakeRoom         ;new score is higher; branch
32c0: c8                           iny                       ;move on to next slot
32c1: c8                           iny
32c2: c8                           iny
32c3: c0 18                        cpy     #24               ;checked all 8 scores?
32c5: 90 e8                        bcc     :ChkLoop          ;not yet, loop
                   ; 
32c7: ca           :PlayerLoop     dex                       ;move to next player
32c8: 10 e3                        bpl     :FindLower        ;if we haven't done all players, loop
32ca: a5 c2                        lda     plyr_hs_init_slot+1 ;do we need a score for player 2?
32cc: 30 0e                        bmi     :InitInitial      ;if no, branch
32ce: c5 c1                        cmp     plyr_hs_init_slot ;did player 1 find a higher spot?
32d0: 90 0a                        bcc     :InitInitial      ;plyr 2 slot lower, so p2 score is higher; branch
32d2: 69 02                        adc     #$02              ;p1 score higher... add 3 to p2 slot index
32d4: c9 18                        cmp     #24               ;did we hit the end of the table?
32d6: 90 02                        bcc     :SetP2Slot        ;no, so get the initials
32d8: a9 ff                        lda     #$ff              ;yes, p1 knocked p2 out of the top-eight list
32da: 85 c2        :SetP2Slot      sta     plyr_hs_init_slot+1 ;set the "get initials here" value
                   ; 
32dc: a9 00        :InitInitial    lda     #$00
32de: 85 c0                        sta     initial_entry_num ;start with first initial
32e0: a5 c2                        lda     plyr_hs_init_slot+1 ;check both initials-needed flags ($ff if not)
32e2: 25 c1                        and     plyr_hs_init_slot
32e4: 10 07                        bpl     :Return           ;if at least one is non-$ff, branch
                   ; Don't need to enter initials.  Just show scores.
32e6: a9 00                        lda     #$00
32e8: 85 01                        sta     frame_ctr+1       ;don't reset the screen for a while
32ea: 20 60 2d                     jsr     ShowScores        ;show high score table
32ed: 60           :Return         rts

                   ; We need initials.  Shift the existing scores to make room.
32ee: 86 8d        :MakeRoom       stx     temp2             ;copy player number (0/1) to ZP
32f0: 84 8e                        sty     temp2+1           ;save high score slot index
32f2: 94 c1                        sty     plyr_hs_init_slot,x ;set it for the initials-entry code
32f4: a2 17                        ldx     #23               ;start at the bottom
32f6: b5 17        :ShiftLoop      lda     hs_initials-3,x   ;copy initials down
32f8: 95 1a                        sta     hs_initials,x
32fa: bd ff ff                     lda     hs_scores-3,x     ;copy scores down
32fd: 95 02                        sta     hs_scores,x
32ff: ca                           dex                       ;advance index
3300: e4 8e                        cpx     temp2+1           ;have we reached the slot we want to fill?
3302: d0 f2                        bne     :ShiftLoop        ;not yet, loop
                   ; 
3304: a9 01                        lda     #$01
3306: 95 1a                        sta     hs_initials,x     ;set first initial to 'A'
3308: a9 00                        lda     #$00
330a: 95 1b                        sta     hs_initials+1,x   ;set second and third to ' '
330c: 95 1c                        sta     hs_initials+2,x
330e: 85 b9                        sta     tball_read_horz   ;clear trackball axis
                   ; 
3310: a6 8d                        ldx     temp2             ;get player number (0/1)
3312: b5 ac                        lda     plyr_score2,x     ;copy player's score to newly-opened slot
3314: 99 04 00                     sta     hs_scores+2,y
3317: b5 aa                        lda     plyr_score1,x
3319: 99 03 00                     sta     hs_scores+1,y
331c: b5 a8                        lda     plyr_score0,x
331e: 99 02 00                     sta     hs_scores,y
3321: a9 f0                        lda     #240              ;set timeout to ~60 sec
3323: 85 01                        sta     frame_ctr+1
3325: d0 a0                        bne     :PlayerLoop       ;(always)

                   ; 
                   ; UPSCRE: draws scores at top of screen.
                   ; 
3327: a9 1f        DrawScores      lda     #$1f              ;row 31 (top of screen)
3329: 45 f5                        eor     ckp2_bf           ;if flipped, #$a0 (row 0, low 3 bits of col -> 011)
332b: 85 91                        sta     out_ptr           ;set low byte of output pointer
332d: a9 04                        lda     #$04              ;column 0
332f: 45 f7                        eor     ckp2_03           ;if flipped, set high 2 bits of column (col 31)
3331: 85 92                        sta     out_ptr+1         ;set high byte of pointer (now $041f or $07a0)
                   ; 
3333: a5 ac                        lda     plyr_score2       ;get high byte of player 1's BCD score
3335: 38                           sec                       ;suppress leading '0'
3336: 20 9e 38                     jsr     Draw2Digits       ;draw both digits
3339: a5 aa                        lda     plyr_score1       ;get middle byte
333b: 20 9e 38                     jsr     Draw2Digits       ;draw it
333e: a5 a8                        lda     plyr_score0       ;get low byte
3340: 18                           clc                       ;disable zero suppression
3341: 20 9e 38                     jsr     Draw2Digits       ;draw it
                   ; In rev3, the code updated the output pointer and drew player 2's score.  In
                   ; rev4, we need to draw the countdown timer.
3344: ad 01 08                     lda     DSW_N8            ;get N8 switches
3347: 29 1c                        and     #%00011100        ;mask to get the play timer setting
3349: f0 1d                        beq     :DrawHighScore    ;no timer, branch
334b: a9 07                        lda     #$07              ;set playfield pointer
334d: 45 f7                        eor     ckp2_03           ; adjusting for flip (which we know it can't be?)
334f: 85 92                        sta     out_ptr+1
3351: a9 1f                        lda     #$1f
3353: 45 f5                        eor     ckp2_bf
3355: 85 91                        sta     out_ptr
3357: a5 ad                        lda     plyr_score2+1     ;get minutes left from player 2's score storage
3359: 38                           sec                       ;suppress leading zeroes
335a: 20 9e 38                     jsr     Draw2Digits       ;draw minutes
335d: a9 2e                        lda     #$2e              ;':'
335f: 20 85 38                     jsr     DrawChar          ;draw colon
3362: a5 ab                        lda     plyr_score1+1     ;get seconds
3364: 18                           clc                       ;disable zero suppression
3365: 20 9e 38                     jsr     Draw2Digits       ;draw seconds
                   ; Now draw the current high score.
3368: a9 9f        :DrawHighScore  lda     #$9f              ;set playfield pointer
336a: 45 f5                        eor     ckp2_bf
336c: 85 91                        sta     out_ptr
336e: a9 05                        lda     #$05
3370: 45 f7                        eor     ckp2_03
3372: 85 92                        sta     out_ptr+1
3374: a5 04                        lda     hs_scores+2       ;get high byte of high score
3376: 38                           sec                       ;suppress leading zeroes
3377: 20 9e 38                     jsr     Draw2Digits       ;draw high byte
337a: a5 03                        lda     hs_scores+1
337c: 20 9e 38                     jsr     Draw2Digits       ;draw middle byte
337f: a5 02                        lda     hs_scores
3381: 18                           clc                       ;disable zero suppression
3382: 4c 9e 38                     jmp     Draw2Digits       ;draw low byte

3385: ff ff ff ff+                 .junk   39                ;empty space, not needed in rev4
33ac: f5                           .dd1    $f5               ;(checksum byte)

                   ; 
                   ; MOOLAH: handles coins and credits.  Executed during IRQ.
                   ; 
                   ; "Universal" coin routine, common to many Atari games.  The source code has
                   ; lots of conditionals to allow customization.
                   ; 
                   ; Short version: a valid coin is defined as between 16 and 800ms of "coin
                   ; present", preceded and followed by 33ms of "coin absent".  Counters are used
                   ; cleverly to keep track of state and elapsed time.
                   ; 
33ad: a2 02        CoinsAndCredits ldx     #$02              ;start with right coin slot
33af: ad 01 0c     :CoinLoop       lda     IN1               ;get IN1, which has the coin switch inputs
33b2: e0 01                        cpx     #$01              ; in the high 3 bits (R/C/L)
33b4: f0 03                        beq     :Shift2           ;X-reg=1, branch to shift 2x (center)
33b6: b0 02                        bcs     :Shift1           ;X-reg=2, branch to shift 1x (right)
33b8: 0a                           asl     A                 ;X-reg=0, shift 3x (left)
33b9: 0a           :Shift2         asl     A
33ba: 0a           :Shift1         asl     A
33bb: b5 cf                        lda     coin_timer1,x     ;get coin timer
33bd: 29 1f                        and     #%00011111        ;mod 32
33bf: b0 37                        bcs     :SlotBitSet       ;if coin slot bit was set, branch
33c1: f0 10                        beq     :SetValue1        ;if we're at zero, branch
33c3: c9 1b                        cmp     #27               ;>= 27? ("in first five samples?")
33c5: b0 0a                        bcs     :Ge27             ;yes, branch
33c7: a8                           tay                       ;preserve A-reg
33c8: a5 d4                        lda     irq_counter       ;get the IRQ counter
33ca: 29 07                        and     #$07
33cc: c9 07                        cmp     #$07              ;set carry every 8 IRQs
33ce: 98                           tya                       ;restore A-reg
33cf: 90 02                        bcc     :SetValue1        ;branch most of the time
33d1: e9 01        :Ge27           sbc     #$01              ;subtract 1/8th of the time
33d3: 95 cf        :SetValue1      sta     coin_timer1,x     ;save value
                   ; 
33d5: ad 01 0c                     lda     IN1               ;get IN1 again
33d8: 29 10                        and     #%00010000        ;slam switch engaged?
33da: d0 04                        bne     :NoSlam           ;no, branch
33dc: a9 f0                        lda     #$f0              ;yes, request an annoying noise
33de: 85 d2                        sta     slam_tone_ctr1
33e0: a5 d2        :NoSlam         lda     slam_tone_ctr1    ;are we in tilt-whine mode?
33e2: f0 08                        beq     :NoWhine          ;no, branch
33e4: c6 d2                        dec     slam_tone_ctr1    ;count it down
33e6: a9 00                        lda     #$00
33e8: 95 cf                        sta     coin_timer1,x     ;reset the coin stuff so we ignore anything that
33ea: 95 cc                        sta     coin_timer2,x     ; was dropping
33ec: 18           :NoWhine        clc                       ;clear carry, so branch to BCS always falls through
33ed: b5 cc                        lda     coin_timer2,x     ;has value reached zero?
33ef: f0 23                        beq     :MaybeNext        ;yes, move on to next slot
33f1: d6 cc                        dec     coin_timer2,x     ;decrement
33f3: d0 1f                        bne     :MaybeNext        ;not yet zero, move on to next slot
33f5: 38                           sec                       ;set carry for next part
33f6: b0 1c                        bcs     :MaybeNext        ;(always)

33f8: c9 1b        :SlotBitSet     cmp     #27               ;fiddle with the timers
33fa: b0 09                        bcs     :Ge27
33fc: b5 cf                        lda     coin_timer1,x
33fe: 69 20                        adc     #32
3400: 90 d1                        bcc     :SetValue1
3402: f0 01                        beq     :Ge27
3404: 18                           clc
3405: a9 1f        :Ge27           lda     #31
3407: b0 ca                        bcs     :SetValue1
3409: 95 cf                        sta     coin_timer1,x
340b: b5 cc                        lda     coin_timer2,x
340d: f0 01                        beq     :T20
340f: 38                           sec
3410: a9 78        :T20            lda     #120
3412: 95 cc                        sta     coin_timer2,x
3414: 90 2a        :MaybeNext      bcc     :Next
                   ; 
3416: a9 00                        lda     #$00              ;default slot config value
3418: e0 01                        cpx     #$01              ;left slot?
341a: 90 16                        bcc     :AddCred          ;yes, no config for left; branch
341c: f0 0c                        beq     :Center           ;branch if center slot
341e: a5 d3                        lda     n8_nomult         ;N8 switches; rev4 masks off the coin multipliers
3420: 29 0c                        and     #%00001100        ;get right coin multiplier
3422: 4a                           lsr     A                 ;shift right to get 0-3
3423: 4a                           lsr     A                 ;also clears carry flag
3424: f0 0c                        beq     :AddCred          ;if zero, branch
3426: 69 02                        adc     #$02              ;change 1-3 to 3-5
3428: d0 08                        bne     :AddCred          ;(always)

342a: a5 d3        :Center         lda     n8_nomult         ;get masked N8 switches
342c: 29 10                        and     #%00010000        ;get left coin multiplier
342e: f0 02                        beq     :AddCred          ;not set, branch
3430: a9 01                        lda     #$01              ;set to 1
3432: 38           :AddCred        sec                       ;add 1
3433: 48                           pha                       ;preserve value
3434: 65 ca                        adc     bonus_coin_state  ;update coins for bonus check
3436: 85 ca                        sta     bonus_coin_state
3438: 68                           pla                       ;restore value
3439: 38                           sec                       ;add 1
343a: 65 c9                        adc     coins_inserted    ;increase coins inserted
343c: 85 c9                        sta     coins_inserted
343e: f6 c5                        inc     coin_drop_ctr,x   ;incr count for this slot
3440: ca           :Next           dex                       ;move to next coin slot
3441: 30 03                        bmi     :ChkBonus         ;all done, branch
3443: 4c af 33                     jmp     :CoinLoop         ;not done, loop

3446: a5 d3        :ChkBonus       lda     n8_nomult         ;get N8 switches
3448: 4a                           lsr     A                 ;shift bonus coin setting into low bits
3449: 4a                           lsr     A
344a: 4a                           lsr     A
344b: 4a                           lsr     A
344c: 4a                           lsr     A
344d: a8                           tay                       ;use as index
344e: a5 ca                        lda     bonus_coin_state  ;get coins inserted
3450: 38                           sec
3451: f9 62 34                     sbc     :modlo,y          ;subtract num required
3454: 30 14                        bmi     :ChkPerCred       ;not enough, branch
3456: 85 ca                        sta     bonus_coin_state  ;save updated value
3458: e6 cb                        inc     bonus_coin_ctr    ;add bonus coin
345a: c0 03                        cpy     #$03              ;4 coins = +2 coins?
345c: d0 0c                        bne     :ChkPerCred       ;no, branch
345e: e6 cb                        inc     bonus_coin_ctr    ;yes, add the second coin
3460: d0 08                        bne     :ChkPerCred       ;(always)

                   ; Map N8 switch values to number of coins required to trigger bonus:
                   ;   000 = no bonus coins
                   ;   001 = +1 coin for every 2
                   ;   010 = +1 coin for every 4
                   ;   011 = +2 coins for every 4
                   ;   100 = +1 coin for every 5
                   ;   101 = +1 coin for every 3
                   ;   110 = unused
                   ;   111 = unused
3462: 7f 02 04 04+ :modlo          .bulk   $7f,$02,$04,$04,$05,$03,$7f,$7f

346a: a5 d3        :ChkPerCred     lda     n8_nomult         ;get N8 switches
346c: 29 03                        and     #%00000011        ;get coins-per-credit setting
346e: a8                           tay                       ;save for later
346f: f0 1a                        beq     :UpdateCoins      ;if free play, branch
                   ; The modified coins-per-credit setting is:
                   ;    0 - free play (handled above)
                   ;    1 - 1 coin 2 credits
                   ;    2 - 1 coin 1 credit
                   ;    3 - 2 coins 1 credit
                   ; By shifting right and adding the carry back in, we get 1 (1 coin 2 credits), 1
                   ; (1 coin 1 credit), or 2 (2 coins 1 credit), which is the number of coins
                   ; needed to get at least one credit.  We subtract that from the number of coins
                   ; inserted by negating and adding it.
3471: 4a                           lsr     A                 ;shift low bit into carry
3472: 69 00                        adc     #$00              ;add carry back if set
3474: 49 ff                        eor     #$ff              ;negate value by inverting bits
3476: 38                           sec                       ; and adding one
3477: 65 c9                        adc     coins_inserted    ;add negated value to coins inserted
3479: b0 08                        bcs     :AddCredits       ;enough coins, branch
347b: 65 cb                        adc     bonus_coin_ctr    ;add bonus coins
347d: 30 0e                        bmi     :UpdateCounters   ;still not enough, branch
347f: 85 cb                        sta     bonus_coin_ctr    ;track the coin in the bonus count
3481: a9 00                        lda     #$00              ;clear coins inserted
3483: c0 02        :AddCredits     cpy     #$02              ;1 coin 1 credit or 2 coins 1 credit?
3485: b0 02                        bcs     :OneCredit        ;yes, branch
3487: e6 c8                        inc     credit_count      ;no, 1 coin for 2 credits; add another credit
3489: e6 c8        :OneCredit      inc     credit_count
348b: 85 c9        :UpdateCoins    sta     coins_inserted
                   ; 
348d: e6 d4        :UpdateCounters inc     irq_counter       ;advance the IRQ count
348f: a5 d4                        lda     irq_counter       ;get value
3491: 4a                           lsr     A                 ;shift low bit into carry
3492: b0 27                        bcs     :Return           ;do this every-other time
                   ; Update the counter values.
3494: a0 00                        ldy     #$00              ;init mod count to zero
3496: a2 02                        ldx     #$02              ;walk through all 3 coin slots
3498: b5 c5        :CtrLoop1       lda     coin_drop_ctr,x   ;get count
349a: f0 09                        beq     :NextCtr1         ;if zero, branch
349c: c9 10                        cmp     #16               ;is it < 16?
349e: 90 05                        bcc     :NextCtr1         ;yes, branch
34a0: 69 ef                        adc     #$ef              ;add -16 (note carry is set)
34a2: c8                           iny                       ;increment mod count
34a3: 95 c5                        sta     coin_drop_ctr,x   ;store updated value
34a5: ca           :NextCtr1       dex                       ;done with coin slots?
34a6: 10 f0                        bpl     :CtrLoop1         ;no, branch
34a8: 98                           tya                       ;did we update a counter?
34a9: d0 10                        bne     :Return           ;yes, bail
                   ; 
34ab: a2 02                        ldx     #$02              ;walk through all 3 coin slots
34ad: b5 c5        :CtrLoop2       lda     coin_drop_ctr,x   ;get count
34af: f0 07                        beq     :NextCtr2         ;if zero, branch
34b1: 18                           clc
34b2: 69 ef                        adc     #$ef              ;add -17
34b4: 95 c5                        sta     coin_drop_ctr,x   ;store updated value
34b6: 30 03                        bmi     :Return           ;if it went negative, bail
34b8: ca           :NextCtr2       dex                       ;done with coin slots?
34b9: 10 f2                        bpl     :CtrLoop2         ;no, branch
34bb: 60           :Return         rts

                   ; 
                   ; Pointers to messages.
                   ; 
34bc: 14 35        message_ptrs    .dd2    mess0
34be: 1f 35                        .dd2    mess0g
34c0: 2b 35                        .dd2    mess0f
34c2: 36 35                        .dd2    mess0s
34c4: 42 35                        .dd2    mess1
34c6: 54 35                        .dd2    mess1g
34c8: 69 35                        .dd2    mess1f
34ca: 7e 35                        .dd2    mess1s
34cc: 92 35                        .dd2    mess2
34ce: a3 35                        .dd2    mess2g
34d0: b7 35                        .dd2    mess2f
34d2: cb 35                        .dd2    mess2s
34d4: de 35                        .dd2    mess3
34d6: f0 35                        .dd2    mess3g
34d8: 05 36                        .dd2    mess3f
34da: 1a 36                        .dd2    mess3s
34dc: 2e 36                        .dd2    mess4
34de: 3b 36                        .dd2    mess4g
34e0: 48 36                        .dd2    mess4f
34e2: 59 36                        .dd2    mess4s
34e4: 6c 36                        .dd2    mess5
34e6: 83 36                        .dd2    mess5g
34e8: a3 36                        .dd2    mess5f
34ea: bb 36                        .dd2    mess5s
34ec: d2 36                        .dd2    mess6
34ee: e2 36                        .dd2    mess6g
34f0: f1 36                        .dd2    mess6f
34f2: 02 37                        .dd2    mess6s
34f4: 11 37                        .dd2    mess7
34f6: 20 37                        .dd2    mess7g
34f8: 35 37                        .dd2    mess7f
34fa: 48 37                        .dd2    mess7s
34fc: 53 37                        .dd2    mess8
34fe: 62 37                        .dd2    mess8g
3500: 7b 37                        .dd2    mess8f
3502: 8e 37                        .dd2    mess8s
3504: 9e 37                        .dd2    mess9
3506: aa 37                        .dd2    mess9g
3508: b6 37                        .dd2    mess9f
350a: c2 37                        .dd2    mess9s
350c: cf 37                        .dd2    mes10
350e: e3 37                        .dd2    mes10g
3510: ff 37                        .dd2    mes10f
3512: 11 38                        .dd2    mes10s
                   ; 
                   ; Messages, in four languages.
                   ; 
                   ; Each message is preceded by a pointer into playfield RAM where the first
                   ; character should be placed.  The first pointer is for normal mode, the second
                   ; is for upside-down cocktail mode.
                   ; 
3514: 6e 05        mess0           .dd2    $056e
3516: 51 06                        .dd2    $0651
3518: 50 4c 41 59+                 .dstr   ‘PLAYER ’
351f: 6e 05        mess0g          .dd2    $056e
3521: 51 06                        .dd2    $0651
3523: 53 50 49 45+                 .dstr   ‘SPIELER ’
352b: 6e 05        mess0f          .dd2    $056e
352d: 51 06                        .dd2    $0651
352f: 4a 4f 55 45+                 .dstr   ‘JOUEUR ’
3536: 6e 05        mess0s          .dd2    $056e
3538: 51 06                        .dd2    $0651
353a: 4a 55 47 41+                 .dstr   ‘JUGADOR ’
3542: 13 05        mess1           .dd2    $0513
3544: ac 06                        .dd2    $06ac
3546: 31 20 43 4f+                 .dstr   ‘1 COIN 2 PLAYS’
3554: f3 04        mess1g          .dd2    $04f3
3556: cc 06                        .dd2    $06cc
3558: 31 20 4d 55+                 .dstr   ‘1 MUENZE 2 SPIELE’
3569: f3 04        mess1f          .dd2    $04f3
356b: cc 06                        .dd2    $06cc
356d: 31 20 50 49+                 .dstr   ‘1 PIECE 2 JOUEURS’
357e: f3 04        mess1s          .dd2    $04f3
3580: cc 06                        .dd2    $06cc
3582: 31 20 46 49+                 .dstr   ‘1 FICHA 2 JUEGOS’
3592: 13 05        mess2           .dd2    $0513
3594: ac 06                        .dd2    $06ac
3596: 31 20 43 4f+                 .dstr   ‘1 COIN 1 PLAY’
35a3: f3 04        mess2g          .dd2    $04f3
35a5: cc 06                        .dd2    $06cc
35a7: 31 20 4d 55+                 .dstr   ‘1 MUENZE 1 SPIEL’
35b7: f3 04        mess2f          .dd2    $04f3
35b9: cc 06                        .dd2    $06cc
35bb: 31 20 50 49+                 .dstr   ‘1 PIECE 1 JOUEUR’
35cb: f3 04        mess2s          .dd2    $04f3
35cd: cc 06                        .dd2    $06cc
35cf: 31 20 46 49+                 .dstr   ‘1 FICHA 1 JUEGO’
35de: 13 05        mess3           .dd2    $0513
35e0: ac 06                        .dd2    $06ac
35e2: 32 20 43 4f+                 .dstr   ‘2 COINS 1 PLAY’
35f0: f3 04        mess3g          .dd2    $04f3
35f2: cc 06                        .dd2    $06cc
35f4: 32 20 4d 55+                 .dstr   ‘2 MUENZEN 1 SPIEL’
3605: f3 04        mess3f          .dd2    $04f3
3607: cc 06                        .dd2    $06cc
3609: 32 20 50 49+                 .dstr   ‘2 PIECES 1 JOUEUR’
361a: f3 04        mess3s          .dd2    $04f3
361c: cc 06                        .dd2    $06cc
361e: 32 20 46 49+                 .dstr   ‘2 FICHAS 1 JUEGO’
362e: 6f 05        mess4           .dd2    $056f
3630: 50 06                        .dd2    $0650
3632: 47 41 4d 45+                 .dstr   ‘GAME OVER’
363b: 6f 05        mess4g          .dd2    $056f
363d: 50 06                        .dd2    $0650
363f: 53 50 49 45+                 .dstr   ‘SPIELENDE’
3648: 0f 05        mess4f          .dd2    $050f
364a: b0 06                        .dd2    $06b0
364c: 46 49 4e 20+                 .dstr   ‘FIN DE PARTIE’
3659: ef 04        mess4s          .dd2    $04ef
365b: d0 06                        .dd2    $06d0
365d: 4a 55 45 47+                 .dstr   ‘JUEGO TERMINADO’
366c: ab 04        mess5           .dd2    $04ab
366e: 14 07                        .dd2    $0714
3670: 45 4e 54 45+                 .dstr   ‘ENTER YOUR INITIALS’
3683: 2b 04        mess5g          .dd2    $042b
3685: 94 07                        .dd2    $0794
3687: 47 45 42 45+                 .dstr   ‘GEBEN SIE IHRE INITIALEN EIN’
36a3: 8b 04        mess5f          .dd2    $048b
36a5: 34 07                        .dd2    $0734
36a7: 45 4e 54 52+                 .dstr   ‘ENTREZ VOS INITIALES’
36bb: 8b 04        mess5s          .dd2    $048b
36bd: 34 07                        .dd2    $0734
36bf: 45 4e 54 52+                 .dstr   ‘ENTRE SUS INICIALES’
36d2: f1 04        mess6           .dd2    $04f1
36d4: ce 06                        .dd2    $06ce
36d6: 42 4f 4e 55+                 .dstr   ‘BONUS EVERY ’
36e2: f1 04        mess6g          .dd2    $04f1
36e4: ce 06                        .dd2    $06ce
36e6: 42 4f 4e 55+                 .dstr   ‘BONUS JEDE ’
36f1: d1 04        mess6f          .dd2    $04d1
36f3: ee 06                        .dd2    $06ee
36f5: 42 4f 4e 55+                 .dstr   ‘BONUS CHAQUE ’
3702: f1 04        mess6s          .dd2    $04f1
3704: ce 06                        .dd2    $06ce
3706: 45 58 54 52+                 .dstr   ‘EXTRA CADA ’
3711: 5d 05        mess7           .dd2    $055d
3713: 62 06                        .dd2    $0662
3715: 48 49 47 48+                 .dstr   ‘HIGH SCORES’
3720: dd 04        mess7g          .dd2    $04dd
3722: e2 06                        .dd2    $06e2
3724: 48 4f 45 43+                 .dstr   ‘HOECHSTERGEBNISSE’
3735: 1d 05        mess7f          .dd2    $051d
3737: a2 06                        .dd2    $06a2
3739: 4d 45 49 4c+                 .dstr   ‘MEILLEURS SCORE’
3748: 9d 05        mess7s          .dd2    $059d
374a: 22 06                        .dd2    $0622
374c: 52 45 43 4f+                 .dstr   ‘RECORDS’
3753: 2c 05        mess8           .dd2    $052c
3755: 93 06                        .dd2    $0693
3757: 47 52 45 41+                 .dstr   ‘GREAT SCORE’
3762: 8c 04        mess8g          .dd2    $048c
3764: 33 07                        .dd2    $0733
3766: 47 52 4f 53+                 .dstr   ‘GROSSARTIGES ERGEBNIS’
377b: ec 04        mess8f          .dd2    $04ec
377d: d3 06                        .dd2    $06d3
377f: 53 50 4c 45+                 .dstr   ‘SPLENDIDE SCORE’
378e: 0c 05        mess8s          .dd2    $050c
3790: b3 06                        .dd2    $06b3
3792: 47 52 41 4e+                 .dstr   ‘GRAN PUNTAJE’
379e: 52 05        mess9           .dd2    $0552
37a0: 6d 06                        .dd2    $066d
37a2: 43 52 45 44+                 .dstr   ‘CREDITS ’
37aa: 52 05        mess9g          .dd2    $0552
37ac: 6d 06                        .dd2    $066d
37ae: 4b 52 45 44+                 .dstr   ‘KREDITE ’
37b6: 52 05        mess9f          .dd2    $0552
37b8: 6d 06                        .dd2    $066d
37ba: 43 52 45 44+                 .dstr   ‘CREDITS ’
37c2: 52 05        mess9s          .dd2    $0552
37c4: 6d 06                        .dd2    $066d
37c6: 43 52 45 44+                 .dstr   ‘CREDITOS ’
37cf: f0 04        mes10           .dd2    $04f0
37d1: cf 06                        .dd2    $06cf
37d3: 32 20 43 52+                 .dstr   ‘2 CREDIT MINIMUM’
37e3: 30 04        mes10g          .dd2    $0430
37e5: 8f 07                        .dd2    $078f
37e7: 47 45 4c 44+                 .dstr   ‘GELDEINWURF FUR 2 SPIELE’
37ff: f0 04        mes10f          .dd2    $04f0
3801: cf 06                        .dd2    $06cf
3803: 32 20 4a 55+                 .dstr   ‘2 JUEX MINIMUM’
3811: f0 04        mes10s          .dd2    $04f0
3813: cf 06                        .dd2    $06cf
3815: 32 20 4a 55+                 .dstr   ‘2 JUEGAS MINIM0’

                   ; 
                   ; MESS: draws or erases message on screen.
                   ; 
                   ; On entry:
                   ;   A-reg: message number; high bit set to erase instead
                   ; 
                   ; On exit:
                   ;   $91-92: address of next playfield cell
                   ; 
                   • Clear variables
                   ]erase_flag     .var    $8c    {addr/1}
                   ]msg_ptr        .var    $93    {addr/2}

3824: 0a           DrawMessage     asl     A                 ;double it
3825: 66 8c                        ror     ]erase_flag       ;save high bit as "erase" flag
3827: a8                           tay                       ;(?)
3828: 0a                           asl     A                 ;double again, so low two bits are free (4 languages)
3829: 85 8b                        sta     temp1             ;stash
382b: a5 fd                        lda     dsw_n9_copy       ;get N9 switches
382d: 29 03                        and     #%00000011        ;mask off language bits (00=en, ...)
382f: 05 8b                        ora     temp1             ;combine with message number
3831: 0a                           asl     A                 ;double again (pointers are two bytes)
3832: aa                           tax                       ;use as byte offset
3833: bd bc 34                     lda     message_ptrs,x    ;get pointer to message entry
3836: 85 93                        sta     ]msg_ptr          ;copy to ZP
3838: bd bd 34                     lda     message_ptrs+1,x
383b: 85 94                        sta     ]msg_ptr+1
                   ; 
383d: a0 00                        ldy     #$00              ;use non-flipped address
383f: a6 ef                        ldx     ckp2_c0           ;are we flipped?
3841: f0 02                        beq     :NotFlip          ;no, branch
3843: a0 02                        ldy     #$02              ;yes, use flipped address
3845: b1 93        :NotFlip        lda     (]msg_ptr),y      ;get low byte of output address
3847: 85 91                        sta     out_ptr           ;copy to ZP
3849: c8                           iny
384a: b1 93                        lda     (]msg_ptr),y      ;get high byte of output address
384c: 85 92                        sta     out_ptr+1
384e: a0 04                        ldy     #$04              ;message text starts at offset 4
                   ; 
3850: 84 8b        DoDrawText      sty     temp1             ;save char index
3852: a4 8b        :DrawLoop       ldy     temp1             ;get char index
3854: b1 93                        lda     (]msg_ptr),y      ;get ASCII char
3856: 29 3f                        and     #%00111111        ;strip high bits, remapping $40-5f to $00-1f
3858: c9 20                        cmp     #$20              ;is it a blank space?
385a: f0 04                        beq     :DrawBlank        ;yes, branch to set it to zero
385c: a6 8c                        ldx     ]erase_flag       ;are we erasing?
385e: 10 02                        bpl     :NotErase         ;no, branch
3860: a9 00        :DrawBlank      lda     #$00              ;set tile to $00 (blank space)
3862: c9 30        :NotErase       cmp     #$30              ;is it a letter or symbol (e.g. $1b for circle-C)?
3864: 90 02                        bcc     :NotNum           ;yes, leave the value alone
3866: 29 2f                        and     #%00101111        ;no, number or ':'; remap $30-3f to $20-2f
                   ; 
3868: 20 85 38     :NotNum         jsr     DrawChar          ;write the value to the playfield
386b: a4 8b                        ldy     temp1             ;get char index
386d: e6 8b                        inc     temp1             ;advance ZP value
386f: b1 93                        lda     (]msg_ptr),y      ;did last thing we drew have high bit set?
3871: 10 df                        bpl     :DrawLoop         ;no, loop
3873: 60                           rts

                   ; 
                   ; MESSAG: draw arbitrary text at arbitrary position.
                   ; 
                   ; Used for the copyright notice and self-tests.
                   ; 
                   ; On entry:
                   ;   $91-92: pointer to playfield destination
                   ;   $93-94: pointer to text
                   ; 
3874: a0 00        DrawText        ldy     #$00              ;text starts at offset zero
3876: 84 8c                        sty     ]erase_flag       ;clear erase flag
3878: f0 d6                        beq     DoDrawText        ;(always)

                   ; 
                   ; ABS: computes absolute value.
                   ; 
                   ; On entry:
                   ;   A-reg: value
                   ;   N-flag set if value is negative
                   ; 
                   ; On exit:
                   ;   A-reg: result
                   ;   (X/Y-reg preserved)
                   ; 
387a: 10 05        CalcAbsVal      bpl     :Return           ;if value is positive, nothing to do
                   ; 
                   ; COMP: computes 2's complement, negating the value.
                   ; 
                   ; On entry:
                   ;   A-reg: value
                   ; 
                   ; On exit:
                   ;   A-reg: result
                   ; 
387c: 49 ff        Calc2sComp      eor     #$ff              ;invert
387e: 18                           clc
387f: 69 01                        adc     #$01              ;add one
3881: 60           :Return         rts

                   ; 
                   ; INITIAL: draws one of the player's initials.
                   ; 
                   ; On entry:
                   ;   Y-reg: index into table of initials
                   ;   (falls into next function)
                   ; 
3882: b9 1a 00     DrawInitial     lda     hs_initials,y     ;get letter
                   ; 
                   ; CHAR: draws one character on screen.
                   ; 
                   ; On entry:
                   ;   A-reg: playfield bitmap to draw ($00-3f)
                   ;   $91-92: screen position ($0400-07bf)
                   ; 
                   ; On exit:
                   ;   $91-92: advanced to next screen position
                   ; 
                   • Clear variables

3885: a8           DrawChar        tay                       ;is it zero?
3886: f0 02                        beq     :SkipFlags        ;yes, branch so blank space is always $00
3888: 45 ef                        eor     ckp2_c0           ;set flip flags if needed
388a: a0 00        :SkipFlags      ldy     #$00
388c: 91 91                        sta     (out_ptr),y       ;write value to screen
                   ; 
388e: a9 20                        lda     #$20              ;32 bytes per column
3890: 45 ef                        eor     ckp2_c0           ;$20 or $-20 depending on flip
3892: 18                           clc
3893: 65 91                        adc     out_ptr           ;advance to next column
3895: 85 91                        sta     out_ptr
3897: a5 f3                        lda     ckp2_ff           ;0 or -1 depending on flip
3899: 65 92                        adc     out_ptr+1         ;update high byte of pointer
389b: 85 92                        sta     out_ptr+1
389d: 60                           rts

                   ; 
                   ; DIGIT2: draws two digits.
                   ; 
                   ; This is primarily for drawing BCD score values, but also works for hex.  If
                   ; the carry flag is set, leading '0' digits will be suppressed.  If we encounter
                   ; a nonzero digit, the carry will be clear when we return.
                   ; 
                   ; On entry:
                   ;   A-reg: BCD digits
                   ;   $91-92: playfield output pointer
                   ;   C-flag: clear if no zero suppression
                   ; 
                   ; On exit:
                   ;   $91-92: advanced to next position
                   ;   C-flag: clear if no zero suppression
                   ; 
389e: 48           Draw2Digits     pha                       ;preserve A-reg
389f: 08                           php                       ;preserve C-flag
38a0: 4a                           lsr     A                 ;get high nibble
38a1: 4a                           lsr     A
38a2: 4a                           lsr     A
38a3: 4a                           lsr     A
38a4: 28                           plp                       ;restore C-flag
38a5: 20 ab 38                     jsr     Draw1Digit        ;draw the high nibble
38a8: 68                           pla                       ;restore BCD value
38a9: 29 0f                        and     #$0f              ;mask to get low nibble
                   ; 
                   ; DIGITZ: draws one digit.
                   ; 
38ab: 90 04        Draw1Digit      bcc     :NoZero           ;if no zero suppression, always draw
38ad: 29 0f                        and     #$0f              ;mask to set set Z-flag
38af: f0 03                        beq     :SkipAdd          ;suppress by leaving as $00 (blank); carry is set
38b1: 18           :NoZero         clc
38b2: 09 20                        ora     #$20              ;numbers are $20-29
38b4: 08           :SkipAdd        php                       ;preserve C-flag
38b5: c9 2a                        cmp     #$2a              ;is it a number?
38b7: 90 02                        bcc     :IsNumber         ;yes, branch
38b9: e9 29                        sbc     #$29              ;no, map $2a-2f to $01-06 ('A'-'F') for hex
38bb: 20 85 38     :IsNumber       jsr     DrawChar          ;draw it
38be: 28                           plp                       ;restore C-flag
38bf: 60                           rts

                   ; 
                   ; IRQ: handles ~60Hz interrupt.
                   ; 
38c0: 48           HandleIRQ       pha                       ;preserve A/X/Y-reg
38c1: 8a                           txa
38c2: 48                           pha
38c3: 98                           tya
38c4: 48                           pha
38c5: d8                           cld                       ;clear decimal mode
                   ; 
38c6: a5 d2                        lda     slam_tone_ctr1    ;recent slam?
38c8: f0 0a                        beq     :NoSlam           ;no, branch
38ca: a9 10                        lda     #$10              ;high-pitched whine
38cc: 8d 02 10                     sta     POKEY_AUDF2
38cf: a9 af                        lda     #$af              ;constant tone, max volume
38d1: 8d 03 10                     sta     POKEY_AUDC2
                   ; 
38d4: 2c 00 0c     :NoSlam         bit     IN0               ;are we in VBLANK?
38d7: 70 03                        bvs     :InVblank         ;yes, branch
38d9: 4c b4 39                     jmp     :NotVblank        ;no, skip lots of stuff

38dc: e6 8a        :InVblank       inc     irq_sync          ;allow the main program to execute
38de: e6 00                        inc     frame_ctr         ;update frame counter
38e0: d0 11                        bne     :NoIncFrame       ;didn't roll over, branch
38e2: e6 01                        inc     frame_ctr+1       ;increment high byte
38e4: f8                           sed                       ;switch to decimal mode
38e5: a5 fb                        lda     game_time         ;increment total game uptime every 256 frames
38e7: 18                           clc                       ;(10,000 units = 42667 sec = 11.85 hours)
38e8: 69 01                        adc     #$01
38ea: 85 fb                        sta     game_time
38ec: a5 fc                        lda     game_time+1       ;carry into high byte
38ee: 69 00                        adc     #$00
38f0: 85 fc                        sta     game_time+1
38f2: d8                           cld                       ;clear decimal mode
                   ; 
38f3: a5 8a        :NoIncFrame     lda     irq_sync          ;make sure main code is running
38f5: c9 08                        cmp     #$08              ;are we way ahead of main?
38f7: b0 fe        :Die            bcs     :Die              ;yes, spin until watchdog bites
                   ; 
38f9: a5 c8                        lda     credit_count      ;get number of credits
38fb: c9 25                        cmp     #37               ;is it >= 37?
38fd: b0 fe        :Die2           bcs     :Die2             ;yes, die
38ff: c9 13                        cmp     #19               ;is number of credits < 19?
3901: 90 04                        bcc     :ChkJoystick      ;yes, branch
3903: a9 12                        lda     #18               ;no, cap credits at 18
3905: 85 c8                        sta     credit_count
                   ; Read joystick input.
3907: ad 03 0c     :ChkJoystick    lda     IN3               ;get joystick input
390a: a6 88                        ldx     cur_player        ;get current player
390c: ac b8 01                     ldy     joy_last_horz     ;get last horizontal reading
390f: 20 31 3a                     jsr     HandleJoystick    ;handle right/left movement
3912: 8c b8 01                     sty     joy_last_horz     ;save new horizontal speed
3915: 48                           pha                       ;preserve A-reg
3916: 98                           tya
3917: 18                           clc
3918: 65 b9                        adc     tball_read_horz   ;update movement speed
391a: 85 b9                        sta     tball_read_horz
391c: 68                           pla                       ;restore A-reg
391d: ac b9 01                     ldy     joy_last_vert     ;get last vertical reading
3920: 20 31 3a                     jsr     HandleJoystick    ;handle up/down movement
3923: 8c b9 01                     sty     joy_last_vert     ;save new vertical speed
3926: 98                           tya
3927: 20 7c 38                     jsr     Calc2sComp        ;invert
392a: 18                           clc
392b: 65 bb                        adc     tball_read_vert   ;update movement speed
392d: 85 bb                        sta     tball_read_vert
                   ; Update color palette if needed.
392f: b5 c2                        lda     leg_color-1,x     ;get last color of legs
3931: 30 08                        bmi     :NextColor        ;time to change; branch
3933: c9 40                        cmp     #$40
3935: 90 15                        bcc     :CopyMobj         ;not time to change
3937: 29 3f                        and     #$3f              ;strip high bits
3939: 10 0b                        bpl     :ColorOk          ;(always)

393b: 29 3f        :NextColor      and     #$3f              ;strip high bits
393d: 18                           clc
393e: 69 03                        adc     #$03              ;add 3 to get next table entry
3940: c9 2a                        cmp     #42               ;reached the end of the color set?
3942: 90 02                        bcc     :ColorOk          ;no, branch
3944: a9 00                        lda     #$00              ;yes, roll back to zero
3946: 95 c2        :ColorOk        sta     leg_color-1,x
3948: aa                           tax
3949: 20 5a 26                     jsr     SetPalette
                   ; Copy motion object state to hardware.  We configure the horizontal flip flag
                   ; on the motion object based on the direction of movement.
394c: a2 0f        :CopyMobj       ldx     #15               ;16 objects
394e: b5 64        :CopyLoop       lda     mobj_vert,x       ;get vertical position
3950: 9d e0 07                     sta     MOBJ_VERT,x       ;set hardware
3953: b5 54                        lda     mobj_horz,x       ;get horizontal position
3955: a0 00                        ldy     #$00
3957: e0 0d                        cpx     #$0d              ;is this the spider or spider-score?
3959: f0 07                        beq     :Right            ;yes, branch
395b: b4 44                        ldy     mobj_hvel,x       ;get horizontal movement direction
395d: 10 03                        bpl     :Right            ;if moving / facing right, branch
395f: 18                           clc
3960: 69 01                        adc     #$01              ;maintain center by adding 1
3962: 9d d0 07     :Right          sta     MOBJ_HORZ,x       ;set hardware
3965: 98                           tya
3966: 29 80                        and     #$80              ;value will be nonzero if this is right-facing
3968: 85 99                        sta     irqtmp_hflip      ; non-spider
                   ; 
396a: ad 00 0c                     lda     IN0               ;get switch state
396d: 29 20                        and     #%00100000        ;are we in self-test mode?
396f: d0 05                        bne     :NotTest          ;no, branch
3971: b5 34                        lda     mobj_pict,x       ;get mobj picture
3973: 4c 9d 39                     jmp     :SetPict          ;branch to set

3976: b5 34        :NotTest        lda     mobj_pict,x       ;get mobj picture
3978: e0 0c                        cpx     #NCENT            ;is this a centipede segment?
397a: b0 1f                        bcs     :SetNonCent       ;no, branch
                   ; Convert centipede segment to vertical or diagonal if it's moving between rows.
                   ; (This is clever.)
397c: 29 3f                        and     #%00111111        ;strip high bits
397e: c9 30                        cmp     #$30              ;is the segment exploding?
3980: b0 19                        bcs     :SetNonCent       ;yes, branch
3982: 29 0f                        and     #%00001111        ;remove poisoned bit
3984: 85 98                        sta     irqtmp_pict       ;save value
3986: b5 64                        lda     mobj_vert,x       ;get vertical position
3988: 29 07                        and     #%00000111        ;is it row-aligned (not turning)?
398a: f0 0d                        beq     :MixPic           ;yes, branch (with A-reg=0)
398c: a8                           tay                       ;copy row fraction to Y-reg
398d: a9 08                        lda     #$08              ;diagonal segments ($08-0b)
398f: c0 06                        cpy     #$06              ;starting turn?
3991: b0 06                        bcs     :MixPic           ;yes, do diagonal
3993: c0 03                        cpy     #$03              ;finishing turn?
3995: 90 02                        bcc     :MixPic           ;yes, do diagonal
3997: a9 0c                        lda     #$0c              ;use vertical segment ($0c-0f)
3999: 45 98        :MixPic         eor     irqtmp_pict       ;exclusive-OR the picture number in
399b: 45 99        :SetNonCent     eor     irqtmp_hflip      ;add in the horizontal flip
399d: 9d c0 07     :SetPict        sta     MOBJ_PICT,x       ;set hardware
                   ; Set color modifier.  Only applies to non-head centipede segments.
39a0: b5 34                        lda     mobj_pict,x       ;get picture number
39a2: 29 40                        and     #%01000000        ;get head/body flag (overlaps with vert flip)
39a4: f0 06                        beq     :DefaultMap       ;branch if it's a head (with A-reg=0)
39a6: e0 0c                        cpx     #NCENT            ;is this a centipede segment?
39a8: b0 02                        bcs     :DefaultMap       ;no, branch (with A-reg=$40, bit is ignored)
                   ; The color modifier has the form %xx332211, remapping colors 1-3 for motion
                   ; objects.  The default is %xx111001, a direct map: 3=%11, 2=%10, 1=%01.  For
                   ; the body segments we want to map color 2 to color 3 so the eyes disappear, so
                   ; we set 2=%11.
39aa: a9 0c                        lda     #%00001100        ;centipede middle, remap 2->3 to conceal eyes
39ac: 09 39        :DefaultMap     ora     #%00111001        ;default 1/2/3 map
39ae: 9d f0 07                     sta     MOBJ_COLOR,x
39b1: ca                           dex
39b2: 10 9a                        bpl     :CopyLoop
                   ; 
                   ; Jump here from start if not in VLBANK.
                   ; 
39b4: ad 00 0c     :NotVblank      lda     IN0               ;get inputs
39b7: 29 20                        and     #%00100000        ;self test mode?
39b9: d0 1f                        bne     :Moolah           ;no, branch
                   ; 
39bb: a5 d5                        lda     test_col_irq_ctr  ;are we in color bar test?
39bd: 30 3b                        bmi     :ReadTrackball    ;no, branch
39bf: e6 d5                        inc     test_col_irq_ctr  ;update counter
39c1: 2c 00 0c                     bit     IN0               ;get inputs
39c4: 50 04                        bvc     :NotVblank1       ;branch if not in vblank
39c6: a9 00                        lda     #$00
39c8: 85 d5                        sta     test_col_irq_ctr  ;reset counter
39ca: a5 d5        :NotVblank1     lda     test_col_irq_ctr  ;get counter
39cc: 0a                           asl     A                 ;multiply by 4
39cd: 0a                           asl     A
39ce: a2 03                        ldx     #$03
39d0: 9d 04 14     :TstColLoop     sta     COLOR_PAL,x       ;set playfield colors
39d3: 69 01                        adc     #$01              ;increment color
39d5: ca                           dex                       ;next palette entry
39d6: 10 f8                        bpl     :TstColLoop       ;loop until done
39d8: 30 20                        bmi     :ReadTrackball    ;(always)

                   ; Handle coins / credits stuff.  Skipped while in test mode.
39da: 20 ad 33     :Moolah         jsr     CoinsAndCredits   ;do coin processing
39dd: a2 02                        ldx     #$02
39df: b5 c5        :CoinLoop       lda     coin_drop_ctr,x   ;copy coin counter state to hardware
39e1: 9d 00 1c                     sta     COIN_CTR,x
39e4: ca                           dex
39e5: 10 f8                        bpl     :CoinLoop
                   ; Verify the copyright string.
39e7: a2 0a                        ldx     #10               ;copyright string is 11 bytes long
39e9: a9 f4                        lda     #$f4              ;expected checksum result
39eb: 5d 03 20     :CopyChkLoop    eor     copyright,x       ;exclusive-OR each byte
39ee: ca                           dex                       ;done yet?
39ef: 10 fa                        bpl     :CopyChkLoop      ;no, loop
39f1: aa                           tax                       ;set flags
39f2: f0 06                        beq     :ReadTrackball    ;checksum came out to zero, good; branch
39f4: ba                           tsx                       ;get stack pointer in X-reg
39f5: a9 03                        lda     #$03              ;ZC
39f7: 9d 04 01                     sta     $0104,x           ;set RTI flags to cause intermittent weirdness
                   ; Read the trackball.
39fa: a2 02        :ReadTrackball  ldx     #$02              ;start with IN2 (vertical)
39fc: bd 00 0c     :AxisLoop       lda     IN0,x             ;get trackball (+ other stuff)
39ff: a8                           tay                       ;copy value to Y-reg
3a00: 38                           sec
3a01: f5 bd                        sbc     tball_prev_horz,x ;compute difference with previous
3a03: 94 bd                        sty     tball_prev_horz,x ;save new reading
3a05: 29 0f                        and     #$0f              ;get magnitude
3a07: c9 08                        cmp     #$08              ;>= 8?
3a09: 90 02                        bcc     :IsPos            ;no, branch
3a0b: 09 f0                        ora     #$f0              ;yes, sign-extend
3a0d: a8           :IsPos          tay
3a0e: f0 14                        beq     :IsZero           ;"prevents stuck trackball when counter wrapped"
3a10: 55 ba                        eor     tball_chng_horz,x ;same direction as last time?
3a12: 10 08                        bpl     :SameDir          ;yes, branch
3a14: 98                           tya
3a15: 5d 00 0c                     eor     IN0,x             ;"if we are going in the correct direction
3a18: 10 02                        bpl     :SameDir          ; use last reading"
3a1a: b4 ba                        ldy     tball_chng_horz,x
3a1c: 98           :SameDir        tya
3a1d: 95 ba                        sta     tball_chng_horz,x ;save last reading
3a1f: 18                           clc
3a20: 75 b9                        adc     tball_read_horz,x
3a22: 95 b9                        sta     tball_read_horz,x ;update count
3a24: ca           :IsZero         dex                       ;advance to next axis
3a25: ca                           dex                       ;done yet?
3a26: 10 d4                        bpl     :AxisLoop         ;no, loop
                   ; 
3a28: 8d 00 18                     sta     IRQ_ACK           ;acknowledge IRQ
3a2b: 68                           pla                       ;restore Y/X/A
3a2c: a8                           tay
3a2d: 68                           pla
3a2e: aa                           tax
3a2f: 68                           pla
3a30: 40                           rti                       ;return from IRQ

                   ; 
                   ; JOYS: handles one axis of joystick.  Called from IRQ handler.
                   ; 
                   ; On entry:
                   ;   A-reg: joystick reading (two high bits)
                   ;   Y-reg: previous reading
                   ; 
                   ; On exit:
                   ;   A-reg: shifted left twice
                   ;   Y-reg: new direction
                   ; 
3a31: 0a           HandleJoystick  asl     A                 ;shift high bit into carry
3a32: 90 06                        bcc     :RtDn             ;right or down, branch
3a34: 0a                           asl     A                 ;shift next bit into carry
3a35: 90 0e                        bcc     :LfUp             ;left or up, branch
3a37: a0 00                        ldy     #$00              ;centered
3a39: 60                           rts

3a3a: c0 fa        :RtDn           cpy     #$fa              ;at max (-6)?
3a3c: f0 05                        beq     :Shift            ;yes, branch
3a3e: b0 02                        bcs     :SameDir          ;moving same direction as before; branch
3a40: a0 00                        ldy     #$00              ;reset speed
3a42: 88           :SameDir        dey                       ;increase speed
3a43: 0a           :Shift          asl     A                 ;need to ASL twice before return
3a44: 60                           rts

3a45: c0 06        :LfUp           cpy     #$06              ;at max?
3a47: f0 05                        beq     :Return           ;yes, branch
3a49: 90 02                        bcc     :SameDir2         ;moving same direction as before; branch
3a4b: a0 00                        ldy     #$00              ;reset speed
3a4d: c8           :SameDir2       iny                       ;increase speed
3a4e: 60           :Return         rts

                   ; 
                   ; CKSUM: computes EAROM checksum.  Updates the value stored in EAROM.
                   ; 
                   ; On exit:
                   ;   Y-reg: old checksum
                   ;   A-reg: (old ^ new)
                   ;   (Z-flag set according to A-reg)
                   ; 
3a4f: a0 3c        UpdateEaromSum  ldy     #60               ;sum 0-60, skipping checksum and test entries
3a51: a9 ff                        lda     #$ff              ;salt with $ff
3a53: 59 78 01     :Loop           eor     ea_copy,y
3a56: 88                           dey
3a57: 10 fa                        bpl     :Loop
3a59: ac b5 01                     ldy     ea_checksum       ;get old checksum
3a5c: 8d b5 01                     sta     ea_checksum       ;store new checksum
3a5f: 98                           tya
3a60: 4d b5 01                     eor     ea_checksum       ;compute bit difference
3a63: 60                           rts

                   ; 
                   ; CHKEA: initializes high score table.
                   ; 
                   ; Initializes the table to the default set, then checks EAROM integrity.  If
                   ; it's valid, and the machine settings haven't changed, copy high score data
                   ; from EAROM.  If it's not valid, we clear our copy of the EAROM data.
                   ; 
3a64: a2 2f        InitHighScores  ldx     #47               ;8 scores, 6 bytes per score
3a66: bd b0 3a     :DefLoop        lda     default_hs_tbl,x
3a69: 95 02                        sta     hs_scores,x
3a6b: ca                           dex
3a6c: 10 f8                        bpl     :DefLoop
                   ; 
3a6e: 20 4f 3a                     jsr     UpdateEaromSum    ;update checksum
3a71: d0 2b                        bne     :ClearEarom       ;if data didn't match, branch
3a73: a5 fd                        lda     dsw_n9_copy       ;get N9 switches
3a75: 29 7c                        and     #%01111100        ;mask difficulty, bonus, # lives
3a77: cd 8a 01                     cmp     ea_options        ;do the settings match?
3a7a: 8d 8a 01                     sta     ea_options        ;save the new settings
3a7d: d0 1e                        bne     :Return           ;new settings, use default scores
                   ; 
3a7f: ad 7a 01                     lda     ea_copy+2         ;quick check for score validity
3a82: f0 1a                        beq     :ClearEarom       ;(top score must be > 16,543)
                   ; 
3a84: a2 08                        ldx     #8                ;3 entries, 3 bytes of score/initials each
3a86: bd 78 01     :CopyLoop       lda     ea_copy,x         ;get a score byte
3a89: 95 02                        sta     hs_scores,x
3a8b: c9 9a                        cmp     #$9a              ;is score byte > 99 (BCD)?
3a8d: b0 0f                        bcs     :ClearEarom       ;yes, invalid score
3a8f: 29 0f                        and     #$0f              ;check the low nibble
3a91: c9 0a                        cmp     #$0a              ;is low digit > 9?
3a93: b0 09                        bcs     :ClearEarom       ;yes, invalid score
                   ; 
3a95: bd 81 01                     lda     ea_copy+9,x       ;get initial byte
3a98: 95 1a                        sta     hs_initials,x     ;copy it
3a9a: ca                           dex                       ;done yet?
3a9b: 10 e9                        bpl     :CopyLoop         ;no, loop
3a9d: 60           :Return         rts

                   ; EAROM data was invalid, so zero out the data buffer.
3a9e: a9 00        :ClearEarom     lda     #$00
3aa0: a2 3e                        ldx     #62               ;clear 0-62 (all but test byte)
3aa2: 9d 78 01     :Loop           sta     ea_copy,x
3aa5: ca                           dex
3aa6: 10 fa                        bpl     :Loop
                   ; Record the machine settings.
3aa8: a5 fd                        lda     dsw_n9_copy       ;get N9 switches
3aaa: 29 7c                        and     #%01111100        ;difficulty, bonus, # lives
3aac: 8d 8a 01                     sta     ea_options        ;save in EAROM
3aaf: 60                           rts

                   ; 
                   ; Default high score table.
                   ; 
                   ; Map from initials to names:
                   ; http://www.ataricompendium.com/game_library/easter_eggs/arcade/arcadecentipede
                   ; .html
                   ; 
3ab0: 43 65 01     default_hs_tbl  .bulk   $43,$65,$01       ;16,543
3ab3: 32 54 01                     .bulk   $32,$54,$01       ; ...
3ab6: 20 43 01                     .bulk   $20,$43,$01
3ab9: 10 32 01                     .bulk   $10,$32,$01
3abc: 10 30 01                     .bulk   $10,$30,$01
3abf: 05 28 01                     .bulk   $05,$28,$01
3ac2: 01 22 01                     .bulk   $01,$22,$01
3ac5: 02 21 01                     .bulk   $02,$21,$01
                   ; 
3ac8: 05 0a 04                     .bulk   $05,$0a,$04       ;EJD - Erik Durfey
3acb: 04 06 14                     .bulk   $04,$06,$14       ;DFT - Dave Theurer
3ace: 03 01 04                     .bulk   $03,$01,$04       ;CAD - Cris Drobny
3ad1: 04 03 02                     .bulk   $04,$03,$02       ;DCB - Dona Bailey
3ad4: 05 04 00                     .bulk   $05,$04,$00       ;ED - Ed Logg
3ad7: 04 05 17                     .bulk   $04,$05,$17       ;DEW - Dave Webienson
3ada: 04 06 17                     .bulk   $04,$06,$17       ;DFW - ?
3add: 07 0a 12                     .bulk   $07,$0a,$12       ;GJR - Greg Rivera

                   ; 
                   ; INITEA: creates local copy of the EAROM data.  All 64 bytes are copied into
                   ; RAM.
                   ; 
                   ; "This routine must not be interrupted otherwise the timing pulses may cause
                   ; the EA ROM to be misread."
                   ; 
3ae0: a2 3f        ReadAllEarom    ldx     #63               ;64 bytes of data
3ae2: 20 ee 3a     :Loop           jsr     ReadEarom         ;read one location
3ae5: 9d 78 01                     sta     ea_copy,x         ;copy to RAM
3ae8: ca                           dex                       ;advance to next location
3ae9: 10 f7                        bpl     :Loop             ;loop until done
3aeb: 86 f9                        stx     earom_cur_addr    ;stop any operations by setting op addr to $ff
3aed: 60                           rts

                   ; 
                   ; READEA: reads one byte from EAROM.
                   ; 
                   ; Must not be interrupted by NMI or IRQ.
                   ; 
                   ; On entry:
                   ;   X-reg: EAROM offset to read (0-63)
                   ; 
                   ; On exit:
                   ;   A-reg: value read
                   ; 
3aee: 9d 00 16     ReadEarom       sta     EA_ADDR,x         ;set address latch
3af1: a0 08                        ldy     #$08
3af3: 8c 80 16                     sty     EA_CTRL           ;set read mode, enable chip
3af6: c8                           iny                       ;Y-reg=9
3af7: 8c 80 16                     sty     EA_CTRL           ;enable clock
3afa: 88                           dey                       ;Y=reg=8
3afb: 8c 80 16                     sty     EA_CTRL           ;disable clock, data in 2 usec
3afe: a0 00                        ldy     #$00
3b00: bd 00 17                     lda     EA_READ,x         ;read data; "6 cycles at 1.5MHz > 2 us"
3b03: 8c 80 16                     sty     EA_CTRL           ;disable chip within 24 usec
3b06: 60                           rts

                   ; 
                   ; WRITEA: writes data to EAROM, incrementally over several frames.
                   ; 
                   ; This function is called on every game frame.  We write at most one byte every
                   ; 4 frames (~67ms).
                   ; 
                   ; On entry:
                   ;   $f9: address being written (0-63 or $ff)
                   ;   $fa: current operation (0=compare, 1=write)
                   ; 
                   ; On exit:
                   ;   $f9: updated to next location to write
                   ;   $fa: (1-value) to alternate write and compare
                   ; 
3b07: a5 00        WriteEaromInc   lda     frame_ctr         ;check frame counter (60Hz / 16.7ms)
3b09: 29 03                        and     #$03              ;has it been 4 frames (66.7ms)?
3b0b: d0 17                        bne     :Return           ;not yet, bail
3b0d: 8d 80 16                     sta     EA_CTRL           ;set control to zero (disable EAROM)
3b10: a6 f9                        ldx     earom_cur_addr    ;do we have work to do?
3b12: 30 10                        bmi     :Return           ;no, bail
3b14: 46 fa                        lsr     earom_cur_op      ;check operation type
3b16: 90 0d                        bcc     :Compare          ;0=compare, branch
                   ; 
3b18: a9 02                        lda     #$02
3b1a: 8d 80 16                     sta     EA_CTRL           ;write latched data
3b1d: a9 0a                        lda     #$0a
3b1f: 8d 80 16                     sta     EA_CTRL           ;enable chip and start
3b22: c6 f9                        dec     earom_cur_addr    ;move to next address
3b24: 60           :Return         rts

3b25: 78           :Compare        sei                       ;disable interrupts
3b26: 20 ee 3a     :CmpLoop        jsr     ReadEarom         ;read the value stored in EAROM
3b29: dd 78 01                     cmp     ea_copy,x         ;does it match our local copy?
3b2c: d0 07                        bne     :Mismatch         ;no, branch
3b2e: ca                           dex                       ;reached the end of memory?
3b2f: 10 f5                        bpl     :CmpLoop          ;no, loop
3b31: 58                           cli                       ;enable interrupts
3b32: 86 f9                        stx     earom_cur_addr    ;set addr to -1; chip enable will be cleared on next
3b34: 60                           rts                       ; call (in > 64ms)

3b35: 58           :Mismatch       cli                       ;enable interrupts
3b36: 86 f9                        stx     earom_cur_addr    ;save where we left off
3b38: a9 06                        lda     #$06
3b3a: 8d 80 16                     sta     EA_CTRL           ;deselect chip, and set erase mode
3b3d: bd 78 01                     lda     ea_copy,x
3b40: 9d 00 16                     sta     EA_ADDR,x         ;set data and address latch
3b43: a9 0e                        lda     #$0e
3b45: 8d 80 16                     sta     EA_CTRL           ;enable chip and start erase
3b48: e6 fa                        inc     earom_cur_op      ;set to write on next call
3b4a: 60                           rts

                   ; 
                   ; RESET: handles power-on reset.
                   ; 
3b4b: d8           HandleReset     cld                       ;clear decimal mode
3b4c: a2 ff                        ldx     #$ff
3b4e: 9a                           txs                       ;init stack pointer
3b4f: e8                           inx                       ;X-reg=0
3b50: 8a                           txa                       ;A-reg=0
3b51: 95 00        :Loop           sta     $00,x             ;clear all RAM to zero
3b53: 9d 00 01                     sta     $0100,x
3b56: 9d 00 04                     sta     PLAYFIELD,x
3b59: 9d 00 05                     sta     PLAYFIELD+$100,x
3b5c: 9d 00 06                     sta     PLAYFIELD+$200,x
3b5f: 9d 00 07                     sta     PLAYFIELD+$300,x
3b62: ca                           dex
3b63: d0 ec                        bne     :Loop             ;A-reg and X-reg both 0 when done
                   ; 
3b65: 8d 0f 10                     sta     POKEY_SKCTL       ;init POKEY
3b68: 8d 08 10                     sta     POKEY_AUDCTL
3b6b: 8d 00 24                     sta     CLR_TBALL         ;clear trackball counters
3b6e: 8d 07 1c                     sta     FLIP              ;clear flip
                   ; 
3b71: ad 00 0c                     lda     IN0               ;check buttons
3b74: 29 20                        and     #%00100000        ;is self-test button pressed?
3b76: f0 19                        beq     SelfTest          ;yes, branch
                   ; 
3b78: ad 00 08                     lda     DSW_N9            ;grab a copy of the N9 switches
3b7b: 85 fd                        sta     dsw_n9_copy       ;save for general use
3b7d: ca                           dex                       ;X-reg=$ff
3b7e: 86 86                        stx     attract_mode      ;set "attract" mode
3b80: 86 c1                        stx     plyr_hs_init_slot ;clear "initials needed" flags
3b82: 86 c2                        stx     plyr_hs_init_slot+1
3b84: a9 01                        lda     #$01              ;init copy-protection value
3b86: 85 ff                        sta     one
3b88: 20 e0 3a                     jsr     ReadAllEarom      ;read EAROM contents into RAM
3b8b: 20 64 3a                     jsr     InitHighScores    ;merge default high score table with EAROM contents
3b8e: 4c 0e 20                     jmp     Start             ;continue initialization

                   ; 
                   ; TREPT: performs self-test.
                   ; 
                   ; On entry:
                   ;   X-reg=$00
                   ; 
3b91: 8e 04 14     SelfTest        stx     COLOR_PAL         ;set playfield background color to beige
3b94: 8e 01 10                     stx     POKEY_AUDC1       ;silence POKEY
3b97: 8e 03 10                     stx     POKEY_AUDC2
3b9a: 8e 05 10                     stx     POKEY_AUDC3
3b9d: 8e 07 10                     stx     POKEY_AUDC4
3ba0: e8                           inx                       ;X-reg=1 (greenish cyan)
3ba1: 8e 05 14                     stx     COLOR_PAL+1
3ba4: 8e 0d 14                     stx     COLOR_PAL+9
3ba7: e8                           inx                       ;X-reg=2 (reddish magenta)
3ba8: 8e 06 14                     stx     COLOR_PAL+2
3bab: 8e 0e 14                     stx     COLOR_PAL+10
3bae: e8                           inx                       ;X-reg=3 (dark blue)
3baf: 8e 07 14                     stx     COLOR_PAL+3
3bb2: 8e 0f 14                     stx     COLOR_PAL+11
                   ; Verify that zero page is working.
3bb5: a2 00                        ldx     #$00
3bb7: b5 00        :ZpZero         lda     $00,x             ;was zeroed earlier; see if it still is
3bb9: d0 43                        bne     :BadRam           ;failed, branch
3bbb: a9 11                        lda     #$11              ;initial value for test pattern: bits 0/4
3bbd: 95 00        :ZpLoop         sta     $00,x             ;write test pattern
3bbf: a8                           tay                       ;stash pattern in Y-reg
3bc0: 55 00                        eor     $00,x             ;verify test pattern
3bc2: d0 3a                        bne     :BadRam           ;failed, branch
3bc4: 98                           tya                       ;retrieve pattern from Y-reg
3bc5: 0a                           asl     A                 ;shift left
3bc6: 90 f5                        bcc     :ZpLoop           ;if we haven't tried all bit positions, branch
3bc8: e8                           inx                       ;move on to next address
3bc9: d0 ec                        bne     :ZpZero           ;loop until done
3bcb: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
                   ; 
3bce: 8a                           txa                       ;A-reg=0
3bcf: 85 8b                        sta     temp1             ;set low byte of ptr to $00
3bd1: 2a                           rol     A                 ;carry was set earlier, so this makes A-reg=1
3bd2: 85 8c        :PageLoop       sta     temp1+1           ;set high byte of ptr
3bd4: a0 00                        ldy     #$00              ;start at offset 0
3bd6: a2 11        :PageByteLoop   ldx     #$11              ;bit pattern to test
3bd8: b1 8b                        lda     (temp1),y         ;confirm successfully zeroed by init
3bda: d0 28                        bne     :BadPFRam         ;failed, branch
3bdc: 8a           :PagePatLoop    txa                       ;put test pattern in A-reg
3bdd: 91 8b                        sta     (temp1),y         ;write it
3bdf: 51 8b                        eor     (temp1),y         ;verify it
3be1: d0 21                        bne     :BadPFRam         ;failed, branch
3be3: 8a                           txa                       ;get pattern in A-reg
3be4: 0a                           asl     A                 ;move to next pattern
3be5: aa                           tax
3be6: 90 f4                        bcc     :PagePatLoop      ;loop until all patterns checked
3be8: c8                           iny                       ;move to next byte
3be9: d0 eb                        bne     :PageByteLoop     ;loop until done
3beb: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
                   ; 
3bee: e6 8c                        inc     temp1+1           ;advance pointer to next page
3bf0: a5 8c                        lda     temp1+1           ;check value
3bf2: c9 02                        cmp     #$02              ;on page 2?
3bf4: d0 02                        bne     :NotPg2           ;no, branch
3bf6: a9 04                        lda     #>PLAYFIELD       ;yes, skip pages 2/3
3bf8: c9 08        :NotPg2         cmp     #>PLAYFIELD+$400  ;have we reached the end of RAM?
3bfa: 90 d6                        bcc     :PageLoop         ;not yet, loop
3bfc: b0 5f                        bcs     TestMore          ;(always)

3bfe: c9 10        :BadRam         cmp     #$10              ;which nibble failed? (sets carry flag)
3c00: a9 00                        lda     #$00
3c02: 10 12                        bpl     :ReportRam        ;(always)

3c04: a6 8c        :BadPFRam       ldx     temp1+1           ;get high byte of pointer
3c06: e0 04                        cpx     #$04              ;in playfield RAM?
3c08: 90 f4                        bcc     :BadRam           ;no, page 0/1; branch
3c0a: aa                           tax                       ;copy failed byte value to X-reg
3c0b: 98                           tya                       ;copy byte number to A-reg
3c0c: 29 30                        and     #%00110000        ;"the 2101s are organized by blocks of
3c0e: 4a                           lsr     A                 ; 16 bytes each alternating every
3c0f: 4a                           lsr     A                 ; 4*16 bytes"
3c10: 4a                           lsr     A
3c11: 4a                           lsr     A                 ;clears carry
3c12: 69 01                        adc     #$01              ;"1=first pair, 2=second pair, etc"
3c14: e0 10                        cpx     #$10              ;"determine LSB or MSB"
3c16: 2a           :ReportRam      rol     A                 ;number of good chips before error
3c17: a8                           tay                       ;copy to Y-reg
3c18: a9 40                        lda     #$40
3c1a: 8d 00 10                     sta     POKEY_AUDF1       ;set frequency
3c1d: a2 03                        ldx     #$03
3c1f: 8e 0f 10                     stx     POKEY_SKCTL       ;enable POKEY for error reporting
                   ; Make a number of beeps, specified by Y-reg + 1.
3c22: a2 10        :MakeBeeps      ldx     #16               ;wait for 16 frames (~0.25 sec)
3c24: a9 af                        lda     #%10101111        ;noise=no poly, volume=15
3c26: 8d 01 10                     sta     POKEY_AUDC1
                   ; 
3c29: 2c 00 0c     :WaitForBlank1  bit     IN0               ;in VBLANK?
3c2c: 50 fb                        bvc     :WaitForBlank1    ;no, spin until it starts
3c2e: 2c 00 0c     :WaitForEnd1    bit     IN0               ;in VBLANK?
3c31: 70 fb                        bvs     :WaitForEnd1      ;yes, spin until it ends
3c33: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
3c36: ca                           dex
3c37: d0 f0                        bne     :WaitForBlank1    ;loop until done
                   ; 
3c39: 8e 01 10                     stx     POKEY_AUDC1       ;X-reg=0, disable sound
3c3c: a2 10                        ldx     #16               ;wait for 16 frames
3c3e: 2c 00 0c     :WaitForBlank2  bit     IN0               ;in VBLANK?
3c41: 50 fb                        bvc     :WaitForBlank2    ;no, spin until it starts
3c43: 2c 00 0c     :WaitForEnd2    bit     IN0               ;in VBLANK?
3c46: 70 fb                        bvs     :WaitForEnd2      ;yes, spin until it ends
3c48: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
3c4b: ca                           dex
3c4c: d0 f0                        bne     :WaitForBlank2    ;loop until done
                   ; 
3c4e: 88                           dey                       ;have we made all the beeps?
3c4f: 10 d1                        bpl     :MakeBeeps        ;not yet, branch
                   ; Sit and spin until self-test switch is disabled.
3c51: 8d 00 20     :TestHold       sta     WATCHDOG_RESET    ;feed the dog
3c54: ad 00 0c                     lda     IN0               ;get switch status
3c57: 29 20                        and     #%00100000        ;is self-test enabled?
3c59: f0 f6                        beq     :TestHold         ;yes, branch
3c5b: d0 fe        :SpinToDeath    bne     :SpinToDeath      ;no, let watchdog reset machine

                   ; 
                   ; TVTEST: displays TV monitor adjustment test.
                   ; 
                   ; The procedure is:
                   ;  - Switch to self-test mode while enabling the slam switch.
                   ;  - Release slam switch.  Admire the color bars.
                   ;  - Disable the self-test switch, then activate a coin slot to advance.
                   ;  - Admire the grid of dots.
                   ;  - Enable the self-test switch to advance to general test.
                   ; 
                   ; This is tricky to do in MAME, because the slam switch requires holding a key
                   ; down at the right time, and toggling the state of the self-test switch resets
                   ; the machine.
                   ; 
                   ]ptr            .var    $8b    {addr/2}

3c5d: ad 01 0c     TestMore        lda     IN1               ;get inputs
3c60: 29 10                        and     #%00010000        ;check slam switch
3c62: f0 03                        beq     :ScreenTests      ;enabled, branch
3c64: 4c de 3c                     jmp     TestPatScreen     ;not enabled, jump to next set

                   ; Clear zero page to zeroes.
3c67: aa           :ScreenTests    tax                       ;X-reg=A-reg=0
3c68: 95 00        :ZeroLoop       sta     $00,x
3c6a: e8                           inx
3c6b: d0 fb                        bne     :ZeroLoop
                   ; Place all motion objects in the top row (where they're invisible).
3c6d: a2 0f                        ldx     #15               ;16 motion objects
3c6f: a9 f8                        lda     #$f8
3c71: 95 64        :VertLoop       sta     mobj_vert,x       ;set vertical position
3c73: ca                           dex
3c74: 10 fb                        bpl     :VertLoop
                   ; Draw color bars.
3c76: a9 07                        lda     #$07              ;set pointer to $0700
3c78: 85 8c                        sta     ]ptr+1
3c7a: a0 bf                        ldy     #$bf              ;start at $07bf
3c7c: a9 2d        :ColLoop        lda     #$2d              ;blank tile with color #3
3c7e: a2 08        :ChunkLoop      ldx     #$08              ;use same color for 8 rows
3c80: 91 8b        :SubLoop        sta     (]ptr),y          ;set value
3c82: 88                           dey                       ;advance ptr (move up on row)
3c83: ca                           dex                       ;reached end of 8-row chunk?
3c84: d0 fa                        bne     :SubLoop          ;not yet, loop
3c86: 38                           sec
3c87: e9 01                        sbc     #$01              ;update color value
3c89: c9 2a                        cmp     #$2a              ;more to do?  ($2a is blank tile with color #0)
3c8b: b0 f1                        bcs     :ChunkLoop        ;yes, loop
3c8d: c0 ff                        cpy     #$ff              ;reached end of page?
3c8f: d0 eb                        bne     :ColLoop          ;not yet, loop
3c91: c6 8c                        dec     ]ptr+1            ;update high byte of pointer
3c93: a5 8c                        lda     ]ptr+1            ;get high byte of pointer
3c95: c9 04                        cmp     #$04              ;is it still >= $0400?
3c97: b0 e3                        bcs     :ColLoop          ;yes, loop
                   ; Hold until a coin slot is tripped.
3c99: 58                           cli                       ;enable IRQ
3c9a: ad 00 0c     :ChkCoins       lda     IN0               ;get inputs
3c9d: 29 20                        and     #%00100000        ;check self-test switch
3c9f: d0 f9                        bne     :ChkCoins         ;not enabled, loop
3ca1: 46 8a                        lsr     irq_sync          ;show IRQ handler that we're alive
3ca3: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
3ca6: ad 01 0c                     lda     IN1               ;get inputs
3ca9: 29 e0                        and     #%11100000        ;mask to get coin switches
3cab: 49 e0                        eor     #%11100000        ;invert meaning (so 1=on)
3cad: f0 eb                        beq     :ChkCoins         ;none active, loop
                   ; Fill the playfield with a grid of dots.
3caf: a9 1d                        lda     #$1d              ;centered dot, in color #1
3cb1: 78                           sei                       ;disable IRQ
3cb2: 9d 00 04     :FillLoop       sta     PLAYFIELD,x       ;write value to first 3 pages of playfield
3cb5: 9d 00 05                     sta     PLAYFIELD+$100,x
3cb8: 9d 00 06                     sta     PLAYFIELD+$200,x
3cbb: e8                           inx
3cbc: d0 f4                        bne     :FillLoop
3cbe: 9d 00 07     :FillLoop1      sta     PLAYFIELD+$300,x  ;now do $0700-07bf
3cc1: e8                           inx
3cc2: e0 c0                        cpx     #$c0
3cc4: 90 f8                        bcc     :FillLoop1
                   ; 
3cc6: a2 08                        ldx     #$08              ;white
3cc8: 8e 05 14                     stx     COLOR_PAL+1       ;set color #1
3ccb: a2 0f                        ldx     #$0f              ;black
3ccd: 8e 04 14                     stx     COLOR_PAL         ;set color #0
                   ; Hold until self-test is disabled.
3cd0: ad 00 0c     :WaitTest       lda     IN0               ;get inputs
3cd3: 29 20                        and     #%00100000        ;check self-test switch
3cd5: d0 f9                        bne     :WaitTest         ;not enabled, loop
3cd7: 8d 00 20                     sta     WATCHDOG_RESET
3cda: 46 8a                        lsr     irq_sync
3cdc: 10 f2                        bpl     :WaitTest

                   ; 
                   ; PATSCN: displays pattern screen.
                   ; 
3cde: a2 00        TestPatScreen   ldx     #$00
3ce0: 8a           :InitLoop       txa                       ;copy index to A-reg
3ce1: 9d 00 07                     sta     PLAYFIELD+$300,x  ;store incrementing value in $0700-07ff
3ce4: a9 00                        lda     #$00              ;store $00 in the rest of RAM
3ce6: 95 00                        sta     $00,x             ; (blank tile, color #0)
3ce8: 9d 00 04                     sta     PLAYFIELD,x
3ceb: 9d 00 05                     sta     PLAYFIELD+$100,x
3cee: 9d 00 06                     sta     PLAYFIELD+$200,x
3cf1: e8                           inx
3cf2: d0 ec                        bne     :InitLoop
                   ; 
3cf4: ca                           dex                       ;X-reg=$ff
3cf5: 86 d5                        stx     test_col_irq_ctr  ;init counter
3cf7: 86 e3                        stx     test_input_arr+6  ;"to test polycounter"
3cf9: 8d 03 1c                     sta     ST_LED_1          ;turn on start LEDs
3cfc: 8d 04 1c                     sta     ST_LED_2
                   ; 
3cff: a2 0f                        ldx     #15               ;16 motion objects
3d01: 8a           :MobjInitLoop   txa                       ;use index as position
3d02: 09 80                        ora     #$80              ;center
3d04: 95 54                        sta     mobj_horz,x       ;set horizontal / vertical position
3d06: 95 64                        sta     mobj_vert,x
3d08: ca                           dex
3d09: 10 f6                        bpl     :MobjInitLoop
                   ; 
3d0b: ad 0a 10                     lda     POKEY_RANDOM      ;get random number
3d0e: 4d 0a 10                     eor     POKEY_RANDOM      ;result should be zero since POKEY is not enabled
3d11: 85 e5                        sta     test_input_arr+8
3d13: a9 03                        lda     #$03
3d15: 8d 0f 10                     sta     POKEY_SKCTL       ;enable POKEY
                   ; 
                   ; ROMTST: tests ROM checksums.
                   ; 
                   ; There are four 2KB ROM chips, each of which is checksummed independently.
                   ; 
3d18: a2 00                        ldx     #<ENTRY           ;set pointer to start of ROM
3d1a: 86 8b                        stx     ]ptr
3d1c: a9 20                        lda     #>ENTRY
3d1e: 85 8c                        sta     ]ptr+1
3d20: a2 1f                        ldx     #$1f              ;$20 pages (8KB)
3d22: a9 ff                        lda     #$ff              ;initial value
3d24: a0 00        :PageLoop       ldy     #$00              ;start of page
3d26: 8e 00 20                     stx     WATCHDOG_RESET    ;feed the dog
3d29: 51 8b        :ChkLoop        eor     (]ptr),y          ;exclusive-OR each byte
3d2b: c8                           iny
3d2c: d0 fb                        bne     :ChkLoop
3d2e: a8                           tay                       ;copy checksum to Y-reg
3d2f: 8a                           txa                       ;copy page counter to A-reg
3d30: 29 07                        and     #%00000111        ;get page within ROM chip
3d32: c9 01                        cmp     #$01              ;clear carry if end of ROM
3d34: 98                           tya                       ;put checksum back in A-reg
3d35: b0 03                        bcs     :InRom            ;branch if still in same chip
3d37: 48                           pha                       ;save checksum for this ROM
3d38: a9 ff                        lda     #$ff              ;reset checksum to initial value
3d3a: e6 8c        :InRom          inc     ]ptr+1            ;advance to the next page
3d3c: ca                           dex                       ;decrement the page count
3d3d: 10 e5                        bpl     :PageLoop         ;loop if not yet done
                   ; Display results.
3d3f: a9 04                        lda     #>PLAYFIELD
3d41: 85 92                        sta     out_ptr+1         ;set pointer to $0400
3d43: a2 03                        ldx     #3                ;four ROM chips
3d45: 8a           :DisplayLoop    txa
3d46: 49 3f                        eor     #$3f              ;start address for message
3d48: 85 91                        sta     out_ptr           ;set low byte of pointer
3d4a: 68                           pla                       ;pull result
3d4b: f0 11                        beq     :ChkGood          ;checksum good, branch
3d4d: 48                           pha                       ;push result back on
3d4e: 8a                           txa
3d4f: 09 20                        ora     #$20              ;form ROM number ('0' - '3')
3d51: 20 85 38                     jsr     DrawChar          ;draw it
3d54: a9 00                        lda     #$00
3d56: 20 85 38                     jsr     DrawChar          ;draw blank space
3d59: 68                           pla                       ;pull result
3d5a: 18                           clc
3d5b: 20 9e 38                     jsr     Draw2Digits       ;draw hex value
3d5e: ca           :ChkGood        dex                       ;move to next ROM
3d5f: 10 e4                        bpl     :DisplayLoop      ;loop until done
                   ; 
                   ; EAROM accounting computations.
                   ; 
                   • Clear variables
                   ]avg_game_time  .var    $8d    {addr/1}
                   ]game_count     .var    $8e    {addr/3}
                   ]total_time     .var    $91    {addr/4}

3d61: 20 e0 3a                     jsr     ReadAllEarom      ;read all EAROM values into RAM
3d64: a0 06                        ldy     #$06              ;copy 7 bytes
3d66: b9 8b 01     :CopyLoop       lda     ea_games,y        ;3-byte BCD game count in $8e-90
3d69: 99 8e 00                     sta     temp2+1,y         ;4-byte BCD total play time, in $91-94
3d6c: 88                           dey
3d6d: 10 f7                        bpl     :CopyLoop
                   ; 
3d6f: f8                           sed                       ;enable decimal mode
3d70: ad 8b 01                     lda     ea_games          ;check total game count
3d73: 0d 8c 01                     ora     ea_games+1
3d76: 0d 8d 01                     ora     ea_games+2
3d79: f0 1f                        beq     :GotCount         ;zero, branch with Y-reg=$ff
3d7b: c8                           iny                       ;Y-reg=$00
                   ; Compute (total_time / total_games) to get average game length.
3d7c: c8           :DivLoop        iny                       ;increment result (quotient)
3d7d: f0 1b                        beq     :GotCount         ;no joy after 256 iterations, give up (with Y-reg=0)
3d7f: a5 91                        lda     ]total_time       ;repeatedly subtract game count from total time
3d81: 38                           sec
3d82: e5 8e                        sbc     ]game_count
3d84: 85 91                        sta     ]total_time
3d86: a5 92                        lda     ]total_time+1
3d88: e5 8f                        sbc     ]game_count+1
3d8a: 85 92                        sta     ]total_time+1
3d8c: a5 93                        lda     ]total_time+2
3d8e: e5 90                        sbc     ]game_count+2
3d90: 85 93                        sta     ]total_time+2
3d92: a5 94                        lda     ]total_time+3
3d94: e9 00                        sbc     #$00
3d96: 85 94                        sta     ]total_time+3
3d98: 10 e2                        bpl     :DivLoop
3d9a: d8           :GotCount       cld                       ;disable decimal mode
3d9b: 84 8d                        sty     ]avg_game_time
3d9d: 58                           cli
                   ; 
                   ; SFTEST: main loop of self test.
                   ; 
                   • Clear variables
                   ]avg_game_time  .var    $8d    {addr/1}

3d9e: 46 8a        TestMain        lsr     irq_sync
3da0: 90 fc                        bcc     TestMain          ;wait for IRQ to get to VBLANK
3da2: ad 00 0c                     lda     IN0               ;get inputs
3da5: 29 20                        and     #%00100000        ;is self-test switch enabled?
3da7: d0 fe        :DeathSpin      bne     :DeathSpin        ;no, loop until watchdog restarts us
3da9: 8d 00 20                     sta     WATCHDOG_RESET    ;feed the dog
                   ; 
3dac: ad 01 0c                     lda     IN1               ;get inputs
3daf: 4a                           lsr     A                 ;shift player 1 start into carry
3db0: 26 ea                        rol     test_st1_debounce ;roll into debounce
3db2: a5 ea                        lda     test_st1_debounce
3db4: 29 03                        and     #%00000011
3db6: c9 02                        cmp     #%00000010        ;was off, now on?
3db8: d0 24                        bne     :NotSt1           ;no, branch
                   ; Player 1 start button pressed.  Update background color and audio channel.
3dba: a5 e6                        lda     test_audio_chan   ;get audio test channel
3dbc: aa                           tax                       ;use as index
3dbd: 18                           clc
3dbe: 69 02                        adc     #$02              ;advance to next channel
3dc0: 29 06                        and     #$06
3dc2: 85 e6                        sta     test_audio_chan   ;save updated value
3dc4: a9 00                        lda     #$00
3dc6: 9d 01 10                     sta     POKEY_AUDC1,x     ;disable sound on previous channel
3dc9: a5 e7                        lda     test_mobj         ;get motion object picture index
3dcb: 18                           clc
3dcc: 69 01                        adc     #$01              ;increment it
3dce: 29 0f                        and     #$0f              ;wrap it if it exceeds 15
3dd0: 85 e7                        sta     test_mobj         ;save updated value
3dd2: a6 e8                        ldx     test_bk_color     ;get background color
3dd4: e8                           inx                       ;increment
3dd5: 8a                           txa
3dd6: 29 0f                        and     #$0f              ;wrap it if it exceeds 15
3dd8: aa                           tax
3dd9: 8e 04 14                     stx     COLOR_PAL         ;set as color 0
3ddc: 86 e8                        stx     test_bk_color     ;store updated value
                   ; 
3dde: ad 01 0c     :NotSt1         lda     IN1               ;get inputs
3de1: 4a                           lsr     A                 ;shift player 2 start into carry
3de2: 4a                           lsr     A
3de3: 26 eb                        rol     test_st2_debounce ;roll into debounce
3de5: a5 eb                        lda     test_st2_debounce
3de7: 29 03                        and     #%00000011
3de9: c9 02                        cmp     #%00000010        ;was off, now on?
3deb: d0 16                        bne     :NotSt2           ;no, branch
                   ; Player 2 start button pressed.  Update foreground color.  (Not available on
                   ; rev4.)
3ded: e6 e9                        inc     test_plf_color    ;increment the playfield color
3def: a5 e9                        lda     test_plf_color    ;get the color
3df1: a0 01                        ldy     #$01              ;start at palette entry 1
3df3: 18           :IncColLoop     clc
3df4: 69 01                        adc     #$01              ;increment the color
3df6: 29 0f                        and     #$0f              ;wrap if it exceeds 15
3df8: 99 04 14                     sta     COLOR_PAL,y       ;set playfield color entry
3dfb: 99 0c 14                     sta     COLOR_PAL+8,y     ;set motion object color entry
3dfe: c8                           iny                       ;move to next palette entry
3dff: c0 04                        cpy     #$04              ;done yet?
3e01: 90 f0                        bcc     :IncColLoop       ;no, branch
                   ; 
3e03: ad 01 0c     :NotSt2         lda     IN1               ;get inputs
3e06: 4a                           lsr     A                 ;shift player 1 fire into carry
3e07: 4a                           lsr     A
3e08: 4a                           lsr     A
3e09: 26 ec                        rol     test_fire1_debounce ;roll into debounce
3e0b: a5 ec                        lda     test_fire1_debounce
3e0d: 29 03                        and     #%00000011
3e0f: 49 02                        eor     #%00000010        ;was off, now on?
3e11: d0 07                        bne     :NotFire1         ;no, branch
                   ; Player 1 fire button pressed.  Increment the motion object pictures.
3e13: a2 0f                        ldx     #$0f              ;16 motion objects
3e15: f6 34        :MobjLoop       inc     mobj_pict,x       ;increment the picture value
3e17: ca                           dex
3e18: 10 fb                        bpl     :MobjLoop         ;loop until done
                   ; Display some parameters.
3e1a: a9 05        :NotFire1       lda     #$05              ;set output pointer to $0538
3e1c: 85 92                        sta     out_ptr+1
3e1e: a9 38                        lda     #$38
3e20: 85 91                        sta     out_ptr
3e22: ad 00 08                     lda     DSW_N9            ;get N9 switches
3e25: 29 0c                        and     #%00001100        ;mask to get lives per game (2/3/4/5)
3e27: 4a                           lsr     A                 ;shift into low bits
3e28: 4a                           lsr     A                 ;this also clears carry
3e29: 69 01                        adc     #$01              ;add one (now 1-4)
3e2b: 85 8b                        sta     temp1             ;save in ZP
                   ; Draw icons to indicate lives per game.
3e2d: a2 05                        ldx     #$05              ;draw 5 tiles
3e2f: a9 1f        :LifeLoop       lda     #$1f              ;tile for player life
3e31: 24 8b                        bit     temp1             ;is life value >= 0?
3e33: 10 02                        bpl     :OuttaLives       ;yes, branch
3e35: a9 00                        lda     #$00              ;no, draw a blank tile instead
3e37: 20 85 38     :OuttaLives     jsr     DrawChar          ;draw tile
3e3a: c6 8b                        dec     temp1             ;count down lives
3e3c: ca                           dex                       ;done yet?
3e3d: d0 f0                        bne     :LifeLoop         ;no, branch
                   ; Draw three '1's.  Used to be the coin mech multipliers.
3e3f: a9 37                        lda     #$37              ;one line down from lives
3e41: 85 91                        sta     out_ptr
3e43: a9 21                        lda     #$21              ;draw '1' three times
3e45: 20 85 38                     jsr     DrawChar
3e48: a9 21                        lda     #$21
3e4a: 20 85 38                     jsr     DrawChar
3e4d: a9 21                        lda     #$21
3e4f: 20 85 38                     jsr     DrawChar
                   ; Draw bonus coin setting.  Start by setting the tiles to blank.
3e52: a9 36                        lda     #$36              ;one line down from the '1's
3e54: 85 91                        sta     out_ptr
3e56: a9 00                        lda     #$00
3e58: a8                           tay
3e59: 91 91                        sta     (out_ptr),y       ;set to blank
3e5b: a0 40                        ldy     #$40              ;two columns over
3e5d: 91 91                        sta     (out_ptr),y       ;set to blank
                   ; 
3e5f: ad 01 08                     lda     DSW_N8            ;get N8 settings
3e62: 4a                           lsr     A                 ;shift right to get bonus coin setting in low bits
3e63: 4a                           lsr     A
3e64: 4a                           lsr     A
3e65: 4a                           lsr     A
3e66: 4a                           lsr     A
3e67: f0 1b                        beq     :NoBonus          ;if zero, no bonus coins
3e69: aa                           tax
3e6a: c9 06                        cmp     #$06              ;is it 110 or 111 (undefined config)?
3e6c: b0 16                        bcs     :NoBonus          ;yes, show nothing
3e6e: bd d8 3f                     lda     :bonus_coins-1,x  ;get char value for number of coins to insert
3e71: 20 85 38                     jsr     DrawChar          ;draw it
3e74: a9 00                        lda     #$00              ;blank space
3e76: 20 85 38                     jsr     DrawChar          ;draw that
3e79: a9 21                        lda     #$21              ;'1', for number of bonus coins
3e7b: e0 03                        cpx     #$03              ;setting 011 (4 coins -> +2 coins)?
3e7d: d0 02                        bne     :Add1             ;no, branch
3e7f: a9 22                        lda     #$22              ;'2'
3e81: 20 85 38     :Add1           jsr     DrawChar          ;draw number
                   ; Display game difficulty.
                   ]str_ptr        .var    $93    {addr/2}

3e84: a9 3f        :NoBonus        lda     #>:str_hard       ;get pointer to easy/hard string
3e86: 85 94                        sta     ]str_ptr+1
3e88: a9 ee                        lda     #<:str_hard
3e8a: 2c 00 08                     bit     DSW_N9            ;test N9 switches
3e8d: 50 02                        bvc     :Hard             ;hard difficulty, branch
3e8f: a9 f2                        lda     #<:str_easy
3e91: 85 93        :Hard           sta     ]str_ptr
3e93: a9 35                        lda     #$35              ;one line below bonus coins
3e95: 85 91                        sta     out_ptr           ;set pointer
3e97: 20 74 38                     jsr     DrawText          ;draw the string
                   ; Copy inputs to array.
3e9a: ad 01 0c                     lda     IN1               ;column 2 = IN1
3e9d: 85 df                        sta     test_input_arr+2
3e9f: ad 00 08                     lda     DSW_N9            ;column 0 = N9
3ea2: 85 dd                        sta     test_input_arr
3ea4: ad 01 08                     lda     DSW_N8            ;column 1 = N8
3ea7: 85 de                        sta     test_input_arr+1
3ea9: ad 00 0c                     lda     IN0               ;column 3 = trackball horizontal
3eac: 29 8f                        and     #%10001111
3eae: 85 e0                        sta     test_input_arr+3
3eb0: ad 02 0c                     lda     IN2               ;column 4 = trackball vertical
3eb3: 29 8f                        and     #%10001111
3eb5: 85 e1                        sta     test_input_arr+4
3eb7: ad 03 0c                     lda     IN3               ;column 5 = joystick
3eba: 85 e2                        sta     test_input_arr+5
                   ; 
3ebc: ad 0a 10                     lda     POKEY_RANDOM      ;get random value
3ebf: 48                           pha
3ec0: 25 e3                        and     test_input_arr+6
3ec2: 85 e3                        sta     test_input_arr+6
3ec4: 68                           pla
3ec5: 05 e4                        ora     test_input_arr+7  ;"check for shorted bits in POKEY"
3ec7: 85 e4                        sta     test_input_arr+7
                   ; SWTCHS: play sound based on how many switches are pressed.
3ec9: a2 00                        ldx     #$00              ;init count
3ecb: ad 01 0c                     lda     IN1               ;get IN1 switches
3ece: 38                           sec
3ecf: 2a                           rol     A                 ;rotate high bit into carry, and set low bit of A-reg
3ed0: b0 01        :CountLoop      bcs     :BitSet           ;if bit was set, branch
3ed2: e8                           inx                       ;increment count of 0 bits
3ed3: 0a           :BitSet         asl     A                 ;shift high bit into carry
3ed4: d0 fa                        bne     :CountLoop        ;loop until all bits checked
                   ; 
3ed6: 8a                           txa                       ;copy 0-bit count to A-reg
3ed7: a4 e6                        ldy     test_audio_chan
3ed9: 0a                           asl     A                 ;multiply count x8
3eda: 0a                           asl     A
3edb: 0a                           asl     A
3edc: 99 00 10                     sta     POKEY_AUDF1,y     ;use as frequency
3edf: 8a                           txa                       ;copy 0-bit count to A-reg
3ee0: 09 a0                        ora     #%10100000        ;no noise; use count as volume
3ee2: 99 01 10                     sta     POKEY_AUDC1,y
                   ; Move motion object, with trackball / joystick.
3ee5: a6 e7                        ldx     test_mobj         ;get index of motion object
3ee7: a0 00                        ldy     #$00
3ee9: a5 b9                        lda     tball_read_horz   ;get/reset horizontal movement
3eeb: 84 b9                        sty     tball_read_horz
3eed: 18                           clc
3eee: 75 54                        adc     mobj_horz,x       ;update horizontal position
3ef0: 95 54                        sta     mobj_horz,x
3ef2: b5 64                        lda     mobj_vert,x       ;update vertical position
3ef4: 38                           sec                       ;vertical is inverted
3ef5: e5 bb                        sbc     tball_read_vert
3ef7: 84 bb                        sty     tball_read_vert   ;reset vertical movement
3ef9: 95 64                        sta     mobj_vert,x
                   ; 
3efb: a0 d0                        ldy     #$d0              ;position for 6 columns over, 16 rows up from bottom
3efd: a2 05                        ldx     #$05              ;6 columns of inputs
3eff: 9a           :ByteLoop       txs                       ;use S-reg as temporary (nothing useful on stack)
3f00: a2 07                        ldx     #$07              ;8 bits per byte
3f02: 8a           :BitLoop        txa                       ;preserve bit count
3f03: ba                           tsx                       ;copy byte number to X-reg
3f04: 36 dd                        rol     test_input_arr,x  ;get high bit
3f06: aa                           tax                       ;restore bit count
3f07: a9 21                        lda     #$21              ;'1'
3f09: b0 02                        bcs     :Draw1            ;if bit was set, draw '1'
3f0b: a9 20                        lda     #$20              ;'0'
3f0d: c8           :Draw1          iny                       ;move up one row
3f0e: 99 00 04                     sta     PLAYFIELD,y       ;write character tile
3f11: ca                           dex                       ;decrement bit count
3f12: 10 ee                        bpl     :BitLoop          ;loop until all bits done
3f14: 98                           tya                       ;copy output offset to A-reg
3f15: 38                           sec                       ;subtract 8 to back up to first row
3f16: e9 28                        sbc     #$28              ;subtract $20 to move us left one column
3f18: a8                           tay                       ;move it back to Y-reg
3f19: ba                           tsx                       ;get byte counter
3f1a: ca                           dex                       ;update
3f1b: 10 e2                        bpl     :ByteLoop         ;loop until done
                   ; 
3f1d: a9 04                        lda     #$04              ;set output pointer to $043a (near top left)
3f1f: 85 92                        sta     out_ptr+1
3f21: a9 3a                        lda     #$3a
3f23: 85 91                        sta     out_ptr
3f25: a5 e4                        lda     test_input_arr+7  ;check the POKEY random bits
3f27: 49 ff                        eor     #$ff
3f29: 05 e3                        ora     test_input_arr+6
3f2b: 05 e5                        ora     test_input_arr+8
3f2d: f0 02                        beq     :PokeyOk          ;looks good, branch
3f2f: a9 25                        lda     #$25              ;'5' (indicates failure in chip B/C/D3)
3f31: 20 85 38     :PokeyOk        jsr     DrawChar
                   ; 
3f34: 20 07 3b                     jsr     WriteEaromInc     ;continue any EAROM write in progress
3f37: 20 4f 3a                     jsr     UpdateEaromSum    ;update the EAROM checksum
3f3a: 8c b5 01                     sty     ea_checksum       ;save old checksum
3f3d: f0 16                        beq     :NoChange         ;Z-flag set if nothing changed
3f3f: 48                           pha                       ;push (old ^ new)
3f40: a9 3b                        lda     #$3b              ;output to $043b (near top left)
3f42: 85 91                        sta     out_ptr
3f44: a9 24                        lda     #$24              ;'4' (indicates failure in chip E5)
3f46: 20 85 38                     jsr     DrawChar
3f49: a9 00                        lda     #$00
3f4b: 20 85 38                     jsr     DrawChar          ;draw blank
3f4e: 68                           pla
3f4f: 20 9e 38                     jsr     Draw2Digits       ;draw checksum difference
3f52: 4c d6 3f                     jmp     :JmpTestMain      ;jump to test loop

                   ; Draw number of games played.
3f55: a9 04        :NoChange       lda     #$04              ;set output to $04e9 (lower middle of screen)
3f57: 85 92                        sta     out_ptr+1
3f59: a9 e9                        lda     #$e9
3f5b: 85 91                        sta     out_ptr
3f5d: 38                           sec                       ;suppress leading zeroes
3f5e: ad 8d 01                     lda     ea_games+2        ;draw number of games played
3f61: 20 9e 38                     jsr     Draw2Digits
3f64: ad 8c 01                     lda     ea_games+1
3f67: 20 9e 38                     jsr     Draw2Digits
3f6a: ad 8b 01                     lda     ea_games
3f6d: 18                           clc
3f6e: 20 9e 38                     jsr     Draw2Digits
3f71: a9 de                        lda     #<:str_plays      ;get pointer to " PLAYS"
3f73: 85 93                        sta     ]str_ptr
3f75: a9 3f                        lda     #>:str_plays
3f77: 85 94                        sta     ]str_ptr+1
3f79: 20 74 38                     jsr     DrawText          ;draw the string
                   ; Draw average game time.
3f7c: a9 05                        lda     #$05              ;set output to $0508
3f7e: 85 92                        sta     out_ptr+1
3f80: a9 08                        lda     #$08
3f82: 85 91                        sta     out_ptr
3f84: a5 8d                        lda     ]avg_game_time    ;get average game time, in ~4 sec units
3f86: 4a                           lsr     A                 ;divide by 16 to get minutes
3f87: 4a                           lsr     A
3f88: 4a                           lsr     A
3f89: 4a                           lsr     A
3f8a: f8                           sed                       ;enable decimal mode
3f8b: 18                           clc
3f8c: 69 00                        adc     #$00              ;"0 to 15 in decimal"
3f8e: d8                           cld                       ;disable decimal mode
3f8f: 38                           sec                       ;suppress leading zeroes
3f90: 20 9e 38                     jsr     Draw2Digits       ;draw minutes
3f93: a9 2e                        lda     #$2e              ;':'
3f95: 20 85 38                     jsr     DrawChar          ;draw colon
3f98: a5 8d                        lda     ]avg_game_time    ;get the time again
3f9a: 29 0f                        and     #$0f              ;strip off the minutes, leaving 4-seconds (0-15)
3f9c: f8                           sed                       ;enable decimal mode
3f9d: 18                           clc
3f9e: 69 00                        adc     #$00
3fa0: 85 8e                        sta     temp2+1           ;add it to itself (x2)
3fa2: 65 8e                        adc     temp2+1
3fa4: 85 8e                        sta     temp2+1           ;add it it itself (x4)
3fa6: 65 8e                        adc     temp2+1
3fa8: d8                           cld                       ;disable decimal mode
3fa9: c9 60                        cmp     #$60              ;did we hit 60 (BCD)?
3fab: 90 02                        bcc     :Not60            ;yes, branch
3fad: a9 59                        lda     #$59              ;no, trim to 59 (BCD)
3faf: 18           :Not60          clc
3fb0: 20 9e 38                     jsr     Draw2Digits       ;draw seconds
3fb3: a9 e4                        lda     #<:str_game_time  ;get pointer to " GAME TIME" string
3fb5: 85 93                        sta     ]str_ptr
3fb7: a9 3f                        lda     #>:str_game_time
3fb9: 85 94                        sta     ]str_ptr+1
3fbb: 20 74 38                     jsr     DrawText          ;draw string
                   ; 
                   ; Reset EAROM contents if P1 fire and P1/P2 start are all held for 8 frames
                   ; (which would set the debounce values to $00).
                   ; 
3fbe: a5 ea                        lda     test_st1_debounce ;player 1 start
3fc0: 05 eb                        ora     test_st2_debounce ;player 2 start
3fc2: 05 ec                        ora     test_fire1_debounce ;player 1 fire
3fc4: d0 10                        bne     :JmpTestMain
                   ; Reset EAROM contents.
3fc6: ad b5 01                     lda     ea_checksum       ;change checksum to remove data
3fc9: 49 ff                        eor     #$ff
3fcb: 8d b5 01                     sta     ea_checksum       ;write it into local copy of EAROM data
3fce: a9 3d                        lda     #61               ;checksum byte offset
3fd0: 85 f9                        sta     earom_cur_addr    ;set index
3fd2: a9 00                        lda     #$00              ;erase, write, read new checksum
3fd4: 85 fa                        sta     earom_cur_op
3fd6: 4c 9e 3d     :JmpTestMain    jmp     TestMain

                   ; 
                   ; Tile character values for bonus coins.
                   ; 
3fd9: 22 24 24 25+ :bonus_coins    .bulk   $22,$24,$24,$25,$23 ;2/4/4/5/3
                   ; 
                   ; Accounting messages.
                   ; 
3fde: 20 50 4c 41+ :str_plays      .dstr   ‘ PLAYS’
3fe4: 20 47 41 4d+ :str_game_time  .dstr   ‘ GAME TIME’
3fee: 48 41 52 c4  :str_hard       .dstr   ‘HARD’
3ff2: 45 41 53 d9  :str_easy       .dstr   ‘EASY’

                   ; 
                   ; DIE: handles NMI by looping until watchdog kills us.
                   ; 
3ff6: 4c f6 3f     HandleNMI       jmp     HandleNMI         ;twirl unto death

3ff9: 13                           .dd1    $13               ;(checksum byte)
                   ; 
                   ; 6502 hardware vectors.
                   ; 
3ffa: f6 3f                        .dd2    HandleNMI         ;NMI vector
3ffc: 4b 3b                        .dd2    HandleReset       ;reset vector
3ffe: c0 38                        .dd2    HandleIRQ         ;IRQ vector
                                   .adrend ↑ $2000

                   ; 
                   ; Graphics data.  This section is not addressable by the 6502.  The labels here
                   ; come from the original source code, where they were used to identify the
                   ; various pieces.
                   ; 
                   ; There are two types of data here: 8x8 playfield tiles, and 16x8 "motion
                   ; objects" (sprites).  There are 64 of each.  The bitmaps use 2 bits per pixel,
                   ; with color 0 representing background / transparency.
                   ; 
                   ; The bitmaps are rotated 90 degrees counter-clockwise from the layout you might
                   ; expect.  Instead of the first byte representing the top row, the first byte
                   ; represents the left column.  (This matches the display orientation.)
                   ; 
                   ; The memory layout divides the ROM into two bit planes, with the low bit of
                   ; each pixel in the first half, and the high bit in the second half.  The data
                   ; for an individual tile or motion object is localized, but odd-numbered and
                   ; even-numbered motion objects are separated in memory.
                   ; 
                   ; The source code (CENDE4.MAC) describes the motion object pictures like this:
                   ;   00-07: centipede heads
                   ;   08-0f: turning centipedes
                   ;    D5=1 for poisoned head
                   ;    D6=0 for head
                   ;    D7=1 for no object
                   ;   10: gun
                   ;   11: shot
                   ;   14-1b: pictures of spider
                   ;   1c-1f: ant (flea) pictures
                   ;   3a-3f: explosions
                   ; 
                   ; Pictures $2c-2f are for a grasshopper animation, unused by the game.
                   ; 
                   ; 
                   ; The first chunk of data is the lower color bit plane for the even-numbered
                   ; motion objects.
                   ; 
                   vis vis vis
                                   .addrs  NA
0000: 00 00 00 00+ HEAD0           .bulk   $00,$00,$00,$00,$3c,$18,$18,$ff,$7e,$3c,$18,$00,$00,$00,$00,$00
0010: 00 00 00 00+ HEAD2           .bulk   $00,$00,$00,$00,$3c,$18,$18,$7e,$7e,$bd,$18,$00,$00,$00,$00,$00
0020: 00 00 00 00+ HEAD4           .bulk   $00,$00,$00,$00,$3c,$18,$18,$ff,$7e,$3c,$18,$00,$00,$00,$00,$00
0030: 00 00 00 00+ HEAD6           .bulk   $00,$00,$00,$00,$3c,$99,$18,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00
0040: 00 00 00 00+ HEAD8           .bulk   $00,$00,$00,$00,$0a,$4c,$3e,$3e,$fe,$7c,$b8,$00,$00,$00,$00,$00
0050: 00 00 00 00+ HEADA           .bulk   $00,$00,$00,$00,$0a,$4c,$3e,$3e,$fe,$7c,$b8,$00,$00,$00,$00,$00
0060: 00 00 00 00+ HEADC           .bulk   $00,$00,$00,$00,$10,$18,$9c,$fe,$fe,$9c,$18,$10,$00,$00,$00,$00
0070: 00 00 00 00+ HEADE           .bulk   $00,$00,$00,$00,$10,$18,$9c,$fe,$fe,$9c,$18,$10,$00,$00,$00,$00

                   vis vis vis vis vis vis vis
vis vis 0080: 00 00 00 00+ GUN .bulk $00,$00,$00,$00,$18,$30,$f2,$ff,$f2,$30,$18,$00,$00,$00,$00,$00 0090: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 vis vis vis vis vis vis vis
vis vis 00a0: 44 22 11 22+ BUG0 .bulk $44,$22,$11,$22,$44,$68,$a0,$3c,$a0,$68,$44,$22,$11,$22,$44,$00 00b0: 90 48 24 24+ BUG2 .bulk $90,$48,$24,$24,$44,$68,$e0,$3c,$a0,$68,$44,$24,$24,$48,$90,$00 00c0: 90 48 24 24+ BUG4 .bulk $90,$48,$24,$24,$44,$68,$a0,$3c,$a0,$68,$44,$24,$24,$48,$90,$00 00d0: 10 88 44 33+ BUG6 .bulk $10,$88,$44,$33,$44,$68,$e0,$3c,$a0,$68,$44,$33,$44,$88,$10,$00 vis vis vis vis vis 00e0: 00 00 00 18+ ANT0 .bulk $00,$00,$00,$18,$18,$98,$7f,$8f,$7f,$1e,$7c,$b8,$00,$00,$00,$00 00f0: 00 00 00 18+ ANT2 .bulk $00,$00,$00,$18,$18,$18,$6f,$9f,$2f,$5e,$bc,$78,$00,$00,$00,$00 0100: 22 55 be 74+ PLAY0 .bulk $22,$55,$be,$74,$02,$61,$c2,$74,$36,$43,$e0,$40,$36,$7d,$ae,$44 0110: 14 94 42 f1+ PLAY2 .bulk $14,$94,$42,$f1,$42,$94,$34,$3c,$3c,$34,$94,$42,$f1,$42,$94,$14 0120: 00 24 4a 34+ PLAY4 .bulk $00,$24,$4a,$34,$34,$0c,$3e,$75,$6d,$2a,$1c,$34,$34,$4a,$24,$00 0130: 00 00 00 00+ PLAY6 .bulk $00,$00,$00,$00,$10,$00,$3c,$1c,$1c,$1e,$00,$04,$00,$00,$00,$00 0140: 00 00 00 00+ PLAY8 .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0150: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0160: 80 98 50 34+ GRASS0 .bulk $80,$98,$50,$34,$3c,$3c,$3c,$3f,$3e,$3c,$7c,$bc,$3c,$7c,$b8,$98 0170: 00 86 fc 05+ GRASS2 .bulk $00,$86,$fc,$05,$0f,$0f,$0f,$7f,$8f,$8f,$0f,$1f,$ef,$8f,$0e,$06 vis vis vis vis vis 0180: 01 06 0d 08+ SCORP0 .bulk $01,$06,$0d,$08,$08,$7c,$ff,$fc,$f8,$c8,$cd,$c6,$c1,$cc,$e4,$7c 0190: 01 06 0d 08+ SCORP2 .bulk $01,$06,$0d,$08,$7c,$ff,$fc,$c8,$cd,$c6,$e1,$60,$30,$18,$44,$3c 01a0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 01b0: 00 00 00 f8+ THREE .bulk $00,$00,$00,$f8,$88,$f8,$00,$f8,$88,$f8,$00,$f8,$a8,$a8,$00,$00 01c0: 00 00 00 f8+ SIX .bulk $00,$00,$00,$f8,$88,$f8,$00,$f8,$88,$f8,$00,$e8,$a8,$f8,$00,$00 01d0: 00 00 00 00+ EXPLD0 .bulk $00,$00,$00,$00,$08,$b1,$42,$81,$02,$00,$29,$50,$00,$00,$00,$00 01e0: 00 00 00 00+ EXPLD2 .bulk $00,$00,$00,$00,$59,$96,$01,$83,$82,$84,$ea,$31,$00,$00,$00,$00 01f0: 00 00 00 00+ EXPLD4 .bulk $00,$00,$00,$00,$7c,$fe,$7f,$3e,$7f,$fe,$7c,$7e,$00,$00,$00,$00 ; ; 8x8 playfield tile bitmaps. ; ; Start with the low bit, which is zero for most things because they're drawn in ; color 0/2. ; 0200: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;' ' 0208: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;'A' (color 2 for all alphanumericsc) 0210: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ; ... 0218: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0220: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0228: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0230: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0238: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0240: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0248: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0250: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0258: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0260: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0268: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0270: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0278: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0280: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0288: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0290: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0298: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02a0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02a8: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02b0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02b8: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02c0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02c8: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 02d0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;'Z' 02d8: 3c 42 81 81+ .bulk $3c,$42,$81,$81,$81,$81,$42,$3c ;Circle-C (colors 2 and 3) 02e0: 3c 42 81 81+ .bulk $3c,$42,$81,$81,$81,$81,$42,$3c ;Circle-P (colors 2 and 3) 02e8: 00 00 00 00+ .bulk $00,$00,$00,$00,$08,$00,$00,$00 ;dot for monitor test 02f0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;one-half 02f8: 18 30 f2 ff+ .bulk $18,$30,$f2,$ff,$f2,$30,$18,$00 ;gun (colors 3 and 2) 0300: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;'0' 0308: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ; ... 0310: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0318: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0320: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0328: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0330: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0338: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0340: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 0348: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;'9' 0350: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;color 0 blank 0358: ff ff ff ff+ .bulk $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff ;color 1 blank 0360: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;color 2 blank 0368: ff ff ff ff+ .bulk $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff ;color 3 blank 0370: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;':' 0378: 00 00 00 00+ .fill 72,$00 ;(unused) 03c0: 0c 06 0f 03+ .bulk $0c,$06,$0f,$03,$0f,$07,$0e,$0c ;1/4 poison mushroom 03c8: 1c 0e 3f 0f+ .bulk $1c,$0e,$3f,$0f,$1f,$0f,$1e,$0c ;1/2 poison mushroom 03d0: 1c 0e 3f 7f+ .bulk $1c,$0e,$3f,$7f,$3f,$3f,$1e,$1c ;3/4 poison mushroom 03d8: 1c 1e ff ff+ .bulk $1c,$1e,$ff,$ff,$ff,$ff,$1e,$1c ;full poison mushroom vis vis vis vis vis vis 03e0: 00 04 0e 02+ .bulk $00,$04,$0e,$02,$0e,$06,$0c,$00 ;1/4 mushroom 03e8: 00 0c 0e 0e+ .bulk $00,$0c,$0e,$0e,$0e,$0e,$0c,$00 ;1/2 mushroom 03f0: 00 0c 0e 6e+ .bulk $00,$0c,$0e,$6e,$2e,$0e,$0c,$00 ;3/4 mushroom 03f8: 00 0c 0e 6e+ .bulk $00,$0c,$0e,$6e,$6e,$0e,$0c,$00 ;full mushroom (colors 1 and 2) ; ; Odd-numbered motion objects. ; 0400: 00 00 00 00+ HEAD1 .bulk $00,$00,$00,$00,$3c,$18,$18,$7e,$ff,$3c,$18,$00,$00,$00,$00,$00 0410: 00 00 00 00+ HEAD3 .bulk $00,$00,$00,$00,$3c,$18,$18,$7e,$ff,$3c,$18,$00,$00,$00,$00,$00 0420: 00 00 00 00+ HEAD5 .bulk $00,$00,$00,$00,$3c,$18,$99,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0430: 00 00 00 00+ HEAD7 .bulk $00,$00,$00,$00,$3c,$18,$99,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0440: 00 00 00 00+ HEAD9 .bulk $00,$00,$00,$00,$08,$4d,$3e,$3e,$fe,$7c,$38,$40,$00,$00,$00,$00 0450: 00 00 00 00+ HEADB .bulk $00,$00,$00,$00,$08,$4d,$3e,$3e,$fe,$7c,$38,$40,$00,$00,$00,$00 0460: 00 00 00 00+ HEADD .bulk $00,$00,$00,$00,$04,$18,$9c,$fe,$fe,$9c,$18,$04,$00,$00,$00,$00 0470: 00 00 00 00+ HEADF .bulk $00,$00,$00,$00,$40,$18,$9c,$fe,$fe,$9c,$18,$40,$00,$00,$00,$00 0480: 00 00 00 00+ SHOT .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0490: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 04a0: 44 22 22 22+ BUG1 .bulk $44,$22,$22,$22,$44,$68,$a0,$3c,$e0,$68,$44,$22,$22,$22,$44,$00 04b0: 40 20 90 88+ BUG3 .bulk $40,$20,$90,$88,$68,$68,$e0,$3c,$e0,$68,$68,$88,$90,$20,$40,$00 04c0: 44 22 22 22+ BUG5 .bulk $44,$22,$22,$22,$44,$68,$a0,$3c,$e0,$68,$44,$22,$22,$22,$44,$00 04d0: 20 10 88 64+ BUG7 .bulk $20,$10,$88,$64,$47,$68,$e0,$3c,$e0,$68,$47,$64,$88,$10,$20,$00 04e0: 00 00 00 18+ ANT1 .bulk $00,$00,$00,$18,$18,$d8,$2f,$9f,$4f,$3e,$bc,$78,$00,$00,$00,$00 04f0: 00 00 00 18+ ANT3 .bulk $00,$00,$00,$18,$18,$18,$7f,$8f,$3f,$de,$3c,$f8,$00,$00,$00,$00 0500: 34 42 e9 42+ PLAY1 .bulk $34,$42,$e9,$42,$34,$1c,$36,$43,$e9,$42,$34,$34,$42,$e9,$42,$34 0510: 28 66 da 5d+ PLAY3 .bulk $28,$66,$da,$5d,$be,$4c,$7b,$36,$36,$7b,$4e,$be,$1d,$da,$66,$14 0520: 00 00 08 10+ PLAY5 .bulk $00,$00,$08,$10,$1c,$3e,$31,$74,$2e,$1c,$7c,$38,$08,$10,$00,$00 0530: 00 00 00 00+ PLAY7 .bulk $00,$00,$00,$00,$00,$00,$08,$30,$38,$00,$00,$00,$00,$00,$00,$00 0540: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0550: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0560: 00 8c 68 1a+ GRASS1 .bulk $00,$8c,$68,$1a,$1e,$1e,$1e,$1f,$7e,$9e,$9e,$1e,$fe,$9e,$9c,$8c 0570: 80 b0 a0 68+ GRASS3 .bulk $80,$b0,$a0,$68,$78,$78,$78,$7e,$7a,$7a,$fc,$78,$78,$f8,$f8,$b0 0580: 01 06 0d 18+ SCORP1 .bulk $01,$06,$0d,$18,$10,$7c,$ff,$fc,$d0,$d8,$cd,$c6,$e1,$7c,$04,$0c 0590: 01 06 0d 08+ SCORP3 .bulk $01,$06,$0d,$08,$7c,$ff,$fc,$c8,$cd,$c6,$c1,$cc,$d2,$c4,$cc,$78 05a0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 05b0: 00 00 00 f8+ NINE .bulk $00,$00,$00,$f8,$88,$f8,$00,$f8,$88,$f8,$00,$f8,$a8,$b8,$00,$00 05c0: 00 00 00 00+ EXPLD .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 05d0: 00 00 00 00+ EXPLD1 .bulk $00,$00,$00,$00,$14,$e5,$02,$a2,$85,$20,$63,$04,$00,$00,$00,$00 05e0: 00 00 00 00+ EXPLD3 .bulk $00,$00,$00,$00,$36,$dd,$cf,$67,$46,$ff,$6e,$a8,$00,$00,$00,$00 05f0: 00 00 00 00+ EXPLD5 .bulk $00,$00,$00,$00,$20,$7e,$fe,$6c,$3e,$7c,$78,$a0,$00,$00,$00,$00 0600: 00 00 00 00+ .align $0400 (512 bytes) ;(unused) ; ; Upper bit plane starts here. ; ; Start again with the even-numbered 8x16 motion object bitmaps. ; 0800: 00 00 00 00+ HEADS .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0810: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0820: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0830: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0840: 00 00 00 00+ .bulk $00,$00,$00,$00,$38,$7c,$fe,$fe,$fe,$7c,$38,$00,$00,$00,$00,$00 0850: 00 00 00 00+ .bulk $00,$00,$00,$00,$38,$7c,$fe,$fe,$fe,$7c,$38,$00,$00,$00,$00,$00 0860: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$78,$fc,$fe,$fe,$fc,$78,$00,$00,$00,$00,$00 0870: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$78,$fc,$fe,$fe,$fc,$78,$00,$00,$00,$00,$00 0880: 00 00 00 00+ GUNS .bulk $00,$00,$00,$00,$00,$0c,$0c,$00,$0c,$0c,$00,$00,$00,$00,$00,$00 0890: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 08a0: 00 00 00 00+ BUGS .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 08b0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 08c0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 08d0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 08e0: 00 00 00 00+ ANTS .bulk $00,$00,$00,$00,$04,$86,$70,$80,$70,$00,$40,$80,$00,$00,$00,$00 08f0: 00 00 00 00+ .bulk $00,$00,$00,$00,$04,$06,$60,$90,$20,$40,$80,$40,$00,$00,$00,$00 0900: 22 77 ea 7c+ PLAYS .bulk $22,$77,$ea,$7c,$3e,$7f,$be,$7c,$3e,$7f,$be,$7c,$1e,$7f,$ea,$44 0910: 5c ec 7e ff+ .bulk $5c,$ec,$7e,$ff,$7e,$ec,$5e,$3b,$3b,$5e,$ec,$7e,$ff,$7e,$ec,$5c 0920: 00 24 76 2c+ .bulk $00,$24,$76,$2c,$3c,$14,$2e,$5f,$73,$3e,$04,$3c,$2c,$76,$24,$00 0930: 00 00 00 00+ .bulk $00,$00,$00,$00,$18,$08,$2c,$12,$24,$1a,$01,$0c,$00,$00,$00,$00 0940: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0950: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0960: 00 18 1c 1c+ GRASS .bulk $00,$18,$1c,$1c,$0c,$3c,$3c,$18,$2c,$18,$34,$2c,$1c,$3c,$38,$18 0970: 00 06 07 07+ .bulk $00,$06,$07,$07,$0f,$0f,$0f,$04,$0d,$0b,$07,$0f,$0f,$0f,$0e,$06 0980: 00 00 00 00+ SCORPS .bulk $00,$00,$00,$00,$02,$03,$00,$03,$02,$00,$00,$00,$00,$00,$00,$00 0990: 00 00 00 02+ .bulk $00,$00,$00,$02,$03,$00,$03,$02,$00,$00,$00,$00,$00,$00,$00,$00 09a0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 09b0: 00 00 00 00+ NUMS .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 09c0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 09d0: 00 00 00 00+ EXPLS .bulk $00,$00,$00,$00,$4a,$00,$42,$01,$80,$02,$89,$52,$00,$00,$00,$00 09e0: 00 00 00 00+ .bulk $00,$00,$00,$00,$9d,$b4,$49,$a6,$c2,$a1,$6a,$45,$00,$00,$00,$00 09f0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$fe,$7f,$e7,$67,$fe,$7f,$2a,$00,$00,$00,$00 ; ; Playfield tiles. ; 0a00: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;' ' 0a08: f8 fc 26 22+ .bulk $f8,$fc,$26,$22,$26,$fc,$f8,$00 ;'A' 0a10: fe fe 92 92+ .bulk $fe,$fe,$92,$92,$92,$fe,$6c,$00 ; ... 0a18: 38 7c c6 82+ .bulk $38,$7c,$c6,$82,$82,$c6,$44,$00 0a20: fe fe 82 82+ .bulk $fe,$fe,$82,$82,$c6,$7c,$38,$00 0a28: fe fe 92 92+ .bulk $fe,$fe,$92,$92,$92,$82,$80,$00 0a30: fe fe 12 12+ .bulk $fe,$fe,$12,$12,$12,$12,$02,$00 0a38: 38 7c c6 82+ .bulk $38,$7c,$c6,$82,$92,$f2,$f2,$00 0a40: fe fe 10 10+ .bulk $fe,$fe,$10,$10,$10,$fe,$fe,$00 0a48: 82 82 fe fe+ .bulk $82,$82,$fe,$fe,$82,$82,$00,$00 0a50: 40 c0 80 80+ .bulk $40,$c0,$80,$80,$80,$fe,$7e,$00 0a58: fe fe 30 78+ .bulk $fe,$fe,$30,$78,$ec,$c6,$82,$00 0a60: fe fe 80 80+ .bulk $fe,$fe,$80,$80,$80,$80,$80,$00 0a68: fe fe 1c 38+ .bulk $fe,$fe,$1c,$38,$1c,$fe,$fe,$00 0a70: fe fe 1c 38+ .bulk $fe,$fe,$1c,$38,$70,$fe,$fe,$00 0a78: 7c fe 82 82+ .bulk $7c,$fe,$82,$82,$82,$fe,$7c,$00 0a80: fe fe 22 22+ .bulk $fe,$fe,$22,$22,$22,$3e,$1c,$00 0a88: 7c fe 82 a2+ .bulk $7c,$fe,$82,$a2,$e2,$7e,$bc,$00 0a90: fe fe 22 62+ .bulk $fe,$fe,$22,$62,$f2,$de,$8c,$00 0a98: 4c de 92 92+ .bulk $4c,$de,$92,$92,$96,$f4,$60,$00 0aa0: 02 02 fe fe+ .bulk $02,$02,$fe,$fe,$02,$02,$00,$00 0aa8: 7e fe 80 80+ .bulk $7e,$fe,$80,$80,$80,$fe,$7e,$00 0ab0: 1e 3e 70 e0+ .bulk $1e,$3e,$70,$e0,$70,$3e,$1e,$00 0ab8: fe fe 70 38+ .bulk $fe,$fe,$70,$38,$70,$fe,$fe,$00 0ac0: c6 ee 7c 38+ .bulk $c6,$ee,$7c,$38,$7c,$ee,$c6,$00 0ac8: 0e 1e f0 f0+ .bulk $0e,$1e,$f0,$f0,$1e,$0e,$00,$00 0ad0: c2 e2 f2 ba+ .bulk $c2,$e2,$f2,$ba,$9e,$8e,$86,$00 ;'Z' 0ad8: 3c 42 bd c3+ .bulk $3c,$42,$bd,$c3,$c3,$a5,$42,$3c ;Circle-C 0ae0: 3c 42 fd 93+ .bulk $3c,$42,$fd,$93,$93,$8d,$42,$3c ;Circle-P 0ae8: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;dot 0af0: 2e 10 08 94+ .bulk $2e,$10,$08,$94,$ca,$a8,$90,$00 ;one-half 0af8: 18 3c fe ff+ .bulk $18,$3c,$fe,$ff,$fe,$3c,$18,$00 ;gun 0b00: 38 7c c2 82+ .bulk $38,$7c,$c2,$82,$86,$7c,$38,$00 ;'0' 0b08: 80 84 fe fe+ .bulk $80,$84,$fe,$fe,$80,$80,$00,$00 ; ... 0b10: c4 e6 f2 b2+ .bulk $c4,$e6,$f2,$b2,$ba,$9e,$8c,$00 0b18: 40 c2 92 9a+ .bulk $40,$c2,$92,$9a,$9e,$f6,$62,$00 0b20: 30 38 2c 26+ .bulk $30,$38,$2c,$26,$fe,$fe,$20,$00 0b28: 4e ce 8a 8a+ .bulk $4e,$ce,$8a,$8a,$8a,$fa,$70,$00 0b30: 78 fc 96 92+ .bulk $78,$fc,$96,$92,$92,$f2,$60,$00 0b38: 06 06 e2 f2+ .bulk $06,$06,$e2,$f2,$1a,$0e,$06,$00 0b40: 6c 9e 9a b2+ .bulk $6c,$9e,$9a,$b2,$b2,$ec,$60,$00 0b48: 0c 9e 92 92+ .bulk $0c,$9e,$92,$92,$d2,$7e,$3c,$00 ;'9' 0b50: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;color 0 blank 0b58: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00 ;color 1 blank 0b60: ff ff ff ff+ .bulk $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff ;color 2 blank 0b68: ff ff ff ff+ .bulk $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff ;color 3 blank 0b70: 00 00 00 66+ .bulk $00,$00,$00,$66,$66,$00,$00,$00 ;':' 0b78: 00 00 00 00+ .fill 72,$00 ;(unused) 0bc0: 00 04 0e 02+ .bulk $00,$04,$0e,$02,$0e,$06,$0c,$00 ;1/4 poison mushroom (colors 1 and 3) 0bc8: 00 0c 0e 0e+ .bulk $00,$0c,$0e,$0e,$0e,$0e,$0c,$00 ;1/2 poison mushroom 0bd0: 00 0c 0e 6e+ .bulk $00,$0c,$0e,$6e,$2e,$0e,$0c,$00 ;3/4 poison mushroom 0bd8: 00 0c 0e 6e+ .bulk $00,$0c,$0e,$6e,$6e,$0e,$0c,$00 ;full poison mushroom 0be0: 0c 02 01 01+ .bulk $0c,$02,$01,$01,$01,$01,$02,$0c ;1/4 mushroom 0be8: 1c 02 31 01+ .bulk $1c,$02,$31,$01,$11,$01,$12,$0c ;1/2 mushroom 0bf0: 1c 02 31 11+ .bulk $1c,$02,$31,$11,$11,$31,$12,$1c ;3/4 mushroom 0bf8: 1c 12 f1 91+ .bulk $1c,$12,$f1,$91,$91,$f1,$12,$1c ;full mushroom ; ; Odd-numbered motion objects. ; 0c00: 00 00 00 00+ HEADS_H .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0c10: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0c20: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0c30: 00 00 00 00+ .bulk $00,$00,$00,$00,$3c,$7e,$7e,$7e,$7e,$3c,$18,$00,$00,$00,$00,$00 0c40: 00 00 00 00+ .bulk $00,$00,$00,$00,$38,$7c,$fe,$fe,$fe,$7c,$38,$00,$00,$00,$00,$00 0c50: 00 00 00 00+ .bulk $00,$00,$00,$00,$38,$7c,$fe,$fe,$fe,$7c,$38,$00,$00,$00,$00,$00 0c60: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$78,$fc,$fe,$fe,$fc,$78,$00,$00,$00,$00,$00 0c70: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$78,$fc,$fe,$fe,$fc,$78,$00,$00,$00,$00,$00 0c80: 00 00 00 00+ SHOTS_H .bulk $00,$00,$00,$00,$00,$00,$00,$fc,$00,$00,$00,$00,$00,$00,$00,$00 0c90: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0ca0: 00 00 00 00+ BUGS_H .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 0cb0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 0cc0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 0cd0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$70,$f8,$fc,$f8,$70,$00,$00,$00,$00,$00,$00 0ce0: 00 00 00 00+ ANTS_H .bulk $00,$00,$00,$00,$04,$c6,$20,$90,$40,$20,$80,$40,$00,$00,$00,$00 0cf0: 00 00 00 00+ .bulk $00,$00,$00,$00,$04,$06,$70,$80,$30,$c0,$00,$c0,$00,$00,$00,$00 0d00: 3c 7e f7 7e+ PLAYS_H .bulk $3c,$7e,$f7,$7e,$3c,$1c,$3e,$7f,$f7,$7e,$3c,$3c,$7e,$f7,$7e,$3c 0d10: 28 7e fe 37+ .bulk $28,$7e,$fe,$37,$ea,$74,$5f,$6e,$6e,$5f,$74,$ea,$37,$fe,$7e,$14 0d20: 00 00 18 18+ .bulk $00,$00,$18,$18,$34,$52,$34,$4a,$52,$2c,$4a,$2c,$18,$18,$00,$00 0d30: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$10,$38,$30,$28,$10,$00,$00,$00,$00,$00,$00 0d40: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0d50: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0d60: 00 0c 0e 06+ GRASS_H .bulk $00,$0c,$0e,$06,$1e,$1e,$1e,$08,$0c,$1a,$16,$0e,$1e,$1e,$1c,$0c 0d70: 00 30 38 38+ .bulk $00,$30,$38,$38,$58,$78,$58,$50,$58,$38,$78,$70,$68,$18,$78,$30 0d80: 00 00 00 00+ SCORP_H .bulk $00,$00,$00,$00,$02,$03,$00,$03,$02,$00,$00,$00,$00,$00,$00,$00 0d90: 00 00 00 02+ .bulk $00,$00,$00,$02,$03,$00,$03,$02,$00,$00,$00,$00,$00,$00,$00,$00 0da0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0db0: 00 00 00 00+ NUMS_H .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0dc0: 00 00 00 00+ .bulk $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 0dd0: 00 00 00 00+ EXPLS_H .bulk $00,$00,$00,$00,$54,$cb,$50,$a2,$05,$b2,$4b,$10,$00,$00,$00,$00 0de0: 00 00 00 00+ .bulk $00,$00,$00,$00,$7e,$7d,$e6,$63,$67,$fb,$6e,$2a,$00,$00,$00,$00 0df0: 00 00 00 00+ .bulk $00,$00,$00,$00,$18,$bc,$7f,$ef,$7e,$fd,$14,$a0,$00,$00,$00,$00 0e00: 00 00 00 00+ .align $0400 (512 bytes) ;(unused) .adrend ↑ NA

Symbol Table

LabelValue
ENTRY$2000
HandleIRQ$38c0
HandleReset$3b4b