Traffic

Developer(s): Stan Livingston
Date: 1986
Type: Program
Platform(s): TS 2068
Tags: Demo

Traffic is a TS 2068 animation program that scrolls either road vehicles or a train across two rows of the display using string-rotation techniques. The program uses 21 user-defined graphics (UDGs) loaded via machine-code POKEs to memory address 65368, with the DATA statements supplying the pixel patterns for cars, trucks, or train carriages. Two animation modes are supported: leftward scrolling (lines 30–110) rotates strings by shifting the first character to the end, while rightward scrolling (lines 120–200) takes the last character and prepends it. The user can select the subject (traffic or train), scrolling direction (left or right), and a PAUSE value to control animation speed; pressing Enter (CODE INKEY$ = 13) returns to the main menu.


Program Structure

The program is organized into distinct functional blocks, with the main entry point at line 740 rather than the top of the listing. Lines 30–110 handle leftward scrolling, lines 120–200 handle rightward scrolling, and lines 210–500 contain the UDG loader subroutine and cars/trucks DATA. Lines 510–730 hold the train scene definition and its DATA. The main menu and initialization logic sits at lines 740–820.

  1. Lines 740–820: Initialization, menu, UDG loading dispatch, direction routing
  2. Lines 240–260: Generic UDG POKE subroutine (“poker util”)
  3. Lines 270–500: Cars and trucks scene setup and pixel data
  4. Lines 510–730: Train scene setup and pixel data
  5. Lines 30–110: Left-scroll animation loop
  6. Lines 120–200: Right-scroll animation loop

UDG Loading Mechanism

The subroutine at line 250 reads a byte count from DATA into y, then POKEs successive bytes starting at address 65368, which is the UDG area. This is a compact, data-driven approach to defining custom characters entirely from BASIC without separate machine code routines. The variable y does double duty: it holds the count initially, then is overwritten by each data byte during the POKE loop. The two scenes (traffic and train) use separate RESTORE targets — RESTORE (defaulting to line 300) for traffic and RESTORE 540 for the train — so the same loader subroutine can serve both.

String-Rotation Animation

The scrolling effect is implemented purely through string manipulation rather than any display hardware trick. Two strings are animated simultaneously: a road/track string (c$ or f$) and a vehicle body string (a$), both 32 characters wide to fill a display row.

  • Left scroll (lines 50–60): Takes the first character of each string and appends it to the right end — LET e$=c$(1): LET c$=c$(2 TO )+e$ — producing a circular left shift.
  • Right scroll (lines 140–150): Takes the last character of each string and prepends it — LET g$=f$(32): LET f$=g$+f$ — producing a circular right shift. Note that this incorrectly builds f$ as g$+f$ rather than g$+f$(1 TO 31), so the string grows beyond 32 characters each iteration, which is a bug.

Both rows are printed together with a single PRINT AT 0,0 statement, keeping the two animation layers in sync.

Control Flow and Loop Design

Each animation loop runs a FOR b=1 TO 32 counter, but line 90 (and 180) reset b to 1 when it reaches 32, creating an infinite loop within the FOR...NEXT structure. This is an unusual idiom — the NEXT b at lines 110 and 200 is nominally reached but immediately loops back via the reassignment, effectively turning a finite FOR loop into an infinite one. Pressing Enter (detected as CODE INKEY$=13) breaks out to the main menu at line 740.

DATA Anomalies

The DATA statements (lines 300–500 and 550–730) mix lowercase y and uppercase Y as filler values within the pixel data. In Spectrum BASIC, both evaluate to the current value of the numeric variable y at the time READ is executed. Since y is set to 1 or 2 by the menu logic and then overwritten during the POKE loop, rows containing y/Y mid-sequence will read whatever value y last held, which changes on each iteration. This appears to be intentional shorthand for a common byte value (likely 0 or 255) but is fragile and dependent on loop state — a potential source of subtle graphic errors if the DATA ordering changes.

The train DATA at line 700 also contains PI as a DATA value, which evaluates to approximately 3.14159 and will be truncated to 3 when POKEd. This is almost certainly a typo for a numeric literal.

Key Variables

VariablePurpose
a$32-char vehicle body string (both scenes)
c$32-char road/track string for left-scroll
f$32-char road/track string for right-scroll
nPAUSE duration controlling animation speed
routScene selection (1=traffic, 2=train)
d$Direction selection (“R”/”r” or other)
yUDG byte count / loop data value (overloaded)
bAnimation loop counter (artificially kept infinite)

Notable Techniques and Bugs

  • The UDG loader subroutine is reused for both scenes by changing the RESTORE target before calling it, avoiding code duplication.
  • The right-scroll string growth bug (line 140: f$=g$+f$ without trimming) will cause the string to exceed 32 characters after the first iteration, likely producing a string-length error after a short time.
  • The same bug appears at line 150 for a$: a$=b$+a$ without truncation.
  • Using y as both the DATA sentinel and a recurring placeholder within DATA is a memory-saving trick but makes the code difficult to maintain and error-prone.
  • Line 830 uses SAVE "traffic" LINE 1, directing auto-run to line 1 (the REM), which means the program does not auto-start at the intended entry point (line 740) when loaded from tape.

