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