--- title: "64 Column Utility" id: 51108 type: "computer_media" slug: "64-column-utility" url: "http://localhost/computer_media/64-column-utility/" markdown_url: "http://localhost/computer_media/64-column-utility.md" published_at: "2023-07-12T08:44:09+00:00" modified_at: "2026-03-30T21:40:54+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2023/07/64-col-util-1.png" excerpt: "A machine code-powered utility squeezes 64 columns onto your printer, letting you type, display, save, and copy multi-page text documents from a single BASIC shell." 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: "Lemke Software Development" slug: "lemke-software-development" taxonomy: "post_tag" url: "http://localhost/tag/lemke-software-development/" - 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: "Stan Lemke" slug: "stan-lemke" taxonomy: "indiv" url: "http://localhost/indiv/stan-lemke/" genre: - name: "64 Column" slug: "64-column" taxonomy: "genre" url: "http://localhost/type/64-column/" - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Program" programmers: - name: "Stan Lemke" slug: "stan-lemke" taxonomy: "indiv" url: "http://localhost/indiv/stan-lemke/" download_url: "https://archive.org/download/timex-sinclair-software-archive/64%20Column%20Utility%20%28198x%29%28Lemke%2C%20Stan%29%28US%29%28TS2068%29%28Program%29.zip" mediadate: "1986" images: - url: "http://localhost/wp-content/uploads/2023/07/64-col-util-1.png" - url: "http://localhost/wp-content/uploads/2023/07/64-col-util-2.png" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-1.jpg" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-2.jpg" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-listing-1.jpg" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-listing-2.jpg" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-listing-3.jpg" - url: "http://localhost/wp-content/uploads/2023/07/64-col-print-utility-listing-4.jpg" article_media: - id: 39170 title: "The Fame & The Glory" type: "article" url: "http://localhost/article/the-fame-the-glory/" media_type_tags: "64 Column, Programming" --- This program is a 64-column print utility that uses an external machine code routine (loaded from tape at address 57000, 2671 bytes long) to enable 64-character-wide mode. Text is entered by the user one character at a time, stored in a large 20,000-byte string array `a$`, and passed to the machine code via POKEs to addresses 57039 and 57041 before calling specific entry points (57077 for display file rendering, 57090/57104 for type-input handling, 57131 for a clear/reset routine, and 58000 for copy/print). Pages are 1,408 characters each, allowing the array to hold approximately 14 pages of text. The utility supports typing new text, loading existing data from tape, displaying it on screen, copying to printer, and saving the text buffer back to tape as a raw CODE block. *** ## Program Analysis ### Program Structure The program is organized into a set of short subroutines at low line numbers (10–90), a type-input loop (100–160), a display-file section (300–310), a main initialization block (9000–9060), and a save/master-copy line (9999). The listing appears three times in the source, which is a formatting artifact of the submission — the program itself is a single unit. | Lines | Role | | --- | --- | | 20 | Display-file subroutine: renders stored text via machine code | | 40 | Type subroutine A: passes character to machine code entry point 57090 | | 50 | Type subroutine B: passes character to machine code entry point 57104 | | 60 | Initializes machine code page parameters (column offset 0, width 64) | | 70 | CLS then calls machine code clear/reset at 57131 | | 90 | Clears the lower status bar (stream #0, row 0) | | 100–160 | Interactive text-entry loop, one character at a time | | 300–310 | Display/review loop, page by page | | 9000–9060 | Startup: dimensions array, loads machine code, sets colors, menu | | 9999 | Development save line: saves BASIC and machine code to tape | ### Machine Code Interface The heart of the utility is a 2,671-byte machine code block loaded at address 57000. BASIC communicates with it entirely through POKEs to a small parameter area before each `RANDOMIZE USR` call: - `POKE 57039, CODE a$(i)+1` — writes the character code (incremented by 1, likely converting to an internal token/index) for display rendering - `POKE 57041, CODE a$(i+1)` — writes the second character of a pair for the display-file path - `POKE 57043, 0` and `POKE 57044, 64` — set the column offset and line width for the 64-column mode The machine code exposes at least five distinct entry points: | Address | Purpose | | --- | --- | | 57000 | Main initialization routine | | 57077 | Display-file character renderer (processes pairs from `a$`) | | 57090 | Type-mode handler, first character in a pair | | 57104 | Type-mode handler, second character in a pair | | 57131 | Screen/printer clear or reset | | 58000 | Copy/print trigger | The `OUT 255,54` at line 9040 is a hardware port write, likely controlling a printer interface or related peripheral to set it into 64-column mode. ### Text Storage and Paging Text is held in `a$`, dimensioned to 20,000 characters. A page is exactly 1,408 characters (64 columns × 22 rows, the printable area). Characters are stored in pairs and processed two at a time by the display loop at line 20, which iterates `FOR i=c1 TO c2 STEP 2`. The variable `c1` advances by 1,408 on each new page, so up to approximately 14 pages can be stored in the array without reloading. ### Machine Code Address Recovery Line 9005 contains a notable technique for recovering the load address of the machine code block after it has been loaded from tape. Since `LOAD ""CODE` without an explicit address uses the address stored in the tape header, the code reads system variables `23629`/`23630` (the last LOAD address, sometimes called PROG or a related pointer) via PEEK and reconstructs the 16-bit address into `j`: `POKE 56998, PEEK 23629 : POKE 56999, PEEK 23630 : LET j = 256*PEEK 56999 + PEEK 56998` This value `j` is then used both as the base address for subsequent `LOAD ""CODE j` calls (line 9030) and as the start address for `SAVE "64c"CODE j,i+1` (line 135). ### Key BASIC Idioms - **`VAL "number"` in GO TO/GO SUB:** Used extensively throughout to encode branch targets as string literals, saving tokenized number storage bytes in the BASIC line. - **`PAUSE 0: LET b$=INKEY$`:** Standard efficient keypress-wait pattern appearing at lines 110, 120, 130, 305. - **`LET a$(1)=a$(1)` at line 9005:** A dummy self-assignment used to force the interpreter to allocate and fix the DIM’d string array in memory before its address is captured. - **Stream `#0` for status messages:** All user-facing prompts are written to the lower screen via `PRINT #0;AT ...`, keeping the main display area undisturbed. ### Notable Anomalies - Line 9010 contains a typo: `"Pleae press 2"` instead of `"Please press 2"`. - The `©` keyword (`RESET`) appears in the REM header (line 3) as part of the author attribution layout, not as executable code. - Line 135 saves `CODE j, i+1` — the length `i+1` reflects the current character count, making each save a variable-length snapshot of typed content. - The display loop at line 310 advances `c2` by adding 1407 to the new `c1` (`LET c2=c1+1407`), which is correct for a 1,408-character page (indices `c1` to `c1+1407` inclusive). ## Source Code ``` 1 REM *********************** 2 REM 64 Column Print Utility 3 REM RESET by S D Lemke 4 REM 2144 White Oak 5 REM Wichita, Ks 67207 6 REM *********************** 10 REM Display File 20 FOR i=c1 TO c2 STEP 2: POKE 57039,(CODE a$(i)+1): POKE 57041,CODE a$(i+1): RANDOMIZE USR 57077: NEXT i: RETURN 30 REM Type 40 POKE 57039,CODE b$+1: RANDOMIZE USR 57090: RETURN 50 POKE 57039,CODE b$+1: RANDOMIZE USR 57104: RETURN 60 POKE VAL "57043",VAL "0": POKE VAL "57044",VAL "64": RETURN 70 CLS : RANDOMIZE USR VAL "57131": RETURN 90 PRINT #0;AT 0,0;" ": RETURN 100 PRINT #0;AT 1,10;"TYPE INPUT" 110 PAUSE 0: LET b$=INKEY$: GO SUB 40: LET i=i+1: LET a$(c1+i)=b$ 120 PAUSE 0: LET b$=INKEY$: GO SUB 50: LET i=i+1: LET a$(c1+i)=b$: IF i<=1408 THEN GO TO 110 130 PRINT #0;AT 0,0;"Press N for Next Page, S to Save C to Copy, D to Display File": FOR k=1 TO 20: NEXT k: PAUSE 0: LET b$=INKEY$: GO SUB VAL "90" 135 IF b$="S" OR b$="s" THEN SAVE "64c"CODE j,i+1: GO TO VAL "130" 140 IF b$="C" OR b$="c" THEN PRINT #0;AT 0,0;TAB 31;" ";';TAB 31;" ": RANDOMIZE USR VAL "58000": GO TO VAL "130" 145 IF b$="D" OR b$="d" THEN GO SUB VAL "60": CLS : RANDOMIZE USR VAL "57131": GO TO VAL "300" 150 GO SUB VAL "70": LET c1=1408+c1 160 GO SUB VAL "60": GO TO VAL "100" 300 LET c1=VAL "1": LET c2=VAL "1408": GO SUB VAL "60": GO SUB VAL "70" 305 GO SUB VAL "20": PRINT #0;AT 0,8;"Press any key.": PRINT #0;AT 1,7;"Press C to Copy": PAUSE 0: IF INKEY$="C" OR INKEY$="c" THEN GO SUB VAL "90": RANDOMIZE USR VAL "58000" 310 GO SUB VAL "70": LET c1=c1+1408: LET c2=c1+1407: GO SUB VAL "60": GO TO VAL "305" 9000 DIM a$(20000): LOAD ""CODE : PAPER VAL "1": BORDER VAL "1": INK VAL "7": CLS 9005 LET i=VAL "0": LET c1=VAL "0": GO SUB VAL "60": LET a$(1)=a$(1): POKE VAL "56998",PEEK VAL "23629": POKE VAL "56999",PEEK VAL "23630": LET j=VAL "256"*PEEK VAL "56999"+PEEK VAL "56998" 9010 CLS : PRINT AT VAL "2",VAL "4";"64 Column Print Utility": PRINT AT VAL "10",VAL "0";"Press 1 to type a page";';" 2 to load & display a file Pleae press 2 to load an explanation of this utility and the demo program." 9020 PAUSE VAL "0": LET b$=INKEY$: IF b$="1" OR b$="2" THEN GO TO VAL "9030" 9025 GO TO VAL "9020" 9030 IF b$="2" THEN PRINT AT 14,10;"START TAPE";AT 18,0;" ": LOAD ""CODE j 9040 RANDOMIZE USR VAL "57000": OUT 255,54: CLS : RANDOMIZE USR VAL "57131" 9050 IF b$="1" THEN GO TO VAL "100" 9060 GO TO VAL "300" 9999 CLEAR : SAVE "64c" LINE 9000: SAVE "64c"CODE 57000,2671 1 REM *********************** 2 REM 64 Column Print Utility 3 REM RESET by S D Lemke 4 REM 2144 White Oak 5 REM Wichita, Ks 67207 6 REM *********************** 10 REM Display File 20 FOR i=c1 TO c2 STEP 2: POKE 57039,(CODE a$(i)+1): POKE 57041,CODE a$(i+1): RANDOMIZE USR 57077: NEXT i: RETURN 30 REM Type 40 POKE 57039,CODE b$+1: RANDOMIZE USR 57090: RETURN 50 POKE 57039,CODE b$+1: RANDOMIZE USR 57104: RETURN 60 POKE VAL "57043",VAL "0": POKE VAL "57044",VAL "64": RETURN 70 CLS : RANDOMIZE USR VAL "57131": RETURN 90 PRINT #0;AT 0,0;" ": RETURN 100 PRINT #0;AT 1,10;"TYPE INPUT" 110 PAUSE 0: LET b$=INKEY$: GO SUB 40: LET i=i+1: LET a$(c1+i)=b$ 120 PAUSE 0: LET b$=INKEY$: GO SUB 50: LET i=i+1: LET a$(c1+i)=b$: IF i<=1408 THEN GO TO 110 130 PRINT #0;AT 0,0;"Press N for Next Page, S to Save C to Copy, D to Display File": FOR k=1 TO 20: NEXT k: PAUSE 0: LET b$=INKEY$: GO SUB VAL "90" 135 IF b$="S" OR b$="s" THEN SAVE "64c"CODE j,i+1: GO TO VAL "130" 140 IF b$="C" OR b$="c" THEN PRINT #0;AT 0,0;TAB 31;" ";';TAB 31;" ": RANDOMIZE USR VAL "58000": GO TO VAL "130" 145 IF b$="D" OR b$="d" THEN GO SUB VAL "60": CLS : RANDOMIZE USR VAL "57131": GO TO VAL "300" 150 GO SUB VAL "70": LET c1=1408+c1 160 GO SUB VAL "60": GO TO VAL "100" 300 LET c1=VAL "1": LET c2=VAL "1408": GO SUB VAL "60": GO SUB VAL "70" 305 GO SUB VAL "20": PRINT #0;AT 0,8;"Press any key.": PRINT #0;AT 1,7;"Press C to Copy": PAUSE 0: IF INKEY$="C" OR INKEY$="c" THEN GO SUB VAL "90": RANDOMIZE USR VAL "58000" 310 GO SUB VAL "70": LET c1=c1+1408: LET c2=c1+1407: GO SUB VAL "60": GO TO VAL "305" 9000 DIM a$(20000): LOAD ""CODE : PAPER VAL "1": BORDER VAL "1": INK VAL "7": CLS 9005 LET i=VAL "0": LET c1=VAL "0": GO SUB VAL "60": LET a$(1)=a$(1): POKE VAL "56998",PEEK VAL "23629": POKE VAL "56999",PEEK VAL "23630": LET j=VAL "256"*PEEK VAL "56999"+PEEK VAL "56998" 9010 CLS : PRINT AT VAL "2",VAL "4";"64 Column Print Utility": PRINT AT VAL "10",VAL "0";"Press 1 to type a page";';" 2 to load & display a file Pleae press 2 to load an explanation of this utility and the demo program." 9020 PAUSE VAL "0": LET b$=INKEY$: IF b$="1" OR b$="2" THEN GO TO VAL "9030" 9025 GO TO VAL "9020" 9030 IF b$="2" THEN PRINT AT 14,10;"START TAPE";AT 18,0;" ": LOAD ""CODE j 9040 RANDOMIZE USR VAL "57000": OUT 255,54: CLS : RANDOMIZE USR VAL "57131" 9050 IF b$="1" THEN GO TO VAL "100" 9060 GO TO VAL "300" 9999 CLEAR : SAVE "64c" LINE 9000: SAVE "64c"CODE 57000,2671 1 REM *********************** 2 REM 64 Column Print Utility 3 REM RESET by S D Lemke 4 REM 2144 White Oak 5 REM Wichita, Ks 67207 6 REM *********************** 10 REM Display File 20 FOR i=c1 TO c2 STEP 2: POKE 57039,(CODE a$(i)+1): POKE 57041,CODE a$(i+1): RANDOMIZE USR 57077: NEXT i: RETURN 30 REM Type 40 POKE 57039,CODE b$+1: RANDOMIZE USR 57090: RETURN 50 POKE 57039,CODE b$+1: RANDOMIZE USR 57104: RETURN 60 POKE VAL "57043",VAL "0": POKE VAL "57044",VAL "64": RETURN 70 CLS : RANDOMIZE USR VAL "57131": RETURN 90 PRINT #0;AT 0,0;" ": RETURN 100 PRINT #0;AT 1,10;"TYPE INPUT" 110 PAUSE 0: LET b$=INKEY$: GO SUB 40: LET i=i+1: LET a$(c1+i)=b$ 120 PAUSE 0: LET b$=INKEY$: GO SUB 50: LET i=i+1: LET a$(c1+i)=b$: IF i<=1408 THEN GO TO 110 130 PRINT #0;AT 0,0;"Press N for Next Page, S to Save C to Copy, D to Display File": FOR k=1 TO 20: NEXT k: PAUSE 0: LET b$=INKEY$: GO SUB VAL "90" 135 IF b$="S" OR b$="s" THEN SAVE "64c"CODE j,i+1: GO TO VAL "130" 140 IF b$="C" OR b$="c" THEN PRINT #0;AT 0,0;TAB 31;" ";';TAB 31;" ": RANDOMIZE USR VAL "58000": GO TO VAL "130" 145 IF b$="D" OR b$="d" THEN GO SUB VAL "60": CLS : RANDOMIZE USR VAL "57131": GO TO VAL "300" 150 GO SUB VAL "70": LET c1=1408+c1 160 GO SUB VAL "60": GO TO VAL "100" 300 LET c1=VAL "1": LET c2=VAL "1408": GO SUB VAL "60": GO SUB VAL "70" 305 GO SUB VAL "20": PRINT #0;AT 0,8;"Press any key.": PRINT #0;AT 1,7;"Press C to Copy": PAUSE 0: IF INKEY$="C" OR INKEY$="c" THEN GO SUB VAL "90": RANDOMIZE USR VAL "58000" 310 GO SUB VAL "70": LET c1=c1+1408: LET c2=c1+1407: GO SUB VAL "60": GO TO VAL "305" 9000 DIM a$(20000): LOAD ""CODE : PAPER VAL "1": BORDER VAL "1": INK VAL "7": CLS 9005 LET i=VAL "0": LET c1=VAL "0": GO SUB VAL "60": LET a$(1)=a$(1): POKE VAL "56998",PEEK VAL "23629": POKE VAL "56999",PEEK VAL "23630": LET j=VAL "256"*PEEK VAL "56999"+PEEK VAL "56998" 9010 CLS : PRINT AT VAL "2",VAL "4";"64 Column Print Utility": PRINT AT VAL "10",VAL "0";"Press 1 to type a page";';" 2 to load & display a file Pleae press 2 to load an explanation of this utility and the demo program." 9020 PAUSE VAL "0": LET b$=INKEY$: IF b$="1" OR b$="2" THEN GO TO VAL "9030" 9025 GO TO VAL "9020" 9030 IF b$="2" THEN PRINT AT 14,10;"START TAPE";AT 18,0;" ": LOAD ""CODE j 9040 RANDOMIZE USR VAL "57000": OUT 255,54: CLS : RANDOMIZE USR VAL "57131" 9050 IF b$="1" THEN GO TO VAL "100" 9060 GO TO VAL "300" 9999 CLEAR : SAVE "64c" LINE 9000: SAVE "64c"CODE 57000,2671 ```