  
Code relocation for Z-80 Processors
    Ver 1.40.3  13 Jun 82

     This write-up and  associated  load module for  RELOC  are
copyright 1982,  Frans Van Duinen. Permission to use,  copy and
distribute  without charge  for personal,  non-commercial  pur-
poses is granted, provided this copyright notice is included.
     A commercial use  license, including  the  complete source
code  and  documentation in machine readable  form is available
for a  one-time fee of  $30.- under the  Reciprocal Enhancement
License.
-- Code Relocation --
     Machine code for the Z-80 processor  is not normally,  re-
locatable since certain instructions  specify absolute  addres-
ses. When the item  that such an instruction refers to is  mov-
ed, all instructions explicitly  specifying  that  address need
to be changed. Module RELOC, described in this write-up,  finds
all such instructions  in the module to be  relocated and chan-
ges the addresses as required.
     Code relocation  is particularly  useful for modules  such
as  keyboard, screen  and printer drivers and assembler subrou-
tines  for Basic programs.  Since these routines are in  memory
alongside  the main  program being executed,  for instance  the
Basic  Interpreter  and a  Basic program, it is  important that
they  are out  of the way. Typically these  routines are loaded
as high  in memory  as possible. Problems arise  when there are
several routines that need to be  loaded  in  varying  combina-
tions, or where the same  routine must be used on 16, 32 or 48K
RAM machines.
     By combining  each module with this  code  relocation  and
move routine, it can be  loaded into low memory (e.g. at 5200H)
and relocate itself to  the very top of available memory.  Once
there  the  module would protect its run-time  code  by setting
DOS's top  of  memory  pointer  (4049H for TRSDOS  and NEWDOS).
Provided the run-time code is at  the end of the module and the
initialization at the beginning,  only the run-time  code  need
be  retained  in memory and protected. Memory occupied by  ini-
tialization  and relocation code  can be  re-used  immediately.
RELOC is the module  that  makes all  this wonderful stuff pos-
sible.
     RELOC has 4 subroutines,  whose  entry point addresses are
at the very beginning of the module:
     +0   RELOC  - move  a section of code, including constants
areas,
                  find all absolute  address  instructions  and
change
                  all  addresses  referencing  the  section  of
