I'm writing a subroutine to simply reprint decimal numbers as strings using the stack, but not getting the values I expected. When I run it through the debugger I see that I can't get the value from esi into al. I suspect that I'm not allowed to use esi in the manner that I am, but I'm not sure on another way I can do this. Also, I am not allowed to push the elements I'm storing in edx onto the stack.
Subroutine code:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
%define BUFLEN 256
SECTION .bss ; uninitialized data section
src_str: resb BUFLEN ; buffer for backwards number
dec_str: resb BUFLEN ; number will be converted and put in this buffer
rlen: resb 4 ; length
SECTION .text ; code begins here
global prt_dec
; Begin subroutine
prt_dec:
push eax
push ebx
push ecx
push edx
push esi
push edi
mov eax, [esp + 28] ; store the decimal number 4 bytes each for each push, plus the eip
mov esi, src_str ; point esi to the backwards string buffer
mov edi, dec_str ; point edi to the new buffer
mov ebx, 10 ; stores the constant 10 in ebx
div_loop:
mov edx, 0 ; clear out edx
div ebx ; divide the number by 10
add edx, '0' ; convert from decimal to char
mov [esi], edx ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
cmp eax, 0 ; is there anything left to divide into?
jne div_loop ; if so, continue the loop
output_loop:
add esi, ecx ; move 1 element beyond the end of the buffer
mov al, [esi - 1] ; move the last element in the buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
cmp ecx, 0 ; if it doesn't equal 0, continue loop
jne output_loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
mov edx, 0
mov edx, rlen
int 080h
pop_end:
pop edi ; move the saved values back into their original registers
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret
; End subroutine
Driver:
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT 1
%define SYSCALL_READ 3
%define SYSCALL_WRITE 4
SECTION .data ; initialized data section
lf: db 10 ; just a linefeed
msg1: db " plus "
len1 equ $ - msg1
msg2: db " minus "
len2 equ $ - msg2
msg3: db " equals "
len3 equ $ - msg3
SECTION .text ; Code section.
global _start ; let loader see entry point
extern prt_dec
_start:
mov ebx, 17
mov edx, 214123
mov edi, 2223187809
mov ebp, 1555544444
push dword 24
call prt_dec
add esp, 4
call prt_lf
push dword 0xFFFFFFFF
call prt_dec
add esp, 4
call prt_lf
push 3413151
call prt_dec
add esp, 4
call prt_lf
push ebx
call prt_dec
add esp, 4
call prt_lf
push edx
call prt_dec
add esp, 4
call prt_lf
push edi
call prt_dec
add esp, 4
call prt_lf
push ebp
call prt_dec
add esp, 4
call prt_lf
push 2
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 080h
push 3
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 5
call prt_dec
add esp, 4
call prt_lf
push 7
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 080h
push 4
call prt_dec
add esp, 4
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 080h
push 3
call prt_dec
add esp, 4
call prt_lf
; final exit
;
exit: mov EAX, SYSCALL_EXIT ; exit function
mov EBX, 0 ; exit code, 0=normal
int 080h ; ask kernel to take over
; A subroutine to print a LF, all registers are preserved
prt_lf:
push eax
push ebx
push ecx
push edx
mov eax, SYSCALL_WRITE ; write message
mov ebx, STDOUT
mov ecx, lf
mov edx, 1 ; LF is a single character
int 080h
pop edx
pop ecx
pop ebx
pop eax
ret
Fixes I had on mind (asterisk denotes lines I did touch), hopefully it will be clear from comments what I did:
...
div_loop:
* xor edx, edx ; clear out edx
div ebx ; divide the number by 10
* add dl, '0' ; convert from decimal to char
* mov [esi], dl ; store char in output buffer
inc esi ; move to next spot in output buffer
inc ecx ; keep track of how many chars are added
* test eax,eax ; is there anything left to divide into?
* jnz div_loop ; if so, continue the loop
* ; (jnz is same instruction as jne, but in this context I like "zero" more)
* mov [rlen], ecx ; store number of characters into variable
output_loop:
* ; esi already points beyond last digit, as product of div_loop (removed add)
* dec esi ; point to last/previous digit
mov al, [esi] ; move the char from the div_loop buffer into al
mov [edi], al ; move it into the first position of the converted output buffer
inc edi ; move to the next position of the converted output buffer
dec ecx ; decrement to move backwards through the output buffer
* jnz output_loop ; if it doesn't equal 0, continue loop
print:
mov eax, SYSCALL_WRITE ; write out string
mov ebx, STDOUT
mov ecx, dec_str
* mov edx, [rlen] ; read the number of digits from variable
int 080h
...
Related
I started to learn assembly some days ago and i write my first ever piece of code using user input, string functions, passing arguments by stack or by register etc...
I have some questions. Do you have some advices to make my code faster. For example, in my atoi function, i know that imul is time consuming. Maybe, there are enormous mistakes but as far as i know, many things to improve for sure. So my main question is : are there fatal errors in this first code and my second is : any type to refactoring code with faster instructions
SYS_READ equ 3
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
%macro printm 2
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, %1
mov edx, %2
int 0x80
%endmacro
%macro prolog 0
push ebp,
mov ebp, esp
%endmacro
%macro epilog 0
mov esp, ebp
pop ebp
%endmacro
section .text
global _start
_start:
; first check if our strlen proc works
push dword msgbegin
call strlen
add esp, byte 4
cmp eax, lenbegin
je .suite ; it works, we continue
; exiting prog if the len computed in rax != lenbegin
mov eax, 1
int 0x80
.suite:
; check if strcpy works printing res (msgbegin -> srcdst)
push dword lenbegin
push dword msgbegin
push dword strdst
call strcpy
add esp, byte 12
push dword lenbegin
push dword strdst
call print
add esp, byte 8
; first input
printm msgbinp1, leninp1
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num1
mov edx, 2
int 0x80
printm msgbinp2, leninp2
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, num2
mov edx, 2
int 0x80
printm msgbinp3, leninp3
mov eax, SYS_READ
mov ebx, STDIN
mov ecx, bignum
mov edx, 4
int 0x80
mov edx, bignum
call atoi
cmp eax, 123
je .success ; exit if bignum != 123
mov eax, 1
int 0x80
.success:
; need to strip line feed from bignum
printm bignum, 4
printm msgoutp, lenoutp
; now we compute the sum
mov eax, [num1]
sub eax, '0'
mov ebx, [num2]
sub ebx, '0'
add eax, ebx
add eax, '0'
mov [sum], eax
printm msgres, lenres
; we print it
printm sum, 1
; exiting the programm
mov eax, 1
int 0x80
print:
push ebp
mov ebp, esp
mov eax, 4
mov ebx, 1
mov ecx, [ebp + 8]
mov edx, [ebp + 12]
int 0x80
mov esp, ebp
pop ebp
ret
strcpy:
push ebp
mov ebp, esp
mov ecx, [ebp + 16]
mov esi, [ebp + 12]
mov edi, [ebp + 8]
rep movsb
mov esp, ebp
pop ebp
ret
strlen:
push ebp
mov ebp, esp
push edi
push ecx
mov edi, [ebp + 8]
sub ecx, ecx
sub al, al
not ecx
cld
repne scasb
not ecx
lea eax, [ecx] ; keep null term in size
pop ecx
pop edi
mov esp, ebp
pop ebp
ret
atoi:
xor eax, eax ; zero a "result so far"
.top:
movzx ecx, byte [edx] ; get a character
inc edx ; ready for next one
cmp ecx, '0' ; valid?
jb .done
cmp ecx, '9'
ja .done
sub ecx, '0' ; "convert" character to number
imul eax, 10 ; multiply "result so far" by ten
add eax, ecx ; add in current digit
jmp .top ; until done
.done:
ret
section .data
msgbegin db "hello everyone !", 0xa, 0
lenbegin equ $ - msgbegin
msgbinp1 db "Enter a digit : ", 0xa, 0
leninp1 equ $ - msgbinp1
msgbinp2 db "Enter second digit : ", 0xa, 0
leninp2 equ $ - msgbinp2
msgbinp3 db "Enter third digit : ", 0xa, 0
leninp3 equ $ - msgbinp3
msgoutp db "is equal to 123 !", 0xa, 0
lenoutp equ $ - msgoutp
msgres db "sum of x and y is ", 0xa, 0
lenres equ $ - msgres
strdst times lenbegin db 0
segment .bss
sum resb 1
num1 resb 2
num2 resb 2
bignum resd 4
Thanks you. I started reading the doc but i'm not sure that i understood key concepts.
I'm trying to learn the basics of assembly but can't get across on how to display results stored in memory.
section .data
num1 db 1,2,3,4,5
num2 db 1,2,3,4,5
output: db 'The dot product is "'
outputLen1 : equ $-output
output2: db '" in Hex!', 10
output2Len : equ $-output2
section .bss
dotProd resw 1 ; store dot product in here
section .text
global _start
_start:
mov eax, 0
mov ecx, 5
mov edi, 0
mov esi, 0
looper: mov ax, [edi + num1]
mov dx, [esi + num2]
mul dx
add [dotProd], ax
cmp cx, 1
je printOutput
inc edi
inc esi
dec cx
jmp looper ; go back to looper
printOutput:
mov eax,4 ; The system call for write (sys_write)
mov ebx,1 ; File descriptor 1 - standard output
mov ecx, output ;
mov edx, outputLen1 ;
int 80h ; Call the kernel
mov eax, 4
mov ebx, 1
mov ecx, dotProd,
mov edx, 1
int 80h
mov eax, 4
mov ebx, 1
mov ecx, output2,
mov edx, output2Len
int 80h
jmp done
done:
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 80h
What I'm trying to do is get the dot product of the two list of numbers and display it on the screen. However, I keep getting random letters which I believe are hex representations of the real decimal value. How can I convert it to decimal? The current value display is 7, which should is the equivalent ASCII char for 55, which in this case is the dot product of both list of numbers.
esi and edi must be increased such that it points to next element of array.(in this particular example, only one of them is sufficient).
declare mun1 andnum2 as dd, instead of db (see here).
Also, you have to have method for printing number.(see this and this).
Below is a complete code which uses printf.
;file_name:test.asm
;assemble and link with:
;nasm -f elf test.asm && gcc -m32 -o test test.o
extern printf
%macro push_reg 0
push eax
push ebx
push ecx
push edx
%endmacro
%macro pop_reg 0
pop edx
pop ecx
pop ebx
pop eax
%endmacro
section .data
num1: dd 1,2,3,4,5
num2: dd 1,2,3,4,5
msg: db "Dot product is %d",10,0
section .bss
dotProd resd 1 ; store dot product in here
section .text
global main
main:
mov eax, 0
mov ecx, 5
mov edx, 0
mov esi, 0
mov dword[dotProd], 0h
looper: mov eax, dword[esi + num1]
mov edx, dword[esi + num2]
mul edx
add [dotProd], eax
cmp cx, 1
je printOutput
add esi,4
dec cx
jmp looper ; go back to looper
printOutput:
push_reg
push dword[dotProd]
push dword msg
call printf
add esp,8
pop_reg
jmp done
done:
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 80h
How do I write a variable to a file using NASM?
For example, if I execute some mathematical operation - how do I write the result of the operation to write a file?
My file results have remained empty.
My code:
%include "io.inc"
section .bss
result db 2
section .data
filename db "Downloads/output.txt", 0
section .text
global CMAIN
CMAIN:
mov eax,5
add eax,17
mov [result],eax
PRINT_DEC 2,[result]
jmp write
write:
mov EAX, 8
mov EBX, filename
mov ECX, 0700
int 0x80
mov EBX, EAX
mov EAX, 4
mov ECX, [result]
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
You have to implement ito (integer to ascii) subsequently len for this manner. This code tested and works properly in Ubuntu.
section .bss
answer resb 64
section .data
filename db "./output.txt", 0
section .text
global main
main:
mov eax,5
add eax,44412
push eax ; Push the new calculated number onto the stack
call itoa
mov EAX, 8
mov EBX, filename
mov ECX, 0x0700
int 0x80
push answer
call len
mov EBX, EAX
mov EAX, 4
mov ECX, answer
movzx EDX, di ; move with extended zero edi. length of the string
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
itoa:
; Recursive function. This is going to convert the integer to the character.
push ebp ; Setup a new stack frame
mov ebp, esp
push eax ; Save the registers
push ebx
push ecx
push edx
mov eax, [ebp + 8] ; eax is going to contain the integer
mov ebx, dword 10 ; This is our "stop" value as well as our value to divide with
mov ecx, answer ; Put a pointer to answer into ecx
push ebx ; Push ebx on the field for our "stop" value
itoa_loop:
cmp eax, ebx ; Compare eax, and ebx
jl itoa_unroll ; Jump if eax is less than ebx (which is 10)
xor edx, edx ; Clear edx
div ebx ; Divide by ebx (10)
push edx ; Push the remainder onto the stack
jmp itoa_loop ; Jump back to the top of the loop
itoa_unroll:
add al, 0x30 ; Add 0x30 to the bottom part of eax to make it an ASCII char
mov [ecx], byte al ; Move the ASCII char into the memory references by ecx
inc ecx ; Increment ecx
pop eax ; Pop the next variable from the stack
cmp eax, ebx ; Compare if eax is ebx
jne itoa_unroll ; If they are not equal, we jump back to the unroll loop
; else we are done, and we execute the next few commands
mov [ecx], byte 0xa ; Add a newline character to the end of the character array
inc ecx ; Increment ecx
mov [ecx], byte 0 ; Add a null byte to ecx, so that when we pass it to our
; len function it will properly give us a length
pop edx ; Restore registers
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
len:
; Returns the length of a string. The string has to be null terminated. Otherwise this function
; will fail miserably.
; Upon return. edi will contain the length of the string.
push ebp ; Save the previous stack pointer. We restore it on return
mov ebp, esp ; We setup a new stack frame
push eax ; Save registers we are going to use. edi returns the length of the string
push ecx
mov ecx, [ebp + 8] ; Move the pointer to eax; we want an offset of one, to jump over the return address
mov edi, 0 ; Set the counter to 0. We are going to increment this each loop
len_loop: ; Just a quick label to jump to
movzx eax, byte [ecx + edi] ; Move the character to eax.
movsx eax, al ; Move al to eax. al is part of eax.
inc di ; Increase di.
cmp eax, 0 ; Compare eax to 0.
jnz len_loop ; If it is not zero, we jump back to len_loop and repeat.
dec di ; Remove one from the count
pop ecx ; Restore registers
pop eax
mov esp, ebp ; Set esp back to what ebp used to be.
pop ebp ; Restore the stack frame
ret ; Return to caller
I'm trying to learn NASM. I want to write a procedure to get one character at a time from the console until a newline (0xA) is encountered using only kernel calls. So far I have
global _start
section .data
sys_read equ 3
sys_write equ 4
stdin equ 0
stdout equ 1
section .bss
line resb 11
index resb 4
section .text
_start:
push ebp
mov ebp, esp
call _readLine
afterReadLine:
call _printLine
mov esp, ebp
pop ebp
jmp exit
_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in index when _readLine returns
mov eax, sys_read ; syscall to read
mov ebx, stdin ; stdin
mov edx, [index] ; put index into edx
mov ecx, dword line ; put line addr in ecx
add ecx, edx ; add index to addr in ecx
mov edx, 1 ; read one char
int 0x80 ; call kernel to read char
mov ecx, [index] ; put index into ecx
cmp dword [line + ecx], 0xA ; compare value at line + ecx to new line char
inc byte [index] ; increment index
je afterReadLine ; if last char is newline return
jne _readLine ; if last char is not new line, loop
_printLine:
mov eax, sys_write
mov ebx, stdout
mov ecx, line
mov edx, [index]
int 0x80
ret
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
When I test against the value stored in the index variable at the end, it always equals 0. I tried moving the value of index into eax, but it's also zero after the jump. I tried using the ret keyword, but that also seemed to overwrite my values. What is the best practice way to return the value of characters read from this procedure?
EDIT:
I tried the following and I'm still not getting any output. With input "abcd[newline]" the program outputs "abcd" if I hardcode the value of 4 in edx in the _printLine procedure, but as written won't output anything.
global _start
section .data
sys_read equ 3
sys_write equ 4
stdin equ 0
stdout equ 1
bytesRead dd 0
termios: times 36 db 0
ICANON: equ 1<<1
ECHO: equ 1<<3
section .bss
line resb 11
index resb 4
section .text
_start:
push ebp
mov ebp, esp
call canonical_off
call echo_off
call _readLine
call _printLine
call canonical_on
call echo_on
mov esp, ebp
pop ebp
jmp exit
_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in bytesRead when _readLine returns
mov eax, sys_read ; syscall to read
mov ebx, stdin ; stdin
mov edx, [index] ; put index into edx
mov ecx, dword line ; put line addr in ecx
add ecx, edx ; add index to addr in ecx
mov edx, 1 ; read one char
int 0x80 ; call kernel to read char
mov ecx, [index] ; put index into ecx
cmp dword [line + ecx], 0xA ; compare value at line + ecx to new line char
inc byte [index] ; increment index
jne _readLine ; if last char is not new line, loop
ret
_printLine:
mov eax, sys_write
mov ebx, stdout
mov ecx, line
mov edx, [index] ; Works if hardcoded 4 here!
int 0x80
ret
canonical_off:
call read_stdin_termios
; clear canonical bit in local mode flags
;push rax
mov eax, ICANON
not eax
and [termios+12], eax
;pop rax
call write_stdin_termios
ret
echo_off:
call read_stdin_termios
; clear echo bit in local mode flags
;push rax
mov eax, ECHO
not eax
and [termios+12], eax
;pop rax
call write_stdin_termios
ret
canonical_on:
call read_stdin_termios
; set canonical bit in local mode flags
or dword [termios+12], ICANON
call write_stdin_termios
ret
echo_on:
call read_stdin_termios
; set echo bit in local mode flags
or dword [termios+12], ECHO
call write_stdin_termios
ret
read_stdin_termios:
; push rax
; push rbx
; push rcx
;push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5401h
mov edx, termios
int 80h
;pop rdx
; pop rcx
;pop rbx
;pop rax
ret
write_stdin_termios:
; push rax
;push rbx
;push rcx
; push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5402h
mov edx, termios
int 80h
;pop rdx
;pop rcx
; pop rbx
;pop rax
ret
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
link here http://ideone.com/Lw3fyV
First off, you are calling _readLine but not returning from it, instead you are jumping to the label after call _readLine conveniently called afterReadLine. You should use ret to exit from your function.
Now to your question. By default, the terminal is in canonical mode (Cooked mode), meaning all input is buffered. The system will fill your buffer until the return key is pressed and adds 0xA to the end of the buffer.
What you want is non-canonical mode (raw mode). This means the system does not process the key presses but passes it on to you.
How do i read single character input from keyboard using nasm (assembly) under ubuntu?
By switching to raw mode, you can get each character pressed until the return key and do what you will with it.
Twice i try to read character from input, the first time "call getUserAction" works fine, but the main problem is, that the system didn't call the second read func, it just exits. Here is a little part of my code
SECTION .bss
buffer resb 1
SECTION .text
global _start
_start:
...some unuseful and commented code
call getUserAction ;reading 1 byte from console, works fine
...
jmp count
call exit
count:
...
call getUserAction; don't work, just exits from program without err
...commented code to debug
call exit
getUserAction: ;proc to read from console 1 byte
mov eax, 3
mov ebx, 0
mov ecx, buffer
mov edx, 1
int 80h
ret
Also i tried to put code from getUserAction in "count" proc, but it changes nothing.
UPD: Answer on first comment:
After the second "getUserAction":
mov eax, [buffer]
cmp eax, 'u'
je setUsersValues
cmp eax, 'e'
je callFunc
call exit
UPD2: I'm so sorry, here is all code
%define newline 10,0
%define A1 -7
%define A2 3
%define A3 2
%define A4 4
%define A5 2
SECTION .data
labInfo db "============Lab_3============",newline
labInfoLen equ $ - labInfo
mainMenu db "Choose the ex:",newline,\
" r - call count",newline,\
" t - call beep", newline,\
" y - call exit func",newline
mainMenuLen equ $ - mainMenu
funcStr db "Here func func func", newline
funcStrLen equ $ - funcStr
countPromt db "Please,choose the variant of variables value",newline,\
" u - user defined values", newline,\
" e - execerciese defined values", newline,\
"Your choise:"
promtLen equ $ - countPromt
SECTION .bss
buffer resb 1
resultValue resb 1
%macro calculateFunc 5
push eax
push edx
push ecx
mov eax, %1
mov ecx, %2
add eax, ecx
mov ecx, %3
imul ecx
mov ecx, %4
xor edx, edx
idiv ecx
mov ecx, %5
add eax, ecx
mov [resultValue], eax
pop ecx
pop edx
pop eax
%endmacro
SECTION .text
global _start
_start:
;call showPromt
push labInfo
push labInfoLen
call printStr
add esp, 8
;call showVarsList
push mainMenu
push mainMenuLen
call printStr
add esp, 8
call getUserAction
;get get get action
mov eax, [buffer]
cmp eax, 'r'
je count
cmp eax, 't'
je beep
cmp eax, 'y'
je exit
jmp _start
count:
;showFuncInfo
push funcStr
push funcStrLen
call printStr
add esp, 8
;showProposal
push countPromt
push promtLen
call printStr
add esp, 8
call getUserAction
mov eax, [buffer]
cmp eax, 'u'
je setUsersValues
cmp eax, 'e'
je callFunc
call exit
ret
setUsersValues:
nop; add some code later
ret
callFunc:
calculateFunc A1,A2,A3,A4,A5
add byte [resultValue], '0'
push resultValue
push 1
call printStr ; print Result
add esp, 8
ret
printStr:
push ebp
mov ebp, esp
mov eax, 4
mov ebx, 1
mov ecx, [ebp+12]
mov edx, [ebp+8]
int 80h
pop ebp
ret
getUserAction:
mov eax, 3
mov ebx, 0
mov ecx, buffer
mov edx, 1
int 80h
ret
beep:
nop;add some code later
ret
exit:
mov eax, 1
xor ebx, ebx
int 80h
without seeing all the code, your buffer should be at least 2 bytes. the size for both sys_read and sys_write should be 2 not 1.