asm {
NORMAL_KEY_SCAN_DECODE_TABLE::
                                DU8             0, CH_ESC,       "1234567890-=", CH_BACKSPACE, '\t';
                                DU8             "qwertyuiop[]", '\n', 0, "as";
                                DU8             "dfghjkl;'\`", 0, "\\zxcv";
                                DU8             "bnm,./", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0;
                                DU8             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0;
SHIFT_KEY_SCAN_DECODE_TABLE::
                                DU8             0, CH_SHIFT_ESC, "!@#$%^&*()_+", CH_BACKSPACE, '\t';
                                DU8             "QWERTYUIOP{}", '\n', 0, "AS";
                                DU8             "DFGHJKL:\"~", 0, "|ZXCV";
                                DU8             "BNM<>?", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0;
                                DU8             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0;
CTRL_KEY_SCAN_DECODE_TABLE::
                                DU8             0, CH_ESC,       "1234567890-=", CH_BACKSPACE, '\t';
                                DU8             CH_CTRLQ, CH_CTRLW, CH_CTRLE, CH_CTRLR, CH_CTRLT, CH_CTRLY, CH_CTRLU, CH_CTRLI, CH_CTRLO,
                                                        CH_CTRLP, "[]", '\n', 0, CH_CTRLA, CH_CTRLS;
                                DU8             CH_CTRLD, CH_CTRLF, CH_CTRLG, CH_CTRLH, CH_CTRLJ, CH_CTRLK, CH_CTRLL,
                                                        ";'\`", 0, "\\", CH_CTRLZ, CH_CTRLX, CH_CTRLC, CH_CTRLV;
                                DU8             CH_CTRLB, CH_CTRLN, CH_CTRLM, ",./", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0;
                                DU8             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0;
}

U0 KbdCmdSend(I64 port, U8 val)
{
        F64 timeout = tS + 0.125;

        while (tS < timeout)
        {
                if (!(InU8(KBD_CTRL) & 2))
                {
                        OutU8(port, val);
                        return;
                }
        }
        throw;
}

I64 KbdCmdRead()
{
        F64 timeout = tS + 0.125;

        while (tS < timeout)
                if (InU8(KBD_CTRL) & 1)
                        return InU8(KBD_PORT);
        throw;
}

U0 KbdCmdFlush()
{
        F64 timeout = tS + 0.03;

        while (tS < timeout)
                InU8(KBD_PORT);
}

U0 KbdLEDsSet(I64 sc)
{
        U8 v = 0;

        BEqual(&v, 0, Bt(&sc, SCf_SCROLL));
        BEqual(&v, 1, Bt(&sc, SCf_NUM));
        BEqual(&v, 2, Bt(&sc, SCf_CAPS));
        try
        {
                KbdCmdSend(KBD_PORT, 0xED);
                KbdCmdSend(KBD_PORT, v);
        }
        catch
                Fs->catch_except = TRUE;
}

U0 KbdMouseCmdAck(...)
{
        I64 i, ack, timeout;

        for (i = 0; i < argc; i++)
        {
                timeout = 5;
                do
                {
                        ack = 0;
                        try
                        {
                                KbdCmdSend(KBD_CTRL, 0xD4);
                                KbdCmdSend(KBD_PORT, argv[i]);
                                ack = KbdCmdRead;
                        }
                        catch
                        {
                                KbdCmdFlush;
                                Fs->catch_except = TRUE;
                        }
                }
                while (ack != 0xFA && --timeout);

                if (!timeout)
                        throw;
        }
}

U0 KbdTypeMatic(U8 delay)
{//Set speed of repeated keys.
        try
        {
                KbdCmdSend(KBD_CTRL, 0xA7); //Disable Mouse
                KbdCmdSend(KBD_CTRL, 0xAE); //Enable Keyboard
                KbdCmdSend(KBD_PORT, 0xF3);
                KbdCmdSend(KBD_PORT, delay);//Typematic rate
                KbdCmdSend(KBD_CTRL, 0xA8); //Enable Mouse
        }
        catch
        {
                KbdCmdFlush;
                Fs->catch_except = TRUE;
        }
}

