Assemblersprache - String-Verarbeitung

Die String-Verarbeitung ist eine häufige Anforderung in der Programmierung.

Der Umgang mit Strings in Assembler bedeutet die direkte Manipulation von Byteweihlen im Speicher; es ist hardwarenäher, aber auch flexibler.


Definition und Speicherung von Strings

In NASM sind Strings im Wesentlichen Byteströme und können mit der Pseudoanweisung DB definiert werden:

Beispiel

; String-Definitionsmethoden

section .data
    ; Methode 1: Standard-String
    str1 db 'Hello, example!', 0     ; C-Stil: mit Null-Byte abgeschlossen

    ; Methode 2: mit Zeilenumbruch
    str2 db 'Line1', 0xA, 'Line2', 0xA

    ; Methode 3: Backticks unterstützen Escape-Zeichen
    str3 db `hello\nworld\n`, 0     ; Escape-Sequenzen automatisch konvertieren

    ; Methode 4: Zeichen für Zeichen definieren
    str4 db 'A', 'B', 'C', 'D', 0

    ; Methode 5: dup verwenden, um wiederholte Zeichen zu erzeugen
    border db 40 dup('-')           ; 40 Bindestriche

    ; String-Längenberechnung (zur Kompilierzeit)
    str1_len equ $ - str1           ; Einschließlich des abschließenden Null-Bytes

Berechnung der String-Länge

Es gibt zwei Möglichkeiten, die String-Länge zu ermitteln: Berechnung zur Kompilierzeit (EQU) und Laufzeitberechnung (Durchlaufen bis zum Null-Byte):

Beispiel

; Dateipfad: strlen_demo.asm
; Zwei Wege zur Berechnung der String-Länge

section .data
    ; Methode A: Berechnung zur Kompilierzeit (für konstante Strings)
    msg1 db 'Hello, EXAMPLE!', 0xA
    msg1_len equ $ - msg1          ; Automatisch berechnet zur Kompilierzeit

    ; Methode B: Null-terminierter String (Laufzeitberechnung)
    msg2 db 'Find my length', 0    ; Endet mit 0

section .text
    global _start

_start:
    ; Methode A: Die zur Kompilierzeit berechnete Länge direkt verwenden
    mov eax, 4
    mov ebx, 1
    mov ecx, msg1
    mov edx, msg1_len              ; Konstante direkt verwenden
    int 0x80

    ; Methode B: Länge eines null-terminierten Strings zur Laufzeit berechnen
    mov esi, msg2                  ; esi zeigt auf den Anfang des Strings
    mov ecx, 0                     ; Zähler

strlen_loop:
    cmp byte [esi], 0              ; Ist das aktuelle Byte null?
    je  strlen_done                ; Ja, Ende
    inc ecx                        ; Zähler + 1
    inc esi                        ; Zeiger + 1
    jmp strlen_loop

strlen_done:
    ; ecx enthält nun die String-Länge (ohne Null-Byte)
    mov eax, 4
    mov ebx, 1
    mov ecx, msg2
    mov edx, ecx                   ; Berechnete Länge verwenden
    ; Hier ist ein Problem: ecx wird überschrieben, sollte vorher gesichert werden
    ; Korrektes Vorgehen: ecx pushen und dann nach edx poppen

    mov eax, 1
    mov ebx, 0
    int 0x80

String-Kopie

Verwenden Sie eine Schleife zum Byteweisen Kopieren oder x86-String-BefehleMOVSB:

Beispiel

; Dateipfad: strcpy_demo.asm
; Zwei Implementierungen des String-Kopierens

section .data
    src db 'example source string', 0
    src_len equ $ - src

section .bss
    dest_manual resb 64             ; Manuelles Ziel zum Kopieren
    dest_fast resb 64               ; Schnelles Ziel zum Kopieren

section .text
    global _start

_start:
    ; Methode A: Manuelles Bytweises Kopieren
    mov esi, src                    ; Quelladresse
    mov edi, dest_manual            ; Zieladresse
    mov ecx, src_len                ; Byte-Anzahl

copy_loop:
    mov al, [esi]                   ; Ein Byte lesen
    mov [edi], al                   ; Ein Byte schreiben
    inc esi                         ; Quellzeiger++
    inc edi                         ; Zielzeiger++
    loop copy_loop

    ; Methode B: String-Operationen verwenden (schneller)
    cld                             ; Richtungs-Flag löschen (DF=0, Vorwärtskopieren)
    mov esi, src                    ; Quelladresse (ESI)
    mov edi, dest_fast              ; Zieladresse (EDI)
    mov ecx, src_len                ; Byte-Anzahl (ECX)
    rep movsb                       ; movsb ecx-mal wiederholt ausführen
    ; rep movsb: while(ecx>0) { [edi]=[esi]; esi++; edi++; ecx--; }

    ; Überprüfen: Die beiden Kopierergebnisse ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, dest_manual
    mov edx, src_len
    int 0x80

    mov eax, 4
    mov ebx, 1
    mov ecx, dest_fast
    mov edx, src_len
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

REP MOVSBEs ist die klassischste Methode zum Kopieren von Strings auf x86. Auf modernen CPUs kann jedoch eine manuelle Kopierschleife mit Schleifenabrollung (Loop Unrolling) schneller sein als REP MOVSB, da moderne CPUs eine bessere Pipeline-Optimierung für einfache Operationen bieten.


