
**********************************************************************
files: Relocating_Mcode.TXT, Relocating.TAP
       Adjust.ASM
**********************************************************************


** relocating machine code
   =======================
Adjust
This routine adjusts the absolute addresses in the locations pointed to by the list at CHANGES so that the code will run correctly regardless of the location at which it is loaded (as long as CHANGES correctly identifies the locations of all the addresses which need changing). "LET bc=USR initcd" will make any necessary adjustments and return the the address of 'maincd' in 'bc'. The value for ORG can be anywhere, as it need not bear any relation to where the code will actually be located at runtime. The routine works for negative displacements as well (ie. where the code is relocated below ORG).

The advantages, apart from its primary purpose, are that displacements within MAINCD are applied to the CHANGES list automatically when the updated code is assembled; the CHANGES list only needs editing when absolute addresses are added or removed within MAINCD; entries in the CHANGES list can be in any order; the routine only needs to be called once, each time MAINCD is relocated; it's a POKE-free zone; and it's a quicker, more compact and easier to manage than using a POKE list in BASIC (which is what I was doing before). The routine occupies 63 bytes plus 7 bytes for INITCD plus 2 bytes for each entry in CHANGES.

Each time INITCD is run it records the location at which it was run, so if it is run at a different location next time (eg. if it is placed at the end of a BASIC program, or in a DATA string, which moves as the program is edited) it can calculate the relative adjustment needed each time.

; *****

Adjust.ASM [code]
SAVESP: equ  23698           ;=MEMBOT
;adjust addresses in target routine specified by the setup routine
;called from the setup routine with:
;BC=current location of setup routine
;DE=offset of target routine from setup routine
ADJUST: ld   h,b
        ld   l,c
        push hl
        pop  ix              ;IX=current  location of setup  routine
        add  hl,de           ;HL=current  location of target routine
        ld   e,(ix-2)
        ld   d,(ix-1)        ;DE=previous location of target routine
        ld   (ix-2),l
        ld   (ix-1),h        ;update LOCATE in setup routine
        ld   b,h
        ld   c,l             ;BC=current  location of target routine
        and  a
        sbc  hl,de           ;HL=displacement (+/-)
        ret  z               ;return if no displacement

        di
        push bc
        ld   b,h
        ld   c,l             ;BC=displacement
        ld   (SAVESP),sp     ;save stack pointer
        ld   de,7
        add  ix,de
        ld   sp,ix           ;point SP to CHANGES
ADLOOP: pop  hl
        ld   a,h
        or   l
        jr   z,GET_SP

        add  hl,bc           ;HL=location+displacement
        ld   e,(hl)
        inc  hl
        ld   d,(hl)          ;DE=old value
        ex   de,hl
        add  hl,bc           ;HL=old value+displacement
        ex   de,hl           ;HL=location_hi
        ld   (hl),d          ;DE=new value
        dec  hl
        ld   (hl),e
        jr   ADLOOP

GET_SP: ld   sp,(SAVESP)     ;restore stack pointer
        pop  bc
        ei
        ret


; *****
        ;a simple example table of addresses to change;
        ;all values use the original addresses assembled from ORG
        ;terminated by 0000

SETOFF: equ  MAINCD-INITCD
LOCATE: defw MAINCD          ;previous location of target routine
        ;MAINCD setup (called once)
INITCD: ld   de,SETOFF       ;setup routine
        call ADJUST          ;must address runtime location of ADJUST
        ret                  ;returns runtime address of MAINCD
CHANGES:defw MODA00+1,CALLSR+2
        defw $0000           ;this one is mandatory

        ;MAINCD code         ;target routine
MAINCD: ;...
        ret
MODA00: call CALLSR
        ret
CALLSR: ld bc,(VALUE)        ;ld bc,(nnnn) is 4 bytes
        ret
VALUE:  defw 1234

; ***** END
[/code]


**********************************************************************


** Getting PC into HL
   ==================
18.Feb.2013
As a general solution which avoids absolute addressing I can't see that there's anything to beat the HALT idea. To get the runtime start address of a USR routine, start with:
HALT : DEC SP : DEC SP : POP HL : DEC HL
which is only five bytes; although, as pointed out, it can be done in only two bytes on the Spectrum by starting the USR routine with:
PUSH BC : POP HL
this latter option being also interrupt independent. Either way, the program would need a DEFW to record the ORG address then the difference gives the relative adjustment at runtime.

   ==================

Just to point out that the original question specified "without using absolute addressing", thus any method which uses CALL doesn't satisfy that criterion.

   ==================

I use the simple BC register technique when I [b]don't[/b] know where my routines are going to be; ie. I assemble them at some nominal address but I don't know where they're going to be at runtime as they're appended to a BASIC program whose length can change independently. So I pick up BC on entry to work out the displacement and look up a table of addresses which have to be altered. This is just done once, at the start of the BASIC program, not at the start of the individual routines.


**********************************************************************