[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 VOID CheckForInputEvents()
269 {
270 PINPUT_RECORD Buffer;
271 HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
272 DWORD i, j, Count, TotalEvents;
273 BYTE ScanCode;
274
275 /* Get the number of input events */
276 if (!GetNumberOfConsoleInputEvents(ConsoleInput, &Count)) return;
277 if (Count == 0) return;
278
279 /* Allocate the buffer */
280 Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
281 if (Buffer == NULL) return;
282
283 /* Peek the input events */
284 if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
285
286 for (i = 0; i < TotalEvents; i++)
287 {
288 /* Check if this is a key event */
289 if (Buffer[i].EventType != KEY_EVENT) continue;
290
291 /* Get the scan code */
292 ScanCode = Buffer[i].Event.KeyEvent.wVirtualScanCode;
293
294 /* If this is a key release, set the highest bit in the scan code */
295 if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
296
297 /* Push the scan code onto the keyboard queue */
298 for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
299 {
300 KeyboardQueuePush(ScanCode);
301 }
302
303 /* Yes, IRQ 1 */
304 PicInterruptRequest(1);
305
306 /* Stop the loop */
307 break;
308 }
309
310 Cleanup:
311 HeapFree(GetProcessHeap(), 0, Buffer);
312 }
313
314 /* EOF */
315