|
|
View previous topic :: View next topic |
Author |
Message |
equack
Joined: 21 Sep 2009 Posts: 21
|
incorrectly constructed label |
Posted: Sun Oct 03, 2010 8:01 pm |
|
|
Is there any way to get the address of a function? label_address() and various #asm constructions all fail me with the error "incorrectly constructed label".
I am trying to create a vector table (of functions) because a series of if or switch statements is too large and slow.
Yes, know I could put an #ORG in front of each function to force it to a specific address and then call out those specific addresses from my vector table but I have 100 functions of different sizes so I would have to manually come up with an #ORG address for each one so that they all fit and didn't waste any ROM space.
There ought to be a way to do this but the inline assembly format doesn't seem to be documented anywhere (is it?)
Code: |
void CallMe(void)
{
....
}
void AddressMe(void)
{
int32 address;
address=label_address(CallMe);
}
...or ...
void AddressMe(void)
{
#asm
CALL CallMe
#endasm
}
|
|
|
|
equack
Joined: 21 Sep 2009 Posts: 21
|
|
Posted: Sun Oct 03, 2010 8:09 pm |
|
|
I have also tried:
Code: | void CallMe(void)
{
#asm
fakelabel:
NOP
#endasm
// code goes here
}
void CallAddress(void)
{
#asm
CALL fakelabel
#endasm
} |
It tells me I can't do that. Apparently labels are purely local- you can't refer to a label inside another function. I managed to trick CCS into allowing recursion but I'm not sure I can work around this one. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Mon Oct 04, 2010 2:24 am |
|
|
You do realise that 'switch' will do this for you?.
Key is, that must _not_ have a default case.
For example (a bit long, but you need a minimum number of cases to 'trigger' the behaviour).
Code: |
#include <18F452.h>
#device adc=8
#FUSES NOWDT, WDT128, HS, NOPROTECT, NOOSCSEN, NOBROWNOUT, BORV42, PUT, STVREN, NODEBUG, NOLVP, NOWRT, NOWRTD, NOWRTB, NOWRTC, NOCPD, NOCPB, NOEBTR, NOEBTRB
#use delay(clock=20000000)
#use RS232(UART1,ERRORS)
void fn1(void) {
delay_cycles(1);
}
void fn2(void) {
delay_cycles(2);
}
void fn3(void) {
delay_cycles(3);
}
void fn4(void) {
delay_cycles(4);
}
void main() {
int8 switch_val;
setup_adc_ports(NO_ANALOGS);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_ccp1(CCP_OFF);
while (TRUE) {
switch_val= getc();
if (switch_val<'0') switch_val='0';
if (switch_val>'5') switch_val='5'; //Add your own handling to prevent
//values outside the switch range
switch (switch_val) {
case '0':
fn1();
break;
case '1':
fn2();
break;
case '2':
fn1();
break;
case '3':
fn3();
break;
case '4':
fn4();
break;
case '5':
fn2();
break;
}
}
}
|
If you look at the assembler generated for the switch statement, you get:
Code: |
.................... switch (switch_val) {
007C: MOVLW 30
007E: SUBWF 05,W
0080: ADDLW FA
0082: BC 00A2
0084: ADDLW 06
0086: GOTO 00A6
//and at A6
00A6: ADDWF FE8,W
00A8: CLRF FF7
00AA: RLCF FF7,F
00AC: ADDLW C1
00AE: MOVWF FF6
00B0: MOVLW 00
00B2: ADDWFC FF7,F
00B4: TBLRD*-
00B6: MOVF FF5,W
00B8: MOVWF FFA
00BA: TBLRD*
00BC: MOVF FF5,W
00BE: MOVWF FF9
00C0: DATA 8A,00
00C2: DATA 8E,00
00C4: DATA 92,00
00C6: DATA 96,00
00C8: DATA 9A,00
00CA: DATA 9E,00
|
With this being a jump table, exactly as you are trying to code.
Same happens with the PIC16's as well (except using RETLW instructions, rather than TBLRD).
Requirements are (as far as I have identified):
1) No default.
2) Minimum of five cases.
3) Cases must be consecutive.
The compiler is a lot 'brighter' than is often thought, but unfortunately, these features are not documented, making it impossible for users to work out coding styles to use the abilities.
Best Wishes |
|
|
equack
Joined: 21 Sep 2009 Posts: 21
|
|
Posted: Mon Oct 04, 2010 11:22 am |
|
|
No, I did not realize that. Too bad it's not documented but it's good news! I'll have to get rid of my default case.
Thank you for the tip. |
|
|
equack
Joined: 21 Sep 2009 Posts: 21
|
|
Posted: Tue Oct 05, 2010 7:19 pm |
|
|
OK, it turns out that your suggestion solves the performance problem by creating a jump table but it still wastes an awful lot of ROM because it creates a call stub for each case instead of having the jump table call my functions directly.
When I examine the assembly listing I see that I now have 128 pieces of code that look like these:
Code: |
This:
switch(*line)
{
...
case PRINT_TOKEN: PrintCMD(); break;
case IF_TOKEN: UnusedCMD(); break;
case INPUT_TOKEN: InputCMD(); break;
...
}
Turns into this:
.................... case PRINT_TOKEN:
C1C4: GOTO PrintCMD
C1C8: MOVLB C
C1CA: BRA C4B0
.................... case IF_TOKEN: UnusedCmd(); break;// 163
C1CC: CALL UnusedCMD
C1D0: MOVLB C
C1D2: BRA C4B0
.................... case INPUT_TOKEN: InputCMD(); break;// 164
C1D4: GOTO InputCmd
C1D8: MOVLB C
C1DA: BRA C4B0
|
If the jump table were to call my functions directly it could save 1024 bytes of ROM.
At the very least the compiler could save the "MOVLB 0x0C" instruction until the end instead of doing it 128 times. That would save 254 bytes of ROM.
I would happily declare all of my functions as #SEPARATE if I thought it would help.
At this point I would still would prefer to roll my own in assembly and save the 1024 bytes. I can't do that because I can't figure out how to get the function pointers without a lot of manual #ORG calculations.
Is there any way to get the function pointers? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
equack
Joined: 21 Sep 2009 Posts: 21
|
|
Posted: Wed Oct 06, 2010 12:54 pm |
|
|
Thank you PCM. This worked:
Code: |
typedef void (*_fptr)(void);
const _fptr fptr[4] = {EndCMD,PrintCMD,InputCMD,NewCMD};
void ListFour(void)
{
int loop;
for(loop=0;loop<4;loop++)
{
printf("Function %d is at address %ld\r\n",loop,fptr[loop]);
}
}
|
I can call individual functions:
But I cannot call an arbitrary function- I get the message "code has no effect" when I try this:
It's an interesting limitation but it is not a problem.
Looking at the listing I can see that the compiler creates a table of 16 bit addresses in FLASH. I have not tried compiling for a part with 128K of FLASH. I just got some samples of the PIC18F27J53 so it will be interesting to see if I can have "far" function pointers. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|