Image Gallery

Source Code

  10 REM traffic
  20 GO TO 740
  30 PRINT AT 1,0;a$
  40 FOR b=1 TO 32
  50 LET e$=c$(1):LET c$=c$(2 TO )+e$
  60 LET b$=a$(1):LET a$=a$(2 TO )+b$
  70 PRINT AT 0,0;c$;a$
  80 PAUSE n
  90 IF b=32 THEN LET b=1
 100 IF CODE INKEY$=13 THEN GO TO 740
 110 NEXT b:STOP 
 120 PRINT AT 0,0;f$;a$
 130 FOR b=1 TO 32
 140 LET g$=f$(32):LET f$=g$+f$
 150 LET b$=a$(32):LET a$=b$+a$
 160 PRINT AT 0,0;f$;a$
 170 PAUSE n
 180 IF b=32 THEN LET b=1
 190 IF CODE INKEY$=13 THEN GO TO 740
 200 NEXT b:STOP 
 210 REM 
 220 REM 
 230 REM 
 240 REM  poker util 
 250 READ y:FOR x=65368 TO 65368+y
 260 READ y:POKE x,y:NEXT x:RETURN 
 270 REM  cars & trucks 
 280 LET c$="[UDG-P][UDG-Q][UDG-R][UDG-S][UDG-T]":LET f$="[UDG-P][UDG-Q][UDG-R][UDG-S][UDG-T]":LET a$="[UDG-F][UDG-G][UDG-H][UDG-I][UDG-J]  [UDG-K][UDG-L]  [UDG-M][UDG-N][UDG-O] [UDG-A][UDG-B] [UDG-C][UDG-D][UDG-E] [UDG-C][UDG-D][UDG-E] [UDG-C][UDG-D][UDG-E]":RETURN 
 290 REM  cars & trucksDATA 
 300 DATA 159,0,1,2,30,63,127,56,16
 310 DATA 240,248,76,78,254,y,28,8
 320 DATA 0,1,3,63,63,127,56,16
 330 DATA 255,204,y,255,y,y,0,y
 340 DATA 240,216,204,252,y,254,28,8
 350 DATA 2,63,y,y,y,y,18,12
 360 DATA 66,254,y,y,y,253,36,24
 370 DATA 72,y,73,72,64,255,18,12
 380 DATA 84,134,5,194,0,255,0,y
 390 DATA 84,36,84,36,4,252,72,48
 400 DATA 0,y,120,63,y,91,74,48
 410 DATA 192,y,94,252,y,218,18,12
 420 DATA 3,2,y,63,y,127,56,16
 430 DATA 255,125,82,226,253,255,0,y
 440 DATA 254,170,234,170,y,255,14,4
 450 DATA 0,y,y,y,y,y,3,2
 460 DATA 0,y,y,y,y,y,254,66
 470 DATA 0,y,y,y,y,127,64,93
 480 DATA 0,y,y,y,y,255,0,146
 490 DATA 0,y,y,y,y,252,4,36
 500 RETURN 
 510 REM   "TRAIN"              by Stan Livingston SINCUS       Johnson City NY 1985
 520 REM  train 
 530 LET c$="[UDG-P][UDG-R]":LET f$="[UDG-Q]":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]":RETURN 
 540 REM  trainDATA 
 550 DATA 143,6,63,127,255,127,63,29,8
 560 DATA 0,255,Y,Y,Y,Y,221,136
 570 DATA 63,Y,Y,Y,Y,Y,239,198
 580 DATA 31,63,127,Y,Y,255,28,8
 590 DATA 254,Y,Y,Y,Y,255,56,16
 600 DATA 255,127,Y,Y,Y,255,56,16
 610 DATA 255,254,Y,Y,Y,255,28,8
 620 DATA 127,73,Y,79,Y,255,112,32
 630 DATA 254,146,Y,242,Y,255,28,8
 640 DATA 0,Y,Y,127,Y,255,56,16
 650 DATA 0,Y,Y,254,Y,255,28,8
 660 DATA 0,Y,Y,Y,Y,255,56,16
 670 DATA 0,Y,Y,Y,Y,255,28,8
 680 DATA 3,255,6,38,39,255,56,16
 690 DATA 192,255,48,50,242,255,28,8
 700 DATA 0,Y,Y,Y,Y,Y, PI,4
 710 DATA 0,Y,Y,Y,Y,Y,248,4
 720 DATA 0,Y,Y,Y,Y,Y,255,0
 730 RETURN 
 740 CLS :PLOT 0,159:DRAW 255,0
 750 DIM a$(32):DIM c$(32):DIM f$(32)
 760 INPUT "Do you wish to"''"   1. see traffic"'"   2. see the train ";rout:IF rout<1 OR rout>2 THEN GO TO 760
 770 IF rout=1 THEN RESTORE :LET y=1:GO SUB 240:GO SUB 280
 780 IF rout=2 THEN LET y=2:RESTORE 540:GO SUB 240:GO SUB 530
 790 INPUT "PAUSE? ";n
 800 INPUT "Right or Left? ";d$
 810 IF d$="R" OR d$="r" THEN GO TO 120
 820 GO TO 30
 830 SAVE "traffic" LINE 1

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