0

How do I test to ensure only an integer is entered and ensure length of input is 5 bytes or less in the following code?

I am trying to understand how to properly control input so that the input beyond 5 bytes is not outputted to the terminal upon exiting of the program.

In addition, how would I test to ensure only a string is entered and finally in the last scenario, only a double is entered?

*** Updated code based on x82 and Peter C's guidance. I did some C disas and was able to amend my original code below. It still has some flaws but you are both a great deal of help! I am just stuck on when more than 5 integer bytes are entered it wont re-prompt as it does when I enter in a character data as it continues to dump extra bytes data to tty.

SECTION .data                   ; initialized data section
promptInput db 'Enter Number: ', 0
lenPromptInput equ $ - promptInput
displayInput db 'Data Entered: ', 0
lenDisplayInput equ $ - lenDisplayInput

SECTION .bss                ; uninitialized data section
number resb 1024            ; allocate 1024 bytes for number variable

SECTION .text               ; code section
global _start               ; linker entry point

_start:
nop                         ; used for debugging

Read:
mov eax, 4                  ; specify sys_write call
mov ebx, 1                  ; specify stdout file descriptor
mov ecx, promptInput        ; display promptInput
mov edx, lenPromptInput     ; length of promptInput
int 0x80                    ; call sys_write

mov eax, 3                  ; specify sys_read call
mov ebx, 0                  ; specify stdin file descriptor
mov ecx, number             ; pass address of the buffer to read to
mov edx, 1024               ; specify sys_read to read 1024 bytes stdin
int 0x80                    ; call sys_read

cmp eax, 0                  ; examine sys_read return value in eax
je Exit                     ; je if end of file

cmp byte [number], 0x30     ; test input against numeric 0
jb Read                     ; jb if below 0 in ASCII chart
cmp byte [number], 0x39     ; test input against numeric 9
ja Read                     ; ja if above 9 in ASCII chart

Write:
mov eax, 4                  ; specify sys_write call
mov ebx, 1                  ; specify stdout file descriptor
mov ecx, displayInput       ; display displayInput
mov edx, lenDisplayInput    ; length of displayInput
int 0x80                    ; call sys_write    

mov eax, 4                  ; specify sys_write call
mov ebx, 1                  ; specify stdout file descriptor
mov ecx, number             ; pass address of the number to write
mov edx, 5                  ; pass number of numbers to write
int 0x80                    ; call sys_write

Exit:
mov eax, 1                  ; specific sys_exit call
mov ebx, 0                  ; return code 0 to OS
int 0x80                    ; call sys_exit
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Search for ascii table from GOOGLE and check ranges (and hexadecimal values) for characters, numbers etc . As a little tip , you would check if the characters in inputMsg falls within the range [a-z]-[A-Z] for the string check.For the length check, you should be looking for the null terminator. Double check can be a little harder but I don't think it is impossible . Then you can fine tune your algorithm for your specific needs. – x82 Jul 17 '16 at 17:44
  • Thank you. Here is my updated code based on your help. I am now getting, 'Can't find valid values for all labels after 1000 passes, possible causes recursive EQUs, macro abuse'. It will not compile. – Kevin M. Thomas Jul 17 '16 at 19:53
  • check out this line lenDisplayInput equ $ - lenDisplayInput : Has to be lenDisplayInput equ $ - displayInput – x82 Jul 17 '16 at 20:29
  • Thanks now it compiles however the code doesn't work at all as expected. I am trying to understand how to properly control input so that the input beyond 5 bytes is not outputted to the terminal upon exiting of the program and how would I test to ensure only a string is entered and finally in the last scenario, only a double is entered? – Kevin M. Thomas Jul 17 '16 at 20:35
  • Why do you have to read the characters one by one ? I think it may be better you allocate a buffer for input about 30 character wide and allow the user to enter all at once, then check the buffer from start to finish if it's an integer, double or character string and print an error message if it does not match any of the formats you expect. Anyway, may be not working exactly but at least it's working. It never works in first run and you would not learn anything otherwise. Just try harder :) – x82 Jul 17 '16 at 20:51
  • Actually i could not understand what you mean by "Input beyond 5 bytes is not outputted to the terminal upon exiting of the program". Force quit after 5 bytes ? – x82 Jul 17 '16 at 21:06
  • I meant that if more than 5 bytes of data was entered, a cmp would occur at the end of the input and it would cycle through a subroutine if it is in fact greater than 5 bytes until 5 bytes are entered. I appreciate what you said about trying harder but I am stuck. – Kevin M. Thomas Jul 17 '16 at 21:09
  • re: last edit (which I rolled back because this question has evolved into too many different questions already. SO questions are supposed to be single questions that are useful to other people in the future, not conversations while people help you debug something). Anyway, you put the pointer-increment outside the loop (after the `jb Buffer`), but the initialization (`mov esi, number`) is inside the loop. Single-step your code in gdb. Look at other examples of a loop over an array. Think about how the CPU will execute your program step-by-step. – Peter Cordes Jul 18 '16 at 02:23

