通常情况下,您在手写汇编时使用无符号数字。当然,情况并非总是如此,C 的int 促销应该以某种方式实现,对吧?
让我们从解释位级别的二进制补码开始。首先,最高位在设置时表示负数,在清除时表示非负 nne。如果很清楚,这些东西会按照您的预期运行。即0写成0x00000000,2147483647写成0x7FFFFFFF。然而,对于任何负数N,为了得到它的绝对值,你必须做~N-1,以及所有各自的环绕。这导致-1 被写成0xFF,而-2147483648 被写成0x80。这有一些很好的副作用,例如-0 == 0 和加法/减法对于所有数字都是相同的操作。
现在,你的代码......
XOR EAX,EAX
作为一个数学规则,对某事物进行异或运算总是会产生零作为结果。所以,你可以把它想象成一个优化的“MOV EAX, 0”。顺便说一句,您可能想阅读why XOR is better for this。那么……
MOV AX, BX
MOV 是一条指令,字面意思是“按原样复制位”。即,源中所有清零的位分别在目的中清零,源中所有置位的位分别在目的中置位。在这种情况下,AX 现在将包含BX 内容的精确副本。在x86架构中,EAX、EBX、ECX、EDX都是这样划分的……
________________ ________ ____ ____
| ERX | RX | RH | RL |
|________________|________|____|____|
\_________________________________/
| \________________/
32 bits | \__/
16 bits ||
8 bits
这意味着对于每个寄存器R,ERX 表示其所有 32 位,RX 表示其低 16 位,RH 表示其低 16 位的高字节,RL 表示其低 16 位的低字节。因此,返回您的代码,并假设 BX 包含 0xFFFF(-1 在二进制补码中),这就是发生的事情......
______________ __________________________________ __________________________________
| Instruction | EAX | EBX |
|______________|__________________________________|__________________________________|
| XOR EAX, EAX | 00000000000000000000000000000000 | 00000000000000001111111111111111 |
|______________|__________________________________|__________________________________|
| MOV AX, BX | 00000000000000001111111111111111 | 00000000000000001111111111111111 |
|______________|__________________________________|__________________________________|
那么,如果我们将AX解释为二进制补码,我们就会得到正确的答案,即-1。但是,如果我们将EAX 解释为二进制补码,我们会得到一个错误答案,65535。为了正确地做到这一点,我们必须做一个符号扩展移动。这意味着该指令将考虑该值是二进制补码形式的事实,因此将正确操作它。例如,请参阅...
_______________ __________________________________ __________________________________
| Instruction | EAX | EBX |
|_______________|__________________________________|__________________________________|
| MOVSX EAX, BX | 11111111111111111111111111111111 | 00000000000000001111111111111111 |
|_______________|__________________________________|__________________________________|
现在,将 EAX 解释为 32 位二进制补码将产生正确答案 -1。这是(还)二进制补码的另一个优点。您可以根据需要多次复制最高位来进行符号扩展。