Just as the compiler has added custom support for bits, it should also add custom support for function pointers. Here is one idea:
//Saves on storage (1 byte) //Is faster (fixed jump table) //No need to add/remove references in the linker void fn1( void ){}; void fn2( void ){}; void fn3( void ){}; void fn4( void ){}; enumfn MyFnType { fn1, fn2, fn3 }; //must have same signatures MyFnType myFnVar; void FnA(void) { myFnVar = fn1; // do not add fn1 to FnA's call tree myFnVar = fn4; // compiler error } void FnB(void) { (myFnVar)(); // do add fn1,fn2,fn3 to FnB's call tree }
You can accomplish the same thing with the tools now. Refer to the "TABLES OF FUNCTION POINTERS" section in the following application note: http://www.keil.com/appnotes/docs/apnt_129.asp Jon
Even though I am into "Tree preservation", not having to use the error prone OVERLAY directive sounds great. Can you help me? My simple example fails. I must be missing something.
void fn1(void){ } void fn2(void){ } void fn3(void){ } code void (code *fp_tab [])(void) = { fn1, fn2, fn3 }; void ( code* fp)(void); void fn( void ) { fp = fp_tab[0]; } void main(void) { fn(); (*fp)(); } ?C_C51STARTUP +--> ?PR?MAIN?STATETEST ?PR?MAIN?STATETEST +--> ?PR?FN?STATETEST ?? where is ?CO?STATETEST ?PR?FN?STATETEST +--> ?CO?STATETEST ?? ?CO?STATETEST should not be here ?CO?STATETEST +--> ?PR?FN1?STATETEST +--> ?PR?FN2?STATETEST +--> ?PR?FN3?STATETEST
; FUNCTION fn (BEGIN) ; SOURCE LINE # 12 ; SOURCE LINE # 13 ; SOURCE LINE # 14 0000 900000 R MOV DPTR,#fp_tab 0003 E4 CLR A 0004 93 MOVC A,@A+DPTR 0005 FE MOV R6,A 0006 7401 MOV A,#01H 0008 93 MOVC A,@A+DPTR 0009 8E00 R MOV fp,R6 000B F500 R MOV fp+01H,A ; SOURCE LINE # 15 000D 22 RET ; FUNCTION fn (END)
Since only constants are involved, the compiler should be able to optimize the "fp = fp_tab[0];" to a simple move. Instead you get... The 8051 only provides one instruction for reading code memory: MOV A, @A+DPTR. The code generated by the compiler looks pretty optimal to me. Can you suggest a better solution? Jon
Even though I am into "Tree preservation", not having to use the error prone OVERLAY directive sounds great. Can you help me? My simple example fails. I must be missing something. 1. Your program stores function pointers in the fp_tab which is stored in the code segment. The name of this segment is ?CO?STATETEST. It is important to note that these are the ONLY CODE variables in this module. If there are other CODE variables in this module the following method may not work. 2. Since your program references fp_tab in the fn function, the linker thinks that fn must call one of the functions in fp_tab and so creates an entry in the call tree. However, fn only reads the function address. 3. Main actually calls a function stored in fn_tab. However, since there is nothing in the main function that references fn_tab, the linker creates no entries in the call tree. 4. To fix this, you must remove the reference from the fn function to fn_tab (which is the ?CO?STATETEST segment) and you must add a reference from the main function to fn_tab. The following overlay commands will do the trick.
overlay (FN ~ ?CO?STATETEST, MAIN ! ?CO?STATETEST)
SEGMENT DATA_GROUP +--> CALLED SEGMENT START LENGTH ---------------------------------------------- ?C_C51STARTUP ----- ----- +--> ?PR?MAIN?STATETEST ?PR?MAIN?STATETEST ----- ----- +--> ?PR?FN?STATETEST +--> ?CO?STATETEST ?CO?STATETEST ----- ----- +--> ?PR?FN1?STATETEST +--> ?PR?FN2?STATETEST +--> ?PR?FN3?STATETEST ?PR?FN1?STATETEST 0008H 0008H ?PR?FN2?STATETEST 0008H 0010H ?PR?FN3?STATETEST 0008H 0004H
How about mov fp,#high(fn1) mov fp+1,#low(fn1)
The root message on this thread was a suggested extention that would allow the user not to have to mess around with manually setting an OVERLAY directive. You message said it could be done now, and to see this application note. Now your saying, you need an OVERLAY directive. I think I just did not express my idea clear enough. The point is that bits are a first class part of the compiler. It sure would be nice if functions pointers could also be made a first class part also. I was just presenting a simple idea that would optimize storage, execution, and get rid of the OVERLAY directive. It's just a open suggestion.
Jon, Your program doesn't follow the guidelines laid out in the application note. So, you must use the overlay directive. If you follow the application note's directions, you can create programs using tables of function pointers without the need for the overlay directive. I just assumed that you read the application note, created a program that didn't follow those directions, and then needed help with the overlay directive. Jon
Jon, I would like to use this table of function pointers technique, to eliminate the OVERLAY directive. I have read the app note. I just do not see how I can use it. I have the typical requirement of assigning the fn pointer in one routine and calling the fn pointer in another routine. Can my simple example be restructured to follow the app note?
I have the typical requirement of assigning the fn pointer in one routine and calling the fn pointer in another routine. Can my simple example be restructured to follow the app note? If that's the case, then no. If you can make the assignment inside the function that actually calls thru the function pointers, then yes. However, that doesn't appear to be the case. The call tree is built using the associations that your program physically has. If you have a function that accesses a table of function pointers, then a reference is made (because that function references the table). And, the linker, in the call tree, indicates that the function that accessed the table calls that code segment which in turn calls the functions in the table. You can disable this assumption using the SPEEDOVL linker directive. But, that doesn't automatically add the functions into the call tree for you. If you call the function thru a different pointer (like you example does), the linker cannot be expected to create the call tree. Since the value of the variable may be anything, the linker can't guess what function(s) it may be used to call. Therefore, you must add the reference to the call tree manually. The application note makes the following points: If you... 1. Create a table of function pointers, and 2. Store the table in CODE space (so the pointers cannot be changed), and 3. Access this table in the function where you actually call the functions whose addresses are in the table, then... The linker creates a call tree that requires no changes (using OVERLAY). The application note was intended to show all sides of the overlay issue as well as a trick that I use to avout using the overlay directive for large tables of function pointers. Jon
Here is another approach that would work well for me. Just optimize ( *constFnTbl[i] )(<parms>);
// //c code // typedef char (code* PFN)(char); code fnTbl[] = { fn1, fn2, fn3 }; void main( void ) { volatile char i; volatile char x = ( *fnTbl[i] )( 2 ); } // // I would like // main: mov r7,#2 mov a,i mov dptr,#jmpTbl call evalfn mov x,r7 ret jmpTbl: ajmp fn1 ajmp fn2 ajmp fn3 evalfn: lsl a jmp @+dptr // // Current code // 0000 AF00 R MOV R7,i 0002 EF MOV A,R7 0003 25E0 ADD A,ACC 0005 2400 R ADD A,#LOW fnTbl 0007 F582 MOV DPL,A 0009 E4 CLR A 000A 3400 R ADDC A,#HIGH fnTbl 000C F583 MOV DPH,A 000E E4 CLR A 000F 93 MOVC A,@A+DPTR 0010 FE MOV R6,A 0011 7401 MOV A,#01H 0013 93 MOVC A,@A+DPTR 0014 F582 MOV DPL,A 0016 8E83 MOV DPH,R6 0018 7F02 MOV R7,#02H 001A 120000 R LCALL ?C0005 001D 8002 SJMP ?C0006 001F ?C0005: 001F E4 CLR A 0020 73 JMP @A+DPTR 0021 ?C0006: 0021 8F00 R MOV x,R7 ;0023 22 RET ; FUNCTION main (END)