Because the 6809 has a native 16-bit address space, only 64KB of code and data can be addressed at a time. On most platforms, some of this space is reserved for I/O, limiting the amount that can be used by the compiler even further.
Many architectures allow for some form of bank switching, where a larger amount of memory exists, but only portions of it are accessible at a time. GCC6809 has some builtin support to make it easier to work with systems that support bank switching. However, currently it can only help with switching out code pages. Data accesses across different banks are not handled automatically, and require explicit instructions from the programmer.
Bank switching can only work when the underlying hardware supports it. GCC does not care how the hardware works, aside from a few rules:
jsr.
Only one banked section can be accessible at a time: which one
depends on the current value of the bank switch register.
When an ordinary jsr instruction will not suffice, GCC
can emit what is called a far call.
To determine when a far call is needed, GCC must know the bank
number (if any) of the caller, and that of the callee.
The caller bank is identified from the -far-code-page command-line option. All functions in the same source file are assumed to be in the bank number identified by this option. If not stated, then all of the functions are assumed to be in a fixed section.
The callee bank is identified by using the far attribute
on the declaration of that function. Remember that declarations
occur in header files, not in source files. If you do not provide
a prototype for a function, GCC assumes it is in a fixed section.
When GCC needs to emit a function call, the target function is in a banked section, and the caller is not in the same bank, then a special call sequence is emitted which looks like this:
jsr __far_call_handler
.dw target_function
.db target_bank
Here, target_function is the function being called, and
target_bank is the bank number where that function is located,
according to the far attribute in its declaration.
The function __far_call_handler is system dependent.
GCC6809 does not provide a default farcall handler; you must write it
yourself. This function should:
target_bank.
target_function, making sure that parameter
registers still have the correct values.
It is practically impossible to write the function in C to meet all of these requirements; writing it in assembly language is much easier.
Here is the far call handler used on the FreeWPC platform:
.area direct
__far_call_address:
.blkb 2
.area .text
.globl __far_call_handler
__far_call_handler:
pshs b,u,x ; Save all registers used for parameters
ldu 5,s ; Get pointer to the parameters
ldx ,u++ ; Get the called function offset
ldb ,u+ ; Get the called function page
stu 5,s ; Update return address
stx *__far_call_address ; Move function offset to memory
lda IO_BANK_REGISTER ; Read current bank register value
stb IO_BANK_REGISTER ; Set new bank register value
puls b,u,x ; Restore parameters
pshs a ; Save bank switch value to be restored
jsr [__far_call_address] ; Call function
puls a ; Restore A
sta IO_BANK_REGISTER ; Restore bank register
rts
GCC does not emit the far call sequence when the caller and callee are both in the same bank; in that case, the bank switch register is already correct.
GCC will raise a fatal error if one or more of a far call target's parameters needs to be pushed onto the stack. The thinking is that the farcall handler cannot save the previous value of the bank switch register without also pushing onto the stack, which causes confusion about where the arguments are actually located. If your farcall handler doesn't use the call stack (for example, it saves into a separately declared stack), then you can turn off this error by compiling with -mfar-stack-param.