--- title: "Headline" id: 64591 type: "computer_media" slug: "headline" url: "http://localhost/computer_media/headline/" markdown_url: "http://localhost/computer_media/headline.md" published_at: "2026-03-03T01:12:39+00:00" modified_at: "2026-03-31T09:16:25+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A banner printer that reads UDG pixel data directly from memory, transposes the 8×8 bitmap, and expands every pixel into a chunky 4×3 block character on the 2040 printer." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Banner" slug: "banner" taxonomy: "genre" url: "http://localhost/type/banner/" - name: "Graphics" slug: "graphics" taxonomy: "genre" url: "http://localhost/type/graphics/" media_contents: - id: 64513 title: "CATS Library Tape 2" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-2/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Headline%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2023/06/11-Headline.png" media_type_tags: "Banner, Graphics" --- # Headline This program prints large “banner” text on a 2040 printer by reading the pixel data stored in User Defined Graphic (UDG) character shapes. It accepts a message string, validates that every character is either a space or a UDG (letters “a” through “u”), then for each character it PEEKs the 8 bytes of bitmap data from the UDG address returned by USR. The 8×8 pixel grid is transposed — columns become rows — and each pixel is expanded to a 4-character-wide block (using Spectrum block graphic █), with each expanded row sent to the printer three times via LPRINT to also triple the height. Spaces in the input cause the current screen buffer to be printed with COPY before continuing. *** ## Program Structure The program divides into four logical phases: 1. **Introduction (lines 1–4):** Displays a description and waits for any keypress before clearing the screen. 2. **Setup & validation (lines 150–190):** Builds a powers-of-two lookup array `a()` and inputs the message string, checking each character is either a space or a valid UDG. 3. **Bitmap extraction & transposition (lines 200–410):** For each character, PEEKs its 8-byte UDG definition, converts each byte to an 8-element binary string row in array `b$()`, then transposes the grid into array `e$()` so that columns become rows. 4. **Output (lines 450–500):** Expands each bit of the transposed grid to a 4-space or 4-█ run and sends three copies of each row to the printer with `LPRINT`, producing a tall blocky glyph. ## UDG Bitmap Decoding Line 150 initialises `a(1..8)` with the values 128, 64, 32, 16, 8, 4, 2, 1 — the eight bit-weights of a byte, MSB first. For each character, line 260 uses `PEEK (y+i)` (where `y = USR a$(p)`) to read one row of the UDG bitmap. The inner loop at lines 270–290 performs a simple greedy binary decomposition: if the remaining value `x` is ≥ the current weight, that bit is set to `"1"` and the weight is subtracted. This reconstructs the 8-bit pattern as a character string row in `b$(i+1)`. ## Transposition Technique After filling the 8×8 string array `b$()` (rows = scan lines, columns = bits), lines 340–410 transpose it into `e$()` so that bit-column `k` of the original becomes scan-line `i` of the output. This rotates the glyph 90°, which — combined with reading rows in reverse order at line 380 (`FOR n=8 TO 1 STEP -1`) — effectively mirrors and rotates the image. The net result is that the glyph is printed in a portrait orientation suitable for a banner where characters are rendered tall. ## Banner Expansion Line 470 uses the Spectrum BASIC string-multiplication idiom with `AND`: - `(" " AND e$(i,n)="0")` — four spaces for an unset pixel. - `("████" AND e$(i,n)="1")` — four solid-block characters (█, chr 143) for a set pixel. This expands each pixel to 4 characters wide. Line 480 then prints the resulting 32-character string *three times* on a single `LPRINT` line (`c$'c$'c$`, where `'` is the comma separator in Spectrum BASIC’s LPRINT), tripling the printed height as well. The combined 4-wide × 3-tall expansion gives chunky, readable banner characters. ## Input Validation Line 180 contains a compound validity check using `CODE a$(s)`. The intended logic is to reject anything that is not a UDG character (codes 144–164, corresponding to UDGs \a–\u). However, the Boolean operator precedence in Spectrum BASIC causes the condition to be evaluated differently from what a straightforward reading suggests. The four sub-conditions connected by `OR` and `AND` are: | Sub-condition | Intended meaning | | --- | --- | | `CODE a$(s)<65` | Below “A” (not alpha at all) | | `CODE a$(s)<97 AND CODE a$(s)>85` | Between “Z”+1 and “a”-1 (non-letter gap) | | `CODE a$(s)>117 AND CODE a$(s)<144` | Between “v” and first UDG | | `CODE a$(s)>164` | Above last UDG \u | Due to `AND` binding more tightly than `OR`, the check does function broadly correctly for the ASCII printable range, though it accepts uppercase letters A–U (codes 65–85) as if they were valid, since those ranges are not blocked. In practice this only matters if the UDG table has not been populated for uppercase equivalents. ## Space Handling A space character in the input message triggers a different code path: line 210 issues `CLS : COPY` to print the current screen contents before jumping to `NEXT p` via `GO TO 490`. This uses the screen as a temporary buffer for COPY-based printing, rather than building the space character as a blank glyph. ## Notable BASIC Idioms - `USR a$(p)` — returns the address of the UDG bitmap for character `a$(p)`, used directly as a PEEK base address. - String array slice assignment: `LET b$(i+1)="00000000"` initialises an entire 8-character row of a 2D string array in one statement. - The `AND` conditional string trick on line 470 is a classic Spectrum idiom that returns the left-hand string if the condition is true (numeric 1), or an empty string if false (numeric 0). - `BEEP 1,-20` on the error path produces a low-pitched warning tone; `BEEP 1,20` at line 500 signals successful completion. ## Source Code ``` 1 PRINT "This is a ""banner"" program for the 2040 printer. It makes niceblocky letters. However, it will only work with the letters ""a"" thru ""u""." 2 PRINT : PRINT "Press any key to begin......" 3 IF INKEY$="" THEN GO TO 3 4 CLS 150 DIM a(8): LET a(1)=128: LET a(2)=64: LET a(3)=32: LET a(4)=16: LET a(5)=8: LET a(6)=4: LET a(7)=2: LET a(8)=1 160 INPUT "Enter message > ";a$ 170 FOR s=1 TO LEN a$: IF a$(s)=" " THEN GO TO 190 180 IF CODE a$(s)<65 OR CODE a$(s)<97 AND CODE a$(s)>85 OR CODE a$(s)>117 AND CODE a$(s)<144 OR CODE a$(s)>164 THEN PRINT a$(s);" isn't a user graphic character": BEEP 1,-20: STOP 190 NEXT s 200 FOR p=1 TO LEN a$ 210 IF a$(p)=" " THEN CLS : COPY : GO TO 490 220 LET y=USR a$(p) 230 DIM b$(8,8) 240 FOR i=0 TO 7 250 LET b$(i+1)="00000000" 260 LET x=PEEK (y+i) 270 FOR n=1 TO 8 280 IF x>=a(n) THEN LET b$(i+1,n)="1": LET x=x-a(n) 290 NEXT n 300 NEXT i 340 DIM e$(8,8) 350 LET k=1 360 FOR i=1 TO 8 370 LET f$="" 380 FOR n=8 TO 1 STEP -1 390 LET f$=f$+b$(n,k): NEXT n 400 LET e$(i)=f$ 410 LET k=k+1: NEXT i 450 FOR i=1 TO 8 460 LET c$="": FOR n=1 TO 8 470 LET c$=c$+(" " AND e$(i,n)="0")+("████" AND e$(i,n)="1") 480 NEXT n: LPRINT c$'c$'c$: NEXT i 490 NEXT p 500 BEEP 1,20: STOP ```