/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: subsystems/mvdm/ntvdm/dos/mouse32.c * PURPOSE: VDM 32-bit compatible PS/2 MOUSE.COM driver * PROGRAMMERS: Aleksandar Andrejevic */ /* INCLUDES *******************************************************************/ #include "ntvdm.h" #define NDEBUG #include /* Driver Version number and Copyright */ #include #include #include "emulator.h" #include "cpu/cpu.h" #include "int32.h" #include "hardware/mouse.h" #include "hardware/ps2.h" #include "hardware/pic.h" #include "hardware/video/svga.h" /**/ #include "../console/video.h" /**/ #include "mouse32.h" #include "bios/bios.h" #include "bios/bios32/bios32p.h" #include "memory.h" #include "io.h" #include "dos32krnl/memory.h" /* PRIVATE VARIABLES **********************************************************/ static const CHAR MouseCopyright[] = "ReactOS PS/2 16/32-bit Mouse Driver Compatible MS-MOUSE 6.26\r\n" "Version "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\r\n" "Copyright (C) ReactOS Team 1996-"COPYRIGHT_YEAR"\0"; #pragma pack(push, 1) typedef struct _MOUSE_DRIVER { CHAR Copyright[sizeof(MouseCopyright)]; WORD Version; BYTE MouseContextScratch[TRAMPOLINE_SIZE]; BYTE MouseDosInt16Stub[Int16To32StubSize]; BYTE MouseIrqInt16Stub[Int16To32StubSize]; } MOUSE_DRIVER, *PMOUSE_DRIVER; #pragma pack(pop) /* Global data contained in guest memory */ static WORD MouseDataSegment; static PMOUSE_DRIVER MouseData; static CALLBACK16 MouseContext; #define MICKEYS_PER_CELL_HORIZ 8 #define MICKEYS_PER_CELL_VERT 16 static BOOLEAN DriverEnabled = FALSE; static MOUSE_DRIVER_STATE DriverState; static DWORD OldIrqHandler; static DWORD OldIntHandler; static WORD DefaultGfxScreenMask[16] = { 0xE7FF, // 1110011111111111 0xE3FF, // 1110001111111111 0xE1FF, // 1110000111111111 0xE0FF, // 1110000011111111 0xE07F, // 1110000001111111 0xE03F, // 1110000000111111 0xE01F, // 1110000000011111 0xE00F, // 1110000000001111 0xE007, // 1110000000000111 0xE007, // 1110000000000111 0xE03F, // 1110000000111111 0xE21F, // 1110001000011111 0xE61F, // 1110011000011111 0xFF0F, // 1111111100001111 0xFF0F, // 1111111100001111 0xFF8F, // 1111111110001111 }; static WORD DefaultGfxCursorMask[16] = { 0x0000, // 0000000000000000 0x0800, // 0000100000000000 0x0C00, // 0000110000000000 0x0E00, // 0000111000000000 0x0F00, // 0000111100000000 0x0F80, // 0000111110000000 0x0FC0, // 0000111111000000 0x0FE0, // 0000111111100000 0x0FF0, // 0000111111110000 0x0F80, // 0000111110000000 0x0D80, // 0000110110000000 0x08C0, // 0000100011000000 0x00C0, // 0000000011000000 0x0060, // 0000000001100000 0x0060, // 0000000001100000 0x0000, // 0000000000000000 }; /* PRIVATE FUNCTIONS **********************************************************/ /* static */ VOID BiosPs2Service(UCHAR Function) { /* Save AX and BX */ USHORT AX = getAX(); // USHORT BX = getBX(); /* * Set the parameters: * AL contains the character to print (already set), * BL contains the character attribute, * BH contains the video page to use. */ // setBL(DOS_CHAR_ATTRIBUTE); // setBH(Bda->VideoPage); setAL(Function); /* Call the BIOS INT 15h, AH=C2h "Pointing Device BIOS Interface (PS)" */ setAH(0xC2); Int32Call(&MouseContext, BIOS_MISC_INTERRUPT); /* Restore AX and BX */ // setBX(BX); setAX(AX); } static VOID DosMouseEnable(VOID); static VOID DosMouseDisable(VOID); static VOID PaintMouseCursor(VOID) { if (Bda->VideoMode <= 3) { WORD Character; WORD CellX = DriverState.Position.X / 8; WORD CellY = DriverState.Position.Y / 8; DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize); EmulatorReadMemory(&EmulatorContext, VideoAddress + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD), (LPVOID)&Character, sizeof(WORD)); DriverState.Character = Character; Character &= DriverState.TextCursor.ScreenMask; Character ^= DriverState.TextCursor.CursorMask; EmulatorWriteMemory(&EmulatorContext, VideoAddress + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD), (LPVOID)&Character, sizeof(WORD)); } else if (Bda->VideoMode == 0x12) { INT i, j; BYTE OldMask; BYTE OldMap; /* Save the write mask */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); OldMask = IOReadB(VGA_SEQ_DATA); /* And the selected reading plane */ IOWriteB(VGA_GC_INDEX, VGA_GC_READ_MAP_SEL_REG); OldMap = IOReadB(VGA_GC_DATA); for (i = 0; i < 16; i++) { WORD CursorLine[4]; DWORD VideoAddress = TO_LINEAR(GRAPHICS_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize) + ((DriverState.Position.Y + i) * 640 + DriverState.Position.X) / 8; for (j = 0; j < 4; j++) { /* Select the reading plane */ IOWriteB(VGA_GC_INDEX, VGA_GC_READ_MAP_SEL_REG); IOWriteB(VGA_GC_DATA, j); /* Read a part of the scanline */ EmulatorReadMemory(&EmulatorContext, VideoAddress, &CursorLine[j], sizeof(CursorLine[j])); } /* Save the data below the cursor */ for (j = 0; j < 16; j++) { DriverState.GraphicsData[i * 16 + j] = 0; if (CursorLine[0] & (1 << j)) DriverState.GraphicsData[i * 16 + j] |= 1 << 0; if (CursorLine[1] & (1 << j)) DriverState.GraphicsData[i * 16 + j] |= 1 << 1; if (CursorLine[2] & (1 << j)) DriverState.GraphicsData[i * 16 + j] |= 1 << 2; if (CursorLine[3] & (1 << j)) DriverState.GraphicsData[i * 16 + j] |= 1 << 3; } for (j = 0; j < 4; j++) { /* Apply the screen mask */ CursorLine[j] &= MAKEWORD(HIBYTE(DriverState.GraphicsCursor.ScreenMask[i]), LOBYTE(DriverState.GraphicsCursor.ScreenMask[i])); /* And the cursor mask */ CursorLine[j] ^= MAKEWORD(HIBYTE(DriverState.GraphicsCursor.CursorMask[i]), LOBYTE(DriverState.GraphicsCursor.CursorMask[i])); /* Select the writing plane */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); IOWriteB(VGA_SEQ_DATA, 1 << j); /* Write the cursor data for this scanline */ EmulatorWriteMemory(&EmulatorContext, VideoAddress, &CursorLine[j], sizeof(CursorLine[j])); } } /* Restore the old mask */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); IOWriteB(VGA_SEQ_DATA, OldMask); /* And the old reading plane */ IOWriteB(VGA_GC_INDEX, VGA_GC_READ_MAP_SEL_REG); IOWriteB(VGA_GC_DATA, OldMap); } else if (Bda->VideoMode == 0x13) { INT i, j; for (i = 0; i < 16; i++) { BYTE CursorLine[16]; DWORD VideoAddress = TO_LINEAR(GRAPHICS_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize) + (DriverState.Position.Y + i) * 320 + DriverState.Position.X; /* Read a part of the scanline */ EmulatorReadMemory(&EmulatorContext, VideoAddress, &DriverState.GraphicsData[i * 16], sizeof(CursorLine)); for (j = 0; j < 16; j++) { /* Apply the screen mask by leaving only the masked pixels intact */ CursorLine[j] = (DriverState.GraphicsCursor.ScreenMask[i] & (1 << j)) ? DriverState.GraphicsData[i * 16] : 0x00; /* Apply the cursor mask... */ if (DriverState.GraphicsCursor.CursorMask[i] & (1 << j)) { /* ... by inverting the color of each masked pixel */ CursorLine[j] ^= 0x0F; } } /* Write the cursor data for this scanline */ EmulatorWriteMemory(&EmulatorContext, VideoAddress, &CursorLine, sizeof(CursorLine)); } } else { // TODO: NOT IMPLEMENTED UNIMPLEMENTED; } } static VOID EraseMouseCursor(VOID) { if (Bda->VideoMode <= 3) { WORD CellX = DriverState.Position.X / 8; WORD CellY = DriverState.Position.Y / 8; DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize); EmulatorWriteMemory(&EmulatorContext, VideoAddress + (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD), (LPVOID)&DriverState.Character, sizeof(WORD)); } else if (Bda->VideoMode == 0x12) { INT i, j; BYTE OldMask; /* Save the write mask */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); OldMask = IOReadB(VGA_SEQ_DATA); for (i = 0; i < 16; i++) { WORD CursorLine[4] = {0}; DWORD VideoAddress = TO_LINEAR(GRAPHICS_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize) + ((DriverState.Position.Y + i) * 640 + DriverState.Position.X) / 8; /* Restore the data that was below the cursor */ for (j = 0; j < 16; j++) { if (DriverState.GraphicsData[i * 16 + j] & (1 << 0)) CursorLine[0] |= 1 << j; if (DriverState.GraphicsData[i * 16 + j] & (1 << 1)) CursorLine[1] |= 1 << j; if (DriverState.GraphicsData[i * 16 + j] & (1 << 2)) CursorLine[2] |= 1 << j; if (DriverState.GraphicsData[i * 16 + j] & (1 << 3)) CursorLine[3] |= 1 << j; } for (j = 0; j < 4; j++) { /* Select the writing plane */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); IOWriteB(VGA_SEQ_DATA, 1 << j); /* Write the original data for this scanline */ EmulatorWriteMemory(&EmulatorContext, VideoAddress, &CursorLine[j], sizeof(CursorLine[j])); } } /* Restore the old mask */ IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG); IOWriteB(VGA_SEQ_DATA, OldMask); } else if (Bda->VideoMode == 0x13) { INT i; for (i = 0; i < 16; i++) { DWORD VideoAddress = TO_LINEAR(GRAPHICS_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize) + (DriverState.Position.Y + i) * 320 + DriverState.Position.X; /* Write the original data for this scanline */ EmulatorWriteMemory(&EmulatorContext, VideoAddress, &DriverState.GraphicsData[i * 16], 16 * sizeof(BYTE)); } } else { // TODO: NOT IMPLEMENTED UNIMPLEMENTED; } } static VOID ToMouseCoordinates(PCOORD Position) { COORD Resolution = VgaGetDisplayResolution(); DWORD Width = DriverState.MaxX - DriverState.MinX + 1; DWORD Height = DriverState.MaxY - DriverState.MinY + 1; if (!VgaGetDoubleVisionState(NULL, NULL)) { Resolution.X *= 8; Resolution.Y *= 8; } Position->X = DriverState.MinX + ((Position->X * Width) / Resolution.X); Position->Y = DriverState.MinY + ((Position->Y * Height) / Resolution.Y); } static VOID FromMouseCoordinates(PCOORD Position) { COORD Resolution = VgaGetDisplayResolution(); DWORD Width = DriverState.MaxX - DriverState.MinX + 1; DWORD Height = DriverState.MaxY - DriverState.MinY + 1; if (!VgaGetDoubleVisionState(NULL, NULL)) { Resolution.X *= 8; Resolution.Y *= 8; } Position->X = ((Position->X - DriverState.MinX) * Resolution.X) / Width; Position->Y = ((Position->Y - DriverState.MinY) * Resolution.Y) / Height; } static VOID CallMouseUserHandlers(USHORT CallMask) { USHORT i; USHORT AX, BX, CX, DX, BP, SI, DI, DS, ES; COORD Position = DriverState.Position; ToMouseCoordinates(&Position); /* Call handler 0 */ if ((DriverState.Handler0.CallMask & CallMask) != 0 && DriverState.Handler0.Callback != NULL32) { /* * Set the parameters for the callback. * NOTE: In text modes, the row and column will be reported * as a multiple of the cell size, typically 8x8 pixels. */ AX = getAX(); BX = getBX(); CX = getCX(); DX = getDX(); BP = getBP(); SI = getSI(); DI = getDI(); DS = getDS(); ES = getES(); setAX(CallMask); setBX(DriverState.ButtonState); setCX(Position.X); setDX(Position.Y); setSI(MICKEYS_PER_CELL_HORIZ); setDI(MICKEYS_PER_CELL_VERT); DPRINT("Calling Handler0 %04X:%04X with CallMask 0x%04X\n", HIWORD(DriverState.Handler0.Callback), LOWORD(DriverState.Handler0.Callback), CallMask); /* Call the callback */ RunCallback16(&MouseContext, DriverState.Handler0.Callback); setAX(AX); setBX(BX); setCX(CX); setDX(DX); setBP(BP); setSI(SI); setDI(DI); setDS(DS); setES(ES); } for (i = 0; i < ARRAYSIZE(DriverState.Handlers); ++i) { /* Call the suitable handlers */ if ((DriverState.Handlers[i].CallMask & CallMask) != 0 && DriverState.Handlers[i].Callback != NULL32) { /* * Set the parameters for the callback. * NOTE: In text modes, the row and column will be reported * as a multiple of the cell size, typically 8x8 pixels. */ AX = getAX(); BX = getBX(); CX = getCX(); DX = getDX(); BP = getBP(); SI = getSI(); DI = getDI(); DS = getDS(); ES = getES(); setAX(CallMask); setBX(DriverState.ButtonState); setCX(Position.X); setDX(Position.Y); setSI(MICKEYS_PER_CELL_HORIZ); setDI(MICKEYS_PER_CELL_VERT); DPRINT1("Calling Handler[%d] %04X:%04X with CallMask 0x%04X\n", i, HIWORD(DriverState.Handlers[i].Callback), LOWORD(DriverState.Handlers[i].Callback), CallMask); /* Call the callback */ RunCallback16(&MouseContext, DriverState.Handlers[i].Callback); setAX(AX); setBX(BX); setCX(CX); setDX(DX); setBP(BP); setSI(SI); setDI(DI); setDS(DS); setES(ES); } } } static inline VOID DosUpdatePosition(PCOORD NewPosition) { COORD Resolution = VgaGetDisplayResolution(); /* Check for text mode */ if (!VgaGetDoubleVisionState(NULL, NULL)) { Resolution.X *= 8; Resolution.Y *= 8; } if (DriverState.ShowCount > 0) EraseMouseCursor(); DriverState.Position = *NewPosition; if (DriverState.ShowCount > 0) PaintMouseCursor(); /* Call the mouse handlers */ CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format } static inline VOID DosUpdateButtons(BYTE ButtonState) // WORD ButtonState { USHORT i; USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format for (i = 0; i < NUM_MOUSE_BUTTONS; i++) { BOOLEAN OldState = (DriverState.ButtonState >> i) & 1; BOOLEAN NewState = (ButtonState >> i) & 1; if (NewState > OldState) { /* Mouse press */ DriverState.PressCount[i]++; DriverState.LastPress[i] = DriverState.Position; CallMask |= (1 << (2 * i + 1)); } else if (NewState < OldState) { /* Mouse release */ DriverState.ReleaseCount[i]++; DriverState.LastRelease[i] = DriverState.Position; CallMask |= (1 << (2 * i + 2)); } } DriverState.ButtonState = ButtonState; /* Call the mouse handlers */ CallMouseUserHandlers(CallMask); } static VOID WINAPI DosMouseIrq(LPWORD Stack) { BYTE Flags; SHORT DeltaX, DeltaY; COORD Position; BYTE ButtonState; /* Read the whole packet at once */ Flags = IOReadB(PS2_DATA_PORT); PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231 DeltaX = IOReadB(PS2_DATA_PORT); PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231 DeltaY = IOReadB(PS2_DATA_PORT); /* Adjust the sign */ if (Flags & MOUSE_X_SIGN) DeltaX = -DeltaX; if (Flags & MOUSE_Y_SIGN) DeltaY = -DeltaY; /* Update the counters */ DriverState.HorizCount += DeltaX; DriverState.VertCount += DeltaY; /* * Get the absolute position directly from the mouse, this is the only * way to perfectly synchronize the host and guest mouse pointer. */ MouseGetDataFast(&Position, &ButtonState); /* Call the update subroutines */ DosUpdatePosition(&Position); DosUpdateButtons(ButtonState); /* Complete the IRQ */ PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM])); } static VOID WINAPI DosMouseService(LPWORD Stack) { switch (getAX()) { /* Reset Driver */ case 0x00: { SHORT i; DriverState.ShowCount = 0; DriverState.ButtonState = 0; /* Initialize the default clipping range */ DriverState.MinX = 0; DriverState.MaxX = MOUSE_MAX_HORIZ - 1; DriverState.MinY = 0; DriverState.MaxY = MOUSE_MAX_VERT - 1; /* Set the default text cursor */ DriverState.TextCursor.ScreenMask = 0xFFFF; /* Display everything */ DriverState.TextCursor.CursorMask = 0xFF00; /* ... but with inverted attributes */ /* Set the default graphics cursor */ DriverState.GraphicsCursor.HotSpot.X = 3; DriverState.GraphicsCursor.HotSpot.Y = 1; RtlCopyMemory(DriverState.GraphicsCursor.ScreenMask, DefaultGfxScreenMask, sizeof(DriverState.GraphicsCursor.ScreenMask)); RtlCopyMemory(DriverState.GraphicsCursor.CursorMask, DefaultGfxCursorMask, sizeof(DriverState.GraphicsCursor.CursorMask)); /* Initialize the counters */ DriverState.HorizCount = DriverState.VertCount = 0; for (i = 0; i < NUM_MOUSE_BUTTONS; i++) { DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0; } /* Return mouse information */ setAX(0xFFFF); // Hardware & driver installed setBX(NUM_MOUSE_BUTTONS); break; } /* Show Mouse Cursor */ case 0x01: { DriverState.ShowCount++; if (DriverState.ShowCount == 1) PaintMouseCursor(); break; } /* Hide Mouse Cursor */ case 0x02: { DriverState.ShowCount--; if (DriverState.ShowCount == 0) EraseMouseCursor(); break; } /* Return Position and Button Status */ case 0x03: { COORD Position = DriverState.Position; ToMouseCoordinates(&Position); setBX(DriverState.ButtonState); setCX(Position.X); setDX(Position.Y); break; } /* Position Mouse Cursor */ case 0x04: { COORD Position = { getCX(), getDX() }; FromMouseCoordinates(&Position); DriverState.Position = Position; break; } /* Return Button Press Data */ case 0x05: { WORD Button = getBX(); COORD LastPress = DriverState.LastPress[Button]; ToMouseCoordinates(&LastPress); setAX(DriverState.ButtonState); setBX(DriverState.PressCount[Button]); setCX(LastPress.X); setDX(LastPress.Y); /* Reset the counter */ DriverState.PressCount[Button] = 0; break; } /* Return Button Release Data */ case 0x06: { WORD Button = getBX(); COORD LastRelease = DriverState.LastRelease[Button]; ToMouseCoordinates(&LastRelease); setAX(DriverState.ButtonState); setBX(DriverState.ReleaseCount[Button]); setCX(LastRelease.X); setDX(LastRelease.Y); /* Reset the counter */ DriverState.ReleaseCount[Button] = 0; break; } /* Define Horizontal Cursor Range */ case 0x07: { WORD Min = getCX(); WORD Max = getDX(); if (!VgaGetDoubleVisionState(NULL, NULL)) { /* Text mode */ Min &= ~0x07; Max |= 0x07; } DPRINT("Setting mouse horizontal range: %u - %u\n", Min, Max); DriverState.MinX = Min; DriverState.MaxX = Max; break; } /* Define Vertical Cursor Range */ case 0x08: { WORD Min = getCX(); WORD Max = getDX(); if (!VgaGetDoubleVisionState(NULL, NULL)) { /* Text mode */ Min &= ~0x07; Max |= 0x07; } DPRINT("Setting mouse vertical range: %u - %u\n", Min, Max); DriverState.MinY = Min; DriverState.MaxY = Max; break; } /* Define Graphics Cursor */ case 0x09: { PWORD MaskBitmap = (PWORD)SEG_OFF_TO_PTR(getES(), getDX()); DriverState.GraphicsCursor.HotSpot.X = getBX(); DriverState.GraphicsCursor.HotSpot.Y = getCX(); RtlCopyMemory(DriverState.GraphicsCursor.ScreenMask, MaskBitmap, sizeof(DriverState.GraphicsCursor.ScreenMask)); RtlCopyMemory(DriverState.GraphicsCursor.CursorMask, &MaskBitmap[16], sizeof(DriverState.GraphicsCursor.CursorMask)); break; } /* Define Text Cursor */ case 0x0A: { USHORT BX = getBX(); if (BX == 0x0000) { /* Define software cursor */ DriverState.TextCursor.ScreenMask = getCX(); DriverState.TextCursor.CursorMask = getDX(); } else if (BX == 0x0001) { /* Define hardware cursor */ DPRINT1("Defining hardware cursor is unimplemented\n"); UNIMPLEMENTED; // CX == start scan line // DX == end scan line } else { DPRINT1("Invalid BX value 0x%04X\n", BX); } break; } /* Read Motion Counters */ case 0x0B: { setCX(DriverState.HorizCount); setDX(DriverState.VertCount); /* Reset the counters */ DriverState.HorizCount = DriverState.VertCount = 0; break; } /* Define Interrupt Subroutine Parameters, compatible MS MOUSE v1.0+ */ case 0x0C: { DriverState.Handler0.CallMask = getCX(); DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback DPRINT1("Define callback 0x%04X, %04X:%04X\n", DriverState.Handler0.CallMask, HIWORD(DriverState.Handler0.Callback), LOWORD(DriverState.Handler0.Callback)); break; } /* Define Mickey/Pixel Ratio */ case 0x0F: { /* This call should be completely ignored */ break; } /* Set Exclusion Area */ // http://www.ctyme.com/intr/rb-5972.htm // http://www.techhelpmanual.com/849-int_33h_0010h__set_exclusion_area.html //case 0x10: //{ //} /* Define Double-Speed Threshold */ case 0x13: { DPRINT1("INT 33h, AH=13h: Mouse double-speed threshold is UNSUPPORTED\n"); break; } /* Exchange Interrupt Subroutines, compatible MS MOUSE v3.0+ (see function 0x0C) */ case 0x14: { USHORT OldCallMask = DriverState.Handler0.CallMask; ULONG OldCallback = DriverState.Handler0.Callback; DriverState.Handler0.CallMask = getCX(); DriverState.Handler0.Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback DPRINT1("Exchange old callback 0x%04X, %04X:%04X with new callback 0x%04X, %04X:%04X\n", OldCallMask, HIWORD(OldCallback), LOWORD(OldCallback), DriverState.Handler0.CallMask, HIWORD(DriverState.Handler0.Callback), LOWORD(DriverState.Handler0.Callback)); /* Return old callmask in CX and callback vector in ES:DX */ setCX(OldCallMask); setES(HIWORD(OldCallback)); setDX(LOWORD(OldCallback)); break; } /* Return Driver Storage Requirements */ case 0x15: { setBX(sizeof(MOUSE_DRIVER_STATE)); break; } /* Save Driver State */ case 0x16: { /* Check whether the user buffer has correct size and fail if not */ if (getBX() != sizeof(MOUSE_DRIVER_STATE)) break; *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX())) = DriverState; break; } /* Restore Driver State */ case 0x17: { /* Check whether the user buffer has correct size and fail if not */ if (getBX() != sizeof(MOUSE_DRIVER_STATE)) break; DriverState = *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX())); break; } /* Set Alternate Mouse User Handler, compatible MS MOUSE v6.0+ */ case 0x18: { /* * Up to three handlers can be defined by separate calls to this * function, each with a different combination of shift states in * the call mask; calling this function again with a call mask of * 0000h undefines the specified handler (official documentation); * specifying the same call mask and an address of 0000h:0000h * undefines the handler (real life). * See Ralf Brown: http://www.ctyme.com/intr/rb-5981.htm * for more information. */ USHORT i; USHORT CallMask = getCX(); ULONG Callback = MAKELONG(getDX(), getES()); // Far pointer to the callback BOOLEAN Success = FALSE; DPRINT1("Define v6.0+ callback 0x%04X, %04X:%04X\n", CallMask, HIWORD(Callback), LOWORD(Callback)); if (CallMask == 0x0000) { /* * Find the handler entry corresponding to the given * callback and undefine it. */ for (i = 0; i < ARRAYSIZE(DriverState.Handlers); ++i) { if (DriverState.Handlers[i].Callback == Callback) { /* Found it, undefine the handler */ DriverState.Handlers[i].CallMask = 0x0000; DriverState.Handlers[i].Callback = NULL32; Success = TRUE; break; } } } else if (Callback == NULL32) { /* * Find the handler entry corresponding to the given * callmask and undefine it. */ for (i = 0; i < ARRAYSIZE(DriverState.Handlers); ++i) { if (DriverState.Handlers[i].CallMask == CallMask) { /* Found it, undefine the handler */ DriverState.Handlers[i].CallMask = 0x0000; DriverState.Handlers[i].Callback = NULL32; Success = TRUE; break; } } } else { /* * Try to find a handler entry corresponding to the given * callmask to redefine it, otherwise find an empty handler * entry and set the new handler in there. */ USHORT EmptyHandler = 0xFFFF; // Invalid handler for (i = 0; i < ARRAYSIZE(DriverState.Handlers); ++i) { /* Find the first empty handler */ if (EmptyHandler == 0xFFFF && DriverState.Handlers[i].CallMask == 0x0000 && DriverState.Handlers[i].Callback == NULL32) { EmptyHandler = i; } if (DriverState.Handlers[i].CallMask == CallMask) { /* Found it, redefine the handler */ DriverState.Handlers[i].CallMask = CallMask; DriverState.Handlers[i].Callback = Callback; Success = TRUE; break; } } /* * If we haven't found anything and we found * an empty handler, set it. */ if (!Success && EmptyHandler != 0xFFFF /* && EmptyHandler < ARRAYSIZE(DriverState.Handlers) */) { DriverState.Handlers[EmptyHandler].CallMask = CallMask; DriverState.Handlers[EmptyHandler].Callback = Callback; Success = TRUE; } } /* If we failed, set error code */ if (!Success) setAX(0xFFFF); break; } /* Return User Alternate Interrupt Vector, compatible MS MOUSE v6.0+ */ case 0x19: { USHORT i; USHORT CallMask = getCX(); ULONG Callback; BOOLEAN Success = FALSE; /* * Find the handler entry corresponding to the given callmask. */ for (i = 0; i < ARRAYSIZE(DriverState.Handlers); ++i) { if (DriverState.Handlers[i].CallMask == CallMask) { /* Found it */ Callback = DriverState.Handlers[i].Callback; Success = TRUE; break; } } if (Success) { /* Return the callback vector in BX:DX */ setBX(HIWORD(Callback)); setDX(LOWORD(Callback)); } else { /* We failed, set error code */ setCX(0x0000); } break; } /* Set Mouse Sensitivity */ case 0x1A: { DPRINT1("INT 33h, AH=1Ah: Mouse sensitivity is UNSUPPORTED\n"); // FIXME: Do that at runtime! // UCHAR BH = getBH(); // setBH(0x00); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); break; } /* Return Mouse Sensitivity */ case 0x1B: { DPRINT1("INT 33h, AH=1Bh: Mouse sensitivity is UNSUPPORTED\n"); /* Return default values */ setBX(50); // Horizontal speed setCX(50); // Vertical speed setDX(50); // Double speed threshold // FIXME: Get that at runtime! // UCHAR BH = getBH(); // setBH(0x00); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); break; } /* Disable Mouse Driver */ case 0x1F: { /* INT 33h vector before the mouse driver was first installed */ setES(HIWORD(OldIntHandler)); setBX(LOWORD(OldIntHandler)); DosMouseDisable(); // UCHAR BH = getBH(); // setBH(0x00); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); break; } /* Enable Mouse Driver */ case 0x20: { DosMouseEnable(); // UCHAR BH = getBH(); // setBH(0x01); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); break; } /* Software Reset */ case 0x21: { /* * See: http://www.htl-steyr.ac.at/~morg/pcinfo/hardware/interrupts/inte3sq8.htm * for detailed information and differences with respect to subfunction 0x00: * http://www.htl-steyr.ac.at/~morg/pcinfo/hardware/interrupts/inte3j74.htm */ SHORT i; DriverState.ShowCount = 0; DriverState.ButtonState = 0; /* Initialize the default clipping range */ DriverState.MinX = 0; DriverState.MaxX = MOUSE_MAX_HORIZ - 1; DriverState.MinY = 0; DriverState.MaxY = MOUSE_MAX_VERT - 1; /* Initialize the counters */ DriverState.HorizCount = DriverState.VertCount = 0; for (i = 0; i < NUM_MOUSE_BUTTONS; i++) { DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0; } /* Return mouse information */ setAX(0xFFFF); // Hardware & driver installed setBX(NUM_MOUSE_BUTTONS); break; } /* Get Software Version, Mouse Type, and IRQ Number, compatible MS MOUSE v6.26+ */ case 0x24: { setBX(MOUSE_VERSION); // Version Number /* * See Ralf Brown: http://www.ctyme.com/intr/rb-5993.htm * for the list of possible values. */ // FIXME: To be determined at runtime! setCH(0x04); // PS/2 Type setCL(0x00); // PS/2 Interrupt break; } // BIOS Function INT 33h, AX = 0x0025 NOT IMPLEMENTED case 0x25: { setAX(0); setBX(0); setCX(0); setDX(0); UNIMPLEMENTED; break; } /* Get Maximum Virtual Coordinates */ case 0x26: { setBX(!DriverEnabled); // FIXME: In fact the MaxX and MaxY here are // theoretical values for the current video mode. // They therefore can be different from the current // min/max values. // See http://www.ctyme.com/intr/rb-5995.htm // for more details. setCX(DriverState.MaxX); setDX(DriverState.MaxY); break; } /* Get Current Minimum/Maximum Virtual Coordinates */ case 0x31: { setAX(DriverState.MinX); setBX(DriverState.MinY); setCX(DriverState.MaxX); setDX(DriverState.MaxY); break; } #if 0 case 0x33: { /* * Related to http://www.ctyme.com/intr/rb-5985.htm * INT 33h, AX=001Ch "SET INTERRUPT RATE": * Values for mouse interrupt rate: * BX = rate 00h no interrupts allowed 01h 30 per second 02h 50 per second 03h 100 per second 04h 200 per second */ } #endif /* Return Pointer to Copyright String */ case 0x4D: { setES(MouseDataSegment); setDI(FIELD_OFFSET(MOUSE_DRIVER, Copyright)); break; } /* Get Version String (pointer) */ case 0x6D: { /* * The format of the version "string" is: * Offset Size Description * 00h BYTE major version * 01h BYTE minor version (BCD) */ setES(MouseDataSegment); setDI(FIELD_OFFSET(MOUSE_DRIVER, Version)); break; } default: { DPRINT1("BIOS Function INT 33h, AX = 0x%04X NOT IMPLEMENTED\n", getAX()); } } } /* PUBLIC FUNCTIONS ***********************************************************/ static VOID DosMouseEnable(VOID) { if (DriverEnabled) return; DriverEnabled = TRUE; /* Get the old IRQ handler */ OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT]; /* Set the IRQ handler */ RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment), MOUSE_IRQ_INT, DosMouseIrq, NULL); } static VOID DosMouseDisable(VOID) { if (!DriverEnabled) return; /* Restore the old IRQ handler */ ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler; DriverEnabled = FALSE; } BOOLEAN DosMouseInitialize(VOID) { /* Initialize some memory for storing our data that should be available to DOS */ MouseDataSegment = DosAllocateMemory(sizeof(MOUSE_DRIVER), NULL); if (MouseDataSegment == 0) return FALSE; MouseData = (PMOUSE_DRIVER)SEG_OFF_TO_PTR(MouseDataSegment, 0x0000); /* Initialize the callback context */ InitializeContext(&MouseContext, MouseDataSegment, FIELD_OFFSET(MOUSE_DRIVER, MouseContextScratch)); /* Clear the state */ RtlZeroMemory(&DriverState, sizeof(DriverState)); /* Mouse Driver Copyright */ RtlCopyMemory(MouseData->Copyright, MouseCopyright, sizeof(MouseCopyright)-1); /* Mouse Driver Version in BCD format, compatible MS-MOUSE */ MouseData->Version = MAKEWORD(MOUSE_VERSION/0x0100, MOUSE_VERSION%0x0100); /* Get the old mouse service interrupt handler */ OldIntHandler = ((PDWORD)BaseAddress)[DOS_MOUSE_INTERRUPT]; /* Initialize the interrupt handler */ RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseDosInt16Stub), MouseDataSegment), DOS_MOUSE_INTERRUPT, DosMouseService, NULL); DosMouseEnable(); // UCHAR BH = getBH(); // setBH(0x01); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); return TRUE; } VOID DosMouseCleanup(VOID) { if (DriverState.ShowCount > 0) EraseMouseCursor(); DosMouseDisable(); // UCHAR BH = getBH(); // setBH(0x00); // BiosPs2Service(0x00); // FIXME: Check for return status in AH and CF // setBH(BH); /* Restore the old mouse service interrupt handler */ ((PDWORD)BaseAddress)[DOS_MOUSE_INTERRUPT] = OldIntHandler; DosFreeMemory(MouseDataSegment); MouseDataSegment = 0; MouseData = 0; } /* EOF */