--- title: "Confusion" id: 55004 type: "computer_media" slug: "confusion" url: "http://localhost/computer_media/confusion/" markdown_url: "http://localhost/computer_media/confusion.md" published_at: "2024-06-10T07:03:30+00:00" modified_at: "2026-03-30T21:41:32+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "This clever BASIC utility lets you replace the displayed text of numeric literals in any program while leaving their true floating-point values completely intact." 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/" indiv: - name: "Floyd Chrysler" slug: "floyd-chrysler" taxonomy: "indiv" url: "http://localhost/indiv/floyd-chrysler/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 55021 title: "Long Island Sinclair Timex (LIST) User Group Library Tape #2" type: "computer_media" url: "http://localhost/computer_media/list-lib-2/" media_type: "Program" programmers: - name: "Floyd Chrysler" slug: "floyd-chrysler" taxonomy: "indiv" url: "http://localhost/indiv/floyd-chrysler/" mediadate: "1983" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20260329-satw.png" media_type_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: | Offset | Byte (decimal) | Instruction | | --- | --- | --- | | 0 | 62 | `LD A, n` (opcode; operand is patched at offset 1) | | 1 | 0 | Operand — patched via `POKE usr+1, Y-165` | | 2 | 195 | `JP nn` | | 3 | 69 | Low byte of target address (0x0745 = ROM keyword-print routine) | | 4 | 7 | High 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 `9960`–`9962` 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. ## 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$. 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 ```