Assembler

Assembler
Illustratives Bild des Artikels Assembler
Datum der ersten Version 1949
Dateierweiterung asm und s

Eine Montag Sprache oder Montag Sprache ist, in der Computerprogrammierung , die unterste Ebene der Sprache , der darstellt , in Maschinensprache in einer für Menschen lesbare Form. Die Kombinationen von Maschinensprache Bits durch so genannte „dargestellt werden mnemonic “  Symbole  , das ist leicht zu sagen , sich zu erinnern. Das Assembly-Programm konvertiert diese Mnemonik in Maschinensprache sowie die Werte (in Dezimalzahlen) in Binär- und die Ortsbezeichnungen in Adressen, um beispielsweise eine Objektdatei oder eine ausführbare Datei zu erstellen .

In der gegenwärtigen Praxis wird derselbe Begriff Assembler sowohl zur Bezeichnung der Assemblersprache als auch des Assemblerprogramms verwendet, das sie übersetzt. Wir sprechen daher von „Programmierung im Assembler“.

Die ein für alle Mal von vielen Interpreten durchgeführte Übersetzung jedes Variablennamens, der in einer (erweiterten) Anweisung durch die zugehörige Speicherposition und jeder Konstante (vom Benutzer in Dezimalzahl geschrieben) in Binärform angetroffen wird, ist typisch für eine d-Operation Name Assembler wird in diesem speziellen Fall nicht häufig verwendet.

Geschichte

Die Programme von EDSAC (1949), dem ersten Computer mit aufgezeichneten Programmen , wurden unter Verwendung einer alphabetischen Mnemonik von einem Buchstaben für jede Anweisung geschrieben. Die Übersetzung wurde dann von den Programmierern von Hand durchgeführt, eine lange, mühsame und fehleranfällige Operation.

Das erste Montageprogramm wurde 1954 von Nathaniel Rochester für den IBM 701 (den ersten von IBM veröffentlichten Computer ) geschrieben.

Assemblersprachen haben viele der Fehler beseitigt, die Programmierer der ersten Computergeneration gemacht haben, indem sie auf die Notwendigkeit verzichtet haben, sich die numerischen Codes von Anweisungen zu merken und Adressberechnungen durchzuführen. Die Baugruppenprogrammierung wurde dann verwendet, um alle Arten von Programmen zu schreiben.

In den 1970er und 1980er Jahren wurde die Verwendung von Assembler zum Schreiben von Anwendungen weitgehend durch die Verwendung von Programmiersprachen auf hoher Ebene ersetzt: Fortran , COBOL , PL / I usw. : Die Leistung der Maschinen ermöglichte es, und ein paar Minuten Computerzeit für eine Kompilierung aufzuwenden, um ein paar Stunden Programmierzeit zu sparen, war eine rentable Operation, selbst wenn die damaligen Compiler weniger effizienten Code bereitstellten (größer und oft langsamer). Darüber hinaus ermöglichten diese Hochsprachen die Überwindung der Abhängigkeit von einer einzelnen Hardwarearchitektur.

Die Betriebssysteme wurden bis zur Einführung von MCP in Burroughs im Jahr 1961 in Assemblersprache geschrieben , die in ESPOL, einem Dialekt von Algol, geschrieben wurde .

Der Assembler ist etwas zugunsten der ersten Mikrocomputer zurückgekehrt, bei denen die technischen Eigenschaften (reduzierte Speichergröße, geringe Rechenleistung, spezifische Speicherarchitektur usw.) starke Einschränkungen auferlegten, zu denen ein wichtiger psychologischer Faktor, die "Hobby" -Haltung, hinzukommt der ersten Benutzer von Mikrocomputern, die mit der Langsamkeit der Programme, die mit dem interpretierten BASIC geschrieben wurden, das im Allgemeinen mit dem Computer geliefert wurde, nicht zufrieden waren .

