On the 65816, when the location of a program is not known until run time, the program can use PER and PHK to determine where it is. On the 6502 and 65C02, the WHEREAMI routine can be used for this purpose.
Using JSR
One way to implement WHEREAMI is to place it routine at known location (e.g. in ROM) and call it via JSR, i.e.:
HERE JSR WHEREAMI1
The JSR pushes HERE+2 onto the stack, then WHEREAMI gets the return address (HERE+2) from the stack. It is often useful to return an offset from the address of the JSR rather than just the address of the JSR plus two, i.e. HERE+2+constant rather than HERE+2. So WHEREAMI1 returns HERE+2+A in A (low byte) and Y (high byte). Note that X is overwritten.
WHEREAMI2 is an alternate entry point that returns HERE+2+A+C and is useful if the value of the carry is known prior to the JSR.
WHEREAMI1 CLC
WHEREAMI2 TSX
INX
ADC $100,X ; add lo byte of return address
INX
LDY $100,X ; hi byte of return address
BCC SKIP
INY
SKIP RTS
WHEREAMI works for all values of S (the stack pointer). In most cases, S will have been initialized to $FF; then, by the time the TSX is executed S will be less than $FE, so the two INXs could be eliminated by using $101,X and $102,X. This only saves 2 bytes and 4 cycles, though.
Using BRK
Using JSR requires that WHEREAMI be at a known address. An alternative is to use BRK, i.e.
HERE BRK
The BRK handler below returns HERE+2+A in A (low byte) and Y (high byte). Again, X is overwritten. Also, note that RTI skips the byte after the BRK instruction, i.e. RTI returns to HERE+2 rather than HERE+1.
;
CLC
TSX
INX
INX
ADC $100,X ; add lo byte of return address
INX
LDY $100,X ; hi byte of return address
BCC SKIP
INY
SKIP RTI
Again the INXs could be eliminated, assuming that S is less than $FD.
Using a RAM buffer
Another option is to store a routine at a known, but safe, location, and then JSR to that location. For example, a program is usually not going to be on the zero page, so it would be safe to store a short routine there, and JSR to it. If necessary, those zero page locations could be saved (on the stack), then restored when finished.
The routine below needs a 4-byte safe location. The usual reason to find HERE+2 is to store it in a zero page pointer. If the next two zero page locations are also available, then the pointer (and the following two locations) could be used as the safe area.
Another usually safe location is the bottom of page 1, (i.e. starting at $100), because (1) a program will usually not be there, (2) the stack pointer is usually initialized to start at the top of page 1 (i.e. $1FF), and (3) stack usage will usually not be so heavy as to extend all the way down to $100.
After:
; At LOC to LOC+3, store:
; LDA $100,X
; RTS
;
LDA #$BD
STA LOC
LDA #1
STA LOC+2
LSR ; A = $00, sets carry
STA LOC+1
LDA #$60
STA LOC+3
TSX
DB $90 ; BCC (skips the CLC, since the carry is set)
LOOP CLC
TAY
HERE JSR LOC
DEX
BCS LOOP
A contains the high byte of HERE+2, and Y contains the low byte of HERE+2