--- title: "2068 Display Block Moves" id: 59979 type: "computer_media" slug: "2068-display-block-moves" url: "http://localhost/computer_media/2068-display-block-moves/" markdown_url: "http://localhost/computer_media/2068-display-block-moves.md" published_at: "2025-06-14T11:57:58+00:00" modified_at: "2026-04-03T07:58:00+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A menu-driven screen capture utility that uses a POKEd Z80 LDIR routine to save and restore full or partial display regions to a RAM buffer." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" - name: "Type-in program" slug: "type-in-program" taxonomy: "post_tag" url: "http://localhost/tag/type-in-program/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Robert Hartung" slug: "robert-hartung" taxonomy: "indiv" url: "http://localhost/indiv/robert-hartung/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Program" programmers: - name: "Robert Hartung" slug: "robert-hartung" taxonomy: "indiv" url: "http://localhost/indiv/robert-hartung/" download_url: "https://archive.org/download/timex-sinclair-software-archive/2068%20Display%20Block%20Moves.zip" mediadate: "1993" images: - url: "http://localhost/wp-content/uploads/2025/06/2068-display-block-moves.png" article_media: - id: 22204 title: "2068 Display Block Moves" type: "article" url: "http://localhost/article/2068-display-block-moves/" media_type_tags: "Programming" --- # 2068 Display Block Moves 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. 1. **Lines 1–20:**`CLEAR 29999` reserves RAM above address 29999; menu display and keypress wait loop. 2. **Lines 30–70:** Dispatch on key `k$`; compute `STP` (start address), `INS` (address high byte), `NOB` (number of bytes), `INB` (bytes high byte) for each region option. 3. **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. 4. **Lines 120–240:** POKE a Z80 LDIR routine into addresses 65356–65367, then call it with `RANDOMIZE USR 65356`. 5. **Lines 260–370:** Reverse the LDIR arguments (source and destination swapped) to restore the buffer back to screen, then offer save or menu return. 6. **Lines 9997–9999:**`SAVE "SCRNmove" LINE 1` saves the BASIC program; `SAVE "SCRN" CODE STP,NOB` saves 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 to `STP 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 literal `204S` is not a valid numeric token; this is almost certainly a transcription error for `2048` (the digit 8 rendered as S). - **Line 60 — `LET STP=163S4+2048`:** Similarly, `163S4` is a garbled `16384` (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 29999` places 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,NOB` at line 9998 uses the runtime variables directly, so the correct region is always saved regardless of which menu option was chosen. ## 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 ```