--- title: "Letters" id: 72299 type: "computer_media" slug: "letters" url: "http://localhost/computer_media/letters/" markdown_url: "http://localhost/computer_media/letters.md" published_at: "2026-10-23T07:47:19+00:00" modified_at: "2026-10-23T07:47:19+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/10/letters-1.png" excerpt: "Turns the keyboard into a big-letter typewriter, printing each letter, digit and punctuation mark two character rows high from pairs of user-defined graphic half-letters." 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/" indiv: - name: "John Colonna" slug: "john-colonna" taxonomy: "indiv" url: "http://localhost/indiv/john-colonna/" genre: - name: "Font" slug: "font" taxonomy: "genre" url: "http://localhost/type/font/" media_contents: - id: 72439 title: "SINCUS Exchange Tape 104" type: "computer_media" url: "http://localhost/computer_media/sincus-exchange-tape-104/" media_type: "Program" programmers: - name: "John Colonna" slug: "john-colonna" taxonomy: "indiv" url: "http://localhost/indiv/john-colonna/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Letters%20%28198x%29%28Colonna%2C%20John%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "Letters (198x)(Colonna, John)(TS2068)(US)(Program)/Letters (198x)(Colonna, John)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/10/letters-1.png" - url: "http://localhost/wp-content/uploads/2026/10/letters-2.png" media_type_tags: "Font" license: false --- # Letters Types in large block letters. After a title screen, each key you press prints a letter, digit or punctuation mark two character rows high, built from a top half and a bottom half taken from a set of 21 UDGs. The characters are square-cornered shapes made of thick strokes, and the program handles the letters a–z, the digits, the space, and the full stop, exclamation mark, question mark, apostrophe and double quote. Letters advance two columns at a time, so a line holds 16 characters, with a blank row between lines, and DELETE backs up one character. After seven lines the program returns to the top of the screen and types over what is there. The opening REM reads “letters J. Colonna SINCUS”. The title screen, loaded as a separate SCREEN$, frames the words “JOHN’S PRINT SHOP” with a border of the big letters. This copy comes from a SINCUS library disk; another disk in the collection has a shorter 79-line version with its own title screen. ## Program Analysis ### Structure - Lines 10–30: load the title SCREEN$, define the UDGs (`RESTORE : GO SUB 265`), then wait for a key and clear the screen. - Lines 35–45: `s` is the screen row and `t` the column. Line 40 moves back to the end of the previous line when DELETE takes `t` below zero, and line 45 flashes a cursor block. - Lines 50–240: one `IF INKEY$=` line per character, each printing the top and bottom UDG and adding 2 to `t`. - Line 245: DELETE (`CHR$ 12`) blanks the previous character and moves back two columns. - Lines 250–260: wrap to a new line three rows down after column 31, and return to row 1 once the row passes 20. - Lines 265–685: 21 short blocks, each a DATA line followed by a `FOR`/`READ`/`POKE USR` loop, define UDGs a to u. ### The Character Set The UDGs are building blocks rather than letters: bars down the left, right or both sides, a centre stroke, top and bottom bars, the diagonal halves of K, M, N, V, W and X, and a few special pieces. Each character pairs two of them, so H is a top cell with both sides and a bottom bar over a bottom cell with both sides. Several characters share a shape: 0 and o, 1 and i, 2 and z, 5 and s, and 8 and b. ## Notable Techniques - Each UDG is set up by its own DATA line and loop, and a single `RESTORE` before the `GO SUB` lets the 21 loops read the DATA lines in order. - DELETE is detected by comparing `INKEY$` with `CHR$ 12`, and the double-quote key by comparing with `CHR$ 34`. - The cursor in line 45 prints a block at the current position and immediately blanks it, so it flickers on each pass of the main loop. - Line 255 POKEs 23692, the scroll counter, to hold off the “scroll?” prompt. ## Potential Issues - Every `IF` line reads `INKEY$` afresh and nothing waits for the key to be released, so holding a key repeats the letter and a quick tap can be missed between tests. - Only lower-case letters are tested, so with CAPS LOCK on or Caps Shift held the letter keys print nothing. - Pressing DELETE at the start of a line makes line 245 print at column −2, which appears to stop the program with “B Integer out of range” before line 40 can move back to the previous line. - Each letter ends with a `PRINT " "` on the row below it. On the seventh line (rows 19–20) that falls on row 21, so the screen appears to scroll up one line for each letter typed there, moving the earlier lines out of step. - When the program returns to the top of the screen it does not clear it, so new text is typed over the old. This program came from an Oliger (JLO SAFE) disk and was converted for tape. Line 7, which set a JLO printer variable and POKEd a JLO system byte, is now a REM, and the Oliger `LOAD /` and `SAVE /` commands in lines 10 and 9999 became plain tape commands. The title screen follows the program on the tape. It was tested in ZEsarUX: the title screen, then TIMEX SINCLAIR typed in big letters. ## Source Code ``` 5 REM letters J. Colonna SINCUS 7 REM JLO printer settings removed (tape version) 10 LOAD "letters"SCREEN$ 15 RESTORE : GO SUB 265 20 BORDER 6 25 PRINT AT 17,3; INVERSE 1;"Press any key to continue.": PAUSE 0 30 CLS 35 LET s=1: LET t=0 40 IF t<0 THEN LET t=30: IF t=30 THEN LET s=s-3: IF s<1 THEN LET s=1: IF s=1 THEN LET t=0 45 PRINT AT s,t;"..": PRINT AT s,t;" " 50 IF INKEY$="0" OR INKEY$="o" THEN PRINT AT s,t;"i": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 55 IF INKEY$="1" THEN PRINT AT s,t;"q": PRINT AT s+1,t;"q": PRINT " ": LET t=t+2 60 IF INKEY$="2" OR INKEY$="z" THEN PRINT AT s,t;"b": PRINT AT s+1,t;"c": PRINT " ": LET t=t+2 65 IF INKEY$="3" THEN PRINT AT s,t;"b": PRINT AT s+1,t;"d": PRINT " ": LET t=t+2 70 IF INKEY$="4" THEN PRINT AT s,t;"e": PRINT AT s+1,t;"a": PRINT " ": LET t=t+2 75 IF INKEY$="5" OR INKEY$="s" THEN PRINT AT s,t;"f": PRINT AT s+1,t;"d": PRINT " ": LET t=t+2 80 IF INKEY$="6" THEN PRINT AT s,t;"f": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 85 IF INKEY$="7" THEN PRINT AT s,t;"g": PRINT AT s+1,t;"a": PRINT " ": LET t=t+2 90 IF INKEY$="8" THEN PRINT AT s,t;"h": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 95 IF INKEY$="9" THEN PRINT AT s,t;"h": PRINT AT s+1,t;"a": PRINT " ": LET t=t+2 100 IF INKEY$="a" THEN PRINT AT s,t;"r": PRINT AT s+1,t;"i": PRINT " ": LET t=t+2 105 IF INKEY$=" " THEN PRINT AT s,t;" ": PRINT AT s+1,t;" ": PRINT " ": LET t=t+2 110 IF INKEY$="b" THEN PRINT AT s,t;"h": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 115 IF INKEY$="c" THEN PRINT AT s,t;"k": PRINT AT s+1,t;"c": PRINT " ": LET t=t+2 120 IF INKEY$="d" THEN PRINT AT s,t;"n": PRINT AT s+1,t;"m": PRINT " ": LET t=t+2 125 IF INKEY$="e" THEN PRINT AT s,t;"f": PRINT AT s+1,t;"c": PRINT " ": LET t=t+2 130 IF INKEY$="f" THEN PRINT AT s,t;"f": PRINT AT s+1,t;"l": PRINT " ": LET t=t+2 135 IF INKEY$="g" THEN PRINT AT s,t;"k": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 140 IF INKEY$="h" THEN PRINT AT s,t;"e": PRINT AT s+1,t;"j": PRINT " ": LET t=t+2 145 IF INKEY$="i" THEN PRINT AT s,t;"q": PRINT AT s+1,t;"q": PRINT " ": LET t=t+2 150 IF INKEY$="j" THEN PRINT AT s,t;"a": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 155 IF INKEY$="k" THEN PRINT AT s,t;"m": PRINT AT s+1,t;"n": PRINT " ": LET t=t+2 160 IF INKEY$="l" THEN PRINT AT s,t;"l": PRINT AT s+1,t;"c": PRINT " ": LET t=t+2 165 IF INKEY$="m" THEN PRINT AT s,t;"o": PRINT AT s+1,t;"j": PRINT " ": LET t=t+2 170 IF INKEY$="n" THEN PRINT AT s,t;"n": PRINT AT s+1,t;"j": PRINT " ": LET t=t+2 175 IF INKEY$="p" THEN PRINT AT s,t;"h": PRINT AT s+1,t;"l": PRINT " ": LET t=t+2 180 IF INKEY$="q" THEN PRINT AT s,t;"i": PRINT AT s+1,t;"u": PRINT " ": LET t=t+2 185 IF INKEY$="r" THEN PRINT AT s,t;"h": PRINT AT s+1,t;"n": PRINT " ": LET t=t+2 190 IF INKEY$="t" THEN PRINT AT s,t;"s": PRINT AT s+1,t;"q": PRINT " ": LET t=t+2 195 IF INKEY$="u" THEN PRINT AT s,t;"j": PRINT AT s+1,t;"e": PRINT " ": LET t=t+2 200 IF INKEY$="v" THEN PRINT AT s,t;"j": PRINT AT s+1,t;"p": PRINT " ": LET t=t+2 205 IF INKEY$="w" THEN PRINT AT s,t;"j": PRINT AT s+1,t;"t": PRINT " ": LET t=t+2 210 IF INKEY$="x" THEN PRINT AT s,t;"p": PRINT AT s+1,t;"r": PRINT " ": LET t=t+2 215 IF INKEY$="y" THEN PRINT AT s,t;"p": PRINT AT s+1,t;"q": PRINT " ": LET t=t+2 220 IF INKEY$="." THEN PRINT AT s,t;" ": PRINT AT s+1,t;". ": PRINT " ": LET t=t+2 225 IF INKEY$="!" THEN PRINT AT s,t;"q": PRINT AT s+1,t;".": PRINT " ": LET t=t+2 230 IF INKEY$="?" THEN PRINT AT s,t;"b": PRINT AT s+1,t;".": PRINT " ": LET t=t+2 235 IF INKEY$="'" THEN PRINT AT s,t;"q": PRINT AT s+1,t;" ": PRINT " ": LET t=t+2 240 IF INKEY$=CHR$ 34 THEN PRINT AT s,t;"j": PRINT AT s+1,t;" ": PRINT " ": LET t=t+2 245 IF INKEY$=CHR$ 12 THEN PRINT AT s,t-2;" ": PRINT AT s+1,t-2;" ": LET t=t-2 250 IF t>31 THEN LET s=s+3: LET t=0 255 IF s>20 THEN POKE 23692,100: LET s=1 260 GO TO 40 265 DATA 3,3,3,3,3,3,3,3 270 FOR a=0 TO 7 275 READ b: POKE USR "a"+a,b 280 NEXT a 285 DATA 255,255,3,3,3,3,255,255 290 FOR a=0 TO 7 295 READ b: POKE USR "b"+a,b 300 NEXT a 305 DATA 192,192,192,192,192,192,255,255 310 FOR a=0 TO 7 315 READ b: POKE USR "c"+a,b 320 NEXT a 325 DATA 3,3,3,3,3,3,255,255 330 FOR a=0 TO 7 335 READ b: POKE USR "d"+a,b 340 NEXT a 345 DATA 195,195,195,195,195,195,255,255 350 FOR a=0 TO 7 355 READ b: POKE USR "e"+a,b 360 NEXT a 365 DATA 255,255,192,192,192,192,255,255 370 FOR a=0 TO 7 375 READ b: POKE USR "f"+a,b 380 NEXT a 385 DATA 255,255,3,3,3,3,3,3 390 FOR a=0 TO 7 395 READ b: POKE USR "g"+a,b 400 NEXT a 405 DATA 255,255,195,195,195,195,255,255 410 FOR a=0 TO 7 415 READ b: POKE USR "h"+a,b 420 NEXT a 425 DATA 255,255,195,195,195,195,195,195 430 FOR a=0 TO 7 435 READ b: POKE USR "i"+a,b 440 NEXT a 445 DATA 195,195,195,195,195,195,195,195 450 FOR a=0 TO 7 455 READ b: POKE USR "j"+a,b 460 NEXT a 465 DATA 255,255,192,192,192,192,192,192 470 FOR a=0 TO 7 475 READ b: POKE USR "k"+a,b 480 NEXT a 485 DATA 192,192,192,192,192,192,192,192 490 FOR a=0 TO 7 495 READ b: POKE USR "l"+a,b 500 NEXT a 505 DATA 198,198,204,204,216,216,240,240 510 FOR a=0 TO 7 515 READ b: POKE USR "m"+a,b 520 NEXT a 525 DATA 240,240,216,216,204,204,198,198 530 FOR a=0 TO 7 535 READ b: POKE USR "n"+a,b 540 NEXT a 545 DATA 195,195,231,231,219,219,219,219 550 FOR a=0 TO 7 555 READ b: POKE USR "o"+a,b 560 NEXT a 565 DATA 195,195,102,102,60,60,24,24 570 FOR a=0 TO 7 575 READ b: POKE USR "p"+a,b 580 NEXT a 585 DATA 24,24,24,24,24,24,24,24 590 FOR a=0 TO 7 595 READ b: POKE USR "q"+a,b 600 NEXT a 605 DATA 24,24,60,60,102,102,195,195 610 FOR a=0 TO 7 615 READ b: POKE USR "r"+a,b 620 NEXT a 625 DATA 255,255,24,24,24,24,24,24 630 FOR a=0 TO 7 635 READ b: POKE USR "s"+a,b 640 NEXT a 645 DATA 219,219,219,219,231,231,195,195 650 FOR a=0 TO 7 655 READ b: POKE USR "t"+a,b 660 NEXT a 665 DATA 195,195,195,219,223,207,255,255 670 FOR a=0 TO 7 675 READ b: POKE USR "u"+a,b 680 NEXT a 685 RETURN 9999 SAVE "letters" LINE 1: LOAD "letters"SCREEN$ : SAVE "letters"SCREEN$ : BEEP 1,32 ```