Entering a non-integer into an unsigned integer using scanf in NASM causing problems in loops

Viewed 42

Whenever I try entering a letter or symbol into my scanf for unsigned integers after previously entering an accepted number previously in the loop, it continually enters the previous number, causing the program to run indefinitely. How do I fix this? Here's an example code I wrote:

[bits 32]

global _main

extern _scanf
extern _printf

section .data
    prompt_number db "Please select a number. Enter 0 to escape. ", 0
    display_number db "You entered %u. ", 0
    number_fmt db "%u", 0
    number_input dd 0

section .bss

section .text
_main:
    push ebp
    mov ebp, esp
    and esp, 0xfffffff0

_Label:

    sub   esp, 16
    mov   dword[esp], prompt_number
    call  _printf
    add   esp, 16

    sub esp, 16
    mov dword[esp], number_fmt
    mov dword[esp+4], number_input
    call _scanf
    add esp, 16

    sub esp, 16
    mov dword[esp], display_number
    mov eax, [number_input]
    mov dword[esp+4], eax
    call _printf
    add esp, 16

    cmp dword[number_input], 0
    jne _Label

    mov   esp, ebp
    mov   eax, 1
    pop   ebp
ret 
1 Answers

This is to do with how scanf operates in these circumstances, and a solution is to port one of the suggested solutions for Good way to flush scanf buffer when invalid entry entered from C.

My understanding is that if scanf can't map the input into the format string (in this case trying to convert an arbitrary character into an unsigned integer), then it gives up but doesn't remove anything from the input buffer. Thus, in this case, subsequent calls to scanf just use what is currently in the buffer.

Below is a quick attempt at porting the first solution (using getchar to consume the buffer). I can't vouch for its quality; this is basically my first run through programming assembly:

[bits 32]

global _main

extern _getchar
extern _scanf
extern _printf

section .data
    prompt_number   db "Please select a number. Enter 0 to escape. ", 0
    display_number  db "You entered %u. ", 0
    scan_error      db "Error scanning input. ", 0
    number_fmt      db "%u", 0
    number_input    dd 0

section .bss

section .text
_main:
    push  ebp
    mov   ebp, esp
    and   esp, 0xfffffff0

_Label:

    sub   esp, 16
    mov   dword[esp], prompt_number
    call  _printf
    add   esp, 16

    sub   esp, 16
    mov   dword[esp], number_fmt
    mov   dword[esp+4], number_input
    call  _scanf
    add   esp, 16

    cmp   eax, 0
    jne    _ScanSuccess

_ScanError:
    sub   esp, 16
    mov   dword[esp], scan_error
    call  _printf
    add   esp, 16

_ScanErrorConsumeInput:
    sub   esp, 16
    call  _getchar
    add   esp, 16

    cmp   eax, -1   ; most common value indicating end-of-file
    je    _Label

    cmp   eax, 10   ; newline in ASCII
    je    _Label

    jmp   _ScanErrorConsumeInput

_ScanSuccess:
    sub   esp, 16
    mov   dword[esp], display_number
    mov   eax, [number_input]
    mov   dword[esp+4], eax
    call  _printf
    add   esp, 16

    cmp   dword[number_input], 0
    jne   _Label

_Return:
    mov   esp, ebp
    mov   eax, 1
    pop   ebp
    ret
Related