I have a unique application that is about to overflow 64k. I can not use bank switching because of the overhead. PLEASE do not reply anything about how small the overhead is - I do not care, there IS overhead. Now this application process ONLY ONE OF two formats "classic" and "current". My idea is to locate all common code in the lower 32 k and, immediately after reset detect which format it is and set the COBANK bits so that either page 1 or page 2 would be code addresses 8000 to ffff to process the actual format. I know of no simple way to do this, suggestions appreciated. Erik Cross posted on the SILabs forum.
For the common code to be ignorant of which format bank is in use, there must be a defined API that is the same for both "format" banks. The common code can call this set of routines (and no other) to be sure it works with either bank. So, there is a set of addresses for the functions, say f1() through f10(). The linker's job is to put the addresses of these functions into the points of call in the common bank. Usually, the linker is allowed to locate the functions, so that it knows their addresses. In this case, you have two different sets of functions, which will likely have different code sizes, and thus which will produce different addresses if the linker is left to its own devices. One solution to this problem is to specify the address for each function in the API. This will require some tweaking to make sure enough room is left for the larger of each function so that the addresses align in both banks. This method will be fairly painful if done manually. It's possible to write a tool to choose addresses based on the larger of the two possible sizes, and output the linker control string, but I don't think you can get the linker to do so automatically. Extending the scheme to more than two format methods gets increasingly more painful. This method also wastes a fair amount of code space, as you have to leave gaps to align the functions at their absolute addresses. The usual solution to this quandary is a vector table. One well-known location, say the start of the bank region, contains a table with the addresses of the functions in the API. Common code calls to a banked function look up the function address and jump to it. You'd probably have wrapper routines in the common bank thus:
U16 FormatF2 (U8 a) { return (*FormatVector[2])(a); }
"The usual solution to this quandary is a vector table. One well-known location, say the start of the bank region, contains a table with the addresses of the functions in the API ..." Or a table of JMP instructions to minimize the overhead...
/* A table of vector (ASM JMP) instructions instead of pointers resides at * 0x8000 in both pages 1 and 2 with each vector LJMP'ing to its associated * function. The target function addresses can be different for either page, * but the vector instruction is at its known location. The overhead is an * LJMP. */ #define F0 ((FP)0x8000) #define F1 ((FP)0x8003) #define F2 ((FP)0x8006) #define F3 ((FP)0x8009) typedef void (*FP)(void); void main(void) { /* Determine format and set COBANK accordingly, then ... */ for (;;) { F0(); /* Generates an LCALL 0x8000, which LJMPs to f0. */ F1(); F2(); F3(); } }
Sorry, this is what I compiled, but not what I posted:
typedef void (code *FP)(void);
thnks all, I will keep this till it gt there I have only one format at this time (the "PC generator") for the new format is still in development, I just needed to have a row to put the ducks in. I especially like the linker wildcarding, that will make it manageable. thanks again to all Erik
View all questions in Keil forum