2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
29 #include "bios/bios.h"
32 #include "hardware/ps2.h"
36 /* PRIVATE VARIABLES **********************************************************/
38 CALLBACK16 DosContext
;
40 /* PUBLIC VARIABLES ***********************************************************/
42 /* Global DOS data area contained in guest memory */
44 /* Easy accessors to useful DOS data area parts */
48 /* 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 "X:..." */
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 and set the current directory for the drive */
150 Path
= strchr(DosDirectory
, '\\');
153 Path
++; // Skip the backslash
154 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
158 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
165 static BOOLEAN
DosControlBreak(VOID
)
169 /* Call interrupt 0x23 */
170 Int32Call(&DosContext
, 0x23);
174 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
181 /* PUBLIC FUNCTIONS ***********************************************************/
183 VOID WINAPI
DosInt20h(LPWORD Stack
)
186 * This is the exit interrupt (alias to INT 21h, AH=00h).
187 * CS must be the PSP segment.
189 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
192 VOID WINAPI
DosInt21h(LPWORD Stack
)
195 SYSTEMTIME SystemTime
;
200 /* Save the value of SS:SP on entry in the PSP */
201 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
202 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
204 /* Check the value in the AH register */
207 /* Terminate Program */
210 /* CS must be the PSP segment */
211 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
215 /* Read Character from STDIN with Echo */
218 DPRINT("INT 21h, AH = 01h\n");
220 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
222 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
225 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
226 // Check also Ctrl-P and set echo-to-printer flag.
227 // Ctrl-Z is not interpreted.
233 /* Write Character to STDOUT */
236 // FIXME: Under DOS 2+, output handle may be redirected!!!!
238 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
241 * We return the output character (DOS 2.1+).
242 * Also, if we're going to output a TAB, then
243 * don't return a TAB but a SPACE instead.
244 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
245 * for more information.
247 setAL(Character
== '\t' ? ' ' : Character
);
251 /* Read Character from STDAUX */
254 // FIXME: Really read it from STDAUX!
255 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
256 // setAL(DosReadCharacter());
260 /* Write Character to STDAUX */
263 // FIXME: Really write it to STDAUX!
264 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
265 // DosPrintCharacter(getDL());
269 /* Write Character to Printer */
272 // FIXME: Really write it to printer!
273 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
274 DPRINT1("0x%p\n", getDL());
275 DPRINT1("\n\n-----------\n\n");
279 /* Direct Console I/O */
284 // FIXME: Under DOS 2+, output handle may be redirected!!!!
286 if (Character
!= 0xFF)
289 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
292 * We return the output character (DOS 2.1+).
293 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
294 * for more information.
303 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
304 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
308 /* No character available */
309 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
317 /* Character Input without Echo */
321 DPRINT("Char input without echo\n");
323 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
325 // FIXME: For 0x07, do not check Ctrl-C/Break.
326 // For 0x08, do check those control sequences and if needed,
333 /* Write String to STDOUT */
336 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
338 while (*String
!= '$')
340 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
345 * We return the terminating character (DOS 2.1+).
346 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
347 * for more information.
349 setAL('$'); // *String
353 /* Read Buffered Input */
357 PDOS_INPUT_BUFFER InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
359 DPRINT("Read Buffered Input\n");
361 while (Count
< InputBuffer
->MaxLength
)
363 /* Try to read a character (wait) */
364 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
368 /* Extended character */
371 /* Read the scancode */
372 DosReadCharacter(DOS_INPUT_HANDLE
);
379 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
380 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
382 if (DosControlBreak())
384 /* Set the character to a newline to exit the loop */
398 /* Erase the character */
399 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
400 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
401 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
409 /* Append it to the buffer */
410 InputBuffer
->Buffer
[Count
] = Character
;
412 /* Check if this is a special character */
413 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
415 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
416 Character
+= 'A' - 1;
419 /* Echo the character */
420 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
424 if (Character
== '\r') break;
425 if (Character
== '\b') continue;
426 Count
++; /* Carriage returns are NOT counted */
429 /* Update the length */
430 InputBuffer
->Length
= Count
;
435 /* Get STDIN Status */
438 setAL(DosCheckInput() ? 0xFF : 0x00);
442 /* Flush Buffer and Read STDIN */
445 BYTE InputFunction
= getAL();
447 /* Flush STDIN buffer */
448 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
451 * If the input function number contained in AL is valid, i.e.
452 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
453 * recursively with AL == AH.
455 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
456 InputFunction
== 0x07 || InputFunction
== 0x08 ||
457 InputFunction
== 0x0A)
459 /* Call ourselves recursively */
460 setAH(InputFunction
);
469 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
471 // TODO: Flush what's needed.
472 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
474 /* Clear CF in DOS 6 only */
475 if (PspBlock
->DosVersion
== 0x0006)
476 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
481 /* Set Default Drive */
484 DosChangeDrive(getDL());
485 setAL(SysVars
->NumLocalDrives
);
489 /* NULL Function for CP/M Compatibility */
493 * This function corresponds to the CP/M BDOS function
494 * "get bit map of logged drives", which is meaningless
497 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
498 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
499 * for more information.
505 /* Get Default Drive */
508 setAL(Sda
->CurrentDrive
);
512 /* Set Disk Transfer Area */
515 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
519 /* NULL Function for CP/M Compatibility */
524 * Function 0x1D corresponds to the CP/M BDOS function
525 * "get bit map of read-only drives", which is meaningless
527 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
528 * for more information.
530 * Function 0x1E corresponds to the CP/M BDOS function
531 * "set file attributes", which was meaningless under MS-DOS 1.x.
532 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
533 * for more information.
539 /* NULL Function for CP/M Compatibility */
543 * This function corresponds to the CP/M BDOS function
544 * "get/set default user (sublibrary) number", which is meaningless
547 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
548 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
549 * for more information.
555 /* Set Interrupt Vector */
558 ULONG FarPointer
= MAKELONG(getDX(), getDS());
559 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
560 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
562 /* Write the new far pointer to the IDT */
563 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
570 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */
571 DosClonePsp(getDX(), Stack
[STACK_CS
]);
575 /* Parse Filename into FCB */
578 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
579 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
580 BYTE Options
= getAL();
584 if (FileName
[1] == ':')
586 /* Set the drive number */
587 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
589 /* Skip to the file name part */
594 /* No drive number specified */
595 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
596 else Fcb
->DriveNumber
= 0;
599 /* Parse the file name */
601 while ((*FileName
> 0x20) && (i
< 8))
603 if (*FileName
== '.') break;
604 else if (*FileName
== '*')
610 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
613 /* Fill the whole field with blanks only if bit 2 is not set */
614 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
616 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
619 /* Skip to the extension part */
620 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
621 if (*FileName
== '.') FileName
++;
623 /* Now parse the extension */
627 while ((*FileName
> 0x20) && (i
< 3))
629 if (*FileName
== '*')
635 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
638 /* Fill the whole field with blanks only if bit 3 is not set */
639 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
641 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
647 /* Get System Date */
650 GetLocalTime(&SystemTime
);
651 setCX(SystemTime
.wYear
);
652 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
653 setAL(SystemTime
.wDayOfWeek
);
657 /* Set System Date */
660 GetLocalTime(&SystemTime
);
661 SystemTime
.wYear
= getCX();
662 SystemTime
.wMonth
= getDH();
663 SystemTime
.wDay
= getDL();
665 /* Return success or failure */
666 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
670 /* Get System Time */
673 GetLocalTime(&SystemTime
);
674 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
675 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
679 /* Set System Time */
682 GetLocalTime(&SystemTime
);
683 SystemTime
.wHour
= getCH();
684 SystemTime
.wMinute
= getCL();
685 SystemTime
.wSecond
= getDH();
686 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
688 /* Return success or failure */
689 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
693 /* Get Disk Transfer Area */
696 setES(HIWORD(Sda
->DiskTransferArea
));
697 setBX(LOWORD(Sda
->DiskTransferArea
));
701 /* Get DOS Version */
704 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
707 * DOS 2+ - GET DOS VERSION
708 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
709 * for more information.
712 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
715 * Return DOS OEM number:
716 * 0x00 for IBM PC-DOS
717 * 0x02 for packaged MS-DOS
723 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
726 * Return version flag:
727 * 1 << 3 if DOS is in ROM,
728 * 0 (reserved) if not.
733 /* Return DOS 24-bit user serial number in BL:CX */
738 * Return DOS version: Minor:Major in AH:AL
739 * The Windows NT DOS box returns version 5.00, subject to SETVER.
741 setAX(PspBlock
->DosVersion
);
746 /* Terminate and Stay Resident */
749 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
750 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
754 /* Extended functionalities */
760 * DOS 4+ - GET BOOT DRIVE
764 setDL(SysVars
->BootDrive
);
769 * DOS 5+ - GET TRUE VERSION NUMBER
770 * This function always returns the true version number, unlike
771 * AH=30h, whose return value may be changed with SETVER.
772 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
773 * for more information.
778 * Return the true DOS version: Minor:Major in BH:BL
779 * The Windows NT DOS box returns BX=3205h (version 5.50).
781 setBX(NTDOS_VERSION
);
792 default: // goto Default;
794 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
802 /* Get Address of InDOS flag */
805 setES(DOS_DATA_SEGMENT
);
806 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
810 /* Get Interrupt Vector */
813 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
815 /* Read the address from the IDT into ES:BX */
816 setES(HIWORD(FarPointer
));
817 setBX(LOWORD(FarPointer
));
821 /* Get Free Disk Space */
824 CHAR RootPath
[] = "?:\\";
825 DWORD SectorsPerCluster
;
826 DWORD BytesPerSector
;
827 DWORD NumberOfFreeClusters
;
828 DWORD TotalNumberOfClusters
;
831 RootPath
[0] = 'A' + Sda
->CurrentDrive
;
833 RootPath
[0] = 'A' + getDL() - 1;
835 if (GetDiskFreeSpaceA(RootPath
,
838 &NumberOfFreeClusters
,
839 &TotalNumberOfClusters
))
841 setAX(LOWORD(SectorsPerCluster
));
842 setCX(LOWORD(BytesPerSector
));
843 setBX(min(NumberOfFreeClusters
, 0xFFFF));
844 setDX(min(TotalNumberOfClusters
, 0xFFFF));
855 /* SWITCH character - AVAILDEV */
861 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
862 * This setting is ignored by MS-DOS 4.0+.
863 * MS-DOS 5+ always return AL=00h/DL=2Fh.
864 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
865 * for more information.
873 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
874 * This setting is ignored by MS-DOS 5+.
875 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
876 * for more information.
884 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
885 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
886 * for more information.
894 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
895 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
896 * for more information.
903 /* Invalid subfunction */
912 /* Get/Set Country-dependent Information */
915 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
918 ErrorCode
= DosGetCountryInfo(&CountryId
,
919 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
921 if (ErrorCode
== ERROR_SUCCESS
)
923 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
928 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
935 /* Create Directory */
938 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
940 if (CreateDirectoryA(String
, NULL
))
942 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
946 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
947 setAX(LOWORD(GetLastError()));
953 /* Remove Directory */
956 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
958 if (RemoveDirectoryA(String
))
960 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
964 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
965 setAX(LOWORD(GetLastError()));
971 /* Set Current Directory */
974 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
976 if (DosChangeDirectory(String
))
978 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
982 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
983 setAX(Sda
->LastErrorCode
);
989 /* Create or Truncate File */
993 WORD ErrorCode
= DosCreateFile(&FileHandle
,
994 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
998 if (ErrorCode
== ERROR_SUCCESS
)
1000 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1005 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1012 /* Open File or Device */
1016 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1017 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
1019 if (ErrorCode
== ERROR_SUCCESS
)
1021 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1026 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1033 /* Close File or Device */
1036 if (DosCloseHandle(getBX()))
1038 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1042 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1043 setAX(ERROR_INVALID_HANDLE
);
1049 /* Read from File or Device */
1055 DPRINT("DosReadFile(0x%04X)\n", getBX());
1058 ErrorCode
= DosReadFile(getBX(),
1059 MAKELONG(getDX(), getDS()),
1064 if (ErrorCode
== ERROR_SUCCESS
)
1066 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1069 else if (ErrorCode
!= ERROR_NOT_READY
)
1071 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1078 /* Write to File or Device */
1081 WORD BytesWritten
= 0;
1082 WORD ErrorCode
= DosWriteFile(getBX(),
1083 MAKELONG(getDX(), getDS()),
1087 if (ErrorCode
== ERROR_SUCCESS
)
1089 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1090 setAX(BytesWritten
);
1094 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1104 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1106 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1108 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1110 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1111 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1113 setAL(RtlUpperChar(FileName
[0]) - 'A');
1117 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1118 setAX(GetLastError());
1128 WORD ErrorCode
= DosSeekFile(getBX(),
1129 MAKELONG(getDX(), getCX()),
1133 if (ErrorCode
== ERROR_SUCCESS
)
1135 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1137 /* Return the new offset in DX:AX */
1138 setDX(HIWORD(NewLocation
));
1139 setAX(LOWORD(NewLocation
));
1143 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1150 /* Get/Set File Attributes */
1154 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1156 if (getAL() == 0x00)
1158 /* Get the attributes */
1159 Attributes
= GetFileAttributesA(FileName
);
1161 /* Check if it failed */
1162 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1164 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1165 setAX(GetLastError());
1169 /* Return the attributes that DOS can understand */
1170 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1171 setCX(Attributes
& 0x00FF);
1174 else if (getAL() == 0x01)
1176 /* Try to set the attributes */
1177 if (SetFileAttributesA(FileName
, getCL()))
1179 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1183 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1184 setAX(GetLastError());
1189 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1190 setAX(ERROR_INVALID_FUNCTION
);
1199 WORD Length
= getCX();
1201 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1203 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1208 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1209 setAX(Sda
->LastErrorCode
);
1215 /* Duplicate Handle */
1218 WORD NewHandle
= DosDuplicateHandle(getBX());
1220 if (NewHandle
!= INVALID_DOS_HANDLE
)
1223 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1227 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1228 setAX(Sda
->LastErrorCode
);
1234 /* Force Duplicate Handle */
1237 if (DosForceDuplicateHandle(getBX(), getCX()))
1239 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1243 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1244 setAX(ERROR_INVALID_HANDLE
);
1250 /* Get Current Directory */
1253 BYTE DriveNumber
= getDL();
1254 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1256 /* Get the real drive number */
1257 if (DriveNumber
== 0)
1259 DriveNumber
= Sda
->CurrentDrive
;
1263 /* Decrement DriveNumber since it was 1-based */
1267 if (DriveNumber
< SysVars
->NumLocalDrives
)
1270 * Copy the current directory into the target buffer.
1271 * It doesn't contain the drive letter and the backslash.
1273 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1274 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1275 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1279 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1280 setAX(ERROR_INVALID_DRIVE
);
1286 /* Allocate Memory */
1289 WORD MaxAvailable
= 0;
1290 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1294 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1299 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1300 setAX(Sda
->LastErrorCode
);
1301 setBX(MaxAvailable
);
1310 if (DosFreeMemory(getES()))
1312 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1316 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1317 setAX(Sda
->LastErrorCode
);
1323 /* Resize Memory Block */
1328 if (DosResizeMemory(getES(), getBX(), &Size
))
1330 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1334 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1335 setAX(Sda
->LastErrorCode
);
1345 BYTE OrgAL
= getAL();
1346 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1347 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1350 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1352 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1354 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1356 /* Create a new process */
1357 ErrorCode
= DosCreateProcess(ProgramName
,
1359 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1363 /* Just load an executable */
1364 ErrorCode
= DosLoadExecutable(LoadType
,
1369 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1372 else if (OrgAL
== 0x05)
1374 // http://www.ctyme.com/intr/rb-2942.htm
1375 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1376 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1380 ErrorCode
= ERROR_INVALID_FUNCTION
;
1383 if (ErrorCode
== ERROR_SUCCESS
)
1385 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1389 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1396 /* Terminate with Return Code */
1399 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1403 /* Get Return Code (ERRORLEVEL) */
1407 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1408 * DosErrorLevel is cleared after being read by this function.
1410 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1411 setAX(Sda
->ErrorLevel
);
1412 Sda
->ErrorLevel
= 0x0000; // Clear it
1416 /* Find First File */
1419 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1420 SEG_OFF_TO_PTR(getDS(), getDX()),
1425 if (Result
== ERROR_SUCCESS
)
1426 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1428 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1433 /* Find Next File */
1436 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1440 if (Result
== ERROR_SUCCESS
)
1441 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1443 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1448 /* Internal - Set Current Process ID (Set PSP Address) */
1451 DosSetProcessContext(getBX());
1455 /* Internal - Get Current Process ID (Get PSP Address) */
1457 /* Get Current PSP Address */
1461 * Undocumented AH=51h is identical to the documented AH=62h.
1462 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1463 * and http://www.ctyme.com/intr/rb-3140.htm
1464 * for more information.
1466 setBX(Sda
->CurrentPsp
);
1470 /* Internal - Get "List of lists" (SYSVARS) */
1474 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1475 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1476 * for more information.
1479 /* Return the DOS "list of lists" in ES:BX */
1480 setES(DOS_DATA_SEGMENT
);
1481 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1485 /* Create Child PSP */
1488 DosCreatePsp(getDX(), getSI());
1489 DosSetProcessContext(getDX());
1496 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1497 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1501 * for more information.
1504 if (MoveFileA(ExistingFileName
, NewFileName
))
1506 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1510 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1511 setAX(GetLastError());
1517 /* File Attributes */
1522 /* Get File's last-written Date and Time */
1525 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1526 FILETIME LastWriteTime
;
1527 WORD FileDate
, FileTime
;
1529 if (Descriptor
== NULL
)
1531 /* Invalid handle */
1532 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1533 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1534 setAX(ERROR_INVALID_HANDLE
);
1538 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1540 /* Invalid for devices */
1541 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1542 // setAX(ERROR_INVALID_FUNCTION);
1543 setAX(ERROR_INVALID_HANDLE
);
1548 * Retrieve the last-written Win32 date and time,
1549 * and convert it to DOS format.
1551 if (!GetFileTime(Descriptor
->Win32Handle
,
1552 NULL
, NULL
, &LastWriteTime
) ||
1553 !FileTimeToDosDateTime(&LastWriteTime
,
1554 &FileDate
, &FileTime
))
1556 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1557 setAX(GetLastError());
1561 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1567 /* Set File's last-written Date and Time */
1570 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1571 FILETIME LastWriteTime
;
1572 WORD FileDate
= getDX();
1573 WORD FileTime
= getCX();
1575 if (Descriptor
== NULL
)
1577 /* Invalid handle */
1578 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1579 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1580 setAX(ERROR_INVALID_HANDLE
);
1584 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1586 /* Invalid for devices */
1587 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1588 // setAX(ERROR_INVALID_FUNCTION);
1589 setAX(ERROR_INVALID_HANDLE
);
1594 * Convert the new last-written DOS date and time
1595 * to Win32 format and set it.
1597 if (!DosDateTimeToFileTime(FileDate
, FileTime
,
1599 !SetFileTime(Descriptor
->Win32Handle
,
1600 NULL
, NULL
, &LastWriteTime
))
1602 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1603 setAX(GetLastError());
1607 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1611 default: // goto Default;
1613 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1621 /* Get/Set Memory Management Options */
1626 /* Get allocation strategy */
1629 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1630 setAX(Sda
->AllocStrategy
);
1634 /* Set allocation strategy */
1637 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1638 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1640 /* Can't set both */
1641 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1642 setAX(ERROR_INVALID_PARAMETER
);
1646 if ((getBL() & ~(DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1647 > DOS_ALLOC_LAST_FIT
)
1649 /* Invalid allocation strategy */
1650 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1651 setAX(ERROR_INVALID_PARAMETER
);
1655 Sda
->AllocStrategy
= getBL();
1656 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1660 /* Get UMB link state */
1663 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1664 setAL(SysVars
->UmbLinked
? 0x01 : 0x00);
1668 /* Set UMB link state */
1674 Success
= DosLinkUmb();
1676 Success
= DosUnlinkUmb();
1679 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1681 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1686 /* Invalid or unsupported function */
1689 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1690 setAX(ERROR_INVALID_FUNCTION
);
1697 /* Get Extended Error Information */
1700 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1705 /* Create Temporary File */
1708 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1709 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1715 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1716 * for more information.
1719 // FIXME: Check for buffer validity?
1720 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1721 // to receive the generated filename.
1723 /* First create the temporary file */
1724 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1727 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1728 setAX(GetLastError());
1732 /* Now try to open it in read/write access */
1733 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1734 if (ErrorCode
== ERROR_SUCCESS
)
1736 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1741 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1748 /* Create New File */
1752 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1753 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1757 if (ErrorCode
== ERROR_SUCCESS
)
1759 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1764 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1771 /* Lock/Unlock Region of File */
1774 if (getAL() == 0x00)
1776 /* Lock region of file */
1777 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1779 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1783 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1784 setAX(Sda
->LastErrorCode
);
1787 else if (getAL() == 0x01)
1789 /* Unlock region of file */
1790 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1792 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1796 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1797 setAX(Sda
->LastErrorCode
);
1802 /* Invalid subfunction */
1803 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1804 setAX(ERROR_INVALID_FUNCTION
);
1810 /* Canonicalize File Name or Path */
1814 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1815 * for more information.
1819 * We suppose that the DOS app gave to us a valid
1820 * 128-byte long buffer for the canonicalized name.
1822 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1824 SEG_OFF_TO_PTR(getES(), getDI()),
1828 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1829 setAX(GetLastError());
1833 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1837 // FIXME: Convert the full path name into short version.
1838 // We cannot reliably use GetShortPathName, because it fails
1839 // if the path name given doesn't exist. However this DOS
1840 // function AH=60h should be able to work even for non-existing
1841 // path and file names.
1846 /* Miscellaneous Internal Functions */
1851 /* Get Swappable Data Area */
1854 setDS(DOS_DATA_SEGMENT
);
1855 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1856 setCX(sizeof(DOS_SDA
));
1857 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1859 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1863 default: // goto Default;
1865 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1873 /* Extended Country Information */
1878 case 0x01: case 0x02: case 0x03:
1879 case 0x04: case 0x05: case 0x06:
1882 WORD BufferSize
= getCX();
1884 ErrorCode
= DosGetCountryInfoEx(getAL(),
1887 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1889 if (ErrorCode
== ERROR_SUCCESS
)
1891 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1896 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1903 /* Country-dependent Character Capitalization -- Character */
1905 /* Country-dependent Filename Capitalization -- Character */
1908 setDL(DosToUpper(getDL()));
1909 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1910 // setAX(ERROR_SUCCESS);
1914 /* Country-dependent Character Capitalization -- Counted ASCII String */
1916 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1919 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1920 // FIXME: Check for NULL ptr!!
1921 DosToUpperStrN(Str
, Str
, getCX());
1922 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1923 // setAX(ERROR_SUCCESS);
1927 /* Country-dependent Character Capitalization -- ASCIIZ String */
1929 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1932 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1933 // FIXME: Check for NULL ptr!!
1934 DosToUpperStrZ(Str
, Str
);
1935 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1936 // setAX(ERROR_SUCCESS);
1940 /* Determine if Character represents YES/NO Response */
1943 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1944 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1948 default: // goto Default;
1950 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1958 /* Set Handle Count */
1961 if (!DosResizeHandleTable(getBX()))
1963 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1964 setAX(Sda
->LastErrorCode
);
1966 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1976 * Function 6Ah is identical to function 68h,
1977 * and sets AH to 68h if success.
1978 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1979 * for more information.
1983 if (DosFlushFileBuffers(getBX()))
1985 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1990 setAX(GetLastError());
1996 /* Extended Open/Create */
2000 WORD CreationStatus
;
2003 /* Check for AL == 00 */
2004 if (getAL() != 0x00)
2006 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2007 setAX(ERROR_INVALID_FUNCTION
);
2012 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2013 * for the full detailed description.
2015 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2018 ErrorCode
= DosCreateFileEx(&FileHandle
,
2020 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2025 if (ErrorCode
== ERROR_SUCCESS
)
2027 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2028 setCX(CreationStatus
);
2033 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2041 default: // Default:
2043 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2046 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2047 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2054 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2056 /* Set CF to terminate the running process */
2057 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2060 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
2063 * This call should leave the flags on the stack for some reason,
2064 * so move the stack by one word.
2065 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2067 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2068 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2069 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2070 setSP(LOWORD(getSP() - 2));
2072 // TODO: NOT IMPLEMENTED;
2075 /* General failure */
2077 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2080 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
2083 * This call should leave the flags on the stack for some reason,
2084 * so move the stack by one word.
2085 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2087 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2088 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2089 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2090 setSP(LOWORD(getSP() - 2));
2092 // TODO: NOT IMPLEMENTED;
2095 /* General failure */
2097 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2100 VOID WINAPI
DosInt27h(LPWORD Stack
)
2102 WORD KeepResident
= (getDX() + 0x0F) >> 4;
2104 /* Terminate and Stay Resident. CS must be the PSP segment. */
2105 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident
);
2106 DosTerminateProcess(Stack
[STACK_CS
], 0, KeepResident
);
2109 VOID WINAPI
DosIdle(LPWORD Stack
)
2112 * This will set the carry flag on the first call (to repeat the BOP),
2113 * and clear it in the next, so that exactly one HLT occurs.
2118 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2121 * This is the DOS 2+ Fast Console Output Interrupt.
2122 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2124 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2125 * for more information.
2128 /* Save AX and BX */
2129 USHORT AX
= getAX();
2130 USHORT BX
= getBX();
2133 * Set the parameters:
2134 * AL contains the character to print (already set),
2135 * BL contains the character attribute,
2136 * BH contains the video page to use.
2138 setBL(DOS_CHAR_ATTRIBUTE
);
2139 setBH(Bda
->VideoPage
);
2141 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2143 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2145 /* Restore AX and BX */
2150 VOID WINAPI
DosInt2Ah(LPWORD Stack
)
2152 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2155 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2159 /* DOS 3+ Internal Utility Functions */
2162 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2166 /* Installation Check */
2173 /* Get DOS Data Segment */
2176 setDS(DOS_DATA_SEGMENT
);
2180 /* Compare FAR Pointers */
2183 PVOID PointerFromFarPointer1
= SEG_OFF_TO_PTR(getDS(), getSI());
2184 PVOID PointerFromFarPointer2
= SEG_OFF_TO_PTR(getES(), getDI());
2185 BOOLEAN AreEqual
= (PointerFromFarPointer1
== PointerFromFarPointer2
);
2192 /* Set DOS Version Number to return */
2195 WORD DosVersion
= getDX();
2197 // Special case: return the true DOS version when DX=00h
2198 if (DosVersion
== 0x0000)
2199 DosData
->DosVersion
= DOS_VERSION
;
2201 DosData
->DosVersion
= DosVersion
;
2210 /* Set Disk Interrupt Handler */
2213 /* Save the old values of PrevInt13 and RomBiosInt13 */
2214 ULONG OldInt13
= BiosData
->PrevInt13
;
2215 ULONG OldBiosInt13
= BiosData
->RomBiosInt13
;
2217 /* Set PrevInt13 and RomBiosInt13 to their new values */
2218 BiosData
->PrevInt13
= MAKELONG(getDX(), getDS());
2219 BiosData
->RomBiosInt13
= MAKELONG(getBX(), getES());
2221 /* Return in DS:DX the old value of PrevInt13 */
2222 setDS(HIWORD(OldInt13
));
2223 setDX(LOWORD(OldInt13
));
2225 /* Return in DS:DX the old value of RomBiosInt13 */
2226 setES(HIWORD(OldBiosInt13
));
2227 setBX(LOWORD(OldBiosInt13
));
2232 /* Mostly Windows 2.x/3.x/9x support */
2236 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2237 * Just do nothing in this case.
2239 if (getAL() != 0x80) goto Default
;
2243 /* Extended Memory Specification */
2247 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2251 /* Installation Check */
2254 /* The driver is loaded */
2259 /* Get Driver Address */
2262 setES(HIWORD(DriverEntry
));
2263 setBX(LOWORD(DriverEntry
));
2268 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2277 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2279 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2284 BOOLEAN
DosKRNLInitialize(VOID
)
2289 BOOLEAN Success
= TRUE
;
2291 CHAR CurrentDirectory
[MAX_PATH
];
2292 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2294 static const BYTE NullDriverRoutine
[] =
2296 /* Strategy routine entry */
2297 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2300 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2301 LOBYTE(DOS_DEVSTAT_DONE
),
2302 HIBYTE(DOS_DEVSTAT_DONE
),
2304 /* Interrupt routine entry */
2308 /* Set the data segment */
2309 setDS(DOS_DATA_SEGMENT
);
2311 /* Initialize the global DOS data area */
2312 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2313 RtlZeroMemory(DosData
, sizeof(*DosData
));
2315 /* Initialize the DOS stack */
2316 setSS(DOS_DATA_SEGMENT
);
2317 setSP(DOS_DATA_OFFSET(DosStack
) + sizeof(DosData
->DosStack
) - sizeof(WORD
));
2319 /* Initialize the list of lists */
2320 SysVars
= &DosData
->SysVars
;
2321 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2322 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2323 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2326 * The last drive can be redefined with the LASTDRIVE command.
2327 * At the moment, set the real maximum possible, 'Z'.
2329 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2331 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2332 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2333 SysVars
->BootDrive
= RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]) - 'A' + 1;
2335 /* Initialize the NUL device driver */
2336 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2337 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2338 // Offset from within the DOS data segment
2339 SysVars
->NullDevice
.StrategyRoutine
= DOS_DATA_OFFSET(NullDriverRoutine
);
2340 // Hardcoded to the RETF inside StrategyRoutine
2341 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2342 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2343 sizeof(SysVars
->NullDevice
.DeviceName
),
2345 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2346 RtlCopyMemory(DosData
->NullDriverRoutine
,
2348 sizeof(NullDriverRoutine
));
2350 /* Default DOS version to report */
2351 DosData
->DosVersion
= DOS_VERSION
;
2353 /* Initialize the swappable data area */
2354 Sda
= &DosData
->Sda
;
2355 RtlZeroMemory(Sda
, sizeof(*Sda
));
2357 /* Get the current directory and convert it to a DOS path */
2358 dwRet
= GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
);
2362 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2364 else if (dwRet
> sizeof(CurrentDirectory
))
2367 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet
);
2372 dwRet
= GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
));
2376 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2378 else if (dwRet
> sizeof(DosDirectory
))
2381 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet
);
2387 /* We failed, use the boot drive instead */
2388 DosDirectory
[0] = SysVars
->BootDrive
+ 'A' - 1;
2389 DosDirectory
[1] = ':';
2390 DosDirectory
[2] = '\\';
2391 DosDirectory
[3] = '\0';
2394 /* Set the current drive */
2395 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2397 /* Get the directory part of the path and set the current directory */
2398 Path
= strchr(DosDirectory
, '\\');
2401 Path
++; // Skip the backslash
2402 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2406 DosData
->CurrentDirectories
[Sda
->CurrentDrive
][0] = '\0';
2409 /* Set the current PSP to the system PSP */
2410 Sda
->CurrentPsp
= SYSTEM_PSP
;
2412 /* Initialize the SFT */
2413 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2414 Sft
->Link
= 0xFFFFFFFF;
2415 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2417 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2419 /* Clear the file descriptor entry */
2420 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2423 /* Initialize memory management */
2424 DosInitializeMemory();
2426 /* Initialize the callback context */
2427 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2429 /* Register the DOS 32-bit Interrupts */
2430 RegisterDosInt32(0x20, DosInt20h
);
2431 RegisterDosInt32(0x21, DosInt21h
);
2432 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2433 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2434 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2435 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2436 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2437 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2438 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2439 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2440 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2442 /* Unimplemented DOS interrupts */
2443 RegisterDosInt32(0x2A, DosInt2Ah
); // DOS Critical Sections / Network
2444 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2445 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2446 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2448 /* Reserved DOS interrupts */
2449 RegisterDosInt32(0x2B, NULL
);
2450 RegisterDosInt32(0x2C, NULL
);
2451 RegisterDosInt32(0x2D, NULL
);
2453 /* Initialize country data */
2454 DosCountryInitialize();
2456 /* Load the CON driver */
2459 /* Load the XMS driver (HIMEM) */
2462 /* Load the EMS driver */
2463 if (!EmsDrvInitialize(EMS_SEGMENT
, EMS_TOTAL_PAGES
))
2465 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n"
2466 "Page frame segment or number of EMS pages invalid.\n");
2469 /* Finally initialize the UMBs */