Estudando_o_codigo_assembly Objdump

Muitas vezes precisei estudar o código assembly gerado do meu código C/C++, inicialmente para aprender, depois por curiosidade, e até mesmo por acreditar que poderia optimizar alguma parte do código que escrevi. Bem o GCC já faz um excelente trabalho, ainda mais se vc ajuda-lo, já escrevi faz algum tempo sobre como escrever código em C otimizado para microcontroladores AVR.

O comando do GCC que me ajuda a obter o código C de forma legível é o objdump, se seu código foi gerado com símbolos de depuração, que é o caso do Arduino, você terá um resultado bastante interessante de fácil leitura e entendimento, pois as referências ao seu código estarão comandadas.

Para obter o help do objdump, digite o comando com a chave --help e terá o resultado similar abaixo (resumi a saida ao que nos interessa):

$ avr-objdump --help
[...]
Usage: avr-objdump.exe <option(s)> <file(s)>
 Display information from object <file(s)>.
 At least one of the following switches must be given:
[...]
  -d, --disassemble        Display assembler contents of executable sections
  -D, --disassemble-all    Display assembler contents of all sections
  -S, --source             Intermix source code with disassembly
[...]
  @<file>                  Read options from <file>
  -v, --version            Display this program's version number
  -i, --info               List object formats and architectures supported
  -H, --help               Display this information
[...]
  -z, --disassemble-zeroes       Do not skip blocks of zeroes when disassembling
[...]

Usaremos neste exemplo o código do exemplo MoreComplexblinking.ino, quando o arduino compila ele gera uma pasta temporária encontre esta pasta, no windows estará no diretório %TEMP%\\arduino_build_996254, o número muda a cada vez que abre a IDE do arduino e a cada sketch compilado.

Neste diretório você encontrar os seguintes aquivos após uma compilação bem sucedida:

drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 12:35 .
drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 12:40 ..
-rw-r--r-- 1 carlosdelfino 197121 1,6K jul 10 12:04 build.options.json
drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 12:05 core
-rw-r--r-- 1 carlosdelfino 197121 2,2K jul 10 12:04 includes.cache
drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 11:14 libraries
-rw-r--r-- 1 carlosdelfino 197121   13 jul 10 12:05 MoreComplexBlinking.ino.eep
-rw-r--r-- 1 carlosdelfino 197121  36K jul 10 12:05 MoreComplexBlinking.ino.elf
-rw-r--r-- 1 carlosdelfino 197121  13K jul 10 12:05 MoreComplexBlinking.ino.hex
-rw-r--r-- 1 carlosdelfino 197121  14K jul 10 12:05 MoreComplexBlinking.ino.with_bootloader.hex
drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 11:14 preproc
drwxr-xr-x 1 carlosdelfino 197121    0 jul 10 12:04 sketch

O arquivo que é importante para nós é o arquivo de extensão elf, este arquivo conterá todo código já preparada para ser convertido no binário que será enviado ao Arduino, portanto ele pode ser convertido fácilmente em Assembly. o arquivo de extensão .hex já é o arquivo pronto para envio ao microcontrolador.

Vamos criar um arquivo com todo o código assembly, porém iremos exibir aqui apenas uma parte de nosso código, no caso o conteúdo da função que controla o LED vermelho.

taskLoop(redLED)
{
  static unsigned char counter = 0;
  
  if (!greenLED_isOn)
  {
     if (counter >2)
       resumeTask(greenLED);
     counter++;
  }
  
  redLED_isOn = false;
  taskDelay(1000);
  redLED_isOn = true; 
  taskDelay(1000);
}

Resultado do Comando $ avr-objdump -Sz MoreComplexBlinking.ino.elf > MoreComplexBlinking.ino.elf.S.dump com toda o código que controla o LED vermelho, é um código longo, irei entrar em detalhes sobre o código com foco no DuinOS em outro artigo futuro:

