1 | /* $Id: keyboard.c 69501 2017-10-28 16:12:47Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * PC BIOS - ???
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2006-2017 Oracle Corporation
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License (GPL) as published by the Free Software
|
---|
13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | * --------------------------------------------------------------------
|
---|
17 | *
|
---|
18 | * This code is based on:
|
---|
19 | *
|
---|
20 | * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
|
---|
21 | *
|
---|
22 | * Copyright (C) 2002 MandrakeSoft S.A.
|
---|
23 | *
|
---|
24 | * MandrakeSoft S.A.
|
---|
25 | * 43, rue d'Aboukir
|
---|
26 | * 75002 Paris - France
|
---|
27 | * http://www.linux-mandrake.com/
|
---|
28 | * http://www.mandrakesoft.com/
|
---|
29 | *
|
---|
30 | * This library is free software; you can redistribute it and/or
|
---|
31 | * modify it under the terms of the GNU Lesser General Public
|
---|
32 | * License as published by the Free Software Foundation; either
|
---|
33 | * version 2 of the License, or (at your option) any later version.
|
---|
34 | *
|
---|
35 | * This library is distributed in the hope that it will be useful,
|
---|
36 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
37 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
38 | * Lesser General Public License for more details.
|
---|
39 | *
|
---|
40 | * You should have received a copy of the GNU Lesser General Public
|
---|
41 | * License along with this library; if not, write to the Free Software
|
---|
42 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
43 | *
|
---|
44 | */
|
---|
45 |
|
---|
46 | /*
|
---|
47 | * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
|
---|
48 | * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
|
---|
49 | * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
|
---|
50 | * a choice of LGPL license versions is made available with the language indicating
|
---|
51 | * that LGPLv2 or any later version may be used, or where a choice of which version
|
---|
52 | * of the LGPL is applied is otherwise unspecified.
|
---|
53 | */
|
---|
54 |
|
---|
55 |
|
---|
56 | #include <stdint.h>
|
---|
57 | #include "inlines.h"
|
---|
58 | #include "biosint.h"
|
---|
59 |
|
---|
60 | #if DEBUG_INT16
|
---|
61 | # define BX_DEBUG_INT16(...) BX_DEBUG(__VA_ARGS__)
|
---|
62 | #else
|
---|
63 | # define BX_DEBUG_INT16(...)
|
---|
64 | #endif
|
---|
65 |
|
---|
66 | extern void post(void);
|
---|
67 | #pragma aux post "*";
|
---|
68 |
|
---|
69 | void jmp_post(void);
|
---|
70 | #pragma aux jmp_post = "jmp far ptr post" aborts;
|
---|
71 |
|
---|
72 | extern void eoi_master_pic(void); /* in assembly code */
|
---|
73 | #pragma aux eoi_master_pic "*";
|
---|
74 |
|
---|
75 | /* Manually save/restore BP around invoking user Ctrl-Break handler.
|
---|
76 | * The handler could conceivably clobber BP and the compiler does not
|
---|
77 | * believe us when we say 'modify [bp]' (BP is considered unalterable).
|
---|
78 | */
|
---|
79 | void int_1b(void);
|
---|
80 | #pragma aux int_1b = \
|
---|
81 | "push bp" \
|
---|
82 | "int 1Bh" \
|
---|
83 | "pop bp" \
|
---|
84 | value [bp] modify [bp];
|
---|
85 |
|
---|
86 |
|
---|
87 | #define none 0
|
---|
88 | #define MAX_SCAN_CODE 0x58
|
---|
89 |
|
---|
90 | struct {
|
---|
91 | uint16_t normal;
|
---|
92 | uint16_t shift;
|
---|
93 | uint16_t control;
|
---|
94 | uint16_t alt;
|
---|
95 | uint8_t lock_flags;
|
---|
96 | } static const scan_to_scanascii[MAX_SCAN_CODE + 1] = {
|
---|
97 | { none, none, none, none, none },
|
---|
98 | { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
|
---|
99 | { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
|
---|
100 | { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
|
---|
101 | { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
|
---|
102 | { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
|
---|
103 | { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
|
---|
104 | { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
|
---|
105 | { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
|
---|
106 | { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
|
---|
107 | { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
|
---|
108 | { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
|
---|
109 | { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
|
---|
110 | { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
|
---|
111 | { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
|
---|
112 | { 0x0f09, 0x0f00, none, none, none }, /* tab */
|
---|
113 | { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
|
---|
114 | { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
|
---|
115 | { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
|
---|
116 | { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
|
---|
117 | { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
|
---|
118 | { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
|
---|
119 | { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
|
---|
120 | { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
|
---|
121 | { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
|
---|
122 | { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
|
---|
123 | { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
|
---|
124 | { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
|
---|
125 | { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
|
---|
126 | { none, none, none, none, none }, /* L Ctrl */
|
---|
127 | { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
|
---|
128 | { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
|
---|
129 | { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
|
---|
130 | { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
|
---|
131 | { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
|
---|
132 | { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
|
---|
133 | { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
|
---|
134 | { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
|
---|
135 | { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
|
---|
136 | { 0x273b, 0x273a, none, none, none }, /* ;: */
|
---|
137 | { 0x2827, 0x2822, none, none, none }, /* '" */
|
---|
138 | { 0x2960, 0x297e, none, none, none }, /* `~ */
|
---|
139 | { none, none, none, none, none }, /* L shift */
|
---|
140 | { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
|
---|
141 | { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
|
---|
142 | { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
|
---|
143 | { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
|
---|
144 | { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
|
---|
145 | { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
|
---|
146 | { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
|
---|
147 | { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
|
---|
148 | { 0x332c, 0x333c, none, none, none }, /* ,< */
|
---|
149 | { 0x342e, 0x343e, none, none, none }, /* .> */
|
---|
150 | { 0x352f, 0x353f, none, none, none }, /* /? */
|
---|
151 | { none, none, none, none, none }, /* R Shift */
|
---|
152 | { 0x372a, 0x372a, none, none, none }, /* * */
|
---|
153 | { none, none, none, none, none }, /* L Alt */
|
---|
154 | { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
|
---|
155 | { none, none, none, none, none }, /* caps lock */
|
---|
156 | { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
|
---|
157 | { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
|
---|
158 | { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
|
---|
159 | { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
|
---|
160 | { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
|
---|
161 | { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
|
---|
162 | { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
|
---|
163 | { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
|
---|
164 | { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
|
---|
165 | { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
|
---|
166 | { none, none, none, none, none }, /* Num Lock */
|
---|
167 | { none, none, none, none, none }, /* Scroll Lock */
|
---|
168 | { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
|
---|
169 | { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
|
---|
170 | { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
|
---|
171 | { 0x4a2d, 0x4a2d, none, none, none }, /* - */
|
---|
172 | { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
|
---|
173 | { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
|
---|
174 | { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
|
---|
175 | { 0x4e2b, 0x4e2b, none, none, none }, /* + */
|
---|
176 | { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
|
---|
177 | { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
|
---|
178 | { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
|
---|
179 | { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
|
---|
180 | { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
|
---|
181 | { none, none, none, none, none },
|
---|
182 | { none, none, none, none, none },
|
---|
183 | { 0x565c, 0x567c, none, none, none }, /* \| */
|
---|
184 | { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
|
---|
185 | { 0x8600, 0x8800, 0x8a00, 0x8c00, none } /* F12 */
|
---|
186 | };
|
---|
187 |
|
---|
188 |
|
---|
189 | /* Keyboard initialization. */
|
---|
190 |
|
---|
191 | //--------------------------------------------------------------------------
|
---|
192 | // keyboard_panic
|
---|
193 | //--------------------------------------------------------------------------
|
---|
194 | void keyboard_panic(uint16_t status)
|
---|
195 | {
|
---|
196 | // If you're getting a 993 keyboard panic here,
|
---|
197 | // please see the comment in keyboard_init
|
---|
198 |
|
---|
199 | BX_PANIC("Keyboard error:%u\n",status);
|
---|
200 | }
|
---|
201 |
|
---|
202 |
|
---|
203 | //--------------------------------------------------------------------------
|
---|
204 | // keyboard_init
|
---|
205 | //--------------------------------------------------------------------------
|
---|
206 | // this file is based on LinuxBIOS implementation of keyboard.c
|
---|
207 | // could convert to #asm to gain space
|
---|
208 | void BIOSCALL keyboard_init(void)
|
---|
209 | {
|
---|
210 | uint16_t max;
|
---|
211 |
|
---|
212 | /* ------------------- controller side ----------------------*/
|
---|
213 | /* send cmd = 0xAA, self test 8042 */
|
---|
214 | outb(0x64, 0xaa);
|
---|
215 |
|
---|
216 | /* Wait until buffer is empty */
|
---|
217 | max=0xffff;
|
---|
218 | while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
|
---|
219 | if (max==0x0) keyboard_panic(00);
|
---|
220 |
|
---|
221 | /* Wait for data */
|
---|
222 | max=0xffff;
|
---|
223 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
|
---|
224 | if (max==0x0) keyboard_panic(01);
|
---|
225 |
|
---|
226 | /* read self-test result, 0x55 should be returned from 0x60 */
|
---|
227 | if ((inb(0x60) != 0x55)){
|
---|
228 | keyboard_panic(991);
|
---|
229 | }
|
---|
230 |
|
---|
231 | /* send cmd = 0xAB, keyboard interface test */
|
---|
232 | outb(0x64,0xab);
|
---|
233 |
|
---|
234 | /* Wait until buffer is empty */
|
---|
235 | max=0xffff;
|
---|
236 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
|
---|
237 | if (max==0x0) keyboard_panic(10);
|
---|
238 |
|
---|
239 | /* Wait for data */
|
---|
240 | max=0xffff;
|
---|
241 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
|
---|
242 | if (max==0x0) keyboard_panic(11);
|
---|
243 |
|
---|
244 | /* read keyboard interface test result, */
|
---|
245 | /* 0x00 should be returned form 0x60 */
|
---|
246 | if ((inb(0x60) != 0x00)) {
|
---|
247 | keyboard_panic(992);
|
---|
248 | }
|
---|
249 |
|
---|
250 | /* ------------------- keyboard side ------------------------*/
|
---|
251 | /* reset keyboard and self test (keyboard side) */
|
---|
252 | /* also enables the keyboard interface */
|
---|
253 | outb(0x60, 0xff);
|
---|
254 |
|
---|
255 | /* Wait until buffer is empty */
|
---|
256 | max=0xffff;
|
---|
257 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
|
---|
258 | if (max==0x0) keyboard_panic(20);
|
---|
259 |
|
---|
260 | /* Wait for data */
|
---|
261 | max=0xffff;
|
---|
262 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
|
---|
263 | if (max==0x0) keyboard_panic(21);
|
---|
264 |
|
---|
265 | /* keyboard should return ACK */
|
---|
266 | if ((inb(0x60) != 0xfa)) {
|
---|
267 | keyboard_panic(993);
|
---|
268 | }
|
---|
269 |
|
---|
270 | /* Wait for reset to complete */
|
---|
271 | while ( (inb(0x64) & 0x01) == 0 ) outb(0x80, 0x31);
|
---|
272 |
|
---|
273 | if ((inb(0x60) != 0xaa && inb(0x60) != 0xaa)) {
|
---|
274 | keyboard_panic(994);
|
---|
275 | }
|
---|
276 |
|
---|
277 | /* Disable keyboard */
|
---|
278 | outb(0x60, 0xf5);
|
---|
279 |
|
---|
280 | /* Wait until buffer is empty */
|
---|
281 | max=0xffff;
|
---|
282 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
|
---|
283 | if (max==0x0) keyboard_panic(40);
|
---|
284 |
|
---|
285 | /* Wait for data */
|
---|
286 | max=0xffff;
|
---|
287 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
|
---|
288 | if (max==0x0) keyboard_panic(41);
|
---|
289 |
|
---|
290 | /* keyboard should return ACK */
|
---|
291 | if ((inb(0x60) != 0xfa)) {
|
---|
292 | keyboard_panic(995);
|
---|
293 | }
|
---|
294 |
|
---|
295 | /* Write Keyboard Mode */
|
---|
296 | outb(0x64, 0x60);
|
---|
297 |
|
---|
298 | /* Wait until buffer is empty */
|
---|
299 | max=0xffff;
|
---|
300 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
|
---|
301 | if (max==0x0) keyboard_panic(50);
|
---|
302 |
|
---|
303 | /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
|
---|
304 | outb(0x60, 0x65);
|
---|
305 |
|
---|
306 | /* Wait until buffer is empty */
|
---|
307 | max=0xffff;
|
---|
308 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
|
---|
309 | if (max==0x0) keyboard_panic(60);
|
---|
310 |
|
---|
311 | /* Enable keyboard */
|
---|
312 | outb(0x60, 0xf4);
|
---|
313 |
|
---|
314 | /* Wait until buffer is empty */
|
---|
315 | max=0xffff;
|
---|
316 | while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
|
---|
317 | if (max==0x0) keyboard_panic(70);
|
---|
318 |
|
---|
319 | /* Wait for data */
|
---|
320 | max=0xffff;
|
---|
321 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
|
---|
322 | if (max==0x0) keyboard_panic(70);
|
---|
323 |
|
---|
324 | /* keyboard should return ACK */
|
---|
325 | if ((inb(0x60) != 0xfa)) {
|
---|
326 | keyboard_panic(996);
|
---|
327 | }
|
---|
328 |
|
---|
329 | /* Enable aux interface */
|
---|
330 | outb(0x64,0xa8);
|
---|
331 |
|
---|
332 | /* While we're here, disable the A20 gate. Required for
|
---|
333 | * compatibility with the IBM PC and DOS.
|
---|
334 | */
|
---|
335 | set_enable_a20(0);
|
---|
336 | }
|
---|
337 |
|
---|
338 |
|
---|
339 | unsigned int enqueue_key(uint8_t scan_code, uint8_t ascii_code)
|
---|
340 | {
|
---|
341 | uint16_t buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
|
---|
342 |
|
---|
343 | #if BX_CPU < 2
|
---|
344 | buffer_start = 0x001E;
|
---|
345 | buffer_end = 0x003E;
|
---|
346 | #else
|
---|
347 | buffer_start = read_word(0x0040, 0x0080);
|
---|
348 | buffer_end = read_word(0x0040, 0x0082);
|
---|
349 | #endif
|
---|
350 |
|
---|
351 | buffer_head = read_word(0x0040, 0x001A);
|
---|
352 | buffer_tail = read_word(0x0040, 0x001C);
|
---|
353 |
|
---|
354 | temp_tail = buffer_tail;
|
---|
355 | buffer_tail += 2;
|
---|
356 | if (buffer_tail >= buffer_end)
|
---|
357 | buffer_tail = buffer_start;
|
---|
358 |
|
---|
359 | if (buffer_tail == buffer_head)
|
---|
360 | return(0);
|
---|
361 |
|
---|
362 | write_byte(0x0040, temp_tail, ascii_code);
|
---|
363 | write_byte(0x0040, temp_tail+1, scan_code);
|
---|
364 | write_word(0x0040, 0x001C, buffer_tail);
|
---|
365 | return(1);
|
---|
366 | }
|
---|
367 |
|
---|
368 |
|
---|
369 | /* Keyboard hardware interrupt handler. */
|
---|
370 | /// @todo should this be declared as taking arguments at all?
|
---|
371 | void BIOSCALL int09_function(uint16_t ES, uint16_t DI, uint16_t SI, uint16_t BP, uint16_t SP,
|
---|
372 | uint16_t BX, uint16_t DX, uint16_t CX, uint16_t AX)
|
---|
373 | {
|
---|
374 | uint8_t scancode, asciicode, shift_flags;
|
---|
375 | uint8_t mf2_flags, mf2_state, flag;
|
---|
376 |
|
---|
377 | //
|
---|
378 | // DS has been set to F000 before call
|
---|
379 | //
|
---|
380 |
|
---|
381 |
|
---|
382 | scancode = GET_AL();
|
---|
383 |
|
---|
384 | if (scancode == 0) {
|
---|
385 | BX_INFO("KBD: int09 handler: AL=0\n");
|
---|
386 | return;
|
---|
387 | }
|
---|
388 |
|
---|
389 | mf2_flags = read_byte(0x0040, 0x18);
|
---|
390 | mf2_state = read_byte(0x0040, 0x96);
|
---|
391 | shift_flags = read_byte(0x0040, 0x17);
|
---|
392 | asciicode = 0;
|
---|
393 |
|
---|
394 | switch (scancode) {
|
---|
395 | case 0x3a: /* Caps Lock press */
|
---|
396 | shift_flags ^= 0x40;
|
---|
397 | write_byte(0x0040, 0x17, shift_flags);
|
---|
398 | mf2_flags |= 0x40;
|
---|
399 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
400 | break;
|
---|
401 | case 0xba: /* Caps Lock release */
|
---|
402 | mf2_flags &= ~0x40;
|
---|
403 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
404 | break;
|
---|
405 |
|
---|
406 | case 0x2a: /* L Shift press */
|
---|
407 | case 0xaa: /* L Shift release */
|
---|
408 | case 0x36: /* R Shift press */
|
---|
409 | case 0xb6: /* R Shift release */
|
---|
410 | /* If this was an extended (i.e. faked) key, leave flags alone. */
|
---|
411 | if (!(mf2_state & 0x02)) {
|
---|
412 | flag = (scancode & 0x7f) == 0x2a ? 0x02 : 0x01;
|
---|
413 | if (scancode & 0x80)
|
---|
414 | shift_flags &= ~flag;
|
---|
415 | else
|
---|
416 | shift_flags |= flag;
|
---|
417 | write_byte(0x0040, 0x17, shift_flags);
|
---|
418 | }
|
---|
419 | break;
|
---|
420 |
|
---|
421 | case 0x1d: /* Ctrl press */
|
---|
422 | if ((mf2_state & 0x01) == 0) {
|
---|
423 | shift_flags |= 0x04;
|
---|
424 | write_byte(0x0040, 0x17, shift_flags);
|
---|
425 | if (mf2_state & 0x02) {
|
---|
426 | mf2_state |= 0x04;
|
---|
427 | write_byte(0x0040, 0x96, mf2_state);
|
---|
428 | } else {
|
---|
429 | mf2_flags |= 0x01;
|
---|
430 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
431 | }
|
---|
432 | }
|
---|
433 | break;
|
---|
434 | case 0x9d: /* Ctrl release */
|
---|
435 | if ((mf2_state & 0x01) == 0) {
|
---|
436 | shift_flags &= ~0x04;
|
---|
437 | write_byte(0x0040, 0x17, shift_flags);
|
---|
438 | if (mf2_state & 0x02) {
|
---|
439 | mf2_state &= ~0x04;
|
---|
440 | write_byte(0x0040, 0x96, mf2_state);
|
---|
441 | } else {
|
---|
442 | mf2_flags &= ~0x01;
|
---|
443 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
444 | }
|
---|
445 | }
|
---|
446 | break;
|
---|
447 |
|
---|
448 | case 0x38: /* Alt press */
|
---|
449 | shift_flags |= 0x08;
|
---|
450 | write_byte(0x0040, 0x17, shift_flags);
|
---|
451 | if (mf2_state & 0x02) {
|
---|
452 | mf2_state |= 0x08;
|
---|
453 | write_byte(0x0040, 0x96, mf2_state);
|
---|
454 | } else {
|
---|
455 | mf2_flags |= 0x02;
|
---|
456 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
457 | }
|
---|
458 | break;
|
---|
459 | case 0xb8: /* Alt release */
|
---|
460 | shift_flags &= ~0x08;
|
---|
461 | write_byte(0x0040, 0x17, shift_flags);
|
---|
462 | if (mf2_state & 0x02) {
|
---|
463 | mf2_state &= ~0x08;
|
---|
464 | write_byte(0x0040, 0x96, mf2_state);
|
---|
465 | } else {
|
---|
466 | mf2_flags &= ~0x02;
|
---|
467 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
468 | }
|
---|
469 | break;
|
---|
470 |
|
---|
471 | case 0x45: /* Num Lock/Pause press */
|
---|
472 | if ((mf2_state & 0x03) == 0) {
|
---|
473 | /* Num Lock */
|
---|
474 | mf2_flags |= 0x20;
|
---|
475 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
476 | shift_flags ^= 0x20;
|
---|
477 | write_byte(0x0040, 0x17, shift_flags);
|
---|
478 | } else {
|
---|
479 | /* Pause */
|
---|
480 | mf2_flags |= 0x08; /* Set the suspend flag */
|
---|
481 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
482 |
|
---|
483 | /* Enable keyboard and send EOI. */
|
---|
484 | outp(0x64, 0xae);
|
---|
485 | eoi_master_pic();
|
---|
486 |
|
---|
487 | while (read_byte(0x0040, 0x18) & 0x08)
|
---|
488 | ; /* Hold on and wait... */
|
---|
489 |
|
---|
490 | /// @todo We will send EOI again (and enable keyboard) on the way out; we shouldn't
|
---|
491 | }
|
---|
492 | break;
|
---|
493 | case 0xc5: /* Num Lock/Pause release */
|
---|
494 | if ((mf2_state & 0x03) == 0) {
|
---|
495 | mf2_flags &= ~0x20;
|
---|
496 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
497 | }
|
---|
498 | break;
|
---|
499 |
|
---|
500 | case 0x46: /* Scroll Lock/Break press */
|
---|
501 | if (mf2_state & 0x02) { /* E0 prefix? */
|
---|
502 | /* Zap the keyboard buffer. */
|
---|
503 | write_word(0x0040, 0x001c, read_word(0x0040, 0x001a));
|
---|
504 |
|
---|
505 | write_byte(0x0040, 0x71, 0x80); /* Set break flag */
|
---|
506 | outp(0x64, 0xae); /* Enable keyboard */
|
---|
507 | int_1b(); /* Invoke user handler */
|
---|
508 | enqueue_key(0, 0); /* Dummy key press*/
|
---|
509 | } else {
|
---|
510 | mf2_flags |= 0x10;
|
---|
511 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
512 | shift_flags ^= 0x10;
|
---|
513 | write_byte(0x0040, 0x17, shift_flags);
|
---|
514 | }
|
---|
515 | break;
|
---|
516 |
|
---|
517 | case 0xc6: /* Scroll Lock/Break release */
|
---|
518 | if (!(mf2_state & 0x02)) { /* Only if no E0 prefix */
|
---|
519 | mf2_flags &= ~0x10;
|
---|
520 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
521 | }
|
---|
522 | break;
|
---|
523 |
|
---|
524 | case 0x54: /* SysRq press */
|
---|
525 | if (!(mf2_flags & 0x04)) { /* If not already down */
|
---|
526 | mf2_flags |= 0x04;
|
---|
527 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
528 | /// @todo EOI/enable kbd/enable interrupts/call INT 15h/8500h
|
---|
529 | }
|
---|
530 | break;
|
---|
531 |
|
---|
532 | case 0xd4: /* SysRq release */
|
---|
533 | mf2_flags &= ~0x04;
|
---|
534 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
535 | /// @todo EOI/enable kbd/enable interrupts/call INT 15h/8501h
|
---|
536 | break;
|
---|
537 |
|
---|
538 | case 0x53: /* Del press */
|
---|
539 | if ((shift_flags & 0x0c) == 0x0c) {
|
---|
540 | /* Indicate a warm boot. */
|
---|
541 | write_word(0x0040,0x0072, 0x1234);
|
---|
542 | jmp_post();
|
---|
543 | }
|
---|
544 | /* fall through */
|
---|
545 |
|
---|
546 | default:
|
---|
547 | /* Check if suspend flag set. */
|
---|
548 | if (mf2_flags & 0x08) {
|
---|
549 | /* Pause had been pressed. Clear suspend flag and do nothing. */
|
---|
550 | mf2_flags &= ~0x08;
|
---|
551 | write_byte(0x0040, 0x18, mf2_flags);
|
---|
552 | return;
|
---|
553 | }
|
---|
554 |
|
---|
555 | if (scancode & 0x80) {
|
---|
556 | /* Set ack/resend flags if appropriate. */
|
---|
557 | if (scancode == 0xFA) {
|
---|
558 | flag = read_byte(0x0040, 0x97) | 0x10;
|
---|
559 | write_byte(0x0040, 0x97, flag);
|
---|
560 | } else if (scancode == 0xFE) {
|
---|
561 | flag = read_byte(0x0040, 0x97) | 0x20;
|
---|
562 | write_byte(0x0040, 0x97, flag);
|
---|
563 | }
|
---|
564 | break; /* toss key releases ... */
|
---|
565 | }
|
---|
566 | if (scancode > MAX_SCAN_CODE) {
|
---|
567 | BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
|
---|
568 | return;
|
---|
569 | }
|
---|
570 | if (shift_flags & 0x08) { /* ALT */
|
---|
571 | asciicode = scan_to_scanascii[scancode].alt;
|
---|
572 | scancode = scan_to_scanascii[scancode].alt >> 8;
|
---|
573 | } else if (shift_flags & 0x04) { /* CONTROL */
|
---|
574 | asciicode = scan_to_scanascii[scancode].control;
|
---|
575 | scancode = scan_to_scanascii[scancode].control >> 8;
|
---|
576 | } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) {
|
---|
577 | /* extended keys handling */
|
---|
578 | asciicode = 0xe0;
|
---|
579 | scancode = scan_to_scanascii[scancode].normal >> 8;
|
---|
580 | } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
|
---|
581 | /* check if lock state should be ignored
|
---|
582 | * because a SHIFT key are pressed */
|
---|
583 |
|
---|
584 | if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
|
---|
585 | asciicode = scan_to_scanascii[scancode].normal;
|
---|
586 | scancode = scan_to_scanascii[scancode].normal >> 8;
|
---|
587 | } else {
|
---|
588 | asciicode = scan_to_scanascii[scancode].shift;
|
---|
589 | scancode = scan_to_scanascii[scancode].shift >> 8;
|
---|
590 | }
|
---|
591 | } else {
|
---|
592 | /* check if lock is on */
|
---|
593 | if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
|
---|
594 | asciicode = scan_to_scanascii[scancode].shift;
|
---|
595 | scancode = scan_to_scanascii[scancode].shift >> 8;
|
---|
596 | } else {
|
---|
597 | asciicode = scan_to_scanascii[scancode].normal;
|
---|
598 | scancode = scan_to_scanascii[scancode].normal >> 8;
|
---|
599 | }
|
---|
600 | }
|
---|
601 | if (scancode==0 && asciicode==0) {
|
---|
602 | BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
|
---|
603 | }
|
---|
604 | enqueue_key(scancode, asciicode);
|
---|
605 | break;
|
---|
606 | }
|
---|
607 | if ((scancode & 0x7f) != 0x1d) {
|
---|
608 | mf2_state &= ~0x01;
|
---|
609 | }
|
---|
610 | mf2_state &= ~0x02;
|
---|
611 | write_byte(0x0040, 0x96, mf2_state);
|
---|
612 | }
|
---|
613 |
|
---|
614 | unsigned int dequeue_key(uint8_t __far *scan_code, uint8_t __far *ascii_code, unsigned incr)
|
---|
615 | {
|
---|
616 | uint16_t buffer_start, buffer_end, buffer_head, buffer_tail;
|
---|
617 | uint8_t acode, scode;
|
---|
618 |
|
---|
619 | #if BX_CPU < 2
|
---|
620 | buffer_start = 0x001E;
|
---|
621 | buffer_end = 0x003E;
|
---|
622 | #else
|
---|
623 | buffer_start = read_word(0x0040, 0x0080);
|
---|
624 | buffer_end = read_word(0x0040, 0x0082);
|
---|
625 | #endif
|
---|
626 |
|
---|
627 | buffer_head = read_word(0x0040, 0x001a);
|
---|
628 | buffer_tail = read_word(0x0040, 0x001c);
|
---|
629 |
|
---|
630 | if (buffer_head != buffer_tail) {
|
---|
631 | acode = read_byte(0x0040, buffer_head);
|
---|
632 | scode = read_byte(0x0040, buffer_head+1);
|
---|
633 | *ascii_code = acode;
|
---|
634 | *scan_code = scode;
|
---|
635 | BX_DEBUG_INT16("dequeue_key: ascii=%02x scan=%02x \n", acode, scode);
|
---|
636 |
|
---|
637 | if (incr) {
|
---|
638 | buffer_head += 2;
|
---|
639 | if (buffer_head >= buffer_end)
|
---|
640 | buffer_head = buffer_start;
|
---|
641 | write_word(0x0040, 0x001a, buffer_head);
|
---|
642 | }
|
---|
643 | return(1);
|
---|
644 | }
|
---|
645 | else {
|
---|
646 | return(0);
|
---|
647 | }
|
---|
648 | }
|
---|
649 |
|
---|
650 |
|
---|
651 | /// @todo move somewhere else?
|
---|
652 | #define AX r.gr.u.r16.ax
|
---|
653 | #define BX r.gr.u.r16.bx
|
---|
654 | #define CX r.gr.u.r16.cx
|
---|
655 | #define DX r.gr.u.r16.dx
|
---|
656 | #define SI r.gr.u.r16.si
|
---|
657 | #define DI r.gr.u.r16.di
|
---|
658 | #define BP r.gr.u.r16.bp
|
---|
659 | #define SP r.gr.u.r16.sp
|
---|
660 | #define FLAGS r.ra.flags.u.r16.flags
|
---|
661 | #define IFLGS r.ifl
|
---|
662 |
|
---|
663 | /* Interrupt 16h service implementation. */
|
---|
664 |
|
---|
665 | void BIOSCALL int16_function(volatile kbd_regs_t r)
|
---|
666 | {
|
---|
667 | uint8_t scan_code, ascii_code, shift_flags, led_flags, count;
|
---|
668 | uint16_t kbd_code, max;
|
---|
669 |
|
---|
670 | BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
|
---|
671 |
|
---|
672 | shift_flags = read_byte(0x0040, 0x17);
|
---|
673 | led_flags = read_byte(0x0040, 0x97);
|
---|
674 | if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) {
|
---|
675 | int_disable(); /// @todo interrupts should be disabled already??
|
---|
676 | outb(0x60, 0xed);
|
---|
677 | while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21);
|
---|
678 | if ((inb(0x60) == 0xfa)) {
|
---|
679 | led_flags &= 0xc8;
|
---|
680 | led_flags |= ((shift_flags >> 4) & 0x07);
|
---|
681 | outb(0x60, led_flags & 0x07);
|
---|
682 | while ((inb(0x64) & 0x01) == 0)
|
---|
683 | outb(0x80, 0x21);
|
---|
684 | inb(0x60);
|
---|
685 | write_byte(0x0040, 0x97, led_flags);
|
---|
686 | }
|
---|
687 | int_enable();
|
---|
688 | }
|
---|
689 |
|
---|
690 | switch (GET_AH()) {
|
---|
691 | case 0x00: /* read keyboard input */
|
---|
692 | if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
|
---|
693 | BX_PANIC("KBD: int16h: out of keyboard input\n");
|
---|
694 | }
|
---|
695 | if (scan_code !=0 && ascii_code == 0xF0)
|
---|
696 | ascii_code = 0;
|
---|
697 | else if (ascii_code == 0xE0)
|
---|
698 | ascii_code = 0;
|
---|
699 | AX = (scan_code << 8) | ascii_code;
|
---|
700 | break;
|
---|
701 |
|
---|
702 | case 0x01: /* check keyboard status */
|
---|
703 | /* Enable interrupts, preserve most flags. Some callers depend on that! */
|
---|
704 | FLAGS = IFLGS;
|
---|
705 | if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
|
---|
706 | SET_ZF();
|
---|
707 | return;
|
---|
708 | }
|
---|
709 | if (scan_code !=0 && ascii_code == 0xF0)
|
---|
710 | ascii_code = 0;
|
---|
711 | else if (ascii_code == 0xE0)
|
---|
712 | ascii_code = 0;
|
---|
713 | AX = (scan_code << 8) | ascii_code;
|
---|
714 | CLEAR_ZF();
|
---|
715 | break;
|
---|
716 |
|
---|
717 | case 0x02: /* get shift flag status */
|
---|
718 | shift_flags = read_byte(0x0040, 0x17);
|
---|
719 | SET_AL(shift_flags);
|
---|
720 | break;
|
---|
721 |
|
---|
722 | case 0x05: /* store key-stroke into buffer */
|
---|
723 | if ( !enqueue_key(GET_CH(), GET_CL()) ) {
|
---|
724 | SET_AL(1);
|
---|
725 | }
|
---|
726 | else {
|
---|
727 | SET_AL(0);
|
---|
728 | }
|
---|
729 | break;
|
---|
730 |
|
---|
731 | case 0x09: /* GET KEYBOARD FUNCTIONALITY */
|
---|
732 | // bit Bochs Description
|
---|
733 | // 7 0 reserved
|
---|
734 | // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support)
|
---|
735 | // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support)
|
---|
736 | // 4 1 INT 16/AH=0Ah supported
|
---|
737 | // 3 0 INT 16/AX=0306h supported
|
---|
738 | // 2 0 INT 16/AX=0305h supported
|
---|
739 | // 1 0 INT 16/AX=0304h supported
|
---|
740 | // 0 0 INT 16/AX=0300h supported
|
---|
741 | //
|
---|
742 | SET_AL(0x30);
|
---|
743 | break;
|
---|
744 |
|
---|
745 | case 0x0A: /* GET KEYBOARD ID */
|
---|
746 | count = 2;
|
---|
747 | kbd_code = 0x0;
|
---|
748 | /// @todo Might be better to just mask the KB interrupt
|
---|
749 | int_disable();
|
---|
750 | outb(0x60, 0xf2);
|
---|
751 | /* Wait for data */
|
---|
752 | max=0xffff;
|
---|
753 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) )
|
---|
754 | inb(0x80);
|
---|
755 | if (max>0x0) {
|
---|
756 | if ((inb(0x60) == 0xfa)) {
|
---|
757 | do {
|
---|
758 | max=0xffff;
|
---|
759 | while ( ((inb(0x64) & 0x01) == 0) && (--max>0) )
|
---|
760 | inb(0x80);
|
---|
761 | if (max>0x0) {
|
---|
762 | kbd_code >>= 8;
|
---|
763 | kbd_code |= (inb(0x60) << 8);
|
---|
764 | }
|
---|
765 | } while (--count>0);
|
---|
766 | }
|
---|
767 | }
|
---|
768 | BX=kbd_code;
|
---|
769 | break;
|
---|
770 |
|
---|
771 | case 0x10: /* read MF-II keyboard input */
|
---|
772 | if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
|
---|
773 | BX_PANIC("KBD: int16h: out of keyboard input\n");
|
---|
774 | }
|
---|
775 | if (scan_code !=0 && ascii_code == 0xF0)
|
---|
776 | ascii_code = 0;
|
---|
777 | AX = (scan_code << 8) | ascii_code;
|
---|
778 | break;
|
---|
779 |
|
---|
780 | case 0x11: /* check MF-II keyboard status */
|
---|
781 | /* Enable interrupts, preserve most flags. Some callers depend on that! */
|
---|
782 | FLAGS = IFLGS;
|
---|
783 | if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
|
---|
784 | SET_ZF();
|
---|
785 | return;
|
---|
786 | }
|
---|
787 | if (scan_code !=0 && ascii_code == 0xF0)
|
---|
788 | ascii_code = 0;
|
---|
789 | AX = (scan_code << 8) | ascii_code;
|
---|
790 | CLEAR_ZF();
|
---|
791 | break;
|
---|
792 |
|
---|
793 | case 0x12: /* get extended keyboard status */
|
---|
794 | shift_flags = read_byte(0x0040, 0x17);
|
---|
795 | SET_AL(shift_flags);
|
---|
796 | shift_flags = read_byte(0x0040, 0x18) & 0x73;
|
---|
797 | shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
|
---|
798 | SET_AH(shift_flags);
|
---|
799 | BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
|
---|
800 | break;
|
---|
801 |
|
---|
802 | case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
|
---|
803 | SET_AH(0x80); // function int16 ah=0x10-0x12 supported
|
---|
804 | break;
|
---|
805 |
|
---|
806 | case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
|
---|
807 | // don't change AH : function int16 ah=0x20-0x22 NOT supported
|
---|
808 | break;
|
---|
809 |
|
---|
810 | /// @todo what's the point of handling this??
|
---|
811 | #if 0
|
---|
812 | case 0x6F:
|
---|
813 | if (GET_AL() == 0x08)
|
---|
814 | SET_AH(0x02); // unsupported, aka normal keyboard
|
---|
815 | #endif
|
---|
816 |
|
---|
817 | default:
|
---|
818 | BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
|
---|
819 | BX_INFO("AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
|
---|
820 | }
|
---|
821 | BX_DEBUG_INT16("int16ex: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
|
---|
822 | }
|
---|