Code Mover

Developer(s): Chuck Dawson
Date: 1985
Type: Program
Platform(s): TS 2068

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:

  1. Setup (lines 10–50): Collect source address A1, destination A2, and length L from the user; load two opcode lookup tables into arrays A(26) and B(8) from DATA.
  2. 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.
  3. Block copy (line 150): After all patches are applied in-place, copy the patched bytes to the new location with a PEEK/POKE loop.

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.

ArrayValues (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, 123ED 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 ADC lies outside the source block (ADC < A1 or ADC > A1+L).
  • Otherwise recalculate: ADC = ADC - A1 + A2, then POKE the new low and high bytes back. Advance I by 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).
  • RESTORE before the first READ loop 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 runs FOR I=0 TO L-1; the off-by-one between these means the scan covers L+1 bytes but only L bytes are copied — a minor anomaly that could cause the very last byte to be scanned for opcodes but not transferred.

Content

Appears On

This tape is a compilation of programs from user group members (Robert Burton, David Baulch, Frank Bouldin, Chuck Dawson, Ryan

Related Products

Related Articles

Related Content

Image Gallery

Code Mover

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.

Scroll to Top