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