Let's start with a simple example of reading a value from port D:

asm("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)) );

Each asm statement is devided by colons into (up to) four parts:

You can write assembler instructions in much the same way as you would write assembler programs. However, registers and constants are used in a different way if they refer to expressions of your C program. The connection between registers and C operands is specified in the second and third part of the asm instruction, the list of input and output operands, respectively. The general form is

asm(code : output operand list : input operand list [: clobber list]);

In the code section, operands are referenced by a percent sign followed by a single digit. 0 refers to the first 1 to the second operand and so forth. From the above example:

0 refers to "=r" (value) and 1 refers to "I" (_SFR_IO_ADDR(PORTD)).

This may still look a little odd now, but the syntax of an operand list will be explained soon. Let us first examine the part of a compiler listing which may have been generated from our example:

lds r24,value
/* #APP */
in r24, 12
/* #NOAPP */
sts value,r24

The comments have been added by the compiler to inform the assembler that the included code was not generated by the compilation of C statements, but by inline assembler statements. The compiler selected register r24 for storage of the value read from PORTD. The compiler could have selected any other register, though. It may not explicitely load or store the value and it may even decide not to include your assembler code at all. All these decisions are part of the compiler's optimization strategy. For example, if you never use the variable value in the remaining part of the C program, the compiler will most likely remove your code unless you switched off optimization. To avoid this, you can add the volatile attribute to the asm statement:

asm volatile("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)));

Alternatively, operands can be given names. The name is prepended in brackets to the constraints in the operand list, and references to the named operand use the bracketed name instead of a number after the % sign. Thus, the above example could also be written as

asm("in %[retval], %[port]" :
    [retval] "=r" (value) :
    [port] "I" (_SFR_IO_ADDR(PORTD)) );

The last part of the asm instruction, the clobber list, is mainly used to tell the compiler about modifications done by the assembler code. This part may be omitted, all other parts are required, but may be left empty. If your assembler routine won't use any input or output operand, two colons must still follow the assembler code string. A good example is a simple statement to disable interrupts:

asm volatile("cli"::);