Atari VCS 2600 River Raid Labeled Assembler Source Code

You might also like

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 65

; *** R I V E R R A I D ***

; Copyright 1982 Activision


; Designer: Carol Shaw

; Analyzed, labeled and commented


; by Thomas Jentzsch (JTZ)
; Last Update: 13.08.2001 (v0.9)

; Section generation:
; The river is divided into sections which are generated by random. The random
; number generator can generate 57337 different sections. Each section is
; divided into 16 blocks. The last block is the bridge. For each other block a
; random Id is generated, which defines the shape of the river. The river is
; randomly generated with or without islands.
; Each block is 32 lines high and is divided into two parts. Those parts are
; neccessary for the bridge only, because it is smaller than a whole block.
;
; All objects are also randomly generated. There is one object in each block.
; First the game randomly selects if a fuel tank, an enemy object (ship, plane
; or helicopter) or a house should be generated. Then a starting x-position is
; defined and finally the direction (left/right) of the object is set.
; The ships and helicopters start patroling also randomly.
;
; Kernel:
; The main display is 160 lines tall. Below is the state display (score, fuel,
; lives and copyright).
; The kernel displays five or six of the section blocks. When a block leaves
; the display, it is replaced on the fly by a new generated one.
; It's basically a two line kernel, which is repeated twelve times. After that,
; there is code for eight more lines, where the block is iterated and all new
; parameter are set. (12 * 2 + 8 = 32!)
; The parameters for each block are:
; - a pointer to the current playfield pattern
; - a flag for a bright or dark green playfield
; - two pointers for the current object, the data is displayed interlaced,
; this gives in a single line resolution here
; - a color pointer for the object
; - x-positioning values for the object
; - some flags for the object (size, reflection...)
; All collisons are checked for each displayed block and only the hardware
; collision registers are used for that (no software calculations).

; Misc:
; - There are no score variables, the score is stored and changed directly in
; the display pointers!
; - The are no variables to store the x-positions of the objects, all
; calculations are done directly with the position values.
; (There aren't any BCD calculations in this code!)
; - All variables for the players are swapped, when the players change.
; - The game speeds aren't adjusted for PAL, the PAL game run's about 16% slower.
; (This seems to be true for most PAL conversions.)
; - ...

processor 6502
include vcs.h

;===============================================================================
; A S S E M B L E R - S W I T C H E S
;===============================================================================

FILL_OPT = 1 ; fill optimized bytes with NOPs


SCREENSAVER = 1 ; compile with screensaver code
TRAINER = 0 ; enable training mode
NTSC = 1 ; compile for NTSC

;===============================================================================
; C O N S T A N T S
;===============================================================================

; initial values for the random number generator:


SEED_LO = $14 ; change "to go, where no one has gone before" :)
SEED_HI = $A8

; color constants:
BLACK = $00
GREY = $06
ORANGE = $2A
IF NTSC
YELLOW = $1C
RED = $48
BLUE = $84
CYAN = $B0
GREEN = $D2
ELSE
YELLOW = $2C
RED = $68
BLUE = $B4
CYAN = $70
GREEN = $52
ENDIF
DARK_RED = RED - $6
LIGHT_GREEN = GREEN + $8
BROWN = YELLOW - $C
LIGHT_GREY = GREY + $6
DARK_BLUE = BLUE - $4

; main game constants:


NUM_BLOCKS = 6 ; max. number of block on screen
SECTION_BLOCKS = 16 ; number of blocks/stage
BLOCK_PARTS = 2 ; each block has two parts
BLOCK_SIZE = 32 ; number of lines/block
NUM_LINES = 160 ; number of lines in main kernel
MAX_LEVEL = 48 ; number of difficulty levels

DIGIT_H = 8 ; height of the score digits


JET_Y = 19 ; fixed y-position for jet
MIN_MISSILE = JET_Y-6 ; starting position of player missile
MAX_MISSILE = NUM_LINES+1
MISSILE_SPEED = 6 ; y-speed of the jet missile
ROAD_HEIGHT = 13 ; number of lines for road
INTRO_SCROLL = 48 ; counter for scrolling into new game

SWITCH_PAGE_ID = 9 ; first pattern id with data on different page


; constants for shape-ids:
ID_EXPLOSION0 = 0 ; used for explosion end
ID_EXPLOSION1 = 1
ID_EXPLOSION2 = 2
ID_EXPLOSION3 = 3
ID_PLANE = 4
ID_HELI0 = 5
ID_HELI1 = 6
ID_SHIP = 7
ID_BRIDGE = 8
ID_HOUSE = 9
ID_FUEL = 10

; flags for blockLst:


PF1_PAGE_FLAG = %00000001 ; pattern for PF1 in page $FC or $FD
PF2_PAGE_FLAG = %00000010 ; pattern for PF1 in page $FC or $FD
PF_COLOR_FLAG = %00000100 ; bright or dark green PF
PATROL_FLAG = %00010000 ; enemy is patroling (change directions)
PF_COLLIDE_FLAG = %00100000 ; enemy collided with playfield
ENEMY_MOVE_FLAG = %01000000 ; enemy is moving
PF_ROAD_FLAG = %10000000 ; display road and bridge

; flags for State1Lst:


DIRECTION_FLAG = %00001000 ; move direction of object
FINE_MASK = %11110000 ; mask bits for HMxy
NUSIZ_MASK = %00000111 ; mask bits for NUSIx

; flags for PF_State:


ISLAND_FLAG = %10000000 ; island displayed in block
CHANGE_FLAG = %01000000 ; begin or end of island (JTZ: this interpretation
might be wrong)

; joystick bits:
MOVE_RIGHT = %00001000
MOVE_LEFT = %00000100
MOVE_DOWN = %00000010
MOVE_UP = %00000001

; values for ENAxy:


DISABLE = %00
ENABLE = %10 ; value for enabling a missile

; values for NUSIZx:


TWO_COPIES = %001
THREE_COPIES = %011
DOUBLE_SIZE = %101
QUAD_SIZE = %111

; mask for SWCHB:


BW_MASK = %1000 ; black and white bit

;===============================================================================
; Z P - V A R I A B L E S
;===============================================================================

gameVariation = $80 ; one or two player game


gameDelay = $81 ; delay before gameVariation changes
frameCnt = $82 ; simple frame counter
random = $83 ; 8 bit random number (used for:
start of ship and helicopter, sound)
joystick = $84 ; saved joystick value (?000rldu)
IF SCREENSAVER
SS_XOR = $85 ; change colors in screensaver mode
(0/$01..$ff)
SS_Mask = $86 ; darker colors in screensaver mode
($ff/$f7)
ENDIF
dXSpeed = $87 ; x-acceleration
prevPF1PatId = $88 ; playfield pattern Id of the
previous block
PF_State = $89 ; io000000
sectionEnd = $8A ; 0 = end of section
blockOffset = $8B ; offset into first displayed block
posYLo = $8C ; low value of blockOffset
bridgeExplode = $8D ; counter for bridge explosion

; the next 36 bytes are used to save all variables for six blocks:
;---------------------------------------
blockLst = $8E ; ..$93 flags for block definition
blockLstEnd = blockLst+NUM_BLOCKS-1
;---------------------------------------
XPos1Lst = $94 ; ..$99 coarse value for x-positioning of
object
XPos1LstEnd = XPos1Lst+NUM_BLOCKS-1
;---------------------------------------
State1Lst = $9A ; ..$9F bit 0..2 = NUSIZ1, bit 3 = REFP1,
4..7 = fine move
State1LstEnd = State1Lst+NUM_BLOCKS-1
;---------------------------------------
Shape1IdLst = $A0 ;.. $A5 ids for object
Shape1IdLstEnd = Shape1IdLst+NUM_BLOCKS-1
;---------------------------------------
PF1Lst = $A6 ; ..$AB low pointer for PF1 data
PF1LstEnd = PF1Lst+NUM_BLOCKS-1
;---------------------------------------
PF2Lst = $AC ; ..$B1 low pointer for PF2 data
PF2LstEnd = PF2Lst+NUM_BLOCKS-1
;---------------------------------------
; end of block variables
missileY = $B2 ; y-position of player missile
playerX = $B3 ; x-position of player jet
speedX = $B4 ; x-speed of player jet
speedY = $B5 ; y-speed of play jet
blockPart = $B6 ; 1/2 (used for bridge)
fuelHi = $B7 ; high value of fuel (displayed)
fuelLo = $B8 ; low value of fuel
sectionBlock = $B9 ; number of block in current section
(16..1)
shapePtr0 = $BA ; ..$BB pointer to the shape for the
player jet
PF1PatId = $BC ; playfield pattern Id for the new
generated block
;---------------------------------------
player1State = $BD ; ..$C1
level = player1State ; difficulty level for current
player (1..48)
randomLoSave = player1State+1; saved random generator values for
begin of level
randomHiSave = player1State+2;
livesPtr = player1State+3; ..$C1
;---------------------------------------
player2State = $C2 ; ..$C5
livesPtr2 = player2State+3; the high pointer is not saved
here, because it's const
;---------------------------------------
gameMode = $C6 ; 0 = running; -1 = game over; 1..48
= scroll into game
shapePtr1a = $C7 ; ..$C8
shapePtr1b = $C9 ; ..$CA
colorPtr = $CB ; ..$CC
scorePtr1 = $CD ; ..$D8 12 bytes for the score display of
current player
PF1Ptr = $D9 ; ..$DA
PF2Ptr = $DB ; ..$DC
;---------------------------------------
scorePtr2 = $DD ; ..$E7 12 bytes for the score display of
other player
; the constant hi-pointers are temporary used:
blockNum = scorePtr2+1 ; current block in kernel
reflect0 = scorePtr2+3 ; flag for GRP0 (player jet)
reflection
hitEnemyIdx = scorePtr2+5 ; index of enemy that was hit by
missile
PFCrashFlag = scorePtr2+7 ; jet crashed into playfield
missileFlag = scorePtr2+9 ; $ff means: missile enabled
;---------------------------------------
collidedEnemy = $E8 ; jet collided with enemy (id)
randomLo = $E9 ; current number generator values
randomHi = $EA
randomLoSave2 = $EB ; saved number generator values for
current player
randomHiSave2 = $EC
temp2 = $ED ;
roadBlock = temp2 ; bit 7 = 1: road in block
PFcolor = $EE ; color of river banks
valleyWidth = PFcolor ; define minimum width of valley in
first levels (6/0)
playerColor = $EF ; YELLOW/BLACK
stateBKColor = $F0 ; GREY (const!)
statePFColor = $F1 ; YELLOW+2 (const!)
temp = $F2 ; main temporary variable
diffPF = temp ; difference between to PF pattern
ids
zero1 = $f3 ; always zero!
player = $F4 ; 0/1
missileX = $F5 ; x-position of player missile
zero2 = $F6 ; always zero!
IF SCREENSAVER
SS_Delay = $F7 ; screensaver delay
ENDIF
sound0Id = $F8 ;
sound0Cnt = $F9
bridgeSound = $FA ; bridge is exploding
missileSound = $FB ; missile fired
temp3 = $FC
blockLine = temp3 ; current displayed line of block in
kernel
maxId = temp3
lineNum = $FD ; counter for kernel lines

;===============================================================================
; M A C R O S
;===============================================================================

MAC FILL_NOP
IF FILL_OPT
REPEAT {1}
NOP
REPEND
ENDIF
ENDM

;===============================================================================
; R O M - C O D E (Part 1)
;===============================================================================

ORG $F000

START:
SEI ; 2
CLD ; 2
LDX #0 ; 2
Reset:
LDA #0 ; 2
.loopClear:
STA $00,X ; 4
TXS ; 2
INX ; 2
BNE .loopClear ; 2
JSR SetScorePtrs ; 6
LDA #>Zero ; 2
LDX #12-1 ; 2
JSR SetScorePtr1 ; 6 set high-pointers to $FB
LDX #colorPtr+1-PF1Lst; 2 #38
JSR GameInit ; 6
LDA random ; 3
BNE MainLoop ; 2
INC random ; 5
STA livesPtr ; 3 = 0!
LDA #<One ; 2
STA scorePtr1+10 ; 3

MainLoop:
LDX #4 ; 2 offset ball
LDA fuelHi ; 3
LSR ; 2
LSR ; 2
LSR ; 2
CLC ; 2
ADC #69 ; 2
JSR SetPosX ; 6 position ball for fuel display

; *** prepare everything for the main kernel: ***


; set all color registers (and NUSIZ1 = 0)
INX ; 2 x = 5!
.loopSetColors:
LDA ColorTab,X ; 4
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
STA PFcolor,X ; 4
STA NUSIZ1,X ; 4
DEX ; 2
BPL .loopSetColors ; 2
TAY ; 2 y = 0!
LDA scorePtr1+10 ; 3
CMP #<Two ; 2
BEQ .skipTwo ; 2
LDA SWCHB ; 4
LSR ; 2 reset pressed?
BCC .skipTwo ; 2 yes, skip player 2
LDA player ; 3 current player = 2?
BEQ .skipTwo ; 2 no, skip
STY playerColor ; 3 yes, set..
STY COLUP0 ; 3 ..and player 2 color = 0
.skipTwo:

