I've been doing some AVR assembly programming for a university course, and I recently happened upon a situation where I would have wanted an "add immediate" instruction. However, no such instruction seems to exist within the Atmel AVR instruction set. I find this weird given that there are immediate variants of various other instructions, such as subtract, logical AND, compare, and even "add immediate to word" (ADIW), among others. What can I use in place of add immediate to perform immediate addition when doing AVR assembly programming?
-
AFAIK ADIW is only used for pointer-arithmeric. I guess there was no need to implement a generic Add-Intermediate for the whole register file. IMO this was a design decision based on complexity and size constraints without any architectural reasoning. Remember: AVR is old....really old (In terms of time and especially technology). But: This is my intuition - not my expertise. – ElectronicsStudent May 04 '23 at 06:47
-
The only people would could give you a definitive answer would be the designers of that chip. But note nearly all engineering decisions are a result of a set of design goals and more importantly, the compromises made to achieve them. Every instruction a CPU can execute requires some circuitry on the IC. I would guess (without any real proof) they decided the circuitry required to implement that instruction was too much to justify its inclusion, the space it would take up being more useful to other operations. – Kyle B May 04 '23 at 06:58
-
maybe the add immediate can be accomplished with two instructions and use the same number of cycles as an actual add immediate instruction would – jsotola May 04 '23 at 06:59
-
@jsotola This is incorrect for AVR, all 8 bit arithmetic is single cycle. An LDI+ADD would be 2 cycles. – Jeroen3 May 04 '23 at 07:19
-
1@jsotola nope. The lovely thing about classic AVR is that essentially all instructions (everything but multiplication and memory access) take one cycle. `ldi+add` takes two cycles (and clobbers another register). `subi` takes one. – hobbs May 04 '23 at 07:20
-
@ElectronicsStudent, AVR is from 1996, the same age as the Pentium MMX which I always figured was a somewhat sensible processor. Somewhat older than the freshmen by now, but still some way off from the 8086 (1978), the PIC family (1976) or the 6502 (1975). (The first two of which I'm not sure are totally sensible.) – ilkkachu May 04 '23 at 16:42
-
@ilkkachu Thank you for the comment. I guess 1996 is "thirty years" by now - almost a decade older than me :) And technology-wise: Compare a 50Cents ARM M3 these days with the AVR Core....Not even close in the most specs (Core-Feature Set, Code-Size/Complexity, MIPS/Watt and many more) – ElectronicsStudent May 04 '23 at 17:56
2 Answers
There is a subi
instruction - "subtract immediate". You can easily do addition using this instruction, so one can speculate there isnt a need for a dedicated add immediate instruction.
For example, to add 2:
subi r16, 254
Alternatively, you can also express it like this using a negative number:
subi r16, -2

- 193
- 6

- 63,168
- 3
- 139
- 196
-
Is this going to work as expected with the various flags? This might be setting the carry/overflow/underflow bit, as an example... – Vladimir Cravero May 04 '23 at 15:44
-
@VladimirCravero theoretically it shouldn't have any ill-effects. The `subi` instruction sets the flags based on two's complement overflow. Here we are subtracting minus 2 (in twos comp) so the overflow flags should give the correct result as if we just added 2. – Tom Carpenter May 04 '23 at 15:52
-
@TomCarpenter: For non-zero operand values, that would be true, but if AVR is typical of 8-bit machines with two's-complement carry/non-borrow flag, subtracting 0 would set the carry flag, while adding 0 would clear it. – supercat May 04 '23 at 16:47
-
5I don't have an AVR IDE or assembler laying around, so I'll ask the lazy question: Would it be possible to code this as `subi r16, -2` as a hint to future maintainers what's being attempted here? – spuck May 04 '23 at 18:38
-
-
1@spuck I tested this in Atmel Studio 7.0, and yes, it does work as you would expect. – Newbyte May 14 '23 at 08:36
If the assembler isn't braindead, it should be synthesizing addi
using subi
. Some assemblers are lacking in that respect so YMMV. I'm sure Microchip's doesn't synthesize addi even though it'd be trivial to do so.
Anyway, all you need is a macro:
.MACRO ADDI
SUBI @0, low(256-@1)
.ENDMACRO
; Usage
LDI R0, 1
ADDI R0, $AB
; now R0 holds $AC
Yes, I am making a strong judgement about an assembler that supports subi
but doesn't synthesize addi
. What's up with that? Synthesis of more descriptive opcodes is common in various architectures.
Imagine that on RISCV you'd have no nop
because "it doesn't exist", and had to type addi x0, x0, 0
every time you meant nop
? And yes, RISCV does not "waste" an opcode on a nop. By convention, nop is encoded as addi x0, x0, 0
and that's that.
The situation on AVR isn't much different. If you mean addi
, the assembler should just do it, since the hardware supports it directly and in a single instruction...
Also, AVR wastes a perfectly good opcode (nop
) where it could have been encoded as, say mov r17, r17
= 0010'1111'0001'0001b
. Takes same time as a nop
, has same side effects (none)...

- 32,734
- 1
- 38
- 103