Stellar Fighter

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Arcade

Stellar Fighter is a space combat game in which the player pilots a fighter craft across a cockpit-display screen, maneuvering to intercept and destroy waves of alien ships using phasor fire. The game uses User Defined Graphics (UDGs) to render several distinct alien sprite designs, swapping between them by POKEing bitmap data into UDG memory as the score advances. A machine-code-free but UDG-heavy approach drives the visual variety: five separate enemy sprite sets (each three characters wide) are loaded at runtime via RESTORE/READ/POKE loops into UDGs \\a–\\u. Game mechanics track phasor energy (PHE), hull damage (DAM%), alien craft destroyed (ACD), and docking count, with difficulty scaling upward after each completed wave by reducing the enemy fire-rate thresholds.


Program Structure

The program is organized into well-separated functional blocks:

  1. Lines 1–19: Initialization — intro screen, title music (BEEP sequence via DATA at line 12), lift-off animation, and UDG setup via GO SUB 8000.
  2. Lines 20–26: Cockpit HUD drawing using PLOT/DRAW graphics and PRINT for instrument labels.
  3. Lines 590–655: Main game loop — enemy movement, player input polling, display of enemy sprite.
  4. Lines 700–840: Enemy attack resolution, damage accumulation, explosion animation, and end-condition checks.
  5. Lines 6000–6143: Laser firing routine — draws laser beams with looped PLOT/DRAW, checks hit via ATTR, calls explosion subroutine, increments score.
  6. Lines 7000–7045: Explosion animation subroutine using expanding UDG characters in all eight directions.
  7. Lines 8000–8052: UDG definition routines — five enemy sprite sets loaded from DATA.
  8. Lines 8500–8650: Docking sequence — animated docking bar, energy/damage reset after three docks.
  9. Lines 9300–9430: Player-destroyed sequence with “Heaven” screen, music, and replay prompt.
  10. Lines 9500–9530: Intro radio-message routine, printed character by character with BEEP.
  11. Lines 9600–9640: Mission-complete handler — resets stats, increases difficulty, loops to next wave.
  12. Lines 9990–9998: Abort and SAVE/CLEAR.

UDG Sprite System

Five distinct enemy sprite designs are defined across UDGs \\a–\\u (characters 144–164). Each design occupies four UDG characters arranged as a 1×3 display string plus a three-character wide “damaged” variant. Loading is done by FOR F / FOR G / POKE USR CHR$ (144+f)+g, a loops reading from DATA statements, with RESTORE pointing to the correct DATA block before each load.

SubroutineUDGs usedA$ (normal)C$ (damaged)Trigger score
8000\\a–\\d (144–147)" \\a ""\\b\\c\\d"Start
8015\\f–\\i (149–152)" \\f ""\\g\\h\\i"SC=RA (random 4–11)
8025\\k–\\m (153–155)" ,J, ""\\k\\l\\m"SC=RAN (random 13–25)
8035\\n–\\q (157–160)" \\n ""\\o\\p\\q"SC=GOG (random 27–52)
8045\\r–\\u (161–164)" \\r ""\\s\\t\\u"Docking

Note that the subroutine at line 8035 has a bug: the inner POKE loop reads a without a preceding READ a statement, so it will POKE whatever value a holds from the last assignment rather than the DATA values. The corresponding DATA at lines 8037–8040 is never consumed by this routine, meaning the fourth enemy sprite set will display incorrectly or be identical to the previous UDG state.

A similar bug exists at line 8045: POKE USR CHR$ (161+f)+g,a again lacks READ a, so DATA lines 8047–8051 are also never read by their intended loop.

Main Game Loop Logic

The main loop runs from approximately line 620 to 1000. Enemy position is stored in D (row) and A (column). Lines 620–630 apply random drift to both axes. Lines 641–644 read INKEY$ for the five control keys (5/6/7/8/1) with boundary checks. The counter C is incremented each iteration; it is compared to thresholds FO, FOR, and FORT to determine which sprite variant to display and when the enemy fires back. This creates a timed attack cycle without any explicit timing loop.DA

VariableInitial valueMeaning
FO10Threshold for damaged sprite display
FOR20Threshold for second damaged sprite
FORT30Threshold for enemy fire
RF0Enemy fire-event counter
Random damage per hit (15–44)

After each completed wave of 43 kills (SC=43, line 6136), the thresholds FO, FOR, FORT are each decremented by 5 at line 9605, making enemies attack faster. A floor check at line 9611 prevents them from dropping below 3/2/4 respectively.

Hit Detection

