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

pointer help ^^ please !!!
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

pointer help ^^ please !!!
PostPosted: Tue Aug 27, 2013 2:30 pm     Reply with quote

hello,

I'm trying to do something with pointer for more than 1 hour and I don't find the solution :(

Can you help me please Smile

What I want to do:
I have an int16 and I would like to change the first 8bit and the last 8bit separately

For example:
Code:
int16 x = 0x1234;

"something with * and & " to change 12 in 56 to obtain directly x = 0x5634
and
"something with * and & " to change 34 in 78 to obtain directly x = 0x5678

It's to understand how pointer work.

Thanks for your help
temtronic



Joined: 01 Jul 2010
Posts: 9229
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Aug 27, 2013 8:04 pm     Reply with quote

You might look in the manual for the functions make16(), make8() as well as 'unions'.
I'm fairly sure the make16() function will do what you want and not have to figure out 'pointers'.

hth
jay
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Tue Aug 27, 2013 8:41 pm     Reply with quote

Using pointers is a poor way to do that. Within CCS make8() and make16() are the best way to do this. If you want to write code for reuse with other compilers you should use AND and OR or use modulo math depending on what should happen if you switch between big endian and little endian processors.
_________________
The search for better is endless. Instead simply find very good and get the job done.
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Tue Aug 27, 2013 10:49 pm     Reply with quote

Using a union will probably solve the problem as it allows byte addressing then.

Regards
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Wed Aug 28, 2013 2:53 am     Reply with quote

As explained, pointers are a poor way to do this. The PIC, has to work quite hard to deal with pointers, compared to many other processors. Hence all the comments about the better ways of doing this.

However I note your 'last line'.

Now, pointers are just an address. So with your declaration of 'x', then:

&x

will give a number giving the address of 'x' in memory.

Now the 'pointer', has a (possibly strange) thing about it. It remembers the 'size' of the target object. So:

(&x)+1

Will give an address _two_ bytes higher in memory, since it 'knows' that 'x' is an int16. This is standard C, and covered in textbooks about C.

The next thing is that in C, you can change the 'type' of something with a 'cast' (putting a type in brackets in front of the 'thing'). So:

(int8 *)&x

is the same number for the address, but C has now been told to treat it _as if_ it was the address of an int8, not an int16.
So:

((int8 *)&x)+1

Now gives an address just one byte higher in memory.

So:
Code:

   int16 x = 0x1234;
   int8 val1,val2;

   val1=*((int8*)&x);
   val2=*(((int8*)&x)+1);


Will read your two bytes using pointers, while:
Code:

   int16 x = 0x1234;
   *((int8*)&x) = 56;
   *(((int8*)&x)+1) = 78;


Would change your two bytes 'using pointers'.

However, this takes 23 instructions, versus:

x=make16(78,56);

at just 3 instructions....

A 'pointer' is also the same thing in C as an array. So you can do some slightly 'odd' things:
Code:

   int16 x = 0x1234;
   int8 byte_list[]; //declares 'byte_list' to be a pointer to int8's
   //int8 * byte_list; //alternative doing the same declaration

   byte_list=&x; //'byte_list', now _points_ to X

   byte_list[0] = 56;
   byte_list[1] = 78; //writes the two values

   //or
   *byte_list = 56;
   *(byte_list+1) = 78; //and does it using 'byte_list' as a pointer instead.


This is all in standard C text books, and should really be where to start.

Now the 'possibly interesting' thing here is that except for the extra operation of copying the address to 'byte_list', when you use it as an array, with fixed indexes, the compiler can solve the location in memory at compile time, and automatically does this, while once you start using a 'pointer' the efficiency again plummets...

Best Wishes
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

PostPosted: Wed Aug 28, 2013 3:35 am     Reply with quote

thanks a lot for your reply,
I understand beter how it works Smile

I'm just surprised about the number of instructions when we use pointers, I thought it was more complicate for compilation and easier for Pic..
bad thinking Smile

Anyway, thanks again :D
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Wed Aug 28, 2013 7:51 am     Reply with quote

It is down to the fundamentally '8bit' architecture of the PIC16/18.

An address is a 16bit value.

So to calculate a location based on an address, two memory cells have to be accessed, the offset added, to get the address required. A handful of instructions straight away. Then the resulting 16 bit value has to be copied to two CPU registers which are used for 'indirect addressing'. Another handful of instructions, and then the 'target' value has to be read/written through the indirect access register. Yet more work....

Now many historically '8bit' processors, have some registers that work as 16bit registers and can be accessed/have maths done on them, directly as 16bit values. The smaller PIC's don't.

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Aug 28, 2013 1:50 pm     Reply with quote

Ttelmah, what do you think of this method ? The compiler generates
code that uses the FSR and the code is fairly compact. Do you see
anything wrong it ? I tested it with MSVC 6.0 and it worked there as well.

The generated code size for a zero offset is the same as your pointer
code. But for non-zero offsets, the macro generates the same small
amount of code as with a zero offset. That's where it's better.

Explanation of the code below:
The BytePtr macro casts the int16 to a char, and then it takes the address
of it. Pointer arithmetic will now work on a byte basis. Adding the offset
creates a pointer to the desired byte in the int16, which then can be
dereferenced in the main program.
Code:

#include <18F4520.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)

#define BytePtr(var, offset)   (&(char)var + offset)

//======================================
void main(void)
{
int16 x;

x = 0x1234;
*BytePtr(x, 0) = 0x56;
printf("%lx \r", x);

*BytePtr(x, 1) = 0x78;
printf("%lx \r", x);

while(1);
}
asmallri



Joined: 12 Aug 2004
Posts: 1635
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Wed Aug 28, 2013 4:11 pm     Reply with quote

PCM programmer wrote:
Ttelmah, what do you think of this method ? The compiler generates
code that uses the FSR and the code is fairly compact. Do you see
anything wrong it ? I tested it with MSVC 6.0 and it worked there as well.

The generated code size for a zero offset is the same as your pointer
code. But for non-zero offsets, the macro generates the same small
amount of code as with a zero offset. That's where it's better.

Explanation of the code below:
The BytePtr macro casts the int16 to a char, and then it takes the address
of it. Pointer arithmetic will now work on a byte basis. Adding the offset
creates a pointer to the desired byte in the int16, which then can be
dereferenced in the main program.
Code:

#include <18F4520.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)

#define BytePtr(var, offset)   (&(char)var + offset)

//======================================
void main(void)
{
int16 x;

x = 0x1234;
*BytePtr(x, 0) = 0x56;
printf("%lx \r", x);

*BytePtr(x, 1) = 0x78;
printf("%lx \r", x);

while(1);
}


I frequently use this method when working with PICs. This is because I use compilers from different vendors and make8() and make16() are not portable.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 12:44 am     Reply with quote

Quote:
I frequently use this method when working with PICs. This is because I use compilers from different vendors and make8() and make16() are not portable.


Code:
#define BytePtr(var, offset)   (&(char)var + offset)

*BytePtr(x, 1) = 0x78;


I think, most portable codes are using this kind of "universal typecasting". But unfortunately, CCS C doesn't recognize it's nature, resulting in a superfluous real pointer operation. So an effective implementation should refer to make8() or unions for CCS C.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 1:07 am     Reply with quote

Very little is really 'portable'.

You have to remember that the byte order is not the same in many CPU's, so the pointer operation won't be portable.

At the end of the day, the most 'portable', is a union, with the bytes named as high/low, which can then be created for any chip, whatever the memory arrangement.

The 'byte pointer', works well for any chip using LSB lowest, and the CCS compiler is (fortunately) smart enough to not use pointer based operations for constant offsets here, giving good efficiencies, though it's 'efficiency' is really only a result of the compiler being reasonably smart here, and will disappear as soon as variables are used, but that is because of the inefficiencies of the chip here.
At the end of the day, the 'lesson' is that you as a user, need to be aware of the hardware limitations.
A big 'caveat' comes here on 16bit chips, requiring _word_ alignment.

'make8', and 'make16' equivalents, are very easy to generate as defines on other chips.

Best Wishes
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 9:55 am     Reply with quote

Quote:
the CCS compiler is (fortunately) smart enough to not use pointer based operations for constant offsets here

Really?

Code:
#define hi(x)  (*(&x+1))
#define lo(x)  (*(&x))

                    00758 .................... b = lo(h);
0238 6A03           00759 CLRF   03
023A 0E76           00760 MOVLW  76
023C 6EE9           00761 MOVWF  FE9
023E C003 FFEA      00762 MOVFF  03,FEA
0242 CFEF F064      00763 MOVFF  FEF,64
                    00764 .................... b = hi(h);
0246 6A03           00765 CLRF   03
0248 0E78           00766 MOVLW  78
024A 6EE9           00767 MOVWF  FE9
024C C003 FFEA      00768 MOVFF  03,FEA
0250 CFEF F064      00769 MOVFF  FEF,64
                    00770 .................... 
                    00771 .................... b=make8(h,0);
0254 C076 F064      00772 MOVFF  76,64
                    00773 .................... b=make8(h,1);
0258 C077 F064      00774 MOVFF  77,64
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 11:15 am     Reply with quote

Yes, using the pointers as shown in PCM programmers post, _not_ in general operation.

It's an 'oddity' that it doesn't do it for normal operations....

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 1:55 pm     Reply with quote

Also note that the hi() macro posted above will produce incorrect results.
The 'h' variable is an int16 so when hi() adds 1 to the address, it does
pointer arithmetic based on the word size, giving 0x78:
Code:

#define hi(x)  (*(&x+1))
#define lo(x)  (*(&x))

                    00758 .................... b = lo(h);
0238 6A03           00759 CLRF   03
023A 0E76           00760 MOVLW  76
023C 6EE9           00761 MOVWF  FE9
023E C003 FFEA      00762 MOVFF  03,FEA
0242 CFEF F064      00763 MOVFF  FEF,64
                    00764 .................... b = hi(h);
0246 6A03           00765 CLRF   03
0248 0E78           00766 MOVLW  78   **** <== Should be 0x77
024A 6EE9           00767 MOVWF  FE9
024C C003 FFEA      00768 MOVFF  03,FEA
0250 CFEF F064      00769 MOVFF  FEF,64

But you want byte arithmetic, giving 0x77, so then hi() has to be
something like this,
Code:
#define hi(x)  (*((int8 *)&x+1))

which then expands to a large amount of ASM code:
Code:

.................... b = hi(h);
00FC:  CLRF   @@0B
00FE:  MOVLW  h
0100:  MOVWF  @@0A
0102:  MOVLW  01
0104:  ADDWF  @@0A,W
0106:  MOVWF  @01
0108:  MOVLW  00
010A:  ADDWFC @@0B,W
010C:  MOVFF  01,FSR0L
0110:  MOVWF  FSR0H
0112:  MOVFF  INDF0,b
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Aug 29, 2013 2:46 pm     Reply with quote

Quote:
Also note that the hi() macro posted above will produce incorrect results.


Yes, thanks for pointing this out. I don't use it in real code, it was only for demonstrating how CCS C treats
this kind of constructs. And it's even worse, as your example shows. Sad
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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