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