Hit detection uses ATTR (10,16) at line 6130 to check whether the screen cell at the center crosshair position has the enemy’s ink color (6 = yellow). This is a compact and idiomatic attribute-based collision method that avoids any coordinate arithmetic. If the attribute matches, the explosion subroutine at line 7000 is called.

Laser and Explosion Effects

The laser is drawn in a loop (lines 6105–6125) that plots two converging lines from the cockpit sides toward the center, then redraws them in INK 0 to erase. The explosion routine at line 7000 prints the UDG \\e (char 148) at the hit point and then in expanding rings of eight positions using AT 10±P, 15±P patterns, cycling through decreasing INK values for a flash effect. The COX counter repeats the flash twice before returning.

Docking Sequence

When the player presses “1”, line 8500 checks the color of the cockpit bay area using ATTR (20,17). If the bay is showing the correct color (5 = cyan), docking proceeds. After four dock events (DOCK=4), an animated docking bar crawls across row 10 using a CP pointer incremented with a PAUSE delay. Energy and damage are reset at line 8650 via GO SUB 8001 then GO TO 598. Only three docks are permitted per mission (counter checked at line 8503).

Notable BASIC Idioms and Techniques

  • The intro radio message at line 9500 prints each character individually with a BEEP to simulate a teletype/radio effect: FOR B=1 TO LEN I$: BEEP .05,3: PRINT I$(B);: NEXT B.
  • RESTORE 6 at line 11 re-reads the program’s own DATA from line 12 for the title music — though the DATA is actually at line 12, not line 6. This points to a non-existent line, so RESTORE will find the first DATA statement it can reach, which is line 12. This is intentional and works correctly.
  • The “Heaven” end screen at line 9412 draws a pixel-art figure using block-graphic escapes (\\ \\ etc.) combined with OVER 1 printing at line 9412 for overlay effects.
  • Line 8001 is referenced as a subroutine label via GO SUB 8001 (lines 8610, 9640) but is not a subroutine — it contains only LET statements and falls through. This functions as an inline reset of A$ and C$ to the default enemy type, relying on the calling code to subsequently GO TO an appropriate line rather than RETURN.
  • The variable name FOR (line 591) shadows the BASIC keyword FOR. This is valid in Sinclair BASIC since variable names can match keywords when used in an assignment context, but it is unusual and could cause confusion.

Anomalies and Bugs

  • Lines 8035 and 8045 are missing READ a inside their POKE loops (see UDG section above), leaving those two enemy sprite sets non-functional.
  • LET B$ is never defined anywhere in the program, so line 651 (PRINT AT D,A;B$) will cause a variable-not-found error if reached. The FOR threshold comparison logic at line 649 may prevent this in practice if C always exceeds FOR before reaching line 651, but the variable is nonetheless undefined.

Image Gallery

