4.11.2.1 Input and Output Operands

Following the template is a comma-separated list of zero or more output operands, which indicate the names of C objects modified by the assembly code and input operands, which make values from C variables and expressions available to the assembly code.

Each operands has several components, described by:

[ [asmSymbolicName] ] constraint (Cexpression)

where asmSymbolicName is an optional symbolic name for the operand, constraint is string specifying constraints on the placement of the operand, and Cexpression is the C variable or expression to be used by the operand and which is enclosed in parentheses.

The first (left-most) output operand is numbered 0, any subsequent output operands are numbered one higher than the operand before it, with input operands being numbered in the same way.

The supported constraint letters are tabulated below (see table later in this section for operand modifiers).

Table 4-10. Input and Output Operand Constraints
LetterConstraintRange
aSimple upper registersr16 to r23
bBase pointer registers pairsr28 to r32 (Y, Z)
dUpper registerr16 to r31
ePointer register pairsr26 to r31 (X, Y, Z)
lLower registersr0 to r15
qStack pointer registerSPH:SPL
rAny registerr0 to r31
tTemporary registerr0
wSpecial upper register pairs usable in adiw instructionr24, r26, r28, r30
xPointer register pair Xr27:r26 (X)
yPointer register pair Yr29:r28 (Y)
zPointer register pair Zr31:r30 (Z)
GFloating point constant0.0
I6-bit positive integer constant0 to 63
J6-bit negative integer constant-63 to 0
KInteger constant2
LInteger constant0
M8-bit integer constant0 to 255
NInteger constant-1
OInteger constant8, 16, 24
PInteger constant1
QMemory address based on Y or Z pointer with displacement
Cm2Integer constant-2
C0nInteger constant, where n ranges from 0 to 7n
Cann-byte integer constant that allows AND without clobber register, where n ranges from 2 to 4
Conn-byte integer constant that allows OR without clobber register, where n ranges from 2 to 4
Cxnn-byte integer constant that allows XOR without clobber register, where n ranges from 2 to 4
CspInteger constant-6 to 6
Cxf4-byte integer constant with at least one 0xF nibble
C0f4-byte integer constant with no 0xF nibbles
YnnFixed-point constant known at compile time
Y0nFixed-point or integer constant, where n ranges from 0 to 2n
YmnFixed-point or integer constant, where n ranges from 1 to 2-n
YIJFixed-point or integer constant-0x3F to 0x3F

The constraint you choose should match the registers or constants that are appropriate for the AVR instruction operand. The compiler will check the constraint against your C expression; however, if the wrong constraint is used, there is the possibility of code failing at runtime. For example, if you specify the constraint r with an ori instruction, then the compiler is free to select any register (r0 thru r31) for that operand. This will fail, if the compiler chooses a register in the range r2 to r15. The correct constraint in this case is d. On the other hand, if you use the constraint M, the compiler will make sure that you only use an 8-bit immediate value operand.

The table below shows all the AVR assembler mnemonics that require operands and the relevant constraints for each of those operands.

Table 4-11. Instructions and Operand Constraints
MnemonicConstraintsMnemonicConstraints
adcr,raddr,r
adiww,Iandr,r
andid,Masrr
bclrIbldr,I
brbcI,labelbrbsI,label
bsetr,Ibstr,I
cbiI,Icbrd,I
comrcpr,r
cpcr,rcpid,M
cpser,rdecr
elpmt,zeorr,r
inr,Iincr
ldr,elddr,b
ldid,Mldsr,label
lpmt,zlslr
lsrrmovr,r
movwr,rmulr,r
negrorr,r
orid,MoutI,r
poprpushr
rolrrorr
sbcr,rsbcid,M
sbiI,IsbicI,I
sbiww,Isbrd,M
sbrcr,Isbrsr,I
serdste,r
stdb,rstslabel,r
subr,rsubid,M
swapr

Constraint characters may be prepended by a single constraint modifier. Constraints without a modifier specify read-only operands. The constraint modifiers are tabulated below.

