From 4784c22d5087aa87f80d37d3305bbc80dafd07b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Tue, 29 Oct 2013 01:55:54 +0000 Subject: [PATCH] [NTVDM]: DOS: - The DosErrorLevel is stored as a WORD: its LOWORD is the return code and the HIWORD is the termination code. - When copying CurrentDirectories[...], be sure that we copy maximum DOS_DIR_LENGTH chars. - Implement (or stubplement) bunch of INT 21h functions: 0x03, 0x04, 0x05, 0x0C, 0x0D, 0x26, 0x37, 0x47, 0x4D and 0x50. Functions 0x18, 0x1D, 0x1E and 0x20 are NULL functions present in DOS for CP/M compatibility only. - Fix DOS version querying. - Use set/getAX() instead of EmulatorSet/GetRegister, and use setCF instead of EmulatorSetFlag. svn path=/branches/ntvdm/; revision=60790 --- subsystems/ntvdm/dos.c | 404 +++++++++++++++++++++++++++++++++++------ subsystems/ntvdm/dos.h | 3 +- 2 files changed, 349 insertions(+), 58 deletions(-) diff --git a/subsystems/ntvdm/dos.c b/subsystems/ntvdm/dos.c index 0ada9417f2e..79b56922f69 100644 --- a/subsystems/ntvdm/dos.c +++ b/subsystems/ntvdm/dos.c @@ -28,7 +28,7 @@ static HANDLE DosSystemFileTable[DOS_SFT_SIZE]; static WORD DosSftRefCount[DOS_SFT_SIZE]; static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT; static BOOLEAN DosUmbLinked = FALSE; -static BYTE DosErrorLevel = 0; +static WORD DosErrorLevel = 0x0000; /* PRIVATE FUNCTIONS **********************************************************/ @@ -790,6 +790,24 @@ WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset) 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(hFile); + // else + return (BOOLEAN)FlushFileBuffers(Handle); +} + BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle) { BYTE SftIndex; @@ -940,9 +958,15 @@ BOOLEAN DosChangeDirectory(LPSTR Directory) } /* 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; } @@ -963,8 +987,8 @@ VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WOR /* 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; @@ -1260,8 +1284,8 @@ Done: if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE; } - /* Save the return code */ - DosErrorLevel = ReturnCode; + /* Save the return code - Normal termination */ + DosErrorLevel = MAKEWORD(ReturnCode, 0x00); /* Return control to the parent process */ EmulatorExecute(HIWORD(PspBlock->TerminateAddress), @@ -1371,7 +1395,7 @@ VOID DosInt20h(LPWORD Stack) VOID DosInt21h(LPWORD Stack) { - CHAR Character; + BYTE Character; SYSTEMTIME SystemTime; PCHAR String; PDOS_INPUT_BUFFER InputBuffer; @@ -1386,38 +1410,68 @@ VOID DosInt21h(LPWORD Stack) 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) { @@ -1425,8 +1479,8 @@ VOID DosInt21h(LPWORD Stack) 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); @@ -1443,27 +1497,27 @@ VOID DosInt21h(LPWORD Stack) { /* 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()); @@ -1475,8 +1529,8 @@ VOID DosInt21h(LPWORD Stack) } /* - * 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('$'); @@ -1494,7 +1548,7 @@ VOID DosInt21h(LPWORD Stack) 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); @@ -1516,6 +1570,49 @@ VOID DosInt21h(LPWORD Stack) 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: { @@ -1524,6 +1621,22 @@ VOID DosInt21h(LPWORD Stack) 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: { @@ -1538,6 +1651,42 @@ VOID DosInt21h(LPWORD Stack) 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: { @@ -1545,11 +1694,17 @@ VOID DosInt21h(LPWORD Stack) /* Write the new far pointer to the IDT */ ((PDWORD)BaseAddress)[getAL()] = FarPointer; - break; } - /* Get system date */ + /* Create New PSP */ + case 0x26: + { + DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n"); + break; + } + + /* Get System Date */ case 0x2A: { GetLocalTime(&SystemTime); @@ -1559,7 +1714,7 @@ VOID DosInt21h(LPWORD Stack) break; } - /* Set system date */ + /* Set System Date */ case 0x2B: { GetLocalTime(&SystemTime); @@ -1572,7 +1727,7 @@ VOID DosInt21h(LPWORD Stack) break; } - /* Get system time */ + /* Get System Time */ case 0x2C: { GetLocalTime(&SystemTime); @@ -1581,7 +1736,7 @@ VOID DosInt21h(LPWORD Stack) break; } - /* Set system time */ + /* Set System Time */ case 0x2D: { GetLocalTime(&SystemTime); @@ -1608,23 +1763,35 @@ VOID DosInt21h(LPWORD Stack) { 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); - } + /* + * 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(0xFF); + setBH(0x02); } + if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01) + { + /* + * Return version flag: + * 1 << 3 if DOS is in ROM, + * 0 (reserved) if not. + */ + setBH(0x00); + } + + /* Return DOS 24-bit user serial number in BL:CX */ + setBL(0x00); + setCX(0x0000); + /* Return DOS version: Minor:Major in AH:AL */ setAX(PspBlock->DosVersion); @@ -1642,6 +1809,60 @@ VOID DosInt21h(LPWORD Stack) 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 + { + setAL(0xFF); + } + + break; + } + /* Create Directory */ case 0x39: { @@ -1756,7 +1977,7 @@ VOID DosInt21h(LPWORD Stack) break; } - /* Read File */ + /* Read from File or Device */ case 0x3F: { WORD Handle = getBX(); @@ -1770,17 +1991,20 @@ VOID DosInt21h(LPWORD Stack) while (Stack[STACK_COUNTER] < Count) { /* Read a character from the BIOS */ - Buffer[Stack[STACK_COUNTER]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks! + // FIXME: Security checks! + Buffer[Stack[STACK_COUNTER]] = LOBYTE(BiosGetCharacter()); /* Stop if the BOP needs to be repeated */ - if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break; + if (getCF()) break; /* Increment the counter */ Stack[STACK_COUNTER]++; } - if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY; - else BytesRead = Count; + if (Stack[STACK_COUNTER] < Count) + ErrorCode = ERROR_NOT_READY; + else + BytesRead = Count; } else { @@ -1788,7 +2012,7 @@ VOID DosInt21h(LPWORD Stack) ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead); } - if (ErrorCode == 0) + if (ErrorCode == ERROR_SUCCESS) { Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; setAX(BytesRead); @@ -1801,7 +2025,7 @@ VOID DosInt21h(LPWORD Stack) break; } - /* Write File */ + /* Write to File or Device */ case 0x40: { WORD BytesWritten = 0; @@ -1810,7 +2034,7 @@ VOID DosInt21h(LPWORD Stack) getCX(), &BytesWritten); - if (ErrorCode == 0) + if (ErrorCode == ERROR_SUCCESS) { Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; setAX(BytesWritten); @@ -1833,6 +2057,11 @@ VOID DosInt21h(LPWORD Stack) 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 { @@ -1852,7 +2081,7 @@ VOID DosInt21h(LPWORD Stack) getAL(), &NewLocation); - if (ErrorCode == 0) + if (ErrorCode == ERROR_SUCCESS) { Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; @@ -1885,12 +2114,13 @@ VOID DosInt21h(LPWORD Stack) { 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) { @@ -1977,6 +2207,42 @@ VOID DosInt21h(LPWORD Stack) 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: { @@ -2040,7 +2306,28 @@ VOID DosInt21h(LPWORD Stack) 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; + } + + /* Get Current Process ID (Get PSP Address) */ case 0x51: { setBX(CurrentPsp); @@ -2221,7 +2508,7 @@ BOOLEAN DosInitialize(VOID) /* Set the drive */ CurrentDrive = DosDirectory[0] - 'A'; - /* Get the path */ + /* Get the directory part of the path */ Path = strchr(DosDirectory, '\\'); if (Path != NULL) { @@ -2230,7 +2517,10 @@ BOOLEAN DosInitialize(VOID) } /* 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"); diff --git a/subsystems/ntvdm/dos.h b/subsystems/ntvdm/dos.h index ea0ad88f01a..880ac6629e0 100644 --- a/subsystems/ntvdm/dos.h +++ b/subsystems/ntvdm/dos.h @@ -102,7 +102,8 @@ typedef struct _DOS_PSP typedef struct _DOS_INPUT_BUFFER { - BYTE MaxLength, Length; + BYTE MaxLength; + BYTE Length; CHAR Buffer[ANYSIZE_ARRAY]; } DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;