Große Programme wurden vollständig in Assembler für Mikrocomputer geschrieben, wie das DOS- Betriebssystem des IBM-PCs (ca. 4000 Codezeilen) und die Lotus 1-2-3- Tabelle (sein Rivale Multiplan, der bereits unter CP / M existierte) geschrieben in C ). In den 1990er Jahren war dies auch bei den meisten Spielen für Videokonsolen der Fall (zum Beispiel beim Mega Drive oder beim Super Nintendo ).

Besonderheiten des Monteurs

Eine bestimmte Sprache für jeden Prozessor

Die Maschinensprache ist die einzige Sprache, die ein Prozessor ausführen kann. Jede Prozessorfamilie verwendet jedoch einen anderen Befehlssatz .

Beispielsweise erkennt ein Prozessor der x86- Familie eine Anweisung des Typs:

10110000 01100001

In der Assemblersprache wird diese Anweisung durch ein Äquivalent dargestellt, das für den Programmierer leichter zu verstehen ist:

movb $0x61,%al

(10110000 = movb% al
01100001 = $ 0x61)

Dies bedeutet: "Schreiben Sie die Zahl 97 (der Wert wird hexadezimal angegeben  : 61 16 = 97 10 ) in das AL- Register ".

Somit ist die Assemblersprache, eine exakte Darstellung der Maschinensprache, für jede Prozessorarchitektur spezifisch . Darüber hinaus können für einen einzelnen Befehlssatz mehrere Gruppen von Mnemonik- oder Assemblersprachen-Syntaxen vorhanden sein, wodurch Makrobefehle erstellt werden .

Demontage

Die Umwandlung von Assembler-Code in Maschinensprache erfolgt durch ein Programm, das als Assembler-Programm bezeichnet wird . Die umgekehrte Operation , nämlich das Finden des Assemblers, der einem Maschinencode entspricht, hat einen Namen: Es handelt sich um eine Demontage .

Im Gegensatz zu dem, was man denken könnte, gibt es nicht immer eine Eins-zu-Eins-Entsprechung (eine Bijektion ) zwischen Assembler-Code und Maschinensprache. Bei einigen Prozessoren kann die Demontage daher zu Code führen, der für einen Menschen sehr schwer zu verstehen ist, während er von einem Computer perfekt kompiliert werden kann. Die Unmöglichkeit einer Demontage kann verschiedene Gründe haben: Verwendung von selbstmodifizierendem Code, Anweisungen variabler Größe, Unmöglichkeit der Unterscheidung zwischen Code und Daten usw. ( undurchdringlicher Code )

Darüber hinaus gehen viele Elemente im Assembler-Code bei der Übersetzung in die Maschinensprache verloren. Beim Erstellen von Code in Assembler kann der Programmierer Positionen im Speicher Namen zuweisen, seinen Code kommentieren , Makroanweisungen verwenden oder Code verwenden, der unter Bedingungen zum Zeitpunkt der Assemblierung generiert wurde. Alle diese Elemente werden bei der Montage auf das für die Maschine unbedingt Notwendige reduziert und erscheinen daher bei der Demontage nicht deutlich: Eine Position im Speicher wird beispielsweise nur durch ihre numerische Adresse oder durch einen Versatz gekennzeichnet .

Maschinenanweisungen

Einige grundlegende Operationen sind in den meisten Befehlssätzen verfügbar.

  • Verschiebung im Gedächtnis:
    • Laden eines Wertes in ein Register;
    • Verschieben eines Wertes von einem Speicherort in ein Register und umgekehrt;
  • Berechnung:
    • Addition oder Subtraktion der Werte von zwei Registern und Laden des Ergebnisses in ein Register;
    • Kombination von Werten aus zwei Registern nach einer Booleschen Operation (oder einer bitweisen Operation);
  • Änderung des Programmablaufs:
    • zu einer anderen Stelle im Programm springen (normalerweise werden die Anweisungen nacheinander nacheinander ausgeführt);
    • zu einem anderen Ort springen, aber nachdem Sie den Ort der nächsten Anweisung gespeichert haben, damit Sie dorthin zurückkehren können (Rückgabepunkt);
    • Rückkehr zum letzten Rückkehrpunkt;
  • Vergleich:
    • Vergleichen Sie die Werte zweier Register.

