Finding number is Even/Odd in assembly - linux

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`

Related

Appending Two Strings in Assembly

We've learned only a touch (by touch I mean nothing in class, only theoretical talk of how registers work) of assembly in school and my professor wants us to do something in assembly a little harder than adding two integers. I've been doing a bunch of research and have come up with some code. I am using Microsoft Visual Code on a Linux VM. From my understanding, the code puts input1 into ECX, then puts input2 into ECX, then this essentially appends IF the length of input1 is at least 10 bytes. Not entirely sure how this is working. If I use less than 10 bytes for the first input I get the next input printed on a new line.
I've been trying to understand how [variable] and variable, value vs. address if I understand correctly, fit into this and have played around with reserved byte variables. I'm not asking for my project to be done, but I feel like I'm blindly trying this as opposed to without direction. Thanks in advance guys!
section .text
global main ;must be declared for using gcc
extern printf ;C library used to print
extern exit ;C library used to exit
extern scanf ;C library used to input user entry
main: ;tell linker entry point, called main due to C library usage
;Reads prompt message
mov eax, 4 ;system call number (sys_write)
mov ebx, 1 ;file descriptor (stdout)
mov ecx, prompt1 ;message to write called prompt
mov edx, len1 ;message length
int 0x80 ;call kernel
;Accept user input
mov eax, 3 ;system call number (sys_read)
mov ebx, 0 ;file descriptor (stdin)
mov ecx, input1 ;variable to read into, i.e. input
mov edx, 25 ;input length
int 0x80 ;call kernel
;Reads prompt message
mov eax, 4 ;system call number (sys_write)
mov ebx, 1 ;file descriptor (stdout)
mov ecx, prompt2 ;message to write called prompt
mov edx, len2 ;message length
int 0x80 ;call kernel
;Accept user input
mov eax, 3 ;system call number (sys_read)
mov ebx, 0 ;file descriptor (stdin)
mov ecx, input2 ;variable to read into, i.e. input
mov edx, 25 ;input length
int 0x80 ;call kernel
;cld
mov al, 0
mov ecx, 50
mov edi, input3
repne scasb
dec edi
mov ecx, 2
mov esi, input4
rep movsb
int 0x80
;Display user input
mov eax, 4 ;system call number (sys_write)
mov ebx, 1 ;file descriptor (stdout)
mov ecx, input1 ;message to write
mov edx, 50 ;message length
int 0x80 ;call kernel
call exit
section .data
prompt1 db 'Please enter your first string: ', 0xa ;string to print for user input
len1 equ $ - prompt1 ;length of string prompt
prompt2 db 'Please enter your second string: ',0xa ;string to print for user input
len2 equ $ - prompt2 ;length of string prompt
input3: times 10 db 0 ;variable input is of 10 bytes, null string terminated
input4: times 1 db 0
section .bss
input1 resb 50
input2 resb 25

Converting User Input Hexadecimal to Decimal in Assembly

I am trying to create an assembly program that takes a user input hexadecimal number no greater than 4 digits and outputs the same number in base 10. This is being done using NASM on a Linux install. Using some tutorials I've found and my very limited understanding of this language, I have come up with this.
section .data
inp_buf: times 6 db 0
numPrompt: db "Enter a hexadecimal value no greater than 4 digits: "
len1 equ $ - numPrompt
answerText: db "The decimal value is: "
len2 equ $ - answerText
section .bss
dec: resb 5
section .text
global _start
_start:
mov edx, len1
mov ecx, numPrompt
mov ebx, 1
mov eax, 4
int 0x80
mov eax, 3
mov ebx, 0
mov ecx, inp_buf
mov edx, 6
int 0x80
mov esi, dec+11
mov byte[esi], 0xa
mov eax, ecx ;I feel like the problem must be here
mov ebx, 0xa
mov ecx, 1
next:
inc ecx
xor edx, edx
div ebx
add edx, 0x30
dec esi
mov [esi], dl
cmp eax, 0
jnz next
mov edx, len2
mov ecx, answerText
mov ebx, 1
mov eax, 4
int 0x80
mov edx, ecx
mov ecx, esi
mov ebx, 1
mov eax, 4
int 0x80
mov ebx, 0
mov eax, 1
int 0x80
It should be noted that if the user input is ignored and you just put in a variable with the hex in the data section, the program can convert that with no problem. For example, if you have the line hexNum: equ 0xFFFF and you replace the commented line above with mov eax, hexNum then the code is capable of converting that to base 10 correctly. The error must be with the format of the user input, but I don't know how to verify that.
I appreciate any insight on what my issue is here. This is my first assembly program other than "hello world" and a lot of these new concepts are still confusing and hard to understand for me. If there is a better or simpler way for me to go about this entire program then I would love to hear about that as well. Thanks!
The values in `inp_buffer are going to be in hex format, for eg.
1234
will be stored as
0x31 0x32 0x33 0x34
in 4 consecutive bytes.
The number must be converted from ascii format into hex format using the exact opposite procedure for reverse conversion.
After getting the number into hex form, the procedure for conversion to decimal is correct, what may be called as decimal dabble.
I recommend complete conversion first followed by byte by byte conversion into ascii. It is always better to have the final result and then go for conversions, especially in assembly language programming where debugging is difficult.

Why does incrementing not work the same for larger numbers as it does for a single digit?

section .bss
num1 resb 4
result resb 4
section .data
SYS_EXIT equ 1
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
INCREMENT equ 1
msg1 db 'Please enter an integer here:',0xA
len1 EQU $- msg1
msg2 db 'Your integer after being incremented is:',0xA
len2 EQU $- msg2
section .text
global _start:
_start:
mov eax, SYS_WRITE ; Prompting user to enter a number.
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 0x80
mov eax, SYS_READ ; Reading users number.
mov ebx, STDOUT
mov ecx, num1
mov edx, 4
int 0x80
mov eax, SYS_WRITE ; Writing second message.
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 0x80
mov eax, [num1] ; incrementing the user's number.
mov ebx, INCREMENT
add eax, ebx
mov [result], eax
mov eax, SYS_WRITE ; Printing out incremented number.
mov ebx, STDOUT
mov ecx, result
mov edx, 4
int 0x80
Just started learning basic assembly code today and I'm trying to increment user input numbers by 1. I am getting weird results when the number is any bigger than a single digit. What's the difference?
The problem is that your treat your input as numbers and not as strings. When you receive your input (string), you get an (ASCII) string as your return value. Increasing the first char(digit) by add eax, INCREMENT (compacted) does work, because the first ASCII char value of [num] contained in AL(lowest byte of EAX) is increased by one. This happens because the ASCII value of 0 is 48 and the ASCII value of 9 is 57.
If you think you increase these 'digits', you would in fact increase their ASCII values, which works well until you reach the one-digit-limit(0-9). Increasing 9 by one would result in : and not 10, as your may have expected because the ASCII value of 9 is 57 and adding one to it results in 58, which is the ASCII value of :.
So you would have to 'normalize' your 'numbers' before you operate on them with integer arithmetic. Transform them from ASCII strings to integer values.
Luckily for you, others have done this before you and optimized these approaches to the practical maximum. Look and search for atoi(ASCII to integer) and itoa(integer to ASCII). Then surround your arithmetical operations
mov eax, [num1] ; incrementing the user's number.
mov ebx, INCREMENT
add eax, ebx
mov [result], eax
with these functions.

add two digit numbers in NASM(Linux)

I want to add two-digit numbers in NASM(Linux). To add two simple numbers, I use the following code:
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov eax,'3'
sub eax, '0'
mov ebx, '4'
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [sum], eax
mov ecx,msg
mov edx, len
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov ecx,sum
mov edx, 1
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "The sum is:", 0xA,0xD
len equ $ - msg
segment .bss
sum resb 1
The result of the code is 7.But when I carry number 17 in register eax forexample the result is not correct.In this case 5.Tell me please what is the problem? Thank you!
Here's your example with a little bit of cleaning up to help make it easier to read.
Suggestion: this kind of consistency will greatly improve your public image.
But hey; nice commenting, I could read your code and understand it (which is why I decided to answer you)
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov eax, '3'
sub eax, '0'
mov ebx, '4'
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [sum], eax
mov ecx, msg
mov edx, len
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
mov ecx, sum
mov edx, 1
mov ebx, 1 ;file descriptor (stdout)
mov eax, 4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax, 1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db "The sum is:", 0xA,0xD
len equ $ - msg
segment .bss
sum resb 1
Okay now, as for your comment, "...But when I carry number 17 in register eax forexample the result is not correct."
I can imagine !
Question, when you "...carry number 17 in register eax..." are you doing it like this ?...
Mov Eax,"17"
If so, slow down and take a look at your code one step at a time via debug.
I believe that what you'll see is that you are actually doing this...
Mov Eax, 3137h
Although it might be
Mov Eax, 3731h
Interesting concept. I've never done anything like that. Whatever.
What's more, if you are using this place to store that same number...
sum resb 1
You only have one byte.
Best I can tell, your example code is limited to single digit numbers.
Now then, since your label sum has reserved only one byte; 8 bits, you can see the problem as you are storing 32 bits there. (Well, you're trying to; it won't work.) No clue what happens when you do that. You probably want to rethink that structure.
As for why 17 becomes 5, no clue here.
Let us know if any of this helps you. Assembly is great stuff. As you are personally experiencing, the initial thought adjustment can be strange for the brain, can't 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!

Resources