How to read input string from keyboard using assembly language - linux

I'm trying to write a simple program that takes string from the keyboard, then prints it to the screen. So far I couldn't make it work.
Here's the code:
.section .rodata
output: .string "you entered %s\n"
input: .string "%s"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
subl $100, %esp
pushl $input
call scanf
movl %ebp, %esp
subl $100, %esp
pushl $output
call printf
xorl %eax, %eax
movl %ebp, %esp
popl %ebp
ret
When I execute it, the output is you entered (null) for any given input.
when I set the offset of subl $100, %esp command (the one before call print) to subl $104, %esp I get you entered %s, and when the offset is set to 108 I get you entered *gibberish*.
I feel like it's a game where I need to guess where scanf saved the string on the stack (why isn't it where it should be?).
I am using IA32 instruction set.
Any help would be greatly appreciated.

Basically there are 3 problems in your program:
subl $100, %esp
pushl $input
# Error 1:
# As Frank Kotler already wrote at this point
# only $input is stored on the stack; however
# the address of the buffer must also be on
# the stack (see Frank Kotler's comment)
call scanf
movl %ebp, %esp
# Error 2:
# Now the buffer is below ESP.
# Because interrupts use the kernel stack they
# will not overwrite the memory below ESP.
# However signals will destroy the memory below
# ESP!!
#
# Instead of the lines:
# movl %ebp, %esp
# subl $100, %esp
#
# You should use something like this:
# add $8, %esp
# or:
# lea -100(%ebp), %esp
#
subl $100, %esp
# Error 3:
# As for "scanf" the second argument
# is missing on the stack
pushl $output
call printf

[org 0x0100]
jmp start
;=======Data===================================
s1: db 'Enter String: $'
s2: db 'Reversed String:$'
linefeed: db 10, '$'
;=======Read string and store into stack=======
start: mov dx, s1
mov ah, 09h
int 21h
mov dx, linefeed
mov ah, 09h
int 21h
mov si,0
mov bx,0
again: mov ah, 01h
int 21h
cmp al,byte 0dh
je endread
Page 8 of 10
mov bl,al
push bx
inc si
cmp si,15
je endread
jmp again
endread: Push si
;=======Read from stack and print to screen=====
mov dx, linefeed
mov ah, 09h
int 21h
mov dx, s2
mov ah, 09h
int 21h
mov dx, linefeed
mov ah, 09h
int 21h
pop si
r1: pop bx
mov dl,bl
mov ah, 02h
int 21h
dec si
cmp si,0
jne r1
mov dx, linefeed
mov ah, 09h
int 21h
mov ax, 0x4c00 ;terminate program
int 21h

Related

Convert a string of digits to an integer by using a subroutine

Assembly language program to read in a (three-or-more-digit) positive integer as a string and convert the string to the actual value of the integer.
Specifically, create a subroutine to read in a number. Treat this as a string, though it will be composed of digits. Also, create a subroutine to convert a string of digits to an integer.
Do not have to test for input where someone thought i8xc was an integer.
I am doing it like this. Please help.
.section .data
String:
.asciz "1234"
Intg:
.long 0
.section .text
.global _start
_start:
movl $1, %edi
movl $String, %ecx
character_push_loop:
cmpb $0, (%ecx)
je conversion_loop
movzx (%ecx), %eax # move byte from (%ecx) to eax
pushl %eax # Push the byte on the stack
incl %ecx # move to next byte
jmp character_push_loop # loop back
conversion_loop:
popl %eax # pop off a character from the stack
subl $48, %eax # convert to integer
imul %edi, %eax # eax = eax*edi
addl %eax, Intg
imul $10, %edi
decl %ecx
cmpl $String, %ecx # check when it get's to the front %ecx == $String
je end # When done jump to end
jmp conversion_loop
end:
pushl Intg
addl $8, %esp # clean up the stack
movl $0, %eax # return zero from program
ret
Also, I am unable to get the output. I am getting a Segmentation Fault. I am not able to find out what is the error in my code.
Proper interaction with operating system is missing.
In the end: you pushed the result but the following addl $8, %esp invalidates the pushed value and the final ret incorrectly leads the instruction flow to whatever garbage was in the memory pointed by SS:ESP+4 at the program entry.
When you increase the stack pointer, you cannot rely that data below ESP will survive.
Your program does not interact with its user, if you want it to print something, use system function to write.
print_String:
mov $4,eax ; System function "sys_write".
mov $1,ebx ; Handle of the standard output (console).
mov $String,ecx ; Pointer to the text string.
mov $4,edx ; Number of bytes to print.
int 0x80 ; Invoke kernel function.
end:mov $1,eax ; System function "sys_exit".
mov (Intg),ebx ; Let your program terminate gracefully with errorlevel Intg.
int 0x80 ; Invoke kernel function.

