Addieren und Subtrahieren

Aus C und Assembler mit Raspberry
Version vom 26. November 2024, 14:17 Uhr von Satyria (Diskussion | Beiträge) (→‎Subtrahieren zweier 128-Bit-Zahlen)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Addieren und Subtrahieren in ARM64-Assembler

In diesem Abschnitt werden wir einfache arithmetische Operationen wie Addieren und Subtrahieren in ARM64-Assembler beschreiben. Dabei werden wir die Befehle add und sub genauer betrachten, den Umgang mit Überlauf und Unterlauf erläutern und zeigen, wie man mit mehreren Registern und direkten Werten arbeitet.

Einfache arithmetische Operationen: add und sub

Der add-Befehl

Der add-Befehl wird verwendet, um zwei Werte zu addieren und das Ergebnis in einem Register zu speichern. Die grundlegende Syntax lautet:

add <Zielregister>, <Quellregister1>, <Quellregister2>

Beispiele:

Addieren der Inhalte von zwei Registern:

add x0, x1, x2    // x0 = x1 + x2

Addieren eines Registers und eines direkten Werts (Immediates):

add x0, x1, #5    // x0 = x1 + 5

Der sub-Befehl

Der sub-Befehl wird verwendet, um einen Wert von einem anderen zu subtrahieren und das Ergebnis in einem Register zu speichern. Die grundlegende Syntax lautet:

sub <Zielregister>, <Quellregister1>, <Quellregister2>

Beispiele:

Subtrahieren der Inhalte von zwei Registern:

sub x0, x1, x2    // x0 = x1 - x2

Subtrahieren eines direkten Werts von einem Register:

sub x0, x1, #5    // x0 = x1 - 5

Umgang mit Überlauf und Unterlauf

Überlauf und Unterlauf können auftreten, wenn das Ergebnis einer arithmetischen Operation größer oder kleiner als der darstellbare Wertebereich des Registers ist. ARM64-Assembler bietet spezielle Befehle und Bedingungen, um Überlauf und Unterlauf zu erkennen.

Überlauf erkennen

Der Befehl adds funktioniert wie add, setzt jedoch die Bedingungsflags einschließlich des Überlauf-Flags (V):

adds x0, x1, x2    // x0 = x1 + x2, setzt Bedingungsflags
bvs overflow       // Verzweigt zu "overflow", wenn ein Überlauf aufgetreten ist

Unterlauf erkennen

Der Befehl subs funktioniert wie sub, setzt jedoch die Bedingungsflags einschließlich des Unterlauf-Flags:

subs x0, x1, x2    // x0 = x1 - x2, setzt Bedingungsflags
bvc no_overflow    // Verzweigt zu "no_overflow", wenn kein Überlauf aufgetreten ist

Beispielcode zur Erkennung von Überlauf:

.global _start

_start:
    mov x1, #9223372036854775807   // Setze x1 auf den größten positiven Wert (2^63 - 1)
    adds x0, x1, #1                // Versuche, 1 zu x1 zu addieren
    bvs overflow                   // Verzweigt zu "overflow", wenn ein Überlauf aufgetreten ist

no_overflow:
    // Normaler Programmverlauf
    // Code hierhin
    b end

overflow:
    // Behandeln des Überlaufs
    // Code hierhin

end:
    mov x8, #93                    // exit system call
    svc 0                          // system call

Arbeiten mit mehreren Registern und direkten Werten

Sie können Additions- und Subtraktionsbefehle verwenden, um komplexere Berechnungen durchzuführen, indem Sie mit mehreren Registern und direkten Werten arbeiten.

Beispiel 1: Addieren und Subtrahieren mehrerer Register

.global _start

_start:
    mov x1, #10      // Setze x1 auf 10
    mov x2, #20      // Setze x2 auf 20
    mov x3, #30      // Setze x3 auf 30

    add x4, x1, x2   // x4 = x1 + x2  (10 + 20 = 30)
    sub x5, x4, x3   // x5 = x4 - x3  (30 - 30 = 0)

    // Addieren und Subtrahieren von direkten Werten
    add x6, x5, #50  // x6 = x5 + 50  (0 + 50 = 50)
    sub x7, x6, #20  // x7 = x6 - 20  (50 - 20 = 30)

    // Programm beenden
    mov x8, #93      // exit system call
    svc 0            // system call

Diese Beispiele zeigen, wie man einfache arithmetische Operationen in ARM64-Assembler ausführt und sich mit Überlauf und Unterlauf bei diesen Operationen befasst.

Umgang mit Überlauf und Unterlauf bei 128-Bit-Arithmetik in ARM64-Assembler

