Variable Trace

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Utility

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

AddressSystem VariableUsage
23635–23636PROGStart of BASIC program in RAM
23627–23628VARSStart 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.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

People

No people associated with this content.

Scroll to Top