--- title: "Paint Brush" id: 57571 type: "computer_media" slug: "paint-brush" url: "http://localhost/computer_media/paint-brush/" markdown_url: "http://localhost/computer_media/paint-brush.md" published_at: "2024-10-05T22:13:02+00:00" modified_at: "2026-03-30T21:17:15+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/paint-brush.png" excerpt: "A keyboard-driven RAM editor that pokes a cursor character around the display file and lets you save your work — all in 16 lines of BASIC." 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: "Graphics" slug: "graphics" taxonomy: "genre" url: "http://localhost/type/graphics/" media_contents: - id: 56721 title: "Synchro-Sette April 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-april-1983/" media_type: "Program" mediadate: "April 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/paint-brush.png" media_type_tags: "Graphics, Software" --- # Paint Brush This program implements a simple memory editor (POKE tool) that lets the user navigate through RAM and modify individual bytes at addresses relative to the display file. It fills the screen with dots (704 characters via a FOR loop), then enters a loop that reads a byte at offset A from the start of the display file—calculated using PEEK 16396 and PEEK 16397 to find the display file base address—writing zero to erase the previous cursor position before writing the current character. Navigation is handled by single-key input: A/D move by 1 byte, Q/W/E and Z/X/C move by 32, 33, or 34 bytes respectively, S prompts for a new character to write, and K prompts for a filename and SAVEs the program. The cursor character is defined by A$, initialised to the ZX81 block graphic “@” (a checkerboard-style character). *** ## Program Analysis ### Program Structure The program has three distinct phases: 1. **Initialisation (lines 1–5):** Sets the byte offset `A` to 210, sets the cursor character `A$` to a block graphic, then fills the screen with 704 dot characters. 2. **Display loop (lines 6–8):** Erases the previous cursor position with a zero byte, writes the cursor character, then busy-waits until a key is pressed. 3. **Input handling (lines 9–16):** Decodes the keypress, adjusts the offset `A`, optionally prompts for a new character or filename, and loops back. ### Address Calculation The display file base address is obtained at runtime using the system variables at addresses 16396 (low byte) and 16397 (high byte):`PEEK 16396 + PEEK 16397 * 256 + A` This is a standard ZX81 idiom for reading the D_FILE system variable, making the POKE address relocatable regardless of where the display file happens to sit in RAM. Offset `A` is then added to reach the desired byte within the display area. ### Key Mapping | Key | Effect on A | Notes | | --- | --- | --- | | `D` | +1 | Move right one byte | | `A` | −1 | Move left one byte | | `Z` | +32 | Move down one display row | | `E` | −32 | Move up one display row | | `X` | +33 | Move diagonally down-right | | `W` | −33 | Move diagonally up-left | | `C` | +34 | Move diagonally down-right (wider) | | `Q` | −34 | Move diagonally up-left (wider) | | `S` | no change | INPUT new cursor/stamp character into `A$` | | `K` | no change | INPUT filename `Z$`, then SAVE | ### Notable Techniques - **Boolean arithmetic for navigation (line 10):** The expression `(1 AND B$="D")` evaluates to 1 if the condition is true and 0 otherwise on the ZX81, allowing all eight movement directions to be collapsed into a single `LET` statement without any `IF` branching. - **Cursor rendering via POKE (lines 6–7):** Rather than using `PRINT AT`, the program writes directly to the display file with `POKE`. Line 6 writes zero (blank) to erase the old cursor; line 7 writes `CODE A$` (the character code of the first character of `A$`) to stamp the new cursor position. - **Busy-wait keypress detection (line 8):** The loop `IF INKEY$="" THEN GOTO 6` continuously refreshes the cursor display while no key is held, combining animation with input polling. - **704-character fill (lines 3–5):** The ZX81 display file for a 32-column screen consists of 24 rows of 32 characters plus 24 newline bytes (HALT opcodes), totalling 792 bytes. Printing 704 characters (22 full rows) initialises most of the visible screen area with dots, giving the editor a uniform canvas. - **Dual-use of A$ (line 11):**`A$` serves both as the cursor marker and as the stamp character: pressing S lets the user change it, effectively turning the cursor into a paint tool that writes arbitrary characters to display file addresses. ## Source Code ``` 1 LET A=210 2 LET A$="\@@" 3 FOR I=1 TO 704 4 PRINT "."; 5 NEXT I 6 POKE PEEK 16396+PEEK 16397*256+A,0 7 POKE PEEK 16396+PEEK 16397*256+A,CODE A$ 8 IF INKEY$="" THEN GOTO 6 9 LET B$=INKEY$ 10 LET A=A+(1 AND B$="D")+(32 AND B$="Z")+(33 AND B$="X")+(34 AND B$="C")-(1 AND B$="A")-(32 AND B$="E")-(33 AND B$="W")-(34 AND B$="Q") 11 IF B$="S" THEN INPUT A$ 12 IF B$="K" THEN GOTO 14 13 GOTO 6 14 INPUT Z$ 15 SAVE Z$ 16 GOTO 6 ```