首页 > 解决方案 > x86 NASM | 循环输入仅在第一次和第三次工作

问题描述

我有一个成功运行 3 次的循环,但我在此循环中的输入仅在第一次有效。我是组装新手,所以请耐心等待。

代码:

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

输出:

2 ;I inserted 2 as an input
2 ;I inserted 2 again as input
[Program finishes]

好吧,后来我认为循环可能不会运行第三次,所以我稍微更改了代码。

新代码:

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

新输出:

02
122
[Program finishes]

解释:

0 是计数器,2 是我的输入。1 和 2 是计数器,第二个 2 是我的第二个输入,这意味着循环运行成功,它只是在第二次运行时忽略了输入代码

另外,我在代码中使用直线来可视化范围是否使我的代码更具可读性?

标签: linuxassemblyinputx86nasm

解决方案


彼得已经在评论中很好地涵盖了大部分内容,但我想我会更详细地介绍:

x86loop指令假定您在ecx. 如果不是0loop ,则自动递减ecx并跳转到循环标签。由于您在循环中调用 linux 系统调用,并且 linux 期望缓冲区指针位于,您可能应该使用不同的寄存器作为循环计数器和某种跳转改为执行此操作的说明:ecxecx

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

但是,在循环中调用系统调用并不是很有效。相反,您可以执行以下操作:

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

这会调用一次读取,但仍会在循环中调用write来写入计数器和 的值d

要跳过输入中的尾随换行符,您可以在调用read之后尝试执行此操作(记住eax读取的字节数):

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 

换行符仍将存储在内存中,但您不会打印它,因为您将少循环一次。


推荐阅读