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