--- title: "Variable Trace" id: 71252 type: "computer_media" slug: "variable-trace" url: "http://localhost/computer_media/variable-trace/" markdown_url: "http://localhost/computer_media/variable-trace.md" published_at: "2026-08-30T17:39:48+00:00" modified_at: "2026-08-30T17:39:49+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/variable-trace.png" alt: "Variable Trace screen" excerpt: "Pinpoint every line and statement where a named variable appears by walking the tokenized BASIC program area in memory — a genuine debugging utility." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Variable%20Trace%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/variable-trace.png" alt: "Variable Trace screen" media_type_tags: "Utility" --- # Variable Trace Variable Trace scans the BASIC program area to find every line and statement containing a user-specified variable name, then prints the line number and statement number where that variable appears. It reads the BASIC program pointer from system variables at addresses 23635–23636 and the variables area pointer from 23627–23628, walking each tokenized line by its stored length field. The search correctly handles string literals (tracking open/close quote state with flag `a`) and skips the five-byte floating-point number literals embedded after numeric tokens (code 14). A boundary check ensures that a matched string is not a prefix of a longer identifier by examining the character immediately following the match. *** ### Program Structure The program is a single-pass utility with no subroutines. It has three logical phases: 1. **Initialization (lines 5–15):** Read the variable name to search for, then load pointers to the BASIC program area and the variables area from system variables. 2. **Line scanning loop (lines 20–90):** For each BASIC line, decode its line number and length, then walk every byte looking for the target variable name. 3. **Advance and termination (lines 95–100):** Move the pointer `p` to the next line; stop when `p` equals the start of the variables area. ### System Variable Usage | Address | System Variable | Usage | | --- | --- | --- | | 23635–23636 | PROG | Start of BASIC program in RAM | | 23627–23628 | VARS | Start of variables area (used as end sentinel) | ### BASIC Line Format Navigation Each tokenized BASIC line is laid out as: two bytes of line number (high byte first, unusually big-endian), two bytes of line length (little-endian), then the tokenized statement bytes. The program reads the line number at `PEEK p` and `PEEK (p+1)` (note: line 20 has these in big-endian order: `256*PEEK p + PEEK(p+1)`), and the length at `PEEK(p+2) + 256*PEEK(p+3)` (little-endian). The body is then scanned from offset 4 through `le+4`. ### String Literal and Number Literal Skipping Two important guards prevent false positives inside non-identifier contexts: - **String literals (line 45–50):** The flag `a` is toggled each time a quote character (token 34) is encountered. While `a` is true (inside a string), all match logic is bypassed with `GO TO 90`. - **Embedded number literals (line 55):** When token 14 is encountered (the inline floating-point marker), the loop index `j` is advanced by 5 to skip the five number bytes that follow the display token. ### Statement Counter The variable `o` is initialized to 1 at line 35 and incremented at line 60 each time a colon (token 58) is seen, which is the statement separator in tokenized BASIC. This gives the printed column “statement number within the line.” ### Identifier Boundary Check After a byte-for-byte match of all `l` characters of `v$` (lines 70–75), line 80 reads the byte immediately following the match. If that byte is `$` (36), a digit (48–57), an uppercase letter (65–90), or a lowercase letter (97–122), the match is considered part of a longer name and is discarded. This prevents, for example, a search for `A` from hitting `AB` or `A1`. ### Notable Bugs and Anomalies - **Line number byte order inconsistency:** Line 20 reads the line number as `256*PEEK p + PEEK(p+1)` (big-endian), which is correct for the Spectrum’s BASIC line format. However, lines 25 reads the length as `PEEK(p+2) + 256*PEEK(p+3)` (little-endian), also correct. The two different byte orders are intentional and match the actual on-disk format. - **Loop range off-by-one risk:** The FOR loop at line 35 runs `j` from 4 to `le+4`. At the end of the line body, the match check at line 75 peeks at `p+j+k-1`, which for the last valid `j` and `k=l` reaches `p+le+4+l-1`, potentially reading into the next line’s header bytes for long variable names matched near the end of a line. - **Quote tracking limitation:** The quote-toggle approach (line 45) does not account for escaped or doubled quotes that some BASIC dialects support, but this is not relevant here as the tokenizer uses a single-byte delimiter. - **Single-character variable assumption in first-byte pre-check:** Line 65 compares only `CODE v$(1)` before entering the full match loop, which is a valid optimization since any mismatch on the first character skips the inner FOR entirely. ### Output Format For each match found, line 85 prints: the line number `n` left-justified, a colon and statement number `o` at TAB 5, and the matched variable name `v$` at TAB 10. Line 30 prints the line number again at the start of each new line’s scan, which serves as a running progress indicator of lines being examined. ## Source Code ``` 5 INPUT "Variable to trace ";v$:LET l= LEN v$ 10 LET p= PEEK 23635+256* PEEK 23636 15 LET v= PEEK 23627+256* PEEK 23628 20 LET n=256* PEEK p+ PEEK (p+1) 25 LET le= PEEK (p+2)+256* PEEK (p+3) 30 PRINT n 35 LET a=0:LET o=1:FOR j=4 TO le+4 40 LET p1= PEEK (p+j) 45 IF p1=34 THEN LET a= NOT a 50 IF a THEN GO TO 90 55 IF p1=14 THEN LET j=j+5:GO TO 90 60 IF p1=58 THEN LET o=o+1 65 IF p1 <> CODE v$(1) THEN GO TO 90 70 FOR k=1 TO l 75 LET p1= PEEK (p+j+k-1):IF p1 <> CODE v$(k) THEN GO TO 90 80 NEXT k:LET k= PEEK (p+j+k-1):IF k=36 OR (k>47 AND k<58) OR (k>64 AND k<91) OR (k>96 AND k<123) THEN GO TO 90 85 PRINT n; TAB 5;":";o; TAB 10;v$ 90 NEXT j 95 LET p=4+p+le:IF p=v THEN STOP 100 GO TO 20 9000 SAVE "TRACE" LINE 0 ```