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 /* PRIVATE FUNCTIONS **********************************************************/
31 static BOOLEAN
KeyboardQueuePush(BYTE ScanCode
)
33 BOOLEAN Result
= TRUE
;
35 WaitForSingleObject(QueueMutex
, INFINITE
);
37 /* Check if the keyboard queue is full */
38 if (!KeyboardQueueEmpty
&& (KeyboardQueueStart
== KeyboardQueueEnd
))
44 /* Insert the value in the queue */
45 KeyboardQueue
[KeyboardQueueEnd
] = ScanCode
;
47 KeyboardQueueEnd
%= KEYBOARD_BUFFER_SIZE
;
49 /* Since we inserted a value, it's not empty anymore */
50 KeyboardQueueEmpty
= FALSE
;
53 ReleaseMutex(QueueMutex
);
57 static BOOLEAN
KeyboardQueuePop(BYTE
*ScanCode
)
59 BOOLEAN Result
= TRUE
;
61 /* Make sure the keyboard queue is not empty (fast check) */
62 if (KeyboardQueueEmpty
) return FALSE
;
64 WaitForSingleObject(QueueMutex
, INFINITE
);
67 * Recheck whether keyboard queue is not empty (it
68 * may have changed after having grabbed the mutex).
70 if (KeyboardQueueEmpty
)
76 /* Get the scan code */
77 *ScanCode
= KeyboardQueue
[KeyboardQueueStart
];
79 /* Remove the value from the queue */
81 KeyboardQueueStart
%= KEYBOARD_BUFFER_SIZE
;
83 /* Check if the queue is now empty */
84 if (KeyboardQueueStart
== KeyboardQueueEnd
)
86 KeyboardQueueEmpty
= TRUE
;
90 ReleaseMutex(QueueMutex
);
94 static BYTE WINAPI
PS2ReadPort(ULONG Port
)
96 if (Port
== PS2_CONTROL_PORT
)
100 /* Set the first bit if the data can be read */
101 if (KeyboardReadResponse
|| !KeyboardQueueEmpty
) Status
|= 1 << 0;
103 /* Always set bit 2 */
106 /* Set bit 3 if the next byte goes to the controller */
107 if (KeyboardWriteResponse
) Status
|= 1 << 3;
111 else if (Port
== PS2_DATA_PORT
)
113 /* If there was a response byte from the controller, return it */
114 if (KeyboardReadResponse
)
116 KeyboardReadResponse
= FALSE
;
117 KeyboardData
= KeyboardResponse
;
125 static VOID WINAPI
PS2WritePort(ULONG Port
, BYTE Data
)
127 if (Port
== PS2_CONTROL_PORT
)
131 /* Read configuration byte */
134 KeyboardResponse
= KeyboardConfig
;
135 KeyboardReadResponse
= TRUE
;
139 /* Write configuration byte */
141 /* Write controller output port */
143 /* Write keyboard output buffer */
145 /* Write mouse output buffer */
147 /* Write mouse input buffer */
150 /* These commands require a response */
151 KeyboardResponse
= Data
;
152 KeyboardWriteResponse
= TRUE
;
159 // TODO: Mouse support
166 // TODO: Mouse support
170 /* Test mouse port */
173 KeyboardResponse
= 0;
174 KeyboardReadResponse
= TRUE
;
178 /* Test PS/2 controller */
181 KeyboardResponse
= 0x55;
182 KeyboardReadResponse
= TRUE
;
186 /* Disable keyboard */
189 // TODO: Not implemented
193 /* Enable keyboard */
196 // TODO: Not implemented
200 /* Read controller output port */
203 // TODO: Not implemented
223 else if (Port
== PS2_DATA_PORT
)
225 /* Check if the controller is waiting for a response */
226 if (KeyboardWriteResponse
)
228 KeyboardWriteResponse
= FALSE
;
230 /* Check which command it was */
231 switch (KeyboardResponse
)
233 /* Write configuration byte */
236 KeyboardConfig
= Data
;
240 /* Write controller output */
243 /* Check if bit 0 is unset */
244 if (!(Data
& (1 << 0)))
246 /* CPU disabled - Stop the VDM */
250 /* Update the A20 line setting */
251 EmulatorSetA20(Data
& (1 << 1));
258 /* Push the data byte to the keyboard queue */
259 KeyboardQueuePush(Data
);
265 // TODO: Mouse support
271 // TODO: Mouse support
279 // TODO: Implement PS/2 device commands
283 /* PUBLIC FUNCTIONS ***********************************************************/
285 VOID
PS2Dispatch(PINPUT_RECORD InputRecord
)
287 /* Check the event type */
288 switch (InputRecord
->EventType
)
293 BYTE ScanCode
= (BYTE
)InputRecord
->Event
.KeyEvent
.wVirtualScanCode
;
295 /* If this is a key release, set the highest bit in the scan code */
296 if (!InputRecord
->Event
.KeyEvent
.bKeyDown
) ScanCode
|= 0x80;
298 /* Push the scan code onto the keyboard queue */
299 for (i
= 0; i
< InputRecord
->Event
.KeyEvent
.wRepeatCount
; i
++)
301 KeyboardQueuePush(ScanCode
);
309 // TODO: NOT IMPLEMENTED
314 /* We ignore all the rest */
320 VOID
GenerateKeyboardInterrupts(VOID
)
322 /* Generate an IRQ 1 if there is a key ready in the queue */
323 if (KeyboardQueuePop(&KeyboardData
)) PicInterruptRequest(1);
326 BOOLEAN
PS2Initialize(HANDLE ConsoleInput
)
332 /* Create the mutex */
333 QueueMutex
= CreateMutex(NULL
, FALSE
, NULL
);
335 /* Register the I/O Ports */
336 RegisterIoPort(PS2_CONTROL_PORT
, PS2ReadPort
, PS2WritePort
);
337 RegisterIoPort(PS2_DATA_PORT
, PS2ReadPort
, PS2WritePort
);
340 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
344 /* Support mouse input events if there is a mouse on the system */
345 ConInMode
|= ENABLE_MOUSE_INPUT
;
349 /* Do not support mouse input events if there is no mouse on the system */
350 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
353 SetConsoleMode(ConsoleInput
, ConInMode
);
360 VOID
PS2Cleanup(VOID
)
362 CloseHandle(QueueMutex
);