ARM64 Assembly Tutorial – Teil 3: Bedingungen, Schleifen und Kontrollfluss

ARM64 Assembly für Einsteiger – Teil 3: Bedingungen, Schleifen und Kontrollfluss

← Zurück zu Teil 2

Einleitung

Bisher konnten unsere Programme nur linear ablaufen – Zeile für Zeile, von oben nach unten. Echte Programme brauchen aber Entscheidungen und Wiederholungen.

In diesem Teil lernst du:

  • Wie man Werte vergleicht
  • Bedingte Sprünge (if/else)
  • Schleifen (while/for)
  • Wie man komplexe Programmabläufe steuert

Labels – Sprungziele im Code

Was ist ein Label?

Ein Label ist ein Name für eine bestimmte Stelle im Code – wie ein Lesezeichen. Du kannst zu diesem Label springen.

.global _start
.align 2

.text
_start:
    mov x0, #5
    b springe_hierhin       // Springe zum Label
    mov x0, #99             // Wird übersprungen!

springe_hierhin:
    mov x1, #10
    // x0 = 5, x1 = 10 (die Zeile mit #99 wurde nie ausgeführt)

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Wichtig:

  • Labels enden mit einem Doppelpunkt :
  • b = Branch (unbedingter Sprung)
  • Der Code bei mov x0, #99 wird nie erreicht!

Vergleiche – Die Basis für Entscheidungen

Der CMP-Befehl

cmp (Compare) vergleicht zwei Werte und setzt interne Flags:

cmp x0, x1      // Vergleiche x0 mit x1

Der CMP-Befehl verändert die Register nicht, sondern setzt nur unsichtbare Condition Flags:

  • Z (Zero) – Sind die Werte gleich?
  • N (Negative) – Ist das Ergebnis negativ?
  • C (Carry) – Gab es einen Übertrag?
  • V (Overflow) – Gab es einen Überlauf?

Diese Flags kannst du mit bedingten Sprüngen abfragen!

Bedingte Sprünge

Die wichtigsten Bedingungen

BefehlBedeutungBedingung
b.eqBranch if EqualWenn gleich
b.neBranch if Not EqualWenn ungleich
b.ltBranch if Less ThanWenn kleiner (signed)
b.leBranch if Less or EqualWenn kleiner oder gleich
b.gtBranch if Greater ThanWenn größer (signed)
b.geBranch if Greater or EqualWenn größer oder gleich
b.loBranch if LowerWenn kleiner (unsigned)
b.hiBranch if HigherWenn größer (unsigned)

Erstes Beispiel: Einfacher Vergleich

.global _start
.align 2

.text
_start:
    mov x0, #5
    mov x1, #10

    cmp x0, x1          // Vergleiche 5 mit 10
    b.eq sind_gleich    // Springe wenn gleich
    b.lt ist_kleiner    // Springe wenn x0 < x1
    b sind_groesser     // Sonst (x0 > x1)

sind_gleich:
    mov x2, #1          // x2 = 1 (gleich)
    b ende

ist_kleiner:
    mov x2, #2          // x2 = 2 (kleiner)
    b ende

sind_groesser:
    mov x2, #3          // x2 = 3 (größer)
    b ende

ende:
    // x2 = 2, weil 5 < 10

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Ablauf:

  1. cmp x0, x1 vergleicht 5 mit 10
  2. b.eq sind_gleich wird übersprungen (5 ≠ 10)
  3. b.lt ist_kleiner wird ausgeführt (5 < 10)
  4. Sprung zu ist_kleiner
  5. mov x2, #2
  6. b ende springt zum Ende

If/Else in Assembly

Pattern: if (a > b) then … else …

In einer höheren Sprache:

if (x0 > x1) {
    x2 = 100;
} else {
    x2 = 200;
}

In Assembly:

cmp x0, x1          // Vergleiche
b.le else_teil      // Wenn x0 <= x1, gehe zu else
// If-Teil
mov x2, #100
b ende
else_teil:
// Else-Teil
mov x2, #200
ende:
// Weiter im Programm

Praktisches Beispiel: Maximum finden

.global _start
.align 2

.data
zahl_a:
    .quad 42
zahl_b:
    .quad 17
maximum:
    .quad 0

.text
_start:
    // Lade zahl_a
    adrp x0, zahl_a@PAGE
    add x0, x0, zahl_a@PAGEOFF
    ldr x1, [x0]        // x1 = 42

    // Lade zahl_b
    adrp x0, zahl_b@PAGE
    add x0, x0, zahl_b@PAGEOFF
    ldr x2, [x0]        // x2 = 17

    // Vergleiche
    cmp x1, x2
    b.gt a_ist_groesser
    // b ist größer oder gleich
    mov x3, x2
    b weiter

a_ist_groesser:
    mov x3, x1

weiter:
    // Speichere Maximum
    adrp x0, maximum@PAGE
    add x0, x0, maximum@PAGEOFF
    str x3, [x0]        // maximum = 42

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Schleifen – Code wiederholen

Die grundlegende While-Schleife

In einer höheren Sprache:

