How to check if the CF flag is 1 in emu8086?

A.Bon picture A.Bon · Dec 26, 2016 · Viewed 8.4k times · Source

I am trying to find if the CARRY flag is 1 or not, but I don't know how to check it. I wrote the below code, but I need some help for question marks I put in.

  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

Answer

Cody Gray picture Cody Gray · Dec 27, 2016

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.