Die Syntax des Assemblers

Intel-Schreibweise wie NASM – mit den Bytes, die daraus werden.

Asmonite baut ein Programm Byte für Byte aus dem Quelltext. Die Schreibweise ist die vertraute: ; für Kommentare, mov eax, [ebx+ecx*4+8], Labels, db und dd. Diese Seite zeigt, was sie kann, und neben jedem Beispiel die Bytes, die der Assembler schreibt (sie stammen aus dem echten Assembler). Wo er von NASM abweicht, steht es dabei.

Gezeigt wird x86 (32 Bit, .arch i386). Für x64, ARM Thumb-2 und ARM64 gilt dieselbe Zeilenform und dieselbe Rechnung; ihre Befehle erklärt F1 im Programm.

Eine Zeile

Höchstens ein Befehl je Zeile. Alles ist optional.

start:Label (der Doppelpunkt ist freiwillig)
movBefehl
eax, [ebx+8]Operanden, durch Komma getrennt
; copyKommentar (nur ;)
Du schreibst
start:  mov eax, [ebx+8]    ; copy
        add eax, 1
        jmp start
Der Assembler erzeugt
8B 43 08 83 C0 01 EB F8

Ein Label ist die Adresse der Stelle, an der es steht. Groß- und Kleinschreibung bei Labels ist egal (L1 und l1 sind dasselbe Label). Befehle schreibst du klein oder groß, aber ein Befehl mit großem ersten Buchstaben wählt die zweite Kodierung (siehe unten).

Zahlen und Ausdrücke

Wo eine Zahl steht, darf gerechnet werden: in Operanden, bei db, dw, dd, dq, resb, align, org.

Operatoren in dieser Rangfolge, die stärksten zuerst: Vorzeichen und ~; * / %; + -; << >>; &; ^; |. Klammern gehen immer. Zahlen: dezimal, 0x1F, 1Fh, 101b, 0b101 und ein Zeichen in einfachen Anführungszeichen ('A').

AusdruckWertAnmerkung
4*16+165 (0x41)Punkt vor Strich
(2+3)*420 (0x14)Klammern
100/714 (0xE)ganzzahlig, mit Vorzeichen
100%72der Rest
1<<5|335 (0x23)<< bindet stärker als |
0x10^0x111exklusiv-oder
~0xF & 0xFF240 (0xF0)bitweises Nicht, dann Und
-5+83Vorzeichen
'A'+166 (0x42)ein Zeichen in Anführungszeichen ist eine Zahl
1Fh31 (0x1F)hexadezimal mit h
101b5binär mit b
0b1015binär mit 0b

Konstanten, Abstände und die aktuelle Adresse

name equ ausdruck gibt einer Zahl einen Namen (die Zeile schreibt keine Bytes; die Konstante darf auch hinter ihrer Verwendung stehen). $ ist die Adresse der Zeile, $$ der Anfang des Abschnitts. Der Abstand zweier Labels ist eine Zahl, also eine Größe: end-start, $-msg.

Du schreibst
size    equ 4*16
        mov eax, size+1
msg:    db 'hi', 0
len     equ $-msg
        mov al, len
start:  mov ecx, end-start
        nop
end:
Der Assembler erzeugt
B8 41 00 00 00 68 69 00 B0 03 B9 06
00 00 00 90

Ein Abstand braucht die Adressen; der Assembler läuft deshalb mehrmals, bis sie gleich bleiben (das merkst du nicht). Nicht möglich sind eine Zahl hinter einem Label bei Sprung und Aufruf (jmp start+4) und die Division durch null – beides ist ein Fehler mit der Zeilennummer.

Operanden und Adressen

Register, Zahlen, Labels und Speicher in eckigen Klammern: [Basis + Index*Faktor + Zahl].

Du schreibstBytesAnmerkung
mov eax, [ebx]
8B 03
ein Register
mov eax, [ebx+8]
8B 43 08
Register plus Zahl
mov eax, [ebx+ecx*4+8]
8B 44 8B 08
Basis, Index mal Faktor (1, 2, 4, 8), Zahl
mov eax, [ebx+2*4]
8B 43 08
der konstante Teil wird gerechnet: wie [ebx+8]
mov eax, [ebp-4]
8B 45 FC
ein Minus setzt die Adresse fort
mov byte [ebx], 1
C6 03 01
Größe des Zugriffs: byte, word, dword
mov word [ebx], 1
66 C7 03 01 00
wie oben, 16 Bit: Präfix 66
mov eax, [es:ebx]
26 8B 03
Segment vor der Adresse

Ein Label allein (mov eax, label) ist seine Adresse als Zahl, in Klammern (mov eax, [label]) der Speicher dort. label+4 und label-4 sind Adressen, auch als Wert und in dd. Als Adresse gilt die virtuelle Adresse im geladenen Programm (Ladeadresse plus Lage), nicht der Abstand vom Anfang der Datei.

Sprünge und Aufrufe

Ein Label ist das Ziel; die Länge des Abstands sucht der Assembler selbst.

Du schreibstBytesAnmerkung
l: jmp l
EB FE
ohne Größe nimmt der Assembler die kürzeste Form
l: jmp near l
E9 FB FF FF FF
near: die lange Form (32 Bit Abstand)
l: jz short l
74 FE
short: ein Byte Abstand
l: nop
 jz l
90 74 FD
ein bedingter Sprung nach hinten