int i = 0;
while (i < 5) {
    i = i + 1;
}

In Assembly:

.global _start
.align 2

.text
_start:
    mov x0, #0          // i = 0
    mov x1, #5          // Limit = 5

loop:
    add x0, x0, #1      // i++
    cmp x0, x1          // Vergleiche i mit 5
    b.lt loop           // Wenn i < 5, wiederhole

    // Schleife endet, x0 = 5

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Ablauf:

Start: x0 = 0
Loop 1: x0 = 1, 1 < 5? Ja → weiter
Loop 2: x0 = 2, 2 < 5? Ja → weiter
Loop 3: x0 = 3, 3 < 5? Ja → weiter
Loop 4: x0 = 4, 4 < 5? Ja → weiter
Loop 5: x0 = 5, 5 < 5? Nein → Ende

Beispiel: Summe von 1 bis 10

.global _start
.align 2

.text
_start:
    mov x0, #1          // Zähler = 1
    mov x1, #10         // Bis 10
    mov x2, #0          // Summe = 0

summen_loop:
    add x2, x2, x0      // Summe += Zähler
    add x0, x0, #1      // Zähler++
    cmp x0, x1
    b.le summen_loop    // Wiederhole wenn x0 <= 10

    // x2 = 1+2+3+4+5+6+7+8+9+10 = 55

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Countdown-Schleife

.global _start
.align 2

.text
_start:
    mov x0, #10         // Start bei 10

countdown:
    sub x0, x0, #1      // x0--
    cmp x0, #0          // Ist x0 == 0?
    b.ne countdown      // Wenn nicht 0, wiederhole

    // x0 = 0

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Schleifen mit Arrays

Alle Array-Elemente durchgehen

.global _start
.align 2

.data
zahlen:
    .quad 5, 10, 15, 20, 25

.text
_start:
    // Startadresse
    adrp x0, zahlen@PAGE
    add x0, x0, zahlen@PAGEOFF

    mov x1, #0          // Offset = 0
    mov x2, #40         // Ende (5 Zahlen × 8 Bytes = 40)
    mov x3, #0          // Summe = 0

loop:
    ldr x4, [x0, x1]    // Lade zahlen[offset]
    add x3, x3, x4      // Summe += x4
    add x1, x1, #8      // Offset += 8
    cmp x1, x2          // Offset < 40?
    b.lt loop

    // x3 = 5+10+15+20+25 = 75

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Wie es funktioniert:

  • x0 = Basis-Adresse des Arrays
  • x1 = Aktueller Offset (0, 8, 16, 24, 32)
  • ldr x4, [x0, x1] = Lade von Adresse (x0 + x1)

Alternative: Mit Index-Register

.global _start
.align 2

.data
zahlen:
    .quad 10, 20, 30, 40, 50

.text
_start:
    adrp x0, zahlen@PAGE
    add x0, x0, zahlen@PAGEOFF

    mov x1, #0          // Index = 0
    mov x2, #5          // Anzahl Elemente
    mov x3, #0          // Summe = 0

loop:
    // Berechne Offset: Index × 8
    lsl x4, x1, #3      // x4 = x1 << 3 = x1 × 8
    ldr x5, [x0, x4]    // Lade Element
    add x3, x3, x5      // Addiere zur Summe

    add x1, x1, #1      // Index++
    cmp x1, x2          // Index < 5?
    b.lt loop

    // x3 = 150

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Neuer Befehl: lsl = Logical Shift Left (Linksverschiebung)

  • lsl x4, x1, #3 verschiebt x1 um 3 Bits nach links
  • Das ist dasselbe wie × 8 (2³ = 8)

Verschachtelte Bedingungen

Mehrere If-Abfragen

.global _start
.align 2

.text
_start:
    mov x0, #15         // Zu prüfende Zahl
    mov x1, #0          // Ergebnis-Code

    // Prüfe ob < 10
    cmp x0, #10
    b.ge nicht_klein
    mov x1, #1          // Code 1: klein
    b ende

nicht_klein:
    // Prüfe ob < 20
    cmp x0, #20
    b.ge nicht_mittel
    mov x1, #2          // Code 2: mittel
    b ende

nicht_mittel:
    // Prüfe ob < 30
    cmp x0, #30
    b.ge gross
    mov x1, #3          // Code 3: groß
    b ende

gross:
    mov x1, #4          // Code 4: sehr groß

ende:
    // x1 = 2 (weil 15 zwischen 10 und 20 liegt)

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Praktische Beispiele

Beispiel 1: Fakultät berechnen

Fakultät von n = n × (n-1) × (n-2) × … × 1

.global _start
.align 2

.text
_start:
    mov x0, #5          // n = 5
    mov x1, #1          // Ergebnis = 1

fakultaet_loop:
    mul x1, x1, x0      // Ergebnis *= n
    sub x0, x0, #1      // n--
    cmp x0, #1          // n > 1?
    b.gt fakultaet_loop

    // x1 = 5! = 5×4×3×2×1 = 120

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Beispiel 2: Maximum in Array finden

.global _start
.align 2

