This program constructs and executes a 12-byte Z80 machine code routine that relocates a block of memory from one address to a higher address. The machine code uses the Z80’s LDIR instruction (opcode ED B0), which performs an automatic block copy by loading DE with the destination, HL with the source, and BC with the byte count. The BASIC loader accepts the source address, destination address, and byte count from the user, splits each 16-bit value into high and low bytes, and POKEs them directly into the DATA statement’s variable placeholders before writing them to address 64700. A notable feature is the POKE to system variable addresses 23730–23731 (RAMTOP), which adjusts the top of BASIC RAM to protect the relocated code from being overwritten.
Program Analysis
Program Structure
The program is organized into three logical sections: a setup subroutine at line 1000, the main loader body at lines 10–60, and a save/verify utility at line 9000. Execution begins by jumping to the subroutine at line 1000 via GO SUB 1000 at line 5, which collects user input and adjusts RAMTOP before returning. Lines 10–20 then restore and iterate through the DATA statement, POKEing 12 bytes into memory starting at address 64700. Line 50 immediately executes the newly installed machine code via RANDOMIZE USR 64700.
Machine Code Payload
The 12-byte routine embedded in the DATA statement at line 40 is a compact Z80 LDIR block-copy sequence:
| Offset | Bytes | Mnemonic | Description |
|---|---|---|---|
| 0 | 17, lfrom, hfrom | LD DE, from | Load source address into DE |
| 3 | 33, lto, hto | LD HL, to | Load destination address into HL |
| 6 | 1, lbytes, hbytes | LD BC, bytes | Load byte count into BC |
| 9 | 237, 176 | LDIR | Block copy DE→HL, decrement BC until zero |
| 11 | 201 | RET | Return to BASIC |
The LDIR instruction (ED B0) copies bytes from the address in DE to the address in HL, incrementing both pointers and decrementing BC after each byte, repeating until BC reaches zero. Note that the DATA statement uses BASIC variable names (lfrom, hfrom, etc.) rather than literal numbers; these are resolved to their current values at READ time, which is after the subroutine at line 1000 has set them.
Address Splitting Idiom
Lines 1010–1080 use a standard technique to decompose 16-bit addresses into high and low bytes. For any value n, the high byte is INT(n/256) and the low byte is n - (high*256). This is applied identically to the source address (from), destination address (to), and byte count (bytes), producing six variables used directly in the DATA statement.
RAMTOP Adjustment
Line 1090 modifies system variables at addresses 23730 and 23731, which hold RAMTOP (the top of BASIC memory). By setting RAMTOP to one byte below the destination address, the code protects the relocated block from being overwritten by BASIC. The line includes a borrow correction: IF (lto-1)<0 THEN POKE 23731,(hto-1) handles the case where the low byte of the destination is zero, requiring a carry into the high byte. However, there is a subtle bug: the POKE 23730,(lto-1) executes unconditionally and will POKE a value of -1 (interpreted as 255) when lto is 0, which is actually the correct low byte, so the behavior is coincidentally correct on most systems.
Notable Techniques
- The
RESTOREat the start of line 20 ensures theDATApointer is reset before reading, making the loader safely re-runnable. - The
DATAstatement uses BASIC variable names instead of literals, relying on the interpreter evaluating variables atREADtime — a clean way to parameterize machine code bytes without string manipulation or extra POKE loops. - Line 9000 provides a
SAVE/VERIFYutility with aPAUSE 0prompt, allowing the user to rewind the tape before verification. - The program is designed so the user can run it, then type
NEWto clear BASIC while the machine code survives above RAMTOP.
Bugs and Anomalies
- Line 1200 (
STOP) is unreachable dead code; execution from line 1090 returns via line 1100, and there is no path to line 1200. - The Z80 LDIR instruction increments both DE (source) and HL (destination) during the copy. If the source and destination ranges overlap and the source is at a lower address than the destination, bytes will be corrupted mid-copy. The REM at line 1 specifically warns this routine is intended only for copying to a higher address, which partially mitigates this, but does not fully prevent overlap issues.
- No validation is performed on user inputs, so entering a byte count or address of zero, or a source address higher than the destination with overlapping ranges, could produce incorrect results silently.
Content
Source Code
1 REM This is a program to relocate bytes from one address to another. Specifically a machine code from a lower address to another higher address.
2 REM This is a machine code program to do that. I am setting it up to locate this code at 64700. The transfer address will be variable but generally at 60000.
3 REM To use this program you enter the information asked for. When the program is completed you can then delete this loader with NEW.
4 REM You then load in the program you want relocated, key in RANDOMIZE USR 64700 and your program will be relocated above RAMTOP.
5 GO SUB 1000
10 LET s=64700
20 RESTORE : FOR j=0 TO 11: READ a: POKE s+j,a: NEXT j
40 DATA 17,lfrom,hfrom,33,lto,hto,1,lbytes,hbytes,237,176,201
50 RANDOMIZE USR 64700
60 STOP
1000 INPUT "Enter address to be moved from ";from
1010 LET hfrom=INT (from/256)
1020 LET lfrom=from-(hfrom*256)
1030 INPUT "Enter address to be moved to ";to
1040 LET hto=INT (to/256)
1050 LET lto=to-(hto*256)
1060 INPUT "Enter number of bytes to be transferred ";bytes
1070 LET hbytes=INT (bytes/256)
1080 LET lbytes=bytes-(hbytes*256)
1090 POKE 23730,(lto-1): POKE 23731,hto: IF (lto-1)<0 THEN POKE 23731,(hto-1)
1100 RETURN
1200 STOP
9000 SAVE "Relocate": PRINT "Rewind & key ENTER to verify.": PAUSE 0: VERIFY "Relocate"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
