汇编 - 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(根据之前的最高位)。
我有这些代码行应该将进位标志设置为 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(根据之前的最高位)。