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:
Aleksandar Andrejevic 2015-09-25 22:00:57 +00:00
parent d8105a4366
commit 7f662fde76
6 changed files with 240 additions and 104 deletions

View 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;
}

View 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;

View 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)
{

View 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

View 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
{

View 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)