This ZX81/TS1000 program displays an animated Christmas greeting with a scrolling message and a decorative tree scene rendered using block graphics. It embeds two machine code routines directly in a REM statement at line 1: one routine (called via USR 16550) appears to perform a fast block memory copy or clear, and another (USR 16514) provides a delay or synchronisation function. The scrolling text in line 31/44 alternates between a plain string (A$) and an inverse-video version (B$) to create a flashing effect as the message moves across the screen. Lines 999–1060 form a diagnostic/loader section that prints and verifies the hex data for the machine code, and line 2000 saves the program.
Program Analysis
Program Structure
The program is divided into three functional sections:
- Initialisation and display setup (lines 1–28): The REM statement at line 1 stores two machine code routines. Lines 10–28 call the first routine, define the scrolling strings, draw the static scene using block graphics, and configure display settings via
POKE 16418. - Main animation loop (lines 29–60): A
FORloop scrolls the greeting message across line 18, alternating between plain and inverse-video versions of the string for a flashing effect, with a machine code delay called between frames. - Diagnostic/loader section (lines 999–2060): Lines 999–1060 print and verify the hex content of the machine code area for development purposes. Line 2000 saves the program and line 2010 returns to the main entry point.
Machine Code Routines
Two machine code routines are embedded in the REM at line 1. The REM data begins at address 16514 (the first byte after the line’s length bytes), and a second entry point is at 16550.
| Entry point | Called at | Apparent function |
|---|---|---|
USR 16514 | Line 40 | Timing delay / frame sync between scroll steps |
USR 16550 | Line 10 | Fast block memory operation (copy or clear using LDIR) |
The hex string in line 1000 matches the REM content: key Z80 mnemonics visible in the byte sequence include ED B8 (LDDR), ED 52 (SBC HL,DE), E5 (PUSH HL), D1 (POP DE), and C9 (RET). The routine at 16550 loads a 16-bit address from (16448) (the ZX81 D_FILE system variable), computes offsets, and performs a block transfer — consistent with manipulating the display file. The HALT byte 76 appears twice at offset +36/+37, used as a padding or end-of-routine marker.
Display Techniques
The static scene on lines 20–25 uses ZX81 block graphic characters to render a stylised Christmas tree and a fireplace-like border. The tree silhouette is built from \'' (▀) and \': (▛) characters, with a trunk suggested by \:: (▌) pairs. The bottom border uses \:.\.. sequences to form a solid floor line.
The scrolling animation on lines 29–50 uses two pre-built strings of equal length: A$ contains the message with normal characters, and B$ contains the same text with inverse-video characters (e.g. %M%E%R%R%Y). On each iteration, B$ is printed first (line 31), the machine code delay runs (line 40), then A$ is printed (line 44), creating a flicker/flash effect as the text scrolls.
Key BASIC Idioms
POKE 16418,0andPOKE 16418,2(lines 26/28) manipulate theMARGINor a related system variable to control display scrolling behaviour — specifically suppressing the automatic scroll prompt.FOR A=1 TO LEN A$-30 STEP 2(line 29) steps through the string two characters at a time, since each display position consumes one character in the ZX81’s non-collapsed display file.PRINT AT 0,INT (RND*32);"%."(line 30) scatters inverse-video dots randomly across the top row on each frame, simulating falling snow.GOTO 29(line 60) creates an infinite loop that continuously replays the scrolling animation.
Diagnostic Section
Lines 999–1060 appear to be a development aid. Line 1000 defines a string containing the expected hex bytes of the machine code. Lines 1030–1060 then PEEK each byte from address 16514 to 16600 and print it as a two-character hex string using the expression CHR$ (28+INT (B/16)) — offset 28 from ASCII space maps digits 0–9 and letters A–F by exploiting the ZX81 character set layout where digits start at code 28.
Notable Anomalies
- Line 10 assigns the result of
USR 16550toA, but the return value is not used; the call is purely for its side effect on the display file. - The strings
A$andB$include long runs of spaces at start and end, acting as padding to ensure the message scrolls fully on and off screen without boundary errors in the substring sliceA$(A+1 TO A+30). - The
STEP 2on line 29 means only odd-indexed starting positions are used, which is consistent with the ZX81 display file structure where each character cell occupies a predictable offset.
Content
Source Code
1 REM 2A1040 129 1ED421143 0ED52E51121 0ED52D1 18C 1EDB8EB 6202B368010FBC97676 6162A C40237EFE7628 4368018F610F4C9
10 LET A=USR 16550
11 LET A$=" MERRY CHRISTMAS .''..''.---'.!!% . "
12 LET B$=" %M%E%R%R%Y %C%H%R%I%S%T%M%A%S :: :: ---'.!!% . "
20 PRINT AT 13,0;"% % % % % '''''' ''''''% % % % % % % '''''' ''''% % % '''' ''% % '' ':: ,,~~ :: ,,~~ :"
21 FOR A=17 TO 21
22 PRINT AT A,0;": :"
23 NEXT A
25 PRINT AT 21,0;":..............................................................:"
26 POKE 16418,0
27 PRINT AT 22,0;"% % % % % % % % % % % % % % % % % % % % % % % % % %R%Y%A%N% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
28 POKE 16418,2
29 FOR A=1 TO LEN A$-30 STEP 2
30 PRINT AT 0,INT (RND*32);"%."
31 PRINT AT 18,1;B$(A TO A+29)
40 LET Z=USR 16514
44 PRINT AT 18,1;A$(A+1 TO A+30)
50 NEXT A
60 GOTO 29
999 REM CODE
1000 LET A$="2A1040114300ED52E5112100ED52D1018C01EDB8EB06202B308010FBC9767606162A0C40237EFE762804368018F610F4C9"
1010 PRINT A$
1020 PRINT ,,
1030 FOR A=16514 TO 16600
1040 LET B=PEEK A
1050 PRINT CHR$ (28+INT (B/16));CHR$ (28+B-(INT (B/16))*16);" ";
1060 NEXT A
2000 SAVE "1010%4"
2010 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
