为什么x86指令INC
(递增)和DEC
(递减)不影响CF
FLAGSREGISTER中的(进位标志)?
4 回答
要了解为什么您可能需要记住当前具有 32 位和 64 位值的“x86”CPU 的生命开始于更有限的 8 位机器,可以追溯到 Intel 8008。(我在 1973 年在这个世界上编码,我仍然记住(呃)它!)。
在那个世界里,寄存器既珍贵又小。您需要INC
/DEC
用于各种目的,最常见的是循环控制。许多循环涉及执行“多精度算术”(例如,16 位或更多!)通过设置INC
/DEC
设置零标志 ( Z
),您可以使用它们很好地控制循环;通过坚持循环控制指令不改变进位标志CF
(
一旦你习惯了丑陋的指令集,这工作得很好。
在具有更大字数的更现代的机器上,您不需要太多,因此INC
并且DEC
可以在语义上等同于ADD
...,1 等。事实上,这就是我在需要进位集时使用的:-}
大多数情况下,我现在远离INC
,DEC
因为他们会进行部分条件代码更新,这可能会导致管道中的有趣停顿,并且ADD
/SUB
不会。因此,在无关紧要的地方(大多数地方),我使用ADD
/SUB
来避开摊位。我仅在保持代码小问题时才使用INC
/ DEC
,例如,适合一个或两个指令的大小产生足够差异的高速缓存行。这可能是毫无意义的纳米[字面意思!]-优化,但我的编码习惯相当老派。
我的解释告诉我们为什么INC
/DEC
设置零标志 ( Z
)。INC
对于为什么/DEC
设置符号(和奇偶校验标志),我没有特别令人信服的解释。
编辑 2016 年 4 月:似乎在现代 x86 上可以更好地处理失速问题。请参阅INC 指令与 ADD 1:这有关系吗?
当你有由 inc/dec 设置的零标志时为什么要签名的问题最好用问题来解决:你宁愿没有选项a吗?
a) for (n=7;n>=0;n--) // translates to `dec + jns`
b) for (n=8;n>0;n--) // translates to `dec + jnz`
正如Ira Baxter已经澄清的那样,进位标志用于许多算法——不仅是多精度算术,而且还用于单色/cga/EGA 时代的位图处理:这会将 80 像素宽的行向右移动一个像素......
mov cx, 10
begin: lodsb
rcr al,1 // this is rotate though carry:
stosb // for the algorithm to work, carry must not be destroyed
LOOP begin //
但后来:为什么要平价?
我相信答案是为什么不。该指令集来自 70 年代后期,当时晶体管稀缺。拒绝为某些特定指令计算奇偶校验标志没有任何意义,只会增加 CPU 的复杂性。
指令inc和dec通常用于维护迭代或循环计数。使用 32 位,迭代次数可高达 4,294,967,295。这个数字对于大多数应用来说已经足够大了。如果我们需要比这更大的计数怎么办?我们必须使用 add 而不是 inc 吗?这就引出了第二个也是主要的原因。
进位标志检测到的条件也可以通过零标志检测。为什么?因为 inc 和 dec 仅将数字更改 1。例如,假设 ECX 寄存器已达到其最大值 4,294,967,295 (FFFFFFFFH)。如果我们然后执行
inc ECX
我们通常期望进位标志设置为 1。但是,我们可以通过注意 ECX = 0 来检测这种情况,这会设置零标志。因此,设置进位标志对于这些指令来说确实是多余的。
因为不需要影响。检查零标志就足够了。因此,在 inc 和 dec 指令之后,进位标志保持不变,在某些情况下这很有用。