Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Programmdarstellung

Die Einsatzmöglichkeiten eines Mikroprozessorsystems sind zwar durch seine Hardwarestruktur in einem gewissen Rahmen vorgegeben, seine Funktion wird jedoch erst durch das Programm bestimmt, das von ihm ausgeführt wird. Wir wollen im folgenden die Darstellung von Programmen in einer für den Menschen verständlichen, maschinenexternen Form und in einer für den Mikroprozessor verarbeitbaren, maschineninternen Form betrachten. Wir gehen dazu von einer einfachen AufgabensteIlung aus und legen einen für die Lösung der Aufgabe ausreichenden Satz von Maschinenbefehlen fest.

Programmbeispiel. Mit einem Mikroprozessorsystem soll ein Impulsgeber aufgebaut werden, der in konstanten Zeitabständen Impulse an eine periphere Einheit abgibt. Dazu wird als Ein/Ausgabeeinheit ein 16-Bit-Datenregister vorgesehen, in dem zu Beginn der Programmausführung von der peripheren Einheit eine Zahl bereitgestellt wird, die die Periodendauer t der Impulsfolge vorgibt (Bild 1.14). Der Mikroprozessor soll, nachdem er diese Zahl übernommen hat, in dasselbe Register zunächst eine Null und dann im Abstand der Impulse für die Dauer eines Befehls eine Eins ausgeben. Der Inhalt von Bit 0 des Registers bildet das Impulssignal für die Peripherie. Das Datenregister bezeichnen wir mit der symbolischen Adresse EAREG und weisen ihm die absolute Adresse 32768=215 zu.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Den Programmablauf zur Lösung dieser Aufgabe zeigt Bild 1.15 in der Form eines Flußdiagramms. Zunächst wird die Zeitkonstante t aus dem Datenregister EAREG in eine Arbeitsspeicherzelle ZEITK übernommen, danach wird das Datenregister mit dem Wert 0 für die anschließende Impulsausgabe initialisiert (Bit 0 = 0). Die Erzeugung eines Impulses erfolgt durch die wiederholte Übertragung der Werte 1 und 0 an das Datenregister, das den jeweils zuletzt übertragewird. Beide Werte stehen in den Speicherzellen NULL und EINS als konstante Operanden zur Verfügung.

Zur wiederholten Ausgabe der Werte wird der entsprechende Programmteil wiederholt durchlaufen, man sagt, er bildet eine Programmschleife. Diese Schleife enthält eine weitere, innere Programmschleife, die so oft durchlaufen wird, wie es der Wert der Zeitkonstanten ZEITK vorgibt; d.h. der Zeitabstand t zwischen zwei Impulsen wird bestimmt durch die Verarbeitungszeiten der einzelnen Befehle und die Anzahl der Schleifendurchläufe der inneren Schleife. Die Verarbeitungszeiten der Befehle sind durch die Anzahl der Maschinenzyklen pro Befehl und die Maschinenzykluszeit des Mikroprozessors festgelegt.

Symbolische Programmdarstellung.

Die folgende Liste zeigt einen Ausschnitt des Mikroprozessor-Befehlssatzes mit den Befehlen, die zur Lösung unserer Aufgabe benötigt werden. Die Befehle sind in symbolischer Schreibweise, wie sie bereits für den Subtraktionsbefehl verwendet wurde, und mit einer Kurzbeschreibung ihrer Funktionen angegeben.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Bild 1.16 zeigt das symbolische Programm in maschinenexterner Darstellung entsprechend dem Flußdiagramm Bild 1.15. Die innere Programmschleife ist mit dem bedingten Sprungbefehl BNE und die äußere Programmschleife mit dem unbedingten Sprungbefehl JMP abgeschlossen. Die äußere Schleife kann wegen der Verwendung des JMP-Befehls nicht verlassen werden und bildet damit eine Endlosschleife. Die in den Sprungbefehlen verwendeten symbolischen Adressen markieren in der linken Spalte diejenigen Befehle im Programm, die die Sprungziele darstellen. Auf gleiche Weise markieren die Symbole NULL, EINS und ZEITK die am Ende des Programms definierten konstanten Operanden 0 und 1 und den Speicherplatz für die Zeitkonstante.

