Q: What is a farcall?
With only 64k of direcly addressable memory space "farcalls" are needed to jump execution to code in other memory pages. The code to call a function in another page (or bank) is quite simple, and is used extensively in the Rex firmware. The code called might be within the firmware or a further addin. For example, such a technique is used in "Adder" to call addins that have been stored in unused gaps in the firmware.

Q: Can't I just store my data in another addin?
Yes. The ability to reference strings & bmps in another page is built into the system calls DsPrintf and DsDisplayBitmapDraw - just pass the page number as the last parameter.

Q: How do I make a farcall in z80 code?

ld a,pagenum ; target page number
ld hl,offset ; target offset (address) in that page
call $59ca ; "paged fncall" function in low/non-page-switched firmware

(or: call $26d9 ; EXACTLY the same function - not sure why they duplicated this code!)

A well-behaved firmware function will use the appropriate entry & return functions, to make sure it gets back to the calling page upon completion. (A normal addin, using DsAddinTerminate() at the end, will NOT return back to the caller, without modification)

Q: How do I make a farcall in C code?
The rexdk supports "far" calls. This is a small example:

Main Addin:

#define page xx file://this is the page number of the second addin
#define somefunction yyyy file://the offset of the function

void main() {
FarCall(page,somefunction); file://call the "far" function
FarRestore(); file://always call this to restore the stack
}

Second Addin:
void SomeFunction() {
...
FarReturn(); file://return to the main page
}

Q: How do pass parameters between code?
In Z80 you can simply store and retrieve values via the user variable space. Similarly in C if you define a global var in both of the addin pages at the same location, they both refer to the same address space.

In the RexDk kit there are some functions to assist with this:

WritePar1(value); file://pass/return a parameter to the function
par = ReadPar1(); file://read the passed/returned value
The parameter you can pass is a 2-byte value. There is also a second pair of functions WritePar2/ReadPar2 to pass a second parameter.

Q: How do I know what page & offset to use?
To find the offset you need to use compiler output. Using RexDk you can get the offset of the function if you set the -m compiler option. This will create a *.map file where you can find the offset of all functions.

You can get get the page number of the currently loaded page with the REGISTER_READ function. ( REGISTER_READ(REG_BANK1_LO) ).

The 8 addins use pages 0x80-87. (You can see this in RexTools, rightclick on an installed addin, look at properties, 3rd tab - it refers to this as "start page #".)

Q: Background
There are 128 pages, numbered 0x00 through 0x7f, corresponding to 128 8K blocks of memory in 1 MB flash ROM. But here's where it gets tricky: 0 does not start at 0x000000 - it is at 0x008000. And all offsets in all pages are relative to, you guessed it, 0x8000.