Multiprogram Demo

Developer(s): Fred Nachbaur
Date: 1983
Type: Program
Platform(s): TS 1000
Tags: Game

Multiprogram Demo is a collection of demo versions of programs from the “Fun and Games” tape, with the main interactive piece being SYNCWARS, a keyboard-reflex game in which the player must destroy alien SYNCLOIDS and their hidden SYNCLON commanders by pressing the key corresponding to each enemy’s screen position. The game uses USR calls to machine code routines at addresses 16514, 16567, and 16608 for display and timing effects, and employs a position-calculation algorithm at line 2950 that maps random values to screen coordinates using division and modulo arithmetic. Enemy sprites are drawn using block graphics characters arranged in multi-line PRINT AT sequences, giving the illusion of structured alien figures. Numeric variables N0–N5 and O0 are pre-initialized via VAL literals as a memory-saving technique common in constrained BASIC programming. A SAVE command at line 1330 and CLEAR/RUN sequences suggest the tape contains a chained multi-program loader structure.


Program Analysis

Program Structure

The listing is organized into several distinct functional regions, reflecting a multi-program loader design:

  1. Lines 1–8: REM headers (containing embedded data) and a GOTO VAL "2000" entry point.
  2. Lines 10–30: A subroutine that POKEs values into address 16609 and calls USR 16608 twice, likely a display or border effect routine.
  3. Lines 1325–1335: A CLEAR/SAVE/RUN block for chaining to the next program on tape (SYNCWARS-4K).
  4. Lines 1510–1570: A subroutine (called from line 2880 when score equals 10) that cycles through screen rows, POKEing and USR-calling address 16514, and prints a “TOP SCORE” banner.
  5. Lines 2001–2890: Main game initialization and game loop for SYNCWARS.
  6. Lines 2910–2968: Sprite-drawing and position-calculation subroutines.
  7. Lines 3000–3020: A short delay loop subroutine.
  8. Lines 9300–9350: End-of-game display subroutine.
  9. Lines 9995–9998: A secondary loader block using RAND USR 20480 to jump to machine code at address 20480.

Machine Code Usage

The program makes extensive use of USR calls to pre-loaded machine code routines. The results are assigned to the dummy variable M since BASIC requires a value to be returned from USR:

AddressUsageContext
16514USR 16514Called in the top-score scroll (line 1540) and end-of-game sequence (line 9320)
16567USR VAL "16567"Called after POKEing a character code to 16568, used for character output (lines 2070, 2680)
16608USR 16608Called after POKEing to 16609, likely a border/display effect (lines 15, 25)
20480RAND USR 20480Loader entry point at line 9996

The POKE/USR pattern at lines 2050–2070 is particularly notable: a character code is POKEd to address 16568, then USR 16567 is called — this is a classic technique for driving a machine code character-output routine from BASIC, iterating over the characters of A$ to initialize some display or keyboard mapping table.

Numeric Variable Optimization

Lines 2001–2007 pre-initialize a set of short-name variables using VAL "n" literals:

  • N0 = 0, N1 = 1, N2 = 2, N3 = 3, N5 = 5
  • O0 = 10

Using single- or two-character variable names with VAL-encoded literals reduces the per-token storage cost of numeric literals throughout the program. The VAL "number" idiom also prevents the interpreter from storing a floating-point five-byte value inline in the tokenized line, saving memory especially when a constant is used many times.

Game Mechanics and Loop

The core game loop runs from line 2625 to 2870, iterating C from 1 to 10 (one round per enemy). Each iteration:

  1. Calls GOSUB 2950 to randomly position the SYNCLOID (D,E,G) and SYNCLON (H,I,K) on screen, ensuring they don’t share the same slot.
  2. Draws the SYNCLOID sprite (visible) via USR 16567 and GOSUB 2910.
  3. Waits for a keypress in an inner loop; the available time decreases as C increases (O0-C/N3).
  4. If the player hits the SYNCLOID’s key (A$(H+N1)), transitions to phase 2 (reveal/destroy the SYNCLON via GOSUB 2920).
  5. If the player hits the SYNCLON’s key directly (A$(D+N1)), the shot is still counted valid (line 2707 falls through to the Synclon phase).
  6. A miss displays a “>[*]<” marker and gives a brief second-chance window (lines 2850–2858).
  7. Too-slow response triggers the “TOO SLOW” message at line 2820.

Position Calculation Subroutine

Lines 2950–2969 convert a random integer in range 0–37 to row/column screen coordinates:

  • D = INT(RND*38) — random slot index
  • E = INT(D/10) — row zone (0–3)
  • G = (D - 10*E)*3 + E — column offset with spacing
  • E = E*6 - 1 — final row coordinate

The same formula is applied independently for H/I/K (SYNCLON), with a collision check at line 2962 re-rolling if H=D. This maps 38 discrete slots onto a grid spread across the display.

Sprite Drawing

