2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
29 #include "bios/bios.h"
32 #include "hardware/ps2.h"
36 /* PRIVATE VARIABLES **********************************************************/
38 CALLBACK16 DosContext
;
40 /* PUBLIC VARIABLES ***********************************************************/
42 /* Global DOS data area contained in guest memory */
44 /* Easy accessors to useful DOS data area parts */
48 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
49 BOOLEAN DoEcho
= FALSE
;
51 /* PRIVATE FUNCTIONS **********************************************************/
53 static BOOLEAN
DosChangeDrive(BYTE Drive
)
55 CHAR DirectoryPath
[DOS_CMDLINE_LENGTH
+ 1];
57 /* Make sure the drive exists */
58 if (Drive
>= SysVars
->NumLocalDrives
) return FALSE
;
60 RtlZeroMemory(DirectoryPath
, sizeof(DirectoryPath
));
62 /* Find the path to the new current directory */
63 snprintf(DirectoryPath
,
67 DosData
->CurrentDirectories
[Drive
]);
69 /* Change the current directory of the process */
70 if (!SetCurrentDirectoryA(DirectoryPath
)) return FALSE
;
72 /* Set the current drive */
73 Sda
->CurrentDrive
= Drive
;
79 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
84 CHAR CurrentDirectory
[MAX_PATH
];
85 CHAR DosDirectory
[DOS_DIR_LENGTH
];
87 /* Make sure the directory path is not too long */
88 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
90 Sda
->LastErrorCode
= ERROR_PATH_NOT_FOUND
;
94 /* Check whether the directory string is of format "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
)
185 /* This is the exit interrupt */
186 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
189 VOID WINAPI
DosInt21h(LPWORD Stack
)
192 SYSTEMTIME SystemTime
;
194 PDOS_INPUT_BUFFER InputBuffer
;
198 /* Save the value of SS:SP on entry in the PSP */
199 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
=
200 MAKELONG(getSP() + (STACK_FLAGS
+ 1) * 2, getSS());
202 /* Check the value in the AH register */
205 /* Terminate Program */
208 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
212 /* Read Character from STDIN with Echo */
215 DPRINT("INT 21h, AH = 01h\n");
217 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
219 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
222 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
223 // Check also Ctrl-P and set echo-to-printer flag.
224 // Ctrl-Z is not interpreted.
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
));
305 /* No character available */
306 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
314 /* Character Input without Echo */
318 DPRINT("Char input without echo\n");
320 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
322 // FIXME: For 0x07, do not check Ctrl-C/Break.
323 // For 0x08, do check those control sequences and if needed,
330 /* Write String to STDOUT */
333 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
335 while (*String
!= '$')
337 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
342 * We return the terminating character (DOS 2.1+).
343 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
344 * for more information.
346 setAL('$'); // *String
350 /* Read Buffered Input */
354 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
356 DPRINT("Read Buffered Input\n");
358 while (Count
< InputBuffer
->MaxLength
)
360 /* Try to read a character (wait) */
361 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
365 /* Extended character */
368 /* Read the scancode */
369 DosReadCharacter(DOS_INPUT_HANDLE
);
376 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
377 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
379 if (DosControlBreak())
381 /* Set the character to a newline to exit the loop */
395 /* Erase the character */
396 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
397 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
398 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
406 /* Append it to the buffer */
407 InputBuffer
->Buffer
[Count
] = Character
;
409 /* Check if this is a special character */
410 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
412 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
413 Character
+= 'A' - 1;
416 /* Echo the character */
417 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
421 if (Character
== '\r') break;
422 if (Character
== '\b') continue;
423 Count
++; /* Carriage returns are NOT counted */
426 /* Update the length */
427 InputBuffer
->Length
= Count
;
432 /* Get STDIN Status */
435 setAL(DosCheckInput() ? 0xFF : 0x00);
439 /* Flush Buffer and Read STDIN */
442 BYTE InputFunction
= getAL();
444 /* Flush STDIN buffer */
445 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
448 * If the input function number contained in AL is valid, i.e.
449 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
450 * recursively with AL == AH.
452 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
453 InputFunction
== 0x07 || InputFunction
== 0x08 ||
454 InputFunction
== 0x0A)
456 /* Call ourselves recursively */
457 setAH(InputFunction
);
466 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
468 // TODO: Flush what's needed.
469 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
471 /* Clear CF in DOS 6 only */
472 if (PspBlock
->DosVersion
== 0x0006)
473 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
478 /* Set Default Drive */
481 DosChangeDrive(getDL());
482 setAL(SysVars
->NumLocalDrives
);
486 /* NULL Function for CP/M Compatibility */
490 * This function corresponds to the CP/M BDOS function
491 * "get bit map of logged drives", which is meaningless
494 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
495 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
496 * for more information.
502 /* Get Default Drive */
505 setAL(Sda
->CurrentDrive
);
509 /* Set Disk Transfer Area */
512 Sda
->DiskTransferArea
= MAKELONG(getDX(), getDS());
516 /* NULL Function for CP/M Compatibility */
521 * Function 0x1D corresponds to the CP/M BDOS function
522 * "get bit map of read-only drives", which is meaningless
524 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
525 * for more information.
527 * Function 0x1E corresponds to the CP/M BDOS function
528 * "set file attributes", which was meaningless under MS-DOS 1.x.
529 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
530 * for more information.
536 /* NULL Function for CP/M Compatibility */
540 * This function corresponds to the CP/M BDOS function
541 * "get/set default user (sublibrary) number", which is meaningless
544 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
545 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
546 * for more information.
552 /* Set Interrupt Vector */
555 ULONG FarPointer
= MAKELONG(getDX(), getDS());
556 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
557 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
559 /* Write the new far pointer to the IDT */
560 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
567 DosClonePsp(getDX(), getCS());
571 /* Parse Filename into FCB */
574 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
575 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
576 BYTE Options
= getAL();
580 if (FileName
[1] == ':')
582 /* Set the drive number */
583 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
585 /* Skip to the file name part */
590 /* No drive number specified */
591 if (Options
& (1 << 1)) Fcb
->DriveNumber
= Sda
->CurrentDrive
+ 1;
592 else Fcb
->DriveNumber
= 0;
595 /* Parse the file name */
597 while ((*FileName
> 0x20) && (i
< 8))
599 if (*FileName
== '.') break;
600 else if (*FileName
== '*')
606 Fcb
->FileName
[i
++] = RtlUpperChar(*FileName
++);
609 /* Fill the whole field with blanks only if bit 2 is not set */
610 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
612 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
615 /* Skip to the extension part */
616 while (*FileName
> 0x20 && *FileName
!= '.') FileName
++;
617 if (*FileName
== '.') FileName
++;
619 /* Now parse the extension */
623 while ((*FileName
> 0x20) && (i
< 3))
625 if (*FileName
== '*')
631 Fcb
->FileExt
[i
++] = RtlUpperChar(*FileName
++);
634 /* Fill the whole field with blanks only if bit 3 is not set */
635 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
637 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
643 /* Get System Date */
646 GetLocalTime(&SystemTime
);
647 setCX(SystemTime
.wYear
);
648 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
649 setAL(SystemTime
.wDayOfWeek
);
653 /* Set System Date */
656 GetLocalTime(&SystemTime
);
657 SystemTime
.wYear
= getCX();
658 SystemTime
.wMonth
= getDH();
659 SystemTime
.wDay
= getDL();
661 /* Return success or failure */
662 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
666 /* Get System Time */
669 GetLocalTime(&SystemTime
);
670 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
671 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
675 /* Set System Time */
678 GetLocalTime(&SystemTime
);
679 SystemTime
.wHour
= getCH();
680 SystemTime
.wMinute
= getCL();
681 SystemTime
.wSecond
= getDH();
682 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
684 /* Return success or failure */
685 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
689 /* Get Disk Transfer Area */
692 setES(HIWORD(Sda
->DiskTransferArea
));
693 setBX(LOWORD(Sda
->DiskTransferArea
));
697 /* Get DOS Version */
700 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
);
703 * DOS 2+ - GET DOS VERSION
704 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
705 * for more information.
708 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
711 * Return DOS OEM number:
712 * 0x00 for IBM PC-DOS
713 * 0x02 for packaged MS-DOS
719 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
722 * Return version flag:
723 * 1 << 3 if DOS is in ROM,
724 * 0 (reserved) if not.
729 /* Return DOS 24-bit user serial number in BL:CX */
734 * Return DOS version: Minor:Major in AH:AL
735 * The Windows NT DOS box returns version 5.00, subject to SETVER.
737 setAX(PspBlock
->DosVersion
);
742 /* Terminate and Stay Resident */
745 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
746 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), getDX());
750 /* Extended functionalities */
756 * DOS 4+ - GET BOOT DRIVE
760 setDL(SysVars
->BootDrive
);
765 * DOS 5+ - GET TRUE VERSION NUMBER
766 * This function always returns the true version number, unlike
767 * AH=30h, whose return value may be changed with SETVER.
768 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
769 * for more information.
774 * Return the true DOS version: Minor:Major in BH:BL
775 * The Windows NT DOS box returns BX=3205h (version 5.50).
777 setBX(NTDOS_VERSION
);
788 default: // goto Default;
790 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
798 /* Get Address of InDOS flag */
801 setES(DOS_DATA_SEGMENT
);
802 setBX(DOS_DATA_OFFSET(Sda
.InDos
));
806 /* Get Interrupt Vector */
809 ULONG FarPointer
= ((PULONG
)BaseAddress
)[getAL()];
811 /* Read the address from the IDT into ES:BX */
812 setES(HIWORD(FarPointer
));
813 setBX(LOWORD(FarPointer
));
817 /* Get Free Disk Space */
820 CHAR RootPath
[] = "?:\\";
821 DWORD SectorsPerCluster
;
822 DWORD BytesPerSector
;
823 DWORD NumberOfFreeClusters
;
824 DWORD TotalNumberOfClusters
;
827 RootPath
[0] = 'A' + Sda
->CurrentDrive
;
829 RootPath
[0] = 'A' + getDL() - 1;
831 if (GetDiskFreeSpaceA(RootPath
,
834 &NumberOfFreeClusters
,
835 &TotalNumberOfClusters
))
837 setAX(LOWORD(SectorsPerCluster
));
838 setCX(LOWORD(BytesPerSector
));
839 setBX(min(NumberOfFreeClusters
, 0xFFFF));
840 setDX(min(TotalNumberOfClusters
, 0xFFFF));
851 /* SWITCH character - AVAILDEV */
857 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
858 * This setting is ignored by MS-DOS 4.0+.
859 * MS-DOS 5+ always return AL=00h/DL=2Fh.
860 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
861 * for more information.
869 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
870 * This setting is ignored by MS-DOS 5+.
871 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
872 * for more information.
880 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
881 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
882 * for more information.
890 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
891 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
892 * for more information.
899 /* Invalid subfunction */
908 /* Get/Set Country-dependent Information */
911 WORD CountryId
= getAL() < 0xFF ? getAL() : getBX();
914 ErrorCode
= DosGetCountryInfo(&CountryId
,
915 (PDOS_COUNTRY_INFO
)SEG_OFF_TO_PTR(getDS(), getDX()));
917 if (ErrorCode
== ERROR_SUCCESS
)
919 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
924 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
931 /* Create Directory */
934 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
936 if (CreateDirectoryA(String
, NULL
))
938 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
942 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
943 setAX(LOWORD(GetLastError()));
949 /* Remove Directory */
952 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
954 if (RemoveDirectoryA(String
))
956 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
960 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
961 setAX(LOWORD(GetLastError()));
967 /* Set Current Directory */
970 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
972 if (DosChangeDirectory(String
))
974 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
978 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
979 setAX(Sda
->LastErrorCode
);
985 /* Create or Truncate File */
989 WORD ErrorCode
= DosCreateFile(&FileHandle
,
990 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
994 if (ErrorCode
== ERROR_SUCCESS
)
996 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1001 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1008 /* Open File or Device */
1012 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1013 WORD ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
1015 if (ErrorCode
== ERROR_SUCCESS
)
1017 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1022 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1029 /* Close File or Device */
1032 if (DosCloseHandle(getBX()))
1034 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1038 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1039 setAX(ERROR_INVALID_HANDLE
);
1045 /* Read from File or Device */
1051 DPRINT("DosReadFile(0x%04X)\n", getBX());
1054 ErrorCode
= DosReadFile(getBX(),
1055 MAKELONG(getDX(), getDS()),
1060 if (ErrorCode
== ERROR_SUCCESS
)
1062 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1065 else if (ErrorCode
!= ERROR_NOT_READY
)
1067 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1074 /* Write to File or Device */
1077 WORD BytesWritten
= 0;
1078 WORD ErrorCode
= DosWriteFile(getBX(),
1079 MAKELONG(getDX(), getDS()),
1083 if (ErrorCode
== ERROR_SUCCESS
)
1085 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1086 setAX(BytesWritten
);
1090 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1100 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1102 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
1104 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1106 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1107 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1109 setAL(RtlUpperChar(FileName
[0]) - 'A');
1113 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1114 setAX(GetLastError());
1124 WORD ErrorCode
= DosSeekFile(getBX(),
1125 MAKELONG(getDX(), getCX()),
1129 if (ErrorCode
== ERROR_SUCCESS
)
1131 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1133 /* Return the new offset in DX:AX */
1134 setDX(HIWORD(NewLocation
));
1135 setAX(LOWORD(NewLocation
));
1139 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1146 /* Get/Set File Attributes */
1150 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1152 if (getAL() == 0x00)
1154 /* Get the attributes */
1155 Attributes
= GetFileAttributesA(FileName
);
1157 /* Check if it failed */
1158 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1160 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1161 setAX(GetLastError());
1165 /* Return the attributes that DOS can understand */
1166 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1167 setCX(Attributes
& 0x00FF);
1170 else if (getAL() == 0x01)
1172 /* Try to set the attributes */
1173 if (SetFileAttributesA(FileName
, getCL()))
1175 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1179 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1180 setAX(GetLastError());
1185 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1186 setAX(ERROR_INVALID_FUNCTION
);
1195 WORD Length
= getCX();
1197 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length
))
1199 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1204 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1205 setAX(Sda
->LastErrorCode
);
1211 /* Duplicate Handle */
1214 WORD NewHandle
= DosDuplicateHandle(getBX());
1216 if (NewHandle
!= INVALID_DOS_HANDLE
)
1219 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1223 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1224 setAX(Sda
->LastErrorCode
);
1230 /* Force Duplicate Handle */
1233 if (DosForceDuplicateHandle(getBX(), getCX()))
1235 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1239 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1240 setAX(ERROR_INVALID_HANDLE
);
1246 /* Get Current Directory */
1249 BYTE DriveNumber
= getDL();
1250 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1252 /* Get the real drive number */
1253 if (DriveNumber
== 0)
1255 DriveNumber
= Sda
->CurrentDrive
;
1259 /* Decrement DriveNumber since it was 1-based */
1263 if (DriveNumber
< SysVars
->NumLocalDrives
)
1266 * Copy the current directory into the target buffer.
1267 * It doesn't contain the drive letter and the backslash.
1269 strncpy(String
, DosData
->CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
1270 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1271 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1275 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1276 setAX(ERROR_INVALID_DRIVE
);
1282 /* Allocate Memory */
1285 WORD MaxAvailable
= 0;
1286 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1290 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1295 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1296 setAX(Sda
->LastErrorCode
);
1297 setBX(MaxAvailable
);
1306 if (DosFreeMemory(getES()))
1308 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1312 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1313 setAX(ERROR_ARENA_TRASHED
);
1319 /* Resize Memory Block */
1324 if (DosResizeMemory(getES(), getBX(), &Size
))
1326 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1330 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1331 setAX(Sda
->LastErrorCode
);
1341 BYTE OrgAL
= getAL();
1342 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
1343 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
1346 if (OrgAL
<= DOS_LOAD_OVERLAY
)
1348 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)OrgAL
;
1351 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1353 /* Create a new process */
1354 ErrorCode
= DosCreateProcess(ProgramName
,
1356 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1361 /* Just load an executable */
1362 ErrorCode
= DosLoadExecutable(LoadType
,
1367 MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]));
1370 else if (OrgAL
== 0x05)
1372 // http://www.ctyme.com/intr/rb-2942.htm
1373 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1374 ErrorCode
= ERROR_CALL_NOT_IMPLEMENTED
;
1378 ErrorCode
= ERROR_INVALID_FUNCTION
;
1381 if (ErrorCode
== ERROR_SUCCESS
)
1383 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1387 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1394 /* Terminate with Return Code */
1397 DosTerminateProcess(Sda
->CurrentPsp
, getAL(), 0);
1401 /* Get Return Code (ERRORLEVEL) */
1405 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1406 * DosErrorLevel is cleared after being read by this function.
1408 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1409 setAX(Sda
->ErrorLevel
);
1410 Sda
->ErrorLevel
= 0x0000; // Clear it
1414 /* Find First File */
1417 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(Sda
->DiskTransferArea
),
1418 SEG_OFF_TO_PTR(getDS(), getDX()),
1423 if (Result
== ERROR_SUCCESS
)
1424 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1426 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1431 /* Find Next File */
1434 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(Sda
->DiskTransferArea
));
1438 if (Result
== ERROR_SUCCESS
)
1439 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1441 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1446 /* Internal - Set Current Process ID (Set PSP Address) */
1449 DosSetProcessContext(getBX());
1453 /* Internal - Get Current Process ID (Get PSP Address) */
1455 /* Get Current PSP Address */
1459 * Undocumented AH=51h is identical to the documented AH=62h.
1460 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1461 * and http://www.ctyme.com/intr/rb-3140.htm
1462 * for more information.
1464 setBX(Sda
->CurrentPsp
);
1468 /* Internal - Get "List of lists" (SYSVARS) */
1472 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1473 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1474 * for more information.
1477 /* Return the DOS "list of lists" in ES:BX */
1478 setES(DOS_DATA_SEGMENT
);
1479 setBX(DOS_DATA_OFFSET(SysVars
.FirstDpb
));
1483 /* Create Child PSP */
1486 DosCreatePsp(getDX(), getSI());
1487 DosSetProcessContext(getDX());
1494 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1495 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
1498 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1499 * for more information.
1502 if (MoveFileA(ExistingFileName
, NewFileName
))
1504 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1508 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1509 setAX(GetLastError());
1515 /* File Attributes */
1520 /* Get File's last-written Date and Time */
1523 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1524 FILETIME LastWriteTime
;
1525 WORD FileDate
, FileTime
;
1527 if (Descriptor
== NULL
)
1529 /* Invalid handle */
1530 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1531 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1532 setAX(ERROR_INVALID_HANDLE
);
1536 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1538 /* Invalid for devices */
1539 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1540 // setAX(ERROR_INVALID_FUNCTION);
1541 setAX(ERROR_INVALID_HANDLE
);
1546 * Retrieve the last-written Win32 date and time,
1547 * and convert it to DOS format.
1549 if (!GetFileTime(Descriptor
->Win32Handle
,
1550 NULL
, NULL
, &LastWriteTime
) ||
1551 !FileTimeToDosDateTime(&LastWriteTime
,
1552 &FileDate
, &FileTime
))
1554 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1555 setAX(GetLastError());
1559 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1565 /* Set File's last-written Date and Time */
1568 PDOS_FILE_DESCRIPTOR Descriptor
= DosGetHandleFileDescriptor(getBX());
1569 FILETIME LastWriteTime
;
1570 WORD FileDate
= getDX();
1571 WORD FileTime
= getCX();
1573 if (Descriptor
== NULL
)
1575 /* Invalid handle */
1576 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1577 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1578 setAX(ERROR_INVALID_HANDLE
);
1582 if (Descriptor
->DeviceInfo
& FILE_INFO_DEVICE
)
1584 /* Invalid for devices */
1585 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1586 // setAX(ERROR_INVALID_FUNCTION);
1587 setAX(ERROR_INVALID_HANDLE
);
1592 * Convert the new last-written DOS date and time
1593 * to Win32 format and set it.
1595 if (!DosDateTimeToFileTime(FileDate
, FileTime
,
1597 !SetFileTime(Descriptor
->Win32Handle
,
1598 NULL
, NULL
, &LastWriteTime
))
1600 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1601 setAX(GetLastError());
1605 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1609 default: // goto Default;
1611 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1619 /* Get/Set Memory Management Options */
1624 /* Get allocation strategy */
1627 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1628 setAX(Sda
->AllocStrategy
);
1632 /* Set allocation strategy */
1635 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1636 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1638 /* Can't set both */
1639 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1640 setAX(ERROR_INVALID_PARAMETER
);
1644 if ((getBL() & ~(DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1645 > DOS_ALLOC_LAST_FIT
)
1647 /* Invalid allocation strategy */
1648 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1649 setAX(ERROR_INVALID_PARAMETER
);
1653 Sda
->AllocStrategy
= getBL();
1654 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1658 /* Get UMB link state */
1661 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1662 setAL(DosUmbLinked
? 0x01 : 0x00);
1666 /* Set UMB link state */
1674 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1678 /* Invalid or unsupported function */
1681 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1682 setAX(ERROR_INVALID_FUNCTION
);
1689 /* Get Extended Error Information */
1692 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1697 /* Create Temporary File */
1700 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1701 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
1707 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1708 * for more information.
1711 // FIXME: Check for buffer validity?
1712 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1713 // to receive the generated filename.
1715 /* First create the temporary file */
1716 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
1719 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1720 setAX(GetLastError());
1724 /* Now try to open it in read/write access */
1725 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
1726 if (ErrorCode
== ERROR_SUCCESS
)
1728 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1733 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1740 /* Create New File */
1744 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1745 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1749 if (ErrorCode
== ERROR_SUCCESS
)
1751 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1756 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1763 /* Lock/Unlock Region of File */
1766 if (getAL() == 0x00)
1768 /* Lock region of file */
1769 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1771 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1775 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1776 setAX(Sda
->LastErrorCode
);
1779 else if (getAL() == 0x01)
1781 /* Unlock region of file */
1782 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1784 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1788 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1789 setAX(Sda
->LastErrorCode
);
1794 /* Invalid subfunction */
1795 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1796 setAX(ERROR_INVALID_FUNCTION
);
1802 /* Canonicalize File Name or Path */
1806 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1807 * for more information.
1811 * We suppose that the DOS app gave to us a valid
1812 * 128-byte long buffer for the canonicalized name.
1814 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1816 SEG_OFF_TO_PTR(getES(), getDI()),
1820 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1821 setAX(GetLastError());
1825 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1829 // FIXME: Convert the full path name into short version.
1830 // We cannot reliably use GetShortPathName, because it fails
1831 // if the path name given doesn't exist. However this DOS
1832 // function AH=60h should be able to work even for non-existing
1833 // path and file names.
1838 /* Miscellaneous Internal Functions */
1843 /* Get Swappable Data Area */
1846 setDS(DOS_DATA_SEGMENT
);
1847 setSI(DOS_DATA_OFFSET(Sda
.ErrorMode
));
1848 setCX(sizeof(DOS_SDA
));
1849 setDX(FIELD_OFFSET(DOS_SDA
, LastAX
));
1851 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1855 default: // goto Default;
1857 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1865 /* Extended Country Information */
1870 case 0x01: case 0x02: case 0x03:
1871 case 0x04: case 0x05: case 0x06:
1874 WORD BufferSize
= getCX();
1876 ErrorCode
= DosGetCountryInfoEx(getAL(),
1879 (PDOS_COUNTRY_INFO_2
)SEG_OFF_TO_PTR(getES(), getDI()),
1881 if (ErrorCode
== ERROR_SUCCESS
)
1883 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1888 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1895 /* Country-dependent Character Capitalization -- Character */
1897 /* Country-dependent Filename Capitalization -- Character */
1900 setDL(DosToUpper(getDL()));
1901 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1902 // setAX(ERROR_SUCCESS);
1906 /* Country-dependent Character Capitalization -- Counted ASCII String */
1908 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1911 PCHAR Str
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1912 // FIXME: Check for NULL ptr!!
1913 DosToUpperStrN(Str
, Str
, getCX());
1914 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1915 // setAX(ERROR_SUCCESS);
1919 /* Country-dependent Character Capitalization -- ASCIIZ String */
1921 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1924 PSTR Str
= (PSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1925 // FIXME: Check for NULL ptr!!
1926 DosToUpperStrZ(Str
, Str
);
1927 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1928 // setAX(ERROR_SUCCESS);
1932 /* Determine if Character represents YES/NO Response */
1935 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1936 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1940 default: // goto Default;
1942 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1950 /* Set Handle Count */
1953 if (!DosResizeHandleTable(getBX()))
1955 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1956 setAX(Sda
->LastErrorCode
);
1958 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1968 * Function 6Ah is identical to function 68h,
1969 * and sets AH to 68h if success.
1970 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1971 * for more information.
1975 if (DosFlushFileBuffers(getBX()))
1977 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1981 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1982 setAX(GetLastError());
1988 /* Extended Open/Create */
1992 WORD CreationStatus
;
1995 /* Check for AL == 00 */
1996 if (getAL() != 0x00)
1998 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1999 setAX(ERROR_INVALID_FUNCTION
);
2004 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2005 * for the full detailed description.
2007 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2010 ErrorCode
= DosCreateFileEx(&FileHandle
,
2012 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2017 if (ErrorCode
== ERROR_SUCCESS
)
2019 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2020 setCX(CreationStatus
);
2025 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2033 default: // Default:
2035 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2038 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2039 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2046 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2048 /* Set CF to terminate the running process */
2049 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2052 VOID WINAPI
DosAbsoluteRead(LPWORD Stack
)
2055 * This call should leave the flags on the stack for some reason,
2056 * so move the stack by one word.
2057 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2059 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2060 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2061 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2062 setSP(LOWORD(getSP() - 2));
2064 // TODO: NOT IMPLEMENTED;
2067 /* General failure */
2069 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2072 VOID WINAPI
DosAbsoluteWrite(LPWORD Stack
)
2075 * This call should leave the flags on the stack for some reason,
2076 * so move the stack by one word.
2077 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2079 Stack
[STACK_INT_NUM
] = Stack
[STACK_IP
];
2080 Stack
[STACK_IP
] = Stack
[STACK_CS
];
2081 Stack
[STACK_CS
] = Stack
[STACK_FLAGS
];
2082 setSP(LOWORD(getSP() - 2));
2084 // TODO: NOT IMPLEMENTED;
2087 /* General failure */
2089 Stack
[STACK_FLAGS
- 1] |= EMULATOR_FLAG_CF
;
2092 VOID WINAPI
DosInt27h(LPWORD Stack
)
2094 DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
2097 VOID WINAPI
DosIdle(LPWORD Stack
)
2100 * This will set the carry flag on the first call (to repeat the BOP),
2101 * and clear it in the next, so that exactly one HLT occurs.
2106 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2109 * This is the DOS 2+ Fast Console Output Interrupt.
2110 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2112 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2113 * for more information.
2116 /* Save AX and BX */
2117 USHORT AX
= getAX();
2118 USHORT BX
= getBX();
2121 * Set the parameters:
2122 * AL contains the character to print (already set),
2123 * BL contains the character attribute,
2124 * BH contains the video page to use.
2126 setBL(DOS_CHAR_ATTRIBUTE
);
2127 setBH(Bda
->VideoPage
);
2129 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2131 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2133 /* Restore AX and BX */
2138 VOID WINAPI
DosInt2Ah(LPWORD Stack
)
2140 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2143 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2147 /* DOS 3+ Internal Utility Functions */
2150 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2154 /* Installation Check */
2161 /* Get DOS Data Segment */
2164 setDS(DOS_DATA_SEGMENT
);
2168 /* Compare FAR Pointers */
2171 PVOID PointerFromFarPointer1
= SEG_OFF_TO_PTR(getDS(), getSI());
2172 PVOID PointerFromFarPointer2
= SEG_OFF_TO_PTR(getES(), getDI());
2173 BOOLEAN AreEqual
= (PointerFromFarPointer1
== PointerFromFarPointer2
);
2180 /* Set DOS Version Number to return */
2183 WORD DosVersion
= getDX();
2185 // Special case: return the true DOS version when DX=00h
2186 if (DosVersion
== 0x0000)
2187 DosData
->DosVersion
= DOS_VERSION
;
2189 DosData
->DosVersion
= DosVersion
;
2198 /* Set Disk Interrupt Handler */
2201 /* Save the old values of PrevInt13 and RomBiosInt13 */
2202 ULONG OldInt13
= DosData
->PrevInt13
;
2203 ULONG OldBiosInt13
= DosData
->RomBiosInt13
;
2205 /* Set PrevInt13 and RomBiosInt13 to their new values */
2206 DosData
->PrevInt13
= MAKELONG(getDX(), getDS());
2207 DosData
->RomBiosInt13
= MAKELONG(getBX(), getES());
2209 /* Return in DS:DX the old value of PrevInt13 */
2210 setDS(HIWORD(OldInt13
));
2211 setDX(LOWORD(OldInt13
));
2213 /* Return in DS:DX the old value of RomBiosInt13 */
2214 setES(HIWORD(OldBiosInt13
));
2215 setBX(LOWORD(OldBiosInt13
));
2220 /* Mostly Windows 2.x/3.x/9x support */
2224 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2225 * Just do nothing in this case.
2227 if (getAL() != 0x80) goto Default
;
2231 /* Extended Memory Specification */
2235 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2239 /* Installation Check */
2242 /* The driver is loaded */
2247 /* Get Driver Address */
2250 setES(HIWORD(DriverEntry
));
2251 setBX(LOWORD(DriverEntry
));
2256 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2265 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2267 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2272 BOOLEAN
DosKRNLInitialize(VOID
)
2279 BOOLEAN Success
= TRUE
;
2281 CHAR CurrentDirectory
[MAX_PATH
];
2282 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2284 const BYTE NullDriverRoutine
[] = {
2285 /* Strategy routine entry */
2286 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2289 FIELD_OFFSET(DOS_REQUEST_HEADER
, Status
),
2290 LOBYTE(DOS_DEVSTAT_DONE
),
2291 HIBYTE(DOS_DEVSTAT_DONE
),
2293 /* Interrupt routine entry */
2300 /* Initialize the global DOS data area */
2301 DosData
= (PDOS_DATA
)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT
, 0x0000);
2302 RtlZeroMemory(DosData
, sizeof(*DosData
));
2304 /* Initialize the list of lists */
2305 SysVars
= &DosData
->SysVars
;
2306 RtlZeroMemory(SysVars
, sizeof(*SysVars
));
2307 SysVars
->FirstMcb
= FIRST_MCB_SEGMENT
;
2308 SysVars
->FirstSft
= MAKELONG(DOS_DATA_OFFSET(Sft
), DOS_DATA_SEGMENT
);
2309 SysVars
->CurrentDirs
= MAKELONG(DOS_DATA_OFFSET(CurrentDirectories
),
2312 * The last drive can be redefined with the LASTDRIVE command.
2313 * At the moment, set the real maximum possible, 'Z'.
2315 SysVars
->NumLocalDrives
= 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2317 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2318 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2319 SysVars
->BootDrive
= RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]) - 'A' + 1;
2321 /* Initialize the NUL device driver */
2322 SysVars
->NullDevice
.Link
= 0xFFFFFFFF;
2323 SysVars
->NullDevice
.DeviceAttributes
= DOS_DEVATTR_NUL
| DOS_DEVATTR_CHARACTER
;
2324 // Offset from within the DOS data segment
2325 SysVars
->NullDevice
.StrategyRoutine
= DOS_DATA_OFFSET(NullDriverRoutine
);
2326 // Hardcoded to the RETF inside StrategyRoutine
2327 SysVars
->NullDevice
.InterruptRoutine
= SysVars
->NullDevice
.StrategyRoutine
+ 6;
2328 RtlFillMemory(SysVars
->NullDevice
.DeviceName
,
2329 sizeof(SysVars
->NullDevice
.DeviceName
),
2331 RtlCopyMemory(SysVars
->NullDevice
.DeviceName
, "NUL", strlen("NUL"));
2332 RtlCopyMemory(DosData
->NullDriverRoutine
,
2334 sizeof(NullDriverRoutine
));
2336 /* Default DOS version to report */
2337 DosData
->DosVersion
= DOS_VERSION
;
2339 /* Initialize the swappable data area */
2340 Sda
= &DosData
->Sda
;
2341 RtlZeroMemory(Sda
, sizeof(*Sda
));
2343 /* Get the current directory and convert it to a DOS path */
2344 dwRet
= GetCurrentDirectoryA(sizeof(CurrentDirectory
), CurrentDirectory
);
2348 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2350 else if (dwRet
> sizeof(CurrentDirectory
))
2353 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet
);
2358 dwRet
= GetShortPathNameA(CurrentDirectory
, DosDirectory
, sizeof(DosDirectory
));
2362 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2364 else if (dwRet
> sizeof(DosDirectory
))
2367 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet
);
2373 /* We failed, use the boot drive instead */
2374 DosDirectory
[0] = SysVars
->BootDrive
+ 'A' - 1;
2375 DosDirectory
[1] = ':';
2376 DosDirectory
[2] = '\\';
2377 DosDirectory
[3] = '\0';
2380 /* Set the current drive */
2381 Sda
->CurrentDrive
= RtlUpperChar(DosDirectory
[0]) - 'A';
2383 /* Get the directory part of the path and set the current directory */
2384 Path
= strchr(DosDirectory
, '\\');
2387 Path
++; // Skip the backslash
2388 strncpy(DosData
->CurrentDirectories
[Sda
->CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2392 DosData
->CurrentDirectories
[Sda
->CurrentDrive
][0] = '\0';
2395 /* Set the current PSP to the system PSP */
2396 Sda
->CurrentPsp
= SYSTEM_PSP
;
2398 /* Set the initial allocation strategy to "best fit" */
2399 Sda
->AllocStrategy
= DOS_ALLOC_BEST_FIT
;
2401 /* Initialize the SFT */
2402 Sft
= (PDOS_SFT
)FAR_POINTER(SysVars
->FirstSft
);
2403 Sft
->Link
= 0xFFFFFFFF;
2404 Sft
->NumDescriptors
= DOS_SFT_SIZE
;
2406 for (i
= 0; i
< Sft
->NumDescriptors
; i
++)
2408 /* Clear the file descriptor entry */
2409 RtlZeroMemory(&Sft
->FileDescriptors
[i
], sizeof(DOS_FILE_DESCRIPTOR
));
2412 /* Read CONFIG.SYS */
2413 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2416 while (fgetws(Buffer
, 256, Stream
))
2418 // TODO: Parse the line
2425 /* Initialize the callback context */
2426 InitializeContext(&DosContext
, DOS_CODE_SEGMENT
, 0x0000);
2428 /* Register the DOS 32-bit Interrupts */
2429 RegisterDosInt32(0x20, DosInt20h
);
2430 RegisterDosInt32(0x21, DosInt21h
);
2431 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2432 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2433 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2434 RegisterDosInt32(0x25, DosAbsoluteRead
); // Absolute Disk Read
2435 RegisterDosInt32(0x26, DosAbsoluteWrite
); // Absolute Disk Write
2436 RegisterDosInt32(0x27, DosInt27h
); // Terminate and Stay Resident
2437 RegisterDosInt32(0x28, DosIdle
); // DOS Idle Interrupt
2438 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2439 RegisterDosInt32(0x2F, DosInt2Fh
); // Multiplex Interrupt
2441 /* Unimplemented DOS interrupts */
2442 RegisterDosInt32(0x2A, DosInt2Ah
); // DOS Critical Sections / Network
2443 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2444 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2445 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2447 /* Reserved DOS interrupts */
2448 RegisterDosInt32(0x2B, NULL
);
2449 RegisterDosInt32(0x2C, NULL
);
2450 RegisterDosInt32(0x2D, NULL
);
2453 * Initialize the INT 13h (BIOS Disk Services) handler chain support.
2455 * The INT 13h handler chain is some functionality that allows DOS
2456 * to insert disk filter drivers in between the (hooked) INT 13h handler
2457 * and its original handler.
2458 * Typically, those are:
2459 * - filter for detecting disk changes (for floppy disks),
2460 * - filter for tracking formatting calls and correcting DMA boundary errors,
2461 * - a possible filter to work around a bug in a particular version of PC-AT's
2462 * IBM's ROM BIOS (on systems with model byte FCh and BIOS date "01/10/84" only)
2463 * (see http://www.ctyme.com/intr/rb-4453.htm for more details).
2465 * This functionality is known to be used by some legitimate programs,
2466 * by Windows 3.x, as well as some illegitimate ones (aka. virii).
2468 * See extra information about this support in dos.h
2470 // FIXME: Should be done by the DOS BIOS
2471 DosData
->RomBiosInt13
= ((PULONG
)BaseAddress
)[0x13];
2472 DosData
->PrevInt13
= DosData
->RomBiosInt13
;
2473 // RegisterDosInt32(0x13, DosInt13h); // Unused at the moment!
2475 /* Initialize country data */
2476 DosCountryInitialize();
2478 /* Load the CON driver */
2481 /* Load the XMS driver (HIMEM) */
2484 /* Load the EMS driver */
2485 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
2487 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2488 "Try reducing the number of EMS pages.\n");