Author Topic: program errors : divide overflow  (Read 18106 times)

nobody

  • Guest
program errors : divide overflow
« on: June 17, 2005, 12:51:29 PM »
HI all

i'm new to this site, but i'm hoping someone can help. I have a program to write to input 6 numbers as ascii characters add them, divide by six and output the result. I have functions that convert to numeric and from numeric back to string, but when i ask for a keyboard input, i get a divide overflow.I know it must be in the num_to_str where i have idiv bx, but i cannot figure out why. WHen i don't ask for an input and start with '5' as input, i get the answer 9 on the screen no matter what. below is the code. thank you for any help you can provide. my email address is niri@ananzi.co.za

Thanks and kind regards,
NIRI
-----------------------------------------------------------------

bits    16
      org    0x100
      jmp main

init_mess:
            db 'Please enter the numbers you selected : ',0ah,0dh,'$'

input_buf:  db 2

outputbuf:  db '  ','$'

blank:      db '      ','$'

next_mess:
            db 'Number ','$'
store:
            db 'Stored value right now is',0ah,0dh,'$'
input:
            db '2','$'
line_ret:
           db 0ah,0dh,'$'

display_number:
            mov   dx,next_mess
            mov   ah,09
            int   21h
            ret

display_lineret:
            mov   dx,line_ret
            mov    ah,09       ;    Service - display of string
      int    21h
      ret

display_colon:
            mov   ah,02
            mov   dl,3ah
            int   21h
            ret

display_n1:
            mov   ah,02
            mov   dl,31h
            int   21h
            ret

display_n2:
            mov   ah,02
            mov   dl,32h
            int   21h
            ret
display_n3:
            mov   ah,02
            mov   dl,33h
            int   21h
            ret

display_dx:
           mov    ah,09       ;    Service - display of string
      int    21h
      ret

read_string:
            mov   ah,0ah
            mov   dx,input_buf
            int   21h
            ret

;{----------------------------- CONVERT STRING TO NUMBER ----------------------------
; INPUT  = DX
; OUTPUT = AX

str_to_num:
            xor    ax,ax        ; Initial value of AX = 0
            xor    bh,bh        ; Initial value of BH = 0
            mov    cx,10        ; To build integer in ax(multiply by 10)
            mov    si,dx        ; DX point to the start of input buffer

next_char:
            mov    bl,[si]      ; Move contents of memory pointed to by Si to BL
            cmp    bl,0Dh       ; Is it a carriage return?
            je     finis        ; Yes, we are done
            cmp    bl,39h       ; Compare for '9'
            jg     error        ; Is greater than 9 - invalid
            sub    bl,30h       ; Convert to numeric value (ASCII '0' = 30h)
            jl     error        ; <0, invalid character
            imul   cx           ; DX:AX = AX * 10
            add    ax,bx        ; Add next digit
            inc    si           ; Pointer to next character
            jmp    next_char    ; Repeat for next character
error:
            mov    al,'E'       ; Error flag
finis:
            ret                 ; Return to calling program

;{------------------------------ CONVERT NUMBER TO STRING --------------------------
; INPUT  = AX
; OUTPUT = DX

num_to_str:
            mov    di,outputbuf ; Address of output buffer
            ;add    di,1         ; Point to end of output buffer [total buffer - 1]
            mov    bx,10        ; We divide by 10
loopnts:
            idiv   bx           ; Divide DX:AX by BX, result in Ax and remainder in DX
            add    dx,30h       ; Convert remainder to ASCII
            mov    [di],dl      ; Move digit into output string
            cmp    ax,0         ; Is the result 0
            je     finish       ; Yes then we have finished
            dec    di           ; Pointer one back
            xor    dx,dx        ; Clear DX
            jmp    loopnts      ; Repeat loop

finish:
            mov    dx,outputbuf ; DX = address of output buffer
            mov    ah,09        ; Display funtions call
            int    21h          ; DOS system call
            ret
; {---------------------------- MAIN PROGRAM ----------------------------------------   

main:      

mov   dx,input_buf
            call  read_string        

mov   ah,0
            mov   dx,ax
            call  str_to_num
            call  num_to_str

int 20h                 ;   Terminate the

nobody

  • Guest
Re: program errors : divide overflow
« Reply #1 on: June 17, 2005, 04:36:14 PM »
The key is that div/idiv divide dx:ax by whatever. You don't need anything in dx, so you "ignore" it, but the CPU doesn't!  If dx isn't zero (if it's bigger than bx), the result of the divide won't fit in a word. and you get this exception (some older machines insisted that you'd divided by zero, which was even *more* confusing!)

Stick an "xor dx, dx" ( or "mov dx, 0") at the top  of your loop - just before the "idiv" - and it'll work. You do this at the end of your loop, so loops after the first ought to be okay, but the first idiv can crash. You don't need it do it both places, of course - just at the top.

Best,
Frank

nobody

  • Guest
Re: program errors : divide overflow
« Reply #2 on: June 21, 2005, 06:01:43 AM »
Hi Frank

Thank you so much for the reply. I did get it to stop giving me that error. What i realised was the my string input, instead of giving me say '23' it would give me '@#23' and the first 2 characters are always strange characters. what i did was add 2 into the string to make it skip the first two characters which leaft me with '23' and then the program worked. Why would it do that?

Also, with the input of 6 numbers, how can i store and add it together? Should i put it in a stack or an array type?

Thanks and kind Regards,
NIRI

nobody

  • Guest
Re: program errors : divide overflow
« Reply #3 on: June 21, 2005, 04:32:29 PM »
Well, the "input" problem is simple enough - that's the way int 21h/0Ah works. On calling it, the first byte in the buffer gives the *maximum* number of bytes to input - the buffer had *better* be big enough to hold what you ask for! When it returns, the number of bytes actually input is in the second byte of the buffer. What the user input begins at "buffer + 2" (as you've discovered). So that's "normal".

