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