taskLoop(redLED)
{
  static unsigned char counter = 0;
  
  if (!greenLED_isOn)
     ec2:	80 91 17 01 	lds	r24, 0x0117	; 0x800117 <greenLED_isOn>
     ec6:	81 11       	cpse	r24, r1
     ec8:	52 c0       	rjmp	.+164    	; 0xf6e <_Z11redLED_TaskPv+0xbe>
  {
     if (counter >2)
     eca:	f0 90 16 01 	lds	r15, 0x0116	; 0x800116 <__data_end>
     ece:	22 e0       	ldi	r18, 0x02	; 2
     ed0:	2f 15       	cp	r18, r15
     ed2:	08 f0       	brcs	.+2      	; 0xed6 <_Z11redLED_TaskPv+0x26>
     ed4:	49 c0       	rjmp	.+146    	; 0xf68 <_Z11redLED_TaskPv+0xb8>
       resumeTask(greenLED);
     ed6:	00 91 d5 05 	lds	r16, 0x05D5	; 0x8005d5 <greenLED>
     eda:	10 91 d6 05 	lds	r17, 0x05D6	; 0x8005d6 <greenLED+0x1>
		/* It does not make sense to resume the calling task. */
		configASSERT( xTaskToResume );

		/* The parameter cannot be NULL as it is impossible to resume the
		currently executing task. */
		if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
     ede:	80 91 29 06 	lds	r24, 0x0629	; 0x800629 <pxCurrentTCB>
     ee2:	90 91 2a 06 	lds	r25, 0x062A	; 0x80062a <pxCurrentTCB+0x1>
     ee6:	08 17       	cp	r16, r24
     ee8:	19 07       	cpc	r17, r25
     eea:	09 f4       	brne	.+2      	; 0xeee <_Z11redLED_TaskPv+0x3e>
     eec:	3d c0       	rjmp	.+122    	; 0xf68 <_Z11redLED_TaskPv+0xb8>
     eee:	01 15       	cp	r16, r1
     ef0:	11 05       	cpc	r17, r1
     ef2:	09 f4       	brne	.+2      	; 0xef6 <_Z11redLED_TaskPv+0x46>
     ef4:	39 c0       	rjmp	.+114    	; 0xf68 <_Z11redLED_TaskPv+0xb8>
		{
			taskENTER_CRITICAL();
     ef6:	0f b6       	in	r0, 0x3f	; 63
     ef8:	f8 94       	cli
     efa:	0f 92       	push	r0

		/* It does not make sense to check if the calling task is suspended. */
		configASSERT( xTask );

		/* Is the task being resumed actually in the suspended list? */
		if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE )
     efc:	d8 01       	movw	r26, r16
     efe:	1a 96       	adiw	r26, 0x0a	; 10
     f00:	8d 91       	ld	r24, X+
     f02:	9c 91       	ld	r25, X
     f04:	87 5e       	subi	r24, 0xE7	; 231
     f06:	95 40       	sbci	r25, 0x05	; 5
     f08:	69 f5       	brne	.+90     	; 0xf64 <_Z11redLED_TaskPv+0xb4>
		{
			/* Has the task already been resumed from within an ISR? */
			if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE )
     f0a:	f8 01       	movw	r30, r16
     f0c:	84 89       	ldd	r24, Z+20	; 0x14
     f0e:	95 89       	ldd	r25, Z+21	; 0x15
     f10:	f5 e0       	ldi	r31, 0x05	; 5
     f12:	80 3f       	cpi	r24, 0xF0	; 240
     f14:	9f 07       	cpc	r25, r31
     f16:	31 f1       	breq	.+76     	; 0xf64 <_Z11redLED_TaskPv+0xb4>
			{
				/* Is it in the suspended list because it is in the	Suspended
				state, or because is is blocked with no timeout? */
				if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961.  The cast is only redundant when NULL is used. */
     f18:	89 2b       	or	r24, r25
     f1a:	21 f5       	brne	.+72     	; 0xf64 <_Z11redLED_TaskPv+0xb4>
				{
					traceTASK_RESUME( pxTCB );

					/* The ready list can be accessed even if the scheduler is
					suspended because this is inside a critical section. */
					( void ) uxListRemove(  &( pxTCB->xStateListItem ) );
     f1c:	68 01       	movw	r12, r16
     f1e:	22 e0       	ldi	r18, 0x02	; 2
     f20:	c2 0e       	add	r12, r18
     f22:	d1 1c       	adc	r13, r1
     f24:	c6 01       	movw	r24, r12
     f26:	0e 94 a8 01 	call	0x350	; 0x350 <uxListRemove>
					prvAddTaskToReadyList( pxTCB );
     f2a:	d8 01       	movw	r26, r16
     f2c:	56 96       	adiw	r26, 0x16	; 22
     f2e:	8c 91       	ld	r24, X
     f30:	90 91 26 06 	lds	r25, 0x0626	; 0x800626 <uxTopReadyPriority>
     f34:	98 17       	cp	r25, r24
     f36:	10 f4       	brcc	.+4      	; 0xf3c <_Z11redLED_TaskPv+0x8c>
     f38:	80 93 26 06 	sts	0x0626, r24	; 0x800626 <uxTopReadyPriority>
     f3c:	8b 9d       	mul	r24, r11
     f3e:	c0 01       	movw	r24, r0
     f40:	11 24       	eor	r1, r1
     f42:	b6 01       	movw	r22, r12
     f44:	85 5f       	subi	r24, 0xF5	; 245
     f46:	99 4f       	sbci	r25, 0xF9	; 249
     f48:	0e 94 02 02 	call	0x404	; 0x404 <vListInsertEnd>

					/* A higher priority task may have just been resumed. */
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
     f4c:	e0 91 29 06 	lds	r30, 0x0629	; 0x800629 <pxCurrentTCB>
     f50:	f0 91 2a 06 	lds	r31, 0x062A	; 0x80062a <pxCurrentTCB+0x1>
     f54:	d8 01       	movw	r26, r16
     f56:	56 96       	adiw	r26, 0x16	; 22
     f58:	9c 91       	ld	r25, X
     f5a:	86 89       	ldd	r24, Z+22	; 0x16
     f5c:	98 17       	cp	r25, r24
     f5e:	10 f0       	brcs	.+4      	; 0xf64 <_Z11redLED_TaskPv+0xb4>
					{
						/* This yield may not cause the task just resumed to run,
						but will leave the lists in the correct state for the
						next yield. */
						taskYIELD_IF_USING_PREEMPTION();
     f60:	0e 94 45 01 	call	0x28a	; 0x28a <vPortYield>
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();
     f64:	0f 90       	pop	r0
     f66:	0f be       	out	0x3f, r0	; 63
     counter++;
     f68:	f3 94       	inc	r15
     f6a:	f0 92 16 01 	sts	0x0116, r15	; 0x800116 <__data_end>
 * @param ticks 
 */
inline void taskDelay(const portTickType ticks) __attribute__((__always_inline__));
inline void taskDelay(const portTickType ticks)
{
	portTickType xLastWakeTime = xTaskGetTickCount();
     f6e:	0e 94 9e 01 	call	0x33c	; 0x33c <xTaskGetTickCount>
     f72:	9a 83       	std	Y+2, r25	; 0x02
     f74:	89 83       	std	Y+1, r24	; 0x01

	//Better than vTaskDelay:
	vTaskDelayUntil( &xLastWakeTime, ticks);
     f76:	68 ee       	ldi	r22, 0xE8	; 232
     f78:	73 e0       	ldi	r23, 0x03	; 3
     f7a:	ce 01       	movw	r24, r28
     f7c:	01 96       	adiw	r24, 0x01	; 1
     f7e:	0e 94 5e 06 	call	0xcbc	; 0xcbc <vTaskDelayUntil>
  }
  
  redLED_isOn = false;
  taskDelay(1000);
  redLED_isOn = true; 
     f82:	e0 92 d9 05 	sts	0x05D9, r14	; 0x8005d9 <redLED_isOn>
 * @param ticks 
 */
inline void taskDelay(const portTickType ticks) __attribute__((__always_inline__));
inline void taskDelay(const portTickType ticks)
{
	portTickType xLastWakeTime = xTaskGetTickCount();
     f86:	0e 94 9e 01 	call	0x33c	; 0x33c <xTaskGetTickCount>
     f8a:	9a 83       	std	Y+2, r25	; 0x02
     f8c:	89 83       	std	Y+1, r24	; 0x01

	//Better than vTaskDelay:
	vTaskDelayUntil( &xLastWakeTime, ticks);
     f8e:	68 ee       	ldi	r22, 0xE8	; 232
     f90:	73 e0       	ldi	r23, 0x03	; 3
     f92:	ce 01       	movw	r24, r28
     f94:	01 96       	adiw	r24, 0x01	; 1
     f96:	0e 94 5e 06 	call	0xcbc	; 0xcbc <vTaskDelayUntil>
     f9a:	93 cf       	rjmp	.-218    	; 0xec2 <_Z11redLED_TaskPv+0x12>

00000f9c <__vector_16>:
#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
     f9c:	1f 92       	push	r1
     f9e:	0f 92       	push	r0
     fa0:	0f b6       	in	r0, 0x3f	; 63
     fa2:	0f 92       	push	r0
     fa4:	11 24       	eor	r1, r1
     fa6:	2f 93       	push	r18
     fa8:	3f 93       	push	r19
     faa:	8f 93       	push	r24
     fac:	9f 93       	push	r25
     fae:	af 93       	push	r26
     fb0:	bf 93       	push	r27
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
     fb2:	80 91 df 05 	lds	r24, 0x05DF	; 0x8005df <timer0_millis>
     fb6:	90 91 e0 05 	lds	r25, 0x05E0	; 0x8005e0 <timer0_millis+0x1>
     fba:	a0 91 e1 05 	lds	r26, 0x05E1	; 0x8005e1 <timer0_millis+0x2>
     fbe:	b0 91 e2 05 	lds	r27, 0x05E2	; 0x8005e2 <timer0_millis+0x3>
	unsigned char f = timer0_fract;
     fc2:	30 91 de 05 	lds	r19, 0x05DE	; 0x8005de <timer0_fract>

	m += MILLIS_INC;
	f += FRACT_INC;
     fc6:	23 e0       	ldi	r18, 0x03	; 3
     fc8:	23 0f       	add	r18, r19
	if (f >= FRACT_MAX) {
     fca:	2d 37       	cpi	r18, 0x7D	; 125
     fcc:	58 f5       	brcc	.+86     	; 0x1024 <__vector_16+0x88>
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
     fce:	01 96       	adiw	r24, 0x01	; 1
     fd0:	a1 1d       	adc	r26, r1
     fd2:	b1 1d       	adc	r27, r1
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
     fd4:	20 93 de 05 	sts	0x05DE, r18	; 0x8005de <timer0_fract>
	timer0_millis = m;
     fd8:	80 93 df 05 	sts	0x05DF, r24	; 0x8005df <timer0_millis>
     fdc:	90 93 e0 05 	sts	0x05E0, r25	; 0x8005e0 <timer0_millis+0x1>
     fe0:	a0 93 e1 05 	sts	0x05E1, r26	; 0x8005e1 <timer0_millis+0x2>
     fe4:	b0 93 e2 05 	sts	0x05E2, r27	; 0x8005e2 <timer0_millis+0x3>
	timer0_overflow_count++;
     fe8:	80 91 da 05 	lds	r24, 0x05DA	; 0x8005da <timer0_overflow_count>
     fec:	90 91 db 05 	lds	r25, 0x05DB	; 0x8005db <timer0_overflow_count+0x1>
     ff0:	a0 91 dc 05 	lds	r26, 0x05DC	; 0x8005dc <timer0_overflow_count+0x2>
     ff4:	b0 91 dd 05 	lds	r27, 0x05DD	; 0x8005dd <timer0_overflow_count+0x3>
     ff8:	01 96       	adiw	r24, 0x01	; 1
     ffa:	a1 1d       	adc	r26, r1
     ffc:	b1 1d       	adc	r27, r1
     ffe:	80 93 da 05 	sts	0x05DA, r24	; 0x8005da <timer0_overflow_count>
    1002:	90 93 db 05 	sts	0x05DB, r25	; 0x8005db <timer0_overflow_count+0x1>
    1006:	a0 93 dc 05 	sts	0x05DC, r26	; 0x8005dc <timer0_overflow_count+0x2>
    100a:	b0 93 dd 05 	sts	0x05DD, r27	; 0x8005dd <timer0_overflow_count+0x3>
}
    100e:	bf 91       	pop	r27
    1010:	af 91       	pop	r26
    1012:	9f 91       	pop	r25
    1014:	8f 91       	pop	r24
    1016:	3f 91       	pop	r19
    1018:	2f 91       	pop	r18
    101a:	0f 90       	pop	r0
    101c:	0f be       	out	0x3f, r0	; 63
    101e:	0f 90       	pop	r0
    1020:	1f 90       	pop	r1
    1022:	18 95       	reti

É importante observar que o DuinOS tem várias funções inline, o que leva a uma execução mais rápida das threads, porém aumenta o tamanho do código, já que cada função inline é replicada completamente onde é chamada, evitando o uso do stack para chamada de blocos de códigos.

Phone

+55 (85) 985205490

Address

R. Jose Alves Pereira, 50, Casa 1
Aquiraz, CE 61700-000
Brazil