[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter presents the memory bank switching support that the GNU Development Tools support for both 68HC11 and 68HC12.
The 68HC11 and 68HC12 use 16-bit addresses to access the memory, thus limiting the address space to 64K. To access to more than 64K of memory, it is necessary to use a mechanism that will map the memory above 64K in the 16-bit address space. Such mechanism is provided by the 68HC12 through its memory bank switching mechanism.
As far as the 68HC11 is concerned, an external hardware circuitry is necessary to provide such memory bank switching. This mechanism is therefore board-specific and the GNU compiler uses a few support routines to help in generation of calls accross memory banks.
The memory bank switching is supported in a transparent manner only for the code. It can be used for the data but requires application support for this.
To use the memory bank switching mechanism there are several things to take care of:
To control the calling convention of a function or method, the GNU compiler uses function attributes. There are at least two function attributes which are important:
The following declaration:
extern void __attribute__((far)) foo (int); |
indicates that the operation `foo' uses the memory bank switching mechanism
and uses rtc
to return.
The following declaration:
extern void __attribute__((near)) bar (int); |
indicates that the operation `bar' uses the normal calling convention.
When a `far' function is called, the compiler uses a call
instruction
on 68HC12 and for 68HC11 it generates a sequence of instructions to invoke
an external handler.
For 68HC11, this external handler must:
The `far' function uses rtc
on 68HC12 to return (instead of
rts
).
On 68HC11, since rtc
does not exist, the compiler generates a jump
to an external handler which must:
rts
)
The 68HC11 far-call frame is similar to the 68HC12 far-call frame.
For example, consider the following code extract:
void __attribute__((near)) foo () {}; void __attribute__((far)) bar () { foo (); } void __attribute__((near)) main () { bar (); } |
In the example the `foo' and `main' function are declared
as `near' functions
meaning they will use the jsr/rts
calling convention.
The `bar' function
is however declared `far' meaning that it will use the
call/rtc
convention.
In the main, the compiler will generate the following instructions:
68HC11 68HC12 ------ ------ pshb ldy #%addr(bar) ldab #%page(bar) jsr __call_a32 call bar |
For 68HC11, the `__call_a32' support handler will save the current page on
stack (thus creating a 68HC12 call frame), it will switch to the page where
`bar' is defined and finaly will jump (with jmp
) to the
function entry point.
For 68HC12 the call
instruction performs the same actions in hardware.
Within `bar' the call to `foo' is implemented using a
jsr
because `bar'
is a near function. For this to work, the `bar' and `foo'
functions must be in the same page. This is checked by the linker
and if they are in
two different pages an error is reported.
For the return of the `bar' function the compiler generates the following instruction:
68HC11 68HC12 ------ ------ jmp __return_void rtc |
For 68HC11 the `__return_void' support handler must simulate the
68HC12 rtc
instruction. It must pop the previous page number from the stack, install
it and then return using rts
.
It is possible to get the address of a `far' function as well as declare
a C++ virtual method as `far'. In both cases, since the address managed
by the compiler is a 16-bit address, and because the compiler will always
use a jsr
to call the resulting function, a small trampoline code
is used.
The address of the `far' function will always be the address of its
trampoline code. The trampoline is generated automatically by the linker
when an address of a far function is used. There is one trampoline for
each `far' function.
The trampoline is always called by a jsr
instruction. It will switch
the jsr
calling convention to a call
calling convention.
Once the stack layout corresponds to a call
, the trampoline jumps
to the real far function.
The support of memory bank switching for 68HC11 is based on board specific functions which must be provided by the BSP. This section explains how to write these functions.
The `__call_a32' is used to call a far function.
The `__return_void' is used to return from a function returning no value.
The `__return_16' is used to return from a function returning a 8 or 16-bit value.
The `__return_32' is used to return from a function returning a 32-bit value.
Warning: This section is incomplete and will be provided soon.
The linker uses the following formulas to compute the physical address from the symbol/linker virtual address:
if sym_addr >= sym_bank_base then %addr(sym_addr) = ((sym_addr - sym_bank_base) % page_bank_size) + page_bank_base %page(sym_addr) = ((sym_addr - sym_bank_base) / page_bank_size) else %addr(sym_addr) = sym_addr %page(sym_addr) = 0 end if; |
The page_bank_size must be a power of two.
1/ sym_base = 0x8000, page_bank_size = 0x1C00 (7k), page_bank_base = 0x8000 sym_addr = 0x8000 => addr = 0x8000, page = 0 sym_addr = 0x9000 => addr = 0x9000, page = 0 sym_addr = 0xF000 => addr = 0x8FFF, page = 8 => Error 2/ sym_base = 0x10000 memory { b1 : 0x10000..0x11c00; b2 : 0x12000..0x13c00 } sym_addr = 0x10000 => addr = 0x8000, page = 0 sym_addr = 0x11000 => addr = 0x9000, page = 0 sym_addr = 0x12000 => addr = |
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |