So I am working on an x86 Assembly program for Linux using NASM. This program basically asks the user for their name and their favorite color. After doing this and storing the two strings in variables declared in the .bss section, the program prints "No way name of user, favorite color is my favorite color, too!
The problem I am having is that there are enormous spaces in the output because I do not know how long the string was that the user entered, only the length that I declared the buffer to be.
section .data
greet: db 'Hello!', 0Ah, 'What is your name?', 0Ah ;simple greeting
greetL: equ $-greet ;greet length
colorQ: db 'What is your favorite color?' ;color question
colorL: equ $-colorQ ;colorQ length
suprise1: db 'No way '
suprise1L equ $-suprise1
suprise3: db ' is my favorite color, too!', 0Ah
section .bss
name: resb 20 ;user's name
color: resb 15 ;user's color
section .text
global _start
_start:
greeting:
mov eax, 4
mov ebx, 1
mov ecx, greet
mov edx, greetL
int 80 ;print greet
getname:
mov eax, 3
mov ebx, 0
mov ecx, name
mov edx, 20
int 80 ;get name
askcolor:
;asks the user's favorite color using colorQ
getcolor:
mov eax, 3
mov ebx, 0
mov ecx, name
mov edx, 20
int 80
thesuprise:
mov eax, 4
mov ebx, 1
mov ecx, suprise1
mov edx, suprise1L
int 80
mov eax, 4
mov ebx, 1
mov ecx, name
mov edx, 20
int 80
;write the color
;write the "suprise" 3
mov eax, 1
mov ebx, 0
int 80
The code for what I am doing is above. Does anyone have a good method for finding the length of the entered string, or of taking in a character at a time to find out the length of the string?
Thank you in advance.
After int80 in getname returns, EAX will contain the number of bytes actually read, or a negative error indication.
You should
check for error return
store the return value, as it gives you the length of input
Equivalent code in C:
char name[20];
int rc;
rc = syscall(SYS_read, 0, name, 20-1); // leave space for terminating NUL
if (rc < 0) {
// handle error
} else {
name[rc] = '\0'; // NUL terminate the string
}
Related
I would like to make a copy of a string and store the copy in another variable. I want to do it the most basic way, cause I have just started learning Assembly.
I have something like this:
section .data
mystring db "string goes here", 10 ;string
mystringlen equ $- mystring ;length
helper dw 0 ;variable to help me in a loop
Now, I thought of a loop that will take each byte of the string and assign it to new string.
I have this (I know it only changes initial string, but doesn't work either):
loopex:
mov [mystring+helper], byte 'n' ;change byte of string to 'n'
add helper, 1 ;helper+=1
cmp helper,mystringlen ;compare helper with lenght of string
jl loopex
;when loop is done
mov eax, 4
mov ecx, mystring ; print changed string
mov edx, mystringlen ; number of bytes to write
;after printing
int 0x80 ; perform system call
mov eax, 1 ; sys_exit system call
mov ebx, 0 ;exit status 0
int 0x80
So I would need something like:
old_string = 'old_string'
new_string = ''
counter=0
while(counter!=len(old_string)):
new_string[counter] = old_string[counter]
counter+=1
print(new_string)
The code after help I received:
section .data
mystring db "This is the string we are looking for", 10
mystringlen equ $- mystring
section .bss
new_string: resb mystringlen
section .text
global _start
_start:
mov ecx, 0
mov esi, mystring
mov edi, new_string
loopex: ; do {
mov byte al, [esi]
mov byte [edi], al
inc esi
inc edi
inc ecx
cmp ecx, mystringlen
jl loopex ; }while(++ecx < mystringlen)
;when loop is done
mov eax, 4 ; __NR_write (asm/unistd_32.h)
mov ebx, 1 ; fd = STDOUT_FILENO
mov ecx, new_string ; bytes to write
mov edx, mystringlen ; number of bytes to write
int 0x80 ; perform system call: write(1, new_string, mystringlen)
;after printing
mov eax, 1 ; sys_exit system call number
mov ebx, 0 ; exit status 0
int 0x80
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 ;
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.
The following code:
section .bss
name: resb 50
section .text
global _start
_start:
PUSH EBP
MOV EBP, ESP
MOV EDX, len
MOV ECX, msg
MOV EBX, 1
MOV EAX, 4
INT 0x80
MOV EDX, 50
MOV ECX, name
MOV EBX, 0
MOV EAX, 3
INT 0x80
MOV EBX, 1
MOV EAX, 4
INT 0x80
MOV EDX, cm
MOV ECX, ex
MOV EBX, 1
MOV EAX, 4
INT 0x80
MOV EBX, 0
MOV EAX, 1
INT 0x80
section .data
msg db 'Hello!',0xa
ex db '!',0xa
len equ $ - msg
cm equ $ - ex
I intended to make a simple I/O program that printed Hello!, asked for a char and would print %c!.
Input being | and output being :, I get the following:
:Hello!
:!
|4
:4
:!
How do I make it so that it returns the following
:Hello!
|4
:4!
As Damien_The_Unbeliever says, your equs want to come immediately after the string they're supposed to measure. After your sys_read, eax will be the number of characters read, including the linefeed that ends the reading. You probably don't want to print the linefeed (in this case - sometimes you would). So:
mov edx, eax
dec edx
Or if you want to do it in one instruction:
lea edx, [eax - 1]
As it stands, edx still holds 50, so your next sys_write will print 50 characters. It will NOT stop at a zero or any other string-terminator. ecx will still contain name, but I would reload it just for clarity.
By rights, you should check for an error return (eax would be negative) after each and every int 0x80 but an error is unlikely here.
I started learning how to write programs using the NASM assembly programming language. I wrote this simple program that prompts the user to enter two numbers and then adds the two operands together. I got it to compile with no errors or warnings, but when it prompts the user for the two numbers and it begins to add the two numbers it prints out segmentation fault and program ends. I know a segmentation fault is the equivalent to an access reading / writing violation exception in the Win32 world. But, because I don't know how to debug NASM code; I can't figure out what is wrong. I suspect it has to do with an invalid pointer; but I don't know. Here is the code below:
section .data
msg1: db 'Please Enter A Number: ', 0
length1: equ $ - msg1
msg2: db 'Please Enter A Second Number: ', 0
length2: equ $ - msg2
section .bss
operand1: resb 255
operand2: resb 255
answer: resb 255
section .text
global _start
_start:
; Print first message
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, length1
int 80h
; Now read value
mov eax, 3
mov ebx, 1
mov ecx, operand1
mov edx, 255
int 80h
; Print second message
mov eax, 4
mov ebx, 1
mov ecx, msg2
mov edx, length2
int 80h
; Now read second value
mov eax, 3
mov ebx, 1
mov ecx, operand2
mov edx, 255
int 80h
; Now add operand1 and operand2 and print answer
mov eax, 4
mov ebx, 1
xor ecx, ecx ; Make the ecx register 0
mov ecx, operand1
add ecx, operand2
mov edx, 510
int 80h
(Aside: you should be reading from STDIN_FILENO = 0, not STDOUT_FILENO = 1. Also, you're writing a NUL character and you shouldn't.)
The problem is that operand1 and operand2 are addresses to memory locations holding characters you've read. When you add them, you get a pointer to invalid memory.
You'll have to convert them to integers, add them, and convert back to a string, before you can write it out.
Value in ecx is an address of string that is to be printed when you call int 80h. Last part does not make sense
mov eax, 4
mov ebx, 1
xor ecx, ecx ; Make the ecx register 0
mov ecx, operand1
add ecx, operand2 ; **<<< invalid memory address now in ECX !!!**
mov edx, 510
int 80h
because you are adding address of string operand1 and address of string operand2 and trying to print whatever is located ant resulting address which is most likely points to nowhere.
To debug your program with gdb you can do:
nasm -f elf64 -g -l q1.lst q1.asm
gcc -o q1 q1.o
I replaced the "_start" with "main" so that gcc won't complain, and you can skip the 64 in "-f elf64" if you are building on 32 bit platform.
gdb q1
Here is an example f gdb session:
(gdb) br main
Breakpoint 1 at 0x4004d0: file q1.asm, line 20.
(gdb) r
Starting program: /home/anonymous/Projects/asm/q1
Breakpoint 1, main () at q1.asm:20
20 mov eax, 4
(gdb) n
21 mov ebx, 1
(gdb) n
22 mov ecx, msg1
(gdb) n
23 mov edx, length1
(gdb) p msg1
$1 = 1634036816
(gdb)