Multiplying using shifts in Assembly. But getting a way too high number out! Where am I going wrong? - linux

I am having issues with using shifts to multiply two numbers given by the user.
It asks the user to enter two integers and it is supposed to multiply them.
My program works well in asking for the integers, but when it gives the product it is an astronomical number no where near being correct.
Where am I going wrong? what register is it reading?
%include "asm_io.inc"
segment .data
message1 db "Enter a number: ", 0
message2 db "Enter another number: ", 0
message3 db "The product of these two numbers is: ", 0
segment .bss
input1 resd 1
input2 resd 1
segment .text
Global main
main:
enter 0,0
pusha
mov eax, message1 ; print out first message
call print_string
call read_int ; input first number
mov eax, [input1]
mov eax, message2 ; print out second message
call print_string
call read_int ; input second number
mov ebx, [input2]
cmp eax, 0 ; compares eax to zero
cmp ebx, 0 ; compares ebx to zero
jnz LOOP ;
LOOP:
shl eax, 1
dump_regs 1
mov eax, message3 ; print out product
call print_string
mov ebx, eax
call print_int

You are going wrong in pretty much everything besides asking for the numbers.
You are acting like read_int writes the read integer into input1 the first time it is called and into intput2 the second time. This is almost certainly not the case.
Even were that the case, you load the first number into eax and then immediately overwrite it with the address of message2.
Even if eax and ebx were loaded correctly with the input values, your code that is supposed to be multiplying the two is actually be doing something along the lines of "if the second number is non-zero, multiply eax by 2. Otherwise leave it alone."
Even were the loop arranged correctly, it would be multiplying eax by 2 to the power of ebx.
Then you overwrite this result with the address of message3 anyway, so none of that matters.
In the end, it is impossible to determine what register is getting printed from this code. Between this question and your other question, you seem to be expecting print_int to print any of eax, ebx, or ecx.

Ignoring the code you've posted, and looking strictly at how to multiply numbers (without using a multiply instruction), you do something like this:
mult proc
; multiplies eax by ebx and places result in edx:ecx
xor ecx, ecx
xor edx, edx
mul1:
test ebx, 1
jz mul2
add ecx, eax
adc edx, 0
mul2:
shr ebx, 1
shl eax, 1
test ebx, ebx
jnz mul1
done:
ret
mult endp

Related

Finding number is Even/Odd in assembly

