[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / mouse32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: mouse32.c
5 * PURPOSE: VDM 32-bit compatible MOUSE.COM driver
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "cpu/cpu.h"
15 #include "int32.h"
16 #include "hardware/mouse.h"
17 #include "hardware/ps2.h"
18 #include "hardware/pic.h"
19 #include "hardware/video/vga.h"
20
21 #include "mouse32.h"
22 #include "bios/bios.h"
23 #include "bios/bios32/bios32p.h"
24
25 #include "io.h"
26 #include "dos32krnl/dos.h"
27
28 /* PRIVATE VARIABLES **********************************************************/
29
30 #define MICKEYS_PER_CELL_HORIZ 8
31 #define MICKEYS_PER_CELL_VERT 16
32
33 static MOUSE_PACKET Packet;
34 static INT ByteCounter = 0;
35 static BOOLEAN DriverEnabled = FALSE;
36 static MOUSE_DRIVER_STATE DriverState;
37 static DWORD OldIrqHandler;
38
39 /* PRIVATE FUNCTIONS **********************************************************/
40
41 extern VOID WINAPI BiosMouseIrq(LPWORD Stack);
42
43 static VOID PaintMouseCursor(VOID)
44 {
45 if (Bda->VideoMode <= 3)
46 {
47 WORD Character;
48 WORD CellX = DriverState.Position.X / 8;
49 WORD CellY = DriverState.Position.Y / 8;
50 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
51
52 EmulatorReadMemory(&EmulatorContext,
53 VideoAddress
54 + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
55 (LPVOID)&Character,
56 sizeof(WORD));
57
58 DriverState.Character = Character;
59 Character &= DriverState.TextCursor.ScreenMask;
60 Character ^= DriverState.TextCursor.CursorMask;
61
62 EmulatorWriteMemory(&EmulatorContext,
63 VideoAddress
64 + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
65 (LPVOID)&Character,
66 sizeof(WORD));
67 }
68 else
69 {
70 // TODO: NOT IMPLEMENTED
71 UNIMPLEMENTED;
72 }
73 }
74
75 static VOID EraseMouseCursor(VOID)
76 {
77 if (Bda->VideoMode <= 3)
78 {
79 WORD CellX = DriverState.Position.X / 8;
80 WORD CellY = DriverState.Position.Y / 8;
81 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
82
83 EmulatorWriteMemory(&EmulatorContext,
84 VideoAddress
85 + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
86 (LPVOID)&DriverState.Character,
87 sizeof(WORD));
88 }
89 else
90 {
91 // TODO: NOT IMPLEMENTED
92 UNIMPLEMENTED;
93 }
94 }
95
96 static VOID ToMouseCoordinates(PCOORD Position)
97 {
98 COORD Resolution = VgaGetDisplayResolution();
99 WORD Width = DriverState.MaxX - DriverState.MinX + 1;
100 WORD Height = DriverState.MaxY - DriverState.MinY + 1;
101
102 if (!VgaGetDoubleVisionState(NULL, NULL))
103 {
104 Resolution.X *= 8;
105 Resolution.Y *= 8;
106 }
107
108 Position->X = DriverState.MinX + ((Position->X * Width) / Resolution.X);
109 Position->Y = DriverState.MinY + ((Position->Y * Height) / Resolution.Y);
110 }
111
112 static VOID FromMouseCoordinates(PCOORD Position)
113 {
114 COORD Resolution = VgaGetDisplayResolution();
115 WORD Width = DriverState.MaxX - DriverState.MinX + 1;
116 WORD Height = DriverState.MaxY - DriverState.MinY + 1;
117
118 if (!VgaGetDoubleVisionState(NULL, NULL))
119 {
120 Resolution.X *= 8;
121 Resolution.Y *= 8;
122 }
123
124 Position->X = (((Position->X - DriverState.MinX) * Resolution.X) / Width);
125 Position->Y = (((Position->Y - DriverState.MinY) * Resolution.Y) / Height);
126 }
127
128 static VOID CallMouseUserHandlers(USHORT CallMask)
129 {
130 USHORT i;
131 USHORT AX, BX, CX, DX, SI, DI;
132 COORD Position = DriverState.Position;
133
134 ToMouseCoordinates(&Position);
135
136 /* Call handler 0 */
137 if ((DriverState.Handler0.CallMask & CallMask) != 0 &&
138 DriverState.Handler0.Callback != (ULONG)NULL)
139 {
140 /*
141 * Set the parameters for the callback.
142 * NOTE: In text modes, the row and column will be reported
143 * as a multiple of the cell size, typically 8x8 pixels.
144 */
145
146 AX = getAX();
147 BX = getBX();
148 CX = getCX();
149 DX = getDX();
150 SI = getSI();
151 DI = getDI();
152
153 setAX(CallMask);
154 setBX(DriverState.ButtonState);
155 setCX(Position.X);
156 setDX(Position.Y);
157 setSI(MICKEYS_PER_CELL_HORIZ);
158 setDI(MICKEYS_PER_CELL_VERT);
159
160 DPRINT("Calling Handler0 %04X:%04X with CallMask 0x%04X\n",
161 HIWORD(DriverState.Handler0.Callback),
162 LOWORD(DriverState.Handler0.Callback),
163 CallMask);
164
165 /* Call the callback */
166 RunCallback16(&DosContext, DriverState.Handler0.Callback);
167
168 setAX(AX);
169 setBX(BX);
170 setCX(CX);
171 setDX(DX);
172 setSI(SI);
173 setDI(DI);
174 }
175
176 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
177 {
178 /* Call the suitable handlers */
179 if ((DriverState.Handlers[i].CallMask & CallMask) != 0 &&
180 DriverState.Handlers[i].Callback != (ULONG)NULL)
181 {
182 /*
183 * Set the parameters for the callback.
184 * NOTE: In text modes, the row and column will be reported
185 * as a multiple of the cell size, typically 8x8 pixels.
186 */
187
188 AX = getAX();
189 BX = getBX();
190 CX = getCX();
191 DX = getDX();
192 SI = getSI();
193 DI = getDI();
194
195 setAX(CallMask);
196 setBX(DriverState.ButtonState);
197 setCX(Position.X);
198 setDX(Position.Y);
199 setSI(MICKEYS_PER_CELL_HORIZ);
200 setDI(MICKEYS_PER_CELL_VERT);
201
202 DPRINT1("Calling Handler[%d] %04X:%04X with CallMask 0x%04X\n",
203 i,
204 HIWORD(DriverState.Handlers[i].Callback),
205 LOWORD(DriverState.Handlers[i].Callback),
206 CallMask);
207
208 /* Call the callback */
209 RunCallback16(&DosContext, DriverState.Handlers[i].Callback);
210
211 setAX(AX);
212 setBX(BX);
213 setCX(CX);
214 setDX(DX);
215 setSI(SI);
216 setDI(DI);
217 }
218 }
219 }
220
221 static inline VOID DosUpdatePosition(PCOORD NewPosition)
222 {
223 COORD Resolution = VgaGetDisplayResolution();
224
225 /* Check for text mode */
226 if (!VgaGetDoubleVisionState(NULL, NULL))
227 {
228 Resolution.X *= 8;
229 Resolution.Y *= 8;
230 }
231
232 if (DriverState.ShowCount > 0) EraseMouseCursor();
233 DriverState.Position = *NewPosition;
234
235 /* Apply the clipping rectangle */
236 if (DriverState.Position.X < DriverState.MinX) DriverState.Position.X = DriverState.MinX;
237 if (DriverState.Position.X > DriverState.MaxX) DriverState.Position.X = DriverState.MaxX;
238 if (DriverState.Position.Y < DriverState.MinY) DriverState.Position.Y = DriverState.MinY;
239 if (DriverState.Position.Y > DriverState.MaxY) DriverState.Position.Y = DriverState.MaxY;
240
241 if (DriverState.ShowCount > 0) PaintMouseCursor();
242
243 /* Call the mouse handlers */
244 CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
245 }
246
247 static inline VOID DosUpdateButtons(BYTE ButtonState)
248 {
249 USHORT i;
250 USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format
251
252 for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
253 {
254 BOOLEAN OldState = (DriverState.ButtonState >> i) & 1;
255 BOOLEAN NewState = (ButtonState >> i) & 1;
256
257 if (NewState > OldState)
258 {
259 /* Mouse press */
260 DriverState.PressCount[i]++;
261 DriverState.LastPress[i] = DriverState.Position;
262
263 CallMask |= (1 << (2 * i + 1));
264 }
265 else if (NewState < OldState)
266 {
267 /* Mouse release */
268 DriverState.ReleaseCount[i]++;
269 DriverState.LastRelease[i] = DriverState.Position;
270
271 CallMask |= (1 << (2 * i + 2));
272 }
273 }
274
275 DriverState.ButtonState = ButtonState;
276
277 /* Call the mouse handlers */
278 CallMouseUserHandlers(CallMask);
279 }
280
281 static VOID WINAPI DosMouseIrq(LPWORD Stack)
282 {
283 COORD Position;
284 BYTE ButtonState;
285
286 switch (ByteCounter)
287 {
288 case 0:
289 {
290 Packet.Flags = IOReadB(PS2_DATA_PORT);
291 break;
292 }
293
294 case 1:
295 {
296 Packet.HorzCounter = IOReadB(PS2_DATA_PORT);
297 break;
298 }
299
300 case 2:
301 {
302 Packet.VertCounter = IOReadB(PS2_DATA_PORT);
303 break;
304 }
305
306 default:
307 {
308 /* Shouldn't happen */
309 ASSERT(FALSE);
310 }
311 }
312
313 if (++ByteCounter == 3)
314 {
315 SHORT DeltaX = Packet.HorzCounter;
316 SHORT DeltaY = Packet.VertCounter;
317
318 /* Adjust the sign */
319 if (Packet.Flags & MOUSE_X_SIGN) DeltaX = -DeltaX;
320 if (Packet.Flags & MOUSE_Y_SIGN) DeltaY = -DeltaY;
321
322 DriverState.HorizCount += DeltaX;
323 DriverState.VertCount += DeltaY;
324
325 ByteCounter = 0;
326 }
327
328 /*
329 * Get the absolute position directly from the mouse, this is the only
330 * way to perfectly synchronize the host and guest mouse pointer.
331 */
332 MouseGetDataFast(&Position, &ButtonState);
333
334 /* Call the update subroutines */
335 DosUpdatePosition(&Position);
336 DosUpdateButtons(ButtonState);
337
338 /* Complete the IRQ */
339 PicIRQComplete(Stack);
340 }
341
342 static VOID WINAPI DosMouseService(LPWORD Stack)
343 {
344 switch (getAX())
345 {
346 /* Reset Driver */
347 case 0x00:
348 {
349 SHORT i;
350
351 DriverState.ShowCount = 0;
352 DriverState.ButtonState = 0;
353
354 /* Initialize the default clipping range */
355 DriverState.MinX = 0;
356 DriverState.MaxX = MOUSE_MAX_HORIZ - 1;
357 DriverState.MinY = 0;
358 DriverState.MaxY = MOUSE_MAX_VERT - 1;
359
360 /* Set the default text cursor */
361 DriverState.TextCursor.ScreenMask = 0xFFFF; /* Display everything */
362 DriverState.TextCursor.CursorMask = 0xFF00; /* ... but with inverted attributes */
363
364 /* Set the default graphics cursor */
365 DriverState.GraphicsCursor.HotSpot.X = 3;
366 DriverState.GraphicsCursor.HotSpot.Y = 1;
367
368 DriverState.GraphicsCursor.ScreenMask[0] = 0xC3FF; // 1100001111111111
369 DriverState.GraphicsCursor.ScreenMask[1] = 0xC0FF; // 1100000011111111
370 DriverState.GraphicsCursor.ScreenMask[2] = 0xC07F; // 1100000001111111
371 DriverState.GraphicsCursor.ScreenMask[3] = 0xC01F; // 1100000000011111
372 DriverState.GraphicsCursor.ScreenMask[4] = 0xC00F; // 1100000000001111
373 DriverState.GraphicsCursor.ScreenMask[5] = 0xC007; // 1100000000000111
374 DriverState.GraphicsCursor.ScreenMask[6] = 0xC003; // 1100000000000011
375 DriverState.GraphicsCursor.ScreenMask[7] = 0xC007; // 1100000000000111
376 DriverState.GraphicsCursor.ScreenMask[8] = 0xC01F; // 1100000000011111
377 DriverState.GraphicsCursor.ScreenMask[9] = 0xC01F; // 1100000000011111
378 DriverState.GraphicsCursor.ScreenMask[10] = 0xC00F; // 1100000000001111
379 DriverState.GraphicsCursor.ScreenMask[11] = 0xC60F; // 1100011000001111
380 DriverState.GraphicsCursor.ScreenMask[12] = 0xFF07; // 1111111100000111
381 DriverState.GraphicsCursor.ScreenMask[13] = 0xFF07; // 1111111100000111
382 DriverState.GraphicsCursor.ScreenMask[14] = 0xFF87; // 1111111110000111
383 DriverState.GraphicsCursor.ScreenMask[15] = 0xFFCF; // 1111111111001111
384
385 DriverState.GraphicsCursor.CursorMask[0] = 0x0000; // 0000000000000000
386 DriverState.GraphicsCursor.CursorMask[1] = 0x1C00; // 0001110000000000
387 DriverState.GraphicsCursor.CursorMask[2] = 0x1F00; // 0001111100000000
388 DriverState.GraphicsCursor.CursorMask[3] = 0x1F80; // 0001111110000000
389 DriverState.GraphicsCursor.CursorMask[4] = 0x1FE0; // 0001111111100000
390 DriverState.GraphicsCursor.CursorMask[5] = 0x1FF0; // 0001111111110000
391 DriverState.GraphicsCursor.CursorMask[6] = 0x1FF8; // 0001111111111000
392 DriverState.GraphicsCursor.CursorMask[7] = 0x1FE0; // 0001111111100000
393 DriverState.GraphicsCursor.CursorMask[8] = 0x1FC0; // 0001111111000000
394 DriverState.GraphicsCursor.CursorMask[9] = 0x1FC0; // 0001111111000000
395 DriverState.GraphicsCursor.CursorMask[10] = 0x19E0; // 0001100111100000
396 DriverState.GraphicsCursor.CursorMask[11] = 0x00E0; // 0000000011100000
397 DriverState.GraphicsCursor.CursorMask[12] = 0x0070; // 0000000001110000
398 DriverState.GraphicsCursor.CursorMask[13] = 0x0070; // 0000000001110000
399 DriverState.GraphicsCursor.CursorMask[14] = 0x0030; // 0000000000110000
400 DriverState.GraphicsCursor.CursorMask[15] = 0x0000; // 0000000000000000
401
402 /* Initialize the counters */
403 DriverState.HorizCount = DriverState.VertCount = 0;
404
405 for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
406 {
407 DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0;
408 }
409
410 /* Return mouse information */
411 setAX(0xFFFF); // Hardware & driver installed
412 setBX(NUM_MOUSE_BUTTONS);
413
414 break;
415 }
416
417 /* Show Mouse Cursor */
418 case 0x01:
419 {
420 DriverState.ShowCount++;
421 if (DriverState.ShowCount == 1) PaintMouseCursor();
422
423 break;
424 }
425
426 /* Hide Mouse Cursor */
427 case 0x02:
428 {
429 DriverState.ShowCount--;
430 if (DriverState.ShowCount == 0) EraseMouseCursor();
431
432 break;
433 }
434
435 /* Return Position And Button Status */
436 case 0x03:
437 {
438 COORD Position = DriverState.Position;
439 ToMouseCoordinates(&Position);
440
441 setBX(DriverState.ButtonState);
442 setCX(Position.X);
443 setDX(Position.Y);
444 break;
445 }
446
447 /* Position Mouse Cursor */
448 case 0x04:
449 {
450 COORD Position = { getCX(), getDX() };
451 FromMouseCoordinates(&Position);
452
453 DriverState.Position = Position;
454 break;
455 }
456
457 /* Return Button Press Data */
458 case 0x05:
459 {
460 WORD Button = getBX();
461 COORD LastPress = DriverState.LastPress[Button];
462 ToMouseCoordinates(&LastPress);
463
464 setAX(DriverState.ButtonState);
465 setBX(DriverState.PressCount[Button]);
466 setCX(LastPress.X);
467 setDX(LastPress.Y);
468
469 /* Reset the counter */
470 DriverState.PressCount[Button] = 0;
471
472 break;
473 }
474
475 /* Return Button Release Data */
476 case 0x06:
477 {
478 WORD Button = getBX();
479 COORD LastRelease = DriverState.LastRelease[Button];
480 ToMouseCoordinates(&LastRelease);
481
482 setAX(DriverState.ButtonState);
483 setBX(DriverState.ReleaseCount[Button]);
484 setCX(LastRelease.X);
485 setDX(LastRelease.Y);
486
487 /* Reset the counter */
488 DriverState.ReleaseCount[Button] = 0;
489
490 break;
491
492 }
493
494 /* Define Horizontal Cursor Range */
495 case 0x07:
496 {
497 DPRINT("Setting mouse horizontal range: %u - %u\n", getCX(), getDX());
498 DriverState.MinX = getCX();
499 DriverState.MaxX = getDX();
500 break;
501 }
502
503 /* Define Vertical Cursor Range */
504 case 0x08:
505 {
506 DPRINT("Setting mouse vertical range: %u - %u\n", getCX(), getDX());
507 DriverState.MinY = getCX();
508 DriverState.MaxY = getDX();
509 break;
510 }
511
512 /* Define Graphics Cursor */
513 case 0x09:
514 {
515 PWORD MaskBitmap = (PWORD)SEG_OFF_TO_PTR(getES(), getDX());
516
517 DriverState.GraphicsCursor.HotSpot.X = getBX();
518 DriverState.GraphicsCursor.HotSpot.Y = getCX();
519
520 RtlCopyMemory(DriverState.GraphicsCursor.ScreenMask,
521 MaskBitmap,
522 sizeof(DriverState.GraphicsCursor.ScreenMask));
523
524 RtlCopyMemory(DriverState.GraphicsCursor.CursorMask,
525 &MaskBitmap[16],
526 sizeof(DriverState.GraphicsCursor.CursorMask));
527
528 break;
529 }
530
531 /* Define Text Cursor */
532 case 0x0A:
533 {
534 USHORT BX = getBX();
535
536 if (BX == 0x0000)
537 {
538 /* Define software cursor */
539 DriverState.TextCursor.ScreenMask = getCX();
540 DriverState.TextCursor.CursorMask = getDX();
541 }
542 else if (BX == 0x0001)
543 {
544 /* Define hardware cursor */
545 DPRINT1("Defining hardware cursor is unimplemented\n");
546 UNIMPLEMENTED;
547 // CX == start scan line
548 // DX == end scan line
549 }
550 else
551 {
552 DPRINT1("Invalid BX value 0x%04X\n", BX);
553 }
554
555 break;
556 }
557
558 /* Read Motion Counters */
559 case 0x0B:
560 {
561 setCX(DriverState.HorizCount);
562 setDX(DriverState.VertCount);
563
564 /* Reset the counters */
565 DriverState.HorizCount = DriverState.VertCount = 0;
566
567 break;
568 }
569
570 /* Define Interrupt Subroutine Parameters, compatible MS MOUSE v1.0+ */
571 case 0x0C:
572 {
573 DriverState.Handler0.CallMask = getCX();
574 DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
575 DPRINT1("Define callback 0x%04X, %04X:%04X\n",
576 DriverState.Handler0.CallMask,
577 HIWORD(DriverState.Handler0.Callback),
578 LOWORD(DriverState.Handler0.Callback));
579 break;
580 }
581
582 /* Define Mickey/Pixel Ratio */
583 case 0x0F:
584 {
585 /* This call should be completely ignored */
586 break;
587 }
588
589 /* Exchange Interrupt Subroutines, compatible MS MOUSE v3.0+ (see function 0x0C) */
590 case 0x14:
591 {
592 USHORT OldCallMask = DriverState.Handler0.CallMask;
593 ULONG OldCallback = DriverState.Handler0.Callback;
594
595 DriverState.Handler0.CallMask = getCX();
596 DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
597
598 /* Return old callmask in CX and callback vector in ES:DX */
599 setCX(OldCallMask);
600 setES(HIWORD(OldCallback));
601 setDX(LOWORD(OldCallback));
602
603 break;
604 }
605
606 /* Return Driver Storage Requirements */
607 case 0x15:
608 {
609 setBX(sizeof(MOUSE_DRIVER_STATE));
610 break;
611 }
612
613 /* Save Driver State */
614 case 0x16:
615 {
616 *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX())) = DriverState;
617 break;
618 }
619
620 /* Restore Driver State */
621 case 0x17:
622 {
623 DriverState = *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX()));
624 break;
625 }
626
627 /* Set Alternate Mouse User Handler, compatible MS MOUSE v6.0+ */
628 case 0x18:
629 {
630 /*
631 * Up to three handlers can be defined by separate calls to this
632 * function, each with a different combination of shift states in
633 * the call mask; calling this function again with a call mask of
634 * 0000h undefines the specified handler (official documentation);
635 * specifying the same call mask and an address of 0000h:0000h
636 * undefines the handler (real life).
637 * See Ralf Brown: http://www.ctyme.com/intr/rb-5981.htm
638 * for more information.
639 */
640
641 USHORT i;
642 USHORT CallMask = getCX();
643 ULONG Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
644 BOOLEAN Success = FALSE;
645
646 DPRINT1("Define v6.0+ callback 0x%04X, %04X:%04X\n",
647 CallMask, HIWORD(Callback), LOWORD(Callback));
648
649 if (CallMask == 0x0000)
650 {
651 /*
652 * Find the handler entry corresponding to the given
653 * callback and undefine it.
654 */
655 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
656 {
657 if (DriverState.Handlers[i].Callback == Callback)
658 {
659 /* Found it, undefine the handler */
660 DriverState.Handlers[i].CallMask = 0x0000;
661 DriverState.Handlers[i].Callback = (ULONG)NULL;
662 Success = TRUE;
663 break;
664 }
665 }
666 }
667 else if (Callback == (ULONG)NULL)
668 {
669 /*
670 * Find the handler entry corresponding to the given
671 * callmask and undefine it.
672 */
673 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
674 {
675 if (DriverState.Handlers[i].CallMask == CallMask)
676 {
677 /* Found it, undefine the handler */
678 DriverState.Handlers[i].CallMask = 0x0000;
679 DriverState.Handlers[i].Callback = (ULONG)NULL;
680 Success = TRUE;
681 break;
682 }
683 }
684 }
685 else
686 {
687 /*
688 * Try to find a handler entry corresponding to the given
689 * callmask to redefine it, otherwise find an empty handler
690 * entry and set the new handler in there.
691 */
692
693 USHORT EmptyHandler = 0xFFFF; // Invalid handler
694
695 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
696 {
697 /* Find the first empty handler */
698 if (EmptyHandler == 0xFFFF &&
699 DriverState.Handlers[i].CallMask == 0x0000 &&
700 DriverState.Handlers[i].Callback == (ULONG)NULL)
701 {
702 EmptyHandler = i;
703 }
704
705 if (DriverState.Handlers[i].CallMask == CallMask)
706 {
707 /* Found it, redefine the handler */
708 DriverState.Handlers[i].CallMask = CallMask;
709 DriverState.Handlers[i].Callback = Callback;
710 Success = TRUE;
711 break;
712 }
713 }
714
715 /*
716 * If we haven't found anything and we found
717 * an empty handler, set it.
718 */
719 if (!Success && EmptyHandler != 0xFFFF
720 /* && EmptyHandler < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]) */)
721 {
722 DriverState.Handlers[EmptyHandler].CallMask = CallMask;
723 DriverState.Handlers[EmptyHandler].Callback = Callback;
724 Success = TRUE;
725 }
726 }
727
728 /* If we failed, set error code */
729 if (!Success) setAX(0xFFFF);
730
731 break;
732 }
733
734 /* Return User Alternate Interrupt Vector, compatible MS MOUSE v6.0+ */
735 case 0x19:
736 {
737 USHORT i;
738 USHORT CallMask = getCX();
739 ULONG Callback;
740 BOOLEAN Success = FALSE;
741
742 /*
743 * Find the handler entry corresponding to the given callmask.
744 */
745 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
746 {
747 if (DriverState.Handlers[i].CallMask == CallMask)
748 {
749 /* Found it */
750 Callback = DriverState.Handlers[i].Callback;
751 Success = TRUE;
752 break;
753 }
754 }
755
756 if (Success)
757 {
758 /* Return the callback vector in BX:DX */
759 setBX(HIWORD(Callback));
760 setDX(LOWORD(Callback));
761 }
762 else
763 {
764 /* We failed, set error code */
765 setCX(0x0000);
766 }
767
768 break;
769 }
770
771 /* Disable Mouse Driver */
772 case 0x1F:
773 {
774 setES(0x0000);
775 setBX(0x0000);
776
777 DosMouseDisable();
778 break;
779 }
780
781 /* Enable Mouse Driver */
782 case 0x20:
783 {
784 DosMouseEnable();
785 break;
786 }
787
788 default:
789 {
790 DPRINT1("BIOS Function INT 33h, AX = 0x%04X NOT IMPLEMENTED\n", getAX());
791 }
792 }
793 }
794
795 /* PUBLIC FUNCTIONS ***********************************************************/
796
797 VOID DosMouseEnable(VOID)
798 {
799 if (!DriverEnabled)
800 {
801 DriverEnabled = TRUE;
802
803 /* Get the old IRQ handler */
804 OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT];
805
806 /* Set the IRQ handler */
807 RegisterDosInt32(MOUSE_IRQ_INT, DosMouseIrq);
808
809 /* Enable packet reporting */
810 IOWriteB(PS2_CONTROL_PORT, 0xD4);
811 IOWriteB(PS2_DATA_PORT, 0xF4);
812
813 /* Read the mouse ACK reply */
814 PS2PortQueueRead(1);
815 }
816 }
817
818 VOID DosMouseDisable(VOID)
819 {
820 if (DriverEnabled)
821 {
822 /* Restore the old IRQ handler */
823 ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
824
825 DriverEnabled = FALSE;
826 }
827 }
828
829 VOID DosMouseUpdatePosition(PCOORD NewPosition)
830 {
831 SHORT DeltaX = NewPosition->X - DriverState.Position.X;
832 SHORT DeltaY = NewPosition->Y - DriverState.Position.Y;
833
834 if (!DriverEnabled) return;
835
836 DriverState.HorizCount += (DeltaX * MICKEYS_PER_CELL_HORIZ) / 8;
837 DriverState.VertCount += (DeltaY * MICKEYS_PER_CELL_VERT) / 8;
838
839 if (DriverState.ShowCount > 0) EraseMouseCursor();
840 DriverState.Position = *NewPosition;
841 if (DriverState.ShowCount > 0) PaintMouseCursor();
842
843 /* Call the mouse handlers */
844 // if (DeltaX || DeltaY)
845 CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
846 }
847
848 VOID DosMouseUpdateButtons(WORD ButtonState)
849 {
850 USHORT i;
851 USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format
852
853 if (!DriverEnabled) return;
854
855 for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
856 {
857 BOOLEAN OldState = (DriverState.ButtonState >> i) & 1;
858 BOOLEAN NewState = (ButtonState >> i) & 1;
859
860 if (NewState > OldState)
861 {
862 /* Mouse press */
863 DriverState.PressCount[i]++;
864 DriverState.LastPress[i] = DriverState.Position;
865
866 CallMask |= (1 << (2 * i + 1));
867 }
868 else if (NewState < OldState)
869 {
870 /* Mouse release */
871 DriverState.ReleaseCount[i]++;
872 DriverState.LastRelease[i] = DriverState.Position;
873
874 CallMask |= (1 << (2 * i + 2));
875 }
876 }
877
878 DriverState.ButtonState = ButtonState;
879
880 /* Call the mouse handlers */
881 CallMouseUserHandlers(CallMask);
882 }
883
884 BOOLEAN DosMouseInitialize(VOID)
885 {
886 /* Clear the state */
887 RtlZeroMemory(&DriverState, sizeof(DriverState));
888
889 /* Initialize the interrupt handler */
890 RegisterDosInt32(DOS_MOUSE_INTERRUPT, DosMouseService);
891
892 DosMouseEnable();
893 return TRUE;
894 }
895
896 VOID DosMouseCleanup(VOID)
897 {
898 if (DriverState.ShowCount > 0) EraseMouseCursor();
899 DosMouseDisable();
900 }