Comparing 16 bit numbers in nasm produces wrong results - linux

I have just started learning assembly. I am coding nasm in 32 bit mode. I am trying to compare 3 numbers inputted by the user and print the largest number. However, I cannot seem to correctly compare the numbers if I only reserve 16 bits for each number using resb 2. I do, however, get correct results when I reserved 32 bits for the numbers using resw 2. I cannot understand why this is the case. Here is my code:
SYS_EXIT equ 1
SYS_WRITE equ 4
SYS_READ equ 3
STD_IN equ 0
STD_OUT equ 1
segment .data
msg1 db "Enter first number",0xA
msg1_len equ $- msg1
msg2 db "Enter second number",0xA
msg2_len equ $- msg2
msg3 db "Enter third number",0xA
msg3_len equ $- msg3
msg4 db "Largest number is ",0xA
msg4_len equ $- msg4
segment .bss
num1 resb 2
num2 resb 2
num3 resb 2
res resb 2
section .text
global _start
_start:
mov eax, SYS_WRITE
mov ebx, STD_OUT
mov ecx, msg1
mov edx, msg1_len
int 0x80
mov eax, SYS_READ
mov ebx, STD_IN
mov ecx, num1
mov edx, 2
int 0x80
mov eax, SYS_WRITE
mov ebx, STD_OUT
mov ecx, msg2
mov edx, msg2_len
int 0x80
mov eax, SYS_READ
mov ebx, STD_IN
mov ecx, num2
mov edx, 2
int 0x80
mov eax, SYS_WRITE
mov ebx, STD_OUT
mov ecx, msg3
mov edx, msg3_len
int 0x80
mov eax, SYS_READ
mov ebx, STD_IN
mov ecx, num3
mov edx, 2
int 0x80
mov ecx, [num1]
cmp ecx, [num2]
jg check_third
mov ecx, [num2]
check_third:
cmp ecx, [num3]
jg result
mov ecx, [num3]
result:
mov [res], ecx
mov eax, SYS_WRITE
mov ebx, STD_OUT
mov ecx, msg4
mov edx, msg4_len
int 0x80
mov eax, SYS_WRITE
mov ebx, STD_OUT
mov ecx, res
mov edx, 2
int 0x80
exit:
mov eax, SYS_EXIT
int 0x80
Sorry if it has a lot of repeating code. I understand why I need 2 bytes to store keyboard input when supposedly ascii characters are only 8 bits in length (Since the standard input will also read the new line character aside from the digit). However, I do not know a lot of things about how nasm works such as how it reacts when I move 16 bits of memory to a 32 bit register, how it compares 32 bit and 16 bit values (will it do signed expansion or just pad 0 before binary subtraction). I would truly appreciate it if someone can recommend me resources on the technicalities of nasm aside from explaining why I need to reserve 2 words to do the comparison.

Nasm does not keep record of the size of the data at label like some other assemblers. Suppose you enter 1, 2 and 3. Bytes stored at your labels will then be:
num1: db 0x31, 0x0A
num2: db 0x32, 0x0A
num3: db 0x33, 0x0A
When you move 32 bits of data from label num1, you are actually also moving data from num2. Because little endian machines store least significant bytes first, you get something like:
mov ecx, 0x0A320A31 ; high bytes contain num2 and low bytes contain num1
cmp ecx, 0x0A330A32 ; high bytes contain num3 and low bytes contain num2
jg check_third
mov ecx, 0x0A330A32
check_third:
cmp ecx, 0x00000A33 ; high bytes contain res and low bytes contain num3
jg result
.....
resw 2 (or resd 1) would work because reserved memory is initialized to zeros. As Frank stated in the comment, you should use cl instead of ecx because 8 bits is all you need to handle in this case.

Related

Reading multiple lines of input

What command / function can I use to read the second line in the input section so the 4 and 6 I know I'll have to fix the output to output two digits. I just need helping figuring out how to read the second line of input any help would be greatly appreciated.
SYS_EXIT equ 1
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
segment .data
NEWLINE db 0xa, 0xd
LENGTH equ $-NEWLINE
msg1 db "Enter a digit "
len1 equ $- msg1
msg2 db "Please enter a second digit "
len2 equ $- msg2
msg3 db "The sum is: "
len3 equ $- msg3
segment .bss
INPT resd 1
num1 resb 2
num2 resb 2
res resb 1
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num1
mov edx, 2
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, num1
mov edx, 2
int 0x80
mov eax, 0x4
mov ebx, 0x1
mov ecx, NEWLINE
mov edx, LENGTH
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num2
mov edx, 2
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, num2
mov edx, 2
int 0x80
mov eax, 0x4
mov ebx, 0x1
mov ecx, NEWLINE
mov edx, LENGTH
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 0x80
; moving the first number to eax register and second number to ebx
; and subtracting ascii '0' to convert it into a decimal number
mov eax, [num1]
sub eax, '0'
mov ebx, [num2]
sub ebx, '0'
; add eax and ebx
add eax, ebx
; add '0' to to convert the sum from decimal to ASCII
add eax, '0'
; storing the sum in memory location res
mov [res], eax
; print the sum
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, res
mov edx, 1
int 0x80
exit:
mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
enter image description here

Accepting/Showing 2 digit integers on Assembly using NASM