I'm trying to find whether a given number (Input by user) is even or odd.
I'm simply applying AND operation on binary digits of a no. with 1, If the number is odd then operation will result 0 and we will Output Number is odd, otherwise we will output Number is even.
Although logic seems simple, But it's not working in the below code. I'm not getting where is the problem in the code. Can anybody tell me where is the problem
section .data
userMsg db 'Please enter a number'
lenuserMsg equ $ - userMsg
even_msg db 'Even Number!'
len1 equ $ - even_msg
odd_msg db 'Odd Number!'
len2 equ $ - odd_msg
section .bss
num resb 5 ;Reserved 5 Bytes for Input
section .text
global _start ;must be declared for linker (gcc)
_start:
;User Prompt
mov ebx, 1 ;file descriptor (stdout)
mov ecx, userMsg ;message to write 'Please enter a number'
mov edx, lenuserMsg ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
;Taking user input
mov ebx, 0 ;(stdin)
mov ecx, num
mov edx, 5 ;i/p length
mov eax, 3 ;system call number (sys_read)
int 0x80 ;call kernel
mov ax, [num]
and ax, 1
jz evnn ;Jump on Even
;Printing No. is Odd
mov ebx, 1 ;file descriptor (stdout)
mov ecx, odd_msg ;message to write 'Odd Number!'
mov edx, len2 ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
jmp outprog ;Jump to exit
;Printing No. is Even
evnn:
mov ebx, 1 ;file descriptor (stdout)
mov ecx, even_msg ;message to write 'Even Number!'
mov edx, len1 ;message length
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
;Exit
outprog:
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
Just focus on the real problem at hand, shall we? If say an ASCII char is put in AL register, just turn it into a digit and the rest should just be natural. In computing (binary numbers and systems), integers oddness or evenness is determined by the bit 0. If it is 1, it is an odd number. If it is 0, it is an even number. (I am surprised that nobody has specifically put enough emphasis on this thus far).
... ;OS puts a char in AL.
sub al,30h ;turn an ASCII char to one integer digit
shr al,1 ;Lets see how the flags responds below
jc .odd ;CF is set if the first bit (right-most, bit 0) is 1.
;do Even things
;skip pass .odd
.odd:
;do Odd things
Your code does not work because when you ask the user for a number, you read in an ASCII encoded string. You will need to call atoi (ASCII to INT) first to convert the string to a "real" number as computers see it. atoi is included in glibc.
extern atoi
push eax ; pointer to your string to be converted, eg '123'
call atoi
; now eax contains your number, 123
You can also do a bit test on the least significant bit (bit 0) to find out if it is even or odd:
mov al, 01000_1101b
bt al, 0 ; copies the bit to the Carry Flag
jc its_odd ; jump if CF==1
; else - it's even (CF==0)
What BT does, it copies the bit to CF and you can do conditional jumps based on that.
mov ax, [num] loads the first 2 digits of the user's input string, and you're testing the first one. So you're actually testing whether the first character's ASCII code is even.
2 is a factor of 10, so you only need to test the low bit of the last decimal digit to determine if a base-10 number is even or odd.
And since the ASCII code for '0' is 0x30, you can just test the low bit of the last ASCII character of the string.
You don't need to call atoi() unless you need to test n % 3 or some other modulus that isn't a factor of 10. (i.e. you can test n % 2, n % 5, and n % 10 by looking at only the last digit). Note that you can't just test the low 2 bit of the low decimal digit to check for a multiple of 4, because 10 is not a multiple of 4. e.g. 100%4 = 0, but 30%4 = 2.
So, given a pointer + length, you can use TEST byte [last_char], 1 / jnz odd. e.g. after your sys_read, you have a pointer to the string in ECX, and the return value (byte count) in EAX.
;Taking user input
mov ebx, 0 ;(stdin)
mov ecx, num
mov edx, 5 ;i/p length
mov eax, 3 ;system call number (sys_read)
int 0x80 ;call kernel
; Now we make the unsafe assumption that input ended with a newline
; so the last decimal digit is at num+eax-1.
; now do anything that is common to both the odd and even branches,
; instead of duplicating that in each branch.
Then comes the actual test for odd/even: Just one test&branch on the last ASCII digit:
; We still have num in ECX, because int 0x80 doesn't clobber any regs (except for eax with the return value).
test byte [ecx + eax - 1], 1
jnz odd
`section .bss
num resb 1
section .data
msg1 db'enter a number',0xa
len1 equ $-msg1
msg2 db' is even',0xa
len2 equ $-msg2
msg3 db'is odd',0xa
len3 equ $-msg3
section .text
global _start
_start:
mov edx,len1
mov ecx,msg1
mov ebx,1
mov eax,4
int 80h
mov ecx,num
mov ebx,0
mov eax,3
int 80h
mov al,[num]
add al,30h
and al,1
jz iseven
jmp isodd
isodd:
mov edx,len3
mov ecx,msg3
mov ebx,1
mov eax,4
int 80h
jmp exit
iseven:
mov edx,len2
mov ecx,msg2
mov ebx,1
mov eax,4
int 80h
jmp exit
exit:
mov eax,1
int 80h`

3 digit integer to printable 3 digit string in nasm?

