NASM - The Netwide Assembler
NASM Forum => Example Code => Topic started by: nobody 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 (http://mailto: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
-
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
-
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
-
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 (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