The MASM Forum

General => The Campus => Topic started by: pcMike on February 12, 2013, 12:42:30 PM

Title: Get Keyboard scan code from a console app
Post by: pcMike on February 12, 2013, 12:42:30 PM
I am trying to find a way to wait for a key to be pressed from a Console, and detmine the scancode of they key that was pressed.
I need the scan code so I can redirect the I/O to a DOS application.

In DOS I can do it using the BIOS like this:
   mov ah,0
   INT 16h
Which returns:
   AH = BIOS scan code
   AL = ASCII character

I've tried the GETKEY macro, as well as looked at the MSVCRT functions, but I can not find a way to do it in Windows.
Have I overlooked something?

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: hutch-- on February 12, 2013, 12:47:35 PM
Mike,

What OS do you intend to use here, a true 16 bit MS-DOS, a CMD.EXE window in Windows and is the application you have in mind a 16 bit DOS app or a 32 bit PE executable ?
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 12, 2013, 01:00:28 PM
you can use crt__kbhit to poll the keyboard

if there is a key present....
you will want to get it from the buffer with crt_getch
for extended or function keys, you need to call crt_getch a second time
on the first call, AL will either be 0E0h for extended keys or 0 for function keys
if AL is any other value, it is a normal key and only one call is needed

this is a function i wrote that polls the keyboard, but returns immediately if no key is present
;--------------------------------------------------------------------------

InKyb   PROC

;Polled Keyboard Input - DednDave 8, 2010
;
;This function returns a keystroke in EAX if there is one in the buffer.
;If the buffer is empty, the function returns immediately.
;
;If the keyboard buffer is empty, AH = 0, AL = 0, ZF = 1.
;If the stroke is a regular key, AH = 0, AL = key char, ZF = 0.
;If the stroke is an extended key, AH = extended key, AL = E0h, ZF = 0.
;If the stroke is a function key, AH = function key, AL = 0, ZF = 0.
;
;ECX, EDX are not preserved.

        call    crt__kbhit
        or      eax,eax
        jz      InKyb1

        call    crt__getch
        and     eax,0FFh
        jz      InKyb0

        cmp     al,0E0h
        jnz     InKyb1

InKyb0: push    eax
        call    crt__getch
        pop     edx
        shl     eax,8
        or      eax,edx

InKyb1: ret

InKyb   ENDP

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


in the masm32 library, there is a wait_key function that waits for a keypress
but, it does not handle extended or function keys correctly

you can use the above routine in a loop with Sleep....
@@:     call    InKyb
        jnz     @F

        INVOKE  Sleep,50
        jmp     @B

@@:


50 mS is a good wait period - the smallest i would use would be 40 mS
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 12, 2013, 01:04:50 PM
Hutch - My app is a Win32 Console app.

DednDave - Thanks.. I'm going to give your code a try. :-)

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 12, 2013, 01:15:29 PM
DednDave - That was very helpful but it lacks providing the scan code for every character...
For example, Here are some codes which DOS provides:

When I press 'a'  Scan Code= 1Eh Ascii Code = 61h
When I press 'b'  Scan Code= 30h Ascii Code = 62h
When I press 'c'  Scan Code= 2Eh Ascii Code = 63h

I was hoping there is a Win32 API which can provide me with the same scan codes, to save me the trouble
of having to manually create a table with the scan codes for every key (plus various CTRL-ALT-SHIFT states).
Any ideas?

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 12, 2013, 01:18:25 PM
ah, yes - sorry i missed that in your post

for that, you will want to use ReadConsoleInput
not as nice, because the crt takes care of some bugs with the CMD window

but, give this a try....
;*************************************************************************************************************

AnyKey  PROC

;Wait for Any Console Key Press - DednDave 12-2011
;
;  This function returns when any console key is pressed (bKeyDown = 1).
;A possible drawback is that all input event records are removed from
;the console input queue until a key is pressed. In many cases, this is
;not an issue. The virtual key code, virtual scan code, TCHAR character,
;and control key state values are returned in registers.

;Call With: Nothing
;
;  Returns: EAX = TCHAR character (high word of EAX = 0)
;           ECX = control key state flags
;               Bit   Name                Meaning
;                0    RIGHT_ALT_PRESSED   Right ALT key is pressed
;                1    LEFT_ALT_PRESSED    Left ALT key is pressed
;                2    RIGHT_CTRL_PRESSED  Right CTRL key is pressed
;                3    LEFT_CTRL_PRESSED   Left CTRL key is pressed
;                4    SHIFT_PRESSED       SHIFT key is pressed
;                5    NUMLOCK_ON          NUM LOCK light is on
;                6    SCROLLLOCK_ON       SCROLL LOCK light is on
;                7    CAPSLOCK_ON         CAPS LOCK light is on
;                8    ENHANCED_KEY        Key is enhanced
;           EDX:
;           low word (DX) = virtual key code
;               high word = virtual scan code
;
;           all other registers are preserved

        push    ebx
        push    ebp
        sub     esp,(sizeof INPUT_RECORD+3) and -4
        mov     ebp,esp
        INVOKE  GetStdHandle,STD_INPUT_HANDLE
        xchg    eax,ebx
        push    ecx

AnyKy0: INVOKE  ReadConsoleInput,ebx,ebp,1,esp
        movzx   edx,word ptr [ebp].INPUT_RECORD.EventType
        cmp     edx,KEY_EVENT                             ;KEY_EVENT EQU 1
        jnz     AnyKy0

        cmp     edx,[ebp].INPUT_RECORD.KeyEvent.bKeyDown
        jnz     AnyKy0

        pop     ecx
        movzx   eax,word ptr [ebp].INPUT_RECORD.KeyEvent.UnicodeChar
        mov     edx,dword ptr [ebp].INPUT_RECORD.KeyEvent.wVirtualKeyCode
        mov     ecx,[ebp].INPUT_RECORD.KeyEvent.dwControlKeyState
        add     esp,(sizeof INPUT_RECORD+3) and -4
        pop     ebp
        pop     ebx
        ret

AnyKey  ENDP

;*************************************************************************************************************


no loop needed for that one, as it waits for a keypress
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 12, 2013, 02:12:07 PM
Thanks so much DednDave, that works perfect!  :greenclp:
Title: Re: Get Keyboard scan code from a console app
Post by: hutch-- on February 12, 2013, 02:22:15 PM
See if this helps.



IF 0  ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                      Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include\masm32rt.inc

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    print "Hi, I am waiting for you to press the 'q' key",13,10

  back:
    invoke GetAsyncKeyState,VK_Q
    test eax, eax
    jz back

    print hex$(eax),13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 12, 2013, 02:37:50 PM
i guess he wants the scan code, Hutch
but, that is a simple snippet   :t
Title: Re: Get Keyboard scan code from a console app
Post by: Stan on February 12, 2013, 03:53:15 PM
@ DednDave
Excellent code.  That cleared two annoying problems.
Thank you.
Title: Re: Get Keyboard scan code from a console app
Post by: hutch-- on February 12, 2013, 06:28:12 PM
Dave,

What I like about GetAsyncKeyState is you can gang up any key combinations and this makes it very flexible. It is also effectively free of the irritations of the console API functions.
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 12, 2013, 09:08:04 PM
many forum members don't like the way i use the stack
and, they prefer indented .if/.endif and so on
this is a cleaned up version, but may be a few bytes larger
it's probably easier to understand

;*************************************************************************************************************

AnyKey  PROC

;Wait for Any Console Key Press - DednDave
;version 1, 12-2011
;version 2, 2-2013
;
;  This function returns when any console key is pressed (bKeyDown = 1).
;A possible drawback is that all input event records are removed from
;the console input queue until a key is pressed. In many cases, this is
;not an issue. The virtual key code, virtual scan code, TCHAR character,
;and control key state values are returned in registers.

;Call With: Nothing
;
;  Returns: EAX = TCHAR character (high word of EAX = 0)
;           ECX = control key state flags
;               Bit   Name                Meaning
;                0    RIGHT_ALT_PRESSED   Right ALT key is pressed
;                1    LEFT_ALT_PRESSED    Left ALT key is pressed
;                2    RIGHT_CTRL_PRESSED  Right CTRL key is pressed
;                3    LEFT_CTRL_PRESSED   Left CTRL key is pressed
;                4    SHIFT_PRESSED       SHIFT key is pressed
;                5    NUMLOCK_ON          NUM LOCK light is on
;                6    SCROLLLOCK_ON       SCROLL LOCK light is on
;                7    CAPSLOCK_ON         CAPS LOCK light is on
;                8    ENHANCED_KEY        Key is enhanced
;           EDX:
;           low word (DX) = virtual key code
;               high word = virtual scan code
;
;           all other registers are preserved

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

    LOCAL   ir          :INPUT_RECORD
    LOCAL   uRecCnt     :UINT
    LOCAL   hStdInp     :HANDLE

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

    INVOKE  GetStdHandle,STD_INPUT_HANDLE
    mov     hStdInp,eax
    .repeat
        INVOKE  ReadConsoleInput,hStdInp,addr ir,1,addr uRecCnt
        movzx   edx,word ptr ir.EventType
    .until (edx==KEY_EVENT) && (edx==ir.KeyEvent.bKeyDown)
    movzx   eax,word ptr ir.KeyEvent.UnicodeChar
    mov     ecx,ir.KeyEvent.dwControlKeyState
    mov     edx,dword ptr ir.KeyEvent.wVirtualKeyCode
    ret

