我听说摩托罗拉 68000 和 Intel x86 架构处理左移溢出的方式不同。特别是 68k LSL 与英特尔 SAL/SHL 汇编指令。
有人知道这件事的具体情况吗?他们设置不同的标志,还是设置不同的标志?我试图在参考手册中查找这一点,但我没有发现任何区别。为什么要以不同的方式处理这种情况?
X 位不涉及。对 68000 标志的混淆是因为有两个左移指令:
x86 指令集没有那么强大。如果移位计数 = 1,则 OF,溢出标志 = (MSB XOR CF),即如果 MSB 由于 1 位移位而改变符号,则 OF = 1,否则 OF = 0。
如果移位计数 >1,则 OF未定义。 (英特尔 SHL 文档的 HTML 摘录)。
CPU 的程序员手册有以下细节:
X — Set according to the last bit shifted out of the operand;
unaffected for a shift count of zero.
N — Set if the result is negative; cleared otherwise.
Z — Set if the result is zero; cleared otherwise.
V — Always cleared.
C — Set according to the last bit shifted out of the operand;
cleared for a shift count of zero.
所以溢出标志的处理方式不同。如果乘以 2(一位左移)导致溢出,它将在 x86 中通知您。我不知道为什么它只针对 1 位移位。我猜(这只是一个猜测)OF 标志是根据“最后一个”位移设置的——这可能并不表明整个操作是否溢出,因此英特尔只是将其记录为“未定义”。
(是的,我正在查看 1979 年的摩托罗拉 68000 参考。)
可能你想到的是68000的相当奇怪的X位。eXtend 位本质上是 C(进位)位的副本,但不受非算术指令的影响。例如,假设您要添加 12 个字的整数。在 x86 中,您可能会看到如下内容:
.
.
loop:
ADC AX,[SI] ; recycle carry-out from last iter as carry-in to this one
LEA SI, [SI+2] ; flags untouched
INC BX ; BX is loop index. sets all flags except CF
CMP BX, 12 ; doh, changes carry (BUG)
JB loop
此代码不起作用,因为比较指令弄乱了进位标志。这是该指令在历史上很有用的原因之一,它在loop
不修改标志的情况下将 CX 倒数为零。dec
使用/倒数到零jnz
也可以,但会导致现代 x86 上的部分标志停止。不幸loop
的是现在也很慢,所以没有好的方法可以从大约 486 到 Sandybridge 进行这样的循环。
但在 68000 中:
.
.
loop:
ADDX.W (A0)+, D0 ; both C and X set the same
INC.W D7 ; D7 is loop index
CMP.W #12, D7 ; harms C, but X left intact
BCC loop
摩托罗拉认为他们是在帮程序员一个忙,但 X 位业务最终导致的混乱比它的价值还多。