Addendum II: Release & Download
Here it is, the official release of Computer Space 2001 for the PET. — Happy 40th anniversary, PET 2001! — The game is a full-fledged rendition of the very first commercial arcade video game from 1971 — as good as it goes with a character oriented display. The PET version features various screens and the full gameplay, including guided missiles (to be operated by the turn of the ship) and an optional recreation of the common hexadecimal scoring bug of the original machine. (For a description of the original machine see here.)
▶ Try it in in-browser emulation (white phosphor).
▶ Or here the same in green. :-)
(For a download link see below. For the whole story, see here.)
Options
At the start of the program, scores for the rocket ship and the saucers may be set up individually to count either in decimal or in hexadecimal digits. Refer to the following graphics for the display of hexadecimal digits (from the Intel 7448 BCD-to-seven-segment decoder datasheet):
Compatibility
The program is meant to run both on PETs with ROM 1.0 and ROM 2.0 installed and to auto-configure for the ROM version found on the specific machine. Moreover, it should run meaningful on machines with slow screen RAM. (ROM 4.0 has not been tested, but should be supported just like ROM 2.0.) Minimal memory requirements are 8K of RAM (6.6 K used). The game should run with any type of keyboard, as the kernel is used to scan and convert keyboard codes.
C64 Compatibility
Version 0.9.3 adds compatibility for the C64 (as an optional assembler target). The download-archive now also includes a binary version for the C64. While there are a few graphical differences, due to differences in the character ROM (the missing backslash character is a minor hassle), it's the same game as on the PET.
(Meaning, no joysticks, no sprites, and no sound either. — Sorry.)
Download
Current version: 1.1
Disclaimer: Software is provided for free and as-is, any warranties or claims of fitness exluded.
Download the source code and assembled binary files (PET and C64 in prg file format): computerspace2001.zip
In case you like the game and appreciate the effort, consider a donation in the best tradition of shareware games:
Thank you!
Source Code
Here's the source code of version 1.1:
(Formatted for use with the ACME cross-assembler by © 1998-2016 Marco Baye)
; ************************************************************************* ; Personal Computer Space Transactor 2001 (Computer Space 2001) ; by Norbert Landsteiner, 2017 ; (a project for RetroChallenge 2017/04) ; ; Version 1.1 ; ; Original Computer Space arcade game (c) 1971 Nutting Associates, Inc. ; by Nolan Bushnell and Ted Dabney. ; ; Version 0.9.3 adds the C64 as an optional assembler target. ; (There are a few, while not material, graphical differences on the C64 ; for differences in the character ROM.) ; ************************************************************************* ; source set up for ACME, https://sourceforge.net/projects/acme-crossass/ ; (set editor tab-stops to 16 for best display results) ; set output file and format !to "computerspace2001.prg", cbm ; ************************************************************************* ; system specif definitions ; assembler flag for target system (uncomment for C64) ;C64 = 1 !ifndef C64 { ; ========================================================================= ; PET (default) BASIC = $0401 ;start of BASIC/user meory SCREEN = $8000 ;start of screen memory CASS = $027A ;start of cassete buffer TIMER = $E844 ;source of random seed ZPAREA = $23 ;start of usable zero page area ; BASIC input buffer at $23 .. $5A may be reused safely (cf, PET 2001 manual) ; (beginning at $23, the product staging area for multiplication) ; character definitions (there are minor differences on the C64) BSL = $1C ;regular backslash character TBR = $63 ;horizontal top bar (thin) BBR = $64 ;horizontal bottom bar (thin) ; ========================================================================= } else { ; ========================================================================= ; C64 specific setup BASIC = $0801 ;where to start the BASIC intro SCREEN = $0400 ;start of screen memory CASS = $033C ;start of cassete buffer TIMER = $DC04 ;source of random seed ZPAREA = $26 ;start of usable zero page area ; product staging area for multiplication (RESHO) starts at $26 on the C64 ; further C64 specific branches are the IRQ initialization (setup), ; the keyboard routine (readKbd), which exist in a PET version and a ; version for C64 each. ; also, there's no backslash character on the C64 (used for rocket graphics) ; and some of the blockcharacters are bold on the C64, so we have to use ; some substitutes (in the title screen) BSL = $4D ;substitute for backslash (for rocket graphics) TBR = $77 ;horizontal top bar (thick) BBR = $6F ;horizontal bottom bar (thick) C64_FG_CLR = 1 ;foreground color C64_BG_CLR = 0 ;background color C64_FRAME_CLR = 6 ;frame color ; ========================================================================= } ; ************************************************************************* ; symbols / constants screenCols = 40 ;number of screen columns ticksPerSecond = 60 ;60: time in game corresponds to NTSC timing ;generally, timing is configured for 60Hz (NTSC) keyThrust = $41 ;A keyFire = $53 ;S keyFire2 = $44 ;D (secondary option for big hands) keyTurnLeft = $4B ;K keyTurnLeft2 = $4A ;J (secondary option for big hands) keyTurnRight = $4C ;L maxX = 44 ;x-coors max value maxY = 28 ;y-coors max value saucerFreq = 12 ;frames (update frequency, speed) saucerOffset = 14 ;screen lines y-offset (maxY / 2) rocketTurnFreq = 14 ;frames (update frequency, responsiveness) rocketMotionFreq = 4 ;frames (update frequency, pace of motion) rocketVMax = 64 ;max velocity fractional value rocketExpFreq = 5 ;frames (explosion rotation rate) rocketExpTurns = 12 ;animation frames missileLife = 100 ;frames saucerCooling = 80 ;frames explosionFrames = 15 ;duration of reverse video (frames) charQueue = CASS ;start of drawing buffer (cassette buffer) resetQueue = charQueue+60 ;buffer for screen resets ; ************************************************************************* ; symbols / zero-page addresses !address { gameState = ZPAREA+ 0 ;0: attract, 1: active fIRQ = ZPAREA+ 1 ;flag to synchronize irq operations fRepaint = ZPAREA+ 2 ;flag for video rendering/irq control ticks = ZPAREA+ 3 ;ticks counter videoMask = ZPAREA+ 4 ;0: normal, $80: reverse (xor-ed) newRom = ZPAREA+ 5 ;flag ROM 1.0 (0) / ROM 2.0+ (1) IRQVector = ZPAREA+ 6 ;backup of original irq vector (2 bytes) qPosX = ZPAREA+ 8 ;temp x coor for display purpose qPosY = ZPAREA+ 9 ;temp y coor for display purpose qScreenCode = ZPAREA+10 ;temp screen code for display purpose charQueuePtr = ZPAREA+11 ;pointer to top offset of charQueue resetQueuePtr = ZPAREA+12 ;pointer to top offset of charQueue frameCounter = ZPAREA+13 ;counter for animations ran = ZPAREA+14 ;random number (1 byte) tmp = ZPAREA+15 fRepaintScores = ZPAREA+20 ;flag to request a specific repaint fRepaintRocket = ZPAREA+21 fRepaintSaucer = ZPAREA+22 fRepaintScrCtr = ZPAREA+23 fRepaintRktMsl = ZPAREA+24 fRepaintScrMsl = ZPAREA+25 fRepaintThrust = ZPAREA+26 fMslCleared = ZPAREA+27 fExplode = ZPAREA+28 fKeyMessage = ZPAREA+29 PT1 = ZPAREA+30 ;versatile pointer (2 bytes) PT2 = ZPAREA+32 ;versatile pointer (2 bytes) IPT1 = ZPAREA+34 ;versatile pointer for interrupt tasks (2 bytes) IPT2 = ZPAREA+36 ;versatile pointer for interrupt tasks (2 bytes) } ; ************************************************************************* ; prespan (the BASIC equivalent of a loader) ; generate a tiny BASIC program to call our code at main ; ; 10 REM PERSONAL COMPUTER SPACE TRANS- ; 20 REM ACTOR 2001; VERS. 1.1, 2017 ; 30 SYS 1109 * = BASIC ;$0401 .basicLine10 !word .basicLine20 ;line link !word 10 ;line number !byte $8F, $20 ;REM + " " !text "PERSONAL COMPUTER SPACE TRANS-" !byte 0 ;eol .basicLine20 !word .basicLine30 !word 20 !byte $8F, $20 ;REM + " " !text "ACTOR 2001; VERS. 1.1, 2017" !byte 0 ;eol .basicLine30 !word .basicEnd !word 30 !byte $9E, $20 ;SYS + " " !byte $30 + ((main / 1000) % 10) ;addr of main in !byte $30 + ((main / 100) % 10) ; base 10 as string !byte $30 + ((main / 10) % 10) ; (4 chars) !byte $30 + ( main % 10) !byte 0 ;eol .basicEnd !word 0 ;eop ; ************************************************************************* main ; reset / setup cld ;reset BCD flag lda #0 sta fRepaint ;reset basic video flags sta fRepaintScores sta charQueuePtr sta resetQueuePtr sta fKeyMessage !ifndef C64 { setup ; setup irq vector for PETs (autodetects ROM version) sei lda $91 and #$F0 cmp #$E0 ;is it ROM 2.0 or higher? bne .rom1 ;no, it's ROM 1.0 .rom2 lda $90 sta IRQVector lda $91 sta IRQVector+1 lda #<irqRoutine sta $90 lda #>irqRoutine sta $91 lda #1 sta newRom jmp .setupDone .rom1 lda $219 sta IRQVector lda $21A sta IRQVector+1 lda #<irqRoutine sta $219 lda #>irqRoutine sta $21A lda #0 sta newRom .setupDone cli } else { ; setup irq vector for C64 sei lda $314 sta IRQVector lda $315 sta IRQVector+1 lda #<irqRoutine sta $314 lda #>irqRoutine sta $315 cli ; set up colors lda #C64_FG_CLR sta $0286 lda #C64_BG_CLR sta $D021 lda #C64_FRAME_CLR sta $D020 ; set up keyboard repeat lda #$80 sta $028A } ; ************************************************************************* ; if we're running on 80 cols, we need to clear the screen first -- ; do it anyway jsr clearScreen title jsr drawTitleScreen jsr readKbd ;reset the keyboard lda #1 sta fIRQ titleLoop lda fIRQ bne titleLoop jsr readKbd cmp #0 bne setupScreen lda #1 sta fIRQ jmp titleLoop ; ************************************************************************* setupScreen jsr clearScreen jsr drawSetupScreen lda #0 sta videoMask sta rocketX ;selected values (bit-vector) lda #1 sta rocketY ;selected row (1, 2) lda #0 sta charQueuePtr sta resetQueuePtr jsr resetSaucerY lda #14 sta saucerY lda #22 sta saucerX lda #4 sta saucerPhase jsr displaySaucer jsr displaySaucerCenter setupScreenRepaint lda #30 sta rocketTurnCnt ;use as temp counter ldx #1 jsr renderScoreSetup ldx #2 jsr renderScoreSetup lda #1 sta fRepaint sta fIRQ setupScreenLoop lda fIRQ bne setupScreenLoop lda #0 sta fRepaint sta charQueuePtr jsr readKbd tax ldy rocketTurnCnt beq .sslKbd dey sty rocketTurnCnt jmp .sslSaucer .sslKbd cpx #$20 beq .sslChange cpx #$91 beq .sslRow cpx #$11 beq .sslRow cpx #$0D beq .sslExit .sslSaucer lda frameCounter and #3 bne .sslDone jsr animateSaucer jsr displaySaucerCenter lda #1 sta fRepaint .sslDone lda #1 sta fIRQ jmp setupScreenLoop .sslChange lda rocketX eor rocketY sta rocketX jmp setupScreenRepaint .sslRow lda rocketY eor #3 sta rocketY jmp setupScreenRepaint .sslExit ldx #10 lda rocketX and #1 beq .sslRadix1 ldx #16 .sslRadix1 stx scoreRadix1 ldx #10 lda rocketX and #2 beq .sslRadix2 ldx #16 .sslRadix2 stx scoreRadix2 jmp init renderScoreSetup stx PT1 ;x=1 rocket, x=2 saucer lda .scoreSetupRows-1, x sta qPosY ldx #0 lda rocketY cmp PT1 beq .rdrSS1 inx .rdrSS1 stx PT2 lda .scoreSetupFrames, x sta qScreenCode lda #scoreSetupFrameX1 sta qPosX jsr pushScreenCode ldx PT2 lda .scoreSetupFrames+2, x sta qScreenCode lda #scoreSetupFrameX2 sta qPosX jsr pushScreenCode ldx #0 lda rocketX and PT1 bne .rdrSS2 inx .rdrSS2 stx PT2 lda .scoreSetupMarks, x sta qScreenCode lda #scoreSetupDecX sta qPosX jsr pushScreenCode ldx PT2 lda .scoreSetupMarks+1, x sta qScreenCode lda #scoreSetupHexX sta qPosX jsr pushScreenCode rts scoreSetupFrameX1 = 4 scoreSetupFrameX2 = 11 scoreSetupDecX = 15 scoreSetupHexX = 28 .scoreSetupRows !byte 18, 20 .scoreSetupFrames !byte $1B, $20, $1D, $20 .scoreSetupMarks !byte $57, $51, $57 ; ************************************************************************* init lda #0 sta fRepaint sta gameState jsr background lda #0 sta score1 sta score2 sta time1 sta time2 sta ticks sta frameCounter sta charQueuePtr sta resetQueuePtr sta explosionCnt sta rocketState sta rocketTurnCnt jsr initRandom ;init the random seed jsr readKbd ;reset the keyboard jsr resetSaucerY jsr random and #3 clc adc #20 sta saucerX lda #1 ;setup irq task handling sta fRepaintScores sta fKeyMessage sta fRepaint sta fIRQ ; ************************************************************************* ; main job loop loop lda fIRQ bne loop lda #0 sta fRepaint ;reset repaint flags sta fRepaintScores sta fRepaintSaucer sta fRepaintRocket sta fRepaintRktMsl sta fRepaintScrMsl sta fRepaintScrCtr sta fRepaintThrust sta fMslCleared sta fExplode sta charQueuePtr ;reset top-of-queue pointers sta resetQueuePtr lda gameState bne .gameRunning .attractMode lda #1 sta fKeyMessage lda rocketTurnCnt cmp #30 beq .amkbd jsr readKbd ;reset keyboard inc rocketTurnCnt bpl .gameFrame .amkbd jsr readKbd cmp #0 beq .gameFrame jsr newGame .gameRunning ;manage a frame jsr readKbd sta keyPressed cmp #3 ;check stop key (^C) bne .gameTicks jsr gamePaused .gameTicks lda ticks ;manage time sec sbc #ticksPerSecond ;has a second passed? bcc .gameFrame ;no sta ticks inc time1 lda time1 cmp #$0A bne .loopScoresFinal lda #0 sta time1 inc time2 lda time2 cmp #$0A bne .loopScoresFinal lda #0 sta time2 lda score2 cmp score1 bcc .loopScoreRevert .gameEnd jsr resetRocket jsr resetMissiles jsr resetSaucer lda #0 sta rocketState sta fRepaintRocket sta gameState sta explosionCnt lda videoMask beq .loopScoresFinal .loopScoreRevert jsr revertVideo .loopScoresFinal inc fRepaintScores .gameFrame jsr checkCollisions jsr rocketHandler jsr saucerHandler .grpFg lda fRepaintScores ;mangle repaint flags (foreground) ora fMslCleared ;scores changed or missiles were cleared? beq .grpRkt ;no lda #1 ldx rocketState ;rocket on screen? beq .grpFg1 ;no, skip sta fRepaintRocket ;force repaint flag for rocket .grpFg1 ldx saucerState ;saucer on screen? beq .grpFg2 ;no, skip sta fRepaintSaucer ;force repaint flag for saucer .grpFg2 ldx rocketMissileCnt ;rocket missile active? beq .grpFg3 sta fRepaintRktMsl ;force repaint .grpFg3 ldx saucerMissileCnt ;saucer missile active? beq .grpFg4 sta fRepaintScrMsl ;force repaint .grpFg4 ldx rocketThrusting ;thrust? beq .grpRkt sta fRepaintThrust ;force repaint .grpRkt lda fRepaintRocket ;repaint objects, if required beq .grpThr jsr displayRocket .grpThr lda fRepaintThrust beq .grpScr jsr displayThrust .grpScr lda fRepaintSaucer ;check saucer (outline and center) ora fRepaintScrCtr beq .grpRktMsl ;no change lda fRepaintSaucer ;repaint saucer beq .grpScr1 jsr displaySaucer .grpScr1 jsr displaySaucerCenter jsr flipSaucer ;repaint second saucer lda fRepaintSaucer beq .grpScr2 jsr displaySaucer .grpScr2 jsr displaySaucerCenter jsr flipSaucer .grpRktMsl lda fRepaintRktMsl ;check rocket missile beq .grpScrMsl jsr displayRocketMissile .grpScrMsl lda fRepaintScrMsl ;check saucer missile beq .loopExp1 jsr displaySaucerMissile .loopExp1 lda fExplode ;revert video on explosion beq .loopExp2 jsr revertVideo lda #explosionFrames ;set up a counter to revert back sta explosionCnt jmp .loopIter .loopExp2 ldx explosionCnt ;are we reverted for an explosion? beq .loopIter ;no dex ;decrement counter stx explosionCnt bne .loopIter jsr revertVideo ;revert back on zero .loopIter sei ;set flags for IRQ routine lda fRepaintScores ;repaint on either scores changed ora resetQueuePtr ; or anything to be cleared ora charQueuePtr ; or anything queued up newly sta fRepaint lda #1 ;set flag in order to wait sta fIRQ cli .loopEnd jmp loop ;jump to start of loop and wait ; ************************************************************************* newGame lda #0 sta ticks sta score1 sta score2 sta time2 sta explosionCnt sta fExplode sta fMslCleared sta fKeyMessage lda #1 sta time1 sta gameState sta fRepaintRocket sta fRepaintScores jsr background jsr resetRocket jsr random ;rocket coors to random and #3 clc adc #9 sta rocketX jsr random and #3 clc adc #3 sta rocketY jsr random and #3 clc adc #2 sta rocketDir ldx #5 jsr random and #1 beq .ng1 ldx #10 .ng1 stx rocketDxLo ldx #30 jsr random and #1 beq .ng2 ldx #42 .ng2 stx rocketDyLo jsr resetSaucer jsr random ;saucer X to random and #3 clc adc #27 sta saucerX lda #saucerCooling ;initial saucer cooling sta saucerMissileCooling rts ; ************************************************************************* gamePaused lda ticks sta ticksBackup sei lda #1 sta fRepaint sta fKeyMessage sta fIRQ cli .gpLoop lda fIRQ bne .gpLoop lda #0 sta fRepaint jsr readKbd cmp #0 beq .gpIter cmp #3 ;stop key beq .gpIter jmp .gpEnd .gpIter lda #1 sta fIRQ jmp .gpLoop .gpEnd lda #0 sta fKeyMessage jsr clearKeyMessage ldy #1 lda rocketState beq .gpeSaucer sty fRepaintRocket lda rocketMissileCnt beq .gpeSaucer sty fRepaintRktMsl .gpeSaucer lda saucerState beq .gpeTicks sty fRepaintSaucer lda saucerMissileCnt beq .gpeTicks sty fRepaintSaucer .gpeTicks lda ticksBackup sta ticks rts ; ************************************************************************* ; irq handling irqRoutine pha ;save registers txa pha tya pha inc ticks ;manage time inc frameCounter .checkRepaint lda fRepaint beq .irqDone jsr drawResetQueue lda fRepaintScores beq .irqDrawFg jsr drawScores .irqDrawFg jsr drawCharQueue lda fKeyMessage beq .irqDone jsr drawKeyMessage .irqDone lda #0 sta fIRQ pla ;restore registers tay pla tax pla jmp (IRQVector) ; ************************************************************************* ; utility subroutines !ifndef C64 { ;PET readKbd ;reads a character from keyboard, returns char in AC (0 = empty) lda newRom beq .rkbdRom1 .rkbdRom2 ldx $9E ;get # of chars in keyboard buffer beq .kbdEmpty lda $026E,x ;get char from buffer ldx #$FF ;reset keboard matrix stx $97 ; for key repeat ldx #0 ;reset index of keyboard buffer stx $9E ; to clear the queue rts .rkbdRom1 ldx $020D ;same as above for ROM 1.0 beq .kbdEmpty lda $020E,x ldx #$FF stx $0203 ldx #0 stx $020D rts .kbdEmpty lda #0 rts } else { ;same for C64 readKbd ldx $C6 ;get # of chars in keyboard buffer beq .kbdEmpty lda $0276,x ;get char from buffer ldx #$40 ;reset keboard matrix stx $CB ; for key repeat ldx #0 ;reset index of keyboard buffer stx $C6 ; to clear the queue rts .kbdEmpty lda #0 rts } clearScreen ;to be called from main only lda #$93 jsr $FFD2 lda #1 ;wait for next IRQ sta fIRQ .clswait lda fIRQ bne .clswait rts background ;fills the screen with stars ldx #24 .row lda screenLinesLo, x sta PT1 lda screenLinesHi, x sta PT1+1 ldy #39 .col jsr getStar sta (PT1), y dey bpl .col dex bpl .row rts getStar ;returns a background screen code (in AC) for row X, col Y lda starMaskY, x beq .blank and starMaskX, y beq .blank lda #$2E ;return a dot rts .blank lda #$20 ;return a blank rts ; score and time display !address { ; screen locations of score and time numerals screenAddressScore1 = SCREEN + 4*screenCols + 36 screenAddressScore2 = SCREEN + 10*screenCols + 36 screenAddressTime1 = SCREEN + 16*screenCols + 36 screenAddressTime2 = SCREEN + 16*screenCols + 33 } drawScores ;draws scores and time display ldy score1 lda #<screenAddressScore1 sta IPT1 lda #>screenAddressScore1 sta IPT1+1 jsr drawDigit ldy score2 lda #<screenAddressScore2 sta IPT1 lda #>screenAddressScore2 sta IPT1+1 jsr drawDigit ldy time1 lda #<screenAddressTime1 sta IPT1 lda #>screenAddressTime1 sta IPT1+1 jsr drawDigit ldy time2 lda #<screenAddressTime2 sta IPT1 lda #>screenAddressTime2 sta IPT1+1 jsr drawDigit rts drawDigit ;draws a digit (screen address in IPT1, digit in Y) ldx digitOffsets, y ldy #0 lda #4 sta IPT2 .dgRow lda digits, x eor videoMask ;adjust for normal/reverse video sta (IPT1), y inx iny lda digits, x eor videoMask sta (IPT1), y dec IPT2 beq .dgDone inx dey ;reset y to zero clc lda IPT1 ;and increment IPT1 by a screen line adc #screenCols sta IPT1 bcc .dgRow inc IPT1+1 jmp .dgRow .dgDone rts revertVideo ;reverts the screen video sei lda videoMask eor #$80 sta videoMask ldx #24 .rvRow lda screenLinesLo, x sta PT1 lda screenLinesHi, x sta PT1+1 ldy #39 .rvCol lda (PT1), y eor #$80 sta (PT1), y dey bpl .rvCol dex bpl .rvRow cli rts ; draws chars in charQueue of (screenCode, addrLo, addrHi)* ; self-modifying (sets address at .dcqScreen, sta xxxx) drawCharQueue ldx charQueuePtr ;get top-of-queue pointer beq .dcqDone ;exit, if empty dex .dcqLoop lda charQueue, x ;get screen address hi-byte sta .dcqScreen+2 ;fix-up dex lda charQueue, x ;get screen address lo-byte sta .dcqScreen+1 ;fix-up dex lda charQueue, x ;get screen code eor videoMask ;adjust for normal/reverse video .dcqScreen sta $ffff ;store it (dummy address) dex bpl .dcqLoop .dcqDone rts ; same as above, but for resetQueue drawResetQueue ldx resetQueuePtr beq .drqDone dex .drqLoop lda resetQueue, x sta .drqScreen+2 dex lda resetQueue, x sta .drqScreen+1 dex lda resetQueue, x eor videoMask .drqScreen sta $ffff dex bpl .drqLoop .drqDone rts ; a single character 'sprite routine' ; pushes a screen code and address onto the charQueue, if on-screen pushScreenCode lda qPosY bmi .pcqDone ;negative cmp #25 ;y >= 25 (off-screen to the bottom)? bcs .pcqDone lda qPosX bmi .pcqDone ;negative cmp #40 ;x >= 40 (off-screen to the right)? bcs .pcqDone ldx charQueuePtr lda qScreenCode sta charQueue, x inx ldy qPosY lda qPosX clc adc screenLinesLo, y sta charQueue, x inx lda #0 adc screenLinesHi, y sta charQueue, x inx stx charQueuePtr .pcqDone rts ; same as above,but for resetQueue pushScreenReset ldx qPosY bmi .psrDone ;negative cpx #25 ;y >= 25 (off-screen to the bottom)? bcs .psrDone lda qPosX bmi .psrDone ;negative cmp #40 ;x >= 40 (off-screen to the right)? bcs .psrDone sec ;check, if inside score area sbc #33 bcc .psr1 tay lda scoreAreaLineLo, x sta PT1 lda scoreAreaLineHi, x sta PT1+1 lda (PT1), y beq .psr1 inc fRepaintScores .psr1 ldx resetQueuePtr lda qScreenCode sta resetQueue, x inx ldy qPosY lda qPosX clc adc screenLinesLo, y sta resetQueue, x inx lda #0 adc screenLinesHi, y sta resetQueue, x inx stx resetQueuePtr .psrDone rts drawKeyMessage ;displays 'PRESS ANY KEY' at bottom row, center lda #<SCREEN + screenCols * 24 + pressAnyKeyOffset sta IPT1 lda #>SCREEN + screenCols * 24 + pressAnyKeyOffset sta IPT1+1 ldy #pressAnyKeyLength .dkmLoop lda pressAnyKey, y eor videoMask sta (IPT1), y dey bpl .dkmLoop rts clearKeyMessage ;clears the key message (see above) ldx #24 lda screenLinesLo, x sta PT1 lda screenLinesHi, x sta PT1+1 ldy #pressAnyKeyOffset+pressAnyKeyLength .ckmCol jsr getStar eor videoMask sta (PT1), y dey cpy #pressAnyKeyOffset bcs .ckmCol rts random ;a simple random number generator lda ran ror lda ran ror eor %11011001 sta ran rts initRandom ;initialize random number from VIA timer 1 lda TIMER sta ran rts ; ************************************************************************* ; routines for game logic ; collisions checkCollisions lda rocketState and saucerState cmp #1 beq .ccRS rts .ccRS sec lda rocketY sbc saucerY beq .ccRS1 cmp #saucerOffset beq .ccRS1 cmp #-saucerOffset bne .ccRocketMissiles .ccRS1 sec lda rocketX sbc saucerX beq .ccRSHit cmp #1 beq .ccRSHit cmp #$FF bne .ccRocketMissiles .ccRSHit jsr resetMissiles jsr rocketExplode jsr saucerExplode jsr incScore1 jsr incScore2 rts .ccRocketMissiles lda rocketMissileCnt beq .ccSaucerMissile lda saucerMissileCnt beq .ccRMSaucer lda rocketMissileY cmp saucerMissileY bne .ccRMSaucer lda rocketMissileX cmp saucerMissileX bne .ccRMSaucer jsr resetMissiles jsr incScore1 rts .ccRMSaucer sec lda rocketMissileY sbc saucerY beq .ccRMS1 cmp #saucerOffset beq .ccRMS1 cmp #-saucerOffset bne .ccSaucerMissile .ccRMS1 sec lda rocketMissileX sbc saucerX beq .ccRMSHit cmp #1 beq .ccRMSHit cmp #$FF bne .ccSaucerMissile .ccRMSHit jsr resetMissiles jsr saucerExplode jsr incScore1 rts .ccSaucerMissile lda saucerMissileCnt beq .ccDone ldx rocketDir lda rocketY cmp saucerMissileY beq .ccSM1 clc adc rocketExtdHitY, x cmp saucerMissileY bne .ccDone .ccSM1 lda rocketX cmp saucerMissileX beq .ccSMHit clc adc rocketExtdHitX, x cmp saucerMissileX bne .ccDone .ccSMHit jsr resetMissiles jsr rocketExplode jsr incScore2 .ccDone rts ; ************************************************************************* incScore1 lda score1 clc adc #1 cmp scoreRadix1 bne .incS1End lda #0 .incS1End sta score1 inc fRepaintScores rts incScore2 lda score2 clc adc #1 cmp scoreRadix2 bne .incS2End lda #0 .incS2End sta score2 inc fRepaintScores rts ; ************************************************************************* ; object resets rocketExplode inc fExplode lda rocketDir sta rktTempDir jsr resetRocket lda #2 sta rocketState lda #rocketExpTurns sta rocketFxCnt lda #1 sta rocketTurnCnt rts saucerExplode inc fExplode lda #2 sta saucerState lda #explosionFrames sta saucerCnt rts resetRocket lda rocketState beq .rstR0 jsr clearRocket lda rocketThrusting beq .rstR0 jsr clearThrust .rstR0 lda #1 sta rocketState lda #0 sta rocketXLo sta rocketYLo sta rocketDx sta rocketDy sta rocketDxLo sta rocketDyLo sta rocketThrust sta rocketThrusting sta rocketMotionCnt sta rocketTurnCnt sta rocketThrustFlkr .rstRY1 lda rocketY ;reset to visible area cmp #3 bcs .rstRY2 lda #2 sta rocketY jmp .rstRX1 .rstRY2 cmp #27 bcc .rstRX1 lda #26 sta rocketY .rstRX1 lda rocketX cmp #3 bcs .rstRX2 lda #2 sta rocketX jmp .rstRDisp .rstRX2 cmp #42 bcc .rstRDisp lda #41 sta rocketX .rstRDisp inc fRepaintRocket rts resetSaucer lda saucerState beq .rstS0 jsr clearSaucer jsr clearSaucerCenter jsr flipSaucer jsr clearSaucer jsr clearSaucerCenter .rstS0 jsr random clc and #31 adc #maxX-31 clc adc rocketX cmp #maxX bcc .rstSX sec sbc #maxX .rstSX cmp #3 ;reset to visible screen area bcs .rstSX1 lda #2 jmp .rstSX2 .rstSX1 cmp #42 bcc .rstSX2 lda #41 .rstSX2 sta saucerX resetSaucerY jsr random and #31 cmp #maxY bcc .rstSY sec sbc #saucerOffset .rstSY sta saucerY lda #1 sta saucerState lda #0 sta saucerDx sta saucerDy sta saucerPhase sta saucerPhaseDir sta saucerPhaseMask sta saucerCnt sta saucerLegCnt sta saucerStopCnt inc fRepaintSaucer inc fRepaintScrCtr rts resetMissiles jsr resetRocketMissile jsr resetSaucerMissile rts resetRocketMissile lda rocketMissileCnt beq .rstRmDone jsr clearRocketMissile lda #0 sta rocketMissileCnt lda #$FF sta rocketMissileY .rstRmDone rts resetSaucerMissile lda #saucerCooling sta saucerMissileCooling lda saucerMissileCnt beq .rstSmDone jsr clearSaucerMissile lda #0 sta saucerMissileCnt lda #$FF sta saucerMissileY .rstSmDone rts ; ************************************************************************* ; saucer(s) saucerHandler lda saucerState ;active? cmp #1 ;yes beq .shStart cmp #2 ;exploding beq .shX rts .shX dec saucerCnt ;exploding, just decrement bpl .shXEnd ; the counter and stay where we are jsr resetSaucer ;reset on counter zero .shXEnd rts .shStart dec saucerCnt ;decrement counter bmi .shUpdate ;move on underflow lda fRepaintScores ;do we have a score/time update? bne .shRedraw ;yes, redraw the saucers jmp .shAnimate ;just check the animation state .shRedraw jmp .shDisplay .shUpdate lda #saucerFreq ;reset counter sta saucerCnt jsr clearSaucer ;clear previous outlines jsr flipSaucer jsr clearSaucer jsr flipSaucer dec saucerLegCnt ;end of leg? bpl .shMoveY ;no, just move along jsr random ;get duration of next leg and #15 clc adc #4 sta saucerLegCnt lda ran ;new dir of center animation and #1 sta saucerPhaseDir ldx #3 lda ran bpl .shAnimSpeed ldx #7 .shAnimSpeed stx saucerPhaseMask ;new speed of center animation jsr random ldx saucerStopCnt ;have we stopped before? bne .shAsS1 ; yes, skip and #$CF ;eval additional stop condition bne .shAsS1 jmp .shStop .shAsS1 and #3 ;dx from random (0, 1, 2) cmp #3 bne .shSaveDx lda #0 .shSaveDx sta saucerDx jsr random ;dy from random (0, 1, 2) and #3 cmp #3 bne .shSaveDy lda #0 .shSaveDy sta saucerDy ;are we stopping (dx + dy = 0)? ora saucerDx bne .shMoveY ;no ldx saucerStopCnt ;make sure we don't exceed 3 consecutive stops inx stx saucerStopCnt cpx #3 bne .shMoveY jsr initRandom ;reinit random number, force movement jsr random ;make dx 1 or 2 and #1 tax inx stx saucerDx jsr random ;make dy 1 or 2 and #1 tax inx stx saucerDy ldx #0 ;reset stop count stx saucerStopCnt .shMoveY ldx saucerY lda saucerDy beq .shMoveX ;no move by y cmp #1 ;1 = down beq .shMoveY1 inx ;move down (y += 1) cpx #maxY ;check wrap bcc .shSaveY ldx #0 jmp .shSaveY .shMoveY1 dex ;move up (y -= 1) bpl .shSaveY ;check wrap ldx #maxY-1 .shSaveY stx saucerY .shMoveX ldx saucerX lda saucerDx beq .shDisplay ;no move by x cmp #1 ;1 = right beq .shMoveX1 inx ;move right (x += 1) cpx #maxX ;check wrap bcc .shSaveX ldx #0 jmp .shSaveX .shMoveX1 dex ;move left (x -= 1) bpl .shSaveX ;check wrap ldx #maxX-1 .shSaveX stx saucerX .shDisplay ;set flags for repaint inc fRepaintSaucer inc fRepaintScrCtr .shAnimate lda frameCounter and saucerPhaseMask bne .shDone jsr animateSaucer inc fRepaintScrCtr .shDone lda saucerMissileCnt ;missile active? bne .shMissle ;yes, move it jmp saucerAI ;evaluate shooting conditions .shMissle jsr saucerMissileHandler rts .shStop lda #0 ;forced stop sta saucerDx sta saucerDy lda saucerLegCnt ;half duration ror sta saucerLegCnt inc saucerStopCnt ;inc stop count jmp .shMoveY flipSaucer ;flips saucerY by saucerOffset lda saucerY clc adc #saucerOffset cmp #maxY bcc .fpSave sec sbc #maxY .fpSave sta saucerY rts saucerAI ;targeting and missile fire lda saucerMissileCooling ;check cooling beq .sai0 dec saucerMissileCooling rts .sai0 lda rocketState ;is the rocket active cmp #1 beq .sai1 ;evaluate shooting .shExit rts .sai1 jsr random ;get random offset bmi .shExit and #3 sec sbc #1 sta tmp lda rocketDxLo ;rocket dx << 4 (x8) asl sta PT1 lda rocketDx ;for 16-bit dx value rol sta PT1+1 lda PT1 asl sta PT1 lda PT1+1 rol sta PT1+1 lda PT1 asl sta PT1 lda PT1+1 rol sta PT1+1 lda PT1 asl sta PT1 lda PT1+1 rol clc adc rocketX ;add rocket x adc tmp ;add random offset bmi .sai1X1 cmp #maxX ;wrap, if required bcc .sai1X2 sec sbc maxX jmp .sai1X2 .sai1X1 clc adc maxX .sai1X2 sec sbc saucerX ;subtract saucer x sta PT1 ;store distance (x), signed bmi .saiXAbs sta PT1+1 ;abs value (x) jmp .saiXLimit .saiXAbs eor #$FF ;absolute value for negative clc adc #1 sta PT1+1 .saiXLimit ;flip sign of x, cmp #(maxX DIV 2) + 3 ; if distance > half screen bcc .sai1Y lda PT1 eor $FF sta PT1 .sai1Y ;same for y lda rocketDyLo asl sta PT2 lda rocketDy rol sta PT2+1 lda PT2 asl sta PT2 lda PT2+1 rol sta PT2+1 lda PT2 asl sta PT2 lda PT2+1 rol sta PT2+1 lda PT2 asl sta PT2 lda PT2+1 rol clc adc rocketY adc tmp bmi .sai1Y1 cmp #maxY bcc .sai1Y2 sec sbc maxY jmp .sai1Y2 .sai1Y1 clc adc maxY .sai1Y2 sec sbc saucerY sta PT2 ;signed distance y in PT2 bmi .saiYAbs sta PT2+1 ;abs distance y in PT2+1 jmp .saiYLimit .saiYAbs eor #$FF ;absolute value clc adc #1 sta PT2+1 .saiYLimit ;flip sign of y, cmp #(maxY DIV 2) + 3 ; if distance > half screen bcc .saiVert lda PT2 eor $FF sta PT2 .saiVert jsr random ;get random epsilon and #3 tay cmp PT1+1 ;is it in vertical channel? bcc .saiHor ; (abs dx < epsilon?) ldx #0 ;down lda PT1 ;get signed distance bpl .saiFire ;check sign ldx #1 ;up jmp .saiFire .saiHor tya ;transfer epsilon from y cmp PT2+1 ;is it in horizontal channel? bcc .saiDiag ; (abs dy < epsilon?) ldx #2 ;right lda PT2 bpl .saiFire ldx #3 ;left jmp .saiFire .saiDiag sec ;diff dx,dy lda PT1+1 sbc PT2+1 bpl .saiDiag1 eor #$FF ;make it absolute clc adc #1 .saiDiag1 sta tmp tya ;transfer epsilon from y cmp tmp ;inside diagonal channel? bcc .saiDone ldx #4 ;left (up) lda PT1 bmi .saiDiag2 inx ;make it right (up) .saiDiag2 lda PT2 bmi .saiFire inx ;inc by 2 for down inx jmp.saiFire .saiDone rts .saiFire lda saucerMissileDirDxLo, x ;direction in x, get lo-byte dx sta saucerMissileDxLo lda saucerMissileDirDx, x ;hi-byte dx sta saucerMissileDx lda saucerMissileDirDyLo, x ;lo-byte dy sta saucerMissileDyLo lda saucerMissileDirDy, x ;hi-byte dy sta saucerMissileDy clc ;initial position (x) lda saucerMissileOffsetX, x adc saucerX sta saucerMissileX clc ;initial position (x) lda saucerMissileOffsetY, x adc saucerY sta saucerMissileY lda saucerMissileInitialX, x ;initial position, lo-byte (x) sta saucerMissileXLo ; (sign gives block-character side) lda saucerMissileInitialY, x ;initial position, lo-byte (y) sta saucerMissileYLo lda #missileLife ;set up a counter for missile life sta saucerMissileCnt inc fRepaintScrMsl ;request a repaint rts ; ************************************************************************* ; rocket rocketHandler lda rocketState ;active? cmp #1 beq .rhMsl ;yes cmp #2 beq .rhX ;exploding rts .rhX dec rocketTurnCnt ;rocket exploding, decrement count bpl .rhXExit ;skip, if not zero jsr clearRocket ;clear outline inc fRepaintRocket ;set repaint flag dec rocketFxCnt ;reached the end of the sequence? beq .rhXEnd ;yes lda #rocketExpFreq ;reset count sta rocketTurnCnt ldx rocketDir ;rotate right inx txa and #7 sta rocketDir .rhXExit rts .rhXEnd lda #1 ;end of animation sequence sta rocketState ;reset state and direction lda #0 sta rocketTurnCnt lda rktTempDir sta rocketDir inc fRepaintRocket ;set repaint flag lda #saucerCooling ;force missile cooling for saucers sta saucerMissileCooling jsr readKbd ;reset keyboard rts .rhMsl lda #0 sta rktMslHandled lda rocketMissileCnt ;missile active beq .rhInit ;no, move on to rocket movement jsr rocketMissileHandler inc rktMslHandled .rhInit lda rocketX ;duplicate values sta rktTempX ; new values will be the temp values lda rocketY sta rktTempY lda rocketDir sta rktTempDir lda #0 sta rktTempThrusting sta rocketTurn lda keyPressed ;check control input cmp #keyTurnLeft beq .rhLeft cmp #keyTurnLeft2 beq .rhLeft cmp #keyTurnRight beq .rhRight cmp #keyThrust beq .rhThrust cmp #keyFire beq .rhFire cmp #keyFire2 beq .rhFire jmp .rhMove .rhLeft lda #$FF ;rocket turn = -1 sta rocketTurn jmp .rhMove .rhRight lda #$01 ;rocket turn = +1 sta rocketTurn jmp .rhMove jmp .rhMove .rhFire lda rktMslHandled ;missile active? bne .rhfDone ;yes, skip jsr rocketMissileInit ;fire the missile .rhfDone jmp .rhMove .rhThrust inc rktTempThrusting ;set up flag (for animation) ldx rktTempDir ;direction index in X clc ;inc dx lda rocketDxLo adc rocketDirDx, x bmi .rhThrustXM ;process negative value cmp #rocketVMax ;check max velocity (positive) bcc .rhThrustX1 lda #rocketVMax-1 .rhThrustX1 sta rocketDxLo lda #0 ;set HI-byte / sign jmp .rhThrustX3 .rhThrustXM cmp #-rocketVMax ;check max velocity (negative) bcs .rhThrustX2 lda #-rocketVMax+1 .rhThrustX2 sta rocketDxLo lda #$FF ;set HI-byte / sign .rhThrustX3 sta rocketDx .rhThrustY clc ;inc dy lda rocketDyLo adc rocketDirDy, x bmi .rhThrustYM ;process negative value cmp #rocketVMax ;check max velocity (positive) bcc .rhThrustY1 lda #rocketVMax-1 .rhThrustY1 sta rocketDyLo lda #0 ;set HI-byte / sign jmp .rhThrustY3 .rhThrustYM cmp #-rocketVMax ;check max velocity (negative) bcs .rhThrustY2 lda #-rocketVMax+1 .rhThrustY2 sta rocketDyLo lda #$FF ;set HI-byte / sign .rhThrustY3 sta rocketDy ;jmp .rhMove .rhMove ldx rocketTurnCnt ;process Turn on underflow beq .rhTurn dex stx rocketTurnCnt jmp .rhMoveX .rhTurn lda rocketTurn beq .rhMoveX lda #rocketTurnFreq sta rocketTurnCnt clc lda rocketDir adc rocketTurn and #7 sta rktTempDir inc fRepaintRocket ;flag for redraw lda rocketMissileCnt beq .rhMoveX jsr rocketMissileUpdate .rhMoveX dec rocketMotionCnt ;check update frequency bpl .rhRedraw lda #rocketMotionFreq sta rocketMotionCnt clc ;move by dx lda rocketXLo adc rocketDxLo sta rocketXLo lda rocketX adc rocketDx bmi .rhMoveXM ;branch to warp to right cmp #maxX bcc .rhSaveX lda #0 ;wrap to left jmp .rhSaveX .rhMoveXM lda #maxX-1 .rhSaveX sta rktTempX ;save updated value cmp rocketX ;evaluate redraw beq .rhMoveY inc fRepaintRocket .rhMoveY ;move by dy clc lda rocketYLo adc rocketDyLo sta rocketYLo lda rocketY adc rocketDy bmi .rhMoveYM ;branch to wrap to bottom cmp #maxY bcc .rhSaveY lda #0 ;wrap to top jmp .rhSaveY .rhMoveYM lda #maxY-1 .rhSaveY sta rktTempY ;save updated value cmp rocketY ;evaluate redraw beq .rhRedraw inc fRepaintRocket .rhRedraw lda rktTempThrusting ;clear thrust flag on random beq .rhFlkrRst2 ; for a bit of flicker dec rocketThrustFlkr bmi .rhFlkrRst1 lda #0 sta rktTempThrusting jmp .rhRedraw0 .rhFlkrRst1 jsr random and #3 asl sta rocketThrustFlkr jmp .rhRedraw0 .rhFlkrRst2 lda #0 sta rocketThrustFlkr .rhRedraw0 lda rocketThrusting ;were we thrusting? beq .rhRedraw2 ; no, no need to clear cmp rktTempThrusting ;new one the same? bne .rhRedraw1 ; no, clear old one lda fRepaintRocket ;have we turned or moved? beq .rhRedraw3 .rhRedraw1 jsr clearThrust .rhRedraw2 lda fRepaintRocket ;have we moved or turned? beq .rhRedraw3 ; no jsr clearRocket ;clear old outline lda rktTempDir ;copy temp values to standard ones sta rocketDir lda rktTempX sta rocketX lda rktTempY sta rocketY .rhRedraw3 lda rktTempThrusting ;copy temp value for thrust sta rocketThrusting beq .rhDone ;and set repaint flag, if required inc fRepaintThrust .rhDone rts ; ************************************************************************* ; missiles ; rocket missile rocketMissileInit ;fire a missile ldx rocketDir ;get missile velocity (x) for dir lda rocketMissileDirDx, x sta rocketMissileDxLo ;dx lo-byte bmi .rmiX1 ;set hi-byte on sign lda #0 jmp .rmiX2 .rmiX1 lda #$FF .rmiX2 sta rocketMissileDx lda rocketMissileInitialX,x ;set up initial value of lo-position sta rocketMissileXLo ; (left or right block side) lda rocketMissileDirDy, x ;same for y sta rocketMissileDyLo bmi .rmiY1 lda #0 jmp .rmiY2 .rmiY1 lda #$FF .rmiY2 sta rocketMissileDy lda rocketMissileInitialY,x sta rocketMissileYLo clc ;initial hi-position (x) lda rocketX adc rocketMissileOffsetX, x sta rocketMissileX clc ;initial hi-position (y) lda rocketY adc rocketMissileOffsetY, x sta rocketMissileY lda #missileLife ;set up counter for missile life sta rocketMissileCnt inc fRepaintRktMsl ;request a repaint rts rocketMissileHandler dec rocketMissileCnt ;check count for missile life bne .rmh0 jsr clearRocketMissile ;expired, clear and reset it jsr resetRocketMissile rts .rmh0 clc ;add dx (16 bit) lda rocketMissileDxLo adc rocketMissileXLo sta rocketMissileXLo lda rocketMissileDx adc rocketMissileX bmi .rmhXM ;check wrap (hi-pos only) cmp #maxX bcc .rmhSaveX lda #0 jmp .rmhSaveX .rmhXM lda #maxX-1 .rmhSaveX sta rocketMissileTempX ;store updated pos x clc ;add dy (16 bit) lda rocketMissileDyLo adc rocketMissileYLo sta rocketMissileYLo lda rocketMissileDy adc rocketMissileY bmi .rmhYM ;check wrap (hi-pos only) cmp #maxY bcc .rmhSaveY lda #0 jmp .rmhSaveY .rmhYM lda #maxY-1 .rmhSaveY sta rocketMissileTempY ;store updated y ldx #0 ;derive screen code index (in X) lda rocketMissileXLo bpl .rmh1 inx .rmh1 lda rocketMissileYLo bpl .rmh2 inx inx .rmh2 lda rocketMissileY ;check clear/redraw conditions cmp rocketMissileTempY bne .rmhClr ;moved by y lda rocketMissileX cmp rocketMissileTempX bne .rmhClr ;moved by x cpx rocketMissileCode ;compare new code index in x to previous one beq .rmhDone ;no change stx rocketMissileCode ;store new one jmp .rmhDpy ;same position, no need to clear .rmhClr stx rocketMissileCode ;moved, clear it jsr clearRocketMissile .rmhDpy lda rocketMissileTempY sta rocketMissileY lda rocketMissileTempX sta rocketMissileX inc fRepaintRktMsl ;set repaint flag .rmhDone rts rocketMissileUpdate ;update missile motion vector on rocket turn ldx rktTempDir ;new direction lda rocketMissileDxLo ;average old dx, new dx pha ;emulate 2 'asr' (old dx/4) rol pla ror pha rol pla ror clc adc rocketMissileUpdateDx, x ;new dx/1.75 from table sta rocketMissileDxLo bmi .rmuXM ;arrange hi-bite for sign lda #0 jmp .rmuSaveX .rmuXM lda #$FF .rmuSaveX sta rocketMissileDx lda rocketMissileDyLo ;average old dy, new dy pha ;emulate 2 'asr' (old dy/4) rol pla ror pha rol pla ror clc adc rocketMissileUpdateDy, x ;new dy/1.75 from table sta rocketMissileDyLo bmi .rmuYM ;arrange hi-bite for sign lda #0 jmp .rmuSaveY .rmuYM lda #$FF .rmuSaveY sta rocketMissileDy rts ; saucer missile saucerMissileHandler ;same as rocket missile dec saucerMissileCnt bne .smh0 jsr clearSaucerMissile jsr resetSaucerMissile rts .smh0 clc lda saucerMissileDxLo adc saucerMissileXLo sta saucerMissileXLo lda saucerMissileDx adc saucerMissileX bmi .smhXM cmp #maxX bcc .smhSaveX lda #0 jmp .smhSaveX .smhXM lda #maxX-1 .smhSaveX sta saucerMissileTempX clc lda saucerMissileDyLo adc saucerMissileYLo sta saucerMissileYLo lda saucerMissileDy adc saucerMissileY bmi .smhYM cmp #maxY bcc .smhSaveY lda #0 jmp .smhSaveY .smhYM lda #maxY-1 .smhSaveY sta saucerMissileTempY ldx #0 ;derive screenCode (in X) lda saucerMissileXLo bpl .smh1 inx .smh1 lda saucerMissileYLo bpl .smh2 inx inx .smh2 lda saucerMissileY ;check clear/redraw conditions cmp saucerMissileTempY bne .smhClr lda saucerMissileX cmp saucerMissileTempX bne .smhClr cpx saucerMissileCode beq .smhDone stx saucerMissileCode jmp .smhDpy .smhClr stx saucerMissileCode jsr clearSaucerMissile .smhDpy lda saucerMissileTempY sta saucerMissileY lda saucerMissileTempX sta saucerMissileX inc fRepaintScrMsl .smhDone rts ; ************************************************************************* ; display routines (display and clear moving objects) displaySaucer ;pushes a saucer at saucerX /saucerY onto the charQueue ldx saucerY dex ;2 pos offset dex dex ;-1 for top row stx qPosY ldx saucerX dex ;2 pos offset dex stx qPosX lda #$64 sta qScreenCode jsr pushScreenCode ;$64 at x, y-1 ldx qPosY inx stx qPosY ldx qPosX dex stx qPosX lda #$73 sta qScreenCode jsr pushScreenCode ;$73 at x-1, y ldx qPosX inx stx qPosX ldx saucerPhase lda saucerPhases, x sta qScreenCode jsr pushScreenCode ;center code at x, y ldx qPosX inx stx qPosX lda #$6B sta qScreenCode jsr pushScreenCode ;$6B at x+1, y ldx qPosY inx stx qPosY ldx qPosX dex stx qPosX lda #$63 sta qScreenCode jsr pushScreenCode ;$63 at x, y+1 rts clearSaucer ;pushes a saucer at saucerX /saucerY onto the resetQueue ldx saucerY dex ;2 pos offset dex dex ;-1 for top row stx qPosY ldy saucerX dey ;2 pos offset dey sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset ldx qPosY inx stx qPosY ldy qPosX dey sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset ldx qPosY ldy qPosX iny sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset ldx qPosY ldy qPosX iny sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset ldx qPosY inx stx qPosY ldy qPosX dey sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset rts animateSaucer ;saucer center animation lda saucerPhaseDir beq .asLeft ldx saucerPhase inx cpx #10 bne .asNext ldx #0 beq .asNext .asLeft ldx saucerPhase dex bpl .asNext ldx #9 .asNext stx saucerPhase rts displaySaucerCenter ldx saucerPhase lda saucerPhases, x sta qScreenCode ldx saucerX dex ;2 pos offset dex stx qPosX ldx saucerY dex ;2 pos offset dex stx qPosY jsr pushScreenCode rts clearSaucerCenter ldx saucerX dex ;2 pos offset dex stx qPosX ldx saucerY dex ;2 pos offset dex stx qPosY jsr getStar sta qScreenCode jsr pushScreenReset rts displayRocket ; pushes the rocket to the charQueue ldx rocketY dex ;2 pos offset dex stx qPosY ldy rocketX dey ;2 pos offset dey sty qPosX ldy rocketDir ;dispatch on value in rocketDir lda .drJumpTableHi,y sta .drJmp+2 lda .drJumpTableLo,y sta .drJmp+1 .drJmp jmp $ffff ;dummy address (fixed up) .dr0 lda #BSL sta qScreenCode jsr pushScreenCode ldx qPosX dex stx qPosX lda #$67 sta qScreenCode jsr pushScreenCode rts .dr1 lda #$2F sta qScreenCode jsr pushScreenCode ldx qPosX inx stx qPosX lda #$65 sta qScreenCode jsr pushScreenCode rts .dr2 lda #$2F sta qScreenCode jsr pushScreenCode ldx qPosY dex stx qPosY lda #$64 sta qScreenCode jsr pushScreenCode rts .dr3 lda #BSL sta qScreenCode jsr pushScreenCode ldx qPosY inx stx qPosY lda #$63 sta qScreenCode jsr pushScreenCode rts .dr4 lda #BSL sta qScreenCode jsr pushScreenCode ldx qPosX inx stx qPosX lda #$65 sta qScreenCode jsr pushScreenCode rts .dr5 lda #$2F sta qScreenCode jsr pushScreenCode ldx qPosX dex stx qPosX lda #$67 sta qScreenCode jsr pushScreenCode rts .dr6 lda #$2F sta qScreenCode jsr pushScreenCode ldx qPosY inx stx qPosY lda #$63 sta qScreenCode jsr pushScreenCode rts .dr7 lda #BSL sta qScreenCode jsr pushScreenCode ldx qPosY dex stx qPosY lda #$64 sta qScreenCode jsr pushScreenCode rts .drJumpTableLo !byte <.dr0 !byte <.dr1 !byte <.dr2 !byte <.dr3 !byte <.dr4 !byte <.dr5 !byte <.dr6 !byte <.dr7 .drJumpTableHi !byte >.dr0 !byte >.dr1 !byte >.dr2 !byte >.dr3 !byte >.dr4 !byte >.dr5 !byte >.dr6 !byte >.dr7 clearRocket ; pushes the rocket to the resetQueue ldx rocketY dex ;2 pos offset dex stx qPosY ldy rocketX dey ;2 pos offset dey sty qPosX ldy rocketDir ;dispatch on value in rocketDir lda .crJumpTableHi,y sta .crJmp+2 lda .crJumpTableLo,y sta .crJmp+1 .crJmp jmp $ffff ;dummy address (fixed up) .cr0 ldy qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset ldy qPosX dey sty qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset rts .cr1 ldy qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset ldy qPosX iny sty qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset rts .cr2 ldy qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset ldy qPosX ldx qPosY dex stx qPosY jsr getStar sta qScreenCode jsr pushScreenReset rts .cr3 ldy qPosX ldx qPosY jsr getStar sta qScreenCode jsr pushScreenReset ldy qPosX ldx qPosY inx stx qPosY jsr getStar sta qScreenCode jsr pushScreenReset rts .crJumpTableLo !byte <.cr0 !byte <.cr1 !byte <.cr2 !byte <.cr3 !byte <.cr1 !byte <.cr0 !byte <.cr3 !byte <.cr2 .crJumpTableHi !byte >.cr0 !byte >.cr1 !byte >.cr2 !byte >.cr3 !byte >.cr1 !byte >.cr0 !byte >.cr3 !byte >.cr2 displayThrust ;pushes the exhaust onto the charQueue ldx rocketDir clc lda rocketX adc thrustOffsetX, x sta qPosX clc lda rocketY adc thrustOffsetY, x sta qPosY lda #$2A sta qScreenCode jsr pushScreenCode rts clearThrust ;pushes background code for exhaust onto the resetQueue ldx rocketDir clc lda rocketX adc thrustOffsetX, x sta qPosX tay clc lda rocketY adc thrustOffsetY, x sta qPosY tax jsr getStar sta qScreenCode jsr pushScreenReset rts displayRocketMissile ldx rocketMissileCode lda missileCodes, x sta qScreenCode ldx rocketMissileX dex dex stx qPosX ldx rocketMissileY dex dex stx qPosY jsr pushScreenCode rts clearRocketMissile ldx rocketMissileY dex dex stx qPosY ldy rocketMissileX dey dey sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset inc fMslCleared rts displaySaucerMissile ldx saucerMissileCode lda missileCodes, x sta qScreenCode ldx saucerMissileX dex dex stx qPosX ldx saucerMissileY dex dex stx qPosY jsr pushScreenCode rts clearSaucerMissile ldx saucerMissileY dex dex stx qPosY ldy saucerMissileX dey dey sty qPosX jsr getStar sta qScreenCode jsr pushScreenReset inc fMslCleared rts drawTitleScreen ;draws the title screen (directly into screen memory) sei lda screenLinesLo sta PT1 lda screenLinesHi sta PT1+1 lda #<titleScreen sta PT2 lda #>titleScreen sta PT2+1 ldx #24 .dtsRow ldy #39 .dtsCol lda (PT2), y sta (PT1), y dey bpl .dtsCol dex bmi .dtsDone clc lda PT1 adc #screenCols sta PT1 lda #0 adc PT1+1 sta PT1+1 clc lda PT2 adc #screenCols sta PT2 lda #0 adc PT2+1 sta PT2+1 jmp .dtsRow .dtsDone cli rts drawSetupScreen ;draws the info and setup screen (after title screen) sei ldx #0 .drawSSLoop ldy setupScreenTable, x beq .drawSSEnd inx lda setupScreenTable, x sta PT1 inx lda setupScreenTable, x sta PT1+1 inx lda setupScreenTable, x sta PT2 inx lda setupScreenTable, x sta PT2+1 inx .drawSSdisplay lda (PT1), y sta (PT2), y dey bpl .drawSSdisplay jmp .drawSSLoop .drawSSEnd cli rts ; ************************************************************************* ; variables score1 !byte 0 score2 !byte 0 time1 !byte 0 time2 !byte 0 explosionCnt !byte 0 scoreRadix1 !byte 0 scoreRadix2 !byte 0 keyPressed !byte 0 ticksBackup !byte 0 saucerState !byte 0 saucerX !byte 0 saucerY !byte 0 saucerDx !byte 0 saucerDy !byte 0 saucerPhase !byte 0 saucerPhaseDir !byte 0 saucerPhaseMask !byte 0 saucerCnt !byte 0 saucerLegCnt !byte 0 saucerStopCnt !byte 0 rocketState !byte 0 rocketXLo !byte 0 rocketX !byte 0 rocketYLo !byte 0 rocketY !byte 0 rocketDxLo !byte 0 rocketDx !byte 0 rocketDyLo !byte 0 rocketDy !byte 0 rocketDir !byte 0 rocketThrust !byte 0 rocketThrustFlkr !byte 0 rocketTurn !byte 0 rocketTurnCnt !byte 0 rocketMotionCnt !byte 0 rocketThrusting !byte 0 rocketFxCnt !byte 0 rktTempX !byte 0 rktTempY !byte 0 rktTempDir !byte 0 rktTempThrusting !byte 0 rktMslHandled !byte 0 rocketMissileXLo !byte 0 rocketMissileX !byte 0 rocketMissileYLo !byte 0 rocketMissileY !byte 0 rocketMissileDxLo !byte 0 rocketMissileDx !byte 0 rocketMissileDyLo !byte 0 rocketMissileDy !byte 0 rocketMissileCnt !byte 0 rocketMissileTempX !byte 0 rocketMissileTempY !byte 0 rocketMissileCode !byte 0 saucerMissileXLo !byte 0 saucerMissileX !byte 0 saucerMissileYLo !byte 0 saucerMissileY !byte 0 saucerMissileDx !byte 0 saucerMissileDxLo !byte 0 saucerMissileDyLo !byte 0 saucerMissileDy !byte 0 saucerMissileCnt !byte 0 saucerMissileTempX !byte 0 saucerMissileTempY !byte 0 saucerMissileCode !byte 0 saucerMissileCooling !byte 0 ; ************************************************************************* ; data saucerPhases !byte $20,$65,$54,$47,$42,$5D,$48,$59,$67,$20 rocketDirDx !byte -1, 1, 4, 4, 1, -1, -4, -4 rocketDirDy !byte -4, -4, -1, 1, 4, 4, 1, -1 thrustOffsetX !byte -2, -2, -3, -3, -2, -2, -1, -1 thrustOffsetY !byte -1, -1, -2, -2, -3, -3, -2, -2 rocketExtdHitX !byte -1, 1, 0, 0, 1, -1, 0, 0 rocketExtdHitY !byte 0, 0, -1, 1, 0, 0, 1, -1 rocketMissileDirDx !byte -14, 14, 56, 56, 14, -14, -56, -56 rocketMissileDirDy !byte -56, -56, -14, 14, 56, 56, 14, -14 rocketMissileUpdateDx !byte -11, 11, 44, 44, 11, -11, -44, -44 rocketMissileUpdateDy !byte -44, -44, -11, 11, 44, 44, 11, -11 rocketMissileInitialX !byte $E0, $20, $E0, $E0, $20, $E0, $E0, $E0 rocketMissileInitialY !byte $E0, $E0, $E0, $20, $20, $20, $20, $E0 rocketMissileOffsetX !byte -1, 1, 1, 1, 1, -1, -1, -1 rocketMissileOffsetY !byte -1, -1, -1, 1, 1, 1, 1, -1 saucerMissileOffsetX !byte 0, 0, 2, -2, -1, 1, -1, 1 saucerMissileOffsetY !byte 2, -2, 0, 0, -1, -1, 1, 1 saucerMissileDirDxLo !byte 0, 0, 56, -56, -40, 40, -40, 40 saucerMissileDirDyLo !byte 56, -56, 0, 0, -40, -40, 40, 40 saucerMissileDirDx !byte 0, 0, 0, $FF, $FF, 0, $FF, 0 saucerMissileDirDy !byte 0, $FF, 0, 0, $FF, $FF, 0, 0 saucerMissileInitialX !byte $20, $20, $20, $E0, $E0, $20, $E0, $20 saucerMissileInitialY !byte $20, $E0, $E0, $E0, $E0, $E0, $20, $20 missileCodes !byte $7E, $7C, $7B, $6C starMaskX !byte $20, $00, $40, $0A, $08, $01, $82, $00 !byte $00, $00, $00, $00, $40, $00, $00, $02 !byte $00, $04, $20, $10, $88, $44, $00, $40 !byte $00, $01, $20, $00, $00, $42, $14, $00 !byte $48, $20, $00, $10, $18, $00, $00, $40 starMaskY !byte $40, $00, $01, $00, $00, $08, $00, $04 !byte $00, $40, $00, $02, $00, $00, $01, $00 !byte $04, $00, $10, $00, $20, $00, $01, $00 !byte $80 screenLinesHi !byte >(SCREEN + screenCols * 0) !byte >(SCREEN + screenCols * 1) !byte >(SCREEN + screenCols * 2) !byte >(SCREEN + screenCols * 3) !byte >(SCREEN + screenCols * 4) !byte >(SCREEN + screenCols * 5) !byte >(SCREEN + screenCols * 6) !byte >(SCREEN + screenCols * 7) !byte >(SCREEN + screenCols * 8) !byte >(SCREEN + screenCols * 9) !byte >(SCREEN + screenCols * 10) !byte >(SCREEN + screenCols * 11) !byte >(SCREEN + screenCols * 12) !byte >(SCREEN + screenCols * 13) !byte >(SCREEN + screenCols * 14) !byte >(SCREEN + screenCols * 15) !byte >(SCREEN + screenCols * 16) !byte >(SCREEN + screenCols * 17) !byte >(SCREEN + screenCols * 18) !byte >(SCREEN + screenCols * 19) !byte >(SCREEN + screenCols * 20) !byte >(SCREEN + screenCols * 21) !byte >(SCREEN + screenCols * 22) !byte >(SCREEN + screenCols * 23) !byte >(SCREEN + screenCols * 24) screenLinesLo !byte <(SCREEN + screenCols * 0) !byte <(SCREEN + screenCols * 1) !byte <(SCREEN + screenCols * 2) !byte <(SCREEN + screenCols * 3) !byte <(SCREEN + screenCols * 4) !byte <(SCREEN + screenCols * 5) !byte <(SCREEN + screenCols * 6) !byte <(SCREEN + screenCols * 7) !byte <(SCREEN + screenCols * 8) !byte <(SCREEN + screenCols * 9) !byte <(SCREEN + screenCols * 10) !byte <(SCREEN + screenCols * 11) !byte <(SCREEN + screenCols * 12) !byte <(SCREEN + screenCols * 13) !byte <(SCREEN + screenCols * 14) !byte <(SCREEN + screenCols * 15) !byte <(SCREEN + screenCols * 16) !byte <(SCREEN + screenCols * 17) !byte <(SCREEN + screenCols * 18) !byte <(SCREEN + screenCols * 19) !byte <(SCREEN + screenCols * 20) !byte <(SCREEN + screenCols * 21) !byte <(SCREEN + screenCols * 22) !byte <(SCREEN + screenCols * 23) !byte <(SCREEN + screenCols * 24) digits ;0 !byte $62,$62 !byte $61,$E1 !byte $61,$E1 !byte $FC,$FE ;1 !byte $20,$6C !byte $20,$E1 !byte $20,$E1 !byte $20,$E1 ;2 !byte $62,$62 !byte $20,$E1 !byte $EC,$E2 !byte $FC,$62 ;3 !byte $62,$62 !byte $20,$E1 !byte $7C,$FB !byte $62,$FE ;4 !byte $7B,$6C !byte $61,$E1 !byte $E2,$FB !byte $20,$E1 ;5 !byte $62,$62 !byte $61,$20 !byte $E2,$FB !byte $62,$FE ;6 !byte $7B,$2E !byte $61,$20 !byte $EC,$FB !byte $FC,$FE ;7 !byte $62,$62 !byte $20,$E1 !byte $20,$E1 !byte $20,$E1 ;8 !byte $62,$62 !byte $61,$E1 !byte $EC,$FB !byte $FC,$FE ;9 !byte $62,$62 !byte $61,$E1 !byte $E2,$FB !byte $20,$E1 ;$A !byte $20,$2E !byte $20,$20 !byte $EC,$E2 !byte $FC,$62 ;$B !byte $20,$2E !byte $20,$20 !byte $E2,$FB !byte $62,$FE ;$C !byte $7B,$6C !byte $FC,$FE !byte $20,$20 !byte $20,$20 ;$D !byte $62,$62 !byte $61,$20 !byte $E2,$E2 !byte $62,$62 ;$E !byte $7B,$2E !byte $61,$20 !byte $EC,$E2 !byte $FC,$62 ;$F !byte $20,$2E !byte $20,$20 !byte $7C,$7E !byte $20,$20 digitOffsets !byte 0, 8, 16, 24, 32, 40, 48, 56, 64, 72 !byte 80, 88, 96, 104, 112, 120 scoreAreaMap ;x >= 33 .sma0 !byte 0, 0, 0, 0, 0, 0, 0 .sma1 !byte 0, 0, 0, 0, 0, 0, 0 .sma2 !byte 0, 0, 0, 0, 0, 0, 0 .sma3 !byte 0, 0, 0, 0, 0, 0, 0 .sma4 !byte 0, 0, 0, 1, 1, 0, 0 .sma5 !byte 0, 0, 0, 1, 1, 0, 0 .sma6 !byte 0, 0, 0, 1, 1, 0, 0 .sma7 !byte 0, 0, 0, 1, 1, 0, 0 .sma8 !byte 0, 0, 0, 0, 0, 0, 0 .sma9 !byte 0, 0, 0, 0, 0, 0, 0 .sma10 !byte 0, 0, 0, 1, 1, 0, 0 .sma11 !byte 0, 0, 0, 1, 1, 0, 0 .sma12 !byte 0, 0, 0, 1, 1, 0, 0 .sma13 !byte 0, 0, 0, 1, 1, 0, 0 .sma14 !byte 0, 0, 0, 0, 0, 0, 0 .sma15 !byte 0, 0, 0, 0, 0, 0, 0 .sma16 !byte 1, 1, 0, 1, 1, 0, 0 .sma17 !byte 1, 1, 0, 1, 1, 0, 0 .sma18 !byte 1, 1, 0, 1, 1, 0, 0 .sma19 !byte 1, 1, 0, 1, 1, 0, 0 .sma20 !byte 0, 0, 0, 0, 0, 0, 0 .sma21 !byte 0, 0, 0, 0, 0, 0, 0 .sma22 !byte 0, 0, 0, 0, 0, 0, 0 .sma23 !byte 0, 0, 0, 0, 0, 0, 0 .sma24 !byte 0, 0, 0, 0, 0, 0, 0 scoreAreaLineLo !byte <.sma0 !byte <.sma1 !byte <.sma2 !byte <.sma3 !byte <.sma4 !byte <.sma5 !byte <.sma6 !byte <.sma7 !byte <.sma8 !byte <.sma9 !byte <.sma10 !byte <.sma11 !byte <.sma12 !byte <.sma13 !byte <.sma14 !byte <.sma15 !byte <.sma16 !byte <.sma17 !byte <.sma18 !byte <.sma19 !byte <.sma20 !byte <.sma21 !byte <.sma22 !byte <.sma23 !byte <.sma24 scoreAreaLineHi !byte >.sma0 !byte >.sma1 !byte >.sma2 !byte >.sma3 !byte >.sma4 !byte >.sma5 !byte >.sma6 !byte >.sma7 !byte >.sma8 !byte >.sma9 !byte >.sma10 !byte >.sma11 !byte >.sma12 !byte >.sma13 !byte >.sma14 !byte >.sma15 !byte >.sma16 !byte >.sma17 !byte >.sma18 !byte >.sma19 !byte >.sma20 !byte >.sma21 !byte >.sma22 !byte >.sma23 !byte >.sma24 pressAnyKey ;15 chars !byte $90,$92,$85,$93,$93,$A0,$81,$8E !byte $99,$A0,$8B,$85,$99 .pressAnyKeyEnd pressAnyKeyLength = .pressAnyKeyEnd - pressAnyKey - 1 pressAnyKeyOffset = (40 - pressAnyKeyLength) DIV 2 titleScreen !byte $20,$20,$20,$20,$20,$20,$20,$20 ;0 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$4F,TBR,$A0,$20 ;1 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$6C,$61 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$65,$20,$77,$20 ;2 !byte $A0,$50,$67,TBR,$A0,TBR,$A0,$20 !byte $A0,$50,$20,$A0,$67,$20,$FB,$EC !byte $20,$4F,$A0,$20,$A0,$50,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$65,$20,$6F,$20 ;3 !byte $A0,$67,$67,$20,$A0,$20,$A0,$20 !byte $A0,$67,$20,$A0,$67,$20,$E1,$61 !byte $20,$4F,TBR,$20,$A0,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$4C,BBR,$A0,$20 ;4 !byte $A0,$7A,$67,$20,$A0,$20,$A0,$20 !byte $A0,$7A,$20,$A0,$7A,$20,$E1,$61 !byte $20,$4C,$A0,$20,$A0,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;5 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $A0,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$62,$20,$20,$20 ;6 !byte $20,$20,$20,$20,$A0,TBR,$50,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$6C,$A0,$A0,$FB,$7B,$20 ;7 !byte $20,$20,$20,$20,$A0,BBR,BBR,$20 !byte $A0,$50,$20,$A0,$50,$20,$4F,$A0 !byte $20,$4F,$A0,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$A0,$FE,$FB,$A0,$A0,$20 ;8 !byte $20,$20,$20,$20,$20,$20,$A0,$20 !byte $A0,$67,$20,BBR,$7A,$20,$65,$20 !byte $20,$4F,TBR,$20,$20,$20,$20,$03 !byte $0F,$0E,$14,$12,$0F,$0C,$13,$20 !byte $20,$20,$A0,$FE,$A0,$FB,$A0,$20 ;9 !byte $20,$20,$20,$20,$4C,BBR,$A0,$20 !byte $A0,$7A,$20,$4C,$A0,$20,$4C,$A0 !byte $20,$4C,$A0,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$7C,$A0,$A0,$A0,$7E,$20 ;10 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $A0,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$81 !byte $20,$14,$08,$12,$15,$13,$14,$20 !byte $20,$20,$20,$20,$E2,$20,$20,$20 ;11 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;12 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$93 !byte $20,$06,$09,$12,$05,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;13 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $4F,$A0,$20,$4F,$50,$20,$4F,$50 !byte $20,$7A,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;14 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte BBR,$A0,$20,$65,$67,$20,$65,$67 !byte $20,$67,$20,$20,$20,$20,$20,$8B !byte $20,$0C,$05,$06,$14,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;15 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $65,$20,$20,$A0,$67,$20,$A0,$67 !byte $20,$67,$62,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;16 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $4C,$A0,$20,$A0,$7A,$20,$A0,$7A !byte $20,$7A,$A0,$20,$20,$20,$20,$8C !byte $20,$12,$09,$07,$08,$14,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;17 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;18 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;19 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$22,$03,$0F,$0D,$10,$15,$14 ;20 !byte $05,$12,$20,$13,$10,$01,$03,$05 !byte $22,$20,$0F,$12,$09,$07,$09,$0E !byte $01,$0C,$20,$01,$12,$03,$01,$04 !byte $05,$20,$07,$01,$0D,$05,$20,$20 !byte $20,$28,$03,$29,$20,$31,$39,$37 ;21 !byte $31,$20,$0E,$15,$14,$14,$09,$0E !byte $07,$20,$01,$13,$13,$0F,$03,$09 !byte $01,$14,$05,$13,$2C,$20,$09,$0E !byte $03,$2E,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$02,$19,$20 ;22 !byte $0E,$0F,$0C,$01,$0E,$20,$02,$15 !byte $13,$08,$0E,$05,$0C,$0C,$20,$01 !byte $0E,$04,$20,$14,$05,$04,$20,$04 !byte $01,$02,$0E,$05,$19,$3B,$20,$20 !byte $20,$10,$05,$14,$20,$32,$30,$30 ;23 !byte $31,$20,$07,$01,$0D,$05,$20,$02 !byte $19,$20,$0E,$2E,$20,$0C,$01,$0E !byte $04,$13,$14,$05,$09,$0E,$05,$12 !byte $2C,$20,$32,$30,$31,$37,$2E,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 ;24 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 !byte $20,$20,$20,$20,$20,$20,$20,$20 setupScreen0 !byte $03,$0F,$0D,$10,$15,$14,$05,$12 !byte $20,$13,$10,$01,$03,$05 setupScreen1 !byte $01,$20,$13,$09,$0D,$15,$0C,$01 !byte $14,$05,$04,$20,$02,$01,$14,$14 !byte $0C,$05,$20,$14,$08,$01,$14,$20 !byte $10,$09,$14,$13 setupScreen2 !byte $03,$0F,$0D,$10,$15,$14,$05,$12 !byte $2D,$07,$15,$09,$04,$05,$04,$20 !byte $13,$01,$15,$03,$05,$12,$13,$20 !byte $01,$07,$01,$09,$0E,$13,$14 setupScreen3 !byte $01,$20,$12,$0F,$03,$0B,$05,$14 !byte $20,$13,$08,$09,$10,$20,$14,$08 !byte $01,$14,$20,$19,$0F,$15,$20,$03 !byte $0F,$0E,$14,$12,$0F,$0C,$2E setupScreen4 !byte $05,$16,$01,$04,$05,$20,$14,$08 !byte $05,$20,$13,$01,$15,$03,$05,$12 !byte $27,$13,$20,$0D,$09,$13,$13,$09 !byte $0C,$05,$13,$20,$01,$0E,$04 setupScreen5 !byte $15,$13,$05,$20,$19,$0F,$15,$12 !byte $13,$20,$14,$0F,$20,$13,$03,$0F !byte $12,$05,$20,$08,$09,$14,$13,$2E setupScreen6 !byte $0F,$15,$14,$13,$03,$0F,$12,$05 !byte $20,$14,$08,$05,$20,$13,$01,$15 !byte $03,$05,$12,$13,$20,$06,$0F,$12 !byte $20,$05,$18,$2D setupScreen7 !byte $14,$05,$0E,$04,$05,$04,$20,$10 !byte $0C,$01,$19,$20,$09,$0E,$20,$08 !byte $19,$10,$05,$12,$13,$10,$01,$03 !byte $05,$2E setupScreen8 !byte $45,$45,$20,$20,$20,$20,$20,$45,$45 setupScreen9 !byte $12,$05,$03,$12,$05,$01,$14,$05 !byte $20,$0F,$12,$09,$07,$09,$0E,$01 !byte $0C,$20,$08,$05,$18,$2D,$13,$03 !byte $0F,$12,$09,$0E,$07,$20,$02,$15 !byte $07,$3F setupScreen10 !byte $13,$05,$0C,$05,$03,$14,$20,$13 !byte $03,$0F,$12,$09,$0E,$07,$20,$0D !byte $0F,$04,$05,$20,$28,$17,$12,$01 !byte $10,$29,$3A setupScreen11 !byte $12,$0F,$03,$0B,$05,$14,$20,$20 !byte $20,$20,$57,$20,$20,$04,$05,$03 !byte $09,$0D,$01,$0C,$20,$20,$20,$57 !byte $20,$20,$08,$05,$18 setupScreen12 !byte $13,$01,$15,$03,$05,$12,$20,$20 !byte $20,$20,$57,$20,$20,$04,$05,$03 !byte $09,$0D,$01,$0C,$20,$20,$20,$57 !byte $20,$20,$08,$05,$18 setupScreen13 !byte $15,$13,$05,$20,$03,$15,$12,$13 !byte $0F,$12,$20,$15,$10,$2F,$04,$0F !byte $17,$0E,$2C,$20,$13,$10,$01,$03 !byte $05,$20,$14,$0F,$20,$03,$08,$01 !byte $0E,$07,$05,$2C setupScreen14 !byte $10,$12,$05,$13,$13,$20,$12,$05 !byte $14,$15,$12,$0E,$20,$14,$0F,$20 !byte $03,$0F,$0E,$14,$09,$0E,$15,$05 !byte $2E setupScreenEnd setupScreenTable ; length, memory loc, screen address, zero terminated !byte setupScreen1 - setupScreen0 - 1 !word setupScreen0 !word SCREEN + 13 !byte setupScreen2 - setupScreen1 - 1 !word setupScreen1 !word SCREEN + screenCols*2 + 4 !byte setupScreen3 - setupScreen2 - 1 !word setupScreen2 !word SCREEN + screenCols*3 + 4 !byte setupScreen4 - setupScreen3 - 1 !word setupScreen3 !word SCREEN + screenCols*4 + 4 !byte setupScreen5 - setupScreen4 - 1 !word setupScreen4 !word SCREEN + screenCols*6 + 4 !byte setupScreen6 - setupScreen5 - 1 !word setupScreen5 !word SCREEN + screenCols*7 + 4 !byte setupScreen7 - setupScreen6 - 1 !word setupScreen6 !word SCREEN + screenCols*9 + 4 !byte setupScreen8 - setupScreen7 - 1 !word setupScreen7 !word SCREEN + screenCols*10 + 4 !byte setupScreen9 - setupScreen8 - 1 !word setupScreen8 !word SCREEN + screenCols*13 + 16 !byte setupScreen10 - setupScreen9 - 1 !word setupScreen9 !word SCREEN + screenCols*15 + 3 !byte setupScreen11 - setupScreen10 - 1 !word setupScreen10 !word SCREEN + screenCols*16 + 3 !byte setupScreen12 - setupScreen11 - 1 !word setupScreen11 !word SCREEN + screenCols*18 + 5 !byte setupScreen13 - setupScreen12 - 1 !word setupScreen12 !word SCREEN + screenCols*20 + 5 !byte setupScreen14 - setupScreen13 - 1 !word setupScreen13 !word SCREEN + screenCols*22 + 2 !byte setupScreenEnd - setupScreen14 - 1 !word setupScreen14 !word SCREEN + screenCols*23 + 8 !byte 0 ; ************************************************************************* ; eof
Assembles to ca. 6.6K.
◀ Previous:  Addendum I: The Quest Continues
▲ Back to the index.
May 2017, Vienna, Austria
www.masswerk.at – contact me.
— This series is part of Retrochallenge 2017/04. —