Author Topic: ADDITION OF 'N' 32 bit numbers  (Read 21816 times)

Offline Manasi

  • Jr. Member
  • *
  • Posts: 9
ADDITION OF 'N' 32 bit numbers
« 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

Offline MJaoune

  • Jr. Member
  • *
  • Posts: 94
Re: ADDITION OF 'N' 32 bit numbers
« Reply #1 on: December 18, 2013, 08:26:20 PM »
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):

Code: [Select]
;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:

Code: [Select]
mov ecx,result
And remember that when system calling read:

Code: [Select]
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
« Last Edit: December 18, 2013, 10:49:00 PM by MJaoune »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: ADDITION OF 'N' 32 bit numbers
« Reply #2 on: December 20, 2013, 02:50:31 AM »
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...

Code: [Select]
;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:
Code: [Select]
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...
Code: [Select]
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


Offline Manasi

  • Jr. Member
  • *
  • Posts: 9
Re: ADDITION OF 'N' 32 bit numbers
« Reply #3 on: December 23, 2013, 12:03:56 PM »
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

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: ADDITION OF 'N' 32 bit numbers
« Reply #4 on: December 23, 2013, 02:33:46 PM »
Quote
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.

Code: [Select]
;--------------------
; 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...

Code: [Select]
;--------------------
; 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


Offline MJaoune

  • Jr. Member
  • *
  • Posts: 94
Re: ADDITION OF 'N' 32 bit numbers
« Reply #5 on: December 23, 2013, 09:37:26 PM »
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

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: ADDITION OF 'N' 32 bit numbers
« Reply #6 on: December 25, 2013, 05:50:30 PM »
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".
Code: [Select]
; 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