This program implements a word-by-word scrolling marquee display on the ZX81/TS1000, presenting each word of a user-entered message in large pixel-doubled characters built from the ROM character set. The program reads the character font directly from ROM at address 7680 (the ZX81 character set base), unpacking each 8×8 bitmap bit-by-bit using PEEK and plotting individual pixels via UNPLOT on the lo-res display. Words are extracted one at a time using a space-scanning subroutine at line 1000, and after each word the screen scrolls up by a configurable number of lines (stored in G). When the last word is reached, the subroutine at line 2000 resets S=1 so the message loops continuously and sets G=21 to clear the full screen before restarting. Inverse video characters in Z$ (a string of spaces) are used as a solid fill line to create a clean background during scrolling.
Program Analysis
Program Structure
The program is organised into a main flow and three subroutines:
- Initialisation (lines 10–80): Sets scroll gap
G=4, word positionS=1, builds a fill stringZ$of inverse-video spaces, prompts for input, and appends two trailing spaces toB$to ensure the last word is correctly terminated. - Main display loop (lines 100–360): Clears the screen (GOSUB 3000), extracts a word (GOSUB 1000), scrolls
Glines, then renders the word pixel-by-pixel using ROM font data before looping back to line 130. - Subroutine 1000 – Word extractor: Scans
B$from positionSfor the next space, slices out the word intoA$, and advancesS. - Subroutine 2000 – End-of-message detector: Checks whether the current word index
Nis near the end ofB$; if so, resetsS=1and setsG=21to do a full-screen clear on the next cycle. - Subroutine 3000 – Screen clear: POKEs the display file pointer and scrolls 21 lines of
Z$to fill the screen with inverse-video space.
ROM Font Access
The character bitmaps are read directly from ROM starting at address 7680, which is the base of the ZX81 character set. Each character occupies 8 consecutive bytes, one per pixel row. The formula used is:
PEEK (7680 + C*8 + L) where C is the character code and L is the row (0–7).
For characters with codes ≥128 (inverse video), lines 220–240 subtract 128 and set C=0, mapping them to the space character—a limitation meaning inverse characters in the input are rendered as blank columns rather than their true glyphs.
Bit-Unpacking Technique
Each font byte is decoded by successively subtracting powers of two. The variable V starts at 128 and is halved each iteration. If the current byte value P is ≥ V, bit 7-J is set: UNPLOT is called to draw the pixel and V is subtracted from P. This is a standard shift-and-mask idiom for BASIC environments lacking bitwise operators.
Pixel Placement with UNPLOT
UNPLOT on the ZX81 sets a pixel in the lo-res 64×44 graphics grid. Characters are placed at horizontal position 8*(K-1)+J and vertical position 10-L, giving each character an 8-pixel-wide column and rendering rows top-to-bottom by inverting L. This effectively doubles the visual size of each character relative to normal text, since the lo-res grid maps roughly 2 PLOT units per character cell row.
Scrolling Mechanism
The ZX81 SCROLL keyword moves the display up one line. The loop at lines 170–190 scrolls G lines and prints Z$ (a row of inverse spaces) after each scroll to maintain a solid background. Normally G=4 (scrolling 4 lines between words); at message wrap it becomes G=21 to blank the full screen before restarting.
Screen Initialisation via POKE
Line 3000 contains POKE 16418,2. Address 16418 is the system variable DF_SZ (display file size / lower screen lines). Setting it to 2 minimises the lower screen area, maximising the main display—a common ZX81 technique to reclaim display rows.
Variable Summary
| Variable | Role |
|---|---|
S | Current start position within B$ for word scanning |
G | Number of scroll lines between words (4 normal, 21 at wrap) |
Z$ | A 64-character string of inverse spaces used as fill line |
B$ | User-entered message (with two trailing spaces appended) |
A$ | Current extracted word being displayed |
C | Character code of current character in word |
M$ | Temporary holder when stripping inverse-video flag (≥128) |
P | Current font byte being unpacked |
V | Bit-mask value (128, 64, 32, … 1) |
I,J,K,L,N,Z | Loop counters |
Bugs and Anomalies
- The word-extractor at line 1020 (
NEXT N) will run past the end ofB$if no space is found beforeLEN B$, potentially causing a subscript error. The two appended spaces in line 80 mitigate this for most inputs but do not fully protect against it. M$at line 230 is assigned but never used; the intent appears to have been to render the underlying non-inverse character, butCis set to 0 instead, so all inverse characters silently become blank columns.- Subroutine 2000 compares
NagainstLEN B$-1. BecauseB$has two trailing spaces, the boundary check fires one word early, causing the last actual word to trigger the reset condition before it has fully rendered. - The pixel row rendering uses
UNPLOT(which clears a pixel on ZX81), rather thanPLOTwhich would set it. This means the program draws its characters in blank pixels against the inverse-space background—a deliberate inverse-video display effect consistent with the program’s name and the use ofZ$.
Content
Source Code
10 LET S=1
20 LET G=4
30 LET Z$="% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
60 PRINT AT 10,0;"TYPE IN A MESSAGE."
70 INPUT B$
80 LET B$=B$+" "
100 GOSUB 3000
130 GOSUB 1000
170 FOR I=1 TO G
180 SCROLL
185 PRINT Z$
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<V THEN GOTO 320
300 UNPLOT 8*(K-1)+J,10-L
305 GOSUB 2000
310 LET P=P-V
320 LET V=V/2
330 NEXT J
340 NEXT L
350 NEXT K
360 GOTO 130
1000 FOR N=S TO LEN B$
1010 IF B$(N)=" " THEN GOTO 1050
1020 NEXT N
1050 LET A$=B$(S TO N-1)
1060 LET S=N+1
1070 RETURN
2000 IF N<LEN B$-1 THEN LET G=4
2010 IF N>=LEN B$-1 THEN LET S=1
2020 IF N>=LEN B$-1 THEN LET G=21
2030 RETURN
3000 POKE 16418,2
3010 FOR Z=1 TO 21
3020 SCROLL
3030 PRINT Z$
3040 NEXT Z
3050 RETURN
9998 SAVE "INVERSCROL%L"
9999 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
