Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

Source Code

  10 REM \CD\E7



Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
E

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
\FF

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
BC\B5\FB\F6

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
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

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
B\B1\F4\C9\CD\E7

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
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

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
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

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
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

Tracker

This file is part of Timex Sinclair Public Domain Library Tape 1003 . Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Hobby

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:

  1. Lines 10–50: Machine code in REM, array declarations
  2. Lines 60–150: Interactive setup — enter 12 event titles into F$() and 48 field positions into A()
  3. Lines 160–240: Print both arrays to the printer for verification
  4. Lines 250–380: Load a single tape record into A$(1) via machine code
  5. Lines 390–530: Display the loaded record and ask if this is the correct person
  6. Lines 540–930: Format name into B$, build event lines into C$
  7. Lines 940–1020: Bubble sort C$ rows by date/event field
  8. Lines 1030–1240: Three-pass LPRINT output; loop back to load next record
  9. 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.

ArrayDimensionsPurpose
A$(1,1459)1 row × 1459 charsRaw tape record
B$(1,63) / redimmed to 701 row × 70 charsFormatted centred name
F$(12,10)12 rows × 10 charsEvent title labels
A(48)48 elementsField start positions in A$
C$(12,70)12 rows × 70 charsFormatted 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

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Tracker

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.

Scroll to Top
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.

People

No people associated with this content.

Scroll to Top