如何在没有移位/旋转的情况下在GPR的特定位设置进位标志?
我正在为 Intel 80386 处理器在 NASM 中编写程序,我需要在 GPR(通用寄存器)的特定位中设置进位标志的值,而不更改寄存器中的其他位。
是否有可能在不使用任何类型的移位/旋转的情况下这样做?
回答
这样做的一种无分支方法是用进位标志填充临时寄存器,掩码您想要的位,然后或用您的目标寄存器填充它。
使用EAX如划痕(以此为正被操纵的32位的值):
sbb eax, eax
and eax, 1 << 16 ; Adjust bitshift in constant for the desired bit. Multiple bits can be used.
如果未设置进位标志,sbb将执行eax = eax - eax = 0. 如果设置了进位标志,sbb将会执行eax = eax - (eax + 1) = -1,所以所有位都被设置。然后将所需的位屏蔽。
之后,您需要在目标中设置适当的位。如果该位处于初始已知状态,则这可以简化。使用EBX为目标:
and ebx, ~(1 << 16) ; Same value at before. This doesn't need to be a shifted bit, it could be a number.
or ebx, eax
根据之前发生在临时寄存器上的情况(EAX此处),可能值得查看一些优化信息,例如https://www.agner.org/optimize/。一些处理器会认识到新值EAX不依赖于旧值,一些处理器会认为它对旧值有(错误的)依赖。
看了之后,文档“Optimizing subroutines in assembly language”sbb在“用位操作指令替换条件跳转”一节中提到了上述技巧。
使用EAX(累加器)作为暂存寄存器将导致更小的代码大小。
- Also consider using `cmovc` to shorten the critical path latency.