my program is nearing completion and is due tommrow. However i have identified the source of an issue. i am cycling through a few functions which roll a dice 1-6 and add that result to a buffer to keep score.Then when i want to print, i am only able to print single digit integers and anything with two digits such as 10 or greater loops through my write_number function infinetly until a seg fault results. How can i alter this algorithm to accomodate for up to three digit integers as the score will need to progress to 100 before the progam ends? unless anyone has a better previously coded algorithm since my assignment was to use a working function found online. please, any insight is greatly appreciated!
section .bss
userScore: resd 4
pcScore: resb 4
number: resd 4
digit: resd 4
count: resd 4
. . . . . . . . . . .
cmp edx, 6 ;dice roll result
call write_number
jmp printReadOption
write_number:
add dword[userScore], edx
mov edx, dword[userScore]
mov dword[number], edx
mov eax, dword[number]
mov ecx, 0Ah ;hex for 10
cdq
div ecx ;eax=number/10,edx=number%10
mov dword[number], eax ;number= number/10
add edx, 30h ;add 48 (30h) to make a printable character
push edx ;push edx in to the stack and
inc dword[count] ;increment count of numbers in the stack
cmp dword[number], 0 ;if number != 0, loop again
jne write_number
loop2:
pop dword[digit] ;pop the digit from the stack and
call write_digit ;write it
dec dword[count] ;decrement the count
cmp dword[count], 0 ; if count != 0, loop again
jne loop2
ret
write_digit: ;Print score
mov eax, 4
mov ebx, 1
mov ecx, userScoreIs
mov edx, userScoreIsLen
int 80h
mov eax, 4
mov ebx, 1
mov ecx, digit ;score to print
mov edx, 4
int 80h
ret
You keep adding to the number you're diving at the beginning of each loop iteration
(add dword[userScore], edx),
so the quotient probably never reaches zero, which would eventually lead to you pushing too many digits onto the stack. complements of Michael as listed in the comment below.

I'm trying to create a triangle of dots in assembly but it isn't working

I'm trying to create a triangle of dots on the screen by taking a user-entered value (to vary the size of the resulting triangle) and using it to write decrementing lines of dots.
Here is the code:
section .data
global _start
char db ' '
prompt_text db "Enter triangle size (2-99) "
prompt_length equ $-prompt_text
section .bss
tri_size resb 3
tri_size_length equ $-tri_size
section .text
_start:
call prompt
call insert_size
mov rax, [tri_size]
outer_loop:
mov rbx, [tri_size]
inner_loop:
call dot
dec bx
cmp bx, 0
jg inner_loop
call linefeed
call dec_length
dec ax
cmp ax, 0
jne outer_loop
call linefeed
call exit
prompt:
mov rax, 4
mov rbx, 1
mov rcx, prompt_text
mov rdx, prompt_length
int 80h
ret
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size]
mov rdx, tri_size_length
int 80h
ret
dot:
mov [char], byte '.'
call print_char
ret
linefeed:
mov [char], byte 10
call print_char
ret
print_char:
push rax
push rbx
push rcx
push rdx
mov rax, 4
mov rbx, 1
mov rcx, char
mov rdx, 1
int 80h
pop rdx
pop rcx
pop rbx
pop rax
ret
dec_length:
push rax
push rbx
push rcx
push rdx
mov rax, [tri_size]
dec ax
mov [tri_size], rax
pop rdx
pop rcx
pop rbx
pop rax
ret
exit:
mov rax, 1
mov rbx, 0
int 80h
Problem:
On running the program, I want the user-inputted number to be used for the number of dots on the first line. However, when I type any number, loads of lines are printed with a single dot on each, then after about a second, a line is printed which contains 32768 dots. This is followed by a line with 32767 dots etc. The number of dots on each line continue to decrease until the line which has 1 dot.
I've noticed that 32768 is hexadecimal for 10000000_00000000, but other than that I'm completely stuck and I'd REALLY appreciate any help!
P.S. I'm using x84-64 linux and assembling with YASM
There are two problems in your code, both when you read the input. First, the fix. Then, an explanation of the current results.
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, [tri_size] ; issue 1
mov rdx, tri_size_length
int 80h
ret ; issue 2 (sort of)
The Fix
First, rcx should contain the address of a buffer, but you are getting the contents of tri_size, not its address. Since tri_size is in the bss section, it is initialized with 0s, so you are telling the OS to read into a NULL buffer. If you were to check the result of the system call, you would see an error code because of this.
Second, when you read input, you are reading a string, not a number. If you want to use it as a number, you need to convert it first. Here is the code with both issues fixed:
insert_size:
mov rax, 3
mov rbx, 0
mov rcx, tri_size ; 1
mov rdx, tri_size_length
int 80h
mov dh, 0 ; 2
mov ah, 0
mov dl, [tri_size] ; 3
mov ah, [tri_size+1]
sub dl, '0' ; 4
cmp al, '0' ; 5
jb done
cmp al, '9' ; 6
ja done
imul dx, 10 ; 7
sub al, '0' ; 8
add dx, ax ; 9
done:
mov [tri_size], dx ; 10
ret
The first issue is an easy fix, just remove the brackets to get the address instead of the contents. The second is more complicated. First, we are going to use 16 bit registers but only read into 8 bits, so step 2 puts 0 in the high byte for each. Then, we read the first two bytes of the string. Next, we convert the first character from a character to a number. Since the digits in ASCII are sequential, we can do this by subtracting the character '0'. Note that this assumes the first character is a valid digit.
We cannot assume the second character is a valid digit, since there might have been only one entered. Therefore, steps 5 and 6 check to see if it is less than '0' and greater than '9', respectively, and jump to the end if either is true. If we get past both, then the second character is a digit. This means the first digit should be in the 10's place, so we multiply it by 10. Then we convert the second character to a number and add it to the first. Now that both characters have been tested, we can store the result back where it is expected and return.
The Explanation
As explained in the fix, you are passing a NULL buffer, so it returns an error. Your tri_size variable is never changed, so it still contains 0. This means both of your counters start at 0. Since you don't check for this, the first dot is printed and bx is decremented, resulting in -1. Since -1 is not greater than 0, the inner loop exits, a newline is printed, your counter and ax are decremented, resulting in -1. This is not zero, so it loops back to the outer loop. This happens 32768 times, until your counter gets to -32768.
When you decrement bx at this point, it becomes -32769, but this number is not representable using 16-bit 2's complement. Instead, it overflows, and the number you get is 32767. This is greater than 0, so you continue in the inner loop until it gets to 0, printing a total of 32768 dots. After the inner loop exits and the newline is printed, the counter and ax are both decremented, resulting in 32767. From this point on, your program works as if the input was 32767, meaning you get a triangle from there to 0 dots.
Interestingly, if your inner loop tested if bx was equal to 0 instead of greater than, you would simply get a triangle from 65536 dots to 0, with no 1-dot lines before it.

