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
;
29 static HANDLE InputThread
= NULL
;
31 /* PRIVATE FUNCTIONS **********************************************************/
33 static BOOLEAN
KeyboardQueuePush(BYTE ScanCode
)
35 BOOLEAN Result
= TRUE
;
37 WaitForSingleObject(QueueMutex
, INFINITE
);
39 /* Check if the keyboard queue is full */
40 if (!KeyboardQueueEmpty
&& (KeyboardQueueStart
== KeyboardQueueEnd
))
46 /* Insert the value in the queue */
47 KeyboardQueue
[KeyboardQueueEnd
] = ScanCode
;
49 KeyboardQueueEnd
%= KEYBOARD_BUFFER_SIZE
;
51 /* Since we inserted a value, it's not empty anymore */
52 KeyboardQueueEmpty
= FALSE
;
55 ReleaseMutex(QueueMutex
);
59 static BOOLEAN
KeyboardQueuePop(BYTE
*ScanCode
)
61 BOOLEAN Result
= TRUE
;
63 WaitForSingleObject(QueueMutex
, INFINITE
);
65 /* Make sure the keyboard queue is not empty */
66 if (KeyboardQueueEmpty
)
72 /* Get the scan code */
73 *ScanCode
= KeyboardQueue
[KeyboardQueueStart
];
75 /* Remove the value from the queue */
77 KeyboardQueueStart
%= KEYBOARD_BUFFER_SIZE
;
79 /* Check if the queue is now empty */
80 if (KeyboardQueueStart
== KeyboardQueueEnd
)
82 KeyboardQueueEmpty
= TRUE
;
86 ReleaseMutex(QueueMutex
);
90 /* PUBLIC FUNCTIONS ***********************************************************/
92 BYTE
KeyboardReadStatus()
96 WaitForSingleObject(QueueMutex
, INFINITE
);
98 /* Set the first bit if the data can be read */
99 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
101 ReleaseMutex(QueueMutex
);
103 /* Always set bit 2 */
106 /* Set bit 3 if the next byte goes to the controller */
107 if (KeyboardWriteResponse
) Status
|= 1 << 3;
112 VOID
KeyboardWriteCommand(BYTE Command
)
116 /* Read configuration byte */
119 KeyboardResponse
= KeyboardConfig
;
120 KeyboardReadResponse
= TRUE
;
124 /* Write configuration byte */
126 /* Write controller output port */
128 /* Write keyboard output buffer */
130 /* Write mouse output buffer */
132 /* Write mouse input buffer */
135 /* These commands require a response */
136 KeyboardResponse
= Command
;
137 KeyboardWriteResponse
= TRUE
;
144 // TODO: Mouse support
151 // TODO: Mouse support
155 /* Test mouse port */
158 KeyboardResponse
= 0;
159 KeyboardReadResponse
= TRUE
;
163 /* Test PS/2 controller */
166 KeyboardResponse
= 0x55;
167 KeyboardReadResponse
= TRUE
;
171 /* Disable keyboard */
174 // TODO: Not implemented
178 /* Enable keyboard */
181 // TODO: Not implemented
185 /* Read controller output port */
188 // TODO: Not implemented
202 /* Stop the simulation */
209 BYTE
KeyboardReadData()
211 /* If there was a response byte from the controller, return it */
212 if (KeyboardReadResponse
)
214 KeyboardReadResponse
= FALSE
;
215 KeyboardData
= KeyboardResponse
;
219 /* Otherwise, read the data from the queue */
220 KeyboardQueuePop(&KeyboardData
);
226 VOID
KeyboardWriteData(BYTE Data
)
228 /* Check if the controller is waiting for a response */
229 if (KeyboardWriteResponse
)
231 KeyboardWriteResponse
= FALSE
;
233 /* Check which command it was */
234 switch (KeyboardResponse
)
236 /* Write configuration byte */
239 KeyboardConfig
= Data
;
243 /* Write controller output */
246 /* Check if bit 0 is unset */
247 if (!(Data
& (1 << 0)))
249 /* CPU disabled - end simulation */
253 /* Update the A20 line setting */
254 EmulatorSetA20(Data
& (1 << 1));
261 /* Push the data byte to the keyboard queue */
262 KeyboardQueuePush(Data
);
268 // TODO: Mouse support
274 // TODO: Mouse support
282 // TODO: Implement PS/2 device commands
285 BYTE WINAPI
PS2ReadPort(ULONG Port
)
287 if (Port
== PS2_CONTROL_PORT
)
288 return KeyboardReadStatus();
289 else if (Port
== PS2_DATA_PORT
)
290 return KeyboardReadData();
295 VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
297 if (Port
== PS2_CONTROL_PORT
)
298 KeyboardWriteCommand(Data
);
299 else if (Port
== PS2_DATA_PORT
)
300 KeyboardWriteData(Data
);
303 DWORD WINAPI
InputThreadProc(LPVOID Parameter
)
306 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
307 INPUT_RECORD InputRecord
;
312 /* Wait for an input record */
313 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
315 DWORD LastError
= GetLastError();
316 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
322 /* Check the event type */
323 switch (InputRecord
.EventType
)
327 BYTE ScanCode
= (BYTE
)InputRecord
.Event
.KeyEvent
.wVirtualScanCode
;
329 /* If this is a key release, set the highest bit in the scan code */
330 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
332 /* Push the scan code onto the keyboard queue */
333 for (i
= 0; i
< InputRecord
.Event
.KeyEvent
.wRepeatCount
; i
++)
335 KeyboardQueuePush(ScanCode
);
339 PicInterruptRequest(1);
345 // TODO: NOT IMPLEMENTED
361 BOOLEAN
PS2Initialize(HANDLE ConsoleInput
)
363 /* Create the mutex */
364 QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
366 /* Start the input thread */
367 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, ConsoleInput
, 0, NULL
);
369 // if (InputThread == NULL) return FALSE;
371 /* Register the I/O Ports */
372 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
373 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
378 VOID
PS2Cleanup(VOID
)
380 /* Close the input thread handle */
381 if (InputThread
!= NULL
) CloseHandle(InputThread
);
384 CloseHandle(QueueMutex
);