Increment, Decrement, Add and Subtract
INC is a mnemonic for INCrement. INC adds one to the contents of the register placing the result in the register. The C flag in the Status Register (SREG) is not affected by the operation, thus allowing the INC instruction to be used on a loop counter in multiple-precision computations.
volatile char a = 0; asm ( "inc %0 \n" : "+r" (a) );
Remember, you might be tempted to write something similar to the following. While it is not wrong, it’s just inefficient:
asm ( "mov __tmp_reg__, %0 \n" "inc __tmp_reg__ \n" "mov %0, __tmp_reg__ \n" : "+r" (a) );
DEC is a mnemonic for DECrement. DEC subtracts one from the contents of the register placing the result in the register. The C flag in SREG is not affected by the operation, thus allowing the DEC instruction to be used on a loop counter in multiple-precision computations.
volatile char a = 2; asm ( "dec %0 \n" : "+r" (a) );
SUBI is a mnemonic for SUBtract Immediate, which subtracts a register and a constant and places the result in the register. This instruction works with registers R16 to R31.
volatile char x = 2; asm ( "subi %0, %1 \n" : "+r" (x) : "K" (2) );
The ADD instruction is really the mnemonic for “ADD without carry”. However, without getting ahead of ourselves, try to ignore the reference to “carry” for now. ADD, simply adds two registers without the C flag and places the result in the destination register.
volatile char x = 10, y = 20, z = 0; asm ( "add %1, %2 \n" : "=r" (z) : "r" (x), "r" (y) );
Where is ADDI?
Guess what? There’s no Add Immediate instruction. Not to fret, “add immediate” is the same as a SUBI operation using the “two’s complement” of a number.
First, a “One’s Complement” is to negate a number. Negate by flipping the bits (make 0 bits 1, and 1 bits 0). Thus, 12 which is 00001100 in binary, becomes 11110011 (-12 in binary). As in a signed magnitude, the leftmost bit indicates the sign (1 is negative, 0 is positive).
For a “Two’s Complement”, begin with the number’s “One’s Complement” and add 1 if the number is negative. Thus, continuing the previous example, twelve represented as 00001100 (binary), negated becomes 11110011 (-12 in binary), and subtracting one, becomes 11110100 (binary).
volatile char x = 2; asm ( "subi %0, %1 \n" //subi twos-complement = add immediate : "+r" (x) : "i" (-2) //(-2)=0xfe, and (2 – (0xfe))=4 );
The carry flag is a single bit in the Status Register (SREG). It indicates when an arithmetic carry or borrow has been generated out of the most significant bit position. The carry flag is affected by most arithmetic instructions. Several arithmetic instructions have two forms, which either read or ignore the carry. The carry flag enables multi-byte add, subtract, shift, and rotate operations.
For example, if one were to add 255 and 1 using 8-bit registers, the result should be 256, or 100000000 in binary. You will notice that number requires 9 bits. The 8 least significant bits, or the result stored into a byte-sized register would be 00000000 binary (0 in decimal). There’s a need to carry a one out of bit 7 (the eighth bit). When this occurs, the SREG carry bit is set, indicating that the result needs to spill over into the next byte. The valid 9-bit result is the concatenation of the carry flag with the result.
Note, that in an 8-bit two’s complement interpretation, this operation is (−1) + (−1) and yields the correct result of −2, with no overflow, even if the carry is ignored.
Carry or Borrow?
While the carry flag works as described for addition, for subtractive operations, one uses the bit as a borrow flag, setting it if a < b when computing a − b, and a borrow must be performed. A subtract with carry (SBC) instruction will compute a − b − C = a − (b + C), while subtract without carry (SUB) acts as if the borrow bit were clear.
You may want to re-read that section.
ADC is a mnemonic for ADd with Carry, which adds two registers and the contents of the C flag and places the result in the destination register.
volatile int x = 0x00ff, y = 0x0001, z = 0; asm ( "add %A1, %A2 \n" //Add low byte "adc %B1, %B2 \n" //Add with carry high byte : "=r" (z) : "r" (x), "r" (y) );
Integer Add with Carry the Easy Way
ADIW is the mnemonic for ADd Immediate to Word. The ADIW instruction adds an immediate value between 0 and 63 to a register pair and places the result in the register pair. Why only values between 0, and 63? I don’t know. However, notice that there is a constraint explicitly designated for this range of numbers, ‘I’, and it would be wise to use it here.
volatile int x = 0x00ff, y = 0; asm ( "adiw %1, %2 \n" : "=r" (y) : "r" (x), "P" (1) );
SBC is a mnemonic for SuBtract with Carry, which subtracts two registers and subtracts with the C flag and places the result in the destination register.
volatile int x = 0x0100, y = 0x0001, z = 0; asm ( "sub %A1, %A2 \n" //Subtract low byte "sbc %B1, %B2 \n" //Subtract with carry high byte : "=r" (z) : "r" (x), "r" (y) );
However, as with addition, there is an easier method also within limitations. SBIW is the mnemonic for SuBtract Immediate from Word. SBIW subtracts an immediate value between 0 and 63 from a register pair and places the result in the register pair. Notice the use of ‘I’ constraint again.
volatile int x = 0x0100; asm ( "sbiw %0, %1 \n" : "+r" (x) : "I" (1) );
Our next topic will cover “branch” operations, and will wrap up the basics of assembly language programming. This will enable us to start writing actual short snippets of code. Topics for the future will include functions and interrupts.
Also available as a book, with greatly expanded coverage!