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:
- 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.
- 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.
- Advance and termination (lines 95–100): Move the pointer
pto the next line; stop whenpequals 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
ais toggled each time a quote character (token 34) is encountered. Whileais true (inside a string), all match logic is bypassed withGO TO 90. - Embedded number literals (line 55): When token 14 is encountered (the inline floating-point marker), the loop index
jis 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 asPEEK(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
jfrom 4 tole+4. At the end of the line body, the match check at line 75 peeks atp+j+k-1, which for the last validjandk=lreachesp+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.
Content
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
