Confusion

This file is part of and Long Island Sinclair Timex (LIST) User Group Library Tape #2. Download the collection to get this file.
Developer(s): Floyd Chrysler
Date: 1983
Type: Program
Platform(s): TS 2068
Tags: Demo, Utility

CONFUSION is a utility that allows users to alter the visible (text) representation of numeric literals in a BASIC program without changing the underlying 5-byte floating-point value stored internally. It walks through each line of the program in memory, starting from PROG (system variable 23635/23636), decoding tokenized BASIC byte by byte and pausing whenever it encounters a number token (byte value 14) to prompt the user for a replacement display string. A 5-byte machine code routine, POKEd to a user-supplied address, is used to print Spectrum keyword tokens directly from their byte values. The utility also permits renumbering individual line numbers by overwriting the two-byte line number field at the start of each line, making those changes permanent in memory.


Program Analysis

Program Structure

The program enters via line 8800, which immediately jumps to the initialization subroutine at 9990. Control then returns to 9902, which begins the main loop that walks the tokenized BASIC program in memory line by line. The code is organized into several logical subroutines:

  • 9990–9995: Initialization — prints the introduction, reads the user-supplied address for the machine code routine, POKEs 5 bytes of machine code there, and sets up system variable pointers.
  • 9996–9999: Introduction text display subroutine.
  • 9902–9918: Main loop — reads line number and length from the current PROG pointer, offers optional line-number renaming, then iterates over the line’s bytes.
  • 9920–9946: Per-byte dispatch — handles string literals, keyword tokens, printable ASCII ranges, number tokens (byte 14), and control codes.
  • 9950–9970: Number-token handler — uses the machine code routine to print the keyword, shows the true floating-point value alongside the text representation, and allows the user to overwrite the text digits.
  • 9986–9988: Cursor position save/restore helpers using system variables 23688/23689.

Machine Code Usage

Because BASIC alone cannot easily print a Spectrum keyword token as text, the program asks the user to supply a safe 5-byte address and POKEs the following machine code there:

OffsetByte (decimal)Instruction
062LD A, n (opcode; operand is patched at offset 1)
10Operand — patched via POKE usr+1, Y-165
2195JP nn
369Low byte of target address (0x0745 = ROM keyword-print routine)
47High byte of target address

Before calling RANDOMIZE USR usr, the program patches the operand of LD A with Y-165, converting the token byte to the zero-based keyword index expected by the ROM routine at address 0x0745. This is called both in the keyword-printing path (9928) and in the number-token handler (9950).

Tokenized BASIC Line Format

The Spectrum stores each BASIC line as: 2 bytes of line number (big-endian), 2 bytes of line length (little-endian), then the tokenized bytes of the statement terminated by 0x0D (13). Numeric literals in the source are stored as their ASCII digit characters followed by a byte 14 (the number token) and then 5 bytes of floating-point value. The program uses PEEK PROG / PEEK (PROG+1) for the big-endian line number and PEEK (PROG+2) + 256*PEEK (PROG+3) for the little-endian length, advancing PROG by K+4 after each line.

Key BASIC Idioms and Techniques

  • Self-modifying dispatch via POKE + USR: Rather than a lookup table, the token printer patches a single byte in machine code and calls it repeatedly — an efficient technique given BASIC’s lack of indirect calls.
  • System variable access: VARS (address from 23627/23628) is used to expose the 5-byte floating-point value to the user at line 9958; cursor position is saved and restored through 23688/23689.
  • Text vs. real value separation: When the user enters replacement digits at 9970, only the ASCII digit bytes before the 14-token are overwritten; the 5-byte float is never touched, so the program runs correctly while displaying misleading text.
  • Cursor positioning for display: Lines 99609962 use PRINT AT 0,0 twice to overlay the “true value” and “max chars” hint in the top-left corner without scrolling.
  • String padding: Lines 9969–9970 pad the user’s input with spaces to exactly J characters before POKEing, ensuring the replacement string fits precisely into the space vacated by the original digits.

Byte-Value Dispatch Logic

The per-byte handler at 9920–9946 uses a series of cascading IF tests rather than a ON ... GO TO table. The ranges tested are:

  • Y = 34 (quote): enters string-literal copy loop at 9922.
  • Y = 234: a keyword token inside a string — routed to the machine code printer at 9928.
  • Y > 31 AND Y < 40 or Y > 47 AND Y < 59: printable punctuation/digit range.
  • Y > 62 AND Y < 92 or Y > 94 AND Y < 165: upper-ASCII printable range.
  • Y > 164: keyword token — calls machine code printer via 9950.
  • Y = 13: end-of-line, returns.
  • Y = 14: number token — skips 5 bytes (LET K=K+5) since the text digits before it were already printed.
  • Y = 41, 46, 93: specific punctuation characters printed directly.

Notable Anomalies

  • The CY manipulation at lines 9906 and 9960 (incrementing the saved cursor row from 2 to 3 or 3 to 4) is an ad-hoc layout adjustment to avoid overwriting the status line; it relies on the specific screen row where the program begins printing.
  • Line 9944 falls through to 9951 (the trailing-space consumer) via GO TO 9951 after printing, which means any byte in the Y > 31 catch-all also triggers space-skipping — likely intentional for token spacing but could consume spaces that are part of string content if the dispatch reaches this branch unexpectedly.
  • The check at 9952 (PEEK (Z+1) < 48 OR > 57) guards against non-digit bytes after a token, but when a number token is immediately followed by the 14 byte (which has value 14, well below 48), the subroutine returns without processing — this is the normal case and is correct behavior.
  • Line 9904 stops processing when LINE >= 10000, which acts as a sentinel: line numbers 10000+ are treated as end-of-program, consistent with the Spectrum’s 9999-line limit for valid BASIC.

