Train

This file is part of and CATS Library Tape 1. Download the collection to get this file.
Date: 1984
Type: Program
Platform(s): TS 2068
Tags: Game

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:

  1. Initialization (lines 1–5): Sets up the caps-lock POKE, reseeds the random number generator, zeros the score, and jumps to the screen setup.
  2. Screen setup (lines 1000–8915): Draws 11 horizontal “track” rows by printing underscores every other row, then displays the control legend.
  3. 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.
  4. Main scroll loop (lines 9590–9661): Scrolls the full train across columns 0–31 and then scrolls it off the right edge.
  5. 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.
  6. Crash handler (lines 60–100): Prints an explosion graphic, sounds a crash, displays the final score, and stops.
  7. 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:

KeyEffect
GLet train continue (green light)
RSpawn a new train immediately
Any other keyStop 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 near 2*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 at I+4.
  • I<40: Prints the tail of A$ 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 ) and A$( 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)*2 guarantees only even rows (valid track rows) are chosen.
  • Score display: Line 80 uses PRINT AT 21,0 with numeric variable S embedded 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, AND has lower precedence than = but NOT applies to the equality result (0 or 1), so this correctly means “key pressed AND key is not G.” However, the double INKEY$ 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; if R=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+8 could print outside the 32-column display when I is between 7 and 9, since 2*9+8=26 and the 6-character print would extend to column 31, which is valid, but 2*10+8=28 with 6 characters would overflow — partially mitigated by the I<10 guard.

Content

Appears On

Capital Area Timex Sinclair User Group’s Library Tape.

Related Products

Related Articles

Related Content

Image Gallery

Train

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.

People

No people associated with this content.

Scroll to Top