1

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.

  • 1
    A transcript of a run of the program, exhibiting the problem you describe, would make this a much clearer question. In particular, when you say "skips one input", do you mean the first or second input? Is it just the opportunity to type something in that is skipped, or the prompt text as well? – jasonharper Jul 02 '18 at 04:15
  • 1
    Well, 2 things occur to me right off: 1) If the code to figure out which menu option they picked fails, it drops into Addition (probably not what you intended). Perhaps an unconditional `jmp` at the end of the last back to _start? 2) Is this really 64bit code (as the tag implies)? If so, you shouldn't be using `int 80`, but `syscall` (see [this](https://stackoverflow.com/q/46087730/2189500)). Other than that, the details jason has requested would make this much clearer. – David Wohlferd Jul 02 '18 at 05:14
  • @jasonharper: you can use `[mcve]` in a comment, it expands to [mcve]. And yes, agreed that the description of how it fails is not clear, so it's not a good MCVE. (@Robert: It's also nowhere near minimal: step 1 in debugging a problem is to *use a debugger and single-step*) . If that doesn't work, step 2 is cutting your code down until it's as small as possible while still having the behaviour you don't understand. Often this will clue you in to the bug when you see which line or statement was relevant.) – Peter Cordes Jul 02 '18 at 06:03
  • You could also use `strace` to see what system calls your code is making, but that will show the wrong system calls if you build a 64-bit executable out of this code that uses the 32-bit ABI. ([strace decodes 32-bit system calls from 64-bit processes incorrectly](https://stackoverflow.com/questions/2500362/running-32-bit-assembly-code-on-a-64-bit-linux-64-bit-processor-explain-the/46020177#46020177), which is one of several reasons not to use `int 80h` in x86-64 code. But given that all your addressing modes are using 32-bit regs, it looks like you should build this as 32-bit.) – Peter Cordes Jul 02 '18 at 06:06
  • Edit that comment into the question, e.g. in a code block. You can copy/paste from your terminal. – Peter Cordes Jul 02 '18 at 10:55
  • 1
    Try one of these solutions: https://stackoverflow.com/a/23040860/3512216 – rkhb Jul 02 '18 at 12:18

1 Answers1

2

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.

Terje D.
  • 6,250
  • 1
  • 22
  • 30