.data
zahlen:
    .quad 23, 67, 12, 89, 34, 56

.text
_start:
    adrp x0, zahlen@PAGE
    add x0, x0, zahlen@PAGEOFF

    ldr x1, [x0]        // x1 = erstes Element (aktuelles Max)
    mov x2, #8          // Offset (starte bei 2. Element)
    mov x3, #48         // Ende (6 × 8 = 48)

find_max_loop:
    ldr x4, [x0, x2]    // Lade nächstes Element
    cmp x4, x1          // Ist es größer als aktuelles Max?
    b.le nicht_groesser
    mov x1, x4          // Neues Maximum

nicht_groesser:
    add x2, x2, #8      // Nächstes Element
    cmp x2, x3          // Noch nicht am Ende?
    b.lt find_max_loop

    // x1 = 89 (das Maximum)

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Beispiel 3: Bubble Sort (vereinfacht)

Sortiert zwei Zahlen, wenn sie in falscher Reihenfolge sind:

.global _start
.align 2

.data
zahl1:
    .quad 42
zahl2:
    .quad 17

.text
_start:
    // Lade beide Zahlen
    adrp x0, zahl1@PAGE
    add x0, x0, zahl1@PAGEOFF
    ldr x1, [x0]        // x1 = 42

    adrp x0, zahl2@PAGE
    add x0, x0, zahl2@PAGEOFF
    ldr x2, [x0]        // x2 = 17

    // Vergleiche
    cmp x1, x2
    b.le richtige_reihenfolge

    // Tausche
    adrp x0, zahl1@PAGE
    add x0, x0, zahl1@PAGEOFF
    str x2, [x0]        // zahl1 = 17

    adrp x0, zahl2@PAGE
    add x0, x0, zahl2@PAGEOFF
    str x1, [x0]        // zahl2 = 42

richtige_reihenfolge:
    // Jetzt: zahl1 = 17, zahl2 = 42

    // Exit
    mov x0, #0
    mov x16, #1
    svc #0x80

Übungen

Übung 1: Gerade oder Ungerade

Schreibe ein Programm, das prüft ob eine Zahl gerade ist:

  • Wenn gerade: x1 = 0
  • Wenn ungerade: x1 = 1

Hinweis: Nutze bitweise AND: and x1, x0, #1 (prüft das niedrigste Bit)

Übung 2: Countdown mit Ausgabe

Erweitere die Countdown-Schleife so, dass jede Zahl als Ziffer ausgegeben wird. Nutze das Wissen aus Teil 1 über Syscalls.

Übung 3: Fibonacci-Zahlen

Berechne die ersten 10 Fibonacci-Zahlen:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

Formel: F(n) = F(n-1) + F(n-2)

Übung 4: Minimum und Maximum

Schreibe ein Programm, das sowohl Minimum als auch Maximum in einem Array findet.

Debugging-Tipps

Häufige Fehler bei Schleifen

Endlosschleife:

// FALSCH:
loop:
    add x0, x0, #1
    cmp x0, #10
    b.lt loop       // Springt immer zurück!

Problem: Keine Abbruchbedingung wenn x0 schon >= 10 war!

Off-by-one Fehler:

// Soll bis 10 zählen (inkl. 10)
mov x0, #1
loop:
    add x0, x0, #1
    cmp x0, #10
    b.lt loop       // Stoppt bei 10, aber führt 10 nicht mehr aus!

// Richtig:
    b.le loop       // <= statt <

Debugging mit lldb (macOS Debugger)

Du kannst dein Programm Schritt für Schritt ausführen:

# Kompiliere mit Debug-Info
as -g -o programm.o programm.s
ld -o programm programm.o -lSystem -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -e _start -arch arm64

# Starte Debugger
lldb programm

# Im lldb:
(lldb) breakpoint set --name _start
(lldb) run
(lldb) register read        # Zeige alle Register
(lldb) step                 # Nächste Instruktion
(lldb) continue             # Weiter ausführen

Zusammenfassung

In diesem Teil hast du gelernt:

  • ✅ Labels markieren Positionen im Code
  • cmp vergleicht Werte und setzt Flags
  • ✅ Bedingte Sprünge: b.eq, b.lt, b.gt, etc.
  • ✅ If/else durch Vergleich + bedingte Sprünge
  • ✅ Schleifen = Label + Vergleich + bedingter Sprung zurück
  • ✅ Arrays mit Schleifen durchlaufen
  • ✅ Praktische Algorithmen (Maximum, Fakultät, Sortierung)

Nächste Schritte

Im nächsten Teil lernst du Funktionen und den Stack:

  • Funktionen aufrufen und zurückkehren
  • Parameter übergeben
  • Lokale Variablen
  • Der Stack (Stapelspeicher)
  • Calling Conventions

Damit kannst du dann strukturierte, modulare Programme schreiben!


← Zurück zu Teil 2 | Weiter zu Teil 4 →

1 Gedanke zu „ARM64 Assembly Tutorial – Teil 3: Bedingungen, Schleifen und Kontrollfluss“

Die Kommentare sind geschlossen.