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:
- Machine code store — Line 1 is a
REMstatement whose body contains raw Z80 opcodes. Because the ZX Spectrum/TS2068 storesREMdata verbatim in RAM, the bytes are directly executable starting at the address of the first byte after theREMtoken. - Input loop — Lines 20–60 form a tight polling loop:
INKEY$is sampled at line 20, then a cascade ofIF/GOTOchains dispatches to the appropriate machine code entry point(s). - Housekeeping — Lines 70–80 provide a
SAVEand aGOTO 5(which jumps toLIST 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 00inside aDJNZloop —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
REMline 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
DJNZloop withLD (HL),0efficiently blanks the newly exposed row or column. - Line 5 LIST 35:
GOTO 5at 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 toGOTO 20at 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\1Cat 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 5Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
