2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
29 #include "bios/bios.h"
32 #include "hardware/ps2.h"
36 /* PRIVATE VARIABLES **********************************************************/
38 CALLBACK16 DosContext
;
40 /* PUBLIC VARIABLES ***********************************************************/
42 /* Global DOS data area contained in guest memory */
44 /* Easy accessors to useful DOS data area parts */
48 /* PRIVATE FUNCTIONS **********************************************************/
50 static BOOLEAN
DosChangeDrive(BYTE Drive
)
52 CHAR DirectoryPath
[DOS_CMDLINE_LENGTH
+ 1];
54 /* Make sure the drive exists */
55 if (Drive
>= SysVars
->NumLocalDrives
) return FALSE
;
57 RtlZeroMemory(DirectoryPath
, sizeof(DirectoryPath
));
59 /* Find the path to the new current directory */
60 snprintf(DirectoryPath
,
64 DosData
->CurrentDirectories
[Drive
]);
66 /* Change the current directory of the process */
67 if (!SetCurrentDirectoryA(DirectoryPath
)) return FALSE
;
69 /* Set the current drive */
70 Sda
->CurrentDrive
= Drive
;
76 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
81 CHAR CurrentDirectory
[MAX_PATH
];
82 CHAR DosDirectory
[DOS_DIR_LENGTH
];
84 /* Make sure the directory path is not too long */
85 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
87 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
91 /* Check whether the directory string is of format "X:..." */
92 if (strlen(Directory
) >= 2 && Directory
[1] == ':')
94 /* Get the drive number */
95 DriveNumber
= RtlUpperChar(Directory
[0]) - 'A';
97 /* Make sure the drive exists */
98 if (DriveNumber
>= SysVars
->NumLocalDrives
)
100 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
106 /* Keep the current drive number */
107 DriveNumber
= Sda
->CurrentDrive
;
110 /* Get the file attributes */
111 Attributes
= GetFileAttributesA(Directory
);
113 /* Make sure the path exists and is a directory */
114 if ((Attributes
== INVALID_FILE_ATTRIBUTES
) ||
115 !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
117 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
121 /* Check if this is the current drive */
122 if (DriveNumber
== Sda
->CurrentDrive
)
124 /* Change the directory */
125 if (!SetCurrentDirectoryA(Directory
))
127 Sda
->LastErrorCode
= LOWORD(GetLastError());
132 /* Get the (possibly new) current directory (needed if we specified a relative directory) */
133 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
135 // TODO: Use some kind of default path?
139 /* Convert it to a DOS path */
140 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
142 // TODO: Use some kind of default path?
146 /* Get the directory part of the path and set the current directory for the drive */
147 Path
= strchr(DosDirectory
, '\\');
150 Path
++; // Skip the backslash
151 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
155 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
162 /* PUBLIC FUNCTIONS ***********************************************************/
164 BOOLEAN
DosControlBreak(VOID
)
168 /* Call interrupt 0x23 */
169 Int32Call(&DosContext
, 0x23);
173 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
180 VOID WINAPI
DosInt20h(LPWORD Stack
)
183 * This is the exit interrupt (alias to INT 21h, AH=00h).
184 * CS must be the PSP segment.
186 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
189 VOID WINAPI
DosInt21h(LPWORD Stack
)
192 SYSTEMTIME SystemTime
;
197 /* Save the value of SS:SP on entry in the PSP */
198 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
199 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
201 /* Check the value in the AH register */
204 /* Terminate Program */
207 /* CS must be the PSP segment */
208 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
212 /* Read Character from STDIN with Echo */
215 DPRINT("INT 21h, AH = 01h\n");
217 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, TRUE
);
218 if (Character
== 0x03 && DosControlBreak()) break;
224 /* Write Character to STDOUT */
227 // FIXME: Under DOS 2+, output handle may be redirected!!!!
229 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
232 * We return the output character (DOS 2.1+).
233 * Also, if we're going to output a TAB, then
234 * don't return a TAB but a SPACE instead.
235 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
236 * for more information.
238 setAL(Character
== '\t' ? ' ' : Character
);
242 /* Read Character from STDAUX */
245 // FIXME: Really read it from STDAUX!
246 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
247 // setAL(DosReadCharacter());
251 /* Write Character to STDAUX */
254 // FIXME: Really write it to STDAUX!
255 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
256 // DosPrintCharacter(getDL());
260 /* Write Character to Printer */
263 // FIXME: Really write it to printer!
264 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
265 DPRINT1("0x%p\n", getDL());
266 DPRINT1("\n\n-----------\n\n");
270 /* Direct Console I/O */
275 // FIXME: Under DOS 2+, output handle may be redirected!!!!
277 if (Character
!= 0xFF)
280 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
283 * We return the output character (DOS 2.1+).
284 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
285 * for more information.
294 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
295 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
299 /* No character available */
300 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
308 /* Direct Character Input without Echo */
311 DPRINT("Direct char input without echo\n");
312 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
316 /* Character Input without Echo */
319 DPRINT("Char input without echo\n");
321 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
);
322 if (Character
== 0x03 && DosControlBreak()) break;
328 /* Write String to STDOUT */
331 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
333 while (*String
!= '$')
335 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
340 * We return the terminating character (DOS 2.1+).
341 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
342 * for more information.
344 setAL('$'); // *String
348 /* Read Buffered Input */
351 PDOS_INPUT_BUFFER InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
353 DPRINT("Read Buffered Input\n");
354 if (InputBuffer
->MaxLength
== 0) break;
356 /* Read from standard input */
357 InputBuffer
->Length
= DosReadLineBuffered(
359 MAKELONG(getDX() + FIELD_OFFSET(DOS_INPUT_BUFFER
, Buffer
), getDS()),
360 InputBuffer
->MaxLength
366 /* Get STDIN Status */
369 setAL(DosCheckInput() ? 0xFF : 0x00);
373 /* Flush Buffer and Read STDIN */
376 BYTE InputFunction
= getAL();
378 /* Flush STDIN buffer */
379 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
382 * If the input function number contained in AL is valid, i.e.
383 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
384 * recursively with AL == AH.
386 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
387 InputFunction
== 0x07 || InputFunction
== 0x08 ||
388 InputFunction
== 0x0A)
390 /* Call ourselves recursively */
391 setAH(InputFunction
);
400 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
402 // TODO: Flush what's needed.
403 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
405 /* Clear CF in DOS 6 only */
406 if (PspBlock
->DosVersion
== 0x0006)
407 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
412 /* Set Default Drive */
415 DosChangeDrive(getDL());
416 setAL(SysVars
->NumLocalDrives
);
420 /* NULL Function for CP/M Compatibility */
424 * This function corresponds to the CP/M BDOS function
425 * "get bit map of logged drives", which is meaningless
428 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
429 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
430 * for more information.
436 /* Get Default Drive */
439 setAL(Sda
->CurrentDrive
);
443 /* Set Disk Transfer Area */
446 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
450 /* NULL Function for CP/M Compatibility */
455 * Function 0x1D corresponds to the CP/M BDOS function
456 * "get bit map of read-only drives", which is meaningless
458 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
459 * for more information.
461 * Function 0x1E corresponds to the CP/M BDOS function
462 * "set file attributes", which was meaningless under MS-DOS 1.x.
463 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
464 * for more information.
470 /* NULL Function for CP/M Compatibility */
474 * This function corresponds to the CP/M BDOS function
475 * "get/set default user (sublibrary) number", which is meaningless
478 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
479 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
480 * for more information.
486 /* Set Interrupt Vector */
489 ULONG FarPointer
= MAKELONG(getDX(), getDS());
490 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
491 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
493 /* Write the new far pointer to the IDT */
494 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
501 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */
502 DosClonePsp(getDX(), Stack
[STACK_CS
]);
506 /* Parse Filename into FCB */
509 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
510 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
511 BYTE Options
= getAL();
515 if (FileName
[1] == ':')
517 /* Set the drive number */
518 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
520 /* Skip to the file name part */
525 /* No drive number specified */
526 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
527 else Fcb
->DriveNumber
= 0;
530 /* Parse the file name */
532 while ((*FileName
> 0x20) && (i
< 8))
534 if (*FileName
== '.') break;
535 else if (*FileName
== '*')
541 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
544 /* Fill the whole field with blanks only if bit 2 is not set */
545 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
547 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
550 /* Skip to the extension part */
551 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
552 if (*FileName
== '.') FileName
++;
554 /* Now parse the extension */
558 while ((*FileName
> 0x20) && (i
< 3))
560 if (*FileName
== '*')
566 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
569 /* Fill the whole field with blanks only if bit 3 is not set */
570 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
572 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
578 /* Get System Date */
581 GetLocalTime(&SystemTime
);
582 setCX(SystemTime
.wYear
);
583 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
584 setAL(SystemTime
.wDayOfWeek
);
588 /* Set System Date */
591 GetLocalTime(&SystemTime
);
592 SystemTime
.wYear
= getCX();
593 SystemTime
.wMonth
= getDH();
594 SystemTime
.wDay
= getDL();
596 /* Return success or failure */
597 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
601 /* Get System Time */
604 GetLocalTime(&SystemTime
);
605 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
606 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
610 /* Set System Time */
613 GetLocalTime(&SystemTime
);
614 SystemTime
.wHour
= getCH();
615 SystemTime
.wMinute
= getCL();
616 SystemTime
.wSecond
= getDH();
617 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
619 /* Return success or failure */
620 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
624 /* Get Disk Transfer Area */
627 setES(HIWORD(Sda
->DiskTransferArea
));
628 setBX(LOWORD(Sda
->DiskTransferArea
));
632 /* Get DOS Version */
635 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
638 * DOS 2+ - GET DOS VERSION
639 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
640 * for more information.
643 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
646 * Return DOS OEM number:
647 * 0x00 for IBM PC-DOS
648 * 0x02 for packaged MS-DOS
654 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
657 * Return version flag:
658 * 1 << 3 if DOS is in ROM,
659 * 0 (reserved) if not.
664 /* Return DOS 24-bit user serial number in BL:CX */
669 * Return DOS version: Minor:Major in AH:AL
670 * The Windows NT DOS box returns version 5.00, subject to SETVER.
672 setAX(PspBlock
->DosVersion
);
677 /* Terminate and Stay Resident */
680 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
681 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
685 /* Extended functionalities */
691 * DOS 4+ - GET BOOT DRIVE
695 setDL(SysVars
->BootDrive
);
700 * DOS 5+ - GET TRUE VERSION NUMBER
701 * This function always returns the true version number, unlike
702 * AH=30h, whose return value may be changed with SETVER.
703 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
704 * for more information.
709 * Return the true DOS version: Minor:Major in BH:BL
710 * The Windows NT DOS box returns BX=3205h (version 5.50).
712 setBX(NTDOS_VERSION
);
723 default: // goto Default;
725 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
733 /* Get Address of InDOS flag */
736 setES(DOS_DATA_SEGMENT
);
737 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
741 /* Get Interrupt Vector */
744 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
746 /* Read the address from the IDT into ES:BX */
747 setES(HIWORD(FarPointer
));
748 setBX(LOWORD(FarPointer
));
752 /* Get Free Disk Space */
755 CHAR RootPath
[] = "?:\\";
756 DWORD SectorsPerCluster
;
757 DWORD BytesPerSector
;
758 DWORD NumberOfFreeClusters
;
759 DWORD TotalNumberOfClusters
;
762 RootPath
[0] = 'A' + Sda
->CurrentDrive
;
764 RootPath
[0] = 'A' + getDL() - 1;
766 if (GetDiskFreeSpaceA(RootPath
,
769 &NumberOfFreeClusters
,
770 &TotalNumberOfClusters
))
772 setAX(LOWORD(SectorsPerCluster
));
773 setCX(LOWORD(BytesPerSector
));
774 setBX(min(NumberOfFreeClusters
, 0xFFFF));
775 setDX(min(TotalNumberOfClusters
, 0xFFFF));
786 /* SWITCH character - AVAILDEV */
792 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
793 * This setting is ignored by MS-DOS 4.0+.
794 * MS-DOS 5+ always return AL=00h/DL=2Fh.
795 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
796 * for more information.
804 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
805 * This setting is ignored by MS-DOS 5+.
806 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
807 * for more information.
815 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
816 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
817 * for more information.
825 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
826 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
827 * for more information.
834 /* Invalid subfunction */
843 /* Get/Set Country-dependent Information */
846 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
849 ErrorCode
= DosGetCountryInfo(&CountryId
,
850 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
852 if (ErrorCode
== ERROR_SUCCESS
)
854 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
859 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
866 /* Create Directory */
869 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
871 if (CreateDirectoryA(String
, NULL
))
873 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
877 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
878 setAX(LOWORD(GetLastError()));
884 /* Remove Directory */
887 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
889 if (RemoveDirectoryA(String
))
891 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
895 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
896 setAX(LOWORD(GetLastError()));
902 /* Set Current Directory */
905 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
907 if (DosChangeDirectory(String
))
909 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
913 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
914 setAX(Sda
->LastErrorCode
);
920 /* Create or Truncate File */
924 WORD ErrorCode
= DosCreateFile(&FileHandle
,
925 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
929 if (ErrorCode
== ERROR_SUCCESS
)
931 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
936 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
943 /* Open File or Device */
947 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
948 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
950 if (ErrorCode
== ERROR_SUCCESS
)
952 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
957 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
964 /* Close File or Device */
967 if (DosCloseHandle(getBX()))
969 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
973 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
974 setAX(ERROR_INVALID_HANDLE
);
980 /* Read from File or Device */
986 DPRINT("DosReadFile(0x%04X)\n", getBX());
988 ErrorCode
= DosReadFile(getBX(),
989 MAKELONG(getDX(), getDS()),
993 if (ErrorCode
== ERROR_SUCCESS
)
995 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
998 else if (ErrorCode
!= ERROR_NOT_READY
)
1000 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1007 /* Write to File or Device */
1010 WORD BytesWritten
= 0;
1011 WORD ErrorCode
= DosWriteFile(getBX(),
1012 MAKELONG(getDX(), getDS()),
1016 if (ErrorCode
== ERROR_SUCCESS
)
1018 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1019 setAX(BytesWritten
);
1023 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1033 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1035 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1037 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1039 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1040 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1042 setAL(RtlUpperChar(FileName
[0]) - 'A');
1046 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1047 setAX(GetLastError());
1057 WORD ErrorCode
= DosSeekFile(getBX(),
1058 MAKELONG(getDX(), getCX()),
1062 if (ErrorCode
== ERROR_SUCCESS
)
1064 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1066 /* Return the new offset in DX:AX */
1067 setDX(HIWORD(NewLocation
));
1068 setAX(LOWORD(NewLocation
));
1072 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1079 /* Get/Set File Attributes */
1083 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1085 if (getAL() == 0x00)
1087 /* Get the attributes */
1088 Attributes
= GetFileAttributesA(FileName
);
1090 /* Check if it failed */
1091 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1093 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1094 setAX(GetLastError());
1098 /* Return the attributes that DOS can understand */
1099 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1100 setCX(Attributes
& 0x00FF);
1103 else if (getAL() == 0x01)
1105 /* Try to set the attributes */
1106 if (SetFileAttributesA(FileName
, getCL()))
1108 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1112 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1113 setAX(GetLastError());
1118 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1119 setAX(ERROR_INVALID_FUNCTION
);
1128 WORD Length
= getCX();
1130 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1132 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1137 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1138 setAX(Sda
->LastErrorCode
);
1144 /* Duplicate Handle */
1147 WORD NewHandle
= DosDuplicateHandle(getBX());
1149 if (NewHandle
!= INVALID_DOS_HANDLE
)
1152 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1156 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1157 setAX(Sda
->LastErrorCode
);
1163 /* Force Duplicate Handle */
1166 if (DosForceDuplicateHandle(getBX(), getCX()))
1168 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1172 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1173 setAX(ERROR_INVALID_HANDLE
);
1179 /* Get Current Directory */
1182 BYTE DriveNumber
= getDL();
1183 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1185 /* Get the real drive number */
1186 if (DriveNumber
== 0)
1188 DriveNumber
= Sda
->CurrentDrive
;
1192 /* Decrement DriveNumber since it was 1-based */
1196 if (DriveNumber
< SysVars
->NumLocalDrives
)
1199 * Copy the current directory into the target buffer.
1200 * It doesn't contain the drive letter and the backslash.
1202 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1203 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1204 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1208 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1209 setAX(ERROR_INVALID_DRIVE
);
1215 /* Allocate Memory */
1218 WORD MaxAvailable
= 0;
1219 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1223 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1228 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1229 setAX(Sda
->LastErrorCode
);
1230 setBX(MaxAvailable
);
1239 if (DosFreeMemory(getES()))
1241 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1245 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1246 setAX(Sda
->LastErrorCode
);
1252 /* Resize Memory Block */
1257 if (DosResizeMemory(getES(), getBX(), &Size
))
1259 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1263 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1264 setAX(Sda
->LastErrorCode
);
1274 BYTE OrgAL
= getAL();
1275 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1276 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1279 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1281 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1283 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1285 /* Create a new process */
1286 ErrorCode
= DosCreateProcess(ProgramName
,
1288 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1292 /* Just load an executable */
1293 ErrorCode
= DosLoadExecutable(LoadType
,
1298 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1301 else if (OrgAL
== 0x05)
1303 // http://www.ctyme.com/intr/rb-2942.htm
1304 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1305 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1309 ErrorCode
= ERROR_INVALID_FUNCTION
;
1312 if (ErrorCode
== ERROR_SUCCESS
)
1314 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1318 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1325 /* Terminate with Return Code */
1328 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1332 /* Get Return Code (ERRORLEVEL) */
1336 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1337 * DosErrorLevel is cleared after being read by this function.
1339 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1340 setAX(Sda
->ErrorLevel
);
1341 Sda
->ErrorLevel
= 0x0000; // Clear it
1345 /* Find First File */
1348 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1349 SEG_OFF_TO_PTR(getDS(), getDX()),
1354 if (Result
== ERROR_SUCCESS
)
1355 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1357 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1362 /* Find Next File */
1365 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1369 if (Result
== ERROR_SUCCESS
)
1370 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1372 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1377 /* Internal - Set Current Process ID (Set PSP Address) */
1380 DosSetProcessContext(getBX());
1384 /* Internal - Get Current Process ID (Get PSP Address) */
1386 /* Get Current PSP Address */
1390 * Undocumented AH=51h is identical to the documented AH=62h.
1391 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1392 * and http://www.ctyme.com/intr/rb-3140.htm
1393 * for more information.
1395 setBX(Sda
->CurrentPsp
);
1399 /* Internal - Get "List of lists" (SYSVARS) */
1403 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1404 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1405 * for more information.
1408 /* Return the DOS "list of lists" in ES:BX */
1409 setES(DOS_DATA_SEGMENT
);
1410 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1414 /* Create Child PSP */
1417 DosCreatePsp(getDX(), getSI());
1418 DosSetProcessContext(getDX());
1425 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1426 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1429 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1430 * for more information.
1433 if (MoveFileA(ExistingFileName
, NewFileName
))
1435 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1439 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1440 setAX(GetLastError());
1446 /* File Attributes */
1451 /* Get File's last-written Date and Time */
1454 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1455 FILETIME LastWriteTime
;
1456 WORD FileDate
, FileTime
;
1458 if (Descriptor
== NULL
)
1460 /* Invalid handle */
1461 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1462 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1463 setAX(ERROR_INVALID_HANDLE
);
1467 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1469 /* Invalid for devices */
1470 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1471 // setAX(ERROR_INVALID_FUNCTION);
1472 setAX(ERROR_INVALID_HANDLE
);
1477 * Retrieve the last-written Win32 date and time,
1478 * and convert it to DOS format.
1480 if (!GetFileTime(Descriptor
->Win32Handle
,
1481 NULL
, NULL
, &LastWriteTime
) ||
1482 !FileTimeToDosDateTime(&LastWriteTime
,
1483 &FileDate
, &FileTime
))
1485 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1486 setAX(GetLastError());
1490 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1496 /* Set File's last-written Date and Time */
1499 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1500 FILETIME LastWriteTime
;
1501 WORD FileDate
= getDX();
1502 WORD FileTime
= getCX();
1504 if (Descriptor
== NULL
)
1506 /* Invalid handle */
1507 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1508 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1509 setAX(ERROR_INVALID_HANDLE
);
1513 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1515 /* Invalid for devices */
1516 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1517 // setAX(ERROR_INVALID_FUNCTION);
1518 setAX(ERROR_INVALID_HANDLE
);
1523 * Convert the new last-written DOS date and time
1524 * to Win32 format and set it.
1526 if (!DosDateTimeToFileTime(FileDate
, FileTime
,
1528 !SetFileTime(Descriptor
->Win32Handle
,
1529 NULL
, NULL
, &LastWriteTime
))
1531 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1532 setAX(GetLastError());
1536 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1540 default: // goto Default;
1542 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1550 /* Get/Set Memory Management Options */
1555 /* Get allocation strategy */
1558 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1559 setAX(Sda
->AllocStrategy
);
1563 /* Set allocation strategy */
1566 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1567 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1569 /* Can't set both */
1570 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1571 setAX(ERROR_INVALID_PARAMETER
);
1575 if ((getBL() & ~(DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1576 > DOS_ALLOC_LAST_FIT
)
1578 /* Invalid allocation strategy */
1579 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1580 setAX(ERROR_INVALID_PARAMETER
);
1584 Sda
->AllocStrategy
= getBL();
1585 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1589 /* Get UMB link state */
1592 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1593 setAL(SysVars
->UmbLinked
? 0x01 : 0x00);
1597 /* Set UMB link state */
1603 Success
= DosLinkUmb();
1605 Success
= DosUnlinkUmb();
1608 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1610 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1615 /* Invalid or unsupported function */
1618 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1619 setAX(ERROR_INVALID_FUNCTION
);
1626 /* Get Extended Error Information */
1629 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1634 /* Create Temporary File */
1637 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1638 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1644 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1645 * for more information.
1648 // FIXME: Check for buffer validity?
1649 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1650 // to receive the generated filename.
1652 /* First create the temporary file */
1653 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1656 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1657 setAX(GetLastError());
1661 /* Now try to open it in read/write access */
1662 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1663 if (ErrorCode
== ERROR_SUCCESS
)
1665 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1670 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1677 /* Create New File */
1681 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1682 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1686 if (ErrorCode
== ERROR_SUCCESS
)
1688 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1693 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1700 /* Lock/Unlock Region of File */
1703 if (getAL() == 0x00)
1705 /* Lock region of file */
1706 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1708 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1712 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1713 setAX(Sda
->LastErrorCode
);
1716 else if (getAL() == 0x01)
1718 /* Unlock region of file */
1719 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1721 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1725 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1726 setAX(Sda
->LastErrorCode
);
1731 /* Invalid subfunction */
1732 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1733 setAX(ERROR_INVALID_FUNCTION
);
1739 /* Canonicalize File Name or Path */
1743 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1744 * for more information.
1748 * We suppose that the DOS app gave to us a valid
1749 * 128-byte long buffer for the canonicalized name.
1751 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1753 SEG_OFF_TO_PTR(getES(), getDI()),
1757 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1758 setAX(GetLastError());
1762 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1766 // FIXME: Convert the full path name into short version.
1767 // We cannot reliably use GetShortPathName, because it fails
1768 // if the path name given doesn't exist. However this DOS
1769 // function AH=60h should be able to work even for non-existing
1770 // path and file names.
1775 /* Miscellaneous Internal Functions */
1780 /* Get Swappable Data Area */
1783 setDS(DOS_DATA_SEGMENT
);
1784 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1785 setCX(sizeof(DOS_SDA
));
1786 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1788 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1792 default: // goto Default;
1794 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1802 /* Extended Country Information */
1807 case 0x01: case 0x02: case 0x03:
1808 case 0x04: case 0x05: case 0x06:
1811 WORD BufferSize
= getCX();
1813 ErrorCode
= DosGetCountryInfoEx(getAL(),
1816 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1818 if (ErrorCode
== ERROR_SUCCESS
)
1820 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1825 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1832 /* Country-dependent Character Capitalization -- Character */
1834 /* Country-dependent Filename Capitalization -- Character */
1837 setDL(DosToUpper(getDL()));
1838 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1839 // setAX(ERROR_SUCCESS);
1843 /* Country-dependent Character Capitalization -- Counted ASCII String */
1845 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1848 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1849 // FIXME: Check for NULL ptr!!
1850 DosToUpperStrN(Str
, Str
, getCX());
1851 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1852 // setAX(ERROR_SUCCESS);
1856 /* Country-dependent Character Capitalization -- ASCIIZ String */
1858 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1861 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1862 // FIXME: Check for NULL ptr!!
1863 DosToUpperStrZ(Str
, Str
);
1864 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1865 // setAX(ERROR_SUCCESS);
1869 /* Determine if Character represents YES/NO Response */
1872 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1873 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1877 default: // goto Default;
1879 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1887 /* Set Handle Count */
1890 if (!DosResizeHandleTable(getBX()))
1892 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1893 setAX(Sda
->LastErrorCode
);
1895 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1905 * Function 6Ah is identical to function 68h,
1906 * and sets AH to 68h if success.
1907 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1908 * for more information.
1912 if (DosFlushFileBuffers(getBX()))
1914 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1918 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1919 setAX(GetLastError());
1925 /* Extended Open/Create */
1929 WORD CreationStatus
;
1932 /* Check for AL == 00 */
1933 if (getAL() != 0x00)
1935 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1936 setAX(ERROR_INVALID_FUNCTION
);
1941 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1942 * for the full detailed description.
1944 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1947 ErrorCode
= DosCreateFileEx(&FileHandle
,
1949 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1954 if (ErrorCode
== ERROR_SUCCESS
)
1956 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1957 setCX(CreationStatus
);
1962 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1970 default: // Default:
1972 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
1975 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1976 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1983 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
1985 /* Set CF to terminate the running process */
1986 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1989 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
1992 * This call should leave the flags on the stack for some reason,
1993 * so move the stack by one word.
1994 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
1996 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
1997 Stack
[STACK_IP
] = Stack
[STACK_CS
];
1998 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
1999 setSP(LOWORD(getSP() - 2));
2001 // TODO: NOT IMPLEMENTED;
2004 /* General failure */
2006 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2009 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
2012 * This call should leave the flags on the stack for some reason,
2013 * so move the stack by one word.
2014 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2016 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2017 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2018 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2019 setSP(LOWORD(getSP() - 2));
2021 // TODO: NOT IMPLEMENTED;
2024 /* General failure */
2026 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2029 VOID WINAPI
DosInt27h(LPWORD Stack
)
2031 WORD KeepResident
= (getDX() + 0x0F) >> 4;
2033 /* Terminate and Stay Resident. CS must be the PSP segment. */
2034 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident
);
2035 DosTerminateProcess(Stack
[STACK_CS
], 0, KeepResident
);
2038 VOID WINAPI
DosIdle(LPWORD Stack
)
2041 * This will set the carry flag on the first call (to repeat the BOP),
2042 * and clear it in the next, so that exactly one HLT occurs.
2047 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2050 * This is the DOS 2+ Fast Console Output Interrupt.
2051 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2053 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2054 * for more information.
2057 /* Save AX and BX */
2058 USHORT AX
= getAX();
2059 USHORT BX
= getBX();
2062 * Set the parameters:
2063 * AL contains the character to print (already set),
2064 * BL contains the character attribute,
2065 * BH contains the video page to use.
2067 setBL(DOS_CHAR_ATTRIBUTE
);
2068 setBH(Bda
->VideoPage
);
2070 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2072 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2074 /* Restore AX and BX */
2079 VOID WINAPI
DosInt2Ah(LPWORD Stack
)
2081 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2084 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2088 /* DOS 3+ Internal Utility Functions */
2091 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2095 /* Installation Check */
2102 /* Get DOS Data Segment */
2105 setDS(DOS_DATA_SEGMENT
);
2109 /* Compare FAR Pointers */
2112 PVOID PointerFromFarPointer1
= SEG_OFF_TO_PTR(getDS(), getSI());
2113 PVOID PointerFromFarPointer2
= SEG_OFF_TO_PTR(getES(), getDI());
2114 BOOLEAN AreEqual
= (PointerFromFarPointer1
== PointerFromFarPointer2
);
2118 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
2119 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2123 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
2124 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2129 /* Set DOS Version Number to return */
2132 WORD DosVersion
= getDX();
2134 // Special case: return the true DOS version when DX=00h
2135 if (DosVersion
== 0x0000)
2136 DosData
->DosVersion
= DOS_VERSION
;
2138 DosData
->DosVersion
= DosVersion
;
2147 /* Set Disk Interrupt Handler */
2150 /* Save the old values of PrevInt13 and RomBiosInt13 */
2151 ULONG OldInt13
= BiosData
->PrevInt13
;
2152 ULONG OldBiosInt13
= BiosData
->RomBiosInt13
;
2154 /* Set PrevInt13 and RomBiosInt13 to their new values */
2155 BiosData
->PrevInt13
= MAKELONG(getDX(), getDS());
2156 BiosData
->RomBiosInt13
= MAKELONG(getBX(), getES());
2158 /* Return in DS:DX the old value of PrevInt13 */
2159 setDS(HIWORD(OldInt13
));
2160 setDX(LOWORD(OldInt13
));
2162 /* Return in DS:DX the old value of RomBiosInt13 */
2163 setES(HIWORD(OldBiosInt13
));
2164 setBX(LOWORD(OldBiosInt13
));
2169 /* Mostly Windows 2.x/3.x/9x support */
2173 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2174 * Just do nothing in this case.
2176 if (getAL() != 0x80) goto Default
;
2180 /* Extended Memory Specification */
2184 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2188 /* Installation Check */
2191 /* The driver is loaded */
2196 /* Get Driver Address */
2199 setES(HIWORD(DriverEntry
));
2200 setBX(LOWORD(DriverEntry
));
2205 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2214 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2216 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2221 BOOLEAN
DosKRNLInitialize(VOID
)
2226 BOOLEAN Success
= TRUE
;
2228 CHAR CurrentDirectory
[MAX_PATH
];
2229 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2231 static const BYTE NullDriverRoutine
[] =
2233 /* Strategy routine entry */
2234 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2237 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2238 LOBYTE(DOS_DEVSTAT_DONE
),
2239 HIBYTE(DOS_DEVSTAT_DONE
),
2241 /* Interrupt routine entry */
2245 /* Set the data segment */
2246 setDS(DOS_DATA_SEGMENT
);
2248 /* Initialize the global DOS data area */
2249 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2250 RtlZeroMemory(DosData
, sizeof(*DosData
));
2252 /* Initialize the DOS stack */
2253 setSS(DOS_DATA_SEGMENT
);
2254 setSP(DOS_DATA_OFFSET(DosStack
) + sizeof(DosData
->DosStack
) - sizeof(WORD
));
2256 /* Initialize the list of lists */
2257 SysVars
= &DosData
->SysVars
;
2258 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2259 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2260 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2263 * The last drive can be redefined with the LASTDRIVE command.
2264 * At the moment, set the real maximum possible, 'Z'.
2266 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2268 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2269 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2270 SysVars
->BootDrive
= RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]) - 'A' + 1;
2272 /* Initialize the NUL device driver */
2273 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2274 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2275 // Offset from within the DOS data segment
2276 SysVars
->NullDevice
.StrategyRoutine
= DOS_DATA_OFFSET(NullDriverRoutine
);
2277 // Hardcoded to the RETF inside StrategyRoutine
2278 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2279 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2280 sizeof(SysVars
->NullDevice
.DeviceName
),
2282 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2283 RtlCopyMemory(DosData
->NullDriverRoutine
,
2285 sizeof(NullDriverRoutine
));
2287 /* Default DOS version to report */
2288 DosData
->DosVersion
= DOS_VERSION
;
2290 /* Initialize the swappable data area */
2291 Sda
= &DosData
->Sda
;
2292 RtlZeroMemory(Sda
, sizeof(*Sda
));
2294 /* Get the current directory and convert it to a DOS path */
2295 dwRet
= GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
);
2299 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2301 else if (dwRet
> sizeof(CurrentDirectory
))
2304 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet
);
2309 dwRet
= GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
));
2313 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2315 else if (dwRet
> sizeof(DosDirectory
))
2318 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet
);
2324 /* We failed, use the boot drive instead */
2325 DosDirectory
[0] = SysVars
->BootDrive
+ 'A' - 1;
2326 DosDirectory
[1] = ':';
2327 DosDirectory
[2] = '\\';
2328 DosDirectory
[3] = '\0';
2331 /* Set the current drive */
2332 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2334 /* Get the directory part of the path and set the current directory */
2335 Path
= strchr(DosDirectory
, '\\');
2338 Path
++; // Skip the backslash
2339 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2343 DosData
->CurrentDirectories
[Sda
->CurrentDrive
][0] = '\0';
2346 /* Set the current PSP to the system PSP */
2347 Sda
->CurrentPsp
= SYSTEM_PSP
;
2349 /* Initialize the SFT */
2350 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2351 Sft
->Link
= 0xFFFFFFFF;
2352 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2354 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2356 /* Clear the file descriptor entry */
2357 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2360 /* Initialize memory management */
2361 DosInitializeMemory();
2363 /* Initialize the callback context */
2364 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2366 /* Register the DOS 32-bit Interrupts */
2367 RegisterDosInt32(0x20, DosInt20h
);
2368 RegisterDosInt32(0x21, DosInt21h
);
2369 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2370 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2371 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2372 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2373 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2374 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2375 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2376 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2377 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2379 /* Unimplemented DOS interrupts */
2380 RegisterDosInt32(0x2A, DosInt2Ah
); // DOS Critical Sections / Network
2381 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2382 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2383 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2385 /* Reserved DOS interrupts */
2386 RegisterDosInt32(0x2B, NULL
);
2387 RegisterDosInt32(0x2C, NULL
);
2388 RegisterDosInt32(0x2D, NULL
);
2390 /* Initialize country data */
2391 DosCountryInitialize();
2393 /* Load the CON driver */
2396 /* Load the XMS driver (HIMEM) */
2399 /* Load the EMS driver */
2400 if (!EmsDrvInitialize(EMS_SEGMENT
, EMS_TOTAL_PAGES
))
2402 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n"
2403 "Page frame segment or number of EMS pages invalid.\n");
2406 /* Finally initialize the UMBs */