1

I want to create a program that can calculate division problems. The problem is that my program crashed when I tried dividing by a negative number, even though I created a branch called "DivNeg" that was supposed to prevent it from crashing. Does anyone have ideas on how to fix this?

Here is my assembly code

    .386

.model flat

public _Divide

.code

_Divide proc
        mov eax, [esp + 4]  ; First address ; this is the dividend
        mov ebx, [esp + 8]  ; Second address ; this is the divisor

        cmp ebx, 0
        je  DivZero
        cmp ebx, 0
        jnae    DivNeg

        cdq
        idiv ebx            ; To divide by eax by ebx
        mov ebx, [esp + 12] ; Third address; this is the remainder
        jmp Done1

DivZero:
    mov     eax,-1          ; If user divides by zero, this will set the result to negative 1
    mov     edx, 0          ; If user divides by zero, this will set the remainder to 0
    mov     ebx,[esp +12]   ; Needed for the remainder if divided by 0
    cmp     ebx, 0
    je      Done2


Done1:
    mov     [ebx], edx
    je  Done1

DivNeg:
    cmp     ebx, 0
    jge     Done2
    mov     eax, -1
    neg     eax
    je      DivNeg


Done2:
        ret
_Divide endp

        end
Fifoernik
  • 9,779
  • 1
  • 21
  • 27
Jaquai
  • 21
  • 1
  • 4
  • 4
    just by the first look; you have: `je DivZero; je DivNeg` one after another; and also what should DivNeg do? why is there an ascii of a '-' ? – Paweł Łukasik Oct 04 '17 at 22:08
  • I created the DivNeg loop so that the result and remainder from dividing by a negative number will be shown. Without it, the program would crash for some reason. As fir the je DivNeg part, I put that there in case if the divisor is less than zero. – Jaquai Oct 04 '17 at 22:16
  • 2
    I think you should read a bit more about asm; the 'loop' doesn't make sense - it eax > 0 it will loop forever. The whole problem is a bit 'strange' for the task it supposed to do – Paweł Łukasik Oct 04 '17 at 22:21
  • The second jump perhaps should check another flag besides equal. Or perhaps, it shouldn't check for negative at all. – Michael Dorgan Oct 04 '17 at 22:24
  • @MichaelDorgan Okay. I changed the je to jnge instead but the problem still persists. I also got rid of the '-' part, but there are still no changes. – Jaquai Oct 04 '17 at 22:38
  • @Jaquai I really want to help you but just think for a moment about DivNeg; you put `0` in `ebx` and right after that you compare it with `-1`? it's useless - jmp will never occur. Not understanding what DivNeg should do prevents from providing help – Paweł Łukasik Oct 05 '17 at 00:06
  • @PawełŁukasik Sorry about the `mov ebx, 0` part. That wasn't supposed to be there in the updated code. The reason why I created the `DivNeg` loop is because I had crashing issues when dividing by a negative number when I didn't have it. I wanted to create that loop specifically in a case if someone was to divide by anything less than zero. It was the only way for my program to work. I apologize if I am not answering your question clearly. – Jaquai Oct 05 '17 at 00:15
  • 1
    I did call it with (40, -7, &remainder), and works for me (returns -5 and 5 in dword remainder). That said, that code is far from correct, I intentionally picked input parameters which works, just to troll you to the same extend as you troll us, by not providing details, how you call it, for which values it does crash, on which instruction it does crash, etc... all those would help, for example I guess you are testing it with passing `nullptr` for remainder, then it will crash. BTW `cmp ebx,0` `jnae` will NEVER jump. You need to pay lot more attention to instruction guide, and use debugger. – Ped7g Oct 05 '17 at 01:56
  • BTW, why do you test for divisor being `0` and return some incorrect result? What's the purpose of that? It will not prevent it from crashing completely, you can still make that `idiv` crash with -2147483648/-1 = +2147483648 (DE = divide error due to overflow). So if you wanted some inaccurate fast division with crash protection, it will not work for this other special case. Rather handle DE exception correctly in the code which is in need of divide. – Ped7g Oct 05 '17 at 02:10
  • @Ped7g It wasn't my intent to troll or deceive people. I'll just try and figure this out myself. Thanks for the help. Sorry if I wasn't detailed enough for everyone here. – Jaquai Oct 05 '17 at 02:16

