Search code examples
assemblyx86-64yasm

Skips one input every time after a jmp


So I have this code that is supposed to ask for 2 numbers and then make some operations with them, and after finishing one operation and outputting the answer, it should ask again for 2 numbers and an option until the user selects the option to exit. But for some reason, right after printing the result one time, the second time it skips one input.

segment .data

    prompt db "Please enter the first number: "
    promptLen equ $-prompt
    prompt2 db "Please enter the second number: "
    prompt2Len equ $-prompt2
    prompt3 db 10, "Your result is: "
    prompt3Len equ $-prompt3
    linefeed db 10
    menu1 db "Please chose what to do with the numbers: ", 10
    menu1Len equ $-menu1
    menu2 db "1. Add", 10
    menu2Len equ $-menu2
    menu3 db "2. Multiply", 10
    menu3Len equ $-menu3
    menu4 db "3. Divide", 10
    menu4Len equ $-menu4
    menu5 db "4. Nothing, exit.", 10
    menu5Len equ $-menu5
    menu6 db "5. Surprise me"
    menu6Len equ $-menu6
    menu7 db 10, "Selection: "
    menu7Len equ $-menu7

segment .bss

    Num1 resb 8
    Num2 resb 8
    uNum1 resd 1
    uNum2 resd 1
    choice resd 1
    uRes resd 1
    Result resb 8
    ResultLen resd 1

segment .text
    global _start

_start:
    call getNumbers
    call getMenuOption

    mov eax, [choice]
    cmp eax, 49
    je Addition
    cmp eax, 50
    je Multiply
    cmp eax, 51
    je Divide
    cmp eax, 52
    je exit
    cmp eax, 53
    je surprise

getNumbers:
    mov eax, 4
    mov ebx, 1
    mov ecx, prompt
    mov edx, promptLen
    int 80h

    mov eax, 3
    mov ebx, 0
    mov ecx, Num1
    mov edx, 8
    int 80h

    mov esi, Num1
    call dec2eax
    mov [uNum1], eax

    mov eax, 4
    mov ebx, 1
    mov ecx, prompt2
    mov edx, prompt2Len
    int 80h

    mov eax, 3
    mov ebx, 0
    mov ecx, Num2
    mov edx, 8
    int 80h

    mov esi, Num2
    call dec2eax
    mov [uNum2], eax
    ret

dec2eax:                            ; Arg ESI: ASCII-string (0x0A-terminated) with decimal digits
    xor eax,eax                     ; Result
    xor edx, edx                    ; Especially to clear the 32-bit-part of EDX

    .loop:
    mov dl, byte [esi]              ; Read digit
    cmp dl, 10                      ; End of string (SYS_READ - in certain cases not existent)?
    je .finish                      ; Yes: done
    lea eax, [eax*4+eax]            ; EAX = 5 * EAX ...
    add eax, eax                    ;   ... and EAX = 2 * EAX results in EAX = EAX * 10
    add esi, 1                      ; Increment pointer to string
    and dl, 0x0F                    ; Eliminate ASCII part of digit
    add eax, edx                    ; Add digit to result
    jmp .loop                       ; Next character

    .finish:
    ret                             ; Result: Converted unsigned integer in EAX

eax2dec:                            ; Arg EDI: Pointer to string that gets ASCII-characters
    mov ebx, 10                     ; Divisor
    xor ecx, ecx                    ; CX=0 (number of digits)

    .first_loop:
    xor edx, edx                    ; Attention: DIV applies also DX!
    div ebx                         ; DX:AX / BX = AX remainder: DX
    push dx                         ; LIFO
    inc cl                          ; Increment number of digits
    test eax, eax                   ; AX = 0?
    jnz .first_loop                 ; No: once more

    mov ebx, ecx                    ; Save strlen

    .second_loop:
    pop ax                          ; Get back pushed digit
    or al, 00110000b                ; AL to ASCII
    mov byte [edi], al              ; Save AL
    inc edi                         ; DI points to next character in string DECIMAL
    loop .second_loop               ; Until there are no digits left

    mov byte [edi], 0               ; End-of-string delimiter (ASCIZ)
    mov eax, ebx                    ; Return strlen in EAX
    ret

getMenuOption:
    mov eax, 4
    mov ebx, 1
    mov ecx, menu1
    mov edx, menu1Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu2
    mov edx, menu2Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu3
    mov edx, menu3Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu4
    mov edx, menu4Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu5
    mov edx, menu5Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu6
    mov edx, menu6Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, menu7
    mov edx, menu7Len
    int 80h

    mov eax, 3
    mov ebx, 0
    mov ecx, choice
    mov edx, 1
    int 80h

    ret

Addition:
    xor edx, edx
    mov eax, [uNum1]
    mov ebx, [uNum2]
    add eax, ebx
    mov [uRes], eax

    call printResult

    jmp _start

Multiply:
    xor edx, edx
    mov eax, [uNum1]
    mov ebx, [uNum2]
    mul ebx
    mov [uRes], eax

    call printResult

    jmp _start

Divide:
    xor edx, edx
    mov eax, [uNum1]
    mov ebx, [uNum2]
    div ebx
    mov [uRes], eax

    call printResult

    jmp _start

surprise:
    jmp _start

printResult:
    mov eax, [uRes]
    mov edi, Result
    call eax2dec
    mov [ResultLen], eax

    mov eax, 4
    mov ebx, 1
    mov ecx, prompt3
    mov edx, prompt3Len
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, Result
    mov edx, [ResultLen]
    int 80h

    mov eax, 4
    mov ebx, 1
    mov ecx, linefeed
    mov edx, 1
    int 80h

    ret

exit:
    mov eax, 1
    xor ebx, ebx
    int 80h

I would really appreciate someone pointing out what's wrong with it as I am very new to this programming language.

Edit: Here's what happens when I run the code.

./menu
Please enter the first number: 3
Please enter the second number: 5
Please chose what to do with the numbers:
1. Add
2. Multiply
3. Divide
4. Nothing, exit.
5. Surprise me
Selection: 1

Your result is: 8
Please enter the first number: Please enter the second number: 3
Please chose what to do with the numbers:
1. Add
2. Multiply
3. Divide
4. Nothing, exit.
5. Surprise me
Selection: 1

Your result is: 3
Please enter the first number: Please enter the second number: 3
Please chose what to do with the numbers:
1. Add
2. Multiply
3. Divide
4. Nothing, exit.
5. Surprise me
Selection: 1

Your result is: 3
Please enter the first number: Please enter the second number: ^C

So right after going through one jmp back to _start it skips one input, but if I take the "getMenuOption" out and just make it do one operation every time it works fine.


Solution

  • The problem seems to be that you read only one character at the end of getMenuOption. The newline charater that you enters after your menu choice is then kept in the input buffer until getNumbers is called, resulting in an automatic empty input for the first number.

    A simple solution is to read more than one character in getMenuOption.