Assemblercode
Ein Assembler übersetzt menschenlesbare Gedächtnishilfen in direkt ausführbaren Maschinencode. Dabei wird jeder Befehl einer konkreten Prozessorarchitektur zugeordnet. So können mithilfe von Befehlen wie MOV, ADD oder JMP Operationen auf Registern und im Speicher ausgeführt werden. Im Folgenden finden sich verschiedene Beispiele, die den praktischen Einsatz von Assemblersprache aufzeigen.
Beispiel 1: Funktion, die das Maximum aus drei Zahlen liefert
; x86-64 GNU Assembler Syntax
; Parameter in rdi, rsi, rdx
; Rückgabewert über rax
global maxofthree
section .text
maxofthree:
mov rax, rdi ; rax ← x
cmp rsi, rax ; vergleiche y mit aktuellem max
cmovl rax, rsi ; falls y größer ist, rax ← y
cmp rdx, rax ; vergleiche z mit aktuellem max
cmovl rax, rdx ; falls z größer ist, rax ← z
ret ; rax enthält das Maximum
Diese Routine nimmt drei int64_t-Werte entgegen und gibt den größten Wert zurück[1]. Ein entsprechender Aufruf aus C setzt die Parameter in rdi, rsi und rdx und erwartet das Ergebnis in rax.
Beispiel 2: Kleine arithmetische Berechnungen
; x86-64 Linux Syntax
; Berechnet (RDI + RSI) * 4 und speichert das Ergebnis in RAX
global compute
section .text
compute:
mov rax, rdi ; rax ← erster Parameter
add rax, rsi ; rax ← rax + zweiter Parameter
shl rax, 2 ; rax ← rax << 2 (Multiplikation mit 4)
ret
Hier wird zunächst ein Wert aus rdi geholt, mit rsi addiert und anschließend mit dem Befehl shl (Shift Left) vervierfacht. Dieser Code verdeutlicht, wie direkt Registerinhalte manipuliert werden können.
Beispiel 3: Einfache Datenzugriffe im Speicher
; Kopiert einen 32-Bit Wert aus der Speicheradresse [RDI] in EAX
; Zum Schluss erhöht es den Wert in EAX um 1
global incrementMemory
section .text
incrementMemory:
mov eax, [rdi] ; eax ← Wert an Speicheradresse rdi
inc eax ; eax ← eax + 1
mov [rdi], eax ; aktualisiere Speicheradresse mit neuem Wert
ret
Die Instruktion mov eax, [rdi] liest 4 Bytes (32 Bit) aus dem Speicher in eax, während mov [rdi], eax den Wert wieder an dieselbe Stelle im Speicher schreibt.
Beispiel 4: Schleife mit Dekrement und bedingtem Sprung
; Summiert mehrere Double-Werte aus einem Array (64-Bit Gleitkommazahlen).
; Param:
; rdi: Zeiger auf das Start-Array
; rsi: Anzahl der Elemente
; Rückgabewert in xmm0
global sumOfDoubles
section .text
sumOfDoubles:
xorpd xmm0, xmm0 ; xmm0 ← 0.0 (Initialisierung)
cmp rsi, 0 ; wenn keine Elemente, überspringen
je .done
.loop:
addsd xmm0, [rdi] ; double-Wert an [rdi] zu xmm0 addieren
add rdi, 8 ; Zeiger um 8 Bytes erhöhen (nächstes Double)
dec rsi ; Anzahl der Elemente dekrementieren
jnz .loop ; solange rsi != 0, weitermachen
.done:
ret
Die Schleife addiert fortlaufend Double-Werte aus dem Speicher, bis alle Elemente verarbeitet sind. Durch jnz wird gesprungen, solange das Zero-Flag den Wert 0 nicht anzeigt, also rsi ungleich 0 ist.
Assembler und Maschinencode
Ein Assembler ordnet jedem Befehl ein maschinenspezifisches Codewort zu und erzeugt daraus ein lauffähiges Objektprogramm. Diese geringe Abstraktionsebene ermöglicht eine präzise Steuerung der Hardware. Praktisch wird ein reines Schreiben von Maschinencode vermieden, da Fehlerquellen hoch sind und Assemblersprache deutlich übersichtlicher ist.