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 /* Set the first bit if the data can be read */
97 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
99 /* Always set bit 2 */
102 /* Set bit 3 if the next byte goes to the controller */
103 if (KeyboardWriteResponse
) Status
|= 1 << 3;
108 VOID
KeyboardWriteCommand(BYTE Command
)
112 /* Read configuration byte */
115 KeyboardResponse
= KeyboardConfig
;
116 KeyboardReadResponse
= TRUE
;
120 /* Write configuration byte */
122 /* Write controller output port */
124 /* Write keyboard output buffer */
126 /* Write mouse output buffer */
128 /* Write mouse input buffer */
131 /* These commands require a response */
132 KeyboardResponse
= Command
;
133 KeyboardWriteResponse
= TRUE
;
140 // TODO: Mouse support
147 // TODO: Mouse support
151 /* Test mouse port */
154 KeyboardResponse
= 0;
155 KeyboardReadResponse
= TRUE
;
159 /* Test PS/2 controller */
162 KeyboardResponse
= 0x55;
163 KeyboardReadResponse
= TRUE
;
167 /* Disable keyboard */
170 // TODO: Not implemented
174 /* Enable keyboard */
177 // TODO: Not implemented
181 /* Read controller output port */
184 // TODO: Not implemented
198 /* Stop the simulation */
205 BYTE
KeyboardReadData()
207 /* If there was a response byte from the controller, return it */
208 if (KeyboardReadResponse
)
210 KeyboardReadResponse
= FALSE
;
211 KeyboardData
= KeyboardResponse
;
215 /* Otherwise, read the data from the queue */
216 KeyboardQueuePop(&KeyboardData
);
222 VOID
KeyboardWriteData(BYTE Data
)
224 /* Check if the controller is waiting for a response */
225 if (KeyboardWriteResponse
)
227 KeyboardWriteResponse
= FALSE
;
229 /* Check which command it was */
230 switch (KeyboardResponse
)
232 /* Write configuration byte */
235 KeyboardConfig
= Data
;
239 /* Write controller output */
242 /* Check if bit 0 is unset */
243 if (!(Data
& (1 << 0)))
245 /* CPU disabled - end simulation */
249 /* Update the A20 line setting */
250 EmulatorSetA20(Data
& (1 << 1));
257 /* Push the data byte to the keyboard queue */
258 KeyboardQueuePush(Data
);
264 // TODO: Mouse support
270 // TODO: Mouse support
278 // TODO: Implement PS/2 device commands
281 BYTE WINAPI
PS2ReadPort(ULONG Port
)
283 if (Port
== PS2_CONTROL_PORT
)
284 return KeyboardReadStatus();
285 else if (Port
== PS2_DATA_PORT
)
286 return KeyboardReadData();
291 VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
293 if (Port
== PS2_CONTROL_PORT
)
294 KeyboardWriteCommand(Data
);
295 else if (Port
== PS2_DATA_PORT
)
296 KeyboardWriteData(Data
);
299 DWORD WINAPI
InputThreadProc(LPVOID Parameter
)
302 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
303 INPUT_RECORD InputRecord
;
308 /* Wait for an input record */
309 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
311 DWORD LastError
= GetLastError();
312 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
318 /* Check the event type */
319 switch (InputRecord
.EventType
)
323 BYTE ScanCode
= (BYTE
)InputRecord
.Event
.KeyEvent
.wVirtualScanCode
;
325 /* If this is a key release, set the highest bit in the scan code */
326 if (!InputRecord
.Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
328 /* Push the scan code onto the keyboard queue */
329 for (i
= 0; i
< InputRecord
.Event
.KeyEvent
.wRepeatCount
; i
++)
331 KeyboardQueuePush(ScanCode
);
335 PicInterruptRequest(1);
341 // TODO: NOT IMPLEMENTED
357 BOOLEAN
PS2Initialize(HANDLE ConsoleInput
)
359 /* Create the mutex */
360 QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
362 /* Start the input thread */
363 InputThread
= CreateThread(NULL
, 0, &InputThreadProc
, ConsoleInput
, 0, NULL
);
365 // if (InputThread == NULL) return FALSE;
367 /* Register the I/O Ports */
368 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
369 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
374 VOID
PS2Cleanup(VOID
)
376 /* Close the input thread handle */
377 if (InputThread
!= NULL
) CloseHandle(InputThread
);
380 CloseHandle(QueueMutex
);