Assembler (80x86 Prozessor)-Programmierung: Einleitung
Aus Wikibooks
[Bearbeiten] Wofür eignet sich Assembler?
Maschinensprache ist die Sprache, die der Prozessor versteht. Ein Programm in Maschinensprache besteht im Grunde nur aus Dualzahlen.
Als Beispielprogramm sehen Sie die ersten 8 Byte des 1. Sektors jeder Festplatte, den Beginn des so genannten "Urladeprogramms". In der ersten Spalte steht die Speicheradresse in hexadezimaler Schreibweise, in der zweiten Spalte der Inhalt des Speicherplatzes, und zwar in binärer Darstellung:
0000 11111010 0001 00110011 0002 11000000 0003 01001110 0004 11010000 0005 00000000 0006 01111100 0007 10001101
Diese Programmdarstellung ist sehr unpraktisch. Die Kolonnen von Einsen und Nullen sind unübersichtlich, sehr aufwändig zu schreiben, sind fehleranfällig bei der Eingabe und nehmen unverhältnismäßig viel Platz auf dem Papier ein. Deshalb ist es üblich, die gleichen Daten in hexadezimaler Darstellung aufzulisten. Sie sehen hier vom gleichen Programm den Anfang, und zwar nicht nur acht Byte, sondern die ersten 32 Byte:
0000 FA 33 C0 8E D0 BC 00 7C 8B F4 50 07 50 1F FB FC 0010 BF 00 06 B9 00 01 F2 A5 EA 1D 06 00 00 BE BE 07
Weil die hexadezimale Schreibweise kompakter ist, hat es sich eingebürgert, wie in diesem Beispiel in jede Zeile den Inhalt von 16 Speicherplätzen zu schreiben. Die Spalte links zeigt daher die Nummern jedes 16ten Speicherplatzes, und zwar in hexadezimaler Darstellung. Hexadezimal 0010 ist der 17te Speicherplatz; er ist in diesem Beispiel mit BF gefüllt.
Ein Programm in diesem Code zu verstehen oder gar zu entwerfen, ist selbst für Spezialisten extrem schwierig. Der Grund hierfür ist, dass der Rechner nur Einsen und Nullen versteht. Zudem kann ein Befehl aus einem oder auch fünf Byte bestehen, und man sieht es den Bytefolgen nicht an, ob es sich um Programmcode oder Daten handelt - es gibt keinen erkennbaren Unterschied zwischen Programm- und Datencode.
Um die Programmierung von Computern zu vereinfachen, kam bald die Idee auf, Abkürzungen für die Gruppen von Einsen und Nullen zu verwenden, aus denen die Maschinensprache besteht. Die Übersetzung der Abkürzungen in Einsen und Nullen kann der Computer selbst mit einem darauf spezialisierten Übersetzungsprogramm erledigen. Solche Programme heißen Assembler.
Der erste Befehl "FA" des Beispielprogramms oben ist "clear interrupt" , auf deutsch "Unterbrechungsleitungen zeitweilig sperren". Aus den Anfangsbuchstaben wird die Abkürzung cli gebildet, die für einen Programmierer leichter zu merken ist als das hexadezimale FA bzw. das binäre 11111010. Solche Abkürzungen werden Mnemonische Bezeichnungen, kurz: Mnemonics, genannt.
Wie kommen diese Abkürzungen zustande? Der Hersteller des Prozessors, z. B. Intel, liefert zu jedem neuen Prozessor ein umfangreiches Handbuch mit. Darin ist für jeden Befehl ein langer Name, ein Kurzname (Mnemonic) und eine Beschreibung des Befehls aufgeführt. Die Mnemonics sind möglichst kurz, um den Schreibaufwand beim Programmieren gering zu halten. So wird beispielsweise das englische Wort "move" (bewegen, transportieren) immer zu mov verkürzt und subtract (subtrahieren) zu sub .
Mit Mnemonics geschrieben sieht das Beispiel von oben so aus:
0000 FA CLI 0001 33C0 XOR AX,AX 0003 8ED0 MOV SS,AX 0005 BC007C MOV SP,7C00 0008 8BF4 MOV SI,SP 000A 50 PUSH AX 000B 07 POP ES 000C 50 PUSH AX 000D 1F POP DS 000E FB STI 000F FC CLD 0010 BF0006 MOV DI,0600 0013 B90001 MOV CX,0100 0016 F2 REPNZ 0017 A5 MOVSW 0018 EA1D060000 JMP 0000:061D 001D BEBE07 MOV SI,07BE
Der Befehl cli bedeutet wie gesagt "Clear Interrupt" (Interrupts sperren), der darauf folgende xor bedeutet "exclusives oder" (eine Logikfunktion) und bewirkt in diesem Beispiel das Löschen des AX-Registers. mov ss, ax heißt "Kopiere Inhalt des Registers AX nach Register SS".
Als Register bezeichnet man die wenigen Speicherplätze, die im Prozessorkern eingebaut sind. Sie werden später erläutert.
Das vollständige Urladerprogramm finden Sie hier
Moderne Assembler-Programme erleichtern dem Programmierer die Arbeit noch weiter: Sie verstehen nämlich auch symbolische Bezeichnungen, Kommentare und Organisationsanweisungen. Der Programmierer kann sie verwenden, um den Überblick im Programmcode zu erleichtern. Unser Beispiel könnte damit so aussehen:
ORG 07C00 ; Startadresse des Programms festlegen AA_MBR SEGMENT CODE STRT: CLI ; alle INTR sperren XOR AX,AX MOV SS,AX MOV SP,Segment AA_MBR ; Sektor von 07C00 umkopieren ; Sektor mit 512 Byte ab 07C00h umkopieren nach 00600h MOV SI,SP ... JMP STEP2 ; Sprung nach 0000:061D ; Partitionstabelle wird nach aktiver Partition abgesucht ORG 0000:0600 STEP2: MOV SI,OFFSET PAR_TAB ... AA_MBR ENDS END
Die ORG-Anweisung ganz am Anfang legt fest, ab welcher Speicheradresse das Programm während der späteren Ausführung im Arbeitsspeicher untergebracht werden soll. AA_MBR, STRT und STEP2 sind Sprungmarken, AA_MBR ist außerdem der Programmname. PAR_TAB ist die symbolische Bezeichnung der Adresse des Beginns der Partitionstabelle. Die Namen der Sprungmarken, des Programmnamens und der Adressbezeichnung hier wählt der Programmierer nach Belieben; sie könnten auch anders aussehen als in diesem Beispiel.
Das klingt kompliziert (und ist es auch). Aber wir werden im Laufe des Buches natürlich noch genauer auf die Bestandteile des Programms eingehen.
Ein Programm, das einen solchen Quelltext in Maschinensprache übersetzen kann, nennt man wie gesagt Assemblerprogramm. Das englische Wort "to assemble" bedeutet "zusammenfügen, zusammenbauen".
Assemblersprachen zählen zur zweiten Generation der Programmiersprachen. Ihr Nachteil: Eine Assemblersprache gilt für die Prozessoren einer Prozessorfamilie, für eine andere Familie kann sie anders lauten und der Programmierer muss umlernen.
Zur dritten Generation der Programmiersprachen zählen so genannte höhere Programmiersprachen wie Java, C#, Delphi, Visual Basic, C oder C++ u.a. Diese Sprachen sind leichter verständlich und nicht mehr auf ein bestimmtes Computersystem beschränkt: Das Programm kann man auf einem PC, einem Macintosh oder einem Cray-Supercomputer ausführen.
Angesichts der Begrenzung von Assemblersprachen auf eine Prozessorfamilie und der Unverständlichkeit des Codes liegt die Frage nahe: Wofür verwendet man heute noch Assemblersprache, wo es doch komfortable Hochsprachen wie Java, C# und andere gibt?
Einige Gründe für die Verwendung von Assemblersprache:
- Treiber werden weitgehend in Maschinensprache geschrieben.
- Anspruchsvolle Programme (z.B. Spiele) werden meist erst in einer Hochsprache geschrieben - und laufen unzumutbar langsam. Ersetzt man später einige kritische (am häufigsten durchlaufene) Programmteile durch Maschinensprache, wird das gesamte Programm wesentlich schneller. Ersetzt man nur ein Prozent des Programms an den richtigen Stellen durch Assemblercode, läuft das Programm 10- bis 100-mal schneller!
- Jeder Compiler erzeugt auch aus einer Hochsprache ohnehin letztlich ein Programm in Maschinensprache.
- Bei industriellen Steuerungen mit Echtzeit-Anforderungen, d.h. die sofort reagieren müssen, ist die Verwendung von Maschinensprache häufig notwendig.
- Beschäftigung mit Maschinensprache erfordert und fördert zugleich das Verständnis für das Funktionieren der Hardware.
Im Gegensatz zur weit verbreiteten Meinung ist ein Betriebssystem nicht in Assembler, sondern weitgehend in einer Hochsprache geschrieben. Das liegt daran, dass der direkte Zugriff auf die Hardware nur einen geringen Teil des Betriebssystems ausmacht und in der Regel von Treibern erledigt wird. Die Aufgabe eines Betriebssystems liegt weniger in Hardwarezugriffen als vielmehr im Management der Ressourcen wie beispielsweise Management der Prozessorzeit für die Prozesse (Scheduling), Ein- / Ausgabe von Dateien und der Speicherverwaltung (Speichermanagement).
Die weitgehende Verwendung einer Hochsprache führt dazu, dass ein Kernel wie Linux (das hauptsächlich in der Hochsprache C geschrieben wurde) leicht auf viele Systeme portiert werden kann, dh. so angepasst, dass es auf anderen Computersystemen läuft. Es müssen "nur" die Anteile des Assemblercodes passend für die andere Prozessorfamilie umgeschrieben und der Hochsprachenanteil vom Compiler neu übersetzt werden.
[Bearbeiten] Was benötige ich für die Assemblerprogrammierung?
Wenn Sie ein Assemblerprogramm für einen modernen 80x86-Prozessor erstellen möchten, benötigen Sie einen aktuellen "Assembler" mit dem Sie die Beispielprogramme dieses Buchs in Maschinencode "assemblieren" können. Das Buch ist passend zu dem frei erhältlichen und als Open Source herausgegebenen Assemblerprogramm "NASM" geschrieben. Sie können es kostenlos herunterladen:
Weitere Informationen findet man unter anderem hier:
- LinuxAssembly
- The Art of Assembly
- Die Intel Pentium4-Prozessor-Dokumentation
- USENET - comp.lang.asm.x86
[Bearbeiten] Welche Vorkenntnisse werden benötigt?
Assembler als Erstsprache zu erlernen ist nicht empfehlenswert. Deshalb sollten Sie bereits Grundkenntnisse in einer anderen Programmiersprache besitzen. Es wird hier nicht mehr erklärt, was eine Schleife oder eine Auswahlanweisung ist. Außerdem sollten Sie den Umgang mit Linux und Windows beherrschen.

