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?
Advertisement
Answer
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.