汇编 - SHL 指令不打开进位标志

Assembly - SHL instruction doesn't turn on carry flag

我有这些代码行应该将进位标志设置为 1。我通过 emu8086 中的模拟器 运行 这段代码,它告诉我没有设置进位标志。在我看来,应该设置进位标志,因为数字 0xC7(二进制:1100 0111)向左移动,所以进位标志应该 return 1。希望有人能提供帮助。

.model small

.data

.code

main proc
mov ax, 0xC7          
xor cl , cl 
shl ax,1 
jnc a1 
inc cl
a1:
shl ax,1 
jnc a2 
inc cl
a2:
shl ax,1 
jnc a3 
inc cl
a3:
shl ax,1 
jnc a4 
inc cl
a4:

endp

end main

所以您想进行 16 位移位,但从低半部分的顶部设置了 CF?我不知道你为什么想要那个,特别是如果你要 jnc 而不是使用 adc cl, 0 来做 CL += CF。

有更有效的方法来 popcnt 寄存器的高半字节,例如制作寄存器的副本并实际移出位而不是仅仅使用寄存器。或者在将寄存器中的这些位隔离后使用 popcnt 指令,如果您可以假设现代 CPU.


无论如何,没有针对此的单一指令,但是有几个选项可以根据位设置 CF,具体取决于您需要的 CPU 兼容性。最直接的是:

;; 386 for BT/BTS
   shl   ax, 1
   bt    ax, 8        ; set CF from the bit that was previously the top of AL

   adc   cl, 0        ; CL += CF

还有这种方式在旧的 CPUs 上可能很慢:x86 shifts/rotates 用 &31 屏蔽计数(或 &63 用于 64 位移位)所以shifts/rotates 比 32 位窄 可以 使用与 operand-size 一样大的计数并且不将其计为 0。

rol根据"wrapped around"从高到低的最后一位设置CF。 8 位寄存器的计数为 8 时保持不变,最后一位循环为低位。此外,286 之前的 CPUs 并没有掩盖计数,它们只是占用了与移位计数一样多的时钟周期。 (所以这在没有用于 constant-time 旋转的桶形移位器的旧 CPUs 上非常慢。)

或者如果 Operation section of the manual 反映了实际的 286 实现,则旋转 mod size 进行实际的移位工作,但是 count != 0 检查是否设置 FLAGS 是基于count & 1Fh 而不是 count mod 8。如果是,CF = LSB(dest)

;; 186 for immediate-count shifts/rotates
   shl   ax, 1
   rol   ah, 8        ; set CF from the bit that was previously the top of AL

   adc   cl, 0        ; CL += CF

显然根据特定位设置ZF更容易,例如

   add   ax, ax     ; AX <<= 1
   test  ah, 1      ; ZF = low bit of AH

   ;test  ax, 1<<8   ; ZF = low bit of AH   same code size, actually
   jz   no_increment
   inc   cl
no_increment:

但是与 adc 相比,分支很糟糕。

请注意,shl by 1 基本上没有任何优势,除非您可以将它与寄存器目标一起使用。在某些 CPU 上,使用 add ax,ax 到 left-shift 更有效。它甚至以相同的方式设置 CF(根据之前的最高位)。