--- title: "Screen Scrolling Routine" id: 57148 type: "computer_media" slug: "unknown-machine-code-routine-4" url: "http://localhost/computer_media/unknown-machine-code-routine-4/" markdown_url: "http://localhost/computer_media/unknown-machine-code-routine-4.md" published_at: "2024-10-04T00:52:07+00:00" modified_at: "2026-03-30T21:19:28+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/97_Unk.png" excerpt: "Eight-directional movement powered by hand-assembled Z80 machine code hidden inside a REM statement — discover how four tight routines combine to cover every direction." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_contents: - id: 56733 title: "Timex Sinclair Public Domain Library Tape 1002" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1002/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/97_Unk.png" media_type_tags: "Programming" --- # Screen Scrolling Routine This program implements a character-movement or screen-scrolling system driven entirely by machine code routines embedded in the REM statement at line 1. The BASIC shell reads a keypress via INKEY$ and dispatches to one of four machine code entry points (USR 16514, 16538, 16567, 16588) depending on the key pressed, covering eight directions: J, M, I, K for cardinal directions and U, O, ., N for diagonals (combining two cardinal calls). The machine code, stored beginning at address 16514 (0x4082, just inside the REM statement data), performs direct memory operations including LDIR and LDDR block moves, consistent with scrolling a screen region or shifting a sprite/character buffer. The SAVE at line 70 uses an inverse-video character in the filename as an auto-run marker. *** ## Program Analysis ### Program Structure The program has three logical layers: 1. **Machine code store** — Line 1 is a `REM` statement whose body contains raw Z80 opcodes. Because the ZX Spectrum/TS2068 stores `REM` data verbatim in RAM, the bytes are directly executable starting at the address of the first byte after the `REM` token. 2. **Input loop** — Lines 20–60 form a tight polling loop: `INKEY$` is sampled at line 20, then a cascade of `IF`/`GOTO` chains dispatches to the appropriate machine code entry point(s). 3. **Housekeeping** — Lines 70–80 provide a `SAVE` and a `GOTO 5` (which jumps to `LIST 35`) for re-entry/display purposes. ### Machine Code Entry Points Line 1 begins at address 16384 (0x4000). The `REM` token occupies one byte and the two-byte length field two more, placing the first opcode at 0x4003 (16387). However, the BASIC calls target four distinct addresses: | USR Address | Hex | Key(s) | Role (inferred) | | --- | --- | --- | --- | | 16514 | 0x4082 | I, U, O | Scroll/move up | | 16538 | 0x409A | M, ., N | Scroll/move down | | 16567 | 0x40B7 | K, ., N | Scroll/move right | | 16588 | 0x40CC | J, U, N | Scroll/move left | The REM data is 59 bytes long (the trailing bytes `\1D\1E\1F\20\21\22\23\24\25\1C` are likely padding or lookup data). The entry points are therefore spread across the REM body and each routine ends with a `C9` (RET) opcode. ### Z80 Routine Analysis Disassembling the REM bytes reveals four subroutines that use the Z80 block-move instructions `LDIR` (ED B0) and `LDDR` (ED B8) to shift screen memory. Common patterns observed: - `2A 0C 40` — `LD HL,(0x400C)`: loads a pointer from within the REM area itself (self-referential data). - `11 21 00` — `LD DE,0x0021` (33 decimal): one character row width on a 32-column display plus one, used as an offset for vertical scrolling. - `ED B0` / `ED B8` — `LDIR`/`LDDR`: bulk copy forward/backward for up/down scroll directions. - `01 B5 02` — `LD BC,0x02B5` (693): the byte count, matching the number of bytes in roughly 21.6 character rows — consistent with scrolling most of the display. - `36 00` inside a `DJNZ` loop — `LD (HL),0`: clears the vacated edge row/column after a scroll. - `06 20` — `LD B,0x20` (32): loop counter for clearing 32 columns. - `06 16` — `LD B,0x16` (22): loop counter for clearing 22 rows. ### Key Mapping and Diagonal Movement The eight keys map naturally to a numeric-keypad layout: | Key | Direction | Routines called | | --- | --- | --- | | I | Up | USR 16514 | | K | Right | USR 16567 | | M | Down | USR 16538 | | J | Left | USR 16588 | | U | Up-Left | USR 16514 + USR 16588 | | O | Up-Right | USR 16514 + USR 16567 | | . | Down-Right | USR 16538 + USR 16567 | | N | Down-Left | USR 16538 + USR 16588 | Diagonal movement is achieved by sequentially calling two cardinal-direction routines in BASIC rather than implementing combined diagonals in machine code, keeping the MC routines simple and orthogonal. ### Notable Techniques - **REM-as-code store**: embedding machine code in a `REM` line is a standard technique that keeps the code in a known, fixed address and prevents the BASIC editor from interpreting the bytes. - **Self-referential data pointer**: `LD HL,(0x400C)` reads a word from within the REM statement itself, allowing the routine to find its own working data without hardcoding a separate address. - **LDIR/LDDR for scrolling**: using the Z80 block-copy instructions is the fastest pure-Z80 approach to scrolling a rectangular region of screen memory. - **DJNZ clear loop**: after a scroll, a `DJNZ` loop with `LD (HL),0` efficiently blanks the newly exposed row or column. - **Line 5 LIST 35**: `GOTO 5` at line 80 causes the program listing to scroll to line 35, effectively hiding the machine code REM from casual view on restart. - **INKEY$ polling without PAUSE**: line 20 samples the keyboard every BASIC iteration with no delay, giving the fastest possible response at the cost of no key-repeat debounce. ### Potential Anomalies - Line 57 is referenced by `GOTO 57` (line 54) but does not exist. After the cascade of diagonal-key checks, control falls through to `GOTO 20` at line 60 — the missing line 57 simply causes BASIC to continue to the next higher line, which is line 60. This is intentional and well-understood behaviour. - The trailing bytes `\1D\1E\1F\20\21\22\23\24\25\1C` at the end of the REM (control codes 28–37) are not reachable as code; they appear to be a lookup table or padding used as data by the routines. ## Source Code ``` 1 REM \2A\0C\40\23\E5\11\21\00\19\D1\01\B5\02\ED\B0\EB\06\20\36\00\23\10\FB\C9\2A\10\40\11\43\00\ED\52\E5\11\21\00\ED\52\D1\01\B5\02\ED\B8\EB\06\20\2B\36\00\10\FB\C9\2A\0C\40\06\16\C5\06\20\3E\00\23\4F\7E\71\10\FA\23\C1\10\F1\C9\2A\10\40\11\43\00\ED\52\06\16\C5\06\20\3E\00\2B\4F\7E\71\10\FA\2B\C1\10\F1\C9\1D\1E\1F\20\21\22\23\24\25\1C 5 LIST 35 20 LET D$=INKEY$ 25 IF D$="J" THEN RAND USR 16588 30 IF D$="M" THEN RAND USR 16538 35 IF D$="I" THEN RAND USR 16514 40 IF D$="K" THEN RAND USR 16567 41 IF D$="U" THEN GOTO 43 42 GOTO 45 43 RAND USR 16514 44 RAND USR 16588 45 IF D$="O" THEN GOTO 47 46 GOTO 49 47 RAND USR 16514 48 RAND USR 16567 49 IF D$="." THEN GOTO 51 50 GOTO 53 51 RAND USR 16538 52 RAND USR 16567 53 IF D$="N" THEN GOTO 55 54 GOTO 57 55 RAND USR 16538 56 RAND USR 16588 60 GOTO 20 70 SAVE "1009%7" 80 GOTO 5 ```