bec4fde71bce9681b4ded13d8090177757ca8f91
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / kbdbios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: kbdbios32.c
5 * PURPOSE: VDM Keyboard 32-bit BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14
15 #include "kbdbios32.h"
16 #include <bios/kbdbios.h>
17 #include "bios32p.h"
18
19 #include "int32.h"
20 #include "cpu/cpu.h" // for EMULATOR_FLAG_ZF
21 #include "io.h"
22 #include "hardware/ps2.h"
23
24 /* PRIVATE VARIABLES **********************************************************/
25
26 static BYTE BiosKeyboardMap[256];
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 static BOOLEAN BiosKbdBufferPush(WORD Data)
31 {
32 /* Get the location of the element after the tail */
33 WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD);
34
35 /* Wrap it around if it's at or beyond the end */
36 if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart;
37
38 /* If it's full, fail */
39 if (NextElement == Bda->KeybdBufferHead)
40 {
41 DPRINT1("BIOS keyboard buffer full.\n");
42 return FALSE;
43 }
44
45 /* Put the value in the queue */
46 *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data;
47 Bda->KeybdBufferTail = NextElement;
48
49 /* Return success */
50 return TRUE;
51 }
52
53 static BOOLEAN BiosKbdBufferTop(LPWORD Data)
54 {
55 /* If it's empty, fail */
56 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
57
58 /* Otherwise, get the value and return success */
59 *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead));
60
61 return TRUE;
62 }
63
64 static BOOLEAN BiosKbdBufferPop(VOID)
65 {
66 /* If it's empty, fail */
67 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
68
69 /* Remove the value from the queue */
70 Bda->KeybdBufferHead += sizeof(WORD);
71
72 /* Check if we are at, or have passed, the end of the buffer */
73 if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd)
74 {
75 /* Return it to the beginning */
76 Bda->KeybdBufferHead = Bda->KeybdBufferStart;
77 }
78
79 /* Return success */
80 return TRUE;
81 }
82
83 static VOID WINAPI BiosKeyboardService(LPWORD Stack)
84 {
85 switch (getAH())
86 {
87 /* Wait for keystroke and read */
88 case 0x00:
89 /* Wait for extended keystroke and read */
90 case 0x10:
91 {
92 WORD Character;
93
94 /* Read the character (and wait if necessary) */
95 if (!BiosKbdBufferTop(&Character))
96 {
97 /* No key available. Set the handler CF to repeat the BOP */
98 setCF(1);
99 break;
100 }
101
102 if (getAH() == 0x00 && LOBYTE(Character) == 0xE0)
103 {
104 /* Clear the extended code */
105 Character &= 0xFF00;
106 }
107
108 BiosKbdBufferPop();
109 setAX(Character);
110 setCF(0);
111
112 break;
113 }
114
115 /* Get keystroke status */
116 case 0x01:
117 /* Get extended keystroke status */
118 case 0x11:
119 {
120 WORD Character;
121
122 if (BiosKbdBufferTop(&Character))
123 {
124 /* There is a character, clear ZF and return it */
125 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
126
127 if (getAH() == 0x01 && LOBYTE(Character) == 0xE0)
128 {
129 /* Clear the extended code */
130 Character &= 0xFF00;
131 }
132
133 setAX(Character);
134 }
135 else
136 {
137 /* No character, set ZF */
138 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
139 }
140
141 break;
142 }
143
144 /* Get shift status */
145 case 0x02:
146 {
147 /* Return the lower byte of the keyboard shift status word */
148 setAL(LOBYTE(Bda->KeybdShiftFlags));
149 break;
150 }
151
152 /* Reserved */
153 case 0x04:
154 {
155 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
156 break;
157 }
158
159 /* Push keystroke */
160 case 0x05:
161 {
162 /* Return 0 if success, 1 if failure */
163 setAL(BiosKbdBufferPush(getCX()) == FALSE);
164 break;
165 }
166
167 /* Get extended shift status */
168 case 0x12:
169 {
170 /*
171 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
172 * but the high byte is organized differently:
173 * the bytes 2 and 3 of the high byte are not the same...
174 */
175 WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF);
176
177 /* Return the extended keyboard shift status word */
178 setAX(KeybdShiftFlags);
179 break;
180 }
181
182 default:
183 {
184 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
185 getAH());
186 }
187 }
188 }
189
190 // Keyboard IRQ 1
191 static VOID WINAPI BiosKeyboardIrq(LPWORD Stack)
192 {
193 static BOOLEAN Extended = FALSE;
194 BOOLEAN SkipScanCode;
195 BYTE ScanCode, VirtualKey;
196 WORD Character;
197
198 /*
199 * Get the scan code from the PS/2 port, then call the
200 * INT 15h, AH=4Fh Keyboard Intercept function to try to
201 * translate the scan code. CF must be set before the call.
202 * In return, if CF is set we continue processing the scan code
203 * stored in AL, and if not, we skip it.
204 */
205 BYTE CF;
206 WORD AX;
207 CF = getCF();
208 AX = getAX();
209
210 setCF(1);
211 setAL(IOReadB(PS2_DATA_PORT));
212 setAH(0x4F);
213 Int32Call(&BiosContext, BIOS_MISC_INTERRUPT);
214
215 /* Retrieve the modified scan code in AL */
216 SkipScanCode = (getCF() == 0);
217 ScanCode = getAL();
218
219 setAX(AX);
220 setCF(CF);
221
222 if (ScanCode == 0xE0)
223 {
224 Extended = TRUE;
225 goto Quit;
226 }
227
228 /* Check whether CF is clear. If so, skip the scan code. */
229 if (SkipScanCode) goto Quit;
230
231 /* Get the corresponding virtual key code */
232 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
233
234 /* Check if this is a key press or release */
235 if (!(ScanCode & (1 << 7)))
236 {
237 /* Key press, set the highest bit */
238 BiosKeyboardMap[VirtualKey] |= (1 << 7);
239
240 switch (VirtualKey)
241 {
242 case VK_NUMLOCK:
243 case VK_CAPITAL:
244 case VK_SCROLL:
245 case VK_INSERT:
246 {
247 /* For toggle keys, toggle the lowest bit in the keyboard map */
248 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
249 break;
250 }
251
252 case VK_CONTROL:
253 case VK_SHIFT:
254 case VK_LSHIFT:
255 case VK_RSHIFT:
256 case VK_MENU:
257 case VK_LMENU:
258 case VK_RMENU:
259 {
260 /* Modifier keys don't go in the buffer */
261 break;
262 }
263
264 default:
265 {
266 Character = Extended ? 0xE0 : 0x00;
267
268 /* If this is not an extended scancode, and ALT isn't held down, find out which character this is */
269 if (!Extended && !(Bda->KeybdShiftFlags & (BDA_KBDFLAG_ALT | BDA_KBDFLAG_LALT | BDA_KBDFLAG_RALT)))
270 {
271 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
272 {
273 /* Not ASCII */
274 Character = 0;
275 }
276 }
277
278 /* Push it onto the BIOS keyboard queue */
279 BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
280 }
281 }
282 }
283 else
284 {
285 /* Key release, unset the highest bit */
286 BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
287 }
288
289 /* Clear the keyboard flags */
290 Bda->KeybdShiftFlags = 0;
291
292 /* Set the appropriate flags based on the state */
293 if (BiosKeyboardMap[VK_SHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
294 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
295 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
296 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
297 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
298 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
299 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
300 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
301 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
302 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
303 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
304 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
305 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
306 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
307 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
308 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
309 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
310
311 /* Clear the extended key flag */
312 Extended = FALSE;
313
314 DPRINT("BiosKeyboardIrq - Character = 0x%X, ScanCode = 0x%X, KeybdShiftFlags = 0x%X\n",
315 Character, ScanCode, Bda->KeybdShiftFlags);
316
317 Quit:
318 PicIRQComplete(Stack);
319 }
320
321 /* PUBLIC FUNCTIONS ***********************************************************/
322
323 BOOLEAN KbdBios32Initialize(VOID)
324 {
325 /* Initialize the common Keyboard BIOS Support Library */
326 if (!KbdBiosInitialize()) return FALSE;
327
328 /* Initialize the BDA */
329 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
330 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
331 Bda->KeybdBufferHead = Bda->KeybdBufferTail = Bda->KeybdBufferStart;
332
333 // FIXME: Fill the keyboard buffer with invalid values for diagnostic purposes...
334 RtlFillMemory(((LPVOID)((ULONG_PTR)Bda + Bda->KeybdBufferStart)),
335 BIOS_KBD_BUFFER_SIZE * sizeof(WORD), 'A');
336
337 /*
338 * Register the BIOS 32-bit Interrupts:
339 * - Software vector handler
340 * - HW vector interrupt
341 */
342 RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService);
343 EnableHwIRQ(1, BiosKeyboardIrq);
344
345 return TRUE;
346 }
347
348 VOID KbdBios32Cleanup(VOID)
349 {
350 /* Cleanup the common Keyboard BIOS Support Library */
351 KbdBiosCleanup();
352 }
353
354 /* EOF */