You cannot use the CMP
instruction directly, since the flags are not valid operands for x86 instructions. They are only used implicitly by certain instructions.
The simplest solution is just to use a conditional branch. This works analogously to the JE
instruction you are already familiar with, except that it branches based on the value of the carry flag (CF), instead of the zero flag (ZF) like JE
does.
To conditionally branch on the status of the carry flag (CF), you would use JC
or JNC
. JC
will branch if the carry flag is set (CF == 1), whereas JNC
will branch if the carry flag is not set (CF == 0). The mnemonics for these opcodes are simply "Jump if Carry" and "Jump if Not Carry".
jc CarryFlagIsSet ; jump if CF == 1
; else fall through: code for CF == 0 goes here
Or do it the other way:
jnc CarryFlagIsNotSet ; jump if CF == 0
; else fall through: code for CF == 1 goes here
So, in keeping with your example, something like:
shr num, 1 ; put least significant bit in CF
jc num_was_odd ; (or jnc LSBNotSet aka num_was_even)
But as Peter Cordes points out in a comment, the code you have is almost certainly wrong, since the identical code will be executed whether or not the branch is taken. In other words, the branch destination is equivalent to the fall-through code. You probably want to have something more like:
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
(Except that it is much faster to operate on an enregistered value than one stored in memory, so if possible, you should place NUM
in a register. Also, assembly language opcodes and registers are case-insensitive, so you can just as easily write the code in lowercase. I think this is both easier to type and easier to read, but it's purely stylistic and therefore up to you.)
When you want to write branchless code (which almost always improves performance, if you can figure out a clever enough way to write the code), you can use the SBB
instruction. If both operands are the same register, this will set that register to -1 if the carry flag is set (CF == 1), or set that register to 0 if the carry flag is not set (CF == 0). This works because SBB
actually performs the operation DEST = (DEST - (SRC + CF))
. When DEST
and SRC
are the same value, this is equivalent to DEST = -CF
.
In cases where it would be more convenient to have the register's value mirror CF exactly, this can be combined with the 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
In certain cases, you can even use the ADC
instruction in an analogous manner. This performs the operation DEST = DEST + SRC + CF
. When DEST
and SRC
are both zero, this is equivalent to DEST = CF
. The tricky part about this is that the destination register has to be either pre-zeroed before the carry flag gets set, or zeroed in such a way that the carry flag is not affected:
; 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
Note that if you wanted to store the value of CF
in memory, you could use the following form of ADC
:
adc DWORD PTR [value], 0
On more modern architectures, you could use either the SETC
or CMOVC
instructions (or SETNC
/CMOVNC
for the converse logic—the mnemonics are the same as JC
/JNC
). These are generally even faster ways to write branchless code; however, neither are available on the 8086. Conditional set (SETcc
) instructions were introduced with the 386, while conditional moves (CMOVcc
) were introduced with the Pentium Pro.
jc
. See this documentation. – Hungrysbb cx,cx
to set CX to 0 or -1, if that's more useful. – Revjnc top_of_loop
. Or, if you're actually looping over the bits in a register, you shouldshr al, 1
/jnz top_of_loop
, to keep looping as long as there are any non-zero bits left. SHR sets ZF according to the result, as well as CF according to the last bit shifted out. (Why are you storing to memory and then shifting that. It's much faster to just shiftal
.) – Rev