Professional Documents
Culture Documents
Atari VCS 2600 River Raid Labeled Assembler Source Code
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