This program relocates a block of Z80 machine code from one memory address to another, automatically patching any internal absolute addresses so the relocated code continues to work correctly. The user inputs the source address, destination address, and byte length; the program then scans every byte, checking it against a table of Z80 opcodes that take 16-bit address operands (stored in the DATA statement at line 160). When a matching opcode is found, the two-byte operand is read, tested to see whether it points within the code block being moved, and if so recalculated relative to the new base address before being POKEd back. A separate path at line 2000 handles the ED-prefixed extended opcodes (such as ED 43 and ED 4B) that also carry 16-bit addresses. After patching, the entire block is copied byte-for-byte to the new location using a POKE/PEEK loop at line 150.
Program Structure
The program has three distinct phases:
- Setup (lines 10–50): Collect source address
A1, destinationA2, and lengthLfrom the user; load two opcode lookup tables into arraysA(26)andB(8)fromDATA. - Patch scan (lines 60–2020): Walk every byte in the source block, identify opcodes that embed 16-bit addresses, and re-base those addresses if they point inside the block.
- Block copy (line 150): After all patches are applied in-place, copy the patched bytes to the new location with a
PEEK/POKEloop.
Opcode Tables
The DATA statement at line 160 contains 34 values. The first 26 are loaded into A(26) and represent single-byte Z80 opcodes that are followed by a 16-bit address operand. The next 8 values are loaded into B(8) and are the second bytes of ED-prefixed instructions that also carry 16-bit operands.
| Array | Values (decimal) | Z80 instructions (examples) |
|---|---|---|
A() | 1, 17, 33, 34, 42, 49, 50, 58, 194–252 (even calls/jumps) | LD BC,nn; LD DE,nn; LD HL,nn; LD (nn),HL; JP cc,nn; CALL cc,nn etc. |
B() | 67, 75, 83, 91, 99, 107, 115, 123 | ED 43 LD (nn),BC; ED 4B LD BC,(nn); and analogues for DE, HL, SP |
Address-Patching Logic
When an opcode from A() is matched at byte offset I, lines 1000–1020 execute:
- Read the 16-bit little-endian operand:
ADC = PEEK(I+1) + 256*PEEK(I+2). - Skip patching if
ADClies outside the source block (ADC < A1orADC > A1+L). - Otherwise recalculate:
ADC = ADC - A1 + A2, then POKE the new low and high bytes back. AdvanceIby 2 to skip the operand bytes.
The ED-prefix path at lines 2000–2020 increments I past the ED byte, then checks the next byte against B() before joining the same patch routine at line 1000.
Progress Display
Line 35 prints a static label "# OF BYTES PROCESSED" at a fixed screen position. Line 140 updates the count at AT 10,14 on every iteration of the outer loop, giving a live byte counter. Line 150 also prints the copy-phase counter with trailing spaces to erase stale digits.
Notable Techniques
- Patching is done in-place in the source block before the final copy; this means the source block is modified as a side effect.
- Little-endian 16-bit assembly/disassembly uses the standard BASIC idiom:
high = INT(ADC/256),low = ADC - 256*INT(ADC/256). RESTOREbefore the firstREADloop ensures the data pointer is reset regardless of earlier activity.- The outer loop at line 60 runs
FOR I=A1 TO A1+L(inclusive of the last byte), while the copy loop at line 150 runsFOR I=0 TO L-1; the off-by-one between these means the scan coversL+1bytes but onlyLbytes are copied — a minor anomaly that could cause the very last byte to be scanned for opcodes but not transferred.
Content
Source Code
1 REM SAVE "code mover": REM © by Chuck Dawson, 1985; ALL RIGHTS RESERVED
2 REM 1.LOAD CODE TO BE MOVE 2.RUN PROGRAM(IT TAKES A WHILE) 3. SAVE CODE TO TAPE FROM NEW LOCATION
10 INPUT "Enter current starting address ";A1
20 INPUT "Enter new starting address ";A2
30 INPUT "Enter length of code ";L
35 PRINT AT 9,6;"# OF BYTES PROCESSED"
40 DIM A(26): RESTORE : FOR I=1 TO 26: READ X: LET A(I)=X: NEXT I
50 DIM B(8): FOR I=1 TO 8: READ X: LET B(I)=X: NEXT I
60 FOR I=A1 TO A1+L
65 IF PEEK I=237 THEN GO TO 2000
70 FOR J=1 TO 26
90 IF PEEK I=A(J) THEN GO TO 1000
100 NEXT J
140 PRINT AT 10,14;I-A1: NEXT I
150 FOR I=0 TO L-1: POKE A2+I,PEEK (A1+I): PRINT AT 10,14;I;" ": NEXT I: STOP
160 DATA 1,17,33,34,42,49,50,58,194,195,196,202,204,205,210,212,218,220,226,228,234,236,242,244,250,252,67,75,83,91,99,107,115,123
1000 LET ADC=PEEK (I+1)+256*PEEK (I+2)
1010 IF ADC<A1 OR ADC>(A1+L) THEN GO TO 140
1015 LET ADC=ADC-A1+A2
1020 POKE I+2,INT (ADC/256): POKE I+1,ADC-256*INT (ADC/256): LET I=I+2: GO TO 140
2000 LET I=I+1: FOR J=1 TO 8
2010 IF PEEK I=B(J) THEN GO TO 1000
2020 NEXT J: GO TO 140
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
