--- title: "TRAINS" id: 70943 type: "computer_media" slug: "trains" url: "http://localhost/computer_media/trains/" markdown_url: "http://localhost/computer_media/trains.md" published_at: "2026-08-24T12:09:09+00:00" modified_at: "2026-08-24T12:10:34+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/trains.png" alt: "TRAINS screen" excerpt: "An animated train rolls left or right across your screen using 18 custom characters, with user-selectable speed and direction — built entirely in BASIC." 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/" indiv: - name: "Stan Livingston" slug: "stan-livingston" taxonomy: "indiv" url: "http://localhost/indiv/stan-livingston/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" media_type: "Program" programmers: - name: "Stan Livingston" slug: "stan-livingston" taxonomy: "indiv" url: "http://localhost/indiv/stan-livingston/" download_url: "https://archive.org/download/timex-sinclair-software-archive/TRAINS%20%281986%29%28Livingston%2C%20Stan%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "TRAINS (1986)(Livingston, Stan)(TS2068)(US)(Program).tap" mediadate: "1986" images: - url: "http://localhost/wp-content/uploads/2026/08/trains.png" alt: "TRAINS screen" media_type_tags: "Demo" --- # TRAINS TRAINS displays an animated train moving across the screen in either left or right direction, using 18 user-defined graphics characters (UDGs A through R) to compose the locomotive and carriages. The UDG bitmap data is loaded via a GOSUB routine that POKEs 144 bytes into the UDG area starting at USR “A”. The train animation is achieved by rotating a 32-character string one character at a time each loop iteration, simulating smooth scrolling. Speed is controlled by a user-supplied PAUSE value, and pressing ENTER at any point resets the display and prompts for new settings. The DATA statements use the variable `y` (which is uninitialized and therefore 0 in Sinclair BASIC) as a shorthand for zero-value bytes in the UDG definitions. *** ### Program Structure The program is organized into three logical sections: initialization and UDG loading (lines 20–130), left-direction animation loop (lines 160–280), and right-direction animation loop (lines 290–420), plus the UDG-loading subroutine at lines 430–640. 1. **Lines 20–130:** Draw a baseline, load UDGs via GOSUB, dimension and initialize the string variables, and prompt for speed (`n`) and direction (`D$`). 2. **Lines 160–280:** Left-moving animation — rotates `a$` (train body) and `c$` (smoke/cab top) leftward one character per iteration. 3. **Lines 290–420:** Right-moving animation — rotates `a$` rightward and prepends from `f$` (smoke for rightward travel). 4. **Lines 430–640:** GOSUB subroutine that READs 144 bytes and POKEs them into the UDG area starting at `USR "A"`, defining 18 custom characters (A through R). ### UDG Definition Technique The subroutine at line 430 uses a `FOR` loop iterating `x` from 0 to 143, READing one byte at a time and POKEing it to `USR "A"+x`. This defines 18 UDGs (18 × 8 = 144 bytes) covering characters A through R, which together form the locomotive cab, body, wheels, and carriages when assembled into the display string `a$`. ### The “y” Variable as Zero Placeholder Throughout the DATA statements (lines 470–630), the identifier `y` appears frequently. In Sinclair BASIC, an uninitialized numeric variable defaults to 0, so `y` is used as a compact stand-in for the value 0 within the DATA list. This avoids typing repeated zeros and is a space-saving idiom, though it relies on `y` never having been assigned a non-zero value elsewhere — which holds here since the only use of `y` as a live variable is in the FOR loop counter at line 430 (where it is the READ target, immediately consumed by POKE). Actually, looking more carefully, `y` in the FOR loop at line 430 is the READ destination: `READ y`. After the subroutine returns, `y` will hold the last value read (0), so using it in DATA as a zero placeholder is safe for all DATA lines that are read after line 430 executes — and all DATA is consumed within that subroutine. ### String-Rotation Animation Smooth scrolling is achieved by treating the 32-character strings as circular buffers. For leftward movement, the first character of `a$` is saved, the string is rebuilt as `a$(2 TO 32)+b$`, and the result is printed. For rightward movement the last character is extracted and prepended. The smoke string (`c$` or `f$`) is similarly rotated one row above the train body on row 0. This rotation-by-slicing technique is a classic Sinclair BASIC scrolling idiom. ### Speed and Keypress Control The user enters a PAUSE value `n` at line 110; small values produce fast animation and larger values slow it down. Within each animation loop, `CODE INKEY$=13` (line 260 / line 400) checks for the ENTER key — if detected, the screen is cleared and the program restarts from line 20, allowing the user to choose a new speed and direction without a full restart. ### Bugs and Anomalies - **Loop counter reset (lines 250, 390):**`IF b=32 THEN LET b=1` is effectively dead code — after this line executes, `NEXT b` immediately increments `b` to 2 (or 33 before loop exit), so resetting to 1 does not cause the loop to repeat. The loop runs exactly 32 iterations regardless. - **Right-rotation string growth (lines 330, 350):** In the rightward loop, `f$=g$+f$` and `a$=b$+a$` prepend without trimming, so these strings grow by one character each iteration. After 32 iterations `a$` will be 57 characters long (25 initial + 32 prepended). This will cause display overflow onto adjacent screen positions and is a likely bug; the left loop correctly trims by slicing `(2 TO 32)`. - **Line 120 — UDG-M appears twice, UDG-L is absent:** The train string references `[UDG-M]` twice consecutively and skips `[UDG-L]`, which may be intentional (two identical carriage-side segments) or a typo. - **STOP at lines 280 and 420:** After one pass of 32 iterations the loop ends and hits STOP rather than looping continuously. Combined with the dead reset code, the animation only runs one 32-step cycle before halting. ### Variable Summary | Variable | Purpose | | --- | --- | | `n` | PAUSE duration (speed control) | | `a$` | 32-char train body string (UDG characters) | | `b$` | Temporary single character during rotation | | `c$` | 32-char smoke/top row string for leftward travel | | `D$` | Direction input (“R”/”L”) | | `e$` | Temporary character for rotating `c$` | | `f$` | 32-char smoke/top row string for rightward travel | | `g$` | Temporary character for rotating `f$` | | `x` | UDG POKE loop counter | | `y` | READ target; doubles as 0 placeholder in DATA | | `b` | Animation loop counter | ## Source Code ``` 10 REM "TRAIN" by Stan Livingston SINCUS Johnson City NY 1985 20 PLOT 0,159:DRAW 255,0 30 GO SUB 430 40 DIM a$(32) 50 DIM c$(32) 60 REM use USER DEFINED GRAPHICS 70 LET c$="[UDG-P][UDG-R] " 80 DIM f$(32) 90 LET f$="[UDG-Q] " 100 REM enter speed of train,try 1-15 110 INPUT "PAUSE?";n 120 LET a$="[UDG-A][UDG-B][UDG-C][UDG-D][UDG-E][UDG-F][UDG-G][UDG-F][UDG-G][UDG-F][UDG-G][UDG-H][UDG-I][UDG-H][UDG-I][UDG-H][UDG-I][UDG-J][UDG-K][UDG-J][UDG-K][UDG-M][UDG-M][UDG-N][UDG-O]" 130 REM DIRECTION OF TRAIN 140 INPUT "RIGHT OR LEFT ?";D$ 150 IF D$="R" OR D$="r" THEN GO TO 290 160 PRINT AT 1,0;a$ 170 FOR b=1 TO 32 180 LET e$=c$(1) 190 LET c$=c$(2 TO 32)+e$ 200 LET b$=a$(1) 210 LET a$=a$(2 TO 32)+b$+" " 220 PRINT AT 1,0;a$ 230 PRINT AT 0,0;c$ 240 PAUSE n 250 IF b=32 THEN LET b=1 260 IF CODE INKEY$=13 THEN CLS :GO TO 20 270 NEXT b 280 STOP 290 PRINT AT 1,0;a$ 300 PRINT AT 0,0;f$ 310 FOR b=1 TO 32 320 LET g$=f$(32) 330 LET f$=g$+f$ 340 LET b$=a$(32) 350 LET a$=b$+a$ 360 PRINT AT 1,0;a$ 370 PRINT AT 0,0;f$ 380 PAUSE n 390 IF b=32 THEN LET b=1 400 IF CODE INKEY$=13 THEN CLS :GO TO 20 410 NEXT b 420 STOP 430 FOR x=0 TO 143 440 READ y:POKE USR "[UDG-A]"+x,y 450 NEXT x 460 DATA 6,63,127,255,127,63,29,8 470 DATA 0,255,y,y,y,y,221,136 480 DATA 63,255,y,y,y,y,239,198 490 DATA 31,63,127,y,y,255,28,8 500 DATA 254,y,y,y,y,255,56,16 510 DATA 255,127,y,y,y,255,56,16 520 DATA 255,254,y,y,y,255,28,8 530 DATA 127,73,y,79,y,255,112,32 540 DATA 254,146,y,242,y,255,28,8 550 DATA 0,y,y,127,y,255,56,16 560 DATA 0,y,y,254,y,255,28,8 570 DATA 0,y,y,y,y,255,56,16 580 DATA 0,y,y,y,y,255,28,8 590 DATA 3,255,6,38,39,255,56,16 600 DATA 192,255,48,50,242,255,28,8 610 DATA 0,y,y,y,y,y,3,4 620 DATA 0,y,y,y,y,y,248,4 630 DATA 0,y,y,y,y,y,255,0 640 RETURN 650 SAVE "TRAINS" LINE 10 ```