Assemblerprogrammierung

Ein Assembler ist ein Programm, welches Assemblersprache in Maschinensprache (1110001) übersetzt. Häufig wird jedoch Assemblersprache durch "Assembler" abgekürzt. Die Assemblersprache zählt allerdings nicht zu den hohen Programmiersprachen wie C, C++ oder Java.

Die meisten Programme werden in höheren Programmiersprachen geschrieben, da diese für uns leichter zu verstehen ist. Allerdings müssen sie durch einen Compiler übersetzt werden. Assemblerprogrammierung ist sehr hardwarenahe und wird deshalb sehr schnell ausgeführt.
Häufig werden komplexe Algorithmen mit schlechter Laufzeit in Assembler oder C ausgelagert, und im Nachhinein z.B. in Java eingebunden.

Heut zu Tage ist eine IDE (Integrated Development Environment) nicht mehr wegzudenken. Eine Integrierte Entwicklungsumgebung stellt eine Sammlung von Werkzeugen zur Verfügung, welche das Entwickeln und Testen von Programmen erheblich erleichtern.
Einige der hilfreichsten Werkzeuge sind:

  • Syntaxhighlighting und Einrückhilfen
  • Der Quelltext wird automatisch in verschiedenen Farben dargestellt. (z.B.: Variablen in blau, Kommentare in Rot, usw.)
  • Syntaktische Fehlererkennung
  • Dem Programmierer werden syntaktische Fehler bereits während des Schreibens angezeigt. Refactoring bezeichnet in der Software-Entwicklung die manuelle oder automatisierte Strukturverbesserung von Quelltexten unter Beibehaltung des beobachtbaren Programmverhaltens.
    Quelle: https://wiki.c2.com/?WhatIsRefactoring
  • Code Vervollständigung
  • Nach Eingabe vieler Befehle erscheint eine Vorschlagsliste, die vollständige Befehle mit Rückgabewert und Parameterliste angibt. Manche IDEs erlauben sogar den Zugriff auf die Dokumentation
  • Meistens offen für Erweiterungen
  • Automatische Organisation der Projektordner und -dateien
  • Unterstützung von Teamarbeit
  • Versionsverwaltung integriert in der Entwicklungsumgebung
  • Refactoring-Unterstützung
  • Debugging / Testing

x86 Prozessor-Architektur

