[NTVDM]
[reactos.git] / subsystems / ntvdm / ps2.c
index f5cbef6..5138d0b 100644 (file)
@@ -10,8 +10,9 @@
 
 #define NDEBUG
 
-#include "ps2.h"
 #include "emulator.h"
+#include "io.h"
+#include "ps2.h"
 #include "pic.h"
 
 /* PRIVATE VARIABLES **********************************************************/
@@ -20,18 +21,26 @@ static BYTE KeyboardQueue[KEYBOARD_BUFFER_SIZE];
 static BOOLEAN KeyboardQueueEmpty = TRUE;
 static UINT KeyboardQueueStart = 0;
 static UINT KeyboardQueueEnd = 0;
-static BYTE KeyboardResponse = 0;
+static BYTE KeyboardData = 0, KeyboardResponse = 0;
 static BOOLEAN KeyboardReadResponse = FALSE, KeyboardWriteResponse = FALSE;
 static BYTE KeyboardConfig = PS2_DEFAULT_CONFIG;
+static HANDLE QueueMutex = NULL;
+
+static HANDLE InputThread = NULL;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
 {
+    BOOLEAN Result = TRUE;
+
+    WaitForSingleObject(QueueMutex, INFINITE);
+
     /* Check if the keyboard queue is full */
     if (!KeyboardQueueEmpty && (KeyboardQueueStart == KeyboardQueueEnd))
     {
-        return FALSE;
+        Result = FALSE;
+        goto Done;
     }
 
     /* Insert the value in the queue */
@@ -42,13 +51,23 @@ static BOOLEAN KeyboardQueuePush(BYTE ScanCode)
     /* Since we inserted a value, it's not empty anymore */
     KeyboardQueueEmpty = FALSE;
 
-    return TRUE;
+Done:
+    ReleaseMutex(QueueMutex);
+    return Result;
 }
 
 static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
 {
+    BOOLEAN Result = TRUE;
+
+    WaitForSingleObject(QueueMutex, INFINITE);
+
     /* Make sure the keyboard queue is not empty */
-    if (KeyboardQueueEmpty) return FALSE;
+    if (KeyboardQueueEmpty)
+    {
+        Result = FALSE;
+        goto Done;
+    }
 
     /* Get the scan code */
     *ScanCode = KeyboardQueue[KeyboardQueueStart];
@@ -63,7 +82,9 @@ static BOOLEAN KeyboardQueuePop(BYTE *ScanCode)
         KeyboardQueueEmpty = TRUE;
     }
 
-    return TRUE;
+Done:
+    ReleaseMutex(QueueMutex);
+    return Result;
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -72,9 +93,13 @@ BYTE KeyboardReadStatus()
 {
     BYTE Status = 0;
 
+    WaitForSingleObject(QueueMutex, INFINITE);
+
     /* Set the first bit if the data can be read */
     if (KeyboardReadResponse || !KeyboardQueueEmpty) Status |= 1 << 0;
 
+    ReleaseMutex(QueueMutex);
+
     /* Always set bit 2 */
     Status |= 1 << 2;
 
@@ -93,7 +118,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         {
             KeyboardResponse = KeyboardConfig;
             KeyboardReadResponse = TRUE;
-
             break;
         }
 
@@ -111,7 +135,6 @@ VOID KeyboardWriteCommand(BYTE Command)
             /* These commands require a response */
             KeyboardResponse = Command;
             KeyboardWriteResponse = TRUE;
-
             break;
         }
 
@@ -119,7 +142,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         case 0xA7:
         {
             // TODO: Mouse support
-
             break;
         }
 
@@ -127,7 +149,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         case 0xA8:
         {
             // TODO: Mouse support
-
             break;
         }
 
@@ -136,7 +157,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         {
             KeyboardResponse = 0;
             KeyboardReadResponse = TRUE;
-
             break;
         }
 
@@ -145,7 +165,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         {
             KeyboardResponse = 0x55;
             KeyboardReadResponse = TRUE;
-
             break;
         }
 
@@ -182,7 +201,6 @@ VOID KeyboardWriteCommand(BYTE Command)
         {
             /* Stop the simulation */
             VdmRunning = FALSE;
-
             break;
         }
     }
@@ -190,19 +208,19 @@ VOID KeyboardWriteCommand(BYTE Command)
 
 BYTE KeyboardReadData()
 {
-    BYTE Value = 0;
-
     /* If there was a response byte from the controller, return it */
     if (KeyboardReadResponse)
     {
         KeyboardReadResponse = FALSE;
-        return KeyboardResponse;
+        KeyboardData = KeyboardResponse;
+    }
+    else
+    {
+        /* Otherwise, read the data from the queue */
+        KeyboardQueuePop(&KeyboardData);
     }
-    
-    /* Otherwise, read the data from the queue */
-    KeyboardQueuePop(&Value);
 
-    return Value;
+    return KeyboardData;
 }
 
 VOID KeyboardWriteData(BYTE Data)
