This program prints large banner-style text on the ZX printer, rendering each input character approximately three inches tall using a dot-matrix expansion technique. It reads the character’s 8×8 pixel definition directly from the system font in ROM by PEEKing memory starting at address 7680, then unpacks each row’s bits by repeated division by 2 and checking for non-integer remainders. Each character column is stored in the 64-byte string D$, and once 8 columns are accumulated the GOSUB 900 routine prints them transposed, so the characters appear rotated 90°, with each pixel rendered as four spaces or four filled characters to achieve the large size. Inverse-video input characters (CODE > 63) are handled by subtracting 128 to get the base character code while inverting the fill pattern, and the output is doubled vertically by the inner loop at line 910 iterating twice.
Program Analysis
Program Structure
The program is divided into three logical phases:
- Initialisation / display (lines 1010–1090): Shows a title screen and instructions, then falls through to the INPUT at line 20.
- Bit-extraction loop (lines 10–160): Iterates over every character of the input string, peeks its ROM font data, unpacks the bits, and stores them in the work buffer
D$. - Print subroutine (lines 900–980): Called once every 8 characters (when the 64-byte buffer is full), it prints the buffered columns on the ZX printer in transposed, double-height form.
Font Data Access via PEEK
Character pixel data is read directly from the system character set. The expression PEEK (7680 + N + 8 * CODE A$) at line 70 computes the address of the Nth byte of character A$‘s 8-byte bitmap. Address 7680 (0x1E00) is the start of the ZX81 character set in ROM. This approach requires no separate font table in RAM and keeps the program compact.
Bit-Unpacking Technique
Rather than using bitwise AND, the program extracts individual bits through repeated halving. At line 90, Z is divided by 2; if the result differs from its integer part (line 100) the least-significant bit was 1, so the pixel is “on”. Z is then floored at line 130 before the next iteration. This is a classic ZX81 idiom used because the platform lacks a native bitwise AND operator in BASIC.
Buffer and Transposition
D$ is dimensioned to 64 characters (line 10), holding exactly 8 characters × 8 rows of pixel data. Pixels are stored row-major (index N*8+X) but printed column-major in the subroutine: the outer loop of GOSUB 900 iterates P over columns 8 down to 1, and the inner loop E iterates over rows 7 down to 0, accessing D$(E*8+P). This 90° transposition turns horizontally-stored font rows into vertically-printed banner columns.
Double-Height Output
The loop FOR F=1 TO 2 at line 910 repeats each column twice, doubling the vertical size of the printed output and contributing to the “3 inches high” appearance described in the on-screen instructions.
Inverse-Video Character Handling
Characters with CODE > 63 are inverse-video variants. Line 60 maps them back to the base character by subtracting 128 before the PEEK lookup. Lines 115 and 505 then swap the fill values (“0” and “1”) so that inverse characters appear with foreground and background swapped in the printed banner.
Print Encoding
In the subroutine, a “set” pixel is printed as "% % % % " (four alternating percent signs and spaces, 8 characters wide) and a clear pixel as " " (four spaces). This produces a visually bold block appearance on the thermal ZX printer.
Variable Summary
| Variable | Role |
|---|---|
M$ | User-entered input string |
D$ | 64-byte pixel buffer (8 chars × 8 rows) |
C | Character index within M$ |
A$ | Current character (base form, inverse stripped) |
N | Row index within font byte (0–7) |
X | Bit index within font byte (1–8) |
Z | Current font byte, shifted right each iteration |
P | Column index in print subroutine (1–8) |
F | Doubling loop counter (1–2) for height |
E | Row index in print subroutine (0–7) |
Notable Anomalies
- The buffer flush at
GOSUB 900is triggered only whenN*8+X=64(line 120), meaning it fires once per 8 characters. If the input string length is not a multiple of 8, the final partial buffer is never printed — the last up-to-7 characters are silently lost. - The
FAST/SLOWpair (lines 30 and 170) suppresses display refresh during the compute-intensive bit-extraction loop, which is good practice on this platform given the cost of PEEK and floating-point division. - Line 1000 contains a
SAVEcommand that is never reached in normal execution (the flow goes 160→170→180→1010, bypassing line 1000), so it serves purely as a storage line embedded in the listing.
Content
Source Code
1 REM *** BANNERS ***
2 REM ***********************
10 DIM D$(64)
20 INPUT M$
30 FAST
40 FOR C=1 TO LEN M$
50 LET A$=M$(C)
60 IF CODE A$>63 THEN LET A$=CHR$ (CODE M$(C)-128)
65 FOR N=0 TO 7
70 LET Z=PEEK (7680+N+8*CODE A$)
80 FOR X=1 TO 8
90 LET Z=Z/2
100 IF Z<>INT Z THEN GOTO 500
110 LET D$(N*8+X)="0"
115 IF CODE M$(C)>63 THEN LET D$(N*8+X)="1"
120 IF N*8+X=64 THEN GOSUB 900
130 LET Z=INT Z
140 NEXT X
150 NEXT N
160 NEXT C
170 SLOW
180 GOTO 1010
500 LET D$(N*8+X)="1"
505 IF CODE M$(C)>63 THEN LET D$(N*8+X)="0"
510 GOTO 120
900 FOR P=8 TO 1 STEP -1
910 FOR F=1 TO 2
920 FOR E=7 TO 0 STEP -1
930 IF D$(E*8+P)="1" THEN LPRINT "% % % % ";
940 IF D$(E*8+P)="0" THEN LPRINT " ";
950 NEXT E
960 NEXT F
970 NEXT P
980 RETURN
\n1000 SAVE "1007%3"
\n1010 CLS
\n1020 PRINT TAB 8;"%Z%X% %B%A%N%N%E%R%S"
\n1030 PRINT TAB 8;"**********"
\n1040 PRINT ,,;"THIS PROGRAM WILL PRINT ON THE ZX PRINTER LETTERS AND SYMBOLS 3"" HIGH IN THE FORM OF BANNERS"
\n1050 PRINT ,,;"THE LENGTH OF THE BANNER IS UP TO YOU. ENTER BELOW AND PRESS ENTER"
\n1060 PRINT ,,;"LETTERS AND GRAPHICS WILL WORK FUNCTIONS WILL NOT"
\n1090 GOTO 10
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
