2

I am currently learning x86 assembly language using NASM from the following website: https://www.asmtutor.com/ and I am presently on Lesson 11 if you require the full code of this example.

Using the divideLoop: subroutine below how does the cmp instruction not set the zero flag (ZF) after just the first iteration? Doesn't idiv set eax to the quotient part and edx to the remainder?

;EAX is set as 1 before the first iteration.

divideLoop: 
    inc     ecx
    mov     edx, 0
    mov     esi, 10
    idiv    esi
    add     edx, 48
    push    edx
    cmp     eax, 0
    jnz     divideLoop

I was under the impression that after idiv esi that eax would be set to 0 and edx would be set to 1 since 1 / 10 = 0R1 and therefore cmp eax, 0 would equal zero and thus set the zero flag.

Maybe I'm misunderstanding how idiv works with the quotient part or how cmp sets the zero flag?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
chromodyne
  • 29
  • 1
  • 5
    I copy/pasted that into a file (with `mov eax,1` first), and single-stepped it in GDB (after assembling with `nasm -felf32 foo.asm` / `ld -melf_i386 foo.o`. `cmp eax,0` *does* set ZF, so execution does fall out of the loop the first time. Yes, EAX = 1/10 = 0. If that's not happening for you, then EAX wasn't actually `1` before execution reached that label. (Or your CPU is broken, but that's not really plausible.) So this isn't a [mcve] of whatever you actually did. – Peter Cordes Aug 10 '21 at 18:27
  • BTW, zero-extension into EDX:EAX (via `mov edx,0`) should normally go with `div`, not `idiv`. Binary integer => string of ASCII digits for signed values normally involves unsigned numbers, after taking the absolute value of the input. (Otherwise you'd mishandle signed-overflow in abs(INT_MIN)). So zero-extension of the dividend is correct, and your base of 10 is normally considered unsigned. – Peter Cordes Aug 10 '21 at 18:33
  • Although the key thing is zero-extending the input; after doing that, I don't think `idiv` can actually cause a bug. And on some older CPUs (like Sandybridge), `idiv r32` is actually fewer uops (10) than `div r32` (11). However, https://uops.info/ shows better best-case latency for `div` by 1 cycle on SnB (18c vs. 19c) – Peter Cordes Aug 10 '21 at 18:35
  • 1
    Also, `push` in one loop then `pop` in another is pretty clunky. [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) shows a better way. – Peter Cordes Aug 10 '21 at 18:37
  • 1
    Thanks for all the testing Peter. I have been stepping through it with gdb over and over again trying to find out what went wrong. This most recent time the ZF was set after the first iteration. I haven't the faintest clue why it was not being set the prior tries. I have also confirmed that EAX was indeed set to 1 prior to the code snippet posted above. – chromodyne Aug 10 '21 at 20:18
  • Maybe you should post a transcript of your gdb session. I would guess that something is wrong with either the way you are checking the value of EAX, or that you are not stepping the code the way you think you are. – Nate Eldredge Aug 11 '21 at 14:46

0 Answers0