All sprites are drawn with multi-line PRINT AT row,col sequences using TAB to re-position within the same PRINT statement, building up a 5-row block-graphic figure. For example, the SYNCLOID sprite (line 2910) prints five rows of three characters each using TAB G to align each row. The destruction animation (line 2940) replaces the sprite with asterisk/block-graphic “explosion” characters.

Keyboard Mapping

The string A$ at line 2085 encodes the full keyboard layout in a single string: digits, then QWERTY... rows including CHR$ 118 (ENTER/NEWLINE) and ending with ZXCVBNM.. Enemy positions are mapped to indices into this string, so pressing the key at the corresponding position in A$ identifies a hit.

Scoring and Chaining

Score F is incremented for each successful destroy. At the end of 10 rounds (line 2875), the end-of-game screen is displayed. If F=10 (perfect score), the top-score subroutine at line 1500 is called. After the end screen, the game loops back to line 2560 (the instructions/start prompt). Line 1330 provides a tape-chaining save point, saving the next program as SYNCWARS-4K and immediately running it, suggesting this demo is part of a larger chained tape sequence.

Notable Anomalies

  • Line 2880 calls GOSUB 1500 but the subroutine begins at line 1510; there is no line 1500, so execution falls through to 1510 — this is a deliberate technique, not a bug.
  • Line 2890 jumps to VAL "2560" but line 2560 does not exist in the listing; line 2570 is the next defined line. Execution would therefore continue from line 2570 (the “PUSH ANY KEY” prompt), which is the intended behavior for restarting the game.
  • The delay loop at lines 3000–3020 iterates only O0 (10) times, making it extremely short — effectively just a brief synchronization pause rather than a perceptible delay.
  • Line 9998 (PRINT PEEK 16412+256*PEEK 16413) reads the system variable PROG and prints the start address of the BASIC program — likely a debugging remnant.

Content

Appears On

Related Products

Tutorial on a technique for storing multiple programs in 16K or more RAM at the same time. No machine language...

Related Articles

Related Content

Image Gallery

