Assemblersprache

hardwarenahe Programmiersprache
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 3. November 2006 um 20:58 Uhr durch 80.109.120.194 (Diskussion). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Eine Assemblersprache ist eine spezielle Programmiersprache, welche die Maschinensprache einer spezifischen Prozessorarchitektur in einer für den Menschen lesbaren Form repräsentiert. Jede Computerarchitektur hat folglich ihre eigene Assemblersprache.

Ein Programm in Assemblersprache wird auch als Assemblercode bezeichnet. Es wird durch einen speziellen Compiler, ebenfalls Assembler genannt, in direkt ausführbare Maschinensprache (auch Maschinencode) umgewandelt. Die umgekehrte Umsetzung von Maschinencode in menschenlesbaren Assemblercode wird Disassemblierung genannt. Allerdings lassen sich dabei beiläufige Informationen wie Bezeichner und Kommentare nicht wiederherstellen, da diese bereits bei der Compilierung unwiederbringlich verloren gingen, was ein Verstehen des Programms erschwert.

Programme in Assemblersprache zeichnen sich dadurch aus, dass man die komplette Bandbreite des Computers ausnutzen und Hardwarechips direkt programmieren kann. Weil Assemblerprogramme faktisch auf Maschinencode-Ebene arbeiten, sind sie oftmals erheblich kleiner und schneller als Programme, die einen ähnlichen Grad an Komplexität aufweisen, aber eine Hochsprache als Basis haben. Der Compiler einer höheren Programmiersprache erzeugt meistens mehr Code, weil er stark generalisiert geschrieben werden muss. Der Abstand hat sich allerdings durch moderne optimierende Compiler stark verringert. Bei einigen speziellen Prozessoren wie Intel Itanium und verschiedenen DSPs kann der Compiler u.U. sogar besseren Code erzeugen, als ein durchschnittlicher Assemblerprogrammierer, da diese Architekturen ihre Leistung erst bei hochkomplexen Optimierungen entfalten, die vom Menschen in diesem Umfang nur schwer zu überblicken sind.

Die Nachteile von Assemblerprogrammen sind u. a. höhere Fehleranfälligkeit (durch erhöhte Komplexität), damit verbunden extrem großer Programmieraufwand bei umfangreichen Projekten und die (praktische) Unmöglichkeit des Portierens auf eine andere Hardware. Faktisch wird daher heute Assemblerprogrammierung nur noch eingesetzt, wenn Programme bzw. Teile davon sehr zeitkritisch sind (beispielsweise bei der Programmierung von Gerätetreibern für Grafikkarten) oder nur einen sehr geringen Speicherplatzbedarf aufweisen dürfen (z. B. in eingebetteten Systemen).

Was ist eine Assemblersprache?

Programmbefehle in Maschinensprache - Instruktionen - sind einfache Bitmuster, die sich aus den Opcodes und den zugehörigen Daten bilden. Da die Zahlenwerte der Opcodes schwieriger zu merken sind, verwendet eine Assemblersprache besser merkbare Kürzel, so genannte mnemonische Symbole (kurz Mnemonics - dieser Begriff wurde von Ada Lovelace eingeführt, die erste Person überhaupt, die Computerprogramme schrieb).

Beispiel:

Der folgende Befehl in der Maschinensprache von x86-Prozessoren

 10110000 01100001

entspricht dem Assemblerbefehl

 movb $0x61, %al    ; AT&T-Syntax (Zeichen nach einem „;“ gelten als Kommentare)

bzw.

 mov al, 61h        ; Intelschreibweise

und bedeutet, dass der hexadezimale Wert 61 (97 dezimal) ins Register ‚al‘ geladen werden soll (al: dabei steht das ‚a‘ für das Register, und das ‚l‘ für low, was praktisch dem ersten Teil des Registers entspricht). Der zweite Teil des Registers wird mit einem ‚h‘ ausgezeichnet, das ‚h‘ steht für High (Beispiel: ‚ah‘). Soll das ganze Register (je nach Speicherbedarfs des Typs, der gespeichert werden soll) angesprochen werden, wird ‚l‘ bzw. ‚h‘ durch ein ‚x‘ ersetzt: ‚ax‘). Mit Computerhilfe kann man das eine in das andere weitgehend eins zu eins übersetzen. Jedoch werden Adressumformungen vorgenommen, so dass man symbolische Adressen benutzen kann. Im Allgemeinen haben die Assembler neben den eigentlichen Codes auch Steueranweisungen, die die Programmierung bequemer machen, zum Beispiel zur Definition eines Basisregisters.

Häufig werden komplexere Assemblersprachen (Makroassembler) verwendet, um die Programmierarbeit zu erleichtern. Makros sind dabei Bruchstücke von Assemblercode, der vor dem eigentlichen Assemblieren automatisch an Stelle der Makroaufrufe eingefügt wird. Dabei können einfache Parameterersetzungen vorgenommen werden. Die Disassemblierung von derart generiertem Code ergibt allerdings den reinen Assemblercode ohne die beim Übersetzen expandierten Makros.

Verschiedene Assemblersprachen