Zunächst muss man sich mit der Architektur und dem Befehlssatz von x86-Prozessoren auseinandersetzen. Es lassen sich nämlich alle CPU`s in einem zum 8086-Prozessor kompatiblen Modus betreiben. Deshalb ist ein gutes Verständnis von Assembler und der darunterliegenden Prozessorarchitektur notwendig. Außerdem sollte man die Arbeitsweise des Stacks und der Register kennen.

Interrupts

Ein Programm kann beim Eintreffen eines bestimmten internen oder externen Ereignisses unterbrochen werden. Ein Interrupt unterbricht das laufende Programm, beendet den aktuellen Maschinenbefehl und ruft ein spezielles Unterprogramm auf (ISR: Interrupt Service Routine). Anschließend erfolgt der Rücksprung aus der Interrupt Service Routine und das Programm wird fortgeführt.

Register - interne Speicherstellen

Register sind interne Speicherstellen, die direkt bei der CPU liegen und Ergebnisse und Berechnungen aufnehmen können. Fast alle CPU Operationen funktionieren nur mit Registern. Man hat mit Assembler direkten Zugriff darauf.

Alle x86 Prozessoren haben 14 Register die jeweils 16 Bit groß sind. 8 Registernamen beginnen mit einem E (extended), da diese von 16 auf 32 Bit erweitert wurden. Die unteren 16 Bit dieser Register können explizit mit den alten 16 Bit Registern angesprochen werden.

4 dieser Register (AX, BX, CX, DX), auch Universalregister genannt, haben zusätzlich noch eine Besonderheit. Jedes dieser Register lässt sich in ein High Byte und ein Low Byte aufteilen, also zu 2 Register je 8 Bit groß.
Zudem gibt es Index- und Zeigerregister (SI, DI, SP, BP) und die Segmentregister (CS, DS, SS, ES).

Bit071531
AkkumulatorALEAXAH 
Base-RegisterBLEBXBH 
Count-RegisterCLECXCH 
Data-RegisterDLEDXDH 
Source IndexESI 
Destination IndexEDI 
StackpointerESP 
BasepointerEBP 
Codesegment-RegisterCS 
Datasegment-RegisterDS 
Stacksegment-RegisterSS 
Extrasegment-RegisterES 
Instruction PointerIP 
FlagsFlags 

Stapel

Bevor man sich mit der eigentlichen Assemblerprogrammierung beschäftigt, muss man sich zunächst mit dem Stack vertraut machen.

Der Stack (Stapel), ist ein bestimmter Abschnitt im Hauptspeicher, der nach dem LIFO (Last In First Out) Verfahren arbeitet. Daten die zuletzt auf den Stack gelegt wurden, werden als erstes wieder entfernt. Er kann mit Hilfe seiner Operationen PUSH und POP direkt benutzt werden. PUSH legt Daten auf dem Stack ab, POP holt sie wieder herunter. Der Stack ist ein wichtiger, elementarer Bestandteil, der sehr schnell arbeitet, da es extra reservierte Register dafür gibt. Diese sind zum einen das Stacksegment (SS) und zum anderen der Stackpointer (SP). Bei den Operationen PUSH und POP wird die automatische Stackverwaltung durch die Register SS und SP benutzt.

Wichtige Maschinenbefehle

Der move-Befehl

mov <Zieloperand, Quelloperand>

der move-Befehl ist einer der am häufigsten verwendeten Assembler Befehle und kopiert den Quelloperanden in den Zieloperanden. Hierbei ist zu beachten, dass beide Operatoren die gleiche Größe besitzen müssen. Des Weiteren kann keine Speicherstelle in eine andere kopiert werden. Das gilt allerdings für alle Assemblerbefehle.

Der exchange-Befehl

xchg <Zieloperand, Quelloperand>

Mit Hilfe des exchange-Befehls können 2 Operanden vertauscht werden.

Der addition/addition with carry-Befehl

add/adc <Zieloperand, Quelloperand>

Diese beiden Befehle addieren Ziel- und Quelloperand und speichern das Ergebnis im Zieloperand ab. Der adc-Befehl berücksichtigt allerdings das Carry Flag. Dieses wird gesetzt, wenn ein Bit-Übertrag vorhanden ist.

Der subtract/subtract with borrow-Befehl

sub/sbb <Zieloperand, Quelloperand>

Diese beiden Befehle ziehen den Quelloperand vom Zieloperand ab und speichern es im Zieloperand.

Verändern des Carry-Flags

Um das Carry Flag zu verändern, stehen dem Programmierer 3 Befehle zu Verfügung.

Zum Setzen des Carry Flags:

stc (Set Carry Flag)

Zum löschen des Carry Flags:

clc (Clear Carry Flag)

Um den Zustand des Carry Flags umzudrehen:

cmc (Complement Carry Flag)

Der increment/decrement-Befehl

inc/dec Zieloperand

Durch diese beiden Befehle kann der Zieloperand um eins erhöht, bzw. um eins verringert werden. Das erreicht man auch durch die Befehle add/sub, allerdings wird beim inc/dec Befehl das Carry Flag nicht beeinflusst.

Der multiply unsigned/integer multiply-Befehl

mul/imul <Zielperand>

Der multiply-Befehl multipliziert den Zieloperanden mit dem Register AL, AX oder EAX. Der imul-Befehl multipiziert dabei eine vorzeichenbeahftete Zahl.

Der interrupt-Befehl

int <InterruptNummer>

Der interrupt-Befehl löst eine Software-Unterbrechung des Betriebssystems aus.
Hier findest du eine Liste aller gängigen Interrupts: http://www.ctyme.com/intr/int.htm

Assemblerprogramme

Um Assemblerprogramme ausführen zu können, benötigst du:

Eine Installations- und Initialisierungsanleitung findest du hier.

Hello World Beispiel

hello.asm

title   Hello World Program

dosseg	
.model small
.stack 100h

.data
hello_message db 'Hello, World!',0dh,0ah,'$'

.code
main  proc
      mov    ax,@data
      mov    ds,ax

      mov    ah,9
      mov    dx,offset hello_message
      int    21h			

      mov    ax,4C00h
      int    21h
main  endp
end   main