#define NDEBUG
-#include "dos.h"
-#include "bios.h"
#include "emulator.h"
+#include "dos.h"
+#include "bios.h"
+#include "bop.h"
+#include "int32.h"
#include "registers.h"
/* PRIVATE VARIABLES **********************************************************/
static WORD DosSftRefCount[DOS_SFT_SIZE];
static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
static BOOLEAN DosUmbLinked = FALSE;
+static WORD DosErrorLevel = 0x0000;
+
+/* BOP Identifiers */
+#define BOP_DOS 0x50 // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
+#define BOP_CMD 0x54 // DOS Command Interpreter BOP (for COMMAND.COM)
/* PRIVATE FUNCTIONS **********************************************************/
return ERROR_SUCCESS;
}
+BOOLEAN DosFlushFileBuffers(WORD FileHandle)
+{
+ HANDLE Handle = DosGetRealHandle(FileHandle);
+
+ /* Make sure the handle is valid */
+ if (Handle == INVALID_HANDLE_VALUE) return FALSE;
+
+ /*
+ * No need to check whether the handle is a console handle since
+ * FlushFileBuffers() automatically does this check and calls
+ * FlushConsoleInputBuffer() for us.
+ */
+ // if (IsConsoleHandle(Handle))
+ // return (BOOLEAN)FlushConsoleInputBuffer(Handle);
+ // else
+ return (BOOLEAN)FlushFileBuffers(Handle);
+}
+
BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
{
BYTE SftIndex;
}
/* Set the directory for the drive */
- if (Path != NULL) strcpy(CurrentDirectories[DriveNumber], Path);
- else strcpy(CurrentDirectories[DriveNumber], "");
-
+ if (Path != NULL)
+ {
+ strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
+ }
+ else
+ {
+ CurrentDirectories[DriveNumber][0] = '\0';
+ }
+
/* Return success */
return TRUE;
}
/* Save the interrupt vectors */
PspBlock->TerminateAddress = IntVecTable[0x22];
- PspBlock->BreakAddress = IntVecTable[0x23];
- PspBlock->CriticalAddress = IntVecTable[0x24];
+ PspBlock->BreakAddress = IntVecTable[0x23];
+ PspBlock->CriticalAddress = IntVecTable[0x24];
/* Set the parent PSP */
PspBlock->ParentPsp = CurrentPsp;
LPBYTE Address = NULL;
LPSTR ProgramFilePath, Parameters[256];
CHAR CommandLineCopy[DOS_CMDLINE_LENGTH];
+ CHAR ParamString[DOS_CMDLINE_LENGTH];
INT ParamCount = 0;
WORD Segment = 0;
WORD MaxAllocSize;
/* Save a copy of the command line */
strcpy(CommandLineCopy, CommandLine);
- // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
-
/* Get the file name of the executable */
ProgramFilePath = strtok(CommandLineCopy, " \t");
ParamCount++;
}
+ ZeroMemory(ParamString, sizeof(ParamString));
+
+ /* Store the parameters in a string */
+ for (i = 0; i < ParamCount; i++)
+ {
+ strncat(ParamString, Parameters[i], DOS_CMDLINE_LENGTH - strlen(ParamString) - 1);
+ strncat(ParamString, " ", DOS_CMDLINE_LENGTH - strlen(ParamString) - 1);
+ }
+
/* Open a handle to the executable */
FileHandle = CreateFileA(ProgramFilePath,
GENERIC_READ,
/* Initialize the PSP */
DosInitializePsp(Segment,
- CommandLine,
+ ParamString,
(WORD)ExeSize,
EnvBlock);
/* Initialize the PSP */
DosInitializePsp(Segment,
- CommandLine,
- (WORD)((FileSize + sizeof(DOS_PSP)) >> 4),
+ ParamString,
+ MaxAllocSize,
EnvBlock);
/* Set the initial segment registers */
/* Set the stack to the last word of the segment */
EmulatorSetStack(Segment, 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);
if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
}
+ /* Save the return code - Normal termination */
+ DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
+
/* Return control to the parent process */
EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
LOWORD(PspBlock->TerminateAddress));
}
}
-VOID DosInt20h(LPWORD Stack)
+VOID WINAPI DosSystemBop(LPWORD Stack)
+{
+ /* Get the Function Number and skip it */
+ BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
+ setIP(getIP() + 1);
+
+ DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
+}
+
+VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
+{
+ /* Get the Function Number and skip it */
+ BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
+ setIP(getIP() + 1);
+
+ switch (FuncNum)
+ {
+ case 0x08: // Launch external command
+ {
+#define CMDLINE_LENGTH 1024
+
+ BOOL Result;
+ DWORD dwExitCode;
+
+ LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
+ CHAR CommandLine[CMDLINE_LENGTH] = "";
+ STARTUPINFOA StartupInfo;
+ PROCESS_INFORMATION ProcessInformation;
+ DPRINT1("CMD Run Command '%s'\n", Command);
+
+ Command[strlen(Command)-1] = 0;
+
+ strcpy(CommandLine, "cmd.exe /c ");
+ strcat(CommandLine, Command);
+
+ ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+ ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
+
+ StartupInfo.cb = sizeof(StartupInfo);
+
+ DosPrintCharacter('\n');
+
+ Result = CreateProcessA(NULL,
+ CommandLine,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &StartupInfo,
+ &ProcessInformation);
+ if (Result)
+ {
+ DPRINT1("Command '%s' launched successfully\n");
+
+ /* Wait for process termination */
+ WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
+
+ /* Get the exit code */
+ GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
+
+ /* Close handles */
+ CloseHandle(ProcessInformation.hThread);
+ CloseHandle(ProcessInformation.hProcess);
+ }
+ else
+ {
+ DPRINT1("Failed when launched command '%s'\n");
+ dwExitCode = GetLastError();
+ }
+
+ DosPrintCharacter('\n');
+
+ setAL((UCHAR)dwExitCode);
+
+ break;
+ }
+
+ default:
+ {
+ DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
+ // setCF(1); // Disable, otherwise we enter an infinite loop
+ break;
+ }
+ }
+}
+
+VOID WINAPI DosInt20h(LPWORD Stack)
{
/* This is the exit interrupt */
DosTerminateProcess(Stack[STACK_CS], 0);
}
-VOID DosInt21h(LPWORD Stack)
+VOID WINAPI DosInt21h(LPWORD Stack)
{
- CHAR Character;
+ BYTE Character;
SYSTEMTIME SystemTime;
PCHAR String;
PDOS_INPUT_BUFFER InputBuffer;
break;
}
- /* Read Character And Echo */
+ /* Read Character from STDIN with Echo */
case 0x01:
{
Character = DosReadCharacter();
DosPrintCharacter(Character);
/* Let the BOP repeat if needed */
- if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+ if (getCF()) break;
setAL(Character);
break;
}
- /* Print Character */
+ /* Write Character to STDOUT */
case 0x02:
{
- BYTE Character = getDL();
+ Character = getDL();
DosPrintCharacter(Character);
/*
- * We return the output character (DOS 2.1+), see:
- * http://www.delorie.com/djgpp/doc/rbinter/id/65/25.html
+ * We return the output character (DOS 2.1+).
+ * Also, if we're going to output a TAB, then
+ * don't return a TAB but a SPACE instead.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
* for more information.
*/
- setAL(Character);
+ setAL(Character == '\t' ? ' ' : Character);
+ break;
+ }
+
+ /* Read Character from STDAUX */
+ case 0x03:
+ {
+ // FIXME: Really read it from STDAUX!
+ DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
+ setAL(DosReadCharacter());
+ break;
+ }
+
+ /* Write Character to STDAUX */
+ case 0x04:
+ {
+ // FIXME: Really write it to STDAUX!
+ DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
+ DosPrintCharacter(getDL());
+ break;
+ }
+
+ /* Write Character to Printer */
+ case 0x05:
+ {
+ // FIXME: Really write it to printer!
+ DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
+ DPRINT1("0x%p\n", getDL());
+ DPRINT1("\n\n-----------\n\n");
break;
}
/* Direct Console I/O */
case 0x06:
{
- BYTE Character = getDL();
+ Character = getDL();
if (Character != 0xFF)
{
DosPrintCharacter(Character);
/*
- * We return the output character (DOS 2.1+), see:
- * http://www.delorie.com/djgpp/doc/rbinter/id/69/25.html
+ * We return the output character (DOS 2.1+).
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
* for more information.
*/
setAL(Character);
{
/* No character available */
Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
- setAL(0);
+ setAL(0x00);
}
}
break;
}
- /* Read Character Without Echo */
+ /* Character Input without Echo */
case 0x07:
case 0x08:
{
Character = DosReadCharacter();
/* Let the BOP repeat if needed */
- if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+ if (getCF()) break;
setAL(Character);
break;
}
- /* Print String */
+ /* Write string to STDOUT */
case 0x09:
{
String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
}
/*
- * We return the output character (DOS 2.1+), see:
- * http://www.delorie.com/djgpp/doc/rbinter/id/73/25.html
+ * We return the terminating character (DOS 2.1+).
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
* for more information.
*/
setAL('$');
Character = DosReadCharacter();
/* If it's not ready yet, let the BOP repeat */
- if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+ if (getCF()) break;
/* Echo the character and append it to the buffer */
DosPrintCharacter(Character);
break;
}
+ /* Flush Buffer and Read STDIN */
+ case 0x0C:
+ {
+ BYTE InputFunction = getAL();
+
+ /* Flush STDIN buffer */
+ DosFlushFileBuffers(DOS_INPUT_HANDLE); // Maybe just create a DosFlushInputBuffer...
+
+ /*
+ * If the input function number contained in AL is valid, i.e.
+ * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
+ * recursively with AL == AH.
+ */
+ if (InputFunction == 0x01 || InputFunction == 0x06 ||
+ InputFunction == 0x07 || InputFunction == 0x08 ||
+ InputFunction == 0x0A)
+ {
+ setAH(InputFunction);
+ /*
+ * Instead of calling ourselves really recursively as in:
+ * DosInt21h(Stack);
+ * prefer resetting the CF flag to let the BOP repeat.
+ */
+ setCF(1);
+ }
+ break;
+ }
+
+ /* Disk Reset */
+ case 0x0D:
+ {
+ PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
+
+ // TODO: Flush what's needed.
+ DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
+
+ /* Clear CF in DOS 6 only */
+ if (PspBlock->DosVersion == 0x0006)
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+ break;
+ }
+
/* Set Default Drive */
case 0x0E:
{
break;
}
+ /* NULL Function for CP/M Compatibility */
+ case 0x18:
+ {
+ /*
+ * This function corresponds to the CP/M BDOS function
+ * "get bit map of logged drives", which is meaningless
+ * under MS-DOS.
+ *
+ * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
+ * for more information.
+ */
+ setAL(0x00);
+ break;
+ }
+
/* Get Default Drive */
case 0x19:
{
break;
}
+ /* NULL Function for CP/M Compatibility */
+ case 0x1D:
+ case 0x1E:
+ {
+ /*
+ * Function 0x1D corresponds to the CP/M BDOS function
+ * "get bit map of read-only drives", which is meaningless
+ * under MS-DOS.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
+ * for more information.
+ *
+ * Function 0x1E corresponds to the CP/M BDOS function
+ * "set file attributes", which was meaningless under MS-DOS 1.x.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
+ * for more information.
+ */
+ setAL(0x00);
+ break;
+ }
+
+ /* NULL Function for CP/M Compatibility */
+ case 0x20:
+ {
+ /*
+ * This function corresponds to the CP/M BDOS function
+ * "get/set default user (sublibrary) number", which is meaningless
+ * under MS-DOS.
+ *
+ * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
+ * for more information.
+ */
+ setAL(0x00);
+ break;
+ }
+
/* Set Interrupt Vector */
case 0x25:
{
DWORD FarPointer = MAKELONG(getDX(), getDS());
+ DPRINT1("Setting interrupt 0x%x ...\n", getAL());
/* Write the new far pointer to the IDT */
((PDWORD)BaseAddress)[getAL()] = FarPointer;
+ break;
+ }
+ /* Create New PSP */
+ case 0x26:
+ {
+ DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n");
break;
}
- /* Get system date */
+ /* Get System Date */
case 0x2A:
{
GetLocalTime(&SystemTime);
break;
}
- /* Set system date */
+ /* Set System Date */
case 0x2B:
{
GetLocalTime(&SystemTime);
break;
}
- /* Get system time */
+ /* Get System Time */
case 0x2C:
{
GetLocalTime(&SystemTime);
break;
}
- /* Set system time */
+ /* Set System Time */
case 0x2D:
{
GetLocalTime(&SystemTime);
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
- if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0)
- {
- /* Return DOS 24-bit user serial number in BL:CX */
- setBL(0x00);
- setCX(0x0000);
- }
+ /*
+ * DOS 2+ - GET DOS VERSION
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
+ * for more information.
+ */
- if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 1)
+ if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00)
{
/*
* Return DOS OEM number:
* 0x00 for IBM PC-DOS
- * 0xFF for MS-DOS
+ * 0x02 for packaged MS-DOS
+ */
+ setBH(0x02);
+ }
+
+ if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
+ {
+ /*
+ * Return version flag:
+ * 1 << 3 if DOS is in ROM,
+ * 0 (reserved) if not.
*/
- setBH(0xFF);
+ setBH(0x00);
}
- /* Return DOS version: Minor:Major in AH:AL */
+ /* Return DOS 24-bit user serial number in BL:CX */
+ setBL(0x00);
+ setCX(0x0000);
+
+ /*
+ * Return DOS version: Minor:Major in AH:AL
+ * The Windows NT DOS box returns version 5.00, subject to SETVER.
+ */
setAX(PspBlock->DosVersion);
break;
}
+ /* Extended functionalities */
+ case 0x33:
+ {
+ if (getAL() == 0x06)
+ {
+ /*
+ * DOS 5+ - GET TRUE VERSION NUMBER
+ * This function always returns the true version number, unlike
+ * AH=30h, whose return value may be changed with SETVER.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
+ * for more information.
+ */
+
+ /*
+ * Return the true DOS version: Minor:Major in BH:BL
+ * The Windows NT DOS box returns BX=3205h (version 5.50).
+ */
+ setBX(NTDOS_VERSION);
+
+ /* DOS revision 0 */
+ setDL(0x00);
+
+ /* Unpatched DOS */
+ setDH(0x00);
+ }
+ // else
+ // {
+ // /* Invalid subfunction */
+ // setAL(0xFF);
+ // }
+
+ break;
+ }
+
/* Get Interrupt Vector */
case 0x35:
{
break;
}
+ /* SWITCH character - AVAILDEV */
+ case 0x37:
+ {
+ if (getAL() == 0x00)
+ {
+ /*
+ * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
+ * This setting is ignored by MS-DOS 4.0+.
+ * MS-DOS 5+ always return AL=00h/DL=2Fh.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
+ * for more information.
+ */
+ setDL('/');
+ setAL(0x00);
+ }
+ else if (getAL() == 0x01)
+ {
+ /*
+ * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
+ * This setting is ignored by MS-DOS 5+.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
+ * for more information.
+ */
+ // getDL();
+ setAL(0xFF);
+ }
+ else if (getAL() == 0x02)
+ {
+ /*
+ * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
+ * for more information.
+ */
+ // setDL();
+ setAL(0xFF);
+ }
+ else if (getAL() == 0x03)
+ {
+ /*
+ * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
+ * for more information.
+ */
+ // getDL();
+ setAL(0xFF);
+ }
+ else
+ {
+ /* Invalid subfunction */
+ setAL(0xFF);
+ }
+
+ break;
+ }
+
/* Create Directory */
case 0x39:
{
break;
}
- /* Read File */
+ /* Read from File or Device */
case 0x3F:
{
WORD Handle = getBX();
WORD Count = getCX();
WORD BytesRead = 0;
WORD ErrorCode = ERROR_SUCCESS;
+ CHAR Character;
if (IsConsoleHandle(DosGetRealHandle(Handle)))
{
while (Stack[STACK_COUNTER] < Count)
{
/* Read a character from the BIOS */
- Buffer[Stack[STACK_COUNTER]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
+ Character = LOBYTE(BiosGetCharacter());
/* Stop if the BOP needs to be repeated */
- if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
-
- /* Increment the counter */
- Stack[STACK_COUNTER]++;
+ if (getCF()) break;
+
+ // FIXME: Security checks!
+ DosPrintCharacter(Character);
+ Buffer[Stack[STACK_COUNTER]++] = Character;
+
+ if (Character == '\r')
+ {
+ /* Stop on first carriage return */
+ DosPrintCharacter('\n');
+ break;
+ }
}
- if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
- else BytesRead = Count;
+ if (Character != '\r')
+ {
+ if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
+ else BytesRead = Count;
+ }
+ else BytesRead = Stack[STACK_COUNTER];
}
else
{
ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead);
}
- if (ErrorCode == 0)
+ if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
setAX(BytesRead);
break;
}
- /* Write File */
+ /* Write to File or Device */
case 0x40:
{
WORD BytesWritten = 0;
getCX(),
&BytesWritten);
- if (ErrorCode == 0)
+ if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
setAX(BytesWritten);
if (DeleteFileA(FileName))
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ /*
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
+ * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
+ */
+ setAL(FileName[0] - 'A');
}
else
{
getAL(),
&NewLocation);
- if (ErrorCode == 0)
+ if (ErrorCode == ERROR_SUCCESS)
{
Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
{
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
setAX(GetLastError());
- break;
}
-
- /* Return the attributes that DOS can understand */
- Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
- setCL(LOBYTE(Attributes));
+ else
+ {
+ /* Return the attributes that DOS can understand */
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setCX(Attributes & 0x00FF);
+ }
}
else if (getAL() == 0x01)
{
break;
}
+ /* Get Current Directory */
+ case 0x47:
+ {
+ BYTE DriveNumber = getDL();
+ String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
+
+ /* Get the real drive number */
+ if (DriveNumber == 0)
+ {
+ DriveNumber = CurrentDrive;
+ }
+ else
+ {
+ /* Decrement DriveNumber since it was 1-based */
+ DriveNumber--;
+ }
+
+ if (DriveNumber <= LastDrive - 'A')
+ {
+ /*
+ * Copy the current directory into the target buffer.
+ * It doesn't contain the drive letter and the backslash.
+ */
+ strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
+ }
+ else
+ {
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+ setAX(ERROR_INVALID_DRIVE);
+ }
+
+ break;
+ }
+
/* Allocate Memory */
case 0x48:
{
break;
}
- /* Get Current Process */
+ /* Get Return Code (ERRORLEVEL) */
+ case 0x4D:
+ {
+ /*
+ * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
+ * DosErrorLevel is cleared after being read by this function.
+ */
+ Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+ setAX(DosErrorLevel);
+ DosErrorLevel = 0x0000; // Clear it
+ break;
+ }
+
+ /* Internal - Set Current Process ID (Set PSP Address) */
+ case 0x50:
+ {
+ // FIXME: Is it really what it's done ??
+ CurrentPsp = getBX();
+ break;
+ }
+
+ /* Internal - Get Current Process ID (Get PSP Address) */
case 0x51:
+ /* Get Current PSP Address */
+ case 0x62:
{
+ /*
+ * Undocumented AH=51h is identical to the documented AH=62h.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
+ * and http://www.ctyme.com/intr/rb-3140.htm
+ * for more information.
+ */
setBX(CurrentPsp);
break;
}
/* Unsupported */
default:
{
- DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", getAH());
+ DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+ getAH(), getAL());
+
+ setAL(0); // Some functions expect AL to be 0 when it's not supported.
Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
}
}
}
-VOID DosBreakInterrupt(LPWORD Stack)
+VOID WINAPI DosBreakInterrupt(LPWORD Stack)
{
UNREFERENCED_PARAMETER(Stack);
+ /* Stop the VDM */
VdmRunning = FALSE;
}
+VOID WINAPI DosFastConOut(LPWORD Stack)
+{
+ /*
+ * This is the DOS 2+ Fast Console Output Interrupt.
+ * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
+ * for more information.
+ */
+ UNREFERENCED_PARAMETER(Stack);
+
+ /*
+ * The default handler under DOS 2.x and 3.x simply calls INT 10/AH=0Eh.
+ * Do better and call directly BiosPrintCharacter: it's what INT 10/AH=0Eh
+ * does. Otherwise we would have to set BL to DOS_CHAR_ATTRIBUTE and
+ * BH to Bda->VideoPage.
+ */
+ BiosPrintCharacter(getAL(), DOS_CHAR_ATTRIBUTE, Bda->VideoPage);
+}
+
+VOID WINAPI DosInt2Fh(LPWORD Stack)
+{
+ DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+ getAH(), getAL());
+ Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+}
+
BOOLEAN DosInitialize(VOID)
{
BYTE i;
/* Set the drive */
CurrentDrive = DosDirectory[0] - 'A';
- /* Get the path */
+ /* Get the directory part of the path */
Path = strchr(DosDirectory, '\\');
if (Path != NULL)
{
}
/* Set the directory */
- if (Path != NULL) strcpy(CurrentDirectories[CurrentDrive], Path);
+ if (Path != NULL)
+ {
+ strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
+ }
/* Read CONFIG.SYS */
Stream = _wfopen(DOS_CONFIG_PATH, L"r");
DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
+ /* Register the DOS BOPs */
+ RegisterBop(BOP_DOS, DosSystemBop );
+ RegisterBop(BOP_CMD, DosCmdInterpreterBop);
+
+ /* Register the DOS 32-bit Interrupts */
+ RegisterInt32(0x20, DosInt20h );
+ RegisterInt32(0x21, DosInt21h );
+// RegisterInt32(0x22, DosInt22h ); // Termination
+ RegisterInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
+// RegisterInt32(0x24, DosInt24h ); // Critical Error
+ RegisterInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
+ RegisterInt32(0x2F, DosInt2Fh );
+
return TRUE;
}