CLONE is a tape utility program that loads and interfaces with a machine code block to provide three functions: block tape copying (EAR to MIC), tape header reading and display, and a “Last Resort” loader. On startup, it detects whether the host machine is a Timex model by reading five bytes from address 4413 and comparing them to the string “Timex,” then branches to alternate machine code entry points accordingly. The header reader decodes standard tape header fields from memory at address 32800 onward, displaying file type, name, length, and autostart LINE number for programs, as well as array variable names and dimensions. A calibration routine at line 8000 reads system timer bytes at addresses 23670–23671 to compute a timing adjustment, patching NOP sleds and a JP instruction into the machine code at address 57780 to compensate for tape speed variation.
Program Analysis
Program Structure
The program is organized into several functional sections, controlled by menu-driven branching:
- Lines 6–12: Initialization — clears memory, loads the machine code block, detects hardware type, and waits for a keypress.
- Lines 20–90: Main menu — offers Block Copy (B), Header Reader (H), and Last Resort (L).
- Lines 100–280: Header Reader — reads and decodes a tape header from memory at address 32800, with output routed to printer or screen.
- Lines 2000–2070: Block Copy menu — sets tape signal polarity, launches the copy routine, or enters calibration.
- Lines 8000–8900: Calibration — measures tape timing deviation and patches the machine code to compensate.
- Line 9999: Save routine — saves the BASIC loader and machine code block twice each, presumably for reliability.
Hardware Detection
Lines 7–9 perform a Timex model detection. After an initial RANDOMIZE USR 57472 call, five bytes are read from address 4413 into the string s$. If s$="Timex", two POKEs redirect subsequent machine code calls to alternate entry points (57532,192 and 57533,26 instead of 57532,125 and 57533,27). This technique reads from the ROM identification area to distinguish Timex hardware, and the result is reused throughout the program at lines 150–151.
Machine Code Interface
The program relies heavily on a machine code block loaded at address 57472 (with a corresponding BASIC save of "cloneO" CODE 57472,8064 at line 9999). Multiple entry points into this block are used:
| Address | Purpose |
|---|---|
| 57472 | Initial setup / hardware probe |
| 57491 | Header read (non-Timex) |
| 57550 | Header read (Timex) |
| 57589 | Calibration timing measurement |
| 57735 | Block copy (EAR to MIC) |
| 58080 | Post-load initialization |
| 65004 | Block load launcher |
Header Decoding
Lines 160–265 decode the standard 17-byte tape header, which the machine code has deposited at address 32800. The byte at 32800 indicates file type (0=Program, 1=Number array, 2=Character array, 3=Bytes). The filename occupies bytes 32801–32810; a trailing-space trimmer at lines 210–240 scans backward to find the last non-space character (mark), then prints only up to that position.
Additional fields are decoded at fixed offsets:
PEEK 32811 + 256*PEEK 32812— data lengthPEEK 32813 + 256*PEEK 32814— parameter 1 (LINE autostart or load address)PEEK 32815 + 256*PEEK 32816— parameter 2 (program area offset for PROGRAM type)
For number arrays, CHR$ (PEEK 32814-64) recovers the variable letter. For character arrays, CHR$ (PEEK 32814-128) is used instead, reflecting the different encoding of array variable names in the header.
Line 250 suppresses printing the LINE number when it is 9999 or above, which is the conventional sentinel value indicating no autostart line is set.
Tape Speed Calibration
The calibration section (lines 8000–8120) reads two system timer bytes from addresses 23670 and 23671 after RANDOMIZE USR 57589 completes. The difference dif is used in the formula:
makeup = INT((29*dif/2 - 49) / 4)
This yields a count of NOP bytes to insert as a timing pad. Lines 8050–8120 fill addresses from 57780 onward with 0 (NOP, opcode 0x00), then write a JP nn instruction (opcode 0xC3 = 195, followed by address bytes 151 and 225, i.e., jump to 57751) at the end of the NOP sled. The value of makeup is clamped between 0 and 290 to prevent overwriting unintended memory.
Polarity Control
Lines 2020–2030 allow toggling the tape signal polarity for the copy function by POKEing pairs of bytes at 57749 and 57750. Normal polarity uses values 95 and 31; inverted polarity reverses them (31 and 95). Line 2010 displays the current setting by using the AND conditional string trick: "Normal " AND PEEK VAL "57749"=VAL "95" evaluates to the string if the condition is true, or an empty string otherwise.
Key BASIC Idioms
VAL "number"inGO TO,AT, andPOKEarguments at lines 2000–2010 is a memory-saving technique, storing numeric literals as shorter string tokens.- The keypress wait loop pattern at lines 11–12 (flush any held key, then wait for a new press) is standard practice.
OPEN #2,n$at line 140 routes subsequentPRINToutput to either the printer ("p") or screen ("s") depending on the user’s earlier choice.- Line 270 uses
POKE 23692,5after each header read to prevent the “scroll?” prompt from interrupting continuous header display.
Bugs and Anomalies
- Lines 30–90 form a polling loop, but line 40 handles the “B” key inline without a
GO TOskip — afterRANDOMIZE USR 65004returns (if it does), execution falls through to line 50 and then 90, looping back. This is likely intentional, as the machine code at 65004 may not return. - Line 150’s
POKE 23692,5resets the line counter before each header read, but the loop atGO TO 150from line 280 will continuously read headers with no exit condition; the only way out is to stop the tape and let a read fail or error out. - The save line (9999) saves the BASIC program under the name
"CLONE"twice and the code block under"cloneO"twice, which provides redundancy on tape but could confuse loaders expecting a single copy.
Content
Source Code
6 CLEAR 32799: BORDER 2: PAPER 2: INK 2: CLS : PRINT AT 10,3; INK 0; FLASH 1;"CLONE Code Is Now Loading!"; FLASH 0: LOAD ""CODE
7 RANDOMIZE USR 57472: POKE 57532,125: POKE 57533,27: LET s$=""
8 FOR j=4413 TO 4417: LET s$=s$+CHR$ PEEK j: NEXT j
9 IF s$="Timex" THEN POKE 57532,192: POKE 57533,26
10 CLOSE #2: RANDOMIZE USR 58080: BORDER 0: INK 0: PAPER 7: PRINT #1;"\* 1985 by 2K Express "
11 IF INKEY$<>"" THEN GO TO 11
12 IF INKEY$="" THEN GO TO 12
20 CLS : PRINT AT 10,0;" Press B for Block Copy";AT 11,0;" Press H for Header Reader";AT 12,0;" Press L for Last Resort"
30 IF INKEY$="H" OR INKEY$="h" THEN GO TO 100
40 IF INKEY$="B" OR INKEY$="b" THEN CLS : PRINT AT 10,0;"Press L to start loading blocks": RANDOMIZE USR 65004
50 IF INKEY$="L" OR INKEY$="l" THEN GO TO 2000
90 GO TO 30
100 CLS
110 PRINT AT 10,5;"Program Header Display";AT 12,0;" Press P For Output To Printer";AT 13,0;" Press S For Output To Screen"
112 IF INKEY$="P" OR INKEY$="p" THEN LET n$="p": GO TO 120
114 IF INKEY$="S" OR INKEY$="s" THEN LET n$="s": GO TO 120
116 GO TO 112
120 BORDER 7: CLS : PAUSE 30: PRINT AT 12,0;"Start The Tape And Press Any Key"
130 IF INKEY$="" THEN GO TO 130
140 PAPER 7: INK 0: CLS : OPEN #2,n$
150 IF s$="Timex" THEN RANDOMIZE USR 57550: POKE 23692,5
151 IF s$<>"Timex" THEN RANDOMIZE USR 57491: POKE 23692,5
160 IF PEEK 32800=0 THEN PRINT "Program: ";
170 IF PEEK 32800=1 THEN PRINT "Number array: ";
180 IF PEEK 32800=2 THEN PRINT "Character array: ";
190 IF PEEK 32800=3 THEN PRINT "Bytes: ";
200 LET mark=0: PRINT """";
210 FOR j=32810 TO 32801 STEP -1
220 IF PEEK j<>32 THEN IF mark=0 THEN LET mark=j
230 NEXT j
240 FOR j=32801 TO mark: PRINT CHR$ PEEK j;: NEXT j
245 PRINT """";
250 IF PEEK 32800=0 THEN IF PEEK 32813+256*PEEK 32814<9999 THEN PRINT " LINE ";PEEK 32813+256*PEEK 32814;
251 IF PEEK 32800=0 THEN PRINT " P=";PEEK 32815+256*PEEK 32816;" V=";PEEK 32811+256*PEEK 32812-PEEK 32815-256*PEEK 32816;" bytes"
255 IF PEEK 32800=1 THEN PRINT " ";CHR$ (PEEK 32814-64);"(";(PEEK 32811+256*PEEK 32812-3)/5;")";
260 IF PEEK 32800=3 THEN PRINT " ";PEEK 32813+256*PEEK 32814;",";PEEK 32811+256*PEEK 32812;
265 IF PEEK 32800=2 THEN PRINT " ";CHR$ (PEEK 32814-128);"$(";PEEK 32811+256*PEEK 32812-3;")";
270 PAUSE 150: PRINT ""
280 GO TO 150
2000 CLS : PRINT "Option?";AT VAL "6",VAL "6";"1=Copy";AT VAL "7",VAL "6";"2=Calibrate";AT VAL "8",VAL "6";"3=Normal Polarity";AT VAL "9",VAL "6";"4=Inverted Polarity"
2010 PRINT AT VAL "16",VAL "6";"Polarity = ";"Normal " AND PEEK VAL "57749"=VAL "95";"Inverted" AND PEEK VAL "57749"=VAL "31"
2020 IF INKEY$="4" THEN POKE 57749,31: POKE 57750,95: GO TO 2010
2030 IF INKEY$="3" THEN POKE 57749,95: POKE 57750,31: GO TO 2010
2040 IF INKEY$="2" THEN GO TO 8000
2050 IF INKEY$<>"1" THEN GO TO 2020
2055 CLS : PRINT AT 10,0;"All signals at the EAR jack are now being copied at the MIC jack"
2060 RANDOMIZE USR 57735
2070 GO TO 10
8000 REM Calibrate
8010 CLS : PRINT AT 10,0;"Start the Tape and Press a Key": PAUSE 3e4: RANDOMIZE USR 57589
8020 LET dif=PEEK 23670-PEEK 23671
8030 LET makeup=INT ((29*dif/2-49)/4)
8040 IF makeup<=0 THEN LET makeup=0: GO TO 8100
8045 IF makeup>290 THEN LET makeup=290
8050 FOR j=57780 TO 57780+makeup: POKE j,0: NEXT j
8100 POKE 57780+makeup,195
8110 POKE 57780+makeup+1,151
8120 POKE 57780+makeup+2,225
8900 GO TO 2000
9999 BEEP .1,30: SAVE "CLONE" LINE 6: BEEP .1,30: SAVE "cloneO"CODE 57472,8064: BEEP .1,30: SAVE "CLONE" LINE 6: BEEP .1,30: SAVE "cloneO"CODE 57472,8064
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


