Assembler gleich interpreter

Aufbau und Prinzipien der Arbeitsweise des Rechners

1. Einführung

Von der Hochsprache zum Maschinencode

Programmiersprachen wie PASCAL, C++, Basic usw. sind dem Menschen angepasst, dass er die Anweisungen leicht verstehen kann. Diese sind oft komplex und können vom Prozessor nur über viele Einzelschritte erledigt werden. Die Anweisungen der Hochsprache sind dabei so gestaltet, dass sie sich am zu behandelnden Problem orientieren und Formulierungen dafür möglichst vereinfachen.
Mit diesen Formulierungen kann der Prozessor jedoch nichts anfangen. Daher ist es erforderlich, diese in eine für den Prozessor verständliche Form zu übersetzen. Diese Codes sind nun Folgen von 1 und 0 und wären für den Menschen wieder nur schwer verständlich. Daher gibt es für die Prozessorbefehle neben dem eigentlichen Code eine direkte Übersetzung in Mnemonics, Abkürzungen, welche die Bedeutung des Codes für den Menschen lesbar machen. Die Sprache auf dieser Ebene heißt Assembler.
Assemblerbefehle sind immer gleich. Sie sind entsprechen dem Befahlswortschatz des Prozessors. Anweisungen der Hochsprache können jedoch auf unterschiedliche Art in Assembleranweisungen übersetzt werden. Hierin unterscheiden sich die verschiedenen Interpreter und Compiler, die diese Aufgabe ausführen.

Im Folgenden soll der Weg untersucht werden, wie ein Programm, das von einem Compiler/Interpreter in Maschinencode übertragen wurde, schließlich vom Prozessor abgearbeitet wird. Wir benutzen den PASCAL-Compiler von Delphi und untersuchen den Maschinen- bzw. Assemblercode, den der Compiler erzeugt. In Delphi kann hierfür das sogenannte CPU-Fenster geöffnet werden, das uns einen Einblick in die Prozessorregister gibt und den Maschinencode mit Assemblermnemonics anzeigt.

[Index] [Untersuchung einfacher Programmstrukturen]

Autor: Jürgen Dehmer
Letzte �nderung: 28.02.2004

26. Sep 2007 22:20

Ich denke in den Wikipediaartikeln stehts eigentlich recht sch�n drin was was ist.

Assembler ist erstmal ein Programm und nicht ein Code oder eine Sprache. Aber selbst wenn er eigentlich einen Code in Assemblersprache auf der einen und das was aus dem Assembler rauskommt auf der anderen Seite meint, dann ist das eine eine menschenlesbare Textdatei und das andere ein bin�res vom Prozessor ausf�hrbares Programm.

Maschinencode hat auch nichts mit Bytecode zu tun. Maschinencode besteht aus einzelnen Anweisungen, die der Prozessor direkt ausf�hrt. Bytecode sind Anweisungen, die eine VM, also Software in Maschinencode �bersetzt.

Da sich die Simulation der Registermaschine am von-Neumann-Rechner orientiert, liegen Befehle und Daten im Arbeitsspeicher. Sie sind also binär codiert, also als Folge von Nullen oder Einsen abgelegt. Diese Darstellung ist für den Menschen nicht lesbar, die Maschine kann diese Zeichenfolgen aber interpretieren. Diese Interpretation basiert auf der sogenannten Maschinensprache.

Die Maschinensprache bezeichnet ein System von Instruktionen, die der jeweilige Prozessor direkt ausführen kann. Vereinfacht gesagt sind der Umfang und die Ausprägung des Instruktionssatzes von den im Prozessor umgesetzten Schaltnetzen abhängig. Jede zur Verfügung stehende Instruktion ist durch eine festgelegte Bitfolge von Nullen und Einsen ansprechbar. Diese legt den Einsprungspunkt im Mikroprogrammspeicher fest.

Zum besseren Umgang mit der Maschinensprache werden die zulässigen Bitfolgen, die Maschinenbefehle, Adressen usw. mit symbolischen, für den Menschen verständlichen Namen belegt. Diese Befehle werden Assemblerbefehle genannt; sie ermöglichen die Implementierung von Assemblerprogrammen.

Mit einem speziellen prozessorspezifischen Werkzeug, dem Assembler, kann ein Assemblerprogramm dann meist nahezu eins zu eins in ein entsprechendes Maschinenprogramm umgewandelt werden.

Beispiel:

AssemblerbefehlMaschinencodeOperationOperand
MOV AX,[20] 00010000 00100000
SUB AX,[21] 00110100 00100001
JS 12 01100101 00010010
DEC AX 01011110 00000000

In Assembler oder gar Maschinensprache programmiert heute fast niemand mehr, da diese Programme sehr unübersichtlich und sehr schlecht auf anderen Computertypen übertragbar sind. Lediglich bei sehr zeitkritischen Anwendungen kann dies noch sinnvoll sein. Daher verwenden wir Hochsprachen, die rechnerunabhängiger sind, wesentlich komplexere Befehle und Möglichkeiten zur Strukturierung des Programmcodes besitzen (z.B. Objektorientierte Programmierung).

Um das Programm auf einem Computer ausführen zu können, muss es aber in Maschinensprache übersetzt werden. Diese Aufgabe haben Compiler und Interpreter. Ein Compiler übersetzt das Programm, sobald der Quelltext fertiggestellt ist. Danach liegt ein ausführbares Programm (bei Windows: z.B. exe-File) vor, das Maschinenbefehle enthält. Ein Interpreter übersetzt das Programm erst dann, wenn es benötigt wird. Im Extremfall wird jeder einzelne Befehl erst übersetzt und dann ausgeführt. Dies verlangsamt die Ausführung erheblich.

Sprachen mit Compiler sind beispielsweise Delphi und C++. Bekannte Interpretersprachen sind Basic oder PHP. Java stellt eine Zwischenlösung dar. Java-Programme werden von einem Compiler in Java-Byte-Code übersetzt (class- bzw. jar-File). Dieser wird dann bei der Ausführung von einem Interpreter (Java Virtual Machine = Java Runtime Enviroment JRE) in Maschinenbefehle übersetzt und ausgeführt. Da Java-Programme erst zur Laufzeit in Maschinenbefehle übersetzt werden, sind sie im Prinzip auf jedem Rechner lauffähig für den es die JRE gibt. Dafür sind sie aber langsamer als exe-Files.

Da die Registermaschine in Bezug auf die Berechenbarkeit genauso mächtig ist, wie ein realer Computer, müssen sich Programme in einer Hochsprache in Programme für eine Registermaschine übersetzen lassen. Für einen konkreten Simulator ist das aufgrund von Beschränkung des Speicherplatzes und der Registerbreite zumindest eingeschränkt möglich.

Variablen:

Variablen werden im RAM gespeichert. Bei der Deklaration der Variable muss festgelegt werden, an welcher Stelle im RAM sie gespeichert wird. Jeder Zugriff auf eine Variable wird dann in eine Assembleranweisung mit Adressangabe (z.B. MOV AX, [30], wenn die Variable an RAM-Stelle 30 steht) übersetzt. Arrays lassen sich dadurch realisieren, dass die Adresse des ersten Arrayelements in ein Register (z.B. BX) übertragen, der Index des benötigten Elements dazu addiert und dann ein Befehl wie MOV AX,[BX] verwendet wird.

Zuweisungen:

Um Variablen neue Werte zuzuweisen, müssen die neuen Werte zunächst in einem Register (meist AX) vorliegen. Dort werden gegebenenfalls auch Berechnungen durchgeführt. Danach wird das Ergebnis an die Speicherstelle der Variablen im Arbeitsspeicher übertragen.

Sollen komplexe Terme berechnet werden, ist es sinnvoll mit einem Stack zu arbeiten. Ein Stack ist ein Speicherbereich, für den es im Wesentlichen zwei Operationen gibt: PUSH legt eine neue Zahl auf den Stapel, POP holt die oberste Zahl vom Stapel und entfernt sie dort. Wandelt man einen Term in die Postfix-Notation um, lässt er sich gut mit einem Stapel berechnen. Die Berechnung erfolgt streng von links nach rechts, da man ohne Klammern auskommt und auch Punkt vor Strich schon berücksichtigt ist.

z.B. aus 3 * 5 + (3 – 2) wird 3 5 * 3 2 - +

Die Operanden werden zunächst auf den Stapel gelegt und immer wenn eine Rechenoperation durchgeführt werden soll, werden die obersten beiden Zahlen dem Stapel entnommen, damit die Berechnung durchgeführt und das Ergebnis wieder auf dem Stapel abgelegt.

