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