EMZETT.
Login

Kurz: Der Arbeitsspeicher besteht aus nummerierten Bytes. Ein Label in .data steht für eine Adresse; eckige Klammern […] bedeuten Inhalt an dieser Adresse (wie ein Zeiger dereferenzieren).

Teil des Kurses Assembler (x86-64)

Kapitel 4 von 8 im Kurs Assembler (x86-64) (Abschnitt „Speicher“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Daten im Speicher

Der Arbeitsspeicher besteht aus nummerierten Bytes. Ein Label in .data steht für eine Adresse; eckige Klammern [...] bedeuten Inhalt an dieser Adresse (wie ein Zeiger dereferenzieren).

DirektiveGrößeBeispiel
db1 Bytezeichen db 'A'
dw2 Bytekurz dw 1000
dd4 Bytemittel dd 100000
dq8 Bytegross dq 9000000000
resb/resq (in .bss)reservierenpuffer resb 64
section .data
    zahl   dq 41
    liste  dq 10, 20, 30, 40, 50
    bytes  db 1, 2, 3
    text   db "Assembler", 0
 
section .bss
    puffer resb 32
    ergebnis resq 1
 
section .text
    global _start
 
_start:
    mov rax, [zahl]           ; Inhalt laden
    inc rax
    mov [zahl], rax           ; zurückschreiben
    mov rax, [zahl]
    call ausgabe_zahl
 
    mov rax, liste            ; Adresse (ohne Klammern)
    mov rbx, [rax]            ; erstes Element
    mov rax, [liste + 8]      ; zweites Element (jedes ist 8 Byte)
    call ausgabe_zahl
 
    lea rsi, [liste]          ; lea berechnet die Adresse, ohne zu lesen
    mov rax, [rsi + 2*8]      ; drittes Element (Basis + Index*Größe)
    call ausgabe_zahl
 
    movzx rax, byte [bytes + 2]   ; 1 Byte laden und auf 64 Bit erweitern
    call ausgabe_zahl
 
    mov qword [ergebnis], 123
    mov rax, [ergebnis]
    call ausgabe_zahl
 
    mov rax, text
    movzx rax, byte [text]        ; erster Buchstabe 'A'
    call ausgabe_zahl
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    mov rcx, puffer + 20        ; Ende des Puffers
    mov byte [rcx], 10          ; Zeilenumbruch ans Ende
    mov rbx, 10
.schleife:
    xor rdx, rdx
    div rbx                     ; RAX = RAX / 10, RDX = Rest
    add dl, '0'                 ; Ziffer in ASCII umwandeln
    dec rcx
    mov [rcx], dl
    test rax, rax
    jnz .schleife
    mov rax, 1                  ; write
    mov rdi, 1
    mov rsi, rcx
    lea rdx, [puffer + 21]
    sub rdx, rcx
    syscall
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret

Ausgabe:

42
20
30
3
123
65

Die allgemeine Adressierung lautet [Basis + Index*Faktor + Verschiebung] mit Faktor 1, 2, 4 oder 8. Bei einem Zugriff auf Speicher muss die Größe klar sein: Mit einem Register ergibt sie sich aus dessen Breite (mov eax, [x] lädt 4 Byte), sonst schreibst du byte, word, dword oder qword.

Arrays durchlaufen

section .data
    zahlen dq 5, 3, 9, 1, 7, 12, 4
    anzahl equ 7
 
section .bss
    puffer resb 32
 
section .text
    global _start
 
_start:
    ; Summe und Maximum
    xor rax, rax              ; Summe
    mov rbx, [zahlen]         ; Maximum (erstes Element)
    xor rcx, rcx              ; Index
.schleife:
    mov rdx, [zahlen + rcx*8]
    add rax, rdx
    cmp rdx, rbx
    jle .weiter
    mov rbx, rdx
.weiter:
    inc rcx
    cmp rcx, anzahl
    jl .schleife
    call ausgabe_zahl         ; Summe
    mov rax, rbx
    call ausgabe_zahl         ; Maximum
 
    ; Bubblesort
    mov r8, anzahl - 1
.aussen:
    xor rcx, rcx
.innen:
    mov rax, [zahlen + rcx*8]
    mov rdx, [zahlen + rcx*8 + 8]
    cmp rax, rdx
    jle .ok
    mov [zahlen + rcx*8], rdx
    mov [zahlen + rcx*8 + 8], rax
.ok:
    inc rcx
    cmp rcx, r8
    jl .innen
    dec r8
    jnz .aussen
 
    xor rcx, rcx
.ausgabe:
    mov rax, [zahlen + rcx*8]
    call ausgabe_zahl
    inc rcx
    cmp rcx, anzahl
    jl .ausgabe
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    mov rcx, puffer + 20        ; Ende des Puffers
    mov byte [rcx], 10          ; Zeilenumbruch ans Ende
    mov rbx, 10
.schleife:
    xor rdx, rdx
    div rbx                     ; RAX = RAX / 10, RDX = Rest
    add dl, '0'                 ; Ziffer in ASCII umwandeln
    dec rcx
    mov [rcx], dl
    test rax, rax
    jnz .schleife
    mov rax, 1                  ; write
    mov rdi, 1
    mov rsi, rcx
    lea rdx, [puffer + 21]
    sub rdx, rcx
    syscall
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret

Ausgabe:

41
12
1
3
4
5
7
9
12

Zeichenketten

Eine Zeichenkette ist eine Folge von Bytes (ASCII-Codes). Hier die klassische Aufgabe: Länge bestimmen, Großschreibung, Umkehren:

section .data
    text    db "Hallo Assembler", 0
    nl      db 10
 
section .bss
    puffer  resb 32
    kopie   resb 64
 
section .text
    global _start
 
_start:
    ; Länge bis zum Nullbyte
    xor rcx, rcx
.laenge:
    cmp byte [text + rcx], 0
    je .laenge_fertig
    inc rcx
    jmp .laenge
.laenge_fertig:
    mov r8, rcx               ; Länge merken
    mov rax, r8
    call ausgabe_zahl
 
    ; Kopie in Großbuchstaben
    xor rcx, rcx
.gross:
    mov al, [text + rcx]
    cmp al, 'a'
    jb .behalten
    cmp al, 'z'
    ja .behalten
    sub al, 32                ; 'a' (97) → 'A' (65)
.behalten:
    mov [kopie + rcx], al
    inc rcx
    cmp rcx, r8
    jb .gross
    mov byte [kopie + r8], 10
 
    mov rax, 1                ; write(1, kopie, länge+1)
    mov rdi, 1
    mov rsi, kopie
    lea rdx, [r8 + 1]
    syscall
 
    ; Rückwärts ausgeben
    mov rcx, r8
.rueck:
    dec rcx
    mov rax, 1
    mov rdi, 1
    lea rsi, [text + rcx]
    mov rdx, 1
    push rcx
    syscall
    pop rcx
    test rcx, rcx
    jnz .rueck
    mov rax, 1
    mov rdi, 1
    mov rsi, nl
    mov rdx, 1
    syscall
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
    mov rcx, puffer + 20        ; Ende des Puffers
    mov byte [rcx], 10          ; Zeilenumbruch ans Ende
    mov rbx, 10
.schleife:
    xor rdx, rdx
    div rbx                     ; RAX = RAX / 10, RDX = Rest
    add dl, '0'                 ; Ziffer in ASCII umwandeln
    dec rcx
    mov [rcx], dl
    test rax, rax
    jnz .schleife
    mov rax, 1                  ; write
    mov rdi, 1
    mov rsi, rcx
    lea rdx, [puffer + 21]
    sub rdx, rcx
    syscall
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret

Ausgabe:

15
HALLO ASSEMBLER
relbmessA ollaH

Es gibt auch String-Befehle (movsb, stosb, lodsb, scasb, cmpsb) mit dem Präfix rep, die mit rsi/rdi/rcx ganze Blöcke bearbeiten:

section .data
    quelle db "Kopiere mich", 10
    laenge equ $ - quelle
 
section .bss
    ziel resb 32
 
section .text
    global _start
 
_start:
    mov rsi, quelle
    mov rdi, ziel
    mov rcx, laenge
    cld                        ; Richtung vorwärts
    rep movsb                  ; kopiert rcx Bytes von [rsi] nach [rdi]
 
    mov rax, 1
    mov rdi, 1
    mov rsi, ziel
    mov rdx, laenge
    syscall
 
    mov rdi, ziel              ; Speicher mit 'x' füllen
    mov al, 'x'
    mov rcx, 5
    rep stosb
    mov byte [ziel + 5], 10
    mov rax, 1
    mov rdi, 1
    mov rsi, ziel
    mov rdx, 6
    syscall
 
    mov rax, 60
    xor rdi, rdi
    syscall

Ausgabe:

Kopiere mich
xxxxx

Merke

  • [adresse] liest/schreibt Speicher, lea berechnet nur Adressen
  • Adressierung: [Basis + Index*Faktor + Verschiebung]
  • db/dw/dd/dq in .data, resb/resq in .bss
  • Arrays: Index mal Elementgröße; Strings: Bytes bis Nullbyte
  • rep movsb/stosb kopieren und füllen Blöcke

Übungsaufgabe

Schreibe eine Routine, die eine Zeichenkette in Kleinbuchstaben umwandelt.

Quiz zur Selbstkontrolle

?

  • Lädt den Inhalt der Speicherstelle zahl (richtig)
  • Lädt die Adresse von zahl
  • Springt nach zahl
  • Löscht zahl

Weiter im Kurs

Zurück: Vergleichen, Springen und Schleifen

Weiter: Der Stack und Funktionen

Alle Kapitel: Assembler (x86-64) im Überblick