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 support@ccsinfo.com

Optimizing built-in functions

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



Joined: 12 Sep 2003
Posts: 43

View user's profile Send private message

Optimizing built-in functions
PostPosted: Thu Jul 26, 2007 2:36 pm     Reply with quote

My program has a lot of user messages, stored in rom, as such:

Code:
int8 const msgHello[7] = {"Hello!"};


Since we're not allowed to have pointers to constants, I cannot do this:

Code:
SendToScreen(msgHello);


Therefore, I copy the rom to ram beforehand:

Code:
int8 buffer[20];

strcpy(buffer, msgHello);
SendToScreen(buffer);


This was all fun an dandy, until I reached the ROM limit of my chip, not with messages, but with code. Upon inspection (LST and TRE files), I realized that the compiler does a trick to escape the pointers to constant limitation, and repeats the code for strcpy() every time I call it in the code, instead of placing in separate.

So I've got 177 times strcpy in my code, each eating about 20 bytes.

The same thing happens with memcpy, which I also use extensively to copy ram to ram mostly. It's 62 times in the code, each time eating new codespace.

Now I wonder, how can I optimize my code for space? Can I change the way I do things to prevent unnecessary duplications of internal functions? Should I make my own "myStrCpy" and just pass along the pointers to force a #seperate on it? Are there better methods?

Maybe I'm having a horribly wrong approach to this problem that one of you would have an elegant solution. I post here for your comments and suggestions, I know people here are very knowledgeable.

I should point out that I am using compiler 3.249 on a PIC18F6722 chip.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Jul 26, 2007 3:30 pm     Reply with quote

Post the SendToScreen() function.
libor



Joined: 14 Dec 2004
Posts: 288
Location: Hungary

View user's profile Send private message

PostPosted: Thu Jul 26, 2007 3:36 pm     Reply with quote

You could store your strings in rom with
Code:
const string1 = "First blabla";
const string2 = "Second blabla";
...

instructions, and then use a modified
Code:
SendToScreen(int16 address_in_rom) {
... //a loop till end of string reached
value = read_program_eeprom(address_in_rom++);
...//send next character to screen
}

function to send the data direct from rom to the screen with no copying back and forth with a function call like this:
Code:
SendToScreen(label_address(string1));
Laurent Chouinard



Joined: 12 Sep 2003
Posts: 43

View user's profile Send private message

PostPosted: Thu Jul 26, 2007 3:50 pm     Reply with quote

PCM Programmer

Well it depends, sometimes I have to send commands to an i2c LCD screen or drive parallel ports.. but in a nutshell, assume that SendToScreen() wants to load the string into RAM for processing, like:

Code:

void SendToScreen(int16 *string) {
  int8 i = 0;

  i2c_start();
   //etc
  while (string[i] != 0) {
    i2c_wrte(string[i]);
  }
  i2c_stop();
}


Something like that... does that answer your question?



libor:
Wouldn't the "label_address()" function require a pointer to a constant, which the compiler would reject? Otherwise I am not entirely sure what you mean by that...
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jul 27, 2007 12:36 am     Reply with quote

Here's one idea that might work:

CCS has a special feature where if a function takes a character as the
argument, then if you pass it a constant string, the compiler will feed the
characters to the function one at a time until they're all sent. This
feature eliminates the need to use strcpy() and a RAM buffer.

So, how can we use this feature and yet also do the i2c operations that
come before and after sending the string ? One way is to put tags at
the start and end of the string. When the SendToScreen() function sees
one of these tags, it knows that it should do the i2c operation(s) instead
of sending the character. The rest of the time, it just sends the character.

In the code below, I've used a '$' to indicate the start of a string and '\r'
to indicate the end. Ideally these characters ought to be made into
constants with a #define statement so you could easily change them to
something else. Also, ideally, a macro would be used to automatically
prefix and append these tag characters to the literal string, so that you
don't have to manually do it. Let the compiler do it. It would be safer
that way. Then you would avoid typing errors that could cause a bad
bug to be built into the code if you forgot to type in the tag character.
I don't have time to do this macro.

