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

3

2

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
Julejulee answered 26/12, 2016 at 19:46 Comment(3)
Use jc. See this documentation.Hungry
Or use sbb cx,cx to set CX to 0 or -1, if that's more useful.Rev
Also note that you have your branch backwards. The branch target for the taken case is the same address as for the fall-through not-taken case. You should jnc top_of_loop. Or, if you're actually looping over the bits in a register, you should shr 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 shift al.)Rev
P
3

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.

Pyroxylin answered 27/12, 2016 at 13:9 Comment(2)
In this particular case, having NUM in a register won't be much faster, since a message display and character input are involved in the main loop. In other situations, it would make a difference.Harriman
Normally, I'd say that would be true. On an 8086, I'm pretty sure it would still make a difference. Memory accesses are very expensive. But the advice was certainly meant more generally, in the overall spirit of this site as being a reference for many, as opposed to a help desk for one.Pyroxylin
E
1

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

Eberhart answered 19/11, 2017 at 5:4 Comment(14)
lahf / and is fine, but storing the mask in memory (instead of an immediate) makes no sense. Also, moving to cfVal and reloading is also pointless. Just mov dl, 1 / and dl, ah. Or use CFMASK equ 1 to define an assembler constant.Rev
@PeterCordes what about now ?Eberhart
Yeah, that's better. I'd recommend naming mask something more specific, like CFmask. I'd also recommend using '0' instead of 48, because character constants are more self-documenting.Rev
I have a question regarding Reading 32bit number in 8086 should i post it ?Eberhart
You mean string to 32-bit integer in dx:ax? I think that's been asked before. Try searching some more for multi-digit number stuff.Rev
Hmm, I found #45904575 for printing a 32-bit integer in DX:AX. (Linked from the the x86 tag wiki in the multi-digit number section of the FAQ. Have a look and see if it mentions anything about reading 32-bit numbers.Rev
I am getting a 8bit num from user and aggregating it and then storing it in a variable num32bit dd ? but for some reason it stores unknown values after crossing 5 digits. the link you send does not show how to scan the 32bit number.Eberhart
16-bit mul only uses AX as its input. It writes the result to DX:AX, but it ignores DX on input. It's a 16x16 -> 32bit multiply, not a 32x16 -> 32bit multiply. 6 digit numbers are greater than 2^16 -1 = 65535. I'm guessing your code also breaks for 65537 and larger. The easiest way is probably to do the multiply-by-10 step using left-shift and add / adc (i.e. n*10 = n * 2 * 5 or n * 2 + n * 8). Or just use 32-bit or 64-bit code so you don't need to deal with extended-precision numbers. (Or look at 32-bit compiler output for uint64_t).Rev
yeah it stores 0001h for 65537Eberhart
but my multipler is declared as multiplier dd 10. how would i use ax:dx when multiplying ?Eberhart
I mean how would i tell assembler when multiple include dx as well not just ax ?Eberhart
@Peter Cordes I think this add ax,bx adc dx,cx should do the job.Eberhart
I mean how would i tell assembler when multiple include dx as well not just ax ? You can't, x86 doesn't have an instruction like that, and no x86 assembler I'm aware of has a "pseudo-instruction" that would emulate that operation with multiple machine instructions. This is why I recommended replacing the multiply with shift / add, because it's easier to do that for extended precision. You could double once with add ax,ax / adc dx,dx then save that n*2 and either do an extended-precision shift or just double twice more to get n*8, then add/adc your n*2Rev
Let us continue this discussion in chat.Eberhart
R
1

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).

Rev answered 19/11, 2017 at 23:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.