Content

Appears On

Track the OSCAR 10 satellite, design Bézier curves interactively, take a geography quiz on a hand-drawn map of North America, or hear the Olympic fanfare — LIST Library Tape 2 is a well-curated selection of practical and creative programs.

Related Products

Related Articles

Related Content

Image Gallery

Source Code

 8800 GO SUB 9990
 9901 REM  ** CONFUSION **            from Practical Computing             September 1983             entered by: F. Chrysler
 9902 LET LINE=256*PEEK PROG+PEEK (PROG+1)
 9903 LET LENGTH=PEEK (PROG+2)+256*PEEK (PROG+3)
 9904 IF LINE>=10000 THEN STOP 
 9906 GO SUB 9988: IF CY=2 THEN LET CY=3
 9907 PRINT LINE;" ";
 9908 INPUT "ENTER NEW LINE NO. ";B$: IF B$="" THEN GO TO 9914
 9910 LET LINE1=VAL B$: IF LINE1<=0 OR LINE1>9999 THEN GO TO 9908
 9912 POKE PROG,INT (LINE1/256): POKE PROG+1,LINE1-256*PEEK PROG: LET LINE=LINE1
 9914 GO SUB 9986: PRINT LINE;" ";
 9916 FOR K=0 TO LENGTH-1: LET Z=PROG+4+K: GO SUB 9920: NEXT K
 9918 PRINT : LET PROG=PROG+K+4: GO TO 9902
 9920 LET Y=PEEK Z: IF Y<>34 THEN GO TO 9926
 9922 PRINT CHR$ Y;: LET K=K+1: LET Z=Z+1: LET Y=PEEK Z
 9923 IF Y<>34 THEN GO TO 9922
 9924 PRINT CHR$ Y;: RETURN 
 9926 IF Y<>234 THEN GO TO 9934
 9928 POKE usr+1,Y-165: RANDOMIZE USR usr
 9930 LET K=K+1: LET Z=Z+1: LET Y=PEEK Z
 9931 IF Y<>13 THEN PRINT CHR$ Y;: GO TO 9930
 9932 RETURN 
 9934 IF (Y>31 AND Y<40) OR (Y>47 AND Y<59) THEN PRINT CHR$ Y;: RETURN 
 9935 IF (Y>62 AND Y<92) OR (Y>94 AND Y<165) THEN PRINT CHR$ Y;: RETURN 
 9936 IF Y>164 THEN GO SUB 9950: RETURN 
 9938 IF Y=13 THEN RETURN 
 9940 IF Y=14 THEN LET K=K+5: RETURN 
 9942 IF Y=41 OR Y=46 OR Y=93 THEN PRINT CHR$ Y;: RETURN 
 9944 IF Y>31 THEN PRINT CHR$ Y;: GO TO 9951
 9946 RETURN 
 9950 POKE usr+1,Y-165: RANDOMIZE USR usr
 9951 IF PEEK (Z+1)=32 THEN PRINT " ";: LET K=K+1: LET Z=Z+1: GO TO 9951
 9952 IF PEEK (Z+1)<48 OR PEEK (Z+1)>57 THEN RETURN 
 9954 LET Z=Z+1: LET J=0: LET A$=""
 9956 IF PEEK (Z+J)<>14 THEN LET A$=A$+CHR$ (PEEK (Z+J)): LET J=J+1: GO TO 9956
 9958 FOR L=0 TO 4: POKE VARS+L,PEEK (Z+J+L+1): NEXT L
 9960 GO SUB 9988: IF CY=3 AND CX-J<1 THEN LET CY=4
 9962 PRINT A$;: PRINT AT 0,0;C$: PRINT AT 0,0;"Really ";A,J;" CHARS MAX."
 9966 GO SUB 9986: INPUT B$: IF B$="" THEN RETURN 
 9968 IF CODE B$(1)<48 OR CODE B$(1)>57 OR LEN B$>J THEN GO TO 9966
 9969 IF LEN B$<J THEN FOR L=LEN B$+1 TO J: LET B$=B$+" ": NEXT L
 9970 FOR L=1 TO J: POKE Z+L-1,CODE (B$(L)): NEXT L: RETURN 
 9986 POKE 23688,CX: POKE 23689,CY: RETURN 
 9988 LET CX=PEEK 23688: LET CY=PEEK 23689: RETURN 
 9990 CLS : LET A=0: LET C$="                                ": LET C$=C$+C$
 9991 GO SUB 9996: RESTORE 9993: INPUT "ENTER ADDRESS FOR USR ";usr
 9992 FOR K=usr TO usr+4: READ L: POKE K,L: NEXT K
 9993 DATA 62,0,195,69,7
 9994 LET VARS=PEEK 23627+256*PEEK 23628+1
 9995 LET PROG=PEEK 23635+256*PEEK 23636: CLS : PRINT AT 2,0;"": RETURN 
 9996 PRINT "         CONFUSION": PRINT : PRINT "The idea of this program is to  enable the user to enter false  numeric data into a programme.": PRINT "Every time a number is found in the text you will be prompted toenter an alternative value."
 9997 PRINT "If you don't wish to change it, press <ENTER>. Otherwise type inthe value you want, first. Note that the 'real' value is not    changed, only the text value."
 9998 PRINT "The present true value is shown at top left. The maximum number of characters allowed is at top centre."
 9999 PRINT "You can also change the line    numbers, but this is for real.  You will now be asked to enter  the address where a 5 byte      machine code routine may be put.": RETURN 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top