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