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