2 Answers2

0

I'm just going to answer the POSIX systems programming part of the question, and leave it up to you to make the right system calls once you know what you want your program to do. Use gdb to debug it (see the bottom of the tag wiki for debug tips), and strace to trace system calls.

You might want to write your program in C instead of trying to learn asm and the Unix system call API at the same time. Write something in C to test the idea, and then implement it in asm. (Then you can look at the compiler output when you get stuck, to see how the compiler did things. As long as you carefully read and understand how the compiler-generated code works, you're still learning. I'd suggest compiling with -O2 or -O3 as a starting point for an asm implementation. At least -O1, definitely not -O0.).


I am trying to understand how to properly control input so that the input beyond 5 bytes is not outputted to the terminal upon exiting of the program.

This is just a POSIX semantics issue, nothing to do with asm. It would be the same if you were doing systems programming in C, calling the read(2) system call. You're calling it in asm with mov eax,3 / int 0x80, instead of calling the glibc wrapper like C compiler output would, but it's the same system call.

If there is unread data on the terminal (tty) when your program exits, the shell will read it when it checks for input.

In an interactive shell running on a tty, programs you run (like ./a.out or /bin/cat) have their stdin connected to the same tty device that the shell takes interactive input from. So unread data on your program's stdin is the same thing as unread data that the shell will see.

Things are different if you redirected your program's input from a file. (./a.out < my_file.txt). Then your program won't start with an already-open file descriptor for the tty. It could still open("/dev/tty") (which is a "magic" symlink that always refers to the controlling tty) and vacuum up anything that was typed while it was running.


ensure only an integer is entered and ensure length of input is 5 bytes or less in the following code?

You can't control what your input will be. You can detect input you don't like, and print an error message or anything else you want to do.

If you want input characters to stop echoing to the screen after 5 bytes, you'd need to put the tty into raw mode (instead of the default line-buffered "cooked" mode) and either do the echo manually, or disable echo after 5 bytes. (The latter wouldn't be reliable, though. There'd be a race condition between disabling echo and the user typing a 6th byte (e.g. as part of a paste).


RE: edit

I am just stuck on when more than 5 integer bytes are entered it wont re-prompt as it does when I enter in a character data as it continues to dump extra bytes data to tty.

You broke your program, because the logic is still designed around re-read()ing a character if you don't like the digit you read. But your read call reads up to 5 bytes.

The normal thing to do is one big read and then parse the whole line by looping over the bytes in the buffer. So use a big buffer (like 1024 bytes) in the .bss section, and make a read system call.

