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