Anders als bei NASM ist eine Zahl hinter jmp der Abstand und nicht die Zieladresse: jmp 5 ist EB 05. Mit Vorzeichen geht es auch: jmp short -2, call near -5. loop und jecxz brauchen short und ein Label oder einen Abstand.

Daten und Platz

Was im Programm als Bytes stehen soll.

Du schreibstBytesAnmerkung
db 'AB', 0, 10
41 42 00 0A
Bytes; Text in einfachen Anführungszeichen
dw 0x1234, 1+1
34 12 02 00
Wörter (2 Bytes, niederwertiges Byte zuerst)
dd 1, -1
01 00 00 00 FF FF FF FF
Doppelwörter (4 Bytes)
dq 1
01 00 00 00 00 00 00 00
vier Wörter: 8 Bytes
du 'Hi'
48 00 69 00
Text als UTF-16: jedes Zeichen 2 Bytes
nop
align 4
nop
90 00 00 00 90
align füllt mit 00 bis zu einem Vielfachen
org 4
nop
00 00 00 00 90
org füllt mit 00 bis zur Position in der Datei
resb 4
nop
00 00 00 00 90
resb: vier Bytes Platz

Ein Label vor einer Datenzeile (msg db 'hi', 0, mit oder ohne Doppelpunkt) ist die Adresse der Daten. Mit .format pe darf resb nur am Ende eines Abschnitts stehen: der Platz steht nicht in der Datei, sondern wird beim Laden angelegt.

Die Art der Datei

Direktiven am Anfang sagen, was der Assembler schreibt. Sie stehen vor dem ersten Befehl.

Windows-Programm

Der Assembler schreibt Kopf, Abschnitte und die Importtabelle selbst; du schreibst Code, Daten und Importe.

.format pe console
.arch i386
.import msvcrt.dll cdecl printf
.import kernel32.dll ExitProcess

.code
start:
    call [printf](text)
    call [ExitProcess](0)

.data
text:   db 'Hello', 10, 0

DOS-Programm oder Bootsektor

Mit .format binary stehen die Bytes so, wie du sie schreibst: eine .COM-Datei, ein Bootsektor oder ein selbst geschriebener Kopf.

.format binary
.output hello.com
.arch x86_real

section 0x100
start:
    mov  ah, 0x09
    mov  dx, msg
    int  0x21
    mov  ax, 0x4C00
    int  0x21
msg:  db 'Hello!', 13, 10, '$'
DirektiveBedeutung
.arch i386Prozessor: i386 (32 Bit), x86_real (16 Bit, DOS), x64, thumb (ARM Thumb-2), arm64
.format pe [console|gui]schreibt ein Windows-Programm; .format binary die Bytes, wie sie stehen
.output nameder Name der Datei, die geschrieben wird (neben dem Quelltext)
.code, .databei pe: die folgenden Zeilen sind Programm oder Daten
.import dll name, nameimportierte Funktionen; der Name ist ein Label der Importtabelle: call [name]. Bei C-Funktionen cdecl dazuschreiben (siehe func)
.entry label, .imagebase nEinsprungpunkt, Ladeadresse
section n, segment n, org nbei binary: ein Abschnitt mit dieser virtuellen Adresse, ein DOS-Segment, Auffüllen bis zur Dateiposition

Was anders ist als bei NASM

Die Schreibweise sieht aus wie NASM, der Assembler ist aber nicht NASM-kompatibel. Das Wichtigste:

Du schreibstBytesAnmerkung
mov eax, ebx
8B C3
kleingeschrieben: die erste Kodierung
MOV eax, ebx
89 D8
der Befehl mit Großbuchstaben am Anfang: die zweite Kodierung, wo es zwei gibt
mul eax, ebx
F7 E3
der Akkumulator wird mitgeschrieben (mul ebx geht nicht)
push byte 5
6A 05
byte vor der Zahl wählt die kurze Form
mov ax, 5
66 B8 05 00
ohne .arch ist es 32 Bit: 16-Bit-Register brauchen das Präfix 66
NASMAsmonite
bits 16, section .text.arch x86_real; section <Zahl> (der Name zählt nicht)
org 0x100 setzt die Ladeadresseorg füllt die Datei bis zu dieser Position mit 00 auf
align 4 füllt Code mit nopalign füllt immer mit 00
times, incbin, Makros, %definegibt es nicht; es gibt keinen Präprozessor
global, externgibt es nicht; Importe: .import
Labels unterscheiden Groß und KleinLabels nicht
.lokal gilt bis zum nächsten Label.l ist ein gewöhnliches Label, zweimal dasselbe ist ein Fehler
db "AB"nur einfache Anführungszeichen: db 'AB'
dd label ist der Abstand vom Anfangdd label ist die virtuelle Adresse; rva label der Abstand
Fehler bei db 256: nur Warnungdb 256 ist ein Fehler

Noch nicht dabei sind die x87-FPU, MMX/SSE und einige Systembefehle. Die Hilfe (F1 auf einer Zeile) in beiden Programmen erklärt jeden Befehl, den der Assembler kennt, und jede Direktive.

Weiter

Funktionen

func schreibt den Stackframe, call Name(…) legt die Argumente ab, mit stdcall und cdecl.

Die func-Seite →

Hilfe im Programm

F1 auf einer Zeile erklärt den Befehl, die Windows-Funktion, die Direktive und die Fehlermeldung.

Mehr zu F1 →

Ausprobieren

Im ZIP liegen Beispiele für Windows, DOS und ARM. In 60 Sekunden vom Download zum ersten Haltepunkt.

Schnellstart →