Je ne comprends pas le JG/JNLE/JL/JNGE
instructions, qui viennent après CMP.
par exemple, si j'ai:
CMP al,dl
jg label1
Quand al=101; dl =200
.
Sur quoi nous demandons le jg
? Est-ce sur al>dl
? ou al-dl>0
?
Même problème pour le code suivant:
test al,dl
jg label1
Je ne comprends pas ce que nous comparons et ce à quoi nous demandons le "jg
".
En d'autres termes, je ne comprends pas quand nous passerions à label1 et quand nous ne le ferions pas.
Quand vous faites un cmp a,b
, les indicateurs sont définis comme si vous aviez calculé a - b
.
Ensuite, les instructions de type jmp
- vérifient ces indicateurs pour voir si le saut doit être effectué.
En d'autres termes, le premier bloc de code que vous avez (avec mes commentaires ajoutés):
cmp al,dl ; set flags based on the comparison
jg label1 ; then jump based on the flags
sauterait à label1
si et seulement si al
était supérieur à dl
.
Vous feriez probablement mieux de penser à cela comme al > dl
mais les deux choix que vous avez là sont mathématiquement équivalents:
al > dl
al - dl > dl - dl (subtract dl from both sides)
al - dl > 0 (cancel the terms on the right hand side)
Vous devez faire attention lorsque vous utilisez jg
dans la mesure où cela suppose que vos valeurs ont été signées. Donc, si vous comparez les octets 101 (101 au complément à deux) à 200 (-56 au complément à deux), le premier sera en réalité plus grand. Si ce n'est pas ce qui était souhaité, vous devriez utiliser la comparaison non signée équivalente.
Voir ici pour plus de détails sur la sélection de sauts, reproduit ci-dessous pour plus de détails. Premièrement ceux où la signature n'est pas appropriée:
+--------+------------------------------+-------------+--------------------+
|Instr | Description | signed-ness | Flags |
+--------+------------------------------+-------------+--------------------+
| JO | Jump if overflow | | OF = 1 |
+--------+------------------------------+-------------+--------------------+
| JNO | Jump if not overflow | | OF = 0 |
+--------+------------------------------+-------------+--------------------+
| JS | Jump if sign | | SF = 1 |
+--------+------------------------------+-------------+--------------------+
| JNS | Jump if not sign | | SF = 0 |
+--------+------------------------------+-------------+--------------------+
| JE/ | Jump if equal | | ZF = 1 |
| JZ | Jump if zero | | |
+--------+------------------------------+-------------+--------------------+
| JNE/ | Jump if not equal | | ZF = 0 |
| JNZ | Jump if not zero | | |
+--------+------------------------------+-------------+--------------------+
| JP/ | Jump if parity | | PF = 1 |
| JPE | Jump if parity even | | |
+--------+------------------------------+-------------+--------------------+
| JNP/ | Jump if no parity | | PF = 0 |
| JPO | Jump if parity odd | | |
+--------+------------------------------+-------------+--------------------+
| JCXZ/ | Jump if CX is zero | | CX = 0 |
| JECXZ | Jump if ECX is zero | | ECX = 0 |
+--------+------------------------------+-------------+--------------------+
Puis les non signés:
+--------+------------------------------+-------------+--------------------+
|Instr | Description | signed-ness | Flags |
+--------+------------------------------+-------------+--------------------+
| JB/ | Jump if below | unsigned | CF = 1 |
| JNAE/ | Jump if not above or equal | | |
| JC | Jump if carry | | |
+--------+------------------------------+-------------+--------------------+
| JNB/ | Jump if not below | unsigned | CF = 0 |
| JAE/ | Jump if above or equal | | |
| JNC | Jump if not carry | | |
+--------+------------------------------+-------------+--------------------+
| JBE/ | Jump if below or equal | unsigned | CF = 1 or ZF = 1 |
| JNA | Jump if not above | | |
+--------+------------------------------+-------------+--------------------+
| JA/ | Jump if above | unsigned | CF = 0 and ZF = 0 |
| JNBE | Jump if not below or equal | | |
+--------+------------------------------+-------------+--------------------+
Et enfin, les signés:
+--------+------------------------------+-------------+--------------------+
|Instr | Description | signed-ness | Flags |
+--------+------------------------------+-------------+--------------------+
| JL/ | Jump if less | signed | SF <> OF |
| JNGE | Jump if not greater or equal | | |
+--------+------------------------------+-------------+--------------------+
| JGE/ | Jump if greater or equal | signed | SF = OF |
| JNL | Jump if not less | | |
+--------+------------------------------+-------------+--------------------+
| JLE/ | Jump if less or equal | signed | ZF = 1 or SF <> OF |
| JNG | Jump if not greater | | |
+--------+------------------------------+-------------+--------------------+
| JG/ | Jump if greater | signed | ZF = 0 and SF = OF |
| JNLE | Jump if not less or equal | | |
+--------+------------------------------+-------------+--------------------+
Wikibooks a un assez bon résumé de instructions de saut . Fondamentalement, il y a en fait deux étapes:
cmp_instruction op1, op2
Ce qui définit divers indicateurs en fonction du résultat, et
jmp_conditional_instruction address
qui exécutera le saut en fonction des résultats de ces drapeaux.
Compare (cmp
) calculera la soustraction op1-op2
, cependant, ceci n'est pas stocké; à la place, seuls les résultats d'indicateur sont définis. Donc, si vous le faisiez cmp eax, ebx
c'est la même chose que de dire eax-ebx
- décidant ensuite, en fonction du résultat, positif, négatif ou nul, quels indicateurs définir.
Référence plus détaillée ici .
L'addition et la soustraction dans le complément à deux sont identiques pour les nombres signés et non signés
L'observation clé est que CMP est fondamentalement une soustraction et:
Dans complément à deux (représentation sous forme d'entier utilisée par x86), les additions signée et non signée sont exactement la même opération
Cela permet par exemple aux développeurs de matériel de le mettre en œuvre plus efficacement avec un seul circuit.
Ainsi, lorsque vous attribuez par exemple des octets d’entrée à l’instruction x86 ADD, le fait qu’ils soient signés ou non ne importe pas.
Cependant, ADD définit quelques indicateurs en fonction de ce qui s'est passé pendant l'opération:
carry: le résultat d'addition ou de soustraction non signé ne tient pas dans la taille en bits, par exemple: 0xFF + 0x01 ou 0x00 - 0x01
Pour plus, nous aurions besoin de porter 1 au niveau suivant.
signe: le résultat a le bit le plus élevé. C'est-à-dire négatif si interprétée comme signée.
débordement: les bits supérieurs sont 0 et 0 ou 1 et 1 et la sortie inversée est le contraire.
C'est à dire. opération signée a changé la signature de manière impossible (par exemple, positive + positive ou négative
Nous pouvons ensuite interpréter ces indicateurs de manière à ce que la comparaison corresponde à nos attentes concernant les nombres signés ou non signés.
Cette interprétation est exactement ce que JA vs JG et JB vs JL font pour nous!
Exemple de code
Voici GNU GAS un extrait de code permettant de rendre ceci plus concret:
/* 0x0 ==
*
* * 0 in 2's complement signed
* * 0 in 2's complement unsigned
*/
mov $0, %al
/* 0xFF ==
*
* * -1 in 2's complement signed
* * 255 in 2's complement unsigned
*/
mov $0xFF, %bl
/* Do the operation "Is al < bl?" */
cmp %bl, %al
Notez que la syntaxe AT & T est "à l'envers": mov src, dst
. Vous devez donc inverser mentalement les opérandes pour que les codes de condition aient un sens avec cmp
. Dans la syntaxe Intel, ce serait cmp al, bl
Après ce point, les sauts suivants seraient effectués:
Notez l’importance de la signature dans cet exemple, par exemple: JB est pris mais pas JL.
Exemple exécutable avec assertions .
Les versions égales/négatives comme JLE/JNG ne sont que des alias
En examinant = Section "Jcc - Sauter si la condition est remplie) --- (manuels des développeurs d'architectures Intel 64 et IA-32, volume 2 , nous voyons que les codages sont identiques, par exemple:
Opcode Instruction Description
7E cb JLE rel8 Jump short if less or equal (ZF=1 or SF ≠ OF).
7E cb JNG rel8 Jump short if not greater (ZF=1 or SF ≠ OF).
La commande JG signifie simplement: Jump if Greater. Le résultat des instructions précédentes est stocké dans certains drapeaux de processeur (dans ce cas, il vérifierait si ZF = 0 et SF = OF) et l'instruction de saut agit en fonction de leur état.