EMZETT.
Login

Kurz: Register sind winzige, superschnelle Speicherplätze direkt im Prozessor. Ein x86-64-Prozessor hat 16 allgemeine 64-Bit-Register:

Teil des Kurses Assembler (x86-64)

Kapitel 2 von 8 im Kurs Assembler (x86-64). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Die Register

Register sind winzige, superschnelle Speicherplätze direkt im Prozessor. Ein x86-64-Prozessor hat 16 allgemeine 64-Bit-Register:

RegisterÜblicher Zweck
raxErgebnis, Systemaufrufnummer, Rückgabewert
rbxallgemein (muss von Funktionen erhalten werden)
rcxZähler
rdxDaten, zweites Ergebnisregister bei mul/div
rsi, rdiQuelle und Ziel (Argument 2 und 1)
rspStackzeiger (nicht anders verwenden!)
rbpBasiszeiger des Stack-Rahmens
r8 bis r15zusätzliche Register
ripBefehlszeiger (nächster Befehl)

Jedes Register hat Teilregister für kleinere Breiten:

64 Bit32 Bit16 Bit8 Bit
raxeaxaxal (niedrig), ah (hoch)
rbxebxbxbl
rcxecxcxcl
r8r8dr8wr8b

mov, add, sub und Ausgabe

Um Zahlen zu sehen, brauchen wir eine Ausgaberoutine. Sie wandelt die Zahl in rax in Ziffern um (Division durch 10, Rest + '0') und schreibt sie per Systemaufruf. Ab hier steht sie unter jedem Beispiel; konzentriere dich auf den Teil darüber.

section .bss
    puffer resb 32
 
section .text
    global _start
 
_start:
    mov rax, 40          ; Zahl in ein Register laden
    call ausgabe_zahl
 
    mov rax, 5
    add rax, 37          ; rax = rax + 37
    call ausgabe_zahl
 
    mov rbx, 100
    mov rax, rbx         ; Register nach Register kopieren
    sub rax, 58          ; rax = rax - 58
    call ausgabe_zahl
 
    mov rax, 41
    inc rax              ; +1
    call ausgabe_zahl
    dec rax              ; -1
    dec rax
    call ausgabe_zahl
 
    mov rax, 5
    neg rax              ; Vorzeichen umkehren (Zweierkomplement)
    neg rax
    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:

40
42
42
42
40
5

Multiplikation und Division

imul multipliziert, div teilt (Dividend in rdx:rax, Quotient in rax, Rest in rdx):

section .bss
    puffer resb 32
 
section .text
    global _start
 
_start:
    mov rax, 6
    mov rbx, 7
    imul rax, rbx        ; rax = 6 * 7
    call ausgabe_zahl
 
    imul rax, rax, 10    ; rax = rax * 10  (Dreioperanden-Form)
    call ausgabe_zahl
 
    mov rax, 100
    xor rdx, rdx         ; Dividenden-Hälfte rdx muss 0 sein
    mov rbx, 7
    div rbx              ; rax = 100 / 7 = 14, rdx = 100 % 7 = 2
    call ausgabe_zahl    ; Quotient
    mov rax, rdx
    call ausgabe_zahl    ; Rest
 
    mov rax, 1
    shl rax, 10          ; Linksschieben = mal 2^10
    call ausgabe_zahl
    shr rax, 4           ; Rechtsschieben = durch 2^4
    call ausgabe_zahl
 
    mov rax, 0b1100
    and rax, 0b1010      ; bitweises UND
    call ausgabe_zahl
    mov rax, 0b1100
    or rax, 0b0011       ; bitweises ODER
    call ausgabe_zahl
    mov rax, 0b1100
    xor rax, 0b1010      ; bitweises XOR
    call ausgabe_zahl
    mov rax, 5
    not rax              ; alle Bits umdrehen
    and rax, 0xFF
    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
420
14
2
1024
64
8
15
6
250

Hinweis

xor rdx, rdx ist der übliche Trick, ein Register auf 0 zu setzen: kürzer und schneller als mov rdx, 0. Division durch 0 löst einen Prozessorfehler (Absturz) aus.

Flags: das Gedächtnis des Prozessors

Rechenbefehle setzen Flags (Bits im Statusregister RFLAGS), die spätere Sprünge auswerten:

FlagNameGesetzt, wenn …
ZFZeroErgebnis ist 0
SFSignErgebnis ist negativ
CFCarryÜbertrag bei vorzeichenloser Rechnung
OFOverflowÜberlauf bei vorzeichenbehafteter Rechnung
section .bss
    puffer resb 32
 
section .text
    global _start
 
_start:
    mov rax, 5
    sub rax, 5           ; ZF = 1 (Ergebnis null)
    setz al              ; al = 1, wenn ZF gesetzt
    movzx rax, al
    call ausgabe_zahl
 
    mov rax, 3
    cmp rax, 5           ; rechnet 3 - 5 nur für die Flags
    setl al              ; 1, wenn kleiner (vorzeichenbehaftet)
    movzx rax, al
    call ausgabe_zahl
 
    mov rax, 0xFFFFFFFFFFFFFFFF   ; größte vorzeichenlose Zahl
    add rax, 1                    ; Überlauf: CF = 1, Ergebnis 0
    setc bl
    movzx rbx, bl
    mov rax, rbx
    call ausgabe_zahl
 
    mov rax, -1
    shr rax, 63          ; oberstes Bit zeigt das Vorzeichen
    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:

1
1
1
1

Merke

  • Register sind die Arbeitsplätze des Prozessors: rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8 bis r15
  • Teilregister: eax (32 Bit), ax (16), al (8)
  • Rechnen: add, sub, inc, dec, imul, div, neg; Bits: and, or, xor, not, shl, shr
  • div nutzt rdx:rax, Quotient in rax, Rest in rdx
  • Flags (ZF, SF, CF, OF) halten das Ergebnis fest und steuern Sprünge

Übungsaufgabe

Berechne (7 + 5) * 3 - 4 mit Registern und gib das Ergebnis aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Was ist Assembler? Das erste Programm

Weiter: Vergleichen, Springen und Schleifen

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