Retrochallenge 2017/04:
Personal Computer Space Transactor 2001

Episode 9: +++ Progress Update (The End is Nigh) +++

Updates on work in progress …

Spending the final hours busily. By now, there are guided rocket missiles and missiles for the saucers. The AI is also fairly complete by now — and fully working. Hit detection and scoring are also done. All the building blocks for effects (spinning rocket and reverse video effect for a player's missile hitting a saucer, see previous episodes) are already in place. However, I'm still lacking the wrappers and control logic for them to make this a complete game. — Please check this site for updates.

Meanwhile: Hit detection pretty much done, a bit too tired to do the effect routines — calling it a day. We have the basic game mechanics working, while still lacking any effects! Going to finish this later. Sorry. — However, we've achieved some, stay tuned for a wrap-up.

Currently, there's a tiny bug in the collision-detection which I'm currently not able to spot. The hit detection issue was really a simple one, some hours of sleep provided. With reset routines and scoring already in place, we could simply disable the rocket at time-wrap, if the players score doesn't exceed the scaucers' score. This way, we would have already a fully playable game, while still lacking bells, whistles, and polish. — Just so short from somehow reaching the finish line …

Overall, while I haven't finished and polish the game in time, it's a proof of concept for things like this being done on the PET. (Where's Asteroids? Where's Spacewar? It can be done!) I'm rather suprised by how well some of the motions work in isolation — while too much of them are prone to a more cluttery experience. Especially with a bit more of experience — did I mention that I haven't touched the 6502 for more than 30 years? — and by going for ROM 2 and ROM 4 exclusively (are there any real, existing ROM 1.0 machines?), hence dropping the bottleneck of updating the screen during V-BLANK only, some really aracady games, other than Space Invaders, can be done on the PET.

+++ MISSION UPDATE +++ missiles implemented for rocket ship and saucer +++
stop +++ guided missile logic for player's shots final +++ stop +++
+++ sub-character-mapping for missiles fully implemented (including overlap) +++
stop +++ saucer AI (targeting) complete +++ stop +++ hit detection, scoring  and
reset routines pretty much done +++ effect wrappers still in the making +++
stop +++ also to be done: a bit of polishing (e.g., 'press any key to start'
message, etc.) +++ stop +++ 
Statistics:

Lines of Code (LOC) .......... 2,229
Bytes assembled .............. 4,790

Note: There still seem to be minor quirks in the collision detection. :-/

See the final state of affairs, as for RC2017/04, in in-browser emulation.

Note: Previously, this link didn't set the flag to disable the keyboard repeat in the emulator, rendering the game rather unresponsive. — Bummer.

See this link for the game in the current state of ongoing development.

Personal Computer Space Transactor 2001

Coming soon to a PET near you.

Code Listing

; Personal Computer Space Transactor 2001 (work in progress)
; by Norbert Landsteiner, 2017
; (a project for RetroChallenge 2017/04)

; Original Computer Space arcade game (c) 1971 Nutting Associates, Inc.
; by Nolan Bushnell and Ted Dabney.

!to "missiles.prg", cbm ;set output file and format

; symbols / constants

screenCols = 40       ;number of screen columns: 40/80 (only 40 cols tested)
ticksPerSecond = 60   ;60: time in game corresponds to NTSC timing

charQueue = $027A     ;start of cassette buffer, used as a drawing buffer
resetQueue = charQueue+60 ;buffer for screen resets

maxX = 45             ;x-coors max value
maxY = 28             ;y-coors max value

rocketFreq = 12       ;frames (update frequency, responsiveness)
saucerFreq = 8        ;frames (update frequency, speed)
saucerOffset = 14     ;screen lines y offset (maxY/2)
rocketVMax = 40       ;max velocity fractional value

missileLife = 100     ;frames
saucerCooling = 80    ;frames

; zero-page
; BASIC input buffer at $23 .. $5A may be reused safely (cf, PET 2001 manual)

gameState = $23       ;0: attract, 1: active
fIRQ  = $24           ;flag to synchronize irq operations
fRepaint = $25        ;flag for video rendering/irq control
ticks = $26           ;ticks counter
videoMask = $27       ;0: normal, $80: reverse (xor-ed)
IRQVector = $28       ;backup of original irq vector (2 bytes)
newRom = $2A          ;flag ROM 1.0 (0) / ROM 2.0+ (1)
qPosX = $2B           ;temp x coor for display purpose
qPosY = $2C           ;temp y coor for display purpose
qScreenCode = $2D     ;temp screen code for display purpose
charQueuePtr = $2E    ;pointer to top offset of charQueue
resetQueuePtr = $2F   ;pointer to top offset of charQueue
scoreRepaint = $30    ;flag to request a repaint (buffer to fRepaint)
frameCounter = $31    ;counter for animations
ran = $32             ;random number (1 byte)
fReset = $33
tmp = $34
PT1 = $50             ;versatile pointer (2 bytes)
PT2 = $52             ;versatile pointer (2 bytes)
IPT1 = $54            ;versatile pointer for interrupt tasks (2 bytes)
IPT2 = $56            ;versatile pointer for interrupt tasks (2 bytes)

; intro

; insert a tiny BASIC program, calling our code at $044C (1100)
;
; 10 REM PERSONAL COMPUTER SPACE
; 20 REM TRANSACTOR 2001, V.0.2, 2017
; 30 SYS 1103

                * = $0401

                !byte $1F, $04, $0A, $00, $8F, $20, $50, $45 ; $0401
                !byte $52, $53, $4F, $4E, $41, $4C, $20, $43 ; $0409
                !byte $4F, $4D, $50, $55, $54, $45, $52, $20 ; $0411
                !byte $53, $50, $41, $43, $45, $00, $42, $04 ; $0419
                !byte $14, $00, $8F, $20, $54, $52, $41, $4E ; $0421
                !byte $53, $41, $43, $54, $4F, $52, $20, $32 ; $0429
                !byte $30, $30, $31, $2C, $20, $56, $2E, $30 ; $0431
                !byte $2E, $32, $2C, $20, $32, $30, $31, $37 ; $0439
                !byte $00, $4D, $04, $1E, $00, $9E, $20, $31 ; $0441
                !byte $31, $30, $33, $00, $00, $00  ; $0449 .. $044E


; main

                * = $044F
                ; reset / setup
                cld  ;reset BCD flag
                lda #0
                sta fRepaint

setup           ; setup irq vector
                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


title
                jsr drawTitleScreen
                jsr readKbd           ; reset the keyboard
                lda #1
                sta fIRQ

titleLoop
                lda fIRQ
                bne titleLoop
                jsr readKbd
                cmp #0
                bne init
                lda #1
                sta fIRQ
                jmp titleLoop


init
                lda #0
                sta gameState
                sta videoMask
                sta fRepaint
                jsr background
                lda #0
                sta score1
                sta score2
                sta time1
                sta time2
                sta ticks
                sta frameCounter
                sta charQueuePtr
                sta saucerCnt
                sta saucerLegCnt
                sta rocketCnt
                lda #1
                sta saucerState

                lda $E844            ; initialize random number from VIA timer 1
                sta ran

                jsr readKbd          ; reset the keyboard

                lda #10
                sta saucerY
                lda #18
                sta saucerX
                jsr displaySaucer
                jsr animateSaucer
                jsr displaySaucerCenter

                lda #3
                sta rocketDir
                lda #10
                sta rocketX
                lda #12
                sta rocketY

                lda #1
                sta fRepaint
                sta fIRQ


; main job loop
loop
                lda fIRQ
                bne loop

                lda #0                 ;reset top-of-queue pointers
                sta charQueuePtr
                sta resetQueuePtr
                sta fReset

                lda gameState
                bne .gameRunning

.attractMode
                lda rocketCnt
                cmp #30
                beq .amkbd
                jsr readKbd             ;reset keyboard
                inc rocketCnt
                bpl .gameFrame
.amkbd          jsr readKbd
                cmp #0
                beq .gameFrame
                lda #0
                sta ticks
                sta rocketCnt
                inc gameState
                inc rocketState
                jsr displayRocket

.gameRunning
                ;manage a frame
                lda #0
                sta scoreRepaint
                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
                jsr revertVideo
.loopScoresFinal
                lda #1
                sta scoreRepaint

.gameFrame
                jsr checkCollisions
                lda fReset
                bne .loopIter
                jsr rocketHandler
                jsr saucerHandler

.loopIter sei
                lda scoreRepaint
                ora resetQueuePtr
                ora charQueuePtr
                sta fRepaint
.loopEnd        lda #1
                sta fIRQ
                cli
                jmp loop


; irq handling

irqRoutine
                pha                   ;save registers
                txa
                pha
                tya
                pha

                inc ticks             ;manage time
                inc frameCounter

.checkRepaint
                lda fRepaint
                beq .irqDone
                jsr drawResetQueue
                jsr drawScores
                jsr drawCharQueue

.irqDone
                lda #0
                sta fRepaint
                sta fIRQ
                pla                    ;restore register
                tay
                pla
                tax
                pla
                jmp (IRQVector)


; subroutines

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


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
; screen locations of score and time numerals
screenAddressScore1 = $8000 + 4*screenCols + 36
screenAddressScore2 = $8000 + 10*screenCols + 36
screenAddressTime1 = $8000 + 16*screenCols + 36
screenAddressTime2 = $8000 + 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 and increment IPT1 by a screen line
                clc
                lda IPT1
                adc #screenCols
                sta IPT1
                bcc .dgRow
                inc IPT1+1
                jmp .dgRow
.dgDone         rts


revertVideo ;reverts the screen video
                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
                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       ;gte 25 (off-screen to the bottom)?
                bcs .pcqDone
                lda qPosX
                bmi .pcqDone  ;negative
                cmp #40       ;gte 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
                lda qPosY
                bmi .psrDone  ;negative
                cmp #25       ;gte 25 (off-screen to the bottom)?
                bcs .psrDone
                lda qPosX
                bmi .psrDone  ;negative
                cmp #40       ;gte 40 (off-screen to the right)?
                bcs .psrDone

                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


random ; a simple random number generator
                lda ran
                ror
                lda ran
                ror
                eor %11011001
                sta ran
                rts


; collisions

checkCollisions
                lda rocketState
                ora saucerState
                bne .ccRS
                rts
.ccRS           lda rocketX
                sta PT1
                lda rocketY
                sta PT1+1
                lda saucerX
                sta PT2
                lda saucerY
                sta PT2+1
                ldx #2
                ldy #2
                jsr checkObjectCollison
                bcs .ccRocketMissiles
                jsr resetRocket
                jsr resetSaucer
                jsr resetMissiles
                jsr incScore1
                jsr incScore2
                rts
.ccRocketMissiles
                lda rocketMissileCnt
                beq .ccSaucerMissile
                lda rocketMissileX
                sta PT1
                lda rocketMissileY
                sta PT1+1
                lda saucerX
                sta PT2
                lda saucerY
                sta PT2+1
                ldx #3
                ldy #2
                jsr checkObjectCollison
                bcs .ccSaucerMissile
                jsr resetSaucer
                jsr resetMissiles
                jsr incScore1
                rts
.ccSaucerMissile
                lda saucerMissileCnt
                beq .ccDone
                lda rocketX
                sta PT1
                lda rocketY
                sta PT1+1
                lda saucerMissileX
                sta PT2
                lda saucerMissileY
                sta PT2+1
                ldx #2
                ldy #2
                jsr checkObjectCollison
                bcs .ccDone
                jsr resetRocket
                jsr resetMissiles
                jsr incScore2
.ccDone         rts

incScore1
                lda score1
                clc
                adc #1
                cmp #10
                bne .incS1End
                lda #0
.incS1End       sta score1
                inc scoreRepaint
                rts

incScore2
                lda score2
                clc
                adc #1
                cmp #10
                bne .incS2End
                lda #0
.incS2End       sta score2
                inc scoreRepaint
                rts

;PT1: x1, PT1+1: y1
;PT2: x2, PT2+1: y2
;X: epsilon-x, Y: epsilon-y
;returns abs x1-x2 < eX and abs y1-y2 < eY in carry
checkObjectCollison
                stx tmp
                sec
                lda PT1
                sbc PT2
                bpl .cocXCmp
                eor #$FF
                clc
                adc #1
.cocXCmp        cmp tmp
                bcs .cocDone
                sty tmp
                sec
                lda PT1+1
                sbc PT2+1
                bpl .cocYCmp
                eor #$FF
                clc
                adc #1
.cocYCmp        cmp tmp
.cocDone        rts

; object resets

resetRocket
                lda #1
                sta fReset
                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
                jsr displayRocket
                rts

resetSaucer
                lda #1
                sta fReset
                lda saucerState
                beq .rsts0
                jsr clearSaucer
                jsr clearSaucerCenter
                jsr flipSaucer
                jsr clearSaucer
                jsr clearSaucerCenter
.rsts0          jsr random
                clc
                and #31
                adc #16
                clc
                adc rocketX
                cmp #maxX
                bcc .rstsX
                sec
                sbc #maxX
.rstsX          sta saucerX
                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
.rstsDone
                jsr displaySaucer
                jsr displaySaucerCenter
                jsr flipSaucer
                jsr displaySaucer
                jsr displaySaucerCenter
                jsr flipSaucer
                rts

resetMissiles
                lda rocketMissileCnt
                beq .rstmScr
                jsr clearRocketMissile
                lda #0
                sta rocketMissileCnt
.rstmScr        lda #saucerCooling
                sta saucerMissileCooling
                lda saucerMissileCnt
                beq .rstmDone
                jsr clearSaucerMissile
                lda #0
                sta saucerMissileCnt
.rstmDone       rts

; saucer(s)

saucerHandler
                lda saucerState
                bne .shInit
                rts
.shInit         dec saucerCnt
                bmi .shUpdate
                lda scoreRepaint   ;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
                sta saucerCnt
                jsr clearSaucer
                jsr flipSaucer
                jsr clearSaucer
                jsr flipSaucer

                dec saucerLegCnt
                bpl .shMoveY
                jsr random
                and #15
                clc
                adc #7
                sta saucerLegCnt
                lda ran
                and #1
                sta saucerPhaseDir
                ldx #3
                lda ran
                bpl .shAnimSpeed
                ldx #7
.shAnimSpeed    stx saucerPhaseMask
                jsr random
                and #$3F
                beq .shStop
                and #3
                cmp #3
                bne .shSaveDx
                lda #0
.shSaveDx       sta saucerDx
                jsr random
                and #3
                cmp #3
                bne .shSaveDy
                lda #0
.shSaveDy       sta saucerDy

.shMoveY        ldx saucerY
                lda saucerDy
                beq .shMoveX
                cmp #1
                beq .shMoveY1
                inx
                cpx #maxY
                bcc .shSaveY
                ldx #0
                jmp .shSaveY
.shMoveY1       dex
                bpl .shSaveY
                ldx #maxY-1
.shSaveY        stx saucerY

.shMoveX        ldx saucerX
                lda saucerDx
                beq .shDisplay
                cmp #1
                beq .shMoveX1
                inx
                cpx #maxX
                bcc .shSaveX
                ldx #0
                jmp .shSaveX
.shMoveX1       dex
                bpl .shSaveX
                ldx #maxX-1
.shSaveX        stx saucerX

.shDisplay
                jsr displaySaucer
                jsr flipSaucer
                jsr displaySaucer
                jsr flipSaucer

.shAnimate      lda frameCounter
                and saucerPhaseMask
                bne .shDone
                jsr animateSaucer
                jsr displaySaucerCenter

.shDone         lda saucerMissileCnt
                bne .shmissle
                jmp saucerAI
.shmissle       jsr saucerMissileHandler
                rts

.shStop
                lda #0
                sta saucerDx
                sta saucerDy
                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
                beq .sai0
                dec saucerMissileCooling
                rts
.sai0           lda rocketState
                cmp #1
                beq .sai1
                rts

.sai1
                jsr random
                and #3
                sec
                sbc #1
                sta tmp
                lda rocketDxLo
                asl
                sta PT1
                lda rocketDx
                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
                adc tmp
                bmi .sai1X1
                cmp #maxX
                bcc .sai1X2
                sec
                sbc maxX
                jmp .sai1X2
.sai1X1         clc
                adc maxX
                ;lda rocketX
.sai1X2         sec
                sbc saucerX
                sta PT1
                bmi .saiXAbs
                sta PT1+1
                jmp .sai1Y
.saiXAbs        eor #$FF             ;absolute value
                sta PT1+1
                inc PT1+1

.sai1Y
                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
                clc
                adc rocketY
                adc tmp
                bmi .sai1Y1
                cmp #maxY
                bcc .sai1Y2
                sec
                sbc maxY
                jmp .sai1Y2
.sai1Y1         clc
                adc maxY
                ;lda rocketY
.sai1Y2         sec
                sbc saucerY
                sta PT2
                bmi .saiYAbs
                sta PT2+1
                jmp .saiVert
.saiYAbs        eor #$FF             ;absolute value
                sta PT2+1
                inc PT2+1

.saiVert        jsr random
                and #3
                tay
                cmp PT1+1
                bcc .saiHor
                ldx #0
                lda PT1
                bpl .saiFire
                ldx #1
                jmp .saiFire

.saiHor         tya
                cmp PT2+1
                bcc .saiDiag
                ldx #2
                lda PT2
                bpl .saiFire
                ldx #3
                jmp .saiFire

.saiDiag sec
                lda PT1+1
                sbc PT2+1
                bpl .saiDiag1
                eor #$FF
                clc
                adc #1
.saiDiag1       sta tmp
                tya
                cmp tmp
                bcc .saiDone
                ldx #4
                lda PT1
                bmi .saiDiag2
                inx
.saiDiag2       lda PT2
                bmi .saiFire
                inx
                inx
                jmp.saiFire

.saiDone        rts

.saiFire        lda saucerMissileDirDxLo, x
                sta saucerMissileDxLo
                lda saucerMissileDirDx, x
                sta saucerMissileDx
                lda saucerMissileDirDyLo, x
                sta saucerMissileDyLo
                lda saucerMissileDirDy, x
                sta saucerMissileDy
                clc
                lda saucerMissileOffsetX, x
                adc saucerX
                sta saucerMissileX
                clc
                lda saucerMissileOffsetY, x
                adc saucerY
                sta saucerMissileY
                lda saucerMissileInitialX, x
                sta saucerMissileXLo
                lda saucerMissileInitialY, x
                sta saucerMissileYLo
                lda #missileLife
                sta saucerMissileCnt
                jsr displaySaucerMissile
                rts


; rocket

rocketHandler
                lda rocketState
                bne .rhInit
                rts
.rhInit         lda #0
                sta rocketRedraw
                lda rocketX
                sta rocketXN
                lda rocketY
                sta rocketYN
                lda rocketDir
                sta rocketDirN
                lda #0
                sta rocketThrustingN

                lda rocketMissileCnt
                beq .rhScan
                jsr rocketMissileHandler

.rhScan         jsr readKbd
                cmp #$4B              ;K
                beq .rhLeft
                cmp #$4A              ;J (for big hands)
                beq .rhLeft
                cmp #$4C              ;L
                beq .rhRight
                cmp #$41              ;A
                beq .rhThrust
                cmp #$53              ;S
                beq .rhFire
                cmp #$44              ;D (for big hands)
                beq .rhFire
                jmp .rhMove

.rhLeft lda #$FF
                sta rocketTurn
                jmp .rhMove

.rhRight lda #$01
                sta rocketTurn
                jmp .rhMove

.rhFire         lda rocketMissileCnt
                bne .rhfDone
                jsr rocketMissileInit
.rhfDone        jmp .rhMove

.rhThrust
                inc rocketThrustingN
                ldx rocketDirN              ;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         dec rocketCnt               ;process Turn on rocketCnt underflow
                bpl .rhMoveX
                lda #rocketFreq
                sta rocketCnt
                lda rocketTurn
                beq .rhMoveX                ;empty / no turn
                clc
                adc rocketDir
                and #7
                sta rocketDirN
                inc rocketRedraw            ;flag for redraw
                lda #0                      ;reset turn
                sta rocketTurn
                lda rocketMissileCnt
                beq .rhMoveX
                jsr rocketMissileUpdate

.rhMoveX                        ;move by dx
                lda rocketDxLo
                pha                         ;emulate 'asr' instruction
                rol
                pla
                ror
                clc                         ;sum it
                adc rocketXLo
                sta rocketXLo
                lda rocketX
                adc rocketDx
                bmi .rhMoveXM               ;branch to warp to right on negativ
                cmp #maxX
                bcc .rhSaveX
                lda #0                      ;wrap to left
                jmp .rhSaveX
.rhMoveXM       lda #maxX-1
.rhSaveX        sta rocketXN                ;save updated value
                cmp rocketX                 ;evaluate redraw
                beq .rhMoveY
                inc rocketRedraw
.rhMoveY                                    ;move by dy
                lda rocketDyLo
                pha                         ;emulate 'asr' instruction
                rol
                pla
                ror
                clc                         ;sum it
                adc rocketYLo
                sta rocketYLo
                lda rocketY
                adc rocketDy
                bmi .rhMoveYM               ;branch to wrap to bottom on negative
                cmp #maxY
                bcc .rhSaveY
                lda #0                      ;wrap to top
                jmp .rhSaveY
.rhMoveYM       lda #maxY-1
.rhSaveY        sta rocketYN                ;save updated value
                cmp rocketY                 ;evaluate redraw
                beq .rhRedraw
                inc rocketRedraw

.rhRedraw
                lda rocketThrusting
                beq .rhRedraw2
                cmp rocketThrustingN
                bne .rhRedraw1
                lda rocketRedraw
                beq .rhRedraw3
.rhRedraw1      jsr clearThrust
.rhRedraw2      lda rocketRedraw
                beq .rhRedraw3
                jsr clearRocket
                lda rocketDirN
                sta rocketDir
                lda rocketXN
                sta rocketX
                lda rocketYN
                sta rocketY
                jsr displayRocket
.rhRedraw3      lda rocketThrustingN
                sta rocketThrusting
                beq .rhDone
                jsr drawThrust
.rhDone         rts


; rocket missile

rocketMissileInit
                ldx rocketDir
                lda rocketMissileDirDx, x
                sta rocketMissileDxLo
                bmi .rmiX1
                lda #0
                jmp .rmiX2
.rmiX1          lda #$FF
.rmiX2          sta rocketMissileDx
                lda rocketMissileInitialX,x
                sta rocketMissileXLo
                lda rocketMissileDirDy, x
                sta rocketMissileDyLo
                bmi .rmiY1
                lda #0
                jmp .rmiY2
.rmiY1          lda #$FF
.rmiY2          sta rocketMissileDy
                lda rocketMissileInitialY,x
                sta rocketMissileYLo
                clc
                lda rocketX
                adc rocketMissileOffsetX, x
                sta rocketMissileX
                clc
                lda rocketY
                adc rocketMissileOffsetY, x
                sta rocketMissileY
                lda #missileLife
                sta rocketMissileCnt
                jsr displayRocketMissile
                rts

rocketMissileHandler
                jsr clearRocketMissile
                dec rocketMissileCnt
                beq .rmhDone
                clc
                lda rocketMissileDxLo
                adc rocketMissileXLo
                sta rocketMissileXLo
                lda rocketMissileDx
                adc rocketMissileX
                bmi .rmhXM
                cmp #maxX
                bcc .rmhSaveX
                lda #0
                jmp .rmhSaveX
.rmhXM                          lda #maxX-1
.rmhSaveX       sta rocketMissileX
                clc
                lda rocketMissileDyLo
                adc rocketMissileYLo
                sta rocketMissileYLo
                lda rocketMissileDy
                adc rocketMissileY
                bmi .rmhYM
                cmp #maxY
                bcc .rmhSaveY
                lda #0
                jmp .rmhSaveY
.rmhYM                          lda #maxY-1
.rmhSaveY       sta rocketMissileY
                jsr displayRocketMissile
.rmhDone        rts

rocketMissileUpdate ;update missile motion vector on rocket turn
                ldx rocketDirN                 ;new direction
                lda rocketMissileDxLo          ;average old dx, new dx
                pha                            ;emulate 'asr' (old dx/2)
                rol
                pla
                ror
                clc
                adc rocketMissileUpdateDx, x   ;new dx/2 from table
                sta rocketMissileDxLo
                bmi .rmuXM                     ;arrange hi-bite for sign
                lda #0
                jmp .rmuSaveX
.rmuXM          lda #$FF
.rmuSaveX       sta rocketMissileDx
                adc rocketMissileDyLo          ;average old dy, new dy
                pha                            ;emulate 'asr' (old dy/2)
                rol
                pla
                ror
                clc
                adc rocketMissileUpdateDy, x   ;new dy/2 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
                jsr clearSaucerMissile
                dec saucerMissileCnt
                bne .smh1
                lda #saucerCooling
                sta saucerMissileCooling
                rts
.smh1           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 saucerMissileX
                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 saucerMissileY
                jsr displaySaucerMissile
                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
                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 #$1C
                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 #$1C
                sta qScreenCode
                jsr pushScreenCode
                ldx qPosY
                inx
                stx qPosY
                lda #$63
                sta qScreenCode
                jsr pushScreenCode
                rts
.dr4
                lda #$1C
                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 #$1C
                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


drawThrust ;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 #0
                lda rocketMissileXLo
                and #$80
                beq .drm1
                inx
.drm1           lda rocketMissileYLo
                and #$80
                beq .drm2
                inx
                inx
.drm2           stx rocketMissileDisplayC
                lda missileCodes, x
                sta qScreenCode
                ldx rocketMissileX
                dex
                dex
                stx qPosX
                stx rocketMissileDisplayX
                ldx rocketMissileY
                dex
                dex
                stx qPosY
                stx rocketMissileDisplayY
                jsr pushScreenCode
                rts

clearRocketMissile
                ldx rocketMissileDisplayY
                stx qPosY
                ldy rocketMissileDisplayX
                sty qPosX
                jsr getStar
                sta qScreenCode
                jsr pushScreenReset
                rts


displaySaucerMissile
                ldx saucerMissileX
                dex
                dex
                stx qPosX
                stx saucerMissileDisplayX
                ldx saucerMissileY
                dex
                dex
                stx qPosY
                stx saucerMissileDisplayY

                ldy #0                          ;overlaps with rocket missile?
                lda rocketMissileCnt
                beq .dsm0
                lda rocketMissileX
                cmp qPosX
                bne .dsm0
                lda rocketMissileY
                cmp qPosY
                bne .dsm0
                iny                             ;yes, set flag in Y
.dsm0           ldx #0
                lda saucerMissileXLo
                and #$80
                beq .dsm1
                inx
.dsm1           lda saucerMissileYLo
                and #$80
                beq .dsm2
                inx
                inx
.dsm2           cpy #1                          ;flag for overlap set?
                bne .dsm3
                stx PT1                         ;resolve overlapping display codes
                lda rocketMissileDisplayC
                rol
                rol
                eor PT1
                tax
                lda missileCodeCombinations, x
                jmp .dsm4
.dsm3           lda missileCodes, x             ;single display code
.dsm4           sta qScreenCode
                jsr pushScreenCode
                rts

clearSaucerMissile
                ldx saucerMissileDisplayY
                stx qPosY
                ldy saucerMissileDisplayX
                sty qPosX
                jsr getStar
                sta qScreenCode
                jsr pushScreenReset
                rts


drawTitleScreen ;draws the title screen (directly into screen memory)
                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        rts

; variables

score1          !byte 0
score2          !byte 0
time1           !byte 0
time2           !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

rocketState     !byte 0
rocketX         !byte 0
rocketY         !byte 0
rocketXLo       !byte 0
rocketYLo       !byte 0
rocketDx        !byte 0
rocketDy        !byte 0
rocketDxLo      !byte 0
rocketDyLo      !byte 0
rocketDir       !byte 0
rocketThrust    !byte 0
rocketCnt       !byte 0
rocketXN        !byte 0
rocketYN        !byte 0
rocketDirN      !byte 0
rocketRedraw    !byte 0
rocketTurn      !byte 0
rocketThrusting !byte 0
rocketThrustingN !byte 0

rocketMissileX        !byte 0
rocketMissileY        !byte 0
rocketMissileXLo      !byte 0
rocketMissileYLo      !byte 0
rocketMissileDx       !byte 0
rocketMissileDy       !byte 0
rocketMissileDxLo     !byte 0
rocketMissileDyLo     !byte 0
rocketMissileCnt      !byte 0
rocketMissileDisplayX !byte 0
rocketMissileDisplayY !byte 0
rocketMissileDisplayC !byte 0

saucerMissileX        !byte 0
saucerMissileY        !byte 0
saucerMissileXLo      !byte 0
saucerMissileYLo      !byte 0
saucerMissileDx       !byte 0
saucerMissileDy       !byte 0
saucerMissileDxLo     !byte 0
saucerMissileDyLo     !byte 0
saucerMissileCnt      !byte 0
saucerMissileDisplayX !byte 0
saucerMissileDisplayY !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

rocketMissileDirDx
                !byte -16,  16,  64,  64,  16, -16, -64, -64

rocketMissileDirDy
                !byte -64, -64, -16,  16,  64,  64,  16, -16

rocketMissileUpdateDx
                !byte -8,  8,  32,  32,  8, -8, -32, -32

rocketMissileUpdateDy
                !byte -32, -32, -8,  8,  32,  32,  8, -8

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, 72, -72,  -48,  48, -48, 48

saucerMissileDirDyLo
                !byte 72, -72, 0, 0,  -48, -48,  48, 48

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,  $20, $E0, $20, $E0

saucerMissileInitialY
                !byte $E0, $20, $E0, $E0,  $E0, $E0, $20, $20

missileCodes
                !byte $7E, $7C, $7B, $6C

missileCodeCombinations
                !byte $7E, $E2, $61, $7F ;combinations with top left ($7E)
                !byte $E2, $7C, $FF, $E1 ;combinations with top right ($7C)
                !byte $61, $FF, $7B, $62 ;combinations with bottom left ($7B)
                !byte $7F, $E1, $62, $6C ;combinations with bottom right ($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 >($8000 + screenCols * 0)
                !byte >($8000 + screenCols * 1)
                !byte >($8000 + screenCols * 2)
                !byte >($8000 + screenCols * 3)
                !byte >($8000 + screenCols * 4)
                !byte >($8000 + screenCols * 5)
                !byte >($8000 + screenCols * 6)
                !byte >($8000 + screenCols * 7)
                !byte >($8000 + screenCols * 8)
                !byte >($8000 + screenCols * 9)
                !byte >($8000 + screenCols * 10)
                !byte >($8000 + screenCols * 11)
                !byte >($8000 + screenCols * 12)
                !byte >($8000 + screenCols * 13)
                !byte >($8000 + screenCols * 14)
                !byte >($8000 + screenCols * 15)
                !byte >($8000 + screenCols * 16)
                !byte >($8000 + screenCols * 17)
                !byte >($8000 + screenCols * 18)
                !byte >($8000 + screenCols * 19)
                !byte >($8000 + screenCols * 20)
                !byte >($8000 + screenCols * 21)
                !byte >($8000 + screenCols * 22)
                !byte >($8000 + screenCols * 23)
                !byte >($8000 + screenCols * 24)

screenLinesLo
                !byte <($8000 + screenCols * 0)
                !byte <($8000 + screenCols * 1)
                !byte <($8000 + screenCols * 2)
                !byte <($8000 + screenCols * 3)
                !byte <($8000 + screenCols * 4)
                !byte <($8000 + screenCols * 5)
                !byte <($8000 + screenCols * 6)
                !byte <($8000 + screenCols * 7)
                !byte <($8000 + screenCols * 8)
                !byte <($8000 + screenCols * 9)
                !byte <($8000 + screenCols * 10)
                !byte <($8000 + screenCols * 11)
                !byte <($8000 + screenCols * 12)
                !byte <($8000 + screenCols * 13)
                !byte <($8000 + screenCols * 14)
                !byte <($8000 + screenCols * 15)
                !byte <($8000 + screenCols * 16)
                !byte <($8000 + screenCols * 17)
                !byte <($8000 + screenCols * 18)
                !byte <($8000 + screenCols * 19)
                !byte <($8000 + screenCols * 20)
                !byte <($8000 + screenCols * 21)
                !byte <($8000 + screenCols * 22)
                !byte <($8000 + screenCols * 23)
                !byte <($8000 + 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,$20
                !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

digitOffsets
                !byte 0, 8, 16, 24, 32, 40, 48, 56, 64, 72


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,$63,$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,$63,$A0,$63,$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,$63,$20,$A0,$20,$20,$20
                !byte $20,$20,$20,$20,$20,$20,$20,$20
                !byte $20,$20,$20,$20,$4C,$64,$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,$63,$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,$64,$64,$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,$64,$7A,$20,$65,$20
                !byte $20,$4F,$63,$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,$64,$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 $64,$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,$20,$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

 

— Stay tuned! —

 

Next:  Episode 10: Not the End, Yet, Still Some of a Wrap-Up

Previous:  Episode 8: Space Commander

Back to the index.

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