Don't make another read system call unless you want to prompt the user to enter another line of text.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thank you both! I updated my code and have made progress based on all of your recommendations It works well except when more than 5 integer bytes are entered it wont re-prompt as it does when I enter in a character data as it continues to dump extra bytes data to tty. – Kevin M. Thomas Jul 17 '16 at 23:28
  • @KevinM.Thomas: sounds like it exits when you don't want it to. BTW, if you're still reading one-byte-at-a-time, you should change that like was suggested in comments. `read()` on a tty will return on newline or EOF (`ctrl-d`) on the tty, even if that means it returns before filling the buffer. (Check the return value to see how many chars you read.) – Peter Cordes Jul 18 '16 at 00:08
  • @KevinM.Thomas: BTW, you can mark this answer as "accepted" (checkbox under the vote arrows) if you think it answered the main question, or enough of it. Don't edit the question so much that it turns into a new question. – Peter Cordes Jul 18 '16 at 00:09
  • Hi @PeterCordes I am unsure how to use read() as I use sys_read currently. With the above code can you suggest how I might implement read() to remedy this? – Kevin M. Thomas Jul 18 '16 at 00:11
  • @KevinM.Thomas: The sys_read system call is the same thing as the POSIX C `read(2)` function that I'm talking about. (Technically, the C function calls a wrapper in glibc, rather than compiling to an inline system call, as you probably noticed if you looked at `gcc -S -fverbose-asm -O3` output. If you disassemble libc, you'll see that `__read()` basically just does a sys_read and sets errno if needed. `read` is a weak alias for `__read`.) The man pages for system calls document any differences between the raw syscall and the libc wrapper, if any. – Peter Cordes Jul 18 '16 at 00:14
  • @KevinM.Thomas: What do you mean "implement `read()`"? You're already calling it with `mov eax, 3` / `int 0x80`. – Peter Cordes Jul 18 '16 at 00:16
  • That's what I thought but I am unsure to update the code above to implement your fix. – Kevin M. Thomas Jul 18 '16 at 00:17
  • @KevinM.Thomas: Updated my answer in response to your new question in the edit. You are/were misunderstanding how read works, or how to use it, I think. – Peter Cordes Jul 18 '16 at 00:24
  • it worked!! I updated the code above. It no longer dumps extra bytes to the tty! The only small very small issue is when over 5 bytes are entered it skips the line return on the output and if i enter in say '343k' it accepts it if I enter 'e3434' or the like it works properly. – Kevin M. Thomas Jul 18 '16 at 00:38
  • @KevinM.Thomas: You're probably still only looking at the first character of the buffer, instead of looping over the bytes and processing each byte separately. Think of your program as getting a whole line as a string, and processing that. – Peter Cordes Jul 18 '16 at 01:20
  • yes you are correct how do I do that? right now I am checking against cmp byte [number] etc and i resb 1024. Can you give me a code suggestion specifically updating the code above to make this so? – Kevin M. Thomas Jul 18 '16 at 01:32
  • Get a pointer to the buffer into a register, like `mov esi, number`. Then `cmp byte [esi], '0' / jb`. Move on to the next character with `inc esi`. (Increment the pointer by one, because that's the size in bytes of each element of the string). Google or SO search should turn up results for "x86 assembly loop over an array". This is an *extremely* common thing in assembly language; see also the [x86 tag wiki](http://stackoverflow.com/tags/x86/info). You can also write code in C and look at compiler output for hints. (http://gcc.godbolt.org/ is handy for that) – Peter Cordes Jul 18 '16 at 01:43
  • unable to properly implement. I updated the code ABOVE with your suggestion and it does not work. I researched like you suggested and attempted several of the methods with no success such as: mov eax, 0 mov ecx, 0 loop_start: cmp ecx, ARRAY_LENGTH jge loop_end add eax, BYTE PTR myArray[ecx] add ecx, 1 jmp loop_start loop_end: – Kevin M. Thomas Jul 18 '16 at 02:13
  • @KevinM.Thomas: I rolled back your edit because it changed this question into a different question. Let's let this question be about how `read` and TTYs work, and you can ask for debugging help in a different question. – Peter Cordes Jul 18 '16 at 02:19
0

(Since you accepted this answer, I'll point out that the actual answer to this question about using read on TTYs is my other answer on this question.)

Here's an answer to your low-quality followup question which I was about to post when you deleted it.

Note that I said "you can ask for debugging help in a new question", not that you should ask 3 different questions in one, and re-post your whole code barely changed with no serious attempt at solving your own problem. It's still up to you to make the new question a good question.

I probably wouldn't have answered it if I hadn't sort of led to you posting it in the first place. Welcome to StackOverflow, I'm being generous since you're new and don't know what's a good question yet.


The usual term for the characters '0' through '9' is "digit", not "integer". It's much more specific.

ensure only integers are inputted in the buffer

You can't. You have to decide what you want to do if you detect such input.

Need help creating an array to loop through

Your buffer is an array of bytes.

You can loop over it with something like

    # eax has the return value from the read system call, which you've checked is strictly greater than 0
    mov    esi, number        ; start pointer

scan_buffer:
    movzx  edx, byte [esi]
        # do something with the character in dl / edx
        ...

    inc    esi                ; move to the next character

    dec    eax
    jnz   scan_buffer         ; loop n times, where n = number of characters read by the system call.

ensure characters over the 1024 buffer do not send data to the tty

If you're worried that 1024 isn't necessarily big enough for this toy program, then use select(2) or poll(2) to check if there's more input to be read without blocking if there isn't.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thank you @petercordes I updated the code based on your suggestion and spent a day taking it through GDB however looking for a final bit of help to get this working. – Kevin M. Thomas Jul 22 '16 at 01:26
  • @KevinM.Thomas: Your question still doesn't say what you *want* it to do. And it's full of useless comments like `pass address of number to ecx`, which we can already see from the `mov ecx, number`. A more useful comment would be that this is the `*buf` arg to `read(2)`. – Peter Cordes Jul 22 '16 at 01:34
  • Question: How do I ensure only an integer value is entered in order to jmp to exit so that when a non-integer value up to 1024 bytes, the program will prompt the input to re-prompt 'Enter Number' question? – Kevin M. Thomas Jul 22 '16 at 01:54
  • @KevinM.Thomas: you keep saying "ensure" something about the input. At least you've finally described what you want to happen when you see valid input vs. when you see invalid input. You should edit your question with that instead of just leaving comments. – Peter Cordes Jul 22 '16 at 01:57