* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: dos/dos32krnl/dos.c
- * PURPOSE: VDM DOS Kernel
+ * 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 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);
}
}
+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)
{
BYTE SftIndex;
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);
}
}
/* Start simulation */
SetEvent(VdmTaskEvent);
- EmulatorSimulate();
+ CpuSimulate();
/* Detach from the console */
VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
}
/* 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;
if (CurrentPsp == SYSTEM_PSP)
{
ResetEvent(VdmTaskEvent);
- EmulatorUnsimulate();
+ CpuUnsimulate();
}
}
GetNextVDMCommand(&CommandInfo);
/* Clear the structure */
- ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Update the VDM state of the task */
CommandInfo.TaskId = SessionId;
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)
{
/* Console input */
InfoWord |= 1 << 0;
+
+ /* It is a device */
+ InfoWord |= 1 << 7;
}
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:
{
+ DPRINT("INT 21h, AH = 01h\n");
+
// FIXME: Under DOS 2+, input / output handle may be redirected!!!!
+ DoEcho = TRUE;
Character = DosReadCharacter(DOS_INPUT_HANDLE);
- DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+ 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;
case 0x07:
case 0x08:
{
+ DPRINT("Char input without echo\n");
+
// FIXME: Under DOS 2+, input handle may be redirected!!!!
Character = DosReadCharacter(DOS_INPUT_HANDLE);
* See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
* for more information.
*/
- setAL('$');
+ setAL('$'); // *String
break;
}
WORD Count = 0;
InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
- DPRINT1("Read Buffered Input\n");
+ DPRINT("Read Buffered Input\n");
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 */
+
if (Character == '\r') break;
- Count++;
}
/* Update the length */
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);
case 0x3F:
{
WORD BytesRead = 0;
- WORD ErrorCode = DosReadFile(getBX(),
- SEG_OFF_TO_PTR(getDS(), getDX()),
- getCX(),
- &BytesRead);
+ WORD ErrorCode;
+
+ DPRINT("INT 21h, AH = 3Fh\n");
+
+ DoEcho = TRUE;
+ ErrorCode = DosReadFile(getBX(),
+ SEG_OFF_TO_PTR(getDS(), getDX()),
+ getCX(),
+ &BytesRead);
+ DoEcho = FALSE;
if (ErrorCode == ERROR_SUCCESS)
{
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.
*/
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);
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))