; flicker background when bridge explodes:


LDA bridgeExplode ; 3
BEQ .skipExplosion ; 2
DEC bridgeExplode ; 5
LSR ; 2
BCC .skipExplosion ; 2
LDA #DARK_RED ; 2 flicker background red
IF SCREENSAVER
AND SS_Mask ; 3
ELSE
FILL_NOP 2
ENDIF
STA COLUBK ; 3
.skipExplosion:

INX ; 2 x = 0!
STX temp ; 3
STX NUSIZ0 ; 3
LDY playerX ; 3
LDA reflect0 ; 3
STA REFP0 ; 3
BEQ .noReflect ; 2
INY ; 2 adjust x-pos
.noReflect:
TYA ; 2
JSR SetPosX ; 6 x-position player jet
INX ; 2
STX CTRLPF ; 3 reflect playfield

STX VDELP1 ; 3 enable vertical delay for player 1

; set size, reflect and postion for top enemy object;


LDY XPos1Lst +NUM_BLOCKS-1; 3
LDA State1Lst+NUM_BLOCKS-1; 3
STA NUSIZ1 ; 3
STA REFP1 ; 3
JSR SetPosX2 ; 6 position top enemy object

; x-position missile:
INX ; 2
LDA missileX ; 3
JSR SetPosX ; 6 position missile

JSR DoHMove ; 6
STY PF0 ; 3 enable complete PF0 (y=$ff)

; clear collsion variables:


STY hitEnemyIdx ; 3
STY PFCrashFlag ; 3
STY missileFlag ; 3
STY collidedEnemy ; 3

; set variables for top block:


LDX #NUM_BLOCKS-1 ; 2
JSR SetPFxPtr ; 6
LDA blockOffset ; 3
CMP #3 ; 2 top block just started?
BCS .skipDex ; 2 no, skip
DEX ; 2 yes, start at block 4
.skipDex:
STX blockNum ; 3
LDY Shape1IdLst,X ; 4
LDX shapePtr1aTab,Y ; 4
STX shapePtr1a ; 3
LDX shapePtr1bTab,Y ; 4
STX shapePtr1b ; 3
LDX ColorPtrTab,Y ; 4
STX colorPtr ; 3
STA CXCLR ; 3 clear all collison registers
STA HMCLR ; 3
; calculate offset into first block:
TAX ; 2
SEC ; 2
SBC #1 ; 2
AND #$1F ; 2
STA blockLine ; 3
LSR blockLine ; 5 0..15
CMP #26 ; 2
BCC lowOffset ; 2
SBC #22 ; 2
BNE endOffset ; 2

lowOffset:
CMP #4 ; 2
BCC endOffset ; 2
AND #%01 ; 2
ORA #%10 ; 2
endOffset:

; set entrypoint into kernel:


TAY ; 2
LDA JmpHiTab,Y ; 4
PHA ; 3
LDA JmpLoTab,Y ; 4
PHA ; 3

; prepare graphics for first line of kernel:


TXA ; 2
LSR ; 2
TAY ; 2
LDA (shapePtr1a),Y ; 5
BCC .evenLine ; 2 even blockOffset!
LDA (shapePtr1b),Y ; 5
.evenLine:
CPX #26 ; 2 blockoffset >= 26?
BCS .noShape ; 2 yes, skip enemy shape
CPX #3 ; 2 blockoffset < 3?
BCC .noShape ; 2 yes, skip enemy shape
STA GRP1 ; 3 no, display enemy shape in first
row
LDA #0 ; 2
STA GRP0 ; 3 VDELP1!
.noShape:
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3
LDA (colorPtr),Y ; 5
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
STA COLUP1 ; 3
LDX blockNum ; 3
LDA blockLst,X ; 4
STA roadBlock ; 3 save road-state
AND #PF_COLOR_FLAG ; 2
ORA #GREEN ; 2
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
STA PFcolor ; 3
BIT blockLstEnd ; 3 road in first block?
BPL .noRoad ; 2 no, use green color
CPY #ROAD_HEIGHT ; 2 offset inside road?
BCS .noRoad ; 2 no, use green color
LDA RoadColorTab,Y ; 4 yes, use road colors
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
.noRoad:
STA COLUPF ; 3
LDY #NUM_LINES ; 2
STY lineNum ; 3
.waitTim:
LDA INTIM ; 4
BNE .waitTim ; 2
STA WSYNC ; 3
STA HMOVE ; 3
STA VBLANK ; 3
RTS ; 6 jump into kernel!

; *** main display kernel: ***


DisplayKernel SUBROUTINE

; first some external code to save cycles in the kernel:


JmpPoint2: ;12
INC lineNum ; 5
LDY blockLine ; 3
BPL enterKernel2 ; 3

.skipJet0:
LDX zero2 ; 3 load 0 with exactly 3 cylces
BEQ .contJet0 ; 3

.noRoad:
LDA PFcolor ; 3
JMP .contPFColor ; 3

.doJet0a:
LDA (shapePtr0),Y ; 5
TAX ; 2
LDA #$00 ; 2
.loopKernel1: ; @19
BEQ .contJet0a ; 2 this jump is taken when comming
from .doJet0a
BNE .contKernel1 ; 3 this jump is taken when comming
from .loopkernel1

IF SCREENSAVER = 0
FILL_NOP 1
ENDIF

JmpPoint3: ;12
JSR Wait12 ;12
.contKernel1:
NOP ; 2 @26
;--------------------------------------
; even line:
; - ...
; - draw player jet
; - load new P1 shape
; *** here starts the main kernel loop: ***
.loopKernel: ;
CPY #JET_Y ; 2 draw player jet?
BCS .skipJet0 ; 2 no, skip
LDA (shapePtr0),Y ; 5 yes, load data..
TAX ; 2 ..into x
.contJet0:
LDY blockLine ; 3
BIT roadBlock ; 3 road displayed?
BPL .noRoad ; 2 no, normal PF color
LDA RoadColorTab,Y ; 4 yes, load road colors
IF SCREENSAVER
EOR SS_XOR ; 3
.contPFColor:
AND SS_Mask ; 3
ELSE
FILL_NOP 2
.contPFColor:
FILL_NOP 1
ENDIF
STA.w temp ; 4
LDA (shapePtr1b),Y ; 5
STA GRP1 ; 3 time doesn't matter (VDELP1!)
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3 @75
;--------------------------------------
; new line starts here!
; odd line:
; - set PF color
; - set P1 color
; - change PF
STA HMOVE ; 3
STX GRP0 ; 3 @2 this also updates GRP1
LDA temp ; 3
STA COLUPF ; 3 @8
LDA (colorPtr),Y ; 5
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
STA COLUP1 ; 3 @22
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3 @30
enterKernel2:
LDA (shapePtr1a),Y ; 5
STA GRP1 ; 3 time doesn't matter (VDELP1!)
LDY lineNum ; 3
DEY ; 2
BEQ .exitKernel2 ; 2
CPY #JET_Y ; 2
BCC .doJet0a ; 2
TYA ; 2
SBC missileY ; 3
AND #$F8 ; 2
BNE .skipEnable0 ; 2
LDA #ENABLE ; 2
.skipEnable0:
LDX #$00 ; 2
.contJet0a:
DEY ; 2
STY lineNum ; 3
STA WSYNC ; 3
;--------------------------------------
; even line:
; - en-/disable missile
; - update P0 and P1 graphics
; - decrease block-line
; - ...
STA HMOVE ; 3
STA ENAM0 ; 3
STX GRP0 ; 3 @6 this also updates GRP1
BEQ .exitKernel2 ; 2
DEC blockLine ; 5
BNE .loopKernel1 ; 2

;*** start of next block (requires eight extra kernel lines): ***
; new block, line 1
; - dec block-number
; - set new road-state
; - get new PF color
; - set new shape-pointer 1a
DEC blockNum ; 5
JmpPoint1:
LDX blockNum ; 3
BMI LF202 ; 2
LDA blockLst,X ; 4 save road-state
STA roadBlock ; 3
AND #PF_COLOR_FLAG ; 2 bright or dark..
ORA #GREEN ; 2 ..green
IF SCREENSAVER
EOR SS_XOR ; 3
AND SS_Mask ; 3
ELSE
FILL_NOP 4
ENDIF
STA PFcolor ; 3
LDA Shape1IdLst,X ; 4 set
TAX ; 2 shape-pointer
LDA shapePtr1aTab,X ; 4 for the
STA shapePtr1a ; 3 next enemy
LF1CE:
LDA #$00 ; 2
STA GRP1 ; 3
CPY #JET_Y ; 2
STA WSYNC ; 3
;--------------------------------------
; new block, line 2
; x = shape-id
; - set jet
; - set PF
; - set new shape-pointer 1b
; - set new color-pointer
STA HMOVE ; 3
BCS .skipJet1 ; 2
LDA (shapePtr0),Y ; 5
.skipJet1:
STA GRP0 ; 3
LDY #0 ; 2 display last line of playfield
pattern
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3
LDY lineNum ; 3
DEY ; 2
.exitKernel2:
BEQ .exitKernel1 ; 2
LDA shapePtr1bTab,X ; 4
STA shapePtr1b ; 3
LDA ColorPtrTab,X ; 4
STA colorPtr ; 3
JmpPoint0:
CPY #JET_Y ; 2
BCS .skipJet2 ; 2
LDA (shapePtr0),Y ; 5
TAX ; 2
LDA #DISABLE ; 2
BEQ .contJet2 ; 3

LF202: INX ; 2
BEQ LF1CE ; 2
JmpPoint9:
NOP ; 2
SEC ; 2
BCS .enterKernel9 ; 3

.skipJet2:
TYA ; 2
SBC missileY ; 3
AND #$F8 ; 2
BNE .skipEnable1 ; 2
LDA #ENABLE ; 2
.skipEnable1:
LDX #$00 ; 2
.contJet2:
STA WSYNC ; 3
;--------------------------------------
; new block, line 3
; - en-/disabvle missile
; - set jet
; - set new PF pointers
STA HMOVE ; 3
STA ENAM0 ; 3
STX GRP0 ; 3
DEY ; 2
STY lineNum ; 3
.exitKernel1:
BEQ .exitKernel ; 2
LDX blockNum ; 3
.enterKernel9:
JSR SetPFxPtr ;50
LDA PFcolor ; 3
CPY #JET_Y ; 2
NOP ; 2 @76
;--------------------------------------
; new block, line 4
; - set new PF color
; - set PF
; - load fine movement
STA HMOVE ; 3
STA COLUPF ; 3
BCS .skipJet3 ; 2
LDA (shapePtr0),Y ; 5
STA GRP0 ; 3
.skipJet3:
LDY #SECTION_BLOCKS-1 ; 2
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3
DEC lineNum ; 5
BEQ .exitKernel ; 2
JmpPoint8:
LDA State1Lst,X ; 4 put fine move-value
STA temp ; 3 into temp
LDY lineNum ; 3
CPY #JET_Y ; 2
BCC .skipJet4 ; 2
TYA ; 2
SBC missileY ; 3
AND #$F8 ; 2
BNE .skipEnable2 ; 2
LDA #ENABLE ; 2
.skipEnable2:
LDY #0 ; 2
.contJet4:
STA WSYNC ; 3
;--------------------------------------
; new block, line 5
; - en-/disable missile
; - set jet
; - position new shape
STA HMOVE ; 3
STA ENAM0 ; 3
STY GRP0 ; 3
; position player 1:
LDA XPos1Lst,X ; 4 load coarse move-value
BEQ .posVeryLeft ; 2
TAX ; 2
CPX #7 ; 2
BCS .posRight ; 2
.waitLeft:
DEX ; 2
BNE .waitLeft ; 2
STA RESP1 ; 3
.contLeft:
DEC lineNum ; 5
LDY lineNum ; 3
BNE .contPos ; 2
.exitKernel:
JMP DisplayState ; 3 exit the kernel

.posVeryLeft:
NOP ; 2
NOP ; 2
LDA #$60 ; 2
STA RESP1 ; 3
STA HMP1 ; 3
BNE .contLeft ; 2

.skipJet4:
LDA (shapePtr0),Y ; 5
TAY ; 2
LDA #$00 ; 2
BEQ .contJet4 ; 2

