{==============================================================================
 = Keyboard Helper Script
 = Author: Jason Goemaat
 = Version 1.0
 =
 = Lets you check key states easily and for presses inside other scripts
 ==============================================================================
 = int CheckKeyPress(MyKeyState *pKeyState)
 =
 = struct MyKeyState {
 =     int VirtualKeyCode,// key code to check
 =     int LastTickCount, // time of last "PRESS" returned from GetTickCount
 =     int ReturnFlags, // also in eax, 800000001 for new press, 1 if still pressed, 0 if not pressed
 =     int Reserved, // currently incrementing count of presses
 =

 Sample call:
 LABEL(MyKeyCode)
 MyKeyCode:
  // 14 is VK_CAPITAL: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731.aspx
  DD 14 0 0 0

 // now the code to actually check
 push MyKeyCode
 call CheckKeyPress
 test eax, 80000001 // eax is return, also [MyKeyCode+8]
 js DestinationIfNewPress // sign bit (MSB) is if new press (last call passing this struct wasn't pressed or was pressed at a different time)
 jnz DestinationIfPressed // bit 0 (LSB) is set if pressed at all
 // not pressed here

}

globalalloc(KeyboardHelper,$10000) // upped to 64k

//=============================================================================
[ENABLE]
//=============================================================================
label(CheckKeyPress)
registersymbol(CheckKeyPress) // method called from other scripts

//-----------------------------------------------------------------------------
// Keyboard
//-----------------------------------------------------------------------------
label(CurrentMS)
label(ExitFlag)
label(KeyPressTimes)
label(IsPressed)
label(AfterPressed)
label(Continue)
label(KEYSTATES)
registersymbol(KEYSTATES) // register if you want to double-check

//-----------------------------------------------------------------------------
KeyboardHelper:
label(MyGetKeyboardState)
MyGetKeyboardState:
    label(ContinueMyGetKeyboardState)
    push ebp
    mov ebp, esp
    push edi
    sub esp, 8

    mov edi, dword ptr [ebp+8]800000001
    mov ecx, 0

ContinueMyGetKeyboardState:
    mov dword ptr [ebp-4], ecx
    push ecx
    call GetAsyncKeyState
    mov byte ptr [edi], ah
    mov ecx, dword ptr [ebp-4]
    inc ecx
    inc edi
    cmp ecx, 00000100
    jb ContinueMyGetKeyboardState

    add esp, 8
    pop edi
    pop ebp
    ret 0004

//-----------------------------------------------------------------------------
CheckKeyPress:
    label(CheckKeyPress_NotCurrentlyPressed)
    // pushed value is a pointer to 16 bytes
    // 0000: Virtual Key Code (http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx)
    // 0004: Time of last press we checked (set by this to 0 if not pressed or
    //       GetTickCount as of when we noticed it was pressed
    // 0008: Returned flags, 800000001 if newly pressed, 1 if still pressed, 0 if not
    // 000C: Reserved for future use
    // RETURN (in EAX): bit 00000001 if currently pressed, 80000001 if newly pressed
    //       "test eax, eax" will let you JNZ if pressed at all, JS if newly pressed
    push ebp
    mov ebp, esp
    push eax // space for result at ebp-4
    pushad // use any registers I want

    // clear result
    xor eax, eax

    mov edi, dword ptr [ebp+8] // first and only argument, edi is address of struct
    mov ebx, dword ptr [edi] // get vkey code
    and ebx, 000000ff // make sure it is in range, should be
    lea esi, [KeyPressTimes+ebx*4] // get address of last stored press time
    mov ecx, dword ptr [esi] // get last time we noticed the key being pressed

    // if not currently pressed, leave eax clear
    test ecx, ecx
    jz CheckKeyPress_NotCurrentlyPressed

    // so it is pressed now, set 1 flag
    or eax, 00000001

    // see if it is newly pressed, stored ticks equals given ticks
    cmp ecx, dword ptr [edi+4]
    je CheckKeyPress_NotCurrentlyPressed // already pass this flag once
    inc [edi+c]
    or eax, 80000000

CheckKeyPress_NotCurrentlyPressed:
    // set value in passed struct no matter what
    mov [edi+4], ecx

    // store result to pop back into eax and in struct
    mov dword ptr [ebp-4], eax
    mov dword ptr [edi+8], eax

    popad
    pop eax // result
    pop ebp
    ret 0004 // pop pushed argument off the stack


LABEL(KeyboardHelperThread)
KeyboardHelperThread:
    push KEYSTATES // function will pop this when returning
    call MyGetKeyboardState

    call GetTickCount
    mov dword ptr [CurrentMS], eax

    // loop through each key so we know when last set
    mov ecx, 0

Continue:
    lea edi, [KeyPressTimes+ecx*4]
    movsx ebx, [KEYSTATES+ecx] // sign-extends, so MSB is right
    test ebx, ebx
    js IsPressed

    // not currently pressed, set time to 0
    mov dword ptr [edi], 00000000
    jmp AfterPressed

IsPressed:
    // only set if current value is 0, meaning it wasn't pressed before
    cmp dword ptr [edi], 0
    jnz AfterPressed
    mov dword ptr [edi], eax

AfterPressed:
    inc ecx
    cmp ecx, 00000100
    jb Continue

    mov eax, 0000000a // 10 ms sleep
    push eax
    call Sleep

    cmp dword ptr [ExitFlag], 0 // see if we should exit
    je KeyboardHelperThread
    ret

// current tick count (ms since system start)
KeyboardHelper+1000:
CurrentMS:
    dd 0
KeyboardHelper+1004:
ExitFlag:
    dd 0 // set to 1 on disable to trigger thread returning
KEYSTATES:
    // 32 quad-words, 256 bytes
    dq 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

KeyboardHelper+2000:
KeyPressTimes:
    // 256 dwords, 0 if key isn't pressed, tickcount when initially pressed
    // if it is pressed, current if was 0, unchanged otherwise
    dq 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    dq 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    dq 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    dq 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

CreateThread(KeyboardHelperThread)

//=============================================================================
[DISABLE]
//=============================================================================
// I leave symbols registered

// tell keyboard thread to exit
KeyboardHelper+1004:
    dd 1

