Assembler-Kurs · Lektion 1

Register und der erste Befehl

Am Ende kennst du Register, den Befehl mov und hast ein vollständiges Programm gestartet und im Debugger Schritt für Schritt beobachtet. Voraussetzung: Asmonite ist entpackt, sonst nichts.

Architektur:
Video zu dieser Lektion folgt.

Die Idee: Register

Ein Prozessor rechnet nicht mit Zahlen im Hauptspeicher, sondern mit Zahlen in Registern: wenigen, winzigen und sehr schnellen Speicherplätzen direkt in der CPU. Ein Assemblerprogramm besteht zum großen Teil daraus, Zahlen in Register zu holen, dort etwas mit ihnen zu tun und sie wieder wegzuschreiben. Jedes Register hat einen Namen, den du im Programm schreibst.

Der i386 hat acht 32-Bit-Register für allgemeine Zwecke: eax, ebx, ecx, edx, esi, edi, ebp und esp. (esp ist der Stackzeiger, ebp meist der Rahmenzeiger; beide lässt du vorerst in Ruhe.) Dazu kommen der Befehlszeiger eip (er zeigt auf den nächsten Befehl) und die Flags (ab Lektion 3).

Der x64 hat sechzehn 64-Bit-Register: rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp und r8 bis r15. Die unteren 32 Bit eines Registers haben einen eigenen Namen: eax ist die untere Hälfte von rax, r8d die von r8. (rsp ist der Stackzeiger; lass ihn vorerst in Ruhe.) Dazu kommen der Befehlszeiger rip und die Flags (ab Lektion 3).

ARM 32 hat sechzehn 32-Bit-Register r0 bis r15. Die letzten drei haben Sonderrollen und zweite Namen: r13 ist sp (Stackzeiger), r14 ist lr (Link-Register: hier steht die Rücksprungadresse) und r15 ist pc (der Befehlszeiger). Für eigene Werte nimmst du r0 bis r12. In diesem Kurs steht ARM 32 für den Thumb-2-Befehlssatz (.arch thumb), den Windows on ARM benutzt.

ARM 64 hat 31 allgemeine 64-Bit-Register x0 bis x30. Die unteren 32 Bit eines Registers heißen w0 bis w30. x30 ist auch lr (hier steht die Rücksprungadresse). Dazu kommen der Stackzeiger sp und der Befehlszeiger pc; mit pc kannst du nicht wie mit einem Register rechnen.

Eines der Register ist besonders: Das Programm gibt sein Ergebnis in einem festen Register an den Aufrufer zurück. Bei einem Windows-Programm wird es zum Exit-Code, den du in der Kommandozeile oder im Debugger siehst.

ArchitekturRegister für das Ergebnis
i386eax
x64rax (eax ist seine untere Hälfte)
ARM 32r0
ARM 64x0 (w0 ist seine untere Hälfte)

Das kleinste Programm

Zwei Befehle genügen: eine Zahl in das Ergebnisregister schreiben und zurückkehren.

; Lesson 1: the smallest program. EAX = 1, then back to Windows: the exit code is 1.

.format pe console
.arch i386

.code
start:
  mov eax, 1
  ret
; Lesson 1: the smallest program. RAX = 1, then back to Windows: the exit code is 1.

.format pe console
.arch x64

.code
start:
  mov eax, 1
  ret
; Lesson 1: the smallest program. R0 = 1, then back to Windows: the exit code is 1.

.format pe console
.arch thumb

.code
start:
  movs r0, #1
  bx lr
; Lesson 1: the smallest program. X0 = 1, then back to Windows: the exit code is 1.

.format pe console
.arch arm64

.code
start:
  mov x0, #1
  ret