Table 4-12. Input and Output Constraint Modifiers
LetterConstraint
=Write-only operand, usually used for all output operands.
+Read-write operand
&Register should be used for output only

So, in the example:

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

the assembler instruction is defined by the template, "in %0, %1". The %0 token refers to the first output operand, "=r" (value), and %1 refers to the first input operand, "I" (_SFR_IO_ADDR(PORTD)). No clobbered registers were indicated in this example.

The compiler might encode the above in-line assembly as follows:

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 enclosed instruction was hand-written. In this example, the compiler selected register r24 for storage of the value read from PORTD; however, it might not explicitly load or store the value, nor include your assembler code at all, based on the compiler's optimization strategy. For example, if you never use the variable value in the remaining part of the C program, the compiler could remove your in-line assembly code unless you switch off the optimizers. To avoid this, you can add the volatile attribute to the asm() statement, as shown below:

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

Operands can be given names, if desired. 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 clobber list is primarily used to tell the compiler about modifications done by the assembler code. This section of the statement can be omitted, but all other sections are required. Use the delimiting colons, but leave the operand field empty if there is no input or output used, for example:

asm volatile(“cli”::);

Output operands must be write-only and the C expression result must be an lvalue, i.e., be valid on the left side of an assignment. Note, that the compiler will not check if the operands are of a reasonable type for the kind of operation used in the assembler instructions. Input operands are read-only.

In cases where you need the same operand for input and output, read-write operands are not supported, but it is possible to indicate which operand’s register to use as the input register by a single digit in the constraint string. Here is an example:

asm volatile("swap %0" : "=r" (value) : "0" (value));

This statement will swap the nibbles of an 8-bit variable named value. Constraint "0" tells the compiler, to use the same input register used by the first operand. Note, however, that this doesn't automatically imply the reverse case.

The compiler may choose the same registers for input and output, even if not told to do so. This can be an issue if the output operand is modified by the assembler code before the input operand is used. In the situation where your code depends on different registers used for input and output operands, you must use the constraint modifier, &, with the output operand, as shown in the following example.

asm volatile("in %0,%1" "\n\t"
 "out %1,%2" "\n\t"
 : "=&r" (result)
 : "I" (_SFR_IO_ADDR(port)), "r" (source)
 );

Here, a value is read from a port and then a value is written to the same port. If the compiler chooses the same register for input and output, then the output value will be clobbered by the first assembler instruction; however, the use of the & constraint modifier prevents the compiler from selecting any register for the output value that is also used for any of the input operands.

Here is another example that swaps the high and low byte of a 16-bit value:

asm volatile("mov __tmp_reg__, %A0" "\n\t"
 "mov %A0, %B0" "\n\t"
 "mov %B0, __tmp_reg__" "\n\t"
 : "=r" (value)
 : "0" (value)
 );

Notice the usage of register __tmp_reg__, which you can use without having to save its content. The letters A and B, used in the tokens representing the instruction operands refer to byte components of a multi-byte register, A referring to the least significant byte, B the next most significant byte, etc.

The following example, which swaps bytes of a 32-bit value, uses the C and D components of a 4 byte quantity, and rather than list the same operand as both input and output operand (via "0" as the input operand constraint), it can also be declared as a read-write operand by using "+r" as the output constraint.

asm volatile("mov __tmp_reg__, %A0" "\n\t"
 "mov %A0, %D0" "\n\t"
 "mov %D0, __tmp_reg__" "\n\t"
 "mov __tmp_reg__, %B0" "\n\t"
 "mov %B0, %C0" "\n\t"
 "mov %C0, __tmp_reg__" "\n\t"
 : "+r" (value)
 );

If operands do not fit into a single register, the compiler will automatically assign enough registers to hold the entire operand. This also implies, that it is often necessary to cast the type of an input operand to the desired size.

If an input operand constraint indicates a pointer register pair, such as "e" (ptr), and the compiler selects register Z (r30:r31), then you must use %a0 (lower case a) to refer to the Z register, when used in a context like:

ld r24,Z