2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: PS/2 controller emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
18 /* PRIVATE VARIABLES **********************************************************/
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
;
30 /* PRIVATE FUNCTIONS **********************************************************/
32 static BOOLEAN
KeyboardQueuePush(BYTE ScanCode
)
34 BOOLEAN Result
= TRUE
;
36 WaitForSingleObject(QueueMutex
, INFINITE
);
38 /* Check if the keyboard queue is full */
39 if (!KeyboardQueueEmpty
&& (KeyboardQueueStart
== KeyboardQueueEnd
))
45 /* Insert the value in the queue */
46 KeyboardQueue
[KeyboardQueueEnd
] = ScanCode
;
48 KeyboardQueueEnd
%= KEYBOARD_BUFFER_SIZE
;
50 /* Since we inserted a value, it's not empty anymore */
51 KeyboardQueueEmpty
= FALSE
;
54 ReleaseMutex(QueueMutex
);
58 static BOOLEAN
KeyboardQueuePop(BYTE
*ScanCode
)
60 BOOLEAN Result
= TRUE
;
62 /* Make sure the keyboard queue is not empty (fast check) */
63 if (KeyboardQueueEmpty
) return FALSE
;
65 WaitForSingleObject(QueueMutex
, INFINITE
);
68 * Recheck whether keyboard queue is not empty (it
69 * may have changed after having grabbed the mutex).
71 if (KeyboardQueueEmpty
)
77 /* Get the scan code */
78 *ScanCode
= KeyboardQueue
[KeyboardQueueStart
];
80 /* Remove the value from the queue */
82 KeyboardQueueStart
%= KEYBOARD_BUFFER_SIZE
;
84 /* Check if the queue is now empty */
85 if (KeyboardQueueStart
== KeyboardQueueEnd
)
87 KeyboardQueueEmpty
= TRUE
;
91 ReleaseMutex(QueueMutex
);
95 static BYTE WINAPI
PS2ReadPort(ULONG Port
)
97 if (Port
== PS2_CONTROL_PORT
)
101 /* Set the first bit if the data can be read */
102 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
104 /* Always set bit 2 */
107 /* Set bit 3 if the next byte goes to the controller */
108 if (KeyboardWriteResponse
) Status
|= 1 << 3;
112 else if (Port
== PS2_DATA_PORT
)
114 /* If there was a response byte from the controller, return it */
115 if (KeyboardReadResponse
)
117 KeyboardReadResponse
= FALSE
;
118 KeyboardData
= KeyboardResponse
;
126 static VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
128 if (Port
== PS2_CONTROL_PORT
)
132 /* Read configuration byte */
135 KeyboardResponse
= KeyboardConfig
;
136 KeyboardReadResponse
= TRUE
;
140 /* Write configuration byte */
142 /* Write controller output port */
144 /* Write keyboard output buffer */
146 /* Write mouse output buffer */
148 /* Write mouse input buffer */
151 /* These commands require a response */
152 KeyboardResponse
= Data
;
153 KeyboardWriteResponse
= TRUE
;
160 // TODO: Mouse support
167 // TODO: Mouse support
171 /* Test mouse port */
174 KeyboardResponse
= 0;
175 KeyboardReadResponse
= TRUE
;
179 /* Test PS/2 controller */
182 KeyboardResponse
= 0x55;
183 KeyboardReadResponse
= TRUE
;
187 /* Disable keyboard */
190 // TODO: Not implemented
194 /* Enable keyboard */
197 // TODO: Not implemented
201 /* Read controller output port */
204 // TODO: Not implemented
218 /* Stop the simulation */
224 else if (Port
== PS2_DATA_PORT
)
226 /* Check if the controller is waiting for a response */
227 if (KeyboardWriteResponse
)
229 KeyboardWriteResponse
= FALSE
;
231 /* Check which command it was */
232 switch (KeyboardResponse
)
234 /* Write configuration byte */
237 KeyboardConfig
= Data
;
241 /* Write controller output */
244 /* Check if bit 0 is unset */
245 if (!(Data
& (1 << 0)))
247 /* CPU disabled - end simulation */
251 /* Update the A20 line setting */
252 EmulatorSetA20(Data
& (1 << 1));
259 /* Push the data byte to the keyboard queue */
260 KeyboardQueuePush(Data
);
266 // TODO: Mouse support
272 // TODO: Mouse support
280 // TODO: Implement PS/2 device commands
284 static DWORD WINAPI
InputThreadProc(LPVOID Parameter
)
287 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
288 INPUT_RECORD InputRecord
;
293 /* Wait for an input record */
294 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
296 DWORD LastError
= GetLastError();
297 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
303 /* Check the event type */
304 switch (InputRecord
.EventType
)
308 BYTE ScanCode
= (BYTE
)InputRecord
.Event
.KeyEvent
.wVirtualScanCode
;
310 /* If this is a key release, set the highest bit in the scan code */
311 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
313 /* Push the scan code onto the keyboard queue */
314 for (i
= 0; i
< InputRecord
.Event
.KeyEvent
.wRepeatCount
; i
++)
316 KeyboardQueuePush(ScanCode
);
324 // TODO: NOT IMPLEMENTED
340 /* PUBLIC FUNCTIONS ***********************************************************/
342 VOID
GenerateKeyboardInterrupts(VOID
)
344 if (KeyboardQueuePop(&KeyboardData
))
347 PicInterruptRequest(1);
351 BOOLEAN
PS2Initialize(HANDLE ConsoleInput
)
353 /* Create the mutex */
354 QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
356 /* Start the input thread */
357 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, ConsoleInput
, 0, NULL
);
359 // if (InputThread == NULL) return FALSE;
361 /* Register the I/O Ports */
362 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
363 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
368 VOID
PS2Cleanup(VOID
)
370 /* Close the input thread handle */
371 if (InputThread
!= NULL
) CloseHandle(InputThread
);
374 CloseHandle(QueueMutex
);