我试图找出CARRY标志是否为1,但我不知道如何检查它。我写了下面的代码,但是我需要一些帮助来处理问号。
LEA DX, MSG
MOV AH, 09H
INT 21H
MOV AH, 01H
INT 21H
MOV NUM, AL
SHR NUM, 1
CMP ??, 1
JE FINISH
FINISH: MOV AH, 4CH
INT 21H
NUM DB 0
RET
答案 0 :(得分:3)
您不能直接使用CMP
指令,因为这些标志不是x86指令的有效操作数。它们仅由某些指令隐式使用。
最简单的解决方案就是使用a conditional branch。这类似于您已经熟悉的JE
指令,除了它根据进位标志(CF)的值进行分支,而不是像JE
那样的零标志(ZF)。< / p>
要有条件地分支进位标志(CF)的状态,您可以使用JC
或JNC
。如果进位标志被设置(CF == 1),JC
将分支,而如果进位标志不设置(CF == 0),JNC
将分支。这些操作码的助记符只是“ J ump如果 C arry”和“ J ump如果 N ot <强> C 强> ARRY”。
jc CarryFlagIsSet ; jump if CF == 1
; else fall through: code for CF == 0 goes here
或者以另一种方式做到:
jnc CarryFlagIsNotSet ; jump if CF == 0
; else fall through: code for CF == 1 goes here
因此,与您的示例一致,例如:
shr num, 1 ; put least significant bit in CF
jc num_was_odd ; (or jnc LSBNotSet aka num_was_even)
但as Peter Cordes points out in a comment,您拥有的代码几乎肯定是错的,因为无论是否采用分支,都将执行相同的代码。换句话说,分支目的地等同于直通代码。你可能想要更像:
TOPOFLOOP:
LEA DX, MSG
MOV AH, 09H
INT 21H
MOV AH, 01H
INT 21H
MOV NUM, AL
SHR NUM, 1
JC TOPOFLOOP ; keep looping as long as CF == 1
; otherwise, if CF == 0, fall through to FINISH
; (use JNC to reverse the logic)
FINISH:
MOV AH, 4CH
INT 21H
RET
(除了操作注册值比存储在内存中的值 更快,所以如果可能,你应该将NUM
放在寄存器中。此外,汇编语言操作码和寄存器是不区分大小写的,因此您可以轻松地以小写形式编写代码。我认为这更容易键入和更容易阅读,但它纯粹是风格,因此取决于您。)
如果您想编写无分支代码(几乎总能提高性能,如果您能够找到一种足够聪明的方法来编写代码),则可以使用SBB
instruction。如果两个操作数都是相同的寄存器,如果进位标志置位(CF == 1),则将该寄存器设置为-1,或者如果未设置进位标志(CF == 0),则将该寄存器设置为0。这是有效的,因为SBB
实际执行了操作DEST = (DEST - (SRC + CF))
。如果DEST
和SRC
的值相同,则相当于DEST = -CF
。
如果准确地使寄存器的值镜像CF更方便,可以将其与NEG
instruction结合使用:
; Perform operation that will set CF.
...
; Set AX to the same value as CF.
sbb ax, ax ; CF == 1 then AX = -1; otherwise, AX = 0
neg ax ; AX == -1 then AX = 1; otherwise, AX = 0
在某些情况下,您甚至可以以类似的方式使用ADC
instruction。这将执行操作DEST = DEST + SRC + CF
。如果DEST
和SRC
都为零,则相当于DEST = CF
。关于这一点的棘手部分是目标寄存器必须在进位标志置位之前预先归零,或者以进位标志不受影响的方式归零:
; Pre-zero AX in preparation for later use of ADC.
xor ax, ax
; Perform operation that will set CF.
; (NOTE: Cannot modify AX register here, nor AL nor AH!)
...
; Set AX to the same value as CF.
adc ax, ax
; Perform operation that will set CF.
...
; Zero AX in such a way that flags are not clobbered.
mov ax, 0
; Set AX to the same value as CF.
adc ax, ax
请注意,如果您想将CF
的值存储在内存中,可以使用以下ADC
形式:
adc DWORD PTR [value], 0
在更现代的体系结构中,您可以使用SETC
或CMOVC
指令(或SETNC
/ CMOVNC
作为逆向逻辑 - 助记符与{相同{1}} / JC
)。这些通常是编写无分支代码的更快的方法;但是,8086上都没有。{38}引入了Conditional set (SETcc
) instructions,而Pentium Pro引入了conditional moves (CMOVcc
)。
答案 1 :(得分:1)
还有另一种方法可以检查CF是否已设置,而且是masking。
这是一个有效的例子:
TITLE 'Check if Carray Flag is set or not'
.model small
.data
cfMask equ 01h ; 01h because CF Flag is the least significant bit (right-most-bit) of the flag register
cfMsg db 10,13,'CF: $'
.code
.startup
mov ax, @data
mov ds, ax
mov ah, 09h
mov dx, offset cfMsg
int 21h
lahf ; Loads contents of flag register into ah
and ah, cfMask ; Check if CF is set or not by anding it with 1
mov dl, ah
add dl, 48
mov ah, 02h
int 21h
mov ah, 04ch
int 21h
end
答案 2 :(得分:1)
如果您想对其进行分支,请使用jc
/ jnc
;这正是他们所要做的。
如果你希望CF的值为0/1整数:Cody的答案是好的,但还有其他一些方法。
如果emu8086支持the undocumented salc
instruction,您可以使用它来设置al
= 0 / -1。它是sbb al,al
的单字节编码,并且在所有Intel和AMD CPU中都支持16和32位模式,包括Skylake和显然甚至是Knight's Landing(Xeon Phi) )。如果英特尔放弃对它的支持,SSSE3 / SSE4指令编码可能缩短一个字节。 (参见Agner Fog的博文Stop the instruction set war)。
但如果英特尔坚持支持它,我们也可以使用它,即使它们由于某种原因没有记录它。 (假设我们正在优化代码大小,即8086,而不是实际的现代CPU。salc
是3 uops但是SnB / Haswell / Skylake的1c延迟)
salc ; 1 byte (but 3 uops on SnB-family)
and al, 1 ; 2 byte special encoding of and al, imm8
; neg al ; 2 bytes also an option
或(@ Doda答案的优化版本):lahf
效率很高。
lahf ; 1 byte, 1 uop (SnB-family). CF is the low bit of FLAGS.
and ah, 1 ; 3 bytes
或者如果你想在其他地方使用0/1整数,例如所以你可以用@ {Doda答案)int 21h
系统调用打印它:
lahf
mov dx, 1 ; or just dl if you don't need to zero-extend it.
and dl, ah
Upside vs. Cody&#39; sbb
/ neg
:避免sbb same,same
(2次uops,以及对除AMD Bulldozer系列以外的CPU上的寄存器的错误依赖)
下行:代码大小更差(真正的8086很重要)。更多说明。 (但在Broadwell之前,英特尔SnB系列的uop数量相同,其中sbb
为2 uops)。写入dl然后在某些CPU(Intel pre-Ivybridge)上读取dx / edx时出现部分注册问题。
如果您不仅限于8086,请使用setc dl
(或任何8位寄存器,或直接使用内存)。
也非常好,特别是在Broadwell和后来:来自@Cody的答案:
xor eax,eax
; do something that sets CF
adc eax, 0
adc
没有setc al
优势,if eax
was xor-zeroed(避免部分注册停顿和合并对英特尔P6系列的处罚,而不是写al,然后读取eax)。