CMP 和 2 的补码
CMP and 2's complement
我已经开始学习 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 位,则为零。
我花了几个小时想弄明白。
将进位位视为结果的下一个最高有效位。
考虑一步减去 "complement, add 1, add"。在硬件中,这是通过 LSB 的进位来实现的:0 表示加法,1 表示减法,C 表示加法。
示例:
1 - 4
0.001 // 1
1.011 + 1 // 4 complemented, carry in
+ -----
1.101 // -3, carry out
-1 - -2
1.111 // -1
0.001 + 1 // -2 complemented, carry in
+ -----
0.001 // 1, no carry out
您显示的 CMP 条件代码用于无符号算术。对于带符号的算术,您必须异或进位和符号位。通常也有一个测试。
例如,JG(更大的跳跃)是 ZF = 0 和 SF = OF
编辑,感谢@Blechdose 的鼓励,这里是一个示例,说明当补减数的符号未传播到 CF 时会发生什么(导致 x86 反转 CF 以进行减法):
1 - 4
.001 // 1
.011 + 1 // 4 complemented, carry in
+ -----
0.101 // -3, no carry out
4 - 1
.100 // 4
.110 + 1 // 1 complemented, carry in
+ -----
1.011 // 3, carry out
所以让我们尝试 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 是进位 out/borrow 并且 Z 是零
您的规则严格来说是未签名的规则。对于签名,我认为它类似于 if N != V then signed less than, if N == V then signed greater than.
所以 0 - 0 是 0 Z 标志它们相等
然后是 0 - 1 和 0 - 2 等。第二个操作数更大 Z 未设置且进位已设置。
直到我们得到 1 - 0 不为零且 C 未设置,左边的数字更大。
然后我们打等号(1 - 1)
还有一些Z没有设置C设置所以正确的数字更大。
然后2-0,2-1左大(Z没设置C没设置)然后2-2Z设置,然后2-3到2-7Z没设置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
请注意,要从进位位中获取借位,您需要反转进位 (borrow = ~carry)。 Carry out is 0 表示发生借位,如果您尝试使用铅笔和纸以小数形式从 1 中减去 4,这很明显。
如果没有借位则设置进位,例如取 4 - 1:
1001
100
+ 110
========
011
4 - 1 = 3 没有借位。 Z 未设置 C 已设置所以 4 > 1
我认为这里的底线是哪个操作数是源,哪个是目标,为什么他们很少正确记录它?任何时候你在指令集中使用比较,你都必须用固定的数字做一些实验,看看哪个操作数是哪个。您还必须非常小心地使用有符号或无符号大于或小于它会有所不同。有些指令集不提供一种或另一种风格,但这没关系,你真的不需要一长串有符号和无符号的这个和那个左右切换操作数并理解标志你可以找到一个简单的如果进位或不进位或如果 whatever 给了你几乎所有的东西,有时你会得到额外的好处,当 Z 被设置时,进位是零权,这意味着进位未设置是 "or equal".
我已经开始学习 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 位,则为零。
我花了几个小时想弄明白。
将进位位视为结果的下一个最高有效位。
考虑一步减去 "complement, add 1, add"。在硬件中,这是通过 LSB 的进位来实现的:0 表示加法,1 表示减法,C 表示加法。
示例:
1 - 4
0.001 // 1
1.011 + 1 // 4 complemented, carry in
+ -----
1.101 // -3, carry out
-1 - -2
1.111 // -1
0.001 + 1 // -2 complemented, carry in
+ -----
0.001 // 1, no carry out
您显示的 CMP 条件代码用于无符号算术。对于带符号的算术,您必须异或进位和符号位。通常也有一个测试。
例如,JG(更大的跳跃)是 ZF = 0 和 SF = OF
编辑,感谢@Blechdose 的鼓励,这里是一个示例,说明当补减数的符号未传播到 CF 时会发生什么(导致 x86 反转 CF 以进行减法):
1 - 4
.001 // 1
.011 + 1 // 4 complemented, carry in
+ -----
0.101 // -3, no carry out
4 - 1
.100 // 4
.110 + 1 // 1 complemented, carry in
+ -----
1.011 // 3, carry out
所以让我们尝试 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 是进位 out/borrow 并且 Z 是零
您的规则严格来说是未签名的规则。对于签名,我认为它类似于 if N != V then signed less than, if N == V then signed greater than.
所以 0 - 0 是 0 Z 标志它们相等 然后是 0 - 1 和 0 - 2 等。第二个操作数更大 Z 未设置且进位已设置。
直到我们得到 1 - 0 不为零且 C 未设置,左边的数字更大。
然后我们打等号(1 - 1)
还有一些Z没有设置C设置所以正确的数字更大。
然后2-0,2-1左大(Z没设置C没设置)然后2-2Z设置,然后2-3到2-7Z没设置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
请注意,要从进位位中获取借位,您需要反转进位 (borrow = ~carry)。 Carry out is 0 表示发生借位,如果您尝试使用铅笔和纸以小数形式从 1 中减去 4,这很明显。
如果没有借位则设置进位,例如取 4 - 1:
1001
100
+ 110
========
011
4 - 1 = 3 没有借位。 Z 未设置 C 已设置所以 4 > 1
我认为这里的底线是哪个操作数是源,哪个是目标,为什么他们很少正确记录它?任何时候你在指令集中使用比较,你都必须用固定的数字做一些实验,看看哪个操作数是哪个。您还必须非常小心地使用有符号或无符号大于或小于它会有所不同。有些指令集不提供一种或另一种风格,但这没关系,你真的不需要一长串有符号和无符号的这个和那个左右切换操作数并理解标志你可以找到一个简单的如果进位或不进位或如果 whatever 给了你几乎所有的东西,有时你会得到额外的好处,当 Z 被设置时,进位是零权,这意味着进位未设置是 "or equal".