How should I work with dynamically-sized input in NASM Assembly?

I'm trying to learn assembly with NASM on 64 bit Linux.
I managed to make a program that reads two numbers and adds them. The first thing I realized was that the program will only work with one-digit numbers (and results):
; Calculator
SECTION .data
msg1 db "Enter the first number: "
msg1len equ $-msg1
msg2 db "Enter the second number: "
msg2len equ $-msg2
msg3 db "The result is: "
msg3len equ $-msg3
SECTION .bss
num1 resb 1
num2 resb 1
result resb 1
SECTION .text
global main
main:
; Ask for the first number
mov EAX,4
mov EBX,1
mov ECX,msg1
mov EDX,msg1len
int 0x80
; Read the first number
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,2
int 0x80
; Ask for the second number
mov EAX,4
mov EBX,1
mov ECX,msg2
mov EDX,msg2len
int 0x80
; Read the second number
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,2
int 0x80
; Prepare to announce the result
mov EAX,4
mov EBX,1
mov ECX,msg3
mov EDX,msg3len
int 0x80
; Do the sum
; Store read values to EAX and EBX
mov EAX,[num1]
mov EBX,[num2]
; From ASCII to decimal
sub EAX,'0'
sub EBX,'0'
; Add
add EAX,EBX
; Convert back to EAX
add EAX,'0'
; Save the result back to the variable
mov [result],EAX
; Print result
mov EAX,4
mov EBX,1
mov ECX,result
mov EDX,1
int 0x80
As you can see, I reserve one byte for the first number, another for the second, and one more for the result. This isn't very flexible. I would like to make additions with numbers of any size.
How should I approach this?
First of all you are generating a 32-bit program, not a 64-bit program. This is no problem as Linux 64-bit can run 32-bit programs if they are either statically linked (this is the case for you) or the 32-bit shared libraries are installed.
Your program contains a real bug: You are reading and writing the "EAX" register from a 1-byte field in RAM:
mov EAX, [num1]
This will normally work on little-endian computers (x86). However if the byte you want to read is at the end of the last memory page of your program you'll get a bus error.
Even more critical is the write command:
mov [result], EAX
This command will overwrite 3 bytes of memory following the "result" variable. If you extend your program by additional bytes:
num1 resb 1
num2 resb 1
result resb 1
newVariable1 resb 1
You'll overwrite these variables! To correct your program you must use the AL (and BL) register instead of the complete EAX register:
mov AL, [num1]
mov BL, [num2]
...
mov [result], AL
Another finding in your program is: You are reading from file handle #1. This is the standard output. Your program should read from file handle #0 (standard input):
mov EAX, 3 ; read
mov EBX, 0 ; standard input
...
int 0x80
But now the answer to the actual question:
The C library functions (e.g. fgets()) use buffered input. Doing it like this would be a bit to complicated for the beginning so reading one byte at a time could be a possibility.
Thinking the way "how would I solve this problem using a high-level language like C". If you don't use libraries in your assembler program you can only use system calls (section 2 man pages) as functions (e.g. you cannot use "fgets()" but only "read()").
In your case a C program reading a number from standard input could look like this:
int num1;
char c;
...
num1 = 0;
while(1)
{
if(read(0,&c,1)!=1) break;
if(c=='\r' || c=='\n') break;
num1 = 10*num1 + c - '0';
}
Now you may think about the assembler code (I typically use GNU assembler, which has another syntax, so maybe this code contains some bugs):
c resb 1
num1 resb 4
...
; Set "num1" to 0
mov EAX, 0
mov [num1], EAX
; Here our while-loop starts
next_digit:
; Read one character
mov EAX, 3
mov EBX, 0
mov ECX, c
mov EDX, 1
int 0x80
; Check for the end-of-input
cmp EAX, 1
jnz end_of_loop
; This will cause EBX to be 0.
; When modifying the BL register the
; low 8 bits of EBX are modified.
; The high 24 bits remain 0.
; So clearing the EBX register before
; reading an 8-bit number into BL is
; a method for converting an 8-bit
; number to a 32-bit number!
xor EBX, EBX
; Load the character read into BL
; Check for "\r" or "\n" as input
mov BL, [c]
cmp BL, 10
jz end_of_loop
cmp BL, 13
jz end_of_loop
; read "num1" into EAX
mov EAX, [num1]
; Multiply "num1" with 10
mov ECX, 10
mul ECX
; Add one digit
sub EBX, '0'
add EAX, EBX
; write "num1" back
mov [num1], EAX
; Do the while loop again
jmp next_digit
; The end of the loop...
end_of_loop:
; Done
Writing decimal numbers with more digits is more difficult!

