3 ms·
Not completely relocatable, but in practice this means that you have some memory reserved at a specific point in memory for the relocator and the template. The
by boomlinde 8y ago
Not completely relocatable, but in practice this means that you have some memory reserved at a specific point in memory for the relocator and the template.
The relocator can be made position independent by using indirect stores. You never need direct access to the program counter for this, but it gets a bit more complex. You don't need to read the program counter directly for this, but I think the simplest way of doing that is the jsr instruction, which puts the PC (plus some fixed offset) on the stack.
The idea is that instead of using a "template" and referring directly to that, you load the relocatable code where you want it, load that location in some zero page vector and in the relocator you do indirect+relative stores based off of that vector:
lda page ; the page that the routine was loaded to
sta $ff
lda #0
sta $fe
jsr relocate ; wherever you've loaded the relocator
...
; vector $fe-$ff = routine location, $fe must be 0
relocate
lda $ff
; load the lsbyte of the offset of the first patch in the routine into y
; in reality, would be calculated at the point of assembling the routine template
ldy #<2+patch1-routine_start
sta ($fe),y
ldy #<2+patch2-routine_start
sta ($fe),y
rts
As you can see, the relocate routine is now completely position independent. The relocated routine has the same constraints (has to be loaded to the beginning of a page). We use the y register to load the offset within a page and $ff as the page counter. It's also not much more complex to write the relocator for routines spanning multiple pages, but it comes with a bunch more considerations that I think would take much more code to demonstrate (e.g. cross page jumps and calls).
- tinus_hn 8y agoThe problem is that the only way to read the program counter is through jsr or an interrupt and these both can’t do relative jumps. If you start with that information and you allow patching you can of course relocate the code on the fly, but I wouldn’t really call that position independent code generation by a compiler. It’s more like you’re implementing your own linker.
- boomlinde 8y ago> The problem is that the only way to read the program counter is through jsr or an interrupt and these both can’t do relative jumps. No, that's not a problem in this case since you don't need to know the value of the program counter to relocate the routine. You only need to know the relative offsets of the patch points from the start of the routine, which are static. The relocator itself is position independent, though, since it never uses absolute or absolute indexed addressing. No jumps or subroutine calls, just zero page loads and vectored writes. That said, if you want to know the program counter, you can use a tiny, position independent subroutine to save it: savepc ; copy return address to $fe-$ff pla sta $fe pla sta $ff pha lda $fe pha ; increment $fe-$ff by 1 so that it contains the ; address of the next instruction after the jsr inc $fe bne done inc $ff done rts ... jsr savepc lda #0 ; $fe-$ff contains address of this instruction after savepc call That has no use in this relocation technique, however. > If you start with that information and you allow patching you can of course relocate the code on the fly, but I wouldn’t really call that position independent code generation by a compiler. Neither would I, since it's assembled, but that was never the question, as far as I understood. Quoting the post you first responded to: "You could write 6502 code to be completely relocatable, but at a cost of space and a more complex assembler." I demonstrated that with an example of a position independent relocator that patches an otherwise position dependent routine so that it can be relocated to the start of any page. And yes, if you've built the module that you need to relocate, you have the information required to patch it. You could even join the position independent relocator with the relocatable routine and have it patch itself out with a jump to the routine after relocation. > It’s more like you’re implementing your own linker. Maybe a small portion, but specifically, it's a position independent code relocator, exactly what was on topic. EDIT: I think we may be confusing two different problems here. One is relocating a routine and producing position independent code. That is what I've demonstrated. The other is the problem of knowing the location at the routine at the call site. In the simple case, the call site of the relocator typically knows what portion of memory needs to be position adjusted. That is what I have assumed so far. In a more complex case, e.g. a position independent/relocated routine needs to call another position independent/relocated routine without knowing its location. In order to do that you can put the address of each subroutine in a table and use it as a trampoline. The routines calling into each other now only need to know the index of the routine they want to call in the table. The table itself can be located anywhere and be moved around freely. call ; $aa-ab = table location, y = subroutine index lda ($aa),y sta jump+1 iny lda ($aa),y sta jump+2 dey jump jmp $ffff ... ldy #11 ; index of PRINT routine jsr call This can also be made position independent via stack trickery or indirect jump. The relocator for each routine could be responsible for populating this table.