I64 Char2ScanCode(I64 ch, I64 sc_flags=0)
{//ASCII val to scan code (Slow).
        I64 i;
        U8 *table;

        if (sc_flags)
        {
                table = NORMAL_KEY_SCAN_DECODE_TABLE;
                if (sc_flags & SCF_CTRL || ch < 26)
                        table = CTRL_KEY_SCAN_DECODE_TABLE;
                else if (sc_flags & SCF_SHIFT || 'A' <= ch <= 'Z')
                {
                        if (!(sc_flags & SCF_CAPS))
                                table = SHIFT_KEY_SCAN_DECODE_TABLE;
                }
                else
                {
                        if (sc_flags & SCF_CAPS)
                                table = SHIFT_KEY_SCAN_DECODE_TABLE;
                }
                for (i = 0; i < 0x50; i++)
                        if (table[i] == ch)
                                return i | sc_flags;
                return sc_flags;
        }
        else
        {
                table = NORMAL_KEY_SCAN_DECODE_TABLE;
                for (i = 0; i < 0x50; i++)
                        if (table[i] == ch)
                                return i;
                table = SHIFT_KEY_SCAN_DECODE_TABLE;
                for (i = 0; i < 0x50; i++)
                        if (table[i] == ch)
                                return i | SCF_SHIFT;
                table = CTRL_KEY_SCAN_DECODE_TABLE;
                for (i = 0; i < 0x50; i++)
                        if (table[i] == ch)
                                return i | SCF_CTRL;
                return 0;
        }
}

U8 ScanCode2Char(I64 sc)
{//Scan code to ASCII val.
        U8 *table = NORMAL_KEY_SCAN_DECODE_TABLE;

        if (sc & SCF_E0_PREFIX)
                return 0;
        if (sc & SCF_CTRL)
                table = CTRL_KEY_SCAN_DECODE_TABLE;
        else if (sc & SCF_SHIFT)
        {
                if (!(sc & SCF_CAPS))
                        table = SHIFT_KEY_SCAN_DECODE_TABLE;
        }
        else
        {
                if (sc & SCF_CAPS)
                        table = SHIFT_KEY_SCAN_DECODE_TABLE;
        }
        sc &= 0x7F;
        if (sc >= 0x50)
                return 0;
        else
                return table[sc];
}

U8 scan_code_map[0x100] =
{
        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, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, SC_SHIFT, 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, 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, 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, 0, 0, 0, 0, 0, SC_ENTER, SC_CTRL, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0x35, 0, 0, SC_ALT, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, SC_HOME, SC_CURSOR_UP, SC_PAGE_UP, 0, SC_CURSOR_LEFT, 0, SC_CURSOR_RIGHT, 0, SC_END, 
        SC_CURSOR_DOWN, SC_PAGE_DOWN, SC_INS, SC_DELETE, 0, 0, 0, 0, 0, 0, 0, 0, SC_GUI, 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, 0, 0, 0, 
};

U8 num_lock_map[0x100] =
{
        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, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, SC_SHIFT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 8, 9, 10, 0, 5, 6, 7, 0, 2, 
        3, 4, 11, 0x34, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, SC_ENTER, SC_CTRL, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0x35, 0, 0, SC_ALT, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, SC_HOME, SC_CURSOR_UP, SC_PAGE_UP, 0, SC_CURSOR_LEFT, 0, SC_CURSOR_RIGHT, 0, SC_END, 
        SC_CURSOR_DOWN, SC_PAGE_DOWN, SC_INS, SC_DELETE, 0, 0, 0, 0, 0, 0, 0, 0, SC_GUI, 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, 0, 0, 0,
};

U8 *Char2KeyName(I64 ch, Bool include_ctrl=TRUE)
{//ASCII val to key name.
        I64 i;
        U8  buf[STR_LEN];

        if (ch <= CH_SPACE)
        {
                switch [ch]
                {
                        case '\n':
                                StrCopy(buf, "ENTER");
                                break;

                        case CH_BACKSPACE:
                                StrCopy(buf, "BACKSPACE");
                                break;

                        case '\t':
                                StrCopy(buf, "TAB");
                                break;

                        case CH_ESC:
                                StrCopy(buf, "ESC");
                                break;

                        case CH_SHIFT_ESC:
                                StrCopy(buf, "SHIFT_ESC");
                                break;

                        case 0: //nobound switch
                        case 29:
                        case 30:
                                *buf = 0;
                                break;

                        case CH_SPACE:
                                StrCopy(buf, "SPACE");
                                break;

                        default:
                                if (include_ctrl)
                                        StrCopy(buf, "CTRL ");
                                buf[i = StrLen(buf)] = ch - 1 + 'a';
                                buf[i + 1] = 0;
                                break;
                }
        }
        else if (Bt(char_bmp_printable, ch))
        {
                *buf = ch;
                buf[1] = 0;
        }
        else
                *buf = 0;
        return StrNew(buf);
}

U8 *ScanCode2KeyName(I64 sc)
{//Scan code to key name.
        I64 ch;
        U8  buf[STR_LEN], *st;

        *buf = 0;
        if (sc & SCF_CTRL)
                CatPrint(buf, "CTRL ");
        if (sc & SCF_ALT)
                CatPrint(buf, "ALT ");
        if (sc & SCF_SHIFT)
                CatPrint(buf, "SHIFT ");
        if (sc & SCF_NO_SHIFT)
                CatPrint(buf, "      ");
        if (ch = ScanCode2Char(sc & 255))
        {
                st = Char2KeyName(ch, FALSE);
                StrCopy(buf + StrLen(buf), st);
                Free(st);
        }
        else
        {
                switch (sc & 255)
                {
                        case SC_BACKSPACE:              CatPrint(buf, "BACK");                  break;
                        case SC_CAPS:                   CatPrint(buf, "CAPS");                  break;
                        case SC_NUM:                    CatPrint(buf, "NUM");                   break;
                        case SC_SCROLL:                 CatPrint(buf, "SCROLL");                break;
                        case SC_CURSOR_UP:              CatPrint(buf, "UP");                    break;
                        case SC_CURSOR_DOWN:    CatPrint(buf, "DOWN");                  break;
                        case SC_CURSOR_LEFT:    CatPrint(buf, "LEFT");                  break;
                        case SC_CURSOR_RIGHT:   CatPrint(buf, "RIGHT");                 break;
                        case SC_PAGE_UP:                CatPrint(buf, "PAGE_UP");               break;
                        case SC_PAGE_DOWN:              CatPrint(buf, "PAGE_DOWN");             break;
                        case SC_HOME:                   CatPrint(buf, "HOME");                  break;
                        case SC_END:                    CatPrint(buf, "END");                   break;
                        case SC_INS:                    CatPrint(buf, "INS");                   break;
                        case SC_DELETE:                 CatPrint(buf, "DELETE");                break;
                        case SC_F1:                             CatPrint(buf, "F1");                    break;
                        case SC_F2:                             CatPrint(buf, "F2");                    break;
                        case SC_F3:                             CatPrint(buf, "F3");                    break;
                        case SC_F4:                             CatPrint(buf, "F4");                    break;
                        case SC_F5:                             CatPrint(buf, "F5");                    break;
                        case SC_F6:                             CatPrint(buf, "F6");                    break;
                        case SC_F7:                             CatPrint(buf, "F7");                    break;
                        case SC_F8:                             CatPrint(buf, "F8");                    break;
                        case SC_F9:                             CatPrint(buf, "F9");                    break;
                        case SC_F10:                    CatPrint(buf, "F10");                   break;
                        case SC_F11:                    CatPrint(buf, "F11");                   break;
                        case SC_F12:                    CatPrint(buf, "F12");                   break;
                        case SC_GUI:                    CatPrint(buf, "WINDOWS");               break;
                        case SC_PRINTSCREEN1:   CatPrint(buf, "PRINTSCREEN1");  break;
                        case SC_PRINTSCREEN2:   CatPrint(buf, "PRINTSCREEN2");  break;
                }
        }
        return StrNew(buf);
}

