assembly - CMP 和 2 的补码

标签 assembly x86 masm

我已经开始了解 CMP指令,比较两个整数。
当使用无符号整数时,我读到:

  • ZF=1意味着目的地和来源相同。
  • ZF=0 && CF = 0意味着 destination > source
  • ZF=0 && CF = 1意味着destination < source

据我了解,CMP 指令使用隐式减法,而不更改操作数的值。
减法没有意义!

假设我执行以下指令:

 CMP 1, 4
     1 - 4 = 1 + (-4)
    ...001
    .+.100
     =  101

我在这里没有看到进位,我的意思是,进位位是 0 ,不是吗?所以我上面说的,我读到的是正确的,是错误的。
我只是不明白为什么这个减法会设置进位标志。 我没有进位!如果进位位只是数字的(在本例中)第 4 位,则它为零。
我花了几个小时试图弄清楚。

最佳答案

所以让我们尝试 3 位系统中的所有 3 位数字

000 - 000 = 0000 :  +0  -  +0  =  + 0      Z
000 - 001 = 1111 :  +0  -  +1  =  +15 [-1] C
000 - 010 = 1110 :  +0  -  +2  =  +14 [-2] C
000 - 011 = 1101 :  +0  -  +3  =  +13 [-3] C
000 - 100 = 1100 :  +0  -  +4  =  +12 [-4] C
000 - 101 = 1011 :  +0  -  +5  =  +11 [-5] C
000 - 110 = 1010 :  +0  -  +6  =  +10 [-6] C
000 - 111 = 1001 :  +0  -  +7  =  + 9 [-7] C
001 - 000 = 0001 :  +1  -  +0  =  + 1      
001 - 001 = 0000 :  +1  -  +1  =  + 0      Z
001 - 010 = 1111 :  +1  -  +2  =  +15 [-1] C
001 - 011 = 1110 :  +1  -  +3  =  +14 [-2] C
001 - 100 = 1101 :  +1  -  +4  =  +13 [-3] C
001 - 101 = 1100 :  +1  -  +5  =  +12 [-4] C
001 - 110 = 1011 :  +1  -  +6  =  +11 [-5] C
001 - 111 = 1010 :  +1  -  +7  =  +10 [-6] C
010 - 000 = 0010 :  +2  -  +0  =  + 2      
010 - 001 = 0001 :  +2  -  +1  =  + 1      
010 - 010 = 0000 :  +2  -  +2  =  + 0      Z
010 - 011 = 1111 :  +2  -  +3  =  +15 [-1] C
010 - 100 = 1110 :  +2  -  +4  =  +14 [-2] C
010 - 101 = 1101 :  +2  -  +5  =  +13 [-3] C
010 - 110 = 1100 :  +2  -  +6  =  +12 [-4] C
010 - 111 = 1011 :  +2  -  +7  =  +11 [-5] C
011 - 000 = 0011 :  +3  -  +0  =  + 3      
011 - 001 = 0010 :  +3  -  +1  =  + 2      
011 - 010 = 0001 :  +3  -  +2  =  + 1      
011 - 011 = 0000 :  +3  -  +3  =  + 0      Z
011 - 100 = 1111 :  +3  -  +4  =  +15 [-1] C
011 - 101 = 1110 :  +3  -  +5  =  +14 [-2] C
011 - 110 = 1101 :  +3  -  +6  =  +13 [-3] C
011 - 111 = 1100 :  +3  -  +7  =  +12 [-4] C
100 - 000 = 0100 :  +4  -  +0  =  + 4 [-4] 
100 - 001 = 0011 :  +4  -  +1  =  + 3      
100 - 010 = 0010 :  +4  -  +2  =  + 2      
100 - 011 = 0001 :  +4  -  +3  =  + 1      
100 - 100 = 0000 :  +4  -  +4  =  + 0      Z
100 - 101 = 1111 :  +4  -  +5  =  +15 [-1] C
100 - 110 = 1110 :  +4  -  +6  =  +14 [-2] C
100 - 111 = 1101 :  +4  -  +7  =  +13 [-3] C
101 - 000 = 0101 :  +5  -  +0  =  + 5 [-3] 
101 - 001 = 0100 :  +5  -  +1  =  + 4 [-4] 
101 - 010 = 0011 :  +5  -  +2  =  + 3      
101 - 011 = 0010 :  +5  -  +3  =  + 2      
101 - 100 = 0001 :  +5  -  +4  =  + 1      
101 - 101 = 0000 :  +5  -  +5  =  + 0      Z
101 - 110 = 1111 :  +5  -  +6  =  +15 [-1] C
101 - 111 = 1110 :  +5  -  +7  =  +14 [-2] C
110 - 000 = 0110 :  +6  -  +0  =  + 6 [-2] 
110 - 001 = 0101 :  +6  -  +1  =  + 5 [-3] 
110 - 010 = 0100 :  +6  -  +2  =  + 4 [-4] 
110 - 011 = 0011 :  +6  -  +3  =  + 3      
110 - 100 = 0010 :  +6  -  +4  =  + 2      
110 - 101 = 0001 :  +6  -  +5  =  + 1      
110 - 110 = 0000 :  +6  -  +6  =  + 0      Z
110 - 111 = 1111 :  +6  -  +7  =  +15 [-1] C
111 - 000 = 0111 :  +7  -  +0  =  + 7 [-1] 
111 - 001 = 0110 :  +7  -  +1  =  + 6 [-2] 
111 - 010 = 0101 :  +7  -  +2  =  + 5 [-3] 
111 - 011 = 0100 :  +7  -  +3  =  + 4 [-4] 
111 - 100 = 0011 :  +7  -  +4  =  + 3      
111 - 101 = 0010 :  +7  -  +5  =  + 2      
111 - 110 = 0001 :  +7  -  +6  =  + 1      
111 - 111 = 0000 :  +7  -  +7  =  + 0      Z

