[comp.sys.apple2] relocatable code

ART100@psuvm.psu.edu (Andy Tefft) (11/12/90)

I'm trying to write something small in assembly that will be
completely relocatable. The problem is that the code
contains a lookup table and I need to know where that
table is in memory.

I can do a neat trick to find where the program is
in memory, but the problem is it requires a jsr. This
means at least a small subroutine (ok, so it's only about
20 bytes long) be at a fixed point in memory. Here it is:

start   jsr storit   ; this is in the main program

        ...

storit  pla          ; obviously this has to be at a fixed loc.
        sta begin
        pla
        sta begin+1
        pha          ; put it back for the rts
        lda begin
        pha
        rts

where begin is a pair of convenient 0-page or otherwise fixed locations.
This routine returns start+2 in begin and begin+1. Then all i have to do
is add a fixed offset from the 'start' location to get the location
of my table.

I guess I can force the user of the routine to store the
starting location of the program at some fixed point and
have the program use that, but obviously it isn't foolproof.
Is there any other way a program can get the contents of the PC
besides pulling them off the stack after a jmp or jsr?
The program will not necessarily be brun from basic.system,
so I can't use any prodos tricks. The table is about 48 bytes,
too long to manually store in memory with lda #3, sta $1000, lda #76,
sta $1001, etc. Same deal with the storit subroutine. Think I've
covered all the important difficulties there.

ideas? ideas?

toddpw@nntp-server.caltech.edu (Todd P. Whitesel) (11/12/90)

Apple has guaranteed that there will be an RTS at $ff58 in the ROMs. This
routine does not need to be fixed and can find its own address:

	php
	sei
	jsr	$ff58
	tsx
	ldy	$100,x
	dex
	lda	$100,x
	plp

now A has the hi byte and Y has the low byte of the address of the $ff in the
jsr $ff58 (i.e. the address of the tsx, minus one).

The php/sei and plp business is necessary to insure that:

a) the return address is not overwritten by an interrupt occurence
b) the interrupt state is restored to its previous value afterwards

If you don't want to even rely on the ROMs then you can always store a $60
somewhere and JSR to it instead. If you're really paranoid, save the value at
some location ($400?) on the stack and put a $60 there just before you jsr to
it, and restore the value just before the plp instruction. Only NMI's, Resets,
or your code being run from that location could conceivably break that.

Todd Whitesel
toddpw @ tybalt.caltech.edu

ericmcg@pnet91.cts.com (Eric Mcgillicuddy) (11/13/90)

What is causing the code to be reloacted?

The function that does the reloaction should return the address to which it
has moved your code.

Your code does not work as you think it should. If storit is at a fixed
location then the address jsr pushes onto the stack will be a fixed location.
This will not generate the addres of your lookup table. 

It is a shame that yu are not using a 65816, PER  would solve your problem
nicely.

Give me the code for the relocator and I will see what can be done.

UUCP: bkj386!pnet91!ericmcg
INET: ericmcg@pnet91.cts.com

GRAY@ADMIN.HumberC.ON.CA (Kelly Gray) (11/14/90)

One way to locate a program in memory is to do some playing with the stack.
The following bit of code will locate itself wherever it may be. The only
restriction is that the motherboard ROM must be enabled since it needs to
use an RTS instruction at a known location.

IORTS    EQU    $FF58     ;Address of known RTS instruction in ROM
;
LOCATE   SEI              ;Disable interrupts for a moment
         JSR    IORTS     ;This subroutine simply returns back,
         TSX              ;Leaving the return address above the stack
         DEX              ;Alter stack pointer to recover old return address
         DEX
         TXS              ;Put altered pointer into stack pointer register
         PLA              ;Get PCL of old return address
         STA    LOC       ;Save it
         PLA              ;Get PCH of old return address
         STA    LOC+1     ;Save it
         CLI              ;Restore interrupts again

 After this program fragment is done, the pointer at LOC will point to the
