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 /* PUBLIC FUNCTIONS ***********************************************************/
97 BYTE WINAPI
PS2ReadPort(ULONG Port
)
99 if (Port
== PS2_CONTROL_PORT
)
103 /* Set the first bit if the data can be read */
104 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
106 /* Always set bit 2 */
109 /* Set bit 3 if the next byte goes to the controller */
110 if (KeyboardWriteResponse
) Status
|= 1 << 3;
114 else if (Port
== PS2_DATA_PORT
)
116 /* If there was a response byte from the controller, return it */
117 if (KeyboardReadResponse
)
119 KeyboardReadResponse
= FALSE
;
120 KeyboardData
= KeyboardResponse
;
128 VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
130 if (Port
== PS2_CONTROL_PORT
)
134 /* Read configuration byte */
137 KeyboardResponse
= KeyboardConfig
;
138 KeyboardReadResponse
= TRUE
;
142 /* Write configuration byte */
144 /* Write controller output port */
146 /* Write keyboard output buffer */
148 /* Write mouse output buffer */
150 /* Write mouse input buffer */
153 /* These commands require a response */
154 KeyboardResponse
= Data
;
155 KeyboardWriteResponse
= TRUE
;
162 // TODO: Mouse support
169 // TODO: Mouse support
173 /* Test mouse port */
176 KeyboardResponse
= 0;
177 KeyboardReadResponse
= TRUE
;
181 /* Test PS/2 controller */
184 KeyboardResponse
= 0x55;
185 KeyboardReadResponse
= TRUE
;
189 /* Disable keyboard */
192 // TODO: Not implemented
196 /* Enable keyboard */
199 // TODO: Not implemented
203 /* Read controller output port */
206 // TODO: Not implemented
220 /* Stop the simulation */
226 else if (Port
== PS2_DATA_PORT
)
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
286 VOID
GenerateKeyboardInterrupts(VOID
)
288 if (KeyboardQueuePop(&KeyboardData
))
291 PicInterruptRequest(1);
295 DWORD WINAPI
InputThreadProc(LPVOID Parameter
)
298 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
299 INPUT_RECORD InputRecord
;
304 /* Wait for an input record */
305 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
307 DWORD LastError
= GetLastError();
308 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
314 /* Check the event type */
315 switch (InputRecord
.EventType
)
319 BYTE ScanCode
= (BYTE
)InputRecord
.Event
.KeyEvent
.wVirtualScanCode
;
321 /* If this is a key release, set the highest bit in the scan code */
322 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
324 /* Push the scan code onto the keyboard queue */
325 for (i
= 0; i
< InputRecord
.Event
.KeyEvent
.wRepeatCount
; i
++)
327 KeyboardQueuePush(ScanCode
);
335 // TODO: NOT IMPLEMENTED
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
);