inline assembly output

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In an attempt to get a grip on inline asm. I converted an .S file to a stub. Everything worked just fine, but one thing that isn't really clear still is when the output operands are used?

Here is the stub:

unsigned int 
      CRC(unsigned int crc, unsigned char ch)
{

 register unsigned char counter asm("r18");
 register unsigned char lopoly  asm("r19");
 register unsigned char hipoly  asm("r20");

 counter = 8;
 lopoly  = (unsigned char)(CRC_POLYNOME & 0xff);
 hipoly  = (unsigned char)
           ((unsigned int)CRC_POLYNOME>>8);


	asm ("CRC_Loop:"::);
	// Rotate left. 
        // If MSb is 1 -> divide with 
        // Generator-polynome.
	asm volatile ("lsl %0" :: "r" (ch) );
	asm volatile ("rol %0" :: "r" 
                     ((unsigned char)(crc & 0xff)) );  
	asm volatile ("rol %0" :: "r" 
                     ((unsigned char)(crc >> 8)) );  
	asm volatile ("brcc	CRC_SkipEor"::);

	asm volatile ("eor %0, %1" :: "r" 
        ((unsigned char)(crc & 0xff)) ,"r" (lopoly) );
        asm volatile ("eor %0, %1" :: "r" 
        ((unsigned char)(crc>>8)) ,"r" (hipoly) );

	asm("CRC_SkipEor:"::);
	asm volatile ("dec %0" :: "r" (counter) );
	asm volatile ("brne	CRC_Loop" ::);

	return crc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CirMicro wrote:
one thing that isn't really clear still is when the output operands are used?
Try the code below. The output operands should list all of the variables that you expect to be modified by the inline assembly code and which you want to be used subsequently in the C code. In contrast, the input operands are variables whose values are set by the preceding C code and expected to be used in the inline assembly code.

In this particular case, the crc variable is both an input and an output.

unsigned int
CRC(unsigned int crc, unsigned char ch)
{
  unsigned char counter = 8;
  unsigned char loPoly = (unsigned char)(CRC_POLYNOME & 0xff);
  unsigned char hiPoly = (unsigned char)((unsigned int)CRC_POLYNOME>>8);

  __asm__ __volatile__
  (
      "CRC_Loop:"            "\n\t"
      "  lsl %1"             "\n\t"
      "  rol %A0"            "\n\t"
      "  rol %B0"            "\n\t"
      "  brcc   CRC_SkipEor" "\n\t"
      "  eor %A0, %3"        "\n\t"
      "  eor %B0, %4"        "\n\t"
      "CRC_SkipEor:"         "\n\t"
      "  dec %2"             "\n\t"
      "  brne   CRC_Loop"    "\n\t"

      : "=&r" (crc)
      : "r" (ch), "r" (counter), "r" (loPoly), "r" (hiPoly), "0" (crc)
  );
  return(crc);
} 

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a slightly simplified form. The only change is the CRC polynomial isn't split into two bytes in the C code. It's just as simple to refer to the low and high bytes of a 16-bit variable using the An, Bn notation.

unsigned int
CRC(unsigned int crc, unsigned char ch)
{
  unsigned char counter = 8;
  unsigned crcPoly = CRC_POLYNOME;

  __asm__ __volatile__
  (
      "CRC_Loop:"            "\n\t"
      "  lsl %1"             "\n\t"
      "  rol %A0"            "\n\t"
      "  rol %B0"            "\n\t"
      "  brcc   CRC_SkipEor" "\n\t"
      "  eor %A0, %A3"       "\n\t"
      "  eor %B0, %B3"       "\n\t"
      "CRC_SkipEor:"         "\n\t"
      "  dec %2"             "\n\t"
      "  brne   CRC_Loop"    "\n\t"

      : "=&r" (crc)
      : "r" (ch), "r" (counter), "r" (crcPoly), "0" (crc)
  );
  return(crc);
} 

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Without being too cynical, you have a function that takes 3 registers, and returns a result in 2 registers.

Personally I would prefer to write an ASM function in English (regular ASM).

The contortions required to write inline avr-gcc assembly are beyond my will to live.

Good luck anyway. If you can make the system more amenable to mere mortals then many people would be grateful.

There is something attractive about regular neatly laid out ASM statements. Does anyone have an attraction to the syntax of "inline gcc" ?

Yes, I can see the purpose for the occasional inline snippet, but functions NO.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David wrote:
There is something attractive about regular neatly laid out ASM statements. Does anyone have an attraction to the syntax of "inline gcc" ?

Not even a little bit! :)

David wrote:
Yes, I can see the purpose for the occasional inline snippet, but functions NO.

I agree. This was just a nice small function to work with for learning purposes.

Mike wrote:
In an attempt to get a grip on inline asm. I converted an .S file to a stub.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the help Don. That format definitely looks much better. One question though. The version I posted works directly on crc 25:24 which was desired but I noticed the code you posted moves it to an intermediate and then the result back to 25:24. is there away to tell it to work directly on crc?

