github.com/jspc/eggos@v0.5.1-0.20221028160421-556c75c878a5/drivers/kbd/kbd.go (about) 1 package kbd 2 3 import ( 4 "github.com/jspc/eggos/drivers/pic" 5 "github.com/jspc/eggos/kernel/sys" 6 "github.com/jspc/eggos/kernel/trap" 7 ) 8 9 const ( 10 _IRQ_KBD = pic.IRQ_BASE + pic.LINE_KBD 11 ) 12 13 const ( 14 KBSTATP = 0x64 // kbd controller status port(I) 15 KBS_DIB = 0x01 // kbd data in buffer 16 KBDATAP = 0x60 // kbd data port(I) 17 18 NO = 0 19 20 SHIFT = (1 << 0) 21 CTL = (1 << 1) 22 ALT = (1 << 2) 23 24 CAPSLOCK = (1 << 3) 25 NUMLOCK = (1 << 4) 26 SCROLLLOCK = (1 << 5) 27 28 E0ESC = (1 << 6) 29 30 // Special keycodes 31 KEY_HOME = 0xE0 32 KEY_END = 0xE1 33 KEY_UP = 0xE2 34 KEY_DN = 0xE3 35 KEY_LF = 0xE4 36 KEY_RT = 0xE5 37 KEY_PGUP = 0xE6 38 KEY_PGDN = 0xE7 39 KEY_INS = 0xE8 40 KEY_DEL = 0xE9 41 ) 42 43 var ( 44 inputCallback func(byte) 45 keyPressed [255]bool 46 ) 47 48 func ctrl(c byte) byte { 49 return c - '@' 50 } 51 52 var shiftcode = [256]byte{ 53 0x1d: CTL, 54 0x2A: SHIFT, 55 0x36: SHIFT, 56 0x38: ALT, 57 0x9d: CTL, 58 0xB8: ALT, 59 } 60 61 var togglecode = [256]byte{ 62 0x3a: CAPSLOCK, 63 0x45: NUMLOCK, 64 0x46: SCROLLLOCK, 65 } 66 67 var normalmap = [256]byte{ 68 NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 69 '7', '8', '9', '0', '-', '=', '\b', '\t', 70 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 71 'o', 'p', '[', ']', '\n', NO, 'a', 's', 72 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 73 '\'', '`', NO, '\\', 'z', 'x', 'c', 'v', 74 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30 75 NO, ' ', NO, NO, NO, NO, NO, NO, 76 NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 77 '8', '9', '-', '4', '5', '6', '+', '1', 78 '2', '3', '0', '.', NO, NO, NO, NO, // 0x50, 79 0x9c: '\n', // KP_Enter 80 0xB5: '/', // KP_Div 81 0xc8: KEY_UP, 0xd0: KEY_DN, 82 0xC9: KEY_PGUP, 0xD1: KEY_PGDN, 83 0xCB: KEY_LF, 0xCD: KEY_RT, 84 0x97: KEY_HOME, 0xCF: KEY_END, 85 0xD2: KEY_INS, 0xD3: KEY_DEL, 86 } 87 88 var shiftmap = [256]byte{ 89 NO, 033, '!', '@', '#', '$', '%', '^', // 0x00 90 '&', '*', '(', ')', '_', '+', '\b', '\t', 91 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 92 'O', 'P', '{', '}', '\n', NO, 'A', 'S', 93 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 94 '"', '~', NO, '|', 'Z', 'X', 'C', 'V', 95 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30 96 NO, ' ', NO, NO, NO, NO, NO, NO, 97 NO, NO, NO, NO, NO, NO, NO, '7', // 0x40 98 '8', '9', '-', '4', '5', '6', '+', '1', 99 '2', '3', '0', '.', NO, NO, NO, NO, // 0x50 100 0x9C: '\n', // KP_Enter 101 0xB5: '/', // KP_Div 102 0xC8: KEY_UP, 0xD0: KEY_DN, 103 0xC9: KEY_PGUP, 0xD1: KEY_PGDN, 104 0xCB: KEY_LF, 0xCD: KEY_RT, 105 0x97: KEY_HOME, 0xCF: KEY_END, 106 0xD2: KEY_INS, 0xD3: KEY_DEL, 107 } 108 109 var ctlmap = [256]byte{ 110 NO, NO, NO, NO, NO, NO, NO, NO, 111 NO, NO, NO, NO, NO, NO, NO, NO, 112 ctrl('Q'), ctrl('W'), ctrl('E'), ctrl('R'), ctrl('T'), ctrl('Y'), ctrl('U'), ctrl('I'), 113 ctrl('O'), ctrl('P'), NO, NO, '\r', NO, ctrl('A'), ctrl('S'), 114 ctrl('D'), ctrl('F'), ctrl('G'), ctrl('H'), ctrl('J'), ctrl('K'), ctrl('L'), NO, 115 NO, NO, NO, ctrl('\\'), ctrl('Z'), ctrl('X'), ctrl('C'), ctrl('V'), 116 ctrl('B'), ctrl('N'), ctrl('M'), NO, NO, ctrl('/'), NO, NO, 117 0x9C: '\r', // KP_Enter 118 0xB5: ctrl('/'), // KP_Div 119 0xC8: KEY_UP, 0xD0: KEY_DN, 120 0xC9: KEY_PGUP, 0xD1: KEY_PGDN, 121 0xCB: KEY_LF, 0xCD: KEY_RT, 122 0x97: KEY_HOME, 0xCF: KEY_END, 123 0xD2: KEY_INS, 0xD3: KEY_DEL, 124 } 125 126 var ( 127 shift byte 128 charcode = [...][256]byte{ 129 normalmap, shiftmap, ctlmap, ctlmap, 130 } 131 ) 132 133 //go:nosplit 134 func ReadByte() int { 135 var ( 136 st byte 137 data, c byte 138 ) 139 st = sys.Inb(KBSTATP) 140 if st&KBS_DIB == 0 { 141 return -1 142 } 143 // from mouse? 144 if st&0x20 != 0 { 145 return -1 146 } 147 data = byte(sys.Inb(KBDATAP)) 148 149 switch { 150 case data == 0xE0: 151 shift |= E0ESC 152 return 0 153 case data&0x80 != 0: 154 // Key released 155 if shift&E0ESC == 0 { 156 data &= 0x7f 157 } 158 shift &= ^(shiftcode[data] | E0ESC) 159 c = charcode[shift&(CTL|SHIFT)][data] 160 keyPressed[c] = false 161 return 0 162 case shift&E0ESC != 0: 163 data |= 0x80 164 shift &= ^byte(E0ESC) 165 } 166 167 shift |= shiftcode[data] 168 shift ^= togglecode[data] 169 c = charcode[shift&(CTL|SHIFT)][data] 170 if shift&CAPSLOCK != 0 { 171 if 'a' <= c && c <= 'z' { 172 c -= 'a' - 'A' 173 } else if 'A' <= c && c <= 'Z' { 174 c += 'a' - 'A' 175 } 176 } 177 keyPressed[c] = true 178 return int(c) 179 } 180 181 const ( 182 csiUp = "\x1b[A" 183 csiDown = "\x1b[B" 184 csiLeft = "\x1b[D" 185 csiRight = "\x1b[C" 186 ) 187 188 func csiEscape(ch byte) string { 189 switch ch { 190 case KEY_UP: 191 return csiUp 192 case KEY_DN: 193 return csiDown 194 case KEY_LF: 195 return csiLeft 196 case KEY_RT: 197 return csiRight 198 default: 199 return "" 200 } 201 } 202 203 //go:nosplit 204 func intr() { 205 if inputCallback == nil { 206 return 207 } 208 for { 209 ch := ReadByte() 210 if ch <= 0 { 211 break 212 } 213 esc := csiEscape(byte(ch)) 214 if esc == "" { 215 inputCallback(byte(ch)) 216 } else { 217 for i := range esc { 218 inputCallback(esc[i]) 219 } 220 } 221 } 222 pic.EOI(_IRQ_KBD) 223 } 224 225 func OnInput(callback func(byte)) { 226 inputCallback = callback 227 } 228 229 func Pressed(key byte) bool { 230 return keyPressed[key] 231 } 232 233 func Init() { 234 trap.Register(_IRQ_KBD, intr) 235 pic.EnableIRQ(pic.LINE_KBD) 236 }