In Asmonite ausprobieren

  1. Starte Asmonite, lege eine neue Datei an und füge den Code oben ein (für die Architektur, die du gewählt hast). Speichere sie, zum Beispiel als erstes.asm.
  2. Drücke Strg+F9: der Assembler übersetzt das Programm. Im Ausgabefenster steht, was er gemacht hat; Fehler stehen dort mit Zeilennummer.
  3. Drücke F8: das Programm startet und hält am ersten Befehl. Rechts siehst du die Register. In der Quelldatei markiert eine Zeile den nächsten Befehl, der ausgeführt wird.
  4. Drücke F8 noch einmal: der erste Befehl läuft. Das Register mit dem Ergebnis hat jetzt den Wert 1, und weil es sich geändert hat, ist es rot markiert.
  5. Drücke F8 ein drittes Mal: der Rücksprung beendet das Programm. Im Ausgabefenster steht der Exit-Code, hier 1.

Dein Rechner ist (vermutlich) kein ARM-Rechner. ARM-Programme laufen auf einem Windows-on-ARM-Gerät; wie du Asmonite mit einem solchen Gerät verbindest, steht auf der Seite Windows on ARM. Bis dahin kannst du die ARM-Programme lesen, assemblieren und mit den Intel-Fassungen vergleichen.

Dein Rechner ist (vermutlich) kein ARM-Rechner. ARM-Programme laufen auf einem Windows-on-ARM-Gerät; wie du Asmonite mit einem solchen Gerät verbindest, steht auf der Seite Windows on ARM. Bis dahin kannst du die ARM-Programme lesen, assemblieren und mit den Intel-Fassungen vergleichen.

Was du siehst

Vor dem ersten F8 hat das Ergebnisregister irgendeinen Wert, den Windows dort hinterlassen hat. Nach dem ersten F8 steht dort die 1 (in rot). Der Befehlszeiger eiprippc ist um die Länge des Befehls weitergerückt. Das ist das ganze Programm: ein Befehl verändert den Zustand der CPU, der nächste ist an der Reihe.

Übungen

1. Anderer Exit-Code

Ändere die Zahl so, dass das Programm mit dem Exit-Code 42 endet. Prüfe es im Debugger.

Lösung

Aus mov eax, 1 wird mov eax, 42.

Aus movs r0, #1 wird movs r0, #42.

Aus mov x0, #1 wird mov x0, #42.

2. Ein Register ins andere

Schreibe erst die Zahl 5 in ein anderes Register als das Ergebnisregister und kopiere sie dann mit mov in das Ergebnisregister. Beobachte beide Register mit F8. Wie viele Befehle brauchst du?

Lösung
mov ebx, 5
mov eax, ebx
mov ecx, 5
mov eax, ecx
movs r1, #5
mov r0, r1
mov x1, #5
mov x0, x1

Drei Befehle mit ret, zwei davon mov. Der zweite kopiert ein Register in ein Register: Das Ziel steht wieder links, die Quelle rechts, und die Quelle behält ihren Wert (kopieren, nicht verschieben).

3. Knobelaufgabe: tauschen

Setze eax auf 3 und ebx auf 4 (bei ARM: die Register mit den Nummern 0 und 1) und vertausche ihre Inhalte, ohne dass ein Wert verloren geht. Am Ende soll das Ergebnisregister die 4 enthalten, das Programm also mit dem Exit-Code 4 enden. Du darfst ein drittes Register benutzen.

Lösung

Wie beim Tauschen zweier Gläser mit Inhalt brauchst du ein leeres dazu: Wert A in das freie Register, B nach A, das freie nach B.

mov eax, 3
mov ebx, 4
mov ecx, eax      ; frei = A
mov eax, ebx      ; A = B
mov ebx, ecx      ; B = frei
mov eax, 3
mov ebx, 4
mov ecx, eax      ; frei = A
mov eax, ebx      ; A = B
mov ebx, ecx      ; B = frei
movs r0, #3
movs r1, #4
mov r2, r0        ; frei = A
mov r0, r1        ; A = B
mov r1, r2        ; B = frei
mov x0, #3
mov x1, #4
mov x2, x0        ; frei = A
mov x0, x1        ; A = B
mov x1, x2        ; B = frei

Merke