[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 25 Sep 2015 22:00:57 +0000 (22:00 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 25 Sep 2015 22:00:57 +0000 (22:00 +0000)
Fix DOS character device I/O. Implement CON line buffering.
Make sure INT 21h functions 01h, 06h, 07h, 08h, 0Ah and 3Fh work as expected
for CON input.

svn path=/trunk/; revision=69356

reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/bios.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/condrv.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.h

index a3c98f9..a1ccb18 100644 (file)
@@ -17,6 +17,7 @@
 #include "../dem.h"
 #include "dos.h"
 #include "dosfiles.h"
+#include "handle.h"
 #include "memory.h"
 #include "bios/bios.h"
 
@@ -45,6 +46,16 @@ PBIOS_DATA BiosData;
 CHAR DosReadCharacter(WORD FileHandle)
 {
     WORD BytesRead;
+    PDOS_FILE_DESCRIPTOR Descriptor = NULL;
+    WORD OldDeviceInfo;
+    
+    /* Find the standard input descriptor and switch it to binary mode */
+    Descriptor = DosGetHandleFileDescriptor(FileHandle);
+    if (Descriptor)
+    {
+        OldDeviceInfo = Descriptor->DeviceInfo;
+        Descriptor->DeviceInfo |= FILE_INFO_BINARY;
+    }
 
     Sda->ByteBuffer = '\0';
     DPRINT("DosReadCharacter\n");
@@ -55,6 +66,8 @@ CHAR DosReadCharacter(WORD FileHandle)
                 1,
                 &BytesRead);
 
+    /* Restore the old mode and return the character */
+    if (Descriptor) Descriptor->DeviceInfo = OldDeviceInfo;
     return Sda->ByteBuffer;
 }
 
index a69c748..5c0b286 100644 (file)
@@ -63,15 +63,8 @@ WORD NTAPI ConDrvReadInput(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length)
 
         Pointer[BytesRead++] = Character;
 
-        if (Character != 0 && DoEcho)
-            DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
-
         /* Stop on first carriage return */
-        if (Character == '\r')
-        {
-            if (DoEcho) DosPrintCharacter(DOS_OUTPUT_HANDLE, '\n');
-            break;
-        }
+        if (Character == '\r') break;
     }
 
     *Length = BytesRead;
index 151186c..18e5081 100644 (file)
@@ -45,9 +45,6 @@ PDOS_DATA DosData;
 PDOS_SYSVARS SysVars;
 PDOS_SDA Sda;
 
-/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
-BOOLEAN DoEcho = FALSE;
-
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static BOOLEAN DosChangeDrive(BYTE Drive)
@@ -162,7 +159,54 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory)
     return TRUE;
 }
 
-static BOOLEAN DosControlBreak(VOID)
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID DosEchoCharacter(CHAR Character)
+{
+    switch (Character)
+    {
+        case '\0':
+        {
+            /* Nothing */
+            break;
+        }
+
+        case '\r':
+        case '\n':
+        {
+            /* Print both a carriage return and a newline */
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\r');
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\n');
+            break;
+        }
+
+        case '\b':
+        {
+            /* Erase the character */
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
+            break;
+        }
+
+        default:
+        {
+            /* Check if this is a special character */
+            if (Character < 0x20)
+            {
+                DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
+                Character += 'A' - 1;
+            }
+            else
+            {
+                /* Echo the character */
+                DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+            }
+        }
+    }
+}
+
+BOOLEAN DosControlBreak(VOID)
 {
     setCF(0);
 
@@ -178,8 +222,6 @@ static BOOLEAN DosControlBreak(VOID)
     return FALSE;
 }
 