Und es gibt spezielle Anweisungen mit einer oder mehreren Anweisungen für Operationen, die viel hätten dauern müssen. Beispiele:

Assembler-Richtlinien

Neben der Codierung von Maschinenanweisungen verfügen Assemblersprachen über zusätzliche Anweisungen zum Zusammenstellen von Datenblöcken und zum Zuweisen von Adressen zu Anweisungen durch Definieren von Tags oder Beschriftungen.

Sie können symbolische Ausdrücke definieren, die in jeder Assembly ausgewertet werden, wodurch der Code noch einfacher zu lesen und zu verstehen ist.

Sie verfügen normalerweise über eine integrierte Makrosprache , um die Generierung komplexer Codes oder Datenblöcke zu erleichtern.

Einfache Beispiele

Hier einige einfache Beispiele:

$ gcc foo.S -c -o foo.o $ ld foo.o -o foo $ ./foo

Hallo anzeigen

(Kommentare sind nach Semikolons)

str: .ascii "Bonjour\n" .global _start _start: movl $4, %eax movl $1, %ebx movl $str, %ecx movl $8, %edx int $0x80 movl $1, %eax movl $0, %ebx int $0x80 ;Compilation: ;as code.s -o code.o ;ld code.o -o code ;Execution: ;./code

Lesen Sie die Tastatur (maximal 16 Zeichen) und zeigen Sie sie an

# define N 16 .global _start .comm BUFF , N _start: mov $3 , %eax mov $0 , %ebx mov $BUFF , %ecx mov $N , %edx int $0x80 mov %eax , %edx mov $4 , %eax mov $1 , %ebx mov $BUFF , %ecx int $0x80 mov $1 , %eax mov $0 , %ebx int $0x80

Einfache Beispiele, Intel x86- Syntax

Hier sind die gleichen Beispiele mit einigen Unterschieden:

  • in Intel x86- Syntax , geschrieben für den NASM- Assembler  ;
  • Verwenden des i386- Befehlssatzes  ;
  • wie folgt zu verwenden:
$ nasm -f elf foo.asm $ ld -o foo foo.o -melf_i386 $ ./foo

Show Guten Abend

(Kommentare sind nach Semikolons)

section .data ; Variables initialisées Buffer: db 'Bonsoir', 10 ; En ascii, 10 = '\n'. La virgule sert à concaténer les chaines BufferSize: equ $-Buffer ; Taille de la chaine section .text ; Le code source est écrit dans cette section global _start ; Définition de l'entrée du programme _start: ; Entrée du programme mov eax, 4 ; Appel de sys_write mov ebx, 1 ; Sortie standard STDOUT mov ecx, Buffer ; Chaine à afficher mov edx, BufferSize ; Taille de la chaine int 80h ; Interruption du kernel mov eax, 1 ; Appel de sys_exit mov ebx, 0 ; Code de retour int 80h ; Interruption du kernel


Lesen Sie die Tastatur (maximal 64 Zeichen) und zeigen Sie sie an

section .bss ; Section des variables non-initialisees Buffer: resb 64 ; Reservation de 64 blocs (octets ?) memoire pour la variable où sera stockee l'entree de l'utilisateur BufferSize: equ $-Buffer ; taille de cette variable section .text ; Section du code source global _start _start: ; Entree du programme mov eax, 3 ; Appel de sys_read mov ebx, 0 ; Entree standard STDIN mov ecx, Buffer ; Stockage de l'entree de l'utilisateur mov edx, BufferSize ; Taille maximale int 80h ; Interruption du kernel mov eax, 4 ; Appel de sys_write mov ebx, 1 ; Sortie standard STDOUT mov ecx, Buffer ; Chaine à afficher mov edx, BufferSize ; Taille de la chaine int 80h ; Interruption du kernel mov eax, 1 ; Appel de sys_exit mov ebx, 0 ; Code de retour int 80h ; Interruption du kernel

