ADD 可以同时设置进位和溢出标志吗?



我已经在汇编工作了几个月。我知道携带标志和溢出标志之间的区别。

我的问题(我在谷歌上找不到答案)是是否可以同时激活进位和溢出标志。 我找不到我在哪里听说只能激活一个标志(或携带或溢出)(不能同时激活两个)。

假设我们有下一个代码:

xor eax, eax
xor ebx, ebx
mov al, 128
mov bl, 128
add al, bl

最后一行是否激活 C 和 O 标志?(请注意,AL 和 BL 具有相同的符号)

在上述情况下,我会说只有进位会被激活。我错了吗?

您可以将此结果推广到同时设置 C 和 O 的确切条件。加法中 C 和 O 的规则是(至少这是一种可能的表述)

  • C = 执行顶部位
  • O = 进位进位或进位进位
  • XOR 进位进位

因此,它们都可以是真的,即确切地说,当顶部位执行而不是执行时。

添加 127 或更少不能同时设置 OF 和 CF(对于任何起始值)。 也许您阅读的内容是在谈论添加1? (请注意,inc/dec保留 CF 不变,因此这仅适用于add al, 1)

顺便说一句,128 + 128 是设置两个标志的第一对输入(对于 8 位操作数大小),如果您使用来自0..255的嵌套循环进行搜索。 我写了一个程序来做到这一点。

global _start
_start:
mov al, 128
add al, 128        ; set a breakpoint here and single step this, then look at flags
xor ecx, ecx
xor edx, edx
.edx:                       ; do {
.ecx:                       ;   do {
movzx eax, cl           ; eax as a scratch register every iteration
; writing to eax instead of al avoids a false dependency for performance.
; mov eax, ecx is just as good on AMD and Haswell/Skylake, but would have partial-reg penalties on earlier Intel CPUs.
add   al, dl
seto  al                ; al = OF  (0 or 1)
lahf                    ; CF is the lowest bit of FLAGS.  LAFH loads AH from the low byte of FLAGS.
test  ah, al            ; ZF = OF & CF
jnz   .carry_and_overflow
.continue:
add   cl, 1             ;    add to set CF on unsigned wraparound
jnc   .ecx              ;    } while(cl++ doesn't wrap)
; fall through when ECX=0
add   dl, 1
jnc   .edx              ; } while(dl++ doesn't wrap)
xor   edi,edi
mov   eax, 231
syscall          ; exit(0) Linux 64-bit.
.carry_and_overflow:
int3             ; run this code inside GDB.
; int3 is a software breakpoint
; if execution stops here, look at cl and dl
jmp  .continue

使用 NASM 或 YASM 在 Linux(或任何操作系统,如果在退出系统调用之前停止在断点处)上构建。

yasm -felf64 -Worphan-labels -gdwarf2 foo.asm &&
ld -o foo foo.o

我用gdb ./foo在 gdb 下运行了这个,然后run.

在我的~/.gdbinit中,我有:

set disassembly-flavor intel
layout reg
set print static-members off
set print pretty on
macro define offsetof(t, f) &((t *) 0)->f)  # https://stackoverflow.com/questions/1768620/how-do-i-show-what-fields-a-struct-has-in-gdb#comment78715348_1770422

layout reg将 GDB 置于文本 UI 全屏模式(而不是面向行)。 在第一个c(继续)命令(在rax=128/rdx=128处)后按住 return 一段时间后,然后点击 control-L 重新绘制屏幕,因为 GDB 的 TUI 东西效果不佳,我得到了这个:

┌──Register group: general──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│rax            0x301    769                                    rbx            0x0      0                                       │
│rcx            0xa7     167                                    rdx            0x85     133                                     │
│rsi            0x0      0                                      rdi            0x0      0                                       │
│rbp            0x0      0x0                                    rsp            0x7fffffffe6c0   0x7fffffffe6c0                  │
│r8             0x0      0                                      r9             0x0      0                                       │
│r10            0x0      0                                      r11            0x0      0                                       │
│r12            0x0      0                                      r13            0x0      0                                       │
│r14            0x0      0                                      r15            0x0      0                                       │
│rip            0x4000a5 0x4000a5 <_start.carry_and_overflow+1> eflags         0x202    [ IF ]                                  │
│cs             0x33     51                                     ss             0x2b     43                                      │
│ds             0x0      0                                      es             0x0      0                                       │
│fs             0x0      0                                      gs             0x0      0                                       │
│                                                                                                                               │
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│0x400089 <_start.edx+5>                 seto   al                                                                           │
│0x40008c <_start.edx+8>                 lahf                                                                                │
│0x40008d <_start.edx+9>                 test   ah,al                                                                        │
│0x40008f <_start.edx+11>                jne    0x4000a4 <_start.carry_and_overflow>                                         │
│0x400091 <_start.continue>              add    cl,0x1                                                                       │
│0x400094 <_start.continue+3>            jae    0x400084 <_start.edx>                                                        │
│0x400096 <_start.continue+5>            add    dl,0x1                                                                       │
│0x400099 <_start.continue+8>            jae    0x400084 <_start.edx>                                                        │
│0x40009b <_start.continue+10>           xor    edi,edi                                                                      │
│0x40009d <_start.continue+12>           mov    eax,0xe7                                                                     │
│0x4000a2 <_start.continue+17>           syscall                                                                             │
│0x4000a4 <_start.carry_and_overflow>    int3                                                                                │
>│0x4000a5 <_start.carry_and_overflow+1>  jmp    0x400091 <_start.continue>                                                   │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
native process 5074 In: _start.carry_and_overflow                                                             L37   PC: 0x4000a5 
Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
(gdb) 

这个模式很有趣,但一旦你停下来思考数学,就很容易解释:对于 DL=128,从 128 到 255 的所有 CL 值都设置 CF 和 OF。 但对于更高的 DL 值,只有从 128 到小于 255 的某个值的 CL 同时设置两者。 因为 133 例如表示 133 - 256 = -123,并且(-123) + (-5) = -128,没有符号溢出。 非常大的无符号值表示 -1 或略低于的有符号值。


另请参阅:

  • 了解进位与溢出条件/标志,以及 x86 标签 wiki 中的其他有用链接。
  • 签名、携带和溢出标志(程序集)
  • 关于程序集 CF(进位)和 OF(溢出)标志

是的,在您给出的示例中,将设置进位和溢出。

溢出标志与有符号数字有关。 您的示例是添加 -128 + -128。结果 (-256) 显然不适合 8 位寄存器,因此设置了溢出标志。

最新更新