This program is a demonstration of four screen-manipulation effects on a ZX81/TS1000, driven by machine code routines embedded in a REM statement at line 1. The four effects are: a border-drawing routine (using a user-supplied character), a line-scroll routine that repeatedly scrolls a typed message up the screen, a screen-fill routine, and a screen-invert routine. Machine code is stored starting at the address of the REM statement’s data bytes (around address 16514–16598) and called via RAND USR to invoke each effect. A POKE at address 16515 or 16546 is used to pass parameters—such as the fill character code or the screen row—directly into the machine code before execution.
Program Analysis
Program Structure
The program is organized as a menu-driven demo with five logical sections:
- Line 1 (REM): Holds the raw machine code bytes for all routines.
- Lines 10–120: Main menu — displays four options and polls INKEY$ after calling a wait/scan routine via
RAND USR 16545. - Lines 1000–1070: Option A — Border effect using a user-chosen character.
- Lines 2000–2070: Option B — Line-scroll effect for a user-typed message.
- Lines 3000–3030: Option C — Screen-fill effect.
- Lines 4000–4030: Option D — Screen-invert effect, then returns to the menu.
- Line 5000–5010: SAVE block, then restarts via
GOTO 1.
Machine Code Routines in the REM Statement
All machine code is packed into the data portion of the REM at line 1. On the ZX81/TS1000, a REM statement’s text bytes are stored directly in memory beginning two bytes after the line’s start, making them executable as Z80 code. The system variable E_LINE (address 16404/0x400C) holds the address of the edit line, but the REM data itself starts at the fixed address 16514 (the program loads at 16509, with the REM token at offset 4 and its length bytes occupying two more, placing data at 16514).
The identified entry points and their approximate roles are:
| USR Address | Role |
|---|---|
16514 | Border draw — fills screen border region with character from POKE at 16515 |
16545 | Keyboard scan / wait routine (called from main menu loop) |
16580 | Screen fill routine (Option C) |
16598 | Screen invert routine (Option D) |
Key BASIC Idioms and Techniques
- RAND USR as a subroutine call:
RAND USR nnnnnis the standard ZX81 idiom for invoking machine code; the return value of USR is passed to RANDOMIZE and discarded. - POKE for parameter passing: Option A uses
POKE 16515, CODE A$(1)to write the desired character code directly into the machine code (one byte past the entry point), effectively setting a parameter before callingRAND USR 16514. Similarly,POKE 16546, 21andPOKE 16546, 10set a row parameter used by the scroll routine. - INKEY$ polling loop: Lines 80–120 form a tight polling loop —
RAND USR 16545is called to perform a scan, then each INKEY$ is tested. This avoids a slow FOR/NEXT delay and instead uses machine code for timing. - Wait-for-keypress idiom: Line 1060 uses
IF INKEY$="" THEN GOTO 1060to spin until any key is held, then returns to the main menu viaRUN. - RUN to restart: Each option ends with
RUNrather thanGOTO 10, which fully reinitialises BASIC variables — a common ZX81 pattern to avoid variable accumulation.
Machine Code Disassembly Notes
Examining the hex bytes in the REM:
3E 26—LD A, 0x26(load character/value)2A 0C 40—LD HL, (0x400C)— loads the D_FILE pointer (display file address) from system variable at 0x400C06 20—LD B, 32— sets column count (32 characters wide)23 77—INC HL / LD (HL), A— writes character to display, repeated in loops10 FC—DJNZloop back — tight inner loop for filling rowsC9—RET— return to BASIC at routine endED B0—LDIR— block copy instruction, used in the scroll/copy routine76—HALT— used as the ZX81 display newline sentinel (0x76 = HALT opcode = ZX81 newline character)
Notable Techniques
- The screen invert loop (Option D) calls
RAND USR 16598ten times inside a FOR loop, creating a flicker/animation effect rather than a single inversion. - The scroll demo (Option B) calls
RAND USR 1654564 times in a FOR loop — using the machine code routine as a per-step delay/scroll engine. - The SAVE at line 5000 followed by
GOTO 1means the program can save itself and immediately continue running.
Potential Anomalies
- Line 1060 waits for a key to be pressed before returning to menu, but the check is
INKEY$=""(wait while no key), then falls through toCLSandRUN— this is correct but means any key press exits immediately without identifying which key was pressed. - The POKE targets (16515, 16546) are hardcoded absolute addresses; if the program is loaded at a different address (e.g., with a different REM length), these will be incorrect. The program is therefore position-dependent.
Content
Source Code
1 REM 3E262A C40 620237710FC 614232377111F 0197710F623 620237710FCC93E15CD1D153E21CD1D15EF 434CDA7 E2A C40 923545D7E23 11F 0EDB02B77C97676 6162A C40237EFE7628 4368018F610F4C92A C40ED5B1040444DA7ED52C860697EFE7628 3C680772318ED
10 PRINT ,," OPTIONS:"
20 PRINT ,,,,"(A) BORDER"
30 PRINT ,,"(B) LINE SCROLL"
40 PRINT ,,"(C) SCREEN FILL"
50 PRINT ,,"(D) INVERT SCREEN"
55 POKE 16546,21
60 PRINT AT 21,0;"PRESS LETTER OF DESIRED OPTION"
70 RAND USR 16545
80 IF INKEY$="A" THEN GOTO 1000
90 IF INKEY$="B" THEN GOTO 2000
100 IF INKEY$="C" THEN GOTO 3000
110 IF INKEY$="D" THEN GOTO 4000
120 GOTO 70
1000 CLS
1010 PRINT AT 21,0;"INPUT DESIRED CHARACTER."
1020 INPUT A$
1030 CLS
1040 POKE 16515,CODE A$(1)
1050 RAND USR 16514
1060 IF INKEY$="" THEN GOTO 1060
1065 CLS
1070 RUN
2000 CLS
2010 PRINT AT 21,0;"INPUT STATEMENT."
2020 INPUT A$
2025 POKE 16546,10
2030 PRINT AT 10,0;A$
2040 FOR A=1 TO 64
2050 RAND USR 16545
2055 NEXT A
2060 CLS
2070 RUN
3000 RAND USR 16580
3010 FOR A=1 TO 20
3020 NEXT A
3025 CLS
3030 RUN
4000 FOR A=1 TO 10
4010 RAND USR 16598
4020 NEXT A
4030 GOTO 70
5000 SAVE "1009%3"
5010 GOTO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
