In Fundamentum 1 wird auf den Maschinencode, der sich hinter der Assemblerprogrammierung versteckt, etwas näher eingegangen.
Die ATmega Prozessoren verfügen über drei verschiedene Speicher.
Das Speicherregister unterteilt sich noch einmal in das I/O memory und register file.
Der Programmspeicher enthält Instruktionen von 16-Bit Wortlänge; er ist WORD-adressierbar, besteht also aus zwei Byte. SRAM und EEPROM sind Byte-adressierbar.
Der ATmega8515 besitzt unter anderen
Die letzten drei Register sorgen dafür, dass die im Programmspeicher abgelegten Instruktionen korrekt ausgeführt werden.
Betrachten wir als Beispiel die Instruktion
Im AVR Instruction Manual werden alle Instruktionen der AVR-Controller beschrieben. Die LDI Instruktion ist ein 16-Bit Opcode mit folgendem Format:
Die 16-Bit der Instruktion LDI (load immediately) sind in vier Nibble aufgeteilt.
Nibble 3 ist der sogenannte OP-Code der Instruktion, hier also 0b1110.
Nibble 0 und 2 stellen acht Bit für eine Konstante zur Verfügung. Es lassen sich damit Werte von 0 bis 255 darstellen.
Nibble 1 beschreibt das Zielregister, in das die Konstante geladen werden soll. Mit vier Bits lassen sich maximal 16 Register benennen: R16 - R31. Zur Darstellung der Zahlen 16 bis 31 benötigt man binär eigentlich 5 Bits, die alle im höchsten Bit eine 1 stehen haben. "Schneidet" man diese 1 ab, dann entspricht der Binärzahl 0b0000 das Register R16, 0b0001 das Register R17 usw.
Der Befehl
lädt in das Register hreg_1 (R16) das higher Byte von Ramend und das ist 02. Der zugehörige binäre Maschinencode lautet:
Nibble 3 ist der OP-Code für die Instruktion LDI, in Nibble 2 und Nibble 0 steht die Konstante 2 und in Nibble 2 das Zielregister R16 mit der Adresse 0b0000.
Der Befehl
lädt in das Register hreg_1 das lower Byte von Ramend, das ist 5F. Der zugehörige binäre Maschinencode lautet jetzt:
Die beiden eben besprochenen Ladebefehle tauchen in einem strukturierten Assemblerprogramm meistens gleich zu Anfang des Programms im Reset-Block auf und initialisieren den sogenannten Stack. Das nachfolgende einfache Programm macht nichts anderes als nur den Stack zu initialisieren.
Gleich zu Anfang des Programms wurde die Direktive
eingebaut. Dadurch wurde im Project-Manager von AVR Studio im Verzeichnis OUTPUT nach dem Kompilierungsvorgang eine Datei fundamente1.lst angelegt. Mit einem Doppelklick öffnen wir sie.
Ganz links befinden sich zwei Spalten. Die schauen wir uns jetzt näher an. Die Spalte ganz links beginnt bei 000000 und gibt die Adresse im Programmspeicher an. Rechts daneben steht der Maschinencode, also das, was der Compiler aus unserem Assemblerprogramm "gemacht" hat. Der Maschinencode ist für jede Instruktion einzeln ausgewiesen.
Es sind genau die Ergebnisse, die wir oben gerade besprochen haben.
Auf die gleiche Weise lassen sich die beiden anderen Maschinenbefehle aus dem Assemblerprogramm herleiten. Dazu muss nur der OP-Code der Instruktion OUT (AVR Instruction Manual) und die Adresse des Stackpointer-Registers nachgeschlagen werden.
Die OUT Instruktion ist ein 16-Bit Opcode mit folgendem Format:
Nibble 3 ist der sogenannte OP-Code der Instruktion OUT.
Nibble 0 und 2 stellen sieben Bit für eine Zieladresse im I/O Registerraum. Es lassen sich damit Werte von 0 bis 63 eingeben. Das Highbyte des Stackpointers (SPH) hat die Adresse $3E oder 0b0011 1110, das Lowbyte $3D oder 0b0011 1101. Das höchste Bit in Nibble 2 ist vom Hersteller vorgegeben eine 1 und das niedrigste Bit ist das höchste Bit (Bit 5) für das in der Instruktion angesprochene Arbeitsregister R16 (0b10000).
Nibble 1 beschreibt die restlichen vier Bits des angesprochenen Arbeitsregisters, aus dem Daten an die Zieladresse übertragen werden.
Der Befehl
wird als binärer Maschinencode zu:
Der Befehl
wird als binärer Maschinencode zu:
Im Programmlisting finden wir genau diese Eintragungen.
Auf die gleiche Weise lässt sich für die abschließende RJMP-Instruktion dem Instruction Manual entnehmen:
In den Nibbeln 0 - 2 steht die Anzahl der Zeilen, die innerhalb des Programmes zurück- oder vorgesprungen werden soll. In diesem Fall ist es ein Rücksprung um eine Programmzeile, also -1 oder 0b1111 1111 1111 oder $FFF. Zusammen mit dem OP-Code ergibt sich als Maschinencode: $CF FF.
Der Maschinencode ist im Programmspeicher abgelegt und kann mit Hilfe des Debuggers im AVR-Studio sichtbar gemacht werden.
Der Maschinencode ist in little-endian-order im Programmspeicher ab Position $0000 abgelegt. Er setzt sich aus fünf Instruktionen zusammen: zwei LDI-, zwei OUT-Instruktionen und einer RJMP-Instruktion.
Die Map-Datei
Im Verzeichnis Output wird nach dem Compilieren eines Programms eine Tabelle *.map angelegt, in der alle benutzten Symbole mit den ihnen zugeordneten Werten zu finden sind. Für das vorliegende Beispielprogramm finden sich am Ende der Liste folgende Eintragungen: