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