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 #include "../bios/bios32/moubios32.h"
20 /* PRIVATE VARIABLES **********************************************************/
22 static BYTE KeyboardQueue
[KEYBOARD_BUFFER_SIZE
];
23 static BOOLEAN KeyboardQueueEmpty
= TRUE
;
24 static UINT KeyboardQueueStart
= 0;
25 static UINT KeyboardQueueEnd
= 0;
26 static BYTE KeyboardData
= 0, KeyboardResponse
= 0;
27 static BOOLEAN KeyboardReadResponse
= FALSE
, KeyboardWriteResponse
= FALSE
;
28 static BYTE KeyboardConfig
= PS2_DEFAULT_CONFIG
;
29 static HANDLE QueueMutex
= NULL
;
31 /* PRIVATE FUNCTIONS **********************************************************/
33 static BYTE WINAPI
PS2ReadPort(ULONG Port
)
35 if (Port
== PS2_CONTROL_PORT
)
39 /* Set the first bit if the data can be read */
40 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
42 /* Always set bit 2 */
45 /* Set bit 3 if the next byte goes to the controller */
46 if (KeyboardWriteResponse
) Status
|= 1 << 3;
50 else if (Port
== PS2_DATA_PORT
)
52 /* If there was a response byte from the controller, return it */
53 if (KeyboardReadResponse
)
55 KeyboardReadResponse
= FALSE
;
56 KeyboardData
= KeyboardResponse
;
64 static VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
66 if (Port
== PS2_CONTROL_PORT
)
70 /* Read configuration byte */
73 KeyboardResponse
= KeyboardConfig
;
74 KeyboardReadResponse
= TRUE
;
78 /* Write configuration byte */
80 /* Write controller output port */
82 /* Write keyboard output buffer */
84 /* Write mouse output buffer */
86 /* Write mouse input buffer */
89 /* These commands require a response */
90 KeyboardResponse
= Data
;
91 KeyboardWriteResponse
= TRUE
;
98 // TODO: Not implemented
105 // TODO: Not implemented
109 /* Test mouse port */
112 KeyboardResponse
= 0;
113 KeyboardReadResponse
= TRUE
;
117 /* Test PS/2 controller */
120 KeyboardResponse
= 0x55;
121 KeyboardReadResponse
= TRUE
;
125 /* Disable keyboard */
128 // TODO: Not implemented
132 /* Enable keyboard */
135 // TODO: Not implemented
139 /* Read controller output port */
142 // TODO: Not implemented
162 else if (Port
== PS2_DATA_PORT
)
164 /* Check if the controller is waiting for a response */
165 if (KeyboardWriteResponse
)
167 KeyboardWriteResponse
= FALSE
;
169 /* Check which command it was */
170 switch (KeyboardResponse
)
172 /* Write configuration byte */
175 KeyboardConfig
= Data
;
179 /* Write controller output */
182 /* Check if bit 0 is unset */
183 if (!(Data
& (1 << 0)))
185 /* CPU disabled - Stop the VDM */
189 /* Update the A20 line setting */
190 EmulatorSetA20(Data
& (1 << 1));
197 /* Push the data byte to the keyboard queue */
198 KeyboardQueuePush(Data
);
204 // TODO: Mouse support
218 // TODO: Implement PS/2 device commands
222 /* PUBLIC FUNCTIONS ***********************************************************/
224 BOOLEAN
KeyboardQueuePush(BYTE ScanCode
)
226 BOOLEAN Result
= TRUE
;
228 WaitForSingleObject(QueueMutex
, INFINITE
);
230 /* Check if the keyboard queue is full */
231 if (!KeyboardQueueEmpty
&& (KeyboardQueueStart
== KeyboardQueueEnd
))
237 /* Insert the value in the queue */
238 KeyboardQueue
[KeyboardQueueEnd
] = ScanCode
;
240 KeyboardQueueEnd
%= KEYBOARD_BUFFER_SIZE
;
242 /* Since we inserted a value, it's not empty anymore */
243 KeyboardQueueEmpty
= FALSE
;
246 ReleaseMutex(QueueMutex
);
250 BOOLEAN
KeyboardQueuePop(BYTE
*ScanCode
)
252 BOOLEAN Result
= TRUE
;
254 /* Make sure the keyboard queue is not empty (fast check) */
255 if (KeyboardQueueEmpty
) return FALSE
;
257 WaitForSingleObject(QueueMutex
, INFINITE
);
260 * Recheck whether keyboard queue is not empty (it
261 * may have changed after having grabbed the mutex).
263 if (KeyboardQueueEmpty
)
269 /* Get the scan code */
270 *ScanCode
= KeyboardQueue
[KeyboardQueueStart
];
272 /* Remove the value from the queue */
273 KeyboardQueueStart
++;
274 KeyboardQueueStart
%= KEYBOARD_BUFFER_SIZE
;
276 /* Check if the queue is now empty */
277 if (KeyboardQueueStart
== KeyboardQueueEnd
)
279 KeyboardQueueEmpty
= TRUE
;
283 ReleaseMutex(QueueMutex
);
287 VOID
PS2Dispatch(PINPUT_RECORD InputRecord
)
289 /* Check the event type */
290 switch (InputRecord
->EventType
)
295 BYTE ScanCode
= (BYTE
)InputRecord
->Event
.KeyEvent
.wVirtualScanCode
;
297 /* If this is a key release, set the highest bit in the scan code */
298 if (!InputRecord
->Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
300 /* Push the scan code onto the keyboard queue */
301 for (i
= 0; i
< InputRecord
->Event
.KeyEvent
.wRepeatCount
; i
++)
303 KeyboardQueuePush(ScanCode
);
311 /* Notify the BIOS driver */
312 MouseBiosUpdatePosition(&InputRecord
->Event
.MouseEvent
.dwMousePosition
);
313 MouseBiosUpdateButtons(LOWORD(InputRecord
->Event
.MouseEvent
.dwButtonState
));
315 // TODO: PS/2, other stuff
320 /* We ignore all the rest */
326 VOID
GenerateKeyboardInterrupts(VOID
)
328 /* Generate an IRQ 1 if there is a key ready in the queue */
329 if (KeyboardQueuePop(&KeyboardData
)) PicInterruptRequest(1);
332 BOOLEAN
PS2Initialize(HANDLE ConsoleInput
)
336 /* Create the mutex */
337 QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
339 /* Register the I/O Ports */
340 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
341 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
343 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
349 /* Support mouse input events if there is a mouse on the system */
350 ConInMode
|= ENABLE_MOUSE_INPUT
;
355 /* Do not support mouse input events if there is no mouse on the system */
356 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
360 SetConsoleMode(ConsoleInput
, ConInMode
);
366 VOID
PS2Cleanup(VOID
)
368 CloseHandle(QueueMutex
);