[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 WaitForSingleObject(QueueMutex, INFINITE);
97
98 /* Set the first bit if the data can be read */
99 if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
100
101 ReleaseMutex(QueueMutex);
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
112 VOID KeyboardWriteCommand(BYTE Command)
113 {
114 switch (Command)
115 {
116 /* Read configuration byte */
117 case 0x20:
118 {
119 KeyboardResponse = KeyboardConfig;
120 KeyboardReadResponse = TRUE;
121 break;
122 }
123
124 /* Write configuration byte */
125 case 0x60:
126 /* Write controller output port */
127 case 0xD1:
128 /* Write keyboard output buffer */
129 case 0xD2:
130 /* Write mouse output buffer */
131 case 0xD3:
132 /* Write mouse input buffer */
133 case 0xD4:
134 {
135 /* These commands require a response */
136 KeyboardResponse = Command;
137 KeyboardWriteResponse = TRUE;
138 break;
139 }
140
141 /* Disable mouse */
142 case 0xA7:
143 {
144 // TODO: Mouse support
145 break;
146 }
147
148 /* Enable mouse */
149 case 0xA8:
150 {
151 // TODO: Mouse support
152 break;
153 }
154
155 /* Test mouse port */
156 case 0xA9:
157 {
158 KeyboardResponse = 0;
159 KeyboardReadResponse = TRUE;
160 break;
161 }
162
163 /* Test PS/2 controller */
164 case 0xAA:
165 {
166 KeyboardResponse = 0x55;
167 KeyboardReadResponse = TRUE;
168 break;
169 }
170
171 /* Disable keyboard */
172 case 0xAD:
173 {
174 // TODO: Not implemented
175 break;
176 }
177
178 /* Enable keyboard */
179 case 0xAE:
180 {
181 // TODO: Not implemented
182 break;
183 }
184
185 /* Read controller output port */
186 case 0xD0:
187 {
188 // TODO: Not implemented
189 break;
190 }
191
192 /* CPU Reset */
193 case 0xF0:
194 case 0xF2:
195 case 0xF4:
196 case 0xF6:
197 case 0xF8:
198 case 0xFA:
199 case 0xFC:
200 case 0xFE:
201 {
202 /* Stop the simulation */
203 VdmRunning = FALSE;
204 break;
205 }
206 }
207 }
208
209 BYTE KeyboardReadData()
210 {
211 /* If there was a response byte from the controller, return it */
212 if (KeyboardReadResponse)
213 {
214 KeyboardReadResponse = FALSE;
215 KeyboardData = KeyboardResponse;
216 }
217 else
218 {
219 /* Otherwise, read the data from the queue */
220 KeyboardQueuePop(&KeyboardData);
221 }
222
223 return KeyboardData;
224 }
225
226 VOID KeyboardWriteData(BYTE Data)
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 BYTE WINAPI PS2ReadPort(ULONG Port)
286 {
287 if (Port == PS2_CONTROL_PORT)
288 return KeyboardReadStatus();
289 else if (Port == PS2_DATA_PORT)
290 return KeyboardReadData();
291 else
292 return 0;
293 }
294
295 VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
296 {
297 if (Port == PS2_CONTROL_PORT)
298 KeyboardWriteCommand(Data);
299 else if (Port == PS2_DATA_PORT)
300 KeyboardWriteData(Data);
301 }
302
303 DWORD WINAPI InputThreadProc(LPVOID Parameter)
304 {
305 INT i;
306 HANDLE ConsoleInput = (HANDLE)Parameter;
307 INPUT_RECORD InputRecord;
308 DWORD Count;
309
310 while (VdmRunning)
311 {
312 /* Wait for an input record */
313 if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
314 {
315 DWORD LastError = GetLastError();
316 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError);
317 return LastError;
318 }
319
320 ASSERT(Count != 0);
321
322 /* Check the event type */
323 switch (InputRecord.EventType)
324 {
325 case KEY_EVENT:
326 {
327 BYTE ScanCode = (BYTE)InputRecord.Event.KeyEvent.wVirtualScanCode;
328
329 /* If this is a key release, set the highest bit in the scan code */
330 if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
331
332 /* Push the scan code onto the keyboard queue */
333 for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++)
334 {
335 KeyboardQueuePush(ScanCode);
336 }
337
338 /* Keyboard IRQ */
339 PicInterruptRequest(1);
340 break;
341 }
342
343 case MOUSE_EVENT:
344 {
345 // TODO: NOT IMPLEMENTED
346 UNIMPLEMENTED;
347 break;
348 }
349
350 default:
351 {
352 /* Ignored */
353 break;
354 }
355 }
356 }
357
358 return 0;
359 }
360
361 BOOLEAN PS2Initialize(HANDLE ConsoleInput)
362 {
363 /* Create the mutex */
364 QueueMutex = CreateMutex(NULL, FALSE, NULL);
365
366 /* Start the input thread */
367 InputThread = CreateThread(NULL, 0, &InputThreadProc, ConsoleInput, 0, NULL);
368
369 // if (InputThread == NULL) return FALSE;
370
371 /* Register the I/O Ports */
372 RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
373 RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
374
375 return TRUE;
376 }
377
378 VOID PS2Cleanup(VOID)
379 {
380 /* Close the input thread handle */
381 if (InputThread != NULL) CloseHandle(InputThread);
382 InputThread = NULL;
383
384 CloseHandle(QueueMutex);
385 }
386
387 /* EOF */