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
Letter Constraint Range
a Simple upper registers r16 to r23
b Base pointer registers pairs r28 to r32 (Y, Z)
d Upper register r16 to r31
e Pointer register pairs r26 to r31 (X, Y, Z)
l Lower registers r0 to r15
q Stack pointer register SPH:SPL
r Any register r0 to r31
t Temporary register r0
w Special upper register pairs usable in adiw instruction r24, r26, r28, r30
x Pointer register pair X r27:r26 (X)
y Pointer register pair Y r29:r28 (Y)
z Pointer register pair Z r31:r30 (Z)
G Floating point constant 0.0
I 6-bit positive integer constant 0 to 63
J 6-bit negative integer constant -63 to 0
K Integer constant 2
L Integer constant 0
M 8-bit integer constant 0 to 255
N Integer constant -1
O Integer constant 8, 16, 24
P Integer constant 1
Q Memory address based on Y or Z pointer with displacement
Cm2 Integer constant -2
C0n Integer constant, where n ranges from 0 to 7 n
Can n-byte integer constant that allows AND without clobber register, where n ranges from 2 to 4
Con n-byte integer constant that allows OR without clobber register, where n ranges from 2 to 4
Cxn n-byte integer constant that allows XOR without clobber register, where n ranges from 2 to 4
Csp Integer constant -6 to 6
Cxf 4-byte integer constant with at least one 0xF nibble
C0f 4-byte integer constant with no 0xF nibbles
Ynn Fixed-point constant known at compile time
Y0n Fixed-point or integer constant, where n ranges from 0 to 2 n
Ymn Fixed-point or integer constant, where n ranges from 1 to 2 -n
YIJ Fixed-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
Mnemonic Constraints Mnemonic Constraints
adc r,r add r,r
adiw w,I and r,r
andi d,M asr r
bclr I bld r,I
brbc I,label brbs I,label
bset r,I bst r,I
cbi I,I cbr d,I
com r cp r,r
cpc r,r cpi d,M
cpse r,r dec r
elpm t,z eor r,r
in r,I inc r
ld r,e ldd r,b
ldi d,M lds r,label
lpm t,z lsl r
lsr r mov r,r
movw r,r mul r,r
neg r or r,r
ori d,M out I,r
pop r push r
rol r ror r
sbc r,r sbci d,M
sbi I,I sbic I,I
sbiw w,I sbr d,M
sbrc r,I sbrs r,I
ser d st e,r
std b,r sts label,r
sub r,r subi d,M
swap r

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
Letter Constraint
= 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