Mit diesen Marken (Labels, Namen) können die zur Überführung des Programms in den Interncode notwendigen Adreßzuordnungen hergestellt werden. Beim späteren Laden des Programms in den Arbeitsspeicher schließt sich der Datenbereich, wie auch in der symbolischen Darstellung, unmittelbar an den Programmbereich an. Als Beispiel für die Ermittlung einer Zeitkonstanten ZEITK, die die Anzahl der inneren Schleifendurchläufe festlegt, geben wir den Impulsabstand mit t = 1 s und die Maschinenzykluszeit mit 1j.ls vor. Für die innere Programmschleife ergibt sich mit den Zyklenangaben aus Bild 1.16 eine Durchlaufzeit von 21 Maschinenzyklen, d.h. von 21j.ls. Diese Schleife wird so oft durchlaufen, wie der Wert von ZEITK angibt, d.h. 21· ZEITK IlS. In der äußeren Schleife kommen weitere 35 Maschinenzyklen, d.h. 35 IlS dazu. Der Impulsabstand t errechnet sich damit zu t = 21 . ZEITK + 35 IlS. Daraus ergibt sich der Wert von ZEITK und damit die Anzahl der inneren Schleifendurchläufe zu ZEITK = (t – 35)/21 ~ 47617.

Maschinencode-Darstellung. Unter Zugrundelegung unserer Befehlsformate wollen wir das symbolische Programm in seine maschineninterne Form, den Maschinencode, übersetzen. Der Maschinencode ist die Darstellung, in der ein Programm und seine Daten im Arbeitsspeicher vorliegen und die vom Mikroprozessor unmittelbar interpretiert werden kann. Dazu müssen die mnemonischen und symbolischen Angaben durch ihre äquivalenten binären Darstellungen ersetzt werden.

Die Zuordnung der mnemonischen Operationscodes zu den maschineninternen Operationscodes (Binärcodes) liegt fest und bildet die für unseren Mikroprozessor in Tabelle 1.4 dargestellte Zuordnungstabelle. Die Ersetzung der symbolischen Speicheradressen durch numerische Adressen ergibt sich aus der Lage des Programms und seiner Daten im Arbeitsspeicher. Die numerischen Adressen der allgemeinen Register und des Datenregisters der Ein/Ausgabeeinheit lassen sich auf diese Weise nicht ermitteln.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Aus diesem Grund hatten wir bereits früher das Symbol Ri dem allgemeinen Register i fest zugeordnet; desgleichen hatten wir dem Symbol EAREG die Adresse 32768 zugewiesen, die durch die Adreßdecodierung der Ein/Ausgabeeinheit vorgegeben ist. Für unser Beispiel wollen wir voraussetzen, daß das Programm den Arbeitsspeicher ab der Zelle 0 belegt, und erhalten damit die in Tabelle 1.5 angegebene Adreßzuordnung als Symboltabelle. Bei der Festlegung der Werte der symbolischen Adressen wurden die unterschiedlichen Befehlslängen (Einwort-, Zweiwort-, Dreiwortbefehle) berücksichtigt.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Als letztes bestimmen wir für jeden Befehl die beiden R/S-Bits aus der Angabe, ob es sich bei den rechts vom Operationscode stehenden Adreßangaben um Registeradressen (R/S = 1) oder um Speicheradressen (R/S = 0) handelt. Hierbei werden alle Adreßsymbole, die nicht den Registerspeicher betreffen, als Speichersymbole (genauer: als Symbole für den prozessorexternen Datenzugriff) aufgefaßt.

