Calligraphy Font

Developer(s): Ben H. Jackson
Date: 1985
Type: Program
Platform(s): TS 2068
Tags: Font, Graphics

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:

  1. 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.
  2. Machine code loader subroutine (lines 100–107): A generic READ/POKE loop that deposits DATA bytes starting at address MCST, terminating when it POKEs a 201 (Z80 RET opcode).
  3. Upper-case draw subroutine (lines 150–154): POKEs X/Y/L into the parameter block starting at 54700, then calls USR 54403.
  4. Lower-case draw subroutine (lines 160–164): Same pattern but POKEs into the lower-data block starting at 58000.
  5. 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.
  6. Lower-case rendering sequence (lines 316–385): Reconfigures for 24×30-pixel characters (90 bytes each) and renders lowercase a–z.
  7. Help text (line 400): Explains the coordinate and letter-index system to the user.

Memory Map

Address rangeContents
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–56592Upper-case bitmap data (26 × 72 bytes)
58000–60360Lower-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

Appears On

One of the largest single-tape collections anywhere, with over 40 programs spanning flight planning, satellite tracking, hydrology, Forth programming, a 17-game mega-pack, and a complete calligraphic font renderer. A snapshot of a thriving Texas user group at its peak.

Related Products

Related Articles

Related Content

Image Gallery

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.

Scroll to Top