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 #define INDOS_POINTER MAKELONG(0x00FE, 0x0070)
39 CALLBACK16 DosContext
;
41 /*static*/ BYTE CurrentDrive
;
42 static CHAR LastDrive
= 'E';
43 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
46 /* PUBLIC VARIABLES ***********************************************************/
50 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
51 BOOLEAN DoEcho
= FALSE
;
53 DWORD DiskTransferArea
;
54 WORD DosErrorLevel
= 0x0000;
55 WORD DosLastError
= 0;
57 /* PRIVATE FUNCTIONS **********************************************************/
59 static BOOLEAN
DosChangeDrive(BYTE Drive
)
61 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
63 /* Make sure the drive exists */
64 if (Drive
> (LastDrive
- 'A')) return FALSE
;
66 /* Find the path to the new current directory */
67 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
69 /* Change the current directory of the process */
70 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
72 /* Set the current drive */
79 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
85 /* Make sure the directory path is not too long */
86 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
88 DosLastError
= ERROR_PATH_NOT_FOUND
;
92 /* Get the drive number */
93 DriveNumber
= Directory
[0] - 'A';
95 /* Make sure the drive exists */
96 if (DriveNumber
> (LastDrive
- 'A'))
98 DosLastError
= ERROR_PATH_NOT_FOUND
;
102 /* Get the file attributes */
103 Attributes
= GetFileAttributesA(Directory
);
105 /* Make sure the path exists and is a directory */
106 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
107 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
109 DosLastError
= ERROR_PATH_NOT_FOUND
;
113 /* Check if this is the current drive */
114 if (DriveNumber
== CurrentDrive
)
116 /* Change the directory */
117 if (!SetCurrentDirectoryA(Directory
))
119 DosLastError
= LOWORD(GetLastError());
124 /* Get the directory part of the path */
125 Path
= strchr(Directory
, '\\');
128 /* Skip the backslash */
132 /* Set the directory for the drive */
135 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
139 CurrentDirectories
[DriveNumber
][0] = '\0';
146 static BOOLEAN
DosControlBreak(VOID
)
150 /* Call interrupt 0x23 */
151 Int32Call(&DosContext
, 0x23);
155 DosTerminateProcess(CurrentPsp
, 0, 0);
162 /* PUBLIC FUNCTIONS ***********************************************************/
164 VOID WINAPI
DosInt20h(LPWORD Stack
)
166 /* This is the exit interrupt */
167 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
170 VOID WINAPI
DosInt21h(LPWORD Stack
)
173 SYSTEMTIME SystemTime
;
175 PDOS_INPUT_BUFFER InputBuffer
;
176 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer
;
181 /* Check the value in the AH register */
184 /* Terminate Program */
187 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
191 /* Read Character from STDIN with Echo */
194 DPRINT("INT 21h, AH = 01h\n");
196 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
198 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
201 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
202 // Check also Ctrl-P and set echo-to-printer flag.
203 // Ctrl-Z is not interpreted.
209 /* Write Character to STDOUT */
212 // FIXME: Under DOS 2+, output handle may be redirected!!!!
214 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
217 * We return the output character (DOS 2.1+).
218 * Also, if we're going to output a TAB, then
219 * don't return a TAB but a SPACE instead.
220 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
221 * for more information.
223 setAL(Character
== '\t' ? ' ' : Character
);
227 /* Read Character from STDAUX */
230 // FIXME: Really read it from STDAUX!
231 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
232 // setAL(DosReadCharacter());
236 /* Write Character to STDAUX */
239 // FIXME: Really write it to STDAUX!
240 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
241 // DosPrintCharacter(getDL());
245 /* Write Character to Printer */
248 // FIXME: Really write it to printer!
249 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
250 DPRINT1("0x%p\n", getDL());
251 DPRINT1("\n\n-----------\n\n");
255 /* Direct Console I/O */
260 // FIXME: Under DOS 2+, output handle may be redirected!!!!
262 if (Character
!= 0xFF)
265 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
268 * We return the output character (DOS 2.1+).
269 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
270 * for more information.
279 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
280 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
284 /* No character available */
285 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
293 /* Character Input without Echo */
297 DPRINT("Char input without echo\n");
299 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
301 // FIXME: For 0x07, do not check Ctrl-C/Break.
302 // For 0x08, do check those control sequences and if needed,
309 /* Write string to STDOUT */
312 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
314 while (*String
!= '$')
316 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
321 * We return the terminating character (DOS 2.1+).
322 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
323 * for more information.
325 setAL('$'); // *String
329 /* Read Buffered Input */
333 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
335 DPRINT("Read Buffered Input\n");
337 while (Count
< InputBuffer
->MaxLength
)
339 /* Try to read a character (wait) */
340 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
344 /* Extended character */
347 /* Read the scancode */
348 DosReadCharacter(DOS_INPUT_HANDLE
);
355 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
356 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
358 if (DosControlBreak())
360 /* Set the character to a newline to exit the loop */
374 /* Erase the character */
375 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
376 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
377 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
385 /* Append it to the buffer */
386 InputBuffer
->Buffer
[Count
] = Character
;
388 /* Check if this is a special character */
389 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
391 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
392 Character
+= 'A' - 1;
395 /* Echo the character */
396 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
400 if (Character
== '\r') break;
401 Count
++; /* Carriage returns are NOT counted */
404 /* Update the length */
405 InputBuffer
->Length
= Count
;
410 /* Get STDIN Status */
413 setAL(DosCheckInput() ? 0xFF : 0x00);
417 /* Flush Buffer and Read STDIN */
420 BYTE InputFunction
= getAL();
422 /* Flush STDIN buffer */
423 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
426 * If the input function number contained in AL is valid, i.e.
427 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
428 * recursively with AL == AH.
430 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
431 InputFunction
== 0x07 || InputFunction
== 0x08 ||
432 InputFunction
== 0x0A)
434 /* Call ourselves recursively */
435 setAH(InputFunction
);
444 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
446 // TODO: Flush what's needed.
447 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
449 /* Clear CF in DOS 6 only */
450 if (PspBlock
->DosVersion
== 0x0006)
451 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
456 /* Set Default Drive */
459 DosChangeDrive(getDL());
460 setAL(LastDrive
- 'A' + 1);
464 /* NULL Function for CP/M Compatibility */
468 * This function corresponds to the CP/M BDOS function
469 * "get bit map of logged drives", which is meaningless
472 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
473 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
474 * for more information.
480 /* Get Default Drive */
487 /* Set Disk Transfer Area */
490 DiskTransferArea
= MAKELONG(getDX(), getDS());
494 /* NULL Function for CP/M Compatibility */
499 * Function 0x1D corresponds to the CP/M BDOS function
500 * "get bit map of read-only drives", which is meaningless
502 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
503 * for more information.
505 * Function 0x1E corresponds to the CP/M BDOS function
506 * "set file attributes", which was meaningless under MS-DOS 1.x.
507 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
508 * for more information.
514 /* NULL Function for CP/M Compatibility */
518 * This function corresponds to the CP/M BDOS function
519 * "get/set default user (sublibrary) number", which is meaningless
522 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
523 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
524 * for more information.
530 /* Set Interrupt Vector */
533 ULONG FarPointer
= MAKELONG(getDX(), getDS());
534 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
535 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
537 /* Write the new far pointer to the IDT */
538 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
545 DosClonePsp(getDX(), getCS());
549 /* Parse Filename into FCB */
552 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
553 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
554 BYTE Options
= getAL();
558 if (FileName
[1] == ':')
560 /* Set the drive number */
561 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
563 /* Skip to the file name part */
568 /* No drive number specified */
569 if (Options
& (1 << 1)) Fcb
->DriveNumber
= CurrentDrive
+ 1;
570 else Fcb
->DriveNumber
= 0;
573 /* Parse the file name */
575 while ((*FileName
> 0x20) && (i
< 8))
577 if (*FileName
== '.') break;
578 else if (*FileName
== '*')
584 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
587 /* Fill the whole field with blanks only if bit 2 is not set */
588 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
590 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
593 /* Skip to the extension part */
594 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
595 if (*FileName
== '.') FileName
++;
597 /* Now parse the extension */
601 while ((*FileName
> 0x20) && (i
< 3))
603 if (*FileName
== '*')
609 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
612 /* Fill the whole field with blanks only if bit 3 is not set */
613 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
615 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
621 /* Get System Date */
624 GetLocalTime(&SystemTime
);
625 setCX(SystemTime
.wYear
);
626 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
627 setAL(SystemTime
.wDayOfWeek
);
631 /* Set System Date */
634 GetLocalTime(&SystemTime
);
635 SystemTime
.wYear
= getCX();
636 SystemTime
.wMonth
= getDH();
637 SystemTime
.wDay
= getDL();
639 /* Return success or failure */
640 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
644 /* Get System Time */
647 GetLocalTime(&SystemTime
);
648 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
649 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
653 /* Set System Time */
656 GetLocalTime(&SystemTime
);
657 SystemTime
.wHour
= getCH();
658 SystemTime
.wMinute
= getCL();
659 SystemTime
.wSecond
= getDH();
660 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
662 /* Return success or failure */
663 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
667 /* Get Disk Transfer Area */
670 setES(HIWORD(DiskTransferArea
));
671 setBX(LOWORD(DiskTransferArea
));
675 /* Get DOS Version */
678 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
681 * DOS 2+ - GET DOS VERSION
682 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
683 * for more information.
686 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
689 * Return DOS OEM number:
690 * 0x00 for IBM PC-DOS
691 * 0x02 for packaged MS-DOS
697 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
700 * Return version flag:
701 * 1 << 3 if DOS is in ROM,
702 * 0 (reserved) if not.
707 /* Return DOS 24-bit user serial number in BL:CX */
712 * Return DOS version: Minor:Major in AH:AL
713 * The Windows NT DOS box returns version 5.00, subject to SETVER.
715 setAX(PspBlock
->DosVersion
);
720 /* Terminate and Stay Resident */
723 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
724 DosTerminateProcess(CurrentPsp
, getAL(), getDX());
728 /* Extended functionalities */
734 * DOS 5+ - GET TRUE VERSION NUMBER
735 * This function always returns the true version number, unlike
736 * AH=30h, whose return value may be changed with SETVER.
737 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
738 * for more information.
742 * Return the true DOS version: Minor:Major in BH:BL
743 * The Windows NT DOS box returns BX=3205h (version 5.50).
745 setBX(NTDOS_VERSION
);
755 // /* Invalid subfunction */
762 /* Get Address of InDOS flag */
765 setES(HIWORD(INDOS_POINTER
));
766 setBX(LOWORD(INDOS_POINTER
));
771 /* Get Interrupt Vector */
774 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
776 /* Read the address from the IDT into ES:BX */
777 setES(HIWORD(FarPointer
));
778 setBX(LOWORD(FarPointer
));
782 /* Get Free Disk Space */
785 CHAR RootPath
[3] = "X:\\";
786 DWORD SectorsPerCluster
;
787 DWORD BytesPerSector
;
788 DWORD NumberOfFreeClusters
;
789 DWORD TotalNumberOfClusters
;
791 if (getDL() == 0) RootPath
[0] = 'A' + CurrentDrive
;
792 else RootPath
[0] = 'A' + getDL() - 1;
794 if (GetDiskFreeSpaceA(RootPath
,
797 &NumberOfFreeClusters
,
798 &TotalNumberOfClusters
))
800 setAX(LOWORD(SectorsPerCluster
));
801 setCX(LOWORD(BytesPerSector
));
802 setBX(LOWORD(NumberOfFreeClusters
));
803 setDX(LOWORD(TotalNumberOfClusters
));
814 /* SWITCH character - AVAILDEV */
820 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
821 * This setting is ignored by MS-DOS 4.0+.
822 * MS-DOS 5+ always return AL=00h/DL=2Fh.
823 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
824 * for more information.
829 else if (getAL() == 0x01)
832 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
833 * This setting is ignored by MS-DOS 5+.
834 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
835 * for more information.
840 else if (getAL() == 0x02)
843 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
844 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
845 * for more information.
850 else if (getAL() == 0x03)
853 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
854 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
855 * for more information.
862 /* Invalid subfunction */
869 /* Get/Set Country-dependent Information */
872 CountryCodeBuffer
= (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
877 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
878 &CountryCodeBuffer
->TimeFormat
,
879 sizeof(CountryCodeBuffer
->TimeFormat
) / sizeof(TCHAR
));
882 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
883 setAX(LOWORD(GetLastError()));
887 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
888 &CountryCodeBuffer
->CurrencySymbol
,
889 sizeof(CountryCodeBuffer
->CurrencySymbol
) / sizeof(TCHAR
));
892 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
893 setAX(LOWORD(GetLastError()));
897 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
898 &CountryCodeBuffer
->ThousandSep
,
899 sizeof(CountryCodeBuffer
->ThousandSep
) / sizeof(TCHAR
));
902 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
903 setAX(LOWORD(GetLastError()));
907 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
908 &CountryCodeBuffer
->DecimalSep
,
909 sizeof(CountryCodeBuffer
->DecimalSep
) / sizeof(TCHAR
));
912 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
913 setAX(LOWORD(GetLastError()));
917 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
921 // TODO: NOT IMPLEMENTED
928 /* Create Directory */
931 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
933 if (CreateDirectoryA(String
, NULL
))
935 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
939 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
940 setAX(LOWORD(GetLastError()));
946 /* Remove Directory */
949 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
951 if (RemoveDirectoryA(String
))
953 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
957 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
958 setAX(LOWORD(GetLastError()));
964 /* Set Current Directory */
967 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
969 if (DosChangeDirectory(String
))
971 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
975 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
982 /* Create or Truncate File */
986 WORD ErrorCode
= DosCreateFile(&FileHandle
,
987 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
991 if (ErrorCode
== ERROR_SUCCESS
)
993 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
998 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1005 /* Open File or Device */
1009 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1010 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
1012 if (ErrorCode
== ERROR_SUCCESS
)
1014 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1019 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1026 /* Close File or Device */
1029 if (DosCloseHandle(getBX()))
1031 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1035 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1036 setAX(ERROR_INVALID_HANDLE
);
1042 /* Read from File or Device */
1048 DPRINT("DosReadFile(0x%04X)\n", getBX());
1051 ErrorCode
= DosReadFile(getBX(),
1052 MAKELONG(getDX(), getDS()),
1057 if (ErrorCode
== ERROR_SUCCESS
)
1059 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1062 else if (ErrorCode
!= ERROR_NOT_READY
)
1064 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1071 /* Write to File or Device */
1074 WORD BytesWritten
= 0;
1075 WORD ErrorCode
= DosWriteFile(getBX(),
1076 MAKELONG(getDX(), getDS()),
1080 if (ErrorCode
== ERROR_SUCCESS
)
1082 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1083 setAX(BytesWritten
);
1087 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1097 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1099 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1101 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1103 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1104 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1106 setAL(FileName
[0] - 'A');
1110 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1111 setAX(GetLastError());
1121 WORD ErrorCode
= DosSeekFile(getBX(),
1122 MAKELONG(getDX(), getCX()),
1126 if (ErrorCode
== ERROR_SUCCESS
)
1128 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1130 /* Return the new offset in DX:AX */
1131 setDX(HIWORD(NewLocation
));
1132 setAX(LOWORD(NewLocation
));
1136 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1143 /* Get/Set File Attributes */
1147 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1149 if (getAL() == 0x00)
1151 /* Get the attributes */
1152 Attributes
= GetFileAttributesA(FileName
);
1154 /* Check if it failed */
1155 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1157 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1158 setAX(GetLastError());
1162 /* Return the attributes that DOS can understand */
1163 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1164 setCX(Attributes
& 0x00FF);
1167 else if (getAL() == 0x01)
1169 /* Try to set the attributes */
1170 if (SetFileAttributesA(FileName
, getCL()))
1172 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1176 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1177 setAX(GetLastError());
1182 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1183 setAX(ERROR_INVALID_FUNCTION
);
1192 WORD Length
= getCX();
1194 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1196 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1201 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1202 setAX(DosLastError
);
1208 /* Duplicate Handle */
1211 WORD NewHandle
= DosDuplicateHandle(getBX());
1213 if (NewHandle
!= INVALID_DOS_HANDLE
)
1216 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1220 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1221 setAX(DosLastError
);
1227 /* Force Duplicate Handle */
1230 if (DosForceDuplicateHandle(getBX(), getCX()))
1232 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1236 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1237 setAX(ERROR_INVALID_HANDLE
);
1243 /* Get Current Directory */
1246 BYTE DriveNumber
= getDL();
1247 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1249 /* Get the real drive number */
1250 if (DriveNumber
== 0)
1252 DriveNumber
= CurrentDrive
;
1256 /* Decrement DriveNumber since it was 1-based */
1260 if (DriveNumber
<= LastDrive
- 'A')
1263 * Copy the current directory into the target buffer.
1264 * It doesn't contain the drive letter and the backslash.
1266 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1267 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1268 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1272 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1273 setAX(ERROR_INVALID_DRIVE
);
1279 /* Allocate Memory */
1282 WORD MaxAvailable
= 0;
1283 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1287 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1292 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1293 setAX(DosLastError
);
1294 setBX(MaxAvailable
);
1303 if (DosFreeMemory(getES()))
1305 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1309 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1310 setAX(ERROR_ARENA_TRASHED
);
1316 /* Resize Memory Block */
1321 if (DosResizeMemory(getES(), getBX(), &Size
))
1323 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1327 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1328 setAX(DosLastError
);
1338 BYTE OrgAL
= getAL();
1339 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1340 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1341 DWORD ReturnAddress
= MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]);
1344 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1346 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1349 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1351 /* Create a new process */
1352 ErrorCode
= DosCreateProcess(ProgramName
,
1359 /* Just load an executable */
1360 ErrorCode
= DosLoadExecutable(LoadType
,
1368 else if (OrgAL
== 0x05)
1370 // http://www.ctyme.com/intr/rb-2942.htm
1371 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1372 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1376 ErrorCode
= ERROR_INVALID_FUNCTION
;
1379 if (ErrorCode
== ERROR_SUCCESS
)
1381 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1385 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1392 /* Terminate With Return Code */
1395 DosTerminateProcess(CurrentPsp
, getAL(), 0);
1399 /* Get Return Code (ERRORLEVEL) */
1403 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1404 * DosErrorLevel is cleared after being read by this function.
1406 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1407 setAX(DosErrorLevel
);
1408 DosErrorLevel
= 0x0000; // Clear it
1412 /* Find First File */
1415 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
1416 SEG_OFF_TO_PTR(getDS(), getDX()),
1421 if (Result
== ERROR_SUCCESS
)
1422 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1424 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1429 /* Find Next File */
1432 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
1436 if (Result
== ERROR_SUCCESS
)
1437 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1439 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1444 /* Internal - Set Current Process ID (Set PSP Address) */
1447 DosSetProcessContext(getBX());
1451 /* Internal - Get Current Process ID (Get PSP Address) */
1453 /* Get Current PSP Address */
1457 * Undocumented AH=51h is identical to the documented AH=62h.
1458 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1459 * and http://www.ctyme.com/intr/rb-3140.htm
1460 * for more information.
1466 /* Internal - Get "List of lists" (SYSVARS) */
1470 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1471 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1472 * for more information.
1475 /* Return the DOS "list of lists" in ES:BX */
1476 setES(DOS_DATA_SEGMENT
);
1477 setBX(FIELD_OFFSET(DOS_SYSVARS
, FirstDpb
));
1482 /* Create Child PSP */
1485 DosCreatePsp(getDX(), getSI());
1492 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1493 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1496 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1497 * for more information.
1500 if (MoveFileA(ExistingFileName
, NewFileName
))
1502 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1506 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1507 setAX(GetLastError());
1513 /* Get/Set Memory Management Options */
1516 if (getAL() == 0x00)
1518 /* Get allocation strategy */
1519 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1520 setAX(DosAllocStrategy
);
1522 else if (getAL() == 0x01)
1524 /* Set allocation strategy */
1526 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1527 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1529 /* Can't set both */
1530 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1531 setAX(ERROR_INVALID_PARAMETER
);
1535 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
1537 /* Invalid allocation strategy */
1538 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1539 setAX(ERROR_INVALID_PARAMETER
);
1543 DosAllocStrategy
= getBL();
1544 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1546 else if (getAL() == 0x02)
1548 /* Get UMB link state */
1549 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1550 setAL(DosUmbLinked
? 0x01 : 0x00);
1552 else if (getAL() == 0x03)
1554 /* Set UMB link state */
1555 if (getBX()) DosLinkUmb();
1556 else DosUnlinkUmb();
1557 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1561 /* Invalid or unsupported function */
1562 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1563 setAX(ERROR_INVALID_FUNCTION
);
1569 /* Get Extended Error Information */
1572 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1577 /* Create Temporary File */
1580 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1581 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1587 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1588 * for more information.
1591 // FIXME: Check for buffer validity?
1592 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
1593 // to receive the generated filename.
1595 /* First create the temporary file */
1596 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1599 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1600 setAX(GetLastError());
1604 /* Now try to open it in read/write access */
1605 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1606 if (ErrorCode
== ERROR_SUCCESS
)
1608 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1613 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1620 /* Create New File */
1624 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1625 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1629 if (ErrorCode
== ERROR_SUCCESS
)
1631 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1636 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1643 /* Lock/Unlock Region of File */
1646 if (getAL() == 0x00)
1648 /* Lock region of file */
1649 if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1651 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1655 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1656 setAX(DosLastError
);
1659 else if (getAL() == 0x01)
1661 /* Unlock region of file */
1662 if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1664 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1668 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1669 setAX(DosLastError
);
1674 /* Invalid subfunction */
1675 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1676 setAX(ERROR_INVALID_FUNCTION
);
1682 /* Canonicalize File Name or Path */
1686 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1687 * for more information.
1691 * We suppose that the DOS app gave to us a valid
1692 * 128-byte long buffer for the canonicalized name.
1694 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1696 SEG_OFF_TO_PTR(getES(), getDI()),
1700 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1701 setAX(GetLastError());
1705 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1709 // FIXME: Convert the full path name into short version.
1710 // We cannot reliably use GetShortPathName, because it fails
1711 // if the path name given doesn't exist. However this DOS
1712 // function AH=60h should be able to work even for non-existing
1713 // path and file names.
1718 /* Set Handle Count */
1721 if (!DosResizeHandleTable(getBX()))
1723 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1724 setAX(DosLastError
);
1726 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1736 * Function 6Ah is identical to function 68h,
1737 * and sets AH to 68h if success.
1738 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1739 * for more information.
1743 if (DosFlushFileBuffers(getBX()))
1745 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1749 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1750 setAX(GetLastError());
1756 /* Extended Open/Create */
1760 WORD CreationStatus
;
1763 /* Check for AL == 00 */
1764 if (getAL() != 0x00)
1766 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1767 setAX(ERROR_INVALID_FUNCTION
);
1772 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1773 * for the full detailed description.
1775 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1778 ErrorCode
= DosCreateFileEx(&FileHandle
,
1780 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1785 if (ErrorCode
== ERROR_SUCCESS
)
1787 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1788 setCX(CreationStatus
);
1793 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1803 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1806 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1807 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1814 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
1816 /* Set CF to terminate the running process */
1817 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1820 VOID WINAPI
DosFastConOut(LPWORD Stack
)
1823 * This is the DOS 2+ Fast Console Output Interrupt.
1824 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1826 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1827 * for more information.
1830 /* Save AX and BX */
1831 USHORT AX
= getAX();
1832 USHORT BX
= getBX();
1835 * Set the parameters:
1836 * AL contains the character to print (already set),
1837 * BL contains the character attribute,
1838 * BH contains the video page to use.
1840 setBL(DOS_CHAR_ATTRIBUTE
);
1841 setBH(Bda
->VideoPage
);
1843 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
1845 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
1847 /* Restore AX and BX */
1852 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
1856 /* Extended Memory Specification */
1860 if (!XmsGetDriverEntry(&DriverEntry
)) break;
1862 if (getAL() == 0x00)
1864 /* The driver is loaded */
1867 else if (getAL() == 0x10)
1869 setES(HIWORD(DriverEntry
));
1870 setBX(LOWORD(DriverEntry
));
1874 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
1882 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1884 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1889 BOOLEAN
DosKRNLInitialize(VOID
)
1894 CHAR CurrentDirectory
[MAX_PATH
];
1895 CHAR DosDirectory
[DOS_DIR_LENGTH
];
1899 const BYTE NullDriverRoutine
[] = {
1900 /* Strategy routine entry */
1901 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
1904 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
1905 LOBYTE(DOS_DEVSTAT_DONE
),
1906 HIBYTE(DOS_DEVSTAT_DONE
),
1908 /* Interrupt routine entry */
1915 /* Setup the InDOS flag */
1916 InDos
= (PBYTE
)FAR_POINTER(INDOS_POINTER
);
1919 /* Clear the current directory buffer */
1920 RtlZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
1922 /* Get the current directory */
1923 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
1925 // TODO: Use some kind of default path?
1929 /* Convert that to a DOS path */
1930 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
1932 // TODO: Use some kind of default path?
1937 CurrentDrive
= DosDirectory
[0] - 'A';
1939 /* Get the directory part of the path */
1940 Path
= strchr(DosDirectory
, '\\');
1943 /* Skip the backslash */
1947 /* Set the directory */
1950 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
1953 /* Read CONFIG.SYS */
1954 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
1957 while (fgetws(Buffer
, 256, Stream
))
1959 // TODO: Parse the line
1964 /* Initialize the list of lists */
1965 SysVars
= (PDOS_SYSVARS
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0);
1966 RtlZeroMemory(SysVars
, sizeof(DOS_SYSVARS
));
1967 SysVars
->FirstMcb
= FIRST_MCB_SEGMENT
;
1968 SysVars
->FirstSft
= MAKELONG(MASTER_SFT_OFFSET
, DOS_DATA_SEGMENT
);
1970 /* Initialize the NUL device driver */
1971 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
1972 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
1973 SysVars
->NullDevice
.StrategyRoutine
= FIELD_OFFSET(DOS_SYSVARS
, NullDriverRoutine
);
1974 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
1975 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
1976 sizeof(SysVars
->NullDevice
.DeviceName
),
1978 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
1979 RtlCopyMemory(SysVars
->NullDriverRoutine
,
1981 sizeof(NullDriverRoutine
));
1983 /* Initialize the SFT */
1984 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
1985 Sft
->Link
= 0xFFFFFFFF;
1986 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
1988 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
1990 /* Clear the file descriptor entry */
1991 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
1996 /* Initialize the callback context */
1997 InitializeContext(&DosContext
, 0x0070, 0x0000);
1999 /* Register the DOS 32-bit Interrupts */
2000 RegisterDosInt32(0x20, DosInt20h
);
2001 RegisterDosInt32(0x21, DosInt21h
);
2002 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2003 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2004 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2005 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2006 RegisterDosInt32(0x2F, DosInt2Fh
);
2008 /* Load the EMS driver */
2009 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
2011 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2012 "Try reducing the number of EMS pages.\n");
2015 /* Load the XMS driver (HIMEM) */
2018 /* Load the CON driver */