Source Code

   1 REM E£RND▞;# RETURN#4▖▌COS /▀LEN █#7/ LET 5  T GOSUB #0RND GOSUB PI##TAN 
   2 REM SYNCWARS
   3 REM Y[*]▞*:4NOT $TAB [X]RND▌TAB [V]RNDTAN 
   4 REM BY F.NACHBAUR
   5 REM Y2 GOSUB #TAN 
   8 GOTO VAL "2000"
  10 POKE 16609,14
  15 LET M=USR 16608
  20 POKE VAL "16609",30
  25 LET M=USR 16608
  30 RETURN
 1325 CLEAR
 1330 SAVE "SYNCWARS-4[K]"
 1335 RUN 
 1510 FOR C=N1 TO N2
 1520 FOR D=N1 TO VAL "24"
 1530 POKE VAL "16518",D
 1540 LET M=USR VAL "16514"
 1545 IF D=VAL "18" THEN PRINT AT D,VAL "8";"***TOP SCORE***"
 1547 IF D=VAL "17" OR D=VAL "19" THEN PRINT AT D,VAL "8";"==============="
 1550 NEXT D
 1560 NEXT C
 1570 RETURN
 2001 LET N0=VAL "0"
 2002 LET N1=VAL "1"
 2003 LET N2=VAL "2"
 2004 LET N3=VAL "3"
 2005 LET N5=VAL "5"
 2007 LET O0=VAL "10"
 2020 LET A$="[*][.][▒]*."
 2030 POKE VAL "16418",N0
 2040 FOR C=N1 TO LEN A$
 2050 POKE VAL "16568",CODE A$(C)
 2060 PRINT AT N0,N0;
 2070 LET M=USR VAL "16567"
 2080 NEXT C
 2085 LET A$="1234567890QWERTYUIOPASDFGHJKL"+CHR$ VAL "118"+"ZXCVBNM."
 2090 LET E=N3
 2092 LET G=VAL "28"
 2094 LET I=VAL "18"
 2095 LET K=VAL "16"
 2100 GOSUB VAL "2910"
 2110 GOSUB VAL "3000"
 2130 GOSUB O0
 2190 GOSUB VAL "2920"
 2200 GOSUB VAL "3000"
 2220 GOSUB O0
 2230 GOSUB VAL "2940"
 2410 GOSUB VAL "3000"
 2430 POKE VAL "16518",VAL "25"
 2440 GOSUB VAL "9310"
 2450 PRINT AT N2,VAL "11";"SYNCWARS"
 2470 GOSUB VAL "3000"
 2475 GOSUB VAL "3000"
 2490 PRINT AT N0,N3;"YOUR PLANET IS BEING INVADED BY 10 DREAD SYNCLONS AND THEIR  ""INVISIBILITY FIELD"" ROBOTS, THESYNCLOIDS."
 2501 LET G=O0
 2503 LET I=N3
 2504 GOSUB VAL "2910"
 2505 GOSUB VAL "2930"
 2510 PRINT TAB N5;"SYNCLOID   SYNCLON",,,
 2530 PRINT "   AT FIRST ONLY THE SYNCLOID   ROBOT APPEARS; YOU MUST DESTROY IT TO MAKE ENEMY SYNCLON VISIBLE(BY PRESSING KEY CORRESPONDING  TO ITS POSITION), AND  THENVAP- ORIZE THE SYNCLON TO SCORE. IF  YOU MISS THE ROBOT, BUT HIT THE (HIDDEN) SYNCLON, YOUR SHOT IS  STILL VALID."
 2570 PRINT AT VAL "22",N0;"PUSH ANY KEY"
 2580 PRINT "  TO START."
 2590 IF INKEY$ <>"" THEN GOTO VAL "2620"
 2610 GOTO VAL "2590"
 2620 LET F=N0
 2625 FOR C=N1 TO O0
 2630 CLS
 2640 FOR A=N1 TO RND*VAL "40"
 2650 NEXT A
 2670 GOSUB 2950
 2680 LET M=USR 16567
 2690 GOSUB 2910
 2700 FOR A=N1 TO O0-C/N3
 2701 LET D$=INKEY$ 
 2702 IF D$="" THEN GOTO 2708
 2703 GOSUB O0
 2704 IF D$=A$(H+N1) THEN GOTO 2750
 2705 IF D$<>A$(D+N1) THEN GOTO 2850
 2707 GOTO 2710
 2708 NEXT A
 2709 GOTO 2810
 2710 GOSUB 2920
 2720 FOR A=N1 TO O0-C/N3
 2721 LET D$=INKEY$ 
 2722 IF D$="" THEN GOTO 2728
 2723 GOSUB O0
 2724 IF D$<>A$(H+N1) THEN GOTO 2850
 2727 GOTO 2750
 2728 NEXT A
 2730 GOTO 2810
 2750 GOSUB 2940
 2760 LET F=F+N1
 2770 PRINT AT VAL "23",N0;"SCORE=";F
 2780 GOSUB VAL "3000"
 2790 GOTO 2870
 2810 CLS
 2820 PRINT AT O0,O0;"█[T][O][O]█[S][L][O][W]█"
 2830 GOSUB VAL "3000"
 2840 GOTO VAL "2870"
 2850 PRINT AT I+N1,K;">[*]<"
 2851 FOR A=N1 TO N5
 2852 LET D$=INKEY$ 
 2853 IF D$="" THEN GOTO 2858
 2854 GOSUB O0
 2855 IF D$=A$(H+N1) THEN GOTO 2750
 2857 GOTO 2870
 2858 NEXT A
 2870 NEXT C
 2875 GOSUB VAL "9300"
 2876 GOSUB VAL "3000"
 2880 IF F=O0 THEN GOSUB 1500
 2890 GOTO VAL "2560"
 2910 PRINT AT E+N1,G;"> <";TAB G;"▛█▜";TAB G;"█▀█";TAB G;"▟█▙";TAB G;"▛ ▜"
 2915 RETURN
 2920 GOSUB 2936
 2922 CLS
 2925 GOSUB 2936
 2930 PRINT AT I+N1,K;"> <";TAB K;"▛█▜";TAB K;"█[~~]█";TAB K;"▙▄▟";TAB K;"▟▀▙"
 2935 RETURN
 2936 PRINT AT E+N1,G;" / ";TAB G;"< <";TAB G;"▞▛*";TAB G;"▟█▙";TAB G;"▛ ▜"
 2937 RETURN
 2940 PRINT AT I+1,K;"[G][O][T]";TAB K;"▗*▖";TAB K;"***";TAB K;"▝*▘";TAB K;"[M][*][E]"
 2945 RETURN
 2950 LET D=INT (RND*VAL "38")
 2952 LET E=INT (D/O0)
 2955 LET G=(D-O0*E)*N3+E
 2957 LET E=E*VAL "6"-N1
 2960 LET H=INT (RND*VAL "38")
 2962 IF H=D THEN GOTO VAL "2960"
 2965 LET I=INT (H/O0)
 2967 LET K=(H-O0*I)*N3+I
 2968 LET I=I*VAL "6"-N1
 2969 RETURN
 3000 FOR A=N1 TO O0
 3010 NEXT A
 3020 RETURN
 9300 PRINT AT VAL "12",VAL "8";" █[E][N][D]█[O][F]█[G][A][M][E][.]█ ",,,"FINAL SCORE=";F;" OUT OF 10"
 9310 FOR A=N1 TO VAL "6"
 9320 LET M=USR 16514
 9330 NEXT A
 9350 RETURN
 9995 CLEAR
 9996 RAND USR 20480
 9997 RUN 
 9998 PRINT PEEK 16412+256*PEEK 16413

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

Scroll to Top