Z-AID is a utility that extends cassette tape operations by adding verify, chain, and block memory save/load capabilities through an embedded machine code routine. The program self-installs by decoding a hex string stored in Z$ (line 60) into raw Z80 machine code bytes at memory address 29000, using a FOR loop that processes the string two characters at a time. Each byte value is reconstructed by subtracting 28 from each character code and combining the two nibbles — an encoding scheme that keeps all characters printable on the ZX81 character set. After decoding, control is transferred to the routine via RAND USR 29000. The startup sequence uses SLOW mode, displays a title screen, and prompts the user to position the tape before the load sequence begins at line 9.
Program Analysis
Program Structure
The program has two distinct execution paths controlled by the entry point. Line 5 jumps immediately to line 18, which runs the interactive title/setup sequence. Lines 9–17 form the tape-load completion handler, reached via GOTO 9 at line 80 after the machine code has been POKEd into memory. The overall flow is:
- Line 5:
GOTO 18— skip the load-complete handler on first run. - Lines 18–55: Display title screen in SLOW mode, prompt user to stop the tape latch.
- Lines 55–80: Initialize target address
T=29000, set up hex stringZ$,CLEAR 3000to protect RAM from 3000 upward, thenGOTO 9. - Lines 9–17: CLS, print restart-tape prompt,
PAUSE 100,RAND USR 29000to execute the installed routine, then display load-complete message andSTOP. - Lines 3000–8000: Subroutine that decodes
Z$two characters at a time and POKEs bytes to addressT. - Lines 9000–9010:
SAVEthenRUN— the distribution/re-save block.
Note that the decode subroutine at lines 3000–8000 is never explicitly called with GO SUB in the visible listing; CLEAR 3000 at line 62 protects the subroutine area, and the flow from line 80 goes to line 9 rather than into the subroutine directly. The subroutine ends with RETURN at line 8000, suggesting it was designed to be called via GO SUB 3000 from a line that may have been removed or is reached differently in the saved version.
Machine Code Encoding Scheme
The hex payload in Z$ (line 60) is not stored as ASCII hex digits. Instead, each nibble is offset by 28 added to its value, placing all characters in the printable ZX81 range. The decode loop at lines 3000–8000 reverses this:
- Take two consecutive characters from
Z$. - High nibble:
(CODE Z$(X) - 28) * 16 - Low nibble:
CODE Z$(X+1) - 28 - Sum the two to reconstruct the full byte, then
POKE T, V.
This encoding ensures the string contains no characters with codes below 28, avoiding control characters that would be illegal or ambiguous inside a ZX81 string literal.
Z80 Payload Analysis
The hex string in Z$ decodes to approximately 50 bytes of Z80 machine code installed at address 29000 (0x7148). Key opcodes visible in the raw hex include CD (CALL), C9 (RET), DB FE (IN A,(254) — keyboard read), D3 FF (OUT (255),A — likely display or border), and loop constructs using 10 (DJNZ) and 20 (JR NZ). The routine is invoked via RAND USR 29000, which is the standard ZX81 idiom for calling machine code from BASIC.
Memory Layout
| Address | Purpose |
|---|---|
| 3000 | CLEAR boundary — BASIC decode subroutine starts here (protected from BASIC memory manager) |
| 29000 (0x7148) | Target address for decoded Z80 machine code; initial value of T |
Key BASIC Idioms
RAND USR 29000— standard ZX81 technique to call a machine code address from BASIC without usingUSRin an expression context.CLEAR 3000— reserves high memory for the machine code while also resetting the stack; used here to protect the subroutine lines from being overwritten.FOR X=1 TO LEN Z$ STEP 2— iterates over pairs of characters in the encoded string without hardcoding the byte count, making the routine length-agnostic.- The
SAVE "[B]"at line 9000 followed immediately byRUNat line 9010 is the standard self-replicating distribution pattern for ZX81 utilities.
Anomalies and Notable Points
- The decode subroutine (lines 3000–8000) ends with
RETURN, implying aGO SUB 3000call that is not present in the listed code between lines 62 and 80. It is likely that aGO SUB 3000was intended between lines 62 and 80 and is missing from the listing, or theGOTO 9at line 80 was meant to beGO SUB 3000: GOTO 9. - Lines 25, 38, and 48 contain copyright and publisher text printed in plain (non-inverse) characters, consistent with a commercial product splash screen.
- The
PAUSE 100at line 11 gives the user roughly 5 seconds to act on the “PLEASE RESTART THE TAPE NOW” prompt beforeRAND USR 29000executes the installed utility.
Content
Image Gallery
Source Code
5 GOTO 18
9 CLS
10 PRINT AT 10,1;"[P][L][E][A][S][E]█[R][E][S][T][A][R][T]█[T][H][E]█[T][A][P][E]█[N][O][W]"
11 PAUSE 100
12 RAND USR 29000
15 CLS
16 PRINT AT 10,3;"[Z][-][A][I][D]█[L][O][A][D]█[I][S]█[N][O][W]█[C][O][M][P][L][E][T][E]"
17 STOP
18 SLOW
19 CLS
20 PRINT AT 4,7;"[Z][-][A][I][D]█[1][.][0]"
25 PRINT AT 6,0;"COPYRIGHT 1982 BY"
38 PRINT AT 7,0;"INTERNATIONAL PUBLISHING ";AT 8,0;"AND SOFTWARE INC."
48 PRINT AT 10,0;"[*][*]██[P][R][E][S][S]█[T][H][E]█[S][T][O][P]█[L][A][T][C][H]██████[*][*][*][*]████[O][N]█[T][H][E]█[T][A][P][E]█[D][E][C][K]████████[*][*]"
55 LET T=29000
60 LET Z$="CD[3]30#260FCD72712520FACD727169CD727161CD7271C5CD727179C147C5CD72717123C10B78B120F4C90E0106003E7FDBFED3FF1FD2A2031717380510F0C372711E94061A1DDBFE17CB7B7B38F510F52004FE5630D63FCB1130D1C9"
62 CLEAR3000
80 GOTO 9
3000 FOR X=1 TO LEN Z$ STEP 2
4000 LET V=(CODE Z$(X)-28)*16+CODE Z$(X+1)-28
5000 POKE T,V
6000 LET T=T+1
7000 NEXT X
8000 RETURN
9000 SAVE "[B]"
9010 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.