# Episode 7: Rocket (Phew!)

About some hassles, despair, and a rocket ship…

Welcome to what may have well been the last episode. But more on this later. We've a rocket ship on the screen, and while it also rotates, it doesn't do much else for the moment, thanks to circumstances which we'll discuss in a minute. However, this is what we eventually get:

Try it in in-browser emulation.

## Programming a Rocket Ship — Or the Nerve-Wracking Tale of How I Grew a Gray Hair

### Plans

As following readers may recall, we already had a sketch of what the rocket ship was to look like (along other elements of the game):

For our rocket ship this translates to the following data (starting with code 0 at the top row in the image above and advancing clockwise, all around to code 7):

```screen      coors       thrust        direction      internal
codes       offset      offset         (dx/dy)        code

\$67,\$1C     x-1, x,     t: x/y+1       d: -1/-3         0

\$2F,\$65     x, x+1      t: x/y+1       d: 1/-3          1

\$64,        y-1,        t: x-1/y       d: 3/-1          2
\$2F         y

\$1C,        y,          t: x-1/y       d: 3/1           3
\$63         y+1

\$1C,\$65     x, x+1      t: x/y-1       d: 1/3           4

\$67,\$2F     x-1, x      t: x/y-1       d: -1/3          5

\$2F,        y,          t: x+1/y       d: -3/1          6
\$63         y+1

\$64,        y-1,        t: x+1/y       d: -3/-1         7
\$1C         y
```

### Stepping out

Provided the procedures and abstractions we have already in place, this is easy to implement (at first). By the use of a dispatch table we're servicing the screen codes for our display, by pushing these along with the screen addresses to the drawing queue:

```
displayRocket ;pushes the rocket to the charQueue (VERSION 1)
ldx rocketX
dex                   ;2 pos offset
dex
stx qPosY
ldy rocketX
dey                   ;2 pos offset
dey
sty qPosX
lda rocketDir         ;dispatch on value in rocketDir (0..7)
rol                   ;×2
tay                   ;AC -> Y
lda .drJumpTable+1,y  ;Hi-byte
sta PT1+1
lda .drJumpTable,y    ;Lo-byte
sta PT
jmp (PT1)             ;dispatch (to .dr0 .. .dr7)
.dr0
lda #\$1C
sta qScreenCode
jsr pushScreenCode
ldx qPosX
dex
stx qPosX
lda #\$67
sta qScreenCode
jsr pushScreenCode
rts
.dr1
;...code....
.dr2
;...code....
.dr3
;...code....
.dr4
;...code....
.dr5
;...code....
.dr6
;...code....
.dr7
;...code....

.drJumpTable
!word .dr0
!word .dr1
!word .dr2
!word .dr3
!word .dr4
!word .dr5
!word .dr6
!word .dr7```

And a similar one for clearing the rocket. This works fine for putting the rocket on the display, but as we're trying to rotate it, the programm stops after a few iterations.

### In Search for an Error

At first it looks like a timing issue and I'm making the program bulletproof regarding any colissions with the interrupt rountine (including using separate pointers during interrupt, while our repaint-flag should really take care of this sufficiently). — To still the same result.

Then I'm testing the program with various parts disabled. While the program stops at the progression of a certain second (the twentieth), it isn't related to the scores/time drawing routine. With all code regarding to this disabled, it's still the same.

After testing all variations, I'm pinning down the culprit: The `clearRocket` routine, which is in essence a copy of the drawing routine above, but pushing background characters to the reset queue. It looks fine, no error to be found. Nevertheless, let's try the same with static characters, taking the backround evaluation out of the equation. — Still the same.

Mhm, maybe it's related to the reset queue, then? Let's reorder the queues in memory. The same. Let's have the `clearRocket` push it's screen codes to the drawing queue… — Same result. — Let's have another call to the `drawRocket` routine, instead. You guess it, still the same result.

