[NTVDM]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 23 Apr 2015 01:02:36 +0000 (01:02 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 23 Apr 2015 01:02:36 +0000 (01:02 +0000)
- Report A20 line status in the PS/2 controller output port.
- Properly implement XMS functions 3 and 5 (Global Enable/Disable A20 line), 4 and 6 (Local Enable/Disable A20 line) and 7 (Get A20 line status) using flag+counter and PS/2 I/O calls.
- Fix XMS driver version report.

svn path=/trunk/; revision=67361

reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h
reactos/subsystems/mvdm/ntvdm/emulator.c
reactos/subsystems/mvdm/ntvdm/emulator.h
reactos/subsystems/mvdm/ntvdm/hardware/ps2.c

index f6f4de7..816141a 100644 (file)
@@ -13,6 +13,8 @@
 #include "ntvdm.h"
 #include "emulator.h"
 #include "cpu/bop.h"
+#include "io.h"
+#include "hardware/ps2.h"
 
 #include "dos.h"
 #include "dos/dem.h"
@@ -44,6 +46,125 @@ static WORD FreeBlocks = XMS_BLOCKS;
 static RTL_BITMAP AllocBitmap;
 static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32];
 
+/*
+ * Flag used by Global Enable/Disable A20 functions, so that they don't
+ * need to re-change the state of A20 if it was already enabled/disabled.
+ */
+static BOOLEAN IsA20Enabled = FALSE;
+/*
+ * This flag is set to TRUE or FALSE when A20 line was already disabled or
+ * enabled when XMS driver was loaded.
+ * In case A20 was disabled, we are allowed to modify it. In case A20 was
+ * already enabled, we are not allowed to touch it.
+ */
+static BOOLEAN CanChangeA20 = TRUE;
+/*
+ * Count for enabling or disabling the A20 line. The A20 line is enabled
+ * only if the enabling count is greater than or equal to 0.
+ */
+static LONG A20EnableCount = 0;
+
+/* HELPERS FOR A20 LINE *******************************************************/
+
+static BOOLEAN PCAT_A20Control(BYTE Control, PBOOLEAN A20Status)
+{
+    BYTE ControllerOutput;
+
+    /* Retrieve PS/2 controller output byte */
+    IOWriteB(PS2_CONTROL_PORT, 0xD0);
+    ControllerOutput = IOReadB(PS2_DATA_PORT);
+
+    switch (Control)
+    {
+        case 0: /* Disable A20 line */
+            ControllerOutput &= ~0x02;
+            IOWriteB(PS2_CONTROL_PORT, 0xD1);
+            IOWriteB(PS2_DATA_PORT, ControllerOutput);
+            break;
+
+        case 1: /* Enable A20 line */
+            ControllerOutput |= 0x02;
+            IOWriteB(PS2_CONTROL_PORT, 0xD1);
+            IOWriteB(PS2_DATA_PORT, ControllerOutput);
+            break;
+
+        default: /* Get A20 status */
+            break;
+    }
+
+    if (A20Status)
+        *A20Status = (ControllerOutput & 0x02) != 0;
+
+    /* Return success */
+    return TRUE;
+}
+
+static VOID XmsLocalEnableA20(VOID)
+{
+    /* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
+    if (!CanChangeA20) goto Quit;
+
+    /* The count is zero so enable A20 */
+    if (A20EnableCount == 0 && !PCAT_A20Control(1, NULL))
+        goto Fail;
+
+    ++A20EnableCount;
+
+Quit:
+    setAX(0x0001); /* Line successfully enabled */
+    setBL(XMS_STATUS_SUCCESS);
+    return;
+
+Fail:
+    setAX(0x0000); /* Line failed to be enabled */
+    setBL(XMS_STATUS_A20_ERROR);
+    return;
+}
+
+static VOID XmsLocalDisableA20(VOID)
+{
+    /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
+    if (!CanChangeA20) goto Quit;
+
+    /* If the count is already zero, fail */
+    if (A20EnableCount == 0) goto Fail;
+
+    --A20EnableCount;
+
+    /* The count is zero so disable A20 */
+    if (A20EnableCount == 0 && !PCAT_A20Control(0, NULL))
+        goto Fail;
+
+Quit:
+    setAX(0x0001); /* Line successfully disabled */
+    setBL(XMS_STATUS_SUCCESS);
+    return;
+
+Fail:
+    setAX(0x0000); /* Line failed to be enabled */
+    setBL(XMS_STATUS_A20_ERROR);
+    return;
+}
+
+static VOID XmsGetA20State(VOID)
+{
+    BOOLEAN A20Status = FALSE;
+
+    /*
+     * NOTE: The XMS specification explicitely says that this check is done
+     * in a hardware-independent manner, by checking whether high memory wraps.
+     * For our purposes we just call the emulator API.
+     */
+
+    /* Get A20 status */
+    if (PCAT_A20Control(2, &A20Status))
+        setBL(XMS_STATUS_SUCCESS);
+    else
+        setBL(XMS_STATUS_A20_ERROR);
+
+    setAX(A20Status);
+}
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static inline PXMS_HANDLE GetHandleRecord(WORD Handle)
@@ -158,23 +279,75 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack)
         /* Get XMS Version */
         case 0x00:
         {
-            setAX(0x0300); /* XMS version 3.0 */
+            setAX(0x0300); /*    XMS version 3.00 */
+            setBX(0x0301); /* Driver version 3.01 */
             setDX(0x0001); /* HMA present */
-
             break;
         }
 
         /* Global Enable A20 */
         case 0x03:
         {
-            EmulatorSetA20(TRUE);
+            /* Enable A20 if needed */
+            if (!IsA20Enabled)
+            {
+                XmsLocalEnableA20();
+                if (getAX() != 1)
+                {
+                    /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
+                    break;
+                }
+
+                IsA20Enabled = TRUE;
+            }
+
+            setAX(0x0001); /* Line successfully enabled */
+            setBL(XMS_STATUS_SUCCESS);
             break;
         }
 
         /* Global Disable A20 */
         case 0x04:
         {
-            EmulatorSetA20(FALSE);
+            /* Disable A20 if needed */
+            if (IsA20Enabled)
+            {
+                XmsLocalDisableA20();
+                if (getAX() != 1)
+                {
+                    /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
+                    break;
+                }
+
+                IsA20Enabled = FALSE;
+            }
+
+            setAX(0x0001); /* Line successfully disabled */
+            setBL(XMS_STATUS_SUCCESS);
+            break;
+        }
+
+        /* Local Enable A20 */
+        case 0x05:
+        {
+            /* This call sets AX and BL to their correct values */
+            XmsLocalEnableA20();
+            break;
+        }
+
+        /* Local Disable A20 */
+        case 0x06:
+        {
+            /* This call sets AX and BL to their correct values */
+            XmsLocalDisableA20();
+            break;
+        }
+
+        /* Query A20 State */
+        case 0x07:
+        {
+            /* This call sets AX and BL to their correct values */
+            XmsGetA20State();
             break;
         }
 
