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 *******************************************************************/
31 #include "bios/bios.h"
34 #include "hardware/ps2.h"
38 /* PRIVATE VARIABLES **********************************************************/
40 CALLBACK16 DosContext
;
42 /* PUBLIC VARIABLES ***********************************************************/
44 /* Global DOS data area contained in guest memory */
46 /* Easy accessors to useful DOS data area parts */
50 /* PRIVATE FUNCTIONS **********************************************************/
52 static BOOLEAN
DosChangeDrive(BYTE Drive
)
54 CHAR DirectoryPath
[DOS_CMDLINE_LENGTH
+ 1];
56 /* Make sure the drive exists */
57 if (Drive
>= SysVars
->NumLocalDrives
) return FALSE
;
59 RtlZeroMemory(DirectoryPath
, sizeof(DirectoryPath
));
61 /* Find the path to the new current directory */
62 snprintf(DirectoryPath
,
66 DosData
->CurrentDirectories
[Drive
]);
68 /* Change the current directory of the process */
69 if (!SetCurrentDirectoryA(DirectoryPath
)) return FALSE
;
71 /* Set the current drive */
72 Sda
->CurrentDrive
= Drive
;
78 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
83 CHAR CurrentDirectory
[MAX_PATH
];
84 CHAR DosDirectory
[DOS_DIR_LENGTH
];
86 /* Make sure the directory path is not too long */
87 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
89 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
93 /* Check whether the directory string is of format "X:..." */
94 if (strlen(Directory
) >= 2 && Directory
[1] == ':')
96 /* Get the drive number */
97 DriveNumber
= RtlUpperChar(Directory
[0]) - 'A';
99 /* Make sure the drive exists */
100 if (DriveNumber
>= SysVars
->NumLocalDrives
)
102 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
108 /* Keep the current drive number */
109 DriveNumber
= Sda
->CurrentDrive
;
112 /* Get the file attributes */
113 Attributes
= GetFileAttributesA(Directory
);
115 /* Make sure the path exists and is a directory */
116 if ((Attributes
== INVALID_FILE_ATTRIBUTES
) ||
117 !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
119 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
123 /* Check if this is the current drive */
124 if (DriveNumber
== Sda
->CurrentDrive
)
126 /* Change the directory */
127 if (!SetCurrentDirectoryA(Directory
))
129 Sda
->LastErrorCode
= LOWORD(GetLastError());
134 /* Get the (possibly new) current directory (needed if we specified a relative directory) */
135 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
137 // TODO: Use some kind of default path?
141 /* Convert it to a DOS path */
142 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
144 // TODO: Use some kind of default path?
148 /* Get the directory part of the path and set the current directory for the drive */
149 Path
= strchr(DosDirectory
, '\\');
152 Path
++; // Skip the backslash
153 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
157 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
164 static BOOLEAN
DosIsFileOnCdRom(VOID
)
167 CHAR RootPathName
[4];
169 /* Construct a simple <letter>:\ string to get drive type */
170 RootPathName
[0] = Sda
->CurrentDrive
+ 'A';
171 RootPathName
[1] = ':';
172 RootPathName
[2] = '\\';
173 RootPathName
[3] = ANSI_NULL
;
175 DriveType
= GetDriveTypeA(RootPathName
);
176 return (DriveType
== DRIVE_CDROM
);
179 /* PUBLIC FUNCTIONS ***********************************************************/
181 BOOLEAN
DosControlBreak(VOID
)
185 /* Print an extra newline */
186 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\r');
187 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\n');
189 /* Call interrupt 0x23 */
190 Int32Call(&DosContext
, 0x23);
194 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
201 VOID WINAPI
DosInt20h(LPWORD Stack
)
204 * This is the exit interrupt (alias to INT 21h, AH=00h).
205 * CS must be the PSP segment.
207 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
210 VOID WINAPI
DosInt21h(LPWORD Stack
)
213 SYSTEMTIME SystemTime
;
218 /* Save the value of SS:SP on entry in the PSP */
219 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
220 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
222 /* Check the value in the AH register */
225 /* Terminate Program */
228 /* CS must be the PSP segment */
229 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
233 /* Read Character from STDIN with Echo */
236 DPRINT("INT 21h, AH = 01h\n");
238 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, TRUE
);
239 if (Character
== 0x03 && DosControlBreak()) break;
245 /* Write Character to STDOUT */
248 // FIXME: Under DOS 2+, output handle may be redirected!!!!
250 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
253 * We return the output character (DOS 2.1+).
254 * Also, if we're going to output a TAB, then
255 * don't return a TAB but a SPACE instead.
256 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
257 * for more information.
259 setAL(Character
== '\t' ? ' ' : Character
);
263 /* Read Character from STDAUX */
266 // FIXME: Really read it from STDAUX!
267 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
268 // setAL(DosReadCharacter());
272 /* Write Character to STDAUX */
275 // FIXME: Really write it to STDAUX!
276 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
277 // DosPrintCharacter(getDL());
281 /* Write Character to Printer */
284 // FIXME: Really write it to printer!
285 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
286 DPRINT1("0x%p\n", getDL());
287 DPRINT1("\n\n-----------\n\n");
291 /* Direct Console I/O */
296 // FIXME: Under DOS 2+, output handle may be redirected!!!!
298 if (Character
!= 0xFF)
301 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
304 * We return the output character (DOS 2.1+).
305 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
306 * for more information.
315 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
316 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
320 /* No character available */
321 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
329 /* Direct Character Input without Echo */
332 DPRINT("Direct char input without echo\n");
333 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
337 /* Character Input without Echo */
340 DPRINT("Char input without echo\n");
342 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
);
343 if (Character
== 0x03 && DosControlBreak()) break;
349 /* Write String to STDOUT */
352 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
354 while (*String
!= '$')
356 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
361 * We return the terminating character (DOS 2.1+).
362 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
363 * for more information.
365 setAL('$'); // *String
369 /* Read Buffered Input */
372 PDOS_INPUT_BUFFER InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
374 DPRINT("Read Buffered Input\n");
375 if (InputBuffer
->MaxLength
== 0) break;
377 /* Read from standard input */
378 InputBuffer
->Length
= DosReadLineBuffered(
380 MAKELONG(getDX() + FIELD_OFFSET(DOS_INPUT_BUFFER
, Buffer
), getDS()),
381 InputBuffer
->MaxLength
387 /* Get STDIN Status */
390 setAL(DosCheckInput() ? 0xFF : 0x00);
394 /* Flush Buffer and Read STDIN */
397 BYTE InputFunction
= getAL();
399 /* Flush STDIN buffer */
400 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
403 * If the input function number contained in AL is valid, i.e.
404 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
405 * recursively with AL == AH.
407 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
408 InputFunction
== 0x07 || InputFunction
== 0x08 ||
409 InputFunction
== 0x0A)
411 /* Call ourselves recursively */
412 setAH(InputFunction
);
421 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
423 // TODO: Flush what's needed.
424 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
426 /* Clear CF in DOS 6 only */
427 if (PspBlock
->DosVersion
== 0x0006)
428 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
433 /* Set Default Drive */
436 DosChangeDrive(getDL());
437 setAL(SysVars
->NumLocalDrives
);
441 /* NULL Function for CP/M Compatibility */
445 * This function corresponds to the CP/M BDOS function
446 * "get bit map of logged drives", which is meaningless
449 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
450 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
451 * for more information.
457 /* Get Default Drive */
460 setAL(Sda
->CurrentDrive
);
464 /* Set Disk Transfer Area */
467 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
471 /* NULL Function for CP/M Compatibility */
476 * Function 0x1D corresponds to the CP/M BDOS function
477 * "get bit map of read-only drives", which is meaningless
479 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
480 * for more information.
482 * Function 0x1E corresponds to the CP/M BDOS function
483 * "set file attributes", which was meaningless under MS-DOS 1.x.
484 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
485 * for more information.
491 /* NULL Function for CP/M Compatibility */
495 * This function corresponds to the CP/M BDOS function
496 * "get/set default user (sublibrary) number", which is meaningless
499 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
501 * for more information.
507 /* Set Interrupt Vector */
510 ULONG FarPointer
= MAKELONG(getDX(), getDS());
511 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
512 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
514 /* Write the new far pointer to the IDT */
515 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
522 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */
523 DosClonePsp(getDX(), Stack
[STACK_CS
]);
527 /* Parse Filename into FCB */
530 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
531 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
532 BYTE Options
= getAL();
536 if (FileName
[1] == ':')
538 /* Set the drive number */
539 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
541 /* Skip to the file name part */
546 /* No drive number specified */
547 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
548 else Fcb
->DriveNumber
= 0;
551 /* Parse the file name */
553 while ((*FileName
> 0x20) && (i
< 8))
555 if (*FileName
== '.') break;
556 else if (*FileName
== '*')
562 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
565 /* Fill the whole field with blanks only if bit 2 is not set */
566 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
568 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
571 /* Skip to the extension part */
572 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
573 if (*FileName
== '.') FileName
++;
575 /* Now parse the extension */
579 while ((*FileName
> 0x20) && (i
< 3))
581 if (*FileName
== '*')
587 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
590 /* Fill the whole field with blanks only if bit 3 is not set */
591 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
593 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
599 /* Get System Date */
602 GetLocalTime(&SystemTime
);
603 setCX(SystemTime
.wYear
);
604 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
605 setAL(SystemTime
.wDayOfWeek
);
609 /* Set System Date */
612 GetLocalTime(&SystemTime
);
613 SystemTime
.wYear
= getCX();
614 SystemTime
.wMonth
= getDH();
615 SystemTime
.wDay
= getDL();
617 /* Return success or failure */
618 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
622 /* Get System Time */
625 GetLocalTime(&SystemTime
);
626 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
627 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
631 /* Set System Time */
634 GetLocalTime(&SystemTime
);
635 SystemTime
.wHour
= getCH();
636 SystemTime
.wMinute
= getCL();
637 SystemTime
.wSecond
= getDH();
638 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
640 /* Return success or failure */
641 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
645 /* Get Disk Transfer Area */
648 setES(HIWORD(Sda
->DiskTransferArea
));
649 setBX(LOWORD(Sda
->DiskTransferArea
));
653 /* Get DOS Version */
656 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
659 * DOS 2+ - GET DOS VERSION
660 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
661 * for more information.
664 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
667 * Return DOS OEM number:
668 * 0x00 for IBM PC-DOS
669 * 0x02 for packaged MS-DOS
675 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
678 * Return version flag:
679 * 1 << 3 if DOS is in ROM,
680 * 0 (reserved) if not.
685 /* Return DOS 24-bit user serial number in BL:CX */
690 * Return DOS version: Minor:Major in AH:AL
691 * The Windows NT DOS box returns version 5.00, subject to SETVER.
693 setAX(PspBlock
->DosVersion
);
698 /* Terminate and Stay Resident */
701 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
702 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
706 /* Extended functionalities */
712 * DOS 4+ - GET BOOT DRIVE
716 setDL(SysVars
->BootDrive
);
721 * DOS 5+ - GET TRUE VERSION NUMBER
722 * This function always returns the true version number, unlike
723 * AH=30h, whose return value may be changed with SETVER.
724 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
725 * for more information.
730 * Return the true DOS version: Minor:Major in BH:BL
731 * The Windows NT DOS box returns BX=3205h (version 5.50).
733 setBX(NTDOS_VERSION
);
744 default: // goto Default;
746 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
754 /* Get Address of InDOS flag */
757 setES(DOS_DATA_SEGMENT
);
758 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
762 /* Get Interrupt Vector */
765 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
767 /* Read the address from the IDT into ES:BX */
768 setES(HIWORD(FarPointer
));
769 setBX(LOWORD(FarPointer
));
773 /* Get Free Disk Space */
776 CHAR RootPath
[] = "?:\\";
777 DWORD SectorsPerCluster
;
778 DWORD BytesPerSector
;
779 DWORD NumberOfFreeClusters
;
780 DWORD TotalNumberOfClusters
;
783 RootPath
[0] = 'A' + Sda
->CurrentDrive
;
785 RootPath
[0] = 'A' + getDL() - 1;
787 if (GetDiskFreeSpaceA(RootPath
,
790 &NumberOfFreeClusters
,
791 &TotalNumberOfClusters
))
793 setAX(LOWORD(SectorsPerCluster
));
794 setCX(LOWORD(BytesPerSector
));
795 setBX(min(NumberOfFreeClusters
, 0xFFFF));
796 setDX(min(TotalNumberOfClusters
, 0xFFFF));
807 /* SWITCH character - AVAILDEV */
813 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
814 * This setting is ignored by MS-DOS 4.0+.
815 * MS-DOS 5+ always return AL=00h/DL=2Fh.
816 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
817 * for more information.
825 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
826 * This setting is ignored by MS-DOS 5+.
827 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
828 * for more information.
836 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
837 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
838 * for more information.
846 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
847 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
848 * for more information.
855 /* Invalid subfunction */
864 /* Get/Set Country-dependent Information */
867 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
870 ErrorCode
= DosGetCountryInfo(&CountryId
,
871 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
873 if (ErrorCode
== ERROR_SUCCESS
)
875 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
880 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
887 /* Create Directory */
890 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
892 if (CreateDirectoryA(String
, NULL
))
894 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
898 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
899 setAX(LOWORD(GetLastError()));
905 /* Remove Directory */
908 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
910 if (RemoveDirectoryA(String
))
912 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
916 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
917 setAX(LOWORD(GetLastError()));
923 /* Set Current Directory */
926 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
928 if (DosChangeDirectory(String
))
930 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
934 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
935 setAX(Sda
->LastErrorCode
);
941 /* Create or Truncate File */
945 WORD ErrorCode
= DosCreateFile(&FileHandle
,
946 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
950 if (ErrorCode
== ERROR_SUCCESS
)
952 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
957 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
964 /* Open File or Device */
968 BYTE AccessShareModes
= getAL();
969 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
970 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, AccessShareModes
);
973 * Check if we failed because we attempted to open a file for write
974 * on a CDROM drive. In that situation, attempt to reopen for read
976 if (ErrorCode
== ERROR_ACCESS_DENIED
&&
977 (AccessShareModes
& 0x03) != 0 && DosIsFileOnCdRom())
979 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 0);
982 if (ErrorCode
== ERROR_SUCCESS
)
984 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
996 /* Close File or Device */
999 if (DosCloseHandle(getBX()))
1001 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1005 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1006 setAX(ERROR_INVALID_HANDLE
);
1012 /* Read from File or Device */
1018 DPRINT("DosReadFile(0x%04X)\n", getBX());
1020 ErrorCode
= DosReadFile(getBX(),
1021 MAKELONG(getDX(), getDS()),
1025 if (ErrorCode
== ERROR_SUCCESS
)
1027 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1030 else if (ErrorCode
!= ERROR_NOT_READY
)
1032 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1039 /* Write to File or Device */
1042 WORD BytesWritten
= 0;
1043 WORD ErrorCode
= DosWriteFile(getBX(),
1044 MAKELONG(getDX(), getDS()),
1048 if (ErrorCode
== ERROR_SUCCESS
)
1050 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1051 setAX(BytesWritten
);
1055 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1065 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1067 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1069 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1071 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1072 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1074 setAL(RtlUpperChar(FileName
[0]) - 'A');
1078 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1079 setAX(GetLastError());
1089 WORD ErrorCode
= DosSeekFile(getBX(),
1090 MAKELONG(getDX(), getCX()),
1094 if (ErrorCode
== ERROR_SUCCESS
)
1096 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1098 /* Return the new offset in DX:AX */
1099 setDX(HIWORD(NewLocation
));
1100 setAX(LOWORD(NewLocation
));
1104 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1111 /* Get/Set File Attributes */
1115 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1117 if (getAL() == 0x00)
1119 /* Get the attributes */
1120 Attributes
= GetFileAttributesA(FileName
);
1122 /* Check if it failed */
1123 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1125 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1126 setAX(GetLastError());
1130 /* Return the attributes that DOS can understand */
1131 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1132 setCX(Attributes
& 0x00FF);
1135 else if (getAL() == 0x01)
1137 /* Try to set the attributes */
1138 if (SetFileAttributesA(FileName
, getCL()))
1140 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1144 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1145 setAX(GetLastError());
1150 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1151 setAX(ERROR_INVALID_FUNCTION
);
1160 WORD Length
= getCX();
1162 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1164 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1169 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1170 setAX(Sda
->LastErrorCode
);
1176 /* Duplicate Handle */
1179 WORD NewHandle
= DosDuplicateHandle(getBX());
1181 if (NewHandle
!= INVALID_DOS_HANDLE
)
1184 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1188 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1189 setAX(Sda
->LastErrorCode
);
1195 /* Force Duplicate Handle */
1198 if (DosForceDuplicateHandle(getBX(), getCX()))
1200 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1204 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1205 setAX(ERROR_INVALID_HANDLE
);
1211 /* Get Current Directory */
1214 BYTE DriveNumber
= getDL();
1215 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1217 /* Get the real drive number */
1218 if (DriveNumber
== 0)
1220 DriveNumber
= Sda
->CurrentDrive
;
1224 /* Decrement DriveNumber since it was 1-based */
1228 if (DriveNumber
< SysVars
->NumLocalDrives
)
1231 * Copy the current directory into the target buffer.
1232 * It doesn't contain the drive letter and the backslash.
1234 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1235 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1236 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1240 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1241 setAX(ERROR_INVALID_DRIVE
);
1247 /* Allocate Memory */
1250 WORD MaxAvailable
= 0;
1251 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1255 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1260 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1261 setAX(Sda
->LastErrorCode
);
1262 setBX(MaxAvailable
);
1271 if (DosFreeMemory(getES()))
1273 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1277 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1278 setAX(Sda
->LastErrorCode
);
1284 /* Resize Memory Block */
1289 if (DosResizeMemory(getES(), getBX(), &Size
))
1291 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1295 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1296 setAX(Sda
->LastErrorCode
);
1306 BYTE OrgAL
= getAL();
1307 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1308 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1311 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1313 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1315 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1317 /* Create a new process */
1318 ErrorCode
= DosCreateProcess(ProgramName
,
1320 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1324 /* Just load an executable */
1325 ErrorCode
= DosLoadExecutable(LoadType
,
1330 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1333 else if (OrgAL
== 0x05)
1335 // http://www.ctyme.com/intr/rb-2942.htm
1336 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1337 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1341 ErrorCode
= ERROR_INVALID_FUNCTION
;
1344 if (ErrorCode
== ERROR_SUCCESS
)
1346 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1350 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1357 /* Terminate with Return Code */
1360 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1364 /* Get Return Code (ERRORLEVEL) */
1368 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1369 * DosErrorLevel is cleared after being read by this function.
1371 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1372 setAX(Sda
->ErrorLevel
);
1373 Sda
->ErrorLevel
= 0x0000; // Clear it
1377 /* Find First File */
1380 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1381 SEG_OFF_TO_PTR(getDS(), getDX()),
1386 if (Result
== ERROR_SUCCESS
)
1387 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1389 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1394 /* Find Next File */
1397 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1401 if (Result
== ERROR_SUCCESS
)
1402 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1404 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1409 /* Internal - Set Current Process ID (Set PSP Address) */
1412 DosSetProcessContext(getBX());
1416 /* Internal - Get Current Process ID (Get PSP Address) */
1418 /* Get Current PSP Address */
1422 * Undocumented AH=51h is identical to the documented AH=62h.
1423 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1424 * and http://www.ctyme.com/intr/rb-3140.htm
1425 * for more information.
1427 setBX(Sda
->CurrentPsp
);
1431 /* Internal - Get "List of lists" (SYSVARS) */
1435 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1436 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1437 * for more information.
1440 /* Return the DOS "list of lists" in ES:BX */
1441 setES(DOS_DATA_SEGMENT
);
1442 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1446 /* Create Child PSP */
1449 DosCreatePsp(getDX(), getSI());
1450 DosSetProcessContext(getDX());
1457 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1458 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1461 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1462 * for more information.
1465 if (MoveFileA(ExistingFileName
, NewFileName
))
1467 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1471 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1472 setAX(GetLastError());
1478 /* File Attributes */
1483 /* Get File's last-written Date and Time */
1486 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1487 FILETIME LastWriteTime
;
1488 WORD FileDate
, FileTime
;
1490 if (Descriptor
== NULL
)
1492 /* Invalid handle */
1493 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1494 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1495 setAX(ERROR_INVALID_HANDLE
);
1499 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1501 /* Invalid for devices */
1502 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1503 // setAX(ERROR_INVALID_FUNCTION);
1504 setAX(ERROR_INVALID_HANDLE
);
1509 * Retrieve the last-written Win32 date and time,
1510 * and convert it to DOS format.
1512 if (!GetFileTime(Descriptor
->Win32Handle
,
1513 NULL
, NULL
, &LastWriteTime
) ||
1514 !FileTimeToDosDateTime(&LastWriteTime
,
1515 &FileDate
, &FileTime
))
1517 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1518 setAX(GetLastError());
1522 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1528 /* Set File's last-written Date and Time */
1531 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1532 FILETIME LastWriteTime
;
1533 WORD FileDate
= getDX();
1534 WORD FileTime
= getCX();
1536 if (Descriptor
== NULL
)
1538 /* Invalid handle */
1539 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1540 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1541 setAX(ERROR_INVALID_HANDLE
);
1545 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1547 /* Invalid for devices */
1548 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1549 // setAX(ERROR_INVALID_FUNCTION);
1550 setAX(ERROR_INVALID_HANDLE
);
1555 * Convert the new last-written DOS date and time
1556 * to Win32 format and set it.
1558 if (!DosDateTimeToFileTime(FileDate
, FileTime
,
1560 !SetFileTime(Descriptor
->Win32Handle
,
1561 NULL
, NULL
, &LastWriteTime
))
1563 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1564 setAX(GetLastError());
1568 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1572 default: // goto Default;
1574 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1582 /* Get/Set Memory Management Options */
1587 /* Get allocation strategy */
1590 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1591 setAX(Sda
->AllocStrategy
);
1595 /* Set allocation strategy */
1598 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1599 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1601 /* Can't set both */
1602 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1603 setAX(ERROR_INVALID_PARAMETER
);
1607 if ((getBL() & ~(DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1608 > DOS_ALLOC_LAST_FIT
)
1610 /* Invalid allocation strategy */
1611 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1612 setAX(ERROR_INVALID_PARAMETER
);
1616 Sda
->AllocStrategy
= getBL();
1617 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1621 /* Get UMB link state */
1624 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1625 setAL(SysVars
->UmbLinked
? 0x01 : 0x00);
1629 /* Set UMB link state */
1635 Success
= DosLinkUmb();
1637 Success
= DosUnlinkUmb();
1640 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1642 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1647 /* Invalid or unsupported function */
1650 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1651 setAX(ERROR_INVALID_FUNCTION
);
1658 /* Get Extended Error Information */
1661 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1666 /* Create Temporary File */
1669 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1670 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1676 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1677 * for more information.
1680 // FIXME: Check for buffer validity?
1681 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1682 // to receive the generated filename.
1684 /* First create the temporary file */
1685 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1688 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1689 setAX(GetLastError());
1693 /* Now try to open it in read/write access */
1694 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1695 if (ErrorCode
== ERROR_SUCCESS
)
1697 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1702 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1709 /* Create New File */
1713 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1714 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1718 if (ErrorCode
== ERROR_SUCCESS
)
1720 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1725 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1732 /* Lock/Unlock Region of File */
1735 if (getAL() == 0x00)
1737 /* Lock region of file */
1738 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1740 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1744 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1745 setAX(Sda
->LastErrorCode
);
1748 else if (getAL() == 0x01)
1750 /* Unlock region of file */
1751 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1753 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1757 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1758 setAX(Sda
->LastErrorCode
);
1763 /* Invalid subfunction */
1764 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1765 setAX(ERROR_INVALID_FUNCTION
);
1771 /* Canonicalize File Name or Path */
1775 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1776 * for more information.
1780 * We suppose that the DOS app gave to us a valid
1781 * 128-byte long buffer for the canonicalized name.
1783 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1785 SEG_OFF_TO_PTR(getES(), getDI()),
1789 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1790 setAX(GetLastError());
1794 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1798 // FIXME: Convert the full path name into short version.
1799 // We cannot reliably use GetShortPathName, because it fails
1800 // if the path name given doesn't exist. However this DOS
1801 // function AH=60h should be able to work even for non-existing
1802 // path and file names.
1807 /* Miscellaneous Internal Functions */
1812 /* Get Swappable Data Area */
1815 setDS(DOS_DATA_SEGMENT
);
1816 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1817 setCX(sizeof(DOS_SDA
));
1818 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1820 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1824 default: // goto Default;
1826 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1834 /* Extended Country Information */
1839 case 0x01: case 0x02: case 0x03:
1840 case 0x04: case 0x05: case 0x06:
1843 WORD BufferSize
= getCX();
1845 ErrorCode
= DosGetCountryInfoEx(getAL(),
1848 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1850 if (ErrorCode
== ERROR_SUCCESS
)
1852 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1857 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1864 /* Country-dependent Character Capitalization -- Character */
1866 /* Country-dependent Filename Capitalization -- Character */
1869 setDL(DosToUpper(getDL()));
1870 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1871 // setAX(ERROR_SUCCESS);
1875 /* Country-dependent Character Capitalization -- Counted ASCII String */
1877 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1880 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1881 // FIXME: Check for NULL ptr!!
1882 DosToUpperStrN(Str
, Str
, getCX());
1883 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1884 // setAX(ERROR_SUCCESS);
1888 /* Country-dependent Character Capitalization -- ASCIIZ String */
1890 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1893 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1894 // FIXME: Check for NULL ptr!!
1895 DosToUpperStrZ(Str
, Str
);
1896 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1897 // setAX(ERROR_SUCCESS);
1901 /* Determine if Character represents YES/NO Response */
1904 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1905 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1909 default: // goto Default;
1911 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1919 /* Set Handle Count */
1922 if (!DosResizeHandleTable(getBX()))
1924 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1925 setAX(Sda
->LastErrorCode
);
1927 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1937 * Function 6Ah is identical to function 68h,
1938 * and sets AH to 68h if success.
1939 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1940 * for more information.
1944 if (DosFlushFileBuffers(getBX()))
1946 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1950 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1951 setAX(GetLastError());
1957 /* Extended Open/Create */
1961 WORD CreationStatus
;
1964 /* Check for AL == 00 */
1965 if (getAL() != 0x00)
1967 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1968 setAX(ERROR_INVALID_FUNCTION
);
1973 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1974 * for the full detailed description.
1976 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1979 ErrorCode
= DosCreateFileEx(&FileHandle
,
1981 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1986 if (ErrorCode
== ERROR_SUCCESS
)
1988 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1989 setCX(CreationStatus
);
1994 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2002 default: // Default:
2004 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2007 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2008 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2015 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2017 /* Set CF to terminate the running process */
2018 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2021 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
2024 * This call should leave the flags on the stack for some reason,
2025 * so move the stack by one word.
2026 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2028 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2029 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2030 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2031 setSP(LOWORD(getSP() - 2));
2033 // TODO: NOT IMPLEMENTED;
2036 /* General failure */
2038 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2041 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
2044 * This call should leave the flags on the stack for some reason,
2045 * so move the stack by one word.
2046 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2048 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2049 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2050 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2051 setSP(LOWORD(getSP() - 2));
2053 // TODO: NOT IMPLEMENTED;
2056 /* General failure */
2058 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2061 VOID WINAPI
DosInt27h(LPWORD Stack
)
2063 WORD KeepResident
= (getDX() + 0x0F) >> 4;
2065 /* Terminate and Stay Resident. CS must be the PSP segment. */
2066 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident
);
2067 DosTerminateProcess(Stack
[STACK_CS
], 0, KeepResident
);
2070 VOID WINAPI
DosIdle(LPWORD Stack
)
2073 * This will set the carry flag on the first call (to repeat the BOP),
2074 * and clear it in the next, so that exactly one HLT occurs.
2079 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2082 * This is the DOS 2+ Fast Console Output Interrupt.
2083 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2085 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2086 * for more information.
2089 /* Save AX and BX */
2090 USHORT AX
= getAX();
2091 USHORT BX
= getBX();
2094 * Set the parameters:
2095 * AL contains the character to print (already set),
2096 * BL contains the character attribute,
2097 * BH contains the video page to use.
2099 setBL(DOS_CHAR_ATTRIBUTE
);
2100 setBH(Bda
->VideoPage
);
2102 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2104 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2106 /* Restore AX and BX */
2111 VOID WINAPI
DosInt2Ah(LPWORD Stack
)
2113 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2116 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2120 /* DOS 3+ Internal Utility Functions */
2123 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2127 /* Installation Check */
2134 /* Get DOS Data Segment */
2137 setDS(DOS_DATA_SEGMENT
);
2141 /* Compare FAR Pointers */
2144 PVOID PointerFromFarPointer1
= SEG_OFF_TO_PTR(getDS(), getSI());
2145 PVOID PointerFromFarPointer2
= SEG_OFF_TO_PTR(getES(), getDI());
2146 BOOLEAN AreEqual
= (PointerFromFarPointer1
== PointerFromFarPointer2
);
2150 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
2151 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2155 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
2156 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2161 /* Set DOS Version Number to return */
2164 WORD DosVersion
= getDX();
2166 // Special case: return the true DOS version when DX=00h
2167 if (DosVersion
== 0x0000)
2168 DosData
->DosVersion
= DOS_VERSION
;
2170 DosData
->DosVersion
= DosVersion
;
2179 /* Set Disk Interrupt Handler */
2182 /* Save the old values of PrevInt13 and RomBiosInt13 */
2183 ULONG OldInt13
= BiosData
->PrevInt13
;
2184 ULONG OldBiosInt13
= BiosData
->RomBiosInt13
;
2186 /* Set PrevInt13 and RomBiosInt13 to their new values */
2187 BiosData
->PrevInt13
= MAKELONG(getDX(), getDS());
2188 BiosData
->RomBiosInt13
= MAKELONG(getBX(), getES());
2190 /* Return in DS:DX the old value of PrevInt13 */
2191 setDS(HIWORD(OldInt13
));
2192 setDX(LOWORD(OldInt13
));
2194 /* Return in DS:DX the old value of RomBiosInt13 */
2195 setES(HIWORD(OldBiosInt13
));
2196 setBX(LOWORD(OldBiosInt13
));
2201 /* Mostly Windows 2.x/3.x/9x support */
2205 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2206 * Just do nothing in this case.
2208 if (getAL() != 0x80) goto Default
;
2212 /* Extended Memory Specification */
2216 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2220 /* Installation Check */
2223 /* The driver is loaded */
2228 /* Get Driver Address */
2231 setES(HIWORD(DriverEntry
));
2232 setBX(LOWORD(DriverEntry
));
2237 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2246 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2248 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2253 BOOLEAN
DosKRNLInitialize(VOID
)
2258 BOOLEAN Success
= TRUE
;
2260 CHAR CurrentDirectory
[MAX_PATH
];
2261 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2263 static const BYTE NullDriverRoutine
[] =
2265 /* Strategy routine entry */
2266 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2269 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2270 LOBYTE(DOS_DEVSTAT_DONE
),
2271 HIBYTE(DOS_DEVSTAT_DONE
),
2273 /* Interrupt routine entry */
2277 /* Set the data segment */
2278 setDS(DOS_DATA_SEGMENT
);
2280 /* Initialize the global DOS data area */
2281 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2282 RtlZeroMemory(DosData
, sizeof(*DosData
));
2284 /* Initialize the DOS stack */
2285 setSS(DOS_DATA_SEGMENT
);
2286 setSP(DOS_DATA_OFFSET(DosStack
) + sizeof(DosData
->DosStack
) - sizeof(WORD
));
2288 /* Initialize the list of lists */
2289 SysVars
= &DosData
->SysVars
;
2290 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2291 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2292 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2295 * The last drive can be redefined with the LASTDRIVE command.
2296 * At the moment, set the real maximum possible, 'Z'.
2298 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2300 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2301 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2302 SysVars
->BootDrive
= RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]) - 'A' + 1;
2304 /* Initialize the NUL device driver */
2305 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2306 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2307 // Offset from within the DOS data segment
2308 SysVars
->NullDevice
.StrategyRoutine
= DOS_DATA_OFFSET(NullDriverRoutine
);
2309 // Hardcoded to the RETF inside StrategyRoutine
2310 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2311 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2312 sizeof(SysVars
->NullDevice
.DeviceName
),
2314 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2315 RtlCopyMemory(DosData
->NullDriverRoutine
,
2317 sizeof(NullDriverRoutine
));
2319 /* Default DOS version to report */
2320 DosData
->DosVersion
= DOS_VERSION
;
2322 /* Initialize the swappable data area */
2323 Sda
= &DosData
->Sda
;
2324 RtlZeroMemory(Sda
, sizeof(*Sda
));
2326 /* Get the current directory and convert it to a DOS path */
2327 dwRet
= GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
);
2331 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2333 else if (dwRet
> sizeof(CurrentDirectory
))
2336 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet
);
2341 dwRet
= GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
));
2345 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2347 else if (dwRet
> sizeof(DosDirectory
))
2350 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet
);
2356 /* We failed, use the boot drive instead */
2357 DosDirectory
[0] = SysVars
->BootDrive
+ 'A' - 1;
2358 DosDirectory
[1] = ':';
2359 DosDirectory
[2] = '\\';
2360 DosDirectory
[3] = '\0';
2363 /* Set the current drive */
2364 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2366 /* Get the directory part of the path and set the current directory */
2367 Path
= strchr(DosDirectory
, '\\');
2370 Path
++; // Skip the backslash
2371 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2375 DosData
->CurrentDirectories
[Sda
->CurrentDrive
][0] = '\0';
2378 /* Set the current PSP to the system PSP */
2379 Sda
->CurrentPsp
= SYSTEM_PSP
;
2381 /* Initialize the SFT */
2382 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2383 Sft
->Link
= 0xFFFFFFFF;
2384 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2386 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2388 /* Clear the file descriptor entry */
2389 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2392 /* Initialize memory management */
2393 DosInitializeMemory();
2395 /* Initialize the callback context */
2396 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2398 /* Register the DOS 32-bit Interrupts */
2399 RegisterDosInt32(0x20, DosInt20h
);
2400 RegisterDosInt32(0x21, DosInt21h
);
2401 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2402 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2403 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2404 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2405 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2406 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2407 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2408 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2409 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2411 /* Unimplemented DOS interrupts */
2412 RegisterDosInt32(0x2A, DosInt2Ah
); // DOS Critical Sections / Network
2413 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2414 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2415 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2417 /* Reserved DOS interrupts */
2418 RegisterDosInt32(0x2B, NULL
);
2419 RegisterDosInt32(0x2C, NULL
);
2420 RegisterDosInt32(0x2D, NULL
);
2422 /* Initialize country data */
2423 DosCountryInitialize();
2425 /* Load the CON driver */
2428 /* Load the XMS driver (HIMEM) */
2431 /* Load the EMS driver */
2432 if (!EmsDrvInitialize(EMS_SEGMENT
, EMS_TOTAL_PAGES
))
2434 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n"
2435 "Page frame segment or number of EMS pages invalid.\n");
2438 /* Finally initialize the UMBs */