Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / subsystems / mvdm / ntvdm / bios / bios32 / moubios32.c
diff --git a/subsystems/mvdm/ntvdm/bios/bios32/moubios32.c b/subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
new file mode 100644 (file)
index 0000000..559101e
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
+ * PURPOSE:         VDM 32-bit PS/2 Mouse BIOS
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ *
+ * NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS.
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#include "emulator.h"
+#include "cpu/cpu.h" // for EMULATOR_FLAG_CF
+
+#include "moubios32.h"
+#include "bios32p.h"
+
+#include "io.h"
+#include "hardware/mouse.h"
+#include "hardware/ps2.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+#define MOUSE_IRQ_INT   0x74
+
+static BOOLEAN MouseEnabled = FALSE;
+static DWORD OldIrqHandler;
+
+/*
+ * Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA.
+ *
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm
+ * for more information. In particular:
+ * when the subroutine is called, it is given 4 WORD values on the stack;
+ * the handler should return with a FAR return without popping the stack.
+ */
+static ULONG DeviceHandler = 0;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static VOID DisableMouseInt(VOID)
+{
+    BYTE ControllerConfig;
+
+    /* Clear the mouse queue */
+    while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
+
+    /* Disable mouse interrupt and events */
+    IOWriteB(PS2_CONTROL_PORT, 0x20);
+    ControllerConfig = IOReadB(PS2_DATA_PORT);
+    ControllerConfig &= ~0x02; // Turn off IRQ12
+    ControllerConfig |=  0x20; // Disable mouse clock line
+    IOWriteB(PS2_CONTROL_PORT, 0x60);
+    IOWriteB(PS2_DATA_PORT, ControllerConfig);
+}
+
+static VOID EnableMouseInt(VOID)
+{
+    BYTE ControllerConfig;
+
+    /* Clear the mouse queue */
+    while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
+
+    /* Enable mouse interrupt and events */
+    IOWriteB(PS2_CONTROL_PORT, 0x20);
+    ControllerConfig = IOReadB(PS2_DATA_PORT);
+    ControllerConfig |=  0x02; // Turn on IRQ12
+    ControllerConfig &= ~0x20; // Enable mouse clock line
+    IOWriteB(PS2_CONTROL_PORT, 0x60);
+    IOWriteB(PS2_DATA_PORT, ControllerConfig);
+}
+
+static inline
+VOID SendMouseCommand(UCHAR Command)
+{
+    /* Clear the mouse queue */
+    while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
+
+    /* Send the command */
+    IOWriteB(PS2_CONTROL_PORT, 0xD4);
+    IOWriteB(PS2_DATA_PORT, Command);
+}
+
+static inline
+UCHAR ReadMouseData(VOID)
+{
+    PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231
+    return IOReadB(PS2_DATA_PORT);
+}
+
+
+static
+VOID BiosMouseEnable(VOID)
+{
+    if (MouseEnabled) return;
+
+    MouseEnabled = 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 BiosMouseDisable(VOID)
+{
+    if (!MouseEnabled) return;
+
+    /* Restore the old IRQ handler */
+    // ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
+
+    MouseEnabled = FALSE;
+}
+
+
+// Mouse IRQ 12
+static VOID WINAPI BiosMouseIrq(LPWORD Stack)
+{
+    DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n",
+            HIWORD(DeviceHandler), LOWORD(DeviceHandler));
+
+    if (DeviceHandler != 0)
+    {
+        /*
+         * Prepare the stack for the mouse device handler:
+         * push Status, X and Y data, and a zero word.
+         */
+        setSP(getSP() - sizeof(WORD));
+        *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status
+        setSP(getSP() - sizeof(WORD));
+        *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0)
+        setSP(getSP() - sizeof(WORD));
+        *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0)
+        setSP(getSP() - sizeof(WORD));
+        *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero
+
+        /* Call the device handler */
+        RunCallback16(&BiosContext, DeviceHandler);
+
+        /* Pop the stack */
+        setSP(getSP() + 4*sizeof(WORD));
+    }
+
+    PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
+}
+
+VOID BiosMousePs2Interface(LPWORD Stack)
+{
+    /* Disable mouse interrupt and events */
+    DisableMouseInt();
+
+    switch (getAL())
+    {
+        /* Enable / Disable */
+        case 0x00:
+        {
+            UCHAR State = getBH();
+
+            if (State > 2)
+            {
+                /* Invalid function */
+                setAH(0x01);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            if (State == 0x00)
+            {
+                BiosMouseDisable();
+
+                /* Disable packet reporting */
+                SendMouseCommand(0xF5);
+            }
+            else // if (State == 0x01)
+            {
+                /* Check for the presence of the device handler */
+                if (DeviceHandler == 0)
+                {
+                    /* No device handler installed */
+                    setAH(0x05);
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                BiosMouseEnable();
+
+                /* Enable packet reporting */
+                SendMouseCommand(0xF4);
+            }
+
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Initialize */
+        case 0x05:
+        {
+            // Fall through
+        }
+
+        /* Reset */
+        case 0x01:
+        {
+            UCHAR Answer;
+
+            SendMouseCommand(0xFF);
+            Answer = ReadMouseData();
+            /* A "Resend" signal (0xFE) is sent if no mouse is attached */
+            if (Answer == 0xFE)
+            {
+                /* Resend */
+                setAH(0x04);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+            else if (Answer != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS
+            setBH(ReadMouseData()); // Mouse ID
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Set Sampling Rate */
+        case 0x02:
+        {
+            UCHAR SampleRate = 0;
+
+            switch (getBH())
+            {
+                case 0x00: SampleRate =  10; break; //  10 reports/sec
+                case 0x01: SampleRate =  20; break; //  20    "     "
+                case 0x02: SampleRate =  40; break; //  40    "     "
+                case 0x03: SampleRate =  60; break; //  60    "     "
+                case 0x04: SampleRate =  80; break; //  80    "     "
+                case 0x05: SampleRate = 100; break; // 100    "     "
+                case 0x06: SampleRate = 200; break; // 200    "     "
+                default:   SampleRate =   0;
+            }
+
+            if (SampleRate == 0)
+            {
+                /* Invalid input */
+                setAH(0x02);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            SendMouseCommand(0xF3);
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            SendMouseCommand(SampleRate);
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Set Resolution */
+        case 0x03:
+        {
+            UCHAR Resolution = getBH();
+
+            /*
+             * 0:  25 dpi, 1 count  per millimeter
+             * 1:  50 dpi, 2 counts per millimeter
+             * 2: 100 dpi, 4 counts per millimeter
+             * 3: 200 dpi, 8 counts per millimeter
+             */
+            if (Resolution > 3)
+            {
+                /* Invalid input */
+                setAH(0x02);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            SendMouseCommand(0xE8);
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            SendMouseCommand(Resolution);
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Get Type */
+        case 0x04:
+        {
+            SendMouseCommand(0xF2);
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            setBH(ReadMouseData());
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Extended Commands (Return Status and Set Scaling Factor) */
+        case 0x06:
+        {
+            UCHAR Command = getBH();
+
+            switch (Command)
+            {
+                /* Return Status */
+                case 0x00:
+                {
+                    SendMouseCommand(0xE9);
+                    if (ReadMouseData() != MOUSE_ACK)
+                    {
+                        /* Failure */
+                        setAH(0x03);
+                        Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                        break;
+                    }
+
+                    setBL(ReadMouseData()); // Status
+                    setCL(ReadMouseData()); // Resolution
+                    setDL(ReadMouseData()); // Sample rate
+
+                    /* Success */
+                    setAH(0x00);
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                /* Set Scaling Factor to 1:1 */
+                case 0x01:
+                /* Set Scaling Factor to 2:1 */
+                case 0x02:
+                {
+                    SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7);
+                    if (ReadMouseData() != MOUSE_ACK)
+                    {
+                        /* Failure */
+                        setAH(0x03);
+                        Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                        break;
+                    }
+
+                    /* Success */
+                    setAH(0x00);
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                default:
+                {
+                    /* Invalid function */
+                    setAH(0x01);
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    break;
+                }
+            }
+
+            break;
+        }
+
+        /* Set Device Handler Address */
+        case 0x07:
+        {
+            /* ES:BX == 0000h:0000h removes the device handler */
+            DeviceHandler = MAKELONG(getBX(), getES());
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Write to Pointer Port */
+        case 0x08:
+        {
+            SendMouseCommand(getBL());
+            if (ReadMouseData() != MOUSE_ACK)
+            {
+                /* Failure */
+                setAH(0x03);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                break;
+            }
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        /* Read from Pointer Port */
+        case 0x09:
+        {
+            setBL(ReadMouseData());
+            setCL(ReadMouseData());
+            setDL(ReadMouseData());
+
+            /* Success */
+            setAH(0x00);
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n",
+                    getAL());
+
+            /* Unknown function */
+            setAH(0x01);
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+        }
+    }
+
+    /* Reenable mouse interrupt and events */
+    EnableMouseInt();
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID MouseBios32Post(VOID)
+{
+    UCHAR Answer;
+
+    /* Initialize PS/2 mouse port */
+    // Enable the port
+    IOWriteB(PS2_CONTROL_PORT, 0xA8);
+
+    /* Detect mouse presence by attempting a reset */
+    SendMouseCommand(0xFF);
+    Answer = ReadMouseData();
+    /* A "Resend" signal (0xFE) is sent if no mouse is attached */
+    if (Answer == 0xFE)
+    {
+        DPRINT1("No mouse present!\n");
+    }
+    else if (Answer != MOUSE_ACK)
+    {
+        DPRINT1("Mouse reset failure!\n");
+    }
+    else
+    {
+        /* Mouse present, try to completely enable it */
+
+        // FIXME: The following is temporary until
+        // this is moved into the mouse driver!!
+
+        /* Enable packet reporting */
+        SendMouseCommand(0xF4);
+        if (ReadMouseData() != MOUSE_ACK)
+        {
+            DPRINT1("Failed to enable mouse!\n");
+        }
+        else
+        {
+            /* Enable mouse interrupt and events */
+            EnableMouseInt();
+        }
+    }
+
+    /* No mouse driver available so far */
+    RegisterBiosInt32(0x33, NULL);
+
+    /* Set up the HW vector interrupts */
+    EnableHwIRQ(12, BiosMouseIrq);
+}
+
+BOOLEAN MouseBiosInitialize(VOID)
+{
+    return TRUE;
+}
+
+VOID MouseBios32Cleanup(VOID)
+{
+}