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