[NTVDM]
[reactos.git] / reactos / subsystems / 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 "emulator.h"
14 #include "cpu/cpu.h"
15 #include "int32.h"
16
17 #include "mouse32.h"
18 #include "bios/bios.h"
19
20 #include "io.h"
21 #include "dos32krnl/dos.h"
22
23 /* PRIVATE VARIABLES **********************************************************/
24
25 static BOOLEAN DriverEnabled = TRUE;
26 static MOUSE_DRIVER_STATE DriverState;
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 static VOID PaintMouseCursor(VOID)
31 {
32 if (Bda->VideoMode <= 3)
33 {
34 WORD Character;
35 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
36
37 EmulatorReadMemory(&EmulatorContext,
38 VideoAddress
39 + (DriverState.Position.Y * Bda->ScreenColumns
40 + DriverState.Position.X) * sizeof(WORD),
41 (LPVOID)&Character,
42 sizeof(WORD));
43
44 DriverState.Character = Character;
45 Character &= DriverState.TextCursor.ScreenMask;
46 Character ^= DriverState.TextCursor.CursorMask;
47
48 EmulatorWriteMemory(&EmulatorContext,
49 VideoAddress
50 + (DriverState.Position.Y * Bda->ScreenColumns
51 + DriverState.Position.X) * sizeof(WORD),
52 (LPVOID)&Character,
53 sizeof(WORD));
54 }
55 else
56 {
57 // TODO: NOT IMPLEMENTED
58 UNIMPLEMENTED;
59 }
60 }
61
62 static VOID EraseMouseCursor(VOID)
63 {
64 if (Bda->VideoMode <= 3)
65 {
66 DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
67
68 EmulatorWriteMemory(&EmulatorContext,
69 VideoAddress
70 + (DriverState.Position.Y * Bda->ScreenColumns
71 + DriverState.Position.X) * sizeof(WORD),
72 (LPVOID)&DriverState.Character,
73 sizeof(WORD));
74 }
75 else
76 {
77 // TODO: NOT IMPLEMENTED
78 UNIMPLEMENTED;
79 }
80 }
81
82 static VOID CallMouseUserHandlers(USHORT CallMask)
83 {
84 #if 0
85 USHORT i;
86 USHORT AX, BX, CX, DX, SI, DI;
87
88 /* Call handler 0 */
89 if ((DriverState.Handler0.CallMask & CallMask) != 0 &&
90 DriverState.Handler0.Callback != (ULONG)NULL)
91 {
92 /*
93 * Set the parameters for the callback.
94 * NOTE: In text modes, the row and column will be reported
95 * as a multiple of the cell size, typically 8x8 pixels.
96 */
97
98 AX = getAX();
99 BX = getBX();
100 CX = getCX();
101 DX = getDX();
102 SI = getSI();
103 DI = getDI();
104
105 setAX(CallMask);
106 setBX(DriverState.ButtonState);
107 setCX(DriverState.Position.X);
108 setDX(DriverState.Position.Y);
109 setSI(DriverState.MickeysPerCellHoriz);
110 setDI(DriverState.MickeysPerCellVert);
111
112 DPRINT1("Calling Handler0 %04X:%04X with CallMask 0x%04X\n",
113 HIWORD(DriverState.Handler0.Callback),
114 LOWORD(DriverState.Handler0.Callback),
115 CallMask);
116
117 /* Call the callback */
118 RunCallback16(&DosContext, DriverState.Handler0.Callback);
119
120 setAX(AX);
121 setBX(BX);
122 setCX(CX);
123 setDX(DX);
124 setSI(SI);
125 setDI(DI);
126 }
127
128 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
129 {
130 /* Call the suitable handlers */
131 if ((DriverState.Handlers[i].CallMask & CallMask) != 0 &&
132 DriverState.Handlers[i].Callback != (ULONG)NULL)
133 {
134 /*
135 * Set the parameters for the callback.
136 * NOTE: In text modes, the row and column will be reported
137 * as a multiple of the cell size, typically 8x8 pixels.
138 */
139
140 AX = getAX();
141 BX = getBX();
142 CX = getCX();
143 DX = getDX();
144 SI = getSI();
145 DI = getDI();
146
147 setAX(CallMask);
148 setBX(DriverState.ButtonState);
149 setCX(DriverState.Position.X);
150 setDX(DriverState.Position.Y);
151 setSI(DriverState.MickeysPerCellHoriz);
152 setDI(DriverState.MickeysPerCellVert);
153
154 DPRINT1("Calling Handler[%d] %04X:%04X with CallMask 0x%04X\n",
155 i,
156 HIWORD(DriverState.Handlers[i].Callback),
157 LOWORD(DriverState.Handlers[i].Callback),
158 CallMask);
159
160 /* Call the callback */
161 RunCallback16(&DosContext, DriverState.Handlers[i].Callback);
162
163 setAX(AX);
164 setBX(BX);
165 setCX(CX);
166 setDX(DX);
167 setSI(SI);
168 setDI(DI);
169 }
170 }
171 #endif
172 }
173
174 static VOID WINAPI BiosMouseService(LPWORD Stack)
175 {
176 switch (getAX())
177 {
178 /* Reset Driver */
179 case 0x00:
180 {
181 SHORT i;
182
183 DriverEnabled = TRUE;
184 DriverState.ShowCount = 0;
185 DriverState.ButtonState = 0;
186
187 /* Set the default text cursor */
188 DriverState.TextCursor.ScreenMask = 0xFFFF; /* Display everything */
189 DriverState.TextCursor.CursorMask = 0xFF00; /* ... but with inverted attributes */
190
191 /* Set the default graphics cursor */
192 DriverState.GraphicsCursor.HotSpot.X = 3;
193 DriverState.GraphicsCursor.HotSpot.Y = 1;
194
195 DriverState.GraphicsCursor.ScreenMask[0] = 0xC3FF; // 1100001111111111
196 DriverState.GraphicsCursor.ScreenMask[1] = 0xC0FF; // 1100000011111111
197 DriverState.GraphicsCursor.ScreenMask[2] = 0xC07F; // 1100000001111111
198 DriverState.GraphicsCursor.ScreenMask[3] = 0xC01F; // 1100000000011111
199 DriverState.GraphicsCursor.ScreenMask[4] = 0xC00F; // 1100000000001111
200 DriverState.GraphicsCursor.ScreenMask[5] = 0xC007; // 1100000000000111
201 DriverState.GraphicsCursor.ScreenMask[6] = 0xC003; // 1100000000000011
202 DriverState.GraphicsCursor.ScreenMask[7] = 0xC007; // 1100000000000111
203 DriverState.GraphicsCursor.ScreenMask[8] = 0xC01F; // 1100000000011111
204 DriverState.GraphicsCursor.ScreenMask[9] = 0xC01F; // 1100000000011111
205 DriverState.GraphicsCursor.ScreenMask[10] = 0xC00F; // 1100000000001111
206 DriverState.GraphicsCursor.ScreenMask[11] = 0xC60F; // 1100011000001111
207 DriverState.GraphicsCursor.ScreenMask[12] = 0xFF07; // 1111111100000111
208 DriverState.GraphicsCursor.ScreenMask[13] = 0xFF07; // 1111111100000111
209 DriverState.GraphicsCursor.ScreenMask[14] = 0xFF87; // 1111111110000111
210 DriverState.GraphicsCursor.ScreenMask[15] = 0xFFCF; // 1111111111001111
211
212 DriverState.GraphicsCursor.CursorMask[0] = 0x0000; // 0000000000000000
213 DriverState.GraphicsCursor.CursorMask[1] = 0x1C00; // 0001110000000000
214 DriverState.GraphicsCursor.CursorMask[2] = 0x1F00; // 0001111100000000
215 DriverState.GraphicsCursor.CursorMask[3] = 0x1F80; // 0001111110000000
216 DriverState.GraphicsCursor.CursorMask[4] = 0x1FE0; // 0001111111100000
217 DriverState.GraphicsCursor.CursorMask[5] = 0x1FF0; // 0001111111110000
218 DriverState.GraphicsCursor.CursorMask[6] = 0x1FF8; // 0001111111111000
219 DriverState.GraphicsCursor.CursorMask[7] = 0x1FE0; // 0001111111100000
220 DriverState.GraphicsCursor.CursorMask[8] = 0x1FC0; // 0001111111000000
221 DriverState.GraphicsCursor.CursorMask[9] = 0x1FC0; // 0001111111000000
222 DriverState.GraphicsCursor.CursorMask[10] = 0x19E0; // 0001100111100000
223 DriverState.GraphicsCursor.CursorMask[11] = 0x00E0; // 0000000011100000
224 DriverState.GraphicsCursor.CursorMask[12] = 0x0070; // 0000000001110000
225 DriverState.GraphicsCursor.CursorMask[13] = 0x0070; // 0000000001110000
226 DriverState.GraphicsCursor.CursorMask[14] = 0x0030; // 0000000000110000
227 DriverState.GraphicsCursor.CursorMask[15] = 0x0000; // 0000000000000000
228
229 /* Initialize the counters */
230 DriverState.HorizCount = DriverState.VertCount = 0;
231
232 for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
233 {
234 DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0;
235 }
236
237 /* Initialize the resolution */
238 DriverState.MickeysPerCellHoriz = 8;
239 DriverState.MickeysPerCellVert = 16;
240
241 /* Return mouse information */
242 setAX(0xFFFF); // Hardware & driver installed
243 setBX(NUM_MOUSE_BUTTONS);
244
245 break;
246 }
247
248 /* Show Mouse Cursor */
249 case 0x01:
250 {
251 DriverState.ShowCount++;
252 if (DriverState.ShowCount > 0) PaintMouseCursor();
253
254 break;
255 }
256
257 /* Hide Mouse Cursor */
258 case 0x02:
259 {
260 DriverState.ShowCount--;
261 if (DriverState.ShowCount <= 0) EraseMouseCursor();
262
263 break;
264 }
265
266 /* Return Position And Button Status */
267 case 0x03:
268 {
269 setBX(DriverState.ButtonState);
270 setCX(DriverState.Position.X);
271 setDX(DriverState.Position.Y);
272 break;
273 }
274
275 /* Position Mouse Cursor */
276 case 0x04:
277 {
278 POINT Point;
279
280 Point.x = getCX();
281 Point.y = getDX();
282
283 ClientToScreen(GetConsoleWindow(), &Point);
284 SetCursorPos(Point.x, Point.y);
285
286 break;
287 }
288
289 /* Return Button Press Data */
290 case 0x05:
291 {
292 WORD Button = getBX();
293
294 setAX(DriverState.ButtonState);
295 setBX(DriverState.PressCount[Button]);
296 setCX(DriverState.LastPress[Button].X);
297 setDX(DriverState.LastPress[Button].Y);
298
299 /* Reset the counter */
300 DriverState.PressCount[Button] = 0;
301
302 break;
303 }
304
305 /* Return Button Release Data */
306 case 0x06:
307 {
308 WORD Button = getBX();
309
310 setAX(DriverState.ButtonState);
311 setBX(DriverState.ReleaseCount[Button]);
312 setCX(DriverState.LastRelease[Button].X);
313 setDX(DriverState.LastRelease[Button].Y);
314
315 /* Reset the counter */
316 DriverState.ReleaseCount[Button] = 0;
317
318 break;
319
320 }
321
322 /* Define Graphics Cursor */
323 case 0x09:
324 {
325 PWORD MaskBitmap = (PWORD)SEG_OFF_TO_PTR(getES(), getDX());
326
327 DriverState.GraphicsCursor.HotSpot.X = getBX();
328 DriverState.GraphicsCursor.HotSpot.Y = getCX();
329
330 RtlMoveMemory(DriverState.GraphicsCursor.ScreenMask,
331 MaskBitmap,
332 sizeof(DriverState.GraphicsCursor.ScreenMask));
333
334 RtlMoveMemory(DriverState.GraphicsCursor.CursorMask,
335 &MaskBitmap[16],
336 sizeof(DriverState.GraphicsCursor.CursorMask));
337
338 break;
339 }
340
341 /* Define Text Cursor */
342 case 0x0A:
343 {
344 USHORT BX = getBX();
345
346 if (BX == 0x0000)
347 {
348 /* Define software cursor */
349 DriverState.TextCursor.ScreenMask = getCX();
350 DriverState.TextCursor.CursorMask = getDX();
351 }
352 else if (BX == 0x0001)
353 {
354 /* Define hardware cursor */
355 DPRINT1("Defining hardware cursor is unimplemented\n");
356 UNIMPLEMENTED;
357 // CX == start scan line
358 // DX == end scan line
359 }
360 else
361 {
362 DPRINT1("Invalid BX value 0x%04X\n", BX);
363 }
364
365 break;
366 }
367
368 /* Read Motion Counters */
369 case 0x0B:
370 {
371 setCX(DriverState.HorizCount);
372 setDX(DriverState.VertCount);
373
374 /* Reset the counters */
375 DriverState.HorizCount = DriverState.VertCount = 0;
376
377 break;
378 }
379
380 /* Define Interrupt Subroutine Parameters, compatible MS MOUSE v1.0+ */
381 case 0x0C:
382 {
383 DriverState.Handler0.CallMask = getCX();
384 DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
385 DPRINT1("Define callback 0x%04X, %04X:%04X\n",
386 DriverState.Handler0.CallMask,
387 HIWORD(DriverState.Handler0.Callback),
388 LOWORD(DriverState.Handler0.Callback));
389 break;
390 }
391
392 /* Define Mickey/Pixel Ratio */
393 case 0x0F:
394 {
395 DriverState.MickeysPerCellHoriz = getCX();
396 DriverState.MickeysPerCellVert = getDX();
397 break;
398 }
399
400 /* Exchange Interrupt Subroutines, compatible MS MOUSE v3.0+ (see function 0x0C) */
401 case 0x14:
402 {
403 USHORT OldCallMask = DriverState.Handler0.CallMask;
404 ULONG OldCallback = DriverState.Handler0.Callback;
405
406 DriverState.Handler0.CallMask = getCX();
407 DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
408
409 /* Return old callmask in CX and callback vector in ES:DX */
410 setCX(OldCallMask);
411 setES(HIWORD(OldCallback));
412 setDX(LOWORD(OldCallback));
413
414 break;
415 }
416
417 /* Return Driver Storage Requirements */
418 case 0x15:
419 {
420 setBX(sizeof(MOUSE_DRIVER_STATE));
421 break;
422 }
423
424 /* Save Driver State */
425 case 0x16:
426 {
427 *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX())) = DriverState;
428 break;
429 }
430
431 /* Restore Driver State */
432 case 0x17:
433 {
434 DriverState = *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX()));
435 break;
436 }
437
438 /* Set Alternate Mouse User Handler, compatible MS MOUSE v6.0+ */
439 case 0x18:
440 {
441 /*
442 * Up to three handlers can be defined by separate calls to this
443 * function, each with a different combination of shift states in
444 * the call mask; calling this function again with a call mask of
445 * 0000h undefines the specified handler (official documentation);
446 * specifying the same call mask and an address of 0000h:0000h
447 * undefines the handler (real life).
448 * See Ralf Brown: http://www.ctyme.com/intr/rb-5981.htm
449 * for more information.
450 */
451
452 USHORT i;
453 USHORT CallMask = getCX();
454 ULONG Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback
455 BOOLEAN Success = FALSE;
456
457 DPRINT1("Define v6.0+ callback 0x%04X, %04X:%04X\n",
458 CallMask, HIWORD(Callback), LOWORD(Callback));
459
460 if (CallMask == 0x0000)
461 {
462 /*
463 * Find the handler entry corresponding to the given
464 * callback and undefine it.
465 */
466 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
467 {
468 if (DriverState.Handlers[i].Callback == Callback)
469 {
470 /* Found it, undefine the handler */
471 DriverState.Handlers[i].CallMask = 0x0000;
472 DriverState.Handlers[i].Callback = (ULONG)NULL;
473 Success = TRUE;
474 break;
475 }
476 }
477 }
478 else if (Callback == (ULONG)NULL)
479 {
480 /*
481 * Find the handler entry corresponding to the given
482 * callmask and undefine it.
483 */
484 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
485 {
486 if (DriverState.Handlers[i].CallMask == CallMask)
487 {
488 /* Found it, undefine the handler */
489 DriverState.Handlers[i].CallMask = 0x0000;
490 DriverState.Handlers[i].Callback = (ULONG)NULL;
491 Success = TRUE;
492 break;
493 }
494 }
495 }
496 else
497 {
498 /*
499 * Try to find a handler entry corresponding to the given
500 * callmask to redefine it, otherwise find an empty handler
501 * entry and set the new handler in there.
502 */
503
504 USHORT EmptyHandler = 0xFFFF; // Invalid handler
505
506 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
507 {
508 /* Find the first empty handler */
509 if (EmptyHandler == 0xFFFF &&
510 DriverState.Handlers[i].CallMask == 0x0000 &&
511 DriverState.Handlers[i].Callback == (ULONG)NULL)
512 {
513 EmptyHandler = i;
514 }
515
516 if (DriverState.Handlers[i].CallMask == CallMask)
517 {
518 /* Found it, redefine the handler */
519 DriverState.Handlers[i].CallMask = CallMask;
520 DriverState.Handlers[i].Callback = Callback;
521 Success = TRUE;
522 break;
523 }
524 }
525
526 /*
527 * If we haven't found anything and we found
528 * an empty handler, set it.
529 */
530 if (!Success && EmptyHandler != 0xFFFF
531 /* && EmptyHandler < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]) */)
532 {
533 DriverState.Handlers[EmptyHandler].CallMask = CallMask;
534 DriverState.Handlers[EmptyHandler].Callback = Callback;
535 Success = TRUE;
536 }
537 }
538
539 /* If we failed, set error code */
540 if (!Success) setAX(0xFFFF);
541
542 break;
543 }
544
545 /* Return User Alternate Interrupt Vector, compatible MS MOUSE v6.0+ */
546 case 0x19:
547 {
548 USHORT i;
549 USHORT CallMask = getCX();
550 ULONG Callback;
551 BOOLEAN Success = FALSE;
552
553 /*
554 * Find the handler entry corresponding to the given callmask.
555 */
556 for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
557 {
558 if (DriverState.Handlers[i].CallMask == CallMask)
559 {
560 /* Found it */
561 Callback = DriverState.Handlers[i].Callback;
562 Success = TRUE;
563 break;
564 }
565 }
566
567 if (Success)
568 {
569 /* Return the callback vector in BX:DX */
570 setBX(HIWORD(Callback));
571 setDX(LOWORD(Callback));
572 }
573 else
574 {
575 /* We failed, set error code */
576 setCX(0x0000);
577 }
578
579 break;
580 }
581
582 /* Disable Mouse Driver */
583 case 0x1F:
584 {
585 setES(0x0000);
586 setBX(0x0000);
587
588 DriverEnabled = FALSE;
589 break;
590 }
591
592 /* Enable Mouse Driver */
593 case 0x20:
594 {
595 DriverEnabled = TRUE;
596 break;
597 }
598
599 default:
600 {
601 DPRINT1("BIOS Function INT 33h, AX = 0x%04X NOT IMPLEMENTED\n", getAX());
602 }
603 }
604 }
605
606 /* PUBLIC FUNCTIONS ***********************************************************/
607
608 VOID MouseBiosUpdatePosition(PCOORD NewPosition)
609 {
610 SHORT DeltaX = NewPosition->X - DriverState.Position.X;
611 SHORT DeltaY = NewPosition->Y - DriverState.Position.Y;
612
613 if (!DriverEnabled) return;
614
615 DriverState.HorizCount += (DeltaX * (SHORT)DriverState.MickeysPerCellHoriz) / 8;
616 DriverState.VertCount += (DeltaY * (SHORT)DriverState.MickeysPerCellVert ) / 8;
617
618 if (DriverState.ShowCount > 0)
619 {
620 EraseMouseCursor();
621 DriverState.Position = *NewPosition;
622 PaintMouseCursor();
623 }
624
625 /* Call the mouse handlers */
626 CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
627 }
628
629 VOID MouseBiosUpdateButtons(WORD ButtonState)
630 {
631 USHORT i;
632 USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format
633
634 if (!DriverEnabled) return;
635
636 for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
637 {
638 BOOLEAN OldState = (DriverState.ButtonState >> i) & 1;
639 BOOLEAN NewState = (ButtonState >> i) & 1;
640
641 if (NewState > OldState)
642 {
643 /* Mouse press */
644 DriverState.PressCount[i]++;
645 DriverState.LastPress[i] = DriverState.Position;
646
647 CallMask |= (1 << (2 * i + 1));
648 }
649 else if (NewState < OldState)
650 {
651 /* Mouse release */
652 DriverState.ReleaseCount[i]++;
653 DriverState.LastRelease[i] = DriverState.Position;
654
655 CallMask |= (1 << (2 * i + 2));
656 }
657 }
658
659 DriverState.ButtonState = ButtonState;
660
661 /* Call the mouse handlers */
662 CallMouseUserHandlers(CallMask);
663 }
664
665 BOOLEAN DosMouseInitialize(VOID)
666 {
667 /* Clear the state */
668 ZeroMemory(&DriverState, sizeof(DriverState));
669
670 /* Initialize the interrupt handler */
671 RegisterDosInt32(BIOS_MOUSE_INTERRUPT, BiosMouseService);
672
673 return TRUE;
674 }
675
676 VOID DosMouseCleanup(VOID)
677 {
678 if (DriverState.ShowCount > 0) EraseMouseCursor();
679 }