This program demonstrates screen region capture and restore using a self-modifying machine code routine on the TS2068/ZX Spectrum. It offers a menu to capture one of three portions of the display file (top, middle, or bottom third) or the full screen (6192 bytes from address 16384), storing the data temporarily at address 30000. The machine code, written into addresses 65356–65367, implements a standard Z80 LDIR block-move sequence assembled via individual POKEs, with source and destination addresses computed at runtime by decomposing values into high and low bytes. A reverse LDIR pass then restores the captured region back to the display file, and the result can be saved as a CODE file named “SCRN”. The program also supports loading a pre-existing SCRN display file for comparison.
Program Analysis
Program Structure
The program is organised into three logical phases: menu and option selection (lines 10–70), screen capture from display file to RAM buffer (lines 80–250), and RAM buffer restore back to display file with optional save (lines 260–370). Utility lines at 9997–9999 handle saving the program itself and the captured screen CODE file.
- Lines 1–20:
CLEAR 29999reserves RAM above address 29999; menu display and keypress wait loop. - Lines 30–70: Dispatch on key
k$; computeSTP(start address),INS(address high byte),NOB(number of bytes),INB(bytes high byte) for each region option. - Lines 80–110: Fill the screen with a tile pattern and print row numbers 0–21 so a visible test image is present during capture.
- Lines 120–240: POKE a Z80 LDIR routine into addresses 65356–65367, then call it with
RANDOMIZE USR 65356. - Lines 260–370: Reverse the LDIR arguments (source and destination swapped) to restore the buffer back to screen, then offer save or menu return.
- Lines 9997–9999:
SAVE "SCRNmove" LINE 1saves the BASIC program;SAVE "SCRN" CODE STP,NOBsaves the captured screen region.
Machine Code Routine
The machine code is assembled piecemeal into high RAM (address 65356 on a 64 KB address space, i.e. wrapping into the mapped area). The routine is a minimal Z80 block copy:
| Address | Opcode(s) | Mnemonic | Purpose |
|---|---|---|---|
| 65356 | 01 lo hi | LD BC,NOB | Byte count |
| 65359 | 11 lo hi | LD DE,30000 | Destination (RAM buffer) |
| 65362 | 21 lo hi | LD HL,STP | Source (display file) |
| 65365 | ED B0 | LDIR | Block copy HL→DE, BC bytes |
| 65367 | C9 | RET | Return to BASIC |
For the restore phase (lines 280–340), only the byte-count and address POKEs are updated; the opcodes at 65356, 65359, 65362, 65365, and 65367 are left in place from the initial write, saving re-POKEing the opcode bytes.
Address Decomposition Idiom
Because POKE takes a single byte value, 16-bit addresses and counts are split into high and low bytes. The pattern used throughout is:
- High byte:
INS = INT(STP/256) - Low byte:
STP-(256*INS)(equivalent toSTP MOD 256)
This avoids the MOD operator (absent on some dialects) and is a common Sinclair BASIC technique for packing 16-bit values.
Screen Region Parameters
| Key | Option | Start (STP) | Bytes (NOB) | Notes |
|---|---|---|---|---|
| 1 | Full screen | 16384 | 6192 | 6144 pixels + 48 attribute bytes |
| 2 | Top third | 16384 | 2045 (typo) | Should be 2048; “204S” is a listing error |
| 3 | Middle third | 163S4+2048 (typo) | 2048 | “163S4” should be 16384 |
| 4 | Bottom third | 16384+4096 | 2096 | Likely should be 2048; 2096 is suspicious |
Bugs and Anomalies
- Line 50 —
NOB=204S: The literal204Sis not a valid numeric token; this is almost certainly a transcription error for2048(the digit 8 rendered as S). - Line 60 —
LET STP=163S4+2048: Similarly,163S4is a garbled16384(8→S substitution). - Line 70 —
NOB=2096: A bottom-third capture should be 2048 bytes; 2096 would overrun the pixel region into attributes or beyond. - Line 360 —
INKEY*: The*should be$(INKEY$); another transcription artifact. - Line 330 —
REM MSB sou rce: A space in a REM comment — harmless but suggests the listing was typed or OCR’d with errors. - Line 90 — screen fill
"\`.": The backtick is the£character in Sinclair character sets; this fills the screen with£.pairs as a test pattern. - Full-screen NOB=6192: The standard Spectrum display is 6912 bytes (6144 pixels + 768 attributes). 6192 only covers 6144 + 48 attribute bytes, leaving most attributes uncaptured. This appears intentional for a partial-attribute save but is not documented in the menu.
Notable Techniques
CLEAR 29999places the BASIC stack and the machine code buffer (at 30000 and 65356) safely outside the display file and program area.- Re-using the partially-POKEd routine for both directions (capture and restore) by only overwriting the operand bytes reduces code and POKE count.
PRINT #0;AT 1,0;writes status messages to the lower screen (stream 0) without disturbing the captured display contents in the upper screen.- The
SAVE "SCRN" CODE STP,NOBat line 9998 uses the runtime variables directly, so the correct region is always saved regardless of which menu option was chosen.
Content
Source Code
1 CLEAR 29999
10 PRINT "0 - Load SCRN display file"'"1 - Full-screen store"'"2 - Top screen store"'"3 - Middle screen store"'"4 - Bottom screen store"
20 PAUSE 0: LET k$=INKEY$: IF k$="" THEN GO TO 20
30 IF k$="0" THEN CLS : LOAD "SCRN" CODE : PRINT "Any key to continue": PAUSE 0: RUN
40 IF k$="1" THEN LET STP=16384: LET INS=INT (STP/256) : LET NOB=6192: LET INB=INT (NOB/256): GO TO 80
50 IF k$="2" THEN LET STP=16384: LET INS=INT (STP/256): LET NOB=204S: LET INB=INT (NOB/256): GO TO 80
60 IF k$="3" THEN LET STP=163S4+2048: LET INS=INT (STP/256): LET NOB=2048: LET INB=INT (NOB/256) : GO TO 80
70 IF k$="4" THEN LET STP=16384+4096: LET INS=INT (STP/256): LET NOB=2096: LET INB=INT (NOB/256)
80 CLS : REM Create screen-fill
90 FOR n=1 TO 704: PRINT "`.";:NEXT n
100 FOR n=0 TO 21: PRINT AT n,0;n: NEXT n
110 REM Defines selected lines/cols and copies from DFILEl to RAM
120 POKE 65356,1: REM LD BC,no. of bytes to move
130 POKE 65357,NOB-(256*INB) : REM n LSB
140 POKE 65358,INB: REM n MSB
150 POKE 65359,17: REM LD DE,destination address 30000
160 POKE 65360,48: REM n LSB
170 POKE 65361,117: REM n MSB
180 POKE 65362,33: REM LD HL,source address
190 POKE 65363,STP-(256*INS) : REM n LSB
200 POKE 65364, INS: REM n MSB
210 POKE 65365,237: REM ED prefix
220 POKE 65366,176: REM LDIR block-move
230 POKE 65367,201: REM RETurn
240 RANDOMIZE USR 65356: REM Call block-move routine
250 PRINT #0;AT 1,0; "Any key to continue": PAUSE 0
260 REM Moves RAM data to DFILE1
270 CLS
280 POKE 65357, NOB-(INB*256) : REM LSB no. bytes
290 POKE 65358, INB: REM MSB nobytes
300 POKE 65360,STP-(256*INS) : REM LSB dest
310 POKE 65361, INS: REM MSB dest
320 POKE 65363,48: REM LSB source in RAM (30000)
330 POKE 65364,117: REM MSB sou rce in RAM (30000)
340 RANDOMIZE USR 65356
350 PRINT #0;"Key 5 to save or m for menu"
360 PAUSE 0: IF INKEY*="5" THEN PRINT #0;AT 0,0,,,,: GO TO 9998
370 RUN
9997 SAVE "SCRNmove" LINE 1: STOP
9998 SAVE "SCRN "CODE STP,NOB
9999 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
