--- title: "Peeker" id: 57156 type: "computer_media" slug: "peeker-2" url: "http://localhost/computer_media/peeker-2/" markdown_url: "http://localhost/computer_media/peeker-2.md" published_at: "2024-10-04T01:00:16+00:00" modified_at: "2026-03-30T21:19:23+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/105_Pk.png" excerpt: "A continuous hex memory-dump tool that converts raw addresses and byte values into readable hexadecimal, pausing on a keypress and scrolling indefinitely." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 56733 title: "Timex Sinclair Public Domain Library Tape 1002" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1002/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/105_Pk.png" media_type_tags: "Programming, Utility" --- # Peeker This program is a scrolling memory dump utility that continuously reads bytes from consecutive memory addresses and displays them in hexadecimal. Starting from a user-supplied address, it decodes each 16-bit address and the byte stored there into four-digit hex notation by repeatedly dividing by powers of 16, using CHR$(28+n) to map nibble values 0–15 to the ZX81 hex character set (characters 28–43 correspond to ‘0’–’9′ and ‘A’–’F’). Each line shows the address in hex, a colon separator, and the byte value in two hex digits, with TAB 31 padding the line to force a new row. The loop increments the address by one on each iteration and halts scrolling only while a key is held, then resumes automatically. *** ## Program Analysis ### Program Structure The program is a simple loop with four logical phases: 1. **Initialisation (line 10):** prompts the user for a starting address. 2. **Address decomposition (lines 21–28):** breaks the 16-bit address into four hex nibbles. 3. **Display (lines 29–40):** prints the address and byte value in hex, then advances to the next address. 4. **Flow control (lines 50–60):** waits while any key is held, then jumps back to scroll and repeat. ### Hex Encoding via CHR$(28+n) The ZX81 character set places the digits `'0'`–`'9'` at positions 28–37 and the letters `'A'`–`'F'` at positions 38–43. By computing a nibble value `n` (0–15) and printing `CHR$(28+n)`, the program neatly maps any nibble to its correct hex character without needing a lookup string or conditional branching. This is a compact and well-known ZX81 idiom. ### Address Nibble Decomposition Lines 21–25 manually decompose the 16-bit address `X` into four nibbles using successive integer division and modulo arithmetic: | Variable | Represents | Calculation | | --- | --- | --- | | `H1` | Most significant nibble | `INT(X/4096)` | | `H2` | Second nibble | `INT((X − H1×4096)/256)` | | `H3` | Third nibble | `INT(Y/16)` where `Y = X − H1×4096 − H2×256` | | `H4` | Least significant nibble | `Y − 16×H3` | The combined hex address string is assembled in line 29 and stored in `A$`. ### Byte Value Display Line 30 reads the byte at address `X` using `PEEK X` into variable `A`. Line 40 then prints the pre-built address string, a colon separator, and the two hex digits of `A` inline, again using the `CHR$(28+n)` technique. The `TAB 31` at the end of the PRINT statement forces the cursor past column 31, causing the ZX81’s 32-column display to wrap and begin a new screen line, effectively terminating the output row without a newline token. ### Scroll and Keypress Pause Line 20 issues a `SCROLL` command before each new line is printed, keeping the display continuously rolling. Lines 50–60 form a busy-wait pause: `IF INKEY$<>""` loops back to itself for as long as any key is depressed, freezing the address increment. Once the key is released the program falls through to `GOTO 20` and resumes scrolling. This is the inverse of the common PAUSE 0 / INKEY$ idiom — here a key being *held* pauses execution rather than a key press advancing it. ### Notable Techniques and Observations - No machine code is used; all arithmetic is done in BASIC floating-point, which is adequate for a display-rate memory browser. - The address string `A$` is built once per iteration (line 29) and reused in the PRINT (line 40), avoiding redundant recalculation. - There is no upper-bound check on `X`; incrementing past 65535 will cause BASIC to attempt a PEEK of an out-of-range address and produce an error. - Lines 70 (`SAVE`) and 80 (`RUN`) are never reached during normal execution; they exist solely as a program header/auto-start mechanism on tape. ## Source Code ``` 10 INPUT X 20 SCROLL 21 LET H1=INT (X/4096) 22 LET H2=INT ((X-H1*4096)/256) 23 LET Y=X-H1*4096-H2*256 24 LET H3=INT (Y/16) 25 LET H4=Y-16*H3 29 LET A$=CHR$ (28+H1)+CHR$ (28+H2)+CHR$ (28+H3)+CHR$ (28+H4) 30 LET A=PEEK X 40 PRINT A$;" : ";CHR$ (28+INT (A/16));CHR$ (28+A-(INT (A/16)*16));TAB 31 45 LET X=X+1 50 IF INKEY$<>"" THEN GOTO 50 60 GOTO 20 70 SAVE "1010%5" 80 RUN ```