.posRight:
SBC #4 ; 2
TAX ; 2
DEC lineNum ; 5
LDY lineNum ; 3
BEQ .exitKernel ; 2
.waitRight:
DEX ; 2
BPL .waitRight ; 2
STA RESP1 ; 3
JmpPoint7:
.contPos:
STA WSYNC ; 3
;--------------------------------------
; new block, line 6
STA HMOVE ; 3
CPY #JET_Y ; 2
BCS .skipJet5 ; 2
LDA (shapePtr0),Y ; 5
STA GRP0 ; 3
.skipJet5:
LDY #SECTION_BLOCKS-2 ; 2
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3
LDY lineNum ; 3
DEY ; 2
BEQ .exitKernel ; 2
LDX blockNum ; 3
LDA temp ; 3
STA HMP1 ; 3
JmpPoint6:
LDA #[BLOCK_SIZE-8]/2 ; 2
STA blockLine ; 3
TYA ; 2
SEC ; 2
SBC missileY ; 3
AND #$F8 ; 2
BNE .skipEnable3 ; 2
LDA #ENABLE ; 2
.skipEnable3:
CPY #JET_Y ; 2
STA WSYNC ; 3
;--------------------------------------
; new block, line 7
STA HMOVE ; 3
STA ENAM0 ; 3
BCS .skipJet6 ; 2
LDA (shapePtr0),Y ; 5
STA GRP0 ; 3
.skipJet6:
LDA State1Lst,X ; 4
STA NUSIZ1 ; 3
STA REFP1 ; 3
DEY ; 2
STY lineNum ; 3
BEQ DisplayState ; 2
STA HMCLR ; 3

; check collisions:
; (the collsion check between jet or missile and playfield aren't
; really neccessary for each block, but the collison registers
; are cleared after each block)
INX ; 2
BIT CXM0P-$30 ; 3 player missile hit enemy?
BPL .notHit ; 2
STX hitEnemyIdx ; 3 save block number
.notHit:
IF TRAINER
BIT zero1
ELSE
BIT CXP0FB-$30 ; 3 jet hit PF?
ENDIF
BPL .noPFCrash ; 2
STX PFCrashFlag ; 3
.noPFCrash:
IF TRAINER
BIT zero1
ELSE
BIT CXM0FB-$30 ; 3 player missile hit PF?
ENDIF
BPL .notHitPF ; 2
STX missileFlag ; 3
.notHitPF:
IF TRAINER
BIT zero1
ELSE
BIT CXPPMM-$30 ; 3 jet crashed into enemy?
ENDIF
BPL .noCrash ; 2
STX collidedEnemy ; 3 save block number
.noCrash:

.enterKernel5:
;--------------------------------------
; new block, line 8
STA WSYNC ; 3
STA HMOVE ; 3
CPY #JET_Y ; 2
BCS .skipJet7 ; 2
LDA (shapePtr0),Y ; 5
STA GRP0 ; 3
.skipJet7:
LDY #SECTION_BLOCKS-3 ; 2
LDA (PF1Ptr),Y ; 5
STA PF1 ; 3
LDA (PF2Ptr),Y ; 5
STA PF2 ; 3
LDY lineNum ; 3
DEY ; 2
BEQ DisplayState ; 2
BIT CXP1FB-$30 ; 3 enemy hit PF?
BPL .notEnemyPF ; 2 no, skip
LDA blockLst,X ; 4
ORA #PF_COLLIDE_FLAG ; 2 yes, set collision flag
STA blockLst,X ; 4
.notEnemyPF:
STA CXCLR ; 3 clear all collison registers

.enterKernel4:
TYA ; 2
SEC ; 2
SBC missileY ; 3
AND #$F8 ; 2
BNE .skipEnable4 ; 2
LDA #ENABLE ; 2
.skipEnable4:
CPY #JET_Y ; 2
STA WSYNC ; 3
;--------------------------------------
; new block, line 9 (= begin of even line)
STA HMOVE ; 3
STA ENAM0 ; 3
BCS .skipJet8 ; 2
LDA (shapePtr0),Y ; 5
STA GRP0 ; 3
.contJet8:
DEY ; 2
STY lineNum ; 3
BEQ DisplayState ; 2 exit the kernel
JMP .loopKernel ; 3 @26

JmpPoint5:
LDA #[BLOCK_SIZE-8]/2 ; 2
STA blockLine ; 3
BNE .enterKernel5 ; 3

JmpPoint4:
LDA #[BLOCK_SIZE-8]/2 ; 2 12
STA blockLine ; 3
BNE .enterKernel4 ; 3

.skipJet8: ; waste some time


NOP ; 2
NOP ; 2
BCS .contJet8 ; 3
DisplayState SUBROUTINE
; finish display kernel:
STA WSYNC ; 3
STA HMOVE ; 3
LDY #$00 ; 2
STY GRP1 ; 3
STY GRP0 ; 3
LDA zero1 ; 3 waste one extra cylce (but also
wastes a variable!)
STA COLUBK ; 3
STY PF0 ; 3
STY PF1 ; 3
STY PF2 ; 3
STY REFP0 ; 3
STY REFP1 ; 3
STY reflect0 ; 3
; prepare state display:
LDA #$11 ; 2 reflect PF, 2 pixel ball width,
also for HMP0!
STA RESP0 ; 3
STA RESP1 ; 3
STA CTRLPF ; 3
STA HMP0 ; 3
LDA #$20 ; 2
STA HMP1 ; 3
LDA playerColor ; 3
JSR SetColPx ; 6
LDA stateBKColor ; 3
STA COLUBK ; 3
LDA #THREE_COPIES ; 2
STA NUSIZ0 ; 3
STA NUSIZ1 ; 3
LDA statePFColor ; 3
STA COLUPF ; 3
LDY #$07 ; 2
STY VDELP0 ; 3
STY lineNum ; 3
STA HMCLR ; 3
; display score:
.loopScore:
LDA (scorePtr1+8),Y ; 5
TAX ; 2
LDA (scorePtr1+10),Y ; 5
STA WSYNC ; 3
STA HMOVE ; 3
STY temp2 ; 3
STA temp ; 3
LDA (scorePtr1),Y ; 5
STA GRP0 ; 3
LDA (scorePtr1+2),Y ; 5
STA GRP1 ; 3
LDA (scorePtr1+4),Y ; 5
STA GRP0 ; 3
LDA (scorePtr1+6),Y ; 5
LDY temp ; 3
STA GRP1 ; 3
STX GRP0 ; 3
STY GRP1 ; 3
STA GRP0 ; 3
LDY temp2 ; 3
DEY ; 2
BPL .loopScore ; 2

LDA zero1 ; 3 a = 0 (BLACK)


JSR FinishDigits ; 6 y = 14
; display fuel:
.loopFuel:
STY temp2 ; 3 line counter
LDA FuelTab4,Y ; 4
LDX FuelTab3,Y ; 4
STA WSYNC ; 3
STA HMOVE ; 3
STA temp ; 3
NOP ; 2
LDA #$00 ; 2
STA GRP0 ; 3
LDA ENABLTab,Y ; 4
STA ENABL ; 3
LDA FuelTab0,Y ; 4
STA GRP1 ; 3
LDA FuelTab1,Y ; 4
STA GRP0 ; 3
LDA FuelTab2,Y ; 4
LDY temp ; 3
STA GRP1 ; 3
STX GRP0 ; 3
STY GRP1 ; 3
STA GRP0 ; 3
LDY temp2 ; 3
DEY ; 2
BPL .loopFuel ; 2

LDA playerColor ; 3
JSR FinishDigits ; 6
INY ; 2 y=15
CLC ; 2
LDX gameMode ; 3
INX ; 2
BNE .noGame ; 2
LDA #<Space ; 2
STA livesPtr ; 3

; animate copyright message:


LDA frameCnt ; 3
LSR ; 2
LSR ; 2
LSR ; 2
CMP #20 ; 2
BCS .ok ; 2
CMP #12 ; 2
.noGame:
LDY #7 ; 2
BCC .ok ; 2
SBC #4 ; 2
TAY ; 2
.ok:
STY temp3 ; 3 copyright scroll offset
; display lives and copyright:
.loopCopyright:
LDY temp3 ; 3
LDA Copyright5,Y ; 4
STA temp ; 3
STA WSYNC ; 3
STA HMOVE ; 3
LDX Copyright4,Y ; 4
LDA (livesPtr),Y ; 5
STA GRP0 ; 3
DEC temp3 ; 5
LDA Copyright1,Y ; 4
STA GRP1 ; 3
LDA Copyright2,Y ; 4
STA GRP0 ; 3
LDA Copyright3,Y ; 4
LDY temp ; 3
STA GRP1 ; 3
STX GRP0 ; 3
STY GRP1 ; 3
STA GRP0 ; 3
DEC lineNum ; 5
BPL .loopCopyright ; 2

STA WSYNC ; 3
STA HMOVE ; 3
LDX #$00 ; 2
STX VDELP0 ; 3
STX GRP1 ; 3
STX GRP0 ; 3
LDA blockOffset ; 3
CMP #BLOCK_SIZE-6 ; 2
BCC .skipInx ; 2
INX ; 2
.skipInx:

; check collisions for the last displayed block:


BIT CXM0P-$30 ; 3 player missile hit enemy?
BPL .notHit2 ; 2
STX hitEnemyIdx ; 3 save block number
.notHit2:
IF TRAINER
BIT zero1
ELSE
BIT CXP0FB-$30 ; 3 jet hit PF?
ENDIF
BPL .noPFCrash2 ; 2
STX PFCrashFlag ; 3
.noPFCrash2:
BIT CXM0FB-$30 ; 3 player missile hit PF?
BPL .notHitPF2 ; 2
STX missileFlag ; 3
.notHitPF2:

IF NTSC
LDA #29 ; 2
ELSE
LDA #58 ; 2
ENDIF
LDY #%10000010 ; 2
STA WSYNC ; 3
STA TIM64T ; 4
STY VBLANK ; 3

BIT CXP1FB-$30 ; 3 enemy hit PF?


BPL .notEnemyPF2 ; 2
LDA blockLst,X ; 4
ORA #PF_COLLIDE_FLAG ; 2
STA blockLst,X ; 4
.notEnemyPF2:
IF TRAINER
LDA zero1
ELSE
BIT CXPPMM-$30 ; 3 jet crashed into enemy?
ENDIF
BPL .noCrash2 ; 2
STX collidedEnemy ; 3 save block number
.noCrash2:

; *** update framecounter, check for screensaver: ***


DEC frameCnt ; 5
BNE .skipSS_Delay ; 2
LDX gameMode ; 3
INX ; 2
BNE .skipInit ; 2
JSR SwapPlayers ; 6
.skipInit:
IF SCREENSAVER
INC SS_Delay ; 5
BNE .skipSS_Delay ; 2
SEC ; 2
ROR SS_Delay ; 5
ELSE
FILL_NOP 4
ENDIF
.skipSS_Delay:
IF SCREENSAVER
LDY #$FF ; 2
LDA SWCHB ; 4
AND #BW_MASK ; 2 black and white?
BNE .colorMode ; 2 no, color mode
LDY #$0F ; 2 yes, mask out high nibble
.colorMode:
TYA ; 2
LDY #$00 ; 2
BIT SS_Delay ; 3
BPL .noScreenSaver ; 2
AND #$F7 ; 2
LDY SS_Delay ; 3
.noScreenSaver:
STY SS_XOR ; 3
ASL SS_XOR ; 5
STA SS_Mask ; 3
ELSE
FILL_NOP 28
ENDIF

; *** randomly start movement of enemies: ***


LDA random ; 3
ASL ; 2
ASL ; 2
ASL ; 2
EOR random ; 3
ASL ; 2
ROL random ; 5
LDA frameCnt ; 3
AND #$0F ; 2 every 16th frame
BNE .skipStartMove ; 2
LDA random ; 3
AND #$07 ; 2
CMP #5 ; 2 start one of the first five
enemies
BCC .inBound ; 2
SBC #5 ; 2 doubled chances for the first
three enemies
.inBound:
TAX ; 2
LDA blockLst,X ; 4 start
ORA #ENEMY_MOVE_FLAG ; 2 movement
STA blockLst,X ; 4 of enemy
.skipStartMove:

;*** animate and move the enemy objects: ***


LDX #NUM_BLOCKS-1 ; 2
.loopEnemies:
; animate some enemies:
LDY Shape1IdLst,X ; 4
CPY #ID_SHIP ; 2 don't animate ship, bridge, house
and fuel
BCS .skipAnimate ; 2
LDA #$01 ; 2 every 2nd frame
CPY #ID_PLANE ; 2 fast animate plane (not done) and
helicopter
BCS .fastAnimation ; 2
LDA #$0F ; 2 slow animate explosions (every
16th frame)
.fastAnimation:
AND frameCnt ; 3
BNE .skipAnimate ; 2
LDA AnimateIdTab,Y ; 4
STA Shape1IdLst,X ; 4
TAY ; 2
.skipAnimate:

; check for move and direction change:


LDA gameMode ; 3
BNE .skipMoveEnemy ; 2
LDA level ; 3
LSR ; 2 first level?
BEQ .skipMoveEnemy ; 2 yes, don't move
CPY #ID_PLANE ; 2 move plane in same direction every
frame
BEQ .xMoveEnemy ; 2
BCC .skipMoveEnemy ; 2
CPY #ID_BRIDGE ; 2 don't move bridge, house and fuel
BCS .skipMoveEnemy ; 2
LDA frameCnt ; 3
ROR ; 2
BCS .skipMoveEnemy ; 2 move helicopter and ship every 2nd
frame
LDA blockLst,X ; 4
ASL ; 2 enemy moving?
BPL .skipMoveEnemy ; 2 no, skip move
ASL ; 2 enemy collided with PF?
BPL .noPFCollision ; 2 no, skip
ASL ; 2 patroling enemy?
BMI .xMoveEnemy ; 2 no, skip swap direction
LDA State1Lst,X ; 4 switch
EOR #DIRECTION_FLAG ; 2 enemy move
STA State1Lst,X ; 4 direction
LDA blockLst,X ; 4
ORA #PATROL_FLAG ; 2 (re)enable patrol mode
BNE .endChangeDir ; 2

.noPFCollision:
LDA blockLst,X ; 4
AND #~PATROL_FLAG ; 2 disable patrol mode (only
temporary)
.endChangeDir:
STA blockLst,X ; 4

; move enemy one pixel left or right:


; (no real position variables, the code is working
; directly with the positioning values)
.xMoveEnemy:
LDY XPos1Lst,X ; 4
LDA State1Lst,X ; 4
LSR ; 2
LSR ; 2
LSR ; 2
LSR ; 2
EOR #$07 ; 2
BCS .xMoveLeft ; 2 moving left!
ADC #1 ; 2
CMP #15 ; 2
BCC .skipRightIny ; 2
SBC #15 ; 2
INY ; 2
.skipRightIny:
CPY #10 ; 2
BCC .contMoveX ; 2
CMP #10 ; 2 >= 160?
BCC .contMoveX ; 2 no, continue
LDY #0 ; 2 yes,..
TYA ; 2 ..move shape (plane)..
BEQ .contMoveX ; 2 ..to the very left (0)

.xMoveLeft:
SBC #1 ; 2
BCS .contMoveX2 ; 2
ADC #15 ; 2
DEY ; 2 < 0?
BPL .contMoveX ; 2 no, continue
LDY #10 ; 2 yes, move shape (plane)..
LDA #9 ; 2 ..to the very right (159)
.contMoveX:
STY XPos1Lst,X ; 4 store coarse value here
.contMoveX2:
EOR #$07 ; 2
JSR Mult16 ; 6
EOR State1Lst,X ; 4
AND #FINE_MASK ; 2 #$F0
EOR State1Lst,X ; 4
STA State1Lst,X ; 4 OR fine value into here
.skipMoveEnemy:

; clear PF-collision:
LDA blockLst,X ; 4
AND #~PF_COLLIDE_FLAG ; 2
STA blockLst,X ; 4
DEX ; 2
BMI .exitLoopEnemies ; 2
JMP .loopEnemies ; 3
.exitLoopEnemies:

; *** read joystick: ***


LDA SWCHA ; 4
LDX player ; 3
BEQ .player1 ; 2
JSR Mult16 ; 6
.player1:
AND #$F0 ; 2 mask out other player joystick
TAX ; 2
LDY #4 ; 2
.loopBits:
ROL ; 2 roll new 4 bits into joystick
ROL joystick ; 5 (old 4 bits got into upper
nibble!)
DEY ; 2
BNE .loopBits ; 2

CPX #$F0 ; 2
BNE .joystickMoved ; 2
LDX player ; 3
LDA INPT4-$30,X ; 4
BMI .noFire ; 2
.joystickMoved:

LDA gameMode ; 3
CMP #INTRO_SCROLL ; 2
BNE .skipRestart ; 2
LDA #64 ; 2 restart new game
STA speedY ; 3
STY gameMode ; 3 y=0!
.skipRestart:
IF SCREENSAVER
STY SS_Delay ; 3 y=0!
ELSE
FILL_NOP 2
ENDIF
.noFire:
LDX gameMode ; 3
BEQ .checkCollisions ; 2 game running
BMI .gameOver ; 2 game is over
CPX #INTRO_SCROLL+1 ; 2 scrolling into new game/life?
BCC .doSoundJmp ; 2 yes, skip decrease
BNE .startGame ; 2 no, finished scrolling, start new
game
; decrease lives:
LDA livesPtr ; 3
BEQ .finishGame ; 2 zero!
SBC #DIGIT_H ; 2
BNE LF5B5 ; 2
LDA #<Space+1 ; 2
LF5B5: CMP #<MaxOut ; 2 score overflow?
BNE LF5BB ; 2
LDA #<Three ; 2 reset lives to 3
LF5BB: STA livesPtr ; 3
.startGame:
DEC gameMode ; 5 start game
BNE .doSoundJmp ; 2

.gameOver:
INX ; 2
BEQ .doSoundJmp ; 2
DEC gameMode ; 5
BMI .doSoundJmp ; 2
STY shapePtr0 ; 3
LDA livesPtr2 ; 3
CMP #<Copyright0 ; 2 other player still alive?
BEQ .skipSwap ; 2 no, skip swap
JSR SwapPlayers ; 6
.skipSwap:
LDA livesPtr ; 3
CMP #<Copyright0 ; 2 current player still alive?
BNE .initPlayer ; 2 yes, continue
.finishGame:
JSR FinishGame ; 6
BNE .doSoundJmp ; 2

.initPlayer:
LDX #shapePtr0+2-PF1Lst;2 #22
JSR GameInit ; 6
TYA ; 2
ORA #PF_ROAD_FLAG ; 2
STA blockLstEnd ; 3
LDA randomLoSave ; 3 load..
STA randomLo ; 3 ..random variables..
LDA randomHiSave ; 3 ..with saved..
STA randomHi ; 3 ..player variables
.doSoundJmp:
JMP DoSound ; 3

.checkCollisions:
LDX collidedEnemy ; 3 collided with enemy?
BMI .endCollisions ; 2 no, skip
LDA Shape1IdLst,X ; 4
CMP #ID_PLANE ; 2 collided with explosion?
BCC .endCollisions ; 2 no, skip
CMP #ID_BRIDGE ; 2 collided with ship, plane or
helicopter?
BCC .noBridge ; 2 yes, do collision
BNE .refuel ; 2 no, collided with fuel!
INC sectionEnd ; 5 no, collided with bridge!
.noBridge:
LDY #$1F ; 2 load some sound values
LDA #1 ; 2 sound id = 1
JSR LooseJet ; 6
LDX collidedEnemy ; 3
LDA #ID_EXPLOSION2 ; 2 start explosion
JMP .contJetExplosion ; 3

.refuel:
LDA fuelHi ; 3
ADC #$01 ; 2
LDX #4 ; 2 sound id = 4
BCC .notFull ; 2
LDA #$FF ; 2
STA fuelLo ; 3
LDX #3 ; 2 sound id = 3
.notFull:
STA fuelHi ; 3
CPX sound0Id ; 3
BEQ .endCollisions ; 2
STX sound0Id ; 3
LDA #$08 ; 2
STA sound0Cnt ; 3

.endCollisions:
LDX PFCrashFlag ; 3 jet crashed?
BMI .skipCrash ; 2 no, skip
.maxedOut:
LDY #$1F ; 2
LDA #1 ; 2 sound id = 1
.looseJet:
JSR LooseJet ; 6
BNE .doSoundJmp ; 2

.skipCrash:

; decrease fuel:
LDA fuelLo ; 3
SEC ; 2
IF TRAINER
SBC #0
ELSE
SBC #$20 ; 2
ENDIF
BCS .skipDecHi ; 2
LDY fuelHi ; 3
BNE .fuelOk ; 2
LDA #2 ; 2 sound id = 2
LDY #$23 ; 2
JMP .looseJet ; 3 out of fuel!
.fuelOk:
DEC fuelHi ; 5
.skipDecHi:
STA fuelLo ; 3

; *** move jet left or right: ***


LDA joystick ; 3
TAY ; 2
AND #MOVE_LEFT|MOVE_RIGHT; 2
EOR #MOVE_LEFT|MOVE_RIGHT; 2
BNE .leftRight ; 2
STA dXSpeed ; 3 jet is flying straight
STA speedX ; 3
LDX #<JetStraight-1 ; 2
BNE .setPtr0 ; 2

.leftRight:
LDA dXSpeed ; 3 increase the x speed change
CLC ; 2
ADC #8 ; 2
BCS .maxChange ; 2
STA dXSpeed ; 3
.maxChange:
LDX #<JetMove-1 ; 2
TYA ; 2
AND #MOVE_RIGHT ; 2
STA reflect0 ; 3
BEQ .moveRight ; 2
BCS .maxChange2 ; 2
LDA speedX ; 3
SEC ; 2
SBC dXSpeed ; 3
BCS .setXSpeed ; 2
.maxChange2:
DEC playerX ; 5
BNE .setXSpeed ; 2

.moveRight:
BCS .maxChange3 ; 2
LDA speedX ; 3
BIT joystick ; 3 moved right before?
BPL .wasRight ; 2 yes, skip
LDA #-1 ; 2 bo, move the jet very slowly to
the left (JTZ: what's that good for?)
.wasRight:
ADC dXSpeed ; 3
BCC .setXSpeed ; 2
.maxChange3:
INC playerX ; 5
.setXSpeed:
STA speedX ; 3
.setPtr0:
STX shapePtr0 ; 3

; change jet speed:


LDX speedY ; 3
TYA ; 2
LSR ; 2
BCS .noMoveUp ; 2
.incSpeed:
TXA ; 2
ADC #2 ; 2
BCC .changeSpeed ; 2
BCS .skipChange ; 2

.noMoveUp:
LSR ; 2
BCC .noMoveDown ; 2
TXA ; 2
ASL ; 2
BCC .incSpeed ; 2
BEQ .skipChange ; 2

.noMoveDown:
TXA ; 2
CMP #$41 ; 2 minimal speed?
BCC .skipChange ; 2 yes, skip slow down
SBC #2 ; 2
.changeSpeed:
STA speedY ; 3
.skipChange:

LDX hitEnemyIdx ; 3 object hit?


BMI .skipCollisions ; 2 no, skip
LDY Shape1IdLst,X ; 4
CPY #ID_PLANE ; 2 enemy objects?
BCC .skipCollisions ; 2 no, explosions
LDA #ID_EXPLOSION1 ; 2 start explosion animation
.contJetExplosion:
LDY Shape1IdLst,X ; 4
STA Shape1IdLst,X ; 4
LDA #23 ; 2
STA bridgeSound ; 3
CPY #ID_BRIDGE ; 2
BNE .skipBridge ; 2
STA bridgeExplode ; 3 start bridge explosion
LDA #$E0|TWO_COPIES ; 2 set fixed position and size (two
copies close)
STA State1Lst,X ; 4
LDA #4 ; 2 coarse positiong value
STA XPos1Lst,X ; 4
INC sectionEnd ; 5 new section has been started
.skipBridge:

; increase score:
LDX #8 ; 2 add 10s
LDA ScoreTab,Y ; 4
BPL .loopSetPtr1 ; 2
AND #$7F ; 2 add n*100 points
LDX scorePtr1+8 ; 3
CPX #<Space ; 2
BNE .noSpace ; 2
LDX #<Zero ; 2 replace Space..
STX scorePtr1+8 ; 3 ..with Zero
.noSpace:
LDX #6 ; 2 add 100s
.loopSetPtr1:
PHA ; 3
CPX #2 ; 2 life pointer
BNE .notLivePtr ; 2
; check for bonus life:
LDA livesPtr ; 3
CMP #<Nine ; 2
BEQ .maxLives ; 2
BCC .notMax ; 2
LDA #$FF ; 2 CF=1!
.notMax:
ADC #DIGIT_H ; 2
STA livesPtr ; 3
.maxLives:
.notLivePtr:
LDA scorePtr1,X ; 4
SEC ; 2
SBC #<Space ; 2
BNE .noSpace2 ; 2
STA scorePtr1,X ; 4 point to '0'
.noSpace2:
PLA ; 4
CLC ; 2
ADC scorePtr1,X ; 4
CMP #<MaxOut ; 2
BCC .noMaxOut ; 2 exit loop
SBC #<MaxOut ; 2
STA scorePtr1,X ; 4
LDA #DIGIT_H ; 2
DEX ; 2
DEX ; 2
BPL .loopSetPtr1 ; 2

; more than 999990 points, set score to !!!!!!, game over:


LDA #<MaxOut ; 2
LDX #12-2 ; 2
JSR SetScorePtr1 ; 6
LDA #<Copyright0 ; 2
STA livesPtr ; 3
JMP .maxedOut ; 3

.noMaxOut:
STA scorePtr1,X ; 4
.noMissile:
LDX #$B4 ; 2 disable missile
BNE .directMissile ; 2
.skipCollisions:

; *** move or fire missiles: ***


