[NTVDM]
[reactos.git] / subsystems / ntvdm / ps2.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: ps2.c
5 * PURPOSE: PS/2 controller emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ps2.h"
12 #include "emulator.h"
13 #include "pic.h"
14
15 /* PRIVATE VARIABLES **********************************************************/
16
17 static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE];
18 static BOOLEAN KeyboardQueueEmpty = TRUE;
19 static UINT KeyboardQueueStart = 0;
20 static UINT KeyboardQueueEnd = 0;
21 static BYTE KeyboardResponse = 0;
22 static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE;
23 static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG;
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
28 {
29 /* Check if the keyboard queue is full */
30 if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
31 {
32 return FALSE;
33 }
34
35 /* Insert the value in the queue */
36 KeyboardQueue[KeyboardQueueEnd] = ScanCode;
37 KeyboardQueueEnd++;
38 KeyboardQueueEnd %= KEYBOARD_BUFFER_SIZE;
39
40 /* Since we inserted a value, it's not empty anymore */
41 KeyboardQueueEmpty = FALSE;
42
43 return TRUE;
44 }
45
46 static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
47 {
48 /* Make sure the keyboard queue is not empty */
49 if (KeyboardQueueEmpty) return FALSE;
50
51 /* Get the scan code */
52 *ScanCode = KeyboardQueue[KeyboardQueueStart];
53
54 /* Remove the value from the queue */
55 KeyboardQueueStart++;
56 KeyboardQueueStart %= KEYBOARD_BUFFER_SIZE;
57
58 /* Check if the queue is now empty */
59 if (KeyboardQueueStart == KeyboardQueueEnd)
60 {
61 KeyboardQueueEmpty = TRUE;
62 }
63
64 return TRUE;
65 }
66
67 /* PUBLIC FUNCTIONS ***********************************************************/
68
69 BYTE KeyboardReadStatus()
70 {
71 BYTE Status = 0;
72
73 /* Set the first bit if the data can be read */
74 if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
75
76 /* Always set bit 2 */
77 Status |= 1 << 2;
78
79 /* Set bit 3 if the next byte goes to the controller */
80 if (KeyboardWriteResponse) Status |= 1 << 3;
81
82 return Status;
83 }
84
85 VOID KeyboardWriteCommand(BYTE Command)
86 {
87 switch (Command)
88 {
89 /* Read configuration byte */
90 case 0x20:
91 {
92 KeyboardResponse = KeyboardConfig;
93 KeyboardReadResponse = TRUE;
94
95 break;
96 }
97
98 /* Write configuration byte */
99 case 0x60:
100 /* Write controller output port */
101 case 0xD1:
102 /* Write keyboard output buffer */
103 case 0xD2:
104 /* Write mouse output buffer */
105 case 0xD3:
106 /* Write mouse input buffer */
107 case 0xD4:
108 {
109 /* These commands require a response */
110 KeyboardResponse = Command;
111 KeyboardWriteResponse = TRUE;
112
113 break;
114 }
115
116 /* Disable mouse */
117 case 0xA7:
118 {
119 // TODO: Mouse support
120
121 break;
122 }
123
124 /* Enable mouse */
125 case 0xA8:
126 {
127 // TODO: Mouse support
128
129 break;
130 }
131
132 /* Test mouse port */
133 case 0xA9:
134 {
135 KeyboardResponse = 0;
136 KeyboardReadResponse = TRUE;
137
138 break;
139 }
140
141 /* Test PS/2 controller */
142 case 0xAA:
143 {
144 KeyboardResponse = 0x55;
145 KeyboardReadResponse = TRUE;
146
147 break;
148 }
149
150 /* Disable keyboard */
151 case 0xAD:
152 {
153 // TODO: Not implemented
154 break;
155 }
156
157 /* Enable keyboard */
158 case 0xAE:
159 {
160 // TODO: Not implemented
161 break;
162 }
163
164 /* Read controller output port */
165 case 0xD0:
166 {
167 // TODO: Not implemented
168 break;
169 }
170
171 /* CPU Reset */
172 case 0xF0:
173 case 0xF2:
174 case 0xF4:
175 case 0xF6:
176 case 0xF8:
177 case 0xFA:
178 case 0xFC:
179 case 0xFE:
180 {
181 /* Stop the simulation */
182 VdmRunning = FALSE;
183
184 break;
185 }
186 }
187 }
188
189 BYTE KeyboardReadData()
190 {
191 BYTE Value = 0;
192
193 /* If there was a response byte from the controller, return it */
194 if (KeyboardReadResponse)
195 {
196 KeyboardReadResponse = FALSE;
197 return KeyboardResponse;
198 }
199
200 /* Otherwise, read the data from the queue */
201 KeyboardQueuePop(&Value);
202
203 return Value;
204 }
205
206 VOID KeyboardWriteData(BYTE Data)
207 {
208 /* Check if the controller is waiting for a response */
209 if (KeyboardWriteResponse)
210 {
211 KeyboardWriteResponse = FALSE;
212
213 /* Check which command it was */
214 switch (KeyboardResponse)
215 {
216 /* Write configuration byte */
217 case 0x60:
218 {
219 KeyboardConfig = Data;
220 break;
221 }
222
223 /* Write controller output */
224 case 0xD1:
225 {
226 /* Check if bit 0 is unset */
227 if (!(Data & (1 << 0)))
228 {
229 /* CPU disabled - end simulation */
230 VdmRunning = FALSE;
231 }
232
233 /* Update the A20 line setting */
234 EmulatorSetA20(Data & (1 << 1));
235
236 break;
237 }
238
239 case 0xD2:
240 {
241 /* Push the data byte to the keyboard queue */
242 KeyboardQueuePush(Data);
243
244 break;
245 }
246
247 case 0xD3:
248 {
249 // TODO: Mouse support
250 break;
251 }
252
253 case 0xD4:
254 {
255 // TODO: Mouse support
256 break;
257 }
258 }
259
260 return;
261 }
262
263 // TODO: Implement PS/2 device commands
264 }
265
266 VOID CheckForInputEvents()
267 {
268 PINPUT_RECORD Buffer;
269 HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
270 DWORD i, j, Count, TotalEvents;
271 BYTE ScanCode;
272
273 /* Get the number of input events */
274 if (!GetNumberOfConsoleInputEvents(ConsoleInput, &Count)) return;
275 if (Count == 0) return;
276
277 /* Allocate the buffer */
278 Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
279 if (Buffer == NULL) return;
280
281 /* Peek the input events */
282 if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
283
284 for (i = 0; i < TotalEvents; i++)
285 {
286 /* Check if this is a key event */
287 if (Buffer[i].EventType != KEY_EVENT) continue;
288
289 /* Get the scan code */
290 ScanCode = Buffer[i].Event.KeyEvent.wVirtualScanCode;
291
292 /* If this is a key release, set the highest bit in the scan code */
293 if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
294
295 /* Push the scan code onto the keyboard queue */
296 for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
297 {
298 KeyboardQueuePush(ScanCode);
299 }
300
301 /* Yes, IRQ 1 */
302 PicInterruptRequest(1);
303
304 /* Stop the loop */
305 break;
306 }
307
308 Cleanup:
309 HeapFree(GetProcessHeap(), 0, Buffer);
310 }
311
312 /* EOF */
313