This question already has answers here:
NASM Assembly convert input to integer?
(2 answers)
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
(1 answer)
Assembling 32-bit binaries on a 64-bit system (GNU toolchain)
(2 answers)
Writing to stdin and reading from stdout (UNIX/LINUX/C Programming)
(5 answers)
Closed 6 months ago.
I want to write a simple assembly program to receive user input 'iterations', then it prints the numbers from zero till 'iterations'.
example, of the expected output:
enter the number of iterations
4
performing the loop:
0123
exiting.
But the actual output is an infinite loop with dummy characters !
here is my code, I have commented every operation:
section .data
user_ask: db "enter the number of iterations", 0xA
str_len: equ $ - user_ask
out_msg: db "performing the loop: ", 0xA
out_len: equ $ - out_msg
exiting: db 0xA, "exiting.", 0xA
ext_len: equ $ - exiting
global _start
section .bss
out_buff resb 4
iterations resb 2
section .text
_start:
;; Prompt the user to enter a number
mov eax, 4
mov ebx, 1
mov ecx, user_ask
mov edx, str_len
int 0x80
;; Read user input and stores that ot 'iterations'
mov eax, 3
mov ebx, 2
mov ecx, iterations
mov edx, 5
int 0x80
;; Message: "performing the loop: ... "
mov eax, 4
mov ebx, 1
mov ecx, out_msg
mov edx, out_len
int 0x80
;; Setting edi to zero
xor edi, edi
.loop:
mov ecx, edi
add ecx, 0x30
mov [out_buff], ecx
mov ecx, out_buff
;; Writing the numbers
mov eax, 4
mov ebx, 1
mov edx, 1
int 0x80
inc edi
;; Compare current edi with user input 'iterations'
cmp edi, DWORD[iterations]
jl .loop
.exit:
; Message: "exiting."
mov eax, 4
mov ebx, 1
mov ecx, exiting
mov edx, ext_len
int 0x80
;exit
mov eax, 1
mov ebx, 0
int 0x80
the problem is happening in the compare line, I think I am comparing an ASCII value with an integer or so...
BTW: I am running this on ubuntu 20.04 with nasm, as follow:
nasm -f elf64 loop.asm && ld -o loop loop.o && ./loop
Related
This is my code here and when I try to link my program linker says undefined reference to printf in my code. The code is a program to check divisibilty by 2 and 8 and print numbers in loop. And im getting the error undefined reference to printf and I need the user to input an integer. I linked it through:
nasm -f elf32 cw2.asm
gcc -m32 cw2.o
Also, I tried online compiler also the same error
global main
extern printf
SECTION .data
message1: db "Enter a number: ", 0
number1: db "%d", 0
integer1: times 10 db 0 ; 32-bits integer = 10 bytes
msg db 'Divisible by 2 and 8', 0xa,0xd
len equ $ - msg
SYS_EXIT equ 1
STDOUT EQU 1
SYS_WRITE EQU 4
SECTION .bss
val2 resb 2
SECTION .text
main:
push message1
call printf
pop ebx
mov eax, 3
mov ebx, 1
mov ebx, val2
mov edx, 2
int 0x80
push val2
mov al, [val2]
l1:
.divisble_by_2:
mov ax, [number1]
xor dx, dx
mov bx, 2
div bx
cmp dx, 0
jnz .not_divisible
.divisble_by_8:
mov ax, [number1]
xor dx, dx
mov bx, 8
div bx
cmp dx, 0
jnz .not_divisible
.print_number:
mov edx, [number1]
add edx, 48
mov [number1], edx
mov eax, 4
mov ebx, 1
mov ecx, [number1]
mov edx, len
int 0x80
mov eax, .divisble_by_2
int 0x80
.not_divisible:
xor eax, eax
mov edx, [eax+len]
mov al, 1
mov esi, .divisble_by_2
mov edi, eax
mov eax, 1
int 0x80
The program should print numbers in loop so there is no ending to the program
Here is the error I got on linux when i tried linking
Error
ld: main.o: in function `main':
main.asm:(.text+0x6): undefined reference to `printf'
I am trying to write a program that gets a number with one or two digits and write Hello! as many times as that number.
I used this posts to write my code:
NASM: The loop turns into an infinite loop
Check null character in assembly language
Multi-Digit Input from NASM I did not understand this :)
But my code only works for two digit numbers and the result for single digit numbers are wrong.
My code:
section .data
msg db 'Hello!',0xA
len equ $-msg
section .bss
n resb 2
section .text
global _start
_start:
;get n
mov edx, 2
mov ecx, n
mov ebx, 0
mov eax, 3
int 0x80
lea esi,[n] ;address of our text
call toint
;loop for print 'Hello!\n'
print_loop:
push ecx
mov edx, len
mov ecx, msg
mov ebx, 1
mov eax, 4
int 0x80
pop ecx
loop print_loop
mov eax, 1
int 0x80
toint:
push eax
push ebx
xor ebx, ebx
next_digit:
movzx eax, byte[esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
cmp byte [esi] , 0x0 ;check next character is null or not
jne next_digit
; end_loop:
mov ecx, ebx
pop eax
pop ebx
ret
The sys_read call returns in EAX the count of characters that were sent to your inputbuffer. Because you allowed for an input of max. 2 characters, this count will be either 0, 1, or 2. You could use this info in your toint routine.
; IN (eax,esi) OUT (ecx)
toint:
mov ecx, eax ; Number of digits
jecxz done
push eax ; (1)
push ebx ; (2)
push esi ; (3)
xor ebx, ebx
next:
movzx eax, byte [esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
dec ecx
jnz next
mov ecx, ebx
pop esi ; (3)
pop ebx ; (2)
pop eax ; (1)
done:
ret
Please notice that there's a reversed ordering to be observed when preserving/restoring registers on the stack! (Your code missed this...)
4 tips
a. Prefer the MOV variant to load an address. It's always a shorter instruction.
b. Guard yourself against an input of zero.
c. Don't use LOOP. It's a slow instruction.
d. Provide the exit code to terminate the program.
mov esi, n ; (a)
call toint ; -> ECX
jecxz Exit ; (b)
print_loop:
...
dec ecx ; (c)
jnz print_loop
Exit:
xor ebx, ebx ; (d)
mov eax, 1
int 0x80
My program works, but there is something wrong with my printMax function. The program terminates with a
Segmentation fault (core dumped).
I have tried building a stack for the function and just doing a pusha popa and both ways, I get the seg fault core dumped.
I've tried calling the function, but it just runs twice.
Any idea what I am doing wrong?
SECTION .data ;data section
msg1 : db "Here are the array elements:", 10, 0
msg1Len: equ $-msg1
msg2 : db "Here is the max value in the array:", 10, 0
msg2Len: equ $-msg2
arr : dd 2,4,6,8,10,20,40
arrLen : equ ($-arr)/4 ;number of elements = array length / 4
SECTION .bss
max resd 1 ;declare and reserve space for max
SECTION .text
global main
main:
push ebp
mov ebp, esp
mov ecx, msg1 ;print msg1
mov edx, msg1Len
call PString
;save array base address in ebx and save sizein in ecx
mov ebx, arr
mov ecx, arrLen; store num elements in ecx
;loop to print array
PrintArray:
mov eax, [ebx] ;move value [ebx] to eax
call PrintDec
call Println
add ebx, 4
loop PrintArray
printMax:
section .text
pusha
;reset array to find max
mov ebx, arr
mov ecx, arrLen
loopForMax:
mov eax, [ebx]
cmp eax, [ebx +4]
jle sameMax
mov [max], eax
sameMax:
add ebx, 4 ;move to next element
loop loopForMax
mov ecx, msg2
mov edx, msg2Len
call PString
mov eax, [max]
call PrintDec
call Println
popa
ret
;exit program and clean stack
mov esp, ebp
pop ebp
ret
PString:; save register values of the called function
pusha
mov eax,4 ; use 'write' system call = 4
mov ebx,1 ; file descriptor 1 = STDOUT
int 80h ; call the kernel
; restore the old register values of the called function
popa
ret
Println:
;will call PString func
;will change content of ecx and edx
;need to save registers used by the main program
section .data
nl db 10
section .text
pusha
mov ecx, nl
mov edx, 1
call PString
;return original register values
popa
ret
PrintDec:
;saves all registers so they return unmodified
;build the function to handle dword size
section .bss
decstr resb 10 ; 10 32-bit digits
ct1 resd 1 ;keep track of dec-string size
section .text
pusha; save registers
mov dword[ct1],0 ;initially assume 0
mov edi, decstr ; edi points to dec-string
add edi, 9 ; moved to the last element of string
xor edx, edx ; clear edx for 64-bit div
whileNotZero:
mov ebx, 10 ; get ready to divide by 10
div ebx ; divide by 10
add edx, '0' ; convert to ascii
mov byte[edi], dl ; put it in string
dec edi ; move to next char in str
inc dword[ct1] ; inc char counter
xor edx, edx ; clear edx
cmp eax, 0 ;is remainder 0?
jne whileNotZero ;if no, keep on looping
inc edi ; conversion finished, bring edi
mov ecx, edi ; back to start of string. make ecx
mov edx, [ct1] ; point to counterm edx gets # chars
mov eax, 4 ; print to stdout
mov ebx, 1
int 0x80 ; call kernel
popa ; restore registers
ret
I'm having trouble understanding how to traverse a 2-d array in x86 assembly language. I am missing a bit of understanding. This is what I have so far.
The issue is the lines with //offset and //moving through array
For the //offset line the error I am getting is "non constant expression in second operand"
and also
"ebx: illegal register in second operand"
For the next line I get the error
"edx: illegal register in second operand"
mov esi, dim
mul esi
mov eax, 0 // row index
mov ebx, 0 // column index
mov ecx, dim
StartFor:
cmp ecx, esi
jge EndFor
lea edi, image;
mov edx, [eax *dim + ebx] // offset
mov dl, byte ptr [edi + esi*edx] // moving through array
mov edx, edi
and edx, 0x80
cmp edx, 0x00
jne OverThreshold
mov edx, 0xFF
OverThreshold:
mov edx, 0x0
See x86 tag wiki, including the list of addressing modes.
You can scale an index register by a constant, but you can't multiply two registers in an addressing mode. You'll have to do that yourself (e.g. with imul edx, esi, if the number of columns isn't a compile time constant. If it's a power-of-2 constant, you can shift, or even use a scaled addressing mode like [reg + reg*8]).
re: edit: *dim should work if dim is defined with something like dim equ 8. If it's a memory location holding a value, then of course it's not going to work. The scale factor can be 1, 2, 4, or 8. (The machine code format has room for a 2-bit shift count, which is why the options are limited.)
I'd also recommend loading with movzx to zero-extend a byte into edx, instead of only writing dl (the low byte). Actually nvm, your code doesn't need that. In fact, you overwrite the value you loaded with edi. I assume that's a bug.
You can replace
imul edx, esi
mov dl, byte ptr [edi + edx] ; note the different addressing mode
mov edx, edi ; bug? throw away the value you just loaded
and edx, 0x80 ; AND already sets flags according to the result
cmp edx, 0x00 ; so this is redundant
jne OverThreshold
with
imul edx, esi
test 0x80, byte ptr [edi + edx] ; AND, discard the result and set flags.
jnz
Of course, instead of multiplying inside the inner loop, you can just add the columns in the outer loop. This is called Strength Reduction. So you do p+=1 along each row, and p+=cols to go from row to row. Or if you don't need to care about rows and columns, you can just iterate over the flat memory of the 2D array.
A 2-dimensional array is just an interpretation of a sequence of bytes. You'll have to choose in which order to store the items. For example, you might choose "row-major order".
I've written a demo where a buffer is filled with a sequence of numbers. Then the sequence is interpreted as single- and two-dimensional array.
tx86.s
%define ITEM_SIZE 4
extern printf
section .bss
cols: equ 3
rows: equ 4
buf: resd cols * rows
c: resd 1
r: resd 1
section .data
fmt: db "%-4d", 0 ; fmt string, '0'
fmt_nl: db 10, 0 ; "\n", '0'
section .text ; Code section.
global main
main:
push ebp
mov ebp, esp
; fill buf
mov ecx, cols * rows - 1
mov [buf + ITEM_SIZE], ecx
.fill_buf:
mov [buf + ecx * ITEM_SIZE], ecx
dec ecx
jnz .fill_buf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 1-dimensional array
; buf[c] = [buf + c * ITEM_SIZE]
xor ecx, ecx
mov [c], ecx
.lp1d:
mov ecx, [c]
push dword [buf + ecx * ITEM_SIZE]
push dword fmt
call printf
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols * rows
jl .lp1d
; print new line
push dword fmt_nl
call printf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 2-dimensional array
; buf[r][c] = [buf + (r * cols + c) * ITEM_SIZE]
xor ecx, ecx
mov [r], ecx
.lp1:
xor ecx, ecx
mov [c], ecx
.lp2:
; calculate address
mov eax, [r]
mov edx, cols
mul edx ; eax = r * cols
add eax, [c] ; eax = r * cols + c
; print buf[r][c]
push dword [buf + eax * ITEM_SIZE]
push dword fmt
call printf
; next column
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols
jl .lp2
; print new line
push dword fmt_nl
call printf
; next row
mov ecx, [r]
inc ecx
mov [r], ecx
cmp ecx, rows
jl .lp1
mov esp, ebp
pop ebp ; restore stack
xor eax, eax ; normal exit code
ret
Buidling(on Linux)
nasm -f elf32 -l tx86.lst tx86.s
gcc -Wall -g -O0 -m32 -o tx86 tx86.o
Running
./tx86
0 1 2 3 4 5 6 7 8 9 10 11
0 1 2
3 4 5
6 7 8
9 10 11
I've written a piece of code that takes a number in ASCII characters from the prompt, converts it into a decimal number and stores it in 'dnumber'. The conversion has been checked and goes well. It goes wrong at the prompt. It seems to be stuck in an infinite loop while asking the user for the ASCII character number. I want the program stop asking for input when the user presses ENTER, but that termination value never seems to be reached even though I think I've set it that way.
I've asked two related questions on this forum lately and it showed that I don't understand system calls properly. I've read all the documentation on 'http://www.tutorialspoint.com/assembly_programming', 'http://cs.lmu.edu/~ray/notes/nasmtutorial/' and some of 'http://www.x86-64.org/documentation/abi.pdf' and apparently I'm still not getting it. Hopefully you can show me the light.
Here is the compiler info:
nasm -f elf64 convinput.asm
ld -s -o convinput convinput.o
Here is the prompt:
$ ./convinput
Enter a number and press enter:
123
123
123
As you can see I've pressed ENTER twice, but the prompt still asks for input.
Here is the code:
section .text
global _start
_start:
mov eax, 4
mov ebx, 1
mov edx, lenmsg1
mov ecx, msg1
int 80h
xor eax, eax
xor ebx, ebx
xor edx, edx
mov esi, data
call input
mov esi, data
movzx ecx, byte [dignum]
xor ebx,ebx ; clear ebx
call string_to_int
mov dword [dnumber], eax
mov eax, 1
mov ebx, 0
int 80h
input:
mov eax, 3
mov ebx, 0
mov ecx, esi
mov edx, 1
int 80h
inc byte [dignum]
cmp byte [esi], 13
inc esi
jne input
ret
string_to_int:
xor ebx,ebx
movzx eax, byte [esi]
inc esi
sub al,'0' ; convert from ASCII to number4
mov ebx, 10
mul ebx
add ebx,eax ; ebx = ebx*10 + eax
dec byte [dignum]
cmp byte [dignum], 0
jne string_to_int
mov eax,ebx
ret
section .bss
dignum resb 1
data resb 1000
dnumber resd 1
section .data
msg1 db 'Enter a number and press enter: ', 10, 0
lenmsg1 equ $ -msg1
; ESI = pointer to the string to convert
; ECX = number of digits in the string (must be > 0)
; Output:
; EAX = integer valu
Judging by values you put into eax you are using a 32-bit syscall table, which on linux is different than 64-bit one.
You can easily see what syscalls (and with what arguments) are called by running your program under strace.