2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: 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 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
49 BOOLEAN DoEcho
= FALSE
;
51 /* PRIVATE FUNCTIONS **********************************************************/
53 static BOOLEAN
DosChangeDrive(BYTE Drive
)
55 CHAR DirectoryPath
[DOS_CMDLINE_LENGTH
+ 1];
57 /* Make sure the drive exists */
58 if (Drive
>= SysVars
->NumLocalDrives
) return FALSE
;
60 RtlZeroMemory(DirectoryPath
, sizeof(DirectoryPath
));
62 /* Find the path to the new current directory */
63 snprintf(DirectoryPath
,
67 DosData
->CurrentDirectories
[Drive
]);
69 /* Change the current directory of the process */
70 if (!SetCurrentDirectoryA(DirectoryPath
)) return FALSE
;
72 /* Set the current drive */
73 Sda
->CurrentDrive
= Drive
;
79 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
84 CHAR CurrentDirectory
[MAX_PATH
];
85 CHAR DosDirectory
[DOS_DIR_LENGTH
];
87 /* Make sure the directory path is not too long */
88 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
90 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
94 /* Check whether the directory string is of format "?:..." */
95 if (strlen(Directory
) >= 2 && Directory
[1] == ':')
97 /* Get the drive number */
98 DriveNumber
= RtlUpperChar(Directory
[0]) - 'A';
100 /* Make sure the drive exists */
101 if (DriveNumber
>= SysVars
->NumLocalDrives
)
103 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
109 /* Keep the current drive number */
110 DriveNumber
= Sda
->CurrentDrive
;
113 /* Get the file attributes */
114 Attributes
= GetFileAttributesA(Directory
);
116 /* Make sure the path exists and is a directory */
117 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
118 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
120 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
124 /* Check if this is the current drive */
125 if (DriveNumber
== Sda
->CurrentDrive
)
127 /* Change the directory */
128 if (!SetCurrentDirectoryA(Directory
))
130 Sda
->LastErrorCode
= LOWORD(GetLastError());
135 /* Get the (possibly new) current directory (needed if we specified a relative directory) */
136 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
138 // TODO: Use some kind of default path?
142 /* Convert it to a DOS path */
143 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
145 // TODO: Use some kind of default path?
149 /* Get the directory part of the path */
150 Path
= strchr(DosDirectory
, '\\');
153 /* Skip the backslash */
157 /* Set the directory for the drive */
160 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
164 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
171 static BOOLEAN
DosControlBreak(VOID
)
175 /* Call interrupt 0x23 */
176 Int32Call(&DosContext
, 0x23);
180 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
187 /* PUBLIC FUNCTIONS ***********************************************************/
189 VOID WINAPI
DosInt20h(LPWORD Stack
)
191 /* This is the exit interrupt */
192 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
195 VOID WINAPI
DosInt21h(LPWORD Stack
)
198 SYSTEMTIME SystemTime
;
200 PDOS_INPUT_BUFFER InputBuffer
;
204 /* Save the value of SS:SP on entry in the PSP */
205 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
206 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
208 /* Check the value in the AH register */
211 /* Terminate Program */
214 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
218 /* Read Character from STDIN with Echo */
221 DPRINT("INT 21h, AH = 01h\n");
223 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
225 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
228 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
229 // Check also Ctrl-P and set echo-to-printer flag.
230 // Ctrl-Z is not interpreted.
236 /* Write Character to STDOUT */
239 // FIXME: Under DOS 2+, output handle may be redirected!!!!
241 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
244 * We return the output character (DOS 2.1+).
245 * Also, if we're going to output a TAB, then
246 * don't return a TAB but a SPACE instead.
247 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
248 * for more information.
250 setAL(Character
== '\t' ? ' ' : Character
);
254 /* Read Character from STDAUX */
257 // FIXME: Really read it from STDAUX!
258 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
259 // setAL(DosReadCharacter());
263 /* Write Character to STDAUX */
266 // FIXME: Really write it to STDAUX!
267 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
268 // DosPrintCharacter(getDL());
272 /* Write Character to Printer */
275 // FIXME: Really write it to printer!
276 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
277 DPRINT1("0x%p\n", getDL());
278 DPRINT1("\n\n-----------\n\n");
282 /* Direct Console I/O */
287 // FIXME: Under DOS 2+, output handle may be redirected!!!!
289 if (Character
!= 0xFF)
292 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
295 * We return the output character (DOS 2.1+).
296 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
297 * for more information.
306 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
307 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
311 /* No character available */
312 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
320 /* Character Input without Echo */
324 DPRINT("Char input without echo\n");
326 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
328 // FIXME: For 0x07, do not check Ctrl-C/Break.
329 // For 0x08, do check those control sequences and if needed,
336 /* Write string to STDOUT */
339 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
341 while (*String
!= '$')
343 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
348 * We return the terminating character (DOS 2.1+).
349 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
350 * for more information.
352 setAL('$'); // *String
356 /* Read Buffered Input */
360 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
362 DPRINT("Read Buffered Input\n");
364 while (Count
< InputBuffer
->MaxLength
)
366 /* Try to read a character (wait) */
367 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
371 /* Extended character */
374 /* Read the scancode */
375 DosReadCharacter(DOS_INPUT_HANDLE
);
382 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
383 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
385 if (DosControlBreak())
387 /* Set the character to a newline to exit the loop */
401 /* Erase the character */
402 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
403 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
404 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
412 /* Append it to the buffer */
413 InputBuffer
->Buffer
[Count
] = Character
;
415 /* Check if this is a special character */
416 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
418 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
419 Character
+= 'A' - 1;
422 /* Echo the character */
423 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
427 if (Character
== '\r') break;
428 if (Character
== '\b') continue;
429 Count
++; /* Carriage returns are NOT counted */
432 /* Update the length */
433 InputBuffer
->Length
= Count
;
438 /* Get STDIN Status */
441 setAL(DosCheckInput() ? 0xFF : 0x00);
445 /* Flush Buffer and Read STDIN */
448 BYTE InputFunction
= getAL();
450 /* Flush STDIN buffer */
451 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
454 * If the input function number contained in AL is valid, i.e.
455 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
456 * recursively with AL == AH.
458 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
459 InputFunction
== 0x07 || InputFunction
== 0x08 ||
460 InputFunction
== 0x0A)
462 /* Call ourselves recursively */
463 setAH(InputFunction
);
472 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
474 // TODO: Flush what's needed.
475 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
477 /* Clear CF in DOS 6 only */
478 if (PspBlock
->DosVersion
== 0x0006)
479 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
484 /* Set Default Drive */
487 DosChangeDrive(getDL());
488 setAL(SysVars
->NumLocalDrives
);
492 /* NULL Function for CP/M Compatibility */
496 * This function corresponds to the CP/M BDOS function
497 * "get bit map of logged drives", which is meaningless
500 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
501 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
502 * for more information.
508 /* Get Default Drive */
511 setAL(Sda
->CurrentDrive
);
515 /* Set Disk Transfer Area */
518 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
522 /* NULL Function for CP/M Compatibility */
527 * Function 0x1D corresponds to the CP/M BDOS function
528 * "get bit map of read-only drives", which is meaningless
530 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
531 * for more information.
533 * Function 0x1E corresponds to the CP/M BDOS function
534 * "set file attributes", which was meaningless under MS-DOS 1.x.
535 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
536 * for more information.
542 /* NULL Function for CP/M Compatibility */
546 * This function corresponds to the CP/M BDOS function
547 * "get/set default user (sublibrary) number", which is meaningless
550 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
551 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
552 * for more information.
558 /* Set Interrupt Vector */
561 ULONG FarPointer
= MAKELONG(getDX(), getDS());
562 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
563 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
565 /* Write the new far pointer to the IDT */
566 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
573 DosClonePsp(getDX(), getCS());
577 /* Parse Filename into FCB */
580 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
581 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
582 BYTE Options
= getAL();
586 if (FileName
[1] == ':')
588 /* Set the drive number */
589 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
591 /* Skip to the file name part */
596 /* No drive number specified */
597 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
598 else Fcb
->DriveNumber
= 0;
601 /* Parse the file name */
603 while ((*FileName
> 0x20) && (i
< 8))
605 if (*FileName
== '.') break;
606 else if (*FileName
== '*')
612 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
615 /* Fill the whole field with blanks only if bit 2 is not set */
616 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
618 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
621 /* Skip to the extension part */
622 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
623 if (*FileName
== '.') FileName
++;
625 /* Now parse the extension */
629 while ((*FileName
> 0x20) && (i
< 3))
631 if (*FileName
== '*')
637 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
640 /* Fill the whole field with blanks only if bit 3 is not set */
641 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
643 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
649 /* Get System Date */
652 GetLocalTime(&SystemTime
);
653 setCX(SystemTime
.wYear
);
654 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
655 setAL(SystemTime
.wDayOfWeek
);
659 /* Set System Date */
662 GetLocalTime(&SystemTime
);
663 SystemTime
.wYear
= getCX();
664 SystemTime
.wMonth
= getDH();
665 SystemTime
.wDay
= getDL();
667 /* Return success or failure */
668 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
672 /* Get System Time */
675 GetLocalTime(&SystemTime
);
676 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
677 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
681 /* Set System Time */
684 GetLocalTime(&SystemTime
);
685 SystemTime
.wHour
= getCH();
686 SystemTime
.wMinute
= getCL();
687 SystemTime
.wSecond
= getDH();
688 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
690 /* Return success or failure */
691 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
695 /* Get Disk Transfer Area */
698 setES(HIWORD(Sda
->DiskTransferArea
));
699 setBX(LOWORD(Sda
->DiskTransferArea
));
703 /* Get DOS Version */
706 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
709 * DOS 2+ - GET DOS VERSION
710 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
711 * for more information.
714 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
717 * Return DOS OEM number:
718 * 0x00 for IBM PC-DOS
719 * 0x02 for packaged MS-DOS
725 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
728 * Return version flag:
729 * 1 << 3 if DOS is in ROM,
730 * 0 (reserved) if not.
735 /* Return DOS 24-bit user serial number in BL:CX */
740 * Return DOS version: Minor:Major in AH:AL
741 * The Windows NT DOS box returns version 5.00, subject to SETVER.
743 setAX(PspBlock
->DosVersion
);
748 /* Terminate and Stay Resident */
751 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
752 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
756 /* Extended functionalities */
762 * DOS 5+ - GET TRUE VERSION NUMBER
763 * This function always returns the true version number, unlike
764 * AH=30h, whose return value may be changed with SETVER.
765 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
766 * for more information.
770 * Return the true DOS version: Minor:Major in BH:BL
771 * The Windows NT DOS box returns BX=3205h (version 5.50).
773 setBX(NTDOS_VERSION
);
783 // /* Invalid subfunction */
790 /* Get Address of InDOS flag */
793 setES(DOS_DATA_SEGMENT
);
794 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
798 /* Get Interrupt Vector */
801 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
803 /* Read the address from the IDT into ES:BX */
804 setES(HIWORD(FarPointer
));
805 setBX(LOWORD(FarPointer
));
809 /* Get Free Disk Space */
812 CHAR RootPath
[] = "?:\\";
813 DWORD SectorsPerCluster
;
814 DWORD BytesPerSector
;
815 DWORD NumberOfFreeClusters
;
816 DWORD TotalNumberOfClusters
;
818 if (getDL() == 0x00) RootPath
[0] = 'A' + Sda
->CurrentDrive
;
819 else RootPath
[0] = 'A' + getDL() - 1;
821 if (GetDiskFreeSpaceA(RootPath
,
824 &NumberOfFreeClusters
,
825 &TotalNumberOfClusters
))
827 setAX(LOWORD(SectorsPerCluster
));
828 setCX(LOWORD(BytesPerSector
));
829 setBX(min(NumberOfFreeClusters
, 0xFFFF));
830 setDX(min(TotalNumberOfClusters
, 0xFFFF));
841 /* SWITCH character - AVAILDEV */
847 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
848 * This setting is ignored by MS-DOS 4.0+.
849 * MS-DOS 5+ always return AL=00h/DL=2Fh.
850 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
851 * for more information.
856 else if (getAL() == 0x01)
859 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
860 * This setting is ignored by MS-DOS 5+.
861 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
862 * for more information.
867 else if (getAL() == 0x02)
870 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
871 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
872 * for more information.
877 else if (getAL() == 0x03)
880 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
881 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
882 * for more information.
889 /* Invalid subfunction */
896 /* Get/Set Country-dependent Information */
899 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
902 ErrorCode
= DosGetCountryInfo(&CountryId
,
903 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
905 if (ErrorCode
== ERROR_SUCCESS
)
907 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
912 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
919 /* Create Directory */
922 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
924 if (CreateDirectoryA(String
, NULL
))
926 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
930 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
931 setAX(LOWORD(GetLastError()));
937 /* Remove Directory */
940 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
942 if (RemoveDirectoryA(String
))
944 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
948 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
949 setAX(LOWORD(GetLastError()));
955 /* Set Current Directory */
958 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
960 if (DosChangeDirectory(String
))
962 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
966 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
967 setAX(Sda
->LastErrorCode
);
973 /* Create or Truncate File */
977 WORD ErrorCode
= DosCreateFile(&FileHandle
,
978 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
982 if (ErrorCode
== ERROR_SUCCESS
)
984 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
996 /* Open File or Device */
1000 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1001 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
1003 if (ErrorCode
== ERROR_SUCCESS
)
1005 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1010 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1017 /* Close File or Device */
1020 if (DosCloseHandle(getBX()))
1022 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1026 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1027 setAX(ERROR_INVALID_HANDLE
);
1033 /* Read from File or Device */
1039 DPRINT("DosReadFile(0x%04X)\n", getBX());
1042 ErrorCode
= DosReadFile(getBX(),
1043 MAKELONG(getDX(), getDS()),
1048 if (ErrorCode
== ERROR_SUCCESS
)
1050 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1053 else if (ErrorCode
!= ERROR_NOT_READY
)
1055 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1062 /* Write to File or Device */
1065 WORD BytesWritten
= 0;
1066 WORD ErrorCode
= DosWriteFile(getBX(),
1067 MAKELONG(getDX(), getDS()),
1071 if (ErrorCode
== ERROR_SUCCESS
)
1073 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1074 setAX(BytesWritten
);
1078 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1088 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1090 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1092 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1094 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1095 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1097 setAL(FileName
[0] - 'A');
1101 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1102 setAX(GetLastError());
1112 WORD ErrorCode
= DosSeekFile(getBX(),
1113 MAKELONG(getDX(), getCX()),
1117 if (ErrorCode
== ERROR_SUCCESS
)
1119 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1121 /* Return the new offset in DX:AX */
1122 setDX(HIWORD(NewLocation
));
1123 setAX(LOWORD(NewLocation
));
1127 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1134 /* Get/Set File Attributes */
1138 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1140 if (getAL() == 0x00)
1142 /* Get the attributes */
1143 Attributes
= GetFileAttributesA(FileName
);
1145 /* Check if it failed */
1146 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1148 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1149 setAX(GetLastError());
1153 /* Return the attributes that DOS can understand */
1154 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1155 setCX(Attributes
& 0x00FF);
1158 else if (getAL() == 0x01)
1160 /* Try to set the attributes */
1161 if (SetFileAttributesA(FileName
, getCL()))
1163 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1167 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1168 setAX(GetLastError());
1173 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1174 setAX(ERROR_INVALID_FUNCTION
);
1183 WORD Length
= getCX();
1185 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1187 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1192 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1193 setAX(Sda
->LastErrorCode
);
1199 /* Duplicate Handle */
1202 WORD NewHandle
= DosDuplicateHandle(getBX());
1204 if (NewHandle
!= INVALID_DOS_HANDLE
)
1207 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1211 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1212 setAX(Sda
->LastErrorCode
);
1218 /* Force Duplicate Handle */
1221 if (DosForceDuplicateHandle(getBX(), getCX()))
1223 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1227 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1228 setAX(ERROR_INVALID_HANDLE
);
1234 /* Get Current Directory */
1237 BYTE DriveNumber
= getDL();
1238 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1240 /* Get the real drive number */
1241 if (DriveNumber
== 0)
1243 DriveNumber
= Sda
->CurrentDrive
;
1247 /* Decrement DriveNumber since it was 1-based */
1251 if (DriveNumber
< SysVars
->NumLocalDrives
)
1254 * Copy the current directory into the target buffer.
1255 * It doesn't contain the drive letter and the backslash.
1257 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1258 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1259 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1263 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1264 setAX(ERROR_INVALID_DRIVE
);
1270 /* Allocate Memory */
1273 WORD MaxAvailable
= 0;
1274 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1278 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1283 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1284 setAX(Sda
->LastErrorCode
);
1285 setBX(MaxAvailable
);
1294 if (DosFreeMemory(getES()))
1296 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1300 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1301 setAX(ERROR_ARENA_TRASHED
);
1307 /* Resize Memory Block */
1312 if (DosResizeMemory(getES(), getBX(), &Size
))
1314 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1318 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1319 setAX(Sda
->LastErrorCode
);
1329 BYTE OrgAL
= getAL();
1330 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1331 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1334 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1336 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1339 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1341 /* Create a new process */
1342 ErrorCode
= DosCreateProcess(ProgramName
,
1344 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1349 /* Just load an executable */
1350 ErrorCode
= DosLoadExecutable(LoadType
,
1355 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1358 else if (OrgAL
== 0x05)
1360 // http://www.ctyme.com/intr/rb-2942.htm
1361 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1362 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1366 ErrorCode
= ERROR_INVALID_FUNCTION
;
1369 if (ErrorCode
== ERROR_SUCCESS
)
1371 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1375 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1382 /* Terminate With Return Code */
1385 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1389 /* Get Return Code (ERRORLEVEL) */
1393 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1394 * DosErrorLevel is cleared after being read by this function.
1396 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1397 setAX(Sda
->ErrorLevel
);
1398 Sda
->ErrorLevel
= 0x0000; // Clear it
1402 /* Find First File */
1405 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1406 SEG_OFF_TO_PTR(getDS(), getDX()),
1411 if (Result
== ERROR_SUCCESS
)
1412 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1414 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1419 /* Find Next File */
1422 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1426 if (Result
== ERROR_SUCCESS
)
1427 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1429 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1434 /* Internal - Set Current Process ID (Set PSP Address) */
1437 DosSetProcessContext(getBX());
1441 /* Internal - Get Current Process ID (Get PSP Address) */
1443 /* Get Current PSP Address */
1447 * Undocumented AH=51h is identical to the documented AH=62h.
1448 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1449 * and http://www.ctyme.com/intr/rb-3140.htm
1450 * for more information.
1452 setBX(Sda
->CurrentPsp
);
1456 /* Internal - Get "List of lists" (SYSVARS) */
1460 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1461 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1462 * for more information.
1465 /* Return the DOS "list of lists" in ES:BX */
1466 setES(DOS_DATA_SEGMENT
);
1467 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1471 /* Create Child PSP */
1474 DosCreatePsp(getDX(), getSI());
1475 DosSetProcessContext(getDX());
1482 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1483 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1486 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1487 * for more information.
1490 if (MoveFileA(ExistingFileName
, NewFileName
))
1492 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1496 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1497 setAX(GetLastError());
1503 /* Get/Set Memory Management Options */
1506 if (getAL() == 0x00)
1508 /* Get allocation strategy */
1509 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1510 setAX(Sda
->AllocStrategy
);
1512 else if (getAL() == 0x01)
1514 /* Set allocation strategy */
1516 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1517 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1519 /* Can't set both */
1520 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1521 setAX(ERROR_INVALID_PARAMETER
);
1525 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
1527 /* Invalid allocation strategy */
1528 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1529 setAX(ERROR_INVALID_PARAMETER
);
1533 Sda
->AllocStrategy
= getBL();
1534 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1536 else if (getAL() == 0x02)
1538 /* Get UMB link state */
1539 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1540 setAL(DosUmbLinked
? 0x01 : 0x00);
1542 else if (getAL() == 0x03)
1544 /* Set UMB link state */
1545 if (getBX()) DosLinkUmb();
1546 else DosUnlinkUmb();
1547 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1551 /* Invalid or unsupported function */
1552 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1553 setAX(ERROR_INVALID_FUNCTION
);
1559 /* Get Extended Error Information */
1562 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1567 /* Create Temporary File */
1570 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1571 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1577 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1578 * for more information.
1581 // FIXME: Check for buffer validity?
1582 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1583 // to receive the generated filename.
1585 /* First create the temporary file */
1586 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1589 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1590 setAX(GetLastError());
1594 /* Now try to open it in read/write access */
1595 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1596 if (ErrorCode
== ERROR_SUCCESS
)
1598 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1603 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1610 /* Create New File */
1614 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1615 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1619 if (ErrorCode
== ERROR_SUCCESS
)
1621 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1626 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1633 /* Lock/Unlock Region of File */
1636 if (getAL() == 0x00)
1638 /* Lock region of file */
1639 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1641 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1645 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1646 setAX(Sda
->LastErrorCode
);
1649 else if (getAL() == 0x01)
1651 /* Unlock region of file */
1652 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1654 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1658 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1659 setAX(Sda
->LastErrorCode
);
1664 /* Invalid subfunction */
1665 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1666 setAX(ERROR_INVALID_FUNCTION
);
1672 /* Canonicalize File Name or Path */
1676 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1677 * for more information.
1681 * We suppose that the DOS app gave to us a valid
1682 * 128-byte long buffer for the canonicalized name.
1684 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1686 SEG_OFF_TO_PTR(getES(), getDI()),
1690 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1691 setAX(GetLastError());
1695 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1699 // FIXME: Convert the full path name into short version.
1700 // We cannot reliably use GetShortPathName, because it fails
1701 // if the path name given doesn't exist. However this DOS
1702 // function AH=60h should be able to work even for non-existing
1703 // path and file names.
1708 /* Miscellaneous Internal Functions */
1713 /* Get Swappable Data Area */
1716 setDS(DOS_DATA_SEGMENT
);
1717 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1718 setCX(sizeof(DOS_SDA
));
1719 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1721 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1727 DPRINT1("INT 21h, AH = 5Dh, subfunction AL = %Xh NOT IMPLEMENTED\n",
1735 /* Extended Country Information */
1740 case 0x01: case 0x02: case 0x03:
1741 case 0x04: case 0x05: case 0x06:
1744 WORD BufferSize
= getCX();
1746 ErrorCode
= DosGetCountryInfoEx(getAL(),
1749 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1751 if (ErrorCode
== ERROR_SUCCESS
)
1753 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1758 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1765 /* Country-dependent Character Capitalization -- Character */
1767 /* Country-dependent Filename Capitalization -- Character */
1770 setDL(DosToUpper(getDL()));
1771 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1772 // setAX(ERROR_SUCCESS);
1776 /* Country-dependent Character Capitalization -- Counted ASCII String */
1778 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1781 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1782 // FIXME: Check for NULL ptr!!
1783 DosToUpperStrN(Str
, Str
, getCX());
1784 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1785 // setAX(ERROR_SUCCESS);
1789 /* Country-dependent Character Capitalization -- ASCIIZ String */
1791 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1794 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1795 // FIXME: Check for NULL ptr!!
1796 DosToUpperStrZ(Str
, Str
);
1797 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1798 // setAX(ERROR_SUCCESS);
1802 /* Determine if Character represents YES/NO Response */
1805 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1806 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1812 DPRINT1("INT 21h, AH = 65h, subfunction AL = %Xh NOT IMPLEMENTED\n",
1820 /* Set Handle Count */
1823 if (!DosResizeHandleTable(getBX()))
1825 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1826 setAX(Sda
->LastErrorCode
);
1828 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1838 * Function 6Ah is identical to function 68h,
1839 * and sets AH to 68h if success.
1840 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1841 * for more information.
1845 if (DosFlushFileBuffers(getBX()))
1847 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1851 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1852 setAX(GetLastError());
1858 /* Extended Open/Create */
1862 WORD CreationStatus
;
1865 /* Check for AL == 00 */
1866 if (getAL() != 0x00)
1868 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1869 setAX(ERROR_INVALID_FUNCTION
);
1874 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1875 * for the full detailed description.
1877 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1880 ErrorCode
= DosCreateFileEx(&FileHandle
,
1882 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1887 if (ErrorCode
== ERROR_SUCCESS
)
1889 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1890 setCX(CreationStatus
);
1895 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1905 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1908 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1909 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1916 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
1918 /* Set CF to terminate the running process */
1919 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1922 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
1925 * This call should leave the flags on the stack for some reason,
1926 * so move the stack by one word.
1927 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
1929 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
1930 Stack
[STACK_IP
] = Stack
[STACK_CS
];
1931 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
1932 setSP(LOWORD(getSP() - 2));
1934 // TODO: NOT IMPLEMENTED;
1937 /* General failure */
1939 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
1942 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
1945 * This call should leave the flags on the stack for some reason,
1946 * so move the stack by one word.
1947 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
1949 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
1950 Stack
[STACK_IP
] = Stack
[STACK_CS
];
1951 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
1952 setSP(LOWORD(getSP() - 2));
1954 // TODO: NOT IMPLEMENTED;
1957 /* General failure */
1959 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
1962 VOID WINAPI
DosInt27h(LPWORD Stack
)
1964 DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
1967 VOID WINAPI
DosIdle(LPWORD Stack
)
1970 * This will set the carry flag on the first call (to repeat the BOP),
1971 * and clear it in the next, so that exactly one HLT occurs.
1976 VOID WINAPI
DosFastConOut(LPWORD Stack
)
1979 * This is the DOS 2+ Fast Console Output Interrupt.
1980 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1982 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1983 * for more information.
1986 /* Save AX and BX */
1987 USHORT AX
= getAX();
1988 USHORT BX
= getBX();
1991 * Set the parameters:
1992 * AL contains the character to print (already set),
1993 * BL contains the character attribute,
1994 * BH contains the video page to use.
1996 setBL(DOS_CHAR_ATTRIBUTE
);
1997 setBH(Bda
->VideoPage
);
1999 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2001 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2003 /* Restore AX and BX */
2008 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2012 /* Extended Memory Specification */
2016 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2020 /* Installation Check */
2023 /* The driver is loaded */
2028 /* Get Driver Address */
2031 setES(HIWORD(DriverEntry
));
2032 setBX(LOWORD(DriverEntry
));
2037 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
2046 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2048 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2053 BOOLEAN
DosKRNLInitialize(VOID
)
2060 CHAR CurrentDirectory
[MAX_PATH
];
2061 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2063 const BYTE NullDriverRoutine
[] = {
2064 /* Strategy routine entry */
2065 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2068 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2069 LOBYTE(DOS_DEVSTAT_DONE
),
2070 HIBYTE(DOS_DEVSTAT_DONE
),
2072 /* Interrupt routine entry */
2079 /* Initialize the global DOS data area */
2080 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2081 RtlZeroMemory(DosData
, sizeof(*DosData
));
2083 /* Initialize the list of lists */
2084 SysVars
= &DosData
->SysVars
;
2085 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2086 SysVars
->FirstMcb
= FIRST_MCB_SEGMENT
;
2087 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2088 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2090 /* The last drive can be redefined with the LASTDRIVE command. At the moment, set the real maximum possible, 'Z'. */
2091 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1;
2093 /* Initialize the NUL device driver */
2094 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2095 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2096 SysVars
->NullDevice
.StrategyRoutine
= FIELD_OFFSET(DOS_SYSVARS
, NullDriverRoutine
);
2097 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2098 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2099 sizeof(SysVars
->NullDevice
.DeviceName
),
2101 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2102 RtlCopyMemory(SysVars
->NullDriverRoutine
,
2104 sizeof(NullDriverRoutine
));
2106 /* Initialize the swappable data area */
2107 Sda
= &DosData
->Sda
;
2108 RtlZeroMemory(Sda
, sizeof(*Sda
));
2110 /* Get the current directory */
2111 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
2113 // TODO: Use some kind of default path?
2117 /* Convert it to a DOS path */
2118 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
2120 // TODO: Use some kind of default path?
2125 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2127 /* Get the directory part of the path */
2128 Path
= strchr(DosDirectory
, '\\');
2131 /* Skip the backslash */
2135 /* Set the directory */
2138 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2141 /* Set the current PSP to the system PSP */
2142 Sda
->CurrentPsp
= SYSTEM_PSP
;
2144 /* Set the initial allocation strategy to "best fit" */
2145 Sda
->AllocStrategy
= DOS_ALLOC_BEST_FIT
;
2147 /* Initialize the SFT */
2148 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2149 Sft
->Link
= 0xFFFFFFFF;
2150 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2152 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2154 /* Clear the file descriptor entry */
2155 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2158 /* Read CONFIG.SYS */
2159 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2162 while (fgetws(Buffer
, 256, Stream
))
2164 // TODO: Parse the line
2171 /* Initialize the callback context */
2172 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2174 /* Register the DOS 32-bit Interrupts */
2175 RegisterDosInt32(0x20, DosInt20h
);
2176 RegisterDosInt32(0x21, DosInt21h
);
2177 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2178 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2179 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2180 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2181 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2182 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2183 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2184 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2185 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2187 /* Unimplemented DOS interrupts */
2188 RegisterDosInt32(0x2A, NULL
); // Network - Installation Check
2190 /* Initialize country data */
2191 DosCountryInitialize();
2193 /* Load the CON driver */
2196 /* Load the XMS driver (HIMEM) */
2199 /* Load the EMS driver */
2200 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
2202 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2203 "Try reducing the number of EMS pages.\n");