LDA missileFlag ; 3
BPL .noMissile ; 2
LDA missileY ; 3
CMP #MAX_MISSILE+1 ; 2
BCS .checkFire ; 2
ADC #MISSILE_SPEED ; 2 y-move missile
TAX ; 2
LDA SWCHB ; 4 read difficulty
LDY player ; 3
BNE .player1a ; 2
ASL ; 2
.player1a:
TAY ; 2
BPL .guidedMissile ; 2
BMI .directMissile ; 2

.checkFire:
LDX player ; 3
LDA INPT4-$30,X ; 4
BMI .noMissile ; 2
LDX #$0F ; 2
STX missileSound ; 3
LDX #MIN_MISSILE ; 2
.guidedMissile:
LDA playerX ; 3
CLC ; 2
ADC #$05 ; 2
STA missileX ; 3
.directMissile:
STX missileY ; 3

; *** sound routines: ***


; TODO: analyze, labels, comments
DoSound:
; start with channel 0:
LDY #$1C ; 2
LDA sound0Cnt ; 3
LDX sound0Id ; 3
BEQ LF789 ; 2
DEX ; 2
BEQ LF770 ; 2
LDY #$0F ; 2
CPX #$02 ; 2
BCS LF776 ; 2
LDY #$08 ; 2
LF770: LSR ; 2
TAX ; 2
LDA #$08 ; 2 white noise
BNE LF77D ; 2

LF776: BEQ LF77A ; 2


LDY #$1F ; 2
LF77A: TAX ; 2
LDA #$04 ; 2 high pure tone
LF77D: DEC sound0Cnt ; 5
BNE .setAud0 ; 2
PHA ; 3
LDA #0 ; 2
STA sound0Id ; 3 stop sound0
PLA ; 4
BPL .setAud0 ; 2

; low fuel sound:


LF789: LDA gameMode ; 3 game running?
BNE .mute0 ; 2 no, quiet (x=0)
LDA fuelHi ; 3
CMP #$40 ; 2
BCS .jetSound ; 2
LDY sound0Cnt ; 3
BNE .contSound0 ; 2
LDY #$3F ; 2
.contSound0:
DEY ; 2
STY sound0Cnt ; 3
LDX fuelLo ; 3
STX temp ; 3
CMP #$04 ; 2
BCS LF7B0 ; 2
ROL temp ; 5
ROL ; 2
ROL temp ; 5
ROL ; 2
EOR #$FF ; 2
ADC #$20 ; 2
BNE .loadAud0 ; 2

LF7B0: CPY #$1C ; 2


BCC .jetSound ; 2
TYA ; 2
LSR ; 2
.loadAud0:
TAY ; 2
LDA #$0C ; 2
LDX #$0F ; 2
BNE .setAud0 ; 2

; make some noise, depending on jet speed:


.jetSound:
LDA speedY ; 3 frequency depends on y-speed
LSR ; 2
LSR ; 2
LSR ; 2
LSR ; 2
EOR #$FF ; 2
SEC ; 2
ADC #$1F ; 2
TAY ; 2
LDA joystick ; 3 volume depends on joystick
position
AND #MOVE_UP|MOVE_DOWN; 2
TAX ; 2
LDA VolumeTab,X ; 4
TAX ; 2
LDA #$08 ; 2 white noise
.setAud0:
STA AUDC0 ; 3
STY AUDF0 ; 3
.mute0:
STX AUDV0 ; 3

; continue with channel 1:


; (missile fire or bridge explosion)
LDA missileSound ; 3
BEQ .noMissileSound ; 2
DEC missileSound ; 5
LDX bridgeSound ; 3 bridge exposion has higher
priority
BNE .doBridge ; 2
EOR #$FF ; 2
SEC ; 2
ADC #$1C ; 2
LDY #$0C ; 2 medium pure tone
LDX #$08 ; 2
BNE .setAud1 ; 2

.noMissileSound:
LDX bridgeSound ; 3
BEQ .skipSound1 ; 2
; let the bridge explode:
.doBridge:
DEC bridgeSound ; 5 countdown volume
TXA ; 2
LSR ; 2
CLC ; 2
ADC #$04 ; 2
TAX ; 2
LDA random ; 3 random frequency
ORA #$18 ; 2
LDY #$08 ; 2
.setAud1:
STA AUDF1 ; 3
STY AUDC1 ; 3
.skipSound1:
STX AUDV1 ; 3

; start next frame:


.waitTim:
LDA INTIM ; 4
BNE .waitTim ; 2
LDY #$82 ; 2
STY WSYNC ; 3
STY VSYNC ; 3
STY WSYNC ; 3
STY WSYNC ; 3
STY WSYNC ; 3
STA VSYNC ; 3
IF NTSC
LDA #43 ; 2
ELSE
LDA #73 ; 2
ENDIF
STA TIM64T ; 4

; *** check switches: ***


LDA SWCHB ; 4
LSR ; 2
BCS .noReset ; 2
LDA gameVariation ; 3 RESET was pressed
STA player ; 3
LDX #$F7 ; 2
JMP Reset ; 3

.noReset:
LSR ; 2
BCS .noSelect ; 2
DEC gameDelay ; 5 SELECT was pressed
BPL .skipSelect ; 2
LDA gameVariation ; 3 toggle game (one or two player)
EOR #$01 ; 2
STA gameVariation ; 3
IF SCREENSAVER
STA SS_Delay ; 3
ELSE
FILL_NOP 2
ENDIF
STA player ; 3
ASL ; 2
ASL ; 2
ASL ; 2
ADC #DIGIT_H ; 2
JSR SetScorePtrs ; 6
JSR FinishGame ; 6
LDY #$1E ; 2
.noSelect:
STY gameDelay ; 3

.skipSelect:
LDA gameMode ; 3
BMI .mainLoopJmp ; 2
CMP #INTRO_SCROLL ; 2 scrolling into game
BNE .setBlockVars ; 2 no, generate new blocks
LDA #<JetStraight-1 ; 2 yes, set..
STA shapePtr0 ; 3 ..jet data pointer..
.mainLoopJmp:
JMP MainLoop ; 3 .. and continue with main loop

; check, if a new block is neccessary:


.setBlockVars:
LDA #3-1 ; 2 add speedY*3 to blockOffset ->
max. speed = 3 lines/frame
STA blockNum ; 3
.loopNext:
DEC blockNum ; 5
BMI .mainLoopJmp ; 2
LDA speedY ; 3
CMP #$FE ; 2 maximum speed?
BCS .incOffset ; 2 yes, increase offset
ADC posYLo ; 3
STA posYLo ; 3
BCC .loopNext ; 2
.incOffset:
INC blockOffset ; 5
LDA blockOffset ; 3
CMP #BLOCK_SIZE ; 2
BCC .loopNext ; 2

; *** it#s time to create a new block: ***


LDX #0 ; 2
STX blockOffset ; 3
LDY #NUM_BLOCKS ; 2
STY temp ; 3 move 6 blocks
LDA level ; 3
CMP #5 ; 2 first four levels?
BCC .firstLevels ; 2 yes, prevent small valley
LDY #0 ; 2 no, allow all widths of valley
.firstLevels:
STY valleyWidth ; 3 0 = all widths allowed, 6 =
limited widths

; first move the other blocks, to make space for the new one:
.loopBlocks: ;
LDY #5 ; 2 move 5 bytes
.loopMoveBlock:
LDA blockLst+1,X ; 4
STA blockLst,X ; 4
INX ; 2
DEY ; 2
BNE .loopMoveBlock ; 2
INX ; 2 skip one entry
DEC temp ; 5
BNE .loopBlocks ; 2

STY State1LstEnd ; 3 y=0!


LDA blockLstEnd ; 3 clear variable (except
PF_COLOR_FLAG)
AND #PF_COLOR_FLAG ; 2
STA blockLstEnd ; 3
LDX PF1PatId ; 3 copy previous PF pattern id
STX prevPF1PatId ; 3

DEC blockPart ; 5 second part of block?


BEQ .nextBlock ; 2 yes, next block
LDX sectionBlock ; 3
DEX ; 2 first part of last block of
section?
BNE .notLast ; 2 no, continue part

; the last block of a section has to be a road with bridge:


STX sectionEnd ; 3 yes, end of current section
LDA level ; 3
LSR ; 2 straight current level?
LDA #PF_ROAD_FLAG ; 2
BCS .isStraight ; 2 yes, dark green in NEXT level
LDA #PF_ROAD_FLAG|PF_COLOR_FLAG; 2 no, lighter green in NEXT level
.isStraight:
STA blockLstEnd ; 3
.notLast:
JSR NextRandom16 ; 6 new random number for next part of
block
JMP .nextBlockPart ; 3

; continue with a 'normal' block:


.nextBlock:
DEC sectionBlock ; 5 last block of section?
BNE .contSection ; 2 no, continue
JSR SaveSection ; 6 yes, save variables..
LDX #SECTION_BLOCKS ; 2 ..and got next level
STX sectionBlock ; 3
.contSection:
JSR NextRandom16 ; 6 new random number for next block
LDX sectionBlock ; 3
DEX ; 2 last block of section?
BNE .notLastBlock ; 2 no, skip
STX PF_State ; 3 yes, PF-State = static
LDA #12 ; 2 pattern-id for last block (with
bridge)
BNE .setPF1Id ; 3

.notLastBlock:
LDA level ; 3
LSR ; 2 straight level?
LDA #7 ; 2 pattern-id for straight block
BCS .setPF1Id ; 2 yes, set
LDA PF_State ; 3
DEX ; 2 last but one block of section?
BNE .notLastButOne ; 2 no, skip

; finish island before end of section:


CMP #ISLAND_FLAG|CHANGE_FLAG; 2 both flags set?
BEQ .isSetBoth ; 2 yes, 11 -> 10 (1. step to finish
island)
BNE .clearBoth ; 3 no, static PF and no island (2.
step to finish island)

; change PF_State bits 7 & 6:


; 00 -> 01/00 static -> changing or static
; 01 -> 11 changing -> island & changing
; 10 -> 00 island & static -> static (JTZ: ???)
; 11 -> 10/11 island & changing -> island & changing or static
.notLastButOne:
ASL ; 2
EOR PF_State ; 3 CHANGE_FLAG != ISLAND_FLAG?
BMI .updateFlags ; 2 yes, change flags
LDA randomLo ; 3 randomly change state?
AND #%00110000 ; 2
BNE .skipFlags ; 2 no, don't change state (75%)
.isSetBoth:
LDA PF_State ; 3
AND #ISLAND_FLAG ; 2 ISLAND_FLAG set?
BNE .isIsland ; 2 yes, clear CHANGE_FLAG
ORA #CHANGE_FLAG ; 2 no, set CHANGE_FLAG
.isIsland:
STA PF_State ; 3
LDA #0 ; 2
BEQ .setPF1Id ; 3

