This program is a demonstration/banner display for a Timex/Sinclair user group, cycling through eight machine code routines stored as addresses in array S. The REM line at line 0 contains a substantial block of Z80 machine code (approximately 100 bytes) that implements the eight display effect subroutines called via RAND USR. Each routine is called 32 times per screen fill, and the program loops endlessly through all eight effects after printing the group’s name 22 times to fill the display. The S() array holds direct entry-point addresses into the machine code embedded in the REM statement, which begins at address 16385 (the byte after the REM token on line 0).
Program Analysis
Program Structure
The program has two distinct layers: a BASIC driver (lines 1–20) and a library of Z80 machine code routines embedded in the REM statement at line 0. The BASIC layer fills the screen with the group name, invokes each machine code effect 32 times, clears the screen, then advances to the next effect in an infinite loop.
- Line 0 —
REMcontaining raw Z80 opcodes (~100 bytes) - Lines 1–9 — Initialise array
S(8)with eight machine code entry-point addresses - Lines 11–19 — Outer loop over 8 effects; inner loops fill screen then call the current effect 32 times
- Line 20 —
GOTO 11for perpetual looping - Lines 21–22 —
SAVEandRUNfor distribution purposes only
Machine Code in the REM Statement
On the ZX81/TS1000, line 0’s REM body starts at address 16385 (0x4001). The eight entry points stored in S(1)–S(8) are offsets into this block:
| Array element | Address (decimal) | Offset into REM (hex) |
|---|---|---|
S(1) | 16626 | +0xF1 (241) |
S(2) | 16514 | +0x81 (129) |
S(3) | 16633 | +0xF8 (248) |
S(4) | 16560 | +0xAF (175) |
S(5) | 16588 | +0xCB (203) |
S(6) | 16619 | +0xEA (234) |
S(7) | 16529 | +0x90 (144) |
S(8) | 16612 | +0xE3 (227) |
The machine code is called via RAND USR S(X), the standard ZX81 idiom for invoking Z80 routines from BASIC. The return value on the calculator stack is ignored.
Disassembly Notes
Tracing the byte sequence reveals several recognisable Z80 patterns:
2A 0C 40—LD HL,(400C): loads the display file address (D_FILE system variable at 0x400C)ED B0/ED B8—LDIR/LDDR: block copy instructions, strongly suggesting scroll or copy screen effects11 21 00 19 D1— setsDEto 0x0021 (33, one display row width) and loads it back, consistent with per-row addressing01 D6 02—LD BC,02D6= 726 bytes, which is 22 rows × 33 bytes (the full ZX81 display file size)36 00/10 FB—LD (HL),00 / DJNZ: clearing loop, used in what appears to be a screen-clear or wipe effectFE 76—CP 76h (HALT): tests for the ZX81 NEWLINE character (0x76) used as a row terminator in the display fileCD CC 40/CD 91 40/CD 82 40/CD B0 40— internalCALLs between sub-routines within the REM block, confirming a modular design with shared helper routines
Key BASIC Idioms
RAND USR address— the canonical ZX81 method for calling machine code;RANDOMIZEdiscards the USR return value cleanly.- Pre-initialised address array — storing entry points in
S()allows the outer loop to dispatch to any of the eight routines with a singleRAND USR S(X)statement, avoiding a largeIF/GOTOdispatch table. - Screen fill via
PRINT— 22 repetitions of the 33-character banner string exactly fills the 22-row ZX81 display before the effect routines run.
Notable Techniques
Embedding machine code in a REM statement at line 0 is a well-established ZX81 technique: line 0 guarantees the code sits at the lowest possible address in the BASIC program area (0x4001 + a small header), making the absolute entry-point addresses predictable and stable regardless of other program content. The eight effects are likely scroll, wipe, invert, and similar display manipulations, each implemented as a small subroutine that shares helper code via internal CALLs, keeping the total footprint compact.
Potential Issues
- The entry-point addresses in
S()are hardcoded and will break if theREMline content or any preceding program line changes length, as this shifts the machine code to a different address. - There is no guard against BASIC editing inadvertently corrupting the machine code bytes in the
REMstatement.
Source Code
0 REM \2A\0C\40\E5\11\21\00\19\D1\01\D6\02\ED\B0\C9\2A\10\40\11\43\00\ED\52\E5\11\21\00\ED\52\D1\01\B5\02\ED\B8\2A\0C\40\06\20\23\36\00\10\FB\C9\2A\0C\40\11\D6\02\19\06\16\2B\4E\36\00\2B\7E\FE\76\28\02\18\03\10\F2\C9\71\4F\18\F1\2A\0C\40\06\16\23\4E\36\00\23\7E\FE\76\28\02\18\03\10\F2\C9\71\4F\18\F1\CD\CC\40\CD\91\40\C9\CD\B0\40\CD\91\40\C9\CD\B0\40\CD\82\40\C9\CD\CC\40\CD\82\40\C9
1 DIM S(8)
2 LET S(1)=16626
3 LET S(2)=16514
4 LET S(3)=16633
5 LET S(4)=16560
6 LET S(5)=16588
7 LET S(6)=16619
8 LET S(7)=16529
9 LET S(8)=16612
11 FOR X=1 TO 8
12 FOR I=1 TO 22
13 PRINT "==TIMEX/SINCLAIR USER""S GROUP=="
14 NEXT I
15 FOR I=1 TO 32
16 RAND USR S(X)
17 NEXT I
18 CLS
19 NEXT X
20 GOTO 11
21 SAVE "1008%2"
22 RUN Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
