Index Card is a utility that generates formatted cassette tape index cards, printed via a TS 2040 printer. The program defines five custom UDG characters (loaded via POKE USR) to draw decorative corner and border graphics for the card layout. It collects user input for tape title, loading instructions, cassette type, length, and up to 14 lines of free text, with word-wrap logic that splits lines at spaces when input exceeds 32 characters. A notable subroutine at line 9000 renders scaled-up text by reading pixel data with POINT and using PLOT to double each pixel in both axes, effectively creating 2× enlarged lettering on the card. Two card sides are produced: one resembling a program information panel and one a cassette spine/label, each sent to the printer with COPY.
Program Structure
The program is divided into four logical phases:
- UDG initialization (lines 1000–1100): Reads pixel data from DATA statements and POKEs five custom UDG characters into memory.
- User input collection (lines 5010–5210): Prompts for tape title, loading instructions, cassette type, length, and up to 14 lines of body text with word-wrap.
- Card rendering and printing (lines 6010–6300): Builds and prints two card faces — a program information side and a cassette spine/label side — each terminated with
COPY. - Pixel-doubling subroutine (lines 9000–9120): Renders enlarged text by scanning pixels with
POINTand redrawing each as a 2×2 block usingPLOT.
UDG Setup
Lines 1000–1100 define five UDGs: "a", "b", "c", "d", and "f". Each is loaded by reading eight bytes from DATA and POKEing them via POKE USR a$+j,b. UDG "f" is a solid top line (value 255) used for the cassette spine header row. The characters \a, \b, \c, \d embedded in string a$ at line 1200 form corner and curve graphics for the “Cut Here” separator line.
Input Validation and Word Wrap
Input validation uses direct GO TO loops rather than structured loops. For the tape title, the program re-prompts if the string is empty or longer than 16 characters. Cassette type is similarly bounded at 20 characters. Loading instructions are silently truncated to 12 characters at line 5060 rather than re-prompting.
Body text entry (lines 5120–5210) includes a word-wrap algorithm. If the user’s input fits within 32 characters it is centered in the 32-character array b$ using the expression 16-LEN i$/2 as the start column. If the input exceeds 32 characters, lines 5160–5207 scan backward from position 33 to find the last space, split the string there, store the first portion flush-left, and center the remainder — then increment i to skip the next input prompt for that row.
Pixel-Doubling Text Enlargement (Subroutine 9000)
The subroutine at line 9000 is the program’s most technically interesting feature. It works by:
- Printing the target string
y$at row 21 (off the visible card area but on screen) usingPRINT AT 21,0;y$. - Iterating over each pixel row (
a, 7 down to 0) and each pixel column (b, 0 to8*LEN y$-1). - Calling
POINT (b,a)to test whether that screen pixel is set. - If set, drawing a 2×2 block at a target position using nested
FOR c/dloops andPLOT x-(8*LEN y$)+2*b+c, y+2*a-d. - Clearing row 21 afterward with spaces.
The parameters x and y passed into the subroutine control where on screen the enlarged text appears before COPY sends it to the printer. This technique requires no machine code — it relies entirely on the built-in POINT and PLOT keywords.
Card Layout
Two distinct card faces are produced:
- Program information side (lines 6010–6140): Prints the “Cut Here” separator, a row of underscores, the enlarged tape title, body text lines from array
b$, and aLOAD "..."instruction line before callingCOPY. - Cassette spine/label side (lines 6150–6290): Prints a row of UDG
"f"characters across the top, the enlarged title, a line of underscores drawn withPRINT OVER 1, cassette type, length formatted asCv(2*v/2 mins), a bottom underline, and the “Cut Here” strip before callingCOPY.
Notable Techniques
PRINT OVER 1;AT 2,i;"_"at line 6210 uses OVER mode to draw underscores without erasing the UDG characters already printed in that row.- The cassette length display at line 6240 uses
v/2arithmetic inline within a PRINT string concatenation — ifvis odd, this will produce a decimal result (e.g., “C45(2*22.5mins)”), which may look unintended. - The word-wrap centering formula
16-LEN i$/2at line 5150 performs integer division implicitly; for odd-length strings this may place text one character off-center due to truncation. - Lines 6020–6040 print a 32-character underline by looping
LPRINT TAB i;"_";— an effective way to position individual characters across a printer line without constructing a string.
Anomalies and Observations
- UDG
"e"is skipped in the DATA — the loop runs fori=1 TO 5reading labels"a","b","c","d","f". This is intentional, as"e"is not used in the card layout string. - The body text array
b$(18,32)is dimensioned at line 1210 but only rows 4–18 are used; rows 1–3 are never written or printed. - At line 5207, incrementing
iinside theFOR i=4 TO 17loop means the word-wrapped overflow line consumes the next loop iteration, preventing the user from entering text for that index. This limits wrapped content but avoids overrunning the array. - The
STOPat line 8999 acts as a guard to prevent the subroutine at line 9000 from being reached by fall-through after the main program ends.
Source Code
1000 DATA "a",120,132,124,3,3,124,132,120
1010 DATA "b",3,12,48,192,192,48,12,3
1020 DATA "c",192,48,12,3,3,12,48,192
1030 DATA "d",30,33,62,192,192,62,33,30
1040 DATA "f",255,0,0,0,0,0,0,0
1050 FOR i=1 TO 5
1060 READ a$
1070 FOR j=0 TO 7
1080 READ b
1090 POKE USR a$+j,b
1100 NEXT j: NEXT i
1200 LET a$="\a\b----------Cut Here----------\c\d"
1210 DIM b$(18,32)
5010 CLS : PRINT AT 10,6;"ENTER THE TAPE TITLE"
5020 INPUT "Maximum 16 Characters ";t$: IF t$="" THEN GO TO 5020
5030 IF LEN t$>16 THEN GO TO 5010
5040 CLS : PRINT AT 10,3;"ENTER LOADING INSTRUCTIONS"
5050 INPUT "Maximum 12 Characters ";l$: IF l$="" THEN GO TO 5050
5060 IF LEN l$>12 THEN LET l$=l$(1 TO 12)
5070 CLS : PRINT AT 10,5;"ENTER TYPE OF CASSETTE"
5080 INPUT "Maximum 20 Characters ";u$: IF u$="" THEN GO TO 5080
5090 IF LEN u$>20 THEN GO TO 5070
5100 CLS : PRINT AT 10,5;"ENTER LENGTH OF CASSETTE"
5110 INPUT v
5120 FOR i=4 TO 17
5130 CLS : PRINT AT 10,6;"ENTER TEXT FOR LINE ";i
5140 INPUT " ";i$
5150 IF LEN i$<=32 THEN LET b$(i)(16-LEN i$/2 TO )=i$: GO TO 5210
5160 LET c=0
5165 IF c=32 THEN GO TO 5195
5170 LET j$=i$(33-c)
5175 IF i$(33-c)=" " THEN GO TO 5200
5180 LET c=c+1
5185 GO TO 5165
5195 LET c=0
5200 LET b$(i)=i$( TO 32-c)
5205 LET b$(i+1)(16-LEN i$(33-c TO )/2 TO )=i$(33-c TO )
5207 LET i=i+1
5210 NEXT i
6010 LPRINT '''a$
6020 FOR i=0 TO 31
6030 LPRINT TAB i;"_";
6040 NEXT i
6050 CLS
6060 LET x=128: LET y=157: LET y$=t$
6070 GO SUB 9000
6080 FOR i=4 TO 18
6090 PRINT AT i,0;b$(i)
6100 NEXT i
6110 LET x=144: LET y=9: LET y$=CHR$ 34+l$+CHR$ 34
6120 GO SUB 9000
6130 PRINT AT 20,14-LEN y$;"LOAD"
6140 COPY
6150 CLS : FOR i=0 TO 31
6160 PRINT AT 0,i;"F"
6170 NEXT i
6180 LET x=128: LET y=157: LET y$=t$
6190 GO SUB 9000
6200 FOR i=0 TO 31
6210 PRINT OVER 1;AT 2,i;"_"
6220 NEXT i: OVER 0
6230 PRINT ''TAB 3;u$+" Cassette"
6240 PRINT ''TAB 3;"C";v;"(2*";v/2;"mins)"
6250 FOR i=0 TO 31
6260 PRINT AT 10,i;"_"
6270 NEXT i
6280 PRINT a$
6290 COPY
6300 CLS : PRINT AT 10,6;"INDEX CARD FINISHED"''TAB 2;"PRESS PAPER FEED & TEAR OFF"
8999 STOP
9000 PRINT AT 21,0;y$
9010 FOR a=7 TO 0 STEP -1
9020 FOR b=0 TO 8*LEN y$-1
9030 IF POINT (b,a)=0 THEN GO TO 9090
9040 FOR c=0 TO 1
9050 FOR d=0 TO 1
9060 PLOT x-(8*LEN y$)+2*b+c,y+2*a-d
9070 NEXT d
9080 NEXT c
9090 NEXT b
9100 NEXT a
9110 PRINT AT 21,0;" "
9120 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.