Verwendung der Assemblersprache

Es gibt Debatten über die Nützlichkeit der Assemblersprache. In vielen Fällen Compiler - Optimierer können verwandeln Hochsprache in Code, läuft so effizient wie Assembler - Code handschriftlich durch einen sehr guten Programmierer, während immer noch viel einfacher, schneller zu sein (und damit weniger effizient) teuer) zu schreiben, lesen und pflegen. .

Effizienz war bereits in den 1950er Jahren ein Problem. Eine Spur davon finden wir im Fortran-Sprachhandbuch (veröffentlicht 1956) für den IBM 704- Computer  : Von Fortran erstellte Objektprogramme werden fast so effizient sein wie die von guten Programmierern geschriebenen .

Da die Compiler inzwischen enorme Fortschritte erzielt haben, ist es daher offensichtlich, dass die überwiegende Mehrheit der Programme aus wirtschaftlichen Gründen jetzt in Hochsprachen geschrieben ist, wobei die zusätzlichen Programmierkosten den Gewinn überwiegen, der sich aus der erwarteten Leistungsverbesserung ergibt.

Es gibt jedoch noch einige sehr spezielle Fälle, in denen die Verwendung von Assembler immer noch gerechtfertigt ist:

  1. Einige komplexe Berechnungen, die direkt in Assembler geschrieben wurden, insbesondere auf massiv parallelen Maschinen , sind schneller, da die Compiler nicht hoch genug sind, um die Besonderheiten dieser Architekturen zu nutzen.
  2. Einige Routinen ( Treiber ) sind manchmal einfacher in niedriger Sprache zu schreiben.
  3. Sehr systemabhängige Aufgaben, die im Speicher des Betriebssystems ausgeführt werden , sind manchmal schwierig oder sogar unmöglich in einer Hochsprache zu schreiben. Beispielsweise können die Assembler-Anweisungen, mit denen Windows die Änderung der Aufgabe (LGDT und LLDT) auf dem i386 und dem folgenden Mikroprozessor verwalten kann, nicht von einer erweiterten Sprache emuliert oder generiert werden. Sie müssen notwendigerweise in einer kurzen Assembly-Subroutine codiert sein, die von einem Programm aufgerufen wird, das in einer höheren Sprache geschrieben ist.

Einige Compiler wandeln Programme, die in einer höheren Sprache geschrieben sind, in Assembler-Code um , wenn ihre höchste Optimierungsoption nicht aktiviert ist. Jede Anweisung auf hoher Ebene führt zu einer Reihe streng äquivalenter Assembler-Anweisungen und verwendet dieselben Symbole. Auf diese Weise können Sie den Code für Debugging- und Profiling-Zwecke anzeigen. Dies spart manchmal viel mehr Zeit durch die Überarbeitung eines Algorithmus . Unter keinen Umständen können diese Techniken für die endgültige Optimierung beibehalten werden.