AnyKey  ENDP

;*************************************************************************************************************


the only time you might have a problem with this routine is if mouse input is to be used while waiting for a key
that seems rather rare
in some cases, it may actually be desirable to flush the mouse input events

Hutch,
yah - i may play with that code when i have some time
i believe that crt__getch uses ReadConsoleInput, and i like the way it behaves - lol
that's why i used it, but that doesn't mean it's the best way   :P :P
Title: Re: Get Keyboard scan code from a console app
Post by: hutch-- on February 12, 2013, 10:21:03 PM
Dave, you may like this one, its one of the reasons why I use GetAsyncKeyState().



IF 0  ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                      Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include\masm32rt.inc

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    print "Press the keys 'd' 'a' 'v' 'e' together to exit",13,10

  lpst:
    invoke GetAsyncKeyState,VK_D
    test eax, eax
    je lpst

    invoke GetAsyncKeyState,VK_A
    test eax, eax
    je lpst

    invoke GetAsyncKeyState,VK_V
    test eax, eax
    je lpst

    invoke GetAsyncKeyState,VK_E
    test eax, eax
    je lpst

    print "TADA",13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 12, 2013, 10:28:33 PM
 :biggrin:

here, lemme put that in a string and use LODSB   :lol:
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 13, 2013, 07:30:36 PM
Dave - The new version looks much cleaner, thanks!
i
Hutch - The GetAsyncKeyState() looks intresting, I'm sure I'll have uses for that in the future.


Mike
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 14, 2013, 12:26:56 AM
it's essentially the same code, just written in a different style
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 14, 2013, 03:47:29 PM
Hi Dave,

I found a little bug in the AnyKey procedure. If the user presses Ctrl-C, it will shut down my application.
Is there any way to prevent this from happening? Otherwise I may end up needing to create a scan table
and stick with GetKey. :-(

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 14, 2013, 08:46:43 PM
i have noticed some "peculiarities" in the routine, also
for example, when pressing keys like shift, control, alt, the routine exits with a keystroke
it is somewhat debatable if that is desirable - lol
and - another debate for caps lock, scroll lock, num lock, etc
these issues can be handled by simply adding filters to the code, and looping back
let me know if you want an example of how to do this

as for the specific problem of Ctrl-C - it's not really a bug - it's considered normal   :biggrin:
you could use SetConsoleCtrlHandler during program initialization to alter this behaviour
however, i don't think you can avoid the Ctrl-Break combination

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx)

    INVOKE  SetConsoleCtrlHandler,NULL,TRUE
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 15, 2013, 04:55:06 AM
ok - i was wrong..... again   :biggrin:

you can capture Ctrl-Break by writing a handler routine

in the attached test program, i wrote a simple handler that intercepts both Ctrl-C and Ctrl-Break
i also added filters to AnyKey so that it does not exit for shift, control, or alt keys

now, the problem is that no keystroke is generated for Ctrl-C
i may be able to insert that stroke into the stream manually
let me try it on the next go

note: there are numerous Ctrl-? strokes that do not place anything into the stream (Ctrl-S, for example)
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 15, 2013, 04:57:56 AM
Thanks Dave that did the trick for Ctrl-C. I'm not too concerned about Ctrl-Break as that seems to kill most Console programs, so I can live with that. I did have to add filters to ignore the plain ALT, CTL and SHIFT key results... but since it's named AnyKey it lives up to it's name. :)

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 15, 2013, 05:00:25 AM
yah - my initial use for this routine was simply "wait for any key to be pressed"
i decided to add the extra functionality because the info was there   :P
Title: Re: Get Keyboard scan code from a console app
Post by: pcMike on February 15, 2013, 05:04:43 AM
Your new test version works very well so far.  I don't need to test for Ctrl-C in my app so I may use this version instead. :-)

Mike
Title: Re: Get Keyboard scan code from a console app
Post by: dedndave on February 15, 2013, 05:27:40 AM
ok - well, if you need to, you can use WriteConsoleInput to insert INPUT_RECORD structures into the stream
for each keypress, you would want a record for key down and a record for key up