Bild 1.17 zeigt den endgültigen Maschinencode zusammen mit den Speicheradressen der einzelnen Maschinencodewörter, wobei die Dualzahldarstellung der Speicheradressen von 16 auf 5 Bits reduziert wurde. Der Programmcode belegt die Zellen 0 bis 22 und der Datenbereich die Zellen 23 bis 25. Die beiden Operanden NULL und EINS sind als Dualzahlen codiert; der Inhalt des Speicherwortes ZEITK ist vor Ausführung des Programms noch unbestimmt, was durch Striche angedeutet ist. Als Orientierungshilfe ist zu jedem Befehlswort das Mnemon bzw. das prozessorexterne Adreßsymbol angegeben.

Programmübersetzung (Assemblierung)

Ein Vergleich der beiden Programmdarstellungen in den Bildern 1.16 und 1.17 zeigt, daß die symbolische Darstellung für den Menschen sehr viel besser lesbar ist als die Maschinencode-Darstellung, die der Mikroprozessor zur Interpretation benötigt. Man wird deshalb ein Programm zunächst in symbolischer Form schreiben; anschließend wird es in den Maschinencode übersetzt und in den Arbeitsspeicher geladen.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Das Übersetzen kann, wie oben gezeigt wurde, von Hand geschehen. Man benötigt dazu die Zuordnungstabelle und die Adresse des ersten Maschinencodewortes im Speicher, um die Symboltabelle mit den Adreßzuordnungen aufstellen zu können. Bei größeren Programmen ist das manuelle Übersetzen jedoch sehr zeitaufwendig und vor allem fehleranfällig. Außerdem können kleine Änderungen im symbolischen Programm große Änderungen im Maschinencode nach sich ziehen. So ändern sich z.B. beim Entfernen oder Einschieben eines Befehls sämtliche Adreßbezüge auf die nachfolgenden Speicherzellen.

Da der Übersetzungsvorgang nach festen Regeln abläuft, kann seine Ausführung auch dem Mikroprozessor selbst übertragen werden. Dazu muß die symbolische Schreibweise eindeutig festgelegt sein, und wir benötigen ein Programm, welches das zu übersetzende symbolische Programm (Eingabedaten) in den Maschinencode (Ausgabedaten) umformt. Man nennt ein solches Übersetzungsprogramm Assembler (to assemble: montieren).

Die Regeln zur symbolischen Programmierung ergeben sich aus der Definition einer Assemblersprache. Man nennt Programme, die in Assemblersprache geschrieben sind, Assemblerprogramme. Assemblersprache. Die Assemblersprache legt die äußere Form einer Programmzeile fest. Eine solche Zeile ist in Zeichenfelder mit bestimmten Funktionen unterteilt. Die Feldgrenzen sind je nach Assemblersprache innerhalb einer Zeile entweder fließend oder in Form einer Tabelle festgelegt. Bild 1.18 zeigt ein festes Format, bei dem jedes Feld an einer vorgegebenen Position beginnt. Bei flexiblen Formaten muß die Feldabgrenzung durch ein besonderes Zeichen erfolgen, z.B. durch das Leerzeichen (space).

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Das Namensfeld dient zur symbolischen Adressierung einer Programmzeile und kann dazu ein Symbol enthalten. Das Operationsfeld nimmt den mnemonischen Operationscode auf, und im Adreßfeld stehen die symbolischen Adreßangaben, die z.B. durch Kommas getrennt sind. Das Kommentarfeld dient zur Kommentierung der entsprechenden Programmzeile. Programmzeilen können auch ausschließlich aus Kommentar bestehen. Sie werden dann in der ersten Zeichenspalte durch ein Sonderzeichen, z.B. *, gekennzeichnet. Kommentare bleiben während der Programmübersetzung unberücksichtigt und haben damit keinen Einfluß auf die Erzeugung des Maschinencodes.

