--- title: "Super Scroll" id: 57599 type: "computer_media" slug: "super-scroll" url: "http://localhost/computer_media/super-scroll/" markdown_url: "http://localhost/computer_media/super-scroll.md" published_at: "2024-10-05T22:57:07+00:00" modified_at: "2026-04-03T07:58:17+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/superscroll.png" excerpt: "A user-typed message is rendered word by word in large pixel art letters by reading the character ROM directly, then smoothly scrolled in a continuous loop." 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: "Home" slug: "home" taxonomy: "genre" url: "http://localhost/type/home/" media_contents: - id: 56723 title: "Synchro-Sette February 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-february-1983/" media_type: "Program" mediadate: "February 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/superscroll.png" media_type_tags: "Home" --- This program implements a scrolling word-display effect that renders user-input text one word at a time using pixel-level character plotting. It prompts the user for a message, then extracts words sequentially via a subroutine at line 1000, rendering each word by PEEKing the ZX81 character ROM at address 7680 to read 8×8 font bitmaps and plotting individual pixels. After displaying each word, the screen scrolls up by G lines (normally 4, but 21 at the end of the message to clear the screen), then loops back to the start of the message. The program handles inverse-video characters by subtracting 128 from their character code before the ROM lookup, storing the stripped character in M$ (though M$ is set but never used to adjust the PLOT call, which is a bug). *** ## Program Analysis ### Program Structure The program is divided into a main loop and two subroutines. Initialisation sets the word-start pointer `S=1` and the scroll amount `G=4`. After prompting for input and appending two trailing spaces to `B$` (line 80), the program enters a perpetual loop: call the word-extraction subroutine, scroll the screen, plot the word pixel by pixel, then repeat. 1. **Lines 10–90** – Initialisation and user prompt. 2. **Lines 130–360** – Main display loop: extract word, scroll, plot pixels, repeat. 3. **Lines 1000–1070** – Word extraction subroutine: scans `B$` from position `S` to the next space, returning the word in `A$`. 4. **Lines 2000–2030** – End-of-message subroutine: resets `S` and adjusts scroll count when the last word is reached. 5. **Line 9998–9999** – SAVE with auto-run flag, then `RUN`. ### Character ROM Access The pixel rendering (lines 200–350) reads font data directly from ROM. The ZX81 character set begins at address 7680, with each character occupying 8 consecutive bytes (one per pixel row). Line 260 computes the address as `7680 + C*8 + L`, where `C` is the character code and `L` is the row (0–7). Each byte is then bit-shifted by successive halving of `V` (starting at 128) to isolate individual pixels, which are plotted at `PLOT 8*(K-1)+J, 10-L`. ### Word Extraction (Subroutine 1000) The subroutine scans `B$` from the current word pointer `S` until it finds a space character, storing the word slice in `A$` and advancing `S` past the delimiter. The two trailing spaces appended at line 80 ensure the final word is always followed by a space, preventing an out-of-bounds substring error. ### End-of-Message Handling (Subroutine 2000) Called once per pixel plot (line 305), this subroutine checks whether the current word index `N` has reached the end of `B$`. When it has, it resets `S=1` (restarting from the first word on the next cycle) and sets `G=21` to scroll the display clear before the loop begins again. For all other words, `G` is kept at 4. ### Notable Techniques - **Bitmap font decoding:** The successive-halving loop (`V=128` → `V=V/2`) is a common ZX81 idiom for extracting bits without using AND or integer arithmetic beyond subtraction. - **Trailing-space sentinel:** Appending `" "` to `B$` at line 80 is a neat boundary-condition fix for the word-scanner loop. - **Dynamic scroll depth:** Varying `G` between 4 and 21 allows normal inter-word scrolling while still clearing the screen between complete message cycles. - **Inverse-video stripping:** Line 220 detects codes ≥128 (inverse characters) and subtracts 128 to get the normal code for the ROM lookup. ### Bugs and Anomalies | Location | Issue | | --- | --- | | Lines 230–240 | `M$` is assigned the base character at line 230 and `C` is set to 0, but `M$` is never used again. Setting `C=0` means that any inverse character in the input is rendered as the `@` character (code 0) rather than its correct inverse form — the intended logic to use `M$`‘s code is missing. | | Line 305 | `GOSUB 2000` is called inside three nested `FOR` loops (over characters, rows, and columns), so the end-of-message check fires up to 64 times per character. This is harmless since the assignments are idempotent, but it is inefficient. | | Line 1010 | If the input message contains no spaces at all, the inner loop at line 1020 will run past the end of `B$` (the two appended spaces provide a fallback, so this is only a concern if `B$` overflows available string space). | ## Source Code ``` 10 LET S=1 20 LET G=4 60 PRINT AT 10,0;"TYPE IN A MESSAGE." 70 INPUT B$ 80 LET B$=B$+" " 90 CLS 130 GOSUB 1000 170 FOR I=1 TO G 180 SCROLL 190 NEXT I 200 FOR K=1 TO LEN A$ 210 LET C=CODE A$(K) 220 IF C<128 THEN GOTO 250 230 LET M$=CHR$ (C-128) 240 LET C=0 250 FOR L=0 TO 7 260 LET P=PEEK (7680+C*8+L) 270 LET V=128 280 FOR J=0 TO 7 290 IF P=LEN B$-1 THEN LET S=1 2020 IF N>=LEN B$-1 THEN LET G=21 2030 RETURN 9998 SAVE "SUPERSCROL%L" 9999 RUN ```