[NTVDM]
[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: // FIXME: Temporarily do the same as INT 16h, 00h
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 BiosKbdBufferPop();
103 setAX(Character);
104
105 break;
106 }
107
108 /* Get keystroke status */
109 case 0x01:
110 /* Get extended keystroke status */
111 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
112 {
113 WORD Character;
114
115 if (BiosKbdBufferTop(&Character))
116 {
117 /* There is a character, clear ZF and return it */
118 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
119 setAX(Character);
120 }
121 else
122 {
123 /* No character, set ZF */
124 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
125 }
126
127 break;
128 }
129
130 /* Get shift status */
131 case 0x02:
132 {
133 /* Return the lower byte of the keyboard shift status word */
134 setAL(LOBYTE(Bda->KeybdShiftFlags));
135 break;
136 }
137
138 /* Reserved */
139 case 0x04:
140 {
141 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
142 break;
143 }
144
145 /* Push keystroke */
146 case 0x05:
147 {
148 /* Return 0 if success, 1 if failure */
149 setAL(BiosKbdBufferPush(getCX()) == FALSE);
150 break;
151 }
152
153 /* Get extended shift status */
154 case 0x12:
155 {
156 /*
157 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
158 * but the high byte is organized differently:
159 * the bytes 2 and 3 of the high byte are not the same...
160 */
161 WORD KeybdShiftFlags = (Bda->KeybdShiftFlags & 0xF3FF);
162
163 /* Return the extended keyboard shift status word */
164 setAX(KeybdShiftFlags);
165 break;
166 }
167
168 default:
169 {
170 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
171 getAH());
172 }
173 }
174 }
175
176 // Keyboard IRQ 1
177 static VOID WINAPI BiosKeyboardIrq(LPWORD Stack)
178 {
179 BOOLEAN SkipScanCode;
180 BYTE ScanCode, VirtualKey;
181 WORD Character;
182
183 /*
184 * Get the scan code from the PS/2 port, then call the
185 * INT 15h, AH=4Fh Keyboard Intercept function to try to
186 * translate the scan code. CF must be set before the call.
187 * In return, if CF is set we continue processing the scan code
188 * stored in AL, and if not, we skip it.
189 */
190 BYTE CF;
191 WORD AX;
192 CF = getCF();
193 AX = getAX();
194
195 setCF(1);
196 setAL(IOReadB(PS2_DATA_PORT));
197 setAH(0x4F);
198 Int32Call(&BiosContext, BIOS_MISC_INTERRUPT);
199
200 /* Retrieve the modified scan code in AL */
201 SkipScanCode = (getCF() == 0);
202 ScanCode = getAL();
203
204 setAX(AX);
205 setCF(CF);
206
207 /* Check whether CF is clear. If so, skip the scan code. */
208 if (SkipScanCode) goto Quit;
209
210 /* Get the corresponding virtual key code */
211 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
212
213 /* Check if this is a key press or release */
214 if (!(ScanCode & (1 << 7)))
215 {
216 /* Key press, set the highest bit */
217 BiosKeyboardMap[VirtualKey] |= (1 << 7);
218
219 switch (VirtualKey)
220 {
221 case VK_NUMLOCK:
222 case VK_CAPITAL:
223 case VK_SCROLL:
224 case VK_INSERT:
225 {
226 /* For toggle keys, toggle the lowest bit in the keyboard map */
227 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
228 break;
229 }
230
231 case VK_CONTROL:
232 case VK_SHIFT:
233 case VK_LSHIFT:
234 case VK_RSHIFT:
235 case VK_MENU:
236 case VK_LMENU:
237 case VK_RMENU:
238 {
239 /* Modifier keys don't go in the buffer */
240 break;
241 }
242
243 default:
244 {
245 Character = 0;
246
247 /* If ALT isn't held down, find out which character this is */
248 if (!(Bda->KeybdShiftFlags & (BDA_KBDFLAG_ALT | BDA_KBDFLAG_LALT | BDA_KBDFLAG_RALT)))
249 {
250 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
251 {
252 /* Not ASCII */
253 Character = 0;
254 }
255 }
256
257 /* Push it onto the BIOS keyboard queue */
258 BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
259 }
260 }
261 }
262 else
263 {
264 /* Key release, unset the highest bit */
265 BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
266 }
267
268 /* Clear the keyboard flags */
269 Bda->KeybdShiftFlags = 0;
270
271 /* Set the appropriate flags based on the state */
272 if (BiosKeyboardMap[VK_SHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
273 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
274 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
275 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
276 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
277 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
278 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
279 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
280 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
281 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
282 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
283 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
284 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
285 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
286 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
287 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
288 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
289
290 DPRINT("BiosKeyboardIrq - Character = 0x%X, ScanCode = 0x%X, KeybdShiftFlags = 0x%X\n",
291 Character, ScanCode, Bda->KeybdShiftFlags);
292
293 Quit:
294 PicIRQComplete(Stack);
295 }
296
297 /* PUBLIC FUNCTIONS ***********************************************************/
298
299 BOOLEAN KbdBios32Initialize(VOID)
300 {
301 /* Initialize the common Keyboard BIOS Support Library */
302 if (!KbdBiosInitialize()) return FALSE;
303
304 /* Initialize the BDA */
305 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
306 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
307 Bda->KeybdBufferHead = Bda->KeybdBufferTail = Bda->KeybdBufferStart;
308
309 // FIXME: Fill the keyboard buffer with invalid values for diagnostic purposes...
310 RtlFillMemory(((LPVOID)((ULONG_PTR)Bda + Bda->KeybdBufferStart)),
311 BIOS_KBD_BUFFER_SIZE * sizeof(WORD), 'A');
312
313 /*
314 * Register the BIOS 32-bit Interrupts:
315 * - Software vector handler
316 * - HW vector interrupt
317 */
318 RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService);
319 EnableHwIRQ(1, BiosKeyboardIrq);
320
321 return TRUE;
322 }
323
324 VOID KbdBios32Cleanup(VOID)
325 {
326 /* Cleanup the common Keyboard BIOS Support Library */
327 KbdBiosCleanup();
328 }
329
330 /* EOF */