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