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.
- Lines 740–820: Initialization, menu, UDG loading dispatch, direction routing
- Lines 240–260: Generic UDG POKE subroutine (“poker util”)
- Lines 270–500: Cars and trucks scene setup and pixel data
- Lines 510–730: Train scene setup and pixel data
- Lines 30–110: Left-scroll animation loop
- 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 buildsf$asg$+f$rather thang$+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
| Variable | Purpose |
|---|---|
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 |
n | PAUSE duration controlling animation speed |
rout | Scene selection (1=traffic, 2=train) |
d$ | Direction selection (“R”/”r” or other) |
y | UDG byte count / loop data value (overloaded) |
b | Animation 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
yas 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.
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 1Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