-/* PUBLIC FUNCTIONS ***********************************************************/
-
 VOID WINAPI DosInt20h(LPWORD Stack)
 {
     /*
@@ -217,14 +259,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             DPRINT("INT 21h, AH = 01h\n");
 
-            // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
-            DoEcho = TRUE;
             Character = DosReadCharacter(DOS_INPUT_HANDLE);
-            DoEcho = FALSE;
-
-            // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
-            // Check also Ctrl-P and set echo-to-printer flag.
-            // Ctrl-Z is not interpreted.
+            DosEchoCharacter(Character);
+            if (Character == 0x03 && DosControlBreak()) break;
 
             setAL(Character);
             break;
@@ -300,8 +337,11 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 /* Input */
                 if (DosCheckInput())
                 {
+                    CHAR Character = DosReadCharacter(DOS_INPUT_HANDLE);
+                    DosEchoCharacter(Character);
+
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
-                    setAL(DosReadCharacter(DOS_INPUT_HANDLE));
+                    setAL(Character);
                 }
                 else
                 {
@@ -314,17 +354,21 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
-        /* Character Input without Echo */
+        /* Direct Character Input without Echo */
         case 0x07:
+        {
+            DPRINT("Direct char input without echo\n");
+            setAL(DosReadCharacter(DOS_INPUT_HANDLE));
+            break;
+        }
+
+        /* Character Input without Echo */
         case 0x08:
         {
             DPRINT("Char input without echo\n");
 
             Character = DosReadCharacter(DOS_INPUT_HANDLE);
-
-            // FIXME: For 0x07, do not check Ctrl-C/Break.
-            //        For 0x08, do check those control sequences and if needed,
-            //        call INT 0x23.
+            if (Character == 0x03 && DosControlBreak()) break;
 
             setAL(Character);
             break;
@@ -353,82 +397,19 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Read Buffered Input */
         case 0x0A:
         {
-            WORD Count = 0;
+            WORD BytesRead;
             PDOS_INPUT_BUFFER InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
 
             DPRINT("Read Buffered Input\n");
+            if (InputBuffer->MaxLength == 0) break;
 
-            while (Count < InputBuffer->MaxLength)
-            {
-                /* Try to read a character (wait) */
-                Character = DosReadCharacter(DOS_INPUT_HANDLE);
-
-                switch (Character)
-                {
-                    /* Extended character */
-                    case '\0':
-                    {
-                        /* Read the scancode */
-                        DosReadCharacter(DOS_INPUT_HANDLE);
-                        break;
-                    }
-
-                    /* Ctrl-C */
-                    case 0x03:
-                    {
-                        DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
-                        DosPrintCharacter(DOS_OUTPUT_HANDLE, 'C');
-
-                        if (DosControlBreak())
-                        {
-                            /* Set the character to a newline to exit the loop */
-                            Character = '\r';
-                        }
-
-                        break;
-                    }
-
-                    /* Backspace */
-                    case '\b':
-                    {
-                        if (Count > 0)
-                        {
-                            Count--;
-
-                            /* Erase the character */
-                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
-                            DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
-                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
-                        }
-
-                        break;
-                    }
-
-                    default:
-                    {
-                        /* Append it to the buffer */
-                        InputBuffer->Buffer[Count] = Character;
-
-                        /* Check if this is a special character */
-                        if (Character < 0x20 && Character != 0x0A && Character != 0x0D)
-                        {
-                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
-                            Character += 'A' - 1;
-                        }
-
-                        /* Echo the character */
-                        DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
-                    }
-                }
-
-                if (Character == '\r') break;
-                if (Character == '\b') continue;
-                Count++; /* Carriage returns are NOT counted */
-            }
-
-            /* Update the length */
-            InputBuffer->Length = Count;
+            /* Read from standard input */
+            DosReadFile(DOS_INPUT_HANDLE,
+                        MAKELONG(getDX() + FIELD_OFFSET(DOS_INPUT_BUFFER, Buffer), getDS()),
+                        InputBuffer->MaxLength,
+                        &BytesRead);
 
+            InputBuffer->Length = LOBYTE(BytesRead);
             break;
         }
 
@@ -1054,12 +1035,10 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             DPRINT("DosReadFile(0x%04X)\n", getBX());
 
-            DoEcho = TRUE;
             ErrorCode = DosReadFile(getBX(),
                                     MAKELONG(getDX(), getDS()),
                                     getCX(),
                                     &BytesRead);
-            DoEcho = FALSE;
 
             if (ErrorCode == ERROR_SUCCESS)
             {
index 40ce392..f8db858 100644 (file)
@@ -246,6 +246,7 @@ typedef struct _DOS_DATA
     WORD DosVersion; // DOS version to report to programs (can be different from the true one)
     DOS_SDA Sda;
     CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
+    BYTE UnreadConInputBuffer[128];
     BYTE DosStack[384];
     BYTE Sft[ANYSIZE_ARRAY];
 } DOS_DATA, *PDOS_DATA;
@@ -293,7 +294,6 @@ C_ASSERT(sizeof(BIOS_DATA) == 0x100);
 
 /* VARIABLES ******************************************************************/
 
-extern BOOLEAN DoEcho;  // FIXME: Maybe move inside BiosData? (it's set by BIOS but used by CON driver in DOS BIOS)
 extern PBIOS_DATA BiosData;
 extern PDOS_DATA DosData;
 extern PDOS_SYSVARS SysVars;
@@ -324,6 +324,9 @@ VOID DosPrintCharacter(WORD FileHandle, CHAR Character);
 
 BOOLEAN DosBIOSInitialize(VOID);
 
+BOOLEAN DosControlBreak(VOID);
+VOID DosEchoCharacter(CHAR Character);
+
 /*
  * DOS Kernel Functions
  * See dos.c
index 07cce82..ddcc3ff 100644 (file)
@@ -691,9 +691,154 @@ WORD DosReadFile(WORD FileHandle,
         PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
         if (!Node->ReadRoutine) return ERROR_INVALID_FUNCTION;
 
-        /* Read the device */
-        Node->ReadRoutine(Node, Buffer, &Count);
-        *BytesRead = Count;
+        if (Descriptor->DeviceInfo & FILE_INFO_BINARY)
+        {
+            /* Read from the device directly */
+            Node->ReadRoutine(Node, Buffer, &Count);
+            *BytesRead = Count;
+        }
+        else if (Descriptor->DeviceInfo & FILE_INFO_STDIN)
+        {
+            /* Line-buffered CON input */
+            PCHAR ConBuffer = NULL;
+            PCHAR Pointer = FAR_POINTER(Buffer);
+
+            /* Check if the buffer is empty */
+            if (!SysVars->UnreadConInput)
+            {
+                ULONG LineSize = 0;
+
+                SysVars->UnreadConInput = FIELD_OFFSET(DOS_DATA, UnreadConInputBuffer);
+                ConBuffer = (PCHAR)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, SysVars->UnreadConInput);
+
+                while (TRUE)
+                {
+                    USHORT Amount = 1;
+                    CHAR Character;
+
+                    /* Read a character from the CON device */
+                    Node->ReadRoutine(Node,
+                                      MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
+                                               DOS_DATA_SEGMENT),
+                                      &Amount);
+                    if (Amount == 0) break;
+
+                    Character = Sda->ByteBuffer;
+
+                    if (LineSize == 127 && Character != '\r' && Character != '\b')
+                    {
+                        /* Line buffer full */
+                        // TODO: Should we beep?
+                        continue;
+                    }
+
+                    switch (Character)
+                    {
+                        /* Extended character */
+                        case '\0':
+                        {
+                            /* Read the scancode and discard it */
+                            Amount = 1;
+                            Node->ReadRoutine(Node,
+                                              MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
+                                                       DOS_DATA_SEGMENT),
+                                              &Amount);
+                            break;
+                        }
+
+                        /* Ctrl-C */
+                        case 0x03:
+                        {
+                            DosEchoCharacter(Character);
+
+                            if (DosControlBreak())
+                            {
+                                /* Set the character to CR to end the loop */
+                                Character = '\r';
+                            }
+
+                            break;
+                        }
+
+                        case '\b':
+                        {
+                            if (LineSize > 0)
+                            {
+                                LineSize--;
+                                if (ConBuffer[LineSize] == 0) LineSize--;
+
+                                DosEchoCharacter(Character);
+                            }
+
+                            break;
+                        }
+
+                        default:
+                        {
+                            /* Store the character in the buffer */
+                            ConBuffer[LineSize++] = Character;
+                            DosEchoCharacter(Character);
+                        }
+                    }
+
+                    /* Stop on a carriage return */
+                    if (Character == '\r') break;
+                }
+            }
+
+            *BytesRead = 0;
+            ConBuffer = (PCHAR)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, SysVars->UnreadConInput);
+
+            while (*BytesRead < Count)
+            {
+                Pointer[(*BytesRead)++] = *ConBuffer;
+
+                if (*ConBuffer == '\r')
+                {
+                    /* A carriage return turns into a line feed */
+                    *ConBuffer = '\n';
+                }
+                else if (*ConBuffer == '\n')
+                {
+                    /* A line feed marks the true end of the line */
+                    SysVars->UnreadConInput = 0;
+                    break;
+                }
+                else
+                {
+                    /* Move to the next character */
+                    SysVars->UnreadConInput++;
+                    ConBuffer++;
+                }
+            }
+        }
+        else
+        {
+            /* Translated input from a character device that isn't CON */
+            PCHAR Pointer = FAR_POINTER(Buffer);
+
+            while (*BytesRead < Count)
+            {
+                USHORT Amount = 1;
+                CHAR Character;
+
+                /* Read a character from the device */
+                Node->ReadRoutine(Node,
+                                  MAKELONG(DOS_DATA_OFFSET(Sda.ByteBuffer),
+                                           DOS_DATA_SEGMENT),
+                                  &Amount);
+                if (Amount == 0) break;
+
+                Character = Sda->ByteBuffer;
+                // TODO: Process it somehow?
+
+                /* Store the character in the output buffer */
+                Pointer[(*BytesRead)++] = Character;
+
+                /* Check for EOF */
+                if (Character == 0x1A) break;
+            }
+        }
     }
     else
     {
index 1097eb6..4d7a651 100644 (file)
@@ -8,6 +8,9 @@
 
 /* DEFINES ********************************************************************/
 
+#define FILE_INFO_STDIN  (1 << 0)
+#define FILE_INFO_STDOUT (1 << 1)
+#define FILE_INFO_BINARY (1 << 5)
 #define FILE_INFO_DEVICE (1 << 7)
 
 #pragma pack(push, 1)