How to set carry flag in specific bit in GPR without shifts / rotations?

442 views Asked by At

I'm writing a program in NASM for Intel 80386 processor and i need to set the value of carry flag in a specific bit in GPR (general purpose register) without changing other bits in register.

Are there any possibility of doing so without using shifts/rotations of any kind?

2

There are 2 answers

4
Thomas Jager On BEST ANSWER

One branch-free way of doing this would be to fill a scratch register with the carry flag, mask for the bit you want, then or it with your target register.

Using EAX as scratch (taking this as 32-bit values being manipulated):

sbb eax, eax
and eax, 1 << 16  ; Adjust bitshift in constant for the desired bit. Multiple bits can be used.

If the carry flag is unset, sbb will do eax = eax - eax = 0. If the carry flag is set, sbb will do eax = eax - (eax + 1) = -1, so all bits are set. The desired bit(s) is/are then masked in.

After that, you need to set the appropriate bit(s) in the target. If the bit is in an initial known state, this could be simplified. Using EBX as target:

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

Depending on what previously happened to the scratch register (EAX here) it might be worth looking at some optimization information like https://www.agner.org/optimize/. Some processors will recognize that the new value of EAX doesn't depend on the old value, some will see it as having a (false) dependency on that old value.

After looking at it, the document "Optimizing subroutines in assembly language" mentions the above sbb trick in the section "Replacing conditional jumps with bit-manipulation instructions".

Using EAX (the accumulator) as the scratch register will result in smaller code size.

0
fuz On

Predictable Case

If the value CF assumes is highly predictable, use a conditional jump and code like this:

    ... 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:

Unpredictable Case

If branchless code is desired (e.g. because the value of CF is hard to predict or if this is a cryptographic application), consider using a conditional move.

Computing CF is on the Critical Path

Assuming we would like to set the least significant bit in eax to the value of CF after performing some operation:

    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

This code has more instructions than that in Thomas Jager's answer, but its critical path latency is shorter, assuming computing CF is on the critical path but computing the prior value of eax is not. Whether it's actually better depends a lot on the circumstances.

Neverthless, the simple variant

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

is probably best for this specific case (set least significant bit to CF) as it avoids two µops while having the same critical path latency.

Computing EAX is on the Critical Path

If on the other hand computing eax is on the critical path but computing CF is not, Thomas Jager's solution is good, but it needs to be tweaked to clear the least significant bit of eax beforehand so the bit is cleared if CF was clear.