String-Vergleich

Verwenden SieCMPSBzusammen mitREPEByteweiser Vergleich (wiederholen bis ungleich):

Beispiel

; Dateipfad: strcmp_demo.asm
; Vergleichen, ob zwei Strings gleich sind

section .data
    str_a db 'example', 0
    str_b db 'example', 0
    str_c db 'EXAMPLE', 0
    eq_msg db 'Strings are equal', 0xA
    eq_len equ $ - eq_msg
    ne_msg db 'Strings are NOT equal', 0xA
    ne_len equ $ - ne_msg

section .text
    global _start

_start:
    cld                             ; Vorwärtsvergleich
    mov esi, str_a                  ; Erster String
    mov edi, str_b                  ; Zweite Zeichenkette
    mov ecx, 7                      ; Bis zu 7 Bytes vergleichen (einschließlich Null)

    repe cmpsb                      ; Vergleich wiederholen, bis ungleich oder ecx=0
    ; repe: fortfahren, wenn ZF=1 (gleich) und ecx>0
    ; cmpsb: [esi] und [edi] vergleichen, dann esi++, edi++

    je  strings_equal               ; Wenn ZF=1, sind alle Bytes gleich

    ; Nicht gleich
    mov eax, 4
    mov ebx, 1
    mov ecx, ne_msg
    mov edx, ne_len
    int 0x80
    jmp compare_next

strings_equal:
    mov eax, 4
    mov ebx, 1
    mov ecx, eq_msg
    mov edx, eq_len
    int 0x80

compare_next:
    ; str_a und str_c vergleichen (unterschiedliche Groß-/Kleinschreibung)
    cld
    mov esi, str_a
    mov edi, str_c
    mov ecx, 7
    repe cmpsb
    jne not_equal_2

    mov eax, 4
    mov ebx, 1
    mov ecx, eq_msg
    mov edx, eq_len
    int 0x80
    jmp exit

not_equal_2:
    mov eax, 4
    mov ebx, 1
    mov ecx, ne_msg
    mov edx, ne_len
    int 0x80

exit:
    mov eax, 1
    mov ebx, 0
    int 0x80

Zusammenfassung der Zeichenketten-Befehle

AnweisungFunktionVerwendete Register
MOVSBByte kopieren: [EDI] = [ESI]ESI=Quelle, EDI=Ziel, ECX=Anzahl, DF=Richtung
MOVSWWort kopieren (2 Bytes)Wie oben
MOVSDDoppelwort kopieren (4 Bytes)Wie oben
STOSBByte speichern: [EDI] = ALEDI=Ziel, AL=Wert, ECX=Anzahl
LODSBByte laden: AL = [ESI]ESI=Quelle
CMPSBByte vergleichen: [ESI] - [EDI]ESI=Quelle, EDI=Ziel, ECX=Anzahl
SCASBByte scannen: AL - [EDI]EDI=Ziel, AL=Suchwert, ECX=Anzahl

Beispiel zur Konvertierung der Groß-/Kleinschreibung

Beispiel

; Dateipfad: case_convert.asm
; Kleinbuchstaben in einer Zeichenkette in Großbuchstaben umwandeln

section .data
    msg db 'Hello, example! Welcome to Assembly.', 0xA
    len equ $ - msg

section .text
    global _start

_start:
    ; Ursprüngliche Zeichenkette ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, msg
    mov edx, len
    int 0x80

    ; Konvertierung: Zeichenkette durchlaufen, Kleinbuchstabe -> Großbuchstabe
    mov esi, msg                    ; Auf den Anfang der Zeichenkette zeigen
    mov ecx, len                    ; Schleifenzähler

convert_loop:
    mov al, [esi]                   ; Zeichen lesen
    cmp al, 'a'                     ; Ist es >= 'a'
    jb  next_char                   ; Kleiner als 'a', überspringen
    cmp al, 'z'                     ; Ist es <= 'z'
    ja  next_char                   ; Größer als 'z', überspringen

    ; Klein zu Groß: 'a'(97) - 'A'(65) = 32
    sub al, 32                      ; In Großbuchstaben umwandeln
    mov [esi], al                   ; Zurückschreiben

next_char:
    inc esi
    loop convert_loop

    ; Konvertierte Zeichenkette ausgeben
    mov eax, 4
    mov ebx, 1
    mov ecx, msg
    mov edx, len
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

Ausführungsergebnis:

$ nasm -f elf32 case_convert.asm -o case_convert.o
$ ld -m elf_i386 case_convert.o -o case_convert
$ ./case_convert
Hello, example! Welcome to Assembly.
HELLO, EXAMPLE! WELCOME TO ASSEMBLY.

Das Richtungsflag DF bestimmt die Richtung von Zeichenkettenoperationen: Wenn DF=0, inkrementieren ESI/EDI (vorwärts), wenn DF=1, dekrementieren sie (rückwärts). Verwenden SieCLDum DF zu löschen, undSTDum DF zu setzen. Stellen Sie vor dem Aufruf von C-Funktionen oder Systemaufrufen sicher, dass DF=0 ist (cdecl-Anforderung).

Weitere Erweiterungen