linux nasm code displays nothing

I am making a program where the user enters a number, and it prints out all the numbers from zero up to the number. It compiles fine, links fine, and returns no errors when it runs, and yet it prints out absolutely nothing. Here is the code:
SECTION .data
len EQU 32
SECTION .bss
other resd len
data resd len
SECTION .text
GLOBAL _start
_start:
nop
input: ; This section gets the integer from the user
mov eax, 3 ; }
mov ebx, 1 ; }
mov ecx, data ; } System_read call
mov edx, len ; }
int 80h ; }
mov ebp, 1
setup: ; This section sets up the registers ready for looping
mov [other], ebp
loop: ; This section loops, printing out from zero to the number given
mov eax, 4
mov ebx, 1
mov ecx, [other]
mov edx, len
int 80h
exit: ; Exits the program
mov eax, 1 ; }
mov ebx, 0 ; } System_exit call
int 80h ; }
When I step through it on KDBG, it returns a few errors; it receives an interrupt and a segmentation fault, although I can't tell where. I'm not sure why though, because when I run it in Geany, it returns a 0 value at the end and runs without error. Why does it not work?
Thanks in advance
NOTE: This code does not loop. It is not finished yet. All it should do here is print out the number 1.
When you go to print, you are calling mov ecx, [other]. This looks at the address that's stored in other and follows that address to get whatever is stored there. The problem is that this system call is expecting an address in ecx, not a value.
If you call mov ecx, other instead, then ecx will have the address of other and it will be able to go to that address and print what's there.
You have another problem here: when you print the number stored in other, it will translate it into the ascii value. So, for example, when you try to print a 1, instead of printing the number 1, it will print ascii 1 (which happens to be a start of heading character; nothing you want to print). Add '0' (the character '0') if you want to print numbers.
EDIT: One more thing, when you read, you are passing 1 into ebx. 1 is STDOUT. What you want is STDIN which is 0.

Resources