Author Topic: [gcc C] concatenating text to make up code  (Read 19879 times)

0 Members and 1 Guest are viewing this topic.

Offline Nusa

  • Super Contributor
  • ***
  • Posts: 2416
  • Country: us
Re: [gcc C] concatenating text to make up code
« Reply #75 on: October 09, 2018, 07:29:52 am »
volatile is a keyword that tells the compiler not to make any assumptions when optimizing this. It may not be strictly required, but it's a good idea for things like this.

White space before the * is optional. I tend to add a space myself.

If you were doing this with variables it would be something like so, in concept:
volatile uint8_t * myAddress;

myAddress = (your address calculation); // assigning an address to the pointer.
*myAddress = (your pin value); // setting the memory location pointed to by myAddress.

If you replace myAddress with (your address calculation):
*(your address calculation) = (your pin value); // setting the memory location pointed to by (your address calculation).

See why you need the leading *, at least?

What I left out was some required casting of the calculation to make the compiler happy. The cast would be the same (volatile uint8_t *) used to declare myAddress. Parens are required when casting types.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #76 on: October 09, 2018, 07:31:28 am »
Yea I get volatile
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #77 on: October 09, 2018, 07:32:49 am »
Dunno if this helps

So the PORTS_OFFSET... expression calculates a pointer address

(uint8_t *) casts this as a pointer to an 8 bit unsigned integer. Lets leave volatile for a bit but then using *(uint8_t *)(XXX) then de-references the pointer (i.e. gets the 8 bit unsigned integer the pointer points at).




So basically the name and address of the variable is the same?
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #78 on: October 09, 2018, 07:34:27 am »


What I left out was some required casting of the calculation to make the compiler happy. The cast would be the same (volatile uint8_t *) used to declare myAddress. Parens are required when casting types.


That is the confusing bit.
 

Offline Nusa

  • Super Contributor
  • ***
  • Posts: 2416
  • Country: us
Re: [gcc C] concatenating text to make up code
« Reply #79 on: October 09, 2018, 07:49:14 am »


What I left out was some required casting of the calculation to make the compiler happy. The cast would be the same (volatile uint8_t *) used to declare myAddress. Parens are required when casting types.


That is the confusing bit.

When you use:
*myAddress = (your pin value);
the compiler already knows it's of type (volatile uint8_t *), because that's how MyAddress was defined.

When you use:
*(your address calculation) = (your pin value);
the compiler doesn't know what type the calculated value is, so you have to tell it with a cast! In particular it doesn't know the size of the operation. Should the assignment be to a byte, word, double_word, etc. It makes a difference!

So you use this:
*(volatile uint8_t *)(your address calculation) = (your pin value);

 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: [gcc C] concatenating text to make up code
« Reply #80 on: October 09, 2018, 08:35:43 am »
It's syntactically a bit confusing in C to just write stuff into a memory address.

First, you need to tell the compiler which kind of store instruction it needs to generate, because let's say on a typical 32-bit CPU, memory can be written in 8-bit, 16-bit or 32-bit operation. If you use a 32-bit operation to do the store of your 8-bit value, you would accidentally overwrite the neighbors!

C is "high level" enough that it simply lacks operators to do this directly.

So, even if the end-result is not using pointers, you need to use the syntax for creating a pointer, then dereferencing it - neatly in one expression. This way, you can tell the compiler which write operation to use. For example, to write 8 bits into memory address 0x1234, you first make a pointer with your desired 8-bit type:

(uint8_t*)0x1234

Then you dereference it: meaning you write to the location pointer by this pointer:

* ((uint8_t*)0x1234) = something;

Now the pointer itself is meaningless, and it won't exist (or maybe it does, depending on the addressing modes and instructions available on the particular CPU, but this is irrelevant; the compiler knows how to do the write). But you temporarily use the pointer syntax to give the compiler all the information it needs. The only reason the pointer needs to have a certain type is so that the compiler knows whether to pick a 8-bit, 16-bit or 32-bit store instruction. Otherwise, it's just an address.

Volatile is added to tell the compiler that yes, it really needs to perform the operation, right now.

Doing very low-level things in higher level language such as C may actually be more difficult to grasp than writing in assembly directly. This is because the C syntax is not designed around doing assembly-level stuff all the time. So you need to do syntactical extras such as play with a "pointer" even when an actual pointer is not involved.