TSX instruction that follows the JSR to IORTS. From there it sould be an
easy matter to add an offset to this value to point it at your lookup table.

This code itself is completely relocatable. There is only one byte that must
be at a known address, and that is the RTS instruction at IORTS

     <o_o>
 _________________________   ________________________________________
/                         \ /                                        \
|        Kelly Gray        |  The opinions expressed in the preceding |
|                          |  message are not guaranteed to represent |
| GRAY@ADMIN.HumberC.ON.CA |  any form of rational thought whatsoever |
\_________________________/ \_________________________________________/

mziober@trocadero.ics.uci.edu (Michael A. Ziober) (11/14/90)

In <90Nov13.183431est.57621@ugw.utcs.utoronto.ca> GRAY@ADMIN.HumberC.ON.CA (Kelly Gray) writes:

>One way to locate a program in memory is to do some playing with the stack.
>The following bit of code will locate itself wherever it may be. The only
>restriction is that the motherboard ROM must be enabled since it needs to
>use an RTS instruction at a known location.

>IORTS    EQU    $FF58     ;Address of known RTS instruction in ROM
>;
>LOCATE   SEI              ;Disable interrupts for a moment

Bad idea to just disable interrupts.

>         JSR    IORTS     ;This subroutine simply returns back,
>         TSX              ;Leaving the return address above the stack
>         DEX              ;Alter stack pointer to recover old return address
>         DEX
>         TXS              ;Put altered pointer into stack pointer register
>         PLA              ;Get PCL of old return address
>         STA    LOC       ;Save it
>         PLA              ;Get PCH of old return address
>         STA    LOC+1     ;Save it
>         CLI              ;Restore interrupts again

Likewise to just enable interrupts.

> After this program fragment is done, the pointer at LOC will point to the
>TSX instruction that follows the JSR to IORTS.

Actually to the byte BEFORE the TSX or the last byte of the JSR IORTS.

>						From there it sould be an
>easy matter to add an offset to this value to point it at your lookup table.

>This code itself is completely relocatable. There is only one byte that must
>be at a known address, and that is the RTS instruction at IORTS

Although Apple has "guaranteed" an RTS at $FF58, the above code
assumes that the ROMs are mapped in, as Kelly noted.  On any Apple,
location $FF58 may be mapped to RAM.  Additionally, on a GS, if the
shadow register is not set correctly, you probably won't find an RTS
at $FF58 even with the ROMs mapped in.  For these reasons, it is
preferable to make no assumptions about the availability of the ROMs.

Here is some code which makes virtually no assumptions about its
runtime environment.  It does assume that your assembler acts like
LISA.  I don't know about the Pascal or APW assemblers.  Your favorite
assembler might have syntax to force absolute addressing modes, don't
ask me how.  It's been years.

ENTRY   PHP           ;save interrupt mask
        SEI           ;disable interrupts to protect return address on stack
        LDA $0        ;save value at $0
        PHA
        LDA #$60      ;store an RTS at $0
        STA $0
HERE    JSR $0        ;place return address on stack
        PLA           ;restore value at $0
        STA $0
        TSX           ;stack pointer->high byte of return address
        LDA $100-1,X  ;must be absolute address $00FF, not zero page $FF
        LDY $100,X    ;(256 * A) + Y = HERE + 2
        PLP           ;restore interrupt mask

Please note that I do not have my handy-dandy 6502 reference handy so
I am unable to verify the existence of the LDY abs,X instruction.  But
I'm pretty sure it's real.  If not, I apologise for posting bogus
code.

Michael Ziober
ICS senior & proud owner of an Apple ][ (no plus!)
University of California, Irvine

ericmcg@pnet91.cts.com (Eric Mcgillicuddy) (12/16/90)

using  ABS,X would be fine for data, but what about code?

I suggest that bank aligning code segments and only using short (6502)
addressing modes would be a valid solution. Use a couple of Direct Page
locations to access movable data segments that are broken into linked 64k
blocks and you are away. I think that designing a compiler for these
parameters would be easier. But what do I know about writing compilers? It is
not high on the curriculum in Electrical.

BTW, thanks Todd for spelling 'voila' correctly. 'Wahla' could easily result
in the separation of Quebec at this slight of the French language. :)