I'm currently working on a Assembly project. The code works fine on adding 1 digit integer with 1 digit sum, but when I entered 2 digit integers the program will either exit or return no input.
This is my work so far:
SYS_EXIT equ 1
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
segment .data
msg1 db "Enter the first digit: ",0xA, 0xD
len1 equ $- msg1
msg2 db "Please second digit: " ,0xA, 0xD
len2 equ $- msg2
msg3 db "The sum is "
len3 equ $- msg3
segment .bss
num1 resb 255
num2 resb 255
res resb 255
section .text
global _start
_start:
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num1
mov edx, 255
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num2
mov edx, 255
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 0x80
mov eax, [num1]
sub eax, '0'
mov ebx, [num2]
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [res], eax
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, res
mov edx, 1
int 0x80
;move eax, SYS_EXIT
;int 0x80
exit:
mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
This is my sample input:
Please enter first digit: 5
Please second digit: 2
The sum is: 7
My problem is this:
Please enter first digit: 5
Please second digit: 5
The sum is:
The sum returns 0.

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.

Floating point exception (SIGFPE) when doing weighted average of 4 numbers

I'm creating an assembly language program with NASM on Linux. I'm trying to do a weighted average of 4 numbers where the 4th number entered has double the weighting of the others. If I use the numbers 30, 40, 50, 60, I'm calculating the weighted average as (30+40+50+60+60)/5. My code is:
SYS_EXIT equ 1
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
segment .data
msg1 db "Enter exam grade 1: ", 0xA,0xD
len1 equ $- msg1
msg2 db "Enter exam grade 2: ", 0xA,0xD
len2 equ $- msg2
msg3 db "Enter exam grade 3: ", 0xA,0xD
len3 equ $- msg3
msg4 db "Enter final exam grade (worth double: ", 0xA,0xD
len4 equ $- msg4
msg5 db "The sum is: "
len5 equ $- msg5
segment .bss
num1 resb 3
num2 resb 3
num3 resb 3
num4 resb 3
res resb 4
section .text
global main
main:
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num1
mov edx, 3
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num2
mov edx, 4
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num3
mov edx, 3
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg4
mov edx, len4
int 0x80
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num4
mov edx, 3
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg5
mov edx, len5
int 0x80
mov eax, [num1] ; move num1 to register eax
sub eax, '0' ; sub '0' to convert eax bl from ASCII to decimal
mov ebx, [num2] ; move num2 to register ebx
sub ebx, '0' ; sub '0' to convert ebx from ASCII to decimal
mov ecx, [num3] ; move num3 to register ecx
sub ecx, '0' ; sub '0' to convert ecx from ASCII to decimal
mov edx, [num4] ; move num4 to register edx
sub edx, '0' ; sub '0' to convert edx from ASCII to decimal
add eax, ebx ; add ebx to eax
add eax, ecx ; add ecx to eax
add eax, edx ; add edx to eax
add eax, edx ; add edx to eax again
mov bl, '5' ; sub '0' to convert bl from ASCII to decimal
sub bl, '0' ; make bl a decimal
div bl ; divide by bl
add eax, '0' ; add '0' to convert the sum from decimal to ASCII
mov [res], eax ; storing the sum in memory location res
; print the sum
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, res
mov edx, 3
int 0x80
exit:
mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
I can produce this exception if I enter these numbers:
Enter exam grade 1:
30
Enter exam grade 2:
40
Enter exam grade 3:
50
Enter final exam grade (worth double:
60
The sum is: Floating point exception
What causes my program to display Floating Point Exception (SIGFPE) and how can I fix this?

Division in assembly program

this is my code :
section .bss ;Uninitialized data
average resb 5
num1 resb 5
num2 resb 5
num3 resb 5
section .text
global _start ; make the main function externally visible
_start:
;User prompt
mov eax, 4
mov ebx, 1
mov ecx, messaging
mov edx, lenmessaging
int 0x80
;Read and store the user input
mov eax, 3
mov ebx, 0
mov ecx, num1
mov edx, 5 ;5 bytes (numeric, 1 for sign) of that information
int 0x80
mov eax, 4
mov ebx, 1
mov ecx, messaging
mov edx, lenmessaging
int 0x80
;Read and store the user input
mov eax, 3
mov ebx, 0
mov ecx, num2
mov edx, 5 ;5 bytes (numeric, 1 for sign) of that information
int 0x80
mov eax, 4
mov ebx, 1
mov ecx, userMsg
mov edx, lenUserMsg
int 0x80
;Read and store the user input
mov eax, 3
mov ebx, 0
mov ecx, num3
mov edx, 5 ;5 bytes (numeric, 1 for sign) of that information
int 0x80
;Output the message
mov eax, 4
mov ebx, 1
mov ecx, dispMsg
mov edx, lenDispMsg
int 0x80
; moving the first number to eax register and second number to ebx
; and subtracting ascii '0' to convert it into a decimal number
mov eax, [num1]
sub eax, '0'
mov ebx, [num2]
sub ebx, '0'
; add eax and ebx
add eax, ebx
mov ebx, [num3]
sub ebx, '0'
add eax, ebx
mov bl, '3'
sub bl, '0'
div bl
add eax, '0'
mov [average], eax
;Output the number entered
mov eax, 4
mov ebx, 1
mov ecx, average
mov edx, 5
int 0x80
;////////////////
mov eax, 0x1 ; system call number for exit
sub esp, 4 ; OS X (and BSD) system calls needs "extra space" on stack
int 0x80 ; make the system call
section .data ;Data segment
userMsg db 'Please enter a number: ' ;Ask the user to enter a nu
lenUserMsg equ $-userMsg ;The length of the message
dispMsg db 'The average is : '
lenDispMsg equ $-dispMsg
messaging db 'Enter another number : '
lenmessaging equ $-messaging
when I run it ,I have this error :
how can I fix it?
this program is for calculating the average of three numbers.
thanks ;)

Resources