There's another interrupt - int 21h/0Ch which flushes the  input buffer and then does input according to what's specified in al. (from memory, 1, 6, 7, 8 or 0Ah). So if you used "mov ax, 0C0Ah"/"int 21h", it would behave just like what you've got - same "funny" buffer usage - but if there were garbage in the input queue, it would be "flushed" first. I don't think you really need this.

For information on the dos interrupts (and bios interrupts, and ports, and memory) you want Ralf Brown's Interrupt List:



or http://www.pobox.com/~ralf

Big download, but you'll want it for dos programming!

If you need to save multiple numbers the user has input, you could keep them in an array, or on the stack. The stack is a "last in, first out" data structure, so you'll get the numbers back in the opposite order than you stored 'em. This can be useful... when converting a number to an ascii string, for example, dividing by ten repeatedly gives the digits in "backwards" order. One way to deal with this (which you use) is to start with the "write pointer" at the "end" of the buffer, and decrement it towards the "beginning" of the buffer for each new digit. Another way to do this is to push each digit on the stack as it's obtained, and then in a second loop, pop 'em back off the stack and add 'em to your "string buffer" - *or* (and this is the advantage of this method) you can print 'em right away, and not need a buffer to store the string.

If you want to be able to say "the six numbers you entered were 1, 7, 2, 9, 3, 2", you're probably better off to use an array. Your "string-to-number" and "number-to-string" routines handle 16-bit numbers, so you'll probably want a word-sized array. This means that the index (remember indices start at zero - your "first" number is at index zero, not index one!!!) needs to be multiplied by two! You probably wouldn't use a literal "mul" instruction - "shl bx, 1" or "add bx, bx" (if your index were in bx). 32-bit instructions allow much more powerful addressing modes, which makes this much easier. You *can* use these 32-bit instructions in 16-bit code. Make *sure* that the upper word of any 32-bit register is clear, if you try this! Normally they would be clear, but certain "buggy" programs - such as a djgpp build of Nasm :) - will leave garbage in the upper word of 32-bit registers. This will result in a "Segment Overrun Exception" - which dos doesn't handle, so the machine hangs and need to be re-booted. (a dos-box under Windows *does* handle this, and you get the "illegal operation" box). This will interfere with the grading process for your assignment :)

You're probably "supposed" to be using 16-bit code, but...

xor ecx, ecx   ; before we start

...
mov cx, 2  ; index 2 gets the "third" number!
...
mov ax, [my_array + ecx * 2]

Note that "mov ax, [cx]" is *not* a valid instruction. but "mov ax, [ecx]" is... (32-bit code is much easier than 16-bit!)

There's a "penalty" for using 32-bit instructions in 16-bit code - the 66h "size override prefix" bloats your code, and slows it down slightly - and won't work on a 286, of course.

In 16-bit code...

mov bx, 2   ; index 2 - the "third" number
...
add bx, bx  ; double it for word array
mov ax, [bx]

That's if you need "random access". If you just need "sequential access", just add 2 to bx each time...

mov bx, buffer
mov cx, 6

top:
mov ax, [bx]
; call process_it
add bx, 2
loop top

If you don't need to save the individual entries - if you just need the "sum" (or average, or min/max, or whatever) - you don't need an array at all. Just declare a "sum" variable (of an appropriate size!) and add each value to it as they're converted... (watching out for overflow, if you want your program to be robust!)

I think you're on the right track.

Best,
Frank