Banner Writer

Developer(s): Joseph Williamson
Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Banner

Banner Writer reads a user-supplied message and prints each character at 32 times normal size by directly reading the ZX81’s character ROM bitmap data using PEEK. For each character, it looks up the 8-byte font data starting at address 7680 plus the character code multiplied by 8, then iterates over each bit of each row to render an enlarged version on screen. The STEP value of -0.25 in the FOR loop at line 70, combined with the loop counter G running 1 to 3 in line 110, controls both the height scaling and line thickness of the output. After each character is rendered, a COPY command sends the screen contents to a ZX printer. The program runs in FAST mode to reduce rendering time, though each letter still takes approximately 35 seconds to generate.


Program Analysis

Program Structure

The program is organized into three main phases: input collection (lines 10–20), a three-level nested rendering loop (lines 50–260), and a termination step. The outermost loop (line 50) iterates over each character in the input string M$. The middle loop (line 70) iterates over the 8 bytes of font data for the character. The innermost loops (lines 90–210) expand each byte’s bits into large printed blocks.

  1. Lines 1–9: REM documentation and FAST mode setup
  2. Lines 10–20: Display prompt and collect input string M$
  3. Lines 50–60: Outer character loop; compute ROM address for each character
  4. Lines 63–67: Initialize screen position variables A (row) and B (column)
  5. Lines 70–240: Nested loops to read, decode, and print enlarged character pixels
  6. Line 250: COPY to ZX printer
  7. Lines 260–270: Advance to next character; STOP

Font Data Access via PEEK

The program reads character bitmap data directly from memory using PEEK. Line 60 computes the base address as 7680 + (CH * 8), where CH is the character code obtained from CODE M$(L). Each character occupies 8 consecutive bytes in the font table, one byte per pixel row. The FOR loop at line 70 traverses these bytes in reverse order (from R+7 down to R) using a fractional STEP of -0.25, which causes each byte address to be visited multiple times, contributing to vertical scaling.

Bit Extraction Technique

Each font byte is decoded one bit at a time using repeated subtraction and left-shifting rather than bitwise operations, which are unavailable in this BASIC dialect. Line 140 tests whether the current value of RC is 128 or greater (i.e., whether the most significant remaining bit is set). If so, line 150 sets CNTR to 128 (which corresponds to an inverse-video space character, effectively a filled block), and line 160 subtracts 128 from RC. Line 170 then doubles RC, shifting the next bit into the high position for the following iteration. This is a classic bit-serial extraction loop.

Scaling Mechanism

Horizontal scaling is achieved by the H loop (line 90, running 1 to 7), which repeats the bit-extraction and print sequence 7 times per font byte row, widening each pixel column. Vertical scaling is achieved by the fractional STEP (-0.25) in line 70, which causes each integer ROM address to be visited four times before moving to the next, effectively repeating each pixel row four times. The G loop (line 110, running 1 to 3) adds a further factor of 3 to line thickness. Together these produce the claimed 32× enlargement.

Screen Position Management

Variables A and B track the current print row and column respectively. A is incremented at line 190 after each G-loop iteration and reset to 1 at line 220 after each font byte is processed. B is incremented at line 230 to advance one column after all rows for a given font byte column are printed. This builds up the enlarged character column by column across the screen.

Notable Techniques and Anomalies

  • The condition at line 120 reads IF G=2 OR 3, which in this BASIC always evaluates as true (since the numeric value 3 is non-zero and treated as logical TRUE). The intended logic was likely IF G=2 OR G=3. As written, RC is reset to D on every pass through the G loop, meaning each of the 3 repetitions re-processes the same original byte value — this is what produces the 3× line-thickness effect, but it is a coincidental bug rather than intentional logic.
  • The fractional STEP value of -0.25 in line 70 exploits floating-point loop behavior to visit the same integer PEEK address multiple times, a memory-efficient scaling trick that avoids explicit repeat loops.
  • Using character code 0 (the inverse space, CHR$ 128 effectively as CNTR=128) to represent a filled pixel block is idiomatic for this platform’s character set.
  • The COPY command at line 250 is issued once per character rather than once for the entire message, meaning each enlarged letter is printed to the ZX printer individually before the screen is reused for the next character.
  • The variable name CNTR is reused both as a flag (set to 0 or 128) and as the argument to CHR$, doubling as a pixel color control as noted in the REM at line 3.

Variable Summary

VariableRole
M$Input message string
LIndex into M$ (character loop counter)
CHCharacter code of current letter
RROM address of current font byte (also loop variable)
RCCurrent font byte value, modified during bit extraction
DSaved copy of RC for reset in G loop
HHorizontal repeat counter (1–7)
GLine thickness repeat counter (1–3)
ACurrent print row
BCurrent print column
CNTRPixel value: 0 (space) or 128 (filled block)

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

   1 REM % %B%A%N%N%E%R% %W%R%I%T%E%R% 
   2 REM LETTERS=32X NORMAL SIZE
   3 REM LINE 130 CONTROLS BACK-FROUND; LINE 150 CONTROLS LETTER COLOR
   4 REM STEP IN LINE 70 AND    VARIABLE "G" CONTROL HEIGHT AND THCKNES OF LINES
   5 REM SWITCHING "A" AND "B"  IN LINE 180 ROTATES LETTER      AOTHER 90 DEGREES 
   7 REM EACH LETTER TAKES ABOUT   35 SECONDS TO GENERATE AND PRINT
   9 FAST 
  10 PRINT AT 10,3;"% %I%N%P%U%T% %A%N%Y% %L%E%N%G%T%H% %M%E%S%S%A%G%E% "
  20 INPUT M$
  50 FOR L=1 TO LEN M$
  55 LET CH=CODE M$(L)
  60 LET R=7680+(CH*8)
  63 LET A=1
  67 LET B=1
  70 FOR R=R+7 TO R STEP (-0.25)
  80 LET RC=PEEK R
  90 FOR H=1 TO 7
 100 LET D=RC
 110 FOR G=1 TO 3
 120 IF G=2 OR 3 THEN LET RC=D
 130 LET CNTR=0
 140 IF RC<128 THEN GOTO 170
 150 LET CNTR=128
 160 LET RC=RC-128
 170 LET RC=RC*2
 180 PRINT AT A,B;CHR$ CNTR;
 190 LET A=A+1
 200 NEXT G
 210 NEXT H
 220 LET A=1
 230 LET B=B+1
 240 NEXT R
 250 COPY 
 260 NEXT L
 270 STOP 

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

Scroll to Top