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 *******************************************************************/
17 /* PRIVATE VARIABLES **********************************************************/
19 static WORD CurrentPsp
= SYSTEM_PSP
;
20 static WORD DosLastError
= 0;
21 static DWORD DiskTransferArea
;
22 static BYTE CurrentDrive
;
23 static CHAR LastDrive
= 'E';
24 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
25 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
26 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
27 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
28 static BOOLEAN DosUmbLinked
= FALSE
;
30 /* PRIVATE FUNCTIONS **********************************************************/
32 /* Taken from base/shell/cmd/console.c */
33 static BOOL
IsConsoleHandle(HANDLE hHandle
)
37 /* Check whether the handle may be that of a console... */
38 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
41 * It may be. Perform another test... The idea comes from the
42 * MSDN description of the WriteConsole API:
44 * "WriteConsole fails if it is used with a standard handle
45 * that is redirected to a file. If an application processes
46 * multilingual output that can be redirected, determine whether
47 * the output handle is a console handle (one method is to call
48 * the GetConsoleMode function and check whether it succeeds).
49 * If the handle is a console handle, call WriteConsole. If the
50 * handle is not a console handle, the output is redirected and
51 * you should call WriteFile to perform the I/O."
53 return GetConsoleMode(hHandle
, &dwMode
);
56 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
58 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
60 /* If this is the last block or it's not free, quit */
61 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
65 /* Get a pointer to the next MCB */
66 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
68 /* Check if the next MCB is free */
69 if (NextMcb
->OwnerPsp
== 0)
72 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
73 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
74 NextMcb
->BlockType
= 'I';
78 /* No more adjoining free blocks */
84 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
86 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
90 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
92 /* Calculate the size of the environment block */
95 TotalSize
+= strlen(Ptr
) + 1;
96 Ptr
+= strlen(Ptr
) + 1;
100 /* Add the string buffer size */
101 TotalSize
+= strlen(ProgramName
) + 1;
103 /* Allocate the memory for the environment block */
104 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
105 if (!DestSegment
) return 0;
109 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
112 /* Copy the string */
113 strcpy(DestBuffer
, Ptr
);
115 /* Advance to the next string */
116 DestBuffer
+= strlen(Ptr
);
117 Ptr
+= strlen(Ptr
) + 1;
119 /* Put a zero after the string */
123 /* Set the final zero */
126 /* Copy the program name after the environment block */
127 strcpy(DestBuffer
, ProgramName
);
132 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
134 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
136 /* Just set the owner */
137 Mcb
->OwnerPsp
= NewOwner
;
140 static WORD
DosOpenHandle(HANDLE Handle
)
147 /* The system PSP has no handle table */
148 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
150 /* Get a pointer to the handle table */
151 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
152 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
154 /* Find a free entry in the JFT */
155 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
157 if (HandleTable
[DosHandle
] == 0xFF) break;
160 /* If there are no free entries, fail */
161 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
163 /* Check if the handle is already in the SFT */
164 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
166 /* Check if this is the same handle */
167 if (DosSystemFileTable
[i
] != Handle
) continue;
169 /* Already in the table, reference it */
172 /* Set the JFT entry to that SFT index */
173 HandleTable
[DosHandle
] = i
;
175 /* Return the new handle */
179 /* Add the handle to the SFT */
180 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
182 /* Make sure this is an empty table entry */
183 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
185 /* Initialize the empty table entry */
186 DosSystemFileTable
[i
] = Handle
;
187 DosSftRefCount
[i
] = 1;
189 /* Set the JFT entry to that SFT index */
190 HandleTable
[DosHandle
] = i
;
192 /* Return the new handle */
196 /* The SFT is full */
197 return INVALID_DOS_HANDLE
;
200 static HANDLE
DosGetRealHandle(WORD DosHandle
)
205 /* The system PSP has no handle table */
206 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
208 /* Get a pointer to the handle table */
209 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
210 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
212 /* Make sure the handle is open */
213 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
215 /* Return the Win32 handle */
216 return DosSystemFileTable
[HandleTable
[DosHandle
]];
219 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
225 /* Clear the table first */
226 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
228 /* Check if this is the initial process */
229 if (CurrentPsp
== SYSTEM_PSP
)
231 /* Set up the standard I/O devices */
232 for (i
= 0; i
<= 2; i
++)
234 /* Set the index in the SFT */
235 DestinationTable
[i
] = (BYTE
)i
;
237 /* Increase the reference count */
245 /* Get the parent PSP block and handle table */
246 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
247 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
249 /* Copy the first 20 handles into the new table */
250 for (i
= 0; i
< 20; i
++)
252 DestinationTable
[i
] = SourceTable
[i
];
254 /* Increase the reference count */
255 DosSftRefCount
[SourceTable
[i
]]++;
259 /* PUBLIC FUNCTIONS ***********************************************************/
261 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
263 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
264 PDOS_MCB CurrentMcb
, NextMcb
;
265 BOOLEAN SearchUmb
= FALSE
;
267 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
269 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
271 /* Search UMB first */
272 Segment
= UMB_START_SEGMENT
;
278 /* Get a pointer to the MCB */
279 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
281 /* Make sure it's valid */
282 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
284 DPRINT("The DOS memory arena is corrupted!\n");
285 DosLastError
= ERROR_ARENA_TRASHED
;
289 /* Only check free blocks */
290 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
292 /* Combine this free block with adjoining free blocks */
293 DosCombineFreeBlocks(Segment
);
295 /* Update the maximum block size */
296 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
298 /* Check if this block is big enough */
299 if (CurrentMcb
->Size
< Size
) goto Next
;
301 switch (DosAllocStrategy
& 0x3F)
303 case DOS_ALLOC_FIRST_FIT
:
305 /* For first fit, stop immediately */
310 case DOS_ALLOC_BEST_FIT
:
312 /* For best fit, update the smallest block found so far */
313 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
321 case DOS_ALLOC_LAST_FIT
:
323 /* For last fit, make the current block the result, but keep searching */
330 /* If this was the last MCB in the chain, quit */
331 if (CurrentMcb
->BlockType
== 'Z')
333 /* Check if nothing was found while searching through UMBs */
334 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
336 /* Search low memory */
337 Segment
= FIRST_MCB_SEGMENT
;
344 /* Otherwise, update the segment and continue */
345 Segment
+= CurrentMcb
->Size
+ 1;
350 /* If we didn't find a free block, return 0 */
353 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
354 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
358 /* Get a pointer to the MCB */
359 CurrentMcb
= SEGMENT_TO_MCB(Result
);
361 /* Check if the block is larger than requested */
362 if (CurrentMcb
->Size
> Size
)
364 /* It is, split it into two blocks */
365 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
367 /* Initialize the new MCB structure */
368 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
369 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
370 NextMcb
->OwnerPsp
= 0;
372 /* Update the current block */
373 CurrentMcb
->BlockType
= 'M';
374 CurrentMcb
->Size
= Size
;
377 /* Take ownership of the block */
378 CurrentMcb
->OwnerPsp
= CurrentPsp
;
380 /* Return the segment of the data portion of the block */
384 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
386 BOOLEAN Success
= TRUE
;
387 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
388 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
390 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
394 /* Make sure this is a valid, allocated block */
395 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
398 DosLastError
= ERROR_INVALID_HANDLE
;
402 ReturnSize
= Mcb
->Size
;
404 /* Check if we need to expand or contract the block */
405 if (NewSize
> Mcb
->Size
)
407 /* We can't expand the last block */
408 if (Mcb
->BlockType
!= 'M')
414 /* Get the pointer and segment of the next MCB */
415 NextSegment
= Segment
+ Mcb
->Size
+ 1;
416 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
418 /* Make sure the next segment is free */
419 if (NextMcb
->OwnerPsp
!= 0)
421 DPRINT("Cannot expand memory block: next segment is not free!\n");
422 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
427 /* Combine this free block with adjoining free blocks */
428 DosCombineFreeBlocks(NextSegment
);
430 /* Set the maximum possible size of the block */
431 ReturnSize
+= NextMcb
->Size
+ 1;
433 /* Maximize the current block */
434 Mcb
->Size
= ReturnSize
;
435 Mcb
->BlockType
= NextMcb
->BlockType
;
437 /* Invalidate the next block */
438 NextMcb
->BlockType
= 'I';
440 /* Check if the block is larger than requested */
441 if (Mcb
->Size
> NewSize
)
443 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
447 /* It is, split it into two blocks */
448 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
450 /* Initialize the new MCB structure */
451 NextMcb
->BlockType
= Mcb
->BlockType
;
452 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
453 NextMcb
->OwnerPsp
= 0;
455 /* Update the current block */
456 Mcb
->BlockType
= 'M';
460 else if (NewSize
< Mcb
->Size
)
462 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
466 /* Just split the block */
467 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
468 NextMcb
->BlockType
= Mcb
->BlockType
;
469 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
470 NextMcb
->OwnerPsp
= 0;
473 Mcb
->BlockType
= 'M';
478 /* Check if the operation failed */
481 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
484 /* Return the maximum possible size */
485 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
491 BOOLEAN
DosFreeMemory(WORD BlockData
)
493 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
495 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
497 /* Make sure the MCB is valid */
498 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
500 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
504 /* Mark the block as free */
510 BOOLEAN
DosLinkUmb(VOID
)
512 DWORD Segment
= FIRST_MCB_SEGMENT
;
513 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
515 DPRINT("Linking UMB\n");
517 /* Check if UMBs are already linked */
518 if (DosUmbLinked
) return FALSE
;
520 /* Find the last block */
521 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
523 Segment
+= Mcb
->Size
+ 1;
524 Mcb
= SEGMENT_TO_MCB(Segment
);
527 /* Make sure it's valid */
528 if (Mcb
->BlockType
!= 'Z') return FALSE
;
530 /* Connect the MCB with the UMB chain */
531 Mcb
->BlockType
= 'M';
537 BOOLEAN
DosUnlinkUmb(VOID
)
539 DWORD Segment
= FIRST_MCB_SEGMENT
;
540 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
542 DPRINT("Unlinking UMB\n");
544 /* Check if UMBs are already unlinked */
545 if (!DosUmbLinked
) return FALSE
;
547 /* Find the block preceding the MCB that links it with the UMB chain */
548 while (Segment
<= 0xFFFF)
550 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
552 /* This is the last non-UMB segment */
556 /* Advance to the next MCB */
557 Segment
+= Mcb
->Size
+ 1;
558 Mcb
= SEGMENT_TO_MCB(Segment
);
561 /* Mark the MCB as the last MCB */
562 Mcb
->BlockType
= 'Z';
564 DosUmbLinked
= FALSE
;
568 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
573 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
577 /* Create the file */
578 FileHandle
= CreateFileA(FilePath
,
579 GENERIC_READ
| GENERIC_WRITE
,
580 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
586 if (FileHandle
== INVALID_HANDLE_VALUE
)
588 /* Return the error code */
589 return (WORD
)GetLastError();
592 /* Open the DOS handle */
593 DosHandle
= DosOpenHandle(FileHandle
);
595 if (DosHandle
== INVALID_DOS_HANDLE
)
597 /* Close the handle */
598 CloseHandle(FileHandle
);
600 /* Return the error code */
601 return ERROR_TOO_MANY_OPEN_FILES
;
604 /* It was successful */
606 return ERROR_SUCCESS
;
609 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
612 ACCESS_MASK Access
= 0;
615 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
619 /* Parse the access mode */
620 switch (AccessMode
& 3)
625 Access
= GENERIC_READ
;
632 Access
= GENERIC_WRITE
;
639 Access
= GENERIC_READ
| GENERIC_WRITE
;
646 return ERROR_INVALID_PARAMETER
;
651 FileHandle
= CreateFileA(FilePath
,
653 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
656 FILE_ATTRIBUTE_NORMAL
,
659 if (FileHandle
== INVALID_HANDLE_VALUE
)
661 /* Return the error code */
662 return (WORD
)GetLastError();
665 /* Open the DOS handle */
666 DosHandle
= DosOpenHandle(FileHandle
);
668 if (DosHandle
== INVALID_DOS_HANDLE
)
670 /* Close the handle */
671 CloseHandle(FileHandle
);
673 /* Return the error code */
674 return ERROR_TOO_MANY_OPEN_FILES
;
677 /* It was successful */
679 return ERROR_SUCCESS
;
682 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
684 WORD Result
= ERROR_SUCCESS
;
685 DWORD BytesRead32
= 0;
686 HANDLE Handle
= DosGetRealHandle(FileHandle
);
688 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
690 /* Make sure the handle is valid */
691 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
694 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
696 /* Store the error code */
697 Result
= (WORD
)GetLastError();
700 /* The number of bytes read is always 16-bit */
701 *BytesRead
= LOWORD(BytesRead32
);
703 /* Return the error code */
707 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
709 WORD Result
= ERROR_SUCCESS
;
710 DWORD BytesWritten32
= 0;
711 HANDLE Handle
= DosGetRealHandle(FileHandle
);
714 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
718 /* Make sure the handle is valid */
719 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
721 if (IsConsoleHandle(Handle
))
723 for (i
= 0; i
< Count
; i
++)
725 /* Call the BIOS to print the character */
726 BiosPrintCharacter(((LPBYTE
)Buffer
)[i
], DOS_CHAR_ATTRIBUTE
, Bda
->VideoPage
);
733 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
735 /* Store the error code */
736 Result
= (WORD
)GetLastError();
740 /* The number of bytes written is always 16-bit */
741 *BytesWritten
= LOWORD(BytesWritten32
);
743 /* Return the error code */
747 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
749 WORD Result
= ERROR_SUCCESS
;
751 HANDLE Handle
= DosGetRealHandle(FileHandle
);
753 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
758 /* Make sure the handle is valid */
759 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
761 /* Check if the origin is valid */
762 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
764 return ERROR_INVALID_FUNCTION
;
767 /* Move the file pointer */
768 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
770 /* Check if there's a possibility the operation failed */
771 if (FilePointer
== INVALID_SET_FILE_POINTER
)
773 /* Get the real error code */
774 Result
= (WORD
)GetLastError();
777 if (Result
!= ERROR_SUCCESS
)
779 /* The operation did fail */
783 /* Return the file pointer, if requested */
784 if (NewOffset
) *NewOffset
= FilePointer
;
787 return ERROR_SUCCESS
;
790 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
796 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
800 /* The system PSP has no handle table */
801 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
803 /* Get a pointer to the handle table */
804 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
805 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
807 /* Make sure the old handle is open */
808 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
810 /* Check if the new handle is open */
811 if (HandleTable
[NewHandle
] != 0xFF)
814 DosCloseHandle(NewHandle
);
817 /* Increment the reference count of the SFT entry */
818 SftIndex
= HandleTable
[OldHandle
];
819 DosSftRefCount
[SftIndex
]++;
821 /* Make the new handle point to that SFT entry */
822 HandleTable
[NewHandle
] = SftIndex
;
828 BOOLEAN
DosCloseHandle(WORD DosHandle
)
834 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
836 /* The system PSP has no handle table */
837 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
839 /* Get a pointer to the handle table */
840 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
841 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
843 /* Make sure the handle is open */
844 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
846 /* Decrement the reference count of the SFT entry */
847 SftIndex
= HandleTable
[DosHandle
];
848 DosSftRefCount
[SftIndex
]--;
850 /* Check if the reference count fell to zero */
851 if (!DosSftRefCount
[SftIndex
])
853 /* Close the file, it's no longer needed */
854 CloseHandle(DosSystemFileTable
[SftIndex
]);
856 /* Clear the handle */
857 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
860 /* Clear the entry in the JFT */
861 HandleTable
[DosHandle
] = 0xFF;
866 BOOLEAN
DosChangeDrive(BYTE Drive
)
868 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
870 /* Make sure the drive exists */
871 if (Drive
> (LastDrive
- 'A')) return FALSE
;
873 /* Find the path to the new current directory */
874 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
876 /* Change the current directory of the process */
877 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
879 /* Set the current drive */
880 CurrentDrive
= Drive
;
886 BOOLEAN
DosChangeDirectory(LPSTR Directory
)
892 /* Make sure the directory path is not too long */
893 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
895 DosLastError
= ERROR_PATH_NOT_FOUND
;
899 /* Get the drive number */
900 DriveNumber
= Directory
[0] - 'A';
902 /* Make sure the drive exists */
903 if (DriveNumber
> (LastDrive
- 'A'))
905 DosLastError
= ERROR_PATH_NOT_FOUND
;
909 /* Get the file attributes */
910 Attributes
= GetFileAttributesA(Directory
);
912 /* Make sure the path exists and is a directory */
913 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
914 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
916 DosLastError
= ERROR_PATH_NOT_FOUND
;
920 /* Check if this is the current drive */
921 if (DriveNumber
== CurrentDrive
)
923 /* Change the directory */
924 if (!SetCurrentDirectoryA(Directory
))
926 DosLastError
= LOWORD(GetLastError());
931 /* Get the directory part of the path */
932 Path
= strchr(Directory
, '\\');
935 /* Skip the backslash */
939 /* Set the directory for the drive */
940 if (Path
!= NULL
) strcpy(CurrentDirectories
[DriveNumber
], Path
);
941 else strcpy(CurrentDirectories
[DriveNumber
], "");
947 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
949 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
950 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
952 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
954 /* Set the exit interrupt */
955 PspBlock
->Exit
[0] = 0xCD; // int 0x20
956 PspBlock
->Exit
[1] = 0x20;
958 /* Set the number of the last paragraph */
959 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
961 /* Save the interrupt vectors */
962 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
963 PspBlock
->BreakAddress
= IntVecTable
[0x23];
964 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
966 /* Set the parent PSP */
967 PspBlock
->ParentPsp
= CurrentPsp
;
969 /* Copy the parent handle table */
970 DosCopyHandleTable(PspBlock
->HandleTable
);
972 /* Set the environment block */
973 PspBlock
->EnvBlock
= Environment
;
975 /* Set the handle table pointers to the internal handle table */
976 PspBlock
->HandleTableSize
= 20;
977 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
979 /* Set the DOS version */
980 PspBlock
->DosVersion
= DOS_VERSION
;
982 /* Set the far call opcodes */
983 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
984 PspBlock
->FarCall
[1] = 0x21;
985 PspBlock
->FarCall
[2] = 0xCB; // retf
987 /* Set the command line */
988 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
989 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
990 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
993 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
995 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
996 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
997 LPBYTE Address
= NULL
;
998 LPSTR ProgramFilePath
, Parameters
[256];
999 CHAR CommandLineCopy
[DOS_CMDLINE_LENGTH
];
1003 DWORD i
, FileSize
, ExeSize
;
1004 PIMAGE_DOS_HEADER Header
;
1005 PDWORD RelocationTable
;
1008 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
1012 /* Save a copy of the command line */
1013 strcpy(CommandLineCopy
, CommandLine
);
1015 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
1017 /* Get the file name of the executable */
1018 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
1020 /* Load the parameters in the local array */
1021 while ((ParamCount
< sizeof(Parameters
)/sizeof(Parameters
[0]))
1022 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
1027 /* Open a handle to the executable */
1028 FileHandle
= CreateFileA(ProgramFilePath
,
1033 FILE_ATTRIBUTE_NORMAL
,
1035 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
1037 /* Get the file size */
1038 FileSize
= GetFileSize(FileHandle
, NULL
);
1040 /* Create a mapping object for the file */
1041 FileMapping
= CreateFileMapping(FileHandle
,
1047 if (FileMapping
== NULL
) goto Cleanup
;
1049 /* Map the file into memory */
1050 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1051 if (Address
== NULL
) goto Cleanup
;
1053 /* Did we get an environment segment? */
1056 /* Set a flag to know if the environment block was allocated here */
1057 AllocatedEnvBlock
= TRUE
;
1059 /* No, copy the one from the parent */
1060 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
1061 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
1066 /* Check if this is an EXE file or a COM file */
1067 if (Address
[0] == 'M' && Address
[1] == 'Z')
1071 /* Get the MZ header */
1072 Header
= (PIMAGE_DOS_HEADER
)Address
;
1074 /* Get the base size of the file, in paragraphs (rounded up) */
1075 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1077 /* Add the PSP size, in paragraphs */
1078 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1080 /* Add the maximum size that should be allocated */
1081 ExeSize
+= Header
->e_maxalloc
;
1083 /* Make sure it does not pass 0xFFFF */
1084 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1086 /* Reduce the size one by one until the allocation is successful */
1087 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1089 /* Try to allocate that much memory */
1090 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1091 if (Segment
!= 0) break;
1094 /* Check if at least the lowest allocation was successful */
1095 if (Segment
== 0) goto Cleanup
;
1097 /* Initialize the PSP */
1098 DosInitializePsp(Segment
,
1103 /* The process owns its own memory */
1104 DosChangeMemoryOwner(Segment
, Segment
);
1105 DosChangeMemoryOwner(EnvBlock
, Segment
);
1107 /* Copy the program to Segment:0100 */
1108 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
1109 + TO_LINEAR(Segment
, 0x100)),
1110 Address
+ (Header
->e_cparhdr
<< 4),
1111 min(FileSize
- (Header
->e_cparhdr
<< 4),
1112 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1114 /* Get the relocation table */
1115 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1117 /* Perform relocations */
1118 for (i
= 0; i
< Header
->e_crlc
; i
++)
1120 /* Get a pointer to the word that needs to be patched */
1121 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
1122 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
1123 0x100 + LOWORD(RelocationTable
[i
])));
1125 /* Add the number of the EXE segment to it */
1126 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1129 /* Set the initial segment registers */
1130 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1131 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1133 /* Set the stack to the location from the header */
1134 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1138 CurrentPsp
= Segment
;
1139 DiskTransferArea
= MAKELONG(0x80, Segment
);
1140 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1149 /* Find the maximum amount of memory that can be allocated */
1150 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1152 /* Make sure it's enough for the whole program and the PSP */
1153 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1155 /* Allocate all of it */
1156 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1157 if (Segment
== 0) goto Cleanup
;
1159 /* The process owns its own memory */
1160 DosChangeMemoryOwner(Segment
, Segment
);
1161 DosChangeMemoryOwner(EnvBlock
, Segment
);
1163 /* Copy the program to Segment:0100 */
1164 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
1165 + TO_LINEAR(Segment
, 0x100)),
1169 /* Initialize the PSP */
1170 DosInitializePsp(Segment
,
1172 (WORD
)((FileSize
+ sizeof(DOS_PSP
)) >> 4),
1175 /* Set the initial segment registers */
1176 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1177 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1179 /* Set the stack to the last word of the segment */
1180 EmulatorSetStack(Segment
, 0xFFFE);
1183 CurrentPsp
= Segment
;
1184 DiskTransferArea
= MAKELONG(0x80, Segment
);
1185 EmulatorExecute(Segment
, 0x100);
1193 /* It was not successful, cleanup the DOS memory */
1194 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
1195 if (Segment
) DosFreeMemory(Segment
);
1199 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1201 /* Close the file mapping object */
1202 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1204 /* Close the file handle */
1205 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1210 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1213 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1214 PDOS_MCB CurrentMcb
;
1215 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1216 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1218 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1222 /* Check if this PSP is it's own parent */
1223 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1225 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1227 /* Close the handle */
1231 /* Free the memory used by the process */
1234 /* Get a pointer to the MCB */
1235 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1237 /* Make sure the MCB is valid */
1238 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1240 /* If this block was allocated by the process, free it */
1241 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1243 /* If this was the last block, quit */
1244 if (CurrentMcb
->BlockType
== 'Z') break;
1246 /* Update the segment and continue */
1247 McbSegment
+= CurrentMcb
->Size
+ 1;
1251 /* Restore the interrupt vectors */
1252 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1253 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1254 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1256 /* Update the current PSP */
1257 if (Psp
== CurrentPsp
)
1259 CurrentPsp
= PspBlock
->ParentPsp
;
1260 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1263 /* Return control to the parent process */
1264 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1265 LOWORD(PspBlock
->TerminateAddress
));
1268 CHAR
DosReadCharacter(VOID
)
1270 CHAR Character
= '\0';
1273 if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE
)))
1276 Character
= LOBYTE(BiosGetCharacter());
1280 /* Use the file reading function */
1281 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1287 BOOLEAN
DosCheckInput(VOID
)
1289 HANDLE Handle
= DosGetRealHandle(DOS_INPUT_HANDLE
);
1291 if (IsConsoleHandle(Handle
))
1294 return (BiosPeekCharacter() != 0xFFFF);
1299 DWORD FileSize
= GetFileSize(Handle
, &FileSizeHigh
);
1300 LONG LocationHigh
= 0;
1301 DWORD Location
= SetFilePointer(Handle
, 0, &LocationHigh
, FILE_CURRENT
);
1303 return ((Location
!= FileSize
) || (LocationHigh
!= FileSizeHigh
));
1307 VOID
DosPrintCharacter(CHAR Character
)
1311 /* Use the file writing function */
1312 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1315 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1317 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1319 if (Handle
== INVALID_HANDLE_VALUE
)
1322 DosLastError
= ERROR_FILE_NOT_FOUND
;
1326 switch (ControlCode
)
1328 /* Get Device Information */
1333 if (Handle
== DosSystemFileTable
[0])
1338 else if (Handle
== DosSystemFileTable
[1])
1340 /* Console output */
1344 /* It is a character device */
1347 /* Return the device information word */
1348 EmulatorSetRegister(EMULATOR_REG_DX
, InfoWord
);
1353 /* Unsupported control code */
1356 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1358 DosLastError
= ERROR_INVALID_PARAMETER
;
1364 VOID
DosInt20h(LPWORD Stack
)
1366 /* This is the exit interrupt */
1367 DosTerminateProcess(Stack
[STACK_CS
], 0);
1370 VOID
DosInt21h(LPWORD Stack
)
1373 SYSTEMTIME SystemTime
;
1375 PDOS_INPUT_BUFFER InputBuffer
;
1376 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1377 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1378 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1379 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
1380 WORD DataSegment
= (WORD
)EmulatorGetRegister(EMULATOR_REG_DS
);
1381 WORD ExtSegment
= (WORD
)EmulatorGetRegister(EMULATOR_REG_ES
);
1383 /* Check the value in the AH register */
1384 switch (HIBYTE(Eax
))
1386 /* Terminate Program */
1389 DosTerminateProcess(Stack
[STACK_CS
], 0);
1393 /* Read Character And Echo */
1396 Character
= DosReadCharacter();
1397 DosPrintCharacter(Character
);
1399 if (!EmulatorGetFlag(EMULATOR_FLAG_CF
))
1401 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
1407 /* Print Character */
1410 DosPrintCharacter(LOBYTE(Edx
));
1414 /* Direct Console I/O */
1417 BYTE Character
= LOBYTE(Edx
);
1419 if (Character
!= 0xFF)
1422 DosPrintCharacter(Character
);
1429 if (DosCheckInput())
1431 Eax
|= DosReadCharacter();
1432 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1436 /* No character available */
1437 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1444 /* Read Character Without Echo */
1448 Character
= DosReadCharacter();
1450 if (!EmulatorGetFlag(EMULATOR_FLAG_CF
))
1452 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
1461 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1462 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1464 while ((*String
) != '$')
1466 DosPrintCharacter(*String
);
1473 /* Read Buffered Input */
1476 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
1477 + TO_LINEAR(DataSegment
,
1480 while (Stack
[STACK_COUNTER
] < InputBuffer
->MaxLength
)
1482 /* Try to read a character */
1483 Character
= DosReadCharacter();
1485 /* If it's not ready yet, let the BOP repeat */
1486 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1488 /* Echo the character and append it to the buffer */
1489 DosPrintCharacter(Character
);
1490 InputBuffer
->Buffer
[Stack
[STACK_COUNTER
]] = Character
;
1492 if (Character
== '\r') break;
1493 Stack
[STACK_COUNTER
]++;
1496 /* Update the length */
1497 InputBuffer
->Length
= Stack
[STACK_COUNTER
];
1502 /* Get STDIN Status */
1505 if (DosCheckInput()) Eax
|= 0xFF;
1506 else Eax
&= 0xFFFFFF00;
1508 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
1513 /* Set Default Drive */
1516 DosChangeDrive(LOBYTE(Edx
));
1517 EmulatorSetRegister(EMULATOR_REG_AX
,
1518 (Eax
& 0xFFFFFF00) | (LastDrive
- 'A' + 1));
1523 /* Get Default Drive */
1526 EmulatorSetRegister(EMULATOR_REG_AX
,
1527 (Eax
& 0xFFFFFF00) | CurrentDrive
);
1532 /* Set Disk Transfer Area */
1535 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
1539 /* Set Interrupt Vector */
1542 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
1544 /* Write the new far pointer to the IDT */
1545 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
1550 /* Get system date */
1553 GetLocalTime(&SystemTime
);
1554 EmulatorSetRegister(EMULATOR_REG_CX
,
1555 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
1556 EmulatorSetRegister(EMULATOR_REG_DX
,
1558 | (SystemTime
.wMonth
<< 8)
1560 EmulatorSetRegister(EMULATOR_REG_AX
,
1561 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1565 /* Set system date */
1568 GetLocalTime(&SystemTime
);
1569 SystemTime
.wYear
= LOWORD(Ecx
);
1570 SystemTime
.wMonth
= HIBYTE(Edx
);
1571 SystemTime
.wDay
= LOBYTE(Edx
);
1573 if (SetLocalTime(&SystemTime
))
1575 /* Return success */
1576 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1580 /* Return failure */
1581 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1587 /* Get system time */
1590 GetLocalTime(&SystemTime
);
1591 EmulatorSetRegister(EMULATOR_REG_CX
,
1593 | (SystemTime
.wHour
<< 8)
1594 | SystemTime
.wMinute
);
1595 EmulatorSetRegister(EMULATOR_REG_DX
,
1597 | (SystemTime
.wSecond
<< 8)
1598 | (SystemTime
.wMilliseconds
/ 10));
1602 /* Set system time */
1605 GetLocalTime(&SystemTime
);
1606 SystemTime
.wHour
= HIBYTE(Ecx
);
1607 SystemTime
.wMinute
= LOBYTE(Ecx
);
1608 SystemTime
.wSecond
= HIBYTE(Edx
);
1609 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1611 if (SetLocalTime(&SystemTime
))
1613 /* Return success */
1614 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1618 /* Return failure */
1619 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1625 /* Get Disk Transfer Area */
1628 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1629 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1634 /* Get DOS Version */
1637 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1639 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1643 /* Get Interrupt Vector */
1646 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1648 /* Read the address from the IDT into ES:BX */
1649 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1650 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1655 /* Create Directory */
1658 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1659 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1661 if (CreateDirectoryA(String
, NULL
))
1663 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1667 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1668 EmulatorSetRegister(EMULATOR_REG_AX
,
1669 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1675 /* Remove Directory */
1678 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1679 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1681 if (RemoveDirectoryA(String
))
1683 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1687 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1688 EmulatorSetRegister(EMULATOR_REG_AX
,
1689 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1696 /* Set Current Directory */
1699 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1700 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1702 if (DosChangeDirectory(String
))
1704 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1708 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1709 EmulatorSetRegister(EMULATOR_REG_AX
,
1710 (Eax
& 0xFFFF0000) | DosLastError
);
1720 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1721 (LPCSTR
)(ULONG_PTR
)BaseAddress
1722 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1728 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1730 /* Return the handle in AX */
1731 EmulatorSetRegister(EMULATOR_REG_AX
,
1732 (Eax
& 0xFFFF0000) | FileHandle
);
1737 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1739 /* Return the error code in AX */
1740 EmulatorSetRegister(EMULATOR_REG_AX
,
1741 (Eax
& 0xFFFF0000) | ErrorCode
);
1751 WORD ErrorCode
= DosOpenFile(&FileHandle
,
1752 (LPCSTR
)(ULONG_PTR
)BaseAddress
1753 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1759 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1761 /* Return the handle in AX */
1762 EmulatorSetRegister(EMULATOR_REG_AX
,
1763 (Eax
& 0xFFFF0000) | FileHandle
);
1768 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1770 /* Return the error code in AX */
1771 EmulatorSetRegister(EMULATOR_REG_AX
,
1772 (Eax
& 0xFFFF0000) | ErrorCode
);
1781 if (DosCloseHandle(LOWORD(Ebx
)))
1784 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1789 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1791 /* Return the error code in AX */
1792 EmulatorSetRegister(EMULATOR_REG_AX
,
1793 (Eax
& 0xFFFF0000) | ERROR_INVALID_HANDLE
);
1802 WORD Handle
= LOWORD(Ebx
);
1803 LPBYTE Buffer
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1804 WORD Count
= LOWORD(Ecx
);
1806 WORD ErrorCode
= ERROR_SUCCESS
;
1808 if (IsConsoleHandle(DosGetRealHandle(Handle
)))
1810 while (Stack
[STACK_COUNTER
] < Count
)
1812 /* Read a character from the BIOS */
1813 Buffer
[Stack
[STACK_COUNTER
]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
1815 /* Stop if the BOP needs to be repeated */
1816 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1818 /* Increment the counter */
1819 Stack
[STACK_COUNTER
]++;
1822 if (Stack
[STACK_COUNTER
] < Count
) ErrorCode
= ERROR_NOT_READY
;
1823 else BytesRead
= Count
;
1827 /* Use the file reading function */
1828 ErrorCode
= DosReadFile(Handle
, Buffer
, Count
, &BytesRead
);
1834 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1836 /* Return the number of bytes read in AX */
1837 EmulatorSetRegister(EMULATOR_REG_AX
,
1838 (Eax
& 0xFFFF0000) | BytesRead
);
1840 else if (ErrorCode
!= ERROR_NOT_READY
)
1843 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1845 /* Return the error code in AX */
1846 EmulatorSetRegister(EMULATOR_REG_AX
,
1847 (Eax
& 0xFFFF0000) | ErrorCode
);
1855 WORD BytesWritten
= 0;
1856 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1857 (LPVOID
)((ULONG_PTR
)BaseAddress
1858 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1865 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1867 /* Return the number of bytes written in AX */
1868 EmulatorSetRegister(EMULATOR_REG_AX
,
1869 (Eax
& 0xFFFF0000) | BytesWritten
);
1874 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1876 /* Return the error code in AX */
1877 EmulatorSetRegister(EMULATOR_REG_AX
,
1878 (Eax
& 0xFFFF0000) | ErrorCode
);
1887 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1889 /* Call the API function */
1890 if (DeleteFileA(FileName
)) Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1893 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1894 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1904 WORD ErrorCode
= DosSeekFile(LOWORD(Ebx
),
1905 MAKELONG(LOWORD(Edx
), LOWORD(Ecx
)),
1912 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1914 /* Return the new offset in DX:AX */
1915 EmulatorSetRegister(EMULATOR_REG_DX
,
1916 (Edx
& 0xFFFF0000) | HIWORD(NewLocation
));
1917 EmulatorSetRegister(EMULATOR_REG_AX
,
1918 (Eax
& 0xFFFF0000) | LOWORD(NewLocation
));
1923 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1925 /* Return the error code in AX */
1926 EmulatorSetRegister(EMULATOR_REG_AX
,
1927 (Eax
& 0xFFFF0000) | ErrorCode
);
1933 /* Get/Set File Attributes */
1937 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1939 if (LOBYTE(Eax
) == 0x00)
1941 /* Get the attributes */
1942 Attributes
= GetFileAttributesA(FileName
);
1944 /* Check if it failed */
1945 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1947 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1948 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1953 /* Return the attributes that DOS can understand */
1954 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1955 EmulatorSetRegister(EMULATOR_REG_CX
,
1956 (Ecx
& 0xFFFFFF00) | LOBYTE(Attributes
));
1958 else if (LOBYTE(Eax
) == 0x01)
1960 /* Try to set the attributes */
1961 if (SetFileAttributesA(FileName
, LOBYTE(Ecx
)))
1963 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1967 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1968 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1973 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1974 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
1983 if (DosHandleIoctl(LOBYTE(Eax
), LOWORD(Ebx
)))
1985 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1990 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1996 /* Duplicate Handle */
2000 HANDLE Handle
= DosGetRealHandle(LOWORD(Ebx
));
2002 if (Handle
!= INVALID_HANDLE_VALUE
)
2004 /* The handle is invalid */
2005 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2006 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
2011 /* Open a new handle to the same entry */
2012 NewHandle
= DosOpenHandle(Handle
);
2014 if (NewHandle
== INVALID_DOS_HANDLE
)
2016 /* Too many files open */
2017 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2018 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_TOO_MANY_OPEN_FILES
);
2023 /* Return the result */
2024 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2025 EmulatorSetRegister(EMULATOR_REG_AX
, NewHandle
);
2030 /* Force Duplicate Handle */
2033 if (DosDuplicateHandle(LOWORD(Ebx
), LOWORD(Ecx
)))
2035 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2039 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2040 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
2046 /* Allocate Memory */
2049 WORD MaxAvailable
= 0;
2050 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
2054 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
2055 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2059 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
2060 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
2061 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2070 if (DosFreeMemory(ExtSegment
))
2072 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2076 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_ARENA_TRASHED
);
2077 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2083 /* Resize Memory Block */
2088 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
2090 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2094 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
2095 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2096 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
2102 /* Terminate With Return Code */
2105 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
2109 /* Get Current Process */
2112 EmulatorSetRegister(EMULATOR_REG_BX
, CurrentPsp
);
2117 /* Get/Set Memory Management Options */
2120 if (LOBYTE(Eax
) == 0x00)
2122 /* Get allocation strategy */
2124 EmulatorSetRegister(EMULATOR_REG_AX
, DosAllocStrategy
);
2125 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2127 else if (LOBYTE(Eax
) == 0x01)
2129 /* Set allocation strategy */
2131 if ((LOBYTE(Ebx
) & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2132 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2134 /* Can't set both */
2135 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
2136 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2140 if ((LOBYTE(Ebx
) & 0x3F) > DOS_ALLOC_LAST_FIT
)
2142 /* Invalid allocation strategy */
2143 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
2144 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2148 DosAllocStrategy
= LOBYTE(Ebx
);
2149 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2151 else if (LOBYTE(Eax
) == 0x02)
2153 /* Get UMB link state */
2156 if (DosUmbLinked
) Eax
|= 1;
2157 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
2158 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2160 else if (LOBYTE(Eax
) == 0x03)
2162 /* Set UMB link state */
2164 if (Ebx
) DosLinkUmb();
2165 else DosUnlinkUmb();
2166 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2170 /* Invalid or unsupported function */
2172 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2173 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
2182 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
2183 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2188 VOID
DosBreakInterrupt(LPWORD Stack
)
2190 UNREFERENCED_PARAMETER(Stack
);
2195 BOOLEAN
DosInitialize(VOID
)
2198 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
2201 LPWSTR SourcePtr
, Environment
;
2203 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
2205 CHAR CurrentDirectory
[MAX_PATH
];
2206 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2209 /* Initialize the MCB */
2210 Mcb
->BlockType
= 'Z';
2211 Mcb
->Size
= USER_MEMORY_SIZE
;
2214 /* Initialize the link MCB to the UMB area */
2215 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
2216 Mcb
->BlockType
= 'M';
2217 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
2218 Mcb
->OwnerPsp
= SYSTEM_PSP
;
2220 /* Initialize the UMB area */
2221 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
2222 Mcb
->BlockType
= 'Z';
2223 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
2226 /* Get the environment strings */
2227 SourcePtr
= Environment
= GetEnvironmentStringsW();
2228 if (Environment
== NULL
) return FALSE
;
2230 /* Fill the DOS system environment block */
2233 /* Get the size of the ASCII string */
2234 AsciiSize
= WideCharToMultiByte(CP_ACP
,
2243 /* Allocate memory for the ASCII string */
2244 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
2245 if (AsciiString
== NULL
)
2247 FreeEnvironmentStringsW(Environment
);
2251 /* Convert to ASCII */
2252 WideCharToMultiByte(CP_ACP
,
2261 /* Copy the string into DOS memory */
2262 strcpy(DestPtr
, AsciiString
);
2264 /* Move to the next string */
2265 SourcePtr
+= wcslen(SourcePtr
) + 1;
2266 DestPtr
+= strlen(AsciiString
);
2269 /* Free the memory */
2270 HeapFree(GetProcessHeap(), 0, AsciiString
);
2274 /* Free the memory allocated for environment strings */
2275 FreeEnvironmentStringsW(Environment
);
2277 /* Clear the current directory buffer */
2278 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2280 /* Get the current directory */
2281 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2283 // TODO: Use some kind of default path?
2287 /* Convert that to a DOS path */
2288 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2290 // TODO: Use some kind of default path?
2295 CurrentDrive
= DosDirectory
[0] - 'A';
2298 Path
= strchr(DosDirectory
, '\\');
2301 /* Skip the backslash */
2305 /* Set the directory */
2306 if (Path
!= NULL
) strcpy(CurrentDirectories
[CurrentDrive
], Path
);
2308 /* Read CONFIG.SYS */
2309 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2312 while (fgetws(Buffer
, 256, Stream
))
2314 // TODO: Parse the line
2319 /* Initialize the SFT */
2320 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2322 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2323 DosSftRefCount
[i
] = 0;
2326 /* Get handles to standard I/O devices */
2327 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2328 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2329 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);