Die Programmierung eingebetteter Systeme , die häufig auf Mikrocontrollern basieren , ist eine traditionelle „Nische“ für die Baugruppenprogrammierung. Tatsächlich sind diese Systeme häufig sehr ressourcenbeschränkt (zum Beispiel ist ein PIC 16F84- Mikrocontroller auf 1.024 14-Bit-Befehle beschränkt und sein RAM enthält 136 Bytes) und erfordern daher eine sehr optimierte Low-Level-Programmierung, um seine Möglichkeiten zu nutzen. Die Weiterentwicklung der Hardware führt jedoch dazu, dass die Komponenten dieser Systeme bei konstanten Kosten und bei konstantem Stromverbrauch immer leistungsfähiger werden. Die Investition in einen Programmierer "jeden Assembler", der in Arbeitsstunden viel teurer ist, wird dann zu einem Unsinn in Bezug auf den Aufwand. In der Regel ist die Assembler-Programmierung viel länger, heikler (da der Programmierer alle Mikrodetails der Entwicklung berücksichtigen muss, auf die er in der Hochsprache verzichtet) und daher erheblich teurer als die Hochsprachenprogrammierung. Es sollte daher nur für Situationen reserviert werden, für die man nichts anderes tun kann.

Makro-Assembler

Viele Assembler unterstützen eine Sprache von Makros . Es geht darum, mehrere Anweisungen zu gruppieren, um eine logischere und weniger langwierige Reihenfolge zu erreichen.
Zum Beispiel (in Microsoft MASM Assembler ):

putchar Macro car ; Prototype de la macro ifdef car ; si car est défini mov dl,car ; le mettre dans dl endif mov ah,2 ; ah=2 : fonction "putchar" en DOS int 21h ; appel au DOS endm ; fin macro

ist ein Makro, das ein Zeichen unter MS-DOS anzeigt . Es wird zum Beispiel wie folgt verwendet:

putchar "X"

Und es wird erzeugen:

mov dl,"X" mov ah,2 int 21h

Pseudo-Anweisungen

Eine Pseudoanweisung ist eine spezielle Art von Makroanweisung. Sie wird vom Editor der Assemblersoftware vordefiniert und hat die Aufgabe, eine fehlende Prozessoranweisung zu emulieren oder die Verwendung einer vorhandenen Anweisung zu erleichtern. Da der Pseudo-Befehl einen Namen hat, der dem eines echten Prozessor-Befehls sehr ähnlich ist, ist es auf den ersten Blick möglich, ihn mit einem der letzteren zu verwechseln. Beispielsweise verfügt ein RISC- Prozessor möglicherweise nicht über einen JMP-Befehl, mit dem Sie zu einem bestimmten Punkt im Programm springen und ihn nacheinander ausführen können. In diesem Fall hat der Software-Editor für den Programmierer einen Pseudobefehl "JMP <Parameter>" erstellt, der während der Montage durch einen Befehl "mov pc , <Parameter>" ersetzt wird, wobei pc der Zeiger ist ausgeführt werden. Ein weiteres Beispiel ist ein „PUSH <parameter>“ Pseudo-Instruktion wird durch eine Speicherung von <parameter> bei der Adresse ersetzt werden , auf das durch sp mit vorge Dekrementierung des letzteren, sp der Sein Stapelzeiger von dem Prozessor.

Bei RISC-Mikroprozessoren oder Mikrocontrollern wie denen der ARM- Familie gibt es keine Montageanweisung, mit der eine unmittelbare Konstante unabhängig von ihrem Wert in ein Register geladen werden kann. Die meisten Assembler verfügen über eine Pseudoanweisung, die ein solches Laden in Bezug auf die Ausführungszeit auf möglichst effiziente Weise ermöglicht und dem Programmierer diese Aufgabe erspart.

Strukturierte Programmierung im Assembler

Unterstützung für strukturierte Programmierung  : Einige Elemente der strukturierten Programmierung wurden integriert, um den Ausführungsfluss von Dr. HD Mills zu kodieren (März 1970) und implementiert von Marvin Kessler, der den S / 360-Makro-Assembler um if / else / endif- und sogar Flusssteuerungsblöcke erweitert hat. Dies war eine Möglichkeit, die Verwendung von Sprungoperationen im Assembler-Code zu reduzieren oder zu eliminieren .

Anmerkungen und Referenzen

  1. Laurent Bloch , Einführung in die Programmierung mit Schema , Éditions TECHNIP,2011( online lesen ).

Siehe auch