Hope this helps.
« Last Edit: October 09, 2018, 08:38:04 am by Siwastaja »
 
The following users thanked this post: TK

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #81 on: October 09, 2018, 08:59:15 am »
But I can write assembler in C can't I? so in assembler how do you write to a memory location?

My other syntax question is reading from these memory locations, do I just put the variable wanting the value to the left of the = and the same address syntax to the right?

variable = (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET)) & mask;
 

Offline Nusa

  • Super Contributor
  • ***
  • Posts: 2416
  • Country: us
Re: [gcc C] concatenating text to make up code
« Reply #82 on: October 09, 2018, 09:33:27 am »
But I can write assembler in C can't I? so in assembler how do you write to a memory location?

No, you can write C in C. The compiler turns it into assembler. There is a way to insert actual assembly code in the middle of C code, but that's not C anymore. That's actually assembly. If you want to actually write your program in assembly, you don't need C at all!

The exact details vary based on the instruction set your hardware uses. In general you load an address into a register, then you use a load or move instruction to do an indirect memory write based on that register of the appropriate size.

Quote
My other syntax question is reading from these memory locations, do I just put the variable wanting the value to the left of the = and the same address syntax to the right?

variable = (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET)) & mask;

Right! If the variable was declared as uint8_t, even better!
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #83 on: October 09, 2018, 09:39:37 am »
OK I meant the IDE's allow assembler to be used in a C program, I know it can be done I was just wondering if the code was simpler to write when dealing with memory.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [gcc C] concatenating text to make up code
« Reply #84 on: October 09, 2018, 10:09:18 am »
OK I meant the IDE's allow assembler to be used in a C program, I know it can be done I was just wondering if the code was simpler to write when dealing with memory.

Most C compilers allow you to include inline assembler, and it is a real pain. Avoid it at all costs :D

If you really have to, use only one or two instructions that don't need access to data. (e.g. Enabling or disabling interrupts)

Why?

First of, you have to be very familiar with the underlying instruction set and architecture of the target processor

Then you need to know the C compiler's expected assembler syntax, that might not match the vendor's native assembler (looking at you GCC for Intel).

Then you have to work out how you can pass values and addresses between C and assembler (e.g. so you can access C variables), which involves magic incantations.

Inline ASM prevents the optimizer from doing it's job, you have added a little block of magic in the middle of C code.

And you can't format your code nicely, and make it readable, because it is intermixed with C and needs to be quoted

And perhaps worst of all, adding labels so you can do jumps is just ugly. You need to force the C compiler to generate labels and use its naming schemes, rather than sensible meaningful names.

And finally your code isn't portable, so you will only ever use the result of all your blood, sweat and tears once.

See https://locklessinc.com/articles/gcc_asm/ if interested in a brief guide.

To see how bad it really is, here is the start of some code that I wrote to drive WS2812B LEDs. I wish I had just used the SPI peripheral...

Code: [Select]
void outputWS2812Bbytes(unsigned char (*leds)[3], unsigned char length)
{
delay(1);
  asm volatile(
  " cli \n\t" // Disable interrupts
  " mov 18,%1\n\t"  // Copy length
  " add %1, 18\n\t" // Add it back
  " add %1, 18\n\t" // Add it back, so it is now x3 what it was
  "L_next%=:" "\n\t"
  // Bit 7
  " SBI 5, 4 \n\t" // Set port b bit 4 Arduino pin 12
  " NOP \n\t" // A pause
  " LD 18, Z \n\t" // Load and post increment - two cycles
  " ANDI 18, 128 \n\t" // Test the bit - one cycle
  " BRNE L_bit7%= \n\t" // Skip the clear if the bit is set
  " CBI 5, 4 \n\t" // clear port b bit 4
  "L_bit7%=:" "\n\t"
  " NOP \n\t" // A pause
  " NOP \n\t" // A pause
  " NOP \n\t" // A pause
  " NOP \n\t" // A pause
  .....

It is usually better to do the hard yards and get it to work in C
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11622
  • Country: my
  • reassessing directives...
Re: [gcc C] concatenating text to make up code
« Reply #85 on: October 09, 2018, 10:27:34 am »
and i hope the code will not go into some life critical equipments without rigorous testing or peer review from experienced personnel.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: [gcc C] concatenating text to make up code
« Reply #86 on: October 09, 2018, 10:30:04 am »
so in assembler how do you write to a memory location?

You do it by RTFM'ing the exact CPU architecture you are working with. But there is no C pointer syntax involved! Just one clearly documented operation which directly does your memory access.
... hopefully. Not all architectures have simple addressing. C quickly becomes convenient when there is no simple load/store with immediate address operand over the desired address space

The great thing in the C way:
*(uint8_t*)0xacdc = 0xabba;
is that this is portable and the compiler generates the correct 8-bit memory access instruction for practically any CPU.

If you don't like the pointer syntax, you can create some macros, like
#define MEM8(addr) (*(uint8_t*)(addr))
#define MEM16(addr) (*(uint16_t*)(addr))

and then
MEM8(0xacdc) = 0xabba;

but I won't recommend creating such "helpers" on the language basics if you ever intend the code to be read by others. You should learn the basics, and expect that others have learned the basics, too; then you have a common language everyone understands. Create helper macros and add documentative comments when this significantly reduces the syntactic overhead, or when dealing with some difficult specifics with the language most "normal" people never encounter. This isn't such a case.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #87 on: October 09, 2018, 11:48:06 am »
and i hope the code will not go into some life critical equipments

No it won't

Quote
without rigorous testing or peer review from experienced personnel.

That is why I am asking here :). First application will be a fan speed controller, I am planning to use the new ATmega 0-series so all my code templates and basic functionality needs re-writing - with the benefit of hindsight.

assembler does indeed sound like a bad idea in a C project, won't be doing any of that.
 

Offline TK

  • Super Contributor
  • ***
  • Posts: 1722
  • Country: us
  • I am a Systems Analyst who plays with Electronics
Re: [gcc C] concatenating text to make up code
« Reply #88 on: October 09, 2018, 12:11:07 pm »
There is a nice tool that visually executes python, C, C++ code and it shows graphically variables and pointers.

http://pythontutor.com

Select C or C++ as your language, paste your code and execute it.

Try visually executing the following code, it shows variables, pointers, an array and then it assigns the address of the array to the pointer and loops by using the pointer and incrementing it to put a value into each array element.

Code: [Select]
int main() {
  int z[10];
  int x=1, y=2;
  int *pointer;
  pointer = &x;
  y = *pointer;
  z[0] = 3;
  pointer = z;
  for (int i=0; i<10; i++)
    *pointer++ = i;
  return 0;
}
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11622
  • Country: my
  • reassessing directives...
Re: [gcc C] concatenating text to make up code
« Reply #89 on: October 09, 2018, 06:21:11 pm »
That is why I am asking here :). First application will be a fan speed controller, I am planning to use the new ATmega 0-series
i think you asked the wrong question (asking how to practice bad?) thats why i hesitated to make an involvement. but since a simple question usually expanded into several pages so... your question is kind of novel to me afaik nobody needed 48 functions for a simple task of fan speed control, i will not worry much if its 48 cases which some/many coders did so, but i never heard people want to do 48 functions, you can device fancy naming macro workaround in C but in the end your assembled code will expand to 48 routines complete with memory/stack/return vector shuffling each time, which i think sub optimum.

so all my code templates and basic functionality needs re-writing - with the benefit of hindsight.
the sense of reusable codes needs practice (good practice), time and experience. you can get to it fast here in forum if you ask the right question, your kind of question will get you the other way around (worse and even farther from reusable), imho. and other thing is... copy paste might help, if you really have to ;)

assembler does indeed sound like a bad idea in a C project, won't be doing any of that.
you can if you can process everything in mind, with knowledge or sense on how a compiler generates compiled code (c -> assembly/machine code) esp on how compiler will automatically allocate/use register/memory location to do stuff like temporary variables or even the static one, and the implication that you will screw this auto mapping by compiler if you mix it with assembly code. but as other more experienced member here said, its not that easy, last time i looked at the example i gave up i'd rather do it in fully C or fully assembler, or maybe it can be done for a very tight and isolated assembly codes. for a moderate complexity program that full assembly is not feasible, i'll learn how to do a "tight code" not "fancy code" in C and hope compiler is optimum enough at generating optimum/tight/smallest assembly/machine code. one example is explicitly using "register" when declaring a variable thats the wonderfulness of C not available in any other modern and higher level language (well its not a troll :P).

