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