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