Skip to content
Advertisement

x86 NASM | Input in Loop working only the first and third time

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.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement