Atari VCS 2600 River Raid Labeled Assembler Source Code
Atari VCS 2600 River Raid Labeled Assembler Source Code
; 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
;===============================================================================
;===============================================================================
; C O N S T A N T S
;===============================================================================
; 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
; joystick bits:
MOVE_RIGHT = %00001000
MOVE_LEFT = %00000100
MOVE_DOWN = %00000010
MOVE_UP = %00000001
;===============================================================================
; Z P - V A R I A B L E S
;===============================================================================
; 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
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
; x-position missile:
INX ; 2
LDA missileX ; 3
JSR SetPosX ; 6 position missile
JSR DoHMove ; 6
STY PF0 ; 3 enable complete PF0 (y=$ff)
lowOffset:
CMP #4 ; 2
BCC endOffset ; 2
AND #%01 ; 2
ORA #%10 ; 2
endOffset:
.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
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
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:
IF NTSC
LDA #29 ; 2
ELSE
LDA #58 ; 2
ENDIF
LDY #%10000010 ; 2
STA WSYNC ; 3
STA TIM64T ; 4
STY VBLANK ; 3
.noPFCollision:
LDA blockLst,X ; 4
AND #~PATROL_FLAG ; 2 disable patrol mode (only
temporary)
.endChangeDir:
STA blockLst,X ; 4
.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:
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
.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
.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:
; 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
.noMaxOut:
STA scorePtr1,X ; 4
.noMissile:
LDX #$B4 ; 2 disable missile
BNE .directMissile ; 2
.skipCollisions:
.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
.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
.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
; 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
.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
.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
.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
.newId:
BCS .currentBigger ; 2
LDA prevPF1PatId ; 3
.currentBigger:
STA maxId ; 3 maxId cointains max(prevId, newId)
.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
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
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
;===============================================================================
; 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)
;===============================================================================
; 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 ; | |
align 256
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)
;===============================================================================
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|
;===============================================================================
; 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
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