NASM - The Netwide Assembler
NASM Forum => Example Code => Topic started by: Manasi on December 17, 2013, 01:23:45 PM
-
The following attached is my code for accepting 4 32 bit numbers.
How can I accept 'n' 32 bit numbers by inputting the value of 'n' from the user and then add them???
i.e. i need to allocate array dynamically (at run time) and add all the 32 bit numbers in the array.
How do i do that?
Thanks in advance
Regards,
Manasi
-
In order to let the user choose the number of numbers to input, you can make a system call (unix) to read and then convert the read string to an integer using the sub reg,'0' trick, after that you can move that output to dword[count]. Here is a code that asks for N of numbers to enter (Also attached):
;ALP to accept 4 32 bit numbers and display it
SECTION .data
msg: db "enter numbers",10
len: equ $-msg
msg1: db "the entered numbers are",10
len1: equ $-msg1
msg2: db "Enter number of numbers to enter",10
len2: equ $-msg2
count: db 0
count2: db 0
SECTION .bss
num resd 4
countlimit resd 1
SECTION .text
global _start
string_to_int: ;I copied this function from the internet
xor ebx,ebx
.next_digit:
movzx eax,byte[esi]
inc esi
sub al,'0'
imul ebx,10
add ebx,eax
loop .next_digit
mov eax,ebx
ret
_start:
;print message
mov edx,len2
mov ecx,msg2
mov ebx,1
mov eax,4
int 0x80
;Read the number of numbers (syscall)
mov edx,2
mov ecx,countlimit
mov ebx,0
mov eax,3
int 0x80
;print message
mov edx,len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
;Not important but just clean esi and ecx before calling the string_to_int function
xor esi,esi
xor ecx,ecx
;Call the string_to_int function, the output is going to be in eax
mov esi,countlimit
mov ecx,1
call string_to_int
;Put the number N in the contents at [count] address
mov dword[count],eax
loop1:
mov edx,9
mov ecx,esi
mov ebx,2
mov eax,3
int 0x80
add esi,9
dec dword[count]
jne loop1
;print message
mov edx,len1
mov ecx,msg1
mov ebx,1
mov eax,4
int 0x80
;print accepted numbers
;initilaize counter
;ReCall the string_to_int function to put result in eax again (Since eax was changed)
mov esi,countlimit
mov ecx,1
call string_to_int
mov dword[count],eax
loop2:
mov edx,32
mov ecx,esi
mov ebx,1
mov eax,4
int 0x80
add esi,32
dec dword[count]
jnz loop2
;system call to exit
mov ebx,0
mov eax,1
int 0x80
But notice that that this will only accept entering 1 digit numbers due to line 65, I didn't have time to fix that, but to fix it, you can get the length of the entered string at the beginning (countlimit) by keeping on comparing the bytes of the string with 0 and increasing a register or a var, the number of increases is the length which can be put on line 65:
mov ecx,result
And remember that when system calling read:
mov edx,2
mov ecx,buffer
mov ebx,0
mov eax,3
int 0x80
The buffer is filled with a string (char * in most cases), which means, you can't deal with the inputted value as an integer unless you convert it first.
Best wishes,
MJaoune
-
Well these look great and seem to do what they're supposed to do. Closer examination reveals a buffer overflow or two, and perhaps other problems waiting to happen...
;ALP to accept 4 32 bit numbers and display it
SECTION .data
msg: db "enter numbers",10
len: equ $-msg
msg1: db "the entered numbers are",10
len1: equ $-msg1
msg2: db "Enter number of numbers to enter",10
len2: equ $-msg2
count: db 0 ; 1 byte!
count2: db 0 ; a spare byte - not apparently used. That'll help, but not enough.
SECTION .bss
num resd 4 ; 4 dwords = 16 bytes.
countlimit resd 1 ; 4 bytes - this is enough, as used...
SECTION .text
global _start
string_to_int: ;I copied this function from the internet
; expects: string in esi, count in ecx
; querry: what if ecx is zero? (hint: crashes)
; returns: number in eax
xor ebx,ebx
.next_digit:
movzx eax,byte[esi]
inc esi
; I'd make sure we had a valid digit here!
sub al,'0'
imul ebx,10
add ebx,eax
loop .next_digit
mov eax,ebx
ret
_start:
;print message
mov edx,len2
mov ecx,msg2
mov ebx,1
mov eax,4
int 0x80
;Read the number of numbers (syscall)
mov edx,2 ; one for the digit, one for the linefeed
mov ecx,countlimit
mov ebx,0
mov eax,3
int 0x80
;print message
mov edx,len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80
;Not important but just clean esi and ecx before calling the string_to_int function
xor esi,esi
xor ecx,ecx
;Call the string_to_int function, the output is going to be in eax
mov esi,countlimit
mov ecx,1
call string_to_int
;Put the number N in the contents at [count] address
mov dword[count],eax ; dword into byte variable!
loop1:
mov edx,9 ; why 9 innings, Mr. Doubleday?
mov ecx,esi
mov ebx,2 ; stderr? (perhaps surprisingly, this works!)
mov eax,3
int 0x80
add esi,9 ; first string goes in the buffer, second string starts in the buffer; after that...
dec dword[count] ; byte variable!
jne loop1
;print message
mov edx,len1
mov ecx,msg1
mov ebx,1
mov eax,4
int 0x80
;print accepted numbers
;initilaize counter
;ReCall the string_to_int function to put result in eax again (Since eax was changed)
mov esi,countlimit ; I don't know why this hasn't been overwritten.
mov ecx,1
call string_to_int
mov dword[count],eax ; byte variable, again.
loop2:
mov edx,32 ; why 32?
mov ecx,esi
mov ebx,1
mov eax,4
int 0x80
add esi,32 ; way past the end of our buffer!
; at this point, we've printed all of the 4-number version, and don't really need to loop...
dec dword[count]
jnz loop2
;system call to exit
mov ebx,0
mov eax,1
int 0x80
As I said, this seems to work pretty well despite the problems. The "string_to_int" routine solves part of the original question - ya gotta convert 'em to numbers... after that it's just "add". (but what are we going to do about the sum overflowing 32 bits?)
The other part of the original question is how do we arrange storage for N numbers? We can't:
array resd N
'cause we don't know N until runtime. One answer is "just call malloc()", but we can do it with system calls. The sys_brk (45) call returns the current "break" if ebx is zero, and sets a new "break" to the value in ebx otherwise. The "break" is the top of valid memory (well, the stack is above that...) and an attempt to access memory above it will segfault. (as will an attempt to access below valid memory - used to be 0x8048000 but things may have changed)
So now, at runtime, we know what N is and we know we need N*4 bytes to store 'em. What we're doing now is storing the "string representing the number, as decimal". If we're going to do that we'll need more. I asked above "why 9?". A 32 bit number could take up to 10 digits to represent. The sys_read will return the linefeed that ended input (and include it in the count returned in eax - easier than scanning the string to know how many digits we've got) - we want room for that. Possibly a minus sign? I'd go with at least 12 bytes - maybe 16 to make it a "round number" - per string... if we're going to store the strings... which I'm not sure we need to...
Anyway, we know how much memory we need. I'd round this up to a 4k boundary, but Gunner has demonstrated to me that we don't really need to...
mov eax, 45 ; __NR_brk
xor ebx, ebx ; get current break
int 80h
mov [orig_break], eax ; ?
mov [curr_break], eax ; ?
mov ebx, eax
add ebx, [what_we_need]
mov eax, 45 ; __NR_brk
int 80h
; now we should be able to...
mov esi, [orig_break]
; and store N of whatever...
... without overflowing anything...
In the even tougher situation when we don't know how many numbers the user is going to enter until he signals (somehow) that he's done, we'll have to check every time we add to esi (or whatever) to make sure we're within bounds. Else, bump the "break" up some more. I think that's what I'd do...
Best,
Frank
-
Thanks MJaoune and Frank.
But i really could not understand why do we need string_to_int function???
and i read some of the topics on the forum and i had a question that why do we need 17 bytes to store a 64 bit number and 9 bytes to store a 32 bit number??
It would be really helpful if you could clarify the doubts.
Thanks in advance,
Regards,
Manasi
-
But i really could not understand why do we need string_to_int function???
'Cause we've got a string, and we need... well, a number. When I was a boy, "the integers" were an infinite set of numbers. These days, an "int" has a particular number of bits. While "N" will probably fit in a byte - no user is going to commit himself to entering more than 256 numbers... are they? - it's easier to just make all the numbers 32-bit...
What your original "accept.asm" did was to read 4 strings from the user and store them in a buffer (overflowing the buffer, but it didn't do any harm as it was the last thing in your program... and you didn't run into the "break"). Then you printed the strings back out. I understand that besides accepting "N" from the user, you want to add the numbers together. Adding strings together is not going to produce a meaningful result. When the user types "123", we need to calculate 100 * 1 + 10 * 2 + 3 (or 7Bh or 01111011).
Mahmoud showed you one way to do it. I use a slightly different way. This is something Herbert Kleebauer showed me - output from a C compiler. It uses two "lea" instructions to... multiply the "result so far" by ten, and add in the new digit, and subtract '0' (48 or 30h) to "convert" the character representing a decimal digit to that number. I think it's "cute". It has the disadvantage that "lea" does not set flags so we can't tell if overflow has occurred. It exits if it encounters zero, or linefeed (as you'll get from sys_read), or any invalid character (not a decimal digit). If the user enters garbage, we just return zero. Feel free to alter this behavior.
;--------------------
; expects: pointer to string on stack
; returns: number in eax
; expects caller to remove parameter
; ecx, edx "trashed"... but...
; ecx (cl) is the invalid digit it encountered
; edx points to next character
; ... if you care to use them...
atoi:
push ebx
mov edx, [esp + 8] ; pointer to string
xor ebx, ebx ; assume not negative
cmp byte [edx], '-'
jnz notneg
inc ebx ; indicate negative
inc edx ; move past the '-'
notneg:
xor eax, eax ; clear "result"
.top:
movzx ecx, byte [edx]
inc edx
cmp ecx, byte '0'
jb .done
cmp ecx, byte '9'
ja .done
; we have a valid character - multiply
; result-so-far by 10, subtract '0'
; from the character to convert it to
; a number, and add it to result.
lea eax, [eax + eax * 4]
lea eax, [eax * 2 + ecx - '0']
jmp short .top
.done:
test ebx, ebx
jz notminus
neg eax
notminus:
pop ebx
ret
;------------------------
Now you've got a number you can store in 32 bits and/or add to another number (or use as a loop counter for "N"). When you get done adding, you're going to have to convert back to characters to print the result. As I recall, we discussed this regarding printing ascii codes as decimal. Here's another way to do it, shown to me by Chuck Crayne, former moderator of comp.lang.asm.x86 and a mentor to many beginners. It has the advantage that the strings can be right-justified, which looks nice if you're printing a column of numbers...
;--------------------
; from Chuck Crayne - RIP, Chuck.
;convert binary to ascii
;call with eax = signed binary number
; esi = address of output string
; ecx = length of output string
;returns esi = 1st printed digit
; ecx = no of digits printed (includes sign if any)
; other registers preserved
binasc: push edx
push ebx
push edi
push eax
mov edi,esi ;save start of string
ba1: mov byte [esi],' ' ;fill string with blanks
inc esi
loop ba1
mov ebx,10 ;initialize divisor
or eax,eax ;value negative?
jns ba2 ;no problem
neg eax ;make it positive
ba2: xor edx,edx ;clear high part of dividend
div ebx ;divide by 10
add dl,'0' ;convert to ascii digit
dec esi ;step backwards through buffer
mov [esi],dl ;store digit
inc ecx
cmp esi,edi ;out of space
jz ba4 ;yes - quit
or eax,eax ;all digits printed?
jnz ba2 ;no - keep trucking
pop eax ;get original value
or eax,eax ;negative?
jns ba3 ;no - quit
dec esi ;place for sign
mov byte [esi],'-'
inc ecx ;add to char count
ba3: pop edi
pop ebx
pop edx
ret
ba4: pop eax
jmp ba3
;-------------------
I count 10 possible digits in the decimal representation of a 32 bit number (too lazy to count 64 bits right now). Plus, you'll want room for the linefeed (10 or 0Ah) that the sys_read insists on returning with. Maybe a minus sign, although you don't need to worry about that yet. I'd go with 12 bytes. Of course, you don't have to allow all possible numbers - you can prompt "Enter a 4 digit number"... if you think they'll pay any attention. We really ought to be flushing that buffer. Remind me to show you how to do that, but you can "assume a well-behaved user" for now.
Best,
Frank
-
Frank gave you a clear full answer to your question (As he always does). But if you are still confused about "Why should we convert to INT?" I will try to give you a short answer.
Unix and Unix based systems (i.e Linux) were written using C programming language, so are the kernel calls, which means, whenever you call a system call, the function to be processed was written in C (By Linus Torvalds in Linux's case). Thats actually not the problem, its actually that the C programming language works with data types (Simplest I could say) like INT, CHAR, DOUBLE, FLOAT etc..., each with a specific size (Measured by bytes) and each works differently.
To be simple, a string such as "hi Manasi" can't by multiplied, subtracted, divided or added, it doesn't make any sense. And so is the data in the buffer which is the output of the READ system call, the function itself places the output as a string and of course how can the system know whether the user entered a sentence or a number? It treats both the same, as a string, which can't be added, divided etc... Thats why you must convert it to a real integer so it can be treated as a number. (Just consider the word integer means number)
Best wishes,
MJaoune
-
While I've got it at hand, let me post this (from an earlier question). It accepts only one number and just adds one to it, so it does not do what you want. It does show how to "flush the buffer".
; Compiling this code for 32-bit use:
; nasm -f elf file.asm
; ld -melf_i386 -o file file.o
;
global _start
;~.~. Definitions for readability: ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
%define SYS_EXIT 1
%define SYS_READ 3
%define SYS_WRITE 4
%define STDIN 0
%define STDOUT 1
%define STDERR 2
%define LF 10
%define MAX_NUMBER 16
SECTION .data
format: db "The number is: "
SECTION .bss
buffer resb MAX_NUMBER
number resd 1
SECTION .text
_start:
nop
GetInput:
mov EAX, SYS_READ
mov EBX, STDIN
mov ECX, buffer
mov edx, MAX_NUMBER
int 80H
cmp byte [ecx + eax - 1], LF
jz Calculate
; pesky user has tried to overflow us!
; flush the buffer!
sub esp, 4 ; temporary "buffer"
flush:
mov eax, SYS_READ
; ebx still okay
mov ecx, esp ; buffer is on the stack
mov edx, 1
int 80h
cmp byte [ecx], LF
jnz flush
add esp, 4 ; "free" our "buffer"
Calculate:
push buffer
call atoi
add esp, 4
mov [number], eax ; not really used...
add eax, 1 ; just to do something
mov esi, buffer
mov ecx, MAX_NUMBER
call binasc
mov byte [esi + ecx], LF
inc ecx
Display:
mov edx, ecx
mov ecx, esi
mov ebx, STDOUT
mov eax, SYS_WRITE
int 80h
exit:
mov eax, SYS_EXIT
xor ebx, ebx
int 80h
;--------------------
;--------------------
atoi:
push ebx
mov edx, [esp + 8] ; pointer to string
xor ebx, ebx ; assume not negative
cmp byte [edx], '-'
jnz notneg
inc ebx ; indicate negative
inc edx ; move past the '-'
notneg:
xor eax, eax ; clear "result"
.top:
movzx ecx, byte [edx]
inc edx
cmp ecx, byte '0'
jb .done
cmp ecx, byte '9'
ja .done
; we have a valid character - multiply
; result-so-far by 10, subtract '0'
; from the character to convert it to
; a number, and add it to result.
lea eax, [eax + eax * 4]
lea eax, [eax * 2 + ecx - 48]
jmp short .top
.done:
test ebx, ebx
jz notminus
neg eax
notminus:
pop ebx
ret
;------------------------
;--------------------
; from Chuck Crayne - RIP, Chuck.
;convert binary to ascii
;call with eax = signed binary number
; esi = address of output string
; ecx = length of output string
;returns esi = 1st printed digit
; ecx = no of digits printed (includes sign if any)
; other registers preserved
binasc: push edx
push ebx
push edi
push eax
mov edi,esi ;save start of string
ba1: mov byte [esi],' ' ;fill string with blanks
inc esi
loop ba1
mov ebx,10 ;initialize divisor
or eax,eax ;value negative?
jns ba2 ;no problem
neg eax ;make it positive
ba2: xor edx,edx ;clear high part of dividend
div ebx ;divide by 10
add dl,'0' ;convert to ascii digit
dec esi ;step backwards through buffer
mov [esi],dl ;store digit
inc ecx
cmp esi,edi ;out of space
jz ba4 ;yes - quit
or eax,eax ;all digits printed?
jnz ba2 ;no - keep trucking
pop eax ;get original value
or eax,eax ;negative?
jns ba3 ;no - quit
dec esi ;place for sign
mov byte [esi],'-'
inc ecx ;add to char count
ba3: pop edi
pop ebx
pop edx
ret
ba4: pop eax
jmp ba3
;-------------------
To those who celebrate Christmas, "Merry Christmas!" To those who do not (Muslims, Jews, Hindus, Zoroastrians, atheists, etc.) "Have a merry day!" (you don't have to be Christian to have a Merry Christmas!)
Best,
Frank