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 HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
23 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
24 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
25 static BOOLEAN DosUmbLinked
= FALSE
;
27 /* PRIVATE FUNCTIONS **********************************************************/
29 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
31 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
33 /* If this is the last block or it's not free, quit */
34 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
38 /* Get a pointer to the next MCB */
39 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
41 /* Check if the next MCB is free */
42 if (NextMcb
->OwnerPsp
== 0)
45 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
46 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
47 NextMcb
->BlockType
= 'I';
51 /* No more adjoining free blocks */
57 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
59 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
63 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
65 /* Calculate the size of the environment block */
68 TotalSize
+= strlen(Ptr
) + 1;
69 Ptr
+= strlen(Ptr
) + 1;
73 /* Add the string buffer size */
74 TotalSize
+= strlen(ProgramName
) + 1;
76 /* Allocate the memory for the environment block */
77 DestSegment
= DosAllocateMemory((TotalSize
+ 0x0F) >> 4, NULL
);
78 if (!DestSegment
) return 0;
82 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
86 strcpy(DestBuffer
, Ptr
);
88 /* Advance to the next string */
89 DestBuffer
+= strlen(Ptr
);
90 Ptr
+= strlen(Ptr
) + 1;
92 /* Put a zero after the string */
96 /* Set the final zero */
99 /* Copy the program name after the environment block */
100 strcpy(DestBuffer
, ProgramName
);
105 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
107 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
109 /* Just set the owner */
110 Mcb
->OwnerPsp
= NewOwner
;
113 static WORD
DosOpenHandle(HANDLE Handle
)
120 /* The system PSP has no handle table */
121 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
123 /* Get a pointer to the handle table */
124 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
125 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
127 /* Find a free entry in the JFT */
128 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
130 if (HandleTable
[DosHandle
] == 0xFF) break;
133 /* If there are no free entries, fail */
134 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
136 /* Check if the handle is already in the SFT */
137 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
139 /* Check if this is the same handle */
140 if (DosSystemFileTable
[i
] != Handle
) continue;
142 /* Already in the table, reference it */
145 /* Set the JFT entry to that SFT index */
146 HandleTable
[DosHandle
] = i
;
148 /* Return the new handle */
152 /* Add the handle to the SFT */
153 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
155 /* Make sure this is an empty table entry */
156 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
158 /* Initialize the empty table entry */
159 DosSystemFileTable
[i
] = Handle
;
160 DosSftRefCount
[i
] = 1;
162 /* Set the JFT entry to that SFT index */
163 HandleTable
[DosHandle
] = i
;
165 /* Return the new handle */
169 /* The SFT is full */
170 return INVALID_DOS_HANDLE
;
173 static HANDLE
DosGetRealHandle(WORD DosHandle
)
178 /* The system PSP has no handle table */
179 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
181 /* Get a pointer to the handle table */
182 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
183 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
185 /* Make sure the handle is open */
186 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
188 /* Return the Win32 handle */
189 return DosSystemFileTable
[HandleTable
[DosHandle
]];
192 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
198 /* Clear the table first */
199 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
201 /* Check if this is the initial process */
202 if (CurrentPsp
== SYSTEM_PSP
)
204 /* Set up the standard I/O devices */
205 for (i
= 0; i
<= 2; i
++)
207 /* Set the index in the SFT */
208 DestinationTable
[i
] = i
;
210 /* Increase the reference count */
218 /* Get the parent PSP block and handle table */
219 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
220 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
222 /* Copy the first 20 handles into the new table */
223 for (i
= 0; i
< 20; i
++)
225 DestinationTable
[i
] = SourceTable
[i
];
227 /* Increase the reference count */
228 DosSftRefCount
[SourceTable
[i
]]++;
232 /* PUBLIC FUNCTIONS ***********************************************************/
234 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
236 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
237 PDOS_MCB CurrentMcb
, NextMcb
;
238 BOOLEAN SearchUmb
= FALSE
;
240 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
242 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
244 /* Search UMB first */
245 Segment
= UMB_START_SEGMENT
;
251 /* Get a pointer to the MCB */
252 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
254 /* Make sure it's valid */
255 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
257 DPRINT("The DOS memory arena is corrupted!\n");
258 DosLastError
= ERROR_ARENA_TRASHED
;
262 /* Only check free blocks */
263 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
265 /* Combine this free block with adjoining free blocks */
266 DosCombineFreeBlocks(Segment
);
268 /* Update the maximum block size */
269 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
271 /* Check if this block is big enough */
272 if (CurrentMcb
->Size
< Size
) goto Next
;
274 switch (DosAllocStrategy
& 0x3F)
276 case DOS_ALLOC_FIRST_FIT
:
278 /* For first fit, stop immediately */
283 case DOS_ALLOC_BEST_FIT
:
285 /* For best fit, update the smallest block found so far */
286 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
294 case DOS_ALLOC_LAST_FIT
:
296 /* For last fit, make the current block the result, but keep searching */
303 /* If this was the last MCB in the chain, quit */
304 if (CurrentMcb
->BlockType
== 'Z')
306 /* Check if nothing was found while searching through UMBs */
307 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
309 /* Search low memory */
310 Segment
= FIRST_MCB_SEGMENT
;
317 /* Otherwise, update the segment and continue */
318 Segment
+= CurrentMcb
->Size
+ 1;
323 /* If we didn't find a free block, return 0 */
326 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
327 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
331 /* Get a pointer to the MCB */
332 CurrentMcb
= SEGMENT_TO_MCB(Result
);
334 /* Check if the block is larger than requested */
335 if (CurrentMcb
->Size
> Size
)
337 /* It is, split it into two blocks */
338 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
340 /* Initialize the new MCB structure */
341 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
342 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
343 NextMcb
->OwnerPsp
= 0;
345 /* Update the current block */
346 CurrentMcb
->BlockType
= 'M';
347 CurrentMcb
->Size
= Size
;
350 /* Take ownership of the block */
351 CurrentMcb
->OwnerPsp
= CurrentPsp
;
353 /* Return the segment of the data portion of the block */
357 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
359 BOOLEAN Success
= TRUE
;
360 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
361 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
363 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
367 /* Make sure this is a valid, allocated block */
368 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
371 DosLastError
= ERROR_INVALID_HANDLE
;
375 ReturnSize
= Mcb
->Size
;
377 /* Check if we need to expand or contract the block */
378 if (NewSize
> Mcb
->Size
)
380 /* We can't expand the last block */
381 if (Mcb
->BlockType
!= 'M')
387 /* Get the pointer and segment of the next MCB */
388 NextSegment
= Segment
+ Mcb
->Size
+ 1;
389 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
391 /* Make sure the next segment is free */
392 if (NextMcb
->OwnerPsp
!= 0)
394 DPRINT("Cannot expand memory block: next segment is not free!\n");
395 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
400 /* Combine this free block with adjoining free blocks */
401 DosCombineFreeBlocks(NextSegment
);
403 /* Set the maximum possible size of the block */
404 ReturnSize
+= NextMcb
->Size
+ 1;
406 /* Maximize the current block */
407 Mcb
->Size
= ReturnSize
;
408 Mcb
->BlockType
= NextMcb
->BlockType
;
410 /* Invalidate the next block */
411 NextMcb
->BlockType
= 'I';
413 /* Check if the block is larger than requested */
414 if (Mcb
->Size
> NewSize
)
416 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
420 /* It is, split it into two blocks */
421 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
423 /* Initialize the new MCB structure */
424 NextMcb
->BlockType
= Mcb
->BlockType
;
425 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
426 NextMcb
->OwnerPsp
= 0;
428 /* Update the current block */
429 Mcb
->BlockType
= 'M';
433 else if (NewSize
< Mcb
->Size
)
435 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
439 /* Just split the block */
440 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
441 NextMcb
->BlockType
= Mcb
->BlockType
;
442 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
443 NextMcb
->OwnerPsp
= 0;
446 Mcb
->BlockType
= 'M';
451 /* Check if the operation failed */
454 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
457 /* Return the maximum possible size */
458 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
464 BOOLEAN
DosFreeMemory(WORD BlockData
)
466 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
468 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
470 /* Make sure the MCB is valid */
471 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
473 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
477 /* Mark the block as free */
483 BOOLEAN
DosLinkUmb(VOID
)
485 DWORD Segment
= FIRST_MCB_SEGMENT
;
486 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
488 DPRINT("Linking UMB\n");
490 /* Check if UMBs are already linked */
491 if (DosUmbLinked
) return FALSE
;
493 /* Find the last block */
494 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
496 Segment
+= Mcb
->Size
+ 1;
497 Mcb
= SEGMENT_TO_MCB(Segment
);
500 /* Make sure it's valid */
501 if (Mcb
->BlockType
!= 'Z') return FALSE
;
503 /* Connect the MCB with the UMB chain */
504 Mcb
->BlockType
= 'M';
510 BOOLEAN
DosUnlinkUmb(VOID
)
512 DWORD Segment
= FIRST_MCB_SEGMENT
;
513 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
515 DPRINT("Unlinking UMB\n");
517 /* Check if UMBs are already unlinked */
518 if (!DosUmbLinked
) return FALSE
;
520 /* Find the block preceding the MCB that links it with the UMB chain */
521 while (Segment
<= 0xFFFF)
523 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
525 /* This is the last non-UMB segment */
529 /* Advance to the next MCB */
530 Segment
+= Mcb
->Size
+ 1;
531 Mcb
= SEGMENT_TO_MCB(Segment
);
534 /* Mark the MCB as the last MCB */
535 Mcb
->BlockType
= 'Z';
537 DosUmbLinked
= FALSE
;
541 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
546 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
550 /* Create the file */
551 FileHandle
= CreateFileA(FilePath
,
552 GENERIC_READ
| GENERIC_WRITE
,
553 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
559 if (FileHandle
== INVALID_HANDLE_VALUE
)
561 /* Return the error code */
562 return GetLastError();
565 /* Open the DOS handle */
566 DosHandle
= DosOpenHandle(FileHandle
);
568 if (DosHandle
== INVALID_DOS_HANDLE
)
570 /* Close the handle */
571 CloseHandle(FileHandle
);
573 /* Return the error code */
574 return ERROR_TOO_MANY_OPEN_FILES
;
577 /* It was successful */
579 return ERROR_SUCCESS
;
582 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
585 ACCESS_MASK Access
= 0;
588 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
592 /* Parse the access mode */
593 switch (AccessMode
& 3)
598 Access
= GENERIC_READ
;
605 Access
= GENERIC_WRITE
;
612 Access
= GENERIC_READ
| GENERIC_WRITE
;
619 return ERROR_INVALID_PARAMETER
;
624 FileHandle
= CreateFileA(FilePath
,
626 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
629 FILE_ATTRIBUTE_NORMAL
,
632 if (FileHandle
== INVALID_HANDLE_VALUE
)
634 /* Return the error code */
635 return GetLastError();
638 /* Open the DOS handle */
639 DosHandle
= DosOpenHandle(FileHandle
);
641 if (DosHandle
== INVALID_DOS_HANDLE
)
643 /* Close the handle */
644 CloseHandle(FileHandle
);
646 /* Return the error code */
647 return ERROR_TOO_MANY_OPEN_FILES
;
650 /* It was successful */
652 return ERROR_SUCCESS
;
655 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
657 WORD Result
= ERROR_SUCCESS
;
658 DWORD BytesRead32
= 0;
659 HANDLE Handle
= DosGetRealHandle(FileHandle
);
661 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
663 /* Make sure the handle is valid */
664 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
667 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
669 /* Store the error code */
670 Result
= GetLastError();
673 /* The number of bytes read is always 16-bit */
674 *BytesRead
= LOWORD(BytesRead32
);
676 /* Return the error code */
680 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
682 WORD Result
= ERROR_SUCCESS
;
683 DWORD BytesWritten32
= 0;
684 HANDLE Handle
= DosGetRealHandle(FileHandle
);
686 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
690 /* Make sure the handle is valid */
691 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
694 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
696 /* Store the error code */
697 Result
= GetLastError();
700 /* The number of bytes written is always 16-bit */
701 *BytesWritten
= LOWORD(BytesWritten32
);
703 /* Return the error code */
707 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
709 WORD Result
= ERROR_SUCCESS
;
711 HANDLE Handle
= DosGetRealHandle(FileHandle
);
713 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
718 /* Make sure the handle is valid */
719 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
721 /* Check if the origin is valid */
722 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
724 return ERROR_INVALID_FUNCTION
;
727 /* Move the file pointer */
728 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
730 /* Check if there's a possibility the operation failed */
731 if (FilePointer
== INVALID_SET_FILE_POINTER
)
733 /* Get the real error code */
734 Result
= GetLastError();
737 if (Result
!= ERROR_SUCCESS
)
739 /* The operation did fail */
743 /* Return the file pointer, if requested */
744 if (NewOffset
) *NewOffset
= FilePointer
;
747 return ERROR_SUCCESS
;
750 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
756 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
760 /* The system PSP has no handle table */
761 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
763 /* Get a pointer to the handle table */
764 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
765 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
767 /* Make sure the old handle is open */
768 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
770 /* Check if the new handle is open */
771 if (HandleTable
[NewHandle
] != 0xFF)
774 DosCloseHandle(NewHandle
);
777 /* Increment the reference count of the SFT entry */
778 SftIndex
= HandleTable
[OldHandle
];
779 DosSftRefCount
[SftIndex
]++;
781 /* Make the new handle point to that SFT entry */
782 HandleTable
[NewHandle
] = SftIndex
;
788 BOOLEAN
DosCloseHandle(WORD DosHandle
)
794 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
796 /* The system PSP has no handle table */
797 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
799 /* Get a pointer to the handle table */
800 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
801 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
803 /* Make sure the handle is open */
804 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
806 /* Decrement the reference count of the SFT entry */
807 SftIndex
= HandleTable
[DosHandle
];
808 DosSftRefCount
[SftIndex
]--;
810 /* Check if the reference count fell to zero */
811 if (!DosSftRefCount
[SftIndex
])
813 /* Close the file, it's no longer needed */
814 CloseHandle(DosSystemFileTable
[SftIndex
]);
816 /* Clear the handle */
817 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
820 /* Clear the entry in the JFT */
821 HandleTable
[DosHandle
] = 0xFF;
826 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
828 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
829 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
831 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
833 /* Set the exit interrupt */
834 PspBlock
->Exit
[0] = 0xCD; // int 0x20
835 PspBlock
->Exit
[1] = 0x20;
837 /* Set the number of the last paragraph */
838 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
840 /* Save the interrupt vectors */
841 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
842 PspBlock
->BreakAddress
= IntVecTable
[0x23];
843 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
845 /* Set the parent PSP */
846 PspBlock
->ParentPsp
= CurrentPsp
;
848 /* Copy the parent handle table */
849 DosCopyHandleTable(PspBlock
->HandleTable
);
851 /* Set the environment block */
852 PspBlock
->EnvBlock
= Environment
;
854 /* Set the handle table pointers to the internal handle table */
855 PspBlock
->HandleTableSize
= 20;
856 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
858 /* Set the DOS version */
859 PspBlock
->DosVersion
= DOS_VERSION
;
861 /* Set the far call opcodes */
862 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
863 PspBlock
->FarCall
[1] = 0x21;
864 PspBlock
->FarCall
[2] = 0xCB; // retf
866 /* Set the command line */
867 PspBlock
->CommandLineSize
= strlen(CommandLine
);
868 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
869 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
872 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
874 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
875 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
876 LPBYTE Address
= NULL
;
877 LPSTR ProgramFilePath
, Parameters
[128];
878 CHAR CommandLineCopy
[128];
882 DWORD i
, FileSize
, ExeSize
;
883 PIMAGE_DOS_HEADER Header
;
884 PDWORD RelocationTable
;
887 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
891 /* Save a copy of the command line */
892 strcpy(CommandLineCopy
, CommandLine
);
894 /* Get the file name of the executable */
895 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
897 /* Load the parameters in the local array */
898 while ((ParamCount
< 256)
899 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
904 /* Open a handle to the executable */
905 FileHandle
= CreateFileA(ProgramFilePath
,
910 FILE_ATTRIBUTE_NORMAL
,
912 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
914 /* Get the file size */
915 FileSize
= GetFileSize(FileHandle
, NULL
);
917 /* Create a mapping object for the file */
918 FileMapping
= CreateFileMapping(FileHandle
,
924 if (FileMapping
== NULL
) goto Cleanup
;
926 /* Map the file into memory */
927 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
928 if (Address
== NULL
) goto Cleanup
;
930 /* Did we get an environment segment? */
933 /* Set a flag to know if the environment block was allocated here */
934 AllocatedEnvBlock
= TRUE
;
936 /* No, copy the one from the parent */
937 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
938 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
943 /* Check if this is an EXE file or a COM file */
944 if (Address
[0] == 'M' && Address
[1] == 'Z')
948 /* Get the MZ header */
949 Header
= (PIMAGE_DOS_HEADER
)Address
;
951 /* Get the base size of the file, in paragraphs (rounded up) */
952 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
954 /* Add the PSP size, in paragraphs */
955 ExeSize
+= sizeof(DOS_PSP
) >> 4;
957 /* Add the maximum size that should be allocated */
958 ExeSize
+= Header
->e_maxalloc
;
960 /* Make sure it does not pass 0xFFFF */
961 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
963 /* Reduce the size one by one until the allocation is successful */
964 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
966 /* Try to allocate that much memory */
967 Segment
= DosAllocateMemory(ExeSize
, NULL
);
968 if (Segment
!= 0) break;
971 /* Check if at least the lowest allocation was successful */
972 if (Segment
== 0) goto Cleanup
;
974 /* Initialize the PSP */
975 DosInitializePsp(Segment
,
980 /* The process owns its own memory */
981 DosChangeMemoryOwner(Segment
, Segment
);
982 DosChangeMemoryOwner(EnvBlock
, Segment
);
984 /* Copy the program to Segment:0100 */
985 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
986 + TO_LINEAR(Segment
, 0x100)),
987 Address
+ (Header
->e_cparhdr
<< 4),
988 min(FileSize
- (Header
->e_cparhdr
<< 4),
989 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
991 /* Get the relocation table */
992 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
994 /* Perform relocations */
995 for (i
= 0; i
< Header
->e_crlc
; i
++)
997 /* Get a pointer to the word that needs to be patched */
998 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
999 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
1000 0x100 + LOWORD(RelocationTable
[i
])));
1002 /* Add the number of the EXE segment to it */
1003 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1006 /* Set the initial segment registers */
1007 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1008 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1010 /* Set the stack to the location from the header */
1011 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1015 CurrentPsp
= Segment
;
1016 DiskTransferArea
= MAKELONG(0x80, Segment
);
1017 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1026 /* Find the maximum amount of memory that can be allocated */
1027 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1029 /* Make sure it's enough for the whole program and the PSP */
1030 if ((MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1032 /* Allocate all of it */
1033 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1034 if (Segment
== 0) goto Cleanup
;
1036 /* The process owns its own memory */
1037 DosChangeMemoryOwner(Segment
, Segment
);
1038 DosChangeMemoryOwner(EnvBlock
, Segment
);
1040 /* Copy the program to Segment:0100 */
1041 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
1042 + TO_LINEAR(Segment
, 0x100)),
1046 /* Initialize the PSP */
1047 DosInitializePsp(Segment
,
1049 (FileSize
+ sizeof(DOS_PSP
)) >> 4,
1052 /* Set the initial segment registers */
1053 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
1054 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
1056 /* Set the stack to the last word of the segment */
1057 EmulatorSetStack(Segment
, 0xFFFE);
1060 CurrentPsp
= Segment
;
1061 DiskTransferArea
= MAKELONG(0x80, Segment
);
1062 EmulatorExecute(Segment
, 0x100);
1070 /* It was not successful, cleanup the DOS memory */
1071 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
1072 if (Segment
) DosFreeMemory(Segment
);
1076 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1078 /* Close the file mapping object */
1079 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1081 /* Close the file handle */
1082 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1087 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1090 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1091 PDOS_MCB CurrentMcb
;
1092 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1093 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1095 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1099 /* Check if this PSP is it's own parent */
1100 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1102 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1104 /* Close the handle */
1108 /* Free the memory used by the process */
1111 /* Get a pointer to the MCB */
1112 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1114 /* Make sure the MCB is valid */
1115 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1117 /* If this block was allocated by the process, free it */
1118 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
);
1120 /* If this was the last block, quit */
1121 if (CurrentMcb
->BlockType
== 'Z') break;
1123 /* Update the segment and continue */
1124 McbSegment
+= CurrentMcb
->Size
+ 1;
1128 /* Restore the interrupt vectors */
1129 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1130 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1131 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1133 /* Update the current PSP */
1134 if (Psp
== CurrentPsp
)
1136 CurrentPsp
= PspBlock
->ParentPsp
;
1137 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1140 /* Return control to the parent process */
1141 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1142 LOWORD(PspBlock
->TerminateAddress
));
1145 CHAR
DosReadCharacter(VOID
)
1147 CHAR Character
= '\0';
1150 /* Use the file reading function */
1151 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1156 VOID
DosPrintCharacter(CHAR Character
)
1160 /* Use the file writing function */
1161 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1164 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1166 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1168 if (Handle
== INVALID_HANDLE_VALUE
)
1171 DosLastError
= ERROR_FILE_NOT_FOUND
;
1175 switch (ControlCode
)
1177 /* Get Device Information */
1182 if (Handle
== DosSystemFileTable
[0])
1187 else if (Handle
== DosSystemFileTable
[1])
1189 /* Console output */
1193 /* It is a character device */
1196 /* Return the device information word */
1197 EmulatorSetRegister(EMULATOR_REG_DX
, InfoWord
);
1202 /* Unsupported control code */
1205 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1207 DosLastError
= ERROR_INVALID_PARAMETER
;
1213 VOID
DosInt20h(LPWORD Stack
)
1215 /* This is the exit interrupt */
1216 DosTerminateProcess(Stack
[STACK_CS
], 0);
1219 VOID
DosInt21h(LPWORD Stack
)
1223 SYSTEMTIME SystemTime
;
1225 PDOS_INPUT_BUFFER InputBuffer
;
1226 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1227 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1228 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1229 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
1230 WORD DataSegment
= EmulatorGetRegister(EMULATOR_REG_DS
);
1231 WORD ExtSegment
= EmulatorGetRegister(EMULATOR_REG_ES
);
1233 /* Check the value in the AH register */
1234 switch (HIBYTE(Eax
))
1236 /* Terminate Program */
1239 DosTerminateProcess(Stack
[STACK_CS
], 0);
1243 /* Read Character And Echo */
1246 Character
= DosReadCharacter();
1247 DosPrintCharacter(Character
);
1248 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
1252 /* Print Character */
1255 DosPrintCharacter(LOBYTE(Edx
));
1259 /* Read Character Without Echo */
1263 EmulatorSetRegister(EMULATOR_REG_AX
,
1264 (Eax
& 0xFFFFFF00) | DosReadCharacter());
1271 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1272 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1274 while ((*String
) != '$')
1276 DosPrintCharacter(*String
);
1283 /* Read Buffered Input */
1286 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
1287 + TO_LINEAR(DataSegment
,
1290 InputBuffer
->Length
= 0;
1291 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
1293 Character
= DosReadCharacter();
1294 DosPrintCharacter(Character
);
1295 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
1296 if (Character
== '\r') break;
1297 InputBuffer
->Length
++;
1303 /* Set Disk Transfer Area */
1306 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
1310 /* Set Interrupt Vector */
1313 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
1315 /* Write the new far pointer to the IDT */
1316 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
1321 /* Get system date */
1324 GetLocalTime(&SystemTime
);
1325 EmulatorSetRegister(EMULATOR_REG_CX
,
1326 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
1327 EmulatorSetRegister(EMULATOR_REG_DX
,
1329 | (SystemTime
.wMonth
<< 8)
1331 EmulatorSetRegister(EMULATOR_REG_AX
,
1332 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1336 /* Set system date */
1339 GetLocalTime(&SystemTime
);
1340 SystemTime
.wYear
= LOWORD(Ecx
);
1341 SystemTime
.wMonth
= HIBYTE(Edx
);
1342 SystemTime
.wDay
= LOBYTE(Edx
);
1344 if (SetLocalTime(&SystemTime
))
1346 /* Return success */
1347 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1351 /* Return failure */
1352 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1358 /* Get system time */
1361 GetLocalTime(&SystemTime
);
1362 EmulatorSetRegister(EMULATOR_REG_CX
,
1364 | (SystemTime
.wHour
<< 8)
1365 | SystemTime
.wMinute
);
1366 EmulatorSetRegister(EMULATOR_REG_DX
,
1368 | (SystemTime
.wSecond
<< 8)
1369 | (SystemTime
.wMilliseconds
/ 10));
1373 /* Set system time */
1376 GetLocalTime(&SystemTime
);
1377 SystemTime
.wHour
= HIBYTE(Ecx
);
1378 SystemTime
.wMinute
= LOBYTE(Ecx
);
1379 SystemTime
.wSecond
= HIBYTE(Edx
);
1380 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1382 if (SetLocalTime(&SystemTime
))
1384 /* Return success */
1385 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1389 /* Return failure */
1390 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1396 /* Get Disk Transfer Area */
1399 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1400 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1405 /* Get DOS Version */
1408 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1410 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1414 /* Get Interrupt Vector */
1417 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1419 /* Read the address from the IDT into ES:BX */
1420 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1421 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1426 /* Create Directory */
1429 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1430 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1432 if (CreateDirectoryA(String
, NULL
))
1434 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1438 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1439 EmulatorSetRegister(EMULATOR_REG_AX
,
1440 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1446 /* Remove Directory */
1449 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1450 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1452 if (RemoveDirectoryA(String
))
1454 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1458 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1459 EmulatorSetRegister(EMULATOR_REG_AX
,
1460 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1467 /* Set Current Directory */
1470 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1471 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1473 if (SetCurrentDirectoryA(String
))
1475 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1479 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1480 EmulatorSetRegister(EMULATOR_REG_AX
,
1481 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1491 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1492 (LPCSTR
)(ULONG_PTR
)BaseAddress
1493 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1499 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1501 /* Return the handle in AX */
1502 EmulatorSetRegister(EMULATOR_REG_AX
,
1503 (Eax
& 0xFFFF0000) | FileHandle
);
1508 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1510 /* Return the error code in AX */
1511 EmulatorSetRegister(EMULATOR_REG_AX
,
1512 (Eax
& 0xFFFF0000) | ErrorCode
);
1522 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1523 (LPCSTR
)(ULONG_PTR
)BaseAddress
1524 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1530 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1532 /* Return the handle in AX */
1533 EmulatorSetRegister(EMULATOR_REG_AX
,
1534 (Eax
& 0xFFFF0000) | FileHandle
);
1539 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1541 /* Return the error code in AX */
1542 EmulatorSetRegister(EMULATOR_REG_AX
,
1543 (Eax
& 0xFFFF0000) | ErrorCode
);
1552 if (DosCloseHandle(LOWORD(Ebx
)))
1555 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1560 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1562 /* Return the error code in AX */
1563 EmulatorSetRegister(EMULATOR_REG_AX
,
1564 (Eax
& 0xFFFF0000) | ERROR_INVALID_HANDLE
);
1574 WORD ErrorCode
= DosReadFile(LOWORD(Ebx
),
1575 (LPVOID
)((ULONG_PTR
)BaseAddress
1576 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1583 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1585 /* Return the number of bytes read in AX */
1586 EmulatorSetRegister(EMULATOR_REG_AX
,
1587 (Eax
& 0xFFFF0000) | BytesRead
);
1592 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1594 /* Return the error code in AX */
1595 EmulatorSetRegister(EMULATOR_REG_AX
,
1596 (Eax
& 0xFFFF0000) | ErrorCode
);
1604 WORD BytesWritten
= 0;
1605 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1606 (LPVOID
)((ULONG_PTR
)BaseAddress
1607 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1614 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1616 /* Return the number of bytes written in AX */
1617 EmulatorSetRegister(EMULATOR_REG_AX
,
1618 (Eax
& 0xFFFF0000) | BytesWritten
);
1623 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1625 /* Return the error code in AX */
1626 EmulatorSetRegister(EMULATOR_REG_AX
,
1627 (Eax
& 0xFFFF0000) | ErrorCode
);
1636 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1638 /* Call the API function */
1639 if (DeleteFileA(FileName
)) Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1642 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1643 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1653 WORD ErrorCode
= DosSeekFile(LOWORD(Ebx
),
1654 MAKELONG(LOWORD(Edx
), LOWORD(Ecx
)),
1661 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1663 /* Return the new offset in DX:AX */
1664 EmulatorSetRegister(EMULATOR_REG_DX
,
1665 (Edx
& 0xFFFF0000) | HIWORD(NewLocation
));
1666 EmulatorSetRegister(EMULATOR_REG_AX
,
1667 (Eax
& 0xFFFF0000) | LOWORD(NewLocation
));
1672 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1674 /* Return the error code in AX */
1675 EmulatorSetRegister(EMULATOR_REG_AX
,
1676 (Eax
& 0xFFFF0000) | ErrorCode
);
1682 /* Get/Set File Attributes */
1686 LPSTR FileName
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DataSegment
, Edx
));
1688 if (LOBYTE(Eax
) == 0x00)
1690 /* Get the attributes */
1691 Attributes
= GetFileAttributesA(FileName
);
1693 /* Check if it failed */
1694 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1696 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1697 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1702 /* Return the attributes that DOS can understand */
1703 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1704 EmulatorSetRegister(EMULATOR_REG_CX
,
1705 (Ecx
& 0xFFFFFF00) | LOBYTE(Attributes
));
1707 else if (LOBYTE(Eax
) == 0x01)
1709 /* Try to set the attributes */
1710 if (SetFileAttributesA(FileName
, LOBYTE(Ecx
)))
1712 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1716 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1717 EmulatorSetRegister(EMULATOR_REG_AX
, GetLastError());
1722 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1723 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
1732 if (DosHandleIoctl(LOBYTE(Eax
), LOWORD(Ebx
)))
1734 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1738 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1739 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1745 /* Duplicate Handle */
1749 HANDLE Handle
= DosGetRealHandle(LOWORD(Ebx
));
1751 if (Handle
!= INVALID_HANDLE_VALUE
)
1753 /* The handle is invalid */
1754 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1755 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
1760 /* Open a new handle to the same entry */
1761 NewHandle
= DosOpenHandle(Handle
);
1763 if (NewHandle
== INVALID_DOS_HANDLE
)
1765 /* Too many files open */
1766 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1767 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_TOO_MANY_OPEN_FILES
);
1772 /* Return the result */
1773 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1774 EmulatorSetRegister(EMULATOR_REG_AX
, NewHandle
);
1779 /* Force Duplicate Handle */
1782 if (DosDuplicateHandle(LOWORD(Ebx
), LOWORD(Ecx
)))
1784 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1788 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1789 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_HANDLE
);
1795 /* Allocate Memory */
1798 WORD MaxAvailable
= 0;
1799 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
1803 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
1804 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1808 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1809 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
1810 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1819 if (DosFreeMemory(ExtSegment
))
1821 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1825 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_ARENA_TRASHED
);
1826 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1832 /* Resize Memory Block */
1837 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
1839 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1843 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1844 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1845 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
1851 /* Terminate With Return Code */
1854 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
1858 /* Get Current Process */
1861 EmulatorSetRegister(EMULATOR_REG_BX
, CurrentPsp
);
1866 /* Get/Set Memory Management Options */
1869 if (LOBYTE(Eax
) == 0x00)
1871 /* Get allocation strategy */
1873 EmulatorSetRegister(EMULATOR_REG_AX
, DosAllocStrategy
);
1874 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1876 else if (LOBYTE(Eax
) == 0x01)
1878 /* Set allocation strategy */
1880 if ((LOBYTE(Ebx
) & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1881 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1883 /* Can't set both */
1884 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1885 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1889 if ((LOBYTE(Ebx
) & 0x3F) > DOS_ALLOC_LAST_FIT
)
1891 /* Invalid allocation strategy */
1892 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1893 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1897 DosAllocStrategy
= LOBYTE(Ebx
);
1898 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1900 else if (LOBYTE(Eax
) == 0x02)
1902 /* Get UMB link state */
1905 if (DosUmbLinked
) Eax
|= 1;
1906 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
1907 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1909 else if (LOBYTE(Eax
) == 0x03)
1911 /* Set UMB link state */
1913 if (Ebx
) DosLinkUmb();
1914 else DosUnlinkUmb();
1915 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1919 /* Invalid or unsupported function */
1921 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1922 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
1931 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
1932 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1937 VOID
DosBreakInterrupt(LPWORD Stack
)
1942 BOOLEAN
DosInitialize(VOID
)
1945 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
1948 LPWSTR SourcePtr
, Environment
;
1950 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
1953 /* Initialize the MCB */
1954 Mcb
->BlockType
= 'Z';
1955 Mcb
->Size
= USER_MEMORY_SIZE
;
1958 /* Initialize the link MCB to the UMB area */
1959 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
1960 Mcb
->BlockType
= 'M';
1961 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
1962 Mcb
->OwnerPsp
= SYSTEM_PSP
;
1964 /* Initialize the UMB area */
1965 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
1966 Mcb
->BlockType
= 'Z';
1967 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
1970 /* Get the environment strings */
1971 SourcePtr
= Environment
= GetEnvironmentStringsW();
1972 if (Environment
== NULL
) return FALSE
;
1974 /* Fill the DOS system environment block */
1977 /* Get the size of the ASCII string */
1978 AsciiSize
= WideCharToMultiByte(CP_ACP
,
1987 /* Allocate memory for the ASCII string */
1988 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
1989 if (AsciiString
== NULL
)
1991 FreeEnvironmentStringsW(Environment
);
1995 /* Convert to ASCII */
1996 WideCharToMultiByte(CP_ACP
,
2005 /* Copy the string into DOS memory */
2006 strcpy(DestPtr
, AsciiString
);
2008 /* Move to the next string */
2009 SourcePtr
+= wcslen(SourcePtr
) + 1;
2010 DestPtr
+= strlen(AsciiString
);
2013 /* Free the memory */
2014 HeapFree(GetProcessHeap(), 0, AsciiString
);
2018 /* Free the memory allocated for environment strings */
2019 FreeEnvironmentStringsW(Environment
);
2021 /* Read CONFIG.SYS */
2022 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2025 while (fgetws(Buffer
, 256, Stream
))
2027 // TODO: Parse the line
2032 /* Initialize the SFT */
2033 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2035 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2036 DosSftRefCount
[i
] = 0;
2039 /* Get handles to standard I/O devices */
2040 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2041 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2042 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);