.updateFlags:
; change flags: 01 -> 11, 10 -> 00
LDA #ISLAND_FLAG|CHANGE_FLAG; 2
BIT PF_State ; 3 CHANGE_FLAG set?
BVS .setBoth ; 2 yes, set ISLAND_FLAG
.clearBoth:
LDA #0 ; 2 no, clear both flags
.setBoth:
STA PF_State ; 3
.skipFlags:
; create new random PF id:
; (JTZ: I'm not 100% sure, that I understand everything completely)
LDY #14 ; 2 y = 14
LDA randomLo ; 3
AND #$0F ; 2
CMP #2 ; 2
BCS .minOk ; 2
ADC #2 ; 2 minimum = 2
.minOk: ; a = 2..15
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .skipDey ; 2 no, skip
DEY ; 2 y = 13
.skipDey:
LDX valleyWidth ; 3 all widths allowed?
BEQ .allWidths ; 2 yes, skip limit
LDY #8 ; 2 y = 8
.allWidths:
STY temp ; 3 save max. allowed id
CMP temp ; 3 random id < max. id?
BCC .setPF1Id ; 2 yes, skip
LDA temp ; 3 no, use max. id
.setPF1Id:
STA PF1PatId ; 3 a = 2..8 or 2..13/14
LDY #BLOCK_PARTS ; 2 reset blockPart
STY blockPart ; 3

.nextBlockPart:
LDA prevPF1PatId ; 3
TAX ; 2
SEC ; 2
SBC PF1PatId ; 3
STA diffPF ; 3 store the difference between the
two blocks
BCS .biggerPrev ; 2
; new id is bigger:
INC diffPF ; 5
CPX #SWITCH_PAGE_ID-1 ; 2
LDX PF1PatId ; 3
BCS .prevBigId ; 2
CPX #SWITCH_PAGE_ID ; 2
BCC .page1Id ; 2
LDA #-1 ; 2
ADC prevPF1PatId ; 3 CF=1! (JTZ: what's that good for?)
BPL .prevId ; 3

; old id is bigger or equal:


.biggerPrev:
BEQ .equalId ; 2
DEC diffPF ; 5 -1
.equalId:
CPX #SWITCH_PAGE_ID ; 2
BCS .page0Id ; 2
; not enough space for an island:
.page1Id:
JSR GetPageFlag ; 6
JSR LoadPFPattern ; 6 a = 0/1
STA PF1LstEnd ; 3
LDA #0 ; 2
STA PF2LstEnd ; 3
BEQ .contPage1 ; 3

; enough space for an island in previous block:


.page0Id:
LDA PF1PatId ; 3
CMP #SWITCH_PAGE_ID-1 ; 2
BCS .prevBigId ; 2
; enough space for an island in both blocks:
LDA #14+1 ; 2
SBC PF1PatId ; 3 CF=0!
BCS .prevId ; 3 negate id (inverts pattern)

.prevBigId:
LDA #PF1_PAGE_FLAG|PF2_PAGE_FLAG|PF_COLOR_FLAG; 2
.prevId:
STA PF1LstEnd ; 3
JSR GetPageFlag ; 6
SEC ; 2
ROL ; 2
JSR LoadPFPattern ; 6 a = 1/3
.contPage1:
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .skipSwapPF ; 2 no, don't swap
LDA PF1LstEnd ; 3
LDX PF2LstEnd ; 3
STA PF2LstEnd ; 3
STX PF1LstEnd ; 3
.skipSwapPF:
BIT blockLstEnd ; 3 PF_ROAD_FLAG set?
BPL .skipRoad ; 2 no, skip
LDA #QUAD_SIZE ; 2 yes, create road block
STA State1LstEnd ; 3 quad size bridge
LDY #ID_BRIDGE ; 2
LDA #63 ; 2 x-position
JMP .endNewShape ; 3

.skipRoad:
; *** create new objects: ***
LDY #ID_FUEL ; 2
LDA sectionBlock ; 3
CLC ; 2
ADC blockPart ; 3
CMP #SECTION_BLOCKS+BLOCK_PARTS; 2 no enemies at first part of first
block of section
BCS .newHouse ; 2
; create more enemies and less fuel in higher difficulty levels:
LDA #64 ; 2
SBC level ; 3 1..48 (CF=0!)
ASL ; 2 a = 124..30
CMP randomHi ; 3
BCC .newEnemy ; 2 ~48%..88% -> more enemies, less
fuel and houses
BIT randomLo ; 3
BVC .newFuel ; 2 ~24%.. 6% -> less fuel
; no enemy or fuel, create new house instead:
.newHouse:
DEY ; 2 y=ID_HOUSE
LDX PF1PatId ; 3
CPX prevPF1PatId ; 3
BCC .currentSmaller ; 2
LDX prevPF1PatId ; 3
.currentSmaller: ; x = smaller id
LDA #DOUBLE_SIZE ; 2 house is double sized
STA State1LstEnd ; 3
LDA level ; 3
LSR ; 2
BCC .notStraight ; 2
; create random x-position for house in straight section:
LDA randomLo ; 3
AND #$1F ; 2
ADC #8 ; 2
CMP #25 ; 2 random position fits in left bank?
BCC .setShapeDir ; 2 yes, ok
ADC #92 ; 2 no, position house on right bank
BNE .setShapeDir ; 3

; position house in non-straight section:


.notStraight:
LDA ShapePosTab,X ; 4 x-pos based on PF1 id
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .setShapeDir ; 2 no, skip
CPX #0 ; 2 PF id = 0?
BEQ .setShapeDir ; 2 no, skip
LDA #71 ; 2 fixed position for a house on
island
BNE .setShapeDir ; 3

; create new ship, helicopter or plane:


.newEnemy:
LDA #%111 ; 2
LDX level ; 3
CPX #3 ; 2 enemy planes start at level three
BCS .withPlanes ; 2
LDA #%001 ; 2 limit first levels to ship and
helicopter
.withPlanes:
AND randomHi ; 3 create random enemy object
TAX ; 2
LDY EnemyIdTab,X ; 4
.newFuel:
CPY #ID_SHIP ; 2
BNE .noShip ; 2
LDA #DOUBLE_SIZE ; 2 doublesize
STA State1LstEnd ; 3
.noShip:
LDA PF1PatId ; 3
CMP prevPF1PatId ; 3 new pat-id = previous pat-id?
BNE .newId ; 2 no,
; position object in straight blocks:
STA maxId ; 3
LDA level ; 3
LSR ; 2
BCC .notStraight2 ; 2
; position object in straight section:
LDA #106 ; 2
LDX State1LstEnd ; 3 ship? (doublesize)
BEQ .isShip ; 2 yes, position more right
LDA #97 ; 2 no, position more left
.isShip:
SBC valleyWidth ; 3 decrease maximum position (-6) in
first four levels,
; this avoids positioning near the
river bank
STA temp ; 3 store maximum position
LDA randomLo ; 3
AND #$3F ; 2
ADC #45 ; 2
ADC valleyWidth ; 3 increase random position in first
four levels (s.a.)
CMP temp ; 3 random position < maximum?
BCC .setShapeDir ; 2 yes, ok
LDA temp ; 3 no, position = maximum
.setShapeDir:
; make random direction for new shape:
BIT randomLo ; 3
BMI .invertDirection ; 2
BPL .endNewShape ; 3

.newId:
BCS .currentBigger ; 2
LDA prevPF1PatId ; 3
.currentBigger:
STA maxId ; 3 maxId cointains max(prevId, newId)

; position object in non-straight section:


.notStraight2:
; check, if there is enough space for new object:
LDX #13 ; 2 PF id
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .contPage12 ; 2 no, skip
LDX #10 ; 2 yes, lower PF id
.contPage12:
CPX maxId ; 3
BCS .spaceOk ; 2
TYA ; 2
SBC #ID_SHIP-1 ; 2 new enemy is a ship?
BNE .spaceOk ; 2 no, skip
STA State1LstEnd ; 3 yes, change..
DEY ; 2 ..ship into helicopter
.spaceOk:
LDA maxId ; 3
ASL ; 2
ASL ; 2
BEQ .posSomewhere ; 2
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .posSomewhere ; 2 no, position somewhere
; position object outside:
EOR #$FF ; 2
ADC #81 ; 2
BIT randomLo ; 3
BPL .skipNeg ; 2
EOR #$FF ; 2
ADC #160 ; 2
BNE .contPos ; 3

; position object somewhere:


.posSomewhere:
ADC #16 ; 2
BIT randomLo ; 3
BMI .doNeg ; 2
.contPos:
CLC ; 2
ADC #2 ; 2
ADC valleyWidth ; 3 keep space to river bank in first
levels
BNE .endNewShape ; 3

.doNeg:
EOR #$FF ; 2
ADC #160+1 ; 2
.skipNeg:
CPY #ID_FUEL ; 2 position fuel 1 pixel more right
SBC #9 ; 2
SBC valleyWidth ; 3 keep space to river bank in first
levels
LDX State1LstEnd ; 3 double sized object? (ship, house)
BEQ .invertDirection ; 2 no, skip
SBC #10 ; 2 yes, move 10 pixels left
.invertDirection:
CPY #ID_FUEL ; 2 fuel?
BEQ .endNewShape ; 2 yes, has constant direction
PHA ; 3
LDA State1LstEnd ; 3
ORA #DIRECTION_FLAG ; 2 set direction flag
STA State1LstEnd ; 3
PLA ; 4
.endNewShape:
STY Shape1IdLstEnd ; 3 save id of new object
JSR CalcPosX ; 6
STY XPos1LstEnd ; 3 save coarse x-positioning value
ORA State1LstEnd ; 3
STA State1LstEnd ; 3 save fine x-positioning value
JMP .loopNext ; 3

; ****************************** end of main loop ******************************

GameInit SUBROUTINE
; Input: x (= 22/38, number of initialized variables)
; initializes some variables for new game:
.initLoop:
LDA InitTab,X ; 4
STA PF1Lst,X ; 4
DEX ; 2
BPL .initLoop ; 2

; clear some variables for new game:


LDA #0 ; 2
LDX #30 ; 2
.loopClear:
STA dXSpeed,X ; 4
DEX ; 2
BPL .loopClear ; 2

LDX #NUM_BLOCKS-1 ; 2
LDY #PF1_PAGE_FLAG ; 2
LDA level ; 3
LSR ; 2 straight level?
BCC .loopSet ; 2 no, skip
LDY #PF1_PAGE_FLAG|PF_COLOR_FLAG; 2 yes, set brighter green in
current level
.loopSet:
STY blockLst,X ; 4
DEX ; 2
BPL .loopSet ; 2
RTS ; 6

IF NTSC
LoadPFPattern SUBROUTINE
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .contPage1 ; 2 no, set current page-flag
TAY ; 2 yes, read new page-flag from
table
LDA PageFlagTab,Y ; 4
.contPage1:
ORA blockLstEnd ; 3
STA blockLstEnd ; 3
LDA BankPtrTab,X ; 4 load a pattern for the river bank
CLC ; 2
ADC diffPF ; 3 adjust with difference between new
and prev PF id
STA PF2LstEnd ; 3
RTS ; 6
ELSE
FinishDigits SUBROUTINE
INY ; 2
STA WSYNC ; 3
STA HMOVE ; 3
STY GRP1 ; 3
STY GRP0 ; 3
STY GRP1 ; 3
LDY #14 ; 2 load line counter
SetColPx:
STA COLUP0 ; 3
STA COLUP1 ; 3
DoHMove:
STA WSYNC ; 3
STA HMOVE ; 3
RTS ; 6
ENDIF

NextRandom16 SUBROUTINE
; implements a 16 bit LFSR which generates a new random number:
LDA randomHi ; 3
ASL ; 2
ASL ; 2
ASL ; 2
EOR randomHi ; 3
ASL ; 2
ROL randomLo ; 5
ROL randomHi ; 5
; (JTZ: randomHi is very random, randomLo is NOT when more than one bit is used,
; because: randomLo[x+1] = randomLo[x]*2 + 0/1, but randomLo is used more often,
; randomHi only for new enemy and which. This could make the game a bit
predictable.)
RTS ; 6

SaveSection SUBROUTINE
; called at the start of a new section, increases difficulty level
; and saves random variables to be able to restart this section
LDX level ; 3 limit level to 48
CPX #MAX_LEVEL ; 2
BCC .notMax ; 2
LDX #MAX_LEVEL-2 ; 2 go back to 47
.notMax:
LDA randomLoSave ; 3
STA randomLoSave2 ; 3
LDA randomHiSave ; 3
STA randomHiSave2 ; 3
LDA randomLo ; 3
STA randomLoSave ; 3
LDA randomHi ; 3
STA randomHiSave ; 3
INX ; 2
STX level ; 3 1..48
RTS ; 6

SetPosX SUBROUTINE
; calculates the values and positions objects:
JSR CalcPosX ; 6
SetPosX2:
STA HMP0,X ; 4
INY ; 2
INY ; 2
INY ; 2
STA WSYNC ; 3
.waitPos:
DEY ; 2
BPL .waitPos ; 2
STA RESP0,X ; 4
RTS ; 6

;===============================================================================
; R O M - T A B L E S (Part 1)
;===============================================================================

align 256

Zero:
.byte $3C ; | XXXX | $FB00
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $3C ; | XXXX |
One:
.byte $3C ; | XXXX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $38 ; | XXX |
.byte $18 ; | XX |
Two:
.byte $7E ; | XXXXXX |
.byte $60 ; | XX |
.byte $60 ; | XX |
.byte $3C ; | XXXX |
.byte $06 ; | XX |
.byte $06 ; | XX |
.byte $46 ; | X XX |
.byte $3C ; | XXXX |
Three:
.byte $3C ; | XXXX |
.byte $46 ; | X XX |
.byte $06 ; | XX |
.byte $0C ; | XX |
.byte $0C ; | XX |
.byte $06 ; | XX |
.byte $46 ; | X XX |
.byte $3C ; | XXXX |
Four:
.byte $0C ; | XX |
.byte $0C ; | XX |
.byte $0C ; | XX |
.byte $7E ; | XXXXXX |
.byte $4C ; | X XX |
.byte $2C ; | X XX |
.byte $1C ; | XXX |
.byte $0C ; | XX |
Five:
.byte $7C ; | XXXXX |
.byte $46 ; | X XX |
.byte $06 ; | XX |
.byte $06 ; | XX |
.byte $7C ; | XXXXX |
.byte $60 ; | XX |
.byte $60 ; | XX |
.byte $7E ; | XXXXXX |
Six:
.byte $3C ; | XXXX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $7C ; | XXXXX |
.byte $60 ; | XX |
.byte $62 ; | XX X |
.byte $3C ; | XXXX |
Seven:
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $0C ; | XX |
.byte $06 ; | XX |
.byte $42 ; | X X |
.byte $7E ; | XXXXXX |
Eight:
.byte $3C ; | XXXX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $3C ; | XXXX |
.byte $3C ; | XXXX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $3C ; | XXXX |
Nine:
.byte $3C ; | XXXX |
.byte $46 ; | X XX |
.byte $06 ; | XX |
.byte $3E ; | XXXXX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $66 ; | XX XX |
.byte $3C ; | XXXX |
MaxOut:
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $00 ; | |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
.byte $18 ; | XX |
Space:
.byte $00 ; | |
Copyright0:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $F7 ; |XXXX XXX|
.byte $95 ; |X X X X|
.byte $87 ; |X XXX|
.byte $80 ; |X |
.byte $90 ; |X X |
.byte $F0 ; |XXXX |
Copyright1:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $47 ; | X XXX|
.byte $41 ; | X X|
.byte $77 ; | XXX XXX|
.byte $55 ; | X X X X|
.byte $75 ; | XXX X X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
Copyright2:
.byte $AD ; |X X XX X|
.byte $A9 ; |X X X X|
.byte $E9 ; |XXX X X|
.byte $A9 ; |X X X X|
.byte $ED ; |XXX XX X|
.byte $41 ; | X X|
.byte $0F ; | XXXX|
.byte $00 ; | |
.byte $03 ; | XX|
.byte $00 ; | |
.byte $4B ; | X X XX|
.byte $4A ; | X X X |
.byte $6B ; | XX X XX|
.byte $00 ; | |
.byte $08 ; | X |
.byte $00 ; | |
Copyright3:
.byte $50 ; | X X |
.byte $58 ; | X XX |
.byte $5C ; | X XXX |
.byte $56 ; | X X XX |
.byte $53 ; | X X XX|
.byte $11 ; | X X|
.byte $F0 ; |XXXX |
.byte $00 ; | |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $AA ; |X X X X |
.byte $AA ; |X X X X |
.byte $BA ; |X XXX X |
.byte $22 ; | X X |
.byte $27 ; | X XXX|
.byte $02 ; | X |
Copyright4:
.byte $BA ; |X XXX X |
.byte $8A ; |X X X |
.byte $BA ; |X XXX X |
.byte $A2 ; |X X X |
.byte $3A ; | XXX X |
.byte $80 ; |X |
.byte $FE ; |XXXXXXX |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $11 ; | X X|
.byte $11 ; | X X|
.byte $17 ; | X XXX|
.byte $15 ; | X X X|
.byte $17 ; | X XXX|
.byte $00 ; | |
Copyright5:
.byte $E9 ; |XXX X X|
.byte $AB ; |X X X XX|
.byte $AF ; |X X XXXX|
.byte $AD ; |X X XX X|
.byte $E9 ; |XXX X X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $77 ; | XXX XXX|
.byte $54 ; | X X X |
.byte $77 ; | XXX XXX|
.byte $51 ; | X X X|
.byte $77 ; | XXX XXX|

PageFlagTab:
.byte 0, PF2_PAGE_FLAG, PF1_PAGE_FLAG, PF1_PAGE_FLAG|PF2_PAGE_FLAG ;
PF1_PAGE_FLAG unused!
shapePtr1bTab:
.byte <Explosion0-1, <Explosion1B-1, <Explosion2B-1, <Explosion1B-1
.byte <PlaneB-1, <Heli0B-1, <Heli1B-1, <ShipB-1, <BridgeB-1, <HouseB-1,
<FuelB-1

; x-positions of new object:


ShapePosTab:
.byte 143, 141, 7, 10, 132, 13, 128, 18, 124, 22, 120, 26, 116, 30, 112

;===============================================================================
; R O M - C O D E (Part 2)
;===============================================================================

SetPFxPtr SUBROUTINE
; called from kernel, sets pointers for new playfield data:
LDA PF1Lst,X ; 4
STA PF1Ptr ; 3
LDA blockLst,X ; 4
AND #PF1_PAGE_FLAG ; 2
ORA #>PFPat0 ; 2
STA PF1Ptr+1 ; 3

LDA PF2Lst,X ; 4
STA PF2Ptr ; 3
LDA blockLst,X ; 4
LSR ; 2
AND #PF2_PAGE_FLAG>>1 ; 2
ORA #>PFPat0 ; 2
STA PF2Ptr+1 ; 3
RTS ; 6 = 44

;===============================================================================
; R O M - T A B L E S (Part 2)
;===============================================================================

; high addresses of entry points into kernel:


JmpHiTab:
.byte >[JmpPoint0-1], >[JmpPoint1-1], >[JmpPoint2-1], >[JmpPoint3-1],
>[JmpPoint4-1]
.byte >[JmpPoint5-1], >[JmpPoint6-1], >[JmpPoint7-1], >[JmpPoint8-1],
>[JmpPoint9-1]

; used to animate explosions and helicopter:


AnimateIdTab:
.byte 0 ;
.byte ID_EXPLOSION2 ; start of explosion sequence
.byte ID_EXPLOSION3 ;
.byte ID_EXPLOSION0 ; end explosion with 0
.byte ID_PLANE ; no animation for plane
.byte ID_HELI1 ; switch between..
.byte ID_HELI0 ; ..ID_HELI0 and ID_HELI1

; these are the patterns, that are used to define the playfield:
PFPat0:
.byte $00 ; | | $FC00
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $01 ; | X|
.byte $03 ; | XX|
.byte $07 ; | XXX|
.byte $0F ; | XXXX|
.byte $1F ; | XXXXX|
PFPat14:
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $3F ; | XXXXXX|
.byte $1F ; | XXXXX|
.byte $0F ; | XXXX|
.byte $07 ; | XXX|
.byte $03 ; | XX|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $01 ; | X|
.byte $03 ; | XX|
.byte $07 ; | XXX|
.byte $0F ; | XXXX|
PFPat13:
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1f ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $1F ; | XXXXX|
.byte $0F ; | XXXX|
.byte $07 ; | XXX|
.byte $03 ; | XX|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $01 ; | X|
.byte $03 ; | XX|
.byte $07 ; | XXX|
PFPat12:
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $0F ; | XXXX|
.byte $07 ; | XXX|
.byte $03 ; | XX|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $01 ; | X|
.byte $03 ; | XX|
PFPat11:
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $07 ; | XXX|
.byte $03 ; | XX|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $01 ; | X|
PFPat10:
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $03 ; | XX|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
PFPat9:
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
JetStraight:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $2A ; | X X X |
.byte $3E ; | XXXXX |
.byte $1C ; | XXX |
.byte $08 ; | X |
.byte $49 ; | X X X|
.byte $6B ; | XX X XX|
.byte $7F ; | XXXXXXX|
.byte $7F ; | XXXXXXX|
.byte $3E ; | XXXXX |
.byte $1C ; | XXX |
.byte $08 ; | X |
.byte $08 ; | X |
.byte $08 ; | X |
JetMove:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $02 ; | X |
.byte $2E ; | X XXX |
.byte $3C ; | XXXX |
.byte $18 ; | XX |
.byte $08 ; | X |
.byte $0A ; | X X |
.byte $2E ; | X XXX |
.byte $3E ; | XXXXX |
.byte $3E ; | XXXXX |
.byte $3C ; | XXXX |
.byte $18 ; | XX |
.byte $08 ; | X |
.byte $08 ; | X |
.byte $08 ; | X |
JetExplode:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $02 ; | X |
.byte $08 ; | X |
.byte $10 ; | X |
.byte $00 ; | |
.byte $40 ; | X |
.byte $08 ; | X |
.byte $21 ; | X X|
.byte $44 ; | X X |
.byte $10 ; | X |
.byte $04 ; | X |
.byte $08 ; | X |
.byte $00 ; | |

; low pointers to the patterns for the river bank:


BankPtrTab: ; $FCF1
.byte <PFPat0, <PFPat1, <PFPat2, <PFPat3, <PFPat4, <PFPat5, <PFPat6,
<PFPat7, <PFPat8
; last patterns are only used for islands:
.byte <PFPat9, <PFPat10, <PFPat11, <PFPat12, <PFPat13, <PFPat14

align 256

.byte $80 ; |X | $FD00


.byte $C0 ; |XX |
.byte $E0 ; |XXX |
.byte $F0 ; |XXXX |
.byte $F8 ; |XXXXX |
.byte $FC ; |XXXXXX |
.byte $FE ; |XXXXXXX |
PFPat8:
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FE ; |XXXXXXX |
.byte $FC ; |XXXXXX |
.byte $F8 ; |XXXXX |
.byte $F0 ; |XXXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
.byte $C0 ; |XX |
.byte $E0 ; |XXX |
.byte $F0 ; |XXXX |
.byte $F8 ; |XXXXX |
.byte $FC ; |XXXXXX |
PFPat7:
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FE ; |XXXXXXX |
.byte $FC ; |XXXXXX |
.byte $F8 ; |XXXXX |
.byte $F0 ; |XXXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
.byte $C0 ; |XX |
.byte $E0 ; |XXX |
.byte $F0 ; |XXXX |
.byte $F8 ; |XXXXX |
PFPat6:
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $FC ; |XXXXXX |
.byte $F8 ; |XXXXX |
.byte $F0 ; |XXXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
.byte $C0 ; |XX |
.byte $E0 ; |XXX |
.byte $F0 ; |XXXX |
PFPat5:
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F8 ; |XXXXX |
.byte $F0 ; |XXXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
.byte $C0 ; |XX |
.byte $E0 ; |XXX |
PFPat4:
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $F0 ; |XXXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
.byte $C0 ; |XX |
PFPat3:
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $E0 ; |XXX |
.byte $C0 ; |XX |
.byte $80 ; |X |
PFPat2:
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
.byte $C0 ; |XX |
PFPat1:
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |

InitTab: ; $FDB1
.ds 6, <PFPat8 ; PF1Lst
.ds 6, <PFPat12 ; PF2Lst
.byte NUM_LINES+20 ; missileY
.byte 76 ; playerX
.byte 0 ; speedX
.byte 254 ; speedY
.byte 1 ;
.byte $FF, $FF ; fuelHi, fuelLo
.byte 17 ; sectionBlock
.byte <PFPat0, >PFPat0 ; shapePtr0 ;22
.byte 12 ; PF1PatId
.byte 1 ; level
.byte SEED_LO, SEED_HI ; randomLoSave, randomHiSave
.byte <Space, >Space ; livesPtr
.byte 1, SEED_LO, SEED_HI, <Space ; player2State (level,
randomLoSave, randomHiSave, livesPtr)
.byte $80 ; gameMode
.byte <FuelA, >FuelA ; shapePtr1a
.byte <FuelB, >FuelB ; shapePtr1b
.byte <ShipCol-3, >ShipCol ; colorPtr

;===============================================================================
; R O M - C O D E (Part 3)
;===============================================================================

CalcPosX SUBROUTINE
; calculates values for x-positioning:
; Input:
; - a = x-position
; Return:
; - y = coarse value for delay loop
; - a = fine value for HMxy
TAY ; 2
INY ; 2
TYA ; 2
AND #$0F ; 2
STA temp2 ; 3
TYA ; 2
LSR ; 2
LSR ; 2
LSR ; 2
LSR ; 2
TAY ; 2
CLC ; 2
ADC temp2 ; 3
CMP #$0F ; 2
BCC .skipIny ; 2
SBC #$0F ; 2
INY ; 2
.skipIny:
EOR #$07 ; 2
Mult16:
ASL ; 2
ASL ; 2
ASL ; 2
ASL ; 2
Wait12:
RTS ; 6

;===============================================================================
; R O M - T A B L E S (Part 3)
;===============================================================================

; low addresses of entry points into kernel:


JmpLoTab:
.byte <[JmpPoint0-1]
.byte <[JmpPoint1-1]
.byte <[JmpPoint2-1]
.byte <[JmpPoint3-1]
.byte <[JmpPoint4-1]
.byte <[JmpPoint5-1]
.byte <[JmpPoint6-1]
.byte <[JmpPoint7-1]
.byte <[JmpPoint8-1]
.byte <[JmpPoint9-1]

align 256

FuelTab0:
.byte $7F ; | XXXXXXX| $FE00
.byte $40 ; | X |
.byte $4F ; | X XXXX|
.byte $48 ; | X X |
.byte $48 ; | X X |
.byte $4E ; | X XXX |
.byte $48 ; | X X |
.byte $48 ; | X X |
.byte $4F ; | X XXXX|
.byte $40 ; | X |
.byte $40 ; | X |
.byte $4C ; | X XX |
.byte $4C ; | X XX |
.byte $4C ; | X XX |
.byte $7F ; | XXXXXXX|

FuelTab1:
.byte $FF ; |XXXXXXXX|
Explosion0:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
FuelTab2:
.byte $FF ; |XXXXXXXX|
.byte $00 ; | |
.byte $03 ; | XX|
.byte $C2 ; |XX X |
.byte $63 ; | XX XX|
.byte $30 ; | XX |
.byte $1B ; | XX XX|
.byte $EC ; |XXX XX |
.byte $46 ; | X XX |
.byte $43 ; | X XX|
.byte $C1 ; |XX X|
.byte $48 ; | X X |
.byte $08 ; | X |
.byte $08 ; | X |
FuelTab3:
.byte $FF ; |XXXXXXXX|
.byte $00 ; | |
.byte $80 ; |X |
.byte $00 ; | |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $80 ; |X |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $80 ; |X |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
FuelTab4:
.byte $FF ; |XXXXXXXX|
.byte $01 ; | X|
.byte $81 ; |X X|
.byte $81 ; |X X|
.byte $81 ; |X X|
.byte $E1 ; |XXX X|
.byte $81 ; |X X|
.byte $81 ; |X X|
.byte $F1 ; |XXXX X|
.byte $01 ; | X|
.byte $01 ; | X|
.byte $19 ; | XX X|
.byte $19 ; | XX X|
.byte $19 ; | XX X|
.byte $FF ; |XXXXXXXX|

; used to en- or disable ball in fuel display:


ENABLTab:
.byte DISABLE
.byte ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE,
ENABLE, ENABLE

; the scores to the enemy objects (bit 7 = 0: *10, = 1: *100):


ScoreTab:
.byte 0, 0, 0, 0 ; EXPLOSIONS
.byte DIGIT_H * 1 | $80 ; PLANE 100
.byte DIGIT_H * 6 ; HELI 60
.byte DIGIT_H * 6 ; HELI 60
.byte DIGIT_H * 3 ; SHIP 30
.byte DIGIT_H * 5 |$80 ; BRIDGE 500
.byte 0 ; HOUSE
.byte DIGIT_H * 8 ; FUEL 80

; the data is stored for interlaced display:


FuelA:
.byte $FE ; |XXXXXXX |
.byte $DE ; |XX XXXX |
.byte $DE ; |XX XXXX |
.byte $FE ; |XXXXXXX |
.byte $DE ; |XX XXXX |
.byte $DE ; |XX XXXX |
.byte $FE ; |XXXXXXX |
.byte $D6 ; |XX X XX |
.byte $D6 ; |XX X XX |
.byte $DE ; |XX XXXX |
.byte $CE ; |XX XXX |
FuelB:
.byte $C6 ; |XX XX |
.byte $DE ; |XX XXXX |
.byte $DE ; |XX XXXX |
.byte $C6 ; |XX XX |
.byte $CE ; |XX XXX |
.byte $C6 ; |XX XX |
.byte $C6 ; |XX XX |
.byte $D6 ; |XX X XX |

.byte $FE ; |XXXXXXX |


.byte $DE ; |XX XXXX |
.byte $DE ; |XX XXXX |
.byte $7C ; | XXXXX |
BridgeA:
.byte $42 ; | X X |
BridgeB:
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $FF ; |XXXXXXXX|
.byte $42 ; | X X |
ShipB:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $FC ; |XXXXXX |
.byte $FF ; |XXXXXXXX|
.byte $30 ; | XX |
.byte $10 ; | X |
PlaneA:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $30 ; | XX |
.byte $4F ; | X XXXX|
.byte $C6 ; |XX XX |
.byte $00 ; | |
Heli1B:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $0E ; | XXX |
.byte $8E ; |X XXX |
.byte $FF ; |XXXXXXXX|
.byte $0E ; | XXX |
.byte $07 ; | XXX|
Heli0A:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $04 ; | X |
.byte $FF ; |XXXXXXXX|
.byte $9F ; |X XXXXX|
.byte $04 ; | X |
.byte $07 ; | XXX|
ShipA:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $7C ; | XXXXX |
.byte $FE ; |XXXXXXX |
.byte $78 ; | XXXX |
.byte $10 ; | X |
PlaneB:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $38 ; | XXX |
.byte $FF ; |XXXXXXXX|
.byte $80 ; |X |
Heli1A:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $04 ; | X |
.byte $FF ; |XXXXXXXX|
.byte $9F ; |X XXXXX|
.byte $04 ; | X |
.byte $1C ; | XXX |
Heli0B:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $0E ; | XXX |
.byte $8E ; |X XXX |
.byte $FF ; |XXXXXXXX|
.byte $0E ; | XXX |
.byte $1C ; | XXX |
Explosion1B:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $10 ; | X |
.byte $20 ; | X |
.byte $40 ; | X |
.byte $10 ; | X |
Explosion1A:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $04 ; | X |
.byte $02 ; | X |
.byte $08 ; | X |
.byte $04 ; | X |
.byte $00 ; | |
Explosion2B:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $20 ; | X |
.byte $02 ; | X |
.byte $41 ; | X X|
.byte $20 ; | X |
.byte $02 ; | X |
.byte $04 ; | X |
Explosion2A:
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |
.byte $04 ; | X |
.byte $88 ; |X X |
.byte $10 ; | X |
.byte $04 ; | X |
.byte $80 ; |X |
.byte $10 ; | X |
.byte $00 ; | |
.byte $00 ; | |
HouseB:
.byte $00 ; | |
.byte $04 ; | X |
.byte $1F ; | XXXXX|
.byte $0E ; | XXX |
.byte $04 ; | X |
.byte $04 ; | X |
.byte $00 ; | |
.byte $AA ; |X X X X |
.byte $FE ; |XXXXXXX |
.byte $7C ; | XXXXX |
.byte $00 ; | |
HouseA:
.byte $00 ; | |
.byte $04 ; | X |
.byte $0E ; | XXX |
.byte $1F ; | XXXXX|
.byte $0E ; | XXX |
.byte $04 ; | X |
.byte $00 ; | |
.byte $FE ; |XXXXXXX |
.byte $AA ; |X X X X |
.byte $FE ; |XXXXXXX |
.byte $38 ; | XXX |
.byte $00 ; | |
.byte $00 ; | |
.byte $00 ; | |

;===============================================================================
; R O M - C O D E (Part 4)
;===============================================================================

GetPageFlag SUBROUTINE
; get bit 0 of the page for the playfield data:
TXA ;2
BEQ .exit ;2
LDA #%0 ;2
CPX #SWITCH_PAGE_ID ;2 PF id < 9
BCS .exit ;2 no, read data from page $FC
LDA #%1 ;2 yes, read data from page $FD
.exit:
RTS ;6

SetScorePtrs SUBROUTINE
STA scorePtr1+10 ;3
STA scorePtr2+10 ;3

; let score-pointers point to 'Space' to avoid leading zeros:


LDA #<Space ;2
LDX #8 ;2
.loopScorePtr2:
STA scorePtr2,X ;4
DEX ;2
DEX ;2
BPL .loopScorePtr2 ;2

LDX #8 ;2
SetScorePtr1:
STA scorePtr1,X ;4
DEX ;2
DEX ;2
BPL SetScorePtr1 ;2
RTS ;6

;===============================================================================
; R O M - T A B L E S (Part 4)
;===============================================================================
ColorPtrTab:
.byte $49 ; explosion 0 (no extra data for explosion
colors)
.byte $2A ; explosion 1
.byte $66 ; explosion 2
.byte $2A ; explosion 3
.byte <PlaneCol-6 ; plane
.byte <HelicopterCol-4 ; helicopter
.byte <HelicopterCol-4 ; helicopter
.byte <ShipCol-4 ; ship
.byte <BridgeCol-1 ; bridge
.byte <HouseCol-2 ; house (one byte shared!)
.byte <FuelCol-1 ; fuel (=$37)

HouseCol:
.byte BROWN, LIGHT_GREEN, LIGHT_GREEN, LIGHT_GREEN, LIGHT_GREEN
.byte BLACK, LIGHT_GREY, LIGHT_GREY, BLACK, BLACK
FuelCol:
.byte LIGHT_GREY, LIGHT_GREY, LIGHT_GREY, RED, RED, RED
.byte LIGHT_GREY, LIGHT_GREY, LIGHT_GREY, RED, RED, RED
HelicopterCol:
.byte CYAN, CYAN, DARK_BLUE, CYAN, ORANGE, ORANGE
IF NTSC
PlaneCol:
.byte $AC, $9C, $8C
ShipCol:
.byte $A8, $32, BLACK, BLACK
BridgeCol:
.byte $20, $14, $12, $14, $12, $18, $12, $14, $12, $14, $20
ELSE
PlaneCol:
.byte $BC, $BC, $9C
ShipCol:
.byte $98, $42, BLACK, BLACK
BridgeCol:
.byte $20, $24, $22, $24, $22, $28, $22, $24, $22, $24, $20
.byte $B4 ; unused
ENDIF

;===============================================================================
; R O M - C O D E (Part 5)
;===============================================================================

LooseJet SUBROUTINE
; called when player looses a life:
STY sound0Cnt ; 3
STA sound0Id ; 3
LDA blockLst ; 3
EOR blockLstEnd ; 3
AND #PF_COLOR_FLAG ; 2 dark section?
BEQ .skipRestartLevel ; 2 yes, skip
LDA sectionEnd ; 3 end of section?
BEQ .isEnd ; 2 yes, check restart of level
BIT blockLstEnd ; 3 road in new block?
BPL .skipRestartLevel ; 2 no, skip
JSR SaveSection ; 6 yes, goto next section
BNE .skipRestartLevel ; 3
.isEnd:
BIT blockLstEnd ; 3 PF_ROAD_FLAG set?
BMI .skipRestartLevel ; 2 yes, skip restart level
; restart level:
LDX level ; 3 limit level to 48
DEX ; 2
CPX #MAX_LEVEL-2 ; 2
BNE .skipLimit ; 2
LDX #MAX_LEVEL ; 2
.skipLimit:
STX level ; 3
LDA randomLoSave2 ; 3 retrieve saved random values
STA randomLoSave ; 3
LDA randomHiSave2 ; 3
STA randomHiSave ; 3
.skipRestartLevel:
LDA #<JetExplode-1 ; 2
STA shapePtr0 ; 3
.contFinish:
STA gameMode ; 3
LDA #NUM_LINES+20 ; 2 disable missile
STA missileY ; 3
RTS ; 6

FinishGame:
; called when the the game is not running:
LDA #$FF ; 2 disable al animations
STA frameCnt ; 3
BNE .contFinish ; 3

IF NTSC
FinishDigits SUBROUTINE
INY ; 2
STA WSYNC ; 3
STA HMOVE ; 3
STY GRP1 ; 3
STY GRP0 ; 3
STY GRP1 ; 3
LDY #14 ; 2 load line counter
SetColPx:
STA COLUP0 ; 3
STA COLUP1 ; 3
DoHMove:
STA WSYNC ; 3
STA HMOVE ; 3
RTS ; 6
ELSE
LoadPFPattern SUBROUTINE
BIT PF_State ; 3 ISLAND_FLAG set?
BPL .contPage1 ; 2 no, set current page-flag
TAY ; 2 yes, read new page-flag from
table
LDA PageFlagTab,Y ; 4
.contPage1:
ORA blockLstEnd ; 3
STA blockLstEnd ; 3
LDA BankPtrTab,X ; 4
CLC ; 2
ADC diffPF ; 3 adjust with difference between new
and prev PF id
STA PF2LstEnd ; 3
RTS ; 6
ENDIF

;===============================================================================
; R O M - T A B L E S (Part 5)
;===============================================================================

RoadColorTab:
.byte $04, $04, $08, $08, $08, $08, YELLOW, $08, $08, $08, $08 ; next two
bytes are shared
VolumeTab:
.byte $04, $04 ; next
byte ($07) is shared
EnemyIdTab:
.byte ID_SHIP, ID_HELI0, ID_SHIP, ID_HELI0, ID_PLANE, ID_SHIP, ID_HELI0,
ID_HELI0

shapePtr1aTab:
.byte <Explosion0-1, <Explosion1A-1, <Explosion2A-1, <Explosion1A-1
.byte <PlaneA-1, <Heli0A-1, <Heli1A-1, <ShipA-1, <BridgeA-1, <HouseA-1,
<FuelA-1

;===============================================================================
; R O M - C O D E (Part 6)
;===============================================================================

SwapPlayers SUBROUTINE
; swaps player variable blocks in two player game:
LDA gameVariation ;3 don't swap in one player game
BEQ .skipSwap ;2
EOR player ;3 change player
STA player ;3

LDX #3 ;2
.loopSwap0:
LDA player1State,X ;4
LDY player2State,X ;4
STA player2State,X ;4
STY player1State,X ;4
DEX ;2
BPL .loopSwap0 ;2

LDX #12-2 ;2
.loopSwap1:
LDA scorePtr1,X ;4
LDY scorePtr2,X ;4
STA scorePtr2,X ;4
STY scorePtr1,X ;4
DEX ;2
DEX ;2
BPL .loopSwap1 ;2
.skipSwap:
RTS ;6

;===============================================================================
; R O M - T A B L E S (Part 6)
;===============================================================================

ColorTab:
.byte 0, YELLOW, GREY, YELLOW+2, BLUE

.word START
.word 0

You might also like