2 Answers2

1

cdq / idiv ebx will only raise #DE (Divide Exception) in 2 cases:

  • eax = anything, ebx = 0. Divide by zero.
  • eax = 0x80000000, ebx = -1. This overflows because the right answer doesn't fit in eax. The most negative number (highest magnitude) in a 2's complement signed representation has no inverse. -2^31 fits in a signed 32-bit integer, but +2^31 doesn't. (In C, this why INT_MIN / -1 is undefined behaviour.) See Why does integer division by -1 (negative one) result in FPE? for more details.

There's no way for cdq/idiv ebx to fault with a positive dividend and a negative divisor, because overflow is impossible. You are correctly using cdq to sign-extend eax into edx:eax. (Without that, it's easily possible for 64b / 32b => 32b division to overflow the result.)

If you're not crashing on idiv itself, then you have a different bug, and should single-step your code in a debugger. See the bottom of the tag wiki for tips on debugging with GDB, or with Visual Studio.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • I updated it a bit: Under 'je DivZero', I added 'cmp ebx, 0' then 'jnae DivNeg'. In the 'DivNeg:' loop, the updates were 'mov ebx, 0', 'cmp ebx, -1' 'jge Done2' 'mov eax, -1' 'neg eax' 'je Done2'. When I debugged the program it showed the correct result/remainder, but then it crashed again pointing at 'mov [ebx], edx' in the 'Done1' loop. – Jaquai Oct 04 '17 at 23:09
  • @Jaquai: so look at the value in `ebx` and figure out why it's not a valid pointer. Single-step your code from earlier in the program and watch register values change to see how you got there. – Peter Cordes Oct 04 '17 at 23:13
  • In the `Done1` loop, `[ebx]` is supposed to be the result in, while `edx` is the remainder. Even after debugging the program, I still cannot figure out why it crashed. Everything else runs fine just fine (outside of dividing by negatives). Plus, nothing jumps to `Done1` in the `DivNeg` loop. – Jaquai Oct 04 '17 at 23:48
1
mov eax, [esp + 4]  ; First address ; this is the dividend
mov ebx, [esp + 8]  ; Second address ; this is the divisor
...
mov ebx, [esp + 12] ; Third address; this is the remainder

These comments indicate that the arguments to your function are addresses. This implies that you need to dereference before you can do any work on the values themselves. You did this correctly for the 3rd argument, but failed on the 1st and 2nd arguments!

mov  eax, [esp + 4]  ; First address
mov  eax, [eax]      ; this is the dividend
mov  ebx, [esp + 8]  ; Second address
mov  ebx, [ebx]      ; this is the divisor

cmp ebx, 0
je  DivZero
cmp ebx, 0
jnae    DivNeg

You don't need to repeat the cmp instruction. The flags remain set for your second conditional jump. Also because the EQ condition was weeded out, best use jna, or better still use jl.

cmp  ebx, 0
je   DivZero
jl   DivNeg

Done1:
 mov [ebx], edx
 je  Done1

Very problematic code this one! (Infinite loop vs unwanted fall thru).
Better write:

Done1:
  mov  [ebx], edx   ;Return remainder
  jmp  Done2

If I were you, I'd place my Done1 label, 3 lines higher up so the check to guard against a null pointer is always done.

Done1:
  mov  ebx,[esp +12]   ; Needed for the remainder if divided by 0
  cmp  ebx, 0
  je   Done2
  mov  [ebx], edx
  jmp  Done2

When both the dividend and the divider are positive you can safely use div instead of idiv. When the dividend is positive and the divider is negative you can negate the divider, use div as before, but negate the quotient.

DivNeg:
  neg  ebx
  cdq
  div  ebx
  neg  eax
  jmp  Done1
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Fifoernik
  • 9,779
  • 1
  • 21
  • 27