This program is a train-dispatching arcade game in which the player watches trains scroll across a horizontally striped screen and must press keys to stop or let them pass before a collision occurs.
Stack up as many trains as you can on each track. You have no control over which track the next train comes, so you need to stack them as far to the right and close together as possible.
Tracks are sometimes blocked by other broken-down trains or weird aliens.
Press any key to stop the train, G to make it run again or go faster if it’s underway. R launches another train.
The screen is set up with alternating “track” lines using a FOR/NEXT loop at lines 1000–1015, and trains are represented by a string of custom graphics characters stored in `A$` at line 8920.
Train movement is animated by printing successive substrings of `A$` in columns 0 through 31, with collision detection performed via `CODE SCREEN$` to check whether a cell is blank (code 0) before advancing.
The program uses the TS2068 `SOUND` keyword extensively to play tones during train movement and crashes.
Program Analysis
Program Structure
The program is organized into several functional regions:
- Initialization (lines 1–5): Sets up the caps-lock POKE, reseeds the random number generator, zeros the score, and jumps to the screen setup.
- Screen setup (lines 1000–8915): Draws 11 horizontal “track” rows by printing underscores every other row, then displays the control legend.
- Train launch (lines 8920–9052): Picks a random row and starting column offset, builds the train graphic string, and animates the train entering from the left edge using substring slicing.
- Main scroll loop (lines 9590–9661): Scrolls the full train across columns 0–31 and then scrolls it off the right edge.
- Sound subroutines (lines 20–49): Two subroutine entry points — line 20 plays a normal moving-train sound and line 30 plays a crash/alarm sound with a key-wait loop.
- Crash handler (lines 60–100): Prints an explosion graphic, sounds a crash, displays the final score, and stops.
- Save/Load stub (lines 9950–9970): Saves the BASIC program and a machine code block, then reloads and runs.
Screen Layout and Track Drawing
Lines 1000–1015 use a FOR R=0 TO 21 STEP 2 loop to print 32-character underscore strings on even rows (0, 2, 4 … 20), leaving odd rows blank. This creates 11 “tracks” across the 22-row display. The variable R is reused later as the chosen track row for each train, so no separate track-index variable is needed.
Train Graphic String
Line 8920 defines A$="_\g\h\e\f\c\d\a\b", a 9-character string combining an underscore with eight UDG characters (\a–\h, i.e., UDGs A–H at chars 144–151). These UDGs must be pre-loaded by the machine code block “CHOO” (address 65368, 168 bytes). The locomotive silhouette is assembled by printing substrings of A$ in different column positions to simulate horizontal scrolling.
Entry Animation (Left Edge)
Before the main scroll loop, lines 9000–9052 handle the case where the train enters from the left side, one character at a time. Each step prints a progressively longer trailing substring of A$ at column 0:
- Line 9000:
A$(9)— single character (engine front) - Line 9010:
A$(8 TO )— two characters - … and so on down to line 9050:
A$(2 TO )— eight characters - Line 9590 begins the full 9-character scroll
After each partial print, a GO SUB 20 call produces a sound tick and checks for a keypress. Between each step, CODE SCREEN$ (R, col) checks whether the cell ahead is blank; a non-blank cell (another train or track character) causes an immediate branch to the crash routine at line 60.
Collision Detection via SCREEN$
Collision is detected entirely through the display file. CODE SCREEN$ (R, col) returns 0 for a space character. Any non-zero value indicates an obstacle. The checks at lines 9015, 9025, 9032, 9037, 9042, 9047, and 9052 inspect successive columns ahead of the train’s leading edge. Inside the scroll loop (line 9595), the same test is applied at I+8 (eight cells ahead of the train’s current column). This approach requires no separate collision array, but it means the train can collide with its own track underscores if misaligned — a potential source of false positives.
Exit Animation (Right Edge)
Lines 9620–9661 mirror the entry animation in reverse. As the train moves off the right side, progressively shorter leading substrings of A$ are printed starting at columns 24 through 31:
- Line 9620:
A$( TO 8)at column 24 - Line 9630:
A$( TO 7)at column 25 - … down to line 9660:
"_"(just the trailing underscore) at column 31
After exit, line 9900 returns to 8930 to launch a new train on a fresh random row and offset, incrementing the score counter at line 8927.
Sound Design
The program uses SOUND statements with multiple channel parameters. Line 20 configures a continuous tone (channels 6–13) for normal train movement; line 24 silences channels 8–10 after the pause. Line 30 sets a louder, lower-pitched alarm for collisions, with line 35 pausing for 100 frames. The sound subroutine at line 20 also checks INKEY$: pressing any key other than “G” triggers the crash sequence (line 21 → line 30), while “G” lets the train continue (line 45). “R” at line 47 jumps directly to 8925 to spawn a new train without waiting for the current one to clear.
Player Input Model
The control scheme embedded in the subroutine is:
| Key | Effect |
|---|---|
| G | Let train continue (green light) |
| R | Spawn a new train immediately |
| Any other key | Stop the train (triggers alarm loop) |
The alarm loop at lines 40–49 busy-waits for a key release (INKEY$="") then for a new keypress, reading it into v$ and acting accordingly. This prevents key-repeat triggering multiple actions.
Initial Placement Variants
Line 8940 picks I=INT(RND*100). Lines 8960–8964 handle trains that start partially off-screen to the right:
I<10: Prints a short 6-UDG graphic near2*I+8(a scaled offset), then goes to 9000 for left-side entry animation — this path appears to be for trains that wrap around or start near center.I<25: Prints 2 characters atI+4.I<40: Prints the tail ofA$starting at column 22.I≥40: Falls through directly to 9000 with no initial placement — the train enters purely from the left.
Machine Code Dependency
Lines 9950–9960 save the BASIC program with LINE 9960 auto-run and separately save a 168-byte code block named “CHOO” from address 65368. On reload, line 9960 loads “CHOO” back and line 9970 runs the BASIC. The UDG characters used for the train sprite (\a–\h) depend entirely on this machine code block having been loaded first; without it, the train would display as garbled or default UDG patterns.
Notable Techniques
- Substring scrolling: Using
A$(n TO )andA$( TO n)slices to animate entry and exit without storing separate frame arrays. - Display-file collision:
CODE SCREEN$used as a zero-cost collision map. - Row selection:
INT(RND*11)*2guarantees only even rows (valid track rows) are chosen. - Score display: Line 80 uses
PRINT AT 21,0with numeric variableSembedded directly in the string via semicolons. - POKE 23658,8: Sets the FLAGS2 system variable to enable lowercase input, ensuring key-reading behaves consistently regardless of caps-lock state.
Potential Bugs and Anomalies
- Line 21 uses
AND NOT INKEY$="G". In Sinclair BASIC,ANDhas lower precedence than=butNOTapplies to the equality result (0 or 1), so this correctly means “key pressed AND key is not G.” However, the doubleINKEY$call means the key could change between the two reads, causing a rare race condition. - The crash display at line 71 uses
PRINT AT R-1, I+8; ifR=0, this attempts to print at row -1, which would cause an error on a real machine. - Lines 8960’s branch to 9000 after printing near
2*I+8could print outside the 32-column display whenIis between 7 and 9, since2*9+8=26and the 6-character print would extend to column 31, which is valid, but2*10+8=28with 6 characters would overflow — partially mitigated by theI<10guard.
Content
Source Code
1 POKE 23658,8
2 RANDOMIZE
3 LET S=0
5 GO TO 1000
20 SOUND 6,60;7,7;8,16;9,16;10,16;12,6;13,0
21 IF INKEY$<>"" AND NOT INKEY$="G" THEN GO TO 30
23 PAUSE 10
24 SOUND 8,0;9,0;10,0
25 RETURN
30 SOUND 6,20;7,7;12,50;13,0;8,16;9,16;10,16
35 PAUSE 100
40 IF INKEY$<>"" THEN GO TO 40
41 IF INKEY$="" THEN GO TO 41
42 LET v$=INKEY$
45 IF v$="G" THEN RETURN
47 IF v$="R" THEN GO TO 8925
49 GO TO 40
60 PRINT AT R,I+8;"\t"
70 SOUND 6,6;7,7;8,16;9,16;10,16;12,60;13,8
71 PRINT AT R-1,I+8;"\t";AT R,I+7;"\t\t\t"
72 PAUSE 90
74 SOUND 8,0;9,0;10,0
80 PRINT AT 21,0;" YOU DISPATCHED ";S;" TRAINS ! "
100 STOP
1000 FOR R=0 TO 21 STEP 2
1010 PRINT "________________________________": PRINT
1015 NEXT R
8915 PRINT AT 21,0;"R=NEW TRAIN Any=STOP G=GO"
8920 LET A$="_\g\h\e\f\c\d\a\b"
8925 IF INKEY$<>"" THEN GO TO 8925
8927 LET S=S+1
8930 LET R=INT (RND*11)*2
8940 LET I=INT (RND*100)
8960 IF I<10 THEN PRINT AT R,2*I+8;"\i\j\k\l\m\n": GO TO 9000
8962 IF I<25 THEN PRINT AT R,I+4;"\n\m": GO TO 9000
8964 IF I<40 THEN PRINT AT R,22;A$(2 TO )
9000 PRINT AT R,0;A$(9)
9001 GO SUB 20
9010 PRINT AT R,0;A$(8 TO )
9011 GO SUB 20
9015 IF CODE SCREEN$ (R,2)=0 THEN LET I=-6: GO TO 60
9020 PRINT AT R,0;A$(7 TO )
9021 GO SUB 20
9025 IF CODE SCREEN$ (R,3)=0 THEN LET I=-5: GO TO 60
9030 PRINT AT R,0;A$(6 TO )
9031 GO SUB 20
9032 IF CODE SCREEN$ (R,4)=0 THEN LET I=-4: GO TO 60
9035 PRINT AT R,0;A$(5 TO )
9036 GO SUB 20
9037 IF CODE SCREEN$ (R,5)=0 THEN LET I=-3: GO TO 60
9040 PRINT AT R,0;A$(4 TO )
9041 GO SUB 20
9042 IF CODE SCREEN$ (R,6)=0 THEN LET I=-2: GO TO 60
9045 PRINT AT R,0;A$(3 TO )
9046 GO SUB 20
9047 IF CODE SCREEN$ (R,7)=0 THEN LET I=-1: GO TO 60
9050 PRINT AT R,0;A$(2 TO )
9051 GO SUB 20
9052 IF CODE SCREEN$ (R,8)=0 THEN LET I=0: GO TO 60
9590 FOR I=0 TO 23
9595 IF CODE SCREEN$ (R,I+8)=0 THEN GO TO 60
9600 PRINT AT R,I;A$
9605 GO SUB 20
9610 NEXT I
9620 PRINT AT R,24;A$( TO 8)
9621 GO SUB 20
9630 PRINT AT R,25;A$( TO 7)
9631 GO SUB 20
9635 PRINT AT R,26;A$( TO 6)
9636 GO SUB 20
9640 PRINT AT R,27;A$( TO 5)
9641 GO SUB 20
9645 PRINT AT R,28;A$( TO 4)
9646 GO SUB 20
9650 PRINT AT R,29;A$( TO 3)
9651 GO SUB 20
9655 PRINT AT R,30;A$( TO 2)
9656 GO SUB 20
9660 PRINT AT R,31;"_"
9661 GO SUB 20
9900 GO TO 8930
9950 SAVE "TRAIN" LINE 9960: SAVE "CHOO"CODE 65368,168
9960 LOAD "CHOO"CODE
9970 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

