K (→Befehle) |
(→Unterschiede zu anderen Sprachen: Tabelle: Gegenüberstellung) |
||
Zeile 16: | Zeile 16: | ||
Höherer Sprachen kennen alle möglichen Datentypen in allen möglichen Längen, integer, floating point, Strings. Dadurch sind aber auch gleich die möglichen und zulässigen Operationen damit schon eingeschränkt. | Höherer Sprachen kennen alle möglichen Datentypen in allen möglichen Längen, integer, floating point, Strings. Dadurch sind aber auch gleich die möglichen und zulässigen Operationen damit schon eingeschränkt. | ||
− | Beim Assembler gibt es nur zwei Typen: Bytes mit Vorzeichen und Bytes ohne Vorzeichen. Aus. Und es gibt wenige Einschränkungen, was man damit machen kann. | + | Beim Assembler gibt es nur zwei Typen: Bytes mit Vorzeichen und Bytes ohne Vorzeichen. Aus. Und es gibt wenige Einschränkungen, was man damit machen kann. |
+ | |||
+ | '''Gegenüberstellung Assembler und Hochsprache''' | ||
+ | |||
+ | {| {{Blauetabelle}} | ||
+ | | <div align="center">'''Quellcode'''</div> | ||
+ | | <div align="center">'''Vorteile'''</div> | ||
+ | | <div align="center">'''Nachteile'''</div> | ||
+ | |- valign="top" | ||
+ | | '''Assembler''' | ||
+ | | <!--------------------- + Assembler --> | ||
+ | Prinzipiell der schnellste bzw kürzeste Code, weil man volle Kontrolle über die Maschine hat | ||
+ | | <!--------------------- - Assembler --> | ||
+ | * komplette Neuimplementierung bei Portierung auf eine andere Architektur | ||
+ | * sogar Portierung innerhalb der gleichen Controllerfamilie (z.B. AVR) erfordert Anpassungen wegen unterschiedlichem Befehlssätze | ||
+ | * aufwändig in der Entwicklung, fehleranfällig, schwer zu debuggen und nachzuvollziehen | ||
+ | |- valign="top" | ||
+ | | '''Hochsprache''' | ||
+ | | <!--------------------- + Hochsprache --> | ||
+ | * gute Portierbarkeit | ||
+ | * schnelle Entwicklung | ||
+ | * weniger fehlerabfällig, bessere Fehlersuche | ||
+ | * beim Upgrade auf eine neue Compiler-Version mit besserten Optimierunsstrategien hat man die Optimierungen, ohne die Quelle ändern zu müssen | ||
+ | | <!--------------------- - Hochsprache --> | ||
+ | * ineffizienterer Code | ||
+ | * Unsicherheit, wie der Code genau aussieht | ||
+ | |- valign="top" | ||
+ | | '''Intermix''' Hochsprache + Assembler''' | ||
+ | | <!--------------------- + Mix --> | ||
+ | * Vorteile von Hochsprache und Assembler | ||
+ | * nur die Assembler Module/Schnippsel sind schwer portierbar | ||
+ | * Assembler kann ganz zielgerichtet an kritischen Stellen eingesetzt werden | ||
+ | * Quellcode bleibt gut lesbar | ||
+ | | <!--------------------- - Mix --> | ||
+ | * Nachteile von Hochsprache und Assembler | ||
+ | *die Konvention der Hochsprache muss genau befolgt werden (Registerverwendung bei Funktionsaufrufen, etc.) | ||
+ | * es sind zwei Sprachen zu beherrschen | ||
+ | * Objektformat bzw. Compiler und Assembler müssen zueinander passen | ||
+ | |} | ||
==Struktur eines AVR-Maschinenprogrammes== | ==Struktur eines AVR-Maschinenprogrammes== |
Version vom 5. Dezember 2005, 16:57 Uhr
Inhaltsverzeichnis
AVR Assembler Einführung
Es gibt mehrere Gründe, sich mit dem AVR-Assembler zu beschäftigen:
- reines Interesse
- Man hat eben keinen anderen Compiler, Bascom kostet ja was und GCC-C kann man möglicherweise genausowenig, also warum nicht gleich.
- Endlich Programmieren ohne Sprach-Restriktionen
- (theoretisch) sagenhaft schneller und kurzer Code
Dadurch ergeben sich aber auch verschiedene Haupt-Zielgruppen für eine Einführung. Die eine ist in einer oder mehreren anderen Sprachen durchaus versiert, und möchte endlich auch mal richtig in die Tiefen der Hardware steigen. Die andere ist totaler Neueinsteiger und hat eben gehört, daß man Micro-Controller eben am besten mit Assembler programmiert.
Unterschiede zu anderen Sprachen
Bildhaft ist der Unterschied der: Beim Assembler hat man ein weißes Stück Papier (ohne Linien) und ein Alphabet von A-Z. Aus diesen Buchstaben soll man nun erstmal Worte suchen und dann damit einen sinnvollen Text verfassen.
Bei "höheren" Sprachen ist das Papier zumindest mal liniert, und dazu kriegt man auch ein Wörtebuch und die Grammatik. Teile des Textes sind schon vorgeschrieben und ich muß nurmehr bei den ..... Punkten was einsetzen.
- Daten
Höherer Sprachen kennen alle möglichen Datentypen in allen möglichen Längen, integer, floating point, Strings. Dadurch sind aber auch gleich die möglichen und zulässigen Operationen damit schon eingeschränkt.
Beim Assembler gibt es nur zwei Typen: Bytes mit Vorzeichen und Bytes ohne Vorzeichen. Aus. Und es gibt wenige Einschränkungen, was man damit machen kann.
Gegenüberstellung Assembler und Hochsprache
Quellcode
|
Vorteile
|
Nachteile
|
Assembler |
Prinzipiell der schnellste bzw kürzeste Code, weil man volle Kontrolle über die Maschine hat |
|
Hochsprache |
|
|
Intermix Hochsprache + Assembler |
|
|
Struktur eines AVR-Maschinenprogrammes
Bei höheren Sprachen wird diese Struktur vom Compiler/Linker erzeugt, beim Assembler muß man sich selbst darum kümmern.
Der Controller beginnt mit der Abarbeitung links oben bei der Programmspeicheradresse 0000. Wenn man Interrupts verwenden will, muß man gleich das nächste Bereich (ISR-Vectoren) überspringen (mit "JMP"). Wenn nicht, kann man sich diese Vektoren aber auch wegdenken.
Man landet so oder so bei den Befehlen, die der Initialisierung dienen (Setzen der Startbedingung). Das ist zumindest die Festlegung des Stack-Pointers, sonst kann man keine "CALLS" oder Interrupts durchführen.
Danach kommt man von oben in die Haupt-Programm-Schleife, die (üblicherweise) immer wieder ohne Ende durchlaufen wird.
Das "End" ist bei Microcontrollern daher auch kaum wirklich notwendig. Wenn es da ist, ist das eine ewige Wiederholung eines einzigen Befehls.
Source-Code Muster
Das folgende Codebeispiel ist eine reine Basis-Initialisierung ohne irgendeine erkennbare Funktion. Für den Einstieg ist es am besten, das einfach abzuschreiben, wie es ist, und dann an den bezeichnenten Stellen mit eigenen Befehlen nach und nach zu erweitern.
Atmel AVR-Assembler
.NOLIST ; List-Output unterdrücken .INCLUDE <m8def.inc> ; das gibt es für jeden Controllertyp .LIST ; List-Output wieder aufdrehen .CSEG ; was nun folgt, gehört in den FLASH-Speicher ;------------------------------------------------------ ; Start Adresse 0000 ;------------------------------------------------------ RESET: rjmp INIT ; springen nach "INIT" ;------------------------------------------------------ ; ISR VECTORS ;------------------------------------------------------ ; ..... hier kommen dann die Sprungadressen für die Interrupts rein ; dazu kommen wir noch .ORG INT_VECTORS_SIZE ; dadurch haben wir für die Vektoren Platz gelassen INIT: ;------------------------------------------------------ ; INITIALIZE ;------------------------------------------------------ ldi r24,high(RAMEND) ;Stack Pointer setzen out SPH,r24 ; "RAMEND" ist in m8def.inc (s.o.) festgelegt ldi r24,low(RAMEND) ; out SPL,r24 ; ;------------------------------------------------------ ; eigene Initialisierungen ;------------------------------------------------------ ;.... ;.... ;.... ;------------------------------------------------------ ; HAUPTSCHLEIFE ;------------------------------------------------------ Hauptschleife: ;.... eigene befehle ;.... eigene befehle ;.... eigene befehle rjmp Hauptschleife ; immer wiederholen ;------------------------------------------------------ ; ENDE ;------------------------------------------------------ Ende: rjmp Ende
GNU Assembler
#include <avr/io.h> ; das gibt den Controllertyp an .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; 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
Befehle
Autor: PicNick