2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
31 #include "bios/bios.h"
34 #include "hardware/ps2.h"
38 /* PRIVATE VARIABLES **********************************************************/
40 CALLBACK16 DosContext
;
42 /* PUBLIC VARIABLES ***********************************************************/
44 /* Global DOS data area contained in guest memory */
46 /* Easy accessors to useful DOS data area parts */
50 /* PRIVATE FUNCTIONS **********************************************************/
52 static BOOLEAN
DosChangeDrive(BYTE Drive
)
54 CHAR DirectoryPath
[DOS_CMDLINE_LENGTH
+ 1];
56 /* Make sure the drive exists */
57 if (Drive
>= SysVars
->NumLocalDrives
) return FALSE
;
59 RtlZeroMemory(DirectoryPath
, sizeof(DirectoryPath
));
61 /* Find the path to the new current directory */
62 snprintf(DirectoryPath
,
66 DosData
->CurrentDirectories
[Drive
]);
68 /* Change the current directory of the process */
69 if (!SetCurrentDirectoryA(DirectoryPath
)) return FALSE
;
71 /* Set the current drive */
72 Sda
->CurrentDrive
= Drive
;
78 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
83 CHAR CurrentDirectory
[MAX_PATH
];
84 CHAR DosDirectory
[DOS_DIR_LENGTH
];
86 /* Make sure the directory path is not too long */
87 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
89 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
93 /* Check whether the directory string is of format "X:..." */
94 if (strlen(Directory
) >= 2 && Directory
[1] == ':')
96 /* Get the drive number */
97 DriveNumber
= RtlUpperChar(Directory
[0]) - 'A';
99 /* Make sure the drive exists */
100 if (DriveNumber
>= SysVars
->NumLocalDrives
)
102 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
108 /* Keep the current drive number */
109 DriveNumber
= Sda
->CurrentDrive
;
112 /* Get the file attributes */
113 Attributes
= GetFileAttributesA(Directory
);
115 /* Make sure the path exists and is a directory */
116 if ((Attributes
== INVALID_FILE_ATTRIBUTES
) ||
117 !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
119 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
123 /* Check if this is the current drive */
124 if (DriveNumber
== Sda
->CurrentDrive
)
126 /* Change the directory */
127 if (!SetCurrentDirectoryA(Directory
))
129 Sda
->LastErrorCode
= LOWORD(GetLastError());
134 /* Get the (possibly new) current directory (needed if we specified a relative directory) */
135 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
))
137 // TODO: Use some kind of default path?
141 /* Convert it to a DOS path */
142 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
)))
144 // TODO: Use some kind of default path?
148 /* Get the directory part of the path and set the current directory for the drive */
149 Path
= strchr(DosDirectory
, '\\');
152 Path
++; // Skip the backslash
153 strncpy(DosData
->CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
157 DosData
->CurrentDirectories
[DriveNumber
][0] = '\0';
164 /* PUBLIC FUNCTIONS ***********************************************************/
166 BOOLEAN
DosControlBreak(VOID
)
170 /* Print an extra newline */
171 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\r');
172 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\n');
174 /* Call interrupt 0x23 */
175 Int32Call(&DosContext
, 0x23);
179 DosTerminateProcess(Sda
->CurrentPsp
, 0, 0);
186 VOID WINAPI
DosInt20h(LPWORD Stack
)
189 * This is the exit interrupt (alias to INT 21h, AH=00h).
190 * CS must be the PSP segment.
192 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
195 VOID WINAPI
DosInt21h(LPWORD Stack
)
198 SYSTEMTIME SystemTime
;
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 /* CS must be the PSP segment */
214 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
218 /* Read Character from STDIN with Echo */
221 DPRINT("INT 21h, AH = 01h\n");
223 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, TRUE
);
224 if (Character
== 0x03 && DosControlBreak()) break;
230 /* Write Character to STDOUT */
233 // FIXME: Under DOS 2+, output handle may be redirected!!!!
235 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
238 * We return the output character (DOS 2.1+).
239 * Also, if we're going to output a TAB, then
240 * don't return a TAB but a SPACE instead.
241 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
242 * for more information.
244 setAL(Character
== '\t' ? ' ' : Character
);
248 /* Read Character from STDAUX */
251 // FIXME: Really read it from STDAUX!
252 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
253 // setAL(DosReadCharacter());
257 /* Write Character to STDAUX */
260 // FIXME: Really write it to STDAUX!
261 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
262 // DosPrintCharacter(getDL());
266 /* Write Character to Printer */
269 // FIXME: Really write it to printer!
270 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
271 DPRINT1("0x%p\n", getDL());
272 DPRINT1("\n\n-----------\n\n");
276 /* Direct Console I/O */
281 // FIXME: Under DOS 2+, output handle may be redirected!!!!
283 if (Character
!= 0xFF)
286 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
289 * We return the output character (DOS 2.1+).
290 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
291 * for more information.
300 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
301 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
305 /* No character available */
306 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
314 /* Direct Character Input without Echo */
317 DPRINT("Direct char input without echo\n");
318 setAL(DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
));
322 /* Character Input without Echo */
325 DPRINT("Char input without echo\n");
327 Character
= DosReadCharacter(DOS_INPUT_HANDLE
, FALSE
);
328 if (Character
== 0x03 && DosControlBreak()) break;
334 /* Write String to STDOUT */
337 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
339 while (*String
!= '$')
341 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
346 * We return the terminating character (DOS 2.1+).
347 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
348 * for more information.
350 setAL('$'); // *String
354 /* Read Buffered Input */
357 PDOS_INPUT_BUFFER InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
359 DPRINT("Read Buffered Input\n");
360 if (InputBuffer
->MaxLength
== 0) break;
362 /* Read from standard input */
363 InputBuffer
->Length
= DosReadLineBuffered(
365 MAKELONG(getDX() + FIELD_OFFSET(DOS_INPUT_BUFFER
, Buffer
), getDS()),
366 InputBuffer
->MaxLength
372 /* Get STDIN Status */
375 setAL(DosCheckInput() ? 0xFF : 0x00);
379 /* Flush Buffer and Read STDIN */
382 BYTE InputFunction
= getAL();
384 /* Flush STDIN buffer */
385 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
388 * If the input function number contained in AL is valid, i.e.
389 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
390 * recursively with AL == AH.
392 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
393 InputFunction
== 0x07 || InputFunction
== 0x08 ||
394 InputFunction
== 0x0A)
396 /* Call ourselves recursively */
397 setAH(InputFunction
);
406 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
408 // TODO: Flush what's needed.
409 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
411 /* Clear CF in DOS 6 only */
412 if (PspBlock
->DosVersion
== 0x0006)
413 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
418 /* Set Default Drive */
421 DosChangeDrive(getDL());
422 setAL(SysVars
->NumLocalDrives
);
426 /* NULL Function for CP/M Compatibility */
430 * This function corresponds to the CP/M BDOS function
431 * "get bit map of logged drives", which is meaningless
434 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
435 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
436 * for more information.
442 /* Get Default Drive */
445 setAL(Sda
->CurrentDrive
);
449 /* Set Disk Transfer Area */
452 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
456 /* NULL Function for CP/M Compatibility */
461 * Function 0x1D corresponds to the CP/M BDOS function
462 * "get bit map of read-only drives", which is meaningless
464 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
465 * for more information.
467 * Function 0x1E corresponds to the CP/M BDOS function
468 * "set file attributes", which was meaningless under MS-DOS 1.x.
469 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
470 * for more information.
476 /* NULL Function for CP/M Compatibility */
480 * This function corresponds to the CP/M BDOS function
481 * "get/set default user (sublibrary) number", which is meaningless
484 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
485 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
486 * for more information.
492 /* Set Interrupt Vector */
495 ULONG FarPointer
= MAKELONG(getDX(), getDS());
496 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
497 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
499 /* Write the new far pointer to the IDT */
500 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
507 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */
508 DosClonePsp(getDX(), Stack
[STACK_CS
]);
512 /* Parse Filename into FCB */
515 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
516 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
517 BYTE Options
= getAL();
521 if (FileName
[1] == ':')
523 /* Set the drive number */
524 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
526 /* Skip to the file name part */
531 /* No drive number specified */
532 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
533 else Fcb
->DriveNumber
= 0;
536 /* Parse the file name */
538 while ((*FileName
> 0x20) && (i
< 8))
540 if (*FileName
== '.') break;
541 else if (*FileName
== '*')
547 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
550 /* Fill the whole field with blanks only if bit 2 is not set */
551 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
553 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
556 /* Skip to the extension part */
557 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
558 if (*FileName
== '.') FileName
++;
560 /* Now parse the extension */
564 while ((*FileName
> 0x20) && (i
< 3))
566 if (*FileName
== '*')
572 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
575 /* Fill the whole field with blanks only if bit 3 is not set */
576 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
578 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
584 /* Get System Date */
587 GetLocalTime(&SystemTime
);
588 setCX(SystemTime
.wYear
);
589 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
590 setAL(SystemTime
.wDayOfWeek
);
594 /* Set System Date */
597 GetLocalTime(&SystemTime
);
598 SystemTime
.wYear
= getCX();
599 SystemTime
.wMonth
= getDH();
600 SystemTime
.wDay
= getDL();
602 /* Return success or failure */
603 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
607 /* Get System Time */
610 GetLocalTime(&SystemTime
);
611 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
612 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
616 /* Set System Time */
619 GetLocalTime(&SystemTime
);
620 SystemTime
.wHour
= getCH();
621 SystemTime
.wMinute
= getCL();
622 SystemTime
.wSecond
= getDH();
623 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
625 /* Return success or failure */
626 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
630 /* Get Disk Transfer Area */
633 setES(HIWORD(Sda
->DiskTransferArea
));
634 setBX(LOWORD(Sda
->DiskTransferArea
));
638 /* Get DOS Version */
641 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
644 * DOS 2+ - GET DOS VERSION
645 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
646 * for more information.
649 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
652 * Return DOS OEM number:
653 * 0x00 for IBM PC-DOS
654 * 0x02 for packaged MS-DOS
660 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
663 * Return version flag:
664 * 1 << 3 if DOS is in ROM,
665 * 0 (reserved) if not.
670 /* Return DOS 24-bit user serial number in BL:CX */
675 * Return DOS version: Minor:Major in AH:AL
676 * The Windows NT DOS box returns version 5.00, subject to SETVER.
678 setAX(PspBlock
->DosVersion
);
683 /* Terminate and Stay Resident */
686 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
687 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
691 /* Extended functionalities */
697 * DOS 4+ - GET BOOT DRIVE
701 setDL(SysVars
->BootDrive
);
706 * DOS 5+ - GET TRUE VERSION NUMBER
707 * This function always returns the true version number, unlike
708 * AH=30h, whose return value may be changed with SETVER.
709 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
710 * for more information.
715 * Return the true DOS version: Minor:Major in BH:BL
716 * The Windows NT DOS box returns BX=3205h (version 5.50).
718 setBX(NTDOS_VERSION
);
729 default: // goto Default;
731 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
739 /* Get Address of InDOS flag */
742 setES(DOS_DATA_SEGMENT
);
743 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
747 /* Get Interrupt Vector */
750 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
752 /* Read the address from the IDT into ES:BX */
753 setES(HIWORD(FarPointer
));
754 setBX(LOWORD(FarPointer
));
758 /* Get Free Disk Space */
761 CHAR RootPath
[] = "?:\\";
762 DWORD SectorsPerCluster
;
763 DWORD BytesPerSector
;
764 DWORD NumberOfFreeClusters
;
765 DWORD TotalNumberOfClusters
;
768 RootPath
[0] = 'A' + Sda
->CurrentDrive
;
770 RootPath
[0] = 'A' + getDL() - 1;
772 if (GetDiskFreeSpaceA(RootPath
,
775 &NumberOfFreeClusters
,
776 &TotalNumberOfClusters
))
778 setAX(LOWORD(SectorsPerCluster
));
779 setCX(LOWORD(BytesPerSector
));
780 setBX(min(NumberOfFreeClusters
, 0xFFFF));
781 setDX(min(TotalNumberOfClusters
, 0xFFFF));
792 /* SWITCH character - AVAILDEV */
798 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
799 * This setting is ignored by MS-DOS 4.0+.
800 * MS-DOS 5+ always return AL=00h/DL=2Fh.
801 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
802 * for more information.
810 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
811 * This setting is ignored by MS-DOS 5+.
812 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
813 * for more information.
821 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
822 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
823 * for more information.
831 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
832 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
833 * for more information.
840 /* Invalid subfunction */
849 /* Get/Set Country-dependent Information */
852 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
855 ErrorCode
= DosGetCountryInfo(&CountryId
,
856 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
858 if (ErrorCode
== ERROR_SUCCESS
)
860 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
865 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
872 /* Create Directory */
875 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
877 if (CreateDirectoryA(String
, NULL
))
879 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
883 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
884 setAX(LOWORD(GetLastError()));
890 /* Remove Directory */
893 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
895 if (RemoveDirectoryA(String
))
897 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
901 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
902 setAX(LOWORD(GetLastError()));
908 /* Set Current Directory */
911 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
913 if (DosChangeDirectory(String
))
915 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
919 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
920 setAX(Sda
->LastErrorCode
);
926 /* Create or Truncate File */
930 WORD ErrorCode
= DosCreateFile(&FileHandle
,
931 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
935 if (ErrorCode
== ERROR_SUCCESS
)
937 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
942 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
949 /* Open File or Device */
953 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
954 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
956 if (ErrorCode
== ERROR_SUCCESS
)
958 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
963 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
970 /* Close File or Device */
973 if (DosCloseHandle(getBX()))
975 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
979 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
980 setAX(ERROR_INVALID_HANDLE
);
986 /* Read from File or Device */
992 DPRINT("DosReadFile(0x%04X)\n", getBX());
994 ErrorCode
= DosReadFile(getBX(),
995 MAKELONG(getDX(), getDS()),
999 if (ErrorCode
== ERROR_SUCCESS
)
1001 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1004 else if (ErrorCode
!= ERROR_NOT_READY
)
1006 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1013 /* Write to File or Device */
1016 WORD BytesWritten
= 0;
1017 WORD ErrorCode
= DosWriteFile(getBX(),
1018 MAKELONG(getDX(), getDS()),
1022 if (ErrorCode
== ERROR_SUCCESS
)
1024 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1025 setAX(BytesWritten
);
1029 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1039 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1041 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1043 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1045 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1046 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1048 setAL(RtlUpperChar(FileName
[0]) - 'A');
1052 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1053 setAX(GetLastError());
1063 WORD ErrorCode
= DosSeekFile(getBX(),
1064 MAKELONG(getDX(), getCX()),
1068 if (ErrorCode
== ERROR_SUCCESS
)
1070 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1072 /* Return the new offset in DX:AX */
1073 setDX(HIWORD(NewLocation
));
1074 setAX(LOWORD(NewLocation
));
1078 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1085 /* Get/Set File Attributes */
1089 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1091 if (getAL() == 0x00)
1093 /* Get the attributes */
1094 Attributes
= GetFileAttributesA(FileName
);
1096 /* Check if it failed */
1097 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1099 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1100 setAX(GetLastError());
1104 /* Return the attributes that DOS can understand */
1105 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1106 setCX(Attributes
& 0x00FF);
1109 else if (getAL() == 0x01)
1111 /* Try to set the attributes */
1112 if (SetFileAttributesA(FileName
, getCL()))
1114 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1118 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1119 setAX(GetLastError());
1124 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1125 setAX(ERROR_INVALID_FUNCTION
);
1134 WORD Length
= getCX();
1136 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1138 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1143 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1144 setAX(Sda
->LastErrorCode
);
1150 /* Duplicate Handle */
1153 WORD NewHandle
= DosDuplicateHandle(getBX());
1155 if (NewHandle
!= INVALID_DOS_HANDLE
)
1158 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1162 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1163 setAX(Sda
->LastErrorCode
);
1169 /* Force Duplicate Handle */
1172 if (DosForceDuplicateHandle(getBX(), getCX()))
1174 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1178 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1179 setAX(ERROR_INVALID_HANDLE
);
1185 /* Get Current Directory */
1188 BYTE DriveNumber
= getDL();
1189 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1191 /* Get the real drive number */
1192 if (DriveNumber
== 0)
1194 DriveNumber
= Sda
->CurrentDrive
;
1198 /* Decrement DriveNumber since it was 1-based */
1202 if (DriveNumber
< SysVars
->NumLocalDrives
)
1205 * Copy the current directory into the target buffer.
1206 * It doesn't contain the drive letter and the backslash.
1208 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1209 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1210 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1214 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1215 setAX(ERROR_INVALID_DRIVE
);
1221 /* Allocate Memory */
1224 WORD MaxAvailable
= 0;
1225 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1229 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1234 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1235 setAX(Sda
->LastErrorCode
);
1236 setBX(MaxAvailable
);
1245 if (DosFreeMemory(getES()))
1247 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1251 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1252 setAX(Sda
->LastErrorCode
);
1258 /* Resize Memory Block */
1263 if (DosResizeMemory(getES(), getBX(), &Size
))
1265 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1269 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1270 setAX(Sda
->LastErrorCode
);
1280 BYTE OrgAL
= getAL();
1281 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1282 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1285 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1287 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1289 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1291 /* Create a new process */
1292 ErrorCode
= DosCreateProcess(ProgramName
,
1294 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1298 /* Just load an executable */
1299 ErrorCode
= DosLoadExecutable(LoadType
,
1304 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1307 else if (OrgAL
== 0x05)
1309 // http://www.ctyme.com/intr/rb-2942.htm
1310 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1311 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1315 ErrorCode
= ERROR_INVALID_FUNCTION
;
1318 if (ErrorCode
== ERROR_SUCCESS
)
1320 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1324 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1331 /* Terminate with Return Code */
1334 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1338 /* Get Return Code (ERRORLEVEL) */
1342 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1343 * DosErrorLevel is cleared after being read by this function.
1345 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1346 setAX(Sda
->ErrorLevel
);
1347 Sda
->ErrorLevel
= 0x0000; // Clear it
1351 /* Find First File */
1354 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1355 SEG_OFF_TO_PTR(getDS(), getDX()),
1360 if (Result
== ERROR_SUCCESS
)
1361 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1363 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1368 /* Find Next File */
1371 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1375 if (Result
== ERROR_SUCCESS
)
1376 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1378 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1383 /* Internal - Set Current Process ID (Set PSP Address) */
1386 DosSetProcessContext(getBX());
1390 /* Internal - Get Current Process ID (Get PSP Address) */
1392 /* Get Current PSP Address */
1396 * Undocumented AH=51h is identical to the documented AH=62h.
1397 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1398 * and http://www.ctyme.com/intr/rb-3140.htm
1399 * for more information.
1401 setBX(Sda
->CurrentPsp
);
1405 /* Internal - Get "List of lists" (SYSVARS) */
1409 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1410 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1411 * for more information.
1414 /* Return the DOS "list of lists" in ES:BX */
1415 setES(DOS_DATA_SEGMENT
);
1416 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1420 /* Create Child PSP */
1423 DosCreatePsp(getDX(), getSI());
1424 DosSetProcessContext(getDX());
1431 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1432 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1435 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1436 * for more information.
1439 if (MoveFileA(ExistingFileName
, NewFileName
))
1441 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1445 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1446 setAX(GetLastError());
1452 /* File Attributes */
1457 /* Get File's last-written Date and Time */
1460 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1461 FILETIME LastWriteTime
;
1462 WORD FileDate
, FileTime
;
1464 if (Descriptor
== NULL
)
1466 /* Invalid handle */
1467 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1468 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1469 setAX(ERROR_INVALID_HANDLE
);
1473 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1475 /* Invalid for devices */
1476 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1477 // setAX(ERROR_INVALID_FUNCTION);
1478 setAX(ERROR_INVALID_HANDLE
);
1483 * Retrieve the last-written Win32 date and time,
1484 * and convert it to DOS format.
1486 if (!GetFileTime(Descriptor
->Win32Handle
,
1487 NULL
, NULL
, &LastWriteTime
) ||
1488 !FileTimeToDosDateTime(&LastWriteTime
,
1489 &FileDate
, &FileTime
))
1491 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1492 setAX(GetLastError());
1496 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1502 /* Set File's last-written Date and Time */
1505 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1506 FILETIME LastWriteTime
;
1507 WORD FileDate
= getDX();
1508 WORD FileTime
= getCX();
1510 if (Descriptor
== NULL
)
1512 /* Invalid handle */
1513 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1514 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1515 setAX(ERROR_INVALID_HANDLE
);
1519 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1521 /* Invalid for devices */
1522 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1523 // setAX(ERROR_INVALID_FUNCTION);
1524 setAX(ERROR_INVALID_HANDLE
);
1529 * Convert the new last-written DOS date and time
1530 * to Win32 format and set it.
1532 if (!DosDateTimeToFileTime(FileDate
, FileTime
,
1534 !SetFileTime(Descriptor
->Win32Handle
,
1535 NULL
, NULL
, &LastWriteTime
))
1537 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1538 setAX(GetLastError());
1542 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1546 default: // goto Default;
1548 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1556 /* Get/Set Memory Management Options */
1561 /* Get allocation strategy */
1564 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1565 setAX(Sda
->AllocStrategy
);
1569 /* Set allocation strategy */
1572 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1573 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1575 /* Can't set both */
1576 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1577 setAX(ERROR_INVALID_PARAMETER
);
1581 if ((getBL() & ~(DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1582 > DOS_ALLOC_LAST_FIT
)
1584 /* Invalid allocation strategy */
1585 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1586 setAX(ERROR_INVALID_PARAMETER
);
1590 Sda
->AllocStrategy
= getBL();
1591 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1595 /* Get UMB link state */
1598 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1599 setAL(SysVars
->UmbLinked
? 0x01 : 0x00);
1603 /* Set UMB link state */
1609 Success
= DosLinkUmb();
1611 Success
= DosUnlinkUmb();
1614 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1616 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1621 /* Invalid or unsupported function */
1624 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1625 setAX(ERROR_INVALID_FUNCTION
);
1632 /* Get Extended Error Information */
1635 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1640 /* Create Temporary File */
1643 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1644 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1650 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1651 * for more information.
1654 // FIXME: Check for buffer validity?
1655 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1656 // to receive the generated filename.
1658 /* First create the temporary file */
1659 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1662 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1663 setAX(GetLastError());
1667 /* Now try to open it in read/write access */
1668 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1669 if (ErrorCode
== ERROR_SUCCESS
)
1671 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1676 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1683 /* Create New File */
1687 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1688 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1692 if (ErrorCode
== ERROR_SUCCESS
)
1694 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1699 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1706 /* Lock/Unlock Region of File */
1709 if (getAL() == 0x00)
1711 /* Lock region of file */
1712 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1714 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1718 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1719 setAX(Sda
->LastErrorCode
);
1722 else if (getAL() == 0x01)
1724 /* Unlock region of file */
1725 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1727 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1731 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1732 setAX(Sda
->LastErrorCode
);
1737 /* Invalid subfunction */
1738 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1739 setAX(ERROR_INVALID_FUNCTION
);
1745 /* Canonicalize File Name or Path */
1749 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1750 * for more information.
1754 * We suppose that the DOS app gave to us a valid
1755 * 128-byte long buffer for the canonicalized name.
1757 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1759 SEG_OFF_TO_PTR(getES(), getDI()),
1763 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1764 setAX(GetLastError());
1768 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1772 // FIXME: Convert the full path name into short version.
1773 // We cannot reliably use GetShortPathName, because it fails
1774 // if the path name given doesn't exist. However this DOS
1775 // function AH=60h should be able to work even for non-existing
1776 // path and file names.
1781 /* Miscellaneous Internal Functions */
1786 /* Get Swappable Data Area */
1789 setDS(DOS_DATA_SEGMENT
);
1790 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1791 setCX(sizeof(DOS_SDA
));
1792 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1794 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1798 default: // goto Default;
1800 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1808 /* Extended Country Information */
1813 case 0x01: case 0x02: case 0x03:
1814 case 0x04: case 0x05: case 0x06:
1817 WORD BufferSize
= getCX();
1819 ErrorCode
= DosGetCountryInfoEx(getAL(),
1822 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1824 if (ErrorCode
== ERROR_SUCCESS
)
1826 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1831 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1838 /* Country-dependent Character Capitalization -- Character */
1840 /* Country-dependent Filename Capitalization -- Character */
1843 setDL(DosToUpper(getDL()));
1844 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1845 // setAX(ERROR_SUCCESS);
1849 /* Country-dependent Character Capitalization -- Counted ASCII String */
1851 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1854 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1855 // FIXME: Check for NULL ptr!!
1856 DosToUpperStrN(Str
, Str
, getCX());
1857 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1858 // setAX(ERROR_SUCCESS);
1862 /* Country-dependent Character Capitalization -- ASCIIZ String */
1864 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1867 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1868 // FIXME: Check for NULL ptr!!
1869 DosToUpperStrZ(Str
, Str
);
1870 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1871 // setAX(ERROR_SUCCESS);
1875 /* Determine if Character represents YES/NO Response */
1878 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1879 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1883 default: // goto Default;
1885 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1893 /* Set Handle Count */
1896 if (!DosResizeHandleTable(getBX()))
1898 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1899 setAX(Sda
->LastErrorCode
);
1901 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1911 * Function 6Ah is identical to function 68h,
1912 * and sets AH to 68h if success.
1913 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1914 * for more information.
1918 if (DosFlushFileBuffers(getBX()))
1920 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1924 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1925 setAX(GetLastError());
1931 /* Extended Open/Create */
1935 WORD CreationStatus
;
1938 /* Check for AL == 00 */
1939 if (getAL() != 0x00)
1941 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1942 setAX(ERROR_INVALID_FUNCTION
);
1947 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1948 * for the full detailed description.
1950 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1953 ErrorCode
= DosCreateFileEx(&FileHandle
,
1955 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
1960 if (ErrorCode
== ERROR_SUCCESS
)
1962 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1963 setCX(CreationStatus
);
1968 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1976 default: // Default:
1978 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
1981 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1982 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1989 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
1991 /* Set CF to terminate the running process */
1992 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1995 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
1998 * This call should leave the flags on the stack for some reason,
1999 * so move the stack by one word.
2000 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2002 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2003 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2004 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2005 setSP(LOWORD(getSP() - 2));
2007 // TODO: NOT IMPLEMENTED;
2010 /* General failure */
2012 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2015 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
2018 * This call should leave the flags on the stack for some reason,
2019 * so move the stack by one word.
2020 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2022 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2023 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2024 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2025 setSP(LOWORD(getSP() - 2));
2027 // TODO: NOT IMPLEMENTED;
2030 /* General failure */
2032 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2035 VOID WINAPI
DosInt27h(LPWORD Stack
)
2037 WORD KeepResident
= (getDX() + 0x0F) >> 4;
2039 /* Terminate and Stay Resident. CS must be the PSP segment. */
2040 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident
);
2041 DosTerminateProcess(Stack
[STACK_CS
], 0, KeepResident
);
2044 VOID WINAPI
DosIdle(LPWORD Stack
)
2047 * This will set the carry flag on the first call (to repeat the BOP),
2048 * and clear it in the next, so that exactly one HLT occurs.
2053 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2056 * This is the DOS 2+ Fast Console Output Interrupt.
2057 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2059 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2060 * for more information.
2063 /* Save AX and BX */
2064 USHORT AX
= getAX();
2065 USHORT BX
= getBX();
2068 * Set the parameters:
2069 * AL contains the character to print (already set),
2070 * BL contains the character attribute,
2071 * BH contains the video page to use.
2073 setBL(DOS_CHAR_ATTRIBUTE
);
2074 setBH(Bda
->VideoPage
);
2076 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2078 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2080 /* Restore AX and BX */
2085 VOID WINAPI
DosInt2Ah(LPWORD Stack
)
2087 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2090 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2094 /* DOS 3+ Internal Utility Functions */
2097 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2101 /* Installation Check */
2108 /* Get DOS Data Segment */
2111 setDS(DOS_DATA_SEGMENT
);
2115 /* Compare FAR Pointers */
2118 PVOID PointerFromFarPointer1
= SEG_OFF_TO_PTR(getDS(), getSI());
2119 PVOID PointerFromFarPointer2
= SEG_OFF_TO_PTR(getES(), getDI());
2120 BOOLEAN AreEqual
= (PointerFromFarPointer1
== PointerFromFarPointer2
);
2124 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
2125 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2129 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
2130 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2135 /* Set DOS Version Number to return */
2138 WORD DosVersion
= getDX();
2140 // Special case: return the true DOS version when DX=00h
2141 if (DosVersion
== 0x0000)
2142 DosData
->DosVersion
= DOS_VERSION
;
2144 DosData
->DosVersion
= DosVersion
;
2153 /* Set Disk Interrupt Handler */
2156 /* Save the old values of PrevInt13 and RomBiosInt13 */
2157 ULONG OldInt13
= BiosData
->PrevInt13
;
2158 ULONG OldBiosInt13
= BiosData
->RomBiosInt13
;
2160 /* Set PrevInt13 and RomBiosInt13 to their new values */
2161 BiosData
->PrevInt13
= MAKELONG(getDX(), getDS());
2162 BiosData
->RomBiosInt13
= MAKELONG(getBX(), getES());
2164 /* Return in DS:DX the old value of PrevInt13 */
2165 setDS(HIWORD(OldInt13
));
2166 setDX(LOWORD(OldInt13
));
2168 /* Return in DS:DX the old value of RomBiosInt13 */
2169 setES(HIWORD(OldBiosInt13
));
2170 setBX(LOWORD(OldBiosInt13
));
2175 /* Mostly Windows 2.x/3.x/9x support */
2179 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2180 * Just do nothing in this case.
2182 if (getAL() != 0x80) goto Default
;
2186 /* Extended Memory Specification */
2190 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2194 /* Installation Check */
2197 /* The driver is loaded */
2202 /* Get Driver Address */
2205 setES(HIWORD(DriverEntry
));
2206 setBX(LOWORD(DriverEntry
));
2211 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2220 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2222 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2227 BOOLEAN
DosKRNLInitialize(VOID
)
2232 BOOLEAN Success
= TRUE
;
2234 CHAR CurrentDirectory
[MAX_PATH
];
2235 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2237 static const BYTE NullDriverRoutine
[] =
2239 /* Strategy routine entry */
2240 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2243 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2244 LOBYTE(DOS_DEVSTAT_DONE
),
2245 HIBYTE(DOS_DEVSTAT_DONE
),
2247 /* Interrupt routine entry */
2251 /* Set the data segment */
2252 setDS(DOS_DATA_SEGMENT
);
2254 /* Initialize the global DOS data area */
2255 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2256 RtlZeroMemory(DosData
, sizeof(*DosData
));
2258 /* Initialize the DOS stack */
2259 setSS(DOS_DATA_SEGMENT
);
2260 setSP(DOS_DATA_OFFSET(DosStack
) + sizeof(DosData
->DosStack
) - sizeof(WORD
));
2262 /* Initialize the list of lists */
2263 SysVars
= &DosData
->SysVars
;
2264 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2265 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2266 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2269 * The last drive can be redefined with the LASTDRIVE command.
2270 * At the moment, set the real maximum possible, 'Z'.
2272 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2274 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2275 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2276 SysVars
->BootDrive
= RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]) - 'A' + 1;
2278 /* Initialize the NUL device driver */
2279 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2280 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2281 // Offset from within the DOS data segment
2282 SysVars
->NullDevice
.StrategyRoutine
= DOS_DATA_OFFSET(NullDriverRoutine
);
2283 // Hardcoded to the RETF inside StrategyRoutine
2284 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2285 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2286 sizeof(SysVars
->NullDevice
.DeviceName
),
2288 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2289 RtlCopyMemory(DosData
->NullDriverRoutine
,
2291 sizeof(NullDriverRoutine
));
2293 /* Default DOS version to report */
2294 DosData
->DosVersion
= DOS_VERSION
;
2296 /* Initialize the swappable data area */
2297 Sda
= &DosData
->Sda
;
2298 RtlZeroMemory(Sda
, sizeof(*Sda
));
2300 /* Get the current directory and convert it to a DOS path */
2301 dwRet
= GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
);
2305 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2307 else if (dwRet
> sizeof(CurrentDirectory
))
2310 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet
);
2315 dwRet
= GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
));
2319 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2321 else if (dwRet
> sizeof(DosDirectory
))
2324 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet
);
2330 /* We failed, use the boot drive instead */
2331 DosDirectory
[0] = SysVars
->BootDrive
+ 'A' - 1;
2332 DosDirectory
[1] = ':';
2333 DosDirectory
[2] = '\\';
2334 DosDirectory
[3] = '\0';
2337 /* Set the current drive */
2338 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2340 /* Get the directory part of the path and set the current directory */
2341 Path
= strchr(DosDirectory
, '\\');
2344 Path
++; // Skip the backslash
2345 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2349 DosData
->CurrentDirectories
[Sda
->CurrentDrive
][0] = '\0';
2352 /* Set the current PSP to the system PSP */
2353 Sda
->CurrentPsp
= SYSTEM_PSP
;
2355 /* Initialize the SFT */
2356 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2357 Sft
->Link
= 0xFFFFFFFF;
2358 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2360 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2362 /* Clear the file descriptor entry */
2363 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2366 /* Initialize memory management */
2367 DosInitializeMemory();
2369 /* Initialize the callback context */
2370 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2372 /* Register the DOS 32-bit Interrupts */
2373 RegisterDosInt32(0x20, DosInt20h
);
2374 RegisterDosInt32(0x21, DosInt21h
);
2375 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2376 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2377 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2378 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2379 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2380 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2381 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2382 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2383 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2385 /* Unimplemented DOS interrupts */
2386 RegisterDosInt32(0x2A, DosInt2Ah
); // DOS Critical Sections / Network
2387 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2388 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2389 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2391 /* Reserved DOS interrupts */
2392 RegisterDosInt32(0x2B, NULL
);
2393 RegisterDosInt32(0x2C, NULL
);
2394 RegisterDosInt32(0x2D, NULL
);
2396 /* Initialize country data */
2397 DosCountryInitialize();
2399 /* Load the CON driver */
2402 /* Load the XMS driver (HIMEM) */
2405 /* Load the EMS driver */
2406 if (!EmsDrvInitialize(EMS_SEGMENT
, EMS_TOTAL_PAGES
))
2408 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n"
2409 "Page frame segment or number of EMS pages invalid.\n");
2412 /* Finally initialize the UMBs */