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