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