This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$()and 48 field positions intoA() - Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1)via machine code - Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines intoC$ - Lines 940–1020: Bubble sort
C$rows by date/event field - Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
| Array | Dimensions | Purpose |
|---|---|---|
A$(1,1459) | 1 row × 1459 chars | Raw tape record |
B$(1,63) / redimmed to 70 | 1 row × 70 chars | Formatted centred name |
F$(12,10) | 12 rows × 10 chars | Event title labels |
A(48) | 48 elements | Field start positions in A$ |
C$(12,70) | 12 rows × 70 chars | Formatted event output lines |
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.DIM B$(1,70)at line 570 re-dimensionsB$mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.- The
GOTO 0250at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart. - Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT Xto skip blank/unused rows inC$— a ZX81 idiom for sparse array iteration. - The SAVE at line 1270 uses
"1016%1"where%1is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1orZ+1on the final iteration without a bounds check. DIM C$(12,70)at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clearsC$each time, which is actually correct behaviour for processing a new record.- Variable
Lis used as the event line counter but is never explicitly reset to 1 before theFOR I=1 TO 12loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
E
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
\FF
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
BC\B5\FB\F6
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
AE\C5E\CD itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-57273 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.5 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"F\C1
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
B\B1\F4\C9\CD\E7
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
AE\C5 itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-57273 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.5 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"E\DB\FE\D3\FF\F9
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
E itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-57273 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.5 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"A
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
D\DB\FE\CB\F5\F5\FE\E4F\CB itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-57273 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.5 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"D\DE\C1
Skip to content
Tracker
This file is part of Timex Sinclair Public Domain Library Tape 1003
. Download the collection to get this file.
This program is a ZX81/TS1000 athlete or competitor tracking utility called “TRACKER” that reads records from data tape and formats them for printed output. It uses two machine code routines embedded in the line 10 REM block: one at USR 16549 (called via RAND USR 16549) that handles tape loading of a fixed-length 1459-character string array record. The program manages three string arrays — A$ for raw tape data (1×1459), B$ for formatted name output (1×63/70), and C$ for formatted event lines (12×70) — plus a numeric array A(48) storing field start positions within A$. Events are sorted by a simple bubble sort on the first 10 characters of each C$ row, then printed across three LPRINT passes showing name, place, and additional data columns separated by decorative lines.
Program Analysis
Program Structure
The program is organized into clearly delineated phases, each preceded by an inverse-video REM label acting as a section header. The overall flow is:
- Lines 10–50: Machine code in REM, array declarations
- Lines 60–150: Interactive setup — enter 12 event titles into
F$() and 48 field positions into A()
- Lines 160–240: Print both arrays to the printer for verification
- Lines 250–380: Load a single tape record into
A$(1) via machine code
- Lines 390–530: Display the loaded record and ask if this is the correct person
- Lines 540–930: Format name into
B$, build event lines into C$
- Lines 940–1020: Bubble sort
C$ rows by date/event field
- Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
- Lines 1260–1280: SAVE routine (apparently reached only by direct RUN or manual GO TO)
Machine Code Routine (REM Line 10)
The REM at line 10 contains 69 bytes of Z80 machine code. RAND USR 16549 at line 330 jumps into this code. Disassembly of the first routine (starting at approximately offset 1 within the REM data, mapped to address 16549) implements a tape-loading loop: it reads bytes from the tape port (DB FE = IN A,(254)), assembles bits with rotation instructions, and stores them into memory addressed via the A$ array’s data area obtained from a pointer at address 0x4010 (2A 10 40 = LD HL,(16400)). The routine loads exactly the length defined by DIM A$(1,1459). A second sub-routine within the REM handles a delay or sync loop. The use of DB FE/D3 FF (IN/OUT on port 254/255) confirms this is ZX81/TS1000 tape I/O at the hardware level.
Array Layout and Field Addressing
The 1459-character flat string A$(1) stores a complete fixed-format record. Fields within it are not hard-coded; instead, the numeric array A(48) holds 48 start positions, used in groups of four per event (indexed as A(X), A(X+1), A(X+2), A(X+3)) for date, city, state/region, and country respectively. This indirection makes the program adaptable to different record layouts without rewriting field references.
Array Dimensions Purpose A$(1,1459)1 row × 1459 chars Raw tape record B$(1,63) / redimmed to 701 row × 70 chars Formatted centred name F$(12,10)12 rows × 10 chars Event title labels A(48)48 elements Field start positions in A$ C$(12,70)12 rows × 70 chars Formatted event output lines
Name and Place Formatting Subroutines
Lines 620–690 compress the name stored in B$ by iterating character by character, suppressing runs of spaces but inserting a single space between words, building a compacted string in D$. The result is then centred within a 70-character field using the expression (70-LEN D$)/2 as the start position of a string slice assignment — a neat ZX81 BASIC idiom for centring text without explicit PRINT AT arithmetic.
The subroutine at lines 850–930 performs an identical compaction on the place string Z$ (up to 47 characters), called via GOSUB 0850 from line 799. Both routines rely on checking the next character (B$(1,X+1) or Z$(Z+1)) without bounds-guarding, which could cause a subscript error on the last iteration if the final character is not a space — a latent off-by-one bug.
Bubble Sort Implementation
Lines 950–1020 implement an O(n²) selection/bubble sort on the C$() array, comparing the first 10 characters of each row (the date/event field) using standard BASIC string comparison. Row swaps use a temporary string variable S$. Because C$ is a two-dimensional string array, each assignment copies an entire 70-character row, which is memory-intensive but functionally correct.
Three-Pass Printer Output
Rather than printing each full 70-character event line in one pass, the LPRINT section makes three separate passes over the sorted C$ array, each printing a different slice of each row:
- Pass 1 (lines 1040–1100): Characters 1–19 — date and event name, with decorative inverse-space padding
- Pass 2 (lines 1120–1170): Characters 20–51 — place information
- Pass 3 (lines 1180–1240): Characters 52–end — additional data with trailing inverse-space decoration
Each pass is separated by a 32-dash rule printed via LPRINT. This approach effectively creates a three-column printed layout on a 32-column ZX printer by printing each column as a separate block, with the name in B$ as a header for each section.
Notable Techniques and Idioms
IF INKEY$="" THEN GOTO 0490 at line 490 is a busy-wait key poll loop — standard ZX81 practice in the absence of a blocking INPUT for single keystrokes.
DIM B$(1,70) at line 570 re-dimensions B$ mid-program (originally declared as 1×63 at line 30), clearing it and enlarging it. This is intentional to reclaim the layout space needed for centring.
- The
GOTO 0250 at line 1250 creates an indefinite loop: after printing, the program loops back to load the next tape record without requiring a restart.
- Lines 1070, 1140, and 1210 use
IF C$(X,1)=" " THEN NEXT X to skip blank/unused rows in C$ — a ZX81 idiom for sparse array iteration.
- The SAVE at line 1270 uses
"1016%1" where %1 is inverse “1”, functioning as an auto-run flag.
Potential Bugs and Anomalies
- The off-by-one risk in the compaction loops (lines 660 and 900) accessing index
X+1 or Z+1 on the final iteration without a bounds check.
DIM C$(12,70) at line 710 is declared inside the main processing block rather than at program start; if the code path is re-entered (e.g. looping back through line 250), this re-dimensions and clears C$ each time, which is actually correct behaviour for processing a new record.
- Variable
L is used as the event line counter but is never explicitly reset to 1 before the FOR I=1 TO 12 loop at line 740 on subsequent iterations — however, line 730 does reset it to 1 each pass through the format block.
Content
Source Code
10 REM \CD\E7\02\06\0E\21\00\FF\2B\7C\B5\20\FB\10\F6\2A\10\40\23\4E\23\46\23\C5\5E\CD\1F\03\C1\0B\78\B1\20\F4\C9\CD\E7\02\2A\10\40\23\4E\23\46\23\C5\1E\08\DB\FE\D3\FF\17\30\F9\0E\94\06\1A\0D\DB\FE\17\CB\79\79\38\F5\10\F5\20\04\FE\56\30\E4\3F\CB\16\1D\20\DE\C1\0B\78\B1\C8\18\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
1000 LET C$(S)=S$
1010 NEXT S
1020 NEXT X
1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
1040 LPRINT "% % % % % ";B$(1, TO 19),,
1050 LPRINT "% % % % % ";"DATE EVENT",,,
1060 FOR X=1 TO L
1070 IF C$(X,1)=" " THEN NEXT X
1080 LPRINT "% % % % % ";C$(X, TO 19),,
1090 NEXT X
1100 LPRINT ,,"--------------------------------",,
1110 LPRINT B$(1,20 TO 51),,
1120 LPRINT " PLACE ",,
1130 FOR X=1 TO L
1140 IF C$(X,1)=" " THEN NEXT X
1150 LPRINT C$(X,20 TO 51),,
1160 NEXT X
1170 LPRINT ,,"--------------------------------",,
1180 LPRINT B$(1,52 TO );" % % % % % % ",,
1190 LPRINT " % % % % % % ",,
1200 FOR X=1 TO L
1210 IF C$(X,1)=" " THEN NEXT X
1220 LPRINT C$(X,52 TO );" % % % % % % ",,
1230 NEXT X
1240 LPRINT ,,"--------------------------------",,
1250 GOTO 0250
1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
1270 SAVE "1016%1"
1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
People
No people associated with this content.
B\B1\C8\D3
20 DIM A$(1,1459)
30 DIM B$(1,63)
40 DIM F$(12,10)
50 DIM A(48)
60 REM %L%O%A%D% %F%$% %A%R%R%A%Y% % % % % % % % % %
70 FOR X=1 TO 12
80 PRINT AT 10,0;"ENTER EVENT TITLE";TAB 19;X;TAB 24;"(10 POS)"
90 INPUT F$(X)
100 NEXT X
110 REM %L%O%A%D% %A% %A%R%R%A%Y% % % % % % % % % % %
120 FOR X=1 TO 48
130 PRINT AT 10,0;"ENTER STARTING POSITION OF FIELD"
140 INPUT A(X)
150 NEXT X
160 REM %P%R%I%N%T% %A%R%R%A%Y%S% % % % % % % % % % %
170 LPRINT "F$ ARRAY",,,,
180 FOR X=1 TO 12 STEP 2
190 LPRINT X;TAB 3;F$(X);TAB 15;X+1;TAB 19;F$(X+1)
200 NEXT X
210 LPRINT ,,"A ARRAY",,,,
220 FOR X=1 TO 48 STEP 3
230 LPRINT X;TAB 3;A(X);TAB 10;X+1;TAB 13;A(X+1);TAB 20;X+2;TAB 23;A(X+2)
240 NEXT X
250 REM %R%E%A%D% %A% %R%E%C%O%R%D% %F%R%O%M% %T%A%P%E
260 CLS
270 SLOW
280 PRINT AT 2,13;"TRACKER"
290 PRINT AT 10,0;"PLACE DATA TAPE IN TAPE DECK"
300 PRINT AT 12,0;"PRESS ""PLAY"" ON TAPE DECK"
310 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
320 INPUT Z$
330 RAND USR 16549
340 CLS
350 SLOW
360 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
370 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
380 INPUT Z$
390 REM %C%H%E%C%K% %F%O%R% %R%I%G%H%T% %P%E%R%S%O%N%
400 CLS
410 PRINT AT 2,13;"TRACKER"
420 PRINT AT 8,0;"GEN/CODE NBR. ";A$(1, TO 5)
430 PRINT "FIRST NAME ";A$(1,6 TO 20)
440 PRINT "SECOND NAME ";A$(1,21 TO 35)
450 PRINT "THIRD NAME ";A$(1,36 TO 50)
460 PRINT "LAST NAME ";A$(1,51 TO 65)
470 PRINT AT 14,0;"DO YOU WISH TO TRACK THIS PERSON"
480 PRINT "(Y OR N)"
490 IF INKEY$="" THEN GOTO 0490
500 LET I$=INKEY$
510 IF I$<>"Y" AND I$<>"N" THEN GOTO 0480
520 CLS
530 IF I$="N" THEN GOTO 0250
540 REM %F%O%R%M%A%T% %R%E%C%O%R%D%S% % % % % % % % %
550 REM %R%E%D%U%C%E% %N%A%M%E% %L%E%N%G%T%H% % % % %
560 FAST
570 DIM B$(1,70)
580 LET B$(1, TO 15)=A$(1,6 TO 20)
590 LET B$(1,17 TO 31)=A$(1,21 TO 35)
600 LET B$(1,33 TO 47)=A$(1,36 TO 50)
610 LET B$(1,49 TO 63)=A$(1,51 TO 65)
620 LET D$=""
630 FOR X=1 TO 63
640 IF B$(1,X)=" " THEN GOTO 0670
650 LET D$=D$+B$(1,X)
660 IF B$(1,X+1)=" " THEN LET D$=D$+" "
670 NEXT X
680 LET B$(1)=""
690 LET B$(1,((70-LEN D$)/2) TO )=D$
700 REM %F%O%R%M%A%T% %E%V%E%N%T% %L%I%N%E%S% % % % %
710 DIM C$(12,70)
720 LET X=1
730 LET L=1
740 FOR I=1 TO 12
750 IF A$(1,A(X))=" " THEN GOTO 0820
760 LET C$(L, TO 10)=A$(1,A(X) TO (A(X)+9))
770 LET C$(L,12 TO 21)=F$(I)
780 LET Z$=A$(1,A(X+1) TO (A(X+1)+14))+" "+A$(1,A(X+2) TO (A(X+2)+14))+" "+"CO. "+A$(1,A(X+3) TO (A(X+3)+14))+" "
790 GOSUB 0850
800 LET C$(L,23 TO )=Z$
810 LET L=L+1
820 LET X=X+4
830 NEXT I
840 GOTO 0940
850 REM %R%E%D%U%C%E% %P%L%A%C%E% %L%E%N%G%T%H% % % %
860 LET D$=""
870 FOR Z=1 TO 47
880 IF Z$(Z)=" " THEN GOTO 0910
890 LET D$=D$+Z$(Z)
900 IF Z$(Z+1)=" " THEN LET D$=D$+" "
910 NEXT Z
920 LET Z$=D$
930 RETURN
940 REM %S%O%R%T% %R%E%C%O%R%D%S% % % % % % % % % % %
950 FOR X=1 TO L-1
960 FOR S=X+1 TO L
970 IF C$(X, TO 10)<C$(S, TO 10) THEN GOTO 1010
980 LET S$=C$(X)
990 LET C$(X)=C$(S)
\n1000 LET C$(S)=S$
\n1010 NEXT S
\n1020 NEXT X
\n1030 REM %P%R%I%N%T% %R%E%C%O%R%D%S% % % % % % % % % %
\n1040 LPRINT "% % % % % ";B$(1, TO 19),,
\n1050 LPRINT "% % % % % ";"DATE EVENT",,,
\n1060 FOR X=1 TO L
\n1070 IF C$(X,1)=" " THEN NEXT X
\n1080 LPRINT "% % % % % ";C$(X, TO 19),,
\n1090 NEXT X
\n1100 LPRINT ,,"--------------------------------",,
\n1110 LPRINT B$(1,20 TO 51),,
\n1120 LPRINT " PLACE ",,
\n1130 FOR X=1 TO L
\n1140 IF C$(X,1)=" " THEN NEXT X
\n1150 LPRINT C$(X,20 TO 51),,
\n1160 NEXT X
\n1170 LPRINT ,,"--------------------------------",,
\n1180 LPRINT B$(1,52 TO );" % % % % % % ",,
\n1190 LPRINT " % % % % % % ",,
\n1200 FOR X=1 TO L
\n1210 IF C$(X,1)=" " THEN NEXT X
\n1220 LPRINT C$(X,52 TO );" % % % % % % ",,
\n1230 NEXT X
\n1240 LPRINT ,,"--------------------------------",,
\n1250 GOTO 0250
\n1260 REM %S%A%V%E% %T%R%A%C%K%E%R% % % % % % % % % % %
\n1270 SAVE "1016%1"
\n1280 GOTO 0250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
