--- title: "Code Mover" id: 60850 type: "computer_media" slug: "code-mover" url: "http://localhost/computer_media/code-mover/" markdown_url: "http://localhost/computer_media/code-mover.md" published_at: "2025-10-06T12:15:23+00:00" modified_at: "2026-03-30T21:41:29+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2025/10/code-mover.png" excerpt: "A BASIC tool that relocates Z80 machine code to a new address and automatically patches every internal absolute address so nothing breaks." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Chuck Dawson" slug: "chuck-dawson" taxonomy: "indiv" url: "http://localhost/indiv/chuck-dawson/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 60550 title: "Fort Worth TS2068 Club Library Tape" type: "computer_media" url: "http://localhost/computer_media/fort-worth-ts2068-club-library-tape/" media_type: "Program" programmers: - name: "Chuck Dawson" slug: "chuck-dawson" taxonomy: "indiv" url: "http://localhost/indiv/chuck-dawson/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Code%20Mover%20%281985%29%28Dawson%2C%20Chuck%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2025/10/code-mover.png" media_type_tags: "Programming, Utility" --- # Code Mover 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. | 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 `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. ## 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+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 ```