代码之家  ›  专栏  ›  技术社区  ›  Cretu Bogdan

加法是否可以同时设置进位和溢出标志?

  •  2
  • Cretu Bogdan  · 技术社区  · 7 年前

    我在装配厂工作了几个月。我知道进位标志和溢出标志之间的区别。

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

    假设我们有下一个代码:

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

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

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

    3 回复  |  直到 7 年前
        1
  •  3
  •   Peter Cordes Steve Bohrer    7 年前

    添加127或更少不能同时设置和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下用 gdb ./foo 然后 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全屏模式(而不是面向行)。按住return键一段时间后 c (继续)命令(在 rax=128 / rdx=128 ),然后点击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或略低于1。


    另请参见:

        2
  •  3
  •   user555045    7 年前

    您可以将这个结果推广到C和O都被设置的确切条件。加法中C和O的规则是(至少这是一种可能的公式)

    • C=顶部钻头的执行
    • O=进位到最高位的异或进位到最高位的

    所以它们都可能是真的,也就是说,当顶部位有进位,但没有进位时。

        3
  •  2
  •   prl    7 年前

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

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