Durch die Assemblersprache ist auch der Zeichenvorrat vorgegeben, mit dem eine Programmzeile formuliert werden kann (Großbuchstaben, Ziffern, Sonderzeichen). Sie schreibt außerdem vor, welche Zeichenketten zur Bildung von Adreßsymbolen erlaubt sind. So muß z.B. das erste Zeichen eines Adreßsymbols ein Buchstabe sein, es darf maximal 6 Zeichen lang sein und keine Sonderzeichen enthalten. Darüber hinaus legt die Assemblersprache die mnemonischen Operationscodes und spezielle Adreßsymbole, wie RO bis R 7 für den Registerspeicher, fest.

Im Unterschied zu höheren Programmiersprachen entspricht bei Assemblersprachen ein symbolischer Befehl genau einem Befehl im Maschinencode (1-zu-1- Übersetzung). Ein Assemblerprogramm ist damit gegenüber einem FORTRANoder ALGOL-Programm an die Prozessorhardware angepaßt. Assembleranweisungen. In unserem Programmbeispiel kommen neben den Befehlen auch Operanden vor, deren Werte vor der Programmausführung entweder bekannt sind oder für die lediglich eine Speicherzelle reserviert wird. Für diese und ähnliche Vorgaben sieht die Assemblersprache Assembleranweisungen (AssembIerdirektiven, Pseudobefehle) vor, die als Programmzeilen in das symbolische Programm eingefügt werden. Im Gegensatz zu den Maschinenbefehlen führen Assembleranweisungen bei der Übersetzung (Assemblierung) des Programms nicht immer auf Binärcode; sie dienen zur Steuerung des Übersetzungsvorgangs und zur Erzeugung von Konstanten.

Die wichtigsten Assembleranweisungen sind im folgenden zusammengefaßt. Wie die Maschinenbefehle unterliegen sie dem vorgegebenen Format einer Programmzeile und bestehen dementsprechend aus einem mnemonischen Code und einem Adreßteil. Die Verwendung von Namensangaben ist entweder wahlweise, was durch eine eckige Klammer gekennzeichnet ist, oder bindend, wie z.B. bei der EQU-Anweisung.

Mit diesen Assembleranweisungen läßt sich unser Programmbeispiel vollständig in Assemblersprache formulieren (Bild 1.19). Mit der ORG-Anweisung geben wir eine Speicherbelegung ab Zelle 0 vor; die END-Anweisung gibt dem Assembler die letzte zu verarbeitende Programmzeile an. Die Adreßzuweisung an das Datenregister EAREG, die in der bisherigen Schreibweise nicht möglich war, kann jetzt mit der EQU-Anweisung vorgenommen werden. Die beiden DCAnweisungen erzeugen die Dualzahlen für die Operanden NULL und EINS; mit der DS-Anweisung wird ein Speicherwort für den zu Beginn der Ausführung des Programms noch unbekannten Wert von ZEITK reserviert. Die Übersetzung des Programms durch den Assembler führt auf den Maschinencode, der in Bild 1.17 angegeben ist.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Assemblierung

Die Übersetzung (Assemblierung) eines Assemblerprogramms durch den Assembler erfolgt üblicherweise in zwei Phasen, d.h. in zwei Durchgängen durch das zu assemblierende Programm. Im ersten Durchgang werden die Adreßzuordnungen hergestellt und eine Fehlerliste angefertigt. Im zweiten Durchgang wird der Maschinencode erzeugt und eine Auflistung des Programms in symbolischer und binärer oder hexadezimaler Darstellung vorgenommen. In beiden Durchgängen wird das Assemblerprogramm zeilenweise gelesen und verarbeitet. Man nennt Assembler, die nach diesem Prinzip arbeiten, auch ZweiPhasen- Assembler (zur Funktionsweise von Assemblern siehe z.B. [10]). Die Adreßzuordnungen werden, wie bereits beschrieben, durch den Aufbau einer Symboltabelle ermittelt. Die Adreßzählung beginnt mit der in der ORG-Anweisung angegebenen Anfangsadresse und wird durch einen sogenannten Zuordnungszähler im Assembler vorgenommen.

