Program Merge

This file is part of Timex Sinclair Public Domain Library Tape 1002. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Utility

This program implements a BASIC program merger utility for the ZX81/TS1000, allowing two or more BASIC programs to be combined in memory. The machine code routine, stored as a REM statement at line 1, is invoked via RAND USR using the classic PEEK 16388+256*PEEK 16389 idiom to locate the start of BASIC regardless of RAM pack size. The REM data begins with the standard two-byte JP (0xC3) preamble at offset 2, making the machine code self-relocating and compatible with any memory configuration. Line 20 launches the routine directly at address 16516, which corresponds to the start of the REM statement’s data area in the default 1K RAM layout.


Program Analysis

Program Structure

The program is brief by design — its real payload is the machine code embedded in the REM statement at line 1. The remaining lines handle display and invocation:

  1. Line 1 — REM statement containing the binary machine code routine (approximately 230 bytes)
  2. Line 10 — SAVE command to persist the program to tape
  3. Line 11 — Full-screen instructions printed at row 10
  4. Line 12 — Prompt printed at row 20
  5. Line 13 — Busy-wait keypress loop using INKEY$
  6. Line 20RAND USR 16516 launches the machine code

Machine Code Entry Point

RAND USR 16516 at line 20 jumps directly into the REM data area. Address 16514 is the start of the BASIC program area (RAMTOP on a stock 1K/16K machine), and 16516 is two bytes in — past the line number and length fields — placing execution exactly at the first byte of the REM’s content. The REM data begins with \76\76 (two LD (HL),H NOPs effectively) followed by \CD\E7\02, a CALL 0x02E7, consistent with calling a ZX81 ROM routine.

Relocation via PEEK 16388/16389

The user instructions at line 11 direct the operator to invoke the merger after loading a target program using RAND USR(PEEK 16388+256*PEEK 16389). System variables at addresses 16388–16389 hold the PROG pointer — the address of the start of the BASIC program area. This idiom correctly locates the REM’s machine code regardless of which RAM configuration is in use, making the utility memory-size agnostic.

Machine Code Highlights

Disassembling the REM payload reveals several notable operations:

  • \2A\04\40LD HL,(0x4004): loads the PROG system variable, confirming the code operates on BASIC program memory directly.
  • \ED\42SBC HL,BC: used for address arithmetic, likely computing program lengths.
  • \ED\B0LDIR: block memory copy instruction, the core mechanism for merging program bytes.
  • \DB\FE / \D3\FFIN A,(0xFE) / OUT (0xFF),A: keyboard read and display port access, used during the merge operation for user interaction or synchronisation.
  • \CFRST 8 (ZX81 error handler): called at several points, likely for controlled exits or triggering specific ROM behaviours.
  • Multiple \1C bytes at the end of the REM — INC E instructions — appear to be padding or a data table used by the routine.

Key BASIC Idioms

IdiomLocationPurpose
IF INKEY$="" THEN GOTO 13Line 13Busy-wait loop until any key is pressed
RAND USR 16516Line 20Jump into REM machine code at fixed address
PEEK 16388+256*PEEK 16389Line 11 (instructions)Read PROG pointer to find machine code after reload
AT 10,0 / AT 20,0Lines 11–12Positional text layout using PRINT AT

Notable Techniques

Storing executable machine code inside a REM statement is a classic ZX81 technique: the BASIC interpreter skips REM content entirely, but the bytes remain in memory and are addressable. The fixed entry via RAND USR 16516 assumes the program loads at the default BASIC start address (0x4045 / 17477 in decimal, with line 1 starting at 16514 and data at 16516). The additional invocation method documented in the instructions (PEEK 16388+256*PEEK 16389) adds two to the PROG pointer to skip the REM line’s header and reach the code, providing a portable entry point suitable for use after merging programs when the absolute address may shift.

Bugs and Anomalies

Line 10 saves before the program has been run or tested by the user; a user loading this from tape would need to be aware that running line 10 immediately on load would simply re-save the loader. This is a common pattern for self-distributing utilities and is not a functional bug. No other anomalies are present in the BASIC wrapper.

Appears On

Assembled by Tim Ward from many sources. Contains programs 10051 – 10121.

Image Gallery

Source Code

   1 REM \76\76\CD\E7\02\2A\04\40\01\E2\00\B7\ED\42\22\04\40\E5\01\0C\00\09\22\C7\40\22\E6\40\22\F6\40\22\00\41\22\19\41\E1\54\5D\2B\36\3E\2B\F9\2B\2B\22\02\40\21\BF\40\01\E2\00\ED\B0\C3\C3\03\CD\E7\02\FD\CB\21\86\CD\CB\40\18\FB\0E\01\06\00\3E\7F\DB\FE\D3\FF\1F\30\5A\17\17\38\67\10\F1\C1\FD\CB\21\46\20\5A\CD\CB\40\FD\CB\21\C6\79\17\30\F5\1E\05\21\3C\40\CD\CB\40\71\23\1D\20\F8\1E\6F\CD\CB\40\1D\20\FA\2A\3F\40\11\7D\40\B7\ED\52\C8\EB\2A\1C\40\01\80\00\09\E5\CD\CB\40\71\23\ED\4B\02\40\05\E5\ED\42\E1\30\19\1B\7A\B3\20\EB\36\80\E1\18\2E\3E\FF\32\27\40\FD\CB\3B\86\CF\0C\18\8E\CF\0D\CF\03\D5\1E\94\06\1A\1D\DB\FE\17\CB\7B\7B\38\F5\10\F5\D1\20\04\FE\56\30\E3\3F\CB\11\30\DE\C9\11\7D\40\7E\E6\C0\20\31\1A\13\BE\23\20\02\1A\BE\1B\2B\30\08\E5\EB\CD\F2\09\E1\18\EC\28\13\D5\CD\F2\09\E3\C5\2B\CD\9E\09\23\23\C1\D1\EB\ED\B0\18\D2\D5\CD\F2\09\EB\D1\18\CA\21\00\00\22\29\40\CF\FF\25\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C\1C
  10 SAVE "1008%5"
  11 PRINT AT 10,0;"THIS PROGRAM WILL MERGE ANY TWO OR MORE PROGRAMS. IT WILL WORK  WITH ANY SIZE MEMORY. TO USE    ENTER RAND USR(PEEK 16388+256*PEEK 16389)"
  12 PRINT AT 20,0;"PRESS ANY KEY TO RUN"
  13 IF INKEY$="" THEN GOTO 13
  20 RAND USR 16516

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.