This BASIC program relocates a Z80 machine code routine from one RAM address range to another by copying bytes and patching any absolute 16-bit addresses that fall within the original range. It scans each byte of the source block, checking it against a table of 19 Z80 opcodes that use absolute 16-bit addresses (such as JP, CALL, LD (nn),HL, and similar instructions). When a match is found and the embedded address lies within the old block, the address is adjusted by the relocation offset and stored at the new location. The program uses RESTORE-less sequential READs from an inline DATA statement to check each opcode candidate, iterating up to 19 times per byte. A final CLEAR (ns-1) is issued to protect the relocated block from BASIC’s memory management.
Program Analysis
Program Structure
The program is compact, spanning lines 9900–9965. Lines 9900–9905 are REMs establishing authorship. Line 9910 presents an explanatory banner. Line 9915 collects three inputs: new start address (ns), old start address (os), and old end address (oe). Lines 9925–9955 form the main relocation loop. Line 9955 also issues a CLEAR to protect the new block. Line 9960 halts, and line 9965 saves the program.
Relocation Algorithm
The relocation offset a is calculated at line 9920 as ns - os. The outer FOR i = os TO oe loop walks every byte of the source block. For each byte, the 16-bit word at i+1/i+2 is pre-fetched into v (line 9930), and the byte at i is stored in ii (line 9935). An inner loop of 19 iterations READs successive values from the DATA statement and compares each to ii.
If the current opcode matches a known absolute-address opcode AND the embedded 16-bit address v falls within [os, oe], the byte is copied to i+a, the adjusted low byte is POKEd to i+1+a, and the adjusted high byte to i+2+a. Then i is advanced by 2 extra positions and the loop continues at 9955. Otherwise the byte is copied verbatim (line 9950).
Opcode Table
The DATA statement at line 9935 contains 19 Z80 opcode bytes that encode instructions using an inline 16-bit address operand:
| Decimal | Hex | Z80 Mnemonic(s) |
|---|---|---|
| 17 | $11 | LD DE,nn |
| 33 | $21 | LD HL,nn |
| 34 | $22 | LD (nn),HL |
| 42 | $2A | LD HL,(nn) |
| 50 | $32 | LD (nn),A |
| 83 | $53 | LD D,E (false positive risk) |
| 91 | $5B | LD E,E (false positive risk) |
| 115 | $73 | LD (HL),E (false positive risk) |
| 194 | $C2 | JP NZ,nn |
| 195 | $C3 | JP nn |
| 196 | $C4 | CALL NZ,nn |
| 202 | $CA | JP Z,nn |
| 204 | $CC | CALL Z,nn |
| 205 | $CD | CALL nn |
| 210 | $D2 | JP NC,nn |
| 212 | $D4 | CALL NC,nn |
| 218 | $DA | JP C,nn |
| 220 | $DC | CALL C,nn |
| 242 | $F2 | JP P,nn |
Values 83, 91, and 115 ($53, $5B, $73) are register-to-register LD instructions in the Z80 and do not actually take a 16-bit operand. Their inclusion appears to be an error inherited from the original Toronto TTSU listing, and could corrupt bytes following such opcodes if the coincidental address check happens to pass.
Notable Techniques and Idioms
- The
DATAstatement is placed mid-line alongside theREADand loop, exploiting the fact that BASIC reads DATA sequentially; however, because there is noRESTORE, the DATA pointer advances globally each iteration of the outer loop — meaning only the first byte checked in each outer iteration will correctly read from the start of the table. This is a significant bug: after the first pass through the inner loop consumes all 19 DATA values, subsequent outer iterations will find no more DATA to READ and will cause a “Data exhausted” error. - The 16-bit address split uses the idiom
PEEK(i+1) + 256*PEEK(i+2)for little-endian 16-bit assembly, and the inverseINT((v+a)/256)/(v+a) - 256*INT((v+a)/256)for splitting back to bytes. CLEAR (ns-1)at line9955sets RAMTOP below the new block, protecting it from BASIC memory operations.- The use of line numbers in the 9900s and the SAVE with
LINE 9905suggests the program is designed as a utility overlay that won’t interfere with line numbers of user programs. - The
::on line9955is a TS2068 multi-statement separator used to chainNEXT iandCLEARon one line.
Bugs and Anomalies
- DATA exhaustion: As noted above, the DATA pointer is not RESTOREd before each inner loop iteration, so the program will crash with a data error after the first outer loop iteration completes the full 19-item inner scan.
- Spurious opcodes: $53, $5B, and $73 are single-byte register LDs with no 16-bit operand. Matching them could cause the following two bytes to be incorrectly treated as an address and patched.
- Missing opcodes: Several absolute-address Z80 instructions are absent from the table, including
JP (HL)-related extended ops,LD (nn),BC(ED prefix), and conditional calls/jumps for the remaining condition codes (PE, M, etc., e.g. $E2, $E4, $EA, $EC, $F4, $FA, $FC). - No RESTORE: A
RESTORE 9935should precede or be inside the outer loop to reset the DATA pointer for each byte examined.
Content
Image Gallery
Source Code
9900 REM THIS TAPE WAS DONATED BY TORONTO TIMEX SINCLAIR USER'S GROUP. Modified & copied by A. E. Gedris for the "2068" computer
9905 REM RELOCATE
9910 CLS : PRINT "Note:- This program will"'"relocate a machine code program to a new location in RAM. Since it is in BASIC, it is rather slow."''"Follow the instructions."
9915 INPUT "Enter new start address"'ns'"Enter old start address"'os'"Enter old end address",oe
9920 LET a=ns-os
9925 FOR i=os TO oe
9930 LET v=PEEK (i+1)+256*PEEK (i+2)
9935 LET ii=PEEK i: FOR f=1 TO 19: READ iii: DATA 17,33,34,42,50,83,91,115,194,195,196,202,204,205,210,212,218,220,242
9940 IF iii=ii AND v>=os AND v<=oe THEN POKE i+a,PEEK i: POKE i+1+a,(v+a)-256*INT ((v+a)/256): POKE i+a+2,INT ((v+a)/256): LET i=i+2: GO TO 9955
9945 NEXT f
9950 POKE i+a,PEEK i
9955 NEXT i:: CLEAR (ns-1)
9960 STOP
9965 SAVE "RELOCATE" LINE 9905
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.