Reputation: 161
I have a loop that runs succesfully 3 times, but the input I have in this loop works only the first time. I am new to assembly so pls have patience.
Code:
section .bss
| d times 3 resb 1 ;array to store input
| i resb 1 ;counter
section .text
| global _start
|
| _start:
| | mov ecx, 3
| | mov [i], byte 0
| |
| | loop_here:
| | | push ecx
| | |
| | | mov eax, 3
| | | mov ebx, 0
| | | mov ecx, d
| | | add ecx, [i]
| | | inc byte [i]
| | | mov edx, 1
| | | int 80h
| | |
| | | pop ecx
| | loop loop_here
| |
| | mov eax, 1
| | xor ebx, ebx
| | int 80h
Output:
2 ;I inserted 2 as an input
2 ;I inserted 2 again as input
[Program finishes]
Well, later I thought that the loop might not running a third time, so I changed the code a bit.
New Code:
section .bss
| d times 3 resb 1 ;array to store input
| i resb 1 ;counter
section .text
| global _start
|
| _start:
| | mov ecx, 3
| | mov [i], byte 0
| |
| | loop_here:
| | | push ecx
| | |
| | | mov eax, 4
| | | mov ebx, 1
| | | mov ecx, i
| | | add [ecx], byte 30h ;to actually print the count
| | | mov edx, 1
| | | int 80h
| | | sub [i], byte 30h ;to make again a counter
| | |
| | | mov eax, 3
| | | mov ebx, 0
| | | mov ecx, d
| | | add ecx, [i]
| | | inc byte [i]
| | | mov edx, 1
| | | int 80h
| | |
| | | pop ecx
| | loop loop_here
| |
| | mov eax, 1
| | xor ebx, ebx
| | int 80h
New Output:
02
122
[Program finishes]
Explaination:
0 is the counter and 2 my input. 1 and 2 are the counter and the second 2 is my second input, which means that the loop runs succesfully, it just ignores the code for input the second time it runs
Also, do the straight lines I have in my code to visualize the scopes make my code any more readable?
Upvotes: 0
Views: 368
Reputation: 172
Peter already did a great job of covering most of this in the comments, but I thought I'd go into more detail:
The x86 loop
instruction assumes you have a counter in ecx
. loop
automatically decrements ecx
and jumps to the loop label if ecx
is not 0. Since you're calling linux syscalls in a loop, and linux expects the buffer pointer to be in ecx
, you should probably use a different register as your loop counter and some sort of jump instruction to do this instead:
mov esi, 3 ; esi is your loop counter - loop 3 times
loop_here:
; do syscall
dec esi ; decrement loop counter
jnz loop_here ; jump to loop_here if esi is not zero
However, calling syscalls in a loop is not very efficient. Instead, you could do something like this:
mov eax, 3 ; read
mov ebx, 0 ; fd for stdin
mov ecx, d ; address of d buffer into ecx
mov edx, 3 ; read 3 characters at most, the size of your buffer
int 80h
mov esi, eax ; read returns the number of bytes read in eax.
; we'll save it in esi
xor edi, edi ; edi will be our loop counter, this makes it 0
loop_here: ; first, we'll print the loop counter value
mov eax, 4 ; write
mov ebx, 1 ; fd for stdout
mov ecx, i ; address of i for write
mov edx, 1 ; write 1 byte
add edi, '0' ; convert edi loop counter to ASCII
mov [i], byte edi ; put lower byte of edi in i
int 80h
sub edi, '0' ; restore edi
mov eax, 4
mov ebx, 1
mov ecx, d ; address of d for write,
add ecx, edi ; plus current loop counter (offset)
mov edx, 1
int 80h
inc edi ; increment loop counter
cmp esi, edi ; compare to number of bytes read
jne loop_here ; jump to loop_here if not equal
This calls read once, but still calls write in a loop to write the counter and values of d
.
To skip the trailing newline in your input, you could try doing this after your call to read (remember eax
has the number of bytes read):
movzx edi, byte [d+eax-1] ; move and zero-extend last byte of d into edi
cmp edi, `\n` ; is it a newline? (backticks required)
jne skip ; skip if not
dec eax ; otherwise, we'll print one less byte
skip:
; rest of your code here
The newline will still be stored in memory, but you won't print it, because you'll loop one fewer times.
Upvotes: 3