我想编写一个简单的代码(或算法)来设置/清除溢出标志。对于设置OF,我知道可以使用有符号值。但是如何清除它呢?
有很多可能的解决方案。
例如,test al,al
可以清除 OF
标志而不影响寄存器内容。
或者,如果你不想影响其他标志,你可以直接修改 *FLAGS
寄存器。例如,在32位系统中,这样做看起来像:
pushfd ; Push EFLAGS onto the stack
and dword [esp], ~0x800 ; Clear bit 11 (OF)
popfd ; Pop the modified result back into EFLAGS
编辑:根据Peter Cordes的建议,将or al, al
更改为test al, al
。(效果相同但后者在性能方面更优)
test al,al
是根据寄存器内容设置标志位的更好习惯用语。它只写入标志位,因此不会增加涉及al
的依赖链长度。但是pushf
/popf
方法也有其优点(因为sahf
/lahf
不包括OF
)。 - Peter Cordesor
和 test
的观点很好;我相应地更新了我的答案。谢谢! - user1354557提供的条件:
CF
标志假设寄存器是 al
。(只能使用字节寄存器 r/8 的 setc
)
; clear OF-Flag, preserve CF
setc al
sar al, 1
假设寄存器是al
。(适用于r/8、r/16、r/32、r/64)
; set OF-Flag, preserve CF
mov al, 0x7F
inc al
; clear OF-Flag, preserve CF
mov al, 0x0
inc al
不同的方法,如果您可以假设:
adx
启用的处理器(您可以使用 grep adx /proc/cpuinfo
检查CPU标志)假设寄存器是 eax
。(需要r64/r32)
; clear OF-Flag, preserve CF
mov eax, 0x0
adox eax, eax
; set OF-Flag, preserve CF
mov eax, 0xFFFFFFFF
adox eax, eax
xor
(或类似的指令)代替mov
,因为这将清除CF
。0xFFFF ...
+ 1会进行包装,产生进位(ADOX将其输出为OF=1)。但是如果您事先设置了不同的值,那么这是一个有趣的方法来仅设置/清除OF而不触及其他标志。 - Peter Cordesadox
无法处理立即值。我已经编辑了代码。 - Joelpopf
在Skylake上的速度相当慢(大约每20个周期执行一次);如果您需要清除或设置OF,则最好将其作为ALU指令的副作用完成,特别是您已经打算使用它进行有用计算时,您知道不会或会溢出。 (通常很难找到一个会溢出的,与CF不同,您可以始终使用几乎包围所有输入的常量进行sub
而不是add
,除了非常小的范围之外)。
如果您需要仅设置/清除OF而不影响其他条件码,那么是的,pushf
/popf
是正确的方法。lahf
/ sahf
无法获取OF,因为OF位于EFLAGS中的第11位,超出了低8位。
test al,al
(或任何相同,相同寄存器)清除OF和CF,就像比较/减零一样。其他标志根据值进行有用的设置。
xor eax,eax
清除EAX,并清除OF / SF / CF,设置ZF / PF。你经常需要一个清零的寄存器,所以如果你需要清除OF(例如,对于adox
扩展精度链的开始), 那么你可以通过安排代码使最后设置标志的指令是异或清零指令来一举两得。add
不会越过无符号虚拟地址空间的中心,因此清除了OF
。但是这种假设可能会在未来具有完全64位虚拟地址的CPU上被打破,因为那时在签名环绕边界周围将没有虚拟地址空间中的空隙,因此单个连续数组可以跨越它。而且这已经可以在32位代码中发生,在64位内核下运行或者在不使用2G:2G内核:用户分割的虚拟地址空间的32位内核下运行。
xor eax, eax
/ cmp al, -128
设置OF标志,只需要4个字节的代码。这可能是最便宜的方法,而且不像sub
或其他方法那样,它不会写入任何部分寄存器(或任何完整的寄存器)。它仍然将EAX清零。
0 - -128
wraps to -128
, i.e. signed OF。
-128..+127
的值。最小的负数是一个特例,没有逆元。它的绝对值/负数是它自己,或者更准确地说,这些函数会溢出。(或者您可以将绝对值操作视为具有带符号输入和无符号输出,因此结果为+128,即0x80。x86没有整数绝对值指令(请准备一个-x
,然后测试/cmov),但使用SSSE3它确实有向量整数pabsb
)
对于 AL 中的任何已知值,除了 -1,都有一个 cmp al, imm8 会设置 OF。对于从 0..127 的任何值,cmp al, -128 会环绕。对于从 -2..-128 的任何值,cmp al, +127 会环绕并因此设置 OF。对于 -1,减去 127 只会将其带到 -128。减去 -128 会将其提升到 +127。不幸的是,我认为没有一种单指令的方法可以在没有寄存器中的已知值的情况下设置 OF。push rax
xor eax,eax
cmp al, -128
pop rax
pushf
/popf
更快。通常情况下,您可以覆盖某些内容,否则无法覆盖堆栈。
setno al # OF=0 -> AL=1 OF=1 -> AL=0
cmp al, -127 # 1 - -127 = 128 = -128 0 - -127 = +127
fstsw [bp-2]
/ pop ax
/ lahf
。或者在286及更高版本fstsw ax
/lahf
。 - Peter Cordesfstsw ax
/ SAHF
(将AH存储到FLAGS)/ ja
或jb
或jp
。(LAHF / SAHF助记符对我来说似乎是反过来的;按照这个顺序编写,我的大脑认为将AH加载到FLAGS中,而不是从FLAGS中加载AH)。无论如何,这就是为什么更近期的fucomi
和SSE ucomiss
/ ucomisd
指令会像无符号比较一样设置标志位,当出现无序(NaN)时设置PF,以便与遗留方法中x87状态字中的C3 / C2 / C0位对齐。 - Peter Cordes
mov ax, 1; inc ax
- Marc B