1

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
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Atinesh
  • 1,790
  • 9
  • 36
  • 57
  • 2
    Just a thought -- (I haven't programmed asm for over 30 years), but are you sure the input num is being seen as a number? e.g. I got ODD for an input of the letter 'a' which is ascii 65. – belwood Jan 17 '16 at 03:08
  • 1
    Yes, testing the low bit of a number is correct, because they're stored in base2. Your program could be more compact if your odd/even branch just set the pointer and length, without duplicating the rest of the `int 0x80` system call. You should comment your code with at least the symbolic name for the system call numbers (e.g. `sys_write`). – Peter Cordes Jan 17 '16 at 03:30
  • You can use the fact that the number of characters read from the input is returned in `eax` from sys_read. A hack would be to use the number of characters returned as an index to find the right most ASCII digit. The number of characters returned includes the terminating character (0x0a). This might work by replacing `mov ax, [num]` `and ax, 1` with `mov al, [num+eax-2]` `and al, 1` . This assumes that the buffer is large enough to hold the longest number including the terminating character. – Michael Petch Jan 17 '16 at 04:04
  • 2
    If you intend to do any other work with numbers (besides even/odd) input from the console, you'll likely need to convert the string of characters returned into an integer and do work on that integer. – Michael Petch Jan 17 '16 at 04:06
  • It is highly advisable to comment your code. It will assist anyone not familiar with the particular system call numbers to provide assistance without needing to research the platform that you are on. It will also make your own debugging much easier. – David Hoelzer Jan 18 '16 at 01:28
  • @DavidHoelzer I've commented the code please have a look. – Atinesh Jan 18 '16 at 08:53
  • @Atinesh: Michael Petch already fully answered your question with his comments. Note that like belwood pointed out, the ASCII value of the last character will have the opposite odd/evenness from the digit it encodes. – Peter Cordes Jan 18 '16 at 10:14
  • instead of `AND`ing with 1 you can just [`test`](http://stackoverflow.com/q/147173/995714) which doesn't destroy the source register and is more efficient – phuclv Nov 28 '16 at 05:36

4 Answers4

2

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
  • As I pointed out in my answer, `sub al, 30h` doesn't affect the low bit, so you don't need to do it before testing the low bit. (Also, `test al, 1` is more efficient than SHR, and doesn't destroy the value.) – Peter Cordes Nov 28 '16 at 07:20
  • Basically the whole point of my answer was to explain that testing the low bit of a number tells you whether it's odd or even. (Although I spent more time in my answer explaining why it's sufficient to only test the low digit of an ASCII string, while you can't just look at the low 2 bits of the low decimal digit to see if a number is a multiple of 4.) – Peter Cordes Nov 28 '16 at 07:24
  • @PeterCordes I love what you are doing to help people, but sometimes we need to take out that single one problem from the OP's code and descriptions. Try to read between the lines because sometimes an OP doesn't exactly know how to express his/problems. Mixing both solutions and better solutions into a single answer may not be as effective. But anyway keep up the good work. – Soffian A.R Nov 28 '16 at 09:27
  • 1
    Yeah that's fair. Have an upvote for explaining just this one part clearly. (I'd still suggest using `test al,1` instead of a shift, though. Having the number around afterwards without having to do a slow `rcl al, 1` to restore it is useful, and test/jnz can macro-fuse on Intel/AMD CPUs.) I think I was just annoyed that this answer had an upvote but my broader answer (which says this and more) didn't, which is a bit silly on my part. Your answer did get me to update mine to make the important points more clearly. – Peter Cordes Nov 28 '16 at 09:29
  • 1
    @PeterCordes Yeah I agree with that one though. TEST instruction preserves the byte, particularly if it involves STOSB next. – Soffian A.R Nov 28 '16 at 09:35
1

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.

Paweł Pela
  • 421
  • 3
  • 16
  • 1
    `test al, 1` / `jnz` works the same, but has slightly smaller code-size, and is slightly more efficient (test/jnz can macro-fuse into a single test-and-branch uop, unlike BT). `bt r, r/i` is efficient, though: just one uop (unlike with a memory operand for the first arg, in which case it's a lot slower than TEST on a memory arg, because of the [crazy-CISC bitstring semantics](http://www.felixcloutier.com/x86/BT.html) where the bit index affects the memory address). – Peter Cordes Nov 27 '16 at 06:20
  • 1
    More importantly, since 2 is a factor of 10, you only need to test the low bit of the last decimal digit. 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**). – Peter Cordes Nov 27 '16 at 06:24
1

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
-1
`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`
  • 1
    Hi welcome to stack-overflow although sometimes code seem to be obvious , please always add some description – Lucifer Jan 16 '20 at 13:13
  • `add al,30h` doesn't change the low bit so it's pointless. If you were trying to convert from an ASCII digit `'0'..'9'` to an integer `0..9`, it's also backwards: you want `sub al, '0'` not `add`. Also, you don't need to duplicate the print code, just select one of two strings + lengths for the same `int 0x80`. – Peter Cordes Jan 17 '20 at 00:00