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 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
34 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
36 /* If this is the last block or it's not free, quit */
37 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
41 /* Get a pointer to the next MCB */
42 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
44 /* Check if the next MCB is free */
45 if (NextMcb
->OwnerPsp
== 0)
48 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
49 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
50 NextMcb
->BlockType
= 'I';
54 /* No more adjoining free blocks */
60 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
62 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
66 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
68 /* Calculate the size of the environment block */
71 TotalSize
+= strlen(Ptr
) + 1;
72 Ptr
+= strlen(Ptr
) + 1;
76 /* Add the string buffer size */
77 TotalSize
+= strlen(ProgramName
) + 1;
79 /* Allocate the memory for the environment block */
80 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
81 if (!DestSegment
) return 0;
85 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
89 strcpy(DestBuffer
, Ptr
);
91 /* Advance to the next string */
92 DestBuffer
+= strlen(Ptr
);
93 Ptr
+= strlen(Ptr
) + 1;
95 /* Put a zero after the string */
99 /* Set the final zero */
102 /* Copy the program name after the environment block */
103 strcpy(DestBuffer
, ProgramName
);
108 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
110 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
112 /* Just set the owner */
113 Mcb
->OwnerPsp
= NewOwner
;
116 static WORD
DosOpenHandle(HANDLE Handle
)
123 /* The system PSP has no handle table */
124 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
126 /* Get a pointer to the handle table */
127 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
128 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
130 /* Find a free entry in the JFT */
131 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
133 if (HandleTable
[DosHandle
] == 0xFF) break;
136 /* If there are no free entries, fail */
137 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
139 /* Check if the handle is already in the SFT */
140 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
142 /* Check if this is the same handle */
143 if (DosSystemFileTable
[i
] != Handle
) continue;
145 /* Already in the table, reference it */
148 /* Set the JFT entry to that SFT index */
149 HandleTable
[DosHandle
] = i
;
151 /* Return the new handle */
155 /* Add the handle to the SFT */
156 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
158 /* Make sure this is an empty table entry */
159 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
161 /* Initialize the empty table entry */
162 DosSystemFileTable
[i
] = Handle
;
163 DosSftRefCount
[i
] = 1;
165 /* Set the JFT entry to that SFT index */
166 HandleTable
[DosHandle
] = i
;
168 /* Return the new handle */
172 /* The SFT is full */
173 return INVALID_DOS_HANDLE
;
176 static HANDLE
DosGetRealHandle(WORD DosHandle
)
181 /* The system PSP has no handle table */
182 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
184 /* Get a pointer to the handle table */
185 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
186 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
188 /* Make sure the handle is open */
189 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
191 /* Return the Win32 handle */
192 return DosSystemFileTable
[HandleTable
[DosHandle
]];
195 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
201 /* Clear the table first */
202 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
204 /* Check if this is the initial process */
205 if (CurrentPsp
== SYSTEM_PSP
)
207 /* Set up the standard I/O devices */
208 for (i
= 0; i
<= 2; i
++)
210 /* Set the index in the SFT */
211 DestinationTable
[i
] = (BYTE
)i
;
213 /* Increase the reference count */
221 /* Get the parent PSP block and handle table */
222 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
223 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
225 /* Copy the first 20 handles into the new table */
226 for (i
= 0; i
< 20; i
++)
228 DestinationTable
[i
] = SourceTable
[i
];
230 /* Increase the reference count */
231 DosSftRefCount
[SourceTable
[i
]]++;
235 /* PUBLIC FUNCTIONS ***********************************************************/
237 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
239 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
240 PDOS_MCB CurrentMcb
, NextMcb
;
241 BOOLEAN SearchUmb
= FALSE
;
243 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
245 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
247 /* Search UMB first */
248 Segment
= UMB_START_SEGMENT
;
254 /* Get a pointer to the MCB */
255 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
257 /* Make sure it's valid */
258 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
260 DPRINT("The DOS memory arena is corrupted!\n");
261 DosLastError
= ERROR_ARENA_TRASHED
;
265 /* Only check free blocks */
266 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
268 /* Combine this free block with adjoining free blocks */
269 DosCombineFreeBlocks(Segment
);
271 /* Update the maximum block size */
272 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
274 /* Check if this block is big enough */
275 if (CurrentMcb
->Size
< Size
) goto Next
;
277 switch (DosAllocStrategy
& 0x3F)
279 case DOS_ALLOC_FIRST_FIT
:
281 /* For first fit, stop immediately */
286 case DOS_ALLOC_BEST_FIT
:
288 /* For best fit, update the smallest block found so far */
289 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
297 case DOS_ALLOC_LAST_FIT
:
299 /* For last fit, make the current block the result, but keep searching */
306 /* If this was the last MCB in the chain, quit */
307 if (CurrentMcb
->BlockType
== 'Z')
309 /* Check if nothing was found while searching through UMBs */
310 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
312 /* Search low memory */
313 Segment
= FIRST_MCB_SEGMENT
;
320 /* Otherwise, update the segment and continue */
321 Segment
+= CurrentMcb
->Size
+ 1;
326 /* If we didn't find a free block, return 0 */
329 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
330 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
334 /* Get a pointer to the MCB */
335 CurrentMcb
= SEGMENT_TO_MCB(Result
);
337 /* Check if the block is larger than requested */
338 if (CurrentMcb
->Size
> Size
)
340 /* It is, split it into two blocks */
341 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
343 /* Initialize the new MCB structure */
344 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
345 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
346 NextMcb
->OwnerPsp
= 0;
348 /* Update the current block */
349 CurrentMcb
->BlockType
= 'M';
350 CurrentMcb
->Size
= Size
;
353 /* Take ownership of the block */
354 CurrentMcb
->OwnerPsp
= CurrentPsp
;
356 /* Return the segment of the data portion of the block */
360 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
362 BOOLEAN Success
= TRUE
;
363 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
364 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
366 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
370 /* Make sure this is a valid, allocated block */
371 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
374 DosLastError
= ERROR_INVALID_HANDLE
;
378 ReturnSize
= Mcb
->Size
;
380 /* Check if we need to expand or contract the block */
381 if (NewSize
> Mcb
->Size
)
383 /* We can't expand the last block */
384 if (Mcb
->BlockType
!= 'M')
390 /* Get the pointer and segment of the next MCB */
391 NextSegment
= Segment
+ Mcb
->Size
+ 1;
392 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
394 /* Make sure the next segment is free */
395 if (NextMcb
->OwnerPsp
!= 0)
397 DPRINT("Cannot expand memory block: next segment is not free!\n");
398 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
403 /* Combine this free block with adjoining free blocks */
404 DosCombineFreeBlocks(NextSegment
);
406 /* Set the maximum possible size of the block */
407 ReturnSize
+= NextMcb
->Size
+ 1;
409 /* Maximize the current block */
410 Mcb
->Size
= ReturnSize
;
411 Mcb
->BlockType
= NextMcb
->BlockType
;
413 /* Invalidate the next block */
414 NextMcb
->BlockType
= 'I';
416 /* Check if the block is larger than requested */
417 if (Mcb
->Size
> NewSize
)
419 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
423 /* It is, split it into two blocks */
424 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
426 /* Initialize the new MCB structure */
427 NextMcb
->BlockType
= Mcb
->BlockType
;
428 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
429 NextMcb
->OwnerPsp
= 0;
431 /* Update the current block */
432 Mcb
->BlockType
= 'M';
436 else if (NewSize
< Mcb
->Size
)
438 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
442 /* Just split the block */
443 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
444 NextMcb
->BlockType
= Mcb
->BlockType
;
445 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
446 NextMcb
->OwnerPsp
= 0;
449 Mcb
->BlockType
= 'M';
454 /* Check if the operation failed */
457 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
460 /* Return the maximum possible size */
461 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
467 BOOLEAN
DosFreeMemory(WORD BlockData
)
469 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
471 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
473 /* Make sure the MCB is valid */
474 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
476 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
480 /* Mark the block as free */
486 BOOLEAN
DosLinkUmb(VOID
)
488 DWORD Segment
= FIRST_MCB_SEGMENT
;
489 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
491 DPRINT("Linking UMB\n");
493 /* Check if UMBs are already linked */
494 if (DosUmbLinked
) return FALSE
;
496 /* Find the last block */
497 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
499 Segment
+= Mcb
->Size
+ 1;
500 Mcb
= SEGMENT_TO_MCB(Segment
);
503 /* Make sure it's valid */
504 if (Mcb
->BlockType
!= 'Z') return FALSE
;
506 /* Connect the MCB with the UMB chain */
507 Mcb
->BlockType
= 'M';
513 BOOLEAN
DosUnlinkUmb(VOID
)
515 DWORD Segment
= FIRST_MCB_SEGMENT
;
516 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
518 DPRINT("Unlinking UMB\n");
520 /* Check if UMBs are already unlinked */
521 if (!DosUmbLinked
) return FALSE
;
523 /* Find the block preceding the MCB that links it with the UMB chain */
524 while (Segment
<= 0xFFFF)
526 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
528 /* This is the last non-UMB segment */
532 /* Advance to the next MCB */
533 Segment
+= Mcb
->Size
+ 1;
534 Mcb
= SEGMENT_TO_MCB(Segment
);
537 /* Mark the MCB as the last MCB */
538 Mcb
->BlockType
= 'Z';
540 DosUmbLinked
= FALSE
;
544 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
549 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
553 /* Create the file */
554 FileHandle
= CreateFileA(FilePath
,
555 GENERIC_READ
| GENERIC_WRITE
,
556 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
562 if (FileHandle
== INVALID_HANDLE_VALUE
)
564 /* Return the error code */
565 return (WORD
)GetLastError();
568 /* Open the DOS handle */
569 DosHandle
= DosOpenHandle(FileHandle
);
571 if (DosHandle
== INVALID_DOS_HANDLE
)
573 /* Close the handle */
574 CloseHandle(FileHandle
);
576 /* Return the error code */
577 return ERROR_TOO_MANY_OPEN_FILES
;
580 /* It was successful */
582 return ERROR_SUCCESS
;
585 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
588 ACCESS_MASK Access
= 0;
591 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
595 /* Parse the access mode */
596 switch (AccessMode
& 3)
601 Access
= GENERIC_READ
;
608 Access
= GENERIC_WRITE
;
615 Access
= GENERIC_READ
| GENERIC_WRITE
;
622 return ERROR_INVALID_PARAMETER
;
627 FileHandle
= CreateFileA(FilePath
,
629 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
632 FILE_ATTRIBUTE_NORMAL
,
635 if (FileHandle
== INVALID_HANDLE_VALUE
)
637 /* Return the error code */
638 return (WORD
)GetLastError();
641 /* Open the DOS handle */
642 DosHandle
= DosOpenHandle(FileHandle
);
644 if (DosHandle
== INVALID_DOS_HANDLE
)
646 /* Close the handle */
647 CloseHandle(FileHandle
);
649 /* Return the error code */
650 return ERROR_TOO_MANY_OPEN_FILES
;
653 /* It was successful */
655 return ERROR_SUCCESS
;
658 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
660 WORD Result
= ERROR_SUCCESS
;
661 DWORD BytesRead32
= 0;
662 HANDLE Handle
= DosGetRealHandle(FileHandle
);
664 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
666 /* Make sure the handle is valid */
667 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
670 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
672 /* Store the error code */
673 Result
= (WORD
)GetLastError();
676 /* The number of bytes read is always 16-bit */
677 *BytesRead
= LOWORD(BytesRead32
);
679 /* Return the error code */
683 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
685 WORD Result
= ERROR_SUCCESS
;
686 DWORD BytesWritten32
= 0;
687 HANDLE Handle
= DosGetRealHandle(FileHandle
);
689 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
693 /* Make sure the handle is valid */
694 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
697 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
699 /* Store the error code */
700 Result
= (WORD
)GetLastError();
703 /* The number of bytes written is always 16-bit */
704 *BytesWritten
= LOWORD(BytesWritten32
);
706 /* Return the error code */
710 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
712 WORD Result
= ERROR_SUCCESS
;
714 HANDLE Handle
= DosGetRealHandle(FileHandle
);
716 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
721 /* Make sure the handle is valid */
722 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
724 /* Check if the origin is valid */
725 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
727 return ERROR_INVALID_FUNCTION
;
730 /* Move the file pointer */
731 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
733 /* Check if there's a possibility the operation failed */
734 if (FilePointer
== INVALID_SET_FILE_POINTER
)
736 /* Get the real error code */
737 Result
= (WORD
)GetLastError();
740 if (Result
!= ERROR_SUCCESS
)
742 /* The operation did fail */
746 /* Return the file pointer, if requested */
747 if (NewOffset
) *NewOffset
= FilePointer
;
750 return ERROR_SUCCESS
;
753 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
759 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
763 /* The system PSP has no handle table */
764 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
766 /* Get a pointer to the handle table */
767 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
768 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
770 /* Make sure the old handle is open */
771 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
773 /* Check if the new handle is open */
774 if (HandleTable
[NewHandle
] != 0xFF)
777 DosCloseHandle(NewHandle
);
780 /* Increment the reference count of the SFT entry */
781 SftIndex
= HandleTable
[OldHandle
];
782 DosSftRefCount
[SftIndex
]++;
784 /* Make the new handle point to that SFT entry */
785 HandleTable
[NewHandle
] = SftIndex
;
791 BOOLEAN
DosCloseHandle(WORD DosHandle
)
797 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
799 /* The system PSP has no handle table */
800 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
802 /* Get a pointer to the handle table */
803 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
804 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
806 /* Make sure the handle is open */
807 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
809 /* Decrement the reference count of the SFT entry */
810 SftIndex
= HandleTable
[DosHandle
];
811 DosSftRefCount
[SftIndex
]--;
813 /* Check if the reference count fell to zero */
814 if (!DosSftRefCount
[SftIndex
])
816 /* Close the file, it's no longer needed */
817 CloseHandle(DosSystemFileTable
[SftIndex
]);
819 /* Clear the handle */
820 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
823 /* Clear the entry in the JFT */
824 HandleTable
[DosHandle
] = 0xFF;
829 BOOLEAN
DosChangeDrive(BYTE Drive
)
831 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
833 /* Make sure the drive exists */
834 if (Drive
> (LastDrive
- 'A')) return FALSE
;
836 /* Find the path to the new current directory */
837 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
839 /* Change the current directory of the process */
840 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
842 /* Set the current drive */
843 CurrentDrive
= Drive
;
849 BOOLEAN
DosChangeDirectory(LPSTR Directory
)
855 /* Make sure the directory path is not too long */
856 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
858 DosLastError
= ERROR_PATH_NOT_FOUND
;
862 /* Get the drive number */
863 DriveNumber
= Directory
[0] - 'A';
865 /* Make sure the drive exists */
866 if (DriveNumber
> (LastDrive
- 'A'))
868 DosLastError
= ERROR_PATH_NOT_FOUND
;
872 /* Get the file attributes */
873 Attributes
= GetFileAttributesA(Directory
);
875 /* Make sure the path exists and is a directory */
876 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
877 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
879 DosLastError
= ERROR_PATH_NOT_FOUND
;
883 /* Check if this is the current drive */
884 if (DriveNumber
== CurrentDrive
)
886 /* Change the directory */
887 if (!SetCurrentDirectoryA(Directory
))
889 DosLastError
= LOWORD(GetLastError());
894 /* Get the directory part of the path */
895 Path
= strchr(Directory
, '\\');
898 /* Skip the backslash */
902 /* Set the directory for the drive */
903 if (Path
!= NULL
) strcpy(CurrentDirectories
[DriveNumber
], Path
);
904 else strcpy(CurrentDirectories
[DriveNumber
], "");
910 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
912 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
913 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
915 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
917 /* Set the exit interrupt */
918 PspBlock
->Exit
[0] = 0xCD; // int 0x20
919 PspBlock
->Exit
[1] = 0x20;
921 /* Set the number of the last paragraph */
922 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
924 /* Save the interrupt vectors */
925 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
926 PspBlock
->BreakAddress
= IntVecTable
[0x23];
927 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
929 /* Set the parent PSP */
930 PspBlock
->ParentPsp
= CurrentPsp
;
932 /* Copy the parent handle table */
933 DosCopyHandleTable(PspBlock
->HandleTable
);
935 /* Set the environment block */
936 PspBlock
->EnvBlock
= Environment
;
938 /* Set the handle table pointers to the internal handle table */
939 PspBlock
->HandleTableSize
= 20;
940 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
942 /* Set the DOS version */
943 PspBlock
->DosVersion
= DOS_VERSION
;
945 /* Set the far call opcodes */
946 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
947 PspBlock
->FarCall
[1] = 0x21;
948 PspBlock
->FarCall
[2] = 0xCB; // retf
950 /* Set the command line */
951 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
952 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
953 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
956 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
958 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
959 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
960 LPBYTE Address
= NULL
;
961 LPSTR ProgramFilePath
, Parameters
[256];
962 CHAR CommandLineCopy
[DOS_CMDLINE_LENGTH
];
966 DWORD i
, FileSize
, ExeSize
;
967 PIMAGE_DOS_HEADER Header
;
968 PDWORD RelocationTable
;
971 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
975 /* Save a copy of the command line */
976 strcpy(CommandLineCopy
, CommandLine
);
978 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
980 /* Get the file name of the executable */
981 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
983 /* Load the parameters in the local array */
984 while ((ParamCount
< sizeof(Parameters
)/sizeof(Parameters
[0]))
985 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
990 /* Open a handle to the executable */
991 FileHandle
= CreateFileA(ProgramFilePath
,
996 FILE_ATTRIBUTE_NORMAL
,
998 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
1000 /* Get the file size */
1001 FileSize
= GetFileSize(FileHandle
, NULL
);
1003 /* Create a mapping object for the file */
1004 FileMapping
= CreateFileMapping(FileHandle
,
1010 if (FileMapping
== NULL
) goto Cleanup
;
1012 /* Map the file into memory */
1013 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1014 if (Address
== NULL
) goto Cleanup
;
1016 /* Did we get an environment segment? */
1019 /* Set a flag to know if the environment block was allocated here */
1020 AllocatedEnvBlock
= TRUE
;
1022 /* No, copy the one from the parent */
1023 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
1024 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
1029 /* Check if this is an EXE file or a COM file */
1030 if (Address
[0] == 'M' && Address
[1] == 'Z')
1034 /* Get the MZ header */
1035 Header
= (PIMAGE_DOS_HEADER
)Address
;
1037 /* Get the base size of the file, in paragraphs (rounded up) */
1038 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1040 /* Add the PSP size, in paragraphs */
1041 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1043 /* Add the maximum size that should be allocated */
1044 ExeSize
+= Header
->e_maxalloc
;
1046 /* Make sure it does not pass 0xFFFF */
1047 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1049 /* Reduce the size one by one until the allocation is successful */
1050 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1052 /* Try to allocate that much memory */
1053 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1054 if (Segment
!= 0) break;
1057 /* Check if at least the lowest allocation was successful */
1058 if (Segment
== 0) goto Cleanup
;
1060 /* Initialize the PSP */
1061 DosInitializePsp(Segment
,
1066 /* The process owns its own memory */
1067 DosChangeMemoryOwner(Segment
, Segment
);
1068 DosChangeMemoryOwner(EnvBlock
, Segment
);
1070 /* Copy the program to Segment:0100 */
1071 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
1072 + TO_LINEAR(Segment
, 0x100)),
1073 Address
+ (Header
->e_cparhdr
<< 4),
1074 min(FileSize
- (Header
->e_cparhdr
<< 4),
1075 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1077 /* Get the relocation table */
1078 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1080 /* Perform relocations */
1081 for (i
= 0; i
< Header
->e_crlc
; i
++)
1083 /* Get a pointer to the word that needs to be patched */
1084 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
1085 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
1086 0x100 + LOWORD(RelocationTable
[i
])));
1088 /* Add the number of the EXE segment to it */
1089 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1092 /* Set the initial segment registers */
1093 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1094 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1096 /* Set the stack to the location from the header */
1097 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1101 CurrentPsp
= Segment
;
1102 DiskTransferArea
= MAKELONG(0x80, Segment
);
1103 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1112 /* Find the maximum amount of memory that can be allocated */
1113 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1115 /* Make sure it's enough for the whole program and the PSP */
1116 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1118 /* Allocate all of it */
1119 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1120 if (Segment
== 0) goto Cleanup
;
1122 /* The process owns its own memory */
1123 DosChangeMemoryOwner(Segment
, Segment
);
1124 DosChangeMemoryOwner(EnvBlock
, Segment
);
1126 /* Copy the program to Segment:0100 */
1127 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
1128 + TO_LINEAR(Segment
, 0x100)),
1132 /* Initialize the PSP */
1133 DosInitializePsp(Segment
,
1135 (WORD
)((FileSize
+ sizeof(DOS_PSP
)) >> 4),
1138 /* Set the initial segment registers */
1139 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1140 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1142 /* Set the stack to the last word of the segment */
1143 EmulatorSetStack(Segment
, 0xFFFE);
1146 CurrentPsp
= Segment
;
1147 DiskTransferArea
= MAKELONG(0x80, Segment
);
1148 EmulatorExecute(Segment
, 0x100);
1156 /* It was not successful, cleanup the DOS memory */
1157 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
1158 if (Segment
) DosFreeMemory(Segment
);
1162 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1164 /* Close the file mapping object */
1165 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1167 /* Close the file handle */
1168 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1173 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1176 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1177 PDOS_MCB CurrentMcb
;
1178 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1179 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1181 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1185 /* Check if this PSP is it's own parent */
1186 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1188 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1190 /* Close the handle */
1194 /* Free the memory used by the process */
1197 /* Get a pointer to the MCB */
1198 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1200 /* Make sure the MCB is valid */
1201 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1203 /* If this block was allocated by the process, free it */
1204 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1206 /* If this was the last block, quit */
1207 if (CurrentMcb
->BlockType
== 'Z') break;
1209 /* Update the segment and continue */
1210 McbSegment
+= CurrentMcb
->Size
+ 1;
1214 /* Restore the interrupt vectors */
1215 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1216 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1217 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1219 /* Update the current PSP */
1220 if (Psp
== CurrentPsp
)
1222 CurrentPsp
= PspBlock
->ParentPsp
;
1223 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1226 /* Return control to the parent process */
1227 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1228 LOWORD(PspBlock
->TerminateAddress
));
1231 CHAR
DosReadCharacter(VOID
)
1233 CHAR Character
= '\0';
1236 /* Use the file reading function */
1237 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1242 VOID
DosPrintCharacter(CHAR Character
)
1246 /* Use the file writing function */
1247 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1250 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1252 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1254 if (Handle
== INVALID_HANDLE_VALUE
)
1257 DosLastError
= ERROR_FILE_NOT_FOUND
;
1261 switch (ControlCode
)
1263 /* Get Device Information */
1268 if (Handle
== DosSystemFileTable
[0])
1273 else if (Handle
== DosSystemFileTable
[1])
1275 /* Console output */
1279 /* It is a character device */
1282 /* Return the device information word */
1283 EmulatorSetRegister(EMULATOR_REG_DX
, InfoWord
);
1288 /* Unsupported control code */
1291 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1293 DosLastError
= ERROR_INVALID_PARAMETER
;
1299 VOID
DosInt20h(LPWORD Stack
)
1301 /* This is the exit interrupt */
1302 DosTerminateProcess(Stack
[STACK_CS
], 0);
1305 VOID
DosInt21h(LPWORD Stack
)
1309 SYSTEMTIME SystemTime
;
1311 PDOS_INPUT_BUFFER InputBuffer
;
1312 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1313 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1314 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1315 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
1316 WORD DataSegment
= (WORD
)EmulatorGetRegister(EMULATOR_REG_DS
);
1317 WORD ExtSegment
= (WORD
)EmulatorGetRegister(EMULATOR_REG_ES
);
1319 /* Check the value in the AH register */
1320 switch (HIBYTE(Eax
))
1322 /* Terminate Program */
1325 DosTerminateProcess(Stack
[STACK_CS
], 0);
1329 /* Read Character And Echo */
1332 Character
= DosReadCharacter();
1333 DosPrintCharacter(Character
);
1334 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
1338 /* Print Character */
1341 DosPrintCharacter(LOBYTE(Edx
));
1345 /* Read Character Without Echo */
1349 EmulatorSetRegister(EMULATOR_REG_AX
,
1350 (Eax
& 0xFFFFFF00) | DosReadCharacter());
1357 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1358 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1360 while ((*String
) != '$')
1362 DosPrintCharacter(*String
);
1369 /* Read Buffered Input */
1372 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
1373 + TO_LINEAR(DataSegment
,
1376 InputBuffer
->Length
= 0;
1377 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
1379 Character
= DosReadCharacter();
1380 DosPrintCharacter(Character
);
1381 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
1382 if (Character
== '\r') break;
1383 InputBuffer
->Length
++;
1389 /* Set Default Drive */
1392 DosChangeDrive(LOBYTE(Edx
));
1393 EmulatorSetRegister(EMULATOR_REG_AX
,
1394 (Eax
& 0xFFFFFF00) | (LastDrive
- 'A' + 1));
1399 /* Set Disk Transfer Area */
1402 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
1406 /* Set Interrupt Vector */
1409 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
1411 /* Write the new far pointer to the IDT */
1412 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
1417 /* Get system date */
1420 GetLocalTime(&SystemTime
);
1421 EmulatorSetRegister(EMULATOR_REG_CX
,
1422 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
1423 EmulatorSetRegister(EMULATOR_REG_DX
,
1425 | (SystemTime
.wMonth
<< 8)
1427 EmulatorSetRegister(EMULATOR_REG_AX
,
1428 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1432 /* Set system date */
1435 GetLocalTime(&SystemTime
);
1436 SystemTime
.wYear
= LOWORD(Ecx
);
1437 SystemTime
.wMonth
= HIBYTE(Edx
);
1438 SystemTime
.wDay
= LOBYTE(Edx
);
1440 if (SetLocalTime(&SystemTime
))
1442 /* Return success */
1443 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1447 /* Return failure */
1448 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1454 /* Get system time */
1457 GetLocalTime(&SystemTime
);
1458 EmulatorSetRegister(EMULATOR_REG_CX
,
1460 | (SystemTime
.wHour
<< 8)
1461 | SystemTime
.wMinute
);
1462 EmulatorSetRegister(EMULATOR_REG_DX
,
1464 | (SystemTime
.wSecond
<< 8)
1465 | (SystemTime
.wMilliseconds
/ 10));
1469 /* Set system time */
1472 GetLocalTime(&SystemTime
);
1473 SystemTime
.wHour
= HIBYTE(Ecx
);
1474 SystemTime
.wMinute
= LOBYTE(Ecx
);
1475 SystemTime
.wSecond
= HIBYTE(Edx
);
1476 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1478 if (SetLocalTime(&SystemTime
))
1480 /* Return success */
1481 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1485 /* Return failure */
1486 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1492 /* Get Disk Transfer Area */
1495 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1496 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1501 /* Get DOS Version */
1504 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1506 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1510 /* Get Interrupt Vector */
1513 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1515 /* Read the address from the IDT into ES:BX */
1516 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1517 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1522 /* Create Directory */
1525 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1526 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1528 if (CreateDirectoryA(String
, NULL
))
1530 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1534 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1535 EmulatorSetRegister(EMULATOR_REG_AX
,
1536 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1542 /* Remove Directory */
1545 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1546 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1548 if (RemoveDirectoryA(String
))
1550 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1554 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1555 EmulatorSetRegister(EMULATOR_REG_AX
,
1556 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1563 /* Set Current Directory */
1566 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1567 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1569 if (DosChangeDirectory(String
))
1571 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1575 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1576 EmulatorSetRegister(EMULATOR_REG_AX
,
1577 (Eax
& 0xFFFF0000) | DosLastError
);
1587 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1588 (LPCSTR
)(ULONG_PTR
)BaseAddress
1589 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1595 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1597 /* Return the handle in AX */
1598 EmulatorSetRegister(EMULATOR_REG_AX
,
1599 (Eax
& 0xFFFF0000) | FileHandle
);
1604 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1606 /* Return the error code in AX */
1607 EmulatorSetRegister(EMULATOR_REG_AX
,
1608 (Eax
& 0xFFFF0000) | ErrorCode
);
1618 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1619 (LPCSTR
)(ULONG_PTR
)BaseAddress
1620 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1626 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1628 /* Return the handle in AX */
1629 EmulatorSetRegister(EMULATOR_REG_AX
,
1630 (Eax
& 0xFFFF0000) | FileHandle
);
1635 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1637 /* Return the error code in AX */
1638 EmulatorSetRegister(EMULATOR_REG_AX
,
1639 (Eax
& 0xFFFF0000) | ErrorCode
);
1648 if (DosCloseHandle(LOWORD(Ebx
)))
1651 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1656 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1658 /* Return the error code in AX */
1659 EmulatorSetRegister(EMULATOR_REG_AX
,
1660 (Eax
& 0xFFFF0000) | ERROR_INVALID_HANDLE
);
1670 WORD ErrorCode
= DosReadFile(LOWORD(Ebx
),
1671 (LPVOID
)((ULONG_PTR
)BaseAddress
1672 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1679 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1681 /* Return the number of bytes read in AX */
1682 EmulatorSetRegister(EMULATOR_REG_AX
,
1683 (Eax
& 0xFFFF0000) | BytesRead
);
1688 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1690 /* Return the error code in AX */
1691 EmulatorSetRegister(EMULATOR_REG_AX
,
1692 (Eax
& 0xFFFF0000) | ErrorCode
);
1700 WORD BytesWritten
= 0;
1701 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1702 (LPVOID
)((ULONG_PTR
)BaseAddress
1703 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1710 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1712 /* Return the number of bytes written in AX */
1713 EmulatorSetRegister(EMULATOR_REG_AX
,
1714 (Eax
& 0xFFFF0000) | BytesWritten
);
1719 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1721 /* Return the error code in AX */
1722 EmulatorSetRegister(EMULATOR_REG_AX
,
1723 (Eax
& 0xFFFF0000) | ErrorCode
);
1732 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1734 /* Call the API function */
1735 if (DeleteFileA(FileName
)) Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1738 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1739 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1749 WORD ErrorCode
= DosSeekFile(LOWORD(Ebx
),
1750 MAKELONG(LOWORD(Edx
), LOWORD(Ecx
)),
1757 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1759 /* Return the new offset in DX:AX */
1760 EmulatorSetRegister(EMULATOR_REG_DX
,
1761 (Edx
& 0xFFFF0000) | HIWORD(NewLocation
));
1762 EmulatorSetRegister(EMULATOR_REG_AX
,
1763 (Eax
& 0xFFFF0000) | LOWORD(NewLocation
));
1768 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1770 /* Return the error code in AX */
1771 EmulatorSetRegister(EMULATOR_REG_AX
,
1772 (Eax
& 0xFFFF0000) | ErrorCode
);
1778 /* Get/Set File Attributes */
1782 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1784 if (LOBYTE(Eax
) == 0x00)
1786 /* Get the attributes */
1787 Attributes
= GetFileAttributesA(FileName
);
1789 /* Check if it failed */
1790 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1792 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1793 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1798 /* Return the attributes that DOS can understand */
1799 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1800 EmulatorSetRegister(EMULATOR_REG_CX
,
1801 (Ecx
& 0xFFFFFF00) | LOBYTE(Attributes
));
1803 else if (LOBYTE(Eax
) == 0x01)
1805 /* Try to set the attributes */
1806 if (SetFileAttributesA(FileName
, LOBYTE(Ecx
)))
1808 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1812 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1813 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1818 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1819 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
1828 if (DosHandleIoctl(LOBYTE(Eax
), LOWORD(Ebx
)))
1830 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1834 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1835 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1841 /* Duplicate Handle */
1845 HANDLE Handle
= DosGetRealHandle(LOWORD(Ebx
));
1847 if (Handle
!= INVALID_HANDLE_VALUE
)
1849 /* The handle is invalid */
1850 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1851 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
1856 /* Open a new handle to the same entry */
1857 NewHandle
= DosOpenHandle(Handle
);
1859 if (NewHandle
== INVALID_DOS_HANDLE
)
1861 /* Too many files open */
1862 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1863 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_TOO_MANY_OPEN_FILES
);
1868 /* Return the result */
1869 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1870 EmulatorSetRegister(EMULATOR_REG_AX
, NewHandle
);
1875 /* Force Duplicate Handle */
1878 if (DosDuplicateHandle(LOWORD(Ebx
), LOWORD(Ecx
)))
1880 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1884 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1885 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
1891 /* Allocate Memory */
1894 WORD MaxAvailable
= 0;
1895 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
1899 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
1900 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1904 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1905 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
1906 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1915 if (DosFreeMemory(ExtSegment
))
1917 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1921 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_ARENA_TRASHED
);
1922 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1928 /* Resize Memory Block */
1933 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
1935 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1939 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1940 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1941 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
1947 /* Terminate With Return Code */
1950 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
1954 /* Get Current Process */
1957 EmulatorSetRegister(EMULATOR_REG_BX
, CurrentPsp
);
1962 /* Get/Set Memory Management Options */
1965 if (LOBYTE(Eax
) == 0x00)
1967 /* Get allocation strategy */
1969 EmulatorSetRegister(EMULATOR_REG_AX
, DosAllocStrategy
);
1970 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1972 else if (LOBYTE(Eax
) == 0x01)
1974 /* Set allocation strategy */
1976 if ((LOBYTE(Ebx
) & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1977 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1979 /* Can't set both */
1980 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1981 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1985 if ((LOBYTE(Ebx
) & 0x3F) > DOS_ALLOC_LAST_FIT
)
1987 /* Invalid allocation strategy */
1988 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1993 DosAllocStrategy
= LOBYTE(Ebx
);
1994 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1996 else if (LOBYTE(Eax
) == 0x02)
1998 /* Get UMB link state */
2001 if (DosUmbLinked
) Eax
|= 1;
2002 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
2003 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2005 else if (LOBYTE(Eax
) == 0x03)
2007 /* Set UMB link state */
2009 if (Ebx
) DosLinkUmb();
2010 else DosUnlinkUmb();
2011 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2015 /* Invalid or unsupported function */
2017 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2018 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
2027 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
2028 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2033 VOID
DosBreakInterrupt(LPWORD Stack
)
2035 UNREFERENCED_PARAMETER(Stack
);
2040 BOOLEAN
DosInitialize(VOID
)
2043 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
2046 LPWSTR SourcePtr
, Environment
;
2048 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
2050 CHAR CurrentDirectory
[MAX_PATH
];
2051 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2054 /* Initialize the MCB */
2055 Mcb
->BlockType
= 'Z';
2056 Mcb
->Size
= USER_MEMORY_SIZE
;
2059 /* Initialize the link MCB to the UMB area */
2060 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
2061 Mcb
->BlockType
= 'M';
2062 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
2063 Mcb
->OwnerPsp
= SYSTEM_PSP
;
2065 /* Initialize the UMB area */
2066 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
2067 Mcb
->BlockType
= 'Z';
2068 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
2071 /* Get the environment strings */
2072 SourcePtr
= Environment
= GetEnvironmentStringsW();
2073 if (Environment
== NULL
) return FALSE
;
2075 /* Fill the DOS system environment block */
2078 /* Get the size of the ASCII string */
2079 AsciiSize
= WideCharToMultiByte(CP_ACP
,
2088 /* Allocate memory for the ASCII string */
2089 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
2090 if (AsciiString
== NULL
)
2092 FreeEnvironmentStringsW(Environment
);
2096 /* Convert to ASCII */
2097 WideCharToMultiByte(CP_ACP
,
2106 /* Copy the string into DOS memory */
2107 strcpy(DestPtr
, AsciiString
);
2109 /* Move to the next string */
2110 SourcePtr
+= wcslen(SourcePtr
) + 1;
2111 DestPtr
+= strlen(AsciiString
);
2114 /* Free the memory */
2115 HeapFree(GetProcessHeap(), 0, AsciiString
);
2119 /* Free the memory allocated for environment strings */
2120 FreeEnvironmentStringsW(Environment
);
2122 /* Clear the current directory buffer */
2123 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2125 /* Get the current directory */
2126 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2128 // TODO: Use some kind of default path?
2132 /* Convert that to a DOS path */
2133 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2135 // TODO: Use some kind of default path?
2140 CurrentDrive
= DosDirectory
[0] - 'A';
2143 Path
= strchr(DosDirectory
, '\\');
2146 /* Skip the backslash */
2150 /* Set the directory */
2151 if (Path
!= NULL
) strcpy(CurrentDirectories
[CurrentDrive
], Path
);
2153 /* Read CONFIG.SYS */
2154 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2157 while (fgetws(Buffer
, 256, Stream
))
2159 // TODO: Parse the line
2164 /* Initialize the SFT */
2165 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2167 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2168 DosSftRefCount
[i
] = 0;
2171 /* Get handles to standard I/O devices */
2172 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2173 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2174 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);