[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / hardware / ps2.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: ps2.c
5 * PURPOSE: PS/2 controller emulation
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 "io.h"
15 #include "ps2.h"
16 #include "pic.h"
17 #include "mouse.h"
18 #include "../bios/bios32/moubios32.h"
19
20 /* PRIVATE VARIABLES **********************************************************/
21
22 static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE];
23 static BOOLEAN KeyboardQueueEmpty = TRUE;
24 static UINT KeyboardQueueStart = 0;
25 static UINT KeyboardQueueEnd = 0;
26 static BYTE KeyboardData = 0, KeyboardResponse = 0;
27 static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE;
28 static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG;
29 static HANDLE QueueMutex = NULL;
30
31 /* PRIVATE FUNCTIONS **********************************************************/
32
33 static BYTE WINAPI PS2ReadPort(ULONG Port)
34 {
35 if (Port == PS2_CONTROL_PORT)
36 {
37 BYTE Status = 0;
38
39 /* Set the first bit if the data can be read */
40 if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
41
42 /* Always set bit 2 */
43 Status |= 1 << 2;
44
45 /* Set bit 3 if the next byte goes to the controller */
46 if (KeyboardWriteResponse) Status |= 1 << 3;
47
48 return Status;
49 }
50 else if (Port == PS2_DATA_PORT)
51 {
52 /* If there was a response byte from the controller, return it */
53 if (KeyboardReadResponse)
54 {
55 KeyboardReadResponse = FALSE;
56 KeyboardData = KeyboardResponse;
57 }
58
59 return KeyboardData;
60 }
61 else return 0;
62 }
63
64 static VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
65 {
66 if (Port == PS2_CONTROL_PORT)
67 {
68 switch (Data)
69 {
70 /* Read configuration byte */
71 case 0x20:
72 {
73 KeyboardResponse = KeyboardConfig;
74 KeyboardReadResponse = TRUE;
75 break;
76 }
77
78 /* Write configuration byte */
79 case 0x60:
80 /* Write controller output port */
81 case 0xD1:
82 /* Write keyboard output buffer */
83 case 0xD2:
84 /* Write mouse output buffer */
85 case 0xD3:
86 /* Write mouse input buffer */
87 case 0xD4:
88 {
89 /* These commands require a response */
90 KeyboardResponse = Data;
91 KeyboardWriteResponse = TRUE;
92 break;
93 }
94
95 /* Disable mouse */
96 case 0xA7:
97 {
98 // TODO: Not implemented
99 break;
100 }
101
102 /* Enable mouse */
103 case 0xA8:
104 {
105 // TODO: Not implemented
106 break;
107 }
108
109 /* Test mouse port */
110 case 0xA9:
111 {
112 KeyboardResponse = 0;
113 KeyboardReadResponse = TRUE;
114 break;
115 }
116
117 /* Test PS/2 controller */
118 case 0xAA:
119 {
120 KeyboardResponse = 0x55;
121 KeyboardReadResponse = TRUE;
122 break;
123 }
124
125 /* Disable keyboard */
126 case 0xAD:
127 {
128 // TODO: Not implemented
129 break;
130 }
131
132 /* Enable keyboard */
133 case 0xAE:
134 {
135 // TODO: Not implemented
136 break;
137 }
138
139 /* Read controller output port */
140 case 0xD0:
141 {
142 // TODO: Not implemented
143 break;
144 }
145
146 /* CPU Reset */
147 case 0xF0:
148 case 0xF2:
149 case 0xF4:
150 case 0xF6:
151 case 0xF8:
152 case 0xFA:
153 case 0xFC:
154 case 0xFE:
155 {
156 /* Stop the VDM */
157 EmulatorTerminate();
158 break;
159 }
160 }
161 }
162 else if (Port == PS2_DATA_PORT)
163 {
164 /* Check if the controller is waiting for a response */
165 if (KeyboardWriteResponse)
166 {
167 KeyboardWriteResponse = FALSE;
168
169 /* Check which command it was */
170 switch (KeyboardResponse)
171 {
172 /* Write configuration byte */
173 case 0x60:
174 {
175 KeyboardConfig = Data;
176 break;
177 }
178
179 /* Write controller output */
180 case 0xD1:
181 {
182 /* Check if bit 0 is unset */
183 if (!(Data & (1 << 0)))
184 {
185 /* CPU disabled - Stop the VDM */
186 EmulatorTerminate();
187 }
188
189 /* Update the A20 line setting */
190 EmulatorSetA20(Data & (1 << 1));
191
192 break;
193 }
194
195 case 0xD2:
196 {
197 /* Push the data byte to the keyboard queue */
198 KeyboardQueuePush(Data);
199 break;
200 }
201
202 case 0xD3:
203 {
204 // TODO: Mouse support
205 break;
206 }
207
208 case 0xD4:
209 {
210 MouseCommand(Data);
211 break;
212 }
213 }
214
215 return;
216 }
217
218 // TODO: Implement PS/2 device commands
219 }
220 }
221
222 /* PUBLIC FUNCTIONS ***********************************************************/
223
224 BOOLEAN KeyboardQueuePush(BYTE ScanCode)
225 {
226 BOOLEAN Result = TRUE;
227
228 WaitForSingleObject(QueueMutex, INFINITE);
229
230 /* Check if the keyboard queue is full */
231 if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
232 {
233 Result = FALSE;
234 goto Done;
235 }
236
237 /* Insert the value in the queue */
238 KeyboardQueue[KeyboardQueueEnd] = ScanCode;
239 KeyboardQueueEnd++;
240 KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
241
242 /* Since we inserted a value, it's not empty anymore */
243 KeyboardQueueEmpty = FALSE;
244
245 Done:
246 ReleaseMutex(QueueMutex);
247 return Result;
248 }
249
250 BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
251 {
252 BOOLEAN Result = TRUE;
253
254 /* Make sure the keyboard queue is not empty (fast check) */
255 if (KeyboardQueueEmpty) return FALSE;
256
257 WaitForSingleObject(QueueMutex, INFINITE);
258
259 /*
260 * Recheck whether keyboard queue is not empty (it
261 * may have changed after having grabbed the mutex).
262 */
263 if (KeyboardQueueEmpty)
264 {
265 Result = FALSE;
266 goto Done;
267 }
268
269 /* Get the scan code */
270 *ScanCode = KeyboardQueue[KeyboardQueueStart];
271
272 /* Remove the value from the queue */
273 KeyboardQueueStart++;
274 KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
275
276 /* Check if the queue is now empty */
277 if (KeyboardQueueStart == KeyboardQueueEnd)
278 {
279 KeyboardQueueEmpty = TRUE;
280 }
281
282 Done:
283 ReleaseMutex(QueueMutex);
284 return Result;
285 }
286
287 VOID PS2Dispatch(PINPUT_RECORD InputRecord)
288 {
289 /* Check the event type */
290 switch (InputRecord->EventType)
291 {
292 case KEY_EVENT:
293 {
294 WORD i;
295 BYTE ScanCode = (BYTE)InputRecord->Event.KeyEvent.wVirtualScanCode;
296
297 /* If this is a key release, set the highest bit in the scan code */
298 if (!InputRecord->Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
299
300 /* Push the scan code onto the keyboard queue */
301 for (i = 0; i < InputRecord->Event.KeyEvent.wRepeatCount; i++)
302 {
303 KeyboardQueuePush(ScanCode);
304 }
305
306 break;
307 }
308
309 case MOUSE_EVENT:
310 {
311 /* Notify the BIOS driver */
312 MouseBiosUpdatePosition(&InputRecord->Event.MouseEvent.dwMousePosition);
313 MouseBiosUpdateButtons(LOWORD(InputRecord->Event.MouseEvent.dwButtonState));
314
315 // TODO: PS/2, other stuff
316
317 break;
318 }
319
320 /* We ignore all the rest */
321 default:
322 break;
323 }
324 }
325
326 VOID GenerateKeyboardInterrupts(VOID)
327 {
328 /* Generate an IRQ 1 if there is a key ready in the queue */
329 if (KeyboardQueuePop(&KeyboardData)) PicInterruptRequest(1);
330 }
331
332 BOOLEAN PS2Initialize(HANDLE ConsoleInput)
333 {
334 DWORD ConInMode;
335
336 /* Create the mutex */
337 QueueMutex = CreateMutex(NULL, FALSE, NULL);
338
339 /* Register the I/O Ports */
340 RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
341 RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
342
343 if (GetConsoleMode(ConsoleInput, &ConInMode))
344 {
345 #if 0
346 if (MousePresent)
347 {
348 #endif
349 /* Support mouse input events if there is a mouse on the system */
350 ConInMode |= ENABLE_MOUSE_INPUT;
351 #if 0
352 }
353 else
354 {
355 /* Do not support mouse input events if there is no mouse on the system */
356 ConInMode &= ~ENABLE_MOUSE_INPUT;
357 }
358 #endif
359
360 SetConsoleMode(ConsoleInput, ConInMode);
361 }
362
363 return TRUE;
364 }
365
366 VOID PS2Cleanup(VOID)
367 {
368 CloseHandle(QueueMutex);
369 }
370
371 /* EOF */