U0 KbdBuildSC(U8 raw_byte, Bool in_irq, U8 *_last_raw_byte, I64 *_last_sc)
{
        I64  ch, sc_flags, sc, sc2, sc_raw, new_key_f;
        Bool set_LEDs = FALSE;

        if (raw_byte == 0xE0)
        {
                *_last_sc &= ~0x1FF;
                *_last_raw_byte = raw_byte;
                return;
        }
        sc = raw_byte;
        BEqual(&sc, SCf_E0_PREFIX, *_last_raw_byte == 0xE0);
        BEqual(&sc, SCf_KEY_UP, raw_byte & 0x80);
        *_last_raw_byte = raw_byte;

        sc_flags = _last_sc->u32[0] & ~0x1FF;
        sc_raw   = sc;

        if (sc_flags & SCF_NUM)
        {
                if (sc2 = num_lock_map[sc.u8[0]])
                        sc.u8[0] = sc2;
        }
        else
        {
                if (sc2 = scan_code_map[sc.u8[0]])
                        sc.u8[0] = sc2;
        }

        new_key_f = SCF_NEW_KEY;
        if (sc & SCF_KEY_UP)
                switch (sc & ~SCF_KEY_UP)
                {
                        case SC_SHIFT:  sc_flags &= ~SCF_SHIFT;                                 break;
                        case SC_CTRL:   sc_flags &= ~SCF_CTRL;                                  break;
                        case SC_ALT:    sc_flags &= ~SCF_ALT;                                   break;
                        case SC_DELETE: sc_flags &= ~SCF_DELETE;                                break;
                        case SC_INS:    sc_flags &= ~SCF_INS;                                   break;
                        case SC_CAPS:   sc_flags ^= SCF_CAPS;   set_LEDs=TRUE;  break;
                        case SC_NUM:    sc_flags ^= SCF_NUM;    set_LEDs=TRUE;  break;
                        case SC_SCROLL: sc_flags ^= SCF_SCROLL; set_LEDs=TRUE;  break;
                }
        else
                switch (sc)
                {
                        case SC_SHIFT:
                                if (Bts(&sc_flags, SCf_SHIFT))
                                        new_key_f = 0;
                                break;

                        case SC_CTRL:
                                if (Bts(&sc_flags, SCf_CTRL))
                                        new_key_f = 0;
                                break;

                        case SC_ALT:
                                if (Bts(&sc_flags, SCf_ALT))
                                        new_key_f = 0;
                                break;

                        case SC_DELETE:
                                sc_flags |= SCF_DELETE;
                                break;

                        case SC_INS:
                                sc_flags |= SCF_INS;
                                break;
                }

        sc_flags |= new_key_f;
        sc = sc_flags | sc | (sc_flags | sc_raw) << 32;
        if (sc_flags & SCF_CTRL && sc_flags & SCF_ALT)
        {
                if (!(sc & SCF_KEY_UP))
                {
                        if (sc & 255 == SC_DELETE && !(sc_flags & SCF_SHIFT))
                                CtrlAltDel(sc);
                        else
                        {
                                if (sc & 255 == SC_ESC)
                                        ch = 't';
                                else if (sc & 255 == SC_TAB)
                                        ch = 'n';
                                else
                                        ch = ScanCode2Char(sc & 255);
                                if ('a' <= ch <= 'z')
                                {
                                        sc &= ~(SCF_NEW_KEY | SCF_NEW_KEY << 32);
                                        ch -= 'a';
                                        kbd.last_down_scan_code = sc;
                                        if (keydev.fp_ctrl_alt_cbs[ch] &&
                                                        Bt(&keydev.ctrl_alt_in_irq_flags, ch) == in_irq &&
                                                        (!(sc_flags & SCF_SHIFT) &&
                                                        keydev.ctrl_alt_no_shift_descs[ch]) || sc_flags & SCF_SHIFT && keydev.ctrl_alt_shift_descs[ch])
                                                (*keydev.fp_ctrl_alt_cbs[ch])(sc);
                                }
                        }
                }
        }
        if (set_LEDs && !in_irq)
                KbdLEDsSet(sc);
        *_last_sc = sc;
}