Source Code

    1 REM FROM LIST LIBRARY TAPE V1.7
    5 BORDER 0:PAPER 6:INK 0:CLS :GO SUB 9500:GO TO 7
    7 PAPER 7:INK 1:BORDER 1:CLS :PRINT AT 3,8;"STELLAR FIGHTER":PRINT  AT 4,8;"\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''":REM STOP 
    8 PRINT INK 2; AT 7,8;"COCKPIT CONTROLS"; INK 0; AT 9,11;"5=LEFT"; AT 11,11;"8=RIGHT"; AT 13,11;"6=DIVE"; AT 15,11;"7=CLIMB"
    9 PRINT INK 0:PRINT AT 17,11;"1=DOCK":PRINT AT 19,11;"0=FIRE"
   11 RESTORE 6:FOR N=1 TO 19:READ C,D:BEEP C,D:NEXT N:PAUSE 500
   12 DATA .15,7,.15,7,.15,7,.9,0,.9,7,.15,5,.15,4,.15,2,.9,12,.45,7,.15,5,.15,4,.15,2,.9,12,.45,7,.15,5,.15,4,.15,5,1,2
   13 BORDER 0:PAPER 1:INK 7:CLS 
   14 PRINT AT 5,14;"WE"; AT 7,13;"HAVE"; AT 9,11;"LIFT OFF"
   15 FOR G=1 TO 20:FOR F=0 TO 7:BORDER F:BEEP .02,F:NEXT F:NEXT G
   18 GO SUB 8000
   19 INK 5:PAPER 0:BORDER 0:CLS 
   20 PLOT 0,40:DRAW 70,0:DRAW 20,-20:DRAW 75,0:DRAW 20,20:DRAW 70,0
   21 PLOT 2,38:DRAW 68,0:DRAW 20,-20:DRAW 75,0:DRAW 20,20:DRAW 68,0
   22 PLOT 2,38:DRAW 0,-36:DRAW 251,0:DRAW 0,36:PLOT 90,18:DRAW 0,-16:PLOT 165,18:DRAW 0,-16
   25 PRINT INK 7:PRINT AT 18,23;"ACD":PRINT AT 20,23;"CI":PRINT AT 18,1;"DAM%"
   26 PRINT INK 7:PRINT AT 20,1;"PHE":PRINT AT 20,12;"CODE"
  590 LET DOCK=0:LET GOG= INT (RND*26)+27:LET RAN= INT (RND*13)+13:LET RF=0:LET C=0:LET SC=0:LET RA= INT (RND*8)+4:LET ACD=0
  591 LET FO=10:LET FOR=20:LET FORT=30
  598 LET PH=1000:LET DAM=0
  599 PRINT INK 5:PRINT AT 20,17;"\  \  "
  600 LET DA= INT (RND*30)+15
  610 LET D= INT (RND*14):LET A= INT (RND*27)
  620 LET R1= INT (RND+.5):IF R1=0 AND D>4 THEN LET D=D-1:IF R1=1 AND D<17 THEN LET D=D+1
  630 LET R2= INT (RND+.5):IF R2=0 AND A>2 THEN LET A=A-1:IF R2=1 AND A<16 THEN LET A=A+1
  641 IF INKEY$="8" AND A<17 THEN LET A=A+3
  642 IF INKEY$="7" AND D>3 THEN LET D=D-3
  643 IF INKEY$="6" AND D<13 THEN LET D=D+3
  644 IF INKEY$="5" AND A>2 THEN LET A=A-3
  645 IF INKEY$="1" THEN GO TO 8500
  647 IF INKEY$=" " THEN GO TO 9995
  649 LET C=C+1:IF C>FO THEN GO TO 652:IF C >=FOR THEN GO TO 651
  650 PRINT INK 6; AT D,A;A$:GO TO 654
  651 PRINT AT D,A;B$:GO TO 655
  652 PRINT INK 6; AT D,A;C$
  655 IF INKEY$="0" THEN LET PH=PH-10:PRINT INK 7; AT 20,5;PH:GO TO 6000
  700 FOR B=-20 TO -15:BEEP .005,B:NEXT B
  800 LET RF=RF+1
  801 PRINT INK 6; AT 20,26;C$
  805 PRINT INK 7:PRINT AT 9,16;"!":PRINT AT 11,16;"!":PRINT AT 10,15;"-":PRINT AT 10,17;"-"
  810 IF RF<FORT THEN GO TO 900
  812 FOR X=4 TO 0 STEP -4
  813 PLOT A*8+10,175-D*8:DRAW INK X;40,-36
  814 FOR I=0 TO 10:BEEP .05,3:NEXT I:BEEP .05,10:BEEP .05,12
  815 NEXT X
  816 FOR X=7 TO 0 STEP -1:BEEP .005, INT (RND*10):BORDER X:NEXT X
  819 LET RF=0:LET C=0:LET DAM=DAM
  820 LET DAM=DAM+DA:PRINT INK 7:PRINT AT 18,6;DAM
  825 IF DAM>60 AND DAM<100 THEN GO TO 9300
  830 IF DAM >=99 THEN GO TO 9400
  840 PRINT AT D,A;"    ":GO TO 600
  900 PRINT AT D,A;"    "
 1000 GO TO 620
 5000 REM FIRING LASER
 6000 REM SHOOT
 6001 IF PH<100 AND PH>0 THEN PRINT AT 20,7;" "
 6010 IF PH <=-100 THEN GO TO 9300
 6105 FOR N=3 TO 0 STEP -3
 6110 PLOT INK 0;7,48:DRAW INK N;119,41
 6120 PLOT INK 0;248,48:DRAW INK N;-110,40
 6125 NEXT N
 6127 FOR Q=1 TO 10:BEEP .01,1+F:BEEP .01,10:NEXT Q
 6130 IF ATTR (10,16)=6 THEN GO TO 6134
 6133 GO TO 656
 6134 GO SUB 7000
 6135 LET ACD=ACD+1:LET SC=SC+1:LET RF=0:PRINT INK 7:PRINT AT 18,27;ACD
 6136 IF SC=43 THEN GO TO 9600
 6139 LET C=0:IF SC=RA THEN GO SUB 8015
 6140 IF SC=RAN THEN GO SUB 8025
 6141 IF SC=GOG THEN GO SUB 8035
 6143 GO TO 600
 7000 LET Z$="\e"
 7010 FOR N=6 TO 0 STEP -6
 7011 LET COX=0
 7015 PRINT INK N; BRIGHT 1; AT 10,15;Z$
 7020 FOR P=1 TO 4
 7030 PRINT INK N; AT 10+P,15+P;Z$; AT 10-P,15-P;Z$; AT 10+P,15-P;Z$; AT 10-P,15+P;Z$; AT 10,15+P;Z$; AT 10,15-P;Z$; AT 10+P,15;Z$; AT 10-P,15;Z$
 7035 BEEP .0025, INT (RND*20)
 7040 NEXT P:NEXT N
 7041 LET COX=COX+1
 7042 IF COX=2 THEN GO TO 7045
 7043 FOR N=3 TO 0 STEP -3
 7044 GO TO 7015
 7045 BRIGHT 0:RETURN 
 8000 FOR F=0 TO 4:FOR G=0 TO 7:READ A:POKE USR CHR$ (144+f)+g,a:NEXT G:NEXT F
 8001 LET A$= " \a ":LET C$="\b\c\d"
 8005 DATA 0,0,36,195,255,219,0,0
 8006 DATA 96,241,250,252,255,255,241,96
 8007 DATA 129,24,60,126,255,255,255,90
 8008 DATA 6,143,95,63,255,255,143,6
 8009 DATA BIN 01000000,0, BIN 01100010, BIN 00100001,0, BIN 00011000,1, BIN 10000101
 8010 RETURN 
 8015 RESTORE 8020:FOR F=0 TO 3:FOR G=0 TO 7:READ a:POKE USR CHR$ (149+f)+g,a:NEXT G:NEXT F
 8016 LET A$=" \f ":LET C$="\g\h\i"
 8020 DATA 0,129,66,60,60,66,129,0
 8021 DATA 192,48,12,3,3,12,48,192
 8022 DATA 0,126,195,231,255,126,0,0
 8023 DATA 3,12,48,192,192,48,12,3
 8024 RETURN 
 8025 RESTORE 8027:FOR F=0 TO 3:FOR G=0 TO 7:READ A:POKE USR CHR$ (153+f)+g,a:NEXT G:NEXT F
 8026 LET A$=" ,J, ":LET C$="\k\l\m"
 8027 DATA 0,0,126,219,255,126,0,0
 8028 DATA 3,15,62,252,63,15,3,0
 8029 DATA 255,255,126,60,255,255,255,165
 8030 DATA 192,240,124,63,252,240,192,0
 8031 RETURN 
 8035 RESTORE 8037:FOR F=0 TO 3:FOR G=0 TO 7:POKE USR CHR$ (157+f)+g,a:NEXT G:NEXT F
 8036 LET A$=" \n ":LET C$="\o\p\q"
 8037 DATA 0,66,153,255,153,66,0
 8038 DATA 96,64,193,255,255,193,64,96
 8039 DATA 0,255,231,219,219,231,255,0
 8040 DATA 6,2,131,255,255,131,2,6
 8041 RETURN 
 8045 RESTORE 8047:FOR F=0 TO 3:FOR G=0 TO 7:POKE USR CHR$ (161+f)+g,a:NEXT G:NEXT F
 8046 LET A$=" \r ":LET C$="\s\t\u"
 8047 DATA 0,0,219,255,255,219,0,0
 8048 DATA 0,112,250,142,255,254,250,112
 8050 DATA 0,24,60,126,195,126,60,24
 8051 DATA 0,14,95,113,255,127,95,14
 8052 RETURN 
 8500 IF ATTR (20,17)=5 THEN GO TO 610:GO TO 8501
 8501 GO SUB 8045:GO TO 8502
 8502 LET DOCK=DOCK+1
 8503 IF DOCK <>4 THEN GO SUB 8035:GO TO 610
 8506 PRINT INK 6; FLASH 1; AT 20,12;"DOCKING"
 8507 LET CP=1
 8508 PRINT BRIGHT 1:INK 4:PRINT AT 10,CP;C$
 8509 LET CP=CP+1
 8510 IF CP=16 THEN GO TO 8590
 8511 PAUSE 25
 8512 PRINT AT 10,CP-1;"  "
 8513 GO TO 8508
 8591 FOR N=7 TO 0 STEP -7
 8592 PLOT INK 0;248,48:DRAW INK N;119,40
 8594 BEEP 1,-10:BEEP 1,10:BEEP 1,-10
 8595 NEXT N
 8599 PRINT AT 10,14;"(       )"
 8600 PRINT INK 5; AT 20,17;"\  \  "; INK 7; AT 20,12;"CODE "
 8605 PRINT AT 18,6;"  "
 8606 PRINT AT 20,5;"   "
 8610 GO SUB 8001
 8650 GO TO 598
 9300 PRINT INK 2:PRINT AT 20,17;"\  \  "
 9350 GO TO 700
 9400 PRINT  AT D,A;"   ":BORDER 0
 9401 FOR G=1 TO 3:FOR F=0 TO 7:BORDER F:BEEP .05,F+G-1:NEXT F:NEXT G
 9405 BORDER 0:PAPER 7:INK 1:CLS 
 9410 PRINT AT 7,10;"S O  T H I S"; AT 9,10;"M U S T  B E"; AT 11,10;"H E A V E N"
 9411 PRINT TAB 10;"\''\''\''\''\''\''\''\''\''\''\''"
 9412 PRINT INK 0; AT 15,15;"\  \  "; AT 16,15;"\  \  "; AT 17,13;"\  \  \  \  \  \  "; AT 18,15;"\  \  "; AT 19,15;"\  \  "; AT 20,15;"\  \  "; AT 21,14;"\.'\  \  \'.":PRINT OVER 1; AT 21,14;\{20}\{0}"\.:  \:."
 9420 RESTORE 9425:FOR N=0 TO 35:READ C,D:BEEP C,D:NEXT N
 9425 DATA 1,0,1,2,.5,3,.5,2,1,0,1,0,1,2,.5,3,.5,2,1,0,1,3,1,5,2,7,1,3,1,5,2,7,.75,7,.25,8,.5,7,.5,5,.5,3,.5,2,1,0,.75,7,.25,8,.5,7,.5,5,.5,3,.5,2,1,0,1,0,1,-5,2,0,1,0,1,-5,2,0
 9426 CLS :PRINT "O.K.TRAINEE DO YOU WANT ANOTHER TRY AT STAYING ALIVE???         PUSH  'N'  TO STOP  PROGRAM OR  'Y'  TO RUN AGAIN"
 9427 PAUSE 0:IF INKEY$="N" THEN GO TO 9998
 9428 PRINT FLASH 1:PRINT AT 16,11;"BRAVE LAD"
 9429 FOR F=1 TO 30:LET RS= INT (RND*50):BEEP .05,RS:NEXT F
 9430 PAUSE 100:CLS :RUN 
 9500 LET I$="THIS IS BASE CALLING FIGHTER....                                YOUR MISSION IS TO ESCORT A     CONVOY OF CRAFT ACROSS THE      GALAXY. IT IS UNLIKELY YOU WILL SURVIVE THOUGH. YOU MAY DOCK    FOR REPAIRS ONLY 3 TIMES.   "
 9505 LET I$=I$+"                                           READ -OUTS                    *DAM%=DAMAGE                    *PHE =PHASOR ENERGY             *ACD =ALIEN CRAFT DESTROYED     *CI  =CRAFT IDENTITY        THIS IS BASE----OVER AND OUT"
 9510 PRINT "         RADIO MESSAGE          "
 9511 PRINT "         ++++++++++++++          "
 9520 FOR B=1 TO LEN I$:BEEP .005,3:PRINT I$(B);:NEXT B
 9530 PAUSE 0:RETURN 
 9600 PRINT AT D,A;"    "
 9601 PRINT FLASH 1:PRINT AT 0,0;"       MISSION COMPLETE        ":PAUSE 300
 9605 LET SC=0:LET DAM=0:LET PH=1000:LET DOCK=0:LET FO=FO-5:LET FOR=FOR-5:LET FORT=FORT-5
 9610 PAUSE 100:PRINT FLASH 0; AT 0,0;"                               "
 9611 IF FOR<2 THEN LET FOR=2:IF FO<3 THEN LET FO=3:IF FORT<4 THEN LET FORT=4
 9615 PRINT AT 0,0;"    RETURN HOME TO FLY AGAIN"
 9616 PAUSE 200:PRINT AT 0,0;"(32*SP)"
 9620 PRINT AT 18,6;"   ":PRINT AT 20,5;"    "
 9630 FOR F=1 TO 30:BEEP .05,20:BEEP .05,0:NEXT F
 9640 GO SUB 8001:GO TO 598
 9990 PRINT FLASH 1:PRINT AT 0,0;"        MISSION ABORTED        ":PAUSE 50
 9995 PAUSE 50:RUN 
 9997 CLEAR :SAVE "STELRF.B1" LINE 1
 9998 CLEAR 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.