Arduino Disassembly: Who Put This Stuff Here?

too much stuff

Ever wonder what all of the code inserted at the beginning of your program that appears in a disassembly listing is about? Well it’s a bunch of required housekeeping routines that are linked into your program at compile time that come from several locations. Some of the files are Arduino specific while others are AVR GCC and libc code. Here is a breakdown of the basic “Blink” program running on a generic ATMega328.

First we see the interrupt vector table (IVT), which is a table of interrupt vectors that associates an interrupt handler with an interrupt request. More information can be found here and here.

	.macro	vector name
	.if (. - __vectors < _VECTORS_SIZE)
	.weak	\name
	.set	\name, __bad_interrupt
	XJMP	\name

	XJMP	__ctors_end
	vector	__vector_2
	vector	__vector_3
	vector	__vector_125
	vector	__vector_126
	vector	__vector_127

/* Handle unexpected interrupts (enabled and no handler), which
   usually indicate a bug.  Jump to the __vector_default function
   if defined by the user, otherwise jump to the reset address.

   This must be in a different section, otherwise the assembler
   will resolve "rjmp" offsets and there will be no relocs.

	.weak	__vector_default
	.set	__vector_default, __vectors
	XJMP	__vector_default


The blink disassembly will look something like this:

   0:	0c 94 61 00 	jmp	0xc2	; 0xc2 <__ctors_end>
   4:	0c 94 7e 00 	jmp	0xfc	; 0xfc <__bad_interrupt>
   8:	0c 94 7e 00 	jmp	0xfc	; 0xfc <__bad_interrupt>
  3c:	0c 94 7e 00 	jmp	0xfc	; 0xfc <__bad_interrupt>
  40:	0c 94 9d 00 	jmp	0x13a	; 0x13a <__vector_16>

Note the first vector, or reset vector is set to point to the code that begins at the label “__ctors_end”. With the exception of vector #16, the remaining vectors all point to the “bad interrupt code”. See this post for more information about bad interrupt handling. Vector #16 is the Timer0 overflow vector and is used by the Arduino millis timer. See here for more information on how millis works.

Next comes any program memory. For example, Arduino’s digitalWrite/Read functions use program memory for pin-mapping. Information on PROGMEM can be found here and here.

00000068 <port_to_mode_PGM>:
  68:	00 00 00 00 24 00 27 00 2a 00                       ....$.'.*.

00000072 <port_to_output_PGM>:
  72:	00 00 00 00 25 00 28 00 2b 00                       ....%.(.+.

0000007c <port_to_input_PGM>:
  7c:	00 00 00 00 23 00 26 00 29 00                       ....#.&.).

00000086 <digital_pin_to_port_PGM>:
  86:	04 04 04 04 04 04 04 04 02 02 02 02 02 02 03 03     ................
  96:	03 03 03 03                                         ....

0000009a <digital_pin_to_bit_mask_PGM>:
  9a:	01 02 04 08 10 20 40 80 01 02 04 08 10 20 01 02     ..... @...... ..
  aa:	04 08 10 20                                         ...

000000ae <digital_pin_to_timer_PGM>:
  ae:	00 00 00 07 00 02 01 00 00 03 04 06 00 00 00 00     ................
  be:	00 00 00 00                                         ....

Next, the r1 register is zeroed (it’s assumed to always be zero throughout the program), and the SREG register is zeroed. Then our stack is initialized. The stack is used primarily to keep track of program execution and data passing to/from functions.

	clr	__zero_reg__
	out	AVR_STATUS_ADDR, __zero_reg__
	ldi	r28,lo8(__stack)
	ldi	r29,hi8(__stack)

Next is data initialization code. First, the code copies global/static initialized variables into SRAM. Then, if any uninitialized variables exist, a copy BSS section is appended to your code. Finally, if there are any C++ constructor/destructors, a bit of additional code is inserted (none here). In the blink disassembly, 11 bytes of memory are copied: timer0_millis (4 bytes), timer0_overflow_count (4 bytes), timer0_fract (1 byte), and led (2 bytes).

	ldi	r17, hi8(__data_end)
	ldi	r26, lo8(__data_start)
	ldi	r27, hi8(__data_start)
	ldi	r30, lo8(__data_load_start)
	ldi	r31, hi8(__data_load_start)
	rjmp	__do_copy_data_start
	lpm	r0, Z+
	st	X+, r0
	cpi	r26, lo8(__data_end)
	cpc	r27, r17
	brne	__do_copy_data_loop

/* __do_clear_bss is only necessary if there is anything in .bss section.  */
	ldi	r17, hi8(__bss_end)
	ldi	r26, lo8(__bss_start)
	ldi	r27, hi8(__bss_start)
	rjmp	do_clear_bss_start
	st	X+, __zero_reg__
	cpi	r26, lo8(__bss_end)
	cpc	r27, r17
	brne	do_clear_bss_loop

  __do_global_ctors and __do_global_dtors are only necessary if there
  are any constructors/destructors.
	ldi	r17, hi8(__ctors_start)
	ldi	r28, lo8(__ctors_end)
	ldi	r29, hi8(__ctors_end)
	rjmp	__do_global_ctors_start
	sbiw	r28, 2
	mov_h	r31, r29
	mov_l	r30, r28
	XCALL	__tablejump__
	cpi	r28, lo8(__ctors_start)
	cpc	r29, r17
	brne	__do_global_ctors_loop

	ldi	r17, hi8(__dtors_end)
	ldi	r28, lo8(__dtors_start)
	ldi	r29, hi8(__dtors_start)
	rjmp	__do_global_dtors_start
	mov_h	r31, r29
	mov_l	r30, r28
	XCALL	__tablejump__
	adiw	r28, 2
	cpi	r28, lo8(__dtors_end)
	cpc	r29, r17
	brne	__do_global_dtors_loop

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , , , , . Bookmark the permalink.

1 Response to Arduino Disassembly: Who Put This Stuff Here?

  1. Initializing SREG and the stack is unnecessary code that can be removed.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s