x86 NASM Assembly - Problems with Input

2019-02-24 20:23发布

I am working to take input from a user twice, and compare the input. If they are the same, the program exits. If not, it reprints the input from the first time, and waits for the user to type something. If it is the same, the same thing as before occurs. If not, the same thing as before occurs.

Input and looping is not the problem. The main problem is the result I am getting from the program. My following is what I am doing codewise:

%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 %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:   db $-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

        xor eax, eax

    checker:
        mov ebx, check
        mov ecx, input
        cmp ebx, ecx    ;see if input was the same as before

        jne loop    ;if not the same go to input again
        je done     ;else go to the end
        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



    loop:
        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
    sys.write

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

My result is now: EDITED

Hello!
Please enter a word or character:
Nick
I will now repeat this until you type it back to me.
Nick
(I input) Magerko
(I get) M
(I input)Nick
(I get)
(I input)Nick
(I get)

EDITED

And this continues. My checks do not work as intended in the code above, and I eventually don't even get the program to print anything but a newline. Is there a reason for this?

Thanks.

2条回答
ゆ 、 Hurt°
2楼-- · 2019-02-24 20:56

Apart from what @Joshua is pointing out, you're not comparing your strings correctly.

checker:
    mov ebx, check  ; Moves the *address* of check into ebx
    mov ecx, input  ; Similarly for input
    cmp ebx, ecx    ; Checks if the addresses are the same (they never are)

Firstly, when you have e.g. label dd 1234 in your data segment mov eax, label will move the address of label to eax while mov eax, [label] will move the contents stored at label (in this case 1234) into eax.

Note that in the above example I deliberately used a 32-bit variable so that it would fit neatly into eax. If you're using byte sized variables (like ascii characters) e.g. mybyte db 0xfe you'll either have to use byte sized register (al, ah, dh etc.) or use the move with zero/sign extend opcodes: movzx eax, byte [mybyte] will set eax to 254, while movsx eax, byte [mybyte] will set eax to -2 (0xfffffffe).

You also need to do a character by character comparison of the strings. Assuming you save the read string length (you really should be checking for negative return values - meaning errors) in input_len and check_len it could look something like:

    mov eax, [input_len]
    cmp eax, [check_len]
    jne loop       ; Strings of different length, do loop again
    mov ebx, check
    mov ecx, input
.checkloop:
    mov dl, [ebx]  ; Read a character from check
    cmp dl, [ecx]  ; Equal to the character from input?
    jne loop       ; Nope, jump to `loop`
    inc ebx        ; Move ebx to point at next character in check
    inc ecx        ; and ecx to next character in input
    dec eax        ; one less character to check
    jnz .checkloop ; done?

    ; the strings are equal if we reach this point in the code
    jmp done

If you're interested in another way of doing this in fewer instructions look up rep cmpsb.

There are a few other problems in the code immediately following your checker code. The pop edx instruction (and the code following, down to the loop label) will not be execute as you're always jumping either to loop or done.

jne loop    ;if not the same go to input again
je done     ;else go to the end
pop edx   ; Will never be reached!

The reason you're getting funny characters is from newlineL: db $-newline This should be equ instead of db or you should replace mov edx, newlineL with movzx edx, byte [newlineL]. Since newlineL unlike the other *L names refers to a variable and not a constant equ mov edx, newlineL will use the address of the newlineL variable as the number of bytes to write, when you wanted it to be 1.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-02-24 21:19

You are assuming sys.read returns the entire line. It is not required to do so. It may return after only one character, or even possibly after part of the second line.

You know, this kind of thing kind of ticks me off. This looks like a homework problem in writing in assembly, but the problem is not with the assembly, but with the assumptions in how the system calls work.

I really wish the instructors would provide an fgets library function for stuff like this.

Anyway, the stupid way to fix it is to read one byte at a time, looking for LF (byte 10) to end the loop.

查看更多
登录 后发表回答