sorry i cannot help you with your specific question since as i said its a novel question for me ;) so i'm not being helpful here.
« Last Edit: October 09, 2018, 06:32:48 pm by Mechatrommer »
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #90 on: October 09, 2018, 06:28:24 pm »
That is why I am asking here :). First application will be a fan speed controller, I am planning to use the new ATmega 0-series
i think you asked the wrong question (asking how to practice bad?) thats why i hesitated to make an involvement. but since a simple question usually expanded into several pages so... your question is kind of novel to me afaik nobody needed 48 functions for a simple task of fan speed control, i will not worry much if its 48 cases which some/many coders did so, but i never heard people want to do 48 functions, you can device fancy naming macro workaround in C but in the end your assembled code will expand to 48 routines complete with memory/stack/return vector shuffling each time, which i think sub optimum.


i am refering to the attached
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11622
  • Country: my
  • reassessing directives...
Re: [gcc C] concatenating text to make up code
« Reply #91 on: October 09, 2018, 06:38:56 pm »
i am refering to the attached
if you give specific problem you might get specific answer ;)
Code: [Select]
static inline void PA0_input(){ PORTA.DIRCLR = 0x01; }
static inline void PA1_input(){ PORTA.DIRCLR = 0x02; }
static inline void PA2_input(){ PORTA.DIRCLR = 0x04; }
static inline void PA3_input(){ PORTA.DIRCLR = 0x08; }
static inline void PA4_input(){ PORTA.DIRCLR = 0x10; }
static inline void PA5_input(){ PORTA.DIRCLR = 0x20; }
static inline void PA6_input(){ PORTA.DIRCLR = 0x40; }
one way at least i usually see it can be coded as...

Code: [Select]
#define PA0c 0x01
#define PA1c 0x02
...continue...
static inline void input(code){ PORTA.DIRCLR = code; }
that inline function will just expand to a single (reusable) line of code, which also you can define as...
Code: [Select]
#define input(code) PORTA.DIRCLR = code
ymmv...
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: [gcc C] concatenating text to make up code
« Reply #92 on: October 09, 2018, 06:48:42 pm »
volatile is a keyword that tells the compiler not to make any assumptions when optimizing this. It may not be strictly required, but it's a good idea for things like this.

Yes. Actually people thinking that this qualifier is useless are either always compiling with zero optimization or are sadly mistaken.
Any variable that the compiler sees as never modified may (and depending on the opt. level and compiler, WILL) be optimized and either pruned altogether or at least considered as a, and replaced by a constant anywhere it's used.

In embedded dev, a good example of "variables" that *can't* be seen as modified in the source code (at least when you don't write to them explicitly) are declared MCU registers. That's why you can see "volatile" in front of all MCU registers' declarations in most vendors' C headers. The compiler has no way of knowing their value can change.

Another example are variables that are only modified in interrupt routines. In this case, the variables may be explicitly modifed in the source code, but the compiler has no way of knowing the interrupt routines that modify them will actually ever be called. ISRs are never called explicitly. So it may assume they won't, and thus assume as a consequence that the variables in question are never modified. That's why it's also suggested to use volatile as a qualifier for global variables that are written in interrupt routines.
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11622
  • Country: my
  • reassessing directives...
Re: [gcc C] concatenating text to make up code
« Reply #93 on: October 09, 2018, 06:54:56 pm »
That's why it's also suggested to use volatile as a qualifier for global variables that are written in interrupt routines.
+1 i never have a need for "volatile" until i entered embedded interrupt/ISR world. learnt the hardway and took a lot of head scratching there while debugging. imho stating it as optimization purpose may not give its full picture.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3143
  • Country: ca
Re: [gcc C] concatenating text to make up code
« Reply #94 on: October 09, 2018, 08:25:05 pm »
Another example are variables that are only modified in interrupt routines. In this case, the variables may be explicitly modifed in the source code, but the compiler has no way of knowing the interrupt routines that modify them will actually ever be called. ISRs are never called explicitly. So it may assume they won't, and thus assume as a consequence that the variables in question are never modified. That's why it's also suggested to use volatile as a qualifier for global variables that are written in interrupt routines.

