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