Banner

Developer(s): Ryan Gray
Date: 1984
Type: Program
Platform(s): TS 2068

This program generates large-format banner text on a printer by reading character bitmap data directly from the ROM character set. It accepts a message string, a width multiplier (number of times each row is repeated), and a height multiplier (1–4, repeating each pixel row), then decodes each character’s 8×8 pixel matrix by extracting individual bits from ROM bytes. Each decoded pixel is stored in an 8×8 string array and output via LPRINT, allowing arbitrarily wide banner lettering. The ROM character set base address is dynamically located using the system variable at addresses 23606–23607 (CHARS), making the code portable across different memory configurations.


Program Structure

The program is a continuous loop anchored by a GO TO 20 at line 100, prompting for a new message after each print job. The main processing divides into three phases:

  1. Setup (lines 10–25): Clear screen, accept input, and locate the ROM character set.
  2. Bit decoding (lines 30–60): For each character in the message, decode its 8×8 ROM bitmap into string array a$.
  3. Output (lines 70–90): Print each character column-by-column with width and height repetition via LPRINT.

ROM Font Access via CHARS System Variable

Line 25 reads the two-byte system variable CHARS from addresses 23606–23607:

LET s=(PEEK 23607+1)*256+PEEK 23606

The CHARS variable normally holds a value 256 bytes below the start of the font data (so that adding CODE " " * 8 = 0 gives the space character’s address). The program compensates by adding 1 to the high byte, effectively pointing s directly at the font bitmap for character 32 (space). This is a clean and portable technique that works even if the font has been relocated.

Bit-Plane Decoding (Lines 40–57)

For each character m$(x), the program fetches 8 bytes from ROM (one per pixel row) and manually unpacks each byte into individual bits using successive threshold subtraction — a common BASIC technique when bitwise AND is unavailable or less readable:

LineBit weightArray column
50128 (bit 7)a$(1, 8-z)
5164 (bit 6)a$(2, 8-z)
5232 (bit 5)a$(3, 8-z)
5316 (bit 4)a$(4, 8-z)
548 (bit 3)a$(5, 8-z)
554 (bit 2)a$(6, 8-z)
562 (bit 1)a$(7, 8-z)
571 (bit 0)a$(8, 8-z)

The index 8-z where z runs 0–7 transposes the bitmap so that rows become columns in a$, effectively rotating the character 90°. This means the outer print loop (line 70) iterates over z=1 TO 8 as vertical scan lines of the original character.

Output Loop and Scaling (Lines 70–80)

Three nested loops control output:

  • z (1–8): iterates over the 8 vertical pixel columns of the stored bitmap.
  • w (1–f1): repeats each column f1 times, controlling the printed width of the character.
  • y (1–8): iterates over the 8 horizontal pixel rows.
  • e (1–f2): repeats each pixel f2 times vertically within a row.

Each pixel is printed as a block graphic character (full block, character 255) or left as a space (the default initialized value of the string array). An LPRINT with no argument at line 80 sends a newline after each row.

Notable Techniques and Idioms

  • DIM inside the loop (line 35): DIM a$(8,8) is re-executed for every character, which re-initializes the array to spaces. This is an intentional reset mechanism, though it incurs overhead from repeated memory allocation.
  • Bit extraction by subtraction: Rather than using INT(q/128) or q AND 128, the program tests each threshold once and subtracts, which is correct and readable but slightly verbose across 8 lines.
  • No screen preview: Output goes exclusively to the printer via LPRINT; there is no on-screen representation of the banner.

Bugs and Anomalies

  • Bit 0 threshold (line 57): The condition IF q>0 is used for the least-significant bit instead of IF q>0 (equivalent to q=1 at this point). Since all higher bits have been stripped, this is functionally correct.
  • No upper bound on f1: The prompt says “1 TO ?” with no enforced maximum, so very large values will cause extremely long print jobs without warning.
  • No input validation: Non-printable or high-code characters in m$ could cause a PEEK outside the ROM font region, potentially reading garbage data or causing an error.
  • Transposition side-effect: The 90° rotation means each character is printed rotated; however, because the printer outputs rows left-to-right and the loop structure compensates, the final result appears correct as large upright text on the printout.

Content

Appears On

This tape is a compilation of programs from user group members (Robert Burton, David Baulch, Frank Bouldin, Chuck Dawson, Ryan

Related Products

Related Articles

Related Content

Image Gallery

Banner

Source Code

    1 REM   BANNER GENERATOR
    2 REM   © by Ryan Gray, 11/22/84 ALL RIGHTS RESERVED
   10 CLS : PRINT "BANNER GENERATOR:               ________________________________"
   20 INPUT "MESSAGE:";m$
   21 INPUT "WIDTH (1 TO ?):";f1: INPUT "HEIGHT (1 TO 4):";f2
   25 LET s=(PEEK 23607+1)*256+PEEK 23606
   30 FOR x=1 TO LEN m$
   35 DIM a$(8,8)
   40 FOR z=0 TO 7: LET q=PEEK (s+z+8*(CODE m$(x)-32))
   50 IF q>127 THEN LET a$(1,8-z)="█": LET q=q-128
   51 IF q>63 THEN LET a$(2,8-z)="█": LET q=q-64
   52 IF q>31 THEN LET a$(3,8-z)="█": LET q=q-32
   53 IF q>15 THEN LET a$(4,8-z)="█": LET q=q-16
   54 IF q>7 THEN LET a$(5,8-z)="█": LET q=q-8
   55 IF q>3 THEN LET a$(6,8-z)="█": LET q=q-4
   56 IF q>1 THEN LET a$(7,8-z)="█": LET q=q-2
   57 IF q>0 THEN LET a$(8,8-z)="█"
   60 NEXT z
   70 FOR z=1 TO 8: FOR w=1 TO f1: FOR y=1 TO 8
   75 FOR e=1 TO f2: LPRINT a$(z,y);: NEXT e: NEXT y
   80 LPRINT : NEXT w: NEXT z
   90 NEXT x
  100 GO TO 20

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top