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.
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.
April 2017, Vienna, Austria
www.masswerk.at – contact me.
— This series is part of Retrochallenge 2017/04. —