Thanks,
Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CirMicro wrote:
is there away to tell it to work directly on crc?
When compiled with WinAVR 20071227 using -0s it does just that (see below). What you're seeing may be a result of a different optimization level or a later compiler version.

000003b6 :
  3b6: 48 e0  ldi    r20, 0x08
  3b8: 21 e0  ldi    r18, 0x01
  3ba: 30 e8  ldi    r19, 0x80

000003bc :
  3bc: 66 0f  add    r22, r22
  3be: 88 1f  adc    r24, r24
  3c0: 99 1f  adc    r25, r25
  3c2: 10 f4  brcc    .+4
  3c4: 82 27  eor    r24, r18
  3c6: 93 27  eor    r25, r19

000003c8 :
  3c8: 4a 95  dec    r20
  3ca: c1 f7  brne    .-16
  3cc: 08 95  ret

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Must be the version then. I'm using 20080610 -Os

0003f7d6 :
   3f7d6:	9c 01 movw	r18, r24
   3f7d8:	85 e0 ldi	r24, 0x05 ; 5
   3f7da:	90 e8 ldi	r25, 0x80 ; 128
   3f7dc:	48 e0 ldi	r20, 0x08 ; 8

0003f7de :
   3f7de:	66 0f add	r22, r22
   3f7e0:	22 1f adc	r18, r18
   3f7e2:	33 1f adc	r19, r19
   3f7e4:	10 f4 brcc	.+4      	
; 0x3f7ea 
   3f7e6:	28 27 eor	r18, r24
   3f7e8:	39 27 eor	r19, r25

0003f7ea :
   3f7ea:	4a 95 dec	r20
   3f7ec:	c1 f7 brne	.-16     	
; 0x3f7de 

   3f7ee:	c9 01  movw r24, r18
   3f7f0:	08 95  ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This seems to work okay. I don't know if its considered proper or not?

unsigned int
CRC(unsigned int crc, unsigned char ch)
{
  unsigned char counter = 8;
  unsigned int crcPoly = CRC_POLYNOME;

  __asm__ __volatile__
  (
      "CRC_Loop:"            "\n\t"
      "  lsl r22"             "\n\t"
      "  rol r24"            "\n\t"
      "  rol r25"            "\n\t"
      "  brcc   CRC_SkipEor" "\n\t"
      "  eor r24, %A1"       "\n\t"
      "  eor r25, %B1"       "\n\t"
      "CRC_SkipEor:"         "\n\t"
      "  dec %0"             "\n\t"
      "  brne   CRC_Loop"    "\n\t"

      : 
      :  "r" (counter), "r" (crcPoly)
  );
  return(crc);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CirMicro wrote:
I don't know if its considered proper or not?
I would say definitely not. The problem is that you're using registers that you're not telling the compiler about. For example, the compiler could rightfully assign r22 to the counter variable since it has no evidence that r22 is being used anywhere.

Even though it works with the current version of the compiler, there is no guarantee that it will in the future.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Even though it works with the current version of the compiler, there is no guarantee that it will in the future.

I was under the impression that the registers and order that arguments are passed are as good as set in stone?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, this is a little frustrating when you can't even drop to asm without the compiler wanting to put its 2 cents in :)

It seems if I put crc as an output operand and/or define a variable without specifying a register then it wants to offload crc(r25:r24) to a temp register. Almost seems a little buggy to me.

unsigned int 
CRC(unsigned int crc, unsigned char ch)
{
  register unsigned char counter asm("r18"); 
  register unsigned int crcPoly asm("r20");
  counter  = 8;
  crcPoly  = CRC_POLYNOME;

  __asm__ __volatile__
  (
      "CRC_Loop:"            "\n\t"
      "  lsl %3"            "\n\t"
      "  rol %A0"            "\n\t"
      "  rol %B0"            "\n\t"
      "  brcc   CRC_SkipEor" "\n\t"
      "  eor %A0, %A2"       "\n\t"
      "  eor %B0, %B2"       "\n\t"
      "CRC_SkipEor:"         "\n\t"
      "  dec %1"             "\n\t"
      "  brne   CRC_Loop"    "\n\t"
      :  
      :  "r" (crc), "r" (counter), "r" (crcPoly), "r" (ch)
  );
  return crc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CirMicro wrote:
I was under the impression that the registers and order that arguments are passed are as good as set in stone?
It is. What I intended to say was that since you gave no indication that the 'ch' parameter was being used, the compiler could decide to use r22 for the 'counter' variable.

CirMicro wrote:
Almost seems a little buggy to me.
I wouldn't call it a bug since the code generated is perfectly functional and standards compliant. It is probably better described as an optimization regression. There have been quite a few posts describing similar regressions in the 20080610 release - the main reason that I have chosen to stay with 20071221 for now.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After I re-read your other reply I made more sense out of your other reply which why I did some more research and came up with the last version posted. I realize the code emitted is not really buggy but as you indicated more like the optimizer going on vacation :) Thank you for your time and help.

Mike