4

Learning NASM Assembly, I am trying to make a program that reads two one-digit number inputs.

I have two variables declared in the .bss:

num1 resb 1
num2 resb 1

Then, I ask the user to write the numbers like this:

; Get number 1
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,1
int 0x80

; Get number 2
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,1
int 0x80

Since I am only interested in one-digit number inputs, I set EDX to 1. This way, whatever the user types, only the first character will be stored in my variable (right?).

The problem is that everything that follows after that first character will be used for the future reads. If you type 5 and then press ENTER, the ASCII code 53 will be stored in num1 just fine, but the line break you generated by pressing ENTER will carry on to the next read instruction, which will be stored in num2. Clearly that's not what I was intending. I want the user to type a number, press ENTER, type another number, and press ENTER.

I am not entirely sure how to work around this in the simplest way possible.

The dumbest idea was to put a "dummy" read instruction between num1 and num2, which will capture the line break (and do nothing with it). This is obviously not good.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Saturn
  • 17,888
  • 49
  • 145
  • 271
  • See [this](http://stackoverflow.com/questions/3305005/how-do-i-read-single-character-input-from-keyboard-using-nasm-assembly-under-u) and [this](http://stackoverflow.com/questions/17000396/which-linux-system-calls-should-i-use-to-read-raw-characters-from-stdin) if you want unbuffered input. Otherwise you could just pad your variables (make them words, and clear the upper byte after the reads). – Michael Sep 13 '13 at 07:53
  • Although a "dummy read" doesn't sound good, that's almost what you need to do. You need a little "get number" routine which reads characters from the input and skips anything that's not a number until it sees a number and returns it. It's a fairly standard method and not dumb. It's quite simple and effective. – lurker Sep 13 '13 at 11:18
  • What did you plan to do if the user typed "a", for example, instead of a digit? Also, doesn't setting `ebx` to `1` use the standard output. You probably want `0` for standard input. – lurker Sep 13 '13 at 12:24
  • @mbratch: I see. The dummy read doesn't sound so bad now - there doesn't seem to be a trivial way to solve this anyway. As for "a", I don't plan to do anything - I'm just interested in getting one-character inputs for now. – Saturn Sep 13 '13 at 15:23

3 Answers3

3

Here's a very basic way of reading input until you get digits you want. It will skip anything but digits. This approach is fine if it provides the functionality you want. If you need different behavior depending upon other non-numeric input, then you need to specify that behavior. Then that behavior can be programmed as well.

    ; Get number 1
    mov   ECX,num1
    call  GetNumber

    ; Get number 2
    mov   ECX,num2
    call  GetNumber
    ...

GetNumber:
    pusha              ; save regs
get:
    mov   EAX,3        ; system call for reading a character
    mov   EBX,0        ; 0 is standard input
    mov   EDX,1        ; number of characters to read
    int   0x80         ; ECX has the buffer, passed into GetNumber
    cmp   byte [ecx],0x30
    jl    get          ; Retry if the byte read is < '0'
    cmp   byte [ecx],0x39
    jg    get          ; Retry if the byte read is > '9'

    ; At this point, if you want to just return an actual number,
    ; you could subtract '0' (0x30) off of the value read
    popa               ; restore regs
    ret
ecm
  • 2,583
  • 4
  • 21
  • 29
lurker
  • 56,987
  • 9
  • 69
  • 103
3

Meddling with stdin to disable I_CANON will work, but may be the "hard way". Using a two byte buffer and doing mov edx, 2 will work if the pesky user is well behaved - either clear the second byte, or just ignore it.

Sometimes the pesky user is not well behaved. Dealing with "garbage input" or other error conditions generally takes much more code than just "doing the work"! Either deal with it, or be satisfied with a program that "usually" works. The second option may be sufficient for beginners.

The pesky user might just hit "enter" without entering a number. In this case, we want to either re-prompt, or perhaps print "Sorry you didn't like my program" and exit. Or he/she might type more than one character before hitting "enter". This is potentially dangerous! If a malicious user types "1rm -rf .", you've just wiped out your entire system! Unix is powerful, and like any powerful tool can be dangerous in the hands of an unskilled user.

You might try something like (warning: untested code ahead!)...

section .bss
    num1 resb 1
    num2 resb 1
    trashbin resb 1

section .text
re_prompt:
; prompt for your number
; ...
; get the number (character representing the number!)
    mov ecx, num1
reread:
    mov edx, 1
    mov ebx, 0 ; 1 will work, but 0 is stdin
    mov eax, 3 ; sys_read
    int 0x80
    cmp byte [ecx], 10 ; linefeed
    jz got_it
    mov ecx, trashbin
    jmp reread
got_it:
    cmp byte [num1], 10 ; user entered nothing?
    jz re_prompt ; or do something intelligent
; okay, we have  a character in num1
; may want to make sure it's a valid digit
; convert character to number now?
; carry on

You may need to fiddle with that to make it work. I probably shouldn't post untested code (you can embarrass yourself that way!). "Something like that" might be easier for you than fiddling with termios. The second link Michael gave you includes the code I use for that. I'm not very happy with it (sloppy!), but it "kinda works". Either way, have fun! :)

Frank Kotler
  • 3,079
  • 2
  • 14
  • 9
  • Related: Related: [Reading a single-key input on Linux (without waiting for return) using x86\_64 sys\_call](https://stackoverflow.com/q/62937150) re: TCGETS / TCSETS (for 64-bit mode). – Peter Cordes Apr 25 '22 at 10:36
0

You will have to deal with canonical disabling, raw keyboard. This is how linux manages entering console password for exampe without showing it.

The assembly to do this is nicely described here:

http://asm.sourceforge.net/articles/rawkb.html

icbytes
  • 1,831
  • 1
  • 17
  • 27
  • 1
    `stty -echo` is different from `stty -icanon`. You can have no-echo input that's still "cooked" (not raw: backspace works, input not "submitted" until EOL or EOF pressed). e.g. try it by running `cat` in one terminal, and in another use `stty -echo < /proc/$(pidof cat)/fd/0` to mess with the terminal settings of the terminal it's running on. (`stty` does terminal ioctls on its fd 0, and prints output about it to its fd 1, hence the input redirect.) – Peter Cordes Apr 25 '22 at 10:33
  • Related: [Reading a single-key input on Linux (without waiting for return) using x86\_64 sys\_call](https://stackoverflow.com/q/62937150) re: TCGETS / TCSETS (for 64-bit mode). – Peter Cordes Apr 25 '22 at 10:35