25ff3f610b9eb290cf1b740de4c45d60c305ec5a
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / moubios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
5 * PURPOSE: VDM 32-bit PS/2 Mouse BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 *
9 * NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS.
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #define NDEBUG
15
16 #include "ntvdm.h"
17 #include "emulator.h"
18 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF
19
20 #include "moubios32.h"
21 #include "bios32p.h"
22
23 #include "io.h"
24 #include "hardware/mouse.h"
25 #include "hardware/ps2.h"
26
27 /* PRIVATE VARIABLES **********************************************************/
28
29 #define MOUSE_IRQ_INT 0x74
30
31 static BOOLEAN MouseEnabled = FALSE;
32 static DWORD OldIrqHandler;
33
34 /*
35 * Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA.
36 *
37 * See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm
38 * for more information. In particular:
39 * when the subroutine is called, it is given 4 WORD values on the stack;
40 * the handler should return with a FAR return without popping the stack.
41 */
42 static ULONG DeviceHandler = 0;
43
44 /* PRIVATE FUNCTIONS **********************************************************/
45
46 static VOID DisableMouseInt(VOID)
47 {
48 BYTE ControllerConfig;
49
50 /* Clear the mouse queue */
51 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
52
53 /* Disable mouse interrupt and events */
54 IOWriteB(PS2_CONTROL_PORT, 0x20);
55 ControllerConfig = IOReadB(PS2_DATA_PORT);
56 ControllerConfig &= ~0x02; // Turn off IRQ12
57 ControllerConfig |= 0x20; // Disable mouse clock line
58 IOWriteB(PS2_CONTROL_PORT, 0x60);
59 IOWriteB(PS2_DATA_PORT, ControllerConfig);
60 }
61
62 static VOID EnableMouseInt(VOID)
63 {
64 BYTE ControllerConfig;
65
66 /* Clear the mouse queue */
67 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
68
69 /* Enable mouse interrupt and events */
70 IOWriteB(PS2_CONTROL_PORT, 0x20);
71 ControllerConfig = IOReadB(PS2_DATA_PORT);
72 ControllerConfig |= 0x02; // Turn on IRQ12
73 ControllerConfig &= ~0x20; // Enable mouse clock line
74 IOWriteB(PS2_CONTROL_PORT, 0x60);
75 IOWriteB(PS2_DATA_PORT, ControllerConfig);
76 }
77
78 static inline
79 VOID SendMouseCommand(UCHAR Command)
80 {
81 /* Clear the mouse queue */
82 while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
83
84 /* Send the command */
85 IOWriteB(PS2_CONTROL_PORT, 0xD4);
86 IOWriteB(PS2_DATA_PORT, Command);
87 }
88
89 static inline
90 UCHAR ReadMouseData(VOID)
91 {
92 PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231
93 return IOReadB(PS2_DATA_PORT);
94 }
95
96
97 static
98 VOID BiosMouseEnable(VOID)
99 {
100 if (MouseEnabled) return;
101
102 MouseEnabled = TRUE;
103
104 /* Get the old IRQ handler */
105 OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT];
106
107 /* Set the IRQ handler */
108 //RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment),
109 // MOUSE_IRQ_INT, DosMouseIrq, NULL);
110 }
111
112 static
113 VOID BiosMouseDisable(VOID)
114 {
115 if (!MouseEnabled) return;
116
117 /* Restore the old IRQ handler */
118 // ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
119
120 MouseEnabled = FALSE;
121 }
122
123
124 // Mouse IRQ 12
125 static VOID WINAPI BiosMouseIrq(LPWORD Stack)
126 {
127 DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n",
128 HIWORD(DeviceHandler), LOWORD(DeviceHandler));
129
130 if (DeviceHandler != 0)
131 {
132 /*
133 * Prepare the stack for the mouse device handler:
134 * push Status, X and Y data, and a zero word.
135 */
136 setSP(getSP() - sizeof(WORD));
137 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status
138 setSP(getSP() - sizeof(WORD));
139 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0)
140 setSP(getSP() - sizeof(WORD));
141 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0)
142 setSP(getSP() - sizeof(WORD));
143 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero
144
145 /* Call the device handler */
146 RunCallback16(&BiosContext, DeviceHandler);
147
148 /* Pop the stack */
149 setSP(getSP() + 4*sizeof(WORD));
150 }
151
152 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
153 }
154
155 VOID BiosMousePs2Interface(LPWORD Stack)
156 {
157 /* Disable mouse interrupt and events */
158 DisableMouseInt();
159
160 switch (getAL())
161 {
162 /* Enable / Disable */
163 case 0x00:
164 {
165 UCHAR State = getBH();
166
167 if (State > 2)
168 {
169 /* Invalid function */
170 setAH(0x01);
171 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
172 break;
173 }
174
175 if (State == 0x00)
176 {
177 BiosMouseDisable();
178
179 /* Disable packet reporting */
180 SendMouseCommand(0xF5);
181 }
182 else // if (State == 0x01)
183 {
184 /* Check for the presence of the device handler */
185 if (DeviceHandler == 0)
186 {
187 /* No device handler installed */
188 setAH(0x05);
189 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
190 break;
191 }
192
193 BiosMouseEnable();
194
195 /* Enable packet reporting */
196 SendMouseCommand(0xF4);
197 }
198
199 if (ReadMouseData() != MOUSE_ACK)
200 {
201 /* Failure */
202 setAH(0x03);
203 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
204 break;
205 }
206
207 /* Success */
208 setAH(0x00);
209 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
210 break;
211 }
212
213 /* Initialize */
214 case 0x05:
215 {
216 // Fall through
217 }
218
219 /* Reset */
220 case 0x01:
221 {
222 UCHAR Answer;
223
224 SendMouseCommand(0xFF);
225 Answer = ReadMouseData();
226 /* A "Resend" signal (0xFE) is sent if no mouse is attached */
227 if (Answer == 0xFE)
228 {
229 /* Resend */
230 setAH(0x04);
231 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
232 break;
233 }
234 else if (Answer != MOUSE_ACK)
235 {
236 /* Failure */
237 setAH(0x03);
238 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
239 break;
240 }
241
242 setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS
243 setBH(ReadMouseData()); // Mouse ID
244
245 /* Success */
246 setAH(0x00);
247 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
248 break;
249 }
250
251 /* Set Sampling Rate */
252 case 0x02:
253 {
254 UCHAR SampleRate = 0;
255
256 switch (getBH())
257 {
258 case 0x00: SampleRate = 10; break; // 10 reports/sec
259 case 0x01: SampleRate = 20; break; // 20 " "
260 case 0x02: SampleRate = 40; break; // 40 " "
261 case 0x03: SampleRate = 60; break; // 60 " "
262 case 0x04: SampleRate = 80; break; // 80 " "
263 case 0x05: SampleRate = 100; break; // 100 " "
264 case 0x06: SampleRate = 200; break; // 200 " "
265 default: SampleRate = 0;
266 }
267
268 if (SampleRate == 0)
269 {
270 /* Invalid input */
271 setAH(0x02);
272 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
273 break;
274 }
275
276 SendMouseCommand(0xF3);
277 if (ReadMouseData() != MOUSE_ACK)
278 {
279 /* Failure */
280 setAH(0x03);
281 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
282 break;
283 }
284
285 SendMouseCommand(SampleRate);
286 if (ReadMouseData() != MOUSE_ACK)
287 {
288 /* Failure */
289 setAH(0x03);
290 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
291 break;
292 }
293
294 /* Success */
295 setAH(0x00);
296 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
297 break;
298 }
299
300 /* Set Resolution */
301 case 0x03:
302 {
303 UCHAR Resolution = getBH();
304
305 /*
306 * 0: 25 dpi, 1 count per millimeter
307 * 1: 50 dpi, 2 counts per millimeter
308 * 2: 100 dpi, 4 counts per millimeter
309 * 3: 200 dpi, 8 counts per millimeter
310 */
311 if (Resolution > 3)
312 {
313 /* Invalid input */
314 setAH(0x02);
315 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
316 break;
317 }
318
319 SendMouseCommand(0xE8);
320 if (ReadMouseData() != MOUSE_ACK)
321 {
322 /* Failure */
323 setAH(0x03);
324 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
325 break;
326 }
327
328 SendMouseCommand(Resolution);
329 if (ReadMouseData() != MOUSE_ACK)
330 {
331 /* Failure */
332 setAH(0x03);
333 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
334 break;
335 }
336
337 /* Success */
338 setAH(0x00);
339 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
340 break;
341 }
342
343 /* Get Type */
344 case 0x04:
345 {
346 SendMouseCommand(0xF2);
347 if (ReadMouseData() != MOUSE_ACK)
348 {
349 /* Failure */
350 setAH(0x03);
351 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
352 break;
353 }
354
355 setBH(ReadMouseData());
356
357 /* Success */
358 setAH(0x00);
359 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
360 break;
361 }
362
363 /* Extended Commands (Return Status and Set Scaling Factor) */
364 case 0x06:
365 {
366 UCHAR Command = getBH();
367
368 switch (Command)
369 {
370 /* Return Status */
371 case 0x00:
372 {
373 SendMouseCommand(0xE9);
374 if (ReadMouseData() != MOUSE_ACK)
375 {
376 /* Failure */
377 setAH(0x03);
378 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
379 break;
380 }
381
382 setBL(ReadMouseData()); // Status
383 setCL(ReadMouseData()); // Resolution
384 setDL(ReadMouseData()); // Sample rate
385
386 /* Success */
387 setAH(0x00);
388 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
389 break;
390 }
391
392 /* Set Scaling Factor to 1:1 */
393 case 0x01:
394 /* Set Scaling Factor to 2:1 */
395 case 0x02:
396 {
397 SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7);
398 if (ReadMouseData() != MOUSE_ACK)
399 {
400 /* Failure */
401 setAH(0x03);
402 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
403 break;
404 }
405
406 /* Success */
407 setAH(0x00);
408 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
409 break;
410 }
411
412 default:
413 {
414 /* Invalid function */
415 setAH(0x01);
416 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
417 break;
418 }
419 }
420
421 break;
422 }
423
424 /* Set Device Handler Address */
425 case 0x07:
426 {
427 /* ES:BX == 0000h:0000h removes the device handler */
428 DeviceHandler = MAKELONG(getBX(), getES());
429
430 /* Success */
431 setAH(0x00);
432 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
433 break;
434 }
435
436 /* Write to Pointer Port */
437 case 0x08:
438 {
439 SendMouseCommand(getBL());
440 if (ReadMouseData() != MOUSE_ACK)
441 {
442 /* Failure */
443 setAH(0x03);
444 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
445 break;
446 }
447
448 /* Success */
449 setAH(0x00);
450 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
451 break;
452 }
453
454 /* Read from Pointer Port */
455 case 0x09:
456 {
457 setBL(ReadMouseData());
458 setCL(ReadMouseData());
459 setDL(ReadMouseData());
460
461 /* Success */
462 setAH(0x00);
463 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
464 break;
465 }
466
467 default:
468 {
469 DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n",
470 getAL());
471
472 /* Unknown function */
473 setAH(0x01);
474 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
475 }
476 }
477
478 /* Reenable mouse interrupt and events */
479 EnableMouseInt();
480 }
481
482 /* PUBLIC FUNCTIONS ***********************************************************/
483
484 VOID MouseBios32Post(VOID)
485 {
486 UCHAR Answer;
487
488 /* Initialize PS/2 mouse port */
489 // Enable the port
490 IOWriteB(PS2_CONTROL_PORT, 0xA8);
491
492 /* Detect mouse presence by attempting a reset */
493 SendMouseCommand(0xFF);
494 Answer = ReadMouseData();
495 /* A "Resend" signal (0xFE) is sent if no mouse is attached */
496 if (Answer == 0xFE)
497 {
498 DPRINT1("No mouse present!\n");
499 }
500 else if (Answer != MOUSE_ACK)
501 {
502 DPRINT1("Mouse reset failure!\n");
503 }
504 else
505 {
506 /* Mouse present, try to completely enable it */
507
508 // FIXME: The following is temporary until
509 // this is moved into the mouse driver!!
510
511 /* Enable packet reporting */
512 SendMouseCommand(0xF4);
513 if (ReadMouseData() != MOUSE_ACK)
514 {
515 DPRINT1("Failed to enable mouse!\n");
516 }
517 else
518 {
519 /* Enable mouse interrupt and events */
520 EnableMouseInt();
521 }
522 }
523
524 /* No mouse driver available so far */
525 RegisterBiosInt32(0x33, NULL);
526
527 /* Set up the HW vector interrupts */
528 EnableHwIRQ(12, BiosMouseIrq);
529 }
530
531 BOOLEAN MouseBiosInitialize(VOID)
532 {
533 return TRUE;
534 }
535
536 VOID MouseBios32Cleanup(VOID)
537 {
538 }