Function Indirection
I am aware of four known ways to handle function indirection with the 65816/Apple IIgs. As none of them were appropriate for LCC-816, I’ve also devised a new method. I can’t claim it is novel but I am unaware of any previous usage.
Self-Modifying Code
The simplest and most performant way, used by assembly language programmers, is to simply use self-modifying code.
lda function
sta @sm+1
lda function+1
sta @sm+2
@sm jsl $000000
ORCA/Pascal also uses self-modifying code for object indirection calls with optimize flag $0020.
JSL Via RTL
ORCA/C and ORCA/Pascal manipulate the stack and dispatch via an RTL.
ldx function+2
lda function
phx
pha
lda 1,s
dec a
pha
sep #$20
longa off
longi on
lda 5,s
sta 3,s
lda #^@return
sta 6,s
rep #$20
longa on
longi on
lda #@return
sta 4,s
rtl
@return
...
JML
ORCA/Modula2 uses JML [address] to dispatch. However, JML [address] is utterly broken and always reads from an absolute address in bank 0. Thus, to make use of it, self-modifying code is used.
lda >function+2
tay
lda >function
tyx
sta <tmp
stx <tmp+2
phb
phk
plb
ldy #$0000
tdc
clc
adc #tmp
per @sm
sta ($01,s),y
pla
plb
phk
per @return
@sm
jml [$0000]
@return
JML (Revisted)
JML [address] and JMP (address) always read from bank 0 and are generally unusable in GS/OS since you don’t know where your stack/direct page will be. But the GS/OS loader knows where your stack is and will tell you if you ask nicely. The key is to create a named stack/direct page segment. Whenever and wherever you refer to the label, the GS/OS relocator will update it to the actual location in memory.
MyDP STACK
ds 1024
ENDSTACK
...
; create rtl address
phk
per @return-1
lda function
sta MyDP
lda function+2
sta MyDP+2
jml [|MyDP]
@return
(That will use DP location 0-3.)
Function Stub
The 65816 has an instruction that pushes the return address on the stack – JSL. We can load the address into the a/x registers and call a trampoline function to dispatch to the real function.
lda function
ldx function+2
jsl __jsl_ax
...
__jsl_ax
short x
phx
long x
dec
pha
rtl
This is the method LCC-816 uses. This is primarily for the benefit of the optimizer – self modifying code and screwing around with the stack make it hard to reason about. But you have to admit it looks a lot nicer, too!
MPW PascalIIgs uses this technique as well, though I was not aware of that when I discovered it.
Near Functions
LCC-816 supports near functions, which are always within the same bank and use JSR/RTS for dispatch and return instead of JSL/RTL. This makes function indirection easier – JSR (absolute,X) can be used.
ldx function
jsr ($0000,x)