Descubrí cómo ensamblar sin mensajes de error.
Los mensajes de error que ve son causados por el hecho de que avr-as
no invoca el preprocesador C y, por lo tanto, las líneas #include
se leen como comentarios regulares.
Cree un archivo ledON.S
y observe el CAPITAL S
en el nombre del archivo. El capital S
indica que el preprocesador C debe invocarse primero. Crea el archivo con el siguiente contenido:
#include <avr/io.h>
init: sbi _SFR_IO_ADDR(DDRB),0x05 ; Configure port B pin 5 as output
ret
.global main
main:
call init
loop:
sbi _SFR_IO_ADDR(PORTB),0x05 ; Toggle output pin HIGH
cbi _SFR_IO_ADDR(PORTB),0x05 ; Toggle output pin LOW
rjmp loop
La parte main
se requiere en el código fuente, aquí es donde generalmente se encuentra el programa principal. Si lo elimina, nunca se ejecutará ningún código real y, por lo tanto, el compilador se quejará de eso. El código en una rutina llamada init
debe llamarse explícitamente desde main
. Si bien el pin cambia, en la práctica esto será demasiado rápido para ver a simple vista. Si verifica con un osciloscopio, verá una onda cuadrada (estimo en un ciclo de trabajo del 25%).
Luego arme el código fuente con:
avr-gcc -mmcu=atmega168a ledON.S -o ledON.o
De nuevo, se requiere avr-gcc
para invocar el preprocesador C
.
Para verificar el resultado del programa ensamblado, ejecute el siguiente comando:
avr-objdump -C -d ./ledON.o
Y el listado de desmontaje resultante se ve así:
./ledON.o: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d4 e0 ldi r29, 0x04 ; 4
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
74: 0e 94 42 00 call 0x84 ; 0x84 <main>
78: 0c 94 47 00 jmp 0x8e ; 0x8e <_exit>
0000007c <__bad_interrupt>:
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000080 <init>:
80: 25 9a sbi 0x04, 5 ; 4
82: 08 95 ret
00000084 <main>:
84: 0e 94 40 00 call 0x80 ; 0x80 <init>
00000088 <loop>:
88: 2d 9a sbi 0x05, 5 ; 5
8a: 2d 98 cbi 0x05, 5 ; 5
8c: fd cf rjmp .-6 ; 0x88 <loop>
0000008e <_exit>:
8e: f8 94 cli
00000090 <__stop_program>:
90: ff cf rjmp .-2 ; 0x90 <__stop_program>
INTERMEZZO
Notarás que el ensamblador inicializará automáticamente el puntero de pila
y registro de estado en __ctors_end
.
También agregará automáticamente un rjmp
al final del código.
El comportamiento predeterminado de un programa ensamblado por gcc-avr cuando finaliza es:
- desactivar las interrupciones (
_exit
, cli
)
- bucle vacío infinito que no hace nada (
__stop_program
, rjmp .-2
)