--- title: "Asteroids" id: 53787 type: "computer_media" slug: "asteroids" url: "http://localhost/computer_media/asteroids/" markdown_url: "http://localhost/computer_media/asteroids.md" published_at: "2024-05-13T18:56:52+00:00" modified_at: "2026-03-30T21:40:59+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "Dodge a swarm of asteroids, deploy a limited energy screen, and battle for the top spot on this pixelated space survival game's Hall of Fame." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 53877 title: "SINCUS Exchange Tape 101 – Entertainment" type: "computer_media" url: "http://localhost/computer_media/sincus-exchange-tape-101-entertainment/" - id: 55430 title: "Timex Sinclair Public Domain Library Tape 2004" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2004/" media_type: "Program" images: - url: "http://localhost/wp-content/uploads/2024/05/asteroids-sincus-101-2.png" - url: "http://localhost/wp-content/uploads/2024/05/asteroids-sincus-101-1.png" media_type_tags: "Arcade, Game" --- This program implements a scrolling asteroid-field dodge game in which the player steers a spacecraft left and right using keys 5 and 8, and can deploy an energy screen (key 0) to destroy nearby asteroids. The spacecraft is rendered using a set of 21 User-Defined Graphics (UDGs “a” through “u”), whose bitmaps are loaded at startup via a DATA/POKE USR routine starting at line 9000. Collision detection is handled entirely through the ATTR function, checking the paper/ink color code 15 (white on white) at the three cells the ship occupies in row 8. The game tracks score as elapsed ticks, maintains a persistent best-ever score in variable e, and includes a two-letter initials entry for a Hall of Fame when the best score is beaten; a computed GO TO expression at line 405 branches to play-again, save, or quit paths. *** ## Program Analysis ### Program Structure The program is divided into four logical phases: 1. **Introduction (line 1):** Displays a flashing title, instructions, and waits for key 8 to start. 2. **Game loop (lines 4–100):** Initializes variables, then repeatedly reads input, moves the ship, checks for collisions via `ATTR`, draws the ship and scrolling asteroids, and loops back to line 50. 3. **Death sequence and scoring (lines 400–460):** Plays a three-frame explosion animation, calculates the score, handles the Hall of Fame entry, and offers play-again/save/quit. 4. **UDG loader subroutine (lines 9000–9270):** RESTOREs to line 9070 and POKEs bitmap data for 21 UDGs (“a”–”u”) via `POKE USR p$+n,a`. ### User-Defined Graphics Twenty-one UDGs are defined, covering a rich set of spacecraft parts, asteroid fragments, and explosion frames. The loader at lines 9020–9060 iterates over each character label in `p$` and writes eight bytes of bitmap data. In the game loop at line 100, `CHR$ (RND*11+144)` selects a random UDG from the range 144–154 (UDGs “a”–”k”) to render asteroids scrolling onto row 21. The spacecraft body and wings are drawn with UDGs `\s`/`\t` (row 7) and `\p`/`\q`/`\r` (row 8), while the explosion uses UDGs `\n`, `\o`, `\u` etc. ### Collision Detection via ATTR Rather than tracking asteroid positions in an array, collision detection exploits the display attribute system. Asteroids are printed in INK 7 on PAPER 1 (blue background), giving an attribute byte of 15. The ship occupies columns `l`, `l+1`, and `l+2` on row 8. Lines 64–66 check: - `ATTR (8,l)=15` or `ATTR (8,l+2)=15` → wing hit, `p=p-1` - `ATTR (8,l+1)=15` → body hit, `p=p-2` This approach is compact but means any cell with attribute 15 registers as a hit, tying game logic directly to the display state. ### Energy Screen Mechanic Pressing key 0 costs one energy unit (`f=f-1`) and prints a three-cell energy graphic in INK 6 at ship row 8 via `PRINT AT 8,l; INK 6;"\p\q\r"`. Because the energy screen is rendered in INK 6 (yellow) rather than INK 7, it overwrites any asteroid attribute at that cell, preventing a collision detection hit on the next iteration. The energy check appears twice — at line 50 (integrated into the movement/scroll path) and at line 62 — to handle both the normal and post-screen-flash code paths. ### Scrolling Asteroid Field Asteroids are produced by printing two random UDG characters at random column positions on row 21 each tick (line 100). The display naturally scrolls upward because the `PRINT` at the end of line 100 causes the screen to scroll when the cursor reaches row 21. This is a classic Sinclair BASIC scroll-by-printing idiom: `AT 21,31;""` positions the cursor at the last cell; the following bare `PRINT` triggers the scroll, pushing all rows up by one. ### Score and Hall of Fame Score is measured as the tick counter `t` minus 13 (a calibration offset for setup ticks). Variable `b` holds the best score for the current session and `e` the best-ever score, both persisting across plays via `GO TO 4` (not `GO TO 1`), which skips the initialization of `b` and `e` at line 1. When `e` is beaten, the player enters two-letter initials: a loop at line 403 reads `INKEY$` twice with `PAUSE 0` as a keypress-wait idiom, appending each letter plus a period separator into `n$`. ### Computed GO TO Line 405 ends with a computed branch: `GO TO (5 AND INKEY$="p")+(430 AND INKEY$="s")+(450 AND INKEY$="f")` Because `PAUSE 0` precedes it, exactly one of `INKEY$` comparisons will be true (evaluating to 1), so the expression yields 5, 430, or 450. A value of 0 (no matching key) would jump to a non-existent line 0, but in practice `PAUSE 0` ensures a key is held when execution reaches the `GO TO`. ### Variable Summary | Variable | Purpose | | --- | --- | | `t` | Tick counter (raw score) | | `p` | Hit points remaining (starts at 5) | | `l` | Current ship column position | | `l1` | Previous ship column (for erasing) | | `f` | Energy screen units remaining (starts at 6) | | `b` | Best score this session | | `e` | Best-ever score across sessions | | `n$` | Player initials for Hall of Fame | | `m`, `n` | Loop counters (UDG loader, animation, etc.) | | `p$` | UDG character label in loader | | `a` | Single bitmap byte in loader | ### Notable Anomalies - `POKE 23692,255` at line 50 resets the scroll counter to suppress the “scroll?” prompt, a standard Sinclair BASIC technique for uninterrupted scrolling programs. - Line 9065 (`BORDER 1: PAPER 1: CLS : INK 7: GO TO 2`) is unreachable dead code — there is no line 2 and nothing branches to 9065. - The energy variable `f` is initialized to 6 at line 10 despite the instructions stating “5 energy units.” With each activation decrementing before the check (`f=f-1: IF f>0`), activations 1–5 succeed and activation 6 fails, so the player effectively gets exactly 5 uses — the off-by-one is intentional. - Lines 9998–9999 (`SAVE` / `VERIFY`) are developer utilities left in the listing and are only reachable if execution falls through from the UDG loader, which never happens during normal play. ## Source Code ``` 1 BORDER 1: PAPER 1: CLS : INK 7: PRINT AT 0,8; FLASH 1;"ASTEROIDS AHEAD!": PAUSE 100: PRINT FLASH 0;''" Steer your spacecraft through the swarm of asteroids"''"Press 5 for Left or 8 for Right."''"Press 0 to activate your craft'sEnergy Screen to destroy asteroids you cannot avoid"''"But BEWARE - the Screen has onlya short range and you have only 5 energy units left"''" Your craft will disintegrate after 5 hits (wings=1,body=2)"''" HOW LONG CAN YOU SURVIVE?": GO SUB 9000: PRINT '" Press 8 to start": PAUSE 0: LET b=0 4 LET e=0 10 INK 0: CLS : LET t=0: LET p=5: LET l=15: LET l1=l: LET f=6 50 POKE 23692,255: LET l=l+(INKEY$="8" AND l<29)-(INKEY$="5" AND l>0): IF INKEY$="0" THEN LET f=f-1: IF f>0 THEN PRINT AT 8,l; INK 6;"\p\q\r": GO TO 64 62 IF INKEY$="0" THEN LET f=f-1: IF f>0 THEN PRINT AT 8,l; INK 6;"\p\q\r" 64 IF ATTR (8,l+2)=15 OR ATTR (8,l)=15 THEN LET p=p-1 66 IF ATTR (8,l+1)=15 THEN LET p=p-2 67 IF p<1 THEN GO TO 400 100 PRINT AT 7,l1;" ";AT 21,31;"": PRINT : PRINT AT 7,l; INK 6;"\s"; INVERSE 1;p; INVERSE 0;"\t": LET t=t+1: LET l1=l: PRINT AT 21,RND*31; INK 7;CHR$ (RND*11+144);AT 21,RND*31;CHR$ (RND*11+144): GO TO 50 400 FOR n=1 TO 3: PRINT AT 6,l1;" ";AT 7,l1; INK 2;" \m ";AT 8,l1;" ": PAUSE 2: PRINT AT 6,l1; INK 6;"\o\q\u";AT 7,l1;"\n \n";AT 8,l1;"\p\q\r": PAUSE 2: NEXT n: FOR n=6 TO 8: PRINT AT n,l1;" ": NEXT n 402 CLS : IF b