2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
19 #include "bios/bios.h"
20 #include "registers.h"
22 /* PRIVATE VARIABLES **********************************************************/
24 CALLBACK16 DosContext
;
26 static WORD CurrentPsp
= SYSTEM_PSP
;
27 static WORD DosLastError
= 0;
28 static DWORD DiskTransferArea
;
29 /*static*/ BYTE CurrentDrive
;
30 static CHAR LastDrive
= 'E';
31 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
32 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
33 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
34 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
35 static BOOLEAN DosUmbLinked
= FALSE
;
36 static WORD DosErrorLevel
= 0x0000;
38 /* PRIVATE FUNCTIONS **********************************************************/
41 * Memory management functions
43 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
45 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
47 /* If this is the last block or it's not free, quit */
48 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
52 /* Get a pointer to the next MCB */
53 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
55 /* Check if the next MCB is free */
56 if (NextMcb
->OwnerPsp
== 0)
59 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
60 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
61 NextMcb
->BlockType
= 'I';
65 /* No more adjoining free blocks */
71 static WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
73 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
74 PDOS_MCB CurrentMcb
, NextMcb
;
75 BOOLEAN SearchUmb
= FALSE
;
77 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
79 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
81 /* Search UMB first */
82 Segment
= UMB_START_SEGMENT
;
88 /* Get a pointer to the MCB */
89 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
91 /* Make sure it's valid */
92 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
94 DPRINT("The DOS memory arena is corrupted!\n");
95 DosLastError
= ERROR_ARENA_TRASHED
;
99 /* Only check free blocks */
100 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
102 /* Combine this free block with adjoining free blocks */
103 DosCombineFreeBlocks(Segment
);
105 /* Update the maximum block size */
106 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
108 /* Check if this block is big enough */
109 if (CurrentMcb
->Size
< Size
) goto Next
;
111 switch (DosAllocStrategy
& 0x3F)
113 case DOS_ALLOC_FIRST_FIT
:
115 /* For first fit, stop immediately */
120 case DOS_ALLOC_BEST_FIT
:
122 /* For best fit, update the smallest block found so far */
123 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
131 case DOS_ALLOC_LAST_FIT
:
133 /* For last fit, make the current block the result, but keep searching */
140 /* If this was the last MCB in the chain, quit */
141 if (CurrentMcb
->BlockType
== 'Z')
143 /* Check if nothing was found while searching through UMBs */
144 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
146 /* Search low memory */
147 Segment
= FIRST_MCB_SEGMENT
;
154 /* Otherwise, update the segment and continue */
155 Segment
+= CurrentMcb
->Size
+ 1;
160 /* If we didn't find a free block, return 0 */
163 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
164 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
168 /* Get a pointer to the MCB */
169 CurrentMcb
= SEGMENT_TO_MCB(Result
);
171 /* Check if the block is larger than requested */
172 if (CurrentMcb
->Size
> Size
)
174 /* It is, split it into two blocks */
175 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
177 /* Initialize the new MCB structure */
178 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
179 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
180 NextMcb
->OwnerPsp
= 0;
182 /* Update the current block */
183 CurrentMcb
->BlockType
= 'M';
184 CurrentMcb
->Size
= Size
;
187 /* Take ownership of the block */
188 CurrentMcb
->OwnerPsp
= CurrentPsp
;
190 /* Return the segment of the data portion of the block */
194 static BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
196 BOOLEAN Success
= TRUE
;
197 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
198 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
200 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
204 /* Make sure this is a valid, allocated block */
205 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
208 DosLastError
= ERROR_INVALID_HANDLE
;
212 ReturnSize
= Mcb
->Size
;
214 /* Check if we need to expand or contract the block */
215 if (NewSize
> Mcb
->Size
)
217 /* We can't expand the last block */
218 if (Mcb
->BlockType
!= 'M')
224 /* Get the pointer and segment of the next MCB */
225 NextSegment
= Segment
+ Mcb
->Size
+ 1;
226 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
228 /* Make sure the next segment is free */
229 if (NextMcb
->OwnerPsp
!= 0)
231 DPRINT("Cannot expand memory block: next segment is not free!\n");
232 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
237 /* Combine this free block with adjoining free blocks */
238 DosCombineFreeBlocks(NextSegment
);
240 /* Set the maximum possible size of the block */
241 ReturnSize
+= NextMcb
->Size
+ 1;
243 /* Maximize the current block */
244 Mcb
->Size
= ReturnSize
;
245 Mcb
->BlockType
= NextMcb
->BlockType
;
247 /* Invalidate the next block */
248 NextMcb
->BlockType
= 'I';
250 /* Check if the block is larger than requested */
251 if (Mcb
->Size
> NewSize
)
253 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
257 /* It is, split it into two blocks */
258 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
260 /* Initialize the new MCB structure */
261 NextMcb
->BlockType
= Mcb
->BlockType
;
262 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
263 NextMcb
->OwnerPsp
= 0;
265 /* Update the current block */
266 Mcb
->BlockType
= 'M';
270 else if (NewSize
< Mcb
->Size
)
272 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
276 /* Just split the block */
277 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
278 NextMcb
->BlockType
= Mcb
->BlockType
;
279 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
280 NextMcb
->OwnerPsp
= 0;
283 Mcb
->BlockType
= 'M';
288 /* Check if the operation failed */
291 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
294 /* Return the maximum possible size */
295 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
301 static BOOLEAN
DosFreeMemory(WORD BlockData
)
303 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
305 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
307 /* Make sure the MCB is valid */
308 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
310 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
314 /* Mark the block as free */
320 static BOOLEAN
DosLinkUmb(VOID
)
322 DWORD Segment
= FIRST_MCB_SEGMENT
;
323 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
325 DPRINT("Linking UMB\n");
327 /* Check if UMBs are already linked */
328 if (DosUmbLinked
) return FALSE
;
330 /* Find the last block */
331 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
333 Segment
+= Mcb
->Size
+ 1;
334 Mcb
= SEGMENT_TO_MCB(Segment
);
337 /* Make sure it's valid */
338 if (Mcb
->BlockType
!= 'Z') return FALSE
;
340 /* Connect the MCB with the UMB chain */
341 Mcb
->BlockType
= 'M';
347 static BOOLEAN
DosUnlinkUmb(VOID
)
349 DWORD Segment
= FIRST_MCB_SEGMENT
;
350 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
352 DPRINT("Unlinking UMB\n");
354 /* Check if UMBs are already unlinked */
355 if (!DosUmbLinked
) return FALSE
;
357 /* Find the block preceding the MCB that links it with the UMB chain */
358 while (Segment
<= 0xFFFF)
360 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
362 /* This is the last non-UMB segment */
366 /* Advance to the next MCB */
367 Segment
+= Mcb
->Size
+ 1;
368 Mcb
= SEGMENT_TO_MCB(Segment
);
371 /* Mark the MCB as the last MCB */
372 Mcb
->BlockType
= 'Z';
374 DosUmbLinked
= FALSE
;
378 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
380 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
382 /* Just set the owner */
383 Mcb
->OwnerPsp
= NewOwner
;
386 static WORD
DosCopyEnvironmentBlock(LPCVOID Environment
, LPCSTR ProgramName
)
388 PCHAR Ptr
, DestBuffer
= NULL
;
392 Ptr
= (PCHAR
)Environment
;
394 /* Calculate the size of the environment block */
397 TotalSize
+= strlen(Ptr
) + 1;
398 Ptr
+= strlen(Ptr
) + 1;
402 /* Add the string buffer size */
403 TotalSize
+= strlen(ProgramName
) + 1;
405 /* Allocate the memory for the environment block */
406 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
407 if (!DestSegment
) return 0;
409 Ptr
= (PCHAR
)Environment
;
411 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
414 /* Copy the string */
415 strcpy(DestBuffer
, Ptr
);
417 /* Advance to the next string */
418 DestBuffer
+= strlen(Ptr
);
419 Ptr
+= strlen(Ptr
) + 1;
421 /* Put a zero after the string */
425 /* Set the final zero */
428 /* Copy the program name after the environment block */
429 strcpy(DestBuffer
, ProgramName
);
434 /* Taken from base/shell/cmd/console.c */
435 BOOL
IsConsoleHandle(HANDLE hHandle
)
439 /* Check whether the handle may be that of a console... */
440 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
443 * It may be. Perform another test... The idea comes from the
444 * MSDN description of the WriteConsole API:
446 * "WriteConsole fails if it is used with a standard handle
447 * that is redirected to a file. If an application processes
448 * multilingual output that can be redirected, determine whether
449 * the output handle is a console handle (one method is to call
450 * the GetConsoleMode function and check whether it succeeds).
451 * If the handle is a console handle, call WriteConsole. If the
452 * handle is not a console handle, the output is redirected and
453 * you should call WriteFile to perform the I/O."
455 return GetConsoleMode(hHandle
, &dwMode
);
458 static WORD
DosOpenHandle(HANDLE Handle
)
465 /* The system PSP has no handle table */
466 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
468 /* Get a pointer to the handle table */
469 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
470 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
472 /* Find a free entry in the JFT */
473 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
475 if (HandleTable
[DosHandle
] == 0xFF) break;
478 /* If there are no free entries, fail */
479 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
481 /* Check if the handle is already in the SFT */
482 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
484 /* Check if this is the same handle */
485 if (DosSystemFileTable
[i
] != Handle
) continue;
487 /* Already in the table, reference it */
490 /* Set the JFT entry to that SFT index */
491 HandleTable
[DosHandle
] = i
;
493 /* Return the new handle */
497 /* Add the handle to the SFT */
498 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
500 /* Make sure this is an empty table entry */
501 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
503 /* Initialize the empty table entry */
504 DosSystemFileTable
[i
] = Handle
;
505 DosSftRefCount
[i
] = 1;
507 /* Set the JFT entry to that SFT index */
508 HandleTable
[DosHandle
] = i
;
510 /* Return the new handle */
514 /* The SFT is full */
515 return INVALID_DOS_HANDLE
;
518 HANDLE
DosGetRealHandle(WORD DosHandle
)
523 /* The system PSP has no handle table */
524 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
526 /* Get a pointer to the handle table */
527 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
528 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
530 /* Make sure the handle is open */
531 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
533 /* Return the Win32 handle */
534 return DosSystemFileTable
[HandleTable
[DosHandle
]];
537 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
543 /* Clear the table first */
544 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
546 /* Check if this is the initial process */
547 if (CurrentPsp
== SYSTEM_PSP
)
549 /* Set up the standard I/O devices */
550 for (i
= 0; i
<= 2; i
++)
552 /* Set the index in the SFT */
553 DestinationTable
[i
] = (BYTE
)i
;
555 /* Increase the reference count */
563 /* Get the parent PSP block and handle table */
564 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
565 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
567 /* Copy the first 20 handles into the new table */
568 for (i
= 0; i
< 20; i
++)
570 DestinationTable
[i
] = SourceTable
[i
];
572 /* Increase the reference count */
573 DosSftRefCount
[SourceTable
[i
]]++;
577 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
583 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
585 /* The system PSP has no handle table */
586 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
588 /* Get a pointer to the handle table */
589 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
590 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
592 /* Make sure the handle is open */
593 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
595 /* Decrement the reference count of the SFT entry */
596 SftIndex
= HandleTable
[DosHandle
];
597 DosSftRefCount
[SftIndex
]--;
599 /* Check if the reference count fell to zero */
600 if (!DosSftRefCount
[SftIndex
])
602 /* Close the file, it's no longer needed */
603 CloseHandle(DosSystemFileTable
[SftIndex
]);
605 /* Clear the handle */
606 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
609 /* Clear the entry in the JFT */
610 HandleTable
[DosHandle
] = 0xFF;
615 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
621 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
625 /* The system PSP has no handle table */
626 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
628 /* Get a pointer to the handle table */
629 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
630 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
632 /* Make sure the old handle is open */
633 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
635 /* Check if the new handle is open */
636 if (HandleTable
[NewHandle
] != 0xFF)
639 DosCloseHandle(NewHandle
);
642 /* Increment the reference count of the SFT entry */
643 SftIndex
= HandleTable
[OldHandle
];
644 DosSftRefCount
[SftIndex
]++;
646 /* Make the new handle point to that SFT entry */
647 HandleTable
[NewHandle
] = SftIndex
;
653 static WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
658 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
662 /* Create the file */
663 FileHandle
= CreateFileA(FilePath
,
664 GENERIC_READ
| GENERIC_WRITE
,
665 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
671 if (FileHandle
== INVALID_HANDLE_VALUE
)
673 /* Return the error code */
674 return (WORD
)GetLastError();
677 /* Open the DOS handle */
678 DosHandle
= DosOpenHandle(FileHandle
);
680 if (DosHandle
== INVALID_DOS_HANDLE
)
682 /* Close the handle */
683 CloseHandle(FileHandle
);
685 /* Return the error code */
686 return ERROR_TOO_MANY_OPEN_FILES
;
689 /* It was successful */
691 return ERROR_SUCCESS
;
694 static WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
697 ACCESS_MASK Access
= 0;
700 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
704 /* Parse the access mode */
705 switch (AccessMode
& 3)
710 Access
= GENERIC_READ
;
717 Access
= GENERIC_WRITE
;
724 Access
= GENERIC_READ
| GENERIC_WRITE
;
731 return ERROR_INVALID_PARAMETER
;
736 FileHandle
= CreateFileA(FilePath
,
738 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
741 FILE_ATTRIBUTE_NORMAL
,
744 if (FileHandle
== INVALID_HANDLE_VALUE
)
746 /* Return the error code */
747 return (WORD
)GetLastError();
750 /* Open the DOS handle */
751 DosHandle
= DosOpenHandle(FileHandle
);
753 if (DosHandle
== INVALID_DOS_HANDLE
)
755 /* Close the handle */
756 CloseHandle(FileHandle
);
758 /* Return the error code */
759 return ERROR_TOO_MANY_OPEN_FILES
;
762 /* It was successful */
764 return ERROR_SUCCESS
;
767 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
769 WORD Result
= ERROR_SUCCESS
;
770 DWORD BytesRead32
= 0;
771 HANDLE Handle
= DosGetRealHandle(FileHandle
);
773 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
775 /* Make sure the handle is valid */
776 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
779 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
781 /* Store the error code */
782 Result
= (WORD
)GetLastError();
785 /* The number of bytes read is always 16-bit */
786 *BytesRead
= LOWORD(BytesRead32
);
788 /* Return the error code */
792 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
794 WORD Result
= ERROR_SUCCESS
;
795 DWORD BytesWritten32
= 0;
796 HANDLE Handle
= DosGetRealHandle(FileHandle
);
799 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
803 /* Make sure the handle is valid */
804 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
806 if (IsConsoleHandle(Handle
))
808 for (i
= 0; i
< Count
; i
++)
814 /* Set the parameters */
815 setAL(((PCHAR
)Buffer
)[i
]);
816 setBL(DOS_CHAR_ATTRIBUTE
);
817 setBH(Bda
->VideoPage
);
819 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
821 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
823 /* Restore AX and BX */
833 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
835 /* Store the error code */
836 Result
= (WORD
)GetLastError();
840 /* The number of bytes written is always 16-bit */
841 *BytesWritten
= LOWORD(BytesWritten32
);
843 /* Return the error code */
847 static WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
849 WORD Result
= ERROR_SUCCESS
;
851 HANDLE Handle
= DosGetRealHandle(FileHandle
);
853 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
858 /* Make sure the handle is valid */
859 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
861 /* Check if the origin is valid */
862 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
864 return ERROR_INVALID_FUNCTION
;
867 /* Move the file pointer */
868 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
870 /* Check if there's a possibility the operation failed */
871 if (FilePointer
== INVALID_SET_FILE_POINTER
)
873 /* Get the real error code */
874 Result
= (WORD
)GetLastError();
877 if (Result
!= ERROR_SUCCESS
)
879 /* The operation did fail */
883 /* Return the file pointer, if requested */
884 if (NewOffset
) *NewOffset
= FilePointer
;
887 return ERROR_SUCCESS
;
890 static BOOLEAN
DosFlushFileBuffers(WORD FileHandle
)
892 HANDLE Handle
= DosGetRealHandle(FileHandle
);
894 /* Make sure the handle is valid */
895 if (Handle
== INVALID_HANDLE_VALUE
) return FALSE
;
898 * No need to check whether the handle is a console handle since
899 * FlushFileBuffers() automatically does this check and calls
900 * FlushConsoleInputBuffer() for us.
902 // if (IsConsoleHandle(Handle))
903 // return (BOOLEAN)FlushConsoleInputBuffer(Handle);
905 return (BOOLEAN
)FlushFileBuffers(Handle
);
908 static BOOLEAN
DosChangeDrive(BYTE Drive
)
910 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
912 /* Make sure the drive exists */
913 if (Drive
> (LastDrive
- 'A')) return FALSE
;
915 /* Find the path to the new current directory */
916 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
918 /* Change the current directory of the process */
919 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
921 /* Set the current drive */
922 CurrentDrive
= Drive
;
928 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
934 /* Make sure the directory path is not too long */
935 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
937 DosLastError
= ERROR_PATH_NOT_FOUND
;
941 /* Get the drive number */
942 DriveNumber
= Directory
[0] - 'A';
944 /* Make sure the drive exists */
945 if (DriveNumber
> (LastDrive
- 'A'))
947 DosLastError
= ERROR_PATH_NOT_FOUND
;
951 /* Get the file attributes */
952 Attributes
= GetFileAttributesA(Directory
);
954 /* Make sure the path exists and is a directory */
955 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
956 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
958 DosLastError
= ERROR_PATH_NOT_FOUND
;
962 /* Check if this is the current drive */
963 if (DriveNumber
== CurrentDrive
)
965 /* Change the directory */
966 if (!SetCurrentDirectoryA(Directory
))
968 DosLastError
= LOWORD(GetLastError());
973 /* Get the directory part of the path */
974 Path
= strchr(Directory
, '\\');
977 /* Skip the backslash */
981 /* Set the directory for the drive */
984 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
988 CurrentDirectories
[DriveNumber
][0] = '\0';
995 /* PUBLIC FUNCTIONS ***********************************************************/
997 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
999 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
1000 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1002 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
1004 /* Set the exit interrupt */
1005 PspBlock
->Exit
[0] = 0xCD; // int 0x20
1006 PspBlock
->Exit
[1] = 0x20;
1008 /* Set the number of the last paragraph */
1009 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
1011 /* Save the interrupt vectors */
1012 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
1013 PspBlock
->BreakAddress
= IntVecTable
[0x23];
1014 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
1016 /* Set the parent PSP */
1017 PspBlock
->ParentPsp
= CurrentPsp
;
1019 /* Copy the parent handle table */
1020 DosCopyHandleTable(PspBlock
->HandleTable
);
1022 /* Set the environment block */
1023 PspBlock
->EnvBlock
= Environment
;
1025 /* Set the handle table pointers to the internal handle table */
1026 PspBlock
->HandleTableSize
= 20;
1027 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
1029 /* Set the DOS version */
1030 PspBlock
->DosVersion
= DOS_VERSION
;
1032 /* Set the far call opcodes */
1033 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
1034 PspBlock
->FarCall
[1] = 0x21;
1035 PspBlock
->FarCall
[2] = 0xCB; // retf
1037 /* Set the command line */
1038 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
1039 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
1040 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
1043 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
1044 IN LPCSTR ExecutablePath
,
1045 IN LPCSTR CommandLine
,
1046 IN PVOID Environment
,
1047 OUT PDWORD StackLocation OPTIONAL
,
1048 OUT PDWORD EntryPoint OPTIONAL
)
1050 DWORD Result
= ERROR_SUCCESS
;
1051 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
1052 LPBYTE Address
= NULL
;
1056 DWORD i
, FileSize
, ExeSize
;
1057 PIMAGE_DOS_HEADER Header
;
1058 PDWORD RelocationTable
;
1061 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
1069 if (LoadType
== DOS_LOAD_OVERLAY
)
1071 DPRINT1("Overlay loading is not supported yet.\n");
1072 return ERROR_NOT_SUPPORTED
;
1075 /* Open a handle to the executable */
1076 FileHandle
= CreateFileA(ExecutablePath
,
1081 FILE_ATTRIBUTE_NORMAL
,
1083 if (FileHandle
== INVALID_HANDLE_VALUE
)
1085 Result
= GetLastError();
1089 /* Get the file size */
1090 FileSize
= GetFileSize(FileHandle
, NULL
);
1092 /* Create a mapping object for the file */
1093 FileMapping
= CreateFileMapping(FileHandle
,
1099 if (FileMapping
== NULL
)
1101 Result
= GetLastError();
1105 /* Map the file into memory */
1106 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1107 if (Address
== NULL
)
1109 Result
= GetLastError();
1113 /* Copy the environment block to DOS memory */
1114 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
1117 Result
= ERROR_NOT_ENOUGH_MEMORY
;
1121 /* Check if this is an EXE file or a COM file */
1122 if (Address
[0] == 'M' && Address
[1] == 'Z')
1126 /* Get the MZ header */
1127 Header
= (PIMAGE_DOS_HEADER
)Address
;
1129 /* Get the base size of the file, in paragraphs (rounded up) */
1130 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1132 /* Add the PSP size, in paragraphs */
1133 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1135 /* Add the maximum size that should be allocated */
1136 ExeSize
+= Header
->e_maxalloc
;
1138 /* Make sure it does not pass 0xFFFF */
1139 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1141 /* Reduce the size one by one until the allocation is successful */
1142 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1144 /* Try to allocate that much memory */
1145 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1146 if (Segment
!= 0) break;
1149 /* Check if at least the lowest allocation was successful */
1152 Result
= ERROR_NOT_ENOUGH_MEMORY
;
1156 /* Initialize the PSP */
1157 DosInitializePsp(Segment
,
1162 /* The process owns its own memory */
1163 DosChangeMemoryOwner(Segment
, Segment
);
1164 DosChangeMemoryOwner(EnvBlock
, Segment
);
1166 /* Copy the program to Segment:0100 */
1167 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1168 Address
+ (Header
->e_cparhdr
<< 4),
1169 min(FileSize
- (Header
->e_cparhdr
<< 4),
1170 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1172 /* Get the relocation table */
1173 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1175 /* Perform relocations */
1176 for (i
= 0; i
< Header
->e_crlc
; i
++)
1178 /* Get a pointer to the word that needs to be patched */
1179 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1180 0x100 + LOWORD(RelocationTable
[i
]));
1182 /* Add the number of the EXE segment to it */
1183 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1186 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1188 /* Set the initial segment registers */
1192 /* Set the stack to the location from the header */
1193 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1197 CurrentPsp
= Segment
;
1198 DiskTransferArea
= MAKELONG(0x80, Segment
);
1199 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1207 /* Find the maximum amount of memory that can be allocated */
1208 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1210 /* Make sure it's enough for the whole program and the PSP */
1211 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
1213 Result
= ERROR_NOT_ENOUGH_MEMORY
;
1217 /* Allocate all of it */
1218 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1221 Result
= ERROR_ARENA_TRASHED
;
1225 /* The process owns its own memory */
1226 DosChangeMemoryOwner(Segment
, Segment
);
1227 DosChangeMemoryOwner(EnvBlock
, Segment
);
1229 /* Copy the program to Segment:0100 */
1230 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1234 /* Initialize the PSP */
1235 DosInitializePsp(Segment
,
1240 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1242 /* Set the initial segment registers */
1246 /* Set the stack to the last word of the segment */
1247 EmulatorSetStack(Segment
, 0xFFFE);
1250 * Set the value on the stack to 0, so that a near return
1251 * jumps to PSP:0000 which has the exit code.
1253 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
1256 CurrentPsp
= Segment
;
1257 DiskTransferArea
= MAKELONG(0x80, Segment
);
1258 EmulatorExecute(Segment
, 0x100);
1263 if (Result
!= ERROR_SUCCESS
)
1265 /* It was not successful, cleanup the DOS memory */
1266 if (EnvBlock
) DosFreeMemory(EnvBlock
);
1267 if (Segment
) DosFreeMemory(Segment
);
1271 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1273 /* Close the file mapping object */
1274 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1276 /* Close the file handle */
1277 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1282 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1284 PDOS_EXEC_PARAM_BLOCK Parameters
)
1288 LPVOID Environment
= NULL
;
1289 VDM_COMMAND_INFO CommandInfo
;
1290 CHAR CmdLine
[MAX_PATH
];
1291 CHAR AppName
[MAX_PATH
];
1292 CHAR PifFile
[MAX_PATH
];
1293 CHAR Desktop
[MAX_PATH
];
1294 CHAR Title
[MAX_PATH
];
1296 STARTUPINFOA StartupInfo
;
1297 PROCESS_INFORMATION ProcessInfo
;
1299 /* Get the binary type */
1300 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1302 /* Did the caller specify an environment segment? */
1303 if (Parameters
->Environment
)
1305 /* Yes, use it instead of the parent one */
1306 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1309 /* Set up the startup info structure */
1310 ZeroMemory(&StartupInfo
, sizeof(STARTUPINFOA
));
1311 StartupInfo
.cb
= sizeof(STARTUPINFOA
);
1313 /* Create the process */
1314 if (!CreateProcessA(ProgramName
,
1315 FAR_POINTER(Parameters
->CommandLine
),
1325 return GetLastError();
1328 /* Check the type of the program */
1331 /* These are handled by NTVDM */
1332 case SCS_DOS_BINARY
:
1333 case SCS_WOW_BINARY
:
1335 /* Clear the structure */
1336 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1338 /* Initialize the structure members */
1339 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1340 CommandInfo
.CmdLine
= CmdLine
;
1341 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1342 CommandInfo
.AppName
= AppName
;
1343 CommandInfo
.AppLen
= sizeof(AppName
);
1344 CommandInfo
.PifFile
= PifFile
;
1345 CommandInfo
.PifLen
= sizeof(PifFile
);
1346 CommandInfo
.Desktop
= Desktop
;
1347 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1348 CommandInfo
.Title
= Title
;
1349 CommandInfo
.TitleLen
= sizeof(Title
);
1350 CommandInfo
.Env
= Env
;
1351 CommandInfo
.EnvLen
= sizeof(Env
);
1353 /* Get the VDM command information */
1354 if (!GetNextVDMCommand(&CommandInfo
))
1356 /* Shouldn't happen */
1360 /* Increment the re-entry count */
1361 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1362 GetNextVDMCommand(&CommandInfo
);
1364 /* Load the executable */
1365 Result
= DosLoadExecutable(LoadType
,
1369 &Parameters
->StackLocation
,
1370 &Parameters
->EntryPoint
);
1371 if (Result
!= ERROR_SUCCESS
)
1373 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1380 /* Not handled by NTVDM */
1383 /* Wait for the process to finish executing */
1384 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1388 /* Close the handles */
1389 CloseHandle(ProcessInfo
.hProcess
);
1390 CloseHandle(ProcessInfo
.hThread
);
1392 return ERROR_SUCCESS
;
1395 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1398 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1399 PDOS_MCB CurrentMcb
;
1400 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1401 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1402 VDM_COMMAND_INFO CommandInfo
;
1404 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1408 /* Check if this PSP is it's own parent */
1409 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1411 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1413 /* Close the handle */
1417 /* Free the memory used by the process */
1420 /* Get a pointer to the MCB */
1421 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1423 /* Make sure the MCB is valid */
1424 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1426 /* If this block was allocated by the process, free it */
1427 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1429 /* If this was the last block, quit */
1430 if (CurrentMcb
->BlockType
== 'Z') break;
1432 /* Update the segment and continue */
1433 McbSegment
+= CurrentMcb
->Size
+ 1;
1437 /* Restore the interrupt vectors */
1438 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1439 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1440 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1442 /* Update the current PSP */
1443 if (Psp
== CurrentPsp
)
1445 CurrentPsp
= PspBlock
->ParentPsp
;
1446 if (CurrentPsp
== SYSTEM_PSP
)
1448 ResetEvent(VdmTaskEvent
);
1449 EmulatorUnsimulate();
1453 // FIXME: This is probably not the best way to do it
1454 /* Check if this was a nested DOS task */
1455 if (CurrentPsp
!= SYSTEM_PSP
)
1457 /* Decrement the re-entry count */
1458 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1459 GetNextVDMCommand(&CommandInfo
);
1461 /* Clear the structure */
1462 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1464 /* Update the VDM state of the task */
1465 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1466 GetNextVDMCommand(&CommandInfo
);
1469 /* Save the return code - Normal termination */
1470 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1472 /* Return control to the parent process */
1473 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1474 LOWORD(PspBlock
->TerminateAddress
));
1477 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1479 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1481 if (Handle
== INVALID_HANDLE_VALUE
)
1484 DosLastError
= ERROR_FILE_NOT_FOUND
;
1488 switch (ControlCode
)
1490 /* Get Device Information */
1496 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1497 * for a list of possible flags.
1500 if (Handle
== DosSystemFileTable
[0])
1505 else if (Handle
== DosSystemFileTable
[1])
1507 /* Console output */
1511 /* It is a device */
1514 /* Return the device information word */
1519 /* Unsupported control code */
1522 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1524 DosLastError
= ERROR_INVALID_PARAMETER
;
1530 VOID WINAPI
DosInt20h(LPWORD Stack
)
1532 /* This is the exit interrupt */
1533 DosTerminateProcess(Stack
[STACK_CS
], 0);
1536 VOID WINAPI
DosInt21h(LPWORD Stack
)
1539 SYSTEMTIME SystemTime
;
1541 PDOS_INPUT_BUFFER InputBuffer
;
1543 /* Check the value in the AH register */
1546 /* Terminate Program */
1549 DosTerminateProcess(Stack
[STACK_CS
], 0);
1553 /* Read Character from STDIN with Echo */
1556 Character
= DosReadCharacter();
1557 DosPrintCharacter(Character
);
1559 /* Let the BOP repeat if needed */
1566 /* Write Character to STDOUT */
1569 Character
= getDL();
1570 DosPrintCharacter(Character
);
1573 * We return the output character (DOS 2.1+).
1574 * Also, if we're going to output a TAB, then
1575 * don't return a TAB but a SPACE instead.
1576 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1577 * for more information.
1579 setAL(Character
== '\t' ? ' ' : Character
);
1583 /* Read Character from STDAUX */
1586 // FIXME: Really read it from STDAUX!
1587 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1588 setAL(DosReadCharacter());
1592 /* Write Character to STDAUX */
1595 // FIXME: Really write it to STDAUX!
1596 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1597 DosPrintCharacter(getDL());
1601 /* Write Character to Printer */
1604 // FIXME: Really write it to printer!
1605 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1606 DPRINT1("0x%p\n", getDL());
1607 DPRINT1("\n\n-----------\n\n");
1611 /* Direct Console I/O */
1614 Character
= getDL();
1616 if (Character
!= 0xFF)
1619 DosPrintCharacter(Character
);
1622 * We return the output character (DOS 2.1+).
1623 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1624 * for more information.
1631 if (DosCheckInput())
1633 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1634 setAL(DosReadCharacter());
1638 /* No character available */
1639 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1647 /* Character Input without Echo */
1651 Character
= DosReadCharacter();
1653 /* Let the BOP repeat if needed */
1660 /* Write string to STDOUT */
1663 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1665 while (*String
!= '$')
1667 DosPrintCharacter(*String
);
1672 * We return the terminating character (DOS 2.1+).
1673 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1674 * for more information.
1680 /* Read Buffered Input */
1683 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1685 while (Stack
[STACK_COUNTER
] < InputBuffer
->MaxLength
)
1687 /* Try to read a character */
1688 Character
= DosReadCharacter();
1690 /* If it's not ready yet, let the BOP repeat */
1693 /* Echo the character and append it to the buffer */
1694 DosPrintCharacter(Character
);
1695 InputBuffer
->Buffer
[Stack
[STACK_COUNTER
]] = Character
;
1697 if (Character
== '\r') break;
1698 Stack
[STACK_COUNTER
]++;
1701 /* Update the length */
1702 InputBuffer
->Length
= Stack
[STACK_COUNTER
];
1706 /* Get STDIN Status */
1709 setAL(DosCheckInput() ? 0xFF : 0x00);
1713 /* Flush Buffer and Read STDIN */
1716 BYTE InputFunction
= getAL();
1718 /* Flush STDIN buffer */
1719 DosFlushFileBuffers(DOS_INPUT_HANDLE
); // Maybe just create a DosFlushInputBuffer...
1722 * If the input function number contained in AL is valid, i.e.
1723 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1724 * recursively with AL == AH.
1726 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1727 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1728 InputFunction
== 0x0A)
1730 setAH(InputFunction
);
1732 * Instead of calling ourselves really recursively as in:
1734 * prefer resetting the CF flag to let the BOP repeat.
1744 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1746 // TODO: Flush what's needed.
1747 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1749 /* Clear CF in DOS 6 only */
1750 if (PspBlock
->DosVersion
== 0x0006)
1751 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1756 /* Set Default Drive */
1759 DosChangeDrive(getDL());
1760 setAL(LastDrive
- 'A' + 1);
1764 /* NULL Function for CP/M Compatibility */
1768 * This function corresponds to the CP/M BDOS function
1769 * "get bit map of logged drives", which is meaningless
1772 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1773 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1774 * for more information.
1780 /* Get Default Drive */
1783 setAL(CurrentDrive
);
1787 /* Set Disk Transfer Area */
1790 DiskTransferArea
= MAKELONG(getDX(), getDS());
1794 /* NULL Function for CP/M Compatibility */
1799 * Function 0x1D corresponds to the CP/M BDOS function
1800 * "get bit map of read-only drives", which is meaningless
1802 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1803 * for more information.
1805 * Function 0x1E corresponds to the CP/M BDOS function
1806 * "set file attributes", which was meaningless under MS-DOS 1.x.
1807 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1808 * for more information.
1814 /* NULL Function for CP/M Compatibility */
1818 * This function corresponds to the CP/M BDOS function
1819 * "get/set default user (sublibrary) number", which is meaningless
1822 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1823 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1824 * for more information.
1830 /* Set Interrupt Vector */
1833 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1834 DPRINT1("Setting interrupt 0x%x ...\n", getAL());
1836 /* Write the new far pointer to the IDT */
1837 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1841 /* Create New PSP */
1844 DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n");
1848 /* Get System Date */
1851 GetLocalTime(&SystemTime
);
1852 setCX(SystemTime
.wYear
);
1853 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1854 setAL(SystemTime
.wDayOfWeek
);
1858 /* Set System Date */
1861 GetLocalTime(&SystemTime
);
1862 SystemTime
.wYear
= getCX();
1863 SystemTime
.wMonth
= getDH();
1864 SystemTime
.wDay
= getDL();
1866 /* Return success or failure */
1867 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1871 /* Get System Time */
1874 GetLocalTime(&SystemTime
);
1875 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1876 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1880 /* Set System Time */
1883 GetLocalTime(&SystemTime
);
1884 SystemTime
.wHour
= getCH();
1885 SystemTime
.wMinute
= getCL();
1886 SystemTime
.wSecond
= getDH();
1887 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1889 /* Return success or failure */
1890 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1894 /* Get Disk Transfer Area */
1897 setES(HIWORD(DiskTransferArea
));
1898 setBX(LOWORD(DiskTransferArea
));
1902 /* Get DOS Version */
1905 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1908 * DOS 2+ - GET DOS VERSION
1909 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1910 * for more information.
1913 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1916 * Return DOS OEM number:
1917 * 0x00 for IBM PC-DOS
1918 * 0x02 for packaged MS-DOS
1923 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1926 * Return version flag:
1927 * 1 << 3 if DOS is in ROM,
1928 * 0 (reserved) if not.
1933 /* Return DOS 24-bit user serial number in BL:CX */
1938 * Return DOS version: Minor:Major in AH:AL
1939 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1941 setAX(PspBlock
->DosVersion
);
1946 /* Extended functionalities */
1949 if (getAL() == 0x06)
1952 * DOS 5+ - GET TRUE VERSION NUMBER
1953 * This function always returns the true version number, unlike
1954 * AH=30h, whose return value may be changed with SETVER.
1955 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1956 * for more information.
1960 * Return the true DOS version: Minor:Major in BH:BL
1961 * The Windows NT DOS box returns BX=3205h (version 5.50).
1963 setBX(NTDOS_VERSION
);
1965 /* DOS revision 0 */
1973 // /* Invalid subfunction */
1980 /* Get Interrupt Vector */
1983 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1985 /* Read the address from the IDT into ES:BX */
1986 setES(HIWORD(FarPointer
));
1987 setBX(LOWORD(FarPointer
));
1991 /* SWITCH character - AVAILDEV */
1994 if (getAL() == 0x00)
1997 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1998 * This setting is ignored by MS-DOS 4.0+.
1999 * MS-DOS 5+ always return AL=00h/DL=2Fh.
2000 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
2001 * for more information.
2006 else if (getAL() == 0x01)
2009 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
2010 * This setting is ignored by MS-DOS 5+.
2011 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
2012 * for more information.
2017 else if (getAL() == 0x02)
2020 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
2021 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
2022 * for more information.
2027 else if (getAL() == 0x03)
2030 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
2031 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
2032 * for more information.
2039 /* Invalid subfunction */
2046 /* Create Directory */
2049 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2051 if (CreateDirectoryA(String
, NULL
))
2053 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2057 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2058 setAX(LOWORD(GetLastError()));
2064 /* Remove Directory */
2067 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2069 if (RemoveDirectoryA(String
))
2071 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2075 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2076 setAX(LOWORD(GetLastError()));
2082 /* Set Current Directory */
2085 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2087 if (DosChangeDirectory(String
))
2089 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2093 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2094 setAX(DosLastError
);
2104 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2105 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2110 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2115 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2126 WORD ErrorCode
= DosOpenFile(&FileHandle
,
2127 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2132 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2137 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2147 if (DosCloseHandle(getBX()))
2149 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2153 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2154 setAX(ERROR_INVALID_HANDLE
);
2160 /* Read from File or Device */
2163 WORD Handle
= getBX();
2164 LPBYTE Buffer
= (LPBYTE
)SEG_OFF_TO_PTR(getDS(), getDX());
2165 WORD Count
= getCX();
2167 WORD ErrorCode
= ERROR_SUCCESS
;
2170 if (IsConsoleHandle(DosGetRealHandle(Handle
)))
2172 while (Stack
[STACK_COUNTER
] < Count
)
2174 /* Read a character from the BIOS */
2175 Character
= LOBYTE(BiosGetCharacter());
2177 /* Stop if the BOP needs to be repeated */
2180 // FIXME: Security checks!
2181 DosPrintCharacter(Character
);
2182 Buffer
[Stack
[STACK_COUNTER
]++] = Character
;
2184 if (Character
== '\r')
2186 /* Stop on first carriage return */
2187 DosPrintCharacter('\n');
2192 if (Character
!= '\r')
2194 if (Stack
[STACK_COUNTER
] < Count
) ErrorCode
= ERROR_NOT_READY
;
2195 else BytesRead
= Count
;
2197 else BytesRead
= Stack
[STACK_COUNTER
];
2201 /* Use the file reading function */
2202 ErrorCode
= DosReadFile(Handle
, Buffer
, Count
, &BytesRead
);
2205 if (ErrorCode
== ERROR_SUCCESS
)
2207 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2210 else if (ErrorCode
!= ERROR_NOT_READY
)
2212 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2218 /* Write to File or Device */
2221 WORD BytesWritten
= 0;
2222 WORD ErrorCode
= DosWriteFile(getBX(),
2223 SEG_OFF_TO_PTR(getDS(), getDX()),
2227 if (ErrorCode
== ERROR_SUCCESS
)
2229 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2230 setAX(BytesWritten
);
2234 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2244 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2246 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2248 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2250 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2251 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2253 setAL(FileName
[0] - 'A');
2257 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2258 setAX(GetLastError());
2268 WORD ErrorCode
= DosSeekFile(getBX(),
2269 MAKELONG(getDX(), getCX()),
2273 if (ErrorCode
== ERROR_SUCCESS
)
2275 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2277 /* Return the new offset in DX:AX */
2278 setDX(HIWORD(NewLocation
));
2279 setAX(LOWORD(NewLocation
));
2283 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2290 /* Get/Set File Attributes */
2294 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2296 if (getAL() == 0x00)
2298 /* Get the attributes */
2299 Attributes
= GetFileAttributesA(FileName
);
2301 /* Check if it failed */
2302 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2304 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2305 setAX(GetLastError());
2309 /* Return the attributes that DOS can understand */
2310 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2311 setCX(Attributes
& 0x00FF);
2314 else if (getAL() == 0x01)
2316 /* Try to set the attributes */
2317 if (SetFileAttributesA(FileName
, getCL()))
2319 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2323 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2324 setAX(GetLastError());
2329 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2330 setAX(ERROR_INVALID_FUNCTION
);
2339 if (DosHandleIoctl(getAL(), getBX()))
2341 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2345 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2346 setAX(DosLastError
);
2352 /* Duplicate Handle */
2356 HANDLE Handle
= DosGetRealHandle(getBX());
2358 if (Handle
!= INVALID_HANDLE_VALUE
)
2360 /* The handle is invalid */
2361 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2362 setAX(ERROR_INVALID_HANDLE
);
2366 /* Open a new handle to the same entry */
2367 NewHandle
= DosOpenHandle(Handle
);
2369 if (NewHandle
== INVALID_DOS_HANDLE
)
2371 /* Too many files open */
2372 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2373 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2377 /* Return the result */
2378 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2383 /* Force Duplicate Handle */
2386 if (DosDuplicateHandle(getBX(), getCX()))
2388 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2392 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2393 setAX(ERROR_INVALID_HANDLE
);
2399 /* Get Current Directory */
2402 BYTE DriveNumber
= getDL();
2403 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2405 /* Get the real drive number */
2406 if (DriveNumber
== 0)
2408 DriveNumber
= CurrentDrive
;
2412 /* Decrement DriveNumber since it was 1-based */
2416 if (DriveNumber
<= LastDrive
- 'A')
2419 * Copy the current directory into the target buffer.
2420 * It doesn't contain the drive letter and the backslash.
2422 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2423 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2424 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2428 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2429 setAX(ERROR_INVALID_DRIVE
);
2435 /* Allocate Memory */
2438 WORD MaxAvailable
= 0;
2439 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2443 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2448 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2449 setAX(DosLastError
);
2450 setBX(MaxAvailable
);
2459 if (DosFreeMemory(getES()))
2461 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2465 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2466 setAX(ERROR_ARENA_TRASHED
);
2472 /* Resize Memory Block */
2477 if (DosResizeMemory(getES(), getBX(), &Size
))
2479 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2483 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2484 setAX(DosLastError
);
2494 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2495 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2496 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2497 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
);
2499 if (ErrorCode
== ERROR_SUCCESS
)
2501 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2505 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2512 /* Terminate With Return Code */
2515 DosTerminateProcess(CurrentPsp
, getAL());
2519 /* Get Return Code (ERRORLEVEL) */
2523 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2524 * DosErrorLevel is cleared after being read by this function.
2526 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2527 setAX(DosErrorLevel
);
2528 DosErrorLevel
= 0x0000; // Clear it
2532 /* Find First File */
2535 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2536 SEG_OFF_TO_PTR(getDS(), getDX()),
2540 if (Result
== ERROR_SUCCESS
) Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2541 else Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2546 /* Find Next File */
2549 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2552 if (Result
== ERROR_SUCCESS
) Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2553 else Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2558 /* Internal - Set Current Process ID (Set PSP Address) */
2561 // FIXME: Is it really what it's done ??
2562 CurrentPsp
= getBX();
2566 /* Internal - Get Current Process ID (Get PSP Address) */
2568 /* Get Current PSP Address */
2572 * Undocumented AH=51h is identical to the documented AH=62h.
2573 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2574 * and http://www.ctyme.com/intr/rb-3140.htm
2575 * for more information.
2581 /* Get/Set Memory Management Options */
2584 if (getAL() == 0x00)
2586 /* Get allocation strategy */
2587 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2588 setAX(DosAllocStrategy
);
2590 else if (getAL() == 0x01)
2592 /* Set allocation strategy */
2594 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2595 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2597 /* Can't set both */
2598 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2599 setAX(ERROR_INVALID_PARAMETER
);
2603 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2605 /* Invalid allocation strategy */
2606 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2607 setAX(ERROR_INVALID_PARAMETER
);
2611 DosAllocStrategy
= getBL();
2612 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2614 else if (getAL() == 0x02)
2616 /* Get UMB link state */
2617 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2618 setAL(DosUmbLinked
? 0x01 : 0x00);
2620 else if (getAL() == 0x03)
2622 /* Set UMB link state */
2623 if (getBX()) DosLinkUmb();
2624 else DosUnlinkUmb();
2625 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2629 /* Invalid or unsupported function */
2630 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2631 setAX(ERROR_INVALID_FUNCTION
);
2640 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2643 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2644 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2649 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2651 UNREFERENCED_PARAMETER(Stack
);
2653 /* Stop the VDM task */
2654 ResetEvent(VdmTaskEvent
);
2655 EmulatorUnsimulate();
2658 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2661 * This is the DOS 2+ Fast Console Output Interrupt.
2662 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2663 * for more information.
2667 if (Stack
[STACK_COUNTER
] == 0)
2669 Stack
[STACK_COUNTER
]++;
2671 /* Save AX and BX */
2672 Stack
[STACK_VAR_A
] = getAX();
2673 Stack
[STACK_VAR_B
] = getBX();
2675 /* Rewind the BOP manually, we can't use CF because the interrupt could modify it */
2676 EmulatorExecute(getCS(), getIP() - 4);
2678 /* Call INT 0x10, AH = 0x0E */
2680 setBL(DOS_CHAR_ATTRIBUTE
);
2681 setBH(Bda
->VideoPage
);
2683 EmulatorInterrupt(0x10);
2687 /* Restore AX and BX */
2688 setAX(Stack
[STACK_VAR_A
]);
2689 setBX(Stack
[STACK_VAR_B
]);
2692 /* Save AX and BX */
2693 USHORT AX
= getAX();
2694 USHORT BX
= getBX();
2696 /* Set the parameters (AL = character, already set) */
2697 setBL(DOS_CHAR_ATTRIBUTE
);
2698 setBH(Bda
->VideoPage
);
2700 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2702 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2704 /* Restore AX and BX */
2710 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2712 DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2714 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2717 BOOLEAN
DosKRNLInitialize(VOID
)
2723 CHAR CurrentDirectory
[MAX_PATH
];
2724 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2730 /* Clear the current directory buffer */
2731 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2733 /* Get the current directory */
2734 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2736 // TODO: Use some kind of default path?
2740 /* Convert that to a DOS path */
2741 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2743 // TODO: Use some kind of default path?
2748 CurrentDrive
= DosDirectory
[0] - 'A';
2750 /* Get the directory part of the path */
2751 Path
= strchr(DosDirectory
, '\\');
2754 /* Skip the backslash */
2758 /* Set the directory */
2761 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2764 /* Read CONFIG.SYS */
2765 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2768 while (fgetws(Buffer
, 256, Stream
))
2770 // TODO: Parse the line
2775 /* Initialize the SFT */
2776 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2778 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2779 DosSftRefCount
[i
] = 0;
2782 /* Get handles to standard I/O devices */
2783 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2784 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2785 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);
2787 /* Initialize the reference counts */
2788 DosSftRefCount
[0] = DosSftRefCount
[1] = DosSftRefCount
[2] = 1;
2792 /* Initialize the callback context */
2793 InitializeContext(&DosContext
, 0x0070, 0x0000);
2795 /* Register the DOS 32-bit Interrupts */
2796 RegisterDosInt32(0x20, DosInt20h
);
2797 RegisterDosInt32(0x21, DosInt21h
);
2798 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2799 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2800 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2801 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2802 RegisterDosInt32(0x2F, DosInt2Fh
);