x86 程序集:ADD/SUB/INC/DEC 没有溢出和分支

x86 assembly: ADD/SUB/INC/DEC without overflow and branching

在 x86 ASM 中,是否可以禁止在不带分支的情况下进行加法或减法时溢出?因此,例如,当从 0x01 中减去 0x02 时,它会设置 0x00 而不是 0xFF。

我认为这可能是不可能的,所以我也对这个问题的更严格形式的答案感兴趣,其中只有 0x01 added/subtracted。我有一个想法是这样的(OF 是溢出标志):

dec eax
add eax,OF

我不知道其他架构,但对于 i386,我找不到它的操作码,因为显然标志不能被解释为整数并用于算术运算。我找到了一个可行的解决方案,但仅在未使用较高字节时适用于最低字节:

dec ax
sub al,ah
xor ah,ah

有没有更好的方法来做到这一点,也许也适用于更一般的情况?

在递增1的情况下(使用add #1,而不是inc)你可以在之后sbc #0实现饱和。与减 1 类似:使用 sub #1 后跟 adc #0.

或者考虑使用 SSE,它支持在单个指令中进行饱和整数算术运算。

您可以在较新的处理器上使用 cmov条件 MOV)系列指令来避免在许多情况下发生分支,包括您描述的示例。如果标志值设置得当,这些指令的行为类似于常规 mov,否则什么都不做。

http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/mergedProjects/instructions/instruct32_hh/vc35.htm

但是:

I had an idea that goes like this (OF being the overflow flag)

这不是你想要的溢出标志;这表明该值溢出了使用 2 的补码表示的边界(因此对于 16 位字值,如果它变得小于 -32768 或大于 32767)。您似乎正在使用无符号数(并希望钳位为 0),所以您想要的是 carry 标志。

例如,如果有进位(如果结果从 0 到 0xFF 换行),您可以使用 CMOVC 加载新值。

sub ax, 1
xor bx, bx
cmovc ax, bx

这适用于减去任何值。然而,事实证明,如果您只想要递减,则有一种更简单的方法。您可以减去 1,然后使用执行此操作的指令添加 0 + 进位:

sub ax, 1
adc ax, 0

请注意,您不能使用 dec 指令代替 sub,因为 dec 不影响进位标志。

反其道而行之(加法),您使用 sbb 代替 adc(当然还有 add 代替 sub)。对于一般情况,您可以执行以下操作:

add ax, 1
mov bx, 0FFFFh
cmovc ax, bx