It is also possible to error in other direction. I've seen people use "volatile" in such situations and think that this solves all the problems. This is not always the case. If your variable is longer than the processor can load in a single cycle (say 16-bit variable on 8-bit processor), the interrupt may happen right in the middle of the loading. This screws up the loading of the variable regardless of whether it was declared volatile or not.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: [gcc C] concatenating text to make up code
« Reply #95 on: October 09, 2018, 08:27:09 pm »
Another example are variables that are only modified in interrupt routines. In this case, the variables may be explicitly modifed in the source code, but the compiler has no way of knowing the interrupt routines that modify them will actually ever be called. ISRs are never called explicitly. So it may assume they won't, and thus assume as a consequence that the variables in question are never modified. That's why it's also suggested to use volatile as a qualifier for global variables that are written in interrupt routines.

It is also possible to error in other direction. I've seen people use "volatile" in such situations and think that this solves all the problems. This is not always the case. If your variable is longer than the processor can load in a single cycle (say 16-bit variable on 8-bit processor), the interrupt may happen right in the middle of the loading. This screws up the loading of the variable regardless of whether it was declared volatile or not.

... and that is where you need your single line of inline assembler for "disable interrupts" & "enable interrupts" either side...   >:D
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14447
  • Country: fr
Re: [gcc C] concatenating text to make up code
« Reply #96 on: October 09, 2018, 08:35:25 pm »
It is also possible to error in other direction. I've seen people use "volatile" in such situations and think that this solves all the problems. This is not always the case. If your variable is longer than the processor can load in a single cycle (say 16-bit variable on 8-bit processor), the interrupt may happen right in the middle of the loading. This screws up the loading of the variable regardless of whether it was declared volatile or not.

Sure. That's required, but not sufficient.
You also need to care about the atomicity of operations indeed. Yet another possible pitfall. For this, you need to guard the read or write operation to make sure it can't get interrupted. Some processors have specific instructions for this. Most MCUs don't and you need to selectively disable and re-enable interrupts around the operation.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3143
  • Country: ca
Re: [gcc C] concatenating text to make up code
« Reply #97 on: October 09, 2018, 08:39:55 pm »
You also need to care about the atomicity of operations indeed. Yet another possible pitfall. For this, you need to guard the read or write operation to make sure it can't get interrupted. Some processors have specific instructions for this. Most MCUs don't and you need to selectively disable and re-enable interrupts around the operation.

I personally don't like disabling interrupts because it increases interrupt latency. Usually I can figure a way to avoid disabling interrupts, such as reading the variable twice and making sure it's the same both times.

 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [gcc C] concatenating text to make up code
« Reply #98 on: October 10, 2018, 06:28:10 am »
I take it that:

void pin_state(uint8_t port, uint8_t pin){
   return (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_INPUT_PIN_VALUE)) & 0x01 << pin; // return state of pin.
} // Use PORT_x and PIN_n as arguments.

would work? so I'd end up with something like:

if ( pin_state( PORT_B, PIN_5 )) {
       some code;
     }

And presumably !pin_state(PORT_B, PIN_5) to negate the result.
« Last Edit: October 10, 2018, 06:32:57 am by Simon »
 

Offline Nusa

  • Super Contributor
  • ***
  • Posts: 2416
  • Country: us
Re: [gcc C] concatenating text to make up code
« Reply #99 on: October 10, 2018, 06:46:49 am »
I take it that:

void pin_state(uint8_t port, uint8_t pin){
   return (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_INPUT_PIN_VALUE)) & 0x01 << pin; // return state of pin.
} // Use PORT_x and PIN_n as arguments.

would work? so I'd end up with something like:

if ( pin_state( PORT_B, PIN_5 )) {
       some code;
     }

And presumably !pin_state(PORT_B, PIN_5) to negate the result.

No, because a void function doesn't return anything. But otherwise, it looks workable.

I'd probably write it more like this, just for clarity.
bool pin_state(...) {
   if (condition){
      return true;
   }
   return false;
}
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf