Проблема логической петли / входного потока (NASM x86 Assembly) - PullRequest
0 голосов
/ 14 ноября 2011

У меня есть программа ниже, которая пытается принять ввод от пользователя и повторять ту же самую строку, пока пользователь не введет ее снова. (Это личный учебный проект)

Тем не менее, я испытываю серьезные затруднения в том, чтобы заставить его работать правильно. В прошлой ветке здесь вы можете видеть ввод, каламбур, предназначенный другими пользователями для этой проблемы.

%include "system.inc"

section .data
    greet:      db 'Hello!', 0Ah, 'Please enter a word or character:', 0Ah
    greetL:     equ $-greet     ;length of string
    inform:     db 'I will now repeat this until you type it back to me.', 0Ah
    informL:    equ $-inform
    finish:     db 'Good bye!', 0Ah
    finishL:    equ $-finish
    newline:    db 0Ah
    newlineL:   equ $-newline


section .bss

input: resb 40  ;first input buffer
check: resb 40  ;second input buffer

section .text

    global _start
_start:


greeting:
    mov eax, 4
    mov ebx, 1
    mov ecx, greet
    mov edx, greetL
    sys.write

getword:
    mov eax, 3
    mov ebx, 0
    mov ecx, input
    mov edx, 40
    sys.read

    sub eax, 1  ;remove the newline
    push eax    ;store length for later

instruct:
    mov eax, 4
    mov ebx, 1
    mov ecx, inform
    mov edx, informL
    sys.write

    pop edx     ;pop length into edx
    mov ecx, edx    ;copy into ecx
    push ecx    ;store ecx again (needed multiple times)

    mov eax, 4
    mov ebx, 1
    mov ecx, input
    sys.write

    mov eax, 4  ;print newline
    mov ebx, 1
    mov ecx, newline
    mov edx, newlineL
    sys.write

    mov eax, 3  ;get the user's word
    mov ebx, 0
    mov ecx, check
    mov edx, 40
    sys.read

    sub eax, 1
    push eax

    xor eax, eax

checker:
    pop ecx     ;length of check
    pop ebx     ;length of input
    mov edx, ebx    ;copy
    cmp ebx, ecx    ;see if input was the same as before

    jne loop    ;if not the same go to input again

    mov ebx, check
    mov ecx, input
secondcheck:

    mov dl, [ebx]
    cmp dl, [ecx]
    jne loop    
    inc ebx
    inc ecx
    dec eax
    jnz secondcheck

    jmp done

loop:

    pop edx
    mov ecx, edx
    push ecx
    mov eax, 4
    mov ebx, 1
    mov ecx, check
    sys.write   ;repeat the word

    mov eax, 4
    mov ebx, 1
    mov ecx, newline
    mov edx, newlineL
    sys.write
    mov eax, 3  ;replace new input with old
    mov ebx, 0
    mov ecx, check
    mov edx, 40
    sys.read

    jmp checker

done:

    mov eax, 1  
    mov ebx, 0  
    sys.exit

Пример вывода даст:

Hello!
Please enter a word or character:
INPUT: Nick
I will now repeat this until you type it back to me.
Nick
INPUT: Nick
N
INPUT: Nick

INPUT: Nick

И так будет продолжаться до тех пор, пока это не будет до смерти. Есть идеи по проблеме?

Спасибо.

1 Ответ

1 голос
/ 14 ноября 2011

instruct оставляет два элемента в стеке, которые потребляются checker в первый раз в цикле.Но они не заменяются для случая, когда вы снова идете по кругу.Это самая фундаментальная проблема в вашем коде (могут быть и другие).

Вы можете найти это, запустив отладчик и наблюдая за указателем стека esp;но это можно увидеть, просто взглянув на код - если вы уберете все, кроме манипуляций со стеком и ветвлений, вы ясно увидите, что путь checker -> loop -> обратно к checker выскакивает триэлементы, но только один:

greeting:
    ...
getword:
    ...
    push eax    ;store length for later
instruct:
    ...
    pop edx     ;pop length into edx
    ...
    push ecx    ;store ecx again (needed multiple times)
    ...
    push eax
checker:
    pop ecx     ;length of check
    pop ebx     ;length of input
    ...
    jne loop    ;if not the same go to input again
    ...
secondcheck:
    ...
    jne loop    
    ...
    jnz secondcheck
    jmp done
loop:
    pop edx
    ...
    push ecx
    ...
    jmp checker
done:
    ...

Есть лучшие способы сохранить долгоживущие переменные, чем пытаться перетасовать их в стеке, как это с помощью push и pop.

  1. Храните их в разделе данных (.bss, который у вас уже есть), а не в стеке.

  2. Выделите некоторое место в стеке,и загружать / хранить их там напрямую.например, sub esp, 8, чтобы зарезервировать два 32-битных слова, затем получить доступ к [esp] и [esp+4].(Стек должен быть выровнен по 32-битной границе, поэтому всегда резервируйте кратные 4 байта.) Не забудьте add esp, 8, когда вы закончите его использовать.

(Этипо существу эквивалентны тому, что компилятор C сделал бы для глобальных (или static) переменных и локальных переменных, соответственно.)

...