Retrochallenge 2017/04:
Personal Computer Space Transactor 2001

Addendum II: Release & Download

Personal Computer Space Transactor 2001

Computer Space 2001 on the PET 2001.
(Image of PET 2001 [edited; N.L.]: Wikimedia / Tomislav Medak, 2009, Creative Commons.)

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.)

Personal Computer Space Transactor 2001, title

Computer Space 2001, title screen.

Personal Computer Space Transactor 2001, screens

Various screens of Computer Space 2001 (in white and green phosphor, emulation).
From top to bottom: Title screen, instructions and setup, attract mode, gameplay, extended play in hyperspace.
(Mind the recreated hex-scoring bug in the white hyperspace screen on the bottom left.)

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):

Intel 7448 BCD-to-7-Segement-Display digits

Digits as generated by the Intel 7448 BCD-to-seven-segment decoder (Intel, SDLS111, 1974/1988).
The blank display for decimal 15 is substituted by a dash in the game.

Compatibility

Personal Computer Space Transactor 2001 on the PET

Computer Space 2001 on a real PET 2001 (8K, ROM 1.0).
Photos by © 2017 Thomas Skibo.

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.)

Personal Computer Space Transactor 2001 on the C64

Computer Space 2001 on the C64 — dramatically increasing the potential user base.
(Yay, not as elegant as on the PET, due to the C64's bold font, otherwise just the same game.)

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.

— This series is part of Retrochallenge 2017/04. —