@@ -184,7 +357,6 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack)
             setAX(FreeBlocks);
             setDX(XMS_BLOCKS);
             setBL(XMS_STATUS_SUCCESS);
-
             break;
         }
 
index bfb1a8a..142de4a 100644 (file)
@@ -15,6 +15,7 @@
 
 #define XMS_STATUS_SUCCESS         0x00
 #define XMS_STATUS_NOT_IMPLEMENTED 0x80
+#define XMS_STATUS_A20_ERROR       0x82
 #define XMS_STATUS_HMA_IN_USE      0x91
 #define XMS_STATUS_OUT_OF_MEMORY   0xA0
 #define XMS_STATUS_OUT_OF_HANDLES  0xA1
index d9389a4..dcd90c3 100644 (file)
@@ -162,6 +162,11 @@ VOID EmulatorSetA20(BOOLEAN Enabled)
     A20Line = Enabled;
 }
 
+BOOLEAN EmulatorGetA20(VOID)
+{
+    return A20Line;
+}
+
 static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
 {
     DPRINT1("NTVDM: BOP_DEBUGGER\n");
index 169447d..72977b8 100644 (file)
@@ -134,6 +134,7 @@ VOID EmulatorTerminate(VOID);
 
 VOID EmulatorInterruptSignal(VOID);
 VOID EmulatorSetA20(BOOLEAN Enabled);
+BOOLEAN EmulatorGetA20(VOID);
 
 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput);
 VOID EmulatorCleanup(VOID);
index bcb2fb2..829b6b7 100644 (file)
@@ -181,7 +181,11 @@ static VOID WINAPI PS2WritePort(USHORT Port, BYTE Data)
             /* Read controller output port */
             case 0xD0:
             {
-                // TODO: Not implemented
+                /* Bit 0 always set, and bit 1 is the A20 gate state */
+                OutputBuffer = (!!EmulatorGetA20() << 1) | 0x01;
+                // FIXME: Set the status of IRQ1 and IRQ12
+
+                StatusRegister |= (1 << 0); // There is something to read
                 break;
             }
 
@@ -231,6 +235,8 @@ static VOID WINAPI PS2WritePort(USHORT Port, BYTE Data)
                     /* Update the A20 line setting */
                     EmulatorSetA20(Data & (1 << 1));
 
+                    // FIXME: Add the status of IRQ1 and IRQ12
+
                     break;
                 }