CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

incorrectly constructed label

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
equack



Joined: 21 Sep 2009
Posts: 21

View user's profile Send private message

incorrectly constructed label
PostPosted: Sun Oct 03, 2010 8:01 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Oct 03, 2010 8:09 pm     Reply with quote

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: 19513

View user's profile Send private message

PostPosted: Mon Oct 04, 2010 2:24 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Oct 04, 2010 11:22 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Oct 05, 2010 7:19 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Oct 05, 2010 7:47 pm     Reply with quote

There are posts in the archives on creating an array of function pointers:
http://www.ccsinfo.com/forum/viewtopic.php?t=37478
equack



Joined: 21 Sep 2009
Posts: 21

View user's profile Send private message

PostPosted: Wed Oct 06, 2010 12:54 pm     Reply with quote

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:
Code:

        (*fptr[1])();

But I cannot call an arbitrary function- I get the message "code has no effect" when I try this:
Code:

        (*fptr[loop])();


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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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