[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) return FALSE;
39
40 /* Put the value in the queue */
41 *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data;
42 Bda->KeybdBufferTail = NextElement;
43
44 /* Return success */
45 return TRUE;
46 }
47
48 static BOOLEAN BiosKbdBufferTop(LPWORD Data)
49 {
50 /* If it's empty, fail */
51 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
52
53 /* Otherwise, get the value and return success */
54 *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead));
55
56 return TRUE;
57 }
58
59 static BOOLEAN BiosKbdBufferPop(VOID)
60 {
61 /* If it's empty, fail */
62 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE;
63
64 /* Remove the value from the queue */
65 Bda->KeybdBufferHead += sizeof(WORD);
66
67 /* Check if we are at, or have passed, the end of the buffer */
68 if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd)
69 {
70 /* Return it to the beginning */
71 Bda->KeybdBufferHead = Bda->KeybdBufferStart;
72 }
73
74 /* Return success */
75 return TRUE;
76 }
77
78 static WORD BiosPeekCharacter(VOID)
79 {
80 WORD CharacterData = 0;
81
82 /* Get the key from the queue, but don't remove it */
83 if (BiosKbdBufferTop(&CharacterData)) return CharacterData;
84 else return 0xFFFF;
85 }
86
87 static WORD BiosGetCharacter(VOID)
88 {
89 WORD CharacterData = 0;
90
91 /* Check if there is a key available, and if so, remove it from the queue */
92 if (BiosKbdBufferTop(&CharacterData)) BiosKbdBufferPop();
93 else CharacterData = 0xFFFF;
94
95 return CharacterData;
96 }
97
98 static VOID WINAPI BiosKeyboardService(LPWORD Stack)
99 {
100 switch (getAH())
101 {
102 /* Wait for keystroke and read */
103 case 0x00:
104 /* Wait for extended keystroke and read */
105 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
106 {
107 /* Read the character (and wait if necessary) */
108 WORD Character = BiosGetCharacter();
109
110 if (Character == 0xFFFF)
111 {
112 /* No key available. Set the handler CF to repeat the BOP */
113 setCF(1);
114 break;
115 }
116
117 setAX(Character);
118
119 break;
120 }
121
122 /* Get keystroke status */
123 case 0x01:
124 /* Get extended keystroke status */
125 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
126 {
127 WORD Character = BiosPeekCharacter();
128
129 if (Character != 0xFFFF)
130 {
131 /* There is a character, clear ZF and return it */
132 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
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 BYTE ScanCode, VirtualKey;
194 WORD Character;
195
196 /* Get the scan code and virtual key code */
197 ScanCode = IOReadB(PS2_DATA_PORT);
198 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK);
199
200 /* Check if this is a key press or release */
201 if (!(ScanCode & (1 << 7)))
202 {
203 /* Key press */
204 if (VirtualKey == VK_NUMLOCK ||
205 VirtualKey == VK_CAPITAL ||
206 VirtualKey == VK_SCROLL ||
207 VirtualKey == VK_INSERT)
208 {
209 /* For toggle keys, toggle the lowest bit in the keyboard map */
210 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0);
211 }
212
213 /* Set the highest bit */
214 BiosKeyboardMap[VirtualKey] |= (1 << 7);
215
216 /* Find out which character this is */
217 Character = 0;
218 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0)
219 {
220 /* Not ASCII */
221 Character = 0;
222 }
223
224 /* Push it onto the BIOS keyboard queue */
225 BiosKbdBufferPush(MAKEWORD(Character, ScanCode));
226 }
227 else
228 {
229 /* Key release, unset the highest bit */
230 BiosKeyboardMap[VirtualKey] &= ~(1 << 7);
231 }
232
233 /* Clear the keyboard flags */
234 Bda->KeybdShiftFlags = 0;
235
236 /* Set the appropriate flags based on the state */
237 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT;
238 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT;
239 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL;
240 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT;
241 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON;
242 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON;
243 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON;
244 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON;
245 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RALT;
246 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT;
247 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ;
248 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE;
249 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL;
250 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK;
251 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK;
252 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT;
253
254 PicIRQComplete(Stack);
255 }
256
257 /* PUBLIC FUNCTIONS ***********************************************************/
258
259 BOOLEAN KbdBios32Initialize(VOID)
260 {
261 /* Initialize the common Keyboard BIOS Support Library */
262 if (!KbdBiosInitialize()) return FALSE;
263
264 /* Initialize the BDA */
265 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer);
266 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD);
267 Bda->KeybdBufferHead = Bda->KeybdBufferTail = Bda->KeybdBufferStart;
268
269 // FIXME: Fill the keyboard buffer with invalid values, for diagnostic purposes...
270 RtlFillMemory(((LPVOID)((ULONG_PTR)Bda + Bda->KeybdBufferStart)), BIOS_KBD_BUFFER_SIZE * sizeof(WORD), 'A');
271
272 /* Register the BIOS 32-bit Interrupts */
273
274 /* Initialize software vector handlers */
275 RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService);
276
277 /* Set up the HW vector interrupts */
278 EnableHwIRQ(1, BiosKeyboardIrq);
279 // EnableHwIRQ(12, BiosMouseIrq);
280
281 return TRUE;
282 }
283
284 VOID KbdBios32Cleanup(VOID)
285 {
286 /* Cleanup the common Keyboard BIOS Support Library */
287 KbdBiosCleanup();
288 }
289
290 /* EOF */