Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Für die ASM Programmierung kann man an Stelle von AVRStudio auch den Assembler von GCC (AS) benutzen. Dieser läuft z.B. auch unter Linux. Anders als der Atmel Assembler, der direkt ein .hex oder .bin File erzeugt, wird beim GNU Assembler wie beim C-Compiler erst ein Objektfile erzeugt und dann der Linker aufgerufen, um das .hex-file zu erzeugen. Ein Grund den GNU Assembler zu nutzen ist es, wenn man C und ASM Code mischen will, der ASM-Teil aber zu lang ist, um dafür Inline-Assembler zu nutzen.

Die Syntax unterscheidet sich etwas vom Atmel Assembler, vor allem bei den Direktiven. Ein weiterer Unterschied liegt in den Definitionen der IO Register in io.h. Es wird hier jeweils die Speicheradresse angegeben, also die für den Zugriff mit dem Befehl LDS und nicht die IO-Adresse für den Zugriff mit Befehlen wie IN, OUT usw. Um die IO-Adresse zu erhalten gibt es das Makro _SFR_IO_ADDR(sfr).

Eigentlich ist es ein normales GCC-Project, nur dass kein C-Programm da ist. Daher muss die Definition von der "main"-Funktion, die ja obligat ist, im Assembler erfolgen.

#include <avr/io.h>                ; das definiert die SFRS usw., je nach Controllertyp

.text                              ; was nun folgt, gehört in den FLASH-Speicher

.global main                       ; main ist auch in anderen Modulen bekannt

main:                              ; zu 'main' wird nach Reset hingesprungen
        ;....   eigene befehle
Hauptschleife:                     ; ein Sprunglabel
        ;....   eigene befehle
        rjmp Hauptschleife         ; immer wiederholen

Und das wars auch schon.

Es bietet sich an, GCC aufzurufen und ihn die Arbeit an Assembler und Linker delegieren zu lassen. Standard Datei-Erweiterung dazu ist *.S:

avr-gcc -o beispiel.elf -mmcu=atmega8 beispiel.S

Falls die Plattform Ärger mit Groß/Kleinschreibung macht wie Windows, dann geht auch

avr-gcc -x assembler-with-cpp -o beispiel.elf -mmcu=atmega8 beispiel.ss

GCC legt die Vektortabelle selbständig an und nimmt die richtigen Einträge vor. Dies gilt auch für den RESET-Vektor. Bevor zu main gesprungen wird, wird noch eine kleine Initialisierung gemacht: Stackpointer setzen und Y-Reg darauf initialisieren, um es als Framepointer nutzen zu können.

Falls man eine IRQ bedienen möchte, schreibt man

.text
.global SIG_OVERFLOW0   ; alternativ: __vector_9 schreiben, wenn 9 die IRQ-Nummer ist
                        ; SIG_XXX ist ein #define aus avr/ioxxxx.h
                        ; das durch #include <avr/io.h> mitincludet wird

SIG_OVERFLOW0:          ; dito
       ; ISR-Code
       reti

Dadurch wird an der richtigen Stelle der Vektortabelle ein Eintrag veranlasst.

Ein Disassemble des Maschinencodes sieht dann so aus (Vectab gekürzt):

Disassembly of section .text:

00000000 <__vectors>:
   0:	12 c0       	rjmp	.+36     	; 0x26
   2:	18 c0       	rjmp	.+48     	; 0x34
   4:	17 c0       	rjmp	.+46     	; 0x34
...
  12:	11 c0       	rjmp	.+34     	; 0x36
...

00000026 <__ctors_end>:
  26:	11 24       	eor	r1, r1
  28:	1f be       	out	0x3f, r1	; 63
  2a:	cf e5       	ldi	r28, 0x5F	; 95
  2c:	d4 e0       	ldi	r29, 0x04	; 4
  2e:	de bf       	out	0x3e, r29	; 62
  30:	cd bf       	out	0x3d, r28	; 61
  32:	02 c0       	rjmp	.+4      	; 0x38

00000034 <__bad_interrupt>:
  34:	e5 cf       	rjmp	.-54     	; 0x0

00000036 <__vector_9>:
  36:	18 95       	reti

00000038 <main>:
  38:	ff cf       	rjmp	.-2      	; 0x38

Weblinks

  • [1] avr-libc manual (englisch)
  • [2] as manual (englisch)

Siehe auch