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 #include "registers.h"
19 /* PRIVATE VARIABLES **********************************************************/
21 static WORD CurrentPsp
= SYSTEM_PSP
;
22 static WORD DosLastError
= 0;
23 static DWORD DiskTransferArea
;
24 static BYTE CurrentDrive
;
25 static CHAR LastDrive
= 'E';
26 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
27 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
28 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
29 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
30 static BOOLEAN DosUmbLinked
= FALSE
;
31 static BYTE DosErrorLevel
= 0;
33 /* PRIVATE FUNCTIONS **********************************************************/
35 /* Taken from base/shell/cmd/console.c */
36 static BOOL
IsConsoleHandle(HANDLE hHandle
)
40 /* Check whether the handle may be that of a console... */
41 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
44 * It may be. Perform another test... The idea comes from the
45 * MSDN description of the WriteConsole API:
47 * "WriteConsole fails if it is used with a standard handle
48 * that is redirected to a file. If an application processes
49 * multilingual output that can be redirected, determine whether
50 * the output handle is a console handle (one method is to call
51 * the GetConsoleMode function and check whether it succeeds).
52 * If the handle is a console handle, call WriteConsole. If the
53 * handle is not a console handle, the output is redirected and
54 * you should call WriteFile to perform the I/O."
56 return GetConsoleMode(hHandle
, &dwMode
);
59 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
61 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
63 /* If this is the last block or it's not free, quit */
64 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
68 /* Get a pointer to the next MCB */
69 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
71 /* Check if the next MCB is free */
72 if (NextMcb
->OwnerPsp
== 0)
75 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
76 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
77 NextMcb
->BlockType
= 'I';
81 /* No more adjoining free blocks */
87 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
89 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
93 Ptr
= SourceBuffer
= (PCHAR
)SEG_OFF_TO_PTR(SourceSegment
, 0);
95 /* Calculate the size of the environment block */
98 TotalSize
+= strlen(Ptr
) + 1;
99 Ptr
+= strlen(Ptr
) + 1;
103 /* Add the string buffer size */
104 TotalSize
+= strlen(ProgramName
) + 1;
106 /* Allocate the memory for the environment block */
107 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
108 if (!DestSegment
) return 0;
112 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
115 /* Copy the string */
116 strcpy(DestBuffer
, Ptr
);
118 /* Advance to the next string */
119 DestBuffer
+= strlen(Ptr
);
120 Ptr
+= strlen(Ptr
) + 1;
122 /* Put a zero after the string */
126 /* Set the final zero */
129 /* Copy the program name after the environment block */
130 strcpy(DestBuffer
, ProgramName
);
135 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
137 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
139 /* Just set the owner */
140 Mcb
->OwnerPsp
= NewOwner
;
143 static WORD
DosOpenHandle(HANDLE Handle
)
150 /* The system PSP has no handle table */
151 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
153 /* Get a pointer to the handle table */
154 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
155 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
157 /* Find a free entry in the JFT */
158 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
160 if (HandleTable
[DosHandle
] == 0xFF) break;
163 /* If there are no free entries, fail */
164 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
166 /* Check if the handle is already in the SFT */
167 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
169 /* Check if this is the same handle */
170 if (DosSystemFileTable
[i
] != Handle
) continue;
172 /* Already in the table, reference it */
175 /* Set the JFT entry to that SFT index */
176 HandleTable
[DosHandle
] = i
;
178 /* Return the new handle */
182 /* Add the handle to the SFT */
183 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
185 /* Make sure this is an empty table entry */
186 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
188 /* Initialize the empty table entry */
189 DosSystemFileTable
[i
] = Handle
;
190 DosSftRefCount
[i
] = 1;
192 /* Set the JFT entry to that SFT index */
193 HandleTable
[DosHandle
] = i
;
195 /* Return the new handle */
199 /* The SFT is full */
200 return INVALID_DOS_HANDLE
;
203 static HANDLE
DosGetRealHandle(WORD DosHandle
)
208 /* The system PSP has no handle table */
209 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
211 /* Get a pointer to the handle table */
212 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
213 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
215 /* Make sure the handle is open */
216 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
218 /* Return the Win32 handle */
219 return DosSystemFileTable
[HandleTable
[DosHandle
]];
222 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
228 /* Clear the table first */
229 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
231 /* Check if this is the initial process */
232 if (CurrentPsp
== SYSTEM_PSP
)
234 /* Set up the standard I/O devices */
235 for (i
= 0; i
<= 2; i
++)
237 /* Set the index in the SFT */
238 DestinationTable
[i
] = (BYTE
)i
;
240 /* Increase the reference count */
248 /* Get the parent PSP block and handle table */
249 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
250 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
252 /* Copy the first 20 handles into the new table */
253 for (i
= 0; i
< 20; i
++)
255 DestinationTable
[i
] = SourceTable
[i
];
257 /* Increase the reference count */
258 DosSftRefCount
[SourceTable
[i
]]++;
262 /* PUBLIC FUNCTIONS ***********************************************************/
264 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
266 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
267 PDOS_MCB CurrentMcb
, NextMcb
;
268 BOOLEAN SearchUmb
= FALSE
;
270 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
272 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
274 /* Search UMB first */
275 Segment
= UMB_START_SEGMENT
;
281 /* Get a pointer to the MCB */
282 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
284 /* Make sure it's valid */
285 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
287 DPRINT("The DOS memory arena is corrupted!\n");
288 DosLastError
= ERROR_ARENA_TRASHED
;
292 /* Only check free blocks */
293 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
295 /* Combine this free block with adjoining free blocks */
296 DosCombineFreeBlocks(Segment
);
298 /* Update the maximum block size */
299 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
301 /* Check if this block is big enough */
302 if (CurrentMcb
->Size
< Size
) goto Next
;
304 switch (DosAllocStrategy
& 0x3F)
306 case DOS_ALLOC_FIRST_FIT
:
308 /* For first fit, stop immediately */
313 case DOS_ALLOC_BEST_FIT
:
315 /* For best fit, update the smallest block found so far */
316 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
324 case DOS_ALLOC_LAST_FIT
:
326 /* For last fit, make the current block the result, but keep searching */
333 /* If this was the last MCB in the chain, quit */
334 if (CurrentMcb
->BlockType
== 'Z')
336 /* Check if nothing was found while searching through UMBs */
337 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
339 /* Search low memory */
340 Segment
= FIRST_MCB_SEGMENT
;
347 /* Otherwise, update the segment and continue */
348 Segment
+= CurrentMcb
->Size
+ 1;
353 /* If we didn't find a free block, return 0 */
356 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
357 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
361 /* Get a pointer to the MCB */
362 CurrentMcb
= SEGMENT_TO_MCB(Result
);
364 /* Check if the block is larger than requested */
365 if (CurrentMcb
->Size
> Size
)
367 /* It is, split it into two blocks */
368 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
370 /* Initialize the new MCB structure */
371 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
372 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
373 NextMcb
->OwnerPsp
= 0;
375 /* Update the current block */
376 CurrentMcb
->BlockType
= 'M';
377 CurrentMcb
->Size
= Size
;
380 /* Take ownership of the block */
381 CurrentMcb
->OwnerPsp
= CurrentPsp
;
383 /* Return the segment of the data portion of the block */
387 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
389 BOOLEAN Success
= TRUE
;
390 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
391 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
393 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
397 /* Make sure this is a valid, allocated block */
398 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
401 DosLastError
= ERROR_INVALID_HANDLE
;
405 ReturnSize
= Mcb
->Size
;
407 /* Check if we need to expand or contract the block */
408 if (NewSize
> Mcb
->Size
)
410 /* We can't expand the last block */
411 if (Mcb
->BlockType
!= 'M')
417 /* Get the pointer and segment of the next MCB */
418 NextSegment
= Segment
+ Mcb
->Size
+ 1;
419 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
421 /* Make sure the next segment is free */
422 if (NextMcb
->OwnerPsp
!= 0)
424 DPRINT("Cannot expand memory block: next segment is not free!\n");
425 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
430 /* Combine this free block with adjoining free blocks */
431 DosCombineFreeBlocks(NextSegment
);
433 /* Set the maximum possible size of the block */
434 ReturnSize
+= NextMcb
->Size
+ 1;
436 /* Maximize the current block */
437 Mcb
->Size
= ReturnSize
;
438 Mcb
->BlockType
= NextMcb
->BlockType
;
440 /* Invalidate the next block */
441 NextMcb
->BlockType
= 'I';
443 /* Check if the block is larger than requested */
444 if (Mcb
->Size
> NewSize
)
446 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
450 /* It is, split it into two blocks */
451 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
453 /* Initialize the new MCB structure */
454 NextMcb
->BlockType
= Mcb
->BlockType
;
455 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
456 NextMcb
->OwnerPsp
= 0;
458 /* Update the current block */
459 Mcb
->BlockType
= 'M';
463 else if (NewSize
< Mcb
->Size
)
465 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
469 /* Just split the block */
470 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
471 NextMcb
->BlockType
= Mcb
->BlockType
;
472 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
473 NextMcb
->OwnerPsp
= 0;
476 Mcb
->BlockType
= 'M';
481 /* Check if the operation failed */
484 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
487 /* Return the maximum possible size */
488 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
494 BOOLEAN
DosFreeMemory(WORD BlockData
)
496 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
498 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
500 /* Make sure the MCB is valid */
501 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
503 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
507 /* Mark the block as free */
513 BOOLEAN
DosLinkUmb(VOID
)
515 DWORD Segment
= FIRST_MCB_SEGMENT
;
516 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
518 DPRINT("Linking UMB\n");
520 /* Check if UMBs are already linked */
521 if (DosUmbLinked
) return FALSE
;
523 /* Find the last block */
524 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
526 Segment
+= Mcb
->Size
+ 1;
527 Mcb
= SEGMENT_TO_MCB(Segment
);
530 /* Make sure it's valid */
531 if (Mcb
->BlockType
!= 'Z') return FALSE
;
533 /* Connect the MCB with the UMB chain */
534 Mcb
->BlockType
= 'M';
540 BOOLEAN
DosUnlinkUmb(VOID
)
542 DWORD Segment
= FIRST_MCB_SEGMENT
;
543 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
545 DPRINT("Unlinking UMB\n");
547 /* Check if UMBs are already unlinked */
548 if (!DosUmbLinked
) return FALSE
;
550 /* Find the block preceding the MCB that links it with the UMB chain */
551 while (Segment
<= 0xFFFF)
553 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
555 /* This is the last non-UMB segment */
559 /* Advance to the next MCB */
560 Segment
+= Mcb
->Size
+ 1;
561 Mcb
= SEGMENT_TO_MCB(Segment
);
564 /* Mark the MCB as the last MCB */
565 Mcb
->BlockType
= 'Z';
567 DosUmbLinked
= FALSE
;
571 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
576 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
580 /* Create the file */
581 FileHandle
= CreateFileA(FilePath
,
582 GENERIC_READ
| GENERIC_WRITE
,
583 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
589 if (FileHandle
== INVALID_HANDLE_VALUE
)
591 /* Return the error code */
592 return (WORD
)GetLastError();
595 /* Open the DOS handle */
596 DosHandle
= DosOpenHandle(FileHandle
);
598 if (DosHandle
== INVALID_DOS_HANDLE
)
600 /* Close the handle */
601 CloseHandle(FileHandle
);
603 /* Return the error code */
604 return ERROR_TOO_MANY_OPEN_FILES
;
607 /* It was successful */
609 return ERROR_SUCCESS
;
612 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
615 ACCESS_MASK Access
= 0;
618 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
622 /* Parse the access mode */
623 switch (AccessMode
& 3)
628 Access
= GENERIC_READ
;
635 Access
= GENERIC_WRITE
;
642 Access
= GENERIC_READ
| GENERIC_WRITE
;
649 return ERROR_INVALID_PARAMETER
;
654 FileHandle
= CreateFileA(FilePath
,
656 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
659 FILE_ATTRIBUTE_NORMAL
,
662 if (FileHandle
== INVALID_HANDLE_VALUE
)
664 /* Return the error code */
665 return (WORD
)GetLastError();
668 /* Open the DOS handle */
669 DosHandle
= DosOpenHandle(FileHandle
);
671 if (DosHandle
== INVALID_DOS_HANDLE
)
673 /* Close the handle */
674 CloseHandle(FileHandle
);
676 /* Return the error code */
677 return ERROR_TOO_MANY_OPEN_FILES
;
680 /* It was successful */
682 return ERROR_SUCCESS
;
685 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
687 WORD Result
= ERROR_SUCCESS
;
688 DWORD BytesRead32
= 0;
689 HANDLE Handle
= DosGetRealHandle(FileHandle
);
691 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
693 /* Make sure the handle is valid */
694 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
697 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
699 /* Store the error code */
700 Result
= (WORD
)GetLastError();
703 /* The number of bytes read is always 16-bit */
704 *BytesRead
= LOWORD(BytesRead32
);
706 /* Return the error code */
710 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
712 WORD Result
= ERROR_SUCCESS
;
713 DWORD BytesWritten32
= 0;
714 HANDLE Handle
= DosGetRealHandle(FileHandle
);
717 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
721 /* Make sure the handle is valid */
722 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
724 if (IsConsoleHandle(Handle
))
726 for (i
= 0; i
< Count
; i
++)
728 /* Call the BIOS to print the character */
729 BiosPrintCharacter(((LPBYTE
)Buffer
)[i
], DOS_CHAR_ATTRIBUTE
, Bda
->VideoPage
);
736 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
738 /* Store the error code */
739 Result
= (WORD
)GetLastError();
743 /* The number of bytes written is always 16-bit */
744 *BytesWritten
= LOWORD(BytesWritten32
);
746 /* Return the error code */
750 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
752 WORD Result
= ERROR_SUCCESS
;
754 HANDLE Handle
= DosGetRealHandle(FileHandle
);
756 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
761 /* Make sure the handle is valid */
762 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
764 /* Check if the origin is valid */
765 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
767 return ERROR_INVALID_FUNCTION
;
770 /* Move the file pointer */
771 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
773 /* Check if there's a possibility the operation failed */
774 if (FilePointer
== INVALID_SET_FILE_POINTER
)
776 /* Get the real error code */
777 Result
= (WORD
)GetLastError();
780 if (Result
!= ERROR_SUCCESS
)
782 /* The operation did fail */
786 /* Return the file pointer, if requested */
787 if (NewOffset
) *NewOffset
= FilePointer
;
790 return ERROR_SUCCESS
;
793 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
799 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
803 /* The system PSP has no handle table */
804 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
806 /* Get a pointer to the handle table */
807 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
808 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
810 /* Make sure the old handle is open */
811 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
813 /* Check if the new handle is open */
814 if (HandleTable
[NewHandle
] != 0xFF)
817 DosCloseHandle(NewHandle
);
820 /* Increment the reference count of the SFT entry */
821 SftIndex
= HandleTable
[OldHandle
];
822 DosSftRefCount
[SftIndex
]++;
824 /* Make the new handle point to that SFT entry */
825 HandleTable
[NewHandle
] = SftIndex
;
831 BOOLEAN
DosCloseHandle(WORD DosHandle
)
837 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
839 /* The system PSP has no handle table */
840 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
842 /* Get a pointer to the handle table */
843 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
844 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
846 /* Make sure the handle is open */
847 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
849 /* Decrement the reference count of the SFT entry */
850 SftIndex
= HandleTable
[DosHandle
];
851 DosSftRefCount
[SftIndex
]--;
853 /* Check if the reference count fell to zero */
854 if (!DosSftRefCount
[SftIndex
])
856 /* Close the file, it's no longer needed */
857 CloseHandle(DosSystemFileTable
[SftIndex
]);
859 /* Clear the handle */
860 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
863 /* Clear the entry in the JFT */
864 HandleTable
[DosHandle
] = 0xFF;
869 BOOLEAN
DosChangeDrive(BYTE Drive
)
871 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
873 /* Make sure the drive exists */
874 if (Drive
> (LastDrive
- 'A')) return FALSE
;
876 /* Find the path to the new current directory */
877 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
879 /* Change the current directory of the process */
880 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
882 /* Set the current drive */
883 CurrentDrive
= Drive
;
889 BOOLEAN
DosChangeDirectory(LPSTR Directory
)
895 /* Make sure the directory path is not too long */
896 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
898 DosLastError
= ERROR_PATH_NOT_FOUND
;
902 /* Get the drive number */
903 DriveNumber
= Directory
[0] - 'A';
905 /* Make sure the drive exists */
906 if (DriveNumber
> (LastDrive
- 'A'))
908 DosLastError
= ERROR_PATH_NOT_FOUND
;
912 /* Get the file attributes */
913 Attributes
= GetFileAttributesA(Directory
);
915 /* Make sure the path exists and is a directory */
916 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
917 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
919 DosLastError
= ERROR_PATH_NOT_FOUND
;
923 /* Check if this is the current drive */
924 if (DriveNumber
== CurrentDrive
)
926 /* Change the directory */
927 if (!SetCurrentDirectoryA(Directory
))
929 DosLastError
= LOWORD(GetLastError());
934 /* Get the directory part of the path */
935 Path
= strchr(Directory
, '\\');
938 /* Skip the backslash */
942 /* Set the directory for the drive */
943 if (Path
!= NULL
) strcpy(CurrentDirectories
[DriveNumber
], Path
);
944 else strcpy(CurrentDirectories
[DriveNumber
], "");
950 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
952 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
953 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
955 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
957 /* Set the exit interrupt */
958 PspBlock
->Exit
[0] = 0xCD; // int 0x20
959 PspBlock
->Exit
[1] = 0x20;
961 /* Set the number of the last paragraph */
962 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
964 /* Save the interrupt vectors */
965 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
966 PspBlock
->BreakAddress
= IntVecTable
[0x23];
967 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
969 /* Set the parent PSP */
970 PspBlock
->ParentPsp
= CurrentPsp
;
972 /* Copy the parent handle table */
973 DosCopyHandleTable(PspBlock
->HandleTable
);
975 /* Set the environment block */
976 PspBlock
->EnvBlock
= Environment
;
978 /* Set the handle table pointers to the internal handle table */
979 PspBlock
->HandleTableSize
= 20;
980 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
982 /* Set the DOS version */
983 PspBlock
->DosVersion
= DOS_VERSION
;
985 /* Set the far call opcodes */
986 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
987 PspBlock
->FarCall
[1] = 0x21;
988 PspBlock
->FarCall
[2] = 0xCB; // retf
990 /* Set the command line */
991 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
992 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
993 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
996 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
998 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
999 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
1000 LPBYTE Address
= NULL
;
1001 LPSTR ProgramFilePath
, Parameters
[256];
1002 CHAR CommandLineCopy
[DOS_CMDLINE_LENGTH
];
1006 DWORD i
, FileSize
, ExeSize
;
1007 PIMAGE_DOS_HEADER Header
;
1008 PDWORD RelocationTable
;
1011 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
1015 /* Save a copy of the command line */
1016 strcpy(CommandLineCopy
, CommandLine
);
1018 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
1020 /* Get the file name of the executable */
1021 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
1023 /* Load the parameters in the local array */
1024 while ((ParamCount
< sizeof(Parameters
)/sizeof(Parameters
[0]))
1025 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
1030 /* Open a handle to the executable */
1031 FileHandle
= CreateFileA(ProgramFilePath
,
1036 FILE_ATTRIBUTE_NORMAL
,
1038 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
1040 /* Get the file size */
1041 FileSize
= GetFileSize(FileHandle
, NULL
);
1043 /* Create a mapping object for the file */
1044 FileMapping
= CreateFileMapping(FileHandle
,
1050 if (FileMapping
== NULL
) goto Cleanup
;
1052 /* Map the file into memory */
1053 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1054 if (Address
== NULL
) goto Cleanup
;
1056 /* Did we get an environment segment? */
1059 /* Set a flag to know if the environment block was allocated here */
1060 AllocatedEnvBlock
= TRUE
;
1062 /* No, copy the one from the parent */
1063 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
1064 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
1069 /* Check if this is an EXE file or a COM file */
1070 if (Address
[0] == 'M' && Address
[1] == 'Z')
1074 /* Get the MZ header */
1075 Header
= (PIMAGE_DOS_HEADER
)Address
;
1077 /* Get the base size of the file, in paragraphs (rounded up) */
1078 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1080 /* Add the PSP size, in paragraphs */
1081 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1083 /* Add the maximum size that should be allocated */
1084 ExeSize
+= Header
->e_maxalloc
;
1086 /* Make sure it does not pass 0xFFFF */
1087 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1089 /* Reduce the size one by one until the allocation is successful */
1090 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1092 /* Try to allocate that much memory */
1093 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1094 if (Segment
!= 0) break;
1097 /* Check if at least the lowest allocation was successful */
1098 if (Segment
== 0) goto Cleanup
;
1100 /* Initialize the PSP */
1101 DosInitializePsp(Segment
,
1106 /* The process owns its own memory */
1107 DosChangeMemoryOwner(Segment
, Segment
);
1108 DosChangeMemoryOwner(EnvBlock
, Segment
);
1110 /* Copy the program to Segment:0100 */
1111 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1112 Address
+ (Header
->e_cparhdr
<< 4),
1113 min(FileSize
- (Header
->e_cparhdr
<< 4),
1114 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1116 /* Get the relocation table */
1117 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1119 /* Perform relocations */
1120 for (i
= 0; i
< Header
->e_crlc
; i
++)
1122 /* Get a pointer to the word that needs to be patched */
1123 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1124 0x100 + LOWORD(RelocationTable
[i
]));
1126 /* Add the number of the EXE segment to it */
1127 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1130 /* Set the initial segment registers */
1134 /* Set the stack to the location from the header */
1135 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1139 CurrentPsp
= Segment
;
1140 DiskTransferArea
= MAKELONG(0x80, Segment
);
1141 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1150 /* Find the maximum amount of memory that can be allocated */
1151 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1153 /* Make sure it's enough for the whole program and the PSP */
1154 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1156 /* Allocate all of it */
1157 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1158 if (Segment
== 0) goto Cleanup
;
1160 /* The process owns its own memory */
1161 DosChangeMemoryOwner(Segment
, Segment
);
1162 DosChangeMemoryOwner(EnvBlock
, Segment
);
1164 /* Copy the program to Segment:0100 */
1165 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1169 /* Initialize the PSP */
1170 DosInitializePsp(Segment
,
1172 (WORD
)((FileSize
+ sizeof(DOS_PSP
)) >> 4),
1175 /* Set the initial segment registers */
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 /* Save the return code */
1264 DosErrorLevel
= ReturnCode
;
1266 /* Return control to the parent process */
1267 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1268 LOWORD(PspBlock
->TerminateAddress
));
1271 CHAR
DosReadCharacter(VOID
)
1273 CHAR Character
= '\0';
1276 if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE
)))
1279 Character
= LOBYTE(BiosGetCharacter());
1283 /* Use the file reading function */
1284 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1290 BOOLEAN
DosCheckInput(VOID
)
1292 HANDLE Handle
= DosGetRealHandle(DOS_INPUT_HANDLE
);
1294 if (IsConsoleHandle(Handle
))
1297 return (BiosPeekCharacter() != 0xFFFF);
1302 DWORD FileSize
= GetFileSize(Handle
, &FileSizeHigh
);
1303 LONG LocationHigh
= 0;
1304 DWORD Location
= SetFilePointer(Handle
, 0, &LocationHigh
, FILE_CURRENT
);
1306 return ((Location
!= FileSize
) || (LocationHigh
!= FileSizeHigh
));
1310 VOID
DosPrintCharacter(CHAR Character
)
1314 /* Use the file writing function */
1315 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1318 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1320 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1322 if (Handle
== INVALID_HANDLE_VALUE
)
1325 DosLastError
= ERROR_FILE_NOT_FOUND
;
1329 switch (ControlCode
)
1331 /* Get Device Information */
1336 if (Handle
== DosSystemFileTable
[0])
1341 else if (Handle
== DosSystemFileTable
[1])
1343 /* Console output */
1347 /* It is a character device */
1350 /* Return the device information word */
1355 /* Unsupported control code */
1358 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1360 DosLastError
= ERROR_INVALID_PARAMETER
;
1366 VOID
DosInt20h(LPWORD Stack
)
1368 /* This is the exit interrupt */
1369 DosTerminateProcess(Stack
[STACK_CS
], 0);
1372 VOID
DosInt21h(LPWORD Stack
)
1375 SYSTEMTIME SystemTime
;
1377 PDOS_INPUT_BUFFER InputBuffer
;
1379 /* Check the value in the AH register */
1382 /* Terminate Program */
1385 DosTerminateProcess(Stack
[STACK_CS
], 0);
1389 /* Read Character And Echo */
1392 Character
= DosReadCharacter();
1393 DosPrintCharacter(Character
);
1395 /* Let the BOP repeat if needed */
1396 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1402 /* Print Character */
1405 BYTE Character
= getDL();
1406 DosPrintCharacter(Character
);
1409 * We return the output character (DOS 2.1+), see:
1410 * http://www.delorie.com/djgpp/doc/rbinter/id/65/25.html
1411 * for more information.
1417 /* Direct Console I/O */
1420 BYTE Character
= getDL();
1422 if (Character
!= 0xFF)
1425 DosPrintCharacter(Character
);
1428 * We return the output character (DOS 2.1+), see:
1429 * http://www.delorie.com/djgpp/doc/rbinter/id/69/25.html
1430 * for more information.
1437 if (DosCheckInput())
1439 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1440 setAL(DosReadCharacter());
1444 /* No character available */
1445 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1453 /* Read Character Without Echo */
1457 Character
= DosReadCharacter();
1459 /* Let the BOP repeat if needed */
1460 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1469 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1471 while (*String
!= '$')
1473 DosPrintCharacter(*String
);
1478 * We return the output character (DOS 2.1+), see:
1479 * http://www.delorie.com/djgpp/doc/rbinter/id/73/25.html
1480 * for more information.
1486 /* Read Buffered Input */
1489 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1491 while (Stack
[STACK_COUNTER
] < InputBuffer
->MaxLength
)
1493 /* Try to read a character */
1494 Character
= DosReadCharacter();
1496 /* If it's not ready yet, let the BOP repeat */
1497 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1499 /* Echo the character and append it to the buffer */
1500 DosPrintCharacter(Character
);
1501 InputBuffer
->Buffer
[Stack
[STACK_COUNTER
]] = Character
;
1503 if (Character
== '\r') break;
1504 Stack
[STACK_COUNTER
]++;
1507 /* Update the length */
1508 InputBuffer
->Length
= Stack
[STACK_COUNTER
];
1512 /* Get STDIN Status */
1515 setAL(DosCheckInput() ? 0xFF : 0x00);
1519 /* Set Default Drive */
1522 DosChangeDrive(getDL());
1523 setAL(LastDrive
- 'A' + 1);
1527 /* Get Default Drive */
1530 setAL(CurrentDrive
);
1534 /* Set Disk Transfer Area */
1537 DiskTransferArea
= MAKELONG(getDX(), getDS());
1541 /* Set Interrupt Vector */
1544 DWORD FarPointer
= MAKELONG(getDX(), getDS());
1546 /* Write the new far pointer to the IDT */
1547 ((PDWORD
)BaseAddress
)[getAL()] = FarPointer
;
1552 /* Get system date */
1555 GetLocalTime(&SystemTime
);
1556 setCX(SystemTime
.wYear
);
1557 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1558 setAL(SystemTime
.wDayOfWeek
);
1562 /* Set system date */
1565 GetLocalTime(&SystemTime
);
1566 SystemTime
.wYear
= getCX();
1567 SystemTime
.wMonth
= getDH();
1568 SystemTime
.wDay
= getDL();
1570 /* Return success or failure */
1571 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1575 /* Get system time */
1578 GetLocalTime(&SystemTime
);
1579 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1580 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1584 /* Set system time */
1587 GetLocalTime(&SystemTime
);
1588 SystemTime
.wHour
= getCH();
1589 SystemTime
.wMinute
= getCL();
1590 SystemTime
.wSecond
= getDH();
1591 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1593 /* Return success or failure */
1594 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1598 /* Get Disk Transfer Area */
1601 setES(HIWORD(DiskTransferArea
));
1602 setBX(LOWORD(DiskTransferArea
));
1606 /* Get DOS Version */
1609 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1611 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0)
1613 /* Return DOS 24-bit user serial number in BL:CX */
1618 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 1)
1621 * Return DOS OEM number:
1622 * 0x00 for IBM PC-DOS
1628 /* Return DOS version: Minor:Major in AH:AL */
1629 setAX(PspBlock
->DosVersion
);
1634 /* Get Interrupt Vector */
1637 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1639 /* Read the address from the IDT into ES:BX */
1640 setES(HIWORD(FarPointer
));
1641 setBX(LOWORD(FarPointer
));
1645 /* Create Directory */
1648 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1650 if (CreateDirectoryA(String
, NULL
))
1652 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1656 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1657 setAX(LOWORD(GetLastError()));
1663 /* Remove Directory */
1666 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1668 if (RemoveDirectoryA(String
))
1670 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1674 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1675 setAX(LOWORD(GetLastError()));
1681 /* Set Current Directory */
1684 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1686 if (DosChangeDirectory(String
))
1688 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1692 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1693 setAX(DosLastError
);
1703 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1704 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1709 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1714 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1725 WORD ErrorCode
= DosOpenFile(&FileHandle
,
1726 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1731 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1736 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1746 if (DosCloseHandle(getBX()))
1748 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1752 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1753 setAX(ERROR_INVALID_HANDLE
);
1762 WORD Handle
= getBX();
1763 LPBYTE Buffer
= (LPBYTE
)SEG_OFF_TO_PTR(getDS(), getDX());
1764 WORD Count
= getCX();
1766 WORD ErrorCode
= ERROR_SUCCESS
;
1768 if (IsConsoleHandle(DosGetRealHandle(Handle
)))
1770 while (Stack
[STACK_COUNTER
] < Count
)
1772 /* Read a character from the BIOS */
1773 Buffer
[Stack
[STACK_COUNTER
]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
1775 /* Stop if the BOP needs to be repeated */
1776 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1778 /* Increment the counter */
1779 Stack
[STACK_COUNTER
]++;
1782 if (Stack
[STACK_COUNTER
] < Count
) ErrorCode
= ERROR_NOT_READY
;
1783 else BytesRead
= Count
;
1787 /* Use the file reading function */
1788 ErrorCode
= DosReadFile(Handle
, Buffer
, Count
, &BytesRead
);
1793 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1796 else if (ErrorCode
!= ERROR_NOT_READY
)
1798 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1807 WORD BytesWritten
= 0;
1808 WORD ErrorCode
= DosWriteFile(getBX(),
1809 SEG_OFF_TO_PTR(getDS(), getDX()),
1815 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1816 setAX(BytesWritten
);
1820 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1830 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1832 /* Call the API function */
1833 if (DeleteFileA(FileName
))
1835 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1839 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1840 setAX(GetLastError());
1850 WORD ErrorCode
= DosSeekFile(getBX(),
1851 MAKELONG(getDX(), getCX()),
1857 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1859 /* Return the new offset in DX:AX */
1860 setDX(HIWORD(NewLocation
));
1861 setAX(LOWORD(NewLocation
));
1865 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1872 /* Get/Set File Attributes */
1876 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1878 if (getAL() == 0x00)
1880 /* Get the attributes */
1881 Attributes
= GetFileAttributesA(FileName
);
1883 /* Check if it failed */
1884 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1886 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1887 setAX(GetLastError());
1891 /* Return the attributes that DOS can understand */
1892 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1893 setCL(LOBYTE(Attributes
));
1895 else if (getAL() == 0x01)
1897 /* Try to set the attributes */
1898 if (SetFileAttributesA(FileName
, getCL()))
1900 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1904 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1905 setAX(GetLastError());
1910 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1911 setAX(ERROR_INVALID_FUNCTION
);
1920 if (DosHandleIoctl(getAL(), getBX()))
1922 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1926 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1927 setAX(DosLastError
);
1933 /* Duplicate Handle */
1937 HANDLE Handle
= DosGetRealHandle(getBX());
1939 if (Handle
!= INVALID_HANDLE_VALUE
)
1941 /* The handle is invalid */
1942 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1943 setAX(ERROR_INVALID_HANDLE
);
1947 /* Open a new handle to the same entry */
1948 NewHandle
= DosOpenHandle(Handle
);
1950 if (NewHandle
== INVALID_DOS_HANDLE
)
1952 /* Too many files open */
1953 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1954 setAX(ERROR_TOO_MANY_OPEN_FILES
);
1958 /* Return the result */
1959 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1964 /* Force Duplicate Handle */
1967 if (DosDuplicateHandle(getBX(), getCX()))
1969 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1973 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1974 setAX(ERROR_INVALID_HANDLE
);
1980 /* Allocate Memory */
1983 WORD MaxAvailable
= 0;
1984 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1988 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1993 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1994 setAX(DosLastError
);
1995 setBX(MaxAvailable
);
2004 if (DosFreeMemory(getES()))
2006 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2010 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2011 setAX(ERROR_ARENA_TRASHED
);
2017 /* Resize Memory Block */
2022 if (DosResizeMemory(getES(), getBX(), &Size
))
2024 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2028 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2029 setAX(DosLastError
);
2036 /* Terminate With Return Code */
2039 DosTerminateProcess(CurrentPsp
, getAL());
2043 /* Get Current Process */
2050 /* Get/Set Memory Management Options */
2053 if (getAL() == 0x00)
2055 /* Get allocation strategy */
2056 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2057 setAX(DosAllocStrategy
);
2059 else if (getAL() == 0x01)
2061 /* Set allocation strategy */
2063 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2064 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2066 /* Can't set both */
2067 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2068 setAX(ERROR_INVALID_PARAMETER
);
2072 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2074 /* Invalid allocation strategy */
2075 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2076 setAX(ERROR_INVALID_PARAMETER
);
2080 DosAllocStrategy
= getBL();
2081 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2083 else if (getAL() == 0x02)
2085 /* Get UMB link state */
2086 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2087 setAL(DosUmbLinked
? 0x01 : 0x00);
2089 else if (getAL() == 0x03)
2091 /* Set UMB link state */
2092 if (getBX()) DosLinkUmb();
2093 else DosUnlinkUmb();
2094 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2098 /* Invalid or unsupported function */
2099 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2100 setAX(ERROR_INVALID_FUNCTION
);
2109 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", getAH());
2110 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2115 VOID
DosBreakInterrupt(LPWORD Stack
)
2117 UNREFERENCED_PARAMETER(Stack
);
2122 BOOLEAN
DosInitialize(VOID
)
2125 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
2128 LPWSTR SourcePtr
, Environment
;
2130 LPSTR DestPtr
= (LPSTR
)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0);
2132 CHAR CurrentDirectory
[MAX_PATH
];
2133 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2136 /* Initialize the MCB */
2137 Mcb
->BlockType
= 'Z';
2138 Mcb
->Size
= USER_MEMORY_SIZE
;
2141 /* Initialize the link MCB to the UMB area */
2142 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
2143 Mcb
->BlockType
= 'M';
2144 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
2145 Mcb
->OwnerPsp
= SYSTEM_PSP
;
2147 /* Initialize the UMB area */
2148 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
2149 Mcb
->BlockType
= 'Z';
2150 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
2153 /* Get the environment strings */
2154 SourcePtr
= Environment
= GetEnvironmentStringsW();
2155 if (Environment
== NULL
) return FALSE
;
2157 /* Fill the DOS system environment block */
2160 /* Get the size of the ASCII string */
2161 AsciiSize
= WideCharToMultiByte(CP_ACP
,
2170 /* Allocate memory for the ASCII string */
2171 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
2172 if (AsciiString
== NULL
)
2174 FreeEnvironmentStringsW(Environment
);
2178 /* Convert to ASCII */
2179 WideCharToMultiByte(CP_ACP
,
2188 /* Copy the string into DOS memory */
2189 strcpy(DestPtr
, AsciiString
);
2191 /* Move to the next string */
2192 SourcePtr
+= wcslen(SourcePtr
) + 1;
2193 DestPtr
+= strlen(AsciiString
);
2196 /* Free the memory */
2197 HeapFree(GetProcessHeap(), 0, AsciiString
);
2201 /* Free the memory allocated for environment strings */
2202 FreeEnvironmentStringsW(Environment
);
2204 /* Clear the current directory buffer */
2205 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2207 /* Get the current directory */
2208 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2210 // TODO: Use some kind of default path?
2214 /* Convert that to a DOS path */
2215 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2217 // TODO: Use some kind of default path?
2222 CurrentDrive
= DosDirectory
[0] - 'A';
2225 Path
= strchr(DosDirectory
, '\\');
2228 /* Skip the backslash */
2232 /* Set the directory */
2233 if (Path
!= NULL
) strcpy(CurrentDirectories
[CurrentDrive
], Path
);
2235 /* Read CONFIG.SYS */
2236 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2239 while (fgetws(Buffer
, 256, Stream
))
2241 // TODO: Parse the line
2246 /* Initialize the SFT */
2247 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2249 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2250 DosSftRefCount
[i
] = 0;
2253 /* Get handles to standard I/O devices */
2254 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2255 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2256 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);