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