Die Register
Register sind winzige, superschnelle Speicherplätze direkt im Prozessor. Ein x86-64-Prozessor hat 16 allgemeine 64-Bit-Register:
| Register | Üblicher Zweck |
|---|---|
rax | Ergebnis, Systemaufrufnummer, Rückgabewert |
rbx | allgemein (muss von Funktionen erhalten werden) |
rcx | Zähler |
rdx | Daten, zweites Ergebnisregister bei mul/div |
rsi, rdi | Quelle und Ziel (Argument 2 und 1) |
rsp | Stackzeiger (nicht anders verwenden!) |
rbp | Basiszeiger des Stack-Rahmens |
r8 bis r15 | zusätzliche Register |
rip | Befehlszeiger (nächster Befehl) |
Jedes Register hat Teilregister für kleinere Breiten:
| 64 Bit | 32 Bit | 16 Bit | 8 Bit |
|---|---|---|---|
rax | eax | ax | al (niedrig), ah (hoch) |
rbx | ebx | bx | bl |
rcx | ecx | cx | cl |
r8 | r8d | r8w | r8b |
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
retAusgabe
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
retAusgabe
42 420 14 2 1024 64 8 15 6 250
Flags: das Gedächtnis des Prozessors
Rechenbefehle setzen Flags (Bits im Statusregister RFLAGS), die spätere Sprünge auswerten:
| Flag | Name | Gesetzt, wenn ... |
|---|---|---|
ZF | Zero | Ergebnis ist 0 |
SF | Sign | Ergebnis ist negativ |
CF | Carry | Übertrag bei vorzeichenloser Rechnung |
OF | Overflow | Ü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
retAusgabe
1 1 1 1
Merke
- Register sind die Arbeitsplätze des Prozessors:
rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp,r8bisr15 - Teilregister:
eax(32 Bit),ax(16),al(8) - Rechnen:
add,sub,inc,dec,imul,div,neg; Bits:and,or,xor,not,shl,shr divnutztrdx:rax, Quotient inrax, Rest inrdx- Flags (
ZF,SF,CF,OF) halten das Ergebnis fest und steuern Sprünge
Aufgabe
Berechne (7 + 5) * 3 - 4 mit Registern und gib das Ergebnis aus.