Um Überlauf und Unterlauf bei 128-Bit-Arithmetik in ARM64-Assembler zu handhaben, können wir zwei 64-Bit-Register als ein einzelnes 128-Bit-Register behandeln. Dies bedeutet, dass man nacheinander die unteren 64-Bit (Least Significant Bits, LSB) und die oberen 64-Bit (Most Significant Bits, MSB) addieren oder subtrahieren muss und dabei den Übertrag (Carry) berücksichtigen muss.

Addieren zweier 128-Bit-Zahlen

Nehmen wir an, x0 und x1 enthalten die unteren und oberen 64-Bit der ersten Zahl, und x2 und x3 enthalten die unteren und oberen 64-Bit der zweiten Zahl. Das Ergebnis soll in x4 und x5 stehen.

Addiere die unteren 64-Bit und speichere das Ergebnis in x4. Verwende adds, um Bedingungsflags einschließlich Carry zu setzen. Addiere die oberen 64-Bit zusammen mit dem Carry, das vom vorherigen Addieren übrig bleibt, und speichere das Ergebnis in x5.

.global _start

_start:
    // Beispielwerte in den Registern setzen
    // 128-Bit Zahl 1: 0xFFFFFFFFFFFFFFFF0000000000000001
    mov x0, #0x0000000000000001  // LSB von Zahl 1
    mov x1, #0xFFFFFFFFFFFFFFFF  // MSB von Zahl 1

    // 128-Bit Zahl 2: 0x0000000000000001FFFFFFFFFFFFFFFF
    mov x2, #0xFFFFFFFFFFFFFFFF  // LSB von Zahl 2
    mov x3, #0x0000000000000001  // MSB von Zahl 2

    // Addiere die unteren 64-Bit
    adds x4, x0, x2              // x4 = x0 + x2, setzt das Carry-Flag falls ein Überlauf auftritt

    // Addiere die oberen 64-Bit und das Carry
    adc x5, x1, x3               // x5 = x1 + x3 + Carry-Flag

    // Programm beenden
    mov x8, #93                  // exit system call
    svc 0                        // system call

Subtrahieren zweier 128-Bit-Zahlen

Für die Subtraktion folgt das Vorgehen einem ähnlichen Muster.

Subtrahiere die unteren 64-Bit und speichere das Ergebnis in x4. Verwende subs, um Bedingungsflags einschließlich Borrow zu setzen. Subtrahiere die oberen 64-Bit zusammen mit dem Borrow, das von der vorherigen Subtraktion übrig bleibt, und speichere das Ergebnis in x5.

.global _start

_start:
    // Beispielwerte in den Registern setzen
    // 128-Bit Zahl 1: 0x0000000000000002FFFFFFFFFFFFFFFE
    mov x0, #0xFFFFFFFFFFFFFFFE  // LSB von Zahl 1
    mov x1, #0x0000000000000002  // MSB von Zahl 1

    // 128-Bit Zahl 2: 0x0000000000000001FFFFFFFFFFFFFFFF
    mov x2, #0xFFFFFFFFFFFFFFFF  // LSB von Zahl 2
    mov x3, #0x0000000000000001  // MSB von Zahl 2

    // Subtrahiere die unteren 64-Bit
    subs x4, x0, x2              // x4 = x0 - x2, setzt das Borrow-Flag falls ein Unterlauf auftritt

    // Subtrahiere die oberen 64-Bit und das Borrow
    sbc x5, x1, x3               // x5 = x1 - x3 - Borrow-Flag

    // Programm beenden
    mov x8, #93                  // exit system call
    svc 0                        // system call

Erklärung der Befehle

  • adds: Addiert zwei Register und setzt die Bedingungsflags (Carry-Flag bei Überlauf).
  • adc: Addiert zwei Register zusammen mit dem Carry-Flag (Falls gesetzt).
  • subs: Subtrahiert zwei Register und setzt die Bedingungsflags (Borrow-Flag bei Unterlauf).
  • sbc: Subtrahiert zwei Register zusammen mit dem Borrow-Flag (Falls gesetzt).

Zusammenfassung

Durch die Verwendung der oben genannten Befehle können Sie 128-Bit-Zahlen, die in Paaren von 64-Bit-Registern gespeichert sind, addieren und subtrahieren. Diese Technik ermöglicht es Ihnen, den Überlauf oder Unterlauf korrekt zu behandeln, indem Sie die Carry- und Borrow-Flags zwischen den Operationen berücksichtigen. Indem Sie diese Methoden verstehen und anwenden, erhalten Sie die Fähigkeit, mit großen numerischen Werten auf der ARM-Architektur effizient zu arbeiten.