gp2000
Experienced Member
Writing Z-80 code that works no matter where it is loaded is difficult as the Z-80 has no relative CALL instruction and no direct access to the program counter. Most of the time there's no need for such flexibility, but driver programs do need to relocate themselves and it can be handy if you embed Z-80 code in BASIC strings or variables.
I remembered 80 Microcomputing had a good article about this and while looking for it I found a follow-up:
80 Microcomputing, April 1983, "Understanding Relocatable Code"
80 Microcomputing. April 1985, "Relocatable Programs: Microcomputing's Hoboes"
Besides some good techniques it points out that the BASIC ROM has a critically important subroutine for relocation at $000B. A "CALL $000B" will return with HL set to the address after the call. The routine itself is just two instructions: "POP HL; JP (HL)". A self-relocating program can't include this subroutine itself because it wouldn't know where it is. On a Model 4 LDOS and TRSDOS provide the @WHERE system call (SVC-07) that does the same thing.
Here's the puzzle part: Can you figure out a way to get the program counter in code without using these known subroutines?
I came up with one technique. I'm ever hopeful that there is some other way but in any case I'll post my solution in a few days so as not to spoil anyone's fun.
I know from a practical perspective it is almost a given that HL or some other register will contain the location of loaded code. After all, whoever loaded you had to know here to start and "JP (HL)" is as good a way to get to your code as anything else, but my solution doesn't depend on anything like that.
Also be careful not to depend on any hidden addresses. It might seem like "label: LD HL,label" is an easy way around the problem but "label" will be put in as an absolute address at assembly time and won't tell you anything about where the code was actually loaded.
Although it lacks in PC-relative addressing modes, the Z-80 has lots of registers and a relative jump so at least writing the startup code in a relocatable fashion isn't too hard itself.
I remembered 80 Microcomputing had a good article about this and while looking for it I found a follow-up:
80 Microcomputing, April 1983, "Understanding Relocatable Code"
80 Microcomputing. April 1985, "Relocatable Programs: Microcomputing's Hoboes"
Besides some good techniques it points out that the BASIC ROM has a critically important subroutine for relocation at $000B. A "CALL $000B" will return with HL set to the address after the call. The routine itself is just two instructions: "POP HL; JP (HL)". A self-relocating program can't include this subroutine itself because it wouldn't know where it is. On a Model 4 LDOS and TRSDOS provide the @WHERE system call (SVC-07) that does the same thing.
Here's the puzzle part: Can you figure out a way to get the program counter in code without using these known subroutines?
I came up with one technique. I'm ever hopeful that there is some other way but in any case I'll post my solution in a few days so as not to spoil anyone's fun.
I know from a practical perspective it is almost a given that HL or some other register will contain the location of loaded code. After all, whoever loaded you had to know here to start and "JP (HL)" is as good a way to get to your code as anything else, but my solution doesn't depend on anything like that.
Also be careful not to depend on any hidden addresses. It might seem like "label: LD HL,label" is an easy way around the problem but "label" will be put in as an absolute address at assembly time and won't tell you anything about where the code was actually loaded.
Although it lacks in PC-relative addressing modes, the Z-80 has lots of registers and a relative jump so at least writing the startup code in a relocatable fashion isn't too hard itself.