--- title: "Reader 1" id: 71239 type: "computer_media" slug: "reader-1" url: "http://localhost/computer_media/reader-1/" markdown_url: "http://localhost/computer_media/reader-1.md" published_at: "2026-08-30T20:17:58+00:00" modified_at: "2026-08-30T20:18:09+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/reader-2.png" alt: "Reader 1 screen" excerpt: "Peek inside your own BASIC program's raw token stream, byte by byte, with this self-reading memory inspector that decodes addresses and character values live." 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: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Reader%201%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/reader-2.png" alt: "Reader 1 screen" media_type_tags: "Programming" --- # Reader 1 Reader 1 is a BASIC program that walks through the current program’s own tokenized BASIC storage in memory, displaying each byte’s address, decimal value, and printable ASCII representation. It reads the start-of-BASIC address from system variable PROG (held at addresses 23635–23636) and the address of variables (VARS, at 23627–23628) to determine where the program ends. Bytes with values below 16 or above 23 are displayed as characters using CHR$; values in the range 16–23 (which correspond to embedded control codes such as AT, TAB, and similar tokens with following parameter bytes) are labeled as unprintable. The program loops through each byte sequentially, incrementing the pointer and halting when it reaches the VARS boundary. ### Program Structure The program is a short sequential loop with no subroutines. Lines 10–15 initialize two pointers from system variables, lines 20–40 form the main display loop, and line 9000 is a standalone save command. The overall flow is linear: 1. Lines 10–15: Read system variables to establish the byte-walk range. 2. Line 20: Peek the current byte at the pointer address. 3. Line 25: Print address and value; branch on printability. 4. Line 30: Print the unprintable label (reached by fall-through when the condition in line 25 is false). 5. Line 35: Advance the pointer; stop if the VARS boundary is reached. 6. Line 40: Loop back to line 20. ### System Variable Usage The program reads two pairs of system variables to bound its walk: | Variable | Addresses | Role | | --- | --- | --- | | `p` (PROG) | 23635–23636 | Start address of the BASIC program area | | `v` (VARS) | 23627–23628 | Start address of the variables area (end of program) | Both are read as 16-bit little-endian values using the standard idiom `PEEK low + 256 * PEEK high`. The walk therefore covers exactly the tokenized program bytes, stopping before the variables area. ### Printability Test Line 25 uses the condition `IF o<16 OR o>23` to decide whether a byte can be shown with `CHR$`. Values 16–23 are the embedded in-line parameter codes (such as `AT`, `TAB`, `INK`, `PAPER`, `BRIGHT`, `FLASH`, `INVERSE`, and `OVER`) which are followed by one or two data bytes in the token stream and would produce garbled or missing output if passed directly to `CHR$`. Bytes outside that range are considered safe to display as characters. Note that values 0–15 include newline markers and other control codes that are also non-printable in a conventional sense, so the test is a practical approximation rather than a fully rigorous printability filter. ### Key BASIC Idioms - **Compound line with conditional**: Line 25 puts the `PRINT` and the `IF` on a single line, using the colon separator. When the condition is true the `GO TO 35` skips line 30; when false, execution falls through to the “UNPRINTABLE” branch — a common ZX BASIC branching pattern. - **16-bit pointer arithmetic**: Both pointers are reconstructed from pairs of PEEKed bytes with `PEEK addr + 256 * PEEK (addr+1)`, which is the standard technique for reading 16-bit system variables. - **Boundary check before increment**: Line 35 increments `p` first and then checks against `v`, meaning the byte at the very last address before VARS is processed but the VARS byte itself is never read. ### Self-Referential Nature Because PROG points to the start of the running program itself, Reader 1 is examining its own token stream as it executes. This means the output includes the tokenized bytes of its own lines, making it a self-describing diagnostic tool. The large line-number gap between line 40 and the `SAVE` at line 9000 is a deliberate separation to keep the save command visually and logically isolated from the operational code. ### Potential Anomalies - The `SAVE "READER 1" LINE 0` at line 9000 references `LINE 0`. Line 0 does not exist in the program; this is a known technique that causes the loaded program to start execution at the first available line (line 10) rather than indicating an error. - The display column layout relies on fixed `TAB` stops (8 and 16), which could overflow or misalign for addresses above 99999 or byte values printed with `CHR$` producing wide characters — though in practice both values stay within expected ranges. ## Source Code ``` 10 LET p= PEEK 23635+256* PEEK 23636 15 LET v= PEEK 23627+256* PEEK 23628 20 LET o= PEEK p 25 PRINT p; TAB 8;o;:IF o<16 OR o>23 THEN PRINT TAB 16; CHR$ o:GO TO 35 30 PRINT TAB 16;"(UNPRINTABLE)" 35 LET p=p+1:IF p=v THEN STOP 40 GO TO 20 9000 SAVE "READER 1" LINE 0 ```