Der Zuordnungszähler wird dazu mit der Anfangsadresse initialisiert und nach der Bearbeitung einer jeden Programmzeile um die Anzahl der von dieser Zeile im Maschinencode belegten Speicherwörter weitergezählt. Ein Symbol im Namensfeld der Programmzeile wird in die Symboltabelle mit dem augenblicklichen Wert des Zuordnungszählers und dem Attribut definiert eingetragen. Ein prozessorexternes Symbol im Adreßfeld wird ebenfalls in die Symboltabelle übernommen, jedoch mit dem Attribut verwendet versehen. Eine Ausnahme bilden Symbole im Namensfeld von EQUAnweisungen, deren Adreßwerte sich aus den Zahlenangaben im Adreßfeld selbst ergeben.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Enthält ein Programm mehrere ORG-Anweisungen, so wird der Zuordnungszähler bei jeder ORG-Anweisung mit dem im Adreßfeld angegebenen Wert neu geladen. Damit können beispielsweise der Programm- und der Datenbereich im Speicher getrennt voneinander angelegt werden. Bei Erreichen der ENDAnweisung müssen sämtliche Symbole definiert sein. Offene Adreßbezüge und Schreibweisen innerhalb der Programmzeilen, die die Assemblersprache nicht erlaubt, werden in der Fehlerliste vermerkt.

Tabelle 1.6 zeigt einen Schnappschuß beim Aufbau der Symboltabelle für unser Programmbeispiel nach Bearbeitung der BNE-Programmzeile. Zu diesem Zeitpunkt sind die Symbole ZEITK, NULL und EINS zwar verwendet worden, es konnte ihnen aber noch kein Adreßwert zugewiesen werden, da die Programmzeilen, in deren Namensfeldern sie definiert sind, noch nicht bearbeitet wurden. Solche Vorwärtsadreßbezüge sind der Grund dafür, daß nicht bereits beim ersten Durchgang für jede Programmzeile unmittelbar der Maschinencode erzeugt werden kann.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Im zweiten Durchgang werden die Programmzeilen nacheinander in den Maschinencode übersetzt, wobei die Zuordnungs- und die Symboltabelle ausgewertet werden. Aus den fest vereinbarten Registersymbolen RO bis R 7 werden die numerischen Adressen des Registerspeichers ermittelt. Sie ermöglichen die Unterscheidung von prozessorinternen und prozessorexternen Adressen und dürfen nicht zur symbolischen Kennzeichnung von Speicheradressen herangezogen werden. Daraus ergibt sich für unser Beispiel die Codierung der R/S-Bits und damit die Anzahl der Maschinencodewörter pro Befehl. Mit der Codeerzeugung wird gleichzeitig eine Programmliste erstellt, die neben dem symbolischen Programm (Quellprogramm) den Maschinencode (üblicherweise in Hexadezimaldarstellung), die Speicheradressen des Maschinencodes, eine Zeilennumerierung und Fehlerhinweise enthält (Bild 1.20).

Je nach Aufbau des Assemblers wird der erzeugte Maschinencode direkt in den durch die ORG-Anweisungen vorgegebenen Bereichen im Speicher erzeugt, oder er wird vom Assembler zunächst auf ein externes Speichermedium ausgegeben, von wo er dann – gegebenenfalls zu einem späteren Zeitpunkt – von einem Ladeprogramm (Lader) in den Arbeitsspeicher geladen wird. Der Lader lädt das Programm entweder an die durch die ORG-Anweisungen des Programms vorgegebenen Speicheradressen (Absolutlader), oder er nimmt eine Verschiebung des Programms um eine vorgebbare Ladedistanz vor (verschiebender Lader, relocating loader).

