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