#define NDEBUG
+#include "ntvdm.h"
#include "emulator.h"
#include "cpu/cpu.h"
#include "int32.h"
#include "dos.h"
#include "dos/dem.h"
#include "device.h"
+#include "handle.h"
+#include "dosfiles.h"
#include "memory.h"
+#include "process.h"
+#include "himem.h"
#include "bios/bios.h"
#include "io.h"
#include "hardware/ps2.h"
+#include "emsdrv.h"
+
/* PRIVATE VARIABLES **********************************************************/
+#define INDOS_POINTER MAKELONG(0x00FE, 0x0070)
+
CALLBACK16 DosContext;
-static DWORD DiskTransferArea;
/*static*/ BYTE CurrentDrive;
static CHAR LastDrive = 'E';
static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static DOS_SFT_ENTRY DosSystemFileTable[DOS_SFT_SIZE];
-static WORD DosErrorLevel = 0x0000;
+static PBYTE InDos;
/* PUBLIC VARIABLES ***********************************************************/
+PDOS_SYSVARS SysVars;
+
/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
BOOLEAN DoEcho = FALSE;
-WORD CurrentPsp = SYSTEM_PSP;
+
+DWORD DiskTransferArea;
+WORD DosErrorLevel = 0x0000;
WORD DosLastError = 0;
/* PRIVATE FUNCTIONS **********************************************************/
-static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
- LPCSTR ProgramName)
-{
- PCHAR Ptr, DestBuffer = NULL;
- ULONG TotalSize = 0;
- WORD DestSegment;
-
- /* If we have an environment strings list, compute its size */
- if (Environment)
- {
- /* Calculate the size of the environment block */
- Ptr = (PCHAR)Environment;
- while (*Ptr) Ptr += strlen(Ptr) + 1;
- TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
- }
- else
- {
- /* Empty environment string */
- TotalSize = 1;
- }
- /* Add the final environment block NULL-terminator */
- TotalSize++;
-
- /* Add the two bytes for the program name tag */
- TotalSize += 2;
-
- /* Add the string buffer size */
- TotalSize += strlen(ProgramName) + 1;
-
- /* Allocate the memory for the environment block */
- DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
- if (!DestSegment) return 0;
-
- DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
-
- /* If we have an environment strings list, copy it */
- if (Environment)
- {
- Ptr = (PCHAR)Environment;
- while (*Ptr)
- {
- /* Copy the string and NULL-terminate it */
- strcpy(DestBuffer, Ptr);
- DestBuffer += strlen(Ptr);
- *(DestBuffer++) = '\0';
-
- /* Move to the next string */
- Ptr += strlen(Ptr) + 1;
- }
- }
- else
- {
- /* Empty environment string */
- *(DestBuffer++) = '\0';
- }
- /* NULL-terminate the environment block */
- *(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 */
-static BOOL IsConsoleHandle(HANDLE hHandle)
-{
- DWORD dwMode;
-
- /* Check whether the handle may be that of a console... */
- if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE;
-
- /*
- * It may be. Perform another test... The idea comes from the
- * MSDN description of the WriteConsole API:
- *
- * "WriteConsole fails if it is used with a standard handle
- * that is redirected to a file. If an application processes
- * multilingual output that can be redirected, determine whether
- * the output handle is a console handle (one method is to call
- * the GetConsoleMode function and check whether it succeeds).
- * If the handle is a console handle, call WriteConsole. If the
- * handle is not a console handle, the output is redirected and
- * you should call WriteFile to perform the I/O."
- */
- return GetConsoleMode(hHandle, &dwMode);
-}
-
-static inline PDOS_SFT_ENTRY DosFindFreeSftEntry(VOID)
-{
- UINT i;
-
- for (i = 0; i < DOS_SFT_SIZE; i++)
- {
- if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_NONE)
- {
- return &DosSystemFileTable[i];
- }
- }
-
- return NULL;
-}
-
-static inline PDOS_SFT_ENTRY DosFindWin32SftEntry(HANDLE Handle)
-{
- UINT i;
-
- for (i = 0; i < DOS_SFT_SIZE; i++)
- {
- if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_WIN32
- && DosSystemFileTable[i].Handle == Handle)
- {
- return &DosSystemFileTable[i];
- }
- }
-
- return NULL;
-}
-
-static inline PDOS_SFT_ENTRY DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device)
-{
- UINT i;
-
- for (i = 0; i < DOS_SFT_SIZE; i++)
- {
- if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_DEVICE
- && DosSystemFileTable[i].DeviceNode == Device)
- {
- return &DosSystemFileTable[i];
- }
- }
-
- return NULL;
-}
-
-WORD DosOpenHandle(HANDLE Handle)
-{
- WORD DosHandle;
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
- PDOS_SFT_ENTRY SftEntry;
-
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
-
- /* Get a pointer to the handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Find a free entry in the JFT */
- for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
- {
- if (HandleTable[DosHandle] == 0xFF) break;
- }
-
- /* If there are no free entries, fail */
- if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
-
- /* Check if the handle is already in the SFT */
- SftEntry = DosFindWin32SftEntry(Handle);
- if (SftEntry != NULL)
- {
- /* Already in the table, reference it */
- SftEntry->RefCount++;
- goto Finish;
- }
-
- /* Find a free SFT entry to use */
- SftEntry = DosFindFreeSftEntry();
- if (SftEntry == NULL)
- {
- /* The SFT is full */
- return INVALID_DOS_HANDLE;
- }
-
- /* Initialize the empty table entry */
- SftEntry->Type = DOS_SFT_ENTRY_WIN32;
- SftEntry->Handle = Handle;
- SftEntry->RefCount = 1;
-
-Finish:
-
- /* Set the JFT entry to that SFT index */
- HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
-
- /* Return the new handle */
- return DosHandle;
-}
-
-WORD DosOpenDevice(PDOS_DEVICE_NODE Device)
-{
- WORD DosHandle;
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
- PDOS_SFT_ENTRY SftEntry;
-
- DPRINT("DosOpenDevice(\"%Z\")\n", &Device->Name);
-
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
-
- /* Get a pointer to the handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Find a free entry in the JFT */
- for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
- {
- if (HandleTable[DosHandle] == 0xFF) break;
- }
-
- /* If there are no free entries, fail */
- if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
-
- /* Check if the device is already in the SFT */
- SftEntry = DosFindDeviceSftEntry(Device);
- if (SftEntry != NULL)
- {
- /* Already in the table, reference it */
- SftEntry->RefCount++;
- goto Finish;
- }
-
- /* Find a free SFT entry to use */
- SftEntry = DosFindFreeSftEntry();
- if (SftEntry == NULL)
- {
- /* The SFT is full */
- return INVALID_DOS_HANDLE;
- }
-
- /* Initialize the empty table entry */
- SftEntry->Type = DOS_SFT_ENTRY_DEVICE;
- SftEntry->DeviceNode = Device;
- SftEntry->RefCount = 1;
-
-Finish:
-
- /* Call the open routine, if it exists */
- if (Device->OpenRoutine) Device->OpenRoutine(Device);
-
- /* Set the JFT entry to that SFT index */
- HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
-
- /* Return the new handle */
- return DosHandle;
-}
-
-static VOID DosCopyHandleTable(LPBYTE DestinationTable)
-{
- UINT i;
- PDOS_PSP PspBlock;
- LPBYTE SourceTable;
-
- /* Clear the table first */
- for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
-
- /* Check if this is the initial process */
- if (CurrentPsp == SYSTEM_PSP)
- {
- PDOS_SFT_ENTRY SftEntry;
- HANDLE StandardHandles[3];
- PDOS_DEVICE_NODE Con = DosGetDevice("CON");
- ASSERT(Con != NULL);
-
- /* Get the native standard handles */
- StandardHandles[0] = GetStdHandle(STD_INPUT_HANDLE);
- StandardHandles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
- StandardHandles[2] = GetStdHandle(STD_ERROR_HANDLE);
-
- for (i = 0; i < 3; i++)
- {
- /* Find the corresponding SFT entry */
- if (IsConsoleHandle(StandardHandles[i]))
- {
- SftEntry = DosFindDeviceSftEntry(Con);
- }
- else
- {
- SftEntry = DosFindWin32SftEntry(StandardHandles[i]);
- }
-
- if (SftEntry == NULL)
- {
- /* Create a new SFT entry for it */
- SftEntry = DosFindFreeSftEntry();
- if (SftEntry == NULL)
- {
- DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i);
- continue;
- }
-
- SftEntry->RefCount = 0;
-
- if (IsConsoleHandle(StandardHandles[i]))
- {
- SftEntry->Type = DOS_SFT_ENTRY_DEVICE;
- SftEntry->DeviceNode = Con;
-
- /* Call the open routine */
- if (Con->OpenRoutine) Con->OpenRoutine(Con);
- }
- else
- {
- SftEntry->Type = DOS_SFT_ENTRY_WIN32;
- SftEntry->Handle = StandardHandles[i];
- }
- }
-
- SftEntry->RefCount++;
- DestinationTable[i] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
- }
- }
- else
- {
- /* Get the parent PSP block and handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Copy the first 20 handles into the new table */
- for (i = 0; i < DEFAULT_JFT_SIZE; i++)
- {
- DestinationTable[i] = SourceTable[i];
-
- /* Increase the reference count */
- 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 > DEFAULT_JFT_SIZE)
- {
- /* Get the segment of the current table */
- Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
-
- if (NewSize <= DEFAULT_JFT_SIZE)
- {
- /* 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 > DEFAULT_JFT_SIZE)
- {
- 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)
-{
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
- PDOS_SFT_ENTRY SftEntry;
-
- DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
-
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return FALSE;
-
- /* Get a pointer to the handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Make sure the handle is open */
- if (HandleTable[DosHandle] == 0xFF) return FALSE;
-
- /* Make sure the SFT entry is valid */
- SftEntry = &DosSystemFileTable[HandleTable[DosHandle]];
- if (SftEntry->Type == DOS_SFT_ENTRY_NONE) return FALSE;
-
- /* Decrement the reference count of the SFT entry */
- SftEntry->RefCount--;
-
- /* Check if the reference count fell to zero */
- if (!SftEntry->RefCount)
- {
- switch (SftEntry->Type)
- {
- case DOS_SFT_ENTRY_WIN32:
- {
- /* Close the win32 handle and clear it */
- CloseHandle(SftEntry->Handle);
-
- break;
- }
-
- case DOS_SFT_ENTRY_DEVICE:
- {
- PDOS_DEVICE_NODE Node = SftEntry->DeviceNode;
-
- /* Call the close routine, if it exists */
- if (Node->CloseRoutine) SftEntry->DeviceNode->CloseRoutine(SftEntry->DeviceNode);
-
- break;
- }
-
- default:
- {
- /* Shouldn't happen */
- ASSERT(FALSE);
- }
- }
-
- /* Invalidate the SFT entry */
- SftEntry->Type = DOS_SFT_ENTRY_NONE;
- }
-
- /* Clear the entry in the JFT */
- HandleTable[DosHandle] = 0xFF;
-
- return TRUE;
-}
-
-static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
-{
- BYTE SftIndex;
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
-
- DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
- OldHandle,
- NewHandle);
-
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return FALSE;
-
- /* Get a pointer to the handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Make sure the old handle is open */
- if (HandleTable[OldHandle] == 0xFF) return FALSE;
-
- /* Check if the new handle is open */
- if (HandleTable[NewHandle] != 0xFF)
- {
- /* Close it */
- DosCloseHandle(NewHandle);
- }
-
- /* Increment the reference count of the SFT entry */
- SftIndex = HandleTable[OldHandle];
- DosSystemFileTable[SftIndex].RefCount++;
-
- /* Make the new handle point to that SFT entry */
- HandleTable[NewHandle] = SftIndex;
-
- /* Return success */
- return TRUE;
-}
-
static BOOLEAN DosChangeDrive(BYTE Drive)
{
WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
|| !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
{
DosLastError = ERROR_PATH_NOT_FOUND;
- return FALSE;
- }
-
- /* Check if this is the current drive */
- if (DriveNumber == CurrentDrive)
- {
- /* Change the directory */
- if (!SetCurrentDirectoryA(Directory))
- {
- DosLastError = LOWORD(GetLastError());
- return FALSE;
- }
- }
-
- /* Get the directory part of the path */
- Path = strchr(Directory, '\\');
- if (Path != NULL)
- {
- /* Skip the backslash */
- Path++;
- }
-
- /* Set the directory for the drive */
- if (Path != NULL)
- {
- strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
- }
- else
- {
- CurrentDirectories[DriveNumber][0] = '\0';
- }
-
- /* Return success */
- return TRUE;
-}
-
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle)
-{
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
-
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return NULL;
-
- /* Get a pointer to the handle table */
- PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
- /* Make sure the handle is open */
- if (HandleTable[DosHandle] == 0xFF) return NULL;
-
- /* Return a pointer to the SFT entry */
- return &DosSystemFileTable[HandleTable[DosHandle]];
-}
-
-VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
-{
- PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
- LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
-
- RtlZeroMemory(PspBlock, sizeof(*PspBlock));
-
- /* Set the exit interrupt */
- PspBlock->Exit[0] = 0xCD; // int 0x20
- PspBlock->Exit[1] = 0x20;
-
- /* Set the number of the last paragraph */
- PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
-
- /* Save the interrupt vectors */
- PspBlock->TerminateAddress = IntVecTable[0x22];
- PspBlock->BreakAddress = IntVecTable[0x23];
- PspBlock->CriticalAddress = IntVecTable[0x24];
-
- /* Set the parent PSP */
- PspBlock->ParentPsp = CurrentPsp;
-
- /* Copy the parent handle table */
- DosCopyHandleTable(PspBlock->HandleTable);
-
- /* Set the environment block */
- PspBlock->EnvBlock = Environment;
-
- /* Set the handle table pointers to the internal handle table */
- PspBlock->HandleTableSize = 20;
- PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
-
- /* Set the DOS version */
- PspBlock->DosVersion = DOS_VERSION;
-
- /* Set the far call opcodes */
- PspBlock->FarCall[0] = 0xCD; // int 0x21
- PspBlock->FarCall[1] = 0x21;
- PspBlock->FarCall[2] = 0xCB; // retf
-
- /* Set the command line */
- PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
- RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
- PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
-}
-
-DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
- IN LPCSTR ExecutablePath,
- IN LPCSTR CommandLine,
- IN LPCSTR Environment OPTIONAL,
- OUT PDWORD StackLocation OPTIONAL,
- OUT PDWORD EntryPoint OPTIONAL)
-{
- DWORD Result = ERROR_SUCCESS;
- HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
- LPBYTE Address = NULL;
- WORD Segment = 0;
- WORD EnvBlock = 0;
- WORD MaxAllocSize;
- DWORD i, FileSize, ExeSize;
- PIMAGE_DOS_HEADER Header;
- PDWORD RelocationTable;
- PWORD RelocWord;
- LPSTR CmdLinePtr = (LPSTR)CommandLine;
-
- DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
- LoadType,
- ExecutablePath,
- CommandLine,
- Environment ? Environment : "n/a",
- StackLocation,
- EntryPoint);
-
- if (LoadType == DOS_LOAD_OVERLAY)
- {
- DPRINT1("Overlay loading is not supported yet.\n");
- 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,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (FileHandle == INVALID_HANDLE_VALUE)
- {
- Result = GetLastError();
- goto Cleanup;
- }
-
- /* Get the file size */
- FileSize = GetFileSize(FileHandle, NULL);
-
- /* Create a mapping object for the file */
- FileMapping = CreateFileMapping(FileHandle,
- NULL,
- PAGE_READONLY,
- 0,
- 0,
- NULL);
- if (FileMapping == NULL)
- {
- Result = GetLastError();
- goto Cleanup;
- }
-
- /* Map the file into memory */
- Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
- if (Address == NULL)
- {
- Result = GetLastError();
- goto Cleanup;
- }
-
- /* Copy the environment block to DOS memory */
- EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
- if (EnvBlock == 0)
- {
- Result = ERROR_NOT_ENOUGH_MEMORY;
- goto Cleanup;
- }
-
- /* Check if this is an EXE file or a COM file */
- if (Address[0] == 'M' && Address[1] == 'Z')
- {
- /* EXE file */
-
- /* Get the MZ header */
- Header = (PIMAGE_DOS_HEADER)Address;
-
- /* Get the base size of the file, in paragraphs (rounded up) */
- ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
-
- /* Add the PSP size, in paragraphs */
- ExeSize += sizeof(DOS_PSP) >> 4;
-
- /* Add the maximum size that should be allocated */
- ExeSize += Header->e_maxalloc;
-
- /* Make sure it does not pass 0xFFFF */
- if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
-
- /* Try to allocate that much memory */
- Segment = DosAllocateMemory((WORD)ExeSize, &MaxAllocSize);
-
- if (Segment == 0)
- {
- /* Check if there's at least enough memory for the minimum size */
- if (MaxAllocSize < (ExeSize - Header->e_maxalloc + Header->e_minalloc))
- {
- Result = DosLastError;
- goto Cleanup;
- }
-
- /* Allocate that minimum amount */
- ExeSize = MaxAllocSize;
- Segment = DosAllocateMemory((WORD)ExeSize, NULL);
- ASSERT(Segment != 0);
- }
-
- /* Initialize the PSP */
- DosInitializePsp(Segment,
- CommandLine,
- (WORD)ExeSize,
- EnvBlock);
-
- /* The process owns its own memory */
- DosChangeMemoryOwner(Segment, Segment);
- DosChangeMemoryOwner(EnvBlock, Segment);
-
- /* Copy the program to Segment:0100 */
- RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
- Address + (Header->e_cparhdr << 4),
- min(FileSize - (Header->e_cparhdr << 4),
- (ExeSize << 4) - sizeof(DOS_PSP)));
-
- /* Get the relocation table */
- RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
-
- /* Perform relocations */
- for (i = 0; i < Header->e_crlc; i++)
- {
- /* Get a pointer to the word that needs to be patched */
- RelocWord = (PWORD)SEG_OFF_TO_PTR(Segment + HIWORD(RelocationTable[i]),
- 0x100 + LOWORD(RelocationTable[i]));
-
- /* Add the number of the EXE segment to it */
- *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
- }
-
- if (LoadType == DOS_LOAD_AND_EXECUTE)
- {
- /* Set the initial segment registers */
- setDS(Segment);
- setES(Segment);
-
- /* Set the stack to the location from the header */
- setSS(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss);
- setSP(Header->e_sp);
-
- /* Execute */
- CurrentPsp = Segment;
- DiskTransferArea = MAKELONG(0x80, Segment);
- CpuExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
- Header->e_ip);
- }
- }
- else
- {
- /* COM file */
-
- /* Find the maximum amount of memory that can be allocated */
- DosAllocateMemory(0xFFFF, &MaxAllocSize);
-
- /* Make sure it's enough for the whole program and the PSP */
- if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
- {
- Result = ERROR_NOT_ENOUGH_MEMORY;
- goto Cleanup;
- }
-
- /* Allocate all of it */
- Segment = DosAllocateMemory(MaxAllocSize, NULL);
- if (Segment == 0)
- {
- Result = DosLastError;
- goto Cleanup;
- }
-
- /* The process owns its own memory */
- DosChangeMemoryOwner(Segment, Segment);
- DosChangeMemoryOwner(EnvBlock, Segment);
-
- /* Copy the program to Segment:0100 */
- RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
- Address,
- FileSize);
-
- /* Initialize the PSP */
- DosInitializePsp(Segment,
- CommandLine,
- MaxAllocSize,
- EnvBlock);
-
- if (LoadType == DOS_LOAD_AND_EXECUTE)
- {
- /* Set the initial segment registers */
- setDS(Segment);
- setES(Segment);
-
- /* Set the stack to the last word of the segment */
- setSS(Segment);
- setSP(0xFFFE);
-
- /*
- * Set the value on the stack to 0, so that a near return
- * jumps to PSP:0000 which has the exit code.
- */
- *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
-
- /* Execute */
- CurrentPsp = Segment;
- DiskTransferArea = MAKELONG(0x80, Segment);
- CpuExecute(Segment, 0x100);
- }
- }
-
-Cleanup:
- if (Result != ERROR_SUCCESS)
- {
- /* It was not successful, cleanup the DOS memory */
- if (EnvBlock) DosFreeMemory(EnvBlock);
- if (Segment) DosFreeMemory(Segment);
- }
-
- /* Unmap the file*/
- if (Address != NULL) UnmapViewOfFile(Address);
-
- /* Close the file mapping object */
- if (FileMapping != NULL) CloseHandle(FileMapping);
-
- /* Close the file handle */
- if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
-
- return Result;
-}
-
-DWORD DosStartProcess(IN LPCSTR ExecutablePath,
- IN LPCSTR CommandLine,
- IN LPCSTR Environment OPTIONAL)
-{
- 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
-
- // HACK: Simulate a ENTER key release scancode on the PS/2 port because
- // some apps expect to read a key release scancode (> 0x80) when they
- // are started.
- IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
- IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
-
- /* 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)
-{
- DWORD Result;
- DWORD BinaryType;
- LPVOID Environment = NULL;
- VDM_COMMAND_INFO CommandInfo;
- CHAR CmdLine[MAX_PATH];
- CHAR AppName[MAX_PATH];
- CHAR PifFile[MAX_PATH];
- CHAR Desktop[MAX_PATH];
- CHAR Title[MAX_PATH];
- CHAR Env[MAX_PATH];
- STARTUPINFOA StartupInfo;
- PROCESS_INFORMATION ProcessInfo;
-
- /* Get the binary type */
- if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
-
- /* Did the caller specify an environment segment? */
- if (Parameters->Environment)
- {
- /* Yes, use it instead of the parent one */
- Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
- }
-
- /* Set up the startup info structure */
- RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
- StartupInfo.cb = sizeof(StartupInfo);
-
- /* Create the process */
- if (!CreateProcessA(ProgramName,
- FAR_POINTER(Parameters->CommandLine),
- NULL,
- NULL,
- FALSE,
- 0,
- Environment,
- NULL,
- &StartupInfo,
- &ProcessInfo))
- {
- return GetLastError();
- }
-
- /* Check the type of the program */
- switch (BinaryType)
- {
- /* These are handled by NTVDM */
- case SCS_DOS_BINARY:
- case SCS_WOW_BINARY:
- {
- /* Clear the structure */
- RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
- /* Initialize the structure members */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
- CommandInfo.CmdLine = CmdLine;
- CommandInfo.CmdLen = sizeof(CmdLine);
- CommandInfo.AppName = AppName;
- CommandInfo.AppLen = sizeof(AppName);
- CommandInfo.PifFile = PifFile;
- CommandInfo.PifLen = sizeof(PifFile);
- CommandInfo.Desktop = Desktop;
- CommandInfo.DesktopLen = sizeof(Desktop);
- CommandInfo.Title = Title;
- CommandInfo.TitleLen = sizeof(Title);
- CommandInfo.Env = Env;
- CommandInfo.EnvLen = sizeof(Env);
-
- /* Get the VDM command information */
- if (!GetNextVDMCommand(&CommandInfo))
- {
- /* Shouldn't happen */
- ASSERT(FALSE);
- }
-
- /* Increment the re-entry count */
- CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
- GetNextVDMCommand(&CommandInfo);
-
- /* Load the executable */
- Result = DosLoadExecutable(LoadType,
- AppName,
- CmdLine,
- Env,
- &Parameters->StackLocation,
- &Parameters->EntryPoint);
- if (Result != ERROR_SUCCESS)
- {
- DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
- // FIXME: Decrement the reenter count. Or, instead, just increment
- // the VDM reenter count *only* if this call succeeds...
- }
-
- break;
- }
-
- /* Not handled by NTVDM */
- default:
- {
- /* Wait for the process to finish executing */
- WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
- }
- }
-
- /* Close the handles */
- CloseHandle(ProcessInfo.hProcess);
- CloseHandle(ProcessInfo.hThread);
-
- return ERROR_SUCCESS;
-}
-#endif
-
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
-{
- WORD i;
- WORD McbSegment = FIRST_MCB_SEGMENT;
- PDOS_MCB CurrentMcb;
- LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
- PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
-
- DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
- Psp,
- ReturnCode);
-
- /* Check if this PSP is it's own parent */
- if (PspBlock->ParentPsp == Psp) goto Done;
-
- for (i = 0; i < PspBlock->HandleTableSize; i++)
- {
- /* Close the handle */
- DosCloseHandle(i);
- }
-
- /* Free the memory used by the process */
- while (TRUE)
- {
- /* Get a pointer to the MCB */
- CurrentMcb = SEGMENT_TO_MCB(McbSegment);
-
- /* Make sure the MCB is valid */
- if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
-
- /* If this block was allocated by the process, free it */
- if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
-
- /* If this was the last block, quit */
- if (CurrentMcb->BlockType == 'Z') break;
-
- /* Update the segment and continue */
- McbSegment += CurrentMcb->Size + 1;
+ return FALSE;
}
-Done:
- /* Restore the interrupt vectors */
- IntVecTable[0x22] = PspBlock->TerminateAddress;
- IntVecTable[0x23] = PspBlock->BreakAddress;
- IntVecTable[0x24] = PspBlock->CriticalAddress;
-
- /* Update the current PSP */
- if (Psp == CurrentPsp)
+ /* Check if this is the current drive */
+ if (DriveNumber == CurrentDrive)
{
- CurrentPsp = PspBlock->ParentPsp;
- if (CurrentPsp == SYSTEM_PSP)
+ /* Change the directory */
+ if (!SetCurrentDirectoryA(Directory))
{
- ResetEvent(VdmTaskEvent);
- CpuUnsimulate();
+ DosLastError = LOWORD(GetLastError());
+ return FALSE;
}
}
-#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)
+ /* Get the directory part of the path */
+ Path = strchr(Directory, '\\');
+ if (Path != NULL)
{
- VDM_COMMAND_INFO CommandInfo;
-
- /* Decrement the re-entry count */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
- GetNextVDMCommand(&CommandInfo);
-
- /* Clear the structure */
- RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
- /* Update the VDM state of the task */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
- GetNextVDMCommand(&CommandInfo);
+ /* Skip the backslash */
+ Path++;
}
-#endif
- /* Save the return code - Normal termination */
- DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
+ /* Set the directory for the drive */
+ if (Path != NULL)
+ {
+ strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
+ }
+ else
+ {
+ CurrentDirectories[DriveNumber][0] = '\0';
+ }
- /* Return control to the parent process */
- CpuExecute(HIWORD(PspBlock->TerminateAddress),
- LOWORD(PspBlock->TerminateAddress));
+ /* Return success */
+ return TRUE;
}
-BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
+static BOOLEAN DosControlBreak(VOID)
{
- PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle);
- PDOS_DEVICE_NODE Node;
-
- /* Make sure it exists and is a device */
- if (!SftEntry || SftEntry->Type != DOS_SFT_ENTRY_DEVICE)
- {
- DosLastError = ERROR_FILE_NOT_FOUND;
- return FALSE;
- }
+ setCF(0);
- Node = SftEntry->DeviceNode;
+ /* Call interrupt 0x23 */
+ Int32Call(&DosContext, 0x23);
- switch (ControlCode)
+ if (getCF())
{
- /* Get Device Information */
- case 0x00:
- {
- /*
- * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
- * for a list of possible flags.
- */
+ DosTerminateProcess(CurrentPsp, 0, 0);
+ return TRUE;
+ }
- /* Return the device information word */
- setDX(Node->DeviceAttributes);
- return TRUE;
- }
+ return FALSE;
+}
- /* Set Device Information */
- case 0x01:
- {
- Node->DeviceAttributes = getDX();
- return TRUE;
- }
+/* PUBLIC FUNCTIONS ***********************************************************/
- /* Read From Device I/O Control Channel */
- case 0x02:
- {
- WORD Length = getCX();
+VOID DosInitializePsp(WORD PspSegment,
+ LPCSTR CommandLine,
+ WORD ProgramSize,
+ WORD Environment,
+ DWORD ReturnAddress)
+{
+ PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
+ LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
- if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
- {
- DosLastError = ERROR_INVALID_FUNCTION;
- return FALSE;
- }
+ RtlZeroMemory(PspBlock, sizeof(*PspBlock));
- /* Do nothing if there is no IOCTL routine */
- if (!Node->IoctlReadRoutine)
- {
- setAX(0);
- return TRUE;
- }
+ /* Set the exit interrupt */
+ PspBlock->Exit[0] = 0xCD; // int 0x20
+ PspBlock->Exit[1] = 0x20;
- Node->IoctlReadRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
+ /* Set the number of the last paragraph */
+ PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
- setAX(Length);
- return TRUE;
- }
+ /* Save the interrupt vectors */
+ PspBlock->TerminateAddress = ReturnAddress;
+ PspBlock->BreakAddress = IntVecTable[0x23];
+ PspBlock->CriticalAddress = IntVecTable[0x24];
- /* Write To Device I/O Control Channel */
- case 0x03:
- {
- WORD Length = getCX();
+ /* Set the parent PSP */
+ PspBlock->ParentPsp = CurrentPsp;
- if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
- {
- DosLastError = ERROR_INVALID_FUNCTION;
- return FALSE;
- }
+ /* Copy the parent handle table */
+ DosCopyHandleTable(PspBlock->HandleTable);
- /* Do nothing if there is no IOCTL routine */
- if (!Node->IoctlWriteRoutine)
- {
- setAX(0);
- return TRUE;
- }
+ /* Set the environment block */
+ PspBlock->EnvBlock = Environment;
- Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
+ /* Set the handle table pointers to the internal handle table */
+ PspBlock->HandleTableSize = 20;
+ PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
- setAX(Length);
- return TRUE;
- }
+ /* Set the DOS version */
+ PspBlock->DosVersion = DOS_VERSION;
- /* Unsupported control code */
- default:
- {
- DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
+ /* Set the far call opcodes */
+ PspBlock->FarCall[0] = 0xCD; // int 0x21
+ PspBlock->FarCall[1] = 0x21;
+ PspBlock->FarCall[2] = 0xCB; // retf
- DosLastError = ERROR_INVALID_PARAMETER;
- return FALSE;
- }
+ if (CommandLine)
+ {
+ /* Set the command line */
+ PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
+ RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
+ PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
}
}
VOID WINAPI DosInt20h(LPWORD Stack)
{
/* This is the exit interrupt */
- DosTerminateProcess(Stack[STACK_CS], 0);
+ DosTerminateProcess(Stack[STACK_CS], 0, 0);
}
VOID WINAPI DosInt21h(LPWORD Stack)
PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer;
INT Return;
+ (*InDos)++;
+
/* Check the value in the AH register */
switch (getAH())
{
/* Terminate Program */
case 0x00:
{
- DosTerminateProcess(Stack[STACK_CS], 0);
+ DosTerminateProcess(Stack[STACK_CS], 0, 0);
break;
}
{
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;
-
setAL(Character);
break;
}
while (Count < InputBuffer->MaxLength)
{
- // FIXME!! This function should interpret backspaces etc...
-
/* Try to read a character (wait) */
Character = DosReadCharacter(DOS_INPUT_HANDLE);
- // 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(DOS_OUTPUT_HANDLE, Character);
- InputBuffer->Buffer[Count] = Character;
-
- Count++; /* Carriage returns are also counted */
+ 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;
+ Count++; /* Carriage returns are NOT counted */
}
/* Update the length */
/* Create New PSP */
case 0x26:
{
- DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
+ DosClonePsp(getDX(), getCS());
+ break;
+ }
+
+ /* Parse Filename into FCB */
+ case 0x29:
+ {
+ PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
+ PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI());
+ BYTE Options = getAL();
+ INT i;
+ CHAR FillChar = ' ';
+
+ if (FileName[1] == ':')
+ {
+ /* Set the drive number */
+ Fcb->DriveNumber = RtlUpperChar(FileName[0]) - 'A' + 1;
+
+ /* Skip to the file name part */
+ FileName += 2;
+ }
+ else
+ {
+ /* No drive number specified */
+ if (Options & (1 << 1)) Fcb->DriveNumber = CurrentDrive + 1;
+ else Fcb->DriveNumber = 0;
+ }
+
+ /* Parse the file name */
+ i = 0;
+ while ((*FileName > 0x20) && (i < 8))
+ {
+ if (*FileName == '.') break;
+ else if (*FileName == '*')
+ {
+ FillChar = '?';
+ break;
+ }
+
+ Fcb->FileName[i++] = RtlUpperChar(*FileName++);
+ }
+
+ /* Fill the whole field with blanks only if bit 2 is not set */
+ if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 2)))
+ {
+ for (; i < 8; i++) Fcb->FileName[i] = FillChar;
+ }
+
+ /* Skip to the extension part */
+ while (*FileName > 0x20 && *FileName != '.') FileName++;
+ if (*FileName == '.') FileName++;
+
+ /* Now parse the extension */
+ i = 0;
+ FillChar = ' ';
+
+ while ((*FileName > 0x20) && (i < 3))
+ {
+ if (*FileName == '*')
+ {
+ FillChar = '?';
+ break;
+ }
+
+ Fcb->FileExt[i++] = RtlUpperChar(*FileName++);
+ }
+
+ /* Fill the whole field with blanks only if bit 3 is not set */
+ if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 3)))
+ {
+ for (; i < 3; i++) Fcb->FileExt[i] = FillChar;
+ }
+
break;
}
break;
}
+ /* Terminate and Stay Resident */
+ case 0x31:
+ {
+ DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
+ DosTerminateProcess(CurrentPsp, getAL(), getDX());
+ break;
+ }
+
/* Extended functionalities */
case 0x33:
{
break;
}
+ /* Get Address of InDOS flag */
+ case 0x34:
+ {
+ setES(HIWORD(INDOS_POINTER));
+ setBX(LOWORD(INDOS_POINTER));
+
+ break;
+ }
+
/* Get Interrupt Vector */
case 0x35:
{
break;
}
+ /* Get Free Disk Space */
+ case 0x36:
+ {
+ CHAR RootPath[3] = "X:\\";
+ DWORD SectorsPerCluster;
+ DWORD BytesPerSector;
+ DWORD NumberOfFreeClusters;
+ DWORD TotalNumberOfClusters;
+
+ if (getDL() == 0) RootPath[0] = 'A' + CurrentDrive;
+ else RootPath[0] = 'A' + getDL() - 1;
+
+ if (GetDiskFreeSpaceA(RootPath,
+ &SectorsPerCluster,
+ &BytesPerSector,
+ &NumberOfFreeClusters,
+ &TotalNumberOfClusters))
+ {
+ setAX(LOWORD(SectorsPerCluster));
+ setCX(LOWORD(BytesPerSector));
+ setBX(LOWORD(NumberOfFreeClusters));
+ setDX(LOWORD(TotalNumberOfClusters));
+ }
+ else
+ {
+ /* Error */
+ setAX(0xFFFF);
+ }
+
+ break;
+ }
+
/* SWITCH character - AVAILDEV */
case 0x37:
{
case 0x3D:
{
WORD FileHandle;
- WORD ErrorCode;
LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
- PDOS_DEVICE_NODE Device = DosGetDevice(FileName);
-
- if (Device)
- {
- FileHandle = DosOpenDevice(Device);
- ErrorCode = (FileHandle != INVALID_DOS_HANDLE)
- ? ERROR_SUCCESS : ERROR_TOO_MANY_OPEN_FILES;
- }
- else
- {
- ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
- }
+ WORD ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
if (ErrorCode == ERROR_SUCCESS)
{
/* IOCTL */
case 0x44:
{
- if (DosHandleIoctl(getAL(), getBX()))
+ WORD Length = getCX();
+
+ if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length))
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(Length);
}
else
{
/* Duplicate Handle */
case 0x45:
{
- WORD NewHandle;
- PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
- if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE)
- {
- /* The handle is invalid */
- Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(ERROR_INVALID_HANDLE);
- break;
- }
+ WORD NewHandle = DosDuplicateHandle(getBX());
- /* Open a new handle to the same entry */
- switch (SftEntry->Type)
+ if (NewHandle != INVALID_DOS_HANDLE)
{
- case DOS_SFT_ENTRY_WIN32:
- {
- NewHandle = DosOpenHandle(SftEntry->Handle);
- break;
- }
-
- case DOS_SFT_ENTRY_DEVICE:
- {
- NewHandle = DosOpenDevice(SftEntry->DeviceNode);
- break;
- }
-
- default:
- {
- /* Shouldn't happen */
- ASSERT(FALSE);
- }
+ setAX(NewHandle);
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
}
-
- if (NewHandle == INVALID_DOS_HANDLE)
+ else
{
- /* Too many files open */
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(ERROR_TOO_MANY_OPEN_FILES);
- break;
+ setAX(DosLastError);
}
- /* Return the result */
- Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
- setAX(NewHandle);
break;
}
/* Force Duplicate Handle */
case 0x46:
{
- if (DosDuplicateHandle(getBX(), getCX()))
+ if (DosForceDuplicateHandle(getBX(), getCX()))
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
}
break;
}
-#ifndef STANDALONE
/* Execute */
case 0x4B:
{
DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
- WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock);
+ DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
+ WORD ErrorCode;
+
+#ifndef STANDALONE
+ if (LoadType == DOS_LOAD_AND_EXECUTE)
+ {
+ /* Create a new process */
+ ErrorCode = DosCreateProcess(ProgramName, ParamBlock, ReturnAddress);
+ }
+ else
+ {
+#else
+ {
+#endif
+ /* Just load an executable */
+ ErrorCode = DosLoadExecutable(LoadType,
+ ProgramName,
+ ParamBlock,
+ NULL,
+ NULL,
+ ReturnAddress);
+ }
if (ErrorCode == ERROR_SUCCESS)
{
break;
}
-#endif
/* Terminate With Return Code */
case 0x4C:
{
- DosTerminateProcess(CurrentPsp, getAL());
+ DosTerminateProcess(CurrentPsp, getAL(), 0);
break;
}
/* Internal - Set Current Process ID (Set PSP Address) */
case 0x50:
{
- // FIXME: Is it really what it's done ??
- CurrentPsp = getBX();
+ DosSetProcessContext(getBX());
break;
}
*/
/* Return the DOS "list of lists" in ES:BX */
- setES(0x0000);
- setBX(0x0000);
+ setES(DOS_DATA_SEGMENT);
+ setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
+
+ break;
+ }
- DisplayMessage(L"Required for AARD code, do you remember? :P");
+ /* Create Child PSP */
+ case 0x55:
+ {
+ DosCreatePsp(getDX(), getSI());
break;
}
/* Lock/Unlock Region of File */
case 0x5C:
{
- PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
- if (SftEntry == NULL || SftEntry->Type != DOS_SFT_ENTRY_WIN32)
- {
- /* The handle is invalid */
- Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(ERROR_INVALID_HANDLE);
- break;
- }
-
if (getAL() == 0x00)
{
/* Lock region of file */
- if (LockFile(SftEntry->Handle,
- MAKELONG(getCX(), getDX()), 0,
- MAKELONG(getSI(), getDI()), 0))
+ if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
}
else
{
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(GetLastError());
+ setAX(DosLastError);
}
}
else if (getAL() == 0x01)
{
/* Unlock region of file */
- if (UnlockFile(SftEntry->Handle,
- MAKELONG(getCX(), getDX()), 0,
- MAKELONG(getSI(), getDI()), 0))
+ if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
}
else
{
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(GetLastError());
+ setAX(DosLastError);
}
}
else
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
}
}
+
+ (*InDos)--;
}
VOID WINAPI DosBreakInterrupt(LPWORD Stack)
{
- UNREFERENCED_PARAMETER(Stack);
-
- /* Stop the VDM task */
- ResetEvent(VdmTaskEvent);
- CpuUnsimulate();
+ /* Set CF to terminate the running process */
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
}
VOID WINAPI DosFastConOut(LPWORD Stack)
VOID WINAPI DosInt2Fh(LPWORD Stack)
{
- DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
- getAH(), getAL());
- Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ switch (getAH())
+ {
+ /* Extended Memory Specification */
+ case 0x43:
+ {
+ DWORD DriverEntry;
+ if (!XmsGetDriverEntry(&DriverEntry)) break;
+
+ if (getAL() == 0x00)
+ {
+ /* The driver is loaded */
+ setAL(0x80);
+ }
+ else if (getAL() == 0x10)
+ {
+ setES(HIWORD(DriverEntry));
+ setBX(LOWORD(DriverEntry));
+ }
+ else
+ {
+ DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
+ }
+
+ break;
+ }
+
+ default:
+ {
+ DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+ getAH(), getAL());
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ }
+ }
}
BOOLEAN DosKRNLInitialize(VOID)
{
-
#if 1
UCHAR i;
CHAR CurrentDirectory[MAX_PATH];
CHAR DosDirectory[DOS_DIR_LENGTH];
LPSTR Path;
+ PDOS_SFT Sft;
+
+ const BYTE NullDriverRoutine[] = {
+ /* Strategy routine entry */
+ 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
+ 0xC7,
+ 0x47,
+ FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
+ LOBYTE(DOS_DEVSTAT_DONE),
+ HIBYTE(DOS_DEVSTAT_DONE),
+
+ /* Interrupt routine entry */
+ 0xCB, // retf
+ };
FILE *Stream;
WCHAR Buffer[256];
+ /* Setup the InDOS flag */
+ InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
+ *InDos = 0;
+
/* Clear the current directory buffer */
RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
fclose(Stream);
}
+ /* Initialize the list of lists */
+ SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
+ RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
+ SysVars->FirstMcb = FIRST_MCB_SEGMENT;
+ SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
+
+ /* Initialize the NUL device driver */
+ SysVars->NullDevice.Link = 0xFFFFFFFF;
+ SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
+ SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
+ SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
+ RtlFillMemory(SysVars->NullDevice.DeviceName,
+ sizeof(SysVars->NullDevice.DeviceName),
+ ' ');
+ RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
+ RtlCopyMemory(SysVars->NullDriverRoutine,
+ NullDriverRoutine,
+ sizeof(NullDriverRoutine));
+
/* Initialize the SFT */
- for (i = 0; i < DOS_SFT_SIZE; i++)
+ Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
+ Sft->Link = 0xFFFFFFFF;
+ Sft->NumDescriptors = DOS_SFT_SIZE;
+
+ for (i = 0; i < Sft->NumDescriptors; i++)
{
- DosSystemFileTable[i].Type = DOS_SFT_ENTRY_NONE;
- DosSystemFileTable[i].RefCount = 0;
+ /* Clear the file descriptor entry */
+ RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
}
- /* Load the EMS driver */
- EmsDrvInitialize();
-
- /* Load the CON driver */
- ConDrvInitialize();
-
#endif
/* Initialize the callback context */
RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
RegisterDosInt32(0x2F, DosInt2Fh );
+ /* Load the EMS driver */
+ if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
+ {
+ DPRINT1("Could not initialize EMS. EMS will not be available.\n"
+ "Try reducing the number of EMS pages.\n");
+ }
+
+ /* Load the XMS driver (HIMEM) */
+ XmsInitialize();
+
+ /* Load the CON driver */
+ ConDrvInitialize();
+
return TRUE;
}