--- /dev/null
+/*
+ * COPYRIGHT: GPL - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: mouse32.c
+ * PURPOSE: VDM 32-bit compatible MOUSE.COM driver
+ * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "emulator.h"
+#include "callback.h"
+
+#include "mouse32.h"
+#include "bios/bios.h"
+
+#include "io.h"
+#include "dos32krnl/dos.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static BOOLEAN DriverEnabled = TRUE;
+static MOUSE_DRIVER_STATE DriverState;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static VOID PaintMouseCursor(VOID)
+{
+ if (Bda->VideoMode <= 3)
+ {
+ WORD Character;
+ DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
+
+ EmulatorReadMemory(&EmulatorContext,
+ VideoAddress
+ + (DriverState.Position.Y * Bda->ScreenColumns
+ + DriverState.Position.X) * sizeof(WORD),
+ (LPVOID)&Character,
+ sizeof(WORD));
+
+ DriverState.Character = Character;
+ Character &= DriverState.TextCursor.ScreenMask;
+ Character ^= DriverState.TextCursor.CursorMask;
+
+ EmulatorWriteMemory(&EmulatorContext,
+ VideoAddress
+ + (DriverState.Position.Y * Bda->ScreenColumns
+ + DriverState.Position.X) * sizeof(WORD),
+ (LPVOID)&Character,
+ sizeof(WORD));
+ }
+ else
+ {
+ // TODO: NOT IMPLEMENTED
+ UNIMPLEMENTED;
+ }
+}
+
+static VOID EraseMouseCursor(VOID)
+{
+ if (Bda->VideoMode <= 3)
+ {
+ DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
+
+ EmulatorWriteMemory(&EmulatorContext,
+ VideoAddress
+ + (DriverState.Position.Y * Bda->ScreenColumns
+ + DriverState.Position.X) * sizeof(WORD),
+ (LPVOID)&DriverState.Character,
+ sizeof(WORD));
+ }
+ else
+ {
+ // TODO: NOT IMPLEMENTED
+ UNIMPLEMENTED;
+ }
+}
+
+static VOID CallMouseUserHandlers(USHORT CallMask)
+{
+#if 0
+ USHORT i;
+ USHORT AX, BX, CX, DX, SI, DI;
+
+ /* Call handler 0 */
+ if ((DriverState.Handler0.CallMask & CallMask) != 0 &&
+ DriverState.Handler0.Callback != (ULONG)NULL)
+ {
+ /*
+ * 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();
+ SI = getSI();
+ DI = getDI();
+
+ setAX(CallMask);
+ setBX(DriverState.ButtonState);
+ setCX(DriverState.Position.X);
+ setDX(DriverState.Position.Y);
+ setSI(DriverState.MickeysPerCellHoriz);
+ setDI(DriverState.MickeysPerCellVert);
+
+ DPRINT1("Calling Handler0 0x%08x with CallMask 0x%04x\n",
+ DriverState.Handler0.Callback, CallMask);
+
+ /* Call the callback */
+ RunCallback16(&DosContext, DriverState.Handler0.Callback);
+
+ setAX(AX);
+ setBX(BX);
+ setCX(CX);
+ setDX(DX);
+ setSI(SI);
+ setDI(DI);
+ }
+
+ for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
+ {
+ /* Call the suitable handlers */
+ if ((DriverState.Handlers[i].CallMask & CallMask) != 0 &&
+ DriverState.Handlers[i].Callback != (ULONG)NULL)
+ {
+ /*
+ * 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();
+ SI = getSI();
+ DI = getDI();
+
+ setAX(CallMask);
+ setBX(DriverState.ButtonState);
+ setCX(DriverState.Position.X);
+ setDX(DriverState.Position.Y);
+ setSI(DriverState.MickeysPerCellHoriz);
+ setDI(DriverState.MickeysPerCellVert);
+
+ DPRINT1("Calling Handler[%d] 0x%08x with CallMask 0x%04x\n",
+ i, DriverState.Handlers[i].Callback, CallMask);
+
+ /* Call the callback */
+ RunCallback16(&DosContext, DriverState.Handlers[i].Callback);
+
+ setAX(AX);
+ setBX(BX);
+ setCX(CX);
+ setDX(DX);
+ setSI(SI);
+ setDI(DI);
+ }
+ }
+#endif
+}
+
+static VOID WINAPI BiosMouseService(LPWORD Stack)
+{
+ switch (getAX())
+ {
+ /* Reset Driver */
+ case 0x00:
+ {
+ SHORT i;
+
+ DriverEnabled = TRUE;
+ DriverState.ShowCount = 0;
+ DriverState.ButtonState = 0;
+
+ /* 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;
+
+ DriverState.GraphicsCursor.ScreenMask[0] = 0xC3FF; // 1100001111111111
+ DriverState.GraphicsCursor.ScreenMask[1] = 0xC0FF; // 1100000011111111
+ DriverState.GraphicsCursor.ScreenMask[2] = 0xC07F; // 1100000001111111
+ DriverState.GraphicsCursor.ScreenMask[3] = 0xC01F; // 1100000000011111
+ DriverState.GraphicsCursor.ScreenMask[4] = 0xC00F; // 1100000000001111
+ DriverState.GraphicsCursor.ScreenMask[5] = 0xC007; // 1100000000000111
+ DriverState.GraphicsCursor.ScreenMask[6] = 0xC003; // 1100000000000011
+ DriverState.GraphicsCursor.ScreenMask[7] = 0xC007; // 1100000000000111
+ DriverState.GraphicsCursor.ScreenMask[8] = 0xC01F; // 1100000000011111
+ DriverState.GraphicsCursor.ScreenMask[9] = 0xC01F; // 1100000000011111
+ DriverState.GraphicsCursor.ScreenMask[10] = 0xC00F; // 1100000000001111
+ DriverState.GraphicsCursor.ScreenMask[11] = 0xC60F; // 1100011000001111
+ DriverState.GraphicsCursor.ScreenMask[12] = 0xFF07; // 1111111100000111
+ DriverState.GraphicsCursor.ScreenMask[13] = 0xFF07; // 1111111100000111
+ DriverState.GraphicsCursor.ScreenMask[14] = 0xFF87; // 1111111110000111
+ DriverState.GraphicsCursor.ScreenMask[15] = 0xFFCF; // 1111111111001111
+
+ DriverState.GraphicsCursor.CursorMask[0] = 0x0000; // 0000000000000000
+ DriverState.GraphicsCursor.CursorMask[1] = 0x1C00; // 0001110000000000
+ DriverState.GraphicsCursor.CursorMask[2] = 0x1F00; // 0001111100000000
+ DriverState.GraphicsCursor.CursorMask[3] = 0x1F80; // 0001111110000000
+ DriverState.GraphicsCursor.CursorMask[4] = 0x1FE0; // 0001111111100000
+ DriverState.GraphicsCursor.CursorMask[5] = 0x1FF0; // 0001111111110000
+ DriverState.GraphicsCursor.CursorMask[6] = 0x1FF8; // 0001111111111000
+ DriverState.GraphicsCursor.CursorMask[7] = 0x1FE0; // 0001111111100000
+ DriverState.GraphicsCursor.CursorMask[8] = 0x1FC0; // 0001111111000000
+ DriverState.GraphicsCursor.CursorMask[9] = 0x1FC0; // 0001111111000000
+ DriverState.GraphicsCursor.CursorMask[10] = 0x19E0; // 0001100111100000
+ DriverState.GraphicsCursor.CursorMask[11] = 0x00E0; // 0000000011100000
+ DriverState.GraphicsCursor.CursorMask[12] = 0x0070; // 0000000001110000
+ DriverState.GraphicsCursor.CursorMask[13] = 0x0070; // 0000000001110000
+ DriverState.GraphicsCursor.CursorMask[14] = 0x0030; // 0000000000110000
+ DriverState.GraphicsCursor.CursorMask[15] = 0x0000; // 0000000000000000
+
+ /* Initialize the counters */
+ DriverState.HorizCount = DriverState.VertCount = 0;
+
+ for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
+ {
+ DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0;
+ }
+
+ /* Initialize the resolution */
+ DriverState.MickeysPerCellHoriz = 8;
+ DriverState.MickeysPerCellVert = 16;
+
+ /* Return mouse information */
+ setAX(0xFFFF); // Hardware & driver installed
+ setBX(NUM_MOUSE_BUTTONS);
+
+ break;
+ }
+
+ /* Show Mouse Cursor */
+ case 0x01:
+ {
+ DriverState.ShowCount++;
+ if (DriverState.ShowCount > 0) PaintMouseCursor();
+
+ break;
+ }
+
+ /* Hide Mouse Cursor */
+ case 0x02:
+ {
+ DriverState.ShowCount--;
+ if (DriverState.ShowCount <= 0) EraseMouseCursor();
+
+ break;
+ }
+
+ /* Return Position And Button Status */
+ case 0x03:
+ {
+ setBX(DriverState.ButtonState);
+ setCX(DriverState.Position.X);
+ setDX(DriverState.Position.Y);
+ break;
+ }
+
+ /* Position Mouse Cursor */
+ case 0x04:
+ {
+ POINT Point;
+
+ Point.x = getCX();
+ Point.y = getDX();
+
+ ClientToScreen(GetConsoleWindow(), &Point);
+ SetCursorPos(Point.x, Point.y);
+
+ break;
+ }
+
+ /* Return Button Press Data */
+ case 0x05:
+ {
+ WORD Button = getBX();
+
+ setAX(DriverState.ButtonState);
+ setBX(DriverState.PressCount[Button]);
+ setCX(DriverState.LastPress[Button].X);
+ setDX(DriverState.LastPress[Button].Y);
+
+ /* Reset the counter */
+ DriverState.PressCount[Button] = 0;
+
+ break;
+ }
+
+ /* Return Button Release Data */
+ case 0x06:
+ {
+ WORD Button = getBX();
+
+ setAX(DriverState.ButtonState);
+ setBX(DriverState.ReleaseCount[Button]);
+ setCX(DriverState.LastRelease[Button].X);
+ setDX(DriverState.LastRelease[Button].Y);
+
+ /* Reset the counter */
+ DriverState.ReleaseCount[Button] = 0;
+
+ 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();
+
+ RtlMoveMemory(DriverState.GraphicsCursor.ScreenMask,
+ MaskBitmap,
+ sizeof(DriverState.GraphicsCursor.ScreenMask));
+
+ RtlMoveMemory(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, 0x%08x\n",
+ DriverState.Handler0.CallMask, DriverState.Handler0.Callback);
+ break;
+ }
+
+ /* Define Mickey/Pixel Ratio */
+ case 0x0F:
+ {
+ DriverState.MickeysPerCellHoriz = getCX();
+ DriverState.MickeysPerCellVert = getDX();
+ 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
+
+ /* 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:
+ {
+ *((PMOUSE_DRIVER_STATE)SEG_OFF_TO_PTR(getES(), getDX())) = DriverState;
+ break;
+ }
+
+ /* Restore Driver State */
+ case 0x17:
+ {
+ 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, 0x%08x\n",
+ CallMask, Callback);
+
+ if (CallMask == 0x0000)
+ {
+ /*
+ * Find the handler entry corresponding to the given
+ * callback and undefine it.
+ */
+ for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
+ {
+ if (DriverState.Handlers[i].Callback == Callback)
+ {
+ /* Found it, undefine the handler */
+ DriverState.Handlers[i].CallMask = 0x0000;
+ DriverState.Handlers[i].Callback = (ULONG)NULL;
+ Success = TRUE;
+ break;
+ }
+ }
+ }
+ else if (Callback == (ULONG)NULL)
+ {
+ /*
+ * Find the handler entry corresponding to the given
+ * callmask and undefine it.
+ */
+ for (i = 0; i < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
+ {
+ if (DriverState.Handlers[i].CallMask == CallMask)
+ {
+ /* Found it, undefine the handler */
+ DriverState.Handlers[i].CallMask = 0x0000;
+ DriverState.Handlers[i].Callback = (ULONG)NULL;
+ 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 < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++i)
+ {
+ /* Find the first empty handler */
+ if (EmptyHandler == 0xFFFF &&
+ DriverState.Handlers[i].CallMask == 0x0000 &&
+ DriverState.Handlers[i].Callback == (ULONG)NULL)
+ {
+ 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 < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]) */)
+ {
+ 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 < sizeof(DriverState.Handlers)/sizeof(DriverState.Handlers[0]); ++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;
+ }
+
+ /* Disable Mouse Driver */
+ case 0x1F:
+ {
+ setES(0x0000);
+ setBX(0x0000);
+
+ DriverEnabled = FALSE;
+ break;
+ }
+
+ /* Enable Mouse Driver */
+ case 0x20:
+ {
+ DriverEnabled = TRUE;
+ break;
+ }
+
+ default:
+ {
+ DPRINT1("BIOS Function INT 33h, AX = 0x%04X NOT IMPLEMENTED\n", getAX());
+ }
+ }
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID MouseBiosUpdatePosition(PCOORD NewPosition)
+{
+ SHORT DeltaX = NewPosition->X - DriverState.Position.X;
+ SHORT DeltaY = NewPosition->Y - DriverState.Position.Y;
+
+ if (!DriverEnabled) return;
+
+ DriverState.HorizCount += (DeltaX * (SHORT)DriverState.MickeysPerCellHoriz) / 8;
+ DriverState.VertCount += (DeltaY * (SHORT)DriverState.MickeysPerCellVert ) / 8;
+
+ if (DriverState.ShowCount > 0)
+ {
+ EraseMouseCursor();
+ DriverState.Position = *NewPosition;
+ PaintMouseCursor();
+ }
+
+ /* Call the mouse handlers */
+ CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
+}
+
+VOID MouseBiosUpdateButtons(WORD ButtonState)
+{
+ USHORT i;
+ USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format
+
+ if (!DriverEnabled) return;
+
+ 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);
+}
+
+BOOLEAN DosMouseInitialize(VOID)
+{
+ /* Clear the state */
+ ZeroMemory(&DriverState, sizeof(DriverState));
+
+ /* Initialize the interrupt handler */
+ RegisterDosInt32(BIOS_MOUSE_INTERRUPT, BiosMouseService);
+
+ return TRUE;
+}
+
+VOID DosMouseCleanup(VOID)
+{
+ if (DriverState.ShowCount > 0) EraseMouseCursor();
+}