This question already has answers here:
Why should EDX be 0 before using the DIV instruction?
(2 answers)
Closed 6 years ago.
I tried writing my first, very simple program in Assembly. It is supposed to take two digits as input and perform addition, subtraction, multiplication and division on it. I currently do not care about how the program performs when you enter numbers like 5 and 5 (the sum and product of which wouldn't display correctly), cause I'm still learning, and will get to expanding the program further later. Right now, everything works like a charm, except division. When inputting 4 and 2, it, for example, returns the letter 'r' under the quotient. The code that should do this is:
;lastly, division
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg6
mov edx, len6
int 0x80
mov ax, [num1]
sub ax, '0'
mov ebx, [num2]
sub ebx, '0'
div ebx
add al, '0'
add ah, '0' ;in case there is a remainder
mov [quot], al
mov [rem], ah
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, quot
mov edx, 1
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, spacer
mov edx, len8
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg7
mov edx, len7
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, rem
mov edx, 1
int 0x80
Just for the record, msg6 is just the message: "The quotient is: " and msg7 is the message "The remainder is: ". num1 and num2 are the input numbers. spacer is my way of inputting spacing between the results. Thanks in advance!
div ebx
divides ebx by eax, and puts the remainder in edx. This is because ebx is a 4 byte divisor.
You seem to be expecting the remainder in ah. I believe you'll get what you want if you use
div bl
since bl is a 1 byte divisor. For 1 byte divisors the result goes in al and the remainder in ah. See https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic.
EDIT:
As some have mentioned, you must clear the destination for the remainder in advance, edx for the 4-byte case, and ah for the 1-byte case.
Related
The code just prints out nothing what would I do for the output that will be 150 and 48? I'm practicing nasm
section .data
num1 db 99
num2 db 51
result db 0
section .text
global _start
_start:
; Addition
mov al, [num1]
add al, [num2]
mov [result], al
call display_result
; Subtraction
mov al, [num1]
sub al, [num2]
mov [result], al
call display_result
; Exit program
mov eax, 1
int 0x80
display_result:
; Display result
mov edx, 1
mov ecx, [result]
mov ebx, 1
mov eax, 4
int 0x80
; Display newline
mov edx, 1
mov ecx, 10
mov ebx, 1
mov eax, 4
int 0x80
exit:
mov eax, 1
int 0x80`
I expect for my coding to print the Addition and Subtraction of Num1 and Num2 but it is not printing what would I do for it to print?
See DIV instruction, I think it will best fit your needs. Just divide number by 10 and add the remainder to ASCII '0' value and your result will be one digit from the back of the number you are trying to print. For example: 157 will produce 7 remainder first time, 5 second time and then you'll be left with 1 itself. Hope you get the idea.
This question already has answers here:
X86 NASM Assembly converting lower to upper and upper to lowercase characters
(5 answers)
X86 Assembly Converting lower-case to uppercase
(1 answer)
Closed 3 years ago.
I want to change the string to all caps, although I am having trouble getting the length of the input. What i have tried so far is moving the address of the message into a registrar then indexing through the string and also increment a counter variable. Then comparing the char in the address to a '.' (signifying the end of the message) and if its found not to be equal it will recall this block of statements. At least this is what I want my code to do. Not sure if this is even the right logic. I know there are alot of errors and its messy but I'm learning so please just focus on my main question. thank you! EDIT: the input i use is 'this is a TEST.'
;nasm 2.11.08
SYS_Write equ 4
SYS_Read equ 3
STDIN equ 0
STDOUT equ 1
section .bss
message resb 15
counter resb 2
section .data
msg1: db 'Enter input (with a period) that I will turn into all capitals!',0xa ;msg for input
len1 equ $- msg1
section .text
global _start
_start:
mov eax, SYS_Write ; The system call for write (sys_write)
mov ebx, STDOUT ; File descriptor 1 - standard output
mov ecx, msg1 ; msg to print
mov edx, len1 ; len of message
int 0x80 ; Call the kernel
mov eax, SYS_Read ;system call to read input
mov ebx, STDIN ;file descriptor
mov ecx, message ;variable for input
mov edx, 15 ;size of message
int 0x80 ;kernel call
mov [counter], byte '0'
getLen:
mov eax, message
add eax, [counter]
inc byte [counter]
cmp eax, '.'
jne getLen
mov eax, SYS_Write ; this is to print the counter to make sure it got the right len
mov ebx, STDOUT
mov ecx, counter
mov edx, 2
int 0x80
jmp end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov eax, [message]
;add eax, counter
cmp eax, 90
jg toUpper
toUpper:
sub eax, 32
mov [message], eax
mov eax, SYS_Write ; The system call for write (sys_write)
mov ebx, STDOUT ; File descriptor 1 - standard output
mov ecx, message ; Put the offset of hello in ecx
mov edx, 10 ; helloLen is a constant, so we don't need to say
int 0x80 ; Call the kernel
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
end:
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 0x80 ;
I have done my best to explain all of my thought process when adding these two numbers. However, upon running the resulting executable I end up with
"Sum is: j" which is clearly wrong. Additionally, it seems that no matter which inputs I give the sum stays as "j" so there must be something awfully wrong.
I believe this code should work but my understanding is clearly flawed.
Where should I start in fixing this? I just recently began learning assembly.
section .data ;line 1
msg db "Sum is: "
len equ $ - msg
section .bss
num1 resb 1
eol1 resb 1
num2 resb 1
eol2 resb 1
sum resb 2
section .text
global _start
print_int:
mov eax, 4 ;defining routine print_int
mov ebx, 1 ;file descriptor (stdout)
int 0x80 ;system call number (sys_write)
ret ;return back
_start:
;Read and store user input for num1
mov eax, 3
mov ebx, 0
mov ecx, num1
mov edx, 2 ;2 bytes of info
int 80h
mov byte [eol1], 0xA ; value of first end of line
;Read and store user input for num2
mov eax, 3
mov ebx, 0
mov ecx, num2
mov edx, 2 ;2 bytes of info
int 80h
mov byte [eol2], 0xA ;value of 2nd end of line
;Add num1 and num2
mov eax, num1
sub eax, '0' ;this is so that the value in 3 is set to 3 because
;'3' and '0' actually are ASCII values
mov ebx, num2
sub ebx, '0'
add eax, ebx ;Move the sum of 0x3 and 0x4 into eax
add eax, '0' ;Set eax to be the ASCII value for the result of the sum
mov [sum], eax ;Set this ascii value of the sum to sum
mov ecx, msg ;Move msg ('Sum is: ') into eax
mov edx, len ;Move len (length of msh) into edx
call print_int ; call routine print_int above
;load sum to to be printed
mov ecx, sum
mov edx, 2 ;size in bytes of sum
call print_int
mov eax, 1 ;system call number (sys_exit)
xor ebx, ebx
int 0x80 ;line 43
Your program operates on 2 single digit numbers inputted by the user.
mov eax, num1
mov ebx, num2
On NASM, this will move the address of these variables in those registers. What you want is the contents! You need to write the square brackets.
But wait - since the input has only a single byte of information, you should read the data in the byte-sized registers AL and BL.
mov al, [num1]
mov bl, [num2]
All subtractions and additions then will also have to use these smaller sizes.
sub al, '0' ;From character '0'-'9' to number 0-9
sub bl, '0'
add al, bl
add al, '0' ;From number 0-9 to character '0'-'9'
The character in AL is what you want to print. Easiest is to first append the newline 0xA in the AH register and then write AX in the memory.
mov ah, 0xA
mov [sum], ax
All the above is correct whenever the sum of the 2 single digit numbers was less than 10.
Imagine what will happen when you input e.g. numbers 5 and 8 ?
The sum (13) would require 2 characters leaving no room for the extra newline character. The largest sum will come from adding 9 and 9 (18).
Best re-define "sum" as sum resb 3.
Then write the following:
mov al, [num1]
mov bl, [num2]
sub al, '0'
sub bl, '0'
add al, bl
mov edx, 2 ;String length if single digit sum
mov ecx, sum ;Address of sum
cmp al, 10
jb SingleDigit
mov byte [ecx], '1'
inc ecx ;Move to position for the units/newline
inc edx ;String length if double digit sum
sub al, 10 ;Only keep the units
SingleDigit:
add al, '0' ;From number 0-9 to character '0'-'9'
mov ah, 0xA ;Append newline
mov [ecx], ax
mov ecx, sum
call print_int
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.
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.