I guess it depends on the trampoline code that you use, and the routine being called. The general idea is that there is code copied from main ROM to high RAM at boot. When code in option rom wants to call the main rom it runs some code like this (from romhd.asm):
StdCall:
di
shld HoldH ;Caller's hl
xchg
shld HoldD ;Caller's de
pop h ;(hl)=Routine to Call
mov e,m
inx h
mov d,m ;de=Routine to Call
inx h ;hl=Return Address
push h
lxi h,EnableOptROM ;return through EnableOptROM
push h
push d ;Return Address
lhld HoldD ;Caller's de
xchg
lhld HoldH ;Caller's hl
ei
jmp StdOn
This code saves HL and DE and sets up the stack with some return addresses to get to and from the main ROM code. It doesn't look like it damages any registers in the process. The EnableOptROM code preserves a by saving and restoring the psw.
EnableOptROMImg:
push psw
mvi a,1
out 0xe0
pop psw
ret
And this code, org'd at 0x85:
StdOn:
push psw
push h
lxi h,0x26c8
xthl
xra a
OpExit:
out 0xe0
ret
This is in the Option ROM, but I guess it must slide into the main ROM right after the out instruction, like is done on a Remem inter-bank jump. Maybe it doesn't have to transition through high RAM?
I assume that the code at 0x26c8 in the main rom restores the a register before making the jump.
So I think you should be able to call any function in main ROM.
That said, all this stuff adds some overhead to every call. I can imagine if you decided to preserve less of the registers and take advantage of instructions like pchl, you could make a lot more lightweight far calls.
Anyway, I suggest using romhd.asm from club100 as a starting point. There is also code to call interrupt handlers in the main ROM.
