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 value 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 value. 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 value 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; }