Also, by using a static state variable in the SendToScreen() routine,
you could possibly eliminate the need for the "start of string" character.

This is all just an idea. There may be some better way to do this.
Code:

#include <18F452.h>
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)


const char msg1[] = {"$Hello World\r"};
const char msg2[] = {"$Hi there\r"};
const char msg3[] = {"$Last Message\r"};


void SendToScreen(char c)
{
if(c == '$')
  {
   i2c_start();
   // etc.
  }
else if(c == '\r')
  {
   i2c_stop();
  }
else
  {
   i2c_write(c);
  }

}


//=============================
void main()
{

SendToScreen(msg1);
SendToScreen(msg2);
SendToScreen(msg3);

while(1);
}


The generated ASM code looks like this. It is less code than if you
had to do the strcpy() first. However, it also adds 2 bytes of overhead
per string.
Code:
...... SendToScreen(msg1);
0104:  CLRF   06
0106:  MOVF   06,W
0108:  RCALL  0004  // Fetch a char from the const array
010A:  IORLW  00
010C:  BZ    0116
010E:  INCF   06,F
0110:  MOVWF  07
0112:  RCALL  00A8 // Call SendToScreen() with the char in 0x07.
0114:  BRA    0106
libor



Joined: 14 Dec 2004
Posts: 288
Location: Hungary

View user's profile Send private message

PostPosted: Fri Jul 27, 2007 2:10 am     Reply with quote

sorry for the incomplete code, now I have more time (this works with my v.4.0xx compilers, I do not have have v.3 to test it with, I don't know if features has been added to v.4. you can't compile this)
Code:
const char string1[] = {"First blabla"};
const char string2[] = {"Second blabla"};

SendToScreen(int16 address_in_rom) {
  char c;
  i2c_start();
  do {
    c = read_program_eeprom(address_in_rom++);
    i2c_write(c);
  } while (c!=0);
  i2c_stop();
}

SendToScreen(label_address(string1));
Laurent Chouinard



Joined: 12 Sep 2003
Posts: 43

View user's profile Send private message

PostPosted: Fri Jul 27, 2007 9:33 am     Reply with quote

Thank you both for your contributions.

PCM Programmer

That seems to be a very elegant solution. I would have to rewrite the top of SendToScreen a bit in order to actually accumulate each bytes into a buffer, waiting for the terminating character, before doing the work. This is because the messages themselves need to be formatted, adjusted and whatnot before being send out to the i2c LCD controller.

Still, I could do it without start or end characters (other than the normal terminating 0x00), so it could be quite efficient. I'll try something to see.


libor

That's quite clever. I've just tried it, by simply changing how I call my function, and I can save roughly 25 bytes per message I have to print. I save 12 bytes changing the call from strcpy your call suggestion by rom address, and then I save one function call altogether (since I'm already in SendtoScreen, I can remove the overhead), saving another 12 or so bytes.

All in all, this gives me a solid 4KB of saved rom space without any further tweaking.

Thanks guys.
libor



Joined: 14 Dec 2004
Posts: 288
Location: Hungary

View user's profile Send private message

PostPosted: Fri Jul 27, 2007 10:02 am     Reply with quote

If you are absolutely running out of rom space you can implement some kind of compression in your strings:

if you have many messages having some common repeating words, you can take use of the centralized data retrieval function and use one byte macros.
e.g. you have the words ' help ' ' clear ' ' stop ' ' press ' ' button ' (with spaces) used over-and-over in several strings, so you can substitute these substrings with the 0x01, 0x02, ... non-printing character and replace this character with the corresponding substring in the SendToScreen function.

There are many more possibilities: e.g. in an application I have left out the intraword spaces and coded it as the 7.bit set in the previous character

This though make your code (the messages) less readable/manageable. The actual rom saving depends on your messages.
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