Dunfield Development Services Inc.
Customer Support -- Application Note #0008
MICRO-C
Bank switching and page swapping

Applies to: [Micro-C Compiler
]
Last updated: Sunday May 04, 2003
.
Application Note Index
[ Back ] [ Next ]

PROCEDURE
MICRO-C is specifically targeted toward small 8/16 bit control systems, which typically have a limited memory space. If you require more program
code than is supported by your processor, it is usually a sign that you are using the wrong processor in your design, and should consider choosing
to a larger one. It is a fact however that products and designs tend to "grow" as they are maintained, and implementing a banked memory scheme
may be easier than redesigning the entire system.
Although MICRO-C has no built in features for bank switching, it is not too difficult to set up. In fact, you
do it exactly as you would in assembly language. In it's simplest form, bank switching consists of selecting the
memory page where the desired function resides, calling the function, and switching the memory page back to it's previous value. This is complicated
by several issues:
 | Runtime and 'C' library functions must be available to all pages which require them. The solution is to place these in an area of common memory
which does not switch with the page selected. If there is no common memory, these will have to be placed in ALL pages!
|
 | Switching to a page while running in another one would "take away" the one performing the call
before it transfers execution to the new page, thereby crashing the system. The solution is to place a "stub" function
in the common memory which which switches the page, calls the function in the other page, restores the page and then returns to it's caller.
|
 | The extra level of call in the "stub" function misaligns the stack, causing the arguments to appear in the wrong place. There are two
possible solutions:
 | 1) Modify the "stub" so that it keeps the stack aligned properly by saving/restoring the original callers return
address, or |
 | 2) allow for the extra stack usage by placing a dummy LAST argument on each function in the alternate page. NOTE: If you do this,
you MUST supply an extra argument to any direct calls (those which do not go through the stub). |
|
Common memory:
--------------
As described above, a bank switch implementation requires an area of common memory. If your system allows bank switching of only a portion of
the code space, the unswitched portion would be a good place for this. If your system switches ALL of the code at once, then you will have to
program the common memory into an identical location in ALL pages.
If your system does not GUARANTEE a specific code bank will be selected at RESET, you must either select the starting page in your startup code,
or place main() in common memory, and select the page before calling any code which resides in the page memory area.
Be sure to place any 'C' code that you want located in the common memory FIRST in your program. Then, use inline assembly code to create a COMMON
symbol saving the next available address (in common memory), and ORG to the paged memory. Follow this with the C code that you wish to be
placed in that page.
**NOTE** That the above label and ORG must be placed at IMMEDIATELY after
a function, or at the IMMEDIATE START of the program. Do not place it after any variable declarations, as this will change the active linker segment,
and the ORG will not control code placement.
Library functions can be placed in your common area of memory by placing an assembly language ORG statement in your 'C' file IMMEDIATELY AFTER the
LAST function definition. Since the libary functions are output right after your code, this will control their location. If you have already placed
startup and/or runtime code in the common memory, ORG to the COMMON symbol that you left to mark the free address.
STUB FUNCTION:
--------------
The biggest problem in writing the STUB function is to keep the stack aligned so that arguments can be passed correctly. If your functions in the second
page are called only only one at a time from the first page (ie: not-multi tasking), you can do this easily by storing the return address in a memory
location. Your stub will also have to switch the page, then call the desired function, restore the page and return to the original caller.
For each function in an alternate page that you want to call, you need a small function in either the common memory or the calling page which loads
the page and address of the calling function into registers, then jumps to the stub to save the current page/return address, select the page, call the
function, restore the page, and jump back to the return address.
Note that if you only call functions in paged memory from the mainline code (A common configuration when an existing non-paged design is modified to add
more code memory), you can simply reset the page selection to the mainline, instead of saving and restoring it. Additionally, if you have only two pages,
you only need to use the stub to call functions in the non-mainline page, and can therefore hard-code the page select on entry to the function as well.
When the function returns to the stub, it is important to preserve the content of the accumulator, as this will contain return data from the called function.
Also note that if you need to call "register" function through the stub you should also preserve the accumulator when setting the page at the beginning.
If you need to support multitasking, and/or the calling of multiple functions from alternate pages at once, you can do so by keeping the return address and
old page on the stack within the "stub". The disadvantage of this approach is that the extra return address and page appears on the stack as an additional
arguments to the called function, and must be accounted for by declaring dummy
LAST arguments in that functions. eg:
int funA2(int arg1, char arg2, dummy1, dummy2) { ... }
This does not occur with the first form of "stub", giving the advantage that the function can be called from any page with the same number of arguments.
With the second form, you would have to supply the dummy arguments if you called the function from its own page (directly, not via
the stub).
NOTE that 'C' requires that all stacked arguments be words (16 bits), so you must push/pop 16 bits, even if you only save an 8 bit page select.
If you do support multitasking, make sure that your kernal is modified to maintain the page select for each task that is active.
Here is a complete example in 68HC11 C/assembly. It implements a hybrid multi-page scheme where many pages are used, but the are all called from the
mainline code executing in page 0. This is a very common configuration as it allows your mainline code to be located in one page, and logically related
blocks of functions which are called only by the mainline code can be located in other pages. Note that the multiple pages are output at the same address
in the 'S' record file, and it is up to you (the developer) to insure that these blocks of memory are extracted and placed in the apropriate ROM pages.
/*
* System definitions and stub function (Must be FIRST in file)
*/
asm {
*
* System page memory and select locations
*
pagemem EQU $8000 Paged memory is located here
pagesel EQU $C000 Page select register
*
* Joiner function to call the initialization code in page 3.
* It is placed here (in common memory) so that main() can call it.
* Note that it "falls into" the callpag function.
*
init LDAA #3 Init function is in page 3
LDX #init3 Address of init function in page 3
*
* Function to perform a banked switched call to page(A), function(X)
* This is still in common memory (as configured in library).
*
callpag STAA pagesel Select new page
PULY Get old return address
STY saveret Save old return address
JSR ,X Call user function
CLR pagesel Reselect page 0 (mainline)
LDX saveret Get return address
JMP ,X Return (skipping select)
$DD:saveret 2 Word to save return address
}
/*
* Include header files here (after above) so that we can be sure that they
* do not affect the active linker output segment. If there were no functions
* in common memory, include these after the EQU and ORG at the end of this
* section.
*/
#include <6811io.h>
/*
* Main function
* Note that this function is still in common memory. You do not have
* to have main() in common memory if the page select is set by the
* startup code (before main() executes), or some other method of
* insuring that main()'s will be available when the program starts
* is implemented, such as a hardware page select at reset.
*/
main()
{
asm " CLR pagemem"; // Initialize to page 0
if(init()) // Call initialization code in page 3
return; // init failed - stop
mainline("Page 0"); // Call mainline function in page 0
}
/*
* End of initial common memory - save pointer to free location
*/
asm "COMMON EQU *"; // Save position to free common memory
/*
*** PAGE 0 functions
*/
asm " ORG pagemem"; // Reset location counter to start of page
/*
* Joiner functions to call the stub for page 1 and 2 functions.
* These are located in page 0 because in this implementation, they are
* only used from within page 0. If you are using a scheme which allows
* calling of paged functions back and forth between pages (via a stack
* based page save/restore), these "joiners" would have to be placed in
* common memory.
*/
int funA(char *s); // Prototype for function A
char *funB(char c); // Prototype for function B
asm {
funA LDAA #1 Calling function in page1
LDX #funA1 Address of function in page1
JMP callpag Call via stub
funB LDAA #2 Calling function in page2
LDX #funB2 Address of function in page2
JMP callpag Call via stub
}
/*
* This the "mainline" page. The stub is not used to call functions
* from this page because we initialized it as the default page, and
* it will be selected when no other page is active.
*/
mainline()
{
fun0(); // Call another function in page 0
funA("Called from page0"); // Call function in page 1
funB('0'); // Call function in page 2
}
fun0()
{
}
/*
*** PAGE 1 functions
*/
asm " ORG pagemem"; // Reset location counter to start of page
funA1(char *s)
{
putstr(s);
funC1(); // Stub not required because page1 already selected
}
funC1()
{
}
/*
*** PAGE 2 functions
*/
asm " ORG pagemem"; // Reset location counter to start of page
funB2(char c)
{
putch(c);
funD2(); // Stub not required because page2 already selected
}
funD2()
{
}
/*
*** PAGE 3 functions
*/
init3()
{
// Perform hardware/software initialzition here
}
/* At end of program, reposition to common memory for library */
asm " ORG COMMON";
|
|