0

我正在为英特尔 80386 处理器编写 NASM 中的程序,我需要在 GPR(通用寄存器)的特定位中设置进位标志的值,而不更改寄存器中的其他位。

是否有可能在不使用任何形式的轮班/轮换的情况下这样做?

4

2 回答 2

5

这样做的一种无分支方法是用进位标志填充临时寄存器,掩码你想要的位,然后用你的目标寄存器或它。

用作从头开始(将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(累加器)作为临时寄存器将导致更小的代码大小。

于 2020-12-09T14:15:22.140 回答
4

可预测的案例

如果该值CF假定是高度可预测的,请使用条件跳转和如下代码:

    ... operation that sets CF ...
    jnc   nc           ; skip setting bit if CF is clear
    or    eax, 1       ; set bit in eax
    jmp   end
nc: and   eax, ~1      ; clear CF in eax
end:

不可预知的案例

如果需要无分支代码(例如,因为 的值CF难以预测,或者如果这是一个加密应用程序),请考虑使用条件移动。

计算CF处于关键路径

假设我们想在执行一些操作后将最低有效位设置eax为值:CF

    mov    ecx, eax    ; make a copy of eax
    or     ecx, 1      ; set CF in the copy
    and    eax, ~1     ; clear CF in the original
    ... operation that sets CF ...
    cmovc  eax, ecx    ; set eax to ecx if CF was set

这段代码比Thomas Jager 的答案中的指令更多,但它的关键路径延迟更短,假设计算CF在关键路径上但计算的先验值eax不在。它是否实际上更好取决于环境。

尽管如此,简单的变体

    and     eax, ~1    ; clear CF in eax
    ... operation that sets CF ...
    adc     eax, 0     ; add carry flag to eax

可能最适合这种特定情况(将最低有效位设置为CF),因为它避免了两个微操作,同时具有相同的关键路径延迟。

计算 EAX 是关键路径

另一方面,如果计算eax在关键路径上但计算CF不在关键路径上,Thomas Jager 的解决方案很好,但需要对其进行调整以eax预先清除最低有效位,以便在清除时清除该位CF

于 2020-12-09T14:43:27.357 回答