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_SHIFT_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; } public U0 KbdCmdSend(I64 port, U8 val) { F64 timeout=tS+0.125; while (tS<timeout) { if (!(InU8(KBD_CTRL)&2)) { OutU8(port,val); return; } } throw; } public I64 KbdCmdRead() { F64 timeout=tS+0.125; while (tS<timeout) if (InU8(KBD_CTRL)&1) return InU8(KBD_PORT); throw; } public U0 KbdCmdFlush() { F64 timeout=tS+0.03; while (tS<timeout) InU8(KBD_PORT); } U0 KbdLEDsSet(I64 sc) { U8 v=0; BEqu(&v,0,Bt(&sc,SCf_SCROLL)); BEqu(&v,1,Bt(&sc,SCf_NUM)); BEqu(&v,2,Bt(&sc,SCf_CAPS)); try { KbdCmdSend(KBD_PORT,0xED); KbdCmdSend(KBD_PORT,v); } catch Fs->catch_except=TRUE; } public U0 KbdMsCmdAck(...) { 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,0,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': StrCpy(buf,"ENTER"); break; case CH_BACKSPACE: StrCpy(buf,"BACKSPACE"); break; case '\t': StrCpy(buf,"TAB"); break; case CH_ESC: StrCpy(buf,"ESC"); break; case CH_SHIFT_ESC: StrCpy(buf,"SHIFT_ESC"); break; case 0: //nobound switch case 29: case 30: *buf=0; break; case CH_SHIFT_SPACE: StrCpy(buf,"SHIFT_SPACE"); break; case CH_SPACE: StrCpy(buf,"SPACE"); break; default: if (include_ctrl) StrCpy(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); StrCpy(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_F13: CatPrint(buf,"F13"); break; case SC_F14: CatPrint(buf,"F14"); break; case SC_F15: CatPrint(buf,"F15"); break; case SC_F16: CatPrint(buf,"F16"); break; case SC_F17: CatPrint(buf,"F17"); break; case SC_F18: CatPrint(buf,"F18"); break; case SC_F19: CatPrint(buf,"F19"); break; case SC_F20: CatPrint(buf,"F20"); break; case SC_F21: CatPrint(buf,"F21"); break; case SC_F22: CatPrint(buf,"F22"); break; case SC_F23: CatPrint(buf,"F23"); break; case SC_F24: CatPrint(buf,"F24"); break; case SC_GUI: CatPrint(buf,"WINDOWS"); break; case SC_PRTSCRN1: CatPrint(buf,"PRTSCRN1"); break; case SC_PRTSCRN2: CatPrint(buf,"PRTSCRN2"); 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; BEqu(&sc,SCf_E0_PREFIX,*_last_raw_byte==0xE0); BEqu(&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 KbdPktRead() { static U8 last_raw_byte=0; static I64 last_sc=0; U8 raw_byte; if (GetTSC>kbd.timestamp+cnts.time_stamp_freq>>3) FifoU8Flush(kbd.fifo); kbd.timestamp=GetTSC; raw_byte=InU8(KBD_PORT); KbdBuildSC(raw_byte,TRUE,&last_raw_byte,&last_sc); if (!FifoU8Cnt(kbd.fifo)) { FifoU8Ins(kbd.fifo,raw_byte); if (raw_byte!=0xE0) { while (FifoU8Rem(kbd.fifo,&raw_byte)) FifoU8Ins(kbd.fifo2,raw_byte); } } else { FifoU8Ins(kbd.fifo,raw_byte); while (FifoU8Rem(kbd.fifo,&raw_byte)) FifoU8Ins(kbd.fifo2,raw_byte); } } interrupt U0 IRQKbd() { CLD OutU8(0x20,0x20); kbd.irqs_working=TRUE; if (ms_hard.install_in_progress) { kbd.rst=TRUE; return; } keydev.ctrl_alt_ret_addr=GetRBP()(I64)+8; KbdPktRead; } 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(0x21,InU8(0x21)&~2); } U0 KbdHndlr(U8 *pkt=NULL) { static U8 last_raw_byte=0; U8 raw_byte; if (pkt) raw_byte = pkt[0]; else FifoU8Rem(kbd.fifo2,&raw_byte); KbdBuildSC(raw_byte,FALSE,&last_raw_byte,&kbd.scan_code); if (raw_byte==0xE0) { if (pkt) raw_byte = pkt[1]; else FifoU8Rem(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.cnt++; 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 KbdMsgsQue() { I64 arg1,arg2,msg_code=MSG_NULL; CTask *task_focus; if (task_focus=sys_focus_task) { while (FifoI64Rem(kbd.scan_code_fifo,&arg2)) { sc_flags_state=arg2; arg1=ScanCode2Char(arg2); if (arg2 & SCF_KEY_UP) { TaskMsg(task_focus,0,MSG_KEY_UP,arg1,arg2,0); key_state[arg1 & 0x7f]=0; // In case shift pressed after key was down // doesn't cover all cases, but most key_state[(arg1 & 0x7f) ^ 0x20]=0; sc_state[arg2 & 0xff]=0; msg_code=MSG_KEY_UP; } else { TaskMsg(task_focus,0,MSG_KEY_DOWN,arg1,arg2,0); key_state[arg1 & 0x7f]=1; sc_state[arg2 & 0xff]=1; msg_code=MSG_KEY_DOWN; } } } return msg_code; } I64 KbdMsEvtTime() {//Timestamp of last key or mouse event. if (ms_hard.timestamp>kbd.timestamp) return ms_hard.timestamp; else return kbd.new_key_timestamp; } public U0 KbdPktInject(U8 *pkt) {// Inject a 1 or 2 byte keyboard packet from an external non-PS/2 source PUSHFD CLI kbd.timestamp=GetTSC; KbdHndlr(pkt); POPFD }