News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Get Keyboard scan code from a console app

Started by pcMike, February 12, 2013, 12:42:30 PM

Previous topic - Next topic

pcMike

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

hutch--

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 ?

dedndave

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

pcMike

Hutch - My app is a Win32 Console app.

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

Mike

pcMike

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

dedndave

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

pcMike

Thanks so much DednDave, that works perfect!  :greenclp:

hutch--

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

dedndave

i guess he wants the scan code, Hutch
but, that is a simple snippet   :t

Stan

@ DednDave
Excellent code.  That cleared two annoying problems.
Thank you.

hutch--

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.

dedndave

#11
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

hutch--

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

dedndave

 :biggrin:

here, lemme put that in a string and use LODSB   :lol:

pcMike

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