Hierzu müssen während des Ladevorgangs die Adreßan gaben in den Adreßteilen von Befehlen, die über den Zuordnungszähler ermittelt wurden, um die Ladedistanz erhöht werden. Diese Adressen werden als relative oder verschiebbare Adressen bezeichnet. Adressen, die über die EQU -Anweisung als Absolutwerte vorgegeben sind, bleiben unverändert; man bezeichnet sie auch als absolute oder feste Adressen. Um dem verschiebenden Lader diese Unterscheidung zu ermöglichen, muß der Assembler Zusatzinformationen zum Maschinencode liefern.

Ein Programm kann – wie in Kapitel 3 beschrieben ist – auch aus mehreren Teilen bestehen, die unabhängig voneinander assembliert werden. Um die zwischen den Programm teilen auftretenden Adreßquerbezüge aufzulösen, müssen die Programmteile vor oder während des Ladens zusammengefügt (gebunden) werden. Diese Aufgabe übernimmt ein Bindeprogramm (Binder, linkage editor) bzw. ein bindender Lader (linking loader). Die zur Herstellung der Adreßquerbezüge notwendige Information liefert ebenfalls der Assembler.

Häufig wird die Entwicklung von Programmen nicht auf dem zu programmierenden Mikroprozessorsystem selbst, sondern auf sogenannten Entwicklungssystemen oder herkömmlichen Universalrechenanlagen durchgeführt. Entwicklungssysterne sind Mikroprozessorsysteme, die mit Übersetzungs- und Testprogrammen für einen bestimmten Mikroprozessortyp ausgestattet sind. Im allgemeinen arbeiten sie mit demselben Prozessortyp, so daß der erzeugte Maschinencode sowohl auf dem Entwicklungssystem als auch auf dem zu programmierenden System ausführbar ist.

Arbeiten sie mit einem anderen Prozessortyp, so werden spezielle Übersetzungsprogramme benötigt, die Maschinencode für den zu programmierenden Mikroprozessor erzeugen. Das gilt allgemein für die Programmentwicklung mit Universalrechenanlagen. Man bezeichnet Übersetzer, die nicht für den Prozessor, auf dem sie laufen, sondern für andere Prozessoren Maschinencode erzeugen, als Cross-Assembler und Cross-Compiler oder allgemein als Cross-Software.

Programmeingabe und Textausgabe

Das symbolische Programm muß bei der Eingabe in eine Folge von ASCIIZeichen (byte string) umgesetzt werden, um vom Mikroprozessor verarbeitet werden zu können. Das geschieht durch das Eingabegerät, z.B. eine elektrische Schreibmaschine. Beim Drücken einer Taste wird das entsprechende ASCIIZeichen erzeugt und an das Mikroprozessorsystem übertragen. Bild 1.21 zeigt diesen Vorgang an unserem Programmbeispiel.

Einführung in die Assemblerprogrammierung: Programmdarstellung, Programmübersetzung, Assemblierung

Leerzeichen sind mit SP (space) bezeichnet. Das Ende einer Zeile ist mit dem Steuerzeichen Wagenrücklauf (carriage return CR), der Übergang auf die nächste Zeile mit dem Steuerzeichen Zeilenvorschub (line feed LF) angegeben. Jeweils zwei ASCII-Zeichen (zwei Bytes) können in einer 16-Bit-Speicherzelle oder einem 16-Bit-Register untergebracht werden. Ein symbolisches Programm, das vom Assembler als ASCIIZeichenfolge in den Speicher geladen wird, belegt somit wesentlich mehr Speicherzellen als das daraus erzeugte Maschinenprogramm.

Bei der Ausgabe von Text, z.B. bei der Ausgabe der Programmliste auf einem Drucker, überträgt der Prozessor einen Strom von ASCII-Zeichen an das Peripheriegerät. Dieser Zeichenstrom enthält neben dem eigentlichen Text ebenfalls Steuerzeichen, wie Wagenrücklauf, Zeilenvorschub und Seitenvorschub.

Like this post? Please share to your friends:
Schreibe einen Kommentar

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: