Position independent code for 6502 CPU (relocatable)

Post Reply
Thomas
Posts: 29
Joined: Tue May 30, 2023 8:53 am

Position independent code for 6502 CPU (relocatable)

Post by Thomas »

Concerning original 6502 code relocation methods , a few proposals and discussion exist:
http://www.6502.org/users/andre/o65/fileformat.html
or http://wilsonminesco.com/stacks/where-am-I.html
Unfortunately, the 6502 does not provide mnemonics to allow relative long jumps or relative subroutines nor relative access to data elements to make it position independent.

If you have diffrent memory configurations and you want to place your program anywhere in memory you have to have relocatable assembly code.
I came up with an solution, how 6502 code needs to be written to be relocatable. So it could be loaded at any starting addresses without re-assembling.

There is a support code as part of the initialization at program start (see attached example). This routines allow the user to call or jump to relative location within the program. In addition, a support routine is included, that returns an address within the program code to manage data elements, like text string output.

The attached program example makes use of all three support routines and can be loaded to anywhere in RAM.
It is written for the Ohio C1P computer

RELOC_Example.zip
(1.86 KiB) Downloaded 381 times
Only the relocation support routines have to be placed to a know availabe RAM address, here to $026B to $02FF

Code: Select all

;
;   RELOCATION 6502 SUPPORT CODE (to support position independent asm programs)
;   This subroutines have to be loaded to fixed available RAM space
;   Concept: supporting JMP , JSR and ADR function at fixed free RAM area (Free_Mem)
;   Call JMP , target Address ((word)Target offset - * +1) -> Rel_JMP takes 85 cycles
;   Call JSR , target Address ((word)Target offset - * +1) -> Rel_JSR takes 110 cycle (without RTS)
;   Call ADR ; target Address ((word)Target offset - * +1) -> Rel_ADR takes 108 cycles and returns Address on stack (first high)
;   All three routines will not change ACCU, registers or status flags of the CPU !
;
;   Relocation support code needs 149 bytes of available RAM and is written for Ohio Superboard 600 / C1P
;   Zeropage address $FE and $FF are used for Adr_vector and must be available
;   Uses self-modifying code for register backup/restore



Free_Mem  = $026B

Rel_JMP   = Free_Mem            ; 149 Bytes free memory for Relocate code required
Rel_JSR   = Free_Mem + $2E
Rel_ADR   = Free_Mem + $60


temp_Al  = $FE               ; Free Zeropage (normally used for OSI Monitor Address)
temp_Ah  = $FF

      
   .ORG   Free_Mem
SUB_JMP;               ; ************ Jump Version <Rel_JMP>
   sta   temp_A+1         ; Save A
   sty   temp_Y+1         ; Save Y
    php
   pla
   sta   temp_S+1         ; Save Status
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of jump delta
   adc   temp_Al
   sta   jump_A+1
   iny
   lda   (temp_Al),y         ; Points to lowbyte of jump delta
   adc   temp_Ah
   sta   jump_A+2

temp_Y:   ldy   #0            ; Dummy values/address for restore
temp_S:   lda   #0   
   pha
temp_A:   lda   #0
   plp
jump_A:   jmp   0


SUB_JSR;               ; ************ JSR Version <Rel_JSR>
   sta   temp_A+1
   sty   temp_Y+1
    php
   pla
   sta   temp_S+1
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of jsr delta
   adc   temp_Al
   sta   jump_A+1
   iny
   lda   (temp_Al),y         ; Points to lowbyte of jsr delta
   adc   temp_Ah
   sta   jump_A+2
   clc               ; Correct return address
   lda   temp_Al
   adc   #2
   tay
   lda   temp_Ah
   adc   #0
   pha
   tya
   pha
   bcc   temp_Y            ; most of the time

   

SUB_ADR:               ; ************ Get Address onto stack <Rel_ADR>
   
   sta   tema_A+1
   sty   tema_Y+1
    php
   pla
   sta   tema_S+1
   
   pla               ; Get Caller address
   sta    temp_Al
   pla
   sta   temp_Ah
   
   ldy   #1
   clc
   lda   (temp_Al),y         ; Points to lowbyte of address delta
   adc   temp_Al
   pha               ; on stack
   iny
   lda   (temp_Al),y         ; Points to lowbyte of address delta
   adc   temp_Ah
   pha               ; on stack
   clc               ; Correct return address
   lda   temp_Al
   adc   #2
   tay
   lda   temp_Ah
   adc   #0
   pha
   tya
   pha
tema_Y:   ldy   #0            ; Dummy values for restore
tema_S:   lda   #0   
   pha
tema_A:   lda   #0
   plp
   rts
The attached example will clear the screen, show a message, displays key entries and returns on ESC key (see MAIN section)
After compilation of RELOC_Example.asm, you can place the binary anywhere in RAM to run.
bxdanny
Posts: 335
Joined: Thu Apr 16, 2015 2:27 pm
Location: Bronx, NY USA

Re: Position independent code for 6502 CPU (relocatable)

Post by bxdanny »

I've thought for a while that 6502 computers should include a "where am i" routine at some known location in ROM. What I'm thinking of would be just 8 bytes long:

PLA
TAX
PLA
TAY
PHA
TxA
PHA
RTS

Then software could call that routine and use the returned values of Y and X (or Y and A) to determine where it is loaded, and alter itself accordingly. But I don't know of any computer that actually contains such a routine.

Maybe it's because the code needed for a program to patch itself would still be non-trivial. So perhaps an operating system with such a routine callable as a function would be better. And/or one with a relocating loader, with files using a particular format to specify the locations of addresses within their code that need to be patched to have the file's load address added to them.

If a program's code is kept separate from it's data areas, then something like the <R>elocate function in OSI's Extended Monitor works very nicely. Actually, I haven't seen that function anywhere else. Of course, the only 6502 chips made today are 65C02s, and they do have long branch and branch-to-subroutine instructions.
No current OSI hardware
Former programmer for Dwo Quong Fok Lok Sow and Orion Software Associates
Former owner of C1P MF (original version) and C2-8P DF (502-based)
Thomas
Posts: 29
Joined: Tue May 30, 2023 8:53 am

Re: Position independent code for 6502 CPU (relocatable)

Post by Thomas »

A 65C02 does not support long branches or long branch-to-subroutine, to my knowledge. Although it was a major weak point of the NMOS 6502 that needed to be fixed. (see table), The best they came up with, was an absolute indexed version like JSR($1234,X)

JMP_JSR.png
JMP_JSR.png (69.07 KiB) Viewed 11684 times
Basically only new SBC projects using a 65C02. Most of the vintage gear is hopefully equipped with a well running 6502 from the late 70's and programs mostly making use of the standard 6502 mnemonics.

The shown relocation method does not require any code address relocation at startup. It simply works fully transparent like a long relative call

jsr Rel_JSR ; ******** Clear Screen subroutine
.dw CLS - * +1 ; Address word offset (target-acual+1)

with the target offset address in two memory location after the call.
The same for the long relative jump and getting a program internal data table address. Without compromising any register content or status register flags.
At least, I have not seen any position independent program examples for the 6502, that will run at any location in RAM.
Post Reply