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