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

12.4k views Asked by At

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
3

There are 3 answers

2
Cody Gray - on strike On

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.

14
Ahtisham On

There is another way to check if CF is set or not and that is with masking.

Here is a working example:

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

Flag register

0
Peter Cordes On

If you want to branch on it, use jc / jnc; that's exactly what they're for.

If you want the value of CF as a 0 / 1 integer: Cody's answer is good, but there are some other ways.

If emu8086 supports the undocumented salc instruction, you can use it to set al = 0 / -1. It's a single-byte encoding of sbb al,al, and is supported in 16 and 32-bit mode by all Intel and AMD CPUs, including current ones like Skylake and apparently even Knight's Landing (Xeon Phi). SSSE3 / SSE4 instruction encodings could have been one byte shorter if Intel had dropped support for it. (See Agner Fog's blog post Stop the instruction set war).

But if Intel insists on supporting it, we might as well use it even if they don't document it for some reason. (Assuming that we're optimizing for code size, i.e. for 8086, not for an actual modern CPU. salc is 3 uops but 1c latency on SnB/Haswell/Skylake)


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

Or (optimized version of @Doda's answer): lahf is efficient.

lahf            ; 1 byte, 1 uop (SnB-family).  CF is the low bit of FLAGS.
and   ah, 1     ; 3 bytes

Or if you want the 0/1 integer somewhere else, e.g. so you can print it with an int 21h system call like @Doda's answer):

lahf
mov   dx, 1       ; or just dl if you don't need to zero-extend it.
and   dl, ah

Upside vs. Cody's sbb/neg: avoids sbb same,same (2 uops, and false dependency on the register on CPUs other than AMD Bulldozer-family)

Downside: worse code size (matters a lot for a real 8086). More instructions. (But same number of uops on Intel SnB-family before Broadwell, where sbb is 2 uops). Partial-register problems from writing dl and then reading dx / edx on some CPUs (Intel pre-Ivybridge).


If you aren't restricted to 8086-only, use setc dl (or any 8-bit register, or directly to memory).

Also very good, especially on Broadwell and later: from @Cody's answer:

xor  eax,eax
; do something that sets CF
adc  eax, 0

adc has no advantage over setc al though, if eax was xor-zeroed (avoids partial-register stalls and merging penalties on Intel P6-family from writing al and then reading eax).