U0 KbdPacketRead()
{
        static U8  last_raw_byte = 0;
        static I64 last_sc = 0;
        U8                 raw_byte;

        if (TSCGet>kbd.timestamp + counts.time_stamp_freq >> 3)
                FifoU8Flush(kbd.fifo);
        kbd.timestamp = TSCGet;
        raw_byte = InU8(KBD_PORT);
        KbdBuildSC(raw_byte, TRUE, &last_raw_byte, &last_sc);
        if (!FifoU8Count(kbd.fifo))
        {
                FifoU8Insert(kbd.fifo, raw_byte);
                if (raw_byte != 0xE0)
                {
                        while (FifoU8Remove(kbd.fifo, &raw_byte))
                                FifoU8Insert(kbd.fifo2, raw_byte);
                }
        }
        else
        {
                FifoU8Insert(kbd.fifo, raw_byte);
                while (FifoU8Remove(kbd.fifo, &raw_byte))
                        FifoU8Insert(kbd.fifo2, raw_byte);
        }
}

interrupt U0 IRQKbd()
{
        CLD
        OutU8(PIC_1, PIC_EOI);
        kbd.irqs_working = TRUE;
        if (mouse_hard.install_in_progress)
        {
                kbd.reset = TRUE;
                return;
        }
        keydev.ctrl_alt_ret_addr = RBPGet()(I64) + 8;
        KbdPacketRead;
}

U0 KbdInit()
{
        try
        {
                KbdCmdFlush;
                KbdCmdSend(KBD_CTRL, 0xA7); //Disable Mouse
                KbdCmdSend(KBD_CTRL, 0xAE); //Enable Keyboard
                KbdCmdSend(KBD_PORT, 0xF0);
                KbdCmdSend(KBD_PORT, 0x02);
                KbdLEDsSet(kbd.scan_code);
        }
        catch
        {
                KbdCmdFlush;
                Fs->catch_except = TRUE;
        }
        IntEntrySet(0x21, &IRQKbd);
        OutU8(PIC_1_DATA, InU8(PIC_1_DATA) & ~2);
}

U0 KbdHandler()
{
        static U8 last_raw_byte = 0;
        U8                raw_byte;

        FifoU8Remove(kbd.fifo2, &raw_byte);
        KbdBuildSC(raw_byte, FALSE, &last_raw_byte, &kbd.scan_code);
        if (raw_byte == 0xE0)
        {
                FifoU8Remove(kbd.fifo2, &raw_byte);
                KbdBuildSC(raw_byte, FALSE, &last_raw_byte, &kbd.scan_code);
        }
        if (Btr(&kbd.scan_code, SCf_NEW_KEY))
        {
                kbd.new_key_timestamp = kbd.timestamp;
                Btr(&kbd.scan_code, 32 + SCf_NEW_KEY);
                FifoI64Ins(kbd.scan_code_fifo, kbd.scan_code);
                kbd.count++;
                if (!(kbd.scan_code & SCF_KEY_UP))
                {
                        kbd.last_down_scan_code = kbd.scan_code;
                        Bts(kbd.down_bitmap,  kbd.scan_code.u8[0]);
                        Bts(kbd.down_bitmap2, kbd.scan_code.u8[4]);
                }
                else
                {
                        Btr(kbd.down_bitmap,  kbd.scan_code.u8[0]);
                        Btr(kbd.down_bitmap2, kbd.scan_code.u8[4]);
                }
        }
}

I64 KbdMessagesQueue()
{
        I64              arg1, arg2, message_code = MESSAGE_NULL;
        CTask   *task_focus;
        if (task_focus = sys_focus_task)
        {
                while (FifoI64Remove(kbd.scan_code_fifo, &arg2))
                {
                        arg1 = ScanCode2Char(arg2);
                        if (arg2 & SCF_KEY_UP)
                        {
                                TaskMessage(task_focus, 0, MESSAGE_KEY_UP, arg1, arg2, 0);
                                message_code = MESSAGE_KEY_UP;
                        }
                        else
                        {
                                TaskMessage(task_focus, 0, MESSAGE_KEY_DOWN, arg1, arg2, 0);
                                message_code = MESSAGE_KEY_DOWN;
                        }
                }
        }
        return message_code;
}

I64 KbdMouseEventTime()
{//Timestamp of last key or mouse event.
        if (mouse_hard.timestamp > kbd.timestamp)
                return mouse_hard.timestamp;
        else
                return kbd.new_key_timestamp;
}