Ein Vergleich, ein Block – und die Schleife dazu: ohne eigenes Label und ohne den Sprung von Hand.
Statt cmp, eines bedingten Sprungs und einer Marke schreibst du, was du meinst: if ebx == [0x04+eax], den Block und endif. Der Assembler schreibt den Vergleich und den Sprung der Gegenbedingung selbst. Es ist kein Ausdruck und keine Makro-Sprache: ein direkter Vergleich zweier Operanden, und bei < und > sagst du, ob mit Vorzeichen oder ohne.
Für .arch i386 (32 Bit), überall im Code und auch in einer func. Die Bytes stammen aus dem echten Assembler.
Der Block läuft nur, wenn der Vergleich stimmt. Dazu überspringt der Assembler ihn mit dem Sprung der Gegenbedingung: bei == ein jne.
if ebx == [0x04+eax] mov dword [0x04+eax], 0xFFFFFFFF endif mov eax, [eax]
cmp ebx, [0x04+eax] jne skip ; the opposite condition mov dword [0x04+eax], 0xFFFFFFFF skip: mov eax, [eax]
3B 58 04 75 07 C7 40 04 FF FF FF FF
8B 00
if, else, do, while)
deine Zeilen, unverändert
Die Marke macht der Assembler selbst (asmogen_if…, sie ist im Programm nicht zu sehen). Der Debugger führt die if-Zeile, also den Vergleich und den Sprung, als einen Schritt aus.
if [signed|unsigned] Operand Vergleich Operand, der Block, optional else mit einem zweiten Block, und endif. Die Operanden sind alles, was cmp nimmt: Register, Zahlen, Labels, Speicher in eckigen Klammern.
| Du schreibst | Erzeugt und Bytes | Anmerkung |
|---|---|---|
if eax == ebx nop endif | cmp eax, ebx jne skip nop skip: 3B C3 75 01 90 | gleich: cmp, dann jne über den Block (75 01 überspringt das eine Byte des Blocks) |
if eax != 5 nop endif | cmp eax, 5 je skip nop skip: 83 F8 05 74 01 90 | ungleich: je über den Block; die rechte Seite darf eine Zahl sein |
if dword [0x1000] == 5 nop endif | cmp dword [0x1000], 5 jne skip nop skip: 83 3D 00 10 00 00 05 75 01 90 | Speicher gegen eine Zahl braucht die Größe (byte, word, dword), wie bei cmp |
if signed eax < ebx nop endif | cmp eax, ebx jge skip nop skip: 3B C3 7D 01 90 | ein Ordnungsvergleich sagt signed oder unsigned; signed < springt über den Block mit jge |
if unsigned eax >= [ebx] nop endif | cmp eax, [ebx] jb skip nop skip: 3B 03 72 01 90 | Speicher darf auf einer Seite stehen; unsigned >= springt mit jb über den Block |
if, endif)
deine Zeilen, unverändert
Die Vergleiche sind ==, !=, <, <=, > und >=. In einer func gelten die Namen der Parameter und Lokalen auch im Vergleich: if signed eax < b.
Dieselben Bits sind je nach Sicht verschieden groß: 0xFFFFFFFF ist mit Vorzeichen -1, ohne Vorzeichen 4294967295. -1 < 1 stimmt also mit Vorzeichen und nicht ohne. Adressen, Längen und Zähler sind meist vorzeichenlos, Zahlen mit Vorzeichen eben nicht – deshalb verlangt Asmonite die Antwort in jeder Zeile (oder mit .compare, siehe unten). == und != brauchen sie nicht.
| Vergleich | vorzeichenbehaftet | Sprung über den Block | vorzeichenlos | Sprung über den Block |
|---|---|---|---|---|
< | if signed eax < ebx | jge 7D 01 | if unsigned eax < ebx | jae 73 01 |
<= | if signed eax <= ebx | jg 7F 01 | if unsigned eax <= ebx | ja 77 01 |
> | if signed eax > ebx | jle 7E 01 | if unsigned eax > ebx | jbe 76 01 |
>= | if signed eax >= ebx | jl 7C 01 | if unsigned eax >= ebx | jb 72 01 |
Die Sprünge der rechten Spalten sind die der Gegenbedingung: sie überspringen den Block, wenn der Vergleich nicht stimmt. Mit Vorzeichen sind es jl, jle, jg, jge, ohne Vorzeichen jb, jbe, ja, jae.
Mit & statt eines Vergleichs wird der if zu test: der Block läuft, wenn mindestens ein Bit in beiden Operanden gesetzt ist. & ist hier kein Rechenausdruck, sondern die ganze Bedingung – ein &, danach höchstens == 0 oder != 0. Ein Wort signed oder unsigned gibt es dabei nicht: ob etwas null ist, hängt nicht vom Vorzeichen ab.
| Du schreibst | Der Block läuft, wenn … | Erzeugt |
|---|---|---|
if eax & ebx | ein Bit in beiden gesetzt ist | test eax, ebx + jz 85 D8 74 01 |
if eax & ebx != 0 | wie oben (andere Schreibweise) | test eax, ebx + jz 85 D8 74 01 |
if eax & ebx == 0 | kein Bit in beiden gesetzt ist | test eax, ebx + jnz 85 D8 75 01 |
if al & 0x80 | das oberste Bit von al gesetzt ist | test al, 0x80 + jz A8 80 74 01 |
if byte [ebx] & 1 | das unterste Bit des Bytes in [ebx] gesetzt ist | test byte [ebx], 1 + jz F6 03 01 74 01 |
Wie bei cmp braucht Speicher gegen eine Zahl die Größe (byte, word, dword). test kennt nicht „Register gegen Speicher“, sondern nur „Speicher gegen Register“; bei if eax & [ebx] vertauscht Asmonite die Operanden von selbst (für test macht das keinen Unterschied). else, endif, Verschachteln und die Namen einer func gehen wie bei jedem if.
if zeroStehen die Flags schon da (nach dec, sub, add, and ...), braucht es keinen Vergleich: if nennt die Bedingung mit einem Wort und schreibt nur den Sprung um den Block. Die Wörter sind die der Sprungbefehle: zero (auch equal), carry, sign, overflow, parity, dazu above / below (ohne Vorzeichen) und less / greater (mit Vorzeichen). Davor darf not stehen, dahinter bei den letzten vier or equal.
| Du schreibst | Der Block läuft, wenn ... | Erzeugt (mit dem Befehl davor) |
|---|---|---|
dec ecxif zero | das Ergebnis 0 ist | dec ecx + jnz 49 75 01 |
sub eax, 5if not zero | das Ergebnis nicht 0 ist | sub eax, 5 + jz 83 E8 05 74 01 |
add eax, ecxif carry | ein Übertrag entstanden ist | add eax, ecx + jnc 03 C1 73 01 |
cmp eax, ebxif below or equal | eax <= ebx, ohne Vorzeichen | cmp + ja 3B C3 77 01 |
cmp eax, ebxif not less | eax >= ebx, mit Vorzeichen | cmp + jl 3B C3 7C 01 |
Welche Flags gelten, entscheidet der Befehl davor, nicht if: if zero nach einem mov prüft das Flag eines früheren Befehls. Ein Wort signed oder unsigned gibt es hier nicht, above / below und less / greater sagen es selbst. else, endif und das Verschachteln gehen wie bei jedem if.
do ... whileDer Block zwischen do und while läuft einmal und dann so lange, wie die Bedingung hinter while gilt. Die Bedingung hat dieselben Formen wie bei if (ein Vergleich, a & b oder ein Flag-Wort); der Sprung geht zurück zum do. Das Label dort macht der Assembler selbst (asmogen_do…).
do dec ecx while not zero
again: dec ecx jnz again ; the condition itself, back
49 75 FD
do inc eax while signed eax < 10
again: inc eax cmp eax, 10 jl again
40 83 F8 0A 7C FA
if, else, do, while)
deine Zeilen, unverändert
Bei while ist der Sprung die Bedingung selbst (bei if ihr Gegenteil): while not zero ist jnz, while signed eax < 10 ist jl. Ordnungsvergleiche brauchen auch hier signed oder unsigned (oder ein .compare davor). Schleifen lassen sich ineinander und mit if verschachteln; sie müssen sauber ineinander liegen (ein endif in einem offenen do ist ein Fehler). Im Debugger ist die while-Zeile, also Vergleich und Sprung, ein Schritt.
Ein Beispiel mit printf liegt im ZIP: examples\func\func_loop.asm (die Summe von 1 bis 10).
Wer ein ganzes Stück mit Zahlen mit Vorzeichen schreibt, muss signed nicht jedes Mal tippen: .compare signed (oder .compare unsigned) gilt für alle if dahinter, bis zum nächsten .compare. .compare none nimmt es zurück. Ein Wort in der if-Zeile gilt immer.
.compare signed if eax < ebx ; signed (from .compare) nop endif if unsigned eax < ebx ; the word in the line wins nop endif .compare none ; back to: a word is required
3B C3 7D 01 90 3B C3 73 01 90
Ohne .compare und ohne Wort ist ein Ordnungsvergleich ein Fehler – es gibt keinen stillen Standard. Die Direktive steht in derselben blauen Schrift wie die Schlüsselwörter, damit sie im Quelltext auffällt.
else macht hinter dem ersten Block ein jmp über den zweiten; if darf in if stehen, jedes endif schließt das innerste offene.
if eax != 0 inc eax else dec eax endif
cmp eax, 0 je else1 ; if not: to else inc eax jmp end1 ; then part done else1: dec eax end1:
83 F8 00 74 03 40 EB 01 48
if eax == 1 if ebx == 2 nop endif endif
cmp eax, 1 jne end1 cmp ebx, 2 jne end2 nop end2: end1:
83 F8 01 75 06 83 FB 02 75 01 90
if, else, do, while)
deine Zeilen, unverändert
Das Beispiel aus dem ZIP: das Größere von zwei Parametern. Die Namen a und b werden wie sonst in func zu [ebp+8] und [ebp+12].
func Max(a, b) begin mov eax, a if signed eax < b mov eax, b endif end
Max: push ebp mov ebp, esp mov eax, [ebp+8] cmp eax, dword [ebp+12] jge skip mov eax, [ebp+12] skip: pop ebp ret 8
55 8B EC 8B 45 08 3B 45 0C 7D 03 8B
45 0C 5D C2 08 00
func und if ergänzt
deine Zeile, die Namen sind ersetzt
Das end der Funktion schreibt pop ebp und ret 8; offene if vor einem end sind ein Fehler.
Es ist ein Vergleich, kein Ausdruck: if eax+1 == ebx rechnet nicht, und && oder || gibt es nicht (ein einzelnes & ist der Bit-Test oben). Wer mehr braucht, schreibt zwei if oder die Sprünge selbst. Die Meldungen nennen die Zeile des Quelltexts.
| Du schreibst | Meldung | Fall |
|---|---|---|
if eax < ebx nop endif | if eax < ebx: say whether it is signed or unsigned (if signed eax < ebx / if unsigned ..., or .compare signed / .compare unsigned before) | ein Ordnungsvergleich ohne Wort |
if eax = ebx nop endif | use == to compare: if eax == ebx | ein einzelnes = |
if eax == ebx == ecx nop endif | only one comparison behind if, no expressions | zwei Vergleiche |
if eax == ebx
nop | endif is missing for the if in line 1 | endif fehlt |
nop
endif | endif without if | endif ohne if |
if eax == ebx nop else nop else nop endif | a second else for the if in line 1 | ein zweites else |
while eax != 1 | while without do | while ohne do |
do
nop | while is missing for the do in line 1 | while fehlt |
do nop endif | endif without if (the open block is the do in line 1, while is missing) | ein endif im offenen do |
if foo nop endif | if needs a comparison between two operands: ==, !=, <, <=, > or >=, or a flag condition (zero, carry, ...) | weder Vergleich noch Flag-Wort |
Für .arch i386, .arch x64, .arch arm64 und .arch thumb. In x64 nimmt der Assembler für Sprünge vorwärts noch die lange Form (6 statt 2 Byte). In ARM 64 und Thumb schreibst du die Operanden in ARM-Schreibweise (if w0 == 5, if r1 != r2; eine Zahl bekommt ihr # selbst); der Sprung ist b.ne, b.lt (Thumb: bne, blt) und so weiter, bei Thumb wählt der Assembler die lange Form des Sprungs von selbst, wenn der Block zu groß ist, if a & b ist tst, Speicheroperanden gibt es nicht (erst laden), und parity gibt es nicht. carry ist das Flag C (nach einer Subtraktion ist C = 1, wenn nichts geborgt wurde). Eine Marke vor der Zeile (L: if …) geht.
Im ZIP liegen func_if.asm und func_loop.asm (Ordner examples\func); in 60 Sekunden vom Download zum ersten Haltepunkt.