UUCP: bkj386!pnet91!ericmcg
INET: ericmcg@pnet91.cts.com

toddpw@nntp-server.caltech.edu (Todd P. Whitesel) (12/21/90)

ericmcg@pnet91.cts.com (Eric Mcgillicuddy) writes:

>using  ABS,X would be fine for data, but what about code?

ABS,X works for both data and code. I wrote an init that fixes ZipGS Appletalk
problems and it's position independent. It patches out a few vectors and uses
ABS,X to self-modify a pair of JMP long instructions so it can call the
original contents of the vectors. A lot of assembly programmers do a PHK/PLB
when they set up and use short addressing modes to get at code and direct page
or long to get at data. My point was that you can very easily use X or Y to
point at a 64K block that absolute addressing gives you immediate access to --
without a lot of LDY's.

>I suggest that bank aligning code segments and only using short (6502)
>addressing modes would be a valid solution.

Sure it would work, but it defeats the purpose. The goal was position
independent code that didn't need relocation or bank-alignment. Using absolute
code segments IS a valid solution if you aren't worried about the effects on
memory of all those bank aligned blocks. If you are writing a big program and
intentionally write it so that it sits comfortably in a few bank aligned spaces
then that's great -- but I prefer solutions that are still environment friendly
for both small and large projects.

>BTW, thanks Todd for spelling 'voila' correctly. 'Wahla' could easily result
>in the separation of Quebec at this slight of the French language. :)

Actually, my first attempt at spelling that was 'Wallah' in the 2nd grade --
in a collage/report no less -- I was corrected immediately and haven't messed
it up since. (That's the truth folks)

Todd Whitesel
toddpw @ tybalt.caltech.edu

ericmcg@pnet91.cts.com (Eric Mcgillicuddy) (12/23/90)

>>I suggest that bank aligning code segments and only using short (6502)
>>addressing modes would be a valid solution.
>
>Sure it would work, but it defeats the purpose. The goal was position
>independent code that didn't need relocation or bank-alignment. Using
absolute
>code segments IS a valid solution if you aren't worried about the effects on
>memory of all those bank aligned blocks. If you are writing a big program and
>intentionally write it so that it sits comfortably in a few bank aligned
>spaces
>then that's great -- but I prefer solutions that are still environment
>friendly
>for both small and large projects.

I am curious how you would insure that a position independent code segment
would not cross a bank boundary, particularly in the context of a Memory
Manager compaction. I assume that the code segment is unlocked immediately
after it completes execution and locked just prior to commencing execution. My
suggestion is that all code and data segments are precisely 64k long and page
aligned (the only option the memory manager supports). This will create a
certain amount of wastage for small programs, but does provide a known amount
of workspace that is easily accessed by the code. What I am attempting to do
is turn a problem with the 65816 (segmented memory model) into an advantage.
This also has the added advantage of providing hardware memory protection for
multiple processes, since the architecture prevents the crossing of bank
boundaries, as well as providing position independence for code segments.

This is the entire premise of ViM, if it can not work then perhaps I am
wasting my time and should go ont{{o{ {_{{_ano{ther project?i]
{_{.s
w.s

UUCP: bkj386!pnet91!ericmcg
INET: ericmcg@pnet91.cts.com

toddpw@nntp-server.caltech.edu (Todd P. Whitesel) (12/23/90)

ericmcg@pnet91.cts.com (Eric Mcgillicuddy) writes:

>I am curious how you would insure that a position independent code segment
>would not cross a bank boundary, particularly in the context of a Memory
>Manager compaction.

The memory manager attribute attrNoCross takes care of it. Position independent
code segments can move and they can hop over bank boundaries, but the memory
manager won't try to put them across a bank boundary.

Todd Whitesel
toddpw @ tybalt.caltech.edu