#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 "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;
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)
-{
- INT 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)
-{
- INT 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)
-{
- INT 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)
-{
- INT 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 ConIn = DosGetDevice("CONIN$");
- PDOS_DEVICE_NODE ConOut = DosGetDevice("CONOUT$");
- ASSERT(ConIn != NULL && ConOut != 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++)
- {
- PDOS_DEVICE_NODE Device = (i == 0) ? ConIn : ConOut;
-
- /* Find the corresponding SFT entry */
- if (IsConsoleHandle(StandardHandles[i]))
- {
- SftEntry = DosFindDeviceSftEntry(Device);
- }
- 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 = Device;
-
- /* Call the open routine */
- if (Device->OpenRoutine) Device->OpenRoutine(Device);
- }
- 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];
return TRUE;
}
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle)
+static BOOLEAN DosControlBreak(VOID)
{
- PDOS_PSP PspBlock;
- LPBYTE HandleTable;
+ setCF(0);
- /* The system PSP has no handle table */
- if (CurrentPsp == SYSTEM_PSP) return NULL;
+ /* Call interrupt 0x23 */
+ Int32Call(&DosContext, 0x23);
- /* 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;
+ if (getCF())
+ {
+ DosTerminateProcess(CurrentPsp, 0, 0);
+ return TRUE;
+ }
- /* Return a pointer to the SFT entry */
- return &DosSystemFileTable[HandleTable[DosHandle]];
+ return FALSE;
}
-VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+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);
PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
/* Save the interrupt vectors */
- PspBlock->TerminateAddress = IntVecTable[0x22];
+ PspBlock->TerminateAddress = ReturnAddress;
PspBlock->BreakAddress = IntVecTable[0x23];
PspBlock->CriticalAddress = IntVecTable[0x24];
IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL,
+ IN DWORD ReturnAddress OPTIONAL,
OUT PDWORD StackLocation OPTIONAL,
OUT PDWORD EntryPoint OPTIONAL)
{
DosInitializePsp(Segment,
CommandLine,
(WORD)ExeSize,
- EnvBlock);
+ EnvBlock,
+ ReturnAddress);
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
DosInitializePsp(Segment,
CommandLine,
MaxAllocSize,
- EnvBlock);
+ EnvBlock,
+ ReturnAddress);
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
IN LPCSTR Environment OPTIONAL)
{
DWORD Result;
+ LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
ExecutablePath,
CommandLine,
Environment,
+ IntVecTable[0x20], // Use INT 20h
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
+ ConsoleAttach();
+ VidBiosAttachToConsole();
// 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
CpuSimulate();
/* Detach from the console */
- VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
+ VidBiosDetachFromConsole();
+ ConsoleDetach();
Quit:
return Result;
#ifndef STANDALONE
WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
LPCSTR ProgramName,
- PDOS_EXEC_PARAM_BLOCK Parameters)
+ PDOS_EXEC_PARAM_BLOCK Parameters,
+ DWORD ReturnAddress)
{
DWORD Result;
DWORD BinaryType;
CHAR PifFile[MAX_PATH];
CHAR Desktop[MAX_PATH];
CHAR Title[MAX_PATH];
- CHAR Env[MAX_PATH];
+ ULONG EnvSize = 256;
+ PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
STARTUPINFOA StartupInfo;
PROCESS_INFORMATION ProcessInfo;
CommandInfo.Title = Title;
CommandInfo.TitleLen = sizeof(Title);
CommandInfo.Env = Env;
- CommandInfo.EnvLen = sizeof(Env);
+ CommandInfo.EnvLen = EnvSize;
+Command:
/* Get the VDM command information */
if (!GetNextVDMCommand(&CommandInfo))
{
+ if (CommandInfo.EnvLen > EnvSize)
+ {
+ /* Expand the environment size */
+ EnvSize = CommandInfo.EnvLen;
+ CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
+
+ /* Repeat the request */
+ CommandInfo.VDMState |= VDM_FLAG_RETRY;
+ goto Command;
+ }
+
/* 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,
+ ReturnAddress,
&Parameters->StackLocation,
&Parameters->EntryPoint);
- if (Result != ERROR_SUCCESS)
+ if (Result == ERROR_SUCCESS)
+ {
+ /* Increment the re-entry count */
+ CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
+ GetNextVDMCommand(&CommandInfo);
+ }
+ else
{
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;
}
}
+ RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
+
/* Close the handles */
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
}
#endif
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
+VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
{
WORD i;
WORD McbSegment = FIRST_MCB_SEGMENT;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
- DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
+ DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp,
- ReturnCode);
+ ReturnCode,
+ KeepResident);
/* Check if this PSP is it's own parent */
if (PspBlock->ParentPsp == Psp) goto Done;
- for (i = 0; i < PspBlock->HandleTableSize; i++)
+ if (KeepResident == 0)
{
- /* Close the handle */
- DosCloseHandle(i);
+ for (i = 0; i < PspBlock->HandleTableSize; i++)
+ {
+ /* Close the handle */
+ DosCloseHandle(i);
+ }
}
/* Free the memory used by the process */
CurrentMcb = SEGMENT_TO_MCB(McbSegment);
/* Make sure the MCB is valid */
- if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
+ 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);
+ /* Check if this block was allocated by the process */
+ if (CurrentMcb->OwnerPsp == Psp)
+ {
+ if (KeepResident == 0)
+ {
+ /* Free this entire block */
+ DosFreeMemory(McbSegment + 1);
+ }
+ else if (KeepResident < CurrentMcb->Size)
+ {
+ /* Reduce the size of the block */
+ DosResizeMemory(McbSegment + 1, KeepResident, NULL);
+
+ /* No further paragraphs need to stay resident */
+ KeepResident = 0;
+ }
+ else
+ {
+ /* Just reduce the amount of paragraphs we need to keep resident */
+ KeepResident -= CurrentMcb->Size;
+ }
+ }
/* If this was the last block, quit */
if (CurrentMcb->BlockType == 'Z') break;
LOWORD(PspBlock->TerminateAddress));
}
-BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
-{
- PDOS_SFT_ENTRY Entry = DosGetSftEntry(FileHandle);
- PDOS_DEVICE_NODE Node = Entry->DeviceNode;
-
- /* Make sure it exists and is a device */
- if (!Entry || Entry->Type != DOS_SFT_ENTRY_DEVICE)
- {
- DosLastError = ERROR_FILE_NOT_FOUND;
- return FALSE;
- }
-
- switch (ControlCode)
- {
- /* Get Device Information */
- case 0x00:
- {
- /*
- * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
- * for a list of possible flags.
- */
-
- /* Return the device information word */
- setDX(Node->DeviceAttributes);
- return TRUE;
- }
-
- /* Set Device Information */
- case 0x01:
- {
- Node->DeviceAttributes = getDX();
- return TRUE;
- }
-
- /* Read From Device I/O Control Channel */
- case 0x02:
- {
- WORD Length = getCX();
-
- if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
- {
- DosLastError = ERROR_INVALID_FUNCTION;
- return FALSE;
- }
-
- /* Do nothing if there is no IOCTL routine */
- if (!Node->IoctlReadRoutine)
- {
- setAX(0);
- return TRUE;
- }
-
- Node->IoctlReadRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
-
- setAX(Length);
- return TRUE;
- }
-
- /* Write To Device I/O Control Channel */
- case 0x03:
- {
- WORD Length = getCX();
-
- if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
- {
- DosLastError = ERROR_INVALID_FUNCTION;
- return FALSE;
- }
-
- /* Do nothing if there is no IOCTL routine */
- if (!Node->IoctlWriteRoutine)
- {
- setAX(0);
- return TRUE;
- }
-
- Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
-
- setAX(Length);
- return TRUE;
- }
-
- /* Unsupported control code */
- default:
- {
- DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
-
- DosLastError = ERROR_INVALID_PARAMETER;
- return FALSE;
- }
- }
-}
-
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 */
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;
+ }
+
/* Get System Date */
case 0x2A:
{
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:
{
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)
{
WORD BytesRead = 0;
WORD ErrorCode;
- DPRINT1("INT 21h, AH = 3Fh\n");
+ DPRINT("DosReadFile(0x%04X)\n", getBX());
DoEcho = TRUE;
ErrorCode = DosReadFile(getBX(),
/* 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());
+ WORD NewHandle = DosDuplicateHandle(getBX());
- if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE)
+ if (NewHandle != INVALID_DOS_HANDLE)
{
- /* The handle is invalid */
- Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
- setAX(ERROR_INVALID_HANDLE);
- break;
- }
-
- /* Open a new handle to the same entry */
- switch (SftEntry->Type)
- {
- 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;
}
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;
+
+ if (LoadType != DOS_LOAD_OVERLAY)
+ {
+ ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock, ReturnAddress);
+ }
+ else
+ {
+ ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
+ ProgramName,
+ FAR_POINTER(ParamBlock->CommandLine),
+ SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
+ ReturnAddress,
+ NULL,
+ NULL);
+ }
if (ErrorCode == ERROR_SUCCESS)
{
/* Terminate With Return Code */
case 0x4C:
{
- DosTerminateProcess(CurrentPsp, getAL());
+ DosTerminateProcess(CurrentPsp, getAL(), 0);
break;
}
*/
/* Return the DOS "list of lists" in ES:BX */
- setES(0x0000);
- setBX(0x0000);
+ setES(DOS_DATA_SEGMENT);
+ setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
- DisplayMessage(L"Required for AARD code, do you remember? :P");
break;
}
/* Lock/Unlock Region of File */
case 0x5C:
{
- PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
- if (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_DEVICE_NODE ConInDevice, ConOutDevice;
+ 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(&ConInDevice, &ConOutDevice);
-
#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;
}