At this point, I'm beginning to mistrust our trusty, versatile pointers (PT1 & PT2) and the indirect jump instruction (`jmp (PT1)`). So I'm coming up with a classic dispatch scheme with self-modification, fixing up the jump address instead of setting up a pointer (here the drawing routine, of course I'm adressing the problematic `clearRocket` routine first):

```                lda rocketDir         ;dispatch on value in rocketDir (0..7)
rol                   ;×2
tay                   ;AC -> Y
lda .drJumpTable+1,y
sta .drJmp+2
lda .drJumpTable,y
sta .drJmp+1
.drJmp          jmp \$ffff             ;dummy address (fixed up)
.dr0
;...code...
.dr1
;...code...
;...
.dr7
;...code...

.drJumpTable
!word .dr0
!word .dr1
!word .dr2
!word .dr3
!word .dr4
!word .dr5
!word .dr6
!word .dr7```

Still the same result.

### Despair :-(

At this point, I'm at the end of my wits. The problem is reproducible, but it doesn't make any sense. It's clearly the dispatch, because, if I'm just going straight forward to case 0, without any jump, it works. On the other hand, if I'm dispatching all codes to case 0, the issue raises its nasty head again. ***?

Moreover, the exact point in time, when the program breaks, depends on the exact method and time of entry into the program. If I'm starting it in any automated manner, it's breaking at the exact same point each time, but specific to the method used to run the program. If I'm running it manually (by «`LOAD "*",8`» ... «`RUN`», thus starting at a random time depending on my typing skills) the time after which the program freezes or breaks seems rather random.

Is it the emulator???

I know that the emulator is not recreating the video hardware down to the last bit and that the V-SYNC is rather triggered by a smart guess than exact method. Is this rather a collision in the emulator, instead of in our program?

The big problem: I've no other option. VICE stopped working for me completely. I had a working Mac Cocoa build once, but this had a few keyboard issues with the PET emulation and I dumped it for a newer version. Sadly, the new version won't start at all (probably, because I'm not using the very latest iteration of the OS; this is still a production machine, meaning, no Jony Ive-UI). But there's still the X-Windows build! Sadly, it starts a PET in 80-columns mode and ROM 4.0 — and it doesn't expose any configurations, settings or options (nor is there a config-file in the file system). Moreover, the keyboard mapping is far from sane (support of localized keyboard layouts is completly out of question, of course), and I can't get the SHIFT-key working for some of the keys. So I may mount a prg-file, but I can't type «`LOAD "*",8`» as I am unable to type an asterisk; or the \$ sign. (Regardless, if using localized or US mappings, regardless of X-Windows settings, which are, apparently, entirely ignored — as Xpet, inspite of the readme-file, isn't using the X-client at all.) No file loading for me. — Bummer!

At this point, I'm really on the verge of giving up, rendering this my first failed project in 20 years. (I'm usally not giving up that easily.) I simply don't have the time for searching for a runnable legacy build of VICE (just to check, if the issue is related to the emulation) and finishing the project. — This may be sad, but it's only true. There's still a world out there…

### Salvation and Still Open Questions

This said, I'm coming back to the program after a while. Let's give ist a last try. Maybe, just per chance, if we get rid of the role-left instruction?

What, if we split the table in a lo-byte and a hi-byte section, just as we've done it for the srceen-lines table?

