Adding 127 or less can't set OF and CF at the same time (for any starting value). Maybe what you read was talking about adding 1
? (Note that inc
/ dec
leave CF unmodified, so this would only apply to add al, 1
)
BTW, 128 + 128 is the first pair of inputs that sets both flags (for 8-bit operand size), if you search with nested loops from 0..255
. I wrote a program to do exactly that.
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
Build on Linux (or any OS if you stop at a breakpoint before the exit system call) with NASM or YASM.
yasm -felf64 -Worphan-labels -gdwarf2 foo.asm &&
ld -o foo foo.o
I ran this under gdb with gdb ./foo
, then run
.
In my ~/.gdbinit
I have:
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
puts GDB into a text-UI fullscreen mode (instead of line-oriented). After holding down return for a while after the first c
(continue) command (at rax=128
/ rdx=128
), then hitting control-L to redraw the screen because GDB's TUI stuff doesn't work very well, I got this:
┌──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)
The pattern is interesting but easy to explain once you stop to think about the math: For DL=128, all CL values from 128 to 255 set both CF and OF. But for higher DL values, only CL from 128 to some value less than 255 set both. Because 133 for example represents 133 - 256 = -123, and (-123) + (-5) = -128
, with no signed OverFlow. Very large unsigned values represent signed values of -1 or just below.
See also: