November 2, 2019

A quick look at the AVR architecture

I had the final build of the blink example laying around. So I ran the following command:
avr-objdump -d main.elf > main.dump
This yielded a far more readable result than using the -S switch to the compiler and examining the resulting main.s file.
00000000 <__ctors_end>:
   0:   09 c0           rjmp    .+18            ; 0x14 <__init>
   2:   0e c0           rjmp    .+28            ; 0x20 <__bad_interrupt>
   4:   0d c0           rjmp    .+26            ; 0x20 <__bad_interrupt>
   6:   0c c0           rjmp    .+24            ; 0x20 <__bad_interrupt>
   8:   0b c0           rjmp    .+22            ; 0x20 <__bad_interrupt>
   a:   0a c0           rjmp    .+20            ; 0x20 <__bad_interrupt>
   c:   09 c0           rjmp    .+18            ; 0x20 <__bad_interrupt>
   e:   08 c0           rjmp    .+16            ; 0x20 <__bad_interrupt>
  10:   07 c0           rjmp    .+14            ; 0x20 <__bad_interrupt>
  12:   06 c0           rjmp    .+12            ; 0x20 <__bad_interrupt>

00000014 <__init>:
  14:   11 24           eor     r1, r1
  16:   1f be           out     0x3f, r1        ; 63
  18:   cf e9           ldi     r28, 0x9F       ; 159
  1a:   cd bf           out     0x3d, r28       ; 61
  1c:   02 d0           rcall   .+4             ; 0x22 
1e: 17 c0 rjmp .+46 ; 0x4e <_exit> 00000020 <__bad_interrupt>: 20: ef cf rjmp .-34 ; 0x0 <__ctors_end> 00000022
: 22: 81 e0 ldi r24, 0x01 ; 1 24: 87 bb out 0x17, r24 ; 23 26: 91 e0 ldi r25, 0x01 ; 1 28: 80 e0 ldi r24, 0x00 ; 0 2a: 98 bb out 0x18, r25 ; 24 2c: 2f e3 ldi r18, 0x3F ; 63 2e: 35 ef ldi r19, 0xF5 ; 245 30: 46 e0 ldi r20, 0x06 ; 6 32: 21 50 subi r18, 0x01 ; 1 34: 30 40 sbci r19, 0x00 ; 0 36: 40 40 sbci r20, 0x00 ; 0 38: e1 f7 brne .-8 ; 0x32 3a: 00 c0 rjmp .+0 ; 0x3c 3c: 00 00 nop 3e: 88 bb out 0x18, r24 ; 24 40: ef e2 ldi r30, 0x2F ; 47 42: f5 e7 ldi r31, 0x75 ; 117 44: 31 97 sbiw r30, 0x01 ; 1 46: f1 f7 brne .-4 ; 0x44 <__SREG__+0x5> 48: 00 c0 rjmp .+0 ; 0x4a <__SREG__+0xb> 4a: 00 00 nop 4c: ee cf rjmp .-36 ; 0x2a 0000004e <_exit>: 4e: f8 94 cli 00000050 <__stop_program>: 50: ff cf rjmp .-2 ; 0x50 <__stop_program>
Even without knowing anything about AVR assembly this yields a pretty readable listing.

Note that there is a convention of R1 being used as a "zero register". This is entirely a convention, but note that the first thing the "__init" routine does is to clear it (and then use it). Code generated by avr_gcc indeed depends on this and if interrupts are used and r1 is not maintained there can be trouble. The fact that r1 is a zero register could be used to better advantage in this code, but there are limits to what gcc can do.


Have any comments? Questions? Drop me a line!

Tom's Electronics pages / tom@mmto.org