```displayRocket ;pushes the rocket to the charQueue
ldx rocketX
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
;...code...
.dr1
;...code...
.dr2
;...code...
.dr3
;...code...
.dr4
;...code...
.dr5
;...code...
.dr6
;...code...
.dr7
;...code...

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

Now this actually works. — Flawlessly.   :-)

For unknown reasons.   =%-[]

I still don't know, what the issue really was. However, we've a working program.   :-/

And this is what it looks like in reverse video (at just about 3min run time):

Try it in in-browser emulation.

*****

### Code Listing

And here is our code, in its entirety:

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

; symbols / constants

ticksPerSecond = 60   ;60: time in game corresponds to NTSC timing
resetQueue = \$027A     ;start of cassette buffer, used as a drawing buffer
charQueue = resetQueue+60 ;buffer for screen resets

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

saucerSpeed = 7       ;frames
saucerOffset = 15     ;screen lines y offset

; 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)
frameCounter = \$2A    ;counter for animations
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)
ran = \$31             ;random number (1 byte)
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 (NL,2017)
; 30 SYS 1100

* = \$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, \$3F, \$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, \$20, \$28, \$4E, \$4C, \$2C ; \$0431
!byte \$32, \$30, \$31, \$37, \$29, \$00, \$4A, \$04 ; \$0439
!byte \$1E, \$00, \$9E, \$20, \$31, \$31, \$30, \$30 ; \$0441
!byte \$00, \$00, \$00 ; \$0449 .. \$044B

; main

* = \$044C
; 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
jmp .setupDone
.rom1           lda \$219
sta IRQVector
lda \$21A
sta IRQVector+1
lda #<irqRoutine
sta \$219
lda #>irqRoutine
sta \$21A
.setupDone      cli

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

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

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

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

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

;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 saucerHandler
jsr rocketHandler

.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

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
beq .blank
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*40 + 36
screenAddressScore2 = \$8000 + 10*40 + 36
screenAddressTime1 = \$8000 + 16*40 + 36
screenAddressTime2 = \$8000 + 16*40 + 33

drawScores ;draws scores and time display
ldy score1
sta IPT1
sta IPT1+1
jsr drawDigit
ldy score2
sta IPT1
sta IPT1+1
jsr drawDigit
ldy time1
sta IPT1
sta IPT1+1
jsr drawDigit
ldy time2
sta IPT1
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
sta (IPT1), y
inx
iny
lda digits, x
sta (IPT1), y
dec IPT2
beq .dgDone
inx
dey            ;reset y to zero and increment IPT1 by a screen line
clc
lda IPT1
sta IPT1
bcc .dgRow
inc IPT1+1
jmp .dgRow
.dgDone         rts

revertVideo ;reverts the screen video
eor #\$80
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

; 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
.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
.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
sta charQueue, x
inx
lda #0
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
sta resetQueue, x
inx
lda #0
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

; saucer(s)

saucerHandler
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 #saucerSpeed
sta saucerCnt
jsr clearSaucer
jsr flipSaucer
jsr clearSaucer
jsr flipSaucer

dec saucerLegCnt
bpl .shMoveY
jsr random
and #15
clc
sta saucerLegCnt
lda ran
and #1
sta saucerPhaseDir
ldx #3
lda ran
bpl .shAnimSpeed
ldx #7
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
beq .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
beq .shSaveX
.shMoveX1       dex
bpl .shSaveX
ldx #maxX-1
.shSaveX        stx saucerX

.shDisplay
jsr displaySaucer
jsr flipSaucer
jsr displaySaucer
jsr flipSaucer

.shAnimate      lda frameCounter
bne .shDone
jsr animateSaucer

.shDone         rts

.shStop
lda #0
sta saucerDx
sta saucerDy
jmp .shMoveY

flipSaucer ;flips saucerY by saucerOffset
lda saucerY
clc
cmp #maxY
bcc .fpSave
sec
sbc #maxY
.fpSave         sta saucerY
rts

; rocket

rocketHandler
lda frameCounter
and #31
bne .rhDone
jsr clearRocket
clc
lda rocketDir
and #7
sta rocketDir
jsr displayRocket
.rhDone         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
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

displayRocket ;pushes the rocket to the charQueue
ldx rocketX
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 rocketX
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

; variables

score1          !byte 0
score2          !byte 0
time1           !byte 0
time2           !byte 0

saucerX         !byte 0
saucerY         !byte 0
saucerDx        !byte 0
saucerDy        !byte 0
saucerPhase     !byte 0
saucerPhaseDir  !byte 0
saucerCnt       !byte 0
saucerLegCnt    !byte 0

rocketX         !byte 0
rocketY         !byte 0
rocketDir       !byte 0
rocketDx        !byte 0
rocketDy        !byte 0
rocketThrust    !byte 0

; data

!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

!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 \$80
!byte \$80
!byte \$80
!byte \$80
!byte \$80
!byte \$80
!byte \$80
!byte \$81
!byte \$81
!byte \$81
!byte \$81
!byte \$81
!byte \$81
!byte \$82
!byte \$82
!byte \$82
!byte \$82
!byte \$82
!byte \$82
!byte \$82
!byte \$83
!byte \$83
!byte \$83
!byte \$83
!byte \$83

screenLinesLo
!byte \$00
!byte \$28
!byte \$50
!byte \$78
!byte \$A0
!byte \$C8
!byte \$F0
!byte \$18
!byte \$40
!byte \$68
!byte \$90
!byte \$B8
!byte \$E0
!byte \$08
!byte \$30
!byte \$58
!byte \$80
!byte \$A8
!byte \$D0
!byte \$F8
!byte \$20
!byte \$48
!byte \$70
!byte \$98
!byte \$C0

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

saucerPhases
!byte \$20,\$65,\$54,\$47,\$42,\$5D,\$48,\$59,\$67,\$20

```

(Assembles to 1,736 bytes of binary code.)

— Stay tuned! —

Next:  Episode 8: Space Commander

Previous:  Episode 6: Attractive Saucers

Back to the index.

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