0

This procedure waits for a single keystroke from user and returns to caller with response in RAX, or more specifically AL, with an optional prompting string in RDI. Primarily the algorithms scope is expecting 0-9, A-Z for menu selections lets say and Y or N as input.

  Ask:  enter   termios.size * 2, 0
        push    r15
        push    rbx
        or      edi, edi
        jz      $ + 7
        call    Show
        lea     rdi, [rbp-termios.size*2]
        mov     esi, TCGETS
        call    TermAttr
        push    rdi
        push    rsi

        mov     ecx, eax
        mov      cl, 6
        mov     rsi, rdi
        lea     rdi, [rbp-termios.size]
        mov     rbx, rdi
        rep     movsq

        and     byte [rbx+termios.c_lflag], ~(ICANON | ECHO | ISIG) 
        mov     word [rbx+termios.c_cc+VTIME], 0x100
        mov     rdi, rsi
        pop     rsi
        mov      si, TCSETSF
        push    rsi
        call    TermAttr

At this point, console is set to noncanonical mode, waiting for a single keystore from user.

        xor     eax, eax
        mov     edi, eax
        mov     edx, eax
        inc      dl
        push    byte 0
        mov     rsi, rsp 
        syscall

RSP points to the 8 byte buffer filled with nulls created by push byte 0 and in most cases, this buffer will have but one byte of input. Instead of returning with buffer flushed I want RAX to return with all input. For example F12 would return 7E34321B.

        pop     r15
        pop     rsi
        pop     rdi
        call    TermAttr         ; STDIN is flushed here
        pop     rbx
        mov     rax, r15
        pop     r15
        leave
        ret

To my understanding, I have two options;

  • Polling read MIN = 0; VTIME = 0
  • Read with interbyte timeout MIN = 0; VTIME > 0

I believe polling is the most viable option but have a hard time interpreting;

read(2) returns immediately with the lesser of the number of bytes availiable, or the number of bytes requested.

If no data is availiable, read(2) returns 0.

Is my thinking correct that if F12 is pressed, which I would know something other than 7FH - 20H was pressed by ESC in AL, that the next read would return 3 and then I could do whatever to read the remaining characters?

Shift_Left
  • 1,208
  • 8
  • 17
  • Can you please de-obfuscate your code? Or at least write down that you're calling `read(0, buf, 1)`? It's unnecessarily hard to follow your code. At first I thought you were passing a count of zero (which makes read() a no-op), but then I realized that you were setting rdx in two instructions, using 4 bytes. If you want to save code size, at least do it right and use `lea edx, [rax+1]` (3 bytes), since you already have a zeroed rax available. – Peter Cordes Sep 20 '16 at 07:56
  • 2
    I wouldn't poll, as it's terribly inefficient. I'd use VMIN = 1; VTIME = 0 when in the initial state of reading characters and then VMIN = 1; VTIME > 0 when in the state of possibly reading subsequent function key bytes. I also write it in C or C++ since the compiler can generate much better code than what you've written. – Ross Ridge Sep 20 '16 at 08:07
  • 1
    `an optional prompting string in RDI`: not after you truncate RDI down to 32 bits by using `or edi, edi` [instead of `test edi,edi`](https://stackoverflow.com/a/33724806/224132) (assuming that the low 32b of the pointer is non-zero). – Peter Cordes Sep 20 '16 at 08:23
  • Have you considering using `poll(2)` or `select(2)` to wait for input to become available, with a timeout? Waiting for a second or two before returning just the ESC would be consistent with what I've seen in text-terminal programs, where pressing ESC and no further characters has a delay before doing anything. – Peter Cordes Sep 20 '16 at 08:24
  • **[This article](http://unixwiz.net/techtips/termios-vmin-vtime.html) directly answers your question of when does `read()` return.** It explains using VMIN/VTIME, and how it's useful for allowing read() calls with count>1 while still being able to tell the difference between the user pressing ESC and the user pressing F12 or up-arrow on a tty. Note that your question is really a POSIX termios/tty question, and not at all specific to x86-64 assembly or even Linux, but I assume you knew that. You, I, or someone else could summarize key points into an answer if that's what you're looking for – Peter Cordes Sep 20 '16 at 08:51
  • @RossRidge: _Read with timeout_ I had considered that, but the link Peter Cordes provided I think will help a lot. One of the things I've found very common on this site and others for that matter is that comments tend to go off on tangents not related to the question. I'm almost inclined to ask for an example how compiled code is better (_which that term is entirely subjective at best_), but that really doesn't have anything to do with the question at hand and doesn't provide any meaningful context to the question. – Shift_Left Sep 20 '16 at 12:29
  • @PeterCordes: I was debating whether I should include code or not as while developing, I don't waste a lot of time documenting until the code functions as intended. That being said, I will include enough detail in future posts to eliminate respondents need to unnecessarily digest theory when comments would eliminate that. I would agree about x86_64 and will edit post accordingly, but POSIX & Linux are not mutually exclusive. The link you provided will be very helpful. – Shift_Left Sep 20 '16 at 12:45
  • Comments are meant to improve the question, and on that basis mentioning the how a C/C++ compiler can produce objectively better code is actually the most meaningful part of my comment. As Peter Cordes said your question is really a POSIX termios question. Your example assembly code only obfuscates your question, making it much less useful. My comment assumed the fact that writing your code in C/C++ would make it much more easier to understand was obvious and so only pointed out how writing your code in assembly instead hadn't really gotten you anything. – Ross Ridge Sep 20 '16 at 16:37
  • @Shift_Left: I meant this question isn't specific to the Linux asm syscall API. This question applies equally to Linux and to every other POSIX-compatible OS. And BTW: "how compiled code is better (which that term is entirely subjective at best)": There are objective ways to compare code: machine-code size (in bytes), and performance on any CPU you want to run it on (e.g. current-generation Intel Sandybridge-family). There are many factors to perf, but static analysis for number of uops (uop-cache footprint), throughput, and latency are possible. There are reasons this is bad style. – Peter Cordes Sep 20 '16 at 17:35
  • @RossRidge Yes, I do understand your point now. Although my intended forte is assembly, I will make a concerted effort from this point to at least annotate my code with C equivalents. – Shift_Left Sep 20 '16 at 17:41
  • 1
    It's totally fine if you want to ultimately use the answer to write something in asm. But the best way to express the question would be with simple C, and not to actually show any of your asm implementation. There's no need to say any more than "I call `read(0,buf,1)` with the tty in raw mode. How can I tell when the user pressed F12?", then go on to ask about VMIN and VTIME. – Peter Cordes Sep 20 '16 at 18:05
  • @PeterCordes There is no question my methodologies have the potential of crashing, but in the top down paradigm, get it working first and then revisit and optimize accordingly. This algorithm is an example of one that will not see any effort, as the most time consuming part is waiting for operator response. At best I would think it could be 1/3 sec, therefore 8.0e8 uops have passed waiting. So to optimize for speed is mute, but size is always a consideration. In any event, yours and other comments are definite insights into making future post more succinct and understandable. – Shift_Left Sep 20 '16 at 18:27
  • I didn't have room in one comment to finish clarifying: patterns of instruction usage that might look unusual, but have a speed advantage, are common and easy to read for veteran asm coders. (e.g. the xor-zeroing idiom instead of `mov eax, 0`.) Weird ways of doing things that *don't* have this advantage, and thus aren't seen in well-tuned code, are unfamiliar and hard to read. So it's not the performance of this routine that's the issue (it's all insignificant compared to `syscall`), it's that your coding style is hard to read because it's not good for performance. – Peter Cordes Sep 20 '16 at 18:45

0 Answers0