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 */ 1315