z.B. 3 und 5 werden auf den Stapel gelegt, dann die Multiplikation ausgeführt und 15 wieder auf den Stapel gelegt. Dazu kommen 3 und 2 auf den Stapel. Danach wird die Subtraktion mit 3 und 2 ausgeführt und das Ergebnis 1 auf den Stapel gelegt. Am Ende werden die 15 und die 1 vom Stapel entnommen, addiert und das Ergebnis 16 auf den Stapel gelegt.

Entscheidungen:

Der Vergleich zweier Variablen/Werte wird durch eine Subtraktion realisiert. Je nachdem, ob das Ergebnis positiv, negativ oder gleich Null ist, kann entschieden werden, welche Zahl größer war. Durch einen bedingten Sprung wird dann ggf. der IF-Teil übersprungen. Falls es einen ELSE-Teil gibt, muss dieser nach der Ausführung des IF-Teils übersprungen werden.

Bedingungen mit „größer“ oder „kleiner“ werden dabei mit einem JS, bzw. JNS realisiert, Bedingungen mit „gleich“ durch ein JZ oder JNZ. „größer-gleich“ bzw. „kleiner-gleich“ werden in „nicht kleiner“ oder „nicht größer“ umgewandelt.

Schleifen:

While-Schleifen lassen sich ähnlich wie Entscheidungen realisieren. Zunächst wird die While-Bedingung überprüft. Ist sie nicht erfüllt, erfolgt ein (bedingter) Sprung hinter die Schleife. Andernfalls werden die Anweisungen, die wiederholt werden sollen, ausgeführt. Danach erfolgt ein unbedingter Sprung zur erneuten Überprüfung der Bedingungen.

For-Schleifen lassen sich in While-Schleifen umschreiben. Dazu muss der Initialisierungsteil als Anweisung vor der Schleife ausgeführt werden. Die Erhöhung der Zählvariablen wird vor dem unbedingten Sprung durchgeführt.

Damit ist der Weg vom Mikroprozessor zur Hochsprache, bzw. umgekehrt vollständig. Ein Computerprogramm einer Hochsprache wird von einem automatischen Compiler in Maschinencode übersetzt. Dieser wird von einem Computer mit von-Neumann Architektur verarbeitet, indem Befehl für Befehl aus dem Arbeitsspeicher geladen und mit Hilfe des Mikroprogrammspeichers in Steuersignale für den Prozessor übersetzt wird. Der Prozessor besteht aus elektronischen Schaltnetzen. Durch Aktivierung der Steuersignale wird das richtige Schaltnetz ausgewählt und die Berechnung durchgeführt. Die Schaltnetze sind aus einfachen logischen Bauelementen aufgebaut, die ihrerseits aus wenigen CMOS-Feldeffekttransistoren bestehen. Ein Programm in einer Hochsprache wird im Endeffekt durch elektrische Signale in einer Vielzahl von Transistoren ausgeführt.

Hintergrundinformationen: Herunterladen [odt][4 MB]

Weiter zu Literaturhinweise

Was ist der Unterschied zwischen einem Interpreter und einem Compiler?

Definition „Compiler, Interpreter, Compreter“ Der Unterschied von Compiler und Interpreter. Von allein kann ein Computer die Anweisungen des Programmierers nicht verstehen. Sie müssen entweder per Compiler in Maschinensprache übersetzt werden oder per Interpreter zur Laufzeit verarbeitet werden.

Wird Assembler kompiliert?

Häufig besteht das Ergebnis eines Compilers aus Assemblercode, der erst in einem nachgelagerten Assemblerlauf in Maschinencode übersetzt wird. Dieser zweite Lauf ist bei vielen Compilern transparent, weil der Assembler oftmals direkt in den Compiler integriert ist.

Ist Assembler schwer?

Assembler ist leicht erlernbar Es stimmt nicht, dass Assembler komplizierter und schwerer erlernbar ist als Hochsprachen. Das Erlernen einer einzigen Assemblersprache macht Sie mit den wichtigsten Grundkonzepten vertraut, das Erlernen von anderen Assembler-Dialekten ist dann ein Leichtes.

Welche Interpreter gibt es?

Bekannte Programmiersprachen, die üblicherweise in Bytecode übersetzt werden, sind Java, C#, Perl und Python. Für manche Sprachen (etwa Smalltalk) gibt es je nach Anbieter Interpreter, Bytecode-Interpreter, JIT-Compiler oder Compiler in andere Sprachen (beispielsweise nach C oder für . NET-Plattformen).

Toplist

Neuester Beitrag

Stichworte