Jede Computerarchitektur hat ihre eigene Maschinensprache und damit Assemblersprache. Vereinzelt existieren sogar mehrere verschiedene Assemblersprachen (und zugehörige Assembler) für die gleiche Prozessorarchitektur. Das Beispiel oben ist für den 80386. Die Sprachen unterscheiden sich in Anzahl und Typ der Operationen.

Jedoch haben alle Architekturen die folgenden grundlegenden Operationen:

  • Daten lesen und schreiben innerhalb des Zentralspeichers sowie Datenaustausch mit Registern
  • Einfache arithmetische Operationen (mit Gleitkommaprozessoren auch komplexe)
  • Einfache logische Operationen
  • Einfache Kontrolle des Programmflusses

Bestimmte Rechnerarchitekturen haben oft auch komplexere Befehle wie z. B.

  • Einen großen Speicherblock im Hauptspeicher verschieben.
  • Höhere Arithmetik wie Sinus-, Kosinus- und Wurzelberechnung (entweder über spezielle Zusatzprozessoren realisiert oder über Softwareroutinen)
  • Eine einfache Operation (z. B. Addition) auf einen Vektor von Werten anwenden.
  • Massive, direkte Parallelprogrammierbarkeit des Prozessors, etwa bei Signalprozessoren.
  • Unterbrechungssteuerungen, die besonders für Prozessrechner benötigt werden.
  • Aufrufe von Ein-/Ausgabegeräten
  • Synchronisation mit anderen Prozessoren für SMP-Systeme

Der Nutzen von Assemblern

Assemblerprogramme sind schneller als die Produkte der Hochsprachencompiler. Allerdings setzt dies jahrelange Erfahrung des Programmierers voraus. Ein Einsteiger wird einen langsameren Code schreiben und eher von den Compilaten eines guten Compilers lernen können als diesen zu übertreffen. Der Nutzen von Assembler liegt vielmehr in dem Verstehen, wie ein System arbeitet und funktioniert, um damit in Hochsprachen umständliche Konstrukte zu vermeiden. Auch heute noch wird an vielen Hochschulen Assembler gelehrt. Es geht darum, ein Gefühl für den Rechner und seine Arbeitsweise zu bekommen.

Bei vielen Anwendungen für Geräte, die von Mikrocontrollern gesteuert sind, war früher oft eine Programmierung in Assembler notwendig, um die knappen Ressourcen dieser Mikrocontroller optimal auszunutzen. Dies gilt heute praktisch nur noch, wenn wegen Massenproduktion möglichst günstige und damit minimale Mikrocontroller verwendet werden sollen und das Programm nicht zu komplex ist. Um Code für solche Mikrocontroller zu erzeugen, werden Cross-Assembler bei der Entwicklung eingesetzt. Moderne C-Compiler haben aber auch in diesem Bereich die Assembler abgelöst. Nicht zuletzt aufgrund größerer Programmspeicher bei gleichen Kosten für die Chips fallen die Vorteile von Hochsprachen gegenüber der - teils - verschwindend geringen - Vorteilen der Assemblersprache immer mehr ins Gewicht.

Früher wurden sogar ganze Betriebssysteme in einer Assemblersprache geschrieben. Heute wird jedoch C bevorzugt. Allerdings müssen häufig kleine Assemblerroutinen Hardware-nahe Aufgaben in Betriebssystemen übernehmen. Dazu gehört zum Beispiel das Speichern von Registern in Schedulern, oder bei der x86-Architektur der Teil des Boot-Loaders, der innerhalb des 512 Byte großen Master Boot Records untergebracht sein muss. Auch Teile von Gerätetreibern werden in Assemblersprache geschrieben, falls aus den Hochsprachen kein effizienter Hardware-Zugriff möglich ist. Manche Programmiersprachen wie C erlauben es, direkt im eigentlichen Quellcode Assemblercode, sogenannte Inline-Assembler-Routinen, einzubetten.

Es gibt Fälle, in denen diskrete Berechnungen einfacher und effizienter direkt in Assembler geschrieben werden. Die meisten Hochsprachencompiler übersetzen zuerst in Assemblercode oder können diesen optional ausgeben, so dass man, wenn man will, Details genauer betrachten und gewisse Stellen von Hand optimieren kann.

Bis ca. 1990 wurden die meisten Computerspiele in Assemblersprachen programmiert, da nur so auf Heimcomputern und den damaligen Spielkonsolen eine akzeptable Spielgeschwindigkeit und eine den kleinen Speicher dieser Systeme nicht sprengende Programmgröße zu erzielen war. Noch heute gehören Computerspiele zu den Programmen, bei denen am ehesten kleinere assemblersprachliche Programmteile zum Einsatz kommen, um so zum Beispiel die Prozessorerweiterungen wie SSE (Intel CPUs) zu nutzen.

Literatur

  • Reiner Backer: Assembler - Maschinennahes Programmieren von Anfang an. rororo Taschenbücher Nr.61224 (2003), ISBN 3499612240
Wikibooks: Assembler-Programmierung – Lern- und Lehrmaterialien