!pr0
!lm12
!rm75
Relocatable Ampersand-Vector........................Steve Mann

In recent issues of AAL there have been a variety of routines to produce relocatable code.  The BSR, BRA and LEAX opcodes in the June issue and the run-anywhere subroutine calls in the July issue are two examples.

However, in making some of my code relocatable, I encountered a new problem with routines that interface with Applesoft programs through the & command.  The problem is that the routine doesn't know what address to place in the & jump vector because that address may change with each run.

A rather inelegant solution is to derive the address from Applesoft's pointers, then POKE it into the & vector before calling it.  What I wanted was a method to determine the correct address from within the code itself, in much the same way that a non-relocatable program sets up the vector:

!lm+5
1000        LDA #$4C
1010        STA AMPER.VECTOR
1020        LDA #START
1030        STA AMPER.VECTOR+1
1040        LDA /START
1050        STA AMPER.VECTOR+2
1060 *
1070 START   ...
!lm-5

I have written a short routine which will handle the initialization at the beginning of relocatable programs, as long as the program's entry point immediately follows, as in the sample program listed below.

The routine works by first jumping to the subroutine at $FF58, which is simply an RTS instruction.  As Bob explained in the July AAL, this places the return address on the stack and then pops it back off again.  The return address can then be found by reading the first two open bytes below the stack.  The TSX instruction in line 1100 loads the offset to those two bytes into the X-register.  Lines 1110-1130 load the bytes into the A- and Y-registers.

Now we have the address of the third byte of the JSR RETURN instruction - the MSB in Y and the LSB in A.  What we need is the address of the program's entry point, which corresponds to the label START.  To get that address, we must add in the length of the rest of the SETUP routine, that is, the difference between the address at START and the address in the Y- and A-registers.

This is handled in lines 1140-1170.  Line 1150 adds the offset ($1B for this particular routine) to the low byte of the base address.  The extra 1 in the ADC intruction is necessary because the address in Y and A is one less than the actual return address (corresponding to .1).  Lines 1160-1170 check for a carry and adjust the high byte if necessary.  The entry point address is then saved in the ampersand vector at $3F5-$3F7.

!sp25

!sp0
The same principle can be used to set up the monitor's control-Y vector at $3F8-$3FA.  As a matter of fact, I usually use a macro with conditional assembly to set up whichever vector I need.  Here's the macro:

!lm+10
1000        .MA VECTOR
1010        JSR $FF58
1020 :1     TSX
1030        LDY $100,X
1040        DEX
1050        LDA $100,X
1060        CLC
1070        ADC #:3-:1+1
1080        BCC :2
1090        INY
1100 :2     .DO ']1='Y   CTRL-Y?
1110        STA $3F9
1120        STY $3FA
1130        LDA #$4C
1140        STA $3F8
1150        .ELSE        OR &?
1160        STA $3F6
1170        STY $3F7
1180        LDA #$4C
1190        STA $3F5
1200        .FIN
1210        RTS
1220 :3
1230        .EM
!lm-10
!np
Just include this definition at the beginning of your program.  Then macro can then be called like this:

!lm+5
2000        >VECTOR,Y
2010 START  ...
!lm-5

to set the control-Y vector, or like this:

!lm+5
2000        >VECTOR,&
2010 START  ...
!lm-5

to set the ampersand vector.  (Actually any character other than Y will result in setting the & vector.)


(Note:  When I showed this macro to Bob I asked him if the .DO in line 1100 would really work.  He looked at it for a minute and said, "yes, it sure will.  The assembler's macros are even more powerful than I thought!"...Bill)
