Calligraphy font display program that renders a complete custom alphabet in a large bitmap format across the screen. It loads a machine code routine from tape (“olde” CODE at address 54000) that handles the actual pixel-drawing via USR calls, and stores two separate character sets: uppercase letters (72 bytes/character, 24×24 pixels) in the “UPPER DATA” region at 54700–56592, and lowercase letters (90 bytes/character, 24×30 pixels) in “LOWER DATA” at 58000–60360. The program POKEs parameters such as frame reference, bits-per-row, row count, and bytes-per-character directly into the machine code parameter block before each USR 54403 call, and separately configures the display address via POKEs to system variables at 23675–23676. Letters are addressed by index (L=1 to 26, corresponding to a–z) and positioned by pixel coordinates (X, Y) that define the upper-left corner of each character block.
Program Structure
The program is divided into clearly separated functional regions:
- Initialisation (lines 1–14): Clears memory with
CLEAR 54000, loads the machine code block “olde” from tape, prompts the user, then jumps to line 400. - Machine code loader subroutine (lines 100–107): A generic READ/POKE loop that deposits DATA bytes starting at address
MCST, terminating when it POKEs a201(Z80 RET opcode). - Upper-case draw subroutine (lines 150–154): POKEs X/Y/L into the parameter block starting at 54700, then calls
USR 54403. - Lower-case draw subroutine (lines 160–164): Same pattern but POKEs into the lower-data block starting at 58000.
- Upper-case rendering sequence (lines 216–255): Configures the MC parameter block for 24×24-pixel characters (72 bytes each) and iterates through all 26 uppercase letters.
- Lower-case rendering sequence (lines 316–385): Reconfigures for 24×30-pixel characters (90 bytes each) and renders lowercase a–z.
- Help text (line 400): Explains the coordinate and letter-index system to the user.
Memory Map
| Address range | Contents |
|---|---|
| 54000–54053 | “plot” machine code routine |
| 54368–54399 | “GCELL” registers / parameter block |
| 54400–54562 | “GCELL” machine code |
| 54567–54569 | “SEBIT” registers |
| 54570–54697 | “SEBIT” machine code |
| 54700–56592 | Upper-case bitmap data (26 × 72 bytes) |
| 58000–60360 | Lower-case bitmap data (26 × 90 bytes) |
Machine Code Interface
The single entry point USR 54403 is used for both upper and lower-case rendering. The machine code distinguishes the two character sets by where the base address and parameters are POKEd. For uppercase, the parameter block begins at address 54700; for lowercase it begins at 58000. Each block carries the same layout:
- Offset 0: X pixel coordinate
- Offset 1: Y pixel coordinate
- Offset 2: frame/bank reference (always 1)
- Offset 3: bits per row (24)
- Offset 4: rows per character (24 upper / 30 lower)
- Offset 5: bytes per character (72 upper / 90 lower)
- Offset 6: letter index L (1–26)
- Offsets 7–19: zeroed-out working registers
The display base address is separately configured via POKEs to system variables 23675/23676: 172,213 for upper (pointing into the upper display area) and 144,226 for lower.
Notable Techniques
The generic MC loader at lines 100–107 uses Z80 opcode 201 (RET) as a natural sentinel: the loop terminates as soon as that byte is POKEd, avoiding any need to know the exact code length in advance. This is a clean self-documenting termination strategy.
The FOR/NEXT zero-fill loops at lines 223–225 and 323–325 clear 14 bytes of working registers in the parameter block before each rendering pass, ensuring no stale values remain from a previous call.
The pixel-coordinate positioning model is noteworthy: rather than using character-cell addressing, X and Y are raw pixel offsets. The upper-case characters are spaced 30 pixels apart horizontally and arranged in three rows (Y=1, 28, 53, 81), while the lower-case characters use tighter 15-pixel spacing (Y≈81–108), reflecting their proportionally smaller design despite having more rows.
Line 143 (LET X=150: LET L=15: GO TO 150) appears to be a convenience entry point that pre-loads defaults for the draw subroutine — perhaps a debugging shortcut left in the listing.
Content
Source Code
1 REM "calig" © by Ben H. Jackson, 1/31/85; ALL RIGHTS RESERVED
5 CLEAR 54000
6 LOAD "olde"CODE 54000,6361
7 GO TO 400
12 REM --MEMORY ALLOCATION 54000--54053 "plot" 54368--54399 "GCELL" reg 54400--54562 "GCELL" 54567--54569 "SEBIT" regs 54570--54697 "SEBIT" 54700--56592 "UPPER DATA" 58000--60360 "LOWER DATA"
13 CLS : PRINT "1. Turn off the tape recorder."'"2. Press any key and ENTER."
14 INPUT z$: CLS
15 REM --VCRIABLES BYTE--MEMORY CELL DATA MCST--MC START ADDR RS--RESTORE STMT NO
99 GO TO 200
100 REM --LOAD MC SUBROUTINE---
101 RESTORE RS
102 LET I=MCST
103 READ BYTE: POKE I,BYTE
104 IF BYTE=201 THEN GO TO 106
105 LET I=I+1: GO TO 103
106 REM PRINT "LAST CELL USED=";I
107 RETURN
143 LET X=150: LET L=15: GO TO 150
150 REM --DRAW A LETTER SBR----
151 POKE 54700,X: POKE 54701,Y
152 POKE 54706,L
153 LET K=USR 54403
154 RETURN
160 REM --DRAW A LOWER CASE LETTER"
161 POKE 58000,X: POKE 58001,Y
162 POKE 58006,L
163 LET K=USR 54403
164 RETURN
216 POKE 23675,172
217 POKE 23676,213
219 POKE 54702,001: REM ;set ref to frame 1
220 POKE 54703,024: REM ;24 bits/row
221 POKE 54704,024: REM ;24 rows
222 POKE 54705,072: REM ;72 bytes/character
223 FOR i=1 TO 14
224 POKE 54706+i,0
225 NEXT i
230 LET X=1: LET Y=1: LET L=1: GO SUB 150
231 LET X=30: LET Y=1: LET L=2: GO SUB 150
232 LET X=60: LET Y=1: LET L=3: GO SUB 150
233 LET X=90: LET Y=1: LET L=4: GO SUB 150
234 LET X=120: LET Y=1: LET L=5: GO SUB 150
235 LET X=150: LET Y=1: LET L=6: GO SUB 150
236 LET X=180: LET Y=1: LET L=7: GO SUB 150
237 LET X=210: LET Y=1: LET L=8: GO SUB 150
238 LET X=1: LET Y=28: LET L=9: GO SUB 150
239 LET X=30: LET L=10: GO SUB 150
240 LET X=60: LET L=11: GO SUB 150
241 LET X=90: LET L=12: GO SUB 150
242 LET X=120: LET L=13: GO SUB 150
243 LET X=150: LET L=14: GO SUB 150
244 LET X=180: LET L=15: GO SUB 150
245 LET X=210: LET L=16: GO SUB 150
246 LET X=1: LET Y=53: LET L=17: GO SUB 150
247 LET X=30: LET L=18: GO SUB 150
248 LET X=60: LET L=19: GO SUB 150
249 LET X=90: LET L=20: GO SUB 150
250 LET X=120: LET L=21: GO SUB 150
251 LET X=150: LET L=22: GO SUB 150
252 LET X=180: LET L=23: GO SUB 150
253 LET X=210: LET L=24: GO SUB 150
254 LET X=1: LET Y=81: LET L=25: GO SUB 150
255 LET X=30: LET Y=78: LET L=26: GO SUB 150
316 POKE 23675,144
317 POKE 23676,226
319 POKE 58002,001: REM ;set ref to frame 1
320 POKE 58003,024: REM ;24 bits/row
321 POKE 58004,030: REM ;30 rows
322 POKE 58005,090: REM ;90 bytes/character
323 FOR i=1 TO 14
324 POKE 58006+i,0
325 NEXT i
360 LET X=60: LET L=1: GO SUB 160
361 LET X=75: LET L=2: GO SUB 160
362 LET X=90: LET L=3: GO SUB 160
363 LET X=105: LET L=4: GO SUB 160
364 LET X=120: LET L=5: GO SUB 160
365 LET X=135: LET L=6: GO SUB 160
366 LET X=150: LET L=7: GO SUB 160
367 LET X=165: LET L=8: GO SUB 160
368 LET x=180: LET L=9: GO SUB 160
369 LET X=190: LET L=10: GO SUB 160
370 LET X=205: LET L=11: GO SUB 160
371 LET X=220: LET L=12: GO SUB 160
372 LET X=1: LET Y=106: LET L=13: GO SUB 160
373 LET X=24: LET Y=108: LET L=14: GO SUB 160
374 LET X=40: LET L=15: GO SUB 160
375 LET X=55: LET L=16: GO SUB 160
376 LET X=70: LET L=17: GO SUB 160
377 LET X=85: LET L=18: GO SUB 160
378 LET X=100: LET L=19: GO SUB 160
379 LET X=115: LET L=20: GO SUB 160
380 LET X=130: LET L=21: GO SUB 160
381 LET X=145: LET L=22: GO SUB 160
382 LET X=160: LET L=23: GO SUB 160
383 LET X=175: LET L=24: GO SUB 160
384 LET X=190: LET L=25: GO SUB 160
385 LET X=205: LET L=26: GO SUB 160
386 STOP
400 CLS : PRINT "LIST 230 or 360 to see how LET x = a number and LET y = a number defines the PIXELS of the upper left hand corner of the block where the letter will appear. LET l = a number ( 1 to 26 eg. 1 = a, 2 = b) selects the letter for that block"',,,,"To RUN or re-start GOTO 12"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
