Re: read a single character from stdin



Frank Kotler wrote:

I'm trying to figure out how to read a single character from stdin
(i'm using linux & nasm).

This "works for me". It could/should be improved by using two
copies of the termios structure - "get" to one of 'em, copy it to the
other, fiddle with that and "set" to it, then "set" back to normal from
the original. This makes some assumptions about the current state of
stdin that may not always be true. Hell of a lot of system calls to get
just one key! There may be a better/easier way, but I don't know it.
I've found that returning to bash with stdin in an altered state is
*not* a good idea! :)

Does this really work for you?

struc termios
alignb 4
.c_iflag: resd 1 ; input mode flags
.c_oflag: resd 1 ; output mode flags
.c_cflag: resd 1 ; control mode flags
.c_lflag: resd 1 ; local mode flags
.c_line: resb 1 ; line discipline
.c_cc: resb 19 ; control characters
endstruc

from /usr/include/bits/termios.h:

typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;

#define NCCS 32
struct termios
{
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
};

So here termios is 4+4+4+4+1+32+4+4 = 57 byte whereas your termios is 44 byte.

sub esp, termios_size ; make a place for current kbd mode

Because you allocate only 44 byte on the stack, there should be problem when
you get the current mode.

Here a program based on your and a C (http://www.pwilson.net/kbhit.html) example
which prints ever second a '+' until a key is pressed and then echoes the pressed key:


;===========================================================================
seg32
@=$08048000
code_offset=@@
code_addr:
;--------------------------- ELF header -----------------------------------
00000000: 08048000: 464c457f 00010101
00000008: 08048008: 00000000 00000000
00000010: 08048010: 00030002 00000001
00000018: 08048018: 08048074 00000034
00000020: 08048020: 00000000 00000000
00000028: 08048028: 00200034 00000002
00000030: 08048030: 00000000 dc.l $464c457f,$00010101,0,0,$00030002,1,main,$34,0,0,$00200034,2,0
00000034: 08048034: 00000001 00000000
0000003c: 0804803c: 08048000 08048000
00000044: 08048044: 000001e3 000001e3
0000004c: 0804804c: 00000005 00001000 dc.l 1,code_offset,code_addr,code_addr,code_filez,code_memsz,5,4096
00000054: 08048054: 00000001 000001e4
0000005c: 0804805c: 080491e4 080491e4
00000064: 08048064: 00000018 0000008e
0000006c: 0804806c: 00000006 00001000 dc.l 1,data_offset,data_addr,data_addr,data_filez,data_memsz,6,4096
;--------------------------- code ------------------------------------------

main:
; ioctl (/usr/include/sys/ioctl.h)
00000074: 08048074: bb 00000000 move.l #0,r3 ; file descriptor (0 = STDIN)
00000079: 08048079: b9 00005401 move.l #$5401,r2 ; $5401 = TCGETS (/usr/include/asm/ioctl.h)
0000007e: 0804807e: ba 08049200 move.l #termios,r1 ; pointer to termios structure
00000083: 08048083: b8 00000036 move.l #54,r0 ; ioctl (/usr/include/asm/unistd.h)
00000088: 08048088: cd 80 trap #$80
0000008a: 0804808a: 85 c0 tst.l r0,r0
0000008c: 0804808c: 0f 88 000000ad bmi.l error

00000092: 08048092: be 08049200 move.l #termios,r5
00000097: 08048097: bf 08049239 move.l #termios_save,r6
0000009c: 0804809c: b9 00000039 move.l #termios_size,r2
000000a1: 080480a1: f3 a4 rep_r2 move.b (r5)+-,(r6)+-{s1}

; (/usr/include/bits/termios.h)
000000a3: 080480a3: 81 25 0804920c
000000a9: 080480a9: fffffff5 and.l #~$00000a,termios+12 ; ICANON|ECHO -> local
; 2 | 8

; (/usr/include/termios.h)
000000ad: 080480ad: c6 05 08049216 00 move.b #0,termios+17+5 ; control_character[VTIME]
000000b4: 080480b4: c6 05 08049217 01 move.b #1,termios+17+6 ; control_character[VMIN]

; ioctl (/usr/include/sys/ioctl.h)
000000bb: 080480bb: bb 00000000 move.l #0,r3 ; file descriptor (0 = STDIN)
000000c0: 080480c0: b9 00005402 move.l #$5402,r2 ; $5402 = TCSETS (/usr/include/asm/ioctl.h)
000000c5: 080480c5: ba 08049200 move.l #termios,r1 ; pointer to termios structure
000000ca: 080480ca: b8 00000036 move.l #54,r0 ; ioctl (/usr/include/asm/unistd.h)
000000cf: 080480cf: cd 80 trap #$80
000000d1: 080480d1: 85 c0 tst.l r0,r0
000000d3: 080480d3: 78 6a bmi.b error

000000d5: 080480d5: bf 0000000a move.l #10,r6
000000da: 080480da: e8 0000006c _10: bsr.l getcx
000000df: 080480df: 3d ffffffff cmp.l #-1,r0 ; EOF
000000e4: 080480e4: 74 43 beq.b _20
000000e6: 080480e6: 3d fffffffe cmp.l #-2,r0 ; no char available
000000eb: 080480eb: 75 22 bne.b _30

000000ed: 080480ed: bb 080491e4 move.l #time1,r3 ; time to sleep (1 ms)
000000f2: 080480f2: b9 080491ec move.l #time2,r2 ; remaining time to sleep
000000f7: 080480f7: b8 000000a2 move.l #162,r0 ; nanosleep 1 us (/usr/include/asm/unistd.h)
000000fc: 080480fc: cd 80 trap #$80

000000fe: 080480fe: 4f dec.l r6
000000ff: 080480ff: 75 d9 bne.b _10
00000101: 08048101: bf 0000000a move.l #10,r6
00000106: 08048106: b0 2b move.b #'+',r0 ; print a '+' every second
00000108: 08048108: e8 000000ab bsr.l putc
0000010d: 0804810d: eb cb br.b _10

0000010f: 0804810f: 50 _30: move.l r0,-(sp)
00000110: 08048110: b0 0a move.b #10,r0
00000112: 08048112: e8 000000a1 bsr.l putc
00000117: 08048117: e8 0000009c bsr.l putc
0000011c: 0804811c: 58 move.l (sp)+,r0
0000011d: 0804811d: e8 00000096 bsr.l putc
00000122: 08048122: b0 0a move.b #10,r0
00000124: 08048124: e8 0000008f bsr.l putc

_20: ; reset to cooked mode
; ioctl (/usr/include/sys/ioctl.h)
00000129: 08048129: bb 00000000 move.l #0,r3 ; file descriptor (1 = STDIN)
0000012e: 0804812e: b9 00005402 move.l #$5402,r2 ; $5401 = TCSETS (/usr/include/asm/ioctl.h)
00000133: 08048133: ba 08049239 move.l #termios_save,r1; pointer to termios structure
00000138: 08048138: b8 00000036 move.l #54,r0 ; ioctl (/usr/include/asm/unistd.h)
0000013d: 0804813d: cd 80 trap #$80
;tst.l r0,r0
;bmi.b error

0000013f: 0804813f: bb 00000000 error: move.l #0,r3 ; return code
00000144: 08048144: b8 00000001 move.l #1,r0 ; exit
00000149: 08048149: cd 80 trap #$80


0000014b: 0804814b: 60 getcx: movem.l r0-r7,-(sp)
0000014c: 0804814c: bb 080491f4 move.l #pollfd,r3 ; pointer to an array of polling request
; structures
00000151: 08048151: b9 00000001 move.l #1,r2 ; number of structures in the array
00000156: 08048156: ba 00000000 move.l #0,r1 ; timeout in milliseconds
0000015b: 0804815b: b8 000000a8 move.l #168,r0 ; poll (/usr/include/asm/unistd.h)
00000160: 08048160: cd 80 trap #$80
00000162: 08048162: 09 c0 or.l r0,r0
00000164: 08048164: 78 13 bmi.b _10
00000166: 08048166: 75 08 bne.b _20
00000168: 08048168: 61 90 movem.l (sp)+,r0-r7
0000016a: 0804816a: b8 fffffffe move.l #-2,r0
0000016f: 0804816f: c3 rts.l
00000170: 08048170: f6 05 080491f8 01 _20: tst.b #1,pollfd+4 ; something to read
00000177: 08048177: 75 0b bne.b .getc
00000179: 08048179: 83 cb ff _10: orq.l #-1,r3 ; return code
0000017c: 0804817c: b8 00000001 move.l #1,r0 ; exit
00000181: 08048181: cd 80 trap #$80

00000183: 08048183: 60 getc: movem.l r0-r7,-(sp)
00000184: 08048184: bb 00000000 .getc: move.l #0,r3 ; stdin
00000189: 08048189: b9 080491fc move.l #buf,r2
0000018e: 0804818e: ba 00000001 move.l #1,r1 ; 1 byte
00000193: 08048193: b8 00000003 move.l #3,r0 ; read
00000198: 08048198: cd 80 trap #$80
0000019a: 0804819a: 85 c0 tst.l r0,r0
0000019c: 0804819c: 78 10 bmi.b _10
0000019e: 0804819e: 61 90 movem.l (sp)+,r0-r7
000001a0: 080481a0: 74 08 beq.b _20
000001a2: 080481a2: 0f b6 05 080491fc movu.bl buf,r0
000001a9: 080481a9: c3 rts.l
000001aa: 080481aa: 83 c8 ff _20: orq.l #-1,r0
000001ad: 080481ad: c3 rts.l
000001ae: 080481ae: 83 cb ff _10: orq.l #-1,r3 ; return code
000001b1: 080481b1: b8 00000001 move.l #1,r0 ; exit
000001b6: 080481b6: cd 80 trap #$80


000001b8: 080481b8: 60 putc: movem.l r0-r7,-(sp)
000001b9: 080481b9: bb 00000001 move.l #1,r3 ; stdout
000001be: 080481be: b9 080491fc move.l #buf,r2
000001c3: 080481c3: 88 01 move.b r0,(r2)
000001c5: 080481c5: ba 00000001 move.l #1,r1 ; 1 byte
000001ca: 080481ca: b8 00000004 move.l #4,r0 ; write
000001cf: 080481cf: cd 80 trap #$80
000001d1: 080481d1: 83 f8 01 cmpq.l #1,r0
000001d4: 080481d4: 75 03 bne.b _10
000001d6: 080481d6: 61 90 movem.l (sp)+,r0-r7
000001d8: 080481d8: c3 rts.l
000001d9: 080481d9: 83 cb ff _10: orq.l #-1,r3 ; return code
000001dc: 080481dc: b8 00000001 move.l #1,r0 ; exit
000001e1: 080481e1: cd 80 trap #$80

;--------------------------- constant data ---------------------------------


;---------------------------------------------------------------------------

code_filez=@@-code_offset
code_memsz= @-code_addr
000001e3: 080481e3: 00 even 4
@=(@+4095)/4096*4096+(@@\4096)
data_offset=@@
data_addr:

;--------------------------- initialized data ------------------------------

000001e4: 080491e4: 00000000 time1: dc.l 0 ; seconds
000001e8: 080491e8: 05f5e100 dc.l 100000000 ; nanoseconds maust be (<=999999999)
000001ec: 080491ec: 00000000 time2: dc.l 0
000001f0: 080491f0: 00000000 dc.l 0

; struct pollfd (/usr/include/sys/poll.h)
000001f4: 080491f4: 00000000 pollfd: dc.l 0 ; fd (file descriptor): stdin
000001f8: 080491f8: 0001 dc.w 1 ; events (requested events):
; 1=POLLIN (/usr/include/bits/poll.h)
000001fa: 080491fa: 0000 dc.w 0 ; revents (returned events)


;--------------------------- uninitialized data ----------------------------

buf: blk.b 4

; struct termios (/usr/include/bits/termios.h)
termios:blk.l 1 ; input mode flags
blk.l 1 ; output mode flags
blk.l 1 ; control mode flags
blk.l 1 ; local mode flags
blk.b 1 ; line discipline
blk.b 32 ; control charcters
blk.l 1 ; input speed
blk.l 1 ; output speed
termios_size=@-termios

termios_save:
blk.b termios_size


;---------------------------------------------------------------------------

data_filez=@@-data_offset
data_memsz= @-data_addr

;===========================================================================




The binary:

#!/bin/bash
name=kbhit
echo -n>$name
x=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789123
for (( i=0; i<65; i++ )); do eval _$(( $i/62 ))${x:$i:1}=$(( $i&63 )); done
n=0; m=0
(while read -n 1 c; do
case $c in [A-Za-z0-9+/=])
if [ "$c" == "+" ]; then c=11; elif [ "$c" == "/" ]; then c=12;
elif [ "$c" == "=" ]; then c=13; else c=0$c; fi
e=$d; d=$c ; eval c=\$_$c; n=$(( $n*64+$c )); m=$(( $m+1 ))
if [ "$m" == "4" ]; then
echo -n -e \\$(( ($n>>22&7)*100+($n>>19&7)*10+($n>>16&7) ))>>$name
if [ "$e" != "13" ]; then
echo -n -e \\$(( ($n>>14&7)*100+($n>>11&7)*10+($n>>8&7) ))>>$name; fi
if [ "$d" != "13" ]; then
echo -n -e \\$(( ($n>>6&7) *100+($n>>3&7)*10+($n&7) ))>>$name; fi
n=0; m=0
fi
esac
done)<<"---";
f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAdIAECDQAAAAAAAAAAAAAADQAIAACAAAAAAAAAAEA
AAAAAAAAAIAECACABAjjAQAA4wEAAAUAAAAAEAAAAQAAAOQBAADkkQQI5JEECBgAAACOAAAA
BgAAAAAQAAC7AAAAALkBVAAAugCSBAi4NgAAAM2AhcAPiK0AAAC+AJIECL85kgQIuTkAAADz
pIElDJIECPX////GBRaSBAgAxgUXkgQIAbsAAAAAuQJUAAC6AJIECLg2AAAAzYCFwHhqvwoA
AADobAAAAD3/////dEM9/v///3Uiu+SRBAi57JEECLiiAAAAzYBPddm/CgAAALAr6KsAAADr
y1CwCuihAAAA6JwAAABY6JYAAACwCuiPAAAAuwAAAAC5AlQAALo5kgQIuDYAAADNgLsAAAAA
uAEAAADNgGC79JEECLkBAAAAugAAAAC4qAAAAM2ACcB4E3UIYZC4/v///8P2BfiRBAgBdQuD
y/+4AQAAAM2AYLsAAAAAufyRBAi6AQAAALgDAAAAzYCFwHgQYZB0CA+2BfyRBAjDg8j/w4PL
/7gBAAAAzYBguwEAAAC5/JEECIgBugEAAAC4BAAAAM2Ag/gBdQNhkMODy/+4AQAAAM2AAAAA
AAAA4fUFAAAAAAAAAAAAAAAAAQAAAA==
---
chmod +x $name
#############################################################################
.



Relevant Pages

  • Re: read a single character from stdin
    ... copies of the termios structure - "get" to one of 'em, ... resd 1; output mode flags ...
    (alt.lang.asm)
  • Re: read a single character from stdin
    ... the switch to raw mode is done every time a character is read so its very ... the switch to raw mode is done in main. ... termios:blk.l 1; input mode flags ... blk.l 1; control mode flags ...
    (alt.lang.asm)