mirror of
https://github.com/reactos/reactos.git
synced 2024-08-10 05:08:11 +00:00
[NTVDM]
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
This commit is contained in:
parent
d8105a4366
commit
7f662fde76
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -691,10 +691,155 @@ WORD DosReadFile(WORD FileHandle,
|
|||
PDOS_DEVICE_NODE Node = DosGetDriverNode(Descriptor->DevicePointer);
|
||||
if (!Node->ReadRoutine) return ERROR_INVALID_FUNCTION;
|
||||
|
||||
/* Read the device */
|
||||
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
|
||||
{
|
||||
DWORD BytesRead32 = 0;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue