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