我不明白JG/JNLE/JL/JNGE
CMP 之后的说明。
例如,如果我有:
CMP al,dl
jg label1
当al=101; dl =200
.
我们问的是jg
什么?开了al>dl
吗?还是al-dl>0
?
下一个代码相同的问题:
test al,dl
jg label1
我不明白我们比较什么,以及我们问什么“ jg
”。
换句话说,我不明白我们什么时候会跳转到 label1,什么时候不会。
当您执行 acmp a,b
时,会设置标志,就好像您已经计算过一样a - b
。
然后jmp
-type 指令检查这些标志以查看是否应该进行跳转。
换句话说,您拥有的第一个代码块(添加了我的评论):
cmp al,dl ; set flags based on the comparison
jg label1 ; then jump based on the flags
label1
当且仅当al
大于时才会跳转dl
。
您可能最好将其视为al > dl
但是您拥有的两个选择在数学上是等效的:
al > dl
al - dl > dl - dl (subtract dl from both sides)
al - dl > 0 (cancel the terms on the right hand side)
使用时需要小心,jg
因为它假定您的值已签名。因此,如果将字节101(二进制补码中的 101)与 200(二进制补码中的 -56)进行比较,前者实际上会更大。如果这不是我们想要的,您应该使用等效的无符号比较。
有关跳跃选择的更多详细信息,请参见此处,为完整起见,请在下面复制。首先是签名不合适的那些:
+--------+------------------------------+-------------+--------------------+
|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 |
+--------+------------------------------+-------------+--------------------+
然后是未签名的:
+--------+------------------------------+-------------+--------------------+
|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 | | |
+--------+------------------------------+-------------+--------------------+
最后是签名的:
+--------+------------------------------+-------------+--------------------+
|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 对跳转指令有相当好的总结。基本上,实际上有两个阶段:
cmp_instruction op1, op2
它根据结果设置各种标志,以及
jmp_conditional_instruction address
它将根据这些标志的结果执行跳转。
Compare( cmp
) 基本上会计算减法op1-op2
,但是,它不会被存储;而是只设置标志结果。因此,如果您这样做cmp eax, ebx
与说的相同eax-ebx
- 然后根据要设置的标志是正数、负数还是零来决定。
更详细的参考这里。
二进制补码中的加法和减法对于有符号数和无符号数是相同的
关键观察是 CMP 基本上是减法,并且:
在二进制补码(x86 使用的整数表示)中,有符号和无符号加法是完全相同的操作
例如,这允许硬件开发人员仅使用一个电路就可以更有效地实现它。
因此,例如,当您将输入字节提供给 x86 ADD 指令时,它并不关心它们是否已签名。
但是,ADD 确实根据操作期间发生的情况设置了一些标志:
进位:无符号加减结果不适合位大小,例如:0xFF + 0x01 或 0x00 - 0x01
此外,我们需要将 1 带到下一个级别。
符号:结果设置了最高位。即:如果解释为有符号则为负数。
溢出:输入的最高位是 0 和 0 或 1 和 1,输出反转是相反的。
即签名操作以不可能的方式改变了签名(例如正+正或负
然后,我们可以以一种使比较符合我们对有符号或无符号数字的期望的方式来解释这些标志。
这种解释正是 JA vs JG 和 JB vs JL 为我们所做的!
代码示例
这是 GNU GAS 的代码片段,可以使这更具体:
/* 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
请注意,AT&T 语法是“向后”:mov src, dst
. 因此,您必须在心理上颠倒条件代码的操作数才能使cmp
. 在英特尔语法中,这将是cmp al, bl
在此之后,将进行以下跳转:
请注意,在这个特定示例中,签名很重要,例如 JB 被采用但不是 JL。
JLE / JNG 等等于 / 否定版本只是别名
通过查看Intel 64 and IA-32 Architectures Software Developer's Manuals Volume 2部分“Jcc - Jump if Condition Is Met”,我们看到编码是相同的,例如:
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).
命令 JG 简单的意思是:如果更大就跳转。前面指令的结果存储在某些处理器标志中(在此它将测试 ZF=0 和 SF=OF),跳转指令根据它们的状态进行操作。