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 *******************************************************************/
28 #include "bios/bios.h"
31 #include "hardware/ps2.h"
35 /* PRIVATE VARIABLES **********************************************************/
37 CALLBACK16 DosContext
;
39 /* PUBLIC VARIABLES ***********************************************************/
41 /* Global DOS data area contained in guest memory */
43 /* Easy accessors to useful DOS data area parts */
47 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
48 BOOLEAN DoEcho
= FALSE
;
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 "?:..." */
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 */
149 Path
= strchr(DosDirectory
, '\\');
152 /* Skip the backslash */
156 /* Set the directory for the drive */
159 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
163 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
170 static BOOLEAN
DosControlBreak(VOID
)
174 /* Call interrupt 0x23 */
175 Int32Call(&DosContext
, 0x23);
179 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
186 /* PUBLIC FUNCTIONS ***********************************************************/
188 VOID WINAPI
DosInt20h(LPWORD Stack
)
190 /* This is the exit interrupt */
191 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
194 VOID WINAPI
DosInt21h(LPWORD Stack
)
197 SYSTEMTIME SystemTime
;
199 PDOS_INPUT_BUFFER InputBuffer
;
203 /* Save the value of SS:SP on entry in the PSP */
204 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
205 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
207 /* Check the value in the AH register */
210 /* Terminate Program */
213 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
217 /* Read Character from STDIN with Echo */
220 DPRINT("INT 21h, AH = 01h\n");
222 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
224 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
227 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
228 // Check also Ctrl-P and set echo-to-printer flag.
229 // Ctrl-Z is not interpreted.
235 /* Write Character to STDOUT */
238 // FIXME: Under DOS 2+, output handle may be redirected!!!!
240 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
243 * We return the output character (DOS 2.1+).
244 * Also, if we're going to output a TAB, then
245 * don't return a TAB but a SPACE instead.
246 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
247 * for more information.
249 setAL(Character
== '\t' ? ' ' : Character
);
253 /* Read Character from STDAUX */
256 // FIXME: Really read it from STDAUX!
257 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
258 // setAL(DosReadCharacter());
262 /* Write Character to STDAUX */
265 // FIXME: Really write it to STDAUX!
266 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
267 // DosPrintCharacter(getDL());
271 /* Write Character to Printer */
274 // FIXME: Really write it to printer!
275 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
276 DPRINT1("0x%p\n", getDL());
277 DPRINT1("\n\n-----------\n\n");
281 /* Direct Console I/O */
286 // FIXME: Under DOS 2+, output handle may be redirected!!!!
288 if (Character
!= 0xFF)
291 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
294 * We return the output character (DOS 2.1+).
295 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
296 * for more information.
305 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
306 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
310 /* No character available */
311 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
319 /* Character Input without Echo */
323 DPRINT("Char input without echo\n");
325 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
327 // FIXME: For 0x07, do not check Ctrl-C/Break.
328 // For 0x08, do check those control sequences and if needed,
335 /* Write string to STDOUT */
338 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
340 while (*String
!= '$')
342 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
347 * We return the terminating character (DOS 2.1+).
348 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
349 * for more information.
351 setAL('$'); // *String
355 /* Read Buffered Input */
359 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
361 DPRINT("Read Buffered Input\n");
363 while (Count
< InputBuffer
->MaxLength
)
365 /* Try to read a character (wait) */
366 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
370 /* Extended character */
373 /* Read the scancode */
374 DosReadCharacter(DOS_INPUT_HANDLE
);
381 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
382 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
384 if (DosControlBreak())
386 /* Set the character to a newline to exit the loop */
400 /* Erase the character */
401 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
402 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
403 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
411 /* Append it to the buffer */
412 InputBuffer
->Buffer
[Count
] = Character
;
414 /* Check if this is a special character */
415 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
417 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
418 Character
+= 'A' - 1;
421 /* Echo the character */
422 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
426 if (Character
== '\r') break;
427 if (Character
== '\b') continue;
428 Count
++; /* Carriage returns are NOT counted */
431 /* Update the length */
432 InputBuffer
->Length
= Count
;
437 /* Get STDIN Status */
440 setAL(DosCheckInput() ? 0xFF : 0x00);
444 /* Flush Buffer and Read STDIN */
447 BYTE InputFunction
= getAL();
449 /* Flush STDIN buffer */
450 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
453 * If the input function number contained in AL is valid, i.e.
454 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
455 * recursively with AL == AH.
457 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
458 InputFunction
== 0x07 || InputFunction
== 0x08 ||
459 InputFunction
== 0x0A)
461 /* Call ourselves recursively */
462 setAH(InputFunction
);
471 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
473 // TODO: Flush what's needed.
474 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
476 /* Clear CF in DOS 6 only */
477 if (PspBlock
->DosVersion
== 0x0006)
478 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
483 /* Set Default Drive */
486 DosChangeDrive(getDL());
487 setAL(SysVars
->NumLocalDrives
);
491 /* NULL Function for CP/M Compatibility */
495 * This function corresponds to the CP/M BDOS function
496 * "get bit map of logged drives", which is meaningless
499 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
501 * for more information.
507 /* Get Default Drive */
510 setAL(Sda
->CurrentDrive
);
514 /* Set Disk Transfer Area */
517 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
521 /* NULL Function for CP/M Compatibility */
526 * Function 0x1D corresponds to the CP/M BDOS function
527 * "get bit map of read-only drives", which is meaningless
529 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
530 * for more information.
532 * Function 0x1E corresponds to the CP/M BDOS function
533 * "set file attributes", which was meaningless under MS-DOS 1.x.
534 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
535 * for more information.
541 /* NULL Function for CP/M Compatibility */
545 * This function corresponds to the CP/M BDOS function
546 * "get/set default user (sublibrary) number", which is meaningless
549 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
550 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
551 * for more information.
557 /* Set Interrupt Vector */
560 ULONG FarPointer
= MAKELONG(getDX(), getDS());
561 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
562 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
564 /* Write the new far pointer to the IDT */
565 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
572 DosClonePsp(getDX(), getCS());
576 /* Parse Filename into FCB */
579 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
580 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
581 BYTE Options
= getAL();
585 if (FileName
[1] == ':')
587 /* Set the drive number */
588 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
590 /* Skip to the file name part */
595 /* No drive number specified */
596 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
597 else Fcb
->DriveNumber
= 0;
600 /* Parse the file name */
602 while ((*FileName
> 0x20) && (i
< 8))
604 if (*FileName
== '.') break;
605 else if (*FileName
== '*')
611 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
614 /* Fill the whole field with blanks only if bit 2 is not set */
615 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
617 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
620 /* Skip to the extension part */
621 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
622 if (*FileName
== '.') FileName
++;
624 /* Now parse the extension */
628 while ((*FileName
> 0x20) && (i
< 3))
630 if (*FileName
== '*')
636 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
639 /* Fill the whole field with blanks only if bit 3 is not set */
640 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
642 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
648 /* Get System Date */
651 GetLocalTime(&SystemTime
);
652 setCX(SystemTime
.wYear
);
653 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
654 setAL(SystemTime
.wDayOfWeek
);
658 /* Set System Date */
661 GetLocalTime(&SystemTime
);
662 SystemTime
.wYear
= getCX();
663 SystemTime
.wMonth
= getDH();
664 SystemTime
.wDay
= getDL();
666 /* Return success or failure */
667 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
671 /* Get System Time */
674 GetLocalTime(&SystemTime
);
675 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
676 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
680 /* Set System Time */
683 GetLocalTime(&SystemTime
);
684 SystemTime
.wHour
= getCH();
685 SystemTime
.wMinute
= getCL();
686 SystemTime
.wSecond
= getDH();
687 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
689 /* Return success or failure */
690 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
694 /* Get Disk Transfer Area */
697 setES(HIWORD(Sda
->DiskTransferArea
));
698 setBX(LOWORD(Sda
->DiskTransferArea
));
702 /* Get DOS Version */
705 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
708 * DOS 2+ - GET DOS VERSION
709 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
710 * for more information.
713 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
716 * Return DOS OEM number:
717 * 0x00 for IBM PC-DOS
718 * 0x02 for packaged MS-DOS
724 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
727 * Return version flag:
728 * 1 << 3 if DOS is in ROM,
729 * 0 (reserved) if not.
734 /* Return DOS 24-bit user serial number in BL:CX */
739 * Return DOS version: Minor:Major in AH:AL
740 * The Windows NT DOS box returns version 5.00, subject to SETVER.
742 setAX(PspBlock
->DosVersion
);
747 /* Terminate and Stay Resident */
750 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
751 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
755 /* Extended functionalities */
761 * DOS 5+ - GET TRUE VERSION NUMBER
762 * This function always returns the true version number, unlike
763 * AH=30h, whose return value may be changed with SETVER.
764 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
765 * for more information.
769 * Return the true DOS version: Minor:Major in BH:BL
770 * The Windows NT DOS box returns BX=3205h (version 5.50).
772 setBX(NTDOS_VERSION
);
782 // /* Invalid subfunction */
789 /* Get Address of InDOS flag */
792 setES(DOS_DATA_SEGMENT
);
793 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
797 /* Get Interrupt Vector */
800 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
802 /* Read the address from the IDT into ES:BX */
803 setES(HIWORD(FarPointer
));
804 setBX(LOWORD(FarPointer
));
808 /* Get Free Disk Space */
811 CHAR RootPath
[] = "?:\\";
812 DWORD SectorsPerCluster
;
813 DWORD BytesPerSector
;
814 DWORD NumberOfFreeClusters
;
815 DWORD TotalNumberOfClusters
;
817 if (getDL() == 0x00) RootPath
[0] = 'A' + Sda
->CurrentDrive
;
818 else RootPath
[0] = 'A' + getDL() - 1;
820 if (GetDiskFreeSpaceA(RootPath
,
823 &NumberOfFreeClusters
,
824 &TotalNumberOfClusters
))
826 setAX(LOWORD(SectorsPerCluster
));
827 setCX(LOWORD(BytesPerSector
));
828 setBX(min(NumberOfFreeClusters
, 0xFFFF));
829 setDX(min(TotalNumberOfClusters
, 0xFFFF));
840 /* SWITCH character - AVAILDEV */
846 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
847 * This setting is ignored by MS-DOS 4.0+.
848 * MS-DOS 5+ always return AL=00h/DL=2Fh.
849 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
850 * for more information.
855 else if (getAL() == 0x01)
858 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
859 * This setting is ignored by MS-DOS 5+.
860 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
861 * for more information.
866 else if (getAL() == 0x02)
869 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
870 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
871 * for more information.
876 else if (getAL() == 0x03)
879 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
880 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
881 * for more information.
888 /* Invalid subfunction */
895 /* Get/Set Country-dependent Information */
899 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer
=
900 (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
905 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
906 (LPSTR
)&CountryCodeBuffer
->TimeFormat
,
907 sizeof(CountryCodeBuffer
->TimeFormat
));
910 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
911 setAX(LOWORD(GetLastError()));
915 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
916 (LPSTR
)&CountryCodeBuffer
->CurrencySymbol
,
917 sizeof(CountryCodeBuffer
->CurrencySymbol
));
920 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
921 setAX(LOWORD(GetLastError()));
925 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
926 (LPSTR
)&CountryCodeBuffer
->ThousandSep
,
927 sizeof(CountryCodeBuffer
->ThousandSep
));
930 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
931 setAX(LOWORD(GetLastError()));
935 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
936 (LPSTR
)&CountryCodeBuffer
->DecimalSep
,
937 sizeof(CountryCodeBuffer
->DecimalSep
));
940 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
941 setAX(LOWORD(GetLastError()));
945 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
949 // TODO: NOT IMPLEMENTED
956 /* Create Directory */
959 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
961 if (CreateDirectoryA(String
, NULL
))
963 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
967 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
968 setAX(LOWORD(GetLastError()));
974 /* Remove Directory */
977 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
979 if (RemoveDirectoryA(String
))
981 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
985 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
986 setAX(LOWORD(GetLastError()));
992 /* Set Current Directory */
995 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
997 if (DosChangeDirectory(String
))
999 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1003 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1004 setAX(Sda
->LastErrorCode
);
1010 /* Create or Truncate File */
1014 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1015 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1019 if (ErrorCode
== ERROR_SUCCESS
)
1021 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1026 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1033 /* Open File or Device */
1037 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1038 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
1040 if (ErrorCode
== ERROR_SUCCESS
)
1042 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1047 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1054 /* Close File or Device */
1057 if (DosCloseHandle(getBX()))
1059 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1063 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1064 setAX(ERROR_INVALID_HANDLE
);
1070 /* Read from File or Device */
1076 DPRINT("DosReadFile(0x%04X)\n", getBX());
1079 ErrorCode
= DosReadFile(getBX(),
1080 MAKELONG(getDX(), getDS()),
1085 if (ErrorCode
== ERROR_SUCCESS
)
1087 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1090 else if (ErrorCode
!= ERROR_NOT_READY
)
1092 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1099 /* Write to File or Device */
1102 WORD BytesWritten
= 0;
1103 WORD ErrorCode
= DosWriteFile(getBX(),
1104 MAKELONG(getDX(), getDS()),
1108 if (ErrorCode
== ERROR_SUCCESS
)
1110 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1111 setAX(BytesWritten
);
1115 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1125 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1127 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1129 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1131 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1132 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1134 setAL(FileName
[0] - 'A');
1138 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1139 setAX(GetLastError());
1149 WORD ErrorCode
= DosSeekFile(getBX(),
1150 MAKELONG(getDX(), getCX()),
1154 if (ErrorCode
== ERROR_SUCCESS
)
1156 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1158 /* Return the new offset in DX:AX */
1159 setDX(HIWORD(NewLocation
));
1160 setAX(LOWORD(NewLocation
));
1164 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1171 /* Get/Set File Attributes */
1175 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1177 if (getAL() == 0x00)
1179 /* Get the attributes */
1180 Attributes
= GetFileAttributesA(FileName
);
1182 /* Check if it failed */
1183 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1185 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1186 setAX(GetLastError());
1190 /* Return the attributes that DOS can understand */
1191 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1192 setCX(Attributes
& 0x00FF);
1195 else if (getAL() == 0x01)
1197 /* Try to set the attributes */
1198 if (SetFileAttributesA(FileName
, getCL()))
1200 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1204 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1205 setAX(GetLastError());
1210 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1211 setAX(ERROR_INVALID_FUNCTION
);
1220 WORD Length
= getCX();
1222 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1224 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1229 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1230 setAX(Sda
->LastErrorCode
);
1236 /* Duplicate Handle */
1239 WORD NewHandle
= DosDuplicateHandle(getBX());
1241 if (NewHandle
!= INVALID_DOS_HANDLE
)
1244 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1248 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1249 setAX(Sda
->LastErrorCode
);
1255 /* Force Duplicate Handle */
1258 if (DosForceDuplicateHandle(getBX(), getCX()))
1260 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1264 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1265 setAX(ERROR_INVALID_HANDLE
);
1271 /* Get Current Directory */
1274 BYTE DriveNumber
= getDL();
1275 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1277 /* Get the real drive number */
1278 if (DriveNumber
== 0)
1280 DriveNumber
= Sda
->CurrentDrive
;
1284 /* Decrement DriveNumber since it was 1-based */
1288 if (DriveNumber
< SysVars
->NumLocalDrives
)
1291 * Copy the current directory into the target buffer.
1292 * It doesn't contain the drive letter and the backslash.
1294 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1295 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1296 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1300 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1301 setAX(ERROR_INVALID_DRIVE
);
1307 /* Allocate Memory */
1310 WORD MaxAvailable
= 0;
1311 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1315 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1320 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1321 setAX(Sda
->LastErrorCode
);
1322 setBX(MaxAvailable
);
1331 if (DosFreeMemory(getES()))
1333 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1337 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1338 setAX(ERROR_ARENA_TRASHED
);
1344 /* Resize Memory Block */
1349 if (DosResizeMemory(getES(), getBX(), &Size
))
1351 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1355 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1356 setAX(Sda
->LastErrorCode
);
1366 BYTE OrgAL
= getAL();
1367 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1368 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1371 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1373 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1376 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1378 /* Create a new process */
1379 ErrorCode
= DosCreateProcess(ProgramName
, ParamBlock
);
1384 /* Just load an executable */
1385 ErrorCode
= DosLoadExecutable(LoadType
,
1392 else if (OrgAL
== 0x05)
1394 // http://www.ctyme.com/intr/rb-2942.htm
1395 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1396 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1400 ErrorCode
= ERROR_INVALID_FUNCTION
;
1403 if (ErrorCode
== ERROR_SUCCESS
)
1405 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1409 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1416 /* Terminate With Return Code */
1419 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1423 /* Get Return Code (ERRORLEVEL) */
1427 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1428 * DosErrorLevel is cleared after being read by this function.
1430 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1431 setAX(Sda
->ErrorLevel
);
1432 Sda
->ErrorLevel
= 0x0000; // Clear it
1436 /* Find First File */
1439 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1440 SEG_OFF_TO_PTR(getDS(), getDX()),
1445 if (Result
== ERROR_SUCCESS
)
1446 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1448 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1453 /* Find Next File */
1456 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1460 if (Result
== ERROR_SUCCESS
)
1461 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1463 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1468 /* Internal - Set Current Process ID (Set PSP Address) */
1471 DosSetProcessContext(getBX());
1475 /* Internal - Get Current Process ID (Get PSP Address) */
1477 /* Get Current PSP Address */
1481 * Undocumented AH=51h is identical to the documented AH=62h.
1482 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1483 * and http://www.ctyme.com/intr/rb-3140.htm
1484 * for more information.
1486 setBX(Sda
->CurrentPsp
);
1490 /* Internal - Get "List of lists" (SYSVARS) */
1494 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1495 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1496 * for more information.
1499 /* Return the DOS "list of lists" in ES:BX */
1500 setES(DOS_DATA_SEGMENT
);
1501 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1506 /* Create Child PSP */
1509 DosCreatePsp(getDX(), getSI());
1516 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1517 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1520 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1521 * for more information.
1524 if (MoveFileA(ExistingFileName
, NewFileName
))
1526 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1530 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1531 setAX(GetLastError());
1537 /* Get/Set Memory Management Options */
1540 if (getAL() == 0x00)
1542 /* Get allocation strategy */
1543 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1544 setAX(Sda
->AllocStrategy
);
1546 else if (getAL() == 0x01)
1548 /* Set allocation strategy */
1550 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1551 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1553 /* Can't set both */
1554 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1555 setAX(ERROR_INVALID_PARAMETER
);
1559 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
1561 /* Invalid allocation strategy */
1562 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1563 setAX(ERROR_INVALID_PARAMETER
);
1567 Sda
->AllocStrategy
= getBL();
1568 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1570 else if (getAL() == 0x02)
1572 /* Get UMB link state */
1573 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1574 setAL(DosUmbLinked
? 0x01 : 0x00);
1576 else if (getAL() == 0x03)
1578 /* Set UMB link state */
1579 if (getBX()) DosLinkUmb();
1580 else DosUnlinkUmb();
1581 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1585 /* Invalid or unsupported function */
1586 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1587 setAX(ERROR_INVALID_FUNCTION
);
1593 /* Get Extended Error Information */
1596 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1601 /* Create Temporary File */
1604 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1605 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1611 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1612 * for more information.
1615 // FIXME: Check for buffer validity?
1616 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
1617 // to receive the generated filename.
1619 /* First create the temporary file */
1620 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1623 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1624 setAX(GetLastError());
1628 /* Now try to open it in read/write access */
1629 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1630 if (ErrorCode
== ERROR_SUCCESS
)
1632 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1637 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1644 /* Create New File */
1648 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1649 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1653 if (ErrorCode
== ERROR_SUCCESS
)
1655 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1660 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1667 /* Lock/Unlock Region of File */
1670 if (getAL() == 0x00)
1672 /* Lock region of file */
1673 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1675 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1679 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1680 setAX(Sda
->LastErrorCode
);
1683 else if (getAL() == 0x01)
1685 /* Unlock region of file */
1686 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1688 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1692 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1693 setAX(Sda
->LastErrorCode
);
1698 /* Invalid subfunction */
1699 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1700 setAX(ERROR_INVALID_FUNCTION
);
1706 /* Canonicalize File Name or Path */
1710 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1711 * for more information.
1715 * We suppose that the DOS app gave to us a valid
1716 * 128-byte long buffer for the canonicalized name.
1718 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1720 SEG_OFF_TO_PTR(getES(), getDI()),
1724 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1725 setAX(GetLastError());
1729 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1733 // FIXME: Convert the full path name into short version.
1734 // We cannot reliably use GetShortPathName, because it fails
1735 // if the path name given doesn't exist. However this DOS
1736 // function AH=60h should be able to work even for non-existing
1737 // path and file names.
1742 /* Miscellaneous Internal Functions */
1747 /* Get Swappable Data Area */
1750 setDS(DOS_DATA_SEGMENT
);
1751 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1752 setCX(sizeof(DOS_SDA
));
1753 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1755 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1761 DPRINT1("INT 21h, AH = 5Dh, subfunction AL = %Xh NOT IMPLEMENTED\n",
1769 /* Set Handle Count */
1772 if (!DosResizeHandleTable(getBX()))
1774 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1775 setAX(Sda
->LastErrorCode
);
1777 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1787 * Function 6Ah is identical to function 68h,
1788 * and sets AH to 68h if success.
1789 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1790 * for more information.
1794 if (DosFlushFileBuffers(getBX()))
1796 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1800 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1801 setAX(GetLastError());
1807 /* Extended Open/Create */
1811 WORD CreationStatus
;
1814 /* Check for AL == 00 */
1815 if (getAL() != 0x00)
1817 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1818 setAX(ERROR_INVALID_FUNCTION
);
1823 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1824 * for the full detailed description.
1826 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1829 ErrorCode
= DosCreateFileEx(&FileHandle
,
1831 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1836 if (ErrorCode
== ERROR_SUCCESS
)
1838 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1839 setCX(CreationStatus
);
1844 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1854 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1857 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1858 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1865 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
1867 /* Set CF to terminate the running process */
1868 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1871 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
1874 * This call should leave the flags on the stack for some reason,
1875 * so move the stack by one word.
1877 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
1878 Stack
[STACK_IP
] = Stack
[STACK_CS
];
1879 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
1880 setSP(LOWORD(getSP() - 2));
1882 // TODO: NOT IMPLEMENTED;
1885 /* General failure */
1887 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
1890 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
1893 * This call should leave the flags on the stack for some reason,
1894 * so move the stack by one word.
1896 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
1897 Stack
[STACK_IP
] = Stack
[STACK_CS
];
1898 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
1899 setSP(LOWORD(getSP() - 2));
1901 // TODO: NOT IMPLEMENTED;
1904 /* General failure */
1906 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
1909 VOID WINAPI
DosInt27h(LPWORD Stack
)
1911 DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
1914 VOID WINAPI
DosIdle(LPWORD Stack
)
1917 * This will set the carry flag on the first call (to repeat the BOP),
1918 * and clear it in the next, so that exactly one HLT occurs.
1923 VOID WINAPI
DosFastConOut(LPWORD Stack
)
1926 * This is the DOS 2+ Fast Console Output Interrupt.
1927 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1929 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1930 * for more information.
1933 /* Save AX and BX */
1934 USHORT AX
= getAX();
1935 USHORT BX
= getBX();
1938 * Set the parameters:
1939 * AL contains the character to print (already set),
1940 * BL contains the character attribute,
1941 * BH contains the video page to use.
1943 setBL(DOS_CHAR_ATTRIBUTE
);
1944 setBH(Bda
->VideoPage
);
1946 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
1948 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
1950 /* Restore AX and BX */
1955 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
1959 /* Extended Memory Specification */
1963 if (!XmsGetDriverEntry(&DriverEntry
)) break;
1965 if (getAL() == 0x00)
1967 /* The driver is loaded */
1970 else if (getAL() == 0x10)
1972 setES(HIWORD(DriverEntry
));
1973 setBX(LOWORD(DriverEntry
));
1977 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
1985 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1987 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1992 BOOLEAN
DosKRNLInitialize(VOID
)
1999 CHAR CurrentDirectory
[MAX_PATH
];
2000 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2002 const BYTE NullDriverRoutine
[] = {
2003 /* Strategy routine entry */
2004 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2007 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2008 LOBYTE(DOS_DEVSTAT_DONE
),
2009 HIBYTE(DOS_DEVSTAT_DONE
),
2011 /* Interrupt routine entry */
2018 /* Initialize the global DOS data area */
2019 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2020 RtlZeroMemory(DosData
, sizeof(*DosData
));
2022 /* Initialize the list of lists */
2023 SysVars
= &DosData
->SysVars
;
2024 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2025 SysVars
->FirstMcb
= FIRST_MCB_SEGMENT
;
2026 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2027 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2029 /* The last drive can be redefined with the LASTDRIVE command. At the moment, set the real maximum possible, 'Z'. */
2030 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1;
2032 /* Initialize the NUL device driver */
2033 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2034 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2035 SysVars
->NullDevice
.StrategyRoutine
= FIELD_OFFSET(DOS_SYSVARS
, NullDriverRoutine
);
2036 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2037 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2038 sizeof(SysVars
->NullDevice
.DeviceName
),
2040 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2041 RtlCopyMemory(SysVars
->NullDriverRoutine
,
2043 sizeof(NullDriverRoutine
));
2045 /* Initialize the swappable data area */
2046 Sda
= &DosData
->Sda
;
2047 RtlZeroMemory(Sda
, sizeof(*Sda
));
2049 /* Get the current directory */
2050 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
2052 // TODO: Use some kind of default path?
2056 /* Convert it to a DOS path */
2057 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
2059 // TODO: Use some kind of default path?
2064 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2066 /* Get the directory part of the path */
2067 Path
= strchr(DosDirectory
, '\\');
2070 /* Skip the backslash */
2074 /* Set the directory */
2077 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2080 /* Set the current PSP to the system PSP */
2081 Sda
->CurrentPsp
= SYSTEM_PSP
;
2083 /* Set the initial allocation strategy to "best fit" */
2084 Sda
->AllocStrategy
= DOS_ALLOC_BEST_FIT
;
2086 /* Initialize the SFT */
2087 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2088 Sft
->Link
= 0xFFFFFFFF;
2089 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2091 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2093 /* Clear the file descriptor entry */
2094 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2097 /* Read CONFIG.SYS */
2098 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2101 while (fgetws(Buffer
, 256, Stream
))
2103 // TODO: Parse the line
2110 /* Initialize the callback context */
2111 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2113 /* Register the DOS 32-bit Interrupts */
2114 RegisterDosInt32(0x20, DosInt20h
);
2115 RegisterDosInt32(0x21, DosInt21h
);
2116 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2117 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2118 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2119 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2120 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2121 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2122 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2123 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2124 RegisterDosInt32(0x2F, DosInt2Fh
);
2126 /* Unimplemented DOS interrupts */
2127 RegisterDosInt32(0x2A, NULL
); // Network - Installation Check
2129 /* Load the CON driver */
2132 /* Load the XMS driver (HIMEM) */
2135 /* Load the EMS driver */
2136 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
2138 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2139 "Try reducing the number of EMS pages.\n");