其中 C 是进位/借位,Z 为零

您的规则严格来说是一个未签名的规则。对于有符号,我认为这就像如果 N != V 则签名小于,如果 N == V 则签名大于。

所以 0 - 0 是 0 Z 标志它们相等 然后是 0 - 1 和 0 - 2 等等。第二个操作数大于 Z 未设置且进位设置。

直到我们达到 1 - 0(不为零)且 C 未设置时,左边的数字才会更大。

然后我们得到相等的 (1 - 1)

还有一些 Z 未设置 C 设置,因此正确的数字更大。

然后 2 - 0、2 - 1 左侧更大(Z 未设置 C 未设置),然后 2 - 2 Z 已设置,然后 2 - 3 到 2 - 7 Z 未设置 C 已设置,因此右侧更大。

并且这种模式会重复。

当然,诀窍在于源和目标的定义是什么,这通常没有记录在指令集中,您必须通过实验来确定这一点,并且出于某种原因,至少对我来说,我总是猜错。

您的具体示例

1 与 4 相比 1 - 4 = 1 + (-4), 4 = 0b100,因此 -4 = 011 + 1

送入加法器,减法意味着反转进位并将第二个操作数反转:

     1
   001
+  011
=======

填写

  0111
   001
+  011
=======
   101

1 - 4 = -3。 Z 是 0 C 是 0 所以 1 < 4

请注意,要从进位位中获取借位,请反转进位(借位 = ~ 进位)。进位为 0 意味着发生了借位,如果您尝试使用铅笔和纸以十进制形式从 1 中减去 4,这一点是显而易见的。

如果没有借位,则设置进位,例如取 4 - 1:

  1001
   100
+  110
========
   011

4 - 1 = 3,无借位。 Z 未设置 C 已设置,因此 4 > 1

我认为这里的底线是哪个操作数是源,哪个是目标,为什么他们很少正确记录它?任何时候你在指令集中使用比较,你都必须用固定的数字做一些实验来看看哪个操作数是哪个。您还必须非常小心地使用有符号或无符号大于或小于它会产生差异。有些指令集不提供一种或另一种风格,但这没关系,你真的不需要一长串有符号和无符号的 this 和 that 左右切换操作数并理解标志,你可以找到一个简单的 if 进位或不进位或如果什么给你几乎一切,有时你会得到额外的好处,当 Z 设置时,那么进位是零右,这意味着未设置的进位是“或等于”。

关于assembly - CMP 和 2 的补码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28287354/

相关文章:

assembly - MOV src,dest(或)MOV dest,src?

assembly - 如何将负整数转换为字符串并在 MASM Assembly 中输出

c++ - 如何编写跨平台(32 位和 64 位)垃圾汇编代码?

assembly - $ 在 NASM 中究竟是如何工作的?

c++ - 如何将寄存器的值存储到指针指向的内存位置?

创建 x86 Bootstrap

assembly - jmp 和 ja 有什么区别?

linux - "memory segment"是汇编编程中的 intel 专用概念吗?

.net - 将 CLR 主机注入(inject)正在运行的进程 - 可能吗?

arrays - 更改 edi 的偏移量与更改地址处的值?