Laden und Speichern von Werten: Unterschied zwischen den Versionen
| (Eine dazwischenliegende Version desselben Benutzers wird nicht angezeigt) | |||
| Zeile 208: | Zeile 208: | ||
== Indexsierung (Arrays) == | == Indexsierung (Arrays) == | ||
=== Einführung in die Indizierung (Arrays) und Post-Indexierte Adressierung im ARM64-Bit-Assembler === | |||
In diesem Abschnitt wird erklärt, wie Sie im ARM64-Bit-Assembler mit Arrays und der Indizierung arbeiten können. Zudem wird die post-indexierte Adressierung besprochen. | |||
==== PseudoCode und Entsprechendes in Assembler ==== | |||
PseudoCode: | PseudoCode: | ||
<syntaxhighlight lang="basic"> | |||
dim a[10] as word | |||
a[5] = 10 // Setze das 5. Element auf 10 | |||
x = a[8] // Schreibe das 8. Element nach x | |||
for i = 1 to 10 | |||
a[i] = i * 8 | |||
next | |||
</syntaxhighlight> | |||
Assembler-Code: | |||
<syntaxhighlight lang="asm"> | |||
ldr x0, =MeinArray // Lade die Adresse von MeinArray nach x0 | |||
mov w1, #10 // Speichere 10 in w1 | |||
str w1, [x0, #(4*4)] // Speichere w1 in das 5. Element (Beginn ist 0) (Word = 4 Byte) | |||
ldr w1, [x0, #(7*4)] // Lade das 8. Element nach w1 | |||
mov w2, #1 // i = 1 | |||
forloop: | |||
lsl w4, w2, #3 // i * 8 | |||
str w4, [x0, w2, lsl #2] // Speicher den Wert von w4 an die Adresse (x0 + i*4) | |||
add w2, w2, #1 // i = i + 1 | |||
cmp w2, #10 // if i < 10 | |||
b.le forloop // Springen zurück zu forloop, wenn w2 <= 10 | |||
</syntaxhighlight> | |||
'''Schritt für Schritt Erklärung''': | |||
Array-Adresse laden: | |||
<syntaxhighlight lang="asm"> | |||
ldr x0, =MeinArray | |||
</syntaxhighlight> | |||
Hier wird die Anfangsadresse des Arrays MeinArray in das Register x0 geladen. | |||
Wert setzen: | |||
<syntaxhighlight lang="asm"> | |||
mov w1, #10 | |||
str w1, [x0, #(4*4)] // 4*4 weil das 5. Element bei Index 4 liegt (0-basiert) | |||
</syntaxhighlight> | |||
Der Wert 10 wird in das 5. Element des Arrays MeinArray geschrieben. | |||
Wert lesen: | |||
<syntaxhighlight lang="asm"> | |||
ldr w1, [x0, #(7*4)] // Lesen des 8. Elements (Index 7) | |||
</syntaxhighlight> | |||
Der Wert des 8. Elements (Index 7) wird in das Register w1 geladen. | |||
Schleife, um den Array zu füllen: | |||
<syntaxhighlight lang="asm"> | |||
mov w2, #1 | |||
forloop: | |||
lsl w4, w2, #3 // w4 = w2 * 8 (Linksverschiebung um 3 Bits) | |||
str w4, [x0, w2, lsl #2] // Schreiben des Wertes in das Array, Adresse ist (x0 + w2*4) | |||
add w2, w2, #1 // w2 = w2 + 1 | |||
cmp w2, #10 // Vergleichen ob w2 < 10 | |||
b.le forloop // Wiederholen, bis w2 > 10 | |||
</syntaxhighlight> | |||
=== Post-indexierte Adressierung === | |||
Post-indexierte Adressierung ist eine Technik, bei der ein Register nicht nur verwendet wird, um eine Adresse zu bestimmen, sondern danach auch modifiziert wird. Hier ein Beispiel: | |||
<syntaxhighlight lang="asm"> | |||
ldr x1, [x2], #4 // Lese den Wert von der Adresse in x2 nach x1 und erhöhe x2 um 4 | |||
</syntaxhighlight> | |||
In diesem Fall wird die ursprüngliche Adresse verwendet, um den Wert zu laden, und dann wird die Adresse um 4 erhöht. | |||
=== 128-Bit Laden und Speichern mit ldp und stp === | |||
Die Befehle ldp (load pair) und stp (store pair) können verwendet werden, um 128-Bit-Daten (zwei 64-Bit-Register) zu laden und zu speichern: | |||
* Laden | |||
<syntaxhighlight lang="asm"> | |||
ldr x1, =GrosseZahl | |||
ldp x2, x3, [x1] // Lese 128 Bit (in x2 und x3), beginnend bei der Adresse in x1 | |||
</syntaxhighlight> | |||
* Speichern | |||
<syntaxhighlight lang="asm"> | |||
stp x2, x3, [x1] // Speichere 128 Bit (x2 und x3) an die Adresse in x1 | |||
</syntaxhighlight> | |||
* Datenabschnitt | |||
<syntaxhighlight lang="asm"> | |||
.data | |||
GrosseZahl: | |||
.octa 0x12345678901234567890123456789012 | |||
</syntaxhighlight> | |||
Diese Anweisungen sind besonders nützlich für Manipulationen an großen Datenmengen und erlauben effizientere Speicheroperationen. | |||
=== Zusammenfassung === | |||
Indizierung erlaubt es, auf Elemente eines Arrays zuzugreifen und sie zu manipulieren. Relative und post-indexierte Adressierung bieten Methoden, um Speicher effizient zu verwenden. Das Laden und Speichern mit ldp und stp ermöglicht die Handhabung von 128-Bit-Daten, um Speicheroperationen schneller und kompakter zu gestalten. | |||
Aktuelle Version vom 8. April 2025, 12:03 Uhr
Laden und Speichern von Werten in ARM64-Assembler
In diesem Abschnitt werden wir ausführlich und verständlich die grundlegenden Konzepte des Ladens und Speicherns von Werten in ARM64-Assembler behandeln. Dies umfasst das Laden von konstanten und variablen Werten in Register sowie das Speichern von Registerwerten im Speicher. Wir werden die Befehle ldr und str detailliert besprechen. Diese Anleitung ist für Anfänger geschrieben und soll Ihnen helfen, die Grundlagen zu verstehen und anzuwenden.
Register und Speicher: Grundlagen
Register sind spezielle Speicherplätze in der CPU, die sehr schnell zugänglich sind. Sie dienen dazu, Daten während der Programmausführung zu speichern und zu manipulieren. ARM64-Prozessoren verfügen über 31 allgemeine Register, die jeweils 64 Bit groß sind und als x0 bis x30 bezeichnet werden.
Der Speicher hingegen ist eine größere, aber langsamer zugängliche Ressource, auf die mit Speicheradressen zugegriffen wird. Typischerweise werden Werte vom Speicher in Register geladen, um sie zu manipulieren, und anschließend wieder in den Speicher zurückgeschrieben.
Laden von konstanten Werten in Register
Wenn wir einen konstanten Wert in ein Register laden möchten, verwenden wir den mov-Befehl. Ein konstanten Wert ist ein fester Wert, der direkt im Assemblercode angegeben wird.
Beispiel: Laden des Werts 42 in das Register x0
mov x0, #42 // Lade den konstanten Wert 42 in das Register x0
Laden von variablen Werten in Register
Variable Werte sind solche, die im Speicher gespeichert sind und deren Werte sich ändern können. Um einen variablen Wert in ein Register zu laden, verwenden wir den ldr-Befehl.
Beispiel: Angenommen, wir haben eine Variable, die eine Ganzzahl speichert
.section .data
var: .quad 12345 // Definition einer Variable im Datensegment mit dem Wert 12345
.section .text
.global _start
_start:
ldr x0, =var // Lade die Adresse der Variable `var` in das Register x0
ldr x1, [x0] // Lade den Wert der Adresse in x0 in das Register x1
// Im Register x1 steht jetzt der Wert 12345
Speichern von Registerwerten im Speicher
Wenn wir einen Wert von einem Register in den Speicher speichern möchten, verwenden wir den str-Befehl.
Beispiel: Speichern eines Wertes in den Speicher
.section .data
var: .quad 0 // Speicher für eine Variable im Datensegment
.section .text
.global _start
_start:
mov x0, #6789 // Lade den Wert 6789 in das Register x0
ldr x1, =var // Lade die Adresse der Variable `var` in das Register x1
str x0, [x1] // Speichere den Wert aus x0 an der Adresse in x1 (in die Variable `var`)
// Jetzt ist der Wert 6789 im Speicher an der Adresse von `var` gespeichert
Verwende hier im gdb den Befehl x /1h $x1 um den Speicherbereich auf den das Register x1 zeigt, anzuzeigen.
Laden und Speichern: Die Befehle ldr und str
ldr (load register): Dieser Befehl lädt einen Wert aus dem Speicher in ein Register.
Syntax: ldr <Zielregister>, [<Quelladresse>]
Beispiel:
ldr x0, [x1] (Lade den Wert aus der Speicheradresse, die in x1 steht, in x0)
str (store register): Dieser Befehl speichert einen Wert aus einem Register in den Speicher.
Syntax: str <Quellregister>, [<Zieladresse>]
Beispiel:
str x0, [x1] (Speichere den Wert aus x0 in die Speicheradresse, die in x1 steht)
Zusammenfassung
Um das Laden und Speichern von Werten in ARM64-Assembler zusammenzufassen:
Konstanten Werte laden
Verwenden Sie mov, um einen konstanten Wert in ein Register zu laden.
Beispiel: mov x0, #42
Variablen Werte laden
Verwenden Sie ldr, um einen Wert aus dem Speicher in ein Register zu laden.
Beispiel: ldr x0, [x1] //Vorausgesetzt, x1 enthält die Adresse des Werts
Registerwerte speichern
Verwenden Sie str, um einen Wert aus einem Register in den Speicher zu speichern.
Beispiel: str x0, [x1] //Vorausgesetzt, x1 enthält die Zieladresse im Speicher
Indem Sie diese grundlegenden Konzepte verstehen und anwenden, können Sie effektiv mit Werten in ARM64-Assembler arbeiten. Dieser Basisbaustein ist essenziell für die weitere Programmierung und Manipulation von Daten auf niedriger Ebene.
Relative Adressierung am PC
Warum relative Adressierung?
Relative Adressierung wird hauptsächlich aus folgenden Gründen verwendet:
- Portabilität: Programme können unabhängig vom Speicherort korrekt ausgeführt werden. Wenn der Programmcode verschoben wird (z.B. bei der Erstellung von Shared Libraries), kann er weiterhin korrekt auf Daten zugreifen.
- Effizienz: Relative Adressierung reduziert die Notwendigkeit für absolute Adressen und ermöglicht es dem Programm, geringeren Overhead und schnellere Ausführung zu haben.
- Größe und Umfang: ARM64 erlaubt eine relative Adressierung von ca. +/- 1 MB (19-Bit breit), was für viele Anwendungsfälle ausreichend ist und Speicher effizient nutzt.
Grenzen der relativen Adressierung
Die Hauptgrenze der relativen Adressierung liegt in der Reichweite:
- Bereich: Der relative Bereich zur Berechnung der Adresse ist auf ca. +/- 1 MB begrenzt. Für größere Bereiche müssen andere Techniken verwendet werden.
- Komplexität: Je komplexer das Programm, desto mehr Aufwand kann nötig sein, um sicherzustellen, dass alle relativen Adressen korrekt berechnet werden.
In diesem Abschnitt wird erklärt, wie Sie in einem ARM64-Bit-Assembler mit relativer Adressierung arbeiten können. Relatives Adressieren ermöglicht es dem Programm, Adressen im Verhältnis zum Program Counter (PC) zu berechnen. Dies ist nützlich, wenn Sie Variablen oder Datenstrukturen im Speicher des Programms ansprechen wollen.
Relative Adressierung mit ldr
Der Befehl ldr (Load Register) kann verwendet werden, um eine Adresse relativ zum aktuellen Program Counter (PC) zu laden. Eine übliche Art, dies zu tun, ist:
ldr x1, =hello
Hier wird die Adresse des Labels hello relativ zum Program Counter in das Register x1 geladen.
Datenabschnitt deklarieren
.data
hello:
.ascii "Hallo"
In diesem Beispiel enthält der Datenbereich eine Zeichenkette "Hallo" mit dem Label hello.
Direkte 64-Bit-Zuweisung
Eine direkte 64-Bit-Zuweisung kann folgendermaßen erfolgen:
ldr x1, =0x1234567890abcdef
Dies wird vom Assembler in zwei Anweisungen aufgelöst:
ldr x1, #8
.quad 0x1234567890abcdef
So wird die Immediate-Adresse in den Code eingefügt. Der Assembler wählt automatisch die effizienteste Methode, um die 64-Bit-Konstante in das Register zu laden.
Typen des ldr-Befehls es weiteren
Der ldr-Befehl kann verschiedene Typen von Daten laden:
| TYP | Bedeutung |
|---|---|
| B | Unsigned byte |
| SB | Signed byte |
| H | Unsigned halfword (16 bits) |
| SH | Signed halfword (16 bits) |
| SW | Signed word |
Ein Beispiel:
ldr x0, =MeineZahl
ldr x0, [x0]
Hier wird die Adresse MeineZahl in x0 geladen und anschließend wird der Inhalt, der an dieser Adresse steht, in x0 gelesen.
Datenabschnitt mit einer Zahl
.data
MeineZahl:
.quad 0x1234567fe
Dies definiert eine 64-Bit-Zahl im Datenbereich.
Store Register (str)
Der str-Befehl benutzt eine ähnliche Syntax, um Daten aus einem Register in den Speicher zu schreiben:
str{x_typ} xt, [xa]
Hier steht {x_typ} für einen der oben genannten Datentypen (B, SB, H, SH, SW) und xt und xa sind die Register für die Daten und die Adresse.
Alternativen zur relativen Adressierung
Es gibt mehrere Alternativen zur relativen Adressierung, die je nach Bedarf verwendet werden können:
- Absolute Adressierung: Direktes Angeben der vollständigen Adresse im Speicher. Dies ist weniger flexibel, da es spezifisch für eine Speicherort ist.
mov x1, #0x1000 ; Absolute Adresse laden
- Base + Offset Adressierung: Kombination einer Basisadresse mit einem Offset. Dies ist besonders nützlich für Arrays und Strukturen.
mov x1, baseAddress
add x1, x1, offset
PC-relative Adressierung mit Ladebefehl:
ldr x1, [pc, #offset]
Hierbei wird der Wert aus dem Speicher relativ zum Program Counter geladen.
- Indirekte Adressierung: Verwenden eines Registers, das die Adresse speichert.
mov x0, #address
ldr x1, [x0]
Indexsierung (Arrays)
Einführung in die Indizierung (Arrays) und Post-Indexierte Adressierung im ARM64-Bit-Assembler
In diesem Abschnitt wird erklärt, wie Sie im ARM64-Bit-Assembler mit Arrays und der Indizierung arbeiten können. Zudem wird die post-indexierte Adressierung besprochen.
PseudoCode und Entsprechendes in Assembler
PseudoCode:
dim a[10] as word
a[5] = 10 // Setze das 5. Element auf 10
x = a[8] // Schreibe das 8. Element nach x
for i = 1 to 10
a[i] = i * 8
next
Assembler-Code:
ldr x0, =MeinArray // Lade die Adresse von MeinArray nach x0
mov w1, #10 // Speichere 10 in w1
str w1, [x0, #(4*4)] // Speichere w1 in das 5. Element (Beginn ist 0) (Word = 4 Byte)
ldr w1, [x0, #(7*4)] // Lade das 8. Element nach w1
mov w2, #1 // i = 1
forloop:
lsl w4, w2, #3 // i * 8
str w4, [x0, w2, lsl #2] // Speicher den Wert von w4 an die Adresse (x0 + i*4)
add w2, w2, #1 // i = i + 1
cmp w2, #10 // if i < 10
b.le forloop // Springen zurück zu forloop, wenn w2 <= 10
Schritt für Schritt Erklärung:
Array-Adresse laden:
ldr x0, =MeinArray
Hier wird die Anfangsadresse des Arrays MeinArray in das Register x0 geladen.
Wert setzen:
mov w1, #10
str w1, [x0, #(4*4)] // 4*4 weil das 5. Element bei Index 4 liegt (0-basiert)
Der Wert 10 wird in das 5. Element des Arrays MeinArray geschrieben.
Wert lesen:
ldr w1, [x0, #(7*4)] // Lesen des 8. Elements (Index 7)
Der Wert des 8. Elements (Index 7) wird in das Register w1 geladen.
Schleife, um den Array zu füllen:
mov w2, #1
forloop:
lsl w4, w2, #3 // w4 = w2 * 8 (Linksverschiebung um 3 Bits)
str w4, [x0, w2, lsl #2] // Schreiben des Wertes in das Array, Adresse ist (x0 + w2*4)
add w2, w2, #1 // w2 = w2 + 1
cmp w2, #10 // Vergleichen ob w2 < 10
b.le forloop // Wiederholen, bis w2 > 10
Post-indexierte Adressierung
Post-indexierte Adressierung ist eine Technik, bei der ein Register nicht nur verwendet wird, um eine Adresse zu bestimmen, sondern danach auch modifiziert wird. Hier ein Beispiel:
ldr x1, [x2], #4 // Lese den Wert von der Adresse in x2 nach x1 und erhöhe x2 um 4
In diesem Fall wird die ursprüngliche Adresse verwendet, um den Wert zu laden, und dann wird die Adresse um 4 erhöht.
128-Bit Laden und Speichern mit ldp und stp
Die Befehle ldp (load pair) und stp (store pair) können verwendet werden, um 128-Bit-Daten (zwei 64-Bit-Register) zu laden und zu speichern:
- Laden
ldr x1, =GrosseZahl
ldp x2, x3, [x1] // Lese 128 Bit (in x2 und x3), beginnend bei der Adresse in x1
- Speichern
stp x2, x3, [x1] // Speichere 128 Bit (x2 und x3) an die Adresse in x1
- Datenabschnitt
.data
GrosseZahl:
.octa 0x12345678901234567890123456789012
Diese Anweisungen sind besonders nützlich für Manipulationen an großen Datenmengen und erlauben effizientere Speicheroperationen.
Zusammenfassung
Indizierung erlaubt es, auf Elemente eines Arrays zuzugreifen und sie zu manipulieren. Relative und post-indexierte Adressierung bieten Methoden, um Speicher effizient zu verwenden. Das Laden und Speichern mit ldp und stp ermöglicht die Handhabung von 128-Bit-Daten, um Speicheroperationen schneller und kompakter zu gestalten.