@@ -242,7 +260,6 @@ VOID KeyboardWriteData(BYTE Data)
             {
                 /* Push the data byte to the keyboard queue */
                 KeyboardQueuePush(Data);
-
                 break;
             }
 
@@ -265,50 +282,106 @@ VOID KeyboardWriteData(BYTE Data)
     // TODO: Implement PS/2 device commands
 }
 
-VOID CheckForInputEvents()
+BYTE WINAPI PS2ReadPort(ULONG Port)
 {
-    PINPUT_RECORD Buffer;
-    HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
-    DWORD i, j, Count, TotalEvents;
-    BYTE ScanCode;
-
-    /* Get the number of input events */
-    if (!GetNumberOfConsoleInputEvents(ConsoleInput, &Count)) return;
-    if (Count == 0) return;
+    if (Port == PS2_CONTROL_PORT)
+        return KeyboardReadStatus();
+    else if (Port == PS2_DATA_PORT)
+        return KeyboardReadData();
+    else
+        return 0;
+}
 
-    /* Allocate the buffer */
-    Buffer = (PINPUT_RECORD)HeapAlloc(GetProcessHeap(), 0, Count * sizeof(INPUT_RECORD));
-    if (Buffer == NULL) return;
+VOID WINAPI PS2WritePort(ULONG Port, BYTE Data)
+{
+    if (Port == PS2_CONTROL_PORT)
+        KeyboardWriteCommand(Data);
+    else if (Port == PS2_DATA_PORT)
+        KeyboardWriteData(Data);
+}
 
-    /* Peek the input events */
-    if (!ReadConsoleInput(ConsoleInput, Buffer, Count, &TotalEvents)) goto Cleanup;
+DWORD WINAPI InputThreadProc(LPVOID Parameter)
+{
+    INT i;
+    HANDLE ConsoleInput = (HANDLE)Parameter;
+    INPUT_RECORD InputRecord;
+    DWORD Count;
 
-    for (i = 0; i < TotalEvents; i++)
+    while (VdmRunning)
     {
-        /* Check if this is a key event */
-        if (Buffer[i].EventType != KEY_EVENT) continue;
-
-        /* Get the scan code */
-        ScanCode = (BYTE)Buffer[i].Event.KeyEvent.wVirtualScanCode;
+        /* Wait for an input record */
+        if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
+        {
+            DWORD LastError = GetLastError();
+            DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError);
+            return LastError;
+        }
 
-        /* If this is a key release, set the highest bit in the scan code */
-        if (!Buffer[i].Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
+        ASSERT(Count != 0);
 
-        /* Push the scan code onto the keyboard queue */
-        for (j = 0; j < Buffer[i].Event.KeyEvent.wRepeatCount; j++)
+        /* Check the event type */
+        switch (InputRecord.EventType)
         {
-            KeyboardQueuePush(ScanCode);
-        }
+            case KEY_EVENT:
+            {
+                BYTE ScanCode = (BYTE)InputRecord.Event.KeyEvent.wVirtualScanCode;
 
-        /* Yes, IRQ 1 */
-        PicInterruptRequest(1);
+                /* If this is a key release, set the highest bit in the scan code */
+                if (!InputRecord.Event.KeyEvent.bKeyDown) ScanCode |= 0x80;
 
-        /* Stop the loop */
-        break;
+                /* Push the scan code onto the keyboard queue */
+                for (i = 0; i < InputRecord.Event.KeyEvent.wRepeatCount; i++)
+                {
+                    KeyboardQueuePush(ScanCode);
+                }
+
+                /* Keyboard IRQ */
+                PicInterruptRequest(1);
+                break;
+            }
+
+            case MOUSE_EVENT:
+            {
+                // TODO: NOT IMPLEMENTED
+                UNIMPLEMENTED;
+                break;
+            }
+
+            default:
+            {
+                /* Ignored */
+                break;
+            }
+        }
     }
 
-Cleanup:
-    HeapFree(GetProcessHeap(), 0, Buffer);
+    return 0;
+}
+
+BOOLEAN PS2Initialize(HANDLE ConsoleInput)
+{
+    /* Create the mutex */
+    QueueMutex = CreateMutex(NULL, FALSE, NULL);
+
+    /* Start the input thread */
+    InputThread = CreateThread(NULL, 0, &InputThreadProc, ConsoleInput, 0, NULL);
+
+    // if (InputThread == NULL) return FALSE;
+
+    /* Register the I/O Ports */
+    RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
+    RegisterIoPort(PS2_DATA_PORT   , PS2ReadPort, PS2WritePort);
+
+    return TRUE;
+}
+
+VOID PS2Cleanup(VOID)
+{
+    /* Close the input thread handle */
+    if (InputThread != NULL) CloseHandle(InputThread);
+    InputThread = NULL;
+
+    CloseHandle(QueueMutex);
 }
 
 /* EOF */