; *** R I V E R R A I D *** ; Copyright 1982 Activision ; Designer: Carol Shaw ; Analyzed, labeled and commented ; by Thomas Jentzsch (JTZ) ; Last Update: 13.08.2001 (v0.9) ; Section generation: ; The river is divided into sections which are generated by random. The random ; number generator can generate 57337 different sections. Each section is ; divided into 16 blocks. The last block is the bridge. For each other block a ; random Id is generated, which defines the shape of the river. The river is ; randomly generated with or without islands. ; Each block is 32 lines high and is divided into two parts. Those parts are ; neccessary for the bridge only, because it is smaller than a whole block. ; ; All objects are also randomly generated. There is one object in each block. ; First the game randomly selects if a fuel tank, an enemy object (ship, plane ; or helicopter) or a house should be generated. Then a starting x-position is ; defined and finally the direction (left/right) of the object is set. ; The ships and helicopters start patroling also randomly. ; ; Kernel: ; The main display is 160 lines tall. Below is the state display (score, fuel, ; lives and copyright). ; The kernel displays five or six of the section blocks. When a block leaves ; the display, it is replaced on the fly by a new generated one. ; It's basically a two line kernel, which is repeated twelve times. After that, ; there is code for eight more lines, where the block is iterated and all new ; parameter are set. (12 * 2 + 8 = 32!) ; The parameters for each block are: ; - a pointer to the current playfield pattern ; - a flag for a bright or dark green playfield ; - two pointers for the current object, the data is displayed interlaced, ; this gives in a single line resolution here ; - a color pointer for the object ; - x-positioning values for the object ; - some flags for the object (size, reflection...) ; All collisons are checked for each displayed block and only the hardware ; collision registers are used for that (no software calculations). ; Misc: ; - There are no score variables, the score is stored and changed directly in ; the display pointers! ; - The are no variables to store the x-positions of the objects, all ; calculations are done directly with the position values. ; (There aren't any BCD calculations in this code!) ; - All variables for the players are swapped, when the players change. ; - The game speeds aren't adjusted for PAL, the PAL game run's about 16% slower. ; (This seems to be true for most PAL conversions.) ; - ... processor 6502 include vcs.h ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== FILL_OPT = 1 ; fill optimized bytes with NOPs SCREENSAVER = 1 ; compile with screensaver code TRAINER = 0 ; enable training mode NTSC = 1 ; compile for NTSC ;=============================================================================== ; C O N S T A N T S ;=============================================================================== ; initial values for the random number generator: SEED_LO = $14 ; change "to go, where no one has gone before" :) SEED_HI = $A8 ; color constants: BLACK = $00 GREY = $06 ORANGE = $2A IF NTSC YELLOW = $1C RED = $48 BLUE = $84 CYAN = $B0 GREEN = $D2 ELSE YELLOW = $2C RED = $68 BLUE = $B4 CYAN = $70 GREEN = $52 ENDIF DARK_RED = RED - $6 LIGHT_GREEN = GREEN + $8 BROWN = YELLOW - $C LIGHT_GREY = GREY + $6 DARK_BLUE = BLUE - $4 ; main game constants: NUM_BLOCKS = 6 ; max. number of block on screen SECTION_BLOCKS = 16 ; number of blocks/stage BLOCK_PARTS = 2 ; each block has two parts BLOCK_SIZE = 32 ; number of lines/block NUM_LINES = 160 ; number of lines in main kernel MAX_LEVEL = 48 ; number of difficulty levels DIGIT_H = 8 ; height of the score digits JET_Y = 19 ; fixed y-position for jet MIN_MISSILE = JET_Y-6 ; starting position of player missile MAX_MISSILE = NUM_LINES+1 MISSILE_SPEED = 6 ; y-speed of the jet missile ROAD_HEIGHT = 13 ; number of lines for road INTRO_SCROLL = 48 ; counter for scrolling into new game SWITCH_PAGE_ID = 9 ; first pattern id with data on different page ; constants for shape-ids: ID_EXPLOSION0 = 0 ; used for explosion end ID_EXPLOSION1 = 1 ID_EXPLOSION2 = 2 ID_EXPLOSION3 = 3 ID_PLANE = 4 ID_HELI0 = 5 ID_HELI1 = 6 ID_SHIP = 7 ID_BRIDGE = 8 ID_HOUSE = 9 ID_FUEL = 10 ; flags for blockLst: PF1_PAGE_FLAG = %00000001 ; pattern for PF1 in page $FC or $FD PF2_PAGE_FLAG = %00000010 ; pattern for PF1 in page $FC or $FD PF_COLOR_FLAG = %00000100 ; bright or dark green PF PATROL_FLAG = %00010000 ; enemy is patroling (change directions) PF_COLLIDE_FLAG = %00100000 ; enemy collided with playfield ENEMY_MOVE_FLAG = %01000000 ; enemy is moving PF_ROAD_FLAG = %10000000 ; display road and bridge ; flags for State1Lst: DIRECTION_FLAG = %00001000 ; move direction of object FINE_MASK = %11110000 ; mask bits for HMxy NUSIZ_MASK = %00000111 ; mask bits for NUSIx ; flags for PF_State: ISLAND_FLAG = %10000000 ; island displayed in block CHANGE_FLAG = %01000000 ; begin or end of island (JTZ: this interpretation might be wrong) ; joystick bits: MOVE_RIGHT = %00001000 MOVE_LEFT = %00000100 MOVE_DOWN = %00000010 MOVE_UP = %00000001 ; values for ENAxy: DISABLE = %00 ENABLE = %10 ; value for enabling a missile ; values for NUSIZx: TWO_COPIES = %001 THREE_COPIES = %011 DOUBLE_SIZE = %101 QUAD_SIZE = %111 ; mask for SWCHB: BW_MASK = %1000 ; black and white bit ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== gameVariation = $80 ; one or two player game gameDelay = $81 ; delay before gameVariation changes frameCnt = $82 ; simple frame counter random = $83 ; 8 bit random number (used for: start of ship and helicopter, sound) joystick = $84 ; saved joystick value (?000rldu) IF SCREENSAVER SS_XOR = $85 ; change colors in screensaver mode (0/$01..$ff) SS_Mask = $86 ; darker colors in screensaver mode ($ff/$f7) ENDIF dXSpeed = $87 ; x-acceleration prevPF1PatId = $88 ; playfield pattern Id of the previous block PF_State = $89 ; io000000 sectionEnd = $8A ; 0 = end of section blockOffset = $8B ; offset into first displayed block posYLo = $8C ; low value of blockOffset bridgeExplode = $8D ; counter for bridge explosion ; the next 36 bytes are used to save all variables for six blocks: ;--------------------------------------- blockLst = $8E ; ..$93 flags for block definition blockLstEnd = blockLst+NUM_BLOCKS-1 ;--------------------------------------- XPos1Lst = $94 ; ..$99 coarse value for x-positioning of object XPos1LstEnd = XPos1Lst+NUM_BLOCKS-1 ;--------------------------------------- State1Lst = $9A ; ..$9F bit 0..2 = NUSIZ1, bit 3 = REFP1, 4..7 = fine move State1LstEnd = State1Lst+NUM_BLOCKS-1 ;--------------------------------------- Shape1IdLst = $A0 ;.. $A5 ids for object Shape1IdLstEnd = Shape1IdLst+NUM_BLOCKS-1 ;--------------------------------------- PF1Lst = $A6 ; ..$AB low pointer for PF1 data PF1LstEnd = PF1Lst+NUM_BLOCKS-1 ;--------------------------------------- PF2Lst = $AC ; ..$B1 low pointer for PF2 data PF2LstEnd = PF2Lst+NUM_BLOCKS-1 ;--------------------------------------- ; end of block variables missileY = $B2 ; y-position of player missile playerX = $B3 ; x-position of player jet speedX = $B4 ; x-speed of player jet speedY = $B5 ; y-speed of play jet blockPart = $B6 ; 1/2 (used for bridge) fuelHi = $B7 ; high value of fuel (displayed) fuelLo = $B8 ; low value of fuel sectionBlock = $B9 ; number of block in current section (16..1) shapePtr0 = $BA ; ..$BB pointer to the shape for the player jet PF1PatId = $BC ; playfield pattern Id for the new generated block ;--------------------------------------- player1State = $BD ; ..$C1 level = player1State ; difficulty level for current player (1..48) randomLoSave = player1State+1; saved random generator values for begin of level randomHiSave = player1State+2; livesPtr = player1State+3; ..$C1 ;--------------------------------------- player2State = $C2 ; ..$C5 livesPtr2 = player2State+3; the high pointer is not saved here, because it's const ;--------------------------------------- gameMode = $C6 ; 0 = running; -1 = game over; 1..48 = scroll into game shapePtr1a = $C7 ; ..$C8 shapePtr1b = $C9 ; ..$CA colorPtr = $CB ; ..$CC scorePtr1 = $CD ; ..$D8 12 bytes for the score display of current player PF1Ptr = $D9 ; ..$DA PF2Ptr = $DB ; ..$DC ;--------------------------------------- scorePtr2 = $DD ; ..$E7 12 bytes for the score display of other player ; the constant hi-pointers are temporary used: blockNum = scorePtr2+1 ; current block in kernel reflect0 = scorePtr2+3 ; flag for GRP0 (player jet) reflection hitEnemyIdx = scorePtr2+5 ; index of enemy that was hit by missile PFCrashFlag = scorePtr2+7 ; jet crashed into playfield missileFlag = scorePtr2+9 ; $ff means: missile enabled ;--------------------------------------- collidedEnemy = $E8 ; jet collided with enemy (id) randomLo = $E9 ; current number generator values randomHi = $EA randomLoSave2 = $EB ; saved number generator values for current player randomHiSave2 = $EC temp2 = $ED ; roadBlock = temp2 ; bit 7 = 1: road in block PFcolor = $EE ; color of river banks valleyWidth = PFcolor ; define minimum width of valley in first levels (6/0) playerColor = $EF ; YELLOW/BLACK stateBKColor = $F0 ; GREY (const!) statePFColor = $F1 ; YELLOW+2 (const!) temp = $F2 ; main temporary variable diffPF = temp ; difference between to PF pattern ids zero1 = $f3 ; always zero! player = $F4 ; 0/1 missileX = $F5 ; x-position of player missile zero2 = $F6 ; always zero! IF SCREENSAVER SS_Delay = $F7 ; screensaver delay ENDIF sound0Id = $F8 ; sound0Cnt = $F9 bridgeSound = $FA ; bridge is exploding missileSound = $FB ; missile fired temp3 = $FC blockLine = temp3 ; current displayed line of block in kernel maxId = temp3 lineNum = $FD ; counter for kernel lines ;=============================================================================== ; M A C R O S ;=============================================================================== MAC FILL_NOP IF FILL_OPT REPEAT {1} NOP REPEND ENDIF ENDM ;=============================================================================== ; R O M - C O D E (Part 1) ;=============================================================================== ORG $F000 START: SEI ; 2 CLD ; 2 LDX #0 ; 2 Reset: LDA #0 ; 2 .loopClear: STA $00,X ; 4 TXS ; 2 INX ; 2 BNE .loopClear ; 2 JSR SetScorePtrs ; 6 LDA #>Zero ; 2 LDX #12-1 ; 2 JSR SetScorePtr1 ; 6 set high-pointers to $FB LDX #colorPtr+1-PF1Lst; 2 #38 JSR GameInit ; 6 LDA random ; 3 BNE MainLoop ; 2 INC random ; 5 STA livesPtr ; 3 = 0! LDA #= 26? BCS .noShape ; 2 yes, skip enemy shape CPX #3 ; 2 blockoffset < 3? BCC .noShape ; 2 yes, skip enemy shape STA GRP1 ; 3 no, display enemy shape in first row LDA #0 ; 2 STA GRP0 ; 3 VDELP1! .noShape: LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 LDA (colorPtr),Y ; 5 IF SCREENSAVER EOR SS_XOR ; 3 AND SS_Mask ; 3 ELSE FILL_NOP 4 ENDIF STA COLUP1 ; 3 LDX blockNum ; 3 LDA blockLst,X ; 4 STA roadBlock ; 3 save road-state AND #PF_COLOR_FLAG ; 2 ORA #GREEN ; 2 IF SCREENSAVER EOR SS_XOR ; 3 AND SS_Mask ; 3 ELSE FILL_NOP 4 ENDIF STA PFcolor ; 3 BIT blockLstEnd ; 3 road in first block? BPL .noRoad ; 2 no, use green color CPY #ROAD_HEIGHT ; 2 offset inside road? BCS .noRoad ; 2 no, use green color LDA RoadColorTab,Y ; 4 yes, use road colors IF SCREENSAVER EOR SS_XOR ; 3 AND SS_Mask ; 3 ELSE FILL_NOP 4 ENDIF .noRoad: STA COLUPF ; 3 LDY #NUM_LINES ; 2 STY lineNum ; 3 .waitTim: LDA INTIM ; 4 BNE .waitTim ; 2 STA WSYNC ; 3 STA HMOVE ; 3 STA VBLANK ; 3 RTS ; 6 jump into kernel! ; *** main display kernel: *** DisplayKernel SUBROUTINE ; first some external code to save cycles in the kernel: JmpPoint2: ;12 INC lineNum ; 5 LDY blockLine ; 3 BPL enterKernel2 ; 3 .skipJet0: LDX zero2 ; 3 load 0 with exactly 3 cylces BEQ .contJet0 ; 3 .noRoad: LDA PFcolor ; 3 JMP .contPFColor ; 3 .doJet0a: LDA (shapePtr0),Y ; 5 TAX ; 2 LDA #$00 ; 2 .loopKernel1: ; @19 BEQ .contJet0a ; 2 this jump is taken when comming from .doJet0a BNE .contKernel1 ; 3 this jump is taken when comming from .loopkernel1 IF SCREENSAVER = 0 FILL_NOP 1 ENDIF JmpPoint3: ;12 JSR Wait12 ;12 .contKernel1: NOP ; 2 @26 ;-------------------------------------- ; even line: ; - ... ; - draw player jet ; - load new P1 shape ; *** here starts the main kernel loop: *** .loopKernel: ; CPY #JET_Y ; 2 draw player jet? BCS .skipJet0 ; 2 no, skip LDA (shapePtr0),Y ; 5 yes, load data.. TAX ; 2 ..into x .contJet0: LDY blockLine ; 3 BIT roadBlock ; 3 road displayed? BPL .noRoad ; 2 no, normal PF color LDA RoadColorTab,Y ; 4 yes, load road colors IF SCREENSAVER EOR SS_XOR ; 3 .contPFColor: AND SS_Mask ; 3 ELSE FILL_NOP 2 .contPFColor: FILL_NOP 1 ENDIF STA.w temp ; 4 LDA (shapePtr1b),Y ; 5 STA GRP1 ; 3 time doesn't matter (VDELP1!) LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 @75 ;-------------------------------------- ; new line starts here! ; odd line: ; - set PF color ; - set P1 color ; - change PF STA HMOVE ; 3 STX GRP0 ; 3 @2 this also updates GRP1 LDA temp ; 3 STA COLUPF ; 3 @8 LDA (colorPtr),Y ; 5 IF SCREENSAVER EOR SS_XOR ; 3 AND SS_Mask ; 3 ELSE FILL_NOP 4 ENDIF STA COLUP1 ; 3 @22 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 @30 enterKernel2: LDA (shapePtr1a),Y ; 5 STA GRP1 ; 3 time doesn't matter (VDELP1!) LDY lineNum ; 3 DEY ; 2 BEQ .exitKernel2 ; 2 CPY #JET_Y ; 2 BCC .doJet0a ; 2 TYA ; 2 SBC missileY ; 3 AND #$F8 ; 2 BNE .skipEnable0 ; 2 LDA #ENABLE ; 2 .skipEnable0: LDX #$00 ; 2 .contJet0a: DEY ; 2 STY lineNum ; 3 STA WSYNC ; 3 ;-------------------------------------- ; even line: ; - en-/disable missile ; - update P0 and P1 graphics ; - decrease block-line ; - ... STA HMOVE ; 3 STA ENAM0 ; 3 STX GRP0 ; 3 @6 this also updates GRP1 BEQ .exitKernel2 ; 2 DEC blockLine ; 5 BNE .loopKernel1 ; 2 ;*** start of next block (requires eight extra kernel lines): *** ; new block, line 1 ; - dec block-number ; - set new road-state ; - get new PF color ; - set new shape-pointer 1a DEC blockNum ; 5 JmpPoint1: LDX blockNum ; 3 BMI LF202 ; 2 LDA blockLst,X ; 4 save road-state STA roadBlock ; 3 AND #PF_COLOR_FLAG ; 2 bright or dark.. ORA #GREEN ; 2 ..green IF SCREENSAVER EOR SS_XOR ; 3 AND SS_Mask ; 3 ELSE FILL_NOP 4 ENDIF STA PFcolor ; 3 LDA Shape1IdLst,X ; 4 set TAX ; 2 shape-pointer LDA shapePtr1aTab,X ; 4 for the STA shapePtr1a ; 3 next enemy LF1CE: LDA #$00 ; 2 STA GRP1 ; 3 CPY #JET_Y ; 2 STA WSYNC ; 3 ;-------------------------------------- ; new block, line 2 ; x = shape-id ; - set jet ; - set PF ; - set new shape-pointer 1b ; - set new color-pointer STA HMOVE ; 3 BCS .skipJet1 ; 2 LDA (shapePtr0),Y ; 5 .skipJet1: STA GRP0 ; 3 LDY #0 ; 2 display last line of playfield pattern LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 LDY lineNum ; 3 DEY ; 2 .exitKernel2: BEQ .exitKernel1 ; 2 LDA shapePtr1bTab,X ; 4 STA shapePtr1b ; 3 LDA ColorPtrTab,X ; 4 STA colorPtr ; 3 JmpPoint0: CPY #JET_Y ; 2 BCS .skipJet2 ; 2 LDA (shapePtr0),Y ; 5 TAX ; 2 LDA #DISABLE ; 2 BEQ .contJet2 ; 3 LF202: INX ; 2 BEQ LF1CE ; 2 JmpPoint9: NOP ; 2 SEC ; 2 BCS .enterKernel9 ; 3 .skipJet2: TYA ; 2 SBC missileY ; 3 AND #$F8 ; 2 BNE .skipEnable1 ; 2 LDA #ENABLE ; 2 .skipEnable1: LDX #$00 ; 2 .contJet2: STA WSYNC ; 3 ;-------------------------------------- ; new block, line 3 ; - en-/disabvle missile ; - set jet ; - set new PF pointers STA HMOVE ; 3 STA ENAM0 ; 3 STX GRP0 ; 3 DEY ; 2 STY lineNum ; 3 .exitKernel1: BEQ .exitKernel ; 2 LDX blockNum ; 3 .enterKernel9: JSR SetPFxPtr ;50 LDA PFcolor ; 3 CPY #JET_Y ; 2 NOP ; 2 @76 ;-------------------------------------- ; new block, line 4 ; - set new PF color ; - set PF ; - load fine movement STA HMOVE ; 3 STA COLUPF ; 3 BCS .skipJet3 ; 2 LDA (shapePtr0),Y ; 5 STA GRP0 ; 3 .skipJet3: LDY #SECTION_BLOCKS-1 ; 2 LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 DEC lineNum ; 5 BEQ .exitKernel ; 2 JmpPoint8: LDA State1Lst,X ; 4 put fine move-value STA temp ; 3 into temp LDY lineNum ; 3 CPY #JET_Y ; 2 BCC .skipJet4 ; 2 TYA ; 2 SBC missileY ; 3 AND #$F8 ; 2 BNE .skipEnable2 ; 2 LDA #ENABLE ; 2 .skipEnable2: LDY #0 ; 2 .contJet4: STA WSYNC ; 3 ;-------------------------------------- ; new block, line 5 ; - en-/disable missile ; - set jet ; - position new shape STA HMOVE ; 3 STA ENAM0 ; 3 STY GRP0 ; 3 ; position player 1: LDA XPos1Lst,X ; 4 load coarse move-value BEQ .posVeryLeft ; 2 TAX ; 2 CPX #7 ; 2 BCS .posRight ; 2 .waitLeft: DEX ; 2 BNE .waitLeft ; 2 STA RESP1 ; 3 .contLeft: DEC lineNum ; 5 LDY lineNum ; 3 BNE .contPos ; 2 .exitKernel: JMP DisplayState ; 3 exit the kernel .posVeryLeft: NOP ; 2 NOP ; 2 LDA #$60 ; 2 STA RESP1 ; 3 STA HMP1 ; 3 BNE .contLeft ; 2 .skipJet4: LDA (shapePtr0),Y ; 5 TAY ; 2 LDA #$00 ; 2 BEQ .contJet4 ; 2 .posRight: SBC #4 ; 2 TAX ; 2 DEC lineNum ; 5 LDY lineNum ; 3 BEQ .exitKernel ; 2 .waitRight: DEX ; 2 BPL .waitRight ; 2 STA RESP1 ; 3 JmpPoint7: .contPos: STA WSYNC ; 3 ;-------------------------------------- ; new block, line 6 STA HMOVE ; 3 CPY #JET_Y ; 2 BCS .skipJet5 ; 2 LDA (shapePtr0),Y ; 5 STA GRP0 ; 3 .skipJet5: LDY #SECTION_BLOCKS-2 ; 2 LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 LDY lineNum ; 3 DEY ; 2 BEQ .exitKernel ; 2 LDX blockNum ; 3 LDA temp ; 3 STA HMP1 ; 3 JmpPoint6: LDA #[BLOCK_SIZE-8]/2 ; 2 STA blockLine ; 3 TYA ; 2 SEC ; 2 SBC missileY ; 3 AND #$F8 ; 2 BNE .skipEnable3 ; 2 LDA #ENABLE ; 2 .skipEnable3: CPY #JET_Y ; 2 STA WSYNC ; 3 ;-------------------------------------- ; new block, line 7 STA HMOVE ; 3 STA ENAM0 ; 3 BCS .skipJet6 ; 2 LDA (shapePtr0),Y ; 5 STA GRP0 ; 3 .skipJet6: LDA State1Lst,X ; 4 STA NUSIZ1 ; 3 STA REFP1 ; 3 DEY ; 2 STY lineNum ; 3 BEQ DisplayState ; 2 STA HMCLR ; 3 ; check collisions: ; (the collsion check between jet or missile and playfield aren't ; really neccessary for each block, but the collison registers ; are cleared after each block) INX ; 2 BIT CXM0P-$30 ; 3 player missile hit enemy? BPL .notHit ; 2 STX hitEnemyIdx ; 3 save block number .notHit: IF TRAINER BIT zero1 ELSE BIT CXP0FB-$30 ; 3 jet hit PF? ENDIF BPL .noPFCrash ; 2 STX PFCrashFlag ; 3 .noPFCrash: IF TRAINER BIT zero1 ELSE BIT CXM0FB-$30 ; 3 player missile hit PF? ENDIF BPL .notHitPF ; 2 STX missileFlag ; 3 .notHitPF: IF TRAINER BIT zero1 ELSE BIT CXPPMM-$30 ; 3 jet crashed into enemy? ENDIF BPL .noCrash ; 2 STX collidedEnemy ; 3 save block number .noCrash: .enterKernel5: ;-------------------------------------- ; new block, line 8 STA WSYNC ; 3 STA HMOVE ; 3 CPY #JET_Y ; 2 BCS .skipJet7 ; 2 LDA (shapePtr0),Y ; 5 STA GRP0 ; 3 .skipJet7: LDY #SECTION_BLOCKS-3 ; 2 LDA (PF1Ptr),Y ; 5 STA PF1 ; 3 LDA (PF2Ptr),Y ; 5 STA PF2 ; 3 LDY lineNum ; 3 DEY ; 2 BEQ DisplayState ; 2 BIT CXP1FB-$30 ; 3 enemy hit PF? BPL .notEnemyPF ; 2 no, skip LDA blockLst,X ; 4 ORA #PF_COLLIDE_FLAG ; 2 yes, set collision flag STA blockLst,X ; 4 .notEnemyPF: STA CXCLR ; 3 clear all collison registers .enterKernel4: TYA ; 2 SEC ; 2 SBC missileY ; 3 AND #$F8 ; 2 BNE .skipEnable4 ; 2 LDA #ENABLE ; 2 .skipEnable4: CPY #JET_Y ; 2 STA WSYNC ; 3 ;-------------------------------------- ; new block, line 9 (= begin of even line) STA HMOVE ; 3 STA ENAM0 ; 3 BCS .skipJet8 ; 2 LDA (shapePtr0),Y ; 5 STA GRP0 ; 3 .contJet8: DEY ; 2 STY lineNum ; 3 BEQ DisplayState ; 2 exit the kernel JMP .loopKernel ; 3 @26 JmpPoint5: LDA #[BLOCK_SIZE-8]/2 ; 2 STA blockLine ; 3 BNE .enterKernel5 ; 3 JmpPoint4: LDA #[BLOCK_SIZE-8]/2 ; 2 12 STA blockLine ; 3 BNE .enterKernel4 ; 3 .skipJet8: ; waste some time NOP ; 2 NOP ; 2 BCS .contJet8 ; 3 DisplayState SUBROUTINE ; finish display kernel: STA WSYNC ; 3 STA HMOVE ; 3 LDY #$00 ; 2 STY GRP1 ; 3 STY GRP0 ; 3 LDA zero1 ; 3 waste one extra cylce (but also wastes a variable!) STA COLUBK ; 3 STY PF0 ; 3 STY PF1 ; 3 STY PF2 ; 3 STY REFP0 ; 3 STY REFP1 ; 3 STY reflect0 ; 3 ; prepare state display: LDA #$11 ; 2 reflect PF, 2 pixel ball width, also for HMP0! STA RESP0 ; 3 STA RESP1 ; 3 STA CTRLPF ; 3 STA HMP0 ; 3 LDA #$20 ; 2 STA HMP1 ; 3 LDA playerColor ; 3 JSR SetColPx ; 6 LDA stateBKColor ; 3 STA COLUBK ; 3 LDA #THREE_COPIES ; 2 STA NUSIZ0 ; 3 STA NUSIZ1 ; 3 LDA statePFColor ; 3 STA COLUPF ; 3 LDY #$07 ; 2 STY VDELP0 ; 3 STY lineNum ; 3 STA HMCLR ; 3 ; display score: .loopScore: LDA (scorePtr1+8),Y ; 5 TAX ; 2 LDA (scorePtr1+10),Y ; 5 STA WSYNC ; 3 STA HMOVE ; 3 STY temp2 ; 3 STA temp ; 3 LDA (scorePtr1),Y ; 5 STA GRP0 ; 3 LDA (scorePtr1+2),Y ; 5 STA GRP1 ; 3 LDA (scorePtr1+4),Y ; 5 STA GRP0 ; 3 LDA (scorePtr1+6),Y ; 5 LDY temp ; 3 STA GRP1 ; 3 STX GRP0 ; 3 STY GRP1 ; 3 STA GRP0 ; 3 LDY temp2 ; 3 DEY ; 2 BPL .loopScore ; 2 LDA zero1 ; 3 a = 0 (BLACK) JSR FinishDigits ; 6 y = 14 ; display fuel: .loopFuel: STY temp2 ; 3 line counter LDA FuelTab4,Y ; 4 LDX FuelTab3,Y ; 4 STA WSYNC ; 3 STA HMOVE ; 3 STA temp ; 3 NOP ; 2 LDA #$00 ; 2 STA GRP0 ; 3 LDA ENABLTab,Y ; 4 STA ENABL ; 3 LDA FuelTab0,Y ; 4 STA GRP1 ; 3 LDA FuelTab1,Y ; 4 STA GRP0 ; 3 LDA FuelTab2,Y ; 4 LDY temp ; 3 STA GRP1 ; 3 STX GRP0 ; 3 STY GRP1 ; 3 STA GRP0 ; 3 LDY temp2 ; 3 DEY ; 2 BPL .loopFuel ; 2 LDA playerColor ; 3 JSR FinishDigits ; 6 INY ; 2 y=15 CLC ; 2 LDX gameMode ; 3 INX ; 2 BNE .noGame ; 2 LDA #= 160? BCC .contMoveX ; 2 no, continue LDY #0 ; 2 yes,.. TYA ; 2 ..move shape (plane).. BEQ .contMoveX ; 2 ..to the very left (0) .xMoveLeft: SBC #1 ; 2 BCS .contMoveX2 ; 2 ADC #15 ; 2 DEY ; 2 < 0? BPL .contMoveX ; 2 no, continue LDY #10 ; 2 yes, move shape (plane).. LDA #9 ; 2 ..to the very right (159) .contMoveX: STY XPos1Lst,X ; 4 store coarse value here .contMoveX2: EOR #$07 ; 2 JSR Mult16 ; 6 EOR State1Lst,X ; 4 AND #FINE_MASK ; 2 #$F0 EOR State1Lst,X ; 4 STA State1Lst,X ; 4 OR fine value into here .skipMoveEnemy: ; clear PF-collision: LDA blockLst,X ; 4 AND #~PF_COLLIDE_FLAG ; 2 STA blockLst,X ; 4 DEX ; 2 BMI .exitLoopEnemies ; 2 JMP .loopEnemies ; 3 .exitLoopEnemies: ; *** read joystick: *** LDA SWCHA ; 4 LDX player ; 3 BEQ .player1 ; 2 JSR Mult16 ; 6 .player1: AND #$F0 ; 2 mask out other player joystick TAX ; 2 LDY #4 ; 2 .loopBits: ROL ; 2 roll new 4 bits into joystick ROL joystick ; 5 (old 4 bits got into upper nibble!) DEY ; 2 BNE .loopBits ; 2 CPX #$F0 ; 2 BNE .joystickMoved ; 2 LDX player ; 3 LDA INPT4-$30,X ; 4 BMI .noFire ; 2 .joystickMoved: LDA gameMode ; 3 CMP #INTRO_SCROLL ; 2 BNE .skipRestart ; 2 LDA #64 ; 2 restart new game STA speedY ; 3 STY gameMode ; 3 y=0! .skipRestart: IF SCREENSAVER STY SS_Delay ; 3 y=0! ELSE FILL_NOP 2 ENDIF .noFire: LDX gameMode ; 3 BEQ .checkCollisions ; 2 game running BMI .gameOver ; 2 game is over CPX #INTRO_SCROLL+1 ; 2 scrolling into new game/life? BCC .doSoundJmp ; 2 yes, skip decrease BNE .startGame ; 2 no, finished scrolling, start new game ; decrease lives: LDA livesPtr ; 3 BEQ .finishGame ; 2 zero! SBC #DIGIT_H ; 2 BNE LF5B5 ; 2 LDA # max. speed = 3 lines/frame STA blockNum ; 3 .loopNext: DEC blockNum ; 5 BMI .mainLoopJmp ; 2 LDA speedY ; 3 CMP #$FE ; 2 maximum speed? BCS .incOffset ; 2 yes, increase offset ADC posYLo ; 3 STA posYLo ; 3 BCC .loopNext ; 2 .incOffset: INC blockOffset ; 5 LDA blockOffset ; 3 CMP #BLOCK_SIZE ; 2 BCC .loopNext ; 2 ; *** it#s time to create a new block: *** LDX #0 ; 2 STX blockOffset ; 3 LDY #NUM_BLOCKS ; 2 STY temp ; 3 move 6 blocks LDA level ; 3 CMP #5 ; 2 first four levels? BCC .firstLevels ; 2 yes, prevent small valley LDY #0 ; 2 no, allow all widths of valley .firstLevels: STY valleyWidth ; 3 0 = all widths allowed, 6 = limited widths ; first move the other blocks, to make space for the new one: .loopBlocks: ; LDY #5 ; 2 move 5 bytes .loopMoveBlock: LDA blockLst+1,X ; 4 STA blockLst,X ; 4 INX ; 2 DEY ; 2 BNE .loopMoveBlock ; 2 INX ; 2 skip one entry DEC temp ; 5 BNE .loopBlocks ; 2 STY State1LstEnd ; 3 y=0! LDA blockLstEnd ; 3 clear variable (except PF_COLOR_FLAG) AND #PF_COLOR_FLAG ; 2 STA blockLstEnd ; 3 LDX PF1PatId ; 3 copy previous PF pattern id STX prevPF1PatId ; 3 DEC blockPart ; 5 second part of block? BEQ .nextBlock ; 2 yes, next block LDX sectionBlock ; 3 DEX ; 2 first part of last block of section? BNE .notLast ; 2 no, continue part ; the last block of a section has to be a road with bridge: STX sectionEnd ; 3 yes, end of current section LDA level ; 3 LSR ; 2 straight current level? LDA #PF_ROAD_FLAG ; 2 BCS .isStraight ; 2 yes, dark green in NEXT level LDA #PF_ROAD_FLAG|PF_COLOR_FLAG; 2 no, lighter green in NEXT level .isStraight: STA blockLstEnd ; 3 .notLast: JSR NextRandom16 ; 6 new random number for next part of block JMP .nextBlockPart ; 3 ; continue with a 'normal' block: .nextBlock: DEC sectionBlock ; 5 last block of section? BNE .contSection ; 2 no, continue JSR SaveSection ; 6 yes, save variables.. LDX #SECTION_BLOCKS ; 2 ..and got next level STX sectionBlock ; 3 .contSection: JSR NextRandom16 ; 6 new random number for next block LDX sectionBlock ; 3 DEX ; 2 last block of section? BNE .notLastBlock ; 2 no, skip STX PF_State ; 3 yes, PF-State = static LDA #12 ; 2 pattern-id for last block (with bridge) BNE .setPF1Id ; 3 .notLastBlock: LDA level ; 3 LSR ; 2 straight level? LDA #7 ; 2 pattern-id for straight block BCS .setPF1Id ; 2 yes, set LDA PF_State ; 3 DEX ; 2 last but one block of section? BNE .notLastButOne ; 2 no, skip ; finish island before end of section: CMP #ISLAND_FLAG|CHANGE_FLAG; 2 both flags set? BEQ .isSetBoth ; 2 yes, 11 -> 10 (1. step to finish island) BNE .clearBoth ; 3 no, static PF and no island (2. step to finish island) ; change PF_State bits 7 & 6: ; 00 -> 01/00 static -> changing or static ; 01 -> 11 changing -> island & changing ; 10 -> 00 island & static -> static (JTZ: ???) ; 11 -> 10/11 island & changing -> island & changing or static .notLastButOne: ASL ; 2 EOR PF_State ; 3 CHANGE_FLAG != ISLAND_FLAG? BMI .updateFlags ; 2 yes, change flags LDA randomLo ; 3 randomly change state? AND #%00110000 ; 2 BNE .skipFlags ; 2 no, don't change state (75%) .isSetBoth: LDA PF_State ; 3 AND #ISLAND_FLAG ; 2 ISLAND_FLAG set? BNE .isIsland ; 2 yes, clear CHANGE_FLAG ORA #CHANGE_FLAG ; 2 no, set CHANGE_FLAG .isIsland: STA PF_State ; 3 LDA #0 ; 2 BEQ .setPF1Id ; 3 .updateFlags: ; change flags: 01 -> 11, 10 -> 00 LDA #ISLAND_FLAG|CHANGE_FLAG; 2 BIT PF_State ; 3 CHANGE_FLAG set? BVS .setBoth ; 2 yes, set ISLAND_FLAG .clearBoth: LDA #0 ; 2 no, clear both flags .setBoth: STA PF_State ; 3 .skipFlags: ; create new random PF id: ; (JTZ: I'm not 100% sure, that I understand everything completely) LDY #14 ; 2 y = 14 LDA randomLo ; 3 AND #$0F ; 2 CMP #2 ; 2 BCS .minOk ; 2 ADC #2 ; 2 minimum = 2 .minOk: ; a = 2..15 BIT PF_State ; 3 ISLAND_FLAG set? BPL .skipDey ; 2 no, skip DEY ; 2 y = 13 .skipDey: LDX valleyWidth ; 3 all widths allowed? BEQ .allWidths ; 2 yes, skip limit LDY #8 ; 2 y = 8 .allWidths: STY temp ; 3 save max. allowed id CMP temp ; 3 random id < max. id? BCC .setPF1Id ; 2 yes, skip LDA temp ; 3 no, use max. id .setPF1Id: STA PF1PatId ; 3 a = 2..8 or 2..13/14 LDY #BLOCK_PARTS ; 2 reset blockPart STY blockPart ; 3 .nextBlockPart: LDA prevPF1PatId ; 3 TAX ; 2 SEC ; 2 SBC PF1PatId ; 3 STA diffPF ; 3 store the difference between the two blocks BCS .biggerPrev ; 2 ; new id is bigger: INC diffPF ; 5 CPX #SWITCH_PAGE_ID-1 ; 2 LDX PF1PatId ; 3 BCS .prevBigId ; 2 CPX #SWITCH_PAGE_ID ; 2 BCC .page1Id ; 2 LDA #-1 ; 2 ADC prevPF1PatId ; 3 CF=1! (JTZ: what's that good for?) BPL .prevId ; 3 ; old id is bigger or equal: .biggerPrev: BEQ .equalId ; 2 DEC diffPF ; 5 -1 .equalId: CPX #SWITCH_PAGE_ID ; 2 BCS .page0Id ; 2 ; not enough space for an island: .page1Id: JSR GetPageFlag ; 6 JSR LoadPFPattern ; 6 a = 0/1 STA PF1LstEnd ; 3 LDA #0 ; 2 STA PF2LstEnd ; 3 BEQ .contPage1 ; 3 ; enough space for an island in previous block: .page0Id: LDA PF1PatId ; 3 CMP #SWITCH_PAGE_ID-1 ; 2 BCS .prevBigId ; 2 ; enough space for an island in both blocks: LDA #14+1 ; 2 SBC PF1PatId ; 3 CF=0! BCS .prevId ; 3 negate id (inverts pattern) .prevBigId: LDA #PF1_PAGE_FLAG|PF2_PAGE_FLAG|PF_COLOR_FLAG; 2 .prevId: STA PF1LstEnd ; 3 JSR GetPageFlag ; 6 SEC ; 2 ROL ; 2 JSR LoadPFPattern ; 6 a = 1/3 .contPage1: BIT PF_State ; 3 ISLAND_FLAG set? BPL .skipSwapPF ; 2 no, don't swap LDA PF1LstEnd ; 3 LDX PF2LstEnd ; 3 STA PF2LstEnd ; 3 STX PF1LstEnd ; 3 .skipSwapPF: BIT blockLstEnd ; 3 PF_ROAD_FLAG set? BPL .skipRoad ; 2 no, skip LDA #QUAD_SIZE ; 2 yes, create road block STA State1LstEnd ; 3 quad size bridge LDY #ID_BRIDGE ; 2 LDA #63 ; 2 x-position JMP .endNewShape ; 3 .skipRoad: ; *** create new objects: *** LDY #ID_FUEL ; 2 LDA sectionBlock ; 3 CLC ; 2 ADC blockPart ; 3 CMP #SECTION_BLOCKS+BLOCK_PARTS; 2 no enemies at first part of first block of section BCS .newHouse ; 2 ; create more enemies and less fuel in higher difficulty levels: LDA #64 ; 2 SBC level ; 3 1..48 (CF=0!) ASL ; 2 a = 124..30 CMP randomHi ; 3 BCC .newEnemy ; 2 ~48%..88% -> more enemies, less fuel and houses BIT randomLo ; 3 BVC .newFuel ; 2 ~24%.. 6% -> less fuel ; no enemy or fuel, create new house instead: .newHouse: DEY ; 2 y=ID_HOUSE LDX PF1PatId ; 3 CPX prevPF1PatId ; 3 BCC .currentSmaller ; 2 LDX prevPF1PatId ; 3 .currentSmaller: ; x = smaller id LDA #DOUBLE_SIZE ; 2 house is double sized STA State1LstEnd ; 3 LDA level ; 3 LSR ; 2 BCC .notStraight ; 2 ; create random x-position for house in straight section: LDA randomLo ; 3 AND #$1F ; 2 ADC #8 ; 2 CMP #25 ; 2 random position fits in left bank? BCC .setShapeDir ; 2 yes, ok ADC #92 ; 2 no, position house on right bank BNE .setShapeDir ; 3 ; position house in non-straight section: .notStraight: LDA ShapePosTab,X ; 4 x-pos based on PF1 id BIT PF_State ; 3 ISLAND_FLAG set? BPL .setShapeDir ; 2 no, skip CPX #0 ; 2 PF id = 0? BEQ .setShapeDir ; 2 no, skip LDA #71 ; 2 fixed position for a house on island BNE .setShapeDir ; 3 ; create new ship, helicopter or plane: .newEnemy: LDA #%111 ; 2 LDX level ; 3 CPX #3 ; 2 enemy planes start at level three BCS .withPlanes ; 2 LDA #%001 ; 2 limit first levels to ship and helicopter .withPlanes: AND randomHi ; 3 create random enemy object TAX ; 2 LDY EnemyIdTab,X ; 4 .newFuel: CPY #ID_SHIP ; 2 BNE .noShip ; 2 LDA #DOUBLE_SIZE ; 2 doublesize STA State1LstEnd ; 3 .noShip: LDA PF1PatId ; 3 CMP prevPF1PatId ; 3 new pat-id = previous pat-id? BNE .newId ; 2 no, ; position object in straight blocks: STA maxId ; 3 LDA level ; 3 LSR ; 2 BCC .notStraight2 ; 2 ; position object in straight section: LDA #106 ; 2 LDX State1LstEnd ; 3 ship? (doublesize) BEQ .isShip ; 2 yes, position more right LDA #97 ; 2 no, position more left .isShip: SBC valleyWidth ; 3 decrease maximum position (-6) in first four levels, ; this avoids positioning near the river bank STA temp ; 3 store maximum position LDA randomLo ; 3 AND #$3F ; 2 ADC #45 ; 2 ADC valleyWidth ; 3 increase random position in first four levels (s.a.) CMP temp ; 3 random position < maximum? BCC .setShapeDir ; 2 yes, ok LDA temp ; 3 no, position = maximum .setShapeDir: ; make random direction for new shape: BIT randomLo ; 3 BMI .invertDirection ; 2 BPL .endNewShape ; 3 .newId: BCS .currentBigger ; 2 LDA prevPF1PatId ; 3 .currentBigger: STA maxId ; 3 maxId cointains max(prevId, newId) ; position object in non-straight section: .notStraight2: ; check, if there is enough space for new object: LDX #13 ; 2 PF id BIT PF_State ; 3 ISLAND_FLAG set? BPL .contPage12 ; 2 no, skip LDX #10 ; 2 yes, lower PF id .contPage12: CPX maxId ; 3 BCS .spaceOk ; 2 TYA ; 2 SBC #ID_SHIP-1 ; 2 new enemy is a ship? BNE .spaceOk ; 2 no, skip STA State1LstEnd ; 3 yes, change.. DEY ; 2 ..ship into helicopter .spaceOk: LDA maxId ; 3 ASL ; 2 ASL ; 2 BEQ .posSomewhere ; 2 BIT PF_State ; 3 ISLAND_FLAG set? BPL .posSomewhere ; 2 no, position somewhere ; position object outside: EOR #$FF ; 2 ADC #81 ; 2 BIT randomLo ; 3 BPL .skipNeg ; 2 EOR #$FF ; 2 ADC #160 ; 2 BNE .contPos ; 3 ; position object somewhere: .posSomewhere: ADC #16 ; 2 BIT randomLo ; 3 BMI .doNeg ; 2 .contPos: CLC ; 2 ADC #2 ; 2 ADC valleyWidth ; 3 keep space to river bank in first levels BNE .endNewShape ; 3 .doNeg: EOR #$FF ; 2 ADC #160+1 ; 2 .skipNeg: CPY #ID_FUEL ; 2 position fuel 1 pixel more right SBC #9 ; 2 SBC valleyWidth ; 3 keep space to river bank in first levels LDX State1LstEnd ; 3 double sized object? (ship, house) BEQ .invertDirection ; 2 no, skip SBC #10 ; 2 yes, move 10 pixels left .invertDirection: CPY #ID_FUEL ; 2 fuel? BEQ .endNewShape ; 2 yes, has constant direction PHA ; 3 LDA State1LstEnd ; 3 ORA #DIRECTION_FLAG ; 2 set direction flag STA State1LstEnd ; 3 PLA ; 4 .endNewShape: STY Shape1IdLstEnd ; 3 save id of new object JSR CalcPosX ; 6 STY XPos1LstEnd ; 3 save coarse x-positioning value ORA State1LstEnd ; 3 STA State1LstEnd ; 3 save fine x-positioning value JMP .loopNext ; 3 ; ****************************** end of main loop ****************************** GameInit SUBROUTINE ; Input: x (= 22/38, number of initialized variables) ; initializes some variables for new game: .initLoop: LDA InitTab,X ; 4 STA PF1Lst,X ; 4 DEX ; 2 BPL .initLoop ; 2 ; clear some variables for new game: LDA #0 ; 2 LDX #30 ; 2 .loopClear: STA dXSpeed,X ; 4 DEX ; 2 BPL .loopClear ; 2 LDX #NUM_BLOCKS-1 ; 2 LDY #PF1_PAGE_FLAG ; 2 LDA level ; 3 LSR ; 2 straight level? BCC .loopSet ; 2 no, skip LDY #PF1_PAGE_FLAG|PF_COLOR_FLAG; 2 yes, set brighter green in current level .loopSet: STY blockLst,X ; 4 DEX ; 2 BPL .loopSet ; 2 RTS ; 6 IF NTSC LoadPFPattern SUBROUTINE BIT PF_State ; 3 ISLAND_FLAG set? BPL .contPage1 ; 2 no, set current page-flag TAY ; 2 yes, read new page-flag from table LDA PageFlagTab,Y ; 4 .contPage1: ORA blockLstEnd ; 3 STA blockLstEnd ; 3 LDA BankPtrTab,X ; 4 load a pattern for the river bank CLC ; 2 ADC diffPF ; 3 adjust with difference between new and prev PF id STA PF2LstEnd ; 3 RTS ; 6 ELSE FinishDigits SUBROUTINE INY ; 2 STA WSYNC ; 3 STA HMOVE ; 3 STY GRP1 ; 3 STY GRP0 ; 3 STY GRP1 ; 3 LDY #14 ; 2 load line counter SetColPx: STA COLUP0 ; 3 STA COLUP1 ; 3 DoHMove: STA WSYNC ; 3 STA HMOVE ; 3 RTS ; 6 ENDIF NextRandom16 SUBROUTINE ; implements a 16 bit LFSR which generates a new random number: LDA randomHi ; 3 ASL ; 2 ASL ; 2 ASL ; 2 EOR randomHi ; 3 ASL ; 2 ROL randomLo ; 5 ROL randomHi ; 5 ; (JTZ: randomHi is very random, randomLo is NOT when more than one bit is used, ; because: randomLo[x+1] = randomLo[x]*2 + 0/1, but randomLo is used more often, ; randomHi only for new enemy and which. This could make the game a bit predictable.) RTS ; 6 SaveSection SUBROUTINE ; called at the start of a new section, increases difficulty level ; and saves random variables to be able to restart this section LDX level ; 3 limit level to 48 CPX #MAX_LEVEL ; 2 BCC .notMax ; 2 LDX #MAX_LEVEL-2 ; 2 go back to 47 .notMax: LDA randomLoSave ; 3 STA randomLoSave2 ; 3 LDA randomHiSave ; 3 STA randomHiSave2 ; 3 LDA randomLo ; 3 STA randomLoSave ; 3 LDA randomHi ; 3 STA randomHiSave ; 3 INX ; 2 STX level ; 3 1..48 RTS ; 6 SetPosX SUBROUTINE ; calculates the values and positions objects: JSR CalcPosX ; 6 SetPosX2: STA HMP0,X ; 4 INY ; 2 INY ; 2 INY ; 2 STA WSYNC ; 3 .waitPos: DEY ; 2 BPL .waitPos ; 2 STA RESP0,X ; 4 RTS ; 6 ;=============================================================================== ; R O M - T A B L E S (Part 1) ;=============================================================================== align 256 Zero: .byte $3C ; | XXXX | $FB00 .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $3C ; | XXXX | One: .byte $3C ; | XXXX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $38 ; | XXX | .byte $18 ; | XX | Two: .byte $7E ; | XXXXXX | .byte $60 ; | XX | .byte $60 ; | XX | .byte $3C ; | XXXX | .byte $06 ; | XX | .byte $06 ; | XX | .byte $46 ; | X XX | .byte $3C ; | XXXX | Three: .byte $3C ; | XXXX | .byte $46 ; | X XX | .byte $06 ; | XX | .byte $0C ; | XX | .byte $0C ; | XX | .byte $06 ; | XX | .byte $46 ; | X XX | .byte $3C ; | XXXX | Four: .byte $0C ; | XX | .byte $0C ; | XX | .byte $0C ; | XX | .byte $7E ; | XXXXXX | .byte $4C ; | X XX | .byte $2C ; | X XX | .byte $1C ; | XXX | .byte $0C ; | XX | Five: .byte $7C ; | XXXXX | .byte $46 ; | X XX | .byte $06 ; | XX | .byte $06 ; | XX | .byte $7C ; | XXXXX | .byte $60 ; | XX | .byte $60 ; | XX | .byte $7E ; | XXXXXX | Six: .byte $3C ; | XXXX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $7C ; | XXXXX | .byte $60 ; | XX | .byte $62 ; | XX X | .byte $3C ; | XXXX | Seven: .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $0C ; | XX | .byte $06 ; | XX | .byte $42 ; | X X | .byte $7E ; | XXXXXX | Eight: .byte $3C ; | XXXX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $3C ; | XXXX | .byte $3C ; | XXXX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $3C ; | XXXX | Nine: .byte $3C ; | XXXX | .byte $46 ; | X XX | .byte $06 ; | XX | .byte $3E ; | XXXXX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $66 ; | XX XX | .byte $3C ; | XXXX | MaxOut: .byte $18 ; | XX | .byte $18 ; | XX | .byte $00 ; | | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | .byte $18 ; | XX | Space: .byte $00 ; | | Copyright0: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $F7 ; |XXXX XXX| .byte $95 ; |X X X X| .byte $87 ; |X XXX| .byte $80 ; |X | .byte $90 ; |X X | .byte $F0 ; |XXXX | Copyright1: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $47 ; | X XXX| .byte $41 ; | X X| .byte $77 ; | XXX XXX| .byte $55 ; | X X X X| .byte $75 ; | XXX X X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | Copyright2: .byte $AD ; |X X XX X| .byte $A9 ; |X X X X| .byte $E9 ; |XXX X X| .byte $A9 ; |X X X X| .byte $ED ; |XXX XX X| .byte $41 ; | X X| .byte $0F ; | XXXX| .byte $00 ; | | .byte $03 ; | XX| .byte $00 ; | | .byte $4B ; | X X XX| .byte $4A ; | X X X | .byte $6B ; | XX X XX| .byte $00 ; | | .byte $08 ; | X | .byte $00 ; | | Copyright3: .byte $50 ; | X X | .byte $58 ; | X XX | .byte $5C ; | X XXX | .byte $56 ; | X X XX | .byte $53 ; | X X XX| .byte $11 ; | X X| .byte $F0 ; |XXXX | .byte $00 ; | | .byte $80 ; |X | .byte $80 ; |X | .byte $AA ; |X X X X | .byte $AA ; |X X X X | .byte $BA ; |X XXX X | .byte $22 ; | X X | .byte $27 ; | X XXX| .byte $02 ; | X | Copyright4: .byte $BA ; |X XXX X | .byte $8A ; |X X X | .byte $BA ; |X XXX X | .byte $A2 ; |X X X | .byte $3A ; | XXX X | .byte $80 ; |X | .byte $FE ; |XXXXXXX | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $11 ; | X X| .byte $11 ; | X X| .byte $17 ; | X XXX| .byte $15 ; | X X X| .byte $17 ; | X XXX| .byte $00 ; | | Copyright5: .byte $E9 ; |XXX X X| .byte $AB ; |X X X XX| .byte $AF ; |X X XXXX| .byte $AD ; |X X XX X| .byte $E9 ; |XXX X X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $77 ; | XXX XXX| .byte $54 ; | X X X | .byte $77 ; | XXX XXX| .byte $51 ; | X X X| .byte $77 ; | XXX XXX| PageFlagTab: .byte 0, PF2_PAGE_FLAG, PF1_PAGE_FLAG, PF1_PAGE_FLAG|PF2_PAGE_FLAG ; PF1_PAGE_FLAG unused! shapePtr1bTab: .byte PFPat0 ; 2 STA PF1Ptr+1 ; 3 LDA PF2Lst,X ; 4 STA PF2Ptr ; 3 LDA blockLst,X ; 4 LSR ; 2 AND #PF2_PAGE_FLAG>>1 ; 2 ORA #>PFPat0 ; 2 STA PF2Ptr+1 ; 3 RTS ; 6 = 44 ;=============================================================================== ; R O M - T A B L E S (Part 2) ;=============================================================================== ; high addresses of entry points into kernel: JmpHiTab: .byte >[JmpPoint0-1], >[JmpPoint1-1], >[JmpPoint2-1], >[JmpPoint3-1], >[JmpPoint4-1] .byte >[JmpPoint5-1], >[JmpPoint6-1], >[JmpPoint7-1], >[JmpPoint8-1], >[JmpPoint9-1] ; used to animate explosions and helicopter: AnimateIdTab: .byte 0 ; .byte ID_EXPLOSION2 ; start of explosion sequence .byte ID_EXPLOSION3 ; .byte ID_EXPLOSION0 ; end explosion with 0 .byte ID_PLANE ; no animation for plane .byte ID_HELI1 ; switch between.. .byte ID_HELI0 ; ..ID_HELI0 and ID_HELI1 ; these are the patterns, that are used to define the playfield: PFPat0: .byte $00 ; | | $FC00 .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $01 ; | X| .byte $03 ; | XX| .byte $07 ; | XXX| .byte $0F ; | XXXX| .byte $1F ; | XXXXX| PFPat14: .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $3F ; | XXXXXX| .byte $1F ; | XXXXX| .byte $0F ; | XXXX| .byte $07 ; | XXX| .byte $03 ; | XX| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $01 ; | X| .byte $03 ; | XX| .byte $07 ; | XXX| .byte $0F ; | XXXX| PFPat13: .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1f ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $1F ; | XXXXX| .byte $0F ; | XXXX| .byte $07 ; | XXX| .byte $03 ; | XX| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $01 ; | X| .byte $03 ; | XX| .byte $07 ; | XXX| PFPat12: .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $0F ; | XXXX| .byte $07 ; | XXX| .byte $03 ; | XX| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $01 ; | X| .byte $03 ; | XX| PFPat11: .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $07 ; | XXX| .byte $03 ; | XX| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $01 ; | X| PFPat10: .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $03 ; | XX| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | PFPat9: .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $01 ; | X| .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | JetStraight: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $2A ; | X X X | .byte $3E ; | XXXXX | .byte $1C ; | XXX | .byte $08 ; | X | .byte $49 ; | X X X| .byte $6B ; | XX X XX| .byte $7F ; | XXXXXXX| .byte $7F ; | XXXXXXX| .byte $3E ; | XXXXX | .byte $1C ; | XXX | .byte $08 ; | X | .byte $08 ; | X | .byte $08 ; | X | JetMove: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $02 ; | X | .byte $2E ; | X XXX | .byte $3C ; | XXXX | .byte $18 ; | XX | .byte $08 ; | X | .byte $0A ; | X X | .byte $2E ; | X XXX | .byte $3E ; | XXXXX | .byte $3E ; | XXXXX | .byte $3C ; | XXXX | .byte $18 ; | XX | .byte $08 ; | X | .byte $08 ; | X | .byte $08 ; | X | JetExplode: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $02 ; | X | .byte $08 ; | X | .byte $10 ; | X | .byte $00 ; | | .byte $40 ; | X | .byte $08 ; | X | .byte $21 ; | X X| .byte $44 ; | X X | .byte $10 ; | X | .byte $04 ; | X | .byte $08 ; | X | .byte $00 ; | | ; low pointers to the patterns for the river bank: BankPtrTab: ; $FCF1 .byte PFPat0 ; shapePtr0 ;22 .byte 12 ; PF1PatId .byte 1 ; level .byte SEED_LO, SEED_HI ; randomLoSave, randomHiSave .byte Space ; livesPtr .byte 1, SEED_LO, SEED_HI, FuelA ; shapePtr1a .byte FuelB ; shapePtr1b .byte ShipCol ; colorPtr ;=============================================================================== ; R O M - C O D E (Part 3) ;=============================================================================== CalcPosX SUBROUTINE ; calculates values for x-positioning: ; Input: ; - a = x-position ; Return: ; - y = coarse value for delay loop ; - a = fine value for HMxy TAY ; 2 INY ; 2 TYA ; 2 AND #$0F ; 2 STA temp2 ; 3 TYA ; 2 LSR ; 2 LSR ; 2 LSR ; 2 LSR ; 2 TAY ; 2 CLC ; 2 ADC temp2 ; 3 CMP #$0F ; 2 BCC .skipIny ; 2 SBC #$0F ; 2 INY ; 2 .skipIny: EOR #$07 ; 2 Mult16: ASL ; 2 ASL ; 2 ASL ; 2 ASL ; 2 Wait12: RTS ; 6 ;=============================================================================== ; R O M - T A B L E S (Part 3) ;=============================================================================== ; low addresses of entry points into kernel: JmpLoTab: .byte <[JmpPoint0-1] .byte <[JmpPoint1-1] .byte <[JmpPoint2-1] .byte <[JmpPoint3-1] .byte <[JmpPoint4-1] .byte <[JmpPoint5-1] .byte <[JmpPoint6-1] .byte <[JmpPoint7-1] .byte <[JmpPoint8-1] .byte <[JmpPoint9-1] align 256 FuelTab0: .byte $7F ; | XXXXXXX| $FE00 .byte $40 ; | X | .byte $4F ; | X XXXX| .byte $48 ; | X X | .byte $48 ; | X X | .byte $4E ; | X XXX | .byte $48 ; | X X | .byte $48 ; | X X | .byte $4F ; | X XXXX| .byte $40 ; | X | .byte $40 ; | X | .byte $4C ; | X XX | .byte $4C ; | X XX | .byte $4C ; | X XX | .byte $7F ; | XXXXXXX| FuelTab1: .byte $FF ; |XXXXXXXX| Explosion0: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | FuelTab2: .byte $FF ; |XXXXXXXX| .byte $00 ; | | .byte $03 ; | XX| .byte $C2 ; |XX X | .byte $63 ; | XX XX| .byte $30 ; | XX | .byte $1B ; | XX XX| .byte $EC ; |XXX XX | .byte $46 ; | X XX | .byte $43 ; | X XX| .byte $C1 ; |XX X| .byte $48 ; | X X | .byte $08 ; | X | .byte $08 ; | X | FuelTab3: .byte $FF ; |XXXXXXXX| .byte $00 ; | | .byte $80 ; |X | .byte $00 ; | | .byte $80 ; |X | .byte $80 ; |X | .byte $80 ; |X | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $80 ; |X | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | FuelTab4: .byte $FF ; |XXXXXXXX| .byte $01 ; | X| .byte $81 ; |X X| .byte $81 ; |X X| .byte $81 ; |X X| .byte $E1 ; |XXX X| .byte $81 ; |X X| .byte $81 ; |X X| .byte $F1 ; |XXXX X| .byte $01 ; | X| .byte $01 ; | X| .byte $19 ; | XX X| .byte $19 ; | XX X| .byte $19 ; | XX X| .byte $FF ; |XXXXXXXX| ; used to en- or disable ball in fuel display: ENABLTab: .byte DISABLE .byte ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE, ENABLE ; the scores to the enemy objects (bit 7 = 0: *10, = 1: *100): ScoreTab: .byte 0, 0, 0, 0 ; EXPLOSIONS .byte DIGIT_H * 1 | $80 ; PLANE 100 .byte DIGIT_H * 6 ; HELI 60 .byte DIGIT_H * 6 ; HELI 60 .byte DIGIT_H * 3 ; SHIP 30 .byte DIGIT_H * 5 |$80 ; BRIDGE 500 .byte 0 ; HOUSE .byte DIGIT_H * 8 ; FUEL 80 ; the data is stored for interlaced display: FuelA: .byte $FE ; |XXXXXXX | .byte $DE ; |XX XXXX | .byte $DE ; |XX XXXX | .byte $FE ; |XXXXXXX | .byte $DE ; |XX XXXX | .byte $DE ; |XX XXXX | .byte $FE ; |XXXXXXX | .byte $D6 ; |XX X XX | .byte $D6 ; |XX X XX | .byte $DE ; |XX XXXX | .byte $CE ; |XX XXX | FuelB: .byte $C6 ; |XX XX | .byte $DE ; |XX XXXX | .byte $DE ; |XX XXXX | .byte $C6 ; |XX XX | .byte $CE ; |XX XXX | .byte $C6 ; |XX XX | .byte $C6 ; |XX XX | .byte $D6 ; |XX X XX | .byte $FE ; |XXXXXXX | .byte $DE ; |XX XXXX | .byte $DE ; |XX XXXX | .byte $7C ; | XXXXX | BridgeA: .byte $42 ; | X X | BridgeB: .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $42 ; | X X | ShipB: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $FC ; |XXXXXX | .byte $FF ; |XXXXXXXX| .byte $30 ; | XX | .byte $10 ; | X | PlaneA: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $30 ; | XX | .byte $4F ; | X XXXX| .byte $C6 ; |XX XX | .byte $00 ; | | Heli1B: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $0E ; | XXX | .byte $8E ; |X XXX | .byte $FF ; |XXXXXXXX| .byte $0E ; | XXX | .byte $07 ; | XXX| Heli0A: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $04 ; | X | .byte $FF ; |XXXXXXXX| .byte $9F ; |X XXXXX| .byte $04 ; | X | .byte $07 ; | XXX| ShipA: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $7C ; | XXXXX | .byte $FE ; |XXXXXXX | .byte $78 ; | XXXX | .byte $10 ; | X | PlaneB: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $38 ; | XXX | .byte $FF ; |XXXXXXXX| .byte $80 ; |X | Heli1A: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $04 ; | X | .byte $FF ; |XXXXXXXX| .byte $9F ; |X XXXXX| .byte $04 ; | X | .byte $1C ; | XXX | Heli0B: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $0E ; | XXX | .byte $8E ; |X XXX | .byte $FF ; |XXXXXXXX| .byte $0E ; | XXX | .byte $1C ; | XXX | Explosion1B: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $10 ; | X | .byte $20 ; | X | .byte $40 ; | X | .byte $10 ; | X | Explosion1A: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $04 ; | X | .byte $02 ; | X | .byte $08 ; | X | .byte $04 ; | X | .byte $00 ; | | Explosion2B: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $20 ; | X | .byte $02 ; | X | .byte $41 ; | X X| .byte $20 ; | X | .byte $02 ; | X | .byte $04 ; | X | Explosion2A: .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | .byte $04 ; | X | .byte $88 ; |X X | .byte $10 ; | X | .byte $04 ; | X | .byte $80 ; |X | .byte $10 ; | X | .byte $00 ; | | .byte $00 ; | | HouseB: .byte $00 ; | | .byte $04 ; | X | .byte $1F ; | XXXXX| .byte $0E ; | XXX | .byte $04 ; | X | .byte $04 ; | X | .byte $00 ; | | .byte $AA ; |X X X X | .byte $FE ; |XXXXXXX | .byte $7C ; | XXXXX | .byte $00 ; | | HouseA: .byte $00 ; | | .byte $04 ; | X | .byte $0E ; | XXX | .byte $1F ; | XXXXX| .byte $0E ; | XXX | .byte $04 ; | X | .byte $00 ; | | .byte $FE ; |XXXXXXX | .byte $AA ; |X X X X | .byte $FE ; |XXXXXXX | .byte $38 ; | XXX | .byte $00 ; | | .byte $00 ; | | .byte $00 ; | | ;=============================================================================== ; R O M - C O D E (Part 4) ;=============================================================================== GetPageFlag SUBROUTINE ; get bit 0 of the page for the playfield data: TXA ;2 BEQ .exit ;2 LDA #%0 ;2 CPX #SWITCH_PAGE_ID ;2 PF id < 9 BCS .exit ;2 no, read data from page $FC LDA #%1 ;2 yes, read data from page $FD .exit: RTS ;6 SetScorePtrs SUBROUTINE STA scorePtr1+10 ;3 STA scorePtr2+10 ;3 ; let score-pointers point to 'Space' to avoid leading zeros: LDA #