Assembler (80x86 Prozessor)-Programmierung: Einleitung

Aus Wikibooks

Wechseln zu: Navigation, Suche
Nuvola apps bookcase.svg Assembler (80x86 Prozessor)-Programmierung


[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   10001110
 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.

Abb. 1 - Übersetzung eines Assemblerprogramms in Maschinensprache

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 Heimcomputer, einem Cray-Supercomputer oder gar auf manchen Smartphones 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:

  1. Treiber werden weitgehend in Maschinensprache geschrieben.
  2. 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!
  3. Jeder Compiler erzeugt auch aus einer Hochsprache ohnehin letztlich ein Programm in Maschinensprache.
  4. Bei industriellen Steuerungen mit Echtzeit-Anforderungen, d.h. die sofort reagieren müssen, ist die Verwendung von Maschinensprache häufig notwendig.
  5. 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:

[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.

One wikibook.svg Hoch zu Assembler (80x86 Prozessor)-Programmierung | Vor zu Grundlagen Wikibooks buchseite.svg


Persönliche Werkzeuge