ARM64 Assembly für Einsteiger – Teil 2: Register, Arithmetik und Speicher
Einleitung
Im ersten Teil hast du dein Entwicklungssystem eingerichtet und dein erstes „Hello World“-Programm geschrieben. Jetzt tauchen wir tiefer ein und lernen die fundamentalen Bausteine der ARM64-Architektur kennen.
In diesem Teil lernst du:
- Was Register sind und wie man sie benutzt
- Einfache Berechnungen durchführen
- Daten im Speicher ablegen und laden
- Mit Arrays arbeiten
Register – Die Bausteine der CPU
Was sind Register?
Stell dir Register als winzige Schubladen direkt in der CPU vor. Sie können Zahlen speichern und sind extrem schnell – viel schneller als der normale Arbeitsspeicher (RAM). Der Nachteil: Es gibt nur wenige davon.
ARM64 hat 31 allgemeine Register plus ein paar spezielle:
x0bisx30– 64-Bit Register (können große Zahlen bis 2^64 speichern)w0bisw30– 32-Bit Versionen (die unteren 32 Bits der x-Register)sp– Stack Pointer (spezielles Register)pc– Program Counter (wird automatisch verwaltet)
x-Register vs. w-Register
Jedes Register kann auf zwei Arten verwendet werden:
x0 = 64 Bit: 0x0000000000000042 (komplette 8 Bytes)
w0 = 32 Bit: 0x00000042 (nur untere 4 Bytes)
Wenn du w0 verwendest, werden die oberen 32 Bits automatisch auf 0 gesetzt!
Dein erstes Rechenbeispiel
Zahlen in Register schreiben
Erstelle eine neue Datei rechnen.s:
.global _start
.align 2
.text
_start:
mov x0, #5 // x0 = 5
mov x1, #10 // x1 = 10
// Programm beenden
mov x0, #0
mov x16, #1
svc #0x80
Was passiert hier?
mov x0, #5– Schreibe die Zahl 5 in Register x0mov x1, #10– Schreibe die Zahl 10 in Register x1- Das
#bedeutet „direkte Zahl“ (Immediate Value)
Nach diesen zwei Zeilen enthält x0 den Wert 5 und x1 den Wert 10. Die Register behalten diese Werte, bis du sie überschreibst!
Arithmetische Operationen
Addition und Subtraktion
.global _start
.align 2
.text
_start:
mov x0, #5 // x0 = 5
mov x1, #3 // x1 = 3
add x2, x0, x1 // x2 = x0 + x1 = 8
sub x3, x0, x1 // x3 = x0 - x1 = 2
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Syntax von Arithmetik-Befehlen:
add Ziel, Quelle1, Quelle2
sub Ziel, Quelle1, Quelle2
Das Ergebnis wird im Ziel-Register gespeichert, die Quell-Register bleiben unverändert.
Multiplikation
.global _start
.align 2
.text
_start:
mov x0, #6 // x0 = 6
mov x1, #7 // x1 = 7
mul x2, x0, x1 // x2 = 6 * 7 = 42
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Übung: Was kommt heraus?
Versuche mental auszurechnen, was in den Registern steht:
mov x0, #10
mov x1, #4
mov x2, #2
add x3, x0, x1 // x3 = ?
mul x4, x3, x2 // x4 = ?
sub x5, x4, x1 // x5 = ?
Lösung anzeigen
x3 = 10 + 4 = 14
x4 = 14 * 2 = 28
x5 = 28 - 4 = 24
Negative Zahlen
Können Register negative Zahlen speichern?
Ja! Register speichern nur Bits (Nullen und Einsen). Ob eine Zahl positiv oder negativ ist, hängt davon ab, wie wir sie interpretieren.
mov x0, #10
mov x1, #15
sub x2, x0, x1 // x2 = 10 - 15 = -5
Computer verwenden das Zweierkomplement für negative Zahlen:
Binär: 11111111111111111111111111111111111111111111111111111111111111011
Als unsigned: 18446744073709551611 (riesige positive Zahl)
Als signed: -5 (negative Zahl)
Die CPU rechnet einfach mit den Bits. Du entscheidest, ob es eine negative oder positive Zahl ist!
Speicher – Der große Bruder der Register
Register vs. Speicher
| Eigenschaft | Register | Speicher (RAM) |
|---|---|---|
| Geschwindigkeit | Extrem schnell | Langsamer |
| Anzahl | 31 Register | Gigabytes |
| Zugriff | Direkt per Name | Über Adressen |
Daten im Speicher definieren
.global _start
.align 2
.data
meine_zahl:
.quad 42 // 64-bit Zahl (8 Bytes)
andere_zahl:
.quad 100
.text
_start:
// Lade Zahlen aus dem Speicher
adrp x0, meine_zahl@PAGE
add x0, x0, meine_zahl@PAGEOFF
ldr x1, [x0] // x1 = 42
adrp x0, andere_zahl@PAGE
add x0, x0, andere_zahl@PAGEOFF
ldr x2, [x0] // x2 = 100
add x3, x1, x2 // x3 = 142
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Wichtige Befehle:
.quad– Definiert eine 64-Bit Zahl im Speicheradrp+add– Lädt die Adresse (nicht den Wert!)ldr x1, [x0]– Load – Lies den Wert von der Adresse in x0
Adressen verstehen
Eine Adresse ist wie eine Hausnummer:
adrp x0, meine_zahl@PAGE
add x0, x0, meine_zahl@PAGEOFF
// x0 enthält jetzt die "Hausnummer" von meine_zahl
ldr x1, [x0]
// "Gehe zur Hausnummer in x0 und hole den Wert dort"
// x1 enthält jetzt: 42
Die eckigen Klammern [x0] bedeuten: „Der Wert AN der Adresse in x0“.
Daten in den Speicher schreiben
Store – Der Gegenspieler von Load
.global _start
.align 2
.data
ergebnis:
.quad 0 // Startwert: 0
.text
_start:
mov x1, #99 // x1 = 99
// Adresse laden
adrp x0, ergebnis@PAGE
add x0, x0, ergebnis@PAGEOFF
str x1, [x0] // Schreibe x1 in den Speicher
// Zur Kontrolle: wieder lesen
ldr x2, [x0] // x2 = 99
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Load vs. Store:
ldr x1, [x0]– Load = Lesen: Speicher → Registerstr x1, [x0]– Store = Schreiben: Register → Speicher
Praktisches Beispiel: Wert erhöhen
.global _start
.align 2
.data
zaehler:
.quad 5
.text
_start:
// Adresse laden
adrp x0, zaehler@PAGE
add x0, x0, zaehler@PAGEOFF
// Wert lesen
ldr x1, [x0] // x1 = 5
// Erhöhen
add x1, x1, #1 // x1 = 6
// Zurück schreiben
str x1, [x0] // zaehler = 6
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Arrays – Mehrere Werte hintereinander
Ein Array definieren
Ein Array ist einfach eine Liste von Werten, die hintereinander im Speicher liegen:
.data
zahlen:
.quad 10, 20, 30, 40, 50 // 5 Zahlen
Im Speicher sieht das so aus:
Adresse Wert
0x1000 10
0x1008 20 (8 Bytes weiter, weil .quad = 8 Bytes)
0x1010 30
0x1018 40
0x1020 50
Array-Elemente lesen
.global _start
.align 2
.data
zahlen:
.quad 100, 200, 300, 400
.text
_start:
// Startadresse laden
adrp x0, zahlen@PAGE
add x0, x0, zahlen@PAGEOFF
// Elemente mit Offset lesen
ldr x1, [x0] // x1 = 100 (Element 0)
ldr x2, [x0, #8] // x2 = 200 (Element 1, +8 Bytes)
ldr x3, [x0, #16] // x3 = 300 (Element 2, +16 Bytes)
ldr x4, [x0, #24] // x4 = 400 (Element 3, +24 Bytes)
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Offset-Berechnung:
- Element 0: Offset 0 (0 × 8)
- Element 1: Offset 8 (1 × 8)
- Element 2: Offset 16 (2 × 8)
- Element 3: Offset 24 (3 × 8)
Array-Summe berechnen
.global _start
.align 2
.data
zahlen:
.quad 5, 10, 15, 20, 25
.text
_start:
// Startadresse laden
adrp x0, zahlen@PAGE
add x0, x0, zahlen@PAGEOFF
mov x1, #0 // Summe = 0
// Alle 5 Zahlen addieren
ldr x2, [x0]
add x1, x1, x2 // Summe += zahlen[0]
ldr x2, [x0, #8]
add x1, x1, x2 // Summe += zahlen[1]
ldr x2, [x0, #16]
add x1, x1, x2 // Summe += zahlen[2]
ldr x2, [x0, #24]
add x1, x1, x2 // Summe += zahlen[3]
ldr x2, [x0, #32]
add x1, x1, x2 // Summe += zahlen[4]
// x1 = 5 + 10 + 15 + 20 + 25 = 75
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Im nächsten Teil lernst du, wie man das mit Schleifen elegant löst!
Byte-Operationen
Nicht nur 64-Bit!
Manchmal brauchst du kleinere Datentypen:
.byte– 1 Byte (8 Bit).hword– 2 Bytes (16 Bit).word– 4 Bytes (32 Bit).quad– 8 Bytes (64 Bit)
Und entsprechende Load/Store-Befehle:
ldrb/strb– Byte (8 Bit)ldrh/strh– Halfword (16 Bit)ldr/str– Word/Doubleword (32/64 Bit)
Beispiel mit Bytes
.global _start
.align 2
.data
buchstaben:
.byte 'A', 'B', 'C', 'D'
.text
_start:
adrp x0, buchstaben@PAGE
add x0, x0, buchstaben@PAGEOFF
ldrb w1, [x0] // w1 = 'A' (65)
ldrb w2, [x0, #1] // w2 = 'B' (66)
ldrb w3, [x0, #2] // w3 = 'C' (67)
// Exit
mov x0, #0
mov x16, #1
svc #0x80
Beachte: Bei Bytes ist der Offset +1 (nicht +8), weil Bytes nur 1 Byte groß sind!
Übungen
Übung 1: Einfache Berechnung
Schreibe ein Programm, das:
- x0 = 7, x1 = 3 setzt
- x2 = x0 + x1 berechnet
- x3 = x2 * 2 berechnet
Was enthält x3 am Ende?
Übung 2: Speicher-Manipulation
Erstelle ein Array mit drei Zahlen: 10, 20, 30. Lade alle drei, addiere sie, und schreibe das Ergebnis in eine vierte Variable im Speicher.
Übung 3: Mittelwert
Berechne den Mittelwert von drei Zahlen:
zahlen: .quad 6, 9, 12
// Mittelwert = (6 + 9 + 12) / 3 = 9
Hinweis: Für Division kannst du udiv verwenden:
udiv x2, x0, x1 // x2 = x0 / x1 (unsigned)
Zusammenfassung
In diesem Teil hast du gelernt:
- ✅ Register sind schnelle Speicher in der CPU (x0-x30)
- ✅ Arithmetik-Befehle:
add,sub,mul - ✅ Negative Zahlen funktionieren automatisch (Zweierkomplement)
- ✅ Speicher wird über Adressen angesprochen
- ✅
ldr= Lesen aus Speicher,str= Schreiben in Speicher - ✅ Arrays sind aufeinanderfolgende Werte mit Offsets
- ✅ Verschiedene Datengrößen: byte, hword, word, quad
Nächste Schritte
Im nächsten Teil lernst du Kontrollfluss:
- Bedingungen (if/else)
- Vergleiche
- Sprünge (Branches)
- Schleifen (Loops)
Damit wirst du dann richtig dynamische Programme schreiben können!
2 Gedanken zu „ARM64 Assembly Tutorial – Teil 2: Register, Arithmetik und Speicher“
Die Kommentare sind geschlossen.