code just moved.
                  uses RLCODE and RLADDR
     +3   RLINIT  - initialize for RLCODE and  RLADDR (the same
initialization
                  is performed by RELOC
     +6  RLCODE - analyze  instructions,  change their  operand
address
                  if  required, stop  at end  address specified
in
                  last call to RELOC or RLINIT; uses RLADDR
     +9  RLADDR  -  change the address  pointed at if it refer-
ences within
                  the  range  specified  on  the  last  call to
RELOC or RLINIT
-- RELOC, Main Relocation Routine --
     RELOC receives the information it  requires via  the stack
and returns  one parameter that way. On  input  the  stack must
contain:

    -Return address
    -Address of the section to be relocated ('old start')
    -Length of section to be relocated
    -Address  of  the last byte of area  where  code  is  to be
moved to ('new end')
       (Normally obtained from DOS's  top of memory  pointer as
being the
        last available byte)
    -Address  of  table of information  on constants in section
being moved, zero if not used
RELOC removes  these entries from the  stack and in their place
returns 'new start',  the  start address of the code in the new
location. ('New start' minus one may be used as the  new top of
memory  value for DOS.) The end address and length must be such
that address wrap-around (from FFFFH to 0000H) does not occur.
     The constants table  defines  those  sections of the  code
that  must  not  be interpreted  as  instructions  (but will be
moved). These constant areas  do not affect the 'old start' and
'old end'  addresses  used  by RLADDR. The  first byte  of this
table contains bit flags (7 is h/o bit):
   Bit 1 - Length of constants-preceding specified
       0 - Length of constants-following specified
Length-preceding is  a 2  byte  field (present only if bit 1 is
set),  that  specifies how many bytes at the beginning  of  the
section  of  code being  moved should be treated  as constants.
Length-following specifies the same  for constants at  the  end
of the section. All other bits in this byte must be zero.
     RELOC  does  not  save  any  registers.  RELOC  determines
whether the move is up-  or  down-memory,  and accordingly uses
an  LDDR  (load  decrementing) or  LDIR  (load incrementing, to
cope with possible  partial overlap of the  old  and new  loca-
tions. Overlap that would clobber RELOC  in its old location is
tested for; in this case RELOC  does not move or relocate code,
but rather  returns 'old  start'  as the 'new start' address on
the stack.  No  test is made to  ensure that the return address
(following the CALL to RELOC) is not affected.
     After moving the section  of code, RELOC adjusts the 'new'
start  and end addresses to exclude the constants preceding and
following. If  there is any code  left  to  be checked  for in-
structions  (i.e.  not  part of  constants preceding or follow-
ing), RELOC uses RLCODE to find  and  change  addresses.  RELOC
sets  up  the 'old start', 'old end',  'new end'  and  'offset'
that RLCODE and RLADDR need.
-- RLCODE, Find and Change Absolute Address in Instruction --
     When  RLCODE  gets control, it expects  certain fields  to
have been  initialized,  as would have been done by RELOC or by
RLINIT.  RLCODE expects HL> to  point  to the first instruction
to be  checked. On exit  HL>  points to  the first  instruction
that starts  following  the 'new  end'  address  (see  RLINIT),
other registers are  undefined  (not saved). If wrap-around oc-
curred <HL> contains 0000H, wich  may not be the beginning of a
new instruction. RLCODE will  not change the address in an  in-
struction that starts before  the 'new end' address but extends
beyond it.
     RLCODE analyzes the byte to which  HL> points to determine
the length of  the instruction (1,2,3 or 4 bytes)  and  whether
it  has an absolute  address  as  its  parameter.  Opcodes  are
analyzed  based  on  bit  patterns.  Invalid  opcodes  are  not
recognized, but rather are treated  based  on how certain  bits
in the  opcode  make them  look  like some  valid  instruction.
Non-standard opcodes,  i.e. those  not  formally documented  by
ZILOG  are  probably  handled correctly  since  the opcode  bit
pattern is consistent,  but if not,  thats too bad! When RLCODE
recognizes a 3 or 4 byte instruction  with an absolute address,
it calls RLADDR to change the address if required.
-- RLADDR, Change Address if in Range --
     When RLADDR  gets control, it expects HL> to point  to a 2
byte  absolute address (i.e. past the  1 or  2 byte opcode  for
the  instruction).  RLADDR  returns  <HL>  unchanged, but  does
change  <AF>. RLADDR checks if the  address stored at the loca-
tion  HL>  points to is whithin  the range 'old start'  to 'old
end'  (inclusive), where 'old start/end' are  as  set by RLINIT
or RELOC. If it  is, RLADDR adds the signed offset (also set up
by RLINIT or by RELOC), to change the address from a  reference
within  the old range to  the corresponding  reference  whithin
the new range; if it is not, the address is left unchanged.
-- RLINIT, Initialize for RLCODE and RLADDR --
     RLCODE  and  RADDR require  that certain fields have  been
initialized. RELOC  initializes those fields,  so if RLCODE  or
RLADDR are  used  subsequently for the same parameters,  RLINIT
is not required.  (This might  be where RELOC could not  handle
all  code  at once  because of bothersome constants in the mid-
dle.) RLCODE  and RLADDR may also  be used  to change reference
addresses  outside the code being moved (if indeed any code  is
moved).  In  this  case  RLINIT sets up the required constants.
RLINIT expects the following parameters on the stack:
    -Return address
    -Start reference address ('old start')
    -End reference address ('old end')
    -End address of code to be checked ('new end')
    -Offset from  old  to  new address (signed  2  byte  value,
normally 'new end' minus 'old end')
RLINIT removes these  parameters from the stack,  does not  re-
turn any parameters and does not save any registers.
     The end of code  address is set  up for RLCODE and  is al-
ways required, even  if  only RLADDR is  to be used; the corre-
sponding  start  of  code address is passed  to RLCODE in <HL>.
The  start and end  reference  address  ('old  start'  and 'old
end') are used to determine  which addresses are to be changed;
RLADDR will leave all addresses  referencing  outside  this in-
clusive range unchanged.  The offset  is added to all addresses
to be changed as a signed two byte value.
-- Constants Treated as Instructions --
     RLCODE  treats everything it  sees as an instruction. Spe-
cial provisions  are made in RELOC to  allow exclusion  of con-
stants at the  beginning or end  of a  section  of code. It may
still be  useful to have the ability to include constants as if
they  were instructions. There are two potential problems  that
we  will have  to cope with: 1) not recognizing  an instruction
immediately following a constant,  2) changing  a constant  be-
cause it is seen as  an  absolute memory reference instruction.
The first case  occurs  when the last byte  of  the constant is
recognized as the first byte of  a 2, 3 or 4 byte  instruction.
In this case  the beginning of the  actual instuction that fol-
lows the  constant would  be missed.  The  same would happen if
the  second  or third to last byte is recognized  as a 3  or  4
byte instruction. Because of the preponderance of  one and  two
byte  instructions in  the Z-80  instruction set,  RELOC resyn-
chronizes within a few  bytes with the actual  instructions.  A
problem would only  exist if the instruction missed,  or missed
in part, required its operand address to be changed.
     The second case may occur if  a byte of a constant is seen
as  the first byte  of an  instruction with an absolute address
and the next two  bytes  look like an address within the proper
range.
     In actual fact both  cases are very rare, except  possibly
for address constants  (which  may  take on  any  value).  Null
constants (00H)  are not affected  by  relocation, only  a  few
ASCII characters look  like  absolute address instructions, and
commonly  the last  constant in a  group is 00H  (e.g.  end  of
string  marker). This does depend on how  the module is  coded,
and it is possible to code it in  such  a way  that RELOC  will
trip up.
     The following codes are recognized  as multi-byte opcodes,
with absolute  address references in the 3 or 4 byte ones  (all
in hex):
- 2 bytes in length:  06  0E  10  16  18  1E
20  26  28  2E  30  36  3E  (ASCII characters)
C6  CB  CE  D3  D6  DB  DE  E6  EE F6  FE
- 3 bytes in length, and with absolute adress:  01  11
21  22  2A  31  32  3A  (ASCII characters)
C2  C3  C4  CA  CC  CD   D2  D4  DA  DC  E2  E4  EA  EC  F2  F4
 FA  FC
- 2,3 or 4 bytes:  DD  ED  FD; normally the second  byte is the
regular opcode for a 1, 2  or 3 byte code as  above,  e.g. DD21
is a 4 byte instruction with an absolute address.
-- How to set up RELOC --
     RELOC  was  developed on  a TRS 80,  Model  I  to load  at
5200H.  It  contains no references  to ROM, I/O devices or spe-
cific memory  locations,  and will run  on  any Z-80  processor
with RAM memory for RELOC to  load in. RELOC will not run on an
8080 processor.  The module was  developed  using Radio Shack's
M80  assembler  and L80  relocating, linking  loader,  but  for
purposes of this distribution is set up as a loadable module.
     RELOC  loads at 5200H  (RELOCCPM  loads at  0100H for  CPM
systems,  see CPM  section  below) and  normally  the code with
which  it is to  be  combined will be assembled  to  load right
behind it. This way RELOC and other initialization code  can be
jettisoned once the  module has  been moved to its final  loca-
tion (up-memory). If it  is desired to move  RELOC to a differ-
ent location before  linking  it, this can be done by executing
RELOC to relocate itself to the desired location. RELOC  cannot
be executed from  DOS directly, as the load  module's entry ad-
dress is set at 0000H (system reboot).
     The  debugger (DEBUG, DDT or whatever) can be used to  set
up the stack  with the information  RELOC  requires, and to set
up a CALL instruction to RELOC (CD 00 52H;  CD  03 10H for CPM)
in some unused spot  in memory.  Then, by executing that  call,
with a  breakpoint set right behind  it, RELOC will shuffle off
to the desired location. If the  wanted  new location  overlaps
with  the old one, two moves are required; one  to get clear of
overlap  with the  target area  and  one to move to  the actual
target.
     The following is a typical code  sequence  to use the rou-
tine RELOC to move some section  of code, not  including RELOC,
to the top of  available memory. The example uses a  2 byte top
of memory  pointer that points  to  the last byte of  available
memory.  Under  TRSDOS or  NEWDOS  on the TRS-80 model  I, this
field is at 4049H. For CPM systems 0006H may be used.
RELOC   EQU  5200H     ;Define RELOC entry point
                       ;Use 0103H for CPM
TOPMEM  EQU  4049H     ;DOS's top of memory pointer
                       ; use 0006H for CPM
MODLEN  EQU  MODEND-CONEX ;Length is end addr minus start addr

CONST:   DB   0011B      ;Constants table,  both preceding  and
following spec'd
        DW   CODE-CONEX ;First instruction  is  at  CODE, first
constant at CONEX
        DW   MODEND-CDEND  ;Module  ends at  MODEND, executable
code at CDEND
         .
INIT:                  ;Program start address
        LD HL,CONST    ;Set up constants table addr
        PUSH HL        ; on stack
        LD HL,(TOPMEM) ;Get last available byte addr &
                       ; under CPM subtract one to get
                       ; addr of last avail byte
        PUSH HL        ; set on stack as 'new end'
        LD HL,MODLEN   ;Set up length to be moved
        PUSH HL        ;
        LD HL,CONEX    ;Set to first byte to be moved
        PUSH HL        ; & pass via stack

        CALL RELOC     ;Go relocate the stuff

        POP HL         ;Get start address of new locn
        DEC HL         ;Calc last available byte
        LD (TOPMEM),HL ;Set up for DOS
                       ; dont do this under CPM

                ;Now jump to code in new location
        INC HL         ;Set back to 'new start'
        LD DE,(CONST+1) ;Get offset of first instr at  new  lo-
cation
                        ;  (the  first  so  many bytes are con-
stants)
        ADD HL,DE      ;Step past constants
        JP (HL)        ;Jump to first instr at new locn
                       ; Leaving DOS's return addr at  the  top
of the stack
                (If the  code  moved  is  a  printer driver  or
something, we
                would  set up its  address  <HL>  in the  print
DCB, or whatever,
                and would RET to DOS, rather  than jump  to the
driver code

        .
CONEX:  DB 'First constant to be moved',13,0
        .
        .
CODEX:          ;First instruction to be executed at new addr
        . etc
     Note that while  RELOC protects  itself  from  destructive
overlap, care must be taken that  the initialization code to be
executed  after  control  is returned  from RELOC  (return  ad-
dress), is still there in the old location.
-- Relocation under CPM --
     RELOCCPM  is a  version  of RELOC for  use  under CPM. The
differences  are as follows. The program  loads at 100H  rather
than 5200H. An extra JP has been added to  the beginning of the
module.  This  jumps  past  the actual RELOC  code to a  sample
initialization routine. This routine is along  the lines of the
above  example. It  is set  up to  relocate all of  RELOCCPM to
just below  CCP (to top of TPA), and  then pass control to  the
relocated version.
     CPM always passes control  to a program at 100H, the  very
start of the program, hence the need to insert this  jump to go
to  the actual initialization code.  The  entry point addresses
for  RELOC, RLINIT, RLCODE and RLADDR  are at 103H,  106H, 109H
and 10CH respectively.
-- Retrofitting RELOC --
     One of the nice features  of RELOC is  that it can be com-
bined with  previously  compiled  modules,  such as those where
the source code  is not available.  Using  some  seperately  or
hand  assembled initialization code as shown above,  the  whole
may  be combined  in memory  using the debugger  program.  Even
when  treating all of the previously compiled module (including
constants)  as  instructions,  the relocation will most  likely
work directly.  If  not, some work will be required to identify
address  constants  that are  not part of  an instruction,  and
exclude constants that interfere with the relocation process..
     Using a debugger,  the  sequence  is more or less  as fol-
lows:
- Load RELOC
-  Load the compiled  module (if  it overlaps with RELOC, first
execute RELOC from  the debugger  to relocate itself out of the
way (this  is always the case under CPM, as CPM  loads all pro-
grams at 100H).
 - From the debugger  load or hand assemble the  initialization
code listed above, preceding or immediately following RELOC
-  Mark the address  of  this  code as the future start address
for the combined module
- Using RELOC (from  the debugger)  relocate  the target module
to  immediately following  this  initialization code  or RELOC,
whichever is last
-  Calculate the  beginning, end and execution start address of
the  target module  in this new location, and  set  up in  ini-
tialization code
- Write  combined module to disk,  using the debugger if it can
do this (e.g. TASMON  from The Alternate Source), or the TRSDOS
DUMP command, or the CPM SAVE command.
- Test the module (but write it out  first, to  checkpoint  all
the work done)
- Presto, self-shuffling code






