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