/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
- * FILE: dos.c
- * PURPOSE: VDM DOS Kernel
+ * FILE: dos/dos32krnl/dos.c
+ * PURPOSE: DOS32 Kernel
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#define NDEBUG
#include "emulator.h"
-#include "callback.h"
+#include "cpu/cpu.h"
+#include "int32.h"
#include "dos.h"
#include "dos/dem.h"
#include "bios/bios.h"
-#include "registers.h"
/* PRIVATE VARIABLES **********************************************************/
/*static*/ BYTE CurrentDrive;
static CHAR LastDrive = 'E';
static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static HANDLE DosSystemFileTable[DOS_SFT_SIZE];
-static WORD DosSftRefCount[DOS_SFT_SIZE];
+
+static struct
+{
+ HANDLE Handle;
+ WORD RefCount;
+} DosSystemFileTable[DOS_SFT_SIZE];
+
static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
static BOOLEAN DosUmbLinked = FALSE;
static WORD DosErrorLevel = 0x0000;
+/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
+BOOLEAN DoEcho = FALSE;
+
/* PRIVATE FUNCTIONS **********************************************************/
/*
/* Set the maximum possible size of the block */
ReturnSize += NextMcb->Size + 1;
+ if (ReturnSize < NewSize)
+ {
+ DPRINT("Cannot expand memory block: insufficient free segments available!\n");
+ DosLastError = ERROR_NOT_ENOUGH_MEMORY;
+ Success = FALSE;
+ goto Done;
+ }
+
/* Maximize the current block */
Mcb->Size = ReturnSize;
Mcb->BlockType = NextMcb->BlockType;
/* Add the string buffer size */
TotalSize += strlen(ProgramName) + 1;
+ /* Add the two extra bytes */
+ TotalSize += 2;
+
/* Allocate the memory for the environment block */
DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
if (!DestSegment) return 0;
/* Set the final zero */
*(DestBuffer++) = 0;
+ /* Store the special program name tag */
+ *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
+ *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
+
/* Copy the program name after the environment block */
strcpy(DestBuffer, ProgramName);
return DestSegment;
}
+
+
+
+
+
/* Taken from base/shell/cmd/console.c */
BOOL IsConsoleHandle(HANDLE hHandle)
{
return GetConsoleMode(hHandle, &dwMode);
}
-static WORD DosOpenHandle(HANDLE Handle)
+WORD DosOpenHandle(HANDLE Handle)
{
BYTE i;
WORD DosHandle;
for (i = 0; i < DOS_SFT_SIZE; i++)
{
/* Check if this is the same handle */
- if (DosSystemFileTable[i] != Handle) continue;
+ if (DosSystemFileTable[i].Handle != Handle) continue;
/* Already in the table, reference it */
- DosSftRefCount[i]++;
+ DosSystemFileTable[i].RefCount++;
/* Set the JFT entry to that SFT index */
HandleTable[DosHandle] = i;
for (i = 0; i < DOS_SFT_SIZE; i++)
{
/* Make sure this is an empty table entry */
- if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue;
+ if (DosSystemFileTable[i].Handle != INVALID_HANDLE_VALUE) continue;
/* Initialize the empty table entry */
- DosSystemFileTable[i] = Handle;
- DosSftRefCount[i] = 1;
+ DosSystemFileTable[i].Handle = Handle;
+ DosSystemFileTable[i].RefCount = 1;
/* Set the JFT entry to that SFT index */
HandleTable[DosHandle] = i;
if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
/* Return the Win32 handle */
- return DosSystemFileTable[HandleTable[DosHandle]];
+ return DosSystemFileTable[HandleTable[DosHandle]].Handle;
}
static VOID DosCopyHandleTable(LPBYTE DestinationTable)
DestinationTable[i] = (BYTE)i;
/* Increase the reference count */
- DosSftRefCount[i]++;
+ DosSystemFileTable[i].RefCount++;
}
/* Done */
DestinationTable[i] = SourceTable[i];
/* Increase the reference count */
- DosSftRefCount[SourceTable[i]]++;
+ DosSystemFileTable[SourceTable[i]].RefCount++;
+ }
+}
+
+static BOOLEAN DosResizeHandleTable(WORD NewSize)
+{
+ PDOS_PSP PspBlock;
+ LPBYTE HandleTable;
+ WORD Segment;
+
+ /* Get the PSP block */
+ PspBlock = SEGMENT_TO_PSP(CurrentPsp);
+
+ if (NewSize == PspBlock->HandleTableSize)
+ {
+ /* No change */
+ return TRUE;
}
+
+ if (PspBlock->HandleTableSize > 20)
+ {
+ /* Get the segment of the current table */
+ Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
+
+ if (NewSize <= 20)
+ {
+ /* Get the current handle table */
+ HandleTable = FAR_POINTER(PspBlock->HandleTablePtr);
+
+ /* Copy it to the PSP */
+ RtlCopyMemory(PspBlock->HandleTable, HandleTable, NewSize);
+
+ /* Free the memory */
+ DosFreeMemory(Segment);
+
+ /* Update the handle table pointer and size */
+ PspBlock->HandleTableSize = NewSize;
+ PspBlock->HandleTablePtr = MAKELONG(0x18, CurrentPsp);
+ }
+ else
+ {
+ /* Resize the memory */
+ if (!DosResizeMemory(Segment, NewSize, NULL))
+ {
+ /* Unable to resize, try allocating it somewhere else */
+ Segment = DosAllocateMemory(NewSize, NULL);
+ if (Segment == 0) return FALSE;
+
+ /* Get the new handle table */
+ HandleTable = SEG_OFF_TO_PTR(Segment, 0);
+
+ /* Copy the handles to the new table */
+ RtlCopyMemory(HandleTable,
+ FAR_POINTER(PspBlock->HandleTablePtr),
+ PspBlock->HandleTableSize);
+
+ /* Update the handle table pointer */
+ PspBlock->HandleTablePtr = MAKELONG(0, Segment);
+ }
+
+ /* Update the handle table size */
+ PspBlock->HandleTableSize = NewSize;
+ }
+ }
+ else if (NewSize > 20)
+ {
+ Segment = DosAllocateMemory(NewSize, NULL);
+ if (Segment == 0) return FALSE;
+
+ /* Get the new handle table */
+ HandleTable = SEG_OFF_TO_PTR(Segment, 0);
+
+ /* Copy the handles from the PSP to the new table */
+ RtlCopyMemory(HandleTable,
+ FAR_POINTER(PspBlock->HandleTablePtr),
+ PspBlock->HandleTableSize);
+
+ /* Update the handle table pointer and size */
+ PspBlock->HandleTableSize = NewSize;
+ PspBlock->HandleTablePtr = MAKELONG(0, Segment);
+ }
+
+ return TRUE;
}
static BOOLEAN DosCloseHandle(WORD DosHandle)
/* Decrement the reference count of the SFT entry */
SftIndex = HandleTable[DosHandle];
- DosSftRefCount[SftIndex]--;
+ DosSystemFileTable[SftIndex].RefCount--;
/* Check if the reference count fell to zero */
- if (!DosSftRefCount[SftIndex])
+ if (!DosSystemFileTable[SftIndex].RefCount)
{
/* Close the file, it's no longer needed */
- CloseHandle(DosSystemFileTable[SftIndex]);
+ CloseHandle(DosSystemFileTable[SftIndex].Handle);
/* Clear the handle */
- DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
+ DosSystemFileTable[SftIndex].Handle = INVALID_HANDLE_VALUE;
}
/* Clear the entry in the JFT */
/* Increment the reference count of the SFT entry */
SftIndex = HandleTable[OldHandle];
- DosSftRefCount[SftIndex]++;
+ DosSystemFileTable[SftIndex].RefCount++;
/* Make the new handle point to that SFT entry */
HandleTable[NewHandle] = SftIndex;
return TRUE;
}
-static WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
-{
- HANDLE FileHandle;
- WORD DosHandle;
-
- DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
- FilePath,
- Attributes);
-
- /* Create the file */
- FileHandle = CreateFileA(FilePath,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- CREATE_ALWAYS,
- Attributes,
- NULL);
-
- if (FileHandle == INVALID_HANDLE_VALUE)
- {
- /* Return the error code */
- return (WORD)GetLastError();
- }
-
- /* Open the DOS handle */
- DosHandle = DosOpenHandle(FileHandle);
-
- if (DosHandle == INVALID_DOS_HANDLE)
- {
- /* Close the handle */
- CloseHandle(FileHandle);
-
- /* Return the error code */
- return ERROR_TOO_MANY_OPEN_FILES;
- }
-
- /* It was successful */
- *Handle = DosHandle;
- return ERROR_SUCCESS;
-}
-
-static WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
-{
- HANDLE FileHandle;
- ACCESS_MASK Access = 0;
- WORD DosHandle;
-
- DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
- FilePath,
- AccessMode);
-
- /* Parse the access mode */
- switch (AccessMode & 3)
- {
- case 0:
- {
- /* Read-only */
- Access = GENERIC_READ;
- break;
- }
-
- case 1:
- {
- /* Write only */
- Access = GENERIC_WRITE;
- break;
- }
- case 2:
- {
- /* Read and write */
- Access = GENERIC_READ | GENERIC_WRITE;
- break;
- }
- default:
- {
- /* Invalid */
- return ERROR_INVALID_PARAMETER;
- }
- }
- /* Open the file */
- FileHandle = CreateFileA(FilePath,
- Access,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (FileHandle == INVALID_HANDLE_VALUE)
- {
- /* Return the error code */
- return (WORD)GetLastError();
- }
- /* Open the DOS handle */
- DosHandle = DosOpenHandle(FileHandle);
-
- if (DosHandle == INVALID_DOS_HANDLE)
- {
- /* Close the handle */
- CloseHandle(FileHandle);
-
- /* Return the error code */
- return ERROR_TOO_MANY_OPEN_FILES;
- }
-
- /* It was successful */
- *Handle = DosHandle;
- return ERROR_SUCCESS;
-}
-
-WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
-{
- WORD Result = ERROR_SUCCESS;
- DWORD BytesRead32 = 0;
- HANDLE Handle = DosGetRealHandle(FileHandle);
-
- DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
-
- /* Make sure the handle is valid */
- if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
- /* Read the file */
- if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
- {
- /* Store the error code */
- Result = (WORD)GetLastError();
- }
-
- /* The number of bytes read is always 16-bit */
- *BytesRead = LOWORD(BytesRead32);
-
- /* Return the error code */
- return Result;
-}
-
-WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten)
-{
- WORD Result = ERROR_SUCCESS;
- DWORD BytesWritten32 = 0;
- HANDLE Handle = DosGetRealHandle(FileHandle);
- WORD i;
-
- DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
- FileHandle,
- Count);
-
- /* Make sure the handle is valid */
- if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
- if (IsConsoleHandle(Handle))
- {
- for (i = 0; i < Count; i++)
- {
- /* Save AX and BX */
- USHORT AX = getAX();
- USHORT BX = getBX();
-
- /* Set the parameters */
- setAL(((PCHAR)Buffer)[i]);
- setBL(DOS_CHAR_ATTRIBUTE);
- setBH(Bda->VideoPage);
-
- /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
- setAH(0x0E);
- Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
-
- /* Restore AX and BX */
- setBX(BX);
- setAX(AX);
-
- BytesWritten32++;
- }
- }
- else
- {
- /* Write the file */
- if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
- {
- /* Store the error code */
- Result = (WORD)GetLastError();
- }
- }
-
- /* The number of bytes written is always 16-bit */
- *BytesWritten = LOWORD(BytesWritten32);
-
- /* Return the error code */
- return Result;
-}
-
-static WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset)
-{
- WORD Result = ERROR_SUCCESS;
- DWORD FilePointer;
- HANDLE Handle = DosGetRealHandle(FileHandle);
-
- DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
- FileHandle,
- Offset,
- Origin);
-
- /* Make sure the handle is valid */
- if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
- /* Check if the origin is valid */
- if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END)
- {
- return ERROR_INVALID_FUNCTION;
- }
-
- /* Move the file pointer */
- FilePointer = SetFilePointer(Handle, Offset, NULL, Origin);
-
- /* Check if there's a possibility the operation failed */
- if (FilePointer == INVALID_SET_FILE_POINTER)
- {
- /* Get the real error code */
- Result = (WORD)GetLastError();
- }
-
- if (Result != ERROR_SUCCESS)
- {
- /* The operation did fail */
- return Result;
- }
-
- /* Return the file pointer, if requested */
- if (NewOffset) *NewOffset = FilePointer;
-
- /* Return success */
- return ERROR_SUCCESS;
-}
-
-static BOOLEAN DosFlushFileBuffers(WORD FileHandle)
-{
- HANDLE Handle = DosGetRealHandle(FileHandle);
-
- /* Make sure the handle is valid */
- if (Handle == INVALID_HANDLE_VALUE) return FALSE;
-
- /*
- * No need to check whether the handle is a console handle since
- * FlushFileBuffers() automatically does this check and calls
- * FlushConsoleInputBuffer() for us.
- */
- // if (IsConsoleHandle(Handle))
- // return (BOOLEAN)FlushConsoleInputBuffer(Handle);
- // else
- return (BOOLEAN)FlushFileBuffers(Handle);
-}
static BOOLEAN DosChangeDrive(BYTE Drive)
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
- ZeroMemory(PspBlock, sizeof(DOS_PSP));
+ RtlZeroMemory(PspBlock, sizeof(*PspBlock));
/* Set the exit interrupt */
PspBlock->Exit[0] = 0xCD; // int 0x20
PIMAGE_DOS_HEADER Header;
PDWORD RelocationTable;
PWORD RelocWord;
+ LPSTR CmdLinePtr = (LPSTR)CommandLine;
DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
LoadType,
return ERROR_NOT_SUPPORTED;
}
+ /* NULL-terminate the command line by removing the return carriage character */
+ while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
+ *CmdLinePtr = '\0';
+
/* Open a handle to the executable */
FileHandle = CreateFileA(ExecutablePath,
GENERIC_READ,
/* Check if at least the lowest allocation was successful */
if (Segment == 0)
{
- Result = ERROR_NOT_ENOUGH_MEMORY;
+ Result = DosLastError;
goto Cleanup;
}
setES(Segment);
/* Set the stack to the location from the header */
- EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
- Header->e_sp);
+ setSS(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss);
+ setSP(Header->e_sp);
/* Execute */
CurrentPsp = Segment;
DiskTransferArea = MAKELONG(0x80, Segment);
- EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
- Header->e_ip);
+ CpuExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
+ Header->e_ip);
}
}
else
Segment = DosAllocateMemory(MaxAllocSize, NULL);
if (Segment == 0)
{
- Result = ERROR_ARENA_TRASHED;
+ Result = DosLastError;
goto Cleanup;
}
setES(Segment);
/* Set the stack to the last word of the segment */
- EmulatorSetStack(Segment, 0xFFFE);
+ setSS(Segment);
+ setSP(0xFFFE);
/*
* Set the value on the stack to 0, so that a near return
/* Execute */
CurrentPsp = Segment;
DiskTransferArea = MAKELONG(0x80, Segment);
- EmulatorExecute(Segment, 0x100);
+ CpuExecute(Segment, 0x100);
}
}
return Result;
}
+DWORD DosStartProcess(IN LPCSTR ExecutablePath,
+ IN LPCSTR CommandLine,
+ IN PVOID Environment)
+{
+ DWORD Result;
+
+ Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+ ExecutablePath,
+ CommandLine,
+ Environment,
+ NULL,
+ NULL);
+
+ if (Result != ERROR_SUCCESS) goto Quit;
+
+ /* Attach to the console */
+ VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
+
+ /* Start simulation */
+ SetEvent(VdmTaskEvent);
+ CpuSimulate();
+
+ /* Detach from the console */
+ VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
+
+Quit:
+ return Result;
+}
+
+#ifndef STANDALONE
WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
LPCSTR ProgramName,
PDOS_EXEC_PARAM_BLOCK Parameters)
}
/* Set up the startup info structure */
- ZeroMemory(&StartupInfo, sizeof(STARTUPINFOA));
- StartupInfo.cb = sizeof(STARTUPINFOA);
+ RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
/* Create the process */
if (!CreateProcessA(ProgramName,
case SCS_WOW_BINARY:
{
/* Clear the structure */
- ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Initialize the structure members */
CommandInfo.TaskId = SessionId;
GetNextVDMCommand(&CommandInfo);
/* Load the executable */
- Result= DosLoadExecutable(LoadType,
- AppName,
- CmdLine,
- Env,
- &Parameters->StackLocation,
- &Parameters->EntryPoint);
+ Result = DosLoadExecutable(LoadType,
+ AppName,
+ CmdLine,
+ Env,
+ &Parameters->StackLocation,
+ &Parameters->EntryPoint);
if (Result != ERROR_SUCCESS)
{
DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
- break;
+ // FIXME: Decrement the reenter count. Or, instead, just increment
+ // the VDM reenter count *only* if this call succeeds...
}
break;
return ERROR_SUCCESS;
}
+#endif
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
{
PDOS_MCB CurrentMcb;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
- VDM_COMMAND_INFO CommandInfo;
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
Psp,
if (CurrentPsp == SYSTEM_PSP)
{
ResetEvent(VdmTaskEvent);
- EmulatorUnsimulate();
+ CpuUnsimulate();
}
}
+#ifndef STANDALONE
// FIXME: This is probably not the best way to do it
/* Check if this was a nested DOS task */
if (CurrentPsp != SYSTEM_PSP)
{
+ VDM_COMMAND_INFO CommandInfo;
+
/* Decrement the re-entry count */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
GetNextVDMCommand(&CommandInfo);
/* Clear the structure */
- ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Update the VDM state of the task */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
GetNextVDMCommand(&CommandInfo);
}
+#endif
/* Save the return code - Normal termination */
DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
/* Return control to the parent process */
- EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
- LOWORD(PspBlock->TerminateAddress));
+ CpuExecute(HIWORD(PspBlock->TerminateAddress),
+ LOWORD(PspBlock->TerminateAddress));
}
BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
* for a list of possible flags.
*/
- if (Handle == DosSystemFileTable[0])
+ if (Handle == DosSystemFileTable[DOS_INPUT_HANDLE].Handle)
{
/* Console input */
InfoWord |= 1 << 0;
+
+ /* It is a device */
+ InfoWord |= 1 << 7;
}
- else if (Handle == DosSystemFileTable[1])
+ else if (Handle == DosSystemFileTable[DOS_OUTPUT_HANDLE].Handle)
{
/* Console output */
InfoWord |= 1 << 1;
- }
- /* It is a device */
- InfoWord |= 1 << 7;
+ /* It is a device */
+ InfoWord |= 1 << 7;
+ }
/* Return the device information word */
setDX(InfoWord);
/* Read Character from STDIN with Echo */
case 0x01:
{
- Character = DosReadCharacter();
- DosPrintCharacter(Character);
+ 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;
- /* Let the BOP repeat if needed */
- if (getCF()) break;
+ // 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.
setAL(Character);
break;
/* Write Character to STDOUT */
case 0x02:
{
+ // FIXME: Under DOS 2+, output handle may be redirected!!!!
Character = getDL();
- DosPrintCharacter(Character);
+ DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
/*
* We return the output character (DOS 2.1+).
{
// FIXME: Really read it from STDAUX!
DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
- setAL(DosReadCharacter());
+ // setAL(DosReadCharacter());
break;
}
{
// FIXME: Really write it to STDAUX!
DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
- DosPrintCharacter(getDL());
+ // DosPrintCharacter(getDL());
break;
}
{
Character = getDL();
+ // FIXME: Under DOS 2+, output handle may be redirected!!!!
+
if (Character != 0xFF)
{
/* Output */
- DosPrintCharacter(Character);
+ DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
/*
* We return the output character (DOS 2.1+).
if (DosCheckInput())
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
- setAL(DosReadCharacter());
+ setAL(DosReadCharacter(DOS_INPUT_HANDLE));
}
else
{
case 0x07:
case 0x08:
{
- Character = DosReadCharacter();
+ DPRINT("Char input without echo\n");
+
+ // FIXME: Under DOS 2+, input handle may be redirected!!!!
+ 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.
- /* Let the BOP repeat if needed */
- if (getCF()) break;
+ // /* Let the BOP repeat if needed */
+ // if (getCF()) break;
setAL(Character);
break;
while (*String != '$')
{
- DosPrintCharacter(*String);
+ DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
String++;
}
* See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
* for more information.
*/
- setAL('$');
+ setAL('$'); // *String
break;
}
/* Read Buffered Input */
case 0x0A:
{
+ WORD Count = 0;
InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
- while (Stack[STACK_COUNTER] < InputBuffer->MaxLength)
+ DPRINT("Read Buffered Input\n");
+
+ while (Count < InputBuffer->MaxLength)
{
- /* Try to read a character */
- Character = DosReadCharacter();
+ // FIXME!! This function should interpret backspaces etc...
+
+ /* Try to read a character (wait) */
+ Character = DosReadCharacter(DOS_INPUT_HANDLE);
- /* If it's not ready yet, let the BOP repeat */
- if (getCF()) break;
+ // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
/* Echo the character and append it to the buffer */
- DosPrintCharacter(Character);
- InputBuffer->Buffer[Stack[STACK_COUNTER]] = Character;
+ DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+ InputBuffer->Buffer[Count] = Character;
+
+ Count++; /* Carriage returns are also counted */
if (Character == '\r') break;
- Stack[STACK_COUNTER]++;
}
/* Update the length */
- InputBuffer->Length = Stack[STACK_COUNTER];
+ InputBuffer->Length = Count;
+
break;
}
BYTE InputFunction = getAL();
/* Flush STDIN buffer */
- DosFlushFileBuffers(DOS_INPUT_HANDLE); // Maybe just create a DosFlushInputBuffer...
+ DosFlushFileBuffers(DOS_INPUT_HANDLE);
/*
* If the input function number contained in AL is valid, i.e.
InputFunction == 0x07 || InputFunction == 0x08 ||
InputFunction == 0x0A)
{
+ /* Call ourselves recursively */
setAH(InputFunction);
- /*
- * Instead of calling ourselves really recursively as in:
- * DosInt21h(Stack);
- * prefer resetting the CF flag to let the BOP repeat.
- */
- setCF(1);
+ DosInt21h(Stack);
}
break;
}
case 0x25:
{
ULONG FarPointer = MAKELONG(getDX(), getDS());
- DPRINT1("Setting interrupt 0x%x ...\n", getAL());
+ DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
+ getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
/* Write the new far pointer to the IDT */
((PULONG)BaseAddress)[getAL()] = FarPointer;
/* Create New PSP */
case 0x26:
{
- DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n");
+ DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
break;
}
* Return DOS OEM number:
* 0x00 for IBM PC-DOS
* 0x02 for packaged MS-DOS
+ * 0xFF for NT DOS
*/
- setBH(0x02);
+ setBH(0xFF);
}
if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
break;
}
- /* Create File */
+ /* Create or Truncate File */
case 0x3C:
{
WORD FileHandle;
WORD ErrorCode = DosCreateFile(&FileHandle,
(LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+ CREATE_ALWAYS,
getCX());
- if (ErrorCode == 0)
+ if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
setAX(FileHandle);
(LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
getAL());
- if (ErrorCode == 0)
+ if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
setAX(FileHandle);
/* Read from File or Device */
case 0x3F:
{
- WORD Handle = getBX();
- LPBYTE Buffer = (LPBYTE)SEG_OFF_TO_PTR(getDS(), getDX());
- WORD Count = getCX();
WORD BytesRead = 0;
- WORD ErrorCode = ERROR_SUCCESS;
- CHAR Character;
+ WORD ErrorCode;
- if (IsConsoleHandle(DosGetRealHandle(Handle)))
- {
- while (Stack[STACK_COUNTER] < Count)
- {
- /* Read a character from the BIOS */
- Character = LOBYTE(BiosGetCharacter());
-
- /* Stop if the BOP needs to be repeated */
- if (getCF()) break;
-
- // FIXME: Security checks!
- DosPrintCharacter(Character);
- Buffer[Stack[STACK_COUNTER]++] = Character;
-
- if (Character == '\r')
- {
- /* Stop on first carriage return */
- DosPrintCharacter('\n');
- break;
- }
- }
+ DPRINT("INT 21h, AH = 3Fh\n");
- if (Character != '\r')
- {
- if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
- else BytesRead = Count;
- }
- else BytesRead = Stack[STACK_COUNTER];
- }
- else
- {
- /* Use the file reading function */
- ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead);
- }
+ DoEcho = TRUE;
+ ErrorCode = DosReadFile(getBX(),
+ SEG_OFF_TO_PTR(getDS(), getDX()),
+ getCX(),
+ &BytesRead);
+ DoEcho = FALSE;
if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
setAX(ErrorCode);
}
+
break;
}
WORD NewHandle;
HANDLE Handle = DosGetRealHandle(getBX());
- if (Handle != INVALID_HANDLE_VALUE)
+ if (Handle == INVALID_HANDLE_VALUE)
{
/* The handle is invalid */
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
+#ifndef STANDALONE
/* Execute */
case 0x4B:
{
break;
}
+#endif
/* Terminate With Return Code */
case 0x4C:
getCX());
setAX(Result);
- if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
- else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+ if (Result == ERROR_SUCCESS)
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ else
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
setAX(Result);
- if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
- else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+ if (Result == ERROR_SUCCESS)
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ else
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
break;
}
break;
}
+ /* Internal - Get "List of lists" (SYSVARS) */
+ case 0x52:
+ {
+ /*
+ * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
+ * for more information.
+ */
+
+ /* Return the DOS "list of lists" in ES:BX */
+ setES(0x0000);
+ setBX(0x0000);
+
+ DisplayMessage(L"Required for AARD code, do you remember? :P");
+ break;
+ }
+
+ /* Rename File */
+ case 0x56:
+ {
+ LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
+ LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
+
+ /*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
+ * for more information.
+ */
+
+ if (MoveFileA(ExistingFileName, NewFileName))
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ }
+
+ break;
+ }
+
/* Get/Set Memory Management Options */
case 0x58:
{
break;
}
+ /* Get Extended Error Information */
+ case 0x59:
+ {
+ DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
+ getBX());
+ break;
+ }
+
+ /* Create Temporary File */
+ case 0x5A:
+ {
+ LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
+ LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
+ UINT uRetVal;
+ WORD FileHandle;
+ WORD ErrorCode;
+
+ /*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
+ * for more information.
+ */
+
+ // FIXME: Check for buffer validity?
+ // It should be a ASCIZ path ending with a '\' + 13 zero bytes
+ // to receive the generated filename.
+
+ /* First create the temporary file */
+ uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
+ if (uRetVal == 0)
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ break;
+ }
+
+ /* Now try to open it in read/write access */
+ ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
+ if (ErrorCode == ERROR_SUCCESS)
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(FileHandle);
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ErrorCode);
+ }
+
+ break;
+ }
+
+ /* Create New File */
+ case 0x5B:
+ {
+ WORD FileHandle;
+ WORD ErrorCode = DosCreateFile(&FileHandle,
+ (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+ CREATE_NEW,
+ getCX());
+
+ if (ErrorCode == ERROR_SUCCESS)
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(FileHandle);
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ErrorCode);
+ }
+
+ break;
+ }
+
+ /* Lock/Unlock Region of File */
+ case 0x5C:
+ {
+ HANDLE Handle = DosGetRealHandle(getBX());
+
+ if (Handle == INVALID_HANDLE_VALUE)
+ {
+ /* The handle is invalid */
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ERROR_INVALID_HANDLE);
+ break;
+ }
+
+ if (getAL() == 0x00)
+ {
+ /* Lock region of file */
+ if (LockFile(Handle,
+ MAKELONG(getCX(), getDX()), 0,
+ MAKELONG(getSI(), getDI()), 0))
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ }
+ }
+ else if (getAL() == 0x01)
+ {
+ /* Unlock region of file */
+ if (UnlockFile(Handle,
+ MAKELONG(getCX(), getDX()), 0,
+ MAKELONG(getSI(), getDI()), 0))
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ }
+ }
+ else
+ {
+ /* Invalid subfunction */
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ERROR_INVALID_FUNCTION);
+ }
+
+ break;
+ }
+
+ /* Canonicalize File Name or Path */
+ case 0x60:
+ {
+ /*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
+ * for more information.
+ */
+
+ /*
+ * We suppose that the DOS app gave to us a valid
+ * 128-byte long buffer for the canonicalized name.
+ */
+ DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
+ 128,
+ SEG_OFF_TO_PTR(getES(), getDI()),
+ NULL);
+ if (dwRetVal == 0)
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ }
+ else
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(0x0000);
+ }
+
+ // FIXME: Convert the full path name into short version.
+ // We cannot reliably use GetShortPathName, because it fails
+ // if the path name given doesn't exist. However this DOS
+ // function AH=60h should be able to work even for non-existing
+ // path and file names.
+
+ break;
+ }
+
+ /* Set Handle Count */
+ case 0x67:
+ {
+ if (!DosResizeHandleTable(getBX()))
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(DosLastError);
+ }
+ else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+ break;
+ }
+
+ /* Commit File */
+ case 0x68:
+ case 0x6A:
+ {
+ /*
+ * Function 6Ah is identical to function 68h,
+ * and sets AH to 68h if success.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
+ * for more information.
+ */
+ setAH(0x68);
+
+ if (DosFlushFileBuffers(getBX()))
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(GetLastError());
+ }
+
+ break;
+ }
+
+ /* Extended Open/Create */
+ case 0x6C:
+ {
+ WORD FileHandle;
+ WORD CreationStatus;
+ WORD ErrorCode;
+
+ /* Check for AL == 00 */
+ if (getAL() != 0x00)
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ERROR_INVALID_FUNCTION);
+ break;
+ }
+
+ /*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
+ * for the full detailed description.
+ *
+ * WARNING: BH contains some extended flags that are NOT SUPPORTED.
+ */
+
+ ErrorCode = DosCreateFileEx(&FileHandle,
+ &CreationStatus,
+ (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
+ getBL(),
+ getDL(),
+ getCX());
+
+ if (ErrorCode == ERROR_SUCCESS)
+ {
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setCX(CreationStatus);
+ setAX(FileHandle);
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ErrorCode);
+ }
+
+ break;
+ }
+
/* Unsupported */
default:
{
/* Stop the VDM task */
ResetEvent(VdmTaskEvent);
- EmulatorUnsimulate();
+ CpuUnsimulate();
}
VOID WINAPI DosFastConOut(LPWORD Stack)
{
/*
* This is the DOS 2+ Fast Console Output Interrupt.
+ * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
+ *
* See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
* for more information.
*/
-#if 0
- if (Stack[STACK_COUNTER] == 0)
- {
- Stack[STACK_COUNTER]++;
-
- /* Save AX and BX */
- Stack[STACK_VAR_A] = getAX();
- Stack[STACK_VAR_B] = getBX();
-
- /* Rewind the BOP manually, we can't use CF because the interrupt could modify it */
- EmulatorExecute(getCS(), getIP() - 4);
-
- /* Call INT 0x10, AH = 0x0E */
- setAH(0x0E);
- setBL(DOS_CHAR_ATTRIBUTE);
- setBH(Bda->VideoPage);
-
- EmulatorInterrupt(0x10);
- }
- else
- {
- /* Restore AX and BX */
- setAX(Stack[STACK_VAR_A]);
- setBX(Stack[STACK_VAR_B]);
- }
-#else
/* Save AX and BX */
USHORT AX = getAX();
USHORT BX = getBX();
- /* Set the parameters (AL = character, already set) */
+ /*
+ * Set the parameters:
+ * AL contains the character to print (already set),
+ * BL contains the character attribute,
+ * BH contains the video page to use.
+ */
setBL(DOS_CHAR_ATTRIBUTE);
setBH(Bda->VideoPage);
/* Restore AX and BX */
setBX(BX);
setAX(AX);
-#endif
}
VOID WINAPI DosInt2Fh(LPWORD Stack)
{
- DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+ DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
getAH(), getAL());
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
}
WCHAR Buffer[256];
/* Clear the current directory buffer */
- ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
+ RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
/* Get the current directory */
if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
/* Initialize the SFT */
for (i = 0; i < DOS_SFT_SIZE; i++)
{
- DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
- DosSftRefCount[i] = 0;
+ DosSystemFileTable[i].Handle = INVALID_HANDLE_VALUE;
+ DosSystemFileTable[i].RefCount = 0;
}
/* Get handles to standard I/O devices */
- DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
- DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
- DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
+ DosSystemFileTable[0].Handle = GetStdHandle(STD_INPUT_HANDLE);
+ DosSystemFileTable[1].Handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ DosSystemFileTable[2].Handle = GetStdHandle(STD_ERROR_HANDLE);
/* Initialize the reference counts */
- DosSftRefCount[0] = DosSftRefCount[1] = DosSftRefCount[2] = 1;
+ DosSystemFileTable[0].RefCount =
+ DosSystemFileTable[1].RefCount =
+ DosSystemFileTable[2].RefCount = 1;
#endif