Printing floating point numbers in assembler

I'm trying to print a floating-point value from assemler calling a printf function. It works fine with strings and integer values but fails printing floats. Here is an example of working code:
global main
extern printf
section .data
message: db "String is: %d %x %s", 10, 0
end_message: db ".. end of string", 0
section .text
main:
mov eax, 0xff
mov edi, message
movsxd rsi, eax
mov rdx, 0xff
mov rcx, end_message
xor rax, rax
call printf
ret
String is: 255 ff .. end of string
So, the parameters are passed through registers: edi contains address of a formatting string, rsi and rdx contain the same number to print in decimal and hex styles, rcx contains end of a string, rax contains 0 as we do not have a float to print.
This code works fine but something changes while trying to print float:
global main
extern printf
section .data
val: dq 123.456
msg: db "Result is: %fl",10, 0
section .text
main:
mov rdi,msg
movsd xmm0,[val]
mov eax,1
call printf
mov rax, 0
ret
This code snipped can be compiled but returns segmentation fault being executed. It seems that the problem is in wrong value of xmm0 but trying to change movsd xmm0,[val] to movsd xmm0,val gives an
error: invalid combination of opcode and operands
message.
The compiler is NASM running on openSuSe 12.3
Update. I tried to make a c program and produce a .S assembly. It gives a very weird solution:
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movq val(%rip), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
movl $.LC0, %edi
movl $1, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
Is it possible to write a simple printf example?
for your assembler problem:
you need to align the stack before your main program starts.
insert
sub rsp, 8
right after main:
then add it again before ret:
add rsp, 8

How to read and display a value in Linux assembly?

I'm a beginner in Linux assembler and I have some questions. I'd like to read some characters from keyboard, convert it to value (I understand that this convertion should be from ASCII to decimal, right?), do some math (add, sub, multiply, whatever) and display the result in Terminal. How should I do that? I wrote some code but it probably doesn't make sense:
SYSEXIT = 1
EXIT_SUCC = 0
SYSWRITE = 4
SYSCALL = 0x80
SYSREAD = 3
.data
value: .space 5, 0
value_len: .long .-value
result: .long
result_len: .long .-result
.text
.global _start
_start:
movl $SYSREAD, %eax
movl $EXIT_SUCC, %ebx
movl $value, %ecx
movl value_len, %edx
int $SYSCALL
movl $0, %edx
movl value_len, %ecx
for:
movb value(, %edx, 1), %al
subb $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for
add $10, result
movl $0, %edx
movl result_len, %ecx
for1:
movb result(, %edx, 1), %al
add $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for1
movl $SYSWRITE, %eax
movl $SYSEXIT, %ebx
movl $result, %ecx
movl result_len, %edx
int $SYSCALL
movl $SYSEXIT, %eax
movl $EXIT_SUCC, %ebx
int $SYSCALL
I don't know if I should reserve memory by spaces? Or reading characters in loop?
How to convert it, to be able to make some math operation and then convert it to be able to display it?
I know that to get the value of ASCII char I should subtract 48, but what next?
I had an idea to multiply each bits by 2^k where k is 0,1,2...n it's good idea? If so, how to implement something like this?
As you can see I had a lot of questions, but I only need to someone show me how to do, what I am asking about. I saw some similar problems, but nothing like this in Linux.
Thank you in advance for the all information.
All the best.
At first reading and writing the console in Linux is by using the file functions with special console handles, that have always the same values: STDIN=0, STDOUT=1 and STDERR=2.
At second you will need some decent documentation about Linux system calls. Notice that the C-centric one (like "man") are not suitable, because C language does not use the system calls directly, but has wrappers that often change the arguments and the result values.
On the following site you can download an assembly-centric SDK for Linux, that contains the needed documentation and many examples and include files.
If you only need the help files, you can browse them online: Here
If the problem is the conversion from ASCII string to number and then back to string, here are two simple procedures that can do the job, if the requirements are not so big. The StrToNum is not so advanced, it simply convert decimal unsigned number:
; Arguments:
; esi - pointer to the string
; Return:
; CF=0
; eax - converted number
; edx - offset to the byte where convertion ended.
;
; CF=1 - the string contains invalid number.
;
StrToNum:
push ebx esi edi
xor ebx,ebx ; ebx will store our number
xor eax,eax
mov al,[esi]
cmp al,'0'
jb .error
cmp al,'9'
jbe .digit
jmp .error
.digit:
sub al,'0'
add ebx,eax
inc esi
mov al,[esi]
cmp al,'0'
jb .finish
cmp al,'9'
ja .finish
mov edx,ebx ; multiply ebx by 10
shl ebx,3
add ebx,edx
add ebx,edx
jmp .digit
.finish:
mov eax, ebx
mov edx, esi
clc
pop edi esi ebx
ret
.error:
stc
pop edi esi ebx
ret
NumToStr is pretty flexible. It converts number to a string in any radix and with sign:
;**********************************************************************************
; NumToStr converts the number in eax to the string in any radix approx. [2..26]
; Arguments:
; edi - pointer to the string buffer
; ecx - radix
; eax - number to convert.
; There is no parameter check, so be careful.
; returns: edi points to the end of a converted number
;**********************************************************************************
NumToStr:
test eax,eax
jns NumToStrU
neg eax
mov byte [edi],"-"
inc edi
NumToStrU:
cmp eax,ecx
jb .lessA
xor edx,edx
div ecx
push edx
call NumToStrU
pop eax
.lessA:
cmp al, 10
sbb al, 69h
das
stosb
ret

How to clear screen on protected mode

I'm using NASM for Linux and I'd like know how, in the protected mode, you can clear the screen. I found a solution using the int10h, but on the protected mode I can only use int80h. Thanks in advance.
You can write \x1b[2J to the standard output so the terminal get cleared and fix the cursor position using \x1b[H, for example in nasm:
global _start
section .data
clr db 0x1b, "[2J", 0x1b, "[H"
clrlen equ $ - clr
section .text
_start:
mov eax, 4
mov ebx, 1
mov ecx, clr
mov edx, clrlen
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
for gnu assembler:
.globl _start
.data
clr : .ascii "\x1b[2J\x1b[H"
clrlen = . - clr
.text
_start:
movl $4, %eax
movl $1, %ebx
movl $clr, %ecx
movl $clrlen, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80

x86 Assembly 2-Digit Keyboard Input

It needs to allow a two-digit number for input that will be used to indicate how many times the name is printed. I can't figure out how to separate the second digit though and have it checked to make sure it is between 0x30 and 0x39. I also keep getting this weird box after the name that has 0017 inside it.
.data
input_msg_len: .long 26
input_msg: .ascii "Enter a two-digit number: "
name: .ascii "Michael Chabon\n"
name_len: .long 16
max: .long 0
count: .long 0
tmp: .long 0
input_str: .ascii "??"
.text
.global _start
_start:
mov $4, %eax
mov $1, %ebx
mov $input_msg, %ecx
mov input_msg_len, %edx
int $0x80
mov $3, %eax
mov $0, %ebx
mov $input_str, %ecx
mov $2, %edx
int $0x80
mov $input_str, %eax
add count, %eax
mov $input_str, %eax
mov (%eax), %bl
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
mov count, %eax
inc %eax
mov %eax, count
sub $0x30, %bl
mov %bl, max
mov $10, %bl
imul %bl
mov %bl, max
#Not sure how to check second char in input_str.
#Want to check it then subtract $0x30 and move to tmp before adding tmp to max.
mov $0, %edi
again:
cmp max, %edi
je end
mov $4, %eax
mov $1, %ebx
mov $name, %ecx
mov name_len, %edx
int $0x80
inc %edi
jmp again
end:
mov $1, %eax
int $0x80
Thanks in advance!
There are some bugs in your code.
Below, 2 first lines of that block are redundant, as mov $input_str, %eax overwrites eax anyway.
mov $input_str, %eax
add count, %eax
mov $input_str, %eax
Then here, it makes no sense to load count into eax here:
mov count, %eax
inc %eax
mov %eax, count
You can do this in a lot shorter and clearer way with:
incl count
Then, next bug is that you recently loaded count into eax, and then multiply the lowest 8 bits of count loaded into al with 10, in this piece of code:
mov (%eax), %bl // bl = first character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
mov count, %eax // eax = count
inc %eax // eax++
mov %eax, count // count = eax
sub $0x30, %bl // 0 <= bl <= 9
mov %bl, max // max = bl <- you lose this value in the next mov %bl, max
mov $10, %bl // bl = 10
imul %bl // ax = 10 * and(count, 0xff) // ax = al*bl (signed multiply)
mov %bl, max // max = 10 <- here you overwrite the value of max with 10
So, according to my intuition you don't want to do ax = 10 * and(count, 0xff), but 10 * (first number). imul %bl does o signed multiply between al and bl, and stores the result in ax. So the code above could be changed to something like this:
mov (%eax), %bl // bl = first character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
incl count
pushl %eax // push eax to stack
sub $0x30, %bl // 0 <= bl <= 9
mov $10, %al // al = 10
imul %bl // ax = 10 * bl (signed multiply)
mov %al, max // 0 <= max <= 90
Then, you can check the second character similarly to the first character:
pop %eax // pop eax from stack
incl %eax
mov (%eax), %bl // bl = second character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
sub $0x30, %bl // 0 <= bl <= 9
add %bl, max // 0 <= max <= 99
I strongly recommend you to learn to use some debugger. gdb has several frontends, of which I think ddd works best according to my experience. gdbtui is also convenient.

Resources